diff mbox series

[iptables,v2] xtables-restore: Fix flushing referenced custom chains

Message ID 20180906173320.1330-1-phil@nwl.cc
State Accepted
Delegated to: Pablo Neira
Headers show
Series [iptables,v2] xtables-restore: Fix flushing referenced custom chains | expand

Commit Message

Phil Sutter Sept. 6, 2018, 5:33 p.m. UTC
The logic to replicate 'iptables-restore --noflush' behaviour of
flushing custom chains if listed in the dump was broken for chains being
referenced. A minimal dump reproducing the issue is:

| *filter
| :foobar - [0:0]
| -I INPUT -j foobar
| -A foobar -j ACCEPT
| COMMIT

With --noflush, this can be restored just once in iptables-nft-restore.
Consecutive attempts return an error since xtables tries to delete the
referenced chain and recreate it instead of performing a real flush.

Fix this by really flushing the custom chain in 'chain_user_flush'
callback and running 'chain_user_add' callback only if the chain doesn't
exist already.

Fixes: df3d92bec6007 ("xtables-compat-restore: flush user-defined chains with -n")
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v1:
- Add chain_exists logic, required for non-existing chains
  (chain_user_add wasn't called in that case).
---
 iptables/nft.c             | 10 ++--------
 iptables/xtables-restore.c |  8 ++++++--
 2 files changed, 8 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/iptables/nft.c b/iptables/nft.c
index 7123060b34ef5..77ad38bea5211 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -1491,7 +1491,6 @@  static int __nft_chain_user_flush(struct nftnl_chain *c, void *data)
 	struct nft_handle *h = d->handle;
 	const char *table = d->table;
 	const char *chain = d->chain;
-	int ret;
 
 	if (strcmp(table, table_name) != 0)
 		return 0;
@@ -1499,13 +1498,8 @@  static int __nft_chain_user_flush(struct nftnl_chain *c, void *data)
 	if (strcmp(chain, chain_name) != 0)
 		return 0;
 
-	if (!nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM)) {
-		ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_FLUSH, c);
-		if (ret < 0)
-			return ret;
-
-		nftnl_chain_list_del(c);
-	}
+	if (!nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM))
+		__nft_rule_flush(h, table, chain);
 
 	return 0;
 }
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index 3274543677329..9d15593f95eed 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -182,6 +182,7 @@  void xtables_restore_parse(struct nft_handle *h,
 			/* New chain. */
 			char *policy, *chain = NULL;
 			struct xt_counters count = {};
+			bool chain_exists = false;
 
 			chain = strtok(buffer+1, " \t\n");
 			DEBUGP("line %u, chain '%s'\n", line, chain);
@@ -196,7 +197,9 @@  void xtables_restore_parse(struct nft_handle *h,
 				if (cb->chain_del)
 					cb->chain_del(chain_list, curtable->name,
 						      chain);
-			} else {
+			} else if (nft_chain_list_find(chain_list,
+						       curtable->name, chain)) {
+				chain_exists = true;
 				/* Apparently -n still flushes existing user
 				 * defined chains that are redefined. Otherwise,
 				 * leave them as is.
@@ -246,7 +249,8 @@  void xtables_restore_parse(struct nft_handle *h,
 				ret = 1;
 
 			} else {
-				if (cb->chain_user_add &&
+				if (!chain_exists &&
+				    cb->chain_user_add &&
 				    cb->chain_user_add(h, chain,
 						       curtable->name) < 0) {
 					if (errno == EEXIST)