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 <phil@xxxxxx> --- 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