-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmessages.py
224 lines (179 loc) · 7.28 KB
/
messages.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
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
from flowapp.constants import (
ANNOUNCE,
WITHDRAW,
IPV4_DEFMASK,
IPV6_DEFMASK,
MAX_PACKET,
IPV4_PROTOCOL,
IPV6_NEXT_HEADER,
)
from flowapp.flowspec import translate_sequence as trps
from flask import current_app
from flowapp.models import ASPath
from flowapp import db
def create_ipv4(rule, message_type=ANNOUNCE):
"""
create ExaBpg text message for IPv4 rule
@param rule models.Flowspec4
@return string message
"""
protocol = ""
if rule.protocol and rule.protocol != "all":
protocol = "protocol ={};".format(IPV4_PROTOCOL[rule.protocol])
flagstring = rule.flags.replace(";", " ") if rule.flags else ""
flags = "tcp-flags {};".format(flagstring) if rule.flags and rule.protocol == "tcp" else ""
fragment_string = rule.fragment.replace(";", " ") if rule.fragment else ""
fragment = "fragment [ {} ];".format(fragment_string) if rule.fragment else ""
spec = {
"protocol": protocol,
"flags": flags,
"fragment": fragment,
"mask": IPV4_DEFMASK,
}
return create_message(rule, spec, message_type)
def create_ipv6(rule, message_type=ANNOUNCE):
"""
create ExaBpg text message for IPv6 rule
@param rule models.Flowspec6
@return string message
:param message_type:
"""
protocol = ""
if rule.next_header and rule.next_header != "all":
protocol = "next-header ={};".format(IPV6_NEXT_HEADER[rule.next_header])
flagstring = rule.flags.replace(";", " ")
flags = "tcp-flags {};".format(flagstring) if rule.flags and rule.next_header == "tcp" else ""
spec = {"protocol": protocol, "mask": IPV6_DEFMASK, "flags": flags}
return create_message(rule, spec, message_type)
def create_rtbh(rule, message_type=ANNOUNCE):
"""
create RTBH message in ExaBgp text format
route 10.10.10.1/32 next-hop 192.0.2.1 community 65001:666 no-export
"""
action = "announce"
if message_type == WITHDRAW:
action = "withdraw"
if rule.ipv4:
my_4mask = sanitize_mask(rule.ipv4_mask, IPV4_DEFMASK)
source = "{}".format(rule.ipv4) if rule.ipv4 else ""
source += "/{}".format(my_4mask) if rule.ipv4 else ""
nexthop = "192.0.2.1"
if rule.ipv6:
my_6mask = sanitize_mask(rule.ipv6_mask, IPV6_DEFMASK)
source = "{}".format(rule.ipv6) if rule.ipv6 else ""
source += "/{}".format(my_6mask) if rule.ipv6 else ""
nexthop = "100::1"
try:
if current_app.config["USE_RD"]:
rd_string = "rd {rd} label {label}".format(
rd=current_app.config["RD_STRING"], label=current_app.config["RD_LABEL"]
)
else:
rd_string = ""
except KeyError:
rd_string = ""
try:
if current_app.config["USE_MULTI_NEIGHBOR"] and rule.community.comm:
if rule.community.comm in current_app.config["MULTI_NEIGHBOR"].keys():
targets = current_app.config["MULTI_NEIGHBOR"].get(rule.community.comm)
else:
targets = current_app.config["MULTI_NEIGHBOR"].get("primary")
neighbor = prepare_multi_neighbor(targets)
else:
neighbor = ""
except KeyError:
neighbor = ""
community_string = "community [{}]".format(rule.community.comm) if rule.community.comm else ""
large_community_string = "large-community [{}]".format(rule.community.larcomm) if rule.community.larcomm else ""
extended_community_string = (
"extended-community [{}]".format(rule.community.extcomm) if rule.community.extcomm else ""
)
as_path_string = ""
if rule.community.as_path:
match = db.session.query(ASPath).filter(ASPath.prefix == source).first()
as_path_string = f"as-path [ {match.as_path} ]" if match else ""
return "{neighbor}{action} route {source} next-hop {nexthop} {as_path} {community} {large_community} {extended_community}{rd_string}".format(
neighbor=neighbor,
action=action,
source=source,
as_path=as_path_string,
community=community_string,
large_community=large_community_string,
extended_community=extended_community_string,
nexthop=nexthop,
rd_string=rd_string,
)
def create_message(rule, ipv_specific, message_type=ANNOUNCE):
"""
create text message using format
tcp-flagy
flow route { match { source 147.230.17.6/32;protocol =tcp;tcp-flags fin
syn;destination-port =3128 >=8080&<=8088;} then {rate-limit 10000; } }
announce flow route source 4.0.0.0/24 destination 127.0.0.0/24 protocol
[ udp ] source-port [ =53 ] destination-port [ =80 ]
packet-length [ =777 =1122 ] fragment [ is-fragment dont-fragment ] rate-limit 1024"
"""
action = "announce"
if message_type == WITHDRAW:
action = "withdraw"
smask = sanitize_mask(rule.source_mask, ipv_specific["mask"])
source = "source {}".format(rule.source) if rule.source else ""
source += "/{};".format(smask) if rule.source else ""
source_port = "source-port {};".format(trps(rule.source_port)) if rule.source_port else ""
dmask = sanitize_mask(rule.dest_mask, ipv_specific["mask"])
dest = " destination {}".format(rule.dest) if rule.dest else ""
dest += "/{};".format(dmask) if rule.dest else ""
dest_port = "destination-port {};".format(trps(rule.dest_port)) if rule.dest_port else ""
protocol = ipv_specific.get("protocol", "")
flags = ipv_specific.get("flags", "")
fragment = ipv_specific.get("fragment", "")
packet_len = "packet-length {};".format(trps(rule.packet_len, MAX_PACKET)) if rule.packet_len else ""
values = [source, source_port, dest, dest_port, protocol, fragment, flags, packet_len]
match_body = " ".join(v for v in values if v)
command = "{};".format(rule.action.command)
try:
if current_app.config["USE_RD"]:
rd_string = "route-distinguisher {rd};".format(rd=current_app.config["RD_STRING"])
rt_string = "extended-community target:{rt};".format(rt=current_app.config["RT_STRING"])
else:
rd_string = ""
rt_string = ""
except KeyError:
rd_string = ""
rt_string = ""
try:
if current_app.config["USE_MULTI_NEIGHBOR"]:
targets = current_app.config["MULTI_NEIGHBOR"].get("primary")
neighbor = prepare_multi_neighbor(targets)
else:
neighbor = ""
except KeyError:
neighbor = ""
message_body = "{neighbor}{action} flow route {{ {rd_string} match {{ {match_body} }} then {{ {command} {rt_string} }} }}".format(
neighbor=neighbor,
action=action,
match_body=match_body,
command=command,
rd_string=rd_string,
rt_string=rt_string,
)
return message_body
def sanitize_mask(rule_mask, default_mask=IPV4_DEFMASK):
"""
Sanitize mask / prefix of rule
:param default_mask: default mask to return if mask is not in the rule
:param rule: flowspec rule
:return: int mask
"""
if rule_mask is None:
return default_mask
if 0 <= rule_mask <= default_mask:
return rule_mask
else:
return default_mask
def prepare_multi_neighbor(targets: list):
"""
prepare multi neighbor string
"""
neigbors = [f"neighbor {tgt}" for tgt in targets]
return ", ".join(neigbors) + " "