blob: 31d4627975770caf62774332f77ae70cf34fe9da [file] [log] [blame]
Carmelo Cascone59f57de2017-07-11 19:55:09 -04001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.drivers.bmv2;
18
19import com.google.common.collect.ImmutableBiMap;
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020020import com.google.common.collect.ImmutableList;
Andrea Campanella288b2732017-07-28 14:16:16 +020021import org.onlab.packet.Ethernet;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040022import org.onlab.util.ImmutableByteSequence;
Andrea Campanella288b2732017-07-28 14:16:16 +020023import org.onosproject.net.ConnectPoint;
24import org.onosproject.net.DeviceId;
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020025import org.onosproject.net.Port;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040026import org.onosproject.net.PortNumber;
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020027import org.onosproject.net.device.DeviceService;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040028import org.onosproject.net.driver.AbstractHandlerBehaviour;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.flow.criteria.Criterion;
31import org.onosproject.net.flow.instructions.Instruction;
32import org.onosproject.net.flow.instructions.Instructions;
Andrea Campanella288b2732017-07-28 14:16:16 +020033import org.onosproject.net.packet.DefaultInboundPacket;
34import org.onosproject.net.packet.InboundPacket;
Andrea Campanella432f7182017-07-14 18:43:27 +020035import org.onosproject.net.packet.OutboundPacket;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040036import org.onosproject.net.pi.model.PiPipelineInterpreter;
37import org.onosproject.net.pi.runtime.PiAction;
38import org.onosproject.net.pi.runtime.PiActionId;
39import org.onosproject.net.pi.runtime.PiActionParam;
40import org.onosproject.net.pi.runtime.PiActionParamId;
41import org.onosproject.net.pi.runtime.PiHeaderFieldId;
Andrea Campanella432f7182017-07-14 18:43:27 +020042import org.onosproject.net.pi.runtime.PiPacketMetadata;
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020043import org.onosproject.net.pi.runtime.PiPacketMetadataId;
44import org.onosproject.net.pi.runtime.PiPacketOperation;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040045import org.onosproject.net.pi.runtime.PiTableId;
46
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020047import java.nio.ByteBuffer;
Andrea Campanella432f7182017-07-14 18:43:27 +020048import java.util.Collection;
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020049import java.util.List;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040050import java.util.Optional;
51
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020052import static java.util.stream.Collectors.toList;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040053import static org.onosproject.net.PortNumber.CONTROLLER;
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020054import static org.onosproject.net.PortNumber.FLOOD;
55import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
56import static org.onosproject.net.pi.runtime.PiPacketOperation.Type.PACKET_OUT;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040057
58/**
59 * Interpreter implementation for the default pipeconf.
60 */
61public class Bmv2DefaultInterpreter extends AbstractHandlerBehaviour implements PiPipelineInterpreter {
62 private static final String TABLE0 = "table0";
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +020063 private static final String SEND_TO_CPU = "send_to_cpu";
Carmelo Cascone59f57de2017-07-11 19:55:09 -040064 private static final String PORT = "port";
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +020065 private static final String DROP = "drop";
66 private static final String SET_EGRESS_PORT = "set_egress_port";
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020067 private static final String EGRESS_PORT = "egress_port";
68 private static final int PORT_NUMBER_BIT_WIDTH = 9;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040069
70 private static final PiHeaderFieldId IN_PORT_ID = PiHeaderFieldId.of("standard_metadata", "ingress_port");
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +020071 private static final PiHeaderFieldId ETH_DST_ID = PiHeaderFieldId.of("ethernet", "dstAddr");
72 private static final PiHeaderFieldId ETH_SRC_ID = PiHeaderFieldId.of("ethernet", "srcAddr");
73 private static final PiHeaderFieldId ETH_TYPE_ID = PiHeaderFieldId.of("ethernet", "etherType");
Carmelo Cascone59f57de2017-07-11 19:55:09 -040074
Frank Wange254d6c2017-07-20 14:46:36 +080075 private static final ImmutableBiMap<Criterion.Type, PiHeaderFieldId> CRITERION_MAP =
76 new ImmutableBiMap.Builder<Criterion.Type, PiHeaderFieldId>()
77 .put(Criterion.Type.IN_PORT, IN_PORT_ID)
78 .put(Criterion.Type.ETH_DST, ETH_DST_ID)
79 .put(Criterion.Type.ETH_SRC, ETH_SRC_ID)
80 .put(Criterion.Type.ETH_TYPE, ETH_TYPE_ID)
81 .build();
Carmelo Cascone59f57de2017-07-11 19:55:09 -040082
83 private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
84 0, PiTableId.of(TABLE0));
Andrea Campanella288b2732017-07-28 14:16:16 +020085 public static final String INGRESS_PORT = "ingress_port";
Carmelo Cascone59f57de2017-07-11 19:55:09 -040086
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020087
Carmelo Cascone59f57de2017-07-11 19:55:09 -040088 @Override
Carmelo Casconef3a1a382017-07-27 12:04:39 -040089 public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId) throws PiInterpreterException {
Carmelo Cascone59f57de2017-07-11 19:55:09 -040090
91 if (treatment.allInstructions().size() == 0) {
92 // No instructions means drop for us.
93 return actionWithName(DROP);
94 } else if (treatment.allInstructions().size() > 1) {
95 // Otherwise, we understand treatments with only 1 instruction.
96 throw new PiPipelineInterpreter.PiInterpreterException("Treatment has multiple instructions");
97 }
98
99 Instruction instruction = treatment.allInstructions().get(0);
100
101 switch (instruction.type()) {
102 case OUTPUT:
103 Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) instruction;
104 PortNumber port = outInstruction.port();
105 if (!port.isLogical()) {
106 PiAction.builder()
107 .withId(PiActionId.of(SET_EGRESS_PORT))
108 .withParameter(new PiActionParam(PiActionParamId.of(PORT),
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200109 ImmutableByteSequence.copyFrom(port.toLong())))
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400110 .build();
111 } else if (port.equals(CONTROLLER)) {
112 return actionWithName(SEND_TO_CPU);
113 } else {
114 throw new PiInterpreterException("Egress on logical port not supported: " + port);
115 }
116 case NOACTION:
117 return actionWithName(DROP);
118 default:
119 throw new PiInterpreterException("Instruction type not supported: " + instruction.type().name());
120 }
121 }
122
Andrea Campanella432f7182017-07-14 18:43:27 +0200123 @Override
Carmelo Casconef3a1a382017-07-27 12:04:39 -0400124 public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
Andrea Campanella432f7182017-07-14 18:43:27 +0200125 throws PiInterpreterException {
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200126 TrafficTreatment treatment = packet.treatment();
127
128 // default.p4 supports only OUTPUT instructions.
129 List<Instructions.OutputInstruction> outInstructions = treatment.allInstructions()
130 .stream()
131 .filter(i -> i.type().equals(OUTPUT))
132 .map(i -> (Instructions.OutputInstruction) i)
133 .collect(toList());
134
135 if (treatment.allInstructions().size() != outInstructions.size()) {
136 // There are other instructions that are not of type OUTPUT
137 throw new PiInterpreterException("Treatment not supported: " + treatment);
138 }
139
140 ImmutableList.Builder<PiPacketOperation> builder = ImmutableList.builder();
141 for (Instructions.OutputInstruction outInst : outInstructions) {
142 if (outInst.port().isLogical() && !outInst.port().equals(FLOOD)) {
143 throw new PiInterpreterException("Logical port not supported: " +
144 outInst.port());
145 } else if (outInst.port().equals(FLOOD)) {
146 //Since default.p4 does not support flood for each port of the device
147 // create a packet operation to send the packet out of that specific port
148 for (Port port : handler().get(DeviceService.class).getPorts(packet.sendThrough())) {
149 builder.add(createPiPacketOperation(packet.data(), port.number().toLong()));
150 }
151 } else {
152 builder.add(createPiPacketOperation(packet.data(), outInst.port().toLong()));
153 }
154 }
155 return builder.build();
156 }
157
Andrea Campanella288b2732017-07-28 14:16:16 +0200158 @Override
159 public InboundPacket mapInboundPacket(DeviceId deviceId, PiPacketOperation packetIn)
160 throws PiInterpreterException {
161
162 //We are assuming that the packet is ethernet type
163 Ethernet ethPkt = new Ethernet();
164
165 ethPkt.deserialize(packetIn.data().asArray(), 0, packetIn.data().size());
166
167 //Returns the ingress port packet metadata
168 Optional<PiPacketMetadata> packetMetadata = packetIn.metadatas()
169 .stream().filter(metadata -> metadata.id().name().equals(INGRESS_PORT))
170 .findFirst();
171
172 if (packetMetadata.isPresent()) {
173
174 //Obtaining the ingress port as an immutable byte sequence
175 ImmutableByteSequence portByteSequence = packetMetadata.get().value();
176
177 //Converting immutableByteSequence to short
178 short s = portByteSequence.asReadOnlyBuffer().getShort();
179
180 ConnectPoint receivedFrom = new ConnectPoint(deviceId, PortNumber.portNumber(s));
181
182 //FIXME should be optimizable with .asReadOnlyBytebuffer
183 ByteBuffer rawData = ByteBuffer.wrap(packetIn.data().asArray());
184 return new DefaultInboundPacket(receivedFrom, ethPkt, rawData);
185
186 } else {
187 throw new PiInterpreterException("Can't get packet metadata for" + INGRESS_PORT);
188 }
189 }
190
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200191 private PiPacketOperation createPiPacketOperation(ByteBuffer data, long portNumber) throws PiInterpreterException {
192 //create the metadata
193 PiPacketMetadata metadata = createPacketMetadata(portNumber);
194
195 //Create the Packet operation
196 return PiPacketOperation.builder()
197 .withType(PACKET_OUT)
198 .withData(ImmutableByteSequence.copyFrom(data))
199 .withMetadatas(ImmutableList.of(metadata))
200 .build();
201 }
202
203 private PiPacketMetadata createPacketMetadata(long portNumber) throws PiInterpreterException {
204 ImmutableByteSequence portValue = ImmutableByteSequence.copyFrom(portNumber);
205 //FIXME remove hardcoded bitWidth and retrieve it from pipelineModel
206 try {
207 portValue = ImmutableByteSequence.fit(portValue, PORT_NUMBER_BIT_WIDTH);
208 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
209 throw new PiInterpreterException("Port number too big: {}" +
210 portNumber + " causes " + e.getMessage());
211 }
212 return PiPacketMetadata.builder()
213 .withId(PiPacketMetadataId.of(EGRESS_PORT))
214 .withValue(portValue)
215 .build();
Andrea Campanella432f7182017-07-14 18:43:27 +0200216 }
217
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400218 /**
219 * Returns an action instance with no runtime parameters.
220 */
221 private PiAction actionWithName(String name) {
222 return PiAction.builder().withId(PiActionId.of(name)).build();
223 }
224
225 @Override
226 public Optional<PiHeaderFieldId> mapCriterionType(Criterion.Type type) {
227 return Optional.ofNullable(CRITERION_MAP.get(type));
228 }
229
230 @Override
231 public Optional<Criterion.Type> mapPiHeaderFieldId(PiHeaderFieldId headerFieldId) {
232 return Optional.ofNullable(CRITERION_MAP.inverse().get(headerFieldId));
233 }
234
235 @Override
236 public Optional<PiTableId> mapFlowRuleTableId(int flowRuleTableId) {
237 return Optional.ofNullable(TABLE_MAP.get(flowRuleTableId));
238 }
Carmelo Cascone0b22d8f2017-07-31 07:22:27 +0200239
240 @Override
241 public Optional<Integer> mapPiTableId(PiTableId piTableId) {
242 return Optional.ofNullable(TABLE_MAP.inverse().get(piTableId));
243 }
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400244}