BGP connection collision detection.

Change-Id: I124674b79170450409df5d0f4b5fea5f9e11f1d9
diff --git a/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPNotificationMsgVer4.java b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPNotificationMsgVer4.java
index 3bddd37..bf9d575 100644
--- a/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPNotificationMsgVer4.java
+++ b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPNotificationMsgVer4.java
@@ -151,7 +151,9 @@
 
         @Override
         public Builder setData(byte[] data) {
-            this.data = data;
+            if (data != null) {
+                this.data = data;
+            }
             return this;
         }
 
diff --git a/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java b/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java
index f21c311..c467f3c 100755
--- a/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java
+++ b/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java
@@ -23,8 +23,8 @@
 import java.net.UnknownHostException;
 import java.nio.channels.ClosedChannelException;
 import java.util.Collections;
-import java.util.List;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.ListIterator;
 import java.util.concurrent.RejectedExecutionException;
 
@@ -153,8 +153,8 @@
                 if (m.getType() != BGPType.OPEN) {
                     // When the message type is not keep alive message increment the wrong packet statistics
                     h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR,
-                                        BGPErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE, m.getType()
-                                        .getType());
+                                        BGPErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE,
+                                        m.getType().getType());
                     log.debug("Message is not OPEN message");
                 } else {
                     log.debug("Sending keep alive message in OPENSENT state");
@@ -165,6 +165,11 @@
 
                     // validate capabilities and open msg
                     if (h.openMsgValidation(h, pOpenmsg)) {
+                        if (h.connectionCollisionDetection(BGPPeerCfg.State.OPENCONFIRM,
+                                                           h.peerIdentifier, h.peerAddr)) {
+                            h.channel.close();
+                            return;
+                        }
                         log.debug("Sending handshake OPEN message");
 
                         /*
@@ -203,8 +208,8 @@
                 // check for open message
                 if (m.getType() != BGPType.OPEN) {
                     // When the message type is not open message increment the wrong packet statistics
-                    h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR, BGPErrorType.UNSPECIFIED_ERROR, m
-                                        .getType().getType());
+                    h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR, BGPErrorType.UNSPECIFIED_ERROR,
+                                        m.getType().getType());
                     log.debug("Message is not OPEN message");
                 } else {
                     h.bgpPacketStats.addInPacket();
@@ -214,6 +219,11 @@
 
                     // Validate open message
                     if (h.openMsgValidation(h, pOpenmsg)) {
+                        if (h.connectionCollisionDetection(BGPPeerCfg.State.OPENSENT,
+                                                           h.peerIdentifier, h.peerAddr)) {
+                            h.channel.close();
+                            return;
+                        }
                         log.debug("Sending handshake OPEN message");
 
                         /*
@@ -237,6 +247,7 @@
                         h.sendHandshakeOpenMessage();
                         h.bgpPacketStats.addOutPacket();
                         h.setState(OPENCONFIRM);
+                        h.bgpconfig.setPeerConnState(h.peerAddr, BGPPeerCfg.State.OPENCONFIRM);
                     }
                 }
             }
@@ -250,8 +261,8 @@
                 if (m.getType() != BGPType.KEEP_ALIVE) {
                     // When the message type is not keep alive message handle the wrong packet
                     h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR,
-                                        BGPErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE, m.getType()
-                                        .getType());
+                                        BGPErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE,
+                                        m.getType().getType());
                     log.debug("Message is not KEEPALIVE message");
                 } else {
 
@@ -281,7 +292,7 @@
 
                     if (h.negotiatedHoldTime != 0) {
                         h.keepAliveTimer = new BGPKeepAliveTimer(h,
-                                                                 (h.negotiatedHoldTime / BGP_MAX_KEEPALIVE_INTERVAL));
+                                                                (h.negotiatedHoldTime / BGP_MAX_KEEPALIVE_INTERVAL));
                     } else {
                         h.sendKeepAliveMessage();
                     }
@@ -292,13 +303,6 @@
                     h.setHandshakeComplete(true);
 
                     if (!h.peerManager.addConnectedPeer(h.thisbgpId, h.bgpPeer)) {
-                        /*
-                         * RFC 4271, Section 6.8, Based on the value of the BGP identifier, a convention is established
-                         * for detecting which BGP connection is to be preserved when a collision occurs. The convention
-                         * is to compare the BGP Identifiers of the peers involved in the collision and to retain only
-                         * the connection initiated by the BGP speaker with the higher-valued BGP Identifier..
-                         */
-                        // TODO: Connection collision handling.
                         disconnectDuplicate(h);
                     } else {
                         h.setState(ESTABLISHED);
@@ -374,6 +378,7 @@
 
         // Connection should establish only if local ip and Autonomous system number is configured.
         if (bgpconfig.getState() != BGPCfg.State.IP_AS_CONFIGURED) {
+            sendNotification(BGPErrorType.CEASE, BGPErrorType.CONNECTION_REJECTED, null);
             channel.close();
             log.info("BGP local AS and router ID not configured");
             return;
@@ -385,6 +390,7 @@
         // if peer is not configured disconnect session
         if (!bgpconfig.isPeerConfigured(peerAddr)) {
             log.debug("Peer is not configured {}", peerAddr);
+            sendNotification(BGPErrorType.CEASE, BGPErrorType.CONNECTION_REJECTED, null);
             channel.close();
             return;
         }
@@ -436,6 +442,25 @@
                 if (bgpPeer != null) {
                     peerManager.removeConnectedPeer(thisbgpId);
                 }
+
+                // Retry connection if connection is lost to bgp speaker/peer
+                if ((channel != null) && (null != channel.getPipeline().get("ActiveHandler"))) {
+                    BgpConnectPeerImpl connectPeer;
+                    BGPPeerCfg.State peerCfgState;
+
+                    peerCfgState = bgpconfig.getPeerConnState(peerAddr);
+                    // on session disconnect using configuration, do not retry
+                    if (!peerCfgState.equals(BGPPeerCfg.State.IDLE)) {
+                        log.debug("Connection reset by peer, retry, STATE:{}", peerCfgState);
+                        BGPPeerConfig peerConfig = (BGPPeerConfig) bgpconfig.displayPeers(peerAddr);
+
+                        bgpconfig.setPeerConnState(peerAddr, BGPPeerCfg.State.IDLE);
+                        connectPeer = new BgpConnectPeerImpl(bgpController, peerAddr, Controller.getBgpPortNum());
+                        peerConfig.setConnectPeer(connectPeer);
+                    }
+                } else {
+                    bgpconfig.setPeerConnState(peerAddr, BGPPeerCfg.State.IDLE);
+                }
             } else {
                 // A duplicate was disconnected on this ChannelHandler,
                 // this is the same peer reconnecting, but the original state was
@@ -448,6 +473,7 @@
                 keepAliveTimer.getKeepAliveTimer().cancel();
             }
         } else {
+            bgpconfig.setPeerConnState(peerAddr, BGPPeerCfg.State.IDLE);
             log.warn("No bgp ip in channelHandler registered for " + "disconnected peer {}", getPeerInfoString());
         }
     }
@@ -521,6 +547,39 @@
         }
     }
 
+    /**
+     * Check for connection collision.
+     *
+     * @param state connection state
+     * @param peerIdentifier BGP peer identifier
+     * @param peerAddr BGP peer address
+     * @return true if bgp spreakers initiated connection
+     * @throws BGPParseException on error while procession collision detection
+     * @throws IOException on error while procession collision detection
+     */
+    public boolean connectionCollisionDetection(BGPPeerCfg.State state, int peerIdentifier, String peerAddr)
+            throws IOException, BGPParseException {
+        /*
+         * RFC 4271, Section 6.8, Based on the value of the BGP identifier, a convention is established for detecting
+         * which BGP connection is to be preserved when a collision occurs. The convention is to compare the BGP
+         * Identifiers of the peers involved in the collision and to retain only the connection initiated by the BGP
+         * speaker with the higher-valued BGP Identifier..
+         */
+        BGPPeerCfg.State currentState = bgpconfig.getPeerConnState(peerAddr);
+        if (currentState.equals(state)) {
+            if (((Ip4Address.valueOf(bgpconfig.getRouterId())).compareTo(Ip4Address.valueOf(peerIdentifier))) > 0) {
+                // send notification
+                sendNotification(BGPErrorType.CEASE, BGPErrorType.CONNECTION_COLLISION_RESOLUTION, null);
+                log.debug("Connection collision detected, local id: {},  peer id: {}, peer state:{}, in state:{}",
+                          (Ip4Address.valueOf(bgpconfig.getRouterId())), (Ip4Address.valueOf(peerIdentifier)),
+                          currentState, state);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     // *************************
     // Channel utility methods
     // *************************
@@ -603,10 +662,8 @@
 
         bgpId = Ip4Address.valueOf(bgpconfig.getRouterId()).toInt();
         BGPMessage msg = factory4.openMessageBuilder().setAsNumber((short) bgpconfig.getAsNumber())
-                .setHoldTime(bgpconfig.getHoldTime()).setBgpId(bgpId)
-                .setLsCapabilityTlv(bgpconfig.getLsCapability())
-                .setLargeAsCapabilityTlv(bgpconfig.getLargeASCapability())
-                .build();
+                .setHoldTime(bgpconfig.getHoldTime()).setBgpId(bgpId).setLsCapabilityTlv(bgpconfig.getLsCapability())
+                .setLargeAsCapabilityTlv(bgpconfig.getLargeASCapability()).build();
         log.debug("Sending open message to {}", channel.getRemoteAddress());
         channel.write(Collections.singletonList(msg));
 
@@ -621,9 +678,9 @@
      * @throws IOException, BGPParseException while building message
      */
     private void sendNotification(byte errorCode, byte errorSubCode, byte[] data)
-            throws IOException, BGPParseException {
-        BGPMessage msg = factory4.notificationMessageBuilder().setErrorCode(errorCode).setErrorSubCode(errorSubCode)
-                .setData(data).build();
+                                                                           throws IOException, BGPParseException {
+        BGPMessage msg = factory4.notificationMessageBuilder().setErrorCode(errorCode)
+                                                              .setErrorSubCode(errorSubCode).setData(data).build();
         log.debug("Sending notification message to {}", channel.getRemoteAddress());
         channel.write(Collections.singletonList(msg));
     }
@@ -757,8 +814,7 @@
                 BGPValueType tlv = unSupportedCaplistIterator.next();
                 tlv.write(buffer);
             }
-            throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR,
-                    BGPErrorType.UNSUPPORTED_CAPABILITY, buffer);
+            throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.UNSUPPORTED_CAPABILITY, buffer);
         } else {
             return true;
         }