Skip to content

Instantly share code, notes, and snippets.

@AMMullan
Last active November 11, 2020 16:45
Show Gist options
  • Save AMMullan/ee9a3d7a38de703ff437765ebd0fc38e to your computer and use it in GitHub Desktop.
Save AMMullan/ee9a3d7a38de703ff437765ebd0fc38e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import sys
import socket
import struct
import platform
try:
import ipaddress
except:
raise SystemExit('Script can only execute in Python 3.3 or newer.')
def print_usage(msg=None):
print('''%sUsage: ipcalc.py <ADDRESS>[[/]<NETMASK>] [NETMASK]
This is a rewrite to Python of the ipcalc util (http://jodies.de/ipcalc).
Trying to keep Python native.
ipcalc takes an IP address and netmask and calculates the resulting broadcast,
network, Cisco wildcard mask, and host range. By giving a second netmask, you
can design sub- and supernetworks. It is also intended to be a teaching tool
and presents the results as easy-to-understand binary values.
Examples:
ipcalc 192.168.0.1/24
ipcalc 192.168.0.1/255.255.128.0
ipcalc 192.168.0.1 255.255.128.0 255.255.192.0
ipcalc 192.168.0.1 0.0.63.255''' % (msg + '\n\n' if msg else ''))
raise SystemExit
def handle_args(args):
cidr = None
netmask = None
netmask_new = None
if not args[1:]:
print_usage()
if '-h' in args:
print_usage()
is_aws = False
if '-a' in args:
is_aws = True
args.remove('-a')
try:
network = ipaddress.ip_network(args[1], strict=True)
except ValueError:
print_usage('Invalid CIDR Provided')
else:
cidr = network.network_address
netmask = network.prefixlen
if args[2:]:
if args[2].isdigit():
netmask_new = int(args[2])
else:
netmask_new = ip_to_bin(args[2]).count('1')
if netmask_new <= netmask:
print_usage('New prefix must be longer than original.')
return [str(cidr), netmask, netmask_new, is_aws]
def ip_to_bin(ip_addr):
return '.'.join([bin(int(x) + 256)[3:] for x in ip_addr.split('.')])
def prefix_to_netmask(prefix):
return socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - prefix)) & 0xffffffff))
def get_network_ips(network):
sub = ipaddress.ip_network(network)
return [str(host) for host in list(sub.hosts())]
def get_network_subnets(network, new_prefix):
sub = ipaddress.ip_network(network)
if new_prefix:
return [str(subnet) for subnet in sub.subnets(new_prefix=new_prefix)]
return [network]
def print_line(title, address, binary="", comment=""):
print(
'%-20s%-22s%-36s%s' % (
title,
address,
binary,
comment
)
)
def main(cidr, subnet, output_to=None, is_aws=False):
network = '%s/%s' % (cidr, subnet)
aws = {}
sub_count = addr_count = 0
for network_subnet in get_network_subnets(network, output_to):
address, prefix = network_subnet.split('/')
netmask = prefix_to_netmask(int(prefix))
subnet_hosts = get_network_ips(network_subnet)
if subnet_hosts:
if is_aws:
aws['router'] = subnet_hosts[0]
aws['dns'] = subnet_hosts[1]
aws['fu'] = subnet_hosts[2]
first_host = subnet_hosts[3]
else:
first_host = subnet_hosts[0]
last_host = subnet_hosts[-1]
# /32 network
else:
subnet_hosts = [network_subnet]
first_host = last_host = cidr
host_count = len(subnet_hosts) - len(aws.keys())
sub_count += 1
addr_count += host_count
print_line('Address:', address, ip_to_bin(address))
print_line('Netmask:', '%s = %d' % (netmask, int(prefix)), ip_to_bin(netmask))
print_line('Network:', network_subnet, ip_to_bin(cidr))
if is_aws:
print_line('- AWS Router:', aws['router'], ip_to_bin(aws['router']))
print_line('- AWS DNS:', aws['dns'], ip_to_bin(aws['dns']))
print_line('- AWS Future Use:', aws['fu'], ip_to_bin(aws['fu']))
print_line('- First Host:', first_host, ip_to_bin(first_host))
print_line('- Last Host:', last_host, ip_to_bin(last_host))
print_line('- Host Count:', host_count)
print('')
if sub_count:
print('=====\nSubnets: %d' % sub_count)
print('Address Count: %d' % addr_count)
if __name__ == "__main__":
if not platform.python_version().startswith('3.'):
raise SystemExit('Python 3.3 or newer required.')
CIDR, NETMASK, NETMASK_NEW, IS_AWS = handle_args(sys.argv)
main(CIDR, NETMASK, NETMASK_NEW, IS_AWS)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment