[英]Optimization in obtaining a list of IP conflicts from a Dataset
我有一个包含 ID 号的网络数据列表,它是网络 IP 和虚拟 IP。 示例如下所示。
ID Network IP Virtual IP
1 10.1.1.0/24 -
2 10.2.2.0/24 -
3 10.3.3.0/24 10.4.4.88
4 10.4.4.0/24 -
5 10.1.0.0/16 -
6 ...
...
...
目标是检测并输出列表中是否存在任何网络 IP 或虚拟 IP 冲突/重叠,其中包括产生冲突的网络 IP 和备注。 eg. Network IP conflict --> 10.1.1.0/24 overlaps with 10.1.0.0/16
eg. Network IP conflict --> 10.1.1.0/24 overlaps with 10.1.0.0/16
且Virtual-ip conflict --> 10.4.4.88 is in range of 10.4.4.0/24 (of different ID)
这是代码的示例输出。
ID Network IP Virtual IP Result Remark
5 10.1.0.0/16 - 10.1.0.0/16 Network Conflict
1 10.1.1.0/24 - 10.1.0.0/16 Network Conflict
3 10.3.3.0/24 10.4.4.88 10.4.4.0/24 Virtual-ip Conflict
4 10.4.4.0/24 - 10.4.4.0/24 Virtual-ip Conflict
下面是示例代码,目前我正在使用非常慢的嵌套“for”循环。 因此,我只是想知道是否有更有效的算法来解决这个问题,尤其是在比较 100k+ 数据时?
import ipaddress
class Network:
def __init__(self, ID='-', IPNet='-', VIP='-', Result='-', Remark='-'):
self.ID = ID # ID Number
self.IPNet = IPNet # Network IP
self.VIP = VIP # Virtual IP
self.Result = Result # Resulting Network IP that conflicts
self.Remark = Remark
def __eq__(self, other):
return self.ID == other.ID and self.IPNet == other.IPNet and self.VIP == other.VIP and \
self.Result == other.Result and self.Remark == other.Remark
def __hash__(self):
return hash((self.ID, self.IPNet, self.VIP, self.Result, self.Remark))
Final_List = []
Input_List = [Network('1', '10.1.1.0/24', '-', '-', '-'),
Network('2', '10.2.2.0/24', '-', '-', '-'),
Network('3', '10.3.3.0/24', '10.4.4.88', '-', '-'),
Network('4', '10.4.4.0/24', '-', '-', '-'),
Network('5', '10.1.0.0/16', '-', '-', '-')]
for in1 in Input_List:
IPNet1 = in1.IPNet
for in2 in Input_List:
IPNet2 = in2.IPNet
# Resolving overlapping Network IP
if in1.ID != in2.ID and ipaddress.ip_network(IPNet1).overlaps(ipaddress.ip_network(IPNet2)):
Remark = 'Network Conflict'
Result = '-'
# Comparing size of network range. Resulting network IP will be the one with larger size
size1 = ipaddress.ip_network(IPNet1).num_addresses
size2 = ipaddress.ip_network(IPNet2).num_addresses
if size1 >= size2:
Result = IPNet1
else:
Result = IPNet2
_in1 = Data(in1.ID, IPNet1, in1.VIP, Result, Remark)
Final_List.append(_in1)
_in2 = Data(in2.ID, IPNet2, in2.VIP, Result, Remark)
Final_List.append(_in2)
break
# Resolving Virtual IP conflicts
elif in1.ID != in2.ID and in1.VIP != '-' and ipaddress.ip_address(in1.VIP) in ipaddress.ip_network(IPNet2):
Remark = 'Virtual-ip Conflict'
Result = IPNet2
_in1 = Data(in1.ID, IPNet1, in1.VIP, Result, Remark)
Final_List.append(_in1)
_in2 = Data(in2.ID, IPNet2, in2.VIP, Result, Remark)
Final_List.append(_in2)
break
Final_List = list(set(Final_List)) # Remove duplicates
Final_List = sorted(Final_List, key=lambda x: (ipaddress.ip_network(x.IPNet), x.ID), reverse=False)
您可以将数据存储在树中以便更快地查找。
将每个 IP 网络与其他 IP 网络中的地址进行比较需要对网络数量进行 for 循环,并且可能需要 O(log n) 算法来确定它们是否重叠。 这使您的速度为 O(n^2 log n),这非常慢。
我们可以创建一个名为IPTree
的类,它根据 IP 的最高有效位拆分 IP。 如果您的所有网络都像最常见的网络一样以 1 个字节( /32, /24, /16, /8
)为增量,您可以在每个树级别使用 256 的分支因子,这样您就无需向下探索树一样多。 我将根据您的示例做出这个假设,但是如果您想支持任何类型的网络,您可以按位而不是按字节进行拆分。
现在,我们在第一级的第一个节点将是根(无),第一级节点将存储第一个字节(例如 10),第二级将存储第二个字节,第三级将存储第三个字节,以及第四层将存储第四个字节。 要将网络标记为“已采用”,我们只需要在对应于L = n/8
的级别的节点中切换一个标志,其中 n 是网络掩码中的位数。
可是等等! 像这样存储一棵完整的树将需要 2^32 个整数! 当然,我们只能减少少数网络的内存量。 我们可以通过使用稀疏树来做到这一点。
从根元素开始,然后对于每个网络,将网络的每个字节添加到树的一个级别,将最后一个节点标记为“结束”节点。 树的其余部分只是填充了None
。 节点的存在,“结束”与否,表示树中的这一点至少部分被另一个网络或主机使用,因此不能放入新网络中。 “结束”节点的存在表示树中的这一点已被网络占据,并且您不能将网络放入其中。
这是解决方案的粗略说明,绿色节点代表“结束”节点:
使用此解决方案,我们可以在 O(32/b) 中完成搜索和添加,其中 b 是每层的位数(字节为 8,位为 1),这将使比较 n 个网络 O(n/b )。 对于常数 b,这将按 O(n) 进行缩放。
现在,对于问题的第二部分,您希望能够找出与添加的网络冲突的 IP 地址或网络。 我们有两种方法可以做到这一点,每种方法都针对不同类型的冲突:
对于到达“结束”节点的冲突,我们知道我们正在尝试分配一个 IP 范围或已占用范围内的 IP,并且我们知道被占用的范围是什么,因为我们有相应的“结束”节点。 我们需要做的就是连接节点和它的每个父节点,用零填充末尾,并附加网络中的位数(可以通过父节点的计数找到)。 例如,尝试在上面的树中分配 10.1.1.3 将导致我们到达 10. -> 1. -> 1. 处的结束节点,即变为 10.1.1.0/24。 这将花费 O(1)。
对于我们尚未到达“结束”节点但该节点已被占用的冲突,我们知道我们正在尝试分配一个 IP 范围,该范围内有一个已占用的 IP 范围。 为此,我们只需搜索子节点以找到作为该节点后代的结束节点。 这将需要 O(2^b * 32/b)。 对于常数 b,这将具有恒定的时间效率。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.