Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nftables
[pandora-kernel.git] / net / netfilter / nf_tables_api.c
index 92cec68..0478847 100644 (file)
@@ -96,13 +96,14 @@ static void nft_ctx_init(struct nft_ctx *ctx,
                         struct nft_chain *chain,
                         const struct nlattr * const *nla)
 {
-       ctx->net   = sock_net(skb->sk);
-       ctx->skb   = skb;
-       ctx->nlh   = nlh;
-       ctx->afi   = afi;
-       ctx->table = table;
-       ctx->chain = chain;
-       ctx->nla   = nla;
+       ctx->net        = sock_net(skb->sk);
+       ctx->afi        = afi;
+       ctx->table      = table;
+       ctx->chain      = chain;
+       ctx->nla        = nla;
+       ctx->portid     = NETLINK_CB(skb).portid;
+       ctx->report     = nlmsg_report(nlh);
+       ctx->seq        = nlh->nlmsg_seq;
 }
 
 static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type,
@@ -190,8 +191,8 @@ nf_tables_chain_type_lookup(const struct nft_af_info *afi,
 #ifdef CONFIG_MODULES
        if (autoload) {
                nfnl_unlock(NFNL_SUBSYS_NFTABLES);
-               request_module("nft-chain-%u-%*.s", afi->family,
-                              nla_len(nla)-1, (const char *)nla_data(nla));
+               request_module("nft-chain-%u-%.*s", afi->family,
+                              nla_len(nla), (const char *)nla_data(nla));
                nfnl_lock(NFNL_SUBSYS_NFTABLES);
                type = __nf_tables_chain_type_lookup(afi->family, nla);
                if (type != NULL)
@@ -235,20 +236,13 @@ nla_put_failure:
        return -1;
 }
 
-static int nf_tables_table_notify(const struct sk_buff *oskb,
-                                 const struct nlmsghdr *nlh,
-                                 const struct nft_table *table,
-                                 int event, int family)
+static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
 {
        struct sk_buff *skb;
-       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
-       u32 seq = nlh ? nlh->nlmsg_seq : 0;
-       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
-       bool report;
        int err;
 
-       report = nlh ? nlmsg_report(nlh) : false;
-       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -256,18 +250,20 @@ static int nf_tables_table_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_table_info(skb, portid, seq, event, 0,
-                                       family, table);
+       err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
+                                       ctx->afi->family, ctx->table);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -307,6 +303,9 @@ done:
        return skb->len;
 }
 
+/* Internal table flags */
+#define NFT_TABLE_INACTIVE     (1 << 15)
+
 static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -333,6 +332,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2)
@@ -381,7 +382,7 @@ err:
        return err;
 }
 
-static int nf_tables_table_disable(const struct nft_af_info *afi,
+static void nf_tables_table_disable(const struct nft_af_info *afi,
                                   struct nft_table *table)
 {
        struct nft_chain *chain;
@@ -391,45 +392,63 @@ static int nf_tables_table_disable(const struct nft_af_info *afi,
                        nf_unregister_hooks(nft_base_chain(chain)->ops,
                                            afi->nops);
        }
-
-       return 0;
 }
 
-static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
-                             const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct nft_af_info *afi, struct nft_table *table)
+static int nf_tables_updtable(struct nft_ctx *ctx)
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       int family = nfmsg->nfgen_family, ret = 0;
+       struct nft_trans *trans;
+       u32 flags;
+       int ret = 0;
 
-       if (nla[NFTA_TABLE_FLAGS]) {
-               u32 flags;
+       if (!ctx->nla[NFTA_TABLE_FLAGS])
+               return 0;
 
-               flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
-               if (flags & ~NFT_TABLE_F_DORMANT)
-                       return -EINVAL;
+       flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS]));
+       if (flags & ~NFT_TABLE_F_DORMANT)
+               return -EINVAL;
+
+       trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
+                               sizeof(struct nft_trans_table));
+       if (trans == NULL)
+               return -ENOMEM;
 
-               if ((flags & NFT_TABLE_F_DORMANT) &&
-                   !(table->flags & NFT_TABLE_F_DORMANT)) {
-                       ret = nf_tables_table_disable(afi, table);
-                       if (ret >= 0)
-                               table->flags |= NFT_TABLE_F_DORMANT;
-               } else if (!(flags & NFT_TABLE_F_DORMANT) &&
-                          table->flags & NFT_TABLE_F_DORMANT) {
-                       ret = nf_tables_table_enable(afi, table);
-                       if (ret >= 0)
-                               table->flags &= ~NFT_TABLE_F_DORMANT;
+       if ((flags & NFT_TABLE_F_DORMANT) &&
+           !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
+               nft_trans_table_enable(trans) = false;
+       } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+                  ctx->table->flags & NFT_TABLE_F_DORMANT) {
+               ret = nf_tables_table_enable(ctx->afi, ctx->table);
+               if (ret >= 0) {
+                       ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
+                       nft_trans_table_enable(trans) = true;
                }
-               if (ret < 0)
-                       goto err;
        }
+       if (ret < 0)
+               goto err;
 
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
+       nft_trans_table_update(trans) = true;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
 err:
+       nft_trans_destroy(trans);
        return ret;
 }
 
+static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
+       if (trans == NULL)
+               return -ENOMEM;
+
+       if (msg_type == NFT_MSG_NEWTABLE)
+               ctx->table->flags |= NFT_TABLE_INACTIVE;
+
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
+}
+
 static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -441,6 +460,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        u32 flags = 0;
+       struct nft_ctx ctx;
+       int err;
 
        afi = nf_tables_afinfo_lookup(net, family, true);
        if (IS_ERR(afi))
@@ -455,11 +476,15 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        }
 
        if (table != NULL) {
+               if (table->flags & NFT_TABLE_INACTIVE)
+                       return -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
-               return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
+
+               nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+               return nf_tables_updtable(&ctx);
        }
 
        if (nla[NFTA_TABLE_FLAGS]) {
@@ -482,8 +507,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        INIT_LIST_HEAD(&table->sets);
        table->flags = flags;
 
+       nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+       err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
+       if (err < 0) {
+               kfree(table);
+               module_put(afi->owner);
+               return err;
+       }
        list_add_tail(&table->list, &afi->tables);
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
        return 0;
 }
 
@@ -495,7 +526,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
        struct nft_af_info *afi;
        struct nft_table *table;
        struct net *net = sock_net(skb->sk);
-       int family = nfmsg->nfgen_family;
+       int family = nfmsg->nfgen_family, err;
+       struct nft_ctx ctx;
 
        afi = nf_tables_afinfo_lookup(net, family, false);
        if (IS_ERR(afi))
@@ -504,17 +536,27 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        if (!list_empty(&table->chains) || !list_empty(&table->sets))
                return -EBUSY;
 
+       nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+       err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
+       if (err < 0)
+               return err;
+
        list_del(&table->list);
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
-       kfree(table);
-       module_put(afi->owner);
        return 0;
 }
 
+static void nf_tables_table_destroy(struct nft_ctx *ctx)
+{
+       kfree(ctx->table);
+       module_put(ctx->afi->owner);
+}
+
 int nft_register_chain_type(const struct nf_chain_type *ctype)
 {
        int err = 0;
@@ -675,21 +717,13 @@ nla_put_failure:
        return -1;
 }
 
-static int nf_tables_chain_notify(const struct sk_buff *oskb,
-                                 const struct nlmsghdr *nlh,
-                                 const struct nft_table *table,
-                                 const struct nft_chain *chain,
-                                 int event, int family)
+static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
 {
        struct sk_buff *skb;
-       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
-       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
-       u32 seq = nlh ? nlh->nlmsg_seq : 0;
-       bool report;
        int err;
 
-       report = nlh ? nlmsg_report(nlh) : false;
-       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -697,18 +731,21 @@ static int nf_tables_chain_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family,
-                                       table, chain);
+       err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
+                                       ctx->afi->family, ctx->table,
+                                       ctx->chain);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -778,10 +815,14 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2)
@@ -847,6 +888,34 @@ static void nft_chain_stats_replace(struct nft_base_chain *chain,
                rcu_assign_pointer(chain->stats, newstats);
 }
 
+static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
+       if (trans == NULL)
+               return -ENOMEM;
+
+       if (msg_type == NFT_MSG_NEWCHAIN)
+               ctx->chain->flags |= NFT_CHAIN_INACTIVE;
+
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
+}
+
+static void nf_tables_chain_destroy(struct nft_chain *chain)
+{
+       BUG_ON(chain->use > 0);
+
+       if (chain->flags & NFT_BASE_CHAIN) {
+               module_put(nft_base_chain(chain)->type->owner);
+               free_percpu(nft_base_chain(chain)->stats);
+               kfree(nft_base_chain(chain));
+       } else {
+               kfree(chain);
+       }
+}
+
 static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -866,6 +935,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        struct nft_stats __percpu *stats;
        int err;
        bool create;
+       struct nft_ctx ctx;
 
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
 
@@ -911,6 +981,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        }
 
        if (chain != NULL) {
+               struct nft_stats *stats = NULL;
+               struct nft_trans *trans;
+
+               if (chain->flags & NFT_CHAIN_INACTIVE)
+                       return -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
@@ -927,17 +1002,28 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                        stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
                        if (IS_ERR(stats))
                                return PTR_ERR(stats);
-
-                       nft_chain_stats_replace(nft_base_chain(chain), stats);
                }
 
-               if (nla[NFTA_CHAIN_POLICY])
-                       nft_base_chain(chain)->policy = policy;
+               nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+               trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
+                                       sizeof(struct nft_trans_chain));
+               if (trans == NULL)
+                       return -ENOMEM;
 
-               if (nla[NFTA_CHAIN_HANDLE] && name)
-                       nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
+               nft_trans_chain_stats(trans) = stats;
+               nft_trans_chain_update(trans) = true;
+
+               if (nla[NFTA_CHAIN_POLICY])
+                       nft_trans_chain_policy(trans) = policy;
+               else
+                       nft_trans_chain_policy(trans) = -1;
 
-               goto notify;
+               if (nla[NFTA_CHAIN_HANDLE] && name) {
+                       nla_strlcpy(nft_trans_chain_name(trans), name,
+                                   NFT_CHAIN_MAXNAMELEN);
+               }
+               list_add_tail(&trans->list, &net->nft.commit_list);
+               return 0;
        }
 
        if (table->use == UINT_MAX)
@@ -1033,31 +1119,26 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        if (!(table->flags & NFT_TABLE_F_DORMANT) &&
            chain->flags & NFT_BASE_CHAIN) {
                err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
-               if (err < 0) {
-                       module_put(basechain->type->owner);
-                       free_percpu(basechain->stats);
-                       kfree(basechain);
-                       return err;
-               }
+               if (err < 0)
+                       goto err1;
        }
-       list_add_tail(&chain->list, &table->chains);
-       table->use++;
-notify:
-       nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN,
-                              family);
-       return 0;
-}
 
-static void nf_tables_chain_destroy(struct nft_chain *chain)
-{
-       BUG_ON(chain->use > 0);
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+       err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
+       if (err < 0)
+               goto err2;
 
-       if (chain->flags & NFT_BASE_CHAIN) {
-               module_put(nft_base_chain(chain)->type->owner);
-               free_percpu(nft_base_chain(chain)->stats);
-               kfree(nft_base_chain(chain));
-       } else
-               kfree(chain);
+       list_add_tail(&chain->list, &table->chains);
+       return 0;
+err2:
+       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+           chain->flags & NFT_BASE_CHAIN) {
+               nf_unregister_hooks(nft_base_chain(chain)->ops,
+                                   afi->nops);
+       }
+err1:
+       nf_tables_chain_destroy(chain);
+       return err;
 }
 
 static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
@@ -1070,6 +1151,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
        struct nft_chain *chain;
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
+       struct nft_ctx ctx;
+       int err;
 
        afi = nf_tables_afinfo_lookup(net, family, false);
        if (IS_ERR(afi))
@@ -1078,28 +1161,23 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
-
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
        if (!list_empty(&chain->rules) || chain->use > 0)
                return -EBUSY;
 
-       list_del(&chain->list);
-       table->use--;
-
-       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
-           chain->flags & NFT_BASE_CHAIN)
-               nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
-
-       nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
-                              family);
-
-       /* Make sure all rule references are gone before this is released */
-       synchronize_rcu();
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+       err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
+       if (err < 0)
+               return err;
 
-       nf_tables_chain_destroy(chain);
+       list_del(&chain->list);
        return 0;
 }
 
@@ -1388,22 +1466,15 @@ nla_put_failure:
        return -1;
 }
 
-static int nf_tables_rule_notify(const struct sk_buff *oskb,
-                                const struct nlmsghdr *nlh,
-                                const struct nft_table *table,
-                                const struct nft_chain *chain,
+static int nf_tables_rule_notify(const struct nft_ctx *ctx,
                                 const struct nft_rule *rule,
-                                int event, u32 flags, int family)
+                                int event)
 {
        struct sk_buff *skb;
-       u32 portid = NETLINK_CB(oskb).portid;
-       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
-       u32 seq = nlh->nlmsg_seq;
-       bool report;
        int err;
 
-       report = nlmsg_report(nlh);
-       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -1411,18 +1482,21 @@ static int nf_tables_rule_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_rule_info(skb, portid, seq, event, flags,
-                                      family, table, chain, rule);
+       err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
+                                      ctx->afi->family, ctx->table,
+                                      ctx->chain, rule);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -1538,10 +1612,14 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
 
        rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
        if (IS_ERR(rule))
@@ -1801,6 +1879,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        if (nla[NFTA_RULE_CHAIN]) {
                chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
@@ -1929,7 +2009,8 @@ nft_select_set_ops(const struct nlattr * const nla[],
 
 static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
        [NFTA_SET_TABLE]                = { .type = NLA_STRING },
-       [NFTA_SET_NAME]                 = { .type = NLA_STRING },
+       [NFTA_SET_NAME]                 = { .type = NLA_STRING,
+                                           .len = IFNAMSIZ - 1 },
        [NFTA_SET_FLAGS]                = { .type = NLA_U32 },
        [NFTA_SET_KEY_TYPE]             = { .type = NLA_U32 },
        [NFTA_SET_KEY_LEN]              = { .type = NLA_U32 },
@@ -1967,6 +2048,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
                table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
                if (IS_ERR(table))
                        return PTR_ERR(table);
+               if (table->flags & NFT_TABLE_INACTIVE)
+                       return -ENOENT;
        }
 
        nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
@@ -2053,8 +2136,8 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
        struct nfgenmsg *nfmsg;
        struct nlmsghdr *nlh;
        struct nlattr *desc;
-       u32 portid = NETLINK_CB(ctx->skb).portid;
-       u32 seq = ctx->nlh->nlmsg_seq;
+       u32 portid = ctx->portid;
+       u32 seq = ctx->seq;
 
        event |= NFNL_SUBSYS_NFTABLES << 8;
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
@@ -2106,12 +2189,11 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
                                int event)
 {
        struct sk_buff *skb;
-       u32 portid = NETLINK_CB(ctx->skb).portid;
-       bool report;
+       u32 portid = ctx->portid;
        int err;
 
-       report = nlmsg_report(ctx->nlh);
-       if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -2125,8 +2207,8 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
                goto err;
        }
 
-       err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
        if (err < 0)
                nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
@@ -2644,7 +2726,8 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
 static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
                                      const struct sk_buff *skb,
                                      const struct nlmsghdr *nlh,
-                                     const struct nlattr * const nla[])
+                                     const struct nlattr * const nla[],
+                                     bool trans)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        struct nft_af_info *afi;
@@ -2658,6 +2741,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
        table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (!trans && (table->flags & NFT_TABLE_INACTIVE))
+               return -ENOENT;
 
        nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
        return 0;
@@ -2731,7 +2816,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        if (err < 0)
                return err;
 
-       err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla);
+       err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
+                                        false);
        if (err < 0)
                return err;
 
@@ -2796,7 +2882,7 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
        struct nft_ctx ctx;
        int err;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
        if (err < 0)
                return err;
 
@@ -2864,14 +2950,12 @@ static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
                                    const struct nft_set_elem *elem,
                                    int event, u16 flags)
 {
-       const struct sk_buff *oskb = ctx->skb;
-       struct net *net = sock_net(oskb->sk);
-       u32 portid = NETLINK_CB(oskb).portid;
-       bool report = nlmsg_report(ctx->nlh);
+       struct net *net = ctx->net;
+       u32 portid = ctx->portid;
        struct sk_buff *skb;
        int err;
 
-       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+       if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -2886,7 +2970,7 @@ static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
+       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report,
                             GFP_KERNEL);
 err:
        if (err < 0)
@@ -2894,7 +2978,21 @@ err:
        return err;
 }
 
-static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
+static struct nft_trans *nft_trans_elem_alloc(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_elem));
+       if (trans == NULL)
+               return NULL;
+
+       nft_trans_elem_set(trans) = set;
+       return trans;
+}
+
+static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
@@ -2902,6 +3000,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
        enum nft_registers dreg;
+       struct nft_trans *trans;
        int err;
 
        if (set->size && set->nelems == set->size)
@@ -2969,14 +3068,20 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
                }
        }
 
+       trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
+       if (trans == NULL)
+               goto err3;
+
        err = set->ops->insert(set, &elem);
        if (err < 0)
-               goto err3;
-       set->nelems++;
+               goto err4;
 
-       nf_tables_setelem_notify(ctx, set, &elem, NFT_MSG_NEWSETELEM, 0);
+       nft_trans_elem(trans) = elem;
+       list_add(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
+err4:
+       kfree(trans);
 err3:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
                nft_data_uninit(&elem.data, d2.type);
@@ -2994,9 +3099,9 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
-       int rem, err;
+       int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
        if (err < 0)
                return err;
 
@@ -3016,17 +3121,18 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_add_set_elem(&ctx, set, attr);
                if (err < 0)
-                       return err;
+                       break;
        }
-       return 0;
+       return err;
 }
 
-static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
+static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
                           const struct nlattr *attr)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        struct nft_data_desc desc;
        struct nft_set_elem elem;
+       struct nft_trans *trans;
        int err;
 
        err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
@@ -3050,10 +3156,12 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                goto err2;
 
-       set->ops->remove(set, &elem);
-       set->nelems--;
+       trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
+       if (trans == NULL)
+               goto err2;
 
-       nf_tables_setelem_notify(ctx, set, &elem, NFT_MSG_DELSETELEM, 0);
+       nft_trans_elem(trans) = elem;
+       list_add(&trans->list, &ctx->net->nft.commit_list);
 
        nft_data_uninit(&elem.key, NFT_DATA_VALUE);
        if (set->flags & NFT_SET_MAP)
@@ -3072,9 +3180,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
-       int rem, err;
+       int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
        if (err < 0)
                return err;
 
@@ -3087,14 +3195,14 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_del_setelem(&ctx, set, attr);
                if (err < 0)
-                       return err;
+                       break;
        }
-       return 0;
+       return err;
 }
 
 static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
        [NFT_MSG_NEWTABLE] = {
-               .call           = nf_tables_newtable,
+               .call_batch     = nf_tables_newtable,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
@@ -3104,12 +3212,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_table_policy,
        },
        [NFT_MSG_DELTABLE] = {
-               .call           = nf_tables_deltable,
+               .call_batch     = nf_tables_deltable,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
        [NFT_MSG_NEWCHAIN] = {
-               .call           = nf_tables_newchain,
+               .call_batch     = nf_tables_newchain,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
@@ -3119,7 +3227,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_chain_policy,
        },
        [NFT_MSG_DELCHAIN] = {
-               .call           = nf_tables_delchain,
+               .call_batch     = nf_tables_delchain,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
@@ -3170,10 +3278,56 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
        },
 };
 
+static void nft_chain_commit_update(struct nft_trans *trans)
+{
+       struct nft_base_chain *basechain;
+
+       if (nft_trans_chain_name(trans)[0])
+               strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans));
+
+       if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN))
+               return;
+
+       basechain = nft_base_chain(trans->ctx.chain);
+       nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
+
+       switch (nft_trans_chain_policy(trans)) {
+       case NF_DROP:
+       case NF_ACCEPT:
+               basechain->policy = nft_trans_chain_policy(trans);
+               break;
+       }
+}
+
+/* Schedule objects for release via rcu to make sure no packets are accesing
+ * removed rules.
+ */
+static void nf_tables_commit_release_rcu(struct rcu_head *rt)
+{
+       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
+
+       switch (trans->msg_type) {
+       case NFT_MSG_DELTABLE:
+               nf_tables_table_destroy(&trans->ctx);
+               break;
+       case NFT_MSG_DELCHAIN:
+               nf_tables_chain_destroy(trans->ctx.chain);
+               break;
+       case NFT_MSG_DELRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               break;
+       case NFT_MSG_DELSET:
+               nft_set_destroy(nft_trans_set(trans));
+               break;
+       }
+       kfree(trans);
+}
+
 static int nf_tables_commit(struct sk_buff *skb)
 {
        struct net *net = sock_net(skb->sk);
        struct nft_trans *trans, *next;
+       struct nft_set *set;
 
        /* Bump generation counter, invalidate any dump in progress */
        net->nft.genctr++;
@@ -3188,23 +3342,53 @@ static int nf_tables_commit(struct sk_buff *skb)
 
        list_for_each_entry_safe(trans, next, &net->nft.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(trans->ctx.afi,
+                                                               trans->ctx.table);
+                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+                               }
+                       } else {
+                               trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
+                       }
+                       nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELTABLE:
+                       nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE);
+                       break;
+               case NFT_MSG_NEWCHAIN:
+                       if (nft_trans_chain_update(trans))
+                               nft_chain_commit_update(trans);
+                       else {
+                               trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
+                               trans->ctx.table->use++;
+                       }
+                       nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELCHAIN:
+                       trans->ctx.table->use--;
+                       nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
+                       if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
+                           trans->ctx.chain->flags & NFT_BASE_CHAIN) {
+                               nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
+                                                   trans->ctx.afi->nops);
+                       }
+                       break;
                case NFT_MSG_NEWRULE:
                        nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
-                       nf_tables_rule_notify(trans->ctx.skb, trans->ctx.nlh,
-                                             trans->ctx.table,
-                                             trans->ctx.chain,
+                       nf_tables_rule_notify(&trans->ctx,
                                              nft_trans_rule(trans),
-                                             NFT_MSG_NEWRULE, 0,
-                                             trans->ctx.afi->family);
+                                             NFT_MSG_NEWRULE);
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELRULE:
                        list_del_rcu(&nft_trans_rule(trans)->list);
-                       nf_tables_rule_notify(trans->ctx.skb, trans->ctx.nlh,
-                                             trans->ctx.table,
-                                             trans->ctx.chain,
-                                             nft_trans_rule(trans), NFT_MSG_DELRULE, 0,
-                                             trans->ctx.afi->family);
+                       nf_tables_rule_notify(&trans->ctx,
+                                             nft_trans_rule(trans),
+                                             NFT_MSG_DELRULE);
                        break;
                case NFT_MSG_NEWSET:
                        nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
@@ -3216,36 +3400,106 @@ static int nf_tables_commit(struct sk_buff *skb)
                        nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
                                             NFT_MSG_DELSET);
                        break;
+               case NFT_MSG_NEWSETELEM:
+                       nft_trans_elem_set(trans)->nelems++;
+                       nf_tables_setelem_notify(&trans->ctx,
+                                                nft_trans_elem_set(trans),
+                                                &nft_trans_elem(trans),
+                                                NFT_MSG_NEWSETELEM, 0);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSETELEM:
+                       nft_trans_elem_set(trans)->nelems--;
+                       nf_tables_setelem_notify(&trans->ctx,
+                                                nft_trans_elem_set(trans),
+                                                &nft_trans_elem(trans),
+                                                NFT_MSG_DELSETELEM, 0);
+                       set = nft_trans_elem_set(trans);
+                       set->ops->get(set, &nft_trans_elem(trans));
+                       set->ops->remove(set, &nft_trans_elem(trans));
+                       nft_trans_destroy(trans);
+                       break;
                }
        }
 
-       /* Make sure we don't see any packet traversing old rules */
-       synchronize_rcu();
-
-       /* Now we can safely release unused old rules */
        list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
-               switch (trans->msg_type) {
-               case NFT_MSG_DELRULE:
-                       nf_tables_rule_destroy(&trans->ctx,
-                                              nft_trans_rule(trans));
-                       break;
-               case NFT_MSG_DELSET:
-                       nft_set_destroy(nft_trans_set(trans));
-                       break;
-               }
-               nft_trans_destroy(trans);
+               list_del(&trans->list);
+               trans->ctx.nla = NULL;
+               call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
        }
 
        return 0;
 }
 
+/* Schedule objects for release via rcu to make sure no packets are accesing
+ * aborted rules.
+ */
+static void nf_tables_abort_release_rcu(struct rcu_head *rt)
+{
+       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
+
+       switch (trans->msg_type) {
+       case NFT_MSG_NEWTABLE:
+               nf_tables_table_destroy(&trans->ctx);
+               break;
+       case NFT_MSG_NEWCHAIN:
+               nf_tables_chain_destroy(trans->ctx.chain);
+               break;
+       case NFT_MSG_NEWRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               break;
+       case NFT_MSG_NEWSET:
+               nft_set_destroy(nft_trans_set(trans));
+               break;
+       }
+       kfree(trans);
+}
+
 static int nf_tables_abort(struct sk_buff *skb)
 {
        struct net *net = sock_net(skb->sk);
        struct nft_trans *trans, *next;
+       struct nft_set *set;
 
        list_for_each_entry_safe(trans, next, &net->nft.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(trans->ctx.afi,
+                                                               trans->ctx.table);
+                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+                               }
+                               nft_trans_destroy(trans);
+                       } else {
+                               list_del(&trans->ctx.table->list);
+                       }
+                       break;
+               case NFT_MSG_DELTABLE:
+                       list_add_tail(&trans->ctx.table->list,
+                                     &trans->ctx.afi->tables);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWCHAIN:
+                       if (nft_trans_chain_update(trans)) {
+                               if (nft_trans_chain_stats(trans))
+                                       free_percpu(nft_trans_chain_stats(trans));
+
+                               nft_trans_destroy(trans);
+                       } else {
+                               list_del(&trans->ctx.chain->list);
+                               if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
+                                   trans->ctx.chain->flags & NFT_BASE_CHAIN) {
+                                       nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
+                                                           trans->ctx.afi->nops);
+                               }
+                       }
+                       break;
+               case NFT_MSG_DELCHAIN:
+                       list_add_tail(&trans->ctx.chain->list,
+                                     &trans->ctx.table->chains);
+                       nft_trans_destroy(trans);
+                       break;
                case NFT_MSG_NEWRULE:
                        list_del_rcu(&nft_trans_rule(trans)->list);
                        break;
@@ -3261,23 +3515,22 @@ static int nf_tables_abort(struct sk_buff *skb)
                                      &trans->ctx.table->sets);
                        nft_trans_destroy(trans);
                        break;
+               case NFT_MSG_NEWSETELEM:
+                       set = nft_trans_elem_set(trans);
+                       set->ops->get(set, &nft_trans_elem(trans));
+                       set->ops->remove(set, &nft_trans_elem(trans));
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSETELEM:
+                       nft_trans_destroy(trans);
+                       break;
                }
        }
 
-       /* Make sure we don't see any packet accessing aborted rules */
-       synchronize_rcu();
-
        list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
-               switch (trans->msg_type) {
-               case NFT_MSG_NEWRULE:
-                       nf_tables_rule_destroy(&trans->ctx,
-                                              nft_trans_rule(trans));
-                       break;
-               case NFT_MSG_NEWSET:
-                       nft_set_destroy(nft_trans_set(trans));
-                       break;
-               }
-               nft_trans_destroy(trans);
+               list_del(&trans->list);
+               trans->ctx.nla = NULL;
+               call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
        }
 
        return 0;