diff mbox series

[nft] cache: filter out rules by chain

Message ID 20211110193003.497822-1-pablo@netfilter.org
State Accepted
Delegated to: Pablo Neira
Headers show
Series [nft] cache: filter out rules by chain | expand

Commit Message

Pablo Neira Ayuso Nov. 10, 2021, 7:30 p.m. UTC
With an autogenerated ruleset with ~20k chains.

 # time nft list ruleset &> /dev/null

 real    0m1,712s
 user    0m1,258s
 sys     0m0,454s

Speed up listing of a specific chain:

 # time nft list chain nat MWDG-UGR-234PNG3YBUOTS5QD &> /dev/null

 real    0m0,542s
 user    0m0,251s
 sys     0m0,292s

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/cache.h   |  1 +
 include/netlink.h |  2 --
 src/cache.c       | 84 +++++++++++++++++++++++++++++++++++++++++++++--
 src/netlink.c     | 42 ------------------------
 4 files changed, 82 insertions(+), 47 deletions(-)
diff mbox series

Patch

diff --git a/include/cache.h b/include/cache.h
index 120a1b8d91b5..e5c509e8510c 100644
--- a/include/cache.h
+++ b/include/cache.h
@@ -51,6 +51,7 @@  struct nft_cache_filter {
 	struct {
 		uint32_t	family;
 		const char	*table;
+		const char	*chain;
 		const char	*set;
 	} list;
 
diff --git a/include/netlink.h b/include/netlink.h
index c1d7d3189455..a692edcdb5bf 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -124,8 +124,6 @@  extern struct expr *netlink_alloc_data(const struct location *loc,
 				       const struct nft_data_delinearize *nld,
 				       enum nft_registers dreg);
 
-extern int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h);
-
 struct netlink_linearize_ctx;
 extern void netlink_linearize_rule(struct netlink_ctx *ctx,
 				   const struct rule *rule,
diff --git a/src/cache.c b/src/cache.c
index e82e0b8d1215..fe31e3f02163 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -200,6 +200,14 @@  static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
 		}
 		flags |= NFT_CACHE_FULL;
 		break;
+	case CMD_OBJ_CHAIN:
+		if (filter && cmd->handle.chain.name) {
+			filter->list.family = cmd->handle.family;
+			filter->list.table = cmd->handle.table.name;
+			filter->list.chain = cmd->handle.chain.name;
+		}
+		flags |= NFT_CACHE_FULL;
+		break;
 	case CMD_OBJ_SET:
 	case CMD_OBJ_MAP:
 		if (filter && cmd->handle.table.name && cmd->handle.set.name) {
@@ -334,11 +342,13 @@  struct table *table_cache_find(const struct cache *cache,
 struct chain_cache_dump_ctx {
 	struct netlink_ctx	*nlctx;
 	struct table		*table;
+	const struct nft_cache_filter *filter;
 };
 
 static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
 {
 	struct chain_cache_dump_ctx *ctx = arg;
+	const struct nft_cache_filter *filter = ctx->filter;
 	const char *chain_name, *table_name;
 	uint32_t hash, family;
 	struct chain *chain;
@@ -351,6 +361,12 @@  static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
 	    family != ctx->table->handle.family)
 		return 0;
 
+	if (filter && filter->list.table && filter->list.chain &&
+	    (filter->list.family != family ||
+	     strcmp(filter->list.table, table_name) ||
+	     strcmp(filter->list.chain, chain_name)))
+		return 0;
+
 	hash = djb_hash(chain_name) % NFT_CACHE_HSIZE;
 	chain = netlink_delinearize_chain(ctx->nlctx, nlc);
 
@@ -367,11 +383,13 @@  static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
 }
 
 static int chain_cache_init(struct netlink_ctx *ctx, struct table *table,
-			    struct nftnl_chain_list *chain_list)
+			    struct nftnl_chain_list *chain_list,
+			    const struct nft_cache_filter *filter)
 {
 	struct chain_cache_dump_ctx dump_ctx = {
 		.nlctx	= ctx,
 		.table	= table,
+		.filter	= filter,
 	};
 	nftnl_chain_list_foreach(chain_list, chain_cache_cb, &dump_ctx);
 
@@ -423,6 +441,66 @@  struct chain *chain_cache_find(const struct table *table, const char *name)
 	return NULL;
 }
 
+struct rule_cache_dump_ctx {
+	struct netlink_ctx	*nlctx;
+	const struct nft_cache_filter *filter;
+};
+
+static int list_rule_cb(struct nftnl_rule *nlr, void *data)
+{
+	struct rule_cache_dump_ctx *rule_ctx = data;
+	const struct nft_cache_filter *filter = rule_ctx->filter;
+	struct netlink_ctx *ctx = rule_ctx->nlctx;
+	const struct handle *h = ctx->data;
+	const char *table, *chain;
+	struct rule *rule;
+	uint32_t family;
+
+	family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
+	table  = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
+	chain  = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
+
+	if (h->family != family ||
+	    strcmp(table, h->table.name) != 0 ||
+	    (h->chain.name && strcmp(chain, h->chain.name) != 0))
+		return 0;
+
+	if (filter && filter->list.table && filter->list.chain &&
+	    (filter->list.family != family ||
+	     strcmp(filter->list.table, table) ||
+	     strcmp(filter->list.chain, chain)))
+		return 0;
+
+	netlink_dump_rule(nlr, ctx);
+	rule = netlink_delinearize_rule(ctx, nlr);
+	list_add_tail(&rule->list, &ctx->list);
+
+	return 0;
+}
+
+static int rule_cache_init(struct netlink_ctx *ctx, const struct handle *h,
+			   const struct nft_cache_filter *filter)
+{
+	struct rule_cache_dump_ctx rule_ctx = {
+		.nlctx = ctx,
+		.filter = filter,
+	};
+	struct nftnl_rule_list *rule_cache;
+
+	rule_cache = mnl_nft_rule_dump(ctx, h->family);
+	if (rule_cache == NULL) {
+		if (errno == EINTR)
+			return -1;
+
+		return 0;
+	}
+
+	ctx->data = h;
+	nftnl_rule_list_foreach(rule_cache, list_rule_cb, &rule_ctx);
+	nftnl_rule_list_free(rule_cache);
+	return 0;
+}
+
 struct set_cache_dump_ctx {
 	struct netlink_ctx	*nlctx;
 	struct table		*table;
@@ -777,7 +855,7 @@  static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
 			}
 		}
 		if (flags & NFT_CACHE_CHAIN_BIT) {
-			ret = chain_cache_init(ctx, table, chain_list);
+			ret = chain_cache_init(ctx, table, chain_list, filter);
 			if (ret < 0) {
 				ret = -1;
 				goto cache_fails;
@@ -815,7 +893,7 @@  static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
 		}
 
 		if (flags & NFT_CACHE_RULE_BIT) {
-			ret = netlink_list_rules(ctx, &table->handle);
+			ret = rule_cache_init(ctx, &table->handle, filter);
 			list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
 				chain = chain_cache_find(table, rule->handle.chain.name);
 				if (!chain)
diff --git a/src/netlink.c b/src/netlink.c
index f63f2bd1bd60..ab90d0c05aca 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -483,48 +483,6 @@  void netlink_dump_expr(const struct nftnl_expr *nle,
 	fprintf(fp, "\n");
 }
 
-static int list_rule_cb(struct nftnl_rule *nlr, void *arg)
-{
-	struct netlink_ctx *ctx = arg;
-	const struct handle *h = ctx->data;
-	struct rule *rule;
-	const char *table, *chain;
-	uint32_t family;
-
-	family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
-	table  = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
-	chain  = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
-
-	if (h->family != family ||
-	    strcmp(table, h->table.name) != 0 ||
-	    (h->chain.name && strcmp(chain, h->chain.name) != 0))
-		return 0;
-
-	netlink_dump_rule(nlr, ctx);
-	rule = netlink_delinearize_rule(ctx, nlr);
-	list_add_tail(&rule->list, &ctx->list);
-
-	return 0;
-}
-
-int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h)
-{
-	struct nftnl_rule_list *rule_cache;
-
-	rule_cache = mnl_nft_rule_dump(ctx, h->family);
-	if (rule_cache == NULL) {
-		if (errno == EINTR)
-			return -1;
-
-		return 0;
-	}
-
-	ctx->data = h;
-	nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx);
-	nftnl_rule_list_free(rule_cache);
-	return 0;
-}
-
 void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx)
 {
 	FILE *fp = ctx->nft->output.output_fp;