/* SIP extension for IP connection tracking. * * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> * based on RR's ip_conntrack_ftp.c and other modules. * (C) 2007 United Security Providers * (C) 2007, 2008 Patrick McHardy <kaber@trash.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include<linux/module.h>#include<linux/ctype.h>#include<linux/skbuff.h>#include<linux/inet.h>#include<linux/in.h>#include<linux/udp.h>#include<linux/tcp.h>#include<linux/netfilter.h>#include<net/netfilter/nf_conntrack.h>#include<net/netfilter/nf_conntrack_core.h>#include<net/netfilter/nf_conntrack_expect.h>#include<net/netfilter/nf_conntrack_helper.h>#include<net/netfilter/nf_conntrack_zones.h>#include<linux/netfilter/nf_conntrack_sip.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");MODULE_DESCRIPTION("SIP connection tracking helper");MODULE_ALIAS("ip_conntrack_sip");MODULE_ALIAS_NFCT_HELPER("sip");#define MAX_PORTS 8staticunsignedshortports[MAX_PORTS];staticunsignedintports_c;module_param_array(ports,ushort,&ports_c,0400);MODULE_PARM_DESC(ports,"port numbers of SIP servers");staticunsignedintsip_timeout__read_mostly=SIP_TIMEOUT;module_param(sip_timeout,uint,0600);MODULE_PARM_DESC(sip_timeout,"timeout for the master SIP session");staticintsip_direct_signalling__read_mostly=1;module_param(sip_direct_signalling,int,0600);MODULE_PARM_DESC(sip_direct_signalling,"expect incoming calls from registrar ""only (default 1)");staticintsip_direct_media__read_mostly=1;module_param(sip_direct_media,int,0600);MODULE_PARM_DESC(sip_direct_media,"Expect Media streams between signalling ""endpoints only (default 1)");unsignedint(*nf_nat_sip_hook)(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen)__read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sip_hook);void(*nf_nat_sip_seq_adjust_hook)(structsk_buff*skb,unsignedintprotoff,s16off)__read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook);unsignedint(*nf_nat_sip_expect_hook)(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,structnf_conntrack_expect*exp,unsignedintmatchoff,unsignedintmatchlen)__read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook);unsignedint(*nf_nat_sdp_addr_hook)(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintsdpoff,enumsdp_header_typestype,enumsdp_header_typesterm,constunionnf_inet_addr*addr)__read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook);unsignedint(*nf_nat_sdp_port_hook)(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintmatchoff,unsignedintmatchlen,u_int16_tport)__read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook);unsignedint(*nf_nat_sdp_session_hook)(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintsdpoff,constunionnf_inet_addr*addr)__read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook);unsignedint(*nf_nat_sdp_media_hook)(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,structnf_conntrack_expect*rtp_exp,structnf_conntrack_expect*rtcp_exp,unsignedintmediaoff,unsignedintmedialen,unionnf_inet_addr*rtp_addr)__read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook);staticintstring_len(conststructnf_conn*ct,constchar*dptr,constchar*limit,int*shift){intlen=0;while(dptr<limit&&isalpha(*dptr)){dptr++;len++;}returnlen;}staticintdigits_len(conststructnf_conn*ct,constchar*dptr,constchar*limit,int*shift){intlen=0;while(dptr<limit&&isdigit(*dptr)){dptr++;len++;}returnlen;}staticintiswordc(constcharc){if(isalnum(c)||c=='!'||c=='"'||c=='%'||(c>='('&&c<='/')||c==':'||c=='<'||c=='>'||c=='?'||(c>='['&&c<=']')||c=='_'||c=='`'||c=='{'||c=='}'||c=='~')return1;return0;}staticintword_len(constchar*dptr,constchar*limit){intlen=0;while(dptr<limit&&iswordc(*dptr)){dptr++;len++;}returnlen;}staticintcallid_len(conststructnf_conn*ct,constchar*dptr,constchar*limit,int*shift){intlen,domain_len;len=word_len(dptr,limit);dptr+=len;if(!len||dptr==limit||*dptr!='@')returnlen;dptr++;len++;domain_len=word_len(dptr,limit);if(!domain_len)return0;returnlen+domain_len;}/* get media type + port length */staticintmedia_len(conststructnf_conn*ct,constchar*dptr,constchar*limit,int*shift){intlen=string_len(ct,dptr,limit,shift);dptr+=len;if(dptr>=limit||*dptr!=' ')return0;len++;dptr++;returnlen+digits_len(ct,dptr,limit,shift);}staticintsip_parse_addr(conststructnf_conn*ct,constchar*cp,constchar**endp,unionnf_inet_addr*addr,constchar*limit,booldelim){constchar*end;intret;if(!ct)return0;memset(addr,0,sizeof(*addr));switch(nf_ct_l3num(ct)){caseAF_INET:ret=in4_pton(cp,limit-cp,(u8*)&addr->ip,-1,&end);if(ret==0)return0;break;caseAF_INET6:if(cp<limit&&*cp=='[')cp++;elseif(delim)return0;ret=in6_pton(cp,limit-cp,(u8*)&addr->ip6,-1,&end);if(ret==0)return0;if(end<limit&&*end==']')end++;elseif(delim)return0;break;default:BUG();}if(endp)*endp=end;return1;}/* skip ip address. returns its length. */staticintepaddr_len(conststructnf_conn*ct,constchar*dptr,constchar*limit,int*shift){unionnf_inet_addraddr;constchar*aux=dptr;if(!sip_parse_addr(ct,dptr,&dptr,&addr,limit,true)){pr_debug("ip: %s parse failed.!\n",dptr);return0;}/* Port number */if(*dptr==':'){dptr++;dptr+=digits_len(ct,dptr,limit,shift);}returndptr-aux;}/* get address length, skiping user info. */staticintskp_epaddr_len(conststructnf_conn*ct,constchar*dptr,constchar*limit,int*shift){constchar*start=dptr;ints=*shift;/* Search for @, but stop at the end of the line. * We are inside a sip: URI, so we don't need to worry about * continuation lines. */while(dptr<limit&&*dptr!='@'&&*dptr!='\r'&&*dptr!='\n'){(*shift)++;dptr++;}if(dptr<limit&&*dptr=='@'){dptr++;(*shift)++;}else{dptr=start;*shift=s;}returnepaddr_len(ct,dptr,limit,shift);}/* Parse a SIP request line of the form: * * Request-Line = Method SP Request-URI SP SIP-Version CRLF * * and return the offset and length of the address contained in the Request-URI. */intct_sip_parse_request(conststructnf_conn*ct,constchar*dptr,unsignedintdatalen,unsignedint*matchoff,unsignedint*matchlen,unionnf_inet_addr*addr,__be16*port){constchar*start=dptr,*limit=dptr+datalen,*end;unsignedintmlen;unsignedintp;intshift=0;/* Skip method and following whitespace */mlen=string_len(ct,dptr,limit,NULL);if(!mlen)return0;dptr+=mlen;if(++dptr>=limit)return0;/* Find SIP URI */for(;dptr<limit-strlen("sip:");dptr++){if(*dptr=='\r'||*dptr=='\n')return-1;if(strnicmp(dptr,"sip:",strlen("sip:"))==0){dptr+=strlen("sip:");break;}}if(!skp_epaddr_len(ct,dptr,limit,&shift))return0;dptr+=shift;if(!sip_parse_addr(ct,dptr,&end,addr,limit,true))return-1;if(end<limit&&*end==':'){end++;p=simple_strtoul(end,(char**)&end,10);if(p<1024||p>65535)return-1;*port=htons(p);}else*port=htons(SIP_PORT);if(end==dptr)return0;*matchoff=dptr-start;*matchlen=end-dptr;return1;}EXPORT_SYMBOL_GPL(ct_sip_parse_request);/* SIP header parsing: SIP headers are located at the beginning of a line, but * may span several lines, in which case the continuation lines begin with a * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or * CRLF, RFC 3261 allows only CRLF, we support both. * * Headers are followed by (optionally) whitespace, a colon, again (optionally) * whitespace and the values. Whitespace in this context means any amount of * tabs, spaces and continuation lines, which are treated as a single whitespace * character. * * Some headers may appear multiple times. A comma separated list of values is * equivalent to multiple headers. */staticconststructsip_headerct_sip_hdrs[]={[SIP_HDR_CSEQ]=SIP_HDR("CSeq",NULL,NULL,digits_len),[SIP_HDR_FROM]=SIP_HDR("From","f","sip:",skp_epaddr_len),[SIP_HDR_TO]=SIP_HDR("To","t","sip:",skp_epaddr_len),[SIP_HDR_CONTACT]=SIP_HDR("Contact","m","sip:",skp_epaddr_len),[SIP_HDR_VIA_UDP]=SIP_HDR("Via","v","UDP ",epaddr_len),[SIP_HDR_VIA_TCP]=SIP_HDR("Via","v","TCP ",epaddr_len),[SIP_HDR_EXPIRES]=SIP_HDR("Expires",NULL,NULL,digits_len),[SIP_HDR_CONTENT_LENGTH]=SIP_HDR("Content-Length","l",NULL,digits_len),[SIP_HDR_CALL_ID]=SIP_HDR("Call-Id","i",NULL,callid_len),};staticconstchar*sip_follow_continuation(constchar*dptr,constchar*limit){/* Walk past newline */if(++dptr>=limit)returnNULL;/* Skip '\n' in CR LF */if(*(dptr-1)=='\r'&&*dptr=='\n'){if(++dptr>=limit)returnNULL;}/* Continuation line? */if(*dptr!=' '&&*dptr!='\t')returnNULL;/* skip leading whitespace */for(;dptr<limit;dptr++){if(*dptr!=' '&&*dptr!='\t')break;}returndptr;}staticconstchar*sip_skip_whitespace(constchar*dptr,constchar*limit){for(;dptr<limit;dptr++){if(*dptr==' ')continue;if(*dptr!='\r'&&*dptr!='\n')break;dptr=sip_follow_continuation(dptr,limit);if(dptr==NULL)returnNULL;}returndptr;}/* Search within a SIP header value, dealing with continuation lines */staticconstchar*ct_sip_header_search(constchar*dptr,constchar*limit,constchar*needle,unsignedintlen){for(limit-=len;dptr<limit;dptr++){if(*dptr=='\r'||*dptr=='\n'){dptr=sip_follow_continuation(dptr,limit);if(dptr==NULL)break;continue;}if(strnicmp(dptr,needle,len)==0)returndptr;}returnNULL;}intct_sip_get_header(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,enumsip_header_typestype,unsignedint*matchoff,unsignedint*matchlen){conststructsip_header*hdr=&ct_sip_hdrs[type];constchar*start=dptr,*limit=dptr+datalen;intshift=0;for(dptr+=dataoff;dptr<limit;dptr++){/* Find beginning of line */if(*dptr!='\r'&&*dptr!='\n')continue;if(++dptr>=limit)break;if(*(dptr-1)=='\r'&&*dptr=='\n'){if(++dptr>=limit)break;}/* Skip continuation lines */if(*dptr==' '||*dptr=='\t')continue;/* Find header. Compact headers must be followed by a * non-alphabetic character to avoid mismatches. */if(limit-dptr>=hdr->len&&strnicmp(dptr,hdr->name,hdr->len)==0)dptr+=hdr->len;elseif(hdr->cname&&limit-dptr>=hdr->clen+1&&strnicmp(dptr,hdr->cname,hdr->clen)==0&&!isalpha(*(dptr+hdr->clen)))dptr+=hdr->clen;elsecontinue;/* Find and skip colon */dptr=sip_skip_whitespace(dptr,limit);if(dptr==NULL)break;if(*dptr!=':'||++dptr>=limit)break;/* Skip whitespace after colon */dptr=sip_skip_whitespace(dptr,limit);if(dptr==NULL)break;*matchoff=dptr-start;if(hdr->search){dptr=ct_sip_header_search(dptr,limit,hdr->search,hdr->slen);if(!dptr)return-1;dptr+=hdr->slen;}*matchlen=hdr->match_len(ct,dptr,limit,&shift);if(!*matchlen)return-1;*matchoff=dptr-start+shift;return1;}return0;}EXPORT_SYMBOL_GPL(ct_sip_get_header);/* Get next header field in a list of comma separated values */staticintct_sip_next_header(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,enumsip_header_typestype,unsignedint*matchoff,unsignedint*matchlen){conststructsip_header*hdr=&ct_sip_hdrs[type];constchar*start=dptr,*limit=dptr+datalen;intshift=0;dptr+=dataoff;dptr=ct_sip_header_search(dptr,limit,",",strlen(","));if(!dptr)return0;dptr=ct_sip_header_search(dptr,limit,hdr->search,hdr->slen);if(!dptr)return0;dptr+=hdr->slen;*matchoff=dptr-start;*matchlen=hdr->match_len(ct,dptr,limit,&shift);if(!*matchlen)return-1;*matchoff+=shift;return1;}/* Walk through headers until a parsable one is found or no header of the * given type is left. */staticintct_sip_walk_headers(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,enumsip_header_typestype,int*in_header,unsignedint*matchoff,unsignedint*matchlen){intret;if(in_header&&*in_header){while(1){ret=ct_sip_next_header(ct,dptr,dataoff,datalen,type,matchoff,matchlen);if(ret>0)returnret;if(ret==0)break;dataoff+=*matchoff;}*in_header=0;}while(1){ret=ct_sip_get_header(ct,dptr,dataoff,datalen,type,matchoff,matchlen);if(ret>0)break;if(ret==0)returnret;dataoff+=*matchoff;}if(in_header)*in_header=1;return1;}/* Locate a SIP header, parse the URI and return the offset and length of * the address as well as the address and port themselves. A stream of * headers can be parsed by handing in a non-NULL datalen and in_header * pointer. */intct_sip_parse_header_uri(conststructnf_conn*ct,constchar*dptr,unsignedint*dataoff,unsignedintdatalen,enumsip_header_typestype,int*in_header,unsignedint*matchoff,unsignedint*matchlen,unionnf_inet_addr*addr,__be16*port){constchar*c,*limit=dptr+datalen;unsignedintp;intret;ret=ct_sip_walk_headers(ct,dptr,dataoff?*dataoff:0,datalen,type,in_header,matchoff,matchlen);WARN_ON(ret<0);if(ret==0)returnret;if(!sip_parse_addr(ct,dptr+*matchoff,&c,addr,limit,true))return-1;if(*c==':'){c++;p=simple_strtoul(c,(char**)&c,10);if(p<1024||p>65535)return-1;*port=htons(p);}else*port=htons(SIP_PORT);if(dataoff)*dataoff=c-dptr;return1;}EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri);staticintct_sip_parse_param(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,constchar*name,unsignedint*matchoff,unsignedint*matchlen){constchar*limit=dptr+datalen;constchar*start;constchar*end;limit=ct_sip_header_search(dptr+dataoff,limit,",",strlen(","));if(!limit)limit=dptr+datalen;start=ct_sip_header_search(dptr+dataoff,limit,name,strlen(name));if(!start)return0;start+=strlen(name);end=ct_sip_header_search(start,limit,";",strlen(";"));if(!end)end=limit;*matchoff=start-dptr;*matchlen=end-start;return1;}/* Parse address from header parameter and return address, offset and length */intct_sip_parse_address_param(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,constchar*name,unsignedint*matchoff,unsignedint*matchlen,unionnf_inet_addr*addr,booldelim){constchar*limit=dptr+datalen;constchar*start,*end;limit=ct_sip_header_search(dptr+dataoff,limit,",",strlen(","));if(!limit)limit=dptr+datalen;start=ct_sip_header_search(dptr+dataoff,limit,name,strlen(name));if(!start)return0;start+=strlen(name);if(!sip_parse_addr(ct,start,&end,addr,limit,delim))return0;*matchoff=start-dptr;*matchlen=end-start;return1;}EXPORT_SYMBOL_GPL(ct_sip_parse_address_param);/* Parse numerical header parameter and return value, offset and length */intct_sip_parse_numerical_param(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,constchar*name,unsignedint*matchoff,unsignedint*matchlen,unsignedint*val){constchar*limit=dptr+datalen;constchar*start;char*end;limit=ct_sip_header_search(dptr+dataoff,limit,",",strlen(","));if(!limit)limit=dptr+datalen;start=ct_sip_header_search(dptr+dataoff,limit,name,strlen(name));if(!start)return0;start+=strlen(name);*val=simple_strtoul(start,&end,0);if(start==end)return0;if(matchoff&&matchlen){*matchoff=start-dptr;*matchlen=end-start;}return1;}EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param);staticintct_sip_parse_transport(structnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,u8*proto){unsignedintmatchoff,matchlen;if(ct_sip_parse_param(ct,dptr,dataoff,datalen,"transport=",&matchoff,&matchlen)){if(!strnicmp(dptr+matchoff,"TCP",strlen("TCP")))*proto=IPPROTO_TCP;elseif(!strnicmp(dptr+matchoff,"UDP",strlen("UDP")))*proto=IPPROTO_UDP;elsereturn0;if(*proto!=nf_ct_protonum(ct))return0;}else*proto=nf_ct_protonum(ct);return1;}staticintsdp_parse_addr(conststructnf_conn*ct,constchar*cp,constchar**endp,unionnf_inet_addr*addr,constchar*limit){constchar*end;intret;memset(addr,0,sizeof(*addr));switch(nf_ct_l3num(ct)){caseAF_INET:ret=in4_pton(cp,limit-cp,(u8*)&addr->ip,-1,&end);break;caseAF_INET6:ret=in6_pton(cp,limit-cp,(u8*)&addr->ip6,-1,&end);break;default:BUG();}if(ret==0)return0;if(endp)*endp=end;return1;}/* skip ip address. returns its length. */staticintsdp_addr_len(conststructnf_conn*ct,constchar*dptr,constchar*limit,int*shift){unionnf_inet_addraddr;constchar*aux=dptr;if(!sdp_parse_addr(ct,dptr,&dptr,&addr,limit)){pr_debug("ip: %s parse failed.!\n",dptr);return0;}returndptr-aux;}/* SDP header parsing: a SDP session description contains an ordered set of * headers, starting with a section containing general session parameters, * optionally followed by multiple media descriptions. * * SDP headers always start at the beginning of a line. According to RFC 2327: * "The sequence CRLF (0x0d0a) is usedto end a record, although parsers should * be tolerantand also acceptrecords terminated with a single newline * character". We handle both cases. */staticconststructsip_headerct_sdp_hdrs_v4[]={[SDP_HDR_VERSION]=SDP_HDR("v=",NULL,digits_len),[SDP_HDR_OWNER]=SDP_HDR("o=","IN IP4 ",sdp_addr_len),[SDP_HDR_CONNECTION]=SDP_HDR("c=","IN IP4 ",sdp_addr_len),[SDP_HDR_MEDIA]=SDP_HDR("m=",NULL,media_len),};staticconststructsip_headerct_sdp_hdrs_v6[]={[SDP_HDR_VERSION]=SDP_HDR("v=",NULL,digits_len),[SDP_HDR_OWNER]=SDP_HDR("o=","IN IP6 ",sdp_addr_len),[SDP_HDR_CONNECTION]=SDP_HDR("c=","IN IP6 ",sdp_addr_len),[SDP_HDR_MEDIA]=SDP_HDR("m=",NULL,media_len),};/* Linear string search within SDP header values */staticconstchar*ct_sdp_header_search(constchar*dptr,constchar*limit,constchar*needle,unsignedintlen){for(limit-=len;dptr<limit;dptr++){if(*dptr=='\r'||*dptr=='\n')break;if(strncmp(dptr,needle,len)==0)returndptr;}returnNULL;}/* Locate a SDP header (optionally a substring within the header value), * optionally stopping at the first occurrence of the term header, parse * it and return the offset and length of the data we're interested in. */intct_sip_get_sdp_header(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,enumsdp_header_typestype,enumsdp_header_typesterm,unsignedint*matchoff,unsignedint*matchlen){conststructsip_header*hdrs,*hdr,*thdr;constchar*start=dptr,*limit=dptr+datalen;intshift=0;hdrs=nf_ct_l3num(ct)==NFPROTO_IPV4?ct_sdp_hdrs_v4:ct_sdp_hdrs_v6;hdr=&hdrs[type];thdr=&hdrs[term];for(dptr+=dataoff;dptr<limit;dptr++){/* Find beginning of line */if(*dptr!='\r'&&*dptr!='\n')continue;if(++dptr>=limit)break;if(*(dptr-1)=='\r'&&*dptr=='\n'){if(++dptr>=limit)break;}if(term!=SDP_HDR_UNSPEC&&limit-dptr>=thdr->len&&strnicmp(dptr,thdr->name,thdr->len)==0)break;elseif(limit-dptr>=hdr->len&&strnicmp(dptr,hdr->name,hdr->len)==0)dptr+=hdr->len;elsecontinue;*matchoff=dptr-start;if(hdr->search){dptr=ct_sdp_header_search(dptr,limit,hdr->search,hdr->slen);if(!dptr)return-1;dptr+=hdr->slen;}*matchlen=hdr->match_len(ct,dptr,limit,&shift);if(!*matchlen)return-1;*matchoff=dptr-start+shift;return1;}return0;}EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);staticintct_sip_parse_sdp_addr(conststructnf_conn*ct,constchar*dptr,unsignedintdataoff,unsignedintdatalen,enumsdp_header_typestype,enumsdp_header_typesterm,unsignedint*matchoff,unsignedint*matchlen,unionnf_inet_addr*addr){intret;ret=ct_sip_get_sdp_header(ct,dptr,dataoff,datalen,type,term,matchoff,matchlen);if(ret<=0)returnret;if(!sdp_parse_addr(ct,dptr+*matchoff,NULL,addr,dptr+*matchoff+*matchlen))return-1;return1;}staticintrefresh_signalling_expectation(structnf_conn*ct,unionnf_inet_addr*addr,u8proto,__be16port,unsignedintexpires){structnf_conn_help*help=nfct_help(ct);structnf_conntrack_expect*exp;structhlist_node*next;intfound=0;spin_lock_bh(&nf_conntrack_lock);hlist_for_each_entry_safe(exp,next,&help->expectations,lnode){if(exp->class!=SIP_EXPECT_SIGNALLING||!nf_inet_addr_cmp(&exp->tuple.dst.u3,addr)||exp->tuple.dst.protonum!=proto||exp->tuple.dst.u.udp.port!=port)continue;if(!del_timer(&exp->timeout))continue;exp->flags&=~NF_CT_EXPECT_INACTIVE;exp->timeout.expires=jiffies+expires*HZ;add_timer(&exp->timeout);found=1;break;}spin_unlock_bh(&nf_conntrack_lock);returnfound;}staticvoidflush_expectations(structnf_conn*ct,boolmedia){structnf_conn_help*help=nfct_help(ct);structnf_conntrack_expect*exp;structhlist_node*next;spin_lock_bh(&nf_conntrack_lock);hlist_for_each_entry_safe(exp,next,&help->expectations,lnode){if((exp->class!=SIP_EXPECT_SIGNALLING)^media)continue;if(!del_timer(&exp->timeout))continue;nf_ct_unlink_expect(exp);nf_ct_expect_put(exp);if(!media)break;}spin_unlock_bh(&nf_conntrack_lock);}staticintset_expected_rtp_rtcp(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unionnf_inet_addr*daddr,__be16port,enumsip_expectation_classesclass,unsignedintmediaoff,unsignedintmedialen){structnf_conntrack_expect*exp,*rtp_exp,*rtcp_exp;enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnet*net=nf_ct_net(ct);enumip_conntrack_dirdir=CTINFO2DIR(ctinfo);unionnf_inet_addr*saddr;structnf_conntrack_tupletuple;intdirect_rtp=0,skip_expect=0,ret=NF_DROP;u_int16_tbase_port;__be16rtp_port,rtcp_port;typeof(nf_nat_sdp_port_hook)nf_nat_sdp_port;typeof(nf_nat_sdp_media_hook)nf_nat_sdp_media;saddr=NULL;if(sip_direct_media){if(!nf_inet_addr_cmp(daddr,&ct->tuplehash[dir].tuple.src.u3))returnNF_ACCEPT;saddr=&ct->tuplehash[!dir].tuple.src.u3;}/* We need to check whether the registration exists before attempting * to register it since we can see the same media description multiple * times on different connections in case multiple endpoints receive * the same call. * * RTP optimization: if we find a matching media channel expectation * and both the expectation and this connection are SNATed, we assume * both sides can reach each other directly and use the final * destination address from the expectation. We still need to keep * the NATed expectations for media that might arrive from the * outside, and additionally need to expect the direct RTP stream * in case it passes through us even without NAT. */memset(&tuple,0,sizeof(tuple));if(saddr)tuple.src.u3=*saddr;tuple.src.l3num=nf_ct_l3num(ct);tuple.dst.protonum=IPPROTO_UDP;tuple.dst.u3=*daddr;tuple.dst.u.udp.port=port;rcu_read_lock();do{exp=__nf_ct_expect_find(net,nf_ct_zone(ct),&tuple);if(!exp||exp->master==ct||nfct_help(exp->master)->helper!=nfct_help(ct)->helper||exp->class!=class)break;#ifdef CONFIG_NF_NAT_NEEDEDif(!direct_rtp&&(!nf_inet_addr_cmp(&exp->saved_addr,&exp->tuple.dst.u3)||exp->saved_proto.udp.port!=exp->tuple.dst.u.udp.port)&&ct->status&IPS_NAT_MASK){*daddr=exp->saved_addr;tuple.dst.u3=exp->saved_addr;tuple.dst.u.udp.port=exp->saved_proto.udp.port;direct_rtp=1;}else#endifskip_expect=1;}while(!skip_expect);rcu_read_unlock();base_port=ntohs(tuple.dst.u.udp.port)&~1;rtp_port=htons(base_port);rtcp_port=htons(base_port+1);if(direct_rtp){nf_nat_sdp_port=rcu_dereference(nf_nat_sdp_port_hook);if(nf_nat_sdp_port&&!nf_nat_sdp_port(skb,protoff,dataoff,dptr,datalen,mediaoff,medialen,ntohs(rtp_port)))gotoerr1;}if(skip_expect)returnNF_ACCEPT;rtp_exp=nf_ct_expect_alloc(ct);if(rtp_exp==NULL)gotoerr1;nf_ct_expect_init(rtp_exp,class,nf_ct_l3num(ct),saddr,daddr,IPPROTO_UDP,NULL,&rtp_port);rtcp_exp=nf_ct_expect_alloc(ct);if(rtcp_exp==NULL)gotoerr2;nf_ct_expect_init(rtcp_exp,class,nf_ct_l3num(ct),saddr,daddr,IPPROTO_UDP,NULL,&rtcp_port);nf_nat_sdp_media=rcu_dereference(nf_nat_sdp_media_hook);if(nf_nat_sdp_media&&ct->status&IPS_NAT_MASK&&!direct_rtp)ret=nf_nat_sdp_media(skb,protoff,dataoff,dptr,datalen,rtp_exp,rtcp_exp,mediaoff,medialen,daddr);else{if(nf_ct_expect_related(rtp_exp)==0){if(nf_ct_expect_related(rtcp_exp)!=0)nf_ct_unexpect_related(rtp_exp);elseret=NF_ACCEPT;}}nf_ct_expect_put(rtcp_exp);err2:nf_ct_expect_put(rtp_exp);err1:returnret;}staticconststructsdp_media_typesdp_media_types[]={SDP_MEDIA_TYPE("audio ",SIP_EXPECT_AUDIO),SDP_MEDIA_TYPE("video ",SIP_EXPECT_VIDEO),SDP_MEDIA_TYPE("image ",SIP_EXPECT_IMAGE),};staticconststructsdp_media_type*sdp_media_type(constchar*dptr,unsignedintmatchoff,unsignedintmatchlen){conststructsdp_media_type*t;unsignedinti;for(i=0;i<ARRAY_SIZE(sdp_media_types);i++){t=&sdp_media_types[i];if(matchlen<t->len||strncmp(dptr+matchoff,t->name,t->len))continue;returnt;}returnNULL;}staticintprocess_sdp(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);unsignedintmatchoff,matchlen;unsignedintmediaoff,medialen;unsignedintsdpoff;unsignedintcaddr_len,maddr_len;unsignedinti;unionnf_inet_addrcaddr,maddr,rtp_addr;unsignedintport;conststructsdp_media_type*t;intret=NF_ACCEPT;typeof(nf_nat_sdp_addr_hook)nf_nat_sdp_addr;typeof(nf_nat_sdp_session_hook)nf_nat_sdp_session;nf_nat_sdp_addr=rcu_dereference(nf_nat_sdp_addr_hook);/* Find beginning of session description */if(ct_sip_get_sdp_header(ct,*dptr,0,*datalen,SDP_HDR_VERSION,SDP_HDR_UNSPEC,&matchoff,&matchlen)<=0)returnNF_ACCEPT;sdpoff=matchoff;/* The connection information is contained in the session description * and/or once per media description. The first media description marks * the end of the session description. */caddr_len=0;if(ct_sip_parse_sdp_addr(ct,*dptr,sdpoff,*datalen,SDP_HDR_CONNECTION,SDP_HDR_MEDIA,&matchoff,&matchlen,&caddr)>0)caddr_len=matchlen;mediaoff=sdpoff;for(i=0;i<ARRAY_SIZE(sdp_media_types);){if(ct_sip_get_sdp_header(ct,*dptr,mediaoff,*datalen,SDP_HDR_MEDIA,SDP_HDR_UNSPEC,&mediaoff,&medialen)<=0)break;/* Get media type and port number. A media port value of zero * indicates an inactive stream. */t=sdp_media_type(*dptr,mediaoff,medialen);if(!t){mediaoff+=medialen;continue;}mediaoff+=t->len;medialen-=t->len;port=simple_strtoul(*dptr+mediaoff,NULL,10);if(port==0)continue;if(port<1024||port>65535){nf_ct_helper_log(skb,ct,"wrong port %u",port);returnNF_DROP;}/* The media description overrides the session description. */maddr_len=0;if(ct_sip_parse_sdp_addr(ct,*dptr,mediaoff,*datalen,SDP_HDR_CONNECTION,SDP_HDR_MEDIA,&matchoff,&matchlen,&maddr)>0){maddr_len=matchlen;memcpy(&rtp_addr,&maddr,sizeof(rtp_addr));}elseif(caddr_len)memcpy(&rtp_addr,&caddr,sizeof(rtp_addr));else{nf_ct_helper_log(skb,ct,"cannot parseSDPmessage");returnNF_DROP;}ret=set_expected_rtp_rtcp(skb,protoff,dataoff,dptr,datalen,&rtp_addr,htons(port),t->class,mediaoff,medialen);if(ret!=NF_ACCEPT){nf_ct_helper_log(skb,ct,"cannot add expectation for voice");returnret;}/* Update media connection address if present */if(maddr_len&&nf_nat_sdp_addr&&ct->status&IPS_NAT_MASK){ret=nf_nat_sdp_addr(skb,protoff,dataoff,dptr,datalen,mediaoff,SDP_HDR_CONNECTION,SDP_HDR_MEDIA,&rtp_addr);if(ret!=NF_ACCEPT){nf_ct_helper_log(skb,ct,"cannot mangleSDP");returnret;}}i++;}/* Update session connection and owner addresses */nf_nat_sdp_session=rcu_dereference(nf_nat_sdp_session_hook);if(nf_nat_sdp_session&&ct->status&IPS_NAT_MASK)ret=nf_nat_sdp_session(skb,protoff,dataoff,dptr,datalen,sdpoff,&rtp_addr);returnret;}staticintprocess_invite_response(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq,unsignedintcode){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnf_ct_sip_master*ct_sip_info=nfct_help_data(ct);if((code>=100&&code<=199)||(code>=200&&code<=299))returnprocess_sdp(skb,protoff,dataoff,dptr,datalen,cseq);elseif(ct_sip_info->invite_cseq==cseq)flush_expectations(ct,true);returnNF_ACCEPT;}staticintprocess_update_response(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq,unsignedintcode){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnf_ct_sip_master*ct_sip_info=nfct_help_data(ct);if((code>=100&&code<=199)||(code>=200&&code<=299))returnprocess_sdp(skb,protoff,dataoff,dptr,datalen,cseq);elseif(ct_sip_info->invite_cseq==cseq)flush_expectations(ct,true);returnNF_ACCEPT;}staticintprocess_prack_response(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq,unsignedintcode){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnf_ct_sip_master*ct_sip_info=nfct_help_data(ct);if((code>=100&&code<=199)||(code>=200&&code<=299))returnprocess_sdp(skb,protoff,dataoff,dptr,datalen,cseq);elseif(ct_sip_info->invite_cseq==cseq)flush_expectations(ct,true);returnNF_ACCEPT;}staticintprocess_invite_request(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnf_ct_sip_master*ct_sip_info=nfct_help_data(ct);unsignedintret;flush_expectations(ct,true);ret=process_sdp(skb,protoff,dataoff,dptr,datalen,cseq);if(ret==NF_ACCEPT)ct_sip_info->invite_cseq=cseq;returnret;}staticintprocess_bye_request(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);flush_expectations(ct,true);returnNF_ACCEPT;}/* Parse a REGISTER request and create a permanent expectation for incoming * signalling connections. The expectation is marked inactive and is activated * when receiving a response indicating success from the registrar. */staticintprocess_register_request(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnf_ct_sip_master*ct_sip_info=nfct_help_data(ct);enumip_conntrack_dirdir=CTINFO2DIR(ctinfo);unsignedintmatchoff,matchlen;structnf_conntrack_expect*exp;unionnf_inet_addr*saddr,daddr;__be16port;u8proto;unsignedintexpires=0;intret;typeof(nf_nat_sip_expect_hook)nf_nat_sip_expect;/* Expected connections can not register again. */if(ct->status&IPS_EXPECTED)returnNF_ACCEPT;/* We must check the expiration time: a value of zero signals the * registrar to release the binding. We'll remove our expectation * when receiving the new bindings in the response, but we don't * want to create new ones. * * The expiration time may be contained in Expires: header, the * Contact: header parameters or the URI parameters. */if(ct_sip_get_header(ct,*dptr,0,*datalen,SIP_HDR_EXPIRES,&matchoff,&matchlen)>0)expires=simple_strtoul(*dptr+matchoff,NULL,10);ret=ct_sip_parse_header_uri(ct,*dptr,NULL,*datalen,SIP_HDR_CONTACT,NULL,&matchoff,&matchlen,&daddr,&port);if(ret<0){nf_ct_helper_log(skb,ct,"cannot parse contact");returnNF_DROP;}elseif(ret==0)returnNF_ACCEPT;/* We don't support third-party registrations */if(!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,&daddr))returnNF_ACCEPT;if(ct_sip_parse_transport(ct,*dptr,matchoff+matchlen,*datalen,&proto)==0)returnNF_ACCEPT;if(ct_sip_parse_numerical_param(ct,*dptr,matchoff+matchlen,*datalen,"expires=",NULL,NULL,&expires)<0){nf_ct_helper_log(skb,ct,"cannot parse expires");returnNF_DROP;}if(expires==0){ret=NF_ACCEPT;gotostore_cseq;}exp=nf_ct_expect_alloc(ct);if(!exp){nf_ct_helper_log(skb,ct,"cannot alloc expectation");returnNF_DROP;}saddr=NULL;if(sip_direct_signalling)saddr=&ct->tuplehash[!dir].tuple.src.u3;nf_ct_expect_init(exp,SIP_EXPECT_SIGNALLING,nf_ct_l3num(ct),saddr,&daddr,proto,NULL,&port);exp->timeout.expires=sip_timeout*HZ;exp->helper=nfct_help(ct)->helper;exp->flags=NF_CT_EXPECT_PERMANENT|NF_CT_EXPECT_INACTIVE;nf_nat_sip_expect=rcu_dereference(nf_nat_sip_expect_hook);if(nf_nat_sip_expect&&ct->status&IPS_NAT_MASK)ret=nf_nat_sip_expect(skb,protoff,dataoff,dptr,datalen,exp,matchoff,matchlen);else{if(nf_ct_expect_related(exp)!=0){nf_ct_helper_log(skb,ct,"cannot add expectation");ret=NF_DROP;}elseret=NF_ACCEPT;}nf_ct_expect_put(exp);store_cseq:if(ret==NF_ACCEPT)ct_sip_info->register_cseq=cseq;returnret;}staticintprocess_register_response(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen,unsignedintcseq,unsignedintcode){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnf_ct_sip_master*ct_sip_info=nfct_help_data(ct);enumip_conntrack_dirdir=CTINFO2DIR(ctinfo);unionnf_inet_addraddr;__be16port;u8proto;unsignedintmatchoff,matchlen,coff=0;unsignedintexpires=0;intin_contact=0,ret;/* According to RFC 3261, "UAs MUST NOTsend a new registration until * they have received a finalresponsefrom the registrar for the * previous oneor the previous REGISTERrequest has timed out". * * However, some servers fail to detect retransmissions and send late * responses, so we store the sequence number of the last valid * request and compare it here. */if(ct_sip_info->register_cseq!=cseq)returnNF_ACCEPT;if(code>=100&&code<=199)returnNF_ACCEPT;if(code<200||code>299)gotoflush;if(ct_sip_get_header(ct,*dptr,0,*datalen,SIP_HDR_EXPIRES,&matchoff,&matchlen)>0)expires=simple_strtoul(*dptr+matchoff,NULL,10);while(1){unsignedintc_expires=expires;ret=ct_sip_parse_header_uri(ct,*dptr,&coff,*datalen,SIP_HDR_CONTACT,&in_contact,&matchoff,&matchlen,&addr,&port);if(ret<0){nf_ct_helper_log(skb,ct,"cannot parse contact");returnNF_DROP;}elseif(ret==0)break;/* We don't support third-party registrations */if(!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,&addr))continue;if(ct_sip_parse_transport(ct,*dptr,matchoff+matchlen,*datalen,&proto)==0)continue;ret=ct_sip_parse_numerical_param(ct,*dptr,matchoff+matchlen,*datalen,"expires=",NULL,NULL,&c_expires);if(ret<0){nf_ct_helper_log(skb,ct,"cannot parse expires");returnNF_DROP;}if(c_expires==0)break;if(refresh_signalling_expectation(ct,&addr,proto,port,c_expires))returnNF_ACCEPT;}flush:flush_expectations(ct,false);returnNF_ACCEPT;}staticconststructsip_handlersip_handlers[]={SIP_HANDLER("INVITE",process_invite_request,process_invite_response),SIP_HANDLER("UPDATE",process_sdp,process_update_response),SIP_HANDLER("ACK",process_sdp,NULL),SIP_HANDLER("PRACK",process_sdp,process_prack_response),SIP_HANDLER("BYE",process_bye_request,NULL),SIP_HANDLER("REGISTER",process_register_request,process_register_response),};staticintprocess_sip_response(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);unsignedintmatchoff,matchlen,matchend;unsignedintcode,cseq,i;if(*datalen<strlen("SIP/2.0 200"))returnNF_ACCEPT;code=simple_strtoul(*dptr+strlen("SIP/2.0 "),NULL,10);if(!code){nf_ct_helper_log(skb,ct,"cannot getcode");returnNF_DROP;}if(ct_sip_get_header(ct,*dptr,0,*datalen,SIP_HDR_CSEQ,&matchoff,&matchlen)<=0){nf_ct_helper_log(skb,ct,"cannot parse cseq");returnNF_DROP;}cseq=simple_strtoul(*dptr+matchoff,NULL,10);if(!cseq){nf_ct_helper_log(skb,ct,"cannot get cseq");returnNF_DROP;}matchend=matchoff+matchlen+1;for(i=0;i<ARRAY_SIZE(sip_handlers);i++){conststructsip_handler*handler;handler=&sip_handlers[i];if(handler->response==NULL)continue;if(*datalen<matchend+handler->len||strnicmp(*dptr+matchend,handler->method,handler->len))continue;returnhandler->response(skb,protoff,dataoff,dptr,datalen,cseq,code);}returnNF_ACCEPT;}staticintprocess_sip_request(structsk_buff*skb,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen){enumip_conntrack_infoctinfo;structnf_conn*ct=nf_ct_get(skb,&ctinfo);structnf_ct_sip_master*ct_sip_info=nfct_help_data(ct);enumip_conntrack_dirdir=CTINFO2DIR(ctinfo);unsignedintmatchoff,matchlen;unsignedintcseq,i;unionnf_inet_addraddr;__be16port;/* Many Cisco IP phones use a high source port for SIP requests, but * listen for the response on port 5060. If we are the local * router for one of these phones, save the port number from the * Via: header so that nf_nat_sip can redirect the responses to * the correct port. */if(ct_sip_parse_header_uri(ct,*dptr,NULL,*datalen,SIP_HDR_VIA_UDP,NULL,&matchoff,&matchlen,&addr,&port)>0&&port!=ct->tuplehash[dir].tuple.src.u.udp.port&&nf_inet_addr_cmp(&addr,&ct->tuplehash[dir].tuple.src.u3))ct_sip_info->forced_dport=port;for(i=0;i<ARRAY_SIZE(sip_handlers);i++){conststructsip_handler*handler;handler=&sip_handlers[i];if(handler->request==NULL)continue;if(*datalen<handler->len||strnicmp(*dptr,handler->method,handler->len))continue;if(ct_sip_get_header(ct,*dptr,0,*datalen,SIP_HDR_CSEQ,&matchoff,&matchlen)<=0){nf_ct_helper_log(skb,ct,"cannot parse cseq");returnNF_DROP;}cseq=simple_strtoul(*dptr+matchoff,NULL,10);if(!cseq){nf_ct_helper_log(skb,ct,"cannot get cseq");returnNF_DROP;}returnhandler->request(skb,protoff,dataoff,dptr,datalen,cseq);}returnNF_ACCEPT;}staticintprocess_sip_msg(structsk_buff*skb,structnf_conn*ct,unsignedintprotoff,unsignedintdataoff,constchar**dptr,unsignedint*datalen){typeof(nf_nat_sip_hook)nf_nat_sip;intret;if(strnicmp(*dptr,"SIP/2.0 ",strlen("SIP/2.0 "))!=0)ret=process_sip_request(skb,protoff,dataoff,dptr,datalen);elseret=process_sip_response(skb,protoff,dataoff,dptr,datalen);if(ret==NF_ACCEPT&&ct->status&IPS_NAT_MASK){nf_nat_sip=rcu_dereference(nf_nat_sip_hook);if(nf_nat_sip&&!nf_nat_sip(skb,protoff,dataoff,dptr,datalen)){nf_ct_helper_log(skb,ct,"cannot NAT SIPmessage");ret=NF_DROP;}}returnret;}staticintsip_help_tcp(structsk_buff*skb,unsignedintprotoff,structnf_conn*ct,enumip_conntrack_infoctinfo){structtcphdr*th,_tcph;unsignedintdataoff,datalen;unsignedintmatchoff,matchlen,clen;unsignedintmsglen,origlen;constchar*dptr,*end;s16diff,tdiff=0;intret=NF_ACCEPT;boolterm;typeof(nf_nat_sip_seq_adjust_hook)nf_nat_sip_seq_adjust;if(ctinfo!=IP_CT_ESTABLISHED&&ctinfo!=IP_CT_ESTABLISHED_REPLY)returnNF_ACCEPT;/* No Data ? */th=skb_header_pointer(skb,protoff,sizeof(_tcph),&_tcph);if(th==NULL)returnNF_ACCEPT;dataoff=protoff+th->doff*4;if(dataoff>=skb->len)returnNF_ACCEPT;nf_ct_refresh(ct,skb,sip_timeout*HZ);if(unlikely(skb_linearize(skb)))returnNF_DROP;dptr=skb->data+dataoff;datalen=skb->len-dataoff;if(datalen<strlen("SIP/2.0 200"))returnNF_ACCEPT;while(1){if(ct_sip_get_header(ct,dptr,0,datalen,SIP_HDR_CONTENT_LENGTH,&matchoff,&matchlen)<=0)break;clen=simple_strtoul(dptr+matchoff,(char**)&end,10);if(dptr+matchoff==end)break;term=false;for(;end+strlen("\r\n\r\n")<=dptr+datalen;end++){if(end[0]=='\r'&&end[1]=='\n'&&end[2]=='\r'&&end[3]=='\n'){term=true;break;}}if(!term)break;end+=strlen("\r\n\r\n")+clen;msglen=origlen=end-dptr;if(msglen>datalen)returnNF_ACCEPT;ret=process_sip_msg(skb,ct,protoff,dataoff,&dptr,&msglen);/* process_sip_* functions report why this packet is dropped */if(ret!=NF_ACCEPT)break;diff=msglen-origlen;tdiff+=diff;dataoff+=msglen;dptr+=msglen;datalen=datalen+diff-msglen;}if(ret==NF_ACCEPT&&ct->status&IPS_NAT_MASK){nf_nat_sip_seq_adjust=rcu_dereference(nf_nat_sip_seq_adjust_hook);if(nf_nat_sip_seq_adjust)nf_nat_sip_seq_adjust(skb,protoff,tdiff);}returnret;}staticintsip_help_udp(structsk_buff*skb,unsignedintprotoff,structnf_conn*ct,enumip_conntrack_infoctinfo){unsignedintdataoff,datalen;constchar*dptr;/* No Data ? */dataoff=protoff+sizeof(structudphdr);if(dataoff>=skb->len)returnNF_ACCEPT;nf_ct_refresh(ct,skb,sip_timeout*HZ);if(unlikely(skb_linearize(skb)))returnNF_DROP;dptr=skb->data+dataoff;datalen=skb->len-dataoff;if(datalen<strlen("SIP/2.0 200"))returnNF_ACCEPT;returnprocess_sip_msg(skb,ct,protoff,dataoff,&dptr,&datalen);}staticstructnf_conntrack_helpersip[MAX_PORTS][4]__read_mostly;staticconststructnf_conntrack_expect_policysip_exp_policy[SIP_EXPECT_MAX+1]={[SIP_EXPECT_SIGNALLING]={.name="signalling",.max_expected=1,.timeout=3*60,},[SIP_EXPECT_AUDIO]={.name="audio",.max_expected=2*IP_CT_DIR_MAX,.timeout=3*60,},[SIP_EXPECT_VIDEO]={.name="video",.max_expected=2*IP_CT_DIR_MAX,.timeout=3*60,},[SIP_EXPECT_IMAGE]={.name="image",.max_expected=IP_CT_DIR_MAX,.timeout=3*60,},};staticvoidnf_conntrack_sip_fini(void){inti,j;for(i=0;i<ports_c;i++){for(j=0;j<ARRAY_SIZE(sip[i]);j++){if(sip[i][j].me==NULL)continue;nf_conntrack_helper_unregister(&sip[i][j]);}}}staticint__initnf_conntrack_sip_init(void){inti,j,ret;if(ports_c==0)ports[ports_c++]=SIP_PORT;for(i=0;i<ports_c;i++){memset(&sip[i],0,sizeof(sip[i]));sip[i][0].tuple.src.l3num=AF_INET;sip[i][0].tuple.dst.protonum=IPPROTO_UDP;sip[i][0].help=sip_help_udp;sip[i][1].tuple.src.l3num=AF_INET;sip[i][1].tuple.dst.protonum=IPPROTO_TCP;sip[i][1].help=sip_help_tcp;sip[i][2].tuple.src.l3num=AF_INET6;sip[i][2].tuple.dst.protonum=IPPROTO_UDP;sip[i][2].help=sip_help_udp;sip[i][3].tuple.src.l3num=AF_INET6;sip[i][3].tuple.dst.protonum=IPPROTO_TCP;sip[i][3].help=sip_help_tcp;for(j=0;j<ARRAY_SIZE(sip[i]);j++){sip[i][j].data_len=sizeof(structnf_ct_sip_master);sip[i][j].tuple.src.u.udp.port=htons(ports[i]);sip[i][j].expect_policy=sip_exp_policy;sip[i][j].expect_class_max=SIP_EXPECT_MAX;sip[i][j].me=THIS_MODULE;if(ports[i]==SIP_PORT)sprintf(sip[i][j].name,"sip");elsesprintf(sip[i][j].name,"sip-%u",i);pr_debug("port #%u: %u\n",i,ports[i]);ret=nf_conntrack_helper_register(&sip[i][j]);if(ret){printk(KERN_ERR"nf_ct_sip: failed to register"" helper for pf: %u port: %u\n",sip[i][j].tuple.src.l3num,ports[i]);nf_conntrack_sip_fini();returnret;}}}return0;}module_init(nf_conntrack_sip_init);module_exit(nf_conntrack_sip_fini);