]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: listing of stateful objects
authorPablo Neira Ayuso <pablo@netfilter.org>
Sun, 27 Nov 2016 22:35:25 +0000 (23:35 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 3 Jan 2017 13:21:53 +0000 (14:21 +0100)
This patch allows you to dump existing stateful objects, eg.

 # nft list ruleset
 table ip filter {
        counter test {
                packets 64 bytes 1268
        }

        quota test {
                over 1 mbytes used 1268 bytes
        }

        chain input {
                type filter hook input priority 0; policy accept;
                quota name test drop
                counter name test
        }
 }

 # nft list quotas
 table ip filter {
        quota test {
                over 1 mbytes used 1268 bytes
        }
 }
 # nft list counters
 table ip filter {
        counter test {
                packets 64 bytes 1268
        }
 }

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/mnl.h
include/netlink.h
include/rule.h
include/statement.h
src/evaluate.c
src/mnl.c
src/netlink.c
src/parser_bison.y
src/rule.c
src/scanner.l
src/statement.c

index 87db96afd369687073749f4cda07b3650f1dafcc..ad036aefabbd8bbf29b25d1c7f05f2928e1e8d40 100644 (file)
@@ -86,6 +86,8 @@ int mnl_nft_setelem_batch_flush(struct nftnl_set *nls, unsigned int flags,
                                uint32_t seqnum);
 int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nftnl_set *nls);
 
+struct nftnl_obj_list *mnl_nft_obj_dump(struct mnl_socket *nf_sock, int family,
+                                       const char *table);
 struct nftnl_ruleset *mnl_nft_ruleset_dump(struct mnl_socket *nf_sock,
                                         uint32_t family);
 int mnl_nft_event_listener(struct mnl_socket *nf_sock,
index 363b5251968f3d32d715293c2cc57240e42232bd..ce577871761f51feeba794aea189b97eccdf53ca 100644 (file)
@@ -6,6 +6,7 @@
 #include <libnftnl/rule.h>
 #include <libnftnl/expr.h>
 #include <libnftnl/set.h>
+#include <libnftnl/object.h>
 
 #include <linux/netlink.h>
 #include <linux/netfilter/nf_tables.h>
@@ -168,6 +169,9 @@ extern int netlink_get_setelems(struct netlink_ctx *ctx, const struct handle *h,
 extern int netlink_flush_setelems(struct netlink_ctx *ctx, const struct handle *h,
                                  const struct location *loc);
 
+extern int netlink_list_objs(struct netlink_ctx *ctx, const struct handle *h,
+                              const struct location *loc);
+
 extern void netlink_dump_table(const struct nftnl_table *nlt);
 extern void netlink_dump_chain(const struct nftnl_chain *nlc);
 extern void netlink_dump_rule(const struct nftnl_rule *nlr);
index f74630c53d2b704e3e236a34ac22d00f77b773fb..e0f891393276996243c7cbcecb79c3a375ebe2e8 100644 (file)
@@ -34,6 +34,7 @@ struct position_spec {
  * @table:     table name
  * @chain:     chain name (chains and rules only)
  * @set:       set name (sets only)
+ * @obj:       stateful object name (stateful object only)
  * @handle:    rule handle (rules only)
  * @position:  rule position (rules only)
  * @set_id:    set ID (sets only)
@@ -43,6 +44,7 @@ struct handle {
        const char              *table;
        const char              *chain;
        const char              *set;
+       const char              *obj;
        struct handle_spec      handle;
        struct position_spec    position;
        uint32_t                set_id;
@@ -95,6 +97,7 @@ enum table_flags {
  * @location:  location the table was defined at
  * @chains:    chains contained in the table
  * @sets:      sets contained in the table
+ * @objs:      stateful objects contained in the table
  * @flags:     table flags
  * @refcnt:    table reference counter
  */
@@ -105,6 +108,7 @@ struct table {
        struct scope            scope;
        struct list_head        chains;
        struct list_head        sets;
+       struct list_head        objs;
        enum table_flags        flags;
        unsigned int            refcnt;
 };
@@ -241,6 +245,45 @@ extern struct set *set_lookup_global(uint32_t family, const char *table,
 extern void set_print(const struct set *set);
 extern void set_print_plain(const struct set *s);
 
+#include <statement.h>
+
+struct counter {
+       uint64_t        packets;
+       uint64_t        bytes;
+};
+
+struct quota {
+       uint64_t        bytes;
+       uint64_t        used;
+       uint32_t        flags;
+};
+
+/**
+ * struct obj - nftables stateful object statement
+ *
+ * @list:      table set list node
+ * @location:  location the stateful object was defined/declared at
+ * @handle:    counter handle
+ * @type:      type of stateful object
+ */
+struct obj {
+       struct list_head                list;
+       struct location                 location;
+       struct handle                   handle;
+       uint32_t                        type;
+
+       union {
+               struct counter          counter;
+               struct quota            quota;
+       };
+};
+
+struct obj *obj_alloc(const struct location *loc);
+void obj_free(struct obj *obj);
+void obj_add_hash(struct obj *obj, struct table *table);
+void obj_print(const struct obj *n);
+const char *obj_type_name(enum stmt_types type);
+
 /**
  * enum cmd_ops - command operations
  *
@@ -287,6 +330,10 @@ enum cmd_ops {
  * @CMD_OBJ_EXPR:      expression
  * @CMD_OBJ_MONITOR:   monitor
  * @CMD_OBJ_EXPORT:    export
+ * @CMD_OBJ_COUNTER:   counter
+ * @CMD_OBJ_COUNTERS:  multiple counters
+ * @CMD_OBJ_QUOTA:     quota
+ * @CMD_OBJ_QUOTAS:    multiple quotas
  */
 enum cmd_obj {
        CMD_OBJ_INVALID,
@@ -305,6 +352,10 @@ enum cmd_obj {
        CMD_OBJ_FLOWTABLES,
        CMD_OBJ_MAP,
        CMD_OBJ_MAPS,
+       CMD_OBJ_COUNTER,
+       CMD_OBJ_COUNTERS,
+       CMD_OBJ_QUOTA,
+       CMD_OBJ_QUOTAS,
 };
 
 struct export {
index d317ae368164f637fa556e85b225ff91034dd1a8..9d0f601f98a296a78169c14543486bc7beb4d9d9 100644 (file)
@@ -66,6 +66,7 @@ struct limit_stmt {
 };
 
 extern struct stmt *limit_stmt_alloc(const struct location *loc);
+extern void __limit_stmt_print(const struct limit_stmt *limit);
 
 struct reject_stmt {
        struct expr             *expr;
@@ -301,4 +302,6 @@ extern void stmt_free(struct stmt *stmt);
 extern void stmt_list_free(struct list_head *list);
 extern void stmt_print(const struct stmt *stmt);
 
+const char *get_rate(uint64_t byte_rate, uint64_t *rate);
+
 #endif /* NFTABLES_STATEMENT_H */
index 557c61c814dfed74f32433c52d20f0b8c82e8a35..b3630c303920ef3c9612f4d3495960fd0e2a372e 100644 (file)
@@ -2845,6 +2845,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
                return 0;
        case CMD_OBJ_CHAINS:
        case CMD_OBJ_SETS:
+       case CMD_OBJ_COUNTERS:
+       case CMD_OBJ_QUOTAS:
        case CMD_OBJ_RULESET:
        case CMD_OBJ_FLOWTABLES:
        case CMD_OBJ_MAPS:
index 257b630e2a2696858309b5693a646ad89c588260..534d02f4ff32b2fcde6a55d176d7177369664aa1 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -16,6 +16,7 @@
 #include <libnftnl/rule.h>
 #include <libnftnl/expr.h>
 #include <libnftnl/set.h>
+#include <libnftnl/object.h>
 #include <libnftnl/batch.h>
 
 #include <linux/netfilter/nfnetlink.h>
@@ -795,6 +796,64 @@ err:
        return NULL;
 }
 
+static int obj_cb(const struct nlmsghdr *nlh, void *data)
+{
+       struct nftnl_obj_list *nln_list = data;
+       struct nftnl_obj *n;
+
+       if (check_genid(nlh) < 0)
+               return MNL_CB_ERROR;
+
+       n = nftnl_obj_alloc();
+       if (n == NULL)
+               memory_allocation_error();
+
+       if (nftnl_obj_nlmsg_parse(nlh, n) < 0)
+               goto err_free;
+
+       nftnl_obj_list_add_tail(n, nln_list);
+       return MNL_CB_OK;
+
+err_free:
+       nftnl_obj_free(n);
+       return MNL_CB_OK;
+}
+
+
+struct nftnl_obj_list *
+mnl_nft_obj_dump(struct mnl_socket *nf_sock, int family, const char *table)
+{
+       struct nftnl_obj_list *nln_list;
+       char buf[MNL_SOCKET_BUFFER_SIZE];
+       struct nftnl_obj *n;
+       struct nlmsghdr *nlh;
+       int ret;
+
+       n = nftnl_obj_alloc();
+       if (n == NULL)
+               memory_allocation_error();
+
+       nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETOBJ, family,
+                                   NLM_F_DUMP | NLM_F_ACK, seq);
+       if (table != NULL)
+               nftnl_obj_set(n, NFTNL_OBJ_TABLE, table);
+       nftnl_obj_nlmsg_build_payload(nlh, n);
+       nftnl_obj_free(n);
+
+       nln_list = nftnl_obj_list_alloc();
+       if (nln_list == NULL)
+               memory_allocation_error();
+
+       ret = nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, obj_cb, nln_list);
+       if (ret < 0)
+               goto err;
+
+       return nln_list;
+err:
+       nftnl_obj_list_free(nln_list);
+       return NULL;
+}
+
 static int set_get_cb(const struct nlmsghdr *nlh, void *data)
 {
        struct nftnl_set *s = data;
index e37d3bf124a6c96232430d7f1be7c9ac10336279..bbf675f90def3d9654d13ae62dcd34a9f1f03412 100644 (file)
@@ -1608,6 +1608,73 @@ out:
        return err;
 }
 
+static struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
+                                          struct nftnl_obj *nlo)
+{
+       struct obj *obj;
+       uint32_t type;
+
+       obj = obj_alloc(&netlink_location);
+       obj->handle.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY);
+       obj->handle.table =
+               xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE));
+       obj->handle.obj =
+               xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME));
+
+       type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE);
+       switch (type) {
+       case NFT_OBJECT_COUNTER:
+               obj->counter.packets =
+                       nftnl_obj_get_u64(nlo, NFTNL_OBJ_CTR_PKTS);
+               obj->counter.bytes =
+                       nftnl_obj_get_u64(nlo, NFTNL_OBJ_CTR_BYTES);
+               break;
+       case NFT_OBJECT_QUOTA:
+               obj->quota.bytes =
+                       nftnl_obj_get_u64(nlo, NFTNL_OBJ_QUOTA_BYTES);
+               obj->quota.used =
+                       nftnl_obj_get_u64(nlo, NFTNL_OBJ_QUOTA_CONSUMED);
+               obj->quota.flags =
+                       nftnl_obj_get_u32(nlo, NFTNL_OBJ_QUOTA_FLAGS);
+       }
+       obj->type = type;
+
+       return obj;
+}
+
+static int list_obj_cb(struct nftnl_obj *nls, void *arg)
+{
+       struct netlink_ctx *ctx = arg;
+       struct obj *obj;
+
+       obj = netlink_delinearize_obj(ctx, nls);
+       if (obj == NULL)
+               return -1;
+       list_add_tail(&obj->list, &ctx->list);
+       return 0;
+}
+
+int netlink_list_objs(struct netlink_ctx *ctx, const struct handle *h,
+                     const struct location *loc)
+{
+       struct nftnl_obj_list *obj_cache;
+       int err;
+
+       obj_cache = mnl_nft_obj_dump(nf_sock, h->family, h->table);
+       if (obj_cache == NULL) {
+               if (errno == EINTR)
+                       return -1;
+
+               return netlink_io_error(ctx, loc,
+                                       "Could not receive stateful object from kernel: %s",
+                                       strerror(errno));
+       }
+
+       err = nftnl_obj_list_foreach(obj_cache, list_obj_cb, ctx);
+       nftnl_obj_list_free(obj_cache);
+       return err;
+}
+
 int netlink_batch_send(struct list_head *err_list)
 {
        return mnl_batch_talk(nf_sock, err_list);
index aea6e47d8b1295ede1854cfe811f01e7352ee611..2213f3808db439a9ec50ceee098cee6c44339dbc 100644 (file)
@@ -364,6 +364,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token PACKETS                 "packets"
 %token BYTES                   "bytes"
 
+%token COUNTERS                        "counters"
+%token QUOTAS                  "quotas"
+
 %token LOG                     "log"
 %token PREFIX                  "prefix"
 %token GROUP                   "group"
@@ -441,8 +444,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 
 %type <handle>                 table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
 %destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
-%type <handle>                 set_spec set_identifier
-%destructor { handle_free(&$$); } set_spec set_identifier
+%type <handle>                 set_spec set_identifier obj_spec
+%destructor { handle_free(&$$); } set_spec set_identifier obj_spec
 %type <val>                    family_spec family_spec_explicit chain_policy prio_spec
 
 %type <string>                 dev_spec quota_unit
@@ -872,6 +875,22 @@ list_cmd           :       TABLE           table_spec
                        {
                                $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL);
                        }
+                       |       COUNTERS        ruleset_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
+                       }
+                       |       COUNTER         obj_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+                       }
+                       |       QUOTAS          ruleset_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
+                       }
+                       |       QUOTA           obj_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+                       }
                        |       RULESET         ruleset_spec
                        {
                                $$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -1299,6 +1318,13 @@ set_identifier           :       identifier
                        }
                        ;
 
+obj_spec               :       table_spec      identifier
+                       {
+                               $$              = $1;
+                               $$.obj          = $2;
+                       }
+                       ;
+
 handle_spec            :       HANDLE          NUM
                        {
                                memset(&$$, 0, sizeof($$));
index 988305b576153fa4b128fc538329845eaa50996e..05f0eb87b16266d41dd1edf72d19814fc7066b85 100644 (file)
@@ -93,6 +93,12 @@ static int cache_init_objects(struct netlink_ctx *ctx, enum cmd_ops cmd)
                        return -1;
                list_splice_tail_init(&ctx->list, &table->chains);
 
+               /* Don't check for errors on listings, this would break nft with
+                * old kernels with no stateful object support.
+                */
+               netlink_list_objs(ctx, &table->handle, &internal_location);
+               list_splice_tail_init(&ctx->list, &table->objs);
+
                /* Skip caching other objects to speed up things: We only need
                 * a full cache when listing the existing ruleset.
                 */
@@ -688,6 +694,7 @@ struct table *table_alloc(void)
        table = xzalloc(sizeof(*table));
        init_list_head(&table->chains);
        init_list_head(&table->sets);
+       init_list_head(&table->objs);
        init_list_head(&table->scope.symbols);
        table->refcnt = 1;
 
@@ -762,6 +769,7 @@ static void table_print_options(const struct table *table, const char **delim)
 static void table_print(const struct table *table)
 {
        struct chain *chain;
+       struct obj *obj;
        struct set *set;
        const char *delim = "";
        const char *family = family2str(table->handle.family);
@@ -769,6 +777,11 @@ static void table_print(const struct table *table)
        printf("table %s %s {\n", family, table->handle.table);
        table_print_options(table, &delim);
 
+       list_for_each_entry(obj, &table->objs, list) {
+               printf("%s", delim);
+               obj_print(obj);
+               delim = "\n";
+       }
        list_for_each_entry(set, &table->sets, list) {
                if (set->flags & NFT_SET_ANONYMOUS)
                        continue;
@@ -1098,6 +1111,129 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
        return 0;
 }
 
+struct obj *obj_alloc(const struct location *loc)
+{
+       struct obj *obj;
+
+       obj = xzalloc(sizeof(*obj));
+       if (loc != NULL)
+               obj->location = *loc;
+       return obj;
+}
+
+void obj_free(struct obj *obj)
+{
+       handle_free(&obj->handle);
+       xfree(obj);
+}
+
+void obj_add_hash(struct obj *obj, struct table *table)
+{
+       list_add_tail(&obj->list, &table->objs);
+}
+
+static void obj_print_data(const struct obj *obj,
+                          struct print_fmt_options *opts)
+{
+       switch (obj->type) {
+       case NFT_OBJECT_COUNTER:
+               printf(" %s {%s%s%s", obj->handle.obj,
+                                     opts->nl, opts->tab, opts->tab);
+               printf("packets %"PRIu64" bytes %"PRIu64"",
+                      obj->counter.packets, obj->counter.bytes);
+               break;
+       case NFT_OBJECT_QUOTA: {
+               const char *data_unit;
+               uint64_t bytes;
+
+               printf(" %s {%s%s%s", obj->handle.obj,
+                                     opts->nl, opts->tab, opts->tab);
+               data_unit = get_rate(obj->quota.bytes, &bytes);
+               printf("%s%"PRIu64" %s",
+                      obj->quota.flags & NFT_QUOTA_F_INV ? "over " : "",
+                      bytes, data_unit);
+               if (obj->quota.used) {
+                       data_unit = get_rate(obj->quota.used, &bytes);
+                       printf(" used %"PRIu64" %s", bytes, data_unit);
+               }
+               }
+               break;
+       default:
+               printf("unknown {%s", opts->nl);
+               break;
+       }
+}
+
+static const char *obj_type_name_array[] = {
+       [NFT_OBJECT_COUNTER]    = "counter",
+       [NFT_OBJECT_QUOTA]      = "quota",
+};
+
+const char *obj_type_name(enum stmt_types type)
+{
+       assert(type <= NFT_OBJECT_QUOTA && obj_type_name_array[type]);
+
+       return obj_type_name_array[type];
+}
+
+static void obj_print_declaration(const struct obj *obj,
+                                 struct print_fmt_options *opts)
+{
+       printf("%s%s", opts->tab, obj_type_name(obj->type));
+
+       if (opts->family != NULL)
+               printf(" %s", opts->family);
+
+       if (opts->table != NULL)
+               printf(" %s", opts->table);
+
+       obj_print_data(obj, opts);
+
+       printf("%s%s}%s", opts->nl, opts->tab, opts->nl);
+}
+
+void obj_print(const struct obj *obj)
+{
+       struct print_fmt_options opts = {
+               .tab            = "\t",
+               .nl             = "\n",
+               .stmt_separator = "\n",
+       };
+
+       obj_print_declaration(obj, &opts);
+}
+
+static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type)
+{
+       struct print_fmt_options opts = {
+               .tab            = "\t",
+               .nl             = "\n",
+               .stmt_separator = "\n",
+       };
+       struct table *table;
+       struct obj *obj;
+
+       list_for_each_entry(table, &table_list, list) {
+               if (cmd->handle.family != NFPROTO_UNSPEC &&
+                   cmd->handle.family != table->handle.family)
+                       continue;
+
+               printf("table %s %s {\n",
+                      family2str(table->handle.family),
+                      table->handle.table);
+
+               list_for_each_entry(obj, &table->objs, list) {
+                       if (obj->type != type)
+                               continue;
+
+                       obj_print_declaration(obj, &opts);
+               }
+
+               printf("}\n");
+       }
+       return 0;
+}
+
 static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd)
 {
        unsigned int family = cmd->handle.family;
@@ -1230,6 +1366,10 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
                return do_list_sets(ctx, cmd);
        case CMD_OBJ_MAP:
                return do_list_set(ctx, cmd, table);
+       case CMD_OBJ_COUNTERS:
+               return do_list_obj(ctx, cmd, NFT_OBJECT_COUNTER);
+       case CMD_OBJ_QUOTAS:
+               return do_list_obj(ctx, cmd, NFT_OBJECT_QUOTA);
        default:
                BUG("invalid command object type %u\n", cmd->obj);
        }
index 8aa4b08ba8fc813c87a64dcc1d479f680c5ff684..cb989320324426ac8f0acaebd8d3eb3cfae00b24 100644 (file)
@@ -293,6 +293,9 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
 "packets"              { return PACKETS; }
 "bytes"                        { return BYTES; }
 
+"counters"             { return COUNTERS; }
+"quotas"               { return QUOTAS; }
+
 "log"                  { return LOG; }
 "prefix"               { return PREFIX; }
 "group"                        { return GROUP; }
index 4d3ca55a4081067dd74afa00c91630ab8a4589bb..fbd78aafe69a32237546a906c2aecc7a985e843e 100644 (file)
@@ -255,7 +255,7 @@ static const char *data_unit[] = {
        NULL
 };
 
-static const char *get_rate(uint64_t byte_rate, uint64_t *rate)
+const char *get_rate(uint64_t byte_rate, uint64_t *rate)
 {
        uint64_t res, prev, rest;
        int i;