SIP 模塊是 FreeSWITCH 的主要模塊,所以,值得拿出專門一章來講解。
在前幾章時裏,你肯定見過幾次 sofia 這個詞,只是或許還不知道是什麼意思。是這樣的,Sofia-SIP 是由諾基亞公司開發的 SIP 協議棧,它以開源的許可證 LGPL 發佈,爲了避免重複發明輪子,FreeSWITCH 便直接使用了它。
在 FreeSWITCH 中,實現一些互聯協議接口的模塊稱爲 Endpoint。FreeSWITH 支持很多的 Endpoint, 如 SIP、H232等。那麼實現 SIP 的模塊爲什麼不支持叫 mod_sip呢?這是由於 FreeSWITCH 的 Endpoint 是一個抽象的概念,你可以用任何的技術來實現。實際上 mod_sofia 只是對 Sofia-SIP 庫的一個粘合和封裝。除 Sofia-SIP 外,還有很多開源的 SIP 協議棧,如 pjsip、osip 等。最初選型的時候,FreeSWITCH 的開發團隊也對比過許多不同的 SIP 協議棧,最終選用了 Sofia-SIP。FreeSWITCH 是一個高度模塊化的結構,如果你不喜歡,可以自己實現 mod_pjsip 或 mod_osip 等,它們是互不影響的。這也正是 FreeSWITCH 架構設計的精巧之處。
Sofia-SIP 遵循 RFC3261 標準,因而 FreeSWITCH 也是。
配置文件
Sofia 的配置文件是 conf/autoload_configs/sofia.conf.xml,不過,你一般不用直接修改它,因爲它實際上直接使用一條預處理指令裝入了 conf/sip_profiles/ 目錄中的 XML 文件:
<X-PRE-PROCESS cmd="include" data="../sip_profiles/*.xml"/>
所以,從現在起,可以認爲所有的 Sofia 配置文件都在 conf/sip_profiles/ 中。
Sofia 支持多個 profile,而一個 profile 相當於一個 SIP UA,在啓動後它會監聽一個 “IP地址:端口” 對。讀到這裏細心的讀者或許會發現我們前面的一個錯誤。我們在講 B2BUA 的概念時,實際上只用到了一個 profile,也就是一個 UA,但我們還是說 FreeSWITCH 啓動了兩個 UA(一對背靠背的 UA)來爲 alice 和 bob 服務。是的,從物理上來講,它確實只是一個 UA,但由於它同時支持多個 Session,在邏輯上就是相當於兩個 UA,爲了不使用讀者太糾結於這種概念問題中,我在前面沒有太多的分析。但到了本章,你應該非常清楚 UA 的含義了。
FreeSWITCH 默認的配置帶了三個 profile(也就是三個 UA),在這裏,我們不討論 IPv6,因此只剩下 internal 和 external 兩個。 internal 和 external 的區別就是一個運行在 5060 端口上,另一個是在 5080 端口上。當然,還有其它區別,我們慢慢講。
internal.xml
internel.xml 定義了一個 profile,在本節,我們以系統默認的配置逐行來解釋:
<profile name="internal">
profile 的名字就叫 internal,這個名字本身並沒有特殊的意義,也不需要與文件名相同,你可以改成任何你喜歡的名字,只是需要記住它,因爲很多地方要使用這個名字。
-
<aliases>
-
<!-- <alias name="default"/> -->
-
</aliases>
如果你喜歡,可以爲該 profile 起一個別名。注意默認是加了註釋的,也就是說不起作用。再說一遍,“<!-- -->”在 XML 中的含義是註釋。
-
<gateways>
-
<X-PRE-PROCESS cmd="include" data="internal/*.xml"/>
-
</gateways>
即然 profile 是一個 UA,它就可以註冊到別的 SIP 服務器上去,它要註冊的 SIP 服務器就稱爲 Gateway。我們一般不在 internal 這個 profile 上使用 Gateway,這個留到 external 時再講。
-
<domains>
-
<!--<domain name="$${domain}" parse="true"/>-->
-
<domain name="all" alias="true" parse="false"/>
-
</domains>
定義該 profile 所屬的 domain。它可以是 IP 地址,或一個 DNS 域名。需要注意,直接在 hosts 文件中設置的 IP-域名可能不好用。
<settings>
settings 部分設置 profile 的參數。
<!--<param name="media-option" value="resume-media-on-hold"/> -->
如果 FreeSWITCH 是沒有媒體(no media)的,那麼如果設置了該參數,當你在話機上按下 hold 鍵時,FreeSWITCH 將會回到有媒體的狀態。
那麼什麼叫有媒體無媒體呢?如下圖,bob 和 alice 通過 FreeSWITCH 使用 SIP 接通了電話,他們談話的語音(或視頻)數據要通過 RTP 包傳送的。RTP 可以 像 SIP 一樣經過 FreeSWITCH 轉發,但是,RTP 佔用很大的帶寬,如果 FreeSWITCH 不需要“偷聽”他們談話的話,爲了節省帶寬,完全可以讓 RTP 直接在兩者間傳送,這種情況對 FreeSWITCH 來講就是沒有 media 的,在 FreeSWITCH 中也稱 bypass media(繞過媒體)。
-
FreeSWITCH
-
SIP / \ SIP
-
/ \
-
bob ------RTP------ alice
<!--<param name="media-option" value="bypass-media-after-att-xfer"/>-->
Attended Transfer 稱爲出席轉移,它需要 media 才能完成工作。但如果在執行 att-xfer 之前沒有媒體,該參數能讓 att-xfer 執行時有 media,轉移結束後再回到 bypass media 狀態。
<!-- <param name="user-agent-string" value="FreeSWITCH Rocks!"/> -->
不用解釋,就是設置 SIP 消息中顯示的 User-Agent 字段。
<param name="debug" value="0"/>
debug 級別。
<!-- <param name="shutdown-on-fail" value="true"/> -->
由於各種原因(如端口被佔用,IP地址錯誤等),都可能造成 UA 在初始化時失敗,該參數在失敗時會停止 FreeSWITCH。
<param name="sip-trace" value="no"/>
是否開啓 SIP 消息跟蹤。另外,也可以在控制檯上用以下命令開啓和關閉 sip-trace:
-
sofia profile internal siptrace on
-
sofia profile internal siptrace off
<param name="log-auth-failures" value="true"/>
是否將認證錯誤寫入日誌。
<param name="context" value="public"/>
context 是 dialplan 中的環境。在此指定來話要落到 dialplan 的哪個 context 環境中。需要指出,如果用戶註冊到該 profile 上(或是經過認證的用戶,即本地用戶),則用戶目錄(directory)中設置的 contex 優先級要比這裏高。
<param name="rfc2833-pt" value="101"/>
設置 SDP 中 RFC2833 的值。RFC2833 是傳遞 DTMF 的標準。
<param name="sip-port" value="$${internal_sip_port}"/>
監聽的 SIP 端口號,變量 internal_sip_port 在 vars.xml 中定義,默認是 5060。
<param name="dialplan" value="XML"/>
設置對應默認的 dialplan。我們後面會專門講 dialplan。
<param name="dtmf-duration" value="2000"/>
設置 DTMF 的時長。
<param name="inbound-codec-prefs" value="$${global_codec_prefs}"/>
支持的來話語音編碼,用於語音編碼協商。global_codec_prefs 是在 vars.xml中定義的。
<param name="outbound-codec-prefs" value="$${global_codec_prefs}"/>
支持的去話語音編碼。
<param name="rtp-timer-name" value="soft"/>
RTP 時鐘名稱
<param name="rtp-ip" value="$${local_ip_v4}"/>
RTP 的 IP 地址,僅支持 IP 地址而不支持域名。雖然 RTP 標準說應該域名,但實際情況是域名解析有時不可靠。
<param name="sip-ip" value="$${local_ip_v4}"/>
SIP 的 IP。不支持域名。
<param name="hold-music" value="$${hold_music}"/>
UA 進行 hold 狀態時默認播放的音樂。
<param name="apply-nat-acl" value="nat.auto"/>
使用哪個 NAT ACL。
<!-- <param name="extended-info-parsing" value="true"/> -->
擴展 INFO 解析支持。
<!--<param name="aggressive-nat-detection" value="true"/>-->
NAT穿越,檢測 SIP 消息中的 IP 地址與實際的 IP 地址是否相符,詳見 NAT穿越。
-
<!--
-
There are known issues (asserts and segfaults) when 100rel is enabled.
-
It is not recommended to enable 100rel at this time.
-
-->
-
<!--<param name="enable-100rel" value="true"/>-->
該功能暫時還不推薦使用。
<!--<param name="enable-compact-headers" value="true"/>-->
支持壓縮 SIP 頭。
<!--<param name="enable-timer" value="false"/>-->
開啓、關閉 SIP 時鐘。
<!--<param name="minimum-session-expires" value="120"/>-->
SIP 會話超時值,在 SIP 消息中設置 Min-SE。
<param name="apply-inbound-acl" value="domains"/>
對來話採用哪個 ACL。詳見 ACL。
<param name="local-network-acl" value="localnet.auto"/>
默認情況下,FreeSWITCH 會自動檢測本地網絡,並創建一條 localnet.auto ACL 規則。
<!--<param name="apply-register-acl" value="domains"/>-->
對註冊請求採用哪個 ACL。
<!--<param name="dtmf-type" value="info"/>-->
DTMF 收號的類型。有三種方式,info、inband、rfc2833。
info 方式是採用 SIP 的 INFO 消息傳送 DTMF 按鍵信息的,由於 SIP 和 RTP 是分開走的,所以,可能會造成不同步。
inband 是在 RTP 包中象普通語音數據那樣進行帶內傳送,由於需要對所有包進行鑑別和提取,需要佔用更多的資源。
rfc2833 也是在帶內傳送,但它的 RTP 包有特殊的標記,因而比 inband 方式節省資源。它是在 RFC2833 中定義的。
-
<!-- 'true' means every time 'first-only' means on the first register -->
-
<!--<param name="send-message-query-on-register" value="true"/>-->
如何發送請求消息。true 是每次都發送,而 first-only 只是首次註冊時發送。
<!--<param name="caller-id-type" value="rpid|pid|none"/>-->
設置來電顯示的類型,rpid 將會在 SIP 消息中設置 Remote-Party-ID,而 pid 則會設置 P-*-Identity,如果不需要這些,可以設置成 none。
<param name="record-path" value="$${recordings_dir}"/>
錄音文件的默認存放路徑。
<param name="record-template" value="${caller_id_number}.${target_domain}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
錄音文件名模板。
<param name="manage-presence" value="true"/>
是否支持列席。
<!--<param name="manage-shared-appearance" value="true"/>-->
是否支持 SLA - Shared Line Apperance。
-
<!--<param name="dbname" value="share_presence"/>-->
-
<!--<param name="presence-hosts" value="$${domain}"/>-->
這兩個參數用以在多個 profile 間共享列席信息。
-
<!-- This setting is for AAL2 bitpacking on G726 -->
-
<!-- <param name="bitpacking" value="aal2"/> -->
-
<!--<param name="max-proceeding" value="1000"/>-->
最大的開放對話(SIP Dialog)數。
-
<!--session timers for all call to expire after the specified seconds -->
-
<!--<param name="session-timeout" value="120"/>-->
會話超時時間。
-
<!-- Can be 'true' or 'contact' -->
-
<!--<param name="multiple-registrations" value="contact"/>-->
是否支持多點註冊,可以是 contact 或 true。開啓多點註冊後多個 UA 可以註冊上來,有人呼叫這些 UA 時所有 UA 都會振鈴。
-
<!--set to 'greedy' if you want your codec list to take precedence -->
-
<param name="inbound-codec-negotiation" value="generous"/>
SDP 中的語音編協商,如果設成 greedy,則自己提供的語音編碼列表會有優先權.
-
<!-- if you want to send any special bind params of your own -->
-
<!--<param name="bind-params" value="transport=udp"/>-->
-
<!--<param name="unregister-on-options-fail" value="true"/>-->
爲了 NAT 穿越或 keep alive,如果 FreeSWITCH 向其它網關注冊時,可以週期性地發一些 OPTIONS 包,相當於 ping 功能。該參數說明當 ping 失敗時是否自動取消註冊。
<param name="tls" value="$${internal_ssl_enable}"/>
是否支持 TLS,默認否。
-
<!-- additional bind parameters for TLS -->
-
<param name="tls-bind-params" value="transport=tls"/>
-
<!-- Port to listen on for TLS requests. (5061 will be used if unspecified) -->
-
<param name="tls-sip-port" value="$${internal_tls_port}"/>
-
<!-- Location of the agent.pem and cafile.pem ssl certificates (needed for TLS server) -->
-
<param name="tls-cert-dir" value="$${internal_ssl_dir}"/>
-
<!-- TLS version ("sslv23" (default), "tlsv1"). NOTE: Phones may not work with TLSv1 -->
-
<param name="tls-version" value="$${sip_tls_version}"/>
下面都是與 TLS 有關的參數,略。
<!--<param name="rtp-autoflush-during-bridge" value="false"/>-->
該選項默認爲 true。即在橋接電話是是否自動 flush 媒體數據(如果套接字上已有數據時,它會忽略定時器睡眠,能有效減少延遲)。
<!--<param name="rtp-rewrite-timestamps" value="true"/>-->
是否透傳 RTP 時間戳。
<!--<param name="pass-rfc2833" value="true"/>-->
是否透傳 RFC2833 DTMF 包。
<!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
使用 ODBC 數據庫代替默認的 SQLite。
<!--<param name="inbound-bypass-media" value="true"/>-->
將所有來電設置爲媒體繞過。
<!--<param name="inbound-proxy-media" value="true"/>-->
將所有來電設置爲媒體透傳。
-
<!--Uncomment to let calls hit the dialplan *before* you decide if the codec is ok-->
-
<!--<param name="inbound-late-negotiation" value="true"/>-->
對所有來電來講,晚協商有助於在協商媒體編碼之前,先前電話送到 Dialplan,因而在 Dialplan 中可以進行個性化的媒體協商。
<!-- <param name="accept-blind-reg" value="true"/> -->
該選項允許任何電話註冊,而不檢查用戶和密碼及其它設置。
<!-- <param name="accept-blind-auth" value="true"/> -->
與上一條類似,該選項允許任何電話通過認證。
<!-- <param name="suppress-cng" value="true"/> -->
抑制 CNG。
<param name="nonce-ttl" value="60"/>
SIP 認證中 nonce 的生存時間。
<!--<param name="disable-transcoding" value="true"/>-->
禁止譯碼,如果該項爲 true 則在 bridge 其它電話時,只提供與 a-leg 兼容或相同的語音編碼列表進行協商,以避免譯碼。
<!--<param name="manual-redirect" value="true"/> -->
允許在 Dialplan 中進行人工轉向。
<!--<param name="disable-transfer" value="true"/> -->
禁止轉移。
<!--<param name="disable-register" value="true"/> -->
禁止註冊。
-
<!-- Used for when phones respond to a challenged ACK with method INVITE in the hash -->
-
<!--<param name="NDLB-broken-auth-hash" value="true"/>-->
-
<!-- add a ;received="<ip>:<port>" to the contact when replying to register for nat handling -->
-
<!--<param name="NDLB-received-in-nat-reg-contact" value="true"/>-->
-
<param name="auth-calls" value="$${internal_auth_calls}"/>
是否對電話進行認證。
-
<!-- Force the user and auth-user to match. -->
-
<param name="inbound-reg-force-matching-username" value="true"/>
強制用戶與認證用戶必須相同。
<param name="auth-all-packets" value="false"/>
在認證時,對所有 SIP 消息都進行認證,而不是僅針對 INVITE 消息。
-
<!-- external_sip_ip
-
Used as the public IP address for SDP.
-
Can be an one of:
-
ip address - "12.34.56.78"
-
a stun server lookup - "stun:stun.server.com"
-
a DNS name - "host:host.server.com"
-
auto - Use guessed ip.
-
auto-nat - Use ip learned from NAT-PMP or UPNP
-
-->
-
<param name="ext-rtp-ip" value="auto-nat"/>
-
<param name="ext-sip-ip" value="auto-nat"/>
設置 NAT 環境中公網的 RTP IP。該設置會影響 SDP 中的 IP 地址。有以下幾種可能:
一個IP 地址,如 12.34.56.78
一個 stun 服務器,它會使用 stun 協議獲得公網 IP, 如 stun:stun.server.com
一個 DNS 名稱,如 host:host.server.com
auto , 它會自動檢測 IP 地址
auto-nat,如果路由器支持 NAT-PMP 或 UPNP,則可以使用這些協議獲取公網 IP。
<param name="rtp-timeout-sec" value="300"/>
指定的時間內 RTP 沒有數據傳送,則掛機。
<param name="rtp-hold-timeout-sec" value="1800"/>
RTP 處理保持狀態的最大時長。
-
<!-- <param name="vad" value="in"/> -->
-
<!-- <param name="vad" value="out"/> -->
-
<!-- <param name="vad" value="both"/> -->
語音活動狀態檢測,有三種可能,可設爲入、出,或雙向,通常來說“出”(out)是一個比較好的選擇。
<!--<param name="alias" value="sip:10.0.1.251:5555"/>-->
給本 sip profile 設置別名。
-
<!--all inbound reg will look in this domain for the users -->
-
<param name="force-register-domain" value="$${domain}"/>
-
<!--force the domain in subscriptions to this value -->
-
<param name="force-subscription-domain" value="$${domain}"/>
-
<!--all inbound reg will stored in the db using this domain -->
-
<param name="force-register-db-domain" value="$${domain}"/>
-
<!--force suscription expires to a lower value than requested-->
-
<!--<param name="force-subscription-expires" value="60"/>-->
以上選項默認是起作用的,這有助於默認的例子更好的工作。它們會在註冊及訂閱時在數據庫中寫入同樣的域信息。如果你在使用一個 FreeSWITCH 支持多個域時,不要選這些選項。
<!--<param name="enable-3pcc" value="true"/>-->
該選項有兩個值,true 或 poxy。 true 則直接接受 3pcc 來電;如果選 proxy,則會一直等待電話應答後纔回送接受。
-
<!-- use at your own risk or if you know what this does.-->
-
<!--<param name="NDLB-force-rport" value="true"/>-->
在 NAT 時強制 rport。除非你很瞭解該參數,否則後果自負。
<param name="challenge-realm" value="auto_from"/>
設置 SIP Challenge 是使用的 realm 字段是從哪個域獲取,auto_from 和 auto_to 分別是從 from 和 to 中獲取,除了這兩者,也可以是任意的值,如 freeswitch.org.cn。
<!--<param name="disable-rtp-auto-adjust" value="true"/>-->
大多數情況下,爲了更好的穿越 NAT,FreeSWITCH 會自動調整 RTP 包的 IP 地址,但在某些情況下(尤其是在 mod_dingaling 中會有多個候選 IP),FreeSWITCH 可能會改變本來正確的 IP 地址。該參數禁用此功能。
<!--<param name="inbound-use-callid-as-uuid" value="true"/>-->
在 FreeSWITCH 是,每一個 Channel 都有一個 UUID, 該 UUID 是由系統生成的全局唯一的。對於來話,你可以使用 SIP 中的 callid 字段來做 UUID. 在某些情況下對於信令的跟蹤分析比較有用。
<!--<param name="outbound-use-uuid-as-callid" value="true"/>-->
與上一個參數差不多,只是在去話時可以使用 UUID 作爲 callid。
<!--<param name="rtp-autofix-timing" value="false"/>-->
RTP 自動定時。如果語音質量有問題,可以嘗試將該值設成 false。
<!--<param name="pass-callee-id" value="false"/>-->
默認情況下 FreeSWITCH 會設置額外的 X- SIP 消息頭,在 SIP 標準中,所有 X- 打頭的消息頭都是應該忽略的。但並不是所有的實現都符合標準,所以在對方的網關不支持這種 SIP 頭時,該選項允許你關掉它。
-
<!-- clear clears them all or supply the name to add or the name prefixed with ~ to remove
-
valid values:
-
clear
-
CISCO_SKIP_MARK_BIT_2833
-
SONUS_SEND_INVALID_TIMESTAMP_2833
-
-->
-
<!--<param name="auto-rtp-bugs" data="clear"/>-->
某些運營商的設備不符合標準。爲了最大限度的支持這些設備,FreeSWITCH 在這方面進行了妥協。使用該參數時要小心。
-
<!-- the following can be used as workaround with bogus SRV/NAPTR records -->
-
<!--<param name="disable-srv" value="false" />-->
-
<!--<param name="disable-naptr" value="false" />-->
這兩個參數可以規避 DNS 中某些錯誤的 SRV 或 NAPTR 記錄。
最後的這幾個參數允許根據需要調整 sofia 庫中底層的時鐘,一般情況下不需要改動。
-
<!-- The following can be used to fine-tune timers within sofia's transport layer
-
Those settings are for advanced users and can safely be left as-is -->
-
<!-- Initial retransmission interval (in milliseconds).
-
Set the T1 retransmission interval used by the SIP transaction engine.
-
The T1 is the initial duration used by request retransmission timers A and E (UDP) as well as response retransmission timer G. -->
-
<!-- <param name="timer-T1" value="500" /> -->
-
<!-- Transaction timeout (defaults to T1 * 64).
-
Set the T1x64 timeout value used by the SIP transaction engine.
-
The T1x64 is duration used for timers B, F, H, and J (UDP) by the SIP transaction engine.
-
The timeout value T1x64 can be adjusted separately from the initial retransmission interval T1. -->
-
<!-- <param name="timer-T1X64" value="32000" /> -->
-
<!-- Maximum retransmission interval (in milliseconds).
-
Set the maximum retransmission interval used by the SIP transaction engine.
-
The T2 is the maximum duration used for the timers E (UDP) and G by the SIP transaction engine.
-
Note that the timer A is not capped by T2. Retransmission interval of INVITE requests grows exponentially
-
until the timer B fires. -->
-
<!-- <param name="timer-T2" value="4000" /> -->
-
<!--
-
Transaction lifetime (in milliseconds).
-
Set the lifetime for completed transactions used by the SIP transaction engine.
-
A completed transaction is kept around for the duration of T4 in order to catch late responses.
-
The T4 is the maximum duration for the messages to stay in the network and the duration of SIP timer K. -->
-
<!-- <param name="timer-T4" value="4000" /> -->
-
</settings>
-
</profile>
external.xml
它是另一個 UA 配置文件,它默認使用端口 5080。你可以看到,大部分參數都與 internal.xml 相同。最大的不同是 auth-calls 參數。在 internal.xml 中,auth-calls 默認是 true;而在 external.xml 中,默認是 false。也就是說,發往 5060 端口的 SIP 消息(一般只有 INVITE 消息)需要認證,而發往 5080 的消息則不需要認證。我們一般把本地用戶都註冊到 5060 上,所以,它們打電話時要經過認證,保證只有在們用戶 directory 中配置的用戶能打電話。而 5080 則不同,任何人均可以向該端口發送 SIP 請求。