netfilter: nf_tables: switch registers to 32 bit addressing
[linux-block.git] / net / netfilter / nf_tables_api.c
index 0b969b66cb77521b35a00f28888a6040f9f4599e..03faf76ce3b868a17d16db9d038593349633f1d6 100644 (file)
@@ -198,36 +198,31 @@ static int nft_delchain(struct nft_ctx *ctx)
 static inline bool
 nft_rule_is_active(struct net *net, const struct nft_rule *rule)
 {
-       return (rule->genmask & (1 << net->nft.gencursor)) == 0;
-}
-
-static inline int gencursor_next(struct net *net)
-{
-       return net->nft.gencursor+1 == 1 ? 1 : 0;
+       return (rule->genmask & nft_genmask_cur(net)) == 0;
 }
 
 static inline int
 nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
 {
-       return (rule->genmask & (1 << gencursor_next(net))) == 0;
+       return (rule->genmask & nft_genmask_next(net)) == 0;
 }
 
 static inline void
 nft_rule_activate_next(struct net *net, struct nft_rule *rule)
 {
        /* Now inactive, will be active in the future */
-       rule->genmask = (1 << net->nft.gencursor);
+       rule->genmask = nft_genmask_cur(net);
 }
 
 static inline void
 nft_rule_deactivate_next(struct net *net, struct nft_rule *rule)
 {
-       rule->genmask = (1 << gencursor_next(net));
+       rule->genmask = nft_genmask_next(net);
 }
 
 static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
 {
-       rule->genmask &= ~(1 << gencursor_next(net));
+       rule->genmask &= ~nft_genmask_next(net);
 }
 
 static int
@@ -2164,7 +2159,7 @@ 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;
+               features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT;
        }
 
        bops       = NULL;
@@ -2221,6 +2216,8 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
        [NFTA_SET_POLICY]               = { .type = NLA_U32 },
        [NFTA_SET_DESC]                 = { .type = NLA_NESTED },
        [NFTA_SET_ID]                   = { .type = NLA_U32 },
+       [NFTA_SET_TIMEOUT]              = { .type = NLA_U64 },
+       [NFTA_SET_GC_INTERVAL]          = { .type = NLA_U32 },
 };
 
 static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
@@ -2371,6 +2368,13 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                        goto nla_put_failure;
        }
 
+       if (set->timeout &&
+           nla_put_be64(skb, NFTA_SET_TIMEOUT, cpu_to_be64(set->timeout)))
+               goto nla_put_failure;
+       if (set->gc_int &&
+           nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(set->gc_int)))
+               goto nla_put_failure;
+
        if (set->policy != NFT_SET_POL_PERFORMANCE) {
                if (nla_put_be32(skb, NFTA_SET_POLICY, htonl(set->policy)))
                        goto nla_put_failure;
@@ -2583,7 +2587,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        char name[IFNAMSIZ];
        unsigned int size;
        bool create;
-       u32 ktype, dtype, flags, policy;
+       u64 timeout;
+       u32 ktype, dtype, flags, policy, gc_int;
        struct nft_set_desc desc;
        int err;
 
@@ -2610,7 +2615,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        if (nla[NFTA_SET_FLAGS] != NULL) {
                flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
                if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
-                             NFT_SET_INTERVAL | NFT_SET_MAP))
+                             NFT_SET_INTERVAL | NFT_SET_MAP |
+                             NFT_SET_TIMEOUT))
                        return -EINVAL;
        }
 
@@ -2636,6 +2642,19 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        } else if (flags & NFT_SET_MAP)
                return -EINVAL;
 
+       timeout = 0;
+       if (nla[NFTA_SET_TIMEOUT] != NULL) {
+               if (!(flags & NFT_SET_TIMEOUT))
+                       return -EINVAL;
+               timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_TIMEOUT]));
+       }
+       gc_int = 0;
+       if (nla[NFTA_SET_GC_INTERVAL] != NULL) {
+               if (!(flags & NFT_SET_TIMEOUT))
+                       return -EINVAL;
+               gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL]));
+       }
+
        policy = NFT_SET_POL_PERFORMANCE;
        if (nla[NFTA_SET_POLICY] != NULL)
                policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
@@ -2695,6 +2714,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                goto err2;
 
        INIT_LIST_HEAD(&set->bindings);
+       write_pnet(&set->pnet, net);
        set->ops   = ops;
        set->ktype = ktype;
        set->klen  = desc.klen;
@@ -2703,6 +2723,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        set->flags = flags;
        set->size  = desc.size;
        set->policy = policy;
+       set->timeout = timeout;
+       set->gc_int = gc_int;
 
        err = ops->init(set, &desc, nla);
        if (err < 0)
@@ -2771,12 +2793,14 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
                                        const struct nft_set_iter *iter,
                                        const struct nft_set_elem *elem)
 {
+       const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        enum nft_registers dreg;
 
        dreg = nft_type_to_reg(set->dtype);
-       return nft_validate_data_load(ctx, dreg, &elem->data,
-                                     set->dtype == NFT_DATA_VERDICT ?
-                                     NFT_DATA_VERDICT : NFT_DATA_VALUE);
+       return nft_validate_register_store(ctx, dreg, nft_set_ext_data(ext),
+                                          set->dtype == NFT_DATA_VERDICT ?
+                                          NFT_DATA_VERDICT : NFT_DATA_VALUE,
+                                          set->dlen);
 }
 
 int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
@@ -2788,12 +2812,13 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
        if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
                return -EBUSY;
 
-       if (set->flags & NFT_SET_MAP) {
+       if (binding->flags & NFT_SET_MAP) {
                /* If the set is already bound to the same chain all
                 * jumps are already validated for that chain.
                 */
                list_for_each_entry(i, &set->bindings, list) {
-                       if (i->chain == binding->chain)
+                       if (binding->flags & NFT_SET_MAP &&
+                           i->chain == binding->chain)
                                goto bind;
                }
 
@@ -2827,6 +2852,34 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
                nf_tables_set_destroy(ctx, set);
 }
 
+const struct nft_set_ext_type nft_set_ext_types[] = {
+       [NFT_SET_EXT_KEY]               = {
+               .len    = sizeof(struct nft_data),
+               .align  = __alignof__(struct nft_data),
+       },
+       [NFT_SET_EXT_DATA]              = {
+               .len    = sizeof(struct nft_data),
+               .align  = __alignof__(struct nft_data),
+       },
+       [NFT_SET_EXT_FLAGS]             = {
+               .len    = sizeof(u8),
+               .align  = __alignof__(u8),
+       },
+       [NFT_SET_EXT_TIMEOUT]           = {
+               .len    = sizeof(u64),
+               .align  = __alignof__(u64),
+       },
+       [NFT_SET_EXT_EXPIRATION]        = {
+               .len    = sizeof(unsigned long),
+               .align  = __alignof__(unsigned long),
+       },
+       [NFT_SET_EXT_USERDATA]          = {
+               .len    = sizeof(struct nft_userdata),
+               .align  = __alignof__(struct nft_userdata),
+       },
+};
+EXPORT_SYMBOL_GPL(nft_set_ext_types);
+
 /*
  * Set elements
  */
@@ -2835,6 +2888,9 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
        [NFTA_SET_ELEM_KEY]             = { .type = NLA_NESTED },
        [NFTA_SET_ELEM_DATA]            = { .type = NLA_NESTED },
        [NFTA_SET_ELEM_FLAGS]           = { .type = NLA_U32 },
+       [NFTA_SET_ELEM_TIMEOUT]         = { .type = NLA_U64 },
+       [NFTA_SET_ELEM_USERDATA]        = { .type = NLA_BINARY,
+                                           .len = NFT_USERDATA_MAXLEN },
 };
 
 static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
@@ -2873,6 +2929,7 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                                  const struct nft_set *set,
                                  const struct nft_set_elem *elem)
 {
+       const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
 
@@ -2880,20 +2937,48 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
        if (nest == NULL)
                goto nla_put_failure;
 
-       if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE,
-                         set->klen) < 0)
+       if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext),
+                         NFT_DATA_VALUE, set->klen) < 0)
                goto nla_put_failure;
 
-       if (set->flags & NFT_SET_MAP &&
-           !(elem->flags & NFT_SET_ELEM_INTERVAL_END) &&
-           nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data,
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
+           nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext),
                          set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
                          set->dlen) < 0)
                goto nla_put_failure;
 
-       if (elem->flags != 0)
-               if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags)))
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
+           nla_put_be32(skb, NFTA_SET_ELEM_FLAGS,
+                        htonl(*nft_set_ext_flags(ext))))
+               goto nla_put_failure;
+
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
+           nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
+                        cpu_to_be64(*nft_set_ext_timeout(ext))))
+               goto nla_put_failure;
+
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
+               unsigned long expires, now = jiffies;
+
+               expires = *nft_set_ext_expiration(ext);
+               if (time_before(now, expires))
+                       expires -= now;
+               else
+                       expires = 0;
+
+               if (nla_put_be64(skb, NFTA_SET_ELEM_EXPIRATION,
+                                cpu_to_be64(jiffies_to_msecs(expires))))
+                       goto nla_put_failure;
+       }
+
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_USERDATA)) {
+               struct nft_userdata *udata;
+
+               udata = nft_set_ext_userdata(ext);
+               if (nla_put(skb, NFTA_SET_ELEM_USERDATA,
+                           udata->len + 1, udata->data))
                        goto nla_put_failure;
+       }
 
        nla_nest_end(skb, nest);
        return 0;
@@ -3114,20 +3199,63 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
        return trans;
 }
 
+void *nft_set_elem_init(const struct nft_set *set,
+                       const struct nft_set_ext_tmpl *tmpl,
+                       const u32 *key, const u32 *data,
+                       u64 timeout, gfp_t gfp)
+{
+       struct nft_set_ext *ext;
+       void *elem;
+
+       elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
+       if (elem == NULL)
+               return NULL;
+
+       ext = nft_set_elem_ext(set, elem);
+       nft_set_ext_init(ext, tmpl);
+
+       memcpy(nft_set_ext_key(ext), key, set->klen);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
+               memcpy(nft_set_ext_data(ext), data, set->dlen);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION))
+               *nft_set_ext_expiration(ext) =
+                       jiffies + msecs_to_jiffies(timeout);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT))
+               *nft_set_ext_timeout(ext) = timeout;
+
+       return elem;
+}
+
+void nft_set_elem_destroy(const struct nft_set *set, void *elem)
+{
+       struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
+
+       nft_data_uninit(nft_set_ext_key(ext), NFT_DATA_VALUE);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
+               nft_data_uninit(nft_set_ext_data(ext), set->dtype);
+
+       kfree(elem);
+}
+EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
+
 static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        struct nft_data_desc d1, d2;
+       struct nft_set_ext_tmpl tmpl;
+       struct nft_set_ext *ext;
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
+       struct nft_userdata *udata;
+       struct nft_data data;
        enum nft_registers dreg;
        struct nft_trans *trans;
+       u64 timeout;
+       u32 flags;
+       u8 ulen;
        int err;
 
-       if (set->size && set->nelems == set->size)
-               return -ENFILE;
-
        err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
                               nft_set_elem_policy);
        if (err < 0)
@@ -3136,28 +3264,41 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (nla[NFTA_SET_ELEM_KEY] == NULL)
                return -EINVAL;
 
-       elem.flags = 0;
+       nft_set_ext_prepare(&tmpl);
+
+       flags = 0;
        if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
-               elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
-               if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END)
+               flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
+               if (flags & ~NFT_SET_ELEM_INTERVAL_END)
                        return -EINVAL;
                if (!(set->flags & NFT_SET_INTERVAL) &&
-                   elem.flags & NFT_SET_ELEM_INTERVAL_END)
+                   flags & NFT_SET_ELEM_INTERVAL_END)
                        return -EINVAL;
+               if (flags != 0)
+                       nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
        }
 
        if (set->flags & NFT_SET_MAP) {
                if (nla[NFTA_SET_ELEM_DATA] == NULL &&
-                   !(elem.flags & NFT_SET_ELEM_INTERVAL_END))
+                   !(flags & NFT_SET_ELEM_INTERVAL_END))
                        return -EINVAL;
                if (nla[NFTA_SET_ELEM_DATA] != NULL &&
-                   elem.flags & NFT_SET_ELEM_INTERVAL_END)
+                   flags & NFT_SET_ELEM_INTERVAL_END)
                        return -EINVAL;
        } else {
                if (nla[NFTA_SET_ELEM_DATA] != NULL)
                        return -EINVAL;
        }
 
+       timeout = 0;
+       if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) {
+               if (!(set->flags & NFT_SET_TIMEOUT))
+                       return -EINVAL;
+               timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_ELEM_TIMEOUT]));
+       } else if (set->flags & NFT_SET_TIMEOUT) {
+               timeout = set->timeout;
+       }
+
        err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
                goto err1;
@@ -3165,12 +3306,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
                goto err2;
 
-       err = -EEXIST;
-       if (set->ops->get(set, &elem) == 0)
-               goto err2;
+       nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY);
+       if (timeout > 0) {
+               nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
+               if (timeout != set->timeout)
+                       nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
+       }
 
        if (nla[NFTA_SET_ELEM_DATA] != NULL) {
-               err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]);
+               err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]);
                if (err < 0)
                        goto err2;
 
@@ -3186,30 +3330,66 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                                .chain  = (struct nft_chain *)binding->chain,
                        };
 
-                       err = nft_validate_data_load(&bind_ctx, dreg,
-                                                    &elem.data, d2.type);
+                       if (!(binding->flags & NFT_SET_MAP))
+                               continue;
+
+                       err = nft_validate_register_store(&bind_ctx, dreg,
+                                                         &data,
+                                                         d2.type, d2.len);
                        if (err < 0)
                                goto err3;
                }
+
+               nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA);
+       }
+
+       /* The full maximum length of userdata can exceed the maximum
+        * offset value (U8_MAX) for following extensions, therefor it
+        * must be the last extension added.
+        */
+       ulen = 0;
+       if (nla[NFTA_SET_ELEM_USERDATA] != NULL) {
+               ulen = nla_len(nla[NFTA_SET_ELEM_USERDATA]);
+               if (ulen > 0)
+                       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_USERDATA,
+                                              ulen);
+       }
+
+       err = -ENOMEM;
+       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.data, data.data,
+                                     timeout, GFP_KERNEL);
+       if (elem.priv == NULL)
+               goto err3;
+
+       ext = nft_set_elem_ext(set, elem.priv);
+       if (flags)
+               *nft_set_ext_flags(ext) = flags;
+       if (ulen > 0) {
+               udata = nft_set_ext_userdata(ext);
+               udata->len = ulen - 1;
+               nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
        }
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
        if (trans == NULL)
-               goto err3;
+               goto err4;
 
+       ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
        err = set->ops->insert(set, &elem);
        if (err < 0)
-               goto err4;
+               goto err5;
 
        nft_trans_elem(trans) = elem;
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
-err4:
+err5:
        kfree(trans);
+err4:
+       kfree(elem.priv);
 err3:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
-               nft_data_uninit(&elem.data, d2.type);
+               nft_data_uninit(&data, d2.type);
 err2:
        nft_data_uninit(&elem.key, d1.type);
 err1:
@@ -3247,11 +3427,15 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
                return -EBUSY;
 
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
+               if (set->size &&
+                   !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact))
+                       return -ENFILE;
+
                err = nft_add_set_elem(&ctx, set, attr);
-               if (err < 0)
+               if (err < 0) {
+                       atomic_dec(&set->nelems);
                        break;
-
-               set->nelems++;
+               }
        }
        return err;
 }
@@ -3282,19 +3466,24 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
                goto err2;
 
-       err = set->ops->get(set, &elem);
-       if (err < 0)
-               goto err2;
-
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
        if (trans == NULL) {
                err = -ENOMEM;
                goto err2;
        }
 
+       elem.priv = set->ops->deactivate(set, &elem);
+       if (elem.priv == NULL) {
+               err = -ENOENT;
+               goto err3;
+       }
+
        nft_trans_elem(trans) = elem;
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
+
+err3:
+       kfree(trans);
 err2:
        nft_data_uninit(&elem.key, desc.type);
 err1:
@@ -3328,11 +3517,36 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
                if (err < 0)
                        break;
 
-               set->nelems--;
+               set->ndeact++;
        }
        return err;
 }
 
+void nft_set_gc_batch_release(struct rcu_head *rcu)
+{
+       struct nft_set_gc_batch *gcb;
+       unsigned int i;
+
+       gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu);
+       for (i = 0; i < gcb->head.cnt; i++)
+               nft_set_elem_destroy(gcb->head.set, gcb->elems[i]);
+       kfree(gcb);
+}
+EXPORT_SYMBOL_GPL(nft_set_gc_batch_release);
+
+struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set,
+                                               gfp_t gfp)
+{
+       struct nft_set_gc_batch *gcb;
+
+       gcb = kzalloc(sizeof(*gcb), gfp);
+       if (gcb == NULL)
+               return gcb;
+       gcb->head.set = set;
+       return gcb;
+}
+EXPORT_SYMBOL_GPL(nft_set_gc_batch_alloc);
+
 static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
                                   u32 portid, u32 seq)
 {
@@ -3532,6 +3746,10 @@ static void nf_tables_commit_release(struct nft_trans *trans)
        case NFT_MSG_DELSET:
                nft_set_destroy(nft_trans_set(trans));
                break;
+       case NFT_MSG_DELSETELEM:
+               nft_set_elem_destroy(nft_trans_elem_set(trans),
+                                    nft_trans_elem(trans).priv);
+               break;
        }
        kfree(trans);
 }
@@ -3546,7 +3764,7 @@ static int nf_tables_commit(struct sk_buff *skb)
        while (++net->nft.base_seq == 0);
 
        /* A new generation has just started */
-       net->nft.gencursor = gencursor_next(net);
+       net->nft.gencursor = nft_gencursor_next(net);
 
        /* Make sure all packets have left the previous generation before
         * purging old rules.
@@ -3617,24 +3835,23 @@ static int nf_tables_commit(struct sk_buff *skb)
                                             NFT_MSG_DELSET, GFP_KERNEL);
                        break;
                case NFT_MSG_NEWSETELEM:
-                       nf_tables_setelem_notify(&trans->ctx,
-                                                nft_trans_elem_set(trans),
-                                                &nft_trans_elem(trans),
+                       te = (struct nft_trans_elem *)trans->data;
+
+                       te->set->ops->activate(te->set, &te->elem);
+                       nf_tables_setelem_notify(&trans->ctx, te->set,
+                                                &te->elem,
                                                 NFT_MSG_NEWSETELEM, 0);
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM:
                        te = (struct nft_trans_elem *)trans->data;
+
                        nf_tables_setelem_notify(&trans->ctx, te->set,
                                                 &te->elem,
                                                 NFT_MSG_DELSETELEM, 0);
-                       te->set->ops->get(te->set, &te->elem);
-                       nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
-                       if (te->set->flags & NFT_SET_MAP &&
-                           !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
-                               nft_data_uninit(&te->elem.data, te->set->dtype);
                        te->set->ops->remove(te->set, &te->elem);
-                       nft_trans_destroy(trans);
+                       atomic_dec(&te->set->nelems);
+                       te->set->ndeact--;
                        break;
                }
        }
@@ -3666,6 +3883,10 @@ static void nf_tables_abort_release(struct nft_trans *trans)
        case NFT_MSG_NEWSET:
                nft_set_destroy(nft_trans_set(trans));
                break;
+       case NFT_MSG_NEWSETELEM:
+               nft_set_elem_destroy(nft_trans_elem_set(trans),
+                                    nft_trans_elem(trans).priv);
+               break;
        }
        kfree(trans);
 }
@@ -3734,18 +3955,17 @@ static int nf_tables_abort(struct sk_buff *skb)
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSETELEM:
-                       nft_trans_elem_set(trans)->nelems--;
                        te = (struct nft_trans_elem *)trans->data;
-                       te->set->ops->get(te->set, &te->elem);
-                       nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
-                       if (te->set->flags & NFT_SET_MAP &&
-                           !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
-                               nft_data_uninit(&te->elem.data, te->set->dtype);
+
                        te->set->ops->remove(te->set, &te->elem);
-                       nft_trans_destroy(trans);
+                       atomic_dec(&te->set->nelems);
                        break;
                case NFT_MSG_DELSETELEM:
-                       nft_trans_elem_set(trans)->nelems++;
+                       te = (struct nft_trans_elem *)trans->data;
+
+                       te->set->ops->activate(te->set, &te->elem);
+                       te->set->ndeact--;
+
                        nft_trans_destroy(trans);
                        break;
                }
@@ -3820,13 +4040,18 @@ static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
                                        const struct nft_set_iter *iter,
                                        const struct nft_set_elem *elem)
 {
-       if (elem->flags & NFT_SET_ELEM_INTERVAL_END)
+       const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
+       const struct nft_data *data;
+
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
+           *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
                return 0;
 
-       switch (elem->data.verdict) {
+       data = nft_set_ext_data(ext);
+       switch (data->verdict.code) {
        case NFT_JUMP:
        case NFT_GOTO:
-               return nf_tables_check_loops(ctx, elem->data.chain);
+               return nf_tables_check_loops(ctx, data->verdict.chain);
        default:
                return 0;
        }
@@ -3859,10 +4084,11 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
                        if (data == NULL)
                                continue;
 
-                       switch (data->verdict) {
+                       switch (data->verdict.code) {
                        case NFT_JUMP:
                        case NFT_GOTO:
-                               err = nf_tables_check_loops(ctx, data->chain);
+                               err = nf_tables_check_loops(ctx,
+                                                       data->verdict.chain);
                                if (err < 0)
                                        return err;
                        default:
@@ -3877,7 +4103,8 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
                        continue;
 
                list_for_each_entry(binding, &set->bindings, list) {
-                       if (binding->chain != chain)
+                       if (!(binding->flags & NFT_SET_MAP) ||
+                           binding->chain != chain)
                                continue;
 
                        iter.skip       = 0;
@@ -3895,85 +4122,129 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
 }
 
 /**
- *     nft_validate_input_register - validate an expressions' input register
+ *     nft_parse_register - parse a register value from a netlink attribute
  *
- *     @reg: the register number
+ *     @attr: netlink attribute
  *
- *     Validate that the input register is one of the general purpose
- *     registers.
+ *     Parse and translate a register value from a netlink attribute.
+ *     Registers used to be 128 bit wide, these register numbers will be
+ *     mapped to the corresponding 32 bit register numbers.
  */
-int nft_validate_input_register(enum nft_registers reg)
+unsigned int nft_parse_register(const struct nlattr *attr)
 {
-       if (reg <= NFT_REG_VERDICT)
-               return -EINVAL;
-       if (reg > NFT_REG_MAX)
-               return -ERANGE;
-       return 0;
+       unsigned int reg;
+
+       reg = ntohl(nla_get_be32(attr));
+       switch (reg) {
+       case NFT_REG_VERDICT...NFT_REG_4:
+               return reg * NFT_REG_SIZE / NFT_REG32_SIZE;
+       default:
+               return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
+       }
 }
-EXPORT_SYMBOL_GPL(nft_validate_input_register);
+EXPORT_SYMBOL_GPL(nft_parse_register);
 
 /**
- *     nft_validate_output_register - validate an expressions' output register
+ *     nft_dump_register - dump a register value to a netlink attribute
+ *
+ *     @skb: socket buffer
+ *     @attr: attribute number
+ *     @reg: register number
+ *
+ *     Construct a netlink attribute containing the register number. For
+ *     compatibility reasons, register numbers being a multiple of 4 are
+ *     translated to the corresponding 128 bit register numbers.
+ */
+int nft_dump_register(struct sk_buff *skb, unsigned int attr, unsigned int reg)
+{
+       if (reg % (NFT_REG_SIZE / NFT_REG32_SIZE) == 0)
+               reg = reg / (NFT_REG_SIZE / NFT_REG32_SIZE);
+       else
+               reg = reg - NFT_REG_SIZE / NFT_REG32_SIZE + NFT_REG32_00;
+
+       return nla_put_be32(skb, attr, htonl(reg));
+}
+EXPORT_SYMBOL_GPL(nft_dump_register);
+
+/**
+ *     nft_validate_register_load - validate a load from a register
  *
  *     @reg: the register number
+ *     @len: the length of the data
  *
- *     Validate that the output register is one of the general purpose
- *     registers or the verdict register.
+ *     Validate that the input register is one of the general purpose
+ *     registers and that the length of the load is within the bounds.
  */
-int nft_validate_output_register(enum nft_registers reg)
+int nft_validate_register_load(enum nft_registers reg, unsigned int len)
 {
-       if (reg < NFT_REG_VERDICT)
+       if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE)
+               return -EINVAL;
+       if (len == 0)
                return -EINVAL;
-       if (reg > NFT_REG_MAX)
+       if (reg * NFT_REG32_SIZE + len > FIELD_SIZEOF(struct nft_regs, data))
                return -ERANGE;
+
        return 0;
 }
-EXPORT_SYMBOL_GPL(nft_validate_output_register);
+EXPORT_SYMBOL_GPL(nft_validate_register_load);
 
 /**
- *     nft_validate_data_load - validate an expressions' data load
+ *     nft_validate_register_store - validate an expressions' register store
  *
  *     @ctx: context of the expression performing the load
  *     @reg: the destination register number
  *     @data: the data to load
  *     @type: the data type
+ *     @len: the length of the data
  *
  *     Validate that a data load uses the appropriate data type for
- *     the destination register. A value of NULL for the data means
- *     that its runtime gathered data, which is always of type
- *     NFT_DATA_VALUE.
+ *     the destination register and the length is within the bounds.
+ *     A value of NULL for the data means that its runtime gathered
+ *     data.
  */
-int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg,
-                          const struct nft_data *data,
-                          enum nft_data_types type)
+int nft_validate_register_store(const struct nft_ctx *ctx,
+                               enum nft_registers reg,
+                               const struct nft_data *data,
+                               enum nft_data_types type, unsigned int len)
 {
        int err;
 
        switch (reg) {
        case NFT_REG_VERDICT:
-               if (data == NULL || type != NFT_DATA_VERDICT)
+               if (type != NFT_DATA_VERDICT)
                        return -EINVAL;
 
-               if (data->verdict == NFT_GOTO || data->verdict == NFT_JUMP) {
-                       err = nf_tables_check_loops(ctx, data->chain);
+               if (data != NULL &&
+                   (data->verdict.code == NFT_GOTO ||
+                    data->verdict.code == NFT_JUMP)) {
+                       err = nf_tables_check_loops(ctx, data->verdict.chain);
                        if (err < 0)
                                return err;
 
-                       if (ctx->chain->level + 1 > data->chain->level) {
+                       if (ctx->chain->level + 1 >
+                           data->verdict.chain->level) {
                                if (ctx->chain->level + 1 == NFT_JUMP_STACK_SIZE)
                                        return -EMLINK;
-                               data->chain->level = ctx->chain->level + 1;
+                               data->verdict.chain->level = ctx->chain->level + 1;
                        }
                }
 
                return 0;
        default:
+               if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE)
+                       return -EINVAL;
+               if (len == 0)
+                       return -EINVAL;
+               if (reg * NFT_REG32_SIZE + len >
+                   FIELD_SIZEOF(struct nft_regs, data))
+                       return -ERANGE;
+
                if (data != NULL && type != NFT_DATA_VALUE)
                        return -EINVAL;
                return 0;
        }
 }
-EXPORT_SYMBOL_GPL(nft_validate_data_load);
+EXPORT_SYMBOL_GPL(nft_validate_register_store);
 
 static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = {
        [NFTA_VERDICT_CODE]     = { .type = NLA_U32 },
@@ -3994,11 +4265,11 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
 
        if (!tb[NFTA_VERDICT_CODE])
                return -EINVAL;
-       data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
+       data->verdict.code = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
 
-       switch (data->verdict) {
+       switch (data->verdict.code) {
        default:
-               switch (data->verdict & NF_VERDICT_MASK) {
+               switch (data->verdict.code & NF_VERDICT_MASK) {
                case NF_ACCEPT:
                case NF_DROP:
                case NF_QUEUE:
@@ -4024,7 +4295,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
                        return -EOPNOTSUPP;
 
                chain->use++;
-               data->chain = chain;
+               data->verdict.chain = chain;
                desc->len = sizeof(data);
                break;
        }
@@ -4035,10 +4306,10 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
 
 static void nft_verdict_uninit(const struct nft_data *data)
 {
-       switch (data->verdict) {
+       switch (data->verdict.code) {
        case NFT_JUMP:
        case NFT_GOTO:
-               data->chain->use--;
+               data->verdict.chain->use--;
                break;
        }
 }
@@ -4051,13 +4322,14 @@ static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
        if (!nest)
                goto nla_put_failure;
 
-       if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict)))
+       if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict.code)))
                goto nla_put_failure;
 
-       switch (data->verdict) {
+       switch (data->verdict.code) {
        case NFT_JUMP:
        case NFT_GOTO:
-               if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name))
+               if (nla_put_string(skb, NFTA_VERDICT_CHAIN,
+                                  data->verdict.chain->name))
                        goto nla_put_failure;
        }
        nla_nest_end(skb, nest);