Commit 98fab1e4 authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NETFILTER_IPV4]: De-linearization of IP Connection Tracking.

This converts connection tracking and all the connection tracking
modules to handle non-linear skbs.  Enough interfaces have been
broken in the process that old helpers won't compile.

Interfaces which used to take a "void *data, int len" or
"struct iphdr *iph, int len" now take the skb itself (and an offset to
the data in the case of the first interface), which is not
linearized in any way (although Alexey says after ip_rcv the IP header
is always linear, so IPv4 netfilter hooks can always assume a linear
IP hdr).

Helpers which examine data (amanda, FTP, IRC) now copy it into a buffer
and examine that.
parent 0eb8fea5
...@@ -11,14 +11,6 @@ DECLARE_LOCK_EXTERN(ip_amanda_lock); ...@@ -11,14 +11,6 @@ DECLARE_LOCK_EXTERN(ip_amanda_lock);
#endif #endif
struct conn {
char* match;
int matchlen;
};
#define NUM_MSGS 3
struct ip_ct_amanda_expect struct ip_ct_amanda_expect
{ {
u_int16_t port; /* port number of this expectation */ u_int16_t port; /* port number of this expectation */
......
...@@ -24,9 +24,11 @@ extern struct list_head protocol_list; ...@@ -24,9 +24,11 @@ extern struct list_head protocol_list;
extern struct ip_conntrack *icmp_error_track(struct sk_buff *skb, extern struct ip_conntrack *icmp_error_track(struct sk_buff *skb,
enum ip_conntrack_info *ctinfo, enum ip_conntrack_info *ctinfo,
unsigned int hooknum); unsigned int hooknum);
extern int get_tuple(const struct iphdr *iph, size_t len, extern int get_tuple(const struct iphdr *iph,
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple, struct ip_conntrack_tuple *tuple,
struct ip_conntrack_protocol *protocol); const struct ip_conntrack_protocol *protocol);
/* Find a connection corresponding to a tuple. */ /* Find a connection corresponding to a tuple. */
struct ip_conntrack_tuple_hash * struct ip_conntrack_tuple_hash *
......
...@@ -25,7 +25,7 @@ struct ip_conntrack_helper ...@@ -25,7 +25,7 @@ struct ip_conntrack_helper
/* Function to call when data passes; return verdict, or -1 to /* Function to call when data passes; return verdict, or -1 to
invalidate. */ invalidate. */
int (*help)(const struct iphdr *, size_t len, int (*help)(struct sk_buff *skb,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info conntrackinfo); enum ip_conntrack_info conntrackinfo);
}; };
......
...@@ -37,11 +37,6 @@ struct ip_ct_irc_master { ...@@ -37,11 +37,6 @@ struct ip_ct_irc_master {
#define IRC_PORT 6667 #define IRC_PORT 6667
struct dccproto {
char* match;
int matchlen;
};
/* Protects irc part of conntracks */ /* Protects irc part of conntracks */
DECLARE_LOCK_EXTERN(ip_irc_lock); DECLARE_LOCK_EXTERN(ip_irc_lock);
......
...@@ -14,9 +14,11 @@ struct ip_conntrack_protocol ...@@ -14,9 +14,11 @@ struct ip_conntrack_protocol
/* Protocol name */ /* Protocol name */
const char *name; const char *name;
/* Try to fill in the third arg; return true if possible. */ /* Try to fill in the third arg: dataoff is offset past IP
int (*pkt_to_tuple)(const void *datah, size_t datalen, hdr. Return true if possible. */
struct ip_conntrack_tuple *tuple); int (*pkt_to_tuple)(const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple);
/* Invert the per-proto part of the tuple: ie. turn xmit into reply. /* Invert the per-proto part of the tuple: ie. turn xmit into reply.
* Some packets can't be inverted: return 0 in that case. * Some packets can't be inverted: return 0 in that case.
...@@ -34,20 +36,19 @@ struct ip_conntrack_protocol ...@@ -34,20 +36,19 @@ struct ip_conntrack_protocol
/* Returns verdict for packet, or -1 for invalid. */ /* Returns verdict for packet, or -1 for invalid. */
int (*packet)(struct ip_conntrack *conntrack, int (*packet)(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len, const struct sk_buff *skb,
enum ip_conntrack_info ctinfo); enum ip_conntrack_info ctinfo);
/* Called when a new connection for this protocol found; /* Called when a new connection for this protocol found;
* returns TRUE if it's OK. If so, packet() called next. */ * returns TRUE if it's OK. If so, packet() called next. */
int (*new)(struct ip_conntrack *conntrack, struct iphdr *iph, int (*new)(struct ip_conntrack *conntrack, const struct sk_buff *skb);
size_t len);
/* 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 */ /* Has to decide if a expectation matches one packet or not */
int (*exp_matches_pkt)(struct ip_conntrack_expect *exp, int (*exp_matches_pkt)(struct ip_conntrack_expect *exp,
struct sk_buff **pskb); const struct sk_buff *skb);
/* Module (if any) which this is connected to. */ /* Module (if any) which this is connected to. */
struct module *me; struct module *me;
......
...@@ -37,14 +37,8 @@ MODULE_PARM(master_timeout, "i"); ...@@ -37,14 +37,8 @@ MODULE_PARM(master_timeout, "i");
MODULE_PARM_DESC(master_timeout, "timeout for the master connection"); MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
DECLARE_LOCK(ip_amanda_lock); DECLARE_LOCK(ip_amanda_lock);
struct module *ip_conntrack_amanda = THIS_MODULE;
#define MAXMATCHLEN 6 char *conns[] = { "DATA ", "MESG ", "INDEX " };
struct conn conns[NUM_MSGS] = {
{"DATA ", 5},
{"MESG ", 5},
{"INDEX ", 6},
};
#if 0 #if 0
#define DEBUGP printk #define DEBUGP printk
...@@ -52,18 +46,15 @@ struct conn conns[NUM_MSGS] = { ...@@ -52,18 +46,15 @@ struct conn conns[NUM_MSGS] = {
#define DEBUGP(format, args...) #define DEBUGP(format, args...)
#endif #endif
/* This is slow, but it's simple. --RR */
static char amanda_buffer[65536];
/* FIXME: This should be in userspace. Later. */ static int help(struct sk_buff *skb,
static int help(const struct iphdr *iph, size_t len,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{ {
struct udphdr *udph = (void *)iph + iph->ihl * 4; char *data, *data_limit;
u_int32_t udplen = len - iph->ihl * 4;
u_int32_t datalen = udplen - sizeof(struct udphdr);
char *data = (char *)udph + sizeof(struct udphdr);
char *datap = data;
char *data_limit = (char *) data + datalen;
int dir = CTINFO2DIR(ctinfo); int dir = CTINFO2DIR(ctinfo);
unsigned int dataoff, i;
struct ip_ct_amanda *info = struct ip_ct_amanda *info =
(struct ip_ct_amanda *)&ct->help.ct_ftp_info; (struct ip_ct_amanda *)&ct->help.ct_ftp_info;
...@@ -79,121 +70,121 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -79,121 +70,121 @@ static int help(const struct iphdr *iph, size_t len,
if (dir == IP_CT_DIR_ORIGINAL) if (dir == IP_CT_DIR_ORIGINAL)
return NF_ACCEPT; return NF_ACCEPT;
/* Not whole UDP header? */ /* No data? */
if (udplen < sizeof(struct udphdr)) { dataoff = skb->nh.iph->ihl*4 + sizeof(struct udphdr);
printk("ip_conntrack_amanda_help: udplen = %u\n", if (dataoff >= skb->len) {
(unsigned)udplen); if (net_ratelimit())
printk("ip_conntrack_amanda_help: skblen = %u\n",
(unsigned)skb->len);
return NF_ACCEPT; return NF_ACCEPT;
} }
/* Checksum invalid? Ignore. */ LOCK_BH(&ip_amanda_lock);
if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, skb_copy_bits(skb, dataoff, amanda_buffer, skb->len - dataoff);
csum_partial((char *)udph, udplen, 0))) { data = amanda_buffer;
DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u " data_limit = amanda_buffer + skb->len - dataoff;
"%u.%u.%u.%u\n", *data_limit = '\0';
udph, udplen, NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
return NF_ACCEPT;
}
/* Search for the CONNECT string */ /* Search for the CONNECT string */
while (data < data_limit) { data = strstr(data, "CONNECT ");
if (!memcmp(data, "CONNECT ", 8)) { if (!data)
break; goto out;
}
data++;
}
if (memcmp(data, "CONNECT ", 8))
return NF_ACCEPT;
DEBUGP("ip_conntrack_amanda_help: CONNECT found in connection " DEBUGP("ip_conntrack_amanda_help: CONNECT found in connection "
"%u.%u.%u.%u:%u %u.%u.%u.%u:%u\n", "%u.%u.%u.%u:%u %u.%u.%u.%u:%u\n",
NIPQUAD(iph->saddr), htons(udph->source), NIPQUAD(iph->saddr), htons(udph->source),
NIPQUAD(iph->daddr), htons(udph->dest)); NIPQUAD(iph->daddr), htons(udph->dest));
data += 8; data += strlen("CONNECT ");
while (*data != 0x0a && data < data_limit) {
/* Only search first line. */
int i; if (strchr(data, '\n'))
*strchr(data, '\n') = '\0';
for (i = 0; i < NUM_MSGS; i++) {
if (!memcmp(data, conns[i].match, for (i = 0; i < ARRAY_SIZE(conns); i++) {
conns[i].matchlen)) { char *match = strstr(data, conns[i]);
if (match) {
char *portchr; char *portchr;
struct ip_conntrack_expect expect; struct ip_conntrack_expect expect;
struct ip_ct_amanda_expect struct ip_ct_amanda_expect *exp_amanda_info =
*exp_amanda_info = &expect.help.exp_amanda_info;
&expect.help.exp_amanda_info;
memset(&expect, 0, sizeof(expect));
memset(&expect, 0, sizeof(expect));
data += strlen(conns[i]);
data += conns[i].matchlen; /* this is not really tcp, but let's steal an
/* this is not really tcp, but let's steal an * idea from a tcp stream helper :-) */
* idea from a tcp stream helper :-) // XXX expect.seq = data - amanda_buffer;
exp_amanda_info->offset = data - amanda_buffer;
// XXX DEBUGP("expect.seq = %p - %p = %d\n", data, amanda_buffer, expect.seq);
DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, amanda_buffer, exp_amanda_info->offset);
portchr = data;
exp_amanda_info->port = simple_strtoul(data, &data,10);
exp_amanda_info->len = data - portchr;
/* eat whitespace */
while (*data == ' ')
data++;
DEBUGP("ip_conntrack_amanda_help: "
"CONNECT %s request with port "
"%u found\n", conns[i],
exp_amanda_info->port);
expect.tuple = ((struct ip_conntrack_tuple)
{ { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
{ 0 } },
{ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
{ htons(exp_amanda_info->port) },
IPPROTO_TCP }});
expect.mask = ((struct ip_conntrack_tuple)
{ { 0, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
expect.expectfn = NULL;
DEBUGP ("ip_conntrack_amanda_help: "
"expect_related: %u.%u.%u.%u:%u - "
"%u.%u.%u.%u:%u\n",
NIPQUAD(expect.tuple.src.ip),
ntohs(expect.tuple.src.u.tcp.port),
NIPQUAD(expect.tuple.dst.ip),
ntohs(expect.tuple.dst.u.tcp.port));
if (ip_conntrack_expect_related(ct, &expect)
== -EEXIST) {
;
/* this must be a packet being resent */
/* XXX - how do I get the
* ip_conntrack_expect that
* already exists so that I can
* update the .seq so that the
* nat module rewrites the port
* numbers?
* Perhaps I should use the
* exp_amanda_info instead of
* .seq.
*/ */
// XXX expect.seq = data - datap; }
exp_amanda_info->offset = data - datap; }
// XXX DEBUGP("expect.seq = %p - %p = %d\n", data, datap, expect.seq); }
DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, datap, exp_amanda_info->offset); out:
portchr = data; UNLOCK_BH(&ip_amanda_lock);
exp_amanda_info->port =
simple_strtoul(data, &data, 10);
exp_amanda_info->len = data - portchr;
/* eat whitespace */
while (*data == ' ')
data++;
DEBUGP ("ip_conntrack_amanda_help: "
"CONNECT %s request with port "
"%u found\n", conns[i].match,
exp_amanda_info->port);
LOCK_BH(&ip_amanda_lock);
expect.tuple = ((struct ip_conntrack_tuple)
{ { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
{ 0 } },
{ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
{ htons(exp_amanda_info->port) },
IPPROTO_TCP }});
expect.mask = ((struct ip_conntrack_tuple)
{ { 0, { 0 } },
{ 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
expect.expectfn = NULL;
DEBUGP ("ip_conntrack_amanda_help: "
"expect_related: %u.%u.%u.%u:%u - "
"%u.%u.%u.%u:%u\n",
NIPQUAD(expect.tuple.src.ip),
ntohs(expect.tuple.src.u.tcp.port),
NIPQUAD(expect.tuple.dst.ip),
ntohs(expect.tuple.dst.u.tcp.port));
if (ip_conntrack_expect_related(ct, &expect) ==
-EEXIST) {
;
/* this must be a packet being resent */
/* XXX - how do I get the
* ip_conntrack_expect that
* already exists so that I can
* update the .seq so that the
* nat module rewrites the port
* numbers?
* Perhaps I should use the
* exp_amanda_info instead of
* .seq.
*/
}
UNLOCK_BH(&ip_amanda_lock);
} /* if memcmp(conns) */
} /* for .. NUM_MSGS */
data++;
} /* while (*data != 0x0a && data < data_limit) */
return NF_ACCEPT; return NF_ACCEPT;
} }
static struct ip_conntrack_helper amanda_helper; static struct ip_conntrack_helper amanda_helper = {
.max_expected = ARRAY_SIZE(conns),
.timeout = 180,
.flags = IP_CT_HELPER_F_REUSE_EXPECT,
.me = THIS_MODULE,
.help = help,
.name = "amanda",
.tuple = { .src = { .u = { __constant_htons(10080) } },
.dst = { .protonum = IPPROTO_UDP },
},
.mask = { .src = { .u = { 0xFFFF } },
.dst = { .protonum = 0xFFFF },
},
};
static void fini(void) static void fini(void)
{ {
...@@ -205,20 +196,7 @@ static int __init init(void) ...@@ -205,20 +196,7 @@ static int __init init(void)
{ {
int ret; int ret;
memset(&amanda_helper, 0, sizeof(struct ip_conntrack_helper));
amanda_helper.tuple.src.u.udp.port = htons(10080);
amanda_helper.tuple.dst.protonum = IPPROTO_UDP;
amanda_helper.mask.src.u.udp.port = 0xFFFF;
amanda_helper.mask.dst.protonum = 0xFFFF;
amanda_helper.max_expected = NUM_MSGS;
amanda_helper.timeout = 180;
amanda_helper.flags = IP_CT_HELPER_F_REUSE_EXPECT;
amanda_helper.me = ip_conntrack_amanda;
amanda_helper.help = help;
amanda_helper.name = "amanda";
DEBUGP("ip_ct_amanda: registering helper for port 10080\n"); DEBUGP("ip_ct_amanda: registering helper for port 10080\n");
ret = ip_conntrack_helper_register(&amanda_helper); ret = ip_conntrack_helper_register(&amanda_helper);
if (ret) { if (ret) {
......
...@@ -120,31 +120,25 @@ hash_conntrack(const struct ip_conntrack_tuple *tuple) ...@@ -120,31 +120,25 @@ hash_conntrack(const struct ip_conntrack_tuple *tuple)
% ip_conntrack_htable_size; % ip_conntrack_htable_size;
} }
inline int int
get_tuple(const struct iphdr *iph, size_t len, get_tuple(const struct iphdr *iph,
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple, struct ip_conntrack_tuple *tuple,
struct ip_conntrack_protocol *protocol) const struct ip_conntrack_protocol *protocol)
{ {
int ret;
/* Never happen */ /* Never happen */
if (iph->frag_off & htons(IP_OFFSET)) { if (iph->frag_off & htons(IP_OFFSET)) {
printk("ip_conntrack_core: Frag of proto %u.\n", printk("ip_conntrack_core: Frag of proto %u.\n",
iph->protocol); iph->protocol);
return 0; return 0;
} }
/* Guarantee 8 protocol bytes: if more wanted, use len param */
else if (iph->ihl * 4 + 8 > len)
return 0;
tuple->src.ip = iph->saddr; tuple->src.ip = iph->saddr;
tuple->dst.ip = iph->daddr; tuple->dst.ip = iph->daddr;
tuple->dst.protonum = iph->protocol; tuple->dst.protonum = iph->protocol;
ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl, return protocol->pkt_to_tuple(skb, dataoff, tuple);
len - 4*iph->ihl,
tuple);
return ret;
} }
static int static int
...@@ -496,54 +490,40 @@ icmp_error_track(struct sk_buff *skb, ...@@ -496,54 +490,40 @@ icmp_error_track(struct sk_buff *skb,
enum ip_conntrack_info *ctinfo, enum ip_conntrack_info *ctinfo,
unsigned int hooknum) unsigned int hooknum)
{ {
const struct iphdr *iph;
struct icmphdr *hdr;
struct ip_conntrack_tuple innertuple, origtuple; struct ip_conntrack_tuple innertuple, origtuple;
struct iphdr *inner; struct {
size_t datalen; struct icmphdr icmp;
struct iphdr ip;
} inside;
struct ip_conntrack_protocol *innerproto; struct ip_conntrack_protocol *innerproto;
struct ip_conntrack_tuple_hash *h; struct ip_conntrack_tuple_hash *h;
int dataoff;
IP_NF_ASSERT(iph->protocol == IPPROTO_ICMP);
IP_NF_ASSERT(skb->nfct == NULL); IP_NF_ASSERT(skb->nfct == NULL);
iph = skb->nh.iph; /* Not enough header? */
hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &inside, sizeof(inside))!=0)
inner = (struct iphdr *)(hdr + 1);
datalen = skb->len - iph->ihl*4 - sizeof(*hdr);
if (skb->len < iph->ihl * 4 + sizeof(*hdr) + sizeof(*iph)) {
DEBUGP("icmp_error_track: too short\n");
return NULL; return NULL;
}
if (hdr->type != ICMP_DEST_UNREACH if (inside.icmp.type != ICMP_DEST_UNREACH
&& hdr->type != ICMP_SOURCE_QUENCH && inside.icmp.type != ICMP_SOURCE_QUENCH
&& hdr->type != ICMP_TIME_EXCEEDED && inside.icmp.type != ICMP_TIME_EXCEEDED
&& hdr->type != ICMP_PARAMETERPROB && inside.icmp.type != ICMP_PARAMETERPROB
&& hdr->type != ICMP_REDIRECT) && inside.icmp.type != ICMP_REDIRECT)
return NULL; return NULL;
/* Ignore ICMP's containing fragments (shouldn't happen) */ /* Ignore ICMP's containing fragments (shouldn't happen) */
if (inner->frag_off & htons(IP_OFFSET)) { if (inside.ip.frag_off & htons(IP_OFFSET)) {
DEBUGP("icmp_error_track: fragment of proto %u\n", DEBUGP("icmp_error_track: fragment of proto %u\n",
inner->protocol); inside.ip.protocol);
return NULL;
}
/* Ignore it if the checksum's bogus. */
if (ip_compute_csum((unsigned char *)hdr, sizeof(*hdr) + datalen)) {
DEBUGP("icmp_error_track: bad csum\n");
return NULL; return NULL;
} }
innerproto = ip_ct_find_proto(inner->protocol); innerproto = ip_ct_find_proto(inside.ip.protocol);
dataoff = skb->nh.iph->ihl*4 + sizeof(inside.icmp) + inside.ip.ihl*4;
/* Are they talking about one of our connections? */ /* Are they talking about one of our connections? */
if (inner->ihl * 4 + 8 > datalen if (!get_tuple(&inside.ip, skb, dataoff, &origtuple, innerproto)) {
|| !get_tuple(inner, datalen, &origtuple, innerproto)) { DEBUGP("icmp_error: ! get_tuple p=%u", inside.ip.protocol);
DEBUGP("icmp_error: ! get_tuple p=%u (%u*4+%u dlen=%u)\n",
inner->protocol, inner->ihl, 8,
datalen);
return NULL; return NULL;
} }
...@@ -679,7 +659,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple, ...@@ -679,7 +659,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
for (i=0; i < IP_CT_NUMBER; i++) for (i=0; i < IP_CT_NUMBER; i++)
conntrack->infos[i].master = &conntrack->ct_general; conntrack->infos[i].master = &conntrack->ct_general;
if (!protocol->new(conntrack, skb->nh.iph, skb->len)) { if (!protocol->new(conntrack, skb)) {
kmem_cache_free(ip_conntrack_cachep, conntrack); kmem_cache_free(ip_conntrack_cachep, conntrack);
return NULL; return NULL;
} }
...@@ -748,7 +728,7 @@ resolve_normal_ct(struct sk_buff *skb, ...@@ -748,7 +728,7 @@ resolve_normal_ct(struct sk_buff *skb,
IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0); IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto)) if (!get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4, &tuple, proto))
return NULL; return NULL;
/* look for tuple match */ /* look for tuple match */
...@@ -823,10 +803,6 @@ unsigned int ip_conntrack_in(unsigned int hooknum, ...@@ -823,10 +803,6 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
if ((*pskb)->nfct) if ((*pskb)->nfct)
return NF_ACCEPT; return NF_ACCEPT;
/* FIXME: Push down to extensions --RR */
if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
return NF_DROP;
/* Gather fragments. */ /* Gather fragments. */
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
*pskb = ip_ct_gather_frags(*pskb); *pskb = ip_ct_gather_frags(*pskb);
...@@ -851,7 +827,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum, ...@@ -851,7 +827,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
IP_NF_ASSERT((*pskb)->nfct); IP_NF_ASSERT((*pskb)->nfct);
ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo); ret = proto->packet(ct, *pskb, ctinfo);
if (ret == -1) { if (ret == -1) {
/* Invalid */ /* Invalid */
nf_conntrack_put((*pskb)->nfct); nf_conntrack_put((*pskb)->nfct);
...@@ -860,8 +836,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum, ...@@ -860,8 +836,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
} }
if (ret != NF_DROP && ct->helper) { if (ret != NF_DROP && ct->helper) {
ret = ct->helper->help((*pskb)->nh.iph, (*pskb)->len, ret = ct->helper->help(*pskb, ct, ctinfo);
ct, ctinfo);
if (ret == -1) { if (ret == -1) {
/* Invalid */ /* Invalid */
nf_conntrack_put((*pskb)->nfct); nf_conntrack_put((*pskb)->nfct);
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
#include <linux/netfilter_ipv4/ip_conntrack_helper.h> #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h> #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
/* This is slow, but it's simple. --RR */
static char ftp_buffer[65536];
DECLARE_LOCK(ip_ftp_lock); DECLARE_LOCK(ip_ftp_lock);
struct module *ip_conntrack_ftp = THIS_MODULE; struct module *ip_conntrack_ftp = THIS_MODULE;
...@@ -228,18 +231,14 @@ static int find_pattern(const char *data, size_t dlen, ...@@ -228,18 +231,14 @@ static int find_pattern(const char *data, size_t dlen,
return 1; return 1;
} }
/* FIXME: This should be in userspace. Later. */ static int help(struct sk_buff *skb,
static int help(const struct iphdr *iph, size_t len,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo)
{ {
/* tcplen not negative guaranteed by ip_conntrack_tcp.c */ unsigned int dataoff, datalen;
struct tcphdr *tcph = (void *)iph + iph->ihl * 4; struct tcphdr tcph;
const char *data = (const char *)tcph + tcph->doff * 4;
unsigned int tcplen = len - iph->ihl * 4;
unsigned int datalen = tcplen - tcph->doff * 4;
u_int32_t old_seq_aft_nl; u_int32_t old_seq_aft_nl;
int old_seq_aft_nl_set; int old_seq_aft_nl_set, ret;
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;
...@@ -257,45 +256,42 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -257,45 +256,42 @@ static int help(const struct iphdr *iph, size_t len,
return NF_ACCEPT; return NF_ACCEPT;
} }
/* Not whole TCP header? */ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
DEBUGP("ftp: tcplen = %u\n", (unsigned)tcplen);
return NF_ACCEPT; return NF_ACCEPT;
}
/* Checksum invalid? Ignore. */ dataoff = skb->nh.iph->ihl*4 + tcph.doff*4;
/* FIXME: Source route IP option packets --RR */ /* No data? */
if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, if (dataoff >= skb->len) {
csum_partial((char *)tcph, tcplen, 0))) { DEBUGP("ftp: skblen = %u\n", skb->len);
DEBUGP("ftp_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
tcph, tcplen, NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
return NF_ACCEPT; return NF_ACCEPT;
} }
datalen = skb->len - dataoff;
LOCK_BH(&ip_ftp_lock); LOCK_BH(&ip_ftp_lock);
skb_copy_bits(skb, dataoff, ftp_buffer, skb->len - dataoff);
old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[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]; 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 (ftp_buffer[datalen - 1] == '\n') {
DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen); DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen);
if (!old_seq_aft_nl_set if (!old_seq_aft_nl_set
|| 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);
ct_ftp_info->seq_aft_nl[dir] = ct_ftp_info->seq_aft_nl[dir] =
ntohl(tcph->seq) + datalen; ntohl(tcph.seq) + datalen;
ct_ftp_info->seq_aft_nl_set[dir] = 1; ct_ftp_info->seq_aft_nl_set[dir] = 1;
} }
} }
UNLOCK_BH(&ip_ftp_lock);
if(!old_seq_aft_nl_set || if(!old_seq_aft_nl_set ||
(ntohl(tcph->seq) != old_seq_aft_nl)) { (ntohl(tcph.seq) != old_seq_aft_nl)) {
DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u)\n", DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u)\n",
old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl); old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
return NF_ACCEPT; ret = NF_ACCEPT;
goto out;
} }
/* Initialize IP array to expected address (it's not mentioned /* Initialize IP array to expected address (it's not mentioned
...@@ -308,7 +304,7 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -308,7 +304,7 @@ static int help(const struct iphdr *iph, size_t len,
for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) { for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) {
if (search[i].dir != dir) continue; if (search[i].dir != dir) continue;
found = find_pattern(data, datalen, found = find_pattern(ftp_buffer, skb->len - dataoff,
search[i].pattern, search[i].pattern,
search[i].plen, search[i].plen,
search[i].skip, search[i].skip,
...@@ -326,22 +322,24 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -326,22 +322,24 @@ static int help(const struct iphdr *iph, size_t len,
if (net_ratelimit()) if (net_ratelimit())
printk("conntrack_ftp: partial %s %u+%u\n", printk("conntrack_ftp: partial %s %u+%u\n",
search[i].pattern, search[i].pattern,
ntohl(tcph->seq), datalen); ntohl(tcph.seq), datalen);
return NF_DROP; ret = NF_DROP;
} else if (found == 0) /* No match */ goto out;
return NF_ACCEPT; } else if (found == 0) { /* No match */
ret = NF_ACCEPT;
goto out;
}
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)); memset(&expect, 0, sizeof(expect));
/* Update the ftp info */ /* Update the ftp info */
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) {
exp->seq = ntohl(tcph->seq) + matchoff; exp->seq = ntohl(tcph.seq) + matchoff;
exp_ftp_info->len = matchlen; exp_ftp_info->len = matchlen;
exp_ftp_info->ftptype = search[i].ftptype; exp_ftp_info->ftptype = search[i].ftptype;
exp_ftp_info->port = array[4] << 8 | array[5]; exp_ftp_info->port = array[4] << 8 | array[5];
...@@ -358,7 +356,10 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -358,7 +356,10 @@ static int help(const struct iphdr *iph, size_t len,
<lincoln@cesar.org.br> for reporting this potential <lincoln@cesar.org.br> for reporting this potential
problem (DMZ machines opening holes to internal problem (DMZ machines opening holes to internal
networks, or the packet filter itself). */ networks, or the packet filter itself). */
if (!loose) goto out; if (!loose) {
ret = NF_ACCEPT;
goto out;
}
} }
exp->tuple = ((struct ip_conntrack_tuple) exp->tuple = ((struct ip_conntrack_tuple)
...@@ -376,10 +377,10 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -376,10 +377,10 @@ static int help(const struct iphdr *iph, size_t len,
/* Ignore failure; should only happen with NAT */ /* Ignore failure; should only happen with NAT */
ip_conntrack_expect_related(ct, &expect); ip_conntrack_expect_related(ct, &expect);
ret = NF_ACCEPT;
out: out:
UNLOCK_BH(&ip_ftp_lock); UNLOCK_BH(&ip_ftp_lock);
return ret;
return NF_ACCEPT;
} }
static struct ip_conntrack_helper ftp[MAX_PORTS]; static struct ip_conntrack_helper ftp[MAX_PORTS];
......
...@@ -38,6 +38,8 @@ static int ports[MAX_PORTS]; ...@@ -38,6 +38,8 @@ static int ports[MAX_PORTS];
static int ports_c = 0; static int ports_c = 0;
static int max_dcc_channels = 8; static int max_dcc_channels = 8;
static unsigned int dcc_timeout = 300; static unsigned int dcc_timeout = 300;
/* This is slow, but it's simple. --RR */
static char irc_buffer[65536];
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");
...@@ -51,14 +53,7 @@ MODULE_PARM(dcc_timeout, "i"); ...@@ -51,14 +53,7 @@ MODULE_PARM(dcc_timeout, "i");
MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels"); MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
#endif #endif
#define NUM_DCCPROTO 5 static char *dccprotos[] = { "SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT " };
struct dccproto dccprotos[NUM_DCCPROTO] = {
{"SEND ", 5},
{"CHAT ", 5},
{"MOVE ", 5},
{"TSEND ", 6},
{"SCHAT ", 6}
};
#define MAXMATCHLEN 6 #define MAXMATCHLEN 6
DECLARE_LOCK(ip_irc_lock); DECLARE_LOCK(ip_irc_lock);
...@@ -102,18 +97,12 @@ int parse_dcc(char *data, char *data_end, u_int32_t * ip, u_int16_t * port, ...@@ -102,18 +97,12 @@ int parse_dcc(char *data, char *data_end, u_int32_t * ip, u_int16_t * port,
return 0; return 0;
} }
static int help(struct sk_buff *skb,
/* FIXME: This should be in userspace. Later. */
static int help(const struct iphdr *iph, size_t len,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{ {
/* tcplen not negative guaranteed by ip_conntrack_tcp.c */ unsigned int dataoff;
struct tcphdr *tcph = (void *) iph + iph->ihl * 4; struct tcphdr tcph;
const char *data = (const char *) tcph + tcph->doff * 4; char *data, *data_limit;
const char *_data = data;
char *data_limit;
u_int32_t tcplen = len - iph->ihl * 4;
u_int32_t datalen = tcplen - tcph->doff * 4;
int dir = CTINFO2DIR(ctinfo); int dir = CTINFO2DIR(ctinfo);
struct ip_conntrack_expect expect, *exp = &expect; struct ip_conntrack_expect expect, *exp = &expect;
struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info; struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info;
...@@ -136,23 +125,20 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -136,23 +125,20 @@ static int help(const struct iphdr *iph, size_t len,
return NF_ACCEPT; return NF_ACCEPT;
} }
/* Not whole TCP header? */ /* Not a full tcp header? */
if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
DEBUGP("tcplen = %u\n", (unsigned) tcplen);
return NF_ACCEPT; return NF_ACCEPT;
}
/* Checksum invalid? Ignore. */ /* No data? */
/* FIXME: Source route IP option packets --RR */ dataoff = skb->nh.iph->ihl*4 + tcph.doff*4;
if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, if (dataoff >= skb->len)
csum_partial((char *) tcph, tcplen, 0))) {
DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
tcph, tcplen, NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
return NF_ACCEPT; return NF_ACCEPT;
}
data_limit = (char *) data + datalen; LOCK_BH(&ip_irc_lock);
skb_copy_bits(skb, dataoff, irc_buffer, skb->len - dataoff);
data = irc_buffer;
data_limit = irc_buffer + skb->len - dataoff;
while (data < (data_limit - (22 + MAXMATCHLEN))) { while (data < (data_limit - (22 + MAXMATCHLEN))) {
if (memcmp(data, "\1DCC ", 5)) { if (memcmp(data, "\1DCC ", 5)) {
data++; data++;
...@@ -162,19 +148,18 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -162,19 +148,18 @@ static int help(const struct iphdr *iph, size_t len,
data += 5; data += 5;
DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n", DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n",
NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->saddr), ntohs(tcph.source),
NIPQUAD(iph->daddr), ntohs(tcph->dest)); NIPQUAD(iph->daddr), ntohs(tcph.dest));
for (i = 0; i < NUM_DCCPROTO; i++) { for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
if (memcmp(data, dccprotos[i].match, if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) {
dccprotos[i].matchlen)) {
/* no match */ /* no match */
continue; continue;
} }
DEBUGP("DCC %s detected\n", dccprotos[i].match); DEBUGP("DCC %s detected\n", dccprotos[i]);
data += dccprotos[i].matchlen; data += strlen(dccprotos[i]);
if (parse_dcc((char *) data, data_limit, &dcc_ip, if (parse_dcc((char *)data, data_limit, &dcc_ip,
&dcc_port, &addr_beg_p, &addr_end_p)) { &dcc_port, &addr_beg_p, &addr_end_p)) {
/* unable to parse */ /* unable to parse */
DEBUGP("unable to parse dcc command\n"); DEBUGP("unable to parse dcc command\n");
...@@ -196,12 +181,10 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -196,12 +181,10 @@ static int help(const struct iphdr *iph, size_t len,
memset(&expect, 0, sizeof(expect)); memset(&expect, 0, sizeof(expect));
LOCK_BH(&ip_irc_lock);
/* save position of address in dcc string, /* save position of address in dcc string,
* necessary for NAT */ * necessary for NAT */
DEBUGP("tcph->seq = %u\n", tcph->seq); DEBUGP("tcph->seq = %u\n", tcph.seq);
exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data); exp->seq = ntohl(tcph.seq) + (addr_beg_p - irc_buffer);
exp_irc_info->len = (addr_end_p - addr_beg_p); exp_irc_info->len = (addr_end_p - addr_beg_p);
exp_irc_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",
...@@ -224,12 +207,13 @@ static int help(const struct iphdr *iph, size_t len, ...@@ -224,12 +207,13 @@ static int help(const struct iphdr *iph, size_t len,
ntohs(exp->tuple.dst.u.tcp.port)); ntohs(exp->tuple.dst.u.tcp.port));
ip_conntrack_expect_related(ct, &expect); ip_conntrack_expect_related(ct, &expect);
UNLOCK_BH(&ip_irc_lock);
return NF_ACCEPT; goto out;
} /* for .. NUM_DCCPROTO */ } /* for .. NUM_DCCPROTO */
} /* while data < ... */ } /* while data < ... */
out:
UNLOCK_BH(&ip_irc_lock);
return NF_ACCEPT; return NF_ACCEPT;
} }
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
#define GENERIC_TIMEOUT (600*HZ) #define GENERIC_TIMEOUT (600*HZ)
static int generic_pkt_to_tuple(const void *datah, size_t datalen, static int generic_pkt_to_tuple(const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple) struct ip_conntrack_tuple *tuple)
{ {
tuple->src.u.all = 0; tuple->src.u.all = 0;
...@@ -39,17 +40,16 @@ static unsigned int generic_print_conntrack(char *buffer, ...@@ -39,17 +40,16 @@ static unsigned int generic_print_conntrack(char *buffer,
} }
/* Returns verdict for packet, or -1 for invalid. */ /* Returns verdict for packet, or -1 for invalid. */
static int established(struct ip_conntrack *conntrack, static int packet(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len, const struct sk_buff *skb,
enum ip_conntrack_info conntrackinfo) enum ip_conntrack_info conntrackinfo)
{ {
ip_ct_refresh(conntrack, GENERIC_TIMEOUT); ip_ct_refresh(conntrack, GENERIC_TIMEOUT);
return NF_ACCEPT; return NF_ACCEPT;
} }
/* Called when a new connection for this protocol found. */ /* Called when a new connection for this protocol found. */
static int static int new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len)
{ {
return 1; return 1;
} }
...@@ -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, NULL }; generic_print_conntrack, packet, new, NULL, NULL, NULL };
...@@ -14,14 +14,18 @@ ...@@ -14,14 +14,18 @@
#define DEBUGP(format, args...) #define DEBUGP(format, args...)
#endif #endif
static int icmp_pkt_to_tuple(const void *datah, size_t datalen, static int icmp_pkt_to_tuple(const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple) struct ip_conntrack_tuple *tuple)
{ {
const struct icmphdr *hdr = datah; struct icmphdr hdr;
tuple->dst.u.icmp.type = hdr->type; if (skb_copy_bits(skb, dataoff, &hdr, sizeof(hdr)) != 0)
tuple->src.u.icmp.id = hdr->un.echo.id; return 0;
tuple->dst.u.icmp.code = hdr->code;
tuple->dst.u.icmp.type = hdr.type;
tuple->src.u.icmp.id = hdr.un.echo.id;
tuple->dst.u.icmp.code = hdr.code;
return 1; return 1;
} }
...@@ -69,7 +73,7 @@ static unsigned int icmp_print_conntrack(char *buffer, ...@@ -69,7 +73,7 @@ static unsigned int icmp_print_conntrack(char *buffer,
/* Returns verdict for packet, or -1 for invalid. */ /* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct ip_conntrack *ct, static int icmp_packet(struct ip_conntrack *ct,
struct iphdr *iph, size_t len, const struct sk_buff *skb,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo)
{ {
/* Try to delete connection immediately after all replies: /* Try to delete connection immediately after all replies:
...@@ -90,7 +94,7 @@ static int icmp_packet(struct ip_conntrack *ct, ...@@ -90,7 +94,7 @@ static int icmp_packet(struct ip_conntrack *ct,
/* Called when a new connection for this protocol found. */ /* Called when a new connection for this protocol found. */
static int icmp_new(struct ip_conntrack *conntrack, static int icmp_new(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len) const struct sk_buff *skb)
{ {
static u_int8_t valid_new[] static u_int8_t valid_new[]
= { [ICMP_ECHO] = 1, = { [ICMP_ECHO] = 1,
......
...@@ -96,13 +96,18 @@ static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = { ...@@ -96,13 +96,18 @@ static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
} }
}; };
static int tcp_pkt_to_tuple(const void *datah, size_t datalen, static int tcp_pkt_to_tuple(const struct sk_buff *skb,
struct ip_conntrack_tuple *tuple) unsigned int dataoff,
struct ip_conntrack_tuple *tuple)
{ {
const struct tcphdr *hdr = datah; struct tcphdr hdr;
tuple->src.u.tcp.port = hdr->source; /* Actually only need first 8 bytes. */
tuple->dst.u.tcp.port = hdr->dest; if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
return 0;
tuple->src.u.tcp.port = hdr.source;
tuple->dst.u.tcp.port = hdr.dest;
return 1; return 1;
} }
...@@ -148,30 +153,26 @@ static unsigned int get_conntrack_index(const struct tcphdr *tcph) ...@@ -148,30 +153,26 @@ static unsigned int get_conntrack_index(const struct tcphdr *tcph)
/* Returns verdict for packet, or -1 for invalid. */ /* Returns verdict for packet, or -1 for invalid. */
static int tcp_packet(struct ip_conntrack *conntrack, static int tcp_packet(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len, const struct sk_buff *skb,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo)
{ {
enum tcp_conntrack newconntrack, oldtcpstate; enum tcp_conntrack newconntrack, oldtcpstate;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); struct tcphdr tcph;
/* We're guaranteed to have the base header, but maybe not the if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
options. */
if (len < (iph->ihl + tcph->doff) * 4) {
DEBUGP("ip_conntrack_tcp: Truncated packet.\n");
return -1; return -1;
}
WRITE_LOCK(&tcp_lock); WRITE_LOCK(&tcp_lock);
oldtcpstate = conntrack->proto.tcp.state; oldtcpstate = conntrack->proto.tcp.state;
newconntrack newconntrack
= tcp_conntracks = tcp_conntracks
[CTINFO2DIR(ctinfo)] [CTINFO2DIR(ctinfo)]
[get_conntrack_index(tcph)][oldtcpstate]; [get_conntrack_index(&tcph)][oldtcpstate];
/* Invalid */ /* Invalid */
if (newconntrack == TCP_CONNTRACK_MAX) { if (newconntrack == TCP_CONNTRACK_MAX) {
DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n", DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
CTINFO2DIR(ctinfo), get_conntrack_index(tcph), CTINFO2DIR(ctinfo), get_conntrack_index(&tcph),
conntrack->proto.tcp.state); conntrack->proto.tcp.state);
WRITE_UNLOCK(&tcp_lock); WRITE_UNLOCK(&tcp_lock);
return -1; return -1;
...@@ -182,15 +183,15 @@ static int tcp_packet(struct ip_conntrack *conntrack, ...@@ -182,15 +183,15 @@ static int tcp_packet(struct ip_conntrack *conntrack,
/* Poor man's window tracking: record SYN/ACK for handshake check */ /* Poor man's window tracking: record SYN/ACK for handshake check */
if (oldtcpstate == TCP_CONNTRACK_SYN_SENT if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
&& tcph->syn && tcph->ack) && tcph.syn && tcph.ack)
conntrack->proto.tcp.handshake_ack conntrack->proto.tcp.handshake_ack
= htonl(ntohl(tcph->seq) + 1); = htonl(ntohl(tcph.seq) + 1);
/* If only reply is a RST, we can consider ourselves not to /* If only reply is a RST, we can consider ourselves not to
have an established connection: this is a fairly common have an established connection: this is a fairly common
problem case, so we can delete the conntrack problem case, so we can delete the conntrack
immediately. --RR */ immediately. --RR */
if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph->rst) { if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph.rst) {
WRITE_UNLOCK(&tcp_lock); WRITE_UNLOCK(&tcp_lock);
if (del_timer(&conntrack->timeout)) if (del_timer(&conntrack->timeout))
conntrack->timeout.function((unsigned long)conntrack); conntrack->timeout.function((unsigned long)conntrack);
...@@ -198,8 +199,8 @@ static int tcp_packet(struct ip_conntrack *conntrack, ...@@ -198,8 +199,8 @@ static int tcp_packet(struct ip_conntrack *conntrack,
/* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */ /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */
if (oldtcpstate == TCP_CONNTRACK_SYN_RECV if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
&& tcph->ack && !tcph->syn && tcph.ack && !tcph.syn
&& tcph->ack_seq == conntrack->proto.tcp.handshake_ack) && tcph.ack_seq == conntrack->proto.tcp.handshake_ack)
set_bit(IPS_ASSURED_BIT, &conntrack->status); set_bit(IPS_ASSURED_BIT, &conntrack->status);
WRITE_UNLOCK(&tcp_lock); WRITE_UNLOCK(&tcp_lock);
...@@ -210,15 +211,17 @@ static int tcp_packet(struct ip_conntrack *conntrack, ...@@ -210,15 +211,17 @@ static int tcp_packet(struct ip_conntrack *conntrack,
} }
/* Called when a new connection for this protocol found. */ /* Called when a new connection for this protocol found. */
static int tcp_new(struct ip_conntrack *conntrack, static int tcp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
struct iphdr *iph, size_t len)
{ {
enum tcp_conntrack newconntrack; enum tcp_conntrack newconntrack;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); struct tcphdr tcph;
if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
return -1;
/* Don't need lock here: this conntrack not in circulation yet */ /* Don't need lock here: this conntrack not in circulation yet */
newconntrack newconntrack
= tcp_conntracks[0][get_conntrack_index(tcph)] = tcp_conntracks[0][get_conntrack_index(&tcph)]
[TCP_CONNTRACK_NONE]; [TCP_CONNTRACK_NONE];
/* Invalid: delete conntrack */ /* Invalid: delete conntrack */
...@@ -232,15 +235,17 @@ static int tcp_new(struct ip_conntrack *conntrack, ...@@ -232,15 +235,17 @@ static int tcp_new(struct ip_conntrack *conntrack,
} }
static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp, static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp,
struct sk_buff **pskb) const struct sk_buff *skb)
{ {
struct iphdr *iph = (*pskb)->nh.iph; const struct iphdr *iph = skb->nh.iph;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); struct tcphdr tcph;
unsigned int datalen; unsigned int datalen;
datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
return 0;
datalen = skb->len - iph->ihl*4 - tcph.doff*4;
return between(exp->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); 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
......
...@@ -9,13 +9,18 @@ ...@@ -9,13 +9,18 @@
#define UDP_TIMEOUT (30*HZ) #define UDP_TIMEOUT (30*HZ)
#define UDP_STREAM_TIMEOUT (180*HZ) #define UDP_STREAM_TIMEOUT (180*HZ)
static int udp_pkt_to_tuple(const void *datah, size_t datalen, static int udp_pkt_to_tuple(const struct sk_buff *skb,
struct ip_conntrack_tuple *tuple) unsigned int dataoff,
struct ip_conntrack_tuple *tuple)
{ {
const struct udphdr *hdr = datah; struct udphdr hdr;
tuple->src.u.udp.port = hdr->source; /* Actually only need first 8 bytes. */
tuple->dst.u.udp.port = hdr->dest; if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
return 0;
tuple->src.u.udp.port = hdr.source;
tuple->dst.u.udp.port = hdr.dest;
return 1; return 1;
} }
...@@ -46,7 +51,7 @@ static unsigned int udp_print_conntrack(char *buffer, ...@@ -46,7 +51,7 @@ static unsigned int udp_print_conntrack(char *buffer,
/* Returns verdict for packet, and may modify conntracktype */ /* Returns verdict for packet, and may modify conntracktype */
static int udp_packet(struct ip_conntrack *conntrack, static int udp_packet(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len, const struct sk_buff *skb,
enum ip_conntrack_info conntrackinfo) enum ip_conntrack_info conntrackinfo)
{ {
/* If we've seen traffic both ways, this is some kind of UDP /* If we've seen traffic both ways, this is some kind of UDP
...@@ -62,8 +67,7 @@ static int udp_packet(struct ip_conntrack *conntrack, ...@@ -62,8 +67,7 @@ static int udp_packet(struct ip_conntrack *conntrack,
} }
/* Called when a new connection for this protocol found. */ /* Called when a new connection for this protocol found. */
static int udp_new(struct ip_conntrack *conntrack, static int udp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
struct iphdr *iph, size_t len)
{ {
return 1; return 1;
} }
......
...@@ -192,10 +192,6 @@ static unsigned int ip_refrag(unsigned int hooknum, ...@@ -192,10 +192,6 @@ static unsigned int ip_refrag(unsigned int hooknum,
{ {
struct rtable *rt = (struct rtable *)(*pskb)->dst; struct rtable *rt = (struct rtable *)(*pskb)->dst;
/* FIXME: Push down to extensions --RR */
if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
return NF_DROP;
/* We've seen it coming out the other side: confirm */ /* We've seen it coming out the other side: confirm */
if (ip_confirm(hooknum, pskb, in, out, okfn) != NF_ACCEPT) if (ip_confirm(hooknum, pskb, in, out, okfn) != NF_ACCEPT)
return NF_DROP; return NF_DROP;
...@@ -217,10 +213,6 @@ static unsigned int ip_conntrack_local(unsigned int hooknum, ...@@ -217,10 +213,6 @@ static unsigned int ip_conntrack_local(unsigned int hooknum,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
/* FIXME: Push down to extensions --RR */
if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
return NF_DROP;
/* root is playing with raw sockets. */ /* root is playing with raw sockets. */
if ((*pskb)->len < sizeof(struct iphdr) if ((*pskb)->len < sizeof(struct iphdr)
|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
......
...@@ -35,15 +35,18 @@ MODULE_PARM_DESC(ports, "port numbers of tftp servers"); ...@@ -35,15 +35,18 @@ MODULE_PARM_DESC(ports, "port numbers of tftp servers");
#define DEBUGP(format, args...) #define DEBUGP(format, args...)
#endif #endif
static int tftp_help(const struct iphdr *iph, size_t len, static int tftp_help(struct sk_buff *skb,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo)
{ {
struct udphdr *udph = (void *)iph + iph->ihl * 4; struct tftphdr tftph;
struct tftphdr *tftph = (void *)udph + 8;
struct ip_conntrack_expect exp; struct ip_conntrack_expect exp;
switch (ntohs(tftph->opcode)) { if (skb_copy_bits(skb, skb->nh.iph->ihl * 4 + sizeof(struct udphdr),
&tftph, sizeof(tftph)) != 0)
return -1;
switch (ntohs(tftph.opcode)) {
/* RRQ and WRQ works the same way */ /* RRQ and WRQ works the same way */
case TFTP_OPCODE_READ: case TFTP_OPCODE_READ:
case TFTP_OPCODE_WRITE: case TFTP_OPCODE_WRITE:
......
...@@ -146,6 +146,12 @@ check_for_demasq(struct sk_buff **pskb) ...@@ -146,6 +146,12 @@ check_for_demasq(struct sk_buff **pskb)
server here (== DNAT). Do SNAT icmp manips server here (== DNAT). Do SNAT icmp manips
in POST_ROUTING handling. */ in POST_ROUTING handling. */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
/* FIXME: Remove once NAT handled non-linear.
*/
if (skb_is_nonlinear(*pskb)
&& skb_linearize(*pskb, GFP_ATOMIC) != 0)
return NF_DROP;
icmp_reply_translation(*pskb, ct, icmp_reply_translation(*pskb, ct,
NF_IP_PRE_ROUTING, NF_IP_PRE_ROUTING,
CTINFO2DIR(ctinfo)); CTINFO2DIR(ctinfo));
...@@ -160,7 +166,7 @@ check_for_demasq(struct sk_buff **pskb) ...@@ -160,7 +166,7 @@ check_for_demasq(struct sk_buff **pskb)
case IPPROTO_UDP: case IPPROTO_UDP:
IP_NF_ASSERT(((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) == 0); IP_NF_ASSERT(((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
if (!get_tuple(iph, (*pskb)->len, &tuple, protocol)) { if (!get_tuple(iph, *pskb, iph->ihl*4, &tuple, protocol)) {
if (net_ratelimit()) if (net_ratelimit())
printk("ip_fw_compat_masq: Can't get tuple\n"); printk("ip_fw_compat_masq: Can't get tuple\n");
return NF_ACCEPT; return NF_ACCEPT;
......
...@@ -730,15 +730,15 @@ manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, ...@@ -730,15 +730,15 @@ manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len,
} }
static inline int exp_for_packet(struct ip_conntrack_expect *exp, static inline int exp_for_packet(struct ip_conntrack_expect *exp,
struct sk_buff **pskb) struct sk_buff *skb)
{ {
struct ip_conntrack_protocol *proto; struct ip_conntrack_protocol *proto;
int ret = 1; int ret = 1;
MUST_BE_READ_LOCKED(&ip_conntrack_lock); MUST_BE_READ_LOCKED(&ip_conntrack_lock);
proto = __ip_ct_find_proto((*pskb)->nh.iph->protocol); proto = __ip_ct_find_proto(skb->nh.iph->protocol);
if (proto->exp_matches_pkt) if (proto->exp_matches_pkt)
ret = proto->exp_matches_pkt(exp, pskb); ret = proto->exp_matches_pkt(exp, skb);
return ret; return ret;
} }
...@@ -813,7 +813,7 @@ do_bindings(struct ip_conntrack *ct, ...@@ -813,7 +813,7 @@ do_bindings(struct ip_conntrack *ct,
if (exp->sibling) if (exp->sibling)
continue; continue;
if (exp_for_packet(exp, pskb)) { if (exp_for_packet(exp, *pskb)) {
/* FIXME: May be true multiple times in the case of UDP!! */ /* FIXME: May be true multiple times in the case of UDP!! */
DEBUGP("calling nat helper (exp=%p) for packet\n", DEBUGP("calling nat helper (exp=%p) for packet\n",
exp); exp);
......
...@@ -153,7 +153,7 @@ static void fini(void) ...@@ -153,7 +153,7 @@ static void fini(void)
static int __init init(void) static int __init init(void)
{ {
int i, ret; int i, ret = 0;
char *tmpname; char *tmpname;
if (!ports[0]) if (!ports[0])
......
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