猛士設計了Netfilter,在失眠的時候就有事做了,安息日應守為聖日,否則會激怒神,是以雙休日我一般不學習和工作,相反,我會在午夜玩一些自己喜歡的東西。我沒有受過洗,不是因為不是笃信者,沒有安息夜...
這個問題就不多說了,總之,NAT裝置将所有資料包的大量不同的源位址都轉換為了單一的或者少數幾個位址,這個轉換動作和TCP伺服器的PAWS機制一起工作的時候會導緻無法建立連接配接的問題。
簡單的講,解決辦法有兩個,一個是在用戶端禁掉TCP的時間戳機制,另一個是在TCP服務端禁掉TCP的時間戳機制,可是一般而言,這兩個地方都不是我們所能控制的,比如,你能禁掉每一個智能手機的TCP時間戳,也不能指望手機使用者去做這件事,你同樣也不能指望可以順利地禁掉伺服器的TCP時間戳,于是,第三個辦法就出爐了,那就是在中間的NAT裝置上修改掉TCP資料包,實際上隻要修改TCP初始化的SYN包即可,将時間戳選項去掉,隻所有這麼做是可行的,歸功于TCP協定和IP協定的協定頭是規則且簡單的。
任何協定,如果沒有被設計成可擴充性的,那它就不是一個好的協定。一個好的協定,在其基礎部分應該取定長的格式,而其擴充的部分,應該是一定範圍内的變長格式,不管是IP協定還是TCP協定,協定頭都有一個“頭長度”這麼一個字段,該字段正是為了表示所謂的擴充協定頭,否則如果都是定長的,也就不需要該字段了,是以可以說,IP協定和TCP協定都是比較好的協定。
識别了協定好壞的标準之後,擴充字段如何布局就純粹成了一個編碼問題,一般而言,“類型-長度-值”的方式是首選,它可以很友善的編碼任意類型,任意長度的擴充字段,在TCP的協定頭擴充中,其名稱叫做options,即TCP選項,它便是采用了上述的編碼方式,每一個選項我可以稱之為一個“塊”,整個TCP options由多個塊組成,每一個塊由下面的結構組成:
<a href="http://s3.51cto.com/wyfs02/M00/12/05/wKiom1L0TbiB2FG2AAAonfbOnGc782.jpg" target="_blank"></a>
注意,有一種TCP選項叫做NOP,它是為了確定TCP協定頭結束在4位元組對齊的位置,我們可以從RFC793的3.1節看出:
Padding: variable
The TCP header padding is used to ensure that the TCP header ends
and data begins on a 32 bit boundary. The padding is composed of
zeros.
在本文中,我心愛的NOP并不是為了填充的,而是為了用其替換時間戳選項。我不知道别的系統怎麼處理NOP,反正在Linux中,是直接忽略NOP,而這種忽略正是提供了一種将時間戳選項替換為NOP的可能性。
Netfilter原則上可以将所有的資料包“偷走”,從标準協定棧偷走,這麼極端的事它都可以做,還有什麼不能做的呢?和往常一樣,還是寫一個iptables的module,包括兩個元件,一個是核心子產品,另一個是iptables的使用者态子產品。我依然将取名字這件事擱置,是以我的這個target姑且确定為YYY,核心子產品代碼如下:
該有的解釋都在注釋裡面了。使用者态的子產品就不貼出了了,例行公事而已,沒有任何邏輯,畢竟目前的版本YYY target不需要任何參數。
隻需要簡單地在NAT網關添加一個iptables規則:
iptables -A FORWARD -p tcp -......-j YYY
接下來的時間裡,你将不會再面對NAT裝置的TCP timestamps的問題了。值得注意的是,由于TCP服務端僅僅針對建立連接配接來做檢查,是以可以不必對非SYN包來做YYY target,我自己在測試的時候,抓包結果如下:
建立連接配接的SYN包:
<a href="http://s3.51cto.com/wyfs02/M02/12/04/wKioL1L0Ta-BGKLMAAvXeKmrggo257.bmp" target="_blank"></a>
ESTABLISHED狀态的非SYN包:
<a href="http://s3.51cto.com/wyfs02/M01/12/05/wKiom1L0TezBKJSTAAjqJPOsh6Q402.bmp" target="_blank"></a>
對于一個在IT領域從業5年以上的人而言,任何問題靠技術手段解決都不是個事,關鍵是如何徹底地解決,相比于每次重複相同的排障過程,一次性解決會好很多,當然,這也許會減少一些出頭露面的機會。這個timestamps+NAT問題是如此簡單,以至于百行量級的代碼就能搞定,但是為何加入這項功能的裝置卻如此之少,反之,網上的這方面的文章卻是汗牛充棟(當然我也貢獻過幾篇)。難道是因為有些伺服器确實要用到timestamps嗎?而正常的理由就是,不能改變TCP的語義!我想罵了,NAT保留IP的語義了嗎?它為何後來成了标準的東西,原始的IP語義是希望任意IP主機可以僅依靠IP路由雙向互聯互通,結果有了狀态NAT以後,事情複雜了,于是更加複雜的各種打洞技術被呼喚出來,幾年前我曾經遇到過一個人,自诩他實作的打洞技術世界第一,我C-T-M-D,他就是一小醜!
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1357017