STUN和TURN技術淺析

在現實Internet網絡環境中,大多數計算機主機都位於防火牆或NAT之後,只有少部分主機能夠直接接入Internet。很多時候,我們希望網絡中的兩臺主機能夠直接進行通信,即所謂的P2P通信,而不需要其他公共服務器的中轉。由於主機可能位於防火牆或NAT之後,在進行P2P通信之前,我們需要進行檢測以確認它們之間能否進行P2P通信以及如何通信。這種技術通常稱爲NAT穿透(NAT Traversal)。最常見的NAT穿透是基於UDP的技術,如RFC3489中定義的STUN協議。

STUN,首先在RFC3489中定義,作爲一個完整的NAT穿透解決方案,英文全稱是Simple Traversal of UDP Through NATs,即簡單的用UDP穿透NAT。

在新的RFC5389修訂中把STUN協議定位於爲穿透NAT提供工具,而不是一個完整的解決方案,英文全稱是Session Traversal Utilities for NAT,即NAT會話穿透效用。RFC5389與RFC3489除了名稱變化外,最大的區別是支持TCP穿透。

TURN,首先在RFC5766中定義,英文全稱是Traversal Using Relays around NAT:Relay Extensions to Session Traversal Utilities for NAT,即使用中繼穿透NAT:STUN的擴展。簡單的說,TURN與STURN的共同點都是通過修改應用層中的私網地址達到NAT穿透的效果,異同點是TURN是通過兩方通訊的“中間人”方式實現穿透。

 

1      STUN    

瞭解STUN之前,我們需要了解NAT的種類。

NAT對待UDP的實現方式有4種,分別如下:

1.    Full Cone NAT

完全錐形NAT,所有從同一個內網IP和端口號發送過來的請求都會被映射成同一個外網IP和端口號,並且任何一個外網主機都可以通過這個映射的外網IP和端口號向這臺內網主機發送包。

2.    Restricted Cone NAT

限制錐形NAT,它也是所有從同一個內網IP和端口號發送過來的請求都會被映射成同一個外網IP和端口號。與完全錐形不同的是,外網主機只能夠向先前已經向它發送過數據包的內網主機發送包。

3.    Port Restricted Cone NAT

端口限制錐形NAT,與限制錐形NAT很相似,只不過它包括端口號。也就是說,一臺IP地址X和端口P的外網主機想給內網主機發送包,必須是這臺內網主機先前已經給這個IP地址X和端口P發送過數據包。

4.    Symmetric NAT

對稱NAT,所有從同一個內網IP和端口號發送到一個特定的目的IP和端口號的請求,都會被映射到同一個IP和端口號。如果同一臺主機使用相同的源地址和端口號發送包,但是發往不同的目的地,NAT將會使用不同的映射。此外,只有收到數據的外網主機纔可以反過來向內網主機發送包。

 

1.1      RFC3489/STUN

STUN(Simple Traversal of User Datagram Protocol Through Network Address Translators),即簡單的用UDP穿透NAT,是個輕量級的協議,是基於UDP的完整的穿透NAT的解決方案。它允許應用程序發現它們與公共互聯網之間存在的NAT和防火牆及其他類型。它也可以讓應用程序確定NAT分配給它們的公網IP地址和端口號。STUN是一種Client/Server的協議,也是一種Request/Response的協議,默認端口號是3478。

1.1.1      報文結構

Ø  消息頭

所有的STUN消息都包含20個字節的消息頭,包括16位的消息類型,16位的消息長度和128位的事務ID。

字節

0                                 1                                 2                                 3

消息類型

消息長度

事務ID

 

 

 

消息類型許可的值如下:

0x0001:捆綁請求

0x0101:捆綁響應

0x0111:捆綁錯誤響應

0x0002:共享私密請求

0x0102:共享私密響應

0x0112:共享私密錯誤響應

消息長度,是消息大小的字節數,但不包括20字節的頭部。

事務ID,128位的標識符,用於隨機請求和響應,請求與其相應的所有響應具有相同的標識符。

Ø  消息屬性

消息頭之後是0或多個屬性,每個屬性進行TLV編碼,包括16位的屬性類型、16位的屬性長度和變長屬性值。

字節

0                                 1                                 2                                 3

屬性類型

屬性長度

屬性值

……

屬性類型定義如下:

MAPPED-ADDRESS

MAPPED-ADDRESS屬性表示映射過的IP地址和端口。它包括8位的地址族,16位的端口號及長度固定的IP地址。

RESPONSE-ADDRESS

RESPONSE-ADDRESS屬性表示響應的目的地址

CHASNGE-REQUEST

客戶使用32位的CHANGE-REQUEST屬性來請求服務器使用不同的地址或端口號來發送響應。

SOURCE-ADDRESS

SOURCE-ADDRESS屬性出現在捆綁響應中,它表示服務器發送響應的源IP地址和端口。

CHANGED-ADDRESS

如果捆綁請求的CHANGE-REQUEST屬性中的“改變IP”和“改變端口”標誌設置了,則CHANGED-ADDRESS屬性表示響應發出的IP地址和端口號。

USERNAME

USERNAME屬性用於消息的完整性檢查,用於消息完整性檢查中標識共享私密。USERNAME通常出現在共享私密響應中,與PASSWORD一起。當使用消息完整性檢查時,可有選擇地出現在捆綁請求中。

PASSWORD

PASSWORD屬性用在共享私密響應中,與USERNAME一起。PASSWORD的值是變長的,用作共享私密,它的長度必須是4字節的倍數,以保證屬性與邊界對齊。

MESSAGE-INTEGRITY

MESSAGE-INTEGRITY屬性包含STUN消息的HMAC-SHA1,它可以出現在捆綁請求或捆綁響應中;MESSAGE-INTEGRITY屬性必須是任何STUN消息的最後一個屬性。它的內容決定了HMAC輸入的Key值。

ERROR-CODE

ERROR-CODE屬性出現在捆綁錯誤響應或共享私密錯誤響應中。它的響應號數值範圍從100到699。

下面的響應號,與它們缺省的原因語句一起,目前定義如下:

400(錯誤請求):請求變形了。客戶在修改先前的嘗試前不應該重試該請求。

401(未授權):捆綁請求沒有包含MESSAGE-INTERITY屬性。

420(未知屬性):服務器不認識請求中的強制屬性。

430(過期資格):捆綁請求沒有包含MESSAGE-INTEGRITY屬性,但它使用過期

的共享私密。客戶應該獲得新的共享私密並再次重試。

431(完整性檢查失敗):捆綁請求包含MESSAGE-INTEGRITY屬性,但HMAC驗

證失敗。這可能是潛在攻擊的表現,或者客戶端實現錯誤

432(丟失用戶名):捆綁請求包含MESSAGE-INTEGRITY屬性,但沒有

USERNAME屬性。完整性檢查中兩項都必須存在。

433(使用TLS):共享私密請求已經通過TLS(Transport Layer Security,即安全

傳輸層協議)發送,但沒有在TLS上收到。

500(服務器錯誤):服務器遇到臨時錯誤,客戶應該再次嘗試。

600(全局失敗):服務器拒絕完成請求,客戶不應該重試。

 

UNKNOWN-ATTRIBUTES

UNKNOWN-ATTRIBUTES屬性只存在於其ERROR-CODE屬性中的響應號爲420的捆綁錯誤響應或共享私密錯誤響應中。

REFLECTED-FROM

REFLECTED-FROM屬性只存在於其對應的捆綁請求包含RESPONSE-ADDRESS屬性的捆綁響應中。屬性包含請求發出的源IP地址,它的目的是提供跟蹤能力,這樣STUN就不能被用作DOS攻擊的反射器。

屬性空間分爲可選部分與強制部分,值超過0x7fff的屬性是可選的,即客戶或服務器即使不認識該屬性也能夠處理該消息;值小於或等於0x7fff的屬性是強制理解的,即除非理解該屬性,否則客戶或服務器就不能處理該消息。

1.1.2      實現原理

 

圖1:STUN

STUN協議的完整交互過程如上,下面我們來介紹具體實現步驟。

一般情況下,客戶會配置STUN服務器提供者的域名,該域名被解析爲IP地址和SRV過程的端口號。服務器名是“stun”,使用UDP協議發送捆綁請求,使用TCP協議發送共享私密請求。STUN協議的缺省端口號爲3478。

若要提供完整性檢查,STUN在客戶和服務器間使用128位的共享私密,作爲在捆綁請求和捆綁響應中的密匙。

首先,客戶通過發現過程獲得它將與之建立TCP連接的IP地址和端口號。客戶打開該地址和端口的連接,開始TLS協商,驗證服務器的標識。客戶發送共享私密請求。該請求沒有屬性,只有頭。服務器生成響應。

客戶會在該連接上生成多個請求,但在獲得用戶名和密碼後關閉該連接。

服務器收到共享私密請求,驗證從TLS連接上到達的該請求;如果不是通過TLS收到的請求,則生成共享私密錯誤響應,並設置ERROR-CODE屬性爲響應號433;這裏區分兩種情況:若通過TCP收到請求,則錯誤響應通過收到請求的相同連接發送;若通過UDP收到請求,則錯誤響應發送回請求送出的源IP和端口。

服務器檢查請求中的任何屬性,當其中有不理解的小於或等於0x7fff的值,則生成共享私密錯誤響應,設置ERROR-CODE屬性爲響應號420,幷包括UNKNOWN-ATTRIBUTE屬性,列出它不理解的小於或等於0x7fff的屬性的值。該錯誤響應通過TLS連接發送。

若請求正確,服務器創建共享私密響應,包含與請求中相同的事務ID,幷包含USERNAME和PASSWORD屬性。用戶名在10分鐘內有效。

共享私密響應通過與收到請求的相同的TLS連接發送,服務器保持連接打開狀態,由客戶關閉它。

接着,客戶發送捆綁請求,攜帶的屬性包括:

*        可選屬性:RESPONSE-ADDRESS屬性和CHANGE-REQUEST屬性;

*        強制屬性:MESSAGE-INTEGRITY屬性和USERNAME屬性。

客戶發送捆綁請求,通過客戶重傳來提供可靠性。客戶開始用100ms的間隔重傳,每次

重傳間隔加倍,直至1.6秒。之間間隔1.6秒的重傳繼續,直到收到響應或總共已經發送了9次。因此,若9500ms後,還未收到響應,客戶認爲傳輸已經失敗。

服務器檢查捆綁請求的MESSAGE-INTEGRITY屬性,不存在則生成捆綁錯誤響應,設置ERROR-CODE屬性爲響應號401;若存在,計算請求的HMACKey值。

服務器檢查USERNAME屬性,不存在則生成捆綁錯誤響應,設置ERROR-CODE屬性爲響應號432;若存在,但不認識該USERNAME的共享私密(例如,它超時了),生成捆綁錯誤響應,設置ERROR-CODE屬性爲響應號430。

若服務器知道該共享私密,但所計算的HMAC與請求的不同,生成捆綁錯誤響應,設置ERROR-CODE屬性爲響應號431。

假設消息完整性檢查通過了,服務器檢查請求中的任何屬性的值,若遇到不理解的小於或等於0x7fff的值,生成捆綁錯誤響應,設置ERROR-CODE屬性爲響應號420,該響應包含UNKNOWN-ATTRIBUTE屬性,並列出不理解的小於或等於0x7fff的屬性。

若請求正確,服務器生成單個捆綁響應,包含與捆綁請求相同的事務ID。服務器在捆綁響應中加入MAPPED-ADDRESS屬性,該屬性的IP地址和端口號爲捆綁請求的源IP地址和端口號。

捆綁響應的源地址和端口號取決於捆綁請求中CHANGE-REQUEST屬性的值及捆綁請求收到的地址和端口號相關。總結如下:

標誌

源地址

源端口號

CHANGED-ADDRESS

Da

Dp

Ca:Cp

改變IP

Ca

Dp

Ca:Cp

改變端口號

Da

Cp

Ca:Cp

改變IP且改變端口號

Ca

Cp

Ca:Cp

表1:標誌對數據包源和CHANGED-ADDRESS的影響

服務器在捆綁響應中加入SOURCE-ADDRESS屬性,包含用於發送捆綁響應的源地址和端口號;加入CHANGED-ADDRESS屬性,包含源IP地址和端口號。

如果捆綁請求中包含了USERNAME和MESSAGE-INTEGRITY屬性,則服務器在捆綁響應中加入MESSAGE-INTEGRITY屬性。

如果捆綁請求包含RESPONSE-ADDRESS屬性,則服務器在捆綁響應中加入REFLECTED-FROM屬性:如果捆綁請求使用從共享私密請求獲得的用戶名進行認證,則REFLECTED-FROM屬性包含共享私密請求到達的源IP地址和端口號;若請求中的用戶名不是使用共享私密分配的,則REFLECTED-FROM屬性包含獲得該用戶名的實體的源IP地址和端口號;若請求中沒有用戶名,且服務器願意處理該請求,則REFLECTED-FROM屬性包含請求發出的源IP地址和端口號。

服務器不會重傳響應,可靠性通過客戶週期性地重發請求來保障,每個請求都會觸發服務器進行響應。

客戶端判斷響應的類型是捆綁錯誤響應還是捆綁響應。捆綁錯誤響應通常在請求發送的源地址和端口收到;捆綁響應通常在請求中的RESPONSE-ADDRESS屬性的地址和端口收到,若沒有該屬性,則捆綁響應將在請求發送的源地址和端口號收到。

*        若是捆綁錯誤響應,客戶檢查響應中的ERROR-CODE屬性的響應號:400至499之間的未知屬性按屬性400處理,500至599之間的未知屬性按500處理,600至699之間的未知屬性按600處理。任何100和399之間的響應都會使請求重傳中止,但其他則忽略;若客戶收到響應的屬性類型大於0x7fff,則忽略該屬性,若小於或等於0x7fff,則請求重傳停止,並忽略整個響應;

*        若是捆綁響應,客戶檢查響應的MESSAGE-INTEGRITY屬性:如果不存在,客戶在請求中加入MESSAGE-INTEGRITY屬性,並放棄該響應;如果存在,客戶計算響應的HMAC。如果計算出的HMAC與響應中的不同,則放棄該響應,並警告客戶可能受到了攻擊;若計算出的HMAC與響應中的匹配,則過程繼續;

*        不論收到捆綁響應還是捆綁錯誤響應,都將中止該請求的重傳。客戶在第一次響應後繼續監聽捆綁請求的響應10秒鐘,如果這期間它收到任何消息類型不同的響應或不同的MAPPED-ADDRESS屬性,它將警告用戶可能受到攻擊;並且,如果客戶收到的捆綁響應次數超過它發送的捆綁請求數的兩倍,它將警告用戶可能受到攻擊;若捆綁響應經過認證,上述攻擊並未導致客戶丟棄MAPPED-ADDRESS,則客戶可以使用該MAPPED-ADDRESS和SOURCE-ADDRESS屬性。

1.1.3      STUN功能舉例

客戶通過帶外方式獲得STUN服務器信息後,就打開對應的地址和端口的連接,並開始與STUN服務器進行TLS協商。一旦打開了連接,客戶就通過TCP協議發送共享私密請求,服務器生成共享私密響應。STUN在客戶和服務器間使用共享私密,用作捆綁請求和捆綁響應中的密匙。之後,客戶使用UDP協議向STUN服務器發送捆綁請求,當捆綁請求消息到達服務器的時候,它可能經過了一個或者多個NAT。結果是STUN服務器收到的捆綁請求消息的源IP地址被映射成最靠近STUN服務器的NAT的IP地址,STUN服務器把這個源IP地址和端口號複製到一個捆綁響應消息中,發送回擁有這個IP地址和端口號的客戶端。

當STUN客戶端收到捆綁響應消息之後,它會將自己發送捆綁請求時綁定的本地IP地址和端口號同捆綁響應消息中的IP地址和端口號進行比較,如果不匹配,就表示客戶端正處於一個或者多個NAT的前面。

在Full-Cone NAT的情況下,在捆綁響應消息中的IP地址和端口是屬於公網的,公網上的任何主機都可以使用這個IP地址和端口號向這個應用程序發送數據包,應用程序只需要在剛纔發送捆綁請求的IP地址和端口上監聽即可。

當然,客戶可能並不在一個Full-Cone NAT的前面,實際上,它並不知道自己在一個什麼類型的NAT的前面。爲了確定NAT的類型,客戶端使用附加的捆綁請求。具體過程是很靈活的,但一般都會像下面這樣工作:客戶端再發送一個捆綁請求,這次發往另一個IP地址,但是使用的是跟上一次同一個源IP地址和源端口號,如果返回的數據包裏面的IP地址和端口號和第一次返回的數據包中的不同,客戶端就會知道它是在一個對稱NAT的前面。客戶端爲了確認自己是否在一個完全錐形NAT的前面,客戶端可以發送一個帶有標誌的捆綁請求,這個標誌告訴服務器使用另一個IP地址和端口發送捆綁響應。換句話說,如果客戶端使X/Y的IP地址端口對向A/B的IP地址端口對發送捆綁請求,服務器就會使用源IP地址和源端口號爲C/D的地址端口對向X/Y發送捆綁響應。如果客戶端收到了這個響應,它就知道它是在一個Full-Cone NAT前面。

STUN協議允許客戶端請求服務器從收到捆綁請求的IP地址往回發捆綁響應,但是要使用不同的端口號。這可以用來檢查客戶端是否在Port Restricted Cone NAT的前面還是在Restricted Cone NAT的前面。

 

1.2      RFC5389/STUN

STUN協議在RFC5389中被重新命名爲Session Traversal Utilities for NAT,即NAT會話穿透效用。在這裏,NAT會話穿透效用被定位爲一個用於其他解決NAT穿透問題協議的協議。它可以用於終端設備檢查由NAT分配給終端的IP地址和端口號。同時,它也被用來檢查兩個終端之間的連接性,好比是一種維持NAT綁定表項的保活協議。STUN可以用於多種NAT類型,並不需要它們提供特殊的行爲。

STUN本身不再是一種完整的NAT穿透解決方案,它相當於是一種NAT穿透解決方案中的工具。這是與RFC3489/STUN版本相比最重要的改變。

1.2.1      STUN用途

目前定義了三種STUN用途:

Interactive Connectivity Establishment(ICE)[MMUSIC-ICE],交互式連接建立

Client-initiated connections for SIP [SIP-OUTBOUND],用於SIP的客戶端初始化連接

NAT Behavior Discovery [BEHAVE-NAT],NAT行爲發現

      

1.2.2      報文結構

Ø  消息頭

STUN消息頭爲20字節,後面緊跟0或多個屬性。STUN頭部包含一STUN消息類型、magic cookie、事務ID和消息長度。

0                                 1                                 2                                 3

00

STUN消息類型

消息長度

魔術字

事務ID(96位)

 

每個STUN消息的最高位前2位必須爲0。當STUN協議爲多個協議多路複用時若使用的是同一個端口,這可以用於與其他協議區分STUN數據包。

消息類型確定消息的類別(如請求、成功迴應、失敗迴應、標誌)。雖然這裏有四種消息類型,但可以分爲2類事務:請求/響應事務、標誌事務。

消息類型字段可進一步劃分爲下面結構:

M11

M10

M9

M8

M7

C1

M6

M5

M4

C0

M3

M2

M1

M0

消息類型定義如下:

0b00,表示請求

0b01,表示標誌

0b10,表示成功響應

0b11,表示錯誤響應

魔術字域必須包含固定的值0x2112A442。在RFC3489中,該域是事務ID的一部分。配置魔術字允許服務器檢測客戶是否理解某些在改進的版本中增加的屬性。另外,還可用於STUN多路複用時與其他協議的包進行區分。

96位的事務ID用於唯一的識別STUN事務。對於請求/響應事務,事務ID由STUN客戶端來選擇;對於標誌事務,由代理(代理指支持STUN的客戶端或服務器)來選擇併發送。它主要服務於與請求相關的響應,因此它也扮演着一個幫助阻止確定類型的攻擊的角色。服務器使用事務ID來唯一的標識出所有客戶端的每一個事務。事務ID本身必須是唯一的,並且隨機的從0到2的96-1次方中選擇。重新發送相同的請求時,也必須使用新的事務ID。成功或錯誤響應必須攜帶與相對應的請求相同的事務ID。

消息長度字段不包括20字節的STUN頭部。所有的STUN屬性必須填充爲4字節的倍數。消息長度字段的最後2位總是爲0,這爲區分STUN包與其他協議的包提供了另外一種方法。

Ø  消息屬性

STUN頭之後是0或多個屬性。每個屬性都採用TLV編碼,16位的類型、16位的長度及可變長度的值。每個STUN屬性必須是4字節邊界對齊。

字節

0                                 1                                 2                                 3

屬性類型

屬性長度

屬性值

……

 

屬性空間被劃分爲2個範圍。屬性的類型值在0x0000到0x7fff是強制理解屬性,這意味着除非STUN代理能夠理解這些屬性,否則將不能正常處理包含該屬性的消息;屬性的類型值在0x8000到0xffff範圍是可選理解屬性,這意味着如果STUN代理不能理解它們的話這些屬性可以被忽略。

STUN屬性類型集由IANA維護。

MAPPED-ADDRESS

MAPPED-ADDRESS屬性標識了客戶端反向傳輸地址(映射後的地址),這個屬性只用於服務器向後兼容RFC3489的客戶端。

XOR-MAPPED-ADDRESS

XOR-MAPPED-ADDRESS屬性與MAPPED-ADDRESS屬性是相同的,除了這映射後的地址經過了異或處理。(注意,異或運算是其自身的逆運算,再異或一下就可以得出真實的MAPPED-ADDRESS)

USERNAME

USERNAME屬性用於消息完整性。它採用USERNAME和PASSWORD組合方式用於消息完整性檢查。

MESSAGE-INTEGRITY

MESSAGE-INTEGRITY屬性包含STUN消息的HMAC-SHA1。它可以出現在任何類型的STUN消息中。由於使用SHA1散列算法,HMAC將會是20字節。用作HMAC輸入的文本是STUN消息,包括頭部,直到且包括MESSAGE-INTEGRITY屬性前面的屬性。除了FINGERPRINT屬性外,代理必須忽略其他出現在MESSAGE-INTEGRITY屬性後的任何屬性。

STUN消息頭中的長度字段的值必須包括直到MESSAGE-INTEGRITY屬性本身,但不包括任何在它之後的屬性。

FINGERPRINT

FINGERPRINT屬性可以存在於所有的STUN消息中,提供輔助區分STUN數據包與其他協議數據包的功能。屬性的值爲採用CRC32方式計算STUN消息直到但不包括FINGERPRINT屬性的的結果,並與32位的值0x5354554e異或。

ERROR-CODE

ERROR-CODE屬性被用於錯誤響應消息中。它包含一個在300至699範圍內的錯誤響應號。錯誤響應號定義如下:

300:嘗試代替,客戶端應該使用該請求聯繫一個代替的服務器。這個錯誤響應僅在請求包括一個USERNAME屬性和一個有效的MESSAGE-INTEGRITY屬性時發送;否則它不會被髮送,而是發送錯誤代碼爲400的錯誤響應;

400:錯誤請求,請求是變形了,客戶在修改先前的嘗試前不應該重試該請求。

401:未授權,請求未包括正確的資格來繼續。客戶應該採用一個合適的資格來重試該請求。

420:未知屬性,服務器收到一個STUN包包含一個強制理解的屬性但是它不會理解。服務器必須將不認識的屬性放在錯誤響應的UNKNOWN-ATTRIBUTE屬性中。

438:過期Nonce,客戶使用的Nonce不再有效,客戶應該使用響應中提供的Nonce來重試。

500:服務器錯誤,服務器遇到臨時錯誤,客戶應該再次嘗試。

REALM

REALM屬性可能出現在請求和響應中。在請求中表示長期資格將在認證中使用。當在錯誤響應中出現表示服務器希望客戶使用長期資格來進行認證。

NONCE

NONCE屬性可能出現在請求和響應消息中。

UNKNOWN-ATTRIBUTES

UNKNOWN-ATTRIBUTES屬性只在錯誤代碼爲420的的錯誤響應中出現。

SOFTWARE

SOFTWARE屬性用於代理髮送消息時包含版本的描述。它用於客戶端和服務器。它的值包括製造商和版本號。該屬性對於協議的運行沒有任何影響,僅爲診斷和調試目的提供服務。SOFTWARE屬性是個可變長度的,採用UTF-8編碼的小於128個字符的序列號。

ALTERNATE-SERVER     

ALTERNATE-SERVER屬性標識一個備份的傳輸地址表明一個STUN客戶可以嘗試的不同的STUN服務器。屬性格式與MAPPED-ADDRESS相同。IP地址族必須與請求的源IP地址的相同。

1.3      RFC5389與RFC3489的區別

RFC5389與RFC3489的不同點如下:

*        去掉STUN是一種完整的NAT穿透方案的概念,現在是一種用於提供NAT穿透解決方案的工具。因而,協議的名稱變爲NAT會話穿透效用;

*        定義了STUN的用途;

*        去掉了STUN關於NAT類型檢測和綁定生命期發現的用法,去掉了RESPONSE-ADDRESS、CHANGED-ADDRESS、CHANGE-REQUEST、SOURCE-ADDRESS和REFLECTED-FROM屬性;

*        增加了一個固定的32位的魔術字字段,事務ID字段減少了32位長度;

*        增加了XOR-MAPPED-ADDRESS屬性,若魔術字在捆綁請求中出現時,該屬性包括在捆綁響應中。否則,RFC3489中的行爲是保留的(換句話說,捆綁響應中包括MAPPED-ADDRESS);

*        介紹了消息類型字段的正式結構,帶有一對明確的位來標識Request、Response、Error-Response或Indication消息。因此,消息類型字段被劃分爲類別和方法兩部分;

*        明確的指出了STUN的最高2位是0b00,當用於ICE時可以簡單的與RTP包區分開來;

*        增加指紋屬性來提供一種明確的方法來檢測當STUN協議多路複用時,STUN與其他協議之間的差異;

*        增加支持IPv6,IPv4客戶端可以獲取一個IPv6映射地址,反之亦然;

*        增加一個long-term-credential-based認證機制;

*        增加了SOFTWARE、REALM、NONCE和ALTERNATE-SERVER屬性;

*        去掉了共享密匙方法,因此PASSWORD屬性也去掉了;

*        去掉了使用連續10秒偵聽STUN響應來識別一個攻擊的做法;

*        改變事務計時器來增加TCP友好性;

*        去掉了STUN例子如集中分離控制和媒體面,代替的,在使用STUN協議時提供了更多的信息;

*        定義了一類填充機制來改變長度屬性的說明;

*        REALM、SERVER、原因語句和NONCE限制在127個字符,USERNAME限制在513個字節以內;

*        爲TCP和TLS改變了DNS SRV規程,UDP仍然和以前保持一致;

1.4      新特性介紹

1.4.1      指紋機制

FINGERPRINT機制是一種可選的用於其他協議多路複用STUN時發送給相同的傳輸地址時區分STUN數據包的機制,該機制不支持與RFC3489相兼容。

在一些用途中,基於相同的傳輸地址時多個協議會多路複用STUN消息,例如RTP協議。STUN消息必須首先和應用報文分離開。目前,在STUN報頭中有3種固定的字段可以用於該目的。儘管如此,在一些案例中,三種固定字段仍然不能充分的區別開。

當擴展的指紋機制被使用時,STUN代理在發送給其他STUN代理的消息中包括FINGERPRINT屬性。當其他STUN代理收到時,除基本的檢查之外,還將檢查是否包含FINGERPRINT屬性及它是否包含正確的值,至此,它將相信這是一個STUN消息。指紋機制幫助STUN代理檢查其他協議那些看起來像是STUN消息的消息。

1.4.2      通過DNS發現服務器機制

STUN客戶端可以使用DNS來發現STUN服務器的IP地址和端口。客戶端必須知道服務器的域名。

當客戶端希望找出服務器在公網上的位置就採用捆綁請求/響應事務,SRV(資源記錄表)中服務器名稱是“stun”。當通過TLS會話採用捆綁請求/響應事務,SRV中服務器名稱爲“stuns”。STUN用戶可以定義額外的DNS資源記錄服務名稱。

STUN請求的默認端口是3478,用於TCP和UDP。STUN在TLS上的默認端口是5349。服務器能夠在TLS上運行STUN與STUN在TCP上時使用相同的端口,只有服務器軟件支持決定初始消息是否是TLS或STUN消息。

如果SRV中沒有記錄可查,客戶端執行A或AAAA記錄查找域名。結果將會是1張IP地址表,每一個都可以使用TCP或UDP採用默認端口號連接。通常要求使用TLS,客戶端使用STUN在TLS上的默認端口號連接其中一個IP地址。

1.4.3      認證和消息完整性機制

短期證書機制

短期證書機制假設在STUN事務之前,客戶端和服務器已經使用了其他協議來交換了證書,以username和password形式。這個證書是有時間限制的。

例如,在ICE用途中,兩個終端使用帶外方式交換信息來對username和password達成一致,並在媒體會話期間使用。

這個證書被用來進行消息完整性檢查,用於每個請求和多個響應中。與長期證書機制相比,沒有挑戰和響應方式,因此,這種證書的時間限制特性的優點是可以阻止重播。

 

長期證書機制

長期證書機制依賴於一個長期證書,username和password在客戶端和服務器中是共用的。這個證書從它提供給 用戶開始將一直是有效的,直到該用戶不再是該系統的用戶。

這本質上是一個提供給用戶username和password的傳統的登入方式。    

客戶端初始發送一個請求,沒有提供任何證書和任何完整性檢測。服務器拒絕這個請求,並提供給用戶一個範圍(用於指導用戶或代理選擇username和password)和一個nonce。這個nonce提供重放保護。它是一個cookie,由服務器選擇,以這樣一種方式來標示有效時間或客戶端身份是有效的。客戶端重試這個請求,這次包括它的username和realm和服務器提供的nonce來回應。服務器確認這個nonce和檢查這個message integrity。如果它們匹配,請求則通過認證。如果這個nonce不再有效,即過期了,服務器就拒絕該請求,並提供一個新的nonce。

在隨後的到同一服務器的請求,客戶端重新使用這個nonce、username和realm,和先前使用的password。這樣,隨後的請求不會被拒絕直到這個nonce變成無效的。

需要注意的是,長期證書機制不能用來保護Indications,由於Indications不能被改變,因此,使用Indications時要麼使用短期證書,要麼就省略認證和消息完整性。

因爲長期證書機制對離線字典攻擊敏感,部署的時候應該使用很難猜測的密碼。

1.4.4      備份服務器機制

服務器使用增強的重定向功能將一個客戶端轉向另一個服務器,通過迴應一個錯誤響應號爲300(嘗試備份)的錯誤響應。服務器在錯誤響應中攜帶一個ALTERNATE-SERVER屬性。

客戶端收到錯誤響應號爲300的錯誤響應後,在該響應中查找ALTERNATE-SERVER屬性。若找到一個,客戶端就會將當前的事務作廢,並重新嘗試發送請求到該屬性中列出的服務器。請求報文若已經通過認證,則必須使用與先前發送給執行重定向操作的服務器同樣的證書。如果客戶端在最後5分鐘裏已經重試發送請求時已經重定向到了一個服務器,它必須忽略重定向操作並將當前的事務作廢,這是爲了防止無限的重定向循環。

1.5      RFC5389與RFC3489的兼容

在RFC3489中:

*        UDP是唯一支持的傳輸協議

*        RFC5389中的魔術字字段是RFC3489中事務ID的一部分,事務ID長128位

*        沒有XOR-MAPPED-ADDRESS屬性,綁定方法是使用MAPPED-ADDRESS屬性代替

*        有3個需要強制理解的屬性,分別是:RESPONSE-ADDRESS、CHANGE-REQUEST、CHANGED-ADDRESS屬性,而RFC5389中不再支持這些屬性。

1.5.1      客戶端處理的改變

客戶端想要與RFC3489的服務器互操作,應發送一個使用綁定方法的請求消息,不包含任何消息,使用UDP協議發送給服務器。如果成功,將收到服務器發回的包含MAPPED-ADDRESS屬性而不是XOR-MAPPED-ADDRESS屬性的成功響應。客戶端試圖與基於RFC3489的應用服務器互操作必須準備好接收任意一個屬性。此外,客戶端必須忽略任何在響應中出現的保留的強制理解的屬性。RFC3489中規定保留屬性中的0x0002、0x0004、0x0005和0x000B可能出現在綁定響應中。

1.5.2      服務器處理的改變

服務器能夠察覺由RFC3489中的客戶端發送的攜帶有不正確的魔術字的捆綁請求消息。當服務器察覺到RFC3489中的客戶端,它應該將捆綁請消息中魔術字域中的值拷貝到捆綁響應中的魔術字字段中,並且插入一個MAPPED-ADDRESS屬性代替XOR-MAPPED-ADDRESS屬性。

客戶端在極少的環境下可能包括RESPONSE-ADDRESS或CHANGE-REQUEST屬性中的一個。在這些情況下,服務器把這些屬性看做是一個不認識的強制理解的屬性,並回應一個錯誤響應。

RFC3489版本中的STUN缺少魔術字和指紋屬性這兩種能夠高可靠性的正確標識其他協議多路複用時的STUN消息。因此,STUN執行與RFC3489兼容時不應該被用於多個協議。

 

2      TURN

2.1      RFC5766/TURN

TURN,在RFC5766中定義,英文全稱Traversal Using Relays around NAT(TURN):Relay Extensions to Session Traversal Utilities for NAT(STUN),即使用中繼穿透NAT:STUN的中繼擴展。簡單的說,TURN與STUN的共同點都是通過修改應用層中的私網地址達到NAT穿透的效果,異同點是TURN是通過兩方通訊的“中間人”方式實現穿透。

如果一個主機位於NAT的後面,在某些情況下它不能夠與其他主機點對點直接連接。在這些情況下,它需要使用中間網點提供的中繼連接服務。TURN協議就是用來允許主機控制中繼的操作並且使用中繼與對端交換數據。TURN與其他中繼控制協議不同的是它能夠允許一個客戶端使用一箇中繼地址與多個對端連接。

TURN協議被設計爲ICE的一部分,用於NAT穿越,雖然如此,它也可以在沒有ICE的地方單獨使用。

2.1.1      操作概述

圖2:TURN

在一個典型組網中,一個TURN客戶端連接在一個私有網絡中,通過一個或多個NAT來連接到公網。在公網中有一個TURN服務器。在因特網的別處有一個或多個對端是這個TURN客戶端希望通訊的。這些對端也有可能是在一個或多個NAT的後面。該客戶端使用服務器作爲一箇中繼來發送數據包 到這些對端去,並且從這些對端接收數據包。

客戶端通過一個IP地址和端口的組合來與服務器建立會話。客戶端使用TURN命令在服務器上創建和操作一個ALLOCATION。一旦這個allocation創建好了,客戶端能夠在數據發往哪個對端的指示下發送應用數據到這個服務器,服務器將中繼這些數據到合適的對端。客戶端發送的應用數據包含在TURN消息中,服務器將數據提取出來,並以UDP數據包方式發送給對端。反向上,對端以UDP數據包方式發送應用數據到這個allocation提供的中繼傳輸地址。因爲TURN消息總是包含客戶端與哪些對端通訊的指示,客戶端能夠使用單一的allocation來與多個對端通訊。

2.1.2      術語

TURN client:遵循RFC5766的STUN客戶端。

TURN server:遵循RFC5766的STUN服務器。

Peer:TURN客戶端希望連接的主機。TURN服務器爲TURN客戶端和它的對端中繼流量,但Peer並不與TURN服務器使用TURN協議進行交互,它接收從TURN服務器發送過來的數據,並向TURN服務器發送數據。

Transport Address:IP地址與端口號的組合。

Host Transport Address:客戶端或對端的傳輸地址。

Server-Reflexive Transport Address:NAT公網側的傳輸地址,該地址由NAT分配,相當於一個特定的主機傳輸地址。

Relayed Transport Address:TURN服務器上的傳輸地址,用於客戶端和對端中繼數據。

TURN Server Transport Address:TURN服務器上的傳輸地址,用於客戶端發送STUN消息給服務器。

Peer Transport Address:服務器看到的對端的傳輸地址,當對端是在NAT後面,則是對端的服務器反射傳輸地址。

Allocation:通過Allocate請求將中繼傳輸地址提供給客戶端,除了中繼狀態外,還有許可和超時定時器等。

5-tuple:五元組,包括客戶端IP地址和端口,服務器IP地址和端口和傳輸協議(包括UDP、TCP、TLS)的組合。

Channel:通道號與對端傳輸地址的關聯,一旦一個通道號與一個對端的傳輸地址綁定,客戶端和服務器就能夠利用帶寬效應更大的通道數據消息來交換數據。

Permission:一個對端允許使用它的IP地址和傳輸協議來發送數據到TURN服務器,服務器只爲從對端發來的並且匹配一個已經存在的許可的流量中繼到相應的客戶端。

Realm:服務器內用於描述服務器或內容的一個字符串,這個realm告訴客戶端哪些用戶名和密碼的組合可用於認證請求。

Nonce:服務器隨機選擇的一個字符串,包含在報文摘要中。爲了防止中繼攻擊,服務器應該有規律的改變這個nonce。

2.1.3      新的STUN方法

下面給出了新的STUN方法的編號:

0x003    Allocate

0x004    Refresh

0x006    Send

0x007    Data

0x008    CreatePermission

0x009    ChannelBind

2.1.4      新的STUN屬性

0x000c   CHANNEL-NUMBER

0x000D  LIFETIME

0x0010  Reserved      (was BANDWIDTH)

0x0012  XOR-PEER-ADDRESS

0x0013  DATA

0x0016 XOR-RELAYED-ADDRESS

0x0018  EVEN-PORT

0x0019  REQUESTED-TRANSPORT

0x001A  DON’T-FRAGMENT

0x0021  Reserved      (was TIMER-VAL)

0x0022  RESERVATION-TOKEN

上面屬性中的部分屬性長度不是4字節的倍數,採用STUN的規則,使用1~3個padding字節來補齊。

CHANNEL-NUMBER

       CHANNEL-NUMBER屬性包含通道的號碼。屬性長4字節,包含16比特的無符號整數和2字節的RFFU(Reserved For Future Use)字段,該字段必須設爲0且在接收時被忽略。

字節

0                                 1                                 2                                 3

Channel Number

RFFU=0

LIFETIME

LIFETIME屬性表示服務器在沒有收到refresh時維持一個allocation的持續時間。屬性長4字節,包含一個32比特的無符號整數值,表示剩餘多少秒終止

XOR-PEER-ADDRESS

XOR-PEER-ADDRESS指定從TURN服務器看到的對端的地址和端口,例如如果對端是在一個NAT後面,則爲對端的server-reflexive傳輸地址。

DATA

DATA屬性存在於所有的Send和Data indications消息中。屬性的值是可變長度的,包括應用數據。如果屬性的長度不上4字節的倍數,必須進行填充。

XOR-RELAYED-ADDRESS

XOR-RELAYED-ADDRESS存在於所有的Allocate響應中。它指定了服務器分配給客戶端的地址和端口。

字節

0                                 1                                 2                                 3

00000000

地址族

端口號

IP地址(32位或128位)

EVEN-PORT

這個屬性允許客戶端請求在中繼傳輸地址的端口爲偶數,並且服務器可選的保留緊跟着的下一個端口號。屬性的值長1字節,結構如下:

字節

0                                

R

RFFU

值包括一個1比特標誌字段:

R:如果爲1,服務器被請求保留下一個更高的端口號(基於同一個IP地址)爲隨後的allocation。如果爲0,則不請求保留。屬性的其他7比特值必須設置爲0,並且在接收時被忽略。因爲屬性不是4字節的倍數,必須進行填充。

REQUESTED-TRANSPORT

客戶端通過該屬性爲已分配的傳輸地址請求一個特定的傳輸協議。屬性的值是4字節長度的。

字節

0                                 1                                 2                                 3

Protocol

RFFU

協議字段指定了需求的協議。可以取自IPv4報頭中的協議字段的值或IPv6報頭的下一個報頭字段的協議號。目前僅允許設置爲17,即UDP。RFFU字段在傳輸時必須設置爲0,並在接收時被忽略。保留用於未來使用。

DON’T-FRAGMENT

客戶端使用該屬性來請求服務器設置IP報頭中的DF(不要分片)位,當中繼應用數據到對端時。該屬性沒有值,因此屬性長度字段爲0。

RESERVATION-TOKEN

RESERVATION-TOKEN屬性包含一個token來唯一的標識一箇中繼傳輸地址已經被服務器保留。服務器在一個成功響應中包含該屬性來告訴客戶端這個token,客戶端在接下來的Allocate請求中包括該屬性來請求服務器爲這個allocation使用那個中繼傳輸地址。屬性值是8字節長。

2.1.5      新的STUN錯誤響應號

403(Forbidden):請求是有效的,但因管理或類似的規定而不能被執行。

437(Allocation Mismatch):服務器接收到請求,要求在適當的位置的allocation,但沒有allocation存在,或者一個收到的請求不指定任何allocation,但是一個allocation存在。

441(Wrong Credentials):請求中的證書沒有匹配那些用來創建allocation的證書

442(不支持的傳輸協議):Allocate請求要求服務器使用一個用於服務器和對端的傳輸協議但是該服務器不支持該傳輸協議。

486(Allocation Quota Reached):目前沒有更多的allocations資源使用相同的用戶名可被創建。

508(Insufficient Capacity):服務器不能夠完成請求因爲一些性能限制已經達到上限。在一個Allocate響應中,這可能因爲服務器此時已經沒有更多的中繼傳輸地址資源了,沒有更多的被請求的性能,或者相當於特定的保留的token不可用。

 

2.1.6      協議交互過程詳細舉例

以圖2爲例進行講解,每個消息中,多個屬性包含在消息中並顯示它們的值。爲了方便閱讀,值以人們可讀的格式來顯示。

客戶端使用10.1.1.2:49271作爲傳輸地址向服務器的傳輸地址發送Allocate請求。客戶端隨機選擇一個96位的事務ID。該Allocate請求消息包括SOFTWARE屬性來提供客戶端的軟件版本信息;包括LIFETIME屬性,指明客戶端希望該allocation具有1小時的生命期而非缺省的10分鐘;包括REQUESTED-TRANSPORT屬性來告訴服務器與對端之間採用UDP協議來傳輸;包括DONT-FRAGMENT屬性因爲客戶端希望在隨後的Send indications中使用DON’T-FRAGMENT屬性。

服務器需要任何請求必須是經過認證的,因此服務器拒絕了該最初的Allocation請求,並且迴應了攜帶有錯誤響應號爲401(未授權)的Allocate錯誤響應;該響應包括一個REALM屬性,指明認證的域;還包括一個NONCE屬性和一個SOFTWARE屬性。

客戶端收到了錯誤響應號爲401的Allocate錯誤響應,將重新嘗試發送Allocate請求,此時將包括認證屬性。客戶端在新的請求中重新選擇一個新的事務ID。客戶端包括一個USERNAME屬性,使用從服務器那收到的realm值來幫助它決定使用哪個值;請求還包括REALM和NONCE屬性,這兩個屬性是從收到的錯誤響應中拷貝出來的。最後,客戶端包括一個MESSAGE-INTEGRITY屬性。

服務器收到認證的Allocate請求後,檢查每個屬性是否正確;然後,產生一個allocation,並給客戶端迴應Allocate成功響應。服務器在該成功響應中攜帶一個LIFETIME屬性,本例中服務器將客戶端請求的1小時生命期減小爲20分鐘,這是因爲這個特定的服務器可能不允許超過20分鐘的生命期;該響應包括XOR-RELAYED-ADDRESS屬性,值爲該allocation的中繼傳輸地址;該響應還包括XOR-MAPPED-ADDRESS屬性,值爲客戶端的server-reflexive地址;該響應也包含一個SOFTWARE屬性;最後,包括一個MESSAGE-INTEGRITY屬性來證明該響應,確保它的完整性;

 

接着,客戶端爲了準備向對端A發送一些應用數據而創建一個permission。這裏通過一個CreatePermission請求來做到。該請求攜帶XOR-PEER-ADDRESS屬性包含有確定的請求的IP地址,這裏爲對端A的地址;需要注意的是,屬性中地址的端口號被設置爲0在CreatePermission請求中,並且客戶端使用的是對端A的server-reflexive地址而不是它的主機地址(私網地址);客戶端在該請求中攜帶與之前的Allocate請求中一樣的username、realm和nonce值,因此該請求被服務器認可。此時在該請求中,客戶端沒有攜帶SOFTWARE屬性。

服務器收到該CreatePermission請求,產生一個相應的許可,並以CreatePermission成功響應來回應。該響應中只包含了Transaction-ID和MESSAGE-INTEGRITY屬性。

現在客戶端使用Send indication來發送應用數據到對端A。對端的server-reflexive傳輸地址包含在XOR-PEER-ADDRESS屬性中,應用數據包含在DATA屬性中。客戶端已經在應用層上執行了路徑MTU發現功能,因此通過DON’T-FRAGMENT屬性來告知服務器當通過UDP方式來向對端發送數據時應設置DF位。Indications不能使用長期證書機制來認證,所以該消息中沒有MESSAGE-INTEGRITY屬性。

服務器收到Send indication後,提取出應用數據封裝成UDP格式發給對端A;UDP報文的源傳輸地址爲中繼傳輸地址,並設置DF位。

對端A迴應它自己的包含有應用數據的UDP包給服務器。目的地址爲服務器的中繼傳輸地址。當服務器收到後,將生成Data indication消息給客戶端,攜帶有XOR-PEER-ADDRESS屬性。應用數據包含在DATA屬性中。

客戶端現在若要綁定一個通道到對端B,將指定一個空閒的通道號(本例中爲0x4000)包含在CHANNEL-NUMBER屬性中,對端B的傳輸地址包含在XOR-PEER-ADDRESS屬性中。與以前一樣,客戶端再次利用上次請求中的username、realm和nonce。

當服務器收到該請求後,服務器綁定這個對端的通道號,爲對端B的IP地址安裝一個permission,然後給客戶端迴應一個ChannelBind成功響應消息。

客戶端現在發送一個ChannelData消息給服務器,攜帶有發送給對端B的數據。這個消息不是一個STUN消息,因此沒有事務ID。它之有3個字段:通道號、數據、數據長度;服務器收到後,檢查通道號後發現當前已經綁定了,就以UDP方式發送數據給對端B。

接着,對端B發送UDP數據包迴應給服務器的中繼傳輸地址。服務器收到後,迴應給客戶端ChannelData消息,包含UDP數據包中的數據。服務器知道是給哪個客戶端發送ChannelData消息,這是因爲收到的UDP數據包中的目的地址(即服務器的中繼傳輸地址),並且知道使用的是哪個通道號,這是因爲通道已經與相應的傳輸地址綁定了。

有時候,20分鐘的生命期已經到了,客戶端需要刷新allocation。此時通過發送Refresh請求來進行。該請求包含最後一次使用的username、realm和nonce,還包含SOFTWARE屬性。當服務器收到這個Refresh請求時,它注意到這個nonce值已經超期了,則給客戶端迴應一個錯誤響應號爲438(過期Nonce)的Refresh錯誤響應,並提供一個新的nonce值。可護端將重試該請求,此時攜帶新的nonce值。若第二次嘗試被接受,服務器將回應一個成功響應。需要注意的是,此時客戶端在請求中沒有攜帶LIFETIME屬性,所以服務器刷新客戶端的allocation時採用缺省的10分鐘生命期。

 

3      總結

在現實Internet網絡環境中,大多數計算機主機都位於防火牆或NAT之後,只有少部分主機能夠直接接入Internet。很多時候,我們希望網絡中的兩臺主機能夠直接進行通信(即所謂的P2P通信),而不需要其它公共服務器的中轉。由於主機可能位於防火牆或NAT之後,在進行P2P通信之前,我們需要進行檢測以確認它們之間能否進行P2P通信以及如何通信。這種技術通常被稱爲NAT穿透(NAT Traversal)。

RFC3489中定義的STUN,即簡單地用UDP穿過NAT(STUN)是個輕量級的協議。它允許應用發現它們與公共互聯網之間存在的NAT和防火牆及其他類型。它還爲應用提供判斷NAT給它們分配的公共網際協議(IP)地址。STUN可工作在許多現存NAT上,並且不需要它們做任何特別的行爲。它允許廣泛的各類的應用穿越現存的NAT設施。

RFC5389中對STUN協議進行了修訂,將其定位於爲穿透NAT提供工具,即NAT會話穿透效用是一個用於其他解決NAT穿透問題協議的協議。它可以用於終端設備檢查由NAT分配給終端的IP地址和端口號。同時,它也被用來檢查兩個終端之間的連接性,好比是一種維持NAT綁定表項的保活協議。STUN本身並不是一種完整的NAT穿透解決方案。它相當於是一種NAT穿透解決方案中的工具。這是與先前的版本相比最重要的改變。之前的RFC3489中定義的STUN是一個完整的穿透NAT解決方案。此外,最大的區別是支持TCP穿透。

RFC5766中對STUN協議再次進行了擴展,即中繼穿透NAT:STUN的擴展。TURN與STUN的共同點都是通過修改應用層中的私網地址達到NAT穿透的效用,異同點是TUN採用了兩方通訊的“中間人”方式實現穿透,突破了原先STUN協議無法在兩臺主機不能夠點對點直接連接下提供作用的限制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章