git: b52b61c0b6b1 - main - pf: distinguish forwarding and output cases for pf_refragment6()

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Thu, 16 Mar 2023 10:01:24 UTC
The branch main has been updated by kp:

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

commit b52b61c0b6b1cb309461060f53cd5b7f5c3bb4ed
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-03-12 17:34:42 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-03-16 09:59:04 +0000

    pf: distinguish forwarding and output cases for pf_refragment6()
    
    Re-introduce PFIL_FWD, because pf's pf_refragment6() needs to know if
    we're ip6_forward()-ing or ip6_output()-ing.
    
    ip6_forward() relies on m->m_pkthdr.rcvif, at least for link-local
    traffic (for in6_get_unicast_scopeid()). rcvif is not set for locally
    generated traffic (e.g. from icmp6_reflect()), so we need to call the
    correct output function.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revisi:    https://reviews.freebsd.org/D39061
---
 sys/net/pfil.c             | 15 +++++++++++++--
 sys/net/pfil.h             |  4 +++-
 sys/net/pfvar.h            |  2 +-
 sys/netinet6/ip6_forward.c |  2 +-
 sys/netpfil/pf/pf.c        |  2 +-
 sys/netpfil/pf/pf_norm.c   | 10 ++++++++--
 6 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/sys/net/pfil.c b/sys/net/pfil.c
index e6f3ff8c1269..71b126558b44 100644
--- a/sys/net/pfil.c
+++ b/sys/net/pfil.c
@@ -202,8 +202,11 @@ pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp,
 	pfil_return_t rv;
 
 	NET_EPOCH_ASSERT();
-	KASSERT(flags == PFIL_IN || flags == PFIL_OUT,
-	    ("%s: unsupported flags %d", __func__, flags));
+	KASSERT((flags & ~(PFIL_IN|PFIL_OUT|PFIL_FWD)) == 0,
+	    ("%s: unsupported flags %#x", __func__, flags));
+	KASSERT((flags & ~PFIL_FWD) == PFIL_IN ||
+	    (flags & ~PFIL_FWD) == PFIL_OUT,
+	    ("%s: conflicting directions %#x", __func__, flags));
 
 	rv = PFIL_PASS;
 	CK_STAILQ_FOREACH(link, pch, link_chain) {
@@ -231,6 +234,14 @@ pfil_mbuf_out(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
 	return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT, inp));
 }
 
+int
+pfil_mbuf_fwd(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
+    struct inpcb *inp)
+{
+
+	return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT | PFIL_FWD, inp));
+}
+
 /*
  * pfil_head_register() registers a pfil_head with the packet filter hook
  * mechanism.
diff --git a/sys/net/pfil.h b/sys/net/pfil.h
index b99ec6896266..96a43d05fbac 100644
--- a/sys/net/pfil.h
+++ b/sys/net/pfil.h
@@ -80,7 +80,7 @@ struct pfilioc_link {
 
 #define	PFIL_IN		0x00010000
 #define	PFIL_OUT	0x00020000
-/* UNUSED		0x00040000 */
+#define	PFIL_FWD	0x00040000
 #define	PFIL_DIR(f)	((f) & (PFIL_IN|PFIL_OUT))
 #define	PFIL_HEADPTR	0x00100000
 #define	PFIL_HOOKPTR	0x00200000
@@ -179,6 +179,8 @@ int	pfil_mbuf_in(struct pfil_head *, struct mbuf **, struct ifnet *,
     struct inpcb *inp);
 int	pfil_mbuf_out(struct pfil_head *, struct mbuf **, struct ifnet *,
     struct inpcb *inp);
+int	pfil_mbuf_fwd(struct pfil_head *, struct mbuf **, struct ifnet *,
+    struct inpcb *);
 
 /*
  * Minimally exposed structure to avoid function call in case of absence
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index bb058c8e8ae6..b97524f34bc3 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2135,7 +2135,7 @@ int	pf_normalize_ip6(struct mbuf **, int, struct pfi_kkif *, u_short *,
 void	pf_poolmask(struct pf_addr *, struct pf_addr*,
 	    struct pf_addr *, struct pf_addr *, u_int8_t);
 void	pf_addr_inc(struct pf_addr *, sa_family_t);
-int	pf_refragment6(struct ifnet *, struct mbuf **, struct m_tag *);
+int	pf_refragment6(struct ifnet *, struct mbuf **, struct m_tag *, bool);
 #endif /* INET6 */
 
 u_int32_t	pf_new_isn(struct pf_kstate *);
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index 293fcd977344..c8f9079e0aa7 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -321,7 +321,7 @@ again:
 
 	odst = ip6->ip6_dst;
 	/* Run through list of hooks for forwarded packets. */
-	if (pfil_mbuf_out(V_inet6_pfil_head, &m, nh->nh_ifp,
+	if (pfil_mbuf_fwd(V_inet6_pfil_head, &m, nh->nh_ifp,
 	    NULL) != PFIL_PASS)
 		goto freecopy;
 	ip6 = mtod(m, struct ip6_hdr *);
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 0ccf1fcfc693..f20851cb60e8 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -7953,7 +7953,7 @@ done:
 	/* If reassembled packet passed, create new fragments. */
 	if (action == PF_PASS && *m0 && dir == PF_OUT &&
 	    (mtag = m_tag_find(m, PF_REASSEMBLED, NULL)) != NULL)
-		action = pf_refragment6(ifp, m0, mtag);
+		action = pf_refragment6(ifp, m0, mtag, pflags & PFIL_FWD);
 
 	SDT_PROBE4(pf, ip, test6, done, action, reason, r, s);
 
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
index 283f6771221c..bc5f6d38a2bf 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -942,7 +942,8 @@ fail:
 
 #ifdef INET6
 int
-pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag)
+pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag,
+    bool forward)
 {
 	struct mbuf		*m = *m0, *t;
 	struct pf_fragment_tag	*ftag = (struct pf_fragment_tag *)(mtag + 1);
@@ -1009,7 +1010,12 @@ pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag)
 		memset(&pd, 0, sizeof(pd));
 		pd.pf_mtag = pf_find_mtag(m);
 		if (error == 0)
-			ip6_forward(m, 0);
+			if (forward) {
+				MPASS(m->m_pkthdr.rcvif != NULL);
+				ip6_forward(m, 0);
+			} else {
+				ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
+			}
 		else
 			m_freem(m);
 	}