Merge git://oss.sgi.com:8090/xfs/xfs-2.6
[linux-drm-fsl-dcu.git] / net / ipv4 / netfilter / ip_conntrack_amanda.c
index a604b1ccfdaa8c83b86f00744296f26afad07d98..4f561f52c83a52c42baea4019023f6d641cd4c93 100644 (file)
@@ -9,7 +9,7 @@
  *
  *     Module load syntax:
  *     insmod ip_conntrack_amanda.o [master_timeout=n]
- *     
+ *
  *     Where master_timeout is the timeout (in seconds) of the master
  *     connection (port 10080).  This defaults to 5 minutes but if
  *     your clients take longer than 5 minutes to do their work
  *     this value.
  *
  */
-
-#include <linux/in.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/netfilter.h>
-#include <linux/ip.h>
 #include <linux/moduleparam.h>
+#include <linux/textsearch.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
 #include <linux/udp.h>
-#include <net/checksum.h>
-#include <net/udp.h>
 
+#include <linux/netfilter.h>
 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
 #include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
 
 static unsigned int master_timeout = 300;
+static char *ts_algo = "kmp";
 
 MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
 MODULE_DESCRIPTION("Amanda connection tracking module");
 MODULE_LICENSE("GPL");
 module_param(master_timeout, uint, 0600);
 MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
-
-static const char *conns[] = { "DATA ", "MESG ", "INDEX " };
-
-/* This is slow, but it's simple. --RR */
-static char *amanda_buffer;
-static DEFINE_SPINLOCK(amanda_buffer_lock);
+module_param(ts_algo, charp, 0400);
+MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)");
 
 unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
                                   enum ip_conntrack_info ctinfo,
@@ -52,14 +48,51 @@ unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
                                   struct ip_conntrack_expect *exp);
 EXPORT_SYMBOL_GPL(ip_nat_amanda_hook);
 
+enum amanda_strings {
+       SEARCH_CONNECT,
+       SEARCH_NEWLINE,
+       SEARCH_DATA,
+       SEARCH_MESG,
+       SEARCH_INDEX,
+};
+
+static struct {
+       char                    *string;
+       size_t                  len;
+       struct ts_config        *ts;
+} search[] = {
+       [SEARCH_CONNECT] = {
+               .string = "CONNECT ",
+               .len    = 8,
+       },
+       [SEARCH_NEWLINE] = {
+               .string = "\n",
+               .len    = 1,
+       },
+       [SEARCH_DATA] = {
+               .string = "DATA ",
+               .len    = 5,
+       },
+       [SEARCH_MESG] = {
+               .string = "MESG ",
+               .len    = 5,
+       },
+       [SEARCH_INDEX] = {
+               .string = "INDEX ",
+               .len    = 6,
+       },
+};
+
 static int help(struct sk_buff **pskb,
-                struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
+               struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
 {
+       struct ts_state ts;
        struct ip_conntrack_expect *exp;
-       char *data, *data_limit, *tmp;
-       unsigned int dataoff, i;
+       unsigned int dataoff, start, stop, off, i;
+       char pbuf[sizeof("65535")], *tmp;
        u_int16_t port, len;
        int ret = NF_ACCEPT;
+       typeof(ip_nat_amanda_hook) ip_nat_amanda;
 
        /* Only look at packets from the Amanda server */
        if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
@@ -77,29 +110,34 @@ static int help(struct sk_buff **pskb,
                return NF_ACCEPT;
        }
 
-       spin_lock_bh(&amanda_buffer_lock);
-       skb_copy_bits(*pskb, dataoff, amanda_buffer, (*pskb)->len - dataoff);
-       data = amanda_buffer;
-       data_limit = amanda_buffer + (*pskb)->len - dataoff;
-       *data_limit = '\0';
-
-       /* Search for the CONNECT string */
-       data = strstr(data, "CONNECT ");
-       if (!data)
+       memset(&ts, 0, sizeof(ts));
+       start = skb_find_text(*pskb, dataoff, (*pskb)->len,
+                             search[SEARCH_CONNECT].ts, &ts);
+       if (start == UINT_MAX)
                goto out;
-       data += strlen("CONNECT ");
+       start += dataoff + search[SEARCH_CONNECT].len;
 
-       /* Only search first line. */   
-       if ((tmp = strchr(data, '\n')))
-               *tmp = '\0';
+       memset(&ts, 0, sizeof(ts));
+       stop = skb_find_text(*pskb, start, (*pskb)->len,
+                            search[SEARCH_NEWLINE].ts, &ts);
+       if (stop == UINT_MAX)
+               goto out;
+       stop += start;
 
-       for (i = 0; i < ARRAY_SIZE(conns); i++) {
-               char *match = strstr(data, conns[i]);
-               if (!match)
+       for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) {
+               memset(&ts, 0, sizeof(ts));
+               off = skb_find_text(*pskb, start, stop, search[i].ts, &ts);
+               if (off == UINT_MAX)
                        continue;
-               tmp = data = match + strlen(conns[i]);
-               port = simple_strtoul(data, &data, 10);
-               len = data - tmp;
+               off += start + search[i].len;
+
+               len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off);
+               if (skb_copy_bits(*pskb, off, pbuf, len))
+                       break;
+               pbuf[len] = '\0';
+
+               port = simple_strtoul(pbuf, &tmp, 10);
+               len = tmp - pbuf;
                if (port == 0 || len > 5)
                        break;
 
@@ -118,34 +156,34 @@ static int help(struct sk_buff **pskb,
                exp->tuple.dst.protonum = IPPROTO_TCP;
                exp->tuple.dst.u.tcp.port = htons(port);
 
-               exp->mask.src.ip = 0xFFFFFFFF;
+               exp->mask.src.ip = htonl(0xFFFFFFFF);
                exp->mask.src.u.tcp.port = 0;
-               exp->mask.dst.ip = 0xFFFFFFFF;
+               exp->mask.dst.ip = htonl(0xFFFFFFFF);
                exp->mask.dst.protonum = 0xFF;
-               exp->mask.dst.u.tcp.port = 0xFFFF;
+               exp->mask.dst.u.tcp.port = htons(0xFFFF);
 
-               if (ip_nat_amanda_hook)
-                       ret = ip_nat_amanda_hook(pskb, ctinfo,
-                                                tmp - amanda_buffer,
-                                                len, exp);
+               /* RCU read locked by nf_hook_slow */
+               ip_nat_amanda = rcu_dereference(ip_nat_amanda_hook);
+               if (ip_nat_amanda)
+                       ret = ip_nat_amanda(pskb, ctinfo, off - dataoff,
+                                           len, exp);
                else if (ip_conntrack_expect_related(exp) != 0)
                        ret = NF_DROP;
                ip_conntrack_expect_put(exp);
        }
 
 out:
-       spin_unlock_bh(&amanda_buffer_lock);
        return ret;
 }
 
 static struct ip_conntrack_helper amanda_helper = {
-       .max_expected = ARRAY_SIZE(conns),
+       .max_expected = 3,
        .timeout = 180,
        .me = THIS_MODULE,
        .help = help,
        .name = "amanda",
 
-       .tuple = { .src = { .u = { __constant_htons(10080) } },
+       .tuple = { .src = { .u = { .udp = {.port = __constant_htons(10080) } } },
                   .dst = { .protonum = IPPROTO_UDP },
        },
        .mask = { .src = { .u = { 0xFFFF } },
@@ -155,26 +193,36 @@ static struct ip_conntrack_helper amanda_helper = {
 
 static void __exit ip_conntrack_amanda_fini(void)
 {
+       int i;
+
        ip_conntrack_helper_unregister(&amanda_helper);
-       kfree(amanda_buffer);
+       for (i = 0; i < ARRAY_SIZE(search); i++)
+               textsearch_destroy(search[i].ts);
 }
 
 static int __init ip_conntrack_amanda_init(void)
 {
-       int ret;
-
-       amanda_buffer = kmalloc(65536, GFP_KERNEL);
-       if (!amanda_buffer)
-               return -ENOMEM;
-
-       ret = ip_conntrack_helper_register(&amanda_helper);
-       if (ret < 0) {
-               kfree(amanda_buffer);
-               return ret;
+       int ret, i;
+
+       ret = -ENOMEM;
+       for (i = 0; i < ARRAY_SIZE(search); i++) {
+               search[i].ts = textsearch_prepare(ts_algo, search[i].string,
+                                                 search[i].len,
+                                                 GFP_KERNEL, TS_AUTOLOAD);
+               if (search[i].ts == NULL)
+                       goto err;
        }
+       ret = ip_conntrack_helper_register(&amanda_helper);
+       if (ret < 0)
+               goto err;
        return 0;
 
-
+err:
+       for (; i >= 0; i--) {
+               if (search[i].ts)
+                       textsearch_destroy(search[i].ts);
+       }
+       return ret;
 }
 
 module_init(ip_conntrack_amanda_init);