fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_ftp.c
index dd86503..0410c99 100644 (file)
@@ -8,7 +8,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/netfilter.h>
 #include <linux/ip.h>
@@ -16,7 +15,6 @@
 #include <net/checksum.h>
 #include <net/tcp.h>
 
-#include <linux/netfilter_ipv4/lockhelp.h>
 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
 #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
 #include <linux/moduleparam.h>
@@ -26,17 +24,16 @@ MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
 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);
+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,
@@ -57,38 +54,49 @@ 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 {
-       enum ip_conntrack_dir dir;
+static const struct ftp_search {
        const char *pattern;
        size_t plen;
        char skip;
        char term;
        enum ip_ct_ftp_type ftptype;
        int (*getnum)(const char *, size_t, u_int32_t[], char);
-} search[] = {
-       {
-               IP_CT_DIR_ORIGINAL,
-               "PORT", sizeof("PORT") - 1, ' ', '\r',
-               IP_CT_FTP_PORT,
-               try_rfc959,
-       },
-       {
-               IP_CT_DIR_REPLY,
-               "227 ", sizeof("227 ") - 1, '(', ')',
-               IP_CT_FTP_PASV,
-               try_rfc959,
-       },
-       {
-               IP_CT_DIR_ORIGINAL,
-               "EPRT", sizeof("EPRT") - 1, ' ', '\r',
-               IP_CT_FTP_EPRT,
-               try_eprt,
+} search[IP_CT_DIR_MAX][2] = {
+       [IP_CT_DIR_ORIGINAL] = {
+               {
+                       .pattern        =  "PORT",
+                       .plen           = sizeof("PORT") - 1,
+                       .skip           = ' ',
+                       .term           = '\r',
+                       .ftptype        = IP_CT_FTP_PORT,
+                       .getnum         = try_rfc959,
+               },
+               {
+                       .pattern        = "EPRT",
+                       .plen           = sizeof("EPRT") - 1,
+                       .skip           = ' ',
+                       .term           = '\r',
+                       .ftptype        = IP_CT_FTP_EPRT,
+                       .getnum         = try_eprt,
+               },
        },
-       {
-               IP_CT_DIR_REPLY,
-               "229 ", sizeof("229 ") - 1, '(', ')',
-               IP_CT_FTP_EPSV,
-               try_epsv_response,
+       [IP_CT_DIR_REPLY] = {
+               {
+                       .pattern        = "227 ",
+                       .plen           = sizeof("227 ") - 1,
+                       .skip           = '(',
+                       .term           = ')',
+                       .ftptype        = IP_CT_FTP_PASV,
+                       .getnum         = try_rfc959,
+               },
+               {
+                       .pattern        = "229 ",
+                       .plen           = sizeof("229 ") - 1,
+                       .skip           = '(',
+                       .term           = ')',
+                       .ftptype        = IP_CT_FTP_EPSV,
+                       .getnum         = try_epsv_response,
+               },
        },
 };
 
@@ -263,7 +271,8 @@ static int find_nl_seq(u32 seq, const struct ip_ct_ftp_master *info, int dir)
 }
 
 /* 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)
+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;
 
@@ -277,10 +286,13 @@ static void update_nl_seq(u32 nl_seq, struct ip_ct_ftp_master *info, int dir)
                        oldest = i;
        }
 
-       if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER)
+       if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) {
                info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq;
-       else if (oldest != NUM_SEQ_TO_REMEMBER)
+               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,
@@ -298,6 +310,7 @@ static int help(struct sk_buff **pskb,
        struct ip_conntrack_expect *exp;
        unsigned int i;
        int found = 0, ends_in_nl;
+       typeof(ip_nat_ftp_hook) ip_nat_ftp;
 
        /* Until there's been traffic both ways, don't look in packets. */
        if (ctinfo != IP_CT_ESTABLISHED
@@ -319,7 +332,7 @@ static int help(struct sk_buff **pskb,
        }
        datalen = (*pskb)->len - dataoff;
 
-       LOCK_BH(&ip_ftp_lock);
+       spin_lock_bh(&ip_ftp_lock);
        fb_ptr = skb_header_pointer(*pskb, dataoff,
                                    (*pskb)->len - dataoff, ftp_buffer);
        BUG_ON(fb_ptr == NULL);
@@ -344,17 +357,15 @@ static int help(struct sk_buff **pskb,
        array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF;
        array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF;
 
-       for (i = 0; i < ARRAY_SIZE(search); i++) {
-               if (search[i].dir != dir) continue;
-
+       for (i = 0; i < ARRAY_SIZE(search[dir]); i++) {
                found = find_pattern(fb_ptr, (*pskb)->len - dataoff,
-                                    search[i].pattern,
-                                    search[i].plen,
-                                    search[i].skip,
-                                    search[i].term,
+                                    search[dir][i].pattern,
+                                    search[dir][i].plen,
+                                    search[dir][i].skip,
+                                    search[dir][i].term,
                                     &matchoff, &matchlen,
                                     array,
-                                    search[i].getnum);
+                                    search[dir][i].getnum);
                if (found) break;
        }
        if (found == -1) {
@@ -364,7 +375,7 @@ static int help(struct sk_buff **pskb,
                   this case. */
                if (net_ratelimit())
                        printk("conntrack_ftp: partial %s %u+%u\n",
-                              search[i].pattern,
+                              search[dir][i].pattern,
                               ntohl(th->seq), datalen);
                ret = NF_DROP;
                goto out;
@@ -377,7 +388,7 @@ static int help(struct sk_buff **pskb,
               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_DROP;
                goto out;
@@ -404,8 +415,7 @@ static int help(struct sk_buff **pskb,
                   networks, or the packet filter itself). */
                if (!loose) {
                        ret = NF_ACCEPT;
-                       ip_conntrack_expect_free(exp);
-                       goto out_update_nl;
+                       goto out_put_expect;
                }
                exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16)
                                         | (array[2] << 8) | array[3]);
@@ -416,41 +426,44 @@ static int help(struct sk_buff **pskb,
        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 } }, 0xFF }});
+               { { htonl(0xFFFFFFFF), { 0 } },
+                 { htonl(0xFFFFFFFF), { .tcp = { htons(0xFFFF) } }, 0xFF }});
 
        exp->expectfn = NULL;
-       exp->master = ct;
+       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);
+       ip_nat_ftp = rcu_dereference(ip_nat_ftp_hook);
+       if (ip_nat_ftp)
+               ret = ip_nat_ftp(pskb, ctinfo, search[dir][i].ftptype,
+                                matchoff, matchlen, exp, &seq);
        else {
                /* Can't expect this?  Best to drop packet now. */
-               if (ip_conntrack_expect_related(exp) != 0) {
-                       ip_conntrack_expect_free(exp);
+               if (ip_conntrack_expect_related(exp) != 0)
                        ret = NF_DROP;
-               else
+               else
                        ret = NF_ACCEPT;
        }
 
+out_put_expect:
+       ip_conntrack_expect_put(exp);
+
 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);
+               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++) {
@@ -458,20 +471,26 @@ 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;
 
        for (i = 0; i < ports_c; i++) {
                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.src.u.tcp.port = htons(0xFFFF);
                ftp[i].mask.dst.protonum = 0xFF;
                ftp[i].max_expected = 1;
                ftp[i].timeout = 5 * 60; /* 5 minutes */
@@ -490,12 +509,12 @@ static int __init init(void)
                ret = ip_conntrack_helper_register(&ftp[i]);
 
                if (ret) {
-                       fini();
+                       ip_conntrack_ftp_fini();
                        return ret;
                }
        }
        return 0;
 }
 
-module_init(init);
-module_exit(fini);
+module_init(ip_conntrack_ftp_init);
+module_exit(ip_conntrack_ftp_fini);