On Wed, Dec 16, 2015 at 09:46:26PM +0100, Alexander Bluhm wrote:
> 10.188.70.17       fe:e1:ba:d0:d5:6d  UHLS       0        3     -     8 vio0 

This is this route that crashed the machine when the arp entry expired.

When I move the rtref()/rtfree() calls into rtdeletemsg() it also
protects the calls from arptfree().

> __assert() at __assert+0x25
> rtfree() at rtfree+0x11e
> rtable_delete() at rtable_delete+0x5d
> rt_delete() at rt_delete+0x6e
> rtdeletemsg() at rtdeletemsg+0x2d
> arptfree() at arptfree+0x4f
> arptimer() at arptimer+0x63

After rt_delete() has called rtable_delete(), the route is still
used and should not be destroyed.

bluhm

Index: net/route.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
retrieving revision 1.292
diff -u -p -r1.292 route.c
--- net/route.c 11 Dec 2015 08:58:23 -0000      1.292
+++ net/route.c 16 Dec 2015 22:42:34 -0000
@@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t
 
 struct ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr *,
                    u_int);
-int    rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
-           struct rtentry **, u_int);
+int    rt_delete(struct rtentry *, struct ifnet *, unsigned int);
 
 #ifdef DDB
 void   db_print_sa(struct sockaddr *);
@@ -613,34 +612,22 @@ out:
 }
 
 /*
- * Delete a route and generate a message
+ * Delete a route and generate a message.  The caller must hold a reference
+ * on ``rt''.
  */
 int
-rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
+rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
 {
        int                     error;
-       struct rt_addrinfo      info;
-       unsigned int            ifidx;
-       struct sockaddr_in6     sa_mask;
 
        KASSERT(rt->rt_ifidx == ifp->if_index);
 
-       /*
-        * Request the new route so that the entry is not actually
-        * deleted.  That will allow the information being reported to
-        * be accurate (and consistent with route_output()).
-        */
-       bzero((caddr_t)&info, sizeof(info));
-       info.rti_info[RTAX_DST] = rt_key(rt);
-       if (!ISSET(rt->rt_flags, RTF_HOST))
-               info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask);
-       info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
-       info.rti_flags = rt->rt_flags;
-       ifidx = rt->rt_ifidx;
-       error = rtrequest_delete(&info, rt->rt_priority, ifp, &rt, tableid);
-       rt_missmsg(RTM_DELETE, &info, info.rti_flags, ifidx, error, tableid);
+       rtref(rt);
+       error = rt_delete(rt, ifp, rtableid);
        if (error == 0)
-               rtfree(rt);
+               rt_sendmsg(rt, RTM_DELETE, rtableid);
+       rtfree(rt);
+
        return (error);
 }
 
@@ -671,10 +658,10 @@ rtflushclone1(struct rtentry *rt, void *
         * the routes are being purged by rt_if_remove().
         */
        if (ifp == NULL)
-               return 0;
+               return 0;
 
        if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent))
-               rtdeletemsg(rt, ifp, id);
+               rtdeletemsg(rt, ifp, id);
 
        if_put(ifp);
        return 0;
@@ -818,60 +805,20 @@ rt_getifa(struct rt_addrinfo *info, u_in
 }
 
 int
-rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
-    struct rtentry **ret_nrt, u_int tableid)
+rt_delete(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
 {
-       struct rtentry  *rt;
-       int              error;
-
-       splsoftassert(IPL_SOFTNET);
-
-       if (!rtable_exists(tableid))
-               return (EAFNOSUPPORT);
-       rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
-           info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], prio);
-       if (rt == NULL)
-               return (ESRCH);
-
-       /* Make sure that's the route the caller want to delete. */
-       if (ifp != NULL && ifp->if_index != rt->rt_ifidx) {
-               rtfree(rt);
-               return (ESRCH);
-       }
+       struct sockaddr_in6      sa_mask;
 
-#ifndef SMALL_KERNEL
-       /*
-        * If we got multipath routes, we require users to specify
-        * a matching gateway.
-        */
-       if ((rt->rt_flags & RTF_MPATH) &&
-           info->rti_info[RTAX_GATEWAY] == NULL) {
-               rtfree(rt);
-               return (ESRCH);
-       }
-#endif
+       KASSERT(ifp->if_index == rt->rt_ifidx);
 
-       /*
-        * Since RTP_LOCAL cannot be set by userland, make
-        * sure that local routes are only modified by the
-        * kernel.
-        */
-       if ((rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST)) &&
-           prio != RTP_LOCAL) {
-               rtfree(rt);
-               return (EINVAL);
-       }
+       splsoftassert(IPL_SOFTNET);
 
-       error = rtable_delete(tableid, info->rti_info[RTAX_DST],
-           info->rti_info[RTAX_NETMASK], rt);
-       if (error != 0) {
-               rtfree(rt);
+       if (rtable_delete(rtableid, rt_key(rt), rt_plen2mask(rt, &sa_mask), rt))
                return (ESRCH);
-       }
 
        /* clean up any cloned children */
        if ((rt->rt_flags & RTF_CLONING) != 0)
-               rtflushclone(tableid, rt);
+               rtflushclone(rtableid, rt);
 
        rtfree(rt->rt_gwroute);
        rt->rt_gwroute = NULL;
@@ -881,23 +828,10 @@ rtrequest_delete(struct rt_addrinfo *inf
 
        rt->rt_flags &= ~RTF_UP;
 
-       if (ifp == NULL) {
-               ifp = if_get(rt->rt_ifidx);
-               KASSERT(ifp != NULL);
-               ifp->if_rtrequest(ifp, RTM_DELETE, rt);
-               if_put(ifp);
-       } else {
-               KASSERT(ifp->if_index == rt->rt_ifidx);
-               ifp->if_rtrequest(ifp, RTM_DELETE, rt);
-       }
+       ifp->if_rtrequest(ifp, RTM_DELETE, rt);
 
        atomic_inc_int(&rttrash);
 
-       if (ret_nrt != NULL)
-               *ret_nrt = rt;
-       else
-               rtfree(rt);
-
        return (0);
 }
 
@@ -924,9 +858,50 @@ rtrequest(int req, struct rt_addrinfo *i
                info->rti_info[RTAX_NETMASK] = NULL;
        switch (req) {
        case RTM_DELETE:
-               error = rtrequest_delete(info, prio, NULL, ret_nrt, tableid);
-               if (error)
+               rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
+                   info->rti_info[RTAX_NETMASK],
+                   info->rti_info[RTAX_GATEWAY], prio);
+               if (rt == NULL)
+                       return (ESRCH);
+
+#ifndef SMALL_KERNEL
+               /*
+                * If we got multipath routes, we require users to specify
+                * a matching gateway.
+                */
+               if (ISSET(rt->rt_flags, RTF_MPATH) &&
+                   info->rti_info[RTAX_GATEWAY] == NULL) {
+                       rtfree(rt);
+                       return (ESRCH);
+               }
+#endif
+
+               /*
+                * Since RTP_LOCAL cannot be set by userland, make
+                * sure that local routes are only modified by the
+                * kernel.
+                */
+               if (ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST) &&
+                   prio != RTP_LOCAL) {
+                       rtfree(rt);
+                       return (EINVAL);
+               }
+
+               ifp = if_get(rt->rt_ifidx);
+               KASSERT(ifp != NULL);
+               error = rt_delete(rt, ifp, tableid);
+               if_put(ifp);
+
+               if (error) {
+                       rtfree(rt);
                        return (error);
+               }
+
+               if (ret_nrt != NULL)
+                       *ret_nrt = rt;
+               else
+                       rtfree(rt);
+
                break;
 
        case RTM_RESOLVE:
@@ -1265,8 +1240,8 @@ rt_ifa_del(struct ifaddr *ifa, int flags
        struct rtentry          *rt;
        struct mbuf             *m = NULL;
        struct sockaddr         *deldst;
-       struct rt_addrinfo       info;
-       struct sockaddr_rtlabel  sa_rl;
+       struct sockaddr         *mask = NULL;
+       struct sockaddr         *gateway = NULL;
        unsigned int             rtableid = ifp->if_rdomain;
        uint8_t                  prio = ifp->if_priority + RTP_STATIC;
        int                      error;
@@ -1286,16 +1261,10 @@ rt_ifa_del(struct ifaddr *ifa, int flags
                dst = deldst;
        }
 
-       memset(&info, 0, sizeof(info));
-       info.rti_ifa = ifa;
-       info.rti_flags = flags;
-       info.rti_info[RTAX_DST] = dst;
        if ((flags & RTF_LLINFO) == 0)
-               info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
-       info.rti_info[RTAX_LABEL] = rtlabel_id2sa(ifp->if_rtlabelid, &sa_rl);
-
+               gateway = ifa->ifa_addr;
        if ((flags & RTF_HOST) == 0)
-               info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
+               mask = ifa->ifa_netmask;
 
        if (flags & (RTF_LOCAL|RTF_BROADCAST))
                prio = RTP_LOCAL;
@@ -1303,13 +1272,32 @@ rt_ifa_del(struct ifaddr *ifa, int flags
        if (flags & RTF_CONNECTED)
                prio = RTP_CONNECTED;
 
-       error = rtrequest_delete(&info, prio, ifp, &rt, rtableid);
-       if (error == 0) {
-               rt_sendmsg(rt, RTM_DELETE, rtableid);
-               if (flags & RTF_LOCAL)
-                       rt_sendaddrmsg(rt, RTM_DELADDR);
+       rt = rtable_lookup(rtableid, dst, mask, gateway, prio);
+       if (rt == NULL)
+               return (ESRCH);
+
+#ifndef SMALL_KERNEL
+       /*
+        * If we got multipath routes, we require users to specify
+        * a matching gateway.
+        */
+       if (ISSET(rt->rt_flags, RTF_MPATH) && gateway == NULL) {
                rtfree(rt);
+               return (ESRCH);
        }
+#endif
+
+       /* Make sure that's the route the caller want to delete. */
+       if (rt->rt_ifidx != ifp->if_index) {
+               rtfree(rt);
+               return (ESRCH);
+       }
+
+       error = rtdeletemsg(rt, ifp, rtableid);
+       if (error == 0 && (flags & RTF_LOCAL))
+               rt_sendaddrmsg(rt, RTM_DELADDR);
+       rtfree(rt);
+
        if (m != NULL)
                m_free(m);
 
@@ -1721,7 +1709,7 @@ rt_if_remove_rtdelete(struct rtentry *rt
        struct ifnet    *ifp = vifp;
 
        if (rt->rt_ifidx == ifp->if_index) {
-               int     cloning = (rt->rt_flags & RTF_CLONING);
+               int     cloning = ISSET(rt->rt_flags, RTF_CLONING);
 
                if (rtdeletemsg(rt, ifp, id) == 0 && cloning)
                        return (EAGAIN);

Reply via email to