将IP地址与大型路由条目匹配的最有效方法是什么?

时间:2014-06-16 09:50:39

标签: algorithm ip-address subnet

想象有防火墙,系统管理员阻止了许多子网,也许是特定国家/地区的所有子网。

例如:

192.168.2.0 / 255.255.255.0
223.201.0.0 / 255.255.0.0
223.202.0.0 / 255.254.0.0
223.208.0.0 / 255.252.0.0
....

要确定IP地址是否已被阻止,防火墙可能会使用以下算法。

func blocked(ip)
    foreach subnet in blocked_subnets
        if in_subnet(subnet, ip)
            return true
    return false

但是,该算法需要太多时间来运行,时间复杂度为O(n)。如果路由表包含太多条目,则网络几乎不可用。

是否有更有效的方法将IP地址与大型路由条目相匹配?它是基于某种树/图(特里?)我想。我已经阅读了有关最长前缀匹配和Trie的内容,但没有明白这一点。

5 个答案:

答案 0 :(得分:13)

你真正需要的是一个trie,有四个等级。每个非叶节点包含最多256个子节点的数组。每个节点还包含子网掩码。所以,举个例子:

192.168.2.0 / 255.255.255.0
223.201.0.0 / 255.255.0.0
223.202.0.0 / 255.254.0.0
223.208.0.0 / 255.252.0.0

你的树看起来就像下面那样。每个节点的两个数字是IP段,后跟子网掩码。

             root
         /           \
     192,255             223,255
       |           -------------------------
     168,255       |           |           |
       |          201,255    202,255    208,255
      2,255

当您获得IP地址时,将其分成若干段。您在根级别搜索第一个段。对于速度,您可能希望在根级别使用数组,以便您可以直接查找。

假设IP地址的第一段是223.您从root[223]获取节点,现在您只使用该子树。除非您的数据非常密集,否则您可能不希望在其他级别使用完整数组。后续关卡的某种词典可能就是你想要的。如果下一个段是201,则在字典中查找201节点的223,现在您可能的候选列表只有64K项(即所有IP地址为223,201.x.x)。你可以用其他两个级别做同样的事情。结果是您只需四次查找即可解析IP地址:一个数组查找和三个字典查找。

这种结构也很容易维护。插入新地址或范围最多需要四次查找和添加。与删除相同。更新可以就地完成,而无需重建整个树。您只需要确保在进行更新时不会尝试阅读,并且您不会尝试进行并发更新。但是任何数量的读者都可以同时访问这个东西。

答案 1 :(得分:3)

使用哈希映射或trie会让你很难处理CIDR IP范围(即掩码不一定是基于8的,如192.168.1.0/28)

有效的方法是二进制搜索,因为所有这些IP范围都不重叠:

  1. 将范围A.B.C.D/X转换为表示起始IP地址的32位整数,以及此范围内多少个IP的整数。例如,192.168.1.0/24会转换为3232235776, 256

  2. 在列表或数组中添加这些范围,并按起始IP地址编号排序。

  3. 要将传入的IP地址与列表中的任何范围相匹配,请执行二进制搜索。

答案 2 :(得分:1)

使用红黑或avl树为单独的子网存储被阻止的ip。当您处理基本上由4个数字组成的ip时,您可以使用所需编程语言的自定义比较器并将其存储在红黑树或avl树中。

比较者: -

  

使用4/6 ip部分比较两个ip是否更大   少用第一个不匹配的部分。

     

示例: -

     

10.0.1.1和10.0.0.1

     

这里ip1> ip2因为第三个不匹配的条目在一个中更大。

时间复杂度: -

由于红黑树是平衡的BST,因此您需要O(logn)进行插入,删除和搜索。对于k个子网的每个子网,总共O(log(n)*k)用于搜索ip。

优化: - 如果子网数量很大,则使用不同的密钥,使用与上述类似的比较,但只使用一个红黑树。

Key = (subnet_no,ip)
     

你可以比较它们与上面类似,并得到O(log(S))其中S   是所有子网中的ip条目总数。

答案 3 :(得分:1)

这可能很简单,但由于没有人说过内存限制,你可以使用查找表。即使在实践中,拥有2 ^ 32项LUT也是不可能的,然后无论规则如何,问题都会简化为单个表查找。 (同样可以用于路由。)如果你想要它快,它需要2 ^ 32个八位字节(4 GiB),如果你有更多的时间,一个按位表需要2 ^ 32位,即512 MiB 。即使在这种情况下,它也可以快速完成,但是使用高级编程语言可能会产生不理想的结果。

当然,问题是"快速"总是有点棘手。你想在实践中或在理论上快速吗?如果在实践中,在哪个平台上?如果你的系统将表交换到HDD中,即使LUT方法也可能很慢,并且根据缓存结构,与基于RAM的LUT相比,更复杂的方法可能更快,因为它们适合处理器缓存。高速缓存未命中可能是几百个CPU周期,并且在这些周期中可以进行相当复杂的操作。

LUT方法的问题(除了内存使用)是规则删除的成本。由于表是按所有规则的按位OR生成的,因此没有简单的方法可以删除规则。因此,在这种情况下,必须确定与要删除的规则没有重叠规则的位置,然后必须将这些区域清零。这可能是与其他答案中概述的结构一起逐位完成的。

答案 4 :(得分:-2)

回想一下,IP地址基本上是32位数。

您可以将每个子网加密为其正常形式,并将所有常规表单存储在哈希表中。

在运行时,加密给定地址(易于操作),并检查哈希表是否包含此条目 - 如果包含此条目,则阻止。否则 - 允许。

示例,假设您要阻止子网5.*.*.*,这实际上是具有前导位00000101的网络。所以将地址5.0.0.000000101 - 00000000 - 00000000 - 00000000添加到哈希表中。
一旦特定地址到达 - 例如5.1.2.3,请将其加工回5.0.0.0,并检查其是否在表格中。

使用哈希表,查询时间平均为O(1)