git: a908f8f0dc62 - main - pf: tag dummynet'd route-to packets with their real destination

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Thu, 12 May 2022 19:58:14 UTC
The branch main has been updated by kp:

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

commit a908f8f0dc62ebf61b6f92c60c9c053be6ccb194
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2022-05-10 07:23:36 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-05-12 19:50:10 +0000

    pf: tag dummynet'd route-to packets with their real destination
    
    If we delay route-to/dup-to/reply-to through dummynet we are eventually
    returned to pf_test(). At that point we no longer have the context for
    the route-to destination. We'd just skip the pf_test() and continue
    processing. This means that route-to did not work as expected.
    
    Extend pf_mtag to carry the route-to destination so we can apply it when
    we re-enter pf_test().
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D35159
---
 sys/netpfil/pf/pf.c      | 67 +++++++++++++++++++++++++++++++++++++++++++++---
 sys/netpfil/pf/pf_mtag.h |  5 +++-
 2 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 343668030d0d..bd9334982be9 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -283,6 +283,9 @@ void			 pf_rule_to_actions(struct pf_krule *,
 			    struct pf_rule_actions *);
 static int		 pf_dummynet(struct pf_pdesc *, int, struct pf_kstate *,
 			    struct pf_krule *, struct mbuf **);
+static int		 pf_dummynet_route(struct pf_pdesc *, int,
+			    struct pf_kstate *, struct pf_krule *,
+			    struct ifnet *, struct sockaddr *, struct mbuf **);
 static int		 pf_test_eth_rule(int, struct pfi_kkif *,
 			    struct mbuf **);
 static int		 pf_test_rule(struct pf_krule **, struct pf_kstate **,
@@ -6382,7 +6385,7 @@ pf_route(struct mbuf **m, struct pf_krule *r, int dir, struct ifnet *oifp,
 		m_clrprotoflags(m0);	/* Avoid confusing lower layers. */
 
 		md = m0;
-		error = pf_dummynet(pd, dir, s, r, &md);
+		error = pf_dummynet_route(pd, dir, s, r, ifp, sintosa(&dst), &md);
 		if (md != NULL)
 			error = (*ifp->if_output)(ifp, md, sintosa(&dst), NULL);
 		goto done;
@@ -6415,7 +6418,8 @@ pf_route(struct mbuf **m, struct pf_krule *r, int dir, struct ifnet *oifp,
 		if (error == 0) {
 			m_clrprotoflags(m0);
 			md = m0;
-			error = pf_dummynet(pd, dir, s, r, &md);
+			error = pf_dummynet_route(pd, dir, s, r, ifp,
+			    sintosa(&dst), &md);
 			if (md != NULL)
 				error = (*ifp->if_output)(ifp, md,
 				    sintosa(&dst), NULL);
@@ -6565,7 +6569,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, int dir, struct ifnet *oifp,
 		dst.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
 	if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) {
 		md = m0;
-		pf_dummynet(pd, dir, s, r, &md);
+		pf_dummynet_route(pd, dir, s, r, ifp, sintosa(&dst), &md);
 		if (md != NULL)
 			nd6_output_ifp(ifp, ifp, md, &dst, NULL);
 	}
@@ -6827,6 +6831,16 @@ static int
 pf_dummynet(struct pf_pdesc *pd, int dir, struct pf_kstate *s,
     struct pf_krule *r, struct mbuf **m0)
 {
+	return (pf_dummynet_route(pd, dir, s, r, NULL, NULL, m0));
+}
+
+static int
+pf_dummynet_route(struct pf_pdesc *pd, int dir, struct pf_kstate *s,
+    struct pf_krule *r, struct ifnet *ifp, struct sockaddr *sa,
+    struct mbuf **m0)
+{
+	NET_EPOCH_ASSERT();
+
 	if (s && (s->dnpipe || s->dnrpipe)) {
 		pd->act.dnpipe = s->dnpipe;
 		pd->act.dnrpipe = s->dnrpipe;
@@ -6851,6 +6865,22 @@ pf_dummynet(struct pf_pdesc *pd, int dir, struct pf_kstate *s,
 			return (ENOMEM);
 		}
 
+		if (ifp != NULL) {
+			pd->pf_mtag->flags |= PF_TAG_ROUTE_TO;
+
+			pd->pf_mtag->if_index = ifp->if_index;
+			pd->pf_mtag->if_idxgen = ifp->if_idxgen;
+
+			MPASS(sa != NULL);
+
+			if (pd->af == AF_INET)
+				memcpy(&pd->pf_mtag->dst, sa,
+				    sizeof(struct sockaddr_in));
+			else
+				memcpy(&pd->pf_mtag->dst, sa,
+				    sizeof(struct sockaddr_in6));
+		}
+
 		if (pf_pdesc_to_dnflow(dir, pd, r, s, &dnflow)) {
 			pd->pf_mtag->flags |= PF_TAG_DUMMYNET;
 			ip_dn_io_ptr(m0, &dnflow);
@@ -6900,6 +6930,21 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb *
 	memset(&pd, 0, sizeof(pd));
 	pd.pf_mtag = pf_find_mtag(m);
 
+	if (pd.pf_mtag != NULL && (pd.pf_mtag->flags & PF_TAG_ROUTE_TO)) {
+		pd.pf_mtag->flags &= ~PF_TAG_ROUTE_TO;
+
+		ifp = ifnet_byindexgen(pd.pf_mtag->if_index,
+		    pd.pf_mtag->if_idxgen);
+		if (ifp == NULL || ifp->if_flags & IFF_DYING) {
+			m_freem(*m0);
+			*m0 = NULL;
+			return (PF_PASS);
+		}
+		(ifp->if_output)(ifp, m, sintosa(&pd.pf_mtag->dst), NULL);
+		*m0 = NULL;
+		return (PF_PASS);
+	}
+
 	if (pd.pf_mtag && pd.pf_mtag->dnpipe) {
 		pd.act.dnpipe = pd.pf_mtag->dnpipe;
 		pd.act.flags = pd.pf_mtag->dnflags;
@@ -7374,6 +7419,22 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
 	memset(&pd, 0, sizeof(pd));
 	pd.pf_mtag = pf_find_mtag(m);
 
+	if (pd.pf_mtag != NULL && (pd.pf_mtag->flags & PF_TAG_ROUTE_TO)) {
+		pd.pf_mtag->flags &= ~PF_TAG_ROUTE_TO;
+
+		ifp = ifnet_byindexgen(pd.pf_mtag->if_index,
+		    pd.pf_mtag->if_idxgen);
+		if (ifp == NULL || ifp->if_flags & IFF_DYING) {
+			m_freem(*m0);
+			*m0 = NULL;
+			return (PF_PASS);
+		}
+		nd6_output_ifp(ifp, ifp, m,
+                    (struct sockaddr_in6 *)&pd.pf_mtag->dst, NULL);
+		*m0 = NULL;
+		return (PF_PASS);
+	}
+
 	if (pd.pf_mtag && pd.pf_mtag->dnpipe) {
 		pd.act.dnpipe = pd.pf_mtag->dnpipe;
 		pd.act.flags = pd.pf_mtag->dnflags;
diff --git a/sys/netpfil/pf/pf_mtag.h b/sys/netpfil/pf/pf_mtag.h
index 50928d4b204b..c9ea98b80b69 100644
--- a/sys/netpfil/pf/pf_mtag.h
+++ b/sys/netpfil/pf/pf_mtag.h
@@ -36,7 +36,7 @@
 
 #ifdef _KERNEL
 
-/*					0x01 unused. */
+#define	PF_TAG_ROUTE_TO			0x01
 #define	PF_TAG_DUMMYNET			0x02
 #define	PF_TAG_TRANSLATE_LOCALHOST	0x04
 #define	PF_PACKET_LOOPED		0x08
@@ -54,6 +54,9 @@ struct pf_mtag {
 	u_int8_t	 routed;
 	u_int16_t	 dnpipe;
 	u_int32_t	 dnflags;
+	u_int16_t	 if_index;	/* For ROUTE_TO */
+	u_int16_t	 if_idxgen;	/* For ROUTE_TO */
+	struct sockaddr_storage	dst;	/* For ROUTE_TO */
 };
 
 static __inline struct pf_mtag *