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 --- 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 + 6 files changed, 254 insertions(+) (limited to 'src') 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