基於OpenSIPS做註冊服務下,場景A打B,一方發起BYE掛斷後收到500,另一方無法掛斷的問題

基於OpenSIPS做註冊服務下,場景A打B,一方發起BYE掛斷後收到500,另一方無法掛斷的問題
 
 
最近在工作中遇到一個看似很奇怪的,排除起來很費勁,但最後的解決方式又及其簡單的問題,下面我們一起來看看具體發生了什麼吧!
 
一句話概括:那都是OpenSIPS  Dialog模組的default_timeout 惹的禍(學業不精,木辦法呀……)
 
問題現象:
  1. A打B,電話接通後,持續通過話5分鐘後,任意一方掛斷電話,另一方無法正常掛斷,另一方電話始終顯示正在通話中。
  2. 如果通話時長在4分鐘以內,任何一方掛斷,則另一方都能正常結束。
 
運行環境:
    OpenSIPS      2.4.2
    FreeSWITCH  1.6.20
 
 
業務場景:
  • OpenSIPs為分機提供註冊服務
  • OpenSIPs為FreeSWITCH提供Load balance服務,將電話轉接至相應FreeSWITCH
  • OpenSIPs為FreeSWITCH提供Gateway服務
    如下圖所示,無論分機互打,9001撥打9003,OpenSIPs都是先將INVITE發送至FS,FS 再將電話通過OpenSIPS呼叫到 9003
        

   

問題分析:
  1. 通過抓包分析,發現主被叫任意一方掛斷後,FS收到bye後,直接回給OpenSIPS 500 (Internal Server Error), 而導致OpenSIPS沒能將bye訊號發給另一方電話終端
  2. OpenSIPS 給FS和電話終端發OPTIONS檢測會話狀態只發4分鐘後,就不再發了,並且主被雙方可以繼續通話
  3. 如果OpenSIPS關閉掉對FS的dialog會話OPTIONS檢測,FS收到BYE後,能轉發到另一方電話終端,但終端會返回500
  4. 四分之內正常掛斷的BYE報文,跟超過4分鐘無法掛斷的BYE報文構造形式完全相同
      SIP信令時序圖部分內容如下所示:
           
 
    下面是FreeSWITCH 收到 分機9003 發起的BYE之後的SIP報文情況:
View Code

 

問題原因:
    通過上面分析,我們可以猜測這是由於OpenSIPS對dialog做OPTIONS探測引起的。仔細檢測OpenSIPS的dialog 模組配置,我發現dialog模組的 default_timeout = 240 (秒)。
    這就能充分解釋上面『問題分析』中的第2點了(只發4分鐘OPTIONS), 因為4分鐘後dialog就過期了,OpenSIPS不再發送探測包。
 
    FreeSWITCH 和 電話終端(如Yealink話機) 都遵循 SIP 協議規範,如果一通已建立的電話,在通話過程中,FS或電話終端有收到上游(如OpenSIPS)的OPTIONS探測,那麼電話從接通到掛斷,都必須收到周期性的進行OPTIONS探測,
    如果超過周期時長了,FS或電話終端仍沒有收到OPTIONS探測, 那麼FS和電話終端就會認為通話存在錯誤,後面在收到BYE時,就會返回 500 (Internal Server Error)。
 
 
解決辦法:
    將 OpenSIPS 的 default_timeout 值改大,如改成10800 即3小時,重啟OpenSIPS即可解決問題。
loadmodule "dialog.so"
modparam("dialog", "db_mode", 1)
modparam("dialog", "dlg_match_mode", 1)
modparam("dialog", "default_timeout", 10800)  # 3 hours timeout   【正確配置】
#modparam("dialog", "default_timeout", 240)  # 4 mins             【錯誤配置 :如果使用了create_dialog("Pp"),當一同電話超過 default_timout + options_pint_interval ,就會出現終端返回500】
modparam("dialog", "profiles_with_value", "caller ; callee")
modparam("dialog", "options_ping_interval", 60) # 1 minu

route{
        if (is_method("INVITE")) {
                if ( !create_dialog("Pp") ) {   # 對OpenSIPS上進出兩個dialog(如主/被叫)都進行OPTIONS探測
                        xlog("create_dialog error :  Internal Server Error");
                        send_reply("500","Internal Server Error");
                        exit();
                }
        }
}