Skip to content

Instantly share code, notes, and snippets.

@jg75
Created June 18, 2018 13:50
Show Gist options
  • Save jg75/e6c7b0b84867462ef343e0d9cae74f43 to your computer and use it in GitHub Desktop.
Save jg75/e6c7b0b84867462ef343e0d9cae74f43 to your computer and use it in GitHub Desktop.
A CIDR IP address calculator
"""A subnet IP address range calculator."""
import argparse
import copy
import math
class Subnet:
"""A subnet."""
@staticmethod
def get_routing_prefix_octets(mask):
"""Get the octets of a CIDR mask as a list of integers."""
octets = [0, 0, 0, 0]
elements = mask[:mask.index("/")].split(".")
for i in range(4):
if len(elements) > i:
octets[i] = int(elements[i])
if octets[i] < 0 or octets[i] > 255:
raise ValueError(f"Invalid CIDR mask: {mask}")
return octets
@staticmethod
def get_routing_prefix_length(mask):
"""Get the length of a CIDR mask as an integer."""
length = int(mask[mask.index("/") + 1:])
if length < 0 or length > 32:
raise ValueError(f"Invalid CIDR mask: {mask}")
return length
@staticmethod
def get_theoretical_ip_count(octets, length):
"""Get the theoretical number of available IP's in the subnet."""
capacity = 1
for i in range(int(length / 8), 4):
if i < 3:
capacity = capacity * (256 - octets[i])
else:
capacity = capacity * 256 - octets[i]
return capacity
@staticmethod
def get_theoretical_size(octets, length):
"""Get the theoretical size of the subnet."""
return int(math.pow(2, 32 - length))
@classmethod
def get_length(cls, octets, length):
"""Get the length of the subnet IP generator."""
ip_count = cls.get_theoretical_ip_count(octets, length)
size = cls.get_theoretical_size(octets, length)
return min(ip_count, size)
def __init__(self, mask="0.0.0.0/0"):
"""Override."""
self.mask = mask
def __repr__(self):
"""Override."""
return f"{self.__class__.__name__}('{self.mask}')"
def __str__(self):
"""Override."""
return self.mask
def __iter__(self):
"""Override."""
return self.generate()
def __next__(self):
"""Override."""
return next(self.generator)
def __len__(self):
"""Count the IP addresses in the subnet."""
return self.length
@property
def mask(self):
"""Get mask."""
return self._mask
@mask.setter
def mask(self, mask):
"""Set mask."""
self.routing_prefix_octets = self.get_routing_prefix_octets(mask)
self.routing_prefix_length = self.get_routing_prefix_length(mask)
self._mask = mask
self.generator = self.generate()
self.length = self.get_length(self.routing_prefix_octets,
self.routing_prefix_length)
def generate(self):
"""Generate IP addresses in the subnet."""
octets = copy.deepcopy(self.routing_prefix_octets)
static_octets = int(self.routing_prefix_length / 8)
for _ in range(len(self)):
ip_address = ".".join([str(octet) for octet in octets])
for i in reversed(range(static_octets, 4)):
octets[i] += 1
if octets[i] > 255:
octets[i] = 0
else:
break
yield ip_address
def parse_args(description=__doc__):
"""Parse input arguments."""
parser = argparse.ArgumentParser(description=description)
parser.add_argument(
'-c', "--count",
help="Count the IP addresses in the subnet",
action='store_true',
default=False
)
parser.add_argument(
"cidr",
help="A CIDR mask",
nargs="*"
)
return parser.parse_args()
def main():
"""Do the CLI."""
arguments = parse_args()
for mask in arguments.cidr:
subnet = Subnet(mask)
if arguments.count:
print(len(subnet))
else:
for ip in subnet:
print(ip)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment