The branch main has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=ad591caf2a70d6f3a5f1ba7aff182591afc0cc04

commit ad591caf2a70d6f3a5f1ba7aff182591afc0cc04
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2025-06-20 09:41:19 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2025-06-26 13:11:01 +0000

    pf: decrement TTL in pf_route(6)()
    
    When pf(4) forwards incoming packets with route-to or reply-to,
    decrement the time-to-live or hop-limit field to prevent routing
    loops.  Sending an ICMP time exceeded error makes traceroute work.
    For outgoing packets ip_forward() has already done this.
    OK visa@ sashan@
    
    Add a test case for the above, and fix the nat64 tests to account for the 
nat64
    router now decrementing the TTL.
    
    Obtained from:  OpenBSD, bluhm <bl...@openbsd.org>, 18421856bb
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 sys/netpfil/pf/pf.c              | 24 +++++++++++++++++++-
 tests/sys/netpfil/pf/nat64.py    | 10 ++++-----
 tests/sys/netpfil/pf/route_to.sh | 47 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index cdf48fc4d60a..a40e1744cbc8 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -8949,7 +8949,18 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
        dst->sin_len = sizeof(struct sockaddr_in);
        dst->sin_addr.s_addr = pd->act.rt_addr.v4.s_addr;
 
-       if (s != NULL){
+       if (pd->dir == PF_IN) {
+               if (ip->ip_ttl <= IPTTLDEC) {
+                       if (r->rt != PF_DUPTO)
+                               pf_send_icmp(m0, ICMP_TIMXCEED,
+                                   ICMP_TIMXCEED_INTRANS, 0, pd->af, r,
+                                   pd->act.rtableid);
+                       goto bad_locked;
+               }
+               ip->ip_ttl -= IPTTLDEC;
+       }
+
+       if (s != NULL) {
                if (ifp == NULL && (pd->af != pd->naf)) {
                        /* We're in the AFTO case. Do a route lookup. */
                        const struct nhop_object *nh;
@@ -9231,6 +9242,17 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
        dst.sin6_len = sizeof(dst);
        PF_ACPY((struct pf_addr *)&dst.sin6_addr, &pd->act.rt_addr, AF_INET6);
 
+       if (pd->dir == PF_IN) {
+               if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
+                       if (r->rt != PF_DUPTO)
+                               pf_send_icmp(m0, ICMP6_TIME_EXCEEDED,
+                                   ICMP6_TIME_EXCEED_TRANSIT, 0, pd->af, r,
+                                   pd->act.rtableid);
+                       goto bad_locked;
+               }
+               ip6->ip6_hlim -= IPV6_HLIMDEC;
+       }
+
        if (s != NULL) {
                if (ifp == NULL && (pd->af != pd->naf)) {
                        const struct nhop_object *nh;
diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py
index 32fd8f4245a1..adae2489ce5e 100644
--- a/tests/sys/netpfil/pf/nat64.py
+++ b/tests/sys/netpfil/pf/nat64.py
@@ -178,7 +178,7 @@ class TestNAT64(VnetTestTemplate):
 
         # Check the hop limit
         ip6 = reply.getlayer(sp.IPv6)
-        assert ip6.hlim == 62
+        assert ip6.hlim == 61
 
     @pytest.mark.require_user("root")
     @pytest.mark.require_progs(["scapy"])
@@ -236,7 +236,7 @@ class TestNAT64(VnetTestTemplate):
         ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
         import scapy.all as sp
 
-        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=1) \
+        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
             / sp.TCP(sport=1111, dport=2222, flags="S")
         self.common_test_source_addr(packet)
 
@@ -246,7 +246,7 @@ class TestNAT64(VnetTestTemplate):
         ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
         import scapy.all as sp
 
-        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=1) \
+        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
             / sp.UDP(sport=1111, dport=2222) / sp.Raw("foo")
         self.common_test_source_addr(packet)
 
@@ -256,7 +256,7 @@ class TestNAT64(VnetTestTemplate):
         ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
         import scapy.all as sp
 
-        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=1) \
+        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
             / sp.SCTP(sport=1111, dport=2222) \
             / sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, 
a_rwnd=1500)
         self.common_test_source_addr(packet)
@@ -267,7 +267,7 @@ class TestNAT64(VnetTestTemplate):
         ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
         import scapy.all as sp
 
-        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=1) \
+        packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
             / sp.ICMPv6EchoRequest() / sp.Raw("foo")
         reply = self.common_test_source_addr(packet)
         icmp = reply.getlayer(sp.ICMPv6EchoRequest)
diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh
index 0354d1f59306..5c0d355b8ea1 100644
--- a/tests/sys/netpfil/pf/route_to.sh
+++ b/tests/sys/netpfil/pf/route_to.sh
@@ -813,6 +813,52 @@ sticky_cleanup()
        pft_cleanup
 }
 
+atf_test_case "ttl" "cleanup"
+ttl_head()
+{
+       atf_set descr 'Ensure we decrement TTL on route-to'
+       atf_set require.user root
+}
+
+ttl_body()
+{
+       pft_init
+
+       epair_one=$(vnet_mkepair)
+       epair_two=$(vnet_mkepair)
+       ifconfig ${epair_one}b 192.0.2.2/24 up
+       route add default 192.0.2.1
+
+       vnet_mkjail alcatraz ${epair_one}a ${epair_two}a
+       jexec alcatraz ifconfig ${epair_one}a 192.0.2.1/24 up
+       jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up
+       jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+       vnet_mkjail singsing ${epair_two}b
+       jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up
+       jexec singsing route add default 198.51.100.1
+
+       # Sanity check
+       atf_check -s exit:0 -o ignore \
+           ping -c 3 198.51.100.2
+
+       jexec alcatraz pfctl -e
+       pft_set_rules alcatraz \
+               "pass out" \
+               "pass in route-to (${epair_two}a 198.51.100.2)"
+
+       atf_check -s exit:0 -o ignore \
+           ping -c 3 198.51.100.2
+
+       atf_check -s exit:2 -o ignore \
+           ping -m 1 -c 3 198.51.100.2
+}
+
+ttl_cleanup()
+{
+       pft_cleanup
+}
+
 atf_init_test_cases()
 {
        atf_add_test_case "v4"
@@ -830,4 +876,5 @@ atf_init_test_cases()
        atf_add_test_case "dummynet_frag"
        atf_add_test_case "dummynet_double"
        atf_add_test_case "sticky"
+       atf_add_test_case "ttl"
 }

Reply via email to