git: 9bb06778f822 - main - pf: support listing ethernet anchors
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 30 Mar 2022 09:17:22 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=9bb06778f822ad6b47d2a825d47e284ca8dd29a1
commit 9bb06778f822ad6b47d2a825d47e284ca8dd29a1
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2022-03-29 12:15:10 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-03-30 08:28:19 +0000
pf: support listing ethernet anchors
Sponsored by: Rubicon Communications, LLC ("Netgate")
---
lib/libpfctl/libpfctl.c | 75 +++++++++++++++++
lib/libpfctl/libpfctl.h | 14 ++++
sbin/pfctl/pfctl.c | 40 ++++++++++
sys/net/pfvar.h | 2 +
sys/netpfil/pf/pf_ioctl.c | 182 ++++++++++++++++++++++++++++++++++++++++++
tests/sys/netpfil/pf/ether.sh | 2 +
6 files changed, 315 insertions(+)
diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 8f064594260b..1e1a90594210 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -622,6 +622,81 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
rule->action = nvlist_get_number(nvl, "action");
}
+int
+pfctl_get_eth_rulesets_info(int dev, struct pfctl_eth_rulesets_info *ri,
+ const char *path)
+{
+ uint8_t buf[1024];
+ struct pfioc_nv nv;
+ nvlist_t *nvl;
+ void *packed;
+ size_t len;
+
+ bzero(ri, sizeof(*ri));
+
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "path", path);
+ packed = nvlist_pack(nvl, &len);
+ memcpy(buf, packed, len);
+ free(packed);
+ nvlist_destroy(nvl);
+
+ nv.data = buf;
+ nv.len = len;
+ nv.size = sizeof(buf);
+
+ if (ioctl(dev, DIOCGETETHRULESETS, &nv) != 0)
+ return (errno);
+
+ nvl = nvlist_unpack(buf, nv.len, 0);
+ if (nvl == NULL)
+ return (EIO);
+
+ ri->nr = nvlist_get_number(nvl, "nr");
+
+ nvlist_destroy(nvl);
+ return (0);
+}
+
+int
+pfctl_get_eth_ruleset(int dev, const char *path, int nr,
+ struct pfctl_eth_ruleset_info *ri)
+{
+ uint8_t buf[1024];
+ struct pfioc_nv nv;
+ nvlist_t *nvl;
+ void *packed;
+ size_t len;
+
+ bzero(ri, sizeof(*ri));
+
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "path", path);
+ nvlist_add_number(nvl, "nr", nr);
+ packed = nvlist_pack(nvl, &len);
+ memcpy(buf, packed, len);
+ free(packed);
+ nvlist_destroy(nvl);
+
+ nv.data = buf;
+ nv.len = len;
+ nv.size = sizeof(buf);
+
+ if (ioctl(dev, DIOCGETETHRULESET, &nv) != 0)
+ return (errno);
+
+ nvl = nvlist_unpack(buf, nv.len, 0);
+ if (nvl == NULL)
+ return (EIO);
+
+ ri->nr = nvlist_get_number(nvl, "nr");
+ strlcpy(ri->path, nvlist_get_string(nvl, "path"), MAXPATHLEN);
+ strlcpy(ri->name, nvlist_get_string(nvl, "name"),
+ PF_ANCHOR_NAME_SIZE);
+
+ return (0);
+}
+
int
pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
const char *path)
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index b7f703b64def..92a1ea9b7cef 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -66,6 +66,10 @@ struct pfctl_status {
uint64_t bcounters[2][2];
};
+struct pfctl_eth_rulesets_info {
+ uint32_t nr;
+};
+
struct pfctl_eth_rules_info {
uint32_t nr;
uint32_t ticket;
@@ -111,6 +115,12 @@ struct pfctl_eth_rule {
};
TAILQ_HEAD(pfctl_eth_rules, pfctl_eth_rule);
+struct pfctl_eth_ruleset_info {
+ uint32_t nr;
+ char name[PF_ANCHOR_NAME_SIZE];
+ char path[MAXPATHLEN];
+};
+
struct pfctl_eth_ruleset {
struct pfctl_eth_rules rules;
struct pfctl_eth_anchor *anchor;
@@ -356,6 +366,10 @@ struct pfctl_syncookies {
struct pfctl_status* pfctl_get_status(int dev);
void pfctl_free_status(struct pfctl_status *status);
+int pfctl_get_eth_rulesets_info(int dev,
+ struct pfctl_eth_rulesets_info *ri, const char *path);
+int pfctl_get_eth_ruleset(int dev, const char *path, int nr,
+ struct pfctl_eth_ruleset_info *ri);
int pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
const char *path);
int pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 88a96bd303a0..67358a325f77 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -111,6 +111,7 @@ int pfctl_show_limits(int, int);
void pfctl_debug(int, u_int32_t, int);
int pfctl_test_altqsupport(int, int);
int pfctl_show_anchors(int, int, char *);
+int pfctl_show_eth_anchors(int, int, char *);
int pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *, bool);
int pfctl_eth_ruleset_trans(struct pfctl *, char *,
struct pfctl_eth_anchor *);
@@ -2604,6 +2605,44 @@ pfctl_show_anchors(int dev, int opts, char *anchorname)
return (0);
}
+int
+pfctl_show_eth_anchors(int dev, int opts, char *anchorname)
+{
+ struct pfctl_eth_rulesets_info ri;
+ struct pfctl_eth_ruleset_info rs;
+ int ret;
+
+ if ((ret = pfctl_get_eth_rulesets_info(dev, &ri, anchorname)) != 0) {
+ if (ret == ENOENT)
+ fprintf(stderr, "Anchor '%s' not found.\n",
+ anchorname);
+ else
+ err(1, "DIOCGETETHRULESETS");
+ return (-1);
+ }
+
+ for (int nr = 0; nr < ri.nr; nr++) {
+ char sub[MAXPATHLEN];
+
+ if (pfctl_get_eth_ruleset(dev, anchorname, nr, &rs) != 0)
+ err(1, "DIOCGETETHRULESET");
+
+ if (!strcmp(rs.name, PF_RESERVED_ANCHOR))
+ continue;
+ sub[0] = 0;
+ if (rs.path[0]) {
+ strlcat(sub, rs.path, sizeof(sub));
+ strlcat(sub, "/", sizeof(sub));
+ }
+ strlcat(sub, rs.name, sizeof(sub));
+ if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
+ printf(" %s\n", sub);
+ if ((opts & PF_OPT_VERBOSE) && pfctl_show_eth_anchors(dev, opts, sub))
+ return (-1);
+ }
+ return (0);
+}
+
const char *
pfctl_lookup_option(char *cmd, const char * const *list)
{
@@ -2830,6 +2869,7 @@ main(int argc, char *argv[])
switch (*showopt) {
case 'A':
pfctl_show_anchors(dev, opts, anchorname);
+ pfctl_show_eth_anchors(dev, opts, anchorname);
break;
case 'r':
pfctl_load_fingerprints(dev, opts);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index ccc81ea137b9..db6b5c22f07f 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1865,6 +1865,8 @@ struct pfioc_iface {
#define DIOCADDETHRULE _IOWR('D', 97, struct pfioc_nv)
#define DIOCGETETHRULE _IOWR('D', 98, struct pfioc_nv)
#define DIOCGETETHRULES _IOWR('D', 99, struct pfioc_nv)
+#define DIOCGETETHRULESETS _IOWR('D', 100, struct pfioc_nv)
+#define DIOCGETETHRULESET _IOWR('D', 101, struct pfioc_nv)
struct pf_ifspeed_v0 {
char ifname[IFNAMSIZ];
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 6012825ead9b..3cb5552d20c5 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2463,6 +2463,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCCLRIFFLAG:
case DIOCGETETHRULES:
case DIOCGETETHRULE:
+ case DIOCGETETHRULESETS:
+ case DIOCGETETHRULESET:
break;
case DIOCRCLRTABLES:
case DIOCRADDTABLES:
@@ -2512,6 +2514,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCGETRULENV:
case DIOCGETETHRULES:
case DIOCGETETHRULE:
+ case DIOCGETETHRULESETS:
+ case DIOCGETETHRULESET:
break;
case DIOCRCLRTABLES:
case DIOCRADDTABLES:
@@ -2864,6 +2868,184 @@ DIOCADDETHRULE_error:
break;
}
+ case DIOCGETETHRULESETS: {
+ struct epoch_tracker et;
+ struct pfioc_nv *nv = (struct pfioc_nv *)addr;
+ nvlist_t *nvl = NULL;
+ void *nvlpacked = NULL;
+ struct pf_keth_ruleset *ruleset;
+ struct pf_keth_anchor *anchor;
+ int nr = 0;
+
+#define ERROUT(x) do { error = (x); goto DIOCGETETHRULESETS_error; } while (0)
+
+ if (nv->len > pf_ioctl_maxcount)
+ ERROUT(ENOMEM);
+
+ nvlpacked = malloc(nv->len, M_NVLIST, M_WAITOK);
+ if (nvlpacked == NULL)
+ ERROUT(ENOMEM);
+
+ error = copyin(nv->data, nvlpacked, nv->len);
+ if (error)
+ ERROUT(error);
+
+ nvl = nvlist_unpack(nvlpacked, nv->len, 0);
+ if (nvl == NULL)
+ ERROUT(EBADMSG);
+ if (! nvlist_exists_string(nvl, "path"))
+ ERROUT(EBADMSG);
+
+ NET_EPOCH_ENTER(et);
+
+ if ((ruleset = pf_find_keth_ruleset(
+ nvlist_get_string(nvl, "path"))) == NULL) {
+ NET_EPOCH_EXIT(et);
+ ERROUT(ENOENT);
+ }
+
+ if (ruleset->anchor == NULL) {
+ RB_FOREACH(anchor, pf_keth_anchor_global, &V_pf_keth_anchors)
+ if (anchor->parent == NULL)
+ nr++;
+ } else {
+ RB_FOREACH(anchor, pf_keth_anchor_node,
+ &ruleset->anchor->children)
+ nr++;
+ }
+
+ NET_EPOCH_EXIT(et);
+
+ nvlist_destroy(nvl);
+ nvl = NULL;
+ free(nvlpacked, M_NVLIST);
+ nvlpacked = NULL;
+
+ nvl = nvlist_create(0);
+ if (nvl == NULL)
+ ERROUT(ENOMEM);
+
+ nvlist_add_number(nvl, "nr", nr);
+
+ nvlpacked = nvlist_pack(nvl, &nv->len);
+ if (nvlpacked == NULL)
+ ERROUT(ENOMEM);
+
+ if (nv->size == 0)
+ ERROUT(0);
+ else if (nv->size < nv->len)
+ ERROUT(ENOSPC);
+
+ error = copyout(nvlpacked, nv->data, nv->len);
+
+#undef ERROUT
+DIOCGETETHRULESETS_error:
+ free(nvlpacked, M_NVLIST);
+ nvlist_destroy(nvl);
+ break;
+ }
+
+ case DIOCGETETHRULESET: {
+ struct epoch_tracker et;
+ struct pfioc_nv *nv = (struct pfioc_nv *)addr;
+ nvlist_t *nvl = NULL;
+ void *nvlpacked = NULL;
+ struct pf_keth_ruleset *ruleset;
+ struct pf_keth_anchor *anchor;
+ int nr = 0, req_nr = 0;
+ bool found = false;
+
+#define ERROUT(x) do { error = (x); goto DIOCGETETHRULESET_error; } while (0)
+
+ if (nv->len > pf_ioctl_maxcount)
+ ERROUT(ENOMEM);
+
+ nvlpacked = malloc(nv->len, M_NVLIST, M_WAITOK);
+ if (nvlpacked == NULL)
+ ERROUT(ENOMEM);
+
+ error = copyin(nv->data, nvlpacked, nv->len);
+ if (error)
+ ERROUT(error);
+
+ nvl = nvlist_unpack(nvlpacked, nv->len, 0);
+ if (nvl == NULL)
+ ERROUT(EBADMSG);
+ if (! nvlist_exists_string(nvl, "path"))
+ ERROUT(EBADMSG);
+ if (! nvlist_exists_number(nvl, "nr"))
+ ERROUT(EBADMSG);
+
+ req_nr = nvlist_get_number(nvl, "nr");
+
+ NET_EPOCH_ENTER(et);
+
+ if ((ruleset = pf_find_keth_ruleset(
+ nvlist_get_string(nvl, "path"))) == NULL) {
+ NET_EPOCH_EXIT(et);
+ ERROUT(ENOENT);
+ }
+
+ nvlist_destroy(nvl);
+ nvl = NULL;
+ free(nvlpacked, M_NVLIST);
+ nvlpacked = NULL;
+
+ nvl = nvlist_create(0);
+ if (nvl == NULL) {
+ NET_EPOCH_EXIT(et);
+ ERROUT(ENOMEM);
+ }
+
+ if (ruleset->anchor == NULL) {
+ RB_FOREACH(anchor, pf_keth_anchor_global,
+ &V_pf_keth_anchors) {
+ if (anchor->parent == NULL && nr++ == req_nr) {
+ found = true;
+ break;
+ }
+ }
+ } else {
+ RB_FOREACH(anchor, pf_keth_anchor_node,
+ &ruleset->anchor->children) {
+ if (nr++ == req_nr) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ NET_EPOCH_EXIT(et);
+ if (found) {
+ nvlist_add_number(nvl, "nr", nr);
+ nvlist_add_string(nvl, "name", anchor->name);
+ if (ruleset->anchor)
+ nvlist_add_string(nvl, "path",
+ ruleset->anchor->path);
+ else
+ nvlist_add_string(nvl, "path", "");
+ } else {
+ ERROUT(EBUSY);
+ }
+
+ nvlpacked = nvlist_pack(nvl, &nv->len);
+ if (nvlpacked == NULL)
+ ERROUT(ENOMEM);
+
+ if (nv->size == 0)
+ ERROUT(0);
+ else if (nv->size < nv->len)
+ ERROUT(ENOSPC);
+
+ error = copyout(nvlpacked, nv->data, nv->len);
+
+#undef ERROUT
+DIOCGETETHRULESET_error:
+ free(nvlpacked, M_NVLIST);
+ nvlist_destroy(nvl);
+ break;
+ }
+
case DIOCADDRULENV: {
struct pfioc_nv *nv = (struct pfioc_nv *)addr;
nvlist_t *nvl = NULL;
diff --git a/tests/sys/netpfil/pf/ether.sh b/tests/sys/netpfil/pf/ether.sh
index 6e81f07ca26e..7a7f91844148 100644
--- a/tests/sys/netpfil/pf/ether.sh
+++ b/tests/sys/netpfil/pf/ether.sh
@@ -490,6 +490,8 @@ anchor_body()
"}" \
"ether pass in from ${epair_a_mac}"
atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ atf_check -s exit:0 -o match:'baz' jexec alcatraz pfctl -sA
}
anchor_cleanup()