An introduction to the ipaddress module¶
作者: | 彼得·穆迪 |
---|---|
作者: | 尼克·科格兰 |
Creating Address/Network/Interface objects¶
由于ipaddress
是一个用于检查和操作IP地址的模块,因此您需要做的第一件事是创建一些对象。您可以使用ipaddress
从字符串和整数创建对象。
A Note on IP Versions¶
对于不特别熟悉IP寻址的读者,重要的是要知道互联网协议当前正在从协议版本4移动到版本6。发生这种转换主要是因为协议的版本4不提供足够的地址来处理整个世界的需要,特别是考虑到与互联网直接连接的设备数量的增加。
解释协议的两个版本之间的差异的细节超出了本介绍的范围,但是读者需要至少知道这两个版本存在,并且有时有必要强制使用一个版本或其他。
IP Host Addresses¶
地址,通常称为“主机地址”是使用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')
Defining Networks¶
主机地址通常组合成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
不描述网络。这样的定义被称为接口对象,因为网络上的符号通常用于描述给定网络上的计算机的网络接口,并且在下一部分中进一步描述。
默认情况下,尝试创建具有设置的主机位的网络对象将导致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')
与地址一样,可以通过直接调用类构造函数而不是使用工厂函数来强制创建特定类型的网络。
Host Interfaces¶
如上所述,如果需要描述特定网络上的地址,则地址和网络类别都不足。Notation like 192.0.2.1/24
is commonly used by network engineers and the people who write tools for firewalls and routers as shorthand for “the host 192.0.2.1
on the network 192.0.2.0/24
”, Accordingly, ipaddress
provides a set of hybrid classes that associate an address with a particular network. 用于创建的接口与用于定义网络对象的接口相同,除了地址部分不被约束为网络地址。
>>> 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版本。
Inspecting Address/Network/Interface Objects¶
你已经去了创建一个IPv(4 | 6)(地址|网络|接口)对象的麻烦,所以你可能想得到它的信息。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地址。
Networks as lists of Addresses¶
将网络视为列表有时很有用。这意味着可以像这样索引它们:
>>> 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
Comparisons¶
ipaddress
提供了一些简单的,希望直观的方式来比较对象,它是有意义的:
>>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
True
如果尝试比较不同版本或不同类型的对象,则会引发TypeError
异常。
Using IP Addresses with other modules¶
使用IP地址的其他模块(例如socket
)通常不会直接从此模块接受对象。相反,它们必须被强制转换为另一个模块将接受的整数或字符串:
>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> str(addr4)
'192.0.2.1'
>>> int(addr4)
3221225985
Getting more detail when instance creation fails¶
当使用版本不可知的工厂函数创建地址/网络/接口对象时,任何错误都将报告为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)