blob: 828d623e4477a023178f0ee172cf172c27973f35 [file] [log] [blame]
Carmelo Cascone770507f2017-09-14 20:58:04 +02001/*
2 * Copyright 2017-present Open Networking Foundation
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.p4tutorial.pipeconf;
18
Carmelo Cascone770507f2017-09-14 20:58:04 +020019import com.google.common.collect.ImmutableList;
Carmelo Cascone33f36a02019-04-17 20:05:21 -070020import com.google.common.collect.ImmutableMap;
Carmelo Cascone700648c2018-04-11 12:02:16 -070021import com.google.common.collect.Lists;
Ray Milkeyf0c47612017-09-28 11:29:38 -070022import org.onlab.packet.DeserializationException;
Carmelo Cascone770507f2017-09-14 20:58:04 +020023import org.onlab.packet.Ethernet;
24import org.onlab.util.ImmutableByteSequence;
25import org.onosproject.net.ConnectPoint;
Carmelo Cascone700648c2018-04-11 12:02:16 -070026import org.onosproject.net.DeviceId;
Carmelo Cascone770507f2017-09-14 20:58:04 +020027import org.onosproject.net.Port;
28import org.onosproject.net.PortNumber;
29import org.onosproject.net.device.DeviceService;
30import org.onosproject.net.driver.AbstractHandlerBehaviour;
31import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.flow.criteria.Criterion;
33import org.onosproject.net.flow.instructions.Instruction;
Carmelo Cascone700648c2018-04-11 12:02:16 -070034import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Carmelo Cascone770507f2017-09-14 20:58:04 +020035import org.onosproject.net.packet.DefaultInboundPacket;
36import org.onosproject.net.packet.InboundPacket;
37import org.onosproject.net.packet.OutboundPacket;
Carmelo Cascone87892e22017-11-13 16:01:29 -080038import org.onosproject.net.pi.model.PiActionId;
39import org.onosproject.net.pi.model.PiActionParamId;
Carmelo Cascone87892e22017-11-13 16:01:29 -080040import org.onosproject.net.pi.model.PiMatchFieldId;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080041import org.onosproject.net.pi.model.PiPacketMetadataId;
Carmelo Cascone770507f2017-09-14 20:58:04 +020042import org.onosproject.net.pi.model.PiPipelineInterpreter;
Carmelo Cascone87892e22017-11-13 16:01:29 -080043import org.onosproject.net.pi.model.PiTableId;
Carmelo Cascone770507f2017-09-14 20:58:04 +020044import org.onosproject.net.pi.runtime.PiAction;
Carmelo Cascone770507f2017-09-14 20:58:04 +020045import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080046import org.onosproject.net.pi.runtime.PiPacketMetadata;
Carmelo Cascone770507f2017-09-14 20:58:04 +020047import org.onosproject.net.pi.runtime.PiPacketOperation;
Carmelo Cascone770507f2017-09-14 20:58:04 +020048
49import java.nio.ByteBuffer;
50import java.util.Collection;
51import java.util.List;
Carmelo Cascone33f36a02019-04-17 20:05:21 -070052import java.util.Map;
Carmelo Cascone770507f2017-09-14 20:58:04 +020053import java.util.Optional;
54
55import static java.lang.String.format;
Carmelo Cascone770507f2017-09-14 20:58:04 +020056import static org.onlab.util.ImmutableByteSequence.copyFrom;
Carmelo Cascone770507f2017-09-14 20:58:04 +020057import static org.onosproject.net.PortNumber.CONTROLLER;
58import static org.onosproject.net.PortNumber.FLOOD;
59import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
Carmelo Cascone87892e22017-11-13 16:01:29 -080060import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
Carmelo Cascone770507f2017-09-14 20:58:04 +020061
62/**
Carmelo Cascone700648c2018-04-11 12:02:16 -070063 * Implementation of a pipeline interpreter for the mytunnel.p4 program.
Carmelo Cascone770507f2017-09-14 20:58:04 +020064 */
Carmelo Cascone700648c2018-04-11 12:02:16 -070065public final class PipelineInterpreterImpl
66 extends AbstractHandlerBehaviour
67 implements PiPipelineInterpreter {
Carmelo Cascone770507f2017-09-14 20:58:04 +020068
Carmelo Cascone700648c2018-04-11 12:02:16 -070069 private static final String DOT = ".";
Kevin Chuangd79bfd02017-11-14 10:34:35 -080070 private static final String HDR = "hdr";
Carmelo Cascone700648c2018-04-11 12:02:16 -070071 private static final String C_INGRESS = "c_ingress";
72 private static final String T_L2_FWD = "t_l2_fwd";
Carmelo Cascone770507f2017-09-14 20:58:04 +020073 private static final String EGRESS_PORT = "egress_port";
74 private static final String INGRESS_PORT = "ingress_port";
75 private static final String ETHERNET = "ethernet";
76 private static final String STANDARD_METADATA = "standard_metadata";
77 private static final int PORT_FIELD_BITWIDTH = 9;
78
Carmelo Cascone700648c2018-04-11 12:02:16 -070079 private static final PiMatchFieldId INGRESS_PORT_ID =
80 PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
81 private static final PiMatchFieldId ETH_DST_ID =
82 PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
83 private static final PiMatchFieldId ETH_SRC_ID =
84 PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
85 private static final PiMatchFieldId ETH_TYPE_ID =
86 PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
Carmelo Cascone770507f2017-09-14 20:58:04 +020087
Carmelo Cascone700648c2018-04-11 12:02:16 -070088 private static final PiTableId TABLE_L2_FWD_ID =
89 PiTableId.of(C_INGRESS + DOT + T_L2_FWD);
90
91 private static final PiActionId ACT_ID_NOP =
92 PiActionId.of("NoAction");
93 private static final PiActionId ACT_ID_SEND_TO_CPU =
94 PiActionId.of(C_INGRESS + DOT + "send_to_cpu");
95 private static final PiActionId ACT_ID_SET_EGRESS_PORT =
Carmelo Cascone9bc6a142018-04-16 17:35:15 -070096 PiActionId.of(C_INGRESS + DOT + "set_out_port");
Carmelo Cascone700648c2018-04-11 12:02:16 -070097
98 private static final PiActionParamId ACT_PARAM_ID_PORT =
99 PiActionParamId.of("port");
100
Carmelo Cascone33f36a02019-04-17 20:05:21 -0700101 private static final Map<Integer, PiTableId> TABLE_MAP =
102 new ImmutableMap.Builder<Integer, PiTableId>()
Carmelo Cascone700648c2018-04-11 12:02:16 -0700103 .put(0, TABLE_L2_FWD_ID)
104 .build();
Carmelo Cascone770507f2017-09-14 20:58:04 +0200105
Carmelo Cascone33f36a02019-04-17 20:05:21 -0700106 private static final Map<Criterion.Type, PiMatchFieldId> CRITERION_MAP =
107 ImmutableMap.<Criterion.Type, PiMatchFieldId>builder()
Carmelo Cascone770507f2017-09-14 20:58:04 +0200108 .put(Criterion.Type.IN_PORT, INGRESS_PORT_ID)
109 .put(Criterion.Type.ETH_DST, ETH_DST_ID)
110 .put(Criterion.Type.ETH_SRC, ETH_SRC_ID)
111 .put(Criterion.Type.ETH_TYPE, ETH_TYPE_ID)
112 .build();
113
114 @Override
Carmelo Cascone87892e22017-11-13 16:01:29 -0800115 public Optional<PiMatchFieldId> mapCriterionType(Criterion.Type type) {
Carmelo Cascone770507f2017-09-14 20:58:04 +0200116 return Optional.ofNullable(CRITERION_MAP.get(type));
117 }
118
119 @Override
Carmelo Cascone770507f2017-09-14 20:58:04 +0200120 public Optional<PiTableId> mapFlowRuleTableId(int flowRuleTableId) {
121 return Optional.ofNullable(TABLE_MAP.get(flowRuleTableId));
122 }
123
124 @Override
Carmelo Cascone700648c2018-04-11 12:02:16 -0700125 public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
126 throws PiInterpreterException {
127
128 if (piTableId != TABLE_L2_FWD_ID) {
129 throw new PiInterpreterException(
130 "Can map treatments only for 't_l2_fwd' table");
131 }
132
133 if (treatment.allInstructions().size() == 0) {
134 // 0 instructions means "NoAction"
135 return PiAction.builder().withId(ACT_ID_NOP).build();
136 } else if (treatment.allInstructions().size() > 1) {
137 // We understand treatments with only 1 instruction.
138 throw new PiInterpreterException("Treatment has multiple instructions");
139 }
140
141 // Get the first and only instruction.
142 Instruction instruction = treatment.allInstructions().get(0);
143
144 if (instruction.type() != OUTPUT) {
145 // We can map only instructions of type OUTPUT.
146 throw new PiInterpreterException(format(
147 "Instruction of type '%s' not supported", instruction.type()));
148 }
149
150 OutputInstruction outInstruction = (OutputInstruction) instruction;
151 PortNumber port = outInstruction.port();
152 if (!port.isLogical()) {
153 return PiAction.builder()
154 .withId(ACT_ID_SET_EGRESS_PORT)
155 .withParameter(new PiActionParam(
156 ACT_PARAM_ID_PORT, copyFrom(port.toLong())))
157 .build();
158 } else if (port.equals(CONTROLLER)) {
159 return PiAction.builder()
160 .withId(ACT_ID_SEND_TO_CPU)
161 .build();
162 } else {
163 throw new PiInterpreterException(format(
164 "Output on logical port '%s' not supported", port));
165 }
166 }
167
168 @Override
169 public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
170 throws PiInterpreterException {
171
172 TrafficTreatment treatment = packet.treatment();
173
174 // We support only packet-out with OUTPUT instructions.
175 if (treatment.allInstructions().size() != 1 &&
176 treatment.allInstructions().get(0).type() != OUTPUT) {
177 throw new PiInterpreterException(
178 "Treatment not supported: " + treatment.toString());
179 }
180
181 Instruction instruction = treatment.allInstructions().get(0);
182 PortNumber port = ((OutputInstruction) instruction).port();
183 List<PiPacketOperation> piPacketOps = Lists.newArrayList();
184
185 if (!port.isLogical()) {
186 piPacketOps.add(createPiPacketOp(packet.data(), port.toLong()));
187 } else if (port.equals(FLOOD)) {
188 // Since mytunnel.p4 does not support flooding, we create a packet
189 // operation for each switch port.
190 DeviceService deviceService = handler().get(DeviceService.class);
191 DeviceId deviceId = packet.sendThrough();
192 for (Port p : deviceService.getPorts(deviceId)) {
193 piPacketOps.add(createPiPacketOp(packet.data(), p.number().toLong()));
194 }
195 } else {
196 throw new PiInterpreterException(format(
197 "Output on logical port '%s' not supported", port));
198 }
199
200 return piPacketOps;
201 }
202
203 @Override
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800204 public InboundPacket mapInboundPacket(PiPacketOperation packetIn, DeviceId deviceId)
Carmelo Cascone700648c2018-04-11 12:02:16 -0700205 throws PiInterpreterException {
206 // We assume that the packet is ethernet, which is fine since mytunnel.p4
207 // can deparse only ethernet packets.
208 Ethernet ethPkt;
209
210 try {
211 ethPkt = Ethernet.deserializer().deserialize(
212 packetIn.data().asArray(), 0, packetIn.data().size());
213 } catch (DeserializationException dex) {
214 throw new PiInterpreterException(dex.getMessage());
215 }
216
217 // Returns the ingress port packet metadata.
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800218 Optional<PiPacketMetadata> packetMetadata = packetIn.metadatas().stream()
Carmelo Cascone700648c2018-04-11 12:02:16 -0700219 .filter(metadata -> metadata.id().toString().equals(INGRESS_PORT))
220 .findFirst();
221
222 if (packetMetadata.isPresent()) {
223 short s = packetMetadata.get().value().asReadOnlyBuffer().getShort();
224 ConnectPoint receivedFrom = new ConnectPoint(
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800225 deviceId, PortNumber.portNumber(s));
Carmelo Cascone700648c2018-04-11 12:02:16 -0700226 return new DefaultInboundPacket(
227 receivedFrom, ethPkt, packetIn.data().asReadOnlyBuffer());
228 } else {
229 throw new PiInterpreterException(format(
230 "Missing metadata '%s' in packet-in received from '%s': %s",
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800231 INGRESS_PORT, deviceId, packetIn));
Carmelo Cascone700648c2018-04-11 12:02:16 -0700232 }
233 }
234
235 private PiPacketOperation createPiPacketOp(ByteBuffer data, long portNumber)
236 throws PiInterpreterException {
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800237 PiPacketMetadata metadata = createPacketMetadata(portNumber);
Carmelo Cascone700648c2018-04-11 12:02:16 -0700238 return PiPacketOperation.builder()
Carmelo Cascone700648c2018-04-11 12:02:16 -0700239 .withType(PACKET_OUT)
240 .withData(copyFrom(data))
241 .withMetadatas(ImmutableList.of(metadata))
242 .build();
243 }
244
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800245 private PiPacketMetadata createPacketMetadata(long portNumber)
Carmelo Cascone700648c2018-04-11 12:02:16 -0700246 throws PiInterpreterException {
247 try {
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800248 return PiPacketMetadata.builder()
249 .withId(PiPacketMetadataId.of(EGRESS_PORT))
Carmelo Cascone700648c2018-04-11 12:02:16 -0700250 .withValue(copyFrom(portNumber).fit(PORT_FIELD_BITWIDTH))
251 .build();
252 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
253 throw new PiInterpreterException(format(
254 "Port number %d too big, %s", portNumber, e.getMessage()));
255 }
256 }
Carmelo Cascone770507f2017-09-14 20:58:04 +0200257}