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 8d061008f2..e71d910467 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.driver.HandlerBehaviour; 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; /** @@ -75,6 +78,18 @@ public interface PiPipelineInterpreter extends HandlerBehaviour { PiTableAction mapTreatment(TrafficTreatment treatment, PiPipeconf pipeconf) 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 mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf) + throws PiInterpreterException; + /** * Signals that an error was encountered while executing the interpreter. */ 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 0000000000..0f707a3f42 --- /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 0000000000..038e2a2d34 --- /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 { + + /** + * 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 0000000000..a37a13f84f --- /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 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 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 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 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 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 ff8ac93823..ddee2ca0f4 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.driver.AbstractHandlerBehaviour; 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.PiActionId; 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 @@ public class MockInterpreter extends AbstractHandlerBehaviour implements PiPipel } } + @Override + public Collection 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 59f0c8c605..ee307b6145 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.TrafficTreatment; 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.PiActionId; 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 @@ public class Bmv2DefaultInterpreter extends AbstractHandlerBehaviour implements } } + @Override + public Collection 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 ee622cba4c..03e28c298a 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; @@ -74,6 +76,16 @@ public interface P4RuntimeClient { */ CompletableFuture> 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 packetOut(PiPacketOperation packet, PiPipeconf pipeconf); + + /** * Shutdown the client by terminating any active RPC such as the stream channel. */ 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 35e9726f32..3762eb1eee 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.StatusRuntimeException; 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 java.util.concurrent.TimeUnit; 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 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { StreamMessageRequest initRequest = StreamMessageRequest .newBuilder() .setArbitration(MasterArbitrationUpdate - .newBuilder() - .setDeviceId(p4DeviceId) - .build()) + .newBuilder() + .setDeviceId(p4DeviceId) + .build()) .build(); streamRequestObserver.onNext(initRequest); return true; @@ -133,12 +140,12 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { 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 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { .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); @@ -189,6 +196,37 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { throw new UnsupportedOperationException("dumpTable not implemented."); } + @Override + public CompletableFuture packetOut(PiPacketOperation packet, PiPipeconf pipeconf) { + CompletableFuture 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() {