The branch main has been updated by kp:

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

commit a7191e5d7b625c35434f99f51ccbf577228b2afc
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2023-10-04 10:27:54 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2023-10-10 09:48:21 +0000

    pf: add a way to list creator ids
    
    Allow userspace to retrieve a list of distinct creator ids for the
    current states.
    
    This is used by pfSense, and used to require dumping all states to
    userspace. It's rather inefficient to export a (potentially extremely
    large) state table to obtain a handful (typically 2) of 32-bit integers.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D42092
---
 lib/libpfctl/libpfctl.c        | 68 ++++++++++++++++++++++++++++++++++-
 lib/libpfctl/libpfctl.h        |  1 +
 sbin/pfctl/pfctl.c             | 21 ++++++++++-
 sbin/pfctl/pfctl.h             |  1 +
 sys/netpfil/pf/pf_nl.c         | 80 ++++++++++++++++++++++++++++++++++++++++++
 sys/netpfil/pf/pf_nl.h         |  1 +
 tests/sys/netpfil/pf/pfsync.sh |  8 +++++
 7 files changed, 178 insertions(+), 2 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 03f80baa2178..ca29645f9c34 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -1106,6 +1106,71 @@ pfctl_set_keepcounters(int dev, bool keep)
        return (ret);
 }
 
+struct pfctl_creator {
+       uint32_t id;
+};
+#define        _IN(_field)     offsetof(struct genlmsghdr, _field)
+#define        _OUT(_field)    offsetof(struct pfctl_creator, _field)
+static struct snl_attr_parser ap_creators[] = {
+       { .type = PF_ST_CREATORID, .off = _OUT(id), .cb = snl_attr_get_uint32 },
+};
+static struct snl_field_parser fp_creators[] = {
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(creator_parser, struct genlmsghdr, fp_creators, 
ap_creators);
+
+static int
+pfctl_get_creators_nl(struct snl_state *ss, uint32_t *creators, size_t *len)
+{
+
+       int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME);
+       size_t i = 0;
+
+       struct nlmsghdr *hdr;
+       struct snl_writer nw;
+
+       snl_init_writer(ss, &nw);
+       hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETCREATORS);
+       hdr->nlmsg_flags |= NLM_F_DUMP;
+       snl_finalize_msg(&nw);
+       uint32_t seq_id = hdr->nlmsg_seq;
+
+       snl_send_message(ss, hdr);
+
+       struct snl_errmsg_data e = {};
+       while ((hdr = snl_read_reply_multi(ss, seq_id, &e)) != NULL) {
+               struct pfctl_creator c;
+               bzero(&c, sizeof(c));
+
+               if (!snl_parse_nlmsg(ss, hdr, &creator_parser, &c))
+                       continue;
+
+               creators[i] = c.id;
+               i++;
+               if (i > *len)
+                       return (E2BIG);
+       }
+
+       *len = i;
+
+       return (0);
+}
+
+int
+pfctl_get_creatorids(uint32_t *creators, size_t *len)
+{
+       struct snl_state ss = {};
+       int error;
+
+       snl_init(&ss, NETLINK_GENERIC);
+       error = pfctl_get_creators_nl(&ss, creators, len);
+       snl_free(&ss);
+
+       return (error);
+
+}
+
 static void
 pfctl_nv_add_state_cmp(nvlist_t *nvl, const char *name,
     const struct pfctl_state_cmp *cmp)
@@ -1199,7 +1264,8 @@ static struct snl_field_parser fp_state[] = {
 SNL_DECLARE_PARSER(state_parser, struct genlmsghdr, fp_state, ap_state);
 
 static const struct snl_hdr_parser *all_parsers[] = {
-       &state_parser, &skey_parser, &speer_parser
+       &state_parser, &skey_parser, &speer_parser,
+       &creator_parser,
 };
 
 static int
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 4906ec3ccfce..e75f93d8775e 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -413,6 +413,7 @@ int pfctl_add_rule(int dev, const struct pfctl_rule *r,
            const char *anchor, const char *anchor_call, uint32_t ticket,
            uint32_t pool_ticket);
 int    pfctl_set_keepcounters(int dev, bool keep);
+int    pfctl_get_creatorids(uint32_t *creators, size_t *len);
 typedef int (*pfctl_get_state_fn)(struct pfctl_state *, void *);
 int pfctl_get_states_iter(pfctl_get_state_fn f, void *arg);
 int    pfctl_get_states(int dev, struct pfctl_states *states);
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 9a75eb7d00b5..759b36d9cebe 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -233,7 +233,7 @@ static const char * const clearopt_list[] = {
 static const char * const showopt_list[] = {
        "ether", "nat", "queue", "rules", "Anchors", "Sources", "states",
        "info", "Interfaces", "labels", "timeouts", "memory", "Tables",
-       "osfp", "Running", "all", NULL
+       "osfp", "Running", "all", "creatorids", NULL
 };
 
 static const char * const tblcmdopt_list[] = {
@@ -1639,6 +1639,22 @@ pfctl_show_limits(int dev, int opts)
        return (0);
 }
 
+void
+pfctl_show_creators(int opts)
+{
+       int ret;
+       uint32_t creators[16];
+       size_t count = nitems(creators);
+
+       ret = pfctl_get_creatorids(creators, &count);
+       if (ret != 0)
+               errx(ret, "Failed to retrieve creators");
+
+       printf("Creator IDs:\n");
+       for (size_t i = 0; i < count; i++)
+               printf("%08x\n", creators[i]);
+}
+
 /* callbacks for rule/nat/rdr/addr */
 int
 pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af)
@@ -3121,6 +3137,9 @@ main(int argc, char *argv[])
                case 'I':
                        pfctl_show_ifaces(ifaceopt, opts);
                        break;
+               case 'c':
+                       pfctl_show_creators(opts);
+                       break;
                }
        }
 
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index b9da5e96a90e..cf198d557299 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -88,6 +88,7 @@ int    pfctl_command_tables(int, char *[], char *, const char 
*, char *,
 int     pfctl_show_altq(int, const char *, int, int);
 void    warn_namespace_collision(const char *);
 int     pfctl_show_ifaces(const char *, int);
+void   pfctl_show_creators(int);
 FILE   *pfctl_fopen(const char *, const char *);
 
 #ifdef __FreeBSD__
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index cbea76e7386f..bb50b3b2b321 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -246,6 +246,30 @@ handle_getstate(struct nlpcb *nlp, struct nl_parsed_state 
*attrs,
        return (dump_state(nlp, hdr, s, npt));
 }
 
+static int
+dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator,
+    struct nl_pstate *npt)
+{
+       struct nl_writer *nw = npt->nw;
+
+       if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
+               goto enomem;
+
+       struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct 
genlmsghdr);
+       ghdr_new->cmd = PFNL_CMD_GETCREATORS;
+       ghdr_new->version = 0;
+       ghdr_new->reserved = 0;
+
+       nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator));
+
+       if (nlmsg_end(nw))
+               return (0);
+
+enomem:
+       nlmsg_abort(nw);
+       return (ENOMEM);
+}
+
 static int
 pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt)
 {
@@ -264,6 +288,56 @@ pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate 
*npt)
        return (error);
 }
 
+static int
+pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+       uint32_t creators[16];
+       int error = 0;
+
+       bzero(creators, sizeof(creators));
+
+       for (int i = 0; i < pf_hashmask; i++) {
+               struct pf_idhash *ih = &V_pf_idhash[i];
+               struct pf_kstate *s;
+
+               if (LIST_EMPTY(&ih->states))
+                       continue;
+
+               PF_HASHROW_LOCK(ih);
+               LIST_FOREACH(s, &ih->states, entry) {
+                       int j;
+                       if (s->timeout == PFTM_UNLINKED)
+                               continue;
+
+                       for (j = 0; j < nitems(creators); j++) {
+                               if (creators[j] == s->creatorid)
+                                       break;
+                               if (creators[j] == 0) {
+                                       creators[j] = s->creatorid;
+                                       break;
+                               }
+                       }
+                       if (j == nitems(creators))
+                               printf("Warning: too many creators!\n");
+               }
+               PF_HASHROW_UNLOCK(ih);
+       }
+
+       hdr->nlmsg_flags |= NLM_F_MULTI;
+       for (int i = 0; i < nitems(creators); i++) {
+               if (creators[i] == 0)
+                       break;
+               error = dump_creatorid(npt->nlp, hdr, creators[i], npt);
+       }
+
+       if (!nlmsg_end_dump(npt->nw, error, hdr)) {
+               NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
+               return (ENOMEM);
+       }
+
+       return (error);
+}
+
 static const struct nlhdr_parser *all_parsers[] = { &state_parser };
 
 static int family_id;
@@ -275,6 +349,12 @@ static const struct genl_cmd pf_cmds[] = {
                .cmd_cb = pf_handle_getstates,
                .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | 
GENL_CMD_CAP_HASPOL,
        },
+       {
+               .cmd_num = PFNL_CMD_GETCREATORS,
+               .cmd_name = "GETCREATORS",
+               .cmd_cb = pf_handle_getcreators,
+               .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | 
GENL_CMD_CAP_HASPOL,
+       },
 };
 
 void
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index 5ef757eead21..98525641b43d 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -37,6 +37,7 @@
 enum {
        PFNL_CMD_UNSPEC = 0,
        PFNL_CMD_GETSTATES = 1,
+       PFNL_CMD_GETCREATORS = 2,
        __PFNL_CMD_MAX,
 };
 #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh
index 5d30f5b44888..87dfcf748d3c 100644
--- a/tests/sys/netpfil/pf/pfsync.sh
+++ b/tests/sys/netpfil/pf/pfsync.sh
@@ -78,6 +78,8 @@ common_body()
                "set skip on ${epair_sync}b" \
                "pass out keep state"
 
+       hostid_one=$(jexec one pfctl -si -v | awk '/Hostid:/ { gsub(/0x/, "", 
$2); printf($2); }')
+
        ifconfig ${epair_one}b 198.51.100.254/24 up
 
        ping -c 1 -S 198.51.100.254 198.51.100.1
@@ -89,6 +91,12 @@ common_body()
            grep 198.51.100.254 ; then
                atf_fail "state not found on synced host"
        fi
+
+       if ! jexec two pfctl -sc | grep ""${hostid_one}"";
+       then
+               jexec two pfctl -sc
+               atf_fail "HostID for host one not found on two"
+       fi
 }
 
 basic_cleanup()

Reply via email to