blob: e5d11a5e9dc3f8450ce5d0af4f841ea3b4f1e5c5 [file] [log] [blame]
Yi Tsengfa4a1c72017-11-03 10:22:38 -07001/*
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.pipelines.fabric;
18
19import com.google.common.collect.ImmutableList;
20import org.onlab.packet.MacAddress;
Yi Tseng1b154bd2017-11-20 17:48:19 -080021import org.onlab.packet.MplsLabel;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070022import org.onlab.packet.VlanId;
23import org.onlab.util.ImmutableByteSequence;
24import org.onosproject.net.PortNumber;
Yi Tsengdf3eec52018-02-15 14:56:02 -080025import org.onosproject.net.flow.DefaultTrafficTreatment;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070026import org.onosproject.net.flow.TrafficTreatment;
27import org.onosproject.net.flow.instructions.Instruction;
28import org.onosproject.net.flow.instructions.Instructions;
29import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
30import org.onosproject.net.flow.instructions.L2ModificationInstruction;
31import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
Yi Tseng1b154bd2017-11-20 17:48:19 -080032import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070033import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
34import org.onosproject.net.pi.model.PiActionId;
35import org.onosproject.net.pi.model.PiPipelineInterpreter.PiInterpreterException;
Yi Tseng47eac892018-07-11 02:17:04 +080036import org.onosproject.net.pi.model.PiTableId;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070037import org.onosproject.net.pi.runtime.PiAction;
38import org.onosproject.net.pi.runtime.PiActionParam;
Yi Tseng1b154bd2017-11-20 17:48:19 -080039import org.slf4j.Logger;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070040
41import java.util.List;
42
43import static java.lang.String.format;
44import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070045import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
46import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020047import static org.onosproject.pipelines.fabric.FabricUtils.getOutputPort;
Yi Tseng1b154bd2017-11-20 17:48:19 -080048import static org.slf4j.LoggerFactory.getLogger;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070049
50
51final class FabricTreatmentInterpreter {
Yi Tseng1b154bd2017-11-20 17:48:19 -080052 private static final Logger log = getLogger(FabricTreatmentInterpreter.class);
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020053 private static final String INVALID_TREATMENT = "Invalid treatment for %s block [%s]";
54 private static final String INVALID_TREATMENT_WITH_EXP = "Invalid treatment for %s block: %s [%s]";
Charles Chancf696e52018-08-16 16:25:13 -070055 private static final PiAction NOP = PiAction.builder().withId(FabricConstants.NOP).build();
56 private static final PiAction NOP_ACL = PiAction.builder()
57 .withId(FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ACL).build();
Yi Tseng20f9e7b2018-05-24 23:27:39 +080058
59 private static final PiAction POP_VLAN = PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +080060 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
Yi Tseng20f9e7b2018-05-24 23:27:39 +080061 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -070062
63 // Hide default constructor
64 protected FabricTreatmentInterpreter() {
65 }
66
67 /*
68 * In Filtering block, we need to implement these actions:
Yi Tsengfa4a1c72017-11-03 10:22:38 -070069 * push_internal_vlan
70 * set_vlan
71 * nop
72 *
73 * Unsupported, using PiAction directly:
74 * set_forwarding_type
Yi Tseng47eac892018-07-11 02:17:04 +080075 * drop
Yi Tsengfa4a1c72017-11-03 10:22:38 -070076 */
77
Yi Tseng47eac892018-07-11 02:17:04 +080078 static PiAction mapFilteringTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -070079 throws PiInterpreterException {
80 List<Instruction> instructions = treatment.allInstructions();
81 Instruction noActInst = Instructions.createNoAction();
82 if (instructions.isEmpty() || instructions.contains(noActInst)) {
83 // nop
Yi Tseng20f9e7b2018-05-24 23:27:39 +080084 return NOP;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070085 }
86
87 L2ModificationInstruction.ModVlanHeaderInstruction pushVlanInst = null;
88 ModVlanIdInstruction setVlanInst = null;
89
90 for (Instruction inst : instructions) {
91 if (inst.type() == L2MODIFICATION) {
92 L2ModificationInstruction l2Inst = (L2ModificationInstruction) inst;
93
94 if (l2Inst.subtype() == VLAN_PUSH) {
95 pushVlanInst = (L2ModificationInstruction.ModVlanHeaderInstruction) l2Inst;
96
97 } else if (l2Inst.subtype() == VLAN_ID) {
98 setVlanInst = (ModVlanIdInstruction) l2Inst;
99 }
100 }
101 }
102
103 if (setVlanInst == null) {
104 throw new PiInterpreterException(format(INVALID_TREATMENT, "filtering", treatment));
105 }
106
107 VlanId vlanId = setVlanInst.vlanId();
Yi Tseng43ee7e82018-04-12 16:37:34 +0800108 PiActionParam param = new PiActionParam(FabricConstants.NEW_VLAN_ID,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700109 ImmutableByteSequence.copyFrom(vlanId.toShort()));
110 PiActionId actionId;
111 if (pushVlanInst != null) {
112 // push_internal_vlan
Yi Tseng43ee7e82018-04-12 16:37:34 +0800113 actionId = FabricConstants.FABRIC_INGRESS_FILTERING_PUSH_INTERNAL_VLAN;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700114 } else {
Yi Tseng43ee7e82018-04-12 16:37:34 +0800115 actionId = FabricConstants.FABRIC_INGRESS_FILTERING_SET_VLAN;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700116 }
117
118 // set_vlan
119 return PiAction.builder()
120 .withId(actionId)
121 .withParameter(param)
122 .build();
123 }
124
125 /*
126 * In forwarding block, we need to implement these actions:
Yi Tseng47eac892018-07-11 02:17:04 +0800127 * send_to_controller
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700128 *
129 * Unsupported, using PiAction directly:
Yi Tseng47eac892018-07-11 02:17:04 +0800130 * set_next_id_bridging
131 * pop_mpls_and_next
132 * set_next_id_unicast_v4
133 * set_next_id_multicast_v4
134 * set_next_id_acl
135 * drop
136 * set_next_id_unicast_v6
137 * set_next_id_multicast_v6
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700138 */
139
Yi Tseng47eac892018-07-11 02:17:04 +0800140 public static PiAction mapForwardingTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700141 throws PiInterpreterException {
Yi Tsengdf3eec52018-02-15 14:56:02 -0800142 // Empty treatment, generate table entry with no action
143 if (treatment.equals(DefaultTrafficTreatment.emptyTreatment())) {
Charles Chancf696e52018-08-16 16:25:13 -0700144 if (tableId.equals(FabricConstants.FABRIC_INGRESS_FORWARDING_ACL)) {
145 return NOP_ACL;
146 } else {
147 return NOP;
148 }
Yi Tsengdf3eec52018-02-15 14:56:02 -0800149 }
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200150 PortNumber outPort = getOutputPort(treatment);
151 if (outPort == null
152 || !outPort.equals(PortNumber.CONTROLLER)
153 || treatment.allInstructions().size() > 1) {
154 throw new PiInterpreterException(
155 format(INVALID_TREATMENT_WITH_EXP,
156 "forwarding", "supports only punt/clone to CPU actions",
157 treatment));
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700158 }
159
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200160 final PiActionId actionId = treatment.clearedDeferred()
161 ? FabricConstants.FABRIC_INGRESS_FORWARDING_PUNT_TO_CPU
162 : FabricConstants.FABRIC_INGRESS_FORWARDING_CLONE_TO_CPU;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700163
164 return PiAction.builder()
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200165 .withId(actionId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700166 .build();
167 }
168
169 /*
170 * In Next block, we need to implement these actions:
Yi Tseng47eac892018-07-11 02:17:04 +0800171 * set_vlan
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700172 * set_vlan_output
Yi Tseng47eac892018-07-11 02:17:04 +0800173 * output_simple
174 * output_hashed
175 * l3_routing_simple
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800176 * l3_routing_vlan
Yi Tseng47eac892018-07-11 02:17:04 +0800177 * l3_routing_hashed
178 * mpls_routing_v4_simple
179 * mpls_routing_v6_simple
180 * mpls_routing_v4_hashed
181 * mpls_routing_v6_hashed
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700182 *
Yi Tseng1b154bd2017-11-20 17:48:19 -0800183 * Unsupported, need to find a way to implement it
Yi Tseng47eac892018-07-11 02:17:04 +0800184 *
185 * set_mcast_group
186 *
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700187 */
188
Yi Tseng47eac892018-07-11 02:17:04 +0800189 public static PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700190 throws PiInterpreterException {
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800191 // TODO: refactor this method
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700192 List<Instruction> insts = treatment.allInstructions();
193 OutputInstruction outInst = null;
194 ModEtherInstruction modEthDstInst = null;
195 ModEtherInstruction modEthSrcInst = null;
196 ModVlanIdInstruction modVlanIdInst = null;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800197 ModMplsLabelInstruction modMplsInst = null;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700198
Yi Tseng1b154bd2017-11-20 17:48:19 -0800199 // TODO: add NextFunctionType (like ForwardingFunctionType)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700200 for (Instruction inst : insts) {
201 switch (inst.type()) {
202 case L2MODIFICATION:
203 L2ModificationInstruction l2Inst = (L2ModificationInstruction) inst;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800204 switch (l2Inst.subtype()) {
205 case ETH_SRC:
206 modEthSrcInst = (ModEtherInstruction) l2Inst;
207 break;
208 case ETH_DST:
209 modEthDstInst = (ModEtherInstruction) l2Inst;
210 break;
211 case VLAN_ID:
212 modVlanIdInst = (ModVlanIdInstruction) l2Inst;
213 break;
214 case MPLS_LABEL:
215 modMplsInst = (ModMplsLabelInstruction) l2Inst;
216 break;
Charles Chan8da57792018-08-16 16:59:42 -0700217 case VLAN_POP:
218 // VLAN_POP will be handled by mapEgressNextTreatment()
219 break;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800220 default:
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200221 log.warn("Unsupported l2 instruction sub type {} [table={}, {}]",
222 l2Inst.subtype(), tableId, treatment);
Yi Tseng1b154bd2017-11-20 17:48:19 -0800223 break;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700224 }
225 break;
226 case OUTPUT:
227 outInst = (OutputInstruction) inst;
228 break;
229 default:
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200230 log.warn("Unsupported instruction sub type {} [table={}, {}]",
231 inst.type(), tableId, treatment);
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700232 break;
233 }
234 }
235
Yi Tseng47eac892018-07-11 02:17:04 +0800236 if (tableId.equals(FabricConstants.FABRIC_INGRESS_NEXT_VLAN_META) &&
237 modVlanIdInst != null) {
238 // set_vlan
239 VlanId vlanId = modVlanIdInst.vlanId();
240 PiActionParam newVlanParam =
241 new PiActionParam(FabricConstants.NEW_VLAN_ID,
242 ImmutableByteSequence.copyFrom(vlanId.toShort()));
243 return PiAction.builder()
244 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
245 .withParameter(newVlanParam)
246 .build();
247 }
248
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700249 if (outInst == null) {
Yi Tseng47eac892018-07-11 02:17:04 +0800250 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700251 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800252
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700253 short portNum = (short) outInst.port().toLong();
Yi Tseng43ee7e82018-04-12 16:37:34 +0800254 PiActionParam portNumParam = new PiActionParam(FabricConstants.PORT_NUM,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700255 ImmutableByteSequence.copyFrom(portNum));
256 if (modEthDstInst == null && modEthSrcInst == null) {
257 if (modVlanIdInst != null) {
258 VlanId vlanId = modVlanIdInst.vlanId();
259 PiActionParam vlanParam =
Yi Tseng43ee7e82018-04-12 16:37:34 +0800260 new PiActionParam(FabricConstants.NEW_VLAN_ID,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700261 ImmutableByteSequence.copyFrom(vlanId.toShort()));
Yi Tseng47eac892018-07-11 02:17:04 +0800262 // set_vlan_output (simple table)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700263 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800264 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN_OUTPUT)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700265 .withParameters(ImmutableList.of(portNumParam, vlanParam))
266 .build();
267 } else {
Yi Tseng47eac892018-07-11 02:17:04 +0800268 // output (simple or hashed table)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700269 return PiAction.builder()
Yi Tseng47eac892018-07-11 02:17:04 +0800270 .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700271 .withParameter(portNumParam)
272 .build();
273 }
274 }
275
276 if (modEthDstInst != null && modEthSrcInst != null) {
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700277 MacAddress srcMac = modEthSrcInst.mac();
278 MacAddress dstMac = modEthDstInst.mac();
Yi Tseng43ee7e82018-04-12 16:37:34 +0800279 PiActionParam srcMacParam = new PiActionParam(FabricConstants.SMAC,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700280 ImmutableByteSequence.copyFrom(srcMac.toBytes()));
Yi Tseng43ee7e82018-04-12 16:37:34 +0800281 PiActionParam dstMacParam = new PiActionParam(FabricConstants.DMAC,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700282 ImmutableByteSequence.copyFrom(dstMac.toBytes()));
Yi Tseng1b154bd2017-11-20 17:48:19 -0800283
284 if (modMplsInst != null) {
285 // MPLS routing
286 MplsLabel mplsLabel = modMplsInst.label();
287 try {
288 ImmutableByteSequence mplsValue =
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700289 ImmutableByteSequence.copyFrom(mplsLabel.toInt()).fit(20);
Yi Tseng43ee7e82018-04-12 16:37:34 +0800290 PiActionParam mplsParam = new PiActionParam(FabricConstants.LABEL, mplsValue);
Yi Tseng47eac892018-07-11 02:17:04 +0800291
292 PiActionId actionId;
293 // FIXME: finds a way to determine v4 or v6
294 if (tableId.equals(FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE)) {
295 actionId = FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_V4_SIMPLE;
296 } else if (tableId.equals(FabricConstants.FABRIC_INGRESS_NEXT_HASHED)) {
297 actionId = FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_V4_HASHED;
298 } else {
299 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
300 }
301
Yi Tseng1b154bd2017-11-20 17:48:19 -0800302 return PiAction.builder()
Yi Tseng47eac892018-07-11 02:17:04 +0800303 .withId(actionId)
Yi Tseng1b154bd2017-11-20 17:48:19 -0800304 .withParameters(ImmutableList.of(portNumParam,
305 srcMacParam,
306 dstMacParam,
307 mplsParam))
308 .build();
309 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
310 // Basically this won't happened because we already limited
311 // size of mpls value to 20 bits (0xFFFFF)
312 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
313 }
314 }
315
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800316 if (modVlanIdInst != null) {
317 VlanId vlanId = modVlanIdInst.vlanId();
318 PiActionParam vlanParam =
Yi Tseng43ee7e82018-04-12 16:37:34 +0800319 new PiActionParam(FabricConstants.NEW_VLAN_ID,
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800320 ImmutableByteSequence.copyFrom(vlanId.toShort()));
321 // L3 routing and set VLAN
322 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800323 .withId(FabricConstants.FABRIC_INGRESS_NEXT_L3_ROUTING_VLAN)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800324 .withParameters(ImmutableList.of(srcMacParam, dstMacParam, portNumParam, vlanParam))
325 .build();
326 } else {
Yi Tseng47eac892018-07-11 02:17:04 +0800327 PiActionId actionId;
328 if (tableId.equals(FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE)) {
329 actionId = FabricConstants.FABRIC_INGRESS_NEXT_L3_ROUTING_SIMPLE;
330 } else if (tableId.equals(FabricConstants.FABRIC_INGRESS_NEXT_HASHED)) {
331 actionId = FabricConstants.FABRIC_INGRESS_NEXT_L3_ROUTING_HASHED;
332 } else {
333 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
334 }
335
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800336 // L3 routing
337 return PiAction.builder()
Yi Tseng47eac892018-07-11 02:17:04 +0800338 .withId(actionId)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800339 .withParameters(ImmutableList.of(portNumParam,
340 srcMacParam,
341 dstMacParam))
342 .build();
343 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700344 }
345
346 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
347 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800348
Yi Tseng47eac892018-07-11 02:17:04 +0800349 /*
350 * pop_vlan
351 */
352 public static PiAction mapEgressNextTreatment(TrafficTreatment treatment, PiTableId tableId) {
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800353 // Pop VLAN action for now, may add new action to this control block in the future.
354 return treatment.allInstructions()
355 .stream()
356 .filter(inst -> inst.type() == Instruction.Type.L2MODIFICATION)
357 .map(inst -> (L2ModificationInstruction) inst)
358 .filter(inst -> inst.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP)
359 .findFirst()
360 .map(inst -> POP_VLAN)
361 .orElse(NOP);
362 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700363}