From nobody Tue Dec 17 10:08:20 2024 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 4YCCF06bvZz5h0pM; Tue, 17 Dec 2024 10:08:20 +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 "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4YCCF0437xz4CQB; Tue, 17 Dec 2024 10:08:20 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1734430100; 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=ED+1QYNbxnZYSK26U/+Flpq3wWBXzfDNhGvjFnhNzlM=; b=dPwGqh+lYXuWOmJbtI+yDldjVvU0XTqTuZlsuZIlCNcjPgLktlITKBs/RHywcdjzHmYDu1 3i4KgBKiiToORIC61iDzoBL6NWne0ETCFxpDj4K/CKhcHsC4iT2lV6ho6NYMghIRcY8zqN nL3ndreoOoQLebwR0igllLiBYntjqKsp67qDkUFZFU82LgpTxKKuvEHI1j4vNq67SXvGC1 CjS/JXPnjPF9HTSiEuFFDm89FRf/JjKBVt3Gl7b+ODS7h0NaULCdPCGC3d78FhU8cDtNhl qaDJmI8sgyVKEDBf7qZWrjE+vfD5bCEdi00NedsmWDsZJny69DFU7JM/Eibusw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1734430100; 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=ED+1QYNbxnZYSK26U/+Flpq3wWBXzfDNhGvjFnhNzlM=; b=GGxq91Ph4Twp3DMdcoGr4ECPtmdJjXwKlRR6p4wx607NM8OXfGO3J3G6oj1zlr20x0LWBI mzZUCo5+urcWuDHIx8IM2J3tzPMDJ1lsZSHmYP8eksVVDPGwoE3qv9O6Lgh8AGmEdl6MIf eX8DskNhX+agYqjmTgu+cCSq7N/m3BEjzMjA34POVE2eRLQ1jSzvi+ABnG4ajtpTYXy7we QuPiKUw7fhzp+tpi1vOKlV7iedpYCVVRJm8PfYH7osQyolzz51mr1wjBxxEbm5/aN6KnsS nojej5xfagqkesXuuiPSSpaL3n8RQfR886APQJoPboGRDTz4elDOszPGbm54Cw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1734430100; a=rsa-sha256; cv=none; b=t4eqZvBFRQ6yWmeoKMTb5YvQXOb5tiKgcanypMLtnikxOtgHD8q0hOZKbGyFfCXUYET3ln v9rtFbwSYTmj0q+sbdY//uSgUGEVkhEMCRYfJ1Ajwq4A0oWAPeEHh1DOtp/JDrGWurQw2X nekbzb+j/FFzv3Sx5Dbx7VDu+K7lNKf7Liq81XXKOhOvJ939zAGCRjWIVzSmmzPFHURbag UdtnfOsqGbZFWkB3A8j3mrhFJTufeuJJgWcllvYrM/WShxIHsYefBi2TRos6osP6T62/bq U+iel4F/LlFPfq15AyPNu9F1kqal+pvx01c14bgaq6GAEn4DHtLJB3FoNOJinQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (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 did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4YCCF03gNMzJVR; Tue, 17 Dec 2024 10:08:20 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 4BHA8KDu025237; Tue, 17 Dec 2024 10:08:20 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 4BHA8KB2025234; Tue, 17 Dec 2024 10:08:20 GMT (envelope-from git) Date: Tue, 17 Dec 2024 10:08:20 GMT Message-Id: <202412171008.4BHA8KB2025234@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: 5d1219378dd5 - main - pf: teach nat64 to handle 0 UDP checksums List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@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/main X-Git-Reftype: branch X-Git-Commit: 5d1219378dd5d9b031926cf7806455f33677792b Auto-Submitted: auto-generated The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=5d1219378dd5d9b031926cf7806455f33677792b commit 5d1219378dd5d9b031926cf7806455f33677792b Author: Kristof Provost AuthorDate: 2024-12-16 10:23:59 +0000 Commit: Kristof Provost CommitDate: 2024-12-17 10:07:19 +0000 pf: teach nat64 to handle 0 UDP checksums For IPv4 it's valid for a UDP checksum to be 0 (i.e. no checksum). This isn't the case for IPv6, so if we translate a UDP packet from IPv4 to IPv6 we need to ensure that the checksum is calculated. Add a test case to verify this. Rework the server jail so it can listen for TCP and UDP packets at the same time. Sponsored by: Rubicon Communications, LLC ("Netgate") --- sys/netpfil/pf/pf.c | 12 +++++++++ tests/sys/netpfil/pf/nat64.py | 61 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 9128562fd71c..f2e19693b863 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -3469,6 +3469,7 @@ pf_translate_af(struct pf_pdesc *pd) ip4->ip_dst = pd->ndaddr.v4; pd->src = (struct pf_addr *)&ip4->ip_src; pd->dst = (struct pf_addr *)&ip4->ip_dst; + pd->off = sizeof(struct ip); break; case AF_INET6: ip6 = mtod(pd->m, struct ip6_hdr *); @@ -3485,6 +3486,7 @@ pf_translate_af(struct pf_pdesc *pd) ip6->ip6_dst = pd->ndaddr.v6; pd->src = (struct pf_addr *)&ip6->ip6_src; pd->dst = (struct pf_addr *)&ip6->ip6_dst; + pd->off = sizeof(struct ip6_hdr); /* * If we're dealing with a reassembled packet we need to adjust @@ -9094,6 +9096,16 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp, PF_STATE_UNLOCK(s); } + if (pd->af != pd->naf) { + struct udphdr *uh = &pd->hdr.udp; + + if (pd->proto == IPPROTO_UDP && uh->uh_sum == 0) { + uh->uh_sum = in6_cksum_pseudo(ip6, + ntohs(uh->uh_ulen), IPPROTO_UDP, 0); + m_copyback(m0, pd->off, sizeof(*uh), pd->hdr.any); + } + } + if (ifp == NULL) { m0 = *m; *m = NULL; diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py index eeddd5118168..64ec5ae15262 100644 --- a/tests/sys/netpfil/pf/nat64.py +++ b/tests/sys/netpfil/pf/nat64.py @@ -25,6 +25,9 @@ # SUCH DAMAGE. import pytest +import selectors +import socket +import sys from atf_python.sys.net.tools import ToolsHelper from atf_python.sys.net.vnet import VnetTestTemplate @@ -41,7 +44,44 @@ class TestNAT64(VnetTestTemplate): def vnet3_handler(self, vnet): ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1") ToolsHelper.print_output("/sbin/sysctl net.inet.ip.ttl=62") - ToolsHelper.print_output("echo foo | nc -l 1234 &") + ToolsHelper.print_output("/sbin/sysctl net.inet.udp.checksum=0") + + sel = selectors.DefaultSelector() + t = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + t.bind(("0.0.0.0", 1234)) + t.setblocking(False) + t.listen() + sel.register(t, selectors.EVENT_READ, data=None) + + u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + u.bind(("0.0.0.0", 4444)) + u.setblocking(False) + sel.register(u, selectors.EVENT_READ, data="UDP") + + while True: + events = sel.select(timeout=20) + for key, mask in events: + sock = key.fileobj + if key.data is None: + conn, addr = sock.accept() + print(f"Accepted connection from {addr}") + data = types.SimpleNamespace(addr=addr, inb=b"", outb=b"") + events = selectors.EVENT_READ | selectors.EVENT_WRITE + sel.register(conn, events, data=data) + elif key.data == "UDP": + recv_data, addr = sock.recvfrom(1024) + print(f"Received UDP {recv_data} from {addr}") + sock.sendto(b"foo", addr) + else: + if mask & selectors.EVENT_READ: + recv_data = sock.recv(1024) + print(f"Received TCP {recv_data}") + sock.send(b"foo") + else: + print("Unknown event?") + t.close() + u.close() + return def vnet2_handler(self, vnet): ifname = vnet.iface_alias_map["if1"].name @@ -130,3 +170,22 @@ class TestNAT64(VnetTestTemplate): # Check the hop limit ip6 = reply.getlayer(sp.IPv6) assert ip6.hlim == 62 + + @pytest.mark.require_user("root") + def test_udp_checksum(self): + ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1") + + import scapy.all as sp + + # Send an outbound UDP packet to establish state + packet = sp.IPv6(dst="64:ff9b::192.0.2.2") \ + / sp.UDP(sport=3333, dport=4444) / sp.Raw("foo") + + # Get a reply + # We'll send the reply without UDP checksum on the IPv4 side + # but that's not valid for IPv6, so expect pf to update the checksum. + reply = sp.sr1(packet, timeout=5) + + udp = reply.getlayer(sp.UDP) + assert udp + assert udp.chksum != 0