On 15/08/16(Mon) 13:52, Martin Pieuchot wrote: > On 11/08/16(Thu) 16:04, Martin Pieuchot wrote: > > One of the remaining SMP issue with our routing table usage is to > > guarantee that the L2 entry referenced by a RTF_GATEWAY route via > > the ``rt_gwroute'' pointer wont be replaced/invalidated by another > > CPU while we are filling the address field of an Ethernet frame. > > > > The most efficient way, performance wise, to do that is to make the > > ``rt_gwroute'' pointer immutable during the lifetime of a RTF_GATEWAY > > route. If we know that this pointer won't change and that the memory > > it points to won't be freed as long as a CPU has reference to one of > > the RTF_GATEWAY routes, we don't need any special protection inside > > arp_resolve() and nd6_resolve(). > > > > Diff below achieve that by always resolving the next-hop entry before > > inserting the corresponding RTF_GATEWAY route in the tree. In other > > words rt_setgwroute() is no longer called in the sending path. > > > > To guarantee that a cached route won't be deleted while it is still > > referenced a new flag, RTF_CACHED, is added to the route. arp(8) and > > ndp(8) treat entry with this flag like RTF_LOCAL. > > > > Removing rt_setgwroute() from the sending path means that inserting a > > RTF_GATEWAY route will now fail if the next-hop cannot be resolve. This > > might introduce regression for some setups but I see that has an > > improvement since the kernel no longer let you add a route that cannot > > be used. > > It also mean that stale routes need to be fixed whenever a new address > > is configured. The diff does that and also plug an ifa leak that was > > happening when the same address is configured twice on an ifa. > > > > Note that even with this diff there are *still* some MP-safeness issue > > due to stale routes, they will be address in a later diff. > > Updated diff that: > > - Fix a panic reported by Hrvoje Popovski > > - Properly invalidate ARP/NDP entry when issuing a RTM_DELETE from > userland, as discussed with bluhm@. For that I introduced > RTM_INVALIDATE and use it instead of deleting L2 entries. With > this we no longer generate deletion/insertion for L2 entries, of > course that include RTF_CACHED. > > - This has been tested with a pppoe(4) setup to make sure that > inserting a route with a magic 0.0.0.1 gateway works, pointed out > by claudio@. > > More tests, comments, ok are welcome.
Guys I'd rather move forward with that before g2k16, so comments are welcome such that I can split the diff and commit it. If people really have good arguments for using locks/SRPs in the hot in order to keep the existing behavior I'd like to hear them. > Index: net/route.c > =================================================================== > RCS file: /cvs/src/sys/net/route.c,v > retrieving revision 1.313 > diff -u -p -r1.313 route.c > --- net/route.c 22 Jul 2016 11:03:30 -0000 1.313 > +++ net/route.c 15 Aug 2016 08:58:39 -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); > } > @@ -267,8 +268,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 > @@ -349,16 +348,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''. > @@ -367,44 +360,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 > @@ -412,13 +396,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); > } > > /* > @@ -429,10 +407,75 @@ 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(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 > @@ -889,8 +932,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; > @@ -1099,7 +1141,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); > @@ -1129,7 +1171,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); > @@ -1170,33 +1212,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 > @@ -1260,6 +1296,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 > @@ -1269,6 +1307,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); > } > @@ -1788,7 +1838,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 15 Aug 2016 08:11:14 -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 15 Aug 2016 08:42:49 -0000 > @@ -556,7 +556,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,7 +615,27 @@ route_output(struct mbuf *m, ...) > } > break; > case RTM_DELETE: > - error = rtrequest(RTM_DELETE, &info, prio, &rt, tableid); > + 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); > + > + if (rt != NULL) { > + /* Only invalidate the cache of L2 entries */ > + if (ISSET(rt->rt_flags, RTF_LLINFO)) { > + ifp = if_get(rt->rt_ifidx); > + KASSERT(ifp != NULL); > + ifp->if_rtrequest(ifp, RTM_INVALIDATE, rt); > + if_put(ifp); > + goto report; > + } > + } > + > + error = rtrequest(RTM_DELETE, &info, prio, NULL, tableid); > if (error == 0) > goto report; > break; > 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 15 Aug 2016 08:25:35 -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 15 Aug 2016 08:26:22 -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,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); > > /* > * Address resolution or Neighbor Unreachability Detection >