ONOS-734 Add unit tests for 4 Octets AS numbers in SDN-IP

 * Fix a bug in the storing, handling and verification of the AS numbers
   with 4 octet AS capability is used.

 * Add an unit test to test the decoding and parsing of supported
   BGP Capabilities: Multiprotocol Extensions AFI/SAFI, and 4 octet AS.

 * Minor refactoring of the BGP unit test framework.

Change-Id: I474b356bc00369c307ac0c5c214b065c1cc0c52c
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 d15a669..93ef852 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
@@ -101,32 +101,6 @@
 
         // Remote AS number
         long remoteAs = message.readUnsignedShort();
-        //
-        // Verify that the AS number is same for all other BGP Sessions
-        // NOTE: This check applies only for our use-case where all BGP
-        // sessions are iBGP.
-        //
-        for (BgpSession bs : bgpSession.getBgpSessionManager().getBgpSessions()) {
-            if ((bs.remoteInfo().asNumber() != 0) &&
-                (remoteAs != bs.remoteInfo().asNumber())) {
-                log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " +
-                          "Expected {}",
-                          bgpSession.remoteInfo().address(), remoteAs,
-                          bs.remoteInfo().asNumber());
-                //
-                // ERROR: Bad Peer AS
-                //
-                // Send NOTIFICATION and close the connection
-                int errorCode = OpenMessageError.ERROR_CODE;
-                int errorSubcode = OpenMessageError.BAD_PEER_AS;
-                ChannelBuffer txMessage =
-                    BgpNotification.prepareBgpNotification(errorCode,
-                                                           errorSubcode, null);
-                ctx.getChannel().write(txMessage);
-                bgpSession.closeSession(ctx);
-                return;
-            }
-        }
         bgpSession.remoteInfo().setAsNumber(remoteAs);
         //
         // NOTE: Currently, the local AS number is always set to the remote AS.
@@ -194,16 +168,63 @@
             return;
         }
 
+        //
+        // NOTE: Prepare the BGP OPEN message before the original local AS
+        // is overwritten by the 4-octet AS number
+        //
+        ChannelBuffer txOpenMessage = prepareBgpOpen(bgpSession.localInfo());
+
+        //
+        // Use the 4-octet AS number in lieu of the "My AS" field
+        // See RFC 6793, Section 4.1, second paragraph.
+        //
+        if (bgpSession.remoteInfo().as4OctetCapability()) {
+            long as4Number = bgpSession.remoteInfo().as4Number();
+            bgpSession.remoteInfo().setAsNumber(as4Number);
+            bgpSession.localInfo().setAsNumber(as4Number);
+        }
+
+        //
+        // Verify that the AS number is same for all other BGP Sessions
+        // NOTE: This check applies only for our use-case where all BGP
+        // sessions are iBGP.
+        //
+        for (BgpSession bs : bgpSession.getBgpSessionManager().getBgpSessions()) {
+            if ((bs.remoteInfo().asNumber() != 0) &&
+                (bgpSession.remoteInfo().asNumber() !=
+                 bs.remoteInfo().asNumber())) {
+                log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " +
+                          "Expected {}",
+                          bgpSession.remoteInfo().address(),
+                          bgpSession.remoteInfo().asNumber(),
+                          bs.remoteInfo().asNumber());
+                //
+                // ERROR: Bad Peer AS
+                //
+                // Send NOTIFICATION and close the connection
+                int errorCode = OpenMessageError.ERROR_CODE;
+                int errorSubcode = OpenMessageError.BAD_PEER_AS;
+                ChannelBuffer txMessage =
+                    BgpNotification.prepareBgpNotification(errorCode,
+                                                           errorSubcode, null);
+                ctx.getChannel().write(txMessage);
+                bgpSession.closeSession(ctx);
+                return;
+            }
+        }
+
         log.debug("BGP RX OPEN message from {}: " +
                   "BGPv{} AS {} BGP-ID {} Holdtime {}",
-                  bgpSession.remoteInfo().address(), remoteBgpVersion,
-                  remoteAs, remoteBgpId, remoteHoldtime);
+                  bgpSession.remoteInfo().address(),
+                  bgpSession.remoteInfo().bgpVersion(),
+                  bgpSession.remoteInfo().asNumber(),
+                  bgpSession.remoteInfo().bgpId(),
+                  bgpSession.remoteInfo().holdtime());
 
         // Send my OPEN followed by KEEPALIVE
-        ChannelBuffer txMessage = prepareBgpOpen(bgpSession.localInfo());
-        ctx.getChannel().write(txMessage);
+        ctx.getChannel().write(txOpenMessage);
         //
-        txMessage = BgpKeepalive.prepareBgpKeepalive();
+        ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive();
         ctx.getChannel().write(txMessage);
 
         // Start the KEEPALIVE timer
@@ -219,7 +240,7 @@
      * @param localInfo the BGP Session local information to use
      * @return the message to transmit (BGP header included)
      */
-    private static ChannelBuffer prepareBgpOpen(BgpSessionInfo localInfo) {
+    static ChannelBuffer prepareBgpOpen(BgpSessionInfo localInfo) {
         ChannelBuffer message =
             ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
 
@@ -360,9 +381,6 @@
 
                     bgpSession.remoteInfo().setAs4OctetCapability();
                     bgpSession.remoteInfo().setAs4Number(as4Number);
-                    // Use the 4-octet AS number in lieu of the "My AS" field
-                    // See RFC 6793, Section 4.1, second paragraph.
-                    bgpSession.remoteInfo().setAsNumber(as4Number);
 
                     //
                     // Copy remote 4-octet AS Number Capabilities and AS
@@ -371,7 +389,6 @@
                     //
                     bgpSession.localInfo().setAs4OctetCapability();
                     bgpSession.localInfo().setAs4Number(as4Number);
-                    bgpSession.localInfo().setAsNumber(as4Number);
                     log.debug("BGP RX OPEN Capability: AS4 Number = {}",
                               as4Number);
                     break;
@@ -461,8 +478,8 @@
             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.writeByte(As4Octet.CODE);                   // Capab. code
+            message.writeByte(As4Octet.LENGTH);                 // Capab. len
             message.writeInt((int) localInfo.as4Number());
         }
         return message;
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
index 11a8b5e..45ee321 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
@@ -85,9 +85,10 @@
     private BgpSessionManager bgpSessionManager;
 
     // Remote Peer state
-    TestBgpPeer peer1 = new TestBgpPeer(BGP_PEER1_ID);
-    TestBgpPeer peer2 = new TestBgpPeer(BGP_PEER2_ID);
-    TestBgpPeer peer3 = new TestBgpPeer(BGP_PEER3_ID);
+    private final Collection<TestBgpPeer> peers = new LinkedList<>();
+    TestBgpPeer peer1;
+    TestBgpPeer peer2;
+    TestBgpPeer peer3;
 
     // Local BGP per-peer session state
     BgpSession bgpSession1;
@@ -238,6 +239,14 @@
 
     @Before
     public void setUp() throws Exception {
+        peer1 = new TestBgpPeer(BGP_PEER1_ID);
+        peer2 = new TestBgpPeer(BGP_PEER2_ID);
+        peer3 = new TestBgpPeer(BGP_PEER3_ID);
+        peers.clear();
+        peers.add(peer1);
+        peers.add(peer2);
+        peers.add(peer3);
+
         //
         // Setup the BGP Session Manager to test, and start listening for BGP
         // connections.
@@ -366,24 +375,14 @@
         // Test the fields from the BGP OPEN message:
         // BGP version, AS number, BGP ID
         //
-        assertThat(peer1.peerFrameDecoder.remoteBgpVersion,
-                   is(BgpConstants.BGP_VERSION));
-        assertThat(peer1.peerFrameDecoder.remoteAs,
-                   is(TestBgpPeerChannelHandler.PEER_AS));
-        assertThat(peer1.peerFrameDecoder.remoteBgpIdentifier,
-                   is(IP_LOOPBACK_ID));
-        assertThat(peer2.peerFrameDecoder.remoteBgpVersion,
-                   is(BgpConstants.BGP_VERSION));
-        assertThat(peer2.peerFrameDecoder.remoteAs,
-                   is(TestBgpPeerChannelHandler.PEER_AS));
-        assertThat(peer2.peerFrameDecoder.remoteBgpIdentifier,
-                   is(IP_LOOPBACK_ID));
-        assertThat(peer3.peerFrameDecoder.remoteBgpVersion,
-                   is(BgpConstants.BGP_VERSION));
-        assertThat(peer3.peerFrameDecoder.remoteAs,
-                   is(TestBgpPeerChannelHandler.PEER_AS));
-        assertThat(peer3.peerFrameDecoder.remoteBgpIdentifier,
-                   is(IP_LOOPBACK_ID));
+        for (TestBgpPeer peer : peers) {
+            assertThat(peer.peerFrameDecoder.remoteInfo.bgpVersion(),
+                       is(BgpConstants.BGP_VERSION));
+            assertThat(peer.peerFrameDecoder.remoteInfo.bgpId(),
+                       is(IP_LOOPBACK_ID));
+            assertThat(peer.peerFrameDecoder.remoteInfo.asNumber(),
+                       is(TestBgpPeerChannelHandler.PEER_AS));
+        }
 
         //
         // Test that the BgpSession instances have been created
@@ -399,6 +398,72 @@
         }
     }
 
+
+    /**
+     * Tests that the BGP OPEN with Capability messages have been exchanged,
+     * followed by KEEPALIVE.
+     * <p>
+     * The BGP Peer opens the sessions and transmits OPEN Message, eventually
+     * followed by KEEPALIVE. The tested BGP listener should respond by
+     * OPEN Message, followed by KEEPALIVE.
+     *
+     * @throws TestUtilsException TestUtils error
+     */
+    @Test
+    public void testExchangedBgpOpenCapabilityMessages()
+            throws InterruptedException, TestUtilsException {
+        //
+        // Setup the BGP Capabilities for all peers
+        //
+        for (TestBgpPeer peer : peers) {
+            peer.peerChannelHandler.localInfo.setIpv4Unicast();
+            peer.peerChannelHandler.localInfo.setIpv4Multicast();
+            peer.peerChannelHandler.localInfo.setIpv6Unicast();
+            peer.peerChannelHandler.localInfo.setIpv6Multicast();
+            peer.peerChannelHandler.localInfo.setAs4OctetCapability();
+            peer.peerChannelHandler.localInfo.setAs4Number(
+                TestBgpPeerChannelHandler.PEER_AS4);
+        }
+
+        // Initiate the connections
+        peer1.connect(connectToSocket);
+        peer2.connect(connectToSocket);
+        peer3.connect(connectToSocket);
+
+        //
+        // Test the fields from the BGP OPEN message:
+        // BGP version, BGP ID
+        //
+        for (TestBgpPeer peer : peers) {
+            assertThat(peer.peerFrameDecoder.remoteInfo.bgpVersion(),
+                       is(BgpConstants.BGP_VERSION));
+            assertThat(peer.peerFrameDecoder.remoteInfo.bgpId(),
+                       is(IP_LOOPBACK_ID));
+        }
+
+        //
+        // Test that the BgpSession instances have been created,
+        // and contain the appropriate BGP session information.
+        //
+        assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID));
+        assertThat(bgpSessionManager.getBgpSessions(), hasSize(3));
+        assertThat(bgpSession1, notNullValue());
+        assertThat(bgpSession2, notNullValue());
+        assertThat(bgpSession3, notNullValue());
+        for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) {
+            BgpSessionInfo localInfo = bgpSession.localInfo();
+            assertThat(localInfo.ipv4Unicast(), is(true));
+            assertThat(localInfo.ipv4Multicast(), is(true));
+            assertThat(localInfo.ipv6Unicast(), is(true));
+            assertThat(localInfo.ipv6Multicast(), is(true));
+            assertThat(localInfo.as4OctetCapability(), is(true));
+            assertThat(localInfo.asNumber(),
+                       is(TestBgpPeerChannelHandler.PEER_AS4));
+            assertThat(localInfo.as4Number(),
+                       is(TestBgpPeerChannelHandler.PEER_AS4));
+        }
+    }
+
     /**
      * Tests that the BGP UPDATE messages have been received and processed.
      */
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerChannelHandler.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerChannelHandler.java
index 05fb5b4..979a1ed 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerChannelHandler.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerChannelHandler.java
@@ -30,8 +30,10 @@
  */
 class TestBgpPeerChannelHandler extends SimpleChannelHandler {
     static final long PEER_AS = 65001;
+    static final long PEER_AS4 = 0x12345678;
     static final int PEER_HOLDTIME = 120;       // 120 seconds
-    final Ip4Address bgpId;                     // The BGP ID
+
+    final BgpSessionInfo localInfo = new BgpSessionInfo();
     ChannelHandlerContext savedCtx;
 
     /**
@@ -40,7 +42,10 @@
      * @param bgpId the BGP ID to use
      */
     TestBgpPeerChannelHandler(Ip4Address bgpId) {
-        this.bgpId = bgpId;
+        this.localInfo.setBgpVersion(BgpConstants.BGP_VERSION);
+        this.localInfo.setBgpId(bgpId);
+        this.localInfo.setAsNumber(PEER_AS);
+        this.localInfo.setHoldtime(PEER_HOLDTIME);
     }
 
     /**
@@ -55,7 +60,7 @@
                                  ChannelStateEvent channelEvent) {
         this.savedCtx = ctx;
         // Prepare and transmit BGP OPEN message
-        ChannelBuffer message = prepareBgpOpen();
+        ChannelBuffer message = BgpOpen.prepareBgpOpen(localInfo);
         ctx.getChannel().write(message);
 
         // Prepare and transmit BGP KEEPALIVE message
@@ -70,23 +75,6 @@
     }
 
     /**
-     * Prepares BGP OPEN message.
-     *
-     * @return the message to transmit (BGP header included)
-     */
-    ChannelBuffer prepareBgpOpen() {
-        ChannelBuffer message =
-            ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
-        message.writeByte(BgpConstants.BGP_VERSION);
-        message.writeShort((int) PEER_AS);
-        message.writeShort(PEER_HOLDTIME);
-        message.writeInt(bgpId.toInt());
-        message.writeByte(0);                   // No Optional Parameters
-        return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN,
-                                            message);
-    }
-
-    /**
      * Prepares BGP UPDATE message.
      *
      * @param nextHopRouter the next-hop router address for the routes to add
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerFrameDecoder.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerFrameDecoder.java
index 7f56df0..4f39547 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerFrameDecoder.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerFrameDecoder.java
@@ -28,10 +28,7 @@
  * BGP peer session.
  */
 class TestBgpPeerFrameDecoder extends FrameDecoder {
-    int remoteBgpVersion;               // 1 octet
-    long remoteAs;                      // 2 octets
-    long remoteHoldtime;                // 2 octets
-    Ip4Address remoteBgpIdentifier;     // 4 octets -> IPv4 address
+    final BgpSessionInfo remoteInfo = new BgpSessionInfo();
 
     final CountDownLatch receivedOpenMessageLatch = new CountDownLatch(1);
     final CountDownLatch receivedKeepaliveMessageLatch = new CountDownLatch(1);
@@ -141,11 +138,10 @@
         //
         // Parse the OPEN message
         //
-        remoteBgpVersion = message.readUnsignedByte();
-        remoteAs = message.readUnsignedShort();
-        remoteHoldtime = message.readUnsignedShort();
-        remoteBgpIdentifier =
-            Ip4Address.valueOf((int) message.readUnsignedInt());
+        remoteInfo.setBgpVersion(message.readUnsignedByte());
+        remoteInfo.setAsNumber(message.readUnsignedShort());
+        remoteInfo.setHoldtime(message.readUnsignedShort());
+        remoteInfo.setBgpId(Ip4Address.valueOf((int) message.readUnsignedInt()));
         // Optional Parameters
         int optParamLen = message.readUnsignedByte();
         if (message.readableBytes() < optParamLen) {