The branch main has been updated by kp:

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

commit 9bb06778f822ad6b47d2a825d47e284ca8dd29a1
Author:     Kristof Provost <[email protected]>
AuthorDate: 2022-03-29 12:15:10 +0000
Commit:     Kristof Provost <[email protected]>
CommitDate: 2022-03-30 08:28:19 +0000

    pf: support listing ethernet anchors
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c       |  75 +++++++++++++++++
 lib/libpfctl/libpfctl.h       |  14 ++++
 sbin/pfctl/pfctl.c            |  40 ++++++++++
 sys/net/pfvar.h               |   2 +
 sys/netpfil/pf/pf_ioctl.c     | 182 ++++++++++++++++++++++++++++++++++++++++++
 tests/sys/netpfil/pf/ether.sh |   2 +
 6 files changed, 315 insertions(+)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 8f064594260b..1e1a90594210 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -622,6 +622,81 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct 
pfctl_eth_rule *rule)
        rule->action = nvlist_get_number(nvl, "action");
 }
 
+int
+pfctl_get_eth_rulesets_info(int dev, struct pfctl_eth_rulesets_info *ri,
+    const char *path)
+{
+       uint8_t buf[1024];
+       struct pfioc_nv nv;
+       nvlist_t *nvl;
+       void *packed;
+       size_t len;
+
+       bzero(ri, sizeof(*ri));
+
+       nvl = nvlist_create(0);
+       nvlist_add_string(nvl, "path", path);
+       packed = nvlist_pack(nvl, &len);
+       memcpy(buf, packed, len);
+       free(packed);
+       nvlist_destroy(nvl);
+
+       nv.data = buf;
+       nv.len = len;
+       nv.size = sizeof(buf);
+
+       if (ioctl(dev, DIOCGETETHRULESETS, &nv) != 0)
+               return (errno);
+
+       nvl = nvlist_unpack(buf, nv.len, 0);
+       if (nvl == NULL)
+               return (EIO);
+
+       ri->nr = nvlist_get_number(nvl, "nr");
+
+       nvlist_destroy(nvl);
+       return (0);
+}
+
+int
+pfctl_get_eth_ruleset(int dev, const char *path, int nr,
+    struct pfctl_eth_ruleset_info *ri)
+{
+       uint8_t buf[1024];
+       struct pfioc_nv nv;
+       nvlist_t *nvl;
+       void *packed;
+       size_t len;
+
+       bzero(ri, sizeof(*ri));
+
+       nvl = nvlist_create(0);
+       nvlist_add_string(nvl, "path", path);
+       nvlist_add_number(nvl, "nr", nr);
+       packed = nvlist_pack(nvl, &len);
+       memcpy(buf, packed, len);
+       free(packed);
+       nvlist_destroy(nvl);
+
+       nv.data = buf;
+       nv.len = len;
+       nv.size = sizeof(buf);
+
+       if (ioctl(dev, DIOCGETETHRULESET, &nv) != 0)
+               return (errno);
+
+       nvl = nvlist_unpack(buf, nv.len, 0);
+       if (nvl == NULL)
+               return (EIO);
+
+       ri->nr = nvlist_get_number(nvl, "nr");
+       strlcpy(ri->path, nvlist_get_string(nvl, "path"), MAXPATHLEN);
+       strlcpy(ri->name, nvlist_get_string(nvl, "name"),
+           PF_ANCHOR_NAME_SIZE);
+
+       return (0);
+}
+
 int
 pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
     const char *path)
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index b7f703b64def..92a1ea9b7cef 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -66,6 +66,10 @@ struct pfctl_status {
        uint64_t        bcounters[2][2];
 };
 
+struct pfctl_eth_rulesets_info {
+       uint32_t        nr;
+};
+
 struct pfctl_eth_rules_info {
        uint32_t        nr;
        uint32_t        ticket;
@@ -111,6 +115,12 @@ struct pfctl_eth_rule {
 };
 TAILQ_HEAD(pfctl_eth_rules, pfctl_eth_rule);
 
+struct pfctl_eth_ruleset_info {
+       uint32_t        nr;
+       char            name[PF_ANCHOR_NAME_SIZE];
+       char            path[MAXPATHLEN];
+};
+
 struct pfctl_eth_ruleset {
        struct pfctl_eth_rules   rules;
        struct pfctl_eth_anchor *anchor;
@@ -356,6 +366,10 @@ struct pfctl_syncookies {
 struct pfctl_status* pfctl_get_status(int dev);
 void   pfctl_free_status(struct pfctl_status *status);
 
+int    pfctl_get_eth_rulesets_info(int dev,
+           struct pfctl_eth_rulesets_info *ri, const char *path);
+int    pfctl_get_eth_ruleset(int dev, const char *path, int nr,
+           struct pfctl_eth_ruleset_info *ri);
 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,
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 88a96bd303a0..67358a325f77 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -111,6 +111,7 @@ 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_show_eth_anchors(int, int, char *);
 int     pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *, 
bool);
 int     pfctl_eth_ruleset_trans(struct pfctl *, char *,
            struct pfctl_eth_anchor *);
@@ -2604,6 +2605,44 @@ pfctl_show_anchors(int dev, int opts, char *anchorname)
        return (0);
 }
 
+int
+pfctl_show_eth_anchors(int dev, int opts, char *anchorname)
+{
+       struct pfctl_eth_rulesets_info ri;
+       struct pfctl_eth_ruleset_info rs;
+       int ret;
+
+       if ((ret = pfctl_get_eth_rulesets_info(dev, &ri, anchorname)) != 0) {
+               if (ret == ENOENT)
+                       fprintf(stderr, "Anchor '%s' not found.\n",
+                           anchorname);
+               else
+                       err(1, "DIOCGETETHRULESETS");
+               return (-1);
+       }
+
+       for (int nr = 0; nr < ri.nr; nr++) {
+               char sub[MAXPATHLEN];
+
+               if (pfctl_get_eth_ruleset(dev, anchorname, nr, &rs) != 0)
+                       err(1, "DIOCGETETHRULESET");
+
+               if (!strcmp(rs.name, PF_RESERVED_ANCHOR))
+                       continue;
+               sub[0] = 0;
+               if (rs.path[0]) {
+                       strlcat(sub, rs.path, sizeof(sub));
+                       strlcat(sub, "/", sizeof(sub));
+               }
+               strlcat(sub, rs.name, sizeof(sub));
+               if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
+                       printf("  %s\n", sub);
+               if ((opts & PF_OPT_VERBOSE) && pfctl_show_eth_anchors(dev, 
opts, sub))
+                       return (-1);
+       }
+       return (0);
+}
+
 const char *
 pfctl_lookup_option(char *cmd, const char * const *list)
 {
@@ -2830,6 +2869,7 @@ main(int argc, char *argv[])
                switch (*showopt) {
                case 'A':
                        pfctl_show_anchors(dev, opts, anchorname);
+                       pfctl_show_eth_anchors(dev, opts, anchorname);
                        break;
                case 'r':
                        pfctl_load_fingerprints(dev, opts);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index ccc81ea137b9..db6b5c22f07f 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1865,6 +1865,8 @@ struct pfioc_iface {
 #define        DIOCADDETHRULE          _IOWR('D', 97, struct pfioc_nv)
 #define        DIOCGETETHRULE          _IOWR('D', 98, struct pfioc_nv)
 #define        DIOCGETETHRULES         _IOWR('D', 99, struct pfioc_nv)
+#define        DIOCGETETHRULESETS      _IOWR('D', 100, struct pfioc_nv)
+#define        DIOCGETETHRULESET       _IOWR('D', 101, struct pfioc_nv)
 
 struct pf_ifspeed_v0 {
        char                    ifname[IFNAMSIZ];
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 6012825ead9b..3cb5552d20c5 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2463,6 +2463,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int 
flags, struct thread *td
                case DIOCCLRIFFLAG:
                case DIOCGETETHRULES:
                case DIOCGETETHRULE:
+               case DIOCGETETHRULESETS:
+               case DIOCGETETHRULESET:
                        break;
                case DIOCRCLRTABLES:
                case DIOCRADDTABLES:
@@ -2512,6 +2514,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int 
flags, struct thread *td
                case DIOCGETRULENV:
                case DIOCGETETHRULES:
                case DIOCGETETHRULE:
+               case DIOCGETETHRULESETS:
+               case DIOCGETETHRULESET:
                        break;
                case DIOCRCLRTABLES:
                case DIOCRADDTABLES:
@@ -2864,6 +2868,184 @@ DIOCADDETHRULE_error:
                break;
        }
 
+       case DIOCGETETHRULESETS: {
+               struct epoch_tracker     et;
+               struct pfioc_nv         *nv = (struct pfioc_nv *)addr;
+               nvlist_t                *nvl = NULL;
+               void                    *nvlpacked = NULL;
+               struct pf_keth_ruleset  *ruleset;
+               struct pf_keth_anchor   *anchor;
+               int                      nr = 0;
+
+#define ERROUT(x)      do { error = (x); goto DIOCGETETHRULESETS_error; } 
while (0)
+
+               if (nv->len > pf_ioctl_maxcount)
+                       ERROUT(ENOMEM);
+
+               nvlpacked = malloc(nv->len, M_NVLIST, M_WAITOK);
+               if (nvlpacked == NULL)
+                       ERROUT(ENOMEM);
+
+               error = copyin(nv->data, nvlpacked, nv->len);
+               if (error)
+                       ERROUT(error);
+
+               nvl = nvlist_unpack(nvlpacked, nv->len, 0);
+               if (nvl == NULL)
+                       ERROUT(EBADMSG);
+               if (! nvlist_exists_string(nvl, "path"))
+                       ERROUT(EBADMSG);
+
+               NET_EPOCH_ENTER(et);
+
+               if ((ruleset = pf_find_keth_ruleset(
+                   nvlist_get_string(nvl, "path"))) == NULL) {
+                       NET_EPOCH_EXIT(et);
+                       ERROUT(ENOENT);
+               }
+
+               if (ruleset->anchor == NULL) {
+                       RB_FOREACH(anchor, pf_keth_anchor_global, 
&V_pf_keth_anchors)
+                               if (anchor->parent == NULL)
+                                       nr++;
+               } else {
+                       RB_FOREACH(anchor, pf_keth_anchor_node,
+                           &ruleset->anchor->children)
+                               nr++;
+               }
+
+               NET_EPOCH_EXIT(et);
+
+               nvlist_destroy(nvl);
+               nvl = NULL;
+               free(nvlpacked, M_NVLIST);
+               nvlpacked = NULL;
+
+               nvl = nvlist_create(0);
+               if (nvl == NULL)
+                       ERROUT(ENOMEM);
+
+               nvlist_add_number(nvl, "nr", nr);
+
+               nvlpacked = nvlist_pack(nvl, &nv->len);
+               if (nvlpacked == NULL)
+                       ERROUT(ENOMEM);
+
+               if (nv->size == 0)
+                       ERROUT(0);
+               else if (nv->size < nv->len)
+                       ERROUT(ENOSPC);
+
+               error = copyout(nvlpacked, nv->data, nv->len);
+
+#undef ERROUT
+DIOCGETETHRULESETS_error:
+               free(nvlpacked, M_NVLIST);
+               nvlist_destroy(nvl);
+               break;
+       }
+
+       case DIOCGETETHRULESET: {
+               struct epoch_tracker     et;
+               struct pfioc_nv         *nv = (struct pfioc_nv *)addr;
+               nvlist_t                *nvl = NULL;
+               void                    *nvlpacked = NULL;
+               struct pf_keth_ruleset  *ruleset;
+               struct pf_keth_anchor   *anchor;
+               int                      nr = 0, req_nr = 0;
+               bool                     found = false;
+
+#define ERROUT(x)      do { error = (x); goto DIOCGETETHRULESET_error; } while 
(0)
+
+               if (nv->len > pf_ioctl_maxcount)
+                       ERROUT(ENOMEM);
+
+               nvlpacked = malloc(nv->len, M_NVLIST, M_WAITOK);
+               if (nvlpacked == NULL)
+                       ERROUT(ENOMEM);
+
+               error = copyin(nv->data, nvlpacked, nv->len);
+               if (error)
+                       ERROUT(error);
+
+               nvl = nvlist_unpack(nvlpacked, nv->len, 0);
+               if (nvl == NULL)
+                       ERROUT(EBADMSG);
+               if (! nvlist_exists_string(nvl, "path"))
+                       ERROUT(EBADMSG);
+               if (! nvlist_exists_number(nvl, "nr"))
+                       ERROUT(EBADMSG);
+
+               req_nr = nvlist_get_number(nvl, "nr");
+
+               NET_EPOCH_ENTER(et);
+
+               if ((ruleset = pf_find_keth_ruleset(
+                   nvlist_get_string(nvl, "path"))) == NULL) {
+                       NET_EPOCH_EXIT(et);
+                       ERROUT(ENOENT);
+               }
+
+               nvlist_destroy(nvl);
+               nvl = NULL;
+               free(nvlpacked, M_NVLIST);
+               nvlpacked = NULL;
+
+               nvl = nvlist_create(0);
+               if (nvl == NULL) {
+                       NET_EPOCH_EXIT(et);
+                       ERROUT(ENOMEM);
+               }
+
+               if (ruleset->anchor == NULL) {
+                       RB_FOREACH(anchor, pf_keth_anchor_global,
+                           &V_pf_keth_anchors) {
+                               if (anchor->parent == NULL && nr++ == req_nr) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+               } else {
+                       RB_FOREACH(anchor, pf_keth_anchor_node,
+                            &ruleset->anchor->children) {
+                               if (nr++ == req_nr) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+               }
+
+               NET_EPOCH_EXIT(et);
+               if (found) {
+                       nvlist_add_number(nvl, "nr", nr);
+                       nvlist_add_string(nvl, "name", anchor->name);
+                       if (ruleset->anchor)
+                               nvlist_add_string(nvl, "path",
+                                   ruleset->anchor->path);
+                       else
+                               nvlist_add_string(nvl, "path", "");
+               } else {
+                       ERROUT(EBUSY);
+               }
+
+               nvlpacked = nvlist_pack(nvl, &nv->len);
+               if (nvlpacked == NULL)
+                       ERROUT(ENOMEM);
+
+               if (nv->size == 0)
+                       ERROUT(0);
+               else if (nv->size < nv->len)
+                       ERROUT(ENOSPC);
+
+               error = copyout(nvlpacked, nv->data, nv->len);
+
+#undef ERROUT
+DIOCGETETHRULESET_error:
+               free(nvlpacked, M_NVLIST);
+               nvlist_destroy(nvl);
+               break;
+       }
+
        case DIOCADDRULENV: {
                struct pfioc_nv *nv = (struct pfioc_nv *)addr;
                nvlist_t        *nvl = NULL;
diff --git a/tests/sys/netpfil/pf/ether.sh b/tests/sys/netpfil/pf/ether.sh
index 6e81f07ca26e..7a7f91844148 100644
--- a/tests/sys/netpfil/pf/ether.sh
+++ b/tests/sys/netpfil/pf/ether.sh
@@ -490,6 +490,8 @@ anchor_body()
                "}" \
                "ether pass in from ${epair_a_mac}"
        atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+       atf_check -s exit:0 -o match:'baz' jexec alcatraz pfctl -sA
 }
 
 anchor_cleanup()

Reply via email to