ipaddress 模块简介¶
- 作者:
Peter Moody
- 作者:
Nick Coghlan
创建地址/网络/接口对象¶
由于 ipaddress
是一个用于检查和操作 IP 地址的模块,因此您要做的第一件事就是创建一些对象。您可以使用 ipaddress
从字符串和整数创建对象。
关于 IP 版本的说明¶
对于不太熟悉 IP 寻址的读者来说,了解互联网协议 (IP) 目前正在从协议的版本 4 迁移到版本 6 非常重要。这种转变主要是因为协议的版本 4 没有提供足够的地址来满足全世界的需求,尤其是考虑到越来越多的设备直接连接到互联网。
解释两个协议版本之间差异的细节超出了本介绍的范围,但读者至少需要知道这两个版本的存在,并且有时需要强制使用其中一个版本。
IP 主机地址¶
地址,通常称为“主机地址”,是使用 IP 寻址时的最基本单位。创建地址的最简单方法是使用 ipaddress.ip_address()
工厂函数,该函数会自动根据传入的值确定是创建 IPv4 地址还是 IPv6 地址。
>>> ipaddress.ip_address('192.0.2.1')
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address('2001:DB8::1')
IPv6Address('2001:db8::1')
地址也可以直接从整数创建。适合 32 位的值被认为是 IPv4 地址
>>> ipaddress.ip_address(3221225985)
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address(42540766411282592856903984951653826561)
IPv6Address('2001:db8::1')
要强制使用 IPv4 或 IPv6 地址,可以直接调用相关的类。这对于强制为小整数创建 IPv6 地址特别有用
>>> ipaddress.ip_address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv4Address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv6Address(1)
IPv6Address('::1')
定义网络¶
主机地址通常分组到 IP 网络中,因此 ipaddress
提供了一种创建、检查和操作网络定义的方法。IP 网络对象是从定义网络中包含的主机地址范围的字符串构造的。该信息的最简单形式是“网络地址/网络前缀”对,其中前缀定义用于确定地址是否属于网络的比较的前导位数,而网络地址定义这些位的预期值。
与地址一样,提供了一个工厂函数,该函数会自动确定正确的 IP 版本
>>> ipaddress.ip_network('192.0.2.0/24')
IPv4Network('192.0.2.0/24')
>>> ipaddress.ip_network('2001:db8::0/96')
IPv6Network('2001:db8::/96')
网络对象不能设置任何主机位。实际上,192.0.2.1/24
不描述一个网络。这种定义被称为接口对象,因为网络上的 IP 地址表示法通常用于描述给定网络上计算机的网络接口,并在下一节中进一步描述。
默认情况下,尝试创建具有主机位设置的网络对象将导致引发 ValueError
。若要请求将附加位强制为零,则可以将标志 strict=False
传递给构造函数。
>>> ipaddress.ip_network('192.0.2.1/24')
Traceback (most recent call last):
...
ValueError: 192.0.2.1/24 has host bits set
>>> ipaddress.ip_network('192.0.2.1/24', strict=False)
IPv4Network('192.0.2.0/24')
虽然字符串形式提供了更大的灵活性,但网络也可以用整数定义,就像主机地址一样。在这种情况下,网络被认为只包含由整数标识的单个地址,因此网络前缀包含整个网络地址。
>>> ipaddress.ip_network(3221225984)
IPv4Network('192.0.2.0/32')
>>> ipaddress.ip_network(42540766411282592856903984951653826560)
IPv6Network('2001:db8::/128')
与地址一样,可以通过直接调用类构造函数而不是使用工厂函数来强制创建特定类型的网络。
主机接口¶
如上所述,如果您需要描述特定网络上的地址,则地址类和网络类都不够。网络工程师和编写防火墙和路由器工具的人员通常使用 192.0.2.1/24
这样的表示法作为“网络 192.0.2.0/24
上的主机 192.0.2.1
”的简写。因此,ipaddress
提供了一组混合类,将地址与特定网络相关联。创建接口与定义网络对象相同,只是地址部分不受限于网络地址。
>>> ipaddress.ip_interface('192.0.2.1/24')
IPv4Interface('192.0.2.1/24')
>>> ipaddress.ip_interface('2001:db8::1/96')
IPv6Interface('2001:db8::1/96')
接受整数输入(与网络一样),并且可以通过直接调用相关构造函数来强制使用特定 IP 版本。
检查地址/网络/接口对象¶
您已经费心创建了一个 IPv(4|6)(Address|Network|Interface) 对象,因此您可能希望获取有关它的信息。 ipaddress
试图使这变得容易和直观。
提取 IP 版本
>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> addr6 = ipaddress.ip_address('2001:db8::1')
>>> addr6.version
6
>>> addr4.version
4
从接口获取网络
>>> host4 = ipaddress.ip_interface('192.0.2.1/24')
>>> host4.network
IPv4Network('192.0.2.0/24')
>>> host6 = ipaddress.ip_interface('2001:db8::1/96')
>>> host6.network
IPv6Network('2001:db8::/96')
找出网络中有多少个独立地址
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.num_addresses
256
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.num_addresses
4294967296
遍历网络上的“可用”地址
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> for x in net4.hosts():
... print(x)
192.0.2.1
192.0.2.2
192.0.2.3
192.0.2.4
...
192.0.2.252
192.0.2.253
192.0.2.254
获取网络掩码(即与网络前缀相对应的设置位)或主机掩码(任何不是网络掩码一部分的位)
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.netmask
IPv4Address('255.255.255.0')
>>> net4.hostmask
IPv4Address('0.0.0.255')
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.netmask
IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::')
>>> net6.hostmask
IPv6Address('::ffff:ffff')
展开或压缩地址
>>> addr6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0001'
>>> addr6.compressed
'2001:db8::1'
>>> net6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0000/96'
>>> net6.compressed
'2001:db8::/96'
虽然 IPv4 不支持展开或压缩,但相关对象仍然提供相关属性,以便版本中立代码可以轻松确保对 IPv6 地址使用最简洁或最详细的形式,同时仍然正确处理 IPv4 地址。
网络作为地址列表¶
有时将网络视为列表很有用。这意味着可以像这样索引它们
>>> net4[1]
IPv4Address('192.0.2.1')
>>> net4[-1]
IPv4Address('192.0.2.255')
>>> net6[1]
IPv6Address('2001:db8::1')
>>> net6[-1]
IPv6Address('2001:db8::ffff:ffff')
这也意味着网络对象适合使用列表成员资格测试语法,例如
if address in network:
# do something
基于网络前缀有效地进行包含测试
>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> addr4 in ipaddress.ip_network('192.0.2.0/24')
True
>>> addr4 in ipaddress.ip_network('192.0.3.0/24')
False
比较¶
ipaddress
提供了一些简单且直观的比较对象的方法,在有意义的情况下。
>>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
True
如果尝试比较不同版本或不同类型的对象,将引发 TypeError
异常。
使用 IP 地址与其他模块¶
其他使用 IP 地址的模块(例如 socket
)通常不会直接接受来自此模块的对象。相反,它们必须被强制转换为其他模块将接受的整数或字符串。
>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> str(addr4)
'192.0.2.1'
>>> int(addr4)
3221225985
实例创建失败时获取更多详细信息¶
使用与版本无关的工厂函数创建地址/网络/接口对象时,任何错误都将被报告为 ValueError
,并显示一个通用的错误消息,简单地说传入的值未被识别为该类型的对象。缺少特定错误的原因是,需要知道该值是否 *应该* 是 IPv4 或 IPv6,才能提供有关其被拒绝原因的更多详细信息。
为了支持在访问这些额外详细信息很有用时的情况,各个类构造函数实际上会引发 ValueError
子类 ipaddress.AddressValueError
和 ipaddress.NetmaskValueError
,以准确指示定义的哪个部分解析失败。
直接使用类构造函数时,错误消息将更加详细。例如
>>> ipaddress.ip_address("192.168.0.256")
Traceback (most recent call last):
...
ValueError: '192.168.0.256' does not appear to be an IPv4 or IPv6 address
>>> ipaddress.IPv4Address("192.168.0.256")
Traceback (most recent call last):
...
ipaddress.AddressValueError: Octet 256 (> 255) not permitted in '192.168.0.256'
>>> ipaddress.ip_network("192.168.0.1/64")
Traceback (most recent call last):
...
ValueError: '192.168.0.1/64' does not appear to be an IPv4 or IPv6 network
>>> ipaddress.IPv4Network("192.168.0.1/64")
Traceback (most recent call last):
...
ipaddress.NetmaskValueError: '64' is not a valid netmask
但是,模块特定的两个异常都以 ValueError
作为其父类,因此,如果您不关心特定类型的错误,您仍然可以编写如下代码
try:
network = ipaddress.IPv4Network(address)
except ValueError:
print('address/netmask is invalid for IPv4:', address)