The branch main has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=c5131afee39b4fa9e3889deb2ceea35a43ef35e2

commit c5131afee39b4fa9e3889deb2ceea35a43ef35e2
Author:     Kristof Provost <[email protected]>
AuthorDate: 2021-10-01 17:05:50 +0000
Commit:     Kristof Provost <[email protected]>
CommitDate: 2022-03-02 16:00:07 +0000

    pf: add anchor support for ether rules
    
    Support anchors in ether rules.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D32482
---
 lib/libpfctl/libpfctl.c     |  31 ++++-
 lib/libpfctl/libpfctl.h     |  28 +++-
 sbin/pfctl/parse.y          | 119 +++++++++++++++--
 sbin/pfctl/pf_ruleset.c     | 197 +++++++++++++++++++++++++++-
 sbin/pfctl/pfctl.c          | 254 +++++++++++++++++++++++++++++++-----
 sbin/pfctl/pfctl.h          |   9 ++
 sbin/pfctl/pfctl_parser.c   |  13 +-
 sbin/pfctl/pfctl_parser.h   |   7 +-
 sys/net/pfvar.h             |  82 ++++++++++--
 sys/netpfil/pf/pf.c         | 136 ++++++++++++++++++--
 sys/netpfil/pf/pf_ioctl.c   | 228 ++++++++++++++++++++------------
 sys/netpfil/pf/pf_nv.c      |   3 +
 sys/netpfil/pf/pf_ruleset.c | 307 +++++++++++++++++++++++++++++++++++++++++++-
 13 files changed, 1247 insertions(+), 167 deletions(-)

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

Reply via email to