[NETFILTER]: Add ctnetlink subsystem

Add ctnetlink subsystem for userspace-access to ip_conntrack table.
This allows reading and updating of existing entries, as well as
creating new ones (and new expect's) via nfnetlink.

Please note the 'strange' byte order: nfattr (tag+length) are in host
byte order, while the payload is always guaranteed to be in network
byte order.  This allows a simple userspace process to encapsulate netlink
messages into arch-independent udp packets by just processing/swapping the
headers and not knowing anything about the actual payload.

Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Harald Welte 2005-08-09 19:32:58 -07:00 committed by David S. Miller
parent 6f1cf16582
commit 080774a243
23 changed files with 2277 additions and 100 deletions

View File

@ -56,7 +56,7 @@ struct nfgenmsg {
u_int16_t res_id; /* resource id */ u_int16_t res_id; /* resource id */
} __attribute__ ((packed)); } __attribute__ ((packed));
#define NFNETLINK_V1 1 #define NFNETLINK_V0 0
#define NFM_NFA(n) ((struct nfattr *)(((char *)(n)) \ #define NFM_NFA(n) ((struct nfattr *)(((char *)(n)) \
+ NLMSG_ALIGN(sizeof(struct nfgenmsg)))) + NLMSG_ALIGN(sizeof(struct nfgenmsg))))
@ -81,6 +81,7 @@ enum nfnl_subsys_id {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/netlink.h>
#include <linux/capability.h> #include <linux/capability.h>
struct nfnl_callback struct nfnl_callback

View File

@ -0,0 +1,123 @@
#ifndef _IPCONNTRACK_NETLINK_H
#define _IPCONNTRACK_NETLINK_H
#include <linux/netfilter/nfnetlink.h>
enum cntl_msg_types {
IPCTNL_MSG_CT_NEW,
IPCTNL_MSG_CT_GET,
IPCTNL_MSG_CT_DELETE,
IPCTNL_MSG_CT_GET_CTRZERO,
IPCTNL_MSG_MAX
};
enum ctnl_exp_msg_types {
IPCTNL_MSG_EXP_NEW,
IPCTNL_MSG_EXP_GET,
IPCTNL_MSG_EXP_DELETE,
IPCTNL_MSG_EXP_MAX
};
enum ctattr_type {
CTA_UNSPEC,
CTA_TUPLE_ORIG,
CTA_TUPLE_REPLY,
CTA_STATUS,
CTA_PROTOINFO,
CTA_HELP,
CTA_NAT,
CTA_TIMEOUT,
CTA_MARK,
CTA_COUNTERS_ORIG,
CTA_COUNTERS_REPLY,
CTA_USE,
CTA_EXPECT,
CTA_ID,
__CTA_MAX
};
#define CTA_MAX (__CTA_MAX - 1)
enum ctattr_tuple {
CTA_TUPLE_UNSPEC,
CTA_TUPLE_IP,
CTA_TUPLE_PROTO,
__CTA_TUPLE_MAX
};
#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
enum ctattr_ip {
CTA_IP_UNSPEC,
CTA_IP_V4_SRC,
CTA_IP_V4_DST,
CTA_IP_V6_SRC,
CTA_IP_V6_DST,
__CTA_IP_MAX
};
#define CTA_IP_MAX (__CTA_IP_MAX - 1)
enum ctattr_l4proto {
CTA_PROTO_UNSPEC,
CTA_PROTO_NUM,
CTA_PROTO_SRC_PORT,
CTA_PROTO_DST_PORT,
CTA_PROTO_ICMP_ID,
CTA_PROTO_ICMP_TYPE,
CTA_PROTO_ICMP_CODE,
__CTA_PROTO_MAX
};
#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
enum ctattr_protoinfo {
CTA_PROTOINFO_UNSPEC,
CTA_PROTOINFO_TCP_STATE,
__CTA_PROTOINFO_MAX
};
#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
enum ctattr_counters {
CTA_COUNTERS_UNSPEC,
CTA_COUNTERS_PACKETS,
CTA_COUNTERS_BYTES,
__CTA_COUNTERS_MAX
};
#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
enum ctattr_nat {
CTA_NAT_UNSPEC,
CTA_NAT_MINIP,
CTA_NAT_MAXIP,
CTA_NAT_PROTO,
__CTA_NAT_MAX
};
#define CTA_NAT_MAX (__CTA_NAT_MAX - 1)
enum ctattr_protonat {
CTA_PROTONAT_UNSPEC,
CTA_PROTONAT_PORT_MIN,
CTA_PROTONAT_PORT_MAX,
__CTA_PROTONAT_MAX
};
#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
enum ctattr_expect {
CTA_EXPECT_UNSPEC,
CTA_EXPECT_TUPLE,
CTA_EXPECT_MASK,
CTA_EXPECT_TIMEOUT,
CTA_EXPECT_ID,
__CTA_EXPECT_MAX
};
#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1)
enum ctattr_help {
CTA_HELP_UNSPEC,
CTA_HELP_NAME,
__CTA_HELP_MAX
};
#define CTA_HELP_MAX (__CTA_HELP_MAX - 1)
#define CTA_HELP_MAXNAMESIZE 32
#endif /* _IPCONNTRACK_NETLINK_H */

View File

@ -209,6 +209,9 @@ struct ip_conntrack
/* Current number of expected connections */ /* Current number of expected connections */
unsigned int expecting; unsigned int expecting;
/* Unique ID that identifies this conntrack*/
unsigned int id;
/* Helper, if any. */ /* Helper, if any. */
struct ip_conntrack_helper *helper; struct ip_conntrack_helper *helper;
@ -257,6 +260,9 @@ struct ip_conntrack_expect
/* Usage count. */ /* Usage count. */
atomic_t use; atomic_t use;
/* Unique ID */
unsigned int id;
#ifdef CONFIG_IP_NF_NAT_NEEDED #ifdef CONFIG_IP_NF_NAT_NEEDED
/* This is the original per-proto part, used to map the /* This is the original per-proto part, used to map the
* expected connection the way the recipient expects. */ * expected connection the way the recipient expects. */
@ -296,7 +302,12 @@ ip_conntrack_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
} }
/* decrement reference count on a conntrack */ /* decrement reference count on a conntrack */
extern void ip_conntrack_put(struct ip_conntrack *ct); static inline void
ip_conntrack_put(struct ip_conntrack *ct)
{
IP_NF_ASSERT(ct);
nf_conntrack_put(&ct->ct_general);
}
/* call to create an explicit dependency on ip_conntrack. */ /* call to create an explicit dependency on ip_conntrack. */
extern void need_ip_conntrack(void); extern void need_ip_conntrack(void);
@ -331,6 +342,39 @@ extern void
ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *data), ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *data),
void *data); void *data);
extern struct ip_conntrack_helper *
__ip_conntrack_helper_find_byname(const char *);
extern struct ip_conntrack_helper *
ip_conntrack_helper_find_get(const struct ip_conntrack_tuple *tuple);
extern void ip_conntrack_helper_put(struct ip_conntrack_helper *helper);
extern struct ip_conntrack_protocol *
__ip_conntrack_proto_find(u_int8_t protocol);
extern struct ip_conntrack_protocol *
ip_conntrack_proto_find_get(u_int8_t protocol);
extern void ip_conntrack_proto_put(struct ip_conntrack_protocol *proto);
extern void ip_ct_remove_expectations(struct ip_conntrack *ct);
extern struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *,
struct ip_conntrack_tuple *);
extern void ip_conntrack_free(struct ip_conntrack *ct);
extern void ip_conntrack_hash_insert(struct ip_conntrack *ct);
extern struct ip_conntrack_expect *
__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple);
extern struct ip_conntrack_expect *
ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple);
extern struct ip_conntrack_tuple_hash *
__ip_conntrack_find(const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack *ignored_conntrack);
extern void ip_conntrack_flush(void);
/* It's confirmed if it is, or has been in the hash table. */ /* It's confirmed if it is, or has been in the hash table. */
static inline int is_confirmed(struct ip_conntrack *ct) static inline int is_confirmed(struct ip_conntrack *ct)
{ {

View File

@ -2,6 +2,9 @@
#define _IP_CONNTRACK_CORE_H #define _IP_CONNTRACK_CORE_H
#include <linux/netfilter.h> #include <linux/netfilter.h>
#define MAX_IP_CT_PROTO 256
extern struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO];
/* This header is used to share core functionality between the /* This header is used to share core functionality between the
standalone connection tracking module, and the compatibility layer's use standalone connection tracking module, and the compatibility layer's use
of connection tracking. */ of connection tracking. */
@ -53,6 +56,8 @@ struct ip_conntrack_ecache;
extern void __ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ec); extern void __ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ec);
#endif #endif
extern void __ip_ct_expect_unlink_destroy(struct ip_conntrack_expect *exp);
extern struct list_head *ip_conntrack_hash; extern struct list_head *ip_conntrack_hash;
extern struct list_head ip_conntrack_expect_list; extern struct list_head ip_conntrack_expect_list;
extern rwlock_t ip_conntrack_lock; extern rwlock_t ip_conntrack_lock;

View File

@ -24,6 +24,8 @@ struct ip_conntrack_helper
int (*help)(struct sk_buff **pskb, int (*help)(struct sk_buff **pskb,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info conntrackinfo); enum ip_conntrack_info conntrackinfo);
int (*to_nfattr)(struct sk_buff *skb, const struct ip_conntrack *ct);
}; };
extern int ip_conntrack_helper_register(struct ip_conntrack_helper *); extern int ip_conntrack_helper_register(struct ip_conntrack_helper *);

View File

@ -2,6 +2,7 @@
#ifndef _IP_CONNTRACK_PROTOCOL_H #ifndef _IP_CONNTRACK_PROTOCOL_H
#define _IP_CONNTRACK_PROTOCOL_H #define _IP_CONNTRACK_PROTOCOL_H
#include <linux/netfilter_ipv4/ip_conntrack.h> #include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter/nfnetlink_conntrack.h>
struct seq_file; struct seq_file;
@ -47,22 +48,22 @@ struct ip_conntrack_protocol
int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
unsigned int hooknum); unsigned int hooknum);
/* convert protoinfo to nfnetink attributes */
int (*to_nfattr)(struct sk_buff *skb, struct nfattr *nfa,
const struct ip_conntrack *ct);
int (*tuple_to_nfattr)(struct sk_buff *skb,
const struct ip_conntrack_tuple *t);
int (*nfattr_to_tuple)(struct nfattr *tb[],
struct ip_conntrack_tuple *t);
/* Module (if any) which this is connected to. */ /* Module (if any) which this is connected to. */
struct module *me; struct module *me;
}; };
#define MAX_IP_CT_PROTO 256
extern struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO];
/* Protocol registration. */ /* Protocol registration. */
extern int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto); extern int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto);
extern void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto); extern void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto);
static inline struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol)
{
return ip_ct_protos[protocol];
}
/* Existing built-in protocols */ /* Existing built-in protocols */
extern struct ip_conntrack_protocol ip_conntrack_protocol_tcp; extern struct ip_conntrack_protocol ip_conntrack_protocol_tcp;
extern struct ip_conntrack_protocol ip_conntrack_protocol_udp; extern struct ip_conntrack_protocol ip_conntrack_protocol_udp;
@ -73,6 +74,11 @@ extern int ip_conntrack_protocol_tcp_init(void);
/* Log invalid packets */ /* Log invalid packets */
extern unsigned int ip_ct_log_invalid; extern unsigned int ip_ct_log_invalid;
extern int ip_ct_port_tuple_to_nfattr(struct sk_buff *,
const struct ip_conntrack_tuple *);
extern int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[],
struct ip_conntrack_tuple *);
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
#ifdef DEBUG_INVALID_PACKETS #ifdef DEBUG_INVALID_PACKETS
#define LOG_INVALID(proto) \ #define LOG_INVALID(proto) \

View File

@ -4,6 +4,9 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter/nfnetlink_conntrack.h>
struct iphdr; struct iphdr;
struct ip_nat_range; struct ip_nat_range;
@ -15,6 +18,8 @@ struct ip_nat_protocol
/* Protocol number. */ /* Protocol number. */
unsigned int protonum; unsigned int protonum;
struct module *me;
/* Translate a packet to the target according to manip type. /* Translate a packet to the target according to manip type.
Return true if succeeded. */ Return true if succeeded. */
int (*manip_pkt)(struct sk_buff **pskb, int (*manip_pkt)(struct sk_buff **pskb,
@ -43,19 +48,20 @@ struct ip_nat_protocol
unsigned int (*print_range)(char *buffer, unsigned int (*print_range)(char *buffer,
const struct ip_nat_range *range); const struct ip_nat_range *range);
};
#define MAX_IP_NAT_PROTO 256 int (*range_to_nfattr)(struct sk_buff *skb,
extern struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO]; const struct ip_nat_range *range);
int (*nfattr_to_range)(struct nfattr *tb[],
struct ip_nat_range *range);
};
/* Protocol registration. */ /* Protocol registration. */
extern int ip_nat_protocol_register(struct ip_nat_protocol *proto); extern int ip_nat_protocol_register(struct ip_nat_protocol *proto);
extern void ip_nat_protocol_unregister(struct ip_nat_protocol *proto); extern void ip_nat_protocol_unregister(struct ip_nat_protocol *proto);
static inline struct ip_nat_protocol *ip_nat_find_proto(u_int8_t protocol) extern struct ip_nat_protocol *ip_nat_proto_find_get(u_int8_t protocol);
{ extern void ip_nat_proto_put(struct ip_nat_protocol *proto);
return ip_nat_protos[protocol];
}
/* Built-in protocols. */ /* Built-in protocols. */
extern struct ip_nat_protocol ip_nat_protocol_tcp; extern struct ip_nat_protocol ip_nat_protocol_tcp;
@ -67,4 +73,9 @@ extern int init_protocols(void) __init;
extern void cleanup_protocols(void); extern void cleanup_protocols(void);
extern struct ip_nat_protocol *find_nat_proto(u_int16_t protonum); extern struct ip_nat_protocol *find_nat_proto(u_int16_t protonum);
extern int ip_nat_port_range_to_nfattr(struct sk_buff *skb,
const struct ip_nat_range *range);
extern int ip_nat_port_nfattr_to_range(struct nfattr *tb[],
struct ip_nat_range *range);
#endif /*_IP_NAT_PROTO_H*/ #endif /*_IP_NAT_PROTO_H*/

View File

@ -702,5 +702,12 @@ config IP_NF_ARP_MANGLE
Allows altering the ARP packet payload: source and destination Allows altering the ARP packet payload: source and destination
hardware and network addresses. hardware and network addresses.
config IP_NF_CONNTRACK_NETLINK
tristate 'Connection tracking netlink interface'
depends on IP_NF_CONNTRACK && NETFILTER_NETLINK
help
This option enables support for a netlink-based userspace interface
endmenu endmenu

View File

@ -9,6 +9,10 @@ iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_core.o ip_nat_helpe
# connection tracking # connection tracking
obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
# conntrack netlink interface
obj-$(CONFIG_IP_NF_CONNTRACK_NETLINK) += ip_conntrack_netlink.o
# SCTP protocol connection tracking # SCTP protocol connection tracking
obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o

View File

@ -50,7 +50,7 @@
#include <linux/netfilter_ipv4/ip_conntrack_core.h> #include <linux/netfilter_ipv4/ip_conntrack_core.h>
#include <linux/netfilter_ipv4/listhelp.h> #include <linux/netfilter_ipv4/listhelp.h>
#define IP_CONNTRACK_VERSION "2.2" #define IP_CONNTRACK_VERSION "2.3"
#if 0 #if 0
#define DEBUGP printk #define DEBUGP printk
@ -77,6 +77,8 @@ unsigned int ip_ct_log_invalid;
static LIST_HEAD(unconfirmed); static LIST_HEAD(unconfirmed);
static int ip_conntrack_vmalloc; static int ip_conntrack_vmalloc;
static unsigned int ip_conntrack_next_id = 1;
static unsigned int ip_conntrack_expect_next_id = 1;
#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
struct notifier_block *ip_conntrack_chain; struct notifier_block *ip_conntrack_chain;
struct notifier_block *ip_conntrack_expect_chain; struct notifier_block *ip_conntrack_expect_chain;
@ -154,13 +156,6 @@ void ip_conntrack_event_cache_init(const struct sk_buff *skb)
DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat); DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat);
void
ip_conntrack_put(struct ip_conntrack *ct)
{
IP_NF_ASSERT(ct);
nf_conntrack_put(&ct->ct_general);
}
static int ip_conntrack_hash_rnd_initted; static int ip_conntrack_hash_rnd_initted;
static unsigned int ip_conntrack_hash_rnd; static unsigned int ip_conntrack_hash_rnd;
@ -222,6 +217,12 @@ static void unlink_expect(struct ip_conntrack_expect *exp)
exp->master->expecting--; exp->master->expecting--;
} }
void __ip_ct_expect_unlink_destroy(struct ip_conntrack_expect *exp)
{
unlink_expect(exp);
ip_conntrack_expect_put(exp);
}
static void expectation_timed_out(unsigned long ul_expect) static void expectation_timed_out(unsigned long ul_expect)
{ {
struct ip_conntrack_expect *exp = (void *)ul_expect; struct ip_conntrack_expect *exp = (void *)ul_expect;
@ -232,6 +233,33 @@ static void expectation_timed_out(unsigned long ul_expect)
ip_conntrack_expect_put(exp); ip_conntrack_expect_put(exp);
} }
struct ip_conntrack_expect *
__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple)
{
struct ip_conntrack_expect *i;
list_for_each_entry(i, &ip_conntrack_expect_list, list) {
if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) {
atomic_inc(&i->use);
return i;
}
}
return NULL;
}
/* Just find a expectation corresponding to a tuple. */
struct ip_conntrack_expect *
ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple)
{
struct ip_conntrack_expect *i;
read_lock_bh(&ip_conntrack_lock);
i = __ip_conntrack_expect_find(tuple);
read_unlock_bh(&ip_conntrack_lock);
return i;
}
/* If an expectation for this connection is found, it gets delete from /* If an expectation for this connection is found, it gets delete from
* global list then returned. */ * global list then returned. */
static struct ip_conntrack_expect * static struct ip_conntrack_expect *
@ -256,7 +284,7 @@ find_expectation(const struct ip_conntrack_tuple *tuple)
} }
/* delete all expectations for this conntrack */ /* delete all expectations for this conntrack */
static void remove_expectations(struct ip_conntrack *ct) void ip_ct_remove_expectations(struct ip_conntrack *ct)
{ {
struct ip_conntrack_expect *i, *tmp; struct ip_conntrack_expect *i, *tmp;
@ -286,7 +314,7 @@ clean_from_lists(struct ip_conntrack *ct)
LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]); LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]);
/* Destroy all pending expectations */ /* Destroy all pending expectations */
remove_expectations(ct); ip_ct_remove_expectations(ct);
} }
static void static void
@ -304,7 +332,7 @@ destroy_conntrack(struct nf_conntrack *nfct)
/* To make sure we don't get any weird locking issues here: /* To make sure we don't get any weird locking issues here:
* destroy_conntrack() MUST NOT be called with a write lock * destroy_conntrack() MUST NOT be called with a write lock
* to ip_conntrack_lock!!! -HW */ * to ip_conntrack_lock!!! -HW */
proto = ip_ct_find_proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); proto = __ip_conntrack_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
if (proto && proto->destroy) if (proto && proto->destroy)
proto->destroy(ct); proto->destroy(ct);
@ -316,7 +344,7 @@ destroy_conntrack(struct nf_conntrack *nfct)
* except TFTP can create an expectation on the first packet, * except TFTP can create an expectation on the first packet,
* before connection is in the list, so we need to clean here, * before connection is in the list, so we need to clean here,
* too. */ * too. */
remove_expectations(ct); ip_ct_remove_expectations(ct);
/* We overload first tuple to link into unconfirmed list. */ /* We overload first tuple to link into unconfirmed list. */
if (!is_confirmed(ct)) { if (!is_confirmed(ct)) {
@ -331,8 +359,7 @@ destroy_conntrack(struct nf_conntrack *nfct)
ip_conntrack_put(ct->master); ip_conntrack_put(ct->master);
DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct);
kmem_cache_free(ip_conntrack_cachep, ct); ip_conntrack_free(ct);
atomic_dec(&ip_conntrack_count);
} }
static void death_by_timeout(unsigned long ul_conntrack) static void death_by_timeout(unsigned long ul_conntrack)
@ -359,7 +386,7 @@ conntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i,
&& ip_ct_tuple_equal(tuple, &i->tuple); && ip_ct_tuple_equal(tuple, &i->tuple);
} }
static struct ip_conntrack_tuple_hash * struct ip_conntrack_tuple_hash *
__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, __ip_conntrack_find(const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack *ignored_conntrack) const struct ip_conntrack *ignored_conntrack)
{ {
@ -394,6 +421,29 @@ ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
return h; return h;
} }
static void __ip_conntrack_hash_insert(struct ip_conntrack *ct,
unsigned int hash,
unsigned int repl_hash)
{
ct->id = ++ip_conntrack_next_id;
list_prepend(&ip_conntrack_hash[hash],
&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
list_prepend(&ip_conntrack_hash[repl_hash],
&ct->tuplehash[IP_CT_DIR_REPLY].list);
}
void ip_conntrack_hash_insert(struct ip_conntrack *ct)
{
unsigned int hash, repl_hash;
hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
write_lock_bh(&ip_conntrack_lock);
__ip_conntrack_hash_insert(ct, hash, repl_hash);
write_unlock_bh(&ip_conntrack_lock);
}
/* Confirm a connection given skb; places it in hash table */ /* Confirm a connection given skb; places it in hash table */
int int
__ip_conntrack_confirm(struct sk_buff **pskb) __ip_conntrack_confirm(struct sk_buff **pskb)
@ -440,10 +490,7 @@ __ip_conntrack_confirm(struct sk_buff **pskb)
/* Remove from unconfirmed list */ /* Remove from unconfirmed list */
list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
list_prepend(&ip_conntrack_hash[hash], __ip_conntrack_hash_insert(ct, hash, repl_hash);
&ct->tuplehash[IP_CT_DIR_ORIGINAL]);
list_prepend(&ip_conntrack_hash[repl_hash],
&ct->tuplehash[IP_CT_DIR_REPLY]);
/* Timer relative to confirmation time, not original /* Timer relative to confirmation time, not original
setting time, otherwise we'd get timer wrap in setting time, otherwise we'd get timer wrap in
weird delay cases. */ weird delay cases. */
@ -527,34 +574,84 @@ static inline int helper_cmp(const struct ip_conntrack_helper *i,
return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask); return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
} }
static struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple) static struct ip_conntrack_helper *
__ip_conntrack_helper_find( const struct ip_conntrack_tuple *tuple)
{ {
return LIST_FIND(&helpers, helper_cmp, return LIST_FIND(&helpers, helper_cmp,
struct ip_conntrack_helper *, struct ip_conntrack_helper *,
tuple); tuple);
} }
/* Allocate a new conntrack: we return -ENOMEM if classification struct ip_conntrack_helper *
failed due to stress. Otherwise it really is unclassifiable. */ ip_conntrack_helper_find_get( const struct ip_conntrack_tuple *tuple)
static struct ip_conntrack_tuple_hash * {
init_conntrack(const struct ip_conntrack_tuple *tuple, struct ip_conntrack_helper *helper;
struct ip_conntrack_protocol *protocol,
struct sk_buff *skb) /* need ip_conntrack_lock to assure that helper exists until
* try_module_get() is called */
read_lock_bh(&ip_conntrack_lock);
helper = __ip_conntrack_helper_find(tuple);
if (helper) {
/* need to increase module usage count to assure helper will
* not go away while the caller is e.g. busy putting a
* conntrack in the hash that uses the helper */
if (!try_module_get(helper->me))
helper = NULL;
}
read_unlock_bh(&ip_conntrack_lock);
return helper;
}
void ip_conntrack_helper_put(struct ip_conntrack_helper *helper)
{
module_put(helper->me);
}
struct ip_conntrack_protocol *
__ip_conntrack_proto_find(u_int8_t protocol)
{
return ip_ct_protos[protocol];
}
/* this is guaranteed to always return a valid protocol helper, since
* it falls back to generic_protocol */
struct ip_conntrack_protocol *
ip_conntrack_proto_find_get(u_int8_t protocol)
{
struct ip_conntrack_protocol *p;
preempt_disable();
p = __ip_conntrack_proto_find(protocol);
if (p) {
if (!try_module_get(p->me))
p = &ip_conntrack_generic_protocol;
}
preempt_enable();
return p;
}
void ip_conntrack_proto_put(struct ip_conntrack_protocol *p)
{
module_put(p->me);
}
struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *orig,
struct ip_conntrack_tuple *repl)
{ {
struct ip_conntrack *conntrack; struct ip_conntrack *conntrack;
struct ip_conntrack_tuple repl_tuple;
size_t hash;
struct ip_conntrack_expect *exp;
if (!ip_conntrack_hash_rnd_initted) { if (!ip_conntrack_hash_rnd_initted) {
get_random_bytes(&ip_conntrack_hash_rnd, 4); get_random_bytes(&ip_conntrack_hash_rnd, 4);
ip_conntrack_hash_rnd_initted = 1; ip_conntrack_hash_rnd_initted = 1;
} }
hash = hash_conntrack(tuple);
if (ip_conntrack_max if (ip_conntrack_max
&& atomic_read(&ip_conntrack_count) >= ip_conntrack_max) { && atomic_read(&ip_conntrack_count) >= ip_conntrack_max) {
unsigned int hash = hash_conntrack(orig);
/* Try dropping from this hash chain. */ /* Try dropping from this hash chain. */
if (!early_drop(&ip_conntrack_hash[hash])) { if (!early_drop(&ip_conntrack_hash[hash])) {
if (net_ratelimit()) if (net_ratelimit())
@ -565,31 +662,58 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
} }
} }
if (!ip_ct_invert_tuple(&repl_tuple, tuple, protocol)) {
DEBUGP("Can't invert tuple.\n");
return NULL;
}
conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC); conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC);
if (!conntrack) { if (!conntrack) {
DEBUGP("Can't allocate conntrack.\n"); DEBUGP("Can't allocate conntrack.\n");
return ERR_PTR(-ENOMEM); return NULL;
} }
memset(conntrack, 0, sizeof(*conntrack)); memset(conntrack, 0, sizeof(*conntrack));
atomic_set(&conntrack->ct_general.use, 1); atomic_set(&conntrack->ct_general.use, 1);
conntrack->ct_general.destroy = destroy_conntrack; conntrack->ct_general.destroy = destroy_conntrack;
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple; conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple; conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;
if (!protocol->new(conntrack, skb)) {
kmem_cache_free(ip_conntrack_cachep, conntrack);
return NULL;
}
/* Don't set timer yet: wait for confirmation */ /* Don't set timer yet: wait for confirmation */
init_timer(&conntrack->timeout); init_timer(&conntrack->timeout);
conntrack->timeout.data = (unsigned long)conntrack; conntrack->timeout.data = (unsigned long)conntrack;
conntrack->timeout.function = death_by_timeout; conntrack->timeout.function = death_by_timeout;
atomic_inc(&ip_conntrack_count);
return conntrack;
}
void
ip_conntrack_free(struct ip_conntrack *conntrack)
{
atomic_dec(&ip_conntrack_count);
kmem_cache_free(ip_conntrack_cachep, conntrack);
}
/* Allocate a new conntrack: we return -ENOMEM if classification
* failed due to stress. Otherwise it really is unclassifiable */
static struct ip_conntrack_tuple_hash *
init_conntrack(struct ip_conntrack_tuple *tuple,
struct ip_conntrack_protocol *protocol,
struct sk_buff *skb)
{
struct ip_conntrack *conntrack;
struct ip_conntrack_tuple repl_tuple;
struct ip_conntrack_expect *exp;
if (!ip_ct_invert_tuple(&repl_tuple, tuple, protocol)) {
DEBUGP("Can't invert tuple.\n");
return NULL;
}
if (!(conntrack = ip_conntrack_alloc(tuple, &repl_tuple)))
return NULL;
if (!protocol->new(conntrack, skb)) {
ip_conntrack_free(conntrack);
return NULL;
}
write_lock_bh(&ip_conntrack_lock); write_lock_bh(&ip_conntrack_lock);
exp = find_expectation(tuple); exp = find_expectation(tuple);
@ -610,7 +734,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
nf_conntrack_get(&conntrack->master->ct_general); nf_conntrack_get(&conntrack->master->ct_general);
CONNTRACK_STAT_INC(expect_new); CONNTRACK_STAT_INC(expect_new);
} else { } else {
conntrack->helper = ip_ct_find_helper(&repl_tuple); conntrack->helper = __ip_conntrack_helper_find(&repl_tuple);
CONNTRACK_STAT_INC(new); CONNTRACK_STAT_INC(new);
} }
@ -618,7 +742,6 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
/* Overload tuple linked list to put us in unconfirmed list. */ /* Overload tuple linked list to put us in unconfirmed list. */
list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed); list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed);
atomic_inc(&ip_conntrack_count);
write_unlock_bh(&ip_conntrack_lock); write_unlock_bh(&ip_conntrack_lock);
if (exp) { if (exp) {
@ -729,7 +852,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
} }
#endif #endif
proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); proto = __ip_conntrack_proto_find((*pskb)->nh.iph->protocol);
/* It may be an special packet, error, unclean... /* It may be an special packet, error, unclean...
* inverse of the return code tells to the netfilter * inverse of the return code tells to the netfilter
@ -777,7 +900,7 @@ int invert_tuplepr(struct ip_conntrack_tuple *inverse,
const struct ip_conntrack_tuple *orig) const struct ip_conntrack_tuple *orig)
{ {
return ip_ct_invert_tuple(inverse, orig, return ip_ct_invert_tuple(inverse, orig,
ip_ct_find_proto(orig->dst.protonum)); __ip_conntrack_proto_find(orig->dst.protonum));
} }
/* Would two expected things clash? */ /* Would two expected things clash? */
@ -857,6 +980,8 @@ static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp)
exp->timeout.expires = jiffies + exp->master->helper->timeout * HZ; exp->timeout.expires = jiffies + exp->master->helper->timeout * HZ;
add_timer(&exp->timeout); add_timer(&exp->timeout);
exp->id = ++ip_conntrack_expect_next_id;
atomic_inc(&exp->use);
CONNTRACK_STAT_INC(expect_create); CONNTRACK_STAT_INC(expect_create);
} }
@ -936,7 +1061,7 @@ void ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
if (!conntrack->master && conntrack->expecting == 0) if (!conntrack->master && conntrack->expecting == 0)
conntrack->helper = ip_ct_find_helper(newreply); conntrack->helper = __ip_conntrack_helper_find(newreply);
write_unlock_bh(&ip_conntrack_lock); write_unlock_bh(&ip_conntrack_lock);
} }
@ -950,6 +1075,19 @@ int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
return 0; return 0;
} }
struct ip_conntrack_helper *
__ip_conntrack_helper_find_byname(const char *name)
{
struct ip_conntrack_helper *h;
list_for_each_entry(h, &helpers, list) {
if (!strcmp(h->name, name))
return h;
}
return NULL;
}
static inline int unhelp(struct ip_conntrack_tuple_hash *i, static inline int unhelp(struct ip_conntrack_tuple_hash *i,
const struct ip_conntrack_helper *me) const struct ip_conntrack_helper *me)
{ {
@ -1025,6 +1163,39 @@ void ip_ct_refresh_acct(struct ip_conntrack *ct,
} }
} }
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
/* Generic function for tcp/udp/sctp/dccp and alike. This needs to be
* in ip_conntrack_core, since we don't want the protocols to autoload
* or depend on ctnetlink */
int ip_ct_port_tuple_to_nfattr(struct sk_buff *skb,
const struct ip_conntrack_tuple *tuple)
{
NFA_PUT(skb, CTA_PROTO_SRC_PORT, sizeof(u_int16_t),
&tuple->src.u.tcp.port);
NFA_PUT(skb, CTA_PROTO_DST_PORT, sizeof(u_int16_t),
&tuple->dst.u.tcp.port);
return 0;
nfattr_failure:
return -1;
}
int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[],
struct ip_conntrack_tuple *t)
{
if (!tb[CTA_PROTO_SRC_PORT-1] || !tb[CTA_PROTO_DST_PORT-1])
return -EINVAL;
t->src.u.tcp.port =
*(u_int16_t *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]);
t->dst.u.tcp.port =
*(u_int16_t *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]);
return 0;
}
#endif
/* Returns new sk_buff, or NULL */ /* Returns new sk_buff, or NULL */
struct sk_buff * struct sk_buff *
ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user) ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user)
@ -1203,16 +1374,13 @@ static void free_conntrack_hash(void)
* ip_conntrack_htable_size)); * ip_conntrack_htable_size));
} }
/* Mishearing the voices in his head, our hero wonders how he's void ip_conntrack_flush()
supposed to kill the mall. */
void ip_conntrack_cleanup(void)
{ {
ip_ct_attach = NULL;
/* This makes sure all current packets have passed through /* This makes sure all current packets have passed through
netfilter framework. Roll on, two-stage module netfilter framework. Roll on, two-stage module
delete... */ delete... */
synchronize_net(); synchronize_net();
i_see_dead_people: i_see_dead_people:
ip_ct_iterate_cleanup(kill_all, NULL); ip_ct_iterate_cleanup(kill_all, NULL);
if (atomic_read(&ip_conntrack_count) != 0) { if (atomic_read(&ip_conntrack_count) != 0) {
@ -1222,7 +1390,14 @@ void ip_conntrack_cleanup(void)
/* wait until all references to ip_conntrack_untracked are dropped */ /* wait until all references to ip_conntrack_untracked are dropped */
while (atomic_read(&ip_conntrack_untracked.ct_general.use) > 1) while (atomic_read(&ip_conntrack_untracked.ct_general.use) > 1)
schedule(); schedule();
}
/* Mishearing the voices in his head, our hero wonders how he's
supposed to kill the mall. */
void ip_conntrack_cleanup(void)
{
ip_ct_attach = NULL;
ip_conntrack_flush();
kmem_cache_destroy(ip_conntrack_cachep); kmem_cache_destroy(ip_conntrack_cachep);
kmem_cache_destroy(ip_conntrack_expect_cachep); kmem_cache_destroy(ip_conntrack_expect_cachep);
free_conntrack_hash(); free_conntrack_hash();

File diff suppressed because it is too large Load Diff

View File

@ -109,16 +109,17 @@ static int icmp_packet(struct ip_conntrack *ct,
return NF_ACCEPT; return NF_ACCEPT;
} }
static u_int8_t valid_new[] = {
[ICMP_ECHO] = 1,
[ICMP_TIMESTAMP] = 1,
[ICMP_INFO_REQUEST] = 1,
[ICMP_ADDRESS] = 1
};
/* Called when a new connection for this protocol found. */ /* Called when a new connection for this protocol found. */
static int icmp_new(struct ip_conntrack *conntrack, static int icmp_new(struct ip_conntrack *conntrack,
const struct sk_buff *skb) const struct sk_buff *skb)
{ {
static u_int8_t valid_new[]
= { [ICMP_ECHO] = 1,
[ICMP_TIMESTAMP] = 1,
[ICMP_INFO_REQUEST] = 1,
[ICMP_ADDRESS] = 1 };
if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
|| !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) { || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
/* Can't create a new ICMP `conn' with this. */ /* Can't create a new ICMP `conn' with this. */
@ -159,11 +160,12 @@ icmp_error_message(struct sk_buff *skb,
return NF_ACCEPT; return NF_ACCEPT;
} }
innerproto = ip_ct_find_proto(inside->ip.protocol); innerproto = ip_conntrack_proto_find_get(inside->ip.protocol);
dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp) + inside->ip.ihl*4; dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp) + inside->ip.ihl*4;
/* Are they talking about one of our connections? */ /* Are they talking about one of our connections? */
if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto)) { if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto)) {
DEBUGP("icmp_error: ! get_tuple p=%u", inside->ip.protocol); DEBUGP("icmp_error: ! get_tuple p=%u", inside->ip.protocol);
ip_conntrack_proto_put(innerproto);
return NF_ACCEPT; return NF_ACCEPT;
} }
@ -171,8 +173,10 @@ icmp_error_message(struct sk_buff *skb,
been preserved inside the ICMP. */ been preserved inside the ICMP. */
if (!ip_ct_invert_tuple(&innertuple, &origtuple, innerproto)) { if (!ip_ct_invert_tuple(&innertuple, &origtuple, innerproto)) {
DEBUGP("icmp_error_track: Can't invert tuple\n"); DEBUGP("icmp_error_track: Can't invert tuple\n");
ip_conntrack_proto_put(innerproto);
return NF_ACCEPT; return NF_ACCEPT;
} }
ip_conntrack_proto_put(innerproto);
*ctinfo = IP_CT_RELATED; *ctinfo = IP_CT_RELATED;
@ -266,6 +270,47 @@ checksum_skipped:
return icmp_error_message(skb, ctinfo, hooknum); return icmp_error_message(skb, ctinfo, hooknum);
} }
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
static int icmp_tuple_to_nfattr(struct sk_buff *skb,
const struct ip_conntrack_tuple *t)
{
NFA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(u_int16_t),
&t->src.u.icmp.id);
NFA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t),
&t->dst.u.icmp.type);
NFA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t),
&t->dst.u.icmp.code);
if (t->dst.u.icmp.type >= sizeof(valid_new)
|| !valid_new[t->dst.u.icmp.type])
return -EINVAL;
return 0;
nfattr_failure:
return -1;
}
static int icmp_nfattr_to_tuple(struct nfattr *tb[],
struct ip_conntrack_tuple *tuple)
{
if (!tb[CTA_PROTO_ICMP_TYPE-1]
|| !tb[CTA_PROTO_ICMP_CODE-1]
|| !tb[CTA_PROTO_ICMP_ID-1])
return -1;
tuple->dst.u.icmp.type =
*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
tuple->dst.u.icmp.code =
*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
tuple->src.u.icmp.id =
*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);
return 0;
}
#endif
struct ip_conntrack_protocol ip_conntrack_protocol_icmp = struct ip_conntrack_protocol ip_conntrack_protocol_icmp =
{ {
.proto = IPPROTO_ICMP, .proto = IPPROTO_ICMP,
@ -277,4 +322,9 @@ struct ip_conntrack_protocol ip_conntrack_protocol_icmp =
.packet = icmp_packet, .packet = icmp_packet,
.new = icmp_new, .new = icmp_new,
.error = icmp_error, .error = icmp_error,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
.tuple_to_nfattr = icmp_tuple_to_nfattr,
.nfattr_to_tuple = icmp_nfattr_to_tuple,
#endif
}; };

View File

@ -505,7 +505,12 @@ static struct ip_conntrack_protocol ip_conntrack_protocol_sctp = {
.packet = sctp_packet, .packet = sctp_packet,
.new = sctp_new, .new = sctp_new,
.destroy = NULL, .destroy = NULL,
.me = THIS_MODULE .me = THIS_MODULE,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
.tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
.nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,
#endif
}; };
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL

View File

@ -336,6 +336,23 @@ static int tcp_print_conntrack(struct seq_file *s,
return seq_printf(s, "%s ", tcp_conntrack_names[state]); return seq_printf(s, "%s ", tcp_conntrack_names[state]);
} }
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa,
const struct ip_conntrack *ct)
{
read_lock_bh(&tcp_lock);
NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
&ct->proto.tcp.state);
read_unlock_bh(&tcp_lock);
return 0;
nfattr_failure:
return -1;
}
#endif
static unsigned int get_conntrack_index(const struct tcphdr *tcph) static unsigned int get_conntrack_index(const struct tcphdr *tcph)
{ {
if (tcph->rst) return TCP_RST_SET; if (tcph->rst) return TCP_RST_SET;
@ -1100,4 +1117,10 @@ struct ip_conntrack_protocol ip_conntrack_protocol_tcp =
.packet = tcp_packet, .packet = tcp_packet,
.new = tcp_new, .new = tcp_new,
.error = tcp_error, .error = tcp_error,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
.to_nfattr = tcp_to_nfattr,
.tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
.nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,
#endif
}; };

View File

@ -145,4 +145,9 @@ struct ip_conntrack_protocol ip_conntrack_protocol_udp =
.packet = udp_packet, .packet = udp_packet,
.new = udp_new, .new = udp_new,
.error = udp_error, .error = udp_error,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
.tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
.nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,
#endif
}; };

View File

@ -5,7 +5,7 @@
*/ */
/* (C) 1999-2001 Paul `Rusty' Russell /* (C) 1999-2001 Paul `Rusty' Russell
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> * (C) 2002-2005 Netfilter Core Team <coreteam@netfilter.org>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
@ -147,8 +147,7 @@ static int ct_seq_show(struct seq_file *s, void *v)
if (DIRECTION(hash)) if (DIRECTION(hash))
return 0; return 0;
proto = ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] proto = __ip_conntrack_proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
.tuple.dst.protonum);
IP_NF_ASSERT(proto); IP_NF_ASSERT(proto);
if (seq_printf(s, "%-8s %u %ld ", if (seq_printf(s, "%-8s %u %ld ",
@ -283,7 +282,7 @@ static int exp_seq_show(struct seq_file *s, void *v)
seq_printf(s, "proto=%u ", expect->tuple.dst.protonum); seq_printf(s, "proto=%u ", expect->tuple.dst.protonum);
print_tuple(s, &expect->tuple, print_tuple(s, &expect->tuple,
ip_ct_find_proto(expect->tuple.dst.protonum)); __ip_conntrack_proto_find(expect->tuple.dst.protonum));
return seq_putc(s, '\n'); return seq_putc(s, '\n');
} }
@ -992,12 +991,16 @@ EXPORT_SYMBOL(ip_conntrack_helper_register);
EXPORT_SYMBOL(ip_conntrack_helper_unregister); EXPORT_SYMBOL(ip_conntrack_helper_unregister);
EXPORT_SYMBOL(ip_ct_iterate_cleanup); EXPORT_SYMBOL(ip_ct_iterate_cleanup);
EXPORT_SYMBOL(ip_ct_refresh_acct); EXPORT_SYMBOL(ip_ct_refresh_acct);
EXPORT_SYMBOL(ip_ct_protos);
EXPORT_SYMBOL(ip_ct_find_proto);
EXPORT_SYMBOL(ip_conntrack_expect_alloc); EXPORT_SYMBOL(ip_conntrack_expect_alloc);
EXPORT_SYMBOL(ip_conntrack_expect_put); EXPORT_SYMBOL(ip_conntrack_expect_put);
EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get);
EXPORT_SYMBOL(ip_conntrack_expect_related); EXPORT_SYMBOL(ip_conntrack_expect_related);
EXPORT_SYMBOL(ip_conntrack_unexpect_related); EXPORT_SYMBOL(ip_conntrack_unexpect_related);
EXPORT_SYMBOL_GPL(ip_conntrack_expect_list);
EXPORT_SYMBOL_GPL(__ip_conntrack_expect_find);
EXPORT_SYMBOL_GPL(__ip_ct_expect_unlink_destroy);
EXPORT_SYMBOL(ip_conntrack_tuple_taken); EXPORT_SYMBOL(ip_conntrack_tuple_taken);
EXPORT_SYMBOL(ip_ct_gather_frags); EXPORT_SYMBOL(ip_ct_gather_frags);
EXPORT_SYMBOL(ip_conntrack_htable_size); EXPORT_SYMBOL(ip_conntrack_htable_size);
@ -1005,7 +1008,28 @@ EXPORT_SYMBOL(ip_conntrack_lock);
EXPORT_SYMBOL(ip_conntrack_hash); EXPORT_SYMBOL(ip_conntrack_hash);
EXPORT_SYMBOL(ip_conntrack_untracked); EXPORT_SYMBOL(ip_conntrack_untracked);
EXPORT_SYMBOL_GPL(ip_conntrack_find_get); EXPORT_SYMBOL_GPL(ip_conntrack_find_get);
EXPORT_SYMBOL_GPL(ip_conntrack_put);
#ifdef CONFIG_IP_NF_NAT_NEEDED #ifdef CONFIG_IP_NF_NAT_NEEDED
EXPORT_SYMBOL(ip_conntrack_tcp_update); EXPORT_SYMBOL(ip_conntrack_tcp_update);
#endif #endif
EXPORT_SYMBOL_GPL(ip_conntrack_flush);
EXPORT_SYMBOL_GPL(__ip_conntrack_find);
EXPORT_SYMBOL_GPL(ip_conntrack_alloc);
EXPORT_SYMBOL_GPL(ip_conntrack_free);
EXPORT_SYMBOL_GPL(ip_conntrack_hash_insert);
EXPORT_SYMBOL_GPL(ip_ct_remove_expectations);
EXPORT_SYMBOL_GPL(ip_conntrack_helper_find_get);
EXPORT_SYMBOL_GPL(ip_conntrack_helper_put);
EXPORT_SYMBOL_GPL(__ip_conntrack_helper_find_byname);
EXPORT_SYMBOL_GPL(ip_conntrack_proto_find_get);
EXPORT_SYMBOL_GPL(ip_conntrack_proto_put);
EXPORT_SYMBOL_GPL(__ip_conntrack_proto_find);
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
EXPORT_SYMBOL_GPL(ip_ct_port_tuple_to_nfattr);
EXPORT_SYMBOL_GPL(ip_ct_port_nfattr_to_tuple);
#endif

View File

@ -47,8 +47,39 @@ DEFINE_RWLOCK(ip_nat_lock);
static unsigned int ip_nat_htable_size; static unsigned int ip_nat_htable_size;
static struct list_head *bysource; static struct list_head *bysource;
#define MAX_IP_NAT_PROTO 256
struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO]; struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO];
static inline struct ip_nat_protocol *
__ip_nat_proto_find(u_int8_t protonum)
{
return ip_nat_protos[protonum];
}
struct ip_nat_protocol *
ip_nat_proto_find_get(u_int8_t protonum)
{
struct ip_nat_protocol *p;
/* we need to disable preemption to make sure 'p' doesn't get
* removed until we've grabbed the reference */
preempt_disable();
p = __ip_nat_proto_find(protonum);
if (p) {
if (!try_module_get(p->me))
p = &ip_nat_unknown_protocol;
}
preempt_enable();
return p;
}
void
ip_nat_proto_put(struct ip_nat_protocol *p)
{
module_put(p->me);
}
/* We keep an extra hash for each conntrack, for fast searching. */ /* We keep an extra hash for each conntrack, for fast searching. */
static inline unsigned int static inline unsigned int
@ -103,7 +134,8 @@ static int
in_range(const struct ip_conntrack_tuple *tuple, in_range(const struct ip_conntrack_tuple *tuple,
const struct ip_nat_range *range) const struct ip_nat_range *range)
{ {
struct ip_nat_protocol *proto = ip_nat_find_proto(tuple->dst.protonum); struct ip_nat_protocol *proto =
__ip_nat_proto_find(tuple->dst.protonum);
/* If we are supposed to map IPs, then we must be in the /* If we are supposed to map IPs, then we must be in the
range specified, otherwise let this drag us onto a new src IP. */ range specified, otherwise let this drag us onto a new src IP. */
@ -216,8 +248,7 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple,
struct ip_conntrack *conntrack, struct ip_conntrack *conntrack,
enum ip_nat_manip_type maniptype) enum ip_nat_manip_type maniptype)
{ {
struct ip_nat_protocol *proto struct ip_nat_protocol *proto;
= ip_nat_find_proto(orig_tuple->dst.protonum);
/* 1) If this srcip/proto/src-proto-part is currently mapped, /* 1) If this srcip/proto/src-proto-part is currently mapped,
and that same mapping gives a unique tuple within the given and that same mapping gives a unique tuple within the given
@ -242,14 +273,20 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple,
/* 3) The per-protocol part of the manip is made to map into /* 3) The per-protocol part of the manip is made to map into
the range to make a unique tuple. */ the range to make a unique tuple. */
proto = ip_nat_proto_find_get(orig_tuple->dst.protonum);
/* Only bother mapping if it's not already in range and unique */ /* Only bother mapping if it's not already in range and unique */
if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
|| proto->in_range(tuple, maniptype, &range->min, &range->max)) || proto->in_range(tuple, maniptype, &range->min, &range->max))
&& !ip_nat_used_tuple(tuple, conntrack)) && !ip_nat_used_tuple(tuple, conntrack)) {
ip_nat_proto_put(proto);
return; return;
}
/* Last change: get protocol to try to obtain unique tuple. */ /* Last change: get protocol to try to obtain unique tuple. */
proto->unique_tuple(tuple, range, maniptype, conntrack); proto->unique_tuple(tuple, range, maniptype, conntrack);
ip_nat_proto_put(proto);
} }
unsigned int unsigned int
@ -320,6 +357,7 @@ manip_pkt(u_int16_t proto,
enum ip_nat_manip_type maniptype) enum ip_nat_manip_type maniptype)
{ {
struct iphdr *iph; struct iphdr *iph;
struct ip_nat_protocol *p;
if (!skb_ip_make_writable(pskb, iphdroff + sizeof(*iph))) if (!skb_ip_make_writable(pskb, iphdroff + sizeof(*iph)))
return 0; return 0;
@ -327,9 +365,12 @@ manip_pkt(u_int16_t proto,
iph = (void *)(*pskb)->data + iphdroff; iph = (void *)(*pskb)->data + iphdroff;
/* Manipulate protcol part. */ /* Manipulate protcol part. */
if (!ip_nat_find_proto(proto)->manip_pkt(pskb, iphdroff, p = ip_nat_proto_find_get(proto);
target, maniptype)) if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) {
ip_nat_proto_put(p);
return 0; return 0;
}
ip_nat_proto_put(p);
iph = (void *)(*pskb)->data + iphdroff; iph = (void *)(*pskb)->data + iphdroff;
@ -425,7 +466,8 @@ int icmp_reply_translation(struct sk_buff **pskb,
if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 + if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 +
sizeof(struct icmphdr) + inside->ip.ihl*4, sizeof(struct icmphdr) + inside->ip.ihl*4,
&inner, ip_ct_find_proto(inside->ip.protocol))) &inner,
__ip_conntrack_proto_find(inside->ip.protocol)))
return 0; return 0;
/* Change inner back to look like incoming packet. We do the /* Change inner back to look like incoming packet. We do the
@ -495,6 +537,49 @@ void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
synchronize_net(); synchronize_net();
} }
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
int
ip_nat_port_range_to_nfattr(struct sk_buff *skb,
const struct ip_nat_range *range)
{
NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(u_int16_t),
&range->min.tcp.port);
NFA_PUT(skb, CTA_PROTONAT_PORT_MAX, sizeof(u_int16_t),
&range->max.tcp.port);
return 0;
nfattr_failure:
return -1;
}
int
ip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range)
{
int ret = 0;
/* we have to return whether we actually parsed something or not */
if (tb[CTA_PROTONAT_PORT_MIN-1]) {
ret = 1;
range->min.tcp.port =
*(u_int16_t *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]);
}
if (!tb[CTA_PROTONAT_PORT_MAX-1]) {
if (ret)
range->max.tcp.port = range->min.tcp.port;
} else {
ret = 1;
range->max.tcp.port =
*(u_int16_t *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]);
}
return ret;
}
#endif
int __init ip_nat_init(void) int __init ip_nat_init(void)
{ {
size_t i; size_t i;

View File

@ -107,10 +107,15 @@ icmp_print_range(char *buffer, const struct ip_nat_range *range)
} }
struct ip_nat_protocol ip_nat_protocol_icmp struct ip_nat_protocol ip_nat_protocol_icmp
= { "ICMP", IPPROTO_ICMP, = { "ICMP", IPPROTO_ICMP, THIS_MODULE,
icmp_manip_pkt, icmp_manip_pkt,
icmp_in_range, icmp_in_range,
icmp_unique_tuple, icmp_unique_tuple,
icmp_print, icmp_print,
icmp_print_range icmp_print_range,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
ip_nat_port_range_to_nfattr,
ip_nat_port_nfattr_to_range,
#endif
}; };

View File

@ -12,6 +12,7 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/netfilter/nfnetlink_conntrack.h>
#include <linux/netfilter_ipv4/ip_nat.h> #include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter_ipv4/ip_nat_rule.h> #include <linux/netfilter_ipv4/ip_nat_rule.h>
#include <linux/netfilter_ipv4/ip_nat_protocol.h> #include <linux/netfilter_ipv4/ip_nat_protocol.h>
@ -170,10 +171,15 @@ tcp_print_range(char *buffer, const struct ip_nat_range *range)
} }
struct ip_nat_protocol ip_nat_protocol_tcp struct ip_nat_protocol ip_nat_protocol_tcp
= { "TCP", IPPROTO_TCP, = { "TCP", IPPROTO_TCP, THIS_MODULE,
tcp_manip_pkt, tcp_manip_pkt,
tcp_in_range, tcp_in_range,
tcp_unique_tuple, tcp_unique_tuple,
tcp_print, tcp_print,
tcp_print_range tcp_print_range,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
ip_nat_port_range_to_nfattr,
ip_nat_port_nfattr_to_range,
#endif
}; };

View File

@ -157,10 +157,15 @@ udp_print_range(char *buffer, const struct ip_nat_range *range)
} }
struct ip_nat_protocol ip_nat_protocol_udp struct ip_nat_protocol ip_nat_protocol_udp
= { "UDP", IPPROTO_UDP, = { "UDP", IPPROTO_UDP, THIS_MODULE,
udp_manip_pkt, udp_manip_pkt,
udp_in_range, udp_in_range,
udp_unique_tuple, udp_unique_tuple,
udp_print, udp_print,
udp_print_range udp_print_range,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
ip_nat_port_range_to_nfattr,
ip_nat_port_nfattr_to_range,
#endif
}; };

View File

@ -61,7 +61,7 @@ unknown_print_range(char *buffer, const struct ip_nat_range *range)
} }
struct ip_nat_protocol ip_nat_unknown_protocol = { struct ip_nat_protocol ip_nat_unknown_protocol = {
"unknown", 0, "unknown", 0, THIS_MODULE,
unknown_manip_pkt, unknown_manip_pkt,
unknown_in_range, unknown_in_range,
unknown_unique_tuple, unknown_unique_tuple,

View File

@ -394,6 +394,8 @@ module_exit(fini);
EXPORT_SYMBOL(ip_nat_setup_info); EXPORT_SYMBOL(ip_nat_setup_info);
EXPORT_SYMBOL(ip_nat_protocol_register); EXPORT_SYMBOL(ip_nat_protocol_register);
EXPORT_SYMBOL(ip_nat_protocol_unregister); EXPORT_SYMBOL(ip_nat_protocol_unregister);
EXPORT_SYMBOL_GPL(ip_nat_proto_find_get);
EXPORT_SYMBOL_GPL(ip_nat_proto_put);
EXPORT_SYMBOL(ip_nat_cheat_check); EXPORT_SYMBOL(ip_nat_cheat_check);
EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
EXPORT_SYMBOL(ip_nat_mangle_udp_packet); EXPORT_SYMBOL(ip_nat_mangle_udp_packet);

View File

@ -121,6 +121,7 @@ void __nfa_fill(struct sk_buff *skb, int attrtype, int attrlen,
nfa->nfa_type = attrtype; nfa->nfa_type = attrtype;
nfa->nfa_len = size; nfa->nfa_len = size;
memcpy(NFA_DATA(nfa), data, attrlen); memcpy(NFA_DATA(nfa), data, attrlen);
memset(NFA_DATA(nfa) + attrlen, 0, NFA_ALIGN(size) - size);
} }
int nfattr_parse(struct nfattr *tb[], int maxattr, struct nfattr *nfa, int len) int nfattr_parse(struct nfattr *tb[], int maxattr, struct nfattr *nfa, int len)