在本文中,我们会看一看Python 3.3及更高版本中提供的ipaddress模块。 本教程旨在为想知道如何在Python中解析和使用IP地址的网络工程师们提供一个简要参考。
在这篇概述中,你将了解到:
从大的方面来说,IPv4地址和IPv6地址具有相同的目的和功能。但是,由于每个协议的地址结构存在很大差异。本教程用了不同的部分,来分别讨论IPv4和IPv6。
当今的互联网中,IPv4协议承担了绝大部分的IP处理任务,在不久的将来也依旧如此。 尽管IPv6所带来的规模和功能方面的增强对未来互联网不可或缺,正在被逐步应用,但是到目前为止,应用率仍然很低。
一个IPv4地址由32位组成,分为四个“八位组”。 “八位组”一词用于标识一个八位结构来代替更常见的术语“字节”,但它们的定义相同。四个八位组被称为octet1,octet2,octet3和octet4。这是一个“点分十进制”格式,其中每个八位组对应一个从0到255的十进制值。
IPv4地址示例:192.168.100.10
IPv4地址示例(CIDR表示法):192.168.100.10/24
“/24”是CIDR表示法,表示32位的前24位用于标识地址的网络部分。 记住每个八位组长度为8位,这意味着前三个字节(3×8 = 24)标识网络(192.168.100.x),地址的其余八位标识节点(x.x.x.10)。
CIDR表示法可以是从 /8位 到 /30位的任何值,偶尔有 /32位(/31无效),但通常使用/24。 例如,你的家庭网络,或你的学校或公司网络很可能用/24 CIDR来表示。
用于表示网络标识的早期术语是子网掩码,其中CIDR表示为单独的点分十进制数。 例如,一个/24 CIDR相当于一个网络掩码255.255.255.0。
IPv6地址长度为128位,与IPv4地址中的32位相比,有显著的增加。 IPv4和IPv6之间有很多不同之处,但最大的区别在于寻址结构。 额外的长度提供了可支持的网络和主机数量的指数级增长。
IPv6地址示例:2001:db8:abcd:100::1/64
在IPv4地址使用点分十进制格式的情况下,IPv6协议使用十六进制表示法。 IPv6地址中的每个位置表示4个位,其值从0到f,按以下方式组织:
所有的IPv6地址结构都使用CIDR表示法来确定有多少前导位用于网络标识,其余部分则用于主机/接口标识。考虑到是128位,产生的组合有很多。
ipaddress模块是按照CIDR表示法设计的,由于其简洁易用,受到人们的推荐。 ipaddress模块还包含了一些方法,用于在必要的情况下还原子网掩码。
IPv4地址的最初定义中包含一个“类”,这个“类”由第一个八位组中的地址范围所定义。 ipaddress模块不识别IPv4类,故在本教程中不会涉及。
ipaddress模块包含三个特定的IPv4地址对象类型:
“主机”和“接口”之间的主要区别在于主机或ip_address对象不包含CIDR表示法,而ip_interface对象包含CIDR表示法:
ipaddress.ip_address() 工厂函数用于创建ip_address对象。它会根据传入的值自动确定是创建IPv4还是IPv6地址(IPv6地址将在本教程的后面部分讨论)。 如上所述,这个对象表示一个数据包在穿越不需要CIDR的网络的过程中,所发现的IP地址。
在多数情况下,用于创建ip_address对象的值将是一个字符串,格式为IPv4点分十进制,如图所示:
或者,IPv4地址可以以二进制形式输入,如完整32位二进制值的十进制值,或按照此例,以十六进制格式输入:
第一个例子使用完整的32位地址,第二个例子是32位地址的十进制值。 两者都很笨拙,容易出错且没什么太大的价值。 第三个示例使用十六进制值,这可能很有用,因为解析或嗅探中的大多数数据包,都以十六进制格式表示。
ipaddress.ip_interface() 工厂函数用于创建ip_interface对象,该对象根据传入的值自动确定是创建IPv4还是IPv6地址(IPv6地址将在本教程的后面部分讨论)。
如前所述,ip_interface对象表示在正确处理数据包所需的CIDR(或掩码)所在的主机或网络接口上找到的IP地址。
在创建ip_interface选项时可以使用与ip_address选项(二进制,十进制值,十六进制)相同的选项。 但是,唯一通过CIDR表示法或掩码来有效创建ip_interface的办法,是使用点分十进制IPv4地址字符串。
ipaddress.ip_network()工厂函数用于创建ip_network对象,该对象根据传入的值自动确定是创建IPv4还是IPv6地址(IPv6地址将在本教程的后面部分讨论)。
IP网络定义:包括了一个网络或子网的连续IP地址范围。 例如:
创建ip_network对象遵循与创建ip_interface对象相同的语法:
在上面的例子中,使用的网络地址必须是一个有效的网络地址,它是构成网络的IPv4地址范围中的第一个地址。 否则,Python将抛出一个异常:
在使用主机或路由器接口时,通常需要确定网络地址。 可以经由计算得出,但是需要几个步骤,可以使用strict = False选项(strict = True是默认值)在一个步骤中完成。
在上面的例子中,ip_interface地址是已知的(192.168.100.10),但不是接口所属的ip_network。 使用strict = False选项,计算ip_network地址(192.168.100.0/24)并将其填充到ip_network对象中。
与IPv4一样,ipaddress模块使用与IPv4相同的三种基本工厂功能。 包括:
由于详细信息在IPv4部分中已经介绍,在此仅作简要描述。
ipaddress.ip_address() 工厂函数用于创建ip_address对象。 它会根据传入的值自动确定使用IPv6地址格式。 请注意,CIDR表示法未与ip_address函数一起使用。
在大多数情况下,用于为IPv6创建ip_address对象的值将是根据此示例的IPv6四进制/六进制格式的字符串:
与IPv4一样,可以使用完整的二进制,十进制或十六进制值创建IPv6地址对象。 对于IPv4地址,32位难以处理,而对于128位IPv6地址来说更是尴尬。 实际上,预计八个四重组表示的字符串将是一般形式。
ipaddress.ip_interface() 工厂函数用于创建ip_interface对象,该对象根据传入的值自动创建IPv6地址。 请注意,函数中必须包含CIDR表示法。
ipaddress.ip_network() 工厂函数用于根据传入的值为IPv6创建一个ip_network对象。
与IPv4一样,IPv6网络被定义为可分配给特定主机或路由器接口的一系列连续IP地址。
使用我们以前的示例2001:db8:abcd:100:: /64,/64 CIDR指定四个四重组构成完整的网络标识。 请记住,前三个四重组是IPS分配的全局ID,第四个四重组识别内部子网编号。 64位的余额用于从“0000:0000:0000:0001”到“ffff:ffff:ffff:fffe”的范围内的主机标识。
与IPv4寻址一样,IPv6子网中的第一个和最后一个地址不能用于主机寻址。 给定一个/ 64 CIDR,这意味着有2到2的64次方(减2)可能的主机地址,这意味着从数学角度,每个网络/子网有18,446,744,073,709,551,614个可能的主机地址。
上述全局地址分配如下:
这里是一些额外的资源,以便你进一步了解Python中的ipaddress模块:
本文的扩展PDF版本以及其他信息https://dbader.org/static/img/ipaddress-module-introduction.pdf
ipaddress模块文档https://docs.python.org/3/library/ipaddress.html
ipaddress模块介绍https://docs.python.org/3/howto/ipaddress.html
维基百科 - IPv4https://en.wikipedia.org/wiki/IPv4
维基百科 - IPv6https://en.wikipedia.org/wiki/IPv6