Make the ``rt_gwroute'' pointer of RTF_GATEWAY entries immutable.
authormpi <mpi@openbsd.org>
Mon, 22 Aug 2016 16:01:52 +0000 (16:01 +0000)
committermpi <mpi@openbsd.org>
Mon, 22 Aug 2016 16:01:52 +0000 (16:01 +0000)
This means that no protection is needed to guarantee that the next hop
route wont be modified by CPU1 while CPU0 is dereferencing it in a L2
resolution functions.

While here also fix an ``ifa'' leak resulting in RTF_GATEWAY being always
invalid.

dlg@ likes it, inputs and ok bluhm@

sys/net/route.c
sys/net/route.h
sys/net/rtsock.c
sys/netinet/if_ether.c
sys/netinet6/nd6.c

index 1758bb3..eed062d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: route.c,v 1.315 2016/08/19 07:12:54 mpi Exp $ */
+/*     $OpenBSD: route.c,v 1.316 2016/08/22 16:01:52 mpi Exp $ */
 /*     $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $      */
 
 /*
@@ -153,7 +153,9 @@ struct pool         rtentry_pool;   /* pool for rtentry structures */
 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 *src, int flags, unsigned int tableid)
        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 sockaddr *dst, uint32_t *src)
 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, unsigned int rtableid)
  * 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 rtableid)
         */
        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 rtableid)
                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 *info, u_int8_t prio, struct ifnet *ifp,
        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 *info, u_int8_t prio,
                    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 *info, u_int8_t prio,
                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 sockaddr *gate, u_int rtableid)
        }
        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, struct sockaddr *dst)
 
        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, struct sockaddr *dst)
                        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 *rt, void *arg, u_int id)
                         * 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 0e3eaf5..425341e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: route.h,v 1.141 2016/07/13 08:40:46 mpi Exp $ */
+/*     $OpenBSD: route.h,v 1.142 2016/08/22 16:01:52 mpi Exp $ */
 /*     $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $       */
 
 /*
@@ -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, u_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 af1147f..6c79d2e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rtsock.c,v 1.194 2016/07/11 13:06:31 bluhm Exp $      */
+/*     $OpenBSD: rtsock.c,v 1.195 2016/08/22 16:01:52 mpi Exp $        */
 /*     $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd Exp $  */
 
 /*
@@ -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) {
@@ -890,6 +916,18 @@ fail:
        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);
+}
+
 /*
  * Check if the user request to insert an ARP entry does not conflict
  * with existing ones.
index 1c4839d..cab6f57 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_ether.c,v 1.220 2016/07/14 14:01:40 mpi Exp $      */
+/*     $OpenBSD: if_ether.c,v 1.221 2016/08/22 16:01:52 mpi Exp $      */
 /*     $NetBSD: if_ether.c,v 1.31 1996/05/11 12:59:58 mycroft Exp $    */
 
 /*
@@ -78,6 +78,7 @@ int   arpt_prune = (5 * 60);  /* walk list every 5 minutes */
 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, struct rtentry *rt)
                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 rtentry *rt0, struct mbuf *m,
        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 rtentry *rt0, struct mbuf *m,
                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_arp *ea, struct rtentry *rt)
 
        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 66676cc..6faced7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: nd6.c,v 1.189 2016/08/22 10:33:22 mpi Exp $   */
+/*     $OpenBSD: nd6.c,v 1.190 2016/08/22 16:01:52 mpi Exp $   */
 /*     $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $   */
 
 /*
@@ -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 *addr, struct ifnet *ifp)
        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, struct rtentry *rt)
                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,18 @@ nd6_resolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
        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) &&
+           (rt->rt_expire == 0 || time_uptime < rt->rt_expire)) {
                m_freem(m);
-               return (error);
+               return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
        }
 
        /*