Merge branch 'master' of git://1984.lsi.us.es/nf-next

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>
This commit is contained in:
David S. Miller 2012-06-16 15:23:35 -07:00
commit 82f437b950
39 changed files with 1240 additions and 186 deletions

View 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 (*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_attach(struct sk_buff *, struct sk_buff *);
extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; 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 #else
static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
#endif #endif

View File

@ -10,6 +10,7 @@ header-y += nfnetlink.h
header-y += nfnetlink_acct.h header-y += nfnetlink_acct.h
header-y += nfnetlink_compat.h header-y += nfnetlink_compat.h
header-y += nfnetlink_conntrack.h header-y += nfnetlink_conntrack.h
header-y += nfnetlink_cthelper.h
header-y += nfnetlink_cttimeout.h header-y += nfnetlink_cttimeout.h
header-y += nfnetlink_log.h header-y += nfnetlink_log.h
header-y += nfnetlink_queue.h header-y += nfnetlink_queue.h

View File

@ -2,6 +2,8 @@
#define __NF_CONNTRACK_SIP_H__ #define __NF_CONNTRACK_SIP_H__
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <net/netfilter/nf_conntrack_expect.h>
#define SIP_PORT 5060 #define SIP_PORT 5060
#define SIP_TIMEOUT 3600 #define SIP_TIMEOUT 3600

View File

@ -50,7 +50,8 @@ struct nfgenmsg {
#define NFNL_SUBSYS_IPSET 6 #define NFNL_SUBSYS_IPSET 6
#define NFNL_SUBSYS_ACCT 7 #define NFNL_SUBSYS_ACCT 7
#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 #define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8
#define NFNL_SUBSYS_COUNT 9 #define NFNL_SUBSYS_CTHELPER 9
#define NFNL_SUBSYS_COUNT 10
#ifdef __KERNEL__ #ifdef __KERNEL__

View File

@ -191,6 +191,7 @@ enum ctattr_expect_nat {
enum ctattr_help { enum ctattr_help {
CTA_HELP_UNSPEC, CTA_HELP_UNSPEC,
CTA_HELP_NAME, CTA_HELP_NAME,
CTA_HELP_INFO,
__CTA_HELP_MAX __CTA_HELP_MAX
}; };
#define CTA_HELP_MAX (__CTA_HELP_MAX - 1) #define CTA_HELP_MAX (__CTA_HELP_MAX - 1)

View File

@ -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 */

View File

@ -42,6 +42,8 @@ enum nfqnl_attr_type {
NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */
NFQA_HWADDR, /* nfqnl_msg_packet_hw */ NFQA_HWADDR, /* nfqnl_msg_packet_hw */
NFQA_PAYLOAD, /* opaque data payload */ NFQA_PAYLOAD, /* opaque data payload */
NFQA_CT, /* nf_conntrack_netlink.h */
NFQA_CT_INFO, /* enum ip_conntrack_info */
__NFQA_MAX __NFQA_MAX
}; };
@ -92,5 +94,6 @@ enum nfqnl_attr_config {
/* Flags for NFQA_CFG_FLAGS */ /* Flags for NFQA_CFG_FLAGS */
#define NFQA_CFG_F_FAIL_OPEN (1 << 0) #define NFQA_CFG_F_FAIL_OPEN (1 << 0)
#define NFQA_CFG_F_CONNTRACK (1 << 1)
#endif /* _NFNETLINK_QUEUE_H */ #endif /* _NFNETLINK_QUEUE_H */

View File

@ -66,6 +66,7 @@ enum nf_ip_hook_priorities {
NF_IP_PRI_SECURITY = 50, NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225, NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX, NF_IP_PRI_LAST = INT_MAX,
}; };

View File

@ -71,6 +71,7 @@ enum nf_ip6_hook_priorities {
NF_IP6_PRI_SECURITY = 50, NF_IP6_PRI_SECURITY = 50,
NF_IP6_PRI_NAT_SRC = 100, NF_IP6_PRI_NAT_SRC = 100,
NF_IP6_PRI_SELINUX_LAST = 225, NF_IP6_PRI_SELINUX_LAST = 225,
NF_IP6_PRI_CONNTRACK_HELPER = 300,
NF_IP6_PRI_LAST = INT_MAX, NF_IP6_PRI_LAST = INT_MAX,
}; };

View File

@ -39,36 +39,6 @@ union nf_conntrack_expect_proto {
/* insert expect proto private data here */ /* 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/types.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/timer.h> #include <linux/timer.h>
@ -89,12 +59,13 @@ struct nf_conn_help {
/* Helper. if any */ /* Helper. if any */
struct nf_conntrack_helper __rcu *helper; struct nf_conntrack_helper __rcu *helper;
union nf_conntrack_help help;
struct hlist_head expectations; struct hlist_head expectations;
/* Current number of expected connections */ /* Current number of expected connections */
u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; u8 expecting[NF_CT_MAX_EXPECT_CLASSES];
/* private helper information. */
char data[];
}; };
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>

View File

@ -59,10 +59,12 @@ static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
return nf_ct_net(exp->master); return nf_ct_net(exp->master);
} }
#define NF_CT_EXP_POLICY_NAME_LEN 16
struct nf_conntrack_expect_policy { struct nf_conntrack_expect_policy {
unsigned int max_expected; unsigned int max_expected;
unsigned int timeout; unsigned int timeout;
const char *name; char name[NF_CT_EXP_POLICY_NAME_LEN];
}; };
#define NF_CT_EXPECT_CLASS_DEFAULT 0 #define NF_CT_EXPECT_CLASS_DEFAULT 0

View 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. */ /* Add this type, returns pointer to data or NULL. */
void * void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id,
__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp); size_t var_alloc_len, gfp_t gfp);
#define nf_ct_ext_add(ct, id, 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 #define NF_CT_EXT_F_PREALLOC 0x0001

View File

@ -11,18 +11,27 @@
#define _NF_CONNTRACK_HELPER_H #define _NF_CONNTRACK_HELPER_H
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_extend.h> #include <net/netfilter/nf_conntrack_extend.h>
#include <net/netfilter/nf_conntrack_expect.h>
struct module; 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 #define NF_CT_HELPER_NAME_LEN 16
struct nf_conntrack_helper { struct nf_conntrack_helper {
struct hlist_node hnode; /* Internal use. */ 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 */ struct module *me; /* pointer to self */
const struct nf_conntrack_expect_policy *expect_policy; 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) */ /* Tuple of things we will help (compared against server response) */
struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple tuple;
@ -35,8 +44,12 @@ struct nf_conntrack_helper {
void (*destroy)(struct nf_conn *ct); 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); int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct);
unsigned int expect_class_max; unsigned int expect_class_max;
unsigned int flags;
unsigned int queue_num; /* For user-space helpers. */
}; };
extern struct nf_conntrack_helper * 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 int nf_conntrack_helper_register(struct nf_conntrack_helper *);
extern void nf_conntrack_helper_unregister(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, extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
gfp_t flags); 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); 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 int nf_conntrack_helper_init(struct net *net);
extern void nf_conntrack_helper_fini(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 * struct nf_ct_helper_expectfn *
nf_ct_helper_expectfn_find_by_symbol(const void *symbol); 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*/ #endif /*_NF_CONNTRACK_HELPER_H*/

View 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, extern s16 nf_nat_get_offset(const struct nf_conn *ct,
enum ip_conntrack_dir dir, enum ip_conntrack_dir dir,
u32 seq); u32 seq);
extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
u32 dir, int off);
#endif #endif

View File

@ -95,11 +95,11 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
return NF_ACCEPT; return NF_ACCEPT;
} }
static unsigned int ipv4_confirm(unsigned int hooknum, static unsigned int ipv4_helper(unsigned int hooknum,
struct sk_buff *skb, struct sk_buff *skb,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
struct nf_conn *ct; struct nf_conn *ct;
enum ip_conntrack_info ctinfo; 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. */ /* This is where we call the helper: as the packet goes out. */
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
if (!ct || ctinfo == IP_CT_RELATED_REPLY) if (!ct || ctinfo == IP_CT_RELATED_REPLY)
goto out; return NF_ACCEPT;
help = nfct_help(ct); help = nfct_help(ct);
if (!help) if (!help)
goto out; return NF_ACCEPT;
/* rcu_read_lock()ed by nf_hook_slow */ /* rcu_read_lock()ed by nf_hook_slow */
helper = rcu_dereference(help->helper); helper = rcu_dereference(help->helper);
if (!helper) if (!helper)
goto out; return NF_ACCEPT;
ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
ct, ctinfo); 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_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
"nf_ct_%s: dropping packet", helper->name); "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 */ /* adjust seqs for loopback traffic only in outgoing direction */
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
@ -184,6 +198,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
.hooknum = NF_INET_LOCAL_OUT, .hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_CONNTRACK, .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, .hook = ipv4_confirm,
.owner = THIS_MODULE, .owner = THIS_MODULE,
@ -191,6 +212,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
.hooknum = NF_INET_POST_ROUTING, .hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_CONFIRM, .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, .hook = ipv4_confirm,
.owner = THIS_MODULE, .owner = THIS_MODULE,

View File

@ -13,10 +13,10 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/udp.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_helper.h>
#include <net/netfilter/nf_conntrack_expect.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> #include <linux/netfilter/nf_conntrack_amanda.h>
MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>"); MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");

View File

@ -95,7 +95,7 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
unsigned char **data, unsigned char **data,
TransportAddress *taddr, int count) 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 dir = CTINFO2DIR(ctinfo);
int i; int i;
__be16 port; __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 *rtp_exp,
struct nf_conntrack_expect *rtcp_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 dir = CTINFO2DIR(ctinfo);
int i; int i;
u_int16_t nated_port; 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, TransportAddress *taddr, __be16 port,
struct nf_conntrack_expect *exp) 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); int dir = CTINFO2DIR(ctinfo);
u_int16_t nated_port = ntohs(port); 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, unsigned char **data, TransportAddress *taddr, int idx,
__be16 port, struct nf_conntrack_expect *exp) __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); int dir = CTINFO2DIR(ctinfo);
u_int16_t nated_port = ntohs(port); u_int16_t nated_port = ntohs(port);
union nf_inet_addr addr; union nf_inet_addr addr;

View 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); 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, static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data,
int datalen, __sum16 *check, int oldlen) int datalen, __sum16 *check, int oldlen)
{ {

View File

@ -49,7 +49,7 @@ static void pptp_nat_expected(struct nf_conn *ct,
const struct nf_nat_pptp *nat_pptp_info; const struct nf_nat_pptp *nat_pptp_info;
struct nf_nat_ipv4_range range; 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; nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info;
/* And here goes the grand finale of corrosion... */ /* And here goes the grand finale of corrosion... */
@ -123,7 +123,7 @@ pptp_outbound_pkt(struct sk_buff *skb,
__be16 new_callid; __be16 new_callid;
unsigned int cid_off; 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; nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info;
new_callid = ct_pptp_info->pns_call_id; 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_ct_pptp_master *ct_pptp_info;
struct nf_nat_pptp *nat_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; nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info;
/* save original PAC call ID in nat_info */ /* save original PAC call ID in nat_info */

View File

@ -8,10 +8,10 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/udp.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_helper.h>
#include <net/netfilter/nf_conntrack_expect.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> #include <linux/netfilter/nf_conntrack_tftp.h>
MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>"); MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");

View File

@ -143,11 +143,11 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
return NF_ACCEPT; return NF_ACCEPT;
} }
static unsigned int ipv6_confirm(unsigned int hooknum, static unsigned int ipv6_helper(unsigned int hooknum,
struct sk_buff *skb, struct sk_buff *skb,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
struct nf_conn *ct; struct nf_conn *ct;
const struct nf_conn_help *help; 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. */ /* This is where we call the helper: as the packet goes out. */
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
if (!ct || ctinfo == IP_CT_RELATED_REPLY) if (!ct || ctinfo == IP_CT_RELATED_REPLY)
goto out; return NF_ACCEPT;
help = nfct_help(ct); help = nfct_help(ct);
if (!help) if (!help)
goto out; return NF_ACCEPT;
/* rcu_read_lock()ed by nf_hook_slow */ /* rcu_read_lock()ed by nf_hook_slow */
helper = rcu_dereference(help->helper); helper = rcu_dereference(help->helper);
if (!helper) if (!helper)
goto out; return NF_ACCEPT;
protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum,
skb->len - extoff); skb->len - extoff);
@ -179,12 +179,19 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
} }
ret = helper->help(skb, protoff, ct, ctinfo); 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_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
"nf_ct_%s: dropping packet", helper->name); "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 */ /* We've seen it coming out the other side: confirm it */
return nf_conntrack_confirm(skb); return nf_conntrack_confirm(skb);
} }
@ -253,6 +260,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
.hooknum = NF_INET_LOCAL_OUT, .hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_CONNTRACK, .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, .hook = ipv6_confirm,
.owner = THIS_MODULE, .owner = THIS_MODULE,
@ -260,6 +274,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
.hooknum = NF_INET_POST_ROUTING, .hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_LAST, .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, .hook = ipv6_confirm,
.owner = THIS_MODULE, .owner = THIS_MODULE,

View File

@ -12,6 +12,14 @@ tristate "Netfilter NFACCT over NFNETLINK interface"
If this option is enabled, the kernel will include support If this option is enabled, the kernel will include support
for extended accounting via NFNETLINK. 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 config NETFILTER_NETLINK_QUEUE
tristate "Netfilter NFQUEUE over NFNETLINK interface" tristate "Netfilter NFQUEUE over NFNETLINK interface"
depends on NETFILTER_ADVANCED depends on NETFILTER_ADVANCED

View File

@ -9,6 +9,7 @@ obj-$(CONFIG_NETFILTER) = netfilter.o
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.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_QUEUE) += nfnetlink_queue.o
obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o

View File

@ -264,6 +264,10 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct)
rcu_read_unlock(); rcu_read_unlock();
} }
EXPORT_SYMBOL(nf_conntrack_destroy); EXPORT_SYMBOL(nf_conntrack_destroy);
struct nfq_ct_hook *nfq_ct_hook;
EXPORT_SYMBOL_GPL(nfq_ct_hook);
#endif /* CONFIG_NF_CONNTRACK */ #endif /* CONFIG_NF_CONNTRACK */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS

View File

@ -819,7 +819,8 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
__set_bit(IPS_EXPECTED_BIT, &ct->status); __set_bit(IPS_EXPECTED_BIT, &ct->status);
ct->master = exp->master; ct->master = exp->master;
if (exp->helper) { 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) if (help)
rcu_assign_pointer(help->helper, exp->helper); rcu_assign_pointer(help->helper, exp->helper);
} }

View File

@ -44,7 +44,8 @@ void __nf_ct_ext_destroy(struct nf_conn *ct)
EXPORT_SYMBOL(__nf_ct_ext_destroy); EXPORT_SYMBOL(__nf_ct_ext_destroy);
static void * 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; unsigned int off, len;
struct nf_ct_ext_type *t; 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]); t = rcu_dereference(nf_ct_ext_types[id]);
BUG_ON(t == NULL); BUG_ON(t == NULL);
off = ALIGN(sizeof(struct nf_ct_ext), t->align); off = ALIGN(sizeof(struct nf_ct_ext), t->align);
len = off + t->len; len = off + t->len + var_alloc_len;
alloc_size = t->alloc_size; alloc_size = t->alloc_size + var_alloc_len;
rcu_read_unlock(); rcu_read_unlock();
*ext = kzalloc(alloc_size, gfp); *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; 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; struct nf_ct_ext *old, *new;
int i, newlen, newoff; 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; old = ct->ext;
if (!old) 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)) if (__nf_ct_ext_exist(old, id))
return NULL; 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); BUG_ON(t == NULL);
newoff = ALIGN(old->len, t->align); newoff = ALIGN(old->len, t->align);
newlen = newoff + t->len; newlen = newoff + t->len + var_alloc_len;
rcu_read_unlock(); rcu_read_unlock();
new = __krealloc(old, newlen, gfp); 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); memset((void *)new + newoff, 0, newlen - newoff);
return (void *)new + 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) static void update_alloc_size(struct nf_ct_ext_type *type)
{ {

View File

@ -358,7 +358,7 @@ static int help(struct sk_buff *skb,
u32 seq; u32 seq;
int dir = CTINFO2DIR(ctinfo); int dir = CTINFO2DIR(ctinfo);
unsigned int uninitialized_var(matchlen), uninitialized_var(matchoff); 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; struct nf_conntrack_expect *exp;
union nf_inet_addr *daddr; union nf_inet_addr *daddr;
struct nf_conntrack_man cmd = {}; struct nf_conntrack_man cmd = {};
@ -512,7 +512,6 @@ out_update_nl:
} }
static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly; 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 = { static const struct nf_conntrack_expect_policy ftp_exp_policy = {
.max_expected = 1, .max_expected = 1,
@ -541,7 +540,6 @@ static void nf_conntrack_ftp_fini(void)
static int __init nf_conntrack_ftp_init(void) static int __init nf_conntrack_ftp_init(void)
{ {
int i, j = -1, ret = 0; int i, j = -1, ret = 0;
char *tmpname;
ftp_buffer = kmalloc(65536, GFP_KERNEL); ftp_buffer = kmalloc(65536, GFP_KERNEL);
if (!ftp_buffer) 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][0].tuple.src.l3num = PF_INET;
ftp[i][1].tuple.src.l3num = PF_INET6; ftp[i][1].tuple.src.l3num = PF_INET6;
for (j = 0; j < 2; j++) { 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.src.u.tcp.port = htons(ports[i]);
ftp[i][j].tuple.dst.protonum = IPPROTO_TCP; ftp[i][j].tuple.dst.protonum = IPPROTO_TCP;
ftp[i][j].expect_policy = &ftp_exp_policy; ftp[i][j].expect_policy = &ftp_exp_policy;
ftp[i][j].me = THIS_MODULE; ftp[i][j].me = THIS_MODULE;
ftp[i][j].help = help; ftp[i][j].help = help;
tmpname = &ftp_names[i][j][0];
if (ports[i] == FTP_PORT) if (ports[i] == FTP_PORT)
sprintf(tmpname, "ftp"); sprintf(ftp[i][j].name, "ftp");
else else
sprintf(tmpname, "ftp-%d", ports[i]); sprintf(ftp[i][j].name, "ftp-%d", ports[i]);
ftp[i][j].name = tmpname;
pr_debug("nf_ct_ftp: registering helper for pf: %d " pr_debug("nf_ct_ftp: registering helper for pf: %d "
"port: %d\n", "port: %d\n",

View 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, struct nf_conn *ct, enum ip_conntrack_info ctinfo,
unsigned char **data, int *datalen, int *dataoff) 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); int dir = CTINFO2DIR(ctinfo);
const struct tcphdr *th; const struct tcphdr *th;
struct tcphdr _tcph; 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 = { static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly = {
.name = "H.245", .name = "H.245",
.me = THIS_MODULE, .me = THIS_MODULE,
.data_len = sizeof(struct nf_ct_h323_master),
.tuple.src.l3num = AF_UNSPEC, .tuple.src.l3num = AF_UNSPEC,
.tuple.dst.protonum = IPPROTO_UDP, .tuple.dst.protonum = IPPROTO_UDP,
.help = h245_help, .help = h245_help,
@ -1169,6 +1170,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_q931[] __read_mostly = {
{ {
.name = "Q.931", .name = "Q.931",
.me = THIS_MODULE, .me = THIS_MODULE,
.data_len = sizeof(struct nf_ct_h323_master),
.tuple.src.l3num = AF_INET, .tuple.src.l3num = AF_INET,
.tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT),
.tuple.dst.protonum = IPPROTO_TCP, .tuple.dst.protonum = IPPROTO_TCP,
@ -1244,7 +1246,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
unsigned char **data, unsigned char **data,
TransportAddress *taddr, int count) 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 dir = CTINFO2DIR(ctinfo);
int ret = 0; int ret = 0;
int i; int i;
@ -1359,7 +1361,7 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned char **data, RegistrationRequest *rrq) 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; int ret;
typeof(set_ras_addr_hook) set_ras_addr; 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, enum ip_conntrack_info ctinfo,
unsigned char **data, RegistrationConfirm *rcf) 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 dir = CTINFO2DIR(ctinfo);
int ret; int ret;
struct nf_conntrack_expect *exp; 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, enum ip_conntrack_info ctinfo,
unsigned char **data, UnregistrationRequest *urq) 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 dir = CTINFO2DIR(ctinfo);
int ret; int ret;
typeof(set_sig_addr_hook) set_sig_addr; 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, enum ip_conntrack_info ctinfo,
unsigned char **data, AdmissionRequest *arq) 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); int dir = CTINFO2DIR(ctinfo);
__be16 port; __be16 port;
union nf_inet_addr addr; union nf_inet_addr addr;
@ -1742,6 +1744,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = {
{ {
.name = "RAS", .name = "RAS",
.me = THIS_MODULE, .me = THIS_MODULE,
.data_len = sizeof(struct nf_ct_h323_master),
.tuple.src.l3num = AF_INET, .tuple.src.l3num = AF_INET,
.tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT),
.tuple.dst.protonum = IPPROTO_UDP, .tuple.dst.protonum = IPPROTO_UDP,
@ -1751,6 +1754,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = {
{ {
.name = "RAS", .name = "RAS",
.me = THIS_MODULE, .me = THIS_MODULE,
.data_len = sizeof(struct nf_ct_h323_master),
.tuple.src.l3num = AF_INET6, .tuple.src.l3num = AF_INET6,
.tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT),
.tuple.dst.protonum = IPPROTO_UDP, .tuple.dst.protonum = IPPROTO_UDP,

View File

@ -30,8 +30,10 @@
#include <net/netfilter/nf_conntrack_extend.h> #include <net/netfilter/nf_conntrack_extend.h>
static DEFINE_MUTEX(nf_ct_helper_mutex); static DEFINE_MUTEX(nf_ct_helper_mutex);
static struct hlist_head *nf_ct_helper_hash __read_mostly; struct hlist_head *nf_ct_helper_hash __read_mostly;
static unsigned int nf_ct_helper_hsize __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 unsigned int nf_ct_helper_count __read_mostly;
static bool nf_ct_auto_assign_helper __read_mostly = true; 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); 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; 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) if (help)
INIT_HLIST_HEAD(&help->expectations); INIT_HLIST_HEAD(&help->expectations);
else else
@ -218,13 +223,13 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
} }
if (help == NULL) { if (help == NULL) {
help = nf_ct_helper_ext_add(ct, flags); help = nf_ct_helper_ext_add(ct, helper, flags);
if (help == NULL) { if (help == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
} else { } else {
memset(&help->help, 0, sizeof(help->help)); memset(help->data, 0, helper->data_len);
} }
rcu_assign_pointer(help->helper, helper); 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 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); unsigned int h = helper_hash(&me->tuple);
BUG_ON(me->expect_policy == NULL); 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); BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
mutex_lock(&nf_ct_helper_mutex); 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]); hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
nf_ct_helper_count++; nf_ct_helper_count++;
out:
mutex_unlock(&nf_ct_helper_mutex); mutex_unlock(&nf_ct_helper_mutex);
return ret;
return 0;
} }
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);

View 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 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 struct nf_conntrack_expect_policy irc_exp_policy;
static void nf_conntrack_irc_fini(void); 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) static int __init nf_conntrack_irc_init(void)
{ {
int i, ret; int i, ret;
char *tmpname;
if (max_dcc_channels < 1) { if (max_dcc_channels < 1) {
printk(KERN_ERR "nf_ct_irc: max_dcc_channels must not be zero\n"); 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].me = THIS_MODULE;
irc[i].help = help; irc[i].help = help;
tmpname = &irc_names[i][0];
if (ports[i] == IRC_PORT) if (ports[i] == IRC_PORT)
sprintf(tmpname, "irc"); sprintf(irc[i].name, "irc");
else else
sprintf(tmpname, "irc-%u", i); sprintf(irc[i].name, "irc-%u", i);
irc[i].name = tmpname;
ret = nf_conntrack_helper_register(&irc[i]); ret = nf_conntrack_helper_register(&irc[i]);
if (ret) { if (ret) {

View File

@ -46,6 +46,7 @@
#ifdef CONFIG_NF_NAT_NEEDED #ifdef CONFIG_NF_NAT_NEEDED
#include <net/netfilter/nf_nat_core.h> #include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_protocol.h> #include <net/netfilter/nf_nat_protocol.h>
#include <net/netfilter/nf_nat_helper.h>
#endif #endif
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink.h>
@ -901,7 +902,8 @@ static const struct nla_policy help_nla_policy[CTA_HELP_MAX+1] = {
}; };
static inline int 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]; 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]); *helper_name = nla_data(tb[CTA_HELP_NAME]);
if (tb[CTA_HELP_INFO])
*helpinfo = tb[CTA_HELP_INFO];
return 0; 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_conntrack_helper *helper;
struct nf_conn_help *help = nfct_help(ct); struct nf_conn_help *help = nfct_help(ct);
char *helpname = NULL; char *helpname = NULL;
struct nlattr *helpinfo = NULL;
int err; int err;
/* don't change helper of sibling connections */ /* don't change helper of sibling connections */
if (ct->master) if (ct->master)
return -EBUSY; return -EBUSY;
err = ctnetlink_parse_help(cda[CTA_HELP], &helpname); err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo);
if (err < 0) if (err < 0)
return err; return err;
@ -1213,12 +1219,16 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
} }
if (help) { 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; return 0;
}
if (help->helper) if (help->helper)
return -EBUSY; return -EBUSY;
/* need to zero data of old helper */ /* need to zero data of old helper */
memset(&help->help, 0, sizeof(help->help)); memset(help->data, 0, help->helper->data_len);
} else { } else {
/* we cannot set a helper for an existing conntrack */ /* we cannot set a helper for an existing conntrack */
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -1410,8 +1420,9 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
rcu_read_lock(); rcu_read_lock();
if (cda[CTA_HELP]) { if (cda[CTA_HELP]) {
char *helpname = NULL; char *helpname = NULL;
struct nlattr *helpinfo = NULL;
err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo);
if (err < 0) if (err < 0)
goto err2; goto err2;
@ -1440,11 +1451,14 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
} else { } else {
struct nf_conn_help *help; 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) { if (help == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err2; 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 */ /* not in hash table yet so not strictly necessary */
RCU_INIT_POINTER(help->helper, helper); RCU_INIT_POINTER(help->helper, helper);
@ -1620,6 +1634,143 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
return err; 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 * EXPECT
***********************************************************************/ ***********************************************************************/
@ -2424,7 +2575,11 @@ static int __init ctnetlink_init(void)
pr_err("ctnetlink_init: cannot register pernet operations\n"); pr_err("ctnetlink_init: cannot register pernet operations\n");
goto err_unreg_exp_subsys; 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; return 0;
err_unreg_exp_subsys: err_unreg_exp_subsys:
@ -2442,6 +2597,10 @@ static void __exit ctnetlink_exit(void)
unregister_pernet_subsys(&ctnetlink_net_ops); unregister_pernet_subsys(&ctnetlink_net_ops);
nfnetlink_subsys_unregister(&ctnl_exp_subsys); nfnetlink_subsys_unregister(&ctnl_exp_subsys);
nfnetlink_subsys_unregister(&ctnl_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); module_init(ctnetlink_init);

View 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) static void pptp_destroy_siblings(struct nf_conn *ct)
{ {
struct net *net = nf_ct_net(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; struct nf_conntrack_tuple t;
nf_ct_gre_keymap_destroy(ct); nf_ct_gre_keymap_destroy(ct);
@ -182,16 +182,16 @@ static void pptp_destroy_siblings(struct nf_conn *ct)
/* try original (pns->pac) tuple */ /* try original (pns->pac) tuple */
memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
t.dst.protonum = IPPROTO_GRE; t.dst.protonum = IPPROTO_GRE;
t.src.u.gre.key = help->help.ct_pptp_info.pns_call_id; t.src.u.gre.key = ct_pptp_info->pns_call_id;
t.dst.u.gre.key = help->help.ct_pptp_info.pac_call_id; t.dst.u.gre.key = ct_pptp_info->pac_call_id;
if (!destroy_sibling_or_exp(net, ct, &t)) if (!destroy_sibling_or_exp(net, ct, &t))
pr_debug("failed to timeout original pns->pac ct/exp\n"); pr_debug("failed to timeout original pns->pac ct/exp\n");
/* try reply (pac->pns) tuple */ /* try reply (pac->pns) tuple */
memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
t.dst.protonum = IPPROTO_GRE; t.dst.protonum = IPPROTO_GRE;
t.src.u.gre.key = help->help.ct_pptp_info.pac_call_id; t.src.u.gre.key = ct_pptp_info->pac_call_id;
t.dst.u.gre.key = help->help.ct_pptp_info.pns_call_id; t.dst.u.gre.key = ct_pptp_info->pns_call_id;
if (!destroy_sibling_or_exp(net, ct, &t)) if (!destroy_sibling_or_exp(net, ct, &t))
pr_debug("failed to timeout reply pac->pns ct/exp\n"); 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, struct nf_conn *ct,
enum ip_conntrack_info ctinfo) 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; u_int16_t msg;
__be16 cid = 0, pcid = 0; __be16 cid = 0, pcid = 0;
typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound; 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, struct nf_conn *ct,
enum ip_conntrack_info ctinfo) 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; u_int16_t msg;
__be16 cid = 0, pcid = 0; __be16 cid = 0, pcid = 0;
typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound; 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); 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; const struct tcphdr *tcph;
struct tcphdr _tcph; struct tcphdr _tcph;
const struct pptp_pkt_hdr *pptph; 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 = { static struct nf_conntrack_helper pptp __read_mostly = {
.name = "pptp", .name = "pptp",
.me = THIS_MODULE, .me = THIS_MODULE,
.data_len = sizeof(struct nf_ct_pptp_master),
.tuple.src.l3num = AF_INET, .tuple.src.l3num = AF_INET,
.tuple.src.u.tcp.port = cpu_to_be16(PPTP_CONTROL_PORT), .tuple.src.u.tcp.port = cpu_to_be16(PPTP_CONTROL_PORT),
.tuple.dst.protonum = IPPROTO_TCP, .tuple.dst.protonum = IPPROTO_TCP,

View 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 net *net = nf_ct_net(ct);
struct netns_proto_gre *net_gre = gre_pernet(net); 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; struct nf_ct_gre_keymap **kmp, *km;
kmp = &help->help.ct_pptp_info.keymap[dir]; kmp = &ct_pptp_info->keymap[dir];
if (*kmp) { if (*kmp) {
/* check whether it's a retransmission */ /* check whether it's a retransmission */
read_lock_bh(&net_gre->keymap_lock); 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 net *net = nf_ct_net(ct);
struct netns_proto_gre *net_gre = gre_pernet(net); 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; enum ip_conntrack_dir dir;
pr_debug("entering for ct %p\n", ct); pr_debug("entering for ct %p\n", ct);
write_lock_bh(&net_gre->keymap_lock); write_lock_bh(&net_gre->keymap_lock);
for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { 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", pr_debug("removing %p from list\n",
help->help.ct_pptp_info.keymap[dir]); ct_pptp_info->keymap[dir]);
list_del(&help->help.ct_pptp_info.keymap[dir]->list); list_del(&ct_pptp_info->keymap[dir]->list);
kfree(help->help.ct_pptp_info.keymap[dir]); kfree(ct_pptp_info->keymap[dir]);
help->help.ct_pptp_info.keymap[dir] = NULL; ct_pptp_info->keymap[dir] = NULL;
} }
} }
write_unlock_bh(&net_gre->keymap_lock); write_unlock_bh(&net_gre->keymap_lock);

View File

@ -69,13 +69,12 @@ static int help(struct sk_buff *skb,
void *sb_ptr; void *sb_ptr;
int ret = NF_ACCEPT; int ret = NF_ACCEPT;
int dir = CTINFO2DIR(ctinfo); 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_expect *exp;
struct nf_conntrack_tuple *tuple; struct nf_conntrack_tuple *tuple;
struct sane_request *req; struct sane_request *req;
struct sane_reply_net_start *reply; 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. */ /* Until there's been traffic both ways, don't look in packets. */
if (ctinfo != IP_CT_ESTABLISHED && if (ctinfo != IP_CT_ESTABLISHED &&
ctinfo != IP_CT_ESTABLISHED_REPLY) ctinfo != IP_CT_ESTABLISHED_REPLY)
@ -163,7 +162,6 @@ out:
} }
static struct nf_conntrack_helper sane[MAX_PORTS][2] __read_mostly; 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 = { static const struct nf_conntrack_expect_policy sane_exp_policy = {
.max_expected = 1, .max_expected = 1,
@ -190,7 +188,6 @@ static void nf_conntrack_sane_fini(void)
static int __init nf_conntrack_sane_init(void) static int __init nf_conntrack_sane_init(void)
{ {
int i, j = -1, ret = 0; int i, j = -1, ret = 0;
char *tmpname;
sane_buffer = kmalloc(65536, GFP_KERNEL); sane_buffer = kmalloc(65536, GFP_KERNEL);
if (!sane_buffer) 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][0].tuple.src.l3num = PF_INET;
sane[i][1].tuple.src.l3num = PF_INET6; sane[i][1].tuple.src.l3num = PF_INET6;
for (j = 0; j < 2; j++) { 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.src.u.tcp.port = htons(ports[i]);
sane[i][j].tuple.dst.protonum = IPPROTO_TCP; sane[i][j].tuple.dst.protonum = IPPROTO_TCP;
sane[i][j].expect_policy = &sane_exp_policy; sane[i][j].expect_policy = &sane_exp_policy;
sane[i][j].me = THIS_MODULE; sane[i][j].me = THIS_MODULE;
sane[i][j].help = help; sane[i][j].help = help;
tmpname = &sane_names[i][j][0];
if (ports[i] == SANE_PORT) if (ports[i] == SANE_PORT)
sprintf(tmpname, "sane"); sprintf(sane[i][j].name, "sane");
else else
sprintf(tmpname, "sane-%d", ports[i]); sprintf(sane[i][j].name, "sane-%d", ports[i]);
sane[i][j].name = tmpname;
pr_debug("nf_ct_sane: registering helper for pf: %d " pr_debug("nf_ct_sane: registering helper for pf: %d "
"port: %d\n", "port: %d\n",

View File

@ -1075,12 +1075,12 @@ static int process_invite_response(struct sk_buff *skb, unsigned int dataoff,
{ {
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &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) || if ((code >= 100 && code <= 199) ||
(code >= 200 && code <= 299)) (code >= 200 && code <= 299))
return process_sdp(skb, dataoff, dptr, datalen, cseq); 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); flush_expectations(ct, true);
return NF_ACCEPT; return NF_ACCEPT;
} }
@ -1091,12 +1091,12 @@ static int process_update_response(struct sk_buff *skb, unsigned int dataoff,
{ {
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &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) || if ((code >= 100 && code <= 199) ||
(code >= 200 && code <= 299)) (code >= 200 && code <= 299))
return process_sdp(skb, dataoff, dptr, datalen, cseq); 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); flush_expectations(ct, true);
return NF_ACCEPT; return NF_ACCEPT;
} }
@ -1107,12 +1107,12 @@ static int process_prack_response(struct sk_buff *skb, unsigned int dataoff,
{ {
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &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) || if ((code >= 100 && code <= 199) ||
(code >= 200 && code <= 299)) (code >= 200 && code <= 299))
return process_sdp(skb, dataoff, dptr, datalen, cseq); 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); flush_expectations(ct, true);
return NF_ACCEPT; return NF_ACCEPT;
} }
@ -1123,13 +1123,13 @@ static int process_invite_request(struct sk_buff *skb, unsigned int dataoff,
{ {
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &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; unsigned int ret;
flush_expectations(ct, true); flush_expectations(ct, true);
ret = process_sdp(skb, dataoff, dptr, datalen, cseq); ret = process_sdp(skb, dataoff, dptr, datalen, cseq);
if (ret == NF_ACCEPT) if (ret == NF_ACCEPT)
help->help.ct_sip_info.invite_cseq = cseq; ct_sip_info->invite_cseq = cseq;
return ret; return ret;
} }
@ -1154,7 +1154,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int dataoff,
{ {
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &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); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
unsigned int matchoff, matchlen; unsigned int matchoff, matchlen;
struct nf_conntrack_expect *exp; struct nf_conntrack_expect *exp;
@ -1235,7 +1235,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int dataoff,
store_cseq: store_cseq:
if (ret == NF_ACCEPT) if (ret == NF_ACCEPT)
help->help.ct_sip_info.register_cseq = cseq; ct_sip_info->register_cseq = cseq;
return ret; return ret;
} }
@ -1245,7 +1245,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int dataoff,
{ {
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &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); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
union nf_inet_addr addr; union nf_inet_addr addr;
__be16 port; __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 * responses, so we store the sequence number of the last valid
* request and compare it here. * request and compare it here.
*/ */
if (help->help.ct_sip_info.register_cseq != cseq) if (ct_sip_info->register_cseq != cseq)
return NF_ACCEPT; return NF_ACCEPT;
if (code >= 100 && code <= 199) if (code >= 100 && code <= 199)
@ -1556,7 +1556,6 @@ static void nf_conntrack_sip_fini(void)
static int __init nf_conntrack_sip_init(void) static int __init nf_conntrack_sip_init(void)
{ {
int i, j, ret; int i, j, ret;
char *tmpname;
if (ports_c == 0) if (ports_c == 0)
ports[ports_c++] = SIP_PORT; ports[ports_c++] = SIP_PORT;
@ -1579,17 +1578,16 @@ static int __init nf_conntrack_sip_init(void)
sip[i][3].help = sip_help_tcp; sip[i][3].help = sip_help_tcp;
for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { 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].tuple.src.u.udp.port = htons(ports[i]);
sip[i][j].expect_policy = sip_exp_policy; sip[i][j].expect_policy = sip_exp_policy;
sip[i][j].expect_class_max = SIP_EXPECT_MAX; sip[i][j].expect_class_max = SIP_EXPECT_MAX;
sip[i][j].me = THIS_MODULE; sip[i][j].me = THIS_MODULE;
tmpname = &sip_names[i][j][0];
if (ports[i] == SIP_PORT) if (ports[i] == SIP_PORT)
sprintf(tmpname, "sip"); sprintf(sip_names[i][j], "sip");
else else
sprintf(tmpname, "sip-%u", i); sprintf(sip_names[i][j], "sip-%u", i);
sip[i][j].name = tmpname;
pr_debug("port #%u: %u\n", i, ports[i]); pr_debug("port #%u: %u\n", i, ports[i]);

View 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 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 = { static const struct nf_conntrack_expect_policy tftp_exp_policy = {
.max_expected = 1, .max_expected = 1,
@ -112,7 +111,6 @@ static void nf_conntrack_tftp_fini(void)
static int __init nf_conntrack_tftp_init(void) static int __init nf_conntrack_tftp_init(void)
{ {
int i, j, ret; int i, j, ret;
char *tmpname;
if (ports_c == 0) if (ports_c == 0)
ports[ports_c++] = TFTP_PORT; 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].me = THIS_MODULE;
tftp[i][j].help = tftp_help; tftp[i][j].help = tftp_help;
tmpname = &tftp_names[i][j][0];
if (ports[i] == TFTP_PORT) if (ports[i] == TFTP_PORT)
sprintf(tmpname, "tftp"); sprintf(tftp[i][j].name, "tftp");
else else
sprintf(tmpname, "tftp-%u", i); sprintf(tftp[i][j].name, "tftp-%u", i);
tftp[i][j].name = tmpname;
ret = nf_conntrack_helper_register(&tftp[i][j]); ret = nf_conntrack_helper_register(&tftp[i][j]);
if (ret) { if (ret) {

View File

@ -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);

View File

@ -30,6 +30,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/netfilter/nf_queue.h> #include <net/netfilter/nf_queue.h>
#include <net/netfilter/nf_conntrack.h>
#include <linux/atomic.h> #include <linux/atomic.h>
@ -233,6 +234,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
struct sk_buff *entskb = entry->skb; struct sk_buff *entskb = entry->skb;
struct net_device *indev; struct net_device *indev;
struct net_device *outdev; 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)) size = NLMSG_SPACE(sizeof(struct nfgenmsg))
+ nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr))
@ -266,6 +270,17 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
break; 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); skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) if (!skb)
@ -389,6 +404,24 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
BUG(); 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; nlh->nlmsg_len = skb->tail - old_tail;
return skb; return skb;
@ -469,12 +502,10 @@ err_out:
} }
static int 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; struct sk_buff *nskb;
int diff;
diff = data_len - e->skb->len;
if (diff < 0) { if (diff < 0) {
if (pskb_trim(e->skb, data_len)) if (pskb_trim(e->skb, data_len))
return -ENOMEM; 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_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) },
[NFQA_MARK] = { .type = NLA_U32 }, [NFQA_MARK] = { .type = NLA_U32 },
[NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, [NFQA_PAYLOAD] = { .type = NLA_UNSPEC },
[NFQA_CT] = { .type = NLA_UNSPEC },
}; };
static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { 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; struct nfqnl_instance *queue;
unsigned int verdict; unsigned int verdict;
struct nf_queue_entry *entry; 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); queue = instance_lookup(queue_num);
if (!queue) if (!queue)
@ -750,12 +785,28 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
if (entry == NULL) if (entry == NULL)
return -ENOENT; return -ENOENT;
if (nfqa[NFQA_PAYLOAD]) { rcu_read_lock();
if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), nfq_ct = rcu_dereference(nfq_ct_hook);
nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0) if (nfq_ct != NULL &&
verdict = NF_DROP; (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]),
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]) if (nfqa[NFQA_MARK])
entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK]));

View File

@ -112,6 +112,8 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
goto err3; goto err3;
if (info->helper[0]) { if (info->helper[0]) {
struct nf_conntrack_helper *helper;
ret = -ENOENT; ret = -ENOENT;
proto = xt_ct_find_proto(par); proto = xt_ct_find_proto(par);
if (!proto) { if (!proto) {
@ -120,19 +122,21 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
goto err3; goto err3;
} }
ret = -ENOMEM;
help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
if (help == NULL)
goto err3;
ret = -ENOENT; ret = -ENOENT;
help->helper = nf_conntrack_helper_try_module_get(info->helper, helper = nf_conntrack_helper_try_module_get(info->helper,
par->family, par->family,
proto); proto);
if (help->helper == NULL) { if (helper == NULL) {
pr_info("No such helper \"%s\"\n", info->helper); pr_info("No such helper \"%s\"\n", info->helper);
goto err3; 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); __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; goto err3;
if (info->helper[0]) { if (info->helper[0]) {
struct nf_conntrack_helper *helper;
ret = -ENOENT; ret = -ENOENT;
proto = xt_ct_find_proto(par); proto = xt_ct_find_proto(par);
if (!proto) { if (!proto) {
@ -210,19 +216,21 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
goto err3; goto err3;
} }
ret = -ENOMEM;
help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
if (help == NULL)
goto err3;
ret = -ENOENT; ret = -ENOENT;
help->helper = nf_conntrack_helper_try_module_get(info->helper, helper = nf_conntrack_helper_try_module_get(info->helper,
par->family, par->family,
proto); proto);
if (help->helper == NULL) { if (helper == NULL) {
pr_info("No such helper \"%s\"\n", info->helper); pr_info("No such helper \"%s\"\n", info->helper);
goto err3; 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 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT