[ONOS-6809] Support for Packets and packet Metadata

Change-Id: I53910cd8e2ce5845e301dc68bcbf3ae8f34a197b
This commit is contained in:
Andrea Campanella 2017-07-14 18:43:27 +02:00 committed by Carmelo Cascone
parent fb76b04736
commit 432f718de7
8 changed files with 502 additions and 13 deletions

View File

@ -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<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
throws PiInterpreterException;
/**
* Signals that an error was encountered while executing the interpreter.
*/

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
throws PiInterpreterException {
return null;
}
/**
* Returns an action instance with no runtime parameters.
*/

View File

@ -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<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
throws PiInterpreterException {
throw new UnsupportedOperationException("Currently unsupported");
}
/**
* Returns an action instance with no runtime parameters.
*/

View File

@ -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<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.
*/

View File

@ -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<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() {