blob: a06fe7a011da4b208fea753aa8a966aa82779fef [file] [log] [blame]
/*
* 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.p4runtime.ctl;
import com.google.protobuf.ByteString;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.runtime.PiPacketMetadata;
import org.onosproject.net.pi.runtime.PiPacketMetadataId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.slf4j.Logger;
import p4.config.P4InfoOuterClass;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.p4runtime.ctl.P4InfoBrowser.NotFoundException;
import static org.slf4j.LoggerFactory.getLogger;
import static p4.P4RuntimeOuterClass.PacketIn;
import static p4.P4RuntimeOuterClass.PacketMetadata;
import static p4.P4RuntimeOuterClass.PacketOut;
/**
* Encoder of packet metadata, from ONOS Pi* format, to P4Runtime protobuf messages, and vice versa.
*/
final class PacketIOCodec {
private static final Logger log = getLogger(PacketIOCodec.class);
private static final String PACKET_OUT = "packet_out";
private static final String PACKET_IN = "packet_in";
// TODO: implement cache of encoded entities.
private PacketIOCodec() {
// hide.
}
/**
* Returns a P4Runtime packet out protobuf message, encoded from the given PiPacketOperation
* for the given pipeconf. If a PI packet metadata inside the PacketOperation cannot be encoded,
* it is skipped, hence the returned PacketOut collection of metadatas might have different
* size than the input one.
* <p>
* Please check the log for an explanation of any error that might have occurred.
*
* @param packet PI packet operation
* @param pipeconf the pipeconf for the program on the switch
* @return a P4Runtime packet out protobuf message
* @throws NotFoundException if the browser can't find the packet_out in the given p4Info
*/
static PacketOut encodePacketOut(PiPacketOperation packet, PiPipeconf pipeconf)
throws NotFoundException {
//Get the P4browser
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
//Get the packet out controller packet metadata
P4InfoOuterClass.ControllerPacketMetadata controllerPacketMetadata =
browser.controllerPacketMetadatas().getByName(PACKET_OUT);
PacketOut.Builder packetOutBuilder = PacketOut.newBuilder();
//outer controller packet metadata id
int controllerPacketMetadataId = controllerPacketMetadata.getPreamble().getId();
//Add all its metadata to the packet out
packetOutBuilder.addAllMetadata(encodePacketMetadata(packet, browser, controllerPacketMetadataId));
//Set the packet out payload
packetOutBuilder.setPayload(ByteString.copyFrom(packet.data().asReadOnlyBuffer()));
return packetOutBuilder.build();
}
private static List<PacketMetadata> encodePacketMetadata(PiPacketOperation packet,
P4InfoBrowser browser, int controllerPacketMetadataId) {
return packet.metadatas().stream().map(metadata -> {
try {
//get each metadata id
int metadataId = browser.packetMetadatas(controllerPacketMetadataId)
.getByName(metadata.id().name()).getId();
//Add the metadata id and it's data the packet out
return PacketMetadata.newBuilder()
.setMetadataId(metadataId)
.setValue(ByteString.copyFrom(metadata.value().asReadOnlyBuffer()))
.build();
} catch (NotFoundException e) {
log.error("Cant find metadata with name {} in p4Info file.", metadata.id().name());
return null;
}
}).filter(Objects::nonNull).collect(Collectors.toList());
}
/**
* Returns a PiPacketOperation, decoded from the given P4Runtime PacketIn protobuf message
* for the given pipeconf. If a PI packet metadata inside the protobuf message cannot be decoded,
* it is skipped, hence the returned PiPacketOperation collection of metadatas might have different
* size than the input one.
* <p>
* Please check the log for an explanation of any error that might have occurred.
*
* @param packetIn the P4Runtime PAcketIn message
* @param pipeconf the pipeconf for the program on the switch
* @return a PiPacketOperation
*/
static PiPacketOperation decodePacketIn(PacketIn packetIn, PiPipeconf pipeconf) {
//Get the P4browser
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
List<PiPacketMetadata> packetMetadatas;
try {
int controllerPacketMetadataId = browser.controllerPacketMetadatas().getByName(PACKET_IN)
.getPreamble().getId();
packetMetadatas = decodePacketMetadataIn(packetIn.getMetadataList(), browser,
controllerPacketMetadataId);
} catch (NotFoundException e) {
log.error("Unable to decode packet metadatas: {}", e.getMessage());
packetMetadatas = Collections.emptyList();
}
//Transform the packetIn data
ImmutableByteSequence data = copyFrom(packetIn.getPayload().asReadOnlyByteBuffer());
//Build the PiPacketOperation with all the metadatas.
return PiPacketOperation.builder()
.withType(PiPacketOperation.Type.PACKET_IN)
.withMetadatas(packetMetadatas)
.withData(data)
.build();
}
private static List<PiPacketMetadata> decodePacketMetadataIn(List<PacketMetadata> packetMetadatas,
P4InfoBrowser browser, int controllerPacketMetadataId) {
return packetMetadatas.stream().map(packetMetadata -> {
try {
int packetMetadataId = packetMetadata.getMetadataId();
String packetMetadataName = browser.packetMetadatas(controllerPacketMetadataId)
.getById(packetMetadataId).getName();
PiPacketMetadataId metadataId = PiPacketMetadataId.of(packetMetadataName);
//Build each metadata.
return PiPacketMetadata.builder()
.withId(metadataId)
.withValue(ImmutableByteSequence.copyFrom(packetMetadata.getValue().asReadOnlyByteBuffer()))
.build();
} catch (NotFoundException e) {
log.error("Cant find metadata with id {} in p4Info file.", packetMetadata.getMetadataId());
return null;
}
}).filter(Objects::nonNull).collect(Collectors.toList());
}
}