首先,我假设您的意思是您获得一个或多个不相交的 CIDR 范围作为输入,并且需要生成所有 CIDR 范围的列表,其中不包括作为输入给出的任何 CIDR 范围。为方便起见,让我们进一步假设输入不包括整个 IP 地址空间:即0.0.0.0/0。 (这可以通过一个特殊情况来解决,但没有太大意义。)
我以前写过类似的代码,虽然我不能随意分享代码,但我可以描述方法。它本质上是一种二分搜索算法,在该算法中,您重复地平分整个地址空间,直到您隔离出您感兴趣的一个范围。
将 IP 地址空间视为二叉树:根是完整的 IPv4 地址空间0.0.0.0/0。它的子代分别代表地址空间的一半:0.0.0.0/1 和 128.0.0.0/1. 这些子代又可以细分为分别创建子代0.0.0.0/2 / 64.0.0.0/2 和128.0.0.0/2 / 192.0.0.0/2,。一直继续下去,你会得到2**32 叶子,每个叶子代表一个/32(即一个地址)。
现在,将这棵树视为从输入列表中排除的地址空间部分。所以你的任务是遍历这棵树,从树中的输入列表中找到每个范围,并剪掉你输入中树的所有部分,留下地址空间的剩余部分。
幸运的是,您实际上不需要创建所有 2**32 叶子。如果没有为其创建子节点,则可以假设 CIDR N 的每个节点包括 CIDR N+1 及以上的所有节点(您需要一个标志来记住它已经被细分 - ie 不再是一片叶子——原因见下文)。
因此,首先,整个地址空间都存在于树中,但都可以由单个叶节点表示。调用树excluded,并用单个节点0.0.0.0/0.初始化它
现在,考虑第一个输入范围——我们将其称为trial(我将使用14.27.34.0/24 作为初始trial 值,只是为了提供一个具体的值用于演示)。任务是从excluded 中删除trial,留下剩余的地址空间。
从 current 节点指针设置为 excluded 根节点开始。
开始:
将trial CIDR 与current 进行比较。如果它是相同的,那么你就完成了(但如果你的输入范围不相交并且你已经从输入中排除了0.0.0.0/0,这永远不会发生)。
否则,如果current是一个叶子节点(还没有被细分,意味着它代表了这个CIDR级别及以下的整个地址空间),设置它的细分标志,并为其创建两个子节点:一个@ 987654347@ 指向其地址空间的前半部分,right 指向后半部分。适当地标记其中的每一个(对于根节点的子节点,将是0.0.0.0/1 和128.0.0.0/1)。
确定trial CIDR 是在current 的左侧还是右侧。对于我们最初的trial 值,它位于左侧。现在,如果那一侧的指针已经是NULL,那么你就完成了(尽管如果你的输入范围不相交,那“不可能发生”)。
如果trial CIDR 完全等同于该节点中的 CIDR,则只需释放该节点(以及它可能拥有的任何子节点,如果有,则应该没有只有不相交的输入),将指针设置到那一侧NULL,你就完成了。您刚刚通过从树上剪下那片叶子来排除整个范围。
如果试验值与该侧节点中的CIDR不完全相等,则将current设置为该侧并重新开始(即跳转到上面的开始标签)。
因此,初始输入范围为14.27.34.0/24,您将首先将0.0.0.0/0 拆分为0.0.0.0/1 和128.0.0.0/1。然后,您将在左侧下拉并将0.0.0.0/1 拆分为0.0.0.0/2 和64.0.0.0/2. 然后您将再次下拉到左侧以创建0.0.0.0/3 和32.0.0.0/3. 等等,直到经过23 次拆分,您然后将14.27.34.0/23 拆分为14.27.34.0/24 和14.27.35.0/24. 然后删除左侧的14.27.34.0/24 子节点并将其指针设置为NULL,留下另一个。
这将为您留下一棵包含 24 个叶节点的稀疏树(在您删除目标节点之后)。剩下的叶子节点用*标记:
(ROOT)
0.0.0.0/0
/ \
0.0.0.0/1 128.0.0.0/1*
/ \
0.0.0.0/2 64.0.0.0/2*
/ \
0.0.0.0/3 32.0.0.0.0/3*
/ \
0.0.0.0/4 16.0.0.0/4*
/ \
*0.0.0.0/5 8.0.0.0/5
/ \
*8.0.0.0/6 12.0.0.0/6
/ \
*12.0.0.0/7 14.0.0.0/7
/ \
14.0.0.0/8 15.0.0.0/8*
/ \
...
/ \
*14.27.32.0/23 14.27.34.0/23
/ \
(null) 14.27.35.0/24*
(14.27.34.0/24)
对于每个剩余的输入范围,您将再次遍历树,必要时将叶节点一分为二,通常会产生更多叶,但总是会切掉地址空间的一部分。
最后,您只需按照方便的顺序遍历生成的树,收集剩余叶子的 CIDR。请注意,在此阶段您必须排除之前已细分的那些。例如,在上面的树中,如果您接下来处理输入范围14.27.35.0/24,您将留下没有子级的14.27.34.0/23,但它的两半都被分别切掉,不应包含在输出中。 (如果有一些额外的复杂情况,您当然也可以折叠其上方的节点以适应这种情况,但在每个节点中保留一个标志会更容易。)