On 09/06/19(Sun) 15:41, Martin Pieuchot wrote:
> [...] 
> Another way to prevent stack exhaustion would be to return a reference
> to any `rt' that needs to be deleted instead of deleting it in place.

Diff below does that by adding a new `prt' argument to rtable_walk().
I'm willing to create a manual for rtable_walk(9) if you agree with the
direction below.

The idea is to break the iteration as soon as we encounter a route entry
that we want to delete.  For this purpose I changed the 3 walkers to make
them return EEXIST.  This is just a convention, but coherency helps ;)

So with this approach the calls to rtdeletemsg() and rtrequest_delete()
are now in the same function as rtable_walk().  I decided to refcount
`rt'.  This is not strictly necessary as long as the NET_LOCK() is not
released between rtable_walk() and rtdeletemsg().  But we'll need it as
soon as we make the rtentry layer NET_LOCK() free :)

Obviously since now that we're breaking the iteration we have to roll
the loop ourself.  In all the error handling, EAFNOSUPPORT is the only
value which is returned by the rtable_*() layer.

I haven't converted mroute/mroute6 walkers yet.  I'm waiting for your
comments.

rt_ifa_purge() also contains an improvement, use the AF of the `ifa'
instead of iterating over all existing ones :)

While here I changed some function signatures to return the possible
error.  I'm not checking them for the moment.

Index: net/if_spppsubr.c
===================================================================
RCS file: /cvs/src/sys/net/if_spppsubr.c,v
retrieving revision 1.174
diff -u -p -r1.174 if_spppsubr.c
--- net/if_spppsubr.c   19 Feb 2018 08:59:52 -0000      1.174
+++ net/if_spppsubr.c   11 Jun 2019 15:30:10 -0000
@@ -4167,7 +4167,7 @@ sppp_update_gw(struct ifnet *ifp)
 
        /* update routing table */
        for (tid = 0; tid <= RT_TABLEID_MAX; tid++) {
-               rtable_walk(tid, AF_INET, sppp_update_gw_walker, ifp);
+               rtable_walk(tid, AF_INET, NULL, sppp_update_gw_walker, ifp);
        }
 }
 
Index: net/route.c
===================================================================
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.384
diff -u -p -r1.384 route.c
--- net/route.c 11 May 2019 16:47:02 -0000      1.384
+++ net/route.c 11 Jun 2019 16:24:39 -0000
@@ -156,7 +156,7 @@ void        rt_timer_init(void);
 int    rt_setgwroute(struct rtentry *, u_int);
 void   rt_putgwroute(struct rtentry *);
 int    rtflushclone1(struct rtentry *, void *, u_int);
-void   rtflushclone(unsigned int, struct rtentry *);
+int    rtflushclone(struct ifnet *ifp, struct rtentry *, unsigned int);
 int    rt_ifa_purge_walker(struct rtentry *, void *, unsigned int);
 struct rtentry *rt_match(struct sockaddr *, uint32_t *, int, unsigned int);
 int    rt_clone(struct rtentry **, struct sockaddr *, unsigned int);
@@ -701,7 +701,6 @@ rtflushclone1(struct rtentry *rt, void *
 {
        struct rtentry *cloningrt = arg;
        struct ifnet *ifp;
-       int error;
 
        if (!ISSET(rt->rt_flags, RTF_CLONED))
                return 0;
@@ -721,23 +720,35 @@ rtflushclone1(struct rtentry *rt, void *
        if (ifp == NULL)
                return 0;
 
-       error = rtdeletemsg(rt, ifp, id);
-       if (error == 0)
-               error = EAGAIN;
-
        if_put(ifp);
-       return error;
+       return EEXIST;
 }
 
-void
-rtflushclone(unsigned int rtableid, struct rtentry *parent)
+int
+rtflushclone(struct ifnet *ifp, struct rtentry *parent, unsigned int rtableid)
 {
+       struct rtentry *rt = NULL;
+       int error;
 
 #ifdef DIAGNOSTIC
        if (!parent || (parent->rt_flags & RTF_CLONING) == 0)
                panic("rtflushclone: called with a non-cloning route");
 #endif
-       rtable_walk(rtableid, rt_key(parent)->sa_family, rtflushclone1, parent);
+
+       do {
+               error = rtable_walk(rtableid, rt_key(parent)->sa_family, &rt,
+                   rtflushclone1, parent);
+               if (rt != NULL && error == EEXIST) {
+                       error = rtdeletemsg(rt, ifp, rtableid);
+                       if (error == 0)
+                               error = EAGAIN;
+               }
+               rtfree(rt);
+               rt = NULL;
+       } while (error == EAGAIN);
+
+       return error;
+
 }
 
 int
@@ -779,7 +790,7 @@ rtrequest_delete(struct rt_addrinfo *inf
 
        /* Clean up any cloned children. */
        if (ISSET(rt->rt_flags, RTF_CLONING))
-               rtflushclone(tableid, rt);
+               rtflushclone(ifp, rt, tableid);
 
        rtfree(rt->rt_parent);
        rt->rt_parent = NULL;
@@ -1241,12 +1252,13 @@ rt_ifa_dellocal(struct ifaddr *ifa)
 /*
  * Remove all addresses attached to ``ifa''.
  */
-void
+int
 rt_ifa_purge(struct ifaddr *ifa)
 {
        struct ifnet            *ifp = ifa->ifa_ifp;
+       struct rtentry          *rt = NULL;
        unsigned int             rtableid;
-       int                      i;
+       int                      error, af = ifa->ifa_addr->sa_family;
 
        KASSERT(ifp != NULL);
 
@@ -1254,27 +1266,38 @@ rt_ifa_purge(struct ifaddr *ifa)
                /* skip rtables that are not in the rdomain of the ifp */
                if (rtable_l2(rtableid) != ifp->if_rdomain)
                        continue;
-               for (i = 1; i <= AF_MAX; i++) {
-                       rtable_walk(rtableid, i, rt_ifa_purge_walker, ifa);
-               }
+
+               do {
+                       error = rtable_walk(rtableid, af, &rt,
+                           rt_ifa_purge_walker, ifa);
+                       if (rt != NULL && error == EEXIST) {
+                               error = rtdeletemsg(rt, ifp, rtableid);
+                               if (error == 0)
+                                       error = EAGAIN;
+                       }
+                       rtfree(rt);
+                       rt = NULL;
+               } while (error == EAGAIN);
+
+               if (error == EAFNOSUPPORT)
+                       error = 0;
+
+               if (error)
+                       break;
        }
+
+       return error;
 }
 
 int
 rt_ifa_purge_walker(struct rtentry *rt, void *vifa, unsigned int rtableid)
 {
        struct ifaddr           *ifa = vifa;
-       struct ifnet            *ifp = ifa->ifa_ifp;
-       int                      error;
 
-       if (rt->rt_ifa != ifa)
-               return (0);
-
-       if ((error = rtdeletemsg(rt, ifp, rtableid))) {
-               return (error);
-       }
+       if (rt->rt_ifa == ifa)
+               return EEXIST;
 
-       return (EAGAIN);
+       return 0;
 }
 
 /*
@@ -1596,23 +1619,42 @@ rtlabel_unref(u_int16_t id)
        }
 }
 
-void
+int
 rt_if_track(struct ifnet *ifp)
 {
-       int i;
-       u_int tid;
+       unsigned int rtableid;
+       struct rtentry *rt = NULL;
+       int i, error;
 
-       for (tid = 0; tid < rtmap_limit; tid++) {
+       for (rtableid = 0; rtableid < rtmap_limit; rtableid++) {
                /* skip rtables that are not in the rdomain of the ifp */
-               if (rtable_l2(tid) != ifp->if_rdomain)
+               if (rtable_l2(rtableid) != ifp->if_rdomain)
                        continue;
                for (i = 1; i <= AF_MAX; i++) {
-                       if (!rtable_mpath_capable(tid, i))
+                       if (!rtable_mpath_capable(rtableid, i))
                                continue;
 
-                       rtable_walk(tid, i, rt_if_linkstate_change, ifp);
+                       do {
+                               error = rtable_walk(rtableid, i, &rt,
+                                   rt_if_linkstate_change, ifp);
+                               if (rt != NULL && error == EEXIST) {
+                                       error = rtdeletemsg(rt, ifp, rtableid);
+                                       if (error == 0)
+                                               error = EAGAIN;
+                               }
+                               rtfree(rt);
+                               rt = NULL;
+                       } while (error == EAGAIN);
+
+                       if (error == EAFNOSUPPORT)
+                               error = 0;
+
+                       if (error)
+                               break;
                }
        }
+
+       return (error);
 }
 
 int
@@ -1647,9 +1689,7 @@ rt_if_linkstate_change(struct rtentry *r
                 */
                if (ISSET(rt->rt_flags, RTF_CLONED|RTF_DYNAMIC) &&
                    !ISSET(rt->rt_flags, RTF_CACHED|RTF_BFD)) {
-                       if ((error = rtdeletemsg(rt, ifp, id)))
-                               return (error);
-                       return (EAGAIN);
+                       return (EEXIST);
                }
 
                if (!ISSET(rt->rt_flags, RTF_UP))
@@ -1780,7 +1820,7 @@ int
 db_show_arptab(void)
 {
        db_printf("Route tree for AF_INET\n");
-       rtable_walk(0, AF_INET, db_show_rtentry, NULL);
+       rtable_walk(0, AF_INET, NULL, db_show_rtentry, NULL);
        return (0);
 }
 #endif /* DDB */
Index: net/route.h
===================================================================
RCS file: /cvs/src/sys/net/route.h,v
retrieving revision 1.175
diff -u -p -r1.175 route.h
--- net/route.h 28 Apr 2019 17:59:51 -0000      1.175
+++ net/route.h 11 Jun 2019 15:30:10 -0000
@@ -451,7 +451,7 @@ void         rtfree(struct rtentry *);
 
 int     rt_ifa_add(struct ifaddr *, int, struct sockaddr *, unsigned int);
 int     rt_ifa_del(struct ifaddr *, int, struct sockaddr *, unsigned int);
-void    rt_ifa_purge(struct ifaddr *);
+int     rt_ifa_purge(struct ifaddr *);
 int     rt_ifa_addlocal(struct ifaddr *);
 int     rt_ifa_dellocal(struct ifaddr *);
 void    rtredirect(struct sockaddr *, struct sockaddr *, struct sockaddr *, 
struct rtentry **, unsigned int);
@@ -459,7 +459,7 @@ int  rtrequest(int, struct rt_addrinfo *
             u_int);
 int     rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
             struct rtentry **, u_int);
-void    rt_if_track(struct ifnet *);
+int     rt_if_track(struct ifnet *);
 int     rt_if_linkstate_change(struct rtentry *, void *, u_int);
 int     rtdeletemsg(struct rtentry *, struct ifnet *, u_int);
 #endif /* _KERNEL */
Index: net/rtable.c
===================================================================
RCS file: /cvs/src/sys/net/rtable.c,v
retrieving revision 1.68
diff -u -p -r1.68 rtable.c
--- net/rtable.c        5 Mar 2019 19:07:56 -0000       1.68
+++ net/rtable.c        11 Jun 2019 16:01:39 -0000
@@ -664,6 +664,7 @@ leave:
 struct rtable_walk_cookie {
        int             (*rwc_func)(struct rtentry *, void *, unsigned int);
        void             *rwc_arg;
+       struct rtentry  **rwc_prt;
        unsigned int      rwc_rid;
 };
 
@@ -679,16 +680,21 @@ rtable_walk_helper(struct art_node *an, 
        int                              error = 0;
 
        SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next) {
-               if ((error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid)))
+               error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid);
+               if (error != 0)
                        break;
        }
+       if (rwc->rwc_prt != NULL && rt != NULL) {
+               rtref(rt);
+               *rwc->rwc_prt = rt;
+       }
        SRPL_LEAVE(&sr);
 
        return (error);
 }
 
 int
-rtable_walk(unsigned int rtableid, sa_family_t af,
+rtable_walk(unsigned int rtableid, sa_family_t af, struct rtentry **prt,
     int (*func)(struct rtentry *, void *, unsigned int), void *arg)
 {
        struct art_root                 *ar;
@@ -701,10 +707,10 @@ rtable_walk(unsigned int rtableid, sa_fa
 
        rwc.rwc_func = func;
        rwc.rwc_arg = arg;
+       rwc.rwc_prt = prt;
        rwc.rwc_rid = rtableid;
 
-       while ((error = art_walk(ar, rtable_walk_helper, &rwc)) == EAGAIN)
-               continue;
+       error = art_walk(ar, rtable_walk_helper, &rwc);
 
        return (error);
 }
Index: net/rtable.h
===================================================================
RCS file: /cvs/src/sys/net/rtable.h,v
retrieving revision 1.23
diff -u -p -r1.23 rtable.h
--- net/rtable.h        28 Apr 2019 17:59:51 -0000      1.23
+++ net/rtable.h        11 Jun 2019 15:30:10 -0000
@@ -48,7 +48,7 @@ int            rtable_insert(unsigned int, struct
                     struct rtentry *);
 int             rtable_delete(unsigned int, struct sockaddr *,
                     struct sockaddr *, struct rtentry *);
-int             rtable_walk(unsigned int, sa_family_t,
+int             rtable_walk(unsigned int, sa_family_t, struct rtentry **,
                     int (*)(struct rtentry *, void *, unsigned int), void *);
 
 int             rtable_mpath_capable(unsigned int, sa_family_t);
Index: net/rtsock.c
===================================================================
RCS file: /cvs/src/sys/net/rtsock.c,v
retrieving revision 1.287
diff -u -p -r1.287 rtsock.c
--- net/rtsock.c        5 Jun 2019 12:53:43 -0000       1.287
+++ net/rtsock.c        11 Jun 2019 15:30:10 -0000
@@ -931,7 +931,7 @@ rtm_output(struct rt_msghdr *rtm, struct
                        NET_LOCK();
                        ifp->if_rtrequest(ifp, RTM_INVALIDATE, rt);
                        /* Reset the MTU of the gateway route. */
-                       rtable_walk(tableid, rt_key(rt)->sa_family,
+                       rtable_walk(tableid, rt_key(rt)->sa_family, NULL,
                            route_cleargateway, rt);
                        NET_UNLOCK();
                        if_put(ifp);
@@ -1911,7 +1911,8 @@ sysctl_rtable(int *name, u_int namelen, 
                        if (af != 0 && af != i)
                                continue;
 
-                       error = rtable_walk(tableid, i, sysctl_dumpentry, &w);
+                       error = rtable_walk(tableid, i, NULL, sysctl_dumpentry,
+                           &w);
                        if (error == EAFNOSUPPORT)
                                error = 0;
                        if (error)
Index: netinet/ip_mroute.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_mroute.c,v
retrieving revision 1.126
diff -u -p -r1.126 ip_mroute.c
--- netinet/ip_mroute.c 4 Jun 2019 16:11:13 -0000       1.126
+++ netinet/ip_mroute.c 11 Jun 2019 15:30:10 -0000
@@ -476,8 +476,10 @@ mrt_sysctl_mfc(void *oldp, size_t *oldle
        msa.msa_len = *oldlenp;
        msa.msa_needed = 0;
 
-       for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++)
-               rtable_walk(rtableid, AF_INET, mrt_rtwalk_mfcsysctl, &msa);
+       for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++) {
+               rtable_walk(rtableid, AF_INET, NULL, mrt_rtwalk_mfcsysctl,
+                   &msa);
+       }
 
        if (msa.msa_minfos != NULL && msa.msa_needed > 0 &&
            (error = copyout(msa.msa_minfos, oldp, msa.msa_needed)) != 0) {
@@ -549,7 +551,7 @@ ip_mrouter_done(struct socket *so)
        NET_ASSERT_LOCKED();
 
        /* Delete all remaining installed multicast routes. */
-       rtable_walk(rtableid, AF_INET, mrouter_rtwalk_delete, NULL);
+       rtable_walk(rtableid, AF_INET, NULL, mrouter_rtwalk_delete, NULL);
 
        TAILQ_FOREACH(ifp, &ifnet, if_list) {
                if (ifp->if_rdomain != rtableid)
Index: netinet6/ip6_mroute.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_mroute.c,v
retrieving revision 1.119
diff -u -p -r1.119 ip6_mroute.c
--- netinet6/ip6_mroute.c       4 Jun 2019 16:11:13 -0000       1.119
+++ netinet6/ip6_mroute.c       11 Jun 2019 15:30:10 -0000
@@ -454,8 +454,10 @@ mrt6_sysctl_mfc(void *oldp, size_t *oldl
        msa.ms6a_len = *oldlenp;
        msa.ms6a_needed = 0;
 
-       for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++)
-               rtable_walk(rtableid, AF_INET6, mrt6_rtwalk_mf6csysctl, &msa);
+       for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++) {
+               rtable_walk(rtableid, AF_INET6, NULL, mrt6_rtwalk_mf6csysctl,
+                   &msa);
+       }
 
        if (msa.ms6a_minfos != NULL && msa.ms6a_needed > 0 &&
            (error = copyout(msa.ms6a_minfos, oldp, msa.ms6a_needed)) != 0) {
@@ -523,7 +525,7 @@ ip6_mrouter_done(struct socket *so)
        NET_ASSERT_LOCKED();
 
        /* Delete all remaining installed multicast routes. */
-       rtable_walk(rtableid, AF_INET6, mrouter6_rtwalk_delete, NULL);
+       rtable_walk(rtableid, AF_INET6, NULL, mrouter6_rtwalk_delete, NULL);
 
        /* Unregister all interfaces in the domain. */
        TAILQ_FOREACH(ifp, &ifnet, if_list) {
Index: netinet6/nd6.h
===================================================================
RCS file: /cvs/src/sys/netinet6/nd6.h,v
retrieving revision 1.74
diff -u -p -r1.74 nd6.h
--- netinet6/nd6.h      27 Nov 2017 15:41:30 -0000      1.74
+++ netinet6/nd6.h      11 Jun 2019 15:30:10 -0000
@@ -193,7 +193,7 @@ void nd6_dad_stop(struct ifaddr *);
 void nd6_rtr_cache(struct mbuf *, int, int, int);
 
 int in6_ifdel(struct ifnet *, struct in6_addr *);
-void rt6_flush(struct in6_addr *, struct ifnet *);
+int rt6_flush(struct in6_addr *, struct ifnet *);
 
 void nd6_expire_timer_update(struct in6_ifaddr *);
 #endif /* _KERNEL */
Index: netinet6/nd6_rtr.c
===================================================================
RCS file: /cvs/src/sys/netinet6/nd6_rtr.c,v
retrieving revision 1.166
diff -u -p -r1.166 nd6_rtr.c
--- netinet6/nd6_rtr.c  23 Jan 2018 22:06:42 -0000      1.166
+++ netinet6/nd6_rtr.c  11 Jun 2019 16:26:43 -0000
@@ -173,28 +173,48 @@ nd6_rtr_cache(struct mbuf *m, int off, i
  * it shouldn't be called when acting as a router.
  * The gateway must already contain KAME's hack for link-local scope.
  */
-void
+int
 rt6_flush(struct in6_addr *gateway, struct ifnet *ifp)
 {
+       struct rt_addrinfo info;
+       struct sockaddr_in6 sa_mask;
+       struct rtentry *rt = NULL;
+       int error;
+
        NET_ASSERT_LOCKED();
 
        /* We'll care only link-local addresses */
        if (!IN6_IS_ADDR_LINKLOCAL(gateway))
-               return;
+               return (0);
 
        KASSERT(gateway->s6_addr16[1] != 0);
 
-       rtable_walk(ifp->if_rdomain, AF_INET6, rt6_deleteroute, gateway);
+       do {
+               error = rtable_walk(ifp->if_rdomain, AF_INET6, &rt,
+                   rt6_deleteroute, gateway);
+               if (rt != NULL && error == EEXIST) {
+                       memset(&info, 0, sizeof(info));
+                       info.rti_flags =  rt->rt_flags;
+                       info.rti_info[RTAX_DST] = rt_key(rt);
+                       info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+                       info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt,
+                           &sa_mask);
+                       error = rtrequest_delete(&info, RTP_ANY, ifp, NULL,
+                           ifp->if_rdomain);
+                       if (error == 0)
+                               error = EAGAIN;
+               }
+               rtfree(rt);
+               rt = NULL;
+       } while (error == EAGAIN);
+
+       return (error);
 }
 
 int
 rt6_deleteroute(struct rtentry *rt, void *arg, unsigned int id)
 {
-       struct ifnet *ifp;
-       struct rt_addrinfo info;
        struct in6_addr *gate = (struct in6_addr *)arg;
-       struct sockaddr_in6 sa_mask;
-       int error = 0;
 
        if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6)
                return (0);
@@ -217,16 +237,5 @@ rt6_deleteroute(struct rtentry *rt, void
        if ((rt->rt_flags & RTF_HOST) == 0)
                return (0);
 
-       ifp = if_get(rt->rt_ifidx);
-       if (ifp != NULL) {
-               bzero(&info, sizeof(info));
-               info.rti_flags =  rt->rt_flags;
-               info.rti_info[RTAX_DST] = rt_key(rt);
-               info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
-               info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask);
-               error = rtrequest_delete(&info, RTP_ANY, ifp, NULL, id);
-       }
-       if_put(ifp);
-
-       return (error != 0 ? error : EAGAIN);
+       return (EEXIST);
 }

Reply via email to