blob: b21c5f61f3121dd1b8fba0f0dbcfef8df8121aa1 [file] [log] [blame]
slowrdb071b22017-07-07 11:10:25 -07001/*
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -07002 * Copyright 2017-present Open Networking Foundation
slowrdb071b22017-07-07 11:10:25 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.artemis.impl;
17
18import com.fasterxml.jackson.databind.JsonNode;
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070021import com.google.common.collect.Streams;
slowrdb071b22017-07-07 11:10:25 -070022import org.json.JSONArray;
23import org.json.JSONException;
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070024import org.onlab.packet.IpAddress;
slowrdb071b22017-07-07 11:10:25 -070025import org.onlab.packet.IpPrefix;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.net.config.Config;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.Map;
34import java.util.Objects;
35import java.util.Set;
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070036import java.util.stream.Collectors;
slowrdb071b22017-07-07 11:10:25 -070037
38import static com.google.common.base.Preconditions.checkNotNull;
39
40/**
41 * Artemis Configuration Class.
42 */
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070043public class ArtemisConfig extends Config<ApplicationId> {
slowrdb071b22017-07-07 11:10:25 -070044 private static final String PREFIXES = "prefixes";
45 /* */
46 private static final String PREFIX = "prefix";
47 private static final String PATHS = "paths";
48 private static final String MOAS = "moas";
49 /* */
50 private static final String ORIGIN = "origin";
51 private static final String NEIGHBOR = "neighbor";
52 private static final String ASN = "asn";
53 /* */
slowrdb071b22017-07-07 11:10:25 -070054 private static final String MONITORS = "monitors";
55 /* */
56 private static final String RIPE = "ripe";
57 private static final String EXABGP = "exabgp";
58 /* */
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070059 private static final String MOAS_LEGIT = "legit";
60 private static final String TUNNEL_POINTS = "tunnelPoints";
61 private static final String TUNNEL_OVSDB_IP = "ovsdb_ip";
62 private static final String TUNNEL_LOCAL_IP = "local_ip";
63 private static final String TUNNEL_OVS_PORT = "ovs_port";
slowrdb071b22017-07-07 11:10:25 -070064
65 private final Logger log = LoggerFactory.getLogger(getClass());
66
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070067 Set<IpPrefix> prefixesToMonitor() {
68 JsonNode prefixesNode = object.path(PREFIXES);
69 if (!prefixesNode.isMissingNode()) {
70 return Streams.stream(prefixesNode)
71 .map(prefix -> IpPrefix.valueOf(prefix.get(PREFIX).asText()))
72 .collect(Collectors.toSet());
73 }
74 return null;
75 }
76
slowrdb071b22017-07-07 11:10:25 -070077 /**
78 * Gets the set of monitored prefixes with the details (prefix, paths and MOAS).
79 *
80 * @return artemis class prefixes
81 */
82 Set<ArtemisPrefixes> monitoredPrefixes() {
83 Set<ArtemisPrefixes> prefixes = Sets.newHashSet();
84
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070085 JsonNode prefixesNode = object.path(PREFIXES);
86 if (prefixesNode.isMissingNode()) {
slowrdb071b22017-07-07 11:10:25 -070087 log.warn("prefixes field is null!");
88 return prefixes;
89 }
90
91 prefixesNode.forEach(jsonNode -> {
92 IpPrefix prefix = IpPrefix.valueOf(jsonNode.get(PREFIX).asText());
93
slowrdb071b22017-07-07 11:10:25 -070094 JsonNode moasNode = jsonNode.get(MOAS);
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070095 Set<IpAddress> moasIps = Streams.stream(moasNode)
96 .map(asn -> IpAddress.valueOf(asn.asText()))
97 .collect(Collectors.toSet());
slowrdb071b22017-07-07 11:10:25 -070098
slowrdb071b22017-07-07 11:10:25 -070099 JsonNode pathsNode = jsonNode.get(PATHS);
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700100 Map<Integer, Map<Integer, Set<Integer>>> paths = Maps.newHashMap();
101 pathsNode.forEach(path -> addPath(paths, path));
slowrdb071b22017-07-07 11:10:25 -0700102
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700103 prefixes.add(new ArtemisPrefixes(prefix, moasIps, paths));
slowrdb071b22017-07-07 11:10:25 -0700104 });
105
106 return prefixes;
107 }
108
109 /**
110 * Appends an ASN path on the ASN paths list of the Artemis application.
111 *
112 * @param paths active ASN paths list
113 * @param path ASN path to be added
114 */
115 private void addPath(Map<Integer, Map<Integer, Set<Integer>>> paths, JsonNode path) {
116 Integer origin = path.path(ORIGIN).asInt();
117
118 JsonNode firstNeighborNode = path.path(NEIGHBOR);
119 // Check if neighbor exists in the configuration
120 if (!firstNeighborNode.isMissingNode()) {
121 firstNeighborNode.forEach(firstNeighbor -> {
122 Integer firstNeighborAsn = firstNeighbor.get(ASN).asInt();
123
124 JsonNode secondNeighborNode = firstNeighbor.path(NEIGHBOR);
125 // check if second neighbor exists in configuration
126 if (!secondNeighborNode.isMissingNode()) {
127 secondNeighborNode.forEach(secondNeighbor -> {
128 Integer secondNeighborAsn = secondNeighbor.asInt();
129
130 if (paths.containsKey(origin)) {
131 // paths already contain origin ASN.
132 Map<Integer, Set<Integer>> integerSetMap = paths.get(origin);
133 if (integerSetMap.containsKey(firstNeighborAsn)) {
134 integerSetMap.get(firstNeighborAsn).add(secondNeighborAsn);
135 } else {
136 paths.get(origin).put(firstNeighborAsn, Sets.newHashSet(secondNeighborAsn));
137 }
138 } else {
139 // origin ASN does not exist in Map.
140 Map<Integer, Set<Integer>> first2second = Maps.newHashMap();
141 first2second.put(firstNeighborAsn, Sets.newHashSet(secondNeighborAsn));
142 paths.put(origin, first2second);
143 }
144 });
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700145 // else append to paths without second neighbor
slowrdb071b22017-07-07 11:10:25 -0700146 } else {
147 if (!paths.containsKey(origin)) {
148 Map<Integer, Set<Integer>> first2second = Maps.newHashMap();
149 first2second.put(firstNeighborAsn, Sets.newHashSet());
150 paths.put(origin, first2second);
151 } else {
152 // paths already contain origin ASN.
153 Map<Integer, Set<Integer>> integerSetMap = paths.get(origin);
154 if (!integerSetMap.containsKey(firstNeighborAsn)) {
155 paths.get(origin).put(firstNeighborAsn, Sets.newHashSet());
156 }
157 }
158 }
159 });
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700160 // else append to paths only the origin
slowrdb071b22017-07-07 11:10:25 -0700161 } else {
162 if (!paths.containsKey(origin)) {
163 paths.put(origin, Maps.newHashMap());
164 }
165 }
166 }
167
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700168// /**
169// * Helper function to print the loaded ASN paths.
170// *
171// * @param paths ASN paths to print
172// */
173// private void printPaths(Map<Integer, Map<Integer, Set<Integer>>> paths) {
174// log.warn("------------------------------------");
175// paths.forEach((k, v) -> v.forEach((l, n) -> {
176// n.forEach(p -> log.warn("Origin: " + k + ", 1st: " + l + ", 2nd: " + p));
177// }));
178// }
slowrdb071b22017-07-07 11:10:25 -0700179
180 /**
181 * Gets the active route collectors.
182 *
183 * @return map with type as a key and host as a value.
184 */
185 Map<String, Set<String>> activeMonitors() {
186 Map<String, Set<String>> monitors = Maps.newHashMap();
187
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700188 JsonNode monitorsNode = object.path(MONITORS);
slowrdb071b22017-07-07 11:10:25 -0700189
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700190 if (!monitorsNode.isMissingNode()) {
191 JsonNode ripeNode = monitorsNode.path(RIPE);
192 if (!ripeNode.isMissingNode()) {
193 Set<String> hosts = Sets.newHashSet();
194 ripeNode.forEach(host -> hosts.add(host.asText()));
195 monitors.put(RIPE, hosts);
196 }
slowrdb071b22017-07-07 11:10:25 -0700197
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700198 JsonNode exabgpNode = monitorsNode.path(EXABGP);
199 if (!exabgpNode.isMissingNode()) {
200 Set<String> hosts = Sets.newHashSet();
201 exabgpNode.forEach(host -> hosts.add(host.asText()));
202 monitors.put(EXABGP, hosts);
203 }
slowrdb071b22017-07-07 11:10:25 -0700204 }
205
206 return monitors;
207 }
208
209 /**
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700210 * Get the information about MOAS. Including remote MOAS server IPs, OVSDB ID and local tunnel IP.
211 *
212 * @return MOAS information
213 */
214 MoasInfo moasInfo() {
215 MoasInfo moasInfo = new MoasInfo();
216
217 JsonNode moasNode = object.path(MOAS);
218
219 if (!moasNode.isMissingNode()) {
220 JsonNode legitIpsNode = moasNode.path(MOAS_LEGIT);
221 if (!legitIpsNode.isMissingNode()) {
222 if (legitIpsNode.isArray()) {
223 moasInfo.setMoasAddresses(
224 Streams.stream(legitIpsNode)
225 .map(ipAddress -> IpAddress.valueOf(ipAddress.asText()))
226 .collect(Collectors.toSet())
227 );
228 } else {
229 log.warn("Legit MOAS field need to be a list");
230 }
231 } else {
232 log.warn("No IPs for legit MOAS specified in configuration");
233 }
234
235 JsonNode tunnelPointsNode = moasNode.path(TUNNEL_POINTS);
236 if (!tunnelPointsNode.isMissingNode()) {
237 if (tunnelPointsNode.isArray()) {
238 tunnelPointsNode.forEach(
239 tunnelPoint -> {
240 JsonNode idNode = tunnelPoint.path(TUNNEL_OVSDB_IP),
241 localNode = tunnelPoint.path(TUNNEL_LOCAL_IP),
242 ovsNode = tunnelPoint.path(TUNNEL_OVS_PORT);
243
244 if (!idNode.isMissingNode() && !localNode.isMissingNode()) {
245 moasInfo.addTunnelPoint(
246 new MoasInfo.TunnelPoint(
247 IpAddress.valueOf(idNode.asText()),
248 IpAddress.valueOf(localNode.asText()),
249 ovsNode.asText()
250 )
251 );
252 } else {
253 log.warn("Tunnel point need to have an ID and a Local IP");
254 }
255 }
256 );
257 } else {
258 log.warn("Tunnel points field need to be a list");
259 }
260 }
261 } else {
262 log.warn("No tunnel points specified in configuration");
263 }
264
265 return moasInfo;
266 }
267
268 /**
269 * Information holder for MOAS.
270 */
271 public static class MoasInfo {
272 private Set<IpAddress> moasAddresses;
273 private Set<TunnelPoint> tunnelPoints;
274
275 public MoasInfo() {
276 moasAddresses = Sets.newConcurrentHashSet();
277 tunnelPoints = Sets.newConcurrentHashSet();
278 }
279
280 public Set<IpAddress> getMoasAddresses() {
281 return moasAddresses;
282 }
283
284 public void setMoasAddresses(Set<IpAddress> moasAddresses) {
285 this.moasAddresses = moasAddresses;
286 }
287
288 public Set<TunnelPoint> getTunnelPoints() {
289 return tunnelPoints;
290 }
291
292 public void setTunnelPoints(Set<TunnelPoint> tunnelPoints) {
293 this.tunnelPoints = tunnelPoints;
294 }
295
296 public TunnelPoint getTunnelPoint() {
297 return tunnelPoints.iterator().next();
298 }
299
300 public void addTunnelPoint(TunnelPoint tunnelPoint) {
301 this.tunnelPoints.add(tunnelPoint);
302 }
303
304 @Override
305 public String toString() {
306 return "MoasInfo{" +
307 "moasAddresses=" + moasAddresses +
308 ", tunnelPoints=" + tunnelPoints +
309 '}';
310 }
311
312 public static class TunnelPoint {
313 private IpAddress ovsdbIp;
314 private IpAddress localIP;
315 private String ovsPort;
316
317 public TunnelPoint(IpAddress ovsdbIp, IpAddress localIP, String ovsPort) {
318 this.ovsdbIp = ovsdbIp;
319 this.localIP = localIP;
320 this.ovsPort = ovsPort;
321 }
322
323 public IpAddress getOvsdbIp() {
324 return ovsdbIp;
325 }
326
327 public void setOvsdbIp(IpAddress ovsdbIp) {
328 this.ovsdbIp = ovsdbIp;
329 }
330
331 public IpAddress getLocalIp() {
332 return localIP;
333 }
334
335 public void setLocalIp(IpAddress localIP) {
336 this.localIP = localIP;
337 }
338
339 public String getOvsPort() {
340 return ovsPort;
341 }
342
343 public void setOvsPort(String ovsPort) {
344 this.ovsPort = ovsPort;
345 }
346
347 @Override
348 public String toString() {
349 return "TunnelPoint{" +
350 "ovsdbIp='" + ovsdbIp + '\'' +
351 ", localIP=" + localIP +
352 ", ovsPort='" + ovsPort + '\'' +
353 '}';
354 }
355 }
356 }
357
358 /**
slowrdb071b22017-07-07 11:10:25 -0700359 * Configuration for a specific prefix.
360 */
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700361 public class ArtemisPrefixes {
slowrdb071b22017-07-07 11:10:25 -0700362 private IpPrefix prefix;
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700363 private Set<IpAddress> moas;
slowrdb071b22017-07-07 11:10:25 -0700364 private Map<Integer, Map<Integer, Set<Integer>>> paths;
365
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700366 ArtemisPrefixes(IpPrefix prefix, Set<IpAddress> moas, Map<Integer, Map<Integer, Set<Integer>>> paths) {
slowrdb071b22017-07-07 11:10:25 -0700367 this.prefix = checkNotNull(prefix);
368 this.moas = checkNotNull(moas);
369 this.paths = checkNotNull(paths);
370 }
371
372 protected IpPrefix prefix() {
373 return prefix;
374 }
375
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700376 protected Set<IpAddress> moas() {
slowrdb071b22017-07-07 11:10:25 -0700377 return moas;
378 }
379
380 protected Map<Integer, Map<Integer, Set<Integer>>> paths() {
381 return paths;
382 }
383
384 /**
385 * Given a path we check if the origin is a friendly MOAS or our ASN.
386 * If the origin ASN is not ours the we have a hijack of type 0. Next, in case that the first neighbor is
387 * not a legit neighbor from our configuration we detect a hijack of type 1 and lastly, if the second
388 * neighbor is not a legit neighbor we detect a type 2 hijack.
389 *
390 * @param path as-path that announces our prefix and found from monitors
391 * @return <code>0</code> no bgp hijack detected
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700392 * <code>50</code> friendly anycaster announcing our prefix
393 * <code>100+i</code> BGP hijack type i (0 &lt;= i &lt;=2)
slowrdb071b22017-07-07 11:10:25 -0700394 */
395 int checkPath(JSONArray path) {
396 // TODO add MOAS check
397 ArrayList<Integer> asnPath = new ArrayList<>();
398 for (int i = 0; i < path.length(); i++) {
399 try {
400 asnPath.add(path.getInt(i));
401 } catch (JSONException e) {
402 e.printStackTrace();
403 }
404 }
405 // reverse the list to get path starting from origin
406 Collections.reverse(asnPath);
407
408 if (asnPath.size() > 0 && !paths.containsKey(asnPath.get(0))) {
409 return 100;
410 } else if (asnPath.size() > 1 && !paths.get(asnPath.get(0)).containsKey(asnPath.get(1))) {
411 return 101;
412 } else if (asnPath.size() > 2 && !paths.get(asnPath.get(0)).get(asnPath.get(1)).contains(asnPath.get(2))) {
413 return 102;
414 }
415 return 0;
416 }
417
418 @Override
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700419 public String toString() {
420 return "ArtemisPrefixes{" +
421 "prefix=" + prefix +
422 ", moas=" + moas +
423 ", paths=" + paths +
424 '}';
425 }
426
427 @Override
slowrdb071b22017-07-07 11:10:25 -0700428 public int hashCode() {
429 return Objects.hashCode(prefix);
430 }
431
432 @Override
433 public boolean equals(Object obj) {
434 if (this == obj) {
435 return true;
436 }
437 if (obj instanceof PrefixHandler) {
438 final PrefixHandler that = (PrefixHandler) obj;
439 return Objects.equals(this.prefix, that.getPrefix());
440 }
441 return false;
442 }
443 }
444
445}