blob: 5db9eaf4dab06eac3058357dd6e08eb6d3114d44 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.sdnip.bgp;
Jonathan Hartab63aac2014-10-16 08:52:55 -070017
18import static com.google.common.base.Preconditions.checkNotNull;
19
20import java.util.ArrayList;
21import java.util.Objects;
22
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.sdnip.RouteEntry;
24import org.onosproject.sdnip.bgp.BgpConstants.Update;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080025import org.onlab.packet.Ip4Address;
26import org.onlab.packet.Ip4Prefix;
Jonathan Hartab63aac2014-10-16 08:52:55 -070027
28import com.google.common.base.MoreObjects;
29
30/**
31 * Represents a route in BGP.
32 */
33public class BgpRouteEntry extends RouteEntry {
34 private final BgpSession bgpSession; // The BGP Session the route was
35 // received on
36 private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
37 private final AsPath asPath; // The AS Path
38 private final long localPref; // The local preference for the route
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -080039 private long multiExitDisc = Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
Jonathan Hartab63aac2014-10-16 08:52:55 -070040
41 /**
42 * Class constructor.
43 *
44 * @param bgpSession the BGP Session the route was received on
45 * @param prefix the prefix of the route
46 * @param nextHop the next hop of the route
47 * @param origin the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
48 * @param asPath the AS path
49 * @param localPref the route local preference
50 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080051 public BgpRouteEntry(BgpSession bgpSession, Ip4Prefix prefix,
52 Ip4Address nextHop, byte origin,
Jonathan Hartab63aac2014-10-16 08:52:55 -070053 BgpRouteEntry.AsPath asPath, long localPref) {
54 super(prefix, nextHop);
55 this.bgpSession = checkNotNull(bgpSession);
56 this.origin = origin;
57 this.asPath = checkNotNull(asPath);
58 this.localPref = localPref;
59 }
60
61 /**
62 * Gets the BGP Session the route was received on.
63 *
64 * @return the BGP Session the route was received on
65 */
66 public BgpSession getBgpSession() {
67 return bgpSession;
68 }
69
70 /**
71 * Gets the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE.
72 *
73 * @return the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
74 */
75 public byte getOrigin() {
76 return origin;
77 }
78
79 /**
80 * Gets the route AS path.
81 *
82 * @return the route AS path
83 */
84 public BgpRouteEntry.AsPath getAsPath() {
85 return asPath;
86 }
87
88 /**
89 * Gets the route local preference.
90 *
91 * @return the route local preference
92 */
93 public long getLocalPref() {
94 return localPref;
95 }
96
97 /**
98 * Gets the route MED (Multi-Exit Discriminator).
99 *
100 * @return the route MED (Multi-Exit Discriminator)
101 */
102 public long getMultiExitDisc() {
103 return multiExitDisc;
104 }
105
106 /**
107 * Sets the route MED (Multi-Exit Discriminator).
108 *
109 * @param multiExitDisc the route MED (Multi-Exit Discriminator) to set
110 */
111 void setMultiExitDisc(long multiExitDisc) {
112 this.multiExitDisc = multiExitDisc;
113 }
114
115 /**
116 * Tests whether the route is originated from the local AS.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700117 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700118 * The route is considered originated from the local AS if the AS Path
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800119 * is empty or if it begins with an AS_SET (after skipping
120 * AS_CONFED_SEQUENCE and AS_CONFED_SET).
Thomas Vachuska4b420772014-10-30 16:46:17 -0700121 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700122 *
123 * @return true if the route is originated from the local AS, otherwise
124 * false
125 */
126 boolean isLocalRoute() {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800127 PathSegment firstPathSegment = null;
128
129 // Find the first Path Segment by ignoring the AS_CONFED_* segments
130 for (PathSegment pathSegment : asPath.getPathSegments()) {
131 if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
132 (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
133 firstPathSegment = pathSegment;
134 break;
135 }
136 }
137 if (firstPathSegment == null) {
138 return true; // Local route: no path segments
139 }
140 // If the first path segment is AS_SET, the route is considered local
141 if (firstPathSegment.getType() == Update.AsPath.AS_SET) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700142 return true;
143 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800144
145 return false; // The route is not local
Jonathan Hartab63aac2014-10-16 08:52:55 -0700146 }
147
148 /**
149 * Gets the BGP Neighbor AS number the route was received from.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700150 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700151 * If the router is originated from the local AS, the return value is
152 * zero (BGP_AS_0).
Thomas Vachuska4b420772014-10-30 16:46:17 -0700153 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700154 *
155 * @return the BGP Neighbor AS number the route was received from.
156 */
157 long getNeighborAs() {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800158 PathSegment firstPathSegment = null;
159
Jonathan Hartab63aac2014-10-16 08:52:55 -0700160 if (isLocalRoute()) {
161 return BgpConstants.BGP_AS_0;
162 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800163
164 // Find the first Path Segment by ignoring the AS_CONFED_* segments
165 for (PathSegment pathSegment : asPath.getPathSegments()) {
166 if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
167 (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
168 firstPathSegment = pathSegment;
169 break;
170 }
171 }
172 if (firstPathSegment == null) {
173 // NOTE: Shouldn't happen - should be captured by isLocalRoute()
174 return BgpConstants.BGP_AS_0;
175 }
176
Jonathan Hartab63aac2014-10-16 08:52:55 -0700177 if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
178 // TODO: Shouldn't happen. Should check during the parsing.
179 return BgpConstants.BGP_AS_0;
180 }
181 return firstPathSegment.getSegmentAsNumbers().get(0);
182 }
183
184 /**
185 * Tests whether the AS Path contains a loop.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700186 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700187 * The test is done by comparing whether the AS Path contains the
188 * local AS number.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700189 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700190 *
191 * @param localAsNumber the local AS number to compare against
192 * @return true if the AS Path contains a loop, otherwise false
193 */
194 boolean hasAsPathLoop(long localAsNumber) {
195 for (PathSegment pathSegment : asPath.getPathSegments()) {
196 for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
197 if (asNumber.equals(localAsNumber)) {
198 return true;
199 }
200 }
201 }
202 return false;
203 }
204
205 /**
206 * Compares this BGP route against another BGP route by using the
207 * BGP Decision Process.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700208 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700209 * NOTE: The comparison needs to be performed only on routes that have
210 * same IP Prefix.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700211 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700212 *
213 * @param other the BGP route to compare against
214 * @return true if this BGP route is better than the other BGP route
215 * or same, otherwise false
216 */
217 boolean isBetterThan(BgpRouteEntry other) {
218 if (this == other) {
219 return true; // Return true if same route
220 }
221
222 // Compare the LOCAL_PREF values: larger is better
223 if (getLocalPref() != other.getLocalPref()) {
224 return (getLocalPref() > other.getLocalPref());
225 }
226
227 // Compare the AS number in the path: smaller is better
228 if (getAsPath().getAsPathLength() !=
229 other.getAsPath().getAsPathLength()) {
230 return getAsPath().getAsPathLength() <
231 other.getAsPath().getAsPathLength();
232 }
233
234 // Compare the Origin number: lower is better
235 if (getOrigin() != other.getOrigin()) {
236 return (getOrigin() < other.getOrigin());
237 }
238
239 // Compare the MED if the neighbor AS is same: larger is better
240 medLabel: {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800241 if (isLocalRoute() || other.isLocalRoute()) {
242 // Compare MEDs for non-local routes only
243 break medLabel;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700244 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800245 long thisNeighborAs = getNeighborAs();
246 if (thisNeighborAs != other.getNeighborAs()) {
247 break medLabel; // AS number is different
248 }
249 if (thisNeighborAs == BgpConstants.BGP_AS_0) {
250 break medLabel; // Invalid AS number
Jonathan Hartab63aac2014-10-16 08:52:55 -0700251 }
252
253 // Compare the MED
254 if (getMultiExitDisc() != other.getMultiExitDisc()) {
255 return (getMultiExitDisc() > other.getMultiExitDisc());
256 }
257 }
258
259 // Compare the peer BGP ID: lower is better
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800260 Ip4Address peerBgpId = getBgpSession().getRemoteBgpId();
261 Ip4Address otherPeerBgpId = other.getBgpSession().getRemoteBgpId();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700262 if (!peerBgpId.equals(otherPeerBgpId)) {
263 return (peerBgpId.compareTo(otherPeerBgpId) < 0);
264 }
265
266 // Compare the peer BGP address: lower is better
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800267 Ip4Address peerAddress = getBgpSession().getRemoteIp4Address();
268 Ip4Address otherPeerAddress =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700269 other.getBgpSession().getRemoteIp4Address();
270 if (!peerAddress.equals(otherPeerAddress)) {
271 return (peerAddress.compareTo(otherPeerAddress) < 0);
272 }
273
274 return true; // Routes are same. Shouldn't happen?
275 }
276
277 /**
278 * A class to represent AS Path Segment.
279 */
280 public static class PathSegment {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800281 // Segment type: AS_SET(1), AS_SEQUENCE(2), AS_CONFED_SEQUENCE(3),
282 // AS_CONFED_SET(4)
283 private final byte type;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700284 private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
285
286 /**
287 * Constructor.
288 *
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800289 * @param type the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
290 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
Jonathan Hartab63aac2014-10-16 08:52:55 -0700291 * @param segmentAsNumbers the Segment AS numbers
292 */
293 PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
294 this.type = type;
295 this.segmentAsNumbers = checkNotNull(segmentAsNumbers);
296 }
297
298 /**
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800299 * Gets the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
300 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4).
Jonathan Hartab63aac2014-10-16 08:52:55 -0700301 *
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800302 * @return the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
303 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
Jonathan Hartab63aac2014-10-16 08:52:55 -0700304 */
305 public byte getType() {
306 return type;
307 }
308
309 /**
310 * Gets the Path Segment AS Numbers.
311 *
312 * @return the Path Segment AS Numbers
313 */
314 public ArrayList<Long> getSegmentAsNumbers() {
315 return segmentAsNumbers;
316 }
317
318 @Override
319 public boolean equals(Object other) {
320 if (this == other) {
321 return true;
322 }
323
324 if (!(other instanceof PathSegment)) {
325 return false;
326 }
327
328 PathSegment otherPathSegment = (PathSegment) other;
329 return Objects.equals(this.type, otherPathSegment.type) &&
330 Objects.equals(this.segmentAsNumbers,
331 otherPathSegment.segmentAsNumbers);
332 }
333
334 @Override
335 public int hashCode() {
336 return Objects.hash(type, segmentAsNumbers);
337 }
338
339 @Override
340 public String toString() {
341 return MoreObjects.toStringHelper(getClass())
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800342 .add("type", Update.AsPath.typeToString(type))
Jonathan Hartab63aac2014-10-16 08:52:55 -0700343 .add("segmentAsNumbers", this.segmentAsNumbers)
344 .toString();
345 }
346 }
347
348 /**
349 * A class to represent AS Path.
350 */
351 public static class AsPath {
352 private final ArrayList<PathSegment> pathSegments;
353 private final int asPathLength; // Precomputed AS Path Length
354
355 /**
356 * Constructor.
357 *
358 * @param pathSegments the Path Segments of the Path
359 */
360 AsPath(ArrayList<PathSegment> pathSegments) {
361 this.pathSegments = checkNotNull(pathSegments);
362
363 //
364 // Precompute the AS Path Length:
365 // - AS_SET counts as 1
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800366 // - AS_SEQUENCE counts how many AS numbers are included
367 // - AS_CONFED_SEQUENCE and AS_CONFED_SET are ignored
Jonathan Hartab63aac2014-10-16 08:52:55 -0700368 //
369 int pl = 0;
370 for (PathSegment pathSegment : pathSegments) {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800371 switch (pathSegment.getType()) {
372 case Update.AsPath.AS_SET:
373 pl++; // AS_SET counts as 1
374 break;
375 case Update.AsPath.AS_SEQUENCE:
376 // Count each AS number
377 pl += pathSegment.getSegmentAsNumbers().size();
378 break;
379 case Update.AsPath.AS_CONFED_SEQUENCE:
380 break; // Ignore
381 case Update.AsPath.AS_CONFED_SET:
382 break; // Ignore
383 default:
384 // TODO: What to do if the Path Segment type is unknown?
385 break;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700386 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700387 }
388 asPathLength = pl;
389 }
390
391 /**
392 * Gets the AS Path Segments.
393 *
394 * @return the AS Path Segments
395 */
396 public ArrayList<PathSegment> getPathSegments() {
397 return pathSegments;
398 }
399
400 /**
401 * Gets the AS Path Length as considered by the BGP Decision Process.
402 *
403 * @return the AS Path Length as considered by the BGP Decision Process
404 */
405 int getAsPathLength() {
406 return asPathLength;
407 }
408
409 @Override
410 public boolean equals(Object other) {
411 if (this == other) {
412 return true;
413 }
414
415 if (!(other instanceof AsPath)) {
416 return false;
417 }
418
419 AsPath otherAsPath = (AsPath) other;
420 return Objects.equals(this.pathSegments, otherAsPath.pathSegments);
421 }
422
423 @Override
424 public int hashCode() {
425 return Objects.hash(pathSegments);
426 }
427
428 @Override
429 public String toString() {
430 return MoreObjects.toStringHelper(getClass())
431 .add("pathSegments", this.pathSegments)
432 .toString();
433 }
434 }
435
436 /**
437 * Compares whether two objects are equal.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700438 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700439 * NOTE: The bgpSession field is excluded from the comparison.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700440 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700441 *
442 * @return true if the two objects are equal, otherwise false.
443 */
444 @Override
445 public boolean equals(Object other) {
446 if (this == other) {
447 return true;
448 }
449
450 //
451 // NOTE: Subclasses are considered as change of identity, hence
452 // equals() will return false if the class type doesn't match.
453 //
454 if (other == null || getClass() != other.getClass()) {
455 return false;
456 }
457
458 if (!super.equals(other)) {
459 return false;
460 }
461
462 // NOTE: The bgpSession field is excluded from the comparison
463 BgpRouteEntry otherRoute = (BgpRouteEntry) other;
464 return (this.origin == otherRoute.origin) &&
465 Objects.equals(this.asPath, otherRoute.asPath) &&
466 (this.localPref == otherRoute.localPref) &&
467 (this.multiExitDisc == otherRoute.multiExitDisc);
468 }
469
470 /**
471 * Computes the hash code.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700472 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700473 * NOTE: We return the base class hash code to avoid expensive computation
Thomas Vachuska4b420772014-10-30 16:46:17 -0700474 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700475 *
476 * @return the object hash code
477 */
478 @Override
479 public int hashCode() {
480 return super.hashCode();
481 }
482
483 @Override
484 public String toString() {
485 return MoreObjects.toStringHelper(getClass())
486 .add("prefix", prefix())
487 .add("nextHop", nextHop())
488 .add("bgpId", bgpSession.getRemoteBgpId())
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800489 .add("origin", Update.Origin.typeToString(origin))
Jonathan Hartab63aac2014-10-16 08:52:55 -0700490 .add("asPath", asPath)
491 .add("localPref", localPref)
492 .add("multiExitDisc", multiExitDisc)
493 .toString();
494 }
495}