summaryrefslogtreecommitdiffstats
path: root/iptables
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2020-01-06 13:20:14 +0100
committerPhil Sutter <phil@nwl.cc>2020-05-11 14:28:28 +0200
commit9d07514ac5c7a27ec72df5a81bf067073d63bd99 (patch)
tree129a8f08d7c132296c7d8131c540074995551bac /iptables
parenta7f1e208cdf9c6392c99d3c52764701d004bdde7 (diff)
nft: calculate cache requirements from list of commands
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>
Diffstat (limited to 'iptables')
-rw-r--r--iptables/nft-cache.c68
-rw-r--r--iptables/nft-cache.h2
-rw-r--r--iptables/nft-cmd.c66
-rw-r--r--iptables/nft-cmd.h1
-rw-r--r--iptables/nft.c17
-rw-r--r--iptables/nft.h2
-rw-r--r--iptables/xtables-restore.c87
-rw-r--r--iptables/xtables-save.c3
8 files changed, 119 insertions, 127 deletions
diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c
index 51b371c5..38e353bd 100644
--- a/iptables/nft-cache.c
+++ b/iptables/nft-cache.c
@@ -24,6 +24,14 @@
#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)
diff --git a/iptables/nft-cache.h b/iptables/nft-cache.h
index ed498835..cf28808e 100644
--- a/iptables/nft-cache.h
+++ b/iptables/nft-cache.h
@@ -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);
diff --git a/iptables/nft-cmd.c b/iptables/nft-cmd.c
index 96887c01..8bf361a6 100644
--- a/iptables/nft-cmd.c
+++ b/iptables/nft-cmd.c
@@ -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;
}
diff --git a/iptables/nft-cmd.h b/iptables/nft-cmd.h
index 33ee766a..0e1776ce 100644
--- a/iptables/nft-cmd.h
+++ b/iptables/nft-cmd.h
@@ -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;
diff --git a/iptables/nft.c b/iptables/nft.c
index bbbf7c61..f069396a 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -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;
}
diff --git a/iptables/nft.h b/iptables/nft.h
index 7ddc3a8b..d61a4097 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -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 {
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index 55547e3a..0b0febdf 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -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);
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
index 28f74902..f927aa6e 100644
--- a/iptables/xtables-save.c
+++ b/iptables/xtables-save.c
@@ -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)