Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
authorDavid S. Miller <davem@davemloft.net>
Mon, 11 Jun 2018 21:24:32 +0000 (14:24 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Jun 2018 21:24:32 +0000 (14:24 -0700)
Pablo Neira Ayuso says:

====================
Netfilter/IPVS fixes for net

The following patchset contains Netfilter/IPVS fixes for your net tree:

1) Reject non-null terminated helper names from xt_CT, from Gao Feng.

2) Fix KASAN splat due to out-of-bound access from commit phase, from
   Alexey Kodanev.

3) Missing conntrack hook registration on IPVS FTP helper, from Julian
   Anastasov.

4) Incorrect skbuff allocation size in bridge nft_reject, from Taehee Yoo.

5) Fix inverted check on packet xmit to non-local addresses, also from
   Julian.

6) Fix ebtables alignment compat problems, from Alin Nastac.

7) Hook mask checks are not correct in xt_set, from Serhey Popovych.

8) Fix timeout listing of element in ipsets, from Jozsef.

9) Cap maximum timeout value in ipset, also from Jozsef.

10) Don't allow family option for hash:mac sets, from Florent Fourcot.

11) Restrict ebtables to work with NFPROTO_BRIDGE targets only, this
    Florian.

12) Another bug reported by KASAN in the rbtree set backend, from
    Taehee Yoo.

13) Missing __IPS_MAX_BIT update doesn't include IPS_OFFLOAD_BIT.
    From Gao Feng.

14) Missing initialization of match/target in ebtables, from Florian
    Westphal.

15) Remove useless nft_dup.h file in include path, from C. Labbe.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
1  2 
include/net/ip_vs.h
net/bridge/netfilter/ebtables.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/netfilter/ip6_tables.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_tables_api.c
net/netfilter/nft_set_rbtree.c

diff --combined include/net/ip_vs.h
@@@ -41,6 -41,18 +41,6 @@@ static inline struct netns_ipvs *net_ip
        return net->ipvs;
  }
  
 -/* This one needed for single_open_net since net is stored directly in
 - * private not as a struct i.e. seq_file_net can't be used.
 - */
 -static inline struct net *seq_file_single_net(struct seq_file *seq)
 -{
 -#ifdef CONFIG_NET_NS
 -      return (struct net *)seq->private;
 -#else
 -      return &init_net;
 -#endif
 -}
 -
  /* Connections' size value needed by ip_vs_ctl.c */
  extern int ip_vs_conn_tab_size;
  
@@@ -631,6 -643,7 +631,7 @@@ struct ip_vs_service 
  
        /* alternate persistence engine */
        struct ip_vs_pe __rcu   *pe;
+       int                     conntrack_afmask;
  
        struct rcu_head         rcu_head;
  };
@@@ -656,7 -669,6 +657,7 @@@ struct ip_vs_dest 
        volatile unsigned int   flags;          /* dest status flags */
        atomic_t                conn_flags;     /* flags to copy to conn */
        atomic_t                weight;         /* server weight */
 +      atomic_t                last_weight;    /* server latest weight */
  
        refcount_t              refcnt;         /* reference counter */
        struct ip_vs_stats      stats;          /* statistics */
@@@ -751,14 -763,14 +752,14 @@@ struct ip_vs_app 
         *         2=Mangled but checksum was not updated
         */
        int (*pkt_out)(struct ip_vs_app *, struct ip_vs_conn *,
 -                     struct sk_buff *, int *diff);
 +                     struct sk_buff *, int *diff, struct ip_vs_iphdr *ipvsh);
  
        /* input hook: Process packet in outin direction, diff set for TCP.
         * Return: 0=Error, 1=Payload Not Mangled/Mangled but checksum is ok,
         *         2=Mangled but checksum was not updated
         */
        int (*pkt_in)(struct ip_vs_app *, struct ip_vs_conn *,
 -                    struct sk_buff *, int *diff);
 +                    struct sk_buff *, int *diff, struct ip_vs_iphdr *ipvsh);
  
        /* ip_vs_app initializer */
        int (*init_conn)(struct ip_vs_app *, struct ip_vs_conn *);
@@@ -1316,10 -1328,8 +1317,10 @@@ int register_ip_vs_app_inc(struct netns
  int ip_vs_app_inc_get(struct ip_vs_app *inc);
  void ip_vs_app_inc_put(struct ip_vs_app *inc);
  
 -int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb);
 -int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb);
 +int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb,
 +                    struct ip_vs_iphdr *ipvsh);
 +int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb,
 +                   struct ip_vs_iphdr *ipvsh);
  
  int register_ip_vs_pe(struct ip_vs_pe *pe);
  int unregister_ip_vs_pe(struct ip_vs_pe *pe);
@@@ -1611,6 -1621,35 +1612,35 @@@ static inline bool ip_vs_conn_uses_conn
        return false;
  }
  
+ static inline int ip_vs_register_conntrack(struct ip_vs_service *svc)
+ {
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+       int afmask = (svc->af == AF_INET6) ? 2 : 1;
+       int ret = 0;
+       if (!(svc->conntrack_afmask & afmask)) {
+               ret = nf_ct_netns_get(svc->ipvs->net, svc->af);
+               if (ret >= 0)
+                       svc->conntrack_afmask |= afmask;
+       }
+       return ret;
+ #else
+       return 0;
+ #endif
+ }
+ static inline void ip_vs_unregister_conntrack(struct ip_vs_service *svc)
+ {
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+       int afmask = (svc->af == AF_INET6) ? 2 : 1;
+       if (svc->conntrack_afmask & afmask) {
+               nf_ct_netns_put(svc->ipvs->net, svc->af);
+               svc->conntrack_afmask &= ~afmask;
+       }
+ #endif
+ }
  static inline int
  ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
  {
@@@ -101,7 -101,7 +101,7 @@@ ebt_do_match(struct ebt_entry_match *m
  {
        par->match     = m->u.match;
        par->matchinfo = m->data;
 -      return m->u.match->match(skb, par) ? EBT_MATCH : EBT_NOMATCH;
 +      return !m->u.match->match(skb, par);
  }
  
  static inline int
@@@ -177,12 -177,6 +177,12 @@@ struct ebt_entry *ebt_next_entry(const 
        return (void *)entry + entry->next_offset;
  }
  
 +static inline const struct ebt_entry_target *
 +ebt_get_target_c(const struct ebt_entry *e)
 +{
 +      return ebt_get_target((struct ebt_entry *)e);
 +}
 +
  /* Do some firewalling */
  unsigned int ebt_do_table(struct sk_buff *skb,
                          const struct nf_hook_state *state,
                 */
                EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &acpar);
  
 -              t = (struct ebt_entry_target *)
 -                 (((char *)point) + point->target_offset);
 +              t = ebt_get_target_c(point);
                /* standard target */
                if (!t->u.target->target)
                        verdict = ((struct ebt_standard_target *)t)->verdict;
@@@ -348,16 -343,6 +348,16 @@@ find_table_lock(struct net *net, const 
                                "ebtable_", error, mutex);
  }
  
 +static inline void ebt_free_table_info(struct ebt_table_info *info)
 +{
 +      int i;
 +
 +      if (info->chainstack) {
 +              for_each_possible_cpu(i)
 +                      vfree(info->chainstack[i]);
 +              vfree(info->chainstack);
 +      }
 +}
  static inline int
  ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par,
                unsigned int *cnt)
@@@ -411,6 -396,12 +411,12 @@@ ebt_check_watcher(struct ebt_entry_watc
        watcher = xt_request_find_target(NFPROTO_BRIDGE, w->u.name, 0);
        if (IS_ERR(watcher))
                return PTR_ERR(watcher);
+       if (watcher->family != NFPROTO_BRIDGE) {
+               module_put(watcher->me);
+               return -ENOENT;
+       }
        w->u.watcher = watcher;
  
        par->target   = watcher;
@@@ -642,7 -633,7 +648,7 @@@ ebt_cleanup_entry(struct ebt_entry *e, 
                return 1;
        EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, NULL);
        EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, NULL);
 -      t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
 +      t = ebt_get_target(e);
  
        par.net      = net;
        par.target   = t->u.target;
@@@ -709,6 -700,8 +715,8 @@@ ebt_check_entry(struct ebt_entry *e, st
        }
        i = 0;
  
+       memset(&mtpar, 0, sizeof(mtpar));
+       memset(&tgpar, 0, sizeof(tgpar));
        mtpar.net       = tgpar.net       = net;
        mtpar.table     = tgpar.table     = name;
        mtpar.entryinfo = tgpar.entryinfo = e;
        ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, &tgpar, &j);
        if (ret != 0)
                goto cleanup_watchers;
 -      t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
 +      t = ebt_get_target(e);
        gap = e->next_offset - e->target_offset;
  
        target = xt_request_find_target(NFPROTO_BRIDGE, t->u.name, 0);
                goto cleanup_watchers;
        }
  
+       /* Reject UNSPEC, xtables verdicts/return values are incompatible */
+       if (target->family != NFPROTO_BRIDGE) {
+               module_put(target->me);
+               ret = -ENOENT;
+               goto cleanup_watchers;
+       }
        t->u.target = target;
        if (t->u.target == &ebt_standard_target) {
                if (gap < sizeof(struct ebt_standard_target)) {
@@@ -794,7 -794,8 +809,7 @@@ static int check_chainloops(const struc
                        if (pos == nentries)
                                continue;
                }
 -              t = (struct ebt_entry_target *)
 -                 (((char *)e) + e->target_offset);
 +              t = ebt_get_target_c(e);
                if (strcmp(t->u.name, EBT_STANDARD_TARGET))
                        goto letscontinue;
                if (e->target_offset + sizeof(struct ebt_standard_target) >
@@@ -989,7 -990,7 +1004,7 @@@ static void get_counters(const struct e
  static int do_replace_finish(struct net *net, struct ebt_replace *repl,
                              struct ebt_table_info *newinfo)
  {
 -      int ret, i;
 +      int ret;
        struct ebt_counter *counterstmp = NULL;
        /* used to be able to unlock earlier */
        struct ebt_table_info *table;
                          ebt_cleanup_entry, net, NULL);
  
        vfree(table->entries);
 -      if (table->chainstack) {
 -              for_each_possible_cpu(i)
 -                      vfree(table->chainstack[i]);
 -              vfree(table->chainstack);
 -      }
 +      ebt_free_table_info(table);
        vfree(table);
 -
        vfree(counterstmp);
  
  #ifdef CONFIG_AUDIT
        if (audit_enabled) {
 -              audit_log(current->audit_context, GFP_KERNEL,
 +              audit_log(audit_context(), GFP_KERNEL,
                          AUDIT_NETFILTER_CFG,
                          "table=%s family=%u entries=%u",
                          repl->name, AF_BRIDGE, repl->nentries);
@@@ -1087,7 -1093,11 +1102,7 @@@ free_iterate
  free_counterstmp:
        vfree(counterstmp);
        /* can be initialized in translate_table() */
 -      if (newinfo->chainstack) {
 -              for_each_possible_cpu(i)
 -                      vfree(newinfo->chainstack[i]);
 -              vfree(newinfo->chainstack);
 -      }
 +      ebt_free_table_info(newinfo);
        return ret;
  }
  
@@@ -1152,6 -1162,8 +1167,6 @@@ free_newinfo
  
  static void __ebt_unregister_table(struct net *net, struct ebt_table *table)
  {
 -      int i;
 -
        mutex_lock(&ebt_mutex);
        list_del(&table->list);
        mutex_unlock(&ebt_mutex);
        if (table->private->nentries)
                module_put(table->me);
        vfree(table->private->entries);
 -      if (table->private->chainstack) {
 -              for_each_possible_cpu(i)
 -                      vfree(table->private->chainstack[i]);
 -              vfree(table->private->chainstack);
 -      }
 +      ebt_free_table_info(table->private);
        vfree(table->private);
        kfree(table);
  }
@@@ -1262,7 -1278,11 +1277,7 @@@ int ebt_register_table(struct net *net
  free_unlock:
        mutex_unlock(&ebt_mutex);
  free_chainstack:
 -      if (newinfo->chainstack) {
 -              for_each_possible_cpu(i)
 -                      vfree(newinfo->chainstack[i]);
 -              vfree(newinfo->chainstack);
 -      }
 +      ebt_free_table_info(newinfo);
        vfree(newinfo->entries);
  free_newinfo:
        vfree(newinfo);
@@@ -1400,7 -1420,7 +1415,7 @@@ static inline int ebt_entry_to_user(str
                return -EFAULT;
  
        hlp = ubase + (((char *)e + e->target_offset) - base);
 -      t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
 +      t = ebt_get_target_c(e);
  
        ret = EBT_MATCH_ITERATE(e, ebt_match_to_user, base, ubase);
        if (ret != 0)
@@@ -1605,16 -1625,16 +1620,16 @@@ struct compat_ebt_entry_mwt 
                compat_uptr_t ptr;
        } u;
        compat_uint_t match_size;
-       compat_uint_t data[0];
+       compat_uint_t data[0] __attribute__ ((aligned (__alignof__(struct compat_ebt_replace))));
  };
  
  /* account for possible padding between match_size and ->data */
  static int ebt_compat_entry_padsize(void)
  {
-       BUILD_BUG_ON(XT_ALIGN(sizeof(struct ebt_entry_match)) <
-                       COMPAT_XT_ALIGN(sizeof(struct compat_ebt_entry_mwt)));
-       return (int) XT_ALIGN(sizeof(struct ebt_entry_match)) -
-                       COMPAT_XT_ALIGN(sizeof(struct compat_ebt_entry_mwt));
+       BUILD_BUG_ON(sizeof(struct ebt_entry_match) <
+                       sizeof(struct compat_ebt_entry_mwt));
+       return (int) sizeof(struct ebt_entry_match) -
+                       sizeof(struct compat_ebt_entry_mwt);
  }
  
  static int ebt_compat_match_offset(const struct xt_match *match,
@@@ -1741,7 -1761,7 +1756,7 @@@ static int compat_copy_entry_to_user(st
                return ret;
        target_offset = e->target_offset - (origsize - *size);
  
 -      t = (struct ebt_entry_target *) ((char *) e + e->target_offset);
 +      t = ebt_get_target(e);
  
        ret = compat_target_to_user(t, dstptr, size);
        if (ret)
@@@ -1789,7 -1809,7 +1804,7 @@@ static int compat_calc_entry(const stru
        EBT_MATCH_ITERATE(e, compat_calc_match, &off);
        EBT_WATCHER_ITERATE(e, compat_calc_watcher, &off);
  
 -      t = (const struct ebt_entry_target *) ((char *) e + e->target_offset);
 +      t = ebt_get_target_c(e);
  
        off += xt_compat_target_offset(t->u.target);
        off += ebt_compat_entry_padsize();
@@@ -301,7 -301,7 +301,7 @@@ ipt_do_table(struct sk_buff *skb
                counter = xt_get_this_cpu_counter(&e->counters);
                ADD_COUNTER(*counter, skb->len, 1);
  
 -              t = ipt_get_target(e);
 +              t = ipt_get_target_c(e);
                WARN_ON(!t->u.kernel.target);
  
  #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
@@@ -531,6 -531,7 +531,7 @@@ find_check_entry(struct ipt_entry *e, s
                return -ENOMEM;
  
        j = 0;
+       memset(&mtpar, 0, sizeof(mtpar));
        mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ip;
@@@ -1783,8 -1784,6 +1784,8 @@@ int ipt_register_table(struct net *net
  
        /* set res now, will see skbs right after nf_register_net_hooks */
        WRITE_ONCE(*res, new_table);
 +      if (!ops)
 +              return 0;
  
        ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
        if (ret != 0) {
@@@ -1802,8 -1801,7 +1803,8 @@@ out_free
  void ipt_unregister_table(struct net *net, struct xt_table *table,
                          const struct nf_hook_ops *ops)
  {
 -      nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
 +      if (ops)
 +              nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
        __ipt_unregister_table(net, table);
  }
  
@@@ -529,6 -529,7 +529,6 @@@ static int check_target(struct ip6t_ent
                .family    = NFPROTO_IPV6,
        };
  
 -      t = ip6t_get_target(e);
        return xt_check_target(&par, t->u.target_size - sizeof(*t),
                               e->ipv6.proto,
                               e->ipv6.invflags & IP6T_INV_PROTO);
@@@ -550,6 -551,7 +550,7 @@@ find_check_entry(struct ip6t_entry *e, 
                return -ENOMEM;
  
        j = 0;
+       memset(&mtpar, 0, sizeof(mtpar));
        mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ipv6;
@@@ -1793,8 -1795,6 +1794,8 @@@ int ip6t_register_table(struct net *net
  
        /* set res now, will see skbs right after nf_register_net_hooks */
        WRITE_ONCE(*res, new_table);
 +      if (!ops)
 +              return 0;
  
        ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
        if (ret != 0) {
@@@ -1812,8 -1812,7 +1813,8 @@@ out_free
  void ip6t_unregister_table(struct net *net, struct xt_table *table,
                           const struct nf_hook_ops *ops)
  {
 -      nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
 +      if (ops)
 +              nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
        __ip6t_unregister_table(net, table);
  }
  
@@@ -821,10 -821,6 +821,10 @@@ __ip_vs_update_dest(struct ip_vs_servic
        if (add && udest->af != svc->af)
                ipvs->mixed_address_family_dests++;
  
 +      /* keep the last_weight with latest non-0 weight */
 +      if (add || udest->weight != 0)
 +              atomic_set(&dest->last_weight, udest->weight);
 +
        /* set the weight and the flags */
        atomic_set(&dest->weight, udest->weight);
        conn_flags = udest->conn_flags & IP_VS_CONN_F_DEST_MASK;
                 *    For now only for NAT!
                 */
                ip_vs_rs_hash(ipvs, dest);
+               /* FTP-NAT requires conntrack for mangling */
+               if (svc->port == FTPPORT)
+                       ip_vs_register_conntrack(svc);
        }
        atomic_set(&dest->conn_flags, conn_flags);
  
@@@ -1462,6 -1461,7 +1465,7 @@@ static void __ip_vs_del_service(struct 
   */
  static void ip_vs_unlink_service(struct ip_vs_service *svc, bool cleanup)
  {
+       ip_vs_unregister_conntrack(svc);
        /* Hold svc to avoid double release from dest_trash */
        atomic_inc(&svc->refcnt);
        /*
@@@ -2113,6 -2113,19 +2117,6 @@@ static const struct seq_operations ip_v
        .show  = ip_vs_info_seq_show,
  };
  
 -static int ip_vs_info_open(struct inode *inode, struct file *file)
 -{
 -      return seq_open_net(inode, file, &ip_vs_info_seq_ops,
 -                      sizeof(struct ip_vs_iter));
 -}
 -
 -static const struct file_operations ip_vs_info_fops = {
 -      .open    = ip_vs_info_open,
 -      .read    = seq_read,
 -      .llseek  = seq_lseek,
 -      .release = seq_release_net,
 -};
 -
  static int ip_vs_stats_show(struct seq_file *seq, void *v)
  {
        struct net *net = seq_file_single_net(seq);
        return 0;
  }
  
 -static int ip_vs_stats_seq_open(struct inode *inode, struct file *file)
 -{
 -      return single_open_net(inode, file, ip_vs_stats_show);
 -}
 -
 -static const struct file_operations ip_vs_stats_fops = {
 -      .open = ip_vs_stats_seq_open,
 -      .read = seq_read,
 -      .llseek = seq_lseek,
 -      .release = single_release_net,
 -};
 -
  static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v)
  {
        struct net *net = seq_file_single_net(seq);
  
        return 0;
  }
 -
 -static int ip_vs_stats_percpu_seq_open(struct inode *inode, struct file *file)
 -{
 -      return single_open_net(inode, file, ip_vs_stats_percpu_show);
 -}
 -
 -static const struct file_operations ip_vs_stats_percpu_fops = {
 -      .open = ip_vs_stats_percpu_seq_open,
 -      .read = seq_read,
 -      .llseek = seq_lseek,
 -      .release = single_release_net,
 -};
  #endif
  
  /*
@@@ -4006,12 -4043,10 +4010,12 @@@ int __net_init ip_vs_control_net_init(s
  
        spin_lock_init(&ipvs->tot_stats.lock);
  
 -      proc_create("ip_vs", 0, ipvs->net->proc_net, &ip_vs_info_fops);
 -      proc_create("ip_vs_stats", 0, ipvs->net->proc_net, &ip_vs_stats_fops);
 -      proc_create("ip_vs_stats_percpu", 0, ipvs->net->proc_net,
 -                  &ip_vs_stats_percpu_fops);
 +      proc_create_net("ip_vs", 0, ipvs->net->proc_net, &ip_vs_info_seq_ops,
 +                      sizeof(struct ip_vs_iter));
 +      proc_create_net_single("ip_vs_stats", 0, ipvs->net->proc_net,
 +                      ip_vs_stats_show, NULL);
 +      proc_create_net_single("ip_vs_stats_percpu", 0, ipvs->net->proc_net,
 +                      ip_vs_stats_percpu_show, NULL);
  
        if (ip_vs_control_net_init_sysctl(ipvs))
                goto err;
@@@ -168,7 -168,7 +168,7 @@@ static inline bool crosses_local_route_
                                                bool new_rt_is_local)
  {
        bool rt_mode_allow_local = !!(rt_mode & IP_VS_RT_MODE_LOCAL);
-       bool rt_mode_allow_non_local = !!(rt_mode & IP_VS_RT_MODE_LOCAL);
+       bool rt_mode_allow_non_local = !!(rt_mode & IP_VS_RT_MODE_NON_LOCAL);
        bool rt_mode_allow_redirect = !!(rt_mode & IP_VS_RT_MODE_RDR);
        bool source_is_loopback;
        bool old_rt_is_local;
@@@ -266,13 -266,12 +266,13 @@@ static inline bool decrement_ttl(struc
  
                /* check and decrement ttl */
                if (ipv6_hdr(skb)->hop_limit <= 1) {
 +                      struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
 +
                        /* Force OUTPUT device used as source address */
                        skb->dev = dst->dev;
                        icmpv6_send(skb, ICMPV6_TIME_EXCEED,
                                    ICMPV6_EXC_HOPLIMIT, 0);
 -                      __IP6_INC_STATS(net, ip6_dst_idev(dst),
 -                                      IPSTATS_MIB_INHDRERRORS);
 +                      __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
  
                        return false;
                }
@@@ -28,42 -28,6 +28,42 @@@ static LIST_HEAD(nf_tables_objects)
  static LIST_HEAD(nf_tables_flowtables);
  static u64 table_handle;
  
 +enum {
 +      NFT_VALIDATE_SKIP       = 0,
 +      NFT_VALIDATE_NEED,
 +      NFT_VALIDATE_DO,
 +};
 +
 +static u32 nft_chain_hash(const void *data, u32 len, u32 seed);
 +static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed);
 +static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *);
 +
 +static const struct rhashtable_params nft_chain_ht_params = {
 +      .head_offset            = offsetof(struct nft_chain, rhlhead),
 +      .key_offset             = offsetof(struct nft_chain, name),
 +      .hashfn                 = nft_chain_hash,
 +      .obj_hashfn             = nft_chain_hash_obj,
 +      .obj_cmpfn              = nft_chain_hash_cmp,
 +      .locks_mul              = 1,
 +      .automatic_shrinking    = true,
 +};
 +
 +static void nft_validate_state_update(struct net *net, u8 new_validate_state)
 +{
 +      switch (net->nft.validate_state) {
 +      case NFT_VALIDATE_SKIP:
 +              WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO);
 +              break;
 +      case NFT_VALIDATE_NEED:
 +              break;
 +      case NFT_VALIDATE_DO:
 +              if (new_validate_state == NFT_VALIDATE_NEED)
 +                      return;
 +      }
 +
 +      net->nft.validate_state = new_validate_state;
 +}
 +
  static void nft_ctx_init(struct nft_ctx *ctx,
                         struct net *net,
                         const struct sk_buff *skb,
@@@ -110,43 -74,88 +110,43 @@@ static void nft_trans_destroy(struct nf
        kfree(trans);
  }
  
 -/* removal requests are queued in the commit_list, but not acted upon
 - * until after all new rules are in place.
 - *
 - * Therefore, nf_register_net_hook(net, &nat_hook) runs before pending
 - * nf_unregister_net_hook().
 - *
 - * nf_register_net_hook thus fails if a nat hook is already in place
 - * even if the conflicting hook is about to be removed.
 - *
 - * If collision is detected, search commit_log for DELCHAIN matching
 - * the new nat hooknum; if we find one collision is temporary:
 - *
 - * Either transaction is aborted (new/colliding hook is removed), or
 - * transaction is committed (old hook is removed).
 - */
 -static bool nf_tables_allow_nat_conflict(const struct net *net,
 -                                       const struct nf_hook_ops *ops)
 -{
 -      const struct nft_trans *trans;
 -      bool ret = false;
 -
 -      if (!ops->nat_hook)
 -              return false;
 -
 -      list_for_each_entry(trans, &net->nft.commit_list, list) {
 -              const struct nf_hook_ops *pending_ops;
 -              const struct nft_chain *pending;
 -
 -              if (trans->msg_type != NFT_MSG_NEWCHAIN &&
 -                  trans->msg_type != NFT_MSG_DELCHAIN)
 -                      continue;
 -
 -              pending = trans->ctx.chain;
 -              if (!nft_is_base_chain(pending))
 -                      continue;
 -
 -              pending_ops = &nft_base_chain(pending)->ops;
 -              if (pending_ops->nat_hook &&
 -                  pending_ops->pf == ops->pf &&
 -                  pending_ops->hooknum == ops->hooknum) {
 -                      /* other hook registration already pending? */
 -                      if (trans->msg_type == NFT_MSG_NEWCHAIN)
 -                              return false;
 -
 -                      ret = true;
 -              }
 -      }
 -
 -      return ret;
 -}
 -
  static int nf_tables_register_hook(struct net *net,
                                   const struct nft_table *table,
                                   struct nft_chain *chain)
  {
 -      struct nf_hook_ops *ops;
 -      int ret;
 +      const struct nft_base_chain *basechain;
 +      const struct nf_hook_ops *ops;
  
        if (table->flags & NFT_TABLE_F_DORMANT ||
            !nft_is_base_chain(chain))
                return 0;
  
 -      ops = &nft_base_chain(chain)->ops;
 -      ret = nf_register_net_hook(net, ops);
 -      if (ret == -EBUSY && nf_tables_allow_nat_conflict(net, ops)) {
 -              ops->nat_hook = false;
 -              ret = nf_register_net_hook(net, ops);
 -              ops->nat_hook = true;
 -      }
 +      basechain = nft_base_chain(chain);
 +      ops = &basechain->ops;
  
 -      return ret;
 +      if (basechain->type->ops_register)
 +              return basechain->type->ops_register(net, ops);
 +
 +      return nf_register_net_hook(net, ops);
  }
  
  static void nf_tables_unregister_hook(struct net *net,
                                      const struct nft_table *table,
                                      struct nft_chain *chain)
  {
 +      const struct nft_base_chain *basechain;
 +      const struct nf_hook_ops *ops;
 +
        if (table->flags & NFT_TABLE_F_DORMANT ||
            !nft_is_base_chain(chain))
                return;
 +      basechain = nft_base_chain(chain);
 +      ops = &basechain->ops;
 +
 +      if (basechain->type->ops_unregister)
 +              return basechain->type->ops_unregister(net, ops);
  
 -      nf_unregister_net_hook(net, &nft_base_chain(chain)->ops);
 +      nf_unregister_net_hook(net, ops);
  }
  
  static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
@@@ -406,17 -415,13 +406,17 @@@ static struct nft_table *nft_table_look
  {
        struct nft_table *table;
  
 -      list_for_each_entry(table, &net->nft.tables, list) {
 +      if (nla == NULL)
 +              return ERR_PTR(-EINVAL);
 +
 +      list_for_each_entry_rcu(table, &net->nft.tables, list) {
                if (!nla_strcmp(nla, table->name) &&
                    table->family == family &&
                    nft_active_genmask(table, genmask))
                        return table;
        }
 -      return NULL;
 +
 +      return ERR_PTR(-ENOENT);
  }
  
  static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
                    nft_active_genmask(table, genmask))
                        return table;
        }
 -      return NULL;
 -}
 -
 -static struct nft_table *nf_tables_table_lookup(const struct net *net,
 -                                              const struct nlattr *nla,
 -                                              u8 family, u8 genmask)
 -{
 -      struct nft_table *table;
 -
 -      if (nla == NULL)
 -              return ERR_PTR(-EINVAL);
 -
 -      table = nft_table_lookup(net, nla, family, genmask);
 -      if (table != NULL)
 -              return table;
 -
 -      return ERR_PTR(-ENOENT);
 -}
 -
 -static struct nft_table *nf_tables_table_lookup_byhandle(const struct net *net,
 -                                                       const struct nlattr *nla,
 -                                                       u8 genmask)
 -{
 -      struct nft_table *table;
 -
 -      if (nla == NULL)
 -              return ERR_PTR(-EINVAL);
 -
 -      table = nft_table_lookup_byhandle(net, nla, genmask);
 -      if (table != NULL)
 -              return table;
  
        return ERR_PTR(-ENOENT);
  }
@@@ -582,24 -618,6 +582,24 @@@ done
        return skb->len;
  }
  
 +static int nft_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
 +                                    const struct nlmsghdr *nlh,
 +                                    struct netlink_dump_control *c)
 +{
 +      int err;
 +
 +      if (!try_module_get(THIS_MODULE))
 +              return -EINVAL;
 +
 +      rcu_read_unlock();
 +      err = netlink_dump_start(nlsk, skb, nlh, c);
 +      rcu_read_lock();
 +      module_put(THIS_MODULE);
 +
 +      return err;
 +}
 +
 +/* called with rcu_read_lock held */
  static int nf_tables_gettable(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[],
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_tables,
 +                      .module = THIS_MODULE,
                };
 -              return netlink_dump_start(nlsk, skb, nlh, &c);
 +
 +              return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
        }
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_TABLE_NAME], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_TABLE_NAME], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_TABLE_NAME]);
                return PTR_ERR(table);
 +      }
  
 -      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (!skb2)
                return -ENOMEM;
  
@@@ -734,29 -749,6 +734,29 @@@ err
        return ret;
  }
  
 +static u32 nft_chain_hash(const void *data, u32 len, u32 seed)
 +{
 +      const char *name = data;
 +
 +      return jhash(name, strlen(name), seed);
 +}
 +
 +static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed)
 +{
 +      const struct nft_chain *chain = data;
 +
 +      return nft_chain_hash(chain->name, 0, seed);
 +}
 +
 +static int nft_chain_hash_cmp(struct rhashtable_compare_arg *arg,
 +                            const void *ptr)
 +{
 +      const struct nft_chain *chain = ptr;
 +      const char *name = arg->key;
 +
 +      return strcmp(chain->name, name);
 +}
 +
  static int nf_tables_newtable(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[],
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
 -      const struct nlattr *name;
 -      struct nft_table *table;
        int family = nfmsg->nfgen_family;
 +      const struct nlattr *attr;
 +      struct nft_table *table;
        u32 flags = 0;
        struct nft_ctx ctx;
        int err;
  
 -      name = nla[NFTA_TABLE_NAME];
 -      table = nf_tables_table_lookup(net, name, family, genmask);
 +      attr = nla[NFTA_TABLE_NAME];
 +      table = nft_table_lookup(net, attr, family, genmask);
        if (IS_ERR(table)) {
                if (PTR_ERR(table) != -ENOENT)
                        return PTR_ERR(table);
        } else {
 -              if (nlh->nlmsg_flags & NLM_F_EXCL)
 +              if (nlh->nlmsg_flags & NLM_F_EXCL) {
 +                      NL_SET_BAD_ATTR(extack, attr);
                        return -EEXIST;
 +              }
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
  
        if (table == NULL)
                goto err_kzalloc;
  
 -      table->name = nla_strdup(name, GFP_KERNEL);
 +      table->name = nla_strdup(attr, GFP_KERNEL);
        if (table->name == NULL)
                goto err_strdup;
  
 +      err = rhltable_init(&table->chains_ht, &nft_chain_ht_params);
 +      if (err)
 +              goto err_chain_ht;
 +
        INIT_LIST_HEAD(&table->chains);
        INIT_LIST_HEAD(&table->sets);
        INIT_LIST_HEAD(&table->objects);
        list_add_tail_rcu(&table->list, &net->nft.tables);
        return 0;
  err_trans:
 +      rhltable_destroy(&table->chains_ht);
 +err_chain_ht:
        kfree(table->name);
  err_strdup:
        kfree(table);
@@@ -928,9 -912,8 +928,9 @@@ static int nf_tables_deltable(struct ne
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
 -      struct nft_table *table;
        int family = nfmsg->nfgen_family;
 +      const struct nlattr *attr;
 +      struct nft_table *table;
        struct nft_ctx ctx;
  
        nft_ctx_init(&ctx, net, skb, nlh, 0, NULL, NULL, nla);
            (!nla[NFTA_TABLE_NAME] && !nla[NFTA_TABLE_HANDLE]))
                return nft_flush(&ctx, family);
  
 -      if (nla[NFTA_TABLE_HANDLE])
 -              table = nf_tables_table_lookup_byhandle(net,
 -                                                      nla[NFTA_TABLE_HANDLE],
 -                                                      genmask);
 -      else
 -              table = nf_tables_table_lookup(net, nla[NFTA_TABLE_NAME],
 -                                             family, genmask);
 +      if (nla[NFTA_TABLE_HANDLE]) {
 +              attr = nla[NFTA_TABLE_HANDLE];
 +              table = nft_table_lookup_byhandle(net, attr, genmask);
 +      } else {
 +              attr = nla[NFTA_TABLE_NAME];
 +              table = nft_table_lookup(net, attr, family, genmask);
 +      }
  
 -      if (IS_ERR(table))
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, attr);
                return PTR_ERR(table);
 +      }
  
        if (nlh->nlmsg_flags & NLM_F_NONREC &&
            table->use > 0)
@@@ -965,7 -946,6 +965,7 @@@ static void nf_tables_table_destroy(str
  {
        BUG_ON(ctx->table->use > 0);
  
 +      rhltable_destroy(&ctx->table->chains_ht);
        kfree(ctx->table->name);
        kfree(ctx->table);
  }
@@@ -998,7 -978,8 +998,7 @@@ EXPORT_SYMBOL_GPL(nft_unregister_chain_
   */
  
  static struct nft_chain *
 -nf_tables_chain_lookup_byhandle(const struct nft_table *table, u64 handle,
 -                              u8 genmask)
 +nft_chain_lookup_byhandle(const struct nft_table *table, u64 handle, u8 genmask)
  {
        struct nft_chain *chain;
  
        return ERR_PTR(-ENOENT);
  }
  
 -static struct nft_chain *nf_tables_chain_lookup(const struct nft_table *table,
 -                                              const struct nlattr *nla,
 -                                              u8 genmask)
 +static struct nft_chain *nft_chain_lookup(struct nft_table *table,
 +                                        const struct nlattr *nla, u8 genmask)
  {
 +      char search[NFT_CHAIN_MAXNAMELEN + 1];
 +      struct rhlist_head *tmp, *list;
        struct nft_chain *chain;
  
        if (nla == NULL)
                return ERR_PTR(-EINVAL);
  
 -      list_for_each_entry(chain, &table->chains, list) {
 -              if (!nla_strcmp(nla, chain->name) &&
 -                  nft_active_genmask(chain, genmask))
 -                      return chain;
 -      }
 +      nla_strlcpy(search, nla, sizeof(search));
  
 -      return ERR_PTR(-ENOENT);
 +      WARN_ON(!rcu_read_lock_held() &&
 +              !lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
 +
 +      chain = ERR_PTR(-ENOENT);
 +      rcu_read_lock();
 +      list = rhltable_lookup(&table->chains_ht, search, nft_chain_ht_params);
 +      if (!list)
 +              goto out_unlock;
 +
 +      rhl_for_each_entry_rcu(chain, tmp, list, rhlhead) {
 +              if (nft_active_genmask(chain, genmask))
 +                      goto out_unlock;
 +      }
 +      chain = ERR_PTR(-ENOENT);
 +out_unlock:
 +      rcu_read_unlock();
 +      return chain;
  }
  
  static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
@@@ -1235,7 -1203,6 +1235,7 @@@ done
        return skb->len;
  }
  
 +/* called with rcu_read_lock held */
  static int nf_tables_getchain(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[],
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_cur(net);
 -      const struct nft_table *table;
        const struct nft_chain *chain;
 +      struct nft_table *table;
        struct sk_buff *skb2;
        int family = nfmsg->nfgen_family;
        int err;
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_chains,
 +                      .module = THIS_MODULE,
                };
 -              return netlink_dump_start(nlsk, skb, nlh, &c);
 +
 +              return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
        }
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_CHAIN_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TABLE]);
                return PTR_ERR(table);
 +      }
  
 -      chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask);
 -      if (IS_ERR(chain))
 +      chain = nft_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask);
 +      if (IS_ERR(chain)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_NAME]);
                return PTR_ERR(chain);
 +      }
  
 -      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (!skb2)
                return -ENOMEM;
  
@@@ -1342,32 -1304,17 +1342,32 @@@ static void nft_chain_stats_replace(str
        }
  }
  
 +static void nf_tables_chain_free_chain_rules(struct nft_chain *chain)
 +{
 +      struct nft_rule **g0 = rcu_dereference_raw(chain->rules_gen_0);
 +      struct nft_rule **g1 = rcu_dereference_raw(chain->rules_gen_1);
 +
 +      if (g0 != g1)
 +              kvfree(g1);
 +      kvfree(g0);
 +
 +      /* should be NULL either via abort or via successful commit */
 +      WARN_ON_ONCE(chain->rules_next);
 +      kvfree(chain->rules_next);
 +}
 +
  static void nf_tables_chain_destroy(struct nft_ctx *ctx)
  {
        struct nft_chain *chain = ctx->chain;
  
        BUG_ON(chain->use > 0);
  
 +      /* no concurrent access possible anymore */
 +      nf_tables_chain_free_chain_rules(chain);
 +
        if (nft_is_base_chain(chain)) {
                struct nft_base_chain *basechain = nft_base_chain(chain);
  
 -              if (basechain->type->free)
 -                      basechain->type->free(ctx);
                module_put(basechain->type->owner);
                free_percpu(basechain->stats);
                if (basechain->stats)
@@@ -1457,27 -1404,6 +1457,27 @@@ static void nft_chain_release_hook(stru
        module_put(hook->type->owner);
  }
  
 +struct nft_rules_old {
 +      struct rcu_head h;
 +      struct nft_rule **start;
 +};
 +
 +static struct nft_rule **nf_tables_chain_alloc_rules(const struct nft_chain *chain,
 +                                                   unsigned int alloc)
 +{
 +      if (alloc > INT_MAX)
 +              return NULL;
 +
 +      alloc += 1;     /* NULL, ends rules */
 +      if (sizeof(struct nft_rule *) > INT_MAX / alloc)
 +              return NULL;
 +
 +      alloc *= sizeof(struct nft_rule *);
 +      alloc += sizeof(struct nft_rules_old);
 +
 +      return kvmalloc(alloc, GFP_KERNEL);
 +}
 +
  static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
                              u8 policy, bool create)
  {
        struct nft_stats __percpu *stats;
        struct net *net = ctx->net;
        struct nft_chain *chain;
 +      struct nft_rule **rules;
        int err;
  
        if (table->use == UINT_MAX)
                }
  
                basechain->type = hook.type;
 -              if (basechain->type->init)
 -                      basechain->type->init(ctx);
 -
                chain = &basechain->chain;
  
                ops             = &basechain->ops;
                ops->hook       = hook.type->hooks[ops->hooknum];
                ops->dev        = hook.dev;
  
 -              if (basechain->type->type == NFT_CHAIN_T_NAT)
 -                      ops->nat_hook = true;
 -
                chain->flags |= NFT_BASE_CHAIN;
                basechain->policy = policy;
        } else {
                goto err1;
        }
  
 +      rules = nf_tables_chain_alloc_rules(chain, 0);
 +      if (!rules) {
 +              err = -ENOMEM;
 +              goto err1;
 +      }
 +
 +      *rules = NULL;
 +      rcu_assign_pointer(chain->rules_gen_0, rules);
 +      rcu_assign_pointer(chain->rules_gen_1, rules);
 +
        err = nf_tables_register_hook(net, table, chain);
        if (err < 0)
                goto err1;
  
 +      err = rhltable_insert_key(&table->chains_ht, chain->name,
 +                                &chain->rhlhead, nft_chain_ht_params);
 +      if (err)
 +              goto err2;
 +
        err = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
 -      if (err < 0)
 +      if (err < 0) {
 +              rhltable_remove(&table->chains_ht, &chain->rhlhead,
 +                              nft_chain_ht_params);
                goto err2;
 +      }
  
        table->use++;
        list_add_tail_rcu(&chain->list, &table->chains);
@@@ -1631,7 -1544,8 +1631,7 @@@ static int nf_tables_updchain(struct nf
            nla[NFTA_CHAIN_NAME]) {
                struct nft_chain *chain2;
  
 -              chain2 = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME],
 -                                              genmask);
 +              chain2 = nft_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask);
                if (!IS_ERR(chain2))
                        return -EEXIST;
        }
@@@ -1681,9 -1595,9 +1681,9 @@@ static int nf_tables_newchain(struct ne
                              struct netlink_ext_ack *extack)
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
 -      const struct nlattr * uninitialized_var(name);
        u8 genmask = nft_genmask_next(net);
        int family = nfmsg->nfgen_family;
 +      const struct nlattr *attr;
        struct nft_table *table;
        struct nft_chain *chain;
        u8 policy = NF_ACCEPT;
  
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_CHAIN_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TABLE]);
                return PTR_ERR(table);
 +      }
  
        chain = NULL;
 -      name = nla[NFTA_CHAIN_NAME];
 +      attr = nla[NFTA_CHAIN_NAME];
  
        if (nla[NFTA_CHAIN_HANDLE]) {
                handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE]));
 -              chain = nf_tables_chain_lookup_byhandle(table, handle, genmask);
 -              if (IS_ERR(chain))
 +              chain = nft_chain_lookup_byhandle(table, handle, genmask);
 +              if (IS_ERR(chain)) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_HANDLE]);
                        return PTR_ERR(chain);
 +              }
 +              attr = nla[NFTA_CHAIN_HANDLE];
        } else {
 -              chain = nf_tables_chain_lookup(table, name, genmask);
 +              chain = nft_chain_lookup(table, attr, genmask);
                if (IS_ERR(chain)) {
 -                      if (PTR_ERR(chain) != -ENOENT)
 +                      if (PTR_ERR(chain) != -ENOENT) {
 +                              NL_SET_BAD_ATTR(extack, attr);
                                return PTR_ERR(chain);
 +                      }
                        chain = NULL;
                }
        }
  
        if (nla[NFTA_CHAIN_POLICY]) {
                if (chain != NULL &&
 -                  !nft_is_base_chain(chain))
 +                  !nft_is_base_chain(chain)) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_POLICY]);
                        return -EOPNOTSUPP;
 +              }
  
                if (chain == NULL &&
 -                  nla[NFTA_CHAIN_HOOK] == NULL)
 +                  nla[NFTA_CHAIN_HOOK] == NULL) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_POLICY]);
                        return -EOPNOTSUPP;
 +              }
  
                policy = ntohl(nla_get_be32(nla[NFTA_CHAIN_POLICY]));
                switch (policy) {
        nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
  
        if (chain != NULL) {
 -              if (nlh->nlmsg_flags & NLM_F_EXCL)
 +              if (nlh->nlmsg_flags & NLM_F_EXCL) {
 +                      NL_SET_BAD_ATTR(extack, attr);
                        return -EEXIST;
 +              }
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
  
@@@ -1767,34 -1669,28 +1767,34 @@@ static int nf_tables_delchain(struct ne
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
 +      int family = nfmsg->nfgen_family;
 +      const struct nlattr *attr;
        struct nft_table *table;
        struct nft_chain *chain;
        struct nft_rule *rule;
 -      int family = nfmsg->nfgen_family;
        struct nft_ctx ctx;
        u64 handle;
        u32 use;
        int err;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_CHAIN_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TABLE]);
                return PTR_ERR(table);
 +      }
  
        if (nla[NFTA_CHAIN_HANDLE]) {
 -              handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE]));
 -              chain = nf_tables_chain_lookup_byhandle(table, handle, genmask);
 +              attr = nla[NFTA_CHAIN_HANDLE];
 +              handle = be64_to_cpu(nla_get_be64(attr));
 +              chain = nft_chain_lookup_byhandle(table, handle, genmask);
        } else {
 -              chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask);
 +              attr = nla[NFTA_CHAIN_NAME];
 +              chain = nft_chain_lookup(table, attr, genmask);
        }
 -      if (IS_ERR(chain))
 +      if (IS_ERR(chain)) {
 +              NL_SET_BAD_ATTR(extack, attr);
                return PTR_ERR(chain);
 +      }
  
        if (nlh->nlmsg_flags & NLM_F_NONREC &&
            chain->use > 0)
        /* There are rules and elements that are still holding references to us,
         * we cannot do a recursive removal in this case.
         */
 -      if (use > 0)
 +      if (use > 0) {
 +              NL_SET_BAD_ATTR(extack, attr);
                return -EBUSY;
 +      }
  
        return nft_delchain(&ctx);
  }
@@@ -2011,7 -1905,19 +2011,7 @@@ static int nf_tables_newexpr(const stru
                        goto err1;
        }
  
 -      if (ops->validate) {
 -              const struct nft_data *data = NULL;
 -
 -              err = ops->validate(ctx, expr, &data);
 -              if (err < 0)
 -                      goto err2;
 -      }
 -
        return 0;
 -
 -err2:
 -      if (ops->destroy)
 -              ops->destroy(ctx, expr);
  err1:
        expr->ops = NULL;
        return err;
@@@ -2064,13 -1970,13 +2064,13 @@@ void nft_expr_destroy(const struct nft_
   * Rules
   */
  
 -static struct nft_rule *__nf_tables_rule_lookup(const struct nft_chain *chain,
 -                                              u64 handle)
 +static struct nft_rule *__nft_rule_lookup(const struct nft_chain *chain,
 +                                        u64 handle)
  {
        struct nft_rule *rule;
  
        // FIXME: this sucks
 -      list_for_each_entry(rule, &chain->rules, list) {
 +      list_for_each_entry_rcu(rule, &chain->rules, list) {
                if (handle == rule->handle)
                        return rule;
        }
        return ERR_PTR(-ENOENT);
  }
  
 -static struct nft_rule *nf_tables_rule_lookup(const struct nft_chain *chain,
 -                                            const struct nlattr *nla)
 +static struct nft_rule *nft_rule_lookup(const struct nft_chain *chain,
 +                                      const struct nlattr *nla)
  {
        if (nla == NULL)
                return ERR_PTR(-EINVAL);
  
 -      return __nf_tables_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla)));
 +      return __nft_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla)));
  }
  
  static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
@@@ -2266,7 -2172,6 +2266,7 @@@ static int nf_tables_dump_rules_done(st
        return 0;
  }
  
 +/* called with rcu_read_lock held */
  static int nf_tables_getrule(struct net *net, struct sock *nlsk,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
                             const struct nlattr * const nla[],
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_cur(net);
 -      const struct nft_table *table;
        const struct nft_chain *chain;
        const struct nft_rule *rule;
 +      struct nft_table *table;
        struct sk_buff *skb2;
        int family = nfmsg->nfgen_family;
        int err;
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_rules,
                        .done = nf_tables_dump_rules_done,
 +                      .module = THIS_MODULE,
                };
  
                if (nla[NFTA_RULE_TABLE] || nla[NFTA_RULE_CHAIN]) {
                        struct nft_rule_dump_ctx *ctx;
  
 -                      ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 +                      ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
                        if (!ctx)
                                return -ENOMEM;
  
                        if (nla[NFTA_RULE_TABLE]) {
                                ctx->table = nla_strdup(nla[NFTA_RULE_TABLE],
 -                                                      GFP_KERNEL);
 +                                                      GFP_ATOMIC);
                                if (!ctx->table) {
                                        kfree(ctx);
                                        return -ENOMEM;
                        }
                        if (nla[NFTA_RULE_CHAIN]) {
                                ctx->chain = nla_strdup(nla[NFTA_RULE_CHAIN],
 -                                                      GFP_KERNEL);
 +                                                      GFP_ATOMIC);
                                if (!ctx->chain) {
                                        kfree(ctx->table);
                                        kfree(ctx);
                        c.data = ctx;
                }
  
 -              return netlink_dump_start(nlsk, skb, nlh, &c);
 +              return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
        }
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_RULE_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_TABLE]);
                return PTR_ERR(table);
 +      }
  
 -      chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
 -      if (IS_ERR(chain))
 +      chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
 +      if (IS_ERR(chain)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
                return PTR_ERR(chain);
 +      }
  
 -      rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
 -      if (IS_ERR(rule))
 +      rule = nft_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
 +      if (IS_ERR(rule)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_HANDLE]);
                return PTR_ERR(rule);
 +      }
  
 -      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (!skb2)
                return -ENOMEM;
  
@@@ -2377,53 -2276,6 +2377,53 @@@ static void nf_tables_rule_release(cons
        nf_tables_rule_destroy(ctx, rule);
  }
  
 +int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain)
 +{
 +      struct nft_expr *expr, *last;
 +      const struct nft_data *data;
 +      struct nft_rule *rule;
 +      int err;
 +
 +      list_for_each_entry(rule, &chain->rules, list) {
 +              if (!nft_is_active_next(ctx->net, rule))
 +                      continue;
 +
 +              nft_rule_for_each_expr(expr, last, rule) {
 +                      if (!expr->ops->validate)
 +                              continue;
 +
 +                      err = expr->ops->validate(ctx, expr, &data);
 +                      if (err < 0)
 +                              return err;
 +              }
 +      }
 +
 +      return 0;
 +}
 +EXPORT_SYMBOL_GPL(nft_chain_validate);
 +
 +static int nft_table_validate(struct net *net, const struct nft_table *table)
 +{
 +      struct nft_chain *chain;
 +      struct nft_ctx ctx = {
 +              .net    = net,
 +              .family = table->family,
 +      };
 +      int err;
 +
 +      list_for_each_entry(chain, &table->chains, list) {
 +              if (!nft_is_base_chain(chain))
 +                      continue;
 +
 +              ctx.chain = chain;
 +              err = nft_chain_validate(&ctx, chain);
 +              if (err < 0)
 +                      return err;
 +      }
 +
 +      return 0;
 +}
 +
  #define NFT_RULE_MAXEXPRS     128
  
  static struct nft_expr_info *info;
@@@ -2451,30 -2303,23 +2451,30 @@@ static int nf_tables_newrule(struct ne
  
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_RULE_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_TABLE]);
                return PTR_ERR(table);
 +      }
  
 -      chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
 -      if (IS_ERR(chain))
 +      chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
 +      if (IS_ERR(chain)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
                return PTR_ERR(chain);
 +      }
  
        if (nla[NFTA_RULE_HANDLE]) {
                handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE]));
 -              rule = __nf_tables_rule_lookup(chain, handle);
 -              if (IS_ERR(rule))
 +              rule = __nft_rule_lookup(chain, handle);
 +              if (IS_ERR(rule)) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_HANDLE]);
                        return PTR_ERR(rule);
 +              }
  
 -              if (nlh->nlmsg_flags & NLM_F_EXCL)
 +              if (nlh->nlmsg_flags & NLM_F_EXCL) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_HANDLE]);
                        return -EEXIST;
 +              }
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
                        old_rule = rule;
                else
                        return -EOPNOTSUPP;
  
                pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
 -              old_rule = __nf_tables_rule_lookup(chain, pos_handle);
 -              if (IS_ERR(old_rule))
 +              old_rule = __nft_rule_lookup(chain, pos_handle);
 +              if (IS_ERR(old_rule)) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION]);
                        return PTR_ERR(old_rule);
 +              }
        }
  
        nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
                err = nf_tables_newexpr(&ctx, &info[i], expr);
                if (err < 0)
                        goto err2;
 +
 +              if (info[i].ops->validate)
 +                      nft_validate_state_update(net, NFT_VALIDATE_NEED);
 +
                info[i].ops = NULL;
                expr = nft_expr_next(expr);
        }
                }
        }
        chain->use++;
 -      return 0;
  
 +      if (net->nft.validate_state == NFT_VALIDATE_DO)
 +              return nft_table_validate(net, table);
 +
 +      return 0;
  err2:
        nf_tables_rule_release(&ctx, rule);
  err1:
@@@ -2642,37 -2478,32 +2642,37 @@@ static int nf_tables_delrule(struct ne
        int family = nfmsg->nfgen_family, err = 0;
        struct nft_ctx ctx;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_RULE_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_TABLE]);
                return PTR_ERR(table);
 +      }
  
        if (nla[NFTA_RULE_CHAIN]) {
 -              chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN],
 -                                             genmask);
 -              if (IS_ERR(chain))
 +              chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask);
 +              if (IS_ERR(chain)) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
                        return PTR_ERR(chain);
 +              }
        }
  
        nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
  
        if (chain) {
                if (nla[NFTA_RULE_HANDLE]) {
 -                      rule = nf_tables_rule_lookup(chain,
 -                                                   nla[NFTA_RULE_HANDLE]);
 -                      if (IS_ERR(rule))
 +                      rule = nft_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
 +                      if (IS_ERR(rule)) {
 +                              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_HANDLE]);
                                return PTR_ERR(rule);
 +                      }
  
                        err = nft_delrule(&ctx, rule);
                } else if (nla[NFTA_RULE_ID]) {
                        rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]);
 -                      if (IS_ERR(rule))
 +                      if (IS_ERR(rule)) {
 +                              NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]);
                                return PTR_ERR(rule);
 +                      }
  
                        err = nft_delrule(&ctx, rule);
                } else {
@@@ -2717,12 -2548,14 +2717,12 @@@ void nft_unregister_set(struct nft_set_
  EXPORT_SYMBOL_GPL(nft_unregister_set);
  
  #define NFT_SET_FEATURES      (NFT_SET_INTERVAL | NFT_SET_MAP | \
 -                               NFT_SET_TIMEOUT | NFT_SET_OBJECT)
 +                               NFT_SET_TIMEOUT | NFT_SET_OBJECT | \
 +                               NFT_SET_EVAL)
  
 -static bool nft_set_ops_candidate(const struct nft_set_ops *ops, u32 flags)
 +static bool nft_set_ops_candidate(const struct nft_set_type *type, u32 flags)
  {
 -      if ((flags & NFT_SET_EVAL) && !ops->update)
 -              return false;
 -
 -      return (flags & ops->features) == (flags & NFT_SET_FEATURES);
 +      return (flags & type->features) == (flags & NFT_SET_FEATURES);
  }
  
  /*
@@@ -2759,9 -2592,14 +2759,9 @@@ nft_select_set_ops(const struct nft_ct
        best.space  = ~0;
  
        list_for_each_entry(type, &nf_tables_set_types, list) {
 -              if (!type->select_ops)
 -                      ops = type->ops;
 -              else
 -                      ops = type->select_ops(ctx, desc, flags);
 -              if (!ops)
 -                      continue;
 +              ops = &type->ops;
  
 -              if (!nft_set_ops_candidate(ops, flags))
 +              if (!nft_set_ops_candidate(type, flags))
                        continue;
                if (!ops->estimate(desc, flags, &est))
                        continue;
                if (!try_module_get(type->owner))
                        continue;
                if (bops != NULL)
 -                      module_put(bops->type->owner);
 +                      module_put(to_set_type(bops)->owner);
  
                bops = ops;
                best = est;
@@@ -2833,7 -2671,6 +2833,7 @@@ static int nft_ctx_init_from_setattr(st
                                     const struct sk_buff *skb,
                                     const struct nlmsghdr *nlh,
                                     const struct nlattr * const nla[],
 +                                   struct netlink_ext_ack *extack,
                                     u8 genmask)
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        struct nft_table *table = NULL;
  
        if (nla[NFTA_SET_TABLE] != NULL) {
 -              table = nf_tables_table_lookup(net, nla[NFTA_SET_TABLE],
 -                                             family, genmask);
 -              if (IS_ERR(table))
 +              table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family,
 +                                       genmask);
 +              if (IS_ERR(table)) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_SET_TABLE]);
                        return PTR_ERR(table);
 +              }
        }
  
        nft_ctx_init(ctx, net, skb, nlh, family, table, NULL, nla);
        return 0;
  }
  
 -static struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
 -                                          const struct nlattr *nla, u8 genmask)
 +static struct nft_set *nft_set_lookup(const struct nft_table *table,
 +                                    const struct nlattr *nla, u8 genmask)
  {
        struct nft_set *set;
  
        if (nla == NULL)
                return ERR_PTR(-EINVAL);
  
 -      list_for_each_entry(set, &table->sets, list) {
 +      list_for_each_entry_rcu(set, &table->sets, list) {
                if (!nla_strcmp(nla, set->name) &&
                    nft_active_genmask(set, genmask))
                        return set;
        return ERR_PTR(-ENOENT);
  }
  
 -static struct nft_set *nf_tables_set_lookup_byhandle(const struct nft_table *table,
 -                                                   const struct nlattr *nla, u8 genmask)
 +static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table,
 +                                             const struct nlattr *nla,
 +                                             u8 genmask)
  {
        struct nft_set *set;
  
 -      if (nla == NULL)
 -              return ERR_PTR(-EINVAL);
 -
        list_for_each_entry(set, &table->sets, list) {
                if (be64_to_cpu(nla_get_be64(nla)) == set->handle &&
                    nft_active_genmask(set, genmask))
        return ERR_PTR(-ENOENT);
  }
  
 -static struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
 -                                               const struct nlattr *nla,
 -                                               u8 genmask)
 +static struct nft_set *nft_set_lookup_byid(const struct net *net,
 +                                         const struct nlattr *nla, u8 genmask)
  {
        struct nft_trans *trans;
        u32 id = ntohl(nla_get_be32(nla));
  
        list_for_each_entry(trans, &net->nft.commit_list, list) {
-               struct nft_set *set = nft_trans_set(trans);
+               if (trans->msg_type == NFT_MSG_NEWSET) {
+                       struct nft_set *set = nft_trans_set(trans);
  
-               if (trans->msg_type == NFT_MSG_NEWSET &&
-                   id == nft_trans_set_id(trans) &&
-                   nft_active_genmask(set, genmask))
-                       return set;
+                       if (id == nft_trans_set_id(trans) &&
+                           nft_active_genmask(set, genmask))
+                               return set;
+               }
        }
        return ERR_PTR(-ENOENT);
  }
@@@ -2908,12 -2747,12 +2909,12 @@@ struct nft_set *nft_set_lookup_global(c
  {
        struct nft_set *set;
  
 -      set = nf_tables_set_lookup(table, nla_set_name, genmask);
 +      set = nft_set_lookup(table, nla_set_name, genmask);
        if (IS_ERR(set)) {
                if (!nla_set_id)
                        return set;
  
 -              set = nf_tables_set_lookup_byid(net, nla_set_id, genmask);
 +              set = nft_set_lookup_byid(net, nla_set_id, genmask);
        }
        return set;
  }
@@@ -2973,27 -2812,6 +2974,27 @@@ cont
        return 0;
  }
  
 +static int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result)
 +{
 +      u64 ms = be64_to_cpu(nla_get_be64(nla));
 +      u64 max = (u64)(~((u64)0));
 +
 +      max = div_u64(max, NSEC_PER_MSEC);
 +      if (ms >= max)
 +              return -ERANGE;
 +
 +      ms *= NSEC_PER_MSEC;
 +      *result = nsecs_to_jiffies64(ms);
 +      return 0;
 +}
 +
 +static __be64 nf_jiffies64_to_msecs(u64 input)
 +{
 +      u64 ms = jiffies64_to_nsecs(input);
 +
 +      return cpu_to_be64(div_u64(ms, NSEC_PER_MSEC));
 +}
 +
  static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                              const struct nft_set *set, u16 event, u16 flags)
  {
  
        if (set->timeout &&
            nla_put_be64(skb, NFTA_SET_TIMEOUT,
 -                       cpu_to_be64(jiffies_to_msecs(set->timeout)),
 +                       nf_jiffies64_to_msecs(set->timeout),
                         NFTA_SET_PAD))
                goto nla_put_failure;
        if (set->gc_int &&
@@@ -3166,7 -2984,6 +3167,7 @@@ static int nf_tables_dump_sets_done(str
        return 0;
  }
  
 +/* called with rcu_read_lock held */
  static int nf_tables_getset(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[],
        int err;
  
        /* Verify existence before starting dump */
 -      err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla, genmask);
 +      err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla, extack,
 +                                      genmask);
        if (err < 0)
                return err;
  
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_sets,
                        .done = nf_tables_dump_sets_done,
 +                      .module = THIS_MODULE,
                };
                struct nft_ctx *ctx_dump;
  
 -              ctx_dump = kmalloc(sizeof(*ctx_dump), GFP_KERNEL);
 +              ctx_dump = kmalloc(sizeof(*ctx_dump), GFP_ATOMIC);
                if (ctx_dump == NULL)
                        return -ENOMEM;
  
                *ctx_dump = ctx;
                c.data = ctx_dump;
  
 -              return netlink_dump_start(nlsk, skb, nlh, &c);
 +              return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
        }
  
        /* Only accept unspec with dump */
        if (!nla[NFTA_SET_TABLE])
                return -EINVAL;
  
 -      set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME], genmask);
 +      set = nft_set_lookup(ctx.table, nla[NFTA_SET_NAME], genmask);
        if (IS_ERR(set))
                return PTR_ERR(set);
  
 -      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (skb2 == NULL)
                return -ENOMEM;
  
@@@ -3339,10 -3154,8 +3340,10 @@@ static int nf_tables_newset(struct net 
        if (nla[NFTA_SET_TIMEOUT] != NULL) {
                if (!(flags & NFT_SET_TIMEOUT))
                        return -EINVAL;
 -              timeout = msecs_to_jiffies(be64_to_cpu(nla_get_be64(
 -                                              nla[NFTA_SET_TIMEOUT])));
 +
 +              err = nf_msecs_to_jiffies64(nla[NFTA_SET_TIMEOUT], &timeout);
 +              if (err)
 +                      return err;
        }
        gc_int = 0;
        if (nla[NFTA_SET_GC_INTERVAL] != NULL) {
  
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_SET_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_SET_TABLE]);
                return PTR_ERR(table);
 +      }
  
        nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
  
 -      set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME], genmask);
 +      set = nft_set_lookup(table, nla[NFTA_SET_NAME], genmask);
        if (IS_ERR(set)) {
 -              if (PTR_ERR(set) != -ENOENT)
 +              if (PTR_ERR(set) != -ENOENT) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]);
                        return PTR_ERR(set);
 +              }
        } else {
 -              if (nlh->nlmsg_flags & NLM_F_EXCL)
 +              if (nlh->nlmsg_flags & NLM_F_EXCL) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]);
                        return -EEXIST;
 +              }
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
 +
                return 0;
        }
  
        }
  
        INIT_LIST_HEAD(&set->bindings);
 +      set->table = table;
 +      write_pnet(&set->net, net);
        set->ops   = ops;
        set->ktype = ktype;
        set->klen  = desc.klen;
@@@ -3463,14 -3268,14 +3464,14 @@@ err3
  err2:
        kvfree(set);
  err1:
 -      module_put(ops->type->owner);
 +      module_put(to_set_type(ops)->owner);
        return err;
  }
  
  static void nft_set_destroy(struct nft_set *set)
  {
        set->ops->destroy(set);
 -      module_put(set->ops->type->owner);
 +      module_put(to_set_type(set->ops)->owner);
        kfree(set->name);
        kvfree(set);
  }
@@@ -3489,7 -3294,6 +3490,7 @@@ static int nf_tables_delset(struct net 
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
 +      const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
        int err;
        if (nla[NFTA_SET_TABLE] == NULL)
                return -EINVAL;
  
 -      err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla, genmask);
 +      err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla, extack,
 +                                      genmask);
        if (err < 0)
                return err;
  
 -      if (nla[NFTA_SET_HANDLE])
 -              set = nf_tables_set_lookup_byhandle(ctx.table, nla[NFTA_SET_HANDLE], genmask);
 -      else
 -              set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME], genmask);
 -      if (IS_ERR(set))
 -              return PTR_ERR(set);
 +      if (nla[NFTA_SET_HANDLE]) {
 +              attr = nla[NFTA_SET_HANDLE];
 +              set = nft_set_lookup_byhandle(ctx.table, attr, genmask);
 +      } else {
 +              attr = nla[NFTA_SET_NAME];
 +              set = nft_set_lookup(ctx.table, attr, genmask);
 +      }
  
 +      if (IS_ERR(set)) {
 +              NL_SET_BAD_ATTR(extack, attr);
 +              return PTR_ERR(set);
 +      }
        if (!list_empty(&set->bindings) ||
 -          (nlh->nlmsg_flags & NLM_F_NONREC && atomic_read(&set->nelems) > 0))
 +          (nlh->nlmsg_flags & NLM_F_NONREC && atomic_read(&set->nelems) > 0)) {
 +              NL_SET_BAD_ATTR(extack, attr);
                return -EBUSY;
 +      }
  
        return nft_delset(&ctx, set);
  }
@@@ -3610,8 -3406,8 +3611,8 @@@ const struct nft_set_ext_type nft_set_e
                .align  = __alignof__(u64),
        },
        [NFT_SET_EXT_EXPIRATION]        = {
 -              .len    = sizeof(unsigned long),
 -              .align  = __alignof__(unsigned long),
 +              .len    = sizeof(u64),
 +              .align  = __alignof__(u64),
        },
        [NFT_SET_EXT_USERDATA]          = {
                .len    = sizeof(struct nft_userdata),
@@@ -3648,19 -3444,16 +3649,19 @@@ static int nft_ctx_init_from_elemattr(s
                                      const struct sk_buff *skb,
                                      const struct nlmsghdr *nlh,
                                      const struct nlattr * const nla[],
 +                                    struct netlink_ext_ack *extack,
                                      u8 genmask)
  {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        int family = nfmsg->nfgen_family;
        struct nft_table *table;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE],
 -                                     family, genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
 +                               genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_TABLE]);
                return PTR_ERR(table);
 +      }
  
        nft_ctx_init(ctx, net, skb, nlh, family, table, NULL, nla);
        return 0;
@@@ -3704,21 -3497,22 +3705,21 @@@ static int nf_tables_fill_setelem(struc
  
        if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
            nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
 -                       cpu_to_be64(jiffies_to_msecs(
 -                                              *nft_set_ext_timeout(ext))),
 +                       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_EXPIRATION)) {
 -              unsigned long expires, now = jiffies;
 +              u64 expires, now = get_jiffies_64();
  
                expires = *nft_set_ext_expiration(ext);
 -              if (time_before(now, expires))
 +              if (time_before64(now, expires))
                        expires -= now;
                else
                        expires = 0;
  
                if (nla_put_be64(skb, NFTA_SET_ELEM_EXPIRATION,
 -                               cpu_to_be64(jiffies_to_msecs(expires)),
 +                               nf_jiffies64_to_msecs(expires),
                                 NFTA_SET_ELEM_PAD))
                        goto nla_put_failure;
        }
@@@ -3956,7 -3750,7 +3957,7 @@@ static int nft_get_set_elem(struct nft_
        ext = nft_set_elem_ext(set, &elem);
  
        err = -ENOMEM;
 -      skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (skb == NULL)
                goto err1;
  
@@@ -3978,7 -3772,6 +3979,7 @@@ err1
        return err == -EAGAIN ? -ENOBUFS : err;
  }
  
 +/* called with rcu_read_lock held */
  static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
                                struct sk_buff *skb, const struct nlmsghdr *nlh,
                                const struct nlattr * const nla[],
        struct nft_ctx ctx;
        int rem, err = 0;
  
 -      err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask);
 +      err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, extack,
 +                                       genmask);
        if (err < 0)
                return err;
  
 -      set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET],
 -                                 genmask);
 +      set = nft_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET], genmask);
        if (IS_ERR(set))
                return PTR_ERR(set);
  
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_set,
                        .done = nf_tables_dump_set_done,
 +                      .module = THIS_MODULE,
                };
                struct nft_set_dump_ctx *dump_ctx;
  
 -              dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_KERNEL);
 +              dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_ATOMIC);
                if (!dump_ctx)
                        return -ENOMEM;
  
                dump_ctx->ctx = ctx;
  
                c.data = dump_ctx;
 -              return netlink_dump_start(nlsk, skb, nlh, &c);
 +              return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
        }
  
        if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
@@@ -4095,7 -3887,7 +4096,7 @@@ void *nft_set_elem_init(const struct nf
                memcpy(nft_set_ext_data(ext), data, set->dlen);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION))
                *nft_set_ext_expiration(ext) =
 -                      jiffies + timeout;
 +                      get_jiffies_64() + timeout;
        if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT))
                *nft_set_ext_timeout(ext) = timeout;
  
@@@ -4106,24 -3898,12 +4107,24 @@@ void nft_set_elem_destroy(const struct 
                          bool destroy_expr)
  {
        struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
 +      struct nft_ctx ctx = {
 +              .net    = read_pnet(&set->net),
 +              .family = set->table->family,
 +      };
  
        nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
                nft_data_release(nft_set_ext_data(ext), set->dtype);
 -      if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
 -              nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
 +      if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) {
 +              struct nft_expr *expr = nft_set_ext_expr(ext);
 +
 +              if (expr->ops->destroy_clone) {
 +                      expr->ops->destroy_clone(&ctx, expr);
 +                      module_put(expr->ops->type->owner);
 +              } else {
 +                      nf_tables_expr_destroy(&ctx, expr);
 +              }
 +      }
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
                (*nft_set_ext_obj(ext))->use--;
        kfree(elem);
@@@ -4133,13 -3913,12 +4134,13 @@@ EXPORT_SYMBOL_GPL(nft_set_elem_destroy)
  /* Only called from commit path, nft_set_elem_deactivate() already deals with
   * the refcounting from the preparation phase.
   */
 -static void nf_tables_set_elem_destroy(const struct nft_set *set, void *elem)
 +static 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);
  
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
 -              nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
 +              nf_tables_expr_destroy(ctx, nft_set_ext_expr(ext));
        kfree(elem);
  }
  
@@@ -4195,10 -3974,8 +4196,10 @@@ static int nft_add_set_elem(struct nft_
        if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) {
                if (!(set->flags & NFT_SET_TIMEOUT))
                        return -EINVAL;
 -              timeout = msecs_to_jiffies(be64_to_cpu(nla_get_be64(
 -                                      nla[NFTA_SET_ELEM_TIMEOUT])));
 +              err = nf_msecs_to_jiffies64(nla[NFTA_SET_ELEM_TIMEOUT],
 +                                          &timeout);
 +              if (err)
 +                      return err;
        } else if (set->flags & NFT_SET_TIMEOUT) {
                timeout = set->timeout;
        }
                        err = -EINVAL;
                        goto err2;
                }
 -              obj = nf_tables_obj_lookup(ctx->table, nla[NFTA_SET_ELEM_OBJREF],
 -                                         set->objtype, genmask);
 +              obj = nft_obj_lookup(ctx->table, nla[NFTA_SET_ELEM_OBJREF],
 +                                   set->objtype, genmask);
                if (IS_ERR(obj)) {
                        err = PTR_ERR(obj);
                        goto err2;
                                                          d2.type, d2.len);
                        if (err < 0)
                                goto err3;
 +
 +                      if (d2.type == NFT_DATA_VERDICT &&
 +                          (data.verdict.code == NFT_GOTO ||
 +                           data.verdict.code == NFT_JUMP))
 +                              nft_validate_state_update(ctx->net,
 +                                                        NFT_VALIDATE_NEED);
                }
  
                nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, d2.len);
@@@ -4364,13 -4135,12 +4365,13 @@@ static int nf_tables_newsetelem(struct 
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
 -      int rem, err = 0;
 +      int rem, err;
  
        if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
                return -EINVAL;
  
 -      err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask);
 +      err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, extack,
 +                                       genmask);
        if (err < 0)
                return err;
  
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_add_set_elem(&ctx, set, attr, nlh->nlmsg_flags);
                if (err < 0)
 -                      break;
 +                      return err;
        }
 -      return err;
 +
 +      if (net->nft.validate_state == NFT_VALIDATE_DO)
 +              return nft_table_validate(net, ctx.table);
 +
 +      return 0;
  }
  
  /**
@@@ -4562,12 -4328,12 +4563,12 @@@ static int nf_tables_delsetelem(struct 
        struct nft_ctx ctx;
        int rem, err = 0;
  
 -      err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask);
 +      err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, extack,
 +                                       genmask);
        if (err < 0)
                return err;
  
 -      set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET],
 -                                 genmask);
 +      set = nft_set_lookup(ctx.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)
@@@ -4655,13 -4421,13 +4656,13 @@@ void nft_unregister_obj(struct nft_obje
  }
  EXPORT_SYMBOL_GPL(nft_unregister_obj);
  
 -struct nft_object *nf_tables_obj_lookup(const struct nft_table *table,
 -                                      const struct nlattr *nla,
 -                                      u32 objtype, u8 genmask)
 +struct nft_object *nft_obj_lookup(const struct nft_table *table,
 +                                const struct nlattr *nla, u32 objtype,
 +                                u8 genmask)
  {
        struct nft_object *obj;
  
 -      list_for_each_entry(obj, &table->objects, list) {
 +      list_for_each_entry_rcu(obj, &table->objects, list) {
                if (!nla_strcmp(nla, obj->name) &&
                    objtype == obj->ops->type->type &&
                    nft_active_genmask(obj, genmask))
        }
        return ERR_PTR(-ENOENT);
  }
 -EXPORT_SYMBOL_GPL(nf_tables_obj_lookup);
 +EXPORT_SYMBOL_GPL(nft_obj_lookup);
  
 -static struct nft_object *nf_tables_obj_lookup_byhandle(const struct nft_table *table,
 -                                                      const struct nlattr *nla,
 -                                                      u32 objtype, u8 genmask)
 +static struct nft_object *nft_obj_lookup_byhandle(const struct nft_table *table,
 +                                                const struct nlattr *nla,
 +                                                u32 objtype, u8 genmask)
  {
        struct nft_object *obj;
  
@@@ -4817,25 -4583,22 +4818,25 @@@ static int nf_tables_newobj(struct net 
            !nla[NFTA_OBJ_DATA])
                return -EINVAL;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_OBJ_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_OBJ_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_TABLE]);
                return PTR_ERR(table);
 +      }
  
        objtype = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE]));
 -      obj = nf_tables_obj_lookup(table, nla[NFTA_OBJ_NAME], objtype, genmask);
 +      obj = nft_obj_lookup(table, nla[NFTA_OBJ_NAME], objtype, genmask);
        if (IS_ERR(obj)) {
                err = PTR_ERR(obj);
 -              if (err != -ENOENT)
 +              if (err != -ENOENT) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_NAME]);
                        return err;
 -
 +              }
        } else {
 -              if (nlh->nlmsg_flags & NLM_F_EXCL)
 +              if (nlh->nlmsg_flags & NLM_F_EXCL) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_NAME]);
                        return -EEXIST;
 -
 +              }
                return 0;
        }
  
@@@ -4870,7 -4633,7 +4871,7 @@@ err3
        kfree(obj->name);
  err2:
        if (obj->ops->destroy)
 -              obj->ops->destroy(obj);
 +              obj->ops->destroy(&ctx, obj);
        kfree(obj);
  err1:
        module_put(type->owner);
@@@ -4991,12 -4754,12 +4992,12 @@@ nft_obj_filter_alloc(const struct nlatt
  {
        struct nft_obj_filter *filter;
  
 -      filter = kzalloc(sizeof(*filter), GFP_KERNEL);
 +      filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
        if (!filter)
                return ERR_PTR(-ENOMEM);
  
        if (nla[NFTA_OBJ_TABLE]) {
 -              filter->table = nla_strdup(nla[NFTA_OBJ_TABLE], GFP_KERNEL);
 +              filter->table = nla_strdup(nla[NFTA_OBJ_TABLE], GFP_ATOMIC);
                if (!filter->table) {
                        kfree(filter);
                        return ERR_PTR(-ENOMEM);
        return filter;
  }
  
 +/* called with rcu_read_lock held */
  static int nf_tables_getobj(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[],
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_obj,
                        .done = nf_tables_dump_obj_done,
 +                      .module = THIS_MODULE,
                };
  
                if (nla[NFTA_OBJ_TABLE] ||
  
                        c.data = filter;
                }
 -              return netlink_dump_start(nlsk, skb, nlh, &c);
 +              return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
        }
  
        if (!nla[NFTA_OBJ_NAME] ||
            !nla[NFTA_OBJ_TYPE])
                return -EINVAL;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_OBJ_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_OBJ_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_TABLE]);
                return PTR_ERR(table);
 +      }
  
        objtype = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE]));
 -      obj = nf_tables_obj_lookup(table, nla[NFTA_OBJ_NAME], objtype, genmask);
 -      if (IS_ERR(obj))
 +      obj = nft_obj_lookup(table, nla[NFTA_OBJ_NAME], objtype, genmask);
 +      if (IS_ERR(obj)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_NAME]);
                return PTR_ERR(obj);
 +      }
  
 -      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (!skb2)
                return -ENOMEM;
  
        return err;
  }
  
 -static void nft_obj_destroy(struct nft_object *obj)
 +static void nft_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
  {
        if (obj->ops->destroy)
 -              obj->ops->destroy(obj);
 +              obj->ops->destroy(ctx, obj);
  
        module_put(obj->ops->type->owner);
        kfree(obj->name);
@@@ -5098,7 -4856,6 +5099,7 @@@ static int nf_tables_delobj(struct net 
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
        int family = nfmsg->nfgen_family;
 +      const struct nlattr *attr;
        struct nft_table *table;
        struct nft_object *obj;
        struct nft_ctx ctx;
            (!nla[NFTA_OBJ_NAME] && !nla[NFTA_OBJ_HANDLE]))
                return -EINVAL;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_OBJ_TABLE], family,
 -                                     genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_OBJ_TABLE], family, genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_TABLE]);
                return PTR_ERR(table);
 +      }
  
        objtype = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE]));
 -      if (nla[NFTA_OBJ_HANDLE])
 -              obj = nf_tables_obj_lookup_byhandle(table, nla[NFTA_OBJ_HANDLE],
 -                                                  objtype, genmask);
 -      else
 -              obj = nf_tables_obj_lookup(table, nla[NFTA_OBJ_NAME],
 -                                         objtype, genmask);
 -      if (IS_ERR(obj))
 +      if (nla[NFTA_OBJ_HANDLE]) {
 +              attr = nla[NFTA_OBJ_HANDLE];
 +              obj = nft_obj_lookup_byhandle(table, attr, objtype, genmask);
 +      } else {
 +              attr = nla[NFTA_OBJ_NAME];
 +              obj = nft_obj_lookup(table, attr, objtype, genmask);
 +      }
 +
 +      if (IS_ERR(obj)) {
 +              NL_SET_BAD_ATTR(extack, attr);
                return PTR_ERR(obj);
 -      if (obj->use > 0)
 +      }
 +      if (obj->use > 0) {
 +              NL_SET_BAD_ATTR(extack, attr);
                return -EBUSY;
 +      }
  
        nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
  
@@@ -5201,23 -4951,24 +5202,23 @@@ static const struct nla_policy nft_flow
        [NFTA_FLOWTABLE_HANDLE]         = { .type = NLA_U64 },
  };
  
 -struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
 -                                               const struct nlattr *nla,
 -                                               u8 genmask)
 +struct nft_flowtable *nft_flowtable_lookup(const struct nft_table *table,
 +                                         const struct nlattr *nla, u8 genmask)
  {
        struct nft_flowtable *flowtable;
  
 -      list_for_each_entry(flowtable, &table->flowtables, list) {
 +      list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
                if (!nla_strcmp(nla, flowtable->name) &&
                    nft_active_genmask(flowtable, genmask))
                        return flowtable;
        }
        return ERR_PTR(-ENOENT);
  }
 -EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup);
 +EXPORT_SYMBOL_GPL(nft_flowtable_lookup);
  
  static struct nft_flowtable *
 -nf_tables_flowtable_lookup_byhandle(const struct nft_table *table,
 -                                  const struct nlattr *nla, u8 genmask)
 +nft_flowtable_lookup_byhandle(const struct nft_table *table,
 +                            const struct nlattr *nla, u8 genmask)
  {
         struct nft_flowtable *flowtable;
  
@@@ -5316,7 -5067,7 +5317,7 @@@ static int nf_tables_flowtable_parse_ho
                flowtable->ops[i].pf            = NFPROTO_NETDEV;
                flowtable->ops[i].hooknum       = hooknum;
                flowtable->ops[i].priority      = priority;
 -              flowtable->ops[i].priv          = &flowtable->data.rhashtable;
 +              flowtable->ops[i].priv          = &flowtable->data;
                flowtable->ops[i].hook          = flowtable->data.type->hook;
                flowtable->ops[i].dev           = dev_array[i];
                flowtable->dev_name[i]          = kstrdup(dev_array[i]->name,
@@@ -5357,6 -5108,23 +5358,6 @@@ static const struct nf_flowtable_type *
        return ERR_PTR(-ENOENT);
  }
  
 -void nft_flow_table_iterate(struct net *net,
 -                          void (*iter)(struct nf_flowtable *flowtable, void *data),
 -                          void *data)
 -{
 -      struct nft_flowtable *flowtable;
 -      const struct nft_table *table;
 -
 -      nfnl_lock(NFNL_SUBSYS_NFTABLES);
 -      list_for_each_entry(table, &net->nft.tables, list) {
 -              list_for_each_entry(flowtable, &table->flowtables, list) {
 -                      iter(&flowtable->data, data);
 -              }
 -      }
 -      nfnl_unlock(NFNL_SUBSYS_NFTABLES);
 -}
 -EXPORT_SYMBOL_GPL(nft_flow_table_iterate);
 -
  static void nft_unregister_flowtable_net_hooks(struct net *net,
                                               struct nft_flowtable *flowtable)
  {
@@@ -5390,26 -5158,20 +5391,26 @@@ static int nf_tables_newflowtable(struc
            !nla[NFTA_FLOWTABLE_HOOK])
                return -EINVAL;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_FLOWTABLE_TABLE],
 -                                     family, genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_FLOWTABLE_TABLE], family,
 +                               genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_FLOWTABLE_TABLE]);
                return PTR_ERR(table);
 +      }
  
 -      flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
 -                                             genmask);
 +      flowtable = nft_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
 +                                       genmask);
        if (IS_ERR(flowtable)) {
                err = PTR_ERR(flowtable);
 -              if (err != -ENOENT)
 +              if (err != -ENOENT) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_FLOWTABLE_NAME]);
                        return err;
 +              }
        } else {
 -              if (nlh->nlmsg_flags & NLM_F_EXCL)
 +              if (nlh->nlmsg_flags & NLM_F_EXCL) {
 +                      NL_SET_BAD_ATTR(extack, nla[NFTA_FLOWTABLE_NAME]);
                        return -EEXIST;
 +              }
  
                return 0;
        }
        }
  
        flowtable->data.type = type;
 -      err = rhashtable_init(&flowtable->data.rhashtable, type->params);
 +      err = type->init(&flowtable->data);
        if (err < 0)
                goto err3;
  
        err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],
                                             flowtable);
        if (err < 0)
 -              goto err3;
 +              goto err4;
  
        for (i = 0; i < flowtable->ops_len; i++) {
                if (!flowtable->ops[i].dev)
                                if (flowtable->ops[i].dev == ft->ops[k].dev &&
                                    flowtable->ops[i].pf == ft->ops[k].pf) {
                                        err = -EBUSY;
 -                                      goto err4;
 +                                      goto err5;
                                }
                        }
                }
  
                err = nf_register_net_hook(net, &flowtable->ops[i]);
                if (err < 0)
 -                      goto err4;
 +                      goto err5;
        }
  
        err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);
        if (err < 0)
 -              goto err5;
 -
 -      INIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc);
 -      queue_delayed_work(system_power_efficient_wq,
 -                         &flowtable->data.gc_work, HZ);
 +              goto err6;
  
        list_add_tail_rcu(&flowtable->list, &table->flowtables);
        table->use++;
  
        return 0;
 -err5:
 +err6:
        i = flowtable->ops_len;
 -err4:
 +err5:
        for (k = i - 1; k >= 0; k--) {
                kfree(flowtable->dev_name[k]);
                nf_unregister_net_hook(net, &flowtable->ops[k]);
        }
  
        kfree(flowtable->ops);
 +err4:
 +      flowtable->data.type->free(&flowtable->data);
  err3:
        module_put(type->owner);
  err2:
@@@ -5505,7 -5269,6 +5506,7 @@@ static int nf_tables_delflowtable(struc
        u8 genmask = nft_genmask_next(net);
        int family = nfmsg->nfgen_family;
        struct nft_flowtable *flowtable;
 +      const struct nlattr *attr;
        struct nft_table *table;
        struct nft_ctx ctx;
  
             !nla[NFTA_FLOWTABLE_HANDLE]))
                return -EINVAL;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_FLOWTABLE_TABLE],
 -                                     family, genmask);
 -      if (IS_ERR(table))
 +      table = nft_table_lookup(net, nla[NFTA_FLOWTABLE_TABLE], family,
 +                               genmask);
 +      if (IS_ERR(table)) {
 +              NL_SET_BAD_ATTR(extack, nla[NFTA_FLOWTABLE_TABLE]);
                return PTR_ERR(table);
 +      }
  
 -      if (nla[NFTA_FLOWTABLE_HANDLE])
 -              flowtable = nf_tables_flowtable_lookup_byhandle(table,
 -                                                              nla[NFTA_FLOWTABLE_HANDLE],
 -                                                              genmask);
 -      else
 -              flowtable = nf_tables_flowtable_lookup(table,
 -                                                     nla[NFTA_FLOWTABLE_NAME],
 -                                                     genmask);
 -      if (IS_ERR(flowtable))
 -                return PTR_ERR(flowtable);
 -      if (flowtable->use > 0)
 +      if (nla[NFTA_FLOWTABLE_HANDLE]) {
 +              attr = nla[NFTA_FLOWTABLE_HANDLE];
 +              flowtable = nft_flowtable_lookup_byhandle(table, attr, genmask);
 +      } else {
 +              attr = nla[NFTA_FLOWTABLE_NAME];
 +              flowtable = nft_flowtable_lookup(table, attr, genmask);
 +      }
 +
 +      if (IS_ERR(flowtable)) {
 +              NL_SET_BAD_ATTR(extack, attr);
 +              return PTR_ERR(flowtable);
 +      }
 +      if (flowtable->use > 0) {
 +              NL_SET_BAD_ATTR(extack, attr);
                return -EBUSY;
 +      }
  
        nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
  
@@@ -5667,13 -5424,13 +5668,13 @@@ nft_flowtable_filter_alloc(const struc
  {
        struct nft_flowtable_filter *filter;
  
 -      filter = kzalloc(sizeof(*filter), GFP_KERNEL);
 +      filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
        if (!filter)
                return ERR_PTR(-ENOMEM);
  
        if (nla[NFTA_FLOWTABLE_TABLE]) {
                filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE],
 -                                         GFP_KERNEL);
 +                                         GFP_ATOMIC);
                if (!filter->table) {
                        kfree(filter);
                        return ERR_PTR(-ENOMEM);
        return filter;
  }
  
 +/* called with rcu_read_lock held */
  static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
                                  struct sk_buff *skb,
                                  const struct nlmsghdr *nlh,
                struct netlink_dump_control c = {
                        .dump = nf_tables_dump_flowtable,
                        .done = nf_tables_dump_flowtable_done,
 +                      .module = THIS_MODULE,
                };
  
                if (nla[NFTA_FLOWTABLE_TABLE]) {
  
                        c.data = filter;
                }
 -              return netlink_dump_start(nlsk, skb, nlh, &c);
 +              return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
        }
  
        if (!nla[NFTA_FLOWTABLE_NAME])
                return -EINVAL;
  
 -      table = nf_tables_table_lookup(net, nla[NFTA_FLOWTABLE_TABLE],
 -                                     family, genmask);
 +      table = nft_table_lookup(net, nla[NFTA_FLOWTABLE_TABLE], family,
 +                               genmask);
        if (IS_ERR(table))
                return PTR_ERR(table);
  
 -      flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
 -                                             genmask);
 +      flowtable = nft_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
 +                                       genmask);
        if (IS_ERR(flowtable))
                return PTR_ERR(flowtable);
  
 -      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (!skb2)
                return -ENOMEM;
  
@@@ -5778,9 -5533,11 +5779,9 @@@ err
  
  static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
  {
 -      cancel_delayed_work_sync(&flowtable->data.gc_work);
        kfree(flowtable->ops);
        kfree(flowtable->name);
        flowtable->data.type->free(&flowtable->data);
 -      rhashtable_destroy(&flowtable->data.rhashtable);
        module_put(flowtable->data.type->owner);
  }
  
@@@ -5893,7 -5650,7 +5894,7 @@@ static int nf_tables_getgen(struct net 
        struct sk_buff *skb2;
        int err;
  
 -      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 +      skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (skb2 == NULL)
                return -ENOMEM;
  
@@@ -5915,7 -5672,7 +5916,7 @@@ static const struct nfnl_callback nf_ta
                .policy         = nft_table_policy,
        },
        [NFT_MSG_GETTABLE] = {
 -              .call           = nf_tables_gettable,
 +              .call_rcu       = nf_tables_gettable,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
                .policy         = nft_chain_policy,
        },
        [NFT_MSG_GETCHAIN] = {
 -              .call           = nf_tables_getchain,
 +              .call_rcu       = nf_tables_getchain,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
                .policy         = nft_rule_policy,
        },
        [NFT_MSG_GETRULE] = {
 -              .call           = nf_tables_getrule,
 +              .call_rcu       = nf_tables_getrule,
                .attr_count     = NFTA_RULE_MAX,
                .policy         = nft_rule_policy,
        },
                .policy         = nft_set_policy,
        },
        [NFT_MSG_GETSET] = {
 -              .call           = nf_tables_getset,
 +              .call_rcu       = nf_tables_getset,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_GETSETELEM] = {
 -              .call           = nf_tables_getsetelem,
 +              .call_rcu       = nf_tables_getsetelem,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_GETGEN] = {
 -              .call           = nf_tables_getgen,
 +              .call_rcu       = nf_tables_getgen,
        },
        [NFT_MSG_NEWOBJ] = {
                .call_batch     = nf_tables_newobj,
                .policy         = nft_obj_policy,
        },
        [NFT_MSG_GETOBJ] = {
 -              .call           = nf_tables_getobj,
 +              .call_rcu       = nf_tables_getobj,
                .attr_count     = NFTA_OBJ_MAX,
                .policy         = nft_obj_policy,
        },
                .policy         = nft_obj_policy,
        },
        [NFT_MSG_GETOBJ_RESET] = {
 -              .call           = nf_tables_getobj,
 +              .call_rcu       = nf_tables_getobj,
                .attr_count     = NFTA_OBJ_MAX,
                .policy         = nft_obj_policy,
        },
                .policy         = nft_flowtable_policy,
        },
        [NFT_MSG_GETFLOWTABLE] = {
 -              .call           = nf_tables_getflowtable,
 +              .call_rcu       = nf_tables_getflowtable,
                .attr_count     = NFTA_FLOWTABLE_MAX,
                .policy         = nft_flowtable_policy,
        },
        },
  };
  
 +static int nf_tables_validate(struct net *net)
 +{
 +      struct nft_table *table;
 +
 +      switch (net->nft.validate_state) {
 +      case NFT_VALIDATE_SKIP:
 +              break;
 +      case NFT_VALIDATE_NEED:
 +              nft_validate_state_update(net, NFT_VALIDATE_DO);
 +              /* fall through */
 +      case NFT_VALIDATE_DO:
 +              list_for_each_entry(table, &net->nft.tables, list) {
 +                      if (nft_table_validate(net, table) < 0)
 +                              return -EAGAIN;
 +              }
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
  static void nft_chain_commit_update(struct nft_trans *trans)
  {
        struct nft_base_chain *basechain;
  
 -      if (nft_trans_chain_name(trans))
 +      if (nft_trans_chain_name(trans)) {
 +              rhltable_remove(&trans->ctx.table->chains_ht,
 +                              &trans->ctx.chain->rhlhead,
 +                              nft_chain_ht_params);
                swap(trans->ctx.chain->name, nft_trans_chain_name(trans));
 +              rhltable_insert_key(&trans->ctx.table->chains_ht,
 +                                  trans->ctx.chain->name,
 +                                  &trans->ctx.chain->rhlhead,
 +                                  nft_chain_ht_params);
 +      }
  
        if (!nft_is_base_chain(trans->ctx.chain))
                return;
@@@ -6090,12 -5818,11 +6091,12 @@@ static void nft_commit_release(struct n
                nft_set_destroy(nft_trans_set(trans));
                break;
        case NFT_MSG_DELSETELEM:
 -              nf_tables_set_elem_destroy(nft_trans_elem_set(trans),
 +              nf_tables_set_elem_destroy(&trans->ctx,
 +                                         nft_trans_elem_set(trans),
                                           nft_trans_elem(trans).priv);
                break;
        case NFT_MSG_DELOBJ:
 -              nft_obj_destroy(nft_trans_obj(trans));
 +              nft_obj_destroy(&trans->ctx, nft_trans_obj(trans));
                break;
        case NFT_MSG_DELFLOWTABLE:
                nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
@@@ -6119,175 -5846,21 +6120,175 @@@ static void nf_tables_commit_release(st
        }
  }
  
 +static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain)
 +{
 +      struct nft_rule *rule;
 +      unsigned int alloc = 0;
 +      int i;
 +
 +      /* already handled or inactive chain? */
 +      if (chain->rules_next || !nft_is_active_next(net, chain))
 +              return 0;
 +
 +      rule = list_entry(&chain->rules, struct nft_rule, list);
 +      i = 0;
 +
 +      list_for_each_entry_continue(rule, &chain->rules, list) {
 +              if (nft_is_active_next(net, rule))
 +                      alloc++;
 +      }
 +
 +      chain->rules_next = nf_tables_chain_alloc_rules(chain, alloc);
 +      if (!chain->rules_next)
 +              return -ENOMEM;
 +
 +      list_for_each_entry_continue(rule, &chain->rules, list) {
 +              if (nft_is_active_next(net, rule))
 +                      chain->rules_next[i++] = rule;
 +      }
 +
 +      chain->rules_next[i] = NULL;
 +      return 0;
 +}
 +
 +static void nf_tables_commit_chain_prepare_cancel(struct net *net)
 +{
 +      struct nft_trans *trans, *next;
 +
 +      list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
 +              struct nft_chain *chain = trans->ctx.chain;
 +
 +              if (trans->msg_type == NFT_MSG_NEWRULE ||
 +                  trans->msg_type == NFT_MSG_DELRULE) {
 +                      kvfree(chain->rules_next);
 +                      chain->rules_next = NULL;
 +              }
 +      }
 +}
 +
 +static void __nf_tables_commit_chain_free_rules_old(struct rcu_head *h)
 +{
 +      struct nft_rules_old *o = container_of(h, struct nft_rules_old, h);
 +
 +      kvfree(o->start);
 +}
 +
 +static void nf_tables_commit_chain_free_rules_old(struct nft_rule **rules)
 +{
 +      struct nft_rule **r = rules;
 +      struct nft_rules_old *old;
 +
 +      while (*r)
 +              r++;
 +
 +      r++;    /* rcu_head is after end marker */
 +      old = (void *) r;
 +      old->start = rules;
 +
 +      call_rcu(&old->h, __nf_tables_commit_chain_free_rules_old);
 +}
 +
 +static void nf_tables_commit_chain_active(struct net *net, struct nft_chain *chain)
 +{
 +      struct nft_rule **g0, **g1;
 +      bool next_genbit;
 +
 +      next_genbit = nft_gencursor_next(net);
 +
 +      g0 = rcu_dereference_protected(chain->rules_gen_0,
 +                                     lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
 +      g1 = rcu_dereference_protected(chain->rules_gen_1,
 +                                     lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
 +
 +      /* No changes to this chain? */
 +      if (chain->rules_next == NULL) {
 +              /* chain had no change in last or next generation */
 +              if (g0 == g1)
 +                      return;
 +              /*
 +               * chain had no change in this generation; make sure next
 +               * one uses same rules as current generation.
 +               */
 +              if (next_genbit) {
 +                      rcu_assign_pointer(chain->rules_gen_1, g0);
 +                      nf_tables_commit_chain_free_rules_old(g1);
 +              } else {
 +                      rcu_assign_pointer(chain->rules_gen_0, g1);
 +                      nf_tables_commit_chain_free_rules_old(g0);
 +              }
 +
 +              return;
 +      }
 +
 +      if (next_genbit)
 +              rcu_assign_pointer(chain->rules_gen_1, chain->rules_next);
 +      else
 +              rcu_assign_pointer(chain->rules_gen_0, chain->rules_next);
 +
 +      chain->rules_next = NULL;
 +
 +      if (g0 == g1)
 +              return;
 +
 +      if (next_genbit)
 +              nf_tables_commit_chain_free_rules_old(g1);
 +      else
 +              nf_tables_commit_chain_free_rules_old(g0);
 +}
 +
 +static void nft_chain_del(struct nft_chain *chain)
 +{
 +      struct nft_table *table = chain->table;
 +
 +      WARN_ON_ONCE(rhltable_remove(&table->chains_ht, &chain->rhlhead,
 +                                   nft_chain_ht_params));
 +      list_del_rcu(&chain->list);
 +}
 +
  static int nf_tables_commit(struct net *net, struct sk_buff *skb)
  {
        struct nft_trans *trans, *next;
        struct nft_trans_elem *te;
 +      struct nft_chain *chain;
 +      struct nft_table *table;
  
 -      /* Bump generation counter, invalidate any dump in progress */
 -      while (++net->nft.base_seq == 0);
 +      /* 0. Validate ruleset, otherwise roll back for error reporting. */
 +      if (nf_tables_validate(net) < 0)
 +              return -EAGAIN;
  
 -      /* A new generation has just started */
 -      net->nft.gencursor = nft_gencursor_next(net);
 +      /* 1.  Allocate space for next generation rules_gen_X[] */
 +      list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
 +              int ret;
 +
 +              if (trans->msg_type == NFT_MSG_NEWRULE ||
 +                  trans->msg_type == NFT_MSG_DELRULE) {
 +                      chain = trans->ctx.chain;
 +
 +                      ret = nf_tables_commit_chain_prepare(net, chain);
 +                      if (ret < 0) {
 +                              nf_tables_commit_chain_prepare_cancel(net);
 +                              return ret;
 +                      }
 +              }
 +      }
 +
 +      /* step 2.  Make rules_gen_X visible to packet path */
 +      list_for_each_entry(table, &net->nft.tables, list) {
 +              list_for_each_entry(chain, &table->chains, list) {
 +                      if (!nft_is_active_next(net, chain))
 +                              continue;
 +                      nf_tables_commit_chain_active(net, chain);
 +              }
 +      }
  
 -      /* Make sure all packets have left the previous generation before
 -       * purging old rules.
 +      /*
 +       * Bump generation counter, invalidate any dump in progress.
 +       * Cannot fail after this point.
         */
 -      synchronize_rcu();
 +      while (++net->nft.base_seq == 0);
 +
 +      /* step 3. Start new generation, rules_gen_X now in use. */
 +      net->nft.gencursor = nft_gencursor_next(net);
  
        list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
                switch (trans->msg_type) {
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELCHAIN:
 -                      list_del_rcu(&trans->ctx.chain->list);
 +                      nft_chain_del(trans->ctx.chain);
                        nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
                        nf_tables_unregister_hook(trans->ctx.net,
                                                  trans->ctx.table,
@@@ -6429,7 -6002,7 +6430,7 @@@ static void nf_tables_abort_release(str
                                     nft_trans_elem(trans).priv, true);
                break;
        case NFT_MSG_NEWOBJ:
 -              nft_obj_destroy(nft_trans_obj(trans));
 +              nft_obj_destroy(&trans->ctx, nft_trans_obj(trans));
                break;
        case NFT_MSG_NEWFLOWTABLE:
                nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
@@@ -6469,7 -6042,7 +6470,7 @@@ static int nf_tables_abort(struct net *
                                nft_trans_destroy(trans);
                        } else {
                                trans->ctx.table->use--;
 -                              list_del_rcu(&trans->ctx.chain->list);
 +                              nft_chain_del(trans->ctx.chain);
                                nf_tables_unregister_hook(trans->ctx.net,
                                                          trans->ctx.table,
                                                          trans->ctx.chain);
        return 0;
  }
  
 +static void nf_tables_cleanup(struct net *net)
 +{
 +      nft_validate_state_update(net, NFT_VALIDATE_SKIP);
 +}
 +
  static bool nf_tables_valid_genid(struct net *net, u32 genid)
  {
        return net->nft.base_seq == genid;
@@@ -6566,7 -6134,6 +6567,7 @@@ static const struct nfnetlink_subsyste
        .cb             = nf_tables_cb,
        .commit         = nf_tables_commit,
        .abort          = nf_tables_abort,
 +      .cleanup        = nf_tables_cleanup,
        .valid_genid    = nf_tables_valid_genid,
  };
  
@@@ -6650,18 -6217,19 +6651,18 @@@ static int nf_tables_check_loops(const 
  
        list_for_each_entry(rule, &chain->rules, list) {
                nft_rule_for_each_expr(expr, last, rule) {
 -                      const struct nft_data *data = NULL;
 +                      struct nft_immediate_expr *priv;
 +                      const struct nft_data *data;
                        int err;
  
 -                      if (!expr->ops->validate)
 +                      if (strcmp(expr->ops->type->name, "immediate"))
                                continue;
  
 -                      err = expr->ops->validate(ctx, expr, &data);
 -                      if (err < 0)
 -                              return err;
 -
 -                      if (data == NULL)
 +                      priv = nft_expr_priv(expr);
 +                      if (priv->dreg != NFT_REG_VERDICT)
                                continue;
  
 +                      data = &priv->data;
                        switch (data->verdict.code) {
                        case NFT_JUMP:
                        case NFT_GOTO:
@@@ -6894,8 -6462,8 +6895,8 @@@ static int nft_verdict_init(const struc
        case NFT_GOTO:
                if (!tb[NFTA_VERDICT_CHAIN])
                        return -EINVAL;
 -              chain = nf_tables_chain_lookup(ctx->table,
 -                                             tb[NFTA_VERDICT_CHAIN], genmask);
 +              chain = nft_chain_lookup(ctx->table, tb[NFTA_VERDICT_CHAIN],
 +                                       genmask);
                if (IS_ERR(chain))
                        return PTR_ERR(chain);
                if (nft_is_base_chain(chain))
@@@ -7071,7 -6639,7 +7072,7 @@@ int __nft_release_basechain(struct nft_
                ctx->chain->use--;
                nf_tables_rule_release(ctx, rule);
        }
 -      list_del(&ctx->chain->list);
 +      nft_chain_del(ctx->chain);
        ctx->table->use--;
        nf_tables_chain_destroy(ctx);
  
@@@ -7123,11 -6691,11 +7124,11 @@@ static void __nft_release_tables(struc
                list_for_each_entry_safe(obj, ne, &table->objects, list) {
                        list_del(&obj->list);
                        table->use--;
 -                      nft_obj_destroy(obj);
 +                      nft_obj_destroy(&ctx, obj);
                }
                list_for_each_entry_safe(chain, nc, &table->chains, list) {
                        ctx.chain = chain;
 -                      list_del(&chain->list);
 +                      nft_chain_del(chain);
                        table->use--;
                        nf_tables_chain_destroy(&ctx);
                }
@@@ -7141,8 -6709,6 +7142,8 @@@ static int __net_init nf_tables_init_ne
        INIT_LIST_HEAD(&net->nft.tables);
        INIT_LIST_HEAD(&net->nft.commit_list);
        net->nft.base_seq = 1;
 +      net->nft.validate_state = NFT_VALIDATE_SKIP;
 +
        return 0;
  }
  
@@@ -22,7 -22,6 +22,7 @@@ struct nft_rbtree 
        struct rb_root          root;
        rwlock_t                lock;
        seqcount_t              count;
 +      struct delayed_work     gc_work;
  };
  
  struct nft_rbtree_elem {
@@@ -66,7 -65,7 +66,7 @@@ static bool __nft_rbtree_lookup(const s
                        parent = rcu_dereference_raw(parent->rb_left);
                        if (interval &&
                            nft_rbtree_equal(set, this, interval) &&
-                           nft_rbtree_interval_end(this) &&
+                           nft_rbtree_interval_end(rbe) &&
                            !nft_rbtree_interval_end(interval))
                                continue;
                        interval = rbe;
@@@ -266,7 -265,6 +266,7 @@@ static void nft_rbtree_activate(const s
        struct nft_rbtree_elem *rbe = elem->priv;
  
        nft_set_elem_change_active(net, set, &rbe->ext);
 +      nft_set_elem_clear_busy(&rbe->ext);
  }
  
  static bool nft_rbtree_flush(const struct net *net,
  {
        struct nft_rbtree_elem *rbe = priv;
  
 -      nft_set_elem_change_active(net, set, &rbe->ext);
 -      return true;
 +      if (!nft_set_elem_mark_busy(&rbe->ext) ||
 +          !nft_is_active(net, &rbe->ext)) {
 +              nft_set_elem_change_active(net, set, &rbe->ext);
 +              return true;
 +      }
 +      return false;
  }
  
  static void *nft_rbtree_deactivate(const struct net *net,
@@@ -353,62 -347,6 +353,62 @@@ cont
        read_unlock_bh(&priv->lock);
  }
  
 +static void nft_rbtree_gc(struct work_struct *work)
 +{
 +      struct nft_set_gc_batch *gcb = NULL;
 +      struct rb_node *node, *prev = NULL;
 +      struct nft_rbtree_elem *rbe;
 +      struct nft_rbtree *priv;
 +      struct nft_set *set;
 +      int i;
 +
 +      priv = container_of(work, struct nft_rbtree, gc_work.work);
 +      set  = nft_set_container_of(priv);
 +
 +      write_lock_bh(&priv->lock);
 +      write_seqcount_begin(&priv->count);
 +      for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
 +              rbe = rb_entry(node, struct nft_rbtree_elem, node);
 +
 +              if (nft_rbtree_interval_end(rbe)) {
 +                      prev = node;
 +                      continue;
 +              }
 +              if (!nft_set_elem_expired(&rbe->ext))
 +                      continue;
 +              if (nft_set_elem_mark_busy(&rbe->ext))
 +                      continue;
 +
 +              gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
 +              if (!gcb)
 +                      goto out;
 +
 +              atomic_dec(&set->nelems);
 +              nft_set_gc_batch_add(gcb, rbe);
 +
 +              if (prev) {
 +                      rbe = rb_entry(prev, struct nft_rbtree_elem, node);
 +                      atomic_dec(&set->nelems);
 +                      nft_set_gc_batch_add(gcb, rbe);
 +              }
 +              node = rb_next(node);
 +      }
 +out:
 +      if (gcb) {
 +              for (i = 0; i < gcb->head.cnt; i++) {
 +                      rbe = gcb->elems[i];
 +                      rb_erase(&rbe->node, &priv->root);
 +              }
 +      }
 +      write_seqcount_end(&priv->count);
 +      write_unlock_bh(&priv->lock);
 +
 +      nft_set_gc_batch_complete(gcb);
 +
 +      queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
 +                         nft_set_gc_interval(set));
 +}
 +
  static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[],
                                        const struct nft_set_desc *desc)
  {
@@@ -424,12 -362,6 +424,12 @@@ static int nft_rbtree_init(const struc
        rwlock_init(&priv->lock);
        seqcount_init(&priv->count);
        priv->root = RB_ROOT;
 +
 +      INIT_DEFERRABLE_WORK(&priv->gc_work, nft_rbtree_gc);
 +      if (set->flags & NFT_SET_TIMEOUT)
 +              queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
 +                                 nft_set_gc_interval(set));
 +
        return 0;
  }
  
@@@ -439,7 -371,6 +439,7 @@@ static void nft_rbtree_destroy(const st
        struct nft_rbtree_elem *rbe;
        struct rb_node *node;
  
 +      cancel_delayed_work_sync(&priv->gc_work);
        while ((node = priv->root.rb_node) != NULL) {
                rb_erase(node, &priv->root);
                rbe = rb_entry(node, struct nft_rbtree_elem, node);
@@@ -462,24 -393,28 +462,24 @@@ static bool nft_rbtree_estimate(const s
        return true;
  }
  
 -static struct nft_set_type nft_rbtree_type;
 -static struct nft_set_ops nft_rbtree_ops __read_mostly = {
 -      .type           = &nft_rbtree_type,
 -      .privsize       = nft_rbtree_privsize,
 -      .elemsize       = offsetof(struct nft_rbtree_elem, ext),
 -      .estimate       = nft_rbtree_estimate,
 -      .init           = nft_rbtree_init,
 -      .destroy        = nft_rbtree_destroy,
 -      .insert         = nft_rbtree_insert,
 -      .remove         = nft_rbtree_remove,
 -      .deactivate     = nft_rbtree_deactivate,
 -      .flush          = nft_rbtree_flush,
 -      .activate       = nft_rbtree_activate,
 -      .lookup         = nft_rbtree_lookup,
 -      .walk           = nft_rbtree_walk,
 -      .get            = nft_rbtree_get,
 -      .features       = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT,
 -};
 -
  static struct nft_set_type nft_rbtree_type __read_mostly = {
 -      .ops            = &nft_rbtree_ops,
        .owner          = THIS_MODULE,
 +      .features       = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT | NFT_SET_TIMEOUT,
 +      .ops            = {
 +              .privsize       = nft_rbtree_privsize,
 +              .elemsize       = offsetof(struct nft_rbtree_elem, ext),
 +              .estimate       = nft_rbtree_estimate,
 +              .init           = nft_rbtree_init,
 +              .destroy        = nft_rbtree_destroy,
 +              .insert         = nft_rbtree_insert,
 +              .remove         = nft_rbtree_remove,
 +              .deactivate     = nft_rbtree_deactivate,
 +              .flush          = nft_rbtree_flush,
 +              .activate       = nft_rbtree_activate,
 +              .lookup         = nft_rbtree_lookup,
 +              .walk           = nft_rbtree_walk,
 +              .get            = nft_rbtree_get,
 +      },
  };
  
  static int __init nft_rbtree_module_init(void)