mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 06:31:52 +00:00
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:
commit
82f437b950
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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)
|
||||
|
55
include/linux/netfilter/nfnetlink_cthelper.h
Normal file
55
include/linux/netfilter/nfnetlink_cthelper.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -11,18 +11,27 @@
|
||||
#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*/
|
||||
|
@ -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
|
||||
|
@ -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) &&
|
||||
@ -184,6 +198,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
|
||||
.hooknum = NF_INET_LOCAL_OUT,
|
||||
.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,
|
||||
@ -191,6 +212,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
|
||||
.hooknum = NF_INET_POST_ROUTING,
|
||||
.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,
|
||||
|
@ -13,10 +13,10 @@
|
||||
#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>");
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 */
|
||||
|
@ -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>");
|
||||
|
@ -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);
|
||||
}
|
||||
@ -253,6 +260,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
|
||||
.hooknum = NF_INET_LOCAL_OUT,
|
||||
.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,
|
||||
@ -260,6 +274,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
|
||||
.hooknum = NF_INET_POST_ROUTING,
|
||||
.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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -30,8 +30,10 @@
|
||||
#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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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) {
|
||||
|
672
net/netfilter/nfnetlink_cthelper.c
Normal file
672
net/netfilter/nfnetlink_cthelper.c
Normal 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);
|
@ -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,12 +785,28 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
|
||||
if (entry == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
if (nfqa[NFQA_PAYLOAD]) {
|
||||
if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
|
||||
nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0)
|
||||
verdict = NF_DROP;
|
||||
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]),
|
||||
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]));
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user