參考:
- http://www.52im.net/thread-50-1-1.html
- https://freeswitch.org/confluence/display/FREESWITCH/NAT+Traversal
1. 概述
nat 穿越是一個復雜困難的話題,在較新的 ice 協議中,完整實現了各種 nat 場景下通信兩端的 nat 穿越,webrtc 對 ice 協議有良好支持。
但是在 sip 協議中,nat 穿越是一系列較為復雜的 sip 擴展特性(且依賴於應用層的各種實現),sip 協議實際很難應對所有 nat 場景。
這篇文章主要記錄自己對 sip 協議下 nat 穿越的理解。
2. sip agent 的網絡類型
這里將 sip agent 分為 uac 端和 uas 端。根據 sip agent 在不同 nat 下的情況,實際上有如下幾種場景:
- uac 和 uas 在同一 nat 下,信令和媒體都能正常通信
- uac 和 uas 在不同 nat 下,這時如果不進行
- uac 在 nat 下,uas 在公網下
- uas 在 nat 下,uac 在公網下
在 sip server 的部署中,一般都會將 server 部署在能讓 sip client 直接能訪問到的網絡上,比如與 sip client 處於同一 nat 下,或者 server 部署在公網上。即使 server 部署在另一 nat 下,也會對 nat 進行靜態地址映射。
所以為了方便分析,以下假設有一個 sip agent 客戶端,其在 nat 下;一個 sip agent 服務端,其在公網下。sip client 向 sip server 進行注冊。傳輸協議優先 udp。
2.1 nat 類型
nat 有如下類型:
- Full Cone NAT(全錐型NAT),被 nat 網關映射到外網的 ip:port 地址對,任何一個外部主機/端口發送到此 ip:port 地址對的報文都會順利通過
- Restricted Cone NAT(限制錐型NAT),被 nat 網關映射到外網的 ip:port 地址對,只有與映射時目標外部主機相同 ip 的報文,才能順利通過,即限制了 ip 地址
- Port Restricted Cone NAT(端口限制錐型),被 nat 網關映射到外網的 ip:port 地址對,只有與映射時目標外部主機相同 ip:port 的報文,才能順利通過,即限制了 ip 地址和 port 端口
- Symmetric NAT(對稱型NAT),前面幾種 nat 類型,只要內網源 ip:port 地址對相同,nat 映射后的地址也相同。但是對稱型 nat 只要目標地址不同,同一個內網源 ip:port 地址 nat 映射后的地址也不同
注意,這里只是簡單描述幾種 nat 類型,具體請參看其它文檔。
3. sip 響應消息的穿越
sip client 發送過來的消息,sip server 能夠得到 nat 映射后的 ip:port 地址對。
3.1 rport
sip client 發送請求給 sip server 時,via 頭可以攜帶空的 rport 參數,sip server 進行響應的時候,會將 rport 設置為 nat 映射后的端口。
- 請求:
- 響應:
via 中攜帶 rport 意味着 client 期待 server 將響應消息發送給 rport 的端口,而不是 sent-by 中的端口。
sip server 端也可以根據應用層策略,強制將響應發送給 nat 映射后的端口。
3.2 received
在 sip 協議中,如果 sent-by 中的 host 是一個域名或者與 server 看到的 nat 映射后的地址不一樣,server sip 協議棧會添加 received 字段表明 nat 映射后的 ip。
sip server 端也可以根據應用層策略,強制將響應發送給 nat 映射后的 ip。
3.3 tcp 的情況
如果 sip client 是通過 tcp 發送的請求,sip 協議規定優先使用原來的連接來發送響應。如果 tcp 已經斷開,sip server 端也可以根據應用層策略,強制將響應通過 udp 發送給 nat 映射后的 ip:port 對。
3.4 總結
響應消息的 nat 穿越實際比較簡單,即將響應發送給 nat 映射后的 ip:port 對即可。
4. sip contact
sip contact 是 sip client 的聯系地址。sip server 需要將 client 的 contact 保存起來,用於 server 主動發送請求時確定目的地址。
4.1 注冊時 contact
sip client 注冊的時候,會攜帶一個或多個 contact 地址。當 sip client 處於 nat 下,contact 地址有兩種情況:
- ip:port 地址對是私網地址,sip server 無法主動發送請求給 sip client
- ip:port 地址對是 nat 映射后的地址,sip server 可以主動發送請求給 sip client
對於 contact 是 nat 映射后的地址,為了保持 nat 地址映射,需要進行保活處理:
- sip client 可以每隔不大於 30s 的時間發起刷新注冊
- sip server 可以每隔不大於 30s 的時間發起 option 請求
4.2 invite 時的 contact
invite 時的 contact 應該是來自於注冊時多個 contacts 中的某一個,但是實際上也可以不一樣。此 contact 地址也可能是私網地址,或者是 nat 映射后的地址。
對於 contact 是 nat 映射后的地址,為了保持 nat 地址映射,需要進行保活處理:
- sip client 可以每隔不大於 30s 的時間發起會話內刷新 invite 請求,或者會話內 option 請求
- sip server 可以每隔不大於 30s 的時間發起會話內 option 請求
5. sip 非會話內請求消息的穿越
實現 nat 穿越可以在 client 端主動實現,也可以由 server 端實現。
5.1 sip client 主動發現 nat
主動發現 nat 可以有如下方式(也可以稱為 nat 地址映射/綁定):
- 配置 stun 服務,sip client 通過 stun 服務實現 nat 地址映射
- client 向 server 發送 option 請求,根據 rport 和 received 地址實現 nat 地址映射
- client 第一次注冊時,收到 401 響應后,根據 rport 和 received 地址實現 nat 地址映射
sip client 綁定 nat 地址映射后,將得到的映射后的 ip:port 地址對,修改 register 中 contact 地址,然后發起注冊(register 401 后重新發起注冊),這樣 server 端就能得到 client nat 映射后的地址。
5.1.1 不同 nat 類型的討論
sip client 主動發現 nat,考慮不同的 nat 類型下的有效性:
- 通過 stun 服務發現的 nat 映射地址,如果 sip client 不是用映射地址發起的注冊,那么 contact 地址只對 Full Cone NAT(全錐形 nat) 類型有效;否則,對所有 nat 類型都有效
- 通過 option 請求后 rport 和 received 得到的 nat 映射地址,contact 地址對所有 nat 類型都有效,即使 sip client 不是用映射地址發起的注冊
- 通過 register 得到的 nat 映射地址,對所有 nat 類型都有效
5.2 sip server 發現 nat
如果 sip client 沒有主動發現 nat 的動作,那么 server 端可以根據應用層策略,將 client 的 contact 地址強制替換為 nat 映射后的地址。並進行保活。
5.3 server 發送請求
非會話內請求直接查找 client 對應的 contact 地址,然后將請求發送過去(多個 contacts 的情況需要 fork)。
因為之前實現了 contact 地址 nat 映射的替換,且進行了保活,這里可以直接將請求發送過去。
5.4 tcp 下的考慮
tcp 下 server 主動發送請求與 udp 情況相同。
6. sip 會話內請求消息的穿越
- 初始的 invite 如果是 sip client 發送的,那么 contact 地址(nat 映射后的,參見前文 contact 的內容)將被保存和保活。后面會話內請求從 sip server 發送給 sip client 的時候,直接使用 invite 接收的 contact 地址。
- 初始的 invite 如果是 sip server 發送的,invite 穿越過程與 sip 非會話內請求消息的穿越 章節相同。后面會話內請求從 sip server 發送給 sip client 的時候,直接使用發送 invite 時的 contact 地址。
注意,invite 由 sip server 發送時,sip client 返回的 200ok 中攜帶的 contact 地址,sip server 可以根據本地策略是否保存下來作為后續會話內請求的目的地址。
7. rtp 媒體穿越
根據前文的假設,sip server 工作在 sip client 能直接訪問到的網絡中,所以從 sip client 發給 sip server 的媒體流能直接發送到。
但是 sip server 發送給 sip client 的媒體流,如果 sip client 端 sdp 中的媒體地址是私網地址,那么就無法發送過去。
sip client 也可以使用 stun 服務器主動發現 nat 映射后的媒體地址,但是此地址只對 Full Cone NAT(全錐形 nat) 類型有效,大部分情況下 nat 媒體流穿越將失敗。
所以一種較好的做法是 sip server 先等待 sip client 發送媒體,得到 nat 映射后的地址后,sip server 再發送媒體流。
這里有一個問題是 sip client 或許不會發送任何媒體流給 server 端,這個時候就會出現信令走通但是媒體不通的情況。
7.1 tcp 下的考慮
由於 tcp 需要先建立連接,所以 sip server 可以修改 sdp 讓 client 主動通過 tcp 連接上來,這樣雙方的媒體流就能打通。
8. 其它 nat 穿越的方式
一種典型方式就是 ALG(應用層網關),應用層網關能主動解析收發到的 sip 協議頭和 sdp 內容,並進行地址替換。但是實際上許多路由器對於此功能的支持有限,有時還會影響正常 nat 穿越方法的實行。