]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/netfilter/nf_tables_api.c
netfilter: nf_tables: bogus EBUSY in chain deletions
[mirror_ubuntu-bionic-kernel.git] / net / netfilter / nf_tables_api.c
index 6e72ae341fda1952e895e31ac54345e78459fd35..fd3fc0d3e40204b28eb1714afb8fb606ba044fef 100644 (file)
@@ -220,6 +220,34 @@ static int nft_delchain(struct nft_ctx *ctx)
        return err;
 }
 
+static void nft_rule_expr_activate(const struct nft_ctx *ctx,
+                                  struct nft_rule *rule)
+{
+       struct nft_expr *expr;
+
+       expr = nft_expr_first(rule);
+       while (expr != nft_expr_last(rule) && expr->ops) {
+               if (expr->ops->activate)
+                       expr->ops->activate(ctx, expr);
+
+               expr = nft_expr_next(expr);
+       }
+}
+
+static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
+                                    struct nft_rule *rule)
+{
+       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 = nft_expr_next(expr);
+       }
+}
+
 static int
 nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
 {
@@ -265,6 +293,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
                nft_trans_destroy(trans);
                return err;
        }
+       nft_rule_expr_deactivate(ctx, rule);
 
        return 0;
 }
@@ -2218,6 +2247,13 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
        kfree(rule);
 }
 
+static void nf_tables_rule_release(const struct nft_ctx *ctx,
+                                  struct nft_rule *rule)
+{
+       nft_rule_expr_deactivate(ctx, rule);
+       nf_tables_rule_destroy(ctx, rule);
+}
+
 #define NFT_RULE_MAXEXPRS      128
 
 static struct nft_expr_info *info;
@@ -2385,7 +2421,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
        return 0;
 
 err2:
-       nf_tables_rule_destroy(&ctx, rule);
+       nf_tables_rule_release(&ctx, rule);
 err1:
        for (i = 0; i < n; i++) {
                if (info[i].ops != NULL)
@@ -4123,7 +4159,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
  *     NFT_GOTO verdicts. This function must be called on active data objects
  *     from the second phase of the commit protocol.
  */
-static void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
+void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
 {
        if (type == NFT_DATA_VERDICT) {
                switch (data->verdict.code) {
@@ -5290,10 +5326,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
                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));
                        break;
                case NFT_MSG_DELRULE:
                        trans->ctx.chain->use++;
                        nft_clear(trans->ctx.net, nft_trans_rule(trans));
+                       nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSET:
@@ -5873,7 +5911,7 @@ int __nft_release_basechain(struct nft_ctx *ctx)
        list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
                list_del(&rule->list);
                ctx->chain->use--;
-               nf_tables_rule_destroy(ctx, rule);
+               nf_tables_rule_release(ctx, rule);
        }
        list_del(&ctx->chain->list);
        ctx->table->use--;
@@ -5907,7 +5945,7 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
                        list_for_each_entry_safe(rule, nr, &chain->rules, list) {
                                list_del(&rule->list);
                                chain->use--;
-                               nf_tables_rule_destroy(&ctx, rule);
+                               nf_tables_rule_release(&ctx, rule);
                        }
                }
                list_for_each_entry_safe(set, ns, &table->sets, list) {