fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_sip.c
index 6ffba63..6223abc 100644 (file)
@@ -29,27 +29,70 @@ MODULE_DESCRIPTION("SIP NAT helper");
 #define DEBUGP(format, args...)
 #endif
 
-extern struct sip_header_nfo ct_sip_hdrs[];
+struct addr_map {
+       struct {
+               char            src[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
+               char            dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
+               unsigned int    srclen, srciplen;
+               unsigned int    dstlen, dstiplen;
+       } addr[IP_CT_DIR_MAX];
+};
+
+static void addr_map_init(struct ip_conntrack *ct, struct addr_map *map)
+{
+       struct ip_conntrack_tuple *t;
+       enum ip_conntrack_dir dir;
+       unsigned int n;
+
+       for (dir = 0; dir < IP_CT_DIR_MAX; dir++) {
+               t = &ct->tuplehash[dir].tuple;
+
+               n = sprintf(map->addr[dir].src, "%u.%u.%u.%u",
+                           NIPQUAD(t->src.ip));
+               map->addr[dir].srciplen = n;
+               n += sprintf(map->addr[dir].src + n, ":%u",
+                            ntohs(t->src.u.udp.port));
+               map->addr[dir].srclen = n;
+
+               n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u",
+                           NIPQUAD(t->dst.ip));
+               map->addr[dir].dstiplen = n;
+               n += sprintf(map->addr[dir].dst + n, ":%u",
+                            ntohs(t->dst.u.udp.port));
+               map->addr[dir].dstlen = n;
+       }
+}
 
-static unsigned int mangle_sip_packet(struct sk_buff **pskb,
-                                     enum ip_conntrack_info ctinfo,
-                                     struct ip_conntrack *ct,
-                                     const char **dptr, size_t dlen,
-                                     char *buffer, int bufflen,
-                                     struct sip_header_nfo *hnfo)
+static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
+                       struct ip_conntrack *ct, const char **dptr, size_t dlen,
+                       enum sip_header_pos pos, struct addr_map *map)
 {
-       unsigned int matchlen, matchoff;
+       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+       unsigned int matchlen, matchoff, addrlen;
+       char *addr;
 
-       if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, hnfo) <= 0)
-               return 0;
+       if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
+               return 1;
+
+       if ((matchlen == map->addr[dir].srciplen ||
+            matchlen == map->addr[dir].srclen) &&
+           memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) {
+               addr    = map->addr[!dir].dst;
+               addrlen = map->addr[!dir].dstlen;
+       } else if ((matchlen == map->addr[dir].dstiplen ||
+                   matchlen == map->addr[dir].dstlen) &&
+                  memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) {
+               addr    = map->addr[!dir].src;
+               addrlen = map->addr[!dir].srclen;
+       } else
+               return 1;
 
        if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
-                                     matchoff, matchlen, buffer, bufflen))
+                                     matchoff, matchlen, addr, addrlen))
                return 0;
-
-       /* We need to reload this. Thanks Patrick. */
        *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
        return 1;
+
 }
 
 static unsigned int ip_nat_sip(struct sk_buff **pskb,
@@ -57,70 +100,61 @@ static unsigned int ip_nat_sip(struct sk_buff **pskb,
                               struct ip_conntrack *ct,
                               const char **dptr)
 {
-       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-       char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
-       unsigned int bufflen, dataoff;
-       u_int32_t ip;
-       u_int16_t port;
+       enum sip_header_pos pos;
+       struct addr_map map;
+       int dataoff, datalen;
 
        dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+       datalen = (*pskb)->len - dataoff;
+       if (datalen < sizeof("SIP/2.0") - 1)
+               return NF_DROP;
 
-       ip   = ct->tuplehash[!dir].tuple.dst.ip;
-       port = ct->tuplehash[!dir].tuple.dst.u.udp.port;
-       bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port));
+       addr_map_init(ct, &map);
+
+       /* Basic rules: requests and responses. */
+       if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) {
+               /* 10.2: Constructing the REGISTER Request:
+                *
+                * The "userinfo" and "@" components of the SIP URI MUST NOT
+                * be present.
+                */
+               if (datalen >= sizeof("REGISTER") - 1 &&
+                   strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0)
+                       pos = POS_REG_REQ_URI;
+               else
+                       pos = POS_REQ_URI;
+
+               if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map))
+                       return NF_DROP;
+       }
 
-       /* short packet ? */
-       if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1))
+       if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) ||
+           !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) ||
+           !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) ||
+           !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map))
+               return NF_DROP;
+       return NF_ACCEPT;
+}
+
+static unsigned int mangle_sip_packet(struct sk_buff **pskb,
+                                     enum ip_conntrack_info ctinfo,
+                                     struct ip_conntrack *ct,
+                                     const char **dptr, size_t dlen,
+                                     char *buffer, int bufflen,
+                                     enum sip_header_pos pos)
+{
+       unsigned int matchlen, matchoff;
+
+       if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
                return 0;
 
-       /* Basic rules: requests and responses. */
-       if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {
-               const char *aux;
-
-               if ((ctinfo) < IP_CT_IS_REPLY) {
-                       mangle_sip_packet(pskb, ctinfo, ct, dptr,
-                                         (*pskb)->len - dataoff,
-                                         buffer, bufflen,
-                                         &ct_sip_hdrs[POS_CONTACT]);
-                       return 1;
-               }
+       if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
+                                     matchoff, matchlen, buffer, bufflen))
+               return 0;
 
-               if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
-                                      (*pskb)->len - dataoff,
-                                      buffer, bufflen, &ct_sip_hdrs[POS_VIA]))
-                       return 0;
-
-               /* This search should ignore case, but later.. */
-               aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
-                                   (*pskb)->len - dataoff);
-               if (!aux)
-                       return 0;
-
-               if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
-                   ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff)))
-                       return 1;
-
-               return mangle_sip_packet(pskb, ctinfo, ct, dptr,
-                                        (*pskb)->len - dataoff,
-                                        buffer, bufflen,
-                                        &ct_sip_hdrs[POS_CONTACT]);
-       }
-       if ((ctinfo) < IP_CT_IS_REPLY) {
-               if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
-                                      (*pskb)->len - dataoff,
-                                      buffer, bufflen, &ct_sip_hdrs[POS_VIA]))
-                       return 0;
-
-               /* Mangle Contact if exists only. - watch udp_nat_mangle()! */
-               mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff,
-                                 buffer, bufflen, &ct_sip_hdrs[POS_CONTACT]);
-               return 1;
-       }
-       /* This mangle requests headers. */
-       return mangle_sip_packet(pskb, ctinfo, ct, dptr,
-                                ct_sip_lnlen(*dptr,
-                                             *dptr + (*pskb)->len - dataoff),
-                                buffer, bufflen, &ct_sip_hdrs[POS_REQ_HEADER]);
+       /* We need to reload this. Thanks Patrick. */
+       *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
+       return 1;
 }
 
 static int mangle_content_len(struct sk_buff **pskb,
@@ -136,7 +170,7 @@ static int mangle_content_len(struct sk_buff **pskb,
 
        /* Get actual SDP lenght */
        if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
-                           &matchlen, &ct_sip_hdrs[POS_SDP_HEADER]) > 0) {
+                           &matchlen, POS_SDP_HEADER) > 0) {
 
                /* since ct_sip_get_info() give us a pointer passing 'v='
                   we need to add 2 bytes in this count. */
@@ -144,7 +178,7 @@ static int mangle_content_len(struct sk_buff **pskb,
 
                /* Now, update SDP lenght */
                if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
-                                   &matchlen, &ct_sip_hdrs[POS_CONTENT]) > 0) {
+                                   &matchlen, POS_CONTENT) > 0) {
 
                        bufflen = sprintf(buffer, "%u", c_len);
 
@@ -159,7 +193,7 @@ static int mangle_content_len(struct sk_buff **pskb,
 static unsigned int mangle_sdp(struct sk_buff **pskb,
                               enum ip_conntrack_info ctinfo,
                               struct ip_conntrack *ct,
-                              u_int32_t newip, u_int16_t port,
+                              __be32 newip, u_int16_t port,
                               const char *dptr)
 {
        char buffer[sizeof("nnn.nnn.nnn.nnn")];
@@ -170,17 +204,17 @@ static unsigned int mangle_sdp(struct sk_buff **pskb,
        /* Mangle owner and contact info. */
        bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
        if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
-                              buffer, bufflen, &ct_sip_hdrs[POS_OWNER]))
+                              buffer, bufflen, POS_OWNER))
                return 0;
 
        if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
-                              buffer, bufflen, &ct_sip_hdrs[POS_CONNECTION]))
+                              buffer, bufflen, POS_CONNECTION))
                return 0;
 
        /* Mangle media port. */
        bufflen = sprintf(buffer, "%u", port);
        if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
-                              buffer, bufflen, &ct_sip_hdrs[POS_MEDIA]))
+                              buffer, bufflen, POS_MEDIA))
                return 0;
 
        return mangle_content_len(pskb, ctinfo, ct, dptr);
@@ -195,7 +229,7 @@ static unsigned int ip_nat_sdp(struct sk_buff **pskb,
 {
        struct ip_conntrack *ct = exp->master;
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-       u_int32_t newip;
+       __be32 newip;
        u_int16_t port;
 
        DEBUGP("ip_nat_sdp():\n");
@@ -230,18 +264,17 @@ static unsigned int ip_nat_sdp(struct sk_buff **pskb,
 
 static void __exit fini(void)
 {
-       ip_nat_sip_hook = NULL;
-       ip_nat_sdp_hook = NULL;
-       /* Make sure noone calls it, meanwhile. */
-       synchronize_net();
+       rcu_assign_pointer(ip_nat_sip_hook, NULL);
+       rcu_assign_pointer(ip_nat_sdp_hook, NULL);
+       synchronize_rcu();
 }
 
 static int __init init(void)
 {
-       BUG_ON(ip_nat_sip_hook);
-       BUG_ON(ip_nat_sdp_hook);
-       ip_nat_sip_hook = ip_nat_sip;
-       ip_nat_sdp_hook = ip_nat_sdp;
+       BUG_ON(rcu_dereference(ip_nat_sip_hook));
+       BUG_ON(rcu_dereference(ip_nat_sdp_hook));
+       rcu_assign_pointer(ip_nat_sip_hook, ip_nat_sip);
+       rcu_assign_pointer(ip_nat_sdp_hook, ip_nat_sdp);
        return 0;
 }