From nobody Tue Oct 13 16:07:25 2026 X-Original-To: dev-commits-src-all@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 4j3zkV4vM5z6vbmk for ; Tue, 13 Oct 2026 16:07:30 +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 "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4j3zkV36cLz4T6J for ; Tue, 13 Oct 2026 16:07:30 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791907650; 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=DWLpi3I5W/4F+LqTZRmDEIFdQWk9TcnjJfZPtDgUba8=; b=sHEgBdooScSa3tTToBmcE8TbnxwHBjKLYRrDxEPLTuTybwpr8ekuHukZttOzisqQOT+tfl 15bSWmtPpYTZQxc8uOOFB4AkVav6DgLsy2onT/L5zvGFdE5DUSVSmGFxdK0fQvH0cVkdYc 13Y2kaFbwXPpYIC+zpliyuc+jyH9ZJa5t9ptK21bBIPKqLBQH3EGQC08XQsb6pUWrwwyxN 44amHG9V+Rr1g+st0AIdJSh0RYE9VoFemev1KLOYnYIfTlGL9nlynjnkGBuMAShdn9Fs9a oO75oFEYitgkR7gKBn8F8TR/2MlfIwnpUUNChGq7Nj+KdFY5vudQ6fNSif2OWA== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791907650; b=PS16veo3KK4JtIRKTGiBLa1e4rOnuDrVsLBglgoL3AHCsOkQxfScCoeC/DU5qDsIblhZ6/ xsQDN+I7OJWxRG3VqW5AGKc2GLLBye8WLKaa77anBa0lujGa7C8eWBN1Ef/VGYv9BcxrTQ buEVwxvM13K/myF9svaqTkuLAPD5qOGEo+LLV5m/9SkyWqJZpPqi9SukjzLXyOoLZFtnV/ p4dCAQUo4bSP6hEvPuyFuP6X+RPPK98oCEtNXC2nbKYqjpkuQ9uVtnhJXx8Mth6/uP/VRl Rk7u4zJh4sqNIRhoV2z4J7FpnKDHrBsU051q+4SIOtqOlVDaKtYvVmvWRDDa8w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791907650; 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=DWLpi3I5W/4F+LqTZRmDEIFdQWk9TcnjJfZPtDgUba8=; b=VtqsmzDh9YUAhlOl0WvWMAdnxaDwZPZ/kA3YwqpPwSoZRM7Hd93QOuZO0cwHyDn2nuKvw3 1U0D69C3+0RSzmbq44zZY3JTRE1yCmZo3TAYdcxbFZCorM/H7ft2vPKtqdrmh5ppOD9Wfc 8Rw1JW2+VLDbx0+bNXSOqWt2XH7IdDZInmIOhIZYH1mZktBNaaRrOZbtUNCVpRfyu8UAdf 0AeaLTON07V5bZYftNL4Mr1s6fdAqTPltn10sRE7gplqCsI/Bd5vMO3wNr5lSYyDcGirVW U4IB9Wa2U6JZSkLIM1d5EivWZ6WO74vE1sr3528yMRf/1hNIO5B1ZmHeyieiqw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4j3zkV1w0HzLns for ; Tue, 13 Oct 2026 16:07:30 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 22566 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 13 Oct 2026 16:07:25 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: R. Christian McDonald Subject: git: a6e1dde287eb - main - pf: Do not corrupt neighbour advertisements when translating List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: Commit messages for all branches of the src repository 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: rcm X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a6e1dde287eb3854873224200cea40996b0b5cbf Auto-Submitted: auto-generated Date: Tue, 13 Oct 2026 16:07:25 +0000 Message-Id: <6ace573d.22566.7d404c4@gitrepo.freebsd.org> The branch main has been updated by rcm: URL: https://cgit.FreeBSD.org/src/commit/?id=a6e1dde287eb3854873224200cea40996b0b5cbf commit a6e1dde287eb3854873224200cea40996b0b5cbf Author: R. Christian McDonald AuthorDate: 2026-10-13 15:58:55 +0000 Commit: R. Christian McDonald CommitDate: 2026-10-13 16:02:39 +0000 pf: Do not corrupt neighbour advertisements when translating When an ICMPv6 query or reply matches a translated state, pf_test_state_icmp() copies the state's virtual id into icmp6_id. Only echo messages have an identifier there. For all other types the virtual id is 0 and those two bytes hold something else; in a neighbour advertisement they hold the R, S and O flags. The checksum was not updated either, so icmp6_input() dropped the advertisement as a bad checksum. Unicast NUD probes to a link-local neighbour, such as the default router, are sent from the link-local address. With a nat rule that matches it (e.g. "from any"), every advertisement answering a probe was dropped: NUD failed, the neighbour cache entry was deleted, and traffic to the neighbour stalled until it was resolved again. Only rewrite the id of echo messages, as the ICMPv6 error path already does, and update the checksum, as the IPv4 path does. Add a regression test. Reviewed by: kp Fixes: f1ddd7f1dae6 ("pf: add forgotten fixup for icmp6 id's when translating") See also: https://redmine.pfsense.org/issues/16236 MFC after: 1 week Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D60542 --- sys/netpfil/pf/pf.c | 14 ++++++++- tests/sys/netpfil/pf/icmp6.sh | 73 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 817790ba0ef0..00990e17d0c8 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -8856,9 +8856,21 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, &pd->hdr.icmp6.icmp6_cksum, &nk->addr[didx], 0); - if (nk->port[iidx] != pd->hdr.icmp6.icmp6_id) + /* + * Only echo has an identifier. Other + * types use a virtual id of 0 and keep + * other fields here (e.g. the NA flags). + */ + if (virtual_type == htons(ICMP6_ECHO_REQUEST) && + nk->port[iidx] != pd->hdr.icmp6.icmp6_id) { + pd->hdr.icmp6.icmp6_cksum = + pf_cksum_fixup( + pd->hdr.icmp6.icmp6_cksum, + pd->hdr.icmp6.icmp6_id, + nk->port[iidx], 0); pd->hdr.icmp6.icmp6_id = nk->port[iidx]; + } m_copyback(pd->m, pd->off, sizeof(struct icmp6_hdr), (caddr_t )&pd->hdr.icmp6); diff --git a/tests/sys/netpfil/pf/icmp6.sh b/tests/sys/netpfil/pf/icmp6.sh index c55af906e3a6..acce09dd45c5 100644 --- a/tests/sys/netpfil/pf/icmp6.sh +++ b/tests/sys/netpfil/pf/icmp6.sh @@ -196,9 +196,82 @@ repeat_cleanup() pft_cleanup } +atf_test_case "nat_nud" "cleanup" +nat_nud_head() +{ + atf_set descr 'Test that NUD works when pf translates the probes' + atf_set require.user root +} + +nat_nud_body() +{ + pft_init + + epair=$(vnet_mkepair) + + vnet_mkjail alcatraz ${epair}a + jexec alcatraz ifconfig ${epair}a inet6 no_dad + jexec alcatraz ifconfig ${epair}a inet6 2001:db8::1/64 up + # Probe a stale neighbour one second after using it, not five. + jexec alcatraz sysctl net.inet6.icmp6.nd6_delay=1 + + vnet_mkjail singsing ${epair}b + jexec singsing ifconfig ${epair}b inet6 no_dad + jexec singsing ifconfig ${epair}b inet6 2001:db8::2/64 up + + lla=$(jexec alcatraz ifconfig ${epair}a inet6 | \ + awk '/fe80:/ { sub(/%.*/, "", $2); print $2 }') + llb=$(jexec singsing ifconfig ${epair}b inet6 | \ + awk '/fe80:/ { sub(/%.*/, "", $2); print $2 }') + + # NUD probes are sent from the link-local address, so this translates + # them, and the advertisements answering them come back to 2001:db8::1. + jexec alcatraz pfctl -e + pft_set_rules alcatraz \ + "nat on ${epair}a inet6 from fe80::/10 to any -> 2001:db8::1" \ + "pass" + + # singsing resolving alcatraz leaves it stale in alcatraz's neighbour + # cache, and alcatraz answering uses it, so alcatraz will probe it. + atf_check -s exit:0 -o ignore \ + jexec singsing ping -6 -c 1 ${lla}%${epair}b + + # The advertisement answering the probe makes the neighbour reachable. + # A corrupted one is dropped: alcatraz probes twice more, then deletes + # the entry. + state= + for i in $(seq 1 15); do + state=$(jexec alcatraz ndp -n ${llb}%${epair}a 2>/dev/null | \ + awk 'NR == 2 { print $5 }') + if [ "${state}" = "R" ]; then + break + fi + sleep 1 + done + + jexec alcatraz ndp -an + jexec alcatraz pfctl -ss -vv + + if [ "${state}" != "R" ]; then + atf_fail "NUD failed, neighbour state '${state:-no entry}'" + fi + atf_check -o match:"^[[:space:]]*0 bad checksums$" \ + jexec alcatraz netstat -s -p icmp6 + + # The probe was translated + atf_check -o match:"2001:db8::1 \(${lla}\) -> ${llb}\[135\]" \ + jexec alcatraz pfctl -ss +} + +nat_nud_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "zero_id" atf_add_test_case "ttl_exceeded" atf_add_test_case "repeat" + atf_add_test_case "nat_nud" }