79 lines
2.2 KiB
Python
Executable file
79 lines
2.2 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
import ipaddress
|
|
import sys
|
|
import argparse
|
|
|
|
# Default to /32 and /128 for single IP addresses
|
|
start_v4 = 32
|
|
start_v6 = 128
|
|
|
|
parser = argparse.ArgumentParser(
|
|
prog='uniqcidr',
|
|
description='Like uniq, but understands CIDR netmasks',
|
|
epilog='...but written by an idiot')
|
|
|
|
parser.add_argument('-c', '--count', action='store_true', help="count number of IPs occurring in each subnet")
|
|
parser.add_argument('-n', '--network', action='store_true', help="Enable network mode, don't require contiguous IP addresses. Sets /48 and /24 thresholds")
|
|
parser.add_argument('-6', '--v6netmask', type=int, help=f'IPv6 netmask, default: {start_v6}')
|
|
parser.add_argument('-4', '--v4netmask', type=int, help=f'IPv4 netmask, default: {start_v4}')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Network mode counts every /24 or /48
|
|
if args.network:
|
|
start_v4 = 24
|
|
start_v6 = 48
|
|
|
|
# Custom subnet thresholds. -6 64 is probably the othe most useful here.
|
|
if args.v6netmask:
|
|
start_v6 = args.v6netmask
|
|
if args.v4netmask:
|
|
start_v4 = args.v4netmask
|
|
|
|
def render_line(count, curr_network):
|
|
if args.count:
|
|
print(f"{count:>5} {curr_network}")
|
|
else:
|
|
print(curr_network)
|
|
|
|
curr_network = None
|
|
count = 0
|
|
for line in sys.stdin:
|
|
|
|
line = line.rstrip()
|
|
try:
|
|
ifa = ipaddress.ip_interface(line)
|
|
except ValueError:
|
|
print(f"Not an ip address: {line}", file=sys.stderr)
|
|
continue
|
|
|
|
prefix = start_v6
|
|
if ifa.version == 4:
|
|
prefix=start_v4
|
|
|
|
network = ifa.network.supernet(new_prefix=prefix)
|
|
|
|
if curr_network is None:
|
|
curr_network = network
|
|
continue
|
|
|
|
count = count + 1
|
|
|
|
if network.version != curr_network.version:
|
|
render_line(count, curr_network)
|
|
curr_network = network
|
|
count = 0
|
|
|
|
if curr_network != network:
|
|
if network.subnet_of(curr_network):
|
|
pass
|
|
elif network.subnet_of(curr_network.supernet()):
|
|
curr_network = curr_network.supernet()
|
|
else:
|
|
render_line(count, curr_network)
|
|
curr_network = network
|
|
count = 0
|
|
|
|
count = count + 1
|
|
render_line(count, curr_network)
|