blob: 133d5c9af9b2b87213bd3f265638b678737c55e4 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
Jonathan Hartab63aac2014-10-16 08:52:55 -070019package org.onlab.onos.sdnip.bgp;
20
21import static com.google.common.base.Preconditions.checkNotNull;
22
23import java.util.ArrayList;
24import java.util.Objects;
25
26import org.onlab.onos.sdnip.RouteEntry;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
29
30import com.google.common.base.MoreObjects;
31
32/**
33 * Represents a route in BGP.
34 */
35public class BgpRouteEntry extends RouteEntry {
36 private final BgpSession bgpSession; // The BGP Session the route was
37 // received on
38 private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
39 private final AsPath asPath; // The AS Path
40 private final long localPref; // The local preference for the route
41 private long multiExitDisc =
42 BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
43
44 /**
45 * Class constructor.
46 *
47 * @param bgpSession the BGP Session the route was received on
48 * @param prefix the prefix of the route
49 * @param nextHop the next hop of the route
50 * @param origin the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
51 * @param asPath the AS path
52 * @param localPref the route local preference
53 */
54 public BgpRouteEntry(BgpSession bgpSession, IpPrefix prefix,
55 IpAddress nextHop, byte origin,
56 BgpRouteEntry.AsPath asPath, long localPref) {
57 super(prefix, nextHop);
58 this.bgpSession = checkNotNull(bgpSession);
59 this.origin = origin;
60 this.asPath = checkNotNull(asPath);
61 this.localPref = localPref;
62 }
63
64 /**
65 * Gets the BGP Session the route was received on.
66 *
67 * @return the BGP Session the route was received on
68 */
69 public BgpSession getBgpSession() {
70 return bgpSession;
71 }
72
73 /**
74 * Gets the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE.
75 *
76 * @return the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
77 */
78 public byte getOrigin() {
79 return origin;
80 }
81
82 /**
83 * Gets the route AS path.
84 *
85 * @return the route AS path
86 */
87 public BgpRouteEntry.AsPath getAsPath() {
88 return asPath;
89 }
90
91 /**
92 * Gets the route local preference.
93 *
94 * @return the route local preference
95 */
96 public long getLocalPref() {
97 return localPref;
98 }
99
100 /**
101 * Gets the route MED (Multi-Exit Discriminator).
102 *
103 * @return the route MED (Multi-Exit Discriminator)
104 */
105 public long getMultiExitDisc() {
106 return multiExitDisc;
107 }
108
109 /**
110 * Sets the route MED (Multi-Exit Discriminator).
111 *
112 * @param multiExitDisc the route MED (Multi-Exit Discriminator) to set
113 */
114 void setMultiExitDisc(long multiExitDisc) {
115 this.multiExitDisc = multiExitDisc;
116 }
117
118 /**
119 * Tests whether the route is originated from the local AS.
120 * <p/>
121 * The route is considered originated from the local AS if the AS Path
122 * is empty or if it begins with an AS_SET.
123 *
124 * @return true if the route is originated from the local AS, otherwise
125 * false
126 */
127 boolean isLocalRoute() {
128 if (asPath.getPathSegments().isEmpty()) {
129 return true;
130 }
131 PathSegment firstPathSegment = asPath.getPathSegments().get(0);
132 if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
133 return true;
134 }
135 return false;
136 }
137
138 /**
139 * Gets the BGP Neighbor AS number the route was received from.
140 * <p/>
141 * If the router is originated from the local AS, the return value is
142 * zero (BGP_AS_0).
143 *
144 * @return the BGP Neighbor AS number the route was received from.
145 */
146 long getNeighborAs() {
147 if (isLocalRoute()) {
148 return BgpConstants.BGP_AS_0;
149 }
150 PathSegment firstPathSegment = asPath.getPathSegments().get(0);
151 if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
152 // TODO: Shouldn't happen. Should check during the parsing.
153 return BgpConstants.BGP_AS_0;
154 }
155 return firstPathSegment.getSegmentAsNumbers().get(0);
156 }
157
158 /**
159 * Tests whether the AS Path contains a loop.
160 * <p/>
161 * The test is done by comparing whether the AS Path contains the
162 * local AS number.
163 *
164 * @param localAsNumber the local AS number to compare against
165 * @return true if the AS Path contains a loop, otherwise false
166 */
167 boolean hasAsPathLoop(long localAsNumber) {
168 for (PathSegment pathSegment : asPath.getPathSegments()) {
169 for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
170 if (asNumber.equals(localAsNumber)) {
171 return true;
172 }
173 }
174 }
175 return false;
176 }
177
178 /**
179 * Compares this BGP route against another BGP route by using the
180 * BGP Decision Process.
181 * <p/>
182 * NOTE: The comparison needs to be performed only on routes that have
183 * same IP Prefix.
184 *
185 * @param other the BGP route to compare against
186 * @return true if this BGP route is better than the other BGP route
187 * or same, otherwise false
188 */
189 boolean isBetterThan(BgpRouteEntry other) {
190 if (this == other) {
191 return true; // Return true if same route
192 }
193
194 // Compare the LOCAL_PREF values: larger is better
195 if (getLocalPref() != other.getLocalPref()) {
196 return (getLocalPref() > other.getLocalPref());
197 }
198
199 // Compare the AS number in the path: smaller is better
200 if (getAsPath().getAsPathLength() !=
201 other.getAsPath().getAsPathLength()) {
202 return getAsPath().getAsPathLength() <
203 other.getAsPath().getAsPathLength();
204 }
205
206 // Compare the Origin number: lower is better
207 if (getOrigin() != other.getOrigin()) {
208 return (getOrigin() < other.getOrigin());
209 }
210
211 // Compare the MED if the neighbor AS is same: larger is better
212 medLabel: {
213 boolean thisIsLocalRoute = isLocalRoute();
214 if (thisIsLocalRoute != other.isLocalRoute()) {
215 break medLabel; // AS number is different
216 }
217 if (!thisIsLocalRoute) {
218 long thisNeighborAs = getNeighborAs();
219 if (thisNeighborAs != other.getNeighborAs()) {
220 break medLabel; // AS number is different
221 }
222 if (thisNeighborAs == BgpConstants.BGP_AS_0) {
223 break medLabel; // Invalid AS number
224 }
225 }
226
227 // Compare the MED
228 if (getMultiExitDisc() != other.getMultiExitDisc()) {
229 return (getMultiExitDisc() > other.getMultiExitDisc());
230 }
231 }
232
233 // Compare the peer BGP ID: lower is better
234 IpAddress peerBgpId = getBgpSession().getRemoteBgpId();
235 IpAddress otherPeerBgpId = other.getBgpSession().getRemoteBgpId();
236 if (!peerBgpId.equals(otherPeerBgpId)) {
237 return (peerBgpId.compareTo(otherPeerBgpId) < 0);
238 }
239
240 // Compare the peer BGP address: lower is better
241 IpAddress peerAddress = getBgpSession().getRemoteIp4Address();
242 IpAddress otherPeerAddress =
243 other.getBgpSession().getRemoteIp4Address();
244 if (!peerAddress.equals(otherPeerAddress)) {
245 return (peerAddress.compareTo(otherPeerAddress) < 0);
246 }
247
248 return true; // Routes are same. Shouldn't happen?
249 }
250
251 /**
252 * A class to represent AS Path Segment.
253 */
254 public static class PathSegment {
255 private final byte type; // Segment type: AS_SET, AS_SEQUENCE
256 private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
257
258 /**
259 * Constructor.
260 *
261 * @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
262 * @param segmentAsNumbers the Segment AS numbers
263 */
264 PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
265 this.type = type;
266 this.segmentAsNumbers = checkNotNull(segmentAsNumbers);
267 }
268
269 /**
270 * Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
271 *
272 * @return the Path Segment Type: AS_SET, AS_SEQUENCE
273 */
274 public byte getType() {
275 return type;
276 }
277
278 /**
279 * Gets the Path Segment AS Numbers.
280 *
281 * @return the Path Segment AS Numbers
282 */
283 public ArrayList<Long> getSegmentAsNumbers() {
284 return segmentAsNumbers;
285 }
286
287 @Override
288 public boolean equals(Object other) {
289 if (this == other) {
290 return true;
291 }
292
293 if (!(other instanceof PathSegment)) {
294 return false;
295 }
296
297 PathSegment otherPathSegment = (PathSegment) other;
298 return Objects.equals(this.type, otherPathSegment.type) &&
299 Objects.equals(this.segmentAsNumbers,
300 otherPathSegment.segmentAsNumbers);
301 }
302
303 @Override
304 public int hashCode() {
305 return Objects.hash(type, segmentAsNumbers);
306 }
307
308 @Override
309 public String toString() {
310 return MoreObjects.toStringHelper(getClass())
311 .add("type", this.type)
312 .add("segmentAsNumbers", this.segmentAsNumbers)
313 .toString();
314 }
315 }
316
317 /**
318 * A class to represent AS Path.
319 */
320 public static class AsPath {
321 private final ArrayList<PathSegment> pathSegments;
322 private final int asPathLength; // Precomputed AS Path Length
323
324 /**
325 * Constructor.
326 *
327 * @param pathSegments the Path Segments of the Path
328 */
329 AsPath(ArrayList<PathSegment> pathSegments) {
330 this.pathSegments = checkNotNull(pathSegments);
331
332 //
333 // Precompute the AS Path Length:
334 // - AS_SET counts as 1
335 //
336 int pl = 0;
337 for (PathSegment pathSegment : pathSegments) {
338 if (pathSegment.getType() ==
339 BgpConstants.Update.AsPath.AS_SET) {
340 pl++;
341 continue;
342 }
343 pl += pathSegment.getSegmentAsNumbers().size();
344 }
345 asPathLength = pl;
346 }
347
348 /**
349 * Gets the AS Path Segments.
350 *
351 * @return the AS Path Segments
352 */
353 public ArrayList<PathSegment> getPathSegments() {
354 return pathSegments;
355 }
356
357 /**
358 * Gets the AS Path Length as considered by the BGP Decision Process.
359 *
360 * @return the AS Path Length as considered by the BGP Decision Process
361 */
362 int getAsPathLength() {
363 return asPathLength;
364 }
365
366 @Override
367 public boolean equals(Object other) {
368 if (this == other) {
369 return true;
370 }
371
372 if (!(other instanceof AsPath)) {
373 return false;
374 }
375
376 AsPath otherAsPath = (AsPath) other;
377 return Objects.equals(this.pathSegments, otherAsPath.pathSegments);
378 }
379
380 @Override
381 public int hashCode() {
382 return Objects.hash(pathSegments);
383 }
384
385 @Override
386 public String toString() {
387 return MoreObjects.toStringHelper(getClass())
388 .add("pathSegments", this.pathSegments)
389 .toString();
390 }
391 }
392
393 /**
394 * Compares whether two objects are equal.
395 * <p/>
396 * NOTE: The bgpSession field is excluded from the comparison.
397 *
398 * @return true if the two objects are equal, otherwise false.
399 */
400 @Override
401 public boolean equals(Object other) {
402 if (this == other) {
403 return true;
404 }
405
406 //
407 // NOTE: Subclasses are considered as change of identity, hence
408 // equals() will return false if the class type doesn't match.
409 //
410 if (other == null || getClass() != other.getClass()) {
411 return false;
412 }
413
414 if (!super.equals(other)) {
415 return false;
416 }
417
418 // NOTE: The bgpSession field is excluded from the comparison
419 BgpRouteEntry otherRoute = (BgpRouteEntry) other;
420 return (this.origin == otherRoute.origin) &&
421 Objects.equals(this.asPath, otherRoute.asPath) &&
422 (this.localPref == otherRoute.localPref) &&
423 (this.multiExitDisc == otherRoute.multiExitDisc);
424 }
425
426 /**
427 * Computes the hash code.
428 * <p/>
429 * NOTE: We return the base class hash code to avoid expensive computation
430 *
431 * @return the object hash code
432 */
433 @Override
434 public int hashCode() {
435 return super.hashCode();
436 }
437
438 @Override
439 public String toString() {
440 return MoreObjects.toStringHelper(getClass())
441 .add("prefix", prefix())
442 .add("nextHop", nextHop())
443 .add("bgpId", bgpSession.getRemoteBgpId())
444 .add("origin", origin)
445 .add("asPath", asPath)
446 .add("localPref", localPref)
447 .add("multiExitDisc", multiExitDisc)
448 .toString();
449 }
450}