git: a08dc822f494 - main - udp6: make udp6_ctlinput() same as IPv4 version
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 02 Nov 2026 21:42:44 UTC
The branch main has been updated by glebius:
URL: https://cgit.FreeBSD.org/src/commit/?id=a08dc822f4943f2cea509cd0ba1f11a89ec9e82d
commit a08dc822f4943f2cea509cd0ba1f11a89ec9e82d
Author: Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-11-02 21:42:15 +0000
Commit: Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-11-02 21:42:15 +0000
udp6: make udp6_ctlinput() same as IPv4 version
Processing an incoming ICMPv6 we need to find just the matching inpcb and
call udp_notify() on it.
The call to in6_pcbnotify() at the first glance seems like a notifying
sweep across the entire database, but it is not. It finds just the
matching inpcb, but does that in the most inefficient way! The added test
case proves that ICMPv6 processing stays the same before and after the
change. This weird use of in6_pcbnotify() can be tracked down all the way
to the initial KAME code.
Reviewed by: tuexen, gallatin
Differential Revision: https://reviews.freebsd.org/D60967
---
sys/netinet6/udp6_usrreq.c | 34 +++++++++-------
tests/sys/netinet6/Makefile | 1 +
tests/sys/netinet6/udp6.c | 97 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 117 insertions(+), 15 deletions(-)
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 10ba889066ec..5fa574cca753 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -578,23 +578,27 @@ udp6_common_ctlinput(struct ip6ctlparam *ip6cp, struct inpcbinfo *pcbinfo)
bzero(&uh, sizeof(uh));
m_copydata(m, off, sizeof(*uhp), &uh);
- /* Check to see if its tunneled */
- inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_dst, uh.uh_dport,
- &ip6->ip6_src, uh.uh_sport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB,
- m->m_pkthdr.rcvif, m);
+ inp = in6_pcblookup(pcbinfo, &ip6->ip6_dst, uh.uh_dport, &ip6->ip6_src,
+ uh.uh_sport, INPLOOKUP_WLOCKPCB, NULL);
if (inp != NULL) {
- struct udpcb *up;
- udp_tun_icmp_t *func;
-
- up = intoudpcb(inp);
- func = up->u_icmp_func;
- INP_RUNLOCK(inp);
- if (func != NULL)
- func(ip6cp);
+ INP_WLOCK_ASSERT(inp);
+ udp_notify(inp, errno);
+ INP_WUNLOCK(inp);
+ } else {
+ inp = in6_pcblookup(pcbinfo, &ip6->ip6_dst, uh.uh_dport,
+ &ip6->ip6_src, uh.uh_sport,
+ INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, NULL);
+ if (inp != NULL) {
+ struct udpcb *up;
+ udp_tun_icmp_t *func;
+
+ up = intoudpcb(inp);
+ func = up->u_icmp_func;
+ INP_RUNLOCK(inp);
+ if (func != NULL)
+ func(ip6cp);
+ }
}
- in6_pcbnotify(pcbinfo, ip6cp->ip6c_finaldst, uh.uh_dport,
- ip6cp->ip6c_src, uh.uh_sport, errno, ip6cp->ip6c_cmdarg,
- udp_notify);
}
static void
diff --git a/tests/sys/netinet6/Makefile b/tests/sys/netinet6/Makefile
index f5a04a299dc6..88db13600f55 100644
--- a/tests/sys/netinet6/Makefile
+++ b/tests/sys/netinet6/Makefile
@@ -4,6 +4,7 @@ TESTSDIR= ${TESTSBASE}/sys/netinet6
BINDIR= ${TESTSDIR}
FILESDIR= ${TESTSDIR}
+ATF_TESTS_C= udp6
ATF_TESTS_PYTEST= test_ip6_output.py
ATF_TESTS_SH= exthdr \
mld \
diff --git a/tests/sys/netinet6/udp6.c b/tests/sys/netinet6/udp6.c
new file mode 100644
index 000000000000..ba4b0503cdb7
--- /dev/null
+++ b/tests/sys/netinet6/udp6.c
@@ -0,0 +1,97 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <atf-c.h>
+
+static int
+connectedsock(void)
+{
+ struct sockaddr_in6 sa = {
+ .sin6_family = AF_INET6,
+ .sin6_len = sizeof(sa),
+ .sin6_port = htons(1638),
+ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
+ };
+ int s;
+
+ ATF_REQUIRE((s = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0);
+ ATF_REQUIRE(connect(s, (struct sockaddr *)&sa, sizeof(sa)) == 0);
+ return (s);
+}
+
+static void
+REQUIRE_SO_ERROR(int s, int expected)
+{
+ socklen_t len = sizeof(int);
+ int error;
+
+ ATF_REQUIRE(getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) == 0);
+ ATF_REQUIRE_MSG(error == expected,
+ "SO_ERROR: got %d (%s), expected %d (%s)",
+ error, strerror(error), expected, strerror(expected));
+}
+
+/*
+ * Two connected sockets to the same remote address and port. Kernel will
+ * assign different local ports to them. Trigger ICMPv6 Port Unreachable for
+ * each (kernel generates it). Assert that only one of them is notified.
+ * XXX: the remote is actually ::1 and the test relies on port being
+ * unreachable.
+ */
+ATF_TC_WITHOUT_HEAD(icmp_port_unreach_notify);
+ATF_TC_BODY(icmp_port_unreach_notify, tc)
+{
+ const char buf[] = "probe";
+ int s1, s2;
+
+ s1 = connectedsock();
+ s2 = connectedsock();
+ ATF_REQUIRE(send(s1, buf, sizeof(buf), 0) == sizeof(buf));
+ usleep(100000);
+ REQUIRE_SO_ERROR(s1, ECONNREFUSED);
+ REQUIRE_SO_ERROR(s2, 0);
+ ATF_REQUIRE(send(s2, buf, sizeof(buf), 0) == sizeof(buf));
+ usleep(100000);
+ REQUIRE_SO_ERROR(s2, ECONNREFUSED);
+ REQUIRE_SO_ERROR(s1, 0);
+
+ close(s1);
+ close(s2);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, icmp_port_unreach_notify);
+
+ return (atf_no_error());
+}