Date: Sat, 4 Dec 2021 19:15:35 GMT From: "Alexander V. Chernikov" <melifaro@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: b9772822a6b3 - stable/13 - routing: fix source address selection rules for IPv4 over IPv6. Message-ID: <202112041915.1B4JFZPN052281@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by melifaro: URL: https://cgit.FreeBSD.org/src/commit/?id=b9772822a6b363d2dad42ae8915730405986edb9 commit b9772822a6b363d2dad42ae8915730405986edb9 Author: Alexander V. Chernikov <melifaro@FreeBSD.org> AuthorDate: 2021-09-06 22:08:15 +0000 Commit: Alexander V. Chernikov <melifaro@FreeBSD.org> CommitDate: 2021-12-04 19:02:52 +0000 routing: fix source address selection rules for IPv4 over IPv6. Current logic always selects an IFA of the same family from the outgoing interfaces. In IPv4 over IPv6 setup there can be just single non-127.0.0.1 ifa, attached to the loopback interface. Create a separate rt_getifa_family() to handle entire ifa selection for the IPv4 over IPv6. Differential Revision: https://reviews.freebsd.org/D31868 MFC after: 1 week (cherry picked from commit 4b631fc832acf1bab24aa88aa06229d368d8e131) --- sys/net/route.c | 38 ++++++++++++++++++++++++++++++++++++++ sys/netinet/in.c | 34 ++++++++++++++++++++++++++++++++++ sys/netinet/in.h | 1 + 3 files changed, 73 insertions(+) diff --git a/sys/net/route.c b/sys/net/route.c index a24438563f50..b2c9051d98c0 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -532,6 +532,41 @@ info_get_ifp(struct rt_addrinfo *info) return (NULL); } +/* + * Calculates proper ifa/ifp for the cases when gateway AF is different + * from dst AF. + * + * Returns 0 on success. + */ +__noinline static int +rt_getifa_family(struct rt_addrinfo *info, uint32_t fibnum) +{ + if (info->rti_ifp == NULL) { + struct ifaddr *ifa = NULL; + /* + * No transmit interface specified. Guess it by checking gw sa. + */ + const struct sockaddr *gw = info->rti_info[RTAX_GATEWAY]; + ifa = ifa_ifwithroute(RTF_GATEWAY, gw, gw, fibnum); + if (ifa == NULL) + return (ENETUNREACH); + info->rti_ifp = ifa->ifa_ifp; + } + + /* Prefer address from outgoing interface */ + info->rti_ifa = ifaof_ifpforaddr(info->rti_info[RTAX_DST], info->rti_ifp); +#ifdef INET + if (info->rti_ifa == NULL) { + /* Use first found IPv4 address */ + bool loopback_ok = info->rti_ifp->if_flags & IFF_LOOPBACK; + info->rti_ifa = (struct ifaddr *)in_findlocal(fibnum, loopback_ok); + } +#endif + if (info->rti_ifa == NULL) + return (ENETUNREACH); + return (0); +} + /* * Look up rt_addrinfo for a specific fib. * @@ -564,6 +599,9 @@ rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum) */ if (info->rti_ifa == NULL && ifaaddr != NULL) info->rti_ifa = ifa_ifwithaddr(ifaaddr); + if ((info->rti_ifa == NULL) && ((info->rti_flags & RTF_GATEWAY) != 0) && + (gateway->sa_family != dst->sa_family)) + return (rt_getifa_family(info, fibnum)); if (info->rti_ifa == NULL) { const struct sockaddr *sa; diff --git a/sys/netinet/in.c b/sys/netinet/in.c index a0ea17e47154..b51f1111b88a 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -194,6 +194,40 @@ in_localip_more(struct in_ifaddr *original_ia) return (NULL); } +/* + * Tries to find first IPv4 address in the provided fib. + * Prefers non-loopback addresses and return loopback IFF + * @loopback_ok is set. + * + * Returns ifa or NULL. + */ +struct in_ifaddr * +in_findlocal(uint32_t fibnum, bool loopback_ok) +{ + struct rm_priotracker in_ifa_tracker; + struct in_ifaddr *ia = NULL, *ia_lo = NULL; + + NET_EPOCH_ASSERT(); + + IN_IFADDR_RLOCK(&in_ifa_tracker); + CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { + uint32_t ia_fib = ia->ia_ifa.ifa_ifp->if_fib; + if (!V_rt_add_addr_allfibs && (fibnum != ia_fib)) + continue; + + if (!IN_LOOPBACK(ntohl(IA_SIN(ia)->sin_addr.s_addr))) + break; + if (loopback_ok) + ia_lo = ia; + } + IN_IFADDR_RUNLOCK(&in_ifa_tracker); + + if (ia == NULL) + ia = ia_lo; + + return (ia); +} + /* * Determine whether an IP address is in a reserved set of addresses * that may not be forwarded, or whether datagrams to that destination diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 28d6721edc53..0206fd16d2fe 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -651,6 +651,7 @@ int in_canforward(struct in_addr); int in_localaddr(struct in_addr); int in_localip(struct in_addr); int in_ifhasaddr(struct ifnet *, struct in_addr); +struct in_ifaddr *in_findlocal(uint32_t, bool); int inet_aton(const char *, struct in_addr *); /* in libkern */ char *inet_ntoa_r(struct in_addr ina, char *buf); /* in libkern */ char *inet_ntop(int, const void *, char *, socklen_t); /* in libkern */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202112041915.1B4JFZPN052281>