Skip to content

Instantly share code, notes, and snippets.

@magnetikonline
Last active December 22, 2023 11:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save magnetikonline/686fde8ee0bce4d4930ce8738908a009 to your computer and use it in GitHub Desktop.
Save magnetikonline/686fde8ee0bce4d4930ce8738908a009 to your computer and use it in GitHub Desktop.
Python function to determine if a given IPv4 CIDR "fits" into another.

CIDR fit

Python function to determine if a given IPv4 CIDR fits into another.

How cidr_fit(cidr_a,cidr_b) works:

  • For both CIDR's given:
    • Splits each CIDR into address and prefix size parts.
    • Converts the address part to a 32 bit binary string.
      • Example: 192.168.0.1 becomes 11000000101010000000000000000001.
    • Cuts out everything after the prefix size.
      • Example: 192.168.0.1/12 becomes 110000001010.
  • Now check if either:
    • Remaining binary string A starts with B, or...
    • Remaining binary string B starts with A
  • Done.

Example

Using an example CIDR list of Google API ranges from _spf.google.com against a Google IP of 216.58.196.138:

$ ./cidrfit.py
('108.177.8.0/21    ', False)
('108.177.96.0/19   ', False)
('130.211.0.0/22    ', False)
('172.217.0.0/19    ', False)
('172.217.128.0/19  ', False)
('172.217.160.0/20  ', False)
('172.217.192.0/19  ', False)
('172.217.32.0/20   ', False)
('173.194.0.0/16    ', False)
('209.85.128.0/17   ', False)
('216.239.32.0/19   ', False)
('216.58.192.0/19   ', True)
('35.190.247.0/24   ', False)
('35.191.0.0/16     ', False)
('64.233.160.0/19   ', False)
('66.102.0.0/20     ', False)
('66.249.80.0/20    ', False)
('72.14.192.0/18    ', False)
('74.125.0.0/16     ', False)
#!/usr/bin/env python3
def cidr_fit(cidr_a, cidr_b):
def split_cidr(cidr):
part_list = cidr.split("/")
if len(part_list) == 1:
# if just an IP address, assume /32
part_list.append("32")
# return address and prefix size
return part_list[0].strip(), int(part_list[1])
def address_to_bits(address):
# convert each octet of IP address to binary
bit_list = [bin(int(part)) for part in address.split(".")]
# join binary parts together
# note: part[2:] to slice off the leading "0b" from bin() results
return "".join([part[2:].zfill(8) for part in bit_list])
def binary_network_prefix(cidr):
# return CIDR as bits, to the length of the prefix size only (drop the rest)
address, prefix_size = split_cidr(cidr)
return address_to_bits(address)[:prefix_size]
prefix_a = binary_network_prefix(cidr_a)
prefix_b = binary_network_prefix(cidr_b)
return prefix_a.startswith(prefix_b) or prefix_b.startswith(prefix_a)
def main():
CIDR_TEST_LIST = [
"108.177.8.0/21",
"108.177.96.0/19",
"130.211.0.0/22",
"172.217.0.0/19",
"172.217.128.0/19",
"172.217.160.0/20",
"172.217.192.0/19",
"172.217.32.0/20",
"173.194.0.0/16",
"209.85.128.0/17",
"216.239.32.0/19",
"216.58.192.0/19",
"35.190.247.0/24",
"35.191.0.0/16",
"64.233.160.0/19",
"66.102.0.0/20",
"66.249.80.0/20",
"72.14.192.0/18",
"74.125.0.0/16",
]
for cidr_test in CIDR_TEST_LIST:
print((cidr_test.ljust(18, " "), cidr_fit(cidr_test, "216.58.196.138")))
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment