Skip to content

Instantly share code, notes, and snippets.

@wonderb0lt
Last active December 16, 2021 21:21
Show Gist options
  • Save wonderb0lt/a25779f83e2accc50baccffbf52e3e0c to your computer and use it in GitHub Desktop.
Save wonderb0lt/a25779f83e2accc50baccffbf52e3e0c to your computer and use it in GitHub Desktop.
Fun with subnet fragmentation
from sys import argv
from ipaddress import IPv4Network
class SubnetUsage:
def __init__(self, subnet_cidr):
self.subnet_cidr = subnet_cidr
self.network = IPv4Network(subnet_cidr)
self.used = []
def mark(self, ip_or_cidr):
self.used.append(IPv4Network(ip_or_cidr))
self.used = sorted(self.used) # Lazy implementation of sorted list, sue me
def is_allocatable(self, cidr):
our_network = IPv4Network(cidr)
return not any(map(lambda their_network: our_network.overlaps(their_network), self.used))
def find_blocks(self, prefix=28):
for block in self.network.subnets(new_prefix=prefix):
if self.is_allocatable(block):
yield block
def static_list():
usage = SubnetUsage('172.18.106.0/24')
usage.mark('172.18.106.0')
usage.mark('172.18.106.1')
usage.mark('172.18.106.2')
usage.mark('172.18.106.3')
usage.mark('172.18.106.255')
# Single ENIs
usage.mark('172.18.106.78')
usage.mark('172.18.106.82')
usage.mark('172.18.106.54')
usage.mark('172.18.106.83')
usage.mark('172.18.106.106')
usage.mark('172.18.106.209')
usage.mark('172.18.106.84')
usage.mark('172.18.106.102')
# ip ranges
usage.mark('172.18.106.128/28')
usage.mark('172.18.106.160/28')
usage.mark('172.18.106.144/28')
usage.mark('172.18.106.128/28')
usage.mark('172.18.106.160/28')
usage.mark('172.18.106.144/28')
usage.mark('172.18.106.16/28')
usage.mark('172.18.106.32/28')
usage.mark('172.18.106.192/28')
usage.mark('172.18.106.224/28')
usage.mark('172.18.106.112/28')
usage.mark('172.18.106.176/28')
return usage
def _per_subnet_usage(ec2, subnet_id):
cidr_block = ec2.describe_subnets(SubnetIds=[subnet_id])['Subnets'][0]['CidrBlock']
enis = ec2.describe_network_interfaces(Filters=[{'Name':'subnet-id','Values':[subnet_id]}])['NetworkInterfaces']
usage = SubnetUsage(cidr_block)
for iface in enis:
usage.mark(iface['PrivateIpAddress'])
if 'Ipv4Prefixes' in iface:
for prefix in iface['Ipv4Prefixes']:
usage.mark(prefix['Ipv4Prefix'])
return usage
def aws_per_subnet_usage():
import boto3
ec2 = boto3.client('ec2', region_name='eu-central-1')
subnet_id = input('Enter subnet ID: ')
return _per_subnet_usage(ec2, subnet_id)
def aws_per_vpc_availability():
import boto3
ec2 = boto3.client('ec2', region_name='eu-central-1')
vpc_id = input('VPC ID? ')
all_subnets = ec2.describe_subnets(Filters=[{'Name':'vpc-id', 'Values': [vpc_id]}])['Subnets']
all_usages = map(lambda subnet: list(_per_subnet_usage(ec2, subnet['SubnetId']).find_blocks(28)), all_subnets)
return zip(map(lambda subnet: subnet['SubnetId'], all_subnets), all_usages)
if len(argv) <= 1 or argv[1] == 'static':
usage = static_list()
blocks = list(usage.find_blocks(28))
print(f'Static list of IPs: The gathered subnet has {len(blocks)} free blocks of size /28')
elif argv[1] == 'aws_subnet':
usage = aws_per_subnet_usage()
blocks = list(usage.find_blocks(28))
print(f'AWS subnet: The specified subnet has >> {len(blocks)} << free blocks of size /28')
elif argv[1] == 'aws_vpc':
for subnet, available_blocks in aws_per_vpc_availability():
print(f'Subnet {subnet} has >> {len(available_blocks)} << free blocks of size /28')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment