ipaddress 模块简介

  • author

    • Peter Moody
  • author

    • Nick Coghlan

Overview

本文档旨在简要介绍ipaddress模块。它主要针对尚未熟悉 IP 网络术语的用户,但对于希望概述ipaddress如何表示 IP 网络寻址概念的网络工程师也很有用。

创建地址/网络/接口对象

由于ipaddress是用于检查和操作 IP 地址的模块,因此您要做的第一件事就是创建一些对象。您可以使用ipaddress从字符串和整数创建对象。

IP 版本说明

对于不特别了解 IP 寻址的 Reader,重要的是要知道 Internet 协议当前正在从协议的版本 4 过渡到版本 6.之所以发生这种过渡,很大程度上是因为协议的版本 4 没有。提供足够的地址来满足整个世界的需求,特别是考虑到越来越多具有直接连接到 Internet 的设备。

解释协议的两个版本之间的差异的详细信息超出了本导言的范围,但是 Reader 至少需要意识到这两个版本的存在,并且有时有必要强制使用一个版本或其他。

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')

Defining Networks

主机地址通常分为 IP 网络,因此ipaddress提供了一种创建,检查和操作网络定义的方法。 IP 网络对象是由字符串构成的,这些字符串定义了该网络一部分的主机地址范围。该信息的最简单形式是“网络地址/网络前缀”对,其中前缀定义了前导位数,将这些前导位数进行比较以确定地址是否为网络的一部分,而网络地址则定义了期望的值。这些位。

至于地址,提供了工厂Function,可自动确定正确的 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”符号通常用于描述给定网络上计算机的网络接口,并在下一节中进一步描述。

默认情况下,try创建设置了主机位的网络对象将导致引发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')

虽然字符串形式提供了更大的灵 Active,但也可以使用整数定义网络,就像主机地址一样。在这种情况下,网络被视为仅包含由整数标识的单个地址,因此网络前缀包括整个网络地址:

>>> ipaddress.ip_network(3221225984)
IPv4Network('192.0.2.0/32')
>>> ipaddress.ip_network(42540766411282592856903984951653826560)
IPv6Network('2001:db8::/128')

与地址一样,可以pass直接调用类构造函数而不是使用工厂函数来强制创建特定类型的网络。

Host Interfaces

如上所述,如果您需要描述特定网络上的地址,则该地址和网络类别都不足够。网络工程师和为防火墙和 Router 编写工具的人通常使用诸如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')

接受整数 Importing(与网络一样),并且可以pass直接调用相关的构造函数来强制使用特定 IP 版本。

检查地址/网络/接口对象

您已经麻烦创建一个 IPv(4 | 6)(Address | Network | Interface)对象,因此您可能想获取有关它的信息。 ipaddresstry使此操作简单直观。

提取 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

Comparisons

ipaddress提供了一些比较简单,希望直观的方法来比较对象(如果有意义):

>>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
True

如果您try比较不同版本或不同类型的对象,则会引发TypeError异常。

将 IP 地址与其他模块一起使用

使用 IP 地址的其他模块(例如socket)通常不会直接接受该模块中的对象。相反,必须将它们强制为另一个模块将接受的整数或字符串:

>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> str(addr4)
'192.0.2.1'
>>> int(addr4)
3221225985

创建实例失败时获取更多详细信息

使用与版本无关的工厂Function创建地址/网络/接口对象时,任何错误都将报告为ValueError,并带有一条通用错误消息,该错误消息只是简单地指出传递的值未被识别为该类型的对象。缺少特定的错误是因为有必要知道该值是*假定为 IPv4 还是 IPv6,以便提供有关为何拒绝该值的更多详细信息。

为了支持访问此附加详细信息很有用的用例,各个类的构造函数实际上会引发ValueError子类ipaddress.AddressValueErroripaddress.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)