首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

LInux上的SIP协议跟踪

2012-07-22 
LInux下的SIP协议跟踪本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完

LInux下的SIP协议跟踪
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

1. 前言SIP(Session Initiation Protocol)在RFC3261中定义的用于建立会话的文本协议,多用于VoIP等多媒体应用中,其格式和HTTP类似,先有SIP头定义,然后是具体的数据。目前linux2.6内核中已经正式将SIP跟踪和NAT处理纳入,说明该模块应该经过足够测试证明可用了。以下Linux内核代码版本为2.6.19.2。2. SIP基本信息格式SIP协议本身只定义应用层数据,对于传输层协议是TCP还是UDP没有限制,只是定义了SIP服务端口是5060。以下使用RFC3665中提供的SIP应用实例来描述SIP过程,从中可知道对于NAT设备来说需要修改哪些内容信息。2.1 登记过程    Bob                        SIP Server     |                               |     |          REGISTER F1          |     |------------------------------>|     |      401 Unauthorized F2      |     |<------------------------------|     |          REGISTER F3          |     |------------------------------>|     |            200 OK F4          |     |<------------------------------|     |                               |   Message Details   F1 REGISTER Bob -> SIP Server   REGISTER sips:ss2.biloxi.example.com SIP/2.0   Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashds7   Max-Forwards: 70   From: Bob <sips:bob@biloxi.example.com>;tag=a73kszlfl   To: Bob <sips:bob@biloxi.example.com>   Call-ID: 1j9FpLxk3uxtm8tn@biloxi.example.com   CSeq: 1 REGISTER   Contact: <sips:bob@client.biloxi.example.com>   Content-Length: 0    F2 401 Unauthorized SIP Server -> Bob   SIP/2.0 401 Unauthorized   Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashds7    ;received=192.0.2.201   From: Bob <sips:bob@biloxi.example.com>;tag=a73kszlfl   To: Bob <sips:bob@biloxi.example.com>;tag=1410948204   Call-ID: 1j9FpLxk3uxtm8tn@biloxi.example.com   CSeq: 1 REGISTER   WWW-Authenticate: Digest realm="atlanta.example.com", qop="auth",    nonce="ea9c8e88df84f1cec4341ae6cbe5a359",    opaque="", stale=FALSE, algorithm=MD5   Content-Length: 0   F3 REGISTER Bob -> SIP Server   REGISTER sips:ss2.biloxi.example.com SIP/2.0   Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashd92   Max-Forwards: 70   From: Bob <sips:bob@biloxi.example.com>;tag=ja743ks76zlflH   To: Bob <sips:bob@biloxi.example.com>   Call-ID: 1j9FpLxk3uxtm8tn@biloxi.example.com   CSeq: 2 REGISTER   Contact: <sips:bob@client.biloxi.example.com>   Authorization: Digest username="bob", realm="atlanta.example.com"    nonce="ea9c8e88df84f1cec4341ae6cbe5a359", opaque="",    uri="sips:ss2.biloxi.example.com",    response="dfe56131d1958046689d83306477ecc"   Content-Length: 0   F4 200 OK SIP Server -> Bob   SIP/2.0 200 OK   Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashd92    ;received=192.0.2.201   From: Bob <sips:bob@biloxi.example.com>;tag=ja743ks76zlflH   To: Bob <sips:bob@biloxi.example.com>;tag=37GkEhwl6   Call-ID: 1j9FpLxk3uxtm8tn@biloxi.example.com   CSeq: 2 REGISTER   Contact: <sips:bob@client.biloxi.example.com>;expires=3600   Content-Length: 0由此可见,在“Via:”、“From:”、“To:”、“Call-ID:”、“Contact:”等字段中都有地址表示的ID,对于大部分机器是没有域名的,只能由IP地址表示,因此NAT设备要能修改这些字段中的值。2.2 SIP通信传输数据SIP数据传输时使用SDP(Session Description Protocol, RFC4566)协议来描述数据通道信息:   Alice                     Bob     |                        |     |       INVITE F1        |     |----------------------->|     |    180 Ringing F2      |     |<-----------------------|     |                        |     |       200 OK F3        |     |<-----------------------|     |         ACK F4         |     |----------------------->|     |   Both Way RTP Media   |     |<======================>|     |                        |     |         BYE F5         |     |<-----------------------|     |       200 OK F6        |     |----------------------->|     |                        |   F1 INVITE Alice -> Bob   INVITE sip:bob@biloxi.example.com SIP/2.0   Via: SIP/2.0/TCP client.atlanta.example.com:5060;branch=z9hG4bK74bf9   Max-Forwards: 70   From: Alice <sip:alice@atlanta.example.com>;tag=9fxced76sl   To: Bob <sip:bob@biloxi.example.com>   Call-ID: 3848276298220188511@atlanta.example.com   CSeq: 1 INVITE   Contact: <sip:alice@client.atlanta.example.com;transport=tcp>   Content-Type: application/sdp   Content-Length: 151   v=0   o=alice 2890844526 2890844526 IN IP4 client.atlanta.example.com   s=-   c=IN IP4 192.0.2.101   t=0 0   m=audio 49172 RTP/AVP 0   a=rtpmap:0 PCMU/8000   F2 180 Ringing Bob -> Alice   SIP/2.0 180 Ringing   Via: SIP/2.0/TCP client.atlanta.example.com:5060;branch=z9hG4bK74bf9    ;received=192.0.2.101   From: Alice <sip:alice@atlanta.example.com>;tag=9fxced76sl   To: Bob <sip:bob@biloxi.example.com>;tag=8321234356   Call-ID: 3848276298220188511@atlanta.example.com   CSeq: 1 INVITE   Contact: <sip:bob@client.biloxi.example.com;transport=tcp>   Content-Length: 0   F3 200 OK Bob -> Alice   SIP/2.0 200 OK   Via: SIP/2.0/TCP client.atlanta.example.com:5060;branch=z9hG4bK74bf9    ;received=192.0.2.101   From: Alice <sip:alice@atlanta.example.com>;tag=9fxced76sl   To: Bob <sip:bob@biloxi.example.com>;tag=8321234356   Call-ID: 3848276298220188511@atlanta.example.com   CSeq: 1 INVITE   Contact: <sip:bob@client.biloxi.example.com;transport=tcp>   Content-Type: application/sdp   Content-Length: 147   v=0   o=bob 2890844527 2890844527 IN IP4 client.biloxi.example.com   s=-   c=IN IP4 192.0.2.201   t=0 0   m=audio 3456 RTP/AVP 0   a=rtpmap:0 PCMU/8000   F4 ACK Alice -> Bob   ACK sip:bob@client.biloxi.example.com SIP/2.0   Via: SIP/2.0/TCP client.atlanta.example.com:5060;branch=z9hG4bK74bd5   Max-Forwards: 70   From: Alice <sip:alice@atlanta.example.com>;tag=9fxced76sl   To: Bob <sip:bob@biloxi.example.com>;tag=8321234356   Call-ID: 3848276298220188511@atlanta.example.com   CSeq: 1 ACK   Content-Length: 0   /* RTP streams are established between Alice and Bob */   /* Bob Hangs Up with Alice. Note that the CSeq is NOT 2, since      Alice and Bob maintain their own independent CSeq counts.      (The INVITE was request 1 generated by Alice, and the BYE is      request 1 generated by Bob) */   F5 BYE Bob -> Alice   BYE sip:alice@client.atlanta.example.com SIP/2.0   Via: SIP/2.0/TCP client.biloxi.example.com:5060;branch=z9hG4bKnashds7   Max-Forwards: 70   From: Bob <sip:bob@biloxi.example.com>;tag=8321234356   To: Alice <sip:alice@atlanta.example.com>;tag=9fxced76sl   Call-ID: 3848276298220188511@atlanta.example.com   CSeq: 1 BYE   Content-Length: 0   F6 200 OK Alice -> Bob   SIP/2.0 200 OK   Via: SIP/2.0/TCP client.biloxi.example.com:5060;branch=z9hG4bKnashds7    ;received=192.0.2.201   From: Bob <sip:bob@biloxi.example.com>;tag=8321234356   To: Alice <sip:alice@atlanta.example.com>;tag=9fxced76sl   Call-ID: 3848276298220188511@atlanta.example.com   CSeq: 1 BYE   Content-Length: 0可见,在SDP定义数据中,“o=”、“c=”中有地址信息,“m=”中有媒体通信用的端口信息,这些都需要NAT设备修改,如果修改后SDP数据长度发生变化,则应该修改SIP头中的“Content-Length:”字段的值。3. SIP跟踪SIP跟踪处理文件为net/ipv4/netfilter/ip_conntrack.sip.c, 头文件为include/linux/netfilter_ipv4/ip_conntrack_sip.h.3.1 初始化static int __init init(void){ int i, ret; char *tmpname; if (ports_c == 0)  ports[ports_c++] = SIP_PORT; for (i = 0; i < ports_c; i++) {// 以下定义SIP的ip_conntrack_helper结构参数  /* Create helper structure */  memset(&sip[i], 0, sizeof(struct ip_conntrack_helper));// 只处理使用UDP协议的SIP// 使用UDP协议简化很多处理,如TCP序列号跟踪等  sip[i].tuple.dst.protonum = IPPROTO_UDP;// 跟踪端口,缺省5060  sip[i].tuple.src.u.udp.port = htons(ports[i]);// tuple掩码  sip[i].mask.src.u.udp.port = htons(0xFFFF);  sip[i].mask.dst.protonum = 0xFF;// 最大的并发子连接数为2个  sip[i].max_expected = 2;// 3分钟的子连接超时  sip[i].timeout = 3 * 60; /* 3 minutes */  sip[i].me = THIS_MODULE;// 跟踪帮助函数  sip[i].help = sip_help;// helper的名字  tmpname = &sip_names[i][0];  if (ports[i] == SIP_PORT)   sprintf(tmpname, "sip");  else   sprintf(tmpname, "sip-%d", i);  sip[i].name = tmpname;  DEBUGP("port #%d: %d\n", i, ports[i]);// 登记跟踪函数  ret = ip_conntrack_helper_register(&sip[i]);  if (ret) {   printk("ERROR registering helper for port %d\n",    ports[i]);   fini();   return ret;  } } return 0;}3.2 sip_helpstatic int sip_help(struct sk_buff **pskb,      struct ip_conntrack *ct,      enum ip_conntrack_info ctinfo){ unsigned int dataoff, datalen; const char *dptr; int ret = NF_ACCEPT; int matchoff, matchlen; __be32 ipaddr; u_int16_t port; /* No Data ? */// dataoff为ip头加UDP头长度 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= (*pskb)->len) {// dataoff大于等于整个IP包数据长度, 没应用数据  DEBUGP("skb->len = %u\n", (*pskb)->len);  return NF_ACCEPT;        }// 更新一下该连接的超时, 用的是sip专门定义的超时值而不是标准的UDP超时(30秒)// 缺省3600秒 ip_ct_refresh(ct, *pskb, sip_timeout * HZ);// 如果这个包是非线性的,不处理,只处理线性包// 其实可以用skb_make_writable处理一下即可继续解析 if (!skb_is_nonlinear(*pskb))// 从应用层数据开始解析  dptr = (*pskb)->data + dataoff; else {  DEBUGP("Copy of skbuff not supported yet.\n");  goto out; } if (ip_nat_sip_hook) {// 如果定义了SIP NAT, 修改SIP头中信息  if (!ip_nat_sip_hook(pskb, ctinfo, ct, &dptr)) {   ret = NF_DROP;   goto out;  } } /* After this point NAT, could have mangled skb, so    we need to recalculate payload lenght. */// 数据长度 datalen = (*pskb)->len - dataoff;// 以下重点检查和修改SDP部分的信息// 合法的最小长度检查 if (datalen < (sizeof("SIP/2.0 200") - 1))  goto out; /* RTP info only in some SDP pkts */// SDP定义的RTP信息只在处理发起方的INVITE包和相应方的200信息// 其他的都不处理 if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 &&     memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) {  goto out; } /* Get ip and port address from SDP packet. */// 查找SDP中的"c="信息, matchoff为地址相对起点的偏移 if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,                     &ct_sip_hdrs[POS_CONNECTION]) > 0) {  /* We'll drop only if there are parse problems. */// 解析"c="中的地址信息, 失败则丢包  if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr,                   dptr + datalen) < 0) {   ret = NF_DROP;   goto out;  }// 查找SDP中的"m="信息, matchoff为端口相对起点的偏移  if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,                      &ct_sip_hdrs[POS_MEDIA]) > 0) {// 获取端口   port = simple_strtoul(dptr + matchoff, NULL, 10);// 端口不可能是特权端口,只能是普通端口   if (port < 1024) {    ret = NF_DROP;    goto out;   }// 建立期待的子连接信息   ret = set_expected_rtp(pskb, ct, ctinfo,            ipaddr, port, dptr);  } }out: return ret;}3.3 ct_sip_get_info该函数在sip数据中查找指定的模式,获取模式的偏移和长度/* Returns 0 if not found, -1 error parsing. */// dptr为缓冲区起点, dlen为缓冲区总长// matchoff和matchlen作为成功时的返回值, 记录查找模式的偏移是长度信息// hnfo为要查找的模式信息指针int ct_sip_get_info(const char *dptr, size_t dlen,      unsigned int *matchoff,      unsigned int *matchlen,      struct sip_header_nfo *hnfo){ const char *limit, *aux, *k = dptr; int shift = 0;// 查找结束点 limit = dptr + (dlen - hnfo->lnlen); while (dptr <= limit) {// 线性查找两个模式: lname和sname, lname是全称, sname是缩写名称, 两种都合法// 注意用的是大小写敏感的strncmp, 似乎用strnicmp更好一些  if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&      (strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {   dptr++;   continue;  }// 找到模式// 在当前行中查找ln_str标志信息,如"UDP", "sip:"等, 这就是个普通字符串查找函数  aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,                      ct_sip_lnlen(dptr, limit));  if (!aux) {// 没有标志, 出错   DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str,          hnfo->lname);   return -1;  }// aux跳过标志长度  aux += hnfo->ln_strlen;// 计算匹配的模式长度, shift是从aux到实际模式地址的偏移  *matchlen = hnfo->match_len(aux, limit, &shift);// 如果为匹配长度为0出错  if (!*matchlen)   return -1;// 模式相对数据头的偏移, 跳过了标志本身  *matchoff = (aux - k) + shift;  DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname,         *matchlen);  return 1; } DEBUGP("%s header not found.\n", hnfo->lname); return 0;}可查找的数据模式定义如下:// 用于查找SIP头中的“Via:”、“Contact:”、“Content-Length:”// SDP头中的“m=”、“v=”、“o=”、“c=”等struct sip_header_nfo ct_sip_hdrs[] = { {  /* Via header */  .lname  = "Via:",  .lnlen  = sizeof("Via:") - 1,  .sname  = "\r\nv:",  .snlen  = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */  .ln_str  = "UDP ",  .ln_strlen = sizeof("UDP ") - 1,  .match_len = epaddr_len, }, {  /* Contact header */  .lname  = "Contact:",  .lnlen  = sizeof("Contact:") - 1,  .sname  = "\r\nm:",  .snlen  = sizeof("\r\nm:") - 1,  .ln_str  = "sip:",  .ln_strlen = sizeof("sip:") - 1,  .match_len = skp_epaddr_len }, {  /* Content length header */  .lname  = "Content-Length:",  .lnlen  = sizeof("Content-Length:") - 1,  .sname  = "\r\nl:",  .snlen  = sizeof("\r\nl:") - 1,  .ln_str  = ":",  .ln_strlen = sizeof(":") - 1,  .match_len = skp_digits_len }, { /* SDP media info */  .lname  = "\nm=",  .lnlen  = sizeof("\nm=") - 1,  .sname  = "\rm=",  .snlen  = sizeof("\rm=") - 1,  .ln_str  = "audio ",  .ln_strlen = sizeof("audio ") - 1,  .match_len = digits_len }, {  /* SDP owner address*/  .lname  = "\no=",  .lnlen  = sizeof("\no=") - 1,  .sname  = "\ro=",  .snlen  = sizeof("\ro=") - 1,  .ln_str  = "IN IP4 ",  .ln_strlen = sizeof("IN IP4 ") - 1,  .match_len = epaddr_len }, {  /* SDP connection info */  .lname  = "\nc=",  .lnlen  = sizeof("\nc=") - 1,  .sname  = "\rc=",  .snlen  = sizeof("\rc=") - 1,  .ln_str  = "IN IP4 ",  .ln_strlen = sizeof("IN IP4 ") - 1,  .match_len = epaddr_len }, {  /* Requests headers */  .lname  = "sip:",  .lnlen  = sizeof("sip:") - 1,  .sname  = "sip:",  .snlen  = sizeof("sip:") - 1, /* yes, i know.. ;) */  .ln_str  = "@",  .ln_strlen = sizeof("@") - 1,  .match_len = epaddr_len }, {  /* SDP version header */  .lname  = "\nv=",  .lnlen  = sizeof("\nv=") - 1,  .sname  = "\rv=",  .snlen  = sizeof("\rv=") - 1,  .ln_str  = "=",  .ln_strlen = sizeof("=") - 1,  .match_len = digits_len }};3.4 建立期待连接信息static int set_expected_rtp(struct sk_buff **pskb,       struct ip_conntrack *ct,       enum ip_conntrack_info ctinfo,       __be32 ipaddr, u_int16_t port,       const char *dptr){ struct ip_conntrack_expect *exp; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); int ret;// 分配expect连接空间 exp = ip_conntrack_expect_alloc(ct); if (exp == NULL)  return NF_DROP;// 期待连接的端口地址信息, 解析出的地址端口用于目的方 exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; exp->tuple.src.u.udp.port = 0; exp->tuple.dst.ip = ipaddr; exp->tuple.dst.u.udp.port = htons(port); exp->tuple.dst.protonum = IPPROTO_UDP;// 掩码部分地址 exp->mask.src.ip = htonl(0xFFFFFFFF); exp->mask.src.u.udp.port = 0; exp->mask.dst.ip = htonl(0xFFFFFFFF); exp->mask.dst.u.udp.port = htons(0xFFFF); exp->mask.dst.protonum = 0xFF; exp->expectfn = NULL; exp->flags = 0; if (ip_nat_sdp_hook)// 如果定义了SDP的NAT处理,修改SDP数据中的信息,并建立期待连接  ret = ip_nat_sdp_hook(pskb, ctinfo, exp, dptr); else {// 建立期待连接  if (ip_conntrack_expect_related(exp) != 0)   ret = NF_DROP;  else   ret = NF_ACCEPT; } ip_conntrack_expect_put(exp); return ret;}4. SIP的NAT处理nat处理函数为net/ipv4/netfilter/ip_nat_sip.c, 包括两个函数, 分别处理SIP和SDP数据4.1 ip_nat_sip (ip_nat_sip_hook)// 只修改SIP头中的数据, 不建立期待连接// 函数返回0表示失败, 非0表示成功// 最多的情况下需要修改两个字段的信息static unsigned int ip_nat_sip(struct sk_buff **pskb,          enum ip_conntrack_info ctinfo,          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; __be32 ip; __be16 port;// 重新计算应用层数据的起点 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);// 找到NAT转换后的地址和端口数据, 也就是相反方向的目的地址端口 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)); /* short packet ? */// 异常短包, 返回// 不过应该提前点操作, 这样就不用计算ip, port和bufflen了 if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1))  return 0; /* Basic rules: requests and responses. */ if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {// 数据以"SIP/2.0"开头, 是SIP回应数据  const char *aux;// 此处为什么不用dir判断呢?  if ((ctinfo) < IP_CT_IS_REPLY) {// 正方向数据, 发起方->响应方,修改"Contact: "字段中的地址端口数据, 用buffer中的数据替代   mangle_sip_packet(pskb, ctinfo, ct, dptr,                     (*pskb)->len - dataoff,                     buffer, bufflen,                     &ct_sip_hdrs[POS_CONTACT]);   return 1;  }// 反方向数据, 响应方->发起方// 修改"Via: "字段中的地址端口数据, 用buffer中的数据替代// 返回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.. */// 查找"CSeq:"字符串的位置  aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,                      (*pskb)->len - dataoff);  if (!aux)   return 0;// 如果在"CSeq:"字段行中没有"REGISTER",  if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),      ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff)))   return 1;// 修改"Contact: "字段中的地址端口数据, 用buffer中的数据替代  return mangle_sip_packet(pskb, ctinfo, ct, dptr,      (*pskb)->len - dataoff,                           buffer, bufflen,      &ct_sip_hdrs[POS_CONTACT]); }// 运行到这里说明数据不是以"SIP/2.0"开头的, 是SIP请求方数据// 此处为什么不用dir判断呢? if ((ctinfo) < IP_CT_IS_REPLY) {// 正方向数据, 发起方->响应方,修改"Via: "字段中的地址端口数据, 用buffer中的数据替代  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()! */// 修改"Contact: "字段中的地址端口数据, 用buffer中的数据替代  mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff,                    buffer, bufflen, &ct_sip_hdrs[POS_CONTACT]);  return 1; }// 修改的是反方向数据 /* This mangle requests headers. */// 修改"sip:"中的数据 return mangle_sip_packet(pskb, ctinfo, ct, dptr,                          ct_sip_lnlen(*dptr,                  *dptr + (*pskb)->len - dataoff),                          buffer, bufflen, &ct_sip_hdrs[POS_REQ_HEADER]);}// 修改SIP字段// 查找由hnfo定义的数据, 然后用buffer中的数据替代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){ unsigned int matchlen, matchoff;// 查找hnfo定义的数据, 获取偏移地址matchoff和长度matchlen if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, hnfo) <= 0)  return 0;// 修改数据内容, 用buffer内容替代matchoff出的数据 if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,                               matchoff, matchlen, buffer, bufflen))  return 0; /* We need to reload this. Thanks Patrick. */// 重新定位应用层数据起始地址 *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); return 1;}4.2 ip_nat_sdp(ip_nat_sdp_hook)修改SDP数据, 并根据SDP中的参数建立期待子连接参数,函数返回NF_ACCEPT或NF_DROP/* So, this packet has hit the connection tracking matching code.   Mangle it, and change the expectation to match the new version. */static unsigned int ip_nat_sdp(struct sk_buff **pskb,          enum ip_conntrack_info ctinfo,          struct ip_conntrack_expect *exp,          const char *dptr){ struct ip_conntrack *ct = exp->master; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); __be32 newip; u_int16_t port; DEBUGP("ip_nat_sdp():\n"); /* Connection will come from reply */// NAT修改后的地址 newip = ct->tuplehash[!dir].tuple.dst.ip;// 期待连接的参数 exp->tuple.dst.ip = newip; exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; exp->dir = !dir; /* When you see the packet, we need to NAT it the same as the    this one. */// 期待处理函数, 用于建立子连接的nat信息 exp->expectfn = ip_nat_follow_master; /* Try to get same port: if not, try to change it. */// 查找一个可用的空闲端口代替原来的端口值 for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {  exp->tuple.dst.u.udp.port = htons(port);  if (ip_conntrack_expect_related(exp) == 0)   break; }// 没可用端口了, 丢包 if (port == 0)  return NF_DROP;// 修改SDP数据 if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {  ip_conntrack_unexpect_related(exp);  return NF_DROP; } return NF_ACCEPT;}// 修改SDP数据static unsigned int mangle_sdp(struct sk_buff **pskb,          enum ip_conntrack_info ctinfo,          struct ip_conntrack *ct,// 新地址,端口值          __be32 newip, u_int16_t port,          const char *dptr){ char buffer[sizeof("nnn.nnn.nnn.nnn")]; unsigned int dataoff, bufflen; dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); /* Mangle owner and contact info. */// 新地址字符串 bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));// 修改"o="字段中的地址 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,                        buffer, bufflen, &ct_sip_hdrs[POS_OWNER]))  return 0;// 修改"c="字段中的地址信息 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,                        buffer, bufflen, &ct_sip_hdrs[POS_CONNECTION]))  return 0; /* Mangle media port. */// 新端口字符串 bufflen = sprintf(buffer, "%u", port);// 修改"m="字段中的端口信息 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,                        buffer, bufflen, &ct_sip_hdrs[POS_MEDIA]))  return 0;// 最后修改"Content-Length: "字段中的内容长度值, 因为修改了上述SDP数据后长度// 可能会发生变化 return mangle_content_len(pskb, ctinfo, ct, dptr);}// 修改"Content-Length: "字段中的内容长度值static int mangle_content_len(struct sk_buff **pskb,         enum ip_conntrack_info ctinfo,         struct ip_conntrack *ct,         const char *dptr){ unsigned int dataoff, matchoff, matchlen; char buffer[sizeof("65536")]; int bufflen; dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); /* Get actual SDP lenght */// 找"v="字符串位置,这是SDP数据的起始点 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,                     &matchlen, &ct_sip_hdrs[POS_SDP_HEADER]) > 0) {  /* since ct_sip_get_info() give us a pointer passing 'v='     we need to add 2 bytes in this count. */// 目前SDP数据的真实长度, 最后+2是因为要加回"v="这两个字符长度  int c_len = (*pskb)->len - dataoff - matchoff + 2;  /* Now, update SDP lenght */// 找"Content-Length: "字段位置,获取数据长度  if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,                      &matchlen, &ct_sip_hdrs[POS_CONTENT]) > 0) {// 新长度字符串   bufflen = sprintf(buffer, "%u", c_len);// 更新长度数据   return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,       matchoff, matchlen,       buffer, bufflen);  } } return 0;} 5. 结论Linux内核中的SIP跟踪和NAT模块基本上比较完善地解决了SIP处理, 不过只是针对UDP协议的, 如果是TCP实现的SIP则无效。 在编程中,定义了struct sip_header_nfo结构来描述要查找的各种类型数据的信息,实现对象化编程,而且使程序更简洁。不过查找数据应该要支持大小写无关方式查找数据,扫描数据次数也比较多,最多会扫描4次。

热点排行