工作上,平時上網,都用到了OpenVPN,事實上我比較喜歡用TCP模式而不用UDP(直到發現了重傳疊加),因為自從上學時代,我就對 UDP 不感冒,它除了多路複用,分解的作用外,和IP幾乎一樣,而且根據我們的習慣,越複雜的東西越好,UDP簡單,所以它不好!我不知道我們為何會有這樣的想法,如果德國人和英國人也這樣想,那很多好東西就都不會出現了,甚至都不會發生工業革命…後來,我發現,事情慢慢起了變化,不是想的那麼簡單,確實是這樣,TCP有好處,也會帶來問題,UDP是不好,同樣也會帶來問題,也會有一些優勢,本文就針對我挖掘出的一些問題進行總結,不很全面,僅僅作為一個小結。
1. 一些詞彙和短語的解釋
正常傳輸佇列:從應用層來一個資料,該資料就會被TCP封裝,然後加入正常傳輸佇列末尾。
重傳佇列:在正常傳輸佇列中每傳送一個TCP分段,該分段就會加入重傳佇列末尾。
正常傳輸:TCP會視傳送視窗的大小以及視窗的可用性從正常傳輸佇列頭取出一個TCP分段,然後傳輸。
重傳:
超時重傳:從重傳佇列頭取出一個TCP分段,重傳之;
快速重傳:從重傳佇列頭依次取出一個個可能丟失的TCP分段,重傳之;
避免不必要重傳:在快速重傳中,如果已經重傳了可能丟失的TCP分段,理論上講,按照標準,接下來會依次重傳重傳佇列後面的所有TCP分段,然而如果是由於ACK丟失,或者接收端已經快取了亂序的分段,這樣的重傳就是冗餘的,由於正常傳輸佇列和重傳佇列是分開的(重傳佇列和傳送佇列分離設計),此時傳送正常傳輸佇列中的當前可以傳送(沒有視窗限制和Nagel限制)的TCP分段可以有效地“帶回一些ACK”這些ACK會清除重傳佇列裡的TCP分段。因此根據這個策略,不必要的重傳實際上是很少的。背景流量:如果一條IP層鏈路上封裝了一個隧道,所有非隧道的流量都是背景流量。
2. TCP隧道的問題
2.1. TCP擁塞控制的意義
注意,TCP本來是不包含擁塞控制的,但是因為它是端到端的協議,並沒有執行於任何網路節點,因此裸TCP對於頻寬是極其貪婪的,和UDP一樣,當網路擁堵以後,TCP就加入了擁塞控制。然而端到端的協議並不僅僅就是TCP,雖然TCP加入了擁塞控制,可是UDP並沒有,UDP本來就不關心丟包,網路擁塞了,丟包就是了。然而,UDP是情願丟包也不進行擁塞控制,這確實是連累了TCP,結果就是TCP頻繁的進入擁塞狀態,不斷的慢啟動或者快速重傳,導致TCP流量的顛簸,雖然諸多TCP流量都不斷的徘徊在鋸齒的峰谷之間,顯得很公平,然而對於總體的頻寬,在存在沒有擁塞控制機制比如UDP流量的情況下,TCP的效能將急劇下降。
因此整個頻寬對各個端到端協議流量的分配是不公平的。
2.2. TCP公平性
TCP的擁塞控制內在的實現了公平性,大家都會對擁塞快速反應。如果所有流量都是TCP,那再好不過了,頻寬可以得到完全公平分配。
2.3. 並不是所有的流量承載於TCP
然而,還有很多協議是沒有擁塞控制的,比如UDP,大量貪婪的流量瞬間擠滿整個通路,雖然大多數貪婪流量被丟棄了,然而傳送端並不在乎,仍然繼續快速大量傳送,結果導致對丟包很在乎的TCP進入擁塞狀態,TCP的退出,導致UDP流量不再被丟棄,結果,整個通路長期保持擁堵狀態,TCP流量很難有機會再獲得公平頻寬,直到UDP流量的退出。
2.4. 需要做的工作
現在怎麼辦?難道為UDP加入擁塞控制嗎?這樣不妥,那樣對實時要求很高的UDP應用影響太大,因此需要一種更為溫和的方式解決這個頻寬在不同型別端到端協議之間的不公平分配問題。
這種溫和的解決方式就是TCP隧道,簡而言之,就是在核心網路的容易擁堵的三層鏈路的兩個路由器之間部署一條TCP隧道,封裝所有的流量,包括UDP的流量,這樣,起碼在該段通路上,頻寬分配公平了,特別是,如果僅僅部署一條TCP隧道(所有流量通過該隧道)的話,排除中間路由器排隊/路由處理的影響,擁堵是不會發生的。
2.5. TCP隧道導致的重傳疊加
現在考慮一個問題,那就是TCP隧道之上的載荷也是TCP流量,這樣的話,恰好TCP隧道上發生擁堵而丟包,這種情況下,載荷TCP超時也在預料之中,因此會發生重傳疊加,事實上,我們知道,載荷的重傳是不必要的,因為隧道是TCP的,所有的丟包都會得到重傳。
我們可以把TCP隧道封裝的一條網路通路想象成一個路由器,在該路由器中,只要得到排隊的資料包都會安全可靠到達輸出埠,在路由處理過程中不會丟包。這也許不太符合真實的路由器,因為真實的路由器中在路由處理的過程中很有可能發生錯誤的。既然是這樣的話,我們就知道,一旦在這個虛擬的路由器中發生丟包,端TCP系統是不需要重傳的,然而事實是,端到端的TCP協議永遠都不知道是什麼原因導致了丟包,並且更不知道丟包發生在哪裡。
部署TCP隧道的時候需要做什麼?很簡單,就是儘量避免隧道的丟包,可以理解為不要讓隧道和其它流量競爭頻寬,那麼可選的方案只有一個,就是部署TCP隧道的通路最好不要再有其它的流量,也就是不要擁有背景流量!
2.5.1. 設計的意義:分層模型的層間功能不能重合
TCP是一個端到端的有連線的可靠傳輸的協議,它作為盡力而為的IP協議的載荷而存在,如果它跑在TCP之上的話,就會造成重傳疊加,這樣的話很多重傳將是不必要的,因此從設計的角度看來,分層模型之間的功能最好不要重合。
2.6. TCP隧道導致的延遲增加
部署良好的TCP隧道雖然對於頻寬的公平性有很好的促進作用,然而卻會導致單個資料包延遲的增加,這個延遲的增加具體是什麼原因導致的呢?其實很簡單,那就是在TCP隧道的入口處,TCP分段要作為TCP載荷被封裝,這個時間實際上完全是CPU完成的,這是延遲增加的原因。當然延遲雖然增加了,也不完全是壞事,後面我們會看到,通過TCP隧道的速率匹配,可能做到最大化吞吐量,畢竟,吞吐量和延遲總是滿足反比律的。
3. 有效利用TCP隧道
事情並不是想象的那麼壞到了極點。這是因為還有比TCP更壞的,幾乎所有的端到端協議都比TCP更可惡,因為它們都比TCP更貪心,所有的端到端協議都以為整個頻寬都是自己的,而TCP起碼還做了擁塞控制使得整個網路使用者更加公平。既然如此,我們需要有效的利用TCP的這點優點。
3.1. 有效利用公平性
載入擁塞控制機制的TCP的優點就是公平性,然而UDP和TCP們共享統一網路通路,卻沒有對擁塞進行反應,這樣導致了網路頻寬分配的嚴重不公,大量UDP流量會瞬間吃掉所有頻寬。因此一個好的解決方案就是將UDP(以及其它所有的沒有擁塞控制的端到端協議資料)封裝在TCP隧道中。3.2.TCP隧道在中間路由器上增加了容量適配功能我們知道,在單一連線的鏈路上,延遲和頻寬的乘積表示了該鏈路的容量,如果能始終保持鏈路的滿載,那無疑是對網路頻寬的最有效的利用,可惜的是,端到端的TCP並不能做到這一點,因為端到端之間要經過無數的二層鏈路,經過無數的路由器,往往中間的核心網路頻寬是很大的,然而端系統的TCP卻不可能認識到這一點,最有效的措施是在核心網最容易擁堵的鏈路上部署一個TCP隧道,以及在核心網容量差別最大的兩條相鄰鏈路之中的大容量鏈路上部署一條TCP隧道,這樣該TCP隧道就會起到容量匹配的作用。
然而事情並不是這麼簡單,這個TCP隧道的構建是有要求的,事實證明,它的出入口快取的容量達到該鏈路的延遲和頻寬乘積,那將是最高效的。
3.3. TCP隧道減少了擁塞控制起作用的頻率
TCP隧道顯式的減少了流的數量,在構建了兩條TCP隧道且沒有背景流量的情況下,原來N個流量的頻寬競爭現在成了兩個TCP的競爭。更可貴的是,原來的N個流量中可能還有UDP流量,它會吃掉幾乎整個頻寬的哦!現在公平了,只有兩個TCP,完全履行擁塞公平的原則。可見TCP隧道縮小了競爭者的數量,並且消除了貪婪者,是一個化干戈為玉帛的利器。
我們知道,TCP隧道本身封裝了N個流量,它們之間的頻寬分配是不加權的,要想實現N個被封裝流量頻寬的加權分配,最好的辦法是按照協議型別對TCP隧道進行一些配置,比如UDP流量封裝在一個TCP隧道中,TCP流量封裝在另一個TCP隧道中,然後對排隊規則進行配置,比如實時優先順序高的UDP優先排隊,或者同一TCP隧道中的統一端到端協議的不同流量按照路由器的配置進行加權排隊…
3.4. 強制措施
雖然TCP隧道可以解決網路頻寬分配不公的問題,然而並不是所有人都能部署TCP隧道的,作為一種實驗,在實驗室可以隨意配置,然而作為一種實施,你就必須依賴政府和運營商。ISP或者政府需要對整個核心網路規劃十分了解,這樣它們便知道在哪裡部署TCP隧道了,更為重要的是,只有它們可以觸動核心網路的配置,只有它們有權力這麼做。
3.5. 部署TCP隧道
我們知道,只有一個TCP連線的鏈路(也沒有中間路由器)是沒有擁塞的,因此總是能期望其資料傳輸最終使網路滿載。
因此需要在流量很雜,UDP特別多的擁堵鏈路上部署TCP隧道,這樣所有的流量就進入了一個隧道,為了避免UDP撐滿整個隧道,有時需要部署兩條TCP隧道,一條專門用於UDP,另一條用於別的,這樣,UDP流量也要接受擁塞控制了。如果實現瞭解不用很慣著UDP,那麼就可以只部署一條TCP隧道,這樣的話,可以消除大部分的堵塞事件。
4. 效能
4.1. Selected ACK的意義
選擇重傳實現了一種機制,可以只重傳丟失的TCP分段,並且只要不是連續丟包就不用等待超時,只要接收端都到亂序分段就會在ack中附帶sack資訊,這樣即使TCP隧道的丟包也會得到快速的選擇重傳,這種重傳僅僅是TCP隧道兩端的,遠遠比端到端的鏈路距離要短很多,這樣端系統感知到的擁塞將會大大減小,減少了端系統TCP重傳的次數,同時也提升了傳送速率,因為進入慢啟動的機會少了。
4.2. TCP隧道的緩衝區大小的意義
頻寬,延遲的乘積表示了一個網路鏈路的容量,如果希望能獲得很大的吞吐量和較小的延遲,保持整個鏈路容量滿載是最有效的,然而要想避免擁塞,使TCP的擁塞控制不起作用,那麼只能保持僅有一個TCP連線的狀態,這樣TCP的端到端的流控機制最終會使鏈路滿載的。雖然由於可惡UDP流量的存在,不可能只部署一個TCP隧道,也就是說,最終TCP的擁塞控制還是會起作用,其時間-視窗圖可能還是會出現經典的鋸齒狀,然而實驗表明,保持TCP隧道入口緩衝區大小等於頻寬,延遲的乘積的話,還是會得到最大的吞吐量。
可以想象,在這個場景下,TCP隧道起到了容量適配的作用。否則,僅憑各個TCP端使用者瘋狂在網路上爭搶頻寬,大量頻寬將浪費在大量TCP使用者擁塞控制導致的鋸齒下尖角的位置。
4.3. TCP隧道對IP分片的影響
一旦由於MTU的影響,IP資料包分了片,一旦分片丟失,如果其上的載荷是TCP,那麼將會導致端系統的TCP重傳,在一條長長的且MTU變化很大並且還有點擁堵的三層鏈路上,IP分片很容易丟失,只要丟失一個分片,IP將不能重組,TCP分段也會玩完,在這種鏈路上,這很可能發生,如果只有1個資料包那麼其丟失造成TCP分段不能接收的可能為10%,如果該IP資料包分了10個片,那麼只要有一個分片丟失,那將造成同樣的結局,畢竟IP是盡力而為的,這個資訊最終會反饋到端TCP系統,如果在這種鏈路上的MTU突變造成分片的鏈路上部署一條TCP隧道,那麼將把到達穩定鏈路的跳數減少為1跳,有效遮蔽了MTU突變造成的IP分片且丟失的影響。
記住,TCP隧道兩端的路由器和中間的鏈路組成了一個虛擬路由器,該路由器只要資料排隊就不會丟包,是一種名副其實的工業級路由器!TCP隧道的存在使N跳變成了1跳,好事!
5. 影響TCP隧道效能因素
開門見山指出,就三點
5.1. TCP隧道的實現
這是一個很重要的因素,因為TCP隧道的實現有很多的方案,諸如OpenVPN的方式是一種很低效的實現,因為它是基於socket的,而socket是一個使用者介面,它作用於端到端的系統,對於核心網行為的理解是很片面的,因此OpenVPN只能面對TCP隧道的問題而拋棄它(但是在udp被封堵的時候仍然支援它),OpenVPN始終沒有認識到TCP隧道的優勢,根據設計需求,它沒有必要認識到…
更高效的實現方式是在核心協議棧中直接實現,像IPsec一樣,對於Linux而言,這是很高效的,因為可在軟中斷中完成一切TCP隧道載荷的封裝和解封裝,避免了端系統程式排程系統帶來的悲哀。
5.2. 有沒有背景流量
存在背景流量將是可悲的,如果背景流量是UDP的話,那將是致命的,本來N個TCP流量和UDP是並列的,然而N個TCP流量被封裝了,即使這樣,單獨的UDP流量也可能吃掉頻寬,因此部署TCP隧道的時候,一定不要UDP背景流量的存在,將它們封裝在一個單獨的加權值比較低的TCP隧道中是一個好方法。只要沒有背景流量,tunnel是可靠的,一旦有背景流量,在TCP隧道中丟包,就會出現重複的重傳疊加
5.3. 原始鏈路的性質
如果原始鏈路本來就不擁堵,那麼TCP隧道是沒有意義的,只有在原始鏈路很擁堵或者背景流量很大的情況下,才要部署TCP隧道。
5.4. OpenVPN的問題
故事:
首先列出一個網址:sites.inka.de/bigred/inde…
它是一個開源VPN實現CIPE的作者的網站,其中有一篇文章《Why TCP Over TCP Is A Bad Idea》,從其名字可以看出,在實現隧道的時候,用TCP封裝TCP並不是一個好主意。如今,雖然CIPE已經風光不再(它實際上從來都沒有風光過,從未流行過),然而它的思想被最大限度的擴充套件了,其中之一就是“儘量不用TCP來封裝IP隧道”。如今是OpenVPN的時代,在其man手冊裡,其proto欄位的解釋中,明確指出了一篇文章,旨在說明最好不用TCP作為封裝協議,除非UDP不可用時才使用TCP,該文章就是《Why TCP Over TCP Is A Bad Idea》,這篇文章很簡潔,沒有專業術語,寫得也很通俗易懂,不妨看一下
故事的一點解釋:
TCP隧道用於OpenVPN等使用者態的VPN實現時,加之部署這些VPN的組織並沒有強制措施,因此其效能嚴重依賴於RTT的測量演算法,,依賴於當前網路的狀態,否則網路傳輸可能將不可用。OpenVPN的使用者不會部署整個核心網路的,因此他們不可能期望TCP隧道帶來良好的收益,相反的,如果失去了強制措施,TCP隧道是弊大於利。
通過“TCP隧道對IP分片的影響”一節可以明白,在TCP隧道中傳輸大資料塊(OpenVPN下超過100位元組就算大了,因為虛擬網路卡的動態mtu在某時就是100)的時候,最容易使得IP分片,一旦一個分片丟失就會導致重傳疊加,就會引起問題,而對於互動式的TCP應用,重傳疊加發生的機率很小,因此就會出現在擁有背景流量的TCP隧道上訪問http會很慢,而登入SSH卻沒有問題。
記住,TCP隧道部署的意義:
1.UDP和TCP全部封裝在TCP隧道,因為TCP擁塞控制具有公平性,公平性;
2.頻寬的適配。