git: fb330f3931a2 - main - pf: support dummynet on L2 rules

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Wed, 02 Mar 2022 16:00:57 UTC
The branch main has been updated by kp:

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

commit fb330f3931a2df0cebd2c8726f9d5362cceb0f2a
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2021-09-27 12:50:30 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-03-02 16:00:06 +0000

    pf: support dummynet on L2 rules
    
    Allow packets to be tagged with dummynet information. Note that we do
    not apply dummynet shaping on the L2 traffic, but instead mark it for
    dummynet processing in the L3 code. This is the same approach as we take
    for ALTQ.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D32222
---
 lib/libpfctl/libpfctl.c   |  6 ++++++
 lib/libpfctl/libpfctl.h   |  2 ++
 sbin/pfctl/parse.y        | 10 ++++++++++
 sbin/pfctl/pfctl_parser.c |  4 ++++
 sys/net/pfvar.h           |  2 ++
 sys/netpfil/pf/pf.c       | 35 +++++++++++++++++++++++++++++++++--
 sys/netpfil/pf/pf_mtag.h  |  2 ++
 sys/netpfil/pf/pf_nv.c    |  8 +++++++-
 8 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 7255d18410a8..fd7dd7a474a0 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -603,6 +603,9 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
 	strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"),
 	    PF_TAG_NAME_SIZE);
 
+	rule->dnpipe = nvlist_get_number(nvl, "dnpipe");
+	rule->dnflags = nvlist_get_number(nvl, "dnflags");
+
 	rule->action = nvlist_get_number(nvl, "action");
 }
 
@@ -709,6 +712,9 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, uint32_t ticket)
 
 	nvlist_add_string(nvl, "qname", r->qname);
 	nvlist_add_string(nvl, "tagname", r->tagname);
+	nvlist_add_number(nvl, "dnpipe", r->dnpipe);
+	nvlist_add_number(nvl, "dnflags", r->dnflags);
+
 	nvlist_add_number(nvl, "action", r->action);
 
 	packed = nvlist_pack(nvl, &size);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index b44200e00ad9..6c3dbfc5d0df 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -96,6 +96,8 @@ struct pfctl_eth_rule {
 	/* Action */
 	char			 qname[PF_QNAME_SIZE];
 	char			 tagname[PF_TAG_NAME_SIZE];
+	uint16_t		 dnpipe;
+	uint32_t		 dnflags;
 	uint8_t			 action;
 
 	TAILQ_ENTRY(pfctl_eth_rule)	 entries;
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 1bf9372cd7a6..a2d8cfef8232 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -1200,6 +1200,8 @@ etherrule	: ETHER action dir quick interface etherproto etherfromto etherfilter_
 				memcpy(&r.tagname, $8.tag, sizeof(r.tagname));
 			if ($8.queues.qname != NULL)
 				memcpy(&r.qname, $8.queues.qname, sizeof(r.qname));
+			r.dnpipe = $8.dnpipe;
+			r.dnflags = $8.free_flags;
 
 			expand_eth_rule(&r, $5, $6);
 		}
@@ -1229,6 +1231,14 @@ etherfilter_opt	: etherqname	{
 		| TAG string				{
 			filter_opts.tag = $2;
 		}
+		| DNPIPE number {
+			filter_opts.dnpipe = $2;
+			filter_opts.free_flags |= PFRULE_DN_IS_PIPE;
+		}
+		| DNQUEUE number {
+			filter_opts.dnpipe = $2;
+			filter_opts.free_flags |= PFRULE_DN_IS_QUEUE;
+		}
 		;
 
 scrubrule	: scrubaction dir logquick interface af proto fromto scrub_opts
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index c7e980103fad..bde6d4b10696 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -747,6 +747,10 @@ print_eth_rule(struct pfctl_eth_rule *r, int rule_numbers)
 		printf(" queue %s", r->qname);
 	if (r->tagname[0])
 		printf(" tag %s", r->tagname);
+	if (r->dnpipe)
+		printf(" %s %d",
+		    r->dnflags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue",
+		    r->dnpipe);
 }
 
 void
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index a0b23759857a..11fb57b49fca 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -617,6 +617,8 @@ struct pf_keth_rule {
 	char			 tagname[PF_TAG_NAME_SIZE];
 	uint16_t		 tag;
 	uint8_t			 action;
+	uint16_t		 dnpipe;
+	uint32_t		 dnflags;
 };
 
 TAILQ_HEAD(pf_keth_rules, pf_keth_rule);
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 8bf309caf7d4..09d727508750 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -3813,6 +3813,22 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
 		mtag->qid = r->qid;
 	}
 
+	/* Dummynet */
+	if (r->dnpipe) {
+		/** While dummynet supports handling Ethernet packets directly
+		 * it still wants some L3/L4 information, and we're not set up
+		 * to provide that here. Instead we'll do what we do for ALTQ
+		 * and merely mark the packet with the dummynet queue/pipe number.
+		 **/
+		mtag = pf_get_mtag(m);
+		if (mtag == NULL) {
+			counter_u64_add(V_pf_status.counters[PFRES_MEMORY], 1);
+			return (PF_DROP);
+		}
+		mtag->dnpipe = r->dnpipe;
+		mtag->dnflags = r->dnflags;
+	}
+
 	action = r->action;
 
 	return (action);
@@ -6515,8 +6531,13 @@ pf_pdesc_to_dnflow(int dir, const struct pf_pdesc *pd,
 {
 	int dndir = r->direction;
 
-	if (s && dndir == PF_INOUT)
+	if (s && dndir == PF_INOUT) {
 		dndir = s->direction;
+	} else if (dndir == PF_INOUT) {
+		/* Assume primary direction. Happens when we've set dnpipe in
+		 * the ethernet level code. */
+		dndir = dir;
+	}
 
 	memset(dnflow, 0, sizeof(*dnflow));
 
@@ -6541,7 +6562,7 @@ pf_pdesc_to_dnflow(int dir, const struct pf_pdesc *pd,
 	}
 
 	dnflow->rule.info |= IPFW_IS_DUMMYNET;
-	if (r->free_flags & PFRULE_DN_IS_PIPE)
+	if (r->free_flags & PFRULE_DN_IS_PIPE || pd->act.flags & PFRULE_DN_IS_PIPE)
 		dnflow->rule.info |= IPFW_IS_PIPE;
 
 	dnflow->f_id.proto = pd->proto;
@@ -6635,6 +6656,11 @@ 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 && pd.pf_mtag->dnpipe) {
+		pd.act.dnpipe = pd.pf_mtag->dnpipe;
+		pd.act.flags = pd.pf_mtag->dnflags;
+	}
+
 	if (ip_dn_io_ptr != NULL && pd.pf_mtag != NULL &&
 	    pd.pf_mtag->flags & PF_TAG_DUMMYNET) {
 		/* Dummynet re-injects packets after they've
@@ -7134,6 +7160,11 @@ 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 && pd.pf_mtag->dnpipe) {
+		pd.act.dnpipe = pd.pf_mtag->dnpipe;
+		pd.act.flags = pd.pf_mtag->dnflags;
+	}
+
 	if (ip_dn_io_ptr != NULL && pd.pf_mtag != NULL &&
 	    pd.pf_mtag->flags & PF_TAG_DUMMYNET) {
 		pd.pf_mtag->flags &= ~PF_TAG_DUMMYNET;
diff --git a/sys/netpfil/pf/pf_mtag.h b/sys/netpfil/pf/pf_mtag.h
index 82b8d32fbdfc..50928d4b204b 100644
--- a/sys/netpfil/pf/pf_mtag.h
+++ b/sys/netpfil/pf/pf_mtag.h
@@ -52,6 +52,8 @@ struct pf_mtag {
 	u_int16_t	 tag;		/* tag id */
 	u_int8_t	 flags;
 	u_int8_t	 routed;
+	u_int16_t	 dnpipe;
+	u_int32_t	 dnflags;
 };
 
 static __inline struct pf_mtag *
diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c
index 24128a21c363..b662bb4e7d95 100644
--- a/sys/netpfil/pf/pf_nv.c
+++ b/sys/netpfil/pf/pf_nv.c
@@ -1081,6 +1081,9 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
 	nvlist_add_string(nvl, "qname", krule->qname);
 	nvlist_add_string(nvl, "tagname", krule->tagname);
 
+	nvlist_add_number(nvl, "dnpipe", krule->dnpipe);
+	nvlist_add_number(nvl, "dnflags", krule->dnflags);
+
 	nvlist_add_number(nvl, "action", krule->action);
 
 	return (nvl);
@@ -1090,7 +1093,7 @@ int
 pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
     struct pf_keth_rule *krule)
 {
-	int error;
+	int error = 0;
 
 	bzero(krule, sizeof(*krule));
 
@@ -1119,6 +1122,9 @@ pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
 	PFNV_CHK(pf_nvstring(nvl, "tagname", krule->tagname,
 	    sizeof(krule->tagname)));
 
+	PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &krule->dnpipe, 0));
+	PFNV_CHK(pf_nvuint32_opt(nvl, "dnflags", &krule->dnflags, 0));
+
 	PFNV_CHK(pf_nvuint8(nvl, "action", &krule->action));
 
 	if (krule->action != PF_PASS && krule->action != PF_DROP)