Remove routes on an address removal if the routes referencing to the address. Implemented by ozaki-r@n.o.

A route that has a gateway is on a connected route can be invalid if the
connected route is deleted, i.e., an associated address is removed.
Traditionally NetBSD doesn't sweep such a route on the address removal.  Sending
packets over the route fails with "No route to host".  Also the route holds an
orphan ifaddr as rt_ifa that is destructed say by in_purgeaddr.

If the same address is assgined again in such a state, there can be two
different ifaddr objects with the same address.  Until recently it's not a
big problem because we can send packets anyway.  However after MP-ification
of the network stack, we can't send packets because we strictly check if rt_ifa
(i.e., the (old) ifaddr) is valid.

This change automatically removes such routes on a removal of an associated
address to avoid keeping inconsistent routes.
This commit is contained in:
knakahara 2022-09-20 02:23:37 +00:00
parent 75ded7d598
commit bf4e44ab86
6 changed files with 125 additions and 15 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: if.c,v 1.525 2022/09/03 02:53:18 thorpej Exp $ */
/* $NetBSD: if.c,v 1.526 2022/09/20 02:23:37 knakahara Exp $ */
/*-
* Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@ -90,7 +90,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.525 2022/09/03 02:53:18 thorpej Exp $");
__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.526 2022/09/20 02:23:37 knakahara Exp $");
#if defined(_KERNEL_OPT)
#include "opt_inet.h"
@ -1479,7 +1479,7 @@ restart:
/* Delete stray routes from the routing table. */
for (i = 0; i <= AF_MAX; i++)
rt_delete_matched_entries(i, if_delroute_matcher, ifp);
rt_delete_matched_entries(i, if_delroute_matcher, ifp, false);
DOMAIN_FOREACH(dp) {
if (dp->dom_ifdetach != NULL && ifp->if_afdata[dp->dom_family])

View File

@ -1,4 +1,4 @@
/* $NetBSD: route.c,v 1.233 2022/08/29 23:48:18 knakahara Exp $ */
/* $NetBSD: route.c,v 1.234 2022/09/20 02:23:37 knakahara Exp $ */
/*-
* Copyright (c) 1998, 2008 The NetBSD Foundation, Inc.
@ -97,7 +97,7 @@
#endif
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.233 2022/08/29 23:48:18 knakahara Exp $");
__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.234 2022/09/20 02:23:37 knakahara Exp $");
#include <sys/param.h>
#ifdef RTFLUSH_DEBUG
@ -2291,7 +2291,7 @@ rt_check_reject_route(const struct rtentry *rt, const struct ifnet *ifp)
void
rt_delete_matched_entries(sa_family_t family, int (*f)(struct rtentry *, void *),
void *v)
void *v, bool notify)
{
for (;;) {
@ -2308,6 +2308,7 @@ rt_delete_matched_entries(sa_family_t family, int (*f)(struct rtentry *, void *)
return;
}
rt_ref(rt);
RT_REFCNT_TRACE(rt);
splx(s);
RT_UNLOCK();
@ -2316,12 +2317,16 @@ rt_delete_matched_entries(sa_family_t family, int (*f)(struct rtentry *, void *)
if (error == 0) {
KASSERT(retrt == rt);
KASSERT((retrt->rt_flags & RTF_UP) == 0);
if (notify)
rt_newmsg(RTM_DELETE, retrt);
retrt->rt_ifp = NULL;
rt_unref(rt);
RT_REFCNT_TRACE(rt);
rt_free(retrt);
} else if (error == ESRCH) {
/* Someone deleted the entry already. */
rt_unref(rt);
RT_REFCNT_TRACE(rt);
} else {
log(LOG_ERR, "%s: unable to delete rtentry @ %p, "
"error = %d\n", rt->rt_ifp->if_xname, rt, error);
@ -2338,6 +2343,53 @@ rt_walktree_locked(sa_family_t family, int (*f)(struct rtentry *, void *),
return rtbl_walktree(family, f, v);
}
void
rt_replace_ifa_matched_entries(sa_family_t family,
int (*f)(struct rtentry *, void *), void *v, struct ifaddr *ifa)
{
for (;;) {
int s;
#ifdef NET_MPSAFE
int error;
#endif
struct rtentry *rt;
RT_RLOCK();
s = splsoftnet();
rt = rtbl_search_matched_entry(family, f, v);
if (rt == NULL) {
splx(s);
RT_UNLOCK();
return;
}
rt_ref(rt);
RT_REFCNT_TRACE(rt);
splx(s);
RT_UNLOCK();
#ifdef NET_MPSAFE
error = rt_update_prepare(rt);
if (error == 0) {
rt_replace_ifa(rt, ifa);
rt_update_finish(rt);
rt_newmsg(RTM_CHANGE, rt);
} else {
/*
* If error != 0, the rtentry is being
* destroyed, so doing nothing doesn't
* matter.
*/
}
#else
rt_replace_ifa(rt, ifa);
rt_newmsg(RTM_CHANGE, rt);
#endif
rt_unref(rt);
RT_REFCNT_TRACE(rt);
}
}
int
rt_walktree(sa_family_t family, int (*f)(struct rtentry *, void *), void *v)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: route.h,v 1.131 2022/08/29 09:14:02 knakahara Exp $ */
/* $NetBSD: route.h,v 1.132 2022/09/20 02:23:37 knakahara Exp $ */
/*
* Copyright (c) 1980, 1986, 1993
@ -463,7 +463,9 @@ struct sockaddr *
int rt_check_reject_route(const struct rtentry *, const struct ifnet *);
void rt_delete_matched_entries(sa_family_t,
int (*)(struct rtentry *, void *), void *);
int (*)(struct rtentry *, void *), void *, bool);
void rt_replace_ifa_matched_entries(sa_family_t,
int (*)(struct rtentry *, void *), void *, struct ifaddr *);
int rt_walktree(sa_family_t, int (*)(struct rtentry *, void *), void *);
static __inline void

View File

@ -1,4 +1,4 @@
/* $NetBSD: in.c,v 1.242 2021/09/21 15:05:41 christos Exp $ */
/* $NetBSD: in.c,v 1.243 2022/09/20 02:23:37 knakahara Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -91,7 +91,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.242 2021/09/21 15:05:41 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.243 2022/09/20 02:23:37 knakahara Exp $");
#include "arp.h"
@ -1347,6 +1347,17 @@ in_addprefix(struct in_ifaddr *target, int flags)
return error;
}
static int
in_rt_ifa_matcher(struct rtentry *rt, void *v)
{
struct ifaddr *ifa = v;
if (rt->rt_ifa == ifa)
return 1;
else
return 0;
}
/*
* remove a route to prefix ("connected route" in cisco terminology).
* re-installs the route by using another interface address, if there's one
@ -1403,6 +1414,16 @@ in_scrubprefix(struct in_ifaddr *target)
if (error == 0)
ia->ia_flags |= IFA_ROUTE;
if (!ISSET(target->ia_ifa.ifa_flags, IFA_DESTROYING))
goto skip;
/*
* Replace rt_ifa of routes that have the removing address
* with the new address.
*/
rt_replace_ifa_matched_entries(AF_INET,
in_rt_ifa_matcher, &target->ia_ifa, &ia->ia_ifa);
skip:
ia4_release(ia, &psref);
curlwp_bindx(bound);
@ -1416,6 +1437,13 @@ in_scrubprefix(struct in_ifaddr *target)
*/
rtinit(&target->ia_ifa, RTM_DELETE, rtinitflags(target));
target->ia_flags &= ~IFA_ROUTE;
if (ISSET(target->ia_ifa.ifa_flags, IFA_DESTROYING)) {
/* Remove routes that have the removing address as rt_ifa. */
rt_delete_matched_entries(AF_INET, in_rt_ifa_matcher,
&target->ia_ifa, true);
}
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6.c,v 1.285 2021/12/05 04:42:55 msaitoh Exp $ */
/* $NetBSD: in6.c,v 1.286 2022/09/20 02:23:37 knakahara Exp $ */
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/*
@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.285 2021/12/05 04:42:55 msaitoh Exp $");
__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.286 2022/09/20 02:23:37 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@ -279,6 +279,17 @@ in6_ifaddprefix(struct in6_ifaddr *ia)
return error;
}
static int
in6_rt_ifa_matcher(struct rtentry *rt, void *v)
{
struct ifaddr *ifa = v;
if (rt->rt_ifa == ifa)
return 1;
else
return 0;
}
/* Delete network prefix route if present.
* Re-add it to another address if the prefix matches. */
static int
@ -319,6 +330,16 @@ in6_ifremprefix(struct in6_ifaddr *target)
error = in6_ifaddprefix(ia);
if (!ISSET(target->ia_ifa.ifa_flags, IFA_DESTROYING))
goto skip;
/*
* Replace rt_ifa of routes that have the removing address
* with the new address.
*/
rt_replace_ifa_matched_entries(AF_INET6,
in6_rt_ifa_matcher, &target->ia_ifa, &ia->ia_ifa);
skip:
ia6_release(ia, &psref);
curlwp_bindx(bound);
@ -332,6 +353,13 @@ in6_ifremprefix(struct in6_ifaddr *target)
*/
rtinit(&target->ia_ifa, RTM_DELETE, 0);
target->ia_flags &= ~IFA_ROUTE;
if (ISSET(target->ia_ifa.ifa_flags, IFA_DESTROYING)) {
/* Remove routes that have the removing address as rt_ifa. */
rt_delete_matched_entries(AF_INET6, in6_rt_ifa_matcher,
&target->ia_ifa, true);
}
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: nfs_boot.c,v 1.88 2018/05/17 02:34:31 thorpej Exp $ */
/* $NetBSD: nfs_boot.c,v 1.89 2022/09/20 02:23:37 knakahara Exp $ */
/*-
* Copyright (c) 1995, 1997 The NetBSD Foundation, Inc.
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nfs_boot.c,v 1.88 2018/05/17 02:34:31 thorpej Exp $");
__KERNEL_RCSID(0, "$NetBSD: nfs_boot.c,v 1.89 2022/09/20 02:23:37 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_nfs.h"
@ -576,7 +576,7 @@ void
nfs_boot_flushrt(struct ifnet *ifp)
{
rt_delete_matched_entries(AF_INET, nfs_boot_delroute_matcher, ifp);
rt_delete_matched_entries(AF_INET, nfs_boot_delroute_matcher, ifp, false);
}
/*