src dest ip + port的哈希函数

时间:2010-07-09 17:49:10

标签: c hash networking hashtable

所以,我正在研究用于散列4元组ip和端口以识别流的不同散列函数。

我遇到的是

((size_t)(key.src.s_addr) * 59) ^
((size_t)(key.dst.s_addr)) ^
((size_t)(key.sport) << 16) ^
((size_t)(key.dport)) ^
((size_t)(key.proto));

现在对于我的生活,我无法解释所使用的素数(59)。为什么不是31,然后为什么要通过将这项运动乘以2的力量来搞乱它。 是否有更好的哈希函数用于IP地址?

4 个答案:

答案 0 :(得分:12)

使用素数是因为当一个值乘以素数时,当其他类似操作累积在其上时,它往往具有更高的保持唯一概率。特定值59可以是任意选择的,也可以是有意的。这很难说。有可能59倾向于根据最可能的输入生成更好的值分布。

16的移位可能是因为端口被限制在2 ^ 16的范围内。该功能似乎是将源端口移动到位域的较高部分,同时将目标端口保留在下部。我认为这可以在我的下一段进一步解释。

乘法发生的另一个原因(并且这也适用于移位操作)是因为它打破了散列函数的关联性质。请记住,XOR是关联的,因此如果乘法不存在,则IP src = 192.168.1.1和dst = 192.168.1.254将散列为与src = 192.168.1.254和dst = 192.168.1.1(交换)相同的值。

答案 1 :(得分:3)

检查函数的输出以进行均匀分布。如果你不喜欢它,请插入一些不同的素数,直到你得到你喜欢的发行版。哈希可能是一个非常黑暗的艺术,没有“正确”的答案。

答案 2 :(得分:3)

就我个人而言,我认为你最好将四个IP字节作为无符号长整数读取,它会给你一个大致在0 - 2 ^ 32-1范围内的数字。然后,您可以计算出您希望在任何时间有多少流量,这将是您的索引表大小。

以2000为例。这意味着你想要将2 ^ 32个数字映射到大约2 ^ 11个indeces(流信息)。这是行不通的,因为如果填充到100%,则散列几乎不会起作用,甚至90%也很难。使用只填充50%(4000个余额)甚至25%(8000个)的索引表对今天的记忆没什么大不了的。

索引表的确切大小应该是不均匀的位置数,最好是素数。这是因为你很可能需要有一些溢出处理来处理冲突(两个或多个ip编号,它们在散列之后指向索引表中的相同位置) - 你将得到它。溢出处理应该是小于索引表大小的另一个素数。所有这些素数!无论如何,他们有什么关系?

我将举例说明(在C中):

idxsiz = prime(2000 * 2);    // 50% loading
ovfjmp = prime(idxsiz/3);

...

最初使用UNUSED标记(-1)填充idxjmp位置表。准备好DELETED标记(-2)。

您的IP号码进入系统并查找其流量记录(可能存在也可能不存在):

stoppos = ip % idxsiz;    /* modulo (division) just this once */
i = stoppos;
do
{
  if (index[i] == UNUSED) return NULL;
  if (index[i] != DELETED)
  {
    flowrecptr = &flow_record[index[i]];
    if (!flowrecptr->in_use) {/* hash table is broken */}
    if (flowrecptr->ip == ip) return flowrecptr;
  }
  i += ovfjmp;
  if (i >= idxsiz) i -= idxsiz;
}
while (i != stoppos);
return NULL; 

UNUSED用作从未使用过该索引的标记,并且搜索应该停止。 DELETED用作此索引已被使用但不再使用的标记。这意味着必须继续搜索。

这是你试图获得的时候。你从get获得了一个NULL,所以你需要做一个put,你首先找到包含UNUSED或DELETED的第一个索引位置。将此值替换为flow_record表上第一个/下一个空行的索引。将该行标记为in_use。将原始ip号放入flow_record行的ip成员中。

这是构建散列机制的一种非常简单但非常有效的方法。实际上,在此函数或函数失效后使用的特殊函数形式的每个优化都将提高散列的有效性。

使用素数将确保 - 在最坏的情况下所有索引位置都被占用 - 该机制将测试每个位置。为了说明这一点:假设idxsiz可以被ovfjmp整除:你将不会有太多的溢出处理。 35和7将导致位置0,7,14,21和28在索引跳转到0之前进行测试,而while测试将导致搜索停止。

---------------------- OOPS!

我错过了你想要的端口号。假设ip V4意味着6个字节的地址。将其读作无符号64位整数并清除前16位/ 2字节。然后你进行模数计算。

答案 3 :(得分:3)

布莱恩基登几乎总结了一下;乘法和移位旨在作为对称破坏者。因此,这可以捕捉机器A远程登机到机器B的假设情况,反之亦然,他们碰巧选择了相同的短暂的portnum。不是很常见,但并非不可能。 5元组中的大部分是非常不变的:协议来自一个非常小的域,{address,portnum}的一半也是如此。

假设有一个素数大小的散列表,魔术常数59可以被任何素数IMO替换。 (端口&lt;&lt; 16)也可以被另一个移位替换,只要没有位丢失或甚至是(port * some_other_prime)项。

对于两个幂大小的哈希表,5元组的所有(减1)成员应乘以(不同的)素数。 (在过去,当分割成本很高时,本来可以选择)