On Tue, Jun 11, 2019 at 01:29:26PM -0300, Martin Pieuchot wrote:
> 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.
OK bluhm@
> 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);
> }