From nobody Thu Aug 6 15:26:09 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hGB2B20xCz6nD3D for ; Thu, 06 Aug 2026 15:26:10 +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 "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hGB2B1Pq7z3MSm for ; Thu, 06 Aug 2026 15:26:10 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1786029970; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=tR5EhvatZ1xpBfaaYhTMnw72ZzfDVaHqF42Nxu1GSlA=; b=o+1XmRjyKjukRRMDfHCNxcySHcdoQqlKy1+mOsI4Mn2ssiHhYWMVZazpGsHGBH+3wR4ouv nF7Cvv69WNw0YxqEyQxq8od9PcuCaDghA4e7JCRZYUUxBqOP5wg6qCsc1mcIGVbTFpB446 f92T7gfCCqSiFj3N8/iEhwoFuH5RSkhkw90wMiTRuNcaDk4L6Rg7Z3JjSoxT0R9e2NjgLe vDflFGpFyUdlHKI2OJjC5E99RS13pQgfCaTUhB9r03MwyWAlW+25it9MRh2KFD/hzLboCG kk9U2z7O77Fty2hsj2BnzYteyxORLw+7XC8tsb+w27Sb/2og5gP1s8MiIcl7cQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1786029970; a=rsa-sha256; cv=none; b=K/H9NY3XjpgI0Cy71GLlW4Hrhojk3tqBweefvi6rKOEvMywIY2yGgyNRB3y4pQHKL3UppU R4TzL/a83EK7alE9L76Jqz4z55vbaI4AYRbLFeDRH5qeZgwZYA43wxwFPqr+Uzko6f8XLa WULALISiWo//1JXamvAguw0Qzbep8/HV0dzs72S4VSIsgwmfRmHdnvW8oOos5SwXvr+zqH EiBZfCc9QwHXHocp2NsTPdQBzImdhx9jJuETOz1w77yJ1m/ZHuEhsSY7wGw1TRYQhkndi0 goaLkmgdWDS4TG1ap5ixYYqBsyMmy6VbpbzISDp/hwcPe1SOdyKM5Co3UALCLg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1786029970; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=tR5EhvatZ1xpBfaaYhTMnw72ZzfDVaHqF42Nxu1GSlA=; b=UZOqjxeFov8N7aUnz5VPg3hnoiODJBIunvsgBczXKzHl2HlMFRLlAoxbTaJS3x6MffD23Z JgHECeZZglPo6U7iAytFtMNwL/pflBfY56mulV4ZZDcVfxlLQ8/1ncYWM/Zd0zGng3rD4F KW/REtS7H6dZA00Ornpl7XuBHo/kpvVpdkcgQajJpuXVo+y6KINmsV9dvXdlRyU1cQlOMf 6WbY++/+hKxb2DtQFLMXNobCeo1YSMh6g90vlEXyiCpXXoS5FcmQcM4Enrlw8rdGHak0K8 OWwQNGzVJ3AOx/iIoDf0F8rLrHVvy8vNekj+pJYR9CoV2zb8NrUgNpdX1SJm/w== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hGB2B04DTz9cQ for ; Thu, 06 Aug 2026 15:26:10 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 18343 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 06 Aug 2026 15:26:09 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Bojan Novk=?utf-8?Q?ovi=C4=87?= Subject: git: 978b9026b018 - stable/15 - bind(2): Lookup local address in current FIB if '*.bind_all_fibs' is active List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: bnovkov X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 978b9026b01852bc78289fd2c8c747c78441b5f1 Auto-Submitted: auto-generated Date: Thu, 06 Aug 2026 15:26:09 +0000 Message-Id: <6a74a791.18343.42b24743@gitrepo.freebsd.org> The branch stable/15 has been updated by bnovkov: URL: https://cgit.FreeBSD.org/src/commit/?id=978b9026b01852bc78289fd2c8c747c78441b5f1 commit 978b9026b01852bc78289fd2c8c747c78441b5f1 Author: Bojan Novković AuthorDate: 2026-07-15 13:47:01 +0000 Commit: Bojan Novković CommitDate: 2026-08-06 15:25:24 +0000 bind(2): Lookup local address in current FIB if '*.bind_all_fibs' is active When a protocol-specific 'bind_all_fibs' tunable is set to 0, a listening socket will only receive traffic originating from the FIB it was bound to. However, there are no checks to determine whether an address exists in the target FIB when binding the socket, which can lead to a situation where a socket and the address it was bound to belong to different FIBs. Prevent this footgun by looking up the requested address in the current FIB if 'bind_all_fibs' is active and returning an error if the address does not exist. Sponsored by: Stormshield Sponsored by: Klara, Inc. Differential Revision: https://reviews.freebsd.org/D58281 Reviewed by: glebius, pouria, markj MFC after: 2 weeks (cherry picked from commit 948ad32ae1e0811f45e1d38f26636fefed5051f0) --- sys/netinet/in_pcb.c | 2 +- sys/netinet/raw_ip.c | 7 ++- sys/netinet6/in6_pcb.c | 2 +- sys/netinet6/raw_ip6.c | 7 ++- tests/sys/netinet/Makefile | 1 + tests/sys/netinet/fib_bind.py | 100 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 6 deletions(-) diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index f60802d255e1..e59ec7fae8c7 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -951,7 +951,7 @@ in_pcbbind_avail(struct inpcb *inp, const struct in_addr laddr, * to any endpoint address, local or not. */ if ((inp->inp_flags & INP_BINDANY) == 0 && - ifa_ifwithaddr_check((const struct sockaddr *)&sin) == 0) + ifa_ifwithaddr_fib_check((const struct sockaddr *)&sin, fib) == 0) return (EADDRNOTAVAIL); } diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index dfb7cbeaf710..2d9871f9911f 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -935,7 +935,7 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct thread *td) { struct sockaddr_in *addr = (struct sockaddr_in *)nam; struct inpcb *inp; - int error; + int fib, error; if (nam->sa_family != AF_INET) return (EAFNOSUPPORT); @@ -949,11 +949,14 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct thread *td) inp = sotoinpcb(so); KASSERT(inp != NULL, ("rip_bind: inp == NULL")); + fib = V_rip_bind_all_fibs == 0 ? inp->inp_inc.inc_fibnum : + RT_ALL_FIBS; + if (CK_STAILQ_EMPTY(&V_ifnet) || (addr->sin_family != AF_INET && addr->sin_family != AF_IMPLINK) || (addr->sin_addr.s_addr && (inp->inp_flags & INP_BINDANY) == 0 && - ifa_ifwithaddr_check((struct sockaddr *)addr) == 0)) + ifa_ifwithaddr_fib_check((struct sockaddr *)addr, fib) == 0)) return (EADDRNOTAVAIL); INP_WLOCK(inp); diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index dfda0c60c0ba..16dcda743ab8 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -207,7 +207,7 @@ in6_pcbbind_avail(struct inpcb *inp, const struct sockaddr_in6 *sin6, int fib, sin6.sin6_addr = *laddr; NET_EPOCH_ENTER(et); - if ((ifa = ifa_ifwithaddr((const struct sockaddr *)&sin6)) == + if ((ifa = ifa_ifwithaddr_fib((const struct sockaddr *)&sin6, fib)) == NULL && (inp->inp_flags & INP_BINDANY) == 0) { NET_EPOCH_EXIT(et); return (EADDRNOTAVAIL); diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 777396304890..ea12626893a4 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -747,7 +747,7 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) struct inpcb *inp; struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct ifaddr *ifa = NULL; - int error = 0; + int fib, error = 0; inp = sotoinpcb(so); KASSERT(inp != NULL, ("rip6_bind: inp == NULL")); @@ -763,9 +763,12 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0) return (error); + fib = V_rip_bind_all_fibs == 0 ? inp->inp_inc.inc_fibnum : + RT_ALL_FIBS; + NET_EPOCH_ENTER(et); if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && - (ifa = ifa_ifwithaddr((struct sockaddr *)addr)) == NULL) { + (ifa = ifa_ifwithaddr_fib((struct sockaddr *)addr, fib)) == NULL) { NET_EPOCH_EXIT(et); return (EADDRNOTAVAIL); } diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile index cdb31b4be23c..d3588480071f 100644 --- a/tests/sys/netinet/Makefile +++ b/tests/sys/netinet/Makefile @@ -31,6 +31,7 @@ ATF_TESTS_SH= arp \ ATF_TESTS_PYTEST+= carp.py ATF_TESTS_PYTEST+= igmp.py ATF_TESTS_PYTEST+= ip_mroute.py +ATF_TESTS_PYTEST+= fib_bind.py LIBADD.so_reuseport_lb_test= pthread LIBADD.udp_bindings= pthread diff --git a/tests/sys/netinet/fib_bind.py b/tests/sys/netinet/fib_bind.py new file mode 100644 index 000000000000..6768fcee5009 --- /dev/null +++ b/tests/sys/netinet/fib_bind.py @@ -0,0 +1,100 @@ +# +# Copyright (c) 2026 Stormshield +# +# SPDX-License-Identifier: BSD-2-Clause +# + +import pytest +import errno +import os +import socket +import struct +import sys +import logging + +from atf_python.sys.net.vnet import VnetTestTemplate +from atf_python.sys.net.tools import ToolsHelper + +def _common(tunable, addr, domain, type, proto): + """ + Test what happens when we try to bind a socket to an + address that is not present in the current FIB. + """ + sysctl_output = ToolsHelper.get_output(f"sysctl {tunable}") + tunable_val = int(sysctl_output.split(":")[1]) + if tunable_val != 0: + pytest.skip(f"{tunable} must be set to 0") + + port = 12345 if type != socket.SOCK_RAW else 0 + s = socket.socket(domain, type, proto) + s.setsockopt(socket.SOL_SOCKET, socket.SO_SETFIB, 0) + + failed = False + try: + s.bind((addr, port)) + except OSError as e: + assert e.errno == errno.EADDRNOTAVAIL + failed = True + assert failed + + s.setsockopt(socket.SOL_SOCKET, socket.SO_SETFIB, 1) + + failed = False + try: + s.bind((addr, port)) + except OSError: + failed = True + assert not failed + s.close() + +class TestFibBind(VnetTestTemplate): + REQUIRED_MODULES = [] + TOPOLOGY = { + "vnet1": { "ifaces": [ "if1", "if2" ] }, + "if1": { + "prefixes4": [("192.168.1.1/24", "192.168.1.2/24")], + "fib": (0, 0), + }, + "if2": { + "prefixes4": [("192.168.2.1/24", "192.168.2.2/24")], + "fib": (1, 0), + }, + } + + def setup_method(self, method): + super().setup_method(method) + + def test_TCP(self): + _common("net.inet.tcp.bind_all_fibs", "192.168.2.1", socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) + + def test_UDP(self): + _common("net.inet.udp.bind_all_fibs", "192.168.2.1", socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + + def test_RAW(self): + _common("net.inet.raw.bind_all_fibs", "192.168.2.1", socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP) + +class TestFibBind6(VnetTestTemplate): + REQUIRED_MODULES = [] + TOPOLOGY = { + "vnet1": { "ifaces": [ "if1", "if2" ] }, + "if1": { + "prefixes6": [("2001:db8:0:1::1/64", "2001:db8:0:1::2/64")], + "fib": (0, 0), + }, + "if2": { + "prefixes6": [("2001:db8:0:2::1/64", "2001:db8:0:2::2/64")], + "fib": (1, 0), + }, + } + + def setup_method(self, method): + super().setup_method(method) + + def test_TCP(self): + _common("net.inet.tcp.bind_all_fibs", "2001:db8:0:2::1", socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP) + + def test_UDP(self): + _common("net.inet.udp.bind_all_fibs", "2001:db8:0:2::1", socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + + def test_RAW(self): + _common("net.inet.raw.bind_all_fibs", "2001:db8:0:2::1", socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_IPV6)