Index | Thread | Search

From:
Alexander Bluhm <alexander.bluhm@gmx.net>
Subject:
route cache mpath
To:
tech@openbsd.org
Date:
Mon, 26 Feb 2024 11:44:57 +0100

Download raw body.

Thread
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,