git: ef661d4a5bf9 - main - pf: introduce ridentifier and labels to ether rules

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Wed, 26 Apr 2023 09:19:47 UTC
The branch main has been updated by kp:

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

commit ef661d4a5bf912e4d4850faaf50664532d82541c
Author:     Christian McDonald <cmcdonald@netgate.com>
AuthorDate: 2023-04-24 18:55:34 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-04-26 09:14:41 +0000

    pf: introduce ridentifier and labels to ether rules
    
    Make Ethernet rules more similar to the usual layer 3 rules by also
    allowing ridentifier and labels to be set on them.
    
    Reviewed by:    kp
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c              | 20 +++++++++++++++++++-
 lib/libpfctl/libpfctl.h              |  3 +++
 sbin/pfctl/parse.y                   | 33 +++++++++++++++++++++++++++++++++
 sbin/pfctl/pfctl_parser.c            |  9 +++++++++
 sbin/pfctl/tests/files/pf1013.in     |  1 +
 sbin/pfctl/tests/files/pf1013.ok     |  1 +
 sbin/pfctl/tests/files/pf1014.in     |  1 +
 sbin/pfctl/tests/files/pf1014.ok     |  1 +
 sbin/pfctl/tests/files/pf1015.in     |  1 +
 sbin/pfctl/tests/files/pf1015.ok     |  1 +
 sbin/pfctl/tests/files/pf1016.in     |  1 +
 sbin/pfctl/tests/files/pf1016.ok     |  1 +
 sbin/pfctl/tests/files/pf1017.in     |  1 +
 sbin/pfctl/tests/files/pf1017.ok     |  1 +
 sbin/pfctl/tests/pfctl_test_list.inc |  5 +++++
 share/man/man5/pf.conf.5             |  5 +++--
 sys/net/pfvar.h                      |  3 +++
 sys/netpfil/pf/pf_nv.c               | 27 +++++++++++++++++++++++++++
 18 files changed, 112 insertions(+), 3 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index c75f9ab12889..4f251e92d9aa 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -625,6 +625,9 @@ pfctl_eth_addr_to_nveth_addr(const struct pfctl_eth_addr *addr)
 static void
 pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
 {
+	const char *const *labels;
+	size_t labelcount, i;
+
 	rule->nr = nvlist_get_number(nvl, "nr");
 	rule->quick = nvlist_get_bool(nvl, "quick");
 	strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ);
@@ -636,6 +639,12 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
 	rule->match_tag = nvlist_get_number(nvl, "match_tag");
 	rule->match_tag_not = nvlist_get_bool(nvl, "match_tag_not");
 
+	labels = nvlist_get_string_array(nvl, "labels", &labelcount);
+	assert(labelcount <= PF_RULE_MAX_LABEL_COUNT);
+	for (i = 0; i < labelcount; i++)
+		strlcpy(rule->label[i], labels[i], PF_RULE_LABEL_SIZE);
+	rule->ridentifier = nvlist_get_number(nvl, "ridentifier");
+
 	pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "src"),
 	    &rule->src);
 	pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "dst"),
@@ -775,7 +784,7 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
 	nvlist_t *nvl, *addr;
 	void *packed;
 	int error = 0;
-	size_t size;
+	size_t labelcount, size;
 
 	nvl = nvlist_create(0);
 
@@ -811,6 +820,15 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
 	pfctl_nv_add_rule_addr(nvl, "ipsrc", &r->ipsrc);
 	pfctl_nv_add_rule_addr(nvl, "ipdst", &r->ipdst);
 
+	labelcount = 0;
+	while (r->label[labelcount][0] != 0 &&
+	    labelcount < PF_RULE_MAX_LABEL_COUNT) {
+		nvlist_append_string_array(nvl, "labels",
+		    r->label[labelcount]);
+		labelcount++;
+	}
+	nvlist_add_number(nvl, "ridentifier", r->ridentifier);
+
 	nvlist_add_string(nvl, "qname", r->qname);
 	nvlist_add_string(nvl, "tagname", r->tagname);
 	nvlist_add_number(nvl, "dnpipe", r->dnpipe);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 1a07b74dc10f..064adafcf3ed 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -87,6 +87,9 @@ struct pfctl_eth_addr {
 struct pfctl_eth_rule {
 	uint32_t		 nr;
 
+	char			label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
+	uint32_t		ridentifier;
+
 	bool			 quick;
 
 	/* Filter */
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index e5629f9fcd5f..b0f631a5998c 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -379,6 +379,7 @@ int		 expand_skip_interface(struct node_if *);
 int	 check_rulestate(int);
 int	 getservice(char *);
 int	 rule_label(struct pfctl_rule *, char *s[PF_RULE_MAX_LABEL_COUNT]);
+int	 eth_rule_label(struct pfctl_eth_rule *, char *s[PF_RULE_MAX_LABEL_COUNT]);
 int	 rt_tableid_max(void);
 
 void	 mv_rules(struct pfctl_ruleset *, struct pfctl_ruleset *);
@@ -1243,6 +1244,11 @@ etherrule	: ETHER action dir quick interface bridge etherproto etherfromto l3fro
 				memcpy(&r.qname, $10.queues.qname, sizeof(r.qname));
 			r.dnpipe = $10.dnpipe;
 			r.dnflags = $10.free_flags;
+			if (eth_rule_label(&r, $10.label))
+				YYERROR;
+			for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++)
+				free($10.label[i]);
+			r.ridentifier = $10.ridentifier;
 
 			expand_eth_rule(&r, $5, $7, $8.src, $8.dst,
 			    $9.src.host, $9.dst.host, $6, "");
@@ -1366,6 +1372,16 @@ etherfilter_opt	: etherqname	{
 			}
 			filter_opts.queues = $1;
 		}
+		| RIDENTIFIER number {
+			filter_opts.ridentifier = $2;
+		}
+		| label	{
+			if (filter_opts.labelcount >= PF_RULE_MAX_LABEL_COUNT) {
+				yyerror("label can only be used %d times", PF_RULE_MAX_LABEL_COUNT);
+				YYERROR;
+			}
+			filter_opts.label[filter_opts.labelcount++] = $1;
+		}
 		| TAG string				{
 			filter_opts.tag = $2;
 		}
@@ -6945,6 +6961,23 @@ rule_label(struct pfctl_rule *r, char *s[PF_RULE_MAX_LABEL_COUNT])
 	return (0);
 }
 
+int
+eth_rule_label(struct pfctl_eth_rule *r, char *s[PF_RULE_MAX_LABEL_COUNT])
+{
+	for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) {
+		if (s[i] == NULL)
+			return (0);
+
+		if (strlcpy(r->label[i], s[i], sizeof(r->label[0])) >=
+		    sizeof(r->label[0])) {
+			yyerror("rule label too long (max %d chars)",
+			    sizeof(r->label[0])-1);
+			return (-1);
+		}
+	}
+	return (0);
+}
+
 u_int16_t
 parseicmpspec(char *w, sa_family_t af)
 {
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 1f8627f5e246..c3aa840bca40 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -755,6 +755,8 @@ print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
 	static const char *actiontypes[] = { "pass", "block", "", "", "", "",
 	    "", "", "", "", "", "", "match" };
 
+	int i;
+
 	if (rule_numbers)
 		printf("@%u ", r->nr);
 
@@ -797,6 +799,13 @@ print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
 	print_fromto(&r->ipsrc, PF_OSFP_ANY, &r->ipdst,
 	    r->proto == ETHERTYPE_IP ? AF_INET : AF_INET6, 0,
 	    0, 0);
+
+	i = 0;
+	while (r->label[i][0])
+		printf(" label \"%s\"", r->label[i++]);
+	if (r->ridentifier)
+		printf(" ridentifier %u", r->ridentifier);
+
 	if (r->qname[0])
 		printf(" queue %s", r->qname);
 	if (r->tagname[0])
diff --git a/sbin/pfctl/tests/files/pf1013.in b/sbin/pfctl/tests/files/pf1013.in
new file mode 100644
index 000000000000..053804e1a35a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1013.in
@@ -0,0 +1 @@
+ether block out on igb0 ridentifier 12345678
diff --git a/sbin/pfctl/tests/files/pf1013.ok b/sbin/pfctl/tests/files/pf1013.ok
new file mode 100644
index 000000000000..7395f3fd6311
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1013.ok
@@ -0,0 +1 @@
+ether block out on igb0 l3 all ridentifier 12345678
diff --git a/sbin/pfctl/tests/files/pf1014.in b/sbin/pfctl/tests/files/pf1014.in
new file mode 100644
index 000000000000..8739034f1bda
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1014.in
@@ -0,0 +1 @@
+ether block out on igb0 label "test"
diff --git a/sbin/pfctl/tests/files/pf1014.ok b/sbin/pfctl/tests/files/pf1014.ok
new file mode 100644
index 000000000000..d0086cb25e54
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1014.ok
@@ -0,0 +1 @@
+ether block out on igb0 l3 all label "test"
diff --git a/sbin/pfctl/tests/files/pf1015.in b/sbin/pfctl/tests/files/pf1015.in
new file mode 100644
index 000000000000..11c7a211ae8a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1015.in
@@ -0,0 +1 @@
+ether block out on igb0 label "test" label "another label"
diff --git a/sbin/pfctl/tests/files/pf1015.ok b/sbin/pfctl/tests/files/pf1015.ok
new file mode 100644
index 000000000000..d3ea76f1875b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1015.ok
@@ -0,0 +1 @@
+ether block out on igb0 l3 all label "test" label "another label"
diff --git a/sbin/pfctl/tests/files/pf1016.in b/sbin/pfctl/tests/files/pf1016.in
new file mode 100644
index 000000000000..a7b1f6bc0ca9
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1016.in
@@ -0,0 +1 @@
+ether block out on igb0 label "test" ridentifier 12345678
diff --git a/sbin/pfctl/tests/files/pf1016.ok b/sbin/pfctl/tests/files/pf1016.ok
new file mode 100644
index 000000000000..f1d59c988730
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1016.ok
@@ -0,0 +1 @@
+ether block out on igb0 l3 all label "test" ridentifier 12345678
diff --git a/sbin/pfctl/tests/files/pf1017.in b/sbin/pfctl/tests/files/pf1017.in
new file mode 100644
index 000000000000..ad523337bdc5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1017.in
@@ -0,0 +1 @@
+ether block out on igb0 label "test" label "another test" ridentifier 12345678
diff --git a/sbin/pfctl/tests/files/pf1017.ok b/sbin/pfctl/tests/files/pf1017.ok
new file mode 100644
index 000000000000..0efdd55e27a0
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1017.ok
@@ -0,0 +1 @@
+ether block out on igb0 l3 all label "test" label "another test" ridentifier 12345678
diff --git a/sbin/pfctl/tests/pfctl_test_list.inc b/sbin/pfctl/tests/pfctl_test_list.inc
index 0b7d89099efe..7caf66221e2d 100644
--- a/sbin/pfctl/tests/pfctl_test_list.inc
+++ b/sbin/pfctl/tests/pfctl_test_list.inc
@@ -123,3 +123,8 @@ PFCTL_TEST(1009, "Ethernet rule with mask")
 PFCTL_TEST(1010, "POM_STICKYADDRESS test")
 PFCTL_TEST(1011, "Test disabling scrub fragment reassemble")
 PFCTL_TEST(1012, "Test scrub fragment reassemble is default")
+PFCTL_TEST(1013, "Ethernet rule with ridentifier")
+PFCTL_TEST(1014, "Ethernet rule with one label")
+PFCTL_TEST(1015, "Ethernet rule with several labels")
+PFCTL_TEST(1016, "Ethernet rule with ridentifier and one label")
+PFCTL_TEST(1017, "Ethernet rule with ridentifier and several labels")
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index 88c509f36ff7..8292812f7817 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -28,7 +28,7 @@
 .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd April 21, 2023
+.Dd April 26, 2023
 .Dt PF.CONF 5
 .Os
 .Sh NAME
@@ -3108,7 +3108,8 @@ logopts        = logopt [ "," logopts ]
 logopt         = "all" | "user" | "to" interface-name
 
 etherfilteropt-list = etherfilteropt-list etherfilteropt | etherfilteropt
-etherfilteropt = "tag" string | "tagged" string | "queue" ( string )
+etherfilteropt = "tag" string | "tagged" string | "queue" ( string ) |
+                 "ridentifier" number | "label" string
 
 filteropt-list = filteropt-list filteropt | filteropt
 filteropt      = user | group | flags | icmp-type | icmp6-type | "tos" tos |
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 2f017923afa1..a82735f71c8c 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -703,6 +703,9 @@ struct pf_keth_rule {
 	uint8_t			 action;
 	uint16_t		 dnpipe;
 	uint32_t		 dnflags;
+
+	char			label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
+	uint32_t		ridentifier;
 };
 
 union pf_krule_ptr {
diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c
index 544477407861..4391dc0ef8d5 100644
--- a/sys/netpfil/pf/pf_nv.c
+++ b/sys/netpfil/pf/pf_nv.c
@@ -1051,6 +1051,11 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
 	if (nvl == NULL)
 		return (NULL);
 
+	for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) {
+		nvlist_append_string_array(nvl, "labels", krule->label[i]);
+	}
+	nvlist_add_number(nvl, "ridentifier", krule->ridentifier);
+
 	nvlist_add_number(nvl, "nr", krule->nr);
 	nvlist_add_bool(nvl, "quick", krule->quick);
 	nvlist_add_string(nvl, "ifname", krule->ifname);
@@ -1126,8 +1131,29 @@ pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
 {
 	int error = 0;
 
+#define ERROUT(x)	ERROUT_FUNCTION(errout, x)
+
 	bzero(krule, sizeof(*krule));
 
+	if (nvlist_exists_string_array(nvl, "labels")) {
+		const char *const *strs;
+		size_t items;
+		int ret;
+
+		strs = nvlist_get_string_array(nvl, "labels", &items);
+		if (items > PF_RULE_MAX_LABEL_COUNT)
+			ERROUT(E2BIG);
+
+		for (size_t i = 0; i < items; i++) {
+			ret = strlcpy(krule->label[i], strs[i],
+			    sizeof(krule->label[0]));
+			if (ret >= sizeof(krule->label[0]))
+				ERROUT(E2BIG);
+		}
+	}
+
+	PFNV_CHK(pf_nvuint32_opt(nvl, "ridentifier", &krule->ridentifier, 0));
+
 	PFNV_CHK(pf_nvuint32(nvl, "nr", &krule->nr));
 	PFNV_CHK(pf_nvbool(nvl, "quick", &krule->quick));
 	PFNV_CHK(pf_nvstring(nvl, "ifname", krule->ifname,
@@ -1192,6 +1218,7 @@ pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
 	    krule->action != PF_MATCH)
 		return (EBADMSG);
 
+#undef ERROUT
 errout:
 	return (error);
 }