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位数字。
上面的两个宏这是lookup3.c的核心hash算法,hash的基础。
hashword是通用的hash算法,用于计算任意cpu架构,任意长度的字符串的hash值。
不断的把输入的串k,每隔3位进行mix,直到完毕。返回final。
对于ipv4的话,可以直接把源地址,目的地址,(源端口<<16)|目的端口,这三个整数进行final,得到hash值。
对于ip地址和端口号的特点,这种复杂的算法是否真的有更好的hash效果,我持怀疑态度。
linux内核netfilter连接跟踪的hash算法
linux内核中的netfilter是一款强大的基于状态的防火墙,具有连接跟踪(conntrack)的实现。conntrack是netfilter的核心,许多增强的功能,例如,地址转换(NAT),基于内容的业务识别(l7,layer-7module)都是基于连接跟踪。然而,netfilter的性能还有很多值得改进的地方。
netfilter的连接跟踪的hash算法是在BobJenkins的lookup2.c基础上的改进实现,BobJenkins已经推出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效果,我持怀疑态度。
相关文章推荐
- linux内核netfilter连接跟踪的hash算法
- Linux内核分析 - 网络[十七]:NetFilter之连接跟踪
- Linux内核分析 - 网络[十七]:NetFilter之连接跟踪
- Linux netfilter 学习笔记 之十 ip层netfilter的连接跟踪模块 学习小结
- 如何理解Netfilter中的连接跟踪机制
- 如何理解Netfilter中的连接跟踪机制
- Netfilter之连接跟踪实现机制初步分析
- Netfilter之连接跟踪的执行流程分析
- Linux 跟踪连接netfilter 调优
- Netfilter之连接跟踪实现机制初步分析
- Linux netfilter 学习笔记 之七 ip层netfilter的连接跟踪模块的概念及相关的数据结构分析
- Linux netfilter 学习笔记 之八 ip层netfilter的连接跟踪模块初始化
- Linux netfilter 学习笔记 之八 ip层netfilter的连接跟踪模块初始化
- Netfilter 连接跟踪与状态检测的实现
- Netfilter之连接跟踪实现机制初步分析
- 2.6.1*Linux内核中TCP的连接跟踪
- Netfilter之连接跟踪实现机制初步分析
- Linux netfilter 学习笔记 之七 ip层netfilter的连接跟踪模块的概念及相关的数据结构分析
- Netfilter中的连接跟踪系统
- Linux netfilter 学习笔记 之九 ip层netfilter的连接跟踪模块代码分析