]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/netfilter/nf_tables_api.c
Merge tag 'for-5.6-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
[linux.git] / net / netfilter / nf_tables_api.c
index 65f51a2e9c2a7343b2d0677526932f984eba0684..d1318bdf49ca97bcdc0b4dd9ff209fdb6cb183f2 100644 (file)
@@ -552,48 +552,71 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table)
 
 static const struct nft_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];
 
+static const struct nft_chain_type *
+__nft_chain_type_get(u8 family, enum nft_chain_types type)
+{
+       if (family >= NFPROTO_NUMPROTO ||
+           type >= NFT_CHAIN_T_MAX)
+               return NULL;
+
+       return chain_type[family][type];
+}
+
 static const struct nft_chain_type *
 __nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family)
 {
+       const struct nft_chain_type *type;
        int i;
 
        for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
-               if (chain_type[family][i] != NULL &&
-                   !nla_strcmp(nla, chain_type[family][i]->name))
-                       return chain_type[family][i];
+               type = __nft_chain_type_get(family, i);
+               if (!type)
+                       continue;
+               if (!nla_strcmp(nla, type->name))
+                       return type;
        }
        return NULL;
 }
 
-/*
- * Loading a module requires dropping mutex that guards the transaction.
- * A different client might race to start a new transaction meanwhile. Zap the
- * list of pending transaction and then restore it once the mutex is grabbed
- * again. Users of this function return EAGAIN which implicitly triggers the
- * transaction abort path to clean up the list of pending transactions.
- */
+struct nft_module_request {
+       struct list_head        list;
+       char                    module[MODULE_NAME_LEN];
+       bool                    done;
+};
+
 #ifdef CONFIG_MODULES
-static void nft_request_module(struct net *net, const char *fmt, ...)
+static int nft_request_module(struct net *net, const char *fmt, ...)
 {
        char module_name[MODULE_NAME_LEN];
-       LIST_HEAD(commit_list);
+       struct nft_module_request *req;
        va_list args;
        int ret;
 
-       list_splice_init(&net->nft.commit_list, &commit_list);
-
        va_start(args, fmt);
        ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
        va_end(args);
        if (ret >= MODULE_NAME_LEN)
-               return;
+               return 0;
 
-       mutex_unlock(&net->nft.commit_mutex);
-       request_module("%s", module_name);
-       mutex_lock(&net->nft.commit_mutex);
+       list_for_each_entry(req, &net->nft.module_list, list) {
+               if (!strcmp(req->module, module_name)) {
+                       if (req->done)
+                               return 0;
 
-       WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
-       list_splice(&commit_list, &net->nft.commit_list);
+                       /* A request to load this module already exists. */
+                       return -EAGAIN;
+               }
+       }
+
+       req = kmalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       req->done = false;
+       strlcpy(req->module, module_name, MODULE_NAME_LEN);
+       list_add_tail(&req->list, &net->nft.module_list);
+
+       return -EAGAIN;
 }
 #endif
 
@@ -617,10 +640,9 @@ nf_tables_chain_type_lookup(struct net *net, const struct nlattr *nla,
        lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (autoload) {
-               nft_request_module(net, "nft-chain-%u-%.*s", family,
-                                  nla_len(nla), (const char *)nla_data(nla));
-               type = __nf_tables_chain_type_lookup(nla, family);
-               if (type != NULL)
+               if (nft_request_module(net, "nft-chain-%u-%.*s", family,
+                                      nla_len(nla),
+                                      (const char *)nla_data(nla)) == -EAGAIN)
                        return ERR_PTR(-EAGAIN);
        }
 #endif
@@ -1162,11 +1184,8 @@ static void nf_tables_table_destroy(struct nft_ctx *ctx)
 
 void nft_register_chain_type(const struct nft_chain_type *ctype)
 {
-       if (WARN_ON(ctype->family >= NFPROTO_NUMPROTO))
-               return;
-
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
-       if (WARN_ON(chain_type[ctype->family][ctype->type] != NULL)) {
+       if (WARN_ON(__nft_chain_type_get(ctype->family, ctype->type))) {
                nfnl_unlock(NFNL_SUBSYS_NFTABLES);
                return;
        }
@@ -1768,7 +1787,10 @@ static int nft_chain_parse_hook(struct net *net,
        hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
        hook->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
 
-       type = chain_type[family][NFT_CHAIN_T_DEFAULT];
+       type = __nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);
+       if (!type)
+               return -EOPNOTSUPP;
+
        if (nla[NFTA_CHAIN_TYPE]) {
                type = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE],
                                                   family, autoload);
@@ -2328,9 +2350,8 @@ static const struct nft_expr_type *__nft_expr_type_get(u8 family,
 static int nft_expr_type_request_module(struct net *net, u8 family,
                                        struct nlattr *nla)
 {
-       nft_request_module(net, "nft-expr-%u-%.*s", family,
-                          nla_len(nla), (char *)nla_data(nla));
-       if (__nft_expr_type_get(family, nla))
+       if (nft_request_module(net, "nft-expr-%u-%.*s", family,
+                              nla_len(nla), (char *)nla_data(nla)) == -EAGAIN)
                return -EAGAIN;
 
        return 0;
@@ -2356,9 +2377,9 @@ static const struct nft_expr_type *nft_expr_type_get(struct net *net,
                if (nft_expr_type_request_module(net, family, nla) == -EAGAIN)
                        return ERR_PTR(-EAGAIN);
 
-               nft_request_module(net, "nft-expr-%.*s",
-                                  nla_len(nla), (char *)nla_data(nla));
-               if (__nft_expr_type_get(family, nla))
+               if (nft_request_module(net, "nft-expr-%.*s",
+                                      nla_len(nla),
+                                      (char *)nla_data(nla)) == -EAGAIN)
                        return ERR_PTR(-EAGAIN);
        }
 #endif
@@ -2449,9 +2470,10 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
                        err = PTR_ERR(ops);
 #ifdef CONFIG_MODULES
                        if (err == -EAGAIN)
-                               nft_expr_type_request_module(ctx->net,
-                                                            ctx->family,
-                                                            tb[NFTA_EXPR_NAME]);
+                               if (nft_expr_type_request_module(ctx->net,
+                                                                ctx->family,
+                                                                tb[NFTA_EXPR_NAME]) != -EAGAIN)
+                                       err = -ENOENT;
 #endif
                        goto err1;
                }
@@ -3288,8 +3310,7 @@ nft_select_set_ops(const struct nft_ctx *ctx,
        lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (list_empty(&nf_tables_set_types)) {
-               nft_request_module(ctx->net, "nft-set");
-               if (!list_empty(&nf_tables_set_types))
+               if (nft_request_module(ctx->net, "nft-set") == -EAGAIN)
                        return ERR_PTR(-EAGAIN);
        }
 #endif
@@ -3370,6 +3391,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
 
 static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
        [NFTA_SET_DESC_SIZE]            = { .type = NLA_U32 },
+       [NFTA_SET_DESC_CONCAT]          = { .type = NLA_NESTED },
 };
 
 static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net,
@@ -3536,6 +3558,33 @@ static __be64 nf_jiffies64_to_msecs(u64 input)
        return cpu_to_be64(jiffies64_to_msecs(input));
 }
 
+static int nf_tables_fill_set_concat(struct sk_buff *skb,
+                                    const struct nft_set *set)
+{
+       struct nlattr *concat, *field;
+       int i;
+
+       concat = nla_nest_start_noflag(skb, NFTA_SET_DESC_CONCAT);
+       if (!concat)
+               return -ENOMEM;
+
+       for (i = 0; i < set->field_count; i++) {
+               field = nla_nest_start_noflag(skb, NFTA_LIST_ELEM);
+               if (!field)
+                       return -ENOMEM;
+
+               if (nla_put_be32(skb, NFTA_SET_FIELD_LEN,
+                                htonl(set->field_len[i])))
+                       return -ENOMEM;
+
+               nla_nest_end(skb, field);
+       }
+
+       nla_nest_end(skb, concat);
+
+       return 0;
+}
+
 static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                              const struct nft_set *set, u16 event, u16 flags)
 {
@@ -3599,11 +3648,17 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                goto nla_put_failure;
 
        desc = nla_nest_start_noflag(skb, NFTA_SET_DESC);
+
        if (desc == NULL)
                goto nla_put_failure;
        if (set->size &&
            nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
                goto nla_put_failure;
+
+       if (set->field_count > 1 &&
+           nf_tables_fill_set_concat(skb, set))
+               goto nla_put_failure;
+
        nla_nest_end(skb, desc);
 
        nlmsg_end(skb, nlh);
@@ -3776,6 +3831,53 @@ static int nf_tables_getset(struct net *net, struct sock *nlsk,
        return err;
 }
 
+static const struct nla_policy nft_concat_policy[NFTA_SET_FIELD_MAX + 1] = {
+       [NFTA_SET_FIELD_LEN]    = { .type = NLA_U32 },
+};
+
+static int nft_set_desc_concat_parse(const struct nlattr *attr,
+                                    struct nft_set_desc *desc)
+{
+       struct nlattr *tb[NFTA_SET_FIELD_MAX + 1];
+       u32 len;
+       int err;
+
+       err = nla_parse_nested_deprecated(tb, NFTA_SET_FIELD_MAX, attr,
+                                         nft_concat_policy, NULL);
+       if (err < 0)
+               return err;
+
+       if (!tb[NFTA_SET_FIELD_LEN])
+               return -EINVAL;
+
+       len = ntohl(nla_get_be32(tb[NFTA_SET_FIELD_LEN]));
+
+       if (len * BITS_PER_BYTE / 32 > NFT_REG32_COUNT)
+               return -E2BIG;
+
+       desc->field_len[desc->field_count++] = len;
+
+       return 0;
+}
+
+static int nft_set_desc_concat(struct nft_set_desc *desc,
+                              const struct nlattr *nla)
+{
+       struct nlattr *attr;
+       int rem, err;
+
+       nla_for_each_nested(attr, nla, rem) {
+               if (nla_type(attr) != NFTA_LIST_ELEM)
+                       return -EINVAL;
+
+               err = nft_set_desc_concat_parse(attr, desc);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int nf_tables_set_desc_parse(struct nft_set_desc *desc,
                                    const struct nlattr *nla)
 {
@@ -3789,8 +3891,10 @@ static int nf_tables_set_desc_parse(struct nft_set_desc *desc,
 
        if (da[NFTA_SET_DESC_SIZE] != NULL)
                desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
+       if (da[NFTA_SET_DESC_CONCAT])
+               err = nft_set_desc_concat(desc, da[NFTA_SET_DESC_CONCAT]);
 
-       return 0;
+       return err;
 }
 
 static int nf_tables_newset(struct net *net, struct sock *nlsk,
@@ -3813,6 +3917,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        unsigned char *udata;
        u16 udlen;
        int err;
+       int i;
 
        if (nla[NFTA_SET_TABLE] == NULL ||
            nla[NFTA_SET_NAME] == NULL ||
@@ -3991,6 +4096,10 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        set->gc_int = gc_int;
        set->handle = nf_tables_alloc_handle(table);
 
+       set->field_count = desc.field_count;
+       for (i = 0; i < desc.field_count; i++)
+               set->field_len[i] = desc.field_len[i];
+
        err = ops->init(set, &desc, nla);
        if (err < 0)
                goto err3;
@@ -4194,6 +4303,9 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
                .len    = sizeof(struct nft_userdata),
                .align  = __alignof__(struct nft_userdata),
        },
+       [NFT_SET_EXT_KEY_END]           = {
+               .align  = __alignof__(u32),
+       },
 };
 EXPORT_SYMBOL_GPL(nft_set_ext_types);
 
@@ -4212,6 +4324,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
        [NFTA_SET_ELEM_EXPR]            = { .type = NLA_NESTED },
        [NFTA_SET_ELEM_OBJREF]          = { .type = NLA_STRING,
                                            .len = NFT_OBJ_MAXNAMELEN - 1 },
+       [NFTA_SET_ELEM_KEY_END]         = { .type = NLA_NESTED },
 };
 
 static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
@@ -4261,6 +4374,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                          NFT_DATA_VALUE, set->klen) < 0)
                goto nla_put_failure;
 
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
+           nft_data_dump(skb, NFTA_SET_ELEM_KEY_END, nft_set_ext_key_end(ext),
+                         NFT_DATA_VALUE, set->klen) < 0)
+               goto nla_put_failure;
+
        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,
@@ -4503,11 +4621,28 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
        return 0;
 }
 
+static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
+                                struct nft_data *key, struct nlattr *attr)
+{
+       struct nft_data_desc desc;
+       int err;
+
+       err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr);
+       if (err < 0)
+               return err;
+
+       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
+               nft_data_release(key, desc.type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int nft_get_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 desc;
        struct nft_set_elem elem;
        struct sk_buff *skb;
        uint32_t flags = 0;
@@ -4526,15 +4661,16 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                return err;
 
-       err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &desc,
-                           nla[NFTA_SET_ELEM_KEY]);
+       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
+                                   nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
                return err;
 
-       err = -EINVAL;
-       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
-               nft_data_release(&elem.key.val, desc.type);
-               return err;
+       if (nla[NFTA_SET_ELEM_KEY_END]) {
+               err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
+                                           nla[NFTA_SET_ELEM_KEY_END]);
+               if (err < 0)
+                       return err;
        }
 
        priv = set->ops->get(ctx->net, set, &elem, flags);
@@ -4662,8 +4798,8 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
 
 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, u64 expiration, gfp_t gfp)
+                       const u32 *key, const u32 *key_end,
+                       const u32 *data, u64 timeout, u64 expiration, gfp_t gfp)
 {
        struct nft_set_ext *ext;
        void *elem;
@@ -4676,6 +4812,8 @@ void *nft_set_elem_init(const struct nft_set *set,
        nft_set_ext_init(ext, tmpl);
 
        memcpy(nft_set_ext_key(ext), key, set->klen);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
+               memcpy(nft_set_ext_key_end(ext), key_end, 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)) {
@@ -4735,13 +4873,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        u8 genmask = nft_genmask_next(ctx->net);
-       struct nft_data_desc d1, d2;
        struct nft_set_ext_tmpl tmpl;
        struct nft_set_ext *ext, *ext2;
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
        struct nft_object *obj = NULL;
        struct nft_userdata *udata;
+       struct nft_data_desc desc;
        struct nft_data data;
        enum nft_registers dreg;
        struct nft_trans *trans;
@@ -4807,15 +4945,22 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        return err;
        }
 
-       err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &d1,
-                           nla[NFTA_SET_ELEM_KEY]);
+       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
+                                   nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
-               goto err1;
-       err = -EINVAL;
-       if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
-               goto err2;
+               return err;
+
+       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+
+       if (nla[NFTA_SET_ELEM_KEY_END]) {
+               err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
+                                           nla[NFTA_SET_ELEM_KEY_END]);
+               if (err < 0)
+                       goto err_parse_key;
+
+               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+       }
 
-       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, d1.len);
        if (timeout > 0) {
                nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
                if (timeout != set->timeout)
@@ -4825,27 +4970,27 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
                if (!(set->flags & NFT_SET_OBJECT)) {
                        err = -EINVAL;
-                       goto err2;
+                       goto err_parse_key_end;
                }
                obj = nft_obj_lookup(ctx->net, ctx->table,
                                     nla[NFTA_SET_ELEM_OBJREF],
                                     set->objtype, genmask);
                if (IS_ERR(obj)) {
                        err = PTR_ERR(obj);
-                       goto err2;
+                       goto err_parse_key_end;
                }
                nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
        }
 
        if (nla[NFTA_SET_ELEM_DATA] != NULL) {
-               err = nft_data_init(ctx, &data, sizeof(data), &d2,
+               err = nft_data_init(ctx, &data, sizeof(data), &desc,
                                    nla[NFTA_SET_ELEM_DATA]);
                if (err < 0)
-                       goto err2;
+                       goto err_parse_key_end;
 
                err = -EINVAL;
-               if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen)
-                       goto err3;
+               if (set->dtype != NFT_DATA_VERDICT && desc.len != set->dlen)
+                       goto err_parse_data;
 
                dreg = nft_type_to_reg(set->dtype);
                list_for_each_entry(binding, &set->bindings, list) {
@@ -4861,18 +5006,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 
                        err = nft_validate_register_store(&bind_ctx, dreg,
                                                          &data,
-                                                         d2.type, d2.len);
+                                                         desc.type, desc.len);
                        if (err < 0)
-                               goto err3;
+                               goto err_parse_data;
 
-                       if (d2.type == NFT_DATA_VERDICT &&
+                       if (desc.type == NFT_DATA_VERDICT &&
                            (data.verdict.code == NFT_GOTO ||
                             data.verdict.code == NFT_JUMP))
                                nft_validate_state_update(ctx->net,
                                                          NFT_VALIDATE_NEED);
                }
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, d2.len);
+               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, desc.len);
        }
 
        /* The full maximum length of userdata can exceed the maximum
@@ -4888,10 +5033,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        }
 
        err = -ENOMEM;
-       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, data.data,
+       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
+                                     elem.key_end.val.data, data.data,
                                      timeout, expiration, GFP_KERNEL);
        if (elem.priv == NULL)
-               goto err3;
+               goto err_parse_data;
 
        ext = nft_set_elem_ext(set, elem.priv);
        if (flags)
@@ -4908,7 +5054,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
        if (trans == NULL)
-               goto err4;
+               goto err_trans;
 
        ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
        err = set->ops->insert(ctx->net, set, &elem, &ext2);
@@ -4919,7 +5065,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^
                            nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) {
                                err = -EBUSY;
-                               goto err5;
+                               goto err_element_clash;
                        }
                        if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
                             nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) &&
@@ -4932,33 +5078,35 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        else if (!(nlmsg_flags & NLM_F_EXCL))
                                err = 0;
                }
-               goto err5;
+               goto err_element_clash;
        }
 
        if (set->size &&
            !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) {
                err = -ENFILE;
-               goto err6;
+               goto err_set_full;
        }
 
        nft_trans_elem(trans) = elem;
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
-err6:
+err_set_full:
        set->ops->remove(ctx->net, set, &elem);
-err5:
+err_element_clash:
        kfree(trans);
-err4:
+err_trans:
        if (obj)
                obj->use--;
        kfree(elem.priv);
-err3:
+err_parse_data:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
-               nft_data_release(&data, d2.type);
-err2:
-       nft_data_release(&elem.key.val, d1.type);
-err1:
+               nft_data_release(&data, desc.type);
+err_parse_key_end:
+       nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
+err_parse_key:
+       nft_data_release(&elem.key.val, NFT_DATA_VALUE);
+
        return err;
 }
 
@@ -5053,7 +5201,6 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        struct nft_set_ext_tmpl tmpl;
-       struct nft_data_desc desc;
        struct nft_set_elem elem;
        struct nft_set_ext *ext;
        struct nft_trans *trans;
@@ -5064,11 +5211,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
                                          nft_set_elem_policy, NULL);
        if (err < 0)
-               goto err1;
+               return err;
 
-       err = -EINVAL;
        if (nla[NFTA_SET_ELEM_KEY] == NULL)
-               goto err1;
+               return -EINVAL;
 
        nft_set_ext_prepare(&tmpl);
 
@@ -5078,37 +5224,41 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        if (flags != 0)
                nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
 
-       err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &desc,
-                           nla[NFTA_SET_ELEM_KEY]);
+       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
+                                   nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
-               goto err1;
+               return err;
 
-       err = -EINVAL;
-       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
-               goto err2;
+       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
 
-       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, desc.len);
+       if (nla[NFTA_SET_ELEM_KEY_END]) {
+               err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
+                                           nla[NFTA_SET_ELEM_KEY_END]);
+               if (err < 0)
+                       return err;
+
+               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+       }
 
        err = -ENOMEM;
-       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, NULL, 0,
-                                     0, GFP_KERNEL);
+       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
+                                     elem.key_end.val.data, NULL, 0, 0,
+                                     GFP_KERNEL);
        if (elem.priv == NULL)
-               goto err2;
+               goto fail_elem;
 
        ext = nft_set_elem_ext(set, elem.priv);
        if (flags)
                *nft_set_ext_flags(ext) = flags;
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
-       if (trans == NULL) {
-               err = -ENOMEM;
-               goto err3;
-       }
+       if (trans == NULL)
+               goto fail_trans;
 
        priv = set->ops->deactivate(ctx->net, set, &elem);
        if (priv == NULL) {
                err = -ENOENT;
-               goto err4;
+               goto fail_ops;
        }
        kfree(elem.priv);
        elem.priv = priv;
@@ -5119,13 +5269,12 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
-err4:
+fail_ops:
        kfree(trans);
-err3:
+fail_trans:
        kfree(elem.priv);
-err2:
-       nft_data_release(&elem.key.val, desc.type);
-err1:
+fail_elem:
+       nft_data_release(&elem.key.val, NFT_DATA_VALUE);
        return err;
 }
 
@@ -5415,8 +5564,7 @@ nft_obj_type_get(struct net *net, u32 objtype)
        lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (type == NULL) {
-               nft_request_module(net, "nft-obj-%u", objtype);
-               if (__nft_obj_type_get(objtype))
+               if (nft_request_module(net, "nft-obj-%u", objtype) == -EAGAIN)
                        return ERR_PTR(-EAGAIN);
        }
 #endif
@@ -5989,8 +6137,7 @@ nft_flowtable_type_get(struct net *net, u8 family)
        lockdep_nfnl_nft_mutex_not_held();
 #ifdef CONFIG_MODULES
        if (type == NULL) {
-               nft_request_module(net, "nf-flowtable-%u", family);
-               if (__nft_flowtable_type_get(family))
+               if (nft_request_module(net, "nf-flowtable-%u", family) == -EAGAIN)
                        return ERR_PTR(-EAGAIN);
        }
 #endif
@@ -6992,6 +7139,18 @@ static void nft_chain_del(struct nft_chain *chain)
        list_del_rcu(&chain->list);
 }
 
+static void nf_tables_module_autoload_cleanup(struct net *net)
+{
+       struct nft_module_request *req, *next;
+
+       WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
+       list_for_each_entry_safe(req, next, &net->nft.module_list, list) {
+               WARN_ON_ONCE(!req->done);
+               list_del(&req->list);
+               kfree(req);
+       }
+}
+
 static void nf_tables_commit_release(struct net *net)
 {
        struct nft_trans *trans;
@@ -7004,6 +7163,7 @@ static void nf_tables_commit_release(struct net *net)
         * to prevent expensive synchronize_rcu() in commit phase.
         */
        if (list_empty(&net->nft.commit_list)) {
+               nf_tables_module_autoload_cleanup(net);
                mutex_unlock(&net->nft.commit_mutex);
                return;
        }
@@ -7018,6 +7178,7 @@ static void nf_tables_commit_release(struct net *net)
        list_splice_tail_init(&net->nft.commit_list, &nf_tables_destroy_list);
        spin_unlock(&nf_tables_destroy_list_lock);
 
+       nf_tables_module_autoload_cleanup(net);
        mutex_unlock(&net->nft.commit_mutex);
 
        schedule_work(&trans_destroy_work);
@@ -7209,6 +7370,26 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
        return 0;
 }
 
+static void nf_tables_module_autoload(struct net *net)
+{
+       struct nft_module_request *req, *next;
+       LIST_HEAD(module_list);
+
+       list_splice_init(&net->nft.module_list, &module_list);
+       mutex_unlock(&net->nft.commit_mutex);
+       list_for_each_entry_safe(req, next, &module_list, list) {
+               if (req->done) {
+                       list_del(&req->list);
+                       kfree(req);
+               } else {
+                       request_module("%s", req->module);
+                       req->done = true;
+               }
+       }
+       mutex_lock(&net->nft.commit_mutex);
+       list_splice(&module_list, &net->nft.module_list);
+}
+
 static void nf_tables_abort_release(struct nft_trans *trans)
 {
        switch (trans->msg_type) {
@@ -7238,7 +7419,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
        kfree(trans);
 }
 
-static int __nf_tables_abort(struct net *net)
+static int __nf_tables_abort(struct net *net, bool autoload)
 {
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
@@ -7360,6 +7541,11 @@ static int __nf_tables_abort(struct net *net)
                nf_tables_abort_release(trans);
        }
 
+       if (autoload)
+               nf_tables_module_autoload(net);
+       else
+               nf_tables_module_autoload_cleanup(net);
+
        return 0;
 }
 
@@ -7368,9 +7554,9 @@ static void nf_tables_cleanup(struct net *net)
        nft_validate_state_update(net, NFT_VALIDATE_SKIP);
 }
 
-static int nf_tables_abort(struct net *net, struct sk_buff *skb)
+static int nf_tables_abort(struct net *net, struct sk_buff *skb, bool autoload)
 {
-       int ret = __nf_tables_abort(net);
+       int ret = __nf_tables_abort(net, autoload);
 
        mutex_unlock(&net->nft.commit_mutex);
 
@@ -7965,6 +8151,7 @@ static int __net_init nf_tables_init_net(struct net *net)
 {
        INIT_LIST_HEAD(&net->nft.tables);
        INIT_LIST_HEAD(&net->nft.commit_list);
+       INIT_LIST_HEAD(&net->nft.module_list);
        mutex_init(&net->nft.commit_mutex);
        net->nft.base_seq = 1;
        net->nft.validate_state = NFT_VALIDATE_SKIP;
@@ -7976,7 +8163,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
 {
        mutex_lock(&net->nft.commit_mutex);
        if (!list_empty(&net->nft.commit_list))
-               __nf_tables_abort(net);
+               __nf_tables_abort(net, false);
        __nft_release_tables(net);
        mutex_unlock(&net->nft.commit_mutex);
        WARN_ON_ONCE(!list_empty(&net->nft.tables));