From nobody Wed Mar 02 16:01:01 2022 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 5172D19E1540; Wed, 2 Mar 2022 16:01:04 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4K7zPB5Thdz3NNG; Wed, 2 Mar 2022 16:01:02 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1646236863; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=MvgMwJzK5QL8rNEMec0ixaa8eq5iHhHgU+C0nwcQ7dQ=; b=WLKK5NxG8IkMAf4jpiYfJk2selVmCIxHrXK3v8lo80sG6MUA25bwXCbo8MOToi4f6aQLdA fQ6ZJmFvtPvprpOkmZHN+LBQf7vHGLzfx6HZ6iIUjYg6hiPq566cTnyI+BYwrnSroCPfO6 O7ZPW+13crMVqoCw5sODCDeQmSJjgOL+NR5uWgmTD51uDxipRk61/J5OnONKTx1F30PGot N8JkiC/+lO4mPap8rnd+vN/zK/gRVtCy4ed9ZAsBPBF2EtfJ3ScpKRBeBbF8h8fZEYclXU IqgCWgGgk/2+K1yjGWOEG5tPdBvhEbL8Z9DT9U32Jpo2E7NqTX1rizHo97d/FA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id E620725E57; Wed, 2 Mar 2022 16:01:01 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 222G11Je091407; Wed, 2 Mar 2022 16:01:01 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 222G11df091405; Wed, 2 Mar 2022 16:01:01 GMT (envelope-from git) Date: Wed, 2 Mar 2022 16:01:01 GMT Message-Id: <202203021601.222G11df091405@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: c5131afee39b - main - pf: add anchor support for ether rules List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: c5131afee39b4fa9e3889deb2ceea35a43ef35e2 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1646236863; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=MvgMwJzK5QL8rNEMec0ixaa8eq5iHhHgU+C0nwcQ7dQ=; b=NpjpEa4JH8yLxNh/lr2VaiLFaLPY0G5BW5sjdKh1ZFbvexA3EGoYiTDTr/BPCag3qDpStn 01BnfqNNo8IYAr5xpgh70LyymD3vke615LjRAac/18NI5uIAMrv/HMUjRZNE1H3KGlRxh9 CSCuq82aCI3V4v2Atc3ZHbcfw2E+I29493SKD2A867UeRRxsFu5dtJoL7pVIfIddzeW2mR 1SLOiXataZXzB51Vy/N/Hk3XFijwrERDzHG41K/PfHIGFsIM97W6PEQMr5tVwEGTmpaD7R 33opdS1AWrr2COvNbqsqfoO6+ocH71iONFreQs8Raf/pBvEcBlEPMxV417GUNw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1646236863; a=rsa-sha256; cv=none; b=RcROI2w0XUXDXHqv093xRWEI6O1/o5mW4PdfAAMeo7PYOqv4elPBZT6pwanFa8T/WHbyzb zCJ85LYipL5aQi5U+l6ldCcRCIL3TSrzT3Y0B+4wL8iKUBkzWRVRs7BRvqoruXwYTYRpkC 8lLKC+gjM95wzasL5FaoRKgXFEMWW5GqCsI7rBwPLTCZ9MyDAy+qHGdBx4HJk482JHgHhy 5SR7mbaJ3QQllb4hkoeLZY9DG8N4H6Kgx6g/Qjq7LKJCmjSehxD5TQ8ac+ogVOEWzZI8J6 KvTP2y+SPexg2tZd+IL39Xg8vQxs/h2T/w6+H13QFcxLqr5Y43IkiEhAuPoUPw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=c5131afee39b4fa9e3889deb2ceea35a43ef35e2 commit c5131afee39b4fa9e3889deb2ceea35a43ef35e2 Author: Kristof Provost AuthorDate: 2021-10-01 17:05:50 +0000 Commit: Kristof Provost 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 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 @@ -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 ***