[NETFILTER]: H.323 helper: Add support for Call Forwarding
Jing Min Zhao [Tue, 30 May 2006 01:26:27 +0000 (18:26 -0700)]
Signed-off-by: Jing Min Zhao <zhaojingmin@users.sourceforge.net>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/linux/netfilter_ipv4/ip_conntrack.h
include/linux/netfilter_ipv4/ip_conntrack_h323.h
include/linux/netfilter_ipv4/ip_conntrack_helper_h323_types.h
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/ip_conntrack_helper_h323.c
net/ipv4/netfilter/ip_conntrack_helper_h323_types.c
net/ipv4/netfilter/ip_nat_helper_h323.c

index 5473c01..17d7ef9 100644 (file)
@@ -154,6 +154,7 @@ struct ip_conntrack_expect
        unsigned int flags;
 
 #ifdef CONFIG_IP_NF_NAT_NEEDED
+       u_int32_t saved_ip;
        /* This is the original per-proto part, used to map the
         * expected connection the way the recipient expects. */
        union ip_conntrack_manip_proto saved_proto;
index eace86b..3cbff73 100644 (file)
@@ -71,6 +71,13 @@ extern int (*nat_h245_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct,
                             unsigned char **data, int dataoff,
                             TransportAddress * addr, u_int16_t port,
                             struct ip_conntrack_expect * exp);
+extern 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);
 extern int (*nat_q931_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct,
                             enum ip_conntrack_info ctinfo,
                             unsigned char **data, TransportAddress * addr,
index cc98f7a..3d4a773 100644 (file)
@@ -1,4 +1,4 @@
-/* Generated by Jing Min Zhao's ASN.1 parser, Mar 15 2006
+/* Generated by Jing Min Zhao's ASN.1 parser, Apr 20 2006
  *
  * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
  *
@@ -412,6 +412,7 @@ typedef struct Facility_UUIE {      /* SEQUENCE */
                eFacility_UUIE_destinationInfo = (1 << 14),
                eFacility_UUIE_h245SecurityMode = (1 << 13),
        } options;
+       TransportAddress alternativeAddress;
        FacilityReason reason;
        TransportAddress h245Address;
        Facility_UUIE_fastStart fastStart;
index 1540e22..f86aeda 100644 (file)
@@ -183,10 +183,10 @@ config IP_NF_H323
          With this module you can support H.323 on a connection tracking/NAT
          firewall.
 
-         This module supports RAS, Fast-start, H.245 tunnelling, RTP/RTCP
-         and T.120 based data and applications including audio, video, FAX,
-         chat, whiteboard, file transfer, etc. For more information, please
-         see http://nath323.sourceforge.net/.
+         This module supports RAS, Fast Start, H.245 Tunnelling, Call
+         Forwarding, RTP/RTCP and T.120 based audio, video, fax, chat,
+         whiteboard, file transfer, etc. For more information, please
+         visit http://nath323.sourceforge.net/.
 
          If you want to compile it as a module, say 'M' here and read
          Documentation/modules.txt.  If unsure, say 'N'.
index 518f581..3052468 100644 (file)
@@ -22,6 +22,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 +40,13 @@ static int gkrouted_only = 1;
 module_param(gkrouted_only, int, 0600);
 MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper");
 
+static char *internal_net = NULL;
+static u_int32_t internal_net_addr = 0;
+static u_int32_t internal_net_mask = 0;
+module_param(internal_net, charp, 0600);
+MODULE_PARM_DESC(internal_net, "specify your internal network using format "
+                "address/mask. this is used by call forwarding support");
+
 /* Hooks for NAT */
 int (*set_h245_addr_hook) (struct sk_buff ** pskb,
                           unsigned char **data, int dataoff,
@@ -77,6 +86,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 +698,76 @@ 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 (internal_net &&
+           ((ip & internal_net_mask) == internal_net_addr) ==
+           ((ct->tuplehash[!dir].tuple.src.ip & internal_net_mask) ==
+            internal_net_addr)) {
+               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 +963,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);
@@ -1668,6 +1762,7 @@ static void fini(void)
 static int __init init(void)
 {
        int ret;
+       char *p;
 
        h323_buffer = kmalloc(65536, GFP_KERNEL);
        if (!h323_buffer)
@@ -1678,6 +1773,22 @@ static int __init init(void)
                return ret;
        }
 
+       if (internal_net) {
+               if ((p = strchr(internal_net, '/')))
+                       *p++ = 0;
+               if (isdigit(internal_net[0])) {
+                       internal_net_addr = in_aton(internal_net);
+                       if (p && isdigit(p[0]))
+                               internal_net_mask = in_aton(p);
+                       else
+                               internal_net_mask = 0xffffffff;
+                       internal_net_addr &= internal_net_mask;
+               }
+               DEBUGP("ip_ct_h323: internal_net = %u.%u.%u.%u/%u.%u.%u.%u\n",
+                      NIPQUAD(internal_net_addr),
+                      NIPQUAD(internal_net_mask));
+       }
+
        DEBUGP("ip_ct_h323: init success\n");
        return 0;
 }
@@ -1696,6 +1807,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>");
index 022c47b..4b35961 100644 (file)
@@ -1,4 +1,4 @@
-/* Generated by Jing Min Zhao's ASN.1 parser, Mar 15 2006
+/* Generated by Jing Min Zhao's ASN.1 parser, Apr 20 2006
  *
  * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
  *
@@ -1069,8 +1069,8 @@ static field_t _Facility_UUIE_fastStart[] = {     /* SEQUENCE OF */
 
 static field_t _Facility_UUIE[] = {    /* SEQUENCE */
        {FNAME("protocolIdentifier") OID, BYTE, 0, 0, SKIP, 0, NULL},
-       {FNAME("alternativeAddress") CHOICE, 3, 7, 7, SKIP | EXT | OPT, 0,
-        _TransportAddress},
+       {FNAME("alternativeAddress") CHOICE, 3, 7, 7, DECODE | EXT | OPT,
+        offsetof(Facility_UUIE, alternativeAddress), _TransportAddress},
        {FNAME("alternativeAliasAddress") SEQOF, SEMI, 0, 0, SKIP | OPT, 0,
         _Facility_UUIE_alternativeAliasAddress},
        {FNAME("conferenceID") OCTSTR, FIXD, 16, 0, SKIP | OPT, 0, NULL},
index d45663d..419b878 100644 (file)
@@ -487,6 +487,80 @@ static int nat_q931(struct sk_buff **pskb, struct ip_conntrack *ct,
 }
 
 /****************************************************************************/
+static void ip_nat_callforwarding_expect(struct ip_conntrack *new,
+                                        struct ip_conntrack_expect *this)
+{
+       struct ip_nat_range range;
+
+       /* This must be a fresh one. */
+       BUG_ON(new->status & IPS_NAT_DONE_MASK);
+
+       /* Change src to where master sends to */
+       range.flags = IP_NAT_RANGE_MAP_IPS;
+       range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip;
+
+       /* hook doesn't matter, but it has to do source manip */
+       ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING);
+
+       /* For DST manip, map port here to where it's expected. */
+       range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
+       range.min = range.max = this->saved_proto;
+       range.min_ip = range.max_ip = this->saved_ip;
+
+       /* hook doesn't matter, but it has to do destination manip */
+       ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING);
+
+       ip_conntrack_q931_expect(new, this);
+}
+
+/****************************************************************************/
+static int nat_callforwarding(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 dir = CTINFO2DIR(ctinfo);
+       u_int16_t nated_port;
+
+       /* Set expectations for NAT */
+       exp->saved_ip = exp->tuple.dst.ip;
+       exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
+       exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
+       exp->expectfn = ip_nat_callforwarding_expect;
+       exp->dir = !dir;
+
+       /* Try to get same port: if not, try to change it. */
+       for (nated_port = port; nated_port != 0; nated_port++) {
+               exp->tuple.dst.u.tcp.port = htons(nated_port);
+               if (ip_conntrack_expect_related(exp) == 0)
+                       break;
+       }
+
+       if (nated_port == 0) {  /* No port available */
+               if (net_ratelimit())
+                       printk("ip_nat_q931: out of TCP ports\n");
+               return 0;
+       }
+
+       /* Modify signal */
+       if (!set_h225_addr(pskb, data, dataoff, addr,
+                          ct->tuplehash[!dir].tuple.dst.ip,
+                          nated_port) == 0) {
+               ip_conntrack_unexpect_related(exp);
+               return -1;
+       }
+
+       /* Success */
+       DEBUGP("ip_nat_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));
+
+       return 0;
+}
+
+/****************************************************************************/
 static int __init init(void)
 {
        BUG_ON(set_h245_addr_hook != NULL);
@@ -496,6 +570,7 @@ static int __init init(void)
        BUG_ON(nat_rtp_rtcp_hook != NULL);
        BUG_ON(nat_t120_hook != NULL);
        BUG_ON(nat_h245_hook != NULL);
+       BUG_ON(nat_callforwarding_hook != NULL);
        BUG_ON(nat_q931_hook != NULL);
 
        set_h245_addr_hook = set_h245_addr;
@@ -505,6 +580,7 @@ static int __init init(void)
        nat_rtp_rtcp_hook = nat_rtp_rtcp;
        nat_t120_hook = nat_t120;
        nat_h245_hook = nat_h245;
+       nat_callforwarding_hook = nat_callforwarding;
        nat_q931_hook = nat_q931;
 
        DEBUGP("ip_nat_h323: init success\n");
@@ -521,6 +597,7 @@ static void __exit fini(void)
        nat_rtp_rtcp_hook = NULL;
        nat_t120_hook = NULL;
        nat_h245_hook = NULL;
+       nat_callforwarding_hook = NULL;
        nat_q931_hook = NULL;
        synchronize_net();
 }