From db0697ce7f6020b525cee072e7c0c85512daabda Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 4 Dec 2017 13:28:25 +0100 Subject: src: support for flowtable listing This patch allows you to dump existing flowtable. # nft list ruleset table ip x { flowtable x { hook ingress priority 10 devices = { eth0, tap0 } } } You can also list existing flowtables via: # nft list flowtables table ip x { flowtable x { hook ingress priority 10 devices = { eth0, tap0 } } } You need a Linux kernel >= 4.16-rc to test this new feature. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 6 ++ include/mnl.h | 3 + include/netlink.h | 4 ++ include/rule.h | 24 +++++++ src/evaluate.c | 1 + src/mnl.c | 58 +++++++++++++++++ src/netlink.c | 65 +++++++++++++++++++ src/parser_bison.y | 6 ++ src/rule.c | 122 ++++++++++++++++++++++++++++++++++++ src/scanner.l | 2 + 10 files changed, 291 insertions(+) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 2efbf974..b2d36004 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -92,6 +92,9 @@ enum nft_verdicts { * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes) + * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes) + * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes) + * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -116,6 +119,9 @@ enum nf_tables_msg_types { NFT_MSG_GETOBJ, NFT_MSG_DELOBJ, NFT_MSG_GETOBJ_RESET, + NFT_MSG_NEWFLOWTABLE, + NFT_MSG_GETFLOWTABLE, + NFT_MSG_DELFLOWTABLE, NFT_MSG_MAX, }; diff --git a/include/mnl.h b/include/mnl.h index 4662cd04..4475e7f8 100644 --- a/include/mnl.h +++ b/include/mnl.h @@ -89,6 +89,9 @@ int mnl_nft_obj_batch_add(struct nftnl_obj *nln, struct nftnl_batch *batch, int mnl_nft_obj_batch_del(struct nftnl_obj *nln, struct nftnl_batch *batch, unsigned int flags, uint32_t seqnum); +struct nftnl_flowtable_list * +mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table); + struct nftnl_ruleset *mnl_nft_ruleset_dump(struct netlink_ctx *ctx, uint32_t family); int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask, diff --git a/include/netlink.h b/include/netlink.h index 2c4250e7..387eb9d8 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -179,6 +179,10 @@ extern int netlink_add_obj(struct netlink_ctx *ctx, const struct handle *h, extern int netlink_delete_obj(struct netlink_ctx *ctx, const struct handle *h, struct location *loc, uint32_t type); +extern int netlink_list_flowtables(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc); + extern void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx); extern void netlink_dump_rule(const struct nftnl_rule *nlr, diff --git a/include/rule.h b/include/rule.h index 58c4aeef..33bb24fa 100644 --- a/include/rule.h +++ b/include/rule.h @@ -35,6 +35,7 @@ struct position_spec { * @chain: chain name (chains and rules only) * @set: set name (sets only) * @obj: stateful object name (stateful object only) + * @flowtable: flow table name (flow table only) * @handle: rule handle (rules only) * @position: rule position (rules only) * @set_id: set ID (sets only) @@ -45,6 +46,7 @@ struct handle { const char *chain; const char *set; const char *obj; + const char *flowtable; struct handle_spec handle; struct position_spec position; uint32_t set_id; @@ -102,6 +104,7 @@ enum table_flags { * @chains: chains contained in the table * @sets: sets contained in the table * @objs: stateful objects contained in the table + * @flowtables: flow tables contained in the table * @flags: table flags * @refcnt: table reference counter */ @@ -113,6 +116,7 @@ struct table { struct list_head chains; struct list_head sets; struct list_head objs; + struct list_head flowtables; enum table_flags flags; unsigned int refcnt; }; @@ -319,6 +323,24 @@ void obj_print_plain(const struct obj *obj, struct output_ctx *octx); const char *obj_type_name(uint32_t type); uint32_t obj_type_to_cmd(uint32_t type); +struct flowtable { + struct list_head list; + struct handle handle; + struct location location; + unsigned int hooknum; + int priority; + const char **dev_array; + int dev_array_len; + unsigned int refcnt; +}; + +extern struct flowtable *flowtable_alloc(const struct location *loc); +extern struct flowtable *flowtable_get(struct flowtable *flowtable); +extern void flowtable_free(struct flowtable *flowtable); +extern void flowtable_add_hash(struct flowtable *flowtable, struct table *table); + +void flowtable_print(const struct flowtable *n, struct output_ctx *octx); + /** * enum cmd_ops - command operations * @@ -377,6 +399,7 @@ enum cmd_ops { * @CMD_OBJ_QUOTAS: multiple quotas * @CMD_OBJ_LIMIT: limit * @CMD_OBJ_LIMITS: multiple limits + * @CMD_OBJ_FLOWTABLES: flow tables */ enum cmd_obj { CMD_OBJ_INVALID, @@ -403,6 +426,7 @@ enum cmd_obj { CMD_OBJ_CT_HELPERS, CMD_OBJ_LIMIT, CMD_OBJ_LIMITS, + CMD_OBJ_FLOWTABLES, }; struct markup { diff --git a/src/evaluate.c b/src/evaluate.c index cc8eac83..6094d0c5 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -3209,6 +3209,7 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) case CMD_OBJ_CT_HELPERS: case CMD_OBJ_LIMITS: case CMD_OBJ_SETS: + case CMD_OBJ_FLOWTABLES: if (cmd->handle.table == NULL) return 0; if (table_lookup(&cmd->handle, ctx->cache) == NULL) diff --git a/src/mnl.c b/src/mnl.c index 5587e158..e70b0cde 100644 --- a/src/mnl.c +++ b/src/mnl.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -953,6 +954,63 @@ int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls) return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_elem_cb, nls); } +static int flowtable_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_flowtable_list *nln_list = data; + struct nftnl_flowtable *n; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + n = nftnl_flowtable_alloc(); + if (n == NULL) + memory_allocation_error(); + + if (nftnl_flowtable_nlmsg_parse(nlh, n) < 0) + goto err_free; + + nftnl_flowtable_list_add_tail(n, nln_list); + return MNL_CB_OK; + +err_free: + nftnl_flowtable_free(n); + return MNL_CB_OK; +} + +struct nftnl_flowtable_list * +mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table) +{ + struct nftnl_flowtable_list *nln_list; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nftnl_flowtable *n; + struct nlmsghdr *nlh; + int ret; + + n = nftnl_flowtable_alloc(); + if (n == NULL) + memory_allocation_error(); + + nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family, + NLM_F_DUMP | NLM_F_ACK, ctx->seqnum); + if (table != NULL) + nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_TABLE, table); + nftnl_flowtable_nlmsg_build_payload(nlh, n); + nftnl_flowtable_free(n); + + nln_list = nftnl_flowtable_list_alloc(); + if (nln_list == NULL) + memory_allocation_error(); + + ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, flowtable_cb, nln_list); + if (ret < 0) + goto err; + + return nln_list; +err: + nftnl_flowtable_list_free(nln_list); + return NULL; +} + /* * ruleset */ diff --git a/src/netlink.c b/src/netlink.c index 403f93ca..9fadccd0 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1542,6 +1543,70 @@ int netlink_reset_objs(struct netlink_ctx *ctx, const struct handle *h, return err; } +static struct flowtable * +netlink_delinearize_flowtable(struct netlink_ctx *ctx, + struct nftnl_flowtable *nlo) +{ + struct flowtable *flowtable; + const char **dev_array; + int len = 0, i; + + flowtable = flowtable_alloc(&netlink_location); + flowtable->handle.family = + nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FAMILY); + flowtable->handle.table = + xstrdup(nftnl_flowtable_get_str(nlo, NFTNL_FLOWTABLE_TABLE)); + flowtable->handle.flowtable = + xstrdup(nftnl_flowtable_get_str(nlo, NFTNL_FLOWTABLE_NAME)); + dev_array = nftnl_flowtable_get_array(nlo, NFTNL_FLOWTABLE_DEVICES); + while (dev_array[len] != '\0') + len++; + + flowtable->dev_array = calloc(1, len * sizeof(char *)); + for (i = 0; i < len; i++) + flowtable->dev_array[i] = xstrdup(dev_array[i]); + + flowtable->dev_array_len = len; + + flowtable->priority = + nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO); + flowtable->hooknum = + nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM); + + return flowtable; +} + +static int list_flowtable_cb(struct nftnl_flowtable *nls, void *arg) +{ + struct netlink_ctx *ctx = arg; + struct flowtable *flowtable; + + flowtable = netlink_delinearize_flowtable(ctx, nls); + if (flowtable == NULL) + return -1; + list_add_tail(&flowtable->list, &ctx->list); + return 0; +} + +int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc) +{ + struct nftnl_flowtable_list *flowtable_cache; + int err; + + flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, h->table); + if (flowtable_cache == NULL) { + if (errno == EINTR) + return -1; + + return 0; + } + + err = nftnl_flowtable_list_foreach(flowtable_cache, list_flowtable_cb, ctx); + nftnl_flowtable_list_free(flowtable_cache); + return err; +} + int netlink_batch_send(struct netlink_ctx *ctx, struct list_head *err_list) { return mnl_batch_talk(ctx, err_list); diff --git a/src/parser_bison.y b/src/parser_bison.y index ee6729f1..faa613c2 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -251,6 +251,8 @@ int nft_lex(void *, void *, void *); %token METER "meter" %token METERS "meters" +%token FLOWTABLES "flowtables" + %token NUM "number" %token STRING "string" %token QUOTED_STRING "quoted string" @@ -1127,6 +1129,10 @@ list_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &$2, &@$, NULL); } + | FLOWTABLES ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLES, &$2, &@$, NULL); + } | MAPS ruleset_spec { $$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAPS, &$2, &@$, NULL); diff --git a/src/rule.c b/src/rule.c index b331416a..9c8d7f7d 100644 --- a/src/rule.c +++ b/src/rule.c @@ -95,6 +95,11 @@ static int cache_init_objects(struct netlink_ctx *ctx, enum cmd_ops cmd) return -1; list_splice_tail_init(&ctx->list, &table->chains); + ret = netlink_list_flowtables(ctx, &table->handle, &internal_location); + if (ret < 0) + return -1; + list_splice_tail_init(&ctx->list, &table->flowtables); + if (cmd != CMD_RESET) { ret = netlink_list_objs(ctx, &table->handle, &internal_location); if (ret < 0) @@ -764,6 +769,7 @@ struct table *table_alloc(void) init_list_head(&table->chains); init_list_head(&table->sets); init_list_head(&table->objs); + init_list_head(&table->flowtables); init_list_head(&table->scope.symbols); table->refcnt = 1; @@ -839,6 +845,7 @@ static void table_print_options(const struct table *table, const char **delim, static void table_print(const struct table *table, struct output_ctx *octx) { + struct flowtable *flowtable; struct chain *chain; struct obj *obj; struct set *set; @@ -860,6 +867,11 @@ static void table_print(const struct table *table, struct output_ctx *octx) set_print(set, octx); delim = "\n"; } + list_for_each_entry(flowtable, &table->flowtables, list) { + nft_print(octx, "%s", delim); + flowtable_print(flowtable, octx); + delim = "\n"; + } list_for_each_entry(chain, &table->chains, list) { nft_print(octx, "%s", delim); chain_print(chain, octx); @@ -1524,6 +1536,114 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type) return 0; } +struct flowtable *flowtable_alloc(const struct location *loc) +{ + struct flowtable *flowtable; + + flowtable = xzalloc(sizeof(*flowtable)); + if (loc != NULL) + flowtable->location = *loc; + + flowtable->refcnt = 1; + return flowtable; +} + +struct flowtable *flowtable_get(struct flowtable *flowtable) +{ + flowtable->refcnt++; + return flowtable; +} + +void flowtable_free(struct flowtable *flowtable) +{ + if (--flowtable->refcnt > 0) + return; + handle_free(&flowtable->handle); + xfree(flowtable); +} + +void flowtable_add_hash(struct flowtable *flowtable, struct table *table) +{ + list_add_tail(&flowtable->list, &table->flowtables); +} + +static void flowtable_print_declaration(const struct flowtable *flowtable, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + int i; + + nft_print(octx, "%sflowtable", opts->tab); + + if (opts->family != NULL) + nft_print(octx, " %s", opts->family); + + if (opts->table != NULL) + nft_print(octx, " %s", opts->table); + + nft_print(octx, " %s {%s", flowtable->handle.flowtable, opts->nl); + + nft_print(octx, "%s%shook %s priority %d%s", + opts->tab, opts->tab, "ingress", + flowtable->priority, opts->stmt_separator); + + nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab); + for (i = 0; i < flowtable->dev_array_len; i++) { + nft_print(octx, "%s", flowtable->dev_array[i]); + if (i + 1 != flowtable->dev_array_len) + nft_print(octx, ", "); + } + nft_print(octx, " }%s", opts->stmt_separator); +} + +static void do_flowtable_print(const struct flowtable *flowtable, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + flowtable_print_declaration(flowtable, opts, octx); + nft_print(octx, "%s}%s", opts->tab, opts->nl); +} + +void flowtable_print(const struct flowtable *s, struct output_ctx *octx) +{ + struct print_fmt_options opts = { + .tab = "\t", + .nl = "\n", + .stmt_separator = "\n", + }; + + do_flowtable_print(s, &opts, octx); +} + +static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct print_fmt_options opts = { + .tab = "\t", + .nl = "\n", + .stmt_separator = "\n", + }; + struct flowtable *flowtable; + struct table *table; + + list_for_each_entry(table, &ctx->cache->list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + nft_print(ctx->octx, "table %s %s {\n", + family2str(table->handle.family), + table->handle.table); + + list_for_each_entry(flowtable, &table->flowtables, list) { + flowtable_print_declaration(flowtable, &opts, ctx->octx); + nft_print(ctx->octx, "%s}%s", opts.tab, opts.nl); + } + + nft_print(ctx->octx, "}\n"); + } + return 0; +} + static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd) { unsigned int family = cmd->handle.family; @@ -1671,6 +1791,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_LIMIT: case CMD_OBJ_LIMITS: return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_FLOWTABLES: + return do_list_flowtables(ctx, cmd); default: BUG("invalid command object type %u\n", cmd->obj); } diff --git a/src/scanner.l b/src/scanner.l index 05c70afe..3ea33b09 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -299,6 +299,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "meter" { return METER; } "meters" { return METERS; } +"flowtables" { return FLOWTABLES; } + "counter" { return COUNTER; } "name" { return NAME; } "packets" { return PACKETS; } -- cgit v1.2.3