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);
#endif
struct conn {
char* match;
int matchlen;
};
#define NUM_MSGS 3
struct ip_ct_amanda_expect
{
u_int16_t port; /* port number of this expectation */
......
......@@ -24,9 +24,11 @@ extern struct list_head protocol_list;
extern struct ip_conntrack *icmp_error_track(struct sk_buff *skb,
enum ip_conntrack_info *ctinfo,
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_protocol *protocol);
const struct ip_conntrack_protocol *protocol);
/* Find a connection corresponding to a tuple. */
struct ip_conntrack_tuple_hash *
......
......@@ -25,7 +25,7 @@ struct ip_conntrack_helper
/* Function to call when data passes; return verdict, or -1 to
invalidate. */
int (*help)(const struct iphdr *, size_t len,
int (*help)(struct sk_buff *skb,
struct ip_conntrack *ct,
enum ip_conntrack_info conntrackinfo);
};
......
......@@ -37,11 +37,6 @@ struct ip_ct_irc_master {
#define IRC_PORT 6667
struct dccproto {
char* match;
int matchlen;
};
/* Protects irc part of conntracks */
DECLARE_LOCK_EXTERN(ip_irc_lock);
......
......@@ -14,8 +14,10 @@ struct ip_conntrack_protocol
/* Protocol name */
const char *name;
/* Try to fill in the third arg; return true if possible. */
int (*pkt_to_tuple)(const void *datah, size_t datalen,
/* Try to fill in the third arg: dataoff is offset past IP
hdr. Return true if possible. */
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.
......@@ -34,20 +36,19 @@ struct ip_conntrack_protocol
/* Returns verdict for packet, or -1 for invalid. */
int (*packet)(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len,
const struct sk_buff *skb,
enum ip_conntrack_info ctinfo);
/* Called when a new connection for this protocol found;
* returns TRUE if it's OK. If so, packet() called next. */
int (*new)(struct ip_conntrack *conntrack, struct iphdr *iph,
size_t len);
int (*new)(struct ip_conntrack *conntrack, const struct sk_buff *skb);
/* 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);
const struct sk_buff *skb);
/* Module (if any) which this is connected to. */
struct module *me;
......
......@@ -37,14 +37,8 @@ MODULE_PARM(master_timeout, "i");
MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
DECLARE_LOCK(ip_amanda_lock);
struct module *ip_conntrack_amanda = THIS_MODULE;
#define MAXMATCHLEN 6
struct conn conns[NUM_MSGS] = {
{"DATA ", 5},
{"MESG ", 5},
{"INDEX ", 6},
};
char *conns[] = { "DATA ", "MESG ", "INDEX " };
#if 0
#define DEBUGP printk
......@@ -52,18 +46,15 @@ struct conn conns[NUM_MSGS] = {
#define DEBUGP(format, args...)
#endif
/* This is slow, but it's simple. --RR */
static char amanda_buffer[65536];
/* FIXME: This should be in userspace. Later. */
static int help(const struct iphdr *iph, size_t len,
static int help(struct sk_buff *skb,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{
struct udphdr *udph = (void *)iph + iph->ihl * 4;
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;
char *data, *data_limit;
int dir = CTINFO2DIR(ctinfo);
unsigned int dataoff, i;
struct ip_ct_amanda *info =
(struct ip_ct_amanda *)&ct->help.ct_ftp_info;
......@@ -79,77 +70,65 @@ static int help(const struct iphdr *iph, size_t len,
if (dir == IP_CT_DIR_ORIGINAL)
return NF_ACCEPT;
/* Not whole UDP header? */
if (udplen < sizeof(struct udphdr)) {
printk("ip_conntrack_amanda_help: udplen = %u\n",
(unsigned)udplen);
/* No data? */
dataoff = skb->nh.iph->ihl*4 + sizeof(struct udphdr);
if (dataoff >= skb->len) {
if (net_ratelimit())
printk("ip_conntrack_amanda_help: skblen = %u\n",
(unsigned)skb->len);
return NF_ACCEPT;
}
/* Checksum invalid? Ignore. */
if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP,
csum_partial((char *)udph, udplen, 0))) {
DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u "
"%u.%u.%u.%u\n",
udph, udplen, NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
return NF_ACCEPT;
}
LOCK_BH(&ip_amanda_lock);
skb_copy_bits(skb, dataoff, amanda_buffer, skb->len - dataoff);
data = amanda_buffer;
data_limit = amanda_buffer + skb->len - dataoff;
*data_limit = '\0';
/* Search for the CONNECT string */
while (data < data_limit) {
if (!memcmp(data, "CONNECT ", 8)) {
break;
}
data++;
}
if (memcmp(data, "CONNECT ", 8))
return NF_ACCEPT;
data = strstr(data, "CONNECT ");
if (!data)
goto out;
DEBUGP("ip_conntrack_amanda_help: CONNECT found in connection "
"%u.%u.%u.%u:%u %u.%u.%u.%u:%u\n",
NIPQUAD(iph->saddr), htons(udph->source),
NIPQUAD(iph->daddr), htons(udph->dest));
data += 8;
while (*data != 0x0a && data < data_limit) {
data += strlen("CONNECT ");
int i;
for (i = 0; i < NUM_MSGS; i++) {
if (!memcmp(data, conns[i].match,
conns[i].matchlen)) {
/* Only search first line. */
if (strchr(data, '\n'))
*strchr(data, '\n') = '\0';
for (i = 0; i < ARRAY_SIZE(conns); i++) {
char *match = strstr(data, conns[i]);
if (match) {
char *portchr;
struct ip_conntrack_expect expect;
struct ip_ct_amanda_expect
*exp_amanda_info =
struct ip_ct_amanda_expect *exp_amanda_info =
&expect.help.exp_amanda_info;
memset(&expect, 0, sizeof(expect));
data += conns[i].matchlen;
data += strlen(conns[i]);
/* this is not really tcp, but let's steal an
* idea from a tcp stream helper :-)
*/
// 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);
* 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->port = simple_strtoul(data, &data,10);
exp_amanda_info->len = data - portchr;
/* eat whitespace */
while (*data == ' ')
data++;
DEBUGP ("ip_conntrack_amanda_help: "
DEBUGP("ip_conntrack_amanda_help: "
"CONNECT %s request with port "
"%u found\n", conns[i].match,
"%u found\n", conns[i],
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 } },
......@@ -169,8 +148,8 @@ DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, datap, exp_amanda_info-
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) {
if (ip_conntrack_expect_related(ct, &expect)
== -EEXIST) {
;
/* this must be a packet being resent */
/* XXX - how do I get the
......@@ -184,16 +163,28 @@ DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, datap, exp_amanda_info-
* .seq.
*/
}
}
}
out:
UNLOCK_BH(&ip_amanda_lock);
} /* if memcmp(conns) */
} /* for .. NUM_MSGS */
data++;
} /* while (*data != 0x0a && data < data_limit) */
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)
{
......@@ -205,20 +196,7 @@ static int __init init(void)
{
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");
ret = ip_conntrack_helper_register(&amanda_helper);
if (ret) {
......
......@@ -120,31 +120,25 @@ hash_conntrack(const struct ip_conntrack_tuple *tuple)
% ip_conntrack_htable_size;
}
inline int
get_tuple(const struct iphdr *iph, size_t len,
int
get_tuple(const struct iphdr *iph,
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
struct ip_conntrack_protocol *protocol)
const struct ip_conntrack_protocol *protocol)
{
int ret;
/* Never happen */
if (iph->frag_off & htons(IP_OFFSET)) {
printk("ip_conntrack_core: Frag of proto %u.\n",
iph->protocol);
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->dst.ip = iph->daddr;
tuple->dst.protonum = iph->protocol;
ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl,
len - 4*iph->ihl,
tuple);
return ret;
return protocol->pkt_to_tuple(skb, dataoff, tuple);
}
static int
......@@ -496,54 +490,40 @@ icmp_error_track(struct sk_buff *skb,
enum ip_conntrack_info *ctinfo,
unsigned int hooknum)
{
const struct iphdr *iph;
struct icmphdr *hdr;
struct ip_conntrack_tuple innertuple, origtuple;
struct iphdr *inner;
size_t datalen;
struct {
struct icmphdr icmp;
struct iphdr ip;
} inside;
struct ip_conntrack_protocol *innerproto;
struct ip_conntrack_tuple_hash *h;
int dataoff;
IP_NF_ASSERT(iph->protocol == IPPROTO_ICMP);
IP_NF_ASSERT(skb->nfct == NULL);
iph = skb->nh.iph;
hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
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");
/* Not enough header? */
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &inside, sizeof(inside))!=0)
return NULL;
}
if (hdr->type != ICMP_DEST_UNREACH
&& hdr->type != ICMP_SOURCE_QUENCH
&& hdr->type != ICMP_TIME_EXCEEDED
&& hdr->type != ICMP_PARAMETERPROB
&& hdr->type != ICMP_REDIRECT)
if (inside.icmp.type != ICMP_DEST_UNREACH
&& inside.icmp.type != ICMP_SOURCE_QUENCH
&& inside.icmp.type != ICMP_TIME_EXCEEDED
&& inside.icmp.type != ICMP_PARAMETERPROB
&& inside.icmp.type != ICMP_REDIRECT)
return NULL;
/* 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",
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;
}
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? */
if (inner->ihl * 4 + 8 > datalen
|| !get_tuple(inner, datalen, &origtuple, innerproto)) {
DEBUGP("icmp_error: ! get_tuple p=%u (%u*4+%u dlen=%u)\n",
inner->protocol, inner->ihl, 8,
datalen);
if (!get_tuple(&inside.ip, skb, dataoff, &origtuple, innerproto)) {
DEBUGP("icmp_error: ! get_tuple p=%u", inside.ip.protocol);
return NULL;
}
......@@ -679,7 +659,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
for (i=0; i < IP_CT_NUMBER; i++)
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);
return NULL;
}
......@@ -748,7 +728,7 @@ resolve_normal_ct(struct sk_buff *skb,
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;
/* look for tuple match */
......@@ -823,10 +803,6 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
if ((*pskb)->nfct)
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. */
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
*pskb = ip_ct_gather_frags(*pskb);
......@@ -851,7 +827,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
IP_NF_ASSERT((*pskb)->nfct);
ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo);
ret = proto->packet(ct, *pskb, ctinfo);
if (ret == -1) {
/* Invalid */
nf_conntrack_put((*pskb)->nfct);
......@@ -860,8 +836,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
}
if (ret != NF_DROP && ct->helper) {
ret = ct->helper->help((*pskb)->nh.iph, (*pskb)->len,
ct, ctinfo);
ret = ct->helper->help(*pskb, ct, ctinfo);
if (ret == -1) {
/* Invalid */
nf_conntrack_put((*pskb)->nfct);
......
......@@ -11,6 +11,9 @@
#include <linux/netfilter_ipv4/ip_conntrack_helper.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);
struct module *ip_conntrack_ftp = THIS_MODULE;
......@@ -228,18 +231,14 @@ static int find_pattern(const char *data, size_t dlen,
return 1;
}
/* FIXME: This should be in userspace. Later. */
static int help(const struct iphdr *iph, size_t len,
static int help(struct sk_buff *skb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo)
{
/* tcplen not negative guaranteed by ip_conntrack_tcp.c */
struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
const char *data = (const char *)tcph + tcph->doff * 4;
unsigned int tcplen = len - iph->ihl * 4;
unsigned int datalen = tcplen - tcph->doff * 4;
unsigned int dataoff, datalen;
struct tcphdr tcph;
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 };
int dir = CTINFO2DIR(ctinfo);
unsigned int matchlen, matchoff;
......@@ -257,45 +256,42 @@ static int help(const struct iphdr *iph, size_t len,
return NF_ACCEPT;
}
/* Not whole TCP header? */
if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
DEBUGP("ftp: tcplen = %u\n", (unsigned)tcplen);
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
return NF_ACCEPT;
}
/* Checksum invalid? Ignore. */
/* FIXME: Source route IP option packets --RR */
if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
csum_partial((char *)tcph, tcplen, 0))) {
DEBUGP("ftp_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
tcph, tcplen, NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
dataoff = skb->nh.iph->ihl*4 + tcph.doff*4;
/* No data? */
if (dataoff >= skb->len) {
DEBUGP("ftp: skblen = %u\n", skb->len);
return NF_ACCEPT;
}
datalen = skb->len - dataoff;
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 = ct_ftp_info->seq_aft_nl[dir];
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);
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",
ntohl(tcph->seq) + datalen);
ntohl(tcph.seq) + datalen);
ct_ftp_info->seq_aft_nl[dir] =
ntohl(tcph->seq) + datalen;
ntohl(tcph.seq) + datalen;
ct_ftp_info->seq_aft_nl_set[dir] = 1;
}
}
UNLOCK_BH(&ip_ftp_lock);
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",
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
......@@ -308,7 +304,7 @@ static int help(const struct iphdr *iph, size_t len,
for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) {
if (search[i].dir != dir) continue;
found = find_pattern(data, datalen,
found = find_pattern(ftp_buffer, skb->len - dataoff,
search[i].pattern,
search[i].plen,
search[i].skip,
......@@ -326,22 +322,24 @@ static int help(const struct iphdr *iph, size_t len,
if (net_ratelimit())
printk("conntrack_ftp: partial %s %u+%u\n",
search[i].pattern,
ntohl(tcph->seq), datalen);
return NF_DROP;
} else if (found == 0) /* No match */
return NF_ACCEPT;
ntohl(tcph.seq), datalen);
ret = NF_DROP;
goto out;
} else if (found == 0) { /* No match */
ret = NF_ACCEPT;
goto out;
}
DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
(int)matchlen, data + matchoff,
matchlen, ntohl(tcph->seq) + 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) {
exp->seq = ntohl(tcph->seq) + matchoff;
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];
......@@ -358,7 +356,10 @@ static int help(const struct iphdr *iph, size_t len,
<lincoln@cesar.org.br> for reporting this potential
problem (DMZ machines opening holes to internal
networks, or the packet filter itself). */
if (!loose) goto out;
if (!loose) {
ret = NF_ACCEPT;
goto out;
}
}
exp->tuple = ((struct ip_conntrack_tuple)
......@@ -376,10 +377,10 @@ static int help(const struct iphdr *iph, size_t len,
/* Ignore failure; should only happen with NAT */
ip_conntrack_expect_related(ct, &expect);
ret = NF_ACCEPT;
out:
UNLOCK_BH(&ip_ftp_lock);
return NF_ACCEPT;
return ret;
}
static struct ip_conntrack_helper ftp[MAX_PORTS];
......
......@@ -38,6 +38,8 @@ static int ports[MAX_PORTS];
static int ports_c = 0;
static int max_dcc_channels = 8;
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_DESCRIPTION("IRC (DCC) connection tracking module");
......@@ -51,14 +53,7 @@ MODULE_PARM(dcc_timeout, "i");
MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
#endif
#define NUM_DCCPROTO 5
struct dccproto dccprotos[NUM_DCCPROTO] = {
{"SEND ", 5},
{"CHAT ", 5},
{"MOVE ", 5},
{"TSEND ", 6},
{"SCHAT ", 6}
};
static char *dccprotos[] = { "SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT " };
#define MAXMATCHLEN 6
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,
return 0;
}
/* FIXME: This should be in userspace. Later. */
static int help(const struct iphdr *iph, size_t len,
static int help(struct sk_buff *skb,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{
/* tcplen not negative guaranteed by ip_conntrack_tcp.c */
struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
const char *data = (const char *) tcph + tcph->doff * 4;
const char *_data = data;
char *data_limit;
u_int32_t tcplen = len - iph->ihl * 4;
u_int32_t datalen = tcplen - tcph->doff * 4;
unsigned int dataoff;
struct tcphdr tcph;
char *data, *data_limit;
int dir = CTINFO2DIR(ctinfo);
struct ip_conntrack_expect expect, *exp = &expect;
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,
return NF_ACCEPT;
}
/* Not whole TCP header? */
if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
DEBUGP("tcplen = %u\n", (unsigned) tcplen);
/* Not a full tcp header? */
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
return NF_ACCEPT;
}
/* Checksum invalid? Ignore. */
/* FIXME: Source route IP option packets --RR */
if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
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));
/* No data? */
dataoff = skb->nh.iph->ihl*4 + tcph.doff*4;
if (dataoff >= skb->len)
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))) {
if (memcmp(data, "\1DCC ", 5)) {
data++;
......@@ -162,19 +148,18 @@ static int help(const struct iphdr *iph, size_t len,
data += 5;
DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n",
NIPQUAD(iph->saddr), ntohs(tcph->source),
NIPQUAD(iph->daddr), ntohs(tcph->dest));
NIPQUAD(iph->saddr), ntohs(tcph.source),
NIPQUAD(iph->daddr), ntohs(tcph.dest));
for (i = 0; i < NUM_DCCPROTO; i++) {
if (memcmp(data, dccprotos[i].match,
dccprotos[i].matchlen)) {
for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) {
/* no match */
continue;
}
DEBUGP("DCC %s detected\n", dccprotos[i].match);
data += dccprotos[i].matchlen;
if (parse_dcc((char *) data, data_limit, &dcc_ip,
DEBUGP("DCC %s detected\n", dccprotos[i]);
data += strlen(dccprotos[i]);
if (parse_dcc((char *)data, data_limit, &dcc_ip,
&dcc_port, &addr_beg_p, &addr_end_p)) {
/* unable to parse */
DEBUGP("unable to parse dcc command\n");
......@@ -196,12 +181,10 @@ static int help(const struct iphdr *iph, size_t len,
memset(&expect, 0, sizeof(expect));
LOCK_BH(&ip_irc_lock);
/* save position of address in dcc string,
* necessary for NAT */
DEBUGP("tcph->seq = %u\n", tcph->seq);
exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data);
DEBUGP("tcph->seq = %u\n", tcph.seq);
exp->seq = ntohl(tcph.seq) + (addr_beg_p - irc_buffer);
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",
......@@ -224,12 +207,13 @@ static int help(const struct iphdr *iph, size_t len,
ntohs(exp->tuple.dst.u.tcp.port));
ip_conntrack_expect_related(ct, &expect);
UNLOCK_BH(&ip_irc_lock);
return NF_ACCEPT;
goto out;
} /* for .. NUM_DCCPROTO */
} /* while data < ... */
out:
UNLOCK_BH(&ip_irc_lock);
return NF_ACCEPT;
}
......
......@@ -6,7 +6,8 @@
#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)
{
tuple->src.u.all = 0;
......@@ -39,8 +40,8 @@ static unsigned int generic_print_conntrack(char *buffer,
}
/* Returns verdict for packet, or -1 for invalid. */
static int established(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len,
static int packet(struct ip_conntrack *conntrack,
const struct sk_buff *skb,
enum ip_conntrack_info conntrackinfo)
{
ip_ct_refresh(conntrack, GENERIC_TIMEOUT);
......@@ -48,8 +49,7 @@ static int established(struct ip_conntrack *conntrack,
}
/* Called when a new connection for this protocol found. */
static int
new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len)
static int new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
{
return 1;
}
......@@ -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, NULL };
generic_print_conntrack, packet, new, NULL, NULL, NULL };
......@@ -14,14 +14,18 @@
#define DEBUGP(format, args...)
#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)
{
const struct icmphdr *hdr = datah;
struct icmphdr hdr;
tuple->dst.u.icmp.type = hdr->type;
tuple->src.u.icmp.id = hdr->un.echo.id;
tuple->dst.u.icmp.code = hdr->code;
if (skb_copy_bits(skb, dataoff, &hdr, sizeof(hdr)) != 0)
return 0;
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;
}
......@@ -69,7 +73,7 @@ static unsigned int icmp_print_conntrack(char *buffer,
/* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct ip_conntrack *ct,
struct iphdr *iph, size_t len,
const struct sk_buff *skb,
enum ip_conntrack_info ctinfo)
{
/* Try to delete connection immediately after all replies:
......@@ -90,7 +94,7 @@ static int icmp_packet(struct ip_conntrack *ct,
/* Called when a new connection for this protocol found. */
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[]
= { [ICMP_ECHO] = 1,
......
......@@ -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,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple)
{
const struct tcphdr *hdr = datah;
struct tcphdr hdr;
tuple->src.u.tcp.port = hdr->source;
tuple->dst.u.tcp.port = hdr->dest;
/* Actually only need first 8 bytes. */
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;
}
......@@ -148,30 +153,26 @@ static unsigned int get_conntrack_index(const struct tcphdr *tcph)
/* Returns verdict for packet, or -1 for invalid. */
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 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
options. */
if (len < (iph->ihl + tcph->doff) * 4) {
DEBUGP("ip_conntrack_tcp: Truncated packet.\n");
if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
return -1;
}
WRITE_LOCK(&tcp_lock);
oldtcpstate = conntrack->proto.tcp.state;
newconntrack
= tcp_conntracks
[CTINFO2DIR(ctinfo)]
[get_conntrack_index(tcph)][oldtcpstate];
[get_conntrack_index(&tcph)][oldtcpstate];
/* Invalid */
if (newconntrack == TCP_CONNTRACK_MAX) {
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);
WRITE_UNLOCK(&tcp_lock);
return -1;
......@@ -182,15 +183,15 @@ static int tcp_packet(struct ip_conntrack *conntrack,
/* Poor man's window tracking: record SYN/ACK for handshake check */
if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
&& tcph->syn && tcph->ack)
&& tcph.syn && tcph.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
have an established connection: this is a fairly common
problem case, so we can delete the conntrack
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);
if (del_timer(&conntrack->timeout))
conntrack->timeout.function((unsigned long)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 */
if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
&& tcph->ack && !tcph->syn
&& tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
&& tcph.ack && !tcph.syn
&& tcph.ack_seq == conntrack->proto.tcp.handshake_ack)
set_bit(IPS_ASSURED_BIT, &conntrack->status);
WRITE_UNLOCK(&tcp_lock);
......@@ -210,15 +211,17 @@ static int tcp_packet(struct ip_conntrack *conntrack,
}
/* Called when a new connection for this protocol found. */
static int tcp_new(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len)
static int tcp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
{
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 */
newconntrack
= tcp_conntracks[0][get_conntrack_index(tcph)]
= tcp_conntracks[0][get_conntrack_index(&tcph)]
[TCP_CONNTRACK_NONE];
/* Invalid: delete conntrack */
......@@ -232,15 +235,17 @@ static int tcp_new(struct ip_conntrack *conntrack,
}
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;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
const struct iphdr *iph = skb->nh.iph;
struct tcphdr tcph;
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
......
......@@ -9,13 +9,18 @@
#define UDP_TIMEOUT (30*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,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple)
{
const struct udphdr *hdr = datah;
struct udphdr hdr;
tuple->src.u.udp.port = hdr->source;
tuple->dst.u.udp.port = hdr->dest;
/* Actually only need first 8 bytes. */
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;
}
......@@ -46,7 +51,7 @@ static unsigned int udp_print_conntrack(char *buffer,
/* Returns verdict for packet, and may modify conntracktype */
static int udp_packet(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len,
const struct sk_buff *skb,
enum ip_conntrack_info conntrackinfo)
{
/* 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,
}
/* Called when a new connection for this protocol found. */
static int udp_new(struct ip_conntrack *conntrack,
struct iphdr *iph, size_t len)
static int udp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
{
return 1;
}
......
......@@ -192,10 +192,6 @@ static unsigned int ip_refrag(unsigned int hooknum,
{
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 */
if (ip_confirm(hooknum, pskb, in, out, okfn) != NF_ACCEPT)
return NF_DROP;
......@@ -217,10 +213,6 @@ static unsigned int ip_conntrack_local(unsigned int hooknum,
const struct net_device *out,
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. */
if ((*pskb)->len < sizeof(struct iphdr)
|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
......
......@@ -35,15 +35,18 @@ MODULE_PARM_DESC(ports, "port numbers of tftp servers");
#define DEBUGP(format, args...)
#endif
static int tftp_help(const struct iphdr *iph, size_t len,
static int tftp_help(struct sk_buff *skb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo)
{
struct udphdr *udph = (void *)iph + iph->ihl * 4;
struct tftphdr *tftph = (void *)udph + 8;
struct tftphdr tftph;
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 */
case TFTP_OPCODE_READ:
case TFTP_OPCODE_WRITE:
......
......@@ -146,6 +146,12 @@ check_for_demasq(struct sk_buff **pskb)
server here (== DNAT). Do SNAT icmp manips
in POST_ROUTING handling. */
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,
NF_IP_PRE_ROUTING,
CTINFO2DIR(ctinfo));
......@@ -160,7 +166,7 @@ check_for_demasq(struct sk_buff **pskb)
case IPPROTO_UDP:
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())
printk("ip_fw_compat_masq: Can't get tuple\n");
return NF_ACCEPT;
......
......@@ -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,
struct sk_buff **pskb)
struct sk_buff *skb)
{
struct ip_conntrack_protocol *proto;
int ret = 1;
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)
ret = proto->exp_matches_pkt(exp, pskb);
ret = proto->exp_matches_pkt(exp, skb);
return ret;
}
......@@ -813,7 +813,7 @@ do_bindings(struct ip_conntrack *ct,
if (exp->sibling)
continue;
if (exp_for_packet(exp, pskb)) {
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);
......
......@@ -153,7 +153,7 @@ static void fini(void)
static int __init init(void)
{
int i, ret;
int i, ret = 0;
char *tmpname;
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