diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1762,6 +1762,13 @@ #define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ #define PFR_KENTRY_HIWAT 200000 /* Number of table entries */ +struct pf_fragment_tag { + uint16_t ft_hdrlen; /* header length of reassembled pkt */ + uint16_t ft_extoff; /* last extension header offset or 0 */ + uint16_t ft_maxlen; /* maximum fragment payload length */ + uint32_t ft_id; /* fragment id */ +}; + /* * Limit the length of the fragment queue traversal. Remember * search entry points based on the fragment offset. diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -3440,6 +3440,8 @@ struct ip *ip4; struct ip6_hdr *ip6; struct icmp6_hdr *icmp; + struct m_tag *mtag; + struct pf_fragment_tag *ftag; int hlen; hlen = pd->naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6); @@ -3460,7 +3462,6 @@ ip4->ip_hl = hlen >> 2; ip4->ip_len = htons(hlen + (pd->tot_len - pd->off)); ip_fillid(ip4); - ip4->ip_off = htons(IP_DF); ip4->ip_ttl = pd->ttl; ip4->ip_p = pd->proto; ip4->ip_src = pd->nsaddr.v4; @@ -3482,6 +3483,19 @@ ip6->ip6_dst = pd->ndaddr.v6; pd->src = (struct pf_addr *)&ip6->ip6_src; pd->dst = (struct pf_addr *)&ip6->ip6_dst; + + /* + * If we're dealing with a reassembled packet we need to adjust + * the header length from the IPv4 header size to IPv6 header + * size. + */ + mtag = m_tag_find(pd->m, PACKET_TAG_PF_REASSEMBLED, NULL); + if (mtag) { + ftag = (struct pf_fragment_tag *)(mtag + 1); + ftag->ft_hdrlen = sizeof(*ip6); + ftag->ft_maxlen -= sizeof(struct ip6_hdr) - + sizeof(struct ip) + sizeof(struct ip6_frag); + } break; default: return (-1); diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -103,13 +103,6 @@ TAILQ_HEAD(pf_fragq, pf_frent) fr_queue; }; -struct pf_fragment_tag { - uint16_t ft_hdrlen; /* header length of reassembled pkt */ - uint16_t ft_extoff; /* last extension header offset or 0 */ - uint16_t ft_maxlen; /* maximum fragment payload length */ - uint32_t ft_id; /* fragment id */ -}; - VNET_DEFINE_STATIC(struct mtx, pf_frag_mtx); #define V_pf_frag_mtx VNET(pf_frag_mtx) #define PF_FRAG_LOCK() mtx_lock(&V_pf_frag_mtx) @@ -750,8 +743,12 @@ struct ip *ip = mtod(m, struct ip *); struct pf_frent *frent; struct pf_fragment *frag; + struct m_tag *mtag; + struct pf_fragment_tag *ftag; struct pf_fragment_cmp key; uint16_t total, hdrlen; + uint32_t frag_id; + uint16_t maxlen; /* Get an entry for the fragment queue */ if ((frent = pf_create_fragment(reason)) == NULL) @@ -784,6 +781,8 @@ TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_len; hdrlen = frent->fe_hdrlen; + maxlen = frag->fr_maxlen; + frag_id = frag->fr_id; m = *m0 = pf_join_fragment(frag); frag = NULL; @@ -795,6 +794,19 @@ m->m_pkthdr.len = plen; } + if ((mtag = m_tag_get(PACKET_TAG_PF_REASSEMBLED, + sizeof(struct pf_fragment_tag), M_NOWAIT)) == NULL) { + REASON_SET(reason, PFRES_SHORT); + /* PF_DROP requires a valid mbuf *m0 in pf_test() */ + return (PF_DROP); + } + ftag = (struct pf_fragment_tag *)(mtag + 1); + ftag->ft_hdrlen = hdrlen; + ftag->ft_extoff = 0; + ftag->ft_maxlen = maxlen; + ftag->ft_id = frag_id; + m_tag_prepend(m, mtag); + ip = mtod(m, struct ip *); ip->ip_sum = pf_cksum_fixup(ip->ip_sum, ip->ip_len, htons(hdrlen + total), 0);