4 Octet AS Path Capability is sent to neighbor.
4 Octet AS in AS_PATH is parsed.
Now BGP can establish 4 Octet AS Path enabled peering with neighbor.

Change-Id: Ibb72e8037554928584ccafe6a14b82ffaca7e2cd
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
index 0cb74cf..1d7d4c5 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
@@ -227,6 +227,12 @@
             /** BGP UPDATE Attributes Type Code AS_PATH. */
             public static final int TYPE = 2;
 
+            /** BGP AS length. */
+            public static final int AS_LENGTH = 2;
+
+            /** BGP 4 Octet AS length (RFC 6793). */
+            public static final int AS_4OCTET_LENGTH = 4;
+
             /** BGP UPDATE AS_PATH Type: AS_SET. */
             public static final int AS_SET = 1;
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
index 5b9ac25..31471a3 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
@@ -23,6 +23,7 @@
 import org.onosproject.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
 import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities;
 import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities.MultiprotocolExtensions;
+import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities.As4Octet;
 import org.onosproject.sdnip.bgp.BgpMessage.BgpParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -334,8 +335,14 @@
                         throw new BgpParseException(errorMsg);
                     }
                     long as4Number = message.readUnsignedInt();
-                    // TODO: Implement support for 4-octet AS Numbers
+
+                    bgpSession.setRemoteAs4OctetCapability();
                     bgpSession.setRemoteAs4Octet(as4Number);
+
+                    // Copy remote 4-octet AS Number Capabilities and AS Number.
+                    // This is temporary setting until local AS number configuration is supported.
+                    bgpSession.setLocalAs4OctetCapability();
+                    bgpSession.setRemoteAs(as4Number);
                     log.debug("BGP RX OPEN Capability:  AS4 Number = {}",
                               as4Number);
                     break;
@@ -420,6 +427,15 @@
             message.writeByte(MultiprotocolExtensions.SAFI_MULTICAST);
         }
 
+        // 4 octet AS path capability
+        if (bgpSession.getLocalAs4OctetCapability()) {
+            message.writeByte(Capabilities.TYPE);               // Param type
+            message.writeByte(Capabilities.MIN_LENGTH +
+                              As4Octet.LENGTH);                 // Param len
+            message.writeByte(As4Octet.CODE);                   // Capab, code
+            message.writeByte(As4Octet.LENGTH);                 // Capab, len
+            message.writeInt((int) bgpSession.getLocalAs());
+        }
         return message;
     }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
index 59d0385..52c6827 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
@@ -66,6 +66,7 @@
     private boolean remoteIpv4Multicast;        // Peer IPv4/MULTICAST AFI/SAFI
     private boolean remoteIpv6Unicast;          // Peer IPv6/UNICAST AFI/SAFI
     private boolean remoteIpv6Multicast;        // Peer IPv6/MULTICAST AFI/SAFI
+    private boolean remoteAs4OctetCapability;   // Peer 4 octet AS path capability
     //
     private SocketAddress localAddress;         // Local IP addr/port
     private Ip4Address localIp4Address;         // Local IPv4 address
@@ -77,6 +78,7 @@
     private boolean localIpv4Multicast;      // Local IPv4/MULTICAST AFI/SAFI
     private boolean localIpv6Unicast;        // Local IPv6/UNICAST AFI/SAFI
     private boolean localIpv6Multicast;      // Local IPv6/MULTICAST AFI/SAFI
+    private boolean localAs4OctetCapability;    // Local 4 octet AS path capability
     //
     private long localKeepaliveInterval;        // Keepalive interval
 
@@ -188,7 +190,7 @@
         // In the future the local AS number should be configured as part
         // of an explicit BGP peering configuration.
         //
-        this.localAs = remoteAs;
+        setLocalAs(remoteAs);
     }
 
     /**
@@ -330,6 +332,47 @@
     }
 
     /**
+     * Gets the BGP session remote 4 octet AS path capability.
+     *
+     * @return true when the BGP session remote has 4 octet AS path capability
+     */
+    public boolean getRemoteAs4OctetCapability() {
+        return remoteAs4OctetCapability;
+    }
+
+    /**
+     * Sets the BGP session remote 4 octet AS path capability.
+     */
+    void setRemoteAs4OctetCapability() {
+        this.remoteAs4OctetCapability = true;
+    }
+
+    /**
+     * Gets the BGP session local 4 octet AS path capability.
+     *
+     * @return true when the BGP session local has 4 octet AS path capability
+     */
+    public boolean getLocalAs4OctetCapability() {
+        return localAs4OctetCapability;
+    }
+
+    /**
+     * Sets the BGP session local 4 octet AS path capability.
+     */
+    void setLocalAs4OctetCapability() {
+        this.localAs4OctetCapability = true;
+    }
+
+    /**
+     * Gets the BGP session 4 octet AS path capability.
+     *
+     * @return true when the BGP session is 4 octet AS path capable
+     */
+    public boolean isAs4OctetCapable() {
+        return getRemoteAs4OctetCapability() && getLocalAs4OctetCapability();
+    }
+
+    /**
      * Gets the BGP session local address.
      *
      * @return the BGP session local address
@@ -366,6 +409,15 @@
     }
 
     /**
+     * Sets the BGP session local AS number.
+     *
+     * @param localAs the BGP session local AS number to set
+     */
+    public void setLocalAs(long localAs) {
+        this.localAs = localAs;
+    }
+
+    /**
      * Gets the BGP session local Holdtime.
      *
      * @return the BGP session local Holdtime
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
index 67b47ef..f7bfa32 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
@@ -26,6 +26,7 @@
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onosproject.sdnip.bgp.BgpConstants.Update.AsPath;
 import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
 import org.onosproject.sdnip.bgp.BgpMessage.BgpParseException;
 import org.slf4j.Logger;
@@ -610,17 +611,30 @@
                 throw new BgpParseException(errorMsg);
             }
 
+            // 4-octet AS number handling.
+            int asPathLen;
+            if (bgpSession.isAs4OctetCapable()) {
+                asPathLen = AsPath.AS_4OCTET_LENGTH;
+            } else {
+                asPathLen = AsPath.AS_LENGTH;
+            }
+
             // Parse the AS numbers
-            if (2 * pathSegmentLength > attrLen) {
+            if (asPathLen * pathSegmentLength > attrLen) {
                 // ERROR: Malformed AS_PATH
                 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
                 String errorMsg = "Malformed AS Path";
                 throw new BgpParseException(errorMsg);
             }
-            attrLen -= (2 * pathSegmentLength);
+            attrLen -= (asPathLen * pathSegmentLength);
             ArrayList<Long> segmentAsNumbers = new ArrayList<>();
             while (pathSegmentLength-- > 0) {
-                long asNumber = message.readUnsignedShort();
+                long asNumber;
+                if (asPathLen == AsPath.AS_4OCTET_LENGTH) {
+                    asNumber = message.readUnsignedInt();
+                } else {
+                    asNumber = message.readUnsignedShort();
+                }
                 segmentAsNumbers.add(asNumber);
             }
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
index 1b628f4..7fc5278 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
@@ -48,6 +48,8 @@
         "  Local  router ID %s, IP %s, BGP version %d, Hold time %d";
     private static final String FORMAT_NEIGHBOR_LINE5 =
         "  Local  AFI/SAFI IPv4 Unicast %s Multicast %s, IPv6 Unicast %s Multicast %s";
+    private static final String FORMAT_NEIGHBOR_LINE6 =
+        "  4 Octet AS Capability: %s %s";
 
     @Override
     protected void execute() {
@@ -120,6 +122,11 @@
               bgpSession.getLocalIpv4Multicast() ? "YES" : "NO",
               bgpSession.getLocalIpv6Unicast() ? "YES" : "NO",
               bgpSession.getLocalIpv6Multicast() ? "YES" : "NO");
+        if (bgpSession.getLocalAs4OctetCapability() || bgpSession.getRemoteAs4OctetCapability()) {
+            print(FORMAT_NEIGHBOR_LINE6,
+                  bgpSession.getLocalAs4OctetCapability() ? "Advertised" : "",
+                  bgpSession.getRemoteAs4OctetCapability() ? "Received" : "");
+        }
     }
 
     /**