added files
[bcm963xx.git] / kernel / linux / net / ipv4 / netfilter / ip_conntrack_h323.c
index 133eef9..a1b81da 100755 (executable)
@@ -1,6 +1,7 @@
-/* 
- * H.323 'brute force' extension for H.323 connection tracking. 
+/*
+ * H.323 'brute force' extension for H.323 connection tracking.
  * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * (c) 2005 Max Kellermann <max@duempel.org>
  *
  * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project.
  * (http://www.coritel.it/projects/sofia/nat/)
@@ -26,6 +27,10 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
 MODULE_LICENSE("GPL");
 
+/* This is slow, but it's simple. --RR */
+static char h323_buffer[65536];
+static DECLARE_LOCK(h323_buffer_lock);
+
 DECLARE_LOCK(ip_h323_lock);
 struct module *ip_conntrack_h323 = THIS_MODULE;
 
@@ -40,27 +45,20 @@ static int h245_help(struct sk_buff *skb,
                     struct ip_conntrack *ct,
                     enum ip_conntrack_info ctinfo)
 {
-       struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl * 4;
-       unsigned char *data = (unsigned char *) tcph + tcph->doff * 4;
+       struct iphdr *iph = skb->nh.iph;
+       struct tcphdr _tcph, *tcph;
+       unsigned char *data;
        unsigned char *data_limit;
-       u_int32_t tcplen = skb->len - skb->nh.iph->ihl * 4;
-       u_int32_t datalen = tcplen - tcph->doff * 4;
+       unsigned dataoff, datalen;
        int dir = CTINFO2DIR(ctinfo);
        struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
-       struct ip_conntrack_expect expect, *exp = &expect;
-       struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info;
+       struct ip_conntrack_expect *exp;
+       struct ip_ct_h225_expect *exp_info;
        u_int16_t data_port;
        u_int32_t data_ip;
        unsigned int i;
+       int ret;
 
-       DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
-               NIPQUAD(iph->saddr), ntohs(tcph->source),
-               NIPQUAD(iph->daddr), ntohs(tcph->dest));
-
-       /* Can't track connections formed before we registered */
-       if (!info)
-               return NF_ACCEPT;
-               
        /* Until there's been traffic both ways, don't look in packets. */
        if (ctinfo != IP_CT_ESTABLISHED
            && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
@@ -68,34 +66,49 @@ static int h245_help(struct sk_buff *skb,
                return NF_ACCEPT;
        }
 
-       /* Not whole TCP header or too short packet? */
-       if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) {
-               DEBUGP("ct_h245_help: tcplen = %u\n", (unsigned)tcplen);
+       tcph = skb_header_pointer(skb, skb->nh.iph->ihl*4,
+                                 sizeof(_tcph), &_tcph);
+       if (tcph == NULL)
                return NF_ACCEPT;
-       }
 
-       /* Checksum invalid?  Ignore. */
-       /* FIXME: Source route IP option packets --RR */
-       if (tcp_v4_check(tcph, tcplen, skb->nh.iph->saddr, skb->nh.iph->daddr,
-                             csum_partial((char *)tcph, tcplen, 0))) {
-               DEBUGP("ct_h245_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
-                      tcph, tcplen, NIPQUAD(skb->nh.iph->saddr),
-                      NIPQUAD(skb->nh.iph->daddr));
+       DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
+               NIPQUAD(iph->saddr), ntohs(tcph->source),
+               NIPQUAD(iph->daddr), ntohs(tcph->dest));
+
+       dataoff = skb->nh.iph->ihl*4 + tcph->doff*4;
+       /* No data? */
+       if (dataoff >= skb->len) {
+               DEBUGP("ct_h245_help: skblen = %u\n", skb->len);
                return NF_ACCEPT;
        }
+       datalen = skb->len - dataoff;
+
+       LOCK_BH(&h323_buffer_lock);
+       data = skb_header_pointer(skb, dataoff,
+                                 datalen, h323_buffer);
+       BUG_ON(data == NULL);
 
-       data_limit = (unsigned char *) data + datalen;
+       data_limit = data + datalen - 6;
        /* bytes: 0123   45
                  ipadrr port */
-       for (i = 0; data < (data_limit - 5); data++, i++) {
+       for (i = 0; data <= data_limit; data++, i++) {
                data_ip = *((u_int32_t *)data);
-               if (data_ip == skb->nh.iph->saddr) {
+               if (data_ip == iph->saddr) {
                        data_port = *((u_int16_t *)(data + 4));
-                       memset(&expect, 0, sizeof(expect));
+
                        /* update the H.225 info */
                        DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n",
                                NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
-                               NIPQUAD(skb->nh.iph->saddr), ntohs(data_port));
+                               NIPQUAD(iph->saddr), ntohs(data_port));
+
+                       exp = ip_conntrack_expect_alloc();
+                       if (exp == NULL) {
+                               ret = NF_ACCEPT;
+                               goto out;
+                       }
+
+                       exp_info = &exp->help.exp_h225_info;
+
                        LOCK_BH(&ip_h323_lock);
                        info->is_h225 = H225_PORT + 1;
                        exp_info->port = data_port;
@@ -103,7 +116,7 @@ static int h245_help(struct sk_buff *skb,
                        exp_info->offset = i;
 
                        exp->seq = ntohl(tcph->seq) + i;
-                   
+
                        exp->tuple = ((struct ip_conntrack_tuple)
                                { { ct->tuplehash[!dir].tuple.src.ip,
                                    { 0 } },
@@ -113,9 +126,9 @@ static int h245_help(struct sk_buff *skb,
                        exp->mask = ((struct ip_conntrack_tuple)
                                { { 0xFFFFFFFF, { 0 } },
                                  { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
-       
+
                        exp->expectfn = NULL;
-                       
+
                        /* Ignore failure; should only happen with NAT */
                        ip_conntrack_expect_related(exp, ct);
 
@@ -123,24 +136,23 @@ static int h245_help(struct sk_buff *skb,
                }
        }
 
-       return NF_ACCEPT;
-
+       ret = NF_ACCEPT;
+ out:
+       UNLOCK_BH(&h323_buffer_lock);
+       return ret;
 }
 
 /* H.245 helper is not registered! */
-static struct ip_conntrack_helper h245 = 
-       { { NULL, NULL },
-          name : "H.245",                              /* name */
-          flags: IP_CT_HELPER_F_REUSE_EXPECT,          /* flags */
-          me: THIS_MODULE,                                     /* module */
-          max_expected: 8,                                     /* max_ expected */
-          timeout: 240,                                        /* timeout */
-          tuple: { { 0, { 0 } },                       /* tuple */
-            { 0, { 0 }, IPPROTO_TCP } },
-          mask: { { 0, { 0xFFFF } },                   /* mask */
-            { 0, { 0 }, 0xFFFF } },
-          h245_help                            /* helper */
-       };
+static struct ip_conntrack_helper h245 = {
+       .name = "H.245",
+       .flags = IP_CT_HELPER_F_REUSE_EXPECT,
+       .max_expected = 8,
+       .timeout = 240,
+       .tuple = { .dst = { .protonum = IPPROTO_TCP } },
+       .mask = { .src = { .u = { 0xFFFF } },
+                 .dst = { .protonum = 0xFFFF } },
+       .help = h245_help
+};
 
 static int h225_expect(struct ip_conntrack *ct)
 {
@@ -148,7 +160,7 @@ static int h225_expect(struct ip_conntrack *ct)
        ct->helper = &h245;
        DEBUGP("h225_expect: helper for %p added\n", ct);
        WRITE_UNLOCK(&ip_conntrack_lock);
-       
+
        return NF_ACCEPT;       /* unused */
 }
 
@@ -157,26 +169,19 @@ static int h225_help(struct sk_buff *skb,
                     struct ip_conntrack *ct,
                     enum ip_conntrack_info ctinfo)
 {
-       struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl * 4;
-       unsigned char *data = (unsigned char *) tcph + tcph->doff * 4;
+       struct iphdr *iph = skb->nh.iph;
+       struct tcphdr _tcph, *tcph;
+       unsigned char *data;
        unsigned char *data_limit;
-       u_int32_t tcplen = skb->len - skb->nh.iph->ihl * 4;
-       u_int32_t datalen = tcplen - tcph->doff * 4;
+       unsigned dataoff, datalen;
        int dir = CTINFO2DIR(ctinfo);
        struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
-       struct ip_conntrack_expect expect, *exp = &expect;
-       struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info;
+       struct ip_conntrack_expect *exp;
+       struct ip_ct_h225_expect *exp_info;
        u_int16_t data_port;
        u_int32_t data_ip;
        unsigned int i;
-       
-       DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
-               NIPQUAD(skb->nh.iph->saddr), ntohs(tcph->source),
-               NIPQUAD(skb->nh.iph->daddr), ntohs(tcph->dest));
-
-       /* Can't track connections formed before we registered */
-       if (!info)
-               return NF_ACCEPT;
+       int ret;
 
        /* Until there's been traffic both ways, don't look in packets. */
        if (ctinfo != IP_CT_ESTABLISHED
@@ -185,33 +190,39 @@ static int h225_help(struct sk_buff *skb,
                return NF_ACCEPT;
        }
 
-       /* Not whole TCP header or too short packet? */
-       if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) {
-               DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen);
+       tcph = skb_header_pointer(skb, skb->nh.iph->ihl*4,
+                                 sizeof(_tcph), &_tcph);
+       if (tcph == NULL)
                return NF_ACCEPT;
-       }
 
-       /* Checksum invalid?  Ignore. */
-       /* FIXME: Source route IP option packets --RR */
-       if (tcp_v4_check(tcph, tcplen, skb->nh.iph->saddr, skb->nh.iph->daddr,
-                             csum_partial((char *)tcph, tcplen, 0))) {
-               DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
-                      tcph, tcplen, NIPQUAD(skb->nh.iph->saddr),
-                      NIPQUAD(skb->nh.iph->daddr));
+       DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
+               NIPQUAD(iph->saddr), ntohs(tcph->source),
+               NIPQUAD(iph->daddr), ntohs(tcph->dest));
+
+       dataoff = skb->nh.iph->ihl*4 + tcph->doff*4;
+       /* No data? */
+       if (dataoff >= skb->len) {
+               DEBUGP("ct_h225_help: skblen = %u\n", skb->len);
                return NF_ACCEPT;
        }
-       
-       data_limit = (unsigned char *) data + datalen;
+       datalen = skb->len - dataoff;
+
+       LOCK_BH(&h323_buffer_lock);
+       data = skb_header_pointer(skb, dataoff,
+                                 datalen, h323_buffer);
+       BUG_ON(data == NULL);
+
+       data_limit = data + datalen - 6;
        /* bytes: 0123   45
                  ipadrr port */
-       for (i = 0; data < (data_limit - 5); data++, i++) {
+       for (i = 0; data <= data_limit; data++, i++) {
                data_ip = *((u_int32_t *)data);
-               if (data_ip == skb->nh.iph->saddr) {
+               if (data_ip == iph->saddr) {
                        data_port = *((u_int16_t *)(data + 4));
                        if (data_port == tcph->source) {
                                /* Signal address */
                                DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n",
-                                       NIPQUAD(skb->nh.iph->saddr));
+                                       NIPQUAD(iph->saddr));
                                /* Update the H.225 info so that NAT can mangle the address/port
                                   even when we have no expected connection! */
 #ifdef CONFIG_IP_NF_NAT_NEEDED
@@ -222,9 +233,15 @@ static int h225_help(struct sk_buff *skb,
                                UNLOCK_BH(&ip_h323_lock);
 #endif
                        } else {
-                               memset(&expect, 0, sizeof(expect));
-
                                /* update the H.225 info */
+                               exp = ip_conntrack_expect_alloc();
+                               if (exp == NULL) {
+                                       ret = NF_ACCEPT;
+                                       goto out;
+                               }
+
+                               exp_info = &exp->help.exp_h225_info;
+
                                LOCK_BH(&ip_h323_lock);
                                info->is_h225 = H225_PORT;
                                exp_info->port = data_port;
@@ -242,25 +259,25 @@ static int h225_help(struct sk_buff *skb,
                                exp->mask = ((struct ip_conntrack_tuple)
                                        { { 0xFFFFFFFF, { 0 } },
                                          { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
-       
+
                                exp->expectfn = h225_expect;
-                               
+
                                /* Ignore failure */
                                ip_conntrack_expect_related(exp, ct);
 
                                DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n",
                                        NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
-                                       NIPQUAD(skb->nh.iph->saddr), ntohs(data_port));
+                                       NIPQUAD(iph->saddr), ntohs(data_port));
 
                                UNLOCK_BH(&ip_h323_lock);
-                       }  
+                       }
 #ifdef CONFIG_IP_NF_NAT_NEEDED
-               } else if (data_ip == skb->nh.iph->daddr) {
+               } else if (data_ip == iph->daddr) {
                        data_port = *((u_int16_t *)(data + 4));
                        if (data_port == tcph->dest) {
                                /* Signal address */
                                DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n",
-                                       NIPQUAD(skb->nh.iph->daddr));
+                                       NIPQUAD(iph->daddr));
                                /* Update the H.225 info so that NAT can mangle the address/port
                                   even when we have no expected connection! */
                                LOCK_BH(&ip_h323_lock);
@@ -273,36 +290,53 @@ static int h225_help(struct sk_buff *skb,
                }
        }
 
-       return NF_ACCEPT;
-
+       ret = NF_ACCEPT;
+ out:
+       UNLOCK_BH(&h323_buffer_lock);
+       return ret;
 }
 
-static struct ip_conntrack_helper h225 = 
-       { { NULL, NULL },
-         "H.225",                                      /* name */
-         IP_CT_HELPER_F_REUSE_EXPECT,                  /* flags */
-         THIS_MODULE,                                  /* module */
-         2,                                            /* max_expected */
-         240,                                          /* timeout */
-         { { 0, { __constant_htons(H225_PORT) } },     /* tuple */
-           { 0, { 0 }, IPPROTO_TCP } },
-         { { 0, { 0xFFFF } },                          /* mask */
-           { 0, { 0 }, 0xFFFF } },
-         h225_help                                     /* helper */
-       };
+static struct ip_conntrack_helper h225 = {
+       .name = "H.225",
+       .flags = IP_CT_HELPER_F_REUSE_EXPECT,
+       .me = THIS_MODULE,
+       .max_expected = 2,
+       .timeout = 240,
+       .tuple = { .src = { .ip = 0, 
+                           .u = { .tcp = { .port =  
+                                   __constant_htons(H225_PORT) } } 
+                         }, 
+                  .dst = { .ip = 0, 
+                           .u = { .all = 0 },
+                           .protonum = IPPROTO_TCP
+                         } 
+                },
+       .mask = { .src = { .ip = 0, 
+                          .u = { .tcp = { .port = 0xffff } } 
+                        }, 
+                 .dst = { .ip = 0, 
+                          .u = { .all = 0 },
+                          .protonum = 0xffff 
+                        } 
+               },
+       .help = h225_help
+};
 
 static int __init init(void)
 {
+       printk("ip_conntrack_h323: init \n");
        return ip_conntrack_helper_register(&h225);
 }
 
 static void __exit fini(void)
 {
-       /* Unregister H.225 helper */   
+       /* Unregister H.225 helper */
        ip_conntrack_helper_unregister(&h225);
 }
 
 EXPORT_SYMBOL(ip_h323_lock);
 
+PROVIDES_CONNTRACK(h225);
+PROVIDES_CONNTRACK(h245);
 module_init(init);
 module_exit(fini);