Python处理IP地址的利器IPy


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]
  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IPy是一个Python支持IP的各种操作的第三方模块。可以通过使用pip安装IPy模块来使用它,安装命令是 pip install IPyIPy模块提供了很多功能,其中一个是通过使用version方法来区分IP地址的版本。可以使用IPy.IP().version()方法来查看一个IP地址的版本。比如,IPy.IP('10.0.0.0/8').version()会返回4,而IPy.IP('::1').version()会返回6。 在完成主机扫描的过程中,IPy模块也经常被使用来生成IP地址池。可以使用IPy库来定义一个函数来生成IP地址池,比如makeIpPool函数。这个函数接受起始IP地址和结束IP地址作为参数,然后使用IPy.IP()和IPy.intToIp()方法来将IP地址转换为整型格式并生成IP地址池。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [一文掌握使用PythonIP处理模块IPy](https://blog.csdn.net/qq_27071221/article/details/124987012)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [python学习—— IPy 强大的IP地址解析模块](https://blog.csdn.net/qq_40909772/article/details/90767800)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值