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.

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

Reply via email to