org.opendaylight.openflowjava.nx.codec.action.LearnCodecUtil Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2016 Hewlett-Packard Enterprise and others. All rights reserved.
* Copyright (c) 2020 PANTHEON.tech
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowjava.nx.codec.action;
import static org.opendaylight.yangtools.yang.common.netty.ByteBufUtils.readUint16;
import static org.opendaylight.yangtools.yang.common.netty.ByteBufUtils.readUint32;
import static org.opendaylight.yangtools.yang.common.netty.ByteBufUtils.readUint64;
import static org.opendaylight.yangtools.yang.common.netty.ByteBufUtils.readUint8;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModAddMatchFromFieldCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModAddMatchFromFieldCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModAddMatchFromValueCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModAddMatchFromValueCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModCopyFieldIntoFieldCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModCopyFieldIntoFieldCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModCopyValueIntoFieldCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModCopyValueIntoFieldCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModOutputToPortCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.FlowModOutputToPortCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.add.match.from.field._case.FlowModAddMatchFromField;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.add.match.from.field._case.FlowModAddMatchFromFieldBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.add.match.from.value._case.FlowModAddMatchFromValue;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.add.match.from.value._case.FlowModAddMatchFromValueBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.copy.field.into.field._case.FlowModCopyFieldIntoField;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.copy.field.into.field._case.FlowModCopyFieldIntoFieldBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.copy.value.into.field._case.FlowModCopyValueIntoField;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.copy.value.into.field._case.FlowModCopyValueIntoFieldBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.output.to.port._case.FlowModOutputToPort;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.flow.mod.spec.flow.mod.spec.flow.mod.output.to.port._case.FlowModOutputToPortBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.aug.nx.action.ActionLearn;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.nx.action.learn.grouping.NxActionLearnBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.nx.action.learn.grouping.nx.action.learn.FlowMods;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.nx.action.learn.grouping.nx.action.learn.FlowModsBuilder;
import org.opendaylight.yangtools.yang.common.Uint16;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class LearnCodecUtil {
// For LearnCodec (for now?)
static final int HEADER_LENGTH = 32;
private static final Logger LOG = LoggerFactory.getLogger(LearnCodecUtil.class);
private static final short SRC_MASK = 0x2000;
private static final short DST_MASK = 0x1800;
private static final short NUM_BITS_MASK = 0x07FF;
private static final int SRC_POS = 13;
private static final int DST_POS = 11;
private static final int FROM_FIELD_LENGTH = 14;
private static final int FROM_VALUE_LENGTH = 10;
private static final int TO_PORT_LENGTH = 8;
private static final int EMPTY_FLOW_MOD_LENGTH = 2;
// For overflow detection. We should probably just ByteBuf.slice() and fail-fast, i.e. don't read stuff beyond
// message length limit.
private short length;
private LearnCodecUtil(final short length) {
this.length = length;
}
static short deserializeHeader(final ByteBuf message) {
// size of experimenter type
message.skipBytes(Short.BYTES);
// size of length
short messageLength = message.readShort();
// vendor id
message.skipBytes(Integer.BYTES);
// subtype
message.skipBytes(Short.BYTES);
return messageLength;
}
/*
* SERIALIZATION
*/
static void serializeLearnHeader(final ByteBuf outBuffer, final ActionLearn action) {
outBuffer.writeShort(action.getNxActionLearn().getIdleTimeout().shortValue());
outBuffer.writeShort(action.getNxActionLearn().getHardTimeout().shortValue());
outBuffer.writeShort(action.getNxActionLearn().getPriority().shortValue());
outBuffer.writeLong(action.getNxActionLearn().getCookie().longValue());
outBuffer.writeShort(action.getNxActionLearn().getFlags().shortValue());
outBuffer.writeByte(action.getNxActionLearn().getTableId().byteValue());
outBuffer.writeZero(1);
outBuffer.writeShort(action.getNxActionLearn().getFinIdleTimeout().shortValue());
outBuffer.writeShort(action.getNxActionLearn().getFinHardTimeout().shortValue());
}
static void serializeFlowMods(final ByteBuf outBuffer, final ActionLearn action) {
for (FlowMods flowMod : action.getNxActionLearn().nonnullFlowMods()) {
final var modSpec = flowMod.getFlowModSpec();
if (modSpec instanceof FlowModAddMatchFromFieldCase) {
final var flowModSpecFromField = ((FlowModAddMatchFromFieldCase) modSpec).getFlowModAddMatchFromField();
toFlowModSpecHeader(flowModSpecFromField, outBuffer);
// TODO: Use ByteBufUtils.writeUint() instead of intValue()/shortValue()?
outBuffer.writeInt(flowModSpecFromField.getSrcField().intValue());
outBuffer.writeShort(flowModSpecFromField.getSrcOfs().shortValue());
outBuffer.writeInt(flowModSpecFromField.getDstField().intValue());
outBuffer.writeShort(flowModSpecFromField.getDstOfs().shortValue());
} else if (modSpec instanceof FlowModAddMatchFromValueCase) {
final var flowModSpec = ((FlowModAddMatchFromValueCase) modSpec).getFlowModAddMatchFromValue();
toFlowModSpecHeader(flowModSpec, outBuffer);
outBuffer.writeShort(flowModSpec.getValue().shortValue());
outBuffer.writeInt(flowModSpec.getSrcField().intValue());
outBuffer.writeShort(flowModSpec.getSrcOfs().shortValue());
} else if (modSpec instanceof FlowModCopyFieldIntoFieldCase) {
final var flowModSpec = ((FlowModCopyFieldIntoFieldCase) modSpec).getFlowModCopyFieldIntoField();
toFlowModSpecHeader(flowModSpec, outBuffer);
outBuffer.writeInt(flowModSpec.getSrcField().intValue());
outBuffer.writeShort(flowModSpec.getSrcOfs().shortValue());
outBuffer.writeInt(flowModSpec.getDstField().intValue());
outBuffer.writeShort(flowModSpec.getDstOfs().shortValue());
} else if (modSpec instanceof FlowModCopyValueIntoFieldCase) {
final var flowModSpec = ((FlowModCopyValueIntoFieldCase) modSpec).getFlowModCopyValueIntoField();
toFlowModSpecHeader(flowModSpec, outBuffer);
outBuffer.writeShort(flowModSpec.getValue().shortValue());
outBuffer.writeInt(flowModSpec.getDstField().intValue());
outBuffer.writeShort(flowModSpec.getDstOfs().shortValue());
} else if (modSpec instanceof FlowModOutputToPortCase) {
final var flowModSpec = ((FlowModOutputToPortCase) modSpec).getFlowModOutputToPort();
toFlowModSpecHeader(flowModSpec, outBuffer);
outBuffer.writeInt(flowModSpec.getSrcField().intValue());
outBuffer.writeShort(flowModSpec.getSrcOfs().shortValue());
}
}
}
private static void toFlowModSpecHeader(final FlowModOutputToPort flowModSpec, final ByteBuf outBuffer) {
serializeFlowModSpecHeader(0, 2, flowModSpec.getFlowModNumBits(), outBuffer);
}
private static void toFlowModSpecHeader(final FlowModCopyValueIntoField flowModSpec, final ByteBuf outBuffer) {
serializeFlowModSpecHeader(1, 1, flowModSpec.getFlowModNumBits(), outBuffer);
}
private static void toFlowModSpecHeader(final FlowModCopyFieldIntoField flowModSpec, final ByteBuf outBuffer) {
serializeFlowModSpecHeader(0, 1, flowModSpec.getFlowModNumBits(), outBuffer);
}
private static void toFlowModSpecHeader(final FlowModAddMatchFromValue flowModSpec, final ByteBuf outBuffer) {
serializeFlowModSpecHeader(1, 0, flowModSpec.getFlowModNumBits(), outBuffer);
}
private static void toFlowModSpecHeader(final FlowModAddMatchFromField flowModSpec, final ByteBuf outBuffer) {
serializeFlowModSpecHeader(0, 0, flowModSpec.getFlowModNumBits(), outBuffer);
}
private static void serializeFlowModSpecHeader(final int src, final int dst, final Uint16 bitNum,
final ByteBuf outBuffer) {
outBuffer.writeShort(src << SRC_POS | dst << DST_POS | (short) bitNum.toJava());
}
static int calcLength(final ActionLearn action) {
int actionLength = HEADER_LENGTH;
for (FlowMods flowMod : action.getNxActionLearn().nonnullFlowMods()) {
final var modSpec = flowMod.getFlowModSpec();
if (modSpec instanceof FlowModAddMatchFromFieldCase) {
actionLength += FROM_FIELD_LENGTH;
} else if (modSpec instanceof FlowModAddMatchFromValueCase) {
actionLength += FROM_VALUE_LENGTH;
} else if (modSpec instanceof FlowModCopyFieldIntoFieldCase) {
actionLength += FROM_FIELD_LENGTH;
} else if (modSpec instanceof FlowModCopyValueIntoFieldCase) {
actionLength += FROM_VALUE_LENGTH;
} else if (modSpec instanceof FlowModOutputToPortCase) {
actionLength += TO_PORT_LENGTH;
}
}
return actionLength;
}
/*
* DESERIALIZATION
*/
static void deserializeLearnHeader(final ByteBuf message, final NxActionLearnBuilder nxActionLearnBuilder) {
nxActionLearnBuilder
.setIdleTimeout(readUint16(message))
.setHardTimeout(readUint16(message))
.setPriority(readUint16(message))
.setCookie(readUint64(message))
.setFlags(readUint16(message))
.setTableId(readUint8(message));
message.skipBytes(1);
nxActionLearnBuilder
.setFinIdleTimeout(readUint16(message))
.setFinHardTimeout(readUint16(message));
}
static void buildFlowModSpecs(final NxActionLearnBuilder nxActionLearnBuilder, final ByteBuf message,
final short messageLength) {
new LearnCodecUtil(messageLength).buildFlowModSpecs(nxActionLearnBuilder, message);
}
private void buildFlowModSpecs(final NxActionLearnBuilder nxActionLearnBuilder, final ByteBuf message) {
final List flowModeList = new ArrayList<>();
while (length > 0) {
final FlowMods flowMod = readFlowMod(message);
if (flowMod != null) {
flowModeList.add(flowMod);
} else {
LOG.trace("skipping padding bytes");
}
}
if (length != 0) {
LOG.error("Learn Codec read {} bytes more than needed from stream. Packet might be corrupted",
Math.abs(length));
}
nxActionLearnBuilder.setFlowMods(flowModeList);
}
private FlowMods readFlowMod(final ByteBuf message) {
final short header = message.readShort();
length -= Short.BYTES;
if (header == 0) {
return null;
}
final short src = (short) ((header & SRC_MASK) >> SRC_POS);
final short dst = (short) ((header & DST_MASK) >> DST_POS);
final Uint16 numBits = Uint16.fromShortBits((short) (header & NUM_BITS_MASK));
if (src == 0 && dst == 0) {
return readFlowModAddMatchFromField(message, numBits);
} else if (src == 1 && dst == 0) {
return readFlowModAddMatchFromValue(message, numBits);
} else if (src == 0 && dst == 1) {
return readFlowModCopyFromField(message, numBits);
} else if (src == 1 && dst == 1) {
return readFlowModCopyFromValue(message, numBits);
} else if (src == 0 && dst == 2) {
return readFlowToPort(message, numBits);
} else {
return null;
}
}
private FlowMods readFlowModAddMatchFromField(final ByteBuf message, final Uint16 numBits) {
if (numBits.shortValue() == 0) {
message.skipBytes(EMPTY_FLOW_MOD_LENGTH);
length -= EMPTY_FLOW_MOD_LENGTH;
return null;
}
final var builder = new FlowModAddMatchFromFieldBuilder()
.setSrcField(readUint32(message))
.setSrcOfs(readUint16(message))
.setDstField(readUint32(message))
.setDstOfs(readUint16(message))
.setFlowModNumBits(numBits);
length -= FROM_FIELD_LENGTH - Short.BYTES;
return new FlowModsBuilder()
.setFlowModSpec(new FlowModAddMatchFromFieldCaseBuilder()
.setFlowModAddMatchFromField(builder.build())
.build())
.build();
}
private FlowMods readFlowModAddMatchFromValue(final ByteBuf message, final Uint16 numBits) {
final var builder = new FlowModAddMatchFromValueBuilder()
.setValue(readUint16(message))
.setSrcField(readUint32(message))
.setSrcOfs(readUint16(message))
.setFlowModNumBits(numBits);
length -= FROM_VALUE_LENGTH - Short.BYTES;
return new FlowModsBuilder()
.setFlowModSpec(new FlowModAddMatchFromValueCaseBuilder()
.setFlowModAddMatchFromValue(builder.build())
.build())
.build();
}
private FlowMods readFlowModCopyFromField(final ByteBuf message, final Uint16 numBits) {
final var builder = new FlowModCopyFieldIntoFieldBuilder()
.setSrcField(readUint32(message))
.setSrcOfs(readUint16(message))
.setDstField(readUint32(message))
.setDstOfs(readUint16(message))
.setFlowModNumBits(numBits);
length -= FROM_FIELD_LENGTH - Short.BYTES;
return new FlowModsBuilder()
.setFlowModSpec(new FlowModCopyFieldIntoFieldCaseBuilder()
.setFlowModCopyFieldIntoField(builder.build())
.build())
.build();
}
private FlowMods readFlowModCopyFromValue(final ByteBuf message, final Uint16 numBits) {
final var builder = new FlowModCopyValueIntoFieldBuilder()
.setValue(readUint16(message))
.setDstField(readUint32(message))
.setDstOfs(readUint16(message))
.setFlowModNumBits(numBits);
length -= FROM_VALUE_LENGTH - Short.BYTES;
return new FlowModsBuilder()
.setFlowModSpec(new FlowModCopyValueIntoFieldCaseBuilder()
.setFlowModCopyValueIntoField(builder.build())
.build())
.build();
}
private FlowMods readFlowToPort(final ByteBuf message, final Uint16 numBits) {
final var builder = new FlowModOutputToPortBuilder()
.setSrcField(readUint32(message))
.setSrcOfs(readUint16(message))
.setFlowModNumBits(numBits);
length -= TO_PORT_LENGTH - Short.BYTES;
return new FlowModsBuilder()
.setFlowModSpec(new FlowModOutputToPortCaseBuilder().setFlowModOutputToPort(builder.build()).build())
.build();
}
}