題目有點大了,但是難免有一些憤怒!
我們的網關產品目前處在系統測試階段,不太順利,是太不順利!各方面都在懈怠,包括我!我除了懈怠,還在找機會逆襲!順便蔑視一下測試者,希望產生一種想象,即他發現的問題其實不是問題,而是因為他的無知所導致!就在昨天,機會來了,我便氣揚了!
很多人覺得我是下三層網絡的專家,對於TCP之類的無權問津,但是我對TCP除了辱罵還是辱罵!因為它太復雜了,作為一個低層的設施,如果太復雜,應用層的復雜空間便小了!編程的人,特別是socket編程的人,總是將IP以及網卡作為一個黑盒子,而著重看待TCP,他們對UDP是不屑一顧的,也不會關注什麼非合作UDP流量。這是搞通訊網絡和人和編程的人的本質區別,畢竟分工不同。但是我也是從研究TCP開始的,起初我並無法觸及路由器之類的東西,更不曉得什麼流量工程,可是到了後來當我懂了IP以後,我發現IP是多麼的美妙,可以讓人瞬間高潮!而TCP,就是一團亂麻,在產品系統測試的時候,一個TCP的疑難雜症可以讓一個研發人員折騰一下午甚至好幾天,而這幾天時間,測試人員爽了,沒什麼事,可以聊天,看新聞,這是多麼悲哀,讓人沒法釋懷!真TMD想給測試人員上椅刑!旋轉升降座椅一定會爆炸,菊花殘,滿地傷,花落人斷腸!
只可惜,昨天的事在我憤怒狀態時,徹底滅了這種格局!事情是這樣的。
客戶端A:128.129.1.2
網關外網口(連接客戶端):128.129.1.1
網關內網口(連接服務器):192.168.220.223
服務器:192.168.30.75
網關上做NAT的redirect轉換,將訪問服務器的流量重定向到本地的apache服務器,apache服務器行使正向代理功能(正向代理是一個重要概念,請深入理解)。規則如下:
iptables -t nat -A PREROUTING -d 192.168.30.75 -j REDIRECT --to-ports 80
多麼簡單的規則,然而多麼痛的領悟。
測試人員,嚴格說是黑盒測試人員(非白盒),測試功能就算了,不影響業務就可以了,偏偏要搞什麼抓包,關鍵是抓包還誤會我們!問題如下:
連接的建立以及數據的傳輸,均經過了代理,但是連接的拆除卻沒有被NAT,直接forward出去了,導致192.168.30.75服務器直接接收到了源IP地址為128.129.1.2的數據包。這是怎麼回事?!
實際上,這是毫無影響的,只要應用程序能夠堅持足夠長的時間!
我發現,這是服務器30.75主動斷開的連接,也就是它主動發送了FIN,此後客戶端發送了ACK,然而客戶端遲遲沒有發送自己方面的FIN,過了兩分鐘才發送了FIN/ACK,此時連接只是單方面斷開了。此時,在客戶端沒有發送FIN之前,它處在CLOSE_WAIT狀態。由於目前的產品是基於前一個產品構建的,在前一個產品中,我由於一些特殊的原因將conntrack的和TCP相關的timeout都減到了足夠小,比如我將TCP的conntrack的establish的timeout減少到了120秒(默認是5天),因此斷開連接時的各狀態timeout更小,因此在兩分鐘內,conntrack早就刪除了!
此時客戶端的FIN來了,由於conntrack已經刪除,網關會為其建立一個新的conntrack嗎?這要看有沒有設置loose,我當然設置了,也就是不再以syn為建立新conntrack的依據,按理說,Linux網關會創建一個新的conntrack,然而它攜帶了fin標志!這就意味著Linux不會創建新的conntrack!因此數據包就直接forward了。但是這不會造成什麼影響!因為攜帶fin的數據包擁有自己的控制通道timeout期限。到期後會自動轉換,這是和establish狀態的截然不同!這是為什麼呢?
這樣從TCP/IP的設計說起。有一種設計方案叫做帶內控制,也就是控制通道和數據通道共享一條網絡路徑,TCP協議就是這種設計的典型,另一種就是IP,比如ICMP就是IP的控制協議,然而你不能說IP不是一種完全的帶內控制,它雖然和TCP有所區別,那只是因為它的無狀態所致!
TCP的控制通道用標志來區分,攜帶SYN,FIN的數據段都是控制段,Linux的conntrack對TCP的行為就是以這種控制標志為依據的,如果來了一個establish的數據包-沒有syn,沒有fin,沒有查找到conntrack,在設置loose標志的情況下,Linux為創建一個新的conntrack項,然後對於控制信號,比如fin段,就不同了,Linux不會創建。然而這並不影響終端的結果,雖然由於Linux沒有NAT成功導致了數據不會正確到達服務器,可是由於已經fin了,終端會自動用timeout處理。
但是一定要注意方向,這可能會稍微帶來一些問題。如果主動發出的FIN被攔截,會有大問題嗎?不會的,因為主動發出FIN的一端不管它會不會被攔截,都會更改狀態,而被動關閉的那一端雖然收不到FIN,也會行使超時重傳限制權利,雖然可能慢一點。