Index | Thread | Search

From:
Alexandr Nedvedicky <sashan@fastmail.net>
Subject:
Re: pf af-to breaks traceroute
To:
Kristof Provost <kp@freebsd.org>
Cc:
tech@openbsd.org
Date:
Thu, 27 Feb 2025 02:22:16 +0100

Download raw body.

Thread
Hello Kristof,

I see the problem now. thanks for clarification. after slightly changing
my setup I can confirm OpenBSD current has the same bug. Fix below seems to
work for on OpenBSD. If I understand the issue right we need to combine 6to4
prefix defined in af-to rule with IPv4 source address seen in IP packet which
carries ICMPv4 error. I think your fix is very similar (modulo differences
between pf on FreeBSD and OpenBSD).

without the change I see output as follows when doing traceroute to 8.8.8.8

    eco# traceroute6 -n 64:ff9b::8.8.8.8                                          
    traceroute6 to 64:ff9b::8.8.8.8 (64:ff9b::808:808), 64 hops max, 60 byte packets                                                                              
     1  fdd7:e83e:66bc:212:5054:ff:fe12:3450  2.243 ms  0.794 ms  0.711 ms         
     2  64:ff9b::808:808  2.629 ms  2.168 ms  2.133 ms                             
     3  64:ff9b::808:808  2.029 ms  1.893 ms  2.01 ms                              
     4  64:ff9b::808:808  2.622 ms  2.295 ms  2.305 ms                             
     5  64:ff9b::808:808  3.025 ms  2.747 ms  2.893 ms                             
     6  64:ff9b::808:808  15.597 ms  15.116 ms  17.987 ms                          
     7  64:ff9b::808:808  18.331 ms  18.883 ms  18.402 ms                          
     8  64:ff9b::808:808  19.619 ms  15.487 ms  18.181 ms                          
     9  64:ff9b::808:808  20.157 ms  19.978 ms  17.597 ms                          
    10  64:ff9b::808:808  22.123 ms  17.914 ms  21.457 ms                          
    11  64:ff9b::808:808  19.044 ms  22.731 ms  25.352 ms                          
    12  64:ff9b::808:808  26.151 ms  28.338 ms  23.295 ms                          
    13  64:ff9b::808:808  22.159 ms  17.402 ms  19.978 ms                          
    14  64:ff9b::808:808  23.1 ms  26.365 ms  21.514 ms 

after applying the diff below the traceroute6 output changes to

    eco# traceroute6 -n 64:ff9b::8.8.8.8 
    traceroute6 to 64:ff9b::8.8.8.8 (64:ff9b::808:808), 64 hops max, 60 byte packets
     1  fdd7:e83e:66bc:212:5054:ff:fe12:3450  2.543 ms  0.891 ms  0.645 ms
     2  64:ff9b::abc:d232  2.86 ms  1.869 ms  1.998 ms
     3  64:ff9b::abc:d20a  2.076 ms  2.006 ms  2.182 ms
     4  64:ff9b::ac10:101  2.501 ms  2.305 ms  2.331 ms
     5  64:ff9b::c0a8:201  3.032 ms  2.893 ms  2.763 ms
     6  64:ff9b::a29:3341  10.781 ms  10.214 ms  10.649 ms
     7  64:ff9b::a26:3f1  9.739 ms  12.519 ms  10.337 ms
     8  64:ff9b::a26:1df4  14.046 ms  10.423 ms  9.994 ms
     9  64:ff9b::a26:fefc  10.478 ms  10.067 ms  11.771 ms
    10  64:ff9b::b95b:a81b  14.762 ms  11.652 ms  12.617 ms
    11  64:ff9b::b900:14aa  12.222 ms  13.247 ms  14.116 ms
    12  64:ff9b::c0b2:6223  18.622 ms  11.917 ms  15.751 ms
    13  64:ff9b::d8ef:2f0b  15.12 ms 64:ff9b::d8ef:38ef  12.11 ms 64:ff9b::d8ef:2f0b  11.728 ms
    14  64:ff9b::808:808  12.841 ms  13.738 ms  12.305 ms

thank you for sharing all details here. very appreciated.

kind regards
sashan

--------8<---------------8<---------------8<------------------8<--------
diff --git a/sys/net/pf.c b/sys/net/pf.c
index 6d6dbfd3c14..ce2cb7c2a9c 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -5650,6 +5650,16 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **stp,
 			if (afto) {
 				pf_addrcpy(&pd->nsaddr, &nk->addr[sidx],
 				    nk->af);
+				if (nk->af == AF_INET6) {
+					/*
+					 * IPv4 becomes IPv6 so we must put
+					 * IPv4 src addr to least 32bits in
+					 * IPv6 address to keep traceroute/icmp
+					 * working.
+					 */
+					pd->nsaddr.addr32[3] =
+					    pd->src->addr32[0];
+				}
 				pf_addrcpy(&pd->ndaddr, &nk->addr[didx],
 				    nk->af);
 				pd->naf = nk->af;
@@ -5925,17 +5935,27 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **stp,
 					    pd, &pd2, &nk->addr[sidx],
 					    &nk->addr[didx], pd->af, nk->af))
 						return (PF_DROP);
-					if (nk->af == AF_INET)
-						pd->proto = IPPROTO_ICMP;
-					else
-						pd->proto = IPPROTO_ICMPV6;
 					pd->m->m_pkthdr.ph_rtableid =
 					    nk->rdomain;
 					pd->destchg = 1;
-					pf_addrcpy(&pd->nsaddr,
-					    &nk->addr[pd2.sidx], nk->af);
 					pf_addrcpy(&pd->ndaddr,
 					    &nk->addr[pd2.didx], nk->af);
+					pf_addrcpy(&pd->nsaddr,
+					    &nk->addr[pd2.sidx], nk->af);
+					if (nk->af == AF_INET) {
+						pd->proto = IPPROTO_ICMP;
+					} else {
+						pd->proto = IPPROTO_ICMPV6;
+						/*
+						 * IPv4 becomes IPv6 so we must
+						 * put IPv4 src addr to least
+						 * 32bits in IPv6 address to
+						 * keep traceroute/icmp
+						 * working.
+						 */
+						pd->nsaddr.addr32[3] =
+						    pd->src->addr32[0];
+					}
 					pd->naf = nk->af;
 
 					pf_patch_16(pd,
@@ -6045,17 +6065,27 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **stp,
 					    pd, &pd2, &nk->addr[sidx],
 					    &nk->addr[didx], pd->af, nk->af))
 						return (PF_DROP);
-					if (nk->af == AF_INET)
-						pd->proto = IPPROTO_ICMP;
-					else
-						pd->proto = IPPROTO_ICMPV6;
 					pd->m->m_pkthdr.ph_rtableid =
 					    nk->rdomain;
 					pd->destchg = 1;
-					pf_addrcpy(&pd->nsaddr,
-					    &nk->addr[pd2.sidx], nk->af);
 					pf_addrcpy(&pd->ndaddr,
 					    &nk->addr[pd2.didx], nk->af);
+					pf_addrcpy(&pd->nsaddr,
+					    &nk->addr[pd2.sidx], nk->af);
+					if (nk->af == AF_INET) {
+						pd->proto = IPPROTO_ICMP;
+					} else {
+						pd->proto = IPPROTO_ICMPV6;
+						/*
+						 * IPv4 becomes IPv6 so we must
+						 * put IPv4 src addr to least
+						 * 32bits in IPv6 address to
+						 * keep traceroute/icmp
+						 * working.
+						 */
+						pd->nsaddr.addr32[3] =
+						    pd->src->addr32[0];
+					}
 					pd->naf = nk->af;
 
 					pf_patch_16(pd,
@@ -6182,10 +6212,24 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **stp,
 					pd->m->m_pkthdr.ph_rtableid =
 					    nk->rdomain;
 					pd->destchg = 1;
-					pf_addrcpy(&pd->nsaddr,
-					    &nk->addr[pd2.sidx], nk->af);
 					pf_addrcpy(&pd->ndaddr,
 					    &nk->addr[pd2.didx], nk->af);
+					pf_addrcpy(&pd->nsaddr,
+					    &nk->addr[pd2.sidx], nk->af);
+					if (nk->af == AF_INET) {
+						pd->proto = IPPROTO_ICMP;
+					} else {
+						pd->proto = IPPROTO_ICMPV6;
+						/*
+						 * IPv4 becomes IPv6 so we must
+						 * put IPv4 src addr to least
+						 * 32bits in IPv6 address to
+						 * keep traceroute/icmp
+						 * working.
+						 */
+						pd->nsaddr.addr32[3] =
+						    pd->src->addr32[0];
+					}
 					pd->naf = nk->af;
 					return (PF_AFRT);
 				}