BMP per peer header decoder

Change-Id: I51deb93510cb79e438141e5d9fce20dec7284e43
diff --git a/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/PerPeerPacket.java b/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/PerPeerPacket.java
new file mode 100644
index 0000000..b9ee79a
--- /dev/null
+++ b/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/PerPeerPacket.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2024-present Open Networking Foundation
+ *
+ * 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.bgpmonitoring.type;
+
+
+import com.google.common.base.MoreObjects;
+
+import java.nio.ByteBuffer;
+import java.util.function.BiPredicate;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onosproject.bgpmonitoring.PerPeer;
+import org.onosproject.bgpmonitoring.PeerType;
+import org.onosproject.bgpmonitoring.BmpParseException;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * The per-peer header follows the common header for most BMP messages.
+ * The rest of the data in a BMP message is dependent on the Message
+ * Type field in the common header.
+ * <p>
+ * Peer Type (1 byte): Identifies the type of peer.  Currently, three
+ * types of peers are identified:
+ * <p>
+ * *  Peer Type = 0: Global Instance Peer
+ * *  Peer Type = 1: RD Instance Peer
+ * *  Peer Type = 2: Local Instance Peer
+ * <p>
+ * o  Peer Flags (1 byte): These flags provide more information about
+ * the peer.  The flags are defined as follows:
+ * <p>
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |V|L|A| Reserved|
+ * +-+-+-+-+-+-+-+-+
+ * <p>
+ * *  The V flag indicates that the Peer address is an IPv6 address.
+ * For IPv4 peers, this is set to 0.
+ * <p>
+ * The L flag, if set to 1, indicates that the message reflects
+ * the post-policy Adj-RIB-In (i.e., its path attributes reflect
+ * the application of inbound policy).  It is set to 0 if the
+ * message reflects the pre-policy Adj-RIB-In.  Locally sourced
+ * routes also carry an L flag of 1.  See Section 5 for further
+ * detail.  This flag has no significance when used with route
+ * mirroring messages.
+ * <p>
+ * *  The A flag, if set to 1, indicates that the message is
+ * formatted using the legacy 2-byte AS_PATH format.  If set to 0,
+ * the message is formatted using the 4-byte AS_PATH format
+ * [RFC6793].  A BMP speaker MAY choose to propagate the AS_PATH
+ * information as received from its peer, or it MAY choose to
+ * reformat all AS_PATH information into a 4-byte format
+ * regardless of how it was received from the peer.  In the latter
+ * case, AS4_PATH or AS4_AGGREGATOR path attributes SHOULD NOT be
+ * sent in the BMP UPDATE message.  This flag has no significance
+ * when used with route mirroring messages.
+ * <p>
+ * The remaining bits are reserved for future use.  They MUST be
+ * transmitted as 0 and their values MUST be ignored on receipt.
+ * <p>
+ * Peer Distinguisher (8 bytes): Routers today can have multiple
+ * instances (example: Layer 3 Virtual Private Networks (L3VPNs)
+ * [RFC4364]).  This field is present to distinguish peers that
+ * belong to one address domain from the other.
+ * <p>
+ * If the peer is a "Global Instance Peer", this field is zero-
+ * filled.  If the peer is a "RD Instance Peer", it is set to the
+ * route distinguisher of the particular instance the peer belongs
+ * to.  If the peer is a "Local Instance Peer", it is set to a
+ * unique, locally defined value.  In all cases, the effect is that
+ * the combination of the Peer Type and Peer Distinguisher is
+ * sufficient to disambiguate peers for which other identifying
+ * information might overlap.
+ * <p>
+ * Peer Address: The remote IP address associated with the TCP
+ * session over which the encapsulated PDU was received.  It is 4
+ * bytes long if an IPv4 address is carried in this field (with the
+ * 12 most significant bytes zero-filled) and 16 bytes long if an
+ * IPv6 address is carried in this field.
+ * <p>
+ * Peer AS: The Autonomous System number of the peer from which the
+ * encapsulated PDU was received.  If a 16-bit AS number is stored in
+ * this field [RFC6793], it should be padded with zeroes in the 16
+ * most significant bits.
+ * <p>
+ * Timestamp: The time when the encapsulated routes were received
+ * (one may also think of this as the time when they were installed
+ * in the Adj-RIB-In), expressed in seconds and microseconds since
+ * midnight (zero hour), January 1, 1970 (UTC).  If zero, the time is
+ * unavailable.  Precision of the timestamp is implementation-
+ * dependent.
+ */
+public final class PerPeerPacket extends BasePacket implements PerPeer {
+
+    /*
+      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
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |   Peer Type   |  Peer Flags   |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |         Peer Distinguisher (present based on peer type)       |
+     |                                                               |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                 Peer Address (16 bytes)                       |
+     ~                                                               ~
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                           Peer AS                             |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                         Peer BGP ID                           |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                    Timestamp (seconds)                        |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                  Timestamp (microseconds)                     |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     */
+    public static final int PEER_HEADER_MIN_LENGTH = 42;
+
+    public static final int PEER_DISTINGUISHER = 8;
+    public static final int IPV4_ADDRSZ = 4;
+    public static final int IPV6_ADDRSZ = 16;
+
+    private PeerType type;
+
+    private byte flags;
+
+    private byte[] peerDistinguisher;
+
+    private InetAddress peerAddress;
+
+    private int peerAs;
+
+    private String peerBgpId;
+
+    private int seconds;
+
+    private int microseconds;
+
+    private PerPeerPacket(Builder builder) {
+        this.type = builder.type;
+        this.flags = builder.flags;
+        this.peerDistinguisher = builder.peerDistinguisher;
+        this.peerAddress = builder.peerAddress;
+        this.peerAs = builder.peerAs;
+        this.peerBgpId = builder.peerBgpId;
+        this.seconds = builder.seconds;
+        this.microseconds = builder.microseconds;
+
+    }
+
+    @Override
+    public PeerType getType() {
+        return type;
+    }
+
+    @Override
+    public byte getFlag() {
+        return flags;
+    }
+
+    @Override
+    public byte[] getPeerDistinguisher() {
+        return peerDistinguisher;
+    }
+
+    @Override
+    public InetAddress getIntAddress() {
+        return peerAddress;
+    }
+
+    @Override
+    public int getPeerAs() {
+        return peerAs;
+    }
+
+    @Override
+    public String getPeerBgpId() {
+        return peerBgpId;
+    }
+
+    @Override
+    public int getSeconds() {
+        return seconds;
+    }
+
+    @Override
+    public int getMicroseconds() {
+        return microseconds;
+    }
+
+    @Override
+    public boolean isIpv6() {
+        return ((flags & 0x80) != 0x00);
+    }
+
+    /**
+     * Data deserializer function for BMP per peer header.
+     *
+     * @return data deserializer function
+     */
+    public static Deserializer<PerPeerPacket> deserializer() {
+        return (data, offset, length) -> {
+            BiPredicate<ByteBuffer, Integer> isValidBuffer = (b, l)
+                    -> b.hasRemaining() && b.remaining() >= l;
+
+            ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+            if (!isValidBuffer.test(bb, PEER_HEADER_MIN_LENGTH)) {
+                throw new BmpParseException("Invalid bmp per peer header buffer size.");
+            }
+            Builder builder = new Builder();
+
+            return builder.type(PeerType.getType((int) bb.get()))
+                    .flags(bb.get())
+                    .peerDistinguisher(bb)
+                    .peerAddress(bb)
+                    .peerAs(bb.getInt())
+                    .peerBgpId(bb.getInt())
+                    .seconds(bb.getInt())
+                    .microseconds(bb.getInt())
+                    .build();
+        };
+    }
+
+    @Override
+    public byte[] serialize() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+
+    @Override
+    public String toString() {
+
+        return MoreObjects.toStringHelper(getClass())
+                .add("flags", flags)
+                .add("type", type)
+                .add("peerAddress", peerAddress.getHostAddress())
+                .add("peerAs", peerAs)
+                .add("seconds", seconds)
+                .add("microseconds", microseconds)
+                .toString();
+    }
+
+    public static InetAddress toInetAddress(int length, ByteBuffer bb) {
+        byte[] address = new byte[length];
+        bb.get(address);
+
+        InetAddress ipAddress = null;
+        try {
+            ipAddress = InetAddress.getByAddress(address);
+        } catch (UnknownHostException ex) {
+            throw new BmpParseException(ex);
+        }
+
+        return ipAddress;
+    }
+
+    /**
+     * Builder for BMP per peer header.
+     */
+    private static class Builder {
+
+        private PeerType type;
+
+        private byte flags;
+
+        private byte[] peerDistinguisher;
+
+        private InetAddress peerAddress;
+
+        private int peerAs;
+
+        private String peerBgpId;
+
+        private int seconds;
+
+        private int microseconds;
+
+
+        /**
+         * Setter bmp per peer header.
+         *
+         * @param perPeer bmp per peer header.
+         * @return this class builder.
+         */
+        public Builder type(PeerType type) {
+            this.type = type;
+            return this;
+        }
+
+        /**
+         * Setter bmp per peer header flag.
+         *
+         * @param flags bmp peer per header flag.
+         * @return this class builder.
+         */
+        public Builder flags(byte flags) {
+            this.flags = flags;
+            return this;
+        }
+
+        /**
+         * Setter bgp peer distinguisher.
+         *
+         * @param bb byte buffer.
+         * @return this class builder.
+         */
+        public Builder peerDistinguisher(ByteBuffer bb) {
+            this.peerDistinguisher = new byte[PEER_DISTINGUISHER];
+            bb.get(peerDistinguisher);
+            return this;
+        }
+
+        /**
+         * Setter bgp peer address.
+         *
+         * @param bb byte buffer.
+         * @return this class builder.
+         */
+        public Builder peerAddress(ByteBuffer bb) {
+            if ((flags & 0x80) != 0x00) {
+                this.peerAddress = toInetAddress(IPV6_ADDRSZ, bb);
+            } else {
+                bb.position(bb.position() + (IPV6_ADDRSZ - IPV4_ADDRSZ));
+                this.peerAddress = toInetAddress(IPV4_ADDRSZ, bb);
+            }
+            return this;
+        }
+
+        /**
+         * Setter bgp peer As.
+         *
+         * @param peerAs peer As.
+         * @return this class builder.
+         */
+        public Builder peerAs(int peerAs) {
+            this.peerAs = peerAs;
+            return this;
+        }
+
+        /**
+         * Setter bgp router id.
+         *
+         * @param id bgp router id.
+         * @return this class builder.
+         */
+        public Builder peerBgpId(int id) {
+            StringBuilder sb = new StringBuilder();
+            int result = 0;
+            for (int i = 0; i < 4; ++i) {
+                result = id >> (3 - i) * 8 & 0xff;
+                sb.append(result);
+                if (i != 3) {
+                    sb.append(".");
+                }
+            }
+            this.peerBgpId = sb.toString();
+            return this;
+        }
+
+        /**
+         * Setter bmp per peer message sent time in seconds.
+         *
+         * @param seconds bmp per peer message sent time in seconds.
+         * @return this class builder.
+         */
+        public Builder seconds(int seconds) {
+            this.seconds = seconds;
+            return this;
+        }
+
+        /**
+         * Setter bmp per peer message sent time in micro seconds.
+         *
+         * @param microseconds bmp per peer message sent time in micro seconds.
+         * @return this class builder.
+         */
+        public Builder microseconds(int microseconds) {
+            this.microseconds = microseconds;
+            return this;
+        }
+
+        /**
+         * Checks arguments for bmp per peer header.
+         */
+        private void checkArguments() {
+            checkState(type != null, "Invalid bmp per peer type.");
+            checkState(peerAddress != null, "Invalid bmp per peer address.");
+        }
+
+        /**
+         * Builds bmp per peer header.
+         *
+         * @return bmp per peer header.
+         */
+        public PerPeerPacket build() {
+            checkArguments();
+            return new PerPeerPacket(this);
+        }
+
+    }
+
+}
+