aboutsummaryrefslogtreecommitdiff
path: root/iptables/nft-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables/nft-cache.c')
-rw-r--r--iptables/nft-cache.c384
1 files changed, 208 insertions, 176 deletions
diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c
index 7345a27e..638b18bc 100644
--- a/iptables/nft-cache.c
+++ b/iptables/nft-cache.c
@@ -11,6 +11,7 @@
#include <assert.h>
#include <errno.h>
+#include <stdlib.h>
#include <string.h>
#include <xtables.h>
@@ -24,6 +25,52 @@
#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)
+{
+ struct nft_cache_req *req = &h->cache_req;
+
+ if (level > req->level)
+ req->level = level;
+
+ 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)
{
uint32_t *genid = data;
@@ -86,7 +133,7 @@ static int fetch_table_cache(struct nft_handle *h)
char buf[16536];
struct nlmsghdr *nlh;
struct nftnl_table_list *list;
- int ret;
+ int i, ret;
if (h->cache->tables)
return 0;
@@ -104,6 +151,21 @@ static int fetch_table_cache(struct nft_handle *h)
h->cache->tables = list;
+ for (i = 0; i < NFT_TABLE_MAX; i++) {
+ enum nft_table_type type = h->tables[i].type;
+
+ if (!h->tables[i].name)
+ continue;
+
+ h->cache->table[type].chains = nftnl_chain_list_alloc();
+ if (!h->cache->table[type].chains)
+ return 0;
+
+ h->cache->table[type].sets = nftnl_set_list_alloc();
+ if (!h->cache->table[type].sets)
+ return 0;
+ }
+
return 1;
}
@@ -239,40 +301,31 @@ static int fetch_set_cache(struct nft_handle *h,
.h = h,
.t = t,
};
+ uint16_t flags = NLM_F_DUMP;
+ struct nftnl_set *s = NULL;
struct nlmsghdr *nlh;
char buf[16536];
int i, ret;
- if (!t) {
- for (i = 0; i < NFT_TABLE_MAX; i++) {
- enum nft_table_type type = h->tables[i].type;
+ if (t) {
+ s = nftnl_set_alloc();
+ if (!s)
+ return -1;
- if (!h->tables[i].name)
- continue;
+ nftnl_set_set_str(s, NFTNL_SET_TABLE, t->name);
- h->cache->table[type].sets = nftnl_set_list_alloc();
- if (!h->cache->table[type].sets)
- return -1;
+ if (set) {
+ nftnl_set_set_str(s, NFTNL_SET_NAME, set);
+ flags = NLM_F_ACK;
}
- } else if (!h->cache->table[t->type].sets) {
- h->cache->table[t->type].sets = nftnl_set_list_alloc();
}
- if (t && set) {
- struct nftnl_set *s = nftnl_set_alloc();
-
- if (!s)
- return -1;
+ nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET,
+ h->family, flags, h->seq);
- nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, h->family,
- NLM_F_ACK, h->seq);
- nftnl_set_set_str(s, NFTNL_SET_TABLE, t->name);
- nftnl_set_set_str(s, NFTNL_SET_NAME, set);
+ if (s) {
nftnl_set_nlmsg_build_payload(nlh, s);
nftnl_set_free(s);
- } else {
- nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, h->family,
- NLM_F_DUMP, h->seq);
}
ret = mnl_talk(h, nlh, nftnl_set_list_cb, &d);
@@ -281,13 +334,7 @@ static int fetch_set_cache(struct nft_handle *h,
return ret;
}
- if (t && set) {
- struct nftnl_set *s;
-
- s = nftnl_set_list_lookup_byname(h->cache->table[t->type].sets,
- set);
- set_fetch_elem_cb(s, h);
- } else if (t) {
+ if (t) {
nftnl_set_list_foreach(h->cache->table[t->type].sets,
set_fetch_elem_cb, h);
} else {
@@ -304,9 +351,9 @@ static int fetch_set_cache(struct nft_handle *h,
return ret;
}
-static int fetch_chain_cache(struct nft_handle *h,
- const struct builtin_table *t,
- const char *chain)
+static int __fetch_chain_cache(struct nft_handle *h,
+ const struct builtin_table *t,
+ const struct nftnl_chain *c)
{
struct nftnl_chain_list_cb_data d = {
.h = h,
@@ -314,51 +361,47 @@ static int fetch_chain_cache(struct nft_handle *h,
};
char buf[16536];
struct nlmsghdr *nlh;
- int i, ret;
+ int ret;
- if (!t) {
- for (i = 0; i < NFT_TABLE_MAX; i++) {
- enum nft_table_type type = h->tables[i].type;
+ nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
+ c ? NLM_F_ACK : NLM_F_DUMP, h->seq);
+ if (c)
+ nftnl_chain_nlmsg_build_payload(nlh, c);
- if (!h->tables[i].name)
- continue;
+ ret = mnl_talk(h, nlh, nftnl_chain_list_cb, &d);
+ if (ret < 0 && errno == EINTR)
+ assert(nft_restart(h) >= 0);
- if (h->cache->table[type].chains)
- continue;
+ return ret;
+}
- h->cache->table[type].chains = nftnl_chain_list_alloc();
- if (!h->cache->table[type].chains)
- return -1;
- }
- } else if (!h->cache->table[t->type].chains) {
- h->cache->table[t->type].chains = nftnl_chain_list_alloc();
- if (!h->cache->table[t->type].chains)
- return -1;
- }
+static int fetch_chain_cache(struct nft_handle *h,
+ const struct builtin_table *t,
+ struct list_head *chains)
+{
+ struct cache_chain *cc;
+ struct nftnl_chain *c;
+ int rc, ret = 0;
- if (t && chain) {
- struct nftnl_chain *c = nftnl_chain_alloc();
+ if (!chains)
+ return __fetch_chain_cache(h, t, NULL);
- if (!c)
- return -1;
+ assert(t);
- nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
- h->family, NLM_F_ACK,
- h->seq);
- nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, t->name);
- nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
- nftnl_chain_nlmsg_build_payload(nlh, c);
- nftnl_chain_free(c);
- } else {
- nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
- h->family, NLM_F_DUMP,
- h->seq);
- }
+ c = nftnl_chain_alloc();
+ if (!c)
+ return -1;
- ret = mnl_talk(h, nlh, nftnl_chain_list_cb, &d);
- if (ret < 0 && errno == EINTR)
- assert(nft_restart(h) >= 0);
+ nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, t->name);
+
+ 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;
}
@@ -417,20 +460,14 @@ static int nft_rule_list_update(struct nftnl_chain *c, void *data)
}
static int fetch_rule_cache(struct nft_handle *h,
- const struct builtin_table *t, const char *chain)
+ const struct builtin_table *t)
{
int i;
if (t) {
- struct nftnl_chain_list *list;
- struct nftnl_chain *c;
-
- list = h->cache->table[t->type].chains;
+ struct nftnl_chain_list *list =
+ h->cache->table[t->type].chains;
- if (chain) {
- c = nftnl_chain_list_lookup_byname(list, chain);
- return nft_rule_list_update(c, h);
- }
return nftnl_chain_list_foreach(list, nft_rule_list_update, h);
}
@@ -447,89 +484,46 @@ static int fetch_rule_cache(struct nft_handle *h,
return 0;
}
+static int flush_cache(struct nft_handle *h, struct nft_cache *c,
+ const char *tablename);
+
static void
-__nft_build_cache(struct nft_handle *h, enum nft_cache_level level,
- const struct builtin_table *t, const char *set,
- const char *chain)
+__nft_build_cache(struct nft_handle *h)
{
- uint32_t genid_start, genid_stop;
+ struct nft_cache_req *req = &h->cache_req;
+ const struct builtin_table *t = NULL;
+ struct list_head *chains = NULL;
+ uint32_t genid_check;
- if (level <= h->cache_level)
+ if (h->cache_init)
return;
-retry:
- mnl_genid_get(h, &genid_start);
-
- if (h->cache_level && genid_start != h->nft_genid)
- flush_chain_cache(h, NULL);
-
- switch (h->cache_level) {
- case NFT_CL_NONE:
- fetch_table_cache(h);
- if (level == NFT_CL_TABLES)
- break;
- /* fall through */
- case NFT_CL_TABLES:
- fetch_chain_cache(h, t, chain);
- if (level == NFT_CL_CHAINS)
- break;
- /* fall through */
- case NFT_CL_CHAINS:
- fetch_set_cache(h, t, set);
- if (level == NFT_CL_SETS)
- break;
- /* fall through */
- case NFT_CL_SETS:
- fetch_rule_cache(h, t, chain);
- if (level == NFT_CL_RULES)
- break;
- /* fall through */
- case NFT_CL_RULES:
- break;
- }
- mnl_genid_get(h, &genid_stop);
- if (genid_start != genid_stop) {
- flush_chain_cache(h, NULL);
- goto retry;
+ if (req->table) {
+ t = nft_table_builtin_find(h, req->table);
+ if (!req->all_chains)
+ chains = &req->chain_list;
}
- if (!t && !chain)
- h->cache_level = level;
- else if (h->cache_level < NFT_CL_TABLES)
- h->cache_level = NFT_CL_TABLES;
-
- h->nft_genid = genid_start;
-}
-
-void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c)
-{
- const struct builtin_table *t;
- const char *table, *chain;
-
- if (!c)
- return __nft_build_cache(h, NFT_CL_RULES, NULL, NULL, NULL);
-
- table = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
- chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
- t = nft_table_builtin_find(h, table);
- __nft_build_cache(h, NFT_CL_RULES, t, NULL, chain);
-}
-
-void nft_fake_cache(struct nft_handle *h)
-{
- int i;
-
- fetch_table_cache(h);
- for (i = 0; i < NFT_TABLE_MAX; i++) {
- enum nft_table_type type = h->tables[i].type;
-
- if (!h->tables[i].name)
- continue;
+ h->cache_init = true;
+retry:
+ mnl_genid_get(h, &h->nft_genid);
- h->cache->table[type].chains = nftnl_chain_list_alloc();
+ if (req->level >= NFT_CL_TABLES)
+ fetch_table_cache(h);
+ if (req->level == NFT_CL_FAKE)
+ return;
+ if (req->level >= NFT_CL_CHAINS)
+ fetch_chain_cache(h, t, chains);
+ if (req->level >= NFT_CL_SETS)
+ fetch_set_cache(h, t, NULL);
+ if (req->level >= NFT_CL_RULES)
+ fetch_rule_cache(h, t);
+
+ mnl_genid_get(h, &genid_check);
+ if (h->nft_genid != genid_check) {
+ flush_cache(h, h->cache, NULL);
+ goto retry;
}
- h->cache_level = NFT_CL_RULES;
- mnl_genid_get(h, &h->nft_genid);
}
static void __nft_flush_cache(struct nft_handle *h)
@@ -610,51 +604,93 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
if (h->tables[i].name == NULL)
continue;
- if (!c->table[i].chains)
- continue;
-
- nftnl_chain_list_free(c->table[i].chains);
- c->table[i].chains = NULL;
- if (c->table[i].sets)
+ if (c->table[i].chains) {
+ nftnl_chain_list_free(c->table[i].chains);
+ c->table[i].chains = NULL;
+ }
+ if (c->table[i].sets) {
nftnl_set_list_free(c->table[i].sets);
- c->table[i].sets = NULL;
+ c->table[i].sets = NULL;
+ }
+ }
+ if (c->tables) {
+ nftnl_table_list_free(c->tables);
+ c->tables = NULL;
}
- nftnl_table_list_free(c->tables);
- c->tables = NULL;
return 1;
}
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;
+ }
+
+ __nft_build_cache(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);
- h->cache_level = NFT_CL_NONE;
- __nft_build_cache(h, level, NULL, NULL, NULL);
+ /* 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)
{
- if (h->cache_index)
- flush_cache(h, &h->__cache[0], NULL);
+ 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);
+ flush_cache(h, &h->__cache[0], NULL);
+ h->cache = &h->__cache[0];
+ h->cache_init = false;
+
+ if (req->level != NFT_CL_FAKE)
+ req->level = NFT_CL_TABLES;
+ if (req->table) {
+ 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)
{
- __nft_build_cache(h, NFT_CL_TABLES, NULL, NULL, NULL);
-
return h->cache->tables;
}
@@ -667,8 +703,6 @@ nft_set_list_get(struct nft_handle *h, const char *table, const char *set)
if (!t)
return NULL;
- __nft_build_cache(h, NFT_CL_RULES, t, set, NULL);
-
return h->cache->table[t->type].sets;
}
@@ -681,8 +715,6 @@ nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain)
if (!t)
return NULL;
- __nft_build_cache(h, NFT_CL_CHAINS, t, NULL, chain);
-
return h->cache->table[t->type].chains;
}