如何有效地存储IP地址和CIDR范围

时间:2015-09-16 19:22:24

标签: algorithm networking data-structures ip-address cidr

我有一个用例,我不知道如何解决。我在这里要求它为我需要学习解决这个问题提供一些指导。

  • 我需要商店的IP地址(很多,可能是几亿)。这些可以是1.1.1.2CIDR范围,例如1.1.1.1/24

要求如下

- Save all the IP address which comes as above format in-memory
- Search should work as following  
   - if I have IP address as 1.1.1.2 and 1.1.1.1/24, it should match the specific IP address 1.1.1.2 and not the CIDR range it falls into (1.1.1.1/24)
   - If specific IP address is not found but CIDR range is available, CIDR range is returned
   - if no match is found, return null/throw exception

问题
- 什么数据结构可以帮助我解决这个问题?尝试? - 你会采取什么方法? - 如何确保它不会消耗太多内存并且搜索速度很快(将会有一些合理的权衡)

谢谢

3 个答案:

答案 0 :(得分:4)

我会使用二叉树(这种树称为Radix Trees):

  1. IP地址的位用于从MSB开始导航(例如0 =左子,1 =右子)
  2. 所有特定IP地址都是离开,而CIDR范围是内部节点(使用虚拟叶子表示"缺少" IP地址就像红黑树一样)
  3. 某些节点将是您拥有的实际数据,其他节点仅为"导航"节点(它们对应于您没有的CIDR范围,用标记标记)
  4. 搜索:只需使用您拥有的地址导航树。如果你最终得到一片叶子,那么你就拥有了那个特定的IP地址,否则就是#34;最低的"带标志的节点(参见第3点)是您拥有的最具体的CIDR范围。

    示例:让我们使用8位IP和2位"部分"地址;在插入1.1.1.0/6之后,树将是(IP之后的数字是"包含"标志,nils是假叶子)

        <root> -- nil
          |
    00.00.00.00/1 (0) -- nil
          |
    01.00.00.00/2 (0) -- nil
          |
    01.00.00.00/3 (0) -- nil
          |
    01.01.00.00/4 (0) -- nil
          |
    01.01.00.00/5 (0) -- nil
          |
    01.01.01.00/6 (1) --nil
          |
         nil
    

    如果您查找IP地址1.1.1.1,您将在1.1.1.1/6处停止,这是一个CIDR范围,因为它具有零个孩子并且是最具体的(在树中)。如果您现在插入1.1.1.1,那么树将是

        <root> -- nil
          |
    00.00.00.00/1 (0) -- nil
          |
    01.00.00.00/2 (0) -- nil
          |
    01.00.00.00/3 (0) -- nil
          |
    01.01.00.00/4 (0) -- nil
          |
    01.01.00.00/5 (0) -- nil
          |
    01.01.01.00/6 (1) -- nil
          |
    01.01.01.00/7 (0) -- nil
          |
    01.01.01.01 (1)
    

    1.1.1.1没有叶子,因为它是IP地址。最后,让我们插入1.1.2.1

        <root> -- nil
          |
    00.00.00.00/1 (0) -- nil
          |
    01.00.00.00/2 (0) -- nil
          |
    01.00.00.00/3 (0) -- nil
          |
    01.01.00.00/4 (0) --------------------+
          |                               |
    01.01.00.00/5 (0) -- nil        01.01.10.00/5 (0) -- nil
          |                               |
    01.01.01.00/6 (1) -- nil        01.01.10.00/6 (0) -- nil        
          |                               |
    01.01.01.00/7 (0) -- nil        01.01.10.00/7 (0) -- nil
          |                               |
    01.01.01.01 (1)                 01.01.10.01 (1)
    

答案 1 :(得分:2)

首先,您需要确定一些决策点。这必须是完全确定的。您说的唯一决定点是首先检查主机地址(/ 32),然后寻找更短的掩码。

  • 它应该寻找逐渐缩短的面具(最长匹配)吗?
  • 如果它包含特定的面具长度,你会看短吗或 掩模长度也更长,或者仅限于掩模长度, 那个面具的长度和短,或那个面具长度和更长?如果你想查看其他长度,你想要路由表的最长匹配吗?
  • 您如何处理地址,例如您的示例1.1.1.1/24,其中 地址长度超过掩码长度?你有没有假设 它是一个仅限主机的地址(1.1.1.1/32),还是您在寻找上述决策点发挥作用的子网(1.1.1.0/24)?

您将需要以合理的速度克服几百万个地址所需的内存消耗问题。

我将假设主机地址将是您拥有的最大地址数,并且随着掩码长度变短,每个掩码长度的地址将逐渐减少。

如果使用散列表(32个掩码长度各占一个),则可以从最长的掩码长度(/ 32)表开始,如果找不到匹配,则查看短掩码长度表,具体取决于关于上述确定性规则。

例如,如果您的问题中包含1.1.1.1/24,则假设您的汇总数为1.1.1.0/241.1.0.0/231.1.0.0/22

Table /32
1.1.1.1

Table /31
not found

Table /30
not found

Table /29
not found

Table /28
not found

Table /27
not found

Table /26
not found

Table /25
not found

Table /24
1.1.1.0

Table /23
1.1.0.0

Table /22
1.1.0.0

Table /21
not found

Table /20
not found

Table /19
not found

Table /18
not found

Table /17
not found

Table /16
not found

Table /15
not found

Table /14
not found

Table /13
not found

Table /12
not found

Table /11
not found

Table /10
not found

Table /9
not found

Table /8
not found

Table /7
not found

Table /6
not found

Table /5
not found

Table /4
not found

Table /3
not found

Table /2
not found

Table /1
not found

例如,使用1.1.1.1/24:您可以开始在/32表中查找主机地址。如果您没有找到,那么AND带有掩码的地址将在1.1.1.0表中获得/24。如果你没有找到,AND使用下一个较短的掩码并查看该表。重复,直到找到匹配或掩码长度达到0。具体如何执行此操作取决于您上述决策所产生的算法。

对于每个主机地址或聚合,这只占用一个32位内存块,但是,只有几亿个地址,它不会少量。性能将高度依赖于所使用的散列算法,但它应该表现得非常好。

答案 2 :(得分:0)

postgreSQL支持inet,cidr地址以及内置函数来搜索它们(以及范围类型)。

http://www.postgresql.org/docs/9.4/interactive/datatype-net-types.html

不确定这是不是你要问的。