]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/netfilter/nf_tables_api.c
Merge tag 'drm-intel-next-fixes-2020-02-13' of git://anongit.freedesktop.org/drm...
[linux.git] / net / netfilter / nf_tables_api.c
index 43f05b3acd600f9a751ac9711d6cd227cf4b51fa..d1318bdf49ca97bcdc0b4dd9ff209fdb6cb183f2 100644 (file)
@@ -22,6 +22,8 @@
 #include <net/net_namespace.h>
 #include <net/sock.h>
 
+#define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-"))
+
 static LIST_HEAD(nf_tables_expressions);
 static LIST_HEAD(nf_tables_objects);
 static LIST_HEAD(nf_tables_flowtables);
@@ -550,47 +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.
- * We first need to abort any pending transactions as once
- * mutex is unlocked a different client could start a new
- * transaction.  It must not see any 'future generation'
- * changes * as these changes will never happen.
- */
-#ifdef CONFIG_MODULES
-static int __nf_tables_abort(struct net *net);
+struct nft_module_request {
+       struct list_head        list;
+       char                    module[MODULE_NAME_LEN];
+       bool                    done;
+};
 
-static void nft_request_module(struct net *net, const char *fmt, ...)
+#ifdef CONFIG_MODULES
+static int nft_request_module(struct net *net, const char *fmt, ...)
 {
        char module_name[MODULE_NAME_LEN];
+       struct nft_module_request *req;
        va_list args;
        int ret;
 
-       __nf_tables_abort(net);
-
        va_start(args, fmt);
        ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
        va_end(args);
-       if (WARN(ret >= MODULE_NAME_LEN, "truncated: '%s' (len %d)", module_name, ret))
-               return;
+       if (ret >= MODULE_NAME_LEN)
+               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;
+
+                       /* 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
 
@@ -614,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
@@ -1045,12 +1070,18 @@ static int nft_flush_table(struct nft_ctx *ctx)
        }
 
        list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) {
+               if (!nft_is_active_next(ctx->net, flowtable))
+                       continue;
+
                err = nft_delflowtable(ctx, flowtable);
                if (err < 0)
                        goto out;
        }
 
        list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) {
+               if (!nft_is_active_next(ctx->net, obj))
+                       continue;
+
                err = nft_delobj(ctx, obj);
                if (err < 0)
                        goto out;
@@ -1153,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;
        }
@@ -1241,7 +1269,8 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
                                    .len = NFT_CHAIN_MAXNAMELEN - 1 },
        [NFTA_CHAIN_HOOK]       = { .type = NLA_NESTED },
        [NFTA_CHAIN_POLICY]     = { .type = NLA_U32 },
-       [NFTA_CHAIN_TYPE]       = { .type = NLA_STRING },
+       [NFTA_CHAIN_TYPE]       = { .type = NLA_STRING,
+                                   .len = NFT_MODULE_AUTOLOAD_LIMIT },
        [NFTA_CHAIN_COUNTERS]   = { .type = NLA_NESTED },
        [NFTA_CHAIN_FLAGS]      = { .type = NLA_U32 },
 };
@@ -1676,6 +1705,7 @@ static int nf_tables_parse_netdev_hooks(struct net *net,
                        goto err_hook;
                }
                if (nft_hook_list_find(hook_list, hook)) {
+                       kfree(hook);
                        err = -EEXIST;
                        goto err_hook;
                }
@@ -1757,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);
@@ -2317,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;
@@ -2345,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
@@ -2355,7 +2387,8 @@ static const struct nft_expr_type *nft_expr_type_get(struct net *net,
 }
 
 static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = {
-       [NFTA_EXPR_NAME]        = { .type = NLA_STRING },
+       [NFTA_EXPR_NAME]        = { .type = NLA_STRING,
+                                   .len = NFT_MODULE_AUTOLOAD_LIMIT },
        [NFTA_EXPR_DATA]        = { .type = NLA_NESTED },
 };
 
@@ -2437,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;
                }
@@ -3276,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
@@ -3358,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,
@@ -3524,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)
 {
@@ -3587,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);
@@ -3764,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)
 {
@@ -3777,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,
@@ -3801,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 ||
@@ -3979,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;
@@ -4182,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);
 
@@ -4198,7 +4322,9 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
        [NFTA_SET_ELEM_USERDATA]        = { .type = NLA_BINARY,
                                            .len = NFT_USERDATA_MAXLEN },
        [NFTA_SET_ELEM_EXPR]            = { .type = NLA_NESTED },
-       [NFTA_SET_ELEM_OBJREF]          = { .type = NLA_STRING },
+       [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] = {
@@ -4248,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,
@@ -4490,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;
@@ -4513,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);
@@ -4649,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;
@@ -4663,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)) {
@@ -4722,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;
@@ -4794,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)
@@ -4812,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) {
@@ -4848,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
@@ -4875,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)
@@ -4895,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);
@@ -4906,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) &&
@@ -4919,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;
 }
 
@@ -5040,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;
@@ -5051,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);
 
@@ -5065,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);
+
+       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, desc.len);
+               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;
@@ -5106,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;
 }
 
@@ -5402,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
@@ -5976,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
@@ -6979,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;
@@ -6991,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;
        }
@@ -7005,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);
@@ -7196,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) {
@@ -7225,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;
@@ -7347,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;
 }
 
@@ -7355,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);
 
@@ -7952,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;
@@ -7963,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));