linux核心netfilter連線跟蹤的hash演算法
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
原貼:http://zhuaxia.com/item/73625059linux核心netfilter連線跟蹤的hash演算法
linux核心中的netfilter是一款強大的基於狀態的防火牆,具有連線跟蹤(
netfilter的連線跟蹤的hash演算法是在Bob Jenkins的lookup2.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);
}
這是一個對於ipv6和ipv4的hash求值的通用實現。struct nf_conntrack_tuple是一個通用的連線的四元組,同時用於ipv4和ipv6,tcp,udp,sctp,icmp協議,所以,其定義比較複雜。可以把它理解為源地址,源埠,目的地址,目的埠。
#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;
};
有些混亂,就是源地址和目的地址,protonum和dir不知道為什麼這麼定義?
上面的hash演算法在僅用於ipv4時,可以進行優化。jhash函式是通用的hash函式,上面的目的是把ipv6的長串字元hash為一個32位整數,而ipv4的情況下,可以不用。
最後,使用%運算,這是非常低效的,Bob Jenkins專門指出了這一點。由於table的大小都為2的次方,所以,可以使用&的演算法。
另外,我認為Bob Jenkins的演算法是對於通用的數字的hash演算法,對於tcp連線這樣比較特殊的數字的hash,使用這麼複雜的演算法,是否有意義?簡單的加法運算是否更有效率?
lookup3.c與lookup2.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;
}
優化的版本,對3個32位整數進行hash運算,得到一個32位數字。
static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
{
return jhash_3words(a, b, 0, initval);
}
對2個32位整數進行hash運算,得到一個32位數字。
static inline u32 jhash_1word(u32 a, u32 initval)
{
return jhash_3words(a, 0, 0, initval);
}
對1個32位整數進行hash運算,得到一個32位數字。
上面的兩個巨集這是lookup3.c的核心hash演算法,hash的基礎。
hashword是通用的hash演算法,用於計算任意cpu架構,任意長度的字串的hash值。
不斷的把輸入的串k,每隔3位進行mix,直到完畢。返回final。
對於ipv4的話,可以直接把源地址,目的地址,(源埠<< 16)|目的埠,這三個整數進行final,得到hash值。
對於ip地址和埠號的特點,這種複雜的演算法是否真的有更好的hash效果,我持懷疑態度。