Introduce boolean have_rule_cache to indicate whether rules have been
fetched or not. Introduce nft_build_rule_cache() to trigger explicit
rule cache population.

In a ruleset with many rules, this largely improves performance of
commands which don't need to access the rules themselves. E.g.,
appending a rule to a large chain is now two magnitudes faster than
before and even one magnitude faster than legacy iptables.

Signed-off-by: Phil Sutter <p...@nwl.cc>
---
 iptables/nft.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
 iptables/nft.h |  2 ++
 2 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/iptables/nft.c b/iptables/nft.c
index 1483664510518..82c892ad96f34 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -894,8 +894,10 @@ static void flush_chain_cache(struct nft_handle *h, const 
char *tablename)
        if (!h->have_chain_cache)
                return;
 
-       if (flush_cache(h->cache, h->tables, tablename))
+       if (flush_cache(h->cache, h->tables, tablename)) {
                h->have_chain_cache = false;
+               h->have_rule_cache = false;
+       }
 }
 
 void nft_fini(struct nft_handle *h)
@@ -1276,6 +1278,14 @@ nft_rule_append(struct nft_handle *h, const char *chain, 
const char *table,
 
        nft_xt_builtin_init(h, table);
 
+       /* Since ebtables user-defined chain policies are implemented as last
+        * rule in nftables, rule cache is required here to treat them right. */
+       if (h->family == NFPROTO_BRIDGE) {
+               c = nft_chain_find(h, table, chain);
+               if (c && !nft_chain_builtin(c))
+                       nft_build_rule_cache(h);
+       }
+
        nft_fn = nft_rule_append;
 
        r = nft_rule_new(h, chain, table, data);
@@ -1602,6 +1612,7 @@ retry:
        fetch_chain_cache(h);
        fetch_rule_cache(h);
        h->have_chain_cache = true;
+       h->have_rule_cache = true;
        mnl_genid_get(h, &genid_stop);
 
        if (genid_start != genid_stop) {
@@ -1618,11 +1629,19 @@ void nft_build_cache(struct nft_handle *h)
                __nft_build_cache(h);
 }
 
+void nft_build_rule_cache(struct nft_handle *h)
+{
+       if (!h->have_rule_cache) {
+               fetch_rule_cache(h);
+               h->have_rule_cache = true;
+       }
+}
+
 void nft_fake_cache(struct nft_handle *h)
 {
        int i;
 
-       if (h->have_chain_cache)
+       if (h->have_chain_cache && h->have_rule_cache)
                return;
 
        /* fetch tables so conditional table delete logic works */
@@ -1636,10 +1655,14 @@ void nft_fake_cache(struct nft_handle *h)
                    h->cache->table[type].chains)
                        continue;
 
+               if (h->cache->table[type].chains)
+                       continue;
+
                h->cache->table[type].chains = nftnl_chain_list_alloc();
        }
        mnl_genid_get(h, &h->nft_genid);
        h->have_chain_cache = true;
+       h->have_rule_cache = true;
 }
 
 static void __nft_flush_cache(struct nft_handle *h)
@@ -1675,7 +1698,10 @@ struct nftnl_chain_list *nft_chain_list_get(struct 
nft_handle *h,
        if (!t)
                return NULL;
 
-       nft_build_cache(h);
+       if (!h->have_chain_cache) {
+               fetch_chain_cache(h);
+               h->have_chain_cache = true;
+       }
 
        return h->cache->table[t->type].chains;
 }
@@ -1760,6 +1786,8 @@ int nft_rule_save(struct nft_handle *h, const char 
*table, unsigned int format)
        if (!list)
                return 0;
 
+       nft_build_rule_cache(h);
+
        iter = nftnl_chain_list_iter_create(list);
        if (!iter)
                return 0;
@@ -1981,6 +2009,10 @@ int nft_chain_user_del(struct nft_handle *h, const char 
*chain,
        if (list == NULL)
                return 0;
 
+       /* This triggers required policy rule deletion. */
+       if (h->family == NFPROTO_BRIDGE)
+               nft_build_rule_cache(h);
+
        if (chain) {
                c = nftnl_chain_list_lookup_byname(list, chain);
                if (!c) {
@@ -2242,6 +2274,8 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain 
*c, void *data, int rulen
        struct nftnl_rule_iter *iter;
        bool found = false;
 
+       nft_build_rule_cache(h);
+
        if (rulenum >= 0)
                /* Delete by rule number case */
                return nftnl_rule_lookup_byindex(c, rulenum);
@@ -3062,6 +3096,8 @@ int ebt_set_user_chain_policy(struct nft_handle *h, const 
char *table,
        if (!c)
                return 0;
 
+       nft_build_rule_cache(h);
+
        if (!strcmp(policy, "DROP"))
                pval = NF_DROP;
        else if (!strcmp(policy, "ACCEPT"))
@@ -3339,6 +3375,8 @@ int nft_chain_zero_counters(struct nft_handle *h, const 
char *chain,
        if (list == NULL)
                goto err;
 
+       nft_build_rule_cache(h);
+
        if (chain) {
                c = nftnl_chain_list_lookup_byname(list, chain);
                if (!c) {
@@ -3445,6 +3483,8 @@ bool nft_is_table_compatible(struct nft_handle *h, const 
char *tablename)
        if (clist == NULL)
                return false;
 
+       nft_build_rule_cache(h);
+
        if (nftnl_chain_list_foreach(clist, nft_is_chain_compatible, h))
                return false;
 
diff --git a/iptables/nft.h b/iptables/nft.h
index 11b2d4e3be6ff..718acdbf0c55d 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -54,6 +54,7 @@ struct nft_handle {
        struct nft_cache        __cache[2];
        struct nft_cache        *cache;
        bool                    have_chain_cache;
+       bool                    have_rule_cache;
        bool                    restore;
        bool                    noflush;
        int8_t                  config_done;
@@ -74,6 +75,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_build_rule_cache(struct nft_handle *h);
 void nft_fake_cache(struct nft_handle *h);
 
 /*
-- 
2.23.0

Reply via email to