Search code examples
pythonip-address

How to convert a list of IP addresses to a list of address ranges?


There are multiple ways to convert a list of IP address to a list of CIDR (netaddr, ipaddr-py). Is there a way to convert a list of IP address ranges to a merged IP range?

The following examples return a list of tuples of the following format: [(start, end)].

Example 1:

>>> list_of_ips = ['192.168.0.1', '192.168.0.2', '192.168.0.3']
>>> print merge_ip_list(list_of_ips)
[('192.168.0.1','192.168.0.3')]

Example 2:

>>> list_of_ips2 = ['10.0.0.0', '10.0.0.3', '10.0.0.4']
>>> print merge_ip_list(list_of_ips2)
[('10.0.0.0','10.0.0.0'), ('10.0.0.3','10.0.0.4')]

Solution

  • IMHO a good point to start is to make converters from dotted string into int and back. Integer representation is more convenient to compare.

    I thought about using reduce but it seems too difficult for me. So I've just implemented it using traditional loop and without recursion.

    def int2dot( intip ):
        return '.'.join([ str( (intip>>x*8) & 0xFF ) for x in [3,2,1,0]])
    def dot2int( dotip ):
        return reduce( lambda r,x: int(x)+(r<<8), dotip.split('.'), 0 )
    
    def merge_ip_list(ip_list):
        if not ip_list:
            return []
        orig = map(dot2int,ip_list)
        orig.sort()
        start = orig[0]
        prev = start-1
        res = []
        for x in orig:
            if x != prev+1:
                res.append((int2dot(start),int2dot(prev)))
                start = x
            prev = x
        res.append((int2dot(start),int2dot(prev)))
        return res
    

    Also, I've made an alternative solution:

    def merge_ip_list_alt(ip_list):
        if not ip_list:
            return []
        orig = sorted(map(dot2int,ip_list))
        end, start = zip(*[x for x in zip(orig,orig[1:]) if x[0]+1!=x[1]]) or ((),())
        start = [int2dot(orig[0])] + map(int2dot,start)
        end = map(int2dot,end) + [int2dot(orig[-1])]
        return zip( start, end )