The branch main has been updated by kp:

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

commit 9c125336727b48c576bf3598d7d2c52daa5909d7
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2024-10-11 09:02:27 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2024-10-15 14:29:11 +0000

    pf: convert DIOCGETSRCNODES to netlink
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c   | 68 +++++++++++++++++++++++++++++++++++++++++
 lib/libpfctl/libpfctl.h   | 24 +++++++++++++++
 sbin/pfctl/pfctl.c        | 56 +++++++++++-----------------------
 sbin/pfctl/pfctl_parser.c | 29 ++++++++----------
 sbin/pfctl/pfctl_parser.h |  2 +-
 sys/netpfil/pf/pf_nl.c    | 78 +++++++++++++++++++++++++++++++++++++++++++++++
 sys/netpfil/pf/pf_nl.h    | 27 ++++++++++++++++
 7 files changed, 229 insertions(+), 55 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index e0d93041c551..4634fa99cb19 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -2998,3 +2998,71 @@ pfctl_get_ruleset(struct pfctl_handle *h, const char 
*path, uint32_t nr, struct
        return (e.error);
 }
 
+#define        _OUT(_field)    offsetof(struct pfctl_threshold, _field)
+static const struct snl_attr_parser ap_pfctl_threshold[] = {
+       { .type = PF_TH_LIMIT, .off = _OUT(limit), .cb = snl_attr_get_uint32 },
+       { .type = PF_TH_SECONDS, .off = _OUT(seconds), .cb = 
snl_attr_get_uint32 },
+       { .type = PF_TH_COUNT, .off = _OUT(count), .cb = snl_attr_get_uint32 },
+       { .type = PF_TH_LAST, .off = _OUT(last), .cb = snl_attr_get_uint32 },
+};
+SNL_DECLARE_ATTR_PARSER(pfctl_threshold_parser, ap_pfctl_threshold);
+#undef _OUT
+
+#define        _OUT(_field)    offsetof(struct pfctl_src_node, _field)
+static struct snl_attr_parser ap_srcnode[] = {
+       { .type = PF_SN_ADDR, .off = _OUT(addr), .cb = snl_attr_get_in6_addr },
+       { .type = PF_SN_RADDR, .off = _OUT(raddr), .cb = snl_attr_get_in6_addr 
},
+       { .type = PF_SN_RULE_NR, .off = _OUT(rule), .cb = snl_attr_get_uint32 },
+       { .type = PF_SN_BYTES_IN, .off = _OUT(bytes[0]), .cb = 
snl_attr_get_uint64 },
+       { .type = PF_SN_BYTES_OUT, .off = _OUT(bytes[1]), .cb = 
snl_attr_get_uint64 },
+       { .type = PF_SN_PACKETS_IN, .off = _OUT(packets[0]), .cb = 
snl_attr_get_uint64 },
+       { .type = PF_SN_PACKETS_OUT, .off = _OUT(packets[1]), .cb = 
snl_attr_get_uint64 },
+       { .type = PF_SN_STATES, .off = _OUT(states), .cb = snl_attr_get_uint32 
},
+       { .type = PF_SN_CONNECTIONS, .off = _OUT(conn), .cb = 
snl_attr_get_uint32 },
+       { .type = PF_SN_AF, .off = _OUT(af), .cb = snl_attr_get_uint8 },
+       { .type = PF_SN_RULE_TYPE, .off = _OUT(ruletype), .cb = 
snl_attr_get_uint8 },
+       { .type = PF_SN_CREATION, .off = _OUT(creation), .cb = 
snl_attr_get_uint64 },
+       { .type = PF_SN_EXPIRE, .off = _OUT(expire), .cb = snl_attr_get_uint64 
},
+       { .type = PF_SN_CONNECTION_RATE, .off = _OUT(conn_rate), .arg = 
&pfctl_threshold_parser, .cb = snl_attr_get_nested },
+};
+static struct snl_field_parser fp_srcnode[] = {};
+#undef _OUT
+SNL_DECLARE_PARSER(srcnode_parser, struct genlmsghdr, fp_srcnode, ap_srcnode);
+
+int
+pfctl_get_srcnodes(struct pfctl_handle *h, pfctl_get_srcnode_fn fn, void *arg)
+{
+       struct snl_writer nw;
+       struct pfctl_src_node sn;
+       struct snl_errmsg_data e = {};
+       struct nlmsghdr *hdr;
+       uint32_t seq_id;
+       int family_id;
+       int ret;
+
+       family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
+       if (family_id == 0)
+               return (ENOTSUP);
+
+       snl_init_writer(&h->ss, &nw);
+       hdr = snl_create_genl_msg_request(&nw, family_id, 
PFNL_CMD_GET_SRCNODES);
+
+       if ((hdr = snl_finalize_msg(&nw)) == NULL)
+               return (ENXIO);
+
+       seq_id = hdr->nlmsg_seq;
+
+       if (!snl_send_message(&h->ss, hdr))
+               return (ENXIO);
+
+       while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
+               if (!snl_parse_nlmsg(&h->ss, hdr, &srcnode_parser, &sn))
+                       continue;
+
+               ret = fn(&sn, arg);
+               if (ret != 0)
+                       return (ret);
+       }
+
+       return (e.error);
+}
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 4da21774a958..5c3f1376960c 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -386,6 +386,28 @@ struct pfctl_syncookies {
        uint32_t                        halfopen_states;
 };
 
+struct pfctl_threshold {
+       uint32_t                limit;
+       uint32_t                seconds;
+       uint32_t                count;
+       uint32_t                last;
+};
+
+struct pfctl_src_node {
+       struct pf_addr          addr;
+       struct pf_addr          raddr;
+       int                     rule;
+       uint64_t                bytes[2];
+       uint64_t                packets[2];
+       uint32_t                states;
+       uint32_t                conn;
+       sa_family_t             af;
+       uint8_t                 ruletype;
+       uint64_t                creation;
+       uint64_t                expire;
+       struct pfctl_threshold  conn_rate;
+};
+
 #define        PF_DEVICE       "/dev/pf"
 
 struct pfctl_handle;
@@ -506,5 +528,7 @@ int pfctl_get_addr(struct pfctl_handle *h, uint32_t ticket, 
uint32_t r_num,
            uint8_t r_action, const char *anchor, uint32_t nr, struct 
pfioc_pooladdr *pa);
 int    pfctl_get_rulesets(struct pfctl_handle *h, const char *path, uint32_t 
*nr);
 int    pfctl_get_ruleset(struct pfctl_handle *h, const char *path, uint32_t 
nr, struct pfioc_ruleset *rs);
+typedef int (*pfctl_get_srcnode_fn)(struct pfctl_src_node*, void *);
+int    pfctl_get_srcnodes(struct pfctl_handle *h, pfctl_get_srcnode_fn fn, 
void *arg);
 
 #endif
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 4ca77f6c5f4a..555c5181eac8 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1519,51 +1519,31 @@ pfctl_show_nat(int dev, char *path, int opts, char 
*anchorname, int depth,
        return (0);
 }
 
-int
-pfctl_show_src_nodes(int dev, int opts)
+static int
+pfctl_print_src_node(struct pfctl_src_node *sn, void *arg)
 {
-       struct pfioc_src_nodes psn;
-       struct pf_src_node *p;
-       char *inbuf = NULL, *newinbuf = NULL;
-       unsigned int len = 0;
-       int i;
+       int *opts = (int *)arg;
 
-       memset(&psn, 0, sizeof(psn));
-       for (;;) {
-               psn.psn_len = len;
-               if (len) {
-                       newinbuf = realloc(inbuf, len);
-                       if (newinbuf == NULL)
-                               err(1, "realloc");
-                       psn.psn_buf = inbuf = newinbuf;
-               }
-               if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) {
-                       warn("DIOCGETSRCNODES");
-                       free(inbuf);
-                       return (-1);
-               }
-               if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len)
-                       break;
-               if (len == 0 && psn.psn_len == 0)
-                       goto done;
-               if (len == 0 && psn.psn_len != 0)
-                       len = psn.psn_len;
-               if (psn.psn_len == 0)
-                       goto done;      /* no src_nodes */
-               len *= 2;
-       }
-       p = psn.psn_src_nodes;
-       if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL))
+       if (*opts & PF_OPT_SHOWALL) {
                pfctl_print_title("SOURCE TRACKING NODES:");
-       for (i = 0; i < psn.psn_len; i += sizeof(*p)) {
-               print_src_node(p, opts);
-               p++;
+               *opts &= ~PF_OPT_SHOWALL;
        }
-done:
-       free(inbuf);
+
+       print_src_node(sn, *opts);
+
        return (0);
 }
 
+int
+pfctl_show_src_nodes(int dev, int opts)
+{
+       int error;
+
+       error = pfctl_get_srcnodes(pfh, pfctl_print_src_node, &opts);
+
+       return (error);
+}
+
 struct pfctl_show_state_arg {
        int opts;
        int dotitle;
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 70c2ad42ca55..c25e96e645fb 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -58,6 +58,7 @@
 #include <errno.h>
 #include <err.h>
 #include <ifaddrs.h>
+#include <inttypes.h>
 #include <unistd.h>
 
 #include "pfctl_parser.h"
@@ -648,10 +649,10 @@ print_running(struct pfctl_status *status)
 }
 
 void
-print_src_node(struct pf_src_node *sn, int opts)
+print_src_node(struct pfctl_src_node *sn, int opts)
 {
        struct pf_addr_wrap aw;
-       int min, sec;
+       uint64_t min, sec;
 
        memset(&aw, 0, sizeof(aw));
        if (sn->af == AF_INET)
@@ -672,36 +673,32 @@ print_src_node(struct pf_src_node *sn, int opts)
                sn->creation /= 60;
                min = sn->creation % 60;
                sn->creation /= 60;
-               printf("   age %.2u:%.2u:%.2u", sn->creation, min, sec);
+               printf("   age %.2" PRIu64 ":%.2" PRIu64 ":%.2" PRIu64,
+                   sn->creation, min, sec);
                if (sn->states == 0) {
                        sec = sn->expire % 60;
                        sn->expire /= 60;
                        min = sn->expire % 60;
                        sn->expire /= 60;
-                       printf(", expires in %.2u:%.2u:%.2u",
+                       printf(", expires in %.2" PRIu64 ":%.2" PRIu64 ":%.2" 
PRIu64,
                            sn->expire, min, sec);
                }
-               printf(", %llu pkts, %llu bytes",
-#ifdef __FreeBSD__
-                   (unsigned long long)(sn->packets[0] + sn->packets[1]),
-                   (unsigned long long)(sn->bytes[0] + sn->bytes[1]));
-#else
+               printf(", %" PRIu64 " pkts, %" PRIu64 " bytes",
                    sn->packets[0] + sn->packets[1],
                    sn->bytes[0] + sn->bytes[1]);
-#endif
                switch (sn->ruletype) {
                case PF_NAT:
-                       if (sn->rule.nr != -1)
-                               printf(", nat rule %u", sn->rule.nr);
+                       if (sn->rule != -1)
+                               printf(", nat rule %u", sn->rule);
                        break;
                case PF_RDR:
-                       if (sn->rule.nr != -1)
-                               printf(", rdr rule %u", sn->rule.nr);
+                       if (sn->rule != -1)
+                               printf(", rdr rule %u", sn->rule);
                        break;
                case PF_PASS:
                case PF_MATCH:
-                       if (sn->rule.nr != -1)
-                               printf(", filter rule %u", sn->rule.nr);
+                       if (sn->rule != -1)
+                               printf(", filter rule %u", sn->rule);
                        break;
                }
                printf("\n");
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 550005508f40..e6959e0b95a7 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -302,7 +302,7 @@ int parse_flags(char *);
 int    pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
 
 void   print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int);
-void   print_src_node(struct pf_src_node *, int);
+void   print_src_node(struct pfctl_src_node *, int);
 void   print_eth_rule(struct pfctl_eth_rule *, const char *, int);
 void   print_rule(struct pfctl_rule *, const char *, int, int);
 void   print_tabledef(const char *, int, int, struct node_tinithead *);
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index c75af9091d08..67047a319fb8 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -1713,6 +1713,77 @@ pf_handle_get_ruleset(struct nlmsghdr *hdr, struct 
nl_pstate *npt)
        return (0);
 }
 
+static bool
+nlattr_add_pf_threshold(struct nl_writer *nw, int attrtype, struct 
pf_threshold *t)
+{
+       int off = nlattr_add_nested(nw, attrtype);
+
+       nlattr_add_u32(nw, PF_TH_LIMIT, t->limit);
+       nlattr_add_u32(nw, PF_TH_SECONDS, t->seconds);
+       nlattr_add_u32(nw, PF_TH_COUNT, t->count);
+       nlattr_add_u32(nw, PF_TH_LAST, t->last);
+
+       nlattr_set_len(nw, off);
+
+       return (true);
+}
+
+static int
+pf_handle_get_srcnodes(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+       struct nl_writer        *nw = npt->nw;
+       struct genlmsghdr       *ghdr_new;
+       struct pf_ksrc_node     *n;
+       struct pf_srchash       *sh;
+       int                      i;
+
+       hdr->nlmsg_flags |= NLM_F_MULTI;
+
+       for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask;
+           i++, sh++) {
+               /* Avoid locking empty rows. */
+               if (LIST_EMPTY(&sh->nodes))
+                       continue;
+
+               PF_HASHROW_LOCK(sh);
+               LIST_FOREACH(n, &sh->nodes, entry) {
+                       if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
+                               nlmsg_abort(nw);
+                               return (ENOMEM);
+                       }
+
+                       ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
+                       ghdr_new->cmd = PFNL_CMD_GET_SRCNODES;
+                       ghdr_new->version = 0;
+                       ghdr_new->reserved = 0;
+
+                       nlattr_add_in6_addr(nw, PF_SN_ADDR, &n->addr.v6);
+                       nlattr_add_in6_addr(nw, PF_SN_RADDR, &n->raddr.v6);
+                       nlattr_add_u32(nw, PF_SN_RULE_NR, n->rule->nr);
+                       nlattr_add_u64(nw, PF_SN_BYTES_IN, 
counter_u64_fetch(n->bytes[0]));
+                       nlattr_add_u64(nw, PF_SN_BYTES_OUT, 
counter_u64_fetch(n->bytes[1]));
+                       nlattr_add_u64(nw, PF_SN_PACKETS_IN, 
counter_u64_fetch(n->packets[0]));
+                       nlattr_add_u64(nw, PF_SN_PACKETS_OUT, 
counter_u64_fetch(n->packets[1]));
+                       nlattr_add_u32(nw, PF_SN_STATES, n->states);
+                       nlattr_add_u32(nw, PF_SN_CONNECTIONS, n->conn);
+                       nlattr_add_u8(nw, PF_SN_AF, n->af);
+                       nlattr_add_u8(nw, PF_SN_RULE_TYPE, n->ruletype);
+                       nlattr_add_u64(nw, PF_SN_CREATION, n->creation);
+                       nlattr_add_u64(nw, PF_SN_EXPIRE, n->expire);
+                       nlattr_add_pf_threshold(nw, PF_SN_CONNECTION_RATE, 
&n->conn_rate);
+
+                       if (!nlmsg_end(nw)) {
+                               PF_HASHROW_UNLOCK(sh);
+                               nlmsg_abort(nw);
+                               return (ENOMEM);
+                       }
+               }
+               PF_HASHROW_UNLOCK(sh);
+       }
+
+       return (0);
+}
+
 static const struct nlhdr_parser *all_parsers[] = {
        &state_parser,
        &addrule_parser,
@@ -1899,6 +1970,13 @@ static const struct genl_cmd pf_cmds[] = {
                .cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
                .cmd_priv = PRIV_NETINET_PF,
        },
+       {
+               .cmd_num = PFNL_CMD_GET_SRCNODES,
+               .cmd_name = "GET_SRCNODES",
+               .cmd_cb = pf_handle_get_srcnodes,
+               .cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
+               .cmd_priv = PRIV_NETINET_PF,
+       },
 };
 
 void
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index 0ec68658dcf3..e0b8989ab255 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -60,6 +60,7 @@ enum {
        PFNL_CMD_GET_ADDR = 22,
        PFNL_CMD_GET_RULESETS = 23,
        PFNL_CMD_GET_RULESET = 24,
+       PFNL_CMD_GET_SRCNODES = 25,
        __PFNL_CMD_MAX,
 };
 #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@@ -389,6 +390,32 @@ enum pf_get_rulesets_types_t {
        PF_RS_NAME              = 3, /* string */
 };
 
+enum pf_threshold_types_t {
+       PF_TH_UNSPEC,
+       PF_TH_LIMIT             = 1, /* u32 */
+       PF_TH_SECONDS           = 2, /* u32 */
+       PF_TH_COUNT             = 3, /* u32 */
+       PF_TH_LAST              = 4, /* u32 */
+};
+
+enum pf_srcnodes_types_t {
+       PF_SN_UNSPEC,
+       PF_SN_ADDR              = 1, /* nested, pf_addr */
+       PF_SN_RADDR             = 2, /* nested, pf_addr */
+       PF_SN_RULE_NR           = 3, /* u32 */
+       PF_SN_BYTES_IN          = 4, /* u64 */
+       PF_SN_BYTES_OUT         = 5, /* u64 */
+       PF_SN_PACKETS_IN        = 6, /* u64 */
+       PF_SN_PACKETS_OUT       = 7, /* u64 */
+       PF_SN_STATES            = 8, /* u32 */
+       PF_SN_CONNECTIONS       = 9, /* u32 */
+       PF_SN_AF                = 10, /* u8 */
+       PF_SN_RULE_TYPE         = 11, /* u8 */
+       PF_SN_CREATION          = 12, /* u64 */
+       PF_SN_EXPIRE            = 13, /* u64 */
+       PF_SN_CONNECTION_RATE   = 14, /* nested, pf_threshold */
+};
+
 #ifdef _KERNEL
 
 void   pf_nl_register(void);

Reply via email to