git: 7cae58a44955 - main - pf: handle fragmentation for nat64

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 17 Dec 2024 10:08:05 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=7cae58a449559e7dca179129a37d971379d4e2c2

commit 7cae58a449559e7dca179129a37d971379d4e2c2
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-11-22 15:30:25 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-12-17 10:07:16 +0000

    pf: handle fragmentation for nat64
    
    When we reassemble IPv4 packets tag them just like we tag the IPv6 reassembled
    packtes. Use this information as the basis for refragmenting the IPv6 packet.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D47804
---
 sys/net/pfvar.h          |  7 +++++++
 sys/netpfil/pf/pf.c      | 16 +++++++++++++++-
 sys/netpfil/pf/pf_norm.c | 26 +++++++++++++++++++-------
 3 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index e0ac9561f463..d22f715d6a27 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1762,6 +1762,13 @@ struct pf_divert {
 #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
index 08486d5d1467..13a299a8dcd4 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -3440,6 +3440,8 @@ pf_translate_af(struct pf_pdesc *pd)
 	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 @@ pf_translate_af(struct pf_pdesc *pd)
 		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 @@ pf_translate_af(struct pf_pdesc *pd)
 		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
index cea6f9e72638..4adace4c92cf 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -103,13 +103,6 @@ struct pf_fragment {
 	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 @@ pf_reassemble(struct mbuf **m0, int dir, u_short *reason)
 	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 @@ pf_reassemble(struct mbuf **m0, int dir, u_short *reason)
 		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 @@ pf_reassemble(struct mbuf **m0, int dir, u_short *reason)
 		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);