diff options
| -rw-r--r-- | net/netfilter/nft_compat.c | 48 | 
1 files changed, 47 insertions, 1 deletions
| diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index abed3490a8f8..5eb269428832 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -29,6 +29,9 @@ struct nft_xt {  	struct nft_expr_ops	ops;  	refcount_t		refcnt; +	/* used only when transaction mutex is locked */ +	unsigned int		listcnt; +  	/* Unlike other expressions, ops doesn't have static storage duration.  	 * nft core assumes they do.  We use kfree_rcu so that nft core can  	 * can check expr->ops->size even after nft_compat->destroy() frees @@ -61,7 +64,7 @@ static struct nft_compat_net *nft_compat_pernet(struct net *net)  static bool nft_xt_put(struct nft_xt *xt)  {  	if (refcount_dec_and_test(&xt->refcnt)) { -		list_del(&xt->head); +		WARN_ON_ONCE(!list_empty(&xt->head));  		kfree_rcu(xt, rcu_head);  		return true;  	} @@ -555,6 +558,43 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)  	__nft_match_destroy(ctx, expr, nft_expr_priv(expr));  } +static void nft_compat_activate(const struct nft_ctx *ctx, +				const struct nft_expr *expr, +				struct list_head *h) +{ +	struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops); + +	if (xt->listcnt == 0) +		list_add(&xt->head, h); + +	xt->listcnt++; +} + +static void nft_compat_activate_mt(const struct nft_ctx *ctx, +				   const struct nft_expr *expr) +{ +	struct nft_compat_net *cn = nft_compat_pernet(ctx->net); + +	nft_compat_activate(ctx, expr, &cn->nft_match_list); +} + +static void nft_compat_activate_tg(const struct nft_ctx *ctx, +				   const struct nft_expr *expr) +{ +	struct nft_compat_net *cn = nft_compat_pernet(ctx->net); + +	nft_compat_activate(ctx, expr, &cn->nft_target_list); +} + +static void nft_compat_deactivate(const struct nft_ctx *ctx, +				  const struct nft_expr *expr) +{ +	struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops); + +	if (--xt->listcnt == 0) +		list_del_init(&xt->head); +} +  static void  nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)  { @@ -808,6 +848,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,  	nft_match->ops.eval = nft_match_eval;  	nft_match->ops.init = nft_match_init;  	nft_match->ops.destroy = nft_match_destroy; +	nft_match->ops.activate = nft_compat_activate_mt; +	nft_match->ops.deactivate = nft_compat_deactivate;  	nft_match->ops.dump = nft_match_dump;  	nft_match->ops.validate = nft_match_validate;  	nft_match->ops.data = match; @@ -824,6 +866,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,  	nft_match->ops.size = matchsize; +	nft_match->listcnt = 1;  	list_add(&nft_match->head, &cn->nft_match_list);  	return &nft_match->ops; @@ -910,6 +953,8 @@ nft_target_select_ops(const struct nft_ctx *ctx,  	nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));  	nft_target->ops.init = nft_target_init;  	nft_target->ops.destroy = nft_target_destroy; +	nft_target->ops.activate = nft_compat_activate_tg; +	nft_target->ops.deactivate = nft_compat_deactivate;  	nft_target->ops.dump = nft_target_dump;  	nft_target->ops.validate = nft_target_validate;  	nft_target->ops.data = target; @@ -919,6 +964,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,  	else  		nft_target->ops.eval = nft_target_eval_xt; +	nft_target->listcnt = 1;  	list_add(&nft_target->head, &cn->nft_target_list);  	return &nft_target->ops; | 
