]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
nft: cache: Fetch cache for specific chains
authorPhil Sutter <phil@nwl.cc>
Thu, 19 Mar 2020 17:58:29 +0000 (18:58 +0100)
committerPhil Sutter <phil@nwl.cc>
Mon, 11 May 2020 12:28:28 +0000 (14:28 +0200)
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 <phil@nwl.cc>
iptables/nft-cache.c
iptables/nft.c
iptables/nft.h

index a0c4dfc6722862bfcfab553b5f5486bb347d6d61..84ea97d3e54a62d4b4da64a91d936406a8ce9d6f 100644 (file)
 #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)
index daf0860401ba5dfa2df8bca801fa46a12c7abb34..b807de88206b7283db9f60e429fbfbe308eabffb 100644 (file)
@@ -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;
 }
index 4eaaa77f962f4ebbd029ec46fe1066726335f284..aeacc608fcb9f4fd50947ab07ae0076c9bd17610 100644 (file)
@@ -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 {