pf: support listing ethernet anchors

Sponsored by:	Rubicon Communications, LLC ("Netgate")
This commit is contained in:
Kristof Provost 2022-03-29 14:15:10 +02:00
parent 5473dee730
commit 9bb06778f8
6 changed files with 315 additions and 0 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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);

View File

@ -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];

View File

@ -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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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;

View File

@ -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()