-
Notifications
You must be signed in to change notification settings - Fork 1
/
cidr-merge.py
executable file
·87 lines (63 loc) · 2.53 KB
/
cidr-merge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env python
#
# Takes a list of CIDR ranges or IP addresses, and removes duplicates and
# merges ranges.
import ipaddress
import sys
from typing import Collection, Iterable, List, Union
NetworkList = Union[ipaddress.IPv4Network, ipaddress.IPv6Network]
def _parse_input(input: Iterable[str]) -> Collection[List[NetworkList]]:
ipv4_networks: List[ipaddress.IPv4Network] = []
ipv6_networks: List[ipaddress.IPv6Network] = []
for line in input:
line = line.strip()
try:
parsed = ipaddress.ip_network(line)
except Exception as e:
print(f"Invalid input: {line}: {e}", file=sys.stderr)
exit(1)
if isinstance(parsed, ipaddress.IPv4Network):
ipv4_networks.append(parsed)
elif isinstance(parsed, ipaddress.IPv6Network):
ipv6_networks.append(parsed)
else:
raise Exception(f"Unknown address type {parsed}")
return ipv4_networks, ipv6_networks
def _dedup_list(netlist: List[NetworkList]) -> List[NetworkList]:
# sort the input by ascending network address and prefix length.
netlist = sorted(netlist)
# build a new list, with deduplicated/merged networks.
results = []
for net in netlist:
# if this is the first entry, seed the result list.
if not results:
results.append(net)
continue
# first of all: if this network is a subnet of the previous result, we can
# skip it. We know that the largest ranges will always come first, because
# we've sorted by ascending network address
last_result = results[-1]
if net.subnet_of(last_result):
# print(f"Skipping {net}, subnet of {last_result}")
continue
# next: if this network has the same supernet as the last result, we can merge
# them ... and then possibly carry on all the way back up the results.
while results:
last_result = results[-1]
assert not last_result.overlaps(net)
supernet = net.supernet()
last_result_supernet = last_result.supernet()
if last_result_supernet != supernet:
break
# print(f"Merging {last_result} and {net} into {supernet}")
results.pop()
net = supernet
results.append(net)
return results
def main():
netlists = _parse_input(sys.stdin)
for netlist in netlists:
for net in _dedup_list(netlist):
print(net.with_prefixlen)
if __name__ == "__main__":
main()