[ONOS-4209] Unsuccessful PCEP session formation between ONOS and IOS XR

Change-Id: Ic509c50c5cef39e5f1a5319570f8c9406177b788
diff --git a/protocols/pcep/api/src/main/java/org/onosproject/pcep/controller/ClientCapability.java b/protocols/pcep/api/src/main/java/org/onosproject/pcep/controller/ClientCapability.java
new file mode 100644
index 0000000..e2b5092
--- /dev/null
+++ b/protocols/pcep/api/src/main/java/org/onosproject/pcep/controller/ClientCapability.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pcep.controller;
+
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Representation of capabilities supported by client.
+ */
+public class ClientCapability {
+    private boolean pceccCapability;
+    private boolean statefulPceCapability;
+    private boolean pcInstantiationCapability;
+
+    /**
+     * Creates new instance of client capability.
+     *
+     * @param pceccCapability represents PCECC capability
+     * @param statefulPceCapability represents stateful PCE capability
+     * @param pcInstantiationCapability represents PC initiation capability
+     */
+    public ClientCapability(boolean pceccCapability, boolean statefulPceCapability, boolean pcInstantiationCapability) {
+        this.pceccCapability = pceccCapability;
+        this.statefulPceCapability = statefulPceCapability;
+        this.pcInstantiationCapability = pcInstantiationCapability;
+    }
+
+    /**
+     * Obtains PCECC capability.
+     *
+     * @return true if client supports PCECC capability otherwise false
+     */
+    public boolean pceccCapability() {
+        return pceccCapability;
+    }
+
+    /**
+     * Obtains stateful PCE capability.
+     *
+     * @return true if client supports stateful PCE capability otherwise false
+     */
+    public boolean statefulPceCapability() {
+        return statefulPceCapability;
+    }
+
+    /**
+     * Obtains PC initiation capability.
+     *
+     * @return true if client supports PC initiation capability otherwise false
+     */
+    public boolean pcInstantiationCapability() {
+        return pcInstantiationCapability;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(pceccCapability, statefulPceCapability, pcInstantiationCapability);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof ClientCapability) {
+            ClientCapability other = (ClientCapability) obj;
+            return Objects.equals(pceccCapability, other.pceccCapability)
+                    && Objects.equals(statefulPceCapability, other.statefulPceCapability)
+                    && Objects.equals(pcInstantiationCapability, other.pcInstantiationCapability);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("pceccCapability", pceccCapability)
+                .add("statefulPceCapability", statefulPceCapability)
+                .add("pcInstantiationCapability", pcInstantiationCapability)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/protocols/pcep/api/src/main/java/org/onosproject/pcep/controller/PcepClient.java b/protocols/pcep/api/src/main/java/org/onosproject/pcep/controller/PcepClient.java
old mode 100755
new mode 100644
index 11e4a69..5e3996a
--- a/protocols/pcep/api/src/main/java/org/onosproject/pcep/controller/PcepClient.java
+++ b/protocols/pcep/api/src/main/java/org/onosproject/pcep/controller/PcepClient.java
@@ -107,4 +107,18 @@
      * @return true/false if the synchronization is completed/not completed
      */
     boolean isSyncComplete();
+
+    /**
+     * Sets capability negotiated during open message exchange.
+     *
+     * @param capability supported by client
+     */
+    void setCapability(ClientCapability capability);
+
+    /**
+     * Obtains capability supported by client.
+     *
+     * @return capability supported by client
+     */
+    ClientCapability capability();
 }
diff --git a/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepChannelHandler.java b/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepChannelHandler.java
index cf768ff..dd5d5f5 100644
--- a/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepChannelHandler.java
+++ b/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepChannelHandler.java
@@ -38,6 +38,7 @@
 import org.jboss.netty.handler.timeout.IdleStateHandler;
 import org.jboss.netty.handler.timeout.ReadTimeoutException;
 import org.onlab.packet.IpAddress;
+import org.onosproject.pcep.controller.ClientCapability;
 import org.onosproject.pcep.controller.PccId;
 import org.onosproject.pcep.controller.driver.PcepClientDriver;
 import org.onosproject.pcepio.exceptions.PcepParseException;
@@ -51,7 +52,6 @@
 import org.onosproject.pcepio.protocol.PcepOpenObject;
 import org.onosproject.pcepio.protocol.PcepType;
 import org.onosproject.pcepio.protocol.PcepVersion;
-import org.onosproject.pcepio.types.ErrorObjListWithOpen;
 import org.onosproject.pcepio.types.PceccCapabilityTlv;
 import org.onosproject.pcepio.types.StatefulPceCapabilityTlv;
 import org.onosproject.pcepio.types.PcepErrorDetailInfo;
@@ -74,6 +74,7 @@
     private byte sessionId = 0;
     private byte keepAliveTime;
     private byte deadTime;
+    private ClientCapability capability;
     private PcepPacketStatsImpl pcepPacketStats;
     static final int MAX_WRONG_COUNT_PACKET = 5;
     static final int BYTE_MASK = 0xFF;
@@ -146,8 +147,8 @@
 
                     h.pcepPacketStats.addInPacket();
                     PcepOpenMsg pOpenmsg = (PcepOpenMsg) m;
-                    // do Capability validation.
-                    if (h.capabilityValidation(pOpenmsg)) {
+                        //Do Capability negotiation.
+                        h.capabilityNegotiation(pOpenmsg);
                         log.debug("Sending handshake OPEN message");
                         h.sessionId = pOpenmsg.getPcepOpenObject().getSessionId();
                         h.pcepVersion = pOpenmsg.getPcepOpenObject().getVersion();
@@ -168,13 +169,6 @@
                         h.sendHandshakeOpenMessage();
                         h.pcepPacketStats.addOutPacket();
                         h.setState(KEEPWAIT);
-                    } else {
-                        log.debug("Capability validation failed. Sending PCEP-ERROR message to PCC.");
-                        // Send PCEP-ERROR message.
-                        PcepErrorMsg errMsg = h.getErrorMsg(PcepErrorDetailInfo.ERROR_TYPE_2,
-                                PcepErrorDetailInfo.ERROR_VALUE_2);
-                        h.channel.write(Collections.singletonList(errMsg));
-                    }
                 }
             }
         },
@@ -203,6 +197,8 @@
                     h.thispccId = PccId.pccId(IpAddress.valueOf(inetAddress.getAddress()));
                     h.pc = h.controller.getPcepClientInstance(h.thispccId, h.sessionId, h.pcepVersion,
                             h.pcepPacketStats);
+                    //Get pc instance and set capabilities
+                    h.pc.setCapability(h.capability);
                     // set the status of pcc as connected
                     h.pc.setConnected(true);
                     h.pc.setChannel(h.channel);
@@ -493,17 +489,12 @@
         channel.write(Collections.singletonList(msg));
     }
 
-    /**
-     * Capability Validation.
-     *
-     * @param pOpenmsg pcep open message
-     * @return success or failure
-     */
-    private boolean capabilityValidation(PcepOpenMsg pOpenmsg) {
+    //Capability negotiation
+    private void capabilityNegotiation(PcepOpenMsg pOpenmsg) {
         LinkedList<PcepValueType> tlvList = pOpenmsg.getPcepOpenObject().getOptionalTlv();
-        boolean bFoundPceccCapability = false;
-        boolean bFoundStatefulPceCapability = false;
-        boolean bFoundPcInstantiationCapability = false;
+        boolean pceccCapability = false;
+        boolean statefulPceCapability = false;
+        boolean pcInstantiationCapability = false;
 
         ListIterator<PcepValueType> listIterator = tlvList.listIterator();
         while (listIterator.hasNext()) {
@@ -511,21 +502,20 @@
 
             switch (tlv.getType()) {
             case PceccCapabilityTlv.TYPE:
-                bFoundPceccCapability = true;
+                pceccCapability = true;
                 break;
             case StatefulPceCapabilityTlv.TYPE:
-                bFoundStatefulPceCapability = true;
+                statefulPceCapability = true;
                 StatefulPceCapabilityTlv stetefulPcCapTlv = (StatefulPceCapabilityTlv) tlv;
                 if (stetefulPcCapTlv.getIFlag()) {
-                    bFoundPcInstantiationCapability = true;
+                    pcInstantiationCapability = true;
                 }
                 break;
             default:
                 continue;
             }
         }
-
-        return (bFoundPceccCapability && bFoundStatefulPceCapability && bFoundPcInstantiationCapability);
+        this.capability = new ClientCapability(pceccCapability, statefulPceCapability, pcInstantiationCapability);
     }
 
     /**
@@ -579,23 +569,6 @@
 
         llerrObj.add(errObj);
 
-        if (state == ChannelState.OPENWAIT) {
-            //If Error caught in Openmessage
-            PcepOpenObject openObj = null;
-            ErrorObjListWithOpen errorObjListWithOpen = null;
-
-            if (0 != sessionId) {
-                openObj = factory1.buildOpenObject().setSessionId(sessionId).build();
-                errorObjListWithOpen = new ErrorObjListWithOpen(llerrObj, openObj);
-            } else {
-                errorObjListWithOpen = new ErrorObjListWithOpen(llerrObj, null);
-            }
-
-            errMsg = factory1.buildPcepErrorMsg()
-                    .setErrorObjListWithOpen(errorObjListWithOpen)
-                    .build();
-        } else {
-
             //If Error caught in other than Openmessage
             LinkedList<PcepError> llPcepErr = new LinkedList<>();
 
@@ -612,7 +585,6 @@
             errMsg = factory1.buildPcepErrorMsg()
                     .setPcepErrorInfo(errInfo)
                     .build();
-        }
         return errMsg;
     }
 
diff --git a/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientControllerImpl.java b/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientControllerImpl.java
index d15e285..1f58bed 100644
--- a/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientControllerImpl.java
+++ b/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientControllerImpl.java
@@ -18,6 +18,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -31,12 +32,20 @@
 import org.onosproject.pcep.controller.PcepClientListener;
 import org.onosproject.pcep.controller.PcepEventListener;
 import org.onosproject.pcep.controller.driver.PcepAgent;
+import org.onosproject.pcepio.protocol.PcepError;
+import org.onosproject.pcepio.protocol.PcepErrorInfo;
+import org.onosproject.pcepio.protocol.PcepErrorMsg;
+import org.onosproject.pcepio.protocol.PcepErrorObject;
+import org.onosproject.pcepio.protocol.PcepFactory;
 import org.onosproject.pcepio.protocol.PcepMessage;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.Sets;
 
+import static org.onosproject.pcepio.types.PcepErrorDetailInfo.ERROR_TYPE_19;
+import static org.onosproject.pcepio.types.PcepErrorDetailInfo.ERROR_VALUE_5;
+
 /**
  * Implementation of PCEP client controller.
  */
@@ -126,6 +135,24 @@
             break;
         case ERROR:
             break;
+        case INITIATE:
+            if (!pc.capability().pcInstantiationCapability()) {
+                pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(), ERROR_TYPE_19,
+                        ERROR_VALUE_5)));
+            }
+            break;
+        case UPDATE:
+            if (!pc.capability().statefulPceCapability()) {
+                pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(), ERROR_TYPE_19,
+                        ERROR_VALUE_5)));
+            }
+            break;
+        case LABEL_UPDATE:
+            if (!pc.capability().pceccCapability()) {
+                pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(), ERROR_TYPE_19,
+                        ERROR_VALUE_5)));
+            }
+            break;
         case CLOSE:
             log.info("Sending Close Message  to {" + pccId.toString() + "}");
             pc.sendMessage(Collections.singletonList(pc.factory().buildCloseMsg().build()));
@@ -133,21 +160,18 @@
             pc.disconnectClient();
             break;
         case REPORT:
-            for (PcepEventListener l : pcepEventListener) {
-                l.handleMessage(pccId, msg);
+            //Only update the listener if respective capability is supported else send PCEP-ERR msg
+            if (pc.capability().statefulPceCapability()) {
+                for (PcepEventListener l : pcepEventListener) {
+                    l.handleMessage(pccId, msg);
+                }
+            } else {
+                // Send PCEP-ERROR message.
+                pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(),
+                        ERROR_TYPE_19, ERROR_VALUE_5)));
             }
             break;
-        case UPDATE:
-            for (PcepEventListener l : pcepEventListener) {
-                l.handleMessage(pccId, msg);
-            }
-            break;
-        case INITIATE:
-            for (PcepEventListener l : pcepEventListener) {
-                l.handleMessage(pccId, msg);
-            }
-            break;
-        case LABEL_UPDATE:
+        case LABEL_RANGE_RESERV:
             break;
         case MAX:
             break;
@@ -168,6 +192,34 @@
     }
 
     /**
+     * Returns pcep error message with specific error type and value.
+     *
+     * @param factory represents pcep factory
+     * @param errorType pcep error type
+     * @param errorValue pcep error value
+     * @return pcep error message
+     */
+    public PcepErrorMsg getErrMsg(PcepFactory factory, byte errorType, byte errorValue) {
+        LinkedList<PcepError> llPcepErr = new LinkedList<>();
+
+        LinkedList<PcepErrorObject> llerrObj = new LinkedList<>();
+        PcepErrorMsg errMsg;
+
+        PcepErrorObject errObj = factory.buildPcepErrorObject().setErrorValue(errorValue).setErrorType(errorType)
+                .build();
+
+        llerrObj.add(errObj);
+        PcepError pcepErr = factory.buildPcepError().setErrorObjList(llerrObj).build();
+
+        llPcepErr.add(pcepErr);
+
+        PcepErrorInfo errInfo = factory.buildPcepErrorInfo().setPcepErrorList(llPcepErr).build();
+
+        errMsg = factory.buildPcepErrorMsg().setPcepErrorInfo(errInfo).build();
+        return errMsg;
+    }
+
+    /**
      * Implementation of an Pcep Agent which is responsible for
      * keeping track of connected clients and the state in which
      * they are.
diff --git a/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientImpl.java b/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientImpl.java
index 8c807f6..23b6ba6 100644
--- a/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientImpl.java
+++ b/protocols/pcep/ctl/src/main/java/org/onosproject/pcep/controller/impl/PcepClientImpl.java
@@ -24,6 +24,7 @@
 
 import org.jboss.netty.channel.Channel;
 import org.onlab.packet.IpAddress;
+import org.onosproject.pcep.controller.ClientCapability;
 import org.onosproject.pcep.controller.PccId;
 import org.onosproject.pcep.controller.PcepPacketStats;
 import org.onosproject.pcep.controller.driver.PcepAgent;
@@ -57,6 +58,7 @@
     private PccId pccId;
     private PcepAgent agent;
 
+    private ClientCapability capability;
     private PcepVersion pcepVersion;
     private byte keepAliveTime;
     private byte deadTime;
@@ -76,6 +78,16 @@
     }
 
     @Override
+    public void setCapability(ClientCapability capability) {
+        this.capability = capability;
+    }
+
+    @Override
+    public ClientCapability capability() {
+        return capability;
+    }
+
+    @Override
     public final void sendMessage(PcepMessage m) {
         log.debug("Sending message to {}", channel.getRemoteAddress());
         try {
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/PcepErrorDetailInfo.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/PcepErrorDetailInfo.java
index 6fee5ba..1bf6ce8 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/PcepErrorDetailInfo.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/PcepErrorDetailInfo.java
@@ -57,6 +57,26 @@
     10     Reception of an invalid object
          Error-value=1: reception of an object with P flag not set although the P flag must be
                         set according to this specification.
+
+    Reference draft-ietf-pce-stateful-pce-11, section : 8.4
+    19    Invalid Operation
+         Error-value=1:  Attempted LSP Update Request for a non-
+                                 delegated LSP.  The PCEP-ERROR Object
+                                 is followed by the LSP Object that
+                                 identifies the LSP.
+         Error-value=2:  Attempted LSP Update Request if the
+                                 stateful PCE capability was not
+                                 advertised.
+         Error-value=3:  Attempted LSP Update Request for an LSP
+                                 identified by an unknown PLSP-ID.
+         Error-value=4:  A PCE indicates to a PCC that it has
+                                 exceeded the resource limit allocated
+                                 for its state, and thus it cannot
+                                 accept and process its LSP State Report
+                                 message.
+         Error-value=5:  Attempted LSP State Report if active
+                                 stateful PCE capability was not
+                                 advertised.
      */
     public static final byte ERROR_TYPE_1 = 1;
     public static final byte ERROR_TYPE_2 = 2;
@@ -68,6 +88,7 @@
     public static final byte ERROR_TYPE_8 = 8;
     public static final byte ERROR_TYPE_9 = 9;
     public static final byte ERROR_TYPE_10 = 10;
+    public static final byte ERROR_TYPE_19 = 19;
 
     // Error Values
     public static final byte ERROR_VALUE_1 = 1;