[ONOS-6809] Support for Packets and packet Metadata
Change-Id: I53910cd8e2ce5845e301dc68bcbf3ae8f34a197b
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
index 8d06100..e71d910 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
@@ -21,10 +21,13 @@
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
+import org.onosproject.net.pi.runtime.PiPacketMetadata;
import org.onosproject.net.pi.runtime.PiTableAction;
import org.onosproject.net.pi.runtime.PiTableId;
+import java.util.Collection;
import java.util.Optional;
/**
@@ -76,6 +79,18 @@
throws PiInterpreterException;
/**
+ * Returns a collection of metadatas for a packet-out operation that are equivalent to
+ * the traffic treatment of the given OutboundPacket, for the given pipeline configuration.
+ *
+ * @param packet a ONOS outbound packet
+ * @param pipeconf a pipeline configuration
+ * @return a collection of packet metadata
+ * @throws PiInterpreterException if the packet treatments cannot be mapped to any metadata
+ */
+ Collection<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
+ throws PiInterpreterException;
+
+ /**
* Signals that an error was encountered while executing the interpreter.
*/
@Beta
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java
new file mode 100644
index 0000000..0f707a3
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onlab.util.ImmutableByteSequence;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Instance of a metadata for a packet I/O operation, with id and value for a protocol-independent pipeline.
+ */
+@Beta
+public final class PiPacketMetadata {
+
+ private final PiPacketMetadataId id;
+ private final ImmutableByteSequence value;
+
+ /**
+ * Creates a new packet metadata instance for the given identifier and value.
+ *
+ * @param id packet metadata identifier
+ * @param value value for this metadata
+ */
+ private PiPacketMetadata(PiPacketMetadataId id, ImmutableByteSequence value) {
+ this.id = id;
+ this.value = value;
+ }
+
+ /**
+ * Return the identifier of this packet metadata.
+ *
+ * @return packet metadata identifier
+ */
+ public PiPacketMetadataId id() {
+ return id;
+ }
+
+ /**
+ * Returns the value for the field in this metadata.
+ *
+ * @return value
+ */
+ public ImmutableByteSequence value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PiPacketMetadata piPacket = (PiPacketMetadata) o;
+ return Objects.equal(id, piPacket.id()) &&
+ Objects.equal(value, piPacket.value());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(id, value);
+ }
+
+ @Override
+ public String toString() {
+ return this.id().toString() + " = " + value.toString();
+ }
+
+ /**
+ * Returns a packet metadata builder.
+ *
+ * @return a new builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of protocol-independent packet metadatas.
+ */
+ public static final class Builder {
+
+ private PiPacketMetadataId id;
+ private ImmutableByteSequence value;
+
+ private Builder() {
+ // hides constructor.
+ }
+
+ /**
+ * Sets the identifier of this packet metadata.
+ *
+ * @param id packet metadata identifier
+ * @return this
+ */
+ public Builder withId(PiPacketMetadataId id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Sets the value of this metadata.
+ *
+ * @param value value of the metadata
+ * @return this
+ */
+ public Builder withValue(ImmutableByteSequence value) {
+ this.value = value;
+ return this;
+ }
+
+ /**
+ * Returns a new packet metadata instance.
+ *
+ * @return packet metadata
+ */
+ public PiPacketMetadata build() {
+ checkNotNull(id);
+ checkNotNull(value);
+ return new PiPacketMetadata(id, value);
+ }
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java
new file mode 100644
index 0000000..038e2a2
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import org.onlab.util.Identifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Identifier of a metadata for a packet I/O operation in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiPacketMetadataId extends Identifier<String> {
+
+ /**
+ * Creates a packet metadata identifier.
+ *
+ * @param name packet metadata name
+ */
+ private PiPacketMetadataId(String name) {
+ super(name);
+ }
+
+ /**
+ * Returns the name of the packet metadata.
+ *
+ * @return packet metadata name
+ */
+ public String name() {
+ return this.identifier;
+ }
+
+ /**
+ * Returns a identifier with the given name.
+ *
+ * @param name packet metadata name
+ * @return packet metadata identifier
+ */
+ public static PiPacketMetadataId of(String name) {
+ checkNotNull(name);
+ checkArgument(!name.isEmpty(), "Name can't be empty");
+ return new PiPacketMetadataId(name);
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java
new file mode 100644
index 0000000..a37a13f
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.util.ImmutableByteSequence;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Instance of a packet I/O operation, and its metadatas for a protocol-independent pipeline.
+ */
+@Beta
+public final class PiPacketOperation {
+
+ enum Type {
+ /**
+ * Represents a packet out.
+ */
+ PACKET_OUT,
+
+ /**
+ * Represents a packet in.
+ */
+ PACKET_IN,
+ }
+
+ private final ImmutableByteSequence data;
+ private final Set<PiPacketMetadata> packetMetadatas;
+ private final PiPacketOperation.Type type;
+
+ /**
+ * Creates a new packet I/O operation for the given data, metadatas and operation type.
+ *
+ * @param data the packet raw data
+ * @param packetMetadatas set of packet metadata
+ * @param type type of this packet operation
+ */
+ private PiPacketOperation(ImmutableByteSequence data, Collection<PiPacketMetadata> packetMetadatas, Type type) {
+ this.data = data;
+ this.packetMetadatas = ImmutableSet.copyOf(packetMetadatas);
+ this.type = type;
+ }
+
+ /**
+ * Return the type of this packet.
+ *
+ * @return packet type
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the data of this packet.
+ *
+ * @return packet data
+ */
+ public ImmutableByteSequence data() {
+ return data;
+ }
+
+ /**
+ * Returns all metadatas of this packet.
+ * Returns an empty collection if the packet doesn't have any metadata.
+ *
+ * @return collection of metadatas
+ */
+ public Collection<PiPacketMetadata> metadatas() {
+ return packetMetadatas;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PiPacketOperation that = (PiPacketOperation) o;
+ return Objects.equal(packetMetadatas, that.packetMetadatas) &&
+ Objects.equal(data, that.data()) &&
+ Objects.equal(type, that.type());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(data, packetMetadatas, type);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .addValue(packetMetadatas)
+ .addValue(type.toString())
+ .toString();
+ }
+
+ /**
+ * Returns an packet builder.
+ *
+ * @return a new builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of protocol-independent packets.
+ */
+ public static final class Builder {
+
+ private Map<PiPacketMetadataId, PiPacketMetadata> packetMetadatas = new HashMap<>();
+ private PiPacketOperation.Type type;
+ private ImmutableByteSequence data;
+
+ private Builder() {
+ // hides constructor.
+ }
+
+ /**
+ * Adds the raw packet data.
+ *
+ * @param data the packet raw data
+ * @return this
+ */
+ public Builder withData(ImmutableByteSequence data) {
+ checkNotNull(data);
+ this.data = data;
+ return this;
+ }
+
+ /**
+ * Adds a metadata.
+ * Only one metadata is allowed for a given metadata id.
+ * If a metadata with same id already exists it will be replaced by the given one.
+ *
+ * @param metadata packet metadata
+ * @return this
+ */
+ public Builder withMetadata(PiPacketMetadata metadata) {
+ checkNotNull(metadata);
+ packetMetadatas.put(metadata.id(), metadata);
+
+ return this;
+ }
+
+ /**
+ * Adds many packet metadatas.
+ *
+ * @param metadatas collection of metadata
+ * @return this
+ */
+ public Builder withMetadatas(Collection<PiPacketMetadata> metadatas) {
+ checkNotNull(metadatas);
+ metadatas.forEach(this::withMetadata);
+ return this;
+ }
+
+ /**
+ * Sets the type of this packet.
+ *
+ * @param type type of the packet
+ * @return this
+ */
+ public Builder withType(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Returns a new packet instance.
+ *
+ * @return packet
+ */
+ public PiPacketOperation build() {
+ checkNotNull(data);
+ checkNotNull(packetMetadatas);
+ checkNotNull(type);
+ return new PiPacketOperation(data, packetMetadatas.values(), type);
+ }
+ }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java b/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
index ff8ac93..ddee2ca 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
@@ -23,6 +23,7 @@
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiAction;
@@ -30,9 +31,11 @@
import org.onosproject.net.pi.runtime.PiActionParam;
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.PiTableAction;
import org.onosproject.net.pi.runtime.PiTableId;
+import java.util.Collection;
import java.util.Optional;
import static org.onosproject.net.PortNumber.CONTROLLER;
@@ -98,6 +101,12 @@
}
}
+ @Override
+ public Collection<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
+ throws PiInterpreterException {
+ return null;
+ }
+
/**
* Returns an action instance with no runtime parameters.
*/
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 59f0c8c..ee307b6 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
@@ -24,6 +24,7 @@
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiAction;
@@ -31,9 +32,11 @@
import org.onosproject.net.pi.runtime.PiActionParam;
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.PiTableAction;
import org.onosproject.net.pi.runtime.PiTableId;
+import java.util.Collection;
import java.util.Optional;
import static org.onosproject.net.PortNumber.CONTROLLER;
@@ -97,6 +100,12 @@
}
}
+ @Override
+ public Collection<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
+ throws PiInterpreterException {
+ throw new UnsupportedOperationException("Currently unsupported");
+ }
+
/**
* Returns an action instance with no runtime parameters.
*/
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
index ee622cb..03e28c2 100644
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
+++ b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
@@ -17,6 +17,8 @@
package org.onosproject.p4runtime.api;
import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTableId;
@@ -75,6 +77,16 @@
CompletableFuture<Collection<PiTableEntry>> dumpTable(PiTableId tableId);
/**
+ * Executes a packet-out operation.
+ *
+ * @param packet packet-out operation to be performed by the device
+ * @param pipeconf pipeconf currently deployed on the device
+ * @return a completable future of a boolean, true if the operations was successful, false otherwise.
+ */
+ CompletableFuture<Boolean> packetOut(PiPacketOperation packet, PiPipeconf pipeconf);
+
+
+ /**
* Shutdown the client by terminating any active RPC such as the stream channel.
*/
void shutdown();
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
index 35e9726..3762eb1 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
@@ -27,13 +27,14 @@
import io.grpc.stub.StreamObserver;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTableId;
import org.onosproject.p4runtime.api.P4RuntimeClient;
import org.onosproject.p4runtime.api.P4RuntimeEvent;
import org.slf4j.Logger;
import p4.P4RuntimeGrpc;
-import p4.config.P4InfoOuterClass;
import p4.tmp.P4Config;
import java.io.IOException;
@@ -46,8 +47,14 @@
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.slf4j.LoggerFactory.getLogger;
-import static p4.P4RuntimeOuterClass.*;
+import static p4.P4RuntimeOuterClass.ForwardingPipelineConfig;
+import static p4.P4RuntimeOuterClass.MasterArbitrationUpdate;
+import static p4.P4RuntimeOuterClass.PacketIn;
+import static p4.P4RuntimeOuterClass.SetForwardingPipelineConfigRequest;
import static p4.P4RuntimeOuterClass.SetForwardingPipelineConfigRequest.Action.VERIFY_AND_COMMIT;
+import static p4.P4RuntimeOuterClass.StreamMessageRequest;
+import static p4.P4RuntimeOuterClass.StreamMessageResponse;
+import static p4.config.P4InfoOuterClass.P4Info;
/**
* Implementation of a P4Runtime client.
@@ -112,9 +119,9 @@
StreamMessageRequest initRequest = StreamMessageRequest
.newBuilder()
.setArbitration(MasterArbitrationUpdate
- .newBuilder()
- .setDeviceId(p4DeviceId)
- .build())
+ .newBuilder()
+ .setDeviceId(p4DeviceId)
+ .build())
.build();
streamRequestObserver.onNext(initRequest);
return true;
@@ -133,12 +140,12 @@
log.debug("Setting pipeline config for {}", deviceId);
- P4InfoOuterClass.P4Info.Builder p4iInfoBuilder = P4InfoOuterClass.P4Info.newBuilder();
+ P4Info.Builder p4iInfoBuilder = P4Info.newBuilder();
try {
TextFormat.getParser().merge(new InputStreamReader(p4info),
- ExtensionRegistry.getEmptyRegistry(),
- p4iInfoBuilder);
+ ExtensionRegistry.getEmptyRegistry(),
+ p4iInfoBuilder);
} catch (IOException ex) {
log.warn("Unable to load p4info for {}: {}", deviceId, ex.getMessage());
return false;
@@ -161,11 +168,11 @@
.newBuilder()
.setAction(VERIFY_AND_COMMIT)
.addConfigs(ForwardingPipelineConfig
- .newBuilder()
- .setDeviceId(p4DeviceId)
- .setP4Info(p4iInfoBuilder.build())
- .setP4DeviceConfig(deviceIdConfig.toByteString())
- .build())
+ .newBuilder()
+ .setDeviceId(p4DeviceId)
+ .setP4Info(p4iInfoBuilder.build())
+ .setP4DeviceConfig(deviceIdConfig.toByteString())
+ .build())
.build();
try {
this.blockingStub.setForwardingPipelineConfig(request);
@@ -190,6 +197,37 @@
}
@Override
+ public CompletableFuture<Boolean> packetOut(PiPacketOperation packet, PiPipeconf pipeconf) {
+ CompletableFuture<Boolean> result = new CompletableFuture<>();
+// P4InfoBrowser browser = null; //PipeconfHelper.getP4InfoBrowser(pipeconf);
+// try {
+// ControllerPacketMetadata controllerPacketMetadata =
+// browser.controllerPacketMetadatas().getByName("packet_out");
+// PacketOut.Builder packetOutBuilder = PacketOut.newBuilder();
+// packetOutBuilder.addAllMetadata(packet.metadatas().stream().map(metadata -> {
+// //FIXME we are assuming that there is no more than one metadata per name.
+// int metadataId = controllerPacketMetadata.getMetadataList().stream().filter(metadataInfo -> {
+// return metadataInfo.getName().equals(metadata.id().name());
+// }).findFirst().get().getId();
+// return PacketMetadata.newBuilder()
+// .setMetadataId(metadataId)
+// .setValue(ByteString.copyFrom(metadata.value().asReadOnlyBuffer()))
+// .build();
+// }).filter(Objects::nonNull).collect(Collectors.toList()));
+// packetOutBuilder.setPayload(ByteString.copyFrom(packet.data().asReadOnlyBuffer()));
+// PacketOut packetOut = packetOutBuilder.build();
+// StreamMessageRequest packetOutRequest = StreamMessageRequest
+// .newBuilder().setPacket(packetOut).build();
+// streamRequestObserver.onNext(packetOutRequest);
+// result.complete(true);
+// } catch (P4InfoBrowser.NotFoundException e) {
+// log.error("Cant find metadata with name \"packet_out\" in p4Info file.");
+// result.complete(false);
+// }
+ return result;
+ }
+
+ @Override
public void shutdown() {
log.info("Shutting down client for {}...", deviceId);