On 19/08/16(Fri) 19:19, Alexander Bluhm wrote:
> On Mon, Aug 15, 2016 at 01:52:46PM +0200, Martin Pieuchot wrote:
> > More tests, comments, ok are welcome.
>
> There is an issue with path mtu discovery that my regression test
> found, but i think that can be fixed with this diff in tree.
I integrated your fix in the updated diff below.
> > + KASSERT(nhrt->rt_cachecnt > 0);
>
> Could you put a KASSERT(ISSET(rt->rt_flags, RTF_CACHED)) before
> accessing rt_cachecnt?
Done.
> > @@ -615,7 +615,27 @@ route_output(struct mbuf *m, ...)
> > }
> > break;
> > case RTM_DELETE:
> > - error = rtrequest(RTM_DELETE, &info, prio, &rt, tableid);
> ...
> > + error = rtrequest(RTM_DELETE, &info, prio, NULL, tableid);
> > if (error == 0)
> > goto report;
> > break;
>
> I think you should keep passing &rt instead of NULL, otherwise rt
> might be NULL when you goto report.
Done.
> > +void
> > +arpinvalidate(struct rtentry *rt)
> > +{
> > + struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo;
> > + struct sockaddr_dl *sdl = satosdl(rt->rt_gateway);
> > +
> > + la_hold_total -= ml_purge(&la->la_ml);
> > + sdl->sdl_alen = 0;
> > + la->la_asked = 0;
> > +}
>
> Is it safe to modify the la and sdl while another CPU might use it?
> Especially ml_purge() looks dangerous.
No it is not. This should be addressed afterward.
> > @@ -1495,18 +1512,13 @@ nd6_resolve(struct ifnet *ifp, struct rt
> > struct sockaddr_dl *sdl;
> > struct rtentry *rt;
> > struct llinfo_nd6 *ln = NULL;
> > - int error;
> >
> > if (m->m_flags & M_MCAST) {
> > ETHER_MAP_IPV6_MULTICAST(&satosin6(dst)->sin6_addr, desten);
> > return (0);
> > }
> >
> > - error = rt_checkgate(rt0, &rt);
> > - if (error) {
> > - m_freem(m);
> > - return (error);
> > - }
> > + rt = rt_getll(rt0);
>
> Should we check for RTF_REJECT like it was done in rt_checkgate()
> before?
Indeed.
Updated diff below fix the issues you reported and some regression with
the route(8) and arp(8) regress tests. I'll commit it soon to fix the
remaining issues in tree.
Index: net/route.c
===================================================================
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.315
diff -u -p -r1.315 route.c
--- net/route.c 19 Aug 2016 07:12:54 -0000 1.315
+++ net/route.c 22 Aug 2016 09:00:14 -0000
@@ -153,7 +153,9 @@ struct pool rtentry_pool; /* pool for r
struct pool rttimer_pool; /* pool for rttimer structures */
void rt_timer_init(void);
-void rt_setgwroute(struct rtentry *, u_int);
+int rt_setgwroute(struct rtentry *, u_int);
+void rt_putgwroute(struct rtentry *);
+int rt_fixgwroute(struct rtentry *, void *, unsigned int);
int rtflushclone1(struct rtentry *, void *, u_int);
void rtflushclone(unsigned int, struct rtentry *);
int rt_if_remove_rtdelete(struct rtentry *, void *, u_int);
@@ -204,21 +206,20 @@ rtisvalid(struct rtentry *rt)
if (rt == NULL)
return (0);
-#ifdef DIAGNOSTIC
- if (ISSET(rt->rt_flags, RTF_GATEWAY) && (rt->rt_gwroute != NULL) &&
- ISSET(rt->rt_gwroute->rt_flags, RTF_GATEWAY))
- panic("next hop must be directly reachable");
-#endif
-
- if ((rt->rt_flags & RTF_UP) == 0)
+ if (!ISSET(rt->rt_flags, RTF_UP))
return (0);
/* Routes attached to stale ifas should be freed. */
if (rt->rt_ifa == NULL || rt->rt_ifa->ifa_ifp == NULL)
return (0);
- if (ISSET(rt->rt_flags, RTF_GATEWAY) && !rtisvalid(rt->rt_gwroute))
- return (0);
+#ifdef DIAGNOSTIC
+ if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
+ KASSERT(rt->rt_gwroute != NULL);
+ KASSERT(ISSET(rt->rt_gwroute->rt_flags, RTF_UP));
+ KASSERT(!ISSET(rt->rt_gwroute->rt_flags, RTF_GATEWAY));
+ }
+#endif /* DIAGNOSTIC */
return (1);
}
@@ -269,8 +270,6 @@ rt_match(struct sockaddr *dst, uint32_t
return (rt);
}
-struct rtentry *_rtalloc(struct sockaddr *, uint32_t *, int, unsigned int);
-
#ifndef SMALL_KERNEL
/*
* Originated from bridge_hash() in if_bridge.c
@@ -351,16 +350,10 @@ rt_hash(struct rtentry *rt, struct socka
struct rtentry *
rtalloc_mpath(struct sockaddr *dst, uint32_t *src, unsigned int rtableid)
{
- return (_rtalloc(dst, src, RT_RESOLVE, rtableid));
+ return (rt_match(dst, src, RT_RESOLVE, rtableid));
}
#endif /* SMALL_KERNEL */
-struct rtentry *
-rtalloc(struct sockaddr *dst, int flags, unsigned int rtableid)
-{
- return (_rtalloc(dst, NULL, flags, rtableid));
-}
-
/*
* Look in the routing table for the best matching entry for
* ``dst''.
@@ -369,44 +362,35 @@ rtalloc(struct sockaddr *dst, int flags,
* longer valid, try to cache it.
*/
struct rtentry *
-_rtalloc(struct sockaddr *dst, uint32_t *src, int flags, unsigned int rtableid)
+rtalloc(struct sockaddr *dst, int flags, unsigned int rtableid)
{
- struct rtentry *rt;
-
- rt = rt_match(dst, src, flags, rtableid);
-
- /* No match or route to host? We're done. */
- if (rt == NULL || !ISSET(rt->rt_flags, RTF_GATEWAY))
- return (rt);
-
- /* Nothing to do if the next hop is valid. */
- if (rtisvalid(rt->rt_gwroute))
- return (rt);
-
- rt_setgwroute(rt, rtableid);
-
- return (rt);
+ return (rt_match(dst, NULL, flags, rtableid));
}
-void
+/*
+ * Cache the route entry corresponding to a reachable next hop in
+ * the gateway entry ``rt''.
+ */
+int
rt_setgwroute(struct rtentry *rt, u_int rtableid)
{
struct rtentry *nhrt;
- rtfree(rt->rt_gwroute);
- rt->rt_gwroute = NULL;
+ KERNEL_ASSERT_LOCKED();
- /*
- * If we cannot find a valid next hop, return the route
- * with a gateway.
- *
- * XXX Some dragons hiding in the tree certainly depends on
- * this behavior. But it is safe since rt_checkgate() wont
- * allow us to us this route later on.
- */
+ KASSERT(ISSET(rt->rt_flags, RTF_GATEWAY));
+ KASSERT(rt->rt_gwroute == NULL);
+
+ /* If we cannot find a valid next hop bail. */
nhrt = rt_match(rt->rt_gateway, NULL, RT_RESOLVE, rtable_l2(rtableid));
if (nhrt == NULL)
- return;
+ return (ENOENT);
+
+ /* Next hop entry must be on the same interface. */
+ if (nhrt->rt_ifidx != rt->rt_ifidx) {
+ rtfree(nhrt);
+ return (EHOSTUNREACH);
+ }
/*
* Next hop must be reachable, this also prevents rtentry
@@ -414,13 +398,7 @@ rt_setgwroute(struct rtentry *rt, u_int
*/
if (ISSET(nhrt->rt_flags, RTF_CLONING|RTF_GATEWAY)) {
rtfree(nhrt);
- return;
- }
-
- /* Next hop entry must be UP and on the same interface. */
- if (!ISSET(nhrt->rt_flags, RTF_UP) || nhrt->rt_ifidx != rt->rt_ifidx) {
- rtfree(nhrt);
- return;
+ return (ELOOP);
}
/*
@@ -431,10 +409,76 @@ rt_setgwroute(struct rtentry *rt, u_int
rt->rt_mtu = nhrt->rt_mtu;
/*
- * Do not return the cached next-hop route, rt_checkgate() will
- * do the magic for us.
+ * To avoid reference counting problems when writting link-layer
+ * addresses in an outgoing packet, we ensure that the lifetime
+ * of a cached entry is greater that the bigger lifetime of the
+ * gateway entries it is pointed by.
*/
+ nhrt->rt_flags |= RTF_CACHED;
+ nhrt->rt_cachecnt++;
+
rt->rt_gwroute = nhrt;
+
+ return (0);
+}
+
+/*
+ * Invalidate the cached route entry of the gateway entry ``rt''.
+ */
+void
+rt_putgwroute(struct rtentry *rt)
+{
+ struct rtentry *nhrt = rt->rt_gwroute;
+
+ KERNEL_ASSERT_LOCKED();
+
+ if (!ISSET(rt->rt_flags, RTF_GATEWAY) || nhrt == NULL)
+ return;
+
+ KASSERT(ISSET(nhrt->rt_flags, RTF_CACHED));
+ KASSERT(nhrt->rt_cachecnt > 0);
+
+ --nhrt->rt_cachecnt;
+ if (nhrt->rt_cachecnt == 0)
+ nhrt->rt_flags &= ~RTF_CACHED;
+
+ rtfree(rt->rt_gwroute);
+ rt->rt_gwroute = NULL;
+}
+
+/*
+ * Refresh cached entries of RTF_GATEWAY routes for a given interface.
+ *
+ * This clever logic is necessary to try to fix routes linked to stale
+ * ifas.
+ */
+int
+rt_fixgwroute(struct rtentry *rt, void *arg, unsigned int id)
+{
+ struct ifnet *ifp = arg;
+
+ KERNEL_ASSERT_LOCKED();
+
+ if (rt->rt_ifidx != ifp->if_index || !ISSET(rt->rt_flags, RTF_GATEWAY))
+ return (0);
+
+ /*
+ * If the gateway route is not stale, its associated cached
+ * is also not stale.
+ */
+ if (rt->rt_ifa->ifa_ifp != NULL)
+ return (0);
+
+ /* If we can fix the cached next hop entry, we can fix the ifa. */
+ if (rt_setgate(rt, rt->rt_gateway, ifp->if_rdomain) == 0) {
+ struct ifaddr *ifa = rt->rt_gwroute->rt_ifa;
+
+ ifafree(rt->rt_ifa);
+ ifa->ifa_refcnt++;
+ rt->rt_ifa = ifa;
+ }
+
+ return (0);
}
void
@@ -891,8 +935,7 @@ rtrequest_delete(struct rt_addrinfo *inf
if ((rt->rt_flags & RTF_CLONING) != 0)
rtflushclone(tableid, rt);
- rtfree(rt->rt_gwroute);
- rt->rt_gwroute = NULL;
+ rt_putgwroute(rt);
rtfree(rt->rt_parent);
rt->rt_parent = NULL;
@@ -1101,7 +1144,7 @@ rtrequest(int req, struct rt_addrinfo *i
tableid))) {
ifafree(ifa);
rtfree(rt->rt_parent);
- rtfree(rt->rt_gwroute);
+ rt_putgwroute(rt);
free(rt->rt_gateway, M_RTABLE, 0);
free(ndst, M_RTABLE, dlen);
pool_put(&rtentry_pool, rt);
@@ -1131,7 +1174,7 @@ rtrequest(int req, struct rt_addrinfo *i
if (error != 0) {
ifafree(ifa);
rtfree(rt->rt_parent);
- rtfree(rt->rt_gwroute);
+ rt_putgwroute(rt);
free(rt->rt_gateway, M_RTABLE, 0);
free(ndst, M_RTABLE, dlen);
pool_put(&rtentry_pool, rt);
@@ -1172,33 +1215,27 @@ rt_setgate(struct rtentry *rt, struct so
}
memmove(rt->rt_gateway, gate, glen);
- if (ISSET(rt->rt_flags, RTF_GATEWAY))
- rt_setgwroute(rt, rtableid);
+ if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
+ rt_putgwroute(rt);
+ return (rt_setgwroute(rt, rtableid));
+ }
return (0);
}
-int
-rt_checkgate(struct rtentry *rt, struct rtentry **rtp)
+/*
+ * Return the route entry containing the next hop link-layer
+ * address corresponding to ``rt''.
+ */
+struct rtentry *
+rt_getll(struct rtentry *rt)
{
- struct rtentry *rt0;
-
- KASSERT(rt != NULL);
-
- rt0 = rt;
-
- if (rt->rt_flags & RTF_GATEWAY) {
- if (rt->rt_gwroute == NULL)
- return (EHOSTUNREACH);
- rt = rt->rt_gwroute;
+ if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
+ KASSERT(rt->rt_gwroute != NULL);
+ return (rt->rt_gwroute);
}
- if (rt->rt_flags & RTF_REJECT)
- if (rt->rt_expire == 0 || time_uptime < rt->rt_expire)
- return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
-
- *rtp = rt;
- return (0);
+ return (rt);
}
void
@@ -1262,6 +1299,8 @@ rt_ifa_add(struct ifaddr *ifa, int flags
error = rtrequest(RTM_ADD, &info, prio, &rt, rtableid);
if (error == 0) {
+ unsigned int i;
+
/*
* A local route is created for every address configured
* on an interface, so use this information to notify
@@ -1271,6 +1310,18 @@ rt_ifa_add(struct ifaddr *ifa, int flags
rt_sendaddrmsg(rt, RTM_NEWADDR, ifa);
rt_sendmsg(rt, RTM_ADD, rtableid);
rtfree(rt);
+
+ /*
+ * Userland inserted routes stay in the table even
+ * if their corresponding ``ifa'' is no longer valid.
+ *
+ * Try to fix the stale RTF_GATEWAY entries in case
+ * their gateway match the newly inserted route.
+ */
+ for (i = 0; i <= RT_TABLEID_MAX; i++) {
+ rtable_walk(i, ifa->ifa_addr->sa_family,
+ rt_fixgwroute, ifp);
+ }
}
return (error);
}
@@ -1790,7 +1841,8 @@ rt_if_linkstate_change(struct rtentry *r
* from down interfaces so we have a chance to get
* new routes from a better source.
*/
- if (ISSET(rt->rt_flags, RTF_CLONED|RTF_DYNAMIC)) {
+ if (ISSET(rt->rt_flags, RTF_CLONED|RTF_DYNAMIC) &&
+ !ISSET(rt->rt_flags, RTF_CACHED)) {
int error;
if ((error = rtdeletemsg(rt, ifp, id)))
Index: net/route.h
===================================================================
RCS file: /cvs/src/sys/net/route.h,v
retrieving revision 1.141
diff -u -p -r1.141 route.h
--- net/route.h 13 Jul 2016 08:40:46 -0000 1.141
+++ net/route.h 19 Aug 2016 09:15:32 -0000
@@ -103,7 +103,12 @@ struct rtentry {
struct ifaddr *rt_ifa; /* the answer: interface addr to use */
caddr_t rt_llinfo; /* pointer to link level info cache or
to an MPLS structure */
- struct rtentry *rt_gwroute; /* implied entry for gatewayed routes */
+ union {
+ struct rtentry *_nh; /* implied entry for gatewayed routes */
+ unsigned int _ref; /* # gatewayed caching this route */
+ } RT_gw;
+#define rt_gwroute RT_gw._nh
+#define rt_cachecnt RT_gw._ref
struct rtentry *rt_parent; /* If cloned, parent of this route. */
LIST_HEAD(, rttimer) rt_timer; /* queue of timeouts for misc funcs */
struct rt_kmetrics rt_rmx; /* metrics used by rx'ing protocols */
@@ -139,6 +144,7 @@ struct rtentry {
#define RTF_ANNOUNCE RTF_PROTO2 /* announce L2 entry */
#define RTF_PROTO1 0x8000 /* protocol specific routing flag */
#define RTF_CLONED 0x10000 /* this is a cloned route */
+#define RTF_CACHED 0x20000 /* cached by a RTF_GATEWAY entry */
#define RTF_MPATH 0x40000 /* multipath route or operation */
#define RTF_MPLS 0x100000 /* MPLS additional infos */
#define RTF_LOCAL 0x200000 /* route to a local address */
@@ -227,6 +233,7 @@ struct rt_msghdr {
#define RTM_IFINFO 0xe /* iface going up/down etc. */
#define RTM_IFANNOUNCE 0xf /* iface arrival/departure */
#define RTM_DESYNC 0x10 /* route socket buffer overflow */
+#define RTM_INVALIDATE 0x11 /* Invalidate cache of L2 route */
#define RTV_MTU 0x1 /* init or lock _mtu */
#define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */
@@ -363,7 +370,7 @@ void rt_sendmsg(struct rtentry *, int,
void rt_sendaddrmsg(struct rtentry *, int, struct ifaddr *);
void rt_missmsg(int, struct rt_addrinfo *, int, uint8_t, u_int, int, u_int);
int rt_setgate(struct rtentry *, struct sockaddr *, u_int);
-int rt_checkgate(struct rtentry *, struct rtentry **);
+struct rtentry *rt_getll(struct rtentry *);
void rt_setmetrics(u_long, const struct rt_metrics *, struct rt_kmetrics *);
void rt_getmetrics(const struct rt_kmetrics *, struct rt_metrics *);
Index: net/rtsock.c
===================================================================
RCS file: /cvs/src/sys/net/rtsock.c,v
retrieving revision 1.194
diff -u -p -r1.194 rtsock.c
--- net/rtsock.c 11 Jul 2016 13:06:31 -0000 1.194
+++ net/rtsock.c 22 Aug 2016 10:04:11 -0000
@@ -99,6 +99,7 @@ struct walkarg {
int route_ctloutput(int, struct socket *, int, int, struct mbuf **);
void route_input(struct mbuf *m0, ...);
int route_arp_conflict(struct rt_addrinfo *, unsigned int);
+int route_cleargateway(struct rtentry *, void *, unsigned int);
struct mbuf *rt_msg1(int, struct rt_addrinfo *);
int rt_msg2(int, int, struct rt_addrinfo *, caddr_t,
@@ -556,7 +557,7 @@ route_output(struct mbuf *m, ...)
/* make sure that kernel-only bits are not set */
rtm->rtm_priority &= RTP_MASK;
- rtm->rtm_flags &= ~(RTF_DONE|RTF_CLONED);
+ rtm->rtm_flags &= ~(RTF_DONE|RTF_CLONED|RTF_CACHED);
rtm->rtm_fmask &= RTF_FMASK;
if (rtm->rtm_priority != 0) {
@@ -615,6 +616,33 @@ route_output(struct mbuf *m, ...)
}
break;
case RTM_DELETE:
+ if (!rtable_exists(tableid)) {
+ error = EAFNOSUPPORT;
+ goto flush;
+ }
+
+ rt = rtable_lookup(tableid, info.rti_info[RTAX_DST],
+ info.rti_info[RTAX_NETMASK], info.rti_info[RTAX_GATEWAY],
+ prio);
+
+ /*
+ * Invalidate the cache of automagically created and
+ * referenced L2 entries to make sure that ``rt_gwroute''
+ * pointer stays valid for other CPUs.
+ */
+ if ((rt != NULL) && (ISSET(rt->rt_flags, RTF_CACHED))) {
+ ifp = if_get(rt->rt_ifidx);
+ KASSERT(ifp != NULL);
+ ifp->if_rtrequest(ifp, RTM_INVALIDATE, rt);
+ if_put(ifp);
+ /* Reset the MTU of the gateway route. */
+ rtable_walk(tableid, rt_key(rt)->sa_family,
+ route_cleargateway, rt);
+ goto report;
+ }
+ rtfree(rt);
+ rt = NULL;
+
error = rtrequest(RTM_DELETE, &info, prio, &rt, tableid);
if (error == 0)
goto report;
@@ -746,12 +774,6 @@ report:
if ((error = rt_getifa(&info, tableid)) != 0)
goto flush;
ifa = info.rti_ifa;
- }
- if (info.rti_info[RTAX_GATEWAY] != NULL && (error =
- rt_setgate(rt, info.rti_info[RTAX_GATEWAY],
- tableid)))
- goto flush;
- if (ifa) {
if (rt->rt_ifa != ifa) {
ifp = if_get(rt->rt_ifidx);
KASSERT(ifp != NULL);
@@ -769,6 +791,10 @@ report:
#endif
}
}
+ if (info.rti_info[RTAX_GATEWAY] != NULL && (error =
+ rt_setgate(rt, info.rti_info[RTAX_GATEWAY],
+ tableid)))
+ goto flush;
#ifdef MPLS
if ((rtm->rtm_flags & RTF_MPLS) &&
info.rti_info[RTAX_SRC] != NULL) {
@@ -888,6 +914,18 @@ fail:
rp->rcb_proto.sp_family = PF_ROUTE;
return (error);
+}
+
+int
+route_cleargateway(struct rtentry *rt, void *arg, unsigned int rtableid)
+{
+ struct rtentry *nhrt = arg;
+
+ if (ISSET(rt->rt_flags, RTF_GATEWAY) && rt->rt_gwroute == nhrt &&
+ !ISSET(rt->rt_locks, RTV_MTU))
+ rt->rt_mtu = 0;
+
+ return (0);
}
/*
Index: netinet/if_ether.c
===================================================================
RCS file: /cvs/src/sys/netinet/if_ether.c,v
retrieving revision 1.220
diff -u -p -r1.220 if_ether.c
--- netinet/if_ether.c 14 Jul 2016 14:01:40 -0000 1.220
+++ netinet/if_ether.c 19 Aug 2016 09:15:32 -0000
@@ -78,6 +78,7 @@ int arpt_prune = (5 * 60); /* walk list
int arpt_keep = (20 * 60); /* once resolved, cache for 20 minutes */
int arpt_down = 20; /* once declared down, don't send for 20 secs */
+void arpinvalidate(struct rtentry *);
void arptfree(struct rtentry *);
void arptimer(void *);
struct rtentry *arplookup(struct in_addr *, int, int, unsigned int);
@@ -215,6 +216,12 @@ arp_rtrequest(struct ifnet *ifp, int req
rt->rt_flags &= ~RTF_LLINFO;
la_hold_total -= ml_purge(&la->la_ml);
pool_put(&arp_pool, la);
+ break;
+
+ case RTM_INVALIDATE:
+ if (!ISSET(rt->rt_flags, RTF_LOCAL))
+ arpinvalidate(rt);
+ break;
}
}
@@ -306,7 +313,6 @@ arpresolve(struct ifnet *ifp, struct rte
struct sockaddr_dl *sdl;
struct rtentry *rt = NULL;
char addr[INET_ADDRSTRLEN];
- int error;
if (m->m_flags & M_BCAST) { /* broadcast */
memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr));
@@ -317,10 +323,12 @@ arpresolve(struct ifnet *ifp, struct rte
return (0);
}
- error = rt_checkgate(rt0, &rt);
- if (error) {
+ rt = rt_getll(rt0);
+
+ if (ISSET(rt->rt_flags, RTF_REJECT) &&
+ (rt->rt_expire == 0 || time_uptime < rt->rt_expire)) {
m_freem(m);
- return (error);
+ return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
}
if (!ISSET(rt->rt_flags, RTF_LLINFO)) {
@@ -667,23 +675,31 @@ arpcache(struct ifnet *ifp, struct ether
return (0);
}
+
+void
+arpinvalidate(struct rtentry *rt)
+{
+ struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo;
+ struct sockaddr_dl *sdl = satosdl(rt->rt_gateway);
+
+ la_hold_total -= ml_purge(&la->la_ml);
+ sdl->sdl_alen = 0;
+ la->la_asked = 0;
+}
+
/*
* Free an arp entry.
*/
void
arptfree(struct rtentry *rt)
{
- struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo;
- struct sockaddr_dl *sdl = satosdl(rt->rt_gateway);
struct ifnet *ifp;
- ifp = if_get(rt->rt_ifidx);
- if ((sdl != NULL) && (sdl->sdl_family == AF_LINK)) {
- sdl->sdl_alen = 0;
- la->la_asked = 0;
- }
+ arpinvalidate(rt);
- if (!ISSET(rt->rt_flags, RTF_STATIC))
+ ifp = if_get(rt->rt_ifidx);
+ KASSERT(ifp != NULL);
+ if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED))
rtdeletemsg(rt, ifp, ifp->if_rdomain);
if_put(ifp);
}
Index: netinet6/nd6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/nd6.c,v
retrieving revision 1.188
diff -u -p -r1.188 nd6.c
--- netinet6/nd6.c 13 Jul 2016 08:40:46 -0000 1.188
+++ netinet6/nd6.c 22 Aug 2016 10:17:20 -0000
@@ -93,6 +93,7 @@ struct nd_prhead nd_prefix = { 0 };
int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
void nd6_slowtimo(void *);
+void nd6_invalidate(struct rtentry *);
struct llinfo_nd6 *nd6_free(struct rtentry *, int);
void nd6_llinfo_timer(void *);
@@ -711,6 +712,17 @@ nd6_is_addr_neighbor(struct sockaddr_in6
return (0);
}
+void
+nd6_invalidate(struct rtentry *rt)
+{
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+
+ m_freem(ln->ln_hold);
+ ln->ln_hold = NULL;
+ ln->ln_state = ND6_LLINFO_INCOMPLETE;
+ ln->ln_asked = 0;
+}
+
/*
* Free an nd6 llinfo entry.
* Since the function would cause significant changes in the kernel, DO NOT
@@ -814,7 +826,7 @@ nd6_free(struct rtentry *rt, int gc)
* caches, and disable the route entry not to be used in already
* cached routes.
*/
- if (!ISSET(rt->rt_flags, RTF_STATIC))
+ if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED))
rtdeletemsg(rt, ifp, ifp->if_rdomain);
splx(s);
@@ -1097,6 +1109,11 @@ nd6_rtrequest(struct ifnet *ifp, int req
rt->rt_flags &= ~RTF_LLINFO;
m_freem(ln->ln_hold);
pool_put(&nd6_pool, ln);
+ break;
+
+ case RTM_INVALIDATE:
+ nd6_invalidate(rt);
+ break;
}
}
@@ -1495,17 +1512,17 @@ nd6_resolve(struct ifnet *ifp, struct rt
struct sockaddr_dl *sdl;
struct rtentry *rt;
struct llinfo_nd6 *ln = NULL;
- int error;
if (m->m_flags & M_MCAST) {
ETHER_MAP_IPV6_MULTICAST(&satosin6(dst)->sin6_addr, desten);
return (0);
}
- error = rt_checkgate(rt0, &rt);
- if (error) {
+ rt = rt_getll(rt0);
+
+ if (ISSET(rt->rt_flags, RTF_REJECT)) {
m_freem(m);
- return (error);
+ return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
}
/*