Make the route cache aware of multipath routing.
authorbluhm <bluhm@openbsd.org>
Thu, 22 Feb 2024 14:25:58 +0000 (14:25 +0000)
committerbluhm <bluhm@openbsd.org>
Thu, 22 Feb 2024 14:25:58 +0000 (14:25 +0000)
Pass source address to route_cache() and store it in struct route.
Cached multipath routes are only valid if source address matches.
If sysctl multipath changes, increase route generation number.

OK claudio@

sys/net/route.c
sys/net/route.h
sys/netinet/in_pcb.c
sys/netinet/ip_input.c
sys/netinet/ip_output.c
sys/netinet6/in6_pcb.c
sys/netinet6/in6_src.c
sys/netinet6/ip6_forward.c
sys/netinet6/ip6_input.c
sys/netinet6/ip6_output.c

index f9915cd..9188b6c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: route.c,v 1.432 2024/02/13 12:22:09 bluhm Exp $       */
+/*     $OpenBSD: route.c,v 1.433 2024/02/22 14:25:58 bluhm Exp $       */
 /*     $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $      */
 
 /*
@@ -202,7 +202,8 @@ route_init(void)
 }
 
 int
-route_cache(struct route *ro, struct in_addr addr, u_int rtableid)
+route_cache(struct route *ro, const struct in_addr *dst,
+    const struct in_addr *src, u_int rtableid)
 {
        u_long gen;
 
@@ -213,28 +214,35 @@ route_cache(struct route *ro, struct in_addr addr, u_int rtableid)
            ro->ro_generation == gen &&
            ro->ro_tableid == rtableid &&
            ro->ro_dstsa.sa_family == AF_INET &&
-           ro->ro_dstsin.sin_addr.s_addr == addr.s_addr) {
-               ipstat_inc(ips_rtcachehit);
-               return (0);
+           ro->ro_dstsin.sin_addr.s_addr == dst->s_addr) {
+               if (src == NULL || !ipmultipath ||
+                   !ISSET(ro->ro_rt->rt_flags, RTF_MPATH) ||
+                   (ro->ro_srcin.s_addr != INADDR_ANY &&
+                   ro->ro_srcin.s_addr == src->s_addr)) {
+                       ipstat_inc(ips_rtcachehit);
+                       return (0);
+               }
        }
 
        ipstat_inc(ips_rtcachemiss);
        rtfree(ro->ro_rt);
-       ro->ro_rt = NULL;
+       memset(ro, 0, sizeof(*ro));
        ro->ro_generation = gen;
        ro->ro_tableid = rtableid;
 
-       memset(&ro->ro_dst, 0, sizeof(ro->ro_dst));
        ro->ro_dstsin.sin_family = AF_INET;
        ro->ro_dstsin.sin_len = sizeof(struct sockaddr_in);
-       ro->ro_dstsin.sin_addr = addr;
+       ro->ro_dstsin.sin_addr = *dst;
+       if (src != NULL)
+               ro->ro_srcin = *src;
 
        return (ESRCH);
 }
 
 #ifdef INET6
 int
-route6_cache(struct route *ro, const struct in6_addr *addr, u_int rtableid)
+route6_cache(struct route *ro, const struct in6_addr *dst,
+    const struct in6_addr *src, u_int rtableid)
 {
        u_long gen;
 
@@ -245,21 +253,27 @@ route6_cache(struct route *ro, const struct in6_addr *addr, u_int rtableid)
            ro->ro_generation == gen &&
            ro->ro_tableid == rtableid &&
            ro->ro_dstsa.sa_family == AF_INET6 &&
-           IN6_ARE_ADDR_EQUAL(&ro->ro_dstsin6.sin6_addr, addr)) {
-               ip6stat_inc(ip6s_rtcachehit);
-               return (0);
+           IN6_ARE_ADDR_EQUAL(&ro->ro_dstsin6.sin6_addr, dst)) {
+               if (src == NULL || !ip6_multipath ||
+                   !ISSET(ro->ro_rt->rt_flags, RTF_MPATH) ||
+                   (!IN6_IS_ADDR_UNSPECIFIED(&ro->ro_srcin6) &&
+                   IN6_ARE_ADDR_EQUAL(&ro->ro_srcin6, src))) {
+                       ip6stat_inc(ip6s_rtcachehit);
+                       return (0);
+               }
        }
 
        ip6stat_inc(ip6s_rtcachemiss);
        rtfree(ro->ro_rt);
-       ro->ro_rt = NULL;
+       memset(ro, 0, sizeof(*ro));
        ro->ro_generation = gen;
        ro->ro_tableid = rtableid;
 
-       memset(&ro->ro_dst, 0, sizeof(ro->ro_dst));
        ro->ro_dstsin6.sin6_family = AF_INET6;
        ro->ro_dstsin6.sin6_len = sizeof(struct sockaddr_in6);
-       ro->ro_dstsin6.sin6_addr = *addr;
+       ro->ro_dstsin6.sin6_addr = *dst;
+       if (src != NULL)
+               ro->ro_srcin6 = *src;
 
        return (ESRCH);
 }
index 7833f7c..fe652bd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: route.h,v 1.206 2024/02/13 12:22:09 bluhm Exp $       */
+/*     $OpenBSD: route.h,v 1.207 2024/02/22 14:25:58 bluhm Exp $       */
 /*     $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $       */
 
 /*
@@ -393,13 +393,14 @@ struct route {
        u_long           ro_generation;
        u_long           ro_tableid;    /* u_long because of alignment */
        union {
-               struct  sockaddr        rod_sa;
-               struct  sockaddr_in     rod_sin;
-               struct  sockaddr_in6    rod_sin6;
-       } ro_dst;
-#define ro_dstsa       ro_dst.rod_sa
-#define ro_dstsin      ro_dst.rod_sin
-#define ro_dstsin6     ro_dst.rod_sin6
+               struct  sockaddr        ro_dstsa;
+               struct  sockaddr_in     ro_dstsin;
+               struct  sockaddr_in6    ro_dstsin6;
+       };
+       union {
+               struct  in_addr         ro_srcin;
+               struct  in6_addr        ro_srcin6;
+       };
 };
 
 #endif /* __BSD_VISIBLE */
@@ -462,8 +463,10 @@ struct if_ieee80211_data;
 struct bfd_config;
 
 void    route_init(void);
-int     route_cache(struct route *, struct in_addr, u_int);
-int     route6_cache(struct route *, const struct in6_addr *, u_int);
+int     route_cache(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);
 void    rtm_ifchg(struct ifnet *);
 void    rtm_ifannounce(struct ifnet *, int);
 void    rtm_bfd(struct bfd_config *);
index 87241c0..cc08e62 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in_pcb.c,v 1.293 2024/02/13 12:22:09 bluhm Exp $      */
+/*     $OpenBSD: in_pcb.c,v 1.294 2024/02/22 14:25:58 bluhm Exp $      */
 /*     $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $     */
 
 /*
@@ -919,7 +919,8 @@ in_pcbrtentry(struct inpcb *inp)
 
        if (inp->inp_faddr.s_addr == INADDR_ANY)
                return (NULL);
-       if (route_cache(ro, inp->inp_faddr, inp->inp_rtableid)) {
+       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);
        }
@@ -982,7 +983,7 @@ in_pcbselsrc(struct in_addr *insrc, struct sockaddr_in *sin,
         * 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, rtableid)) {
+       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);
        }
index 60c6e6a..d8709c9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_input.c,v 1.389 2024/02/13 12:22:09 bluhm Exp $    */
+/*     $OpenBSD: ip_input.c,v 1.390 2024/02/22 14:25:58 bluhm Exp $    */
 /*     $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $   */
 
 /*
@@ -118,7 +118,6 @@ const struct sysctl_bounded_args ipctl_vars[] = {
        { IPCTL_IPPORT_HILASTAUTO, &ipport_hilastauto, 0, 65535 },
        { IPCTL_IPPORT_MAXQUEUE, &ip_maxqueue, 0, 10000 },
        { IPCTL_MFORWARDING, &ipmforwarding, 0, 1 },
-       { IPCTL_MULTIPATH, &ipmultipath, 0, 1 },
        { IPCTL_ARPTIMEOUT, &arpt_keep, 0, INT_MAX },
        { IPCTL_ARPDOWN, &arpt_down, 0, INT_MAX },
 };
@@ -1491,7 +1490,7 @@ ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt)
        }
 
        ro.ro_rt = NULL;
-       route_cache(&ro, ip->ip_dst, m->m_pkthdr.ph_rtableid);
+       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,
@@ -1633,10 +1632,10 @@ int
 ip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
     size_t newlen)
 {
-       int error;
 #ifdef MROUTING
        extern struct mrtstat mrtstat;
 #endif
+       int oldval, error;
 
        /* Almost all sysctl names at this level are terminal. */
        if (namelen != 1 && name[0] != IPCTL_IFQUEUE &&
@@ -1721,6 +1720,15 @@ ip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
        case IPCTL_MRTVIF:
                return (EOPNOTSUPP);
 #endif
+       case IPCTL_MULTIPATH:
+               NET_LOCK();
+               oldval = ipmultipath;
+               error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
+                   &ipmultipath, 0, 1);
+               if (oldval != ipmultipath)
+                       atomic_inc_long(&rtgeneration);
+               NET_UNLOCK();
+               return (error);
        default:
                NET_LOCK();
                error = sysctl_bounded_arr(ipctl_vars, nitems(ipctl_vars),
index 561b690..0a60696 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_output.c,v 1.395 2024/02/13 12:22:09 bluhm Exp $   */
+/*     $OpenBSD: ip_output.c,v 1.396 2024/02/22 14:25:58 bluhm Exp $   */
 /*     $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $  */
 
 /*
@@ -166,7 +166,7 @@ reroute:
         * If there is a cached route, check that it is to the same
         * destination and is still up.  If not, free it and try again.
         */
-       route_cache(ro, ip->ip_dst, m->m_pkthdr.ph_rtableid);
+       route_cache(ro, &ip->ip_dst, &ip->ip_src, m->m_pkthdr.ph_rtableid);
        dst = &ro->ro_dstsin;
 
        if ((IN_MULTICAST(ip->ip_dst.s_addr) ||
index d622c5d..cea4415 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in6_pcb.c,v 1.138 2024/02/13 12:22:09 bluhm Exp $     */
+/*     $OpenBSD: in6_pcb.c,v 1.139 2024/02/22 14:25:58 bluhm Exp $     */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -565,7 +565,8 @@ in6_pcbrtentry(struct inpcb *inp)
 
        if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
                return (NULL);
-       if (route6_cache(ro, &inp->inp_faddr6, inp->inp_rtableid)) {
+       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);
        }
index 470ad67..ac35960 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in6_src.c,v 1.94 2024/02/13 12:22:09 bluhm Exp $      */
+/*     $OpenBSD: in6_src.c,v 1.95 2024/02/22 14:25:58 bluhm Exp $      */
 /*     $KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $        */
 
 /*
@@ -179,8 +179,8 @@ in6_pcbselsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
         * 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, rtableid)) {
-               ro->ro_rt = rtalloc(&ro->ro_dstsa, RT_RESOLVE, ro->ro_tableid);
+       if (route6_cache(ro, dst, NULL, rtableid)) {
+               ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, NULL, ro->ro_tableid);
        }
 
        /*
@@ -304,7 +304,7 @@ in6_selectroute(const struct in6_addr *dst, struct ip6_pktopts *opts,
         * a new one.
         */
        if (ro) {
-               if (route6_cache(ro, dst, rtableid)) {
+               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);
index cdb2fb4..65a51d5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_forward.c,v 1.114 2024/02/13 12:22:09 bluhm Exp $ */
+/*     $OpenBSD: ip6_forward.c,v 1.115 2024/02/22 14:25:58 bluhm Exp $ */
 /*     $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $    */
 
 /*
@@ -166,7 +166,8 @@ reroute:
 #endif /* IPSEC */
 
        ro.ro_rt = NULL;
-       route6_cache(&ro, &ip6->ip6_dst, m->m_pkthdr.ph_rtableid);
+       route6_cache(&ro, &ip6->ip6_dst, &ip6->ip6_src,
+           m->m_pkthdr.ph_rtableid);
        dst = &ro.ro_dstsa;
        if (!rtisvalid(rt)) {
                rtfree(rt);
index 21bd284..5d78407 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_input.c,v 1.257 2023/12/03 20:36:24 bluhm Exp $   */
+/*     $OpenBSD: ip6_input.c,v 1.258 2024/02/22 14:25:58 bluhm Exp $   */
 /*     $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $     */
 
 /*
@@ -1451,7 +1451,6 @@ const struct sysctl_bounded_args ipv6ctl_vars[] = {
        { IPV6CTL_USE_DEPRECATED, &ip6_use_deprecated, 0, 1 },
        { IPV6CTL_MAXFRAGS, &ip6_maxfrags, 0, 1000 },
        { IPV6CTL_MFORWARDING, &ip6_mforwarding, 0, 1 },
-       { IPV6CTL_MULTIPATH, &ip6_multipath, 0, 1 },
        { IPV6CTL_MCAST_PMTU, &ip6_mcast_pmtu, 0, 1 },
        { IPV6CTL_NEIGHBORGCTHRESH, &ip6_neighborgcthresh, -1, 5 * 2048 },
        { IPV6CTL_MAXDYNROUTES, &ip6_maxdynroutes, -1, 5 * 4096 },
@@ -1499,7 +1498,7 @@ ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
 #ifdef MROUTING
        extern struct mrt6stat mrt6stat;
 #endif
-       int error;
+       int oldval, error;
 
        /* Almost all sysctl names at this level are terminal. */
        if (namelen != 1 && name[0] != IPV6CTL_IFQUEUE)
@@ -1551,6 +1550,15 @@ ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
                    oldp, oldlenp, newp, newlen, &ip6intrq));
        case IPV6CTL_SOIIKEY:
                return (ip6_sysctl_soiikey(oldp, oldlenp, newp, newlen));
+       case IPV6CTL_MULTIPATH:
+               NET_LOCK();
+               oldval = ip6_multipath;
+               error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
+                   &ip6_multipath, 0, 1);
+               if (oldval != ip6_multipath)
+                       atomic_inc_long(&rtgeneration);
+               NET_UNLOCK();
+               return (error);
        default:
                NET_LOCK();
                error = sysctl_bounded_arr(ipv6ctl_vars, nitems(ipv6ctl_vars),
index adb5f30..e277d49 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_output.c,v 1.286 2024/02/13 12:22:09 bluhm Exp $  */
+/*     $OpenBSD: ip6_output.c,v 1.287 2024/02/22 14:25:58 bluhm Exp $  */
 /*     $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $    */
 
 /*
@@ -480,7 +480,7 @@ reroute:
                        goto bad;
                }
        } else {
-               route6_cache(ro, &ip6->ip6_dst, m->m_pkthdr.ph_rtableid);
+               route6_cache(ro, &ip6->ip6_dst, NULL, m->m_pkthdr.ph_rtableid);
        }
 
        if (rt && (rt->rt_flags & RTF_GATEWAY) &&