Asterisk 1.8 sip 協議棧分析 2

來源:互聯網
上載者:User

上一篇文章分析了 sip註冊訊息的流程,下面分析一下 invite請求的處理流程。

 

從handle_request_invite入口,invite請求此處處理replace要求標頭,如果為replace則認為是諮詢,此時不會建立新的通道,而是找到一個通道植入(masqued),大多數情況下是根據invite建立新的請求,所以此處我們從這裡開始,不考慮諮詢情況

 

首先檢查此請求是否為重複請求,if (!req->ignore) ,接下來調用check_via檢查via頭域,這個函數涉及到nat穿越問題,此函數解析rport頭域參數,如果via頭域有rport= ,則設定標記此請求包含rport 域標誌,同時檢查maddr= 域是否存在,如果此處rport=存在則設定nat mode 為 nat,否則為no nat, 至此check_via結束。

 

返回 invite函數,這裡 invite 有兩種情況,一個為 call-id已經存在,則asterisk認為此請求是re-invite(!p->owner),否則認為是一個新的invite,關於re-invite有很多故事,涉及到asterisk是b2bua還是proxy的問題,下面先討論非 re-invite請求。

 

從列印資訊看到

ast_verbose("Using INVITE request as basis request - %s/n", p->callid);

 

Using INVITE request as basis request - ZjRiYjZkYzYzZDNjNDRmMjhmMmNlNzdmODE4NTYzZmE.

 

如果開啟sip history 可以看到會調用,

append_history(p, "Invite", "New call: %s", p->callid);

 

接下來調用parse_ok_contact ()函數儲存 此invite的contact頭域,以備將來做響應(200 ok,bye, re-invite)

fullcontact 變數儲存 全部cantact頭域,用來做bye,re-invite,okcontacturi儲存uri of acks, bye, re-invite.

 

 接下來調用 下面代碼:

 

if (!p->lastinvite && !req->ignore && !p->owner) {        // 全新invite
  /* This is a new invite */
  /* Handle authentication if this is our first invite */
  int cc_recall_core_id = -1;
  set_pvt_allowed_methods(p, req);
  res = check_user_full(p, req, SIP_INVITE, e, XMIT_RELIABLE, addr, &authpeer);
  if (res == AUTH_CHALLENGE_SENT) {
   p->invitestate = INV_COMPLETED;  /* Needs to restart in another INVITE transaction */
   res = 0;
   goto request_invite_cleanup;
  }

 

對於第一次請求會做驗證,調用check_user_full,下面分析一下此函數。

 

此函數用 請求的 from 頭域 的usr name 和 peer的 ip/port做匹配,check_user_full 調用get_calleridname 從from頭域分理出

 

callid_name,最終調用 check_peer_ok ,check_peer_ok內部尋找 peer name 是否在 peers 鏈表中存在。這裡先嘗試用

from 頭域中的 user name尋找,找不到則用 ip/port尋找。

這裡找到後 輸出如下。

if (debug)
  ast_verbose("Found peer '%s' for '%s' from %s/n",
   peer->name, of, ast_sockaddr_stringify(&p->recv));

 

 

Found peer '1501159973' for '1501159973' from 10.10.10.84:59584

然後 把peer 相關屬性拷貝到為此peer建立的channle中,如 acc,language,amaflags。callgroup,fullcontact等,

 

設定定時器管理 事務。。。

 

接下來調用 dialog_initialize_rtp函數初始化此peer的rtp資訊。

 

是否peer有RTP,有則設定編碼。設定RTP 引擎,這裡需要說明的是 asterisk1.8開始RTP協議棧改動很大,預設使用Asteirsk提供的rtp協議棧,開發人員可以自己嵌入其他rtp協議棧。

設定完協議棧後設定此peer 建立rtp會話的預設編碼規則。

Found peer '1501159973' for '1501159973' from 10.10.10.84:59584
[Dec 21 15:30:13] DEBUG[28437]: rtp_engine.c:344 ast_rtp_instance_new: Using engine 'asterisk' for RTP instance '0xc1c0078'
[Dec 21 15:30:13] DEBUG[28437]: res_rtp_asterisk.c:472 ast_rtp_new: Allocated port 18084 for RTP instance '0xc1c0078'
[Dec 21 15:30:13] DEBUG[28437]: rtp_engine.c:353 ast_rtp_instance_new: RTP instance '0xc1c0078' is setup and ready to go
[Dec 21 15:30:13] DEBUG[28437]: res_rtp_asterisk.c:2370 ast_rtp_prop_set: Setup RTCP on RTP instance '0xc1c0078'

 

此處路線: check_peer_ok->dialog_initialize_rtp->ast_rtp_instance_new->ast_rtp_instance_set_timeout

ast_rtp_instance_set_hold_timeout

ast_rtp_instance_set_prop

 

ast_rtp_instance_set_qos

 

do_setnat

 

上面為一些列調用過程,初始化此peer的RTP資訊,包括 qos,nat mode,rtcp,dtmf幾項任務。check_peer_ok函數做了很多工作哇。。。。

 

 

至此 驗證通過,RTP資訊也初始化完畢,返回 handl_request_invite函數 ,開始處理 SDP啦。。。。

 

 

/* We have a successful authentication, process the SDP portion if there is one */
  if (find_sdp(req)) {

if (process_sdp(p, req, SDP_T38_INITIATE)) {
    /* Asterisk does not yet support any Content-Encoding methods.  Always
     * attempt to process the sdp, but return a 415 if a Content-Encoding header
     * was present after processing fails. */
    if (!ast_strlen_zero(get_header(req, "Content-Encoding"))) {
     transmit_response_reliable(p, "415 Unsupported Media type", req);
    } else {
     /* Unacceptable codecs */
     transmit_response_reliable(p, "488 Not acceptable here", req);
    }

。。。。

 

可以看到,開始處理SDP...

首先當然是從invite請求中找sdp 了,,調用find_sdp (),

 

/*!
  /brief 返回sip invite請求包中是否存在SDP資訊,

 */
static int find_sdp(struct sip_request *req)

 

此處當然是找到了。。

調用 process_sdp 處理SDP,

/*! /brief Process SIP SDP offer, select formats and activate RTP channels
 If offer is rejected, we will not change any properties of the call
  Return 0 on success, a negative value on errors.
 Must be called after find_sdp().
*/
static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action)

 

 

解析SDP包頭。。。

 

[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8197 process_sdp: Processing session-level SDP v=0... UNSUPPORTED.
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8197 process_sdp: Processing session-level SDP o=- 0 2 IN IP4 10.10.10.84... UNSUPPORTED.
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8197 process_sdp: Processing session-level SDP s=CounterPath X-Lite 3.0... UNSUPPORTED.
[Dec 21 15:30:13] DEBUG[28437]: netsock2.c:125 ast_sockaddr_split_hostport: Splitting '10.10.10.84' gives...
[Dec 21 15:30:13] DEBUG[28437]: netsock2.c:155 ast_sockaddr_split_hostport: ...host '10.10.10.84' and port '(null)'.
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8197 process_sdp: Processing session-level SDP c=IN IP4 10.10.10.84... OK.
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8197 process_sdp: Processing session-level SDP t=0 0... UNSUPPORTED.

 

首先掃描 m=頭域,media stream

 

這裡包含一些SDP 資訊,解釋如下

SDP Data from Example

SDP Parameter

Parameter Name

v=0

Version number

o=Tesla 2890844526 2890844526 IN IP4 lab.high-voltage.org

Origin containing name

s=Phone Call

Subject

c=IN IP4 100.101.102.103

Connection

t=0 0

Time

m=audio 49170 RTP/AVP 0

Media

a=rtpmap:0 PCMU/8000

Attributes

  • Connection IP address (100.101.102.103);

  • Media format (audio);

  • Port number (49170);

  • Media transport protocol (RTP);

  • Media encoding (PCM μ Law);

  • Sampling rate (8,000 Hz).

 

每個SDP頭域包含 m,o,c,等對不同SDP頭域的處理,

每種類型調用 process_sdp_類型 函數處理,

如 串連地址 處理函數process_sdp_c,這裡最重要的是 process_sdp_a_媒體類型,此函數處理SDP包的屬性,如音頻,則為

process_sdp_a_audio,關於SDP處理的核心位置都在這個函數中。。。process_sdp根據SDP包屬性(媒體編碼類別型)匹配支援的編碼類別型。

 

[Dec 21 15:30:13] DEBUG[28437]: rtp_engine.c:535 ast_rtp_codecs_payloads_set_m_type: Setting payload 98 based on m type on 0xb7b30490
Found RTP audio format 8
[Dec 21 15:30:13] DEBUG[28437]: rtp_engine.c:535 ast_rtp_codecs_payloads_set_m_type: Setting payload 8 based on m type on 0xb7b30490
Found RTP audio format 101
[Dec 21 15:30:13] DEBUG[28437]: rtp_engine.c:535 ast_rtp_codecs_payloads_set_m_type: Setting payload 101 based on m type on 0xb7b30490
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=alt:1 1 : Bwd30+5D C9DdG2tq 10.10.10.84 50946... UNSUPPORTED.
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=fmtp:101 0-15... UNSUPPORTED.
Found audio description format BV32 for ID 107
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=rtpmap:107 BV32/16000... OK.
Found audio description format BV32-FEC for ID 119
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=rtpmap:119 BV32-FEC/16000... OK.
Found audio description format SPEEX for ID 100
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=rtpmap:100 SPEEX/16000... OK.
Found audio description format SPEEX-FEC for ID 106
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=rtpmap:106 SPEEX-FEC/16000... OK.
Found audio description format SPEEX-FEC for ID 105
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=rtpmap:105 SPEEX-FEC/8000... OK.
Found audio description format iLBC for ID 98
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=rtpmap:98 iLBC/8000... OK.
Found audio description format telephone-event for ID 101
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=rtpmap:101 telephone-event/8000... OK.
[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:8384 process_sdp: Processing media-level (audio) SDP a=sendrecv... OK.

 

 

 

至此,關於此peer的 SDP資訊及 RTP資訊都已初始化完畢。

 

回到 handle_request_invite()

/* Check number of concurrent calls -vs- incoming limit HERE */
  ast_debug(1, "Checking SIP call limits for device %s/n", p->username);

 

開始 檢查 peer的 call-limit

如果此peer達到 上限,則返回 480 Temporarily Unavailable (Call limit)響應。。。所以當調試時返回此響應我們應該猜測到 此裝置已經達到並發上限。。。。

 

接下來 調用get_destination(),我們得給此請求 送到哪????

此函數用 invite請求的 to 頭域 作為請求地址,

根據請求的peer name@ip 尋找請求的peerr是否在我這check_sip_domain()。。。找不到則到dialplan中尋找。。。。。

 

 

/* If we don't have a peer (i.e. we're a guest call),
   * overwrite the original context */
  if (!ast_test_flag(&p->flags[1], SIP_PAGE2_HAVEPEERCONTEXT) && !ast_strlen_zero(domain_context)) {
   ast_string_field_set(p, context, domain_context);
  }

 

這裡是當 我們在sip config 裡設定潤許guest invite時設定 context為預設。。

 

當在dialplan中找到對應分機時我們得給此SIP 請求 建立 channle啦。。

 

調用 sip_new()...這裡,sip_new函數是真正建立sip通道的地方,此函數 在呼入請求,及外呼請求時調用,分別為函數 sip_request_call及handle_request_invite函數。。。

 

sip_new函用 sip_pvt結構建立sip structuer, 設定此通道的編碼類別型,dtfm, caller id等資訊。。。

調用ast_channel_alloc 宏(channel.c)建立sip channle,建立細節在__ast_channel_alloc_ap 函數中,channle的分配用

ao2_alloc函數,同時指定了解構函式釋放通道,申請channel記憶體後設定 管道控制代碼初始狀態,建立此channle調度器上下文,

1.8新增了 caller party information 統計資訊,所以此處先初始化這些結構,接下來申請channle無名管道,

if (pipe(tmp->alertpipe)) {
   ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe! Try increasing max file descriptors with ulimit -n/n");
   return ast_channel_unref(tmp);
  } else {
   flags = fcntl(tmp->alertpipe[0], F_GETFL);
   if (fcntl(tmp->alertpipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
    ast_log(LOG_WARNING, "Channel allocation failed: Unable to set alertpipe nonblocking! (%d: %s)/n", errno, strerror(errno));
    return ast_channel_unref(tmp);
   }
   flags = fcntl(tmp->alertpipe[1], F_GETFL);
   if (fcntl(tmp->alertpipe[1], F_SETFL, flags | O_NONBLOCK) < 0) {
    ast_log(LOG_WARNING, "Channel allocation failed: Unable to set alertpipe nonblocking! (%d: %s)/n", errno, strerror(errno));
    return ast_channel_unref(tmp);
   }
  }

 

把建立的管道加入fd列表監聽。。。。這裡channle fd數量是有限制的,預設一個channle最大10個。

 

 

/* Always watch the alertpipe */
 ast_channel_set_fd(tmp, AST_ALERT_FD, tmp->alertpipe[0]);
 /* And timing pipe */
 ast_channel_set_fd(tmp, AST_TIMING_FD, tmp->timingfd);

 

 

接下來初始化uniqueid,linkeid,這裡uniqueid 最大150個字元,包括系統名字(最大127)+unix時間戳記+遞增序列。。。

 

初始化channle的amaflags,accountcode,context以便於計費使用(cdr)。

 

 

接下來 開始 分配此 channle cdr 結構並初始化。。

 

 

tmp->cdr = ast_cdr_alloc();// 分配
 ast_cdr_init(tmp->cdr, tmp);//初始化
 ast_cdr_start(tmp->cdr); //設定起始 時間,,,cdr->start..

 

 ast_cel_report_event(tmp, AST_CEL_CHANNEL_START, NULL, NULL, NULL);// cdel引擎啟動。。發channle start事件。。

1.8多了個CEL ,也是在這裡 初始化。。把channle放到 channles 容內部器。。

 

channle建立完畢,,,發送ami事件 Newchannel。。完畢後返回 建立channle 到sip_new

 

sip為一種通道類型,實際建立為channle.c中的ast_channle 結構,此結構為眾多通道的介面層,。。

這裡 設定  SIPURI,SIPDOMAIN,parkinglot,accountcode,language,等全域資料,初始化 fd 事件,值得注意的是  asterisk 1.8中 支援了 epoll 非同步Io,在一些情況下對系統的並發應該提高很多。

 

接下來 SIP_new 對於有rtp的peer,則初始化 jt引擎(rtp 抖動),

 

然後,此函數調用 Ast_pbx_start進入Asterisk核心。。。。ast_pbx_start 啟動新的線程處理此channle..

返回後,調用build_route(),記錄 record_Route頭域,此處作用是記錄路由路徑作為未來的請求。

 

ast_debug(2, "%s: New call is still down.... Trying... /n", c->name);

 

至此,channle 已經建立完成,發個臨時響應吧。。。transmit_provisional_response(p, "100 Trying", req, 0);

 

[Dec 21 15:30:13] DEBUG[28437]: chan_sip.c:21609 handle_request_invite: SIP/1501159973-0000000b: New call is still down.... Trying..

 

 

 

<--- Transmitting (no NAT) to 10.10.10.84:59584 --->
SIP/2.0 100 Trying
Via: SIP/2.0/UDP 10.10.10.84:59584;branch=z9hG4bK-d87543-7c6375234a299e1c-1--d87543-;received=10.10.10.84;rport=59584
From: "1501159973"<sip:1501159973@10.10.10.182>;tag=e648a101
To: "6969 (Softphone)"<sip:6969@10.10.10.182>
Call-ID: ZjRiYjZkYzYzZDNjNDRmMjhmMmNlNzdmODE4NTYzZmE.
CSeq: 2 INVITE
Server: Asterisk PBX 1.8.2-rc1
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Contact: <sip:6969@10.10.10.182:5060>
Content-Length: 0

 

這裡,我們可以看到,via頭域多了一個received及rport有值了,解決nat穿透。。。

 

同時記住 100 trying沒有 sdp資訊。。 調用 順序,sip_xmit<-send_response<--transmit_response<---transmit_provisional_response<--Handle_request_response.

 

接下來 執行  dialplan.......至此 一次呼入系統的請求 基本完成。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.