OpenVPN 協議解析-握手資料包分析

笑葉林發表於2018-12-14

學習一種協議的最好的方式就是研究它的資料包,這樣可以加深對協議的理解。對於研究過某種協議資料包的傢伙來講,他一定知道協議頭的哪個位置對應哪個欄位,雖然這對於理解協議為什麼這麼設計可能沒有太大的幫助,然而對於排查問題和實際實施是很有幫助的。既然很多人都對Richard Stevens的《TCP/IP詳解》情有獨鍾,我們就剽竊他的風格,解析一下OpenVPN的握手是如何完成的。本文分析70餘個資料包,當然,最終我會略去重複的內容,來看看OpenVPN的握手協商過程。

1. 分析前的解釋

1.1. 資料包格式

本文以如下格式分析每一個握手資料包,具體OpenVPN資料包的格式,請參閱《OpenVPN協議解析-網路結構之外》:

1.1.1. 本文為了節省篇幅,省去了UDP以下各層的資料,直接從OpenVPN的封裝開始。

1.1.2. 以下為資料包格式,xx為十六進位制位元組:

A->B:方向,分別為Server->Client和Client->Server |xx|:操作碼+key id,1位元組,xx右移5位就是操作碼 |xx|xx|xx|xx|xx|xx|xx|xx|:session ID,這個ID是單方向的,8位元組 |xx|xx|xx|xx|xx|xx|xx|xx|1a|de|38|06|ab|5e|55|0f|8f|ed|ea|ca|:資料包訊息MAC,本文啟用tls-auth選項,本文MAC長度20位元組(使用SHA1) |xx|xx|xx|xx|xx|xx|xx|xx|: 包id和時間戳,包括ack包的id和時間戳,在該方向每法送一個包,包id都會遞增。8位元組 |xx|[...|xx|...]:如果ack buffer length長度為0,沒有ack資訊,如果不為0,則後面包含確認的包序列號,然後追加一個對端的session id,變長 |xx|xx|xx|xx|:包序列號,不包括ack包,也就是說,傳送一個非P_ACK包,包序列號才會遞增,實際上如果是P_ACK包,根本就沒有這一行欄位。4位元組

1.2. TLS/SSL握手協議

關於TLS/SSL握手協議,最好的資料是RFC,然後或許看OpenSSL的原始碼上手更快些。這裡僅給出幾個圖示和幾點解釋,SSL握手協議包格式如下:

OpenVPN 協議解析-握手資料包分析

不管是記錄協議還是握手協議,都會有一個record header,該record格式如下:

OpenVPN 協議解析-握手資料包分析

其中,如果是SSL握手協議的話,record header的type欄位為0x16,我們使用的TLS,因此其major和minor分別為0x03和0x01。接下來,我們看一下握手頭:

OpenVPN 協議解析-握手資料包分析

其中握手型別分別為:

Client hello:0x01
Server hello:0x02
Certificate:0x0b
Server key exchange:0x0c
Certificate request:0x0d
Server hello done:0x0e
Certificate verify:0x0f
Client key exchange:0x10
Finish:0x14
複製程式碼

1.3. SSL握手協議和OpenVPN握手的關係

我們知道,OpenVPN的連線建立使用了SSL握手協議,可是卻抓不到SSL握手包,實際上SSL握手協議是作為一種載荷封裝在了OpenVPN的協議包裡面了,SSL握手協議資料實際上是寫入了一塊記憶體,然後OpenVPN從該記憶體中讀出這些資料包,然後封裝後通過reliable層發出,資料到達對端後,解封裝後寫入一塊記憶體,然後SSL從記憶體中讀出SSL握手載荷,一切都是通過BIO實現的。(如不甚理解,請參閱《OpenVPN協議解析-網路結構之外》)

可以看出,SSL和OpenVPN屬於不同的層次,SSL握手協議在OpenVPN握手協議之上,然而當SSL握手結束後,OpenVPN的金鑰協議又在SSL記錄協議協議之上,更清晰的圖示如下所示:

OpenVPN 協議解析-握手資料包分析

因此,OpenVPN握手協議可以將SSL握手載荷隨意拆分和合並,它們通過BIO建立的記憶體區域建立唯一的聯絡。

2. OpenVPN資料包詳解

2.1. 第1個資料包

Client->Server
|38|
|5f|f2|07|b2|27|12|14|2d|
|5d|67|45|d5|59|1a|c5|b5|1a|de|38|06|ab|5e|55|0f|8f|ed|ea|ca|
|00|00|00|01|4d|da|14|7c|
|00|
|00|00|00|00|
複製程式碼

這是OpenVPN連線的第一個資料包,當然也是握手的第一個資料包,在這裡首先要提出的是,通過緊接著後面兩個資料包,我們可以看出,雖然OpenVPN首推UDP,且本文解析的資料包也是基於UDP的,然而對於OpenVPN握手,是必須要保持連線和保證傳輸的,因此實現了reliable層,OpenVPN的握手完全基於reliable層,等完成了握手,協商好了金鑰(注意,這個金鑰不是SSL握手協商出來的,而是在SSL記錄協議上協商出來的),reliable層就不需要了,OpenVPN記錄協議並不需要reliable層(它只是一個運輸協議,如IP協議)。

既然reliable層是一個有連線和保證傳輸的協議層,那麼最簡便的實現方式就是模仿已有的實現,那就是TCP,雖然reliable層是一個高度簡化的保證傳輸的版本,但是三次握手是少不了的,因此第一個資料包和下面兩個資料包就是三次握手包。

除了和TCP一樣,三次握手建立連線之外,OpenVPN的握手協議的三次握手還協商了一個資訊,那就是金鑰交換演算法,這個協商通過其操作碼可以看出,第一個資料包的操作碼是0x38,右移5位就是7,因此其表示第二類金鑰協商演算法。

其中第二行表示一個session,這個session識別了一個連線,它是單方向的,每一個方向一個。然後第三行是一個HMAC值,在本文,它是20個位元組(特定於MD5和SHA1),這個HMAC只有在開啟tls-auth時才存在,其計算的金鑰是靜態配置的,兩端必須配置一致。tls-auth相當於OpenVPN協議在已經很安全的SSL協議外又加固了一層,者有效防止了拒絕服務攻擊,畢竟,SSL連線的安全性在於其建立安全通路的方式和建立後的安全性,其缺點就是消耗大量的資源。

再往下一行是一個packet id和一個時間戳,這兩個欄位一共佔據8個位元組,它有效的防止了重放攻擊。再往後一行,為一個單位元組0x00,可見第一個資料包並不想確認任何資料,這也是正常的。最後是一個包序列號,佔據4個位元組,為0,每個方向的非確認包(P_ACK),都擁有一個序列號,這個序列號是用於保證傳輸的,並不是用於防止重放攻擊的,注意,它和packet id的意義是不同的,確認包並不包含序列號。

2.2. 第2個資料包

Server->Client
|40| 
|7a|26|7c|45|a6|72|e1|a2|
|b0|72|21|0a|c1|3f|f1|4a|bc|79|46|14|30|51|f3|0c|64|63|54|de| 
|00|00|00|01|4d|da|14|7c| 
|01|00|00|00|00|5f|f2|07|b2|27|12|14|2d|
|00|00|00|00|
複製程式碼

第二個資料包是伺服器發往客戶端的,類似於TCP的SYN/ACK,和第一個資料包一樣,它的操作碼也標識了金鑰交換演算法,這次是8,表示伺服器使用第二類金鑰交換演算法,這樣就和客戶端達成了一致。第二行是一個session id,這是另一方向的session id。後面的資料意義和第一個包是一樣的。最後一行,也是一個4位元組的0,這表示在從伺服器端到客戶端這個方向上,這是第一個資料包。

值得注意的是,我們看第5行,第一個位元組為1,說明有一個資料包確認,緊接著4個位元組為0,說明它確認的是Client到Server方向的第一個資料包,再往後的8個位元組是Client到Server方向的session id。

2.3. 第3個資料包

Client->Server
|28|
|5f|f2|07|b2|27|12|14|2d|
|40|20|5a|f4|92|2c|a7|4e|8e|f5|1a|e8|45|06|e7|5a|ae|64|64|85|
|00|00|00|02|4d|da|14|7c|
|01|00|00|00|00|7a|26|7c|45|a6|72|e1|a2|
複製程式碼

這是一個確認包,從操作碼可以看出來,0x28右移5位就是5,這就是P_ACK_V1。說明這是一個確認。由於它是一個純確認包,它沒有最後的包序列號。

我們直接看第5行,它的意義和第二個資料包是一樣的。到此為止,實際上我們已經沒有必要在繼續分析下去了,如果你已經理解了這前三個資料包的話,實際上對於OpenVPN的握手協議你基本已經理解了。然後我還要繼續下去,因為接下來就要開始SSL握手了。可以通過接下來OpenVPN的握手協議對SSL握手協議的封裝來解釋為何抓取不到OpenVPN的SSL連線握手包。

2.4. 第4個資料包

Client->Server
|20|
|5f|f2|07|b2|27|12|14|2d|
|57|b8|8a|a1|3c|0f|84|2b|91|42|2a|92|c6|f4|55|c9|7c|82|48|65|
|00|00|00|03|4d|da|14|7c|
|00|
|00|00|00|01|
|16|03|01|00|5f|01|00|00|5b|03|01|4d|da|14|7c|77|15|6d|32|69|2a|3d|90|fc|8d|3d|a6|68|83|3f|11|e2|63|b7|1b|e5|d1|46|95|3b|99|a6|e5|00|00|34|00|39|00|38|00|35|00|16|00|13|00|0a|00|33|00|32|00|2f|00|66|00|05|00|04|00|63|00|62|00|61|00|15|00|12|00|09|00|65|00|64|00|60|00|14|00|11|00|08|00|06|00|03|01|00|
複製程式碼

通過前三個包的分析,我們看出,它們是沒有包含資料的,這很類似於TCP的三次握手訊息。這第四個資料包是第一個包含資料的OpenVPN握手包。

如果我們仔細觀察一下OpenVPN的握手協議頭,發現它裡面實際上是沒有長度資訊的。這是因為我們討論的是UDP上的OpenVPN,UDP資料包的長度是收發一致的,如果是TCP的話,它是流式的,收發不一致,當然是需要一個長度追加在操作碼前面。另外,在深入資料載荷之前,我們看一下第六行,也就是一個4個位元組的包序列號,這時是0x01,說明這是Client到Server方向的第二個資料包,我們數一下,也真的是這樣。

好了,我們看一下其資料載荷的前面幾個位元組,其前5個位元組0x16,0x03,0x01,0x00,0x5f,我們發現它是SSL記錄頭的格式,0x16是其type,實際上就是握手協議,後面的0x03和0x01是major和minor號,後面的兩個位元組表示SSL握手訊息長度,那麼它到底是什麼SSL握手訊息呢?我們根據SSL握手協議頭的格式,其第一個位元組是0x01,也就是Client hello,它的長度是後面的3個位元組。最終,我們發現,這個OpenVPN協議封裝的資料載荷就是SSL握手訊息中的Client hello訊息。

2.5. 第5個資料包

Server->Client
|20|
|7a|26|7c|45|a6|72|e1|a2|
|4c|b7|ae|78|e9|67|ca|bb|2b|71|0d|fc|48|51|08|9a|30|6c|dd|54|
|00|00|00|02|4d|da|14|7c|
|01|00|00|00|01|5f|f2|07|b2|27|12|14|2d|
|00|00|00|01|
|16|03|01|00|2a|02|00|00|26|03|01|4d|da|14|7c|5b|f3|9a|fa|b9|d3|b1|75|b7|ba|4f|9d|33|6e|4b|25|cc|f3|dd|6f|d6|73|b0|6e|f0|ac|aa|f4|00|00|39|00|16|03|01|04|8b|0b|00|04|87|00|04|84|00|02|41|30|82|02|3d|30|82|01|a6|02|09|00|fb|0e|c2|7f|49|2a|44|8b|30|0d|06|09|2a|86|48|86|f7|0d|01|01|05|05|00|30|61|31|0b|
複製程式碼

通過第四個資料包的分析,我們很簡單的分析出這個OpenVPN協議封裝的資料載荷是Server hello訊息,因為|16|03|01|00|2a|02|中02表示Server hello。

如果仔細往下看,我們最終又發現了一個SSL記錄頭序列:|16|03|01|04|8b|,緊接著的是|0b|00|04|87|,這明顯又是一個SSL握手訊息,0x0b是其訊息型別,我們發現它是Certificate,它的長度是0x0487,這可夠長的了...

SSL握手訊息之所有可以在OpenVPN協議封裝的任意位置,是因為它只是資料載荷的一部分,再次重申,SSL協議被OpenVPN協議封裝了,SSL是載荷,OpenVPN提供封裝和運輸服務,不管怎樣拆分,最終總是能保證資料能可靠到達對端,由對端的SSL協議負責進行組合。

2.6. 第6個資料包

Server->Client
|20|
|7a|26|7c|45|a6|72|e1|a2|
|16|28|bc|a4|3a|34|01|45|26|2b|ab|59|ea|ee|d3|88|b4|dd|11|ee|
|00|00|00|03|4d|da|14|7c|
|00|
|00|00|00|02|
|30|09|06|03|55|04|06|13|02|61|61|31|0b|30|09|06|03|55|04|08|13|02|61|61|31|0b|30|09|06|03|55|04|07|13|02|61|61|31|0b|30|09|06|03|55|04|0a|13|02|61|61|31|0b|30|09|06|03|55|04|0b|13|02|61|61|31|0b|30|09|06|03|55|04|03|13|02|63|61|31|11|30|0f|06|09|2a|86|48|86|f7|0d|01|09|01|16|02|61|61|30|1e|17|0d|31|
複製程式碼

這個資料包比較奇怪,因為我們沒有在資料載荷的開始處發現任何SSL協議的識別標識,另外,如果我們仔細分析上一個資料包,也就是第五個資料包中在中間封裝的Certificate訊息,它的長度是0x0487,可是無論怎麼看,第五個資料包整個也沒有那麼長,那麼這麼長的資料到底在哪裡呢?答案就是在後續的資料包中,這就類似IP分片,非頭IP分片的載荷中,是沒有包含傳輸層協議頭的,也可以通過TCP分段來理解...OpenVPN的實現中,在reliable層中實現了根據虛擬網路卡MTU或者當前網路狀況任意拆分資料的功能,這樣可達到高效利用網路的目的。這樣,即使一個1000位元組的SSL握手訊息,reliable層也可以將其拆成10個100多位元組的UDP包,資料到達對端後,每一個100多位元組的UDP包進入其SSL BIO的記憶體BIO中,由SSL協議的實現負責重組資料。

我們可以將reliable層當成一個在UDP上偽裝的偽TCP,該偽TCP直接和SSL的記憶體BIO介面。

在瞭解了上述原理之後,我們直接略過Server傳輸的Certificate訊息,當然還是會分析一個Client的確認包,因為reliable層是需要確認的,這就是接下來馬上就要分析的第九個資料包。

2.7. 第9個資料包

Client->Server
|28|
|5f|f2|07|b2|27|12|14|2d|
|60|e1|38|28|46|ef|63|5e|46|e6|31|9c|fd|7f|40|7d|df|bd|fd|df|
|00|00|00|04|4d|da|14|7c|
|01|00|00|00|01|7a|26|7c|45|a6|72|e1|a2|
複製程式碼

這是一個純確認包,我們看到其操作碼是5,它確認的是Server傳送的Certificate的OpenVPN協議封裝的一部分,當然這樣的包有好幾個,這裡僅僅揪出一個來分析。它確認的是Server傳送的第兩個資料包,也就是第五個資料包。

2.8. 第27個資料包

Server->Client
|20|
|7a|26|7c|45|a6|72|e1|a2|
|0a|1d|15|d6|1a|42|e6|57|29|18|71|b9|db|fd|d1|43|9d|6d|ef|8a|
|00|00|00|0e|4d|da|14|7c|
|00|
|00|00|00|0d|
|69|67|1e|99|7a|a1|8b|9d|b1|e7|9a|bb|8b|ca|49|16|03|01|01|8d|0c|00|01|89|00|80|bf|88|f7|46|21|4c|81|5c|20|77|d5|5a|52|1b|d4|e8|94|90|e4|84|ae|74|1a|82|f9|c1|0d|f2|ac|ea|10|2f|25|12|41|d8|d2|41|64|44|a2|a1|42|3f|73|1a|79|0c|e1|d4|64|8e|35|a1|1b|00|90|9f|29|8b|bd|ae|16|84|45|d9|44|f9|44|00|d1|03|fa|3e|
複製程式碼

在這個OpenVPN協議封裝的中間,我們發現了Server的Server key exchange訊息。這個道理請參照2.6節。

2.9. 第34個資料包

Server->Client
|20|
|7a|26|7c|45|a6|72|e1|a2|
|8c|6e|d5|13|e0|f8|14|13|c3|c6|d7|e3|b0|7f|7b|10|92|0c|85|b5|
|00|00|00|12|4d|da|14|7c|
|00|
|00|00|00|11|
|b5|e4|de|cb|85|b9|10|0c|1f|d6|7a|21|56|2c|7e|ca|15|16|03|01|00|74|0d|00|00|6c|04|03|04|01|02|00|65|00|63|30|61|31|0b|30|09|06|03|55|04|06|13|02|61|61|31|0b|30|09|06|03|55|04|08|13|02|61|61|31|0b|30|09|06|03|55|04|07|13|02|61|61|31|0b|30|09|06|03|55|04|0a|13|02|61|61|31|0b|30|09|06|03|55|04|0b|13|02|
複製程式碼

在這個OpenVPN協議封裝的中間,我們發現了Server的Certificate request訊息。這個道理請參照2.6節。

2.10. 第36個資料包

Server->Client
|20|
|7a|26|7c|45|a6|72|e1|a2|
|a4|af|b5|cd|45|2e|33|ea|ff|6b|0b|58|b2|fc|1b|41|bd|e4|ef|99|
|00|00|00|13|4d|da|14|7c|
|00|
|00|00|00|12|
|61|61|31|0b|30|09|06|03|55|04|03|13|02|63|61|31|11|30|0f|06|09|2a|86|48|86|f7|0d|01|09|01|16|02|61|61|0e|00|00|00|
複製程式碼

最終,在資料載荷的最後,我們發現了|0e|00|00|00|序列,這是什麼呢?我們知道,Server hello done的握手type是0x0e,其後三個位元組為訊息長度,為全0,這個訊息只是想告訴Client,Server傳送的訊息結束了,Client可以傳送資料了。注意,和往常不一樣,Server hello down訊息並沒有SSL記錄頭序列,而是直接貼在訊息後面的。

我們只是想分析OpenVPN協議,而不是SSL協議,因此不會過分敘述SSL握手的細節。接下來的OpenVPN協議封裝的載荷包含了Client傳送給Server的Certificate,Verify, Client key exchange訊息等,我們略過這部分重複且枯燥的敘述。

2.11. 第65個資料包

Client->Server
|20|
|5f|f2|07|b2|27|12|14|2d|
|56|ae|02|5f|0c|22|95|78|b7|7e|fb|24|8e|f9|34|56|69|09|87|e7|
|00|00|00|23|4d|da|14|7c|
|00|
|00|00|00|10|
|0f|17|d5|75|85|7b|ba|84|e9|b2|a0|9b|22|cc|ad|cc|cf|aa|38|6d|1c|ef|2b|23|69|8f|06|ac|81|79|50|7c|00|cb|21|0d|26|7d|26|81|14|28|bd|45|36|02|14|03|01|00|01|01|16|03|01|00|30|fc|bf|67|e0|a3|95|5d|ee|e1|59|fb|16|2d|c0|7a|55|82|de|73|34|02|1c|a2|88|15|4a|92|c6|6e|41|87|e4|a1|94|e8|f8|e5|31|e1|e0|ae|2f|7d|
複製程式碼

注意,我們發現了|14|03|01|00|01|01|序列,這個序列特殊嗎?是的,很特殊,因為這並不屬於SSL握手協議,而是一個單獨的SSL協議,那就是CHANGE_CIPHER_SPEC協議,這個協議說明,SSL通道上的安全金鑰(加密對稱金鑰,摘要金鑰等)已經建立,接下來的訊息將會被這些金鑰作用,因此接下來的訊息我們將無法再看到了,它們是加密的。CHANGE_CIPHER_SPEC協議規定,其資料載荷就是一個0x01,因此其長度也就是1,故而出現了|14|03|01|00|01|01|序列。

我本來想把這個資料包作為一個終結,結束討論,因為以後的資料包都是通過SSL握手協商出來的安全通道加密的,也就無法理解其中的內容了,這樣分析還有什麼意義呢?我們知道,SSL握手的最後一步是Finish訊息,這個訊息是協議規定的,可是我們再也看不到了。

2.12. 第71個資料包

Server->Client
|20|
|7a|26|7c|45|a6|72|e1|a2|
|c8|eb|b1|85|e7|b2|bb|34|f1|96|0e|00|1f|56|df|21|30|34|a3|0e|
|00|00|00|23|4d|da|14|7c|
|01|00|00|00|11|5f|f2|07|b2|27|12|14|2d|
|00|00|00|13|
|14|03|01|00|01|01|16|03|01|00|30|8f|57|7d|42|ae|a3|89|2d|34|5a|a7|d1|f7|08|ef|a1|bd|8b|52|1c|32|56|da|5d|33|0b|f2|95|82|d2|f3|c8|60|69|e6|0b|c2|0d|88|65|cf|fc|af|b4|93|6e|0c|31|
複製程式碼

無疑,這個資料包的載荷中也包含了|14|03|01|00|01|01|序列,這是Server傳送的CHANGE_CIPHER_SPEC訊息,表示接下來就是加密資料了。接著上節的話題,本來該結束了,但是為何還沒有結束呢?因為OpenVPN的握手協議之後,緊跟著的就是OpenVPN的金鑰協議,這兩個協議同在OpenVPN的控制通道中進行,也就是說都用到了reliability層,然而OpenVPN金鑰協議卻不是用於封裝SSL載荷的,而是在SSL記錄協議之下,作為SSL記錄協議的載荷存在的。

2.13. 第72個資料包

Client->Server
|20|
|5f|f2|07|b2|27|12|14|2d|
|95|6f|3c|de|d3|4c|45|71|89|7f|86|5c|c0|58|01|47|7d|72|1a|61|
|00|00|00|25|4d|da|14|7c|
|01|00|00|00|13|7a|26|7c|45|a6|72|e1|a2|
|00|00|00|12|
|17|03|01|00|20|42|32|5b|54|35|72|45|07|f7|41|dd|5d|d8|ba|42|46|d9|bf|a7|44|2d|38|38|f3|11|cf|bc|81|df|e4|33|37|17|03|01|01|20|a6|81|5e|5a|cf|5a|dd|9a|4d|b0|1d|8a|87|91|0c|e6|3f|ad|94|16|cf|f9|6c|9f|fb|08|5f|d1|52|35|32|47|3e|93|76|31|27|db|0b|7d|77|cb|4c|c6|b5|6d|da|e1|3d|67|a1|23|24|c4|4f|81|a2|54|
複製程式碼

在這個OpenVPN資料包中,包含了|17|03|01|00|20|序列,這無疑也是SSL記錄頭,其中0x17是type,這是什麼呢?是SSL記錄協議。也就是說,從這裡開始,OpenVPN使用了SSL記錄協議。但是前面不是說OpenVPN使用自己的“盡力而為”協議傳輸資料的嗎?而SSL是基於可靠連線的啊!事實上,這裡使用的SSL記錄協議並不是實際的資料傳輸,而是OpenVPN使用的真正的金鑰的協商,我們前面提到過,這個協商是作為SSL記錄協議的載荷存在的。

再仔細觀察這個資料包,我們發現其中有兩個|17|03|01|00|20|序列,在相反的方向上又有兩個這樣的序列,當然後兩個位元組是不同的,這實際就是OpenVPN金鑰協議的載荷,這裡的握手明顯使用的是第二類方式(因為最開始的三次握手中的操作碼已經決定了)。由於這裡的金鑰協議資料已經使用SSL記錄協議在SSL加密通道中了,因此其協議包我們不得而知(這個實驗中,我使用的cipher是none,然而digest卻不是none)!

3. 總結

通過OpenVPN握手資料包的抓取和分析,我們瞭解到了OpenVPN協議的更多的細節,它分為兩個部分,控制協議和記錄協議,兩個協議通過OpenVPN協議頭複用到了UDP,其中控制協議又分為了握手協議和金鑰協議,它們都跑在一個可靠的偽TCP虛擬連線上,它實際上很複雜同時又很簡單。

通過分析資料包的方式學習網路協議是一個不錯的方式卻不是一個不錯的開始,對於OpenVPN,首先我們必須首先明白它的設計,它的框架,它的用途以及必須可以熟練使用它。其中的前提是使用。如果只是認識到OpenVPN是虛擬網路卡加SSL協議的結合,那未免太膚淺了,一個革命性的開源VPN肯定不可能如此膚淺的,它擁有自己的協議。



reference

相關文章