blob: 24d1a8da737053e02e740e83871ec41f9fab89ce [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 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 Hart2da1e602015-02-18 19:09:24 -080022import org.onosproject.routing.RouteEntry;
Jonathan Hartab63aac2014-10-16 08:52:55 -070023
24import java.util.ArrayList;
25import java.util.Objects;
26
Jonathan Hart41349e92015-02-09 14:14:02 -080027import static com.google.common.base.Preconditions.checkNotNull;
Jonathan Hartab63aac2014-10-16 08:52:55 -070028
29/**
30 * Represents a route in BGP.
31 */
32public class BgpRouteEntry extends RouteEntry {
33 private final BgpSession bgpSession; // The BGP Session the route was
34 // received on
35 private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
36 private final AsPath asPath; // The AS Path
37 private final long localPref; // The local preference for the route
Jonathan Hart41349e92015-02-09 14:14:02 -080038 private long multiExitDisc = BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
Jonathan Hartab63aac2014-10-16 08:52:55 -070039
40 /**
41 * Class constructor.
42 *
43 * @param bgpSession the BGP Session the route was received on
44 * @param prefix the prefix of the route
45 * @param nextHop the next hop of the route
46 * @param origin the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
47 * @param asPath the AS path
48 * @param localPref the route local preference
49 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080050 public BgpRouteEntry(BgpSession bgpSession, IpPrefix prefix,
51 IpAddress nextHop, byte origin,
Jonathan Hartab63aac2014-10-16 08:52:55 -070052 BgpRouteEntry.AsPath asPath, long localPref) {
53 super(prefix, nextHop);
54 this.bgpSession = checkNotNull(bgpSession);
55 this.origin = origin;
56 this.asPath = checkNotNull(asPath);
57 this.localPref = localPref;
58 }
59
60 /**
61 * Gets the BGP Session the route was received on.
62 *
63 * @return the BGP Session the route was received on
64 */
65 public BgpSession getBgpSession() {
66 return bgpSession;
67 }
68
69 /**
70 * Gets the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE.
71 *
72 * @return the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
73 */
74 public byte getOrigin() {
75 return origin;
76 }
77
78 /**
79 * Gets the route AS path.
80 *
81 * @return the route AS path
82 */
83 public BgpRouteEntry.AsPath getAsPath() {
84 return asPath;
85 }
86
87 /**
88 * Gets the route local preference.
89 *
90 * @return the route local preference
91 */
92 public long getLocalPref() {
93 return localPref;
94 }
95
96 /**
97 * Gets the route MED (Multi-Exit Discriminator).
98 *
99 * @return the route MED (Multi-Exit Discriminator)
100 */
101 public long getMultiExitDisc() {
102 return multiExitDisc;
103 }
104
105 /**
106 * Sets the route MED (Multi-Exit Discriminator).
107 *
108 * @param multiExitDisc the route MED (Multi-Exit Discriminator) to set
109 */
110 void setMultiExitDisc(long multiExitDisc) {
111 this.multiExitDisc = multiExitDisc;
112 }
113
114 /**
115 * Tests whether the route is originated from the local AS.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700116 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700117 * The route is considered originated from the local AS if the AS Path
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800118 * is empty or if it begins with an AS_SET (after skipping
119 * AS_CONFED_SEQUENCE and AS_CONFED_SET).
Thomas Vachuska4b420772014-10-30 16:46:17 -0700120 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700121 *
122 * @return true if the route is originated from the local AS, otherwise
123 * false
124 */
125 boolean isLocalRoute() {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800126 PathSegment firstPathSegment = null;
127
128 // Find the first Path Segment by ignoring the AS_CONFED_* segments
129 for (PathSegment pathSegment : asPath.getPathSegments()) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800130 if ((pathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) ||
131 (pathSegment.getType() == BgpConstants.Update.AsPath.AS_SEQUENCE)) {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800132 firstPathSegment = pathSegment;
133 break;
134 }
135 }
136 if (firstPathSegment == null) {
137 return true; // Local route: no path segments
138 }
139 // If the first path segment is AS_SET, the route is considered local
Jonathan Hart41349e92015-02-09 14:14:02 -0800140 if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700141 return true;
142 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800143
144 return false; // The route is not local
Jonathan Hartab63aac2014-10-16 08:52:55 -0700145 }
146
147 /**
148 * Gets the BGP Neighbor AS number the route was received from.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700149 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700150 * If the router is originated from the local AS, the return value is
151 * zero (BGP_AS_0).
Thomas Vachuska4b420772014-10-30 16:46:17 -0700152 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700153 *
154 * @return the BGP Neighbor AS number the route was received from.
155 */
156 long getNeighborAs() {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800157 PathSegment firstPathSegment = null;
158
Jonathan Hartab63aac2014-10-16 08:52:55 -0700159 if (isLocalRoute()) {
160 return BgpConstants.BGP_AS_0;
161 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800162
163 // Find the first Path Segment by ignoring the AS_CONFED_* segments
164 for (PathSegment pathSegment : asPath.getPathSegments()) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800165 if ((pathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) ||
166 (pathSegment.getType() == BgpConstants.Update.AsPath.AS_SEQUENCE)) {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800167 firstPathSegment = pathSegment;
168 break;
169 }
170 }
171 if (firstPathSegment == null) {
172 // NOTE: Shouldn't happen - should be captured by isLocalRoute()
173 return BgpConstants.BGP_AS_0;
174 }
175
Jonathan Hartab63aac2014-10-16 08:52:55 -0700176 if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
Jonathan Harte9ef4f32014-12-04 17:49:22 -0800177 // NOTE: Shouldn't happen. Should check during the parsing.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700178 return BgpConstants.BGP_AS_0;
179 }
180 return firstPathSegment.getSegmentAsNumbers().get(0);
181 }
182
183 /**
184 * Tests whether the AS Path contains a loop.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700185 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700186 * The test is done by comparing whether the AS Path contains the
187 * local AS number.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700188 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700189 *
190 * @param localAsNumber the local AS number to compare against
191 * @return true if the AS Path contains a loop, otherwise false
192 */
193 boolean hasAsPathLoop(long localAsNumber) {
194 for (PathSegment pathSegment : asPath.getPathSegments()) {
195 for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
196 if (asNumber.equals(localAsNumber)) {
197 return true;
198 }
199 }
200 }
201 return false;
202 }
203
204 /**
205 * Compares this BGP route against another BGP route by using the
206 * BGP Decision Process.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700207 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700208 * NOTE: The comparison needs to be performed only on routes that have
209 * same IP Prefix.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700210 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700211 *
212 * @param other the BGP route to compare against
213 * @return true if this BGP route is better than the other BGP route
214 * or same, otherwise false
215 */
216 boolean isBetterThan(BgpRouteEntry other) {
217 if (this == other) {
218 return true; // Return true if same route
219 }
220
221 // Compare the LOCAL_PREF values: larger is better
222 if (getLocalPref() != other.getLocalPref()) {
223 return (getLocalPref() > other.getLocalPref());
224 }
225
226 // Compare the AS number in the path: smaller is better
227 if (getAsPath().getAsPathLength() !=
228 other.getAsPath().getAsPathLength()) {
229 return getAsPath().getAsPathLength() <
230 other.getAsPath().getAsPathLength();
231 }
232
233 // Compare the Origin number: lower is better
234 if (getOrigin() != other.getOrigin()) {
235 return (getOrigin() < other.getOrigin());
236 }
237
238 // Compare the MED if the neighbor AS is same: larger is better
239 medLabel: {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800240 if (isLocalRoute() || other.isLocalRoute()) {
241 // Compare MEDs for non-local routes only
242 break medLabel;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700243 }
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800244 long thisNeighborAs = getNeighborAs();
245 if (thisNeighborAs != other.getNeighborAs()) {
246 break medLabel; // AS number is different
247 }
248 if (thisNeighborAs == BgpConstants.BGP_AS_0) {
249 break medLabel; // Invalid AS number
Jonathan Hartab63aac2014-10-16 08:52:55 -0700250 }
251
252 // Compare the MED
253 if (getMultiExitDisc() != other.getMultiExitDisc()) {
254 return (getMultiExitDisc() > other.getMultiExitDisc());
255 }
256 }
257
258 // Compare the peer BGP ID: lower is better
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800259 Ip4Address peerBgpId = getBgpSession().remoteInfo().bgpId();
260 Ip4Address otherPeerBgpId = other.getBgpSession().remoteInfo().bgpId();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700261 if (!peerBgpId.equals(otherPeerBgpId)) {
262 return (peerBgpId.compareTo(otherPeerBgpId) < 0);
263 }
264
265 // Compare the peer BGP address: lower is better
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800266 Ip4Address peerAddress = getBgpSession().remoteInfo().ip4Address();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800267 Ip4Address otherPeerAddress =
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800268 other.getBgpSession().remoteInfo().ip4Address();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700269 if (!peerAddress.equals(otherPeerAddress)) {
270 return (peerAddress.compareTo(otherPeerAddress) < 0);
271 }
272
273 return true; // Routes are same. Shouldn't happen?
274 }
275
276 /**
277 * A class to represent AS Path Segment.
278 */
279 public static class PathSegment {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800280 // Segment type: AS_SET(1), AS_SEQUENCE(2), AS_CONFED_SEQUENCE(3),
281 // AS_CONFED_SET(4)
282 private final byte type;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700283 private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
284
285 /**
286 * Constructor.
287 *
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800288 * @param type the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
289 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
Jonathan Hartab63aac2014-10-16 08:52:55 -0700290 * @param segmentAsNumbers the Segment AS numbers
291 */
292 PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
293 this.type = type;
294 this.segmentAsNumbers = checkNotNull(segmentAsNumbers);
295 }
296
297 /**
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800298 * Gets the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
299 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4).
Jonathan Hartab63aac2014-10-16 08:52:55 -0700300 *
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800301 * @return the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
302 * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
Jonathan Hartab63aac2014-10-16 08:52:55 -0700303 */
304 public byte getType() {
305 return type;
306 }
307
308 /**
309 * Gets the Path Segment AS Numbers.
310 *
311 * @return the Path Segment AS Numbers
312 */
313 public ArrayList<Long> getSegmentAsNumbers() {
314 return segmentAsNumbers;
315 }
316
317 @Override
318 public boolean equals(Object other) {
319 if (this == other) {
320 return true;
321 }
322
323 if (!(other instanceof PathSegment)) {
324 return false;
325 }
326
327 PathSegment otherPathSegment = (PathSegment) other;
328 return Objects.equals(this.type, otherPathSegment.type) &&
329 Objects.equals(this.segmentAsNumbers,
330 otherPathSegment.segmentAsNumbers);
331 }
332
333 @Override
334 public int hashCode() {
335 return Objects.hash(type, segmentAsNumbers);
336 }
337
338 @Override
339 public String toString() {
340 return MoreObjects.toStringHelper(getClass())
Jonathan Hart41349e92015-02-09 14:14:02 -0800341 .add("type", BgpConstants.Update.AsPath.typeToString(type))
Jonathan Hartab63aac2014-10-16 08:52:55 -0700342 .add("segmentAsNumbers", this.segmentAsNumbers)
343 .toString();
344 }
345 }
346
347 /**
348 * A class to represent AS Path.
349 */
350 public static class AsPath {
351 private final ArrayList<PathSegment> pathSegments;
352 private final int asPathLength; // Precomputed AS Path Length
353
354 /**
355 * Constructor.
356 *
357 * @param pathSegments the Path Segments of the Path
358 */
359 AsPath(ArrayList<PathSegment> pathSegments) {
360 this.pathSegments = checkNotNull(pathSegments);
361
362 //
363 // Precompute the AS Path Length:
364 // - AS_SET counts as 1
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800365 // - AS_SEQUENCE counts how many AS numbers are included
366 // - AS_CONFED_SEQUENCE and AS_CONFED_SET are ignored
Jonathan Hartab63aac2014-10-16 08:52:55 -0700367 //
368 int pl = 0;
369 for (PathSegment pathSegment : pathSegments) {
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800370 switch (pathSegment.getType()) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800371 case BgpConstants.Update.AsPath.AS_SET:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800372 pl++; // AS_SET counts as 1
373 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800374 case BgpConstants.Update.AsPath.AS_SEQUENCE:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800375 // Count each AS number
376 pl += pathSegment.getSegmentAsNumbers().size();
377 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800378 case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800379 break; // Ignore
Jonathan Hart41349e92015-02-09 14:14:02 -0800380 case BgpConstants.Update.AsPath.AS_CONFED_SET:
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800381 break; // Ignore
382 default:
Jonathan Harte9ef4f32014-12-04 17:49:22 -0800383 // NOTE: What to do if the Path Segment type is unknown?
Pavlin Radoslavov49eb64d2014-11-10 17:03:19 -0800384 break;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700385 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700386 }
387 asPathLength = pl;
388 }
389
390 /**
391 * Gets the AS Path Segments.
392 *
393 * @return the AS Path Segments
394 */
395 public ArrayList<PathSegment> getPathSegments() {
396 return pathSegments;
397 }
398
399 /**
400 * Gets the AS Path Length as considered by the BGP Decision Process.
401 *
402 * @return the AS Path Length as considered by the BGP Decision Process
403 */
404 int getAsPathLength() {
405 return asPathLength;
406 }
407
408 @Override
409 public boolean equals(Object other) {
410 if (this == other) {
411 return true;
412 }
413
414 if (!(other instanceof AsPath)) {
415 return false;
416 }
417
418 AsPath otherAsPath = (AsPath) other;
419 return Objects.equals(this.pathSegments, otherAsPath.pathSegments);
420 }
421
422 @Override
423 public int hashCode() {
HIGUCHI Yutaca9cc8e2015-10-29 23:26:51 -0700424 return pathSegments.hashCode();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700425 }
426
427 @Override
428 public String toString() {
429 return MoreObjects.toStringHelper(getClass())
430 .add("pathSegments", this.pathSegments)
431 .toString();
432 }
433 }
434
435 /**
436 * Compares whether two objects are equal.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700437 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700438 * NOTE: The bgpSession field is excluded from the comparison.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700439 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700440 *
441 * @return true if the two objects are equal, otherwise false.
442 */
443 @Override
444 public boolean equals(Object other) {
445 if (this == other) {
446 return true;
447 }
448
449 //
450 // NOTE: Subclasses are considered as change of identity, hence
451 // equals() will return false if the class type doesn't match.
452 //
453 if (other == null || getClass() != other.getClass()) {
454 return false;
455 }
456
457 if (!super.equals(other)) {
458 return false;
459 }
460
461 // NOTE: The bgpSession field is excluded from the comparison
462 BgpRouteEntry otherRoute = (BgpRouteEntry) other;
463 return (this.origin == otherRoute.origin) &&
464 Objects.equals(this.asPath, otherRoute.asPath) &&
465 (this.localPref == otherRoute.localPref) &&
466 (this.multiExitDisc == otherRoute.multiExitDisc);
467 }
468
469 /**
470 * Computes the hash code.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700471 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700472 * NOTE: We return the base class hash code to avoid expensive computation
Thomas Vachuska4b420772014-10-30 16:46:17 -0700473 * </p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700474 *
475 * @return the object hash code
476 */
477 @Override
478 public int hashCode() {
479 return super.hashCode();
480 }
481
482 @Override
483 public String toString() {
484 return MoreObjects.toStringHelper(getClass())
485 .add("prefix", prefix())
486 .add("nextHop", nextHop())
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800487 .add("bgpId", bgpSession.remoteInfo().bgpId())
Jonathan Hart41349e92015-02-09 14:14:02 -0800488 .add("origin", BgpConstants.Update.Origin.typeToString(origin))
Jonathan Hartab63aac2014-10-16 08:52:55 -0700489 .add("asPath", asPath)
490 .add("localPref", localPref)
491 .add("multiExitDisc", multiExitDisc)
492 .toString();
493 }
494}