Commit 852aaba8 authored by Harald Welte's avatar Harald Welte Committed by David S. Miller

Big netfilter newnat patch for 2.5.7:

- support for multiple expected connections
  (necessary for protocols like H.323, SIP, PPTP)
- helper-definable limit of unconfirmed expectations
- timeouts for expectations
- full graph of connection relations, even after expectation confirmed
- various changes in the API towards conntrack and NAT helper
- automatic conntrack helper loading when at helper is loaded
- NAT mangling of TCP SACK in case of sequence number alteration
  (no need to delete SACKPERM anymore, I hope Alexey is happy now)
parent b974e3f3
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/netfilter_ipv4/ip_conntrack_tuple.h> #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
#include <asm/atomic.h>
enum ip_conntrack_info enum ip_conntrack_info
{ {
...@@ -62,27 +63,58 @@ do { \ ...@@ -62,27 +63,58 @@ do { \
#define IP_NF_ASSERT(x) #define IP_NF_ASSERT(x)
#endif #endif
#ifdef CONFIG_IP_NF_NAT_NEEDED
#include <linux/netfilter_ipv4/ip_nat.h>
#endif
/* Add protocol helper include file here */
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
struct ip_conntrack_expect struct ip_conntrack_expect
{ {
/* Internal linked list */ /* Internal linked list (global expectation list) */
struct list_head list; struct list_head list;
/* expectation list for this master */
struct list_head expected_list;
/* The conntrack of the master connection */
struct ip_conntrack *expectant;
/* The conntrack of the sibling connection, set after
* expectation arrived */
struct ip_conntrack *sibling;
/* Tuple saved for conntrack */
struct ip_conntrack_tuple ct_tuple;
/* Timer function; deletes the expectation. */
struct timer_list timeout;
/* Data filled out by the conntrack helpers follow: */
/* We expect this tuple, with the following mask */ /* We expect this tuple, with the following mask */
struct ip_conntrack_tuple tuple, mask; struct ip_conntrack_tuple tuple, mask;
/* Function to call after setup and insertion */ /* Function to call after setup and insertion */
int (*expectfn)(struct ip_conntrack *new); int (*expectfn)(struct ip_conntrack *new);
/* The conntrack we are part of (set iff we're live) */ /* At which sequence number did this expectation occur */
struct ip_conntrack *expectant; u_int32_t seq;
};
union {
/* insert conntrack helper private data (expect) here */
struct ip_ct_ftp_expect exp_ftp_info;
struct ip_ct_irc_expect exp_irc_info;
#ifdef CONFIG_IP_NF_NAT_NEEDED #ifdef CONFIG_IP_NF_NAT_NEEDED
#include <linux/netfilter_ipv4/ip_nat.h> union {
/* insert nat helper private data (expect) here */
} nat;
#endif #endif
} help;
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h> };
#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
struct ip_conntrack struct ip_conntrack
{ {
...@@ -101,10 +133,13 @@ struct ip_conntrack ...@@ -101,10 +133,13 @@ struct ip_conntrack
/* If we're expecting another related connection, this will be /* If we're expecting another related connection, this will be
in expected linked list */ in expected linked list */
struct ip_conntrack_expect expected; struct list_head sibling_list;
/* Current number of expected connections */
unsigned int expecting;
/* If we were expected by another connection, this will be it */ /* If we were expected by an expectation, this will be it */
struct nf_ct_info master; struct ip_conntrack_expect *master;
/* Helper, if any. */ /* Helper, if any. */
struct ip_conntrack_helper *helper; struct ip_conntrack_helper *helper;
...@@ -121,8 +156,9 @@ struct ip_conntrack ...@@ -121,8 +156,9 @@ struct ip_conntrack
} proto; } proto;
union { union {
struct ip_ct_ftp ct_ftp_info; /* insert conntrack helper private data (master) here */
struct ip_ct_irc ct_irc_info; struct ip_ct_ftp_master ct_ftp_info;
struct ip_ct_irc_master ct_irc_info;
} help; } help;
#ifdef CONFIG_IP_NF_NAT_NEEDED #ifdef CONFIG_IP_NF_NAT_NEEDED
...@@ -140,6 +176,9 @@ struct ip_conntrack ...@@ -140,6 +176,9 @@ struct ip_conntrack
}; };
/* get master conntrack via master expectation */
#define master_ct(conntr) (conntr->master ? conntr->master->expectant : NULL)
/* Alter reply tuple (maybe alter helper). If it's already taken, /* Alter reply tuple (maybe alter helper). If it's already taken,
return 0 and don't do alteration. */ return 0 and don't do alteration. */
extern int extern int
......
...@@ -15,7 +15,7 @@ extern int ip_conntrack_init(void); ...@@ -15,7 +15,7 @@ extern int ip_conntrack_init(void);
extern void ip_conntrack_cleanup(void); extern void ip_conntrack_cleanup(void);
struct ip_conntrack_protocol; struct ip_conntrack_protocol;
extern struct ip_conntrack_protocol *find_proto(u_int8_t protocol); extern struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol);
/* Like above, but you already have conntrack read lock. */ /* Like above, but you already have conntrack read lock. */
extern struct ip_conntrack_protocol *__find_proto(u_int8_t protocol); extern struct ip_conntrack_protocol *__find_proto(u_int8_t protocol);
extern struct list_head protocol_list; extern struct list_head protocol_list;
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
/* Protects ftp part of conntracks */ /* Protects ftp part of conntracks */
DECLARE_LOCK_EXTERN(ip_ftp_lock); DECLARE_LOCK_EXTERN(ip_ftp_lock);
#define FTP_PORT 21
enum ip_ct_ftp_type enum ip_ct_ftp_type
{ {
/* PORT command from client */ /* PORT command from client */
...@@ -23,18 +25,20 @@ enum ip_ct_ftp_type ...@@ -23,18 +25,20 @@ enum ip_ct_ftp_type
IP_CT_FTP_EPSV, IP_CT_FTP_EPSV,
}; };
/* We record seq number and length of ftp ip/port text here: all in /* This structure is per expected connection */
host order. */ struct ip_ct_ftp_expect
struct ip_ct_ftp
{ {
/* This tells NAT that this is an ftp connection */ /* We record seq number and length of ftp ip/port text here: all in
int is_ftp; * host order. */
u_int32_t seq;
/* 0 means not found yet */ /* sequence number of IP address in packet is in ip_conntrack_expect */
u_int32_t len; u_int32_t len; /* length of IP address */
enum ip_ct_ftp_type ftptype; enum ip_ct_ftp_type ftptype; /* PORT or PASV ? */
/* Port that was to be used */ u_int16_t port; /* TCP port that was to be used */
u_int16_t port; };
/* This structure exists only once per master */
struct ip_ct_ftp_master {
/* Next valid seq position for cmd matching after newline */ /* Next valid seq position for cmd matching after newline */
u_int32_t seq_aft_nl[IP_CT_DIR_MAX]; u_int32_t seq_aft_nl[IP_CT_DIR_MAX];
/* 0 means seq_match_aft_nl not set */ /* 0 means seq_match_aft_nl not set */
......
...@@ -5,10 +5,19 @@ ...@@ -5,10 +5,19 @@
struct module; struct module;
/* Reuse expectation when max_expected reached */
#define IP_CT_HELPER_F_REUSE_EXPECT 0x01
struct ip_conntrack_helper struct ip_conntrack_helper
{ {
/* Internal use. */ struct list_head list; /* Internal use. */
struct list_head list;
const char *name; /* name of the module */
unsigned char flags; /* Flags (see above) */
struct module *me; /* pointer to self */
unsigned int max_expected; /* Maximum number of concurrent
* expected connections */
unsigned int timeout; /* timeout for expecteds */
/* Mask of things we will help (compared against server response) */ /* Mask of things we will help (compared against server response) */
struct ip_conntrack_tuple tuple; struct ip_conntrack_tuple tuple;
...@@ -24,11 +33,13 @@ struct ip_conntrack_helper ...@@ -24,11 +33,13 @@ struct ip_conntrack_helper
extern int ip_conntrack_helper_register(struct ip_conntrack_helper *); extern int ip_conntrack_helper_register(struct ip_conntrack_helper *);
extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *); extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *);
/* Add an expected connection: can only have one per connection */ extern struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple);
/* Add an expected connection: can have more than one per connection */
extern int ip_conntrack_expect_related(struct ip_conntrack *related_to, extern int ip_conntrack_expect_related(struct ip_conntrack *related_to,
const struct ip_conntrack_tuple *tuple, struct ip_conntrack_expect *exp);
const struct ip_conntrack_tuple *mask, extern int ip_conntrack_change_expect(struct ip_conntrack_expect *expect,
int (*expectfn)(struct ip_conntrack *)); struct ip_conntrack_tuple *newtuple);
extern void ip_conntrack_unexpect_related(struct ip_conntrack *related_to); extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp);
#endif /*_IP_CONNTRACK_HELPER_H*/ #endif /*_IP_CONNTRACK_HELPER_H*/
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <linux/netfilter_ipv4/lockhelp.h> #include <linux/netfilter_ipv4/lockhelp.h>
#define IP_CONNTR_IRC 2 #define IRC_PORT 6667
struct dccproto { struct dccproto {
char* match; char* match;
...@@ -32,16 +32,18 @@ DECLARE_LOCK_EXTERN(ip_irc_lock); ...@@ -32,16 +32,18 @@ DECLARE_LOCK_EXTERN(ip_irc_lock);
/* We record seq number and length of irc ip/port text here: all in /* We record seq number and length of irc ip/port text here: all in
host order. */ host order. */
struct ip_ct_irc
/* This structure is per expected connection */
struct ip_ct_irc_expect
{ {
/* This tells NAT that this is an IRC connection */ /* length of IP address */
int is_irc;
/* sequence number where address part of DCC command begins */
u_int32_t seq;
/* 0 means not found yet */
u_int32_t len; u_int32_t len;
/* Port that was to be used */ /* Port that was to be used */
u_int16_t port; u_int16_t port;
}; };
/* This structure exists only once per master */
struct ip_ct_irc_master {
};
#endif /* _IP_CONNTRACK_IRC_H */ #endif /* _IP_CONNTRACK_IRC_H */
...@@ -45,6 +45,10 @@ struct ip_conntrack_protocol ...@@ -45,6 +45,10 @@ struct ip_conntrack_protocol
/* Called when a conntrack entry is destroyed */ /* Called when a conntrack entry is destroyed */
void (*destroy)(struct ip_conntrack *conntrack); void (*destroy)(struct ip_conntrack *conntrack);
/* Has to decide if a expectation matches one packet or not */
int (*exp_matches_pkt)(struct ip_conntrack_expect *exp,
struct sk_buff **pskb);
/* Module (if any) which this is connected to. */ /* Module (if any) which this is connected to. */
struct module *me; struct module *me;
}; };
......
...@@ -6,23 +6,37 @@ ...@@ -6,23 +6,37 @@
struct sk_buff; struct sk_buff;
/* Flags */
/* NAT helper must be called on every packet (for TCP) */
#define IP_NAT_HELPER_F_ALWAYS 0x01
/* Standalone NAT helper, without a conntrack part */
#define IP_NAT_HELPER_F_STANDALONE 0x02
struct ip_nat_helper struct ip_nat_helper
{ {
/* Internal use */ struct list_head list; /* Internal use */
struct list_head list;
const char *name; /* name of the module */
unsigned char flags; /* Flags (see above) */
struct module *me; /* pointer to self */
/* Mask of things we will help: vs. tuple from server */ /* Mask of things we will help: vs. tuple from server */
struct ip_conntrack_tuple tuple; struct ip_conntrack_tuple tuple;
struct ip_conntrack_tuple mask; struct ip_conntrack_tuple mask;
/* Helper function: returns verdict */ /* Helper function: returns verdict */
unsigned int (*help)(struct ip_conntrack *ct, unsigned int (*help)(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info, struct ip_nat_info *info,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int hooknum, unsigned int hooknum,
struct sk_buff **pskb); struct sk_buff **pskb);
const char *name; /* Returns verdict and sets up NAT for this connection */
unsigned int (*expect)(struct sk_buff **pskb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info);
}; };
extern struct list_head helpers; extern struct list_head helpers;
...@@ -39,5 +53,5 @@ extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb, ...@@ -39,5 +53,5 @@ extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb,
extern int ip_nat_seq_adjust(struct sk_buff *skb, extern int ip_nat_seq_adjust(struct sk_buff *skb,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo); enum ip_conntrack_info ctinfo);
extern void ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph); extern void ip_nat_delete_sack(struct sk_buff *skb);
#endif #endif
...@@ -5,24 +5,7 @@ ...@@ -5,24 +5,7 @@
#include <linux/netfilter_ipv4/ip_nat.h> #include <linux/netfilter_ipv4/ip_nat.h>
#ifdef __KERNEL__ #ifdef __KERNEL__
/* Want to be told when we first NAT an expected packet for a conntrack? */
struct ip_nat_expect
{
struct list_head list;
/* Returns 1 (and sets verdict) if it has setup NAT for this
connection */
int (*expect)(struct sk_buff **pskb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info,
struct ip_conntrack *master,
struct ip_nat_info *masterinfo,
unsigned int *verdict);
};
extern int ip_nat_expect_register(struct ip_nat_expect *expect);
extern void ip_nat_expect_unregister(struct ip_nat_expect *expect);
extern int ip_nat_rule_init(void) __init; extern int ip_nat_rule_init(void) __init;
extern void ip_nat_rule_cleanup(void); extern void ip_nat_rule_cleanup(void);
extern int ip_nat_rule_find(struct sk_buff **pskb, extern int ip_nat_rule_find(struct sk_buff **pskb,
......
...@@ -9,18 +9,18 @@ ...@@ -9,18 +9,18 @@
O_TARGET := netfilter.o O_TARGET := netfilter.o
export-objs = ip_conntrack_standalone.o ip_conntrack_ftp.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o arp_tables.o export-objs = ip_conntrack_standalone.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o arp_tables.o
# Multipart objects. # Multipart objects.
list-multi := ip_conntrack.o iptable_nat.o ipfwadm.o ipchains.o list-multi := ip_conntrack.o iptable_nat.o ipfwadm.o ipchains.o
# objects for the conntrack and NAT core (used by standalone and backw. compat) # objects for the conntrack and NAT core (used by standalone and backw. compat)
ip_nf_conntrack-objs := ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o ip_nf_conntrack-objs := ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
ip_nf_nat-objs := ip_nat_core.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o ip_nf_nat-objs := ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
# objects for the standalone - connection tracking / NAT # objects for the standalone - connection tracking / NAT
ip_conntrack-objs := ip_conntrack_standalone.o $(ip_nf_conntrack-objs) ip_conntrack-objs := ip_conntrack_standalone.o $(ip_nf_conntrack-objs)
iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_helper.o $(ip_nf_nat-objs) iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o $(ip_nf_nat-objs)
# objects for backwards compatibility mode # objects for backwards compatibility mode
ip_nf_compat-objs := ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(ip_nf_conntrack-objs) $(ip_nf_nat-objs) ip_nf_compat-objs := ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(ip_nf_conntrack-objs) $(ip_nf_nat-objs)
...@@ -33,7 +33,14 @@ obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o ...@@ -33,7 +33,14 @@ obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
# connection tracking helpers # connection tracking helpers
obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
ifdef CONFIG_IP_NF_NAT_FTP
export-objs += ip_conntrack_ftp.o
endif
obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o
ifdef CONFIG_IP_NF_NAT_IRC
export-objs += ip_conntrack_irc.o
endif
# NAT helpers # NAT helpers
obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
......
This diff is collapsed.
/* FTP extension for IP connection tracking. */ /* FTP extension for IP connection tracking. */
#include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/ip.h> #include <linux/ip.h>
...@@ -242,8 +243,10 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -242,8 +243,10 @@ static int help(const struct iphdr *iph, size_t len,
u_int32_t array[6] = { 0 }; u_int32_t array[6] = { 0 };
int dir = CTINFO2DIR(ctinfo); int dir = CTINFO2DIR(ctinfo);
unsigned int matchlen, matchoff; unsigned int matchlen, matchoff;
struct ip_conntrack_tuple t, mask; struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info;
struct ip_ct_ftp *info = &ct->help.ct_ftp_info; struct ip_conntrack_expect expect, *exp = &expect;
struct ip_ct_ftp_expect *exp_ftp_info = &exp->help.exp_ftp_info;
unsigned int i; unsigned int i;
int found = 0; int found = 0;
...@@ -271,8 +274,8 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -271,8 +274,8 @@ static int help(const struct iphdr *iph, size_t len,
} }
LOCK_BH(&ip_ftp_lock); LOCK_BH(&ip_ftp_lock);
old_seq_aft_nl_set = info->seq_aft_nl_set[dir]; old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir];
old_seq_aft_nl = info->seq_aft_nl[dir]; old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir];
DEBUGP("conntrack_ftp: datalen %u\n", datalen); DEBUGP("conntrack_ftp: datalen %u\n", datalen);
if ((datalen > 0) && (data[datalen-1] == '\n')) { if ((datalen > 0) && (data[datalen-1] == '\n')) {
...@@ -281,8 +284,9 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -281,8 +284,9 @@ static int help(const struct iphdr *iph, size_t len,
|| after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) { || after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) {
DEBUGP("conntrack_ftp: updating nl to %u\n", DEBUGP("conntrack_ftp: updating nl to %u\n",
ntohl(tcph->seq) + datalen); ntohl(tcph->seq) + datalen);
info->seq_aft_nl[dir] = ntohl(tcph->seq) + datalen; ct_ftp_info->seq_aft_nl[dir] =
info->seq_aft_nl_set[dir] = 1; ntohl(tcph->seq) + datalen;
ct_ftp_info->seq_aft_nl_set[dir] = 1;
} }
} }
UNLOCK_BH(&ip_ftp_lock); UNLOCK_BH(&ip_ftp_lock);
...@@ -330,16 +334,17 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -330,16 +334,17 @@ static int help(const struct iphdr *iph, size_t len,
DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n", DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
(int)matchlen, data + matchoff, (int)matchlen, data + matchoff,
matchlen, ntohl(tcph->seq) + matchoff); matchlen, ntohl(tcph->seq) + matchoff);
memset(&expect, 0, sizeof(expect));
/* Update the ftp info */ /* Update the ftp info */
LOCK_BH(&ip_ftp_lock); LOCK_BH(&ip_ftp_lock);
if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3])
== ct->tuplehash[dir].tuple.src.ip) { == ct->tuplehash[dir].tuple.src.ip) {
info->is_ftp = 21; exp->seq = ntohl(tcph->seq) + matchoff;
info->seq = ntohl(tcph->seq) + matchoff; exp_ftp_info->len = matchlen;
info->len = matchlen; exp_ftp_info->ftptype = search[i].ftptype;
info->ftptype = search[i].ftptype; exp_ftp_info->port = array[4] << 8 | array[5];
info->port = array[4] << 8 | array[5];
} else { } else {
/* Enrico Scholz's passive FTP to partially RNAT'd ftp /* Enrico Scholz's passive FTP to partially RNAT'd ftp
server: it really wants us to connect to a server: it really wants us to connect to a
...@@ -356,18 +361,21 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -356,18 +361,21 @@ static int help(const struct iphdr *iph, size_t len,
if (!loose) goto out; if (!loose) goto out;
} }
t = ((struct ip_conntrack_tuple) exp->tuple = ((struct ip_conntrack_tuple)
{ { ct->tuplehash[!dir].tuple.src.ip, { { ct->tuplehash[!dir].tuple.src.ip,
{ 0 } }, { 0 } },
{ htonl((array[0] << 24) | (array[1] << 16) { htonl((array[0] << 24) | (array[1] << 16)
| (array[2] << 8) | array[3]), | (array[2] << 8) | array[3]),
{ htons(array[4] << 8 | array[5]) }, { htons(array[4] << 8 | array[5]) },
IPPROTO_TCP }}); IPPROTO_TCP }});
mask = ((struct ip_conntrack_tuple) exp->mask = ((struct ip_conntrack_tuple)
{ { 0xFFFFFFFF, { 0 } }, { { 0xFFFFFFFF, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
exp->expectfn = NULL;
/* Ignore failure; should only happen with NAT */ /* Ignore failure; should only happen with NAT */
ip_conntrack_expect_related(ct, &t, &mask, NULL); ip_conntrack_expect_related(ct, &expect);
out: out:
UNLOCK_BH(&ip_ftp_lock); UNLOCK_BH(&ip_ftp_lock);
...@@ -375,6 +383,7 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -375,6 +383,7 @@ static int help(const struct iphdr *iph, size_t len,
} }
static struct ip_conntrack_helper ftp[MAX_PORTS]; static struct ip_conntrack_helper ftp[MAX_PORTS];
static char ftp_names[MAX_PORTS][10];
/* Not __exit: called from init() */ /* Not __exit: called from init() */
static void fini(void) static void fini(void)
...@@ -390,9 +399,10 @@ static void fini(void) ...@@ -390,9 +399,10 @@ static void fini(void)
static int __init init(void) static int __init init(void)
{ {
int i, ret; int i, ret;
char *tmpname;
if (ports[0] == 0) if (ports[0] == 0)
ports[0] = 21; ports[0] = FTP_PORT;
for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
memset(&ftp[i], 0, sizeof(struct ip_conntrack_helper)); memset(&ftp[i], 0, sizeof(struct ip_conntrack_helper));
...@@ -400,7 +410,19 @@ static int __init init(void) ...@@ -400,7 +410,19 @@ static int __init init(void)
ftp[i].tuple.dst.protonum = IPPROTO_TCP; ftp[i].tuple.dst.protonum = IPPROTO_TCP;
ftp[i].mask.src.u.tcp.port = 0xFFFF; ftp[i].mask.src.u.tcp.port = 0xFFFF;
ftp[i].mask.dst.protonum = 0xFFFF; ftp[i].mask.dst.protonum = 0xFFFF;
ftp[i].max_expected = 1;
ftp[i].timeout = 0;
ftp[i].flags = IP_CT_HELPER_F_REUSE_EXPECT;
ftp[i].me = ip_conntrack_ftp;
ftp[i].help = help; ftp[i].help = help;
tmpname = &ftp_names[i][0];
if (ports[i] == FTP_PORT)
sprintf(tmpname, "ftp");
else
sprintf(tmpname, "ftp-%d", ports[i]);
ftp[i].name = tmpname;
DEBUGP("ip_ct_ftp: registering helper for port %d\n", DEBUGP("ip_ct_ftp: registering helper for port %d\n",
ports[i]); ports[i]);
ret = ip_conntrack_helper_register(&ftp[i]); ret = ip_conntrack_helper_register(&ftp[i]);
...@@ -414,10 +436,10 @@ static int __init init(void) ...@@ -414,10 +436,10 @@ static int __init init(void)
return 0; return 0;
} }
#ifdef CONFIG_IP_NF_NAT_NEEDED
EXPORT_SYMBOL(ip_ftp_lock); EXPORT_SYMBOL(ip_ftp_lock);
EXPORT_SYMBOL(ip_conntrack_ftp); #endif
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
module_init(init); module_init(init);
module_exit(fini); module_exit(fini);
...@@ -11,12 +11,18 @@ ...@@ -11,12 +11,18 @@
** **
* Module load syntax: * Module load syntax:
* insmod ip_conntrack_irc.o ports=port1,port2,...port<MAX_PORTS> * insmod ip_conntrack_irc.o ports=port1,port2,...port<MAX_PORTS>
* max_dcc_channels=n dcc_timeout=secs
* *
* please give the ports of all IRC servers You wish to connect to. * please give the ports of all IRC servers You wish to connect to.
* If You don't specify ports, the default will be port 6667 * If You don't specify ports, the default will be port 6667.
* With max_dcc_channels you can define the maximum number of not
* yet answered DCC channels per IRC session (default 8).
* With dcc_timeout you can specify how long the system waits for
* an expected DCC channel (default 300 seconds).
* *
*/ */
#include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/ip.h> #include <linux/ip.h>
...@@ -30,6 +36,8 @@ ...@@ -30,6 +36,8 @@
#define MAX_PORTS 8 #define MAX_PORTS 8
static int ports[MAX_PORTS]; static int ports[MAX_PORTS];
static int ports_n_c = 0; static int ports_n_c = 0;
static int max_dcc_channels = 8;
static unsigned int dcc_timeout = 300;
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
MODULE_DESCRIPTION("IRC (DCC) connection tracking module"); MODULE_DESCRIPTION("IRC (DCC) connection tracking module");
...@@ -37,6 +45,10 @@ MODULE_LICENSE("GPL"); ...@@ -37,6 +45,10 @@ MODULE_LICENSE("GPL");
#ifdef MODULE_PARM #ifdef MODULE_PARM
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
MODULE_PARM_DESC(ports, "port numbers of IRC servers"); MODULE_PARM_DESC(ports, "port numbers of IRC servers");
MODULE_PARM(max_dcc_channels, "i");
MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session");
MODULE_PARM(dcc_timeout, "i");
MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
#endif #endif
#define NUM_DCCPROTO 5 #define NUM_DCCPROTO 5
...@@ -103,23 +115,15 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -103,23 +115,15 @@ static int help(const struct iphdr *iph, size_t len,
u_int32_t tcplen = len - iph->ihl * 4; u_int32_t tcplen = len - iph->ihl * 4;
u_int32_t datalen = tcplen - tcph->doff * 4; u_int32_t datalen = tcplen - tcph->doff * 4;
int dir = CTINFO2DIR(ctinfo); int dir = CTINFO2DIR(ctinfo);
struct ip_conntrack_tuple t, mask; struct ip_conntrack_expect expect, *exp = &expect;
struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info;
u_int32_t dcc_ip; u_int32_t dcc_ip;
u_int16_t dcc_port; u_int16_t dcc_port;
int i; int i;
char *addr_beg_p, *addr_end_p; char *addr_beg_p, *addr_end_p;
struct ip_ct_irc *info = &ct->help.ct_irc_info;
mask = ((struct ip_conntrack_tuple)
{ { 0, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
DEBUGP("entered\n"); DEBUGP("entered\n");
/* Can't track connections formed before we registered */
if (!info)
return NF_ACCEPT;
/* If packet is coming from IRC server */ /* If packet is coming from IRC server */
if (dir == IP_CT_DIR_REPLY) if (dir == IP_CT_DIR_REPLY)
...@@ -189,33 +193,37 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -189,33 +193,37 @@ static int help(const struct iphdr *iph, size_t len,
continue; continue;
} }
memset(&expect, 0, sizeof(expect));
LOCK_BH(&ip_irc_lock); LOCK_BH(&ip_irc_lock);
/* save position of address in dcc string, /* save position of address in dcc string,
* neccessary for NAT */ * neccessary for NAT */
info->is_irc = IP_CONNTR_IRC;
DEBUGP("tcph->seq = %u\n", tcph->seq); DEBUGP("tcph->seq = %u\n", tcph->seq);
info->seq = ntohl(tcph->seq) + (addr_beg_p - _data); exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data);
info->len = (addr_end_p - addr_beg_p); exp_irc_info->len = (addr_end_p - addr_beg_p);
info->port = dcc_port; exp_irc_info->port = dcc_port;
DEBUGP("wrote info seq=%u (ofs=%u), len=%d\n", DEBUGP("wrote info seq=%u (ofs=%u), len=%d\n",
info->seq, (addr_end_p - _data), info->len); exp->seq, (addr_end_p - _data), exp_irc_info->len);
exp->tuple = ((struct ip_conntrack_tuple)
{ { 0, { 0 } },
{ htonl(dcc_ip), { htons(dcc_port) },
IPPROTO_TCP }});
exp->mask = ((struct ip_conntrack_tuple)
{ { 0, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
memset(&t, 0, sizeof(t)); exp->expectfn = NULL;
t.src.ip = 0;
t.src.u.tcp.port = 0;
t.dst.ip = htonl(dcc_ip);
t.dst.u.tcp.port = htons(info->port);
t.dst.protonum = IPPROTO_TCP;
DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
NIPQUAD(t.src.ip), NIPQUAD(exp->tuple.src.ip),
ntohs(t.src.u.tcp.port), ntohs(exp->tuple.src.u.tcp.port),
NIPQUAD(t.dst.ip), NIPQUAD(exp->tuple.dst.ip),
ntohs(t.dst.u.tcp.port)); ntohs(exp->tuple.dst.u.tcp.port));
ip_conntrack_expect_related(ct, &t, &mask, NULL); ip_conntrack_expect_related(ct, &expect);
UNLOCK_BH(&ip_irc_lock); UNLOCK_BH(&ip_irc_lock);
return NF_ACCEPT; return NF_ACCEPT;
...@@ -226,29 +234,53 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -226,29 +234,53 @@ static int help(const struct iphdr *iph, size_t len,
} }
static struct ip_conntrack_helper irc_helpers[MAX_PORTS]; static struct ip_conntrack_helper irc_helpers[MAX_PORTS];
static char irc_names[MAX_PORTS][10];
static void fini(void); static void fini(void);
static int __init init(void) static int __init init(void)
{ {
int i, ret; int i, ret;
struct ip_conntrack_helper *hlpr;
char *tmpname;
if (max_dcc_channels < 1) {
printk("ip_conntrack_irc: max_dcc_channels must be a positive integer\n");
return -EBUSY;
}
if (dcc_timeout < 0) {
printk("ip_conntrack_irc: dcc_timeout must be a positive integer\n");
return -EBUSY;
}
/* If no port given, default to standard irc port */ /* If no port given, default to standard irc port */
if (ports[0] == 0) if (ports[0] == 0)
ports[0] = 6667; ports[0] = IRC_PORT;
for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
memset(&irc_helpers[i], 0, hlpr = &irc_helpers[i];
memset(hlpr, 0,
sizeof(struct ip_conntrack_helper)); sizeof(struct ip_conntrack_helper));
irc_helpers[i].tuple.src.u.tcp.port = htons(ports[i]); hlpr->tuple.src.u.tcp.port = htons(ports[i]);
irc_helpers[i].tuple.dst.protonum = IPPROTO_TCP; hlpr->tuple.dst.protonum = IPPROTO_TCP;
irc_helpers[i].mask.src.u.tcp.port = 0xFFFF; hlpr->mask.src.u.tcp.port = 0xFFFF;
irc_helpers[i].mask.dst.protonum = 0xFFFF; hlpr->mask.dst.protonum = 0xFFFF;
irc_helpers[i].help = help; hlpr->max_expected = max_dcc_channels;
hlpr->timeout = dcc_timeout;
hlpr->flags = IP_CT_HELPER_F_REUSE_EXPECT;
hlpr->me = ip_conntrack_irc;
hlpr->help = help;
tmpname = &irc_names[i][0];
if (ports[i] == IRC_PORT)
sprintf(tmpname, "irc");
else
sprintf(tmpname, "irc-%d", i);
hlpr->name = tmpname;
DEBUGP("port #%d: %d\n", i, ports[i]); DEBUGP("port #%d: %d\n", i, ports[i]);
ret = ip_conntrack_helper_register(&irc_helpers[i]); ret = ip_conntrack_helper_register(hlpr);
if (ret) { if (ret) {
printk("ip_conntrack_irc: ERROR registering port %d\n", printk("ip_conntrack_irc: ERROR registering port %d\n",
...@@ -273,5 +305,9 @@ static void fini(void) ...@@ -273,5 +305,9 @@ static void fini(void)
} }
} }
#ifdef CONFIG_IP_NF_NAT_NEEDED
EXPORT_SYMBOL(ip_irc_lock);
#endif
module_init(init); module_init(init);
module_exit(fini); module_exit(fini);
...@@ -57,5 +57,5 @@ new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len) ...@@ -57,5 +57,5 @@ new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len)
struct ip_conntrack_protocol ip_conntrack_generic_protocol struct ip_conntrack_protocol ip_conntrack_generic_protocol
= { { NULL, NULL }, 0, "unknown", = { { NULL, NULL }, 0, "unknown",
generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple, generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple,
generic_print_conntrack, established, new, NULL, NULL }; generic_print_conntrack, established, new, NULL, NULL, NULL };
...@@ -113,4 +113,4 @@ static int icmp_new(struct ip_conntrack *conntrack, ...@@ -113,4 +113,4 @@ static int icmp_new(struct ip_conntrack *conntrack,
struct ip_conntrack_protocol ip_conntrack_protocol_icmp struct ip_conntrack_protocol ip_conntrack_protocol_icmp
= { { NULL, NULL }, IPPROTO_ICMP, "icmp", = { { NULL, NULL }, IPPROTO_ICMP, "icmp",
icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple, icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple,
icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL }; icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL, NULL };
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
#include <linux/in.h> #include <linux/in.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <net/tcp.h>
#include <linux/netfilter_ipv4/ip_conntrack.h> #include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
#include <linux/netfilter_ipv4/lockhelp.h> #include <linux/netfilter_ipv4/lockhelp.h>
...@@ -227,7 +230,19 @@ static int tcp_new(struct ip_conntrack *conntrack, ...@@ -227,7 +230,19 @@ static int tcp_new(struct ip_conntrack *conntrack,
return 1; return 1;
} }
static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp,
struct sk_buff **pskb)
{
struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
unsigned int datalen;
datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
return between(exp->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen);
}
struct ip_conntrack_protocol ip_conntrack_protocol_tcp struct ip_conntrack_protocol ip_conntrack_protocol_tcp
= { { NULL, NULL }, IPPROTO_TCP, "tcp", = { { NULL, NULL }, IPPROTO_TCP, "tcp",
tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack, tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack,
tcp_packet, tcp_new, NULL, NULL }; tcp_packet, tcp_new, NULL, tcp_exp_matches_pkt, NULL };
...@@ -71,4 +71,4 @@ static int udp_new(struct ip_conntrack *conntrack, ...@@ -71,4 +71,4 @@ static int udp_new(struct ip_conntrack *conntrack,
struct ip_conntrack_protocol ip_conntrack_protocol_udp struct ip_conntrack_protocol ip_conntrack_protocol_udp
= { { NULL, NULL }, IPPROTO_UDP, "udp", = { { NULL, NULL }, IPPROTO_UDP, "udp",
udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack, udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack,
udp_packet, udp_new, NULL, NULL }; udp_packet, udp_new, NULL, NULL, NULL };
...@@ -62,7 +62,13 @@ print_expect(char *buffer, const struct ip_conntrack_expect *expect) ...@@ -62,7 +62,13 @@ print_expect(char *buffer, const struct ip_conntrack_expect *expect)
{ {
unsigned int len; unsigned int len;
len = sprintf(buffer, "EXPECTING: proto=%u ", if (expect->expectant->helper->timeout)
len = sprintf(buffer, "EXPECTING: %lu ",
timer_pending(&expect->timeout)
? (expect->timeout.expires - jiffies)/HZ : 0);
else
len = sprintf(buffer, "EXPECTING: - ");
len += sprintf(buffer + len, "proto=%u ",
expect->tuple.dst.protonum); expect->tuple.dst.protonum);
len += print_tuple(buffer + len, &expect->tuple, len += print_tuple(buffer + len, &expect->tuple,
__find_proto(expect->tuple.dst.protonum)); __find_proto(expect->tuple.dst.protonum));
...@@ -314,7 +320,7 @@ void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto) ...@@ -314,7 +320,7 @@ void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
{ {
WRITE_LOCK(&ip_conntrack_lock); WRITE_LOCK(&ip_conntrack_lock);
/* find_proto() returns proto_generic in case there is no protocol /* ip_ct_find_proto() returns proto_generic in case there is no protocol
* helper. So this should be enough - HW */ * helper. So this should be enough - HW */
LIST_DELETE(&protocol_list, proto); LIST_DELETE(&protocol_list, proto);
WRITE_UNLOCK(&ip_conntrack_lock); WRITE_UNLOCK(&ip_conntrack_lock);
...@@ -353,8 +359,12 @@ EXPORT_SYMBOL(ip_conntrack_helper_register); ...@@ -353,8 +359,12 @@ EXPORT_SYMBOL(ip_conntrack_helper_register);
EXPORT_SYMBOL(ip_conntrack_helper_unregister); EXPORT_SYMBOL(ip_conntrack_helper_unregister);
EXPORT_SYMBOL(ip_ct_selective_cleanup); EXPORT_SYMBOL(ip_ct_selective_cleanup);
EXPORT_SYMBOL(ip_ct_refresh); EXPORT_SYMBOL(ip_ct_refresh);
EXPORT_SYMBOL(ip_ct_find_proto);
EXPORT_SYMBOL(ip_ct_find_helper);
EXPORT_SYMBOL(ip_conntrack_expect_related); EXPORT_SYMBOL(ip_conntrack_expect_related);
EXPORT_SYMBOL(ip_conntrack_change_expect);
EXPORT_SYMBOL(ip_conntrack_unexpect_related); EXPORT_SYMBOL(ip_conntrack_unexpect_related);
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);
EXPORT_SYMBOL(ip_conntrack_lock);
...@@ -130,7 +130,7 @@ check_for_demasq(struct sk_buff **pskb) ...@@ -130,7 +130,7 @@ check_for_demasq(struct sk_buff **pskb)
struct ip_conntrack *ct; struct ip_conntrack *ct;
int ret; int ret;
protocol = find_proto(iph->protocol); protocol = ip_ct_find_proto(iph->protocol);
/* We don't feed packets to conntrack system unless we know /* We don't feed packets to conntrack system unless we know
they're part of an connection already established by an they're part of an connection already established by an
......
...@@ -21,10 +21,14 @@ ...@@ -21,10 +21,14 @@
#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
#include <linux/netfilter_ipv4/ip_nat.h> #include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter_ipv4/ip_nat_protocol.h> #include <linux/netfilter_ipv4/ip_nat_protocol.h>
#include <linux/netfilter_ipv4/ip_nat_core.h> #include <linux/netfilter_ipv4/ip_nat_core.h>
#include <linux/netfilter_ipv4/ip_nat_helper.h> #include <linux/netfilter_ipv4/ip_nat_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/listhelp.h> #include <linux/netfilter_ipv4/listhelp.h>
#if 0 #if 0
...@@ -34,6 +38,7 @@ ...@@ -34,6 +38,7 @@
#endif #endif
DECLARE_RWLOCK(ip_nat_lock); DECLARE_RWLOCK(ip_nat_lock);
DECLARE_RWLOCK_EXTERN(ip_conntrack_lock);
/* Calculated at init based on memory size */ /* Calculated at init based on memory size */
static unsigned int ip_nat_htable_size; static unsigned int ip_nat_htable_size;
...@@ -628,8 +633,9 @@ ip_nat_setup_info(struct ip_conntrack *conntrack, ...@@ -628,8 +633,9 @@ ip_nat_setup_info(struct ip_conntrack *conntrack,
} }
/* If there's a helper, assign it; based on new tuple. */ /* If there's a helper, assign it; based on new tuple. */
info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, if (!conntrack->master)
&reply); info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
&reply);
/* It's done. */ /* It's done. */
info->initialized |= (1 << HOOK2MANIP(hooknum)); info->initialized |= (1 << HOOK2MANIP(hooknum));
...@@ -724,6 +730,21 @@ manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, ...@@ -724,6 +730,21 @@ manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len,
#endif #endif
} }
static inline int exp_for_packet(struct ip_conntrack_expect *exp,
struct sk_buff **pskb)
{
struct ip_conntrack_protocol *proto;
int ret = 1;
READ_LOCK(&ip_conntrack_lock);
proto = ip_ct_find_proto((*pskb)->nh.iph->protocol);
if (proto->exp_matches_pkt)
ret = proto->exp_matches_pkt(exp, pskb);
READ_UNLOCK(&ip_conntrack_lock);
return ret;
}
/* Do packet manipulations according to binding. */ /* Do packet manipulations according to binding. */
unsigned int unsigned int
do_bindings(struct ip_conntrack *ct, do_bindings(struct ip_conntrack *ct,
...@@ -735,6 +756,7 @@ do_bindings(struct ip_conntrack *ct, ...@@ -735,6 +756,7 @@ do_bindings(struct ip_conntrack *ct,
unsigned int i; unsigned int i;
struct ip_nat_helper *helper; struct ip_nat_helper *helper;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
int is_tcp = (*pskb)->nh.iph->protocol == IPPROTO_TCP;
/* Need nat lock to protect against modification, but neither /* Need nat lock to protect against modification, but neither
conntrack (referenced) and helper (deleted with conntrack (referenced) and helper (deleted with
...@@ -773,11 +795,66 @@ do_bindings(struct ip_conntrack *ct, ...@@ -773,11 +795,66 @@ do_bindings(struct ip_conntrack *ct,
READ_UNLOCK(&ip_nat_lock); READ_UNLOCK(&ip_nat_lock);
if (helper) { if (helper) {
struct ip_conntrack_expect *exp = NULL;
struct list_head *cur_item;
int ret = NF_ACCEPT;
DEBUGP("do_bindings: helper existing for (%p)\n", ct);
/* Always defragged for helpers */ /* Always defragged for helpers */
IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
& __constant_htons(IP_MF|IP_OFFSET))); & __constant_htons(IP_MF|IP_OFFSET)));
return helper->help(ct, info, ctinfo, hooknum, pskb);
} else return NF_ACCEPT; /* Have to grab read lock before sibling_list traversal */
READ_LOCK(&ip_conntrack_lock);
list_for_each(cur_item, &ct->sibling_list) {
exp = list_entry(cur_item, struct ip_conntrack_expect,
expected_list);
/* if this expectation is already established, skip */
if (exp->sibling)
continue;
if (exp_for_packet(exp, pskb)) {
/* FIXME: May be true multiple times in the case of UDP!! */
DEBUGP("calling nat helper (exp=%p) for packet\n",
exp);
ret = helper->help(ct, exp, info, ctinfo,
hooknum, pskb);
if (ret != NF_ACCEPT) {
READ_UNLOCK(&ip_conntrack_lock);
return ret;
}
}
}
/* Helper might want to manip the packet even when there is no expectation */
if (!exp && helper->flags & IP_NAT_HELPER_F_ALWAYS) {
DEBUGP("calling nat helper for packet without expectation\n");
ret = helper->help(ct, NULL, info, ctinfo,
hooknum, pskb);
if (ret != NF_ACCEPT) {
READ_UNLOCK(&ip_conntrack_lock);
return ret;
}
}
READ_UNLOCK(&ip_conntrack_lock);
/* Adjust sequence number only once per packet
* (helper is called at all hooks) */
if (is_tcp && (hooknum == NF_IP_POST_ROUTING
|| hooknum == NF_IP_LOCAL_IN)) {
DEBUGP("ip_nat_core: adjusting sequence number\n");
/* future: put this in a l4-proto specific function,
* and call this function here. */
ip_nat_seq_adjust(*pskb, ct, ctinfo);
}
return ret;
} else
return NF_ACCEPT;
/* not reached */
} }
unsigned int unsigned int
......
...@@ -28,38 +28,30 @@ DECLARE_LOCK_EXTERN(ip_ftp_lock); ...@@ -28,38 +28,30 @@ DECLARE_LOCK_EXTERN(ip_ftp_lock);
/* FIXME: Time out? --RR */ /* FIXME: Time out? --RR */
static int static unsigned int
ftp_nat_expected(struct sk_buff **pskb, ftp_nat_expected(struct sk_buff **pskb,
unsigned int hooknum, unsigned int hooknum,
struct ip_conntrack *ct, struct ip_conntrack *ct,
struct ip_nat_info *info, struct ip_nat_info *info)
struct ip_conntrack *master,
struct ip_nat_info *masterinfo,
unsigned int *verdict)
{ {
struct ip_nat_multi_range mr; struct ip_nat_multi_range mr;
u_int32_t newdstip, newsrcip, newip; u_int32_t newdstip, newsrcip, newip;
struct ip_ct_ftp *ftpinfo; struct ip_ct_ftp_expect *exp_ftp_info;
struct ip_conntrack *master = master_ct(ct);
IP_NF_ASSERT(info); IP_NF_ASSERT(info);
IP_NF_ASSERT(master); IP_NF_ASSERT(master);
IP_NF_ASSERT(masterinfo);
IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum)))); IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
DEBUGP("nat_expected: We have a connection!\n"); DEBUGP("nat_expected: We have a connection!\n");
/* Master must be an ftp connection */ exp_ftp_info = &ct->master->help.exp_ftp_info;
ftpinfo = &master->help.ct_ftp_info;
LOCK_BH(&ip_ftp_lock); LOCK_BH(&ip_ftp_lock);
if (ftpinfo->is_ftp != 21) {
UNLOCK_BH(&ip_ftp_lock);
DEBUGP("nat_expected: master not ftp\n");
return 0;
}
if (ftpinfo->ftptype == IP_CT_FTP_PORT if (exp_ftp_info->ftptype == IP_CT_FTP_PORT
|| ftpinfo->ftptype == IP_CT_FTP_EPRT) { || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) {
/* PORT command: make connection go to the client. */ /* PORT command: make connection go to the client. */
newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
...@@ -92,11 +84,9 @@ ftp_nat_expected(struct sk_buff **pskb, ...@@ -92,11 +84,9 @@ ftp_nat_expected(struct sk_buff **pskb,
mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
mr.range[0].min = mr.range[0].max mr.range[0].min = mr.range[0].max
= ((union ip_conntrack_manip_proto) = ((union ip_conntrack_manip_proto)
{ htons(ftpinfo->port) }); { htons(exp_ftp_info->port) });
} }
*verdict = ip_nat_setup_info(ct, &mr, hooknum); return ip_nat_setup_info(ct, &mr, hooknum);
return 1;
} }
static int static int
...@@ -176,27 +166,22 @@ static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t, ...@@ -176,27 +166,22 @@ static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
[IP_CT_FTP_EPSV] mangle_epsv_packet [IP_CT_FTP_EPSV] mangle_epsv_packet
}; };
static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info, static int ftp_data_fixup(const struct ip_ct_ftp_expect *ct_ftp_info,
struct ip_conntrack *ct, struct ip_conntrack *ct,
unsigned int datalen,
struct sk_buff **pskb, struct sk_buff **pskb,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *expect)
{ {
u_int32_t newip; u_int32_t newip;
struct iphdr *iph = (*pskb)->nh.iph; struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr *tcph = (void *)iph + iph->ihl*4; struct tcphdr *tcph = (void *)iph + iph->ihl*4;
u_int16_t port; u_int16_t port;
struct ip_conntrack_tuple tuple; struct ip_conntrack_tuple newtuple;
/* Don't care about source port */
const struct ip_conntrack_tuple mask
= { { 0xFFFFFFFF, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF } };
memset(&tuple, 0, sizeof(tuple));
MUST_BE_LOCKED(&ip_ftp_lock); MUST_BE_LOCKED(&ip_ftp_lock);
DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n", DEBUGP("FTP_NAT: seq %u + %u in %u\n",
ct_ftp_info->seq, ct_ftp_info->len, expect->seq, ct_ftp_info->len,
ntohl(tcph->seq), datalen); ntohl(tcph->seq));
/* Change address inside packet to match way we're mapping /* Change address inside packet to match way we're mapping
this connection. */ this connection. */
...@@ -206,29 +191,34 @@ static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info, ...@@ -206,29 +191,34 @@ static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info,
is */ is */
newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
/* Expect something from client->server */ /* Expect something from client->server */
tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newtuple.src.ip =
tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newtuple.dst.ip =
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
} else { } else {
/* PORT command: must be where server thinks client is */ /* PORT command: must be where server thinks client is */
newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
/* Expect something from server->client */ /* Expect something from server->client */
tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; newtuple.src.ip =
tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
newtuple.dst.ip =
ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
} }
tuple.dst.protonum = IPPROTO_TCP; newtuple.dst.protonum = IPPROTO_TCP;
newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
/* Try to get same port: if not, try to change it. */ /* Try to get same port: if not, try to change it. */
for (port = ct_ftp_info->port; port != 0; port++) { for (port = ct_ftp_info->port; port != 0; port++) {
tuple.dst.u.tcp.port = htons(port); newtuple.dst.u.tcp.port = htons(port);
if (ip_conntrack_expect_related(ct, &tuple, &mask, NULL) == 0) if (ip_conntrack_change_expect(expect, &newtuple) == 0)
break; break;
} }
if (port == 0) if (port == 0)
return 0; return 0;
if (!mangle[ct_ftp_info->ftptype](pskb, newip, port, if (!mangle[ct_ftp_info->ftptype](pskb, newip, port,
ct_ftp_info->seq - ntohl(tcph->seq), expect->seq - ntohl(tcph->seq),
ct_ftp_info->len, ct, ctinfo)) ct_ftp_info->len, ct, ctinfo))
return 0; return 0;
...@@ -236,6 +226,7 @@ static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info, ...@@ -236,6 +226,7 @@ static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info,
} }
static unsigned int help(struct ip_conntrack *ct, static unsigned int help(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info, struct ip_nat_info *info,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int hooknum, unsigned int hooknum,
...@@ -245,13 +236,12 @@ static unsigned int help(struct ip_conntrack *ct, ...@@ -245,13 +236,12 @@ static unsigned int help(struct ip_conntrack *ct,
struct tcphdr *tcph = (void *)iph + iph->ihl*4; struct tcphdr *tcph = (void *)iph + iph->ihl*4;
unsigned int datalen; unsigned int datalen;
int dir; int dir;
int score; struct ip_ct_ftp_expect *ct_ftp_info;
struct ip_ct_ftp *ct_ftp_info
= &ct->help.ct_ftp_info;
/* Delete SACK_OK on initial TCP SYNs. */ if (!exp)
if (tcph->syn && !tcph->ack) DEBUGP("ip_nat_ftp: no exp!!");
ip_nat_delete_sack(*pskb, tcph);
ct_ftp_info = &exp->help.exp_ftp_info;
/* Only mangle things once: original direction in POST_ROUTING /* Only mangle things once: original direction in POST_ROUTING
and reply direction on PRE_ROUTING. */ and reply direction on PRE_ROUTING. */
...@@ -267,50 +257,34 @@ static unsigned int help(struct ip_conntrack *ct, ...@@ -267,50 +257,34 @@ static unsigned int help(struct ip_conntrack *ct,
} }
datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
score = 0;
LOCK_BH(&ip_ftp_lock); LOCK_BH(&ip_ftp_lock);
if (ct_ftp_info->len) { /* If it's in the right range... */
/* If it's in the right range... */ if (between(exp->seq + ct_ftp_info->len,
score += between(ct_ftp_info->seq, ntohl(tcph->seq), ntohl(tcph->seq),
ntohl(tcph->seq) + datalen); ntohl(tcph->seq) + datalen)) {
score += between(ct_ftp_info->seq + ct_ftp_info->len, if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) {
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
if (score == 1) {
/* Half a match? This means a partial retransmisison.
It's a cracker being funky. */
if (net_ratelimit()) {
printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
ct_ftp_info->seq, ct_ftp_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
}
UNLOCK_BH(&ip_ftp_lock); UNLOCK_BH(&ip_ftp_lock);
return NF_DROP; return NF_DROP;
} else if (score == 2) {
if (!ftp_data_fixup(ct_ftp_info, ct, datalen,
pskb, ctinfo)) {
UNLOCK_BH(&ip_ftp_lock);
return NF_DROP;
}
/* skb may have been reallocated */
iph = (*pskb)->nh.iph;
tcph = (void *)iph + iph->ihl*4;
} }
} else {
/* Half a match? This means a partial retransmisison.
It's a cracker being funky. */
if (net_ratelimit()) {
printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
exp->seq, ct_ftp_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
}
UNLOCK_BH(&ip_ftp_lock);
return NF_DROP;
} }
UNLOCK_BH(&ip_ftp_lock); UNLOCK_BH(&ip_ftp_lock);
ip_nat_seq_adjust(*pskb, ct, ctinfo);
return NF_ACCEPT; return NF_ACCEPT;
} }
static struct ip_nat_helper ftp[MAX_PORTS]; static struct ip_nat_helper ftp[MAX_PORTS];
static char ftp_names[MAX_PORTS][6]; static char ftp_names[MAX_PORTS][10];
static struct ip_nat_expect ftp_expect
= { { NULL, NULL }, ftp_nat_expected };
/* Not __exit: called from init() */ /* Not __exit: called from init() */
static void fini(void) static void fini(void)
...@@ -321,49 +295,49 @@ static void fini(void) ...@@ -321,49 +295,49 @@ static void fini(void)
DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]); DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
ip_nat_helper_unregister(&ftp[i]); ip_nat_helper_unregister(&ftp[i]);
} }
ip_nat_expect_unregister(&ftp_expect);
} }
static int __init init(void) static int __init init(void)
{ {
int i, ret; int i, ret = 0;
char *tmpname; char *tmpname;
ret = ip_nat_expect_register(&ftp_expect); if (ports[0] == 0)
if (ret == 0) { ports[0] = FTP_PORT;
if (ports[0] == 0)
ports[0] = 21;
for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
memset(&ftp[i], 0, sizeof(struct ip_nat_helper));
ftp[i].tuple.dst.protonum = IPPROTO_TCP;
ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
ftp[i].mask.dst.protonum = 0xFFFF;
ftp[i].mask.src.u.tcp.port = 0xFFFF;
ftp[i].help = help;
tmpname = &ftp_names[i][0]; for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
sprintf(tmpname, "ftp%2.2d", i);
ftp[i].name = tmpname;
DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
ports[i]);
ret = ip_nat_helper_register(&ftp[i]);
if (ret) { memset(&ftp[i], 0, sizeof(struct ip_nat_helper));
printk("ip_nat_ftp: error registering helper for port %d\n", ports[i]);
fini(); ftp[i].tuple.dst.protonum = IPPROTO_TCP;
return ret; ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
} ftp[i].mask.dst.protonum = 0xFFFF;
ports_c++; ftp[i].mask.src.u.tcp.port = 0xFFFF;
ftp[i].help = help;
ftp[i].me = THIS_MODULE;
ftp[i].flags = 0;
ftp[i].expect = ftp_nat_expected;
tmpname = &ftp_names[i][0];
if (ports[i] == FTP_PORT)
sprintf(tmpname, "ftp");
else
sprintf(tmpname, "ftp-%d", i);
ftp[i].name = tmpname;
DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
ports[i]);
ret = ip_nat_helper_register(&ftp[i]);
if (ret) {
printk("ip_nat_ftp: error registering "
"helper for port %d\n", ports[i]);
fini();
return ret;
} }
ports_c++;
} else {
ip_nat_expect_unregister(&ftp_expect);
} }
return ret; return ret;
} }
......
/* ip_nat_mangle.c - generic support functions for NAT helpers /* ip_nat_mangle.c - generic support functions for NAT helpers
* *
* (C) 2000 by Harald Welte <laforge@gnumonks.org> * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
* *
* distributed under the terms of GNU GPL * distributed under the terms of GNU GPL
*
* 14 Jan 2002 Harald Welte <laforge@gnumonks.org>:
* - add support for SACK adjustment
* 14 Mar 2002 Harald Welte <laforge@gnumonks.org>:
* - merge SACK support into newnat API
*/ */
#include <linux/version.h> #include <linux/version.h>
#include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kmod.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
...@@ -19,6 +26,8 @@ ...@@ -19,6 +26,8 @@
#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/ip_nat.h> #include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter_ipv4/ip_nat_protocol.h> #include <linux/netfilter_ipv4/ip_nat_protocol.h>
#include <linux/netfilter_ipv4/ip_nat_core.h> #include <linux/netfilter_ipv4/ip_nat_core.h>
...@@ -32,7 +41,7 @@ ...@@ -32,7 +41,7 @@
#define DEBUGP(format, args...) #define DEBUGP(format, args...)
#define DUMP_OFFSET(x) #define DUMP_OFFSET(x)
#endif #endif
DECLARE_LOCK(ip_nat_seqofs_lock); DECLARE_LOCK(ip_nat_seqofs_lock);
static inline int static inline int
...@@ -199,6 +208,103 @@ ip_nat_mangle_tcp_packet(struct sk_buff **skb, ...@@ -199,6 +208,103 @@ ip_nat_mangle_tcp_packet(struct sk_buff **skb,
return 1; return 1;
} }
/* Adjust one found SACK option including checksum correction */
static void
sack_adjust(struct tcphdr *tcph,
unsigned char *ptr,
struct ip_nat_seq *natseq)
{
struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2);
int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3;
int i;
for (i = 0; i < num_sacks; i++, sp++) {
u_int32_t new_start_seq, new_end_seq;
if (after(ntohl(sp->start_seq) - natseq->offset_before,
natseq->correction_pos))
new_start_seq = ntohl(sp->start_seq)
- natseq->offset_after;
else
new_start_seq = ntohl(sp->start_seq)
- natseq->offset_before;
new_start_seq = htonl(new_start_seq);
if (after(ntohl(sp->end_seq) - natseq->offset_before,
natseq->correction_pos))
new_end_seq = ntohl(sp->end_seq)
- natseq->offset_after;
else
new_end_seq = ntohl(sp->end_seq)
- natseq->offset_before;
new_end_seq = htonl(new_end_seq);
DEBUGP("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
ntohl(sp->start_seq), new_start_seq,
ntohl(sp->end_seq), new_end_seq);
tcph->check =
ip_nat_cheat_check(~sp->start_seq, new_start_seq,
ip_nat_cheat_check(~sp->end_seq,
new_end_seq,
tcph->check));
sp->start_seq = new_start_seq;
sp->end_seq = new_end_seq;
}
}
/* TCP SACK sequence number adjustment, return 0 if sack found and adjusted */
static inline int
ip_nat_sack_adjust(struct sk_buff *skb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo)
{
struct iphdr *iph;
struct tcphdr *tcph;
unsigned char *ptr;
int length, dir, sack_adjusted = 0;
iph = skb->nh.iph;
tcph = (void *)iph + iph->ihl*4;
length = (tcph->doff*4)-sizeof(struct tcphdr);
ptr = (unsigned char *)(tcph+1);
dir = CTINFO2DIR(ctinfo);
while (length > 0) {
int opcode = *ptr++;
int opsize;
switch (opcode) {
case TCPOPT_EOL:
return !sack_adjusted;
case TCPOPT_NOP:
length--;
continue;
default:
opsize = *ptr++;
if (opsize > length) /* no partial opts */
return !sack_adjusted;
if (opcode == TCPOPT_SACK) {
/* found SACK */
if((opsize >= (TCPOLEN_SACK_BASE
+TCPOLEN_SACK_PERBLOCK)) &&
!((opsize - TCPOLEN_SACK_BASE)
% TCPOLEN_SACK_PERBLOCK))
sack_adjust(tcph, ptr-2,
&ct->nat.info.seq[!dir]);
sack_adjusted = 1;
}
ptr += opsize-2;
length -= opsize;
}
}
return !sack_adjusted;
}
/* TCP sequence number adjustment */ /* TCP sequence number adjustment */
int int
ip_nat_seq_adjust(struct sk_buff *skb, ip_nat_seq_adjust(struct sk_buff *skb,
...@@ -243,51 +349,9 @@ ip_nat_seq_adjust(struct sk_buff *skb, ...@@ -243,51 +349,9 @@ ip_nat_seq_adjust(struct sk_buff *skb,
tcph->seq = newseq; tcph->seq = newseq;
tcph->ack_seq = newack; tcph->ack_seq = newack;
return 0; ip_nat_sack_adjust(skb, ct, ctinfo);
}
/* Grrr... SACK. Fuck me even harder. Don't want to fix it on the
fly, so blow it away. */
void
ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph)
{
unsigned int i;
u_int8_t *opt = (u_int8_t *)tcph;
DEBUGP("Seeking SACKPERM in SYN packet (doff = %u).\n",
tcph->doff * 4);
for (i = sizeof(struct tcphdr); i < tcph->doff * 4;) {
DEBUGP("%u ", opt[i]);
switch (opt[i]) {
case TCPOPT_NOP:
case TCPOPT_EOL:
i++;
break;
case TCPOPT_SACK_PERM:
goto found_opt;
default: return 0;
/* Worst that can happen: it will take us over. */
i += opt[i+1] ?: 1;
}
}
DEBUGP("\n");
return;
found_opt:
DEBUGP("\n");
DEBUGP("Found SACKPERM at offset %u.\n", i);
/* Must be within TCP header, and valid SACK perm. */
if (i + opt[i+1] <= tcph->doff*4 && opt[i+1] == 2) {
/* Replace with NOPs. */
tcph->check
= ip_nat_cheat_check(*((u_int16_t *)(opt + i))^0xFFFF,
(TCPOPT_NOP<<8)|TCPOPT_NOP, tcph->check);
opt[i] = opt[i+1] = TCPOPT_NOP;
}
else DEBUGP("Something wrong with SACK_PERM.\n");
} }
static inline int static inline int
...@@ -297,10 +361,51 @@ helper_cmp(const struct ip_nat_helper *helper, ...@@ -297,10 +361,51 @@ helper_cmp(const struct ip_nat_helper *helper,
return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask); return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask);
} }
#define MODULE_MAX_NAMELEN 32
int ip_nat_helper_register(struct ip_nat_helper *me) int ip_nat_helper_register(struct ip_nat_helper *me)
{ {
int ret = 0; int ret = 0;
if (me->me && !(me->flags & IP_NAT_HELPER_F_STANDALONE)) {
struct ip_conntrack_helper *ct_helper;
if ((ct_helper = ip_ct_find_helper(&me->tuple))
&& ct_helper->me) {
__MOD_INC_USE_COUNT(ct_helper->me);
} else {
/* We are a NAT helper for protocol X. If we need
* respective conntrack helper for protoccol X, compute
* conntrack helper name and try to load module */
char name[MODULE_MAX_NAMELEN];
const char *tmp = me->me->name;
if (strlen(tmp) + 6 > MODULE_MAX_NAMELEN) {
printk(__FUNCTION__ ": unable to "
"compute conntrack helper name "
"from %s\n", tmp);
return -EBUSY;
}
tmp += 6;
sprintf(name, "ip_conntrack%s", tmp);
#ifdef CONFIG_KMOD
if (!request_module(name)
&& (ct_helper = ip_ct_find_helper(&me->tuple))
&& ct_helper->me) {
__MOD_INC_USE_COUNT(ct_helper->me);
} else {
printk("unable to load module %s\n", name);
return -EBUSY;
}
#else
printk("unable to load module %s automatically "
"because kernel was compiled without kernel "
"module loader support\n", name);
return -EBUSY;
#endif
}
}
WRITE_LOCK(&ip_nat_lock); WRITE_LOCK(&ip_nat_lock);
if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple))
ret = -EBUSY; ret = -EBUSY;
...@@ -327,8 +432,14 @@ kill_helper(const struct ip_conntrack *i, void *helper) ...@@ -327,8 +432,14 @@ kill_helper(const struct ip_conntrack *i, void *helper)
void ip_nat_helper_unregister(struct ip_nat_helper *me) void ip_nat_helper_unregister(struct ip_nat_helper *me)
{ {
int found = 0;
WRITE_LOCK(&ip_nat_lock); WRITE_LOCK(&ip_nat_lock);
LIST_DELETE(&helpers, me); /* Autoloading conntrack helper might have failed */
if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) {
LIST_DELETE(&helpers, me);
found = 1;
}
WRITE_UNLOCK(&ip_nat_lock); WRITE_UNLOCK(&ip_nat_lock);
/* Someone could be still looking at the helper in a bh. */ /* Someone could be still looking at the helper in a bh. */
...@@ -344,5 +455,19 @@ void ip_nat_helper_unregister(struct ip_nat_helper *me) ...@@ -344,5 +455,19 @@ void ip_nat_helper_unregister(struct ip_nat_helper *me)
worse. --RR */ worse. --RR */
ip_ct_selective_cleanup(kill_helper, me); ip_ct_selective_cleanup(kill_helper, me);
MOD_DEC_USE_COUNT; if (found)
MOD_DEC_USE_COUNT;
/* If we are no standalone NAT helper, we need to decrement usage count
* on our conntrack helper */
if (me->me && !(me->flags & IP_NAT_HELPER_F_STANDALONE)) {
struct ip_conntrack_helper *ct_helper;
if ((ct_helper = ip_ct_find_helper(&me->tuple))
&& ct_helper->me) {
__MOD_DEC_USE_COUNT(ct_helper->me);
} else
printk(__FUNCTION__ ": unable to decrement usage count"
" of conntrack helper %s\n", me->me->name);
}
} }
...@@ -51,42 +51,29 @@ DECLARE_LOCK_EXTERN(ip_irc_lock); ...@@ -51,42 +51,29 @@ DECLARE_LOCK_EXTERN(ip_irc_lock);
/* FIXME: Time out? --RR */ /* FIXME: Time out? --RR */
static int static unsigned int
irc_nat_expected(struct sk_buff **pskb, irc_nat_expected(struct sk_buff **pskb,
unsigned int hooknum, unsigned int hooknum,
struct ip_conntrack *ct, struct ip_conntrack *ct,
struct ip_nat_info *info, struct ip_nat_info *info)
struct ip_conntrack *master,
struct ip_nat_info *masterinfo, unsigned int *verdict)
{ {
struct ip_nat_multi_range mr; struct ip_nat_multi_range mr;
u_int32_t newdstip, newsrcip, newip; u_int32_t newdstip, newsrcip, newip;
struct ip_ct_irc *ircinfo;
struct ip_conntrack *master = master_ct(ct);
IP_NF_ASSERT(info); IP_NF_ASSERT(info);
IP_NF_ASSERT(master); IP_NF_ASSERT(master);
IP_NF_ASSERT(masterinfo);
IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
DEBUGP("nat_expected: We have a connection!\n"); DEBUGP("nat_expected: We have a connection!\n");
/* Master must be an irc connection */
ircinfo = &master->help.ct_irc_info;
LOCK_BH(&ip_irc_lock);
if (ircinfo->is_irc != IP_CONNTR_IRC) {
UNLOCK_BH(&ip_irc_lock);
DEBUGP("nat_expected: master not irc\n");
return 0;
}
newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
DEBUGP("nat_expected: DCC cmd. %u.%u.%u.%u->%u.%u.%u.%u\n", DEBUGP("nat_expected: DCC cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
NIPQUAD(newsrcip), NIPQUAD(newdstip)); NIPQUAD(newsrcip), NIPQUAD(newdstip));
UNLOCK_BH(&ip_irc_lock);
if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
newip = newsrcip; newip = newsrcip;
else else
...@@ -99,16 +86,14 @@ irc_nat_expected(struct sk_buff **pskb, ...@@ -99,16 +86,14 @@ irc_nat_expected(struct sk_buff **pskb,
mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
mr.range[0].min_ip = mr.range[0].max_ip = newip; mr.range[0].min_ip = mr.range[0].max_ip = newip;
*verdict = ip_nat_setup_info(ct, &mr, hooknum); return ip_nat_setup_info(ct, &mr, hooknum);
return 1;
} }
static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info, static int irc_data_fixup(const struct ip_ct_irc_expect *ct_irc_info,
struct ip_conntrack *ct, struct ip_conntrack *ct,
unsigned int datalen,
struct sk_buff **pskb, struct sk_buff **pskb,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *expect)
{ {
u_int32_t newip; u_int32_t newip;
struct ip_conntrack_tuple t; struct ip_conntrack_tuple t;
...@@ -121,9 +106,9 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info, ...@@ -121,9 +106,9 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info,
MUST_BE_LOCKED(&ip_irc_lock); MUST_BE_LOCKED(&ip_irc_lock);
DEBUGP("IRC_NAT: info (seq %u + %u) packet(seq %u + %u)\n", DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n",
ct_irc_info->seq, ct_irc_info->len, expect->seq, ct_irc_info->len,
ntohl(tcph->seq), datalen); ntohl(tcph->seq));
newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
...@@ -133,13 +118,11 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info, ...@@ -133,13 +118,11 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info,
only set in ip_conntrack_irc, with ip_irc_lock held only set in ip_conntrack_irc, with ip_irc_lock held
writable */ writable */
t = ct->expected.tuple; t = expect->tuple;
t.dst.ip = newip; t.dst.ip = newip;
for (port = ct_irc_info->port; port != 0; port++) { for (port = ct_irc_info->port; port != 0; port++) {
t.dst.u.tcp.port = htons(port); t.dst.u.tcp.port = htons(port);
if (ip_conntrack_expect_related(ct, &t, if (ip_conntrack_change_expect(expect, &t) == 0) {
&ct->expected.mask,
NULL) == 0) {
DEBUGP("using port %d", port); DEBUGP("using port %d", port);
break; break;
} }
...@@ -166,26 +149,28 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info, ...@@ -166,26 +149,28 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info,
buffer, NIPQUAD(newip), port); buffer, NIPQUAD(newip), port);
return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
ct_irc_info->seq - ntohl(tcph->seq), expect->seq - ntohl(tcph->seq),
ct_irc_info->len, buffer, ct_irc_info->len, buffer,
strlen(buffer)); strlen(buffer));
} }
static unsigned int help(struct ip_conntrack *ct, static unsigned int help(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info, struct ip_nat_info *info,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int hooknum, struct sk_buff **pskb) unsigned int hooknum,
struct sk_buff **pskb)
{ {
struct iphdr *iph = (*pskb)->nh.iph; struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr *tcph = (void *) iph + iph->ihl * 4; struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
unsigned int datalen; unsigned int datalen;
int dir; int dir;
int score; struct ip_ct_irc_expect *ct_irc_info;
struct ip_ct_irc *ct_irc_info = &ct->help.ct_irc_info;
/* Delete SACK_OK on initial TCP SYNs. */ if (!exp)
if (tcph->syn && !tcph->ack) DEBUGP("ip_nat_irc: no exp!!");
ip_nat_delete_sack(*pskb, tcph);
ct_irc_info = &exp->help.exp_irc_info;
/* Only mangle things once: original direction in POST_ROUTING /* Only mangle things once: original direction in POST_ROUTING
and reply direction on PRE_ROUTING. */ and reply direction on PRE_ROUTING. */
...@@ -202,55 +187,35 @@ static unsigned int help(struct ip_conntrack *ct, ...@@ -202,55 +187,35 @@ static unsigned int help(struct ip_conntrack *ct,
DEBUGP("got beyond not touching\n"); DEBUGP("got beyond not touching\n");
datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
score = 0;
LOCK_BH(&ip_irc_lock); LOCK_BH(&ip_irc_lock);
if (ct_irc_info->len) { /* Check wether the whole IP/address pattern is carried in the payload */
DEBUGP("got beyond ct_irc_info->len\n"); if (between(exp->seq + ct_irc_info->len,
ntohl(tcph->seq),
/* If it's in the right range... */ ntohl(tcph->seq) + datalen)) {
score += between(ct_irc_info->seq, ntohl(tcph->seq), if (!irc_data_fixup(ct_irc_info, ct, pskb, ctinfo, exp)) {
ntohl(tcph->seq) + datalen);
score += between(ct_irc_info->seq + ct_irc_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
if (score == 1) {
/* Half a match? This means a partial retransmisison.
It's a cracker being funky. */
if (net_ratelimit()) {
printk
("IRC_NAT: partial packet %u/%u in %u/%u\n",
ct_irc_info->seq, ct_irc_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
}
UNLOCK_BH(&ip_irc_lock); UNLOCK_BH(&ip_irc_lock);
return NF_DROP; return NF_DROP;
} else if (score == 2) {
DEBUGP("IRC_NAT: score=2, calling fixup\n");
if (!irc_data_fixup(ct_irc_info, ct, datalen,
pskb, ctinfo)) {
UNLOCK_BH(&ip_irc_lock);
return NF_DROP;
}
/* skb may have been reallocated */
iph = (*pskb)->nh.iph;
tcph = (void *) iph + iph->ihl * 4;
} }
} else {
/* Half a match? This means a partial retransmisison.
It's a cracker being funky. */
if (net_ratelimit()) {
printk
("IRC_NAT: partial packet %u/%u in %u/%u\n",
exp->seq, ct_irc_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
}
UNLOCK_BH(&ip_irc_lock);
return NF_DROP;
} }
UNLOCK_BH(&ip_irc_lock); UNLOCK_BH(&ip_irc_lock);
ip_nat_seq_adjust(*pskb, ct, ctinfo);
return NF_ACCEPT; return NF_ACCEPT;
} }
static struct ip_nat_helper ip_nat_irc_helpers[MAX_PORTS]; static struct ip_nat_helper ip_nat_irc_helpers[MAX_PORTS];
static char ip_nih_names[MAX_PORTS][6]; static char irc_names[MAX_PORTS][10];
static struct ip_nat_expect irc_expect
= { {NULL, NULL}, irc_nat_expected };
/* This function is intentionally _NOT_ defined as __exit, because /* This function is intentionally _NOT_ defined as __exit, because
* it is needed by init() */ * it is needed by init() */
...@@ -262,52 +227,54 @@ static void fini(void) ...@@ -262,52 +227,54 @@ static void fini(void)
DEBUGP("ip_nat_irc: unregistering helper for port %d\n", DEBUGP("ip_nat_irc: unregistering helper for port %d\n",
ports[i]); ports[i]);
ip_nat_helper_unregister(&ip_nat_irc_helpers[i]); ip_nat_helper_unregister(&ip_nat_irc_helpers[i]);
} }
ip_nat_expect_unregister(&irc_expect);
} }
static int __init init(void) static int __init init(void)
{ {
int ret; int ret = 0;
int i; int i;
struct ip_nat_helper *hlpr; struct ip_nat_helper *hlpr;
char *tmpname; char *tmpname;
ret = ip_nat_expect_register(&irc_expect); if (ports[0] == 0) {
if (ret == 0) { ports[0] = IRC_PORT;
}
if (ports[0] == 0) {
ports[0] = 6667;
}
for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) { for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) {
hlpr = &ip_nat_irc_helpers[i]; hlpr = &ip_nat_irc_helpers[i];
memset(hlpr, 0, memset(hlpr, 0,
sizeof(struct ip_nat_helper)); sizeof(struct ip_nat_helper));
hlpr->tuple.dst.protonum = IPPROTO_TCP; hlpr->tuple.dst.protonum = IPPROTO_TCP;
hlpr->tuple.src.u.tcp.port = htons(ports[i]); hlpr->tuple.src.u.tcp.port = htons(ports[i]);
hlpr->mask.src.u.tcp.port = 0xFFFF; hlpr->mask.src.u.tcp.port = 0xFFFF;
hlpr->mask.dst.protonum = 0xFFFF; hlpr->mask.dst.protonum = 0xFFFF;
hlpr->help = help; hlpr->help = help;
hlpr->flags = 0;
tmpname = &ip_nih_names[i][0]; hlpr->me = THIS_MODULE;
sprintf(tmpname, "irc%2.2d", i); hlpr->expect = irc_nat_expected;
hlpr->name = tmpname; tmpname = &irc_names[i][0];
DEBUGP if (ports[i] == IRC_PORT)
("ip_nat_irc: Trying to register helper for port %d: name %s\n", sprintf(tmpname, "irc");
ports[i], hlpr->name); else
ret = ip_nat_helper_register(hlpr); sprintf(tmpname, "irc-%d", i);
hlpr->name = tmpname;
if (ret) {
printk DEBUGP
("ip_nat_irc: error registering helper for port %d\n", ("ip_nat_irc: Trying to register helper for port %d: name %s\n",
ports[i]); ports[i], hlpr->name);
fini(); ret = ip_nat_helper_register(hlpr);
return 1;
} if (ret) {
ports_c++; printk
("ip_nat_irc: error registering helper for port %d\n",
ports[i]);
fini();
return 1;
} }
ports_c++;
} }
return ret; return ret;
} }
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#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_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>
......
/* The "unknown" protocol. This is what is used for protocols we /* The "unknown" protocol. This is what is used for protocols we
* don't understand. It's returned by find_proto(). * don't understand. It's returned by ip_ct_find_proto().
*/ */
#include <linux/types.h> #include <linux/types.h>
......
...@@ -106,8 +106,6 @@ static struct ipt_table nat_table ...@@ -106,8 +106,6 @@ static struct ipt_table nat_table
= { { NULL, NULL }, "nat", &nat_initial_table.repl, = { { NULL, NULL }, "nat", &nat_initial_table.repl,
NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE }; NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
LIST_HEAD(nat_expect_list);
/* Source NAT */ /* Source NAT */
static unsigned int ipt_snat_target(struct sk_buff **pskb, static unsigned int ipt_snat_target(struct sk_buff **pskb,
unsigned int hooknum, unsigned int hooknum,
...@@ -254,19 +252,6 @@ alloc_null_binding(struct ip_conntrack *conntrack, ...@@ -254,19 +252,6 @@ alloc_null_binding(struct ip_conntrack *conntrack,
return ip_nat_setup_info(conntrack, &mr, hooknum); return ip_nat_setup_info(conntrack, &mr, hooknum);
} }
static inline int call_expect(const struct ip_nat_expect *i,
struct sk_buff **pskb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info,
struct ip_conntrack *master,
struct ip_nat_info *masterinfo,
unsigned int *verdict)
{
return i->expect(pskb, hooknum, ct, info, master, masterinfo,
verdict);
}
int ip_nat_rule_find(struct sk_buff **pskb, int ip_nat_rule_find(struct sk_buff **pskb,
unsigned int hooknum, unsigned int hooknum,
const struct net_device *in, const struct net_device *in,
...@@ -276,19 +261,8 @@ int ip_nat_rule_find(struct sk_buff **pskb, ...@@ -276,19 +261,8 @@ int ip_nat_rule_find(struct sk_buff **pskb,
{ {
int ret; int ret;
/* Master won't vanish while this ctrack still alive */
if (ct->master.master) {
struct ip_conntrack *master;
master = (struct ip_conntrack *)ct->master.master;
if (LIST_FIND(&nat_expect_list,
call_expect,
struct ip_nat_expect *,
pskb, hooknum, ct, info,
master, &master->nat.info, &ret))
return ret;
}
ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL); ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL);
if (ret == NF_ACCEPT) { if (ret == NF_ACCEPT) {
if (!(info->initialized & (1 << HOOK2MANIP(hooknum)))) if (!(info->initialized & (1 << HOOK2MANIP(hooknum))))
/* NUL mapping */ /* NUL mapping */
...@@ -297,22 +271,6 @@ int ip_nat_rule_find(struct sk_buff **pskb, ...@@ -297,22 +271,6 @@ int ip_nat_rule_find(struct sk_buff **pskb,
return ret; return ret;
} }
int ip_nat_expect_register(struct ip_nat_expect *expect)
{
WRITE_LOCK(&ip_nat_lock);
list_prepend(&nat_expect_list, expect);
WRITE_UNLOCK(&ip_nat_lock);
return 0;
}
void ip_nat_expect_unregister(struct ip_nat_expect *expect)
{
WRITE_LOCK(&ip_nat_lock);
LIST_DELETE(&nat_expect_list, expect);
WRITE_UNLOCK(&ip_nat_lock);
}
static struct ipt_target ipt_snat_reg static struct ipt_target ipt_snat_reg
= { { NULL, NULL }, "SNAT", ipt_snat_target, ipt_snat_checkentry, NULL }; = { { NULL, NULL }, "SNAT", ipt_snat_target, ipt_snat_checkentry, NULL };
static struct ipt_target ipt_dnat_reg static struct ipt_target ipt_dnat_reg
......
...@@ -1244,6 +1244,7 @@ static int snmp_translate(struct ip_conntrack *ct, ...@@ -1244,6 +1244,7 @@ static int snmp_translate(struct ip_conntrack *ct,
* NAT helper function, packets arrive here from NAT code. * NAT helper function, packets arrive here from NAT code.
*/ */
static unsigned int nat_help(struct ip_conntrack *ct, static unsigned int nat_help(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info, struct ip_nat_info *info,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int hooknum, unsigned int hooknum,
...@@ -1304,19 +1305,27 @@ static unsigned int nat_help(struct ip_conntrack *ct, ...@@ -1304,19 +1305,27 @@ static unsigned int nat_help(struct ip_conntrack *ct,
return NF_DROP; return NF_DROP;
} }
static struct ip_nat_helper snmp = { { NULL, NULL }, static struct ip_nat_helper snmp = {
{ NULL, NULL },
"snmp",
IP_NAT_HELPER_F_STANDALONE,
THIS_MODULE,
{ { 0, { __constant_htons(SNMP_PORT) } }, { { 0, { __constant_htons(SNMP_PORT) } },
{ 0, { 0 }, IPPROTO_UDP } }, { 0, { 0 }, IPPROTO_UDP } },
{ { 0, { 0xFFFF } }, { { 0, { 0xFFFF } },
{ 0, { 0 }, 0xFFFF } }, { 0, { 0 }, 0xFFFF } },
nat_help, "snmp" }; nat_help, NULL };
static struct ip_nat_helper snmp_trap = { { NULL, NULL }, static struct ip_nat_helper snmp_trap = {
{ NULL, NULL },
"snmp_trap",
IP_NAT_HELPER_F_STANDALONE,
THIS_MODULE,
{ { 0, { __constant_htons(SNMP_TRAP_PORT) } }, { { 0, { __constant_htons(SNMP_TRAP_PORT) } },
{ 0, { 0 }, IPPROTO_UDP } }, { 0, { 0 }, IPPROTO_UDP } },
{ { 0, { 0xFFFF } }, { { 0, { 0xFFFF } },
{ 0, { 0 }, 0xFFFF } }, { 0, { 0 }, 0xFFFF } },
nat_help, "snmp_trap" }; nat_help, NULL };
/***************************************************************************** /*****************************************************************************
* *
......
...@@ -5,7 +5,12 @@ ...@@ -5,7 +5,12 @@
*/ */
/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
Public Licence. */ * Public Licence.
*
* 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
* - new API and handling of conntrack/nat helpers
* - now capable of multiple expectations for one master
* */
#include <linux/config.h> #include <linux/config.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -45,6 +50,15 @@ ...@@ -45,6 +50,15 @@
: ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \ : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \
: "*ERROR*"))) : "*ERROR*")))
static inline int call_expect(struct ip_conntrack *master,
struct sk_buff **pskb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info)
{
return master->nat.info.helper->expect(pskb, hooknum, ct, info);
}
static unsigned int static unsigned int
ip_nat_fn(unsigned int hooknum, ip_nat_fn(unsigned int hooknum,
struct sk_buff **pskb, struct sk_buff **pskb,
...@@ -111,8 +125,16 @@ ip_nat_fn(unsigned int hooknum, ...@@ -111,8 +125,16 @@ ip_nat_fn(unsigned int hooknum,
int in_hashes = info->initialized; int in_hashes = info->initialized;
unsigned int ret; unsigned int ret;
ret = ip_nat_rule_find(pskb, hooknum, in, out, if (ct->master
ct, info); && master_ct(ct)->nat.info.helper
&& master_ct(ct)->nat.info.helper->expect) {
ret = call_expect(master_ct(ct), pskb,
hooknum, ct, info);
} else {
ret = ip_nat_rule_find(pskb, hooknum, in, out,
ct, info);
}
if (ret != NF_ACCEPT) { if (ret != NF_ACCEPT) {
WRITE_UNLOCK(&ip_nat_lock); WRITE_UNLOCK(&ip_nat_lock);
return ret; return ret;
...@@ -335,11 +357,7 @@ EXPORT_SYMBOL(ip_nat_protocol_register); ...@@ -335,11 +357,7 @@ EXPORT_SYMBOL(ip_nat_protocol_register);
EXPORT_SYMBOL(ip_nat_protocol_unregister); EXPORT_SYMBOL(ip_nat_protocol_unregister);
EXPORT_SYMBOL(ip_nat_helper_register); EXPORT_SYMBOL(ip_nat_helper_register);
EXPORT_SYMBOL(ip_nat_helper_unregister); EXPORT_SYMBOL(ip_nat_helper_unregister);
EXPORT_SYMBOL(ip_nat_expect_register);
EXPORT_SYMBOL(ip_nat_expect_unregister);
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_seq_adjust);
EXPORT_SYMBOL(ip_nat_delete_sack);
EXPORT_SYMBOL(ip_nat_used_tuple); EXPORT_SYMBOL(ip_nat_used_tuple);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment