The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=09d62e0658d11cda80678bb9fc0ed5a48c0e838e
commit 09d62e0658d11cda80678bb9fc0ed5a48c0e838e Author: Kristof Provost <k...@freebsd.org> AuthorDate: 2025-05-26 09:21:27 +0000 Commit: Kristof Provost <k...@freebsd.org> CommitDate: 2025-06-06 11:15:59 +0000 pf: align IPv4 and IPv6 AH header handling Pf was handling IPv4 and IPv6 differently regarding AH extension headers. pf_walk_header6() steps over it and detects the real protocol. So to implement a minimal header walking function pf_walk_header() for IPv4. It does the header checks and jumps over AH. Then pf does not understand AH as a protocol, it is just an extension that authenticates the packet. Move some header and option checks to pf_walk_header() for consistency with IPv6. This also improves the header check for IPv4 packets in ICMP payload. OK henning@ Obtained from: OpenBSD, bluhm <bl...@openbsd.org>, 22ef11432c Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D50657 --- sys/netpfil/pf/pf.c | 65 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 4451cca76842..69a68d0249b2 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -372,6 +372,7 @@ static u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, int, u_int16_t); static int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); +static int pf_walk_header(struct pf_pdesc *, struct ip *, u_short *); static int pf_walk_option6(struct pf_pdesc *, struct ip6_hdr *, int, int, u_short *); static int pf_walk_header6(struct pf_pdesc *, struct ip6_hdr *, @@ -7911,9 +7912,10 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd, } /* offset of protocol header that follows h2 */ - pd2.off = ipoff2 + (h2.ip_hl << 2); + pd2.off = ipoff2; + if (pf_walk_header(&pd2, &h2, reason) != PF_PASS) + return (PF_DROP); - pd2.proto = h2.ip_p; pd2.tot_len = ntohs(h2.ip_len); pd2.src = (struct pf_addr *)&h2.ip_src; pd2.dst = (struct pf_addr *)&h2.ip_dst; @@ -9689,6 +9691,44 @@ pf_dummynet_route(struct pf_pdesc *pd, struct pf_kstate *s, return (0); } +static int +pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason) +{ + struct ip6_ext ext; + u_int32_t hlen, end; + + hlen = h->ip_hl << 2; + if (hlen < sizeof(struct ip) || hlen > ntohs(h->ip_len)) { + REASON_SET(reason, PFRES_SHORT); + return (PF_DROP); + } + end = pd->off + ntohs(h->ip_len); + pd->off += hlen; + pd->proto = h->ip_p; + /* stop walking over non initial fragments */ + if ((h->ip_off & htons(IP_OFFMASK)) != 0) + return (PF_PASS); + for (;;) { + switch (pd->proto) { + case IPPROTO_AH: + /* fragments may be short */ + if ((h->ip_off & htons(IP_MF | IP_OFFMASK)) != 0 && + end < pd->off + sizeof(ext)) + return (PF_PASS); + if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext), + NULL, reason, AF_INET)) { + DPFPRINTF(PF_DEBUG_MISC, ("IP short exthdr")); + return (PF_DROP); + } + pd->off += (ext.ip6e_len + 2) * 4; + pd->proto = ext.ip6e_nxt; + break; + default: + return (PF_PASS); + } + } +} + #ifdef INET6 static int pf_walk_option6(struct pf_pdesc *pd, struct ip6_hdr *h, int off, int end, @@ -9936,18 +9976,22 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, *m0 = pd->m; h = mtod(pd->m, struct ip *); - pd->off = h->ip_hl << 2; - if (pd->off < (int)sizeof(*h)) { + if (pd->m->m_pkthdr.len < ntohs(h->ip_len)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); return (-1); } + + if (pf_walk_header(pd, h, reason) != PF_PASS) { + *action = PF_DROP; + return (-1); + } + pd->src = (struct pf_addr *)&h->ip_src; pd->dst = (struct pf_addr *)&h->ip_dst; PF_ACPY(&pd->osrc, pd->src, af); PF_ACPY(&pd->odst, pd->dst, af); pd->ip_sum = &h->ip_sum; - pd->virtual_proto = pd->proto = h->ip_p; pd->tos = h->ip_tos & ~IPTOS_ECN_MASK; pd->ttl = h->ip_ttl; pd->tot_len = ntohs(h->ip_len); @@ -9957,8 +10001,8 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, if (h->ip_hl > 5) /* has options */ pd->badopts++; - if (h->ip_off & htons(IP_MF | IP_OFFMASK)) - pd->virtual_proto = PF_VPROTO_FRAGMENT; + pd->virtual_proto = (h->ip_off & htons(IP_MF | IP_OFFMASK)) ? + PF_VPROTO_FRAGMENT : pd->proto; break; } @@ -9978,7 +10022,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, } h = mtod(pd->m, struct ip6_hdr *); - pd->off = 0; + if (pf_walk_header6(pd, h, reason) != PF_PASS) { *action = PF_DROP; return (-1); @@ -9993,11 +10037,10 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, pd->tos = IPV6_DSCP(h); pd->ttl = h->ip6_hlim; pd->tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); - pd->virtual_proto = pd->proto = h->ip6_nxt; pd->act.rtableid = -1; - if (pd->fragoff != 0) - pd->virtual_proto = PF_VPROTO_FRAGMENT; + pd->virtual_proto = (pd->fragoff != 0) ? + PF_VPROTO_FRAGMENT : pd->proto; /* * we do not support jumbogram. if we keep going, zero ip6_plen