Merge tag 'nf-next-23-06-26' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilt...
authorJakub Kicinski <kuba@kernel.org>
Mon, 26 Jun 2023 19:59:18 +0000 (12:59 -0700)
committerJakub Kicinski <kuba@kernel.org>
Mon, 26 Jun 2023 19:59:18 +0000 (12:59 -0700)
Pablo Neira Ayuso says:

====================
Netfilter/IPVS updates for net-next

1) Allow slightly larger IPVS connection table size from Kconfig for
   64-bit arch, from Abhijeet Rastogi.

2) Since IPVS connection table might be larger than 2^20 after previous
   patch, allow to limit it depending on the available memory.
   Moreover, use kvmalloc. From Julian Anastasov.

3) Do not rebuild VLAN header in nft_payload when matching source and
   destination MAC address.

4) Remove nested rcu read lock side in ip_set_test(), from Florian Westphal.

5) Allow to update set size, also from Florian.

6) Improve NAT tuple selection when connection is closing,
   from Florian Westphal.

7) Support for resetting set element stateful expression, from Phil Sutter.

8) Use NLA_POLICY_MAX to narrow down maximum attribute value in nf_tables,
   from Florian Westphal.

* tag 'nf-next-23-06-26' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  netfilter: nf_tables: limit allowed range via nla_policy
  netfilter: nf_tables: Introduce NFT_MSG_GETSETELEM_RESET
  netfilter: snat: evict closing tcp entries on reply tuple collision
  netfilter: nf_tables: permit update of set size
  netfilter: ipset: remove rcu_read_lock_bh pair from ip_set_test
  netfilter: nft_payload: rebuild vlan header when needed
  ipvs: dynamically limit the connection hash table
  ipvs: increase ip_vs_conn_tab_bits range for 64BIT
====================

Link: https://lore.kernel.org/r/20230626064749.75525-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1  2 
include/net/netfilter/nf_tables.h
net/netfilter/ipset/ip_set_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nft_bitwise.c

@@@ -462,8 -462,7 +462,8 @@@ struct nft_set_ops 
                                               const struct nft_set *set,
                                               const struct nft_set_elem *elem,
                                               unsigned int flags);
 -
 +      void                            (*commit)(const struct nft_set *set);
 +      void                            (*abort)(const struct nft_set *set);
        u64                             (*privsize)(const struct nlattr * const nla[],
                                                    const struct nft_set_desc *desc);
        bool                            (*estimate)(const struct nft_set_desc *desc,
        int                             (*init)(const struct nft_set *set,
                                                const struct nft_set_desc *desc,
                                                const struct nlattr * const nla[]);
 -      void                            (*destroy)(const struct nft_set *set);
 +      void                            (*destroy)(const struct nft_ctx *ctx,
 +                                                 const struct nft_set *set);
        void                            (*gc_init)(const struct nft_set *set);
  
        unsigned int                    elemsize;
@@@ -559,7 -557,6 +559,7 @@@ struct nft_set 
        u16                             policy;
        u16                             udlen;
        unsigned char                   *udata;
 +      struct list_head                pending_update;
        /* runtime data below here */
        const struct nft_set_ops        *ops ____cacheline_aligned;
        u16                             flags:14,
@@@ -810,8 -807,6 +810,8 @@@ int nft_set_elem_expr_clone(const struc
                            struct nft_expr *expr_array[]);
  void nft_set_elem_destroy(const struct nft_set *set, void *elem,
                          bool destroy_expr);
 +void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
 +                              const struct nft_set *set, void *elem);
  
  /**
   *    struct nft_set_gc_batch_head - nf_tables set garbage collection batch
@@@ -904,7 -899,6 +904,7 @@@ struct nft_expr_type 
  
  enum nft_trans_phase {
        NFT_TRANS_PREPARE,
 +      NFT_TRANS_PREPARE_ERROR,
        NFT_TRANS_ABORT,
        NFT_TRANS_COMMIT,
        NFT_TRANS_RELEASE
@@@ -1013,10 -1007,7 +1013,10 @@@ static inline struct nft_userdata *nft_
        return (void *)&rule->data[rule->dlen];
  }
  
 -void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule);
 +void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule);
 +void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule,
 +                            enum nft_trans_phase phase);
 +void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule);
  
  static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
                                            struct nft_regs *regs,
@@@ -1111,8 -1102,6 +1111,8 @@@ int nft_setelem_validate(const struct n
                         const struct nft_set_iter *iter,
                         struct nft_set_elem *elem);
  int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);
 +int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
 +void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
  
  enum nft_chain_types {
        NFT_CHAIN_T_DEFAULT = 0,
@@@ -1149,17 -1138,11 +1149,17 @@@ int nft_chain_validate_dependency(cons
  int nft_chain_validate_hooks(const struct nft_chain *chain,
                               unsigned int hook_flags);
  
 +static inline bool nft_chain_binding(const struct nft_chain *chain)
 +{
 +      return chain->flags & NFT_CHAIN_BINDING;
 +}
 +
  static inline bool nft_chain_is_bound(struct nft_chain *chain)
  {
        return (chain->flags & NFT_CHAIN_BINDING) && chain->bound;
  }
  
 +int nft_chain_add(struct nft_table *table, struct nft_chain *chain);
  void nft_chain_del(struct nft_chain *chain);
  void nf_tables_chain_destroy(struct nft_ctx *ctx);
  
@@@ -1573,7 -1556,6 +1573,7 @@@ static inline void nft_set_elem_clear_b
   *    struct nft_trans - nf_tables object update in transaction
   *
   *    @list: used internally
 + *    @binding_list: list of objects with possible bindings
   *    @msg_type: message type
   *    @put_net: ctx->net needs to be put
   *    @ctx: transaction context
   */
  struct nft_trans {
        struct list_head                list;
 +      struct list_head                binding_list;
        int                             msg_type;
        bool                            put_net;
        struct nft_ctx                  ctx;
@@@ -1592,7 -1573,6 +1592,7 @@@ struct nft_trans_rule 
        struct nft_rule                 *rule;
        struct nft_flow_rule            *flow;
        u32                             rule_id;
 +      bool                            bound;
  };
  
  #define nft_trans_rule(trans) \
        (((struct nft_trans_rule *)trans->data)->flow)
  #define nft_trans_rule_id(trans)      \
        (((struct nft_trans_rule *)trans->data)->rule_id)
 +#define nft_trans_rule_bound(trans)   \
 +      (((struct nft_trans_rule *)trans->data)->bound)
  
  struct nft_trans_set {
        struct nft_set                  *set;
        u64                             timeout;
        bool                            update;
        bool                            bound;
+       u32                             size;
  };
  
  #define nft_trans_set(trans)  \
        (((struct nft_trans_set *)trans->data)->timeout)
  #define nft_trans_set_gc_int(trans)   \
        (((struct nft_trans_set *)trans->data)->gc_int)
+ #define nft_trans_set_size(trans)     \
+       (((struct nft_trans_set *)trans->data)->size)
  
  struct nft_trans_chain {
 +      struct nft_chain                *chain;
        bool                            update;
        char                            *name;
        struct nft_stats __percpu       *stats;
        u8                              policy;
 +      bool                            bound;
        u32                             chain_id;
        struct nft_base_chain           *basechain;
        struct list_head                hook_list;
  };
  
 +#define nft_trans_chain(trans)        \
 +      (((struct nft_trans_chain *)trans->data)->chain)
  #define nft_trans_chain_update(trans) \
        (((struct nft_trans_chain *)trans->data)->update)
  #define nft_trans_chain_name(trans)   \
        (((struct nft_trans_chain *)trans->data)->stats)
  #define nft_trans_chain_policy(trans) \
        (((struct nft_trans_chain *)trans->data)->policy)
 +#define nft_trans_chain_bound(trans)  \
 +      (((struct nft_trans_chain *)trans->data)->bound)
  #define nft_trans_chain_id(trans)     \
        (((struct nft_trans_chain *)trans->data)->chain_id)
  #define nft_trans_basechain(trans)    \
@@@ -1726,7 -1701,6 +1729,7 @@@ static inline int nft_request_module(st
  struct nftables_pernet {
        struct list_head        tables;
        struct list_head        commit_list;
 +      struct list_head        binding_list;
        struct list_head        module_list;
        struct list_head        notify_list;
        struct mutex            commit_mutex;
@@@ -739,9 -739,7 +739,7 @@@ ip_set_test(ip_set_id_t index, const st
            !(opt->family == set->family || set->family == NFPROTO_UNSPEC))
                return 0;
  
-       rcu_read_lock_bh();
        ret = set->variant->kadt(set, skb, par, IPSET_TEST, opt);
-       rcu_read_unlock_bh();
  
        if (ret == -EAGAIN) {
                /* Type requests element to be completed */
@@@ -1694,14 -1692,6 +1692,14 @@@ call_ad(struct net *net, struct sock *c
        bool eexist = flags & IPSET_FLAG_EXIST, retried = false;
  
        do {
 +              if (retried) {
 +                      __ip_set_get(set);
 +                      nfnl_unlock(NFNL_SUBSYS_IPSET);
 +                      cond_resched();
 +                      nfnl_lock(NFNL_SUBSYS_IPSET);
 +                      __ip_set_put(set);
 +              }
 +
                ip_set_lock(set);
                ret = set->variant->uadt(set, tb, adt, &lineno, flags, retried);
                ip_set_unlock(set);
@@@ -151,7 -151,6 +151,7 @@@ static struct nft_trans *nft_trans_allo
                return NULL;
  
        INIT_LIST_HEAD(&trans->list);
 +      INIT_LIST_HEAD(&trans->binding_list);
        trans->msg_type = msg_type;
        trans->ctx      = *ctx;
  
@@@ -164,20 -163,13 +164,20 @@@ static struct nft_trans *nft_trans_allo
        return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL);
  }
  
 -static void nft_trans_destroy(struct nft_trans *trans)
 +static void nft_trans_list_del(struct nft_trans *trans)
  {
        list_del(&trans->list);
 +      list_del(&trans->binding_list);
 +}
 +
 +static void nft_trans_destroy(struct nft_trans *trans)
 +{
 +      nft_trans_list_del(trans);
        kfree(trans);
  }
  
 -static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
 +static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set,
 +                               bool bind)
  {
        struct nftables_pernet *nft_net;
        struct net *net = ctx->net;
                switch (trans->msg_type) {
                case NFT_MSG_NEWSET:
                        if (nft_trans_set(trans) == set)
 -                              nft_trans_set_bound(trans) = true;
 +                              nft_trans_set_bound(trans) = bind;
                        break;
                case NFT_MSG_NEWSETELEM:
                        if (nft_trans_elem_set(trans) == set)
 -                              nft_trans_elem_set_bound(trans) = true;
 +                              nft_trans_elem_set_bound(trans) = bind;
                        break;
                }
        }
  }
  
 +static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
 +{
 +      return __nft_set_trans_bind(ctx, set, true);
 +}
 +
 +static void nft_set_trans_unbind(const struct nft_ctx *ctx, struct nft_set *set)
 +{
 +      return __nft_set_trans_bind(ctx, set, false);
 +}
 +
 +static void __nft_chain_trans_bind(const struct nft_ctx *ctx,
 +                                 struct nft_chain *chain, bool bind)
 +{
 +      struct nftables_pernet *nft_net;
 +      struct net *net = ctx->net;
 +      struct nft_trans *trans;
 +
 +      if (!nft_chain_binding(chain))
 +              return;
 +
 +      nft_net = nft_pernet(net);
 +      list_for_each_entry_reverse(trans, &nft_net->commit_list, list) {
 +              switch (trans->msg_type) {
 +              case NFT_MSG_NEWCHAIN:
 +                      if (nft_trans_chain(trans) == chain)
 +                              nft_trans_chain_bound(trans) = bind;
 +                      break;
 +              case NFT_MSG_NEWRULE:
 +                      if (trans->ctx.chain == chain)
 +                              nft_trans_rule_bound(trans) = bind;
 +                      break;
 +              }
 +      }
 +}
 +
 +static void nft_chain_trans_bind(const struct nft_ctx *ctx,
 +                               struct nft_chain *chain)
 +{
 +      __nft_chain_trans_bind(ctx, chain, true);
 +}
 +
 +int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
 +{
 +      if (!nft_chain_binding(chain))
 +              return 0;
 +
 +      if (nft_chain_binding(ctx->chain))
 +              return -EOPNOTSUPP;
 +
 +      if (chain->bound)
 +              return -EBUSY;
 +
 +      chain->bound = true;
 +      chain->use++;
 +      nft_chain_trans_bind(ctx, chain);
 +
 +      return 0;
 +}
 +
 +void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
 +{
 +      __nft_chain_trans_bind(ctx, chain, false);
 +}
 +
  static int nft_netdev_register_hooks(struct net *net,
                                     struct list_head *hook_list)
  {
@@@ -364,19 -292,6 +364,19 @@@ static void nft_trans_commit_list_add_t
  {
        struct nftables_pernet *nft_net = nft_pernet(net);
  
 +      switch (trans->msg_type) {
 +      case NFT_MSG_NEWSET:
 +              if (!nft_trans_set_update(trans) &&
 +                  nft_set_is_anonymous(nft_trans_set(trans)))
 +                      list_add_tail(&trans->binding_list, &nft_net->binding_list);
 +              break;
 +      case NFT_MSG_NEWCHAIN:
 +              if (!nft_trans_chain_update(trans) &&
 +                  nft_chain_binding(nft_trans_chain(trans)))
 +                      list_add_tail(&trans->binding_list, &nft_net->binding_list);
 +              break;
 +      }
 +
        list_add_tail(&trans->list, &nft_net->commit_list);
  }
  
@@@ -423,9 -338,8 +423,9 @@@ static struct nft_trans *nft_trans_chai
                                ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID]));
                }
        }
 -
 +      nft_trans_chain(trans) = ctx->chain;
        nft_trans_commit_list_add_tail(ctx->net, trans);
 +
        return trans;
  }
  
@@@ -443,7 -357,8 +443,7 @@@ static int nft_delchain(struct nft_ctx 
        return 0;
  }
  
 -static void nft_rule_expr_activate(const struct nft_ctx *ctx,
 -                                 struct nft_rule *rule)
 +void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule)
  {
        struct nft_expr *expr;
  
        }
  }
  
 -static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
 -                                   struct nft_rule *rule,
 -                                   enum nft_trans_phase phase)
 +void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule,
 +                            enum nft_trans_phase phase)
  {
        struct nft_expr *expr;
  
@@@ -567,6 -483,7 +567,7 @@@ static int __nft_trans_set_add(const st
                nft_trans_set_update(trans) = true;
                nft_trans_set_gc_int(trans) = desc->gc_int;
                nft_trans_set_timeout(trans) = desc->timeout;
+               nft_trans_set_size(trans) = desc->size;
        }
        nft_trans_commit_list_add_tail(ctx->net, trans);
  
@@@ -579,58 -496,6 +580,58 @@@ static int nft_trans_set_add(const stru
        return __nft_trans_set_add(ctx, msg_type, set, NULL);
  }
  
 +static void nft_setelem_data_deactivate(const struct net *net,
 +                                      const struct nft_set *set,
 +                                      struct nft_set_elem *elem);
 +
 +static int nft_mapelem_deactivate(const struct nft_ctx *ctx,
 +                                struct nft_set *set,
 +                                const struct nft_set_iter *iter,
 +                                struct nft_set_elem *elem)
 +{
 +      nft_setelem_data_deactivate(ctx->net, set, elem);
 +
 +      return 0;
 +}
 +
 +struct nft_set_elem_catchall {
 +      struct list_head        list;
 +      struct rcu_head         rcu;
 +      void                    *elem;
 +};
 +
 +static void nft_map_catchall_deactivate(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;
 +
 +      list_for_each_entry(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;
 +              nft_setelem_data_deactivate(ctx->net, set, &elem);
 +              break;
 +      }
 +}
 +
 +static void nft_map_deactivate(const struct nft_ctx *ctx, struct nft_set *set)
 +{
 +      struct nft_set_iter iter = {
 +              .genmask        = nft_genmask_next(ctx->net),
 +              .fn             = nft_mapelem_deactivate,
 +      };
 +
 +      set->ops->walk(ctx, set, &iter);
 +      WARN_ON_ONCE(iter.err);
 +
 +      nft_map_catchall_deactivate(ctx, set);
 +}
 +
  static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
  {
        int err;
        if (err < 0)
                return err;
  
 +      if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
 +              nft_map_deactivate(ctx, set);
 +
        nft_deactivate_next(ctx->net, set);
        ctx->table->use--;
  
@@@ -1739,8 -1601,6 +1740,8 @@@ static int nft_dump_basechain_hook(stru
  
        if (nft_base_chain_netdev(family, ops->hooknum)) {
                nest_devs = nla_nest_start_noflag(skb, NFTA_HOOK_DEVS);
 +              if (!nest_devs)
 +                      goto nla_put_failure;
  
                if (!hook_list)
                        hook_list = &basechain->hook_list;
@@@ -2365,7 -2225,7 +2366,7 @@@ static int nft_basechain_init(struct nf
        return 0;
  }
  
 -static int nft_chain_add(struct nft_table *table, struct nft_chain *chain)
 +int nft_chain_add(struct nft_table *table, struct nft_chain *chain)
  {
        int err;
  
@@@ -2667,8 -2527,6 +2668,8 @@@ static int nf_tables_updchain(struct nf
        nft_trans_basechain(trans) = basechain;
        INIT_LIST_HEAD(&nft_trans_chain_hooks(trans));
        list_splice(&hook.list, &nft_trans_chain_hooks(trans));
 +      if (nla[NFTA_CHAIN_HOOK])
 +              module_put(hook.type->owner);
  
        nft_trans_commit_list_add_tail(ctx->net, trans);
  
@@@ -2811,18 -2669,21 +2812,18 @@@ static int nf_tables_newchain(struct sk
        return nf_tables_addchain(&ctx, family, genmask, policy, flags, extack);
  }
  
 -static int nft_delchain_hook(struct nft_ctx *ctx, struct nft_chain *chain,
 +static int nft_delchain_hook(struct nft_ctx *ctx,
 +                           struct nft_base_chain *basechain,
                             struct netlink_ext_ack *extack)
  {
 +      const struct nft_chain *chain = &basechain->chain;
        const struct nlattr * const *nla = ctx->nla;
        struct nft_chain_hook chain_hook = {};
 -      struct nft_base_chain *basechain;
        struct nft_hook *this, *hook;
        LIST_HEAD(chain_del_list);
        struct nft_trans *trans;
        int err;
  
 -      if (!nft_is_base_chain(chain))
 -              return -EOPNOTSUPP;
 -
 -      basechain = nft_base_chain(chain);
        err = nft_chain_parse_hook(ctx->net, basechain, nla, &chain_hook,
                                   ctx->family, chain->flags, extack);
        if (err < 0)
@@@ -2907,12 -2768,7 +2908,12 @@@ static int nf_tables_delchain(struct sk
                if (chain->flags & NFT_CHAIN_HW_OFFLOAD)
                        return -EOPNOTSUPP;
  
 -              return nft_delchain_hook(&ctx, chain, extack);
 +              if (nft_is_base_chain(chain)) {
 +                      struct nft_base_chain *basechain = nft_base_chain(chain);
 +
 +                      if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
 +                              return nft_delchain_hook(&ctx, basechain, extack);
 +              }
        }
  
        if (info->nlh->nlmsg_flags & NLM_F_NONREC &&
@@@ -3633,7 -3489,8 +3634,7 @@@ err_fill_rule_info
        return err;
  }
  
 -static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
 -                                 struct nft_rule *rule)
 +void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule)
  {
        struct nft_expr *expr, *next;
  
        kfree(rule);
  }
  
 -void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
 +static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
  {
        nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
        nf_tables_rule_destroy(ctx, rule);
@@@ -3738,6 -3595,12 +3739,6 @@@ int nft_setelem_validate(const struct n
        return 0;
  }
  
 -struct nft_set_elem_catchall {
 -      struct list_head        list;
 -      struct rcu_head         rcu;
 -      void                    *elem;
 -};
 -
  int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set)
  {
        u8 genmask = nft_genmask_next(ctx->net);
@@@ -3980,8 -3843,7 +3981,8 @@@ err_destroy_flow_rule
        if (flow)
                nft_flow_rule_destroy(flow);
  err_release_rule:
 -      nf_tables_rule_release(&ctx, rule);
 +      nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR);
 +      nf_tables_rule_destroy(&ctx, rule);
  err_release_expr:
        for (i = 0; i < n; i++) {
                if (expr_info[i].ops) {
@@@ -4913,9 -4775,6 +4914,9 @@@ static int nf_tables_newset(struct sk_b
                if (!(flags & NFT_SET_TIMEOUT))
                        return -EINVAL;
  
 +              if (flags & NFT_SET_ANONYMOUS)
 +                      return -EOPNOTSUPP;
 +
                err = nf_msecs_to_jiffies64(nla[NFTA_SET_TIMEOUT], &desc.timeout);
                if (err)
                        return err;
        if (nla[NFTA_SET_GC_INTERVAL] != NULL) {
                if (!(flags & NFT_SET_TIMEOUT))
                        return -EINVAL;
 +
 +              if (flags & NFT_SET_ANONYMOUS)
 +                      return -EOPNOTSUPP;
 +
                desc.gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL]));
        }
  
                if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
  
 +              if (nft_set_is_anonymous(set))
 +                      return -EOPNOTSUPP;
 +
                err = nft_set_expr_alloc(&ctx, set, nla, exprs, &num_exprs, flags);
                if (err < 0)
                        return err;
  
        set->num_exprs = num_exprs;
        set->handle = nf_tables_alloc_handle(table);
 +      INIT_LIST_HEAD(&set->pending_update);
  
        err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
        if (err < 0)
@@@ -5080,7 -4931,7 +5081,7 @@@ err_set_expr_alloc
        for (i = 0; i < set->num_exprs; i++)
                nft_expr_destroy(&ctx, set->exprs[i]);
  err_set_destroy:
 -      ops->destroy(set);
 +      ops->destroy(&ctx, set);
  err_set_init:
        kfree(set->name);
  err_set_name:
@@@ -5095,7 -4946,7 +5096,7 @@@ static void nft_set_catchall_destroy(co
  
        list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                list_del_rcu(&catchall->list);
 -              nft_set_elem_destroy(set, catchall->elem, true);
 +              nf_tables_set_elem_destroy(ctx, set, catchall->elem);
                kfree_rcu(catchall, rcu);
        }
  }
@@@ -5110,7 -4961,7 +5111,7 @@@ static void nft_set_destroy(const struc
        for (i = 0; i < set->num_exprs; i++)
                nft_expr_destroy(ctx, set->exprs[i]);
  
 -      set->ops->destroy(set);
 +      set->ops->destroy(ctx, set);
        nft_set_catchall_destroy(ctx, set);
        kfree(set->name);
        kvfree(set);
@@@ -5275,60 -5126,10 +5276,60 @@@ static void nf_tables_unbind_set(const 
        }
  }
  
 +static void nft_setelem_data_activate(const struct net *net,
 +                                    const struct nft_set *set,
 +                                    struct nft_set_elem *elem);
 +
 +static int nft_mapelem_activate(const struct nft_ctx *ctx,
 +                              struct nft_set *set,
 +                              const struct nft_set_iter *iter,
 +                              struct nft_set_elem *elem)
 +{
 +      nft_setelem_data_activate(ctx->net, set, elem);
 +
 +      return 0;
 +}
 +
 +static void nft_map_catchall_activate(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;
 +
 +      list_for_each_entry(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;
 +              nft_setelem_data_activate(ctx->net, set, &elem);
 +              break;
 +      }
 +}
 +
 +static void nft_map_activate(const struct nft_ctx *ctx, struct nft_set *set)
 +{
 +      struct nft_set_iter iter = {
 +              .genmask        = nft_genmask_next(ctx->net),
 +              .fn             = nft_mapelem_activate,
 +      };
 +
 +      set->ops->walk(ctx, set, &iter);
 +      WARN_ON_ONCE(iter.err);
 +
 +      nft_map_catchall_activate(ctx, set);
 +}
 +
  void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set)
  {
 -      if (nft_set_is_anonymous(set))
 +      if (nft_set_is_anonymous(set)) {
 +              if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
 +                      nft_map_activate(ctx, set);
 +
                nft_clear(ctx->net, set);
 +      }
  
        set->use++;
  }
@@@ -5339,28 -5140,14 +5340,28 @@@ void nf_tables_deactivate_set(const str
                              enum nft_trans_phase phase)
  {
        switch (phase) {
 -      case NFT_TRANS_PREPARE:
 +      case NFT_TRANS_PREPARE_ERROR:
 +              nft_set_trans_unbind(ctx, set);
                if (nft_set_is_anonymous(set))
                        nft_deactivate_next(ctx->net, set);
  
                set->use--;
 +              break;
 +      case NFT_TRANS_PREPARE:
 +              if (nft_set_is_anonymous(set)) {
 +                      if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
 +                              nft_map_deactivate(ctx, set);
 +
 +                      nft_deactivate_next(ctx->net, set);
 +              }
 +              set->use--;
                return;
        case NFT_TRANS_ABORT:
        case NFT_TRANS_RELEASE:
 +              if (nft_set_is_anonymous(set) &&
 +                  set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
 +                      nft_map_deactivate(ctx, set);
 +
                set->use--;
                fallthrough;
        default:
@@@ -5442,7 -5229,8 +5443,8 @@@ static const struct nla_policy nft_set_
  
  static int nft_set_elem_expr_dump(struct sk_buff *skb,
                                  const struct nft_set *set,
-                                 const struct nft_set_ext *ext)
+                                 const struct nft_set_ext *ext,
+                                 bool reset)
  {
        struct nft_set_elem_expr *elem_expr;
        u32 size, num_exprs = 0;
  
        if (num_exprs == 1) {
                expr = nft_setelem_expr_at(elem_expr, 0);
-               if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr, false) < 0)
+               if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr, reset) < 0)
                        return -1;
  
                return 0;
  
                nft_setelem_expr_foreach(expr, elem_expr, size) {
                        expr = nft_setelem_expr_at(elem_expr, size);
-                       if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr, false) < 0)
+                       if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr, reset) < 0)
                                goto nla_put_failure;
                }
                nla_nest_end(skb, nest);
@@@ -5479,11 -5267,13 +5481,13 @@@ nla_put_failure
  
  static int nf_tables_fill_setelem(struct sk_buff *skb,
                                  const struct nft_set *set,
-                                 const struct nft_set_elem *elem)
+                                 const struct nft_set_elem *elem,
+                                 bool reset)
  {
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
+       u64 timeout = 0;
  
        nest = nla_nest_start_noflag(skb, NFTA_LIST_ELEM);
        if (nest == NULL)
                goto nla_put_failure;
  
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS) &&
-           nft_set_elem_expr_dump(skb, set, ext))
+           nft_set_elem_expr_dump(skb, set, ext, reset))
                goto nla_put_failure;
  
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) &&
                         htonl(*nft_set_ext_flags(ext))))
                goto nla_put_failure;
  
-       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
-           nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
-                        nf_jiffies64_to_msecs(*nft_set_ext_timeout(ext)),
-                        NFTA_SET_ELEM_PAD))
-               goto nla_put_failure;
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT)) {
+               timeout = *nft_set_ext_timeout(ext);
+               if (nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
+                                nf_jiffies64_to_msecs(timeout),
+                                NFTA_SET_ELEM_PAD))
+                       goto nla_put_failure;
+       } else if (set->flags & NFT_SET_TIMEOUT) {
+               timeout = READ_ONCE(set->timeout);
+       }
  
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
                u64 expires, now = get_jiffies_64();
                                 nf_jiffies64_to_msecs(expires),
                                 NFTA_SET_ELEM_PAD))
                        goto nla_put_failure;
+               if (reset)
+                       *nft_set_ext_expiration(ext) = now + timeout;
        }
  
        if (nft_set_ext_exists(ext, NFT_SET_EXT_USERDATA)) {
@@@ -5561,6 -5358,7 +5572,7 @@@ struct nft_set_dump_args 
        const struct netlink_callback   *cb;
        struct nft_set_iter             iter;
        struct sk_buff                  *skb;
+       bool                            reset;
  };
  
  static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
        struct nft_set_dump_args *args;
  
        args = container_of(iter, struct nft_set_dump_args, iter);
-       return nf_tables_fill_setelem(args->skb, set, elem);
+       return nf_tables_fill_setelem(args->skb, set, elem, args->reset);
  }
  
  struct nft_set_dump_ctx {
  };
  
  static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb,
-                                const struct nft_set *set)
+                                const struct nft_set *set, bool reset)
  {
        struct nft_set_elem_catchall *catchall;
        u8 genmask = nft_genmask_cur(net);
                        continue;
  
                elem.priv = catchall->elem;
-               ret = nf_tables_fill_setelem(skb, set, &elem);
+               ret = nf_tables_fill_setelem(skb, set, &elem, reset);
                break;
        }
  
@@@ -5613,6 -5411,7 +5625,7 @@@ static int nf_tables_dump_set(struct sk
        bool set_found = false;
        struct nlmsghdr *nlh;
        struct nlattr *nest;
+       bool reset = false;
        u32 portid, seq;
        int event;
  
        if (nest == NULL)
                goto nla_put_failure;
  
+       if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET)
+               reset = true;
        args.cb                 = cb;
        args.skb                = skb;
+       args.reset              = reset;
        args.iter.genmask       = nft_genmask_cur(net);
        args.iter.skip          = cb->args[0];
        args.iter.count         = 0;
        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);
+               args.iter.err = nft_set_catchall_dump(net, skb, set, reset);
        rcu_read_unlock();
  
        nla_nest_end(skb, nest);
@@@ -5708,7 -5511,8 +5725,8 @@@ static int nf_tables_fill_setelem_info(
                                       const struct nft_ctx *ctx, u32 seq,
                                       u32 portid, int event, u16 flags,
                                       const struct nft_set *set,
-                                      const struct nft_set_elem *elem)
+                                      const struct nft_set_elem *elem,
+                                      bool reset)
  {
        struct nlmsghdr *nlh;
        struct nlattr *nest;
        if (nest == NULL)
                goto nla_put_failure;
  
-       err = nf_tables_fill_setelem(skb, set, elem);
+       err = nf_tables_fill_setelem(skb, set, elem, reset);
        if (err < 0)
                goto nla_put_failure;
  
@@@ -5835,7 -5639,7 +5853,7 @@@ static int nft_setelem_get(struct nft_c
  }
  
  static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
-                           const struct nlattr *attr)
+                           const struct nlattr *attr, bool reset)
  {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        struct nft_set_elem elem;
                return err;
  
        err = nf_tables_fill_setelem_info(skb, ctx, ctx->seq, ctx->portid,
-                                         NFT_MSG_NEWSETELEM, 0, set, &elem);
+                                         NFT_MSG_NEWSETELEM, 0, set, &elem,
+                                         reset);
        if (err < 0)
                goto err_fill_setelem;
  
@@@ -5903,6 -5708,7 +5922,7 @@@ static int nf_tables_getsetelem(struct 
        struct nft_set *set;
        struct nlattr *attr;
        struct nft_ctx ctx;
+       bool reset = false;
        int rem, err = 0;
  
        table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
        if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
                return -EINVAL;
  
+       if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET)
+               reset = true;
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
-               err = nft_get_set_elem(&ctx, set, attr);
+               err = nft_get_set_elem(&ctx, set, attr, reset);
                if (err < 0) {
                        NL_SET_BAD_ATTR(extack, attr);
                        break;
@@@ -5971,7 -5780,7 +5994,7 @@@ static void nf_tables_setelem_notify(co
                flags |= ctx->flags & (NLM_F_CREATE | NLM_F_EXCL);
  
        err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
-                                         set, elem);
+                                         set, elem, false);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
@@@ -6113,7 -5922,6 +6136,7 @@@ static void nft_set_elem_expr_destroy(c
                __nft_set_elem_expr_destroy(ctx, expr);
  }
  
 +/* Drop references and destroy. Called from gc, dynset and abort path. */
  void nft_set_elem_destroy(const struct nft_set *set, void *elem,
                          bool destroy_expr)
  {
  }
  EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
  
 -/* Only called from commit path, nft_setelem_data_deactivate() already deals
 - * with the refcounting from the preparation phase.
 +/* Destroy element. References have been already dropped in the preparation
 + * path via nft_setelem_data_deactivate().
   */
 -static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
 -                                     const struct nft_set *set, void *elem)
 +void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
 +                              const struct nft_set *set, void *elem)
  {
        struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
  
@@@ -6702,19 -6510,19 +6725,19 @@@ static int nft_add_set_elem(struct nft_
        if (flags)
                *nft_set_ext_flags(ext) = flags;
  
 +      if (obj) {
 +              *nft_set_ext_obj(ext) = obj;
 +              obj->use++;
 +      }
        if (ulen > 0) {
                if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
                        err = -EINVAL;
 -                      goto err_elem_userdata;
 +                      goto err_elem_free;
                }
                udata = nft_set_ext_userdata(ext);
                udata->len = ulen - 1;
                nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
        }
 -      if (obj) {
 -              *nft_set_ext_obj(ext) = obj;
 -              obj->use++;
 -      }
        err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
        if (err < 0)
                goto err_elem_free;
@@@ -6772,7 -6580,10 +6795,7 @@@ err_set_full
  err_element_clash:
        kfree(trans);
  err_elem_free:
 -      if (obj)
 -              obj->use--;
 -err_elem_userdata:
 -      nf_tables_set_elem_destroy(ctx, set, elem.priv);
 +      nft_set_elem_destroy(set, elem.priv, true);
  err_parse_data:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
                nft_data_release(&elem.data.val, desc.type);
@@@ -6816,8 -6627,7 +6839,8 @@@ static int nf_tables_newsetelem(struct 
        if (IS_ERR(set))
                return PTR_ERR(set);
  
 -      if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
 +      if (!list_empty(&set->bindings) &&
 +          (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS)))
                return -EBUSY;
  
        nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
  void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
  {
        struct nft_chain *chain;
 -      struct nft_rule *rule;
  
        if (type == NFT_DATA_VERDICT) {
                switch (data->verdict.code) {
                case NFT_GOTO:
                        chain = data->verdict.chain;
                        chain->use++;
 -
 -                      if (!nft_chain_is_bound(chain))
 -                              break;
 -
 -                      chain->table->use++;
 -                      list_for_each_entry(rule, &chain->rules, list)
 -                              chain->use++;
 -
 -                      nft_chain_add(chain->table, chain);
                        break;
                }
        }
@@@ -7091,9 -6911,7 +7114,9 @@@ static int nf_tables_delsetelem(struct 
        set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask);
        if (IS_ERR(set))
                return PTR_ERR(set);
 -      if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
 +
 +      if (!list_empty(&set->bindings) &&
 +          (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS)))
                return -EBUSY;
  
        nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
@@@ -7875,7 -7693,6 +7898,7 @@@ void nf_tables_deactivate_flowtable(con
                                    enum nft_trans_phase phase)
  {
        switch (phase) {
 +      case NFT_TRANS_PREPARE_ERROR:
        case NFT_TRANS_PREPARE:
        case NFT_TRANS_ABORT:
        case NFT_TRANS_RELEASE:
@@@ -8920,6 -8737,12 +8943,12 @@@ static const struct nfnl_callback nf_ta
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
+       [NFT_MSG_GETSETELEM_RESET] = {
+               .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           = nf_tables_delsetelem,
                .type           = NFNL_CB_BATCH,
@@@ -9148,7 -8971,7 +9177,7 @@@ static void nf_tables_trans_destroy_wor
        synchronize_rcu();
  
        list_for_each_entry_safe(trans, next, &head, list) {
 -              list_del(&trans->list);
 +              nft_trans_list_del(trans);
                nft_commit_release(trans);
        }
  }
@@@ -9214,7 -9037,7 +9243,7 @@@ static int nf_tables_commit_chain_prepa
                                continue;
                        }
  
 -                      if (WARN_ON_ONCE(data + expr->ops->size > data_boundary))
 +                      if (WARN_ON_ONCE(data + size + expr->ops->size > data_boundary))
                                return -ENOMEM;
  
                        memcpy(data + size, expr, expr->ops->size);
@@@ -9482,25 -9305,10 +9511,25 @@@ static void nf_tables_commit_audit_log(
        }
  }
  
 +static void nft_set_commit_update(struct list_head *set_update_list)
 +{
 +      struct nft_set *set, *next;
 +
 +      list_for_each_entry_safe(set, next, set_update_list, pending_update) {
 +              list_del_init(&set->pending_update);
 +
 +              if (!set->ops->commit)
 +                      continue;
 +
 +              set->ops->commit(set);
 +      }
 +}
 +
  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;
 +      LIST_HEAD(set_update_list);
        struct nft_trans_elem *te;
        struct nft_chain *chain;
        struct nft_table *table;
                return 0;
        }
  
 +      list_for_each_entry(trans, &nft_net->binding_list, binding_list) {
 +              switch (trans->msg_type) {
 +              case NFT_MSG_NEWSET:
 +                      if (!nft_trans_set_update(trans) &&
 +                          nft_set_is_anonymous(nft_trans_set(trans)) &&
 +                          !nft_trans_set_bound(trans)) {
 +                              pr_warn_once("nftables ruleset with unbound set\n");
 +                              return -EINVAL;
 +                      }
 +                      break;
 +              case NFT_MSG_NEWCHAIN:
 +                      if (!nft_trans_chain_update(trans) &&
 +                          nft_chain_binding(nft_trans_chain(trans)) &&
 +                          !nft_trans_chain_bound(trans)) {
 +                              pr_warn_once("nftables ruleset with unbound chain\n");
 +                              return -EINVAL;
 +                      }
 +                      break;
 +              }
 +      }
 +
        /* 0. Validate ruleset, otherwise roll back for error reporting. */
        if (nf_tables_validate(net) < 0)
                return -EAGAIN;
  
                                WRITE_ONCE(set->timeout, nft_trans_set_timeout(trans));
                                WRITE_ONCE(set->gc_int, nft_trans_set_gc_int(trans));
+                               if (nft_trans_set_size(trans))
+                                       WRITE_ONCE(set->size, nft_trans_set_size(trans));
                        } else {
                                nft_clear(net, nft_trans_set(trans));
                                /* This avoids hitting -EBUSY when deleting the table
                        nf_tables_setelem_notify(&trans->ctx, te->set,
                                                 &te->elem,
                                                 NFT_MSG_NEWSETELEM);
 +                      if (te->set->ops->commit &&
 +                          list_empty(&te->set->pending_update)) {
 +                              list_add_tail(&te->set->pending_update,
 +                                            &set_update_list);
 +                      }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM:
                                atomic_dec(&te->set->nelems);
                                te->set->ndeact--;
                        }
 +                      if (te->set->ops->commit &&
 +                          list_empty(&te->set->pending_update)) {
 +                              list_add_tail(&te->set->pending_update,
 +                                            &set_update_list);
 +                      }
                        break;
                case NFT_MSG_NEWOBJ:
                        if (nft_trans_obj_update(trans)) {
                }
        }
  
 +      nft_set_commit_update(&set_update_list);
 +
        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);
@@@ -9843,25 -9621,10 +9875,25 @@@ static void nf_tables_abort_release(str
        kfree(trans);
  }
  
 +static void nft_set_abort_update(struct list_head *set_update_list)
 +{
 +      struct nft_set *set, *next;
 +
 +      list_for_each_entry_safe(set, next, set_update_list, pending_update) {
 +              list_del_init(&set->pending_update);
 +
 +              if (!set->ops->abort)
 +                      continue;
 +
 +              set->ops->abort(set);
 +      }
 +}
 +
  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;
 +      LIST_HEAD(set_update_list);
        struct nft_trans_elem *te;
  
        if (action == NFNL_ABORT_VALIDATE &&
                                kfree(nft_trans_chain_name(trans));
                                nft_trans_destroy(trans);
                        } else {
 -                              if (nft_chain_is_bound(trans->ctx.chain)) {
 +                              if (nft_trans_chain_bound(trans)) {
                                        nft_trans_destroy(trans);
                                        break;
                                }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWRULE:
 +                      if (nft_trans_rule_bound(trans)) {
 +                              nft_trans_destroy(trans);
 +                              break;
 +                      }
                        trans->ctx.chain->use--;
                        list_del_rcu(&nft_trans_rule(trans)->list);
                        nft_rule_expr_deactivate(&trans->ctx,
                case NFT_MSG_DESTROYSET:
                        trans->ctx.table->use++;
                        nft_clear(trans->ctx.net, nft_trans_set(trans));
 +                      if (nft_trans_set(trans)->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
 +                              nft_map_activate(&trans->ctx, nft_trans_set(trans));
 +
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSETELEM:
                        nft_setelem_remove(net, te->set, &te->elem);
                        if (!nft_setelem_is_catchall(te->set, &te->elem))
                                atomic_dec(&te->set->nelems);
 +
 +                      if (te->set->ops->abort &&
 +                          list_empty(&te->set->pending_update)) {
 +                              list_add_tail(&te->set->pending_update,
 +                                            &set_update_list);
 +                      }
                        break;
                case NFT_MSG_DELSETELEM:
                case NFT_MSG_DESTROYSETELEM:
                        if (!nft_setelem_is_catchall(te->set, &te->elem))
                                te->set->ndeact--;
  
 +                      if (te->set->ops->abort &&
 +                          list_empty(&te->set->pending_update)) {
 +                              list_add_tail(&te->set->pending_update,
 +                                            &set_update_list);
 +                      }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWOBJ:
                }
        }
  
 +      nft_set_abort_update(&set_update_list);
 +
        synchronize_rcu();
  
        list_for_each_entry_safe_reverse(trans, next,
                                         &nft_net->commit_list, list) {
 -              list_del(&trans->list);
 +              nft_trans_list_del(trans);
                nf_tables_abort_release(trans);
        }
  
@@@ -10496,12 -10239,22 +10528,12 @@@ static int nft_verdict_init(const struc
  static void nft_verdict_uninit(const struct nft_data *data)
  {
        struct nft_chain *chain;
 -      struct nft_rule *rule;
  
        switch (data->verdict.code) {
        case NFT_JUMP:
        case NFT_GOTO:
                chain = data->verdict.chain;
                chain->use--;
 -
 -              if (!nft_chain_is_bound(chain))
 -                      break;
 -
 -              chain->table->use--;
 -              list_for_each_entry(rule, &chain->rules, list)
 -                      chain->use--;
 -
 -              nft_chain_del(chain);
                break;
        }
  }
@@@ -10736,9 -10489,6 +10768,9 @@@ static void __nft_release_table(struct 
        list_for_each_entry_safe(set, ns, &table->sets, list) {
                list_del(&set->list);
                table->use--;
 +              if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
 +                      nft_map_deactivate(&ctx, set);
 +
                nft_set_destroy(&ctx, set);
        }
        list_for_each_entry_safe(obj, ne, &table->objects, list) {
@@@ -10823,7 -10573,6 +10855,7 @@@ static int __net_init nf_tables_init_ne
  
        INIT_LIST_HEAD(&nft_net->tables);
        INIT_LIST_HEAD(&nft_net->commit_list);
 +      INIT_LIST_HEAD(&nft_net->binding_list);
        INIT_LIST_HEAD(&nft_net->module_list);
        INIT_LIST_HEAD(&nft_net->notify_list);
        mutex_init(&nft_net->commit_mutex);
@@@ -86,7 -86,7 +86,7 @@@ static const struct nla_policy nft_bitw
        [NFTA_BITWISE_LEN]      = { .type = NLA_U32 },
        [NFTA_BITWISE_MASK]     = { .type = NLA_NESTED },
        [NFTA_BITWISE_XOR]      = { .type = NLA_NESTED },
-       [NFTA_BITWISE_OP]       = { .type = NLA_U32 },
+       [NFTA_BITWISE_OP]       = NLA_POLICY_MAX(NLA_BE32, 255),
        [NFTA_BITWISE_DATA]     = { .type = NLA_NESTED },
  };
  
@@@ -323,7 -323,7 +323,7 @@@ static bool nft_bitwise_reduce(struct n
        dreg = priv->dreg;
        regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE);
        for (i = 0; i < regcount; i++, dreg++)
 -              track->regs[priv->dreg].bitwise = expr;
 +              track->regs[dreg].bitwise = expr;
  
        return false;
  }