# Find the single smallest IP network with the given IP addresses

I have a bunch of IP addresses. I want one IP network that has them all. ipaddr.collapse_address_list already exists, but that gives you a list of all the IP networks that identify your IP addresses, no more, no less, whereas I only need one IP network.

I tried at first using XOR, but that fails on some situations (say, 10.0.0.1, 10.0.0.2, 10.0.0.255, 10.0.0.251: bin(1 ^ 2 ^ 255 ^ 251) = 0b111). And'ing has similar problems. This is what I ended up writing.

#http://stackoverflow.com/a/3789000/13992
def _AllSame(items):
it = iter(items)
first = next(it, None)
return all(x == first for x in it)

"""Get the one IP network that covers all IPs in input.

ipaddr.collapse_address_list is a little bit TOO smart and gives you the LIST
of networks that describe a number of IP addreses. Ceph just wants one

...   "10.0.0.251", "10.0.0.255"))
10.0.0.0/24
"""

#"Transpose" the list (abc),(def) → (ad),(be),(cf)

differing_bits = len(bit_comparison)
#Find the first different bit
while _AllSame(bit_comparison[-differing_bits]):
differing_bits -= 1

#That's the no. of bits that differ. The mask is the number of bits that DON'T

#Return the network
return str(network)


I tried to be as IP-version agnostic as possible here, but chances are I've overlooked a few more-or-less obvious things here — or, even more likely, there's already a better inbuilt solution somewhere in the python libraries or even ipaddr itself.

You don't have to look at all the IPs to catch 'em all. Just looking at the lowest and highest does the trick. This realisation comes in handy when you consider that ipaddr does have a (private) function that gives you the common prefix length between two IP addresses. The code can then be simplified to:

#with many thanks to gfalcon
"""Get the one IP network that covers all IPs in input.

ipaddr.collapse_address_list is a little bit TOO smart and gives you the LIST
of networks that describe a number of IP addreses. Ceph just wants one

...   "10.0.0.251","10.0.0.255"))
10.0.0.0/24
"""

#Look at larger and smaller IP addresses
lowest_ip, highest_ip = min(ips), max(ips)

int(lowest_ip), int(highest_ip), lowest_ip.max_prefixlen)

#Return the network
return str(network)


You still can't use public functions like ipaddr.summarize_address_range, because the library will still painstakingly try to avoid any IP address ever so slightly off range.

>>> ipaddr.summarize_address_range(IPv4Address('10.0.0.1'), IPv4Address('10.0.0.255'))
[IPv4Network('10.0.0.1/32'),
IPv4Network('10.0.0.2/31'),
IPv4Network('10.0.0.4/30'),
IPv4Network('10.0.0.8/29'),
IPv4Network('10.0.0.16/28'),
IPv4Network('10.0.0.32/27'),
IPv4Network('10.0.0.64/26'),
IPv4Network('10.0.0.128/25')]