</para>
</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>
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 */
unsigned int stateless;
unsigned int ip2name;
unsigned int handle;
+ unsigned int echo;
};
struct nft_ctx {
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:
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);
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[] = {
{
.name = "handle",
.val = OPT_HANDLE_OUTPUT,
},
+ {
+ .name = "echo",
+ .val = OPT_ECHO,
+ },
{
.name = NULL
}
" -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"
case OPT_HANDLE_OUTPUT:
nft.output.handle++;
break;
+ case OPT_ECHO:
+ nft.output.echo++;
+ break;
case OPT_INVALID:
exit(NFT_EXIT_FAILURE);
}
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)
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);
}
/*
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);
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)
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)
{
return -1;
if (set->init != NULL) {
return __do_add_setelems(ctx, &set->handle, set, set->init,
- false);
+ flags);
}
return 0;
}
{
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,
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);
}