Python实现子网保留IP校验的性能优化与IPv6适配咨询
IP地址保留段校验逻辑优化方案
问题背景
- 需求:实现IP地址校验逻辑,判断待分配IP是否属于子网预留地址段,规则为每个子网前5个、后5个IP为保留地址,不允许分配
- 现有实现问题:
- 运行效率极低,通过遍历全子网主机列表判断,大网段(尤其是IPv6)场景下性能极差甚至无法运行
- 仅硬编码支持/16、/24两种掩码,不兼容其他掩码、不支持IPv6场景
- 自行尝试用
num_addresses属性校验后5个地址时出现索引越界问题
原有问题实现
def verify_ip_subnet(ip_address, subnet_address): new_subnet = subnet_address.split('/')[1].strip() if int(new_subnet) == 16: new_vpn_range = [] new_vpn_range.append(subnet_address.split('/')[0].strip()) new_count = 0 for address in ipaddress.ip_network(subnet_address).hosts(): if new_count == 0 or new_count == 1 or new_count == 2 or new_count == 3 or new_count == 4 or new_count == 65531 or new_count == 65532 or new_count == 65533: new_vpn_range.append(str(address)) new_count += 1 if ip_address in new_vpn_range: return False if int(new_subnet) == 24: new_vpn_range = [] new_vpn_range.append(subnet_address.split('/')[0].strip()) new_count = 0 for address in ipaddress.ip_network(subnet_address).hosts(): if new_count == 0 or new_count == 1 or new_count == 2 or new_count == 3 or new_count == 4 or new_count == 251 or new_count == 252 or new_count == 253: new_vpn_range.append(str(address)) new_count += 1 if ip_address in new_vpn_range: return False
调用示例
ip_address = '10.8.0.5' subnet_address = '10.8.0.0/16'
之前尝试的报错实现(索引越界)
之前尝试通过num_addresses属性直接索引后段地址时出现越界,测试代码如下:
ip_check = ipaddress.ip_network(subnet_address) for i in range(6): if str(ip_check[i]) == ip_address: return False last_address = ip_check.num_addresses print(last_address) print(ip_check[last_address])
索引越界原因:Python的
ipaddress库网络对象索引从0开始,总地址数为num_addresses时,合法索引范围是0 ~ num_addresses-1,直接访问ip_check[num_addresses]必然越界;且原有逻辑没有先判断IP是否属于对应子网,也没有兼容IPv6地址的整数运算逻辑。
优化实现方案
核心思路:完全抛弃遍历全网段的低效逻辑,直接通过IP地址的整数值做范围判断,兼容所有IPv4/IPv6网段、任意掩码长度,时间复杂度为O(1)。
import ipaddress def verify_ip_subnet(ip_address: str, subnet_address: str, reserve_head: int =5, reserve_tail: int=5) -> bool: """ 校验IP是否属于子网可分配范围 :param ip_address: 待校验IP字符串 :param subnet_address: 子网CIDR字符串 :param reserve_head: 子网头部保留IP数量,默认5个 :param reserve_tail: 子网尾部保留IP数量,默认5个 :return: True=IP可分配,False=IP属于保留段/不属于子网/格式非法 """ try: ip = ipaddress.ip_address(ip_address) subnet = ipaddress.ip_network(subnet_address, strict=False) except ValueError: return False # IP不属于对应子网直接返回不可分配 if ip not in subnet: return False total_addr = subnet.num_addresses # 子网总地址数小于等于保留总数,整个子网无可用地址 if total_addr <= reserve_head + reserve_tail: return False # 转换为整数做范围比较,天然兼容IPv4/IPv6 start_int = int(subnet.network_address) end_int = int(subnet.broadcast_address) ip_int = int(ip) # 命中头部保留段 if ip_int < start_int + reserve_head: return False # 命中尾部保留段 if ip_int > end_int - reserve_tail: return False return True
方案优势
- 性能极高:不需要遍历任何地址,仅做整数比较,哪怕是/64的IPv6大网段也能瞬间完成计算
- 兼容性强:自动支持所有合法掩码的IPv4、IPv6网段,不需要硬编码掩码对应地址数
- 可配置:头部、尾部保留数量支持自定义调整
- 鲁棒性强:自带格式校验、子网归属校验,不会出现索引越界问题
测试验证
# 原场景测试:10.8.0.5不在/16的前5个保留段,返回True(可分配) print(verify_ip_subnet('10.8.0.5', '10.8.0.0/16')) # 输出True print(verify_ip_subnet('10.8.0.3', '10.8.0.0/16')) # 头部保留,输出False print(verify_ip_subnet('10.8.255.253', '10.8.0.0/16')) # 尾部保留,输出False # IPv6场景测试 print(verify_ip_subnet('2001:db8::5', '2001:db8::/64')) # 可分配,输出True print(verify_ip_subnet('2001:db8::3', '2001:db8::/64')) # 头部保留,输出False
内容的提问来源于stack exchange,提问作者yasa
相关产品推荐
相关产品推荐

