I trying to write a Python script to convert the range between two IP addresses to CIDR,
# cidr.py
import argparse
import netaddr
import sys
parser = argparse.ArgumentParser()
parser.add_argument('start_addr')
parser.add_argument('end_addr')
args = parser.parse_args()
start_addr = netaddr.IPAddress(args.start_addr)
end_addr = netaddr.IPAddress(args.end_addr)
if end_addr < start_addr:
print('start address is larger than end address. check arguments.')
sys.exit()
ip_cidr = netaddr.iprange_to_cidrs(start_addr, end_addr)
print(str(ip_cidr))
For example, execute this script using Salesforce's IP address range as arguments, it returns the correct CIDR expression.
$ python cidr.py 13.108.0.0 13.111.255.255
[IPNetwork('13.108.0.0/14')]
But when I modify the second argument, it returns a couple of CIDRs.
$ python hoge.py 13.108.0.0 13.211.255.255
[IPNetwork('13.108.0.0/14'), IPNetwork('13.112.0.0/12'), IPNetwork('13.128.0.0/10'), IPNetwork('13.192.0.0/12'), IPNetwork('13.208.0.0/14')]
I want to know the address range of 2 IP addresses only contains 1 CIDR expressions. How do I check it?
If iprange_to_cidrs
always returns the minimum number of IP ranges needed to span the supplied range (as seems likely) then you only need to test the length of the list which it returns.
However, if you do not want to verify that this is always the case, the following approach could be used.
def ip_to_int(ip):
return sum(256**i * int(v) for i, v in enumerate(reversed(ip.split('.'))))
def single_cidr(ip1, ip2):
n1 = ip_to_int(ip1)
n2 = ip_to_int(ip2)
xor = n2 ^ n1
return xor == abs(n2 - n1) and '0' not in bin(xor)[2:]
print(single_cidr('13.108.0.0', '13.111.255.255')) # True
print(single_cidr('13.108.0.0', '13.211.255.255')) # False
Here, xor
contains a 1 for each bit where the two IPs (when converted to integer) differ. We want this number to be equal to the difference between the values and also one less than a power of two -- so that the two IP addresses in binary start with the same stem and then one has all 0s while the other has all 1s.
In the part '0' not in bin(xor)[2:]
we use the fact that the output of bin
after the initial 0b
will start with the first 1 bit without padding any leading zeros (e.g. for 65535 it will be '0b11111111'
), so we just need to test that there is no 0 after the initial 0b
.