X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fip_nat_sip.c;h=6223abc924ff33f5ad884de6128d3d60b2a797be;hb=refs%2Fheads%2Fvserver;hp=6ffba63adca230e17ed1345838e49240879a857a;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ip_nat_sip.c b/net/ipv4/netfilter/ip_nat_sip.c index 6ffba63ad..6223abc92 100644 --- a/net/ipv4/netfilter/ip_nat_sip.c +++ b/net/ipv4/netfilter/ip_nat_sip.c @@ -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; }