--- b/include/net/netfilter/nf_tables.h +++ a/include/net/netfilter/nf_tables.h @@ -469,7 +469,9 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_binding *binding); void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding); +void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding); - struct nft_set_binding *binding, bool commit); void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set); /** @@ -719,13 +721,6 @@ #define NFT_EXPR_STATEFUL 0x1 #define NFT_EXPR_GC 0x2 -enum nft_trans_phase { - NFT_TRANS_PREPARE, - NFT_TRANS_ABORT, - NFT_TRANS_COMMIT, - NFT_TRANS_RELEASE -}; - /** * struct nft_expr_ops - nf_tables expression operations * @@ -755,8 +750,7 @@ void (*activate)(const struct nft_ctx *ctx, const struct nft_expr *expr); void (*deactivate)(const struct nft_ctx *ctx, + const struct nft_expr *expr); - const struct nft_expr *expr, - enum nft_trans_phase phase); void (*destroy)(const struct nft_ctx *ctx, const struct nft_expr *expr); void (*destroy_clone)(const struct nft_ctx *ctx, @@ -1329,15 +1323,12 @@ struct nft_trans_set { struct nft_set *set; u32 set_id; - bool bound; }; #define nft_trans_set(trans) \ (((struct nft_trans_set *)trans->data)->set) #define nft_trans_set_id(trans) \ (((struct nft_trans_set *)trans->data)->set_id) -#define nft_trans_set_bound(trans) \ - (((struct nft_trans_set *)trans->data)->bound) struct nft_trans_chain { bool update; --- b/net/netfilter/nf_tables_api.c +++ a/net/netfilter/nf_tables_api.c @@ -116,23 +116,6 @@ kfree(trans); } -static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) -{ - struct net *net = ctx->net; - struct nft_trans *trans; - - if (!nft_set_is_anonymous(set)) - return; - - list_for_each_entry_reverse(trans, &net->nft.commit_list, list) { - if (trans->msg_type == NFT_MSG_NEWSET && - nft_trans_set(trans) == set) { - nft_trans_set_bound(trans) = true; - break; - } - } -} - static int nf_tables_register_hook(struct net *net, const struct nft_table *table, struct nft_chain *chain) @@ -228,6 +211,18 @@ return err; } +/* either expr ops provide both activate/deactivate, or neither */ +static bool nft_expr_check_ops(const struct nft_expr_ops *ops) +{ + if (!ops) + return true; + + if (WARN_ON_ONCE((!ops->activate ^ !ops->deactivate))) + return false; + + return true; +} + static void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule) { @@ -243,15 +238,14 @@ } static void nft_rule_expr_deactivate(const struct nft_ctx *ctx, + struct nft_rule *rule) - struct nft_rule *rule, - enum nft_trans_phase phase) { struct nft_expr *expr; expr = nft_expr_first(rule); while (expr != nft_expr_last(rule) && expr->ops) { if (expr->ops->deactivate) + expr->ops->deactivate(ctx, expr); - expr->ops->deactivate(ctx, expr, phase); expr = nft_expr_next(expr); } @@ -302,7 +296,7 @@ nft_trans_destroy(trans); return err; } + nft_rule_expr_deactivate(ctx, rule); - nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_PREPARE); return 0; } @@ -1938,6 +1932,9 @@ */ int nft_register_expr(struct nft_expr_type *type) { + if (!nft_expr_check_ops(type->ops)) + return -EINVAL; + nfnl_lock(NFNL_SUBSYS_NFTABLES); if (type->family == NFPROTO_UNSPEC) list_add_tail_rcu(&type->list, &nf_tables_expressions); @@ -2085,6 +2082,10 @@ err = PTR_ERR(ops); goto err1; } + if (!nft_expr_check_ops(ops)) { + err = -EINVAL; + goto err1; + } } else ops = type->ops; @@ -2481,7 +2482,7 @@ static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) { + nft_rule_expr_deactivate(ctx, rule); - nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE); nf_tables_rule_destroy(ctx, rule); } @@ -3678,30 +3679,39 @@ bind: binding->chain = ctx->chain; list_add_tail_rcu(&binding->list, &set->bindings); - nft_set_trans_bind(ctx, set); - return 0; } EXPORT_SYMBOL_GPL(nf_tables_bind_set); +void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding) +{ + if (list_empty(&set->bindings) && nft_set_is_anonymous(set) && + nft_is_active(ctx->net, set)) + list_add_tail_rcu(&set->list, &ctx->table->sets); + + list_add_tail_rcu(&binding->list, &set->bindings); +} +EXPORT_SYMBOL_GPL(nf_tables_rebind_set); + void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding) - struct nft_set_binding *binding, bool event) { list_del_rcu(&binding->list); + if (list_empty(&set->bindings) && nft_set_is_anonymous(set) && + nft_is_active(ctx->net, set)) - if (list_empty(&set->bindings) && nft_set_is_anonymous(set)) { list_del_rcu(&set->list); - if (event) - nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, - GFP_KERNEL); - } } EXPORT_SYMBOL_GPL(nf_tables_unbind_set); void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set) { + if (list_empty(&set->bindings) && nft_set_is_anonymous(set) && + nft_is_active(ctx->net, set)) { + nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC); - if (list_empty(&set->bindings) && nft_set_is_anonymous(set)) nft_set_destroy(set); + } } EXPORT_SYMBOL_GPL(nf_tables_destroy_set); @@ -6494,9 +6504,6 @@ nf_tables_rule_notify(&trans->ctx, nft_trans_rule(trans), NFT_MSG_DELRULE); - nft_rule_expr_deactivate(&trans->ctx, - nft_trans_rule(trans), - NFT_TRANS_COMMIT); break; case NFT_MSG_NEWSET: nft_clear(net, nft_trans_set(trans)); @@ -6583,8 +6590,7 @@ nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); break; case NFT_MSG_NEWSET: + nft_set_destroy(nft_trans_set(trans)); - if (!nft_trans_set_bound(trans)) - nft_set_destroy(nft_trans_set(trans)); break; case NFT_MSG_NEWSETELEM: nft_set_elem_destroy(nft_trans_elem_set(trans), @@ -6645,9 +6651,7 @@ case NFT_MSG_NEWRULE: trans->ctx.chain->use--; list_del_rcu(&nft_trans_rule(trans)->list); + nft_rule_expr_deactivate(&trans->ctx, nft_trans_rule(trans)); - nft_rule_expr_deactivate(&trans->ctx, - nft_trans_rule(trans), - NFT_TRANS_ABORT); break; case NFT_MSG_DELRULE: trans->ctx.chain->use++; @@ -6657,8 +6661,7 @@ break; case NFT_MSG_NEWSET: trans->ctx.table->use--; + list_del_rcu(&nft_trans_set(trans)->list); - if (!nft_trans_set_bound(trans)) - list_del_rcu(&nft_trans_set(trans)->list); break; case NFT_MSG_DELSET: trans->ctx.table->use++; --- b/net/netfilter/nft_compat.c +++ a/net/netfilter/nft_compat.c @@ -575,15 +575,12 @@ } static void nft_compat_deactivate(const struct nft_ctx *ctx, - const struct nft_expr *expr, - enum nft_trans_phase phase) + const struct nft_expr *expr) { struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops); - if (phase == NFT_TRANS_ABORT || phase == NFT_TRANS_COMMIT) { - if (--xt->listcnt == 0) - list_del_init(&xt->head); - } + if (--xt->listcnt == 0) + list_del_init(&xt->head); } static void --- b/net/netfilter/nft_dynset.c +++ a/net/netfilter/nft_dynset.c @@ -235,17 +235,20 @@ return err; } +static void nft_dynset_activate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_dynset *priv = nft_expr_priv(expr); + + nf_tables_rebind_set(ctx, priv->set, &priv->binding); +} + static void nft_dynset_deactivate(const struct nft_ctx *ctx, + const struct nft_expr *expr) - const struct nft_expr *expr, - enum nft_trans_phase phase) { struct nft_dynset *priv = nft_expr_priv(expr); + nf_tables_unbind_set(ctx, priv->set, &priv->binding); - if (phase == NFT_TRANS_PREPARE) - return; - - nf_tables_unbind_set(ctx, priv->set, &priv->binding, - phase == NFT_TRANS_COMMIT); } static void nft_dynset_destroy(const struct nft_ctx *ctx, @@ -293,6 +296,7 @@ .eval = nft_dynset_eval, .init = nft_dynset_init, .destroy = nft_dynset_destroy, + .activate = nft_dynset_activate, .deactivate = nft_dynset_deactivate, .dump = nft_dynset_dump, }; --- b/net/netfilter/nft_immediate.c +++ a/net/netfilter/nft_immediate.c @@ -72,14 +72,10 @@ } static void nft_immediate_deactivate(const struct nft_ctx *ctx, + const struct nft_expr *expr) - const struct nft_expr *expr, - enum nft_trans_phase phase) { const struct nft_immediate_expr *priv = nft_expr_priv(expr); - if (phase == NFT_TRANS_COMMIT) - return; - return nft_data_release(&priv->data, nft_dreg_to_type(priv->dreg)); } --- b/net/netfilter/nft_lookup.c +++ a/net/netfilter/nft_lookup.c @@ -121,17 +121,20 @@ return 0; } +static void nft_lookup_activate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_lookup *priv = nft_expr_priv(expr); + + nf_tables_rebind_set(ctx, priv->set, &priv->binding); +} + static void nft_lookup_deactivate(const struct nft_ctx *ctx, + const struct nft_expr *expr) - const struct nft_expr *expr, - enum nft_trans_phase phase) { struct nft_lookup *priv = nft_expr_priv(expr); + nf_tables_unbind_set(ctx, priv->set, &priv->binding); - if (phase == NFT_TRANS_PREPARE) - return; - - nf_tables_unbind_set(ctx, priv->set, &priv->binding, - phase == NFT_TRANS_COMMIT); } static void nft_lookup_destroy(const struct nft_ctx *ctx, @@ -222,6 +225,7 @@ .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), .eval = nft_lookup_eval, .init = nft_lookup_init, + .activate = nft_lookup_activate, .deactivate = nft_lookup_deactivate, .destroy = nft_lookup_destroy, .dump = nft_lookup_dump, --- b/net/netfilter/nft_objref.c +++ a/net/netfilter/nft_objref.c @@ -155,17 +155,20 @@ return -1; } +static void nft_objref_map_activate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_objref_map *priv = nft_expr_priv(expr); + + nf_tables_rebind_set(ctx, priv->set, &priv->binding); +} + static void nft_objref_map_deactivate(const struct nft_ctx *ctx, + const struct nft_expr *expr) - const struct nft_expr *expr, - enum nft_trans_phase phase) { struct nft_objref_map *priv = nft_expr_priv(expr); + nf_tables_unbind_set(ctx, priv->set, &priv->binding); - if (phase == NFT_TRANS_PREPARE) - return; - - nf_tables_unbind_set(ctx, priv->set, &priv->binding, - phase == NFT_TRANS_COMMIT); } static void nft_objref_map_destroy(const struct nft_ctx *ctx, @@ -182,6 +185,7 @@ .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)), .eval = nft_objref_map_eval, .init = nft_objref_map_init, + .activate = nft_objref_map_activate, .deactivate = nft_objref_map_deactivate, .destroy = nft_objref_map_destroy, .dump = nft_objref_map_dump,