]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
xtables-restore: Fix flushing referenced custom chains
authorPhil Sutter <phil@nwl.cc>
Thu, 6 Sep 2018 17:33:20 +0000 (19:33 +0200)
committerFlorian Westphal <fw@strlen.de>
Mon, 10 Sep 2018 13:20:13 +0000 (15:20 +0200)
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>
Signed-off-by: Florian Westphal <fw@strlen.de>
iptables/nft.c
iptables/xtables-restore.c

index 7123060b34ef531dcdd7825aec149f6e9390a5f7..77ad38bea5211f92a7ae86872b42cf0417a9fa19 100644 (file)
@@ -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;
 }
index d2b7920869344c0651c837b2bd07e6b62ee275a5..d187b129f35da921a3d4de0f037605ee85dd5fa4 100644 (file)
@@ -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)