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>
64 files changed:
include/linux/netfilter.h
include/linux/netfilter/Kbuild
include/linux/netfilter/nf_conntrack_common.h
include/linux/netfilter/nfnetlink.h
include/linux/netfilter/x_tables.h
include/linux/netfilter/xt_CT.h [new file with mode: 0644]
include/linux/netfilter_bridge/ebtables.h
include/linux/netfilter_ipv4/ip_tables.h
include/linux/netfilter_ipv6/ip6_tables.h
include/net/ip_vs.h
include/net/ipv6.h
include/net/net_namespace.h
include/net/netfilter/nf_conntrack.h
include/net/netfilter/nf_conntrack_ecache.h
include/net/netfilter/nf_conntrack_helper.h
include/net/netns/ipv4.h
include/net/netns/ipv6.h
net/bridge/netfilter/ebtable_broute.c
net/bridge/netfilter/ebtable_filter.c
net/bridge/netfilter/ebtable_nat.c
net/bridge/netfilter/ebtables.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_CLUSTERIP.c
net/ipv4/netfilter/iptable_filter.c
net/ipv4/netfilter/iptable_mangle.c
net/ipv4/netfilter/iptable_raw.c
net/ipv4/netfilter/iptable_security.c
net/ipv4/netfilter/nf_defrag_ipv4.c
net/ipv4/netfilter/nf_nat_ftp.c
net/ipv4/netfilter/nf_nat_rule.c
net/ipv4/netfilter/nf_nat_snmp_basic.c
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/ip6table_filter.c
net/ipv6/netfilter/ip6table_mangle.c
net/ipv6/netfilter/ip6table_raw.c
net/ipv6/netfilter/ip6table_security.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/reassembly.c
net/netfilter/Kconfig
net/netfilter/Makefile
net/netfilter/ipvs/Kconfig
net/netfilter/ipvs/ip_vs_conn.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_ftp.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_proto_sctp.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netfilter/nf_conntrack_proto_udp.c
net/netfilter/nf_conntrack_proto_udplite.c
net/netfilter/nfnetlink.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue.c
net/netfilter/xt_CT.c [new file with mode: 0644]
net/netfilter/xt_NFQUEUE.c
net/netfilter/xt_RATEEST.c
net/netfilter/xt_TCPMSS.c
net/netfilter/xt_connlimit.c
net/netfilter/xt_hashlimit.c
net/netfilter/xt_osf.c
net/netfilter/xt_recent.c

index 48c5496..78f33d2 100644 (file)
@@ -114,15 +114,17 @@ struct nf_sockopt_ops {
        int set_optmin;
        int set_optmax;
        int (*set)(struct sock *sk, int optval, void __user *user, unsigned int len);
+#ifdef CONFIG_COMPAT
        int (*compat_set)(struct sock *sk, int optval,
                        void __user *user, unsigned int len);
-
+#endif
        int get_optmin;
        int get_optmax;
        int (*get)(struct sock *sk, int optval, void __user *user, int *len);
+#ifdef CONFIG_COMPAT
        int (*compat_get)(struct sock *sk, int optval,
                        void __user *user, int *len);
-
+#endif
        /* Use the module struct to lock set/get code in place */
        struct module *owner;
 };
@@ -222,11 +224,12 @@ int nf_setsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt,
                  unsigned int len);
 int nf_getsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt,
                  int *len);
-
+#ifdef CONFIG_COMPAT
 int compat_nf_setsockopt(struct sock *sk, u_int8_t pf, int optval,
                char __user *opt, unsigned int len);
 int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, int optval,
                char __user *opt, int *len);
+#endif
 
 /* Call this before modifying an existing packet: ensures it is
    modifiable and linear to the point you care about (writable_len).
index 2aea503..a5a63e4 100644 (file)
@@ -6,6 +6,7 @@ header-y += nfnetlink_queue.h
 header-y += xt_CLASSIFY.h
 header-y += xt_CONNMARK.h
 header-y += xt_CONNSECMARK.h
+header-y += xt_CT.h
 header-y += xt_DSCP.h
 header-y += xt_LED.h
 header-y += xt_MARK.h
index a374787..c608677 100644 (file)
@@ -72,6 +72,28 @@ enum ip_conntrack_status {
        /* Connection has fixed timeout. */
        IPS_FIXED_TIMEOUT_BIT = 10,
        IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+
+       /* Conntrack is a template */
+       IPS_TEMPLATE_BIT = 11,
+       IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT),
+};
+
+/* Connection tracking event types */
+enum ip_conntrack_events {
+       IPCT_NEW,               /* new conntrack */
+       IPCT_RELATED,           /* related conntrack */
+       IPCT_DESTROY,           /* destroyed conntrack */
+       IPCT_REPLY,             /* connection has seen two-way traffic */
+       IPCT_ASSURED,           /* connection status has changed to assured */
+       IPCT_PROTOINFO,         /* protocol information has changed */
+       IPCT_HELPER,            /* new helper has been set */
+       IPCT_MARK,              /* new mark has been set */
+       IPCT_NATSEQADJ,         /* NAT is doing sequence adjustment */
+       IPCT_SECMARK,           /* new security mark has been set */
+};
+
+enum ip_conntrack_expect_events {
+       IPEXP_NEW,              /* new expectation */
 };
 
 #ifdef __KERNEL__
index 49d321f..5392386 100644 (file)
@@ -73,11 +73,11 @@ struct nfnetlink_subsystem {
 extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
 extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
 
-extern int nfnetlink_has_listeners(unsigned int group);
-extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, 
+extern int nfnetlink_has_listeners(struct net *net, unsigned int group);
+extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group,
                          int echo, gfp_t flags);
-extern void nfnetlink_set_err(u32 pid, u32 group, int error);
-extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags);
+extern void nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
+extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
 
 extern void nfnl_lock(void);
 extern void nfnl_unlock(void);
index 378f27a..365fabe 100644 (file)
@@ -205,6 +205,7 @@ struct xt_match_param {
  * @hook_mask: via which hooks the new rule is reachable
  */
 struct xt_mtchk_param {
+       struct net *net;
        const char *table;
        const void *entryinfo;
        const struct xt_match *match;
@@ -215,6 +216,7 @@ struct xt_mtchk_param {
 
 /* Match destructor parameters */
 struct xt_mtdtor_param {
+       struct net *net;
        const struct xt_match *match;
        void *matchinfo;
        u_int8_t family;
@@ -247,6 +249,7 @@ struct xt_target_param {
  * Other fields see above.
  */
 struct xt_tgchk_param {
+       struct net *net;
        const char *table;
        const void *entryinfo;
        const struct xt_target *target;
@@ -257,6 +260,7 @@ struct xt_tgchk_param {
 
 /* Target destructor parameters */
 struct xt_tgdtor_param {
+       struct net *net;
        const struct xt_target *target;
        void *targinfo;
        u_int8_t family;
@@ -281,11 +285,11 @@ struct xt_match {
 
        /* Called when entry of this type deleted. */
        void (*destroy)(const struct xt_mtdtor_param *);
-
+#ifdef CONFIG_COMPAT
        /* Called when userspace align differs from kernel space one */
        void (*compat_from_user)(void *dst, void *src);
        int (*compat_to_user)(void __user *dst, void *src);
-
+#endif
        /* Set this to THIS_MODULE if you are a module, otherwise NULL */
        struct module *me;
 
@@ -294,7 +298,9 @@ struct xt_match {
 
        const char *table;
        unsigned int matchsize;
+#ifdef CONFIG_COMPAT
        unsigned int compatsize;
+#endif
        unsigned int hooks;
        unsigned short proto;
 
@@ -321,17 +327,19 @@ struct xt_target {
 
        /* Called when entry of this type deleted. */
        void (*destroy)(const struct xt_tgdtor_param *);
-
+#ifdef CONFIG_COMPAT
        /* Called when userspace align differs from kernel space one */
        void (*compat_from_user)(void *dst, void *src);
        int (*compat_to_user)(void __user *dst, void *src);
-
+#endif
        /* Set this to THIS_MODULE if you are a module, otherwise NULL */
        struct module *me;
 
        const char *table;
        unsigned int targetsize;
+#ifdef CONFIG_COMPAT
        unsigned int compatsize;
+#endif
        unsigned int hooks;
        unsigned short proto;
 
diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h
new file mode 100644 (file)
index 0000000..7fd0eff
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _XT_CT_H
+#define _XT_CT_H
+
+#define XT_CT_NOTRACK  0x1
+
+struct xt_ct_target_info {
+       u_int16_t       flags;
+       u_int16_t       __unused;
+       u_int32_t       ct_events;
+       u_int32_t       exp_events;
+       char            helper[16];
+
+       /* Used internally by the kernel */
+       struct nf_conn  *ct __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CT_H */
index 3cc40c1..1c6f0c5 100644 (file)
@@ -289,7 +289,7 @@ struct ebt_table {
                     ~(__alignof__(struct ebt_replace)-1))
 extern struct ebt_table *ebt_register_table(struct net *net,
                                            const struct ebt_table *table);
-extern void ebt_unregister_table(struct ebt_table *table);
+extern void ebt_unregister_table(struct net *net, struct ebt_table *table);
 extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff *skb,
    const struct net_device *in, const struct net_device *out,
    struct ebt_table *table);
index 27b3f58..8d1f273 100644 (file)
@@ -242,7 +242,7 @@ extern void ipt_init(void) __init;
 extern struct xt_table *ipt_register_table(struct net *net,
                                           const struct xt_table *table,
                                           const struct ipt_replace *repl);
-extern void ipt_unregister_table(struct xt_table *table);
+extern void ipt_unregister_table(struct net *net, struct xt_table *table);
 
 /* Standard entry. */
 struct ipt_standard {
index b31050d..d2952d2 100644 (file)
@@ -300,7 +300,7 @@ extern void ip6t_init(void) __init;
 extern struct xt_table *ip6t_register_table(struct net *net,
                                            const struct xt_table *table,
                                            const struct ip6t_replace *repl);
-extern void ip6t_unregister_table(struct xt_table *table);
+extern void ip6t_unregister_table(struct net *net, struct xt_table *table);
 extern unsigned int ip6t_do_table(struct sk_buff *skb,
                                  unsigned int hook,
                                  const struct net_device *in,
index 8dc3296..a816c37 100644 (file)
 #include <linux/ipv6.h>                        /* for struct ipv6hdr */
 #include <net/ipv6.h>                  /* for ipv6_addr_copy */
 
+
+/* Connections' size value needed by ip_vs_ctl.c */
+extern int ip_vs_conn_tab_size;
+
+
 struct ip_vs_iphdr {
        int len;
        __u8 protocol;
@@ -592,17 +597,6 @@ extern void ip_vs_init_hash_table(struct list_head *table, int rows);
  *     (from ip_vs_conn.c)
  */
 
-/*
- *     IPVS connection entry hash table
- */
-#ifndef CONFIG_IP_VS_TAB_BITS
-#define CONFIG_IP_VS_TAB_BITS   12
-#endif
-
-#define IP_VS_CONN_TAB_BITS    CONFIG_IP_VS_TAB_BITS
-#define IP_VS_CONN_TAB_SIZE     (1 << IP_VS_CONN_TAB_BITS)
-#define IP_VS_CONN_TAB_MASK     (IP_VS_CONN_TAB_SIZE - 1)
-
 enum {
        IP_VS_DIR_INPUT = 0,
        IP_VS_DIR_OUTPUT,
index ccab594..299bbf5 100644 (file)
@@ -246,6 +246,8 @@ extern int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb);
 int ip6_frag_nqueues(struct net *net);
 int ip6_frag_mem(struct net *net);
 
+#define IPV6_FRAG_HIGH_THRESH  262144          /* == 256*1024 */
+#define IPV6_FRAG_LOW_THRESH   196608          /* == 192*1024 */
 #define IPV6_FRAG_TIMEOUT      (60*HZ)         /* 60 seconds */
 
 extern int __ipv6_addr_type(const struct in6_addr *addr);
index f307e13..82b7be4 100644 (file)
@@ -81,6 +81,8 @@ struct net {
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        struct netns_ct         ct;
 #endif
+       struct sock             *nfnl;
+       struct sock             *nfnl_stash;
 #endif
 #ifdef CONFIG_XFRM
        struct netns_xfrm       xfrm;
index a0904ad..5043d61 100644 (file)
@@ -272,6 +272,11 @@ nf_conntrack_alloc(struct net *net,
                   const struct nf_conntrack_tuple *repl,
                   gfp_t gfp);
 
+static inline int nf_ct_is_template(const struct nf_conn *ct)
+{
+       return test_bit(IPS_TEMPLATE_BIT, &ct->status);
+}
+
 /* It's confirmed if it is, or has been in the hash table. */
 static inline int nf_ct_is_confirmed(struct nf_conn *ct)
 {
index 475facc..96ba5f7 100644 (file)
 #include <linux/netfilter/nf_conntrack_tuple_common.h>
 #include <net/netfilter/nf_conntrack_extend.h>
 
-/* Connection tracking event types */
-enum ip_conntrack_events {
-       IPCT_NEW                = 0,    /* new conntrack */
-       IPCT_RELATED            = 1,    /* related conntrack */
-       IPCT_DESTROY            = 2,    /* destroyed conntrack */
-       IPCT_STATUS             = 3,    /* status has changed */
-       IPCT_PROTOINFO          = 4,    /* protocol information has changed */
-       IPCT_HELPER             = 5,    /* new helper has been set */
-       IPCT_MARK               = 6,    /* new mark has been set */
-       IPCT_NATSEQADJ          = 7,    /* NAT is doing sequence adjustment */
-       IPCT_SECMARK            = 8,    /* new security mark has been set */
-};
-
-enum ip_conntrack_expect_events {
-       IPEXP_NEW               = 0,    /* new expectation */
-};
-
 struct nf_conntrack_ecache {
-       unsigned long cache;            /* bitops want long */
-       unsigned long missed;           /* missed events */
-       u32 pid;                        /* netlink pid of destroyer */
+       unsigned long cache;    /* bitops want long */
+       unsigned long missed;   /* missed events */
+       u16 ctmask;             /* bitmask of ct events to be delivered */
+       u16 expmask;            /* bitmask of expect events to be delivered */
+       u32 pid;                /* netlink pid of destroyer */
 };
 
 static inline struct nf_conntrack_ecache *
@@ -42,14 +27,24 @@ nf_ct_ecache_find(const struct nf_conn *ct)
 }
 
 static inline struct nf_conntrack_ecache *
-nf_ct_ecache_ext_add(struct nf_conn *ct, gfp_t gfp)
+nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp)
 {
        struct net *net = nf_ct_net(ct);
+       struct nf_conntrack_ecache *e;
 
-       if (!net->ct.sysctl_events)
+       if (!ctmask && !expmask && net->ct.sysctl_events) {
+               ctmask = ~0;
+               expmask = ~0;
+       }
+       if (!ctmask && !expmask)
                return NULL;
 
-       return nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp);
+       e = nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp);
+       if (e) {
+               e->ctmask  = ctmask;
+               e->expmask = expmask;
+       }
+       return e;
 };
 
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
@@ -82,6 +77,9 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
        if (e == NULL)
                return;
 
+       if (!(e->ctmask & (1 << event)))
+               return;
+
        set_bit(event, &e->cache);
 }
 
@@ -92,7 +90,6 @@ nf_conntrack_eventmask_report(unsigned int eventmask,
                              int report)
 {
        int ret = 0;
-       struct net *net = nf_ct_net(ct);
        struct nf_ct_event_notifier *notify;
        struct nf_conntrack_ecache *e;
 
@@ -101,9 +98,6 @@ nf_conntrack_eventmask_report(unsigned int eventmask,
        if (notify == NULL)
                goto out_unlock;
 
-       if (!net->ct.sysctl_events)
-               goto out_unlock;
-
        e = nf_ct_ecache_find(ct);
        if (e == NULL)
                goto out_unlock;
@@ -117,6 +111,9 @@ nf_conntrack_eventmask_report(unsigned int eventmask,
                /* This is a resent of a destroy event? If so, skip missed */
                unsigned long missed = e->pid ? 0 : e->missed;
 
+               if (!((eventmask | missed) & e->ctmask))
+                       goto out_unlock;
+
                ret = notify->fcn(eventmask | missed, &item);
                if (unlikely(ret < 0 || missed)) {
                        spin_lock_bh(&ct->lock);
@@ -172,18 +169,19 @@ nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
                          u32 pid,
                          int report)
 {
-       struct net *net = nf_ct_exp_net(exp);
        struct nf_exp_event_notifier *notify;
+       struct nf_conntrack_ecache *e;
 
        rcu_read_lock();
        notify = rcu_dereference(nf_expect_event_cb);
        if (notify == NULL)
                goto out_unlock;
 
-       if (!net->ct.sysctl_events)
+       e = nf_ct_ecache_find(exp->master);
+       if (e == NULL)
                goto out_unlock;
 
-       {
+       if (e->expmask & (1 << event)) {
                struct nf_exp_event item = {
                        .exp    = exp,
                        .pid    = pid,
index d015de9..32c305d 100644 (file)
@@ -40,14 +40,18 @@ struct nf_conntrack_helper {
 };
 
 extern struct nf_conntrack_helper *
-__nf_conntrack_helper_find_byname(const char *name);
+__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum);
+
+extern struct nf_conntrack_helper *
+nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum);
 
 extern int nf_conntrack_helper_register(struct nf_conntrack_helper *);
 extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *);
 
 extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp);
 
-extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags);
+extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
+                                    gfp_t flags);
 
 extern void nf_ct_helper_destroy(struct nf_conn *ct);
 
index 9a4b8b7..2764994 100644 (file)
@@ -37,7 +37,9 @@ 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;
index dfeb2d7..1f11ebc 100644 (file)
@@ -36,8 +36,10 @@ struct netns_ipv6 {
        struct xt_table         *ip6table_filter;
        struct xt_table         *ip6table_mangle;
        struct xt_table         *ip6table_raw;
+#ifdef CONFIG_SECURITY
        struct xt_table         *ip6table_security;
 #endif
+#endif
        struct rt6_info         *ip6_null_entry;
        struct rt6_statistics   *rt6_stats;
        struct timer_list       ip6_fib_timer;
index d32ab13..ae3f106 100644 (file)
@@ -71,7 +71,7 @@ static int __net_init broute_net_init(struct net *net)
 
 static void __net_exit broute_net_exit(struct net *net)
 {
-       ebt_unregister_table(net->xt.broute_table);
+       ebt_unregister_table(net, net->xt.broute_table);
 }
 
 static struct pernet_operations broute_net_ops = {
index 60b1a6c..42e6bd0 100644 (file)
@@ -107,7 +107,7 @@ static int __net_init frame_filter_net_init(struct net *net)
 
 static void __net_exit frame_filter_net_exit(struct net *net)
 {
-       ebt_unregister_table(net->xt.frame_filter);
+       ebt_unregister_table(net, net->xt.frame_filter);
 }
 
 static struct pernet_operations frame_filter_net_ops = {
index 4a98804..6dc2f87 100644 (file)
@@ -107,7 +107,7 @@ static int __net_init frame_nat_net_init(struct net *net)
 
 static void __net_exit frame_nat_net_exit(struct net *net)
 {
-       ebt_unregister_table(net->xt.frame_nat);
+       ebt_unregister_table(net, net->xt.frame_nat);
 }
 
 static struct pernet_operations frame_nat_net_ops = {
index 0b7f262..208f4e3 100644 (file)
@@ -561,13 +561,14 @@ ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
 }
 
 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;
@@ -578,13 +579,14 @@ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
 }
 
 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;
@@ -595,7 +597,7 @@ ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
 }
 
 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;
@@ -605,10 +607,11 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
        /* 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;
@@ -619,7 +622,9 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
 }
 
 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)
 {
@@ -671,6 +676,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        }
        i = 0;
 
+       mtpar.net       = tgpar.net       = net;
        mtpar.table     = tgpar.table     = name;
        mtpar.entryinfo = tgpar.entryinfo = e;
        mtpar.hook_mask = tgpar.hook_mask = hookmask;
@@ -726,9 +732,9 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        (*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;
 }
 
@@ -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;
@@ -917,10 +924,10 @@ static int translate_table(char *name, struct ebt_table_info *newinfo)
        /* 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;
@@ -1017,7 +1024,7 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
        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;
@@ -1070,7 +1077,7 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
 
        /* 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) {
@@ -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() */
@@ -1154,7 +1161,7 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table)
                        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;
@@ -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;
 
@@ -1216,7 +1223,7 @@ void ebt_unregister_table(struct ebt_table *table)
        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);
index 3ce53cf..5bf7de1 100644 (file)
@@ -553,13 +553,14 @@ mark_source_chains(struct xt_table_info *newinfo,
 }
 
 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;
@@ -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,
@@ -661,8 +663,8 @@ static int check_target(struct ipt_entry *e, const char *name)
 }
 
 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;
@@ -675,6 +677,7 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
                return ret;
 
        j = 0;
+       mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ip;
        mtpar.hook_mask = e->comefrom;
@@ -695,7 +698,7 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
        }
        t->u.kernel.target = target;
 
-       ret = check_target(e, name);
+       ret = check_target(e, net, name);
        if (ret)
                goto err;
 
@@ -704,7 +707,7 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
  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;
 }
 
@@ -774,7 +777,7 @@ check_entry_size_and_hooks(struct ipt_entry *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;
@@ -783,9 +786,10 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i)
                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;
@@ -798,7 +802,8 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i)
 /* 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,
@@ -860,11 +865,11 @@ translate_table(const char *name,
        /* 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;
        }
 
@@ -1258,7 +1263,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
        /* 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)
@@ -1303,7 +1308,7 @@ do_replace(struct net *net, void __user *user, unsigned int len)
                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)
@@ -1318,7 +1323,7 @@ do_replace(struct net *net, void __user *user, unsigned int len)
        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;
@@ -1655,7 +1660,7 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
 }
 
 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;
@@ -1663,6 +1668,7 @@ compat_check_entry(struct ipt_entry *e, const char *name,
        int ret;
 
        j = 0;
+       mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ip;
        mtpar.hook_mask = e->comefrom;
@@ -1671,7 +1677,7 @@ compat_check_entry(struct ipt_entry *e, const char *name,
        if (ret)
                goto cleanup_matches;
 
-       ret = check_target(e, name);
+       ret = check_target(e, net, name);
        if (ret)
                goto cleanup_matches;
 
@@ -1679,12 +1685,13 @@ compat_check_entry(struct ipt_entry *e, const char *name,
        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,
@@ -1773,12 +1780,12 @@ translate_compat_table(const char *name,
 
        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;
        }
@@ -1833,7 +1840,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
                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);
@@ -1849,7 +1856,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
        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;
@@ -2086,7 +2093,7 @@ struct xt_table *ipt_register_table(struct net *net,
        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,
@@ -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;
@@ -2118,7 +2125,7 @@ void ipt_unregister_table(struct xt_table *table)
 
        /* 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);
index 40ca2d2..0886f96 100644 (file)
@@ -560,8 +560,7 @@ struct clusterip_seq_position {
 
 static void *clusterip_seq_start(struct seq_file *s, loff_t *pos)
 {
-       const struct proc_dir_entry *pde = s->private;
-       struct clusterip_config *c = pde->data;
+       struct clusterip_config *c = s->private;
        unsigned int weight;
        u_int32_t local_nodes;
        struct clusterip_seq_position *idx;
@@ -632,10 +631,9 @@ static int clusterip_proc_open(struct inode *inode, struct file *file)
 
        if (!ret) {
                struct seq_file *sf = file->private_data;
-               struct proc_dir_entry *pde = PDE(inode);
-               struct clusterip_config *c = pde->data;
+               struct clusterip_config *c = PDE(inode)->data;
 
-               sf->private = pde;
+               sf->private = c;
 
                clusterip_config_get(c);
        }
@@ -645,8 +643,7 @@ static int clusterip_proc_open(struct inode *inode, struct file *file)
 
 static int clusterip_proc_release(struct inode *inode, struct file *file)
 {
-       struct proc_dir_entry *pde = PDE(inode);
-       struct clusterip_config *c = pde->data;
+       struct clusterip_config *c = PDE(inode)->data;
        int ret;
 
        ret = seq_release(inode, file);
@@ -660,10 +657,9 @@ static int clusterip_proc_release(struct inode *inode, struct file *file)
 static ssize_t clusterip_proc_write(struct file *file, const char __user *input,
                                size_t size, loff_t *ofs)
 {
+       struct clusterip_config *c = PDE(file->f_path.dentry->d_inode)->data;
 #define PROC_WRITELEN  10
        char buffer[PROC_WRITELEN+1];
-       const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
-       struct clusterip_config *c = pde->data;
        unsigned long nodenum;
 
        if (copy_from_user(buffer, input, PROC_WRITELEN))
index df566cb..dee90eb 100644 (file)
@@ -138,7 +138,7 @@ static int __net_init iptable_filter_net_init(struct net *net)
 
 static void __net_exit iptable_filter_net_exit(struct net *net)
 {
-       ipt_unregister_table(net->ipv4.iptable_filter);
+       ipt_unregister_table(net, net->ipv4.iptable_filter);
 }
 
 static struct pernet_operations iptable_filter_net_ops = {
index fae78c3..e07bf24 100644 (file)
@@ -208,7 +208,7 @@ static int __net_init iptable_mangle_net_init(struct net *net)
 
 static void __net_exit iptable_mangle_net_exit(struct net *net)
 {
-       ipt_unregister_table(net->ipv4.iptable_mangle);
+       ipt_unregister_table(net, net->ipv4.iptable_mangle);
 }
 
 static struct pernet_operations iptable_mangle_net_ops = {
index 993edc2..40f2b9f 100644 (file)
@@ -100,7 +100,7 @@ static int __net_init iptable_raw_net_init(struct net *net)
 
 static void __net_exit iptable_raw_net_exit(struct net *net)
 {
-       ipt_unregister_table(net->ipv4.iptable_raw);
+       ipt_unregister_table(net, net->ipv4.iptable_raw);
 }
 
 static struct pernet_operations iptable_raw_net_ops = {
index 3bd3d63..7ce2366 100644 (file)
@@ -138,7 +138,7 @@ static int __net_init iptable_security_net_init(struct net *net)
 
 static void __net_exit iptable_security_net_exit(struct net *net)
 {
-       ipt_unregister_table(net->ipv4.iptable_security);
+       ipt_unregister_table(net, net->ipv4.iptable_security);
 }
 
 static struct pernet_operations iptable_security_net_ops = {
index 331ead3..f6f4668 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/netfilter_bridge.h>
 #include <linux/netfilter_ipv4.h>
 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
+#include <net/netfilter/nf_conntrack.h>
 
 /* Returns new sk_buff, or NULL */
 static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
@@ -59,7 +60,7 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
 #if !defined(CONFIG_NF_NAT) && !defined(CONFIG_NF_NAT_MODULE)
        /* Previously seen (loopback)?  Ignore.  Do this before
           fragment check. */
-       if (skb->nfct)
+       if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct))
                return NF_ACCEPT;
 #endif
 #endif
index a1d5d58..86e0e84 100644 (file)
@@ -27,76 +27,29 @@ MODULE_ALIAS("ip_nat_ftp");
 
 /* FIXME: Time out? --RR */
 
-static int
-mangle_rfc959_packet(struct sk_buff *skb,
-                    __be32 newip,
-                    u_int16_t port,
-                    unsigned int matchoff,
-                    unsigned int matchlen,
-                    struct nf_conn *ct,
-                    enum ip_conntrack_info ctinfo)
+static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type,
+                             char *buffer, size_t buflen,
+                             __be32 addr, u16 port)
 {
-       char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
-
-       sprintf(buffer, "%u,%u,%u,%u,%u,%u",
-               NIPQUAD(newip), port>>8, port&0xFF);
-
-       pr_debug("calling nf_nat_mangle_tcp_packet\n");
-
-       return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
-                                       matchlen, buffer, strlen(buffer));
-}
-
-/* |1|132.235.1.2|6275| */
-static int
-mangle_eprt_packet(struct sk_buff *skb,
-                  __be32 newip,
-                  u_int16_t port,
-                  unsigned int matchoff,
-                  unsigned int matchlen,
-                  struct nf_conn *ct,
-                  enum ip_conntrack_info ctinfo)
-{
-       char buffer[sizeof("|1|255.255.255.255|65535|")];
-
-       sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
-
-       pr_debug("calling nf_nat_mangle_tcp_packet\n");
-
-       return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
-                                       matchlen, buffer, strlen(buffer));
-}
-
-/* |1|132.235.1.2|6275| */
-static int
-mangle_epsv_packet(struct sk_buff *skb,
-                  __be32 newip,
-                  u_int16_t port,
-                  unsigned int matchoff,
-                  unsigned int matchlen,
-                  struct nf_conn *ct,
-                  enum ip_conntrack_info ctinfo)
-{
-       char buffer[sizeof("|||65535|")];
-
-       sprintf(buffer, "|||%u|", port);
-
-       pr_debug("calling nf_nat_mangle_tcp_packet\n");
+       switch (type) {
+       case NF_CT_FTP_PORT:
+       case NF_CT_FTP_PASV:
+               return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u",
+                               ((unsigned char *)&addr)[0],
+                               ((unsigned char *)&addr)[1],
+                               ((unsigned char *)&addr)[2],
+                               ((unsigned char *)&addr)[3],
+                               port >> 8,
+                               port & 0xFF);
+       case NF_CT_FTP_EPRT:
+               return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port);
+       case NF_CT_FTP_EPSV:
+               return snprintf(buffer, buflen, "|||%u|", port);
+       }
 
-       return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
-                                       matchlen, buffer, strlen(buffer));
+       return 0;
 }
 
-static int (*mangle[])(struct sk_buff *, __be32, u_int16_t,
-                      unsigned int, unsigned int, struct nf_conn *,
-                      enum ip_conntrack_info)
-= {
-       [NF_CT_FTP_PORT] = mangle_rfc959_packet,
-       [NF_CT_FTP_PASV] = mangle_rfc959_packet,
-       [NF_CT_FTP_EPRT] = mangle_eprt_packet,
-       [NF_CT_FTP_EPSV] = mangle_epsv_packet
-};
-
 /* So, this packet has hit the connection tracking matching code.
    Mangle it, and change the expectation to match the new version. */
 static unsigned int nf_nat_ftp(struct sk_buff *skb,
@@ -110,6 +63,8 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb,
        u_int16_t port;
        int dir = CTINFO2DIR(ctinfo);
        struct nf_conn *ct = exp->master;
+       char buffer[sizeof("|1|255.255.255.255|65535|")];
+       unsigned int buflen;
 
        pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
 
@@ -132,11 +87,21 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb,
        if (port == 0)
                return NF_DROP;
 
-       if (!mangle[type](skb, newip, port, matchoff, matchlen, ct, ctinfo)) {
-               nf_ct_unexpect_related(exp);
-               return NF_DROP;
-       }
+       buflen = nf_nat_ftp_fmt_cmd(type, buffer, sizeof(buffer), newip, port);
+       if (!buflen)
+               goto out;
+
+       pr_debug("calling nf_nat_mangle_tcp_packet\n");
+
+       if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
+                                     matchlen, buffer, buflen))
+               goto out;
+
        return NF_ACCEPT;
+
+out:
+       nf_ct_unexpect_related(exp);
+       return NF_DROP;
 }
 
 static void __exit nf_nat_ftp_fini(void)
index 9e81e0d..85da34f 100644 (file)
@@ -195,7 +195,7 @@ static int __net_init nf_nat_rule_net_init(struct net *net)
 
 static void __net_exit nf_nat_rule_net_exit(struct net *net)
 {
-       ipt_unregister_table(net->ipv4.nat_table);
+       ipt_unregister_table(net, net->ipv4.nat_table);
 }
 
 static struct pernet_operations nf_nat_rule_net_ops = {
index d9521f6..0b9c7ce 100644 (file)
@@ -1038,7 +1038,7 @@ static int snmp_parse_mangle(unsigned char *msg,
        unsigned int cls, con, tag, vers, pdutype;
        struct asn1_ctx ctx;
        struct asn1_octstr comm;
-       struct snmp_object **obj;
+       struct snmp_object *obj;
 
        if (debug > 1)
                hex_dump(msg, len);
@@ -1148,43 +1148,34 @@ static int snmp_parse_mangle(unsigned char *msg,
        if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
                return 0;
 
-       obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC);
-       if (obj == NULL) {
-               if (net_ratelimit())
-                       printk(KERN_WARNING "OOM in bsalg(%d)\n", __LINE__);
-               return 0;
-       }
-
        while (!asn1_eoc_decode(&ctx, eoc)) {
                unsigned int i;
 
-               if (!snmp_object_decode(&ctx, obj)) {
-                       if (*obj) {
-                               kfree((*obj)->id);
-                               kfree(*obj);
+               if (!snmp_object_decode(&ctx, &obj)) {
+                       if (obj) {
+                               kfree(obj->id);
+                               kfree(obj);
                        }
-                       kfree(obj);
                        return 0;
                }
 
                if (debug > 1) {
                        printk(KERN_DEBUG "bsalg: object: ");
-                       for (i = 0; i < (*obj)->id_len; i++) {
+                       for (i = 0; i < obj->id_len; i++) {
                                if (i > 0)
                                        printk(".");
-                               printk("%lu", (*obj)->id[i]);
+                               printk("%lu", obj->id[i]);
                        }
-                       printk(": type=%u\n", (*obj)->type);
+                       printk(": type=%u\n", obj->type);
 
                }
 
-               if ((*obj)->type == SNMP_IPADDR)
+               if (obj->type == SNMP_IPADDR)
                        mangle_address(ctx.begin, ctx.pointer - 4 , map, check);
 
-               kfree((*obj)->id);
-               kfree(*obj);
+               kfree(obj->id);
+               kfree(obj);
        }
-       kfree(obj);
 
        if (!asn1_eoc_decode(&ctx, eoc))
                return 0;
index 8a7e0f5..4332f45 100644 (file)
@@ -585,13 +585,14 @@ mark_source_chains(struct xt_table_info *newinfo,
 }
 
 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;
@@ -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,
@@ -693,8 +695,8 @@ static int check_target(struct ip6t_entry *e, const char *name)
 }
 
 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;
@@ -707,6 +709,7 @@ find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
                return ret;
 
        j = 0;
+       mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ipv6;
        mtpar.hook_mask = e->comefrom;
@@ -727,7 +730,7 @@ find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
        }
        t->u.kernel.target = target;
 
-       ret = check_target(e, name);
+       ret = check_target(e, net, name);
        if (ret)
                goto err;
 
@@ -736,7 +739,7 @@ find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
  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;
 }
 
@@ -806,7 +809,7 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
 }
 
 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;
@@ -815,9 +818,10 @@ cleanup_entry(struct ip6t_entry *e, unsigned int *i)
                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;
@@ -830,7 +834,8 @@ cleanup_entry(struct ip6t_entry *e, unsigned int *i)
 /* 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,
@@ -892,11 +897,11 @@ translate_table(const char *name,
        /* 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;
        }
 
@@ -1291,7 +1296,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
        /* 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)
@@ -1336,7 +1341,7 @@ do_replace(struct net *net, void __user *user, unsigned int len)
                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)
@@ -1351,7 +1356,7 @@ do_replace(struct net *net, void __user *user, unsigned int len)
        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;
@@ -1690,14 +1695,15 @@ compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
        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;
@@ -1706,7 +1712,7 @@ static int compat_check_entry(struct ip6t_entry *e, const char *name,
        if (ret)
                goto cleanup_matches;
 
-       ret = check_target(e, name);
+       ret = check_target(e, net, name);
        if (ret)
                goto cleanup_matches;
 
@@ -1714,12 +1720,13 @@ static int compat_check_entry(struct ip6t_entry *e, const char *name,
        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,
@@ -1808,12 +1815,12 @@ translate_compat_table(const char *name,
 
        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;
        }
@@ -1868,7 +1875,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
                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);
@@ -1884,7 +1891,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
        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;
@@ -2121,7 +2128,7 @@ struct xt_table *ip6t_register_table(struct net *net,
        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,
@@ -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;
@@ -2152,7 +2159,7 @@ void ip6t_unregister_table(struct xt_table *table)
 
        /* 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);
index ad378ef..33ddfe5 100644 (file)
@@ -131,7 +131,7 @@ static int __net_init ip6table_filter_net_init(struct net *net)
 
 static void __net_exit ip6table_filter_net_exit(struct net *net)
 {
-       ip6t_unregister_table(net->ipv6.ip6table_filter);
+       ip6t_unregister_table(net, net->ipv6.ip6table_filter);
 }
 
 static struct pernet_operations ip6table_filter_net_ops = {
index a929c19..9bc483f 100644 (file)
@@ -182,7 +182,7 @@ static int __net_init ip6table_mangle_net_init(struct net *net)
 
 static void __net_exit ip6table_mangle_net_exit(struct net *net)
 {
-       ip6t_unregister_table(net->ipv6.ip6table_mangle);
+       ip6t_unregister_table(net, net->ipv6.ip6table_mangle);
 }
 
 static struct pernet_operations ip6table_mangle_net_ops = {
index ed1a118..4c90b55 100644 (file)
@@ -94,7 +94,7 @@ static int __net_init ip6table_raw_net_init(struct net *net)
 
 static void __net_exit ip6table_raw_net_exit(struct net *net)
 {
-       ip6t_unregister_table(net->ipv6.ip6table_raw);
+       ip6t_unregister_table(net, net->ipv6.ip6table_raw);
 }
 
 static struct pernet_operations ip6table_raw_net_ops = {
index 41b444c..baa8d4e 100644 (file)
@@ -134,7 +134,7 @@ static int __net_init ip6table_security_net_init(struct net *net)
 
 static void __net_exit ip6table_security_net_exit(struct net *net)
 {
-       ip6t_unregister_table(net->ipv6.ip6table_security);
+       ip6t_unregister_table(net, net->ipv6.ip6table_security);
 }
 
 static struct pernet_operations ip6table_security_net_ops = {
index 0956eba..55ce22e 100644 (file)
@@ -212,7 +212,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
        struct sk_buff *reasm;
 
        /* Previously seen (loopback)?  */
-       if (skb->nfct)
+       if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct))
                return NF_ACCEPT;
 
        reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb));
index 624a548..ad1fcda 100644 (file)
@@ -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
 {
@@ -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);
 
index fa38fc7..d93812d 100644 (file)
@@ -747,8 +747,8 @@ static inline void ip6_frags_sysctl_unregister(void)
 
 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);
index 634d14a..4469d45 100644 (file)
@@ -341,6 +341,18 @@ config NETFILTER_XT_TARGET_CONNSECMARK
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config NETFILTER_XT_TARGET_CT
+       tristate '"CT" target support'
+       depends on NF_CONNTRACK
+       depends on IP_NF_RAW || IP6_NF_RAW
+       depends on NETFILTER_ADVANCED
+       help
+         This options adds a `CT' target, which allows to specify initial
+         connection tracking parameters like events to be delivered and
+         the helper to be used.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config NETFILTER_XT_TARGET_DSCP
        tristate '"DSCP" and "TOS" target support'
        depends on IP_NF_MANGLE || IP6_NF_MANGLE
index 49f62ee..f873644 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
index f2d7623..817a889 100644 (file)
@@ -68,6 +68,10 @@ config       IP_VS_TAB_BITS
          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
index 27c30cf..60bb41a 100644 (file)
 #include <net/ip_vs.h>
 
 
+#ifndef CONFIG_IP_VS_TAB_BITS
+#define CONFIG_IP_VS_TAB_BITS  12
+#endif
+
+/*
+ * Connection hash size. Default is what was selected at compile time.
+*/
+int ip_vs_conn_tab_bits = CONFIG_IP_VS_TAB_BITS;
+module_param_named(conn_tab_bits, ip_vs_conn_tab_bits, int, 0444);
+MODULE_PARM_DESC(conn_tab_bits, "Set connections' hash size");
+
+/* size and mask values */
+int ip_vs_conn_tab_size;
+int ip_vs_conn_tab_mask;
+
 /*
  *  Connection hash table: for input and output packets lookups of IPVS
  */
@@ -125,11 +140,11 @@ static unsigned int ip_vs_conn_hashkey(int af, unsigned proto,
        if (af == AF_INET6)
                return jhash_3words(jhash(addr, 16, ip_vs_conn_rnd),
                                    (__force u32)port, proto, ip_vs_conn_rnd)
-                       & IP_VS_CONN_TAB_MASK;
+                       & ip_vs_conn_tab_mask;
 #endif
        return jhash_3words((__force u32)addr->ip, (__force u32)port, proto,
                            ip_vs_conn_rnd)
-               & IP_VS_CONN_TAB_MASK;
+               & ip_vs_conn_tab_mask;
 }
 
 
@@ -760,7 +775,7 @@ static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos)
        int idx;
        struct ip_vs_conn *cp;
 
-       for(idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) {
+       for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
                ct_read_lock_bh(idx);
                list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) {
                        if (pos-- == 0) {
@@ -797,7 +812,7 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos)
        idx = l - ip_vs_conn_tab;
        ct_read_unlock_bh(idx);
 
-       while (++idx < IP_VS_CONN_TAB_SIZE) {
+       while (++idx < ip_vs_conn_tab_size) {
                ct_read_lock_bh(idx);
                list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) {
                        seq->private = &ip_vs_conn_tab[idx];
@@ -976,8 +991,8 @@ void ip_vs_random_dropentry(void)
        /*
         * Randomly scan 1/32 of the whole table every second
         */
-       for (idx = 0; idx < (IP_VS_CONN_TAB_SIZE>>5); idx++) {
-               unsigned hash = net_random() & IP_VS_CONN_TAB_MASK;
+       for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) {
+               unsigned hash = net_random() & ip_vs_conn_tab_mask;
 
                /*
                 *  Lock is actually needed in this loop.
@@ -1029,7 +1044,7 @@ static void ip_vs_conn_flush(void)
        struct ip_vs_conn *cp;
 
   flush_again:
-       for (idx=0; idx<IP_VS_CONN_TAB_SIZE; idx++) {
+       for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
                /*
                 *  Lock is actually needed in this loop.
                 */
@@ -1060,10 +1075,15 @@ int __init ip_vs_conn_init(void)
 {
        int idx;
 
+       /* Compute size and mask */
+       ip_vs_conn_tab_size = 1 << ip_vs_conn_tab_bits;
+       ip_vs_conn_tab_mask = ip_vs_conn_tab_size - 1;
+
        /*
         * Allocate the connection hash table and initialize its list heads
         */
-       ip_vs_conn_tab = vmalloc(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head));
+       ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size *
+                                sizeof(struct list_head));
        if (!ip_vs_conn_tab)
                return -ENOMEM;
 
@@ -1078,12 +1098,12 @@ int __init ip_vs_conn_init(void)
 
        pr_info("Connection hash table configured "
                "(size=%d, memory=%ldKbytes)\n",
-               IP_VS_CONN_TAB_SIZE,
-               (long)(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head))/1024);
+               ip_vs_conn_tab_size,
+               (long)(ip_vs_conn_tab_size*sizeof(struct list_head))/1024);
        IP_VS_DBG(0, "Each connection entry needs %Zd bytes at least\n",
                  sizeof(struct ip_vs_conn));
 
-       for (idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) {
+       for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
                INIT_LIST_HEAD(&ip_vs_conn_tab[idx]);
        }
 
index c37ac2d..00d0b15 100644 (file)
@@ -1843,7 +1843,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
        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,
@@ -2386,7 +2386,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
                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;
@@ -2399,7 +2399,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
        {
                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;
@@ -3243,7 +3243,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
        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;
        }
 
index 33e2c79..73f38ea 100644 (file)
@@ -208,7 +208,7 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
                 */
                from.ip = n_cp->vaddr.ip;
                port = n_cp->vport;
-               sprintf(buf, "%d,%d,%d,%d,%d,%d", NIPQUAD(from.ip),
+               sprintf(buf, "%u,%u,%u,%u,%u,%u", NIPQUAD(from.ip),
                        (ntohs(port)>>8)&255, ntohs(port)&255);
                buf_len = strlen(buf);
 
index 4d79e3c..bd83141 100644 (file)
@@ -619,7 +619,7 @@ 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,
@@ -629,6 +629,7 @@ init_conntrack(struct net *net,
        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)) {
@@ -649,7 +650,11 @@ init_conntrack(struct net *net,
        }
 
        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);
@@ -674,7 +679,7 @@ init_conntrack(struct net *net,
                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);
        }
 
@@ -695,7 +700,7 @@ init_conntrack(struct net *net,
 
 /* 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,
@@ -719,7 +724,8 @@ resolve_normal_ct(struct net *net,
        /* 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))
@@ -756,7 +762,7 @@ unsigned int
 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;
@@ -765,10 +771,14 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
        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 */
@@ -779,7 +789,8 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
                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);
@@ -792,22 +803,25 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
                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);
@@ -822,11 +836,15 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
                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;
 }
@@ -865,7 +883,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
                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);
index 4b1a56b..4509fa6 100644 (file)
@@ -65,7 +65,7 @@ __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
 }
 
 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;
@@ -73,13 +73,34 @@ __nf_conntrack_helper_find_byname(const char *name)
 
        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)
 {
@@ -94,13 +115,22 @@ 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);
index 0ffe689..327c517 100644 (file)
@@ -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>
@@ -456,6 +457,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct)
 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;
@@ -482,7 +484,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
        } 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);
@@ -559,7 +562,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
        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;
 
@@ -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 */
@@ -586,6 +590,7 @@ static int ctnetlink_done(struct netlink_callback *cb)
 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;
@@ -594,9 +599,9 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
 
        rcu_read_lock();
        last = (struct nf_conn *)cb->args[1];
-       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;
@@ -768,6 +773,7 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        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;
@@ -781,7 +787,7 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
                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;
@@ -790,7 +796,7 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
        if (err < 0)
                return err;
 
-       h = nf_conntrack_find_get(&init_net, &tuple);
+       h = nf_conntrack_find_get(net, &tuple);
        if (!h)
                return -ENOENT;
 
@@ -828,6 +834,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        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;
@@ -850,7 +857,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
        if (err < 0)
                return err;
 
-       h = nf_conntrack_find_get(&init_net, &tuple);
+       h = nf_conntrack_find_get(net, &tuple);
        if (!h)
                return -ENOENT;
 
@@ -994,7 +1001,8 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
                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);
@@ -1005,7 +1013,8 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
                }
 
                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
@@ -1175,7 +1184,8 @@ ctnetlink_change_conntrack(struct nf_conn *ct,
 }
 
 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)
@@ -1184,7 +1194,7 @@ ctnetlink_create_conntrack(const struct nlattr * const cda[],
        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);
 
@@ -1203,7 +1213,8 @@ ctnetlink_create_conntrack(const struct nlattr * const cda[],
                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
@@ -1213,7 +1224,9 @@ ctnetlink_create_conntrack(const struct nlattr * const cda[],
                        }
 
                        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;
@@ -1236,7 +1249,7 @@ ctnetlink_create_conntrack(const struct nlattr * const cda[],
                }
        } 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;
        }
@@ -1268,7 +1281,7 @@ ctnetlink_create_conntrack(const struct nlattr * const cda[],
        }
 
        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])
@@ -1285,7 +1298,7 @@ ctnetlink_create_conntrack(const struct nlattr * const cda[],
                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;
@@ -1313,6 +1326,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        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);
@@ -1333,9 +1347,9 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
 
        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;
@@ -1343,7 +1357,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        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);
@@ -1357,7 +1371,8 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        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) |
@@ -1382,7 +1397,8 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                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) |
@@ -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;
@@ -1540,7 +1557,7 @@ ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item)
                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);
@@ -1563,7 +1580,7 @@ ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item)
        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;
 
@@ -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
@@ -1587,7 +1604,7 @@ static int ctnetlink_exp_done(struct netlink_callback *cb)
 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;
@@ -1640,6 +1657,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
                     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;
@@ -1661,7 +1679,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        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;
 
@@ -1701,9 +1719,9 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                     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;
@@ -1717,7 +1735,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                        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;
 
@@ -1740,18 +1758,13 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
 
                /* 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);
                                }
@@ -1763,7 +1776,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                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);
@@ -1784,7 +1797,8 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x,
 }
 
 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;
@@ -1806,7 +1820,7 @@ ctnetlink_create_expect(const struct nlattr * const cda[], u_int8_t u3,
                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);
@@ -1846,6 +1860,7 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
                     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);
@@ -1862,13 +1877,13 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
                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));
index c99cfba..d899b1a 100644 (file)
@@ -241,7 +241,7 @@ static int gre_packet(struct nf_conn *ct,
                                   ct->proto.gre.stream_timeout);
                /* Also, more likely to be important, and not a probe. */
                set_bit(IPS_ASSURED_BIT, &ct->status);
-               nf_conntrack_event_cache(IPCT_STATUS, ct);
+               nf_conntrack_event_cache(IPCT_ASSURED, ct);
        } else
                nf_ct_refresh_acct(ct, ctinfo, skb,
                                   ct->proto.gre.timeout);
index f9d930f..b68ff15 100644 (file)
@@ -377,7 +377,7 @@ static int sctp_packet(struct nf_conn *ct,
            new_state == SCTP_CONNTRACK_ESTABLISHED) {
                pr_debug("Setting assured bit\n");
                set_bit(IPS_ASSURED_BIT, &ct->status);
-               nf_conntrack_event_cache(IPCT_STATUS, ct);
+               nf_conntrack_event_cache(IPCT_ASSURED, ct);
        }
 
        return NF_ACCEPT;
index 3c96437..ad11805 100644 (file)
@@ -1045,7 +1045,7 @@ static int tcp_packet(struct nf_conn *ct,
                   after SYN_RECV or a valid answer for a picked up
                   connection. */
                set_bit(IPS_ASSURED_BIT, &ct->status);
-               nf_conntrack_event_cache(IPCT_STATUS, ct);
+               nf_conntrack_event_cache(IPCT_ASSURED, ct);
        }
        nf_ct_refresh_acct(ct, ctinfo, skb, timeout);
 
index 5c5518b..8d38f9a 100644 (file)
@@ -77,7 +77,7 @@ static int udp_packet(struct nf_conn *ct,
                nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream);
                /* Also, more likely to be important, and not a probe */
                if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
-                       nf_conntrack_event_cache(IPCT_STATUS, ct);
+                       nf_conntrack_event_cache(IPCT_ASSURED, ct);
        } else
                nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout);
 
index 458655b..0b1bc9b 100644 (file)
@@ -75,7 +75,7 @@ static int udplite_packet(struct nf_conn *ct,
                                   nf_ct_udplite_timeout_stream);
                /* Also, more likely to be important, and not a probe */
                if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
-                       nf_conntrack_event_cache(IPCT_STATUS, ct);
+                       nf_conntrack_event_cache(IPCT_ASSURED, ct);
        } else
                nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udplite_timeout);
 
index eedc0c1..8eb0cc2 100644 (file)
@@ -40,7 +40,6 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
 
 static char __initdata nfversion[] = "0.30";
 
-static struct sock *nfnl = NULL;
 static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
 static DEFINE_MUTEX(nfnl_mutex);
 
@@ -101,34 +100,35 @@ nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss)
        return &ss->cb[cb_id];
 }
 
-int nfnetlink_has_listeners(unsigned int group)
+int nfnetlink_has_listeners(struct net *net, unsigned int group)
 {
-       return netlink_has_listeners(nfnl, group);
+       return netlink_has_listeners(net->nfnl, group);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
 
-int nfnetlink_send(struct sk_buff *skb, u32 pid,
+int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid,
                   unsigned group, int echo, gfp_t flags)
 {
-       return nlmsg_notify(nfnl, skb, pid, group, echo, flags);
+       return nlmsg_notify(net->nfnl, skb, pid, group, echo, flags);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_send);
 
-void nfnetlink_set_err(u32 pid, u32 group, int error)
+void nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
 {
-       netlink_set_err(nfnl, pid, group, error);
+       netlink_set_err(net->nfnl, pid, group, error);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_set_err);
 
-int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags)
+int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags)
 {
-       return netlink_unicast(nfnl, skb, pid, flags);
+       return netlink_unicast(net->nfnl, skb, pid, flags);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_unicast);
 
 /* Process one complete nfnetlink message. */
 static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
+       struct net *net = sock_net(skb->sk);
        const struct nfnl_callback *nc;
        const struct nfnetlink_subsystem *ss;
        int type, err;
@@ -170,7 +170,7 @@ replay:
                if (err < 0)
                        return err;
 
-               err = nc->call(nfnl, skb, nlh, (const struct nlattr **)cda);
+               err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda);
                if (err == -EAGAIN)
                        goto replay;
                return err;
@@ -184,26 +184,45 @@ static void nfnetlink_rcv(struct sk_buff *skb)
        nfnl_unlock();
 }
 
-static void __exit nfnetlink_exit(void)
+static int __net_init nfnetlink_net_init(struct net *net)
 {
-       printk("Removing netfilter NETLINK layer.\n");
-       netlink_kernel_release(nfnl);
-       return;
+       struct sock *nfnl;
+
+       nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX,
+                                    nfnetlink_rcv, NULL, THIS_MODULE);
+       if (!nfnl)
+               return -ENOMEM;
+       net->nfnl_stash = nfnl;
+       rcu_assign_pointer(net->nfnl, nfnl);
+       return 0;
 }
 
-static int __init nfnetlink_init(void)
+static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
 {
-       printk("Netfilter messages via NETLINK v%s.\n", nfversion);
+       struct net *net;
 
-       nfnl = netlink_kernel_create(&init_net, NETLINK_NETFILTER, NFNLGRP_MAX,
-                                    nfnetlink_rcv, NULL, THIS_MODULE);
-       if (!nfnl) {
-               printk(KERN_ERR "cannot initialize nfnetlink!\n");
-               return -ENOMEM;
-       }
+       list_for_each_entry(net, net_exit_list, exit_list)
+               rcu_assign_pointer(net->nfnl, NULL);
+       synchronize_net();
+       list_for_each_entry(net, net_exit_list, exit_list)
+               netlink_kernel_release(net->nfnl_stash);
+}
 
-       return 0;
+static struct pernet_operations nfnetlink_net_ops = {
+       .init           = nfnetlink_net_init,
+       .exit_batch     = nfnetlink_net_exit_batch,
+};
+
+static int __init nfnetlink_init(void)
+{
+       printk("Netfilter messages via NETLINK v%s.\n", nfversion);
+       return register_pernet_subsys(&nfnetlink_net_ops);
 }
 
+static void __exit nfnetlink_exit(void)
+{
+       printk("Removing netfilter NETLINK layer.\n");
+       unregister_pernet_subsys(&nfnetlink_net_ops);
+}
 module_init(nfnetlink_init);
 module_exit(nfnetlink_exit);
index 9de0470..285e902 100644 (file)
@@ -323,7 +323,8 @@ __nfulnl_send(struct nfulnl_instance *inst)
                          NLMSG_DONE,
                          sizeof(struct nfgenmsg));
 
-       status = nfnetlink_unicast(inst->skb, inst->peer_pid, MSG_DONTWAIT);
+       status = nfnetlink_unicast(inst->skb, &init_net, inst->peer_pid,
+                                  MSG_DONTWAIT);
 
        inst->qlen = 0;
        inst->skb = NULL;
index 7e3fa41..90cf36d 100644 (file)
@@ -414,13 +414,13 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
                queue->queue_dropped++;
                if (net_ratelimit())
                          printk(KERN_WARNING "nf_queue: full at %d entries, "
-                                "dropping packets(s). Dropped: %d\n",
-                                queue->queue_total, queue->queue_dropped);
+                                "dropping packets(s).\n",
+                                queue->queue_total);
                goto err_out_free_nskb;
        }
 
        /* nfnetlink_unicast will either free the nskb or add it to a socket */
-       err = nfnetlink_unicast(nskb, queue->peer_pid, MSG_DONTWAIT);
+       err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT);
        if (err < 0) {
                queue->queue_user_dropped++;
                goto err_out_unlock;
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
new file mode 100644 (file)
index 0000000..8183a05
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/selinux.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_CT.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_ecache.h>
+
+static unsigned int xt_ct_target(struct sk_buff *skb,
+                                const struct xt_target_param *par)
+{
+       const struct xt_ct_target_info *info = par->targinfo;
+       struct nf_conn *ct = info->ct;
+
+       /* Previously seen (loopback)? Ignore. */
+       if (skb->nfct != NULL)
+               return XT_CONTINUE;
+
+       atomic_inc(&ct->ct_general.use);
+       skb->nfct = &ct->ct_general;
+       skb->nfctinfo = IP_CT_NEW;
+
+       return XT_CONTINUE;
+}
+
+static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
+{
+       if (par->family == AF_INET) {
+               const struct ipt_entry *e = par->entryinfo;
+
+               if (e->ip.invflags & IPT_INV_PROTO)
+                       return 0;
+               return e->ip.proto;
+       } else if (par->family == AF_INET6) {
+               const struct ip6t_entry *e = par->entryinfo;
+
+               if (e->ipv6.invflags & IP6T_INV_PROTO)
+                       return 0;
+               return e->ipv6.proto;
+       } else
+               return 0;
+}
+
+static bool xt_ct_tg_check(const struct xt_tgchk_param *par)
+{
+       struct xt_ct_target_info *info = par->targinfo;
+       struct nf_conntrack_tuple t;
+       struct nf_conn_help *help;
+       struct nf_conn *ct;
+       u8 proto;
+
+       if (info->flags & ~XT_CT_NOTRACK)
+               return false;
+
+       if (info->flags & XT_CT_NOTRACK) {
+               ct = &nf_conntrack_untracked;
+               atomic_inc(&ct->ct_general.use);
+               goto out;
+       }
+
+       if (nf_ct_l3proto_try_module_get(par->family) < 0)
+               goto err1;
+
+       memset(&t, 0, sizeof(t));
+       ct = nf_conntrack_alloc(par->net, &t, &t, GFP_KERNEL);
+       if (IS_ERR(ct))
+               goto err2;
+
+       if ((info->ct_events || info->exp_events) &&
+           !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
+                                 GFP_KERNEL))
+               goto err3;
+
+       if (info->helper[0]) {
+               proto = xt_ct_find_proto(par);
+               if (!proto)
+                       goto err3;
+
+               help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
+               if (help == NULL)
+                       goto err3;
+
+               help->helper = nf_conntrack_helper_try_module_get(info->helper,
+                                                                 par->family,
+                                                                 proto);
+               if (help->helper == NULL)
+                       goto err3;
+       }
+
+       __set_bit(IPS_TEMPLATE_BIT, &ct->status);
+       __set_bit(IPS_CONFIRMED_BIT, &ct->status);
+out:
+       info->ct = ct;
+       return true;
+
+err3:
+       nf_conntrack_free(ct);
+err2:
+       nf_ct_l3proto_module_put(par->family);
+err1:
+       return false;
+}
+
+static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par)
+{
+       struct xt_ct_target_info *info = par->targinfo;
+       struct nf_conn *ct = info->ct;
+       struct nf_conn_help *help;
+
+       if (ct != &nf_conntrack_untracked) {
+               help = nfct_help(ct);
+               if (help)
+                       module_put(help->helper->me);
+
+               nf_ct_l3proto_module_put(par->family);
+       }
+       nf_ct_put(info->ct);
+}
+
+static struct xt_target xt_ct_tg __read_mostly = {
+       .name           = "CT",
+       .family         = NFPROTO_UNSPEC,
+       .targetsize     = XT_ALIGN(sizeof(struct xt_ct_target_info)),
+       .checkentry     = xt_ct_tg_check,
+       .destroy        = xt_ct_tg_destroy,
+       .target         = xt_ct_target,
+       .table          = "raw",
+       .me             = THIS_MODULE,
+};
+
+static int __init xt_ct_tg_init(void)
+{
+       return xt_register_target(&xt_ct_tg);
+}
+
+static void __exit xt_ct_tg_exit(void)
+{
+       xt_unregister_target(&xt_ct_tg);
+}
+
+module_init(xt_ct_tg_init);
+module_exit(xt_ct_tg_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Xtables: connection tracking target");
+MODULE_ALIAS("ipt_CT");
+MODULE_ALIAS("ip6t_CT");
index f28f6a5..12dcd70 100644 (file)
@@ -28,6 +28,7 @@ MODULE_ALIAS("ip6t_NFQUEUE");
 MODULE_ALIAS("arpt_NFQUEUE");
 
 static u32 jhash_initval __read_mostly;
+static bool rnd_inited __read_mostly;
 
 static unsigned int
 nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par)
@@ -90,6 +91,10 @@ static bool nfqueue_tg_v1_check(const struct xt_tgchk_param *par)
        const struct xt_NFQ_info_v1 *info = par->targinfo;
        u32 maxid;
 
+       if (unlikely(!rnd_inited)) {
+               get_random_bytes(&jhash_initval, sizeof(jhash_initval));
+               rnd_inited = true;
+       }
        if (info->queues_total == 0) {
                pr_err("NFQUEUE: number of total queues is 0\n");
                return false;
@@ -135,7 +140,6 @@ static struct xt_target nfqueue_tg_reg[] __read_mostly = {
 
 static int __init nfqueue_tg_init(void)
 {
-       get_random_bytes(&jhash_initval, sizeof(jhash_initval));
        return xt_register_targets(nfqueue_tg_reg, ARRAY_SIZE(nfqueue_tg_reg));
 }
 
index d80b819..87ae97e 100644 (file)
@@ -23,6 +23,7 @@ static DEFINE_MUTEX(xt_rateest_mutex);
 #define RATEEST_HSIZE  16
 static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly;
 static unsigned int jhash_rnd __read_mostly;
+static bool rnd_inited __read_mostly;
 
 static unsigned int xt_rateest_hash(const char *name)
 {
@@ -93,6 +94,11 @@ static bool xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
                struct gnet_estimator   est;
        } cfg;
 
+       if (unlikely(!rnd_inited)) {
+               get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
+               rnd_inited = true;
+       }
+
        est = xt_rateest_lookup(info->name);
        if (est) {
                /*
@@ -164,7 +170,6 @@ static int __init xt_rateest_tg_init(void)
        for (i = 0; i < ARRAY_SIZE(rateest_hash); i++)
                INIT_HLIST_HEAD(&rateest_hash[i]);
 
-       get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
        return xt_register_target(&xt_rateest_tg_reg);
 }
 
index eda64c1..6f21b43 100644 (file)
@@ -60,17 +60,9 @@ tcpmss_mangle_packet(struct sk_buff *skb,
        tcplen = skb->len - tcphoff;
        tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
 
-       /* Since it passed flags test in tcp match, we know it is is
-          not a fragment, and has data >= tcp header length.  SYN
-          packets should not contain data: if they did, then we risk
-          running over MTU, sending Frag Needed and breaking things
-          badly. --RR */
-       if (tcplen != tcph->doff*4) {
-               if (net_ratelimit())
-                       printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n",
-                              skb->len);
+       /* Header cannot be larger than the packet */
+       if (tcplen < tcph->doff*4)
                return -1;
-       }
 
        if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
                if (dst_mtu(skb_dst(skb)) <= minlen) {
@@ -115,6 +107,12 @@ tcpmss_mangle_packet(struct sk_buff *skb,
                }
        }
 
+       /* There is data after the header so the option can't be added
+          without moving it, and doing so may make the SYN packet
+          itself too large. Accept the packet unmodified instead. */
+       if (tcplen > tcph->doff*4)
+               return 0;
+
        /*
         * MSS Option not found ?! add it..
         */
index 38f03f7..0d9d18e 100644 (file)
@@ -40,15 +40,11 @@ struct xt_connlimit_data {
        spinlock_t lock;
 };
 
-static u_int32_t connlimit_rnd;
-static bool connlimit_rnd_inited;
+static u_int32_t connlimit_rnd __read_mostly;
+static bool connlimit_rnd_inited __read_mostly;
 
 static inline unsigned int connlimit_iphash(__be32 addr)
 {
-       if (unlikely(!connlimit_rnd_inited)) {
-               get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
-               connlimit_rnd_inited = true;
-       }
        return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF;
 }
 
@@ -59,11 +55,6 @@ connlimit_iphash6(const union nf_inet_addr *addr,
        union nf_inet_addr res;
        unsigned int i;
 
-       if (unlikely(!connlimit_rnd_inited)) {
-               get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
-               connlimit_rnd_inited = true;
-       }
-
        for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
                res.ip6[i] = addr->ip6[i] & mask->ip6[i];
 
@@ -99,7 +90,8 @@ same_source_net(const union nf_inet_addr *addr,
        }
 }
 
-static int count_them(struct xt_connlimit_data *data,
+static int count_them(struct net *net,
+                     struct xt_connlimit_data *data,
                      const struct nf_conntrack_tuple *tuple,
                      const union nf_inet_addr *addr,
                      const union nf_inet_addr *mask,
@@ -122,7 +114,7 @@ static int count_them(struct xt_connlimit_data *data,
 
        /* check the saved connections */
        list_for_each_entry_safe(conn, tmp, hash, list) {
-               found    = nf_conntrack_find_get(&init_net, &conn->tuple);
+               found    = nf_conntrack_find_get(net, &conn->tuple);
                found_ct = NULL;
 
                if (found != NULL)
@@ -180,6 +172,7 @@ static int count_them(struct xt_connlimit_data *data,
 static bool
 connlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
 {
+       struct net *net = dev_net(par->in ? par->in : par->out);
        const struct xt_connlimit_info *info = par->matchinfo;
        union nf_inet_addr addr;
        struct nf_conntrack_tuple tuple;
@@ -204,7 +197,7 @@ connlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
        }
 
        spin_lock_bh(&info->data->lock);
-       connections = count_them(info->data, tuple_ptr, &addr,
+       connections = count_them(net, info->data, tuple_ptr, &addr,
                                 &info->mask, par->family);
        spin_unlock_bh(&info->data->lock);
 
@@ -226,6 +219,10 @@ static bool connlimit_mt_check(const struct xt_mtchk_param *par)
        struct xt_connlimit_info *info = par->matchinfo;
        unsigned int i;
 
+       if (unlikely(!connlimit_rnd_inited)) {
+               get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+               connlimit_rnd_inited = true;
+       }
        if (nf_ct_l3proto_try_module_get(par->family) < 0) {
                printk(KERN_WARNING "cannot load conntrack support for "
                       "address family %u\n", par->family);
index dd16e40..017c959 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 
 #include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
@@ -40,9 +41,19 @@ MODULE_DESCRIPTION("Xtables: per hash-bucket rate-limit match");
 MODULE_ALIAS("ipt_hashlimit");
 MODULE_ALIAS("ip6t_hashlimit");
 
+struct hashlimit_net {
+       struct hlist_head       htables;
+       struct proc_dir_entry   *ipt_hashlimit;
+       struct proc_dir_entry   *ip6t_hashlimit;
+};
+
+static int hashlimit_net_id;
+static inline struct hashlimit_net *hashlimit_pernet(struct net *net)
+{
+       return net_generic(net, hashlimit_net_id);
+}
+
 /* need to declare this at the top */
-static struct proc_dir_entry *hashlimit_procdir4;
-static struct proc_dir_entry *hashlimit_procdir6;
 static const struct file_operations dl_file_ops;
 
 /* hash table crap */
@@ -79,27 +90,26 @@ struct dsthash_ent {
 
 struct xt_hashlimit_htable {
        struct hlist_node node;         /* global list of all htables */
-       atomic_t use;
+       int use;
        u_int8_t family;
+       bool rnd_initialized;
 
        struct hashlimit_cfg1 cfg;      /* config */
 
        /* used internally */
        spinlock_t lock;                /* lock for list_head */
        u_int32_t rnd;                  /* random seed for hash */
-       int rnd_initialized;
        unsigned int count;             /* number entries in table */
        struct timer_list timer;        /* timer for gc */
 
        /* seq_file stuff */
        struct proc_dir_entry *pde;
+       struct net *net;
 
        struct hlist_head hash[0];      /* hashtable itself */
 };
 
-static DEFINE_SPINLOCK(hashlimit_lock);        /* protects htables list */
-static DEFINE_MUTEX(hlimit_mutex);     /* additional checkentry protection */
-static HLIST_HEAD(hashlimit_htables);
+static DEFINE_MUTEX(hashlimit_mutex);  /* protects htables list */
 static struct kmem_cache *hashlimit_cachep __read_mostly;
 
 static inline bool dst_cmp(const struct dsthash_ent *ent,
@@ -150,7 +160,7 @@ dsthash_alloc_init(struct xt_hashlimit_htable *ht,
         * the first hashtable entry */
        if (!ht->rnd_initialized) {
                get_random_bytes(&ht->rnd, sizeof(ht->rnd));
-               ht->rnd_initialized = 1;
+               ht->rnd_initialized = true;
        }
 
        if (ht->cfg.max && ht->count >= ht->cfg.max) {
@@ -185,8 +195,9 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent)
 }
 static void htable_gc(unsigned long htlong);
 
-static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family)
+static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_int8_t family)
 {
+       struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
        struct xt_hashlimit_htable *hinfo;
        unsigned int size;
        unsigned int i;
@@ -232,33 +243,36 @@ static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family)
        for (i = 0; i < hinfo->cfg.size; i++)
                INIT_HLIST_HEAD(&hinfo->hash[i]);
 
-       atomic_set(&hinfo->use, 1);
+       hinfo->use = 1;
        hinfo->count = 0;
        hinfo->family = family;
-       hinfo->rnd_initialized = 0;
+       hinfo->rnd_initialized = false;
        spin_lock_init(&hinfo->lock);
        hinfo->pde = proc_create_data(minfo->name, 0,
                (family == NFPROTO_IPV4) ?
-               hashlimit_procdir4 : hashlimit_procdir6,
+               hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,
                &dl_file_ops, hinfo);
        if (!hinfo->pde) {
                vfree(hinfo);
                return -1;
        }
+       hinfo->net = net;
 
        setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo);
        hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
        add_timer(&hinfo->timer);
 
-       spin_lock_bh(&hashlimit_lock);
-       hlist_add_head(&hinfo->node, &hashlimit_htables);
-       spin_unlock_bh(&hashlimit_lock);
+       mutex_lock(&hashlimit_mutex);
+       hlist_add_head(&hinfo->node, &hashlimit_net->htables);
+       mutex_unlock(&hashlimit_mutex);
 
        return 0;
 }
 
-static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family)
+static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo,
+                        u_int8_t family)
 {
+       struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
        struct xt_hashlimit_htable *hinfo;
        unsigned int size;
        unsigned int i;
@@ -293,28 +307,29 @@ static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family)
        for (i = 0; i < hinfo->cfg.size; i++)
                INIT_HLIST_HEAD(&hinfo->hash[i]);
 
-       atomic_set(&hinfo->use, 1);
+       hinfo->use = 1;
        hinfo->count = 0;
        hinfo->family = family;
-       hinfo->rnd_initialized = 0;
+       hinfo->rnd_initialized = false;
        spin_lock_init(&hinfo->lock);
 
        hinfo->pde = proc_create_data(minfo->name, 0,
                (family == NFPROTO_IPV4) ?
-               hashlimit_procdir4 : hashlimit_procdir6,
+               hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,
                &dl_file_ops, hinfo);
        if (hinfo->pde == NULL) {
                vfree(hinfo);
                return -1;
        }
+       hinfo->net = net;
 
        setup_timer(&hinfo->timer, htable_gc, (unsigned long)hinfo);
        hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
        add_timer(&hinfo->timer);
 
-       spin_lock_bh(&hashlimit_lock);
-       hlist_add_head(&hinfo->node, &hashlimit_htables);
-       spin_unlock_bh(&hashlimit_lock);
+       mutex_lock(&hashlimit_mutex);
+       hlist_add_head(&hinfo->node, &hashlimit_net->htables);
+       mutex_unlock(&hashlimit_mutex);
 
        return 0;
 }
@@ -364,43 +379,46 @@ static void htable_gc(unsigned long htlong)
 
 static void htable_destroy(struct xt_hashlimit_htable *hinfo)
 {
+       struct hashlimit_net *hashlimit_net = hashlimit_pernet(hinfo->net);
+       struct proc_dir_entry *parent;
+
        del_timer_sync(&hinfo->timer);
 
-       /* remove proc entry */
-       remove_proc_entry(hinfo->pde->name,
-                         hinfo->family == NFPROTO_IPV4 ? hashlimit_procdir4 :
-                                                    hashlimit_procdir6);
+       if (hinfo->family == NFPROTO_IPV4)
+               parent = hashlimit_net->ipt_hashlimit;
+       else
+               parent = hashlimit_net->ip6t_hashlimit;
+       remove_proc_entry(hinfo->pde->name, parent);
        htable_selective_cleanup(hinfo, select_all);
        vfree(hinfo);
 }
 
-static struct xt_hashlimit_htable *htable_find_get(const char *name,
+static struct xt_hashlimit_htable *htable_find_get(struct net *net,
+                                                  const char *name,
                                                   u_int8_t family)
 {
+       struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
        struct xt_hashlimit_htable *hinfo;
        struct hlist_node *pos;
 
-       spin_lock_bh(&hashlimit_lock);
-       hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) {
+       hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) {
                if (!strcmp(name, hinfo->pde->name) &&
                    hinfo->family == family) {
-                       atomic_inc(&hinfo->use);
-                       spin_unlock_bh(&hashlimit_lock);
+                       hinfo->use++;
                        return hinfo;
                }
        }
-       spin_unlock_bh(&hashlimit_lock);
        return NULL;
 }
 
 static void htable_put(struct xt_hashlimit_htable *hinfo)
 {
-       if (atomic_dec_and_test(&hinfo->use)) {
-               spin_lock_bh(&hashlimit_lock);
+       mutex_lock(&hashlimit_mutex);
+       if (--hinfo->use == 0) {
                hlist_del(&hinfo->node);
-               spin_unlock_bh(&hashlimit_lock);
                htable_destroy(hinfo);
        }
+       mutex_unlock(&hashlimit_mutex);
 }
 
 /* The algorithm used is the Simple Token Bucket Filter (TBF)
@@ -665,6 +683,7 @@ hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
 
 static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
 {
+       struct net *net = par->net;
        struct xt_hashlimit_info *r = par->matchinfo;
 
        /* Check for overflow. */
@@ -687,25 +706,20 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
        if (r->name[sizeof(r->name) - 1] != '\0')
                return false;
 
-       /* This is the best we've got: We cannot release and re-grab lock,
-        * since checkentry() is called before x_tables.c grabs xt_mutex.
-        * We also cannot grab the hashtable spinlock, since htable_create will
-        * call vmalloc, and that can sleep.  And we cannot just re-search
-        * the list of htable's in htable_create(), since then we would
-        * create duplicate proc files. -HW */
-       mutex_lock(&hlimit_mutex);
-       r->hinfo = htable_find_get(r->name, par->match->family);
-       if (!r->hinfo && htable_create_v0(r, par->match->family) != 0) {
-               mutex_unlock(&hlimit_mutex);
+       mutex_lock(&hashlimit_mutex);
+       r->hinfo = htable_find_get(net, r->name, par->match->family);
+       if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) {
+               mutex_unlock(&hashlimit_mutex);
                return false;
        }
-       mutex_unlock(&hlimit_mutex);
+       mutex_unlock(&hashlimit_mutex);
 
        return true;
 }
 
 static bool hashlimit_mt_check(const struct xt_mtchk_param *par)
 {
+       struct net *net = par->net;
        struct xt_hashlimit_mtinfo1 *info = par->matchinfo;
 
        /* Check for overflow. */
@@ -728,19 +742,13 @@ static bool hashlimit_mt_check(const struct xt_mtchk_param *par)
                        return false;
        }
 
-       /* This is the best we've got: We cannot release and re-grab lock,
-        * since checkentry() is called before x_tables.c grabs xt_mutex.
-        * We also cannot grab the hashtable spinlock, since htable_create will
-        * call vmalloc, and that can sleep.  And we cannot just re-search
-        * the list of htable's in htable_create(), since then we would
-        * create duplicate proc files. -HW */
-       mutex_lock(&hlimit_mutex);
-       info->hinfo = htable_find_get(info->name, par->match->family);
-       if (!info->hinfo && htable_create(info, par->match->family) != 0) {
-               mutex_unlock(&hlimit_mutex);
+       mutex_lock(&hashlimit_mutex);
+       info->hinfo = htable_find_get(net, info->name, par->match->family);
+       if (!info->hinfo && htable_create(net, info, par->match->family) != 0) {
+               mutex_unlock(&hashlimit_mutex);
                return false;
        }
-       mutex_unlock(&hlimit_mutex);
+       mutex_unlock(&hashlimit_mutex);
        return true;
 }
 
@@ -841,8 +849,7 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = {
 static void *dl_seq_start(struct seq_file *s, loff_t *pos)
        __acquires(htable->lock)
 {
-       struct proc_dir_entry *pde = s->private;
-       struct xt_hashlimit_htable *htable = pde->data;
+       struct xt_hashlimit_htable *htable = s->private;
        unsigned int *bucket;
 
        spin_lock_bh(&htable->lock);
@@ -859,8 +866,7 @@ static void *dl_seq_start(struct seq_file *s, loff_t *pos)
 
 static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
-       struct proc_dir_entry *pde = s->private;
-       struct xt_hashlimit_htable *htable = pde->data;
+       struct xt_hashlimit_htable *htable = s->private;
        unsigned int *bucket = (unsigned int *)v;
 
        *pos = ++(*bucket);
@@ -874,8 +880,7 @@ static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos)
 static void dl_seq_stop(struct seq_file *s, void *v)
        __releases(htable->lock)
 {
-       struct proc_dir_entry *pde = s->private;
-       struct xt_hashlimit_htable *htable = pde->data;
+       struct xt_hashlimit_htable *htable = s->private;
        unsigned int *bucket = (unsigned int *)v;
 
        kfree(bucket);
@@ -917,8 +922,7 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,
 
 static int dl_seq_show(struct seq_file *s, void *v)
 {
-       struct proc_dir_entry *pde = s->private;
-       struct xt_hashlimit_htable *htable = pde->data;
+       struct xt_hashlimit_htable *htable = s->private;
        unsigned int *bucket = (unsigned int *)v;
        struct dsthash_ent *ent;
        struct hlist_node *pos;
@@ -944,7 +948,7 @@ static int dl_proc_open(struct inode *inode, struct file *file)
 
        if (!ret) {
                struct seq_file *sf = file->private_data;
-               sf->private = PDE(inode);
+               sf->private = PDE(inode)->data;
        }
        return ret;
 }
@@ -957,10 +961,61 @@ static const struct file_operations dl_file_ops = {
        .release = seq_release
 };
 
+static int __net_init hashlimit_proc_net_init(struct net *net)
+{
+       struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
+
+       hashlimit_net->ipt_hashlimit = proc_mkdir("ipt_hashlimit", net->proc_net);
+       if (!hashlimit_net->ipt_hashlimit)
+               return -ENOMEM;
+#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+       hashlimit_net->ip6t_hashlimit = proc_mkdir("ip6t_hashlimit", net->proc_net);
+       if (!hashlimit_net->ip6t_hashlimit) {
+               proc_net_remove(net, "ipt_hashlimit");
+               return -ENOMEM;
+       }
+#endif
+       return 0;
+}
+
+static void __net_exit hashlimit_proc_net_exit(struct net *net)
+{
+       proc_net_remove(net, "ipt_hashlimit");
+#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+       proc_net_remove(net, "ip6t_hashlimit");
+#endif
+}
+
+static int __net_init hashlimit_net_init(struct net *net)
+{
+       struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
+
+       INIT_HLIST_HEAD(&hashlimit_net->htables);
+       return hashlimit_proc_net_init(net);
+}
+
+static void __net_exit hashlimit_net_exit(struct net *net)
+{
+       struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
+
+       BUG_ON(!hlist_empty(&hashlimit_net->htables));
+       hashlimit_proc_net_exit(net);
+}
+
+static struct pernet_operations hashlimit_net_ops = {
+       .init   = hashlimit_net_init,
+       .exit   = hashlimit_net_exit,
+       .id     = &hashlimit_net_id,
+       .size   = sizeof(struct hashlimit_net),
+};
+
 static int __init hashlimit_mt_init(void)
 {
        int err;
 
+       err = register_pernet_subsys(&hashlimit_net_ops);
+       if (err < 0)
+               return err;
        err = xt_register_matches(hashlimit_mt_reg,
              ARRAY_SIZE(hashlimit_mt_reg));
        if (err < 0)
@@ -974,41 +1029,21 @@ static int __init hashlimit_mt_init(void)
                printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n");
                goto err2;
        }
-       hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", init_net.proc_net);
-       if (!hashlimit_procdir4) {
-               printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
-                               "entry\n");
-               goto err3;
-       }
-       err = 0;
-#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
-       hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", init_net.proc_net);
-       if (!hashlimit_procdir6) {
-               printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
-                               "entry\n");
-               err = -ENOMEM;
-       }
-#endif
-       if (!err)
-               return 0;
-       remove_proc_entry("ipt_hashlimit", init_net.proc_net);
-err3:
-       kmem_cache_destroy(hashlimit_cachep);
+       return 0;
+
 err2:
        xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
 err1:
+       unregister_pernet_subsys(&hashlimit_net_ops);
        return err;
 
 }
 
 static void __exit hashlimit_mt_exit(void)
 {
-       remove_proc_entry("ipt_hashlimit", init_net.proc_net);
-#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
-       remove_proc_entry("ip6t_hashlimit", init_net.proc_net);
-#endif
        kmem_cache_destroy(hashlimit_cachep);
        xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
+       unregister_pernet_subsys(&hashlimit_net_ops);
 }
 
 module_init(hashlimit_mt_init);
index 4d1a41b..4169e20 100644 (file)
@@ -334,7 +334,7 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,
                        if (info->flags & XT_OSF_LOG)
                                nf_log_packet(p->family, p->hooknum, skb,
                                        p->in, p->out, NULL,
-                                       "%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
+                                       "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n",
                                        f->genre, f->version, f->subtype,
                                        &ip->saddr, ntohs(tcp->source),
                                        &ip->daddr, ntohs(tcp->dest),
@@ -349,7 +349,7 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,
 
        if (!fcount && (info->flags & XT_OSF_LOG))
                nf_log_packet(p->family, p->hooknum, skb, p->in, p->out, NULL,
-                       "Remote OS is not known: %pi4:%u -> %pi4:%u\n",
+                       "Remote OS is not known: %pI4:%u -> %pI4:%u\n",
                                &ip->saddr, ntohs(tcp->source),
                                &ip->daddr, ntohs(tcp->dest));
 
index fc70a49..2033331 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/skbuff.h>
 #include <linux/inet.h>
 #include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_recent.h>
@@ -78,37 +79,40 @@ struct recent_table {
        struct list_head        iphash[0];
 };
 
-static LIST_HEAD(tables);
+struct recent_net {
+       struct list_head        tables;
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry   *xt_recent;
+#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
+       struct proc_dir_entry   *ipt_recent;
+#endif
+#endif
+};
+
+static int recent_net_id;
+static inline struct recent_net *recent_pernet(struct net *net)
+{
+       return net_generic(net, recent_net_id);
+}
+
 static DEFINE_SPINLOCK(recent_lock);
 static DEFINE_MUTEX(recent_mutex);
 
 #ifdef CONFIG_PROC_FS
-#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
-static struct proc_dir_entry *proc_old_dir;
-#endif
-static struct proc_dir_entry *recent_proc_dir;
 static const struct file_operations recent_old_fops, recent_mt_fops;
 #endif
 
-static u_int32_t hash_rnd;
-static bool hash_rnd_initted;
+static u_int32_t hash_rnd __read_mostly;
+static bool hash_rnd_inited __read_mostly;
 
-static unsigned int recent_entry_hash4(const union nf_inet_addr *addr)
+static inline unsigned int recent_entry_hash4(const union nf_inet_addr *addr)
 {
-       if (!hash_rnd_initted) {
-               get_random_bytes(&hash_rnd, sizeof(hash_rnd));
-               hash_rnd_initted = true;
-       }
        return jhash_1word((__force u32)addr->ip, hash_rnd) &
               (ip_list_hash_size - 1);
 }
 
-static unsigned int recent_entry_hash6(const union nf_inet_addr *addr)
+static inline unsigned int recent_entry_hash6(const union nf_inet_addr *addr)
 {
-       if (!hash_rnd_initted) {
-               get_random_bytes(&hash_rnd, sizeof(hash_rnd));
-               hash_rnd_initted = true;
-       }
        return jhash2((u32 *)addr->ip6, ARRAY_SIZE(addr->ip6), hash_rnd) &
               (ip_list_hash_size - 1);
 }
@@ -180,11 +184,12 @@ static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
        list_move_tail(&e->lru_list, &t->lru_list);
 }
 
-static struct recent_table *recent_table_lookup(const char *name)
+static struct recent_table *recent_table_lookup(struct recent_net *recent_net,
+                                               const char *name)
 {
        struct recent_table *t;
 
-       list_for_each_entry(t, &tables, list)
+       list_for_each_entry(t, &recent_net->tables, list)
                if (!strcmp(t->name, name))
                        return t;
        return NULL;
@@ -203,6 +208,8 @@ static void recent_table_flush(struct recent_table *t)
 static bool
 recent_mt(const struct sk_buff *skb, const struct xt_match_param *par)
 {
+       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct recent_net *recent_net = recent_pernet(net);
        const struct xt_recent_mtinfo *info = par->matchinfo;
        struct recent_table *t;
        struct recent_entry *e;
@@ -235,7 +242,7 @@ recent_mt(const struct sk_buff *skb, const struct xt_match_param *par)
                ttl++;
 
        spin_lock_bh(&recent_lock);
-       t = recent_table_lookup(info->name);
+       t = recent_table_lookup(recent_net, info->name);
        e = recent_entry_lookup(t, &addr, par->match->family,
                                (info->check_set & XT_RECENT_TTL) ? ttl : 0);
        if (e == NULL) {
@@ -279,6 +286,7 @@ out:
 
 static bool recent_mt_check(const struct xt_mtchk_param *par)
 {
+       struct recent_net *recent_net = recent_pernet(par->net);
        const struct xt_recent_mtinfo *info = par->matchinfo;
        struct recent_table *t;
 #ifdef CONFIG_PROC_FS
@@ -287,6 +295,10 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
        unsigned i;
        bool ret = false;
 
+       if (unlikely(!hash_rnd_inited)) {
+               get_random_bytes(&hash_rnd, sizeof(hash_rnd));
+               hash_rnd_inited = true;
+       }
        if (hweight8(info->check_set &
                     (XT_RECENT_SET | XT_RECENT_REMOVE |
                      XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1)
@@ -301,7 +313,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
                return false;
 
        mutex_lock(&recent_mutex);
-       t = recent_table_lookup(info->name);
+       t = recent_table_lookup(recent_net, info->name);
        if (t != NULL) {
                t->refcnt++;
                ret = true;
@@ -318,7 +330,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
        for (i = 0; i < ip_list_hash_size; i++)
                INIT_LIST_HEAD(&t->iphash[i]);
 #ifdef CONFIG_PROC_FS
-       pde = proc_create_data(t->name, ip_list_perms, recent_proc_dir,
+       pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent,
                  &recent_mt_fops, t);
        if (pde == NULL) {
                kfree(t);
@@ -327,10 +339,10 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
        pde->uid = ip_list_uid;
        pde->gid = ip_list_gid;
 #ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
-       pde = proc_create_data(t->name, ip_list_perms, proc_old_dir,
+       pde = proc_create_data(t->name, ip_list_perms, recent_net->ipt_recent,
                      &recent_old_fops, t);
        if (pde == NULL) {
-               remove_proc_entry(t->name, proc_old_dir);
+               remove_proc_entry(t->name, recent_net->xt_recent);
                kfree(t);
                goto out;
        }
@@ -339,7 +351,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
 #endif
 #endif
        spin_lock_bh(&recent_lock);
-       list_add_tail(&t->list, &tables);
+       list_add_tail(&t->list, &recent_net->tables);
        spin_unlock_bh(&recent_lock);
        ret = true;
 out:
@@ -349,20 +361,21 @@ out:
 
 static void recent_mt_destroy(const struct xt_mtdtor_param *par)
 {
+       struct recent_net *recent_net = recent_pernet(par->net);
        const struct xt_recent_mtinfo *info = par->matchinfo;
        struct recent_table *t;
 
        mutex_lock(&recent_mutex);
-       t = recent_table_lookup(info->name);
+       t = recent_table_lookup(recent_net, info->name);
        if (--t->refcnt == 0) {
                spin_lock_bh(&recent_lock);
                list_del(&t->list);
                spin_unlock_bh(&recent_lock);
 #ifdef CONFIG_PROC_FS
 #ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
-               remove_proc_entry(t->name, proc_old_dir);
+               remove_proc_entry(t->name, recent_net->ipt_recent);
 #endif
-               remove_proc_entry(t->name, recent_proc_dir);
+               remove_proc_entry(t->name, recent_net->xt_recent);
 #endif
                recent_table_flush(t);
                kfree(t);
@@ -611,8 +624,65 @@ static const struct file_operations recent_mt_fops = {
        .release = seq_release_private,
        .owner   = THIS_MODULE,
 };
+
+static int __net_init recent_proc_net_init(struct net *net)
+{
+       struct recent_net *recent_net = recent_pernet(net);
+
+       recent_net->xt_recent = proc_mkdir("xt_recent", net->proc_net);
+       if (!recent_net->xt_recent)
+               return -ENOMEM;
+#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
+       recent_net->ipt_recent = proc_mkdir("ipt_recent", net->proc_net);
+       if (!recent_net->ipt_recent) {
+               proc_net_remove(net, "xt_recent");
+               return -ENOMEM;
+       }
+#endif
+       return 0;
+}
+
+static void __net_exit recent_proc_net_exit(struct net *net)
+{
+#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
+       proc_net_remove(net, "ipt_recent");
+#endif
+       proc_net_remove(net, "xt_recent");
+}
+#else
+static inline int recent_proc_net_init(struct net *net)
+{
+       return 0;
+}
+
+static inline void recent_proc_net_exit(struct net *net)
+{
+}
 #endif /* CONFIG_PROC_FS */
 
+static int __net_init recent_net_init(struct net *net)
+{
+       struct recent_net *recent_net = recent_pernet(net);
+
+       INIT_LIST_HEAD(&recent_net->tables);
+       return recent_proc_net_init(net);
+}
+
+static void __net_exit recent_net_exit(struct net *net)
+{
+       struct recent_net *recent_net = recent_pernet(net);
+
+       BUG_ON(!list_empty(&recent_net->tables));
+       recent_proc_net_exit(net);
+}
+
+static struct pernet_operations recent_net_ops = {
+       .init   = recent_net_init,
+       .exit   = recent_net_exit,
+       .id     = &recent_net_id,
+       .size   = sizeof(struct recent_net),
+};
+
 static struct xt_match recent_mt_reg[] __read_mostly = {
        {
                .name       = "recent",
@@ -644,39 +714,19 @@ static int __init recent_mt_init(void)
                return -EINVAL;
        ip_list_hash_size = 1 << fls(ip_list_tot);
 
-       err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
-#ifdef CONFIG_PROC_FS
+       err = register_pernet_subsys(&recent_net_ops);
        if (err)
                return err;
-       recent_proc_dir = proc_mkdir("xt_recent", init_net.proc_net);
-       if (recent_proc_dir == NULL) {
-               xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
-               err = -ENOMEM;
-       }
-#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
-       if (err < 0)
-               return err;
-       proc_old_dir = proc_mkdir("ipt_recent", init_net.proc_net);
-       if (proc_old_dir == NULL) {
-               remove_proc_entry("xt_recent", init_net.proc_net);
-               xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
-               err = -ENOMEM;
-       }
-#endif
-#endif
+       err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
+       if (err)
+               unregister_pernet_subsys(&recent_net_ops);
        return err;
 }
 
 static void __exit recent_mt_exit(void)
 {
-       BUG_ON(!list_empty(&tables));
        xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
-#ifdef CONFIG_PROC_FS
-#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
-       remove_proc_entry("ipt_recent", init_net.proc_net);
-#endif
-       remove_proc_entry("xt_recent", init_net.proc_net);
-#endif
+       unregister_pernet_subsys(&recent_net_ops);
 }
 
 module_init(recent_mt_init);