aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2021-10-01 17:05:50 +0000
committerKristof Provost <kp@FreeBSD.org>2022-03-02 16:00:07 +0000
commitc5131afee39b4fa9e3889deb2ceea35a43ef35e2 (patch)
tree433111d8442204555f0ef4bbdd79eb2784dcfdb6
parent87a89d6e14ac5730572d454ec12a3a30d492816e (diff)
downloadsrc-c5131afee39b4fa9e3889deb2ceea35a43ef35e2.tar.gz
src-c5131afee39b4fa9e3889deb2ceea35a43ef35e2.zip
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
-rw-r--r--lib/libpfctl/libpfctl.c31
-rw-r--r--lib/libpfctl/libpfctl.h28
-rw-r--r--sbin/pfctl/parse.y119
-rw-r--r--sbin/pfctl/pf_ruleset.c197
-rw-r--r--sbin/pfctl/pfctl.c254
-rw-r--r--sbin/pfctl/pfctl.h9
-rw-r--r--sbin/pfctl/pfctl_parser.c13
-rw-r--r--sbin/pfctl/pfctl_parser.h7
-rw-r--r--sys/net/pfvar.h82
-rw-r--r--sys/netpfil/pf/pf.c136
-rw-r--r--sys/netpfil/pf/pf_ioctl.c228
-rw-r--r--sys/netpfil/pf/pf_nv.c3
-rw-r--r--sys/netpfil/pf/pf_ruleset.c307
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);
@@ -6526,6 +6610,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)
{
if (*af != 0 || n == NULL)
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[])
}
if ((opts & PF_OPT_CLRRULECTRS) && showopt == NULL) {
- pfctl_show_eth_rules(dev, opts, PFCTL_SHOW_NOTHING);
+ pfctl_show_eth_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
+ anchorname, 0);
pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
anchorname, 0);
}
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index 606eb729cd44..b4f27e5b9b21 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -38,6 +38,8 @@
#include <libpfctl.h>
+struct pfctl;
+
enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS,
@@ -137,6 +139,13 @@ int pfctl_anchor_setup(struct pfctl_rule *,
void pf_remove_if_empty_ruleset(struct pfctl_ruleset *);
struct pfctl_ruleset *pf_find_ruleset(const char *);
struct pfctl_ruleset *pf_find_or_create_ruleset(const char *);
+void pf_init_eth_ruleset(struct pfctl_eth_ruleset *);
+int pfctl_eth_anchor_setup(struct pfctl *,
+ struct pfctl_eth_rule *,
+ const struct pfctl_eth_ruleset *, const char *);
+struct pfctl_eth_ruleset *pf_find_or_create_eth_ruleset(const char *);
+void pf_remove_if_empty_eth_ruleset(
+ struct pfctl_eth_ruleset *);
void expand_label(char *, size_t, struct pfctl_rule *);
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index bde6d4b10696..0db0ad355cf7 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -710,14 +710,23 @@ print_eth_addr(const struct pfctl_eth_addr *a)
}
void
-print_eth_rule(struct pfctl_eth_rule *r, int rule_numbers)
+print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
+ int rule_numbers)
{
static const char *actiontypes[] = { "pass", "block" };
if (rule_numbers)
printf("@%u ", r->nr);
- printf("ether %s", actiontypes[r->action]);
+ printf("ether ");
+ if (anchor_call[0]) {
+ if (anchor_call[0] == '_') {
+ printf("anchor");
+ } else
+ printf("anchor \"%s\"", anchor_call);
+ } else {
+ printf("%s", actiontypes[r->action]);
+ }
if (r->direction == PF_IN)
printf(" in");
else if (r->direction == PF_OUT)
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 990f860fe8e3..e60132d15855 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -91,7 +91,8 @@ struct pfctl {
struct pfr_buffer *trans;
struct pfctl_anchor *anchor, *alast;
int eth_nr;
- struct pfctl_eth_rules eth_rules;
+ struct pfctl_eth_anchor *eanchor, *ealast;
+ struct pfctl_eth_anchor *eastack[PFCTL_ANCHOR_STACK_DEPTH];
u_int32_t eth_ticket;
const char *ruleset;
@@ -274,6 +275,8 @@ int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
int pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *);
int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *);
+int pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *,
+ const char *);
int pfctl_add_altq(struct pfctl *, struct pf_altq *);
int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t);
void pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *);
@@ -294,7 +297,7 @@ int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int);
void print_src_node(struct pf_src_node *, int);
-void print_eth_rule(struct pfctl_eth_rule *, int);
+void print_eth_rule(struct pfctl_eth_rule *, const char *, int);
void print_rule(struct pfctl_rule *, const char *, int, int);
void print_tabledef(const char *, int, int, struct node_tinithead *);
void print_status(struct pfctl_status *, struct pfctl_syncookies *, int);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 11fb57b49fca..a2265e7cc1c5 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -583,6 +583,41 @@ struct pf_keth_rule_addr {
uint8_t isset;
};
+struct pf_keth_anchor;
+
+TAILQ_HEAD(pf_keth_ruleq, pf_keth_rule);
+
+struct pf_keth_ruleset {
+ struct pf_keth_ruleq rules[2];
+ struct pf_keth_rules {
+ struct pf_keth_ruleq *rules;
+ int open;
+ uint32_t ticket;
+ } active, inactive;
+ struct epoch_context epoch_ctx;
+ struct vnet *vnet;
+ struct pf_keth_anchor *anchor;
+};
+
+RB_HEAD(pf_keth_anchor_global, pf_keth_anchor);
+RB_HEAD(pf_keth_anchor_node, pf_keth_anchor);
+struct pf_keth_anchor {
+ RB_ENTRY(pf_keth_anchor) entry_node;
+ RB_ENTRY(pf_keth_anchor) entry_global;
+ struct pf_keth_anchor *parent;
+ struct pf_keth_anchor_node children;
+ char name[PF_ANCHOR_NAME_SIZE];
+ char path[MAXPATHLEN];
+ struct pf_keth_ruleset ruleset;
+ int refcnt; /* anchor rules */
+ uint8_t anchor_relative;
+ uint8_t anchor_wildcard;
+};
+RB_PROTOTYPE(pf_keth_anchor_node, pf_keth_anchor, entry_node,
+ pf_keth_anchor_compare);
+RB_PROTOTYPE(pf_keth_anchor_global, pf_keth_anchor, entry_global,
+ pf_keth_anchor_compare);
+
struct pf_keth_rule {
#define PFE_SKIP_IFP 0
#define PFE_SKIP_DIR 1
@@ -594,6 +629,10 @@ struct pf_keth_rule {
TAILQ_ENTRY(pf_keth_rule) entries;
+ struct pf_keth_anchor *anchor;
+ u_int8_t anchor_relative;
+ u_int8_t anchor_wildcard;
+
uint32_t nr;
bool quick;
@@ -621,16 +660,6 @@ struct pf_keth_rule {
uint32_t dnflags;
};
-TAILQ_HEAD(pf_keth_rules, pf_keth_rule);
-
-struct pf_keth_settings {
- struct pf_keth_rules rules;
- uint32_t ticket;
- int open;
- struct vnet *vnet;
- struct epoch_context epoch_ctx;
-};
-
union pf_krule_ptr {
struct pf_krule *ptr;
u_int32_t nr;
@@ -1151,6 +1180,7 @@ RB_PROTOTYPE(pf_kanchor_node, pf_kanchor, entry_node, pf_kanchor_compare);
PFR_TFLAG_COUNTERS)
struct pf_kanchor_stackframe;
+struct pf_keth_anchor_stackframe;
struct pfr_table {
char pfrt_anchor[MAXPATHLEN];
@@ -2206,15 +2236,17 @@ VNET_DECLARE(struct pf_kanchor_global, pf_anchors);
#define V_pf_anchors VNET(pf_anchors)
VNET_DECLARE(struct pf_kanchor, pf_main_anchor);
#define V_pf_main_anchor VNET(pf_main_anchor)
+VNET_DECLARE(struct pf_keth_anchor_global, pf_keth_anchors);
+#define V_pf_keth_anchors VNET(pf_keth_anchors)
#define pf_main_ruleset V_pf_main_anchor.ruleset
-VNET_DECLARE(struct pf_keth_settings*, pf_keth);
+VNET_DECLARE(struct pf_keth_anchor, pf_main_keth_anchor);
+#define V_pf_main_keth_anchor VNET(pf_main_keth_anchor)
+VNET_DECLARE(struct pf_keth_ruleset*, pf_keth);
#define V_pf_keth VNET(pf_keth)
-VNET_DECLARE(struct pf_keth_settings*, pf_keth_inactive);
-#define V_pf_keth_inactive VNET(pf_keth_inactive)
void pf_init_kruleset(struct pf_kruleset *);
-void pf_init_keth(struct pf_keth_settings *);
+void pf_init_keth(struct pf_keth_ruleset *);
int pf_kanchor_setup(struct pf_krule *,
const struct pf_kruleset *, const char *);
int pf_kanchor_nvcopyout(const struct pf_kruleset *,
@@ -2227,7 +2259,21 @@ struct pf_kruleset *pf_find_kruleset(const char *);
struct pf_kruleset *pf_find_or_create_kruleset(const char *);
void pf_rs_initialize(void);
+
struct pf_krule *pf_krule_alloc(void);
+
+void pf_remove_if_empty_keth_ruleset(
+ struct pf_keth_ruleset *);
+struct pf_keth_ruleset *pf_find_keth_ruleset(const char *);
+struct pf_keth_anchor *pf_find_keth_anchor(const char *);
+int pf_keth_anchor_setup(struct pf_keth_rule *,
+ const struct pf_keth_ruleset *, const char *);
+int pf_keth_anchor_nvcopyout(
+ const struct pf_keth_ruleset *,
+ const struct pf_keth_rule *, nvlist_t *);
+struct pf_keth_ruleset *pf_find_or_create_keth_ruleset(const char *);
+void pf_keth_anchor_remove(struct pf_keth_rule *);
+
void pf_krule_free(struct pf_krule *);
#endif
@@ -2251,6 +2297,14 @@ void pf_step_into_anchor(struct pf_kanchor_stackframe *, int *,
int pf_step_out_of_anchor(struct pf_kanchor_stackframe *, int *,
struct pf_kruleset **, int, struct pf_krule **,
struct pf_krule **, int *);
+void pf_step_into_keth_anchor(struct pf_keth_anchor_stackframe *,
+ int *, struct pf_keth_ruleset **,
+ struct pf_keth_rule **, struct pf_keth_rule **,
+ int *);
+int pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *,
+ int *, struct pf_keth_ruleset **,
+ struct pf_keth_rule **, struct pf_keth_rule **,
+ int *);
int pf_map_addr(u_int8_t, struct pf_krule *,
struct pf_addr *, struct pf_addr *,
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 09d727508750..bd82c2a9904e 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -3404,6 +3404,110 @@ pf_step_out_of_anchor(struct pf_kanchor_stackframe *stack, int *depth,
return (quick);
}
+struct pf_keth_anchor_stackframe {
+ struct pf_keth_ruleset *rs;
+ struct pf_keth_rule *r; /* XXX: + match bit */
+ struct pf_keth_anchor *child;
+};
+
+#define PF_ETH_ANCHOR_MATCH(f) ((uintptr_t)(f)->r & PF_ANCHORSTACK_MATCH)
+#define PF_ETH_ANCHOR_RULE(f) (struct pf_keth_rule *) \
+ ((uintptr_t)(f)->r & ~PF_ANCHORSTACK_MASK)
+#define PF_ETH_ANCHOR_SET_MATCH(f) do { (f)->r = (void *) \
+ ((uintptr_t)(f)->r | PF_ANCHORSTACK_MATCH); \
+} while (0)
+
+void
+pf_step_into_keth_anchor(struct pf_keth_anchor_stackframe *stack, int *depth,
+ struct pf_keth_ruleset **rs, struct pf_keth_rule **r,
+ struct pf_keth_rule **a, int *match)
+{
+ struct pf_keth_anchor_stackframe *f;
+
+ NET_EPOCH_ASSERT();
+
+ if (match)
+ *match = 0;
+ if (*depth >= PF_ANCHOR_STACKSIZE) {
+ printf("%s: anchor stack overflow on %s\n",
+ __func__, (*r)->anchor->name);
+ *r = TAILQ_NEXT(*r, entries);
+ return;
+ } else if (*depth == 0 && a != NULL)
+ *a = *r;
+ f = stack + (*depth)++;
+ f->rs = *rs;
+ f->r = *r;
+ if ((*r)->anchor_wildcard) {
+ struct pf_keth_anchor_node *parent = &(*r)->anchor->children;
+
+ if ((f->child = RB_MIN(pf_keth_anchor_node, parent)) == NULL) {
+ *r = NULL;
+ return;
+ }
+ *rs = &f->child->ruleset;
+ } else {
+ f->child = NULL;
+ *rs = &(*r)->anchor->ruleset;
+ }
+ *r = TAILQ_FIRST((*rs)->active.rules);
+}
+
+int
+pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *stack, int *depth,
+ struct pf_keth_ruleset **rs, struct pf_keth_rule **r,
+ struct pf_keth_rule **a, int *match)
+{
+ struct pf_keth_anchor_stackframe *f;
+ struct pf_keth_rule *fr;
+ int quick = 0;
+
+ NET_EPOCH_ASSERT();
+
+ do {
+ if (*depth <= 0)
+ break;
+ f = stack + *depth - 1;
+ fr = PF_ETH_ANCHOR_RULE(f);
+ if (f->child != NULL) {
+ struct pf_keth_anchor_node *parent;
+ /*
+ * This block traverses through
+ * a wildcard anchor.
+ */
+ parent = &fr->anchor->children;
+ if (match != NULL && *match) {
+ /*
+ * If any of "*" matched, then
+ * "foo/ *" matched, mark frame
+ * appropriately.
+ */
+ PF_ETH_ANCHOR_SET_MATCH(f);
+ *match = 0;
+ }
+ f->child = RB_NEXT(pf_keth_anchor_node, parent,
+ f->child);
+ if (f->child != NULL) {
+ *rs = &f->child->ruleset;
+ *r = TAILQ_FIRST((*rs)->active.rules);
+ if (*r == NULL)
+ continue;
+ else
+ break;
+ }
+ }
+ (*depth)--;
+ if (*depth == 0 && a != NULL)
+ *a = NULL;
+ *rs = f->rs;
+ if (PF_ETH_ANCHOR_MATCH(f) || (match != NULL && *match))
+ quick = fr->quick;
+ *r = TAILQ_NEXT(fr, entries);
+ } while (*r == NULL);
+
+ return (quick);
+}
+
#ifdef INET6
void
pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr,
@@ -3719,10 +3823,13 @@ static int
pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
{
struct ether_header *e;
- struct pf_keth_rule *r, *rm;
+ struct pf_keth_rule *r, *rm, *a = NULL;
+ struct pf_keth_ruleset *ruleset = NULL;
struct pf_mtag *mtag;
- struct pf_keth_settings *settings;
+ struct pf_keth_ruleq *rules;
+ int asd = 0, match = 0;
uint8_t action;
+ struct pf_keth_anchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE];
NET_EPOCH_ASSERT();
@@ -3733,8 +3840,9 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
e = mtod(m, struct ether_header *);
- settings = ck_pr_load_ptr(&V_pf_keth);
- r = TAILQ_FIRST(&settings->rules);
+ ruleset = V_pf_keth;
+ rules = ck_pr_load_ptr(&ruleset->active.rules);
+ r = TAILQ_FIRST(rules);
rm = NULL;
while (r != NULL) {
@@ -3767,16 +3875,24 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
r = TAILQ_NEXT(r, entries);
}
else {
- /* Rule matches */
- rm = r;
+ if (r->anchor == NULL) {
+ /* Rule matches */
+ rm = r;
- SDT_PROBE2(pf, eth, test_rule, match, r->nr, r);
+ SDT_PROBE2(pf, eth, test_rule, match, r->nr, r);
- if (r->quick)
- break;
+ if (r->quick)
+ break;
- r = TAILQ_NEXT(r, entries);
+ r = TAILQ_NEXT(r, entries);
+ } else {
+ pf_step_into_keth_anchor(anchor_stack, &asd,
+ &ruleset, &r, &a, &match);
+ }
}
+ if (r == NULL && pf_step_out_of_keth_anchor(anchor_stack, &asd,
+ &ruleset, &r, &a, &match))
+ break;
}
r = rm;
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 7e14d0cf405c..908e22184172 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -106,10 +106,10 @@ static void pf_mv_kpool(struct pf_kpalist *, struct pf_kpalist *);
static void pf_empty_kpool(struct pf_kpalist *);
static int pfioctl(struct cdev *, u_long, caddr_t, int,
struct thread *);
-static int pf_begin_eth(uint32_t *);
+static int pf_begin_eth(uint32_t *, const char *);
static void pf_rollback_eth_cb(struct epoch_context *);
-static int pf_rollback_eth(uint32_t);
-static int pf_commit_eth(uint32_t);
+static int pf_rollback_eth(uint32_t, const char *);
+static int pf_commit_eth(uint32_t, const char *);
static void pf_free_eth_rule(struct pf_keth_rule *);
#ifdef ALTQ
static int pf_begin_altq(u_int32_t *);
@@ -318,7 +318,6 @@ pfattach_vnet(void)
pf_init_kruleset(&pf_main_ruleset);
pf_init_keth(V_pf_keth);
- pf_init_keth(V_pf_keth_inactive);
/* default rule should never be garbage collected */
V_pf_default_rule.entries.tqe_prev = &V_pf_default_rule.entries.tqe_next;
@@ -510,6 +509,7 @@ pf_free_eth_rule(struct pf_keth_rule *rule)
counter_u64_free(rule->packets[i]);
counter_u64_free(rule->bytes[i]);
}
+ pf_keth_anchor_remove(rule);
free(rule, M_PFRULE);
}
@@ -701,26 +701,32 @@ pf_tagname2tag(const char *tagname)
}
static int
-pf_begin_eth(uint32_t *ticket)
+pf_begin_eth(uint32_t *ticket, const char *anchor)
{
struct pf_keth_rule *rule, *tmp;
+ struct pf_keth_ruleset *rs;
PF_RULES_WASSERT();
- if (V_pf_keth_inactive->open) {
+ rs = pf_find_or_create_keth_ruleset(anchor);
+ if (rs == NULL)
+ return (EINVAL);
+
+ if (rs->inactive.open)
/* We may be waiting for NET_EPOCH_CALL(pf_rollback_eth_cb) to
* finish. */
return (EBUSY);
- }
/* Purge old inactive rules. */
- TAILQ_FOREACH_SAFE(rule, &V_pf_keth_inactive->rules, entries, tmp) {
- TAILQ_REMOVE(&V_pf_keth_inactive->rules, rule, entries);
+ TAILQ_FOREACH_SAFE(rule, rs->inactive.rules, entries,
+ tmp) {
+ TAILQ_REMOVE(rs->inactive.rules, rule,
+ entries);
pf_free_eth_rule(rule);
}
- *ticket = ++V_pf_keth_inactive->ticket;
- V_pf_keth_inactive->open = 1;
+ *ticket = ++rs->inactive.ticket;
+ rs->inactive.open = 1;
return (0);
}
@@ -728,38 +734,46 @@ pf_begin_eth(uint32_t *ticket)
static void
pf_rollback_eth_cb(struct epoch_context *ctx)
{
- struct pf_keth_settings *settings;
-
- settings = __containerof(ctx, struct pf_keth_settings, epoch_ctx);
+ struct pf_keth_ruleset *rs;
- CURVNET_SET(settings->vnet);
+ rs = __containerof(ctx, struct pf_keth_ruleset, epoch_ctx);
- MPASS(settings == V_pf_keth_inactive);
+ CURVNET_SET(rs->vnet);
PF_RULES_WLOCK();
- pf_rollback_eth(V_pf_keth_inactive->ticket);
+ pf_rollback_eth(rs->inactive.ticket,
+ rs->anchor ? rs->anchor->path : "");
PF_RULES_WUNLOCK();
CURVNET_RESTORE();
}
static int
-pf_rollback_eth(uint32_t ticket)
+pf_rollback_eth(uint32_t ticket, const char *anchor)
{
struct pf_keth_rule *rule, *tmp;
+ struct pf_keth_ruleset *rs;
PF_RULES_WASSERT();
- if (!V_pf_keth_inactive->open || ticket != V_pf_keth_inactive->ticket)
+ rs = pf_find_keth_ruleset(anchor);
+ if (rs == NULL)
+ return (EINVAL);
+
+ if (!rs->inactive.open ||
+ ticket != rs->inactive.ticket)
return (0);
/* Purge old inactive rules. */
- TAILQ_FOREACH_SAFE(rule, &V_pf_keth_inactive->rules, entries, tmp) {
- TAILQ_REMOVE(&V_pf_keth_inactive->rules, rule, entries);
+ TAILQ_FOREACH_SAFE(rule, rs->inactive.rules, entries,
+ tmp) {
+ TAILQ_REMOVE(rs->inactive.rules, rule, entries);
pf_free_eth_rule(rule);
}
- V_pf_keth_inactive->open = 0;
+ rs->inactive.open = 0;
+
+ pf_remove_if_empty_keth_ruleset(rs);
return (0);
}
@@ -773,7 +787,7 @@ pf_rollback_eth(uint32_t ticket)
} while (0)
static void
-pf_eth_calc_skip_steps(struct pf_keth_rules *rules)
+pf_eth_calc_skip_steps(struct pf_keth_ruleq *rules)
{
struct pf_keth_rule *cur, *prev, *head[PFE_SKIP_COUNT];
int i;
@@ -802,26 +816,32 @@ pf_eth_calc_skip_steps(struct pf_keth_rules *rules)
}
static int
-pf_commit_eth(uint32_t ticket)
+pf_commit_eth(uint32_t ticket, const char *anchor)
{
- struct pf_keth_settings *settings;
+ struct pf_keth_ruleq *rules;
+ struct pf_keth_ruleset *rs;
+
+ rs = pf_find_keth_ruleset(anchor);
+ if (rs == NULL) {
+ return (EINVAL);
+ }
- if (!V_pf_keth_inactive->open ||
- ticket != V_pf_keth_inactive->ticket)
+ if (!rs->inactive.open ||
+ ticket != rs->inactive.ticket)
return (EBUSY);
PF_RULES_WASSERT();
- pf_eth_calc_skip_steps(&V_pf_keth_inactive->rules);
+ pf_eth_calc_skip_steps(rs->inactive.rules);
- settings = V_pf_keth;
- ck_pr_store_ptr(&V_pf_keth, V_pf_keth_inactive);
- V_pf_keth_inactive = settings;
- V_pf_keth_inactive->ticket = V_pf_keth->ticket;
+ rules = rs->active.rules;
+ ck_pr_store_ptr(&rs->active.rules, rs->inactive.rules);
+ rs->inactive.rules = rules;
+ rs->inactive.ticket = rs->active.ticket;
/* Clean up inactive rules (i.e. previously active rules), only when
* we're sure they're no longer used. */
- NET_EPOCH_CALL(pf_rollback_eth_cb, &V_pf_keth_inactive->epoch_ctx);
+ NET_EPOCH_CALL(pf_rollback_eth_cb, &rs->epoch_ctx);
return (0);
}
@@ -2475,7 +2495,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
int cpu;
hook_pf();
- if (! TAILQ_EMPTY(&V_pf_keth->rules))
+ if (! TAILQ_EMPTY(V_pf_keth->active.rules))
hook_pf_eth();
V_pf_status.running = 1;
V_pf_status.since = time_second;
@@ -2505,21 +2525,55 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
nvlist_t *nvl;
void *packed;
struct pf_keth_rule *tail;
+ struct pf_keth_ruleset *rs;
u_int32_t ticket, nr;
+ const char *anchor = "";
nvl = NULL;
packed = NULL;
#define ERROUT(x) do { error = (x); goto DIOCGETETHRULES_error; } while (0)
+ if (nv->len > pf_ioctl_maxcount)
+ ERROUT(ENOMEM);
+
+ /* Copy the request in */
+ packed = malloc(nv->len, M_NVLIST, M_WAITOK);
+ if (packed == NULL)
+ ERROUT(ENOMEM);
+
+ error = copyin(nv->data, packed, nv->len);
+ if (error)
+ ERROUT(error);
+
+ nvl = nvlist_unpack(packed, nv->len, 0);
+ if (nvl == NULL)
+ ERROUT(EBADMSG);
+
+ if (! nvlist_exists_string(nvl, "anchor"))
+ ERROUT(EBADMSG);
+
+ anchor = nvlist_get_string(nvl, "anchor");
+
+ rs = pf_find_keth_ruleset(anchor);
+
+ nvlist_destroy(nvl);
+ nvl = NULL;
+ free(packed, M_NVLIST);
+ packed = NULL;
+
+ if (rs == NULL)
+ ERROUT(ENOENT);
+
+ /* Reply */
nvl = nvlist_create(0);
if (nvl == NULL)
ERROUT(ENOMEM);
PF_RULES_RLOCK();
- ticket = V_pf_keth->ticket;
- tail = TAILQ_LAST(&V_pf_keth->rules, pf_keth_rules);
+ ticket = rs->active.ticket;
+ tail = TAILQ_LAST(rs->active.rules, pf_keth_ruleq);
if (tail)
nr = tail->nr + 1;
else
@@ -2543,7 +2597,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
#undef ERROUT
DIOCGETETHRULES_error:
- free(packed, M_TEMP);
+ free(packed, M_NVLIST);
nvlist_destroy(nvl);
break;
}
@@ -2554,8 +2608,10 @@ DIOCGETETHRULES_error:
nvlist_t *nvl = NULL;
void *nvlpacked = NULL;
struct pf_keth_rule *rule = NULL;
+ struct pf_keth_ruleset *rs;
u_int32_t ticket, nr;
bool clear = false;
+ const char *anchor;
#define ERROUT(x) do { error = (x); goto DIOCGETETHRULE_error; } while (0)
@@ -2571,6 +2627,9 @@ DIOCGETETHRULES_error:
if (! nvlist_exists_number(nvl, "ticket"))
ERROUT(EBADMSG);
ticket = nvlist_get_number(nvl, "ticket");
+ if (! nvlist_exists_string(nvl, "anchor"))
+ ERROUT(EBADMSG);
+ anchor = nvlist_get_string(nvl, "anchor");
if (nvlist_exists_bool(nvl, "clear"))
clear = nvlist_get_bool(nvl, "clear");
@@ -2582,6 +2641,17 @@ DIOCGETETHRULES_error:
ERROUT(EBADMSG);
nr = nvlist_get_number(nvl, "nr");
+ PF_RULES_RLOCK();
+ rs = pf_find_keth_ruleset(anchor);
+ if (rs == NULL) {
+ PF_RULES_RUNLOCK();
+ ERROUT(ENOENT);
+ }
+ if (ticket != rs->active.ticket) {
+ PF_RULES_RUNLOCK();
+ ERROUT(EBUSY);
+ }
+
nvlist_destroy(nvl);
nvl = NULL;
free(nvlpacked, M_TEMP);
@@ -2589,12 +2659,7 @@ DIOCGETETHRULES_error:
nvl = nvlist_create(0);
- PF_RULES_RLOCK();
- if (ticket != V_pf_keth->ticket) {
- PF_RULES_RUNLOCK();
- ERROUT(EBUSY);
- }
- rule = TAILQ_FIRST(&V_pf_keth->rules);
+ rule = TAILQ_FIRST(rs->active.rules);
while ((rule != NULL) && (rule->nr != nr))
rule = TAILQ_NEXT(rule, entries);
if (rule == NULL) {
@@ -2605,6 +2670,8 @@ DIOCGETETHRULES_error:
NET_EPOCH_ENTER(et);
PF_RULES_RUNLOCK();
nvl = pf_keth_rule_to_nveth_rule(rule);
+ if (pf_keth_anchor_nvcopyout(rs, rule, nvl))
+ ERROUT(EBUSY);
NET_EPOCH_EXIT(et);
if (nvl == NULL)
ERROUT(ENOMEM);
@@ -2638,8 +2705,10 @@ DIOCGETETHRULE_error:
struct pfioc_nv *nv = (struct pfioc_nv *)addr;
nvlist_t *nvl = NULL;
void *nvlpacked = NULL;
- struct pf_keth_rule *rule = NULL;
+ struct pf_keth_rule *rule = NULL, *tail = NULL;
+ struct pf_keth_ruleset *ruleset = NULL;
struct pfi_kkif *kif = NULL;
+ const char *anchor = "", *anchor_call = "";
#define ERROUT(x) do { error = (x); goto DIOCADDETHRULE_error; } while (0)
@@ -2658,12 +2727,21 @@ DIOCGETETHRULE_error:
if (! nvlist_exists_number(nvl, "ticket"))
ERROUT(EBADMSG);
+ if (nvlist_exists_string(nvl, "anchor"))
+ anchor = nvlist_get_string(nvl, "anchor");
+ if (nvlist_exists_string(nvl, "anchor_call"))
+ anchor_call = nvlist_get_string(nvl, "anchor_call");
+
+ ruleset = pf_find_keth_ruleset(anchor);
+ if (ruleset == NULL)
+ ERROUT(EINVAL);
+
if (nvlist_get_number(nvl, "ticket") !=
- V_pf_keth_inactive->ticket) {
+ ruleset->inactive.ticket) {
DPFPRINTF(PF_DEBUG_MISC,
("ticket: %d != %d\n",
(u_int32_t)nvlist_get_number(nvl, "ticket"),
- V_pf_keth_inactive->ticket));
+ ruleset->inactive.ticket));
ERROUT(EBUSY);
}
@@ -2710,7 +2788,19 @@ DIOCGETETHRULE_error:
ERROUT(error);
}
- TAILQ_INSERT_TAIL(&V_pf_keth_inactive->rules, rule, entries);
+ if (pf_keth_anchor_setup(rule, ruleset, anchor_call)) {
+ pf_free_eth_rule(rule);
+ PF_RULES_WUNLOCK();
+ ERROUT(EINVAL);
+ }
+
+ tail = TAILQ_LAST(ruleset->inactive.rules, pf_keth_ruleq);
+ if (tail)
+ rule->nr = tail->nr + 1;
+ else
+ rule->nr = 0;
+
+ TAILQ_INSERT_TAIL(ruleset->inactive.rules, rule, entries);
PF_RULES_WUNLOCK();
@@ -4765,13 +4855,7 @@ DIOCCHANGEADDR_error:
ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
switch (ioe->rs_num) {
case PF_RULESET_ETH:
- if (ioe->anchor[0]) {
- PF_RULES_WUNLOCK();
- free(ioes, M_TEMP);
- error = EINVAL;
- goto fail;
- }
- if ((error = pf_begin_eth(&ioe->ticket))) {
+ if ((error = pf_begin_eth(&ioe->ticket, ioe->anchor))) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
goto fail;
@@ -4852,13 +4936,8 @@ DIOCCHANGEADDR_error:
ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
switch (ioe->rs_num) {
case PF_RULESET_ETH:
- if (ioe->anchor[0]) {
- PF_RULES_WUNLOCK();
- free(ioes, M_TEMP);
- error = EINVAL;
- goto fail;
- }
- if ((error = pf_rollback_eth(ioe->ticket))) {
+ if ((error = pf_rollback_eth(ioe->ticket,
+ ioe->anchor))) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
goto fail; /* really bad */
@@ -4913,6 +4992,7 @@ DIOCCHANGEADDR_error:
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe, *ioes;
struct pf_kruleset *rs;
+ struct pf_keth_ruleset *ers;
size_t totlen;
int i;
@@ -4942,19 +5022,14 @@ DIOCCHANGEADDR_error:
ioe->anchor[sizeof(ioe->anchor) - 1] = 0;
switch (ioe->rs_num) {
case PF_RULESET_ETH:
- if (ioe->anchor[0]) {
+ ers = pf_find_keth_ruleset(ioe->anchor);
+ if (ers == NULL || ioe->ticket == 0 ||
+ ioe->ticket != ers->inactive.ticket) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
error = EINVAL;
goto fail;
}
- if (!V_pf_keth_inactive->ticket ||
- ioe->ticket != V_pf_keth_inactive->ticket) {
- PF_RULES_WUNLOCK();
- free(ioes, M_TEMP);
- error = EBUSY;
- goto fail;
- }
break;
#ifdef ALTQ
case PF_RULESET_ALTQ:
@@ -5008,7 +5083,7 @@ DIOCCHANGEADDR_error:
for (i = 0, ioe = ioes; i < io->size; i++, ioe++) {
switch (ioe->rs_num) {
case PF_RULESET_ETH:
- if ((error = pf_commit_eth(ioe->ticket))) {
+ if ((error = pf_commit_eth(ioe->ticket, ioe->anchor))) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
goto fail; /* really bad */
@@ -5051,7 +5126,7 @@ DIOCCHANGEADDR_error:
PF_RULES_WUNLOCK();
/* Only hook into EtherNet taffic if we've got rules for it. */
- if (! TAILQ_EMPTY(&V_pf_keth->rules))
+ if (! TAILQ_EMPTY(V_pf_keth->active.rules))
hook_pf_eth();
else
dehook_pf_eth();
@@ -5962,11 +6037,11 @@ shutdown_pf(void)
if ((error = pf_clear_tables()) != 0)
break;
- if ((error = pf_begin_eth(&t[0])) != 0) {
+ if ((error = pf_begin_eth(&t[0], &nn)) != 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: eth\n"));
break;
}
- pf_commit_eth(t[0]);
+ pf_commit_eth(t[0], &nn);
#ifdef ALTQ
if ((error = pf_begin_altq(&t[0])) != 0) {
@@ -6250,9 +6325,7 @@ pf_load_vnet(void)
PF_QUEUE_TAG_HASH_SIZE_DEFAULT);
#endif
- V_pf_keth = malloc(sizeof(*V_pf_keth), M_PFRULE, M_WAITOK);
- V_pf_keth_inactive = malloc(sizeof(*V_pf_keth_inactive),
- M_PFRULE, M_WAITOK);
+ V_pf_keth = &V_pf_main_keth_anchor.ruleset;
pfattach_vnet();
V_pf_vnet_active = 1;
@@ -6364,9 +6437,6 @@ pf_unload_vnet(void)
pf_counter_u64_deinit(&V_pf_status.fcounters[i]);
for (int i = 0; i < SCNT_MAX; i++)
counter_u64_free(V_pf_status.scounters[i]);
-
- free(V_pf_keth, M_PFRULE);
- free(V_pf_keth_inactive, M_PFRULE);
}
static void
diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c
index b662bb4e7d95..5fc222ff3b79 100644
--- a/sys/netpfil/pf/pf_nv.c
+++ b/sys/netpfil/pf/pf_nv.c
@@ -1084,6 +1084,9 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
nvlist_add_number(nvl, "dnpipe", krule->dnpipe);
nvlist_add_number(nvl, "dnflags", krule->dnflags);
+ nvlist_add_number(nvl, "anchor_relative", krule->anchor_relative);
+ nvlist_add_number(nvl, "anchor_wildcard", krule->anchor_wildcard);
+
nvlist_add_number(nvl, "action", krule->action);
return (nvl);
diff --git a/sys/netpfil/pf/pf_ruleset.c b/sys/netpfil/pf/pf_ruleset.c
index 038d82bf2d81..c933e2bd27fb 100644
--- a/sys/netpfil/pf/pf_ruleset.c
+++ b/sys/netpfil/pf/pf_ruleset.c
@@ -70,15 +70,22 @@ __FBSDID("$FreeBSD$");
VNET_DEFINE(struct pf_kanchor_global, pf_anchors);
VNET_DEFINE(struct pf_kanchor, pf_main_anchor);
-VNET_DEFINE(struct pf_keth_settings*, pf_keth);
-VNET_DEFINE(struct pf_keth_settings*, pf_keth_inactive);
+VNET_DEFINE(struct pf_keth_ruleset*, pf_keth);
+VNET_DEFINE(struct pf_keth_anchor, pf_main_keth_anchor);
+VNET_DEFINE(struct pf_keth_anchor_global, pf_keth_anchors);
static __inline int pf_kanchor_compare(struct pf_kanchor *,
struct pf_kanchor *);
+static __inline int pf_keth_anchor_compare(struct pf_keth_anchor *,
+ struct pf_keth_anchor *);
static struct pf_kanchor *pf_find_kanchor(const char *);
RB_GENERATE(pf_kanchor_global, pf_kanchor, entry_global, pf_kanchor_compare);
RB_GENERATE(pf_kanchor_node, pf_kanchor, entry_node, pf_kanchor_compare);
+RB_GENERATE(pf_keth_anchor_global, pf_keth_anchor, entry_global,
+ pf_keth_anchor_compare);
+RB_GENERATE(pf_keth_anchor_node, pf_keth_anchor, entry_node,
+ pf_keth_anchor_compare);
static __inline int
pf_kanchor_compare(struct pf_kanchor *a, struct pf_kanchor *b)
@@ -88,6 +95,14 @@ pf_kanchor_compare(struct pf_kanchor *a, struct pf_kanchor *b)
return (c ? (c < 0 ? -1 : 1) : 0);
}
+static __inline int
+pf_keth_anchor_compare(struct pf_keth_anchor *a, struct pf_keth_anchor *b)
+{
+ int c = strcmp(a->path, b->path);
+
+ return (c ? (c < 0 ? -1 : 1) : 0);
+}
+
int
pf_get_ruleset_number(u_int8_t action)
{
@@ -148,13 +163,18 @@ pf_init_kruleset(struct pf_kruleset *ruleset)
}
void
-pf_init_keth(struct pf_keth_settings *settings)
+pf_init_keth(struct pf_keth_ruleset *rs)
{
- TAILQ_INIT(&settings->rules);
- settings->ticket = 0;
- settings->open = 0;
- settings->vnet = curvnet;
+ bzero(rs, sizeof(*rs));
+ TAILQ_INIT(&rs->rules[0]);
+ TAILQ_INIT(&rs->rules[1]);
+ rs->active.rules = &rs->rules[0];
+ rs->active.open = 0;
+ rs->inactive.rules = &rs->rules[1];
+ rs->inactive.open = 0;
+
+ rs->vnet = curvnet;
}
struct pf_kruleset *
@@ -397,6 +417,53 @@ done:
}
int
+pf_keth_anchor_nvcopyout(const struct pf_keth_ruleset *rs,
+ const struct pf_keth_rule *r, nvlist_t *nvl)
+{
+ char anchor_call[MAXPATHLEN] = { 0 };
+
+ if (r->anchor == NULL)
+ goto done;
+ if (!r->anchor_relative) {
+ strlcpy(anchor_call, "/", sizeof(anchor_call));
+ strlcat(anchor_call, r->anchor->path,
+ sizeof(anchor_call));
+ } else {
+ char a[MAXPATHLEN];
+ char *p;
+ int i;
+ if (rs->anchor == NULL)
+ a[0] = 0;
+ else
+ strlcpy(a, rs->anchor->path, MAXPATHLEN);
+ for (i = 1; i < r->anchor_relative; ++i) {
+ if ((p = strrchr(a, '/')) == NULL)
+ p = a;
+ *p = 0;
+ strlcat(anchor_call, "../",
+ sizeof(anchor_call));
+ }
+ if (strncmp(a, r->anchor->path, strlen(a))) {
+ printf("%s(): '%s' '%s'\n", __func__, a,
+ r->anchor->path);
+ return (1);
+ }
+ if (strlen(r->anchor->path) > strlen(a))
+ strlcat(anchor_call, r->anchor->path + (a[0] ?
+ strlen(a) + 1 : 0), sizeof(anchor_call));
+
+ }
+ if (r->anchor_wildcard)
+ strlcat(anchor_call, anchor_call[0] ? "/*" : "*",
+ sizeof(anchor_call));
+
+done:
+ nvlist_add_string(nvl, "anchor_call", anchor_call);
+
+ return (0);
+}
+
+int
pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r,
struct pfioc_rule *pr)
{
@@ -456,3 +523,229 @@ pf_kanchor_remove(struct pf_krule *r)
pf_remove_if_empty_kruleset(&r->anchor->ruleset);
r->anchor = NULL;
}
+
+struct pf_keth_ruleset *
+pf_find_keth_ruleset(const char *path)
+{
+ struct pf_keth_anchor *anchor;
+
+ while (*path == '/')
+ path++;
+ if (!*path)
+ return (V_pf_keth);
+ anchor = pf_find_keth_anchor(path);
+ if (anchor == NULL)
+ return (NULL);
+ else
+ return (&anchor->ruleset);
+}
+
+static struct pf_keth_anchor *
+_pf_find_keth_anchor(struct pf_keth_ruleset *rs, const char *path)
+{
+ struct pf_keth_anchor *key, *found;
+
+ key = (struct pf_keth_anchor *)rs_malloc(sizeof(*key));
+ if (key == NULL)
+ return (NULL);
+ strlcpy(key->path, path, sizeof(key->path));
+ found = RB_FIND(pf_keth_anchor_global, &V_pf_keth_anchors, key);
+ rs_free(key);
+ return (found);
+}
+
+struct pf_keth_anchor *
+pf_find_keth_anchor(const char *path)
+{
+ return (_pf_find_keth_anchor(V_pf_keth, path));
+}
+
+struct pf_keth_ruleset *
+pf_find_or_create_keth_ruleset(const char *path)
+{
+ char *p, *q, *r;
+ struct pf_keth_anchor *anchor = NULL, *dup = NULL, *parent = NULL;
+ struct pf_keth_ruleset *ruleset;
+
+ if (path[0] == 0)
+ return (V_pf_keth);
+ while (*path == '/')
+ path++;
+ ruleset = pf_find_keth_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_keth_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 pf_keth_anchor *)rs_malloc(sizeof(*anchor));
+ if (anchor == NULL) {
+ rs_free(p);
+ return (NULL);
+ }
+ RB_INIT(&anchor->children);
+ 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 ((dup = RB_INSERT(pf_keth_anchor_global, &V_pf_keth_anchors, anchor)) !=
+ NULL) {
+ printf("%s: RB_INSERT1 "
+ "'%s' '%s' collides with '%s' '%s'\n", __func__,
+ anchor->path, anchor->name, dup->path, dup->name);
+ rs_free(anchor);
+ rs_free(p);
+ return (NULL);
+ }
+ if (parent != NULL) {
+ anchor->parent = parent;
+ if ((dup = RB_INSERT(pf_keth_anchor_node, &parent->children,
+ anchor)) != NULL) {
+ printf("%s: "
+ "RB_INSERT2 '%s' '%s' collides with "
+ "'%s' '%s'\n", __func__, anchor->path,
+ anchor->name, dup->path, dup->name);
+ RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors,
+ anchor);
+ rs_free(anchor);
+ rs_free(p);
+ return (NULL);
+ }
+ }
+ pf_init_keth(&anchor->ruleset);
+ anchor->ruleset.anchor = anchor;
+ parent = anchor;
+ if (r != NULL)
+ q = r + 1;
+ else
+ *q = 0;
+ }
+ rs_free(p);
+ return (&anchor->ruleset);
+}
+
+int
+pf_keth_anchor_setup(struct pf_keth_rule *r, const struct pf_keth_ruleset *s,
+ const char *name)
+{
+ char *p, *path;
+ struct pf_keth_ruleset *ruleset;
+
+ r->anchor = NULL;
+ r->anchor_relative = 0;
+ r->anchor_wildcard = 0;
+ 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 */
+ r->anchor_relative = 1;
+ 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]) {
+ DPFPRINTF("pf_anchor_setup: .. beyond root\n");
+ rs_free(path);
+ return (1);
+ }
+ if ((p = strrchr(path, '/')) != NULL)
+ *p = 0;
+ else
+ path[0] = 0;
+ r->anchor_relative++;
+ name += 3;
+ }
+ if (path[0])
+ strlcat(path, "/", MAXPATHLEN);
+ strlcat(path, name, MAXPATHLEN);
+ }
+ if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
+ r->anchor_wildcard = 1;
+ *p = 0;
+ }
+ ruleset = pf_find_or_create_keth_ruleset(path);
+ rs_free(path);
+ if (ruleset == NULL || ruleset->anchor == NULL) {
+ DPFPRINTF("pf_anchor_setup: ruleset\n");
+ return (1);
+ }
+ r->anchor = ruleset->anchor;
+ r->anchor->refcnt++;
+ return (0);
+}
+
+void
+pf_keth_anchor_remove(struct pf_keth_rule *r)
+{
+ if (r->anchor == NULL)
+ return;
+ if (r->anchor->refcnt <= 0) {
+ printf("%s: broken refcount\n", __func__);
+ r->anchor = NULL;
+ return;
+ }
+ if (!--r->anchor->refcnt)
+ pf_remove_if_empty_keth_ruleset(&r->anchor->ruleset);
+ r->anchor = NULL;
+}
+
+void
+pf_remove_if_empty_keth_ruleset(struct pf_keth_ruleset *ruleset)
+{
+ struct pf_keth_anchor *parent;
+ int i;
+
+ while (ruleset != NULL) {
+ if (ruleset == V_pf_keth || ruleset->anchor == NULL ||
+ !RB_EMPTY(&ruleset->anchor->children) ||
+ ruleset->anchor->refcnt > 0)
+ return;
+ for (i = 0; i < PF_RULESET_MAX; ++i)
+ if (!TAILQ_EMPTY(ruleset->active.rules) ||
+ !TAILQ_EMPTY(ruleset->inactive.rules) ||
+ ruleset->inactive.open)
+ return;
+ RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors, ruleset->anchor);
+ if ((parent = ruleset->anchor->parent) != NULL)
+ RB_REMOVE(pf_keth_anchor_node, &parent->children,
+ ruleset->anchor);
+ rs_free(ruleset->anchor);
+ if (parent == NULL)
+ return;
+ ruleset = &parent->ruleset;
+ }
+}