• Python网络开发之-ipaddress模块的使用
  • 发布于 2个月前
  • 100 热度
    1 评论
在本文中,我们会看一看Python 3.3及更高版本中提供的ipaddress模块。 本教程旨在为想知道如何在Python中解析和使用IP地址的网络工程师们提供一个简要参考。
在这篇概述中,你将了解到:
1.IPv4和IPv6地址之间的区别是什么
2.如何使用Python的ipaddress模块处理IPv4地址
3.如何使用Python的ipaddress模块处理IPv6地址
IPv4与IPv6地址 - 入门知识
从大的方面来说,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,按以下方式组织:
128位被分成8组,每组16位,每组由冒号分隔。一个组被称为4个十六进制字符(4个十六进制字符乘以4位= 16位)的“四重组”或“十六位组”。在上面的例子中,第一个四重组是“2001”。
任何四重组中的头部的0会被去除/压缩。在上面的例子中,第二个四重组是“db8”,实际上是“0db8”“,最开头的0被去掉了。最后一个四重组是“1”,实际上是“0001”“,三个头部0被压缩了。
如果一个四重组包含全零,它将被压缩为一个零。例如:具有“:0000:”的四重组将被压缩为“:0:”。
如果一个地址包含一个连续的全部为零的四重组,则连续的零被压缩并用双冒号表示。在上面的例子中,双冒号表示3个全部为零的四重组,或者“:0000:0000:0000:”浓缩为“::”。由于范例的地址有5个有值的四重组,所以压缩以后的四重组的数量肯定是3个(总数减8)。

所有的IPv6地址结构都使用CIDR表示法来确定有多少前导位用于网络标识,其余部分则用于主机/接口标识。考虑到是128位,产生的组合有很多。


Python的ipaddress模块和IPv4地址
ipaddress模块是按照CIDR表示法设计的,由于其简洁易用,受到人们的推荐。 ipaddress模块还包含了一些方法,用于在必要的情况下还原子网掩码。
IPv4地址的最初定义中包含一个“类”,这个“类”由第一个八位组中的地址范围所定义。 ipaddress模块不识别IPv4类,故在本教程中不会涉及。
ipaddress模块包含三个特定的IPv4地址对象类型:
1.一个“主机”,或一个不包含CIDR表示法的独立的地址对象
2.包含CIDR表示法的单个接口地址对象
以及一个网络地址对象,指的是整个网络的IP地址范围。
3.“主机”和“接口”之间的主要区别在于主机或ip_address对象不包含CIDR表示法,而ip_interface对象包含CIDR表示法:
A.处理不需要或不使用CIDR表示法的IP数据包时,ip_address对象最为有用。
B.当使用节点和接口标识来连接到必须包含网络/子网标识的IP网络时,ip_interface对象最管用。

C.ip_network对象包含网络中的所有地址,并且对于网络标识非常有用。


用ipaddress创建IPv4主机地址对象
ipaddress.ip_address() 工厂函数用于创建ip_address对象。它会根据传入的值自动确定是创建IPv4还是IPv6地址(IPv6地址将在本教程的后面部分讨论)。 如上所述,这个对象表示一个数据包在穿越不需要CIDR的网络的过程中,所发现的IP地址。
在多数情况下,用于创建ip_address对象的值将是一个字符串,格式为IPv4点分十进制,实例所示:
>>> import ipaddress
>>> my_ip = ipaddress.ip_address('192.168.100.10')
>>> my_ip
IPv4Address('192.168.100.10')
或者,IPv4地址可以以二进制形式输入,如完整32位二进制值的十进制值,或按照此例,以十六进制格式输入:
# All 32 binary bits can be used to create an IPv4 address:
>>> ipaddress.ip_address(0b11000000101010000110010000001010)
IPv4Address('192.168.100.10')
# The decimal value of the 32 bit binary number can also be used:
>>> ipaddress.ip_address(3232261130)
IPv4Address('192.168.100.10')
# As can the hexadecimal value of the 32 bits:
>>> ipaddress.ip_address(0xC0A8640A)
IPv4Address('192.168.100.10')

第一个例子使用完整的32位地址,第二个例子是32位地址的十进制值。 两者都很笨拙,容易出错且没什么太大的价值。 第三个示例使用十六进制值,这可能很有用,因为解析或嗅探中的大多数数据包,都以十六进制格式表示。


用ipaddress创建IPv4接口地址对象
ipaddress.ip_interface() 工厂函数用于创建ip_interface对象,该对象根据传入的值自动确定是创建IPv4还是IPv6地址(IPv6地址将在本教程的后面部分讨论)。
如前所述,ip_interface对象表示在正确处理数据包所需的CIDR(或掩码)所在的主机或网络接口上找到的IP地址。
# An ip_interface object is used to represent IP addressing
# for a host or router interface, including the CIDR:
>>> my_ip = ipaddress.ip_interface('192.168.100.10/24')
>>> my_ip
IPv4Interface('192.168.100.10/24')

# This method translates the CIDR into a mask as would normally
# be used on a host or router interface
>>> my_ip.netmask
IPv4Address('255.255.255.0')

在创建ip_interface选项时可以使用与ip_address选项(二进制,十进制值,十六进制)相同的选项。 但是,唯一通过CIDR表示法或掩码来有效创建ip_interface的办法,是使用点分十进制IPv4地址字符串。

用ipaddress创建IPv4网络地址对象
ipaddress.ip_network()工厂函数用于创建ip_network对象,该对象根据传入的值自动确定是创建IPv4还是IPv6地址(IPv6地址将在本教程的后面部分讨论)。
IP网络定义:包括了一个网络或子网的连续IP地址范围。 例如:
192.168.100.0/24是192.168.100.0网络,其中/24指定前3个八位组组构成网络标识。
第4个八位组用于分配给各个主机和路由器接口。
地址范围是192.168.100.1到.254。
192.168.100.0用于定义网络/子网,192.168.100.255是该网络的广播地址。 它们都不能用于分配给主机或路由器接口。
创建ip_network对象遵循与创建ip_interface对象相同的语法:
# Creates an ip_network object. The IPv4 address and CIDR must be
# a valid network address, the first address in an address range:
>>> ipaddress.ip_network('192.168.100.0/24')
IPv4Network('192.168.100.0/24')
在上面的例子中,使用的网络地址必须是一个有效的网络地址,它是构成网络的IPv4地址范围中的第一个地址。 否则,Python将抛出一个异常:
# Python will throw an exception if the address used is not
# a valid network address. In the following, ".10" is a host address
# not a valid network address ident cation, which is ".0":
>>> ipaddress.ip_network('192.168.100.10/24')
ValueError: "192.168.100.10/24 has host bits set"
在使用主机或路由器接口时,通常需要确定网络地址。 可以经由计算得出,但是需要几个步骤,可以使用strict = False选项(strict = True是默认值)在一个步骤中完成。
# If the network address needs to be calculated,
# use the strict=False option. This will calculate and populate
# the ip_network object with the network rather than the
# interface address:
>>> my_ip = ipaddress.ip_interface('192.168.100.10/24')
>>> my_ip
IPv4Interface('192.168.100.10/24')
>>> my_ip_net = ipaddress.ip_network(my_ip, strict=False)
>>> my_ip_net
IPv4Network('192.168.100.0/24')

在上面的例子中,ip_interface地址是已知的(192.168.100.10),但不是接口所属的ip_network。 使用strict = False选项,计算ip_network地址(192.168.100.0/24)并将其填充到ip_network对象中。

Python的ipaddress模块和IPv6地址
与IPv4一样,ipaddress模块使用与IPv4相同的三种基本工厂功能。 包括:
1.一个“主机”,或一个不包含CIDR表示法的独立地址对象,
2.包含CIDR表示法的接口地址对象
3.以及引用整个网络的IP地址范围的网络地址对象。

由于详细信息在IPv4部分中已经介绍,在此仅作简要描述。

用ipaddress创建IPv6主机地址对象
ipaddress.ip_address() 工厂函数用于创建ip_address对象。 它会根据传入的值自动确定使用IPv6地址格式。 请注意,CIDR表示法未与ip_address函数一起使用。
在大多数情况下,用于为IPv6创建ip_address对象的值将是根据此示例的IPv6四进制/六进制格式的字符串:
# Create an IPv6 Address Object for a Global Address:
>>> ipaddress.ip_address('2001:db8:abcd:100::1')
IPv6Address('2001:db8:abcd:100::1')

# Create an IPv6 Address Object for a link-local address:
>>> ipaddress.ip_address('fe80::1')
IPv6Address('fe80::1')

与IPv4一样,可以使用完整的二进制,十进制或十六进制值创建IPv6地址对象。 对于IPv4地址,32位难以处理,而对于128位IPv6地址来说更是尴尬。 实际上,预计八个四重组表示的字符串将是一般形式。
用ipaddress创建IPv6接口地址对象
ipaddress.ip_interface() 工厂函数用于创建ip_interface对象,该对象根据传入的值自动创建IPv6地址。 请注意,函数中必须包含CIDR表示法。
# Creates an IP Interface Object for a Global Address:
>>> ipaddress.ip_interface('2001:db8:abcd:100::1/64')
IPv6Interface('2001:db8:abcd:100::1/64')

# Creates an IP Interface Object for a Link-local Address:
ipaddress.ip_interface('fe80::1/64')
IPv6Interface('fe80::1/64')
用ipaddress创建IPv6网络地址对象
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个可能的主机地址。
# Creates an IP Network Object for a Global Address:
>>> myIPv6net = ipaddress.ip_network('2001:db8:abcd:100::/64')
>>> myIPv6net
IPv6Network('2001:db8:abcd:100::/64')

# Creates an IP Network Object for a Link-local Address:
>>> myIPv6 = ipaddress.ip_network('fe80::/64')
>>> myIPv6
IPv6Network('fe80::/64')
上述全局地址分配如下:
全局标识符由ISP分配:2001:db8:abcd::/48
子网标识:2001:db8:abcd:100:: / 64
子网中的第一个可用地址:2001:db8:abcd:100:: 1/64

子网中最后一个可用地址:2001:db8:abcd:100:ffff:ffff:ffff:fffeffff / 64


更多资源
这里是一些额外的资源,以便你进一步了解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 
用户评论