blob: 21e8a88ee647b3790163065636577a9b44790cd1 [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 */
Jonathan Hartab63aac2014-10-16 08:52:55 -070016package org.onlab.onos.sdnip.bgp;
17
18import static com.google.common.base.Preconditions.checkNotNull;
19
20import java.util.ArrayList;
21import java.util.Objects;
22
23import org.onlab.onos.sdnip.RouteEntry;
24import org.onlab.packet.IpAddress;
25import org.onlab.packet.IpPrefix;
26
27import com.google.common.base.MoreObjects;
28
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
38 private long multiExitDisc =
39 BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
40
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 */
51 public BgpRouteEntry(BgpSession bgpSession, IpPrefix prefix,
52 IpAddress nextHop, byte origin,
53 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.
117 * <p/>
118 * The route is considered originated from the local AS if the AS Path
119 * is empty or if it begins with an AS_SET.
120 *
121 * @return true if the route is originated from the local AS, otherwise
122 * false
123 */
124 boolean isLocalRoute() {
125 if (asPath.getPathSegments().isEmpty()) {
126 return true;
127 }
128 PathSegment firstPathSegment = asPath.getPathSegments().get(0);
129 if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
130 return true;
131 }
132 return false;
133 }
134
135 /**
136 * Gets the BGP Neighbor AS number the route was received from.
137 * <p/>
138 * If the router is originated from the local AS, the return value is
139 * zero (BGP_AS_0).
140 *
141 * @return the BGP Neighbor AS number the route was received from.
142 */
143 long getNeighborAs() {
144 if (isLocalRoute()) {
145 return BgpConstants.BGP_AS_0;
146 }
147 PathSegment firstPathSegment = asPath.getPathSegments().get(0);
148 if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
149 // TODO: Shouldn't happen. Should check during the parsing.
150 return BgpConstants.BGP_AS_0;
151 }
152 return firstPathSegment.getSegmentAsNumbers().get(0);
153 }
154
155 /**
156 * Tests whether the AS Path contains a loop.
157 * <p/>
158 * The test is done by comparing whether the AS Path contains the
159 * local AS number.
160 *
161 * @param localAsNumber the local AS number to compare against
162 * @return true if the AS Path contains a loop, otherwise false
163 */
164 boolean hasAsPathLoop(long localAsNumber) {
165 for (PathSegment pathSegment : asPath.getPathSegments()) {
166 for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
167 if (asNumber.equals(localAsNumber)) {
168 return true;
169 }
170 }
171 }
172 return false;
173 }
174
175 /**
176 * Compares this BGP route against another BGP route by using the
177 * BGP Decision Process.
178 * <p/>
179 * NOTE: The comparison needs to be performed only on routes that have
180 * same IP Prefix.
181 *
182 * @param other the BGP route to compare against
183 * @return true if this BGP route is better than the other BGP route
184 * or same, otherwise false
185 */
186 boolean isBetterThan(BgpRouteEntry other) {
187 if (this == other) {
188 return true; // Return true if same route
189 }
190
191 // Compare the LOCAL_PREF values: larger is better
192 if (getLocalPref() != other.getLocalPref()) {
193 return (getLocalPref() > other.getLocalPref());
194 }
195
196 // Compare the AS number in the path: smaller is better
197 if (getAsPath().getAsPathLength() !=
198 other.getAsPath().getAsPathLength()) {
199 return getAsPath().getAsPathLength() <
200 other.getAsPath().getAsPathLength();
201 }
202
203 // Compare the Origin number: lower is better
204 if (getOrigin() != other.getOrigin()) {
205 return (getOrigin() < other.getOrigin());
206 }
207
208 // Compare the MED if the neighbor AS is same: larger is better
209 medLabel: {
210 boolean thisIsLocalRoute = isLocalRoute();
211 if (thisIsLocalRoute != other.isLocalRoute()) {
212 break medLabel; // AS number is different
213 }
214 if (!thisIsLocalRoute) {
215 long thisNeighborAs = getNeighborAs();
216 if (thisNeighborAs != other.getNeighborAs()) {
217 break medLabel; // AS number is different
218 }
219 if (thisNeighborAs == BgpConstants.BGP_AS_0) {
220 break medLabel; // Invalid AS number
221 }
222 }
223
224 // Compare the MED
225 if (getMultiExitDisc() != other.getMultiExitDisc()) {
226 return (getMultiExitDisc() > other.getMultiExitDisc());
227 }
228 }
229
230 // Compare the peer BGP ID: lower is better
231 IpAddress peerBgpId = getBgpSession().getRemoteBgpId();
232 IpAddress otherPeerBgpId = other.getBgpSession().getRemoteBgpId();
233 if (!peerBgpId.equals(otherPeerBgpId)) {
234 return (peerBgpId.compareTo(otherPeerBgpId) < 0);
235 }
236
237 // Compare the peer BGP address: lower is better
238 IpAddress peerAddress = getBgpSession().getRemoteIp4Address();
239 IpAddress otherPeerAddress =
240 other.getBgpSession().getRemoteIp4Address();
241 if (!peerAddress.equals(otherPeerAddress)) {
242 return (peerAddress.compareTo(otherPeerAddress) < 0);
243 }
244
245 return true; // Routes are same. Shouldn't happen?
246 }
247
248 /**
249 * A class to represent AS Path Segment.
250 */
251 public static class PathSegment {
252 private final byte type; // Segment type: AS_SET, AS_SEQUENCE
253 private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
254
255 /**
256 * Constructor.
257 *
258 * @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
259 * @param segmentAsNumbers the Segment AS numbers
260 */
261 PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
262 this.type = type;
263 this.segmentAsNumbers = checkNotNull(segmentAsNumbers);
264 }
265
266 /**
267 * Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
268 *
269 * @return the Path Segment Type: AS_SET, AS_SEQUENCE
270 */
271 public byte getType() {
272 return type;
273 }
274
275 /**
276 * Gets the Path Segment AS Numbers.
277 *
278 * @return the Path Segment AS Numbers
279 */
280 public ArrayList<Long> getSegmentAsNumbers() {
281 return segmentAsNumbers;
282 }
283
284 @Override
285 public boolean equals(Object other) {
286 if (this == other) {
287 return true;
288 }
289
290 if (!(other instanceof PathSegment)) {
291 return false;
292 }
293
294 PathSegment otherPathSegment = (PathSegment) other;
295 return Objects.equals(this.type, otherPathSegment.type) &&
296 Objects.equals(this.segmentAsNumbers,
297 otherPathSegment.segmentAsNumbers);
298 }
299
300 @Override
301 public int hashCode() {
302 return Objects.hash(type, segmentAsNumbers);
303 }
304
305 @Override
306 public String toString() {
307 return MoreObjects.toStringHelper(getClass())
308 .add("type", this.type)
309 .add("segmentAsNumbers", this.segmentAsNumbers)
310 .toString();
311 }
312 }
313
314 /**
315 * A class to represent AS Path.
316 */
317 public static class AsPath {
318 private final ArrayList<PathSegment> pathSegments;
319 private final int asPathLength; // Precomputed AS Path Length
320
321 /**
322 * Constructor.
323 *
324 * @param pathSegments the Path Segments of the Path
325 */
326 AsPath(ArrayList<PathSegment> pathSegments) {
327 this.pathSegments = checkNotNull(pathSegments);
328
329 //
330 // Precompute the AS Path Length:
331 // - AS_SET counts as 1
332 //
333 int pl = 0;
334 for (PathSegment pathSegment : pathSegments) {
335 if (pathSegment.getType() ==
336 BgpConstants.Update.AsPath.AS_SET) {
337 pl++;
338 continue;
339 }
340 pl += pathSegment.getSegmentAsNumbers().size();
341 }
342 asPathLength = pl;
343 }
344
345 /**
346 * Gets the AS Path Segments.
347 *
348 * @return the AS Path Segments
349 */
350 public ArrayList<PathSegment> getPathSegments() {
351 return pathSegments;
352 }
353
354 /**
355 * Gets the AS Path Length as considered by the BGP Decision Process.
356 *
357 * @return the AS Path Length as considered by the BGP Decision Process
358 */
359 int getAsPathLength() {
360 return asPathLength;
361 }
362
363 @Override
364 public boolean equals(Object other) {
365 if (this == other) {
366 return true;
367 }
368
369 if (!(other instanceof AsPath)) {
370 return false;
371 }
372
373 AsPath otherAsPath = (AsPath) other;
374 return Objects.equals(this.pathSegments, otherAsPath.pathSegments);
375 }
376
377 @Override
378 public int hashCode() {
379 return Objects.hash(pathSegments);
380 }
381
382 @Override
383 public String toString() {
384 return MoreObjects.toStringHelper(getClass())
385 .add("pathSegments", this.pathSegments)
386 .toString();
387 }
388 }
389
390 /**
391 * Compares whether two objects are equal.
392 * <p/>
393 * NOTE: The bgpSession field is excluded from the comparison.
394 *
395 * @return true if the two objects are equal, otherwise false.
396 */
397 @Override
398 public boolean equals(Object other) {
399 if (this == other) {
400 return true;
401 }
402
403 //
404 // NOTE: Subclasses are considered as change of identity, hence
405 // equals() will return false if the class type doesn't match.
406 //
407 if (other == null || getClass() != other.getClass()) {
408 return false;
409 }
410
411 if (!super.equals(other)) {
412 return false;
413 }
414
415 // NOTE: The bgpSession field is excluded from the comparison
416 BgpRouteEntry otherRoute = (BgpRouteEntry) other;
417 return (this.origin == otherRoute.origin) &&
418 Objects.equals(this.asPath, otherRoute.asPath) &&
419 (this.localPref == otherRoute.localPref) &&
420 (this.multiExitDisc == otherRoute.multiExitDisc);
421 }
422
423 /**
424 * Computes the hash code.
425 * <p/>
426 * NOTE: We return the base class hash code to avoid expensive computation
427 *
428 * @return the object hash code
429 */
430 @Override
431 public int hashCode() {
432 return super.hashCode();
433 }
434
435 @Override
436 public String toString() {
437 return MoreObjects.toStringHelper(getClass())
438 .add("prefix", prefix())
439 .add("nextHop", nextHop())
440 .add("bgpId", bgpSession.getRemoteBgpId())
441 .add("origin", origin)
442 .add("asPath", asPath)
443 .add("localPref", localPref)
444 .add("multiExitDisc", multiExitDisc)
445 .toString();
446 }
447}