Merge tag 'v4.5-rc5' into devel
[linux-2.6-block.git] / net / netfilter / nf_tables_api.c
index 2cb429d34c03c99e2e030becede22b05db604a76..2011977cd79d9481cd16404aa769c70524a15af4 100644 (file)
@@ -41,6 +41,8 @@ int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
 }
 EXPORT_SYMBOL_GPL(nft_register_afinfo);
 
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi);
+
 /**
  *     nft_unregister_afinfo - unregister nf_tables address family info
  *
@@ -48,9 +50,10 @@ EXPORT_SYMBOL_GPL(nft_register_afinfo);
  *
  *     Unregister the address family for use with nf_tables.
  */
-void nft_unregister_afinfo(struct nft_af_info *afi)
+void nft_unregister_afinfo(struct net *net, struct nft_af_info *afi)
 {
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       __nft_release_afinfo(net, afi);
        list_del_rcu(&afi->list);
        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
 }
@@ -128,8 +131,8 @@ static void nft_trans_destroy(struct nft_trans *trans)
        kfree(trans);
 }
 
-int nft_register_basechain(struct nft_base_chain *basechain,
-                          unsigned int hook_nops)
+static int nft_register_basechain(struct nft_base_chain *basechain,
+                                 unsigned int hook_nops)
 {
        struct net *net = read_pnet(&basechain->pnet);
 
@@ -138,10 +141,9 @@ int nft_register_basechain(struct nft_base_chain *basechain,
 
        return nf_register_net_hooks(net, basechain->ops, hook_nops);
 }
-EXPORT_SYMBOL_GPL(nft_register_basechain);
 
-void nft_unregister_basechain(struct nft_base_chain *basechain,
-                             unsigned int hook_nops)
+static void nft_unregister_basechain(struct nft_base_chain *basechain,
+                                    unsigned int hook_nops)
 {
        struct net *net = read_pnet(&basechain->pnet);
 
@@ -150,7 +152,6 @@ void nft_unregister_basechain(struct nft_base_chain *basechain,
 
        nf_unregister_net_hooks(net, basechain->ops, hook_nops);
 }
-EXPORT_SYMBOL_GPL(nft_unregister_basechain);
 
 static int nf_tables_register_hooks(const struct nft_table *table,
                                    struct nft_chain *chain,
@@ -542,15 +543,14 @@ done:
        return skb->len;
 }
 
-static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
-                             const struct nlmsghdr *nlh,
+static int nf_tables_gettable(struct net *net, struct sock *nlsk,
+                             struct sk_buff *skb, const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        const struct nft_af_info *afi;
        const struct nft_table *table;
        struct sk_buff *skb2;
-       struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        int err;
 
@@ -831,8 +831,6 @@ static int nf_tables_deltable(struct net *net, struct sock *nlsk,
        table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
        if (IS_ERR(table))
                return PTR_ERR(table);
-       if (table->flags & NFT_TABLE_INACTIVE)
-               return -ENOENT;
 
        ctx.afi = afi;
        ctx.table = table;
@@ -1098,8 +1096,8 @@ done:
        return skb->len;
 }
 
-static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
-                             const struct nlmsghdr *nlh,
+static int nf_tables_getchain(struct net *net, struct sock *nlsk,
+                             struct sk_buff *skb, const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -1107,7 +1105,6 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
        const struct nft_table *table;
        const struct nft_chain *chain;
        struct sk_buff *skb2;
-       struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        int err;
 
@@ -1492,14 +1489,10 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
        table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
-       if (table->flags & NFT_TABLE_INACTIVE)
-               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
-       if (chain->flags & NFT_CHAIN_INACTIVE)
-               return -ENOENT;
        if (chain->use > 0)
                return -EBUSY;
 
@@ -1928,8 +1921,8 @@ done:
        return skb->len;
 }
 
-static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
-                            const struct nlmsghdr *nlh,
+static int nf_tables_getrule(struct net *net, struct sock *nlsk,
+                            struct sk_buff *skb, const struct nlmsghdr *nlh,
                             const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -1938,7 +1931,6 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
        const struct nft_chain *chain;
        const struct nft_rule *rule;
        struct sk_buff *skb2;
-       struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        int err;
 
@@ -2191,8 +2183,6 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
        table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
-       if (table->flags & NFT_TABLE_INACTIVE)
-               return -ENOENT;
 
        if (nla[NFTA_RULE_CHAIN]) {
                chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
@@ -2333,6 +2323,8 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
        [NFTA_SET_ID]                   = { .type = NLA_U32 },
        [NFTA_SET_TIMEOUT]              = { .type = NLA_U64 },
        [NFTA_SET_GC_INTERVAL]          = { .type = NLA_U32 },
+       [NFTA_SET_USERDATA]             = { .type = NLA_BINARY,
+                                           .len  = NFT_USERDATA_MAXLEN },
 };
 
 static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
@@ -2361,8 +2353,6 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net,
                table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
                if (IS_ERR(table))
                        return PTR_ERR(table);
-               if (table->flags & NFT_TABLE_INACTIVE)
-                       return -ENOENT;
        }
 
        nft_ctx_init(ctx, net, skb, nlh, afi, table, NULL, nla);
@@ -2494,6 +2484,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                        goto nla_put_failure;
        }
 
+       if (nla_put(skb, NFTA_SET_USERDATA, set->udlen, set->udata))
+               goto nla_put_failure;
+
        desc = nla_nest_start(skb, NFTA_SET_DESC);
        if (desc == NULL)
                goto nla_put_failure;
@@ -2613,11 +2606,10 @@ static int nf_tables_dump_sets_done(struct netlink_callback *cb)
        return 0;
 }
 
-static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
-                           const struct nlmsghdr *nlh,
+static int nf_tables_getset(struct net *net, struct sock *nlsk,
+                           struct sk_buff *skb, const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
 {
-       struct net *net = sock_net(skb->sk);
        const struct nft_set *set;
        struct nft_ctx ctx;
        struct sk_buff *skb2;
@@ -2704,6 +2696,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        u64 timeout;
        u32 ktype, dtype, flags, policy, gc_int;
        struct nft_set_desc desc;
+       unsigned char *udata;
+       u16 udlen;
        int err;
 
        if (nla[NFTA_SET_TABLE] == NULL ||
@@ -2816,12 +2810,16 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        if (IS_ERR(ops))
                return PTR_ERR(ops);
 
+       udlen = 0;
+       if (nla[NFTA_SET_USERDATA])
+               udlen = nla_len(nla[NFTA_SET_USERDATA]);
+
        size = 0;
        if (ops->privsize != NULL)
                size = ops->privsize(nla);
 
        err = -ENOMEM;
-       set = kzalloc(sizeof(*set) + size, GFP_KERNEL);
+       set = kzalloc(sizeof(*set) + size + udlen, GFP_KERNEL);
        if (set == NULL)
                goto err1;
 
@@ -2830,6 +2828,12 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        if (err < 0)
                goto err2;
 
+       udata = NULL;
+       if (udlen) {
+               udata = set->data + size;
+               nla_memcpy(udata, nla[NFTA_SET_USERDATA], udlen);
+       }
+
        INIT_LIST_HEAD(&set->bindings);
        write_pnet(&set->pnet, net);
        set->ops   = ops;
@@ -2840,6 +2844,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        set->flags = flags;
        set->size  = desc.size;
        set->policy = policy;
+       set->udlen  = udlen;
+       set->udata  = udata;
        set->timeout = timeout;
        set->gc_int = gc_int;
 
@@ -2897,8 +2903,6 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk,
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
        if (IS_ERR(set))
                return PTR_ERR(set);
-       if (set->flags & NFT_SET_INACTIVE)
-               return -ENOENT;
        if (!list_empty(&set->bindings))
                return -EBUSY;
 
@@ -3021,8 +3025,7 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
 static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
                                      const struct sk_buff *skb,
                                      const struct nlmsghdr *nlh,
-                                     const struct nlattr * const nla[],
-                                     bool trans)
+                                     const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        struct nft_af_info *afi;
@@ -3035,8 +3038,6 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
        table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
-       if (!trans && (table->flags & NFT_TABLE_INACTIVE))
-               return -ENOENT;
 
        nft_ctx_init(ctx, net, skb, nlh, afi, table, NULL, nla);
        return 0;
@@ -3145,9 +3146,11 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
                return err;
 
        err = nft_ctx_init_from_elemattr(&ctx, net, cb->skb, cb->nlh,
-                                        (void *)nla, false);
+                                        (void *)nla);
        if (err < 0)
                return err;
+       if (ctx.table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
        if (IS_ERR(set))
@@ -3202,18 +3205,19 @@ nla_put_failure:
        return -ENOSPC;
 }
 
-static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
-                               const struct nlmsghdr *nlh,
+static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
+                               struct sk_buff *skb, const struct nlmsghdr *nlh,
                                const struct nlattr * const nla[])
 {
-       struct net *net = sock_net(skb->sk);
        const struct nft_set *set;
        struct nft_ctx ctx;
        int err;
 
-       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, false);
+       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
        if (err < 0)
                return err;
+       if (ctx.table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
        if (IS_ERR(set))
@@ -3535,7 +3539,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
        if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
                return -EINVAL;
 
-       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, true);
+       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
        if (err < 0)
                return err;
 
@@ -3629,7 +3633,7 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
        if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
                return -EINVAL;
 
-       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, false);
+       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
        if (err < 0)
                return err;
 
@@ -3733,11 +3737,10 @@ err:
        return err;
 }
 
-static int nf_tables_getgen(struct sock *nlsk, struct sk_buff *skb,
-                           const struct nlmsghdr *nlh,
+static int nf_tables_getgen(struct net *net, struct sock *nlsk,
+                           struct sk_buff *skb, const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
 {
-       struct net *net = sock_net(skb->sk);
        struct sk_buff *skb2;
        int err;
 
@@ -3881,9 +3884,8 @@ static void nf_tables_commit_release(struct nft_trans *trans)
        kfree(trans);
 }
 
-static int nf_tables_commit(struct sk_buff *skb)
+static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 {
-       struct net *net = sock_net(skb->sk);
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
 
@@ -4018,9 +4020,8 @@ static void nf_tables_abort_release(struct nft_trans *trans)
        kfree(trans);
 }
 
-static int nf_tables_abort(struct sk_buff *skb)
+static int nf_tables_abort(struct net *net, struct sk_buff *skb)
 {
-       struct net *net = sock_net(skb->sk);
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
 
@@ -4441,22 +4442,22 @@ static void nft_verdict_uninit(const struct nft_data *data)
        }
 }
 
-static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
+int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v)
 {
        struct nlattr *nest;
 
-       nest = nla_nest_start(skb, NFTA_DATA_VERDICT);
+       nest = nla_nest_start(skb, type);
        if (!nest)
                goto nla_put_failure;
 
-       if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict.code)))
+       if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(v->code)))
                goto nla_put_failure;
 
-       switch (data->verdict.code) {
+       switch (v->code) {
        case NFT_JUMP:
        case NFT_GOTO:
                if (nla_put_string(skb, NFTA_VERDICT_CHAIN,
-                                  data->verdict.chain->name))
+                                  v->chain->name))
                        goto nla_put_failure;
        }
        nla_nest_end(skb, nest);
@@ -4567,7 +4568,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
                err = nft_value_dump(skb, data, len);
                break;
        case NFT_DATA_VERDICT:
-               err = nft_verdict_dump(skb, data);
+               err = nft_verdict_dump(skb, NFTA_DATA_VERDICT, &data->verdict);
                break;
        default:
                err = -EINVAL;
@@ -4579,7 +4580,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
 }
 EXPORT_SYMBOL_GPL(nft_data_dump);
 
-static int nf_tables_init_net(struct net *net)
+static int __net_init nf_tables_init_net(struct net *net)
 {
        INIT_LIST_HEAD(&net->nft.af_info);
        INIT_LIST_HEAD(&net->nft.commit_list);
@@ -4587,6 +4588,67 @@ static int nf_tables_init_net(struct net *net)
        return 0;
 }
 
+int __nft_release_basechain(struct nft_ctx *ctx)
+{
+       struct nft_rule *rule, *nr;
+
+       BUG_ON(!(ctx->chain->flags & NFT_BASE_CHAIN));
+
+       nf_tables_unregister_hooks(ctx->chain->table, ctx->chain,
+                                  ctx->afi->nops);
+       list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
+               list_del(&rule->list);
+               ctx->chain->use--;
+               nf_tables_rule_destroy(ctx, rule);
+       }
+       list_del(&ctx->chain->list);
+       ctx->table->use--;
+       nf_tables_chain_destroy(ctx->chain);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__nft_release_basechain);
+
+/* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
+{
+       struct nft_table *table, *nt;
+       struct nft_chain *chain, *nc;
+       struct nft_rule *rule, *nr;
+       struct nft_set *set, *ns;
+       struct nft_ctx ctx = {
+               .net    = net,
+               .afi    = afi,
+       };
+
+       list_for_each_entry_safe(table, nt, &afi->tables, list) {
+               list_for_each_entry(chain, &table->chains, list)
+                       nf_tables_unregister_hooks(table, chain, afi->nops);
+               /* No packets are walking on these chains anymore. */
+               ctx.table = table;
+               list_for_each_entry(chain, &table->chains, list) {
+                       ctx.chain = chain;
+                       list_for_each_entry_safe(rule, nr, &chain->rules, list) {
+                               list_del(&rule->list);
+                               chain->use--;
+                               nf_tables_rule_destroy(&ctx, rule);
+                       }
+               }
+               list_for_each_entry_safe(set, ns, &table->sets, list) {
+                       list_del(&set->list);
+                       table->use--;
+                       nft_set_destroy(set);
+               }
+               list_for_each_entry_safe(chain, nc, &table->chains, list) {
+                       list_del(&chain->list);
+                       table->use--;
+                       nf_tables_chain_destroy(chain);
+               }
+               list_del(&table->list);
+               nf_tables_table_destroy(&ctx);
+       }
+}
+
 static struct pernet_operations nf_tables_net_ops = {
        .init   = nf_tables_init_net,
 };