When called without --noflush, don't fetch full ruleset from kernel but
merely list of tables and the current genid. Locally, initialize chain
lists and set have_cache to simulate an empty ruleset.

Doing so reduces execution time significantly if a large ruleset exists
in kernel space. A simple test case consisting of a dump with 100,000
rules can be restored within 15s on my testing VM. Restoring it a second
time took 21s before this patch and only 17s after it.

Signed-off-by: Phil Sutter <p...@nwl.cc>
---
 iptables/nft.c             | 27 ++++++++++++++++++++++++++-
 iptables/nft.h             |  1 +
 iptables/xtables-restore.c |  7 +++++--
 3 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/iptables/nft.c b/iptables/nft.c
index 7f0f9e1234ae4..820f3392dd495 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -882,7 +882,8 @@ static int flush_cache(struct nft_cache *c, const struct 
builtin_table *tables,
                nftnl_chain_list_free(c->table[i].chains);
                c->table[i].chains = NULL;
        }
-       nftnl_table_list_free(c->tables);
+       if (c->tables)
+               nftnl_table_list_free(c->tables);
        c->tables = NULL;
 
        return 1;
@@ -1617,6 +1618,30 @@ void nft_build_cache(struct nft_handle *h)
                __nft_build_cache(h);
 }
 
+void nft_fake_cache(struct nft_handle *h)
+{
+       int i;
+
+       if (h->have_cache)
+               return;
+
+       /* fetch tables so conditional table delete logic works */
+       if (!h->cache->tables)
+               fetch_table_cache(h);
+
+       for (i = 0; i < NFT_TABLE_MAX; i++) {
+               enum nft_table_type type = h->tables[i].type;
+
+               if (!h->tables[i].name ||
+                   h->cache->table[type].chains)
+                       continue;
+
+               h->cache->table[type].chains = nftnl_chain_list_alloc();
+       }
+       mnl_genid_get(h, &h->nft_genid);
+       h->have_cache = true;
+}
+
 static void __nft_flush_cache(struct nft_handle *h)
 {
        if (!h->cache_index) {
diff --git a/iptables/nft.h b/iptables/nft.h
index 43463d7f262e8..d9eb30a2a2413 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -74,6 +74,7 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
 int nft_init(struct nft_handle *h, const struct builtin_table *t);
 void nft_fini(struct nft_handle *h);
 void nft_build_cache(struct nft_handle *h);
+void nft_fake_cache(struct nft_handle *h);
 
 /*
  * Operations with tables.
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index f930f5ba2d167..d1486afedc480 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -96,6 +96,11 @@ void xtables_restore_parse(struct nft_handle *h,
 
        line = 0;
 
+       if (h->noflush)
+               nft_build_cache(h);
+       else
+               nft_fake_cache(h);
+
        /* Grab standard input. */
        while (fgets(buffer, sizeof(buffer), p->in)) {
                int ret = 0;
@@ -146,8 +151,6 @@ void xtables_restore_parse(struct nft_handle *h,
                        if (p->tablename && (strcmp(p->tablename, table) != 0))
                                continue;
 
-                       nft_build_cache(h);
-
                        if (h->noflush == 0) {
                                DEBUGP("Cleaning all chains of table '%s'\n",
                                        table);
-- 
2.23.0

Reply via email to