Merge branch 'master' of /repos/git/net-next-2.6
authorPatrick McHardy <kaber@trash.net>
Wed, 10 Feb 2010 13:17:10 +0000 (14:17 +0100)
committerPatrick McHardy <kaber@trash.net>
Wed, 10 Feb 2010 13:17:10 +0000 (14:17 +0100)
Signed-off-by: Patrick McHardy <kaber@trash.net>
1  2 
include/net/netns/ipv4.h
net/bridge/netfilter/ebtables.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/reassembly.c
net/netfilter/ipvs/Kconfig
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_conntrack_netlink.c

diff --combined include/net/netns/ipv4.h
@@@ -37,11 -37,10 +37,12 @@@ struct netns_ipv4 
        struct xt_table         *iptable_mangle;
        struct xt_table         *iptable_raw;
        struct xt_table         *arptable_filter;
 +#ifdef CONFIG_SECURITY
        struct xt_table         *iptable_security;
 +#endif
        struct xt_table         *nat_table;
        struct hlist_head       *nat_bysource;
+       unsigned int            nat_htable_size;
        int                     nat_vmalloced;
  #endif
  
@@@ -561,14 -561,13 +561,14 @@@ ebt_get_udc_positions(struct ebt_entry 
  }
  
  static inline int
 -ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
 +ebt_cleanup_match(struct ebt_entry_match *m, struct net *net, unsigned int *i)
  {
        struct xt_mtdtor_param par;
  
        if (i && (*i)-- == 0)
                return 1;
  
 +      par.net       = net;
        par.match     = m->u.match;
        par.matchinfo = m->data;
        par.family    = NFPROTO_BRIDGE;
  }
  
  static inline int
 -ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
 +ebt_cleanup_watcher(struct ebt_entry_watcher *w, struct net *net, unsigned int *i)
  {
        struct xt_tgdtor_param par;
  
        if (i && (*i)-- == 0)
                return 1;
  
 +      par.net      = net;
        par.target   = w->u.watcher;
        par.targinfo = w->data;
        par.family   = NFPROTO_BRIDGE;
  }
  
  static inline int
 -ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
 +ebt_cleanup_entry(struct ebt_entry *e, struct net *net, unsigned int *cnt)
  {
        struct xt_tgdtor_param par;
        struct ebt_entry_target *t;
        /* we're done */
        if (cnt && (*cnt)-- == 0)
                return 1;
 -      EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
 -      EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
 +      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);
  
 +      par.net      = net;
        par.target   = t->u.target;
        par.targinfo = t->data;
        par.family   = NFPROTO_BRIDGE;
  }
  
  static inline int
 -ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
 +ebt_check_entry(struct ebt_entry *e,
 +   struct net *net,
 +   struct ebt_table_info *newinfo,
     const char *name, unsigned int *cnt,
     struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
  {
        }
        i = 0;
  
 +      mtpar.net       = tgpar.net       = net;
        mtpar.table     = tgpar.table     = name;
        mtpar.entryinfo = tgpar.entryinfo = e;
        mtpar.hook_mask = tgpar.hook_mask = hookmask;
        (*cnt)++;
        return 0;
  cleanup_watchers:
 -      EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
 +      EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, &j);
  cleanup_matches:
 -      EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
 +      EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, &i);
        return ret;
  }
  
@@@ -814,8 -808,7 +814,8 @@@ letscontinue
  }
  
  /* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
 -static int translate_table(char *name, struct ebt_table_info *newinfo)
 +static int translate_table(struct net *net, char *name,
 +                         struct ebt_table_info *newinfo)
  {
        unsigned int i, j, k, udc_cnt;
        int ret;
        /* used to know what we need to clean up if something goes wrong */
        i = 0;
        ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
 -         ebt_check_entry, newinfo, name, &i, cl_s, udc_cnt);
 +         ebt_check_entry, net, newinfo, name, &i, cl_s, udc_cnt);
        if (ret != 0) {
                EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
 -                 ebt_cleanup_entry, &i);
 +                                ebt_cleanup_entry, net, &i);
        }
        vfree(cl_s);
        return ret;
@@@ -1024,7 -1017,7 +1024,7 @@@ static int do_replace(struct net *net, 
        if (ret != 0)
                goto free_counterstmp;
  
 -      ret = translate_table(tmp.name, newinfo);
 +      ret = translate_table(net, tmp.name, newinfo);
  
        if (ret != 0)
                goto free_counterstmp;
  
        /* decrease module count and free resources */
        EBT_ENTRY_ITERATE(table->entries, table->entries_size,
 -         ebt_cleanup_entry, NULL);
 +                        ebt_cleanup_entry, net, NULL);
  
        vfree(table->entries);
        if (table->chainstack) {
@@@ -1094,7 -1087,7 +1094,7 @@@ free_unlock
        mutex_unlock(&ebt_mutex);
  free_iterate:
        EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
 -         ebt_cleanup_entry, NULL);
 +                        ebt_cleanup_entry, net, NULL);
  free_counterstmp:
        vfree(counterstmp);
        /* can be initialized in translate_table() */
@@@ -1161,7 -1154,7 +1161,7 @@@ ebt_register_table(struct net *net, con
                        newinfo->hook_entry[i] = p +
                                ((char *)repl->hook_entry[i] - repl->entries);
        }
 -      ret = translate_table(repl->name, newinfo);
 +      ret = translate_table(net, repl->name, newinfo);
        if (ret != 0) {
                BUGPRINT("Translate_table failed\n");
                goto free_chainstack;
@@@ -1211,7 -1204,7 +1211,7 @@@ out
        return ERR_PTR(ret);
  }
  
 -void ebt_unregister_table(struct ebt_table *table)
 +void ebt_unregister_table(struct net *net, struct ebt_table *table)
  {
        int i;
  
        list_del(&table->list);
        mutex_unlock(&ebt_mutex);
        EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
 -                        ebt_cleanup_entry, NULL);
 +                        ebt_cleanup_entry, net, NULL);
        if (table->private->nentries)
                module_put(table->me);
        vfree(table->private->entries);
@@@ -1413,6 -1406,9 +1413,9 @@@ static int do_ebt_set_ctl(struct sock *
  {
        int ret;
  
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
        switch(cmd) {
        case EBT_SO_SET_ENTRIES:
                ret = do_replace(sock_net(sk), user, len);
@@@ -1432,6 -1428,9 +1435,9 @@@ static int do_ebt_get_ctl(struct sock *
        struct ebt_replace tmp;
        struct ebt_table *t;
  
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
        if (copy_from_user(&tmp, user, sizeof(tmp)))
                return -EFAULT;
  
@@@ -553,14 -553,13 +553,14 @@@ mark_source_chains(struct xt_table_inf
  }
  
  static int
 -cleanup_match(struct ipt_entry_match *m, unsigned int *i)
 +cleanup_match(struct ipt_entry_match *m, struct net *net, unsigned int *i)
  {
        struct xt_mtdtor_param par;
  
        if (i && (*i)-- == 0)
                return 1;
  
 +      par.net       = net;
        par.match     = m->u.kernel.match;
        par.matchinfo = m->data;
        par.family    = NFPROTO_IPV4;
@@@ -638,11 -637,10 +638,11 @@@ err
        return ret;
  }
  
 -static int check_target(struct ipt_entry *e, const char *name)
 +static int check_target(struct ipt_entry *e, struct net *net, const char *name)
  {
        struct ipt_entry_target *t = ipt_get_target(e);
        struct xt_tgchk_param par = {
 +              .net       = net,
                .table     = name,
                .entryinfo = e,
                .target    = t->u.kernel.target,
  }
  
  static int
 -find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
 -               unsigned int *i)
 +find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
 +               unsigned int size, unsigned int *i)
  {
        struct ipt_entry_target *t;
        struct xt_target *target;
                return ret;
  
        j = 0;
 +      mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ip;
        mtpar.hook_mask = e->comefrom;
        }
        t->u.kernel.target = target;
  
 -      ret = check_target(e, name);
 +      ret = check_target(e, net, name);
        if (ret)
                goto err;
  
   err:
        module_put(t->u.kernel.target->me);
   cleanup_matches:
 -      IPT_MATCH_ITERATE(e, cleanup_match, &j);
 +      IPT_MATCH_ITERATE(e, cleanup_match, net, &j);
        return ret;
  }
  
@@@ -777,7 -774,7 +777,7 @@@ check_entry_size_and_hooks(struct ipt_e
  }
  
  static int
 -cleanup_entry(struct ipt_entry *e, unsigned int *i)
 +cleanup_entry(struct ipt_entry *e, struct net *net, unsigned int *i)
  {
        struct xt_tgdtor_param par;
        struct ipt_entry_target *t;
                return 1;
  
        /* Cleanup all matches */
 -      IPT_MATCH_ITERATE(e, cleanup_match, NULL);
 +      IPT_MATCH_ITERATE(e, cleanup_match, net, NULL);
        t = ipt_get_target(e);
  
 +      par.net      = net;
        par.target   = t->u.kernel.target;
        par.targinfo = t->data;
        par.family   = NFPROTO_IPV4;
  /* Checks and translates the user-supplied table segment (held in
     newinfo) */
  static int
 -translate_table(const char *name,
 +translate_table(struct net *net,
 +              const char *name,
                unsigned int valid_hooks,
                struct xt_table_info *newinfo,
                void *entry0,
        /* Finally, each sanity check must pass */
        i = 0;
        ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
 -                              find_check_entry, name, size, &i);
 +                              find_check_entry, net, name, size, &i);
  
        if (ret != 0) {
                IPT_ENTRY_ITERATE(entry0, newinfo->size,
 -                              cleanup_entry, &i);
 +                              cleanup_entry, net, &i);
                return ret;
        }
  
@@@ -1137,10 -1132,10 +1137,10 @@@ static int get_info(struct net *net, vo
        if (t && !IS_ERR(t)) {
                struct ipt_getinfo info;
                const struct xt_table_info *private = t->private;
  #ifdef CONFIG_COMPAT
+               struct xt_table_info tmp;
                if (compat) {
-                       struct xt_table_info tmp;
                        ret = compat_table_info(private, &tmp);
                        xt_compat_flush_offsets(AF_INET);
                        private = &tmp;
@@@ -1263,7 -1258,7 +1263,7 @@@ __do_replace(struct net *net, const cha
        /* Decrease module usage counts and free resource */
        loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
        IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
 -                        NULL);
 +                        net, NULL);
        xt_free_table_info(oldinfo);
        if (copy_to_user(counters_ptr, counters,
                         sizeof(struct xt_counters) * num_counters) != 0)
@@@ -1308,7 -1303,7 +1308,7 @@@ do_replace(struct net *net, void __use
                goto free_newinfo;
        }
  
 -      ret = translate_table(tmp.name, tmp.valid_hooks,
 +      ret = translate_table(net, tmp.name, tmp.valid_hooks,
                              newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
                              tmp.hook_entry, tmp.underflow);
        if (ret != 0)
        return 0;
  
   free_newinfo_untrans:
 -      IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
 +      IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
   free_newinfo:
        xt_free_table_info(newinfo);
        return ret;
@@@ -1660,7 -1655,7 +1660,7 @@@ compat_copy_entry_from_user(struct comp
  }
  
  static int
 -compat_check_entry(struct ipt_entry *e, const char *name,
 +compat_check_entry(struct ipt_entry *e, struct net *net, const char *name,
                                     unsigned int *i)
  {
        struct xt_mtchk_param mtpar;
        int ret;
  
        j = 0;
 +      mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ip;
        mtpar.hook_mask = e->comefrom;
        if (ret)
                goto cleanup_matches;
  
 -      ret = check_target(e, name);
 +      ret = check_target(e, net, name);
        if (ret)
                goto cleanup_matches;
  
        return 0;
  
   cleanup_matches:
 -      IPT_MATCH_ITERATE(e, cleanup_match, &j);
 +      IPT_MATCH_ITERATE(e, cleanup_match, net, &j);
        return ret;
  }
  
  static int
 -translate_compat_table(const char *name,
 +translate_compat_table(struct net *net,
 +                     const char *name,
                       unsigned int valid_hooks,
                       struct xt_table_info **pinfo,
                       void **pentry0,
  
        i = 0;
        ret = IPT_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
 -                              name, &i);
 +                              net, name, &i);
        if (ret) {
                j -= i;
                COMPAT_IPT_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
                                                  compat_release_entry, &j);
 -              IPT_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
 +              IPT_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, net, &i);
                xt_free_table_info(newinfo);
                return ret;
        }
@@@ -1840,7 -1833,7 +1840,7 @@@ compat_do_replace(struct net *net, voi
                goto free_newinfo;
        }
  
 -      ret = translate_compat_table(tmp.name, tmp.valid_hooks,
 +      ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
                                     &newinfo, &loc_cpu_entry, tmp.size,
                                     tmp.num_entries, tmp.hook_entry,
                                     tmp.underflow);
        return 0;
  
   free_newinfo_untrans:
 -      IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
 +      IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
   free_newinfo:
        xt_free_table_info(newinfo);
        return ret;
@@@ -2093,7 -2086,7 +2093,7 @@@ struct xt_table *ipt_register_table(str
        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
        memcpy(loc_cpu_entry, repl->entries, repl->size);
  
 -      ret = translate_table(table->name, table->valid_hooks,
 +      ret = translate_table(net, table->name, table->valid_hooks,
                              newinfo, loc_cpu_entry, repl->size,
                              repl->num_entries,
                              repl->hook_entry,
@@@ -2115,7 -2108,7 +2115,7 @@@ out
        return ERR_PTR(ret);
  }
  
 -void ipt_unregister_table(struct xt_table *table)
 +void ipt_unregister_table(struct net *net, struct xt_table *table)
  {
        struct xt_table_info *private;
        void *loc_cpu_entry;
  
        /* Decrease module usage counts and free resources */
        loc_cpu_entry = private->entries[raw_smp_processor_id()];
 -      IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
 +      IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, net, NULL);
        if (private->number > private->initial_entries)
                module_put(table_owner);
        xt_free_table_info(private);
@@@ -585,14 -585,13 +585,14 @@@ mark_source_chains(struct xt_table_inf
  }
  
  static int
 -cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
 +cleanup_match(struct ip6t_entry_match *m, struct net *net, unsigned int *i)
  {
        struct xt_mtdtor_param par;
  
        if (i && (*i)-- == 0)
                return 1;
  
 +      par.net       = net;
        par.match     = m->u.kernel.match;
        par.matchinfo = m->data;
        par.family    = NFPROTO_IPV6;
@@@ -669,11 -668,10 +669,11 @@@ err
        return ret;
  }
  
 -static int check_target(struct ip6t_entry *e, const char *name)
 +static int check_target(struct ip6t_entry *e, struct net *net, const char *name)
  {
        struct ip6t_entry_target *t = ip6t_get_target(e);
        struct xt_tgchk_param par = {
 +              .net       = net,
                .table     = name,
                .entryinfo = e,
                .target    = t->u.kernel.target,
  }
  
  static int
 -find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
 -               unsigned int *i)
 +find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
 +               unsigned int size, unsigned int *i)
  {
        struct ip6t_entry_target *t;
        struct xt_target *target;
                return ret;
  
        j = 0;
 +      mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ipv6;
        mtpar.hook_mask = e->comefrom;
        }
        t->u.kernel.target = target;
  
 -      ret = check_target(e, name);
 +      ret = check_target(e, net, name);
        if (ret)
                goto err;
  
   err:
        module_put(t->u.kernel.target->me);
   cleanup_matches:
 -      IP6T_MATCH_ITERATE(e, cleanup_match, &j);
 +      IP6T_MATCH_ITERATE(e, cleanup_match, net, &j);
        return ret;
  }
  
@@@ -809,7 -806,7 +809,7 @@@ check_entry_size_and_hooks(struct ip6t_
  }
  
  static int
 -cleanup_entry(struct ip6t_entry *e, unsigned int *i)
 +cleanup_entry(struct ip6t_entry *e, struct net *net, unsigned int *i)
  {
        struct xt_tgdtor_param par;
        struct ip6t_entry_target *t;
                return 1;
  
        /* Cleanup all matches */
 -      IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
 +      IP6T_MATCH_ITERATE(e, cleanup_match, net, NULL);
        t = ip6t_get_target(e);
  
 +      par.net      = net;
        par.target   = t->u.kernel.target;
        par.targinfo = t->data;
        par.family   = NFPROTO_IPV6;
  /* Checks and translates the user-supplied table segment (held in
     newinfo) */
  static int
 -translate_table(const char *name,
 +translate_table(struct net *net,
 +              const char *name,
                unsigned int valid_hooks,
                struct xt_table_info *newinfo,
                void *entry0,
        /* Finally, each sanity check must pass */
        i = 0;
        ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
 -                              find_check_entry, name, size, &i);
 +                              find_check_entry, net, name, size, &i);
  
        if (ret != 0) {
                IP6T_ENTRY_ITERATE(entry0, newinfo->size,
 -                                 cleanup_entry, &i);
 +                                 cleanup_entry, net, &i);
                return ret;
        }
  
@@@ -1169,10 -1164,10 +1169,10 @@@ static int get_info(struct net *net, vo
        if (t && !IS_ERR(t)) {
                struct ip6t_getinfo info;
                const struct xt_table_info *private = t->private;
  #ifdef CONFIG_COMPAT
+               struct xt_table_info tmp;
                if (compat) {
-                       struct xt_table_info tmp;
                        ret = compat_table_info(private, &tmp);
                        xt_compat_flush_offsets(AF_INET6);
                        private = &tmp;
@@@ -1296,7 -1291,7 +1296,7 @@@ __do_replace(struct net *net, const cha
        /* Decrease module usage counts and free resource */
        loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
        IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
 -                         NULL);
 +                         net, NULL);
        xt_free_table_info(oldinfo);
        if (copy_to_user(counters_ptr, counters,
                         sizeof(struct xt_counters) * num_counters) != 0)
@@@ -1341,7 -1336,7 +1341,7 @@@ do_replace(struct net *net, void __use
                goto free_newinfo;
        }
  
 -      ret = translate_table(tmp.name, tmp.valid_hooks,
 +      ret = translate_table(net, tmp.name, tmp.valid_hooks,
                              newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
                              tmp.hook_entry, tmp.underflow);
        if (ret != 0)
        return 0;
  
   free_newinfo_untrans:
 -      IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
 +      IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
   free_newinfo:
        xt_free_table_info(newinfo);
        return ret;
@@@ -1695,15 -1690,14 +1695,15 @@@ compat_copy_entry_from_user(struct comp
        return ret;
  }
  
 -static int compat_check_entry(struct ip6t_entry *e, const char *name,
 -                                   unsigned int *i)
 +static int compat_check_entry(struct ip6t_entry *e, struct net *net,
 +                            const char *name, unsigned int *i)
  {
        unsigned int j;
        int ret;
        struct xt_mtchk_param mtpar;
  
        j = 0;
 +      mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ipv6;
        mtpar.hook_mask = e->comefrom;
        if (ret)
                goto cleanup_matches;
  
 -      ret = check_target(e, name);
 +      ret = check_target(e, net, name);
        if (ret)
                goto cleanup_matches;
  
        return 0;
  
   cleanup_matches:
 -      IP6T_MATCH_ITERATE(e, cleanup_match, &j);
 +      IP6T_MATCH_ITERATE(e, cleanup_match, net, &j);
        return ret;
  }
  
  static int
 -translate_compat_table(const char *name,
 +translate_compat_table(struct net *net,
 +                     const char *name,
                       unsigned int valid_hooks,
                       struct xt_table_info **pinfo,
                       void **pentry0,
  
        i = 0;
        ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
 -                               name, &i);
 +                               net, name, &i);
        if (ret) {
                j -= i;
                COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
                                                   compat_release_entry, &j);
 -              IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
 +              IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, net, &i);
                xt_free_table_info(newinfo);
                return ret;
        }
@@@ -1875,7 -1868,7 +1875,7 @@@ compat_do_replace(struct net *net, voi
                goto free_newinfo;
        }
  
 -      ret = translate_compat_table(tmp.name, tmp.valid_hooks,
 +      ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
                                     &newinfo, &loc_cpu_entry, tmp.size,
                                     tmp.num_entries, tmp.hook_entry,
                                     tmp.underflow);
        return 0;
  
   free_newinfo_untrans:
 -      IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
 +      IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
   free_newinfo:
        xt_free_table_info(newinfo);
        return ret;
@@@ -2128,7 -2121,7 +2128,7 @@@ struct xt_table *ip6t_register_table(st
        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
        memcpy(loc_cpu_entry, repl->entries, repl->size);
  
 -      ret = translate_table(table->name, table->valid_hooks,
 +      ret = translate_table(net, table->name, table->valid_hooks,
                              newinfo, loc_cpu_entry, repl->size,
                              repl->num_entries,
                              repl->hook_entry,
@@@ -2149,7 -2142,7 +2149,7 @@@ out
        return ERR_PTR(ret);
  }
  
 -void ip6t_unregister_table(struct xt_table *table)
 +void ip6t_unregister_table(struct net *net, struct xt_table *table)
  {
        struct xt_table_info *private;
        void *loc_cpu_entry;
  
        /* Decrease module usage counts and free resources */
        loc_cpu_entry = private->entries[raw_smp_processor_id()];
 -      IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
 +      IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, net, NULL);
        if (private->number > private->initial_entries)
                module_put(table_owner);
        xt_free_table_info(private);
@@@ -45,6 -45,9 +45,6 @@@
  #include <linux/kernel.h>
  #include <linux/module.h>
  
 -#define NF_CT_FRAG6_HIGH_THRESH 262144 /* == 256*1024 */
 -#define NF_CT_FRAG6_LOW_THRESH 196608  /* == 192*1024 */
 -#define NF_CT_FRAG6_TIMEOUT IPV6_FRAG_TIMEOUT
  
  struct nf_ct_frag6_skb_cb
  {
@@@ -60,6 -63,7 +60,7 @@@ struct nf_ct_frag6_queu
        struct inet_frag_queue  q;
  
        __be32                  id;             /* fragment id          */
+       u32                     user;
        struct in6_addr         saddr;
        struct in6_addr         daddr;
  
@@@ -666,8 -670,8 +667,8 @@@ int nf_ct_frag6_init(void
        nf_frags.frag_expire = nf_ct_frag6_expire;
        nf_frags.secret_interval = 10 * 60 * HZ;
        nf_init_frags.timeout = IPV6_FRAG_TIMEOUT;
 -      nf_init_frags.high_thresh = 256 * 1024;
 -      nf_init_frags.low_thresh = 192 * 1024;
 +      nf_init_frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
 +      nf_init_frags.low_thresh = IPV6_FRAG_LOW_THRESH;
        inet_frags_init_net(&nf_init_frags);
        inet_frags_init(&nf_frags);
  
diff --combined net/ipv6/reassembly.c
@@@ -672,7 -672,7 +672,7 @@@ static struct ctl_table ip6_frags_ctl_t
        { }
  };
  
- static int ip6_frags_ns_sysctl_register(struct net *net)
+ static int __net_init ip6_frags_ns_sysctl_register(struct net *net)
  {
        struct ctl_table *table;
        struct ctl_table_header *hdr;
@@@ -702,7 -702,7 +702,7 @@@ err_alloc
        return -ENOMEM;
  }
  
- static void ip6_frags_ns_sysctl_unregister(struct net *net)
+ static void __net_exit ip6_frags_ns_sysctl_unregister(struct net *net)
  {
        struct ctl_table *table;
  
@@@ -745,10 -745,10 +745,10 @@@ static inline void ip6_frags_sysctl_unr
  }
  #endif
  
- static int ipv6_frags_init_net(struct net *net)
+ static int __net_init ipv6_frags_init_net(struct net *net)
  {
 -      net->ipv6.frags.high_thresh = 256 * 1024;
 -      net->ipv6.frags.low_thresh = 192 * 1024;
 +      net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
 +      net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
        net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT;
  
        inet_frags_init_net(&net->ipv6.frags);
        return ip6_frags_ns_sysctl_register(net);
  }
  
- static void ipv6_frags_exit_net(struct net *net)
+ static void __net_exit ipv6_frags_exit_net(struct net *net)
  {
        ip6_frags_ns_sysctl_unregister(net);
        inet_frags_exit_net(&net->ipv6.frags, &ip6_frags);
@@@ -68,10 -68,6 +68,10 @@@ config      IP_VS_TAB_BIT
          each hash entry uses 8 bytes, so you can estimate how much memory is
          needed for your box.
  
 +        You can overwrite this number setting conn_tab_bits module parameter
 +        or by appending ip_vs.conn_tab_bits=? to the kernel command line
 +        if IP VS was compiled built-in.
 +
  comment "IPVS transport protocol load balancing support"
  
  config        IP_VS_PROTO_TCP
@@@ -116,7 -112,8 +116,8 @@@ config     IP_VS_R
          module, choose M here. If unsure, say N.
   
  config        IP_VS_WRR
-         tristate "weighted round-robin scheduling" 
+       tristate "weighted round-robin scheduling"
+       select GCD
        ---help---
          The weighted robin-robin scheduling algorithm directs network
          connections to different real servers based on server weights
@@@ -1843,7 -1843,7 +1843,7 @@@ static int ip_vs_info_seq_show(struct s
        if (v == SEQ_START_TOKEN) {
                seq_printf(seq,
                        "IP Virtual Server version %d.%d.%d (size=%d)\n",
 -                      NVERSION(IP_VS_VERSION_CODE), IP_VS_CONN_TAB_SIZE);
 +                      NVERSION(IP_VS_VERSION_CODE), ip_vs_conn_tab_size);
                seq_puts(seq,
                         "Prot LocalAddress:Port Scheduler Flags\n");
                seq_puts(seq,
@@@ -2077,6 -2077,10 +2077,10 @@@ do_ip_vs_set_ctl(struct sock *sk, int c
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
  
+       if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_SET_MAX)
+               return -EINVAL;
+       if (len < 0 || len >  MAX_ARG_LEN)
+               return -EINVAL;
        if (len != set_arglen[SET_CMDID(cmd)]) {
                pr_err("set_ctl: len %u != %u\n",
                       len, set_arglen[SET_CMDID(cmd)]);
@@@ -2352,17 -2356,25 +2356,25 @@@ do_ip_vs_get_ctl(struct sock *sk, int c
  {
        unsigned char arg[128];
        int ret = 0;
+       unsigned int copylen;
  
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
  
+       if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_GET_MAX)
+               return -EINVAL;
        if (*len < get_arglen[GET_CMDID(cmd)]) {
                pr_err("get_ctl: len %u < %u\n",
                       *len, get_arglen[GET_CMDID(cmd)]);
                return -EINVAL;
        }
  
-       if (copy_from_user(arg, user, get_arglen[GET_CMDID(cmd)]) != 0)
+       copylen = get_arglen[GET_CMDID(cmd)];
+       if (copylen > 128)
+               return -EINVAL;
+       if (copy_from_user(arg, user, copylen) != 0)
                return -EFAULT;
  
        if (mutex_lock_interruptible(&__ip_vs_mutex))
                char buf[64];
  
                sprintf(buf, "IP Virtual Server version %d.%d.%d (size=%d)",
 -                      NVERSION(IP_VS_VERSION_CODE), IP_VS_CONN_TAB_SIZE);
 +                      NVERSION(IP_VS_VERSION_CODE), ip_vs_conn_tab_size);
                if (copy_to_user(user, buf, strlen(buf)+1) != 0) {
                        ret = -EFAULT;
                        goto out;
        {
                struct ip_vs_getinfo info;
                info.version = IP_VS_VERSION_CODE;
 -              info.size = IP_VS_CONN_TAB_SIZE;
 +              info.size = ip_vs_conn_tab_size;
                info.num_services = ip_vs_num_services;
                if (copy_to_user(user, &info, sizeof(info)) != 0)
                        ret = -EFAULT;
@@@ -3231,7 -3243,7 +3243,7 @@@ static int ip_vs_genl_get_cmd(struct sk
        case IPVS_CMD_GET_INFO:
                NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
                NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
 -                          IP_VS_CONN_TAB_SIZE);
 +                          ip_vs_conn_tab_size);
                break;
        }
  
@@@ -30,6 -30,7 +30,7 @@@
  #include <linux/netdevice.h>
  #include <linux/socket.h>
  #include <linux/mm.h>
+ #include <linux/nsproxy.h>
  #include <linux/rculist_nulls.h>
  
  #include <net/netfilter/nf_conntrack.h>
@@@ -63,8 -64,6 +64,6 @@@ EXPORT_SYMBOL_GPL(nf_conntrack_max)
  struct nf_conn nf_conntrack_untracked __read_mostly;
  EXPORT_SYMBOL_GPL(nf_conntrack_untracked);
  
- static struct kmem_cache *nf_conntrack_cachep __read_mostly;
  static int nf_conntrack_hash_rnd_initted;
  static unsigned int nf_conntrack_hash_rnd;
  
@@@ -86,9 -85,10 +85,10 @@@ static u_int32_t __hash_conntrack(cons
        return ((u64)h * size) >> 32;
  }
  
- static inline u_int32_t hash_conntrack(const struct nf_conntrack_tuple *tuple)
+ static inline u_int32_t hash_conntrack(const struct net *net,
+                                      const struct nf_conntrack_tuple *tuple)
  {
-       return __hash_conntrack(tuple, nf_conntrack_htable_size,
+       return __hash_conntrack(tuple, net->ct.htable_size,
                                nf_conntrack_hash_rnd);
  }
  
@@@ -296,7 -296,7 +296,7 @@@ __nf_conntrack_find(struct net *net, co
  {
        struct nf_conntrack_tuple_hash *h;
        struct hlist_nulls_node *n;
-       unsigned int hash = hash_conntrack(tuple);
+       unsigned int hash = hash_conntrack(net, tuple);
  
        /* Disable BHs the entire time since we normally need to disable them
         * at least once for the stats anyway.
@@@ -366,10 -366,11 +366,11 @@@ static void __nf_conntrack_hash_insert(
  
  void nf_conntrack_hash_insert(struct nf_conn *ct)
  {
+       struct net *net = nf_ct_net(ct);
        unsigned int hash, repl_hash;
  
-       hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-       repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+       hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+       repl_hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
  
        __nf_conntrack_hash_insert(ct, hash, repl_hash);
  }
@@@ -397,8 -398,8 +398,8 @@@ __nf_conntrack_confirm(struct sk_buff *
        if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
                return NF_ACCEPT;
  
-       hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-       repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+       hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+       repl_hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
  
        /* We're not in hash table, and we refuse to set up related
           connections for unconfirmed conns.  But packet copies and
@@@ -468,7 -469,7 +469,7 @@@ nf_conntrack_tuple_taken(const struct n
        struct net *net = nf_ct_net(ignored_conntrack);
        struct nf_conntrack_tuple_hash *h;
        struct hlist_nulls_node *n;
-       unsigned int hash = hash_conntrack(tuple);
+       unsigned int hash = hash_conntrack(net, tuple);
  
        /* Disable BHs the entire time since we need to disable them at
         * least once for the stats anyway.
@@@ -503,7 -504,7 +504,7 @@@ static noinline int early_drop(struct n
        int dropped = 0;
  
        rcu_read_lock();
-       for (i = 0; i < nf_conntrack_htable_size; i++) {
+       for (i = 0; i < net->ct.htable_size; i++) {
                hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash],
                                         hnnode) {
                        tmp = nf_ct_tuplehash_to_ctrack(h);
                if (cnt >= NF_CT_EVICTION_RANGE)
                        break;
  
-               hash = (hash + 1) % nf_conntrack_htable_size;
+               hash = (hash + 1) % net->ct.htable_size;
        }
        rcu_read_unlock();
  
@@@ -557,7 -558,7 +558,7 @@@ struct nf_conn *nf_conntrack_alloc(stru
  
        if (nf_conntrack_max &&
            unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) {
-               unsigned int hash = hash_conntrack(orig);
+               unsigned int hash = hash_conntrack(net, orig);
                if (!early_drop(net, hash)) {
                        atomic_dec(&net->ct.count);
                        if (net_ratelimit())
         * Do not use kmem_cache_zalloc(), as this cache uses
         * SLAB_DESTROY_BY_RCU.
         */
-       ct = kmem_cache_alloc(nf_conntrack_cachep, gfp);
+       ct = kmem_cache_alloc(net->ct.nf_conntrack_cachep, gfp);
        if (ct == NULL) {
                pr_debug("nf_conntrack_alloc: Can't alloc conntrack.\n");
                atomic_dec(&net->ct.count);
@@@ -611,14 -612,14 +612,14 @@@ void nf_conntrack_free(struct nf_conn *
        nf_ct_ext_destroy(ct);
        atomic_dec(&net->ct.count);
        nf_ct_ext_free(ct);
-       kmem_cache_free(nf_conntrack_cachep, ct);
+       kmem_cache_free(net->ct.nf_conntrack_cachep, ct);
  }
  EXPORT_SYMBOL_GPL(nf_conntrack_free);
  
  /* Allocate a new conntrack: we return -ENOMEM if classification
     failed due to stress.  Otherwise it really is unclassifiable. */
  static struct nf_conntrack_tuple_hash *
 -init_conntrack(struct net *net,
 +init_conntrack(struct net *net, struct nf_conn *tmpl,
               const struct nf_conntrack_tuple *tuple,
               struct nf_conntrack_l3proto *l3proto,
               struct nf_conntrack_l4proto *l4proto,
        struct nf_conn *ct;
        struct nf_conn_help *help;
        struct nf_conntrack_tuple repl_tuple;
 +      struct nf_conntrack_ecache *ecache;
        struct nf_conntrack_expect *exp;
  
        if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) {
        }
  
        nf_ct_acct_ext_add(ct, GFP_ATOMIC);
 -      nf_ct_ecache_ext_add(ct, GFP_ATOMIC);
 +
 +      ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL;
 +      nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0,
 +                               ecache ? ecache->expmask : 0,
 +                           GFP_ATOMIC);
  
        spin_lock_bh(&nf_conntrack_lock);
        exp = nf_ct_find_expectation(net, tuple);
                nf_conntrack_get(&ct->master->ct_general);
                NF_CT_STAT_INC(net, expect_new);
        } else {
 -              __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
 +              __nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC);
                NF_CT_STAT_INC(net, new);
        }
  
  
  /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
  static inline struct nf_conn *
 -resolve_normal_ct(struct net *net,
 +resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
                  struct sk_buff *skb,
                  unsigned int dataoff,
                  u_int16_t l3num,
        /* look for tuple match */
        h = nf_conntrack_find_get(net, &tuple);
        if (!h) {
 -              h = init_conntrack(net, &tuple, l3proto, l4proto, skb, dataoff);
 +              h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
 +                                 skb, dataoff);
                if (!h)
                        return NULL;
                if (IS_ERR(h))
@@@ -761,7 -756,7 +762,7 @@@ unsigned in
  nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
                struct sk_buff *skb)
  {
 -      struct nf_conn *ct;
 +      struct nf_conn *ct, *tmpl = NULL;
        enum ip_conntrack_info ctinfo;
        struct nf_conntrack_l3proto *l3proto;
        struct nf_conntrack_l4proto *l4proto;
        int set_reply = 0;
        int ret;
  
 -      /* Previously seen (loopback or untracked)?  Ignore. */
        if (skb->nfct) {
 -              NF_CT_STAT_INC_ATOMIC(net, ignore);
 -              return NF_ACCEPT;
 +              /* Previously seen (loopback or untracked)?  Ignore. */
 +              tmpl = (struct nf_conn *)skb->nfct;
 +              if (!nf_ct_is_template(tmpl)) {
 +                      NF_CT_STAT_INC_ATOMIC(net, ignore);
 +                      return NF_ACCEPT;
 +              }
 +              skb->nfct = NULL;
        }
  
        /* rcu_read_lock()ed by nf_hook_slow */
                pr_debug("not prepared to track yet or error occured\n");
                NF_CT_STAT_INC_ATOMIC(net, error);
                NF_CT_STAT_INC_ATOMIC(net, invalid);
 -              return -ret;
 +              ret = -ret;
 +              goto out;
        }
  
        l4proto = __nf_ct_l4proto_find(pf, protonum);
                if (ret <= 0) {
                        NF_CT_STAT_INC_ATOMIC(net, error);
                        NF_CT_STAT_INC_ATOMIC(net, invalid);
 -                      return -ret;
 +                      ret = -ret;
 +                      goto out;
                }
        }
  
 -      ct = resolve_normal_ct(net, skb, dataoff, pf, protonum,
 +      ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum,
                               l3proto, l4proto, &set_reply, &ctinfo);
        if (!ct) {
                /* Not valid part of a connection */
                NF_CT_STAT_INC_ATOMIC(net, invalid);
 -              return NF_ACCEPT;
 +              ret = NF_ACCEPT;
 +              goto out;
        }
  
        if (IS_ERR(ct)) {
                /* Too stressed to deal. */
                NF_CT_STAT_INC_ATOMIC(net, drop);
 -              return NF_DROP;
 +              ret = NF_DROP;
 +              goto out;
        }
  
        NF_CT_ASSERT(skb->nfct);
                NF_CT_STAT_INC_ATOMIC(net, invalid);
                if (ret == -NF_DROP)
                        NF_CT_STAT_INC_ATOMIC(net, drop);
 -              return -ret;
 +              ret = -ret;
 +              goto out;
        }
  
        if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
 -              nf_conntrack_event_cache(IPCT_STATUS, ct);
 +              nf_conntrack_event_cache(IPCT_REPLY, ct);
 +out:
 +      if (tmpl)
 +              nf_ct_put(tmpl);
  
        return ret;
  }
@@@ -882,7 -865,7 +883,7 @@@ void nf_conntrack_alter_reply(struct nf
                return;
  
        rcu_read_lock();
 -      __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
 +      __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
        rcu_read_unlock();
  }
  EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
@@@ -1032,7 -1015,7 +1033,7 @@@ get_next_corpse(struct net *net, int (*
        struct hlist_nulls_node *n;
  
        spin_lock_bh(&nf_conntrack_lock);
-       for (; *bucket < nf_conntrack_htable_size; (*bucket)++) {
+       for (; *bucket < net->ct.htable_size; (*bucket)++) {
                hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) {
                        ct = nf_ct_tuplehash_to_ctrack(h);
                        if (iter(ct, data))
@@@ -1131,9 -1114,12 +1132,12 @@@ static void nf_ct_release_dying_list(st
  
  static void nf_conntrack_cleanup_init_net(void)
  {
+       /* wait until all references to nf_conntrack_untracked are dropped */
+       while (atomic_read(&nf_conntrack_untracked.ct_general.use) > 1)
+               schedule();
        nf_conntrack_helper_fini();
        nf_conntrack_proto_fini();
-       kmem_cache_destroy(nf_conntrack_cachep);
  }
  
  static void nf_conntrack_cleanup_net(struct net *net)
                schedule();
                goto i_see_dead_people;
        }
-       /* wait until all references to nf_conntrack_untracked are dropped */
-       while (atomic_read(&nf_conntrack_untracked.ct_general.use) > 1)
-               schedule();
  
        nf_ct_free_hashtable(net->ct.hash, net->ct.hash_vmalloc,
-                            nf_conntrack_htable_size);
+                            net->ct.htable_size);
        nf_conntrack_ecache_fini(net);
        nf_conntrack_acct_fini(net);
        nf_conntrack_expect_fini(net);
+       kmem_cache_destroy(net->ct.nf_conntrack_cachep);
+       kfree(net->ct.slabname);
        free_percpu(net->ct.stat);
  }
  
@@@ -1208,10 -1193,12 +1211,12 @@@ int nf_conntrack_set_hashsize(const cha
  {
        int i, bucket, vmalloced, old_vmalloced;
        unsigned int hashsize, old_size;
        struct hlist_nulls_head *hash, *old_hash;
        struct nf_conntrack_tuple_hash *h;
  
+       if (current->nsproxy->net_ns != &init_net)
+               return -EOPNOTSUPP;
        /* On boot, we can set this without any fancy locking. */
        if (!nf_conntrack_htable_size)
                return param_set_uint(val, kp);
        if (!hash)
                return -ENOMEM;
  
-       /* We have to rehahs for the new table anyway, so we also can
-        * use a newrandom seed */
-       get_random_bytes(&rnd, sizeof(rnd));
        /* Lookups in the old hash might happen in parallel, which means we
         * might get false negatives during connection lookup. New connections
         * created because of a false negative won't make it into the hash
         * though since that required taking the lock.
         */
        spin_lock_bh(&nf_conntrack_lock);
-       for (i = 0; i < nf_conntrack_htable_size; i++) {
+       for (i = 0; i < init_net.ct.htable_size; i++) {
                while (!hlist_nulls_empty(&init_net.ct.hash[i])) {
                        h = hlist_nulls_entry(init_net.ct.hash[i].first,
                                        struct nf_conntrack_tuple_hash, hnnode);
                        hlist_nulls_del_rcu(&h->hnnode);
-                       bucket = __hash_conntrack(&h->tuple, hashsize, rnd);
+                       bucket = __hash_conntrack(&h->tuple, hashsize,
+                                                 nf_conntrack_hash_rnd);
                        hlist_nulls_add_head_rcu(&h->hnnode, &hash[bucket]);
                }
        }
-       old_size = nf_conntrack_htable_size;
+       old_size = init_net.ct.htable_size;
        old_vmalloced = init_net.ct.hash_vmalloc;
        old_hash = init_net.ct.hash;
  
-       nf_conntrack_htable_size = hashsize;
+       init_net.ct.htable_size = nf_conntrack_htable_size = hashsize;
        init_net.ct.hash_vmalloc = vmalloced;
        init_net.ct.hash = hash;
-       nf_conntrack_hash_rnd = rnd;
        spin_unlock_bh(&nf_conntrack_lock);
  
        nf_ct_free_hashtable(old_hash, old_vmalloced, old_size);
@@@ -1289,15 -1272,6 +1290,6 @@@ static int nf_conntrack_init_init_net(v
               NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
               nf_conntrack_max);
  
-       nf_conntrack_cachep = kmem_cache_create("nf_conntrack",
-                                               sizeof(struct nf_conn),
-                                               0, SLAB_DESTROY_BY_RCU, NULL);
-       if (!nf_conntrack_cachep) {
-               printk(KERN_ERR "Unable to create nf_conn slab cache\n");
-               ret = -ENOMEM;
-               goto err_cache;
-       }
        ret = nf_conntrack_proto_init();
        if (ret < 0)
                goto err_proto;
        if (ret < 0)
                goto err_helper;
  
+       /* Set up fake conntrack: to never be deleted, not in any hashes */
+ #ifdef CONFIG_NET_NS
+       nf_conntrack_untracked.ct_net = &init_net;
+ #endif
+       atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
+       /*  - and look it like as a confirmed connection */
+       set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);
        return 0;
  
  err_helper:
        nf_conntrack_proto_fini();
  err_proto:
-       kmem_cache_destroy(nf_conntrack_cachep);
- err_cache:
        return ret;
  }
  
@@@ -1334,7 -1314,24 +1332,24 @@@ static int nf_conntrack_init_net(struc
                ret = -ENOMEM;
                goto err_stat;
        }
-       net->ct.hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size,
+       net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%p", net);
+       if (!net->ct.slabname) {
+               ret = -ENOMEM;
+               goto err_slabname;
+       }
+       net->ct.nf_conntrack_cachep = kmem_cache_create(net->ct.slabname,
+                                                       sizeof(struct nf_conn), 0,
+                                                       SLAB_DESTROY_BY_RCU, NULL);
+       if (!net->ct.nf_conntrack_cachep) {
+               printk(KERN_ERR "Unable to create nf_conn slab cache\n");
+               ret = -ENOMEM;
+               goto err_cache;
+       }
+       net->ct.htable_size = nf_conntrack_htable_size;
+       net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size,
                                             &net->ct.hash_vmalloc, 1);
        if (!net->ct.hash) {
                ret = -ENOMEM;
        if (ret < 0)
                goto err_ecache;
  
-       /* Set up fake conntrack:
-           - to never be deleted, not in any hashes */
- #ifdef CONFIG_NET_NS
-       nf_conntrack_untracked.ct_net = &init_net;
- #endif
-       atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
-       /*  - and look it like as a confirmed connection */
-       set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);
        return 0;
  
  err_ecache:
@@@ -1368,8 -1356,12 +1374,12 @@@ err_acct
        nf_conntrack_expect_fini(net);
  err_expect:
        nf_ct_free_hashtable(net->ct.hash, net->ct.hash_vmalloc,
-                            nf_conntrack_htable_size);
+                            net->ct.htable_size);
  err_hash:
+       kmem_cache_destroy(net->ct.nf_conntrack_cachep);
+ err_cache:
+       kfree(net->ct.slabname);
+ err_slabname:
        free_percpu(net->ct.stat);
  err_stat:
        return ret;
@@@ -65,7 -65,7 +65,7 @@@ __nf_ct_helper_find(const struct nf_con
  }
  
  struct nf_conntrack_helper *
 -__nf_conntrack_helper_find_byname(const char *name)
 +__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
  {
        struct nf_conntrack_helper *h;
        struct hlist_node *n;
  
        for (i = 0; i < nf_ct_helper_hsize; i++) {
                hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) {
 -                      if (!strcmp(h->name, name))
 +                      if (!strcmp(h->name, name) &&
 +                          h->tuple.src.l3num == l3num &&
 +                          h->tuple.dst.protonum == protonum)
                                return h;
                }
        }
        return NULL;
  }
 -EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find_byname);
 +EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
 +
 +struct nf_conntrack_helper *
 +nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
 +{
 +      struct nf_conntrack_helper *h;
 +
 +      h = __nf_conntrack_helper_find(name, l3num, protonum);
 +#ifdef CONFIG_MODULES
 +      if (h == NULL) {
 +              if (request_module("nfct-helper-%s", name) == 0)
 +                      h = __nf_conntrack_helper_find(name, l3num, protonum);
 +      }
 +#endif
 +      if (h != NULL && !try_module_get(h->me))
 +              h = NULL;
 +
 +      return h;
 +}
 +EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);
  
  struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
  {
  }
  EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
  
 -int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags)
 +int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
 +                            gfp_t flags)
  {
 +      struct nf_conntrack_helper *helper = NULL;
 +      struct nf_conn_help *help;
        int ret = 0;
 -      struct nf_conntrack_helper *helper;
 -      struct nf_conn_help *help = nfct_help(ct);
  
 -      helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
 +      if (tmpl != NULL) {
 +              help = nfct_help(tmpl);
 +              if (help != NULL)
 +                      helper = help->helper;
 +      }
 +
 +      help = nfct_help(ct);
 +      if (helper == NULL)
 +              helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
        if (helper == NULL) {
                if (help)
                        rcu_assign_pointer(help->helper, NULL);
@@@ -222,7 -192,7 +222,7 @@@ static void __nf_conntrack_helper_unreg
        /* Get rid of expecteds, set helpers to NULL. */
        hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
                unhelp(h, me);
-       for (i = 0; i < nf_conntrack_htable_size; i++) {
+       for (i = 0; i < net->ct.htable_size; i++) {
                hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
                        unhelp(h, me);
        }
@@@ -30,7 -30,6 +30,7 @@@
  
  #include <linux/netfilter.h>
  #include <net/netlink.h>
 +#include <net/sock.h>
  #include <net/netfilter/nf_conntrack.h>
  #include <net/netfilter/nf_conntrack_core.h>
  #include <net/netfilter/nf_conntrack_expect.h>
@@@ -457,7 -456,6 +457,7 @@@ ctnetlink_nlmsg_size(const struct nf_co
  static int
  ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
  {
 +      struct net *net;
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfmsg;
        struct nlattr *nest_parms;
        } else
                return 0;
  
 -      if (!item->report && !nfnetlink_has_listeners(group))
 +      net = nf_ct_net(ct);
 +      if (!item->report && !nfnetlink_has_listeners(net, group))
                return 0;
  
        skb = nlmsg_new(ctnetlink_nlmsg_size(ct), GFP_ATOMIC);
        rcu_read_unlock();
  
        nlmsg_end(skb, nlh);
 -      err = nfnetlink_send(skb, item->pid, group, item->report, GFP_ATOMIC);
 +      err = nfnetlink_send(skb, net, item->pid, group, item->report,
 +                           GFP_ATOMIC);
        if (err == -ENOBUFS || err == -EAGAIN)
                return -ENOBUFS;
  
@@@ -575,7 -571,7 +575,7 @@@ nla_put_failure
  nlmsg_failure:
        kfree_skb(skb);
  errout:
 -      nfnetlink_set_err(0, group, -ENOBUFS);
 +      nfnetlink_set_err(net, 0, group, -ENOBUFS);
        return 0;
  }
  #endif /* CONFIG_NF_CONNTRACK_EVENTS */
@@@ -590,7 -586,6 +590,7 @@@ static int ctnetlink_done(struct netlin
  static int
  ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
  {
 +      struct net *net = sock_net(skb->sk);
        struct nf_conn *ct, *last;
        struct nf_conntrack_tuple_hash *h;
        struct hlist_nulls_node *n;
  
        rcu_read_lock();
        last = (struct nf_conn *)cb->args[1];
-       for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
 -      for (; cb->args[0] < init_net.ct.htable_size; cb->args[0]++) {
++      for (; cb->args[0] < net->ct.htable_size; cb->args[0]++) {
  restart:
 -              hlist_nulls_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]],
 +              hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[cb->args[0]],
                                         hnnode) {
                        if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
                                continue;
@@@ -773,7 -768,6 +773,7 @@@ ctnetlink_del_conntrack(struct sock *ct
                        const struct nlmsghdr *nlh,
                        const struct nlattr * const cda[])
  {
 +      struct net *net = sock_net(ctnl);
        struct nf_conntrack_tuple_hash *h;
        struct nf_conntrack_tuple tuple;
        struct nf_conn *ct;
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
        else {
                /* Flush the whole table */
 -              nf_conntrack_flush_report(&init_net,
 +              nf_conntrack_flush_report(net,
                                         NETLINK_CB(skb).pid,
                                         nlmsg_report(nlh));
                return 0;
        if (err < 0)
                return err;
  
 -      h = nf_conntrack_find_get(&init_net, &tuple);
 +      h = nf_conntrack_find_get(net, &tuple);
        if (!h)
                return -ENOENT;
  
@@@ -834,7 -828,6 +834,7 @@@ ctnetlink_get_conntrack(struct sock *ct
                        const struct nlmsghdr *nlh,
                        const struct nlattr * const cda[])
  {
 +      struct net *net = sock_net(ctnl);
        struct nf_conntrack_tuple_hash *h;
        struct nf_conntrack_tuple tuple;
        struct nf_conn *ct;
        if (err < 0)
                return err;
  
 -      h = nf_conntrack_find_get(&init_net, &tuple);
 +      h = nf_conntrack_find_get(net, &tuple);
        if (!h)
                return -ENOENT;
  
@@@ -1001,8 -994,7 +1001,8 @@@ ctnetlink_change_helper(struct nf_conn 
                return 0;
        }
  
 -      helper = __nf_conntrack_helper_find_byname(helpname);
 +      helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
 +                                          nf_ct_protonum(ct));
        if (helper == NULL) {
  #ifdef CONFIG_MODULES
                spin_unlock_bh(&nf_conntrack_lock);
                }
  
                spin_lock_bh(&nf_conntrack_lock);
 -              helper = __nf_conntrack_helper_find_byname(helpname);
 +              helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
 +                                                  nf_ct_protonum(ct));
                if (helper)
                        return -EAGAIN;
  #endif
@@@ -1184,8 -1175,7 +1184,8 @@@ ctnetlink_change_conntrack(struct nf_co
  }
  
  static struct nf_conn *
 -ctnetlink_create_conntrack(const struct nlattr * const cda[],
 +ctnetlink_create_conntrack(struct net *net,
 +                         const struct nlattr * const cda[],
                           struct nf_conntrack_tuple *otuple,
                           struct nf_conntrack_tuple *rtuple,
                           u8 u3)
        int err = -EINVAL;
        struct nf_conntrack_helper *helper;
  
 -      ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_ATOMIC);
 +      ct = nf_conntrack_alloc(net, otuple, rtuple, GFP_ATOMIC);
        if (IS_ERR(ct))
                return ERR_PTR(-ENOMEM);
  
                if (err < 0)
                        goto err2;
  
 -              helper = __nf_conntrack_helper_find_byname(helpname);
 +              helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
 +                                                  nf_ct_protonum(ct));
                if (helper == NULL) {
                        rcu_read_unlock();
  #ifdef CONFIG_MODULES
                        }
  
                        rcu_read_lock();
 -                      helper = __nf_conntrack_helper_find_byname(helpname);
 +                      helper = __nf_conntrack_helper_find(helpname,
 +                                                          nf_ct_l3num(ct),
 +                                                          nf_ct_protonum(ct));
                        if (helper) {
                                err = -EAGAIN;
                                goto err2;
                }
        } else {
                /* try an implicit helper assignation */
 -              err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
 +              err = __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
                if (err < 0)
                        goto err2;
        }
        }
  
        nf_ct_acct_ext_add(ct, GFP_ATOMIC);
 -      nf_ct_ecache_ext_add(ct, GFP_ATOMIC);
 +      nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
  
  #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
                if (err < 0)
                        goto err2;
  
 -              master_h = nf_conntrack_find_get(&init_net, &master);
 +              master_h = nf_conntrack_find_get(net, &master);
                if (master_h == NULL) {
                        err = -ENOENT;
                        goto err2;
@@@ -1326,7 -1313,6 +1326,7 @@@ ctnetlink_new_conntrack(struct sock *ct
                        const struct nlmsghdr *nlh,
                        const struct nlattr * const cda[])
  {
 +      struct net *net = sock_net(ctnl);
        struct nf_conntrack_tuple otuple, rtuple;
        struct nf_conntrack_tuple_hash *h = NULL;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
  
        spin_lock_bh(&nf_conntrack_lock);
        if (cda[CTA_TUPLE_ORIG])
 -              h = __nf_conntrack_find(&init_net, &otuple);
 +              h = __nf_conntrack_find(net, &otuple);
        else if (cda[CTA_TUPLE_REPLY])
 -              h = __nf_conntrack_find(&init_net, &rtuple);
 +              h = __nf_conntrack_find(net, &rtuple);
  
        if (h == NULL) {
                err = -ENOENT;
                        struct nf_conn *ct;
                        enum ip_conntrack_events events;
  
 -                      ct = ctnetlink_create_conntrack(cda, &otuple,
 +                      ct = ctnetlink_create_conntrack(net, cda, &otuple,
                                                        &rtuple, u3);
                        if (IS_ERR(ct)) {
                                err = PTR_ERR(ct);
                        else
                                events = IPCT_NEW;
  
 -                      nf_conntrack_eventmask_report((1 << IPCT_STATUS) |
 +                      nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
 +                                                    (1 << IPCT_ASSURED) |
                                                      (1 << IPCT_HELPER) |
                                                      (1 << IPCT_PROTOINFO) |
                                                      (1 << IPCT_NATSEQADJ) |
                if (err == 0) {
                        nf_conntrack_get(&ct->ct_general);
                        spin_unlock_bh(&nf_conntrack_lock);
 -                      nf_conntrack_eventmask_report((1 << IPCT_STATUS) |
 +                      nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
 +                                                    (1 << IPCT_ASSURED) |
                                                      (1 << IPCT_HELPER) |
                                                      (1 << IPCT_PROTOINFO) |
                                                      (1 << IPCT_NATSEQADJ) |
@@@ -1453,8 -1437,9 +1453,9 @@@ ctnetlink_exp_dump_mask(struct sk_buff 
        struct nlattr *nest_parms;
  
        memset(&m, 0xFF, sizeof(m));
-       m.src.u.all = mask->src.u.all;
        memcpy(&m.src.u3, &mask->src.u3, sizeof(m.src.u3));
+       m.src.u.all = mask->src.u.all;
+       m.dst.protonum = tuple->dst.protonum;
  
        nest_parms = nla_nest_start(skb, CTA_EXPECT_MASK | NLA_F_NESTED);
        if (!nest_parms)
@@@ -1541,10 -1526,9 +1542,10 @@@ nla_put_failure
  static int
  ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item)
  {
 +      struct nf_conntrack_expect *exp = item->exp;
 +      struct net *net = nf_ct_exp_net(exp);
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfmsg;
 -      struct nf_conntrack_expect *exp = item->exp;
        struct sk_buff *skb;
        unsigned int type;
        int flags = 0;
                return 0;
  
        if (!item->report &&
 -          !nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW))
 +          !nfnetlink_has_listeners(net, NFNLGRP_CONNTRACK_EXP_NEW))
                return 0;
  
        skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        rcu_read_unlock();
  
        nlmsg_end(skb, nlh);
 -      nfnetlink_send(skb, item->pid, NFNLGRP_CONNTRACK_EXP_NEW,
 +      nfnetlink_send(skb, net, item->pid, NFNLGRP_CONNTRACK_EXP_NEW,
                       item->report, GFP_ATOMIC);
        return 0;
  
@@@ -1589,7 -1573,7 +1590,7 @@@ nla_put_failure
  nlmsg_failure:
        kfree_skb(skb);
  errout:
 -      nfnetlink_set_err(0, 0, -ENOBUFS);
 +      nfnetlink_set_err(net, 0, 0, -ENOBUFS);
        return 0;
  }
  #endif
@@@ -1603,7 -1587,7 +1604,7 @@@ static int ctnetlink_exp_done(struct ne
  static int
  ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
  {
 -      struct net *net = &init_net;
 +      struct net *net = sock_net(skb->sk);
        struct nf_conntrack_expect *exp, *last;
        struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
        struct hlist_node *n;
@@@ -1656,7 -1640,6 +1657,7 @@@ ctnetlink_get_expect(struct sock *ctnl
                     const struct nlmsghdr *nlh,
                     const struct nlattr * const cda[])
  {
 +      struct net *net = sock_net(ctnl);
        struct nf_conntrack_tuple tuple;
        struct nf_conntrack_expect *exp;
        struct sk_buff *skb2;
        if (err < 0)
                return err;
  
 -      exp = nf_ct_expect_find_get(&init_net, &tuple);
 +      exp = nf_ct_expect_find_get(net, &tuple);
        if (!exp)
                return -ENOENT;
  
@@@ -1718,9 -1701,9 +1719,9 @@@ ctnetlink_del_expect(struct sock *ctnl
                     const struct nlmsghdr *nlh,
                     const struct nlattr * const cda[])
  {
 +      struct net *net = sock_net(ctnl);
        struct nf_conntrack_expect *exp;
        struct nf_conntrack_tuple tuple;
 -      struct nf_conntrack_helper *h;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        struct hlist_node *n, *next;
        u_int8_t u3 = nfmsg->nfgen_family;
                        return err;
  
                /* bump usage count to 2 */
 -              exp = nf_ct_expect_find_get(&init_net, &tuple);
 +              exp = nf_ct_expect_find_get(net, &tuple);
                if (!exp)
                        return -ENOENT;
  
  
                /* delete all expectations for this helper */
                spin_lock_bh(&nf_conntrack_lock);
 -              h = __nf_conntrack_helper_find_byname(name);
 -              if (!h) {
 -                      spin_unlock_bh(&nf_conntrack_lock);
 -                      return -EOPNOTSUPP;
 -              }
                for (i = 0; i < nf_ct_expect_hsize; i++) {
                        hlist_for_each_entry_safe(exp, n, next,
 -                                                &init_net.ct.expect_hash[i],
 +                                                &net->ct.expect_hash[i],
                                                  hnode) {
                                m_help = nfct_help(exp->master);
 -                              if (m_help->helper == h
 -                                  && del_timer(&exp->timeout)) {
 +                              if (!strcmp(m_help->helper->name, name) &&
 +                                  del_timer(&exp->timeout)) {
                                        nf_ct_unlink_expect(exp);
                                        nf_ct_expect_put(exp);
                                }
                spin_lock_bh(&nf_conntrack_lock);
                for (i = 0; i < nf_ct_expect_hsize; i++) {
                        hlist_for_each_entry_safe(exp, n, next,
 -                                                &init_net.ct.expect_hash[i],
 +                                                &net->ct.expect_hash[i],
                                                  hnode) {
                                if (del_timer(&exp->timeout)) {
                                        nf_ct_unlink_expect(exp);
@@@ -1796,8 -1784,7 +1797,8 @@@ ctnetlink_change_expect(struct nf_connt
  }
  
  static int
 -ctnetlink_create_expect(const struct nlattr * const cda[], u_int8_t u3,
 +ctnetlink_create_expect(struct net *net, const struct nlattr * const cda[],
 +                      u_int8_t u3,
                        u32 pid, int report)
  {
        struct nf_conntrack_tuple tuple, mask, master_tuple;
                return err;
  
        /* Look for master conntrack of this expectation */
 -      h = nf_conntrack_find_get(&init_net, &master_tuple);
 +      h = nf_conntrack_find_get(net, &master_tuple);
        if (!h)
                return -ENOENT;
        ct = nf_ct_tuplehash_to_ctrack(h);
@@@ -1859,7 -1846,6 +1860,7 @@@ ctnetlink_new_expect(struct sock *ctnl
                     const struct nlmsghdr *nlh,
                     const struct nlattr * const cda[])
  {
 +      struct net *net = sock_net(ctnl);
        struct nf_conntrack_tuple tuple;
        struct nf_conntrack_expect *exp;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
                return err;
  
        spin_lock_bh(&nf_conntrack_lock);
 -      exp = __nf_ct_expect_find(&init_net, &tuple);
 +      exp = __nf_ct_expect_find(net, &tuple);
  
        if (!exp) {
                spin_unlock_bh(&nf_conntrack_lock);
                err = -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_CREATE) {
 -                      err = ctnetlink_create_expect(cda,
 +                      err = ctnetlink_create_expect(net, cda,
                                                      u3,
                                                      NETLINK_CB(skb).pid,
                                                      nlmsg_report(nlh));