diff mbox series

[iptables,10/14] nft: Fetch rule cache only if needed

Message ID 20190916165000.18217-11-phil@nwl.cc
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series Improve iptables-nft performance with large rulesets | expand

Commit Message

Phil Sutter Sept. 16, 2019, 4:49 p.m. UTC
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@nwl.cc>
---
 iptables/nft.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
 iptables/nft.h |  2 ++
 2 files changed, 45 insertions(+), 3 deletions(-)
diff mbox series

Patch

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);
 
 /*