def generate_new_cidr(allocated_subnets: List[str], required_cidr_block_type: int) -> str: if required_cidr_block_type >= 32 or required_cidr_block_type < 8: raise NetmaskValueError("Invalid netmask for this operation.") free_subnets = get_free_subnets(allocated_subnets) # compare to the required subnet size and work with what is large enough free_subnets = [ x for x in free_subnets if x.prefixlen <= required_cidr_block_type ] if len(free_subnets) == 0: raise Exception("Not enough space in network.") # allocate in smaller subnets before larger ones free_subnets.sort(key=lambda x: x.prefixlen, reverse=True) allocation_subnet = free_subnets[0] new_subnet = list( allocation_subnet.subnets(new_prefix=required_cidr_block_type)) return str(new_subnet[0])
def ipv4_address(addr: str) -> str: """ Validate an IPv4 address. :param addr: IPv4 address :returns: IPv4 address :raises TypeError, AddressValueError or NetmaskValueError """ if not isinstance(addr, str): raise TypeError("Invalid input, addr must be a string") else: addr = addr.strip() if addr == "": return addr if not netaddr.valid_ipv4(addr): raise AddressValueError("Invalid IPv4 address format (%s)" % addr) if netaddr.IPAddress(addr).is_netmask(): raise NetmaskValueError("Invalid IPv4 host address (%s)" % addr) return addr
def ipv4_address(addr: str) -> str: """ Validate an IPv4 address. :param addr: IPv4 address :returns: IPv4 address :raises TypeError: Raised if ``addr`` is not a string. :raises AddressValueError: Raised in case ``addr`` is not a valid IPv4 address. :raises NetmaskValueError: Raised in case ``addr`` is not a valid IPv4 netmask. """ if not isinstance(addr, str): raise TypeError("Invalid input, addr must be a string") addr = addr.strip() if addr == "": return addr if not netaddr.valid_ipv4(addr): raise AddressValueError("Invalid IPv4 address format (%s)" % addr) if netaddr.IPAddress(addr).is_netmask(): raise NetmaskValueError("Invalid IPv4 host address (%s)" % addr) return addr
def acl_with_wildcard_to_netmasks(address_str: str, wildcard_str: str): """ Translates an ACL (address, wildcard) to a list of ip_network objects (address, netmask) :param address_str: IP address string (v4 or v6) :param wildcard_str: wildcard mask (v4 or v6) :return: list of IP networks E.G for address="172.18.161.2" and wildcard "0.1.2.7" it returns: [IPv4Network('172.18.161.0/29'), IPv4Network('172.18.163.0/29'), IPv4Network('172.19.161.0/29'), IPv4Network('172.19.163.0/29')] """ ip_addr = ipaddress.ip_address(address_str) wildcard = ipaddress.ip_address(wildcard_str) if wildcard.version != ip_addr.version: raise ValueError( f"IP version mismtach: address_str({address_str}), wildcard_str({wildcard_str})" ) # default values for v4 _length = ipaddress.IPV4LENGTH _net_cls = ipaddress.IPv4Network if wildcard.version == 6: # values for v6 _length = ipaddress.IPV6LENGTH _net_cls = ipaddress.IPv6Network mask_bits = [int(b) for b in format(int(wildcard), F"0{_length}b")] # We keep count of zero bits position (left-most is 0) dont_care_bits_index = [ i for i, e in enumerate(reversed(mask_bits)) if e == 1 ] # We count how many contiguous zeros are from left-most bit, and we will mask them with a netmask hostmask_length = 0 for (pos, bit) in enumerate(dont_care_bits_index): if pos != bit: break hostmask_length += 1 # We only keep the bits that can't be dealt with by a netmask and need to be expanded to cartesian product dont_care_to_expand_index = dont_care_bits_index[hostmask_length:] # reverse in order to have the final loop iterate last through high order bits dont_care_to_expand_index.reverse() if len(dont_care_to_expand_index) > MAX_DONT_CARE_BITS: raise NetmaskValueError( f"{wildcard_str} contains more than {MAX_DONT_CARE_BITS} non-contiguous wildcard bits" ) ip_int_original = int(ip_addr) subnets = [] for bits_values in itertools.product( (0, 1), repeat=len(dont_care_to_expand_index)): # enforce the bits_values in the IP address ip_int = ip_int_original for (index, val) in zip(dont_care_to_expand_index, bits_values): sb_mask = 1 << index if val: ip_int |= sb_mask else: ip_int &= ~sb_mask subnets.append( _net_cls((ip_int, _length - hostmask_length), strict=False)) return subnets