fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_amanda.c
index 4e8f4d8..ad246ba 100644 (file)
  *     this value.
  *
  */
-
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/netfilter.h>
+#include <linux/moduleparam.h>
+#include <linux/textsearch.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
 #include <linux/ip.h>
-#include <net/checksum.h>
-#include <net/udp.h>
+#include <linux/udp.h>
 
-#include <linux/netfilter_ipv4/lockhelp.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_PARM(master_timeout, "i");
+module_param(master_timeout, uint, 0600);
 MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
+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,
+                                  unsigned int matchoff,
+                                  unsigned int matchlen,
+                                  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 char *conns[] = { "DATA ", "MESG ", "INDEX " };
-
-/* This is slow, but it's simple. --RR */
-static char amanda_buffer[65536];
-static DECLARE_LOCK(amanda_buffer_lock);
+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 *skb,
+static int help(struct sk_buff **pskb,
                 struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
 {
+       struct ts_state ts;
        struct ip_conntrack_expect *exp;
-       struct ip_ct_amanda_expect *exp_amanda_info;
-       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)
@@ -58,95 +100,130 @@ static int help(struct sk_buff *skb,
 
        /* increase the UDP timeout of the master connection as replies from
         * Amanda clients to the server can be quite delayed */
-       ip_ct_refresh(ct, master_timeout * HZ);
+       ip_ct_refresh(ct, *pskb, master_timeout * HZ);
 
        /* No data? */
-       dataoff = skb->nh.iph->ihl*4 + sizeof(struct udphdr);
-       if (dataoff >= skb->len) {
+       dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+       if (dataoff >= (*pskb)->len) {
                if (net_ratelimit())
-                       printk("amanda_help: skblen = %u\n", skb->len);
+                       printk("amanda_help: skblen = %u\n", (*pskb)->len);
                return NF_ACCEPT;
        }
 
-       LOCK_BH(&amanda_buffer_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 */
-       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;
 
-               exp = ip_conntrack_expect_alloc();
-               if (exp == NULL)
+               exp = ip_conntrack_expect_alloc(ct);
+               if (exp == NULL) {
+                       ret = NF_DROP;
                        goto out;
+               }
+
+               exp->expectfn = NULL;
+               exp->flags = 0;
 
                exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+               exp->tuple.src.u.tcp.port = 0;
                exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
                exp->tuple.dst.protonum = IPPROTO_TCP;
-               exp->mask.src.ip = 0xFFFFFFFF;
-               exp->mask.dst.ip = 0xFFFFFFFF;
-               exp->mask.dst.protonum = 0xFFFF;
-               exp->mask.dst.u.tcp.port = 0xFFFF;
-
-               exp_amanda_info = &exp->help.exp_amanda_info;
-               exp_amanda_info->offset = tmp - amanda_buffer;
-               exp_amanda_info->port   = port;
-               exp_amanda_info->len    = len;
-
                exp->tuple.dst.u.tcp.port = htons(port);
 
-               ip_conntrack_expect_related(exp, ct);
+               exp->mask.src.ip = htonl(0xFFFFFFFF);
+               exp->mask.src.u.tcp.port = 0;
+               exp->mask.dst.ip = htonl(0xFFFFFFFF);
+               exp->mask.dst.protonum = 0xFF;
+               exp->mask.dst.u.tcp.port = htons(0xFFFF);
+
+               /* 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:
-       UNLOCK_BH(&amanda_buffer_lock);
-       return NF_ACCEPT;
+       return ret;
 }
 
 static struct ip_conntrack_helper amanda_helper = {
-       .max_expected = ARRAY_SIZE(conns),
+       .max_expected = 3,
        .timeout = 180,
-       .flags = IP_CT_HELPER_F_REUSE_EXPECT,
        .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 } },
-                .dst = { .protonum = 0xFFFF },
+                .dst = { .protonum = 0xFF },
        },
 };
 
-static void __exit fini(void)
+static void __exit ip_conntrack_amanda_fini(void)
 {
+       int i;
+
        ip_conntrack_helper_unregister(&amanda_helper);
+       for (i = 0; i < ARRAY_SIZE(search); i++)
+               textsearch_destroy(search[i].ts);
 }
 
-static int __init init(void)
+static int __init ip_conntrack_amanda_init(void)
 {
-       return ip_conntrack_helper_register(&amanda_helper);
+       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;
 }
 
-PROVIDES_CONNTRACK(amanda);
-module_init(init);
-module_exit(fini);
+module_init(ip_conntrack_amanda_init);
+module_exit(ip_conntrack_amanda_fini);