[Emu] [ONOS-2598] Implementation of BGP Capabilities parsing, decoding and encoding

Change-Id: I5893c490f97ad23a0180b847e8b8af9b00560806
diff --git a/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java
index c41e5eb..2271b60 100644
--- a/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java
+++ b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.onosproject.bgpio.protocol;
 
 import java.util.LinkedList;
@@ -91,8 +90,7 @@
         /**
          * Sets hold time in Open Message and return its builder.
          *
-         * @param holdtime
-         *           hold timer value in open message
+         * @param holdtime hold timer value in open message
          * @return builder by setting hold time
          */
         Builder setHoldTime(short holdtime);
@@ -107,8 +105,7 @@
         /**
          * Sets AS number in Open Message and return its builder.
          *
-         * @param asNumber
-         *           as number in open message
+         * @param asNumber as number in open message
          * @return builder by setting asNumber
          */
         Builder setAsNumber(short asNumber);
@@ -123,8 +120,7 @@
         /**
          * Sets BGP Identifier in Open Message and return its builder.
          *
-         * @param bgpId
-         *           BGP Identifier in open message
+         * @param bgpId BGP Identifier in open message
          * @return builder by setting BGP Identifier
          */
         Builder setBgpId(int bgpId);
@@ -139,12 +135,29 @@
         /**
          * Sets capabilities in Open Message and return its builder.
          *
-         * @param capabilityTlv
-         *           capabilities in open message
+         * @param capabilityTlv capabilities in open message
          * @return builder by setting capabilities
          */
         Builder setCapabilityTlv(LinkedList<BGPValueType> capabilityTlv);
 
+        /**
+         * Sets isLargeAsCapabilityTlvSet and return its builder.
+         *
+         * @param isLargeAsCapabilitySet
+         *           boolean value to know whether large AS capability is set or not
+         * @return builder by setting capabilities
+         */
+        Builder setLargeAsCapabilityTlv(boolean isLargeAsCapabilitySet);
+
+        /**
+         * Sets isLsCapabilityTlvSet and return its builder.
+         *
+         * @param isLsCapabilitySet
+         *           boolean value to know whether LS capability is set or not
+         * @return builder by setting capabilities
+         */
+        Builder setLsCapabilityTlv(boolean isLsCapabilitySet);
+
         @Override
         Builder setHeader(BGPHeader bgpMsgHeader);
     }
diff --git a/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java
index 1348ecf..bd22889 100644
--- a/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java
+++ b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.onosproject.bgpio.protocol.ver4;
 
 import java.util.LinkedList;
@@ -29,6 +28,8 @@
 import org.onosproject.bgpio.types.BGPErrorType;
 import org.onosproject.bgpio.types.BGPHeader;
 import org.onosproject.bgpio.types.BGPValueType;
+import org.onosproject.bgpio.types.FourOctetAsNumCapabilityTlv;
+import org.onosproject.bgpio.types.MultiProtocolExtnCapabilityTlv;
 import org.onosproject.bgpio.util.Validation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,8 +68,13 @@
     public static final int MSG_HEADER_LENGTH = 19;
     public static final int MARKER_LENGTH  = 16;
     public static final int DEFAULT_HOLD_TIME = 120;
+    public static final short AS_TRANS = 23456;
     public static final int OPT_PARA_TYPE_CAPABILITY = 2;
     public static final BGPType MSG_TYPE = BGPType.OPEN;
+    public static final short AFI = 16388;
+    public static final byte SAFI = 71;
+    public static final byte RES = 0;
+    public static final int FOUR_OCTET_AS_NUM_CAPA_TYPE = 65;
     public static final byte[] MARKER = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
         (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
         (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
@@ -79,6 +85,7 @@
     private short asNumber;
     private short holdTime;
     private int bgpId;
+    private boolean isLargeAsCapabilitySet;
     private LinkedList<BGPValueType> capabilityTlv;
 
     public static final BGPOpenMsgVer4.Reader READER = new Reader();
@@ -98,18 +105,12 @@
     /**
      * Constructor to initialize all variables of BGP Open message.
      *
-     * @param bgpMsgHeader
-     *           BGP Header in open message
-     * @param version
-     *           BGP version in open message
-     * @param holdTime
-     *           hold time in open message
-     * @param asNumber
-     *           AS number in open message
-     * @param bgpId
-     *           BGP identifier in open message
-     * @param capabilityTlv
-     *           capabilities in open message
+     * @param bgpMsgHeader BGP Header in open message
+     * @param version BGP version in open message
+     * @param holdTime hold time in open message
+     * @param asNumber AS number in open message
+     * @param bgpId BGP identifier in open message
+     * @param capabilityTlv capabilities in open message
      */
     public BGPOpenMsgVer4(BGPHeader bgpMsgHeader, byte version, short asNumber, short holdTime,
              int bgpId, LinkedList<BGPValueType> capabilityTlv) {
@@ -236,7 +237,44 @@
 
         LinkedList<BGPValueType> capabilityTlv = new LinkedList<>();
 
-        // TODO: Capability parsing
+        while (cb.readableBytes() > 0) {
+            BGPValueType tlv;
+            short type = cb.readByte();
+            short length = cb.readByte();
+
+            switch (type) {
+            case FourOctetAsNumCapabilityTlv.TYPE:
+                log.debug("FourOctetAsNumCapabilityTlv");
+                if (FourOctetAsNumCapabilityTlv.LENGTH != length) {
+                    throw new BGPParseException("Invalid length received for FourOctetAsNumCapabilityTlv.");
+                }
+                if (length > cb.readableBytes()) {
+                    throw new BGPParseException("Four octet as num tlv length"
+                            + " is more than readableBytes.");
+                }
+                int as4Num = cb.readInt();
+                tlv = new FourOctetAsNumCapabilityTlv(as4Num);
+                break;
+            case MultiProtocolExtnCapabilityTlv.TYPE:
+                log.debug("MultiProtocolExtnCapabilityTlv");
+                if (MultiProtocolExtnCapabilityTlv.LENGTH != length) {
+                    throw new BGPParseException("Invalid length received for MultiProtocolExtnCapabilityTlv.");
+                }
+                if (length > cb.readableBytes()) {
+                    throw new BGPParseException("BGP LS tlv length is more than readableBytes.");
+                }
+                short afi = cb.readShort();
+                byte res = cb.readByte();
+                byte safi = cb.readByte();
+                tlv = new MultiProtocolExtnCapabilityTlv(afi, res, safi);
+                break;
+            default:
+                log.debug("Warning: Unsupported TLV: " + type);
+                cb.skipBytes(length);
+                continue;
+            }
+            capabilityTlv.add(tlv);
+        }
         return capabilityTlv;
     }
 
@@ -248,11 +286,13 @@
         private boolean isHeaderSet = false;
         private BGPHeader bgpMsgHeader;
         private boolean isHoldTimeSet = false;
-        private short  holdTime;
+        private short holdTime;
         private boolean isAsNumSet = false;
         private short asNumber;
         private boolean isBgpIdSet = false;
         private int bgpId;
+        private boolean isLargeAsCapabilityTlvSet = false;
+        private boolean isLsCapabilityTlvSet = false;
 
         LinkedList<BGPValueType> capabilityTlv = new LinkedList<>();
 
@@ -269,7 +309,18 @@
                 throw new BGPParseException("BGPID  is not set (mandatory)");
             }
 
-            // TODO: capabilities build
+            if (this.isLargeAsCapabilityTlvSet) {
+                BGPValueType tlv;
+                int iValue = this.getAsNumber();
+                tlv = new FourOctetAsNumCapabilityTlv(iValue);
+                this.capabilityTlv.add(tlv);
+            }
+
+            if (this.isLsCapabilityTlvSet) {
+                BGPValueType tlv;
+                tlv = new MultiProtocolExtnCapabilityTlv(AFI, RES, SAFI);
+                this.capabilityTlv.add(tlv);
+            }
 
             return new BGPOpenMsgVer4(bgpMsgHeader, PACKET_VERSION, this.asNumber, holdTime, this.bgpId,
                        this.capabilityTlv);
@@ -342,6 +393,18 @@
             this.capabilityTlv = capabilityTlv;
             return this;
         }
+
+        @Override
+        public Builder setLargeAsCapabilityTlv(boolean isLargeAsCapabilitySet) {
+            this.isLargeAsCapabilityTlvSet = isLargeAsCapabilitySet;
+            return this;
+        }
+
+        @Override
+        public Builder setLsCapabilityTlv(boolean isLsCapabilitySet) {
+            this.isLsCapabilityTlvSet = isLsCapabilitySet;
+            return this;
+        }
     }
 
     @Override
@@ -364,6 +427,7 @@
         public void write(ChannelBuffer cb, BGPOpenMsgVer4 message) throws BGPParseException {
 
             int optParaLen = 0;
+            int as4num = 0;
 
             int startIndex = cb.writerIndex();
 
@@ -377,8 +441,29 @@
             // write version in 1-octet
             cb.writeByte(message.version);
 
-            // TODO : Write AS number based on capabilities
-            cb.writeShort(message.asNumber);
+            // get as4num if LS Capability is set
+            if (message.isLargeAsCapabilitySet) {
+                LinkedList<BGPValueType> capabilityTlv = message
+                        .getCapabilityTlv();
+                ListIterator<BGPValueType> listIterator = capabilityTlv
+                        .listIterator();
+
+                while (listIterator.hasNext()) {
+                    BGPValueType tlv = listIterator.next();
+                    if (tlv.getType() == FOUR_OCTET_AS_NUM_CAPA_TYPE) {
+                        as4num = ((FourOctetAsNumCapabilityTlv) tlv).getInt();
+                        break;
+                    }
+                }
+            }
+
+            if ((message.isLargeAsCapabilitySet) && (as4num > 65535)) {
+                // write As number as AS_TRANS
+                cb.writeShort(AS_TRANS);
+            } else {
+                // write AS number in next 2-octet
+                cb.writeShort(message.asNumber);
+            }
 
             // write HoldTime in next 2-octet
             cb.writeShort(message.holdTime);
diff --git a/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/FourOctetAsNumCapabilityTlv.java b/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/FourOctetAsNumCapabilityTlv.java
new file mode 100755
index 0000000..6157028
--- /dev/null
+++ b/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/FourOctetAsNumCapabilityTlv.java
@@ -0,0 +1,114 @@
+/*

+ * Copyright 2015 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.bgpio.types;

+

+import java.util.Objects;

+

+import org.jboss.netty.buffer.ChannelBuffer;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import com.google.common.base.MoreObjects;

+

+/**

+ * Provides FourOctetAsNumCapabilityTlv Capability Tlv.

+ */

+public class FourOctetAsNumCapabilityTlv implements BGPValueType {

+

+    /**

+     * support to indicate its support for four-octet AS numbers -CAPABILITY TLV format.

+     */

+    protected static final Logger log = LoggerFactory

+            .getLogger(FourOctetAsNumCapabilityTlv.class);

+

+    public static final byte TYPE = 65;

+    public static final byte LENGTH = 4;

+

+    private final int rawValue;

+

+    /**

+     * constructor to initialize rawValue.

+     * @param rawValue FourOctetAsNumCapabilityTlv

+     */

+    public FourOctetAsNumCapabilityTlv(int rawValue) {

+        this.rawValue = rawValue;

+    }

+

+    /**

+     * constructor to initialize raw.

+     * @param raw AS number

+     * @return object of FourOctetAsNumCapabilityTlv

+     */

+    public static FourOctetAsNumCapabilityTlv of(final int raw) {

+        return new FourOctetAsNumCapabilityTlv(raw);

+    }

+

+    /**

+     * Returns value of TLV.

+     * @return int value of rawValue

+     */

+    public int getInt() {

+        return rawValue;

+    }

+

+    @Override

+    public short getType() {

+        return TYPE;

+    }

+

+    @Override

+    public int hashCode() {

+        return Objects.hash(rawValue);

+    }

+

+    @Override

+    public boolean equals(Object obj) {

+        if (this == obj) {

+            return true;

+        }

+        if (obj instanceof FourOctetAsNumCapabilityTlv) {

+            FourOctetAsNumCapabilityTlv other = (FourOctetAsNumCapabilityTlv) obj;

+            return Objects.equals(rawValue, other.rawValue);

+        }

+        return false;

+    }

+

+    @Override

+    public int write(ChannelBuffer cb) {

+        int iLenStartIndex = cb.writerIndex();

+        cb.writeByte(TYPE);

+        cb.writeByte(LENGTH);

+        cb.writeInt(rawValue);

+        return cb.writerIndex() - iLenStartIndex;

+    }

+

+    /**

+     * Reads the channel buffer and returns object of FourOctetAsNumCapabilityTlv.

+     * @param cb type of channel buffer

+     * @return object of FourOctetAsNumCapabilityTlv

+     */

+    public static FourOctetAsNumCapabilityTlv read(ChannelBuffer cb) {

+        return FourOctetAsNumCapabilityTlv.of(cb.readInt());

+    }

+

+    @Override

+    public String toString() {

+        return MoreObjects.toStringHelper(getClass())

+                .add("Type", TYPE)

+                .add("Length", LENGTH)

+                .add("Value", rawValue).toString();

+    }

+}

diff --git a/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MultiProtocolExtnCapabilityTlv.java b/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MultiProtocolExtnCapabilityTlv.java
new file mode 100755
index 0000000..9beff68
--- /dev/null
+++ b/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MultiProtocolExtnCapabilityTlv.java
@@ -0,0 +1,160 @@
+/*

+ * Copyright 2015 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.bgpio.types;

+

+import java.util.Objects;

+import org.jboss.netty.buffer.ChannelBuffer;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import com.google.common.base.MoreObjects;

+

+/**

+ * Provides MultiProtocolExtnCapabilityTlv.

+ */

+public class MultiProtocolExtnCapabilityTlv implements BGPValueType {

+

+    /*

+        0       7       15      23      31

+        +-------+-------+-------+-------+

+        |  AFI          | Res   |  SAFI |

+        +-------+-------+-------+-------+

+

+        Multiprotocol Extensions CAPABILITY TLV format

+        REFERENCE : RFC 4760

+     */

+    protected static final Logger log = LoggerFactory

+            .getLogger(MultiProtocolExtnCapabilityTlv.class);

+

+    public static final byte TYPE = 1;

+    public static final byte LENGTH = 4;

+

+    private final short afi;

+    private final byte res;

+    private final byte safi;

+

+    /**

+     * Constructor to initialize variables.

+     * @param afi Address Family Identifiers

+     * @param res reserved field

+     * @param safi Subsequent Address Family Identifier

+     */

+    public MultiProtocolExtnCapabilityTlv(short afi, byte res, byte safi) {

+        this.afi = afi;

+        this.res = res;

+        this.safi = safi;

+    }

+

+    /**

+     * Returns object of MultiProtocolExtnCapabilityTlv.

+     * @param afi Address Family Identifiers

+     * @param res reserved field

+     * @param safi Subsequent Address Family Identifier

+     * @return object of MultiProtocolExtnCapabilityTlv

+     */

+    public static MultiProtocolExtnCapabilityTlv of(short afi, byte res,

+                                                    byte safi) {

+        return new MultiProtocolExtnCapabilityTlv(afi, res, safi);

+    }

+

+    /**

+     * Returns afi Address Family Identifiers value.

+     * @return afi Address Family Identifiers value

+     */

+    public short getAFI() {

+        return afi;

+    }

+

+    /**

+     * Returns res reserved field value.

+     * @return res reserved field value

+     */

+    public byte getRes() {

+        return res;

+    }

+

+    /**

+     * Returns safi Subsequent Address Family Identifier value.

+     * @return safi Subsequent Address Family Identifier value

+     */

+    public byte getSAFI() {

+        return safi;

+    }

+

+    @Override

+    public short getType() {

+        return TYPE;

+    }

+

+    @Override

+    public int hashCode() {

+        return Objects.hash(afi, res, safi);

+    }

+

+    @Override

+    public boolean equals(Object obj) {

+        if (this == obj) {

+            return true;

+        }

+        if (obj instanceof MultiProtocolExtnCapabilityTlv) {

+            MultiProtocolExtnCapabilityTlv other = (MultiProtocolExtnCapabilityTlv) obj;

+            return Objects.equals(this.afi, other.afi)

+                    && Objects.equals(this.res, other.res)

+                    && Objects.equals(this.safi, other.safi);

+        }

+        return false;

+    }

+

+    @Override

+    public int write(ChannelBuffer cb) {

+        int iLenStartIndex = cb.writerIndex();

+        cb.writeByte(TYPE);

+        cb.writeByte(LENGTH);

+

+        // write afi

+        cb.writeShort(afi);

+

+        // write res

+        cb.writeByte(res);

+

+        // write safi

+        cb.writeByte(safi);

+

+        return cb.writerIndex() - iLenStartIndex;

+    }

+

+    /**

+     * Reads from channel buffer and returns object of MultiprotocolCapabilityTlv.

+     * @param cb of type channel buffer

+     * @return object of MultiProtocolExtnCapabilityTlv

+     */

+    public static BGPValueType read(ChannelBuffer cb) {

+        short afi = cb.readShort();

+        byte res = cb.readByte();

+        byte safi = cb.readByte();

+        return new MultiProtocolExtnCapabilityTlv(afi, res, safi);

+    }

+

+    @Override

+    public String toString() {

+        return MoreObjects.toStringHelper(getClass())

+                .add("Type", TYPE)

+                .add("Length", LENGTH)

+                .add("AFI", afi)

+                .add("Reserved", res)

+                .add("SAFI", safi).toString();

+    }

+}