BMP statistics record decoder
Change-Id: Iafee32f83e97fd1fb2b29433eaf8b3774c8dd2ac
diff --git a/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/BmpStatsReport.java b/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/BmpStatsReport.java
new file mode 100644
index 0000000..54f46c1
--- /dev/null
+++ b/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/BmpStatsReport.java
@@ -0,0 +1,327 @@
+/*
+ * 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 com.google.common.collect.Lists;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.IntStream;
+import java.util.function.BiPredicate;
+import java.nio.ByteBuffer;
+
+import org.onosproject.bgpmonitoring.StatsMessage;
+import org.onosproject.bgpmonitoring.BmpStats;
+import org.onosproject.bgpmonitoring.PerPeer;
+import org.onosproject.bgpmonitoring.BmpParseException;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * An ongoing dump of statistics that can be used
+ * by the monitoring station as a high-level indication of the
+ * activity going on in the router.
+ * <p>
+ * These messages contain information that could be used by the
+ * monitoring station to observe interesting events that occur on the
+ * router.
+ * <p>
+ * Transmission of SR messages could be timer triggered or event driven
+ * (for example, when a significant event occurs or a threshold is
+ * reached). This specification does not impose any timing restrictions
+ * on when and on what event these reports have to be transmitted. It
+ * is left to the implementation to determine transmission timings --
+ * however, configuration control should be provided of the timer and/or
+ * threshold values. This document only specifies the form and content
+ * of SR messages.
+ * <p>
+ * Following the common BMP header and per-peer header is a 4-byte field
+ * that indicates the number of counters in the stats message where each
+ * counter is encoded as a TLV.
+ * <p>
+ * Stat Type = 1: (32-bit Counter) Number of (known) duplicate prefix
+ * advertisements.
+ * <p>
+ * Stat Type = 2: (32-bit Counter) Number of (known) duplicate
+ * withdraws.
+ * <p>
+ * Stat Type = 3: (32-bit Counter) Number of updates invalidated due
+ * to CLUSTER_LIST loop.
+ * <p>
+ * Stat Type = 4: (32-bit Counter) Number of updates invalidated due
+ * to AS_PATH loop.
+ * <p>
+ * Stat Type = 5: (32-bit Counter) Number of updates invalidated due
+ * to ORIGINATOR_ID.
+ * <p>
+ * Stat Type = 6: (32-bit Counter) Number of updates invalidated due
+ * to AS_CONFED loop.
+ * <p>
+ * Stat Type = 7: (64-bit Gauge) Number of routes in Adj-RIBs-In.
+ * <p>
+ * Stat Type = 8: (64-bit Gauge) Number of routes in Loc-RIB.
+ * <p>
+ * Stat Type = 9: Number of routes in per-AFI/SAFI Adj-RIB-In. The
+ * value is structured as: 2-byte Address Family Identifier (AFI),
+ * 1-byte Subsequent Address Family Identifier (SAFI), followed by a
+ * 64-bit Gauge.
+ * <p>
+ * Stat Type = 10: Number of routes in per-AFI/SAFI Loc-RIB. The
+ * value is structured as: 2-byte AFI, 1-byte SAFI, followed by a
+ * 64-bit Gauge.
+ * <p>
+ * Stat Type = 11: (32-bit Counter) Number of updates subjected to
+ * treat-as-withdraw treatment [RFC7606].
+ * <p>
+ * Stat Type = 12: (32-bit Counter) Number of prefixes subjected to
+ * treat-as-withdraw treatment [RFC7606].
+ * <p>
+ * Stat Type = 13: (32-bit Counter) Number of duplicate update
+ * messages received.
+ * <p>
+ * Although the current specification only specifies 4-byte counters and
+ * 8-byte gauges as "Stat Data", this does not preclude future versions
+ * from incorporating more complex TLV-type "Stat Data" (for example,
+ * one that can carry prefix-specific data). SR messages are optional.
+ * However, if an SR message is transmitted, at least one statistic MUST
+ * be carried in it.
+ */
+public final class BmpStatsReport extends StatsMessage {
+
+
+/*
+ 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
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Stats Count |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Each counter is encoded as follows:
+
+ 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
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Stat Type | Stat Len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Stat Data |
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ private PerPeer perPeer;
+ private int statsCount;
+ private List<BmpStats> stats;
+
+ private BmpStatsReport(Builder builder) {
+ this.perPeer = builder.perPeer;
+ this.statsCount = builder.statsCount;
+ this.stats = builder.stats;
+
+ }
+
+ /**
+ * Returns number of BMP statistics records.
+ *
+ * @return number of BMP statistics records
+ */
+ public int getStatsCount() {
+ return statsCount;
+ }
+
+ /**
+ * Returns BGP statistics.
+ *
+ * @return BGP statistics
+ */
+ public List<BmpStats> getStats() {
+ return stats;
+ }
+
+ /**
+ * Returns BMP Peer Header of BMP Message.
+ *
+ * @return BMP Peer Header of BMP Message
+ */
+ @Override
+ public PerPeer getPerPeer() {
+ return perPeer;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ BmpStatsReport that = (BmpStatsReport) o;
+ return statsCount == that.statsCount &&
+ Objects.equals(stats, that.stats);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(statsCount, stats);
+ }
+
+
+ /**
+ * Data deserializer function for BMP statistics.
+ *
+ * @return data deserializer function
+ */
+ public static Deserializer<BmpStatsReport> 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,
+ STATS_REPORT_HEADER_MIN_LENGTH +
+ PerPeerPacket.PEER_HEADER_MIN_LENGTH +
+ BmpStatistics.STATISTICS_HEADER_MIN_LENGTH)) {
+ throw new BmpParseException("Invalid bmp statistics message buffer size.");
+ }
+
+
+ byte[] perPeer = new byte[PerPeerPacket.PEER_HEADER_MIN_LENGTH];
+ bb.get(perPeer);
+
+ Builder builder = new Builder()
+ .perPeer(PerPeerPacket.deserializer().deserialize(perPeer,
+ 0, PerPeerPacket.PEER_HEADER_MIN_LENGTH))
+ .statsCount(bb.getInt());
+
+ IntStream.range(0, builder.statsCount)
+ .forEach(index -> {
+ if (bb.hasRemaining()) {
+ int statsType = bb.getShort();
+ int newLength = bb.getShort();
+ bb.position(bb.position() - BmpStatistics.STATISTICS_HEADER_MIN_LENGTH);
+ int statsLength = newLength + BmpStatistics.STATISTICS_HEADER_MIN_LENGTH;
+ if (bb.remaining() < statsLength) {
+ throw new BmpParseException("Invalid bmp statistics message buffer size.");
+ }
+ byte[] statsBytes = new byte[statsLength];
+ bb.get(statsBytes);
+ try {
+ builder.stats((BmpStats) BmpStatistics.deserializer()
+ .deserialize(statsBytes, 0, statsLength));
+ } catch (DeserializationException ex) {
+ throw new BmpParseException(ex);
+ }
+ }
+ });
+
+ return builder.build();
+ };
+ }
+
+
+ @Override
+ public String toString() {
+
+ return MoreObjects.toStringHelper(getClass())
+ .add("perPeer", perPeer)
+ .add("statsCount", statsCount)
+ .add("stats", stats)
+ .toString();
+ }
+
+ /**
+ * Builder for BMP statistics message.
+ */
+ private static class Builder {
+
+ private PerPeer perPeer;
+
+ private int statsCount;
+
+ private List<BmpStats> stats = Lists.newArrayList();
+
+
+ /**
+ * Setter bmp per peer header.
+ *
+ * @param perPeer bmp per peer header.
+ * @return this class builder.
+ */
+ public Builder perPeer(PerPeer perPeer) {
+ this.perPeer = perPeer;
+ return this;
+ }
+
+
+ /**
+ * Setter bmp statistics record count.
+ *
+ * @param statsCount bmp statistics record count.
+ * @return this class builder.
+ */
+ public Builder statsCount(int statsCount) {
+ this.statsCount = statsCount;
+ return this;
+ }
+
+ /**
+ * Setter bmp statistics records.
+ *
+ * @param stats bmp statistics records.
+ * @return this class builder.
+ */
+ public Builder stats(List<BmpStats> stats) {
+ this.stats = stats;
+ return this;
+ }
+
+ /**
+ * Setter bmp statistics record.
+ *
+ * @param stats bmp statistics record.
+ * @return this class builder.
+ */
+ public Builder stats(BmpStats stats) {
+ this.stats.add(stats);
+ return this;
+ }
+
+ /**
+ * Checks arguments for bmp statistics record.
+ */
+ private void checkArguments() {
+ checkState(perPeer != null, "Invalid bmp statistics per peer buffer.");
+ checkState(statsCount != 0, "Invalid bmp statistics records.");
+ }
+
+ /**
+ * Builds BMP statistics message.
+ *
+ * @return BMP statistics message.
+ */
+ public BmpStatsReport build() {
+ checkArguments();
+ return new BmpStatsReport(this);
+ }
+ }
+
+}