From ef661d4a5bf912e4d4850faaf50664532d82541c Mon Sep 17 00:00:00 2001 From: Christian McDonald Date: Mon, 24 Apr 2023 14:55:34 -0400 Subject: [PATCH] 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(-) create mode 100644 sbin/pfctl/tests/files/pf1013.in create mode 100644 sbin/pfctl/tests/files/pf1013.ok create mode 100644 sbin/pfctl/tests/files/pf1014.in create mode 100644 sbin/pfctl/tests/files/pf1014.ok create mode 100644 sbin/pfctl/tests/files/pf1015.in create mode 100644 sbin/pfctl/tests/files/pf1015.ok create mode 100644 sbin/pfctl/tests/files/pf1016.in create mode 100644 sbin/pfctl/tests/files/pf1016.ok create mode 100644 sbin/pfctl/tests/files/pf1017.in create mode 100644 sbin/pfctl/tests/files/pf1017.ok 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); }