pf: pass struct pf_pdesc to pf_walk_option6() and pf_walk_header6()

This makes their argument list shorter. Also fix a bug where pf_walk_option6()
used the outer header in the pd2 case.
ok henning@ mikeb@

Obtained from:	OpenBSD, bluhm <bluhm@openbsd.org>, dfff4707a1
Sponsored by:	Rubicon Communications, LLC ("Netgate")
This commit is contained in:
Kristof Provost 2025-01-09 12:02:39 +01:00
parent 0300b49ac8
commit 1941d370bf
2 changed files with 72 additions and 77 deletions

View file

@ -1626,6 +1626,9 @@ struct pf_pdesc {
u_int32_t off; /* protocol header offset */
u_int32_t hdrlen; /* protocol header length */
u_int32_t p_len; /* total length of protocol payload */
u_int32_t extoff; /* extentsion header offset */
u_int32_t fragoff; /* fragment header offset */
u_int32_t jumbolen; /* length from v6 jumbo header */
u_int32_t badopts; /* v4 options or v6 routing headers */
u_int16_t *ip_sum;
@ -1634,7 +1637,6 @@ struct pf_pdesc {
#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */
u_int16_t virtual_proto;
#define PF_VPROTO_FRAGMENT 256
int extoff;
sa_family_t af;
sa_family_t naf;
u_int8_t proto;
@ -2391,8 +2393,6 @@ int pf_normalize_ip(struct mbuf **, u_short *, struct pf_pdesc *);
#endif /* INET */
#ifdef INET6
int pf_walk_header6(struct mbuf *, struct ip6_hdr *, int *, int *, int *,
uint8_t *, uint32_t *, u_short *);
int pf_normalize_ip6(struct mbuf **, int, u_short *, struct pf_pdesc *);
void pf_poolmask(struct pf_addr *, struct pf_addr*,
struct pf_addr *, struct pf_addr *, sa_family_t);

View file

@ -367,7 +367,9 @@ 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_option6(struct mbuf *, int, int, uint32_t *,
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 *,
u_short *);
static void pf_print_state_parts(struct pf_kstate *,
struct pf_state_key *, struct pf_state_key *);
@ -7850,8 +7852,6 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
#endif /* INET */
#ifdef INET6
struct ip6_hdr h2_6;
int fragoff2, extoff2;
u_int32_t jumbolen;
#endif /* INET6 */
int ipoff2 = 0;
@ -7904,9 +7904,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
return (PF_DROP);
}
pd2.off = ipoff2;
if (pf_walk_header6(pd->m, &h2_6, &pd2.off, &extoff2,
&fragoff2, &pd2.proto, &jumbolen,
reason) != PF_PASS)
if (pf_walk_header6(&pd2, &h2_6, reason) != PF_PASS)
return (PF_DROP);
pd2.src = (struct pf_addr *)&h2_6.ip6_src;
@ -9655,16 +9653,15 @@ pf_dummynet_route(struct pf_pdesc *pd, struct pf_kstate *s,
#ifdef INET6
static int
pf_walk_option6(struct mbuf *m, int off, int end, uint32_t *jumbolen,
pf_walk_option6(struct pf_pdesc *pd, struct ip6_hdr *h, int off, int end,
u_short *reason)
{
struct ip6_opt opt;
struct ip6_opt_jumbo jumbo;
struct ip6_hdr *h = mtod(m, struct ip6_hdr *);
while (off < end) {
if (!pf_pull_hdr(m, off, &opt.ip6o_type, sizeof(opt.ip6o_type),
NULL, reason, AF_INET6)) {
if (!pf_pull_hdr(pd->m, off, &opt.ip6o_type,
sizeof(opt.ip6o_type), NULL, reason, AF_INET6)) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short opt type"));
return (PF_DROP);
}
@ -9672,8 +9669,8 @@ pf_walk_option6(struct mbuf *m, int off, int end, uint32_t *jumbolen,
off++;
continue;
}
if (!pf_pull_hdr(m, off, &opt, sizeof(opt), NULL, reason,
AF_INET6)) {
if (!pf_pull_hdr(pd->m, off, &opt, sizeof(opt), NULL,
reason, AF_INET6)) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short opt"));
return (PF_DROP);
}
@ -9684,7 +9681,7 @@ pf_walk_option6(struct mbuf *m, int off, int end, uint32_t *jumbolen,
}
switch (opt.ip6o_type) {
case IP6OPT_JUMBO:
if (*jumbolen != 0) {
if (pd->jumbolen != 0) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 multiple jumbo"));
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
@ -9694,15 +9691,15 @@ pf_walk_option6(struct mbuf *m, int off, int end, uint32_t *jumbolen,
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (!pf_pull_hdr(m, off, &jumbo, sizeof(jumbo), NULL,
if (!pf_pull_hdr(pd->m, off, &jumbo, sizeof(jumbo), NULL,
reason, AF_INET6)) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short jumbo"));
return (PF_DROP);
}
memcpy(jumbolen, jumbo.ip6oj_jumbo_len,
sizeof(*jumbolen));
*jumbolen = ntohl(*jumbolen);
if (*jumbolen < IPV6_MAXPACKET) {
memcpy(&pd->jumbolen, jumbo.ip6oj_jumbo_len,
sizeof(pd->jumbolen));
pd->jumbolen = ntohl(pd->jumbolen);
if (pd->jumbolen < IPV6_MAXPACKET) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short jumbolen"));
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
@ -9718,43 +9715,41 @@ pf_walk_option6(struct mbuf *m, int off, int end, uint32_t *jumbolen,
}
int
pf_walk_header6(struct mbuf *m, struct ip6_hdr *h, int *off, int *extoff,
int *fragoff, uint8_t *nxt, uint32_t *jumbolen, u_short *reason)
pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
{
struct ip6_frag frag;
struct ip6_ext ext;
struct ip6_rthdr rthdr;
int rthdr_cnt = 0;
*off += sizeof(struct ip6_hdr);
*extoff = *fragoff = 0;
*nxt = h->ip6_nxt;
*jumbolen = 0;
pd->off += sizeof(struct ip6_hdr);
pd->fragoff = pd->extoff = pd->jumbolen = 0;
pd->proto = h->ip6_nxt;
for (;;) {
switch (*nxt) {
switch (pd->proto) {
case IPPROTO_FRAGMENT:
if (*fragoff != 0) {
if (pd->fragoff != 0) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 multiple fragment"));
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
/* jumbo payload packets cannot be fragmented */
if (*jumbolen != 0) {
if (pd->jumbolen != 0) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 fragmented jumbo"));
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
if (!pf_pull_hdr(m, *off, &frag, sizeof(frag), NULL,
reason, AF_INET6)) {
if (!pf_pull_hdr(pd->m, pd->off, &frag, sizeof(frag),
NULL, reason, AF_INET6)) {
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short fragment"));
return (PF_DROP);
}
*fragoff = *off;
pd->fragoff = pd->off;
/* stop walking over non initial fragments */
if ((frag.ip6f_offlg & IP6F_OFF_MASK) != 0)
return (PF_PASS);
*off += sizeof(frag);
*nxt = frag.ip6f_nxt;
pd->off += sizeof(frag);
pd->proto = frag.ip6f_nxt;
break;
case IPPROTO_ROUTING:
if (rthdr_cnt++) {
@ -9762,12 +9757,12 @@ pf_walk_header6(struct mbuf *m, struct ip6_hdr *h, int *off, int *extoff,
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (!pf_pull_hdr(m, *off, &rthdr, sizeof(rthdr), NULL,
reason, AF_INET6)) {
if (!pf_pull_hdr(pd->m, pd->off, &rthdr, sizeof(rthdr),
NULL, reason, AF_INET6)) {
/* fragments may be short */
if (*fragoff != 0) {
*off = *fragoff;
*nxt = IPPROTO_FRAGMENT;
if (pd->fragoff != 0) {
pd->off = pd->fragoff;
pd->proto = IPPROTO_FRAGMENT;
return (PF_PASS);
}
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short rthdr"));
@ -9782,50 +9777,51 @@ pf_walk_header6(struct mbuf *m, struct ip6_hdr *h, int *off, int *extoff,
case IPPROTO_AH:
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
if (!pf_pull_hdr(m, *off, &ext, sizeof(ext), NULL,
reason, AF_INET6)) {
if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext),
NULL, reason, AF_INET6)) {
/* fragments may be short */
if (*fragoff != 0) {
*off = *fragoff;
*nxt = IPPROTO_FRAGMENT;
if (pd->fragoff != 0) {
pd->off = pd->fragoff;
pd->proto = IPPROTO_FRAGMENT;
return (PF_PASS);
}
DPFPRINTF(PF_DEBUG_MISC, ("IPv6 short exthdr"));
return (PF_DROP);
}
/* reassembly needs the ext header before the frag */
if (*fragoff == 0)
*extoff = *off;
if (*nxt == IPPROTO_HOPOPTS && *fragoff == 0) {
if (pf_walk_option6(m, *off + sizeof(ext),
*off + (ext.ip6e_len + 1) * 8, jumbolen,
reason) != PF_PASS)
if (pd->fragoff == 0)
pd->extoff = pd->off;
if (pd->proto == IPPROTO_HOPOPTS && pd->fragoff == 0) {
if (pf_walk_option6(pd, h,
pd->off + sizeof(ext),
pd->off + (ext.ip6e_len + 1) * 8, reason)
!= PF_PASS)
return (PF_DROP);
if (ntohs(h->ip6_plen) == 0 && *jumbolen != 0) {
if (ntohs(h->ip6_plen) == 0 && pd->jumbolen != 0) {
DPFPRINTF(PF_DEBUG_MISC,
("IPv6 missing jumbo"));
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
}
if (*nxt == IPPROTO_AH)
*off += (ext.ip6e_len + 2) * 4;
if (pd->proto == IPPROTO_AH)
pd->off += (ext.ip6e_len + 2) * 4;
else
*off += (ext.ip6e_len + 1) * 8;
*nxt = ext.ip6e_nxt;
pd->off += (ext.ip6e_len + 1) * 8;
pd->proto = ext.ip6e_nxt;
break;
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_SCTP:
case IPPROTO_ICMPV6:
/* fragments may be short, ignore inner header then */
if (*fragoff != 0 && ntohs(h->ip6_plen) < *off +
(*nxt == IPPROTO_TCP ? sizeof(struct tcphdr) :
*nxt == IPPROTO_UDP ? sizeof(struct udphdr) :
*nxt == IPPROTO_SCTP ? sizeof(struct sctphdr) :
if (pd->fragoff != 0 && ntohs(h->ip6_plen) < pd->off +
(pd->proto == IPPROTO_TCP ? sizeof(struct tcphdr) :
pd->proto == IPPROTO_UDP ? sizeof(struct udphdr) :
pd->proto == IPPROTO_SCTP ? sizeof(struct sctphdr) :
sizeof(struct icmp6_hdr))) {
*off = *fragoff;
*nxt = IPPROTO_FRAGMENT;
pd->off = pd->fragoff;
pd->proto = IPPROTO_FRAGMENT;
}
/* FALLTHROUGH */
default:
@ -9913,9 +9909,6 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
#ifdef INET6
case AF_INET6: {
struct ip6_hdr *h;
int fragoff;
uint32_t jumbolen;
uint8_t nxt;
if (__predict_false((*m0)->m_len < sizeof(struct ip6_hdr)) &&
(pd->m = *m0 = m_pullup(*m0, sizeof(struct ip6_hdr))) == NULL) {
@ -9929,8 +9922,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->m, h, &pd->off, &pd->extoff, &fragoff, &nxt,
&jumbolen, reason) != PF_PASS) {
if (pf_walk_header6(pd, h, reason) != PF_PASS) {
*action = PF_DROP;
return (-1);
}
@ -9945,7 +9937,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
pd->virtual_proto = pd->proto = h->ip6_nxt;
pd->act.rtableid = -1;
if (fragoff != 0)
if (pd->fragoff != 0)
pd->virtual_proto = PF_VPROTO_FRAGMENT;
/*
@ -9958,7 +9950,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
}
/* We do IP header normalization and packet reassembly here */
if (pf_normalize_ip6(m0, fragoff, reason, pd) !=
if (pf_normalize_ip6(m0, pd->fragoff, reason, pd) !=
PF_PASS) {
*action = PF_DROP;
return (-1);
@ -9975,20 +9967,23 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
pd->src = (struct pf_addr *)&h->ip6_src;
pd->dst = (struct pf_addr *)&h->ip6_dst;
/*
* Reassembly may have changed the next protocol from fragment
* to something else, so update.
*/
pd->virtual_proto = pd->proto = h->ip6_nxt;
pd->off = 0;
if (pf_walk_header6(pd->m, h, &pd->off, &pd->extoff, &fragoff, &nxt,
&jumbolen, reason) != PF_PASS) {
if (pf_walk_header6(pd, h, reason) != PF_PASS) {
*action = PF_DROP;
return (-1);
}
if (fragoff != 0)
if (m_tag_find(pd->m, PACKET_TAG_PF_REASSEMBLED, NULL) != NULL) {
/*
* Reassembly may have changed the next protocol from
* fragment to something else, so update.
*/
pd->virtual_proto = pd->proto;
MPASS(pd->fragoff == 0);
}
if (pd->fragoff != 0)
pd->virtual_proto = PF_VPROTO_FRAGMENT;
break;