ip_conntrack為無狀態無連接的IP增加了一個流記錄機制,你可以把任何和一個流相關的東西放進去,但是放在哪裡呢?原則上ip_conntrack應該是一個可以無限擴展的東西,但事實上,內核的設計者或者說Netfilter的ip_conntrack設計者並沒有給用戶程序員留下任何可以擴展它的接口和機制,你只能用現有的連接跟蹤機制,雖然可以通過nf_ct_extend_register接口注冊一個nf_ct_ext_type,但是這個接口也只是內核源碼樹自己使用的,你無法從外部定義一個心的nf_ct_ext_type。
既然有一個nf_ct_extend_register,為何不讓從外部定一個nf_ct_ext_type,然後注冊進去呢?除了C語言定義的數據結構不能自己解釋自己實現反序列化之外,我覺得這是ip_conntrack代碼樹的一個缺陷,但是也許是內核開發模式和應用開發模式的不同導致的。一般的,內核API非常不穩定,對於應用來講,內核的系統調用接口盡可能保持穩定就可以了,至於內部則完全是一個黑盒子。再者,內核開發需要一定的功底,不僅僅是你熟悉一些編程技巧和熟讀API文檔就能搞得定的。以上的原因,或者說是我的個人猜測,導致了不允許對ip_conntrack的結構體本身進行任何修改,你能改的僅僅是實現邏輯。Linux內核怎麼做到這種限制的呢?很簡單啊,把相關的頭文件放到內核開發的頭文件目錄就可以了,事實上,只要是那個目錄的頭文件,你就別指望修改任何結構體和聲明,因為那些是要參與符號CRC計算的。
理解了ip_conntrack的extend實際上並不能擴展之後,下面就展示一下老濕是怎麼擴展它的。首先看一下ip_conntrack的extend結構是怎麼組織的。實際上,我覺得這種組織非常好,非常緊湊,不容易產生內存碎片,它之所以如此緊湊是因為所有的extend相關的內容在內存中全部都是連續的。連續的內存當然沒有指針尋址不連續內存方便,但是使用偏移也是不錯的,更大的優勢是申請/拷貝/釋放比較方面,不用考慮深拷貝淺拷貝的問題。類似的數據結構還有iptables的rule在內核中的表示。
連續內存的表示法的最大好處就是可以任意擴展,因為它完全是靠偏移來定位位置的,如果你知道C++和JAVA這類OO語言,就會知道,一個普通類的內存布局中,處在開頭的總是基類,所謂的擴展正是處在緊接著基類的內存位置,從而構成了一個子類,當然這只是從實現上來講的,這裡不談思想!當然這也不是一種有性生殖行為,因為有性生殖從來都不是連續的,你不能說孩子是一個爸爸(是爸爸的一部分...),但是在內存連續的世界裡,我們可以說:
struct B {
struct A a;
....
char *privatedata
};
是一個struct A,畢竟我可以把struct B從a字段往後的字段切掉。知道了這一切之後,我就可以對任意Linux預定義好的所有的nf_ct_ext_type進行任意的擴展了!事實上,我只需要擴展一個指針!雖然我很崇尚連續內存的布局,但是在我還是比較喜歡靈活,畢竟現在的內存價格已經不貴了,干嘛那麼在乎內存啊。僅僅擴展一個指針,你想把它解釋成一個什麼機構都行!我們以一幅圖來做解釋:
理解了上面的那個圖,就明白下面的定義了,不需要改變nf_conn_counter的結構體,只改變它的大小即可,即:
struct nf_conn_counter {
u_int64_t packets;
u_int64_t bytes;
};
的大小不是sizeof(struct nf_conn_counter),而是sizeof(struct nf_conn_counter)+sizeof(char *),定義為:
struct nf_conn_counter {
u_int64_t packets;
u_int64_t bytes;
char *p;
};
但是這樣便改變了nf_conn_counter結構體的定義,但是考慮以上結構體, 在內存結構上,等價於:
struct my_st {
struct nf_conn_counter nfc;
char *p;
};
這樣沒有改變結構體的定義,結構體my事實上真的就“是一個”結構體nf_conn_counter,在這之後,需要修改的地方真的就不多了,僅僅是一個nf_ct_ext_type的len字段的修改。剩下的工作就是,在需要設置或者取出自己自定義的結構體的時候,按照下面這樣:
struct my_st *ms = (struct my_st *)a_instance_of_struct_of_nf_conn_counter;
取出my_st的指針即可。接下來就可以訪問my_st裡面的p了,至於p是什麼,自有作者解釋。為何我不提倡連續內存而是用一個指針,關鍵是我覺得並不是每個conntrack都需要這個字段,為了節省內存只為需要的結構體分配內存,只能用指針。
這就是老濕的做法,JAVA編程思想,真的有思想。老濕很低級,不怎麼懂編程,不符合編程高手的口味,但是老濕懂濕想,老濕知道這個機器奴役人的世界是多麼的殘酷,一個數字化的世界是多麼的可悲,而是是編程的人特別是以編程為命的人所很難理解的...