diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2016-11-27 23:35:25 +0100 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2017-01-03 14:21:53 +0100 |
commit | 4756d92e517ae1f7d662c0ed083b54d8dc822e4a (patch) | |
tree | 5130faa44f2f3e72b257f4d30fd1752749109049 | |
parent | d156fd17ee7ff9a2822d7714e1c8dfe7b6b18f55 (diff) |
src: listing of stateful objects
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>
-rw-r--r-- | include/mnl.h | 2 | ||||
-rw-r--r-- | include/netlink.h | 4 | ||||
-rw-r--r-- | include/rule.h | 51 | ||||
-rw-r--r-- | include/statement.h | 3 | ||||
-rw-r--r-- | src/evaluate.c | 2 | ||||
-rw-r--r-- | src/mnl.c | 59 | ||||
-rw-r--r-- | src/netlink.c | 67 | ||||
-rw-r--r-- | src/parser_bison.y | 30 | ||||
-rw-r--r-- | src/rule.c | 140 | ||||
-rw-r--r-- | src/scanner.l | 3 | ||||
-rw-r--r-- | src/statement.c | 2 |
11 files changed, 360 insertions, 3 deletions
diff --git a/include/mnl.h b/include/mnl.h index 87db96af..ad036aef 100644 --- a/include/mnl.h +++ b/include/mnl.h @@ -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, diff --git a/include/netlink.h b/include/netlink.h index 363b5251..ce577871 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -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); diff --git a/include/rule.h b/include/rule.h index f74630c5..e0f89139 100644 --- a/include/rule.h +++ b/include/rule.h @@ -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 { diff --git a/include/statement.h b/include/statement.h index d317ae36..9d0f601f 100644 --- a/include/statement.h +++ b/include/statement.h @@ -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 */ diff --git a/src/evaluate.c b/src/evaluate.c index 557c61c8..b3630c30 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -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: @@ -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; diff --git a/src/netlink.c b/src/netlink.c index e37d3bf1..bbf675f9 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -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); diff --git a/src/parser_bison.y b/src/parser_bison.y index aea6e47d..2213f380 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -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($$)); @@ -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); } diff --git a/src/scanner.l b/src/scanner.l index 8aa4b08b..cb989320 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -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; } diff --git a/src/statement.c b/src/statement.c index 4d3ca55a..fbd78aaf 100644 --- a/src/statement.c +++ b/src/statement.c @@ -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; |