[PATCH nft, v2] mnl: check for NLM_F_DUMP_INTR when dumping object lists

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This flag allows to detect that an update has ocurred while dumping
any of the object lists. In case of interference, nft cancels the
netlink socket to skip processing the remaining stale entries and
it retries to obtain fresh list of objects.

Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>
---
 include/netlink.h |    2 ++
 src/main.c        |    8 ++++++-
 src/mnl.c         |   15 +++++++++++++
 src/netlink.c     |   30 +++++++++++++++++++++----
 src/rule.c        |   64 +++++++++++++++++++++++++++++++----------------------
 5 files changed, 88 insertions(+), 31 deletions(-)

diff --git a/include/netlink.h b/include/netlink.h
index 4ef7365..af5dcd9 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -137,6 +137,8 @@ extern void netlink_dump_expr(struct nft_rule_expr *nle);
 extern void netlink_dump_set(struct nft_set *nls);
 
 extern int netlink_batch_send(struct list_head *err_list);
+
+extern void netlink_restart(void);
 extern void netlink_abi_error(void) __noreturn;
 extern int netlink_io_error(struct netlink_ctx *ctx,
 			    const struct location *loc, const char *fmt, ...);
diff --git a/src/main.c b/src/main.c
index a446bc6..30ea2c6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -223,8 +223,14 @@ int nft_run(void *scanner, struct parser_state *state, struct list_head *msgs)
 	ret = nft_parse(scanner, state);
 	if (ret != 0 || state->nerrs > 0)
 		return -1;
+retry:
+	ret = nft_netlink(state, msgs);
+	if (ret < 0 && errno == EINTR) {
+		netlink_restart();
+		goto retry;
+	}
 
-	return nft_netlink(state, msgs);
+	return ret;
 }
 
 int main(int argc, char * const *argv)
diff --git a/src/mnl.c b/src/mnl.c
index a816106..d3c91b4 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -363,6 +363,9 @@ static int rule_cb(const struct nlmsghdr *nlh, void *data)
 	struct nft_rule_list *nlr_list = data;
 	struct nft_rule *r;
 
+	if (nlh->nlmsg_flags & NLM_F_DUMP_INTR)
+		return MNL_CB_ERROR;
+
 	r = nft_rule_alloc();
 	if (r == NULL)
 		memory_allocation_error();
@@ -474,6 +477,9 @@ static int chain_cb(const struct nlmsghdr *nlh, void *data)
 	struct nft_chain_list *nlc_list = data;
 	struct nft_chain *c;
 
+	if (nlh->nlmsg_flags & NLM_F_DUMP_INTR)
+		return MNL_CB_ERROR;
+
 	c = nft_chain_alloc();
 	if (c == NULL)
 		memory_allocation_error();
@@ -603,6 +609,9 @@ static int table_cb(const struct nlmsghdr *nlh, void *data)
 	struct nft_table_list *nlt_list = data;
 	struct nft_table *t;
 
+	if (nlh->nlmsg_flags & NLM_F_DUMP_INTR)
+		return MNL_CB_ERROR;
+
 	t = nft_table_alloc();
 	if (t == NULL)
 		memory_allocation_error();
@@ -736,6 +745,9 @@ static int set_cb(const struct nlmsghdr *nlh, void *data)
 	struct nft_set_list *nls_list = data;
 	struct nft_set *s;
 
+	if (nlh->nlmsg_flags & NLM_F_DUMP_INTR)
+		return MNL_CB_ERROR;
+
 	s = nft_set_alloc();
 	if (s == NULL)
 		memory_allocation_error();
@@ -839,6 +851,9 @@ int mnl_nft_setelem_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
 
 static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
 {
+	if (nlh->nlmsg_flags & NLM_F_DUMP_INTR)
+		return MNL_CB_ERROR;
+
 	nft_set_elems_nlmsg_parse(nlh, data);
 	return MNL_CB_OK;
 }
diff --git a/src/netlink.c b/src/netlink.c
index 987dd63..5bc4669 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -70,6 +70,12 @@ static void __exit netlink_close_sock(void)
 		mnl_socket_close(nf_mon_sock);
 }
 
+void netlink_restart(void)
+{
+	netlink_close_sock();
+	netlink_open_sock();
+}
+
 static void netlink_open_mon_sock(void)
 {
 	nf_mon_sock = nfsock_open();
@@ -443,10 +449,14 @@ static int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h,
 	struct nft_rule_list *rule_cache;
 
 	rule_cache = mnl_nft_rule_dump(nf_sock, h->family);
-	if (rule_cache == NULL)
+	if (rule_cache == NULL) {
+		if (errno == EINTR)
+			return -1;
+
 		return netlink_io_error(ctx, loc,
 					"Could not receive rules from kernel: %s",
 					strerror(errno));
+	}
 
 	ctx->data = h;
 	nft_rule_list_foreach(rule_cache, list_rule_cb, ctx);
@@ -704,10 +714,14 @@ int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h,
 	struct chain *chain;
 
 	chain_cache = mnl_nft_chain_dump(nf_sock, h->family);
-	if (chain_cache == NULL)
+	if (chain_cache == NULL) {
+		if (errno == EINTR)
+			return -1;
+
 		return netlink_io_error(ctx, loc,
 					"Could not receive chains from kernel: %s",
 					strerror(errno));
+	}
 
 	ctx->data = h;
 	nft_chain_list_foreach(chain_cache, list_chain_cb, ctx);
@@ -907,10 +921,14 @@ int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h,
 	struct nft_table_list *table_cache;
 
 	table_cache = mnl_nft_table_dump(nf_sock, h->family);
-	if (table_cache == NULL)
+	if (table_cache == NULL) {
+		if (errno == EINTR)
+			return -1;
+
 		return netlink_io_error(ctx, loc,
 					"Could not receive tables from kernel: %s",
 					strerror(errno));
+	}
 
 	nft_table_list_foreach(table_cache, list_table_cb, ctx);
 	nft_table_list_free(table_cache);
@@ -1177,10 +1195,14 @@ int netlink_list_sets(struct netlink_ctx *ctx, const struct handle *h,
 	int err;
 
 	set_cache = mnl_nft_set_dump(nf_sock, h->family, h->table);
-	if (set_cache == NULL)
+	if (set_cache == NULL) {
+		if (errno == EINTR)
+			return -1;
+
 		return netlink_io_error(ctx, loc,
 					"Could not receive sets from kernel: %s",
 					strerror(errno));
+	}
 
 	err = nft_set_list_foreach(set_cache, list_set_cb, ctx);
 	nft_set_list_free(set_cache);
diff --git a/src/rule.c b/src/rule.c
index a7bc6f4..1e54526 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -14,6 +14,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <inttypes.h>
+#include <errno.h>
 
 #include <statement.h>
 #include <rule.h>
@@ -701,12 +702,28 @@ static int do_command_export(struct netlink_ctx *ctx, struct cmd *cmd)
 	return 0;
 }
 
+static void do_command_list_cleanup(struct table *table)
+{
+	struct chain *chain, *nchain;
+	struct set *set, *nset;
+
+	list_for_each_entry_safe(chain, nchain, &table->chains, list) {
+		list_del(&chain->list);
+		chain_free(chain);
+	}
+
+	list_for_each_entry_safe(set, nset, &table->sets, list) {
+		list_del(&set->list);
+		set_free(set);
+	}
+}
+
 static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 {
 	struct table *table = NULL;
-	struct chain *chain, *nchain;
+	struct chain *chain;
 	struct rule *rule, *nrule;
-	struct set *set, *nset;
+	struct set *set;
 
 	/* No need to allocate the table object when listing all tables */
 	if (cmd->handle.table != NULL) {
@@ -726,7 +743,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 
 			if (netlink_list_tables(ctx, &cmd->handle,
 						&cmd->location) < 0)
-				return -1;
+				goto err;
 
 			list_for_each_entry(table, &ctx->list, list) {
 				printf("table %s\n", table->handle.table);
@@ -735,39 +752,41 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 		}
 		/* List content of this table */
 		if (do_list_sets(ctx, &cmd->location, table) < 0)
-			return -1;
+			goto err;
 		if (netlink_list_chains(ctx, &cmd->handle, &cmd->location) < 0)
-			return -1;
+			goto err;
 		list_splice_tail_init(&ctx->list, &table->chains);
 		if (netlink_list_table(ctx, &cmd->handle, &cmd->location) < 0)
-			return -1;
+			goto err;
 		break;
 	case CMD_OBJ_CHAIN:
 		if (do_list_sets(ctx, &cmd->location, table) < 0)
-			return -1;
+			goto err;
 		if (netlink_list_chains(ctx, &cmd->handle, &cmd->location) < 0)
-			return -1;
+			goto err;
 		list_splice_tail_init(&ctx->list, &table->chains);
 		if (netlink_list_table(ctx, &cmd->handle, &cmd->location) < 0)
-			return -1;
+			goto err;
 		break;
 	case CMD_OBJ_SETS:
 		if (netlink_list_sets(ctx, &cmd->handle, &cmd->location) < 0)
-			return -1;
+			goto err;
 		list_for_each_entry(set, &ctx->list, list){
 			if (netlink_get_setelems(ctx, &set->handle,
-						 &cmd->location, set) < 0)
-				return -1;
+						 &cmd->location, set) < 0) {
+				goto err;
+			}
 			set_print(set);
 		}
 		return 0;
 	case CMD_OBJ_SET:
 		if (netlink_get_set(ctx, &cmd->handle, &cmd->location) < 0)
-			return -1;
+			goto err;
 		list_for_each_entry(set, &ctx->list, list) {
 			if (netlink_get_setelems(ctx, &cmd->handle,
-						 &cmd->location, set) < 0)
-				return -1;
+						 &cmd->location, set) < 0) {
+				goto err;
+			}
 			set_print(set);
 		}
 		return 0;
@@ -787,18 +806,11 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 	}
 
 	table_print(table);
-
-	list_for_each_entry_safe(chain, nchain, &table->chains, list) {
-		list_del(&chain->list);
-		chain_free(chain);
-	}
-
-	list_for_each_entry_safe(set, nset, &table->sets, list) {
-		list_del(&set->list);
-		set_free(set);
-	}
-
+	do_command_list_cleanup(table);
 	return 0;
+err:
+	do_command_list_cleanup(table);
+	return -1;
 }
 
 static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd)
-- 
1.7.10.4

--
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




[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux