文章目录

  • 一、安装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]