blob: fe2d4905268127cbf26e3b91e2239220687d8e58 [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
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070017package org.onosproject.pipelines.fabric.impl.behaviour;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070018
Carmelo Casconeb5324e72018-11-25 02:26:32 -080019import com.google.common.collect.ImmutableMap;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070020import org.onosproject.net.PortNumber;
Yi Tsengdf3eec52018-02-15 14:56:02 -080021import org.onosproject.net.flow.DefaultTrafficTreatment;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070022import org.onosproject.net.flow.TrafficTreatment;
23import org.onosproject.net.flow.instructions.Instruction;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070024import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
25import org.onosproject.net.flow.instructions.L2ModificationInstruction;
26import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
Yi Tseng1b154bd2017-11-20 17:48:19 -080027import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070028import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
29import org.onosproject.net.pi.model.PiActionId;
30import org.onosproject.net.pi.model.PiPipelineInterpreter.PiInterpreterException;
Yi Tseng47eac892018-07-11 02:17:04 +080031import org.onosproject.net.pi.model.PiTableId;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070032import org.onosproject.net.pi.runtime.PiAction;
33import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Cascone2102bfb2020-12-04 16:54:24 -080034import org.onosproject.pipelines.fabric.FabricConstants;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070035
Daniele Morof51d0c12019-07-30 10:43:10 -070036import java.util.List;
37import java.util.stream.Collectors;
38
Yi Tsengfa4a1c72017-11-03 10:22:38 -070039import static java.lang.String.format;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080040import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020041import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
42import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_SRC;
43import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.MPLS_LABEL;
44import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.MPLS_PUSH;
45import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
46import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
47import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070048import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.instruction;
49import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instruction;
50import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instructions;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070051
Carmelo Casconeb5324e72018-11-25 02:26:32 -080052/**
53 * Treatment translation logic.
54 */
Yi Tsengfa4a1c72017-11-03 10:22:38 -070055final class FabricTreatmentInterpreter {
Yi Tseng20f9e7b2018-05-24 23:27:39 +080056
Daniele Morof51d0c12019-07-30 10:43:10 -070057 private final FabricCapabilities capabilities;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080058 private static final ImmutableMap<PiTableId, PiActionId> NOP_ACTIONS =
59 ImmutableMap.<PiTableId, PiActionId>builder()
60 .put(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
61 FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT)
62 .put(FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4,
63 FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ROUTING_V4)
64 .put(FabricConstants.FABRIC_INGRESS_ACL_ACL,
65 FabricConstants.FABRIC_INGRESS_ACL_NOP_ACL)
66 .put(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
67 FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
68 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -070069
Daniele Morof51d0c12019-07-30 10:43:10 -070070
71 FabricTreatmentInterpreter(FabricCapabilities capabilities) {
72 this.capabilities = capabilities;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070073 }
74
Yi Tseng47eac892018-07-11 02:17:04 +080075 static PiAction mapFilteringTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -070076 throws PiInterpreterException {
Carmelo Casconeb5324e72018-11-25 02:26:32 -080077
78 if (!tableId.equals(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)) {
79 // Mapping for other tables of the filtering block must be handled
80 // in the pipeliner.
81 tableException(tableId);
Yi Tsengfa4a1c72017-11-03 10:22:38 -070082 }
83
Daniele Morof51d0c12019-07-30 10:43:10 -070084 // VLAN_POP action is equivalent to the permit action (VLANs pop is done anyway)
Wailok Shum4f51bde2021-06-11 22:48:41 +080085 if (isFilteringNoAction(treatment) || isFilteringPopAction(treatment)) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -080086 // Permit action if table is ingress_port_vlan;
87 return nop(tableId);
Yi Tsengfa4a1c72017-11-03 10:22:38 -070088 }
89
Carmelo Casconeb5324e72018-11-25 02:26:32 -080090 final ModVlanIdInstruction setVlanInst = (ModVlanIdInstruction) l2InstructionOrFail(
91 treatment, VLAN_ID, tableId);
Yi Tsengfa4a1c72017-11-03 10:22:38 -070092 return PiAction.builder()
Carmelo Casconeb5324e72018-11-25 02:26:32 -080093 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
94 .withParameter(new PiActionParam(
95 FabricConstants.VLAN_ID, setVlanInst.vlanId().toShort()))
Yi Tsengfa4a1c72017-11-03 10:22:38 -070096 .build();
97 }
98
Yi Tsengfa4a1c72017-11-03 10:22:38 -070099
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800100 static PiAction mapForwardingTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700101 throws PiInterpreterException {
Wailok Shum4f51bde2021-06-11 22:48:41 +0800102 if (isForwardingNoAction(treatment)) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800103 return nop(tableId);
Yi Tsengdf3eec52018-02-15 14:56:02 -0800104 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800105 treatmentException(
106 tableId, treatment,
107 "supports mapping only for empty/no-action treatments");
108 return null;
109 }
110
Daniele Morof51d0c12019-07-30 10:43:10 -0700111 PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800112 throws PiInterpreterException {
113 if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN) {
114 return mapNextVlanTreatment(treatment, tableId);
115 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_HASHED) {
116 return mapNextHashedOrSimpleTreatment(treatment, tableId, false);
117 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE) {
118 return mapNextHashedOrSimpleTreatment(treatment, tableId, true);
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800119 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT) {
120 return mapNextXconnect(treatment, tableId);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800121 }
122 throw new PiInterpreterException(format(
123 "Treatment mapping not supported for table '%s'", tableId));
124 }
125
Daniele Morof51d0c12019-07-30 10:43:10 -0700126 private PiAction mapNextVlanTreatment(TrafficTreatment treatment, PiTableId tableId)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800127 throws PiInterpreterException {
Daniele Morof51d0c12019-07-30 10:43:10 -0700128 final List<ModVlanIdInstruction> modVlanIdInst = l2InstructionsOrFail(treatment, VLAN_ID, tableId)
129 .stream().map(i -> (ModVlanIdInstruction) i).collect(Collectors.toList());
130 if (modVlanIdInst.size() == 1) {
131 return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
132 .withParameter(new PiActionParam(
133 FabricConstants.VLAN_ID,
134 modVlanIdInst.get(0).vlanId().toShort()))
135 .build();
136 }
137 if (modVlanIdInst.size() == 2 && capabilities.supportDoubleVlanTerm()) {
138 return PiAction.builder()
139 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_DOUBLE_VLAN)
140 .withParameter(new PiActionParam(
141 FabricConstants.INNER_VLAN_ID,
142 modVlanIdInst.get(0).vlanId().toShort()))
143 .withParameter(new PiActionParam(
144 FabricConstants.OUTER_VLAN_ID,
145 modVlanIdInst.get(1).vlanId().toShort()))
146 .build();
147 }
148 throw new PiInterpreterException("Too many VLAN instructions");
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800149 }
150
151 private static PiAction mapNextHashedOrSimpleTreatment(
152 TrafficTreatment treatment, PiTableId tableId, boolean simple)
153 throws PiInterpreterException {
154 // Provide mapping for output_hashed, routing_hashed, and
155 // mpls_routing_hashed. multicast_hashed can only be invoked with
156 // PiAction, hence no mapping. outPort required for all actions. Presence
157 // of other instructions will determine which action to map to.
158 final PortNumber outPort = ((OutputInstruction) instructionOrFail(
159 treatment, OUTPUT, tableId)).port();
160 final ModEtherInstruction ethDst = (ModEtherInstruction) l2Instruction(
161 treatment, ETH_DST);
162 final ModEtherInstruction ethSrc = (ModEtherInstruction) l2Instruction(
163 treatment, ETH_SRC);
164 final Instruction mplsPush = l2Instruction(
165 treatment, MPLS_PUSH);
166 final ModMplsLabelInstruction mplsLabel = (ModMplsLabelInstruction) l2Instruction(
167 treatment, MPLS_LABEL);
168
169 final PiAction.Builder actionBuilder = PiAction.builder()
170 .withParameter(new PiActionParam(FabricConstants.PORT_NUM, outPort.toLong()));
171
172 if (ethDst != null && ethSrc != null) {
173 actionBuilder.withParameter(new PiActionParam(
174 FabricConstants.SMAC, ethSrc.mac().toBytes()));
175 actionBuilder.withParameter(new PiActionParam(
176 FabricConstants.DMAC, ethDst.mac().toBytes()));
177 if (mplsLabel != null) {
178 // mpls_routing_hashed
179 return actionBuilder
180 .withParameter(new PiActionParam(FabricConstants.LABEL, mplsLabel.label().toInt()))
181 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_SIMPLE
182 : FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_HASHED)
183 .build();
184 } else {
185 // routing_hashed
186 return actionBuilder
187 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE
188 : FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
189 .build();
190 }
191 } else {
192 // output_hashed
193 return actionBuilder
194 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE
195 : FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_HASHED)
196 .build();
197 }
198 }
199
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800200 private static PiAction mapNextXconnect(
201 TrafficTreatment treatment, PiTableId tableId)
202 throws PiInterpreterException {
203 final PortNumber outPort = ((OutputInstruction) instructionOrFail(
204 treatment, OUTPUT, tableId)).port();
205 return PiAction.builder()
206 .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_XCONNECT)
207 .withParameter(new PiActionParam(
208 FabricConstants.PORT_NUM, outPort.toLong()))
209 .build();
210 }
211
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800212 static PiAction mapAclTreatment(TrafficTreatment treatment, PiTableId tableId)
213 throws PiInterpreterException {
Wailok Shum4f51bde2021-06-11 22:48:41 +0800214 if (isDrop(treatment)) {
215 return drop(tableId);
216 }
217 if (isForwardingNoAction(treatment)) {
218 return nop(tableId);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800219 }
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700220 treatmentException(
221 tableId, treatment,
222 "unsupported treatment");
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800223
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700224 // This function will never return null
225 return null;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700226 }
227
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700228
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800229 static PiAction mapEgressNextTreatment(
230 TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700231 throws PiInterpreterException {
pierventre48e78822020-12-15 17:34:54 +0100232 L2ModificationInstruction pushVlan = l2Instruction(treatment, VLAN_PUSH);
233 if (pushVlan != null) {
234 return PiAction.builder()
235 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_PUSH_VLAN)
236 .build();
237 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800238 l2InstructionOrFail(treatment, VLAN_POP, tableId);
239 return PiAction.builder()
240 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
241 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700242
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700243 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800244
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800245 private static PiAction nop(PiTableId tableId) throws PiInterpreterException {
246 if (!NOP_ACTIONS.containsKey(tableId)) {
247 throw new PiInterpreterException(format("table '%s' doe not specify a nop action", tableId));
248 }
249 return PiAction.builder().withId(NOP_ACTIONS.get(tableId)).build();
250 }
251
Wailok Shum4f51bde2021-06-11 22:48:41 +0800252 private static PiAction drop(PiTableId tableId) throws PiInterpreterException {
253 if (!tableId.equals(FabricConstants.FABRIC_INGRESS_ACL_ACL)) {
254 throw new PiInterpreterException(format("table '%s' doe not specify a nop action", tableId));
255 }
256 return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_ACL_DROP).build();
257 }
258
259 // NOTE: we use clearDeferred to signal when there are no more ports associated to a given vlan
260 private static boolean isFilteringNoAction(TrafficTreatment treatment) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800261 return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
Wailok Shum4f51bde2021-06-11 22:48:41 +0800262 (treatment.allInstructions().isEmpty()) ||
pierventre167d4482021-01-08 17:35:47 +0100263 (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800264 }
265
Wailok Shum4f51bde2021-06-11 22:48:41 +0800266 // NOTE: clearDeferred is used by the routing application to implement ACL drop and route black-holing
267 private static boolean isForwardingNoAction(TrafficTreatment treatment) {
268 return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
269 (treatment.allInstructions().isEmpty() && !treatment.clearedDeferred()) ||
270 (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
271 }
272
273 private static boolean isDrop(TrafficTreatment treatment) {
274 return treatment.allInstructions().isEmpty() && treatment.clearedDeferred();
275 }
276
Daniele Morof51d0c12019-07-30 10:43:10 -0700277 private static boolean isFilteringPopAction(TrafficTreatment treatment) {
278 return l2Instruction(treatment, VLAN_POP) != null;
279 }
280
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800281 private static Instruction l2InstructionOrFail(
282 TrafficTreatment treatment,
283 L2ModificationInstruction.L2SubType subType, PiTableId tableId)
284 throws PiInterpreterException {
285 final Instruction inst = l2Instruction(treatment, subType);
286 if (inst == null) {
287 treatmentException(tableId, treatment, format("missing %s instruction", subType));
288 }
289 return inst;
290 }
291
Daniele Morof51d0c12019-07-30 10:43:10 -0700292 private static List<L2ModificationInstruction> l2InstructionsOrFail(
293 TrafficTreatment treatment,
294 L2ModificationInstruction.L2SubType subType, PiTableId tableId)
295 throws PiInterpreterException {
296 final List<L2ModificationInstruction> inst = l2Instructions(treatment, subType);
297 if (inst == null || inst.isEmpty()) {
298 treatmentException(tableId, treatment, format("missing %s instruction", subType));
299 }
300 return inst;
301 }
302
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800303 private static Instruction instructionOrFail(
304 TrafficTreatment treatment, Instruction.Type type, PiTableId tableId)
305 throws PiInterpreterException {
306 final Instruction inst = instruction(treatment, type);
307 if (inst == null) {
308 treatmentException(tableId, treatment, format("missing %s instruction", type));
309 }
310 return inst;
311 }
312
313 private static void tableException(PiTableId tableId)
314 throws PiInterpreterException {
315 throw new PiInterpreterException(format("Table '%s' not supported", tableId));
316 }
317
318 private static void treatmentException(
319 PiTableId tableId, TrafficTreatment treatment, String explanation)
320 throws PiInterpreterException {
321 throw new PiInterpreterException(format(
322 "Invalid treatment for table '%s', %s: %s", tableId, explanation, treatment));
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800323 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700324}