blob: 5405ae28ae9810782a4503a41adb87ec972b334a [file] [log] [blame]
/*
* 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 org.onlab.packet.DeserializationException;
import org.onlab.packet.Deserializer;
import java.nio.ByteBuffer;
import java.util.function.BiPredicate;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.onosproject.bgpmonitoring.BmpMessage;
import org.onosproject.bgpmonitoring.BmpVersion;
import org.onosproject.bgpmonitoring.BmpPacket;
import org.onosproject.bgpmonitoring.BmpParseException;
import static com.google.common.base.Preconditions.checkState;
/**
* The following common header appears in all BMP messages. The rest of
* the data in a BMP message is dependent on the Message Type field in
* the common header.
* <p>
* Version (1 byte): Indicates the BMP version. This is set to '3'
* for all messages defined in this specification. ('1' and '2' were
* used by draft versions of this document.) Version 0 is reserved
* and MUST NOT be sent.
* <p>
* Message Length (4 bytes): Length of the message in bytes
* (including headers, data, and encapsulated messages, if any).
* <p>
* Message Type (1 byte): This identifies the type of the BMP
* message. A BMP implementation MUST ignore unrecognized message
* types upon receipt.
* <p>
* Type = 0: Route Monitoring
* Type = 1: Statistics Report
* Type = 2: Peer Down Notification
* Type = 3: Peer Up Notification
* Type = 4: Initiation Message
* Type = 5: Termination Message
* Type = 6: Route Mirroring Message
*/
public final class BmpHeader extends BmpPacket {
/*
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
+-+-+-+-+-+-+-+-+
| Version |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Msg. Type |
+---------------+
*/
public static final int DEFAULT_HEADER_LENGTH = 6;
public static final int DEFAULT_PACKET_MINIMUM_LENGTH = 4;
private BmpVersion version;
private BmpType type;
private int length;
private BmpMessage message;
public enum BmpType {
ROUTE_MONITORING(0, BmpRouteMirroring.deserializer()),
STATISTICS_REPORT(1, BmpStatsReport.deserializer()),
PEER_DOWN_NOTIFICATION(2, BmpPeerDownNotification.deserializer()),
PEER_UP_NOTIFICATION(3, BmpPeerUpNotification.deserializer()),
INITIATION_MESSAGE(4, BmpInitiationMessage.deserializer()),
TERMINATION_MESSAGE(5, BmpTerminationMessage.deserializer()),
ROUTE_MIRRORING_MESSAGE(6, BmpRouteMirroring.deserializer());
private final int value;
private final Deserializer<BmpMessage> deserializer;
/**
* Assign value with the value val as the types of BMP message.
*
* @param val type of BMP message
* @param deserializer deserializer
*/
BmpType(int val, Deserializer deserializer) {
this.value = val;
this.deserializer = deserializer;
}
/**
* Returns value as type of BMP message.
*
* @return value type of BMP message
*/
public int getType() {
return value;
}
private static Map<Integer, BmpType> parser = new ConcurrentHashMap<>();
static {
Arrays.stream(BmpType.values()).forEach(type -> parser.put(type.value, type));
}
public static BmpType getType(int type) throws DeserializationException {
if (type > 6) {
throw new DeserializationException("Invalid trap type");
}
return Optional.of(type)
.filter(id -> parser.containsKey(id))
.map(id -> parser.get(id))
.orElse(ROUTE_MONITORING);
}
public Deserializer<BmpMessage> getDecoder() {
return this.deserializer;
}
}
private BmpHeader(Builder builder) {
this.version = builder.version;
this.length = builder.length;
this.type = builder.type;
this.message = builder.message;
}
/**
* Returns message length.
*
* @return message length
*/
@Override
public int getLength() {
return this.length;
}
/**
* Returns message version.
*
* @return message version
*/
@Override
public BmpVersion getVersion() {
return this.version;
}
/**
* Returns message type.
*
* @return message type
*/
@Override
public String getType() {
return this.type.name();
}
/**
* Returns message type.
*
* @return message type
*/
@Override
public BmpMessage getMessage() {
return this.message;
}
/**
* Read from channel buffer and Returns BMP header.
*
* @return Deserializer Deserializer
*/
public static Deserializer<BmpHeader> 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, DEFAULT_HEADER_LENGTH)) {
throw new BmpParseException("Invalid bmp header buffer size.");
}
Builder builder = new Builder()
.version(BmpVersion.getVersion((int) bb.get()))
.length(bb.getInt())
.type(BmpType.getType((int) bb.get()));
if (bb.remaining() != (builder.length - DEFAULT_HEADER_LENGTH)) {
throw new BmpParseException("Invalid bmp header buffer size.");
}
byte[] msgBytes = new byte[bb.remaining()];
bb.get(msgBytes);
return builder.message(builder.type.getDecoder()
.deserialize(msgBytes, 0, (builder.length - DEFAULT_HEADER_LENGTH)))
.build();
};
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("version", version)
.add("type", type)
.add("length", length)
.toString();
}
/**
* Builder for BMP header.
*/
private static class Builder {
private BmpVersion version;
private BmpType type;
private int length;
private BmpMessage message;
/**
* Setter bmp version.
*
* @param version bmp version.
* @return this class builder.
*/
public Builder version(BmpVersion version) {
this.version = version;
return this;
}
/**
* Setter bmp header length.
*
* @param length bmp header length.
* @return this class builder.
*/
public Builder length(int length) {
this.length = length;
return this;
}
/**
* Setter bmp message type.
*
* @param type bmp message type.
* @return this class builder.
*/
public Builder type(BmpType type) {
this.type = type;
return this;
}
/**
* Setter bmp message.
*
* @param message bmp message.
* @return this class builder.
*/
public Builder message(BmpMessage message) {
this.message = message;
return this;
}
/**
* Checks arguments for bmp header.
*/
private void checkArguments() {
checkState(length != 0, "Invalid bmp header length.");
checkState(type != null, "Invalid bmp message type.");
checkState(message != null, "Invalid bmp message.");
}
/**
* Builds BMP header.
*
* @return BMP header.
*/
public BmpHeader build() {
checkArguments();
return new BmpHeader(this);
}
}
}