X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fip_conntrack_ftp.c;h=3e542bf28a9dff7f27093305ddf3f84bdacba04c;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=df7008989c3813473bd557019424abb73f0efbf8;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c index df7008989..3e542bf28 100644 --- a/net/ipv4/netfilter/ip_conntrack_ftp.c +++ b/net/ipv4/netfilter/ip_conntrack_ftp.c @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -26,18 +25,25 @@ MODULE_AUTHOR("Rusty Russell "); MODULE_DESCRIPTION("ftp connection tracking helper"); /* This is slow, but it's simple. --RR */ -static char ftp_buffer[65536]; - -static DECLARE_LOCK(ip_ftp_lock); -struct module *ip_conntrack_ftp = THIS_MODULE; +static char *ftp_buffer; +static DEFINE_SPINLOCK(ip_ftp_lock); #define MAX_PORTS 8 -static int ports[MAX_PORTS]; +static unsigned short ports[MAX_PORTS]; static int ports_c; -module_param_array(ports, int, ports_c, 0400); +module_param_array(ports, ushort, &ports_c, 0400); static int loose; -module_param(loose, int, 0600); +module_param(loose, bool, 0600); + +unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + enum ip_ct_ftp_type type, + unsigned int matchoff, + unsigned int matchlen, + struct ip_conntrack_expect *exp, + u32 *seq); +EXPORT_SYMBOL_GPL(ip_nat_ftp_hook); #if 0 #define DEBUGP printk @@ -49,7 +55,7 @@ static int try_rfc959(const char *, size_t, u_int32_t [], char); static int try_eprt(const char *, size_t, u_int32_t [], char); static int try_epsv_response(const char *, size_t, u_int32_t [], char); -static struct ftp_search { +static const struct ftp_search { enum ip_conntrack_dir dir; const char *pattern; size_t plen; @@ -243,24 +249,57 @@ static int find_pattern(const char *data, size_t dlen, return 1; } -static int help(struct sk_buff *skb, +/* Look up to see if we're just after a \n. */ +static int find_nl_seq(u32 seq, const struct ip_ct_ftp_master *info, int dir) +{ + unsigned int i; + + for (i = 0; i < info->seq_aft_nl_num[dir]; i++) + if (info->seq_aft_nl[dir][i] == seq) + return 1; + return 0; +} + +/* We don't update if it's older than what we have. */ +static void update_nl_seq(u32 nl_seq, struct ip_ct_ftp_master *info, int dir, + struct sk_buff *skb) +{ + unsigned int i, oldest = NUM_SEQ_TO_REMEMBER; + + /* Look for oldest: if we find exact match, we're done. */ + for (i = 0; i < info->seq_aft_nl_num[dir]; i++) { + if (info->seq_aft_nl[dir][i] == nl_seq) + return; + + if (oldest == info->seq_aft_nl_num[dir] + || before(info->seq_aft_nl[dir][i], oldest)) + oldest = i; + } + + if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) { + info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq; + ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb); + } else if (oldest != NUM_SEQ_TO_REMEMBER) { + info->seq_aft_nl[dir][oldest] = nl_seq; + ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb); + } +} + +static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { unsigned int dataoff, datalen; struct tcphdr _tcph, *th; char *fb_ptr; - u_int32_t old_seq_aft_nl; - int old_seq_aft_nl_set, ret; - u_int32_t array[6] = { 0 }; + int ret; + u32 seq, array[6] = { 0 }; int dir = CTINFO2DIR(ctinfo); unsigned int matchlen, matchoff; struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; struct ip_conntrack_expect *exp; - struct ip_ct_ftp_expect *exp_ftp_info; - unsigned int i; - int found = 0; + int found = 0, ends_in_nl; /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED @@ -269,46 +308,35 @@ static int help(struct sk_buff *skb, return NF_ACCEPT; } - th = skb_header_pointer(skb, skb->nh.iph->ihl*4, + th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_tcph), &_tcph); if (th == NULL) return NF_ACCEPT; - dataoff = skb->nh.iph->ihl*4 + th->doff*4; + dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4; /* No data? */ - if (dataoff >= skb->len) { - DEBUGP("ftp: skblen = %u\n", skb->len); + if (dataoff >= (*pskb)->len) { + DEBUGP("ftp: pskblen = %u\n", (*pskb)->len); return NF_ACCEPT; } - datalen = skb->len - dataoff; + datalen = (*pskb)->len - dataoff; - LOCK_BH(&ip_ftp_lock); - fb_ptr = skb_header_pointer(skb, dataoff, - skb->len - dataoff, ftp_buffer); + spin_lock_bh(&ip_ftp_lock); + fb_ptr = skb_header_pointer(*pskb, dataoff, + (*pskb)->len - dataoff, ftp_buffer); BUG_ON(fb_ptr == NULL); - 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 (fb_ptr[datalen - 1] == '\n') { - DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen); - if (!old_seq_aft_nl_set - || after(ntohl(th->seq) + datalen, old_seq_aft_nl)) { - DEBUGP("conntrack_ftp: updating nl to %u\n", - ntohl(th->seq) + datalen); - ct_ftp_info->seq_aft_nl[dir] = - ntohl(th->seq) + datalen; - ct_ftp_info->seq_aft_nl_set[dir] = 1; - } - } + ends_in_nl = (fb_ptr[datalen - 1] == '\n'); + seq = ntohl(th->seq) + datalen; - if(!old_seq_aft_nl_set || - (ntohl(th->seq) != old_seq_aft_nl)) { - DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u)\n", + /* Look up to see if we're just after a \n. */ + if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) { + /* Now if this ends in \n, update ftp info. */ + DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n", + ct_ftp_info->seq_aft_nl[0][dir] old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl); ret = NF_ACCEPT; - goto out; + goto out_update_nl; } /* Initialize IP array to expected address (it's not mentioned @@ -321,7 +349,7 @@ static int help(struct sk_buff *skb, for (i = 0; i < ARRAY_SIZE(search); i++) { if (search[i].dir != dir) continue; - found = find_pattern(fb_ptr, skb->len - dataoff, + found = find_pattern(fb_ptr, (*pskb)->len - dataoff, search[i].pattern, search[i].plen, search[i].skip, @@ -344,30 +372,26 @@ static int help(struct sk_buff *skb, goto out; } else if (found == 0) { /* No match */ ret = NF_ACCEPT; - goto out; + goto out_update_nl; } - DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n", - (int)matchlen, data + matchoff, - matchlen, ntohl(th->seq) + matchoff); - + DEBUGP("conntrack_ftp: match `%s' (%u bytes at %u)\n", + fb_ptr + matchoff, matchlen, ntohl(th->seq) + matchoff); + /* Allocate expectation which will be inserted */ - exp = ip_conntrack_expect_alloc(); + exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) { - ret = NF_ACCEPT; + ret = NF_DROP; goto out; } - exp_ftp_info = &exp->help.exp_ftp_info; + /* We refer to the reverse direction ("!dir") tuples here, + * because we're expecting something in the other direction. + * Doesn't matter unless NAT is happening. */ + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - /* Update the ftp info */ if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) - == ct->tuplehash[dir].tuple.src.ip) { - exp->seq = ntohl(th->seq) + matchoff; - exp_ftp_info->len = matchlen; - exp_ftp_info->ftptype = search[i].ftptype; - exp_ftp_info->port = array[4] << 8 | array[5]; - } else { + != ct->tuplehash[dir].tuple.src.ip) { /* Enrico Scholz's passive FTP to partially RNAT'd ftp server: it really wants us to connect to a different IP address. Simply don't record it for @@ -382,36 +406,54 @@ static int help(struct sk_buff *skb, networks, or the packet filter itself). */ if (!loose) { ret = NF_ACCEPT; - goto out; + goto out_put_expect; } + exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16) + | (array[2] << 8) | array[3]); } - 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]), - { .tcp = { htons(array[4] << 8 | array[5]) } }, - IPPROTO_TCP }}); + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.dst.u.tcp.port = htons(array[4] << 8 | array[5]); + exp->tuple.src.u.tcp.port = 0; /* Don't care. */ + exp->tuple.dst.protonum = IPPROTO_TCP; exp->mask = ((struct ip_conntrack_tuple) { { 0xFFFFFFFF, { 0 } }, - { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFF }}); exp->expectfn = NULL; + exp->flags = 0; + + /* Now, NAT might want to mangle the packet, and register the + * (possibly changed) expectation itself. */ + if (ip_nat_ftp_hook) + ret = ip_nat_ftp_hook(pskb, ctinfo, search[i].ftptype, + matchoff, matchlen, exp, &seq); + else { + /* Can't expect this? Best to drop packet now. */ + if (ip_conntrack_expect_related(exp) != 0) + ret = NF_DROP; + else + ret = NF_ACCEPT; + } + +out_put_expect: + ip_conntrack_expect_put(exp); - /* Ignore failure; should only happen with NAT */ - ip_conntrack_expect_related(exp, ct); - ret = NF_ACCEPT; +out_update_nl: + /* Now if this ends in \n, update ftp info. Seq may have been + * adjusted by NAT code. */ + if (ends_in_nl) + update_nl_seq(seq, ct_ftp_info,dir, *pskb); out: - UNLOCK_BH(&ip_ftp_lock); + spin_unlock_bh(&ip_ftp_lock); return ret; } static struct ip_conntrack_helper ftp[MAX_PORTS]; -static char ftp_names[MAX_PORTS][10]; +static char ftp_names[MAX_PORTS][sizeof("ftp-65535")]; /* Not __exit: called from init() */ -static void fini(void) +static void ip_conntrack_ftp_fini(void) { int i; for (i = 0; i < ports_c; i++) { @@ -419,13 +461,19 @@ static void fini(void) ports[i]); ip_conntrack_helper_unregister(&ftp[i]); } + + kfree(ftp_buffer); } -static int __init init(void) +static int __init ip_conntrack_ftp_init(void) { int i, ret; char *tmpname; + ftp_buffer = kmalloc(65536, GFP_KERNEL); + if (!ftp_buffer) + return -ENOMEM; + if (ports_c == 0) ports[ports_c++] = FTP_PORT; @@ -433,11 +481,10 @@ static int __init init(void) ftp[i].tuple.src.u.tcp.port = htons(ports[i]); ftp[i].tuple.dst.protonum = IPPROTO_TCP; ftp[i].mask.src.u.tcp.port = 0xFFFF; - ftp[i].mask.dst.protonum = 0xFFFF; + ftp[i].mask.dst.protonum = 0xFF; 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].timeout = 5 * 60; /* 5 minutes */ + ftp[i].me = THIS_MODULE; ftp[i].help = help; tmpname = &ftp_names[i][0]; @@ -452,14 +499,12 @@ static int __init init(void) ret = ip_conntrack_helper_register(&ftp[i]); if (ret) { - fini(); + ip_conntrack_ftp_fini(); return ret; } } return 0; } -PROVIDES_CONNTRACK(ftp); - -module_init(init); -module_exit(fini); +module_init(ip_conntrack_ftp_init); +module_exit(ip_conntrack_ftp_fini);