BUG-185 : implemented Best Path Selection Algorithm.
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPObjectComparator.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import java.util.Arrays;
11 import java.util.Comparator;
12 import java.util.List;
13
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
15 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.as.path.Segments;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.AListCase;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.ASetCase;
20
21 import com.google.common.annotations.VisibleForTesting;
22
23 /**
24  * This comparator is intended to implement BGP Best Path Selection algorithm, as described at
25  * 
26  * @see http://www.cisco.com/en/US/tech/tk365/technologies_tech_note09186a0080094431.shtml
27  * 
28  * @param <T> Actual object state reference
29  */
30 final class BGPObjectComparator implements Comparator<PathAttributes> {
31
32         private final AsNumber ourAS;
33
34         private final byte[] id1;
35
36         private final byte[] id2;
37
38         public BGPObjectComparator(final AsNumber ourAs, final byte[] id1, final byte[] id2) {
39                 this.ourAS = ourAs;
40                 this.id1 = id1;
41                 this.id2 = id2;
42         }
43
44         @Override
45         public int compare(final PathAttributes o1, final PathAttributes o2) {
46                 if (o1 == null) {
47                         return 1;
48                 }
49                 if (o2 == null) {
50                         return -1;
51                 }
52                 if (o1.equals(o2) && Arrays.equals(this.id1, this.id2)) {
53                         return 0;
54                 }
55                 // 1. prefer path with accessible nexthop
56                 // - we assume that all nexthops are accessible
57
58                 // 2. prefer path with higher LOCAL_PREF
59                 if (!o1.getLocalPref().equals(o2.getLocalPref())) {
60                         return o1.getLocalPref().getPref().compareTo(o2.getLocalPref().getPref());
61                 }
62
63                 // 3. prefer learned path
64                 // - we assume that all paths are learned
65
66                 // 4. prefer the path with the shortest AS_PATH.
67                 if (!o1.getAsPath().equals(o2.getAsPath())) {
68                         final Integer i1 = countAsPath(o1.getAsPath().getSegments());
69                         final Integer i2 = countAsPath(o2.getAsPath().getSegments());
70                         return i2.compareTo(i1);
71                 }
72
73                 // 5. prefer the path with the lowest origin type
74                 // - IGP is lower than Exterior Gateway Protocol (EGP), and EGP is lower than INCOMPLETE
75                 if (!o1.getOrigin().equals(o2.getOrigin())) {
76                         if (o1.getOrigin().getValue().equals(BgpOrigin.Igp)) {
77                                 return 1;
78                         }
79                         if (o2.getOrigin().getValue().equals(BgpOrigin.Igp)) {
80                                 return -1;
81                         }
82                         if (o1.getOrigin().getValue().equals(BgpOrigin.Egp)) {
83                                 return 1;
84                         } else {
85                                 return -1;
86                         }
87                 }
88
89                 // 6. prefer the path with the lowest multi-exit discriminator (MED)
90                 if (!o1.getMultiExitDisc().equals(o2.getMultiExitDisc())) {
91                         return o2.getMultiExitDisc().getMed().compareTo(o1.getMultiExitDisc().getMed());
92                 }
93
94                 // 7. prefer eBGP over iBGP paths
95                 // EBGP is peering between two different AS, whereas IBGP is between same AS (Autonomous System).
96                 final AsNumber first = getPeerAs(o1.getAsPath().getSegments());
97                 final AsNumber second = getPeerAs(o2.getAsPath().getSegments());
98                 if ((first != null || second != null) && (first != null && !first.equals(second))) {
99                         if (first == null || first.equals(this.ourAS)) {
100                                 return -1;
101                         }
102                         if (second == null || second.equals(this.ourAS)) {
103                                 return 1;
104                         }
105                 }
106
107                 // 8. Prefer the path with the lowest IGP metric to the BGP next hop.
108                 // - no next hop metric is advertized
109
110                 // 9. When both paths are external, prefer the path that was received first (the oldest one).
111                 // if (first.equals(this.ourAS) && second.equals(this.ourAS)) {
112                 // FIXME: do we have a way how to determine which one was received first?
113                 // }
114
115                 // 10. Prefer the route that comes from the BGP router with the lowest router ID.
116                 // The router ID is the highest IP address on the router, with preference given to loopback addresses.
117                 // If a path contains route reflector (RR) attributes, the originator ID is substituted for the router ID in the
118                 // path selection process.
119                 byte[] oid1 = this.id1;
120                 byte[] oid2 = this.id2;
121                 if (o1.getOriginatorId() != null) {
122                         oid1 = o1.getOriginatorId();
123                 }
124                 if (o2.getOriginatorId() != null) {
125                         oid2 = o2.getOriginatorId();
126                 }
127                 if (!Arrays.equals(oid1, oid2)) {
128                         return compareByteArrays(oid1, oid2);
129                 }
130                 // 11. prefer the path with the minimum cluster list length
131                 int cluster1 = 0;
132                 int cluster2 = 0;
133                 if (o1.getClusterId() != null) {
134                         cluster1 = o1.getClusterId().size();
135                 }
136                 if (o2.getClusterId() != null) {
137                         cluster2 = o2.getClusterId().size();
138                 }
139                 if (cluster1 != cluster2) {
140                         return ((Integer) cluster1).compareTo(cluster2);
141                 }
142
143                 // 12. Prefer the path that comes from the lowest neighbor address.
144                 // FIXME: do we know this?
145
146                 return 0;
147         }
148
149         private static int countAsPath(final List<Segments> segments) {
150                 // an AS_SET counts as 1, no matter how many ASs are in the set.
151                 int count = 0;
152                 boolean setPresent = false;
153                 for (final Segments s : segments) {
154                         if (s.getCSegment() instanceof ASetCase) {
155                                 setPresent = true;
156                         } else {
157                                 final AListCase list = (AListCase) s.getCSegment();
158                                 count += list.getAList().getAsSequence().size();
159                         }
160                 }
161                 return (setPresent) ? ++count : count;
162         }
163
164         private static AsNumber getPeerAs(final List<Segments> segments) {
165                 if (segments.size() == 0) {
166                         return null;
167                 }
168                 final AListCase first = (AListCase) segments.get(0).getCSegment();
169                 return first.getAList().getAsSequence().get(0).getAs();
170         }
171
172         @VisibleForTesting
173         public static int compareByteArrays(final byte[] byteOne, final byte[] byteTwo) {
174                 for (int i = 0; i < byteOne.length; i++) {
175                         final int res = Byte.compare(byteOne[i], byteTwo[i]);
176                         if (res != 0) {
177                                 return res;
178                         }
179                 }
180                 return 0;
181         }
182 }