一、IP地址概述
IP地址是互联网协议的基石,用于标识网络中的每一台设备。Python的 ipaddress 模块提供了一套完整的工具,用于创建、操作和验证IP地址与网络。该模块自Python 3.3起被纳入标准库,极大简化了网络编程中的地址处理工作。
IPv4与IPv6
互联网协议目前存在两个版本:IPv4 和 IPv6。IPv4 地址为32位,通常以点分十进制表示(如 192.168.1.1),提供约43亿个地址,随着互联网规模爆炸式增长,地址资源已近枯竭。IPv6 地址为128位,以冒号分隔的十六进制表示(如 2001:db8::1),提供了几乎无限的地址空间。ipaddress 模块同时对两个版本提供了完整的支持。
CIDR表示法
无类域间路由(Classless Inter-Domain Routing, CIDR)是现代IP地址分配和路由的核心机制。它使用 / 后跟网络前缀长度的表示法,例如 192.168.1.0/24 表示前24位为网络位,后8位为主机位。CIDR 取代了传统的基于类(A/B/C类)的地址划分,提高了地址空间的利用率。在 ipaddress 模块中,CIDR 表示法被广泛用于构造网络对象和进行子网操作。
核心概念速览:IPv4地址32位,约43亿个;IPv6地址128位,几乎无限。CIDR格式如 10.0.0.0/8 表示一个网络,其中 /8 表示网络前缀长度。
二、IPv4Address/IPv6Address — 地址对象
IP地址对象是整个模块的基础。无论是 IPv4 还是 IPv6 地址,都可以通过 ipaddress.ip_address() 工厂函数或对应的具体类来创建。地址对象一经创建即不可变,并提供了丰富的属性用于查询地址的特性。
构造函数
ipaddress.IPv4Address(address) 和 ipaddress.IPv6Address(address) 接受字符串或整数形式的地址。工厂函数 ipaddress.ip_address(address) 会自动判断地址版本并返回对应的对象。
import ipaddress
# 字符串构造
addr1 = ipaddress.IPv4Address('192.168.1.1')
addr2 = ipaddress.IPv6Address('2001:db8::1')
# 整数构造(IPv4的32位整数表示)
addr3 = ipaddress.IPv4Address(3232235777) # 3232235777 = 192.168.1.1
# 工厂函数自动识别版本
addr4 = ipaddress.ip_address('10.0.0.1') # => IPv4Address
addr5 = ipaddress.ip_address('::1') # => IPv6Address (环回地址)
print(addr1) # 192.168.1.1
print(addr3) # 192.168.1.1
核心属性
IP地址对象提供了丰富的只读属性,用于获取地址的版本信息、压缩/展开格式、以及判断地址类型。
| 属性 | 说明 | 示例结果 (IPv4 192.168.1.1) |
version | IP协议版本号 | 4 |
max_prefixlen | 该版本的最大前缀长度 | 32 |
compressed | 压缩格式字符串 | 192.168.1.1 |
exploded | 展开格式字符串 | 192.168.1.1 |
packed | 网络字节序的bytes表示 | b'\xc0\xa8\x01\x01' |
is_private | 是否为私有地址 | True |
is_global | 是否为全局公网地址 | False |
is_multicast | 是否为组播地址 | False |
is_loopback | 是否为环回地址 | False |
is_link_local | 是否为链路本地地址 | False |
is_unspecified | 是否为未指定地址 (0.0.0.0) | False |
is_reserved | 是否为保留地址 | False |
addr = ipaddress.IPv4Address('127.0.0.1')
print(addr.is_loopback) # True
print(addr.is_private) # False(127.0.0.1是环回,不是私有)
addr2 = ipaddress.IPv4Address('224.0.0.1')
print(addr2.is_multicast) # True
# IPv6 示例
v6addr = ipaddress.IPv6Address('fe80::1')
print(v6addr.is_link_local) # True
v6addr2 = ipaddress.IPv6Address('ff02::1')
print(v6addr2.is_multicast) # True
# IPv6 特有属性
print(v6addr2.ipv4_mapped) # None (非 IPv4 映射地址)
print(v6addr2.sixtofour) # None
print(v6addr2.teredo) # None
字符串与整数互转
将IP地址转为整数在网络计算中非常有用,例如子网计算、IP地址排序等。 ipaddress 模块通过 int() 内置函数即可将地址对象转为整数,也可以直接比较大小。
# 地址转整数
addr = ipaddress.IPv4Address('192.168.1.1')
num = int(addr)
print(num) # 3232235777
# 整数转地址
addr2 = ipaddress.IPv4Address(3232235777)
print(addr2) # 192.168.1.1
# 比较运算(按整数值比较)
print(ipaddress.IPv4Address('192.168.1.10') > ipaddress.IPv4Address('192.168.1.1')) # True
# IPv6 同样支持
v6 = ipaddress.IPv6Address('::ffff:192.168.1.1')
print(int(v6)) # 281472812449793
提示:IPv6Address 的 exploded 属性会将地址以完整的冒号分隔十六进制格式呈现,包含所有前导零,而 compressed 属性则会省略连续的零段。例如 ::1 的 exploded 格式为 0:0:0:0:0:0:0:1。
# IPv6 压缩与展开格式对比
v6 = ipaddress.IPv6Address('2001:db8::1')
print(v6.compressed) # 2001:db8::1
print(v6.exploded) # 2001:0db8:0000:0000:0000:0000:0000:0001
三、IPv4Network/IPv6Network — 网络对象
网络对象表示一个IP子网,包含地址范围和子网掩码信息。通过 ipaddress.ip_network(address, strict=True) 工厂函数或对应的具体类创建。与地址对象不同,网络对象构造时默认进行严格检查,确保传入的地址是该网络的网络地址(主机位全零)。
# 创建网络对象(严格模式)
net1 = ipaddress.IPv4Network('192.168.1.0/24')
print(net1) # 192.168.1.0/24
# 非严格模式:传入非网络地址时自动修正
net2 = ipaddress.IPv4Network('192.168.1.100/24', strict=False)
print(net2) # 192.168.1.0/24(自动将主机位清零)
# 严格模式下传入非网络地址会抛出异常
# ipaddress.IPv4Network('192.168.1.100/24')
# => ValueError: 192.168.1.100/24 has host bits set
网络边界属性
网络对象提供了多个属性来获取网络范围的关键地址:网络地址(network_address)、广播地址(broadcast_address)、子网掩码(netmask)、反掩码(hostmask)以及前缀长度(prefixlen)。
| 属性 | 说明 | 示例 (192.168.1.0/24) |
network_address | 网络地址(主机位全零) | 192.168.1.0 |
broadcast_address | 广播地址(主机位全一) | 192.168.1.255 |
netmask | 子网掩码 | 255.255.255.0 |
hostmask | 反掩码(主机位掩码) | 0.0.0.255 |
prefixlen | 网络前缀长度 | 24 |
num_addresses | 网络中的地址总数 | 256 |
version | IP协议版本 | 4 |
max_prefixlen | 该版本最大前缀长度 | 32 |
with_netmask | 带子网掩码的字符串表示 | 192.168.1.0/255.255.255.0 |
with_prefixlen | 带前缀长度的字符串表示 | 192.168.1.0/24 |
with_hostmask | 带反掩码的字符串表示 | 192.168.1.0/0.0.0.255 |
is_private | 是否为私有网络 | True |
net = ipaddress.IPv4Network('10.0.0.0/16')
print(f"网络地址: {net.network_address}") # 10.0.0.0
print(f"广播地址: {net.broadcast_address}") # 10.0.255.255
print(f"子网掩码: {net.netmask}") # 255.255.0.0
print(f"反掩码: {net.hostmask}") # 0.0.255.255
print(f"前缀长度: {net.prefixlen}") # 16
print(f"地址总数: {net.num_addresses}") # 65536
print(f"是否为私有: {net.is_private}") # True
子网划分与超网合并
subnets(prefixlen_diff=1) 方法将一个网络划分为更小的子网。supernet(prefixlen_diff=1) 方法则向上寻找包含当前网络的超网。这两个方法是网络规划中的核心工具。
net = ipaddress.IPv4Network('192.168.0.0/24')
# 划分为2个/25子网
subs = list(net.subnets(prefixlen_diff=1))
for sub in subs:
print(sub)
# 192.168.0.0/25
# 192.168.0.128/25
# 划分为4个/26子网
subs4 = list(net.subnets(prefixlen_diff=2))
for sub in subs4:
print(sub)
# 192.168.0.0/26
# 192.168.0.64/26
# 192.168.0.128/26
# 192.168.0.192/26
# subnets() 也可以指定新的前缀长度
subs_new = list(net.subnets(new_prefix=26))
# 同上,4个/26子网
# 超网查询
super_net = net.supernet(prefixlen_diff=1)
print(super_net) # 192.168.0.0/23
# 多次超网合并
super_net2 = net.supernet(prefixlen_diff=8)
print(super_net2) # 192.168.0.0/16
地址迭代器与子网排除
hosts() 方法返回一个迭代器,遍历该网络中所有可用的主机地址(排除网络地址和广播地址)。address_exclude(network) 方法返回从当前网络中排除指定子网后剩余的地址块,是网络规划中非常实用的功能。
# hosts() 遍历可用主机地址
net = ipaddress.IPv4Network('192.168.1.0/28') # 16个地址,14个可用主机
hosts = list(net.hosts())
print(len(hosts)) # 14(排除.0网络地址和.15广播地址)
print(hosts[0]) # 192.168.1.1
print(hosts[-1]) # 192.168.1.14
# address_exclude() 排除子网
net = ipaddress.IPv4Network('10.0.0.0/24')
exclude_net = ipaddress.IPv4Network('10.0.0.0/25')
remaining = list(net.address_exclude(exclude_net))
for r in remaining:
print(r)
# 10.0.0.128/25(排除前一半后剩下的地址块)
# 排除中间子网
net = ipaddress.IPv4Network('10.0.0.0/24')
exclude_net = ipaddress.IPv4Network('10.0.0.64/26')
remaining = list(net.address_exclude(exclude_net))
for r in remaining:
print(r)
# 10.0.0.0/26
# 10.0.0.128/25
四、网络操作
ipaddress 模块提供了简洁的语法来执行常见的网络操作,包括包含关系判断、子网划分、网络重叠检查等。这些操作在网络安全、网络规划、路由分析等场景中频繁使用。
包含判断
使用 in 运算符可以判断一个地址是否属于某个网络,或者一个网络是否是另一个网络的子网。这种直观的语法使得网络包含关系判断代码非常简洁易读。
net = ipaddress.IPv4Network('192.168.1.0/24')
# 地址是否在网络中
print(ipaddress.IPv4Address('192.168.1.50') in net) # True
print(ipaddress.IPv4Address('192.168.2.1') in net) # False
# 网络是否包含另一个网络
sub_net = ipaddress.IPv4Network('192.168.1.0/25')
print(sub_net in net) # True(/25是/24的子网)
another = ipaddress.IPv4Network('10.0.0.0/8')
print(another in net) # False
# 边界地址判断
print(net.network_address in net) # True(网络地址自身)
print(net.broadcast_address in net) # True(广播地址)
子网划分
子网划分是网络管理中最常见的操作之一。ipaddress 提供了灵活的子网划分方法,支持按前缀长度差或直接指定新前缀长度。此外,还可以使用 subnets_of() 方法来判断一个网络是否可以被另一个网络包含。
# 将/16网络划分为/20子网
net = ipaddress.IPv4Network('172.16.0.0/16')
subnets = list(net.subnets(new_prefix=20))
print(f"子网数量: {len(subnets)}") # 16(2^(20-16) = 16)
print(subnets[0]) # 172.16.0.0/20
print(subnets[1]) # 172.16.16.0/20
print(subnets[-1]) # 172.16.240.0/20
# subnet_of() 判断子网关系
parent = ipaddress.IPv4Network('10.0.0.0/16')
child = ipaddress.IPv4Network('10.0.1.0/24')
other = ipaddress.IPv4Network('10.1.0.0/24')
print(child.subnet_of(parent)) # True
print(other.subnet_of(parent)) # False
print(parent.supernet_of(child)) # True
网络交集与重叠检查
overlaps() 方法用于检查两个网络是否存在重叠(即共享部分地址空间)。这在网络规划中避免地址冲突、合并路由条目时非常有用。
# 检查网络是否重叠
net1 = ipaddress.IPv4Network('10.0.0.0/24')
net2 = ipaddress.IPv4Network('10.0.0.128/25') # net1 的子网
net3 = ipaddress.IPv4Network('10.0.1.0/24') # 邻近网络
print(net1.overlaps(net2)) # True(net2 是 net1 的子网,完全重叠)
print(net1.overlaps(net3)) # False(两个网络不重叠)
# 部分重叠的情况
net4 = ipaddress.IPv4Network('10.0.0.128/25')
net5 = ipaddress.IPv4Network('10.0.0.0/25')
net6 = ipaddress.IPv4Network('10.0.0.64/26') # 跨越 net4 和 net5 的边界
# 相邻不重叠的情况:
print(net4.overlaps(net5)) # False(net4是128-255,net5是0-127,不重叠)
# 通过地址范围计算重叠
def check_overlap(net_a, net_b):
a_start = int(net_a.network_address)
a_end = int(net_a.broadcast_address)
b_start = int(net_b.network_address)
b_end = int(net_b.broadcast_address)
return a_start <= b_end and b_start <= a_end
print(check_overlap(net4, net5)) # False
print(check_overlap(net4, net6)) # True
五、实战应用
理论知识的最终目的是解决实际问题。本节展示 ipaddress 模块在真实网络编程中的三个典型应用场景:IP地址验证、子网扫描和CIDR合并。
应用一:IP地址验证函数
在Web应用或网络工具开发中,验证用户输入的IP地址或CIDR格式是否正确是常见需求。ipaddress 模块通过异常机制提供了天然的验证能力。
def validate_ip(address):
"""验证IP地址格式是否正确,支持IPv4和IPv6。返回 (是否有效, 对象或错误信息)"""
try:
addr = ipaddress.ip_address(address)
return True, addr
except ValueError as e:
return False, str(e)
def validate_network(cidr, strict=True):
"""验证CIDR网络格式是否正确"""
try:
net = ipaddress.ip_network(cidr, strict=strict)
return True, net
except ValueError as e:
return False, str(e)
def classify_address(address):
"""分类IP地址并提供详细信息"""
addr = ipaddress.ip_address(address)
info = {
'address': str(addr),
'version': addr.version,
'is_private': addr.is_private,
'is_global': addr.is_global,
'is_loopback': addr.is_loopback,
'is_multicast': addr.is_multicast,
'is_link_local': addr.is_link_local,
}
return info
# 测试
print(validate_ip('192.168.1.1')) # (True, IPv4Address('192.168.1.1'))
print(validate_ip('256.1.1.1')) # (False, '... 256 ...')
print(validate_ip('::1')) # (True, IPv6Address('::1'))
print(classify_address('127.0.0.1'))
# {'address': '127.0.0.1', 'version': 4, 'is_private': False,
# 'is_global': False, 'is_loopback': True, ...}
应用二:子网扫描
在网络运维中,经常需要扫描某个子网内的所有地址,或者对特定范围内的地址进行操作。结合 hosts() 方法和 subnets() 方法,可以灵活地实现各种扫描策略。
def scan_subnet(cidr):
"""扫描子网内所有可用主机地址,返回地址列表"""
net = ipaddress.ip_network(cidr)
return list(net.hosts())
def get_network_info(cidr):
"""获取网络的完整信息摘要"""
net = ipaddress.ip_network(cidr)
return {
'network': str(net),
'netmask': str(net.netmask),
'broadcast': str(net.broadcast_address),
'total_addresses': net.num_addresses,
'usable_hosts': net.num_addresses - 2 if net.num_addresses > 2 else net.num_addresses,
'prefixlen': net.prefixlen,
'version': net.version,
}
# 划分多个小网络并分别扫描
def multi_subnet_scan(cidr, sub_prefix):
net = ipaddress.ip_network(cidr)
results = {}
for sub in net.subnets(new_prefix=sub_prefix):
results[str(sub)] = {
'hosts': list(sub.hosts()),
'total': sub.num_addresses,
}
return results
# 示例:将 /24 划分为 4 个 /26,分别获取信息
info = multi_subnet_scan('10.0.0.0/24', 26)
for net_name, data in info.items():
print(f"{net_name}: {data['total']} 个地址")
应用三:CIDR合并
在路由表优化和防火墙规则聚合中,经常需要将多个连续的CIDR块合并为更少、更大的网络块。collapse_addresses() 函数提供了这个能力。
def merge_cidrs(cidr_list):
"""将多个CIDR块合并为最小的不重叠网络列表"""
networks = [ipaddress.ip_network(c) for c in cidr_list]
merged = ipaddress.collapse_addresses(networks)
return list(merged)
# 合并连续子网
cidrs = [
'192.168.0.0/24',
'192.168.1.0/24',
'192.168.2.0/24',
'192.168.3.0/24',
]
result = merge_cidrs(cidrs)
for r in result:
print(r)
# 192.168.0.0/22(4个/24被合并为一个/22)
# 非连续子网无法合并
cidrs2 = ['10.0.0.0/24', '10.0.2.0/24']
result2 = merge_cidrs(cidrs2)
for r in result2:
print(r)
# 10.0.0.0/24(无法合并,各自保留)
# 10.0.2.0/24
# 混合大小的子网合并
cidrs3 = [
'10.1.0.0/24',
'10.1.1.0/24',
'10.1.2.0/23',
'10.1.4.0/24',
]
result3 = merge_cidrs(cidrs3)
for r in result3:
print(r)
# 10.1.0.0/22(前三个合并)
# 10.1.4.0/24(单独)
扩展应用:除了 collapse_addresses(),ipaddress 模块还提供了 summarize_address_range(first, last) 函数,用于将任意连续的地址范围转换为最简CIDR列表。这在防火墙规则优化和IP地址分配审计中非常实用。
# summarize_address_range:将地址范围转为CIDR列表
first = ipaddress.IPv4Address('192.168.0.0')
last = ipaddress.IPv4Address('192.168.3.255')
summary = list(ipaddress.summarize_address_range(first, last))
for s in summary:
print(s)
# 192.168.0.0/22
# 非对齐范围也能正确处理
first2 = ipaddress.IPv4Address('10.0.0.100')
last2 = ipaddress.IPv4Address('10.0.0.200')
summary2 = list(ipaddress.summarize_address_range(first2, last2))
for s in summary2:
print(s)
# 10.0.0.100/32
# 10.0.0.101/32
# 10.0.0.102/31
# ...(自动生成最简CIDR表示)
六、核心总结
ipaddress 模块核心要点:
1. 两大核心类:IPv4Address/IPv6Address 表示单个IP地址,IPv4Network/IPv6Network 表示子网。工厂函数 ip_address() 和 ip_network() 自动识别协议版本。
2. 地址属性丰富:通过 is_private、is_global、is_multicast、is_loopback、is_link_local 等布尔属性快速判断地址类型。
3. 网络对象核心:network_address、broadcast_address、netmask、hostmask、num_addresses 提供网络边界信息。
4. 子网划分:subnets(prefixlen_diff) / subnets(new_prefix=N) 将网络划分为更小子网;supernet() 向上合并。
5. 包含与重叠:in 运算符判断地址/网络的包含关系;overlaps() 检查网络是否重叠。
6. 实用工具函数:collapse_addresses() 合并CIDR;summarize_address_range() 将地址范围转为CIDR;hosts() 遍历可用主机地址;address_exclude() 排除子网。
7. 严格模式:默认严格模式要求构造网络时传入的地址必须是网络地址(主机位全零),可通过 strict=False 放宽。
8. 不可变性:所有 IP 地址和网络对象都是不可变的,创建后不能修改,保证了线程安全。
ipaddress 模块设计简洁、接口统一,是Python标准库中处理网络地址问题的首选工具。无论是简单的地址验证,还是复杂的子网规划与CIDR合并,它都能以寥寥数行代码优雅地解决问题。熟练掌握该模块,对于网络编程、系统管理脚本、安全工具开发等领域都有着直接的帮助。