您的位置:首页 > 运维架构 > Linux

linux内核netfilter连接跟踪的hash算法

2007-10-14 22:06 609 查看
原贴:http://zhuaxia.com/item/73625059

linux内核netfilter连接跟踪的hash算法

linux内核中的netfilter是一款强大的基于状态的防火墙,具有连接跟踪(conntrack)的实现。conntrack是netfilter的核心,许多增强的功能,例如,地址转换(NAT),基于内容的业务识别(l7,layer-7module)都是基于连接跟踪。然而,netfilter的性能还有很多值得改进的地方。
netfilter的连接跟踪的hash算法是在BobJenkins的lookup2.c基础上的改进实现,BobJenkins已经推出lookup3.c的实现,见地址:http://burtleburtle.net/bob/hash/和http://burtleburtle.net/bob/c/lookup3.c
netfilter中的hash求值的代码如下:

staticu_int32_t__hash_conntrack(conststructnf_conntrack_tuple*tuple,
unsignedintsize,unsignedintrnd)
{
unsignedinta,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);

returnjhash_2words(a,b,rnd)%size;
}

staticinlineu_int32_thash_conntrack(conststructnf_conntrack_tuple*tuple)
{
return__hash_conntrack(tuple,nf_conntrack_htable_size,
nf_conntrack_hash_rnd);
}

这是一个对于ipv6和ipv4的hash求值的通用实现。structnf_conntrack_tuple是一个通用的连接的四元组,同时用于ipv4和ipv6,tcp,udp,sctp,icmp协议,所以,其定义比较复杂。可以把它理解为源地址,源端口,目的地址,目的端口。
#defineNF_CT_TUPLE_L3SIZE4
unionnf_conntrack_man_l3proto{
u_int32_tall[NF_CT_TUPLE_L3SIZE];
u_int32_tip;
u_int32_tip6[4];
};
其实这就是ip地址。
unionnf_conntrack_man_proto
{
/*Addotherprotocolshere.*/
u_int16_tall;

struct{
u_int16_tport;
}tcp;
struct{
u_int16_tport;
}udp;
struct{
u_int16_tid;
}icmp;
struct{
u_int16_tport;
}sctp;
};
这就是端口。
structnf_conntrack_man
{
unionnf_conntrack_man_l3protou3;
unionnf_conntrack_man_protou;
/*Layer3protocol*/
u_int16_tl3num;
};
目的地址和端口,l3num不知道是什么东西?
structnf_conntrack_tuple
{
structnf_conntrack_mansrc;

/*Thesearethepartsofthetuplewhicharefixed.*/
struct{
union{
u_int32_tall[NF_CT_TUPLE_L3SIZE];
u_int32_tip;
u_int32_tip6[4];
}u3;
union{
/*Addotherprotocolshere.*/
u_int16_tall;

struct{
u_int16_tport;
}tcp;
struct{
u_int16_tport;
}udp;
struct{
u_int8_ttype,code;
}icmp;
struct{
u_int16_tport;
}sctp;
}u;

/*Theprotocol.*/
u_int8_tprotonum;

/*Thedirection(fortuplehash)*/
u_int8_tdir;
}dst;
};
有些混乱,就是源地址和目的地址,protonum和dir不知道为什么这么定义?

上面的hash算法在仅用于ipv4时,可以进行优化。jhash函数是通用的hash函数,上面的目的是把ipv6的长串字符hash为一个32位整数,而ipv4的情况下,可以不用。

最后,使用%运算,这是非常低效的,BobJenkins专门指出了这一点。由于table的大小都为2的次方,所以,可以使用&的算法。

另外,我认为BobJenkins的算法是对于通用的数字的hash算法,对于tcp连接这样比较特殊的数字的hash,使用这么复杂的算法,是否有意义?简单的加法运算是否更有效率?

lookup3.c与lookup2.c有很大的不同。lookup3.c中,使用了final宏,和mix宏分开。而lookup2.c中没有使用final宏。

linux下的修改过的hash函数:
staticinlineu32jhash(constvoid*key,u32length,u32initval)
通用的hash函数,对任意长度的key字符串进行hash运算,得到一个32位数字。

staticinlineu32jhash2(u32*k,u32length,u32initval)
优化的版本,对任意长度的32位整数进行hash运算,得到一个32位数字。
staticinlineu32jhash_3words(u32a,u32b,u32c,u32initval)
{
a+=JHASH_GOLDEN_RATIO;
b+=JHASH_GOLDEN_RATIO;
c+=initval;

__jhash_mix(a,b,c);

returnc;
}
优化的版本,对3个32位整数进行hash运算,得到一个32位数字。
staticinlineu32jhash_2words(u32a,u32b,u32initval)
{
returnjhash_3words(a,b,0,initval);
}
对2个32位整数进行hash运算,得到一个32位数字。

staticinlineu32jhash_1word(u32a,u32initval)
{
returnjhash_3words(a,0,0,initval);
}
对1个32位整数进行hash运算,得到一个32位数字。

#definemix(a,b,c)/

{/

a-=c;a^=rot(c,4);c+=b;/

b-=a;b^=rot(a,6);a+=c;/

c-=b;c^=rot(b,8);b+=a;/

a-=c;a^=rot(c,16);c+=b;/

b-=a;b^=rot(a,19);a+=c;/

c-=b;c^=rot(b,4);b+=a;/

}


#definefinal(a,b,c)/

{/

c^=b;c-=rot(b,14);/

a^=c;a-=rot(c,11);/

b^=a;b-=rot(a,25);/

c^=b;c-=rot(b,16);/

a^=c;a-=rot(c,4);/

b^=a;b-=rot(a,14);/

c^=b;c-=rot(b,24);/

}


上面的两个宏这是lookup3.c的核心hash算法,hash的基础。

uint32_thashword(

constuint32_t*k,/*thekey,anarrayofuint32_tvalues*/

size_tlength,/*thelengthofthekey,inuint32_ts*/

uint32_tinitval)/*theprevioushash,oranarbitraryvalue*/

{

uint32_ta,b,c;


/*Setuptheinternalstate*/

a=b=c=0xdeadbeef+(((uint32_t)length)<<2)+initval;


/*-------------------------------------------------handlemostofthekey*/

while(length>3)

{

a+=k[0];

b+=k[1];

c+=k[2];

mix(a,b,c);

length-=3;

k+=3;

}


/*-------------------------------------------handlethelast3uint32_t's*/

switch(length)/*allthecasestatementsfallthrough*/

{

case3:c+=k[2];

case2:b+=k[1];

case1:a+=k[0];

final(a,b,c);

case0:/*case0:nothinglefttoadd*/

break;

}

/*------------------------------------------------------reporttheresult*/

returnc;

}


hashword是通用的hash算法,用于计算任意cpu架构,任意长度的字符串的hash值。

不断的把输入的串k,每隔3位进行mix,直到完毕。返回final。

对于ipv4的话,可以直接把源地址,目的地址,(源端口<<16)|目的端口,这三个整数进行final,得到hash值。

对于ip地址和端口号的特点,这种复杂的算法是否真的有更好的hash效果,我持怀疑态度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: