文章目录
- 一、安装IPy
- 二、IP类:处理单网段
- 2.1 查看网段包含的IP地址
- 2.2 查看地址是IPv4还是IPv6
- 2.3 格式化输出地址
- 2.4 已知掩码求网段
- 2.5 将IP地址转换为字符串
- 2.6 处理IP地址段的包含关系
- 三、IPSet类:处理多网段
- 3.1 多网段聚合处理
- 3.2 判断多个网段是否相交
- 3.3 输出网段的IP地址数量
- 附:IPSet类源码
IP地址的规划,不仅是网络设计中的重中之重,还会直接影响网络的转发效率和扩展性。
很多从事网络工作多年的朋友,免不了要在工作中重复计算诸如网段、子网掩码、广播地址、子网数。还要判断IP网段的包含关系和对多个IP地址段进行汇总等等。
如果计算数据量特别大,不仅费时费力,还容易出错。
Python有一个非常强大的第三方库IPy,适用于Python 2.6-3.7版本,可以帮助我们完成此类计算任务。大家可以参见IPy的项目地址:https://github.com/autocracy/python-ipy
一、安装IPy
1、打开终端;
2、输入:pip list查看自己安装的库,如果有,就不需要安装了。
3、如果没有IPy,则输入pip install IPy
即可
pip install IPy
二、IP类:处理单网段
IP类允许对IPv4和IPv6地址和网络中使用的大多数符号进行轻松的解析和处理。
2.1 查看网段包含的IP地址
from IPy import IP
ip = IP('127.0.0.0/30')
for x in ip:
print(x)
输出如下:
127.0.0.0
127.0.0.1
127.0.0.2
127.0.0.3
2.2 查看地址是IPv4还是IPv6
print(IP('10.0.0.0/8').version()) # 4
print(IP('::1').version()) # 6
2.3 格式化输出地址
- IPv4
print(IP(0x7f000001)) # 127.0.0.1
print(IP('0x7f000001')) # 127.0.0.1
print(IP('127.0.0.1')) # 127.0.0.1
print(IP('10')) # 10.0.0.0
- IPv6
print(IP('1080:0:0:0:8:800:200C:417A')) # 1080::8:800:200c:417a
print(IP('1080::8:800:200C:417A')) # 1080::8:800:200c:417a
print(IP('::1')) # ::1
print(IP('::13.1.68.3')) # ::d01:4403
- 子网掩码及网段前缀转换
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
2.4 已知掩码求网段
已知IP地址的子网掩码,求出该地址所在的网段:
print(IP('127.0.0.1/255.0.0.0', make_net=True)) # 127.0.0.0/8
print(IP('127.0.0.1').make_net('255.0.0.0')) # 127.0.0.0/8
2.5 将IP地址转换为字符串
可以通过strNomal方法指定不同的wantprefixlen参数定制不同输出类型的网段输出为字符串。
wantprefixlen == 0 / None 无返回 1.2.3.0
wantprefixlen == 1 返回前缀prefix 1.2.3.0/24
wantprefixlen == 2 返回网段netmask 1.2.3.0/255.255.255.0
wantprefixlen == 3 返回IP地址范围lastip 1.2.3.0-1.2.3.255
示例如下:
IP('10.0.0.0/32').strNormal() # '10.0.0.0'
IP('10.0.0.0/24').strNormal() # '10.0.0.0/24'
IP('10.0.0.0/24').strNormal(0) # '10.0.0.0'
IP('10.0.0.0/24').strNormal(1) # '10.0.0.0/24'
IP('10.0.0.0/24').strNormal(2) # '10.0.0.0/255.255.255.0'
IP('10.0.0.0/24').strNormal(3) # '10.0.0.0-10.0.0.255'
2.6 处理IP地址段的包含关系
涉及处理两个网段是否包含的关系,IPy中的IP方法也提供了这个功能,他会返回一个布尔值告诉我们是否包含:
- 判断两个IP网段的大小:
from IPy import IP
IP('1.1.1.0/24') < IP('2.2.2.0/24')
# True
- 判断一个IP地址是否包含于另一个IP网段
from IPy import IP
'192.168.100.1' in IP('192.168.100.0/27')
# True
- 判断一个IP网段是否被另一个IP网段包含
from IPy import IP
IP('192.168.2.0/24') in IP('192.168.0.0/23')
# False
from IPy import IP
IP('192.168.1.0/24') in IP('192.168.0.0/23')
# True
三、IPSet类:处理多网段
将相邻的网段的进行聚合操作,将得到两者的父网段。IPSet类可提供更复杂的范围映射和聚合要求,其保存任何数量的唯一地址范围,并对重叠范围进行聚合处理。
3.1 多网段聚合处理
>>> from IPy import IP, IPSet
IP('10.0.0.0/22') - IP('10.0.2.0/24')
# IPSet([IP('10.0.0.0/23'), IP('10.0.3.0/24')])
IPSet([IP('10.0.0.0/23'), IP('10.0.3.0/24'), IP('10.0.2.0/24')])
# IPSet([IP('10.0.0.0/22')])
s = IPSet([IP('10.0.0.0/22')])
s.add(IP('192.168.1.0/29')) # 增加网段
s
# IPSet([IP('10.0.0.0/22'), IP('192.168.1.0/29')])
s.discard(IP('192.168.1.2')) # 删除网段
s
# IPSet([IP('10.0.0.0/22'), IP('192.168.1.0/31'), IP('192.168.1.3'), IP('192.168.1.4/30')])
3.2 判断多个网段是否相交
IPSet支持isdisjoint
方法,用来判断网段之间是否是 “不相交” 的:
s.isdisjoint(IPSet([IP('192.168.0.0/16')]))
# False
s.isdisjoint(IPSet([IP('172.16.0.0/12')]))
# True
IPSet支持&
方法,用来判断网段之间是否是 “相交” 的:
s & IPSet([IP('10.0.0.0/8')])
# IPSet([IP('10.0.0.0/22')])
3.3 输出网段的IP地址数量
IPSet([IP('10.0.0.0/24')]).len() # 256
IPSet([IP('10.0.0.0/22')]).len() # 1024
【注意】:当使用IPv6地址时,最好使用 IP().len()
方法而不是len(IP)
方法。整数值大于64位的地址可能使得第二种方法出Bug,具体可以参考 http://stackoverflow.com/questions/15650878 了解更多信息。
附:IPSet类源码
class IPSet(MutableSet):
def __init__(self, iterable=[]):
# Make sure it's iterable, otherwise wrap
if not isinstance(iterable, Iterable):
raise TypeError("'%s' object is not iterable" % type(iterable).__name__)
# Make sure we only accept IP objects
for prefix in iterable:
if not isinstance(prefix, IP):
raise ValueError('Only IP objects can be added to an IPSet')
# Store and optimize
self.prefixes = iterable[:]
self.optimize()
def __contains__(self, ip):
valid_masks = self.prefixtable.keys()
if isinstance(ip, IP):
#Don't dig through more-specific ranges
ip_mask = ip._prefixlen
valid_masks = [x for x in valid_masks if x <= ip_mask]
for mask in sorted(valid_masks):
i = bisect.bisect(self.prefixtable[mask], ip)
# Because of sorting order, a match can only occur in the prefix
# that comes before the result of the search.
if i and ip in self.prefixtable[mask][i - 1]:
return True
def __iter__(self):
for prefix in self.prefixes:
yield prefix
def __len__(self):
return self.len()
def __add__(self, other):
return IPSet(self.prefixes + other.prefixes)
def __sub__(self, other):
new = IPSet(self.prefixes)
for prefix in other:
new.discard(prefix)
return new
def __and__(self, other):
left = iter(self.prefixes)
right = iter(other.prefixes)
result = []
try:
l = next(left)
r = next(right)
while True:
# iterate over prefixes in order, keeping the smaller of the
# two if they overlap
if l in r:
result.append(l)
l = next(left)
continue
elif r in l:
result.append(r)
r = next(right)
continue
if l < r:
l = next(left)
else:
r = next(right)
except StopIteration:
return IPSet(result)
def __repr__(self):
return '%s([' % self.__class__.__name__ + ', '.join(map(repr, self.prefixes)) + '])'
def len(self):
return sum(prefix.len() for prefix in self.prefixes)
def add(self, value):
# Make sure it's iterable, otherwise wrap
if not isinstance(value, Iterable):
value = [value]
# Check type
for prefix in value:
if not isinstance(prefix, IP):
raise ValueError('Only IP objects can be added to an IPSet')
# Append and optimize
self.prefixes.extend(value)
self.optimize()
def discard(self, value):
# Make sure it's iterable, otherwise wrap
if not isinstance(value, Iterable):
value = [value]
# This is much faster than iterating over the addresses
if isinstance(value, IPSet):
value = value.prefixes
# Remove
for del_prefix in value:
if not isinstance(del_prefix, IP):
raise ValueError('Only IP objects can be removed from an IPSet')
# First check if this prefix contains anything in our list
found = False
d = 0
for i in range(len(self.prefixes)):
if self.prefixes[i - d] in del_prefix:
self.prefixes.pop(i - d)
d = d + 1
found = True
if found:
# If the prefix was bigger than an existing prefix, then it's
# certainly not a subset of one, so skip the rest
continue
# Maybe one of our prefixes contains this prefix
found = False
for i in range(len(self.prefixes)):
if del_prefix in self.prefixes[i]:
self.prefixes[i:i+1] = self.prefixes[i] - del_prefix
break
self.optimize()
def isdisjoint(self, other):
left = iter(self.prefixes)
right = iter(other.prefixes)
try:
l = next(left)
r = next(right)
while True:
if l in r or r in l:
return False
if l < r:
l = next(left)
else:
r = next(right)
except StopIteration:
return True
def optimize(self):
# The algorithm below *depends* on the sort order
self.prefixes.sort()
# First eliminate all values that are a subset of other values
addrlen = len(self.prefixes)
i = 0
while i < addrlen:
# Everything that might be inside this prefix follows
# directly behind it
j = i+1
while j < addrlen and self.prefixes[j] in self.prefixes[i]:
# Mark for deletion by overwriting with None
self.prefixes[j] = None
j += 1
# Continue where we left off
i = j
# Try to merge as many prefixes as possible
run_again = True
while run_again:
# Filter None values. This happens when a subset is eliminated
# above, or when two prefixes are merged below
self.prefixes = [a for a in self.prefixes if a is not None]
# We'll set run_again to True when we make changes that require
# re-evaluation of the whole list
run_again = False
# We can merge two prefixes that have the same version, same
# prefix length and differ only on the last bit of the prefix
addrlen = len(self.prefixes)
i = 0
while i < addrlen-1:
j = i + 1
try:
# The next line will throw an exception when merging
# is not possible
self.prefixes[i] += self.prefixes[j]
self.prefixes[j] = None
i = j + 1
run_again = True
except ValueError:
# Can't be merged, see if position j can be merged
i = j
# O(n) insertion now by prefix means faster searching on __contains__
# when lots of ranges with the same length exist
self.prefixtable = {}
for address in self.prefixes:
try:
self.prefixtable[address._prefixlen].append(address)
except KeyError:
self.prefixtable[address._prefixlen] = [address]