]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - net/netfilter/nf_tables_api.c
Merge tag 'v3.16-rc1' into i2c/for-next
[karo-tx-linux.git] / net / netfilter / nf_tables_api.c
index 047884776586db680a9b9547ceb94e9a2035bf9c..624e083125b93b8755819f868c90307f04cd74b8 100644 (file)
@@ -538,8 +538,7 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
                return PTR_ERR(table);
        if (table->flags & NFT_TABLE_INACTIVE)
                return -ENOENT;
-
-       if (!list_empty(&table->chains) || !list_empty(&table->sets))
+       if (table->use > 0)
                return -EBUSY;
 
        nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
@@ -553,6 +552,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
 
 static void nf_tables_table_destroy(struct nft_ctx *ctx)
 {
+       BUG_ON(ctx->table->use > 0);
+
        kfree(ctx->table);
        module_put(ctx->afi->owner);
 }
@@ -1128,6 +1129,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        if (err < 0)
                goto err2;
 
+       table->use++;
        list_add_tail(&chain->list, &table->chains);
        return 0;
 err2:
@@ -1169,7 +1171,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
                return PTR_ERR(chain);
        if (chain->flags & NFT_CHAIN_INACTIVE)
                return -ENOENT;
-       if (!list_empty(&chain->rules) || chain->use > 0)
+       if (chain->use > 0)
                return -EBUSY;
 
        nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
@@ -1177,6 +1179,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
        if (err < 0)
                return err;
 
+       table->use--;
        list_del(&chain->list);
        return 0;
 }
@@ -1814,6 +1817,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
                err = -ENOMEM;
                goto err3;
        }
+       chain->use++;
        return 0;
 
 err3:
@@ -1841,6 +1845,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
                if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL)
                        return -ENOMEM;
                nft_rule_disactivate_next(ctx->net, rule);
+               ctx->chain->use--;
                return 0;
        }
        return -ENOENT;
@@ -2186,7 +2191,7 @@ nla_put_failure:
 
 static int nf_tables_set_notify(const struct nft_ctx *ctx,
                                const struct nft_set *set,
-                               int event)
+                               int event, gfp_t gfp_flags)
 {
        struct sk_buff *skb;
        u32 portid = ctx->portid;
@@ -2197,7 +2202,7 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
                return 0;
 
        err = -ENOBUFS;
-       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       skb = nlmsg_new(NLMSG_GOODSIZE, gfp_flags);
        if (skb == NULL)
                goto err;
 
@@ -2208,7 +2213,7 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
        }
 
        err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
-                            ctx->report, GFP_KERNEL);
+                            ctx->report, gfp_flags);
 err:
        if (err < 0)
                nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
@@ -2588,6 +2593,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                goto err2;
 
        list_add_tail(&set->list, &table->sets);
+       table->use++;
        return 0;
 
 err2:
@@ -2607,7 +2613,7 @@ static void nft_set_destroy(struct nft_set *set)
 static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
 {
        list_del(&set->list);
-       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
+       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
        nft_set_destroy(set);
 }
 
@@ -2642,6 +2648,7 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
                return err;
 
        list_del(&set->list);
+       ctx.table->use--;
        return 0;
 }
 
@@ -3077,7 +3084,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                goto err4;
 
        nft_trans_elem(trans) = elem;
-       list_add(&trans->list, &ctx->net->nft.commit_list);
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
 err4:
@@ -3122,6 +3129,8 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
                err = nft_add_set_elem(&ctx, set, attr);
                if (err < 0)
                        break;
+
+               set->nelems++;
        }
        return err;
 }
@@ -3161,7 +3170,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
                goto err2;
 
        nft_trans_elem(trans) = elem;
-       list_add(&trans->list, &ctx->net->nft.commit_list);
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
 
        nft_data_uninit(&elem.key, NFT_DATA_VALUE);
        if (set->flags & NFT_SET_MAP)
@@ -3196,6 +3205,8 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
                err = nft_del_setelem(&ctx, set, attr);
                if (err < 0)
                        break;
+
+               set->nelems--;
        }
        return err;
 }
@@ -3361,15 +3372,13 @@ static int nf_tables_commit(struct sk_buff *skb)
                case NFT_MSG_NEWCHAIN:
                        if (nft_trans_chain_update(trans))
                                nft_chain_commit_update(trans);
-                       else {
+                       else
                                trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
-                               trans->ctx.table->use++;
-                       }
+
                        nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELCHAIN:
-                       trans->ctx.table->use--;
                        nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
                        if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
                            trans->ctx.chain->flags & NFT_BASE_CHAIN) {
@@ -3392,16 +3401,22 @@ static int nf_tables_commit(struct sk_buff *skb)
                        break;
                case NFT_MSG_NEWSET:
                        nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
+                       /* This avoids hitting -EBUSY when deleting the table
+                        * from the transaction.
+                        */
+                       if (nft_trans_set(trans)->flags & NFT_SET_ANONYMOUS &&
+                           !list_empty(&nft_trans_set(trans)->bindings))
+                               trans->ctx.table->use--;
+
                        nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
-                                            NFT_MSG_NEWSET);
+                                            NFT_MSG_NEWSET, GFP_KERNEL);
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSET:
                        nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
-                                            NFT_MSG_DELSET);
+                                            NFT_MSG_DELSET, GFP_KERNEL);
                        break;
                case NFT_MSG_NEWSETELEM:
-                       nft_trans_elem_set(trans)->nelems++;
                        nf_tables_setelem_notify(&trans->ctx,
                                                 nft_trans_elem_set(trans),
                                                 &nft_trans_elem(trans),
@@ -3409,7 +3424,6 @@ static int nf_tables_commit(struct sk_buff *skb)
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM:
-                       nft_trans_elem_set(trans)->nelems--;
                        nf_tables_setelem_notify(&trans->ctx,
                                                 nft_trans_elem_set(trans),
                                                 &nft_trans_elem(trans),
@@ -3487,6 +3501,7 @@ static int nf_tables_abort(struct sk_buff *skb)
 
                                nft_trans_destroy(trans);
                        } else {
+                               trans->ctx.table->use--;
                                list_del(&trans->ctx.chain->list);
                                if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
                                    trans->ctx.chain->flags & NFT_BASE_CHAIN) {
@@ -3496,38 +3511,46 @@ static int nf_tables_abort(struct sk_buff *skb)
                        }
                        break;
                case NFT_MSG_DELCHAIN:
+                       trans->ctx.table->use++;
                        list_add_tail(&trans->ctx.chain->list,
                                      &trans->ctx.table->chains);
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWRULE:
+                       trans->ctx.chain->use--;
                        list_del_rcu(&nft_trans_rule(trans)->list);
                        break;
                case NFT_MSG_DELRULE:
+                       trans->ctx.chain->use++;
                        nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSET:
+                       trans->ctx.table->use--;
                        list_del(&nft_trans_set(trans)->list);
                        break;
                case NFT_MSG_DELSET:
+                       trans->ctx.table->use++;
                        list_add_tail(&nft_trans_set(trans)->list,
                                      &trans->ctx.table->sets);
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSETELEM:
+                       nft_trans_elem_set(trans)->nelems--;
                        set = nft_trans_elem_set(trans);
                        set->ops->get(set, &nft_trans_elem(trans));
                        set->ops->remove(set, &nft_trans_elem(trans));
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM:
+                       nft_trans_elem_set(trans)->nelems++;
                        nft_trans_destroy(trans);
                        break;
                }
        }
 
-       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+       list_for_each_entry_safe_reverse(trans, next,
+                                        &net->nft.commit_list, list) {
                list_del(&trans->list);
                trans->ctx.nla = NULL;
                call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);