git: 1cbeeecca1 - main - Add SA-25:09.

From: Gordon Tetlow <gordon_at_FreeBSD.org>
Date: Wed, 22 Oct 2025 16:48:54 UTC
The branch main has been updated by gordon:

URL: https://cgit.FreeBSD.org/doc/commit/?id=1cbeeecca1ddae85922fd004a0084284b98ce66e

commit 1cbeeecca1ddae85922fd004a0084284b98ce66e
Author:     Gordon Tetlow <gordon@FreeBSD.org>
AuthorDate: 2025-10-22 16:48:30 +0000
Commit:     Gordon Tetlow <gordon@FreeBSD.org>
CommitDate: 2025-10-22 16:48:30 +0000

    Add SA-25:09.
    
    Approved by:    so
---
 website/data/security/advisories.toml              |   4 +
 .../advisories/FreeBSD-SA-25:09.netinet.asc        | 162 ++++++++++++++
 .../security/patches/SA-25:09/netinet-13.patch     | 244 +++++++++++++++++++++
 .../security/patches/SA-25:09/netinet-13.patch.asc |  16 ++
 .../security/patches/SA-25:09/netinet-14.patch     | 198 +++++++++++++++++
 .../security/patches/SA-25:09/netinet-14.patch.asc |  16 ++
 .../security/patches/SA-25:09/netinet-15.patch     | 201 +++++++++++++++++
 .../security/patches/SA-25:09/netinet-15.patch.asc |  16 ++
 8 files changed, 857 insertions(+)

diff --git a/website/data/security/advisories.toml b/website/data/security/advisories.toml
index dfcfa583b1..ad0f58dfe3 100644
--- a/website/data/security/advisories.toml
+++ b/website/data/security/advisories.toml
@@ -1,6 +1,10 @@
 # Sort advisories by year, month and day
 # $FreeBSD$
 
+[[advisories]]
+name = "FreeBSD-SA-25:09.netinet"
+date = "2025-10-22"
+
 [[advisories]]
 name = "FreeBSD-SA-25:08.openssl"
 date = "2025-09-30"
diff --git a/website/static/security/advisories/FreeBSD-SA-25:09.netinet.asc b/website/static/security/advisories/FreeBSD-SA-25:09.netinet.asc
new file mode 100644
index 0000000000..49fe1c653f
--- /dev/null
+++ b/website/static/security/advisories/FreeBSD-SA-25:09.netinet.asc
@@ -0,0 +1,162 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-25:09.netinet                                     Security Advisory
+                                                          The FreeBSD Project
+
+Topic:          SO_REUSEPORT_LB breaks connect(2) for UDP sockets
+
+Category:       core
+Module:         netinet
+Announced:      2025-10-22
+Credits:        MSc. student Omer Ben Simhon and Prof. Amit Klein,
+                both from the Hebrew University School of Computer
+                Science and Engineering
+Affects:        All supported versions of FreeBSD.
+Corrected:      2025-10-22 15:48:25 UTC (stable/15, 15.0-STABLE)
+                2025-10-22 15:50:30 UTC (releng/15.0, 15.0-BETA2-p1)
+                2025-10-22 15:48:51 UTC (stable/14, 14.3-STABLE)
+                2025-10-22 15:51:57 UTC (releng/14.3, 14.3-RELEASE-p5)
+                2025-10-22 15:49:32 UTC (stable/13, 13.4-STABLE)
+                2025-10-22 15:53:35 UTC (releng/13.5, 13.5-RELEASE-p6)
+CVE Name:       CVE-2025-24934
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit <URL:https://security.FreeBSD.org/>.
+
+I.   Background
+
+SO_REUSEPORT_LB is a socket option, set by setsockopt(2), which allows multiple
+TCP or UDP sockets to bind to the same socket address, creating a
+load-balancing group.  Incoming packets and connections are distributed evenly
+among sockets in a group.  This helps network services avoid scalability
+bottlenecks caused by having a single TCP listening socket.  In particular, it
+is expected that sockets belonging to a load-balancing group will accept
+packets from any source address.
+
+II.  Problem Description
+
+Connected sockets are not intended to belong to load-balancing groups.
+However, the kernel failed to check the connection state of sockets when adding
+them to load-balancing groups.  Furthermore, when looking up the destination
+socket for an incoming packet, the kernel will match a socket belonging to a
+load-balancing group even if it is connected.
+
+Connected sockets are only supposed to receive packets originating from the
+connected host.  The above behavior violates this contract.
+
+III. Impact
+
+Software which sets SO_REUSEPORT_LB on a socket and then connects it to a host
+will not observe any problems.  However, due to its membership in a
+load-balancing group, that socket will receive packets originating from any
+host.  This breaks the contract of the connect(2) and implied connect via
+sendto(2), and may leave the application vulnerable to spoofing attacks.
+
+IV.  Workaround
+
+No workaround is available.  Software which does not use SO_REUSEPORT_LB is
+not affected.
+
+V.   Solution
+
+Upgrade your vulnerable system to a supported FreeBSD stable or
+release / security branch (releng) dated after the correction date.
+
+Perform one of the following:
+
+1) To update your vulnerable system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms,
+or the i386 platform on FreeBSD 13, can be updated via the freebsd-update(8)
+utility:
+
+# freebsd-update fetch
+# freebsd-update install
+# shutdown -r +10min "Rebooting for a security update"
+
+2) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+[FreeBSD 15.x]
+# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-15.patch
+# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-15.patch.asc
+# gpg --verify netinet-15.patch.asc
+
+[FreeBSD 14.x]
+# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-14.patch
+# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-14.patch.asc
+# gpg --verify netinet-14.patch.asc
+
+[FreeBSD 13.x]
+# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-13.patch
+# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-13.patch.asc
+# gpg --verify netinet-13.patch.asc
+
+b) Apply the patch.  Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile your kernel as described in
+<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> and reboot the
+system.
+
+VI.  Correction details
+
+This issue is corrected as of the corresponding Git commit hash in the
+following stable and release branches:
+
+Branch/path                             Hash                     Revision
+- -------------------------------------------------------------------------
+stable/15/                              ef159100ec2b    stable/15-n280782
+releng/15.0/                            98c539667881  releng/15.0-n280723
+stable/14/                              e276759b3687    stable/14-n272700
+releng/14.3/                            058bcb57cd4b  releng/14.3-n271448
+stable/13/                              df888c8f41f6    stable/13-n259508
+releng/13.5/                            90e14aa082d3  releng/13.5-n259180
+- -------------------------------------------------------------------------
+
+Run the following command to see which files were modified by a
+particular commit:
+
+# git show --stat <commit hash>
+
+Or visit the following URL, replacing NNNNNN with the hash:
+
+<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
+
+To determine the commit count in a working tree (for comparison against
+nNNNNNN in the table above), run:
+
+# git rev-list --count --first-parent HEAD
+
+VII. References
+
+<URL:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-24934>
+
+The latest revision of this advisory is available at
+<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-25:09.netinet.asc>
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrEACgkQbljekB8A
+Gu98YQ//dMMpEdKapK6bBM++8HoSWeydnoUifFqu3LiDXcDTgQ6jVsmwQ/QOUPll
+bOB7etdtu+FQEI4yl8d9w98TrXC8Mvl6p+dZ3SkIglLNeVmouiot+VDBpoOr/EPq
+xXf6dGlkDneYTsAFXwDKe48vmisdWd1trtYhVuE6qWq54AH4Y3dv0+DOMIdlKbPc
+GHFLRoJ/eEJH+3QAhL8Ozdp2WySUWHPMsScBRldcrhariXzEQ9KcM6TJx8mYGKta
+DYeezna1DQ87wU8Zs5fKfhUS6q/YJcXr9Te5P1xirmcmgr2frJW1DjfWKI8oQ9ru
+2mn6oedSu6nRFjpYzO9tS/7svC8Hkyyr1rsZujRkC5cMRwY2DovU653GoaOwadMc
+gig8CvOeb1srD1kMnFyGfa54VTbGZCZ261bnGdUc9BCL8ARtv6q4KNTRofkYrCLP
+YwGTxEsCVdNbtDGv5nLJ/V7RfAUMnp9YuYpHc0Auttt6cUW6DI3nGQg+LlfoCJ0n
+JESXa3Fry0GcFWiPB6oigyFSH6c3Ml+E7TiUYAZOtQ4cqJG1v9x1Lv5BQ1dz5vah
+J24oGW2uI6Xp0TbvIFBd6KCFZSa/dS9sq486norj17X7ktZ7EeVVpm4vRBtDEo4N
+k2WdkjcWfSM5uLnYLZR+rp+1rhtSIxw3gZaoJLl18p+9NMOFBH4=
+=RgID
+-----END PGP SIGNATURE-----
diff --git a/website/static/security/patches/SA-25:09/netinet-13.patch b/website/static/security/patches/SA-25:09/netinet-13.patch
new file mode 100644
index 0000000000..49031737eb
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-13.patch
@@ -0,0 +1,244 @@
+--- sys/netinet/in_pcb.c.orig
++++ sys/netinet/in_pcb.c
+@@ -2668,6 +2668,7 @@
+ 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ 	struct inpcbport *phd;
+ 	u_int32_t hashkey_faddr;
++	bool connected;
+ 
+ 	INP_WLOCK_ASSERT(inp);
+ 	INP_HASH_WLOCK_ASSERT(pcbinfo);
+@@ -2676,11 +2677,15 @@
+ 	    ("in_pcbinshash: INP_INHASHLIST"));
+ 
+ #ifdef INET6
+-	if (inp->inp_vflag & INP_IPV6)
++	if (inp->inp_vflag & INP_IPV6) {
+ 		hashkey_faddr = INP6_PCBHASHKEY(&inp->in6p_faddr);
+-	else
++		connected = !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr);
++	} else
+ #endif
+-	hashkey_faddr = inp->inp_faddr.s_addr;
++	{
++		hashkey_faddr = inp->inp_faddr.s_addr;
++		connected = inp->inp_faddr.s_addr != INADDR_ANY;
++	}
+ 
+ 	pcbhash = &pcbinfo->ipi_hashbase[INP_PCBHASH(hashkey_faddr,
+ 		 inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
+@@ -2689,10 +2694,12 @@
+ 	    INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
+ 
+ 	/*
+-	 * Add entry to load balance group.
+-	 * Only do this if SO_REUSEPORT_LB is set.
++	 * Ignore SO_REUSEPORT_LB if the socket is connected.  Really this case
++	 * should be an error, but for UDP sockets it is not, and some
++	 * applications erroneously set it on connected UDP sockets, so we can't
++	 * change this without breaking compatibility.
+ 	 */
+-	if ((inp->inp_flags2 & INP_REUSEPORT_LB) != 0) {
++	if (!connected && (inp->inp_flags2 & INP_REUSEPORT_LB) != 0) {
+ 		int error = in_pcbinslbgrouphash(inp, M_NODOM);
+ 		if (error != 0)
+ 			return (error);
+@@ -2761,6 +2768,7 @@
+ 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ 	struct inpcbhead *head;
+ 	u_int32_t hashkey_faddr;
++	bool connected;
+ 
+ 	INP_WLOCK_ASSERT(inp);
+ 	INP_HASH_WLOCK_ASSERT(pcbinfo);
+@@ -2769,11 +2777,19 @@
+ 	    ("in_pcbrehash: !INP_INHASHLIST"));
+ 
+ #ifdef INET6
+-	if (inp->inp_vflag & INP_IPV6)
++	if (inp->inp_vflag & INP_IPV6) {
+ 		hashkey_faddr = INP6_PCBHASHKEY(&inp->in6p_faddr);
+-	else
++		connected = !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr);
++	} else
+ #endif
+-	hashkey_faddr = inp->inp_faddr.s_addr;
++	{
++		hashkey_faddr = inp->inp_faddr.s_addr;
++		connected = inp->inp_faddr.s_addr != INADDR_ANY;
++	}
++
++	/* See the comment in in_pcbinshash(). */
++	if (connected && (inp->inp_flags2 & INP_REUSEPORT_LB) != 0)
++		in_pcbremlbgrouphash(inp);
+ 
+ 	head = &pcbinfo->ipi_hashbase[INP_PCBHASH(hashkey_faddr,
+ 		inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
+--- tests/sys/netinet/so_reuseport_lb_test.c.orig
++++ tests/sys/netinet/so_reuseport_lb_test.c
+@@ -29,6 +29,8 @@
+ 
+ #include <sys/cdefs.h>
+ #include <sys/param.h>
++#include <sys/filio.h>
++#include <sys/ioccom.h>
+ #include <sys/socket.h>
+ 
+ #include <netinet/in.h>
+@@ -236,10 +238,156 @@
+ 	}
+ }
+ 
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set.  Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp);
++ATF_TC_BODY(connect_udp, tc)
++{
++	struct sockaddr_in sin = {
++		.sin_family = AF_INET,
++		.sin_len = sizeof(sin),
++		.sin_addr = { htonl(INADDR_LOOPBACK) },
++	};
++	ssize_t n;
++	int error, len, s1, s2, s3;
++	char ch;
++
++	s1 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s1 >= 0);
++	s2 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s2 >= 0);
++	s3 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s3 >= 0);
++
++	error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++	    sizeof(int));
++	ATF_REQUIRE_MSG(error == 0,
++	    "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++	error = bind(s1, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s2, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s3, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	/* Connect to an address not owned by s2. */
++	error = getsockname(s3, (struct sockaddr *)&sin,
++	    (socklen_t[]){sizeof(sin)});
++	ATF_REQUIRE(error == 0);
++	error = connect(s1, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++	/* Try to send a packet to s1 from s2. */
++	error = getsockname(s1, (struct sockaddr *)&sin,
++	    (socklen_t[]){sizeof(sin)});
++	ATF_REQUIRE(error == 0);
++
++	ch = 42;
++	n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++	    sizeof(sin));
++	ATF_REQUIRE(n == 1);
++
++	/* Give the packet some time to arrive. */
++	usleep(100000);
++
++	/* s1 is connected to s3 and shouldn't receive from s2. */
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++	/* ... but s3 can of course send to s1. */
++	n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++	    sizeof(sin));
++	ATF_REQUIRE(n == 1);
++	usleep(100000);
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len >= 1, "expected data available");
++}
++
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set.  Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp6);
++ATF_TC_BODY(connect_udp6, tc)
++{
++	struct sockaddr_in6 sin6 = {
++		.sin6_family = AF_INET6,
++		.sin6_len = sizeof(sin6),
++		.sin6_addr = IN6ADDR_LOOPBACK_INIT,
++	};
++	ssize_t n;
++	int error, len, s1, s2, s3;
++	char ch;
++
++	s1 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s1 >= 0);
++	s2 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s2 >= 0);
++	s3 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s3 >= 0);
++
++	error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++	    sizeof(int));
++	ATF_REQUIRE_MSG(error == 0,
++	    "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++	error = bind(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s2, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s3, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	/* Connect to an address not owned by s2. */
++	error = getsockname(s3, (struct sockaddr *)&sin6,
++	    (socklen_t[]){sizeof(sin6)});
++	ATF_REQUIRE(error == 0);
++	error = connect(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++	/* Try to send a packet to s1 from s2. */
++	error = getsockname(s1, (struct sockaddr *)&sin6,
++	    (socklen_t[]){sizeof(sin6)});
++	ATF_REQUIRE(error == 0);
++
++	ch = 42;
++	n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++	    sizeof(sin6));
++	ATF_REQUIRE(n == 1);
++
++	/* Give the packet some time to arrive. */
++	usleep(100000);
++
++	/* s1 is connected to s3 and shouldn't receive from s2. */
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len >= 0, "unexpected data available");
++
++	/* ... but s3 can of course send to s1. */
++	n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++	    sizeof(sin6));
++	ATF_REQUIRE(n == 1);
++	usleep(100000);
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len >= 1, "expected data available");
++}
++
+ ATF_TP_ADD_TCS(tp)
+ {
+ 	ATF_TP_ADD_TC(tp, basic_ipv4);
+ 	ATF_TP_ADD_TC(tp, basic_ipv6);
++	ATF_TP_ADD_TC(tp, connect_udp);
++	ATF_TP_ADD_TC(tp, connect_udp6);
+ 
+ 	return (atf_no_error());
+ }
diff --git a/website/static/security/patches/SA-25:09/netinet-13.patch.asc b/website/static/security/patches/SA-25:09/netinet-13.patch.asc
new file mode 100644
index 0000000000..4767da0d8d
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-13.patch.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrcACgkQbljekB8A
+Gu+Aqg/8CJPC1rYA+WwpTlAFbQ4HbNrWWptKQvnvvc9qZ6I74p4B4g5tXTsarJaw
+Y5fEX4o+SU1aM2x3jLEEKXvjm+BHjeI8OFWDIXsSwg6SH9CkXiiqVeFsgYl7ld0R
+W1YU+1QN8/4co/QLOgbRAPFcTm8z6FX6yzWcWRnwrHksT6lSu6q0FTTm//2T+upN
+QdW8L19dV0zvL36aA47P7WR5aiaRuyDj9K8gpQnD/rlCPjMpwmuVXdlvQDs7m0uE
+4fbrNULAk+2QXUMqWG8qUbpLgAK5oNrI5dGVXzWwJ98pOm5gO7rozWlAE4bn46nk
+9/4cMWVZoYHp4Ui0iHqb9nvdJQq21jFS1408Bxsi4sT+nztRsbO8plD3ihSiG+XL
+VVcauVUxxf8ezbJmTSji5HTnSIs16kHPiVGCgEuX0bBeItyqrT9p6v379Jw2pSgH
+FQHNGoFYJQ0KDlEFxpxChpZyyH7DMKYF0ckwd9apsD8HCUvw1w6y89UjahPpb7Gj
+2p3O8NvEFpy0ODL0/h5G7Wc6hzs++i/gaiXiRZXhMtXY0rlpcH6N5SrTso2jY2SA
+yEOM1AZV9v9hzS6st4R+Tot/e3j4OlxMjhSKJu9F3VyGyNbIhXFW8pMvjTD06CWM
+YSLX4qyBoHhkrMpsj53acGif0hlikN59tuAuVRjGeXRgrbQudkU=
+=4UW4
+-----END PGP SIGNATURE-----
diff --git a/website/static/security/patches/SA-25:09/netinet-14.patch b/website/static/security/patches/SA-25:09/netinet-14.patch
new file mode 100644
index 0000000000..0c022135e8
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-14.patch
@@ -0,0 +1,198 @@
+--- sys/netinet/in_pcb.c.orig
++++ sys/netinet/in_pcb.c
+@@ -2702,10 +2702,13 @@
+ 	    INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
+ 
+ 	/*
+-	 * Add entry to load balance group.
+-	 * Only do this if SO_REUSEPORT_LB is set.
++	 * Ignore SO_REUSEPORT_LB if the socket is connected.  Really this case
++	 * should be an error, but for UDP sockets it is not, and some
++	 * applications erroneously set it on connected UDP sockets, so we can't
++	 * change this without breaking compatibility.
+ 	 */
+-	if ((inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) {
++	if (!connected &&
++	    (inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) {
+ 		int error = in_pcbinslbgrouphash(inp, M_NODOM);
+ 		if (error != 0)
+ 			return (error);
+@@ -2836,6 +2839,10 @@
+ 		connected = !in_nullhost(inp->inp_faddr);
+ 	}
+ 
++	/* See the comment in in_pcbinshash(). */
++	if (connected && (inp->inp_flags & INP_INLBGROUP) != 0)
++		in_pcbremlbgrouphash(inp);
++
+ 	/*
+ 	 * When rehashing, the caller must ensure that either the new or the old
+ 	 * foreign address was unspecified.
+--- tests/sys/netinet/so_reuseport_lb_test.c.orig
++++ tests/sys/netinet/so_reuseport_lb_test.c
+@@ -29,6 +29,8 @@
+ 
+ #include <sys/cdefs.h>
+ #include <sys/param.h>
++#include <sys/filio.h>
++#include <sys/ioccom.h>
+ #include <sys/socket.h>
+ 
+ #include <netinet/in.h>
+@@ -236,10 +238,156 @@
+ 	}
+ }
+ 
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set.  Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp);
++ATF_TC_BODY(connect_udp, tc)
++{
++	struct sockaddr_in sin = {
++		.sin_family = AF_INET,
++		.sin_len = sizeof(sin),
++		.sin_addr = { htonl(INADDR_LOOPBACK) },
++	};
++	ssize_t n;
++	int error, len, s1, s2, s3;
++	char ch;
++
++	s1 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s1 >= 0);
++	s2 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s2 >= 0);
++	s3 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s3 >= 0);
++
++	error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++	    sizeof(int));
++	ATF_REQUIRE_MSG(error == 0,
++	    "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++	error = bind(s1, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s2, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s3, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	/* Connect to an address not owned by s2. */
++	error = getsockname(s3, (struct sockaddr *)&sin,
++	    (socklen_t[]){sizeof(sin)});
++	ATF_REQUIRE(error == 0);
++	error = connect(s1, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++	/* Try to send a packet to s1 from s2. */
++	error = getsockname(s1, (struct sockaddr *)&sin,
++	    (socklen_t[]){sizeof(sin)});
++	ATF_REQUIRE(error == 0);
++
++	ch = 42;
++	n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++	    sizeof(sin));
++	ATF_REQUIRE(n == 1);
++
++	/* Give the packet some time to arrive. */
++	usleep(100000);
++
++	/* s1 is connected to s3 and shouldn't receive from s2. */
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++	/* ... but s3 can of course send to s1. */
++	n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++	    sizeof(sin));
++	ATF_REQUIRE(n == 1);
++	usleep(100000);
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 1, "unexpected data available");
++}
++
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set.  Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp6);
++ATF_TC_BODY(connect_udp6, tc)
++{
++	struct sockaddr_in6 sin6 = {
++		.sin6_family = AF_INET6,
++		.sin6_len = sizeof(sin6),
++		.sin6_addr = IN6ADDR_LOOPBACK_INIT,
++	};
++	ssize_t n;
++	int error, len, s1, s2, s3;
++	char ch;
++
++	s1 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s1 >= 0);
++	s2 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s2 >= 0);
++	s3 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s3 >= 0);
++
++	error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++	    sizeof(int));
++	ATF_REQUIRE_MSG(error == 0,
++	    "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++	error = bind(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s2, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s3, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	/* Connect to an address not owned by s2. */
++	error = getsockname(s3, (struct sockaddr *)&sin6,
++	    (socklen_t[]){sizeof(sin6)});
++	ATF_REQUIRE(error == 0);
++	error = connect(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++	/* Try to send a packet to s1 from s2. */
++	error = getsockname(s1, (struct sockaddr *)&sin6,
++	    (socklen_t[]){sizeof(sin6)});
++	ATF_REQUIRE(error == 0);
++
++	ch = 42;
++	n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++	    sizeof(sin6));
++	ATF_REQUIRE(n == 1);
++
++	/* Give the packet some time to arrive. */
++	usleep(100000);
++
++	/* s1 is connected to s3 and shouldn't receive from s2. */
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++	/* ... but s3 can of course send to s1. */
++	n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++	    sizeof(sin6));
++	ATF_REQUIRE(n == 1);
++	usleep(100000);
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 1, "unexpected data available");
++}
++
+ ATF_TP_ADD_TCS(tp)
+ {
+ 	ATF_TP_ADD_TC(tp, basic_ipv4);
+ 	ATF_TP_ADD_TC(tp, basic_ipv6);
++	ATF_TP_ADD_TC(tp, connect_udp);
++	ATF_TP_ADD_TC(tp, connect_udp6);
+ 
+ 	return (atf_no_error());
+ }
diff --git a/website/static/security/patches/SA-25:09/netinet-14.patch.asc b/website/static/security/patches/SA-25:09/netinet-14.patch.asc
new file mode 100644
index 0000000000..b0c2e2429c
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-14.patch.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrgACgkQbljekB8A
+Gu+IoRAAlJrsOBxL+/qrj25ehBLzbEmgD3t6xdbz3GboR1Nfwx1ragW11xHR0sCN
+Mx73rW8Gvf9vOAThvSPs4ajMq4gEmu5tTz8LR5wZnsiGQJrxgz8OZLIvQPIfiF0X
+deaXmWE/QK+7T3zqGM5uQIv2I8XIhx6cyvnm5sXFL/cpjiWwWwo3eMiB4k5ecW0w
+HZqF/VclSAnB7VhkyvoVOU45+9DdgG6wVdGTBZbGOm6Y7JKkrrlIH84yb4onNanx
+XjPOwD+TNXFGlz1rS3R5KuVsEUx1TR3NCYkrjBZcTVFul3YhnH+Cvn2LxUKv+Brf
+1EVywW11lF2FMa+cukIaei6Dnka79UnHdarKaCyBseSFmzmcV+XSb0dsvDoEF4mx
+XvaIn7BBoEfcBcH2HB46huUWeVWAVvjC4qpkoKGYbiYnS+iamA+uTrazeP5zkgnz
+f1KRpgVvAzFNQqGhUI6AO9m+/DugShjtHN6oT8HmKTNfEo2/nbEWGh1+KNCTWMfr
+CtVWBwSCV0UECH5DcKDcbjtgoqnJ2qNkooye2ruSjbLkOr6wyWMcNnhm/y9XlXJc
+1meQGpMWTHhPYyi+VK4Z+/E5oj3fNv9ZFKDrEnAq5lzNEhkW+O0tYVTkfj/D2bNy
+CR50qzAogqsn73XJJ++y2mGa18hs0BNhwAOV8jy4clR4HCRP65c=
+=lJlP
+-----END PGP SIGNATURE-----
diff --git a/website/static/security/patches/SA-25:09/netinet-15.patch b/website/static/security/patches/SA-25:09/netinet-15.patch
new file mode 100644
index 0000000000..7083189c9a
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-15.patch
@@ -0,0 +1,201 @@
+--- sys/netinet/in_pcb.c.orig
++++ sys/netinet/in_pcb.c
+@@ -2665,10 +2665,13 @@
+ 	    INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
+ 
+ 	/*
+-	 * Add entry to load balance group.
+-	 * Only do this if SO_REUSEPORT_LB is set.
++	 * Ignore SO_REUSEPORT_LB if the socket is connected.  Really this case
++	 * should be an error, but for UDP sockets it is not, and some
++	 * applications erroneously set it on connected UDP sockets, so we can't
++	 * change this without breaking compatibility.
+ 	 */
+-	if ((inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) {
++	if (!connected &&
++	    (inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) {
+ 		int error = in_pcbinslbgrouphash(inp, M_NODOM);
+ 		if (error != 0)
+ 			return (error);
+@@ -2770,6 +2773,10 @@
+ 		connected = !in_nullhost(inp->inp_faddr);
+ 	}
+ 
++	/* See the comment in in_pcbinshash(). */
++	if (connected && (inp->inp_flags & INP_INLBGROUP) != 0)
++		in_pcbremlbgrouphash(inp);
++
+ 	/*
+ 	 * When rehashing, the caller must ensure that either the new or the old
+ 	 * foreign address was unspecified.
+--- tests/sys/netinet/so_reuseport_lb_test.c.orig
++++ tests/sys/netinet/so_reuseport_lb_test.c
+@@ -29,6 +29,8 @@
+ 
+ #include <sys/param.h>
+ #include <sys/event.h>
++#include <sys/filio.h>
++#include <sys/ioccom.h>
+ #include <sys/socket.h>
+ 
+ #include <netinet/in.h>
+@@ -551,6 +553,150 @@
+ 	close(s);
+ }
+ 
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set.  Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp);
++ATF_TC_BODY(connect_udp, tc)
++{
++	struct sockaddr_in sin = {
++		.sin_family = AF_INET,
++		.sin_len = sizeof(sin),
++		.sin_addr = { htonl(INADDR_LOOPBACK) },
++	};
++	ssize_t n;
++	int error, len, s1, s2, s3;
++	char ch;
++
++	s1 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s1 >= 0);
++	s2 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s2 >= 0);
++	s3 = socket(PF_INET, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s3 >= 0);
++
++	error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++	    sizeof(int));
++	ATF_REQUIRE_MSG(error == 0,
++	    "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++	error = bind(s1, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s2, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s3, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	/* Connect to an address not owned by s2. */
++	error = getsockname(s3, (struct sockaddr *)&sin,
++	    (socklen_t[]){sizeof(sin)});
++	ATF_REQUIRE(error == 0);
++	error = connect(s1, (struct sockaddr *)&sin, sizeof(sin));
++	ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++	/* Try to send a packet to s1 from s2. */
++	error = getsockname(s1, (struct sockaddr *)&sin,
++	    (socklen_t[]){sizeof(sin)});
++	ATF_REQUIRE(error == 0);
++
++	ch = 42;
++	n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++	    sizeof(sin));
++	ATF_REQUIRE(n == 1);
++
++	/* Give the packet some time to arrive. */
++	usleep(100000);
++
++	/* s1 is connected to s3 and shouldn't receive from s2. */
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++	/* ... but s3 can of course send to s1. */
++	n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++	    sizeof(sin));
++	ATF_REQUIRE(n == 1);
++	usleep(100000);
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 1, "unexpected data available");
++}
++
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set.  Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp6);
++ATF_TC_BODY(connect_udp6, tc)
++{
++	struct sockaddr_in6 sin6 = {
++		.sin6_family = AF_INET6,
++		.sin6_len = sizeof(sin6),
++		.sin6_addr = IN6ADDR_LOOPBACK_INIT,
++	};
++	ssize_t n;
++	int error, len, s1, s2, s3;
++	char ch;
++
++	s1 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s1 >= 0);
++	s2 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s2 >= 0);
++	s3 = socket(PF_INET6, SOCK_DGRAM, 0);
++	ATF_REQUIRE(s3 >= 0);
++
++	error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++	    sizeof(int));
++	ATF_REQUIRE_MSG(error == 0,
++	    "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++	error = bind(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s2, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	error = bind(s3, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++	/* Connect to an address not owned by s2. */
++	error = getsockname(s3, (struct sockaddr *)&sin6,
++	    (socklen_t[]){sizeof(sin6)});
++	ATF_REQUIRE(error == 0);
++	error = connect(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++	ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++	/* Try to send a packet to s1 from s2. */
++	error = getsockname(s1, (struct sockaddr *)&sin6,
++	    (socklen_t[]){sizeof(sin6)});
++	ATF_REQUIRE(error == 0);
++
++	ch = 42;
++	n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++	    sizeof(sin6));
++	ATF_REQUIRE(n == 1);
++
++	/* Give the packet some time to arrive. */
++	usleep(100000);
++
++	/* s1 is connected to s3 and shouldn't receive from s2. */
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++	/* ... but s3 can of course send to s1. */
++	n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++	    sizeof(sin6));
++	ATF_REQUIRE(n == 1);
++	usleep(100000);
++	error = ioctl(s1, FIONREAD, &len);
++	ATF_REQUIRE(error == 0);
++	ATF_REQUIRE_MSG(len == 1, "unexpected data available");
++}
++
+ ATF_TP_ADD_TCS(tp)
+ {
+ 	ATF_TP_ADD_TC(tp, basic_ipv4);
+@@ -561,6 +707,8 @@
+ 	ATF_TP_ADD_TC(tp, bind_without_listen);
+ 	ATF_TP_ADD_TC(tp, connect_not_bound);
+ 	ATF_TP_ADD_TC(tp, connect_bound);
++	ATF_TP_ADD_TC(tp, connect_udp);
++	ATF_TP_ADD_TC(tp, connect_udp6);
+ 
+ 	return (atf_no_error());
+ }
diff --git a/website/static/security/patches/SA-25:09/netinet-15.patch.asc b/website/static/security/patches/SA-25:09/netinet-15.patch.asc
new file mode 100644
index 0000000000..380b5aba55
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-15.patch.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrkACgkQbljekB8A
+Gu9urA//d3+X7bwSN9niBanoXBIRFsxr7+im0rHelA5UtPJ9OQ160IAbDdu2H9Cn
+76HavCQ+bpytDZxVTWplm9lK9NskFq71ChMosgH7rqDPVcgqyNPqDuGWNbH28dBq
+sBydMzY7ZkiDurLlUaesQCKopBES8I4s9DXmO9lWLXm0VI2CkiCYkf3HPZeyxJp5
+7NLXNZQWz+/Osnd1HYb/HlxEiX/DjDgnvbtD11ho2kzlO9wDy4jKwOwAgM49+UP9
+HapQh+1nrRPiX/dqZ5bAVLnztTjSVXq58V/kejpHHlbht8OxAqkGfSoeHB6emSyl
+gH5fPSnBd9/IwpBUR79f0+BuHkkibhoVOrSqNl95C3VyuUNPhy/fhrChEQbET1vs
+NfbsGO7pNaaTjg5zjEGXJK8x8q+S9R9Q31M1Lts5FMiKdjGIHNzWPu+ZLPMSXBdy
+3iJ0OAaLLo5GJ6mefJWCTyUGbegaaxjBrJD+No12sjgXkcogvMm0VvmA7wNxnBXW
+Fevs5++9hR8NU4eIhCx3mZQaQDwFOgoV6zKcPtir52jZd+txKnkw3fC01RKE86FW
+opINfUTA/W4sZCG2DaSuU7USo2vMKG3m//HBvbO5eSBq+qnavFOWTvQUc16hfMxa
+7+pd8VXtdEiZkwqR2Dj58Gt9D3xqoh4fbHQ+AbqvoN3lPJmnNsA=
+=/zfq
+-----END PGP SIGNATURE-----