From nobody Mon Dec 29 10:13:19 2025 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 4dfsVl5pNKz6M4sM for ; Mon, 29 Dec 2025 10:13:19 +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 "R13" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4dfsVl55lXz3H6t for ; Mon, 29 Dec 2025 10:13:19 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1767003199; 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=54AgvnRIIRARcTgwfYPAaXoWQ1MjB/V4wfQ9jqZz/ek=; b=DahPXxewmM+ktduUuZ6B5b6sAc3D4rjCGhP9G++c6LIzOWrIAbuMcojm2N7JNxMZEqWueb njkiNQ9TC1v+wZ8tsWGCHUdUGTlbyHI4mW/SHw8hSiXhafU9UsFD7bl2pRAkYq+uQLZsUb 5/3f9xx8L7GNltJ0DP11fYCMjLT6UlCkC8o7Ht7xMtS3KHQUCBquDQKOXP7HGunR3F+7Qa XA28tjfmiyE4a98GGf2+ZiQXRUYh7QiCeomQCXGm5YBA+kJI6sLHbuaz7GO8yZC+ZhRKsc 8ckNod/c4gBk2mnB0MTVRdR/LQxJL8VG07eH3gNZTXknSRJpbUqrZeRdEvCGIw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1767003199; 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=54AgvnRIIRARcTgwfYPAaXoWQ1MjB/V4wfQ9jqZz/ek=; b=x9IRC4fOZ9TTQfupWq/gg2a1doLCdGU3MGvVWpcYfaqXU6wm/M4u2+9H8uhPlF1ma49lt/ hvABvi6vygHd0KAP956Gh5k2xyKUzhcTXZDCEEEJE7pQSNqNVwlIgXOAVel7onFDMc7IZ5 gd0vT7i2lcbKO89fpPYBgz0Tj6NfEC4M427zQ0rC4yYmVJ6JHnAPC5taykZHrKftFY2Wyx vNwId9/rDUiG4SYICR8KfDVnMwpnGTKcIdagZYc0556nywtU3NROlGfEDymNrR83o+Opd7 qzb4IKolfFSpJQzs/f8G+CPZE46UJgvQzoI2tTRFIM/YUypv7lllItOyKL1xow== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1767003199; a=rsa-sha256; cv=none; b=y3ja7aGBpLHuySIpdYiV+x4wuAKmvd2Yt0kLAcF2dDo/x78+UxOSRxZfZku7i1oPNoH8DI KNkci7jkQKxuiQn7d7fhWeigJyP3C8Ga8tObK6Gkzbn80dUwUvxEbo6RCyccV6SllTQSZX B0nv9yXbmwJmWvs8UeDR1N63Ur0pXGalZxpBybprDH7BKXfLb5SoTUvwkr/dtoUo37UQ9W 0VBBKXP6TLh7jEiGPCnm7O65w6StdyO3xYeopXH1gcCBEfRNl295fMLbnYg5P72uQ1X8wn tG3Erd6Pjz8YZpWM8K7eEgYUdZ0mcDYgn9yAGc7X5h4xUnpx0DJir7to4cso6w== 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 4dfsVl4XSXz1PD8 for ; Mon, 29 Dec 2025 10:13:19 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id ddb1 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 29 Dec 2025 10:13:19 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kristof Provost Subject: git: 261642478c8e - stable/15 - pf: handle TTL expired during nat64 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 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 261642478c8e796679103612c41064f0ac78d13d Auto-Submitted: auto-generated Date: Mon, 29 Dec 2025 10:13:19 +0000 Message-Id: <6952543f.ddb1.10b20b3d@gitrepo.freebsd.org> The branch stable/15 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=261642478c8e796679103612c41064f0ac78d13d commit 261642478c8e796679103612c41064f0ac78d13d Author: Kristof Provost AuthorDate: 2025-12-10 16:27:51 +0000 Commit: Kristof Provost CommitDate: 2025-12-29 06:54:56 +0000 pf: handle TTL expired during nat64 If the TTL (or hop limit) expires during nat64 translation we may need to send the error message in the original address family (i.e. pre-translation). We'd usually handle this in pf_route()/pf_route6(), but at that point we have already translated the packet, making it difficult to include it in the generated ICMP message. Check for this case in pf_translate_af() and send icmp errors directly from it. PR: 291527 MFC after: 2 weeks Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D54166 (cherry picked from commit ac4fb06d096d6308b9522f454b68fbfc45bb8531) --- sys/net/pfvar.h | 1 - sys/netpfil/pf/pf.c | 25 ++++++++++++++++++++----- tests/sys/netpfil/pf/nat64.py | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 7e030910e0ec..55b841f970ea 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -2511,7 +2511,6 @@ int pf_socket_lookup(struct pf_pdesc *); struct pf_state_key *pf_alloc_state_key(int); int pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t, u_int16_t, int); -int pf_translate_af(struct pf_pdesc *); bool pf_init_threshold(struct pf_kthreshold *, uint32_t, uint32_t); uint16_t pf_tagname2tag(const char *); #ifdef ALTQ diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index fb67c37b1556..1fdfd6e0f38b 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -3605,8 +3605,8 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, } } -int -pf_translate_af(struct pf_pdesc *pd) +static int +pf_translate_af(struct pf_pdesc *pd, struct pf_krule *r) { #if defined(INET) && defined(INET6) struct mbuf *mp; @@ -3617,6 +3617,21 @@ pf_translate_af(struct pf_pdesc *pd) struct pf_fragment_tag *ftag; int hlen; + if (pd->ttl == 1) { + /* We'd generate an ICMP error. Do so now rather than after af translation. */ + if (pd->af == AF_INET) { + pf_send_icmp(pd->m, ICMP_TIMXCEED, + ICMP_TIMXCEED_INTRANS, 0, pd->af, r, + pd->act.rtableid); + } else { + pf_send_icmp(pd->m, ICMP6_TIME_EXCEEDED, + ICMP6_TIME_EXCEED_TRANSIT, 0, pd->af, r, + pd->act.rtableid); + } + + return (-1); + } + hlen = pd->naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6); /* trim the old header */ @@ -9158,7 +9173,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp, if (pd->dir == PF_IN) { if (ip->ip_ttl <= IPTTLDEC) { - if (r->rt != PF_DUPTO) + if (r->rt != PF_DUPTO && pd->naf == pd->af) pf_send_icmp(m0, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, pd->af, r, pd->act.rtableid); @@ -9483,7 +9498,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp, if (pd->dir == PF_IN) { if (ip6->ip6_hlim <= IPV6_HLIMDEC) { - if (r->rt != PF_DUPTO) + if (r->rt != PF_DUPTO && pd->naf == pd->af) pf_send_icmp(m0, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0, pd->af, r, pd->act.rtableid); @@ -11299,7 +11314,7 @@ done: *m0 = NULL; break; case PF_AFRT: - if (pf_translate_af(&pd)) { + if (pf_translate_af(&pd, r)) { *m0 = pd.m; action = PF_DROP; break; diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py index 705de72f5bc4..ba0d2ae01a9e 100644 --- a/tests/sys/netpfil/pf/nat64.py +++ b/tests/sys/netpfil/pf/nat64.py @@ -329,6 +329,42 @@ class TestNAT64(VnetTestTemplate): for r in packets: r.show() + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_ttl_one(self): + """ + PR 291527: invalid ICMP error generated by nat64 router + """ + ifname = self.vnet.iface_alias_map["if1"].name + gw_mac = self.vnet.iface_alias_map["if1"].epairb.ether + ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1") + + import scapy.all as sp + + pkt = sp.Ether(dst=gw_mac) \ + / sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=1) \ + / sp.SCTP(sport=1111, dport=2222) \ + / sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, \ + a_rwnd=1500, params=[]) + s = DelayedSend(pkt, sendif=ifname) + + found = False + packets = sp.sniff(iface=ifname, timeout=5) + for r in packets: + print("Reply packet:") + r.show() + + ip6 = r.getlayer(sp.IPv6) + icmp6 = r.getlayer(sp.ICMPv6TimeExceeded) + if not ip6 or not icmp6: + continue + assert ip6.src == "2001:db8::1" + assert ip6.dst == "2001:db8::2" + assert icmp6.type == 3 # Time exceeded + assert icmp6.code == 0 # hop limit exceeded in transit + found = True + assert found + @pytest.mark.require_user("root") @pytest.mark.require_progs(["scapy"]) def test_ttl_zero(self):