blob: 71839159911a03dc45ecdfbd72bb6c8000d409ff [file] [log] [blame]
/*
* Copyright 2023-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.netflow.impl;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import com.google.common.base.MoreObjects;
import org.onlab.packet.DeserializationException;
import org.onlab.packet.Deserializer;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* A Data FlowSet is one or more records, of the same type, that are
* grouped together in an Export Packet. Each record is either a Flow
* Data Record or an Options Data Record previously defined by a
* Template Record or an Options Template Record.
* Ref: https://www.ietf.org/rfc/rfc3954.txt
*/
public final class DataFlowSet extends FlowSet {
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FlowSet ID = Template ID | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record 1 - Field Value 1 | Record 1 - Field Value 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record 1 - Field Value 3 | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record 2 - Field Value 1 | Record 2 - Field Value 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record 2 - Field Value 3 | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record 3 - Field Value 1 | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
private int flowSetId;
private int length;
private byte[] data;
private DataFlowSet(Builder builder) {
this.flowSetId = builder.flowSetId;
this.length = builder.length;
this.data = builder.data;
}
private List<DataFlowRecord> dataFlow = new LinkedList<>();
/**
* Returns flowset id.
* Each Data FlowSet is associated with a FlowSet ID. The FlowSet
* ID maps to a (previously generated) Template ID
*
* @return flowset id
*/
@Override
public int getFlowSetId() {
return this.flowSetId;
}
/**
* Returns length of this FlowSet.
* Length is the sum of the lengths
* of the FlowSet ID, Length itself, all Flow Records within this
* FlowSet, and the padding bytes, if any.
*
* @return length of the flowset
*/
@Override
public int getLength() {
return this.length;
}
/**
* Returns list of data flow records.
*
* @return list of data flow records
*/
public List<DataFlowRecord> getDataFlow() {
return dataFlow;
}
/**
* Set data flow record.
*
* @param dataFlow data flow record
*/
public void setDataFlow(DataFlowRecord dataFlow) {
this.dataFlow.add(dataFlow);
}
/**
* Returns type of flowset.
*
* @return type of flowset
*/
@Override
public Type getType() {
return Type.DATA_FLOWSET;
}
@Override
public int hashCode() {
int hash = 3;
hash = 79 * hash + this.flowSetId;
hash = 79 * hash + this.length;
hash = 79 * hash + Objects.hashCode(this.dataFlow);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DataFlowSet other = (DataFlowSet) obj;
if (this.flowSetId != other.flowSetId) {
return false;
}
if (this.length != other.length) {
return false;
}
return Objects.equals(this.dataFlow, other.dataFlow);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("flowSetId", flowSetId)
.add("length", length)
.add("data", data)
.toString();
}
/**
* Deserializer function for data flow set.
*
* @return deserializer function
*/
public static Deserializer<DataFlowSet> deserializer() {
return (data, offset, length) -> {
Function<ByteBuffer, byte[]> readBytes = b -> {
if (b.remaining() == b.limit()) {
return null;
}
byte[] bytes = new byte[b.remaining()];
b.get(bytes);
return bytes;
};
ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
return new Builder()
.flowSetId(bb.getShort())
.length(bb.getShort())
.data(readBytes.apply(bb))
.build();
};
}
/**
* Data eserializer function for data flow record.
*
* @param template data template record
* @throws DeserializationException if unable to deserialize data
*/
public void dataDeserializer(DataTemplateRecord template) throws DeserializationException {
dataFlow = new LinkedList<>();
ByteBuffer bb = ByteBuffer.wrap(data, 0, data.length);
while (bb.hasRemaining()) {
if (bb.remaining() < template.getValueLength()) {
break;
}
byte[] dataRecord = new byte[template.getValueLength()];
bb.get(dataRecord);
this.setDataFlow(DataFlowRecord.deserializer().deserialize(
data, 0, template.getValueLength(), template));
}
}
/**
* Builder for data flow set.
*/
private static class Builder {
private int flowSetId;
private int length;
private byte[] data;
/**
* Setter for flowset id.
*
* @param flowSetId flowset id.
* @return this class builder.
*/
public Builder flowSetId(int flowSetId) {
this.flowSetId = flowSetId;
return this;
}
/**
* Setter for length of this FlowSet.
*
* @param length length of this FlowSet.
* @return this class builder.
*/
public Builder length(int length) {
this.length = length;
return this;
}
/**
* Setter for flow data.
*
* @param data flow data.
* @return this class builder.
*/
public Builder data(byte[] data) {
this.data = data;
return this;
}
/**
* Checks arguments for data flow set.
*/
private void checkArguments() {
checkState(flowSetId != 0, "Invalid data flowset id.");
checkState(length != 0, "Invalid data flowset length.");
checkNotNull(data, "Data flow set cannot be null");
}
/**
* Builds data flowset.
*
* @return data flowset.
*/
public DataFlowSet build() {
checkArguments();
return new DataFlowSet(this);
}
}
}