天天看點

在Linux的連接配接跟蹤(nf_conntrack)中緩存私有資料省去每次查找

前面說過很多次,conntrack作為一中連接配接跟蹤機制,如果它本身是可擴充的,那麼将會是多麼令人激動的一件事,當你看了N多文檔代碼之後,你發現它确實是可以擴充的,但是卻沒有感到激動,因為你可能發現:

1.它可以注冊一個account擴充,但是計數機制卻很原始;

2.我希望增加一個新型的擴充,卻不得不重新編譯核心;

怎 麼辦?我曾經很生氣地默默指責過當初實作這個的人,想當然的認為将擴充本身也做成可擴充的,而不是寫死幾個特定的擴充将是一個多麼容易的事,我一直憋着沒 有去做這個實作,就是因為覺得它太簡單,在工作中也确實需要一個新的擴充,然而既有的擴充類型中沒有,為了不重新編譯核心,我隻好盜用了acct擴充。采 用了一個OO中典型的封裝方法:

...

是時候改變一些事态了。基于下面幾個原因,在周六的早上,我突然決定在周末完成它:

1.外部因素:好不容易感冒了,作為一個羸弱的人,我不希望得到别人的同情,隻需要獲得周末的安靜,感冒發燒是最好的選擇;

2.内部因素:年終總結完了,工作計劃也确定了,後面是個收網的過程,穩為重,不需要太激進,是以也就沒有什麼技術上不可控的因素,心理安了,事就可以開始做了;

可能我又要笑話自己了,不就寫個簡單的子產品麼?怎麼搞得跟諸葛孔明布陣一樣...如此感性且主觀一人怎麼就...

       不管怎麼講,這個子產品看起來确實是簡單的。然而一旦做起來,發現有兩個比較嚴重的問題:

1.反射内省問題

如 果conntrack的extend有128個slot,每個slot裡面放一個私有資料。問題是,程式怎麼知道哪個slot裡面有哪個資料。程式有能力 存儲,但是程式自己卻不知道這一點...這就是一個怪圈,你必須讓資料成為自描述的,或者就規定死第n号slot必須放路由項,第m個slot必須放 socket...現有的nf conntrack子產品使用了後一種方法,即枚舉nf_ct_ext_id做的事。

       可是我還是想随機選擇slot,這樣更加靈活。自描述的資料結構也看了不好,ASN.1太複雜,且核心資料更多的不是辨別屬性,而是定義一種行 為,google的protocol buffer也不是很合适,需要定義太多的回調函數來完成反射自省..後來我想了一個辦法,那就是定義個索引藍圖,辨別“slot索引的索引”,而不是标 識具體slot的位置。

       這就需要定義一個新的枚舉,定義藍圖:

然後定義一個數組來辨別真正的索引:

定義一個bitmap來表示slot的使用情況即可,具體的做法可以看代碼,一目則了然。

2.記憶體尋址問題 

内 核記憶體是寶貴的,不是說實體記憶體用不起,而是它的虛拟位址空間也是有限的,是以建議使用64位系統,如果是32位系統,如果希望核心儲存比較大的資料結 構,請在編譯的時候按照2G/2G或者1G/3G來拆分位址空間,前者情況使用者和核心各自占據2G,後者的話核心占3G,使用者僅僅占1G。

       也許就是因為存在這個記憶體問題,Linux的nf conntrack限制了extend的記憶體使用,其最大長度字段資料類型是u8。由于我知道我的系統,是以我将其改為了u16。你必須要知道的是,nf connrtack的extend記憶體使用時是連續的,你不能采用一個sizeof(char *)大小的空間儲存一個指針,然後這個指針指向一個超級大的連續空間...但是為什麼不能呢?還是因為代碼的普适問題,我了解我的系統,是以我可以使用保 存指針的做法。另外我還保留了數組的方式,總之,數組和指針分工是明确的,數組用于extend的尋址,而指針用于資料的擷取。

       還是在這裡貼一份備份,怕哪天github被wall了...

修改include/net/netfilter/nf_conntrack_extend.h:

增加include/net/netfilter/nf_conntrack_ext.h:

修改net/netfilter/nf_conntrack_core.c:

增加net/netfilter/nf_conntrack_ext.c:

測試程式nf_conntrack_private_data_auto_save_restore.c:

在測試程式中,我緩存了路由項以及到達本機資料包的socket,這樣僅僅查詢到conntrack就可以直接将路由和socket取出來了,取值的過程由于存在索引數組和索引的索引數組,是以就是數組下标尋址,不再需要查詢。

'

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1605198

繼續閱讀