[ONOS-6809] Implementation for packet out in p4Runtime
Change-Id: I873a1fd18529fe9fd41aa33f862298892ece7d1c
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java
index 29e2a3a..a2e7812 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java
@@ -17,8 +17,11 @@
package org.onosproject.drivers.bmv2;
import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
@@ -33,13 +36,21 @@
import org.onosproject.net.pi.runtime.PiActionParamId;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketMetadata;
+import org.onosproject.net.pi.runtime.PiPacketMetadataId;
+import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableAction;
import org.onosproject.net.pi.runtime.PiTableId;
+import java.nio.ByteBuffer;
import java.util.Collection;
+import java.util.List;
import java.util.Optional;
+import static java.util.stream.Collectors.toList;
import static org.onosproject.net.PortNumber.CONTROLLER;
+import static org.onosproject.net.PortNumber.FLOOD;
+import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
+import static org.onosproject.net.pi.runtime.PiPacketOperation.Type.PACKET_OUT;
/**
* Interpreter implementation for the default pipeconf.
@@ -50,6 +61,8 @@
private static final String PORT = "port";
private static final String DROP = "_drop_0";
private static final String SET_EGRESS_PORT = "set_egress_port_0";
+ private static final String EGRESS_PORT = "egress_port";
+ private static final int PORT_NUMBER_BIT_WIDTH = 9;
private static final PiHeaderFieldId IN_PORT_ID = PiHeaderFieldId.of("standard_metadata", "ingress_port");
private static final PiHeaderFieldId ETH_DST_ID = PiHeaderFieldId.of("ethernet_t", "dstAddr");
@@ -67,6 +80,7 @@
private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
0, PiTableId.of(TABLE0));
+
@Override
public PiTableAction mapTreatment(TrafficTreatment treatment, PiPipeconf pipeconf) throws PiInterpreterException {
@@ -88,7 +102,7 @@
PiAction.builder()
.withId(PiActionId.of(SET_EGRESS_PORT))
.withParameter(new PiActionParam(PiActionParamId.of(PORT),
- ImmutableByteSequence.copyFrom(port.toLong())))
+ ImmutableByteSequence.copyFrom(port.toLong())))
.build();
} else if (port.equals(CONTROLLER)) {
return actionWithName(SEND_TO_CPU);
@@ -103,9 +117,65 @@
}
@Override
- public Collection<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
+ public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
throws PiInterpreterException {
- throw new UnsupportedOperationException("Currently unsupported");
+ TrafficTreatment treatment = packet.treatment();
+
+ // default.p4 supports only OUTPUT instructions.
+ List<Instructions.OutputInstruction> outInstructions = treatment.allInstructions()
+ .stream()
+ .filter(i -> i.type().equals(OUTPUT))
+ .map(i -> (Instructions.OutputInstruction) i)
+ .collect(toList());
+
+ if (treatment.allInstructions().size() != outInstructions.size()) {
+ // There are other instructions that are not of type OUTPUT
+ throw new PiInterpreterException("Treatment not supported: " + treatment);
+ }
+
+ ImmutableList.Builder<PiPacketOperation> builder = ImmutableList.builder();
+ for (Instructions.OutputInstruction outInst : outInstructions) {
+ if (outInst.port().isLogical() && !outInst.port().equals(FLOOD)) {
+ throw new PiInterpreterException("Logical port not supported: " +
+ outInst.port());
+ } else if (outInst.port().equals(FLOOD)) {
+ //Since default.p4 does not support flood for each port of the device
+ // create a packet operation to send the packet out of that specific port
+ for (Port port : handler().get(DeviceService.class).getPorts(packet.sendThrough())) {
+ builder.add(createPiPacketOperation(packet.data(), port.number().toLong()));
+ }
+ } else {
+ builder.add(createPiPacketOperation(packet.data(), outInst.port().toLong()));
+ }
+ }
+ return builder.build();
+ }
+
+ private PiPacketOperation createPiPacketOperation(ByteBuffer data, long portNumber) throws PiInterpreterException {
+ //create the metadata
+ PiPacketMetadata metadata = createPacketMetadata(portNumber);
+
+ //Create the Packet operation
+ return PiPacketOperation.builder()
+ .withType(PACKET_OUT)
+ .withData(ImmutableByteSequence.copyFrom(data))
+ .withMetadatas(ImmutableList.of(metadata))
+ .build();
+ }
+
+ private PiPacketMetadata createPacketMetadata(long portNumber) throws PiInterpreterException {
+ ImmutableByteSequence portValue = ImmutableByteSequence.copyFrom(portNumber);
+ //FIXME remove hardcoded bitWidth and retrieve it from pipelineModel
+ try {
+ portValue = ImmutableByteSequence.fit(portValue, PORT_NUMBER_BIT_WIDTH);
+ } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
+ throw new PiInterpreterException("Port number too big: {}" +
+ portNumber + " causes " + e.getMessage());
+ }
+ return PiPacketMetadata.builder()
+ .withId(PiPacketMetadataId.of(EGRESS_PORT))
+ .withValue(portValue)
+ .build();
}
/**