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

linux内核中的netfilter是一款强大的基于状态的防火墙,具有连接跟踪(conntrack)的实现。conntrack是netfilter的核心,许多增强的功能,例如,地址转换(NAT),基于内容的业务识别(l7, layer-7 module)都是基于连接跟踪。然而,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位数字。

#define mix(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; /
}
#define final(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_t hashword(
const uint32_t *k,                   /* the key, an array of uint32_t values */
size_t          length,               /* the length of the key, in uint32_ts */
uint32_t        initval)         /* the previous hash, or an arbitrary value */
{
  uint32_t a,b,c;
 
  /* Set up the internal state */
  a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
 
  /*------------------------------------------------- handle most of the key */
  while (length > 3)
  {
    a += k[0];
    b += k[1];
    c += k[2];
    mix(a,b,c);
    length -= 3;
    k += 3;
  }
 
  /*------------------------------------------- handle the last 3 uint32_t's */
  switch(length)                     /* all the case statements fall through */
  { 
  case 3 : c+=k[2];
  case 2 : b+=k[1];
  case 1 : a+=k[0];
    final(a,b,c);
  case 0:     /* case 0: nothing left to add */
    break;
  }
  /*------------------------------------------------------ report the result */
  return c;
}
 

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

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

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

对于ip地址和端口号的特点,这种复杂的算法是否真的有更好的hash效果,我持怀疑态度。

linux内核netfilter连接跟踪的hash算法的更多相关文章

  1. Linux内核分析之跟踪分析Linux内核的启动过程

    一.实验过程 使用实验楼虚拟机打开shell cd LinuxKernel/ qemu -kernel linux-/arch/x86/boot/bzImage -initrd rootfs.img ...

  2. linux内核netfilter模块分析之:HOOKs点的注册及调用

    转自;http://blog.csdn.net/suiyuan19840208/article/details/19684883 -1: 为什么要写这个东西?最近在找工作,之前netfilter 这一 ...

  3. linux内核学习之三 跟踪分析内核的启动过程

    一   前期准备工作       1 搭建环境 1.1下载内核源代码并编译内核 创建目录,并进入该目录: 下载源码: 解压缩,并进入该目录:xz -d linux-3.18.6.tar.xz tar ...

  4. linux内核中的C语言常规算法(前提:你的编译器要支持typeof和type)

    学过C语言的伙伴都知道,曾经比较两个数,输出最大或最小的一个,或者是比较三个数,输出最大或者最小的那个,又或是两个数交换,又或是绝对值等等,其实这些算法在linux内核中通通都有实现,以下的代码是我从 ...

  5. Linux内核中常用的数据结构和算法(转)

    知乎链接:https://zhuanlan.zhihu.com/p/58087261 Linux内核代码中广泛使用了数据结构和算法,其中最常用的两个是链表和红黑树. 链表 Linux内核代码大量使用了 ...

  6. 分析linux内核中的slub内存管理算法

    1. 分析的linux内核源码版本为4.18.0 2. 与slub相关的内核配置项为CONFIG_SLUB 3. 一切都从一个结构体数组kmalloc_caches开始,它的原型如下: ] __ro_ ...

  7. Linux内核中的有关Page的算法

    static inline int get_order(unsigned long size) { int order; size = (size-1) >> (PAGE_SHIFT-1) ...

  8. Linux 内核SBus连接

    当大部分计算机配备有 PCI 或 ISA 接口总线, 大部分老式的基于 SPARC 的工作站使用 SBus 来连接它们的外设. SBus 使一个非常先进的设计, 尽管它已出现很长时间. 它意图是处理器 ...

  9. Netfilter&iptables:如何理解连接跟踪机制?

    如何理解Netfilter中的连接跟踪机制? 本篇我打算以一个问句开头,因为在知识探索的道路上只有多问然后充分调动起思考的机器才能让自己走得更远.连接跟踪定义很简单:用来记录和跟踪连接的状态. 问:为 ...

随机推荐

  1. 关于Python的那点吐槽

    之前听到过别人有说过Python只是一个玩具做不了大项目,我当时是嗤之以鼻的,不说豆瓣这样的公司采用Python做的网站,GitHub上那么多大项目都是用Python写的,怎么能说Python只是一个 ...

  2. BZOJ2568 [国家集训队2012]比特集合

    Description 比特集合是一种抽象数据类型(Abstract Data Type) ,其包含一个集合S,并支持如下几种操作: INS M : 将元素 M 插入到集合S中: DEL M : 将集 ...

  3. sublime3下载安装及常用插件、浏览器预览设置

    之前与学习前端有关的软件都安装在了实验室电脑上,最近由于要放寒假(也许我寒假回去会学习呢),于是得在笔记本电脑上重新安装一遍.几个软件各种出错,花了一下午才安装好,必须记录下来啊! 这篇文章主要介绍s ...

  4. 03--CSS布局设置

    一 盒模型 盒模型 在CSS中,"box model"这一术语是用来设计和布局时使用,然后在网页中基本上都会显示一些方方正正的盒子.我们称为这种盒子叫盒模型. 盒模型有两种:标准模 ...

  5. <Android Framework 之路> N版本 Framework Camera的一些改动

    前言 Android N版本最近发布,Nougat是否好吃,不得而知,慢慢看下~ 感谢AndroidXref这个网站,给开发者提供了大量的便捷~以后学习Android就靠它了. N版本上Framewo ...

  6. Ubuntu,忘记了root密码,怎么重置?

    进入单用户模式: 1.开机到grub时,用上下键移到第二行的恢复模式,按e(注意不是回车) 即Ubuntu,With Linux 3.2.0-23-generic(recovery mode) 2.把 ...

  7. 修改maven默认的jdk版本

    修改maven默认的jdk版本,想改彻底需要在maven的全局配文件(settings.xml)增加以下信息:   在profiles 节点下增加: <profile> <id> ...

  8. oracle基础之游标的理解与使用

    关于游标,首先要知道游标的定义. 游标,是内存中的一款区域,用来存放select的结果集 游标用来处理从数据库中检索的多行记录(使用select语句).利用游标,程序可以逐个的处理和遍历一次索引返回的 ...

  9. javascript通用代码合集

    1.逐一绑定操作到window.onload上 //func:新函数 function addLoadEvent(func){ //把现有的window.onload事件处理函数的值存入变量oldon ...

  10. [转]实现Hive数据同步更新的shell脚本

    引言: 上一篇文章<Sqoop1.4.4 实现将 Oracle10g 中的增量数据导入 Hive0.13.1 ,并更新Hive中的主表>http://www.linuxidc.com/Li ...