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

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

2014-06-21 23:25 1051 查看
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效果,我持怀疑态度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: