blob: adf00904b9660e5dafda7b7e63780dc229244d94 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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 */
Jonathan Hart41349e92015-02-09 14:14:02 -080016package org.onosproject.routing.bgp;
Jonathan Hartab63aac2014-10-16 08:52:55 -070017
Jonathan Hart41349e92015-02-09 14:14:02 -080018import com.google.common.base.MoreObjects;
19import org.onlab.packet.Ip4Address;
20import org.onlab.packet.IpAddress;
21import org.onlab.packet.IpPrefix;
Jonathan Hartab63aac2014-10-16 08:52:55 -070022
23import java.util.ArrayList;
24import java.util.Objects;
25
Jonathan Hart41349e92015-02-09 14:14:02 -080026import static com.google.common.base.Preconditions.checkNotNull;
Jonathan Hartab63aac2014-10-16 08:52:55 -070027
28/**
29 * Represents a route in BGP.
30 */
31public class BgpRouteEntry extends RouteEntry {
32 private final BgpSession bgpSession; // The BGP Session the route was
33 // received on
34 private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
35 private final AsPath asPath; // The AS Path
36 private final long localPref; // The local preference for the route
Jonathan Hart41349e92015-02-09 14:14:02 -080037 private long multiExitDisc = BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
Jonathan Hartab63aac2014-10-16 08:52:55 -070038
39 /**
40 * Class constructor.
41 *
42 * @param bgpSession the BGP Session the route was received on
43 * @param prefix the prefix of the route
44 * @param nextHop the next hop of the route
45 * @param origin the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
46 * @param asPath the AS path
47 * @param localPref the route local preference
48 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080049 public BgpRouteEntry(BgpSession bgpSession, IpPrefix prefix,
50 IpAddress nextHop, byte origin,
Jonathan Hartab63aac2014-10-16 08:52:55 -070051 BgpRouteEntry.AsPath asPath, long localPref) {
52 super(prefix, nextHop);
53 this.bgpSession = checkNotNull(bgpSession);
54 this.origin = origin;
55 this.asPath = checkNotNull(asPath);
56 this.localPref = localPref;
57 }
58
59 /**
60 * Gets the BGP Session the route was received on.
61 *
62 * @return the BGP Session the route was received on
63 */
64 public BgpSession getBgpSession() {
65 return bgpSession;
66 }
67
68 /**
69 * Gets the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE.
70 *
71 * @return the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
72 */
73 public byte getOrigin() {
74 return origin;
75 }
76
77 /**
78 * Gets the route AS path.
79 *
80 * @return the route AS path
81 */
82 public BgpRouteEntry.AsPath getAsPath() {
83 return asPath;
84 }
85
86 /**
87 * Gets the route local preference.
88 *
89 * @return the route local preference
90 */
91 public long getLocalPref() {
92 return localPref;
93 }
94
95 /**
96 * Gets the route MED (Multi-Exit Discriminator).
97 *
98 * @return the route MED (Multi-Exit Discriminator)
99 */
100 public long getMultiExitDisc() {
101 return multiExitDisc;
102 }
103
104 /**
105 * Sets the route MED (Multi-Exit Discriminator).
106 *
107 * @param multiExitDisc the route MED (Multi-Exit Discriminator) to set
108 */
109 void setMultiExitDisc(long multiExitDisc) {
110 this.multiExitDisc = multiExitDisc;
111 }
112
113 /**
114 * Tests whether the route is originated from the local AS.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700115 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700116 * The route is considered originated from the local AS if the AS Path
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800117 * is empty or if it begins with an AS_SET (after skipping
118 * AS_CONFED_SEQUENCE and AS_CONFED_SET).
Thomas Vachuska4b420772014-10-30 16:46:17 -0700119 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700120 *
121 * @return true if the route is originated from the local AS, otherwise
122 * false
123 */
124 boolean isLocalRoute() {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800125 PathSegment firstPathSegment = null;
126
127 // Find the first Path Segment by ignoring the AS_CONFED_* segments
128 for (PathSegment pathSegment : asPath.getPathSegments()) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800129 if ((pathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) ||
130 (pathSegment.getType() == BgpConstants.Update.AsPath.AS_SEQUENCE)) {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800131 firstPathSegment = pathSegment;
132 break;
133 }
134 }
135 if (firstPathSegment == null) {
136 return true; // Local route: no path segments
137 }
138 // If the first path segment is AS_SET, the route is considered local
Jonathan Hart41349e92015-02-09 14:14:02 -0800139 if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700140 return true;
141 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800142
143 return false; // The route is not local
Jonathan Hartab63aac2014-10-16 08:52:55 -0700144 }
145
146 /**
147 * Gets the BGP Neighbor AS number the route was received from.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700148 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700149 * If the router is originated from the local AS, the return value is
150 * zero (BGP_AS_0).
Thomas Vachuska4b420772014-10-30 16:46:17 -0700151 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700152 *
153 * @return the BGP Neighbor AS number the route was received from.
154 */
155 long getNeighborAs() {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800156 PathSegment firstPathSegment = null;
157
Jonathan Hartab63aac2014-10-16 08:52:55 -0700158 if (isLocalRoute()) {
159 return BgpConstants.BGP_AS_0;
160 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800161
162 // Find the first Path Segment by ignoring the AS_CONFED_* segments
163 for (PathSegment pathSegment : asPath.getPathSegments()) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800164 if ((pathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) ||
165 (pathSegment.getType() == BgpConstants.Update.AsPath.AS_SEQUENCE)) {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800166 firstPathSegment = pathSegment;
167 break;
168 }
169 }
170 if (firstPathSegment == null) {
171 // NOTE: Shouldn't happen - should be captured by isLocalRoute()
172 return BgpConstants.BGP_AS_0;
173 }
174
Jonathan Hartab63aac2014-10-16 08:52:55 -0700175 if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
Jonathan Harte9ef4f32014-12-04 17:49:22 -0800176 // NOTE: Shouldn't happen. Should check during the parsing.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700177 return BgpConstants.BGP_AS_0;
178 }
179 return firstPathSegment.getSegmentAsNumbers().get(0);
180 }
181
182 /**
183 * Tests whether the AS Path contains a loop.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700184 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700185 * The test is done by comparing whether the AS Path contains the
186 * local AS number.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700187 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700188 *
189 * @param localAsNumber the local AS number to compare against
190 * @return true if the AS Path contains a loop, otherwise false
191 */
192 boolean hasAsPathLoop(long localAsNumber) {
193 for (PathSegment pathSegment : asPath.getPathSegments()) {
194 for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
195 if (asNumber.equals(localAsNumber)) {
196 return true;
197 }
198 }
199 }
200 return false;
201 }
202
203 /**
204 * Compares this BGP route against another BGP route by using the
205 * BGP Decision Process.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700206 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700207 * NOTE: The comparison needs to be performed only on routes that have
208 * same IP Prefix.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700209 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700210 *
211 * @param other the BGP route to compare against
212 * @return true if this BGP route is better than the other BGP route
213 * or same, otherwise false
214 */
215 boolean isBetterThan(BgpRouteEntry other) {
216 if (this == other) {
217 return true; // Return true if same route
218 }
219
220 // Compare the LOCAL_PREF values: larger is better
221 if (getLocalPref() != other.getLocalPref()) {
222 return (getLocalPref() > other.getLocalPref());
223 }
224
225 // Compare the AS number in the path: smaller is better
226 if (getAsPath().getAsPathLength() !=
227 other.getAsPath().getAsPathLength()) {
228 return getAsPath().getAsPathLength() <
229 other.getAsPath().getAsPathLength();
230 }
231
232 // Compare the Origin number: lower is better
233 if (getOrigin() != other.getOrigin()) {
234 return (getOrigin() < other.getOrigin());
235 }
236
237 // Compare the MED if the neighbor AS is same: larger is better
238 medLabel: {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800239 if (isLocalRoute() || other.isLocalRoute()) {
240 // Compare MEDs for non-local routes only
241 break medLabel;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700242 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800243 long thisNeighborAs = getNeighborAs();
244 if (thisNeighborAs != other.getNeighborAs()) {
245 break medLabel; // AS number is different
246 }
247 if (thisNeighborAs == BgpConstants.BGP_AS_0) {
248 break medLabel; // Invalid AS number
Jonathan Hartab63aac2014-10-16 08:52:55 -0700249 }
250
251 // Compare the MED
252 if (getMultiExitDisc() != other.getMultiExitDisc()) {
253 return (getMultiExitDisc() > other.getMultiExitDisc());
254 }
255 }
256
257 // Compare the peer BGP ID: lower is better
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800258 Ip4Address peerBgpId = getBgpSession().remoteInfo().bgpId();
259 Ip4Address otherPeerBgpId = other.getBgpSession().remoteInfo().bgpId();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700260 if (!peerBgpId.equals(otherPeerBgpId)) {
261 return (peerBgpId.compareTo(otherPeerBgpId) < 0);
262 }
263
264 // Compare the peer BGP address: lower is better
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800265 Ip4Address peerAddress = getBgpSession().remoteInfo().ip4Address();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800266 Ip4Address otherPeerAddress =
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800267 other.getBgpSession().remoteInfo().ip4Address();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700268 if (!peerAddress.equals(otherPeerAddress)) {
269 return (peerAddress.compareTo(otherPeerAddress) < 0);
270 }
271
272 return true; // Routes are same. Shouldn't happen?
273 }
274
275 /**
276 * A class to represent AS Path Segment.
277 */
278 public static class PathSegment {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800279 // Segment type: AS_SET(1), AS_SEQUENCE(2), AS_CONFED_SEQUENCE(3),
280 // AS_CONFED_SET(4)
281 private final byte type;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700282 private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
283
284 /**
285 * Constructor.
286 *
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800287 * @param type the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
288 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
Jonathan Hartab63aac2014-10-16 08:52:55 -0700289 * @param segmentAsNumbers the Segment AS numbers
290 */
291 PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
292 this.type = type;
293 this.segmentAsNumbers = checkNotNull(segmentAsNumbers);
294 }
295
296 /**
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800297 * Gets the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
298 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4).
Jonathan Hartab63aac2014-10-16 08:52:55 -0700299 *
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800300 * @return the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
301 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
Jonathan Hartab63aac2014-10-16 08:52:55 -0700302 */
303 public byte getType() {
304 return type;
305 }
306
307 /**
308 * Gets the Path Segment AS Numbers.
309 *
310 * @return the Path Segment AS Numbers
311 */
312 public ArrayList<Long> getSegmentAsNumbers() {
313 return segmentAsNumbers;
314 }
315
316 @Override
317 public boolean equals(Object other) {
318 if (this == other) {
319 return true;
320 }
321
322 if (!(other instanceof PathSegment)) {
323 return false;
324 }
325
326 PathSegment otherPathSegment = (PathSegment) other;
327 return Objects.equals(this.type, otherPathSegment.type) &&
328 Objects.equals(this.segmentAsNumbers,
329 otherPathSegment.segmentAsNumbers);
330 }
331
332 @Override
333 public int hashCode() {
334 return Objects.hash(type, segmentAsNumbers);
335 }
336
337 @Override
338 public String toString() {
339 return MoreObjects.toStringHelper(getClass())
Jonathan Hart41349e92015-02-09 14:14:02 -0800340 .add("type", BgpConstants.Update.AsPath.typeToString(type))
Jonathan Hartab63aac2014-10-16 08:52:55 -0700341 .add("segmentAsNumbers", this.segmentAsNumbers)
342 .toString();
343 }
344 }
345
346 /**
347 * A class to represent AS Path.
348 */
349 public static class AsPath {
350 private final ArrayList<PathSegment> pathSegments;
351 private final int asPathLength; // Precomputed AS Path Length
352
353 /**
354 * Constructor.
355 *
356 * @param pathSegments the Path Segments of the Path
357 */
358 AsPath(ArrayList<PathSegment> pathSegments) {
359 this.pathSegments = checkNotNull(pathSegments);
360
361 //
362 // Precompute the AS Path Length:
363 // - AS_SET counts as 1
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800364 // - AS_SEQUENCE counts how many AS numbers are included
365 // - AS_CONFED_SEQUENCE and AS_CONFED_SET are ignored
Jonathan Hartab63aac2014-10-16 08:52:55 -0700366 //
367 int pl = 0;
368 for (PathSegment pathSegment : pathSegments) {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800369 switch (pathSegment.getType()) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800370 case BgpConstants.Update.AsPath.AS_SET:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800371 pl++; // AS_SET counts as 1
372 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800373 case BgpConstants.Update.AsPath.AS_SEQUENCE:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800374 // Count each AS number
375 pl += pathSegment.getSegmentAsNumbers().size();
376 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800377 case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800378 break; // Ignore
Jonathan Hart41349e92015-02-09 14:14:02 -0800379 case BgpConstants.Update.AsPath.AS_CONFED_SET:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800380 break; // Ignore
381 default:
Jonathan Harte9ef4f32014-12-04 17:49:22 -0800382 // NOTE: What to do if the Path Segment type is unknown?
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800383 break;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700384 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700385 }
386 asPathLength = pl;
387 }
388
389 /**
390 * Gets the AS Path Segments.
391 *
392 * @return the AS Path Segments
393 */
394 public ArrayList<PathSegment> getPathSegments() {
395 return pathSegments;
396 }
397
398 /**
399 * Gets the AS Path Length as considered by the BGP Decision Process.
400 *
401 * @return the AS Path Length as considered by the BGP Decision Process
402 */
403 int getAsPathLength() {
404 return asPathLength;
405 }
406
407 @Override
408 public boolean equals(Object other) {
409 if (this == other) {
410 return true;
411 }
412
413 if (!(other instanceof AsPath)) {
414 return false;
415 }
416
417 AsPath otherAsPath = (AsPath) other;
418 return Objects.equals(this.pathSegments, otherAsPath.pathSegments);
419 }
420
421 @Override
422 public int hashCode() {
HIGUCHI Yutaca9cc8e2015-10-29 23:26:51 -0700423 return pathSegments.hashCode();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700424 }
425
426 @Override
427 public String toString() {
428 return MoreObjects.toStringHelper(getClass())
429 .add("pathSegments", this.pathSegments)
430 .toString();
431 }
432 }
433
434 /**
435 * Compares whether two objects are equal.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700436 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700437 * NOTE: The bgpSession field is excluded from the comparison.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700438 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700439 *
440 * @return true if the two objects are equal, otherwise false.
441 */
442 @Override
443 public boolean equals(Object other) {
444 if (this == other) {
445 return true;
446 }
447
448 //
449 // NOTE: Subclasses are considered as change of identity, hence
450 // equals() will return false if the class type doesn't match.
451 //
452 if (other == null || getClass() != other.getClass()) {
453 return false;
454 }
455
456 if (!super.equals(other)) {
457 return false;
458 }
459
460 // NOTE: The bgpSession field is excluded from the comparison
461 BgpRouteEntry otherRoute = (BgpRouteEntry) other;
462 return (this.origin == otherRoute.origin) &&
463 Objects.equals(this.asPath, otherRoute.asPath) &&
464 (this.localPref == otherRoute.localPref) &&
465 (this.multiExitDisc == otherRoute.multiExitDisc);
466 }
467
468 /**
469 * Computes the hash code.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700470 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700471 * NOTE: We return the base class hash code to avoid expensive computation
Thomas Vachuska4b420772014-10-30 16:46:17 -0700472 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700473 *
474 * @return the object hash code
475 */
476 @Override
477 public int hashCode() {
478 return super.hashCode();
479 }
480
481 @Override
482 public String toString() {
483 return MoreObjects.toStringHelper(getClass())
484 .add("prefix", prefix())
485 .add("nextHop", nextHop())
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800486 .add("bgpId", bgpSession.remoteInfo().bgpId())
Jonathan Hart41349e92015-02-09 14:14:02 -0800487 .add("origin", BgpConstants.Update.Origin.typeToString(origin))
Jonathan Hartab63aac2014-10-16 08:52:55 -0700488 .add("asPath", asPath)
489 .add("localPref", localPref)
490 .add("multiExitDisc", multiExitDisc)
491 .toString();
492 }
493}