在日常网络规划中,会有很多关于IP地址的分配规划问题,如果是手动分配,在量很大的情况下,容易出错。而利用IPy这个python模块,可以很容易实现对iP地址的分配等操作。
以下是对IPy模块学习的一个记录。
IPy-用于处理IPv4和IPv6地址和网络的类和工具。
IPy源码: https://github.com/autocracy/python-ipy/
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这都是定义的常量。
处理返回整数的IP地址。
因为IP类继承自IPint,所以IPint类中的方法在IP类中同样适用。
下面会记录常用方法。
data可以是常见的IPv4和IPv6地址的各种表现形式。支持前缀表示,IP段表示,小数点掩码表示,单个IP等。
也可以输入10进制的进制,2进制的地址等。
ipversion可以制定地址的版本,版本为4或者6,输入其他会报错。
mask_net如果是为Ture,则可以通过指定掩码来分配IP地址。
测试如下:
>>> 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
...
#实例化一个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类继承自IPint,所以IP类具有IPint的所有方法。
IP类主要处理IP地址和网络。IP类与IPint相比,返会的不再是整数型值,而是网络形式的字符串。
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仅仅接收IP实例的对象。
IPSet的值是存在列表中。
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
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'
通过下面的程序,可以指定开始的一个IPv4或者IPv6地址,然后指定掩码,指定需要生成的网段个数,就可以通过程序批量分配地址。
地址输出格式,可以根据需要,灵活定义。
# -*- 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)
程序运行结果:
批量分配业务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表格,或者文件中,用作后续批量生成网络设备配置等。
在网络规划中,需要给业务分配IPv6地址时,假设掩码都是64。同时需要给出IPv6对应的网关地址。
以下程序,给出一个开始的IPv6地址,然后指定count需要分配多少个地址。就会打印出指定数量的IPv6地址,同时给错每个地址对应的指定的网关地址。
# -*- 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)
程序运行结果:
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