Hi, The checksum of a ICMP "need to frag" packet for TCP was wrong when created from a ICMP6 "too big" packet. The function pf_change_icmp_af() has code to adjust the pseudo-header checksum in the ICMP6 case, but pf_test_state_icmp() changed the proto before the case was entered.
So call pf_change_icmp_af() before the pd->proto is converted in the TCP and UDP payload case like it was already done for ICMP and ICMP6 payload case. This was found by the sys/net/pf_forward regress test. My printf debugging revealed that dlen is positive and d may be negative. So declare the variables with the correct sign in pf_change_icmp_af(). ok? bluhm Index: net/pf.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/pf.c,v retrieving revision 1.989 diff -u -p -r1.989 pf.c --- net/pf.c 9 Oct 2016 18:01:57 -0000 1.989 +++ net/pf.c 17 Oct 2016 20:00:41 -0000 @@ -2340,7 +2340,8 @@ pf_change_icmp_af(struct mbuf *m, int ip struct mbuf *n = NULL; struct ip *ip4; struct ip6_hdr *ip6; - u_int hlen, ohlen, d; + u_int hlen, ohlen, dlen; + int d; if (af == naf || (af != AF_INET && af != AF_INET6) || (naf != AF_INET && naf != AF_INET6)) @@ -2417,7 +2418,7 @@ pf_change_icmp_af(struct mbuf *m, int ip if (pd->proto == IPPROTO_ICMPV6) { /* fixup pseudo-header */ - int dlen = pd->tot_len - pd->off; + dlen = pd->tot_len - pd->off; pf_cksum_fixup(pd->pcksum, htons(dlen), htons(dlen + d), pd->proto); } @@ -5104,6 +5105,10 @@ pf_test_state_icmp(struct pf_pdesc *pd, m_copyback(pd->m, pd->off, sizeof(struct icmp6_hdr), pd->hdr.icmp6, M_NOWAIT); + if (pf_change_icmp_af(pd->m, ipoff2, + 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 @@ -5117,11 +5122,6 @@ pf_test_state_icmp(struct pf_pdesc *pd, &nk->addr[pd2.didx], nk->af); pd->naf = nk->af; - if (pf_change_icmp_af(pd->m, ipoff2, - pd, &pd2, &nk->addr[sidx], - &nk->addr[didx], pd->af, nk->af)) - return (PF_DROP); - pf_patch_16(pd, &th.th_sport, nk->port[sidx]); pf_patch_16(pd, @@ -5220,6 +5220,10 @@ pf_test_state_icmp(struct pf_pdesc *pd, m_copyback(pd->m, pd->off, sizeof(struct icmp6_hdr), pd->hdr.icmp6, M_NOWAIT); + if (pf_change_icmp_af(pd->m, ipoff2, + 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 @@ -5232,11 +5236,6 @@ pf_test_state_icmp(struct pf_pdesc *pd, PF_ACPY(&pd->ndaddr, &nk->addr[pd2.didx], nk->af); pd->naf = nk->af; - - if (pf_change_icmp_af(pd->m, ipoff2, - pd, &pd2, &nk->addr[sidx], - &nk->addr[didx], pd->af, nk->af)) - return (PF_DROP); pf_patch_16(pd, &uh.uh_sport, nk->port[sidx]);