1.
Chan_sip.c是SIP协议(RFC3261)的实现代码,它没有实现对S/MIME, TCP and TLS的支持,对应的配置文件是sip.conf,代码所在的分组是:通道驱动类(channel_drivers)。
CLI命令sip show channels的示例:
Asterisk247*CLI> sip show channels
Peer
192.168.52.245
192.168.52.246
192.168.52.245
3 active SIP channels
在进行代码剖析之前,先看一个重要的数据结构sip_pvt.定义在chan_sip.c中,表示一个sip dialog。
sip_pvt这个结构维护了一个sip session的重要数据信息,关键字段如下:
struct sip_pvt* next
struct ast_channel* owner
struct sip_pkt* packets
int pendinginvite
struct ast_rtp* rtp
int rtptimeout
struct sockaddr_in sa
2.
首先chan_sip模块注册了load_module()函数作为asterisk在加载本模块时的入口函数。
17818AST_MODULE_INFO (ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , "Session Initiation Protocol (SIP)",
17819.load = load_module ,
17820.unload = unload_module ,
17821.reload = reload ,
17822);
load_module()函数读取配置文件sip.conf,并且注册一个通道驱动类型,即sip,具体见sip_tech中的结构内容。
17696 if(reload_config(sip_reloadreason))
17697return AST_MODULE_LOAD_DECLINE ;
17699
17700if (ast_channel_register (&sip_tech )) {
17701ast_log (LOG_ERROR , "Unable to register channel type 'SIP'/n");
17702io_context_destroy (io );
17703sched_context_destroy (sched );
17704return AST_MODULE_LOAD_FAILURE ;
17705}
Load_module最后调用restart_monitor()来启动sip监听。restart_monitor另外还有两处会被调用,在sip_request_call()和sip_reload()函数体内。
17735
17736restart_monitor ();
restart_monitor调用pthread接口启动一个独立的监听线程,线程id记录在monitor_thread,线程入口函数是do_monitor()。
if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor , NULL) < 0) {
ast_mutex_unlock (&monlock);
ast_log (LOG_ERROR , "Unable to start monitor thread./n");
return -1;
}
do_monitor()给SIP UDP socket添加事件处理器,sipsock_read负责读取socket收到的数据。
15233
15234if (sipsock > -1)
15235sipsock_read_id = ast_io_add (io , sipsock , sipsock_read , AST_IO_IN , NULL);
do_monitor()函数然后进入一个for(;;)循环中,这个循环不断检测是否需要reload sip模块,并且遍历sip session列表检查是否有需要kill的session。它是怎么遍历的呢?原来是chan_sip 维护了一个sip_pvt结构的列表,头指针保存在全局变量iflist中,通过sip_pvt的next域进行遍历。每个sip_pvt结构记录了一个 session的全部信息。
变量t表示现在的时间,sip->lastrtptx表示上次发送rtp包的时间,如果两者之差大于keep alive间隔,则说明需要发送keep alive包了。
15272if (sip->lastrtptx &&
15273ast_rtp_get_rtpkeepalive (sip->rtp) &&
15274(t > sip->lastrtptx + ast_rtp_get_rtpkeepalive (sip->rtp))) {
15275
15276sip->lastrtptx = time(NULL);
15277ast_rtp_sendcng (sip->rtp, 0);
15278}
变量t表示现在的时间,sip->lastrtprx表示上次收到rtp包的时间,如果两者之差大于rpt的timeout间隔,则说明已经超时了。
这两个超时参数可以在sip.conf中配置,分别如下:
rtptimeout=60
;rtpholdtimeout=300
if (sip->lastrtprx &&
(ast_rtp_get_rtptimeout(sip->rtp) || ast_rtp_get_rtpholdtimeout (sip->rtp)) &&
(t > sip->lastrtprx +ast_rtp_get_rtptimeout (sip->rtp))) {
15282
此时再检测holdtimeout,并对channel上锁,ast_channel_trylock(sip->owner)。如果不是bridged channel,则调用soft hangup。
ast_softhangup_nolock(sip->owner, AST_SOFTHANGUP_DEV );
现在回过头来把焦点转移到sipsock_read()函数。所有到来的sip包都在这里开始处理,在处理sip包期 间,sipsock_read需要对sip的拥有者channel上锁,sipsock_read成功则返回0,失败则返回1。它解析sip包并且找到所 在的dialog,或者创建新的dialog。并且把解析好的包交给handle_request()处理。
res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
解析SIP包,获取sip request method,如INVITE, BYE等。
15086parse_request (&req);
15087req.method = find_sip_method (req.rlPart1);
随后找到对应的sip_pvt结构,或者创建新的sip_pvt结构,结构指针返回到变量p中。
15099
15100p = find_call (&req, &sin, req.method);
在进一步操作之前,需要对p->owner上锁,这个操作会最多尝试100次直至成功。
如果上锁操作失败,将会返回503 sip消息。
15127if (req.method != SIP_ACK )
15128transmit_response (p, "503 Server error", &req);
15129
15130append_history (p, "LockFail", "Owner lock failed, transaction failed.");
15131return 1;
更深一步的解析处理操作交给handle_request()函数处理,完了之后就是释放channel的锁。
15134if (handle_request (p, &req, &sin, &recount, &nounlock) == -1) {
15135
15136if (option_debug )
15137ast_log (LOG_DEBUG , "SIP message could not be handled, bad request: %-70.70s/n", p->callid[0] ? p->callid : "<no callid>");
15138}
15139
15140if (p->owner && !nounlock)
15141ast_channel_unlock (p->owner);
函数handle_request()视数据包的类型而处理,如果是对外出包的回应,则交给 handle_response()处理,如果是一个请求包,则视请求类型(INVITE, OPTIONS, REFER, BYE, CANCEL etc)交给不同的函数处理。如果是一个INVITE包,则交给handle_request_invite()处理,在那里将会创建一个新的 channel,这个通道随后会执行一个单独的通道线程。这就是一个来电呼叫。如果这个呼叫被应答,则一个桥接通道或者PBX本身会回调 sip_answer()函数。而真正的媒体数据,音频或者视频,则会在RTP子系统中处理,具体见rtp.c。
在注册SIP通道驱动时,我们注册了一系列通道驱动的回调函数,这些有什么用呢?比如当需要发出一个outbound call时,则会调用sip_request_call()。而当需要hangup时,则调用sip_hangup()。
01541
01542static const struct ast_channel_tech sip_tech = {
01543.type = "SIP",
01544.description = "Session Initiation Protocol (SIP)",
01545.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
01546.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER ,
01547.requester = sip_request_call ,
01548.devicestate = sip_devicestate ,
01549.call = sip_call ,
01550.hangup = sip_hangup ,
01551.answer = sip_answer ,
01552.read = sip_read ,
01553.write = sip_write ,
01554.write_video = sip_write ,
01555.indicate = sip_indicate ,
01556.transfer = sip_transfer ,
01557.fixup = sip_fixup ,
01558.send_digit_begin = sip_senddigit_begin ,
01559.send_digit_end = sip_senddigit_end ,
01560.bridge = ast_rtp_bridge ,
01561.send_text = sip_sendtext ,
01562.func_channel_read = acf_channel_read ,
01563 };
现在开始分析handle_request_invite()函数。检查invite包中的headers中是否有require,最好是没有,如果有的话也必须是Replaces,其它的不支持一律不予处理。
13394
13395required = get_header (req, "Require");
13396if (!ast_strlen_zero (required)) {
13397required_profile = parse_sip_options (NULL, required);
13398if (required_profile && required_profile != SIP_OPT_REPLACES ) {
13399
13400transmit_response_with_unsupported (p, "420 Bad extension (unsupported)", req, required);
13401ast_log (LOG_WARNING,"Received SIP INVITE with unsupported required extension: %s/n", required);
13402p->invitestate = INV_COMPLETED ;
13403if (!p->lastinvite)
13404sip_scheddestroy (p, DEFAULT_TRANS_TIMEOUT );
13405return -1;
13406}
13407}
开始验证sip user的合法性,check_user()调用check_user_full()函数,该函数从heades中的from中取出用户名并在sip user list 和 sip peer list中匹配,如果没找着,再查看是否允许guest,如果不允许,则认证通不过。
检查sip包中是否有SDP信息,如: application/sdp 。SDP(Session Description Protocol)是指会话描述协议,SIP包中使用它来描述语音流协议的细节,比如某端所支持的介质编码(这些编码使用RTP进行传输)。
13558
13559if (find_sdp (req)) {
13560if (process_sdp (p, req)) {
13561transmit_response (p, "488 Not acceptable here", req);
13562if (!p->lastinvite)
13563sip_scheddestroy (p, DEFAULT_TRANS_TIMEOUT );
13564return -1;
13565}
检查该用户的并行拨打电话数有没有达到上限。
13633
13634if (option_debug )
13635ast_log (LOG_DEBUG , "Checking SIP call limits for device %s/n", p->username);
13636if ((res = update_call_counter (p, INC_CALL_LIMIT ))) {
13637if (res < 0) {
13638ast_log (LOG_NOTICE , "Failed to place call for user %s, too many calls/n", p->username);
13639transmit_response_reliable (p, "480 Temporarily Unavailable (Call limit) ", req);
13640sip_scheddestroy (p, DEFAULT_TRANS_TIMEOUT );
13641p->invitestate = INV_COMPLETED ;
13642}
13643return 0;
13644}
查找对应的extension,如果没有对应的extension,则从extension s开始执行(extension s是默认的extension,s表示start)。
13645gotdest = get_destination (p, NULL);
调用sip_new()创建channel,这时候是incoming call。当调用dial application发起outbound call时asterisk pbx根据注册的回调函数sip_request_call()同样进入到sip_new中创建channel。
13672
13673c = sip_new (p, AST_STATE_DOWN , S_OR (p->username, NULL));
调用ast_pbx_start(),该函数启动一个独立线程负责这个channel,线程函数是pbx_thread(),pbx_thread()调用__ast_pbx_run()函数。
13717res = ast_pbx_start (c);
__ast_pbx_run()函数allocate 一个pbx结构和cdr结构,并把它们的指针保存到ast_channel结构的pbx域和cdr域。随后进入for循环逐个执行application。具体见./main/pbx.c。
02385
02386while (ast_exists_extension (c, c->context , c->exten , c->priority , c->cid .cid_num)) {
02387found = 1;
02388if ((res = ast_spawn_extension (c, c->context , c->exten , c->priority , c->cid.cid_num ))) {
02389
下面再来分析下handle_request_bye()函数,这个函数比较简单,它在收到BYE包时被触发,首先记录 下rtp, vrtp的qos到channel内置变量,调用stop_media_flows(p)结束rtp流,调用 ast_queue_hangup(p->owner)进行挂断操作,调用transmit_response(p, "200 OK", req)返回200 OK消息。其中ast_queue_hangup()调用ast_queue_frame()在ast_channel机构的ast_frame队列里插 入一个HANGUP的帧。