From nobody Wed Oct 28 20:35:58 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 4jFJzL60KXz6x6Jr for ; Wed, 28 Oct 2026 20:35:58 +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) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 4jFJzL35K1z4RM8 for ; Wed, 28 Oct 2026 20:35:58 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793219758; 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=YhJN5AByXvE/yg3DPPRC5F0z9BHT5byPzYe3kj2QPAk=; b=oJmvsSiD+OWcfHEQ94fLIkkljSxemcy8QBQ2eTWWx3Uq/RBedOVYkfjVb3xBmSr1mpmWb3 20L5v6l9zbXakkk2xvpRVZMWqnFuof8ZUrVC/DdqrbIMUiNC2Q8Uje1jhujt54+rvLVc1K gBafHj4IbjlH0bWeh/ntHbK157bVOAt/V9jYRiX10jeUAoOrU4ERsDnrObIa7FwV6XUEtt Z2ITkNWKvPf1X/SDNyflknLMoOiuTF+G4ApfUWJvrQfqGO1uL8QMfbLsCxX3fixxEvDamZ BeYA7HjNjLlAfxYa4QLxI5Zb/37YVr63lIB3K6T0qKXHnjLN+XQqKL/nNfTjwQ== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1793219758; b=r/Dh1HecK6kr+cwlWC7paPWiKR6GL/FpBqMn3Mlw2K+9f4Zs1/9GcprezsSsqa3lF5Pahk MZ0PAreFOlL0urxhGXdFYynmppL0baWe6qiV3fPu+SokTvr0uJ2NsO8uUSMoYnayJwtuIR ZDbhTgP0KdmpPYbXbld2JP9fpUpfQxpgNpj2/pHwZIk7x8iaw5g0AACBH4gtnsVVh2vTi4 ymclEgu6VIr/eU/DtU128eg1vo0GyfNqISOsHp0i3lq1fydH18qisXu18cB6aokcNET14r KOFr5ttKuQRif+hY2tt23KLzvqjMjPO2g/saC8r1iJ6d26450NTveey+jGODKQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793219758; 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=YhJN5AByXvE/yg3DPPRC5F0z9BHT5byPzYe3kj2QPAk=; b=VOvAloVKx09qJEB9OgiwU8PYubGADOYxpWplglUrwU+dudDaZxWl6ytkar+7TDfIjABedW u541tyTKU4TRu9K3zCxV/7MUpRKQkpaOHIi7Bz2qf0ZI+GXw8MAYTEP/1mN1wEDnC0mDmA FhBN+1PFM03kV4DPu75cx2ZfxpZi7EdVavgoyzW66lf/V2Onay3iC8dAPzg7wjy61j9qre cVWXCST4oFbaPiJgDfT69UMNNqYyIBLPLknUG7e9kAD/Gv0e8rLA1MC8KOTWyy0YnQJX55 C9LjXzp8YTMRiPbPe9n8na9Bx5guznfxC2XN61I7H3/3rjYK8kUru41mnDNJDg== 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 4jFJzL2NdSz1Mv6 for ; Wed, 28 Oct 2026 20:35:58 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1e203 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 28 Oct 2026 20:35:58 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: R. Christian McDonald Subject: git: de62c977c0ce - stable/15 - pf: Do not corrupt neighbour advertisements when translating List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: Commits to the stable branches of the FreeBSD 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/stable/15 X-Git-Reftype: branch X-Git-Commit: de62c977c0ce543414ab4f528c59e828c27b79a0 Auto-Submitted: auto-generated Date: Wed, 28 Oct 2026 20:35:58 +0000 Message-Id: <6ae25cae.1e203.57cfcc30@gitrepo.freebsd.org> The branch stable/15 has been updated by rcm: URL: https://cgit.FreeBSD.org/src/commit/?id=de62c977c0ce543414ab4f528c59e828c27b79a0 commit de62c977c0ce543414ab4f528c59e828c27b79a0 Author: R. Christian McDonald AuthorDate: 2026-10-13 15:58:55 +0000 Commit: R. Christian McDonald CommitDate: 2026-10-28 20:34:15 +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 (cherry picked from commit a6e1dde287eb3854873224200cea40996b0b5cbf) --- 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 dc17969cd9d4..f380be478d63 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -8075,9 +8075,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" }