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);
+ }
+
+ }
+
+}
+