1. 程式人生 > >linux核心netfilter連線跟蹤的hash演算法

linux核心netfilter連線跟蹤的hash演算法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                原貼:http://zhuaxia.com/item/73625059

linux核心netfilter連線跟蹤的hash演算法

 

linux核心中的netfilter是一款強大的基於狀態的防火牆,具有連線跟蹤(

conntrack)的實現。conntracknetfilter的核心,許多增強的功能,例如,地址轉換(NAT),基於內容的業務識別(l7 layer-7 module)都是基於連線跟蹤。然而,netfilter的效能還有很多值得改進的地方。

netfilter的連線跟蹤的hash演算法是在Bob Jenkinslookup2.c基礎上的改進實現,Bob Jenkins已經推出lookup3.c的實現,見地址:http://burtleburtle.net/bob/hash/http://burtleburtle.net/bob/c/lookup3.c

netfilter中的hash求值的程式碼如下:

 

static u_int32_t __hash_conntrack(const struct nf_conntrack_tuple *tuple,

                              unsigned int size, unsigned int rnd)

{

       unsigned int a, b;

       a = jhash((void *)tuple->src.u3.all, sizeof(tuple->src.u3.all),

                ((tuple->src.l3num) << 16) | tuple->dst.protonum);

       b = jhash((void *)tuple->dst.u3.all, sizeof(tuple->dst.u3.all),

                     (tuple->src.u.all << 16) | tuple->dst.u.all);

 

       return jhash_2words(a, b, rnd) % size;

}

 

static inline u_int32_t hash_conntrack(const struct nf_conntrack_tuple *tuple)

{

       return __hash_conntrack(tuple, nf_conntrack_htable_size,

                            nf_conntrack_hash_rnd);

}

 

這是一個對於ipv6ipv4hash求值的通用實現。struct nf_conntrack_tuple是一個通用的連線的四元組,同時用於ipv4ipv6tcpudpsctpicmp協議,所以,其定義比較複雜。可以把它理解為源地址,源埠,目的地址,目的埠。

#define NF_CT_TUPLE_L3SIZE  4

union nf_conntrack_man_l3proto {

       u_int32_t all[NF_CT_TUPLE_L3SIZE];

       u_int32_t ip;

       u_int32_t ip6[4];

};

其實這就是ip地址。

union nf_conntrack_man_proto

{

       /* Add other protocols here. */

       u_int16_t all;

 

       struct {

              u_int16_t port;

       } tcp;

       struct {

              u_int16_t port;

       } udp;

       struct {

              u_int16_t id;

       } icmp;

       struct {

              u_int16_t port;

       } sctp;

};

這就是埠。

struct nf_conntrack_man

{

       union nf_conntrack_man_l3proto u3;

       union nf_conntrack_man_proto u;

       /* Layer 3 protocol */

       u_int16_t l3num;

};

目的地址和埠,l3num不知道是什麼東西?

struct nf_conntrack_tuple

{

       struct nf_conntrack_man src;

 

       /* These are the parts of the tuple which are fixed. */

       struct {

              union {

                     u_int32_t all[NF_CT_TUPLE_L3SIZE];

                     u_int32_t ip;

                     u_int32_t ip6[4];

              } u3;

              union {

                     /* Add other protocols here. */

                     u_int16_t all;

 

                     struct {

                            u_int16_t port;

                     } tcp;

                     struct {

                            u_int16_t port;

                     } udp;

                     struct {

                            u_int8_t type, code;

                     } icmp;

                     struct {

                            u_int16_t port;

                     } sctp;

              } u;

 

              /* The protocol. */

              u_int8_t protonum;

 

              /* The direction (for tuplehash) */

              u_int8_t dir;

       } dst;

};

有些混亂,就是源地址和目的地址,protonumdir不知道為什麼這麼定義?

 

 

上面的hash演算法在僅用於ipv4時,可以進行優化。jhash函式是通用的hash函式,上面的目的是把ipv6的長串字元hash為一個32位整數,而ipv4的情況下,可以不用。

 

最後,使用%運算,這是非常低效的,Bob Jenkins專門指出了這一點。由於table的大小都為2的次方,所以,可以使用&的演算法。

 

另外,我認為Bob Jenkins的演算法是對於通用的數字的hash演算法,對於tcp連線這樣比較特殊的數字的hash,使用這麼複雜的演算法,是否有意義?簡單的加法運算是否更有效率?

 

lookup3.clookup2.c有很大的不同。lookup3.c中,使用了final巨集,和mix巨集分開。而lookup2.c中沒有使用final巨集。

 

linux下的修改過的hash函式:

static inline u32 jhash(const void *key, u32 length, u32 initval)

通用的hash函式,對任意長度的key字串進行hash運算,得到一個32位數字。

 

static inline u32 jhash2(u32 *k, u32 length, u32 initval)

優化的版本,對任意長度的32位整數進行hash運算,得到一個32位數字。

static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)

{

       a += JHASH_GOLDEN_RATIO;

       b += JHASH_GOLDEN_RATIO;

       c += initval;

 

       __jhash_mix(a, b, c);

 

       return c;

}

優化的版本,對332位整數進行hash運算,得到一個32位數字。

static inline u32 jhash_2words(u32 a, u32 b, u32 initval)

{

       return jhash_3words(a, b, 0, initval);

}

232位整數進行hash運算,得到一個32位數字。

 

static inline u32 jhash_1word(u32 a, u32 initval)

{

       return jhash_3words(a, 0, 0, initval);

}

132位整數進行hash運算,得到一個32位數字。

 

 

 

 

上面的兩個巨集這是lookup3.c的核心hash演算法,hash的基礎。

 

 

 

 

 

hashword是通用的hash演算法,用於計算任意cpu架構,任意長度的字串的hash值。

 

不斷的把輸入的串k,每隔3位進行mix,直到完畢。返回final

 

對於ipv4的話,可以直接把源地址,目的地址,(源埠<< 16)|目的埠,這三個整數進行final,得到hash值。

 

對於ip地址和埠號的特點,這種複雜的演算法是否真的有更好的hash效果,我持懷疑態度。

            

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述