From: Alexandr Nedvedicky Subject: Re: pf af-to breaks traceroute To: Kristof Provost Cc: tech@openbsd.org Date: Thu, 27 Feb 2025 02:22:16 +0100 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); }