弈心:從事計算機網絡工作十年(新加坡7年,沙特3年),2013年考取CCIE,在新加坡先後任職於AT&T,新加坡交通部,蘋果,Equinix,蘇格蘭皇家銀行等大型企業、銀行和政府部門。 目前供職於“世界第一土豪大學“沙特阿卜杜拉國王科技大學(KAUST),擔任Senior Network Engineer,為KAUST校史上第一位也是唯一一位華人IT部門高級職員。2019年6月在知乎發佈了華語圈第一本專門為編程零基礎的網絡工程師量身打造的Python教程《網絡工程師的Python之路》。
國外流傳的一道CCIE挑戰題,這道題難度不低,出題的人是一位羅馬尼亞裔的美籍CCIE。為了防止作弊,所有參與者的回答都被出題人屏蔽,自己和出題者交流了一會,在得到所有自己所需要的信息後,花了幾天的時間做模擬實驗,找相關資料研究,最終給出了正確的答案。出題者肯定了我的解法,但是提醒我解法不止一種,並向我解釋了其他的解法,自己受益頗多,對ACL和Fragmentation(數據包分片)也有了更深層的理解。
先來看看這道題的拓撲,背景信息以及問題。
背景信息:
1. 某公司有三個site,分別為R1,R2,R3。
2. R3和R2之間跑GRE隧道,R2和R3的interface tunnel的MTU被人為的從1476改為1440.
3. R2和R1之間有兩條connection,一條是它們兩個互相直連的Backdoor(192.168.12.0/24),另外一條是通過ISP走Internet,R2-ISP-R1,走Internet的流量做了NAT(嚴格來說是PAT),走Backdoor的流量沒有NAT。
4. 用戶(172.16.1.10)直連R3,服務器(1.1.1.10)直連R1,服務器上運行的程序需要用到TCP port 1001和1002.
5. 需求: 如圖2所示,在R2上用PBR分流,實現TCP 1001的流量走R3-R2-R1, TCP 1002的流量走R3-R2-ISP-R1。
問題:
圖1給出了R2的配置,測試結果是走R3-R2-R1這條route的TCP1001的流量正常(圖3),走R3-R2-ISP-R1的TCP 1002的流量則有問題:TCP三次握手已經完成,但是session隨後卡住,不久之後session timeout(圖4),需要找出原因並給出解法。
解題思路:
1.
首先通看一遍出題者給的R2的配置,沒發現什麼大問題。因為既然用戶端(172.16.1.10)和服務器(1.1.1.10)之間的TCP 1001和1002兩種流量的TCP三次握手都成功完成了,那就說明了無論是路由,PBR,GRE隧道,還是PAT等等的配置都沒有任何問題。
2. 重新讀一遍題目的背景信息,其中第二條引起了我的注意:“R2和R3的interface tunnel的MTU被人為地從1476改為1440”。眾所周知,GRE隧道的header是24 Byte,所以通常來說GRE隧道端口的MTU是1500-24=1476 Byte (前提是配置GRE之前,物理端口使用的MTU是默認的1500),那麼再被人為地改為1440之後有什麼影響呢?這裡有很重要的一點要考慮,那就是用戶端(172.16.1.10)是否開啟了PMTUD(Path MTU Discovery),也就是“路徑MTU發現”的功能,因為這直接影響到用戶端的IP包在經過R3時會不會因為前後MTU的不匹配而導致被R3進行Fragmentation (IP數據包分片),因為通常來說,Fragementation有一個很大的缺點,那就是它大大降低了網絡的傳輸性能,會引起很多諸如丟包,延時,線路不穩定,甚至連接失效的問題,對於這個問題,因為出題者在背景信息裡沒有明確說明,我特此詢問了他,得到的回答是,在他的實驗環境裡,用戶端(172.16.1.10)實際上是一臺跑FreeBSD的服務器,他在用戶端通過PMTUD探測到了GRE的MTU(1476)之後,故意關閉了PMTUD,並給我解釋他之所以沒在背景信息裡給出這個條件,目的就是為了提高這道題的難度,並說我能考慮到這點,說明我的思路是對的,叫我繼續做下去。
3.順著這個思路繼續往下看,現在用戶端的MTU是1476(PMTUD被關閉之前學到的),PMTUD被關閉後,用戶端無法重新探測到R2和R3的GRE隧道的新MTU(1440),所以用戶端還是照舊把1476的包丟給R3,又因為1476大於1440,導致這時R3不得不對用戶端的包做IP分片了。這就導致了R2從R3接收到的用戶端的包其實是一大堆fragments(IP碎片)。考慮到這裡後,雖然自己清楚IP分片會導致各種各樣的鏈路問題,但針對這道題來說,究竟是什麼導致了TCP1002流量的問題,自己曾一時毫無頭緒。
4. 既然用戶端的包有被R3做IP分片的事實存在,那麼為什麼走R3-R2-R1的TCP 1001的流量沒有問題,唯獨走R3-R2-ISP-R1的TCP 1002卻有問題呢?帶著這個疑問,回頭再看R2的配置,下面這個被PBR用來分流的ACL引起了我的注意。
<code>ip access-list extended ACL_USE_BACKDOOR
permit tcp host 172.16.1.10 host 1.1.1.10 eq 1001
exit/<code>
這個ACL表達的意思一目瞭然:匹配所有從用戶端172.16.1.10去往服務器1.1.1.10,TCP端口為1001的數據。乍一看沒有任何問題,實則這裡面隱含了一個巨大的玄機,那就是當4層ACL遇到IP碎片時所產生的一些列“化學反應",要搞清楚這個,必須先弄明白IP碎片到底是怎麼一回事,為此我特意畫了下面兩個圖來說明。圖5為正常情況下,沒有被路由器fragmented的一個完整的IP包(MTU為1500),圖6則是這道題裡,已經被R3(MTU 1440)做了IP分片後的用戶端的包(MTU 1476),分別為“第一個碎片”和“第二個碎片”
5.圖5很好理解,通常一個完整IP包的MTU是1500 Byte,扣去3層的IP header (20 Byte,藍色部分)和4層的TCP header (20 Byte,橙色部分),剩下的data payload為1460 Byte, 這個1460 Byte在TCP裡又叫做MSS (Maximum Segment Size,注意只有TCP有MSS,UDP沒有這個東西),MSS是後話,暫且不表。
6. 圖6是解出這道題的關鍵所在。第一個碎片(1440 Byte)和第二個碎片(56 Byte)的區別一目瞭然,第一個碎片有4層header(橙色),第二個碎片沒有,這種情況對1001和1002的包有什麼影響呢?詳解如下:
首先再回顧一次這個ACL,看它對TCP 1001和TCP 1002兩種IP碎片包產生了什麼影響:
<code>ip access-list extended ACL_USE_BACKDOOR
permit tcp host 172.16.1.10 host 1.1.1.10 eq 1001
exit/<code>
TCP 1001的第一個碎片,Src IP = 172.16.1.10, Dst IP = 1.1.1.10, Src tcp port = any, Dst tcp port = 1001,符合ACL的條件,R2對這個碎片做PBR,即將它丟給R1。
TCP 1001的第二個碎片,Src IP = 172.16.1.10, Dst IP = 1.1.1.10, 依然符合ACL的條件,R2對這個碎片做PBR,即將它丟給R1。
TCP 1002的第一個碎片,Src IP = 172.16.1.10, Dst IP = 1.1.1.10, Src tcp port = any, Dst tcp port = 1002,不符合ACL的條件,R2對這個碎片做PAT,將它丟給ISP。
TCP 1002的第二個碎片,Src IP = 172.16.1.10, Dst IP = 1.1.1.10, 符合ACL的條件,R2對這個碎片做PBR,即將它丟給R1。
結論:
A.TCP 1001的第一個和第二個碎片都滿足ACL,都被R2的PBR丟給了R1,R1再將兩個碎片包傳給服務器1.1.1.10,所以TCP 1001的流量正常。
B.TCP 1002的第一個碎片(1440 Byte)沒有滿足ACL,被R2的PBR丟給了ISP,第二個碎片(56 Byte)滿足了ACL,被R2的PBR丟給了R1,現在兩個碎片被活生生的拆散開了,兩個碎片包“分道揚鑣”的結果造成了TCP 3次握手成功,但是隨即timeout的問題。
解法:
前面已經很系統地分析出了問題所在:因為用戶端關閉了PMTUD,再加上R3和R2的MTU從1476被改小到1440,導致MTU不匹配,從而引發了R3對用戶端的包進行Fragmentation(IP 分片)的行為,而又因為R2上配置的4層ACL導致了TCP 1002的第一個和第二個碎片包被R2分別丟去了ISP和R1,導致碎片無法復原,最終造成TCP 1002數據的連接問題。
弄清楚了問題出在哪裡後,接下來就是對症下藥了,這道題解法有多種,一一解釋如下。
解法1
用戶端172.16.1.10開回PMTUD,讓用戶端學習到新的MTU(1440),從而避免fragmentation。這是最直接也是最簡單的解法,這也是為什麼出題者在背景信息裡沒有給出“關閉了PMTUD"這個條件,不然的話,每個人都回答“開回PMTUD",那這道挑戰CCIE的題還有什麼挑戰性可言?
解法2
在R3端口上修改MSS,命令為ip tcp adjust-mss,避免fragmentation。
前面簡單提到過MSS這個東西,關於MSS,一些總結如下:
1. MSS出現在TCP的SYN包裡, 它和UDP無關。
2.MSS和MTU的區別在於,MSS是TCP數據包每次能夠傳輸的最大數據分段,它永遠比MTU小40 Byte,這40 Byte是什麼?前面的圖5已經解釋過了,即3層包頭(20 Byte)和4層包頭(20 Byte)。以這道題為例,用戶端包的MTU 為1476,那麼它TCP SYN包裡的MSS就為1436。
3. 顧名思義,思科的ip tcp adjust-mss這個命令的作用就是用來修改MSS大小的,以這道題為例,在R3上配置ip tcp adjust-mss 1300,當用戶端的包(MSS 1436)到了R3上後,因為1436>1300,這時R3就會把用戶端TCP包的MSS修改為1300,然後丟給R2, R2看到1300<1400,也就不會再對這個包做IP分片了,從而避免了fragmentation。
4. ip tcp adjust-mss這個命令是在端口下配置,至於配置在哪個端口,沒有硬性規定,就這道題來說,把它配置在直連用戶端的端口或者連接R2的端口上都可以,效果一樣。
解法3
修改PBR的ACL,讓TCP 1002的碎片也走R3-R2-R1。
<code>Ip access-list extended ACL_USE_BACKDOOR
permit tcp host 172.16.1.10 host 1.1.1.10 eq 1001
Permit tcp host 172.16.1.10 host 1.1.1.10 eq 1002/<code>
這個解法不完美,主要有兩個原因:
1. 違反了題目的規定,即TCP 1002的流量要走R3-R2-ISP-R1。
2. 這個解法治標不治本,因為將來如果遇到其他TCP端口的類似需求怎麼辦,一直違背分流的初衷?
解法4
修改PBR的ACL,逼迫TCP 1002的碎片被PAT,然後走R3-R2-ISP-R1。
<code>ip access-list extended ACL_USE_BACKDOOR
deny tcp host 172.16.1.10 host 1.1.1.10 eq 1002
permit tcp host 172.16.1.10 host 1.1.1.10 eq 1001/<code>
這個解法同樣不完美,原因很簡單,TCP 1001的第一個碎片沒問題,但是第二個碎片也被丟給了ISP,同樣的問題又發生了,只是這次出問題的是TCP 1001。
閱讀更多 弈心 的文章