WebRTC-ICE-STUN連接性檢查-客戶端流程

​在前面章節中,無論是關於發送offer還是關於接收answer消息的討論中都沒有離開一個重要的步驟,那就是執行連接性檢查(connectivity checks)。因為側重點的不同,筆者在前面的章節沒有詳細介紹連接性檢查的具體細節,在這個章節,我們重點討論連接檢查的具體內容和執行流程。


所有ICE的部署需要符合RFC5389中STUN的規範(已經更新為RFC8489)。大家知道,全部署場景的agent才是ICE部署中核心內容。全部署場景agent需要扮演STUN客戶端(生成檢查)和STUN服務器端(接收角色)的兩種角色。輕量級的agent則作為服務器端只能接收檢查(check)。因此,我們所討論的內容將以SUTN客戶端生成流程和服務器端接收流程兩個部分的內容進行討論。

其中,客戶端流程主要包括:

  • 創建權限支持轉發候選地址
  • 發送請求
  • 處理響應

服務器端主要流程包括:

  • 全場景部署agent的其他額外步驟處理
  • 輕量級部署agent的其他額外步驟處理


筆者會根據以上的內容逐一進行討論。首先,筆者將介紹關於STUN客戶端的流程處理。


STUN客戶端流程總覽

這裡,筆者首先說明一下關於STUN客戶端的發送流程。STUN客戶端發送連接檢查(connectivity checks),確認連接檢查是一個ordinary還是triggered check。另外,筆者說明一下,以下討論的流程僅支持全部署場景agent。


為了保證安全,筆者在前面的文章中也提到過,轉發也需要一個權限要求。如果使用relayed 本地候選地址發送連接檢查的話,如果以前沒有創建轉發權限的話,客戶端必須首先需要創建一個轉發權限,允許通過relayed本地候選地址進行轉發。如果已經有已創建的轉發權限的話,agent可以使用這個權限。如果需要創建轉發權限的話,agent轉發權限需要根據一定的流程來實現權限處理。具體的權限創建流程讀者可以參與RFC5766-8和9章節。創建好的權限必須支持遠端候選地址。一種情況需要特別處理,agent在正常情況下,發送CreatePermission請求進行連接檢查創建時,RFC5245規範推薦agent要延遲TURN通道創建,直到ICE完成後再開始創建。一旦權限創建完成以後,agent必須維護權限的活動狀態,一直到ICE結束此流程。


發送請求

在STUN客戶端處理流程中,本地候選地址對遠端候選地址發送綁定請求(Binding),發送此請求後生成一個check。關於綁定請求的具體構建和生成,讀者可以參與RFC8489。連接檢查的安全策略必須使用STUN短期安全機制來實現。關於STUN短期安全機制(用戶名稱/密碼+時間限定)和長期安全機制筆者在以前也做過介紹,讀者也可以查閱RFC8489-9.1章節瞭解更多細節。注意,不能使用RFC3489實現向後兼容的支持能力。連接檢查必須使用FINGERPRINT機制來實現STUN消息區分。關於FINGERPRINT機制的定義,讀者可以查閱RFC8489-7。


ICE通過定義了四個新屬性拓展了STUN支持,這四個屬性分別是PRIORITY,USE-CANDIDATE,ICE-CONTROLLED,和ICE-CONTROLLING。在接下來的章節中筆者分別介紹這四個新定義的拓展屬性。這四個STUN拓展的新屬性僅支持ICE的連接檢查。


ICE定義的四個新STUN拓展說明

如果agent發送綁定請求時,它必須在其綁定請求中包含一個PRIORITY拓展屬性。agent必須設定這個PRIORITY等於候選優先級排序中設定的那個權限設置。對於反射候選地址來說,這個優先級可以通過檢查結果的學習來獲得。除了反射候選地址中的偏好類型設置為此偏好設置以外,此優先級值計算和本地候選地址配對計算是一樣的。


在綁定請求中,被控方agent可以包含一個USE-CANDIDATE。但是,主控方一定不能在綁定請求中包含此拓展屬性。此STUN拓展屬性USE-CANDIDATE表示的意思是被控方agent希望退出此構件中的檢查,使用來自於此構件中的候選配對執行檢查。這裡涉及了一個配對推薦的具體流程,筆者在未來的文章中將具體討論此指導原則和此STUN拓展的使用。


其餘兩個拓展屬性根據角色不同選擇包含不同的屬性。具體來說,如果agent是在一個主控方角色中的話,在agent發送綁定請求時,它必須在請求中包含ICE-CONTROLLED拓展屬性。如果是在被控方agent中的話,它必須在綁定請求中包含一個ICE-CONTROLLING拓展屬性。當然,兩種角色可能會因為會話不同其內容也可能不同,具體關於其角色決定的內容處理,筆者在前面的文章中有非常詳細說明。


構建安全信息

除了agent發送綁定請求中需要各自包含所需要的拓展屬性以外,agent同時也需要發送一定的安全信息來實現安全驗證。綁定請求作為一種連接檢查,它也必須使用STUN短期安全機制。STUN短期安全機制需要用戶名稱和密碼。安全策略中的用戶名稱是本地agent的用戶名稱和遠端peer的用戶名稱的合併名稱(通過括號分開),密碼是遠端peer提供的密碼。這裡的策略設置比較迷惑,讀者一定要注意。

WebRTC-ICE-STUN連接性檢查-客戶端流程

連接檢查用戶安全構件組合


具體連接檢查的處理方式如上圖示例,根據連接檢查的方向不同,用戶名稱和密碼的選擇組合是不同的。如果從發起,L端agent需要對R端agent進行連接檢查的話,它的用戶名稱組合方式是(RFRAG:LFRAG),密碼是RPASS;反之亦然,如果從R端發起連接檢查,其用戶名稱組合是(LFRAG:RFRAG),密碼是LPASS密碼。返回響應時,使用的用戶名稱和密碼和請求中的相同,包含發生變化(USERNAME屬性不會出現)。


區分服務處理

如果agent在媒體數據中(例如在QoS中)使用了Diffserv(遵從RFC2475),agent也應該對連接檢查使用同樣的標識方式。因為區分服務不在我們討論的範圍,具體關於Diffserv,讀者查閱RFC2475,這裡不做進一步討論。


處理響應

當收到響應以後,這個響應需要使用事務ID和其綁定請求關聯在一起。這個綁定關聯的處理流程在RFC5389或RFC8489中定義。然後把響應綁定到候選配對上,這個候選配對是綁定請求以前發送的候選配對。針對具體的STUN使用中關於失敗響應和成功響應的不同場景,這個部分定義了額外的步驟來處理這些流程。下面,我們專門針對失敗響應處理和成功響應處理做進一步的介紹。


失敗響應場景和成功響應場景詳解

失敗響應有很多種。如果STUN事務生成一個487錯誤碼,這表示agent角色衝突。agent需要檢查在請求綁定中是否包含了前面所說的拓展屬性ICE-CONTROLLED或ICE-CONTROLLING。如果請求包含的是ICE-CONTROLLED屬性,agent還沒有完成角色切換的話,它必須強化到主控方角色。如果請求包含的是ICE-CONTROLLING屬性,agent還沒有完成角色切換的話,它必須強化到被控方角色。一旦agent完成切換的話,它必須對生成487錯誤的候選配對進行入隊處理,然後這個隊列進入到triggered check queue中。然後將此配對狀態設置為等待狀態。當triggered check發送以後,它將包含一個ICE-CONTROLLED或ICE-CONTROLLING屬性,這個屬性反映它新的角色。這裡注意,tie-breaker的值一定不能重選。


角色切換以後,需要進一步的計算。因為不同角色具有不同的功能,因此agent需要重新計算配對優先級(前面文章有介紹)。另外,角色切換還會影響agent所負責的其他功能,例如基於ICE結果選擇推薦配對,和生成更新的offer消息。除了ICMP錯誤以外,因為環境不同,可能STUN事務還能生成其他的錯誤響應。Agent可以支持針對連接檢查的ICMP錯誤接收的工作。如果STUN事務生成ICMP錯誤,agent將會設置此配對為錯誤狀態。如果STUN事務生成的錯誤是不可恢復的錯誤或者超時錯誤的話,agent也會設置此配對的狀態為錯誤狀態。關於不可恢復的錯誤,讀者可以查閱RFC5389-7.3.4章節關於錯誤的詳解。


Agent必須檢查源IP地址和響應端口是否等同於目的地地址和端口(目的地地址和端口是綁定請求發送過去的地址和端口),並且響應中的目的地地址和端口是否匹配源地址和端口(源地址和端口是從綁定請求中發送的)。換句話說,在請求和響應中的源地址和目的地傳輸地址是對稱的。如果這兩組地址不對稱,agent將會設置此配對是失敗狀態。


以上筆者討論的是檢查失敗響應的處理。現在,筆者討論成功響應的處理。如果檢查成功的話,檢查成功,以下所有條件必須為真:

  • STUN事務生成成功響應。
  • 響應中的源IP地址和端口等同於目的地IP地址和端口(綁定請求中發送的)。
  • 響應中的目的地IP地址和端口匹配源IP地址和端口(從綁定請求發送的)。


獲得成功響應以後,agent需要進一步對響應消息和候選配對進行處理。其流程需要經過四個步驟的處理。筆者繼續分別介紹這幾個流程。


第一個步驟是發現peer的反射候選地址。Agent需要從響應中檢查映射地址。如果傳輸地址不能匹配agent獲知的任何本地候選地址,映射地址表示一個新候選地址,此地址就是peer反射候選地址。此反射候選地址和其他的候選地址一樣,同樣具有類型,基準地址,優先級和foundation。這四個屬性的計算方式如下:

  • 它的類型等同於peer的反射地址。
  • 它的基準地址等同於候選配對中的本地候選地址,從地址來自於STUN檢查發送地址。
  • 它的優先級設置為綁定請求中的PRIORITY屬性值。
  • 它的foundation是通過foundation計算獲得。


計算以後,peer反射候選地址(peer reflexive candidate)會添加到媒體流的本地候選地址列表中。對此媒體流來說,其用戶名稱和密碼和其他本地候選地址的相同。但是,peer反射候選地址不會和其他遠端候選地址配對。在此流程中,也沒有必要進行反射地址和遠端地址的配對處理。一對有效的配對會立刻通過構建有效配對的流程進行處理(此章節第二個步驟介紹)。如果agent希望peer反射候選地址和其他遠端候選進行配對的話(這個遠端候選地址不在將會生成的有效配對內),agent可以生成一個更新的offer,在此offer中包含peer反射候選地址。通過這樣的方式,可以完成此peer反射候選地址和其他遠端候選地址配對的流程。


發現了反射候選地址以後,第二步的流程就是構建有效配對。agent可以開始構建一對候選配對。候選配對中,它的本地候選地址等同於響應中的映射地址,它的遠端候選地址等同於綁定請求中發送的目的地地址。這樣的配對稱之為有效配對。因為它們的有效性已經通過STUN連接檢查驗證。有效配對可能來自於不同的配對,讀者需要注意。具體來說,有效配對可能等同於檢查生成的配對,可能等同於檢查列表中不同的配對,或也可能是一對當前不在檢查列表中的配對。如果這個配對是來自於檢查流程生成的配對或者在當前檢查列表的配對,它也可以被添加到有效列表中(VALID LIST)。agent為每個媒體流維護此列表狀態。在ICE處理流程啟動時,這個列表是為空狀態,檢查流程開始執行後在列表中逐漸增加配對,最後生成一個有效候選配對。


Agent經常也會遇到這樣的狀態,配對不在任何檢查列表中。為什麼會出現這樣的情況呢?我們可以回顧一下這樣的情景,檢查列表有一對配對,其本地候選地址從來不是一個反射候選地址。這種配對已經有它們的本地候選地址(這些地址已經轉換成了服務器端反射候選地址的基準地址),如果這些配對重疊的話,需要進行過濾篩選處理。當針對STUN檢查的響應返回時,如果兩個agent之間存在一個NAT時,映射地址將會是一個反射地址。這種情況下,有效配對將有一個本地候選地址,這個地址不能匹配檢查列表中的配對的任何候選地址。


如果一對配對不在任何檢查列表中的話,還要進行關於優先級的計算處理。Agent將會為此配對計算優先級。優先級計算基於每個候選地址優先級,其計算方式根據構建檢查列表的流程來進行。本地候選地址的優先級基於其類型來決定。如果它不是peer reflexive,優先級等同於SDP中候選地址指示的優先級。如果它是peer reflexive,優先級等同於agent完成的綁定請求中的PRIORITY屬性值。遠端候選地址的優先級來自於對端peer的SDP中。如果沒有出現遠端候選地址,檢查流程必須啟動一個triggered check來生成一個遠端候選地址。這種情況下,優先級來自於triggered check完成的綁定請求中的PRIORITY屬性值。然後,這個有效配對被添加到有效列表中(VALID LIST)。


完成有效配對的構建,agent還要進行第三步處理流程。這個步驟就是更新有效配對的狀態。Agent設置配對狀態,這個狀態是一個生成檢查成功的流程。這裡一定要注意,作為一種響應結果,這裡的配對(生成檢查流程)和構建有效配對是不同的處理方式(前面介紹過)。檢查的成功可能引起其他檢查的狀態也發生改變。Agent必須按照以下兩個步驟來執行:

  1. 針對同樣媒體流和同樣foundation,agent修改所有其他封凍狀態的配對到一個等待狀態。通常來說,也不總是這樣,它們的其他配對將有不同的component IDs。
  2. 對此媒體流的每個構件來說,如果在有效列表中有一對配對,這個檢查的成功可能對其他媒體流關閉封凍檢查。注意,按照這一步的流程執行的話,對每個媒體流的構件來說,不僅是第一次有效列表配對在這種情況下所需要考慮按照這些流程執行,每一個後續檢查獲得的檢查成功結果獲得的配對都要添加到有效列表中。agent按照順序對其他媒體流查詢檢查列表:如果檢查列表是在活動狀態,agent修改檢查列表中所有封凍狀態的配對(它們的foundation匹配了有效列表中等待狀態的配對的foundation值)。如果檢查列表是封凍狀態,並且在檢查列表中至少有一對配對,它的foundation匹配了有效列表的配對的foundation,在檢查列表中所有配對中,如果其foundation匹配了有效列表中的一對配對的foundation的話,所有列表中配對的狀態會設置為等待狀態。這樣的處理結果會導致檢查列表變為活動狀態,ordinary checks開始為其工作。具體細節查閱筆者歷史文檔中的定時檢查設置。
    如果檢查列表是封凍狀態,檢查列表中沒有配對,它的foundation匹配有效列表中的配對的foundation的話,agent將執行以下處理流程,agent將會對所有的配對分組設置,所有配對有同樣的foundation,並且,針對每個組設置,使用最低component ID方式設置其配對狀態為等待狀態,如果有一個以上這樣的配對的話,則優先啟用最高優先級的配對。


Agent完成了更新配對狀態以後,最後agent將會執行第四步進行更新推薦配對處理。如果agent是一個被控方agent,它已經在綁定請求中包含了USE- CANDIDATE屬性的話,從check生成的有效配對已經有一個推薦配對設置flag,這個設置為true狀態。如果此配對的優先級在所有標識的配對中具有最高的優先級,這個標識則表示此媒體流或所有媒體流將使用這一對有效配對。如果agent是一個主控方agent,這個響應可能是triggered check的結果,這個triggered check在響應中返回到請求,此請求自己包含一個USE-CANDIDATE屬性。這樣的情況就是一個更新推薦配對示例,因此它可能導致對這個配對(這個配對是從原始請求學習獲得)進行推薦flag設置。


以上介紹了失敗響應和成功響應的處理。在處理響應的流程中,除了對失敗響應和成功響應進行處理以外,還要對檢查列表和定時器更新進行處理。無論檢查是否是成功還是失敗,最後事務完成需要更新檢查列表和定時器的狀態。如果所有在檢查列表中的配對是失敗或者成功的狀態:

  • 針對每個媒體構件來說,在有效列表中沒有一對配對,檢查列表的這個狀態設置為失敗狀態。
  • 對每個封凍的檢查列表,agent以同樣的foundation對所有配對進行分組
  • 並且針對每個組,把帶最低component ID的配對的狀態設置為等待狀態。如果有超過一個以上這樣的配對,則使用具有最高優先級的配對。


如果在檢查列表中沒有任何配對在封凍或者等待狀態的話,這個檢查列表則不再認為是活動的檢查列表,並且針對ordinary checks,檢查列表不會在定時器計算中計入N的值。這裡N是指活動檢查列表數量。具體就是流程,讀者可以查閱筆者上一篇歷史文檔關於設定定時檢查的討論。


本章節重點介紹了關於ICE連接檢查中的STUN客戶端的處理流程。為了避免讓讀者引起歧義,方便讀者閱讀,筆者將STUN服務器端處理流程獨立分為另外一篇文章發佈,關於STUN服務器端的處理流程將在下一篇文章中加以介紹。STUN服務器端主要流程包括兩個場景的處理流程:首先是關於全場景部署agent的其他額外步驟處理(檢測和修復角色衝突,計算映射地址,通過學習獲得peer反射候選地址,Triggered Checks討論和更新推薦配對標識),然後是關於輕量級部署agent的其他額外步驟處理。


參考資料:

https://www.rfc-editor.org/rfc/rfc5389

https://www.rfc-editor.org/rfc/rfc8489

https://www.rfc-editor.org/rfc/rfc5766

https://ir.nctu.edu.tw/bitstream/11536/43505/1/655201.pdf

https://www.cisco.com/c/en/us/td/docs/solutions/PA/ICE/icepa125.html

https://dev.w3.org/2011/webrtc/editor/archives/20130830/webrtc.html


分享到:


相關文章: