Merge branch 'master' of git://1984.lsi.us.es/nf-next
authorDavid S. Miller <davem@davemloft.net>
Sat, 16 Jun 2012 22:23:35 +0000 (15:23 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 16 Jun 2012 22:23:35 +0000 (15:23 -0700)
Pablo says:

====================
This is the second batch of Netfilter updates for net-next. It contains the
kernel changes for the new user-space connection tracking helper
infrastructure.

More details on this infrastructure are provides here:
http://lwn.net/Articles/500196/

Still, I plan to provide some official documentation through the
conntrack-tools user manual on how to setup user-space utilities for this.
So far, it provides two helper in user-space, one for NFSv3 and another for
Oracle/SQLnet/TNS. Yet in my TODO list.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
39 files changed:
include/linux/netfilter.h
include/linux/netfilter/Kbuild
include/linux/netfilter/nf_conntrack_sip.h
include/linux/netfilter/nfnetlink.h
include/linux/netfilter/nfnetlink_conntrack.h
include/linux/netfilter/nfnetlink_cthelper.h [new file with mode: 0644]
include/linux/netfilter/nfnetlink_queue.h
include/linux/netfilter_ipv4.h
include/linux/netfilter_ipv6.h
include/net/netfilter/nf_conntrack.h
include/net/netfilter/nf_conntrack_expect.h
include/net/netfilter/nf_conntrack_extend.h
include/net/netfilter/nf_conntrack_helper.h
include/net/netfilter/nf_nat_helper.h
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv4/netfilter/nf_nat_amanda.c
net/ipv4/netfilter/nf_nat_h323.c
net/ipv4/netfilter/nf_nat_helper.c
net/ipv4/netfilter/nf_nat_pptp.c
net/ipv4/netfilter/nf_nat_tftp.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/netfilter/Kconfig
net/netfilter/Makefile
net/netfilter/core.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_extend.c
net/netfilter/nf_conntrack_ftp.c
net/netfilter/nf_conntrack_h323_main.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_conntrack_irc.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_pptp.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_sane.c
net/netfilter/nf_conntrack_sip.c
net/netfilter/nf_conntrack_tftp.c
net/netfilter/nfnetlink_cthelper.c [new file with mode: 0644]
net/netfilter/nfnetlink_queue.c
net/netfilter/xt_CT.c

index 4541f33..dca19e6 100644 (file)
@@ -393,6 +393,18 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
 extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu;
 extern void nf_ct_attach(struct sk_buff *, struct sk_buff *);
 extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu;
+
+struct nf_conn;
+struct nlattr;
+
+struct nfq_ct_hook {
+       size_t (*build_size)(const struct nf_conn *ct);
+       int (*build)(struct sk_buff *skb, struct nf_conn *ct);
+       int (*parse)(const struct nlattr *attr, struct nf_conn *ct);
+       void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct,
+                          u32 ctinfo, int off);
+};
+extern struct nfq_ct_hook *nfq_ct_hook;
 #else
 static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
 #endif
index 1697036..874ae8f 100644 (file)
@@ -10,6 +10,7 @@ header-y += nfnetlink.h
 header-y += nfnetlink_acct.h
 header-y += nfnetlink_compat.h
 header-y += nfnetlink_conntrack.h
+header-y += nfnetlink_cthelper.h
 header-y += nfnetlink_cttimeout.h
 header-y += nfnetlink_log.h
 header-y += nfnetlink_queue.h
index 0ce91d5..0dfc8b7 100644 (file)
@@ -2,6 +2,8 @@
 #define __NF_CONNTRACK_SIP_H__
 #ifdef __KERNEL__
 
+#include <net/netfilter/nf_conntrack_expect.h>
+
 #define SIP_PORT       5060
 #define SIP_TIMEOUT    3600
 
index a1048c1..18341cd 100644 (file)
@@ -50,7 +50,8 @@ struct nfgenmsg {
 #define NFNL_SUBSYS_IPSET              6
 #define NFNL_SUBSYS_ACCT               7
 #define NFNL_SUBSYS_CTNETLINK_TIMEOUT  8
-#define NFNL_SUBSYS_COUNT              9
+#define NFNL_SUBSYS_CTHELPER           9
+#define NFNL_SUBSYS_COUNT              10
 
 #ifdef __KERNEL__
 
index e58e4b9..7688833 100644 (file)
@@ -191,6 +191,7 @@ enum ctattr_expect_nat {
 enum ctattr_help {
        CTA_HELP_UNSPEC,
        CTA_HELP_NAME,
+       CTA_HELP_INFO,
        __CTA_HELP_MAX
 };
 #define CTA_HELP_MAX (__CTA_HELP_MAX - 1)
diff --git a/include/linux/netfilter/nfnetlink_cthelper.h b/include/linux/netfilter/nfnetlink_cthelper.h
new file mode 100644 (file)
index 0000000..33659f6
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _NFNL_CTHELPER_H_
+#define _NFNL_CTHELPER_H_
+
+#define NFCT_HELPER_STATUS_DISABLED    0
+#define NFCT_HELPER_STATUS_ENABLED     1
+
+enum nfnl_acct_msg_types {
+       NFNL_MSG_CTHELPER_NEW,
+       NFNL_MSG_CTHELPER_GET,
+       NFNL_MSG_CTHELPER_DEL,
+       NFNL_MSG_CTHELPER_MAX
+};
+
+enum nfnl_cthelper_type {
+       NFCTH_UNSPEC,
+       NFCTH_NAME,
+       NFCTH_TUPLE,
+       NFCTH_QUEUE_NUM,
+       NFCTH_POLICY,
+       NFCTH_PRIV_DATA_LEN,
+       NFCTH_STATUS,
+       __NFCTH_MAX
+};
+#define NFCTH_MAX (__NFCTH_MAX - 1)
+
+enum nfnl_cthelper_policy_type {
+       NFCTH_POLICY_SET_UNSPEC,
+       NFCTH_POLICY_SET_NUM,
+       NFCTH_POLICY_SET,
+       NFCTH_POLICY_SET1       = NFCTH_POLICY_SET,
+       NFCTH_POLICY_SET2,
+       NFCTH_POLICY_SET3,
+       NFCTH_POLICY_SET4,
+       __NFCTH_POLICY_SET_MAX
+};
+#define NFCTH_POLICY_SET_MAX (__NFCTH_POLICY_SET_MAX - 1)
+
+enum nfnl_cthelper_pol_type {
+       NFCTH_POLICY_UNSPEC,
+       NFCTH_POLICY_NAME,
+       NFCTH_POLICY_EXPECT_MAX,
+       NFCTH_POLICY_EXPECT_TIMEOUT,
+       __NFCTH_POLICY_MAX
+};
+#define NFCTH_POLICY_MAX (__NFCTH_POLICY_MAX - 1)
+
+enum nfnl_cthelper_tuple_type {
+       NFCTH_TUPLE_UNSPEC,
+       NFCTH_TUPLE_L3PROTONUM,
+       NFCTH_TUPLE_L4PROTONUM,
+       __NFCTH_TUPLE_MAX,
+};
+#define NFCTH_TUPLE_MAX (__NFCTH_TUPLE_MAX - 1)
+
+#endif /* _NFNL_CTHELPER_H */
index a6c1dda..e0d8fd8 100644 (file)
@@ -42,6 +42,8 @@ enum nfqnl_attr_type {
        NFQA_IFINDEX_PHYSOUTDEV,        /* __u32 ifindex */
        NFQA_HWADDR,                    /* nfqnl_msg_packet_hw */
        NFQA_PAYLOAD,                   /* opaque data payload */
+       NFQA_CT,                        /* nf_conntrack_netlink.h */
+       NFQA_CT_INFO,                   /* enum ip_conntrack_info */
 
        __NFQA_MAX
 };
@@ -92,5 +94,6 @@ enum nfqnl_attr_config {
 
 /* Flags for NFQA_CFG_FLAGS */
 #define NFQA_CFG_F_FAIL_OPEN                   (1 << 0)
+#define NFQA_CFG_F_CONNTRACK                   (1 << 1)
 
 #endif /* _NFNETLINK_QUEUE_H */
index fa0946c..e2b1280 100644 (file)
@@ -66,6 +66,7 @@ enum nf_ip_hook_priorities {
        NF_IP_PRI_SECURITY = 50,
        NF_IP_PRI_NAT_SRC = 100,
        NF_IP_PRI_SELINUX_LAST = 225,
+       NF_IP_PRI_CONNTRACK_HELPER = 300,
        NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
        NF_IP_PRI_LAST = INT_MAX,
 };
index 57c0251..7c8a513 100644 (file)
@@ -71,6 +71,7 @@ enum nf_ip6_hook_priorities {
        NF_IP6_PRI_SECURITY = 50,
        NF_IP6_PRI_NAT_SRC = 100,
        NF_IP6_PRI_SELINUX_LAST = 225,
+       NF_IP6_PRI_CONNTRACK_HELPER = 300,
        NF_IP6_PRI_LAST = INT_MAX,
 };
 
index cce7f6a..f1494fe 100644 (file)
@@ -39,36 +39,6 @@ union nf_conntrack_expect_proto {
        /* insert expect proto private data here */
 };
 
-/* Add protocol helper include file here */
-#include <linux/netfilter/nf_conntrack_ftp.h>
-#include <linux/netfilter/nf_conntrack_pptp.h>
-#include <linux/netfilter/nf_conntrack_h323.h>
-#include <linux/netfilter/nf_conntrack_sane.h>
-#include <linux/netfilter/nf_conntrack_sip.h>
-
-/* per conntrack: application helper private data */
-union nf_conntrack_help {
-       /* insert conntrack helper private data (master) here */
-#if defined(CONFIG_NF_CONNTRACK_FTP) || defined(CONFIG_NF_CONNTRACK_FTP_MODULE)
-       struct nf_ct_ftp_master ct_ftp_info;
-#endif
-#if defined(CONFIG_NF_CONNTRACK_PPTP) || \
-    defined(CONFIG_NF_CONNTRACK_PPTP_MODULE)
-       struct nf_ct_pptp_master ct_pptp_info;
-#endif
-#if defined(CONFIG_NF_CONNTRACK_H323) || \
-    defined(CONFIG_NF_CONNTRACK_H323_MODULE)
-       struct nf_ct_h323_master ct_h323_info;
-#endif
-#if defined(CONFIG_NF_CONNTRACK_SANE) || \
-    defined(CONFIG_NF_CONNTRACK_SANE_MODULE)
-       struct nf_ct_sane_master ct_sane_info;
-#endif
-#if defined(CONFIG_NF_CONNTRACK_SIP) || defined(CONFIG_NF_CONNTRACK_SIP_MODULE)
-       struct nf_ct_sip_master ct_sip_info;
-#endif
-};
-
 #include <linux/types.h>
 #include <linux/skbuff.h>
 #include <linux/timer.h>
@@ -89,12 +59,13 @@ struct nf_conn_help {
        /* Helper. if any */
        struct nf_conntrack_helper __rcu *helper;
 
-       union nf_conntrack_help help;
-
        struct hlist_head expectations;
 
        /* Current number of expected connections */
        u8 expecting[NF_CT_MAX_EXPECT_CLASSES];
+
+       /* private helper information. */
+       char data[];
 };
 
 #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
index 4619caa..983f002 100644 (file)
@@ -59,10 +59,12 @@ static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
        return nf_ct_net(exp->master);
 }
 
+#define NF_CT_EXP_POLICY_NAME_LEN      16
+
 struct nf_conntrack_expect_policy {
        unsigned int    max_expected;
        unsigned int    timeout;
-       const char      *name;
+       char            name[NF_CT_EXP_POLICY_NAME_LEN];
 };
 
 #define NF_CT_EXPECT_CLASS_DEFAULT     0
index 96755c3..8b4d1fc 100644 (file)
@@ -80,10 +80,13 @@ static inline void nf_ct_ext_free(struct nf_conn *ct)
 }
 
 /* Add this type, returns pointer to data or NULL. */
-void *
-__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp);
+void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id,
+                            size_t var_alloc_len, gfp_t gfp);
+
 #define nf_ct_ext_add(ct, id, gfp) \
-       ((id##_TYPE *)__nf_ct_ext_add((ct), (id), (gfp)))
+       ((id##_TYPE *)__nf_ct_ext_add_length((ct), (id), 0, (gfp)))
+#define nf_ct_ext_add_length(ct, id, len, gfp) \
+       ((id##_TYPE *)__nf_ct_ext_add_length((ct), (id), (len), (gfp)))
 
 #define NF_CT_EXT_F_PREALLOC   0x0001
 
index 1d18894..9aad956 100644 (file)
 #define _NF_CONNTRACK_HELPER_H
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_extend.h>
+#include <net/netfilter/nf_conntrack_expect.h>
 
 struct module;
 
+enum nf_ct_helper_flags {
+       NF_CT_HELPER_F_USERSPACE        = (1 << 0),
+       NF_CT_HELPER_F_CONFIGURED       = (1 << 1),
+};
+
 #define NF_CT_HELPER_NAME_LEN  16
 
 struct nf_conntrack_helper {
        struct hlist_node hnode;        /* Internal use. */
 
-       const char *name;               /* name of the module */
+       char name[NF_CT_HELPER_NAME_LEN]; /* name of the module */
        struct module *me;              /* pointer to self */
        const struct nf_conntrack_expect_policy *expect_policy;
 
+       /* length of internal data, ie. sizeof(struct nf_ct_*_master) */
+       size_t data_len;
+
        /* Tuple of things we will help (compared against server response) */
        struct nf_conntrack_tuple tuple;
 
@@ -35,8 +44,12 @@ struct nf_conntrack_helper {
 
        void (*destroy)(struct nf_conn *ct);
 
+       int (*from_nlattr)(struct nlattr *attr, struct nf_conn *ct);
        int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct);
        unsigned int expect_class_max;
+
+       unsigned int flags;
+       unsigned int queue_num;         /* For user-space helpers. */
 };
 
 extern struct nf_conntrack_helper *
@@ -48,7 +61,7 @@ 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 struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, struct nf_conntrack_helper *helper, gfp_t gfp);
 
 extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
                                     gfp_t flags);
@@ -60,6 +73,15 @@ static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct)
        return nf_ct_ext_find(ct, NF_CT_EXT_HELPER);
 }
 
+static inline void *nfct_help_data(const struct nf_conn *ct)
+{
+       struct nf_conn_help *help;
+
+       help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER);
+
+       return (void *)help->data;
+}
+
 extern int nf_conntrack_helper_init(struct net *net);
 extern void nf_conntrack_helper_fini(struct net *net);
 
@@ -82,4 +104,7 @@ nf_ct_helper_expectfn_find_by_name(const char *name);
 struct nf_ct_helper_expectfn *
 nf_ct_helper_expectfn_find_by_symbol(const void *symbol);
 
+extern struct hlist_head *nf_ct_helper_hash;
+extern unsigned int nf_ct_helper_hsize;
+
 #endif /*_NF_CONNTRACK_HELPER_H*/
index 02bb6c2..7d8fb7b 100644 (file)
@@ -54,4 +54,8 @@ extern void nf_nat_follow_master(struct nf_conn *ct,
 extern s16 nf_nat_get_offset(const struct nf_conn *ct,
                             enum ip_conntrack_dir dir,
                             u32 seq);
+
+extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
+                                 u32 dir, int off);
+
 #endif
index d79b961..e7ff2dc 100644 (file)
@@ -95,11 +95,11 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
        return NF_ACCEPT;
 }
 
-static unsigned int ipv4_confirm(unsigned int hooknum,
-                                struct sk_buff *skb,
-                                const struct net_device *in,
-                                const struct net_device *out,
-                                int (*okfn)(struct sk_buff *))
+static unsigned int ipv4_helper(unsigned int hooknum,
+                               struct sk_buff *skb,
+                               const struct net_device *in,
+                               const struct net_device *out,
+                               int (*okfn)(struct sk_buff *))
 {
        struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
@@ -110,24 +110,38 @@ static unsigned int ipv4_confirm(unsigned int hooknum,
        /* This is where we call the helper: as the packet goes out. */
        ct = nf_ct_get(skb, &ctinfo);
        if (!ct || ctinfo == IP_CT_RELATED_REPLY)
-               goto out;
+               return NF_ACCEPT;
 
        help = nfct_help(ct);
        if (!help)
-               goto out;
+               return NF_ACCEPT;
 
        /* rcu_read_lock()ed by nf_hook_slow */
        helper = rcu_dereference(help->helper);
        if (!helper)
-               goto out;
+               return NF_ACCEPT;
 
        ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
                           ct, ctinfo);
-       if (ret != NF_ACCEPT) {
+       if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
                nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
                              "nf_ct_%s: dropping packet", helper->name);
-               return ret;
        }
+       return ret;
+}
+
+static unsigned int ipv4_confirm(unsigned int hooknum,
+                                struct sk_buff *skb,
+                                const struct net_device *in,
+                                const struct net_device *out,
+                                int (*okfn)(struct sk_buff *))
+{
+       struct nf_conn *ct;
+       enum ip_conntrack_info ctinfo;
+
+       ct = nf_ct_get(skb, &ctinfo);
+       if (!ct || ctinfo == IP_CT_RELATED_REPLY)
+               goto out;
 
        /* adjust seqs for loopback traffic only in outgoing direction */
        if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
@@ -185,6 +199,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
                .priority       = NF_IP_PRI_CONNTRACK,
        },
        {
+               .hook           = ipv4_helper,
+               .owner          = THIS_MODULE,
+               .pf             = NFPROTO_IPV4,
+               .hooknum        = NF_INET_POST_ROUTING,
+               .priority       = NF_IP_PRI_CONNTRACK_HELPER,
+       },
+       {
                .hook           = ipv4_confirm,
                .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
@@ -192,6 +213,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM,
        },
        {
+               .hook           = ipv4_helper,
+               .owner          = THIS_MODULE,
+               .pf             = NFPROTO_IPV4,
+               .hooknum        = NF_INET_LOCAL_IN,
+               .priority       = NF_IP_PRI_CONNTRACK_HELPER,
+       },
+       {
                .hook           = ipv4_confirm,
                .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
index 7b22382..3c04d24 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/udp.h>
 
-#include <net/netfilter/nf_nat_helper.h>
-#include <net/netfilter/nf_nat_rule.h>
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
 #include <linux/netfilter/nf_conntrack_amanda.h>
 
 MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
index cad29c1..c6784a1 100644 (file)
@@ -95,7 +95,7 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
                        unsigned char **data,
                        TransportAddress *taddr, int count)
 {
-       const struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       const struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        int i;
        __be16 port;
@@ -178,7 +178,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
                        struct nf_conntrack_expect *rtp_exp,
                        struct nf_conntrack_expect *rtcp_exp)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        int i;
        u_int16_t nated_port;
@@ -330,7 +330,7 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct,
                    TransportAddress *taddr, __be16 port,
                    struct nf_conntrack_expect *exp)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        u_int16_t nated_port = ntohs(port);
 
@@ -419,7 +419,7 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct,
                    unsigned char **data, TransportAddress *taddr, int idx,
                    __be16 port, struct nf_conntrack_expect *exp)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        u_int16_t nated_port = ntohs(port);
        union nf_inet_addr addr;
index af65958..2e59ad0 100644 (file)
@@ -153,6 +153,19 @@ void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
 }
 EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust);
 
+void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
+                          u32 ctinfo, int off)
+{
+       const struct tcphdr *th;
+
+       if (nf_ct_protonum(ct) != IPPROTO_TCP)
+               return;
+
+       th = (struct tcphdr *)(skb_network_header(skb)+ ip_hdrlen(skb));
+       nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
+}
+EXPORT_SYMBOL_GPL(nf_nat_tcp_seq_adjust);
+
 static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data,
                        int datalen, __sum16 *check, int oldlen)
 {
index c273d58..3881408 100644 (file)
@@ -49,7 +49,7 @@ static void pptp_nat_expected(struct nf_conn *ct,
        const struct nf_nat_pptp *nat_pptp_info;
        struct nf_nat_ipv4_range range;
 
-       ct_pptp_info = &nfct_help(master)->help.ct_pptp_info;
+       ct_pptp_info = nfct_help_data(master);
        nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info;
 
        /* And here goes the grand finale of corrosion... */
@@ -123,7 +123,7 @@ pptp_outbound_pkt(struct sk_buff *skb,
        __be16 new_callid;
        unsigned int cid_off;
 
-       ct_pptp_info  = &nfct_help(ct)->help.ct_pptp_info;
+       ct_pptp_info = nfct_help_data(ct);
        nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info;
 
        new_callid = ct_pptp_info->pns_call_id;
@@ -192,7 +192,7 @@ pptp_exp_gre(struct nf_conntrack_expect *expect_orig,
        struct nf_ct_pptp_master *ct_pptp_info;
        struct nf_nat_pptp *nat_pptp_info;
 
-       ct_pptp_info  = &nfct_help(ct)->help.ct_pptp_info;
+       ct_pptp_info = nfct_help_data(ct);
        nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info;
 
        /* save original PAC call ID in nat_info */
index a2901bf..9dbb8d2 100644 (file)
@@ -8,10 +8,10 @@
 #include <linux/module.h>
 #include <linux/udp.h>
 
-#include <net/netfilter/nf_nat_helper.h>
-#include <net/netfilter/nf_nat_rule.h>
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
 #include <linux/netfilter/nf_conntrack_tftp.h>
 
 MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
index fca10da..4794f96 100644 (file)
@@ -143,11 +143,11 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
        return NF_ACCEPT;
 }
 
-static unsigned int ipv6_confirm(unsigned int hooknum,
-                                struct sk_buff *skb,
-                                const struct net_device *in,
-                                const struct net_device *out,
-                                int (*okfn)(struct sk_buff *))
+static unsigned int ipv6_helper(unsigned int hooknum,
+                               struct sk_buff *skb,
+                               const struct net_device *in,
+                               const struct net_device *out,
+                               int (*okfn)(struct sk_buff *))
 {
        struct nf_conn *ct;
        const struct nf_conn_help *help;
@@ -161,15 +161,15 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
        /* This is where we call the helper: as the packet goes out. */
        ct = nf_ct_get(skb, &ctinfo);
        if (!ct || ctinfo == IP_CT_RELATED_REPLY)
-               goto out;
+               return NF_ACCEPT;
 
        help = nfct_help(ct);
        if (!help)
-               goto out;
+               return NF_ACCEPT;
        /* rcu_read_lock()ed by nf_hook_slow */
        helper = rcu_dereference(help->helper);
        if (!helper)
-               goto out;
+               return NF_ACCEPT;
 
        protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum,
                                         skb->len - extoff);
@@ -179,12 +179,19 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
        }
 
        ret = helper->help(skb, protoff, ct, ctinfo);
-       if (ret != NF_ACCEPT) {
+       if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
                nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
                              "nf_ct_%s: dropping packet", helper->name);
-               return ret;
        }
-out:
+       return ret;
+}
+
+static unsigned int ipv6_confirm(unsigned int hooknum,
+                                struct sk_buff *skb,
+                                const struct net_device *in,
+                                const struct net_device *out,
+                                int (*okfn)(struct sk_buff *))
+{
        /* We've seen it coming out the other side: confirm it */
        return nf_conntrack_confirm(skb);
 }
@@ -254,6 +261,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
                .priority       = NF_IP6_PRI_CONNTRACK,
        },
        {
+               .hook           = ipv6_helper,
+               .owner          = THIS_MODULE,
+               .pf             = NFPROTO_IPV6,
+               .hooknum        = NF_INET_POST_ROUTING,
+               .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
+       },
+       {
                .hook           = ipv6_confirm,
                .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
@@ -261,6 +275,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
                .priority       = NF_IP6_PRI_LAST,
        },
        {
+               .hook           = ipv6_helper,
+               .owner          = THIS_MODULE,
+               .pf             = NFPROTO_IPV6,
+               .hooknum        = NF_INET_LOCAL_IN,
+               .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
+       },
+       {
                .hook           = ipv6_confirm,
                .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
index 209c1ed..aae6c62 100644 (file)
@@ -12,6 +12,14 @@ tristate "Netfilter NFACCT over NFNETLINK interface"
          If this option is enabled, the kernel will include support
          for extended accounting via NFNETLINK.
 
+config NETFILTER_NETLINK_CTHELPER
+tristate "Netfilter CTHELPER over NFNETLINK interface"
+       depends on NETFILTER_ADVANCED
+       select NETFILTER_NETLINK
+       help
+         If this option is enabled, the kernel will include support
+         for user-space connection tracking helpers via NFNETLINK.
+
 config NETFILTER_NETLINK_QUEUE
        tristate "Netfilter NFQUEUE over NFNETLINK interface"
        depends on NETFILTER_ADVANCED
index 4e7960c..2f3bc0f 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_NETFILTER) = netfilter.o
 
 obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
 obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o
+obj-$(CONFIG_NETFILTER_NETLINK_CTHELPER) += nfnetlink_cthelper.o
 obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
 obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
 
index e19f365..7eef845 100644 (file)
@@ -264,6 +264,10 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct)
        rcu_read_unlock();
 }
 EXPORT_SYMBOL(nf_conntrack_destroy);
+
+struct nfq_ct_hook *nfq_ct_hook;
+EXPORT_SYMBOL_GPL(nfq_ct_hook);
+
 #endif /* CONFIG_NF_CONNTRACK */
 
 #ifdef CONFIG_PROC_FS
index 1ee2082..cf48755 100644 (file)
@@ -819,7 +819,8 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
                __set_bit(IPS_EXPECTED_BIT, &ct->status);
                ct->master = exp->master;
                if (exp->helper) {
-                       help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
+                       help = nf_ct_helper_ext_add(ct, exp->helper,
+                                                   GFP_ATOMIC);
                        if (help)
                                rcu_assign_pointer(help->helper, exp->helper);
                }
index 641ff5f..1a95459 100644 (file)
@@ -44,7 +44,8 @@ void __nf_ct_ext_destroy(struct nf_conn *ct)
 EXPORT_SYMBOL(__nf_ct_ext_destroy);
 
 static void *
-nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp)
+nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id,
+                size_t var_alloc_len, gfp_t gfp)
 {
        unsigned int off, len;
        struct nf_ct_ext_type *t;
@@ -54,8 +55,8 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp)
        t = rcu_dereference(nf_ct_ext_types[id]);
        BUG_ON(t == NULL);
        off = ALIGN(sizeof(struct nf_ct_ext), t->align);
-       len = off + t->len;
-       alloc_size = t->alloc_size;
+       len = off + t->len + var_alloc_len;
+       alloc_size = t->alloc_size + var_alloc_len;
        rcu_read_unlock();
 
        *ext = kzalloc(alloc_size, gfp);
@@ -68,7 +69,8 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp)
        return (void *)(*ext) + off;
 }
 
-void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
+void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id,
+                            size_t var_alloc_len, gfp_t gfp)
 {
        struct nf_ct_ext *old, *new;
        int i, newlen, newoff;
@@ -79,7 +81,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
 
        old = ct->ext;
        if (!old)
-               return nf_ct_ext_create(&ct->ext, id, gfp);
+               return nf_ct_ext_create(&ct->ext, id, var_alloc_len, gfp);
 
        if (__nf_ct_ext_exist(old, id))
                return NULL;
@@ -89,7 +91,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
        BUG_ON(t == NULL);
 
        newoff = ALIGN(old->len, t->align);
-       newlen = newoff + t->len;
+       newlen = newoff + t->len + var_alloc_len;
        rcu_read_unlock();
 
        new = __krealloc(old, newlen, gfp);
@@ -117,7 +119,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
        memset((void *)new + newoff, 0, newlen - newoff);
        return (void *)new + newoff;
 }
-EXPORT_SYMBOL(__nf_ct_ext_add);
+EXPORT_SYMBOL(__nf_ct_ext_add_length);
 
 static void update_alloc_size(struct nf_ct_ext_type *type)
 {
index 8c5c95c..4bb771d 100644 (file)
@@ -358,7 +358,7 @@ static int help(struct sk_buff *skb,
        u32 seq;
        int dir = CTINFO2DIR(ctinfo);
        unsigned int uninitialized_var(matchlen), uninitialized_var(matchoff);
-       struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info;
+       struct nf_ct_ftp_master *ct_ftp_info = nfct_help_data(ct);
        struct nf_conntrack_expect *exp;
        union nf_inet_addr *daddr;
        struct nf_conntrack_man cmd = {};
@@ -512,7 +512,6 @@ out_update_nl:
 }
 
 static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly;
-static char ftp_names[MAX_PORTS][2][sizeof("ftp-65535")] __read_mostly;
 
 static const struct nf_conntrack_expect_policy ftp_exp_policy = {
        .max_expected   = 1,
@@ -541,7 +540,6 @@ static void nf_conntrack_ftp_fini(void)
 static int __init nf_conntrack_ftp_init(void)
 {
        int i, j = -1, ret = 0;
-       char *tmpname;
 
        ftp_buffer = kmalloc(65536, GFP_KERNEL);
        if (!ftp_buffer)
@@ -556,17 +554,16 @@ static int __init nf_conntrack_ftp_init(void)
                ftp[i][0].tuple.src.l3num = PF_INET;
                ftp[i][1].tuple.src.l3num = PF_INET6;
                for (j = 0; j < 2; j++) {
+                       ftp[i][j].data_len = sizeof(struct nf_ct_ftp_master);
                        ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]);
                        ftp[i][j].tuple.dst.protonum = IPPROTO_TCP;
                        ftp[i][j].expect_policy = &ftp_exp_policy;
                        ftp[i][j].me = THIS_MODULE;
                        ftp[i][j].help = help;
-                       tmpname = &ftp_names[i][j][0];
                        if (ports[i] == FTP_PORT)
-                               sprintf(tmpname, "ftp");
+                               sprintf(ftp[i][j].name, "ftp");
                        else
-                               sprintf(tmpname, "ftp-%d", ports[i]);
-                       ftp[i][j].name = tmpname;
+                               sprintf(ftp[i][j].name, "ftp-%d", ports[i]);
 
                        pr_debug("nf_ct_ftp: registering helper for pf: %d "
                                 "port: %d\n",
index 31f50bc..4283b20 100644 (file)
@@ -114,7 +114,7 @@ static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff,
                         struct nf_conn *ct, enum ip_conntrack_info ctinfo,
                         unsigned char **data, int *datalen, int *dataoff)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        const struct tcphdr *th;
        struct tcphdr _tcph;
@@ -617,6 +617,7 @@ static const struct nf_conntrack_expect_policy h245_exp_policy = {
 static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly = {
        .name                   = "H.245",
        .me                     = THIS_MODULE,
+       .data_len               = sizeof(struct nf_ct_h323_master),
        .tuple.src.l3num        = AF_UNSPEC,
        .tuple.dst.protonum     = IPPROTO_UDP,
        .help                   = h245_help,
@@ -1169,6 +1170,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_q931[] __read_mostly = {
        {
                .name                   = "Q.931",
                .me                     = THIS_MODULE,
+               .data_len               = sizeof(struct nf_ct_h323_master),
                .tuple.src.l3num        = AF_INET,
                .tuple.src.u.tcp.port   = cpu_to_be16(Q931_PORT),
                .tuple.dst.protonum     = IPPROTO_TCP,
@@ -1244,7 +1246,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
                       unsigned char **data,
                       TransportAddress *taddr, int count)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        int ret = 0;
        int i;
@@ -1359,7 +1361,7 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct,
                       enum ip_conntrack_info ctinfo,
                       unsigned char **data, RegistrationRequest *rrq)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int ret;
        typeof(set_ras_addr_hook) set_ras_addr;
 
@@ -1394,7 +1396,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct,
                       enum ip_conntrack_info ctinfo,
                       unsigned char **data, RegistrationConfirm *rcf)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        int ret;
        struct nf_conntrack_expect *exp;
@@ -1443,7 +1445,7 @@ static int process_urq(struct sk_buff *skb, struct nf_conn *ct,
                       enum ip_conntrack_info ctinfo,
                       unsigned char **data, UnregistrationRequest *urq)
 {
-       struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        int ret;
        typeof(set_sig_addr_hook) set_sig_addr;
@@ -1475,7 +1477,7 @@ static int process_arq(struct sk_buff *skb, struct nf_conn *ct,
                       enum ip_conntrack_info ctinfo,
                       unsigned char **data, AdmissionRequest *arq)
 {
-       const struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info;
+       const struct nf_ct_h323_master *info = nfct_help_data(ct);
        int dir = CTINFO2DIR(ctinfo);
        __be16 port;
        union nf_inet_addr addr;
@@ -1742,6 +1744,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = {
        {
                .name                   = "RAS",
                .me                     = THIS_MODULE,
+               .data_len               = sizeof(struct nf_ct_h323_master),
                .tuple.src.l3num        = AF_INET,
                .tuple.src.u.udp.port   = cpu_to_be16(RAS_PORT),
                .tuple.dst.protonum     = IPPROTO_UDP,
@@ -1751,6 +1754,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = {
        {
                .name                   = "RAS",
                .me                     = THIS_MODULE,
+               .data_len               = sizeof(struct nf_ct_h323_master),
                .tuple.src.l3num        = AF_INET6,
                .tuple.src.u.udp.port   = cpu_to_be16(RAS_PORT),
                .tuple.dst.protonum     = IPPROTO_UDP,
index 4fa2ff9..2918ec2 100644 (file)
 #include <net/netfilter/nf_conntrack_extend.h>
 
 static DEFINE_MUTEX(nf_ct_helper_mutex);
-static struct hlist_head *nf_ct_helper_hash __read_mostly;
-static unsigned int nf_ct_helper_hsize __read_mostly;
+struct hlist_head *nf_ct_helper_hash __read_mostly;
+EXPORT_SYMBOL_GPL(nf_ct_helper_hash);
+unsigned int nf_ct_helper_hsize __read_mostly;
+EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
 static unsigned int nf_ct_helper_count __read_mostly;
 
 static bool nf_ct_auto_assign_helper __read_mostly = true;
@@ -161,11 +163,14 @@ nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
 }
 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)
+struct nf_conn_help *
+nf_ct_helper_ext_add(struct nf_conn *ct,
+                    struct nf_conntrack_helper *helper, gfp_t gfp)
 {
        struct nf_conn_help *help;
 
-       help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
+       help = nf_ct_ext_add_length(ct, NF_CT_EXT_HELPER,
+                                   helper->data_len, gfp);
        if (help)
                INIT_HLIST_HEAD(&help->expectations);
        else
@@ -218,13 +223,13 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
        }
 
        if (help == NULL) {
-               help = nf_ct_helper_ext_add(ct, flags);
+               help = nf_ct_helper_ext_add(ct, helper, flags);
                if (help == NULL) {
                        ret = -ENOMEM;
                        goto out;
                }
        } else {
-               memset(&help->help, 0, sizeof(help->help));
+               memset(help->data, 0, helper->data_len);
        }
 
        rcu_assign_pointer(help->helper, helper);
@@ -319,6 +324,9 @@ EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
 
 int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
 {
+       int ret = 0;
+       struct nf_conntrack_helper *cur;
+       struct hlist_node *n;
        unsigned int h = helper_hash(&me->tuple);
 
        BUG_ON(me->expect_policy == NULL);
@@ -326,11 +334,19 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
        BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
 
        mutex_lock(&nf_ct_helper_mutex);
+       hlist_for_each_entry(cur, n, &nf_ct_helper_hash[h], hnode) {
+               if (strncmp(cur->name, me->name, NF_CT_HELPER_NAME_LEN) == 0 &&
+                   cur->tuple.src.l3num == me->tuple.src.l3num &&
+                   cur->tuple.dst.protonum == me->tuple.dst.protonum) {
+                       ret = -EEXIST;
+                       goto out;
+               }
+       }
        hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
        nf_ct_helper_count++;
+out:
        mutex_unlock(&nf_ct_helper_mutex);
-
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
 
index 81366c1..009c52c 100644 (file)
@@ -221,7 +221,6 @@ static int help(struct sk_buff *skb, unsigned int protoff,
 }
 
 static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly;
-static char irc_names[MAX_PORTS][sizeof("irc-65535")] __read_mostly;
 static struct nf_conntrack_expect_policy irc_exp_policy;
 
 static void nf_conntrack_irc_fini(void);
@@ -229,7 +228,6 @@ static void nf_conntrack_irc_fini(void);
 static int __init nf_conntrack_irc_init(void)
 {
        int i, ret;
-       char *tmpname;
 
        if (max_dcc_channels < 1) {
                printk(KERN_ERR "nf_ct_irc: max_dcc_channels must not be zero\n");
@@ -255,12 +253,10 @@ static int __init nf_conntrack_irc_init(void)
                irc[i].me = THIS_MODULE;
                irc[i].help = help;
 
-               tmpname = &irc_names[i][0];
                if (ports[i] == IRC_PORT)
-                       sprintf(tmpname, "irc");
+                       sprintf(irc[i].name, "irc");
                else
-                       sprintf(tmpname, "irc-%u", i);
-               irc[i].name = tmpname;
+                       sprintf(irc[i].name, "irc-%u", i);
 
                ret = nf_conntrack_helper_register(&irc[i]);
                if (ret) {
index 6f4b00a..ae156df 100644 (file)
@@ -46,6 +46,7 @@
 #ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_protocol.h>
+#include <net/netfilter/nf_nat_helper.h>
 #endif
 
 #include <linux/netfilter/nfnetlink.h>
@@ -901,7 +902,8 @@ static const struct nla_policy help_nla_policy[CTA_HELP_MAX+1] = {
 };
 
 static inline int
-ctnetlink_parse_help(const struct nlattr *attr, char **helper_name)
+ctnetlink_parse_help(const struct nlattr *attr, char **helper_name,
+                    struct nlattr **helpinfo)
 {
        struct nlattr *tb[CTA_HELP_MAX+1];
 
@@ -912,6 +914,9 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name)
 
        *helper_name = nla_data(tb[CTA_HELP_NAME]);
 
+       if (tb[CTA_HELP_INFO])
+               *helpinfo = tb[CTA_HELP_INFO];
+
        return 0;
 }
 
@@ -1172,13 +1177,14 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
        struct nf_conntrack_helper *helper;
        struct nf_conn_help *help = nfct_help(ct);
        char *helpname = NULL;
+       struct nlattr *helpinfo = NULL;
        int err;
 
        /* don't change helper of sibling connections */
        if (ct->master)
                return -EBUSY;
 
-       err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
+       err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo);
        if (err < 0)
                return err;
 
@@ -1213,12 +1219,16 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
        }
 
        if (help) {
-               if (help->helper == helper)
+               if (help->helper == helper) {
+                       /* update private helper data if allowed. */
+                       if (helper->from_nlattr && helpinfo)
+                               helper->from_nlattr(helpinfo, ct);
                        return 0;
+               }
                if (help->helper)
                        return -EBUSY;
                /* need to zero data of old helper */
-               memset(&help->help, 0, sizeof(help->help));
+               memset(help->data, 0, help->helper->data_len);
        } else {
                /* we cannot set a helper for an existing conntrack */
                return -EOPNOTSUPP;
@@ -1410,8 +1420,9 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
        rcu_read_lock();
        if (cda[CTA_HELP]) {
                char *helpname = NULL;
-               err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
+               struct nlattr *helpinfo = NULL;
+
+               err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo);
                if (err < 0)
                        goto err2;
 
@@ -1440,11 +1451,14 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
                } else {
                        struct nf_conn_help *help;
 
-                       help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
+                       help = nf_ct_helper_ext_add(ct, helper, GFP_ATOMIC);
                        if (help == NULL) {
                                err = -ENOMEM;
                                goto err2;
                        }
+                       /* set private helper data if allowed. */
+                       if (helper->from_nlattr && helpinfo)
+                               helper->from_nlattr(helpinfo, ct);
 
                        /* not in hash table yet so not strictly necessary */
                        RCU_INIT_POINTER(help->helper, helper);
@@ -1620,6 +1634,143 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
        return err;
 }
 
+#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \
+    defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE)
+static size_t
+ctnetlink_nfqueue_build_size(const struct nf_conn *ct)
+{
+       return 3 * nla_total_size(0) /* CTA_TUPLE_ORIG|REPL|MASTER */
+              + 3 * nla_total_size(0) /* CTA_TUPLE_IP */
+              + 3 * nla_total_size(0) /* CTA_TUPLE_PROTO */
+              + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */
+              + nla_total_size(0) /* CTA_PROTOINFO */
+              + nla_total_size(0) /* CTA_HELP */
+              + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
+              + ctnetlink_secctx_size(ct)
+#ifdef CONFIG_NF_NAT_NEEDED
+              + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
+              + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */
+#endif
+#ifdef CONFIG_NF_CONNTRACK_MARK
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */
+#endif
+              + ctnetlink_proto_size(ct)
+              ;
+}
+
+static int
+ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct)
+{
+       struct nlattr *nest_parms;
+
+       rcu_read_lock();
+       nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+       if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
+               goto nla_put_failure;
+       nla_nest_end(skb, nest_parms);
+
+       nest_parms = nla_nest_start(skb, CTA_TUPLE_REPLY | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+       if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_REPLY)) < 0)
+               goto nla_put_failure;
+       nla_nest_end(skb, nest_parms);
+
+       if (nf_ct_zone(ct)) {
+               if (nla_put_be16(skb, CTA_ZONE, htons(nf_ct_zone(ct))))
+                       goto nla_put_failure;
+       }
+
+       if (ctnetlink_dump_id(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_status(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_timeout(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_protoinfo(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_helpinfo(skb, ct) < 0)
+               goto nla_put_failure;
+
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+       if (ct->secmark && ctnetlink_dump_secctx(skb, ct) < 0)
+               goto nla_put_failure;
+#endif
+       if (ct->master && ctnetlink_dump_master(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if ((ct->status & IPS_SEQ_ADJUST) &&
+           ctnetlink_dump_nat_seq_adj(skb, ct) < 0)
+               goto nla_put_failure;
+
+#ifdef CONFIG_NF_CONNTRACK_MARK
+       if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0)
+               goto nla_put_failure;
+#endif
+       rcu_read_unlock();
+       return 0;
+
+nla_put_failure:
+       rcu_read_unlock();
+       return -ENOSPC;
+}
+
+static int
+ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
+{
+       int err;
+
+       if (cda[CTA_TIMEOUT]) {
+               err = ctnetlink_change_timeout(ct, cda);
+               if (err < 0)
+                       return err;
+       }
+       if (cda[CTA_STATUS]) {
+               err = ctnetlink_change_status(ct, cda);
+               if (err < 0)
+                       return err;
+       }
+       if (cda[CTA_HELP]) {
+               err = ctnetlink_change_helper(ct, cda);
+               if (err < 0)
+                       return err;
+       }
+#if defined(CONFIG_NF_CONNTRACK_MARK)
+       if (cda[CTA_MARK])
+               ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
+#endif
+       return 0;
+}
+
+static int
+ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
+{
+       struct nlattr *cda[CTA_MAX+1];
+
+       nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy);
+
+       return ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
+}
+
+static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
+       .build_size     = ctnetlink_nfqueue_build_size,
+       .build          = ctnetlink_nfqueue_build,
+       .parse          = ctnetlink_nfqueue_parse,
+#ifdef CONFIG_NF_NAT_NEEDED
+       .seq_adjust     = nf_nat_tcp_seq_adjust,
+#endif
+};
+#endif /* CONFIG_NETFILTER_NETLINK_QUEUE */
+
 /***********************************************************************
  * EXPECT
  ***********************************************************************/
@@ -2424,7 +2575,11 @@ static int __init ctnetlink_init(void)
                pr_err("ctnetlink_init: cannot register pernet operations\n");
                goto err_unreg_exp_subsys;
        }
-
+#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \
+    defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE)
+       /* setup interaction between nf_queue and nf_conntrack_netlink. */
+       RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook);
+#endif
        return 0;
 
 err_unreg_exp_subsys:
@@ -2442,6 +2597,10 @@ static void __exit ctnetlink_exit(void)
        unregister_pernet_subsys(&ctnetlink_net_ops);
        nfnetlink_subsys_unregister(&ctnl_exp_subsys);
        nfnetlink_subsys_unregister(&ctnl_subsys);
+#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \
+    defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE)
+       RCU_INIT_POINTER(nfq_ct_hook, NULL);
+#endif
 }
 
 module_init(ctnetlink_init);
index 31d56b2..6fed9ec 100644 (file)
@@ -174,7 +174,7 @@ static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct,
 static void pptp_destroy_siblings(struct nf_conn *ct)
 {
        struct net *net = nf_ct_net(ct);
-       const struct nf_conn_help *help = nfct_help(ct);
+       const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
        struct nf_conntrack_tuple t;
 
        nf_ct_gre_keymap_destroy(ct);
@@ -182,16 +182,16 @@ static void pptp_destroy_siblings(struct nf_conn *ct)
        /* try original (pns->pac) tuple */
        memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
        t.dst.protonum = IPPROTO_GRE;
-       t.src.u.gre.key = help->help.ct_pptp_info.pns_call_id;
-       t.dst.u.gre.key = help->help.ct_pptp_info.pac_call_id;
+       t.src.u.gre.key = ct_pptp_info->pns_call_id;
+       t.dst.u.gre.key = ct_pptp_info->pac_call_id;
        if (!destroy_sibling_or_exp(net, ct, &t))
                pr_debug("failed to timeout original pns->pac ct/exp\n");
 
        /* try reply (pac->pns) tuple */
        memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
        t.dst.protonum = IPPROTO_GRE;
-       t.src.u.gre.key = help->help.ct_pptp_info.pac_call_id;
-       t.dst.u.gre.key = help->help.ct_pptp_info.pns_call_id;
+       t.src.u.gre.key = ct_pptp_info->pac_call_id;
+       t.dst.u.gre.key = ct_pptp_info->pns_call_id;
        if (!destroy_sibling_or_exp(net, ct, &t))
                pr_debug("failed to timeout reply pac->pns ct/exp\n");
 }
@@ -269,7 +269,7 @@ pptp_inbound_pkt(struct sk_buff *skb,
                 struct nf_conn *ct,
                 enum ip_conntrack_info ctinfo)
 {
-       struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
+       struct nf_ct_pptp_master *info = nfct_help_data(ct);
        u_int16_t msg;
        __be16 cid = 0, pcid = 0;
        typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound;
@@ -396,7 +396,7 @@ pptp_outbound_pkt(struct sk_buff *skb,
                  struct nf_conn *ct,
                  enum ip_conntrack_info ctinfo)
 {
-       struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
+       struct nf_ct_pptp_master *info = nfct_help_data(ct);
        u_int16_t msg;
        __be16 cid = 0, pcid = 0;
        typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound;
@@ -506,7 +506,7 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff,
 
 {
        int dir = CTINFO2DIR(ctinfo);
-       const struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
+       const struct nf_ct_pptp_master *info = nfct_help_data(ct);
        const struct tcphdr *tcph;
        struct tcphdr _tcph;
        const struct pptp_pkt_hdr *pptph;
@@ -592,6 +592,7 @@ static const struct nf_conntrack_expect_policy pptp_exp_policy = {
 static struct nf_conntrack_helper pptp __read_mostly = {
        .name                   = "pptp",
        .me                     = THIS_MODULE,
+       .data_len               = sizeof(struct nf_ct_pptp_master),
        .tuple.src.l3num        = AF_INET,
        .tuple.src.u.tcp.port   = cpu_to_be16(PPTP_CONTROL_PORT),
        .tuple.dst.protonum     = IPPROTO_TCP,
index 25ba5a2..5cac41c 100644 (file)
@@ -117,10 +117,10 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
 {
        struct net *net = nf_ct_net(ct);
        struct netns_proto_gre *net_gre = gre_pernet(net);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
        struct nf_ct_gre_keymap **kmp, *km;
 
-       kmp = &help->help.ct_pptp_info.keymap[dir];
+       kmp = &ct_pptp_info->keymap[dir];
        if (*kmp) {
                /* check whether it's a retransmission */
                read_lock_bh(&net_gre->keymap_lock);
@@ -158,19 +158,19 @@ void nf_ct_gre_keymap_destroy(struct nf_conn *ct)
 {
        struct net *net = nf_ct_net(ct);
        struct netns_proto_gre *net_gre = gre_pernet(net);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
        enum ip_conntrack_dir dir;
 
        pr_debug("entering for ct %p\n", ct);
 
        write_lock_bh(&net_gre->keymap_lock);
        for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
-               if (help->help.ct_pptp_info.keymap[dir]) {
+               if (ct_pptp_info->keymap[dir]) {
                        pr_debug("removing %p from list\n",
-                                help->help.ct_pptp_info.keymap[dir]);
-                       list_del(&help->help.ct_pptp_info.keymap[dir]->list);
-                       kfree(help->help.ct_pptp_info.keymap[dir]);
-                       help->help.ct_pptp_info.keymap[dir] = NULL;
+                                ct_pptp_info->keymap[dir]);
+                       list_del(&ct_pptp_info->keymap[dir]->list);
+                       kfree(ct_pptp_info->keymap[dir]);
+                       ct_pptp_info->keymap[dir] = NULL;
                }
        }
        write_unlock_bh(&net_gre->keymap_lock);
index 8501823..295429f 100644 (file)
@@ -69,13 +69,12 @@ static int help(struct sk_buff *skb,
        void *sb_ptr;
        int ret = NF_ACCEPT;
        int dir = CTINFO2DIR(ctinfo);
-       struct nf_ct_sane_master *ct_sane_info;
+       struct nf_ct_sane_master *ct_sane_info = nfct_help_data(ct);
        struct nf_conntrack_expect *exp;
        struct nf_conntrack_tuple *tuple;
        struct sane_request *req;
        struct sane_reply_net_start *reply;
 
-       ct_sane_info = &nfct_help(ct)->help.ct_sane_info;
        /* Until there's been traffic both ways, don't look in packets. */
        if (ctinfo != IP_CT_ESTABLISHED &&
            ctinfo != IP_CT_ESTABLISHED_REPLY)
@@ -163,7 +162,6 @@ out:
 }
 
 static struct nf_conntrack_helper sane[MAX_PORTS][2] __read_mostly;
-static char sane_names[MAX_PORTS][2][sizeof("sane-65535")] __read_mostly;
 
 static const struct nf_conntrack_expect_policy sane_exp_policy = {
        .max_expected   = 1,
@@ -190,7 +188,6 @@ static void nf_conntrack_sane_fini(void)
 static int __init nf_conntrack_sane_init(void)
 {
        int i, j = -1, ret = 0;
-       char *tmpname;
 
        sane_buffer = kmalloc(65536, GFP_KERNEL);
        if (!sane_buffer)
@@ -205,17 +202,16 @@ static int __init nf_conntrack_sane_init(void)
                sane[i][0].tuple.src.l3num = PF_INET;
                sane[i][1].tuple.src.l3num = PF_INET6;
                for (j = 0; j < 2; j++) {
+                       sane[i][j].data_len = sizeof(struct nf_ct_sane_master);
                        sane[i][j].tuple.src.u.tcp.port = htons(ports[i]);
                        sane[i][j].tuple.dst.protonum = IPPROTO_TCP;
                        sane[i][j].expect_policy = &sane_exp_policy;
                        sane[i][j].me = THIS_MODULE;
                        sane[i][j].help = help;
-                       tmpname = &sane_names[i][j][0];
                        if (ports[i] == SANE_PORT)
-                               sprintf(tmpname, "sane");
+                               sprintf(sane[i][j].name, "sane");
                        else
-                               sprintf(tmpname, "sane-%d", ports[i]);
-                       sane[i][j].name = tmpname;
+                               sprintf(sane[i][j].name, "sane-%d", ports[i]);
 
                        pr_debug("nf_ct_sane: registering helper for pf: %d "
                                 "port: %d\n",
index 93faf6a..758a1ba 100644 (file)
@@ -1075,12 +1075,12 @@ static int process_invite_response(struct sk_buff *skb, unsigned int dataoff,
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
 
        if ((code >= 100 && code <= 199) ||
            (code >= 200 && code <= 299))
                return process_sdp(skb, dataoff, dptr, datalen, cseq);
-       else if (help->help.ct_sip_info.invite_cseq == cseq)
+       else if (ct_sip_info->invite_cseq == cseq)
                flush_expectations(ct, true);
        return NF_ACCEPT;
 }
@@ -1091,12 +1091,12 @@ static int process_update_response(struct sk_buff *skb, unsigned int dataoff,
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
 
        if ((code >= 100 && code <= 199) ||
            (code >= 200 && code <= 299))
                return process_sdp(skb, dataoff, dptr, datalen, cseq);
-       else if (help->help.ct_sip_info.invite_cseq == cseq)
+       else if (ct_sip_info->invite_cseq == cseq)
                flush_expectations(ct, true);
        return NF_ACCEPT;
 }
@@ -1107,12 +1107,12 @@ static int process_prack_response(struct sk_buff *skb, unsigned int dataoff,
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
 
        if ((code >= 100 && code <= 199) ||
            (code >= 200 && code <= 299))
                return process_sdp(skb, dataoff, dptr, datalen, cseq);
-       else if (help->help.ct_sip_info.invite_cseq == cseq)
+       else if (ct_sip_info->invite_cseq == cseq)
                flush_expectations(ct, true);
        return NF_ACCEPT;
 }
@@ -1123,13 +1123,13 @@ static int process_invite_request(struct sk_buff *skb, unsigned int dataoff,
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
        unsigned int ret;
 
        flush_expectations(ct, true);
        ret = process_sdp(skb, dataoff, dptr, datalen, cseq);
        if (ret == NF_ACCEPT)
-               help->help.ct_sip_info.invite_cseq = cseq;
+               ct_sip_info->invite_cseq = cseq;
        return ret;
 }
 
@@ -1154,7 +1154,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int dataoff,
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
        unsigned int matchoff, matchlen;
        struct nf_conntrack_expect *exp;
@@ -1235,7 +1235,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int dataoff,
 
 store_cseq:
        if (ret == NF_ACCEPT)
-               help->help.ct_sip_info.register_cseq = cseq;
+               ct_sip_info->register_cseq = cseq;
        return ret;
 }
 
@@ -1245,7 +1245,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int dataoff,
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-       struct nf_conn_help *help = nfct_help(ct);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
        union nf_inet_addr addr;
        __be16 port;
@@ -1262,7 +1262,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int dataoff,
         * responses, so we store the sequence number of the last valid
         * request and compare it here.
         */
-       if (help->help.ct_sip_info.register_cseq != cseq)
+       if (ct_sip_info->register_cseq != cseq)
                return NF_ACCEPT;
 
        if (code >= 100 && code <= 199)
@@ -1556,7 +1556,6 @@ static void nf_conntrack_sip_fini(void)
 static int __init nf_conntrack_sip_init(void)
 {
        int i, j, ret;
-       char *tmpname;
 
        if (ports_c == 0)
                ports[ports_c++] = SIP_PORT;
@@ -1579,17 +1578,16 @@ static int __init nf_conntrack_sip_init(void)
                sip[i][3].help = sip_help_tcp;
 
                for (j = 0; j < ARRAY_SIZE(sip[i]); j++) {
+                       sip[i][j].data_len = sizeof(struct nf_ct_sip_master);
                        sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
                        sip[i][j].expect_policy = sip_exp_policy;
                        sip[i][j].expect_class_max = SIP_EXPECT_MAX;
                        sip[i][j].me = THIS_MODULE;
 
-                       tmpname = &sip_names[i][j][0];
                        if (ports[i] == SIP_PORT)
-                               sprintf(tmpname, "sip");
+                               sprintf(sip_names[i][j], "sip");
                        else
-                               sprintf(tmpname, "sip-%u", i);
-                       sip[i][j].name = tmpname;
+                               sprintf(sip_names[i][j], "sip-%u", i);
 
                        pr_debug("port #%u: %u\n", i, ports[i]);
 
index 75466fd..81fc61c 100644 (file)
@@ -92,7 +92,6 @@ static int tftp_help(struct sk_buff *skb,
 }
 
 static struct nf_conntrack_helper tftp[MAX_PORTS][2] __read_mostly;
-static char tftp_names[MAX_PORTS][2][sizeof("tftp-65535")] __read_mostly;
 
 static const struct nf_conntrack_expect_policy tftp_exp_policy = {
        .max_expected   = 1,
@@ -112,7 +111,6 @@ static void nf_conntrack_tftp_fini(void)
 static int __init nf_conntrack_tftp_init(void)
 {
        int i, j, ret;
-       char *tmpname;
 
        if (ports_c == 0)
                ports[ports_c++] = TFTP_PORT;
@@ -129,12 +127,10 @@ static int __init nf_conntrack_tftp_init(void)
                        tftp[i][j].me = THIS_MODULE;
                        tftp[i][j].help = tftp_help;
 
-                       tmpname = &tftp_names[i][j][0];
                        if (ports[i] == TFTP_PORT)
-                               sprintf(tmpname, "tftp");
+                               sprintf(tftp[i][j].name, "tftp");
                        else
-                               sprintf(tmpname, "tftp-%u", i);
-                       tftp[i][j].name = tmpname;
+                               sprintf(tftp[i][j].name, "tftp-%u", i);
 
                        ret = nf_conntrack_helper_register(&tftp[i][j]);
                        if (ret) {
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
new file mode 100644 (file)
index 0000000..d683619
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * (C) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 (or any later at your option).
+ *
+ * This software has been sponsored by Vyatta Inc. <http://www.vyatta.com>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <net/netlink.h>
+#include <net/sock.h>
+
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_ecache.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+#include <linux/netfilter/nfnetlink_cthelper.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
+MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers");
+
+static int
+nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
+                       struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+       const struct nf_conn_help *help;
+       struct nf_conntrack_helper *helper;
+
+       help = nfct_help(ct);
+       if (help == NULL)
+               return NF_DROP;
+
+       /* rcu_read_lock()ed by nf_hook_slow */
+       helper = rcu_dereference(help->helper);
+       if (helper == NULL)
+               return NF_DROP;
+
+       /* This is an user-space helper not yet configured, skip. */
+       if ((helper->flags &
+           (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) ==
+            NF_CT_HELPER_F_USERSPACE)
+               return NF_ACCEPT;
+
+       /* If the user-space helper is not available, don't block traffic. */
+       return NF_QUEUE_NR(helper->queue_num) | NF_VERDICT_FLAG_QUEUE_BYPASS;
+}
+
+static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = {
+       [NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, },
+       [NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, },
+};
+
+static int
+nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple,
+                         const struct nlattr *attr)
+{
+       struct nlattr *tb[NFCTH_TUPLE_MAX+1];
+
+       nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol);
+
+       if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM])
+               return -EINVAL;
+
+       tuple->src.l3num = ntohs(nla_get_u16(tb[NFCTH_TUPLE_L3PROTONUM]));
+       tuple->dst.protonum = nla_get_u8(tb[NFCTH_TUPLE_L4PROTONUM]);
+
+       return 0;
+}
+
+static int
+nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
+{
+       const struct nf_conn_help *help = nfct_help(ct);
+
+       if (help->helper->data_len == 0)
+               return -EINVAL;
+
+       memcpy(&help->data, nla_data(attr), help->helper->data_len);
+       return 0;
+}
+
+static int
+nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct)
+{
+       const struct nf_conn_help *help = nfct_help(ct);
+
+       if (help->helper->data_len &&
+           nla_put(skb, CTA_HELP_INFO, help->helper->data_len, &help->data))
+               goto nla_put_failure;
+
+       return 0;
+
+nla_put_failure:
+       return -ENOSPC;
+}
+
+static const struct nla_policy nfnl_cthelper_expect_pol[NFCTH_POLICY_MAX+1] = {
+       [NFCTH_POLICY_NAME] = { .type = NLA_NUL_STRING,
+                               .len = NF_CT_HELPER_NAME_LEN-1 },
+       [NFCTH_POLICY_EXPECT_MAX] = { .type = NLA_U32, },
+       [NFCTH_POLICY_EXPECT_TIMEOUT] = { .type = NLA_U32, },
+};
+
+static int
+nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
+                           const struct nlattr *attr)
+{
+       struct nlattr *tb[NFCTH_POLICY_MAX+1];
+
+       nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol);
+
+       if (!tb[NFCTH_POLICY_NAME] ||
+           !tb[NFCTH_POLICY_EXPECT_MAX] ||
+           !tb[NFCTH_POLICY_EXPECT_TIMEOUT])
+               return -EINVAL;
+
+       strncpy(expect_policy->name,
+               nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
+       expect_policy->max_expected =
+               ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
+       expect_policy->timeout =
+               ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
+
+       return 0;
+}
+
+static const struct nla_policy
+nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = {
+       [NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, },
+};
+
+static int
+nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
+                                 const struct nlattr *attr)
+{
+       int i, ret;
+       struct nf_conntrack_expect_policy *expect_policy;
+       struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
+
+       nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
+                                       nfnl_cthelper_expect_policy_set);
+
+       if (!tb[NFCTH_POLICY_SET_NUM])
+               return -EINVAL;
+
+       helper->expect_class_max =
+               ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
+
+       if (helper->expect_class_max != 0 &&
+           helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES)
+               return -EOVERFLOW;
+
+       expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) *
+                               helper->expect_class_max, GFP_KERNEL);
+       if (expect_policy == NULL)
+               return -ENOMEM;
+
+       for (i=0; i<helper->expect_class_max; i++) {
+               if (!tb[NFCTH_POLICY_SET+i])
+                       goto err;
+
+               ret = nfnl_cthelper_expect_policy(&expect_policy[i],
+                                                 tb[NFCTH_POLICY_SET+i]);
+               if (ret < 0)
+                       goto err;
+       }
+       helper->expect_policy = expect_policy;
+       return 0;
+err:
+       kfree(expect_policy);
+       return -EINVAL;
+}
+
+static int
+nfnl_cthelper_create(const struct nlattr * const tb[],
+                    struct nf_conntrack_tuple *tuple)
+{
+       struct nf_conntrack_helper *helper;
+       int ret;
+
+       if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
+               return -EINVAL;
+
+       helper = kzalloc(sizeof(struct nf_conntrack_helper), GFP_KERNEL);
+       if (helper == NULL)
+               return -ENOMEM;
+
+       ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]);
+       if (ret < 0)
+               goto err;
+
+       strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
+       helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
+       helper->flags |= NF_CT_HELPER_F_USERSPACE;
+       memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
+
+       helper->me = THIS_MODULE;
+       helper->help = nfnl_userspace_cthelper;
+       helper->from_nlattr = nfnl_cthelper_from_nlattr;
+       helper->to_nlattr = nfnl_cthelper_to_nlattr;
+
+       /* Default to queue number zero, this can be updated at any time. */
+       if (tb[NFCTH_QUEUE_NUM])
+               helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
+
+       if (tb[NFCTH_STATUS]) {
+               int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
+
+               switch(status) {
+               case NFCT_HELPER_STATUS_ENABLED:
+                       helper->flags |= NF_CT_HELPER_F_CONFIGURED;
+                       break;
+               case NFCT_HELPER_STATUS_DISABLED:
+                       helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
+                       break;
+               }
+       }
+
+       ret = nf_conntrack_helper_register(helper);
+       if (ret < 0)
+               goto err;
+
+       return 0;
+err:
+       kfree(helper);
+       return ret;
+}
+
+static int
+nfnl_cthelper_update(const struct nlattr * const tb[],
+                    struct nf_conntrack_helper *helper)
+{
+       int ret;
+
+       if (tb[NFCTH_PRIV_DATA_LEN])
+               return -EBUSY;
+
+       if (tb[NFCTH_POLICY]) {
+               ret = nfnl_cthelper_parse_expect_policy(helper,
+                                                       tb[NFCTH_POLICY]);
+               if (ret < 0)
+                       return ret;
+       }
+       if (tb[NFCTH_QUEUE_NUM])
+               helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
+
+       if (tb[NFCTH_STATUS]) {
+               int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
+
+               switch(status) {
+               case NFCT_HELPER_STATUS_ENABLED:
+                       helper->flags |= NF_CT_HELPER_F_CONFIGURED;
+                       break;
+               case NFCT_HELPER_STATUS_DISABLED:
+                       helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int
+nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb,
+                 const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+{
+       const char *helper_name;
+       struct nf_conntrack_helper *cur, *helper = NULL;
+       struct nf_conntrack_tuple tuple;
+       struct hlist_node *n;
+       int ret = 0, i;
+
+       if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
+               return -EINVAL;
+
+       helper_name = nla_data(tb[NFCTH_NAME]);
+
+       ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
+       if (ret < 0)
+               return ret;
+
+       rcu_read_lock();
+       for (i = 0; i < nf_ct_helper_hsize && !helper; i++) {
+               hlist_for_each_entry_rcu(cur, n, &nf_ct_helper_hash[i], hnode) {
+
+                       /* skip non-userspace conntrack helpers. */
+                       if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
+                               continue;
+
+                       if (strncmp(cur->name, helper_name,
+                                       NF_CT_HELPER_NAME_LEN) != 0)
+                               continue;
+
+                       if ((tuple.src.l3num != cur->tuple.src.l3num ||
+                            tuple.dst.protonum != cur->tuple.dst.protonum))
+                               continue;
+
+                       if (nlh->nlmsg_flags & NLM_F_EXCL) {
+                               ret = -EEXIST;
+                               goto err;
+                       }
+                       helper = cur;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       if (helper == NULL)
+               ret = nfnl_cthelper_create(tb, &tuple);
+       else
+               ret = nfnl_cthelper_update(tb, helper);
+
+       return ret;
+err:
+       rcu_read_unlock();
+       return ret;
+}
+
+static int
+nfnl_cthelper_dump_tuple(struct sk_buff *skb,
+                        struct nf_conntrack_helper *helper)
+{
+       struct nlattr *nest_parms;
+
+       nest_parms = nla_nest_start(skb, NFCTH_TUPLE | NLA_F_NESTED);
+       if (nest_parms == NULL)
+               goto nla_put_failure;
+
+       if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM,
+                        htons(helper->tuple.src.l3num)))
+               goto nla_put_failure;
+
+       if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum))
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest_parms);
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static int
+nfnl_cthelper_dump_policy(struct sk_buff *skb,
+                       struct nf_conntrack_helper *helper)
+{
+       int i;
+       struct nlattr *nest_parms1, *nest_parms2;
+
+       nest_parms1 = nla_nest_start(skb, NFCTH_POLICY | NLA_F_NESTED);
+       if (nest_parms1 == NULL)
+               goto nla_put_failure;
+
+       if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM,
+                        htonl(helper->expect_class_max)))
+               goto nla_put_failure;
+
+       for (i=0; i<helper->expect_class_max; i++) {
+               nest_parms2 = nla_nest_start(skb,
+                               (NFCTH_POLICY_SET+i) | NLA_F_NESTED);
+               if (nest_parms2 == NULL)
+                       goto nla_put_failure;
+
+               if (nla_put_string(skb, NFCTH_POLICY_NAME,
+                                  helper->expect_policy[i].name))
+                       goto nla_put_failure;
+
+               if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_MAX,
+                                htonl(helper->expect_policy[i].max_expected)))
+                       goto nla_put_failure;
+
+               if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_TIMEOUT,
+                                htonl(helper->expect_policy[i].timeout)))
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, nest_parms2);
+       }
+       nla_nest_end(skb, nest_parms1);
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static int
+nfnl_cthelper_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type,
+                       int event, struct nf_conntrack_helper *helper)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+       unsigned int flags = pid ? NLM_F_MULTI : 0;
+       int status;
+
+       event |= NFNL_SUBSYS_CTHELPER << 8;
+       nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags);
+       if (nlh == NULL)
+               goto nlmsg_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family = AF_UNSPEC;
+       nfmsg->version = NFNETLINK_V0;
+       nfmsg->res_id = 0;
+
+       if (nla_put_string(skb, NFCTH_NAME, helper->name))
+               goto nla_put_failure;
+
+       if (nla_put_be32(skb, NFCTH_QUEUE_NUM, htonl(helper->queue_num)))
+               goto nla_put_failure;
+
+       if (nfnl_cthelper_dump_tuple(skb, helper) < 0)
+               goto nla_put_failure;
+
+       if (nfnl_cthelper_dump_policy(skb, helper) < 0)
+               goto nla_put_failure;
+
+       if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len)))
+               goto nla_put_failure;
+
+       if (helper->flags & NF_CT_HELPER_F_CONFIGURED)
+               status = NFCT_HELPER_STATUS_ENABLED;
+       else
+               status = NFCT_HELPER_STATUS_DISABLED;
+
+       if (nla_put_be32(skb, NFCTH_STATUS, htonl(status)))
+               goto nla_put_failure;
+
+       nlmsg_end(skb, nlh);
+       return skb->len;
+
+nlmsg_failure:
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -1;
+}
+
+static int
+nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct nf_conntrack_helper *cur, *last;
+       struct hlist_node *n;
+
+       rcu_read_lock();
+       last = (struct nf_conntrack_helper *)cb->args[1];
+       for (; cb->args[0] < nf_ct_helper_hsize; cb->args[0]++) {
+restart:
+               hlist_for_each_entry_rcu(cur, n,
+                               &nf_ct_helper_hash[cb->args[0]], hnode) {
+
+                       /* skip non-userspace conntrack helpers. */
+                       if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
+                               continue;
+
+                       if (cb->args[1]) {
+                               if (cur != last)
+                                       continue;
+                               cb->args[1] = 0;
+                       }
+                       if (nfnl_cthelper_fill_info(skb,
+                                           NETLINK_CB(cb->skb).pid,
+                                           cb->nlh->nlmsg_seq,
+                                           NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
+                                           NFNL_MSG_CTHELPER_NEW, cur) < 0) {
+                               cb->args[1] = (unsigned long)cur;
+                               goto out;
+                       }
+               }
+       }
+       if (cb->args[1]) {
+               cb->args[1] = 0;
+               goto restart;
+       }
+out:
+       rcu_read_unlock();
+       return skb->len;
+}
+
+static int
+nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb,
+                 const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+{
+       int ret = -ENOENT, i;
+       struct nf_conntrack_helper *cur;
+       struct hlist_node *n;
+       struct sk_buff *skb2;
+       char *helper_name = NULL;
+       struct nf_conntrack_tuple tuple;
+       bool tuple_set = false;
+
+       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = nfnl_cthelper_dump_table,
+               };
+               return netlink_dump_start(nfnl, skb, nlh, &c);
+       }
+
+       if (tb[NFCTH_NAME])
+               helper_name = nla_data(tb[NFCTH_NAME]);
+
+       if (tb[NFCTH_TUPLE]) {
+               ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
+               if (ret < 0)
+                       return ret;
+
+               tuple_set = true;
+       }
+
+       for (i = 0; i < nf_ct_helper_hsize; i++) {
+               hlist_for_each_entry_rcu(cur, n, &nf_ct_helper_hash[i], hnode) {
+
+                       /* skip non-userspace conntrack helpers. */
+                       if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
+                               continue;
+
+                       if (helper_name && strncmp(cur->name, helper_name,
+                                               NF_CT_HELPER_NAME_LEN) != 0) {
+                               continue;
+                       }
+                       if (tuple_set &&
+                           (tuple.src.l3num != cur->tuple.src.l3num ||
+                            tuple.dst.protonum != cur->tuple.dst.protonum))
+                               continue;
+
+                       skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+                       if (skb2 == NULL) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+
+                       ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).pid,
+                                               nlh->nlmsg_seq,
+                                               NFNL_MSG_TYPE(nlh->nlmsg_type),
+                                               NFNL_MSG_CTHELPER_NEW, cur);
+                       if (ret <= 0) {
+                               kfree_skb(skb2);
+                               break;
+                       }
+
+                       ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).pid,
+                                               MSG_DONTWAIT);
+                       if (ret > 0)
+                               ret = 0;
+
+                       /* this avoids a loop in nfnetlink. */
+                       return ret == -EAGAIN ? -ENOBUFS : ret;
+               }
+       }
+       return ret;
+}
+
+static int
+nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb,
+            const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+{
+       char *helper_name = NULL;
+       struct nf_conntrack_helper *cur;
+       struct hlist_node *n, *tmp;
+       struct nf_conntrack_tuple tuple;
+       bool tuple_set = false, found = false;
+       int i, j = 0, ret;
+
+       if (tb[NFCTH_NAME])
+               helper_name = nla_data(tb[NFCTH_NAME]);
+
+       if (tb[NFCTH_TUPLE]) {
+               ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
+               if (ret < 0)
+                       return ret;
+
+               tuple_set = true;
+       }
+
+       for (i = 0; i < nf_ct_helper_hsize; i++) {
+               hlist_for_each_entry_safe(cur, n, tmp, &nf_ct_helper_hash[i],
+                                                               hnode) {
+                       /* skip non-userspace conntrack helpers. */
+                       if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
+                               continue;
+
+                       j++;
+
+                       if (helper_name && strncmp(cur->name, helper_name,
+                                               NF_CT_HELPER_NAME_LEN) != 0) {
+                               continue;
+                       }
+                       if (tuple_set &&
+                           (tuple.src.l3num != cur->tuple.src.l3num ||
+                            tuple.dst.protonum != cur->tuple.dst.protonum))
+                               continue;
+
+                       found = true;
+                       nf_conntrack_helper_unregister(cur);
+               }
+       }
+       /* Make sure we return success if we flush and there is no helpers */
+       return (found || j == 0) ? 0 : -ENOENT;
+}
+
+static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = {
+       [NFCTH_NAME] = { .type = NLA_NUL_STRING,
+                        .len = NF_CT_HELPER_NAME_LEN-1 },
+       [NFCTH_QUEUE_NUM] = { .type = NLA_U32, },
+};
+
+static const struct nfnl_callback nfnl_cthelper_cb[NFNL_MSG_CTHELPER_MAX] = {
+       [NFNL_MSG_CTHELPER_NEW]         = { .call = nfnl_cthelper_new,
+                                           .attr_count = NFCTH_MAX,
+                                           .policy = nfnl_cthelper_policy },
+       [NFNL_MSG_CTHELPER_GET]         = { .call = nfnl_cthelper_get,
+                                           .attr_count = NFCTH_MAX,
+                                           .policy = nfnl_cthelper_policy },
+       [NFNL_MSG_CTHELPER_DEL]         = { .call = nfnl_cthelper_del,
+                                           .attr_count = NFCTH_MAX,
+                                           .policy = nfnl_cthelper_policy },
+};
+
+static const struct nfnetlink_subsystem nfnl_cthelper_subsys = {
+       .name                           = "cthelper",
+       .subsys_id                      = NFNL_SUBSYS_CTHELPER,
+       .cb_count                       = NFNL_MSG_CTHELPER_MAX,
+       .cb                             = nfnl_cthelper_cb,
+};
+
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTHELPER);
+
+static int __init nfnl_cthelper_init(void)
+{
+       int ret;
+
+       ret = nfnetlink_subsys_register(&nfnl_cthelper_subsys);
+       if (ret < 0) {
+               pr_err("nfnl_cthelper: cannot register with nfnetlink.\n");
+               goto err_out;
+       }
+       return 0;
+err_out:
+       return ret;
+}
+
+static void __exit nfnl_cthelper_exit(void)
+{
+       struct nf_conntrack_helper *cur;
+       struct hlist_node *n, *tmp;
+       int i;
+
+       nfnetlink_subsys_unregister(&nfnl_cthelper_subsys);
+
+       for (i=0; i<nf_ct_helper_hsize; i++) {
+               hlist_for_each_entry_safe(cur, n, tmp, &nf_ct_helper_hash[i],
+                                                                       hnode) {
+                       /* skip non-userspace conntrack helpers. */
+                       if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
+                               continue;
+
+                       nf_conntrack_helper_unregister(cur);
+               }
+       }
+}
+
+module_init(nfnl_cthelper_init);
+module_exit(nfnl_cthelper_exit);
index 630da3d..ff82c79 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/list.h>
 #include <net/sock.h>
 #include <net/netfilter/nf_queue.h>
+#include <net/netfilter/nf_conntrack.h>
 
 #include <linux/atomic.h>
 
@@ -233,6 +234,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        struct sk_buff *entskb = entry->skb;
        struct net_device *indev;
        struct net_device *outdev;
+       struct nfq_ct_hook *nfq_ct;
+       struct nf_conn *ct = NULL;
+       enum ip_conntrack_info uninitialized_var(ctinfo);
 
        size =    NLMSG_SPACE(sizeof(struct nfgenmsg))
                + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr))
@@ -266,6 +270,17 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
                break;
        }
 
+       /* rcu_read_lock()ed by __nf_queue already. */
+       nfq_ct = rcu_dereference(nfq_ct_hook);
+       if (nfq_ct != NULL && (queue->flags & NFQA_CFG_F_CONNTRACK)) {
+               ct = nf_ct_get(entskb, &ctinfo);
+               if (ct) {
+                       if (!nf_ct_is_untracked(ct))
+                               size += nfq_ct->build_size(ct);
+                       else
+                               ct = NULL;
+               }
+       }
 
        skb = alloc_skb(size, GFP_ATOMIC);
        if (!skb)
@@ -389,6 +404,24 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
                        BUG();
        }
 
+       if (ct) {
+               struct nlattr *nest_parms;
+               u_int32_t tmp;
+
+               nest_parms = nla_nest_start(skb, NFQA_CT | NLA_F_NESTED);
+               if (!nest_parms)
+                       goto nla_put_failure;
+
+               if (nfq_ct->build(skb, ct) < 0)
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, nest_parms);
+
+               tmp = ctinfo;
+               if (nla_put_u32(skb, NFQA_CT_INFO, htonl(ctinfo)))
+                       goto nla_put_failure;
+       }
+
        nlh->nlmsg_len = skb->tail - old_tail;
        return skb;
 
@@ -469,12 +502,10 @@ err_out:
 }
 
 static int
-nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
+nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff)
 {
        struct sk_buff *nskb;
-       int diff;
 
-       diff = data_len - e->skb->len;
        if (diff < 0) {
                if (pskb_trim(e->skb, data_len))
                        return -ENOMEM;
@@ -632,6 +663,7 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = {
        [NFQA_VERDICT_HDR]      = { .len = sizeof(struct nfqnl_msg_verdict_hdr) },
        [NFQA_MARK]             = { .type = NLA_U32 },
        [NFQA_PAYLOAD]          = { .type = NLA_UNSPEC },
+       [NFQA_CT]               = { .type = NLA_UNSPEC },
 };
 
 static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = {
@@ -732,6 +764,9 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
        struct nfqnl_instance *queue;
        unsigned int verdict;
        struct nf_queue_entry *entry;
+       struct nfq_ct_hook *nfq_ct;
+       enum ip_conntrack_info uninitialized_var(ctinfo);
+       struct nf_conn *ct = NULL;
 
        queue = instance_lookup(queue_num);
        if (!queue)
@@ -750,11 +785,27 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
        if (entry == NULL)
                return -ENOENT;
 
+       rcu_read_lock();
+       nfq_ct = rcu_dereference(nfq_ct_hook);
+       if (nfq_ct != NULL &&
+           (queue->flags & NFQA_CFG_F_CONNTRACK) && nfqa[NFQA_CT]) {
+               ct = nf_ct_get(entry->skb, &ctinfo);
+               if (ct && !nf_ct_is_untracked(ct))
+                       nfq_ct->parse(nfqa[NFQA_CT], ct);
+       }
+
        if (nfqa[NFQA_PAYLOAD]) {
+               u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]);
+               int diff = payload_len - entry->skb->len;
+
                if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
-                                nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0)
+                                payload_len, entry, diff) < 0)
                        verdict = NF_DROP;
+
+               if (ct && (ct->status & IPS_NAT_MASK) && diff)
+                       nfq_ct->seq_adjust(skb, ct, ctinfo, diff);
        }
+       rcu_read_unlock();
 
        if (nfqa[NFQA_MARK])
                entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK]));
index a51de9b..1160185 100644 (file)
@@ -112,6 +112,8 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
                goto err3;
 
        if (info->helper[0]) {
+               struct nf_conntrack_helper *helper;
+
                ret = -ENOENT;
                proto = xt_ct_find_proto(par);
                if (!proto) {
@@ -120,19 +122,21 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
                        goto err3;
                }
 
-               ret = -ENOMEM;
-               help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
-               if (help == NULL)
-                       goto err3;
-
                ret = -ENOENT;
-               help->helper = nf_conntrack_helper_try_module_get(info->helper,
-                                                                 par->family,
-                                                                 proto);
-               if (help->helper == NULL) {
+               helper = nf_conntrack_helper_try_module_get(info->helper,
+                                                           par->family,
+                                                           proto);
+               if (helper == NULL) {
                        pr_info("No such helper \"%s\"\n", info->helper);
                        goto err3;
                }
+
+               ret = -ENOMEM;
+               help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
+               if (help == NULL)
+                       goto err3;
+
+               help->helper = helper;
        }
 
        __set_bit(IPS_TEMPLATE_BIT, &ct->status);
@@ -202,6 +206,8 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
                goto err3;
 
        if (info->helper[0]) {
+               struct nf_conntrack_helper *helper;
+
                ret = -ENOENT;
                proto = xt_ct_find_proto(par);
                if (!proto) {
@@ -210,19 +216,21 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
                        goto err3;
                }
 
-               ret = -ENOMEM;
-               help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
-               if (help == NULL)
-                       goto err3;
-
                ret = -ENOENT;
-               help->helper = nf_conntrack_helper_try_module_get(info->helper,
-                                                                 par->family,
-                                                                 proto);
-               if (help->helper == NULL) {
+               helper = nf_conntrack_helper_try_module_get(info->helper,
+                                                           par->family,
+                                                           proto);
+               if (helper == NULL) {
                        pr_info("No such helper \"%s\"\n", info->helper);
                        goto err3;
                }
+
+               ret = -ENOMEM;
+               help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
+               if (help == NULL)
+                       goto err3;
+
+               help->helper = helper;
        }
 
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT