blob: b6bc51eb7b0b3fafb21891c3458803c493e994d8 [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.function.Predicate;
import java.util.Objects;
import com.google.common.base.MoreObjects;
import org.onlab.packet.DeserializationException;
import org.onlab.packet.Deserializer;
import static com.google.common.base.Preconditions.checkState;
/**
* One of the essential elements in the NetFlow format is the Template
* FlowSet. Templates greatly enhance the flexibility of the Flow
* Record format because they allow the NetFlow Collector to process
* Flow Records without necessarily knowing the interpretation of all
* the data in the Flow Record.
* Ref: https://www.ietf.org/rfc/rfc3954.txt
*/
public final class TemplateFlowSet 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 = 0 | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Template ID 256 | Field Count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Field Type 1 | Field Length 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Field Type 2 | Field Length 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Field Type N | Field Length N |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Template ID 257 | Field Count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Field Type 1 | Field Length 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Field Type 2 | Field Length 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Field Type M | Field Length M |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Template ID K | Field Count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
private int flowSetId;
private int length;
private List<DataTemplateRecord> records;
private TemplateFlowSet(Builder builder) {
this.records = builder.records;
this.length = builder.length;
this.flowSetId = builder.flowSetId;
}
/**
* Return template flow set id.
* FlowSet ID value of 0 is reserved for the Template FlowSet.
*
* @return flow set ID
*/
@Override
public int getFlowSetId() {
return this.flowSetId;
}
/**
* Returns total length of this flowSet.
*
* @return length of flowset
*/
@Override
public int getLength() {
return this.length;
}
/**
* Returns list of flow records.
*
* @return list of flow records
*/
public List<DataTemplateRecord> getRecords() {
return records;
}
/**
* Returns type of the flowset.
*
* @return type of the flowset
*/
@Override
public Type getType() {
return Type.TEMPLATE_FLOWSET;
}
@Override
public int hashCode() {
int hash = 7;
hash = 53 * hash + this.flowSetId;
hash = 53 * hash + this.length;
hash = 53 * hash + Objects.hashCode(this.records);
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 TemplateFlowSet other = (TemplateFlowSet) obj;
if (this.flowSetId != other.flowSetId) {
return false;
}
if (this.length != other.length) {
return false;
}
return Objects.equals(this.records, other.records);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("flowSetId", flowSetId)
.add("length", length)
.add("records", records)
.toString();
}
/**
* Data deserializer function for template flow set.
*
* @return data deserializer function
*/
public static Deserializer<TemplateFlowSet> deserializer() {
return (data, offset, length) -> {
Predicate<ByteBuffer> isValidBuffer = b -> b.remaining() < FlowSet.FIELD_LENTH;
ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
if (isValidBuffer.test(bb)) {
throw new DeserializationException("Invalid buffer size");
}
Builder builder = new Builder()
.flowSetId(bb.getShort())
.length(bb.getShort());
while (bb.hasRemaining()) {
if (isValidBuffer.test(bb)) {
break;
}
int templateId = bb.getShort();
int fieldCount = bb.getShort();
int bufferLength = (fieldCount * FlowSet.FIELD_LENTH) + FlowSet.FIELD_LENTH;
byte[] record = new byte[bufferLength];
bb.position(bb.position() - FlowSet.FIELD_LENTH);
if (bb.remaining() < bufferLength) {
break;
}
bb.get(record);
builder.templateRecord(DataTemplateRecord.deserializer().deserialize(record, 0, bufferLength));
}
return builder.build();
};
}
/**
* Builder for template flow set.
*/
private static class Builder {
private int flowSetId;
private int length;
private List<DataTemplateRecord> records = new LinkedList<>();
/**
* Setter for template flow set id.
*
* @param flowSetId template flow set id.
* @return this class builder.
*/
public Builder flowSetId(int flowSetId) {
this.flowSetId = flowSetId;
return this;
}
/**
* Setter for total length of this flowSet.
*
* @param length total length of this flowSet.
* @return this class builder.
*/
public Builder length(int length) {
this.length = length;
return this;
}
/**
* Setter for list of flow records.
*
* @param records list of flow records.
* @return this class builder.
*/
public Builder templateRecords(List<DataTemplateRecord> records) {
this.records = records;
return this;
}
/**
* Setter for flow records.
*
* @param record flow records.
* @return this class builder.
*/
public Builder templateRecord(DataTemplateRecord record) {
this.records.add(record);
return this;
}
/**
* Checks arguments for template flow set.
*/
private void checkArguments() {
checkState(flowSetId == 0, "Invalid flow set id.");
checkState(length == 0, "Invalid flow set length.");
}
/**
* Builds template flow set.
*
* @return template flow set.
*/
public TemplateFlowSet build() {
checkArguments();
return new TemplateFlowSet(this);
}
}
}