Blame SOURCES/0006-xtables-Introduce-per-table-chain-caches.patch

029dc7
From a09e8ae2a1e2ff589af839ad3460493fc04306d7 Mon Sep 17 00:00:00 2001
029dc7
From: Phil Sutter <phil@nwl.cc>
029dc7
Date: Thu, 15 Nov 2018 14:53:02 +0100
029dc7
Subject: [PATCH] xtables: Introduce per table chain caches
029dc7
029dc7
Being able to omit the previously obligatory table name check when
029dc7
iterating over the chain cache might help restore performance with large
029dc7
rulesets in xtables-save and -restore.
029dc7
029dc7
There is one subtle quirk in the code: flush_chain_cache() did free the
029dc7
global chain cache if not called with a table name but didn't if a table
029dc7
name was given even if it emptied the chain cache. In other places,
029dc7
chain_cache being non-NULL prevented a cache update from happening, so
029dc7
this patch establishes the same behaviour (for each individual chain
029dc7
cache) since otherwise unexpected cache updates lead to weird problems.
029dc7
029dc7
Signed-off-by: Phil Sutter <phil@nwl.cc>
029dc7
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
029dc7
(cherry picked from commit c58ecf9f8bcb7619a27ef8ffaddf847a562475a5)
029dc7
Signed-off-by: Phil Sutter <psutter@redhat.com>
029dc7
---
029dc7
 iptables/nft-shared.h      |   3 +-
029dc7
 iptables/nft.c             | 160 +++++++++++++++++--------------------
029dc7
 iptables/nft.h             |  10 ++-
029dc7
 iptables/xtables-restore.c |  16 ++--
029dc7
 iptables/xtables-save.c    |  12 +--
029dc7
 5 files changed, 95 insertions(+), 106 deletions(-)
029dc7
029dc7
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
029dc7
index e3ecdb4d23df3..9a61d8d2863e3 100644
029dc7
--- a/iptables/nft-shared.h
029dc7
+++ b/iptables/nft-shared.h
029dc7
@@ -251,7 +251,8 @@ struct nftnl_chain_list;
029dc7
 
029dc7
 struct nft_xt_restore_cb {
029dc7
 	void (*table_new)(struct nft_handle *h, const char *table);
029dc7
-	struct nftnl_chain_list *(*chain_list)(struct nft_handle *h);
029dc7
+	struct nftnl_chain_list *(*chain_list)(struct nft_handle *h,
029dc7
+					       const char *table);
029dc7
 	void (*chain_del)(struct nftnl_chain_list *clist, const char *curtable,
029dc7
 			  const char *chain);
029dc7
 	int (*chain_user_flush)(struct nft_handle *h,
029dc7
diff --git a/iptables/nft.c b/iptables/nft.c
029dc7
index 6863d851e44c2..36529048a0ca6 100644
029dc7
--- a/iptables/nft.c
029dc7
+++ b/iptables/nft.c
029dc7
@@ -673,15 +673,17 @@ nft_chain_builtin_find(struct builtin_table *t, const char *chain)
029dc7
 static void nft_chain_builtin_init(struct nft_handle *h,
029dc7
 				   struct builtin_table *table)
029dc7
 {
029dc7
-	struct nftnl_chain_list *list = nft_chain_list_get(h);
029dc7
+	struct nftnl_chain_list *list = nft_chain_list_get(h, table->name);
029dc7
 	struct nftnl_chain *c;
029dc7
 	int i;
029dc7
 
029dc7
+	if (!list)
029dc7
+		return;
029dc7
+
029dc7
 	/* Initialize built-in chains if they don't exist yet */
029dc7
 	for (i=0; i < NF_INET_NUMHOOKS && table->chains[i].name != NULL; i++) {
029dc7
 
029dc7
-		c = nft_chain_list_find(list, table->name,
029dc7
-					table->chains[i].name);
029dc7
+		c = nft_chain_list_find(list, table->chains[i].name);
029dc7
 		if (c != NULL)
029dc7
 			continue;
029dc7
 
029dc7
@@ -782,27 +784,33 @@ static void flush_rule_cache(struct nft_handle *h, const char *tablename)
029dc7
 
029dc7
 static int __flush_chain_cache(struct nftnl_chain *c, void *data)
029dc7
 {
029dc7
-	const char *tablename = data;
029dc7
-
029dc7
-	if (!strcmp(nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), tablename)) {
029dc7
-		nftnl_chain_list_del(c);
029dc7
-		nftnl_chain_free(c);
029dc7
-	}
029dc7
+	nftnl_chain_list_del(c);
029dc7
+	nftnl_chain_free(c);
029dc7
 
029dc7
 	return 0;
029dc7
 }
029dc7
 
029dc7
 static void flush_chain_cache(struct nft_handle *h, const char *tablename)
029dc7
 {
029dc7
-	if (!h->chain_cache)
029dc7
-		return;
029dc7
+	int i;
029dc7
 
029dc7
-	if (tablename) {
029dc7
-		nftnl_chain_list_foreach(h->chain_cache, __flush_chain_cache,
029dc7
-					 (void *)tablename);
029dc7
-	} else {
029dc7
-		nftnl_chain_list_free(h->chain_cache);
029dc7
-		h->chain_cache = NULL;
029dc7
+	for (i = 0; i < NFT_TABLE_MAX; i++) {
029dc7
+		if (h->tables[i].name == NULL)
029dc7
+			continue;
029dc7
+
029dc7
+		if (tablename && strcmp(h->tables[i].name, tablename))
029dc7
+			continue;
029dc7
+
029dc7
+		if (h->tables[i].chain_cache) {
029dc7
+			if (tablename) {
029dc7
+				nftnl_chain_list_foreach(h->tables[i].chain_cache,
029dc7
+							 __flush_chain_cache, NULL);
029dc7
+				break;
029dc7
+			} else {
029dc7
+				nftnl_chain_list_free(h->tables[i].chain_cache);
029dc7
+				h->tables[i].chain_cache = NULL;
029dc7
+			}
029dc7
+		}
029dc7
 	}
029dc7
 }
029dc7
 
029dc7
@@ -1244,8 +1252,9 @@ nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
029dc7
 
029dc7
 static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
029dc7
 {
029dc7
+	struct nft_handle *h = data;
029dc7
+	struct builtin_table *t;
029dc7
 	struct nftnl_chain *c;
029dc7
-	struct nftnl_chain_list *list = data;
029dc7
 
029dc7
 	c = nftnl_chain_alloc();
029dc7
 	if (c == NULL)
029dc7
@@ -1254,7 +1263,18 @@ static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
029dc7
 	if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
029dc7
 		goto out;
029dc7
 
029dc7
-	nftnl_chain_list_add_tail(c, list);
029dc7
+	t = nft_table_builtin_find(h,
029dc7
+			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE));
029dc7
+	if (!t)
029dc7
+		goto out;
029dc7
+
029dc7
+	if (!t->chain_cache) {
029dc7
+		t->chain_cache = nftnl_chain_list_alloc();
029dc7
+		if (!t->chain_cache)
029dc7
+			goto out;
029dc7
+	}
029dc7
+
029dc7
+	nftnl_chain_list_add_tail(c, t->chain_cache);
029dc7
 
029dc7
 	return MNL_CB_OK;
029dc7
 out:
029dc7
@@ -1263,35 +1283,34 @@ err:
029dc7
 	return MNL_CB_OK;
029dc7
 }
029dc7
 
029dc7
-struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h)
029dc7
+struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
029dc7
+					    const char *table)
029dc7
 {
029dc7
 	char buf[16536];
029dc7
 	struct nlmsghdr *nlh;
029dc7
-	struct nftnl_chain_list *list;
029dc7
+	struct builtin_table *t;
029dc7
 	int ret;
029dc7
 
029dc7
-	if (h->chain_cache)
029dc7
-		return h->chain_cache;
029dc7
-retry:
029dc7
-	list = nftnl_chain_list_alloc();
029dc7
-	if (list == NULL) {
029dc7
-		errno = ENOMEM;
029dc7
+	t = nft_table_builtin_find(h, table);
029dc7
+	if (!t)
029dc7
 		return NULL;
029dc7
-	}
029dc7
 
029dc7
+	if (t->chain_cache)
029dc7
+		return t->chain_cache;
029dc7
+retry:
029dc7
 	nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
029dc7
 					NLM_F_DUMP, h->seq);
029dc7
 
029dc7
-	ret = mnl_talk(h, nlh, nftnl_chain_list_cb, list);
029dc7
+	ret = mnl_talk(h, nlh, nftnl_chain_list_cb, h);
029dc7
 	if (ret < 0 && errno == EINTR) {
029dc7
 		assert(nft_restart(h) >= 0);
029dc7
-		nftnl_chain_list_free(list);
029dc7
 		goto retry;
029dc7
 	}
029dc7
 
029dc7
-	h->chain_cache = list;
029dc7
+	if (!t->chain_cache)
029dc7
+		t->chain_cache = nftnl_chain_list_alloc();
029dc7
 
029dc7
-	return list;
029dc7
+	return t->chain_cache;
029dc7
 }
029dc7
 
029dc7
 static const char *policy_name[NF_ACCEPT+1] = {
029dc7
@@ -1299,8 +1318,7 @@ static const char *policy_name[NF_ACCEPT+1] = {
029dc7
 	[NF_ACCEPT] = "ACCEPT",
029dc7
 };
029dc7
 
029dc7
-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
029dc7
-		   const char *table)
029dc7
+int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
029dc7
 {
029dc7
 	struct nftnl_chain_list_iter *iter;
029dc7
 	struct nft_family_ops *ops;
029dc7
@@ -1314,13 +1332,8 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
029dc7
 
029dc7
 	c = nftnl_chain_list_iter_next(iter);
029dc7
 	while (c != NULL) {
029dc7
-		const char *chain_table =
029dc7
-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
029dc7
 		const char *policy = NULL;
029dc7
 
029dc7
-		if (strcmp(table, chain_table) != 0)
029dc7
-			goto next;
029dc7
-
029dc7
 		if (nft_chain_builtin(c)) {
029dc7
 			uint32_t pol = NF_ACCEPT;
029dc7
 
029dc7
@@ -1331,7 +1344,7 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
029dc7
 
029dc7
 		if (ops->save_chain)
029dc7
 			ops->save_chain(c, policy);
029dc7
-next:
029dc7
+
029dc7
 		c = nftnl_chain_list_iter_next(iter);
029dc7
 	}
029dc7
 
029dc7
@@ -1502,7 +1515,7 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
029dc7
 
029dc7
 	nft_fn = nft_rule_flush;
029dc7
 
029dc7
-	list = nft_chain_list_get(h);
029dc7
+	list = nft_chain_list_get(h, table);
029dc7
 	if (list == NULL) {
029dc7
 		ret = 1;
029dc7
 		goto err;
029dc7
@@ -1516,21 +1529,16 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
029dc7
 
029dc7
 	c = nftnl_chain_list_iter_next(iter);
029dc7
 	while (c != NULL) {
029dc7
-		const char *table_name =
029dc7
-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
029dc7
 		const char *chain_name =
029dc7
 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
029dc7
 
029dc7
-		if (strcmp(table, table_name) != 0)
029dc7
-			goto next;
029dc7
-
029dc7
 		if (chain != NULL && strcmp(chain, chain_name) != 0)
029dc7
 			goto next;
029dc7
 
029dc7
 		if (verbose)
029dc7
 			fprintf(stdout, "Flushing chain `%s'\n", chain_name);
029dc7
 
029dc7
-		__nft_rule_flush(h, table_name, chain_name);
029dc7
+		__nft_rule_flush(h, table, chain_name);
029dc7
 
029dc7
 		if (chain != NULL)
029dc7
 			break;
029dc7
@@ -1546,6 +1554,7 @@ err:
029dc7
 
029dc7
 int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
029dc7
 {
029dc7
+	struct nftnl_chain_list *list;
029dc7
 	struct nftnl_chain *c;
029dc7
 	int ret;
029dc7
 
029dc7
@@ -1564,9 +1573,9 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
029dc7
 
029dc7
 	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
029dc7
 
029dc7
-	nft_chain_list_get(h);
029dc7
-
029dc7
-	nftnl_chain_list_add(c, h->chain_cache);
029dc7
+	list = nft_chain_list_get(h, table);
029dc7
+	if (list)
029dc7
+		nftnl_chain_list_add(c, list);
029dc7
 
029dc7
 	/* the core expects 1 for success and 0 for error */
029dc7
 	return ret == 0 ? 1 : 0;
029dc7
@@ -1588,7 +1597,7 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
029dc7
 
029dc7
 	nft_fn = nft_chain_user_del;
029dc7
 
029dc7
-	list = nft_chain_list_get(h);
029dc7
+	list = nft_chain_list_get(h, table);
029dc7
 	if (list == NULL)
029dc7
 		goto err;
029dc7
 
029dc7
@@ -1598,8 +1607,6 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
029dc7
 
029dc7
 	c = nftnl_chain_list_iter_next(iter);
029dc7
 	while (c != NULL) {
029dc7
-		const char *table_name =
029dc7
-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
029dc7
 		const char *chain_name =
029dc7
 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
029dc7
 
029dc7
@@ -1607,9 +1614,6 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
029dc7
 		if (nft_chain_builtin(c))
029dc7
 			goto next;
029dc7
 
029dc7
-		if (strcmp(table, table_name) != 0)
029dc7
-			goto next;
029dc7
-
029dc7
 		if (chain != NULL && strcmp(chain, chain_name) != 0)
029dc7
 			goto next;
029dc7
 
029dc7
@@ -1644,8 +1648,7 @@ err:
029dc7
 }
029dc7
 
029dc7
 struct nftnl_chain *
029dc7
-nft_chain_list_find(struct nftnl_chain_list *list,
029dc7
-		    const char *table, const char *chain)
029dc7
+nft_chain_list_find(struct nftnl_chain_list *list, const char *chain)
029dc7
 {
029dc7
 	struct nftnl_chain_list_iter *iter;
029dc7
 	struct nftnl_chain *c;
029dc7
@@ -1656,14 +1659,9 @@ nft_chain_list_find(struct nftnl_chain_list *list,
029dc7
 
029dc7
 	c = nftnl_chain_list_iter_next(iter);
029dc7
 	while (c != NULL) {
029dc7
-		const char *table_name =
029dc7
-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
029dc7
 		const char *chain_name =
029dc7
 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
029dc7
 
029dc7
-		if (strcmp(table, table_name) != 0)
029dc7
-			goto next;
029dc7
-
029dc7
 		if (strcmp(chain, chain_name) != 0)
029dc7
 			goto next;
029dc7
 
029dc7
@@ -1681,11 +1679,11 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
029dc7
 {
029dc7
 	struct nftnl_chain_list *list;
029dc7
 
029dc7
-	list = nft_chain_list_get(h);
029dc7
+	list = nft_chain_list_get(h, table);
029dc7
 	if (list == NULL)
029dc7
 		return NULL;
029dc7
 
029dc7
-	return nft_chain_list_find(list, table, chain);
029dc7
+	return nft_chain_list_find(list, chain);
029dc7
 }
029dc7
 
029dc7
 bool nft_chain_exists(struct nft_handle *h,
029dc7
@@ -2297,7 +2295,9 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
029dc7
 		return 1;
029dc7
 	}
029dc7
 
029dc7
-	list = nft_chain_list_get(h);
029dc7
+	list = nft_chain_list_get(h, table);
029dc7
+	if (!list)
029dc7
+		goto err; /* XXX: return 0 instead? */
029dc7
 
029dc7
 	iter = nftnl_chain_list_iter_create(list);
029dc7
 	if (iter == NULL)
029dc7
@@ -2308,8 +2308,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
029dc7
 
029dc7
 	c = nftnl_chain_list_iter_next(iter);
029dc7
 	while (c != NULL) {
029dc7
-		const char *chain_table =
029dc7
-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
029dc7
 		const char *chain_name =
029dc7
 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
029dc7
 		uint32_t policy =
029dc7
@@ -2326,8 +2324,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
029dc7
 		if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
029dc7
 			basechain = true;
029dc7
 
029dc7
-		if (strcmp(table, chain_table) != 0)
029dc7
-			goto next;
029dc7
 		if (chain) {
029dc7
 			if (strcmp(chain, chain_name) != 0)
029dc7
 				goto next;
029dc7
@@ -2442,7 +2438,9 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
029dc7
 		return 0;
029dc7
 	}
029dc7
 
029dc7
-	list = nft_chain_list_get(h);
029dc7
+	list = nft_chain_list_get(h, table);
029dc7
+	if (!list)
029dc7
+		goto err; /* XXX: correct? */
029dc7
 
029dc7
 	/* Dump policies and custom chains first */
029dc7
 	if (!rulenum)
029dc7
@@ -2460,13 +2458,9 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
029dc7
 
029dc7
 	c = nftnl_chain_list_iter_next(iter);
029dc7
 	while (c != NULL) {
029dc7
-		const char *chain_table =
029dc7
-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
029dc7
 		const char *chain_name =
029dc7
 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
029dc7
 
029dc7
-		if (strcmp(table, chain_table) != 0)
029dc7
-			goto next;
029dc7
 		if (chain && strcmp(chain, chain_name) != 0)
029dc7
 			goto next;
029dc7
 
029dc7
@@ -3045,7 +3039,7 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
029dc7
 	struct nftnl_chain *c;
029dc7
 	int ret = 0;
029dc7
 
029dc7
-	list = nft_chain_list_get(h);
029dc7
+	list = nft_chain_list_get(h, table);
029dc7
 	if (list == NULL)
029dc7
 		goto err;
029dc7
 
029dc7
@@ -3057,11 +3051,6 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
029dc7
 	while (c != NULL) {
029dc7
 		const char *chain_name =
029dc7
 			nftnl_chain_get(c, NFTNL_CHAIN_NAME);
029dc7
-		const char *chain_table =
029dc7
-			nftnl_chain_get(c, NFTNL_CHAIN_TABLE);
029dc7
-
029dc7
-		if (strcmp(table, chain_table) != 0)
029dc7
-			goto next;
029dc7
 
029dc7
 		if (chain != NULL && strcmp(chain, chain_name) != 0)
029dc7
 			goto next;
029dc7
@@ -3202,7 +3191,7 @@ static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename
029dc7
 	struct nftnl_chain *chain;
029dc7
 	int ret = 0;
029dc7
 
029dc7
-	list = nft_chain_list_get(h);
029dc7
+	list = nft_chain_list_get(h, tablename);
029dc7
 	if (list == NULL)
029dc7
 		return -1;
029dc7
 
029dc7
@@ -3212,12 +3201,7 @@ static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename
029dc7
 
029dc7
 	chain = nftnl_chain_list_iter_next(iter);
029dc7
 	while (chain != NULL) {
029dc7
-		const char *chain_table;
029dc7
-
029dc7
-		chain_table = nftnl_chain_get_str(chain, NFTNL_CHAIN_TABLE);
029dc7
-
029dc7
-		if (strcmp(chain_table, tablename) ||
029dc7
-		    !nft_chain_builtin(chain))
029dc7
+		if (!nft_chain_builtin(chain))
029dc7
 			goto next;
029dc7
 
029dc7
 		ret = nft_is_chain_compatible(h, chain);
029dc7
diff --git a/iptables/nft.h b/iptables/nft.h
029dc7
index 052105fc6f3cd..6229221bd51f7 100644
029dc7
--- a/iptables/nft.h
029dc7
+++ b/iptables/nft.h
029dc7
@@ -25,6 +25,7 @@ struct builtin_table {
029dc7
 	const char *name;
029dc7
 	struct builtin_chain chains[NF_INET_NUMHOOKS];
029dc7
 	bool initialized;
029dc7
+	struct nftnl_chain_list *chain_cache;
029dc7
 };
029dc7
 
029dc7
 struct nft_handle {
029dc7
@@ -38,7 +39,6 @@ struct nft_handle {
029dc7
 	struct list_head	err_list;
029dc7
 	struct nft_family_ops	*ops;
029dc7
 	struct builtin_table	*tables;
029dc7
-	struct nftnl_chain_list	*chain_cache;
029dc7
 	struct nftnl_rule_list	*rule_cache;
029dc7
 	bool			restore;
029dc7
 	int8_t			config_done;
029dc7
@@ -78,9 +78,11 @@ struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *t
029dc7
 struct nftnl_chain;
029dc7
 
029dc7
 int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters);
029dc7
-struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h);
029dc7
-struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list, const char *table, const char *chain);
029dc7
-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list, const char *table);
029dc7
+struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
029dc7
+					    const char *table);
029dc7
+struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list,
029dc7
+					const char *chain);
029dc7
+int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list);
029dc7
 int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table);
029dc7
 int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table, bool verbose);
029dc7
 int nft_chain_user_flush(struct nft_handle *h, struct nftnl_chain_list *list,
029dc7
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
029dc7
index f529774054215..a46a92955a01a 100644
029dc7
--- a/iptables/xtables-restore.c
029dc7
+++ b/iptables/xtables-restore.c
029dc7
@@ -56,11 +56,12 @@ static void print_usage(const char *name, const char *version)
029dc7
 			"	   [ --ipv6 ]\n", name);
029dc7
 }
029dc7
 
029dc7
-static struct nftnl_chain_list *get_chain_list(struct nft_handle *h)
029dc7
+static struct nftnl_chain_list *get_chain_list(struct nft_handle *h,
029dc7
+					       const char *table)
029dc7
 {
029dc7
 	struct nftnl_chain_list *chain_list;
029dc7
 
029dc7
-	chain_list = nft_chain_list_get(h);
029dc7
+	chain_list = nft_chain_list_get(h, table);
029dc7
 	if (chain_list == NULL)
029dc7
 		xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");
029dc7
 
029dc7
@@ -72,7 +73,7 @@ static void chain_delete(struct nftnl_chain_list *clist, const char *curtable,
029dc7
 {
029dc7
 	struct nftnl_chain *chain_obj;
029dc7
 
029dc7
-	chain_obj = nft_chain_list_find(clist, curtable, chain);
029dc7
+	chain_obj = nft_chain_list_find(clist, chain);
029dc7
 	/* This chain has been found, delete from list. Later
029dc7
 	 * on, unvisited chains will be purged out.
029dc7
 	 */
029dc7
@@ -112,9 +113,6 @@ void xtables_restore_parse(struct nft_handle *h,
029dc7
 
029dc7
 	line = 0;
029dc7
 
029dc7
-	if (cb->chain_list)
029dc7
-		chain_list = cb->chain_list(h);
029dc7
-
029dc7
 	/* Grab standard input. */
029dc7
 	while (fgets(buffer, sizeof(buffer), p->in)) {
029dc7
 		int ret = 0;
029dc7
@@ -165,6 +163,9 @@ void xtables_restore_parse(struct nft_handle *h,
029dc7
 			if (p->tablename && (strcmp(p->tablename, table) != 0))
029dc7
 				continue;
029dc7
 
029dc7
+			if (cb->chain_list)
029dc7
+				chain_list = cb->chain_list(h, table);
029dc7
+
029dc7
 			if (noflush == 0) {
029dc7
 				DEBUGP("Cleaning all chains of table '%s'\n",
029dc7
 					table);
029dc7
@@ -197,8 +198,7 @@ void xtables_restore_parse(struct nft_handle *h,
029dc7
 				if (cb->chain_del)
029dc7
 					cb->chain_del(chain_list, curtable->name,
029dc7
 						      chain);
029dc7
-			} else if (nft_chain_list_find(chain_list,
029dc7
-						       curtable->name, chain)) {
029dc7
+			} else if (nft_chain_list_find(chain_list, chain)) {
029dc7
 				chain_exists = true;
029dc7
 				/* Apparently -n still flushes existing user
029dc7
 				 * defined chains that are redefined. Otherwise,
029dc7
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
029dc7
index bed3ee0318995..d121d50e180ff 100644
029dc7
--- a/iptables/xtables-save.c
029dc7
+++ b/iptables/xtables-save.c
029dc7
@@ -73,7 +73,9 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
029dc7
 		return 0;
029dc7
 	}
029dc7
 
029dc7
-	chain_list = nft_chain_list_get(h);
029dc7
+	chain_list = nft_chain_list_get(h, tablename);
029dc7
+	if (!chain_list)
029dc7
+		return 0;
029dc7
 
029dc7
 	time_t now = time(NULL);
029dc7
 
029dc7
@@ -83,7 +85,7 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
029dc7
 
029dc7
 	/* Dump out chain names first,
029dc7
 	 * thereby preventing dependency conflicts */
029dc7
-	nft_chain_save(h, chain_list, tablename);
029dc7
+	nft_chain_save(h, chain_list);
029dc7
 	nft_rule_save(h, tablename, counters ? 0 : FMT_NOCOUNTS);
029dc7
 
029dc7
 	now = time(NULL);
029dc7
@@ -257,7 +259,7 @@ static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters
029dc7
 		return 0;
029dc7
 	}
029dc7
 
029dc7
-	chain_list = nft_chain_list_get(h);
029dc7
+	chain_list = nft_chain_list_get(h, tablename);
029dc7
 
029dc7
 	if (first) {
029dc7
 		now = time(NULL);
029dc7
@@ -272,7 +274,7 @@ static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters
029dc7
 
029dc7
 	/* Dump out chain names first,
029dc7
 	 * thereby preventing dependency conflicts */
029dc7
-	nft_chain_save(h, chain_list, tablename);
029dc7
+	nft_chain_save(h, chain_list);
029dc7
 	nft_rule_save(h, tablename, format);
029dc7
 	printf("\n");
029dc7
 	return 0;
029dc7
@@ -399,7 +401,7 @@ int xtables_arp_save_main(int argc, char **argv)
029dc7
 	}
029dc7
 
029dc7
 	printf("*filter\n");
029dc7
-	nft_chain_save(&h, nft_chain_list_get(&h), "filter");
029dc7
+	nft_chain_save(&h, nft_chain_list_get(&h, "filter"));
029dc7
 	nft_rule_save(&h, "filter", show_counters ? 0 : FMT_NOCOUNTS);
029dc7
 	printf("\n");
029dc7
 	nft_fini(&h);
029dc7
-- 
029dc7
2.21.0
029dc7