forked from freifunk/icvpn-scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmkbgp
executable file
·176 lines (150 loc) · 6.43 KB
/
mkbgp
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/env python3
import socket
from collections import defaultdict
from textwrap import dedent
from optparse import OptionParser
from formatter import Formatter
from filereader import get_communities_data
from multiprocessing.dummy import Pool
import ipaddress
class BirdFormatter(Formatter):
"Formatter for Bird"
def add_data(self, asn, name, template, peer, passive=False):
self.config.append(dedent("""
protocol bgp {name} from {template} {{
neighbor {peer} as {asn};""".format(
peer=peer, asn=asn, name=name[:32], template=template)))
if passive:
self.config.append(" passive yes;")
self.config.append("}\n")
class QuaggaFormatter(Formatter):
"Formatter for quagga"
def add_comment(self, comment):
self.config.append("! " + "\n! ".join(comment.split("\n")))
def add_data(self, asn, name, template, peer, passive=False):
self.config.append(dedent("""
neighbor {peer} remote-as {asn}
neighbor {peer} description {name}
neighbor {peer} peer-group {template}""".format(peer=peer, asn=asn, name=name, template=template)))
if passive:
self.config.append("neighbor {peer} passive".format(peer=peer))
def is_reachable(host, port, timeout):
"""
Test reachability of host by opening a TCP connection to the specified
port.
"""
try:
fd = socket.create_connection((host, port), timeout)
return True
except (socket.timeout, socket.error):
return False
finally:
try:
fd.close()
except (OSError, NameError):
pass
def create_config(srcdir, exclude, prefix, defaulttemplate, templates, family,
fmtclass, timeout, interface):
"""
Generates a configuration using all files in srcdir
(non-recursively) excluding communities from 'exclude'.
The files are read in lexicographic order to produce deterministic
results.
"""
formatter = fmtclass()
template = defaultdict(lambda: defaulttemplate)
template.update(dict(map(lambda s: s.split(":"), templates)))
peers = []
for community, data in get_communities_data(srcdir, exclude):
try:
bgp = data["bgp"]
asn = data["asn"]
except (TypeError, KeyError):
continue
for host in sorted(bgp.keys()):
d = bgp[host]
if family not in d:
continue
peer = d[family]
try:
addr = ipaddress.IPv6Address(peer)
if addr.is_link_local:
peer = "{}%{}".format(peer, interface)
except ipaddress.AddressValueError:
pass # Ignore - No valid IP-Address - but still, a DNS-Lookup may work
peers.append({
"asn": asn,
"host": host,
"community": community,
"peer": peer,
"passive": False,
})
if timeout > 0:
def update_peer_passive(peer):
peer["passive"] = not is_reachable(peer["peer"], 179, timeout)
pool = Pool(len(peers))
pool.map(update_peer_passive, peers)
pool.close()
pool.join()
# if all peers are passive, ignore the check results
if all(peer["passive"] for peer in peers):
for peer in peers:
peer["passive"] = False
for peer in peers:
formatter.add_data(peer["asn"], prefix + peer["host"],
template[peer["community"]], peer["peer"],
peer["passive"])
print(formatter.finalize())
if __name__ == "__main__":
formatters = {
"bird": BirdFormatter,
"quagga": QuaggaFormatter,
}
parser = OptionParser()
parser.add_option("-f", "--format", dest="fmt",
help="""Create config in format FMT.
Possible values: {}. Default: bird""".format(
", ".join(formatters.keys())),
metavar="FMT",
choices=list(formatters.keys()),
default="bird")
parser.add_option("-4", dest="family", action="store_const", const="ipv4",
help="Generate IPv4 config")
parser.add_option("-6", dest="family", action="store_const", const="ipv6",
help="Generate IPv6 config")
parser.add_option("-s", "--sourcedir", dest="src",
help="""Use files in DIR as input files.
Default: ../icvpn-meta/""", metavar="DIR",
default="../icvpn-meta/")
parser.add_option("-x", "--exclude", dest="exclude", action="append",
help="Exclude the comma-separated list of COMMUNITIES",
metavar="COMMUNITIES",
default=[])
parser.add_option("-p", "--prefix", dest="prefix",
help="Prefix, e.g. bgp_icvpn_",
metavar="PREFIX",
default="")
parser.add_option("-P", "--passive-offline", dest="passive_offline",
help="""Add peers that take longer than TIMEOUT to
respond at time of creation as passive peers
Set to 0 do disable
Default: 3 seconds""",
default=3, type="float", metavar="TIMEOUT",)
parser.add_option("-d", "--default", dest="defaulttemplate",
help="Default template/peer-group to use",
metavar="TEMPLATE",
default=None)
parser.add_option("-t", "--template", dest="templates", action="append",
help="Use different template/peer-group for some " +
"communities",
metavar="COMMUNITY:TEMPLATE",
default=[])
parser.add_option("-I", "--interface", dest="interface",
help="""Interface used for link local addresses. Default: icvpn""", metavar="INTERFACE",
default="icvpn")
parser.set_defaults(family="ipv6")
(options, args) = parser.parse_args()
create_config(options.src, set(options.exclude), options.prefix,
options.defaulttemplate, options.templates,
options.family, formatters[options.fmt],
options.passive_offline, options.interface)