[Emu] [ONOS-2600] Implement Basic BGP Update protocol message parsing and decode excluding path attributes.

Change-Id: I37ed38cb79583fbbb31057dafc06df245aef3c49
diff --git a/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BgpUpdateMsgVer4.java b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BgpUpdateMsgVer4.java
new file mode 100644
index 0000000..9f4cf9b
--- /dev/null
+++ b/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BgpUpdateMsgVer4.java
@@ -0,0 +1,285 @@
+/*
+ * 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.protocol.ver4;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.protocol.BGPMessageReader;
+import org.onosproject.bgpio.protocol.BGPType;
+import org.onosproject.bgpio.protocol.BgpUpdateMsg;
+import org.onosproject.bgpio.types.BGPErrorType;
+import org.onosproject.bgpio.types.BGPHeader;
+import org.onosproject.bgpio.util.Validation;
+import org.onosproject.bgpio.protocol.BGPVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * BGP Update Message: UPDATE messages are used to transfer routing information
+ * between BGP peers. The information in the UPDATE message is used by core to
+ * construct a graph
+ */
+public class BgpUpdateMsgVer4 implements BgpUpdateMsg {
+
+    /*      0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    +                                                               +
+    |                                                               |
+    +                                                               +
+    |                           Marker                              |
+    +                                                               +
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |          Length               |      Type     |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   Withdrawn Routes Length (2 octets)                |
+    +-----------------------------------------------------+
+    |   Withdrawn Routes (variable)                       |
+    +-----------------------------------------------------+
+    |   Total Path Attribute Length (2 octets)            |
+    +-----------------------------------------------------+
+    |   Path Attributes (variable)                        |
+    +-----------------------------------------------------+
+    |   Network Layer Reachability Information (variable) |
+    +-----------------------------------------------------+
+    REFERENCE : RFC 4271
+    */
+
+    protected static final Logger log = LoggerFactory
+            .getLogger(BgpUpdateMsgVer4.class);
+
+    public static final byte PACKET_VERSION = 4;
+    //Withdrawn Routes Length(2) + Total Path Attribute Length(2)
+    public static final int PACKET_MINIMUM_LENGTH = 4;
+    public static final int BYTE_IN_BITS = 8;
+    public static final int MIN_LEN_AFTER_WITHDRW_ROUTES = 2;
+    public static final int MINIMUM_COMMON_HEADER_LENGTH = 19;
+    public static final BGPType MSG_TYPE = BGPType.UPDATE;
+    public static final BgpUpdateMsgVer4.Reader READER = new Reader();
+
+    private List<IpPrefix> withdrawnRoutes;
+    private BgpPathAttributes bgpPathAttributes;
+    private BGPHeader bgpHeader;
+    private List<IpPrefix> nlri;
+
+    /**
+     * Constructor to initialize parameters for BGP Update message.
+     *
+     * @param bgpHeader in Update message
+     * @param withdrawnRoutes withdrawn routes
+     * @param bgpPathAttributes BGP Path attributes
+     * @param nlri Network Layer Reachability Information
+     */
+    public BgpUpdateMsgVer4(BGPHeader bgpHeader, List<IpPrefix> withdrawnRoutes,
+                     BgpPathAttributes bgpPathAttributes, List<IpPrefix> nlri) {
+        this.bgpHeader = bgpHeader;
+        this.withdrawnRoutes = withdrawnRoutes;
+        this.bgpPathAttributes = bgpPathAttributes;
+        this.nlri = nlri;
+    }
+
+    /**
+     * Reader reads BGP Update Message from the channel buffer.
+     */
+    static class Reader implements BGPMessageReader<BgpUpdateMsg> {
+
+        @Override
+        public BgpUpdateMsg readFrom(ChannelBuffer cb, BGPHeader bgpHeader)
+                throws BGPParseException {
+
+            if (cb.readableBytes() != (bgpHeader.getLength() - MINIMUM_COMMON_HEADER_LENGTH)) {
+                Validation.validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                        BGPErrorType.BAD_MESSAGE_LENGTH, bgpHeader.getLength());
+            }
+
+            LinkedList<IpPrefix> withDrwRoutes = new LinkedList<>();
+            LinkedList<IpPrefix> nlri = new LinkedList<>();
+            BgpPathAttributes bgpPathAttributes = new BgpPathAttributes();
+            // Reading Withdrawn Routes Length
+            Short withDrwLen = cb.readShort();
+
+            if (cb.readableBytes() < withDrwLen) {
+                Validation.validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                        BGPErrorType.MALFORMED_ATTRIBUTE_LIST,
+                        cb.readableBytes());
+            }
+            ChannelBuffer tempCb = cb.readBytes(withDrwLen);
+            if (withDrwLen != 0) {
+                // Parsing WithdrawnRoutes
+                withDrwRoutes = parseWithdrawnRoutes(tempCb);
+            }
+            if (cb.readableBytes() < MIN_LEN_AFTER_WITHDRW_ROUTES) {
+                log.debug("Bgp Path Attribute len field not present");
+                throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                        BGPErrorType.MALFORMED_ATTRIBUTE_LIST, null);
+            }
+
+            // Reading Total Path Attribute Length
+            short totPathAttrLen = cb.readShort();
+            int len = withDrwLen + totPathAttrLen + PACKET_MINIMUM_LENGTH;
+            if (len > bgpHeader.getLength()) {
+                throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                        BGPErrorType.MALFORMED_ATTRIBUTE_LIST, null);
+            }
+            if (totPathAttrLen != 0) {
+                // Parsing BGPPathAttributes
+                if (cb.readableBytes() < totPathAttrLen) {
+                    Validation
+                            .validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                                         BGPErrorType.MALFORMED_ATTRIBUTE_LIST,
+                                         cb.readableBytes());
+                }
+                tempCb = cb.readBytes(totPathAttrLen);
+                bgpPathAttributes = BgpPathAttributes.read(tempCb);
+            }
+            if (cb.readableBytes() > 0) {
+                // Parsing NLRI
+                nlri = parseNlri(cb);
+            }
+            return new BgpUpdateMsgVer4(bgpHeader, withDrwRoutes,
+                    bgpPathAttributes, nlri);
+        }
+    }
+
+    /**
+     * Parses NLRI from channel buffer.
+     *
+     * @param cb channelBuffer
+     * @return list of IP Prefix
+     * @throws BGPParseException while parsing NLRI
+     */
+    public static LinkedList<IpPrefix> parseNlri(ChannelBuffer cb)
+            throws BGPParseException {
+        LinkedList<IpPrefix> nlri = new LinkedList<>();
+        while (cb.readableBytes() > 0) {
+            int length = cb.readByte();
+            IpPrefix ipPrefix;
+            if (length == 0) {
+                byte[] prefix = new byte[] {0};
+                ipPrefix = Validation.bytesToPrefix(prefix, length);
+                nlri.add(ipPrefix);
+            } else {
+                int len = length / BYTE_IN_BITS;
+                int reminder = length % BYTE_IN_BITS;
+                if (reminder > 0) {
+                    len = len + 1;
+                }
+                if (cb.readableBytes() < len) {
+                    Validation.validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                            BGPErrorType.MALFORMED_ATTRIBUTE_LIST,
+                            cb.readableBytes());
+                }
+                byte[] prefix = new byte[len];
+                cb.readBytes(prefix, 0, len);
+                ipPrefix = Validation.bytesToPrefix(prefix, length);
+                nlri.add(ipPrefix);
+            }
+        }
+        return nlri;
+    }
+
+    /**
+     * Parsing withdrawn routes from channel buffer.
+     *
+     * @param cb channelBuffer
+     * @return list of IP prefix
+     * @throws BGPParseException while parsing withdrawn routes
+     */
+    public static LinkedList<IpPrefix> parseWithdrawnRoutes(ChannelBuffer cb)
+            throws BGPParseException {
+        LinkedList<IpPrefix> withDrwRoutes = new LinkedList<>();
+        while (cb.readableBytes() > 0) {
+            int length = cb.readByte();
+            IpPrefix ipPrefix;
+            if (length == 0) {
+                byte[] prefix = new byte[] {0};
+                ipPrefix = Validation.bytesToPrefix(prefix, length);
+                withDrwRoutes.add(ipPrefix);
+            } else {
+                int len = length / BYTE_IN_BITS;
+                int reminder = length % BYTE_IN_BITS;
+                if (reminder > 0) {
+                    len = len + 1;
+                }
+                if (cb.readableBytes() < len) {
+                    Validation
+                            .validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                                         BGPErrorType.MALFORMED_ATTRIBUTE_LIST,
+                                         cb.readableBytes());
+                }
+                byte[] prefix = new byte[len];
+                cb.readBytes(prefix, 0, len);
+                ipPrefix = Validation.bytesToPrefix(prefix, length);
+                withDrwRoutes.add(ipPrefix);
+            }
+        }
+        return withDrwRoutes;
+    }
+
+    @Override
+    public BGPVersion getVersion() {
+        return BGPVersion.BGP_4;
+    }
+
+    @Override
+    public BGPType getType() {
+        return BGPType.UPDATE;
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer channelBuffer) throws BGPParseException {
+        //Not to be implemented as of now
+    }
+
+    @Override
+    public BgpPathAttributes bgpPathAttributes() {
+        return this.bgpPathAttributes;
+    }
+
+    @Override
+    public List<IpPrefix> withdrawnRoutes() {
+        return withdrawnRoutes;
+    }
+
+    @Override
+    public List<IpPrefix> nlri() {
+        return nlri;
+    }
+
+    @Override
+    public BGPHeader getHeader() {
+        return this.bgpHeader;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .omitNullValues()
+                .add("bgpHeader", bgpHeader)
+                .add("withDrawnRoutes", withdrawnRoutes)
+                .add("nlri", nlri)
+                .add("bgpPathAttributes", bgpPathAttributes)
+                .toString();
+    }
+}