diff options
| author | Kristof Provost <kp@FreeBSD.org> | 2021-10-01 17:05:50 +0000 |
|---|---|---|
| committer | Kristof Provost <kp@FreeBSD.org> | 2022-03-02 16:00:07 +0000 |
| commit | c5131afee39b4fa9e3889deb2ceea35a43ef35e2 (patch) | |
| tree | 433111d8442204555f0ef4bbdd79eb2784dcfdb6 /sbin/pfctl | |
| parent | 87a89d6e14ac5730572d454ec12a3a30d492816e (diff) | |
Diffstat (limited to 'sbin/pfctl')
| -rw-r--r-- | sbin/pfctl/parse.y | 119 | ||||
| -rw-r--r-- | sbin/pfctl/pf_ruleset.c | 197 | ||||
| -rw-r--r-- | sbin/pfctl/pfctl.c | 254 | ||||
| -rw-r--r-- | sbin/pfctl/pfctl.h | 9 | ||||
| -rw-r--r-- | sbin/pfctl/pfctl_parser.c | 13 | ||||
| -rw-r--r-- | sbin/pfctl/pfctl_parser.h | 7 |
6 files changed, 550 insertions, 49 deletions
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); |
