blob: 741a4fe7d9f1275a21331c1c9dc7cc1afee9ff20 [file] [log] [blame]
slowrdb071b22017-07-07 11:10:25 -07001/*
2 * Copyright 2015-present Open Networking Laboratory
3 *
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;
21import org.json.JSONArray;
22import org.json.JSONException;
23import org.onlab.packet.IpPrefix;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.net.config.Config;
26import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
28
29import java.util.ArrayList;
30import java.util.Collections;
31import java.util.Map;
32import java.util.Objects;
33import java.util.Set;
34
35import static com.google.common.base.Preconditions.checkNotNull;
36
37/**
38 * Artemis Configuration Class.
39 */
40class ArtemisConfig extends Config<ApplicationId> {
41
42 private static final String PREFIXES = "prefixes";
43 /* */
44 private static final String PREFIX = "prefix";
45 private static final String PATHS = "paths";
46 private static final String MOAS = "moas";
47 /* */
48 private static final String ORIGIN = "origin";
49 private static final String NEIGHBOR = "neighbor";
50 private static final String ASN = "asn";
51 /* */
52
53 private static final String MONITORS = "monitors";
54 /* */
55 private static final String RIPE = "ripe";
56 private static final String EXABGP = "exabgp";
57 /* */
58
59 private static final String FREQUENCY = "frequency";
60
61
62 private final Logger log = LoggerFactory.getLogger(getClass());
63
64 /**
65 * Gets the set of monitored prefixes with the details (prefix, paths and MOAS).
66 *
67 * @return artemis class prefixes
68 */
69 Set<ArtemisPrefixes> monitoredPrefixes() {
70 Set<ArtemisPrefixes> prefixes = Sets.newHashSet();
71
72 JsonNode prefixesNode = object.get(PREFIXES);
73 if (prefixesNode == null) {
74 log.warn("prefixes field is null!");
75 return prefixes;
76 }
77
78 prefixesNode.forEach(jsonNode -> {
79 IpPrefix prefix = IpPrefix.valueOf(jsonNode.get(PREFIX).asText());
80
81 Set<Integer> moasNumbers = Sets.newHashSet();
82 JsonNode moasNode = jsonNode.get(MOAS);
83 moasNode.forEach(asn ->
84 moasNumbers.add(asn.asInt())
85 );
86
87 /*
88 "paths" : [{
89 "origin" : 65004,
90 "neighbor" : [{
91 "asn" : 65002,
92 "neighbor": [{
93 "asn" : 65001,
94 }]
95 }]
96 }]
97 */
98
99 Map<Integer, Map<Integer, Set<Integer>>> paths = Maps.newHashMap();
100 JsonNode pathsNode = jsonNode.get(PATHS);
101 pathsNode.forEach(path -> {
102 addPath(paths, path);
103 });
104
105 // printPaths(paths);
106
107 prefixes.add(new ArtemisPrefixes(prefix, moasNumbers, paths));
108 });
109
110 return prefixes;
111 }
112
113 /**
114 * Appends an ASN path on the ASN paths list of the Artemis application.
115 *
116 * @param paths active ASN paths list
117 * @param path ASN path to be added
118 */
119 private void addPath(Map<Integer, Map<Integer, Set<Integer>>> paths, JsonNode path) {
120 Integer origin = path.path(ORIGIN).asInt();
121
122 JsonNode firstNeighborNode = path.path(NEIGHBOR);
123 // Check if neighbor exists in the configuration
124 if (!firstNeighborNode.isMissingNode()) {
125 firstNeighborNode.forEach(firstNeighbor -> {
126 Integer firstNeighborAsn = firstNeighbor.get(ASN).asInt();
127
128 JsonNode secondNeighborNode = firstNeighbor.path(NEIGHBOR);
129 // check if second neighbor exists in configuration
130 if (!secondNeighborNode.isMissingNode()) {
131 secondNeighborNode.forEach(secondNeighbor -> {
132 Integer secondNeighborAsn = secondNeighbor.asInt();
133
134 if (paths.containsKey(origin)) {
135 // paths already contain origin ASN.
136 Map<Integer, Set<Integer>> integerSetMap = paths.get(origin);
137 if (integerSetMap.containsKey(firstNeighborAsn)) {
138 integerSetMap.get(firstNeighborAsn).add(secondNeighborAsn);
139 } else {
140 paths.get(origin).put(firstNeighborAsn, Sets.newHashSet(secondNeighborAsn));
141 }
142 } else {
143 // origin ASN does not exist in Map.
144 Map<Integer, Set<Integer>> first2second = Maps.newHashMap();
145 first2second.put(firstNeighborAsn, Sets.newHashSet(secondNeighborAsn));
146 paths.put(origin, first2second);
147 }
148 });
149 // else append to paths without second neighbor
150 } else {
151 if (!paths.containsKey(origin)) {
152 Map<Integer, Set<Integer>> first2second = Maps.newHashMap();
153 first2second.put(firstNeighborAsn, Sets.newHashSet());
154 paths.put(origin, first2second);
155 } else {
156 // paths already contain origin ASN.
157 Map<Integer, Set<Integer>> integerSetMap = paths.get(origin);
158 if (!integerSetMap.containsKey(firstNeighborAsn)) {
159 paths.get(origin).put(firstNeighborAsn, Sets.newHashSet());
160 }
161 }
162 }
163 });
164 // else append to paths only the origin
165 } else {
166 if (!paths.containsKey(origin)) {
167 paths.put(origin, Maps.newHashMap());
168 }
169 }
170 }
171
172 /**
173 * Helper function to print the loaded ASN paths.
174 *
175 * @param paths ASN paths to print
176 */
177 private void printPaths(Map<Integer, Map<Integer, Set<Integer>>> paths) {
178 log.warn("------------------------------------");
179 paths.forEach((k, v) -> v.forEach((l, n) -> {
180 n.forEach(p -> log.warn("Origin: " + k + ", 1st: " + l + ", 2nd: " + p));
181 }));
182 }
183
184 /**
185 * Gets the frequency of the detection module in milliseconds.
186 *
187 * @return frequency (ms)
188 */
189 int detectionFrequency() {
190 JsonNode thresholdNode = object.get(FREQUENCY);
191 int threshold = 0;
192
193 if (thresholdNode == null) {
194 log.warn("threshold field is null!");
195 return threshold;
196 }
197
198 return thresholdNode.asInt();
199 }
200
201 /**
202 * Gets the active route collectors.
203 *
204 * @return map with type as a key and host as a value.
205 */
206 Map<String, Set<String>> activeMonitors() {
207 Map<String, Set<String>> monitors = Maps.newHashMap();
208
209 JsonNode monitorsNode = object.get(MONITORS);
210
211 JsonNode ripeNode = monitorsNode.path(RIPE);
212 if (!ripeNode.isMissingNode()) {
213 Set<String> hosts = Sets.newHashSet();
214 ripeNode.forEach(host -> hosts.add(host.asText()));
215 monitors.put(RIPE, hosts);
216 }
217
218 JsonNode exabgpNode = monitorsNode.path(EXABGP);
219 if (!exabgpNode.isMissingNode()) {
220 Set<String> hosts = Sets.newHashSet();
221 exabgpNode.forEach(host -> hosts.add(host.asText()));
222 monitors.put(EXABGP, hosts);
223 }
224
225 return monitors;
226 }
227
228 /**
229 * Configuration for a specific prefix.
230 */
231 static class ArtemisPrefixes {
232 private IpPrefix prefix;
233 private Set<Integer> moas;
234 private Map<Integer, Map<Integer, Set<Integer>>> paths;
235
236 private final Logger log = LoggerFactory.getLogger(getClass());
237
238 ArtemisPrefixes(IpPrefix prefix, Set<Integer> moas, Map<Integer, Map<Integer, Set<Integer>>> paths) {
239 this.prefix = checkNotNull(prefix);
240 this.moas = checkNotNull(moas);
241 this.paths = checkNotNull(paths);
242 }
243
244 protected IpPrefix prefix() {
245 return prefix;
246 }
247
248 protected Set<Integer> moas() {
249 return moas;
250 }
251
252 protected Map<Integer, Map<Integer, Set<Integer>>> paths() {
253 return paths;
254 }
255
256 /**
257 * Given a path we check if the origin is a friendly MOAS or our ASN.
258 * If the origin ASN is not ours the we have a hijack of type 0. Next, in case that the first neighbor is
259 * not a legit neighbor from our configuration we detect a hijack of type 1 and lastly, if the second
260 * neighbor is not a legit neighbor we detect a type 2 hijack.
261 *
262 * @param path as-path that announces our prefix and found from monitors
263 * @return <code>0</code> no bgp hijack detected
264 * <code>50</code> friendly anycaster announcing our prefix
265 * <code>100+i</code> BGP hijack type i (0 &lt;= i &lt;=2)
266 */
267 int checkPath(JSONArray path) {
268 // TODO add MOAS check
269 ArrayList<Integer> asnPath = new ArrayList<>();
270 for (int i = 0; i < path.length(); i++) {
271 try {
272 asnPath.add(path.getInt(i));
273 } catch (JSONException e) {
274 e.printStackTrace();
275 }
276 }
277 // reverse the list to get path starting from origin
278 Collections.reverse(asnPath);
279
280 if (asnPath.size() > 0 && !paths.containsKey(asnPath.get(0))) {
281 return 100;
282 } else if (asnPath.size() > 1 && !paths.get(asnPath.get(0)).containsKey(asnPath.get(1))) {
283 return 101;
284 } else if (asnPath.size() > 2 && !paths.get(asnPath.get(0)).get(asnPath.get(1)).contains(asnPath.get(2))) {
285 return 102;
286 }
287 return 0;
288 }
289
290 @Override
291 public int hashCode() {
292 return Objects.hashCode(prefix);
293 }
294
295 @Override
296 public boolean equals(Object obj) {
297 if (this == obj) {
298 return true;
299 }
300 if (obj instanceof PrefixHandler) {
301 final PrefixHandler that = (PrefixHandler) obj;
302 return Objects.equals(this.prefix, that.getPrefix());
303 }
304 return false;
305 }
306 }
307
308}