C++ 根据子网列表进行匹配

C++ 根据子网列表进行匹配,c++,networking,C++,Networking,有一个以net addr/mask形式存在的子网列表,例如 12.34.45.0/24192.168.0.0/1645.0.0.0/10 想知道什么是判断给定IP地址是否在任何子网中的最佳方法 下面是关于匹配的一点背景: 对于IP地址x,我们将其转换为整数。例如,11.12.13.14被转换为0x0b0c0d0e。对于掩码m,我们将其转换为前导(32m)位为1,其余位为0的整数 要检查IP x是否在子网A/m中, 我们只需要检查(x&m)==(A&m) 好奇是什么样的数据结构或功能使得匹配一系列

有一个以net addr/mask形式存在的子网列表,例如 12.34.45.0/24192.168.0.0/1645.0.0.0/10

想知道什么是判断给定IP地址是否在任何子网中的最佳方法

下面是关于匹配的一点背景: 对于IP地址x,我们将其转换为整数。例如,11.12.13.14被转换为0x0b0c0d0e。对于掩码m,我们将其转换为前导(32m)位为1,其余位为0的整数

要检查IP x是否在子网A/m中,
我们只需要检查
(x&m)==(A&m)


好奇是什么样的数据结构或功能使得匹配一系列子网变得快速。当然,我们可以在一个循环中遍历子网,但这不是有效的。

您有任何证据表明性能是一个问题吗?只有2^24个子网(当然,您可以有/28个子网,但它们通常是组织内部的,因此即使组织有a类网络,也只有2^24个子网)

进行1600万次
和比较不会花费时间


保持简单(直到你真的必须做得更好)。

制作一个树,其中每个级别代表IP地址的n位。在每个级别上存储子网,以便掩码位数介于
n*级别
n*(级别+1)
之间。例如,当n=4时,每个节点有16个子节点。因此,如果您针对
11.12.13.14
(==
0x0b0c0d0e
)进行测试,您可以像这样遍历树:

0 -> b -> 0 -> c -> 0 -> d -> 0 -> e
在节点上,您可以跟踪具有相应大小的子网。我的意思是:级别0应该有子网/1到/4(包括),级别1应该有子网/5到/8,以此类推,直到/29到/32。请注意,/0匹配所有内容,因此在数据结构中使用它是无用的

要在树中搜索,请将IP分组为n个位(在我的示例4中)。下降到与前n位匹配的第一级,并测试该级上的所有子网。如果未找到,则下降到与下一个n位匹配的下一级

这样,您最多必须测试每个2^n子网的32/n级别。对于n=4,您必须测试8个级别,每个级别最多有16个子网。这件事很快就完成了

澄清:节点是子网,例如(在十六进制中,一个数字是半字节,即4位):0a.5a.00.00/16。此节点的父节点将是包含此子网的子网:例如:0a.50.00.00/12。指向子节点的边可以解释为:“包含”,如:“此(父)子网包含子节点表示的子网”。要使此树包含所需的所有子网,您可能必须插入节点,这些节点表示不在列表中的子网。因此,请将这些节点标记为辅助节点,以便您知道在搜索此树时,您知道在它下面有更多特定的子网,但节点本身不属于要检查的子网列表的一部分。只应添加直接位于列表中的这些节点以及所有父节点,以使节点在树结构中可访问

下面是一个关于我如何看待它的
struct

struct subnet_tree_node
{
    uint_32 ip;  // 32 bit IP address
    subnet_tree_node *children;
    uint_8 number_of_children;
    uint_8 mask; // number of bits for this subnet
    uint_8 valid; // wether this node is valid or auxiliary
}

因此,您已经确定性能是一个问题

  • 将每个网络掩码/地址对视为一对IP地址:第一个有效,最后一个有效
  • 让我们假设最后一个valid总是奇怪的(不确定在/32网络中是否如此-但这真的很奇怪)
  • 构造这些IP地址的排序向量。(如果网络重叠或发生任何愚蠢的事情,请投诉。)
  • 用某种二进制切块在向量中搜索目标IP地址
  • 如果IP地址在向量中,则为a)wierd;b) 在其中一个子网中
  • 如果IP地址不在向量中,且下面的值为偶数,则它位于子网中。如果下面的值为奇数,则它不在子网中

    • 感谢这里的讨论,他们给了我这个解决方案的灵感

      首先,在失去一般性的情况下,我们假设没有一个子网覆盖另一个子网(或者我们只是删除较小的子网)

      每个子网被视为一个间隔[子网\最小值,子网\最大值]


      我们只需要将所有子网组织成一棵二叉树,每个节点都是一对(subnet\u min,subnet\u max)。在搜索IP时,它像只基于子网min的常规二进制搜索一样遍历树,目的是找到子网min的节点是所有子网min中最大的。这里有一些建议:关于这方面有很多学术研究。搜索例如“ip前缀算法”或“ip路由查找”。@alan stokes,这是存在的问题,它与查找最长的ip前缀匹配相关,但不同。感谢您的建议。感谢@martin bonner提醒我KISS原则:-)我可能有成千上万的子网,对于每个IP,我可能有几微秒的时间来决定给定的IP是否属于其中一个子网。所以我需要注意这里的性能。你是说每个树节点都是一个子网吗?比如说,树的每一层都有子网,子网带有掩码/24到/28?该级别可以有数百万个树节点(带掩码/24到/28)。不,每个节点最多有16个子网(按4位分组时)。子网表示节点的子网。每个节点都跟踪一个标志,该标志指示它是列表中的子网,还是仅用于将子网附加到子网。我想我不理解树中节点的定义。我以为你树上的节点都是子网。你能澄清树中节点和边的定义吗?我在回答中添加了一个澄清。如果所有节点和边都不重叠,正如你所描述的,那么只需对它们进行排序并在列表中执行二进制搜索。超级快速和简单。