-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathfindfree
executable file
·136 lines (111 loc) · 4.56 KB
/
findfree
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env python3
"""
findfree - find free resources in the icvpn meta universe
"""
import argparse
import yaml
from itertools import islice
from ipaddress import ip_address, ip_network
from filereader import get_communities_data
TRANSFER_NET_IP4 = '10.207.0.0/16'
TRANSFER_NET_IP6 = 'fec0::a:cf:0:0/96'
FREIFUNK_NET_IP4 = '10.0.0.0/8'
FREIFUNK_NET_IP6 = 'fd00::/8'
ASN_RANGE = range(64856, 65535)
def find_transfer_ip(allocated):
"""
Find free transfer ip pairs (ipv4 & ipv6)
:param allocated: list of allocated transfer ipv4 and ipv6 addresses
:return: yield free transfer ip address pairs
"""
for ip_addr in ip_network(TRANSFER_NET_IP4).hosts():
if ip_addr in allocated:
continue
# create matching ipv6 address
ip6_addr = ip_address("fec0::a:cf:%X:%X" % (ip_addr.packed[2],
ip_addr.packed[3]))
if ip6_addr not in allocated:
yield [str(ip_addr), str(ip6_addr)]
def find_subnet(allocated, prefix_len):
"""
Find free ipv4 subnet with the given prefix length
in the freifunk range (10.0.0.0/8)
:param allocated: list of allocated subnets
:param prefix_len: required prefix length
:return: yield free ipv4 subnets
"""
def is_colliding(network, allocations):
"""
Check if given network is colliding with an
already allocated networks
"""
for allocation in allocations:
if network.overlaps(allocation):
return True
return False
for option in ip_network(FREIFUNK_NET_IP4).subnets(new_prefix=prefix_len):
if is_colliding(option, allocated):
continue
yield str(option)
def find(srcdir, required_prefix_len, option_count):
"""
Find and present free resources in the Freifunk ICVPN Universe
:param srcdir: path to icvpn-meta repository
:param required_prefix_len: required prefix lenght
:param option_count: amount of options to show
"""
transfer_net_allocations = set()
freifunk_ip4_allocations = set()
asn_free = list(ASN_RANGE)
freifunk_ip4_allocations.add(ip_network(TRANSFER_NET_IP4))
for _, data in get_communities_data(srcdir, []):
try:
for transfer_ips in data['bgp'].values():
for transfer_ip in transfer_ips.values():
transfer_net_allocations.add(ip_address(transfer_ip))
except KeyError:
# KeyError: missing bgp block
pass
try:
for ip_subnets in data['networks'].values():
for ip_subnet in ip_subnets:
freifunk_ip4_allocations.add(ip_network(ip_subnet))
except KeyError:
# KeyError: missing network block
pass
try:
asn_free.remove(data['asn'])
except (KeyError, ValueError):
# KeyError: missing asn field
# ValueError: removing non-existant value from list
pass
def take(num, iterable):
""" Return first num items of the iterable as a list """
return list(islice(iterable, num))
store = {'asn': asn_free[:option_count],
'transfer': take(option_count,
find_transfer_ip(transfer_net_allocations)),
'subnets': take(option_count,
find_subnet(freifunk_ip4_allocations,
required_prefix_len))}
print(yaml.dump(store))
if __name__ == "__main__":
PARSER = argparse.ArgumentParser(
description="Find free resources in the Freifunk ICVPN Universe",
epilog="Make sure your copy of icvpn-meta is up to date, to ensure "
"this utility produces useful results.")
PARSER.add_argument('-s', '--source-dir',
metavar='DIRECTORY', dest='src',
default='../icvpn-meta/',
help="path to the local copy of the icvpn-meta"
"repository (Default: ../icvpn-meta/)")
PARSER.add_argument('-p', '--prefix-length',
type=int, default=20,
metavar='PREFIX', dest='required_prefix_len',
help="Required prefix length (Default: 20)")
PARSER.add_argument('-c', '--count',
type=int, default=5,
metavar='COUNT', dest='option_count',
help="The amount of options to show (Default: 5)")
ARGS = PARSER.parse_args()
find(ARGS.src, ARGS.required_prefix_len, ARGS.option_count)