Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
[cascardo/linux.git] / net / netfilter / nf_tables_api.c
index deeb95f..19e79f0 100644 (file)
@@ -127,6 +127,204 @@ static void nft_trans_destroy(struct nft_trans *trans)
        kfree(trans);
 }
 
+static void nf_tables_unregister_hooks(const struct nft_table *table,
+                                      const struct nft_chain *chain,
+                                      unsigned int hook_nops)
+{
+       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+           chain->flags & NFT_BASE_CHAIN)
+               nf_unregister_hooks(nft_base_chain(chain)->ops, hook_nops);
+}
+
+/* Internal table flags */
+#define NFT_TABLE_INACTIVE     (1 << 15)
+
+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 nft_deltable(struct nft_ctx *ctx)
+{
+       int err;
+
+       err = nft_trans_table_add(ctx, NFT_MSG_DELTABLE);
+       if (err < 0)
+               return err;
+
+       list_del_rcu(&ctx->table->list);
+       return err;
+}
+
+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 int nft_delchain(struct nft_ctx *ctx)
+{
+       int err;
+
+       err = nft_trans_chain_add(ctx, NFT_MSG_DELCHAIN);
+       if (err < 0)
+               return err;
+
+       ctx->table->use--;
+       list_del_rcu(&ctx->chain->list);
+
+       return err;
+}
+
+static inline bool
+nft_rule_is_active(struct net *net, const struct nft_rule *rule)
+{
+       return (rule->genmask & (1 << net->nft.gencursor)) == 0;
+}
+
+static inline int gencursor_next(struct net *net)
+{
+       return net->nft.gencursor+1 == 1 ? 1 : 0;
+}
+
+static inline int
+nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
+{
+       return (rule->genmask & (1 << gencursor_next(net))) == 0;
+}
+
+static inline void
+nft_rule_activate_next(struct net *net, struct nft_rule *rule)
+{
+       /* Now inactive, will be active in the future */
+       rule->genmask = (1 << net->nft.gencursor);
+}
+
+static inline void
+nft_rule_deactivate_next(struct net *net, struct nft_rule *rule)
+{
+       rule->genmask = (1 << gencursor_next(net));
+}
+
+static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
+{
+       rule->genmask = 0;
+}
+
+static int
+nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
+{
+       /* You cannot delete the same rule twice */
+       if (nft_rule_is_active_next(ctx->net, rule)) {
+               nft_rule_deactivate_next(ctx->net, rule);
+               ctx->chain->use--;
+               return 0;
+       }
+       return -ENOENT;
+}
+
+static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
+                                           struct nft_rule *rule)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
+       if (trans == NULL)
+               return NULL;
+
+       nft_trans_rule(trans) = rule;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+       return trans;
+}
+
+static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
+{
+       struct nft_trans *trans;
+       int err;
+
+       trans = nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule);
+       if (trans == NULL)
+               return -ENOMEM;
+
+       err = nf_tables_delrule_deactivate(ctx, rule);
+       if (err < 0) {
+               nft_trans_destroy(trans);
+               return err;
+       }
+
+       return 0;
+}
+
+static int nft_delrule_by_chain(struct nft_ctx *ctx)
+{
+       struct nft_rule *rule;
+       int err;
+
+       list_for_each_entry(rule, &ctx->chain->rules, list) {
+               err = nft_delrule(ctx, rule);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+/* Internal set flag */
+#define NFT_SET_INACTIVE       (1 << 15)
+
+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 nft_delset(struct nft_ctx *ctx, struct nft_set *set)
+{
+       int err;
+
+       err = nft_trans_set_add(ctx, NFT_MSG_DELSET, set);
+       if (err < 0)
+               return err;
+
+       list_del_rcu(&set->list);
+       ctx->table->use--;
+
+       return err;
+}
+
 /*
  * Tables
  */
@@ -207,9 +405,9 @@ static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
        [NFTA_TABLE_FLAGS]      = { .type = NLA_U32 },
 };
 
-static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
-                                    int event, u32 flags, int family,
-                                    const struct nft_table *table)
+static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
+                                    u32 portid, u32 seq, int event, u32 flags,
+                                    int family, const struct nft_table *table)
 {
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfmsg;
@@ -222,7 +420,7 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
        nfmsg = nlmsg_data(nlh);
        nfmsg->nfgen_family     = family;
        nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = 0;
+       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)) ||
@@ -250,8 +448,8 @@ static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
-                                       ctx->afi->family, ctx->table);
+       err = nf_tables_fill_table_info(skb, ctx->net, ctx->portid, ctx->seq,
+                                       event, 0, ctx->afi->family, ctx->table);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
@@ -290,7 +488,7 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
                        if (idx > s_idx)
                                memset(&cb->args[1], 0,
                                       sizeof(cb->args) - sizeof(cb->args[0]));
-                       if (nf_tables_fill_table_info(skb,
+                       if (nf_tables_fill_table_info(skb, net,
                                                      NETLINK_CB(cb->skb).portid,
                                                      cb->nlh->nlmsg_seq,
                                                      NFT_MSG_NEWTABLE,
@@ -309,9 +507,6 @@ 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[])
@@ -345,7 +540,7 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
        if (!skb2)
                return -ENOMEM;
 
-       err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid,
+       err = nf_tables_fill_table_info(skb2, net, NETLINK_CB(skb).portid,
                                        nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
                                        family, table);
        if (err < 0)
@@ -443,21 +638,6 @@ err:
        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[])
@@ -527,6 +707,67 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        return 0;
 }
 
+static int nft_flush_table(struct nft_ctx *ctx)
+{
+       int err;
+       struct nft_chain *chain, *nc;
+       struct nft_set *set, *ns;
+
+       list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) {
+               ctx->chain = chain;
+
+               err = nft_delrule_by_chain(ctx);
+               if (err < 0)
+                       goto out;
+
+               err = nft_delchain(ctx);
+               if (err < 0)
+                       goto out;
+       }
+
+       list_for_each_entry_safe(set, ns, &ctx->table->sets, list) {
+               if (set->flags & NFT_SET_ANONYMOUS &&
+                   !list_empty(&set->bindings))
+                       continue;
+
+               err = nft_delset(ctx, set);
+               if (err < 0)
+                       goto out;
+       }
+
+       err = nft_deltable(ctx);
+out:
+       return err;
+}
+
+static int nft_flush(struct nft_ctx *ctx, int family)
+{
+       struct nft_af_info *afi;
+       struct nft_table *table, *nt;
+       const struct nlattr * const *nla = ctx->nla;
+       int err = 0;
+
+       list_for_each_entry(afi, &ctx->net->nft.af_info, list) {
+               if (family != AF_UNSPEC && afi->family != family)
+                       continue;
+
+               ctx->afi = afi;
+               list_for_each_entry_safe(table, nt, &afi->tables, list) {
+                       if (nla[NFTA_TABLE_NAME] &&
+                           nla_strcmp(nla[NFTA_TABLE_NAME], table->name) != 0)
+                               continue;
+
+                       ctx->table = table;
+
+                       err = nft_flush_table(ctx);
+                       if (err < 0)
+                               goto out;
+               }
+       }
+out:
+       return err;
+}
+
 static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -535,9 +776,13 @@ 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, err;
+       int family = nfmsg->nfgen_family;
        struct nft_ctx ctx;
 
+       nft_ctx_init(&ctx, skb, nlh, NULL, NULL, NULL, nla);
+       if (family == AF_UNSPEC || nla[NFTA_TABLE_NAME] == NULL)
+               return nft_flush(&ctx, family);
+
        afi = nf_tables_afinfo_lookup(net, family, false);
        if (IS_ERR(afi))
                return PTR_ERR(afi);
@@ -547,16 +792,11 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
                return PTR_ERR(table);
        if (table->flags & NFT_TABLE_INACTIVE)
                return -ENOENT;
-       if (table->use > 0)
-               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;
+       ctx.afi = afi;
+       ctx.table = table;
 
-       list_del_rcu(&table->list);
-       return 0;
+       return nft_flush_table(&ctx);
 }
 
 static void nf_tables_table_destroy(struct nft_ctx *ctx)
@@ -674,9 +914,9 @@ nla_put_failure:
        return -ENOSPC;
 }
 
-static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
-                                    int event, u32 flags, int family,
-                                    const struct nft_table *table,
+static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
+                                    u32 portid, u32 seq, int event, u32 flags,
+                                    int family, const struct nft_table *table,
                                     const struct nft_chain *chain)
 {
        struct nlmsghdr *nlh;
@@ -690,7 +930,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
        nfmsg = nlmsg_data(nlh);
        nfmsg->nfgen_family     = family;
        nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = 0;
+       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
 
        if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name))
                goto nla_put_failure;
@@ -748,8 +988,8 @@ static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
-                                       ctx->afi->family, ctx->table,
+       err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq,
+                                       event, 0, ctx->afi->family, ctx->table,
                                        ctx->chain);
        if (err < 0) {
                kfree_skb(skb);
@@ -791,7 +1031,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
                                if (idx > s_idx)
                                        memset(&cb->args[1], 0,
                                               sizeof(cb->args) - sizeof(cb->args[0]));
-                               if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid,
+                               if (nf_tables_fill_chain_info(skb, net,
+                                                             NETLINK_CB(cb->skb).portid,
                                                              cb->nlh->nlmsg_seq,
                                                              NFT_MSG_NEWCHAIN,
                                                              NLM_F_MULTI,
@@ -850,7 +1091,7 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
        if (!skb2)
                return -ENOMEM;
 
-       err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid,
+       err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid,
                                        nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
                                        family, table, chain);
        if (err < 0)
@@ -913,21 +1154,6 @@ 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);
@@ -1157,11 +1383,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        list_add_tail_rcu(&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);
-       }
+       nf_tables_unregister_hooks(table, chain, afi->nops);
 err1:
        nf_tables_chain_destroy(chain);
        return err;
@@ -1178,7 +1400,6 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
        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))
@@ -1199,13 +1420,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
                return -EBUSY;
 
        nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
-       err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
-       if (err < 0)
-               return err;
 
-       table->use--;
-       list_del_rcu(&chain->list);
-       return 0;
+       return nft_delchain(&ctx);
 }
 
 /*
@@ -1432,8 +1648,9 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
                                    .len = NFT_USERDATA_MAXLEN },
 };
 
-static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
-                                   int event, u32 flags, int family,
+static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
+                                   u32 portid, u32 seq, int event,
+                                   u32 flags, int family,
                                    const struct nft_table *table,
                                    const struct nft_chain *chain,
                                    const struct nft_rule *rule)
@@ -1453,7 +1670,7 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
        nfmsg = nlmsg_data(nlh);
        nfmsg->nfgen_family     = family;
        nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = 0;
+       nfmsg->res_id           = htons(net->nft.base_seq & 0xffff);
 
        if (nla_put_string(skb, NFTA_RULE_TABLE, table->name))
                goto nla_put_failure;
@@ -1509,8 +1726,8 @@ static int nf_tables_rule_notify(const struct nft_ctx *ctx,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
-                                      ctx->afi->family, ctx->table,
+       err = nf_tables_fill_rule_info(skb, ctx->net, ctx->portid, ctx->seq,
+                                      event, 0, ctx->afi->family, ctx->table,
                                       ctx->chain, rule);
        if (err < 0) {
                kfree_skb(skb);
@@ -1527,41 +1744,6 @@ err:
        return err;
 }
 
-static inline bool
-nft_rule_is_active(struct net *net, const struct nft_rule *rule)
-{
-       return (rule->genmask & (1 << net->nft.gencursor)) == 0;
-}
-
-static inline int gencursor_next(struct net *net)
-{
-       return net->nft.gencursor+1 == 1 ? 1 : 0;
-}
-
-static inline int
-nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
-{
-       return (rule->genmask & (1 << gencursor_next(net))) == 0;
-}
-
-static inline void
-nft_rule_activate_next(struct net *net, struct nft_rule *rule)
-{
-       /* Now inactive, will be active in the future */
-       rule->genmask = (1 << net->nft.gencursor);
-}
-
-static inline void
-nft_rule_disactivate_next(struct net *net, struct nft_rule *rule)
-{
-       rule->genmask = (1 << gencursor_next(net));
-}
-
-static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
-{
-       rule->genmask = 0;
-}
-
 static int nf_tables_dump_rules(struct sk_buff *skb,
                                struct netlink_callback *cb)
 {
@@ -1591,7 +1773,7 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
                                        if (idx > s_idx)
                                                memset(&cb->args[1], 0,
                                                       sizeof(cb->args) - sizeof(cb->args[0]));
-                                       if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid,
+                                       if (nf_tables_fill_rule_info(skb, net, NETLINK_CB(cb->skb).portid,
                                                                      cb->nlh->nlmsg_seq,
                                                                      NFT_MSG_NEWRULE,
                                                                      NLM_F_MULTI | NLM_F_APPEND,
@@ -1657,7 +1839,7 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
        if (!skb2)
                return -ENOMEM;
 
-       err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid,
+       err = nf_tables_fill_rule_info(skb2, net, NETLINK_CB(skb).portid,
                                       nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
                                       family, table, chain, rule);
        if (err < 0)
@@ -1687,21 +1869,6 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
        kfree(rule);
 }
 
-static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
-                                           struct nft_rule *rule)
-{
-       struct nft_trans *trans;
-
-       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
-       if (trans == NULL)
-               return NULL;
-
-       nft_trans_rule(trans) = rule;
-       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
-
-       return trans;
-}
-
 #define NFT_RULE_MAXEXPRS      128
 
 static struct nft_expr_info *info;
@@ -1823,7 +1990,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
                                err = -ENOMEM;
                                goto err2;
                        }
-                       nft_rule_disactivate_next(net, old_rule);
+                       nft_rule_deactivate_next(net, old_rule);
                        chain->use--;
                        list_add_tail_rcu(&rule->list, &old_rule->list);
                } else {
@@ -1867,33 +2034,6 @@ err1:
        return err;
 }
 
-static int
-nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
-{
-       /* You cannot delete the same rule twice */
-       if (nft_rule_is_active_next(ctx->net, rule)) {
-               if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL)
-                       return -ENOMEM;
-               nft_rule_disactivate_next(ctx->net, rule);
-               ctx->chain->use--;
-               return 0;
-       }
-       return -ENOENT;
-}
-
-static int nf_table_delrule_by_chain(struct nft_ctx *ctx)
-{
-       struct nft_rule *rule;
-       int err;
-
-       list_for_each_entry(rule, &ctx->chain->rules, list) {
-               err = nf_tables_delrule_one(ctx, rule);
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
 static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
                             const struct nlmsghdr *nlh,
                             const struct nlattr * const nla[])
@@ -1932,14 +2072,14 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
                        if (IS_ERR(rule))
                                return PTR_ERR(rule);
 
-                       err = nf_tables_delrule_one(&ctx, rule);
+                       err = nft_delrule(&ctx, rule);
                } else {
-                       err = nf_table_delrule_by_chain(&ctx);
+                       err = nft_delrule_by_chain(&ctx);
                }
        } else {
                list_for_each_entry(chain, &table->chains, list) {
                        ctx.chain = chain;
-                       err = nf_table_delrule_by_chain(&ctx);
+                       err = nft_delrule_by_chain(&ctx);
                        if (err < 0)
                                break;
                }
@@ -2183,7 +2323,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
        nfmsg = nlmsg_data(nlh);
        nfmsg->nfgen_family     = ctx->afi->family;
        nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = 0;
+       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;
@@ -2204,6 +2344,11 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                        goto nla_put_failure;
        }
 
+       if (set->policy != NFT_SET_POL_PERFORMANCE) {
+               if (nla_put_be32(skb, NFTA_SET_POLICY, htonl(set->policy)))
+                       goto nla_put_failure;
+       }
+
        desc = nla_nest_start(skb, NFTA_SET_DESC);
        if (desc == NULL)
                goto nla_put_failure;
@@ -2322,8 +2467,6 @@ static int nf_tables_dump_sets_done(struct netlink_callback *cb)
        return 0;
 }
 
-#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[])
@@ -2398,26 +2541,6 @@ static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
        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[])
@@ -2551,6 +2674,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        set->dlen  = desc.dlen;
        set->flags = flags;
        set->size  = desc.size;
+       set->policy = policy;
 
        err = ops->init(set, &desc, nla);
        if (err < 0)
@@ -2611,13 +2735,7 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
        if (!list_empty(&set->bindings))
                return -EBUSY;
 
-       err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
-       if (err < 0)
-               return err;
-
-       list_del_rcu(&set->list);
-       ctx.table->use--;
-       return 0;
+       return nft_delset(&ctx, set);
 }
 
 static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
@@ -2815,7 +2933,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        nfmsg = nlmsg_data(nlh);
        nfmsg->nfgen_family = ctx.afi->family;
        nfmsg->version      = NFNETLINK_V0;
-       nfmsg->res_id       = 0;
+       nfmsg->res_id       = htons(ctx.net->nft.base_seq & 0xffff);
 
        if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
                goto nla_put_failure;
@@ -2896,7 +3014,7 @@ static int nf_tables_fill_setelem_info(struct sk_buff *skb,
        nfmsg = nlmsg_data(nlh);
        nfmsg->nfgen_family     = ctx->afi->family;
        nfmsg->version          = NFNETLINK_V0;
-       nfmsg->res_id           = 0;
+       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;
@@ -3183,6 +3301,87 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        return err;
 }
 
+static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
+                                  u32 portid, u32 seq)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+       int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWGEN;
+
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), 0);
+       if (nlh == NULL)
+               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)))
+               goto nla_put_failure;
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -EMSGSIZE;
+}
+
+static int nf_tables_gen_notify(struct net *net, struct sk_buff *skb, int event)
+{
+       struct nlmsghdr *nlh = nlmsg_hdr(skb);
+       struct sk_buff *skb2;
+       int err;
+
+       if (nlmsg_report(nlh) &&
+           !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+               return 0;
+
+       err = -ENOBUFS;
+       skb2 = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb2 == NULL)
+               goto err;
+
+       err = nf_tables_fill_gen_info(skb2, net, NETLINK_CB(skb).portid,
+                                     nlh->nlmsg_seq);
+       if (err < 0) {
+               kfree_skb(skb2);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb2, net, NETLINK_CB(skb).portid,
+                            NFNLGRP_NFTABLES, nlmsg_report(nlh), GFP_KERNEL);
+err:
+       if (err < 0) {
+               nfnetlink_set_err(net, NETLINK_CB(skb).portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
+       return err;
+}
+
+static int nf_tables_getgen(struct sock *nlsk, struct sk_buff *skb,
+                           const struct nlmsghdr *nlh,
+                           const struct nlattr * const nla[])
+{
+       struct net *net = sock_net(skb->sk);
+       struct sk_buff *skb2;
+       int err;
+
+       skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb2 == NULL)
+               return -ENOMEM;
+
+       err = nf_tables_fill_gen_info(skb2, net, NETLINK_CB(skb).portid,
+                                     nlh->nlmsg_seq);
+       if (err < 0)
+               goto err;
+
+       return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
+err:
+       kfree_skb(skb2);
+       return err;
+}
+
 static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
        [NFT_MSG_NEWTABLE] = {
                .call_batch     = nf_tables_newtable,
@@ -3259,6 +3458,9 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
+       [NFT_MSG_GETGEN] = {
+               .call           = nf_tables_getgen,
+       },
 };
 
 static void nft_chain_commit_update(struct nft_trans *trans)
@@ -3352,11 +3554,9 @@ static int nf_tables_commit(struct sk_buff *skb)
                        break;
                case NFT_MSG_DELCHAIN:
                        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);
-                       }
+                       nf_tables_unregister_hooks(trans->ctx.table,
+                                                  trans->ctx.chain,
+                                                  trans->ctx.afi->nops);
                        break;
                case NFT_MSG_NEWRULE:
                        nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
@@ -3418,6 +3618,8 @@ static int nf_tables_commit(struct sk_buff *skb)
                call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
        }
 
+       nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
+
        return 0;
 }
 
@@ -3479,11 +3681,9 @@ static int nf_tables_abort(struct sk_buff *skb)
                        } else {
                                trans->ctx.table->use--;
                                list_del_rcu(&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);
-                               }
+                               nf_tables_unregister_hooks(trans->ctx.table,
+                                                          trans->ctx.chain,
+                                                          trans->ctx.afi->nops);
                        }
                        break;
                case NFT_MSG_DELCHAIN: