网络工程师学Python——处理IP地址的利器 IPy

网络工程师学Python——处理IP地址的利器 IPy

3 个月前 · 来自专栏 网络工程师学编程

考过思科和华为认证的朋友们,想必对IP编址和计算烂熟于心。IP地址的规划,不仅是网络设计中的重中之重,还会直接影响网络的转发效率和扩展性。

很多从事网络工作多年的朋友,免不了要在工作中重复计算诸如网段、子网掩码、广播地址、子网数。还要判断IP网段的包含关系和对多个IP地址段进行汇总等等。

如果计算数据量特别大,不仅费时费力,还容易出错。

正好Python有一个非常强大的第三方库IPy,可以帮助我们完成此类计算任务。大家可以参见IPy的项目地址: github.com/haypo/python

下面我就把自己对IPy使用的一些小技巧分享给各位网络爱好者。

IPy是python的第三方库,需要手动安装。

安装方法如下:

我的系统环境是MacOS,直接打开终端就能安装。

步骤是:1、打开终端;2、输入:pip list查看自己安装的库,如果有,就不需要安装了。3、如果没有IPy,则输入pip install IPy即可(Windows类似:直接打开命令提示符输入)

pip list
若有IPy,则不需要安装,若没有安装IPy则输入:pip install IPy

一、使用IPy中的IP方法迅速格式化

(1)查看地址是IPv4还是IPv6

>>> from IPy import IP
>>> IP('10.0.0.0/8').version()
>>> IP('::1').version()
>>> 

(2)格式化输出IPv4和IPv6的地址

>>> from IPy import IP
>>> print(IP('127.0.0.1'))
127.0.0.1
>>> print(IP('10'))
10.0.0.0
>>> print(IP('0x7f000001'))
127.0.0.1
>>> print(IP(0x7f000001))
127.0.0.1
>>> 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'))
>>> print(IP('::13.1.68.3'))
::d01:4403

(3)子网掩码及网段前缀转换

>>> from IPy import IP
>>> print(IP('1.0.0.0/8'))
1.0.0.0/8
>>> print(IP('1.0.0.0/255.0.0.0'))
1.0.0.0/8
>>> print(IP('1.0.0.0-1.255.255.255'))
1.0.0.0/8

(4)已知IP地址的子网掩码,快速求出该地址所在的网段

>>> from IPy import IP
>>> print(IP('127.0.0.1/255.0.0.0', make_net=True))
127.0.0.0/8
>>> print(IP('12.1.0.1').make_net('255.255.255.224'))
12.1.0.0/27

二、IPy中的IP方法格式化的高级用法

(1)IPy中IP地址格式转换的高级用法

>>> from IPy import IP
>>> ip_address = IP('192.168.100.3')
>>> ip.reverseNames()
>>> ip_address.reverseNames() #反向解析地址格式
['3.100.168.192.in-addr.arpa.']
>>> ip_address.iptype() #解析地址的“公网、私网属性” 192.168.100.0为私有网段
'PRIVATE'
>>> ip_address.int() #把192.168.100.3转换成整数
3232261123
>>> ip_address.strHex() #把192.168.100.3转换成十六进制数
'0xc0a86403'
>>> ip_address.strBin() #把192.168.100.3转换成二进制数
'11000000101010000110010000000011'
>>> print(IP(0xc0a86403)) #把十六进制数0xc0a86403转换成点分十进制192.168.100.3
192.168.100.3

(2)IPy将IP地址转换成字符串的方法:

可以通过strNomal方法指定不同的wantprefixlen参数定制不同输出类型的网段输出为字符串。

wantprefixlen = 0 ,无返回的意思

wantprefixlen = 1 ,返回前缀的类型

wantprefixlen = 2 ,返回网段/子网掩码的类型

wantprefixlen = 3 ,返回IP地址范围的类型

>>> from IPy import IP
>>> 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'

三、 使用IPy中的IP方法处理IP地址段的包含关系

涉及处理两个网段是否包含的关系,IPy中的IP方法也提供了这个功能,他会返回一个布尔值告诉我们是否包含:

(1)判断两个IP网段的大小:

>>> from IPy import IP
>>> IP('1.1.1.0/24') < IP('2.2.2.0/24')
True

(2)判断一个IP地址是否包含于另一个IP网段

>>> from IPy import IP
>>> '192.168.100.1' in IP('192.168.100.0/27')
True

(3)判断一个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

四、使用IPy中的IPSet方法汇总不连续的IP网段

IPy中还提供了IPSet这个神奇的方法来达成汇总网段的目的。非常灵活。下面来看看方法:

>>> from IPy import IP, IPSet
>>> IPSet([IP('192.168.0.0/30'), IP('192.168.0.4/30'),IP('192.168.0.8/30'), IP('192.168.0.12/30')])
IPSet([IP('192.168.0.0/28')])
#! /usr/bin/env python3
#! _*_ coding: utf-8 _*_
from IPy import IP, IPSet
a = IP('192.168.0.0/17')
a1 = IP('192.168.0.64/27')
a2 = IP('192.168.0.96/27')
a3 = IP('192.168.0.128/27')
a4 = IP('192.168.0.160/27')
a5 = IP('192.168.0.192/27')
a6 = IP('192.168.0.224/27')
b = IP('192.168.1.0/27')
c = IP('192.168.2.0/27')
d = IP('192.168.4.0/27')
d1 = IP('192.168.4.32/27')
d2 = IP('192.168.4.128/25')
e = IP('192.168.5.0/27')
e1 = IP('192.168.5.32/27')
summary = IPSet([a1, a2, a3, a4, a5, a6, b, c, d, d1, d2, e, e1])
print(summary)

注意IPSet的数据类型是集合Set中的列表。


这样我们就能用IPSet这个方法来快速的计算IP地址汇总的问题。

最后,为坚持看完这篇文章的小伙伴,分享一个我自己做的IP地址处理小程序的源码:

#! /usr/bin/env python3
#! _*_ coding: utf-8 _*_
本程序是一个用于处理IP地址的程序
from IPy import IP, IPSet
def ip_belongs_prefix(ip_address, ip_prefix):
    #定义了一个判断IP地址是否从属于另外一个IP网段的方法
    ip_net_b = IP(ip_prefix)
    return print(ip_address in ip_net_b)
    #返回一个布尔值,如果IP地址从属于另一个网段就返回 True,如果不属于,就返回 False
def prefix_belongs_prefix(ip_prefix_1, ip_prefix_2):
    #定义了一个判断IP网段是否从属于另外一个IP网段的方法
    ip_prefix_a = IP(ip_prefix_1)
    ip_prefix_b = IP(ip_prefix_2)
    return print(ip_prefix_a in ip_prefix_b)
    #返回一个布尔值,如果IP地址从属于另一个网段就返回 True,如果不属于,就返回 False
def summary_ip(ip_prefix_times):
    #定一个使用IPSet汇总网段的方法
    ip_prefix_times = int(ip_prefix_times)
    #接收选择d后的输入的数字,并且把input中str的类型转换为int类型
    ip_list = [None] * ip_prefix_times
    #利用接收到的次数来定义列表元素的个数,用于存放输入的IP网段
    code = 0
    while code < ip_prefix_times:
        print('您第:%d 次输入的 ip 前缀,比如 192.168.1.0/24 。 您的输入是: ' % (code + 1))
        ip = input(': ')
        ip_list[code] = IP(ip)
        code = code + 1
        #使用while循环,创建列表中元素的索引,用来存放接收的IP网段
    return print(IPSet(ip_list))
    #使用IPSet求出汇总的网段
key = input('按键盘的字母 y 开始本程序,您的选择是:  ')
while key == 'y' or key == 'Y':
    print('***************** 欢迎使用 IP Prefix 处理程序 ***************')
    print('* 按 a 开始 IP Prefix 前缀网段处理功能,列出网段详细信息    *')
    print('* 按 b 开始判断 IP 地址是否属于一个 IP 地址段               *')
    print('* 按 c 开始判断 一个 IP 地址段,是否从属于另外一个 IP 地址段*')
    print('* 按 d 启动 IP 网段地址汇总功能                             *')
    print('*********************************************** @ by惰惰猴 **')
    choose_key = input('请输入您的选择, a/b/c/d : ')
    if choose_key == 'a' or choose_key == 'A':
        print('*****  您选择了a, Prefix 前缀网段处理功能,列出网段详细信息  *****\n')
        ip_name = input('请输入IP前缀,比如 192.168.1.0/24 。您的输入是: ')
        ip_addr = IP(ip_name)
        if len(ip_addr) > 1:
            print('ip 前缀的网段为: %s' % ip_addr.net())
            print('ip 前缀子网掩码为 : %s' % ip_addr.netmask())
            print('ip 前缀广播地址为 : %s' % ip_addr.broadcast())
            print('ip 前缀的reverse : %s' % ip_addr.reverseNames()[0])
            print('ip 前缀地址个数为: %s' % len(ip_addr))
        else:
            print('hex_addr is : %s' % ip_addr.strHex)
            print('bin_addr is : %s' % ip_addr.strBin)
    elif choose_key == 'b' or choose_key == 'B':
        print('*****  您选择了b, 判断 IP 地址是否属于一个 IP 地址段   *****\n')
        ipaddress = input('请输入一个ip地址,比如 192.168.1.1 。您的输入为 : ')
        ipprefix = input('请输入一个ip网段前缀,比如 192.168.1.0/24 。您的输入为 : ')
        ip_belongs_prefix(ipaddress, ipprefix)
    elif choose_key == 'c' or choose_key == 'C':
        print('*****  您选择了c, 判断 一个 IP 地址段 是否 从属于另外一个 IP 地址段  *****\n')
        ipprefix_1 = input('请输入一个ip网段前缀,比如 192.168.1.0/24 。您的输入为 : ')
        ipprefix_2 = input('请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您的输入为 : ')
        prefix_belongs_prefix(ipprefix_1, ipprefix_2) 
    elif choose_key == 'd' or choose_key == 'D':
        print('*****  您选择了d, 启动 IP 网段地址汇总功能  *****\n')
        summary_prefix = input('请输入您要汇总网段的数量,比如您要汇总5个网段,就输入5. 您的选择是 : ')
        summary_ip(summary_prefix)
    else:
        print('没有这个选项 , 请在 a,b,c,d 的四个选项中选择')
    print('*****  继续使用本程序,青按字母 y 退出本程序,请按字母 n 或者其他键 *****')
    key = input('继续本程序 或 退出本程序 Y/N : ')

运行起来是这个样子的:

再来一段基于面向对象的:

#! /usr/bin/env python3
#! _*_ coding: utf-8 _*_
from IPy import IP, IPSet
class IpProgram(object):
    this ip address or ip prefix compute program
    def __init__(self, ip_prefix_1, ip_prefix_2):
        self.ip_prefix_1 = ip_prefix_1
        self.ip_prefix_2 = ip_prefix_2
    def ip_list(self):
        self.ip_prefix_1 = IP(self.ip_prefix_1)
        print('子网掩码是: %s' % self.ip_prefix_1.strNormal(2))
        print('可用地址段: %s' % self.ip_prefix_1.strNormal(3))
        print('ip地址的版本是 : IPv%s' % self.ip_prefix_1.version())
        print('ip前缀的反向解析 : %s' % self.ip_prefix_1.reverseNames()[0])
        print('ip前缀地址个数为: %s' % len(self.ip_prefix_1))
    def ip_mask_to_ip_prefix(self):
        self.ip_prefix_1 = self.ip_prefix_1
        self.ip_prefix_2 = self.ip_prefix_2
        print('ip地址转换成前缀是: %s' % IP(self.ip_prefix_1).make_net(self.ip_prefix_2))
    def ip_belongs_prefix(self):
        self.ip_prefix_1 = self.ip_prefix_1
        self.ip_prefix_2 = IP(self.ip_prefix_1)
        print('ip地址:{0} 属于 ip网段:{1} 为:{2}'.format(self.ip_prefix_1, self.ip_prefix_2, self.ip_prefix_1 in self.ip_prefix_2))
    def ip_prefix_belongs_other_prefix(self):
        self.ip_prefix_1 = IP(self.ip_prefix_1)
        self.ip_prefix_2 = IP(self.ip_prefix_1)
        print('ip网段:{0} 属于 ip网段:{1} 为:{2}'.format(self.ip_prefix_1, self.ip_prefix_2, self.ip_prefix_1 in self.ip_prefix_2))
class IpSummary(object):
    this IP address summary compute
    def __init__(self, codes):
            self.codes = codes
    def summary(self):
            self.codes = int(self.codes)
            ip_list = [None] * self.codes
            code = 0
            while code < self.codes:
                print('您第:%d 次输入的 ip 前缀,比如 192.168.1.0/24 。 您的输入是: ' % (code + 1))
                ip = input(': ')
                ip_list[code] = IP(ip)
                code = code + 1
            print(IPSet(ip_list))
key = input('按键盘的字母 y 开始本程序,您的选择是:  ')
while key == 'y' or key == 'Y':
    print('***************** 欢迎使用 IP Prefix 处理程序 ***************')
    print('* 按 a 开始 IP Prefix 前缀网段处理功能,列出网段详细信息    *')
    print('* 按 b 开始判断 IP 地址是否属于一个 IP 地址段               *')
    print('* 按 c 开始判断 一个 IP 地址段,是否从属于另外一个 IP 地址段*')
    print('* 按 d 输入一个 IP 地址、IP 地址的子网掩码 换算该地址前缀   *')
    print('* 按 e 启动 IP 网段地址汇总功能                             *')
    print('*********************************************** @ by惰惰猴 **')
    choose_key = input('请输入您的选择, a/b/c/d/e : ')
    if choose_key == 'a' or choose_key == 'A':
        ipaddress_1 = input('请输入您要处理的网段,例如 192.168.1.0/24: ')
        ip1 = IpProgram(ipaddress_1 , None)
        ip1.ip_list()
    elif choose_key == 'b' or choose_key == 'B':
        print('*****  您选择了b, 判断 IP 地址是否属于一个 IP 地址段   *****\n')
        ipaddress_2 = input('请输入一个ip地址,比如 192.168.1.1 。您的输入为 : ')
        ipprefix_2 = input('请输入一个ip网段前缀,比如 192.168.1.0/24 。您的输入为 : ')
        ip2 = IpProgram(ipaddress_2, ipprefix_2)
        ip2.ip_belongs_prefix()
    elif choose_key == 'c' or choose_key == 'C':
        print('*****  您选择了c, 判断 一个 IP 地址段 是否 从属于另外一个 IP 地址段  *****\n')
        ipprefix_3 = input('请输入一个ip网段前缀,比如 192.168.1.0/24 。您的输入为 : ')
        ipprefix_3 = input('请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您的输入为 : ')
        ip3 = IpProgram(ipprefix_3, ipprefix_3)
        ip3.ip_prefix_belongs_other_prefix()
    elif choose_key == 'd' or choose_key == 'D':
        print('*****  您选择了d, 输入一个地址的 ip地址和子网掩码,换算该地址前缀 *****\n')
        ipprefix_4 = input('请输入一个ip地址,比如 192.168.1.1 。您的输入为 : ')
        ipmask_4 = input('请输入这个ip地址的子网掩码,比如 255.255.255.0 您的输入为 : ')
        ip4 = IpProgram(ipprefix_4, ipmask_4)
        ip4.ip_mask_to_ip_prefix()
    elif choose_key == 'e' or choose_key == 'e':
        print('*****  您选择了d, 启动 IP 网段地址汇总功能  *****\n')
        times = input('请输入您要汇总网段的数量,比如您要汇总5个网段,就输入5. 您的选择是 : ')