summaryrefslogtreecommitdiffstats
path: root/src/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cache.c')
-rw-r--r--src/cache.c287
1 files changed, 216 insertions, 71 deletions
diff --git a/src/cache.c b/src/cache.c
index 85de970f..b75a5bf3 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -6,6 +6,8 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <expression.h>
#include <statement.h>
#include <rule.h>
@@ -28,10 +30,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
break;
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
- NFT_CACHE_FLOWTABLE;
+ NFT_CACHE_SET;
list_for_each_entry(set, &cmd->table->sets, list) {
if (set->automerge)
flags |= NFT_CACHE_SETELEM_MAYBE;
@@ -52,21 +51,15 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
break;
case CMD_OBJ_ELEMENTS:
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
NFT_CACHE_SETELEM_MAYBE;
break;
case CMD_OBJ_RULE:
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
- NFT_CACHE_FLOWTABLE;
+ NFT_CACHE_SET;
- if (cmd->handle.index.id ||
- cmd->handle.position.id)
- flags |= NFT_CACHE_RULE | NFT_CACHE_UPDATE;
+ if (cmd->handle.index.id)
+ flags |= NFT_CACHE_FULL | NFT_CACHE_UPDATE;
break;
default:
break;
@@ -79,9 +72,11 @@ static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- flags |= NFT_CACHE_SETELEM_MAYBE;
+ flags |= NFT_CACHE_SET |
+ NFT_CACHE_SETELEM_MAYBE;
break;
default:
+ flags = NFT_CACHE_TABLE;
break;
}
@@ -124,9 +119,9 @@ void nft_cache_filter_fini(struct nft_cache_filter *filter)
struct nft_filter_obj *obj, *next;
list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
- xfree(obj);
+ free(obj);
}
- xfree(filter);
+ free(filter);
}
static void cache_filter_add(struct nft_cache_filter *filter,
@@ -195,37 +190,58 @@ static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags)
return flags;
}
+static void obj_filter_setup(const struct cmd *cmd, unsigned int *flags,
+ struct nft_cache_filter *filter, int type)
+{
+ assert(filter);
+
+ if (cmd->handle.family)
+ filter->list.family = cmd->handle.family;
+ if (cmd->handle.table.name)
+ filter->list.table = cmd->handle.table.name;
+ if (cmd->handle.obj.name)
+ filter->list.obj = cmd->handle.obj.name;
+
+ filter->list.obj_type = type;
+ *flags |= NFT_CACHE_TABLE | NFT_CACHE_OBJECT;
+}
+
static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
unsigned int flags,
struct nft_cache_filter *filter)
{
switch (cmd->obj) {
case CMD_OBJ_TABLE:
- if (filter && cmd->handle.table.name) {
- filter->list.family = cmd->handle.family;
+ filter->list.family = cmd->handle.family;
+ if (!cmd->handle.table.name) {
+ flags |= NFT_CACHE_TABLE;
+ break;
+ } else {
filter->list.table = cmd->handle.table.name;
}
flags |= NFT_CACHE_FULL;
break;
case CMD_OBJ_CHAIN:
- if (filter && cmd->handle.chain.name) {
+ if (cmd->handle.chain.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
filter->list.chain = cmd->handle.chain.name;
+ /* implicit terse listing to fetch content of anonymous
+ * sets only when chain name is specified.
+ */
+ flags |= NFT_CACHE_TERSE;
}
flags |= NFT_CACHE_FULL;
break;
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
- if (filter && cmd->handle.table.name && cmd->handle.set.name) {
+ if (cmd->handle.table.name && cmd->handle.set.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
filter->list.set = cmd->handle.set.name;
}
if (filter->list.table && filter->list.set)
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM;
- else if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
else
flags |= NFT_CACHE_FULL;
break;
@@ -235,10 +251,11 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_SETS:
case CMD_OBJ_MAPS:
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET;
+ if (!nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_SETELEM;
break;
case CMD_OBJ_FLOWTABLE:
- if (filter &&
- cmd->handle.table.name &&
+ if (cmd->handle.table.name &&
cmd->handle.flowtable.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
@@ -248,11 +265,91 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_FLOWTABLES:
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_COUNTER);
+ break;
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_QUOTA);
+ break;
+ case CMD_OBJ_CT_HELPER:
+ case CMD_OBJ_CT_HELPERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_HELPER);
+ break;
+ case CMD_OBJ_LIMIT:
+ case CMD_OBJ_LIMITS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_LIMIT);
+ break;
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_TIMEOUT);
+ break;
+ case CMD_OBJ_SECMARK:
+ case CMD_OBJ_SECMARKS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_SECMARK);
+ break;
+ case CMD_OBJ_CT_EXPECT:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_EXPECT);
+ break;
+ case CMD_OBJ_SYNPROXY:
+ case CMD_OBJ_SYNPROXYS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_SYNPROXY);
+ break;
case CMD_OBJ_RULESET:
- if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
- else
- flags |= NFT_CACHE_FULL;
+ default:
+ flags |= NFT_CACHE_FULL;
+ break;
+ }
+ flags |= NFT_CACHE_REFRESH;
+
+ if (nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_TERSE;
+
+ return flags;
+}
+
+static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags,
+ struct nft_cache_filter *filter)
+{
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ case CMD_OBJ_CHAIN:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
+ if (cmd->handle.table.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ }
+ if (cmd->handle.chain.name)
+ filter->list.chain = cmd->handle.chain.name;
+ if (cmd->handle.family)
+ filter->list.family = cmd->handle.family;
+ if (cmd->handle.handle.id)
+ filter->list.rule_handle = cmd->handle.handle.id;
+
+ filter->reset.rule = true;
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_COUNTER);
+ filter->reset.obj = true;
+ break;
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_QUOTA);
+ filter->reset.obj = true;
+ break;
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ if (cmd->handle.table.name && cmd->handle.set.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.set = cmd->handle.set.name;
+ }
+ flags |= NFT_CACHE_SETELEM;
+ filter->reset.elem = true;
break;
default:
flags |= NFT_CACHE_FULL;
@@ -277,6 +374,7 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
}
break;
case CMD_OBJ_RULE:
+ case CMD_OBJ_RULES:
case CMD_OBJ_CHAIN:
case CMD_OBJ_CHAINS:
if (h->table.name &&
@@ -342,7 +440,9 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
case CMD_OBJ_CT_HELPER:
case CMD_OBJ_CT_HELPERS:
case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_CT_EXPECTATIONS:
if (h->table.name &&
strlen(h->table.name) > NFT_NAME_MAXLEN) {
loc = &h->table.location;
@@ -365,51 +465,52 @@ err_name_too_long:
return -1;
}
+static void reset_filter(struct nft_cache_filter *filter)
+{
+ memset(&filter->list, 0, sizeof(filter->list));
+ memset(&filter->reset, 0, sizeof(filter->reset));
+}
+
int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
struct list_head *msgs, struct nft_cache_filter *filter,
unsigned int *pflags)
{
- unsigned int flags = NFT_CACHE_EMPTY;
+ unsigned int flags, batch_flags = NFT_CACHE_EMPTY;
struct cmd *cmd;
+ assert(filter);
+
list_for_each_entry(cmd, cmds, list) {
if (nft_handle_validate(cmd, msgs) < 0)
return -1;
- if (filter->list.table && cmd->op != CMD_LIST)
- memset(&filter->list, 0, sizeof(filter->list));
+ flags = NFT_CACHE_EMPTY;
+ reset_filter(filter);
switch (cmd->op) {
case CMD_ADD:
case CMD_INSERT:
case CMD_CREATE:
flags = evaluate_cache_add(cmd, flags);
- if (nft_output_echo(&nft->output))
- flags |= NFT_CACHE_FULL;
break;
- case CMD_REPLACE:
- flags = NFT_CACHE_FULL;
+ case CMD_REPLACE: /* only for rule */
+ flags = NFT_CACHE_TABLE | NFT_CACHE_SET;
break;
case CMD_DELETE:
- flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_FLOWTABLE |
- NFT_CACHE_OBJECT;
-
+ case CMD_DESTROY:
flags = evaluate_cache_del(cmd, flags);
break;
case CMD_GET:
flags = evaluate_cache_get(cmd, flags);
break;
case CMD_RESET:
- flags |= NFT_CACHE_TABLE;
+ flags = evaluate_cache_reset(cmd, flags, filter);
break;
case CMD_LIST:
- flags |= evaluate_cache_list(nft, cmd, flags, filter);
+ flags = evaluate_cache_list(nft, cmd, flags, filter);
break;
case CMD_MONITOR:
- flags |= NFT_CACHE_FULL;
+ flags = NFT_CACHE_FULL;
break;
case CMD_FLUSH:
flags = evaluate_cache_flush(cmd, flags, filter);
@@ -424,8 +525,9 @@ int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
default:
break;
}
+ batch_flags |= flags;
}
- *pflags = flags;
+ *pflags = batch_flags;
return 0;
}
@@ -591,8 +693,8 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data)
table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
- if (h->family != family ||
- strcmp(table, h->table.name) != 0 ||
+ if ((h->family != NFPROTO_UNSPEC && h->family != family) ||
+ (h->table.name && strcmp(table, h->table.name) != 0) ||
(h->chain.name && strcmp(chain, h->chain.name) != 0))
return 0;
@@ -608,15 +710,28 @@ static int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
const struct nft_cache_filter *filter)
{
struct nftnl_rule_list *rule_cache;
- const char *table = NULL;
+ const char *table = h->table.name;
const char *chain = NULL;
+ uint64_t rule_handle = 0;
+ int family = h->family;
+ bool dump = true;
if (filter) {
- table = filter->list.table;
- chain = filter->list.chain;
+ if (filter->list.table)
+ table = filter->list.table;
+ if (filter->list.chain)
+ chain = filter->list.chain;
+ if (filter->list.rule_handle) {
+ rule_handle = filter->list.rule_handle;
+ dump = false;
+ }
+ if (filter->list.family)
+ family = filter->list.family;
}
- rule_cache = mnl_nft_rule_dump(ctx, h->family, table, chain);
+ rule_cache = mnl_nft_rule_dump(ctx, family,
+ table, chain, rule_handle, dump,
+ filter->reset.rule);
if (rule_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -740,10 +855,19 @@ struct obj_cache_dump_ctx {
static int obj_cache_cb(struct nftnl_obj *nlo, void *arg)
{
struct obj_cache_dump_ctx *ctx = arg;
+ const char *obj_table;
const char *obj_name;
+ uint32_t obj_family;
struct obj *obj;
uint32_t hash;
+ obj_table = nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE);
+ obj_family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY);
+
+ if (obj_family != ctx->table->handle.family ||
+ strcmp(obj_table, ctx->table->handle.table.name))
+ return 0;
+
obj = netlink_delinearize_obj(ctx->nlctx, nlo);
if (!obj)
return -1;
@@ -752,6 +876,9 @@ static int obj_cache_cb(struct nftnl_obj *nlo, void *arg)
hash = djb_hash(obj_name) % NFT_CACHE_HSIZE;
cache_add(&obj->cache, &ctx->table->obj_cache, hash);
+ nftnl_obj_list_del(nlo);
+ nftnl_obj_free(nlo);
+
return 0;
}
@@ -768,13 +895,28 @@ static int obj_cache_init(struct netlink_ctx *ctx, struct table *table,
}
static struct nftnl_obj_list *obj_cache_dump(struct netlink_ctx *ctx,
- const struct table *table)
+ const struct nft_cache_filter *filter)
{
struct nftnl_obj_list *obj_list;
+ int type = NFT_OBJECT_UNSPEC;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *obj = NULL;
+ bool dump = true;
- obj_list = mnl_nft_obj_dump(ctx, table->handle.family,
- table->handle.table.name, NULL,
- 0, true, false);
+ if (filter) {
+ family = filter->list.family;
+ if (filter->list.table)
+ table = filter->list.table;
+ if (filter->list.obj) {
+ obj = filter->list.obj;
+ dump = false;
+ }
+ if (filter->list.obj_type)
+ type = filter->list.obj_type;
+ }
+ obj_list = mnl_nft_obj_dump(ctx, family, table, obj, type, dump,
+ filter->reset.obj);
if (!obj_list) {
if (errno == EINTR)
return NULL;
@@ -976,13 +1118,14 @@ err_ctx_list:
static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
const char *chain_name)
{
- struct nft_cache_filter filter;
+ struct nft_cache_filter filter = {};
struct chain *chain;
int ret = 0;
list_for_each_entry(chain, &table->chain_bindings, cache.list) {
filter.list.table = table->handle.table.name;
filter.list.chain = chain->handle.chain.name;
+
ret = rule_init_cache(ctx, table, &filter);
}
@@ -995,7 +1138,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
struct nftnl_flowtable_list *ft_list = NULL;
struct nftnl_chain_list *chain_list = NULL;
struct nftnl_set_list *set_list = NULL;
- struct nftnl_obj_list *obj_list;
+ struct nftnl_obj_list *obj_list = NULL;
struct table *table;
struct set *set;
int ret = 0;
@@ -1012,6 +1155,13 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
goto cache_fails;
}
}
+ if (flags & NFT_CACHE_OBJECT_BIT) {
+ obj_list = obj_cache_dump(ctx, filter);
+ if (!obj_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
ft_list = ft_cache_dump(ctx, filter);
if (!ft_list) {
@@ -1035,7 +1185,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, filter->reset.elem);
if (ret < 0)
goto cache_fails;
}
@@ -1048,7 +1198,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, filter->reset.elem);
if (ret < 0)
goto cache_fails;
}
@@ -1064,15 +1214,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
goto cache_fails;
}
if (flags & NFT_CACHE_OBJECT_BIT) {
- obj_list = obj_cache_dump(ctx, table);
- if (!obj_list) {
- ret = -1;
- goto cache_fails;
- }
ret = obj_cache_init(ctx, table, obj_list);
-
- nftnl_obj_list_free(obj_list);
-
if (ret < 0)
goto cache_fails;
}
@@ -1093,6 +1235,8 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
cache_fails:
if (set_list)
nftnl_set_list_free(set_list);
+ if (obj_list)
+ nftnl_obj_list_free(obj_list);
if (ft_list)
nftnl_flowtable_list_free(ft_list);
@@ -1129,9 +1273,10 @@ static bool nft_cache_is_complete(struct nft_cache *cache, unsigned int flags)
return (cache->flags & flags) == flags;
}
-static bool nft_cache_needs_refresh(struct nft_cache *cache)
+static bool nft_cache_needs_refresh(struct nft_cache *cache, unsigned int flags)
{
- return cache->flags & NFT_CACHE_REFRESH;
+ return (cache->flags & NFT_CACHE_REFRESH) ||
+ (flags & NFT_CACHE_REFRESH);
}
static bool nft_cache_is_updated(struct nft_cache *cache, uint16_t genid)
@@ -1159,7 +1304,7 @@ int nft_cache_update(struct nft_ctx *nft, unsigned int flags,
replay:
ctx.seqnum = cache->seqnum++;
genid = mnl_genid_get(&ctx);
- if (!nft_cache_needs_refresh(cache) &&
+ if (!nft_cache_needs_refresh(cache, flags) &&
nft_cache_is_complete(cache, flags) &&
nft_cache_is_updated(cache, genid))
return 0;
@@ -1231,7 +1376,7 @@ void cache_init(struct cache *cache)
void cache_free(struct cache *cache)
{
- xfree(cache->ht);
+ free(cache->ht);
}
void cache_add(struct cache_item *item, struct cache *cache, uint32_t hash)