IPy-IPv4和IPv6地址处理模块学习笔记

在日常网络规划中,会有很多关于IP地址的分配规划问题,如果是手动分配,在量很大的情况下,容易出错。而利用IPy这个python模块,可以很容易实现对iP地址的分配等操作。

以下是对IPy模块学习的一个记录。

IPy模块简介

主要用处:

IPy-用于处理IPv4和IPv6地址和网络的类和工具。

IPy相关:

IPy源码: https://github.com/autocracy/python-ipy/

IPy类和方法

IPy主要有如下类和方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
dir(IPy)
Out[8]:
['INT_TYPES',
'IP',
'IPSet',
'IPV6_MAP_MASK',
'IPV6_TEST_MAP',
'IPint',
'IPv4ranges',
'IPv6ranges',
'MAX_IPV4_ADDRESS',
'MAX_IPV6_ADDRESS',
'STR_TYPES',
'_BitTable',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'__version__',
'_checkNetaddrWorksWithPrefixlen',
'_checkNetmask',
'_checkPrefix',
'_count0Bits',
'_count1Bits',
'_countFollowingZeros',
'_intToBin',
'_ipVersionToLen',
'_netmaskToPrefixlen',
'_parseAddressIPv6',
'_prefixlenToNetmask',
'_remove_subprefix',
'bisect',
'collections',
'intToIp',
'parseAddress',
'sys',
'types',
'xrange']

其中有IPint,IP,IPSet是三个类。

一般都使用IP类,IP类是IPint的代替,IP类继承IPint类。

INT_TYPES,IPV6_MAP_MASK,IPV6_TEST_MAP,IPv4ranges, IPv6ranges,MAX_IPV4_ADDRESS,MAX_IPV6_ADDRESS,STR_TYPES这都是定义的常量。

IPint类 class IPint(object):

处理返回整数的IP地址。

因为IP类继承自IPint,所以IPint类中的方法在IP类中同样适用。

下面会记录常用方法。

__init__(self, data, ipversion=0, make_net=0)

  • data可以是常见的IPv4和IPv6地址的各种表现形式。支持前缀表示,IP段表示,小数点掩码表示,单个IP等。

    也可以输入10进制的进制,2进制的地址等。

  • ipversion可以制定地址的版本,版本为4或者6,输入其他会报错。

  • mask_net如果是为Ture,则可以通过指定掩码来分配IP地址。

测试如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> print(IP('127.0.0.0/8'))
127.0.0.0/8
>>> print(IP('127.0.0.0/255.0.0.0'))
127.0.0.0/8
>>> print(IP('127.0.0.0-127.255.255.255'))
127.0.0.0/8
>>> print(IP('127.0.0.1/255.0.0.0', make_net=True))
127.0.0.0/8
[39]: from IPy import IPint
ip = IPint('192.168.1.0/24',make_net=True)
ip
Out[41]: IPint('192.168.1.0/24')
for x in ip:
print(x)

3232235776
3232235777 #返回的是整数类型的IP地址。
3232235778
3232235779
3232235780
...

常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#实例化一个IP地址
ip = IPint('192.168.1.0/24')
ip.int() #返回第一个长整型的IP地址
Out[52]: 3232235776
ip.version() #返回ip的版本号
Out[53]: 4
ip.prefixlen() #返回掩码长度
Out[54]: 24
ip.net() #返回第一个长整型的IP地址
Out[55]: 3232235776
ip.broadcast() #返回长整型的广播地址
Out[56]: 3232236031
-----------------------------------------

_printPrefix(self, want): #按输入的want打印掩码
ip._printPrefix(0) #want == 0,None,什么也不返回
Out[58]: ''
ip._printPrefix(1) #want == 1, 返回前缀形式的掩码
Out[59]: '/24'
ip._printPrefix(2) #want == 2,返回小数点的掩码
Out[60]: '/255.255.255.0'
ip._printPrefix(3) #want == 3,返回网络号的最后一个地址
Out[61]: '-192.168.1.255'

ipv6 = IPint('2003::/64')

ipv6._printPrefix
Out[70]: <bound method IPint._printPrefix of IPint('2003::/64')>
ipv6._printPrefix(0)
Out[71]: ''
ipv6._printPrefix(1)
Out[72]: '/64'
ipv6._printPrefix(2)
Out[73]: '/ffff:ffff:ffff:ffff:0000:0000:0000:0000'
ipv6._printPrefix(3)
Out[74]: '-2003:0000:0000:0000:ffff:ffff:ffff:ffff'

--------------------------------------
#可以将IP地址转化为多种类型的字符串。
ipv6.strBin() #返会IPv4或者IPv6的2进制形式字符串
Out[79]: '00100000000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
ipv6.strCompressed() #返会简写的形式的地址,主要针对IPv6有效果
Out[80]: '2003::/64'
ipv6.strDec() #返会10进制的地址形式
Out[81]: '42550872755692912415807417417958686720'
ipv6.strFullsize() #返会完整形式的地址格式。
Out[82]: '2003:0000:0000:0000:0000:0000:0000:0000/64'
ipv6.strHex() #返会16进制形式的地址
Out[83]: '0x20030000000000000000000000000000'
ipv6.strNetmask() #返会掩码长度
Out[84]: '/64'
ipv6.strNormal(wantprefixlen = None) #可以按输入的wantprefixlen来转化为字符串
ipv6.strNormal(wantprefixlen = 0) #不输出掩码
Out[92]: '2003:0:0:0:0:0:0:0'
ipv6.strNormal((wantprefixlen = 1) #输出前缀掩码
Out[93]: '2003:0:0:0:0:0:0:0/64'
ipv6.strNormal((wantprefixlen = 2) #输出网络掩码
Out[94]: '2003:0:0:0:0:0:0:0/ffff:ffff:ffff:ffff:0000:0000:0000:0000'
ipv6.strNormal(3) #输出地址段
Out[95]: '2003:0:0:0:0:0:0:0-2003:0000:0000:0000:ffff:ffff:ffff:ff
---------------------------------------

ip.iptype() #返会IP地址的类型
Out[98]: 'PRIVATE'
ip2 = IPint('1.1.1.1')
ip2.iptype()
Out[100]: 'PUBLIC'
print(IP('127.0.0.1').iptype())
Out[101]: LOOPBACK
print(IP('::1').iptype())
Out[102]: LOOPBACK
print(IP('2001:0658:022a:cafe:0200::1').iptype())
Out[103]: ALLOCATED RIPE NCC
-----------------------------------------

ip.netmask() #返会整型形式的掩码
Out[104]: 4294967040
ip.strNetmask() #返会字符串形式的掩码
Out[105]: '255.255.255.0'
ip.len() #返会子网的长度
Out[106]: 256
IP('10.1.1.0/28').len()
Out[108]: 16

----------------------------------------
ip.__getitem__(1) #返会网络号中第i个地址。
Out[120]: 3232235777
ip.__getitem__(2)
Out[121]: 3232235778
ip.__getitem__(3)
Out[122]: 3232235779

ip
Out[124]: IPint('192.168.1.0/24')
ip.__contains__('192.168.1.1') #判断一个地址是否在这个网段中
Out[125]: True
ip.__contains__('192.168.2.1')
Out[126]: False
'192.168.1.1' in ip
Out[127]: True
'192.168.3.1' in ip
Out[128]: False

------------------------------------------
#检查给定IP地址是否在这个实例化的IP地址返回内
def overlaps(self, item):
#如果不重叠,返会0;
#如果给定地址在这个实例化的IP地址范围内返会1.
#如果给定地址在不这个实例化的IP地址范围内返会-1.
IP('192.168.1.0/24').overlaps('192.168.1.254')
Out[131]: 1
IP('192.168.1.0/24').overlaps('192.168.2.254')
Out[132]: 0
IP('192.168.1.0/24').overlaps('192.168.0.0/16')
Out[134]: -1

----------------------------------------
#实例化后的两个IP地址可以进行比较,IPv4总是小于IPv6地址,第一个地址总是小的。 掩码短的小于掩码长的
#如果self < other,返会-1, 如果self == other,返会0,如果self > other,返会1.

ip
Out[158]: IPint('192.168.1.0/24')
ip2
Out[159]: IPint('1.1.1.1')
ip3
Out[160]: IP('192.0.0.0/8')
ipv6
Out[161]: IPint('2003::/64')
ip4 = IP('192.168.1.0/24')
ip.__cmp__(ip4)
Out[163]: 0
ip.__cmp__(ip3)
Out[166]: 1
ip.__cmp__(ipv6)
Out[168]: -1
ip > ip2
Out[177]: True
ip < ip2
Out[178]: False
ip == i

IP类,class IP(IPint):

IP类继承自IPint,所以IP类具有IPint的所有方法。

IP类主要处理IP地址和网络。IP类与IPint相比,返会的不再是整数型值,而是网络形式的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ipint = IPint('192.168.1.0/24')
ip = IP('192.168.1.0/24')
ipint.net()
Out[197]: 3232235776
ip.net() #返回网络地址
Out[198]: IP('192.168.1.0')
ip.broadcast() #返会广播地址
Out[199]: IP('192.168.1.255')
ipint.broadcast()
Out[200]: 3232236031
ip.netmask() #返会网络掩码
Out[201]: IP('255.255.255.0')
ip.reverseName()
Out[207]: '1.168.192.in-addr.arpa.'
ip.reverseNames() #返会用于反向查找的列表值。
Out[208]: ['1.168.192.in-addr.arpa.']
IP('213.221.112.224/30').reverseNames()

Out[209]:
['224.112.221.213.in-addr.arpa.',
'225.112.221.213.in-addr.arpa.',
'226.112.221.213.in-addr.arpa.',
'227.112.221.213.in-addr.arpa.']
IP('::1:2').reverseNames()
Out[211]: ['2.0.0.0.1.ip6.arpa.']
IP('213.221.112.224/30').reverseName() #返会用于反向查找的一个字符串,而不是列表。
Out[217]: '224-227.112.221.213.in-addr.arpa.'

IP('10.1.1.1').make_net(24) #对于单个IP地址,可以加上掩码,返会新的IP地址实例
Out[231]: IP('10.1.1.0/24')
ip
Out[235]: IP('192.168.1.0/24')
ip.__getitem__(1) #返回网络号中的地i个地址
Out[236]: IP('192.168.1.1')

IPSet类,IPSet(collections.MutableSet):

可以用来对网段进行汇总。

IPSet仅仅接收IP实例的对象。

IPSet的值是存在列表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from IPy import IPSet
from IPy import IP
ipset = IPSet() #实例化一个IPSet对象
ipset.add(IP('192.168.1.0/24')) #给IPSet集合中添加一个网段
ipset
Out[301]: IPSet([IP('192.168.1.0/24')])
ipset.add([IP('192.168.2.0/24'),IP('192.168.3.0/24')])
ipset #以列表形式添加多个网段
Out[303]: IPSet([IP('192.168.1.0/24'), IP('192.168.2.0/23')])
ipset.add(IP('192.168.0.0/16'))
ipset#会对已有的所有网段进行汇总
Out[308]: IPSet([IP('192.168.0.0/16')])
ipset.discard(IP('192.168.1.0/24')) #移除汇总网段中某个网段
ipset
Out[310]: IPSet([IP('192.168.0.0/24'), IP('192.168.2.0/23'), IP('192.168.4.0/22'), IP('192.168.8.0/21'), IP('192.168.16.0/20'), IP('192.168.32.0/19'), IP('192.168.64.0/18'), IP('192.168.128.0/17')])
n [317]: ipset2 = IPSet()

ipset2.add(IP('192.168.1.0/24'))
ipset2
Out[319]: IPSet([IP('192.168.1.0/24')])
ipset.isdisjoint(ipset2) #判断某个集合中的网段是否在现有集合网段中
Out[320]: True
ipset3 = IPSet()
ipset3.add(IP('192.168.2.0/24'))
ipset.isdisjoint(ipset3)
Out[323]: False
ipset.__contains__(IP('192.168.2.0/24')) #判断某个网段是否在现有汇总网段中
Out[335]: True

IPy中的其他常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def _parseAddressIPv6(ipstr): #将IPv6字符串解析为十进制的整型数据
ipv6 = IPy._parseAddressIPv6('1080:200C::1')
ipv6
Out[345]: 21932911918296378927538433929194242049
IP(ipv6)
Out[346]: IP('1080:200c::1')

def parseAddress(ipstr, ipversion=0):#解析IP地址
IPy.parseAddress('192.168.1.1')
Out[348]: (3232235777, 4)
IPy.parseAddress('123.132')#不够的后面加0补齐
Out[349]: (2072248320, 4)

IPy.intToIp(3232235777, version=4)
Out[351]: '192.168.1.1'

def intToIp(ip, version): #将整型的IP地址转化为字符串IP地址
IPy.intToIp(3232235777, version=6)
Out[352]: '0000:0000:0000:0000:0000:0000:c0a8:0101'

def _intToBin(val): #将int型的地址转化为二进制
IPy._intToBin(13241)
Out[360]: '11001110111001'

利用IPy写的小程序:

批量分配指定掩码和分配个数的IPv4或者IPv6地址:

通过下面的程序,可以指定开始的一个IPv4或者IPv6地址,然后指定掩码,指定需要生成的网段个数,就可以通过程序批量分配地址。

地址输出格式,可以根据需要,灵活定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# -*- coding: utf-8 -*-
"""
Created on Sun Dec 1 21:51:11 2019

@author: cao
"""

import IPy

def get_ip_list(begin_ip, count, netmask):
ip_list = '' #用来存放生成的IP地址
begin_ip = IPy.IP(begin_ip)
ip_list += str(begin_ip) + '\n' #将第一个地址放入ip_列表中
if begin_ip.version() == 4:
for i in range(count):
ip = IPy.IP(begin_ip)
new_ip = IPy.IP(ip.ip + 2 ** (32 - netmask))
begin_ip = str(new_ip)
ip_list += begin_ip + '\n'
else:
for i in range(count):
ipv6 = IPy.IP(begin_ip)
new_ipv6 = IPy.IP(ipv6.ip + 2 ** (128 - netmask))
begin_ip = str(new_ipv6)
ip_list += begin_ip + '\n'
return ip_list

if __name__ == "__main__":
ipv6_list = get_ip_list(begin_ip = '2002::', count=10, netmask=64)
print('批量分配业务IPv6地址:')
print('============================')
print(ipv6_list)

ipv6_list2 = get_ip_list(begin_ip = 'FD00:0:2e3f::', count=10, netmask=127)
print('批量分配互联IPv6地址:')
print('============================')
print(ipv6_list2)

ip_list = get_ip_list(begin_ip='192.168.1.0', count=10,netmask=24)
print('批量分配业务IPv4地址:')
print('============================')
print(ip_list)

ip_list2 = get_ip_list(begin_ip='192.168.2.0', count = 10, netmask=30)
print('批量分配互联IPv4地:')
print('============================')
print(ip_list2)

程序运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
批量分配业务IPv6地址:
============================
2002::
2002:0:0:1::
2002:0:0:2::
2002:0:0:3::
2002:0:0:4::
2002:0:0:5::
2002:0:0:6::
2002:0:0:7::
2002:0:0:8::
2002:0:0:9::
2002:0:0:a::

批量分配互联IPv6地址:
============================
fd00:0:2e3f::
fd00:0:2e3f::2
fd00:0:2e3f::4
fd00:0:2e3f::6
fd00:0:2e3f::8
fd00:0:2e3f::a
fd00:0:2e3f::c
fd00:0:2e3f::e
fd00:0:2e3f::10
fd00:0:2e3f::12
fd00:0:2e3f::14

批量分配业务IPv4地址:
============================
192.168.1.0
192.168.2.0
192.168.3.0
192.168.4.0
192.168.5.0
192.168.6.0
192.168.7.0
192.168.8.0
192.168.9.0
192.168.10.0
192.168.11.0

批量分配互联IPv4地:
============================
192.168.2.0
192.168.2.4
192.168.2.8
192.168.2.12
192.168.2.16
192.168.2.20
192.168.2.24
192.168.2.28
192.168.2.32
192.168.2.36
192.168.2.40

可将批量生成的IP地址直接复制到Excel表格,或者文件中,用作后续批量生成网络设备配置等。

按固定格式批量分配掩码为64位的IPv6地址:

在网络规划中,需要给业务分配IPv6地址时,假设掩码都是64。同时需要给出IPv6对应的网关地址。

以下程序,给出一个开始的IPv6地址,然后指定count需要分配多少个地址。就会打印出指定数量的IPv6地址,同时给错每个地址对应的指定的网关地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# -*- coding: utf-8 -*-
"""
Created on Tue Nov 26 16:04:36 2019

@author: cao
"""

#批量计算业务网段,默认按掩码64位计算

import IPy


#按冒号分割后,判断段是否满足4位,不是前面补零
#这个是为了分下来地址格式比较规整。可以不需要。
def add_0(tmp):
if len(tmp) == 3:
tmp = '0' + tmp
elif len(tmp) == 2:
tmp = '00' + tmp
elif len(tmp) == 1:
tmp = '000' + tmp
else:
pass
return tmp

#传入一个IPv6地址,进项每段0的填充
def ipv6_format(ipv6):
tmp_ip = ''
for i in ipv6.split(':'):
if(len(i) == 0):
continue
if(i == '0'):
tmp_ip += '0:'
continue
tmp_ip += add_0(i) + ':'
return tmp_ip + ':'

#获取业务网段IPv6对应的网关地址
def ipv6_GW(ipv6_list):
ip_list_gw = ''
for ip_net in ipv6_list.split('\n'):
if(len(ip_net) == 0):
break
ip_list_gw += ip_net + 'FFFF\n'
return ip_list_gw


if __name__ == "__main__":
begin_ip = 'FD00:0:0004:009B::'
ipv6_count = 10
ipv6_list = begin_ip + '\n' #业务网段地址
ipv6_gw = '' #业务网段对应的网关

for i in range(ipv6_count):
ipv6 = IPy.IP(begin_ip)
new_ipv6 = str.upper(str(IPy.IP(ipv6.ip + 2**64)))
begin_ip = ipv6_format(new_ipv6)
ipv6_list += begin_ip + '\n'

print(ipv6_list)

ipv6_gw = ipv6_GW(ipv6_list)
print(ipv6_gw)

程序运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FD00:0:0004:009B::
FD00:0:0004:009C::
FD00:0:0004:009D::
FD00:0:0004:009E::
FD00:0:0004:009F::
FD00:0:0004:00A0::
FD00:0:0004:00A1::
FD00:0:0004:00A2::
FD00:0:0004:00A3::
FD00:0:0004:00A4::
FD00:0:0004:00A5::

FD00:0:0004:009B::FFFF
FD00:0:0004:009C::FFFF
FD00:0:0004:009D::FFFF
FD00:0:0004:009E::FFFF
FD00:0:0004:009F::FFFF
FD00:0:0004:00A0::FFFF
FD00:0:0004:00A1::FFFF
FD00:0:0004:00A2::FFFF
FD00:0:0004:00A3::FFFF
FD00:0:0004:00A4::FFFF
FD00:0:0004:00A5::FFFF

得到想要地址后,可以直接复制到Excel表格中,或者直接写到文件中。用于后续批量生成配置。

至于输出的IPv6地址格式,或者IPv4地址格式,可以根据需要灵活控制。


参考资料:

https://pypi.org/project/IPy/

IPy源码: https://github.com/autocracy/python-ipy/

https://zhuanlan.zhihu.com/p/64791275


坚持原创技术分享,您的支持将鼓励我继续创作!