JSON API as well as nft CLI allow to run multiple commands within the same batch. Depending on the local cache state, a later command may trigger a cache update which removes the local entry added by an earlier command. To overcome this, introduce a special genid value to set when local entries are added to the cache which blocks all cache updates until the batch has been committed to the kernel. Signed-off-by: Phil Sutter <phil@xxxxxx> --- include/nftables.h | 2 ++ include/rule.h | 12 ++++++++---- src/evaluate.c | 6 +++--- src/libnftables.c | 2 ++ src/monitor.c | 4 ++-- src/rule.c | 17 +++++++++++++---- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/include/nftables.h b/include/nftables.h index 25e78c80df7e0..47d2c2bb036cc 100644 --- a/include/nftables.h +++ b/include/nftables.h @@ -37,6 +37,8 @@ struct nft_cache { struct list_head list; uint32_t seqnum; }; +#define CACHE_LOCK_GENID (uint16_t)-1 +#define CACHE_UNLOCK_GENID 1 struct mnl_socket; struct parser_state; diff --git a/include/rule.h b/include/rule.h index 9e029899513fd..d5ddc78a7d3c4 100644 --- a/include/rule.h +++ b/include/rule.h @@ -216,7 +216,8 @@ extern const char *chain_hookname_lookup(const char *name); extern struct chain *chain_alloc(const char *name); extern struct chain *chain_get(struct chain *chain); extern void chain_free(struct chain *chain); -extern void chain_add_hash(struct chain *chain, struct table *table); +extern void chain_add_hash(struct chain *chain, struct table *table, + struct nft_cache *cache); extern struct chain *chain_lookup(const struct table *table, const struct handle *h); @@ -299,7 +300,8 @@ extern struct set *set_alloc(const struct location *loc); extern struct set *set_get(struct set *set); extern void set_free(struct set *set); extern struct set *set_clone(const struct set *set); -extern void set_add_hash(struct set *set, struct table *table); +extern void set_add_hash(struct set *set, struct table *table, + struct nft_cache *cache); extern struct set *set_lookup(const struct table *table, const char *name); extern struct set *set_lookup_global(uint32_t family, const char *table, const char *name, struct nft_cache *cache); @@ -381,7 +383,8 @@ struct obj { struct obj *obj_alloc(const struct location *loc); extern struct obj *obj_get(struct obj *obj); void obj_free(struct obj *obj); -void obj_add_hash(struct obj *obj, struct table *table); +void obj_add_hash(struct obj *obj, struct table *table, + struct nft_cache *cache); struct obj *obj_lookup(const struct table *table, const char *name, uint32_t type); void obj_print(const struct obj *n, struct output_ctx *octx); @@ -406,7 +409,8 @@ struct flowtable { extern struct flowtable *flowtable_alloc(const struct location *loc); extern struct flowtable *flowtable_get(struct flowtable *flowtable); extern void flowtable_free(struct flowtable *flowtable); -extern void flowtable_add_hash(struct flowtable *flowtable, struct table *table); +extern void flowtable_add_hash(struct flowtable *flowtable, + struct table *table, struct nft_cache *cache); void flowtable_print(const struct flowtable *n, struct output_ctx *octx); diff --git a/src/evaluate.c b/src/evaluate.c index db49a18d0150c..f2448296be0c0 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -3014,7 +3014,7 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set) ctx->set = NULL; if (set_lookup(table, set->handle.set.name) == NULL) - set_add_hash(set_get(set), table); + set_add_hash(set_get(set), table, ctx->cache); /* Default timeout value implies timeout support */ if (set->timeout) @@ -3198,12 +3198,12 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain) if (chain_lookup(table, &ctx->cmd->handle) == NULL) { chain = chain_alloc(NULL); handle_merge(&chain->handle, &ctx->cmd->handle); - chain_add_hash(chain, table); + chain_add_hash(chain, table, ctx->cache); } return 0; } else { if (chain_lookup(table, &chain->handle) == NULL) - chain_add_hash(chain_get(chain), table); + chain_add_hash(chain_get(chain), table, ctx->cache); } if (chain->flags & CHAIN_F_BASECHAIN) { diff --git a/src/libnftables.c b/src/libnftables.c index 977763793e768..1726b0ee19b54 100644 --- a/src/libnftables.c +++ b/src/libnftables.c @@ -464,6 +464,7 @@ err: } erec_print_list(&nft->output, &msgs, nft->debug_mask); iface_cache_release(); + nft->cache.genid = CACHE_UNLOCK_GENID; if (nft->scanner) { scanner_destroy(nft->scanner); nft->scanner = NULL; @@ -505,6 +506,7 @@ err: } erec_print_list(&nft->output, &msgs, nft->debug_mask); iface_cache_release(); + nft->cache.genid = CACHE_UNLOCK_GENID; if (nft->scanner) { scanner_destroy(nft->scanner); nft->scanner = NULL; diff --git a/src/monitor.c b/src/monitor.c index 213c40d119b4c..bab298f8352f7 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -608,7 +608,7 @@ static void netlink_events_cache_addset(struct netlink_mon_handler *monh, goto out; } - set_add_hash(s, t); + set_add_hash(s, t, monh->cache); out: nftnl_set_free(nls); } @@ -698,7 +698,7 @@ static void netlink_events_cache_addobj(struct netlink_mon_handler *monh, goto out; } - obj_add_hash(obj, t); + obj_add_hash(obj, t, monh->cache); out: nftnl_obj_free(nlo); } diff --git a/src/rule.c b/src/rule.c index 8f78a36ca1f91..35a4de5d9b8c5 100644 --- a/src/rule.c +++ b/src/rule.c @@ -234,6 +234,8 @@ int cache_update(struct mnl_socket *nf_sock, struct nft_cache *cache, .octx = octx, }; + if (cache->genid == CACHE_LOCK_GENID) + return 0; replay: ctx.seqnum = cache->seqnum++; genid = mnl_genid_get(&ctx); @@ -341,9 +343,10 @@ void set_free(struct set *set) xfree(set); } -void set_add_hash(struct set *set, struct table *table) +void set_add_hash(struct set *set, struct table *table, struct nft_cache *cache) { list_add_tail(&set->list, &table->sets); + cache->genid = CACHE_LOCK_GENID; } struct set *set_lookup(const struct table *table, const char *name) @@ -753,9 +756,11 @@ void chain_free(struct chain *chain) xfree(chain); } -void chain_add_hash(struct chain *chain, struct table *table) +void chain_add_hash(struct chain *chain, struct table *table, + struct nft_cache *cache) { list_add_tail(&chain->list, &table->chains); + cache->genid = CACHE_LOCK_GENID; } struct chain *chain_lookup(const struct table *table, const struct handle *h) @@ -1094,6 +1099,7 @@ struct table *table_get(struct table *table) void table_add_hash(struct table *table, struct nft_cache *cache) { list_add_tail(&table->list, &cache->list); + cache->genid = CACHE_LOCK_GENID; } struct table *table_lookup(const struct handle *h, @@ -1634,9 +1640,10 @@ void obj_free(struct obj *obj) xfree(obj); } -void obj_add_hash(struct obj *obj, struct table *table) +void obj_add_hash(struct obj *obj, struct table *table, struct nft_cache *cache) { list_add_tail(&obj->list, &table->objs); + cache->genid = CACHE_LOCK_GENID; } struct obj *obj_lookup(const struct table *table, const char *name, @@ -1935,9 +1942,11 @@ void flowtable_free(struct flowtable *flowtable) xfree(flowtable); } -void flowtable_add_hash(struct flowtable *flowtable, struct table *table) +void flowtable_add_hash(struct flowtable *flowtable, struct table *table, + struct nft_cache *cache) { list_add_tail(&flowtable->list, &table->flowtables); + cache->genid = CACHE_LOCK_GENID; } static void flowtable_print_declaration(const struct flowtable *flowtable, -- 2.19.0