From: Alexander Bluhm Subject: route cache mpath To: tech@openbsd.org Date: Mon, 26 Feb 2024 11:44:57 +0100 Hi, We can combine route_cache() and rtalloc_mpath() in a new route_mpath() function. Then the caller does not have to care about the uint32_t *src pointer and just pass struct in_addr. All the conversions are done inside the functions. ro->ro_rt is either valid or NULL. Also IP input should pass a struct route to IP forward. This is the same logic that is done when passing a route from IP forward to IP output. As a result the numbers of route cache lookups in netstat -s should be correct now. Finally I removed some inconsistencies between IPv4 and IPv4 and IP forward and IP output. ok? Or should I split the diff in smaller pieces? bluhm Index: net/route.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v diff -u -p -r1.433 route.c --- net/route.c 22 Feb 2024 14:25:58 -0000 1.433 +++ net/route.c 23 Feb 2024 23:58:57 -0000 @@ -239,6 +239,28 @@ route_cache(struct route *ro, const stru return (ESRCH); } +/* + * Check cache for route, else allocate a new one, potentially using multipath + * to select the peer. Update cache and return valid route or NULL. + */ +struct rtentry * +route_mpath(struct route *ro, const struct in_addr *dst, + const struct in_addr *src, u_int rtableid) +{ + if (route_cache(ro, dst, src, rtableid)) { + uint32_t *s = NULL; + + if (ro->ro_srcin.s_addr != INADDR_ANY) + s = &ro->ro_srcin.s_addr; + ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, s, ro->ro_tableid); + if (!rtisvalid(ro->ro_rt)) { + rtfree(ro->ro_rt); + ro->ro_rt = NULL; + } + } + return (ro->ro_rt); +} + #ifdef INET6 int route6_cache(struct route *ro, const struct in6_addr *dst, @@ -276,6 +298,24 @@ route6_cache(struct route *ro, const str ro->ro_srcin6 = *src; return (ESRCH); +} + +struct rtentry * +route6_mpath(struct route *ro, const struct in6_addr *dst, + const struct in6_addr *src, u_int rtableid) +{ + if (route6_cache(ro, dst, src, rtableid)) { + uint32_t *s = NULL; + + if (IN6_IS_ADDR_UNSPECIFIED(&ro->ro_srcin6)) + s = &ro->ro_srcin6.s6_addr32[0]; + ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, s, ro->ro_tableid); + if (!rtisvalid(ro->ro_rt)) { + rtfree(ro->ro_rt); + ro->ro_rt = NULL; + } + } + return (ro->ro_rt); } #endif Index: net/route.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.h,v diff -u -p -r1.207 route.h --- net/route.h 22 Feb 2024 14:25:58 -0000 1.207 +++ net/route.h 23 Feb 2024 23:58:57 -0000 @@ -465,7 +465,11 @@ struct bfd_config; void route_init(void); int route_cache(struct route *, const struct in_addr *, const struct in_addr *, u_int); +struct rtentry *route_mpath(struct route *, const struct in_addr *, + const struct in_addr *, u_int); int route6_cache(struct route *, const struct in6_addr *, + const struct in6_addr *, u_int); +struct rtentry *route6_mpath(struct route *, const struct in6_addr *, const struct in6_addr *, u_int); void rtm_ifchg(struct ifnet *); void rtm_ifannounce(struct ifnet *, int); Index: netinet/in_pcb.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in_pcb.c,v diff -u -p -r1.294 in_pcb.c --- netinet/in_pcb.c 22 Feb 2024 14:25:58 -0000 1.294 +++ netinet/in_pcb.c 23 Feb 2024 23:58:57 -0000 @@ -908,23 +908,15 @@ in_pcblookup_local_lock(struct inpcbtabl struct rtentry * in_pcbrtentry(struct inpcb *inp) { - struct route *ro; - #ifdef INET6 if (ISSET(inp->inp_flags, INP_IPV6)) return in6_pcbrtentry(inp); #endif - ro = &inp->inp_route; - if (inp->inp_faddr.s_addr == INADDR_ANY) return (NULL); - if (route_cache(ro, &inp->inp_faddr, &inp->inp_laddr, - inp->inp_rtableid)) { - ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, - &inp->inp_laddr.s_addr, ro->ro_tableid); - } - return (ro->ro_rt); + return (route_mpath(&inp->inp_route, &inp->inp_faddr, &inp->inp_laddr, + inp->inp_rtableid)); } /* @@ -938,7 +930,7 @@ in_pcbselsrc(struct in_addr *insrc, stru struct inpcb *inp) { struct ip_moptions *mopts = inp->inp_moptions; - struct route *ro = &inp->inp_route; + struct rtentry *rt; const struct in_addr *laddr = &inp->inp_laddr; u_int rtableid = inp->inp_rtableid; struct sockaddr *ip4_source = NULL; @@ -983,17 +975,14 @@ in_pcbselsrc(struct in_addr *insrc, stru * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ - if (route_cache(ro, &sin->sin_addr, NULL, rtableid)) { - /* No route yet, so try to acquire one */ - ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, NULL, ro->ro_tableid); - } + rt = route_mpath(&inp->inp_route, &sin->sin_addr, NULL, rtableid); /* * If we found a route, use the address * corresponding to the outgoing interface. */ - if (ro->ro_rt != NULL) - ia = ifatoia(ro->ro_rt->rt_ifa); + if (rt != NULL) + ia = ifatoia(rt->rt_ifa); /* * Use preferred source address if : @@ -1001,8 +990,7 @@ in_pcbselsrc(struct in_addr *insrc, stru * - preferred source address is set * - output interface is UP */ - if (ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO) && - !(ro->ro_rt->rt_flags & RTF_HOST)) { + if (rt && !(rt->rt_flags & RTF_LLINFO) && !(rt->rt_flags & RTF_HOST)) { ip4_source = rtable_getsource(rtableid, AF_INET); if (ip4_source != NULL) { struct ifaddr *ifa; Index: netinet/ip_input.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_input.c,v diff -u -p -r1.390 ip_input.c --- netinet/ip_input.c 22 Feb 2024 14:25:58 -0000 1.390 +++ netinet/ip_input.c 23 Feb 2024 23:58:57 -0000 @@ -138,7 +138,7 @@ extern struct niqueue arpinq; int ip_ours(struct mbuf **, int *, int, int); int ip_dooptions(struct mbuf *, struct ifnet *); -int in_ouraddr(struct mbuf *, struct ifnet *, struct rtentry **); +int in_ouraddr(struct mbuf *, struct ifnet *, struct route *); int ip_fragcheck(struct mbuf **, int *); struct mbuf * ip_reass(struct ipqent *, struct ipq *); @@ -387,14 +387,18 @@ bad: int ip_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp) { - struct mbuf *m; - struct rtentry *rt = NULL; - struct ip *ip; + struct route ro; + struct mbuf *m; + struct ip *ip; int hlen; - in_addr_t pfrdr = 0; +#if NPF > 0 + struct in_addr odst; +#endif + int pfrdr = 0; KASSERT(*offp == 0); + ro.ro_rt = NULL; ipstat_inc(ips_total); m = *mp = ipv4_check(ifp, *mp); if (m == NULL) @@ -412,7 +416,7 @@ ip_input_if(struct mbuf **mp, int *offp, /* * Packet filter */ - pfrdr = ip->ip_dst.s_addr; + odst = ip->ip_dst; if (pf_test(AF_INET, PF_IN, ifp, mp) != PF_PASS) goto bad; m = *mp; @@ -420,7 +424,7 @@ ip_input_if(struct mbuf **mp, int *offp, goto bad; ip = mtod(m, struct ip *); - pfrdr = (pfrdr != ip->ip_dst.s_addr); + pfrdr = odst.s_addr != ip->ip_dst.s_addr; #endif hlen = ip->ip_hl << 2; @@ -442,7 +446,7 @@ ip_input_if(struct mbuf **mp, int *offp, goto out; } - switch(in_ouraddr(m, ifp, &rt)) { + switch(in_ouraddr(m, ifp, &ro)) { case 2: goto bad; case 1: @@ -544,14 +548,14 @@ ip_input_if(struct mbuf **mp, int *offp, } #endif /* IPSEC */ - ip_forward(m, ifp, rt, pfrdr); + ip_forward(m, ifp, &ro, pfrdr); *mp = NULL; return IPPROTO_DONE; bad: nxt = IPPROTO_DONE; m_freemp(mp); out: - rtfree(rt); + rtfree(ro.ro_rt); return nxt; } @@ -748,11 +752,10 @@ ip_deliver(struct mbuf **mp, int *offp, #undef IPSTAT_INC int -in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct rtentry **prt) +in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct route *ro) { struct rtentry *rt; struct ip *ip; - struct sockaddr_in sin; int match = 0; #if NPF > 0 @@ -769,13 +772,8 @@ in_ouraddr(struct mbuf *m, struct ifnet ip = mtod(m, struct ip *); - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr = ip->ip_dst; - rt = rtalloc_mpath(sintosa(&sin), &ip->ip_src.s_addr, - m->m_pkthdr.ph_rtableid); - if (rtisvalid(rt)) { + rt = route_mpath(ro, &ip->ip_dst, &ip->ip_src, m->m_pkthdr.ph_rtableid); + if (rt != NULL) { if (ISSET(rt->rt_flags, RTF_LOCAL)) match = 1; @@ -791,7 +789,6 @@ in_ouraddr(struct mbuf *m, struct ifnet m->m_flags |= M_BCAST; } } - *prt = rt; if (!match) { struct ifaddr *ifa; @@ -1470,11 +1467,12 @@ const u_char inetctlerrmap[PRC_NCMDS] = * via a source route. */ void -ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt) +ip_forward(struct mbuf *m, struct ifnet *ifp, struct route *ro, int srcrt) { - struct mbuf mfake, *mcopy = NULL; + struct mbuf mfake, *mcopy; struct ip *ip = mtod(m, struct ip *); - struct route ro; + struct route iproute; + struct rtentry *rt; int error = 0, type = 0, code = 0, destmtu = 0, fake = 0, len; u_int32_t dest; @@ -1482,26 +1480,23 @@ ip_forward(struct mbuf *m, struct ifnet if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) { ipstat_inc(ips_cantforward); m_freem(m); - goto freecopy; + goto done; } if (ip->ip_ttl <= IPTTLDEC) { icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0); - goto freecopy; + goto done; } - ro.ro_rt = NULL; - route_cache(&ro, &ip->ip_dst, &ip->ip_src, m->m_pkthdr.ph_rtableid); - if (!rtisvalid(rt)) { - rtfree(rt); - rt = rtalloc_mpath(&ro.ro_dstsa, &ip->ip_src.s_addr, - m->m_pkthdr.ph_rtableid); - if (rt == NULL) { - ipstat_inc(ips_noroute); - icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0); - return; - } + if (ro == NULL) { + ro = &iproute; + ro->ro_rt = NULL; + } + rt = route_mpath(ro, &ip->ip_dst, &ip->ip_src, m->m_pkthdr.ph_rtableid); + if (rt == NULL) { + ipstat_inc(ips_noroute); + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0); + goto done; } - ro.ro_rt = rt; /* * Save at most 68 bytes of the packet in case @@ -1552,10 +1547,10 @@ ip_forward(struct mbuf *m, struct ifnet } } - error = ip_output(m, NULL, &ro, + error = ip_output(m, NULL, ro, (IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)), NULL, NULL, 0); - rt = ro.ro_rt; + rt = ro->ro_rt; if (error) ipstat_inc(ips_cantforward); else { @@ -1563,10 +1558,10 @@ ip_forward(struct mbuf *m, struct ifnet if (type) ipstat_inc(ips_redirectsent); else - goto freecopy; + goto done; } if (!fake) - goto freecopy; + goto done; switch (error) { case 0: /* forwarded, but need redirect */ @@ -1590,7 +1585,7 @@ ip_forward(struct mbuf *m, struct ifnet } ipstat_inc(ips_cantfrag); if (destmtu == 0) - goto freecopy; + goto done; break; case EACCES: @@ -1598,7 +1593,7 @@ ip_forward(struct mbuf *m, struct ifnet * pf(4) blocked the packet. There is no need to send an ICMP * packet back since pf(4) takes care of it. */ - goto freecopy; + goto done; case ENOBUFS: /* @@ -1607,7 +1602,7 @@ ip_forward(struct mbuf *m, struct ifnet * source quench could be a big problem under DoS attacks, * or the underlying interface is rate-limited. */ - goto freecopy; + goto done; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: @@ -1622,10 +1617,11 @@ ip_forward(struct mbuf *m, struct ifnet if (mcopy) icmp_error(mcopy, type, code, dest, destmtu); -freecopy: +done: + if (ro == &iproute && ro->ro_rt) + rtfree(ro->ro_rt); if (fake) m_tag_delete_chain(&mfake); - rtfree(rt); } int Index: netinet/ip_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_var.h,v diff -u -p -r1.113 ip_var.h --- netinet/ip_var.h 13 Feb 2024 12:22:09 -0000 1.113 +++ netinet/ip_var.h 23 Feb 2024 18:59:54 -0000 @@ -255,7 +255,7 @@ void ip_savecontrol(struct inpcb *, str struct mbuf *); int ip_input_if(struct mbuf **, int *, int, int, struct ifnet *); int ip_deliver(struct mbuf **, int *, int, int); -void ip_forward(struct mbuf *, struct ifnet *, struct rtentry *, int); +void ip_forward(struct mbuf *, struct ifnet *, struct route *, int); int rip_ctloutput(int, struct socket *, int, int, struct mbuf *); void rip_init(void); int rip_input(struct mbuf **, int *, int, int); Index: netinet6/in6_pcb.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6_pcb.c,v diff -u -p -r1.139 in6_pcb.c --- netinet6/in6_pcb.c 22 Feb 2024 14:25:58 -0000 1.139 +++ netinet6/in6_pcb.c 23 Feb 2024 23:58:57 -0000 @@ -561,16 +561,10 @@ in6_pcbnotify(struct inpcbtable *table, struct rtentry * in6_pcbrtentry(struct inpcb *inp) { - struct route *ro = &inp->inp_route; - if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) return (NULL); - if (route6_cache(ro, &inp->inp_faddr6, &inp->inp_laddr6, - inp->inp_rtableid)) { - ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, - &inp->inp_laddr6.s6_addr32[0], ro->ro_tableid); - } - return (ro->ro_rt); + return (route6_mpath(&inp->inp_route, &inp->inp_faddr6, + &inp->inp_laddr6, inp->inp_rtableid)); } struct inpcb * Index: netinet6/in6_src.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6_src.c,v diff -u -p -r1.95 in6_src.c --- netinet6/in6_src.c 22 Feb 2024 14:25:58 -0000 1.95 +++ netinet6/in6_src.c 23 Feb 2024 23:58:57 -0000 @@ -95,7 +95,7 @@ in6_pcbselsrc(const struct in6_addr **in struct inpcb *inp, struct ip6_pktopts *opts) { struct ip6_moptions *mopts = inp->inp_moptions6; - struct route *ro = &inp->inp_route; + struct rtentry *rt; const struct in6_addr *laddr = &inp->inp_laddr6; u_int rtableid = inp->inp_rtableid; struct ifnet *ifp = NULL; @@ -118,7 +118,8 @@ in6_pcbselsrc(const struct in6_addr **in struct sockaddr_in6 sa6; /* get the outgoing interface */ - error = in6_selectif(dst, opts, mopts, ro, &ifp, rtableid); + error = in6_selectif(dst, opts, mopts, &inp->inp_route, &ifp, + rtableid); if (error) return (error); @@ -179,9 +180,7 @@ in6_pcbselsrc(const struct in6_addr **in * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ - if (route6_cache(ro, dst, NULL, rtableid)) { - ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, NULL, ro->ro_tableid); - } + rt = route6_mpath(&inp->inp_route, dst, NULL, rtableid); /* * in_pcbconnect() checks out IFF_LOOPBACK to skip using @@ -190,14 +189,14 @@ in6_pcbselsrc(const struct in6_addr **in * so doesn't check out IFF_LOOPBACK. */ - if (ro->ro_rt) { - ifp = if_get(ro->ro_rt->rt_ifidx); + if (rt != NULL) { + ifp = if_get(rt->rt_ifidx); if (ifp != NULL) { ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); } if (ia6 == NULL) /* xxx scope error ?*/ - ia6 = ifatoia6(ro->ro_rt->rt_ifa); + ia6 = ifatoia6(rt->rt_ifa); } /* @@ -206,8 +205,7 @@ in6_pcbselsrc(const struct in6_addr **in * - preferred source address is set * - output interface is UP */ - if (ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO) && - !(ro->ro_rt->rt_flags & RTF_HOST)) { + if (rt && !(rt->rt_flags & RTF_LLINFO) && !(rt->rt_flags & RTF_HOST)) { ip6_source = rtable_getsource(rtableid, AF_INET6); if (ip6_source != NULL) { struct ifaddr *ifa; @@ -304,11 +302,9 @@ in6_selectroute(const struct in6_addr *d * a new one. */ if (ro) { - if (route6_cache(ro, dst, NULL, rtableid)) { - /* No route yet, so try to acquire one */ - ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, NULL, - ro->ro_tableid); - } + struct rtentry *rt; + + rt = route6_mpath(ro, dst, NULL, rtableid); /* * Check if the outgoing interface conflicts with @@ -319,15 +315,13 @@ in6_selectroute(const struct in6_addr *d */ if (opts && opts->ip6po_pktinfo && opts->ip6po_pktinfo->ipi6_ifindex) { - if (ro->ro_rt != NULL && - !ISSET(ro->ro_rt->rt_flags, RTF_LOCAL) && - ro->ro_rt->rt_ifidx != - opts->ip6po_pktinfo->ipi6_ifindex) { + if (rt != NULL && !ISSET(rt->rt_flags, RTF_LOCAL) && + rt->rt_ifidx != opts->ip6po_pktinfo->ipi6_ifindex) { return (NULL); } } - return (ro->ro_rt); + return (rt); } return (NULL); @@ -338,7 +332,7 @@ in6_selectif(const struct in6_addr *dst, struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp, u_int rtableid) { - struct rtentry *rt = NULL; + struct rtentry *rt; struct in6_pktinfo *pi = NULL; /* If the caller specify the outgoing interface explicitly, use it. */ Index: netinet6/ip6_forward.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_forward.c,v diff -u -p -r1.115 ip6_forward.c --- netinet6/ip6_forward.c 22 Feb 2024 14:25:58 -0000 1.115 +++ netinet6/ip6_forward.c 23 Feb 2024 23:58:57 -0000 @@ -82,14 +82,15 @@ */ void -ip6_forward(struct mbuf *m, struct rtentry *rt, int srcrt) +ip6_forward(struct mbuf *m, struct route *ro, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct route iproute; + struct rtentry *rt; struct sockaddr *dst; - struct route ro; struct ifnet *ifp = NULL; int error = 0, type = 0, code = 0, destmtu = 0; - struct mbuf *mcopy = NULL; + struct mbuf *mcopy; #ifdef IPSEC struct tdb *tdb = NULL; #endif /* IPSEC */ @@ -121,13 +122,13 @@ ip6_forward(struct mbuf *m, struct rtent m->m_pkthdr.ph_ifidx); } m_freem(m); - goto out; + goto done; } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); - goto out; + goto done; } ip6->ip6_hlim -= IPV6_HLIMDEC; @@ -165,25 +166,22 @@ reroute: } #endif /* IPSEC */ - ro.ro_rt = NULL; - route6_cache(&ro, &ip6->ip6_dst, &ip6->ip6_src, + if (ro == NULL) { + ro = &iproute; + ro->ro_rt = NULL; + } + rt = route6_mpath(ro, &ip6->ip6_dst, &ip6->ip6_src, m->m_pkthdr.ph_rtableid); - dst = &ro.ro_dstsa; - if (!rtisvalid(rt)) { - rtfree(rt); - rt = rtalloc_mpath(dst, &ip6->ip6_src.s6_addr32[0], - m->m_pkthdr.ph_rtableid); - if (rt == NULL) { - ip6stat_inc(ip6s_noroute); - if (mcopy) { - icmp6_error(mcopy, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); - } - m_freem(m); - goto out; + if (rt == NULL) { + ip6stat_inc(ip6s_noroute); + if (mcopy) { + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); } + m_freem(m); + goto done; } - ro.ro_rt = rt; + dst = &ro->ro_dstsa; /* * Scope check: if a packet can't be delivered to its destination @@ -215,7 +213,7 @@ reroute: icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); - goto out; + goto done; } #ifdef IPSEC @@ -225,8 +223,8 @@ reroute: */ if (tdb != NULL) { /* Callee frees mbuf */ - error = ip6_output_ipsec_send(tdb, m, &ro, 0, 1); - rt = ro.ro_rt; + error = ip6_output_ipsec_send(tdb, m, ro, 0, 1); + rt = ro->ro_rt; if (error) goto senderr; goto freecopy; @@ -254,7 +252,7 @@ reroute: ip6_sendredirects && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((ifp->if_flags & IFF_POINTOPOINT) && - nd6_is_addr_neighbor(&ro.ro_dstsin6, ifp)) { + nd6_is_addr_neighbor(&ro->ro_dstsin6, ifp)) { /* * If the incoming interface is equal to the outgoing * one, the link attached to the interface is @@ -274,7 +272,7 @@ reroute: icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); m_freem(m); - goto out; + goto done; } type = ND_REDIRECT; } @@ -308,8 +306,7 @@ reroute: /* tag as generated to skip over pf_test on rerun */ m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; srcrt = 1; - rtfree(rt); - rt = NULL; + ro = NULL; if_put(ifp); ifp = NULL; goto reroute; @@ -324,21 +321,21 @@ reroute: if (error || m == NULL) goto senderr; - if (mcopy != NULL) + if (mcopy) icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); m_freem(m); - goto out; + goto done; senderr: if (mcopy == NULL) - goto out; + goto done; switch (error) { case 0: if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); ip6stat_inc(ip6s_redirectsent); - goto out; + goto done; } goto freecopy; @@ -383,12 +380,13 @@ senderr: break; } icmp6_error(mcopy, type, code, destmtu); - goto out; + goto done; freecopy: m_freem(mcopy); -out: - rtfree(rt); +done: + if (ro == &iproute && ro->ro_rt) + rtfree(ro->ro_rt); if_put(ifp); #ifdef IPSEC tdb_unref(tdb); Index: netinet6/ip6_input.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_input.c,v diff -u -p -r1.258 ip6_input.c --- netinet6/ip6_input.c 22 Feb 2024 14:25:58 -0000 1.258 +++ netinet6/ip6_input.c 23 Feb 2024 23:58:57 -0000 @@ -357,21 +357,21 @@ bad: int ip6_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp) { + struct route ro; struct mbuf *m; struct ip6_hdr *ip6; - struct sockaddr_in6 sin6; - struct rtentry *rt = NULL; + struct rtentry *rt; int ours = 0; u_int16_t src_scope, dst_scope; #if NPF > 0 struct in6_addr odst; #endif - int srcrt = 0; + int pfrdr = 0; KASSERT(*offp == 0); + ro.ro_rt = NULL; ip6stat_inc(ip6s_total); - m = *mp = ipv6_check(ifp, *mp); if (m == NULL) goto bad; @@ -413,7 +413,7 @@ ip6_input_if(struct mbuf **mp, int *offp goto bad; ip6 = mtod(m, struct ip6_hdr *); - srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst); + pfrdr = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst); #endif /* @@ -517,18 +517,14 @@ ip6_input_if(struct mbuf **mp, int *offp /* * Unicast check */ - memset(&sin6, 0, sizeof(struct sockaddr_in6)); - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = ip6->ip6_dst; - rt = rtalloc_mpath(sin6tosa(&sin6), &ip6->ip6_src.s6_addr32[0], + rt = route6_mpath(&ro, &ip6->ip6_dst, &ip6->ip6_src, m->m_pkthdr.ph_rtableid); /* * Accept the packet if the route to the destination is marked * as local. */ - if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL)) { + if (rt != NULL && ISSET(rt->rt_flags, RTF_LOCAL)) { struct in6_ifaddr *ia6 = ifatoia6(rt->rt_ifa); if (ip6_forwarding == 0 && rt->rt_ifidx != ifp->if_index && @@ -618,14 +614,14 @@ ip6_input_if(struct mbuf **mp, int *offp } #endif /* IPSEC */ - ip6_forward(m, rt, srcrt); + ip6_forward(m, &ro, pfrdr); *mp = NULL; return IPPROTO_DONE; bad: nxt = IPPROTO_DONE; m_freemp(mp); out: - rtfree(rt); + rtfree(ro.ro_rt); return nxt; } Index: netinet6/ip6_output.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_output.c,v diff -u -p -r1.287 ip6_output.c --- netinet6/ip6_output.c 22 Feb 2024 14:25:58 -0000 1.287 +++ netinet6/ip6_output.c 23 Feb 2024 23:58:57 -0000 @@ -391,7 +391,7 @@ reroute: /* initialize cached route */ if (ro == NULL) { ro = &iproute; - bzero((caddr_t)ro, sizeof(*ro)); + ro->ro_rt = NULL; } ro_pmtu = ro; if (opt && opt->ip6po_rthdr) @@ -748,7 +748,15 @@ reroute: (error = if_output_ml(ifp, &ml, sin6tosa(dst), ro->ro_rt))) goto done; ip6stat_inc(ip6s_fragmented); + goto done; +freehdrs: + m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */ + m_freem(exthdrs.ip6e_dest1); + m_freem(exthdrs.ip6e_rthdr); + m_freem(exthdrs.ip6e_dest2); +bad: + m_freem(m); done: if (ro == &iproute && ro->ro_rt) { rtfree(ro->ro_rt); @@ -760,16 +768,6 @@ done: tdb_unref(tdb); #endif /* IPSEC */ return (error); - -freehdrs: - m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */ - m_freem(exthdrs.ip6e_dest1); - m_freem(exthdrs.ip6e_rthdr); - m_freem(exthdrs.ip6e_dest2); - /* FALLTHROUGH */ -bad: - m_freem(m); - goto done; } int Index: netinet6/ip6_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_var.h,v diff -u -p -r1.114 ip6_var.h --- netinet6/ip6_var.h 14 Feb 2024 13:18:21 -0000 1.114 +++ netinet6/ip6_var.h 23 Feb 2024 23:21:02 -0000 @@ -320,7 +320,7 @@ int ip6_process_hopopts(struct mbuf **, void ip6_savecontrol(struct inpcb *, struct mbuf *, struct mbuf **); int ip6_sysctl(int *, u_int, void *, size_t *, void *, size_t); -void ip6_forward(struct mbuf *, struct rtentry *, int); +void ip6_forward(struct mbuf *, struct route *, int); void ip6_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in6 *); int ip6_output(struct mbuf *, struct ip6_pktopts *, struct route *, int,