]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
nft: calculate cache requirements from list of commands
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 6 Jan 2020 12:20:14 +0000 (13:20 +0100)
committerPhil Sutter <phil@nwl.cc>
Mon, 11 May 2020 12:28:28 +0000 (14:28 +0200)
This patch uses the new list of commands to calculate the cache
requirements, the rationale after this updates is the following:

 #1 Parsing, that builds the list of commands and it also calculates
    cache level requirements.
 #2 Cache building.
 #3 Translate commands to jobs
 #4 Translate jobs to netlink

This patch removes the pre-parsing code in xtables-restore.c to
calculate the cache.

After this patch, cache is calculated only once, there is no need
to cancel and refetch for an in-transit transaction.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <phil@nwl.cc>
iptables/nft-cache.c
iptables/nft-cache.h
iptables/nft-cmd.c
iptables/nft-cmd.h
iptables/nft.c
iptables/nft.h
iptables/xtables-restore.c
iptables/xtables-save.c

index 51b371c51c3f48f1f35d6e8fb585987bd0d619e1..38e353bd7231f5ad6cf2c336d5ee46b8eaa914fc 100644 (file)
 #include "nft.h"
 #include "nft-cache.h"
 
+void nft_cache_level_set(struct nft_handle *h, int level)
+{
+       if (level <= h->cache_level)
+               return;
+
+       h->cache_level = level;
+}
+
 static int genid_cb(const struct nlmsghdr *nlh, void *data)
 {
        uint32_t *genid = data;
@@ -436,42 +444,20 @@ __nft_build_cache(struct nft_handle *h, enum nft_cache_level level,
                  const struct builtin_table *t, const char *set,
                  const char *chain)
 {
-       if (level <= h->cache_level)
+       if (h->cache_init)
                return;
 
-       if (!h->nft_genid)
-               mnl_genid_get(h, &h->nft_genid);
+       h->cache_init = true;
+       mnl_genid_get(h, &h->nft_genid);
 
-       switch (h->cache_level) {
-       case NFT_CL_NONE:
+       if (h->cache_level >= NFT_CL_TABLES)
                fetch_table_cache(h);
-               if (level == NFT_CL_TABLES)
-                       break;
-               /* fall through */
-       case NFT_CL_TABLES:
+       if (h->cache_level >= NFT_CL_CHAINS)
                fetch_chain_cache(h, t, chain);
-               if (level == NFT_CL_CHAINS)
-                       break;
-               /* fall through */
-       case NFT_CL_CHAINS:
+       if (h->cache_level >= NFT_CL_SETS)
                fetch_set_cache(h, t, set);
-               if (level == NFT_CL_SETS)
-                       break;
-               /* fall through */
-       case NFT_CL_SETS:
+       if (h->cache_level >= NFT_CL_RULES)
                fetch_rule_cache(h, t, chain);
-               if (level == NFT_CL_RULES)
-                       break;
-               /* fall through */
-       case NFT_CL_RULES:
-       case NFT_CL_FAKE:
-               break;
-       }
-
-       if (!t && !chain)
-               h->cache_level = level;
-       else if (h->cache_level < NFT_CL_TABLES)
-               h->cache_level = NFT_CL_TABLES;
 }
 
 void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c)
@@ -493,6 +479,7 @@ void nft_fake_cache(struct nft_handle *h)
        fetch_table_cache(h);
 
        h->cache_level = NFT_CL_FAKE;
+       h->cache_init = true;
        mnl_genid_get(h, &h->nft_genid);
 }
 
@@ -593,26 +580,29 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
 
 void flush_chain_cache(struct nft_handle *h, const char *tablename)
 {
-       if (!h->cache_level)
+       if (!h->cache_init)
                return;
 
        if (flush_cache(h, h->cache, tablename))
-               h->cache_level = NFT_CL_NONE;
+               h->cache_init = false;
 }
 
 void nft_rebuild_cache(struct nft_handle *h)
 {
-       enum nft_cache_level level = h->cache_level;
-
-       if (h->cache_level)
+       if (h->cache_init) {
                __nft_flush_cache(h);
+               h->cache_init = false;
+       }
 
-       if (h->cache_level == NFT_CL_FAKE) {
+       if (h->cache_level == NFT_CL_FAKE)
                nft_fake_cache(h);
-       } else {
-               h->cache_level = NFT_CL_NONE;
-               __nft_build_cache(h, level, NULL, NULL, NULL);
-       }
+       else
+               __nft_build_cache(h, h->cache_level, NULL, NULL, NULL);
+}
+
+void nft_cache_build(struct nft_handle *h)
+{
+       __nft_build_cache(h, h->cache_level, NULL, NULL, NULL);
 }
 
 void nft_release_cache(struct nft_handle *h)
index ed498835676e2f88d187e6a5b43c7ec5d8078a87..cf28808e22c72eb775ee59f6ed749c4d62411218 100644 (file)
@@ -3,6 +3,7 @@
 
 struct nft_handle;
 
+void nft_cache_level_set(struct nft_handle *h, int level);
 void nft_fake_cache(struct nft_handle *h);
 void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c);
 void nft_rebuild_cache(struct nft_handle *h);
@@ -10,6 +11,7 @@ void nft_release_cache(struct nft_handle *h);
 void flush_chain_cache(struct nft_handle *h, const char *tablename);
 int flush_rule_cache(struct nft_handle *h, const char *table,
                     struct nftnl_chain *c);
+void nft_cache_build(struct nft_handle *h);
 
 struct nftnl_chain_list *
 nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain);
index 96887c0127e2415e31e842054732678d7d6cea4f..8bf361a6ba33541dfff86cf5c3d38777e038d332 100644 (file)
@@ -71,12 +71,33 @@ void nft_cmd_free(struct nft_cmd *cmd)
        free(cmd);
 }
 
+static void nft_cmd_rule_bridge(struct nft_handle *h, const char *chain,
+                               const char *table)
+{
+       const struct builtin_table *t;
+
+       t = nft_table_builtin_find(h, table);
+       if (!t)
+               return;
+
+       /* 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 &&
+           !nft_chain_builtin_find(t, chain))
+               nft_cache_level_set(h, NFT_CL_RULES);
+       else
+               nft_cache_level_set(h, NFT_CL_CHAINS);
+}
+
 int nft_cmd_rule_append(struct nft_handle *h, const char *chain,
                        const char *table, struct iptables_command_state *state,
                        void *ref, bool verbose)
 {
        struct nft_cmd *cmd;
 
+       nft_cmd_rule_bridge(h, chain, table);
+
        cmd = nft_cmd_new(h, NFT_COMPAT_RULE_APPEND, table, chain, state, -1,
                          verbose);
        if (!cmd)
@@ -91,11 +112,18 @@ int nft_cmd_rule_insert(struct nft_handle *h, const char *chain,
 {
        struct nft_cmd *cmd;
 
+       nft_cmd_rule_bridge(h, chain, table);
+
        cmd = nft_cmd_new(h, NFT_COMPAT_RULE_INSERT, table, chain, state,
                          rulenum, verbose);
        if (!cmd)
                return 0;
 
+       if (cmd->rulenum > 0)
+               nft_cache_level_set(h, NFT_CL_RULES);
+       else
+               nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -110,6 +138,8 @@ int nft_cmd_rule_delete(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
@@ -123,6 +153,8 @@ int nft_cmd_rule_delete_num(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
@@ -136,6 +168,8 @@ int nft_cmd_rule_flush(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -149,6 +183,8 @@ int nft_cmd_chain_zero_counters(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -162,6 +198,8 @@ int nft_cmd_chain_user_add(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -175,6 +213,14 @@ int nft_cmd_chain_user_del(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       /* This triggers nft_bridge_chain_postprocess() when fetching the
+        * rule cache.
+        */
+       if (h->family == NFPROTO_BRIDGE)
+               nft_cache_level_set(h, NFT_CL_RULES);
+       else
+               nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -190,6 +236,8 @@ int nft_cmd_chain_user_rename(struct nft_handle *h,const char *chain,
 
        cmd->rename = strdup(newname);
 
+       nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -205,6 +253,8 @@ int nft_cmd_rule_list(struct nft_handle *h, const char *chain,
 
        cmd->format = format;
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
@@ -219,6 +269,8 @@ int nft_cmd_rule_replace(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
@@ -232,6 +284,8 @@ int nft_cmd_rule_check(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
@@ -250,6 +304,8 @@ int nft_cmd_chain_set(struct nft_handle *h, const char *table,
        if (counters)
                cmd->counters = *counters;
 
+       nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -262,6 +318,8 @@ int nft_cmd_table_flush(struct nft_handle *h, const char *table)
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_TABLES);
+
        return 1;
 }
 
@@ -275,6 +333,8 @@ int nft_cmd_chain_restore(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_CHAINS);
+
        return 1;
 }
 
@@ -288,6 +348,8 @@ int nft_cmd_rule_zero_counters(struct nft_handle *h, const char *chain,
        if (!cmd)
                return 0;
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
@@ -303,6 +365,8 @@ int nft_cmd_rule_list_save(struct nft_handle *h, const char *chain,
 
        cmd->counters_save = counters;
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
@@ -318,6 +382,8 @@ int ebt_cmd_user_chain_policy(struct nft_handle *h, const char *table,
 
        cmd->policy = strdup(policy);
 
+       nft_cache_level_set(h, NFT_CL_RULES);
+
        return 1;
 }
 
index 33ee766ae823f9737f3dde264a0bb9dc7e6768f9..0e1776ce088bfdd238a1636775ee22d1783fdfff 100644 (file)
@@ -18,6 +18,7 @@ struct nft_cmd {
        unsigned int                    format;
        struct {
                struct nftnl_rule       *rule;
+               struct nftnl_set        *set;
        } obj;
        const char                      *policy;
        struct xt_counters              counters;
index bbbf7c6166ac6b5d6f011e9edbcecfb04e0778be..f069396a0519095b7e568c58883149c77ef762e4 100644 (file)
@@ -954,6 +954,7 @@ static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
 {
        static uint32_t set_id = 0;
        struct nftnl_set *s;
+       struct nft_cmd *cmd;
 
        s = nftnl_set_alloc();
        if (!s)
@@ -969,7 +970,14 @@ static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
        nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, key_len);
        nftnl_set_set_u32(s, NFTNL_SET_DESC_SIZE, size);
 
-       return batch_set_add(h, NFT_COMPAT_SET_ADD, s) ? s : NULL;
+       cmd = nft_cmd_new(h, NFT_COMPAT_SET_ADD, table, NULL, NULL, -1, false);
+       if (!cmd) {
+               nftnl_set_free(s);
+               return NULL;
+       }
+       cmd->obj.set = s;
+
+       return s;
 }
 
 static struct nftnl_expr *
@@ -2996,6 +3004,8 @@ static int nft_prepare(struct nft_handle *h)
        struct nft_cmd *cmd, *next;
        int ret = 1;
 
+       nft_cache_build(h);
+
        list_for_each_entry_safe(cmd, next, &h->cmd_list, head) {
                switch (cmd->command) {
                case NFT_COMPAT_TABLE_FLUSH:
@@ -3081,9 +3091,12 @@ static int nft_prepare(struct nft_handle *h)
                        nft_xt_builtin_init(h, cmd->table);
                        ret = 1;
                        break;
+               case NFT_COMPAT_SET_ADD:
+                       batch_set_add(h, NFT_COMPAT_SET_ADD, cmd->obj.set);
+                       ret = 1;
+                       break;
                case NFT_COMPAT_TABLE_ADD:
                case NFT_COMPAT_CHAIN_ADD:
-               case NFT_COMPAT_SET_ADD:
                        assert(0);
                        break;
                }
index 7ddc3a8bbb0426081faf4a51ed13ac4b67e6efd3..d61a40979d5bcefcd63af4299be3e4dd64430aae 100644 (file)
@@ -30,7 +30,6 @@ struct builtin_table {
 };
 
 enum nft_cache_level {
-       NFT_CL_NONE,
        NFT_CL_TABLES,
        NFT_CL_CHAINS,
        NFT_CL_SETS,
@@ -95,6 +94,7 @@ struct nft_handle {
        bool                    noflush;
        int8_t                  config_done;
        struct list_head        cmd_list;
+       bool                    cache_init;
 
        /* meta data, for error reporting */
        struct {
index 55547e3af9d2e2f40bba39520da6968ef7c51d20..0b0febdf6ef018c45b7dd23314d3be315133707e 100644 (file)
@@ -254,99 +254,16 @@ static void xtables_restore_parse_line(struct nft_handle *h,
        }
 }
 
-/* Return true if given iptables-restore line will require a full cache.
- * Typically these are commands referring to an existing rule
- * (either by number or content) or commands listing the ruleset. */
-static bool cmd_needs_full_cache(char *cmd)
-{
-       char c, chain[32];
-       int rulenum, mcount;
-
-       mcount = sscanf(cmd, "-%c %31s %d", &c, chain, &rulenum);
-
-       if (mcount == 3)
-               return true;
-       if (mcount < 1)
-               return false;
-
-       switch (c) {
-       case 'D':
-       case 'C':
-       case 'S':
-       case 'L':
-       case 'Z':
-               return true;
-       }
-
-       return false;
-}
-
-#define PREBUFSIZ      65536
-
 void xtables_restore_parse(struct nft_handle *h,
                           const struct nft_xt_restore_parse *p)
 {
        struct nft_xt_restore_state state = {};
-       char preload_buffer[PREBUFSIZ] = {}, buffer[10240] = {}, *ptr;
+       char buffer[10240] = {};
 
-       if (!h->noflush) {
+       if (!h->noflush)
                nft_fake_cache(h);
-       } else {
-               ssize_t pblen = sizeof(preload_buffer);
-               bool do_cache = false;
-
-               ptr = preload_buffer;
-               while (fgets(buffer, sizeof(buffer), p->in)) {
-                       size_t blen = strlen(buffer);
-
-                       /* Drop trailing newline; xtables_restore_parse_line()
-                        * uses strtok() which replaces them by nul-characters,
-                        * causing unpredictable string delimiting in
-                        * preload_buffer.
-                        * Unless this is an empty line which would fold into a
-                        * spurious EoB indicator (double nul-char). */
-                       if (buffer[blen - 1] == '\n' && blen > 1)
-                               buffer[blen - 1] = '\0';
-                       else
-                               blen++;
-
-                       pblen -= blen;
-                       if (pblen <= 0) {
-                               /* buffer exhausted */
-                               do_cache = true;
-                               break;
-                       }
-
-                       if (cmd_needs_full_cache(buffer)) {
-                               do_cache = true;
-                               break;
-                       }
-
-                       /* copy string including terminating nul-char */
-                       memcpy(ptr, buffer, blen);
-                       ptr += blen;
-                       buffer[0] = '\0';
-               }
-
-               if (do_cache)
-                       nft_build_cache(h, NULL);
-       }
 
        line = 0;
-       ptr = preload_buffer;
-       while (*ptr) {
-               size_t len = strlen(ptr);
-
-               h->error.lineno = ++line;
-               DEBUGP("%s: buffered line %d: '%s'\n", __func__, line, ptr);
-               xtables_restore_parse_line(h, p, &state, ptr);
-               ptr += len + 1;
-       }
-       if (*buffer) {
-               h->error.lineno = ++line;
-               DEBUGP("%s: overrun line %d: '%s'\n", __func__, line, buffer);
-               xtables_restore_parse_line(h, p, &state, buffer);
-       }
        while (fgets(buffer, sizeof(buffer), p->in)) {
                h->error.lineno = ++line;
                DEBUGP("%s: input line %d: '%s'\n", __func__, line, buffer);
index 28f7490275ce54247bbb73c7d82eef23f6bae1e5..f927aa6e9e4041eb141e57c5fc6176ff3753ac4a 100644 (file)
@@ -239,6 +239,9 @@ xtables_save_main(int family, int argc, char *argv[],
                exit(EXIT_FAILURE);
        }
 
+       nft_cache_level_set(&h, NFT_CL_RULES);
+       nft_cache_build(&h);
+
        ret = do_output(&h, tablename, &d);
        nft_fini(&h);
        if (dump)