Index: net/art.c =================================================================== RCS file: /cvs/src/sys/net/art.c,v retrieving revision 1.28 diff -u -p -r1.28 art.c --- net/art.c 31 Mar 2019 19:29:27 -0000 1.28 +++ net/art.c 12 Apr 2020 06:36:52 -0000 @@ -900,11 +900,27 @@ art_get(void *dst, uint8_t plen) an->an_plen = plen; SRPL_INIT(&an->an_rtlist); + refcnt_init(&an->an_refcnt); + + return (an); +} + +struct art_node * +art_take(struct art_node *an) +{ + refcnt_take(&an->an_refcnt); return (an); } void +art_rele(struct art_node *an) +{ + if (refcnt_rele(&an->an_refcnt)) + pool_put(&an_pool, an); +} + +void art_put(struct art_node *an) { KASSERT(SRPL_EMPTY_LOCKED(&an->an_rtlist)); @@ -932,7 +948,7 @@ art_gc(void *null) srp_finalize(an, "artnfini"); - pool_put(&an_pool, an); + art_rele(an); an = next; } Index: net/art.h =================================================================== RCS file: /cvs/src/sys/net/art.h,v retrieving revision 1.18 diff -u -p -r1.18 art.h --- net/art.h 31 Mar 2019 14:03:40 -0000 1.18 +++ net/art.h 12 Apr 2020 06:36:52 -0000 @@ -20,6 +20,7 @@ #define _NET_ART_H_ #include +#include #include #define ART_MAXLVL 32 /* We currently use 32 levels for IPv6. */ @@ -82,6 +83,7 @@ struct art_node { SRPL_HEAD(, rtentry) an__rtlist; /* Route related to this node */ struct art_node *an__gc; /* Entry on GC list */ } an_pointer; + struct refcnt an_refcnt; uint8_t an_plen; /* Prefix length */ }; #define an_rtlist an_pointer.an__rtlist @@ -100,6 +102,8 @@ int art_walk(struct art_root *, int (*)(struct art_node *, void *), void *); struct art_node *art_get(void *, uint8_t); +struct art_node *art_take(struct art_node *); +void art_rele(struct art_node *); void art_put(struct art_node *); #endif /* _NET_ART_H_ */ Index: net/route.c =================================================================== RCS file: /cvs/src/sys/net/route.c,v retrieving revision 1.387 diff -u -p -r1.387 route.c --- net/route.c 24 Jun 2019 22:26:25 -0000 1.387 +++ net/route.c 12 Apr 2020 06:36:52 -0000 @@ -379,7 +379,7 @@ rtalloc(struct sockaddr *dst, int flags, int rt_setgwroute(struct rtentry *rt, u_int rtableid) { - struct rtentry *prt, *nhrt; + struct rtentry *prt, *nhrt, *nhrt0; unsigned int rdomain = rtable_l2(rtableid); int error; @@ -388,35 +388,45 @@ 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); + rtref(nhrt0); + nhrt = nhrt0; + + 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. Query - * the first route of the multipath chain and iterate - * until we find the correct one. + * RTF_CLONING routes for the same subnet. Try to + * pick the correct one. */ - prt = rtable_lookup(rdomain, rt_key(nhrt->rt_parent), - rt_plen2mask(nhrt->rt_parent, &sa_mask), NULL, RTP_ANY); - rtfree(nhrt); + prt = rtable_first(nhrt0->rt_parent); + rtfree(nhrt0); + for (;;) { + if (prt == NULL) + return (EHOSTUNREACH); + + if (prt->rt_ifidx == rt->rt_ifidx) + break; - while (prt != NULL && prt->rt_ifidx != rt->rt_ifidx) prt = rtable_iterate(prt); + } - /* We found nothing or a non-cloning MPATH route. */ - if (prt == NULL || !ISSET(prt->rt_flags, RTF_CLONING)) { + /* We found a non-cloning MPATH route, we're done. */ + if (!ISSET(prt->rt_flags, RTF_CLONING)) { rtfree(prt); return (EHOSTUNREACH); } @@ -438,6 +448,7 @@ rt_setgwroute(struct rtentry *rt, u_int return (ENETUNREACH); } +llinfo: /* Next hop is valid so remove possible old cache. */ rt_putgwroute(rt); KASSERT(rt->rt_gwroute == NULL); @@ -503,6 +514,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); Index: net/route.h =================================================================== RCS file: /cvs/src/sys/net/route.h,v retrieving revision 1.178 diff -u -p -r1.178 route.h --- net/route.h 9 Nov 2019 17:16:39 -0000 1.178 +++ net/route.h 12 Apr 2020 06:36:52 -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 */ Index: net/rtable.c =================================================================== RCS file: /cvs/src/sys/net/rtable.c,v retrieving revision 1.69 diff -u -p -r1.69 rtable.c --- net/rtable.c 21 Jun 2019 17:11:42 -0000 1.69 +++ net/rtable.c 12 Apr 2020 06:36:52 -0000 @@ -548,6 +548,7 @@ rtable_insert(unsigned int rtableid, str rt->rt_flags &= ~RTF_MPATH; rt->rt_dest = dst; rt->rt_plen = plen; + rt->rt_node = art_take(an); SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next); prev = art_insert(ar, an, addr, plen); @@ -555,6 +556,7 @@ rtable_insert(unsigned int rtableid, str SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next); rt->rt_flags = rt_flags; + art_rele(rt->rt_node); art_put(an); if (prev == NULL) { @@ -587,6 +589,7 @@ rtable_insert(unsigned int rtableid, str } /* Put newly inserted entry at the right place. */ + rt->rt_node = art_take(an); rtable_mpath_insert(an, rt); } leave: @@ -628,6 +631,9 @@ rtable_delete(unsigned int rtableid, str goto leave; } + art_rele(rt->rt_node); + rt->rt_node = NULL; + /* * If other multipath route entries are still attached to * this ART node we only have to unlink it. @@ -726,6 +732,22 @@ rtable_iterate(struct rtentry *rt0) rtref(rt); SRPL_LEAVE(&sr); rtfree(rt0); + return (rt); +} + +struct rtentry * +rtable_first(struct rtentry *rtn) +{ + struct art_node *an; + struct rtentry *rt; + struct srp_ref sr; + + an = rtn->rt_node; + rt = SRPL_FIRST(&sr, &an->an_rtlist); + if (rt != NULL) + rtref(rt); + SRPL_LEAVE(&sr); + return (rt); } Index: net/rtable.h =================================================================== RCS file: /cvs/src/sys/net/rtable.h,v retrieving revision 1.24 diff -u -p -r1.24 rtable.h --- net/rtable.h 21 Jun 2019 17:11:42 -0000 1.24 +++ net/rtable.h 12 Apr 2020 06:36:52 -0000 @@ -54,6 +54,7 @@ int rtable_walk(unsigned int, sa_famil int rtable_mpath_capable(unsigned int, sa_family_t); struct rtentry *rtable_mpath_match(unsigned int, struct rtentry *, struct sockaddr *, uint8_t); +struct rtentry *rtable_first(struct rtentry *); int rtable_mpath_reprio(unsigned int, struct sockaddr *, int, uint8_t, struct rtentry *); Index: netinet/if_ether.c =================================================================== RCS file: /cvs/src/sys/netinet/if_ether.c,v retrieving revision 1.242 diff -u -p -r1.242 if_ether.c --- netinet/if_ether.c 7 Nov 2019 11:23:23 -0000 1.242 +++ netinet/if_ether.c 12 Apr 2020 06:36:52 -0000 @@ -502,7 +502,7 @@ in_arpinput(struct ifnet *ifp, struct mb struct sockaddr_in sin; struct in_addr isaddr, itaddr; char addr[INET_ADDRSTRLEN]; - int op, target = 0; + int op, resolve = 0; unsigned int rdomain; rdomain = rtable_l2(m->m_pkthdr.ph_rtableid); @@ -514,9 +514,6 @@ in_arpinput(struct ifnet *ifp, struct mb memcpy(&itaddr, ea->arp_tpa, sizeof(itaddr)); memcpy(&isaddr, ea->arp_spa, sizeof(isaddr)); - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; if (ETHER_IS_MULTICAST(ea->arp_sha) && ETHER_IS_BROADCAST(ea->arp_sha)) { @@ -530,44 +527,66 @@ in_arpinput(struct ifnet *ifp, struct mb goto out; /* it's from me, ignore it. */ /* Check target against our interface addresses. */ + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; sin.sin_addr = itaddr; rt = rtalloc(sintosa(&sin), 0, rdomain); - if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) && - rt->rt_ifidx == ifp->if_index) - target = 1; + while (rtisvalid(rt)) { + if (ISSET(rt->rt_flags, RTF_LOCAL) && + rt->rt_ifidx == ifp->if_index) { + resolve = RT_RESOLVE; + break; + } + + rt = rtable_iterate(rt); + } rtfree(rt); rt = NULL; #if NCARP > 0 - if (target && op == ARPOP_REQUEST && ifp->if_type == IFT_CARP && + if (resolve && op == ARPOP_REQUEST && ifp->if_type == IFT_CARP && !carp_iamatch(ifp)) goto out; #endif /* Do we have an ARP cache for the sender? Create if we are target. */ - rt = arplookup(&isaddr, target, 0, rdomain); + sin.sin_addr = isaddr; + for (rt = rtalloc(sintosa(&sin), resolve, rdomain); + rtisvalid(rt); rt = rtable_iterate(rt)) { + if (rt->rt_ifidx != ifp->if_index) + continue; + + if (!ISSET(rt->rt_flags, RTF_GATEWAY) && + ISSET(rt->rt_flags, RTF_LLINFO) && + rt->rt_gateway->sa_family == AF_LINK) + break; + } /* Check sender against our interface addresses. */ - if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) && - rt->rt_ifidx == ifp->if_index && isaddr.s_addr != INADDR_ANY) { - inet_ntop(AF_INET, &isaddr, addr, sizeof(addr)); - log(LOG_ERR, "duplicate IP address %s sent from ethernet " - "address %s\n", addr, ether_sprintf(ea->arp_sha)); - itaddr = isaddr; - } else if (rt != NULL) { - int error; - - KERNEL_LOCK(); - error = arpcache(ifp, ea, rt); - KERNEL_UNLOCK(); - if (error) - goto out; + if (rt != NULL) { + if (ISSET(rt->rt_flags, RTF_LOCAL) && + isaddr.s_addr != INADDR_ANY) { + inet_ntop(AF_INET, &isaddr, addr, sizeof(addr)); + log(LOG_ERR, "duplicate IP address %s sent from " + "ethernet address %s\n", addr, + ether_sprintf(ea->arp_sha)); + itaddr = isaddr; + } else { + int error; + + KERNEL_LOCK(); + error = arpcache(ifp, ea, rt); + KERNEL_UNLOCK(); + if (error) + goto out; + } } if (op == ARPOP_REQUEST) { uint8_t *eaddr; - if (target) { + if (resolve) { /* We already have all info for the reply */ eaddr = LLADDR(ifp->if_sadl); } else {