走火入魔之後,你會覺得需要趕緊将“基于五元組的資料流”改成“基于應用層協定固定偏移的資料流”,趕緊動手,越快越好!于是此人在支援zone conntrack的Linux 3.17核心上為nf_conn增加了幾個字段:
bool l7; //布爾型,表示是否要進行layer7的比對。
u32 offset; //應用層流辨別的偏移
u32 offlen; //應用層流辨別的長度
以上的三個字段在CT target中被設定,同時被設定的還有zone,它表明:
凡 是屬于zone $id的資料包都用應用層固定偏移定義的固定長度的流辨別來識别一個流,而不再使用傳統的五元組來識别一個流。重新定義tuple,同樣增加一個bool 型l7,表示它是否是應用層的流辨別,同時增加一個MAX_IDLEN長度的數組sid,這意味着流辨別别最長是MAX_IDLEN位元組。
話說以上就是基本的資料定義,那麼在代碼邏輯上,修改也不難,主要是修改resolve_normal_ct函數,取出tmpl模闆中的l7,如果它非 0,那就表明需要“應用層流辨別”來識别流,此時根據offset,offlen字段,定位到 [iphdr+iphdrlen+transphdrlen]這個位置,取出offlen位元組的資料,作為hash計算的key計算hash值,在 __nf_conntrack_find_get之前,tuple被填充成了應用層的sid,同時置位tuple的l7,這意味着在find conntrack的時候,比較的是tuple的sid值而不是五元組。最後,在conn confirm的時候,将conntrack按照其offset,offlen定位的payload資訊表示的sid(它已經被放進了tuple結構中, 由其char sid[MAX_IDLEN];字段來辨別)來進行插入。
修改,編譯,測試總共用了不到兩個小時(買的iMac太TMD給力了!!)。随性,随玩,吃點東西,喝口茶,開始得瑟。這個人就是我啊!
開始思考所作所為的意義後,也是一個檢討的過程!我發現,突然發現,所作的一切都沒有意義。conntrack結構體并沒有儲存什麼用于應用層的資訊,雖 然我自己擴充了它,能讓它儲存很多東西,比如路由,socket,等,但是事實上還沒有什麼地方真的用到了這些,即這些都是自己沒事玩玩的東西。 conntrack中儲存的最重要的資訊就是NAT資訊,即tuple資訊,這個tuple是基于傳統5元組的,你想啊,如果我用基于sessionID 的應用層資訊來辨別一個tuple,那麼NAT怎麼辦?如果用戶端的IP位址發生變化,即使sessionID不變,NAT還是要重新做,還是得不到任何 益處。我的本意就是能省去由于IP位址,端口發生變化後的那一系列重新操作,但是最終還是沒有省,因為改變的是IP和端口,需要重新修改或者修飾的依然是 IP和端口這些資訊。
如果上面的代碼是寫在了紙上,很顯然,我會将其撕碎,然後扔進垃圾桶...
Linux 最新的核心已經支援了UDP的reuseport選項,這個機制可以很好地為UDP的負載均衡服務,如果不了解可以bing一下。它之是以可以做負載均 衡,就是它通過一個固定的5元組來計算一個固定hash,然後基于這個固定hash将一個資料包分發到固定的socket,如果IP位址不發生變化,一切 都會很好,但是IP位址在移動環境下會發生變化,這就意味着5元組資訊發生了變化,那麼重新計算的hash将也會發生變化(不發生變化那是碰撞了!),這 就意味着這個變化了IP的用戶端發出的下一個UDP資料包将可能被分發給别的socket,這在基于UDP的長連接配接服務中是不希望發生的。以下是 __udp4_lib_lookup核心代碼:
注釋中提到了大修,意思是,我必須将一個skb傳到這裡,才能根據setsockopt的參數reuseport标志,sid的offset,sid的offlen來擷取sid,然後計算hash,但這個修理很容易,重新編譯一下核心即可。
在UDP的reuseport中采用sessionID識别一個流是很爽的一件事,因為此時資料已經到傳輸層了,除卻重新封裝的資料包,基本都是達到本機 某個UDP服務的,資料包已經到達此地,說明5元組相關的鑒别比如NAT之類的已經完全通過,下一步就是往應用層送資料了,此時根據應用層的sid來識别 一個流,就能確定即便是用戶端IP改變了,它發出的請求也能到達同一個UDP服務線程...這也為移動時代提供了一個好的實景,在五元組頻繁更換的年代, 如何保持應用層不斷開...
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1612808