When used with add, insert or replace commands, nft tool will print event notifications just like 'nft monitor' does for the same commands. Apart from seeing what a given command will turn out in the rule set, this allows to reliably retrieve a new rule's assigned handle (if used together with --handle option). Here are some examples of how it works: | # nft --echo --handle add table ip t | add table ip t | | # nft --echo --handle add chain ip t c \ | '{ type filter hook forward priority 0; }' | add chain ip t c { type filter hook forward priority 0; policy accept; } | | # nft --echo --handle add rule ip t c tcp dport '{22, 80}' accept | add rule ip t c tcp dport { ssh, http } accept # handle 2 | | # nft --echo --handle add set ip t ipset '{ type ipv4_addr; \ | elements = { 192.168.0.1, 192.168.0.2 }; }' | add set ip t ipset { type ipv4_addr; } | add element ip t ipset { 192.168.0.1 } | add element ip t ipset { 192.168.0.2 } Signed-off-by: Phil Sutter <phil@xxxxxx> --- Changes since v1: - Drop extern declaration of unused variable echo_output. - Reworded --echo description in man page a bit. Changes since v2: - Get rid of NFT_MSG_META_ECHO hack, just use -1 instead. - Fix for unknown tag <cmd> in nft.xml. Changes since v3: - Reuse nft monitor code completely. - Added missing cache updates when adding a rule or named object. - Pass flags on to __do_add_setelems() so that anonymous set elements are cached as well. - Drop long description of echo option from nft.8 since it doesn't apply anymore. --- doc/nft.xml | 10 ++++++++++ include/netlink.h | 2 ++ include/nftables.h | 1 + src/evaluate.c | 7 +++++++ src/main.c | 11 ++++++++++- src/mnl.c | 25 +++++++++++++++++++++++-- src/netlink.c | 20 ++++++++++++++++++-- src/rule.c | 9 +++++++-- 8 files changed, 78 insertions(+), 7 deletions(-) diff --git a/doc/nft.xml b/doc/nft.xml index 4d03a3dbc75bf..6c845013c088d 100644 --- a/doc/nft.xml +++ b/doc/nft.xml @@ -157,6 +157,16 @@ vi:ts=4 sw=4 </listitem> </varlistentry> <varlistentry> + <term><option>-e, --echo</option></term> + <listitem> + <para> + When inserting items into the ruleset using <command>add</command>, + <command>insert</command> or <command>replace</command> commands, + print notifications just like <command>nft monitor</command>. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><option>-I, --includepath <replaceable>directory</replaceable></option></term> <listitem> <para> diff --git a/include/netlink.h b/include/netlink.h index ffbc51d352fa0..47ecef38f9e9d 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -222,4 +222,6 @@ extern int netlink_monitor(struct netlink_mon_handler *monhandler, struct mnl_socket *nf_sock); bool netlink_batch_supported(struct mnl_socket *nf_sock); +int netlink_echo_callback(const struct nlmsghdr *nlh, void *data); + #endif /* NFTABLES_NETLINK_H */ diff --git a/include/nftables.h b/include/nftables.h index 640d3c7e715d8..ca609015274a9 100644 --- a/include/nftables.h +++ b/include/nftables.h @@ -29,6 +29,7 @@ struct output_ctx { unsigned int stateless; unsigned int ip2name; unsigned int handle; + unsigned int echo; }; struct nft_ctx { diff --git a/src/evaluate.c b/src/evaluate.c index d24526fef2954..477fb54d51f26 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -2962,6 +2962,9 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) handle_merge(&cmd->set->handle, &cmd->handle); return set_evaluate(ctx, cmd->set); case CMD_OBJ_RULE: + ret = cache_update(ctx->nf_sock, cmd->op, ctx->msgs); + if (ret < 0) + return ret; handle_merge(&cmd->rule->handle, &cmd->handle); return rule_evaluate(ctx, cmd->rule); case CMD_OBJ_CHAIN: @@ -2975,6 +2978,10 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) case CMD_OBJ_COUNTER: case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: + ret = cache_update(ctx->nf_sock, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + return 0; default: BUG("invalid command object type %u\n", cmd->obj); diff --git a/src/main.c b/src/main.c index 1535153ec815d..86862a1088e0c 100644 --- a/src/main.c +++ b/src/main.c @@ -49,10 +49,11 @@ enum opt_vals { OPT_IP2NAME = 'N', OPT_DEBUG = 'd', OPT_HANDLE_OUTPUT = 'a', + OPT_ECHO = 'e', OPT_INVALID = '?', }; -#define OPTSTRING "hvcf:iI:vnsNa" +#define OPTSTRING "hvcf:iI:vnsNae" static const struct option options[] = { { @@ -105,6 +106,10 @@ static const struct option options[] = { .val = OPT_HANDLE_OUTPUT, }, { + .name = "echo", + .val = OPT_ECHO, + }, + { .name = NULL } }; @@ -128,6 +133,7 @@ static void show_help(const char *name) " -s, --stateless Omit stateful information of ruleset.\n" " -N Translate IP addresses to names.\n" " -a, --handle Output rule handle.\n" +" -e, --echo Echo what has been added, inserted or replaced.\n" " -I, --includepath <directory> Add <directory> to the paths searched for include files. Default is: %s\n" #ifdef DEBUG " --debug <level [,level...]> Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n" @@ -375,6 +381,9 @@ int main(int argc, char * const *argv) case OPT_HANDLE_OUTPUT: nft.output.handle++; break; + case OPT_ECHO: + nft.output.echo++; + break; case OPT_INVALID: exit(NFT_EXIT_FAILURE); } diff --git a/src/mnl.c b/src/mnl.c index 862311a740e0e..031b7f39da8f5 100644 --- a/src/mnl.c +++ b/src/mnl.c @@ -67,11 +67,32 @@ out: return ret; } +struct nft_mnl_talk_cb_data { + int (*cb)(const struct nlmsghdr *nlh, void *data); + void *data; +}; + +static int nft_mnl_talk_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_mnl_talk_cb_data *cbdata = data; + int rc; + + if (cbdata->cb) + rc = cbdata->cb(nlh, cbdata->data); + if (rc) + return rc; + return netlink_echo_callback(nlh, cbdata->data); +} + static int nft_mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len, int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data) { uint32_t portid = mnl_socket_get_portid(nf_sock); + struct nft_mnl_talk_cb_data tcb_data = { + .cb = cb, + .data = cb_data, + }; #ifdef DEBUG if (debug_level & DEBUG_MNL) @@ -81,7 +102,7 @@ nft_mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len, if (mnl_socket_sendto(nf_sock, data, len) < 0) return -1; - return nft_mnl_recv(nf_sock, seq, portid, cb, cb_data); + return nft_mnl_recv(nf_sock, seq, portid, &nft_mnl_talk_cb, &tcb_data); } /* @@ -276,7 +297,7 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list) if (ret == -1) return -1; - ret = mnl_cb_run(rcv_buf, ret, 0, portid, NULL, NULL); + ret = mnl_cb_run(rcv_buf, ret, 0, portid, &netlink_echo_callback, ctx); /* Continue on error, make sure we get all acknowledgments */ if (ret == -1) mnl_err_list_node_add(err_list, errno, nlh->nlmsg_seq); diff --git a/src/netlink.c b/src/netlink.c index 26032f956aba6..b172d2cc76aca 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -464,11 +464,11 @@ int netlink_replace_rule_batch(struct netlink_ctx *ctx, const struct handle *h, const struct location *loc) { struct nftnl_rule *nlr; - int err; + int err, flags = ctx->octx->echo ? NLM_F_ECHO : 0; nlr = alloc_nftnl_rule(&rule->handle); netlink_linearize_rule(ctx, nlr, rule); - err = mnl_nft_rule_batch_replace(nlr, ctx->batch, 0, ctx->seqnum); + err = mnl_nft_rule_batch_replace(nlr, ctx->batch, flags, ctx->seqnum); nftnl_rule_free(nlr); if (err < 0) @@ -3069,6 +3069,22 @@ static int netlink_events_cb(const struct nlmsghdr *nlh, void *data) return ret; } +int netlink_echo_callback(const struct nlmsghdr *nlh, void *data) +{ + struct netlink_mon_handler echo_monh = { + .format = NFTNL_OUTPUT_DEFAULT, + .ctx = data, + .loc = &netlink_location, + .monitor_flags = 0xffffffff, + .cache_needed = true, + }; + + if (!echo_monh.ctx->octx->echo) + return MNL_CB_OK; + + return netlink_events_cb(nlh, &echo_monh); +} + int netlink_monitor(struct netlink_mon_handler *monhandler, struct mnl_socket *nf_sock) { diff --git a/src/rule.c b/src/rule.c index 6b9dbb623b313..fae8352a7c3f9 100644 --- a/src/rule.c +++ b/src/rule.c @@ -1009,7 +1009,7 @@ static int do_add_set(struct netlink_ctx *ctx, const struct handle *h, return -1; if (set->init != NULL) { return __do_add_setelems(ctx, &set->handle, set, set->init, - false); + flags); } return 0; } @@ -1018,6 +1018,9 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) { uint32_t flags = excl ? NLM_F_EXCL : 0; + if (ctx->octx->echo) + flags |= NLM_F_ECHO; + switch (cmd->obj) { case CMD_OBJ_TABLE: return netlink_add_table(ctx, &cmd->handle, &cmd->location, @@ -1056,10 +1059,12 @@ static int do_command_replace(struct netlink_ctx *ctx, struct cmd *cmd) static int do_command_insert(struct netlink_ctx *ctx, struct cmd *cmd) { + uint32_t flags = ctx->octx->echo ? NLM_F_ECHO : 0; + switch (cmd->obj) { case CMD_OBJ_RULE: return netlink_add_rule_batch(ctx, &cmd->handle, - cmd->rule, 0); + cmd->rule, flags); default: BUG("invalid command object type %u\n", cmd->obj); } -- 2.13.1 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html