Removed checkstyle warnings.
[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 com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Preconditions;
12 import com.google.common.net.InetAddresses;
13
14 import java.util.Arrays;
15 import java.util.Comparator;
16 import java.util.List;
17
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.as.path.Segments;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.AListCase;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.ASetCase;
25
26 /**
27  * This comparator is intended to implement BGP Best Path Selection algorithm, as described at
28  *
29  * @see http://www.cisco.com/en/US/tech/tk365/technologies_tech_note09186a0080094431.shtml
30  *
31  * @param <T> Actual object state reference
32  */
33 final class BGPObjectComparator implements Comparator<PathAttributes> {
34     private final byte[] localId, remoteId;
35     private final AsNumber ourAS;
36
37     public BGPObjectComparator(final AsNumber ourAs, final Ipv4Address localId, final Ipv4Address remoteId) {
38         this.ourAS = Preconditions.checkNotNull(ourAs);
39         this.localId = InetAddresses.forString(localId.getValue()).getAddress();
40         this.remoteId = InetAddresses.forString(remoteId.getValue()).getAddress();
41     }
42
43     @Override
44     public int compare(final PathAttributes o1, final PathAttributes o2) {
45         if (o1 == null) {
46             return 1;
47         }
48         if (o2 == null) {
49             return -1;
50         }
51         if (o1.equals(o2) && Arrays.equals(this.localId, this.remoteId)) {
52             return 0;
53         }
54         // 1. prefer path with accessible nexthop
55         // - we assume that all nexthops are accessible
56
57         // 2. prefer path with higher LOCAL_PREF
58         if ((o1.getLocalPref() != null || o2.getLocalPref() != null)
59                 && (o1.getLocalPref() != null && !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() != null || o2.getMultiExitDisc() != null)
91                 && (o1.getMultiExitDisc() != null && !o1.getMultiExitDisc().equals(o2.getMultiExitDisc()))) {
92             return o2.getMultiExitDisc().getMed().compareTo(o1.getMultiExitDisc().getMed());
93         }
94
95         // 7. prefer eBGP over iBGP paths
96         // EBGP is peering between two different AS, whereas IBGP is between same AS (Autonomous System).
97         final AsNumber first = getPeerAs(o1.getAsPath().getSegments());
98         final AsNumber second = getPeerAs(o2.getAsPath().getSegments());
99         if ((first != null || second != null) && (first != null && !first.equals(second))) {
100             if (first == null || first.equals(this.ourAS)) {
101                 return -1;
102             }
103             if (second == null || second.equals(this.ourAS)) {
104                 return 1;
105             }
106         }
107
108         // 8. Prefer the path with the lowest IGP metric to the BGP next hop.
109         // - no next hop metric is advertized
110
111         // 9. When both paths are external, prefer the path that was received first (the oldest one).
112         // if (first.equals(this.ourAS) && second.equals(this.ourAS)) {
113         // FIXME: do we have a way how to determine which one was received first?
114         // }
115
116         // 10. Prefer the route that comes from the BGP router with the lowest router ID.
117         // The router ID is the highest IP address on the router, with preference given to loopback addresses.
118         // If a path contains route reflector (RR) attributes, the originator ID is substituted for the router ID in the
119         // path selection process.
120         byte[] oid1 = this.localId;
121         byte[] oid2 = this.remoteId;
122         if (o1.getOriginatorId() != null) {
123             oid1 = InetAddresses.forString(o1.getOriginatorId().getValue()).getAddress();
124         }
125         if (o2.getOriginatorId() != null) {
126             oid2 = InetAddresses.forString(o2.getOriginatorId().getValue()).getAddress();
127         }
128         if (!Arrays.equals(oid1, oid2)) {
129             return compareByteArrays(oid1, oid2);
130         }
131         // 11. prefer the path with the minimum cluster list length
132         int cluster1 = 0;
133         int cluster2 = 0;
134         if (o1.getClusterId() != null) {
135             cluster1 = o1.getClusterId().size();
136         }
137         if (o2.getClusterId() != null) {
138             cluster2 = o2.getClusterId().size();
139         }
140         if (cluster1 != cluster2) {
141             return ((Integer) cluster1).compareTo(cluster2);
142         }
143
144         // 12. Prefer the path that comes from the lowest neighbor address.
145         // FIXME: do we know this?
146
147         return 0;
148     }
149
150     private static int countAsPath(final List<Segments> segments) {
151         // an AS_SET counts as 1, no matter how many ASs are in the set.
152         int count = 0;
153         boolean setPresent = false;
154         for (final Segments s : segments) {
155             if (s.getCSegment() instanceof ASetCase) {
156                 setPresent = true;
157             } else {
158                 final AListCase list = (AListCase) s.getCSegment();
159                 count += list.getAList().getAsSequence().size();
160             }
161         }
162         return (setPresent) ? ++count : count;
163     }
164
165     private static AsNumber getPeerAs(final List<Segments> segments) {
166         if (segments.size() == 0) {
167             return null;
168         }
169         final AListCase first = (AListCase) segments.get(0).getCSegment();
170         return first.getAList().getAsSequence().get(0).getAs();
171     }
172
173     @VisibleForTesting
174     public static int compareByteArrays(final byte[] byteOne, final byte[] byteTwo) {
175         for (int i = 0; i < byteOne.length; i++) {
176             final int res = Byte.compare(byteOne[i], byteTwo[i]);
177             if (res != 0) {
178                 return res;
179             }
180         }
181         return 0;
182     }
183 }