[NFTA_SET_DATA_LEN]             = { .type = NLA_U32 },
        [NFTA_SET_POLICY]               = { .type = NLA_U32 },
        [NFTA_SET_DESC]                 = { .type = NLA_NESTED },
+       [NFTA_SET_ID]                   = { .type = NLA_U32 },
 };
 
 static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
        return ERR_PTR(-ENOENT);
 }
 
+struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
+                                         const struct nlattr *nla)
+{
+       struct nft_trans *trans;
+       u32 id = ntohl(nla_get_be32(nla));
+
+       list_for_each_entry(trans, &net->nft.commit_list, list) {
+               if (trans->msg_type == NFT_MSG_NEWSET &&
+                   id == nft_trans_set_id(trans))
+                       return nft_trans_set(trans);
+       }
+       return ERR_PTR(-ENOENT);
+}
+
 static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
                                    const char *name)
 {
        return ret;
 }
 
+#define NFT_SET_INACTIVE       (1 << 15)       /* Internal set flag */
+
 static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (skb2 == NULL)
        return 0;
 }
 
+static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
+                            struct nft_set *set)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
+       if (trans == NULL)
+               return -ENOMEM;
+
+       if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
+               nft_trans_set_id(trans) =
+                       ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
+               set->flags |= NFT_SET_INACTIVE;
+       }
+       nft_trans_set(trans) = set;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+       return 0;
+}
+
 static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
 
        if (nla[NFTA_SET_TABLE] == NULL ||
            nla[NFTA_SET_NAME] == NULL ||
-           nla[NFTA_SET_KEY_LEN] == NULL)
+           nla[NFTA_SET_KEY_LEN] == NULL ||
+           nla[NFTA_SET_ID] == NULL)
                return -EINVAL;
 
        memset(&desc, 0, sizeof(desc));
        if (err < 0)
                goto err2;
 
+       err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
+       if (err < 0)
+               goto err2;
+
        list_add_tail(&set->list, &table->sets);
-       nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET);
        return 0;
 
 err2:
        return err;
 }
 
-static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+static void nft_set_destroy(struct nft_set *set)
 {
-       list_del(&set->list);
-       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
-
        set->ops->destroy(set);
        module_put(set->ops->owner);
        kfree(set);
 }
 
+static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       list_del(&set->list);
+       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
+       nft_set_destroy(set);
+}
+
 static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
        if (!list_empty(&set->bindings))
                return -EBUSY;
 
-       nf_tables_set_destroy(&ctx, set);
+       err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
+       if (err < 0)
+               return err;
+
+       list_del(&set->list);
        return 0;
 }
 
 {
        list_del(&binding->list);
 
-       if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
+       if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
+           !(set->flags & NFT_SET_INACTIVE))
                nf_tables_set_destroy(ctx, set);
 }
 
        [NFTA_SET_ELEM_LIST_TABLE]      = { .type = NLA_STRING },
        [NFTA_SET_ELEM_LIST_SET]        = { .type = NLA_STRING },
        [NFTA_SET_ELEM_LIST_ELEMENTS]   = { .type = NLA_NESTED },
+       [NFTA_SET_ELEM_LIST_SET_ID]     = { .type = NLA_U32 },
 };
 
 static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        event  = NFT_MSG_NEWSETELEM;
        event |= NFNL_SUBSYS_NFTABLES << 8;
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                                const struct nlmsghdr *nlh,
                                const struct nlattr * const nla[])
 {
+       struct net *net = sock_net(skb->sk);
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
                return err;
 
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
-       if (IS_ERR(set))
-               return PTR_ERR(set);
+       if (IS_ERR(set)) {
+               if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
+                       set = nf_tables_set_lookup_byid(net,
+                                       nla[NFTA_SET_ELEM_LIST_SET_ID]);
+               }
+               if (IS_ERR(set))
+                       return PTR_ERR(set);
+       }
+
        if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
                return -EBUSY;
 
                .policy         = nft_rule_policy,
        },
        [NFT_MSG_NEWSET] = {
-               .call           = nf_tables_newset,
+               .call_batch     = nf_tables_newset,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
                .policy         = nft_set_policy,
        },
        [NFT_MSG_DELSET] = {
-               .call           = nf_tables_delset,
+               .call_batch     = nf_tables_delset,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
        [NFT_MSG_NEWSETELEM] = {
-               .call           = nf_tables_newsetelem,
+               .call_batch     = nf_tables_newsetelem,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_DELSETELEM] = {
-               .call           = nf_tables_delsetelem,
+               .call_batch     = nf_tables_delsetelem,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
                                              nft_trans_rule(trans), NFT_MSG_DELRULE, 0,
                                              trans->ctx.afi->family);
                        break;
+               case NFT_MSG_NEWSET:
+                       nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
+                       nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
+                                            NFT_MSG_NEWSET);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSET:
+                       nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
+                                            NFT_MSG_DELSET);
+                       break;
                }
        }
 
                case NFT_MSG_DELRULE:
                        nf_tables_rule_destroy(&trans->ctx,
                                               nft_trans_rule(trans));
-                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSET:
+                       nft_set_destroy(nft_trans_set(trans));
                        break;
                }
+               nft_trans_destroy(trans);
        }
 
        return 0;
                        nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
                        nft_trans_destroy(trans);
                        break;
+               case NFT_MSG_NEWSET:
+                       list_del(&nft_trans_set(trans)->list);
+                       break;
+               case NFT_MSG_DELSET:
+                       list_add_tail(&nft_trans_set(trans)->list,
+                                     &trans->ctx.table->sets);
+                       nft_trans_destroy(trans);
+                       break;
                }
        }
 
                case NFT_MSG_NEWRULE:
                        nf_tables_rule_destroy(&trans->ctx,
                                               nft_trans_rule(trans));
-                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWSET:
+                       nft_set_destroy(nft_trans_set(trans));
                        break;
                }
+               nft_trans_destroy(trans);
        }
 
        return 0;