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 @@
#include <linux/config.h>
#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
#include <asm/atomic.h>
enum ip_conntrack_info
{
......@@ -62,27 +63,58 @@ do { \
#define IP_NF_ASSERT(x)
#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
{
/* Internal linked list */
/* Internal linked list (global expectation 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 */
struct ip_conntrack_tuple tuple, mask;
/* Function to call after setup and insertion */
int (*expectfn)(struct ip_conntrack *new);
/* The conntrack we are part of (set iff we're live) */
struct ip_conntrack *expectant;
};
/* At which sequence number did this expectation occur */
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
#include <linux/netfilter_ipv4/ip_nat.h>
union {
/* insert nat helper private data (expect) here */
} nat;
#endif
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
#include <linux/netfilter_ipv4/ip_conntrack_irc.h>
} help;
};
struct ip_conntrack
{
......@@ -101,10 +133,13 @@ struct ip_conntrack
/* If we're expecting another related connection, this will be
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 */
struct nf_ct_info master;
/* If we were expected by an expectation, this will be it */
struct ip_conntrack_expect *master;
/* Helper, if any. */
struct ip_conntrack_helper *helper;
......@@ -121,8 +156,9 @@ struct ip_conntrack
} proto;
union {
struct ip_ct_ftp ct_ftp_info;
struct ip_ct_irc ct_irc_info;
/* insert conntrack helper private data (master) here */
struct ip_ct_ftp_master ct_ftp_info;
struct ip_ct_irc_master ct_irc_info;
} help;
#ifdef CONFIG_IP_NF_NAT_NEEDED
......@@ -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,
return 0 and don't do alteration. */
extern int
......
......@@ -15,7 +15,7 @@ extern int ip_conntrack_init(void);
extern void ip_conntrack_cleanup(void);
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. */
extern struct ip_conntrack_protocol *__find_proto(u_int8_t protocol);
extern struct list_head protocol_list;
......
......@@ -11,6 +11,8 @@
/* Protects ftp part of conntracks */
DECLARE_LOCK_EXTERN(ip_ftp_lock);
#define FTP_PORT 21
enum ip_ct_ftp_type
{
/* PORT command from client */
......@@ -23,18 +25,20 @@ enum ip_ct_ftp_type
IP_CT_FTP_EPSV,
};
/* We record seq number and length of ftp ip/port text here: all in
host order. */
struct ip_ct_ftp
/* This structure is per expected connection */
struct ip_ct_ftp_expect
{
/* This tells NAT that this is an ftp connection */
int is_ftp;
u_int32_t seq;
/* 0 means not found yet */
u_int32_t len;
enum ip_ct_ftp_type ftptype;
/* Port that was to be used */
u_int16_t port;
/* We record seq number and length of ftp ip/port text here: all in
* host order. */
/* sequence number of IP address in packet is in ip_conntrack_expect */
u_int32_t len; /* length of IP address */
enum ip_ct_ftp_type ftptype; /* PORT or PASV ? */
u_int16_t port; /* TCP port that was to be used */
};
/* This structure exists only once per master */
struct ip_ct_ftp_master {
/* Next valid seq position for cmd matching after newline */
u_int32_t seq_aft_nl[IP_CT_DIR_MAX];
/* 0 means seq_match_aft_nl not set */
......
......@@ -5,10 +5,19 @@
struct module;
/* Reuse expectation when max_expected reached */
#define IP_CT_HELPER_F_REUSE_EXPECT 0x01
struct ip_conntrack_helper
{
/* Internal use. */
struct list_head list;
struct list_head list; /* Internal use. */
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) */
struct ip_conntrack_tuple tuple;
......@@ -24,11 +33,13 @@ struct ip_conntrack_helper
extern int ip_conntrack_helper_register(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,
const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack_tuple *mask,
int (*expectfn)(struct ip_conntrack *));
extern void ip_conntrack_unexpect_related(struct ip_conntrack *related_to);
struct ip_conntrack_expect *exp);
extern int ip_conntrack_change_expect(struct ip_conntrack_expect *expect,
struct ip_conntrack_tuple *newtuple);
extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp);
#endif /*_IP_CONNTRACK_HELPER_H*/
......@@ -20,7 +20,7 @@
#include <linux/netfilter_ipv4/lockhelp.h>
#define IP_CONNTR_IRC 2
#define IRC_PORT 6667
struct dccproto {
char* match;
......@@ -32,16 +32,18 @@ DECLARE_LOCK_EXTERN(ip_irc_lock);
/* We record seq number and length of irc ip/port text here: all in
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 */
int is_irc;
/* sequence number where address part of DCC command begins */
u_int32_t seq;
/* 0 means not found yet */
/* length of IP address */
u_int32_t len;
/* Port that was to be used */
u_int16_t port;
};
/* This structure exists only once per master */
struct ip_ct_irc_master {
};
#endif /* _IP_CONNTRACK_IRC_H */
......@@ -45,6 +45,10 @@ struct ip_conntrack_protocol
/* Called when a conntrack entry is destroyed */
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. */
struct module *me;
};
......
......@@ -6,10 +6,19 @@
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
{
/* Internal use */
struct list_head list;
struct list_head list; /* Internal use */
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 */
struct ip_conntrack_tuple tuple;
......@@ -17,12 +26,17 @@ struct ip_nat_helper
/* Helper function: returns verdict */
unsigned int (*help)(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
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;
......@@ -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,
struct ip_conntrack *ct,
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
......@@ -5,24 +5,7 @@
#include <linux/netfilter_ipv4/ip_nat.h>
#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 void ip_nat_rule_cleanup(void);
extern int ip_nat_rule_find(struct sk_buff **pskb,
......
......@@ -9,18 +9,18 @@
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.
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)
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
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
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
# connection tracking helpers
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
ifdef CONFIG_IP_NF_NAT_IRC
export-objs += ip_conntrack_irc.o
endif
# NAT helpers
obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
......
This diff is collapsed.
/* FTP extension for IP connection tracking. */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
......@@ -242,8 +243,10 @@ static int help(const struct iphdr *iph, size_t len,
u_int32_t array[6] = { 0 };
int dir = CTINFO2DIR(ctinfo);
unsigned int matchlen, matchoff;
struct ip_conntrack_tuple t, mask;
struct ip_ct_ftp *info = &ct->help.ct_ftp_info;
struct ip_ct_ftp_master *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;
int found = 0;
......@@ -271,8 +274,8 @@ static int help(const struct iphdr *iph, size_t len,
}
LOCK_BH(&ip_ftp_lock);
old_seq_aft_nl_set = info->seq_aft_nl_set[dir];
old_seq_aft_nl = info->seq_aft_nl[dir];
old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir];
old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir];
DEBUGP("conntrack_ftp: datalen %u\n", datalen);
if ((datalen > 0) && (data[datalen-1] == '\n')) {
......@@ -281,8 +284,9 @@ static int help(const struct iphdr *iph, size_t len,
|| after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) {
DEBUGP("conntrack_ftp: updating nl to %u\n",
ntohl(tcph->seq) + datalen);
info->seq_aft_nl[dir] = ntohl(tcph->seq) + datalen;
info->seq_aft_nl_set[dir] = 1;
ct_ftp_info->seq_aft_nl[dir] =
ntohl(tcph->seq) + datalen;
ct_ftp_info->seq_aft_nl_set[dir] = 1;
}
}
UNLOCK_BH(&ip_ftp_lock);
......@@ -331,15 +335,16 @@ static int help(const struct iphdr *iph, size_t len,
(int)matchlen, data + matchoff,
matchlen, ntohl(tcph->seq) + matchoff);
memset(&expect, 0, sizeof(expect));
/* Update the ftp info */
LOCK_BH(&ip_ftp_lock);
if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3])
== ct->tuplehash[dir].tuple.src.ip) {
info->is_ftp = 21;
info->seq = ntohl(tcph->seq) + matchoff;
info->len = matchlen;
info->ftptype = search[i].ftptype;
info->port = array[4] << 8 | array[5];
exp->seq = ntohl(tcph->seq) + matchoff;
exp_ftp_info->len = matchlen;
exp_ftp_info->ftptype = search[i].ftptype;
exp_ftp_info->port = array[4] << 8 | array[5];
} else {
/* Enrico Scholz's passive FTP to partially RNAT'd ftp
server: it really wants us to connect to a
......@@ -356,18 +361,21 @@ static int help(const struct iphdr *iph, size_t len,
if (!loose) goto out;
}
t = ((struct ip_conntrack_tuple)
exp->tuple = ((struct ip_conntrack_tuple)
{ { ct->tuplehash[!dir].tuple.src.ip,
{ 0 } },
{ htonl((array[0] << 24) | (array[1] << 16)
| (array[2] << 8) | array[3]),
{ htons(array[4] << 8 | array[5]) },
IPPROTO_TCP }});
mask = ((struct ip_conntrack_tuple)
exp->mask = ((struct ip_conntrack_tuple)
{ { 0xFFFFFFFF, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
exp->expectfn = NULL;
/* Ignore failure; should only happen with NAT */
ip_conntrack_expect_related(ct, &t, &mask, NULL);
ip_conntrack_expect_related(ct, &expect);
out:
UNLOCK_BH(&ip_ftp_lock);
......@@ -375,6 +383,7 @@ static int help(const struct iphdr *iph, size_t len,
}
static struct ip_conntrack_helper ftp[MAX_PORTS];
static char ftp_names[MAX_PORTS][10];
/* Not __exit: called from init() */
static void fini(void)
......@@ -390,9 +399,10 @@ static void fini(void)
static int __init init(void)
{
int i, ret;
char *tmpname;
if (ports[0] == 0)
ports[0] = 21;
ports[0] = FTP_PORT;
for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
memset(&ftp[i], 0, sizeof(struct ip_conntrack_helper));
......@@ -400,7 +410,19 @@ static int __init init(void)
ftp[i].tuple.dst.protonum = IPPROTO_TCP;
ftp[i].mask.src.u.tcp.port = 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;
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",
ports[i]);
ret = ip_conntrack_helper_register(&ftp[i]);
......@@ -414,10 +436,10 @@ static int __init init(void)
return 0;
}
#ifdef CONFIG_IP_NF_NAT_NEEDED
EXPORT_SYMBOL(ip_ftp_lock);
EXPORT_SYMBOL(ip_conntrack_ftp);
MODULE_LICENSE("GPL");
#endif
MODULE_LICENSE("GPL");
module_init(init);
module_exit(fini);
......@@ -11,12 +11,18 @@
**
* Module load syntax:
* 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.
* 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/netfilter.h>
#include <linux/ip.h>
......@@ -30,6 +36,8 @@
#define MAX_PORTS 8
static int ports[MAX_PORTS];
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_DESCRIPTION("IRC (DCC) connection tracking module");
......@@ -37,6 +45,10 @@ MODULE_LICENSE("GPL");
#ifdef MODULE_PARM
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
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
#define NUM_DCCPROTO 5
......@@ -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 datalen = tcplen - tcph->doff * 4;
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_int16_t dcc_port;
int i;
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");
/* Can't track connections formed before we registered */
if (!info)
return NF_ACCEPT;
/* If packet is coming from IRC server */
if (dir == IP_CT_DIR_REPLY)
......@@ -190,32 +194,36 @@ static int help(const struct iphdr *iph, size_t len,
continue;
}
memset(&expect, 0, sizeof(expect));
LOCK_BH(&ip_irc_lock);
/* save position of address in dcc string,
* neccessary for NAT */
info->is_irc = IP_CONNTR_IRC;
DEBUGP("tcph->seq = %u\n", tcph->seq);
info->seq = ntohl(tcph->seq) + (addr_beg_p - _data);
info->len = (addr_end_p - addr_beg_p);
info->port = dcc_port;
exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data);
exp_irc_info->len = (addr_end_p - addr_beg_p);
exp_irc_info->port = dcc_port;
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);
memset(&t, 0, sizeof(t));
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;
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 }});
exp->expectfn = NULL;
DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
NIPQUAD(t.src.ip),
ntohs(t.src.u.tcp.port),
NIPQUAD(t.dst.ip),
ntohs(t.dst.u.tcp.port));
NIPQUAD(exp->tuple.src.ip),
ntohs(exp->tuple.src.u.tcp.port),
NIPQUAD(exp->tuple.dst.ip),
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);
return NF_ACCEPT;
......@@ -226,29 +234,53 @@ static int help(const struct iphdr *iph, size_t len,
}
static struct ip_conntrack_helper irc_helpers[MAX_PORTS];
static char irc_names[MAX_PORTS][10];
static void fini(void);
static int __init init(void)
{
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 (ports[0] == 0)
ports[0] = 6667;
ports[0] = IRC_PORT;
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));
irc_helpers[i].tuple.src.u.tcp.port = htons(ports[i]);
irc_helpers[i].tuple.dst.protonum = IPPROTO_TCP;
irc_helpers[i].mask.src.u.tcp.port = 0xFFFF;
irc_helpers[i].mask.dst.protonum = 0xFFFF;
irc_helpers[i].help = help;
hlpr->tuple.src.u.tcp.port = htons(ports[i]);
hlpr->tuple.dst.protonum = IPPROTO_TCP;
hlpr->mask.src.u.tcp.port = 0xFFFF;
hlpr->mask.dst.protonum = 0xFFFF;
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]);
ret = ip_conntrack_helper_register(&irc_helpers[i]);
ret = ip_conntrack_helper_register(hlpr);
if (ret) {
printk("ip_conntrack_irc: ERROR registering port %d\n",
......@@ -273,5 +305,9 @@ static void fini(void)
}
}
#ifdef CONFIG_IP_NF_NAT_NEEDED
EXPORT_SYMBOL(ip_irc_lock);
#endif
module_init(init);
module_exit(fini);
......@@ -57,5 +57,5 @@ new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len)
struct ip_conntrack_protocol ip_conntrack_generic_protocol
= { { NULL, NULL }, 0, "unknown",
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,
struct ip_conntrack_protocol ip_conntrack_protocol_icmp
= { { NULL, NULL }, IPPROTO_ICMP, "icmp",
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 @@
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/tcp.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
#include <linux/netfilter_ipv4/lockhelp.h>
......@@ -227,7 +230,19 @@ static int tcp_new(struct ip_conntrack *conntrack,
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
= { { NULL, NULL }, IPPROTO_TCP, "tcp",
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,
struct ip_conntrack_protocol ip_conntrack_protocol_udp
= { { NULL, NULL }, IPPROTO_UDP, "udp",
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)
{
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);
len += print_tuple(buffer + len, &expect->tuple,
__find_proto(expect->tuple.dst.protonum));
......@@ -314,7 +320,7 @@ void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
{
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 */
LIST_DELETE(&protocol_list, proto);
WRITE_UNLOCK(&ip_conntrack_lock);
......@@ -353,8 +359,12 @@ EXPORT_SYMBOL(ip_conntrack_helper_register);
EXPORT_SYMBOL(ip_conntrack_helper_unregister);
EXPORT_SYMBOL(ip_ct_selective_cleanup);
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_change_expect);
EXPORT_SYMBOL(ip_conntrack_unexpect_related);
EXPORT_SYMBOL(ip_conntrack_tuple_taken);
EXPORT_SYMBOL(ip_ct_gather_frags);
EXPORT_SYMBOL(ip_conntrack_htable_size);
EXPORT_SYMBOL(ip_conntrack_lock);
......@@ -130,7 +130,7 @@ check_for_demasq(struct sk_buff **pskb)
struct ip_conntrack *ct;
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
they're part of an connection already established by an
......
......@@ -21,10 +21,14 @@
#define ASSERT_READ_LOCK(x) MUST_BE_READ_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_protocol.h>
#include <linux/netfilter_ipv4/ip_nat_core.h>
#include <linux/netfilter_ipv4/ip_nat_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/listhelp.h>
#if 0
......@@ -34,6 +38,7 @@
#endif
DECLARE_RWLOCK(ip_nat_lock);
DECLARE_RWLOCK_EXTERN(ip_conntrack_lock);
/* Calculated at init based on memory size */
static unsigned int ip_nat_htable_size;
......@@ -628,6 +633,7 @@ ip_nat_setup_info(struct ip_conntrack *conntrack,
}
/* If there's a helper, assign it; based on new tuple. */
if (!conntrack->master)
info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
&reply);
......@@ -724,6 +730,21 @@ manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len,
#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. */
unsigned int
do_bindings(struct ip_conntrack *ct,
......@@ -735,6 +756,7 @@ do_bindings(struct ip_conntrack *ct,
unsigned int i;
struct ip_nat_helper *helper;
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
conntrack (referenced) and helper (deleted with
......@@ -773,11 +795,66 @@ do_bindings(struct ip_conntrack *ct,
READ_UNLOCK(&ip_nat_lock);
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 */
IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
& __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
......
......@@ -28,38 +28,30 @@ DECLARE_LOCK_EXTERN(ip_ftp_lock);
/* FIXME: Time out? --RR */
static int
static unsigned int
ftp_nat_expected(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)
struct ip_nat_info *info)
{
struct ip_nat_multi_range mr;
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(master);
IP_NF_ASSERT(masterinfo);
IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
DEBUGP("nat_expected: We have a connection!\n");
/* Master must be an ftp connection */
ftpinfo = &master->help.ct_ftp_info;
exp_ftp_info = &ct->master->help.exp_ftp_info;
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
|| ftpinfo->ftptype == IP_CT_FTP_EPRT) {
if (exp_ftp_info->ftptype == IP_CT_FTP_PORT
|| exp_ftp_info->ftptype == IP_CT_FTP_EPRT) {
/* PORT command: make connection go to the client. */
newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
......@@ -92,11 +84,9 @@ ftp_nat_expected(struct sk_buff **pskb,
mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
mr.range[0].min = mr.range[0].max
= ((union ip_conntrack_manip_proto)
{ htons(ftpinfo->port) });
{ htons(exp_ftp_info->port) });
}
*verdict = ip_nat_setup_info(ct, &mr, hooknum);
return 1;
return ip_nat_setup_info(ct, &mr, hooknum);
}
static int
......@@ -176,27 +166,22 @@ static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
[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,
unsigned int datalen,
struct sk_buff **pskb,
enum ip_conntrack_info ctinfo)
enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *expect)
{
u_int32_t newip;
struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr *tcph = (void *)iph + iph->ihl*4;
u_int16_t port;
struct ip_conntrack_tuple tuple;
/* Don't care about source port */
const struct ip_conntrack_tuple mask
= { { 0xFFFFFFFF, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF } };
struct ip_conntrack_tuple newtuple;
memset(&tuple, 0, sizeof(tuple));
MUST_BE_LOCKED(&ip_ftp_lock);
DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n",
ct_ftp_info->seq, ct_ftp_info->len,
ntohl(tcph->seq), datalen);
DEBUGP("FTP_NAT: seq %u + %u in %u\n",
expect->seq, ct_ftp_info->len,
ntohl(tcph->seq));
/* Change address inside packet to match way we're mapping
this connection. */
......@@ -206,29 +191,34 @@ static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info,
is */
newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
/* Expect something from client->server */
tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
newtuple.src.ip =
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newtuple.dst.ip =
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
} else {
/* PORT command: must be where server thinks client is */
newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
/* Expect something from server->client */
tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
newtuple.src.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. */
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;
}
if (port == 0)
return 0;
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))
return 0;
......@@ -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,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
......@@ -245,13 +236,12 @@ static unsigned int help(struct ip_conntrack *ct,
struct tcphdr *tcph = (void *)iph + iph->ihl*4;
unsigned int datalen;
int dir;
int score;
struct ip_ct_ftp *ct_ftp_info
= &ct->help.ct_ftp_info;
struct ip_ct_ftp_expect *ct_ftp_info;
if (!exp)
DEBUGP("ip_nat_ftp: no exp!!");
/* Delete SACK_OK on initial TCP SYNs. */
if (tcph->syn && !tcph->ack)
ip_nat_delete_sack(*pskb, tcph);
ct_ftp_info = &exp->help.exp_ftp_info;
/* Only mangle things once: original direction in POST_ROUTING
and reply direction on PRE_ROUTING. */
......@@ -267,50 +257,34 @@ static unsigned int help(struct ip_conntrack *ct,
}
datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
score = 0;
LOCK_BH(&ip_ftp_lock);
if (ct_ftp_info->len) {
/* If it's in the right range... */
score += between(ct_ftp_info->seq, ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
score += between(ct_ftp_info->seq + ct_ftp_info->len,
if (between(exp->seq + ct_ftp_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
if (score == 1) {
ntohl(tcph->seq) + datalen)) {
if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) {
UNLOCK_BH(&ip_ftp_lock);
return NF_DROP;
}
} 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",
ct_ftp_info->seq, ct_ftp_info->len,
exp->seq, ct_ftp_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
}
UNLOCK_BH(&ip_ftp_lock);
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;
}
}
UNLOCK_BH(&ip_ftp_lock);
ip_nat_seq_adjust(*pskb, ct, ctinfo);
return NF_ACCEPT;
}
static struct ip_nat_helper ftp[MAX_PORTS];
static char ftp_names[MAX_PORTS][6];
static struct ip_nat_expect ftp_expect
= { { NULL, NULL }, ftp_nat_expected };
static char ftp_names[MAX_PORTS][10];
/* Not __exit: called from init() */
static void fini(void)
......@@ -321,19 +295,15 @@ static void fini(void)
DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
ip_nat_helper_unregister(&ftp[i]);
}
ip_nat_expect_unregister(&ftp_expect);
}
static int __init init(void)
{
int i, ret;
int i, ret = 0;
char *tmpname;
ret = ip_nat_expect_register(&ftp_expect);
if (ret == 0) {
if (ports[0] == 0)
ports[0] = 21;
ports[0] = FTP_PORT;
for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
......@@ -344,9 +314,15 @@ static int __init init(void)
ftp[i].mask.dst.protonum = 0xFFFF;
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];
sprintf(tmpname, "ftp%2.2d", i);
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",
......@@ -354,16 +330,14 @@ static int __init init(void)
ret = ip_nat_helper_register(&ftp[i]);
if (ret) {
printk("ip_nat_ftp: error registering helper for port %d\n", ports[i]);
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;
}
......
/* 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
*
* 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/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/skbuff.h>
......@@ -19,6 +26,8 @@
#define ASSERT_READ_LOCK(x) MUST_BE_READ_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_protocol.h>
#include <linux/netfilter_ipv4/ip_nat_core.h>
......@@ -199,6 +208,103 @@ ip_nat_mangle_tcp_packet(struct sk_buff **skb,
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 */
int
ip_nat_seq_adjust(struct sk_buff *skb,
......@@ -243,51 +349,9 @@ ip_nat_seq_adjust(struct sk_buff *skb,
tcph->seq = newseq;
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:
/* 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");
return 0;
}
static inline int
......@@ -297,10 +361,51 @@ helper_cmp(const struct ip_nat_helper *helper,
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 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);
if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple))
ret = -EBUSY;
......@@ -327,8 +432,14 @@ kill_helper(const struct ip_conntrack *i, void *helper)
void ip_nat_helper_unregister(struct ip_nat_helper *me)
{
int found = 0;
WRITE_LOCK(&ip_nat_lock);
/* 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);
/* 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)
worse. --RR */
ip_ct_selective_cleanup(kill_helper, me);
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);
/* FIXME: Time out? --RR */
static int
static unsigned int
irc_nat_expected(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)
struct ip_nat_info *info)
{
struct ip_nat_multi_range mr;
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(master);
IP_NF_ASSERT(masterinfo);
IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
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;
newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
DEBUGP("nat_expected: DCC cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
NIPQUAD(newsrcip), NIPQUAD(newdstip));
UNLOCK_BH(&ip_irc_lock);
if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
newip = newsrcip;
else
......@@ -99,16 +86,14 @@ irc_nat_expected(struct sk_buff **pskb,
mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
mr.range[0].min_ip = mr.range[0].max_ip = newip;
*verdict = ip_nat_setup_info(ct, &mr, hooknum);
return 1;
return ip_nat_setup_info(ct, &mr, hooknum);
}
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,
unsigned int datalen,
struct sk_buff **pskb,
enum ip_conntrack_info ctinfo)
enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *expect)
{
u_int32_t newip;
struct ip_conntrack_tuple t;
......@@ -121,9 +106,9 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info,
MUST_BE_LOCKED(&ip_irc_lock);
DEBUGP("IRC_NAT: info (seq %u + %u) packet(seq %u + %u)\n",
ct_irc_info->seq, ct_irc_info->len,
ntohl(tcph->seq), datalen);
DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n",
expect->seq, ct_irc_info->len,
ntohl(tcph->seq));
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,
only set in ip_conntrack_irc, with ip_irc_lock held
writable */
t = ct->expected.tuple;
t = expect->tuple;
t.dst.ip = newip;
for (port = ct_irc_info->port; port != 0; port++) {
t.dst.u.tcp.port = htons(port);
if (ip_conntrack_expect_related(ct, &t,
&ct->expected.mask,
NULL) == 0) {
if (ip_conntrack_change_expect(expect, &t) == 0) {
DEBUGP("using port %d", port);
break;
}
......@@ -166,26 +149,28 @@ static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info,
buffer, NIPQUAD(newip), port);
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,
strlen(buffer));
}
static unsigned int help(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info,
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 tcphdr *tcph = (void *) iph + iph->ihl * 4;
unsigned int datalen;
int dir;
int score;
struct ip_ct_irc *ct_irc_info = &ct->help.ct_irc_info;
struct ip_ct_irc_expect *ct_irc_info;
/* Delete SACK_OK on initial TCP SYNs. */
if (tcph->syn && !tcph->ack)
ip_nat_delete_sack(*pskb, tcph);
if (!exp)
DEBUGP("ip_nat_irc: no exp!!");
ct_irc_info = &exp->help.exp_irc_info;
/* Only mangle things once: original direction in POST_ROUTING
and reply direction on PRE_ROUTING. */
......@@ -202,55 +187,35 @@ static unsigned int help(struct ip_conntrack *ct,
DEBUGP("got beyond not touching\n");
datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
score = 0;
LOCK_BH(&ip_irc_lock);
if (ct_irc_info->len) {
DEBUGP("got beyond ct_irc_info->len\n");
/* If it's in the right range... */
score += between(ct_irc_info->seq, ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
score += between(ct_irc_info->seq + ct_irc_info->len,
/* Check wether the whole IP/address pattern is carried in the payload */
if (between(exp->seq + ct_irc_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
if (score == 1) {
ntohl(tcph->seq) + datalen)) {
if (!irc_data_fixup(ct_irc_info, ct, pskb, ctinfo, exp)) {
UNLOCK_BH(&ip_irc_lock);
return NF_DROP;
}
} 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",
ct_irc_info->seq, ct_irc_info->len,
exp->seq, ct_irc_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
}
UNLOCK_BH(&ip_irc_lock);
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;
}
}
UNLOCK_BH(&ip_irc_lock);
ip_nat_seq_adjust(*pskb, ct, ctinfo);
return NF_ACCEPT;
}
static struct ip_nat_helper ip_nat_irc_helpers[MAX_PORTS];
static char ip_nih_names[MAX_PORTS][6];
static struct ip_nat_expect irc_expect
= { {NULL, NULL}, irc_nat_expected };
static char irc_names[MAX_PORTS][10];
/* This function is intentionally _NOT_ defined as __exit, because
* it is needed by init() */
......@@ -263,20 +228,17 @@ static void fini(void)
ports[i]);
ip_nat_helper_unregister(&ip_nat_irc_helpers[i]);
}
ip_nat_expect_unregister(&irc_expect);
}
static int __init init(void)
{
int ret;
int ret = 0;
int i;
struct ip_nat_helper *hlpr;
char *tmpname;
ret = ip_nat_expect_register(&irc_expect);
if (ret == 0) {
if (ports[0] == 0) {
ports[0] = 6667;
ports[0] = IRC_PORT;
}
for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) {
......@@ -289,11 +251,17 @@ static int __init init(void)
hlpr->mask.src.u.tcp.port = 0xFFFF;
hlpr->mask.dst.protonum = 0xFFFF;
hlpr->help = help;
hlpr->flags = 0;
hlpr->me = THIS_MODULE;
hlpr->expect = irc_nat_expected;
tmpname = &ip_nih_names[i][0];
sprintf(tmpname, "irc%2.2d", i);
tmpname = &irc_names[i][0];
if (ports[i] == IRC_PORT)
sprintf(tmpname, "irc");
else
sprintf(tmpname, "irc-%d", i);
hlpr->name = tmpname;
DEBUGP
("ip_nat_irc: Trying to register helper for port %d: name %s\n",
ports[i], hlpr->name);
......@@ -308,7 +276,6 @@ static int __init init(void)
}
ports_c++;
}
}
return ret;
}
......
......@@ -4,7 +4,6 @@
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/if.h>
#include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter_ipv4/ip_nat_rule.h>
#include <linux/netfilter_ipv4/ip_nat_protocol.h>
......
/* 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>
......
......@@ -106,8 +106,6 @@ static struct ipt_table nat_table
= { { NULL, NULL }, "nat", &nat_initial_table.repl,
NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
LIST_HEAD(nat_expect_list);
/* Source NAT */
static unsigned int ipt_snat_target(struct sk_buff **pskb,
unsigned int hooknum,
......@@ -254,19 +252,6 @@ alloc_null_binding(struct ip_conntrack *conntrack,
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,
unsigned int hooknum,
const struct net_device *in,
......@@ -276,19 +261,8 @@ int ip_nat_rule_find(struct sk_buff **pskb,
{
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);
if (ret == NF_ACCEPT) {
if (!(info->initialized & (1 << HOOK2MANIP(hooknum))))
/* NUL mapping */
......@@ -297,22 +271,6 @@ int ip_nat_rule_find(struct sk_buff **pskb,
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
= { { NULL, NULL }, "SNAT", ipt_snat_target, ipt_snat_checkentry, NULL };
static struct ipt_target ipt_dnat_reg
......
......@@ -1244,6 +1244,7 @@ static int snmp_translate(struct ip_conntrack *ct,
* NAT helper function, packets arrive here from NAT code.
*/
static unsigned int nat_help(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
......@@ -1304,19 +1305,27 @@ static unsigned int nat_help(struct ip_conntrack *ct,
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, { 0 }, IPPROTO_UDP } },
{ { 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, { 0 }, IPPROTO_UDP } },
{ { 0, { 0xFFFF } },
{ 0, { 0 }, 0xFFFF } },
nat_help, "snmp_trap" };
nat_help, NULL };
/*****************************************************************************
*
......
......@@ -5,7 +5,12 @@
*/
/* (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/types.h>
......@@ -45,6 +50,15 @@
: ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \
: "*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
ip_nat_fn(unsigned int hooknum,
struct sk_buff **pskb,
......@@ -111,8 +125,16 @@ ip_nat_fn(unsigned int hooknum,
int in_hashes = info->initialized;
unsigned int ret;
if (ct->master
&& 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) {
WRITE_UNLOCK(&ip_nat_lock);
return ret;
......@@ -335,11 +357,7 @@ EXPORT_SYMBOL(ip_nat_protocol_register);
EXPORT_SYMBOL(ip_nat_protocol_unregister);
EXPORT_SYMBOL(ip_nat_helper_register);
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_mangle_tcp_packet);
EXPORT_SYMBOL(ip_nat_seq_adjust);
EXPORT_SYMBOL(ip_nat_delete_sack);
EXPORT_SYMBOL(ip_nat_used_tuple);
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