diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 12:04:58 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 12:04:58 +0100 |
commit | 008d0be72b2f160382c6e880765e96b64a050c65 (patch) | |
tree | 36f48a98a3815a408e2ce1693dd182af90f80305 /release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_sip.c | |
parent | 611becfb8726c60cb060368541ad98191d4532f5 (diff) | |
download | tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.gz tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.bz2 |
imported original firmware WRT54GL_v4.30.11_11_US
Diffstat (limited to 'release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_sip.c')
-rw-r--r-- | release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_sip.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_sip.c b/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_sip.c new file mode 100644 index 00000000..0b7cb9fd --- /dev/null +++ b/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_sip.c @@ -0,0 +1,375 @@ +/* + * SIP extension for IP connection tracking. + * + * Provided by CyberTAN Corporation. + */ + +#include <linux/module.h> +#include <linux/netfilter.h> +#include <linux/ip.h> +#include <linux/ctype.h> +#include <net/checksum.h> +#include <net/tcp.h> + +#include <linux/netfilter_ipv4/lockhelp.h> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h> +#include <linux/netfilter_ipv4/ip_conntrack_sip.h> +#include <linux/netfilter_ipv4/ip_conntrack_core.h> + +DECLARE_LOCK(ip_sip_lock); + +struct module *ip_conntrack_sip = THIS_MODULE; + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c; +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +#endif + +#if 0 + #define DEBUGP printk +#else + #define DEBUGP(format, args...) +#endif + +#define RTCP_SUPPORT + +/**** 2007-02-07 add by zg to fix cdrouter v3.4 item 321(cdrouter_sip_45) bug ****/ +__inline static int atoi(char *s) +{ + int i=0; + while (isdigit(*s)) { + i = i*10 + *(s++) - '0'; + } + return i; +} +/**** 2007-02-07 end by zg to fix cdrouter v3.4 item 321(cdrouter_sip_45) bug ****/ + +static int set_expected_rtp(struct ip_conntrack *ct, u_int32_t dstip, + u_int16_t dstport, unsigned int conntype) +{ + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_sip_expect *exp_sip_info = &exp->help.exp_sip_info; + int ret = 0; + + memset(&expect, 0, sizeof(expect)); + LOCK_BH(&ip_sip_lock); + + DEBUGP("conntrack_sip: %s: [%s]: DST=%u.%u.%u.%u:%u\n", __FUNCTION__, + conntype == CONN_RTP ? "RTP" : "RTCP", NIPQUAD(dstip), dstport); + + /* exp_sip_info */ + exp_sip_info->port = dstport; + exp_sip_info->type = conntype; + exp_sip_info->nated = 0; + + /* new expectation */ + exp->tuple = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { dstip, { htons(dstport) }, IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + exp->expectfn = NULL; + + if ((ret=ip_conntrack_expect_related(ct, exp)) != 0) { + DEBUGP("Can't add new expectation. \n"); + } + + UNLOCK_BH(&ip_sip_lock); + + return ret; +} + +/* +static int unset_expected_rtp(struct ip_conntrack *ct, u_int32_t dstip, u_int16_t dstport) +{ + struct ip_conntrack_expect *exp; + const struct ip_conntrack_tuple tuple = { { 0, { 0 } }, + { dstip, { htons(dstport) }, IPPROTO_UDP }}; + + LOCK_BH(&ip_sip_lock); + + exp = ip_conntrack_expect_find_get(&tuple); + if (exp) { + DEBUGP("Find the expectation %p, then delete it.\n", exp); + ip_conntrack_unexpect_related(exp); + } + + UNLOCK_BH(&ip_sip_lock); + + return 0; +}*/ + +/* return: + * 0 : Not found + * 1 : Found domain name + * 2 : Found dotted quads + */ +int find_sdp_rtp_addr(const char *data, size_t dlen, + unsigned int *numoff, unsigned int *numlen, u_int32_t *addr) +{ + char *st, *p = (char *)data; + const char *limit = data + dlen; + unsigned char p1, p2, p3, p4; + + while (p < limit) { + /* find 'c=' line */ + if (strncmp(p, "\nc=",3) && strncmp(p, "\rc=",3)) { + p++; + continue; + } + p += 3; + + if (strncmp(p, "IN IP4 ",7)) + continue; + p += 7; + + /* IP address */ + st = p; + + /* FQDNs or dotted quads */ + while (isalpha(*p) || isdigit(*p) || (*p=='.') || (*p=='-')) { + p++; + if (p == limit) + return 0; + } + + *numoff = st - data; + *numlen = p - st; + + /* Convert the IP address */ + p1 = simple_strtoul(st, &st ,10); + if (*st != '.') + return 1; + p2 = simple_strtoul(st+1, &st, 10); + if (*st != '.') + return 1; + p3 = simple_strtoul(st+1, &st, 10); + if (*st != '.') + return 1; + p4 = simple_strtoul(st+1, &st, 10); + + *addr = (p1<<24) | (p2<<16) | (p3<<8) | p4; + + return 2; + } + + return 0; +} + +u_int16_t find_sdp_audio_port(const char *data, size_t dlen, + unsigned int *numoff, unsigned int *numlen) +{ + char *st, *p = (char *)data; + const char *limit = data + dlen; + u_int16_t port = 0; + + while (p < limit) { + /* find 'm=' */ + if (strncmp(p, "\nm=", 3) && strncmp(p, "\rm=", 3)) { + p++; + continue; + } + p += 3; + + /* audio stream */ + if (strncmp(p ,"audio ",6)) + continue; + p += 6; + + st = p; + port = simple_strtoul(p, &p, 10); + + *numoff = st - data; + *numlen = p - st; + + return port; + } + + return 0; +} + +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); + unsigned int matchlen, matchoff; + + u_int32_t ipaddr=0; + u_int16_t port = 0; + + int found = 0; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)udph + 8; + unsigned int udplen = len - iph->ihl * 4; + unsigned int datalen = udplen - 8; + struct ip_ct_sip_master *ct_sip_info = &ct->help.ct_sip_info; + + DEBUGP("\nconntrack_sip: help(): DIR=%d, conntrackinfo=%u\n", dir, ctinfo); + DEBUGP("conntrack_sip: %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.udp.port), + NIPQUAD(ct->tuplehash[dir].tuple.dst.ip), + ntohs(ct->tuplehash[dir].tuple.dst.u.udp.port) ); + + /**** 2007-02-07 add by zg to fix cdrouter v3.4 item 321(cdrouter_sip_45) bug ****/ + /**** Verify Via header port is used for response instead of UDP src port ****/ + if (dir == IP_CT_DIR_ORIGINAL) + { + const char *tmpdata=data; + char *u=NULL; + + if ((u = strstr(tmpdata, "Via:")) != NULL) + { + char tmpport[10]; + memset(tmpport,0,sizeof(tmpport)); + tmpdata=u+4; + u= strstr(tmpdata, ":"); + tmpdata=u+1; + memcpy(tmpport,tmpdata,6); + if ((u= strstr(tmpport, ";"))!=NULL) + { + *u = '\0'; + } + if( (ntohs(ct->tuplehash[dir].tuple.src.u.udp.port))!= (u_int16_t)atoi(tmpport) && + (ntohs(ct->tuplehash[dir].tuple.src.u.udp.port))!= SIP_PORT ) + { + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port = htons( (u_int16_t)atoi(tmpport) ); + } + } + else if ((u = strstr(tmpdata, "v:")) != NULL) + { + char tmpport[10]; + memset(tmpport,0,sizeof(tmpport)); + tmpdata=u+4; + u= strstr(tmpdata, ":"); + tmpdata=u+1; + memcpy(tmpport,tmpdata,6); + if ((u= strstr(tmpport, ";"))!=NULL) + { + *u = '\0'; + } + if( (ntohs(ct->tuplehash[dir].tuple.src.u.udp.port))!= (u_int16_t)atoi(tmpport) && + (ntohs(ct->tuplehash[dir].tuple.src.u.udp.port))!= SIP_PORT ) + { + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port = htons( (u_int16_t)atoi(tmpport) ); + } + } + + } + /**** 2007-02-07 end by zg to fix cdrouter v3.4 item 321(cdrouter_sip_45) bug ****/ + + /* Reset for a new incoming packet */ + ct_sip_info->mangled = 0; + + /* keep the connection alive */ + ip_ct_refresh(ct, (SIP_EXPIRES * HZ)); + + /* Don't need to set the expectation for upstream direction */ + if (dir == IP_CT_DIR_REPLY) + return NF_ACCEPT; + + /* Need to set the expected connection for further incoming RTP stream */ + if (strncmp(data, "INVITE", 6) != 0 && strncmp(data, "SIP/2.0 200", 11) != 0) { + DEBUGP("conntrack_sip: Not interesting packet.\n"); + return NF_ACCEPT; + } + + /* Find RTP address */ + found = find_sdp_rtp_addr(data, datalen, &matchoff, &matchlen, &ipaddr); + if (!found) + return NF_ACCEPT; + + DEBUGP("conntrack_sip: 'IN IP4' is %s.\n", (found == 1) ? "FQDNs" : "dotted quads"); + + /* If it's a null address, then the call is on hold */ + if (found == 2 && ipaddr == 0) { + DEBUGP("conntrack_sip: Null address is found.\n"); + return NF_ACCEPT; + } + + /* Find audio port, and we don't like the well-known ports, + * which is less than 1024 */ + port = find_sdp_audio_port(data, datalen, &matchoff, &matchlen); + if (port < 1024) + return NF_ACCEPT; + + DEBUGP("conntrack_sip: audio port=%d.\n", port); + + ipaddr = ct->tuplehash[dir].tuple.src.ip; + ct_sip_info->rtpport = port; + + /* RFC1889 - RTP uses an even port number and the corresponding RTCP + * stream uses the next higher (odd) port number. */ + if (set_expected_rtp(ct, ipaddr, port, CONN_RTP) == 0) { +#ifdef RTCP_SUPPORT + set_expected_rtp(ct, ipaddr, port + 1, CONN_RTCP); +#endif + } + + return NF_ACCEPT; +} + +static struct ip_conntrack_helper sip[MAX_PORTS]; + + +/* Not __exit: called from init() */ +static void fini(void) +{ + int i; + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + DEBUGP("ip_ct_sip: unregistering helper for port %d\n", + ports[i]); + ip_conntrack_helper_unregister(&sip[i]); + } +} + +static int __init init(void) +{ + int i, ret; + + if (ports[0] == 0) + ports[0] = SIP_PORT; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + memset(&sip[i], 0, sizeof(struct ip_conntrack_helper)); + /************** lzh add *************************** + * when the sip packet destination port match 5056-5071, + * not use src port to match, then come in sip alg + * 2006.7.18 + **************************************************/ +#if 0 + sip[i].tuple.dst.u.udp.port = htons(ports[i]); + sip[i].mask.dst.u.udp.port = 0xF0FF; +#else + sip[i].tuple.src.u.udp.port = htons(ports[i]); + sip[i].mask.src.u.udp.port = 0xF0FF; +#endif + //sip[i].tuple.dst.u.udp.port = htons(ports[i]); + sip[i].tuple.dst.protonum = IPPROTO_UDP; + //sip[i].mask.dst.u.udp.port = 0xF0FF; + sip[i].mask.dst.protonum = 0xFFFF; + sip[i].help = help; + sip[i].timeout = RTP_TIMEOUT; + DEBUGP("ip_ct_sip: registering helper for port %d\n", + ports[i]); + ret = ip_conntrack_helper_register(&sip[i]); + + if (ret) { + fini(); + return ret; + } + ports_c++; + } + return 0; +} + + +EXPORT_SYMBOL(ip_sip_lock); +EXPORT_SYMBOL(ip_conntrack_sip); + +module_init(init); +module_exit(fini); |