asterisk chan_sip.c

本文详细介绍了SIP协议在Asterisk PBX中的实现,包括通道驱动类、对话处理、配置文件、核心函数解析及工作流程,深入探讨了注册过程、出站与入站呼叫、媒体流管理以及错误处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 代码简介:

Chan_sip.c是SIP协议(RFC3261)的实现代码,它没有实现对S/MIME, TCP and TLS的支持,对应的配置文件是sip.conf,代码所在的分组是:通道驱动类(channel_drivers)。

    SIP通道处理各种类型的Sip sessions和dialogs(注意:并不是所有的dialogs都是“电话呼叫”),主要包括:

 * - Incoming calls that will be sent to the PBX core

 * - Outgoing calls, generated by the PBX

 * - SIP subscriptions and notifications of states and voicemail messages

 * - SIP registrations, both inbound and outbound

 * - SIP peer management (peerpoke, OPTIONS)

 * - SIP text messages

 

    在SIP通道中,通常会有一列活跃的SIP dialogs,CLI下的命令sip show channels可以显示出大部分dialogs,除了订阅类的(它们可以用命令sip show subscriptions显示出来)。

CLI命令sip show channels的示例:

debian120*CLI> sip show channels

Peer             User/ANR    Call ID      Seq (Tx/Rx)  Form  Hold     Last Message  

211.150.115.116  0132364499  51e8b037316  00102/00000  alaw  No       Init: INVITE             

202.108.12.94    0000123456  76ad6e55-e0  00101/00001  alaw  No       Rx: ACK                  

211.150.115.116  0216252766  29df5b95633  00102/00000  alaw  No       Init: INVITE             

202.108.12.94    0000123456  76ad6e55-2c  00101/00001  alaw  No       Rx: ACK                  

211.150.115.116  0137587006  720c5ecb32e  00102/00000  alaw  No       Tx: ACK                  

202.108.12.94    0000123456  76ad6e55-bf  00101/00001  alaw  No       Rx: ACK                  

211.150.115.116  0138797950  6d96c21a580  00102/00000  alaw  No       Tx: ACK                  

202.108.12.94    0000123456  76ad6e55-a5  00101/00001  alaw  No       Rx: ACK                  

211.150.115.116  0578708822  617679d2699  00102/00000  alaw  No       Tx: ACK                  

202.108.12.94    0000123456  76ad6e55-20  00101/00001  alaw  No       Rx: ACK                  

211.150.115.116  0512534057  6049a06e77d  00102/00000  alaw  No       Tx: ACK                  

202.108.12.94    0000123456  76ad6e55-b7  00101/00001  alaw  No       Rx: ACK                  

211.150.115.116  0132684273  4224f333507  00102/00000  alaw  No       Tx: ACK                  

202.108.12.94    0000123456  76ad6e55-95  00101/00001  alaw  No       Rx: ACK 

 

2. 代码剖析:(注意:由于word自动更正某些代码中的首单词的首字母为大写,这儿可能与你在asterisk代码包中看到的代码不一致,请见谅)

chan_sip模块注册了load_module()函数作为asterisk在加载本模块时的入口函数。

17818 AST_MODULE_INFO(ASTERISK_GPL_KEYAST_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)) /* Load the configuration from sip.conf */
17697       return AST_MODULE_LOAD_DECLINE;
17698
17699    /* Make sure we can register our sip channel type */
17700    if (ast_channel_register(&sip_tech)) {
17701       ast_log(LOG_ERROR"Unable to register channel type 'SIP'/n");
17702       io_context_destroy(io);
17703       sched_context_destroy(sched);
17704       return AST_MODULE_LOAD_FAILURE;
17705    }

Load_module最后调用restart_monitor()来启动sip监听。restart_monitor另外还有两处会被调用,在sip_request_call()和sip_reload()函数体内。

17735    /* And start the monitor for the first time */
17736    restart_monitor();

restart_monitor调用pthread接口启动一个独立的监听线程,线程id记录在monitor_thread,线程入口函数是do_monitor()

 

15399       if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
15400          ast_mutex_unlock(&monlock);
15401          ast_log(LOG_ERROR"Unable to start monitor thread./n");
15402          return -1;
15403       }

 

    do_monitor()给SIP UDP socket添加事件处理器,sipsock_read负责读取socket收到的数据。

15233    /* Add an I/O event to our SIP UDP socket */
15234    if (sipsock > -1)
15235       sipsock_read_id = ast_io_add(iosipsocksipsock_readAST_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包了。

15272             if (sip->lastrtptx &&
15273                 ast_rtp_get_rtpkeepalive(sip->rtp) &&
15274                 (t > sip->lastrtptx + ast_rtp_get_rtpkeepalive(sip->rtp))) {
15275                /* Need to send an empty RTP packet */
15276                sip->lastrtptx = time(NULL);
15277                ast_rtp_sendcng(sip->rtp, 0);
15278             }

变量t表示现在的时间,sip->lastrtprx表示上次收到rtp包的时间,如果两者之差大于rpt的timeout间隔,则说明已经超时了。

这两个超时参数可以在sip.conf中配置,分别如下:

rtptimeout=60

;rtpholdtimeout=300

15279             if (sip->lastrtprx &&
15280                (ast_rtp_get_rtptimeout(sip->rtp) ||ast_rtp_get_rtpholdtimeout(sip->rtp)) &&
15281                 (t > sip->lastrtprx + ast_rtp_get_rtptimeout(sip->rtp))) {
15282                /* Might be a timeout now -- see if we're on hold */

    此时再检测holdtimeout,并对channel上锁,ast_channel_trylock(sip->owner)。如果不是bridged channel,则调用soft hangup。

15301                            /* Issue a softhangup */
15302                            ast_softhangup_nolock(sip->owner,AST_SOFTHANGUP_DEV);

  

相关的重要数据结构:

sip_pvt: PVT structures are used for each SIP dialog, ie. a call, a registration, a subscribe

sip_pvt这个结构维护了一个sip session的重要数据信息,关键字段如下:

struct sip_pvt* next 

Next dialog in chain。指向链上的下一个sip_pvt结构

struct ast_channel* owner 

Who owns us (if we have an owner)。指向了拥有这个结构的通道的指针

struct sip_pkt* packets 

Packets scheduled for re-transmission。维护待重传的sip packet

int pendinginvite 

Any pending invite ? (seqno of this)。如果有等待的邀请包,则在这里记下这个包序号

struct ast_rtp* rtp 

RTP Session,指向RTP Session的指针

int rtptimeout 

RTP timeout time, RTP的超时时间

struct sockaddr_in sa 

Our peer,对方的地址信息

char tag[11] 

Our tag for this session,比如:tag=965531f1-52721549


现在回过头来把焦点转移到sipsock_read()函数。所有到来的sip包都在这里开始处理,在处理sip包期间,sipsock_read需要对sip的拥有者channel上锁,sipsock_read成功则返回0,失败则返回1。它解析sip包并且找到所在的dialog,或者创建新的dialog。并且把解析好的包交给handle_request()处理。

    sipsock_read第一步接收socket数据,存到结构sip_request的data域中。

15062    res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (structsockaddr *)&sin, &len);

解析SIP包,获取sip request method,如INVITE, BYE等

15086    parse_request(&req);
15087    req.method = find_sip_method(req.rlPart1);

    随后找到对应的sip_pvt结构,或者创建新的sip_pvt结构,结构指针返回到变量p中。

15099       /* Find the active SIP dialog or create a new one */
15100       p = find_call(&req, &sin, req.method); /* returns p locked */

   在进一步操作之前,需要对p->owner上锁,这个操作会最多尝试100次直至成功。

15107       /* Go ahead and lock the owner if it has one -- we may need it */
15108       /* becaues this is deadlock-prone, we need to try and unlock if failed */
15109       if (!p->owner || !ast_channel_trylock(p->owner))
15110          break;   /* locking succeeded */

如果上锁操作失败,将会返回503 sip消息。

15127       if (req.method != SIP_ACK)
15128          transmit_response(p, "503 Server error", &req); /* We must respond according to RFC 3261 sec 12.2 */
15129       /* XXX We could add retry-after to make sure they come back */
15130       append_history(p, "LockFail""Owner lock failed, transaction failed.");
15131       return 1;

更深一步的解析处理操作交给handle_request()函数处理,完了之后就是释放channel的锁。

15134    if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) {
15135       /* Request failed */
15136       if (option_debug)
15137          ast_log(LOG_DEBUG"SIP message could not be handled, bad request: %-70.70s/n", p->callid[0] ? p->callid : "<no callid>");
15138    }
15139      
15140    if (p->owner && !nounlock)
15141       ast_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

Outbound calls

 Outbound calls are set up by the PBX through the sip_request_call() function. After that, they are activated by sip_call().

Hanging up

 The PBX issues a hangup on both incoming and outgoing calls through the sip_hangup() function

 

01502 /*------Request handling functions */
01503 static int handle_request(struct sip_pvt *p, struct sip_request *req,struct sockaddr_in *sin, int *recount, int *nounlock);
01504 static int handle_request_invite(struct sip_pvt *p, struct sip_request*req, int debugint seqno, struct sockaddr_in *sin, int *recount, char *e, int*nounlock);
01505 static int handle_request_refer(struct sip_pvt *p, struct sip_request*req, int debugint ignore, int seqno, int *nounlock);
01506 static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
01507 static int handle_request_register(struct sip_pvt *p, struct sip_request*req, struct sockaddr_in *sin, char *e);
01508 static int handle_request_cancel(struct sip_pvt *p, struct sip_request*req);
01509 static int handle_request_message(struct sip_pvt *p, struct sip_request*req);
01510 static int handle_request_subscribe(struct sip_pvt *p, struct sip_request*req, struct sockaddr_in *sin, int seqno, char *e);
01511 static void handle_request_info(struct sip_pvt *p, struct sip_request*req);
01512 static int handle_request_options(struct sip_pvt *p, struct sip_request*req);
01513 static int handle_invite_replaces(struct sip_pvt *p, struct sip_request*req, int debugint ignore, int seqno, struct sockaddr_in *sin);
01514 static int handle_request_notify(struct sip_pvt *p, struct sip_request*req, struct sockaddr_in *sin, int seqno, char *e);
01515 static int local_attended_transfer(struct sip_pvt *transferer, structsip_dual *current, struct sip_request *req, int seqno);
01516
01517 /*------Response handling functions */
01518 static void handle_response_invite(struct sip_pvt *p, int resp, char*rest, struct sip_request *req, int seqno);
01519 static void handle_response_refer(struct sip_pvt *p, int resp, char *rest,struct sip_request *req, int seqno);
01520 static int handle_response_register(struct sip_pvt *p, int resp, char*rest, struct sip_request *req, int ignore, int seqno);
01521 static void handle_response(struct sip_pvt *p, int resp, char *rest,struct sip_request *req, int ignore, int seqno);

    现在回顾一下注册SIP通道驱动时,我们注册了一系列通道驱动的回调函数,这些有什么用呢?比如当需要发出一个outbound call时,则会调用sip_request_call()。而当需要hangup时,则调用sip_hangup()。

01541 /*! /brief Definition of this channel for PBX channel registration */
01542 static 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()函数。If the INVITE has a Replaces header, it is part of an attended transfer. If so, we do not go through the dial plan but tries to find the active call and masquerade into it。(不是很明白?)

检查invite包的headers中是否有Require。最好是没有,如果有的话也必须是Replaces,其它的不支持一律不予处理。

13394    /* Find out what they require */
13395    required = get_header(req, "Require");
13396    if (!ast_strlen_zero(required)) {
13397       required_profile = parse_sip_options(NULL, required);
13398       if (required_profile && required_profile != SIP_OPT_REPLACES) {
13399          /* At this point we only support REPLACES */
13400          transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, required);
13401          ast_log(LOG_WARNING,"Received SIP INVITE with unsupported required extension: %s/n", required);
13402          p->invitestate = INV_COMPLETED;
13403          if (!p->lastinvite)
13404             sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
13405          return -1;
13406       }
13407    }

接下来检查headers中是否有Replaces,如果有则做相关处理。由于商业项目中很少涉及这种需求,则略过。

开始验证sip user的合法性,check_user()调用check_user_full()函数,该函数从heades中的from中取出用户名并在sip user list 和 sip peer list中匹配,如果没找着,再查看是否允许guest,如果不允许,则认证通不过。

13584       /* This is a new invite */
13585       /* Handle authentication if this is our first invite */
13586       res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);

检查sip包中是否有SDP信息,如: application/sdp 。SDP(Session Description Protocol)是指会话描述协议,SIP包中使用它来描述语音流协议的细节,比如某端所支持的介质编码(这些编码使用RTP进行传输)。

13558          /* Handle SDP here if we already have an owner */
13559          if (find_sdp(req)) {
13560             if (process_sdp(p, req)) {
13561                transmit_response(p, "488 Not acceptable here", req);
13562                if (!p->lastinvite)
13563                   sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
13564                return -1;
13565             }

检查该用户的并行拨打电话数有没有达到上限。

13633       /* Check number of concurrent calls -vs- incoming limit HERE */
13634       if (option_debug)
13635          ast_log(LOG_DEBUG"Checking SIP call limits for device %s/n", p->username);
13636       if ((res = update_call_counter(p, INC_CALL_LIMIT))) {
13637          if (res < 0) {
13638             ast_log(LOG_NOTICE"Failed to place call for user %s, too many calls/n", p->username);
13639             transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req);
13640             sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
13641             p->invitestate = INV_COMPLETED
13642          }
13643          return 0;
13644       }

查找对应的extension,如果没有对应的extension,则从extension s开始执行(extension s是默认的extension,s表示start)

13645       gotdest = get_destination(p, NULL); /* Get destination right away */

调用sip_new()创建channel,这时候是incoming call。当调用dial application发起outbound call时asterisk pbx根据注册的回调函数sip_request_call()同样进入到sip_new中创建channel。

13672          /* First invitation - create the channel */
13673          c = sip_new(p, AST_STATE_DOWNS_OR(p->username, NULL));

调用ast_pbx_start(),该函数启动一个独立线程负责这个channel,线程函数是pbx_thread(),pbx_thread()调用__ast_pbx_run()函数。

13717             res = ast_pbx_start(c);

__ast_pbx_run()函数allocate 一个pbx结构和cdr结构,并把它们的指针保存到ast_channel结构的pbx域和cdr域。随后进入for循环逐个执行application。具体见./main/pbx.c。

02385       /* loop on priorities in this context/exten */
02386       while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
02387          found = 1;
02388          if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
02389             /* Something bad happened, or a hangup has been requested. */

 

下面再来分析下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的帧。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值