From owner-dev-commits-src-main@freebsd.org Tue Sep 7 21:43:20 2021 Return-Path: Delivered-To: dev-commits-src-main@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id D602766DD19; Tue, 7 Sep 2021 21:43:20 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4H3zKN5ZJHz4kgd; Tue, 7 Sep 2021 21:43:20 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 9F04619FB2; Tue, 7 Sep 2021 21:43:20 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 187LhKAY063614; Tue, 7 Sep 2021 21:43:20 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 187LhKww063613; Tue, 7 Sep 2021 21:43:20 GMT (envelope-from git) Date: Tue, 7 Sep 2021 21:43:20 GMT Message-Id: <202109072143.187LhKww063613@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: "Alexander V. Chernikov" Subject: git: 4b631fc832ac - main - routing: fix source address selection rules for IPv4 over IPv6. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: melifaro X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 4b631fc832acf1bab24aa88aa06229d368d8e131 Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-main@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for the main branch of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 07 Sep 2021 21:43:20 -0000 The branch main has been updated by melifaro: URL: https://cgit.FreeBSD.org/src/commit/?id=4b631fc832acf1bab24aa88aa06229d368d8e131 commit 4b631fc832acf1bab24aa88aa06229d368d8e131 Author: Alexander V. Chernikov AuthorDate: 2021-09-06 22:08:15 +0000 Commit: Alexander V. Chernikov CommitDate: 2021-09-07 21:41:05 +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 --- 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 e4ed7d1a2fd1..b99939179ff2 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -534,6 +534,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. * @@ -566,6 +601,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 ce03c0b6d4cb..e968a559a13c 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -188,6 +188,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 */