Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[pandora-kernel.git] / net / netfilter / nf_tables_api.c
index 8746ff9..deeb95f 100644 (file)
@@ -899,6 +899,9 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
 static void nft_chain_stats_replace(struct nft_base_chain *chain,
                                    struct nft_stats __percpu *newstats)
 {
+       if (newstats == NULL)
+               return;
+
        if (chain->stats) {
                struct nft_stats __percpu *oldstats =
                                nft_dereference(chain->stats);
@@ -2247,80 +2250,7 @@ err:
        return err;
 }
 
-static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
-                                    struct netlink_callback *cb)
-{
-       const struct nft_set *set;
-       unsigned int idx = 0, s_idx = cb->args[0];
-
-       if (cb->args[1])
-               return skb->len;
-
-       rcu_read_lock();
-       cb->seq = ctx->net->nft.base_seq;
-
-       list_for_each_entry_rcu(set, &ctx->table->sets, list) {
-               if (idx < s_idx)
-                       goto cont;
-               if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
-                                      NLM_F_MULTI) < 0) {
-                       cb->args[0] = idx;
-                       goto done;
-               }
-               nl_dump_check_consistent(cb, nlmsg_hdr(skb));
-cont:
-               idx++;
-       }
-       cb->args[1] = 1;
-done:
-       rcu_read_unlock();
-       return skb->len;
-}
-
-static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
-                                     struct netlink_callback *cb)
-{
-       const struct nft_set *set;
-       unsigned int idx, s_idx = cb->args[0];
-       struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
-
-       if (cb->args[1])
-               return skb->len;
-
-       rcu_read_lock();
-       cb->seq = ctx->net->nft.base_seq;
-
-       list_for_each_entry_rcu(table, &ctx->afi->tables, list) {
-               if (cur_table) {
-                       if (cur_table != table)
-                               continue;
-
-                       cur_table = NULL;
-               }
-               ctx->table = table;
-               idx = 0;
-               list_for_each_entry_rcu(set, &ctx->table->sets, list) {
-                       if (idx < s_idx)
-                               goto cont;
-                       if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
-                                              NLM_F_MULTI) < 0) {
-                               cb->args[0] = idx;
-                               cb->args[2] = (unsigned long) table;
-                               goto done;
-                       }
-                       nl_dump_check_consistent(cb, nlmsg_hdr(skb));
-cont:
-                       idx++;
-               }
-       }
-       cb->args[1] = 1;
-done:
-       rcu_read_unlock();
-       return skb->len;
-}
-
-static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
-                                  struct netlink_callback *cb)
+static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
 {
        const struct nft_set *set;
        unsigned int idx, s_idx = cb->args[0];
@@ -2328,6 +2258,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
        struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
        struct net *net = sock_net(skb->sk);
        int cur_family = cb->args[3];
+       struct nft_ctx *ctx = cb->data, ctx_set;
 
        if (cb->args[1])
                return skb->len;
@@ -2336,28 +2267,34 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
        cb->seq = net->nft.base_seq;
 
        list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
+               if (ctx->afi && ctx->afi != afi)
+                       continue;
+
                if (cur_family) {
                        if (afi->family != cur_family)
                                continue;
 
                        cur_family = 0;
                }
-
                list_for_each_entry_rcu(table, &afi->tables, list) {
+                       if (ctx->table && ctx->table != table)
+                               continue;
+
                        if (cur_table) {
                                if (cur_table != table)
                                        continue;
 
                                cur_table = NULL;
                        }
-
-                       ctx->table = table;
-                       ctx->afi = afi;
                        idx = 0;
-                       list_for_each_entry_rcu(set, &ctx->table->sets, list) {
+                       list_for_each_entry_rcu(set, &table->sets, list) {
                                if (idx < s_idx)
                                        goto cont;
-                               if (nf_tables_fill_set(skb, ctx, set,
+
+                               ctx_set = *ctx;
+                               ctx_set.table = table;
+                               ctx_set.afi = afi;
+                               if (nf_tables_fill_set(skb, &ctx_set, set,
                                                       NFT_MSG_NEWSET,
                                                       NLM_F_MULTI) < 0) {
                                        cb->args[0] = idx;
@@ -2379,31 +2316,10 @@ done:
        return skb->len;
 }
 
-static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
+static int nf_tables_dump_sets_done(struct netlink_callback *cb)
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
-       struct nlattr *nla[NFTA_SET_MAX + 1];
-       struct nft_ctx ctx;
-       int err, ret;
-
-       err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_MAX,
-                         nft_set_policy);
-       if (err < 0)
-               return err;
-
-       err = nft_ctx_init_from_setattr(&ctx, cb->skb, cb->nlh, (void *)nla);
-       if (err < 0)
-               return err;
-
-       if (ctx.table == NULL) {
-               if (ctx.afi == NULL)
-                       ret = nf_tables_dump_sets_all(&ctx, skb, cb);
-               else
-                       ret = nf_tables_dump_sets_family(&ctx, skb, cb);
-       } else
-               ret = nf_tables_dump_sets_table(&ctx, skb, cb);
-
-       return ret;
+       kfree(cb->data);
+       return 0;
 }
 
 #define NFT_SET_INACTIVE       (1 << 15)       /* Internal set flag */
@@ -2426,7 +2342,17 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_sets,
+                       .done = nf_tables_dump_sets_done,
                };
+               struct nft_ctx *ctx_dump;
+
+               ctx_dump = kmalloc(sizeof(*ctx_dump), GFP_KERNEL);
+               if (ctx_dump == NULL)
+                       return -ENOMEM;
+
+               *ctx_dump = ctx;
+               c.data = ctx_dump;
+
                return netlink_dump_start(nlsk, skb, nlh, &c);
        }
 
@@ -3150,6 +3076,9 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
        struct nft_ctx ctx;
        int rem, err = 0;
 
+       if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
+               return -EINVAL;
+
        err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
        if (err < 0)
                return err;
@@ -3208,16 +3137,14 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
                goto err2;
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
-       if (trans == NULL)
+       if (trans == NULL) {
+               err = -ENOMEM;
                goto err2;
+       }
 
        nft_trans_elem(trans) = elem;
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
-
-       nft_data_uninit(&elem.key, NFT_DATA_VALUE);
-       if (set->flags & NFT_SET_MAP)
-               nft_data_uninit(&elem.data, set->dtype);
-
+       return 0;
 err2:
        nft_data_uninit(&elem.key, desc.type);
 err1:
@@ -3233,6 +3160,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        struct nft_ctx ctx;
        int rem, err = 0;
 
+       if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
+               return -EINVAL;
+
        err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
        if (err < 0)
                return err;
@@ -3380,7 +3310,7 @@ static int nf_tables_commit(struct sk_buff *skb)
 {
        struct net *net = sock_net(skb->sk);
        struct nft_trans *trans, *next;
-       struct nft_set *set;
+       struct nft_trans_elem *te;
 
        /* Bump generation counter, invalidate any dump in progress */
        while (++net->nft.base_seq == 0);
@@ -3466,13 +3396,17 @@ static int nf_tables_commit(struct sk_buff *skb)
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM:
-                       nf_tables_setelem_notify(&trans->ctx,
-                                                nft_trans_elem_set(trans),
-                                                &nft_trans_elem(trans),
+                       te = (struct nft_trans_elem *)trans->data;
+                       nf_tables_setelem_notify(&trans->ctx, te->set,
+                                                &te->elem,
                                                 NFT_MSG_DELSETELEM, 0);
-                       set = nft_trans_elem_set(trans);
-                       set->ops->get(set, &nft_trans_elem(trans));
-                       set->ops->remove(set, &nft_trans_elem(trans));
+                       te->set->ops->get(te->set, &te->elem);
+                       te->set->ops->remove(te->set, &te->elem);
+                       nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
+                       if (te->elem.flags & NFT_SET_MAP) {
+                               nft_data_uninit(&te->elem.data,
+                                               te->set->dtype);
+                       }
                        nft_trans_destroy(trans);
                        break;
                }