git: c5131afee39b - main - pf: add anchor support for ether rules
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 02 Mar 2022 16:01:01 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=c5131afee39b4fa9e3889deb2ceea35a43ef35e2
commit c5131afee39b4fa9e3889deb2ceea35a43ef35e2
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2021-10-01 17:05:50 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-03-02 16:00:07 +0000
pf: add anchor support for ether rules
Support anchors in ether rules.
Sponsored by: Rubicon Communications, LLC ("Netgate")
Differential Revision: https://reviews.freebsd.org/D32482
---
lib/libpfctl/libpfctl.c | 31 ++++-
lib/libpfctl/libpfctl.h | 28 +++-
sbin/pfctl/parse.y | 119 +++++++++++++++--
sbin/pfctl/pf_ruleset.c | 197 +++++++++++++++++++++++++++-
sbin/pfctl/pfctl.c | 254 +++++++++++++++++++++++++++++++-----
sbin/pfctl/pfctl.h | 9 ++
sbin/pfctl/pfctl_parser.c | 13 +-
sbin/pfctl/pfctl_parser.h | 7 +-
sys/net/pfvar.h | 82 ++++++++++--
sys/netpfil/pf/pf.c | 136 ++++++++++++++++++--
sys/netpfil/pf/pf_ioctl.c | 228 ++++++++++++++++++++------------
sys/netpfil/pf/pf_nv.c | 3 +
sys/netpfil/pf/pf_ruleset.c | 307 +++++++++++++++++++++++++++++++++++++++++++-
13 files changed, 1247 insertions(+), 167 deletions(-)
diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index fd7dd7a474a0..90733d421572 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -606,20 +606,34 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
rule->dnpipe = nvlist_get_number(nvl, "dnpipe");
rule->dnflags = nvlist_get_number(nvl, "dnflags");
+ rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
+ rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard");
+
rule->action = nvlist_get_number(nvl, "action");
}
int
-pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules)
+pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
+ const char *path)
{
uint8_t buf[1024];
struct pfioc_nv nv;
nvlist_t *nvl;
+ void *packed;
+ size_t len;
bzero(rules, sizeof(*rules));
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "anchor", path);
+ packed = nvlist_pack(nvl, &len);
+ memcpy(buf, packed, len);
+ free(packed);
+ nvlist_destroy(nvl);
+
nv.data = buf;
- nv.len = nv.size = sizeof(buf);
+ nv.len = len;
+ nv.size = sizeof(buf);
if (ioctl(dev, DIOCGETETHRULES, &nv) != 0)
return (errno);
@@ -637,7 +651,8 @@ pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules)
int
pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
- struct pfctl_eth_rule *rule, bool clear)
+ const char *path, struct pfctl_eth_rule *rule, bool clear,
+ char *anchor_call)
{
uint8_t buf[1024];
struct pfioc_nv nv;
@@ -647,6 +662,7 @@ pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "anchor", path);
nvlist_add_number(nvl, "ticket", ticket);
nvlist_add_number(nvl, "nr", nr);
nvlist_add_bool(nvl, "clear", clear);
@@ -670,12 +686,17 @@ pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
pfctl_nveth_rule_to_eth_rule(nvl, rule);
+ if (anchor_call)
+ strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"),
+ MAXPATHLEN);
+
nvlist_destroy(nvl);
return (0);
}
int
-pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, uint32_t ticket)
+pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
+ const char *anchor_call, uint32_t ticket)
{
struct pfioc_nv nv;
nvlist_t *nvl, *addr;
@@ -686,6 +707,8 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, uint32_t ticket)
nvl = nvlist_create(0);
nvlist_add_number(nvl, "ticket", ticket);
+ nvlist_add_string(nvl, "anchor", anchor);
+ nvlist_add_string(nvl, "anchor_call", anchor_call);
nvlist_add_number(nvl, "nr", r->nr);
nvlist_add_bool(nvl, "quick", r->quick);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 6c3dbfc5d0df..256fa49c4f25 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -37,6 +37,7 @@
#include <netpfil/pf/pf.h>
struct pfctl_anchor;
+struct pfctl_eth_anchor;
struct pfctl_status_counter {
uint64_t id;
@@ -100,11 +101,28 @@ struct pfctl_eth_rule {
uint32_t dnflags;
uint8_t action;
+ struct pfctl_eth_anchor *anchor;
+ uint8_t anchor_relative;
+ uint8_t anchor_wildcard;
+
TAILQ_ENTRY(pfctl_eth_rule) entries;
};
-
TAILQ_HEAD(pfctl_eth_rules, pfctl_eth_rule);
+struct pfctl_eth_ruleset {
+ struct pfctl_eth_rules rules;
+ struct pfctl_eth_anchor *anchor;
+};
+
+struct pfctl_eth_anchor {
+ struct pfctl_eth_anchor *parent;
+ char name[PF_ANCHOR_NAME_SIZE];
+ char path[MAXPATHLEN];
+ struct pfctl_eth_ruleset ruleset;
+ int refcnt; /* anchor rules */
+ int match; /* XXX: used for pfctl black magic */
+};
+
struct pfctl_pool {
struct pf_palist list;
struct pf_pooladdr *cur;
@@ -331,11 +349,13 @@ struct pfctl_syncookies {
struct pfctl_status* pfctl_get_status(int dev);
void pfctl_free_status(struct pfctl_status *status);
-int pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules);
+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,
- struct pfctl_eth_rule *rule, bool clear);
+ const char *path, struct pfctl_eth_rule *rule, bool clear,
+ char *anchor_call);
int pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r,
- uint32_t ticket);
+ const char *anchor, const char *anchor_call, uint32_t ticket);
int pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket,
const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
char *anchor_call);
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index b856621ecf41..5f10c4ab2e17 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -350,7 +350,7 @@ void expand_label_nr(const char *, char *, size_t,
struct pfctl_rule *);
void expand_eth_rule(struct pfctl_eth_rule *,
struct node_if *, struct node_etherproto *,
- struct node_mac *, struct node_mac *);
+ struct node_mac *, struct node_mac *, const char *);
void expand_rule(struct pfctl_rule *, struct node_if *,
struct node_host *, struct node_proto *, struct node_os *,
struct node_host *, struct node_port *, struct node_host *,
@@ -370,6 +370,7 @@ int rule_label(struct pfctl_rule *, char *s[PF_RULE_MAX_LABEL_COUNT]);
int rt_tableid_max(void);
void mv_rules(struct pfctl_ruleset *, struct pfctl_ruleset *);
+void mv_eth_rules(struct pfctl_eth_ruleset *, struct pfctl_eth_ruleset *);
void decide_address_family(struct node_host *, sa_family_t *);
void remove_invalid_hosts(struct node_host **, sa_family_t *);
int invalid_redirect(struct node_host *, sa_family_t);
@@ -566,6 +567,7 @@ ruleset : /* empty */
| ruleset '\n'
| ruleset option '\n'
| ruleset etherrule '\n'
+ | ruleset etheranchorrule '\n'
| ruleset scrubrule '\n'
| ruleset natrule '\n'
| ruleset binatrule '\n'
@@ -1196,7 +1198,95 @@ etherrule : ETHER action dir quick interface etherproto etherfromto etherfilter_
r.dnpipe = $8.dnpipe;
r.dnflags = $8.free_flags;
- expand_eth_rule(&r, $5, $6, $7.src, $7.dst);
+ expand_eth_rule(&r, $5, $6, $7.src, $7.dst, "");
+ }
+ ;
+
+etherpfa_anchorlist : /* empty */
+ | etherpfa_anchorlist '\n'
+ | etherpfa_anchorlist etherrule '\n'
+ | etherpfa_anchorlist etheranchorrule '\n'
+ ;
+
+etherpfa_anchor : '{'
+ {
+ char ta[PF_ANCHOR_NAME_SIZE];
+ struct pfctl_eth_ruleset *rs;
+
+ /* steping into a brace anchor */
+ pf->asd++;
+ pf->bn++;
+
+ /* create a holding ruleset in the root */
+ snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn);
+ rs = pf_find_or_create_eth_ruleset(ta);
+ if (rs == NULL)
+ err(1, "etherpfa_anchor: pf_find_or_create_eth_ruleset");
+ pf->eastack[pf->asd] = rs->anchor;
+ pf->eanchor = rs->anchor;
+ } '\n' etherpfa_anchorlist '}'
+ {
+ pf->ealast = pf->eanchor;
+ pf->asd--;
+ pf->eanchor = pf->eastack[pf->asd];
+ }
+ | /* empty */
+ ;
+
+etheranchorrule : ETHER ANCHOR anchorname dir quick interface etherproto etherfromto etherpfa_anchor
+ {
+ struct pfctl_eth_rule r;
+
+ if (check_rulestate(PFCTL_STATE_ETHER)) {
+ free($3);
+ YYERROR;
+ }
+
+ if ($3 && ($3[0] == '_' || strstr($3, "/_") != NULL)) {
+ free($3);
+ yyerror("anchor names beginning with '_' "
+ "are reserved for internal use");
+ YYERROR;
+ }
+
+ memset(&r, 0, sizeof(r));
+ if (pf->eastack[pf->asd + 1]) {
+ /* move inline rules into relative location */
+ pfctl_eth_anchor_setup(pf, &r,
+ &pf->eastack[pf->asd]->ruleset,
+ $3 ? $3 : pf->ealast->name);
+ if (r.anchor == NULL)
+ err(1, "etheranchorrule: unable to "
+ "create ruleset");
+
+ if (pf->ealast != r.anchor) {
+ if (r.anchor->match) {
+ yyerror("inline anchor '%s' "
+ "already exists",
+ r.anchor->name);
+ YYERROR;
+ }
+ mv_eth_rules(&pf->ealast->ruleset,
+ &r.anchor->ruleset);
+ }
+ pf_remove_if_empty_eth_ruleset(&pf->ealast->ruleset);
+ pf->ealast = r.anchor;
+ } else {
+ if (!$3) {
+ yyerror("anchors without explicit "
+ "rules must specify a name");
+ YYERROR;
+ }
+ }
+
+ r.direction = $4;
+ r.quick = $5.quick;
+
+ expand_eth_rule(&r, $6, $7, $8.src, $8.dst,
+ pf->eastack[pf->asd + 1] ? pf->ealast->name : $3);
+
+ free($3);
+ pf->eastack[pf->asd + 1] = NULL;
}
;
@@ -5640,15 +5730,12 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces,
void
expand_eth_rule(struct pfctl_eth_rule *r,
struct node_if *interfaces, struct node_etherproto *protos,
- struct node_mac *srcs, struct node_mac *dsts)
+ struct node_mac *srcs, struct node_mac *dsts, const char *anchor_call)
{
- struct pfctl_eth_rule *rule;
-
LOOP_THROUGH(struct node_if, interface, interfaces,
LOOP_THROUGH(struct node_etherproto, proto, protos,
LOOP_THROUGH(struct node_mac, src, srcs,
LOOP_THROUGH(struct node_mac, dst, dsts,
- r->nr = pf->eth_nr++;
strlcpy(r->ifname, interface->ifname,
sizeof(r->ifname));
r->ifnot = interface->not;
@@ -5657,12 +5744,9 @@ expand_eth_rule(struct pfctl_eth_rule *r,
r->src.neg = src->neg;
bcopy(dst->mac, r->dst.addr, ETHER_ADDR_LEN);
r->dst.neg = dst->neg;
+ r->nr = pf->eastack[pf->asd]->match++;
- if ((rule = calloc(1, sizeof(*rule))) == NULL)
- err(1, "calloc");
- bcopy(r, rule, sizeof(*rule));
-
- TAILQ_INSERT_TAIL(&pf->eth_rules, rule, entries);
+ pfctl_append_eth_rule(pf, r, anchor_call);
))));
FREE_LIST(struct node_if, interfaces);
@@ -6525,6 +6609,19 @@ mv_rules(struct pfctl_ruleset *src, struct pfctl_ruleset *dst)
}
}
+void
+mv_eth_rules(struct pfctl_eth_ruleset *src, struct pfctl_eth_ruleset *dst)
+{
+ struct pfctl_eth_rule *r;
+
+ while ((r = TAILQ_FIRST(&src->rules)) != NULL) {
+ TAILQ_REMOVE(&src->rules, r, entries);
+ TAILQ_INSERT_TAIL(&dst->rules, r, entries);
+ dst->anchor->match++;
+ }
+ src->anchor->match = 0;
+}
+
void
decide_address_family(struct node_host *n, sa_family_t *af)
{
diff --git a/sbin/pfctl/pf_ruleset.c b/sbin/pfctl/pf_ruleset.c
index 480e0f0c9b45..a7f31366f48c 100644
--- a/sbin/pfctl/pf_ruleset.c
+++ b/sbin/pfctl/pf_ruleset.c
@@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$");
#define rs_free(x) free(x)
#include "pfctl.h"
+#include "pfctl_parser.h"
#ifdef PFDEBUG
#include <sys/stdarg.h>
@@ -74,7 +75,8 @@ __FBSDID("$FreeBSD$");
#endif /* PFDEBUG */
struct pfctl_anchor_global pf_anchors;
-struct pfctl_anchor pf_main_anchor;
+extern struct pfctl_anchor pf_main_anchor;
+extern struct pfctl_eth_anchor pf_eth_main_anchor;
#undef V_pf_anchors
#define V_pf_anchors pf_anchors
#undef pf_main_ruleset
@@ -290,6 +292,148 @@ pf_remove_if_empty_ruleset(struct pfctl_ruleset *ruleset)
ruleset = &parent->ruleset;
}
}
+
+void
+pf_remove_if_empty_eth_ruleset(struct pfctl_eth_ruleset *ruleset)
+{
+ struct pfctl_eth_anchor *parent;
+
+ return;
+ while (ruleset != NULL) {
+ if (ruleset == &pf_eth_main_anchor.ruleset ||
+ ruleset->anchor == NULL || ruleset->anchor->refcnt > 0)
+ return;
+ if (!TAILQ_EMPTY(&ruleset->rules))
+ return;
+ rs_free(ruleset->anchor);
+ if (parent == NULL)
+ return;
+ ruleset = &parent->ruleset;
+ }
+}
+
+void
+pf_init_eth_ruleset(struct pfctl_eth_ruleset *ruleset)
+{
+
+ memset(ruleset, 0, sizeof(*ruleset));
+ TAILQ_INIT(&ruleset->rules);
+}
+
+
+static struct pfctl_eth_anchor*
+_pf_find_eth_anchor(struct pfctl_eth_anchor *anchor, const char *path)
+{
+ struct pfctl_eth_rule *r;
+ struct pfctl_eth_anchor *a;
+
+ if (strcmp(path, anchor->path) == 0)
+ return (anchor);
+
+ TAILQ_FOREACH(r, &anchor->ruleset.rules, entries) {
+ if (! r->anchor)
+ continue;
+
+ /* Step into anchor */
+ a = _pf_find_eth_anchor(r->anchor, path);
+ if (a)
+ return (a);
+ }
+
+ return (NULL);
+}
+
+static struct pfctl_eth_anchor*
+pf_find_eth_anchor(const char *path)
+{
+ return (_pf_find_eth_anchor(&pf_eth_main_anchor, path));
+}
+
+static struct pfctl_eth_ruleset*
+pf_find_eth_ruleset(const char *path)
+{
+ struct pfctl_eth_anchor *anchor;
+
+ while (*path == '/')
+ path++;
+ if (!*path)
+ return (&pf_eth_main_anchor.ruleset);
+ anchor = pf_find_eth_anchor(path);
+ if (anchor == NULL)
+ return (NULL);
+ else
+ return (&anchor->ruleset);
+}
+
+struct pfctl_eth_ruleset *
+pf_find_or_create_eth_ruleset(const char *path)
+{
+ char *p, *q, *r;
+ struct pfctl_eth_ruleset *ruleset;
+ struct pfctl_eth_anchor *anchor = NULL, *parent = NULL;
+
+ if (path[0] == 0)
+ return (&pf_eth_main_anchor.ruleset);
+ while (*path == '/')
+ path++;
+ ruleset = pf_find_eth_ruleset(path);
+ if (ruleset != NULL)
+ return (ruleset);
+ p = (char *)rs_malloc(MAXPATHLEN);
+ if (p == NULL)
+ return (NULL);
+ strlcpy(p, path, MAXPATHLEN);
+ while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
+ *q = 0;
+ if ((ruleset = pf_find_eth_ruleset(p)) != NULL) {
+ parent = ruleset->anchor;
+ break;
+ }
+ }
+ if (q == NULL)
+ q = p;
+ else
+ q++;
+ strlcpy(p, path, MAXPATHLEN);
+ if (!*q) {
+ rs_free(p);
+ return (NULL);
+ }
+ while ((r = strchr(q, '/')) != NULL || *q) {
+ if (r != NULL)
+ *r = 0;
+ if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
+ (parent != NULL && strlen(parent->path) >=
+ MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
+ rs_free(p);
+ return (NULL);
+ }
+ anchor = (struct pfctl_eth_anchor *)rs_malloc(sizeof(*anchor));
+ if (anchor == NULL) {
+ rs_free(p);
+ return (NULL);
+ }
+ strlcpy(anchor->name, q, sizeof(anchor->name));
+ if (parent != NULL) {
+ strlcpy(anchor->path, parent->path,
+ sizeof(anchor->path));
+ strlcat(anchor->path, "/", sizeof(anchor->path));
+ }
+ strlcat(anchor->path, anchor->name, sizeof(anchor->path));
+ if (parent != NULL)
+ anchor->parent = parent;
+ pf_init_eth_ruleset(&anchor->ruleset);
+ anchor->ruleset.anchor = anchor;
+ parent = anchor;
+ if (r != NULL)
+ q = r + 1;
+ else
+ *q = 0;
+ }
+ rs_free(p);
+ return (&anchor->ruleset);
+}
+
int
pfctl_anchor_setup(struct pfctl_rule *r, const struct pfctl_ruleset *s,
const char *name)
@@ -345,3 +489,54 @@ pfctl_anchor_setup(struct pfctl_rule *r, const struct pfctl_ruleset *s,
r->anchor->refcnt++;
return (0);
}
+
+int
+pfctl_eth_anchor_setup(struct pfctl *pf, struct pfctl_eth_rule *r,
+ const struct pfctl_eth_ruleset *s, const char *name)
+{
+ char *p, *path;
+ struct pfctl_eth_ruleset *ruleset;
+
+ r->anchor = NULL;
+ if (!name[0])
+ return (0);
+ path = (char *)rs_malloc(MAXPATHLEN);
+ if (path == NULL)
+ return (1);
+ if (name[0] == '/')
+ strlcpy(path, name + 1, MAXPATHLEN);
+ else {
+ /* relative path */
+ if (s->anchor == NULL || !s->anchor->path[0])
+ path[0] = 0;
+ else
+ strlcpy(path, s->anchor->path, MAXPATHLEN);
+ while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
+ if (!path[0]) {
+ printf("%s: .. beyond root\n", __func__);
+ rs_free(path);
+ return (1);
+ }
+ if ((p = strrchr(path, '/')) != NULL)
+ *p = 0;
+ else
+ path[0] = 0;
+ name += 3;
+ }
+ if (path[0])
+ strlcat(path, "/", MAXPATHLEN);
+ strlcat(path, name, MAXPATHLEN);
+ }
+ if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
+ *p = 0;
+ }
+ ruleset = pf_find_or_create_eth_ruleset(path);
+ rs_free(path);
+ if (ruleset == NULL || ruleset->anchor == NULL) {
+ printf("%s: ruleset\n", __func__);
+ return (1);
+ }
+ r->anchor = ruleset->anchor;
+ r->anchor->refcnt++;
+ return (0);
+}
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index f825ef834ac4..bec37b0bf85f 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -98,7 +98,7 @@ int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int,
char *);
void pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int);
void pfctl_print_rule_counters(struct pfctl_rule *, int);
-int pfctl_show_eth_rules(int, int, enum pfctl_show);
+int pfctl_show_eth_rules(int, char *, int, enum pfctl_show, char *, int);
int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int);
int pfctl_show_nat(int, int, char *);
int pfctl_show_src_nodes(int, int);
@@ -110,15 +110,21 @@ 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_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *);
-int pfctl_load_eth_ruleset(struct pfctl *);
+int pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *, bool);
+int pfctl_eth_ruleset_trans(struct pfctl *, char *,
+ struct pfctl_eth_anchor *);
+int pfctl_load_eth_ruleset(struct pfctl *, char *,
+ struct pfctl_eth_ruleset *, int);
+int pfctl_load_eth_rule(struct pfctl *, char *, struct pfctl_eth_rule *,
+ int);
int pfctl_load_ruleset(struct pfctl *, char *,
struct pfctl_ruleset *, int, int);
int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int);
const char *pfctl_lookup_option(char *, const char * const *);
static struct pfctl_anchor_global pf_anchors;
-static struct pfctl_anchor pf_main_anchor;
+struct pfctl_anchor pf_main_anchor;
+struct pfctl_eth_anchor pf_eth_main_anchor;
static struct pfr_buffer skip_b;
static const char *clearopt;
@@ -1052,31 +1058,66 @@ pfctl_print_title(char *title)
}
int
-pfctl_show_eth_rules(int dev, int opts, enum pfctl_show format)
+pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
+ char *anchorname, int depth)
{
+ char anchor_call[MAXPATHLEN];
struct pfctl_eth_rules_info info;
struct pfctl_eth_rule rule;
int dotitle = opts & PF_OPT_SHOWALL;
+ int len = strlen(path);
+ int brace;
+ char *p;
+
+ if (path[0])
+ snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
+ else
+ snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
- if (pfctl_get_eth_rules_info(dev, &info)) {
+ if (pfctl_get_eth_rules_info(dev, &info, path)) {
warn("DIOCGETETHRULES");
return (-1);
}
for (int nr = 0; nr < info.nr; nr++) {
- if (pfctl_get_eth_rule(dev, nr, info.ticket, &rule,
- opts & PF_OPT_CLRRULECTRS) != 0) {
+ brace = 0;
+ INDENT(depth, !(opts & PF_OPT_VERBOSE));
+ if (pfctl_get_eth_rule(dev, nr, info.ticket, path, &rule,
+ opts & PF_OPT_CLRRULECTRS, anchor_call) != 0) {
warn("DIOCGETETHRULE");
return (-1);
}
+ if (anchor_call[0] &&
+ ((((p = strrchr(anchor_call, '_')) != NULL) &&
+ (p == anchor_call ||
+ *(--p) == '/')) || (opts & PF_OPT_RECURSE))) {
+ brace++;
+ if ((p = strrchr(anchor_call, '/')) !=
+ NULL)
+ p++;
+ else
+ p = &anchor_call[0];
+ } else
+ p = &anchor_call[0];
if (dotitle) {
pfctl_print_title("ETH RULES:");
dotitle = 0;
}
- print_eth_rule(&rule, opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG));
- printf("\n");
+ print_eth_rule(&rule, anchor_call,
+ opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG));
+ if (brace)
+ printf(" {\n");
+ else
+ printf("\n");
pfctl_print_eth_rule_counters(&rule, opts);
+ if (brace) {
+ pfctl_show_eth_rules(dev, path, opts, format,
+ p, depth + 1);
+ INDENT(depth, !(opts & PF_OPT_VERBOSE));
+ printf("}\n");
+ }
}
+ path[len] = '\0';
return (0);
}
@@ -1508,15 +1549,70 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
}
int
-pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a)
+pfctl_append_eth_rule(struct pfctl *pf, struct pfctl_eth_rule *r,
+ const char *anchor_call)
+{
+ struct pfctl_eth_rule *rule;
+ struct pfctl_eth_ruleset *rs;
+ char *p;
+
+ rs = &pf->eanchor->ruleset;
+
+ if (anchor_call[0] && r->anchor == NULL) {
+ /*
+ * Don't make non-brace anchors part of the main anchor pool.
+ */
+ if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL)
+ err(1, "pfctl_append_rule: calloc");
+
+ pf_init_eth_ruleset(&r->anchor->ruleset);
+ r->anchor->ruleset.anchor = r->anchor;
+ if (strlcpy(r->anchor->path, anchor_call,
+ sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path))
+ errx(1, "pfctl_append_rule: strlcpy");
+ if ((p = strrchr(anchor_call, '/')) != NULL) {
+ if (!strlen(p))
+ err(1, "pfctl_append_eth_rule: bad anchor name %s",
+ anchor_call);
+ } else
+ p = (char *)anchor_call;
+ if (strlcpy(r->anchor->name, p,
+ sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name))
+ errx(1, "pfctl_append_eth_rule: strlcpy");
+ }
+
+ if ((rule = calloc(1, sizeof(*rule))) == NULL)
+ err(1, "calloc");
+ bcopy(r, rule, sizeof(*rule));
+
+ TAILQ_INSERT_TAIL(&rs->rules, rule, entries);
+ return (0);
+}
+
+int
+pfctl_eth_ruleset_trans(struct pfctl *pf, char *path,
+ struct pfctl_eth_anchor *a)
{
int osize = pf->trans->pfrb_size;
if ((pf->loadopt & PFCTL_FLAG_ETH) != 0) {
- if (! path[0]) {
- if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
- return (1);
- }
+ if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
+ return (1);
+ }
+ if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize))
+ return (5);
+
+ return (0);
+}
+
+int
+pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a, bool do_eth)
+{
+ int osize = pf->trans->pfrb_size;
+
+ if ((pf->loadopt & PFCTL_FLAG_ETH) != 0 && do_eth) {
+ if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
+ return (1);
}
if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) {
if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) ||
@@ -1544,22 +1640,92 @@ pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a)
}
int
-pfctl_load_eth_ruleset(struct pfctl *pf)
+pfctl_load_eth_ruleset(struct pfctl *pf, char *path,
+ struct pfctl_eth_ruleset *rs, int depth)
{
struct pfctl_eth_rule *r;
- int error;
+ int error, len = strlen(path);
+ int brace = 0;
- while ((r = TAILQ_FIRST(&pf->eth_rules)) != NULL) {
- TAILQ_REMOVE(&pf->eth_rules, r, entries);
+ pf->eanchor = rs->anchor;
+ if (path[0])
+ snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->eanchor->name);
+ else
+ snprintf(&path[len], MAXPATHLEN - len, "%s", pf->eanchor->name);
- if ((pf->opts & PF_OPT_NOACTION) == 0) {
- error = pfctl_add_eth_rule(pf->dev, r, pf->eth_ticket);
- if (error)
+ if (depth) {
+ if (TAILQ_FIRST(&rs->rules) != NULL) {
+ brace++;
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf(" {\n");
+ if ((pf->opts & PF_OPT_NOACTION) == 0 &&
+ (error = pfctl_eth_ruleset_trans(pf,
+ path, rs->anchor))) {
+ printf("pfctl_load_eth_rulesets: "
+ "pfctl_eth_ruleset_trans %d\n", error);
+ goto error;
+ }
+ } else if (pf->opts & PF_OPT_VERBOSE)
+ printf("\n");
+ }
+
+ while ((r = TAILQ_FIRST(&rs->rules)) != NULL) {
+ TAILQ_REMOVE(&rs->rules, r, entries);
+
+ error = pfctl_load_eth_rule(pf, path, r, depth);
+ if (error)
+ return (error);
+
+ if (r->anchor) {
+ if ((error = pfctl_load_eth_ruleset(pf, path,
+ &r->anchor->ruleset, depth + 1)))
return (error);
}
-
free(r);
}
+ if (brace && pf->opts & PF_OPT_VERBOSE) {
+ INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE));
+ printf("}\n");
+ }
+ path[len] = '\0';
+
+ return (0);
+error:
+ path[len] = '\0';
+ return (error);
+}
+
+int
+pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r,
+ int depth)
+{
+ char *name;
+ char anchor[PF_ANCHOR_NAME_SIZE];
+ int len = strlen(path);
+
+ if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
+ errx(1, "pfctl_load_eth_rule: strlcpy");
+
+ if (r->anchor) {
+ if (r->anchor->match) {
+ if (path[0])
+ snprintf(&path[len], MAXPATHLEN - len,
+ "/%s", r->anchor->name);
+ else
+ snprintf(&path[len], MAXPATHLEN - len,
+ "%s", r->anchor->name);
+ name = r->anchor->name;
+ } else
+ name = r->anchor->path;
+ } else
+ name = "";
+
+ if ((pf->opts & PF_OPT_NOACTION) == 0)
+ if (pfctl_add_eth_rule(pf->dev, r, anchor, name,
+ pf->eth_ticket))
+ err(1, "DIOCADDETHRULENV");
+
+ path[len] = '\0';
return (0);
}
@@ -1586,7 +1752,7 @@ pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
printf(" {\n");
if ((pf->opts & PF_OPT_NOACTION) == 0 &&
(error = pfctl_ruleset_trans(pf,
- path, rs->anchor))) {
+ path, rs->anchor, false))) {
printf("pfctl_load_rulesets: "
"pfctl_ruleset_trans %d\n", error);
goto error;
@@ -1711,6 +1877,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
struct pfioc_altq pa;
struct pfctl pf;
struct pfctl_ruleset *rs;
+ struct pfctl_eth_ruleset *ethrs;
struct pfr_table trs;
char *path;
int osize;
@@ -1719,6 +1886,11 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
pf_init_ruleset(&pf_main_anchor.ruleset);
pf_main_anchor.ruleset.anchor = &pf_main_anchor;
+
+ memset(&pf_eth_main_anchor, 0, sizeof(pf_eth_main_anchor));
+ pf_init_eth_ruleset(&pf_eth_main_anchor.ruleset);
+ pf_eth_main_anchor.ruleset.anchor = &pf_eth_main_anchor;
+
if (trans == NULL) {
bzero(&buf, sizeof(buf));
buf.pfrb_type = PFRB_TRANS;
@@ -1742,7 +1914,6 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
pf.opts = opts;
pf.optimize = optimize;
pf.loadopt = loadopt;
- TAILQ_INIT(&pf.eth_rules);
/* non-brace anchor, create without resolving the path */
if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL)
@@ -1752,10 +1923,10 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
rs->anchor = pf.anchor;
if (strlcpy(pf.anchor->path, anchorname,
sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path))
- errx(1, "pfctl_add_rule: strlcpy");
+ errx(1, "pfctl_rules: strlcpy");
if (strlcpy(pf.anchor->name, anchorname,
sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name))
- errx(1, "pfctl_add_rule: strlcpy");
+ errx(1, "pfctl_rules: strlcpy");
pf.astack[0] = pf.anchor;
@@ -1766,13 +1937,29 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
pf.trans = t;
pfctl_init_options(&pf);
+ /* Set up ethernet anchor */
+ if ((pf.eanchor = calloc(1, sizeof(*pf.eanchor))) == NULL)
+ ERRX("pfctl_rules: calloc");
+
+ if (strlcpy(pf.eanchor->path, anchorname,
+ sizeof(pf.eanchor->path)) >= sizeof(pf.eanchor->path))
+ errx(1, "pfctl_rules: strlcpy");
+ if (strlcpy(pf.eanchor->name, anchorname,
+ sizeof(pf.eanchor->name)) >= sizeof(pf.eanchor->name))
+ errx(1, "pfctl_rules: strlcpy");
+
+ ethrs = &pf.eanchor->ruleset;
+ pf_init_eth_ruleset(ethrs);
+ ethrs->anchor = pf.eanchor;
+ pf.eastack[0] = pf.eanchor;
+
if ((opts & PF_OPT_NOACTION) == 0) {
/*
* XXX For the time being we need to open transactions for
* the main ruleset before parsing, because tables are still
* loaded at parse time.
*/
- if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor))
+ if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor, true))
ERRX("pfctl_rules");
if (pf.loadopt & PFCTL_FLAG_ETH)
pf.eth_ticket = pfctl_get_ticket(t, PF_RULESET_ETH, anchorname);
@@ -1797,7 +1984,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
if ((pf.loadopt & PFCTL_FLAG_FILTER &&
(pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) ||
(pf.loadopt & PFCTL_FLAG_ETH &&
- (pfctl_load_eth_ruleset(&pf))) ||
+ (pfctl_load_eth_ruleset(&pf, path, ethrs, 0))) ||
(pf.loadopt & PFCTL_FLAG_NAT &&
(pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) ||
pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) ||
@@ -2572,7 +2759,7 @@ main(int argc, char *argv[])
sizeof(anchorname)) >= sizeof(anchorname))
errx(1, "anchor name '%s' too long",
anchoropt);
- loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE;
+ loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE|PFCTL_FLAG_ETH;
}
if ((opts & PF_OPT_NOACTION) == 0) {
@@ -2640,13 +2827,13 @@ main(int argc, char *argv[])
pfctl_show_limits(dev, opts);
break;
case 'e':
- pfctl_show_eth_rules(dev, opts, 0);
+ pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0);
break;
case 'a':
opts |= PF_OPT_SHOWALL;
pfctl_load_fingerprints(dev, opts);
- pfctl_show_eth_rules(dev, opts, 0);
+ pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0);
pfctl_show_nat(dev, opts, anchorname);
pfctl_show_rules(dev, path, opts, 0, anchorname, 0);
@@ -2674,7 +2861,8 @@ main(int argc, char *argv[])
}
*** 1272 LINES SKIPPED ***