Merge tag 'tty-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
[linux-2.6-block.git] / net / netfilter / nf_tables_api.c
index 1b913760f205be79e1809c983cb3140c284a00cc..ff7304ae58ac4f99cf4841badcbe0afd8a1f6968 100644 (file)
@@ -240,6 +240,10 @@ static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
        if (trans == NULL)
                return NULL;
 
+       if (msg_type == NFT_MSG_NEWRULE && ctx->nla[NFTA_RULE_ID] != NULL) {
+               nft_trans_rule_id(trans) =
+                       ntohl(nla_get_be32(ctx->nla[NFTA_RULE_ID]));
+       }
        nft_trans_rule(trans) = rule;
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
 
@@ -576,6 +580,28 @@ err:
        return err;
 }
 
+static void _nf_tables_table_disable(struct net *net,
+                                    const struct nft_af_info *afi,
+                                    struct nft_table *table,
+                                    u32 cnt)
+{
+       struct nft_chain *chain;
+       u32 i = 0;
+
+       list_for_each_entry(chain, &table->chains, list) {
+               if (!nft_is_active_next(net, chain))
+                       continue;
+               if (!(chain->flags & NFT_BASE_CHAIN))
+                       continue;
+
+               if (cnt && i++ == cnt)
+                       break;
+
+               nf_unregister_net_hooks(net, nft_base_chain(chain)->ops,
+                                       afi->nops);
+       }
+}
+
 static int nf_tables_table_enable(struct net *net,
                                  const struct nft_af_info *afi,
                                  struct nft_table *table)
@@ -598,18 +624,8 @@ static int nf_tables_table_enable(struct net *net,
        }
        return 0;
 err:
-       list_for_each_entry(chain, &table->chains, list) {
-               if (!nft_is_active_next(net, chain))
-                       continue;
-               if (!(chain->flags & NFT_BASE_CHAIN))
-                       continue;
-
-               if (i-- <= 0)
-                       break;
-
-               nf_unregister_net_hooks(net, nft_base_chain(chain)->ops,
-                                       afi->nops);
-       }
+       if (i)
+               _nf_tables_table_disable(net, afi, table, i);
        return err;
 }
 
@@ -617,17 +633,7 @@ static void nf_tables_table_disable(struct net *net,
                                    const struct nft_af_info *afi,
                                    struct nft_table *table)
 {
-       struct nft_chain *chain;
-
-       list_for_each_entry(chain, &table->chains, list) {
-               if (!nft_is_active_next(net, chain))
-                       continue;
-               if (!(chain->flags & NFT_BASE_CHAIN))
-                       continue;
-
-               nf_unregister_net_hooks(net, nft_base_chain(chain)->ops,
-                                       afi->nops);
-       }
+       _nf_tables_table_disable(net, afi, table, 0);
 }
 
 static int nf_tables_updtable(struct nft_ctx *ctx)
@@ -696,10 +702,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
        if (IS_ERR(table)) {
                if (PTR_ERR(table) != -ENOENT)
                        return PTR_ERR(table);
-               table = NULL;
-       }
-
-       if (table != NULL) {
+       } else {
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
@@ -2294,6 +2297,22 @@ err1:
        return err;
 }
 
+static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
+                                            const struct nlattr *nla)
+{
+       u32 id = ntohl(nla_get_be32(nla));
+       struct nft_trans *trans;
+
+       list_for_each_entry(trans, &net->nft.commit_list, list) {
+               struct nft_rule *rule = nft_trans_rule(trans);
+
+               if (trans->msg_type == NFT_MSG_NEWRULE &&
+                   id == nft_trans_rule_id(trans))
+                       return rule;
+       }
+       return ERR_PTR(-ENOENT);
+}
+
 static int nf_tables_delrule(struct net *net, struct sock *nlsk,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
                             const struct nlattr * const nla[])
@@ -2331,6 +2350,12 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
                        if (IS_ERR(rule))
                                return PTR_ERR(rule);
 
+                       err = nft_delrule(&ctx, rule);
+               } else if (nla[NFTA_RULE_ID]) {
+                       rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]);
+                       if (IS_ERR(rule))
+                               return PTR_ERR(rule);
+
                        err = nft_delrule(&ctx, rule);
                } else {
                        err = nft_delrule_by_chain(&ctx);
@@ -2399,12 +2424,14 @@ nft_select_set_ops(const struct nlattr * const nla[],
        features = 0;
        if (nla[NFTA_SET_FLAGS] != NULL) {
                features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
-               features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT;
+               features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT |
+                           NFT_SET_OBJECT;
        }
 
-       bops       = NULL;
-       best.size  = ~0;
-       best.class = ~0;
+       bops        = NULL;
+       best.size   = ~0;
+       best.lookup = ~0;
+       best.space  = ~0;
 
        list_for_each_entry(ops, &nf_tables_set_ops, list) {
                if ((ops->features & features) != features)
@@ -2414,16 +2441,27 @@ nft_select_set_ops(const struct nlattr * const nla[],
 
                switch (policy) {
                case NFT_SET_POL_PERFORMANCE:
-                       if (est.class < best.class)
-                               break;
-                       if (est.class == best.class && est.size < best.size)
+                       if (est.lookup < best.lookup)
                                break;
+                       if (est.lookup == best.lookup) {
+                               if (!desc->size) {
+                                       if (est.space < best.space)
+                                               break;
+                               } else if (est.size < best.size) {
+                                       break;
+                               }
+                       }
                        continue;
                case NFT_SET_POL_MEMORY:
-                       if (est.size < best.size)
-                               break;
-                       if (est.size == best.size && est.class < best.class)
+                       if (!desc->size) {
+                               if (est.space < best.space)
+                                       break;
+                               if (est.space == best.space &&
+                                   est.lookup < best.lookup)
+                                       break;
+                       } else if (est.size < best.size) {
                                break;
+                       }
                        continue;
                default:
                        break;
@@ -2966,10 +3004,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        if (IS_ERR(set)) {
                if (PTR_ERR(set) != -ENOENT)
                        return PTR_ERR(set);
-               set = NULL;
-       }
-
-       if (set != NULL) {
+       } else {
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
@@ -3125,6 +3160,7 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
                iter.count      = 0;
                iter.err        = 0;
                iter.fn         = nf_tables_bind_check_setelem;
+               iter.flush      = false;
 
                set->ops->walk(ctx, set, &iter);
                if (iter.err < 0)
@@ -3378,6 +3414,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        args.iter.count         = 0;
        args.iter.err           = 0;
        args.iter.fn            = nf_tables_dump_setelem;
+       args.iter.flush         = false;
        set->ops->walk(&ctx, set, &args.iter);
 
        nla_nest_end(skb, nest);
@@ -3756,7 +3793,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        return 0;
 
 err6:
-       set->ops->remove(set, &elem);
+       set->ops->remove(ctx->net, set, &elem);
 err5:
        kfree(trans);
 err4:
@@ -3902,7 +3939,7 @@ static int nft_flush_set(const struct nft_ctx *ctx,
        if (!trans)
                return -ENOMEM;
 
-       if (!set->ops->deactivate_one(ctx->net, set, elem->priv)) {
+       if (!set->ops->flush(ctx->net, set, elem->priv)) {
                err = -ENOENT;
                goto err1;
        }
@@ -3940,15 +3977,14 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
                return -EBUSY;
 
        if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) {
-               struct nft_set_dump_args args = {
-                       .iter   = {
-                               .genmask        = genmask,
-                               .fn             = nft_flush_set,
-                       },
+               struct nft_set_iter iter = {
+                       .genmask        = genmask,
+                       .fn             = nft_flush_set,
+                       .flush          = true,
                };
-               set->ops->walk(&ctx, set, &args.iter);
+               set->ops->walk(&ctx, set, &iter);
 
-               return args.iter.err;
+               return iter.err;
        }
 
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
@@ -4163,10 +4199,7 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
                if (err != -ENOENT)
                        return err;
 
-               obj = NULL;
-       }
-
-       if (obj != NULL) {
+       } else {
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
 
@@ -4811,7 +4844,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                        nf_tables_setelem_notify(&trans->ctx, te->set,
                                                 &te->elem,
                                                 NFT_MSG_DELSETELEM, 0);
-                       te->set->ops->remove(te->set, &te->elem);
+                       te->set->ops->remove(net, te->set, &te->elem);
                        atomic_dec(&te->set->nelems);
                        te->set->ndeact--;
                        break;
@@ -4932,7 +4965,7 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
                case NFT_MSG_NEWSETELEM:
                        te = (struct nft_trans_elem *)trans->data;
 
-                       te->set->ops->remove(te->set, &te->elem);
+                       te->set->ops->remove(net, te->set, &te->elem);
                        atomic_dec(&te->set->nelems);
                        break;
                case NFT_MSG_DELSETELEM:
@@ -4966,6 +4999,11 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
        return 0;
 }
 
+static bool nf_tables_valid_genid(struct net *net, u32 genid)
+{
+       return net->nft.base_seq == genid;
+}
+
 static const struct nfnetlink_subsystem nf_tables_subsys = {
        .name           = "nf_tables",
        .subsys_id      = NFNL_SUBSYS_NFTABLES,
@@ -4973,6 +5011,7 @@ static const struct nfnetlink_subsystem nf_tables_subsys = {
        .cb             = nf_tables_cb,
        .commit         = nf_tables_commit,
        .abort          = nf_tables_abort,
+       .valid_genid    = nf_tables_valid_genid,
 };
 
 int nft_chain_validate_dependency(const struct nft_chain *chain,
@@ -5098,6 +5137,7 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
                        iter.count      = 0;
                        iter.err        = 0;
                        iter.fn         = nf_tables_loop_check_setelem;
+                       iter.flush      = false;
 
                        set->ops->walk(ctx, set, &iter);
                        if (iter.err < 0)