mptcp: fix security context on server socket
[linux-2.6-microblaze.git] / net / netfilter / nf_tables_api.c
index 4603b66..8d5aa0a 100644 (file)
@@ -206,7 +206,7 @@ static int nf_tables_register_hook(struct net *net,
        if (basechain->type->ops_register)
                return basechain->type->ops_register(net, ops);
 
-       if (table->family == NFPROTO_NETDEV)
+       if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
                return nft_netdev_register_hooks(net, &basechain->hook_list);
 
        return nf_register_net_hook(net, &basechain->ops);
@@ -228,7 +228,7 @@ static void nf_tables_unregister_hook(struct net *net,
        if (basechain->type->ops_unregister)
                return basechain->type->ops_unregister(net, ops);
 
-       if (table->family == NFPROTO_NETDEV)
+       if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
                nft_netdev_unregister_hooks(net, &basechain->hook_list);
        else
                nf_unregister_net_hook(net, &basechain->ops);
@@ -302,7 +302,7 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
        struct nft_expr *expr;
 
        expr = nft_expr_first(rule);
-       while (expr != nft_expr_last(rule) && expr->ops) {
+       while (nft_expr_more(rule, expr)) {
                if (expr->ops->activate)
                        expr->ops->activate(ctx, expr);
 
@@ -317,7 +317,7 @@ static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
        struct nft_expr *expr;
 
        expr = nft_expr_first(rule);
-       while (expr != nft_expr_last(rule) && expr->ops) {
+       while (nft_expr_more(rule, expr)) {
                if (expr->ops->deactivate)
                        expr->ops->deactivate(ctx, expr, phase);
 
@@ -581,7 +581,8 @@ struct nft_module_request {
 };
 
 #ifdef CONFIG_MODULES
-static int nft_request_module(struct net *net, const char *fmt, ...)
+static __printf(2, 3) int nft_request_module(struct net *net, const char *fmt,
+                                            ...)
 {
        char module_name[MODULE_NAME_LEN];
        struct nft_module_request *req;
@@ -619,7 +620,8 @@ static int nft_request_module(struct net *net, const char *fmt, ...)
 static void lockdep_nfnl_nft_mutex_not_held(void)
 {
 #ifdef CONFIG_PROVE_LOCKING
-       WARN_ON_ONCE(lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
+       if (debug_locks)
+               WARN_ON_ONCE(lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
 #endif
 }
 
@@ -650,6 +652,8 @@ static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
                                    .len = NFT_TABLE_MAXNAMELEN - 1 },
        [NFTA_TABLE_FLAGS]      = { .type = NLA_U32 },
        [NFTA_TABLE_HANDLE]     = { .type = NLA_U64 },
+       [NFTA_TABLE_USERDATA]   = { .type = NLA_BINARY,
+                                   .len = NFT_USERDATA_MAXLEN }
 };
 
 static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
@@ -676,6 +680,11 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
                         NFTA_TABLE_PAD))
                goto nla_put_failure;
 
+       if (table->udata) {
+               if (nla_put(skb, NFTA_TABLE_USERDATA, table->udlen, table->udata))
+                       goto nla_put_failure;
+       }
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -988,8 +997,8 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
        int family = nfmsg->nfgen_family;
        const struct nlattr *attr;
        struct nft_table *table;
-       u32 flags = 0;
        struct nft_ctx ctx;
+       u32 flags = 0;
        int err;
 
        lockdep_assert_held(&net->nft.commit_mutex);
@@ -1025,6 +1034,14 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
        if (table->name == NULL)
                goto err_strdup;
 
+       if (nla[NFTA_TABLE_USERDATA]) {
+               table->udata = nla_memdup(nla[NFTA_TABLE_USERDATA], GFP_KERNEL);
+               if (table->udata == NULL)
+                       goto err_table_udata;
+
+               table->udlen = nla_len(nla[NFTA_TABLE_USERDATA]);
+       }
+
        err = rhltable_init(&table->chains_ht, &nft_chain_ht_params);
        if (err)
                goto err_chain_ht;
@@ -1047,6 +1064,8 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
 err_trans:
        rhltable_destroy(&table->chains_ht);
 err_chain_ht:
+       kfree(table->udata);
+err_table_udata:
        kfree(table->name);
 err_strdup:
        kfree(table);
@@ -1202,6 +1221,7 @@ static void nf_tables_table_destroy(struct nft_ctx *ctx)
 
        rhltable_destroy(&ctx->table->chains_ht);
        kfree(ctx->table->name);
+       kfree(ctx->table->udata);
        kfree(ctx->table);
 }
 
@@ -1263,7 +1283,7 @@ static struct nft_chain *nft_chain_lookup(struct net *net,
        if (nla == NULL)
                return ERR_PTR(-EINVAL);
 
-       nla_strlcpy(search, nla, sizeof(search));
+       nla_strscpy(search, nla, sizeof(search));
 
        WARN_ON(!rcu_read_lock_held() &&
                !lockdep_commit_lock_is_held(net));
@@ -1297,6 +1317,8 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
        [NFTA_CHAIN_COUNTERS]   = { .type = NLA_NESTED },
        [NFTA_CHAIN_FLAGS]      = { .type = NLA_U32 },
        [NFTA_CHAIN_ID]         = { .type = NLA_U32 },
+       [NFTA_CHAIN_USERDATA]   = { .type = NLA_BINARY,
+                                   .len = NFT_USERDATA_MAXLEN },
 };
 
 static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = {
@@ -1361,7 +1383,7 @@ static int nft_dump_basechain_hook(struct sk_buff *skb, int family,
        if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
                goto nla_put_failure;
 
-       if (family == NFPROTO_NETDEV) {
+       if (nft_base_chain_netdev(family, ops->hooknum)) {
                nest_devs = nla_nest_start_noflag(skb, NFTA_HOOK_DEVS);
                list_for_each_entry(hook, &basechain->hook_list, list) {
                        if (!first)
@@ -1438,6 +1460,10 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
        if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use)))
                goto nla_put_failure;
 
+       if (chain->udata &&
+           nla_put(skb, NFTA_CHAIN_USERDATA, chain->udlen, chain->udata))
+               goto nla_put_failure;
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -1661,7 +1687,7 @@ void nf_tables_chain_destroy(struct nft_ctx *ctx)
        if (nft_is_base_chain(chain)) {
                struct nft_base_chain *basechain = nft_base_chain(chain);
 
-               if (ctx->family == NFPROTO_NETDEV) {
+               if (nft_base_chain_netdev(ctx->family, basechain->ops.hooknum)) {
                        list_for_each_entry_safe(hook, next,
                                                 &basechain->hook_list, list) {
                                list_del_rcu(&hook->list);
@@ -1674,9 +1700,11 @@ void nf_tables_chain_destroy(struct nft_ctx *ctx)
                        free_percpu(rcu_dereference_raw(basechain->stats));
                }
                kfree(chain->name);
+               kfree(chain->udata);
                kfree(basechain);
        } else {
                kfree(chain->name);
+               kfree(chain->udata);
                kfree(chain);
        }
 }
@@ -1695,7 +1723,11 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
                goto err_hook_alloc;
        }
 
-       nla_strlcpy(ifname, attr, IFNAMSIZ);
+       nla_strscpy(ifname, attr, IFNAMSIZ);
+       /* nf_tables_netdev_event() is called under rtnl_mutex, this is
+        * indirectly serializing all the other holders of the commit_mutex with
+        * the rtnl_mutex.
+        */
        dev = __dev_get_by_name(net, ifname);
        if (!dev) {
                err = -ENOENT;
@@ -1838,7 +1870,7 @@ static int nft_chain_parse_hook(struct net *net,
                if (IS_ERR(type))
                        return PTR_ERR(type);
        }
-       if (hook->num > NF_MAX_HOOKS || !(type->hook_mask & (1 << hook->num)))
+       if (hook->num >= NFT_MAX_HOOKS || !(type->hook_mask & (1 << hook->num)))
                return -EOPNOTSUPP;
 
        if (type->type == NFT_CHAIN_T_NAT &&
@@ -1851,7 +1883,7 @@ static int nft_chain_parse_hook(struct net *net,
        hook->type = type;
 
        INIT_LIST_HEAD(&hook->list);
-       if (family == NFPROTO_NETDEV) {
+       if (nft_base_chain_netdev(family, hook->num)) {
                err = nft_chain_parse_netdev(net, ha, &hook->list);
                if (err < 0) {
                        module_put(type->owner);
@@ -1918,7 +1950,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
        INIT_LIST_HEAD(&basechain->hook_list);
        chain = &basechain->chain;
 
-       if (family == NFPROTO_NETDEV) {
+       if (nft_base_chain_netdev(family, hook->num)) {
                list_splice_init(&hook->list, &basechain->hook_list);
                list_for_each_entry(h, &basechain->hook_list, list)
                        nft_basechain_hook_init(&h->ops, family, hook, chain);
@@ -2030,7 +2062,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
        } else {
                if (!(flags & NFT_CHAIN_BINDING)) {
                        err = -EINVAL;
-                       goto err1;
+                       goto err_destroy_chain;
                }
 
                snprintf(name, sizeof(name), "__chain%llu", ++chain_id);
@@ -2039,13 +2071,22 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
 
        if (!chain->name) {
                err = -ENOMEM;
-               goto err1;
+               goto err_destroy_chain;
+       }
+
+       if (nla[NFTA_CHAIN_USERDATA]) {
+               chain->udata = nla_memdup(nla[NFTA_CHAIN_USERDATA], GFP_KERNEL);
+               if (chain->udata == NULL) {
+                       err = -ENOMEM;
+                       goto err_destroy_chain;
+               }
+               chain->udlen = nla_len(nla[NFTA_CHAIN_USERDATA]);
        }
 
        rules = nf_tables_chain_alloc_rules(chain, 0);
        if (!rules) {
                err = -ENOMEM;
-               goto err1;
+               goto err_destroy_chain;
        }
 
        *rules = NULL;
@@ -2054,12 +2095,12 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
 
        err = nf_tables_register_hook(net, table, chain);
        if (err < 0)
-               goto err1;
+               goto err_destroy_chain;
 
        trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
        if (IS_ERR(trans)) {
                err = PTR_ERR(trans);
-               goto err2;
+               goto err_unregister_hook;
        }
 
        nft_trans_chain_policy(trans) = NFT_CHAIN_POLICY_UNSET;
@@ -2069,15 +2110,15 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
        err = nft_chain_add(table, chain);
        if (err < 0) {
                nft_trans_destroy(trans);
-               goto err2;
+               goto err_unregister_hook;
        }
 
        table->use++;
 
        return 0;
-err2:
+err_unregister_hook:
        nf_tables_unregister_hook(net, table, chain);
-err1:
+err_destroy_chain:
        nf_tables_chain_destroy(ctx);
 
        return err;
@@ -2103,7 +2144,8 @@ static bool nft_hook_list_equal(struct list_head *hook_list1,
 }
 
 static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
-                             u32 flags)
+                             u32 flags, const struct nlattr *attr,
+                             struct netlink_ext_ack *extack)
 {
        const struct nlattr * const *nla = ctx->nla;
        struct nft_table *table = ctx->table;
@@ -2119,9 +2161,10 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
                return -EOPNOTSUPP;
 
        if (nla[NFTA_CHAIN_HOOK]) {
-               if (!nft_is_base_chain(chain))
+               if (!nft_is_base_chain(chain)) {
+                       NL_SET_BAD_ATTR(extack, attr);
                        return -EEXIST;
-
+               }
                err = nft_chain_parse_hook(ctx->net, nla, &hook, ctx->family,
                                           false);
                if (err < 0)
@@ -2130,13 +2173,15 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
                basechain = nft_base_chain(chain);
                if (basechain->type != hook.type) {
                        nft_chain_release_hook(&hook);
+                       NL_SET_BAD_ATTR(extack, attr);
                        return -EEXIST;
                }
 
-               if (ctx->family == NFPROTO_NETDEV) {
+               if (nft_base_chain_netdev(ctx->family, hook.num)) {
                        if (!nft_hook_list_equal(&basechain->hook_list,
                                                 &hook.list)) {
                                nft_chain_release_hook(&hook);
+                               NL_SET_BAD_ATTR(extack, attr);
                                return -EEXIST;
                        }
                } else {
@@ -2144,6 +2189,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
                        if (ops->hooknum != hook.num ||
                            ops->priority != hook.priority) {
                                nft_chain_release_hook(&hook);
+                               NL_SET_BAD_ATTR(extack, attr);
                                return -EEXIST;
                        }
                }
@@ -2156,8 +2202,10 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
 
                chain2 = nft_chain_lookup(ctx->net, table,
                                          nla[NFTA_CHAIN_NAME], genmask);
-               if (!IS_ERR(chain2))
+               if (!IS_ERR(chain2)) {
+                       NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_NAME]);
                        return -EEXIST;
+               }
        }
 
        if (nla[NFTA_CHAIN_COUNTERS]) {
@@ -2200,6 +2248,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
                            nft_trans_chain_update(tmp) &&
                            nft_trans_chain_name(tmp) &&
                            strcmp(name, nft_trans_chain_name(tmp)) == 0) {
+                               NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_NAME]);
                                kfree(name);
                                goto err;
                        }
@@ -2322,7 +2371,8 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
                        return -EOPNOTSUPP;
 
                flags |= chain->flags & NFT_CHAIN_BASE;
-               return nf_tables_updchain(&ctx, genmask, policy, flags);
+               return nf_tables_updchain(&ctx, genmask, policy, flags, attr,
+                                         extack);
        }
 
        return nf_tables_addchain(&ctx, family, genmask, policy, flags);
@@ -3036,7 +3086,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
         * is called on error from nf_tables_newrule().
         */
        expr = nft_expr_first(rule);
-       while (expr != nft_expr_last(rule) && expr->ops) {
+       while (nft_expr_more(rule, expr)) {
                next = nft_expr_next(expr);
                nf_tables_expr_destroy(ctx, expr);
                expr = next;
@@ -3521,6 +3571,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
        [NFTA_SET_OBJ_TYPE]             = { .type = NLA_U32 },
        [NFTA_SET_HANDLE]               = { .type = NLA_U64 },
        [NFTA_SET_EXPR]                 = { .type = NLA_NESTED },
+       [NFTA_SET_EXPRESSIONS]          = { .type = NLA_NESTED },
 };
 
 static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
@@ -3674,7 +3725,7 @@ cont:
        return 0;
 }
 
-static int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result)
+int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result)
 {
        u64 ms = be64_to_cpu(nla_get_be64(nla));
        u64 max = (u64)(~((u64)0));
@@ -3688,7 +3739,7 @@ static int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result)
        return 0;
 }
 
-static __be64 nf_jiffies64_to_msecs(u64 input)
+__be64 nf_jiffies64_to_msecs(u64 input)
 {
        return cpu_to_be64(jiffies64_to_msecs(input));
 }
@@ -3728,6 +3779,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
        u32 portid = ctx->portid;
        struct nlattr *nest;
        u32 seq = ctx->seq;
+       int i;
 
        event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
@@ -3796,12 +3848,23 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
 
        nla_nest_end(skb, nest);
 
-       if (set->expr) {
+       if (set->num_exprs == 1) {
                nest = nla_nest_start_noflag(skb, NFTA_SET_EXPR);
-               if (nf_tables_fill_expr_info(skb, set->expr) < 0)
+               if (nf_tables_fill_expr_info(skb, set->exprs[0]) < 0)
                        goto nla_put_failure;
 
                nla_nest_end(skb, nest);
+       } else if (set->num_exprs > 1) {
+               nest = nla_nest_start_noflag(skb, NFTA_SET_EXPRESSIONS);
+               if (nest == NULL)
+                       goto nla_put_failure;
+
+               for (i = 0; i < set->num_exprs; i++) {
+                       if (nft_expr_dump(skb, NFTA_LIST_ELEM,
+                                         set->exprs[i]) < 0)
+                               goto nla_put_failure;
+               }
+               nla_nest_end(skb, nest);
        }
 
        nlmsg_end(skb, nlh);
@@ -4170,7 +4233,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                        return err;
        }
 
-       if (nla[NFTA_SET_EXPR])
+       if (nla[NFTA_SET_EXPR] || nla[NFTA_SET_EXPRESSIONS])
                desc.expr = true;
 
        table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family, genmask);
@@ -4234,6 +4297,31 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                        err = PTR_ERR(expr);
                        goto err_set_alloc_name;
                }
+               set->exprs[0] = expr;
+               set->num_exprs++;
+       } else if (nla[NFTA_SET_EXPRESSIONS]) {
+               struct nft_expr *expr;
+               struct nlattr *tmp;
+               int left;
+
+               i = 0;
+               nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) {
+                       if (i == NFT_SET_EXPR_MAX) {
+                               err = -E2BIG;
+                               goto err_set_init;
+                       }
+                       if (nla_type(tmp) != NFTA_LIST_ELEM) {
+                               err = -EINVAL;
+                               goto err_set_init;
+                       }
+                       expr = nft_set_elem_expr_alloc(&ctx, set, tmp);
+                       if (IS_ERR(expr)) {
+                               err = PTR_ERR(expr);
+                               goto err_set_init;
+                       }
+                       set->exprs[i++] = expr;
+                       set->num_exprs++;
+               }
        }
 
        udata = NULL;
@@ -4251,7 +4339,6 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        set->dtype = dtype;
        set->objtype = objtype;
        set->dlen  = desc.dlen;
-       set->expr = expr;
        set->flags = flags;
        set->size  = desc.size;
        set->policy = policy;
@@ -4280,8 +4367,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
 err_set_trans:
        ops->destroy(set);
 err_set_init:
-       if (expr)
-               nft_expr_destroy(&ctx, expr);
+       for (i = 0; i < set->num_exprs; i++)
+               nft_expr_destroy(&ctx, set->exprs[i]);
 err_set_alloc_name:
        kfree(set->name);
 err_set_name:
@@ -4291,11 +4378,13 @@ err_set_name:
 
 static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
 {
+       int i;
+
        if (WARN_ON(set->use > 0))
                return;
 
-       if (set->expr)
-               nft_expr_destroy(ctx, set->expr);
+       for (i = 0; i < set->num_exprs; i++)
+               nft_expr_destroy(ctx, set->exprs[i]);
 
        set->ops->destroy(set);
        kfree(set->name);
@@ -4448,8 +4537,8 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
        [NFT_SET_EXT_DATA]              = {
                .align  = __alignof__(u32),
        },
-       [NFT_SET_EXT_EXPR]              = {
-               .align  = __alignof__(struct nft_expr),
+       [NFT_SET_EXT_EXPRESSIONS]       = {
+               .align  = __alignof__(struct nft_set_elem_expr),
        },
        [NFT_SET_EXT_OBJREF]            = {
                .len    = sizeof(struct nft_object *),
@@ -4492,6 +4581,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
        [NFTA_SET_ELEM_OBJREF]          = { .type = NLA_STRING,
                                            .len = NFT_OBJ_MAXNAMELEN - 1 },
        [NFTA_SET_ELEM_KEY_END]         = { .type = NLA_NESTED },
+       [NFTA_SET_ELEM_EXPRESSIONS]     = { .type = NLA_NESTED },
 };
 
 static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
@@ -4525,6 +4615,43 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
        return 0;
 }
 
+static int nft_set_elem_expr_dump(struct sk_buff *skb,
+                                 const struct nft_set *set,
+                                 const struct nft_set_ext *ext)
+{
+       struct nft_set_elem_expr *elem_expr;
+       u32 size, num_exprs = 0;
+       struct nft_expr *expr;
+       struct nlattr *nest;
+
+       elem_expr = nft_set_ext_expr(ext);
+       nft_setelem_expr_foreach(expr, elem_expr, size)
+               num_exprs++;
+
+       if (num_exprs == 1) {
+               expr = nft_setelem_expr_at(elem_expr, 0);
+               if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr) < 0)
+                       return -1;
+
+               return 0;
+       } else if (num_exprs > 1) {
+               nest = nla_nest_start_noflag(skb, NFTA_SET_ELEM_EXPRESSIONS);
+               if (nest == NULL)
+                       goto nla_put_failure;
+
+               nft_setelem_expr_foreach(expr, elem_expr, size) {
+                       expr = nft_setelem_expr_at(elem_expr, size);
+                       if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr) < 0)
+                               goto nla_put_failure;
+               }
+               nla_nest_end(skb, nest);
+       }
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
 static int nf_tables_fill_setelem(struct sk_buff *skb,
                                  const struct nft_set *set,
                                  const struct nft_set_elem *elem)
@@ -4552,8 +4679,8 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                          set->dlen) < 0)
                goto nla_put_failure;
 
-       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR) &&
-           nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0)
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS) &&
+           nft_set_elem_expr_dump(skb, set, ext))
                goto nla_put_failure;
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) &&
@@ -5048,8 +5175,8 @@ void *nft_set_elem_init(const struct nft_set *set,
        return elem;
 }
 
-static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
-                                     struct nft_expr *expr)
+static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
+                                       struct nft_expr *expr)
 {
        if (expr->ops->destroy_clone) {
                expr->ops->destroy_clone(ctx, expr);
@@ -5059,6 +5186,16 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
        }
 }
 
+static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
+                                     struct nft_set_elem_expr *elem_expr)
+{
+       struct nft_expr *expr;
+       u32 size;
+
+       nft_setelem_expr_foreach(expr, elem_expr, size)
+               __nft_set_elem_expr_destroy(ctx, expr);
+}
+
 void nft_set_elem_destroy(const struct nft_set *set, void *elem,
                          bool destroy_expr)
 {
@@ -5071,7 +5208,7 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
        nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
                nft_data_release(nft_set_ext_data(ext), set->dtype);
-       if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
+       if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
                nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext));
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
@@ -5088,15 +5225,57 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
 {
        struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
 
-       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
                nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext));
 
        kfree(elem);
 }
 
+static int nft_set_elem_expr_clone(const struct nft_ctx *ctx,
+                                  struct nft_set *set,
+                                  struct nft_expr *expr_array[])
+{
+       struct nft_expr *expr;
+       int err, i, k;
+
+       for (i = 0; i < set->num_exprs; i++) {
+               expr = kzalloc(set->exprs[i]->ops->size, GFP_KERNEL);
+               if (!expr)
+                       goto err_expr;
+
+               err = nft_expr_clone(expr, set->exprs[i]);
+               if (err < 0) {
+                       nft_expr_destroy(ctx, expr);
+                       goto err_expr;
+               }
+               expr_array[i] = expr;
+       }
+
+       return 0;
+
+err_expr:
+       for (k = i - 1; k >= 0; k++)
+               nft_expr_destroy(ctx, expr_array[i]);
+
+       return -ENOMEM;
+}
+
+static void nft_set_elem_expr_setup(const struct nft_set_ext *ext, int i,
+                                   struct nft_expr *expr_array[])
+{
+       struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
+       struct nft_expr *expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
+
+       memcpy(expr, expr_array[i], expr_array[i]->ops->size);
+       elem_expr->size += expr_array[i]->ops->size;
+       kfree(expr_array[i]);
+       expr_array[i] = NULL;
+}
+
 static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr, u32 nlmsg_flags)
 {
+       struct nft_expr *expr_array[NFT_SET_EXPR_MAX] = {};
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        u8 genmask = nft_genmask_next(ctx->net);
        struct nft_set_ext_tmpl tmpl;
@@ -5104,16 +5283,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
        struct nft_object *obj = NULL;
-       struct nft_expr *expr = NULL;
        struct nft_userdata *udata;
        struct nft_data_desc desc;
        enum nft_registers dreg;
        struct nft_trans *trans;
-       u32 flags = 0;
+       u32 flags = 0, size = 0;
        u64 timeout;
        u64 expiration;
+       int err, i;
        u8 ulen;
-       int err;
 
        err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
                                          nft_set_elem_policy, NULL);
@@ -5146,7 +5324,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
              nla[NFTA_SET_ELEM_TIMEOUT] ||
              nla[NFTA_SET_ELEM_EXPIRATION] ||
              nla[NFTA_SET_ELEM_USERDATA] ||
-             nla[NFTA_SET_ELEM_EXPR]))
+             nla[NFTA_SET_ELEM_EXPR] ||
+             nla[NFTA_SET_ELEM_EXPRESSIONS]))
                return -EINVAL;
 
        timeout = 0;
@@ -5171,23 +5350,62 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        return err;
        }
 
-       if (nla[NFTA_SET_ELEM_EXPR] != NULL) {
+       if (nla[NFTA_SET_ELEM_EXPR]) {
+               struct nft_expr *expr;
+
+               if (set->num_exprs != 1)
+                       return -EOPNOTSUPP;
+
                expr = nft_set_elem_expr_alloc(ctx, set,
                                               nla[NFTA_SET_ELEM_EXPR]);
                if (IS_ERR(expr))
                        return PTR_ERR(expr);
 
-               err = -EOPNOTSUPP;
-               if (set->expr && set->expr->ops != expr->ops)
+               expr_array[0] = expr;
+
+               if (set->exprs[0] && set->exprs[0]->ops != expr->ops) {
+                       err = -EOPNOTSUPP;
                        goto err_set_elem_expr;
-       } else if (set->expr) {
-               expr = kzalloc(set->expr->ops->size, GFP_KERNEL);
-               if (!expr)
-                       return -ENOMEM;
+               }
+       } else if (nla[NFTA_SET_ELEM_EXPRESSIONS]) {
+               struct nft_expr *expr;
+               struct nlattr *tmp;
+               int left;
 
-               err = nft_expr_clone(expr, set->expr);
-               if (err < 0)
+               if (set->num_exprs == 0)
+                       return -EOPNOTSUPP;
+
+               i = 0;
+               nla_for_each_nested(tmp, nla[NFTA_SET_ELEM_EXPRESSIONS], left) {
+                       if (i == set->num_exprs) {
+                               err = -E2BIG;
+                               goto err_set_elem_expr;
+                       }
+                       if (nla_type(tmp) != NFTA_LIST_ELEM) {
+                               err = -EINVAL;
+                               goto err_set_elem_expr;
+                       }
+                       expr = nft_set_elem_expr_alloc(ctx, set, tmp);
+                       if (IS_ERR(expr)) {
+                               err = PTR_ERR(expr);
+                               goto err_set_elem_expr;
+                       }
+                       expr_array[i] = expr;
+
+                       if (expr->ops != set->exprs[i]->ops) {
+                               err = -EOPNOTSUPP;
+                               goto err_set_elem_expr;
+                       }
+                       i++;
+               }
+               if (set->num_exprs != i) {
+                       err = -EOPNOTSUPP;
                        goto err_set_elem_expr;
+               }
+       } else if (set->num_exprs > 0) {
+               err = nft_set_elem_expr_clone(ctx, set, expr_array);
+               if (err < 0)
+                       goto err_set_elem_expr_clone;
        }
 
        err = nft_setelem_parse_key(ctx, set, &elem.key.val,
@@ -5212,9 +5430,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
        }
 
-       if (expr)
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPR,
-                                      expr->ops->size);
+       if (set->num_exprs) {
+               for (i = 0; i < set->num_exprs; i++)
+                       size += expr_array[i]->ops->size;
+
+               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPRESSIONS,
+                                      sizeof(struct nft_set_elem_expr) +
+                                      size);
+       }
 
        if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
                if (!(set->flags & NFT_SET_OBJECT)) {
@@ -5296,11 +5519,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                *nft_set_ext_obj(ext) = obj;
                obj->use++;
        }
-       if (expr) {
-               memcpy(nft_set_ext_expr(ext), expr, expr->ops->size);
-               kfree(expr);
-               expr = NULL;
-       }
+       for (i = 0; i < set->num_exprs; i++)
+               nft_set_elem_expr_setup(ext, i, expr_array);
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
        if (trans == NULL)
@@ -5361,9 +5581,9 @@ err_parse_key_end:
 err_parse_key:
        nft_data_release(&elem.key.val, NFT_DATA_VALUE);
 err_set_elem_expr:
-       if (expr != NULL)
-               nft_expr_destroy(ctx, expr);
-
+       for (i = 0; i < set->num_exprs && expr_array[i]; i++)
+               nft_expr_destroy(ctx, expr_array[i]);
+err_set_elem_expr_clone:
        return err;
 }
 
@@ -5690,7 +5910,7 @@ struct nft_object *nft_obj_lookup(const struct net *net,
        struct rhlist_head *tmp, *list;
        struct nft_object *obj;
 
-       nla_strlcpy(search, nla, sizeof(search));
+       nla_strscpy(search, nla, sizeof(search));
        k.name = search;
 
        WARN_ON_ONCE(!rcu_read_lock_held() &&
@@ -5737,6 +5957,8 @@ static const struct nla_policy nft_obj_policy[NFTA_OBJ_MAX + 1] = {
        [NFTA_OBJ_TYPE]         = { .type = NLA_U32 },
        [NFTA_OBJ_DATA]         = { .type = NLA_NESTED },
        [NFTA_OBJ_HANDLE]       = { .type = NLA_U64},
+       [NFTA_OBJ_USERDATA]     = { .type = NLA_BINARY,
+                                   .len = NFT_USERDATA_MAXLEN },
 };
 
 static struct nft_object *nft_obj_init(const struct nft_ctx *ctx,
@@ -5928,7 +6150,7 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
        obj = nft_obj_init(&ctx, type, nla[NFTA_OBJ_DATA]);
        if (IS_ERR(obj)) {
                err = PTR_ERR(obj);
-               goto err1;
+               goto err_init;
        }
        obj->key.table = table;
        obj->handle = nf_tables_alloc_handle(table);
@@ -5936,32 +6158,42 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
        obj->key.name = nla_strdup(nla[NFTA_OBJ_NAME], GFP_KERNEL);
        if (!obj->key.name) {
                err = -ENOMEM;
-               goto err2;
+               goto err_strdup;
+       }
+
+       if (nla[NFTA_OBJ_USERDATA]) {
+               obj->udata = nla_memdup(nla[NFTA_OBJ_USERDATA], GFP_KERNEL);
+               if (obj->udata == NULL)
+                       goto err_userdata;
+
+               obj->udlen = nla_len(nla[NFTA_OBJ_USERDATA]);
        }
 
        err = nft_trans_obj_add(&ctx, NFT_MSG_NEWOBJ, obj);
        if (err < 0)
-               goto err3;
+               goto err_trans;
 
        err = rhltable_insert(&nft_objname_ht, &obj->rhlhead,
                              nft_objname_ht_params);
        if (err < 0)
-               goto err4;
+               goto err_obj_ht;
 
        list_add_tail_rcu(&obj->list, &table->objects);
        table->use++;
        return 0;
-err4:
+err_obj_ht:
        /* queued in transaction log */
        INIT_LIST_HEAD(&obj->list);
        return err;
-err3:
+err_trans:
        kfree(obj->key.name);
-err2:
+err_userdata:
+       kfree(obj->udata);
+err_strdup:
        if (obj->ops->destroy)
                obj->ops->destroy(&ctx, obj);
        kfree(obj);
-err1:
+err_init:
        module_put(type->owner);
        return err;
 }
@@ -5993,6 +6225,10 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net,
                         NFTA_OBJ_PAD))
                goto nla_put_failure;
 
+       if (obj->udata &&
+           nla_put(skb, NFTA_OBJ_USERDATA, obj->udlen, obj->udata))
+               goto nla_put_failure;
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -6199,6 +6435,7 @@ static void nft_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
 
        module_put(obj->ops->type->owner);
        kfree(obj->key.name);
+       kfree(obj->udata);
        kfree(obj);
 }
 
@@ -7076,7 +7313,7 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
                        GFP_KERNEL);
        kfree(buf);
 
-       if (ctx->report &&
+       if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return;
 
@@ -7198,7 +7435,7 @@ static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb,
        audit_log_nfcfg("?:0;?:0", 0, net->nft.base_seq,
                        AUDIT_NFT_OP_GEN_REGISTER, GFP_KERNEL);
 
-       if (nlmsg_report(nlh) &&
+       if (!nlmsg_report(nlh) &&
            !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
                return;
 
@@ -7992,12 +8229,16 @@ static void nf_tables_abort_release(struct nft_trans *trans)
        kfree(trans);
 }
 
-static int __nf_tables_abort(struct net *net, bool autoload)
+static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
 {
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
        struct nft_hook *hook;
 
+       if (action == NFNL_ABORT_VALIDATE &&
+           nf_tables_validate(net) < 0)
+               return -EAGAIN;
+
        list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list,
                                         list) {
                switch (trans->msg_type) {
@@ -8129,7 +8370,7 @@ static int __nf_tables_abort(struct net *net, bool autoload)
                nf_tables_abort_release(trans);
        }
 
-       if (autoload)
+       if (action == NFNL_ABORT_AUTOLOAD)
                nf_tables_module_autoload(net);
        else
                nf_tables_module_autoload_cleanup(net);
@@ -8142,9 +8383,10 @@ 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, bool autoload)
+static int nf_tables_abort(struct net *net, struct sk_buff *skb,
+                          enum nfnl_abort_action action)
 {
-       int ret = __nf_tables_abort(net, autoload);
+       int ret = __nf_tables_abort(net, action);
 
        mutex_unlock(&net->nft.commit_mutex);
 
@@ -8775,7 +9017,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, false);
+               __nf_tables_abort(net, NFNL_ABORT_NONE);
        __nft_release_tables(net);
        mutex_unlock(&net->nft.commit_mutex);
        WARN_ON_ONCE(!list_empty(&net->nft.tables));