netfilter: nftables: avoid potential overflows on 32bit arches
[linux-2.6-microblaze.git] / net / netfilter / nf_tables_api.c
index 589d2f6..d63d2d8 100644 (file)
@@ -25,6 +25,8 @@
 
 #define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-"))
 
+unsigned int nf_tables_net_id __read_mostly;
+
 static LIST_HEAD(nf_tables_expressions);
 static LIST_HEAD(nf_tables_objects);
 static LIST_HEAD(nf_tables_flowtables);
@@ -66,9 +68,46 @@ static const struct rhashtable_params nft_objname_ht_params = {
        .automatic_shrinking    = true,
 };
 
+struct nft_audit_data {
+       struct nft_table *table;
+       int entries;
+       int op;
+       struct list_head list;
+};
+
+static const u8 nft2audit_op[NFT_MSG_MAX] = { // enum nf_tables_msg_types
+       [NFT_MSG_NEWTABLE]      = AUDIT_NFT_OP_TABLE_REGISTER,
+       [NFT_MSG_GETTABLE]      = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_DELTABLE]      = AUDIT_NFT_OP_TABLE_UNREGISTER,
+       [NFT_MSG_NEWCHAIN]      = AUDIT_NFT_OP_CHAIN_REGISTER,
+       [NFT_MSG_GETCHAIN]      = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_DELCHAIN]      = AUDIT_NFT_OP_CHAIN_UNREGISTER,
+       [NFT_MSG_NEWRULE]       = AUDIT_NFT_OP_RULE_REGISTER,
+       [NFT_MSG_GETRULE]       = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_DELRULE]       = AUDIT_NFT_OP_RULE_UNREGISTER,
+       [NFT_MSG_NEWSET]        = AUDIT_NFT_OP_SET_REGISTER,
+       [NFT_MSG_GETSET]        = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_DELSET]        = AUDIT_NFT_OP_SET_UNREGISTER,
+       [NFT_MSG_NEWSETELEM]    = AUDIT_NFT_OP_SETELEM_REGISTER,
+       [NFT_MSG_GETSETELEM]    = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_DELSETELEM]    = AUDIT_NFT_OP_SETELEM_UNREGISTER,
+       [NFT_MSG_NEWGEN]        = AUDIT_NFT_OP_GEN_REGISTER,
+       [NFT_MSG_GETGEN]        = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_TRACE]         = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_NEWOBJ]        = AUDIT_NFT_OP_OBJ_REGISTER,
+       [NFT_MSG_GETOBJ]        = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_DELOBJ]        = AUDIT_NFT_OP_OBJ_UNREGISTER,
+       [NFT_MSG_GETOBJ_RESET]  = AUDIT_NFT_OP_OBJ_RESET,
+       [NFT_MSG_NEWFLOWTABLE]  = AUDIT_NFT_OP_FLOWTABLE_REGISTER,
+       [NFT_MSG_GETFLOWTABLE]  = AUDIT_NFT_OP_INVALID,
+       [NFT_MSG_DELFLOWTABLE]  = AUDIT_NFT_OP_FLOWTABLE_UNREGISTER,
+};
+
 static void nft_validate_state_update(struct net *net, u8 new_validate_state)
 {
-       switch (net->nft.validate_state) {
+       struct nftables_pernet *nft_net = nft_pernet(net);
+
+       switch (nft_net->validate_state) {
        case NFT_VALIDATE_SKIP:
                WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO);
                break;
@@ -79,7 +118,7 @@ static void nft_validate_state_update(struct net *net, u8 new_validate_state)
                        return;
        }
 
-       net->nft.validate_state = new_validate_state;
+       nft_net->validate_state = new_validate_state;
 }
 static void nf_tables_trans_destroy_work(struct work_struct *w);
 static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work);
@@ -134,13 +173,15 @@ static void nft_trans_destroy(struct nft_trans *trans)
 
 static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
 {
+       struct nftables_pernet *nft_net;
        struct net *net = ctx->net;
        struct nft_trans *trans;
 
        if (!nft_set_is_anonymous(set))
                return;
 
-       list_for_each_entry_reverse(trans, &net->nft.commit_list, list) {
+       nft_net = nft_pernet(net);
+       list_for_each_entry_reverse(trans, &nft_net->commit_list, list) {
                switch (trans->msg_type) {
                case NFT_MSG_NEWSET:
                        if (nft_trans_set(trans) == set)
@@ -234,6 +275,13 @@ static void nf_tables_unregister_hook(struct net *net,
                nf_unregister_net_hook(net, &basechain->ops);
 }
 
+static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans)
+{
+       struct nftables_pernet *nft_net = nft_pernet(net);
+
+       list_add_tail(&trans->list, &nft_net->commit_list);
+}
+
 static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
 {
        struct nft_trans *trans;
@@ -245,7 +293,7 @@ static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
        if (msg_type == NFT_MSG_NEWTABLE)
                nft_activate_next(ctx->net, ctx->table);
 
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
        return 0;
 }
 
@@ -278,7 +326,7 @@ static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
                }
        }
 
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
        return trans;
 }
 
@@ -351,7 +399,7 @@ static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
                        ntohl(nla_get_be32(ctx->nla[NFTA_RULE_ID]));
        }
        nft_trans_rule(trans) = rule;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return trans;
 }
@@ -417,7 +465,7 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
                nft_activate_next(ctx->net, set);
        }
        nft_trans_set(trans) = set;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 }
@@ -449,7 +497,7 @@ static int nft_trans_obj_add(struct nft_ctx *ctx, int msg_type,
                nft_activate_next(ctx->net, obj);
 
        nft_trans_obj(trans) = obj;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 }
@@ -482,7 +530,7 @@ static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
                nft_activate_next(ctx->net, flowtable);
 
        nft_trans_flowtable(trans) = flowtable;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 }
@@ -510,13 +558,15 @@ static struct nft_table *nft_table_lookup(const struct net *net,
                                          const struct nlattr *nla,
                                          u8 family, u8 genmask, u32 nlpid)
 {
+       struct nftables_pernet *nft_net;
        struct nft_table *table;
 
        if (nla == NULL)
                return ERR_PTR(-EINVAL);
 
-       list_for_each_entry_rcu(table, &net->nft.tables, list,
-                               lockdep_is_held(&net->nft.commit_mutex)) {
+       nft_net = nft_pernet(net);
+       list_for_each_entry_rcu(table, &nft_net->tables, list,
+                               lockdep_is_held(&nft_net->commit_mutex)) {
                if (!nla_strcmp(nla, table->name) &&
                    table->family == family &&
                    nft_active_genmask(table, genmask)) {
@@ -535,9 +585,11 @@ static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
                                                   const struct nlattr *nla,
                                                   u8 genmask)
 {
+       struct nftables_pernet *nft_net;
        struct nft_table *table;
 
-       list_for_each_entry(table, &net->nft.tables, list) {
+       nft_net = nft_pernet(net);
+       list_for_each_entry(table, &nft_net->tables, list) {
                if (be64_to_cpu(nla_get_be64(nla)) == table->handle &&
                    nft_active_genmask(table, genmask))
                        return table;
@@ -586,10 +638,11 @@ struct nft_module_request {
 };
 
 #ifdef CONFIG_MODULES
-static __printf(2, 3) int nft_request_module(struct net *net, const char *fmt,
-                                            ...)
+__printf(2, 3) int nft_request_module(struct net *net, const char *fmt,
+                                     ...)
 {
        char module_name[MODULE_NAME_LEN];
+       struct nftables_pernet *nft_net;
        struct nft_module_request *req;
        va_list args;
        int ret;
@@ -600,7 +653,8 @@ static __printf(2, 3) int nft_request_module(struct net *net, const char *fmt,
        if (ret >= MODULE_NAME_LEN)
                return 0;
 
-       list_for_each_entry(req, &net->nft.module_list, list) {
+       nft_net = nft_pernet(net);
+       list_for_each_entry(req, &nft_net->module_list, list) {
                if (!strcmp(req->module, module_name)) {
                        if (req->done)
                                return 0;
@@ -616,10 +670,11 @@ static __printf(2, 3) int nft_request_module(struct net *net, const char *fmt,
 
        req->done = false;
        strlcpy(req->module, module_name, MODULE_NAME_LEN);
-       list_add_tail(&req->list, &net->nft.module_list);
+       list_add_tail(&req->list, &nft_net->module_list);
 
        return -EAGAIN;
 }
+EXPORT_SYMBOL_GPL(nft_request_module);
 #endif
 
 static void lockdep_nfnl_nft_mutex_not_held(void)
@@ -652,6 +707,13 @@ nf_tables_chain_type_lookup(struct net *net, const struct nlattr *nla,
        return ERR_PTR(-ENOENT);
 }
 
+static __be16 nft_base_seq(const struct net *net)
+{
+       struct nftables_pernet *nft_net = nft_pernet(net);
+
+       return htons(nft_net->base_seq & 0xffff);
+}
+
 static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
        [NFTA_TABLE_NAME]       = { .type = NLA_STRING,
                                    .len = NFT_TABLE_MAXNAMELEN - 1 },
@@ -666,18 +728,13 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
                                     int family, const struct nft_table *table)
 {
        struct nlmsghdr *nlh;
-       struct nfgenmsg *nfmsg;
 
        event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, flags, family,
+                          NFNETLINK_V0, nft_base_seq(net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = family;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
            nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) ||
            nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)) ||
@@ -715,19 +772,9 @@ static void nft_notify_enqueue(struct sk_buff *skb, bool report,
 
 static void nf_tables_table_notify(const struct nft_ctx *ctx, int event)
 {
+       struct nftables_pernet *nft_net;
        struct sk_buff *skb;
        int err;
-       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;?:0",
-                             ctx->table->name, ctx->table->handle);
-
-       audit_log_nfcfg(buf,
-                       ctx->family,
-                       ctx->table->use,
-                       event == NFT_MSG_NEWTABLE ?
-                               AUDIT_NFT_OP_TABLE_REGISTER :
-                               AUDIT_NFT_OP_TABLE_UNREGISTER,
-                       GFP_KERNEL);
-       kfree(buf);
 
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@ -744,7 +791,8 @@ static void nf_tables_table_notify(const struct nft_ctx *ctx, int event)
                goto err;
        }
 
-       nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list);
+       nft_net = nft_pernet(ctx->net);
+       nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list);
        return;
 err:
        nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
@@ -754,15 +802,17 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
                                 struct netlink_callback *cb)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+       struct nftables_pernet *nft_net;
        const struct nft_table *table;
        unsigned int idx = 0, s_idx = cb->args[0];
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
 
        rcu_read_lock();
-       cb->seq = net->nft.base_seq;
+       nft_net = nft_pernet(net);
+       cb->seq = nft_net->base_seq;
 
-       list_for_each_entry_rcu(table, &net->nft.tables, list) {
+       list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
                        continue;
 
@@ -808,25 +858,25 @@ static int nft_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
 }
 
 /* called with rcu_read_lock held */
-static int nf_tables_gettable(struct net *net, struct sock *nlsk,
-                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct netlink_ext_ack *extack)
+static int nf_tables_gettable(struct sk_buff *skb, const struct nfnl_info *info,
+                             const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_cur(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       int family = nfmsg->nfgen_family;
        const struct nft_table *table;
+       struct net *net = info->net;
        struct sk_buff *skb2;
-       int family = nfmsg->nfgen_family;
        int err;
 
-       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+       if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_tables,
                        .module = THIS_MODULE,
                };
 
-               return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
+               return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
        }
 
        table = nft_table_lookup(net, nla[NFTA_TABLE_NAME], family, genmask, 0);
@@ -840,8 +890,8 @@ static int nf_tables_gettable(struct net *net, struct sock *nlsk,
                return -ENOMEM;
 
        err = nf_tables_fill_table_info(skb2, net, NETLINK_CB(skb).portid,
-                                       nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
-                                       family, table);
+                                       info->nlh->nlmsg_seq, NFT_MSG_NEWTABLE,
+                                       0, family, table);
        if (err < 0)
                goto err_fill_table_info;
 
@@ -900,6 +950,12 @@ static void nf_tables_table_disable(struct net *net, struct nft_table *table)
        nft_table_disable(net, table, 0);
 }
 
+enum {
+       NFT_TABLE_STATE_UNCHANGED       = 0,
+       NFT_TABLE_STATE_DORMANT,
+       NFT_TABLE_STATE_WAKEUP
+};
+
 static int nf_tables_updtable(struct nft_ctx *ctx)
 {
        struct nft_trans *trans;
@@ -929,21 +985,19 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
 
        if ((flags & NFT_TABLE_F_DORMANT) &&
            !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
-               nft_trans_table_enable(trans) = false;
+               nft_trans_table_state(trans) = NFT_TABLE_STATE_DORMANT;
        } else if (!(flags & NFT_TABLE_F_DORMANT) &&
                   ctx->table->flags & NFT_TABLE_F_DORMANT) {
-               ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
                ret = nf_tables_table_enable(ctx->net, ctx->table);
                if (ret >= 0)
-                       nft_trans_table_enable(trans) = true;
-               else
-                       ctx->table->flags |= NFT_TABLE_F_DORMANT;
+                       nft_trans_table_state(trans) = NFT_TABLE_STATE_WAKEUP;
        }
        if (ret < 0)
                goto err;
 
+       nft_trans_table_flags(trans) = flags;
        nft_trans_table_update(trans) = true;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
        return 0;
 err:
        nft_trans_destroy(trans);
@@ -1001,21 +1055,22 @@ static int nft_objname_hash_cmp(struct rhashtable_compare_arg *arg,
        return strcmp(obj->key.name, k->name);
 }
 
-static int nf_tables_newtable(struct net *net, struct sock *nlsk,
-                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct netlink_ext_ack *extack)
+static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
+                             const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       struct nftables_pernet *nft_net = nft_pernet(info->net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        int family = nfmsg->nfgen_family;
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_table *table;
        struct nft_ctx ctx;
        u32 flags = 0;
        int err;
 
-       lockdep_assert_held(&net->nft.commit_mutex);
+       lockdep_assert_held(&nft_net->commit_mutex);
        attr = nla[NFTA_TABLE_NAME];
        table = nft_table_lookup(net, attr, family, genmask,
                                 NETLINK_CB(skb).portid);
@@ -1023,14 +1078,15 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
                if (PTR_ERR(table) != -ENOENT)
                        return PTR_ERR(table);
        } else {
-               if (nlh->nlmsg_flags & NLM_F_EXCL) {
+               if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
                        NL_SET_BAD_ATTR(extack, attr);
                        return -EEXIST;
                }
-               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+               if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
 
-               nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+               nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
+
                return nf_tables_updtable(&ctx);
        }
 
@@ -1071,12 +1127,12 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
        if (table->flags & NFT_TABLE_F_OWNER)
                table->nlpid = NETLINK_CB(skb).portid;
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
        err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
        if (err < 0)
                goto err_trans;
 
-       list_add_tail_rcu(&table->list, &net->nft.tables);
+       list_add_tail_rcu(&table->list, &nft_net->tables);
        return 0;
 err_trans:
        rhltable_destroy(&table->chains_ht);
@@ -1164,11 +1220,12 @@ out:
 
 static int nft_flush(struct nft_ctx *ctx, int family)
 {
-       struct nft_table *table, *nt;
+       struct nftables_pernet *nft_net = nft_pernet(ctx->net);
        const struct nlattr * const *nla = ctx->nla;
+       struct nft_table *table, *nt;
        int err = 0;
 
-       list_for_each_entry_safe(table, nt, &ctx->net->nft.tables, list) {
+       list_for_each_entry_safe(table, nt, &nft_net->tables, list) {
                if (family != AF_UNSPEC && table->family != family)
                        continue;
 
@@ -1194,19 +1251,19 @@ out:
        return err;
 }
 
-static int nf_tables_deltable(struct net *net, struct sock *nlsk,
-                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct netlink_ext_ack *extack)
+static int nf_tables_deltable(struct sk_buff *skb, const struct nfnl_info *info,
+                             const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        int family = nfmsg->nfgen_family;
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_table *table;
        struct nft_ctx ctx;
 
-       nft_ctx_init(&ctx, net, skb, nlh, 0, NULL, NULL, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, 0, NULL, NULL, nla);
        if (family == AF_UNSPEC ||
            (!nla[NFTA_TABLE_NAME] && !nla[NFTA_TABLE_HANDLE]))
                return nft_flush(&ctx, family);
@@ -1225,7 +1282,7 @@ static int nf_tables_deltable(struct net *net, struct sock *nlsk,
                return PTR_ERR(table);
        }
 
-       if (nlh->nlmsg_flags & NLM_F_NONREC &&
+       if (info->nlh->nlmsg_flags & NLM_F_NONREC &&
            table->use > 0)
                return -EBUSY;
 
@@ -1287,7 +1344,9 @@ nft_chain_lookup_byhandle(const struct nft_table *table, u64 handle, u8 genmask)
 static bool lockdep_commit_lock_is_held(const struct net *net)
 {
 #ifdef CONFIG_PROVE_LOCKING
-       return lockdep_is_held(&net->nft.commit_mutex);
+       struct nftables_pernet *nft_net = nft_pernet(net);
+
+       return lockdep_is_held(&nft_net->commit_mutex);
 #else
        return true;
 #endif
@@ -1434,18 +1493,13 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
                                     const struct nft_chain *chain)
 {
        struct nlmsghdr *nlh;
-       struct nfgenmsg *nfmsg;
 
        event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, flags, family,
+                          NFNETLINK_V0, nft_base_seq(net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = family;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name))
                goto nla_put_failure;
        if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle),
@@ -1495,20 +1549,9 @@ nla_put_failure:
 
 static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
 {
+       struct nftables_pernet *nft_net;
        struct sk_buff *skb;
        int err;
-       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
-                             ctx->table->name, ctx->table->handle,
-                             ctx->chain->name, ctx->chain->handle);
-
-       audit_log_nfcfg(buf,
-                       ctx->family,
-                       ctx->chain->use,
-                       event == NFT_MSG_NEWCHAIN ?
-                               AUDIT_NFT_OP_CHAIN_REGISTER :
-                               AUDIT_NFT_OP_CHAIN_UNREGISTER,
-                       GFP_KERNEL);
-       kfree(buf);
 
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@ -1526,7 +1569,8 @@ static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
                goto err;
        }
 
-       nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list);
+       nft_net = nft_pernet(ctx->net);
+       nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list);
        return;
 err:
        nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
@@ -1536,16 +1580,18 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
                                 struct netlink_callback *cb)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
-       const struct nft_table *table;
-       const struct nft_chain *chain;
        unsigned int idx = 0, s_idx = cb->args[0];
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
+       struct nftables_pernet *nft_net;
+       const struct nft_table *table;
+       const struct nft_chain *chain;
 
        rcu_read_lock();
-       cb->seq = net->nft.base_seq;
+       nft_net = nft_pernet(net);
+       cb->seq = nft_net->base_seq;
 
-       list_for_each_entry_rcu(table, &net->nft.tables, list) {
+       list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
                        continue;
 
@@ -1578,26 +1624,26 @@ done:
 }
 
 /* called with rcu_read_lock held */
-static int nf_tables_getchain(struct net *net, struct sock *nlsk,
-                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct netlink_ext_ack *extack)
+static int nf_tables_getchain(struct sk_buff *skb, const struct nfnl_info *info,
+                             const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_cur(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       int family = nfmsg->nfgen_family;
        const struct nft_chain *chain;
+       struct net *net = info->net;
        struct nft_table *table;
        struct sk_buff *skb2;
-       int family = nfmsg->nfgen_family;
        int err;
 
-       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+       if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_chains,
                        .module = THIS_MODULE,
                };
 
-               return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
+               return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
        }
 
        table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask, 0);
@@ -1617,8 +1663,8 @@ static int nf_tables_getchain(struct net *net, struct sock *nlsk,
                return -ENOMEM;
 
        err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid,
-                                       nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
-                                       family, table, chain);
+                                       info->nlh->nlmsg_seq, NFT_MSG_NEWCHAIN,
+                                       0, family, table, chain);
        if (err < 0)
                goto err_fill_chain_info;
 
@@ -1861,11 +1907,12 @@ static int nft_chain_parse_hook(struct net *net,
                                struct nft_chain_hook *hook, u8 family,
                                bool autoload)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nlattr *ha[NFTA_HOOK_MAX + 1];
        const struct nft_chain_type *type;
        int err;
 
-       lockdep_assert_held(&net->nft.commit_mutex);
+       lockdep_assert_held(&nft_net->commit_mutex);
        lockdep_nfnl_nft_mutex_not_held();
 
        err = nla_parse_nested_deprecated(ha, NFTA_HOOK_MAX,
@@ -2254,6 +2301,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
 
        if (nla[NFTA_CHAIN_HANDLE] &&
            nla[NFTA_CHAIN_NAME]) {
+               struct nftables_pernet *nft_net = nft_pernet(ctx->net);
                struct nft_trans *tmp;
                char *name;
 
@@ -2263,7 +2311,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
                        goto err;
 
                err = -EEXIST;
-               list_for_each_entry(tmp, &ctx->net->nft.commit_list, list) {
+               list_for_each_entry(tmp, &nft_net->commit_list, list) {
                        if (tmp->msg_type == NFT_MSG_NEWCHAIN &&
                            tmp->ctx.table == table &&
                            nft_trans_chain_update(tmp) &&
@@ -2277,7 +2325,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
 
                nft_trans_chain_name(trans) = name;
        }
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 err:
@@ -2289,10 +2337,11 @@ err:
 static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
                                               const struct nlattr *nla)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        u32 id = ntohl(nla_get_be32(nla));
        struct nft_trans *trans;
 
-       list_for_each_entry(trans, &net->nft.commit_list, list) {
+       list_for_each_entry(trans, &nft_net->commit_list, list) {
                struct nft_chain *chain = trans->ctx.chain;
 
                if (trans->msg_type == NFT_MSG_NEWCHAIN &&
@@ -2302,15 +2351,16 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
        return ERR_PTR(-ENOENT);
 }
 
-static int nf_tables_newchain(struct net *net, struct sock *nlsk,
-                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct netlink_ext_ack *extack)
+static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
+                             const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       struct nftables_pernet *nft_net = nft_pernet(info->net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        int family = nfmsg->nfgen_family;
        struct nft_chain *chain = NULL;
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_table *table;
        u8 policy = NF_ACCEPT;
@@ -2318,7 +2368,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
        u64 handle = 0;
        u32 flags = 0;
 
-       lockdep_assert_held(&net->nft.commit_mutex);
+       lockdep_assert_held(&nft_net->commit_mutex);
 
        table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask,
                                 NETLINK_CB(skb).portid);
@@ -2382,14 +2432,14 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
        if (flags & ~NFT_CHAIN_FLAGS)
                return -EOPNOTSUPP;
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);
 
        if (chain != NULL) {
-               if (nlh->nlmsg_flags & NLM_F_EXCL) {
+               if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
                        NL_SET_BAD_ATTR(extack, attr);
                        return -EEXIST;
                }
-               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+               if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
 
                flags |= chain->flags & NFT_CHAIN_BASE;
@@ -2400,14 +2450,14 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
        return nf_tables_addchain(&ctx, family, genmask, policy, flags);
 }
 
-static int nf_tables_delchain(struct net *net, struct sock *nlsk,
-                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct netlink_ext_ack *extack)
+static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info,
+                             const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        int family = nfmsg->nfgen_family;
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_table *table;
        struct nft_chain *chain;
@@ -2437,11 +2487,11 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
                return PTR_ERR(chain);
        }
 
-       if (nlh->nlmsg_flags & NLM_F_NONREC &&
+       if (info->nlh->nlmsg_flags & NLM_F_NONREC &&
            chain->use > 0)
                return -EBUSY;
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);
 
        use = chain->use;
        list_for_each_entry(rule, &chain->rules, list) {
@@ -2664,15 +2714,15 @@ err1:
 }
 
 static int nf_tables_newexpr(const struct nft_ctx *ctx,
-                            const struct nft_expr_info *info,
+                            const struct nft_expr_info *expr_info,
                             struct nft_expr *expr)
 {
-       const struct nft_expr_ops *ops = info->ops;
+       const struct nft_expr_ops *ops = expr_info->ops;
        int err;
 
        expr->ops = ops;
        if (ops->init) {
-               err = ops->init(ctx, expr, (const struct nlattr **)info->tb);
+               err = ops->init(ctx, expr, (const struct nlattr **)expr_info->tb);
                if (err < 0)
                        goto err1;
        }
@@ -2696,21 +2746,21 @@ static void nf_tables_expr_destroy(const struct nft_ctx *ctx,
 static struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
                                      const struct nlattr *nla)
 {
-       struct nft_expr_info info;
+       struct nft_expr_info expr_info;
        struct nft_expr *expr;
        struct module *owner;
        int err;
 
-       err = nf_tables_expr_parse(ctx, nla, &info);
+       err = nf_tables_expr_parse(ctx, nla, &expr_info);
        if (err < 0)
                goto err1;
 
        err = -ENOMEM;
-       expr = kzalloc(info.ops->size, GFP_KERNEL);
+       expr = kzalloc(expr_info.ops->size, GFP_KERNEL);
        if (expr == NULL)
                goto err2;
 
-       err = nf_tables_newexpr(ctx, &info, expr);
+       err = nf_tables_newexpr(ctx, &expr_info, expr);
        if (err < 0)
                goto err3;
 
@@ -2718,9 +2768,9 @@ static struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
 err3:
        kfree(expr);
 err2:
-       owner = info.ops->type->owner;
-       if (info.ops->type->release_ops)
-               info.ops->type->release_ops(info.ops);
+       owner = expr_info.ops->type->owner;
+       if (expr_info.ops->type->release_ops)
+               expr_info.ops->type->release_ops(expr_info.ops);
 
        module_put(owner);
 err1:
@@ -2803,20 +2853,15 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
                                    const struct nft_rule *prule)
 {
        struct nlmsghdr *nlh;
-       struct nfgenmsg *nfmsg;
        const struct nft_expr *expr, *next;
        struct nlattr *list;
        u16 type = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
 
-       nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), flags);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, type, flags, family, NFNETLINK_V0,
+                          nft_base_seq(net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = family;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_RULE_TABLE, table->name))
                goto nla_put_failure;
        if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name))
@@ -2832,6 +2877,9 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
                        goto nla_put_failure;
        }
 
+       if (chain->flags & NFT_CHAIN_HW_OFFLOAD)
+               nft_flow_rule_stats(chain, rule);
+
        list = nla_nest_start_noflag(skb, NFTA_RULE_EXPRESSIONS);
        if (list == NULL)
                goto nla_put_failure;
@@ -2859,20 +2907,9 @@ nla_put_failure:
 static void nf_tables_rule_notify(const struct nft_ctx *ctx,
                                  const struct nft_rule *rule, int event)
 {
+       struct nftables_pernet *nft_net = nft_pernet(ctx->net);
        struct sk_buff *skb;
        int err;
-       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
-                             ctx->table->name, ctx->table->handle,
-                             ctx->chain->name, ctx->chain->handle);
-
-       audit_log_nfcfg(buf,
-                       ctx->family,
-                       rule->handle,
-                       event == NFT_MSG_NEWRULE ?
-                               AUDIT_NFT_OP_RULE_REGISTER :
-                               AUDIT_NFT_OP_RULE_UNREGISTER,
-                       GFP_KERNEL);
-       kfree(buf);
 
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@ -2890,7 +2927,7 @@ static void nf_tables_rule_notify(const struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list);
+       nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list);
        return;
 err:
        nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
@@ -2948,11 +2985,13 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
        unsigned int idx = 0;
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
+       struct nftables_pernet *nft_net;
 
        rcu_read_lock();
-       cb->seq = net->nft.base_seq;
+       nft_net = nft_pernet(net);
+       cb->seq = nft_net->base_seq;
 
-       list_for_each_entry_rcu(table, &net->nft.tables, list) {
+       list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
                        continue;
 
@@ -3038,21 +3077,21 @@ static int nf_tables_dump_rules_done(struct netlink_callback *cb)
 }
 
 /* called with rcu_read_lock held */
-static int nf_tables_getrule(struct net *net, struct sock *nlsk,
-                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const nla[],
-                            struct netlink_ext_ack *extack)
+static int nf_tables_getrule(struct sk_buff *skb, const struct nfnl_info *info,
+                            const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_cur(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       int family = nfmsg->nfgen_family;
        const struct nft_chain *chain;
        const struct nft_rule *rule;
+       struct net *net = info->net;
        struct nft_table *table;
        struct sk_buff *skb2;
-       int family = nfmsg->nfgen_family;
        int err;
 
-       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+       if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .start= nf_tables_dump_rules_start,
                        .dump = nf_tables_dump_rules,
@@ -3061,7 +3100,7 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk,
                        .data = (void *)nla,
                };
 
-               return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
+               return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
        }
 
        table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask, 0);
@@ -3087,7 +3126,7 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk,
                return -ENOMEM;
 
        err = nf_tables_fill_rule_info(skb2, net, NETLINK_CB(skb).portid,
-                                      nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
+                                      info->nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
                                       family, table, chain, rule, NULL);
        if (err < 0)
                goto err_fill_rule_info;
@@ -3178,29 +3217,30 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
 
 #define NFT_RULE_MAXEXPRS      128
 
-static int nf_tables_newrule(struct net *net, struct sock *nlsk,
-                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const nla[],
-                            struct netlink_ext_ack *extack)
+static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
+                            const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
-       struct nft_expr_info *info = NULL;
+       struct nftables_pernet *nft_net = nft_pernet(info->net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       unsigned int size, i, n, ulen = 0, usize = 0;
+       u8 genmask = nft_genmask_next(info->net);
+       struct nft_rule *rule, *old_rule = NULL;
+       struct nft_expr_info *expr_info = NULL;
        int family = nfmsg->nfgen_family;
+       struct net *net = info->net;
        struct nft_flow_rule *flow;
+       struct nft_userdata *udata;
        struct nft_table *table;
        struct nft_chain *chain;
-       struct nft_rule *rule, *old_rule = NULL;
-       struct nft_userdata *udata;
-       struct nft_trans *trans = NULL;
+       struct nft_trans *trans;
+       u64 handle, pos_handle;
        struct nft_expr *expr;
        struct nft_ctx ctx;
        struct nlattr *tmp;
-       unsigned int size, i, n, ulen = 0, usize = 0;
        int err, rem;
-       u64 handle, pos_handle;
 
-       lockdep_assert_held(&net->nft.commit_mutex);
+       lockdep_assert_held(&nft_net->commit_mutex);
 
        table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask,
                                 NETLINK_CB(skb).portid);
@@ -3237,17 +3277,17 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
                        return PTR_ERR(rule);
                }
 
-               if (nlh->nlmsg_flags & NLM_F_EXCL) {
+               if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
                        NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_HANDLE]);
                        return -EEXIST;
                }
-               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+               if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        old_rule = rule;
                else
                        return -EOPNOTSUPP;
        } else {
-               if (!(nlh->nlmsg_flags & NLM_F_CREATE) ||
-                   nlh->nlmsg_flags & NLM_F_REPLACE)
+               if (!(info->nlh->nlmsg_flags & NLM_F_CREATE) ||
+                   info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EINVAL;
                handle = nf_tables_alloc_handle(table);
 
@@ -3270,15 +3310,15 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
                }
        }
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);
 
        n = 0;
        size = 0;
        if (nla[NFTA_RULE_EXPRESSIONS]) {
-               info = kvmalloc_array(NFT_RULE_MAXEXPRS,
-                                     sizeof(struct nft_expr_info),
-                                     GFP_KERNEL);
-               if (!info)
+               expr_info = kvmalloc_array(NFT_RULE_MAXEXPRS,
+                                          sizeof(struct nft_expr_info),
+                                          GFP_KERNEL);
+               if (!expr_info)
                        return -ENOMEM;
 
                nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) {
@@ -3287,10 +3327,10 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
                                goto err1;
                        if (n == NFT_RULE_MAXEXPRS)
                                goto err1;
-                       err = nf_tables_expr_parse(&ctx, tmp, &info[n]);
+                       err = nf_tables_expr_parse(&ctx, tmp, &expr_info[n]);
                        if (err < 0)
                                goto err1;
-                       size += info[n].ops->size;
+                       size += expr_info[n].ops->size;
                        n++;
                }
        }
@@ -3324,20 +3364,20 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
 
        expr = nft_expr_first(rule);
        for (i = 0; i < n; i++) {
-               err = nf_tables_newexpr(&ctx, &info[i], expr);
+               err = nf_tables_newexpr(&ctx, &expr_info[i], expr);
                if (err < 0) {
-                       NL_SET_BAD_ATTR(extack, info[i].attr);
+                       NL_SET_BAD_ATTR(extack, expr_info[i].attr);
                        goto err2;
                }
 
-               if (info[i].ops->validate)
+               if (expr_info[i].ops->validate)
                        nft_validate_state_update(net, NFT_VALIDATE_NEED);
 
-               info[i].ops = NULL;
+               expr_info[i].ops = NULL;
                expr = nft_expr_next(expr);
        }
 
-       if (nlh->nlmsg_flags & NLM_F_REPLACE) {
+       if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
                trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
                if (trans == NULL) {
                        err = -ENOMEM;
@@ -3357,7 +3397,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
                        goto err2;
                }
 
-               if (nlh->nlmsg_flags & NLM_F_APPEND) {
+               if (info->nlh->nlmsg_flags & NLM_F_APPEND) {
                        if (old_rule)
                                list_add_rcu(&rule->list, &old_rule->list);
                        else
@@ -3369,10 +3409,10 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
                                list_add_rcu(&rule->list, &chain->rules);
                }
        }
-       kvfree(info);
+       kvfree(expr_info);
        chain->use++;
 
-       if (net->nft.validate_state == NFT_VALIDATE_DO)
+       if (nft_net->validate_state == NFT_VALIDATE_DO)
                return nft_table_validate(net, table);
 
        if (chain->flags & NFT_CHAIN_HW_OFFLOAD) {
@@ -3388,23 +3428,25 @@ err2:
        nf_tables_rule_release(&ctx, rule);
 err1:
        for (i = 0; i < n; i++) {
-               if (info[i].ops) {
-                       module_put(info[i].ops->type->owner);
-                       if (info[i].ops->type->release_ops)
-                               info[i].ops->type->release_ops(info[i].ops);
+               if (expr_info[i].ops) {
+                       module_put(expr_info[i].ops->type->owner);
+                       if (expr_info[i].ops->type->release_ops)
+                               expr_info[i].ops->type->release_ops(expr_info[i].ops);
                }
        }
-       kvfree(info);
+       kvfree(expr_info);
+
        return err;
 }
 
 static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
                                             const struct nlattr *nla)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        u32 id = ntohl(nla_get_be32(nla));
        struct nft_trans *trans;
 
-       list_for_each_entry(trans, &net->nft.commit_list, list) {
+       list_for_each_entry(trans, &nft_net->commit_list, list) {
                struct nft_rule *rule = nft_trans_rule(trans);
 
                if (trans->msg_type == NFT_MSG_NEWRULE &&
@@ -3414,17 +3456,17 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
        return ERR_PTR(-ENOENT);
 }
 
-static int nf_tables_delrule(struct net *net, struct sock *nlsk,
-                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const nla[],
-                            struct netlink_ext_ack *extack)
+static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
+                            const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
-       struct nft_table *table;
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       int family = nfmsg->nfgen_family, err = 0;
+       u8 genmask = nft_genmask_next(info->net);
        struct nft_chain *chain = NULL;
+       struct net *net = info->net;
+       struct nft_table *table;
        struct nft_rule *rule;
-       int family = nfmsg->nfgen_family, err = 0;
        struct nft_ctx ctx;
 
        table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask,
@@ -3445,7 +3487,7 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
                        return -EOPNOTSUPP;
        }
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);
 
        if (chain) {
                if (nla[NFTA_RULE_HANDLE]) {
@@ -3517,13 +3559,14 @@ nft_select_set_ops(const struct nft_ctx *ctx,
                   const struct nft_set_desc *desc,
                   enum nft_set_policies policy)
 {
+       struct nftables_pernet *nft_net = nft_pernet(ctx->net);
        const struct nft_set_ops *ops, *bops;
        struct nft_set_estimate est, best;
        const struct nft_set_type *type;
        u32 flags = 0;
        int i;
 
-       lockdep_assert_held(&ctx->net->nft.commit_mutex);
+       lockdep_assert_held(&nft_net->commit_mutex);
        lockdep_nfnl_nft_mutex_not_held();
 
        if (nla[NFTA_SET_FLAGS] != NULL)
@@ -3661,10 +3704,11 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table,
 static struct nft_set *nft_set_lookup_byid(const struct net *net,
                                           const struct nlattr *nla, u8 genmask)
 {
-       struct nft_trans *trans;
+       struct nftables_pernet *nft_net = nft_pernet(net);
        u32 id = ntohl(nla_get_be32(nla));
+       struct nft_trans *trans;
 
-       list_for_each_entry(trans, &net->nft.commit_list, list) {
+       list_for_each_entry(trans, &nft_net->commit_list, list) {
                if (trans->msg_type == NFT_MSG_NEWSET) {
                        struct nft_set *set = nft_trans_set(trans);
 
@@ -3799,7 +3843,6 @@ static int nf_tables_fill_set_concat(struct sk_buff *skb,
 static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                              const struct nft_set *set, u16 event, u16 flags)
 {
-       struct nfgenmsg *nfmsg;
        struct nlmsghdr *nlh;
        u32 portid = ctx->portid;
        struct nlattr *nest;
@@ -3807,16 +3850,11 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
        int i;
 
        event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
-                       flags);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, flags, ctx->family,
+                          NFNETLINK_V0, nft_base_seq(ctx->net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = ctx->family;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(ctx->net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
                goto nla_put_failure;
        if (nla_put_string(skb, NFTA_SET_NAME, set->name))
@@ -3904,21 +3942,10 @@ static void nf_tables_set_notify(const struct nft_ctx *ctx,
                                 const struct nft_set *set, int event,
                                 gfp_t gfp_flags)
 {
+       struct nftables_pernet *nft_net = nft_pernet(ctx->net);
        struct sk_buff *skb;
        u32 portid = ctx->portid;
        int err;
-       char *buf = kasprintf(gfp_flags, "%s:%llu;%s:%llu",
-                             ctx->table->name, ctx->table->handle,
-                             set->name, set->handle);
-
-       audit_log_nfcfg(buf,
-                       ctx->family,
-                       set->field_count,
-                       event == NFT_MSG_NEWSET ?
-                               AUDIT_NFT_OP_SET_REGISTER :
-                               AUDIT_NFT_OP_SET_UNREGISTER,
-                       gfp_flags);
-       kfree(buf);
 
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@ -3934,7 +3961,7 @@ static void nf_tables_set_notify(const struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list);
+       nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list);
        return;
 err:
        nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, -ENOBUFS);
@@ -3947,14 +3974,16 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
        struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
        struct net *net = sock_net(skb->sk);
        struct nft_ctx *ctx = cb->data, ctx_set;
+       struct nftables_pernet *nft_net;
 
        if (cb->args[1])
                return skb->len;
 
        rcu_read_lock();
-       cb->seq = net->nft.base_seq;
+       nft_net = nft_pernet(net);
+       cb->seq = nft_net->base_seq;
 
-       list_for_each_entry_rcu(table, &net->nft.tables, list) {
+       list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (ctx->family != NFPROTO_UNSPEC &&
                    ctx->family != table->family)
                        continue;
@@ -4018,25 +4047,25 @@ static int nf_tables_dump_sets_done(struct netlink_callback *cb)
 }
 
 /* called with rcu_read_lock held */
-static int nf_tables_getset(struct net *net, struct sock *nlsk,
-                           struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[],
-                           struct netlink_ext_ack *extack)
+static int nf_tables_getset(struct sk_buff *skb, const struct nfnl_info *info,
+                           const struct nlattr * const nla[])
 {
-       u8 genmask = nft_genmask_cur(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       struct net *net = info->net;
        const struct nft_set *set;
-       struct nft_ctx ctx;
        struct sk_buff *skb2;
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       struct nft_ctx ctx;
        int err;
 
        /* Verify existence before starting dump */
-       err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla, extack,
+       err = nft_ctx_init_from_setattr(&ctx, net, skb, info->nlh, nla, extack,
                                        genmask, 0);
        if (err < 0)
                return err;
 
-       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+       if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .start = nf_tables_dump_sets_start,
                        .dump = nf_tables_dump_sets,
@@ -4045,7 +4074,7 @@ static int nf_tables_getset(struct net *net, struct sock *nlsk,
                        .module = THIS_MODULE,
                };
 
-               return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
+               return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
        }
 
        /* Only accept unspec with dump */
@@ -4139,28 +4168,28 @@ static int nf_tables_set_desc_parse(struct nft_set_desc *desc,
        return err;
 }
 
-static int nf_tables_newset(struct net *net, struct sock *nlsk,
-                           struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[],
-                           struct netlink_ext_ack *extack)
+static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
+                           const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       u32 ktype, dtype, flags, policy, gc_int, objtype;
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        int family = nfmsg->nfgen_family;
        const struct nft_set_ops *ops;
        struct nft_expr *expr = NULL;
+       struct net *net = info->net;
+       struct nft_set_desc desc;
        struct nft_table *table;
+       unsigned char *udata;
        struct nft_set *set;
        struct nft_ctx ctx;
-       char *name;
-       u64 size;
+       size_t alloc_size;
        u64 timeout;
-       u32 ktype, dtype, flags, policy, gc_int, objtype;
-       struct nft_set_desc desc;
-       unsigned char *udata;
+       char *name;
+       int err, i;
        u16 udlen;
-       int err;
-       int i;
+       u64 size;
 
        if (nla[NFTA_SET_TABLE] == NULL ||
            nla[NFTA_SET_NAME] == NULL ||
@@ -4268,7 +4297,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                return PTR_ERR(table);
        }
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
        set = nft_set_lookup(table, nla[NFTA_SET_NAME], genmask);
        if (IS_ERR(set)) {
@@ -4277,17 +4306,17 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                        return PTR_ERR(set);
                }
        } else {
-               if (nlh->nlmsg_flags & NLM_F_EXCL) {
+               if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
                        NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]);
                        return -EEXIST;
                }
-               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+               if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
 
                return 0;
        }
 
-       if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+       if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
                return -ENOENT;
 
        ops = nft_select_set_ops(&ctx, nla, &desc, policy);
@@ -4301,8 +4330,10 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        size = 0;
        if (ops->privsize != NULL)
                size = ops->privsize(nla, &desc);
-
-       set = kvzalloc(sizeof(*set) + size + udlen, GFP_KERNEL);
+       alloc_size = sizeof(*set) + size + udlen;
+       if (alloc_size < size)
+               return -ENOMEM;
+       set = kvzalloc(alloc_size, GFP_KERNEL);
        if (!set)
                return -ENOMEM;
 
@@ -4361,6 +4392,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        }
 
        INIT_LIST_HEAD(&set->bindings);
+       INIT_LIST_HEAD(&set->catchall_list);
        set->table = table;
        write_pnet(&set->net, net);
        set->ops   = ops;
@@ -4406,6 +4438,24 @@ err_set_name:
        return err;
 }
 
+struct nft_set_elem_catchall {
+       struct list_head        list;
+       struct rcu_head         rcu;
+       void                    *elem;
+};
+
+static void nft_set_catchall_destroy(const struct nft_ctx *ctx,
+                                    struct nft_set *set)
+{
+       struct nft_set_elem_catchall *catchall;
+
+       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+               list_del_rcu(&catchall->list);
+               nft_set_elem_destroy(set, catchall->elem, true);
+               kfree_rcu(catchall);
+       }
+}
+
 static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
 {
        int i;
@@ -4417,17 +4467,18 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
                nft_expr_destroy(ctx, set->exprs[i]);
 
        set->ops->destroy(set);
+       nft_set_catchall_destroy(ctx, set);
        kfree(set->name);
        kvfree(set);
 }
 
-static int nf_tables_delset(struct net *net, struct sock *nlsk,
-                           struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[],
-                           struct netlink_ext_ack *extack)
+static int nf_tables_delset(struct sk_buff *skb, const struct nfnl_info *info,
+                           const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
@@ -4438,7 +4489,7 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk,
        if (nla[NFTA_SET_TABLE] == NULL)
                return -EINVAL;
 
-       err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla, extack,
+       err = nft_ctx_init_from_setattr(&ctx, net, skb, info->nlh, nla, extack,
                                        genmask, NETLINK_CB(skb).portid);
        if (err < 0)
                return err;
@@ -4456,7 +4507,8 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk,
                return PTR_ERR(set);
        }
        if (set->use ||
-           (nlh->nlmsg_flags & NLM_F_NONREC && atomic_read(&set->nelems) > 0)) {
+           (info->nlh->nlmsg_flags & NLM_F_NONREC &&
+            atomic_read(&set->nelems) > 0)) {
                NL_SET_BAD_ATTR(extack, attr);
                return -EBUSY;
        }
@@ -4470,10 +4522,9 @@ static int nft_validate_register_store(const struct nft_ctx *ctx,
                                       enum nft_data_types type,
                                       unsigned int len);
 
-static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
-                                       struct nft_set *set,
-                                       const struct nft_set_iter *iter,
-                                       struct nft_set_elem *elem)
+static int nft_setelem_data_validate(const struct nft_ctx *ctx,
+                                    struct nft_set *set,
+                                    struct nft_set_elem *elem)
 {
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        enum nft_registers dreg;
@@ -4485,6 +4536,37 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
                                           set->dlen);
 }
 
+static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
+                                       struct nft_set *set,
+                                       const struct nft_set_iter *iter,
+                                       struct nft_set_elem *elem)
+{
+       return nft_setelem_data_validate(ctx, set, elem);
+}
+
+static int nft_set_catchall_bind_check(const struct nft_ctx *ctx,
+                                      struct nft_set *set)
+{
+       u8 genmask = nft_genmask_next(ctx->net);
+       struct nft_set_elem_catchall *catchall;
+       struct nft_set_elem elem;
+       struct nft_set_ext *ext;
+       int ret = 0;
+
+       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_set_elem_active(ext, genmask))
+                       continue;
+
+               elem.priv = catchall->elem;
+               ret = nft_setelem_data_validate(ctx, set, &elem);
+               if (ret < 0)
+                       break;
+       }
+
+       return ret;
+}
+
 int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
                       struct nft_set_binding *binding)
 {
@@ -4514,6 +4596,9 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
                iter.fn         = nf_tables_bind_check_setelem;
 
                set->ops->walk(ctx, set, &iter);
+               if (!iter.err)
+                       iter.err = nft_set_catchall_bind_check(ctx, set);
+
                if (iter.err < 0)
                        return iter.err;
        }
@@ -4700,7 +4785,8 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
        if (nest == NULL)
                goto nla_put_failure;
 
-       if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext),
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
+           nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext),
                          NFT_DATA_VALUE, set->klen) < 0)
                goto nla_put_failure;
 
@@ -4789,22 +4875,46 @@ struct nft_set_dump_ctx {
        struct nft_ctx          ctx;
 };
 
+static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb,
+                                const struct nft_set *set)
+{
+       struct nft_set_elem_catchall *catchall;
+       u8 genmask = nft_genmask_cur(net);
+       struct nft_set_elem elem;
+       struct nft_set_ext *ext;
+       int ret = 0;
+
+       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_set_elem_active(ext, genmask) ||
+                   nft_set_elem_expired(ext))
+                       continue;
+
+               elem.priv = catchall->elem;
+               ret = nf_tables_fill_setelem(skb, set, &elem);
+               break;
+       }
+
+       return ret;
+}
+
 static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct nft_set_dump_ctx *dump_ctx = cb->data;
        struct net *net = sock_net(skb->sk);
+       struct nftables_pernet *nft_net;
        struct nft_table *table;
        struct nft_set *set;
        struct nft_set_dump_args args;
        bool set_found = false;
-       struct nfgenmsg *nfmsg;
        struct nlmsghdr *nlh;
        struct nlattr *nest;
        u32 portid, seq;
        int event;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(table, &net->nft.tables, list) {
+       nft_net = nft_pernet(net);
+       list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (dump_ctx->ctx.family != NFPROTO_UNSPEC &&
                    dump_ctx->ctx.family != table->family)
                        continue;
@@ -4830,16 +4940,11 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        portid = NETLINK_CB(cb->skb).portid;
        seq    = cb->nlh->nlmsg_seq;
 
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
-                       NLM_F_MULTI);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, NLM_F_MULTI,
+                          table->family, NFNETLINK_V0, nft_base_seq(net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family = table->family;
-       nfmsg->version      = NFNETLINK_V0;
-       nfmsg->res_id       = htons(net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, table->name))
                goto nla_put_failure;
        if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
@@ -4857,6 +4962,9 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        args.iter.err           = 0;
        args.iter.fn            = nf_tables_dump_setelem;
        set->ops->walk(&dump_ctx->ctx, set, &args.iter);
+
+       if (!args.iter.err && args.iter.count == cb->args[0])
+               args.iter.err = nft_set_catchall_dump(net, skb, set);
        rcu_read_unlock();
 
        nla_nest_end(skb, nest);
@@ -4896,22 +5004,16 @@ static int nf_tables_fill_setelem_info(struct sk_buff *skb,
                                       const struct nft_set *set,
                                       const struct nft_set_elem *elem)
 {
-       struct nfgenmsg *nfmsg;
        struct nlmsghdr *nlh;
        struct nlattr *nest;
        int err;
 
        event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
-                       flags);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, flags, ctx->family,
+                          NFNETLINK_V0, nft_base_seq(ctx->net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = ctx->family;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(ctx->net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
                goto nla_put_failure;
        if (nla_put_string(skb, NFTA_SET_NAME, set->name))
@@ -4942,8 +5044,8 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
                return 0;
 
        *flags = ntohl(nla_get_be32(attr));
-       if (*flags & ~NFT_SET_ELEM_INTERVAL_END)
-               return -EINVAL;
+       if (*flags & ~(NFT_SET_ELEM_INTERVAL_END | NFT_SET_ELEM_CATCHALL))
+               return -EOPNOTSUPP;
        if (!(set->flags & NFT_SET_INTERVAL) &&
            *flags & NFT_SET_ELEM_INTERVAL_END)
                return -EINVAL;
@@ -4988,6 +5090,46 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
        return 0;
 }
 
+static void *nft_setelem_catchall_get(const struct net *net,
+                                     const struct nft_set *set)
+{
+       struct nft_set_elem_catchall *catchall;
+       u8 genmask = nft_genmask_cur(net);
+       struct nft_set_ext *ext;
+       void *priv = NULL;
+
+       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_set_elem_active(ext, genmask) ||
+                   nft_set_elem_expired(ext))
+                       continue;
+
+               priv = catchall->elem;
+               break;
+       }
+
+       return priv;
+}
+
+static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set,
+                          struct nft_set_elem *elem, u32 flags)
+{
+       void *priv;
+
+       if (!(flags & NFT_SET_ELEM_CATCHALL)) {
+               priv = set->ops->get(ctx->net, set, elem, flags);
+               if (IS_ERR(priv))
+                       return PTR_ERR(priv);
+       } else {
+               priv = nft_setelem_catchall_get(ctx->net, set);
+               if (!priv)
+                       return -ENOENT;
+       }
+       elem->priv = priv;
+
+       return 0;
+}
+
 static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
@@ -4995,7 +5137,6 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_elem elem;
        struct sk_buff *skb;
        uint32_t flags = 0;
-       void *priv;
        int err;
 
        err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
@@ -5003,17 +5144,19 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                return err;
 
-       if (!nla[NFTA_SET_ELEM_KEY])
-               return -EINVAL;
-
        err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
        if (err < 0)
                return err;
 
-       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
-                                   nla[NFTA_SET_ELEM_KEY]);
-       if (err < 0)
-               return err;
+       if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
+               return -EINVAL;
+
+       if (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;
+       }
 
        if (nla[NFTA_SET_ELEM_KEY_END]) {
                err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
@@ -5022,11 +5165,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        return err;
        }
 
-       priv = set->ops->get(ctx->net, set, &elem, flags);
-       if (IS_ERR(priv))
-               return PTR_ERR(priv);
-
-       elem.priv = priv;
+       err = nft_setelem_get(ctx, set, &elem, flags);
+       if (err < 0)
+               return err;
 
        err = -ENOMEM;
        skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
@@ -5046,18 +5187,19 @@ err_fill_setelem:
 }
 
 /* called with rcu_read_lock held */
-static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
-                               struct sk_buff *skb, const struct nlmsghdr *nlh,
-                               const struct nlattr * const nla[],
-                               struct netlink_ext_ack *extack)
+static int nf_tables_getsetelem(struct sk_buff *skb,
+                               const struct nfnl_info *info,
+                               const struct nlattr * const nla[])
 {
-       u8 genmask = nft_genmask_cur(net);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       struct net *net = info->net;
        struct nft_set *set;
        struct nlattr *attr;
        struct nft_ctx ctx;
        int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, extack,
+       err = nft_ctx_init_from_elemattr(&ctx, net, skb, info->nlh, nla, extack,
                                         genmask, NETLINK_CB(skb).portid);
        if (err < 0)
                return err;
@@ -5066,7 +5208,7 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
        if (IS_ERR(set))
                return PTR_ERR(set);
 
-       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+       if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .start = nf_tables_dump_set_start,
                        .dump = nf_tables_dump_set,
@@ -5079,7 +5221,7 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
                };
 
                c.data = &dump_ctx;
-               return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
+               return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
        }
 
        if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
@@ -5099,22 +5241,11 @@ static void nf_tables_setelem_notify(const struct nft_ctx *ctx,
                                     const struct nft_set_elem *elem,
                                     int event, u16 flags)
 {
+       struct nftables_pernet *nft_net;
        struct net *net = ctx->net;
        u32 portid = ctx->portid;
        struct sk_buff *skb;
        int err;
-       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
-                             ctx->table->name, ctx->table->handle,
-                             set->name, set->handle);
-
-       audit_log_nfcfg(buf,
-                       ctx->family,
-                       set->handle,
-                       event == NFT_MSG_NEWSETELEM ?
-                               AUDIT_NFT_OP_SETELEM_REGISTER :
-                               AUDIT_NFT_OP_SETELEM_UNREGISTER,
-                       GFP_KERNEL);
-       kfree(buf);
 
        if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
                return;
@@ -5130,7 +5261,8 @@ static void nf_tables_setelem_notify(const struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list);
+       nft_net = nft_pernet(net);
+       nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list);
        return;
 err:
        nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS);
@@ -5195,7 +5327,8 @@ void *nft_set_elem_init(const struct nft_set *set,
        ext = nft_set_elem_ext(set, elem);
        nft_set_ext_init(ext, tmpl);
 
-       memcpy(nft_set_ext_key(ext), key, set->klen);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY))
+               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))
@@ -5253,8 +5386,8 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
 }
 EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
 
-/* Only called from commit path, nft_set_elem_deactivate() already deals with
- * the refcounting from the preparation phase.
+/* Only called from commit path, nft_setelem_data_deactivate() already deals
+ * with the refcounting from the preparation phase.
  */
 static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
                                       const struct nft_set *set, void *elem)
@@ -5326,6 +5459,192 @@ err_elem_expr_setup:
        return -ENOMEM;
 }
 
+struct nft_set_ext *nft_set_catchall_lookup(const struct net *net,
+                                           const struct nft_set *set)
+{
+       struct nft_set_elem_catchall *catchall;
+       u8 genmask = nft_genmask_cur(net);
+       struct nft_set_ext *ext;
+
+       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (nft_set_elem_active(ext, genmask) &&
+                   !nft_set_elem_expired(ext))
+                       return ext;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(nft_set_catchall_lookup);
+
+void *nft_set_catchall_gc(const struct nft_set *set)
+{
+       struct nft_set_elem_catchall *catchall, *next;
+       struct nft_set_ext *ext;
+       void *elem = NULL;
+
+       list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+
+               if (!nft_set_elem_expired(ext) ||
+                   nft_set_elem_mark_busy(ext))
+                       continue;
+
+               elem = catchall->elem;
+               list_del_rcu(&catchall->list);
+               kfree_rcu(catchall, rcu);
+               break;
+       }
+
+       return elem;
+}
+EXPORT_SYMBOL_GPL(nft_set_catchall_gc);
+
+static int nft_setelem_catchall_insert(const struct net *net,
+                                      struct nft_set *set,
+                                      const struct nft_set_elem *elem,
+                                      struct nft_set_ext **pext)
+{
+       struct nft_set_elem_catchall *catchall;
+       u8 genmask = nft_genmask_next(net);
+       struct nft_set_ext *ext;
+
+       list_for_each_entry(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (nft_set_elem_active(ext, genmask)) {
+                       *pext = ext;
+                       return -EEXIST;
+               }
+       }
+
+       catchall = kmalloc(sizeof(*catchall), GFP_KERNEL);
+       if (!catchall)
+               return -ENOMEM;
+
+       catchall->elem = elem->priv;
+       list_add_tail_rcu(&catchall->list, &set->catchall_list);
+
+       return 0;
+}
+
+static int nft_setelem_insert(const struct net *net,
+                             struct nft_set *set,
+                             const struct nft_set_elem *elem,
+                             struct nft_set_ext **ext, unsigned int flags)
+{
+       int ret;
+
+       if (flags & NFT_SET_ELEM_CATCHALL)
+               ret = nft_setelem_catchall_insert(net, set, elem, ext);
+       else
+               ret = set->ops->insert(net, set, elem, ext);
+
+       return ret;
+}
+
+static bool nft_setelem_is_catchall(const struct nft_set *set,
+                                   const struct nft_set_elem *elem)
+{
+       struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
+
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
+           *nft_set_ext_flags(ext) & NFT_SET_ELEM_CATCHALL)
+               return true;
+
+       return false;
+}
+
+static void nft_setelem_activate(struct net *net, struct nft_set *set,
+                                struct nft_set_elem *elem)
+{
+       struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
+
+       if (nft_setelem_is_catchall(set, elem)) {
+               nft_set_elem_change_active(net, set, ext);
+               nft_set_elem_clear_busy(ext);
+       } else {
+               set->ops->activate(net, set, elem);
+       }
+}
+
+static int nft_setelem_catchall_deactivate(const struct net *net,
+                                          struct nft_set *set,
+                                          struct nft_set_elem *elem)
+{
+       struct nft_set_elem_catchall *catchall;
+       struct nft_set_ext *ext;
+
+       list_for_each_entry(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_is_active(net, ext) ||
+                   nft_set_elem_mark_busy(ext))
+                       continue;
+
+               kfree(elem->priv);
+               elem->priv = catchall->elem;
+               nft_set_elem_change_active(net, set, ext);
+               return 0;
+       }
+
+       return -ENOENT;
+}
+
+static int __nft_setelem_deactivate(const struct net *net,
+                                   struct nft_set *set,
+                                   struct nft_set_elem *elem)
+{
+       void *priv;
+
+       priv = set->ops->deactivate(net, set, elem);
+       if (!priv)
+               return -ENOENT;
+
+       kfree(elem->priv);
+       elem->priv = priv;
+       set->ndeact++;
+
+       return 0;
+}
+
+static int nft_setelem_deactivate(const struct net *net,
+                                 struct nft_set *set,
+                                 struct nft_set_elem *elem, u32 flags)
+{
+       int ret;
+
+       if (flags & NFT_SET_ELEM_CATCHALL)
+               ret = nft_setelem_catchall_deactivate(net, set, elem);
+       else
+               ret = __nft_setelem_deactivate(net, set, elem);
+
+       return ret;
+}
+
+static void nft_setelem_catchall_remove(const struct net *net,
+                                       const struct nft_set *set,
+                                       const struct nft_set_elem *elem)
+{
+       struct nft_set_elem_catchall *catchall, *next;
+
+       list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
+               if (catchall->elem == elem->priv) {
+                       list_del_rcu(&catchall->list);
+                       kfree_rcu(catchall);
+                       break;
+               }
+       }
+}
+
+static void nft_setelem_remove(const struct net *net,
+                              const struct nft_set *set,
+                              const struct nft_set_elem *elem)
+{
+       if (nft_setelem_is_catchall(set, elem))
+               nft_setelem_catchall_remove(net, set, elem);
+       else
+               set->ops->remove(net, set, elem);
+}
+
 static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr, u32 nlmsg_flags)
 {
@@ -5352,14 +5671,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                return err;
 
-       if (nla[NFTA_SET_ELEM_KEY] == NULL)
-               return -EINVAL;
-
        nft_set_ext_prepare(&tmpl);
 
        err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
        if (err < 0)
                return err;
+
+       if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
+               return -EINVAL;
+
        if (flags != 0)
                nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
 
@@ -5464,12 +5784,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                num_exprs = set->num_exprs;
        }
 
-       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
-                                   nla[NFTA_SET_ELEM_KEY]);
-       if (err < 0)
-               goto err_set_elem_expr;
+       if (nla[NFTA_SET_ELEM_KEY]) {
+               err = nft_setelem_parse_key(ctx, set, &elem.key.val,
+                                           nla[NFTA_SET_ELEM_KEY]);
+               if (err < 0)
+                       goto err_set_elem_expr;
 
-       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+               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,
@@ -5586,7 +5908,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        }
 
        ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
-       err = set->ops->insert(ctx->net, set, &elem, &ext2);
+
+       err = nft_setelem_insert(ctx->net, set, &elem, &ext2, flags);
        if (err) {
                if (err == -EEXIST) {
                        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^
@@ -5613,18 +5936,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                goto err_element_clash;
        }
 
-       if (set->size &&
+       if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size &&
            !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) {
                err = -ENFILE;
                goto err_set_full;
        }
 
        nft_trans_elem(trans) = elem;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
        return 0;
 
 err_set_full:
-       set->ops->remove(ctx->net, set, &elem);
+       nft_setelem_remove(ctx->net, set, &elem);
 err_element_clash:
        kfree(trans);
 err_elem_expr:
@@ -5646,12 +5969,14 @@ err_set_elem_expr_clone:
        return err;
 }
 
-static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
-                               struct sk_buff *skb, const struct nlmsghdr *nlh,
-                               const struct nlattr * const nla[],
-                               struct netlink_ext_ack *extack)
+static int nf_tables_newsetelem(struct sk_buff *skb,
+                               const struct nfnl_info *info,
+                               const struct nlattr * const nla[])
 {
-       u8 genmask = nft_genmask_next(net);
+       struct nftables_pernet *nft_net = nft_pernet(info->net);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
@@ -5660,7 +5985,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
        if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
                return -EINVAL;
 
-       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, extack,
+       err = nft_ctx_init_from_elemattr(&ctx, net, skb, info->nlh, nla, extack,
                                         genmask, NETLINK_CB(skb).portid);
        if (err < 0)
                return err;
@@ -5674,12 +5999,12 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
                return -EBUSY;
 
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
-               err = nft_add_set_elem(&ctx, set, attr, nlh->nlmsg_flags);
+               err = nft_add_set_elem(&ctx, set, attr, info->nlh->nlmsg_flags);
                if (err < 0)
                        return err;
        }
 
-       if (net->nft.validate_state == NFT_VALIDATE_DO)
+       if (nft_net->validate_state == NFT_VALIDATE_DO)
                return nft_table_validate(net, ctx.table);
 
        return 0;
@@ -5721,9 +6046,9 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
        }
 }
 
-static void nft_set_elem_activate(const struct net *net,
-                                 const struct nft_set *set,
-                                 struct nft_set_elem *elem)
+static void nft_setelem_data_activate(const struct net *net,
+                                     const struct nft_set *set,
+                                     struct nft_set_elem *elem)
 {
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
 
@@ -5733,9 +6058,9 @@ static void nft_set_elem_activate(const struct net *net,
                (*nft_set_ext_obj(ext))->use++;
 }
 
-static void nft_set_elem_deactivate(const struct net *net,
-                                   const struct nft_set *set,
-                                   struct nft_set_elem *elem)
+static void nft_setelem_data_deactivate(const struct net *net,
+                                       const struct nft_set *set,
+                                       struct nft_set_elem *elem)
 {
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
 
@@ -5754,7 +6079,6 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_ext *ext;
        struct nft_trans *trans;
        u32 flags = 0;
-       void *priv;
        int err;
 
        err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
@@ -5762,23 +6086,26 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                return err;
 
-       if (nla[NFTA_SET_ELEM_KEY] == NULL)
+       err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
+       if (err < 0)
+               return err;
+
+       if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
                return -EINVAL;
 
        nft_set_ext_prepare(&tmpl);
 
-       err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
-       if (err < 0)
-               return err;
        if (flags != 0)
                nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
 
-       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
-                                   nla[NFTA_SET_ELEM_KEY]);
-       if (err < 0)
-               return err;
+       if (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;
 
-       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+               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,
@@ -5804,18 +6131,14 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        if (trans == NULL)
                goto fail_trans;
 
-       priv = set->ops->deactivate(ctx->net, set, &elem);
-       if (priv == NULL) {
-               err = -ENOENT;
+       err = nft_setelem_deactivate(ctx->net, set, &elem, flags);
+       if (err < 0)
                goto fail_ops;
-       }
-       kfree(elem.priv);
-       elem.priv = priv;
 
-       nft_set_elem_deactivate(ctx->net, set, &elem);
+       nft_setelem_data_deactivate(ctx->net, set, &elem);
 
        nft_trans_elem(trans) = elem;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
        return 0;
 
 fail_ops:
@@ -5827,10 +6150,10 @@ fail_elem:
        return err;
 }
 
-static int nft_flush_set(const struct nft_ctx *ctx,
-                        struct nft_set *set,
-                        const struct nft_set_iter *iter,
-                        struct nft_set_elem *elem)
+static int nft_setelem_flush(const struct nft_ctx *ctx,
+                            struct nft_set *set,
+                            const struct nft_set_iter *iter,
+                            struct nft_set_elem *elem)
 {
        struct nft_trans *trans;
        int err;
@@ -5846,10 +6169,10 @@ static int nft_flush_set(const struct nft_ctx *ctx,
        }
        set->ndeact++;
 
-       nft_set_elem_deactivate(ctx->net, set, elem);
+       nft_setelem_data_deactivate(ctx->net, set, elem);
        nft_trans_elem_set(trans) = set;
        nft_trans_elem(trans) = *elem;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 err1:
@@ -5857,18 +6180,76 @@ err1:
        return err;
 }
 
-static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
-                               struct sk_buff *skb, const struct nlmsghdr *nlh,
-                               const struct nlattr * const nla[],
-                               struct netlink_ext_ack *extack)
+static int __nft_set_catchall_flush(const struct nft_ctx *ctx,
+                                   struct nft_set *set,
+                                   struct nft_set_elem *elem)
 {
-       u8 genmask = nft_genmask_next(net);
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM,
+                                   sizeof(struct nft_trans_elem), GFP_KERNEL);
+       if (!trans)
+               return -ENOMEM;
+
+       nft_setelem_data_deactivate(ctx->net, set, elem);
+       nft_trans_elem_set(trans) = set;
+       nft_trans_elem(trans) = *elem;
+       nft_trans_commit_list_add_tail(ctx->net, trans);
+
+       return 0;
+}
+
+static int nft_set_catchall_flush(const struct nft_ctx *ctx,
+                                 struct nft_set *set)
+{
+       u8 genmask = nft_genmask_next(ctx->net);
+       struct nft_set_elem_catchall *catchall;
+       struct nft_set_elem elem;
+       struct nft_set_ext *ext;
+       int ret = 0;
+
+       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_set_elem_active(ext, genmask) ||
+                   nft_set_elem_mark_busy(ext))
+                       continue;
+
+               elem.priv = catchall->elem;
+               ret = __nft_set_catchall_flush(ctx, set, &elem);
+               if (ret < 0)
+                       break;
+       }
+
+       return ret;
+}
+
+static int nft_set_flush(struct nft_ctx *ctx, struct nft_set *set, u8 genmask)
+{
+       struct nft_set_iter iter = {
+               .genmask        = genmask,
+               .fn             = nft_setelem_flush,
+       };
+
+       set->ops->walk(ctx, set, &iter);
+       if (!iter.err)
+               iter.err = nft_set_catchall_flush(ctx, set);
+
+       return iter.err;
+}
+
+static int nf_tables_delsetelem(struct sk_buff *skb,
+                               const struct nfnl_info *info,
+                               const struct nlattr * const nla[])
+{
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
        int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, extack,
+       err = nft_ctx_init_from_elemattr(&ctx, net, skb, info->nlh, nla, extack,
                                         genmask, NETLINK_CB(skb).portid);
        if (err < 0)
                return err;
@@ -5879,22 +6260,13 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
        if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
                return -EBUSY;
 
-       if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) {
-               struct nft_set_iter iter = {
-                       .genmask        = genmask,
-                       .fn             = nft_flush_set,
-               };
-               set->ops->walk(&ctx, set, &iter);
-
-               return iter.err;
-       }
+       if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
+               return nft_set_flush(&ctx, set, genmask);
 
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_del_setelem(&ctx, set, attr);
                if (err < 0)
                        break;
-
-               set->ndeact++;
        }
        return err;
 }
@@ -6143,7 +6515,7 @@ static int nf_tables_updobj(const struct nft_ctx *ctx,
        nft_trans_obj(trans) = obj;
        nft_trans_obj_update(trans) = true;
        nft_trans_obj_newobj(trans) = newobj;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 
@@ -6152,15 +6524,15 @@ err_free_trans:
        return err;
 }
 
-static int nf_tables_newobj(struct net *net, struct sock *nlsk,
-                           struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[],
-                           struct netlink_ext_ack *extack)
+static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
+                           const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        const struct nft_object_type *type;
-       u8 genmask = nft_genmask_next(net);
        int family = nfmsg->nfgen_family;
+       struct net *net = info->net;
        struct nft_table *table;
        struct nft_object *obj;
        struct nft_ctx ctx;
@@ -6188,20 +6560,20 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
                        return err;
                }
        } else {
-               if (nlh->nlmsg_flags & NLM_F_EXCL) {
+               if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
                        NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_NAME]);
                        return -EEXIST;
                }
-               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+               if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
 
                type = __nft_obj_type_get(objtype);
-               nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+               nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
                return nf_tables_updobj(&ctx, type, nla[NFTA_OBJ_DATA], obj);
        }
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
        type = nft_obj_type_get(net, objtype);
        if (IS_ERR(type))
@@ -6246,9 +6618,9 @@ err_obj_ht:
        INIT_LIST_HEAD(&obj->list);
        return err;
 err_trans:
-       kfree(obj->key.name);
-err_userdata:
        kfree(obj->udata);
+err_userdata:
+       kfree(obj->key.name);
 err_strdup:
        if (obj->ops->destroy)
                obj->ops->destroy(&ctx, obj);
@@ -6263,19 +6635,14 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net,
                                   int family, const struct nft_table *table,
                                   struct nft_object *obj, bool reset)
 {
-       struct nfgenmsg *nfmsg;
        struct nlmsghdr *nlh;
 
        event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, flags, family,
+                          NFNETLINK_V0, nft_base_seq(net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = family;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_OBJ_TABLE, table->name) ||
            nla_put_string(skb, NFTA_OBJ_NAME, obj->key.name) ||
            nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->ops->type->type)) ||
@@ -6310,6 +6677,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
        struct nft_obj_filter *filter = cb->data;
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
+       struct nftables_pernet *nft_net;
        struct nft_object *obj;
        bool reset = false;
 
@@ -6317,9 +6685,10 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
                reset = true;
 
        rcu_read_lock();
-       cb->seq = net->nft.base_seq;
+       nft_net = nft_pernet(net);
+       cb->seq = nft_net->base_seq;
 
-       list_for_each_entry_rcu(table, &net->nft.tables, list) {
+       list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
                        continue;
 
@@ -6338,12 +6707,11 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
                            filter->type != NFT_OBJECT_UNSPEC &&
                            obj->ops->type->type != filter->type)
                                goto cont;
-
                        if (reset) {
                                char *buf = kasprintf(GFP_ATOMIC,
-                                                     "%s:%llu;?:0",
+                                                     "%s:%u",
                                                      table->name,
-                                                     table->handle);
+                                                     nft_net->base_seq);
 
                                audit_log_nfcfg(buf,
                                                family,
@@ -6412,22 +6780,22 @@ static int nf_tables_dump_obj_done(struct netlink_callback *cb)
 }
 
 /* called with rcu_read_lock held */
-static int nf_tables_getobj(struct net *net, struct sock *nlsk,
-                           struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[],
-                           struct netlink_ext_ack *extack)
+static int nf_tables_getobj(struct sk_buff *skb, const struct nfnl_info *info,
+                           const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_cur(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
        int family = nfmsg->nfgen_family;
        const struct nft_table *table;
+       struct net *net = info->net;
        struct nft_object *obj;
        struct sk_buff *skb2;
        bool reset = false;
        u32 objtype;
        int err;
 
-       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+       if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .start = nf_tables_dump_obj_start,
                        .dump = nf_tables_dump_obj,
@@ -6436,7 +6804,7 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk,
                        .data = (void *)nla,
                };
 
-               return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
+               return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
        }
 
        if (!nla[NFTA_OBJ_NAME] ||
@@ -6460,12 +6828,15 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk,
        if (!skb2)
                return -ENOMEM;
 
-       if (NFNL_MSG_TYPE(nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET)
+       if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET)
                reset = true;
 
        if (reset) {
-               char *buf = kasprintf(GFP_ATOMIC, "%s:%llu;?:0",
-                                     table->name, table->handle);
+               const struct nftables_pernet *nft_net;
+               char *buf;
+
+               nft_net = nft_pernet(net);
+               buf = kasprintf(GFP_ATOMIC, "%s:%u", table->name, nft_net->base_seq);
 
                audit_log_nfcfg(buf,
                                family,
@@ -6476,7 +6847,7 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk,
        }
 
        err = nf_tables_fill_obj_info(skb2, net, NETLINK_CB(skb).portid,
-                                     nlh->nlmsg_seq, NFT_MSG_NEWOBJ, 0,
+                                     info->nlh->nlmsg_seq, NFT_MSG_NEWOBJ, 0,
                                      family, table, obj, reset);
        if (err < 0)
                goto err_fill_obj_info;
@@ -6499,14 +6870,14 @@ static void nft_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
        kfree(obj);
 }
 
-static int nf_tables_delobj(struct net *net, struct sock *nlsk,
-                           struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[],
-                           struct netlink_ext_ack *extack)
+static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info,
+                           const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        int family = nfmsg->nfgen_family;
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_table *table;
        struct nft_object *obj;
@@ -6542,7 +6913,7 @@ static int nf_tables_delobj(struct net *net, struct sock *nlsk,
                return -EBUSY;
        }
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
        return nft_delobj(&ctx, obj);
 }
@@ -6551,17 +6922,18 @@ void nft_obj_notify(struct net *net, const struct nft_table *table,
                    struct nft_object *obj, u32 portid, u32 seq, int event,
                    int family, int report, gfp_t gfp)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct sk_buff *skb;
        int err;
-       char *buf = kasprintf(gfp, "%s:%llu;?:0",
-                             table->name, table->handle);
+       char *buf = kasprintf(gfp, "%s:%u",
+                             table->name, nft_net->base_seq);
 
        audit_log_nfcfg(buf,
                        family,
                        obj->handle,
                        event == NFT_MSG_NEWOBJ ?
-                               AUDIT_NFT_OP_OBJ_REGISTER :
-                               AUDIT_NFT_OP_OBJ_UNREGISTER,
+                                AUDIT_NFT_OP_OBJ_REGISTER :
+                                AUDIT_NFT_OP_OBJ_UNREGISTER,
                        gfp);
        kfree(buf);
 
@@ -6580,7 +6952,7 @@ void nft_obj_notify(struct net *net, const struct nft_table *table,
                goto err;
        }
 
-       nft_notify_enqueue(skb, report, &net->nft.notify_list);
+       nft_notify_enqueue(skb, report, &nft_net->notify_list);
        return;
 err:
        nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS);
@@ -6912,7 +7284,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
        INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
        list_splice(&flowtable_hook.list, &nft_trans_flowtable_hooks(trans));
 
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 
@@ -6928,19 +7300,19 @@ err_flowtable_update_hook:
 
 }
 
-static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
-                                 struct sk_buff *skb,
-                                 const struct nlmsghdr *nlh,
-                                 const struct nlattr * const nla[],
-                                 struct netlink_ext_ack *extack)
+static int nf_tables_newflowtable(struct sk_buff *skb,
+                                 const struct nfnl_info *info,
+                                 const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
        struct nft_flowtable_hook flowtable_hook;
+       u8 genmask = nft_genmask_next(info->net);
        const struct nf_flowtable_type *type;
-       u8 genmask = nft_genmask_next(net);
        int family = nfmsg->nfgen_family;
        struct nft_flowtable *flowtable;
        struct nft_hook *hook, *next;
+       struct net *net = info->net;
        struct nft_table *table;
        struct nft_ctx ctx;
        int err;
@@ -6966,17 +7338,17 @@ static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
                        return err;
                }
        } else {
-               if (nlh->nlmsg_flags & NLM_F_EXCL) {
+               if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
                        NL_SET_BAD_ATTR(extack, nla[NFTA_FLOWTABLE_NAME]);
                        return -EEXIST;
                }
 
-               nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+               nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
-               return nft_flowtable_update(&ctx, nlh, flowtable);
+               return nft_flowtable_update(&ctx, info->nlh, flowtable);
        }
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
        flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL);
        if (!flowtable)
@@ -7100,7 +7472,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
        INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
        nft_flowtable_hook_release(&flowtable_hook);
 
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       nft_trans_commit_list_add_tail(ctx->net, trans);
 
        return 0;
 
@@ -7117,16 +7489,16 @@ err_flowtable_del_hook:
        return err;
 }
 
-static int nf_tables_delflowtable(struct net *net, struct sock *nlsk,
-                                 struct sk_buff *skb,
-                                 const struct nlmsghdr *nlh,
-                                 const struct nlattr * const nla[],
-                                 struct netlink_ext_ack *extack)
+static int nf_tables_delflowtable(struct sk_buff *skb,
+                                 const struct nfnl_info *info,
+                                 const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_next(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_next(info->net);
        int family = nfmsg->nfgen_family;
        struct nft_flowtable *flowtable;
+       struct net *net = info->net;
        const struct nlattr *attr;
        struct nft_table *table;
        struct nft_ctx ctx;
@@ -7156,7 +7528,7 @@ static int nf_tables_delflowtable(struct net *net, struct sock *nlsk,
                return PTR_ERR(flowtable);
        }
 
-       nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
+       nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
        if (nla[NFTA_FLOWTABLE_HOOK])
                return nft_delflowtable_hook(&ctx, flowtable);
@@ -7176,20 +7548,15 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
                                         struct list_head *hook_list)
 {
        struct nlattr *nest, *nest_devs;
-       struct nfgenmsg *nfmsg;
        struct nft_hook *hook;
        struct nlmsghdr *nlh;
 
        event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, flags, family,
+                          NFNETLINK_V0, nft_base_seq(net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = family;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
-
        if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) ||
            nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||
            nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)) ||
@@ -7237,12 +7604,14 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb,
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        struct nft_flowtable *flowtable;
+       struct nftables_pernet *nft_net;
        const struct nft_table *table;
 
        rcu_read_lock();
-       cb->seq = net->nft.base_seq;
+       nft_net = nft_pernet(net);
+       cb->seq = nft_net->base_seq;
 
-       list_for_each_entry_rcu(table, &net->nft.tables, list) {
+       list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
                        continue;
 
@@ -7315,21 +7684,20 @@ static int nf_tables_dump_flowtable_done(struct netlink_callback *cb)
 }
 
 /* called with rcu_read_lock held */
-static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
-                                 struct sk_buff *skb,
-                                 const struct nlmsghdr *nlh,
-                                 const struct nlattr * const nla[],
-                                 struct netlink_ext_ack *extack)
+static int nf_tables_getflowtable(struct sk_buff *skb,
+                                 const struct nfnl_info *info,
+                                 const struct nlattr * const nla[])
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       u8 genmask = nft_genmask_cur(net);
+       const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
+       u8 genmask = nft_genmask_cur(info->net);
        int family = nfmsg->nfgen_family;
        struct nft_flowtable *flowtable;
        const struct nft_table *table;
+       struct net *net = info->net;
        struct sk_buff *skb2;
        int err;
 
-       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+       if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .start = nf_tables_dump_flowtable_start,
                        .dump = nf_tables_dump_flowtable,
@@ -7338,7 +7706,7 @@ static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
                        .data = (void *)nla,
                };
 
-               return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
+               return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
        }
 
        if (!nla[NFTA_FLOWTABLE_NAME])
@@ -7359,7 +7727,7 @@ static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
                return -ENOMEM;
 
        err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
-                                           nlh->nlmsg_seq,
+                                           info->nlh->nlmsg_seq,
                                            NFT_MSG_NEWFLOWTABLE, 0, family,
                                            flowtable, &flowtable->hook_list);
        if (err < 0)
@@ -7377,20 +7745,9 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
                                       struct list_head *hook_list,
                                       int event)
 {
+       struct nftables_pernet *nft_net = nft_pernet(ctx->net);
        struct sk_buff *skb;
        int err;
-       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
-                             flowtable->table->name, flowtable->table->handle,
-                             flowtable->name, flowtable->handle);
-
-       audit_log_nfcfg(buf,
-                       ctx->family,
-                       flowtable->hooknum,
-                       event == NFT_MSG_NEWFLOWTABLE ?
-                               AUDIT_NFT_OP_FLOWTABLE_REGISTER :
-                               AUDIT_NFT_OP_FLOWTABLE_UNREGISTER,
-                       GFP_KERNEL);
-       kfree(buf);
 
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@ -7408,7 +7765,7 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list);
+       nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list);
        return;
 err:
        nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
@@ -7433,21 +7790,17 @@ static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
 static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
                                   u32 portid, u32 seq)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nlmsghdr *nlh;
-       struct nfgenmsg *nfmsg;
        char buf[TASK_COMM_LEN];
        int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN);
 
-       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), 0);
-       if (nlh == NULL)
+       nlh = nfnl_msg_put(skb, portid, seq, event, 0, AF_UNSPEC,
+                          NFNETLINK_V0, nft_base_seq(net));
+       if (!nlh)
                goto nla_put_failure;
 
-       nfmsg = nlmsg_data(nlh);
-       nfmsg->nfgen_family     = AF_UNSPEC;
-       nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
-
-       if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)) ||
+       if (nla_put_be32(skb, NFTA_GEN_ID, htonl(nft_net->base_seq)) ||
            nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) ||
            nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current)))
                goto nla_put_failure;
@@ -7482,6 +7835,7 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct nft_flowtable *flowtable;
+       struct nftables_pernet *nft_net;
        struct nft_table *table;
        struct net *net;
 
@@ -7489,13 +7843,14 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
                return 0;
 
        net = dev_net(dev);
-       mutex_lock(&net->nft.commit_mutex);
-       list_for_each_entry(table, &net->nft.tables, list) {
+       nft_net = nft_pernet(net);
+       mutex_lock(&nft_net->commit_mutex);
+       list_for_each_entry(table, &nft_net->tables, list) {
                list_for_each_entry(flowtable, &table->flowtables, list) {
                        nft_flowtable_event(event, dev, flowtable);
                }
        }
-       mutex_unlock(&net->nft.commit_mutex);
+       mutex_unlock(&nft_net->commit_mutex);
 
        return NOTIFY_DONE;
 }
@@ -7511,9 +7866,6 @@ static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb,
        struct sk_buff *skb2;
        int err;
 
-       audit_log_nfcfg("?:0;?:0", 0, net->nft.base_seq,
-                       AUDIT_NFT_OP_GEN_REGISTER, GFP_KERNEL);
-
        if (!nlmsg_report(nlh) &&
            !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
                return;
@@ -7537,10 +7889,8 @@ err:
                          -ENOBUFS);
 }
 
-static int nf_tables_getgen(struct net *net, struct sock *nlsk,
-                           struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[],
-                           struct netlink_ext_ack *extack)
+static int nf_tables_getgen(struct sk_buff *skb, const struct nfnl_info *info,
+                           const struct nlattr * const nla[])
 {
        struct sk_buff *skb2;
        int err;
@@ -7549,12 +7899,12 @@ static int nf_tables_getgen(struct net *net, struct sock *nlsk,
        if (skb2 == NULL)
                return -ENOMEM;
 
-       err = nf_tables_fill_gen_info(skb2, net, NETLINK_CB(skb).portid,
-                                     nlh->nlmsg_seq);
+       err = nf_tables_fill_gen_info(skb2, info->net, NETLINK_CB(skb).portid,
+                                     info->nlh->nlmsg_seq);
        if (err < 0)
                goto err_fill_gen_info;
 
-       return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid);
+       return nfnetlink_unicast(skb2, info->net, NETLINK_CB(skb).portid);
 
 err_fill_gen_info:
        kfree_skb(skb2);
@@ -7563,115 +7913,138 @@ err_fill_gen_info:
 
 static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
        [NFT_MSG_NEWTABLE] = {
-               .call_batch     = nf_tables_newtable,
+               .call           = nf_tables_newtable,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
        [NFT_MSG_GETTABLE] = {
-               .call_rcu       = nf_tables_gettable,
+               .call           = nf_tables_gettable,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
        [NFT_MSG_DELTABLE] = {
-               .call_batch     = nf_tables_deltable,
+               .call           = nf_tables_deltable,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
        [NFT_MSG_NEWCHAIN] = {
-               .call_batch     = nf_tables_newchain,
+               .call           = nf_tables_newchain,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
        [NFT_MSG_GETCHAIN] = {
-               .call_rcu       = nf_tables_getchain,
+               .call           = nf_tables_getchain,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
        [NFT_MSG_DELCHAIN] = {
-               .call_batch     = nf_tables_delchain,
+               .call           = nf_tables_delchain,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
        [NFT_MSG_NEWRULE] = {
-               .call_batch     = nf_tables_newrule,
+               .call           = nf_tables_newrule,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_RULE_MAX,
                .policy         = nft_rule_policy,
        },
        [NFT_MSG_GETRULE] = {
-               .call_rcu       = nf_tables_getrule,
+               .call           = nf_tables_getrule,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_RULE_MAX,
                .policy         = nft_rule_policy,
        },
        [NFT_MSG_DELRULE] = {
-               .call_batch     = nf_tables_delrule,
+               .call           = nf_tables_delrule,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_RULE_MAX,
                .policy         = nft_rule_policy,
        },
        [NFT_MSG_NEWSET] = {
-               .call_batch     = nf_tables_newset,
+               .call           = nf_tables_newset,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
        [NFT_MSG_GETSET] = {
-               .call_rcu       = nf_tables_getset,
+               .call           = nf_tables_getset,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
        [NFT_MSG_DELSET] = {
-               .call_batch     = nf_tables_delset,
+               .call           = nf_tables_delset,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
        [NFT_MSG_NEWSETELEM] = {
-               .call_batch     = nf_tables_newsetelem,
+               .call           = nf_tables_newsetelem,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_GETSETELEM] = {
-               .call_rcu       = nf_tables_getsetelem,
+               .call           = nf_tables_getsetelem,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_DELSETELEM] = {
-               .call_batch     = nf_tables_delsetelem,
+               .call           = nf_tables_delsetelem,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_GETGEN] = {
-               .call_rcu       = nf_tables_getgen,
+               .call           = nf_tables_getgen,
+               .type           = NFNL_CB_RCU,
        },
        [NFT_MSG_NEWOBJ] = {
-               .call_batch     = nf_tables_newobj,
+               .call           = nf_tables_newobj,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_OBJ_MAX,
                .policy         = nft_obj_policy,
        },
        [NFT_MSG_GETOBJ] = {
-               .call_rcu       = nf_tables_getobj,
+               .call           = nf_tables_getobj,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_OBJ_MAX,
                .policy         = nft_obj_policy,
        },
        [NFT_MSG_DELOBJ] = {
-               .call_batch     = nf_tables_delobj,
+               .call           = nf_tables_delobj,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_OBJ_MAX,
                .policy         = nft_obj_policy,
        },
        [NFT_MSG_GETOBJ_RESET] = {
-               .call_rcu       = nf_tables_getobj,
+               .call           = nf_tables_getobj,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_OBJ_MAX,
                .policy         = nft_obj_policy,
        },
        [NFT_MSG_NEWFLOWTABLE] = {
-               .call_batch     = nf_tables_newflowtable,
+               .call           = nf_tables_newflowtable,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_FLOWTABLE_MAX,
                .policy         = nft_flowtable_policy,
        },
        [NFT_MSG_GETFLOWTABLE] = {
-               .call_rcu       = nf_tables_getflowtable,
+               .call           = nf_tables_getflowtable,
+               .type           = NFNL_CB_RCU,
                .attr_count     = NFTA_FLOWTABLE_MAX,
                .policy         = nft_flowtable_policy,
        },
        [NFT_MSG_DELFLOWTABLE] = {
-               .call_batch     = nf_tables_delflowtable,
+               .call           = nf_tables_delflowtable,
+               .type           = NFNL_CB_BATCH,
                .attr_count     = NFTA_FLOWTABLE_MAX,
                .policy         = nft_flowtable_policy,
        },
@@ -7679,16 +8052,17 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
 
 static int nf_tables_validate(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_table *table;
 
-       switch (net->nft.validate_state) {
+       switch (nft_net->validate_state) {
        case NFT_VALIDATE_SKIP:
                break;
        case NFT_VALIDATE_NEED:
                nft_validate_state_update(net, NFT_VALIDATE_DO);
                fallthrough;
        case NFT_VALIDATE_DO:
-               list_for_each_entry(table, &net->nft.tables, list) {
+               list_for_each_entry(table, &nft_net->tables, list) {
                        if (nft_table_validate(net, table) < 0)
                                return -EAGAIN;
                }
@@ -7863,9 +8237,10 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha
 
 static void nf_tables_commit_chain_prepare_cancel(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans, *next;
 
-       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+       list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
                struct nft_chain *chain = trans->ctx.chain;
 
                if (trans->msg_type == NFT_MSG_NEWRULE ||
@@ -7974,10 +8349,11 @@ static void nft_flowtable_hooks_del(struct nft_flowtable *flowtable,
 
 static void nf_tables_module_autoload_cleanup(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(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(!list_empty(&nft_net->commit_list));
+       list_for_each_entry_safe(req, next, &nft_net->module_list, list) {
                WARN_ON_ONCE(!req->done);
                list_del(&req->list);
                kfree(req);
@@ -7986,6 +8362,7 @@ static void nf_tables_module_autoload_cleanup(struct net *net)
 
 static void nf_tables_commit_release(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans;
 
        /* all side effects have to be made visible.
@@ -7995,35 +8372,36 @@ static void nf_tables_commit_release(struct net *net)
         * Memory reclaim happens asynchronously from work queue
         * to prevent expensive synchronize_rcu() in commit phase.
         */
-       if (list_empty(&net->nft.commit_list)) {
+       if (list_empty(&nft_net->commit_list)) {
                nf_tables_module_autoload_cleanup(net);
-               mutex_unlock(&net->nft.commit_mutex);
+               mutex_unlock(&nft_net->commit_mutex);
                return;
        }
 
-       trans = list_last_entry(&net->nft.commit_list,
+       trans = list_last_entry(&nft_net->commit_list,
                                struct nft_trans, list);
        get_net(trans->ctx.net);
        WARN_ON_ONCE(trans->put_net);
 
        trans->put_net = true;
        spin_lock(&nf_tables_destroy_list_lock);
-       list_splice_tail_init(&net->nft.commit_list, &nf_tables_destroy_list);
+       list_splice_tail_init(&nft_net->commit_list, &nf_tables_destroy_list);
        spin_unlock(&nf_tables_destroy_list_lock);
 
        nf_tables_module_autoload_cleanup(net);
        schedule_work(&trans_destroy_work);
 
-       mutex_unlock(&net->nft.commit_mutex);
+       mutex_unlock(&nft_net->commit_mutex);
 }
 
 static void nft_commit_notify(struct net *net, u32 portid)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct sk_buff *batch_skb = NULL, *nskb, *skb;
        unsigned char *data;
        int len;
 
-       list_for_each_entry_safe(skb, nskb, &net->nft.notify_list, list) {
+       list_for_each_entry_safe(skb, nskb, &nft_net->notify_list, list) {
                if (!batch_skb) {
 new_batch:
                        batch_skb = skb;
@@ -8049,19 +8427,72 @@ new_batch:
                               NFT_CB(batch_skb).report, GFP_KERNEL);
        }
 
-       WARN_ON_ONCE(!list_empty(&net->nft.notify_list));
+       WARN_ON_ONCE(!list_empty(&nft_net->notify_list));
+}
+
+static int nf_tables_commit_audit_alloc(struct list_head *adl,
+                                       struct nft_table *table)
+{
+       struct nft_audit_data *adp;
+
+       list_for_each_entry(adp, adl, list) {
+               if (adp->table == table)
+                       return 0;
+       }
+       adp = kzalloc(sizeof(*adp), GFP_KERNEL);
+       if (!adp)
+               return -ENOMEM;
+       adp->table = table;
+       list_add(&adp->list, adl);
+       return 0;
+}
+
+static void nf_tables_commit_audit_collect(struct list_head *adl,
+                                          struct nft_table *table, u32 op)
+{
+       struct nft_audit_data *adp;
+
+       list_for_each_entry(adp, adl, list) {
+               if (adp->table == table)
+                       goto found;
+       }
+       WARN_ONCE(1, "table=%s not expected in commit list", table->name);
+       return;
+found:
+       adp->entries++;
+       if (!adp->op || adp->op > op)
+               adp->op = op;
+}
+
+#define AUNFTABLENAMELEN (NFT_TABLE_MAXNAMELEN + 22)
+
+static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation)
+{
+       struct nft_audit_data *adp, *adn;
+       char aubuf[AUNFTABLENAMELEN];
+
+       list_for_each_entry_safe(adp, adn, adl, list) {
+               snprintf(aubuf, AUNFTABLENAMELEN, "%s:%u", adp->table->name,
+                        generation);
+               audit_log_nfcfg(aubuf, adp->table->family, adp->entries,
+                               nft2audit_op[adp->op], GFP_KERNEL);
+               list_del(&adp->list);
+               kfree(adp);
+       }
 }
 
 static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
        struct nft_chain *chain;
        struct nft_table *table;
+       LIST_HEAD(adl);
        int err;
 
-       if (list_empty(&net->nft.commit_list)) {
-               mutex_unlock(&net->nft.commit_mutex);
+       if (list_empty(&nft_net->commit_list)) {
+               mutex_unlock(&nft_net->commit_mutex);
                return 0;
        }
 
@@ -8074,9 +8505,14 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                return err;
 
        /* 1.  Allocate space for next generation rules_gen_X[] */
-       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+       list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
                int ret;
 
+               ret = nf_tables_commit_audit_alloc(&adl, trans->ctx.table);
+               if (ret) {
+                       nf_tables_commit_chain_prepare_cancel(net);
+                       return ret;
+               }
                if (trans->msg_type == NFT_MSG_NEWRULE ||
                    trans->msg_type == NFT_MSG_DELRULE) {
                        chain = trans->ctx.chain;
@@ -8090,7 +8526,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
        }
 
        /* step 2.  Make rules_gen_X visible to packet path */
-       list_for_each_entry(table, &net->nft.tables, list) {
+       list_for_each_entry(table, &nft_net->tables, list) {
                list_for_each_entry(chain, &table->chains, list)
                        nf_tables_commit_chain(net, chain);
        }
@@ -8099,20 +8535,22 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
         * Bump generation counter, invalidate any dump in progress.
         * Cannot fail after this point.
         */
-       while (++net->nft.base_seq == 0);
+       while (++nft_net->base_seq == 0)
+               ;
 
        /* step 3. Start new generation, rules_gen_X now in use. */
        net->nft.gencursor = nft_gencursor_next(net);
 
-       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+       list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
+               nf_tables_commit_audit_collect(&adl, trans->ctx.table,
+                                              trans->msg_type);
                switch (trans->msg_type) {
                case NFT_MSG_NEWTABLE:
                        if (nft_trans_table_update(trans)) {
-                               if (!nft_trans_table_enable(trans)) {
-                                       nf_tables_table_disable(net,
-                                                               trans->ctx.table);
-                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
-                               }
+                               if (nft_trans_table_state(trans) == NFT_TABLE_STATE_DORMANT)
+                                       nf_tables_table_disable(net, trans->ctx.table);
+
+                               trans->ctx.table->flags = nft_trans_table_flags(trans);
                        } else {
                                nft_clear(net, trans->ctx.table);
                        }
@@ -8179,7 +8617,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                case NFT_MSG_NEWSETELEM:
                        te = (struct nft_trans_elem *)trans->data;
 
-                       te->set->ops->activate(net, te->set, &te->elem);
+                       nft_setelem_activate(net, te->set, &te->elem);
                        nf_tables_setelem_notify(&trans->ctx, te->set,
                                                 &te->elem,
                                                 NFT_MSG_NEWSETELEM, 0);
@@ -8191,9 +8629,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                        nf_tables_setelem_notify(&trans->ctx, te->set,
                                                 &te->elem,
                                                 NFT_MSG_DELSETELEM, 0);
-                       te->set->ops->remove(net, te->set, &te->elem);
-                       atomic_dec(&te->set->nelems);
-                       te->set->ndeact--;
+                       nft_setelem_remove(net, te->set, &te->elem);
+                       if (!nft_setelem_is_catchall(te->set, &te->elem)) {
+                               atomic_dec(&te->set->nelems);
+                               te->set->ndeact--;
+                       }
                        break;
                case NFT_MSG_NEWOBJ:
                        if (nft_trans_obj_update(trans)) {
@@ -8258,6 +8698,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 
        nft_commit_notify(net, NETLINK_CB(skb).portid);
        nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
+       nf_tables_commit_audit_log(&adl, nft_net->base_seq);
        nf_tables_commit_release(net);
 
        return 0;
@@ -8265,17 +8706,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 
 static void nf_tables_module_autoload(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(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_splice_init(&nft_net->module_list, &module_list);
+       mutex_unlock(&nft_net->commit_mutex);
        list_for_each_entry_safe(req, next, &module_list, list) {
                request_module("%s", req->module);
                req->done = true;
        }
-       mutex_lock(&net->nft.commit_mutex);
-       list_splice(&module_list, &net->nft.module_list);
+       mutex_lock(&nft_net->commit_mutex);
+       list_splice(&module_list, &nft_net->module_list);
 }
 
 static void nf_tables_abort_release(struct nft_trans *trans)
@@ -8312,6 +8754,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
 
 static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
        struct nft_hook *hook;
@@ -8320,16 +8763,14 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
            nf_tables_validate(net) < 0)
                return -EAGAIN;
 
-       list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list,
+       list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list,
                                         list) {
                switch (trans->msg_type) {
                case NFT_MSG_NEWTABLE:
                        if (nft_trans_table_update(trans)) {
-                               if (nft_trans_table_enable(trans)) {
-                                       nf_tables_table_disable(net,
-                                                               trans->ctx.table);
-                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
-                               }
+                               if (nft_trans_table_state(trans) == NFT_TABLE_STATE_WAKEUP)
+                                       nf_tables_table_disable(net, trans->ctx.table);
+
                                nft_trans_destroy(trans);
                        } else {
                                list_del_rcu(&trans->ctx.table->list);
@@ -8393,15 +8834,17 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                break;
                        }
                        te = (struct nft_trans_elem *)trans->data;
-                       te->set->ops->remove(net, te->set, &te->elem);
-                       atomic_dec(&te->set->nelems);
+                       nft_setelem_remove(net, te->set, &te->elem);
+                       if (!nft_setelem_is_catchall(te->set, &te->elem))
+                               atomic_dec(&te->set->nelems);
                        break;
                case NFT_MSG_DELSETELEM:
                        te = (struct nft_trans_elem *)trans->data;
 
-                       nft_set_elem_activate(net, te->set, &te->elem);
-                       te->set->ops->activate(net, te->set, &te->elem);
-                       te->set->ndeact--;
+                       nft_setelem_data_activate(net, te->set, &te->elem);
+                       nft_setelem_activate(net, te->set, &te->elem);
+                       if (!nft_setelem_is_catchall(te->set, &te->elem))
+                               te->set->ndeact--;
 
                        nft_trans_destroy(trans);
                        break;
@@ -8446,7 +8889,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
        synchronize_rcu();
 
        list_for_each_entry_safe_reverse(trans, next,
-                                        &net->nft.commit_list, list) {
+                                        &nft_net->commit_list, list) {
                list_del(&trans->list);
                nf_tables_abort_release(trans);
        }
@@ -8467,22 +8910,24 @@ static void nf_tables_cleanup(struct net *net)
 static int nf_tables_abort(struct net *net, struct sk_buff *skb,
                           enum nfnl_abort_action action)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        int ret = __nf_tables_abort(net, action);
 
-       mutex_unlock(&net->nft.commit_mutex);
+       mutex_unlock(&nft_net->commit_mutex);
 
        return ret;
 }
 
 static bool nf_tables_valid_genid(struct net *net, u32 genid)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        bool genid_ok;
 
-       mutex_lock(&net->nft.commit_mutex);
+       mutex_lock(&nft_net->commit_mutex);
 
-       genid_ok = genid == 0 || net->nft.base_seq == genid;
+       genid_ok = genid == 0 || nft_net->base_seq == genid;
        if (!genid_ok)
-               mutex_unlock(&net->nft.commit_mutex);
+               mutex_unlock(&nft_net->commit_mutex);
 
        /* else, commit mutex has to be released by commit or abort function */
        return genid_ok;
@@ -8544,26 +8989,59 @@ EXPORT_SYMBOL_GPL(nft_chain_validate_hooks);
 static int nf_tables_check_loops(const struct nft_ctx *ctx,
                                 const struct nft_chain *chain);
 
+static int nft_check_loops(const struct nft_ctx *ctx,
+                          const struct nft_set_ext *ext)
+{
+       const struct nft_data *data;
+       int ret;
+
+       data = nft_set_ext_data(ext);
+       switch (data->verdict.code) {
+       case NFT_JUMP:
+       case NFT_GOTO:
+               ret = nf_tables_check_loops(ctx, data->verdict.chain);
+               break;
+       default:
+               ret = 0;
+               break;
+       }
+
+       return ret;
+}
+
 static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
                                        struct nft_set *set,
                                        const struct nft_set_iter *iter,
                                        struct nft_set_elem *elem)
 {
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
-       const struct nft_data *data;
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
            *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
                return 0;
 
-       data = nft_set_ext_data(ext);
-       switch (data->verdict.code) {
-       case NFT_JUMP:
-       case NFT_GOTO:
-               return nf_tables_check_loops(ctx, data->verdict.chain);
-       default:
-               return 0;
+       return nft_check_loops(ctx, ext);
+}
+
+static int nft_set_catchall_loops(const struct nft_ctx *ctx,
+                                 struct nft_set *set)
+{
+       u8 genmask = nft_genmask_next(ctx->net);
+       struct nft_set_elem_catchall *catchall;
+       struct nft_set_ext *ext;
+       int ret = 0;
+
+       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_set_elem_active(ext, genmask))
+                       continue;
+
+               ret = nft_check_loops(ctx, ext);
+               if (ret < 0)
+                       return ret;
        }
+
+       return ret;
 }
 
 static int nf_tables_check_loops(const struct nft_ctx *ctx,
@@ -8599,6 +9077,7 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
                                                        data->verdict.chain);
                                if (err < 0)
                                        return err;
+                               break;
                        default:
                                break;
                        }
@@ -8624,6 +9103,9 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
                        iter.fn         = nf_tables_loop_check_setelem;
 
                        set->ops->walk(ctx, set, &iter);
+                       if (!iter.err)
+                               iter.err = nft_set_catchall_loops(ctx, set);
+
                        if (iter.err < 0)
                                return iter.err;
                }
@@ -8657,15 +9139,6 @@ int nft_parse_u32_check(const struct nlattr *attr, int max, u32 *dest)
 }
 EXPORT_SYMBOL_GPL(nft_parse_u32_check);
 
-/**
- *     nft_parse_register - parse a register value from a netlink attribute
- *
- *     @attr: netlink attribute
- *
- *     Parse and translate a register value from a netlink attribute.
- *     Registers used to be 128 bit wide, these register numbers will be
- *     mapped to the corresponding 32 bit register numbers.
- */
 static unsigned int nft_parse_register(const struct nlattr *attr)
 {
        unsigned int reg;
@@ -8701,15 +9174,6 @@ int nft_dump_register(struct sk_buff *skb, unsigned int attr, unsigned int reg)
 }
 EXPORT_SYMBOL_GPL(nft_dump_register);
 
-/**
- *     nft_validate_register_load - validate a load from a register
- *
- *     @reg: the register number
- *     @len: the length of the data
- *
- *     Validate that the input register is one of the general purpose
- *     registers and that the length of the load is within the bounds.
- */
 static int nft_validate_register_load(enum nft_registers reg, unsigned int len)
 {
        if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE)
@@ -8737,20 +9201,6 @@ int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len)
 }
 EXPORT_SYMBOL_GPL(nft_parse_register_load);
 
-/**
- *     nft_validate_register_store - validate an expressions' register store
- *
- *     @ctx: context of the expression performing the load
- *     @reg: the destination register number
- *     @data: the data to load
- *     @type: the data type
- *     @len: the length of the data
- *
- *     Validate that a data load uses the appropriate data type for
- *     the destination register and the length is within the bounds.
- *     A value of NULL for the data means that its runtime gathered
- *     data.
- */
 static int nft_validate_register_store(const struct nft_ctx *ctx,
                                       enum nft_registers reg,
                                       const struct nft_data *data,
@@ -9068,9 +9518,10 @@ static void __nft_release_hook(struct net *net, struct nft_table *table)
 
 static void __nft_release_hooks(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_table *table;
 
-       list_for_each_entry(table, &net->nft.tables, list) {
+       list_for_each_entry(table, &nft_net->tables, list) {
                if (nft_table_has_owner(table))
                        continue;
 
@@ -9127,9 +9578,10 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
 
 static void __nft_release_tables(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_table *table, *nt;
 
-       list_for_each_entry_safe(table, nt, &net->nft.tables, list) {
+       list_for_each_entry_safe(table, nt, &nft_net->tables, list) {
                if (nft_table_has_owner(table))
                        continue;
 
@@ -9140,6 +9592,7 @@ static void __nft_release_tables(struct net *net)
 static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
                            void *ptr)
 {
+       struct nftables_pernet *nft_net;
        struct netlink_notify *n = ptr;
        struct nft_table *table, *nt;
        struct net *net = n->net;
@@ -9148,8 +9601,9 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
        if (event != NETLINK_URELEASE || n->protocol != NETLINK_NETFILTER)
                return NOTIFY_DONE;
 
-       mutex_lock(&net->nft.commit_mutex);
-       list_for_each_entry(table, &net->nft.tables, list) {
+       nft_net = nft_pernet(net);
+       mutex_lock(&nft_net->commit_mutex);
+       list_for_each_entry(table, &nft_net->tables, list) {
                if (nft_table_has_owner(table) &&
                    n->portid == table->nlpid) {
                        __nft_release_hook(net, table);
@@ -9158,13 +9612,13 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
        }
        if (release) {
                synchronize_rcu();
-               list_for_each_entry_safe(table, nt, &net->nft.tables, list) {
+               list_for_each_entry_safe(table, nt, &nft_net->tables, list) {
                        if (nft_table_has_owner(table) &&
                            n->portid == table->nlpid)
                                __nft_release_table(net, table);
                }
        }
-       mutex_unlock(&net->nft.commit_mutex);
+       mutex_unlock(&nft_net->commit_mutex);
 
        return NOTIFY_DONE;
 }
@@ -9175,13 +9629,15 @@ static struct notifier_block nft_nl_notifier = {
 
 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);
-       INIT_LIST_HEAD(&net->nft.notify_list);
-       mutex_init(&net->nft.commit_mutex);
-       net->nft.base_seq = 1;
-       net->nft.validate_state = NFT_VALIDATE_SKIP;
+       struct nftables_pernet *nft_net = nft_pernet(net);
+
+       INIT_LIST_HEAD(&nft_net->tables);
+       INIT_LIST_HEAD(&nft_net->commit_list);
+       INIT_LIST_HEAD(&nft_net->module_list);
+       INIT_LIST_HEAD(&nft_net->notify_list);
+       mutex_init(&nft_net->commit_mutex);
+       nft_net->base_seq = 1;
+       nft_net->validate_state = NFT_VALIDATE_SKIP;
 
        return 0;
 }
@@ -9193,27 +9649,30 @@ static void __net_exit nf_tables_pre_exit_net(struct net *net)
 
 static void __net_exit nf_tables_exit_net(struct net *net)
 {
-       mutex_lock(&net->nft.commit_mutex);
-       if (!list_empty(&net->nft.commit_list))
+       struct nftables_pernet *nft_net = nft_pernet(net);
+
+       mutex_lock(&nft_net->commit_mutex);
+       if (!list_empty(&nft_net->commit_list))
                __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));
-       WARN_ON_ONCE(!list_empty(&net->nft.module_list));
-       WARN_ON_ONCE(!list_empty(&net->nft.notify_list));
+       mutex_unlock(&nft_net->commit_mutex);
+       WARN_ON_ONCE(!list_empty(&nft_net->tables));
+       WARN_ON_ONCE(!list_empty(&nft_net->module_list));
+       WARN_ON_ONCE(!list_empty(&nft_net->notify_list));
 }
 
 static struct pernet_operations nf_tables_net_ops = {
        .init           = nf_tables_init_net,
        .pre_exit       = nf_tables_pre_exit_net,
        .exit           = nf_tables_exit_net,
+       .id             = &nf_tables_net_id,
+       .size           = sizeof(struct nftables_pernet),
 };
 
 static int __init nf_tables_module_init(void)
 {
        int err;
 
-       spin_lock_init(&nf_tables_destroy_list_lock);
        err = register_pernet_subsys(&nf_tables_net_ops);
        if (err < 0)
                return err;