fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_helper_h323.c
index d45663d..bdc99ef 100644 (file)
 /****************************************************************************/
 static int set_addr(struct sk_buff **pskb,
                    unsigned char **data, int dataoff,
-                   unsigned int addroff, u_int32_t ip, u_int16_t port)
+                   unsigned int addroff, __be32 ip, u_int16_t port)
 {
        enum ip_conntrack_info ctinfo;
        struct ip_conntrack *ct = ip_conntrack_get(*pskb, &ctinfo);
        struct {
-               u_int32_t ip;
-               u_int16_t port;
+               __be32 ip;
+               __be16 port;
        } __attribute__ ((__packed__)) buf;
        struct tcphdr _tcph, *th;
 
@@ -86,7 +86,7 @@ static int set_addr(struct sk_buff **pskb,
 static int set_h225_addr(struct sk_buff **pskb,
                         unsigned char **data, int dataoff,
                         TransportAddress * addr,
-                        u_int32_t ip, u_int16_t port)
+                        __be32 ip, u_int16_t port)
 {
        return set_addr(pskb, data, dataoff, addr->ipAddress.ip, ip, port);
 }
@@ -95,7 +95,7 @@ static int set_h225_addr(struct sk_buff **pskb,
 static int set_h245_addr(struct sk_buff **pskb,
                         unsigned char **data, int dataoff,
                         H245_TransportAddress * addr,
-                        u_int32_t ip, u_int16_t port)
+                        __be32 ip, u_int16_t port)
 {
        return set_addr(pskb, data, dataoff,
                        addr->unicastAddress.iPAddress.network, ip, port);
@@ -110,7 +110,7 @@ static int set_sig_addr(struct sk_buff **pskb, struct ip_conntrack *ct,
        struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
        int dir = CTINFO2DIR(ctinfo);
        int i;
-       u_int32_t ip;
+       __be32 ip;
        u_int16_t port;
 
        for (i = 0; i < count; i++) {
@@ -164,7 +164,7 @@ static int set_ras_addr(struct sk_buff **pskb, struct ip_conntrack *ct,
 {
        int dir = CTINFO2DIR(ctinfo);
        int i;
-       u_int32_t ip;
+       __be32 ip;
        u_int16_t port;
 
        for (i = 0; i < count; i++) {
@@ -433,7 +433,7 @@ static int nat_q931(struct sk_buff **pskb, struct ip_conntrack *ct,
        struct ip_ct_h323_master *info = &ct->help.ct_h323_info;
        int dir = CTINFO2DIR(ctinfo);
        u_int16_t nated_port = port;
-       u_int32_t ip;
+       __be32 ip;
 
        /* Set expectations for NAT */
        exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
@@ -486,26 +486,102 @@ static int nat_q931(struct sk_buff **pskb, struct ip_conntrack *ct,
        return 0;
 }
 
+/****************************************************************************/
+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);
-       BUG_ON(set_h225_addr_hook != NULL);
-       BUG_ON(set_sig_addr_hook != NULL);
-       BUG_ON(set_ras_addr_hook != NULL);
-       BUG_ON(nat_rtp_rtcp_hook != NULL);
-       BUG_ON(nat_t120_hook != NULL);
-       BUG_ON(nat_h245_hook != NULL);
-       BUG_ON(nat_q931_hook != NULL);
-
-       set_h245_addr_hook = set_h245_addr;
-       set_h225_addr_hook = set_h225_addr;
-       set_sig_addr_hook = set_sig_addr;
-       set_ras_addr_hook = set_ras_addr;
-       nat_rtp_rtcp_hook = nat_rtp_rtcp;
-       nat_t120_hook = nat_t120;
-       nat_h245_hook = nat_h245;
-       nat_q931_hook = nat_q931;
+       BUG_ON(rcu_dereference(set_h245_addr_hook) != NULL);
+       BUG_ON(rcu_dereference(set_h225_addr_hook) != NULL);
+       BUG_ON(rcu_dereference(set_sig_addr_hook) != NULL);
+       BUG_ON(rcu_dereference(set_ras_addr_hook) != NULL);
+       BUG_ON(rcu_dereference(nat_rtp_rtcp_hook) != NULL);
+       BUG_ON(rcu_dereference(nat_t120_hook) != NULL);
+       BUG_ON(rcu_dereference(nat_h245_hook) != NULL);
+       BUG_ON(rcu_dereference(nat_callforwarding_hook) != NULL);
+       BUG_ON(rcu_dereference(nat_q931_hook) != NULL);
+
+       rcu_assign_pointer(set_h245_addr_hook, set_h245_addr);
+       rcu_assign_pointer(set_h225_addr_hook, set_h225_addr);
+       rcu_assign_pointer(set_sig_addr_hook, set_sig_addr);
+       rcu_assign_pointer(set_ras_addr_hook, set_ras_addr);
+       rcu_assign_pointer(nat_rtp_rtcp_hook, nat_rtp_rtcp);
+       rcu_assign_pointer(nat_t120_hook, nat_t120);
+       rcu_assign_pointer(nat_h245_hook, nat_h245);
+       rcu_assign_pointer(nat_callforwarding_hook, nat_callforwarding);
+       rcu_assign_pointer(nat_q931_hook, nat_q931);
 
        DEBUGP("ip_nat_h323: init success\n");
        return 0;
@@ -514,15 +590,16 @@ static int __init init(void)
 /****************************************************************************/
 static void __exit fini(void)
 {
-       set_h245_addr_hook = NULL;
-       set_h225_addr_hook = NULL;
-       set_sig_addr_hook = NULL;
-       set_ras_addr_hook = NULL;
-       nat_rtp_rtcp_hook = NULL;
-       nat_t120_hook = NULL;
-       nat_h245_hook = NULL;
-       nat_q931_hook = NULL;
-       synchronize_net();
+       rcu_assign_pointer(set_h245_addr_hook, NULL);
+       rcu_assign_pointer(set_h225_addr_hook, NULL);
+       rcu_assign_pointer(set_sig_addr_hook, NULL);
+       rcu_assign_pointer(set_ras_addr_hook, NULL);
+       rcu_assign_pointer(nat_rtp_rtcp_hook, NULL);
+       rcu_assign_pointer(nat_t120_hook, NULL);
+       rcu_assign_pointer(nat_h245_hook, NULL);
+       rcu_assign_pointer(nat_callforwarding_hook, NULL);
+       rcu_assign_pointer(nat_q931_hook, NULL);
+       synchronize_rcu();
 }
 
 /****************************************************************************/