Added support for parsing and handling BGP Confederation related AS Path
attributes.
Note: BGP Confedertions are not supported (yet).

Also, updated/simplified the MED comparison in the BGP Path Comparison
implementation.

Change-Id: Iabe01facffd2c6912f33f647841c1244d85282f3
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
index 203e03c..4ca7da6 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
@@ -21,6 +21,7 @@
 import java.util.Objects;
 
 import org.onlab.onos.sdnip.RouteEntry;
+import org.onlab.onos.sdnip.bgp.BgpConstants.Update;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 
@@ -35,8 +36,7 @@
     private final byte origin;          // Route ORIGIN: IGP, EGP, INCOMPLETE
     private final AsPath asPath;        // The AS Path
     private final long localPref;       // The local preference for the route
-    private long multiExitDisc =
-        BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
+    private long multiExitDisc = Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
 
     /**
      * Class constructor.
@@ -116,21 +116,33 @@
      * Tests whether the route is originated from the local AS.
      * <p>
      * The route is considered originated from the local AS if the AS Path
-     * is empty or if it begins with an AS_SET.
+     * is empty or if it begins with an AS_SET (after skipping
+     * AS_CONFED_SEQUENCE and AS_CONFED_SET).
      * </p>
      *
      * @return true if the route is originated from the local AS, otherwise
      * false
      */
     boolean isLocalRoute() {
-        if (asPath.getPathSegments().isEmpty()) {
+        PathSegment firstPathSegment = null;
+
+        // Find the first Path Segment by ignoring the AS_CONFED_* segments
+        for (PathSegment pathSegment : asPath.getPathSegments()) {
+            if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
+                (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
+                firstPathSegment = pathSegment;
+                break;
+            }
+        }
+        if (firstPathSegment == null) {
+            return true;                // Local route: no path segments
+        }
+        // If the first path segment is AS_SET, the route is considered local
+        if (firstPathSegment.getType() == Update.AsPath.AS_SET) {
             return true;
         }
-        PathSegment firstPathSegment = asPath.getPathSegments().get(0);
-        if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
-            return true;
-        }
-        return false;
+
+        return false;                   // The route is not local
     }
 
     /**
@@ -143,10 +155,25 @@
      * @return the BGP Neighbor AS number the route was received from.
      */
     long getNeighborAs() {
+        PathSegment firstPathSegment = null;
+
         if (isLocalRoute()) {
             return BgpConstants.BGP_AS_0;
         }
-        PathSegment firstPathSegment = asPath.getPathSegments().get(0);
+
+        // Find the first Path Segment by ignoring the AS_CONFED_* segments
+        for (PathSegment pathSegment : asPath.getPathSegments()) {
+            if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
+                (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
+                firstPathSegment = pathSegment;
+                break;
+            }
+        }
+        if (firstPathSegment == null) {
+            // NOTE: Shouldn't happen - should be captured by isLocalRoute()
+            return BgpConstants.BGP_AS_0;
+        }
+
         if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
             // TODO: Shouldn't happen. Should check during the parsing.
             return BgpConstants.BGP_AS_0;
@@ -211,18 +238,16 @@
 
         // Compare the MED if the neighbor AS is same: larger is better
         medLabel: {
-            boolean thisIsLocalRoute = isLocalRoute();
-            if (thisIsLocalRoute != other.isLocalRoute()) {
-                break medLabel;                 // AS number is different
+            if (isLocalRoute() || other.isLocalRoute()) {
+                // Compare MEDs for non-local routes only
+                break medLabel;
             }
-            if (!thisIsLocalRoute) {
-                long thisNeighborAs = getNeighborAs();
-                if (thisNeighborAs != other.getNeighborAs()) {
-                    break medLabel;             // AS number is different
-                }
-                if (thisNeighborAs == BgpConstants.BGP_AS_0) {
-                    break medLabel;             // Invalid AS number
-                }
+            long thisNeighborAs = getNeighborAs();
+            if (thisNeighborAs != other.getNeighborAs()) {
+                break medLabel;             // AS number is different
+            }
+            if (thisNeighborAs == BgpConstants.BGP_AS_0) {
+                break medLabel;             // Invalid AS number
             }
 
             // Compare the MED
@@ -253,13 +278,16 @@
      * A class to represent AS Path Segment.
      */
     public static class PathSegment {
-        private final byte type;        // Segment type: AS_SET, AS_SEQUENCE
+        // Segment type: AS_SET(1), AS_SEQUENCE(2), AS_CONFED_SEQUENCE(3),
+        // AS_CONFED_SET(4)
+        private final byte type;
         private final ArrayList<Long> segmentAsNumbers;   // Segment AS numbers
 
         /**
          * Constructor.
          *
-         * @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
+         * @param type the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
+         * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
          * @param segmentAsNumbers the Segment AS numbers
          */
         PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
@@ -268,9 +296,11 @@
         }
 
         /**
-         * Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
+         * Gets the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
+         * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4).
          *
-         * @return the Path Segment Type: AS_SET, AS_SEQUENCE
+         * @return the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
+         * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
          */
         public byte getType() {
             return type;
@@ -309,7 +339,7 @@
         @Override
         public String toString() {
             return MoreObjects.toStringHelper(getClass())
-                .add("type", BgpConstants.Update.AsPath.typeToString(type))
+                .add("type", Update.AsPath.typeToString(type))
                 .add("segmentAsNumbers", this.segmentAsNumbers)
                 .toString();
         }
@@ -333,15 +363,27 @@
              //
              // Precompute the AS Path Length:
              // - AS_SET counts as 1
+             // - AS_SEQUENCE counts how many AS numbers are included
+             // - AS_CONFED_SEQUENCE and AS_CONFED_SET are ignored
              //
              int pl = 0;
              for (PathSegment pathSegment : pathSegments) {
-                 if (pathSegment.getType() ==
-                     BgpConstants.Update.AsPath.AS_SET) {
-                     pl++;
-                     continue;
+                 switch (pathSegment.getType()) {
+                 case Update.AsPath.AS_SET:
+                     pl++;              // AS_SET counts as 1
+                     break;
+                 case Update.AsPath.AS_SEQUENCE:
+                     // Count each AS number
+                     pl += pathSegment.getSegmentAsNumbers().size();
+                     break;
+                 case Update.AsPath.AS_CONFED_SEQUENCE:
+                     break;             // Ignore
+                 case Update.AsPath.AS_CONFED_SET:
+                     break;             // Ignore
+                 default:
+                     // TODO: What to do if the Path Segment type is unknown?
+                     break;
                  }
-                 pl += pathSegment.getSegmentAsNumbers().size();
              }
              asPathLength = pl;
          }
@@ -444,7 +486,7 @@
             .add("prefix", prefix())
             .add("nextHop", nextHop())
             .add("bgpId", bgpSession.getRemoteBgpId())
-            .add("origin", BgpConstants.Update.Origin.typeToString(origin))
+            .add("origin", Update.Origin.typeToString(origin))
             .add("asPath", asPath)
             .add("localPref", localPref)
             .add("multiExitDisc", multiExitDisc)