Index: if_ethersubr.c =================================================================== RCS file: /cvs/src/sys/net/if_ethersubr.c,v retrieving revision 1.266 diff -u -p -r1.266 if_ethersubr.c --- if_ethersubr.c 22 Jul 2020 02:16:01 -0000 1.266 +++ if_ethersubr.c 27 Jul 2020 06:14:46 -0000 @@ -203,6 +203,7 @@ ether_resolve(struct ifnet *ifp, struct struct rtentry *rt, struct ether_header *eh) { struct arpcom *ac = (struct arpcom *)ifp; + struct rtentry *crt = NULL; sa_family_t af = dst->sa_family; int error = 0; @@ -220,11 +221,18 @@ ether_resolve(struct ifnet *ifp, struct } #endif + if (rt != NULL && ISSET(rt->rt_flags, RTF_CLONING)) { + error = rt_clone(&crt, rt, dst, ifp->if_rdomain); + if (error != 0) + senderr(error); + rt = crt; + } + switch (af) { case AF_INET: error = arpresolve(ifp, rt, m, dst, eh->ether_dhost); if (error) - return (error); + goto error; eh->ether_type = htons(ETHERTYPE_IP); /* If broadcasting on a simplex interface, loopback a copy */ @@ -243,7 +251,7 @@ ether_resolve(struct ifnet *ifp, struct case AF_INET6: error = nd6_resolve(ifp, rt, m, dst, eh->ether_dhost); if (error) - return (error); + goto error; eh->ether_type = htons(ETHERTYPE_IPV6); break; #endif @@ -269,13 +277,13 @@ ether_resolve(struct ifnet *ifp, struct case AF_INET6: error = nd6_resolve(ifp, rt, m, dst, eh->ether_dhost); if (error) - return (error); + goto error; break; #endif case AF_INET: error = arpresolve(ifp, rt, m, dst, eh->ether_dhost); if (error) - return (error); + goto error; break; default: senderr(EHOSTUNREACH); @@ -290,7 +298,7 @@ ether_resolve(struct ifnet *ifp, struct case pseudo_AF_HDRCMPLT: /* take the whole header from the sa */ memcpy(eh, dst->sa_data, sizeof(*eh)); - return (0); + goto done; case AF_UNSPEC: /* take the dst and type from the sa, but get src below */ @@ -304,10 +312,14 @@ ether_resolve(struct ifnet *ifp, struct memcpy(eh->ether_shost, ac->ac_enaddr, sizeof(eh->ether_shost)); +done: + rtfree(crt); return (0); bad: m_freem(m); +error: + rtfree(crt); return (error); } Index: route.c =================================================================== RCS file: /cvs/src/sys/net/route.c,v retrieving revision 1.394 diff -u -p -r1.394 route.c --- route.c 24 Jun 2020 22:03:43 -0000 1.394 +++ route.c 27 Jul 2020 06:14:46 -0000 @@ -159,7 +159,6 @@ int rtflushclone1(struct rtentry *, void int rtflushclone(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); struct sockaddr *rt_plentosa(sa_family_t, int, struct sockaddr_in6 *); static int rt_copysa(struct sockaddr *, struct sockaddr *, struct sockaddr **); @@ -238,22 +237,32 @@ rt_match(struct sockaddr *dst, uint32_t return (NULL); } - if (ISSET(rt->rt_flags, RTF_CLONING) && ISSET(flags, RT_RESOLVE)) - rt_clone(&rt, dst, tableid); - rt->rt_use++; return (rt); } int -rt_clone(struct rtentry **rtp, struct sockaddr *dst, unsigned int rtableid) +rt_clone(struct rtentry **nrtp, struct rtentry *rt, struct sockaddr *dst, + unsigned int rtableid) { struct rt_addrinfo info; - struct rtentry *rt = *rtp; int error = 0; + struct sockaddr_rtlabel sa_rl; + struct sockaddr_dl sa_dl = { sizeof(sa_dl), AF_LINK }; + struct rtentry *nrt = rt; + + if (!ISSET(rt->rt_flags, RTF_CLONING)) + return (EINVAL); memset(&info, 0, sizeof(info)); + info.rti_ifa = rt->rt_ifa; + info.rti_flags = rt->rt_flags; + CLR(info.rti_flags, RTF_CLONING|RTF_CONNECTED|RTF_STATIC); + SET(info.rti_flags, RTF_CLONED|RTF_HOST); info.rti_info[RTAX_DST] = dst; + info.rti_info[RTAX_GATEWAY] = sdltosa(&sa_dl); + info.rti_info[RTAX_NETMASK] = NULL; + info.rti_info[RTAX_LABEL] = rtlabel_id2sa(rt->rt_labelid, &sa_rl); /* * The priority of cloned route should be different @@ -263,18 +272,19 @@ rt_clone(struct rtentry **rtp, struct so * cloned routes instead of the cloning one. */ KERNEL_LOCK(); - error = rtrequest(RTM_RESOLVE, &info, rt->rt_priority - 1, &rt, + error = rtrequest(RTM_RESOLVE, &info, rt->rt_priority - 1, &nrt, rtableid); KERNEL_UNLOCK(); if (error) { rtm_miss(RTM_MISS, &info, 0, RTP_NONE, 0, error, rtableid); - } else { - /* Inform listeners of the new route */ - rtm_send(rt, RTM_ADD, 0, rtableid); - rtfree(*rtp); - *rtp = rt; + return (error); } - return (error); + + /* Inform listeners of the new route */ + rtm_send(nrt, RTM_ADD, 0, rtableid); + *nrtp = nrt; + + return (0); } /* @@ -379,7 +389,7 @@ rtalloc(struct sockaddr *dst, int flags, int rt_setgwroute(struct rtentry *rt, u_int rtableid) { - struct rtentry *prt, *nhrt; + struct rtentry *nhrt, *nhrt0; unsigned int rdomain = rtable_l2(rtableid); int error; @@ -388,56 +398,65 @@ rt_setgwroute(struct rtentry *rt, u_int KASSERT(ISSET(rt->rt_flags, RTF_GATEWAY)); /* If we cannot find a valid next hop bail. */ - nhrt = rt_match(rt->rt_gateway, NULL, RT_RESOLVE, rdomain); - if (nhrt == NULL) + nhrt0 = rt_match(rt->rt_gateway, NULL, RT_RESOLVE, rdomain); + if (nhrt0 == NULL) return (ENOENT); - /* Next hop entry must be on the same interface. */ - if (nhrt->rt_ifidx != rt->rt_ifidx) { - struct sockaddr_in6 sa_mask; - - if (!ISSET(nhrt->rt_flags, RTF_LLINFO) || - !ISSET(nhrt->rt_flags, RTF_CLONED)) { - rtfree(nhrt); - return (EHOSTUNREACH); - } - - /* - * We found a L2 entry, so we might have multiple - * RTF_CLONING routes for the same subnet. Query - * the first route of the multipath chain and iterate - * until we find the correct one. - */ - prt = rtable_lookup(rdomain, rt_key(nhrt->rt_parent), - rt_plen2mask(nhrt->rt_parent, &sa_mask), NULL, RTP_ANY); - rtfree(nhrt); + rtref(nhrt0); + nhrt = nhrt0; - while (prt != NULL && prt->rt_ifidx != rt->rt_ifidx) - prt = rtable_iterate(prt); + while (ISSET(nhrt->rt_flags, RTF_LLINFO)) { + if (nhrt->rt_ifidx == rt->rt_ifidx) { + rtfree(nhrt0); + goto llinfo; + } + + nhrt = rtable_iterate(nhrt); + if (nhrt == NULL) + break; + } + rtfree(nhrt); + + if (ISSET(nhrt0->rt_flags, RTF_LLINFO)) { + /* + * We found a L2 entry, so we might have multiple + * RTF_CLONING routes for the same subnet. Try to + * pick the correct one. + */ + nhrt = rtable_first(nhrt0->rt_parent); + rtfree(nhrt0); + for (;;) { + if (nhrt == NULL) + return (EHOSTUNREACH); - /* We found nothing or a non-cloning MPATH route. */ - if (prt == NULL || !ISSET(prt->rt_flags, RTF_CLONING)) { - rtfree(prt); - return (EHOSTUNREACH); + if (nhrt->rt_ifidx == rt->rt_ifidx) + break; } - error = rt_clone(&prt, rt->rt_gateway, rdomain); - if (error) { - rtfree(prt); - return (error); - } - nhrt = prt; + nhrt0 = nhrt; + } + + /* We found a non-cloning MPATH route, we're done. */ + if (!ISSET(nhrt0->rt_flags, RTF_CLONING)) { + rtfree(nhrt0); + return (EHOSTUNREACH); } + error = rt_clone(&nhrt, nhrt0, rt->rt_gateway, rdomain); + rtfree(nhrt0); + if (error) + return (error); + /* * Next hop must be reachable, this also prevents rtentry * loops for example when rt->rt_gwroute points to rt. */ - if (ISSET(nhrt->rt_flags, RTF_CLONING|RTF_GATEWAY)) { + if (ISSET(nhrt->rt_flags, RTF_GATEWAY)) { rtfree(nhrt); return (ENETUNREACH); } +llinfo: /* Next hop is valid so remove possible old cache. */ rt_putgwroute(rt); KASSERT(rt->rt_gwroute == NULL); @@ -503,6 +522,7 @@ rtfree(struct rtentry *rt) refcnt = (int)atomic_dec_int_nv(&rt->rt_refcnt); if (refcnt <= 0) { + KASSERT(rt->rt_node == NULL); KASSERT(!ISSET(rt->rt_flags, RTF_UP)); KASSERT(!RT_ROOT(rt)); atomic_dec_int(&rttrash); @@ -826,8 +846,7 @@ rtrequest(int req, struct rt_addrinfo *i struct rtentry *rt, *crt; struct ifaddr *ifa; struct sockaddr *ndst; - struct sockaddr_rtlabel *sa_rl, sa_rl2; - struct sockaddr_dl sa_dl = { sizeof(sa_dl), AF_LINK }; + struct sockaddr_rtlabel *sa_rl; int error; NET_ASSERT_LOCKED(); @@ -841,19 +860,6 @@ rtrequest(int req, struct rt_addrinfo *i return (EINVAL); case RTM_RESOLVE: - if (ret_nrt == NULL || (rt = *ret_nrt) == NULL) - return (EINVAL); - if ((rt->rt_flags & RTF_CLONING) == 0) - return (EINVAL); - KASSERT(rt->rt_ifa->ifa_ifp != NULL); - info->rti_ifa = rt->rt_ifa; - info->rti_flags = rt->rt_flags | (RTF_CLONED|RTF_HOST); - info->rti_flags &= ~(RTF_CLONING|RTF_CONNECTED|RTF_STATIC); - info->rti_info[RTAX_GATEWAY] = sdltosa(&sa_dl); - info->rti_info[RTAX_LABEL] = - rtlabel_id2sa(rt->rt_labelid, &sa_rl2); - /* FALLTHROUGH */ - case RTM_ADD: if (info->rti_ifa == NULL) return (EINVAL); Index: route.h =================================================================== RCS file: /cvs/src/sys/net/route.h,v retrieving revision 1.181 diff -u -p -r1.181 route.h --- route.h 10 Mar 2020 21:35:41 -0000 1.181 +++ route.h 27 Jul 2020 06:14:46 -0000 @@ -83,6 +83,8 @@ struct rt_metrics { #include #include +struct art_node; + /* * We distinguish between routes to hosts and routes to networks, * preferring the former if available. For each route we infer @@ -93,6 +95,7 @@ struct rt_metrics { */ struct rtentry { + struct art_node *rt_node; struct sockaddr *rt_dest; /* destination */ SRPL_ENTRY(rtentry) rt_next; /* Next multipath entry to our dst. */ struct sockaddr *rt_gateway; /* value */ @@ -441,6 +444,8 @@ void rtm_miss(int, struct rt_addrinfo * void rtm_proposal(struct ifnet *, struct rt_addrinfo *, int, uint8_t); int rt_setgate(struct rtentry *, struct sockaddr *, u_int); struct rtentry *rt_getll(struct rtentry *); +int rt_clone(struct rtentry **, struct rtentry *, struct sockaddr *, + unsigned int); int rt_timer_add(struct rtentry *, void(*)(struct rtentry *, struct rttimer *),