[ONOS-2587] BGP send notification to peer on error

Change-Id: I5ff970e4da0d27f062e004c034b4325f0d5ec898
diff --git a/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java b/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java
index 9ca7ee3..dfcfc9d 100644
--- a/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java
+++ b/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java
@@ -55,4 +55,20 @@
     public static final byte OPTIONAL_ATTRIBUTE_ERROR = 9;
     public static final byte INVALID_NETWORK_FIELD = 10;
     public static final byte MALFORMED_ASPATH = 11;
+
+    //FSM  Error subcodes
+    public static final byte UNSPECIFIED_ERROR = 0;
+    public static final byte RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE = 1;
+    public static final byte RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE = 2;
+    public static final byte RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE = 3;
+
+    //Cease  Error subcodes
+    public static final byte MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1;
+    public static final byte ADMINISTRATIVE_SHUTDOWN = 2;
+    public static final byte PEER_DECONFIGURED = 3;
+    public static final byte ADMINISTRATIVE_RESET = 4;
+    public static final byte CONNECTION_REJECTED = 5;
+    public static final byte OTHER_CONFIGURATION_CHANGE = 6;
+    public static final byte CONNECTION_COLLISION_RESOLUTION = 7;
+    public static final byte OUT_OF_RESOURCES = 8;
 }
\ No newline at end of file
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 7f47a02..f21c311 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,7 +23,6 @@
 import java.net.UnknownHostException;
 import java.nio.channels.ClosedChannelException;
 import java.util.Collections;
-import java.util.Date;
 import java.util.List;
 import java.util.LinkedList;
 import java.util.ListIterator;
@@ -153,7 +152,9 @@
                 // check for OPEN message
                 if (m.getType() != BGPType.OPEN) {
                     // When the message type is not keep alive message increment the wrong packet statistics
-                    h.processUnknownMsg();
+                    h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR,
+                                        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");
@@ -202,7 +203,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();
+                    h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR, BGPErrorType.UNSPECIFIED_ERROR, m
+                                        .getType().getType());
                     log.debug("Message is not OPEN message");
                 } else {
                     h.bgpPacketStats.addInPacket();
@@ -247,7 +249,9 @@
                 // check for keep alive message
                 if (m.getType() != BGPType.KEEP_ALIVE) {
                     // When the message type is not keep alive message handle the wrong packet
-                    h.processUnknownMsg();
+                    h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR,
+                                        BGPErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE, m.getType()
+                                        .getType());
                     log.debug("Message is not KEEPALIVE message");
                 } else {
 
@@ -274,11 +278,12 @@
                      * time value is zero, then the HoldTimer and KeepaliveTimer are not started. A reasonable maximum
                      * time between KEEPALIVE messages would be one third of the Hold Time interval.
                      */
-                    h.sendKeepAliveMessage();
 
                     if (h.negotiatedHoldTime != 0) {
                         h.keepAliveTimer = new BGPKeepAliveTimer(h,
                                                                  (h.negotiatedHoldTime / BGP_MAX_KEEPALIVE_INTERVAL));
+                    } else {
+                        h.sendKeepAliveMessage();
                     }
 
                     h.bgpPacketStats.addOutPacket();
@@ -375,8 +380,7 @@
         }
 
         inetAddress = (InetSocketAddress) address;
-        ipAddress = IpAddress.valueOf(inetAddress.getAddress());
-        peerAddr = ipAddress.toString();
+        peerAddr = IpAddress.valueOf(inetAddress.getAddress()).toString();
 
         // if peer is not configured disconnect session
         if (!bgpconfig.isPeerConfigured(peerAddr)) {
@@ -418,8 +422,7 @@
         }
 
         inetAddress = (InetSocketAddress) address;
-        ipAddress = IpAddress.valueOf(inetAddress.getAddress());
-        peerAddr = ipAddress.toString();
+        peerAddr = IpAddress.valueOf(inetAddress.getAddress()).toString();
 
         if (thisbgpId != null) {
             if (!duplicateBGPIdFound) {
@@ -458,14 +461,14 @@
             if ((ChannelState.OPENWAIT == state) || (ChannelState.OPENSENT == state)) {
 
                 // When ReadTimeout timer is expired in OPENWAIT/OPENSENT state, it is considered
-                // TODO: Send notification
+                sendNotification(BGPErrorType.HOLD_TIMER_EXPIRED, (byte) 0, null);
                 channel.close();
                 state = ChannelState.IDLE;
                 return;
             } else if (ChannelState.OPENCONFIRM == state) {
 
                 // When ReadTimeout timer is expired in OPENCONFIRM state.
-                // TODO: Send Notification
+                sendNotification(BGPErrorType.HOLD_TIMER_EXPIRED, (byte) 0, null);
                 channel.close();
                 state = ChannelState.IDLE;
                 return;
@@ -480,8 +483,17 @@
             }
             channel.close();
         } else if (e.getCause() instanceof BGPParseException) {
-            // TODO: SEND NOTIFICATION
-            log.debug("BGP Parse Exception: ", e.getCause());
+            byte[] data = new byte[] {};
+            BGPParseException errMsg = (BGPParseException) e.getCause();
+            byte errorCode = errMsg.getErrorCode();
+            byte errorSubCode = errMsg.getErrorSubCode();
+            ChannelBuffer tempCb = errMsg.getData();
+            if (tempCb != null) {
+                int dataLength = tempCb.capacity();
+                data = new byte[dataLength];
+                tempCb.readBytes(data, 0, dataLength);
+            }
+            sendNotification(errorCode, errorSubCode, data);
         } else if (e.getCause() instanceof RejectedExecutionException) {
             log.warn("Could not process message: queue full");
         } else {
@@ -591,13 +603,32 @@
 
         bgpId = Ip4Address.valueOf(bgpconfig.getRouterId()).toInt();
         BGPMessage msg = factory4.openMessageBuilder().setAsNumber((short) bgpconfig.getAsNumber())
-                .setHoldTime(bgpconfig.getHoldTime()).setBgpId(bgpId).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));
 
     }
 
     /**
+     * Send notification message to peer.
+     *
+     * @param errorCode error code send in notification
+     * @param errorSubCode sub error code send in notification
+     * @param data data to send in notification
+     * @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();
+        log.debug("Sending notification message to {}", channel.getRemoteAddress());
+        channel.write(Collections.singletonList(msg));
+    }
+
+    /**
      * Send keep alive message.
      *
      * @throws IOException when channel is disconnected
@@ -611,45 +642,20 @@
     }
 
     /**
-     * Send notification and close channel with peer.
-     */
-    private void sendErrNotificationAndCloseChannel() {
-        // TODO: send notification
-        channel.close();
-    }
-
-    /**
      * Process unknown BGP message received.
      *
-     * @throws BGPParseException when received invalid message
+     * @param errorCode error code
+     * @param errorSubCode error sub code
+     * @param data message type
+     * @throws BGPParseException while processing error messsage
+     * @throws IOException while processing error message
      */
-    public void processUnknownMsg() throws BGPParseException {
+    public void processUnknownMsg(byte errorCode, byte errorSubCode, byte data) throws BGPParseException, IOException {
         log.debug("UNKNOWN message received");
-        Date now = null;
-        if (bgpPacketStats.wrongPacketCount() == 0) {
-            now = new Date();
-            bgpPacketStats.setTime(now.getTime());
-            bgpPacketStats.addWrongPacket();
-            sendErrNotificationAndCloseChannel();
-        }
-        if (bgpPacketStats.wrongPacketCount() > 1) {
-            Date lastest = new Date();
-            bgpPacketStats.addWrongPacket();
-            // converting to seconds
-            if (((lastest.getTime() - bgpPacketStats.getTime()) / 1000) > 60) {
-                now = lastest;
-                bgpPacketStats.setTime(now.getTime());
-                bgpPacketStats.resetWrongPacket();
-                bgpPacketStats.addWrongPacket();
-            } else if (((int) (lastest.getTime() - now.getTime()) / 1000) < 60) {
-                if (MAX_WRONG_COUNT_PACKET <= bgpPacketStats.wrongPacketCount()) {
-                    // reset once wrong packet count reaches MAX_WRONG_COUNT_PACKET
-                    bgpPacketStats.resetWrongPacket();
-                    // max wrong packets received send error message and close the session
-                    sendErrNotificationAndCloseChannel();
-                }
-            }
-        }
+        byte[] byteArray = new byte[1];
+        byteArray[0] = data;
+        sendNotification(errorCode, errorSubCode, byteArray);
+        channel.close();
     }
 
     /**