From f806ee67b5178342d18c8cd3e9201190d8a82c41 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 19 Mar 2020 18:58:29 +0100 Subject: nft: cache: Fetch cache for specific chains Iterate over command list and collect chains to cache. Insert them into a sorted list to pass to __nft_build_cache(). If a command is interested in all chains (e.g., --list), cmd->chain remains unset. To record this case reliably, use a boolean ('all_chains'). Otherwise, it is hard to distinguish between first call to nft_cache_level_set() and previous command with NULL cmd->chain value. When caching only specific chains, manually add builtin ones for the given table as well - otherwise nft_xt_builtin_init() will act as if they don't exist and possibly override non-default chain policies. Signed-off-by: Phil Sutter --- iptables/nft-cache.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++------ iptables/nft.c | 1 + iptables/nft.h | 7 +++++ 3 files changed, 81 insertions(+), 8 deletions(-) (limited to 'iptables') diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c index a0c4dfc6..84ea97d3 100644 --- a/iptables/nft-cache.c +++ b/iptables/nft-cache.c @@ -25,6 +25,24 @@ #include "nft.h" #include "nft-cache.h" +static void cache_chain_list_insert(struct list_head *list, const char *name) +{ + struct cache_chain *pos = NULL, *new; + + list_for_each_entry(pos, list, head) { + int cmp = strcmp(pos->name, name); + + if (!cmp) + return; + if (cmp > 0) + break; + } + + new = xtables_malloc(sizeof(*new)); + new->name = strdup(name); + list_add_tail(&new->head, pos ? &pos->head : list); +} + void nft_cache_level_set(struct nft_handle *h, int level, const struct nft_cmd *cmd) { @@ -33,13 +51,24 @@ void nft_cache_level_set(struct nft_handle *h, int level, if (level > req->level) req->level = level; - if (!cmd) + if (!cmd || !cmd->table || req->all_chains) return; if (!req->table) req->table = strdup(cmd->table); else assert(!strcmp(req->table, cmd->table)); + + if (!cmd->chain) { + req->all_chains = true; + return; + } + + cache_chain_list_insert(&req->chain_list, cmd->chain); + if (cmd->rename) + cache_chain_list_insert(&req->chain_list, cmd->rename); + if (cmd->jumpto) + cache_chain_list_insert(&req->chain_list, cmd->jumpto); } static int genid_cb(const struct nlmsghdr *nlh, void *data) @@ -348,12 +377,13 @@ static int __fetch_chain_cache(struct nft_handle *h, static int fetch_chain_cache(struct nft_handle *h, const struct builtin_table *t, - const char *chain) + struct list_head *chains) { + struct cache_chain *cc; struct nftnl_chain *c; - int ret; + int rc, ret = 0; - if (!chain) + if (!chains) return __fetch_chain_cache(h, t, NULL); assert(t); @@ -363,8 +393,13 @@ static int fetch_chain_cache(struct nft_handle *h, return -1; nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, t->name); - nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain); - ret = __fetch_chain_cache(h, t, c); + + list_for_each_entry(cc, chains, head) { + nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, cc->name); + rc = __fetch_chain_cache(h, t, c); + if (rc) + ret = rc; + } nftnl_chain_free(c); return ret; @@ -454,12 +489,16 @@ __nft_build_cache(struct nft_handle *h) { struct nft_cache_req *req = &h->cache_req; const struct builtin_table *t = NULL; + struct list_head *chains = NULL; if (h->cache_init) return; - if (req->table) + if (req->table) { t = nft_table_builtin_find(h, req->table); + if (!req->all_chains) + chains = &req->chain_list; + } h->cache_init = true; mnl_genid_get(h, &h->nft_genid); @@ -469,7 +508,7 @@ __nft_build_cache(struct nft_handle *h) if (req->level == NFT_CL_FAKE) return; if (req->level >= NFT_CL_CHAINS) - fetch_chain_cache(h, t, NULL); + fetch_chain_cache(h, t, chains); if (req->level >= NFT_CL_SETS) fetch_set_cache(h, t, NULL); if (req->level >= NFT_CL_RULES) @@ -592,12 +631,32 @@ void nft_rebuild_cache(struct nft_handle *h) void nft_cache_build(struct nft_handle *h) { + struct nft_cache_req *req = &h->cache_req; + const struct builtin_table *t = NULL; + int i; + + if (req->table) + t = nft_table_builtin_find(h, req->table); + + /* fetch builtin chains as well (if existing) so nft_xt_builtin_init() + * doesn't override policies by accident */ + if (t && !req->all_chains) { + for (i = 0; i < NF_INET_NUMHOOKS; i++) { + const char *cname = t->chains[i].name; + + if (!cname) + break; + cache_chain_list_insert(&req->chain_list, cname); + } + } + __nft_build_cache(h); } void nft_release_cache(struct nft_handle *h) { struct nft_cache_req *req = &h->cache_req; + struct cache_chain *cc, *cc_tmp; while (h->cache_index) flush_cache(h, &h->__cache[h->cache_index--], NULL); @@ -611,6 +670,12 @@ void nft_release_cache(struct nft_handle *h) free(req->table); req->table = NULL; } + req->all_chains = false; + list_for_each_entry_safe(cc, cc_tmp, &req->chain_list, head) { + list_del(&cc->head); + free(cc->name); + free(cc); + } } struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h) diff --git a/iptables/nft.c b/iptables/nft.c index daf08604..b807de88 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -805,6 +805,7 @@ int nft_init(struct nft_handle *h, int family, const struct builtin_table *t) INIT_LIST_HEAD(&h->obj_list); INIT_LIST_HEAD(&h->err_list); INIT_LIST_HEAD(&h->cmd_list); + INIT_LIST_HEAD(&h->cache_req.chain_list); return 0; } diff --git a/iptables/nft.h b/iptables/nft.h index 4eaaa77f..aeacc608 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -71,9 +71,16 @@ enum obj_update_type { NFT_COMPAT_TABLE_NEW, }; +struct cache_chain { + struct list_head head; + char *name; +}; + struct nft_cache_req { enum nft_cache_level level; char *table; + bool all_chains; + struct list_head chain_list; }; struct nft_handle { -- cgit v1.2.3