Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_helper_h323.c
index 853a3d5..9a39e29 100644 (file)
@@ -11,7 +11,6 @@
  * For more information, please see http://nath323.sourceforge.net/
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/netfilter.h>
 #include <linux/ip.h>
@@ -22,6 +21,8 @@
 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
 #include <linux/netfilter_ipv4/ip_conntrack_h323.h>
 #include <linux/moduleparam.h>
+#include <linux/ctype.h>
+#include <linux/inet.h>
 
 #if 0
 #define DEBUGP printk
@@ -38,6 +39,12 @@ static int gkrouted_only = 1;
 module_param(gkrouted_only, int, 0600);
 MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper");
 
+static int callforward_filter = 1;
+module_param(callforward_filter, bool, 0600);
+MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations "
+                                    "if both endpoints are on different sides "
+                                    "(determined by routing information)");
+
 /* Hooks for NAT */
 int (*set_h245_addr_hook) (struct sk_buff ** pskb,
                           unsigned char **data, int dataoff,
@@ -77,6 +84,12 @@ int (*nat_h245_hook) (struct sk_buff ** pskb,
                      unsigned char **data, int dataoff,
                      TransportAddress * addr, u_int16_t port,
                      struct ip_conntrack_expect * exp);
+int (*nat_callforwarding_hook) (struct sk_buff ** pskb,
+                               struct ip_conntrack * ct,
+                               enum ip_conntrack_info ctinfo,
+                               unsigned char **data, int dataoff,
+                               TransportAddress * addr, u_int16_t port,
+                               struct ip_conntrack_expect * exp);
 int (*nat_q931_hook) (struct sk_buff ** pskb,
                      struct ip_conntrack * ct,
                      enum ip_conntrack_info ctinfo,
@@ -683,6 +696,92 @@ static int expect_h245(struct sk_buff **pskb, struct ip_conntrack *ct,
        return ret;
 }
 
+/* Forwarding declaration */
+void ip_conntrack_q931_expect(struct ip_conntrack *new,
+                             struct ip_conntrack_expect *this);
+
+/****************************************************************************/
+static int expect_callforwarding(struct sk_buff **pskb,
+                                struct ip_conntrack *ct,
+                                enum ip_conntrack_info ctinfo,
+                                unsigned char **data, int dataoff,
+                                TransportAddress * addr)
+{
+       int dir = CTINFO2DIR(ctinfo);
+       int ret = 0;
+       u_int32_t ip;
+       u_int16_t port;
+       struct ip_conntrack_expect *exp = NULL;
+
+       /* Read alternativeAddress */
+       if (!get_h225_addr(*data, addr, &ip, &port) || port == 0)
+               return 0;
+
+       /* If the calling party is on the same side of the forward-to party,
+        * we don't need to track the second call */
+       if (callforward_filter) {
+               struct rtable *rt1, *rt2;
+               struct flowi fl1 = {
+                       .fl4_dst = ip,
+               };
+               struct flowi fl2 = {
+                       .fl4_dst = ct->tuplehash[!dir].tuple.src.ip,
+               };
+
+               if (ip_route_output_key(&rt1, &fl1) == 0) {
+                       if (ip_route_output_key(&rt2, &fl2) == 0) {
+                               if (rt1->rt_gateway == rt2->rt_gateway &&
+                                   rt1->u.dst.dev  == rt2->u.dst.dev)
+                                       ret = 1;
+                               dst_release(&rt2->u.dst);
+                       }
+                       dst_release(&rt1->u.dst);
+               }
+               if (ret) {
+                       DEBUGP("ip_ct_q931: Call Forwarding not tracked\n");
+                       return 0;
+               }
+       }
+
+       /* Create expect for the second call leg */
+       if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
+               return -1;
+       exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
+       exp->tuple.src.u.tcp.port = 0;
+       exp->tuple.dst.ip = ip;
+       exp->tuple.dst.u.tcp.port = htons(port);
+       exp->tuple.dst.protonum = IPPROTO_TCP;
+       exp->mask.src.ip = 0xFFFFFFFF;
+       exp->mask.src.u.tcp.port = 0;
+       exp->mask.dst.ip = 0xFFFFFFFF;
+       exp->mask.dst.u.tcp.port = 0xFFFF;
+       exp->mask.dst.protonum = 0xFF;
+       exp->flags = 0;
+
+       if (ct->tuplehash[dir].tuple.src.ip !=
+           ct->tuplehash[!dir].tuple.dst.ip && nat_callforwarding_hook) {
+               /* Need NAT */
+               ret = nat_callforwarding_hook(pskb, ct, ctinfo, data, dataoff,
+                                             addr, port, exp);
+       } else {                /* Conntrack only */
+               exp->expectfn = ip_conntrack_q931_expect;
+
+               if (ip_conntrack_expect_related(exp) == 0) {
+                       DEBUGP("ip_ct_q931: expect Call Forwarding "
+                              "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
+                              NIPQUAD(exp->tuple.src.ip),
+                              ntohs(exp->tuple.src.u.tcp.port),
+                              NIPQUAD(exp->tuple.dst.ip),
+                              ntohs(exp->tuple.dst.u.tcp.port));
+               } else
+                       ret = -1;
+       }
+
+       ip_conntrack_expect_put(exp);
+
+       return ret;
+}
+
 /****************************************************************************/
 static int process_setup(struct sk_buff **pskb, struct ip_conntrack *ct,
                         enum ip_conntrack_info ctinfo,
@@ -878,6 +977,15 @@ static int process_facility(struct sk_buff **pskb, struct ip_conntrack *ct,
 
        DEBUGP("ip_ct_q931: Facility\n");
 
+       if (facility->reason.choice == eFacilityReason_callForwarded) {
+               if (facility->options & eFacility_UUIE_alternativeAddress)
+                       return expect_callforwarding(pskb, ct, ctinfo, data,
+                                                    dataoff,
+                                                    &facility->
+                                                    alternativeAddress);
+               return 0;
+       }
+
        if (facility->options & eFacility_UUIE_h245Address) {
                ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
                                  &facility->h245Address);
@@ -1677,7 +1785,6 @@ static int __init init(void)
                fini();
                return ret;
        }
-
        DEBUGP("ip_ct_h323: init success\n");
        return 0;
 }
@@ -1696,6 +1803,7 @@ EXPORT_SYMBOL_GPL(set_ras_addr_hook);
 EXPORT_SYMBOL_GPL(nat_rtp_rtcp_hook);
 EXPORT_SYMBOL_GPL(nat_t120_hook);
 EXPORT_SYMBOL_GPL(nat_h245_hook);
+EXPORT_SYMBOL_GPL(nat_callforwarding_hook);
 EXPORT_SYMBOL_GPL(nat_q931_hook);
 
 MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>");