The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=e9a490b10d6cf2006bc2442648f8b2de39aacc5f
commit e9a490b10d6cf2006bc2442648f8b2de39aacc5f Author: Kristof Provost <k...@freebsd.org> AuthorDate: 2025-03-04 16:13:38 +0000 Commit: Kristof Provost <k...@freebsd.org> CommitDate: 2025-03-05 09:37:56 +0000 pf: fix ICMP source address translation for nat64 While handling an ICMP error related to another state (e.g. TTL expired, port closed, fragmentation needed, ...) we can't just use the state's source address as the ICMP source address. We have to translate the IPv4 address back to an IPv6 nat64 address. Failing to do so breaks things like traceroute, where the intermediate router generates an ICMP error message, and the traceroute tool uses the source address to build the path. Use the state's source address to figure out the prefix, and copy the IPv4 IP address to the last bytes to construct the mapped IPv6 address. PR: 284944 Sponsored by: Rubicon Communications, LLC ("Netgate") In collaboration with: sashan <sas...@openbsd.org>, dac07517c7 Differential Revision: https://reviews.freebsd.org/D49143 --- sys/netpfil/pf/pf.c | 72 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index e0b664772544..2b28474f36c6 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -8025,10 +8025,6 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, &nk->addr[didx], pd->af, nk->af)) return (PF_DROP); - if (nk->af == AF_INET) - pd->proto = IPPROTO_ICMP; - else - pd->proto = IPPROTO_ICMPV6; pf_change_ap(pd->m, pd2.src, &th.th_sport, pd->ip_sum, &th.th_sum, &nk->addr[pd2.sidx], nk->port[sidx], 1, pd->af, nk->af); @@ -8036,10 +8032,24 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, pd->ip_sum, &th.th_sum, &nk->addr[pd2.didx], nk->port[didx], 1, pd->af, nk->af); m_copyback(pd2.m, pd2.off, 8, (c_caddr_t)&th); - PF_ACPY(pd->src, - &nk->addr[pd2.sidx], nk->af); - PF_ACPY(pd->dst, + PF_ACPY(&pd->nsaddr, &nk->addr[pd2.sidx], + nk->af); + PF_ACPY(&pd->ndaddr, &nk->addr[pd2.didx], nk->af); + if (nk->af == AF_INET) { + pd->proto = IPPROTO_ICMP; + } else { + pd->proto = IPPROTO_ICMPV6; + /* + * IPv4 becomes IPv6 so we must + * copy 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); } @@ -8148,10 +8158,6 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, &nk->addr[didx], pd->af, nk->af)) return (PF_DROP); - if (nk->af == AF_INET) - pd->proto = IPPROTO_ICMP; - else - pd->proto = IPPROTO_ICMPV6; pf_change_ap(pd->m, pd2.src, &uh.uh_sport, pd->ip_sum, &uh.uh_sum, &nk->addr[pd2.sidx], nk->port[sidx], 1, pd->af, nk->af); @@ -8164,6 +8170,20 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, &nk->addr[pd2.sidx], nk->af); PF_ACPY(&pd->ndaddr, &nk->addr[pd2.didx], nk->af); + if (nk->af == AF_INET) { + pd->proto = IPPROTO_ICMP; + } else { + pd->proto = IPPROTO_ICMPV6; + /* + * IPv4 becomes IPv6 so we must + * copy 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); } @@ -8288,17 +8308,27 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, &nk->addr[didx], pd->af, nk->af)) return (PF_DROP); - if (nk->af == AF_INET) - pd->proto = IPPROTO_ICMP; - else - pd->proto = IPPROTO_ICMPV6; sh.src_port = nk->port[sidx]; sh.dest_port = nk->port[didx]; m_copyback(pd2.m, pd2.off, sizeof(sh), (c_caddr_t)&sh); - PF_ACPY(pd->src, + PF_ACPY(&pd->nsaddr, &nk->addr[pd2.sidx], nk->af); - PF_ACPY(pd->dst, + PF_ACPY(&pd->ndaddr, &nk->addr[pd2.didx], nk->af); + if (nk->af == AF_INET) { + pd->proto = IPPROTO_ICMP; + } else { + pd->proto = IPPROTO_ICMPV6; + /* + * IPv4 becomes IPv6 so we must + * copy 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); } @@ -8427,6 +8457,14 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, &nk->addr[pd2.sidx], nk->af); PF_ACPY(&pd->ndaddr, &nk->addr[pd2.didx], nk->af); + /* + * IPv4 becomes IPv6 so we must copy + * IPv4 src addr to least 32bits in + * IPv6 address to keep traceroute + * working. + */ + pd->nsaddr.addr32[3] = + pd->src->addr32[0]; pd->naf = nk->af; return (PF_AFRT); }