Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 6 Jun 2025 11:17:09 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 09d62e0658d1 - main - pf: align IPv4 and IPv6 AH header handling
Message-ID:  <202506061117.556BH9h1083574@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=09d62e0658d11cda80678bb9fc0ed5a48c0e838e

commit 09d62e0658d11cda80678bb9fc0ed5a48c0e838e
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-05-26 09:21:27 +0000
Commit:     Kristof Provost <kp@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 <bluhm@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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202506061117.556BH9h1083574>