From 8ec2e8438a7c4c48c8005a5f352c8ea6cd40d2fe Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 27 Nov 2016 23:42:54 +0100 Subject: src: reset internal stateful objects This patch allows you to atomically dump and reset stateful objects, eg. # nft list counters table ip filter { counter test { packets 1024 bytes 100000 } } # nft reset quotas table filter counter test { packets 1024 bytes 100000 } # nft reset quotas table filter counter test { packets 0 bytes 0 } Signed-off-by: Pablo Neira Ayuso --- include/mnl.h | 3 ++- include/netlink.h | 6 ++++-- include/rule.h | 2 ++ src/evaluate.c | 1 + src/mnl.c | 14 +++++++++++--- src/netlink.c | 24 +++++++++++++++++++++++- src/parser_bison.y | 24 ++++++++++++++++++++++-- src/rule.c | 43 ++++++++++++++++++++++++++++++++++++++----- src/scanner.l | 1 + 9 files changed, 104 insertions(+), 14 deletions(-) diff --git a/include/mnl.h b/include/mnl.h index d178bd27..4a99972d 100644 --- a/include/mnl.h +++ b/include/mnl.h @@ -87,7 +87,8 @@ int mnl_nft_setelem_batch_flush(struct nftnl_set *nls, unsigned int flags, 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); + const char *table, uint32_t type, + bool reset); int mnl_nft_obj_batch_add(struct nftnl_obj *nln, unsigned int flags, uint32_t seqnum); int mnl_nft_obj_batch_del(struct nftnl_obj *nln, unsigned int flags, diff --git a/include/netlink.h b/include/netlink.h index 841211c4..450aba57 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -170,11 +170,13 @@ extern int netlink_flush_setelems(struct netlink_ctx *ctx, const struct handle * const struct location *loc); extern int netlink_list_objs(struct netlink_ctx *ctx, const struct handle *h, - const struct location *loc); + const struct location *loc); +extern int netlink_reset_objs(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc, uint32_t type); extern int netlink_add_obj(struct netlink_ctx *ctx, const struct handle *h, struct obj *obj, bool excl); extern int netlink_delete_obj(struct netlink_ctx *ctx, const struct handle *h, - struct location *loc, enum stmt_types type); + struct location *loc, uint32_t type); extern void netlink_dump_table(const struct nftnl_table *nlt); extern void netlink_dump_chain(const struct nftnl_chain *nlc); diff --git a/include/rule.h b/include/rule.h index 88acbcc7..9028c84b 100644 --- a/include/rule.h +++ b/include/rule.h @@ -294,6 +294,7 @@ const char *obj_type_name(uint32_t type); * @CMD_INSERT: insert object * @CMD_DELETE: delete object * @CMD_LIST: list container + * @CMD_RESET: reset container * @CMD_FLUSH: flush container * @CMD_RENAME: rename object * @CMD_EXPORT: export the ruleset in a given format @@ -308,6 +309,7 @@ enum cmd_ops { CMD_INSERT, CMD_DELETE, CMD_LIST, + CMD_RESET, CMD_FLUSH, CMD_RENAME, CMD_EXPORT, diff --git a/src/evaluate.c b/src/evaluate.c index 9bc3b7d6..cedf259f 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -3052,6 +3052,7 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd) case CMD_DELETE: return cmd_evaluate_delete(ctx, cmd); case CMD_LIST: + case CMD_RESET: return cmd_evaluate_list(ctx, cmd); case CMD_FLUSH: return cmd_evaluate_flush(ctx, cmd); diff --git a/src/mnl.c b/src/mnl.c index 9458e21b..52cb7736 100644 --- a/src/mnl.c +++ b/src/mnl.c @@ -851,22 +851,30 @@ err_free: struct nftnl_obj_list * -mnl_nft_obj_dump(struct mnl_socket *nf_sock, int family, const char *table) +mnl_nft_obj_dump(struct mnl_socket *nf_sock, int family, const char *table, + uint32_t type, bool reset) { struct nftnl_obj_list *nln_list; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nftnl_obj *n; struct nlmsghdr *nlh; - int ret; + int msg_type, ret; + + if (reset) + msg_type = NFT_MSG_GETOBJ_RESET; + else + msg_type = NFT_MSG_GETOBJ; n = nftnl_obj_alloc(); if (n == NULL) memory_allocation_error(); - nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETOBJ, family, + nlh = nftnl_nlmsg_build_hdr(buf, msg_type, family, NLM_F_DUMP | NLM_F_ACK, seq); if (table != NULL) nftnl_obj_set(n, NFTNL_OBJ_TABLE, table); + if (type != NFT_OBJECT_UNSPEC) + nftnl_obj_set_u32(n, NFTNL_OBJ_TYPE, type); nftnl_obj_nlmsg_build_payload(nlh, n); nftnl_obj_free(n); diff --git a/src/netlink.c b/src/netlink.c index d11b3c01..68bed201 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -1755,7 +1755,29 @@ int netlink_list_objs(struct netlink_ctx *ctx, const struct handle *h, struct nftnl_obj_list *obj_cache; int err; - obj_cache = mnl_nft_obj_dump(nf_sock, h->family, h->table); + obj_cache = mnl_nft_obj_dump(nf_sock, h->family, h->table, + NFT_OBJECT_UNSPEC, false); + if (obj_cache == NULL) { + if (errno == EINTR) + return -1; + + return netlink_io_error(ctx, loc, + "Could not receive stateful objects 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_reset_objs(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc, uint32_t type) +{ + struct nftnl_obj_list *obj_cache; + int err; + + obj_cache = mnl_nft_obj_dump(nf_sock, h->family, h->table, type, true); if (obj_cache == NULL) { if (errno == EINTR) return -1; diff --git a/src/parser_bison.y b/src/parser_bison.y index fd3f0d82..b571fbbe 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -199,6 +199,7 @@ static void location_update(struct location *loc, struct location *rhs, int n) %token INSERT "insert" %token DELETE "delete" %token LIST "list" +%token RESET "reset" %token FLUSH "flush" %token RENAME "rename" %token DESCRIBE "describe" @@ -442,8 +443,8 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type line %destructor { cmd_free($$); } line -%type base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd -%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd +%type base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd +%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd %type 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 @@ -725,6 +726,7 @@ base_cmd : /* empty */ add_cmd { $$ = $1; } | INSERT insert_cmd { $$ = $2; } | DELETE delete_cmd { $$ = $2; } | LIST list_cmd { $$ = $2; } + | RESET reset_cmd { $$ = $2; } | FLUSH flush_cmd { $$ = $2; } | RENAME rename_cmd { $$ = $2; } | EXPORT export_cmd { $$ = $2; } @@ -968,6 +970,24 @@ list_cmd : TABLE table_spec } ; +reset_cmd : COUNTERS ruleset_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL); + } + | COUNTERS TABLE table_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$3, &@$, NULL); + } + | QUOTAS ruleset_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, NULL); + } + | QUOTAS TABLE table_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$3, &@$, NULL); + } + ; + flush_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_TABLE, &$2, &@$, NULL); diff --git a/src/rule.c b/src/rule.c index 29b14506..9eeb436c 100644 --- a/src/rule.c +++ b/src/rule.c @@ -95,11 +95,13 @@ 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); + if (cmd != CMD_RESET) { + /* 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. @@ -1398,6 +1400,35 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) return 0; } +static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct obj *obj, *next; + struct table *table; + uint32_t type; + int ret; + + switch (cmd->obj) { + case CMD_OBJ_COUNTERS: + type = NFT_OBJECT_COUNTER; + break; + case CMD_OBJ_QUOTAS: + type = NFT_OBJECT_QUOTA; + break; + default: + BUG("invalid command object type %u\n", cmd->obj); + } + + ret = netlink_reset_objs(ctx, &cmd->handle, &cmd->location, type); + list_for_each_entry_safe(obj, next, &ctx->list, list) { + table = table_lookup(&obj->handle); + list_move(&obj->list, &table->objs); + } + if (ret < 0) + return ret; + + return do_list_obj(ctx, cmd, type); +} + static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd) { switch (cmd->obj) { @@ -1518,6 +1549,8 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd) return do_command_delete(ctx, cmd); case CMD_LIST: return do_command_list(ctx, cmd); + case CMD_RESET: + return do_command_reset(ctx, cmd); case CMD_FLUSH: return do_command_flush(ctx, cmd); case CMD_RENAME: diff --git a/src/scanner.l b/src/scanner.l index cb989320..1aa2e96b 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -268,6 +268,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "insert" { return INSERT; } "delete" { return DELETE; } "list" { return LIST; } +"reset" { return RESET; } "flush" { return FLUSH; } "rename" { return RENAME; } "export" { return EXPORT; } -- cgit v1.2.3