blob: 9b33d2df3df9b212186a674e909560212071856f [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)
85 if (isNoAction(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 {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800102 if (isNoAction(treatment)) {
103 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 {
214 if (isNoAction(treatment)) {
215 return nop(tableId);
216 }
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700217 treatmentException(
218 tableId, treatment,
219 "unsupported treatment");
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800220
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700221 // This function will never return null
222 return null;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700223 }
224
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700225
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800226 static PiAction mapEgressNextTreatment(
227 TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700228 throws PiInterpreterException {
pierventre48e78822020-12-15 17:34:54 +0100229 L2ModificationInstruction pushVlan = l2Instruction(treatment, VLAN_PUSH);
230 if (pushVlan != null) {
231 return PiAction.builder()
232 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_PUSH_VLAN)
233 .build();
234 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800235 l2InstructionOrFail(treatment, VLAN_POP, tableId);
236 return PiAction.builder()
237 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
238 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700239
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700240 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800241
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800242 private static PiAction nop(PiTableId tableId) throws PiInterpreterException {
243 if (!NOP_ACTIONS.containsKey(tableId)) {
244 throw new PiInterpreterException(format("table '%s' doe not specify a nop action", tableId));
245 }
246 return PiAction.builder().withId(NOP_ACTIONS.get(tableId)).build();
247 }
248
249 private static boolean isNoAction(TrafficTreatment treatment) {
pierventre167d4482021-01-08 17:35:47 +0100250 // Empty treatment OR
251 // No instructions OR
252 // Empty treatment AND writeMetadata
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800253 return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
pierventre167d4482021-01-08 17:35:47 +0100254 treatment.allInstructions().isEmpty() ||
255 (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800256 }
257
Daniele Morof51d0c12019-07-30 10:43:10 -0700258 private static boolean isFilteringPopAction(TrafficTreatment treatment) {
259 return l2Instruction(treatment, VLAN_POP) != null;
260 }
261
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800262 private static Instruction l2InstructionOrFail(
263 TrafficTreatment treatment,
264 L2ModificationInstruction.L2SubType subType, PiTableId tableId)
265 throws PiInterpreterException {
266 final Instruction inst = l2Instruction(treatment, subType);
267 if (inst == null) {
268 treatmentException(tableId, treatment, format("missing %s instruction", subType));
269 }
270 return inst;
271 }
272
Daniele Morof51d0c12019-07-30 10:43:10 -0700273 private static List<L2ModificationInstruction> l2InstructionsOrFail(
274 TrafficTreatment treatment,
275 L2ModificationInstruction.L2SubType subType, PiTableId tableId)
276 throws PiInterpreterException {
277 final List<L2ModificationInstruction> inst = l2Instructions(treatment, subType);
278 if (inst == null || inst.isEmpty()) {
279 treatmentException(tableId, treatment, format("missing %s instruction", subType));
280 }
281 return inst;
282 }
283
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800284 private static Instruction instructionOrFail(
285 TrafficTreatment treatment, Instruction.Type type, PiTableId tableId)
286 throws PiInterpreterException {
287 final Instruction inst = instruction(treatment, type);
288 if (inst == null) {
289 treatmentException(tableId, treatment, format("missing %s instruction", type));
290 }
291 return inst;
292 }
293
294 private static void tableException(PiTableId tableId)
295 throws PiInterpreterException {
296 throw new PiInterpreterException(format("Table '%s' not supported", tableId));
297 }
298
299 private static void treatmentException(
300 PiTableId tableId, TrafficTreatment treatment, String explanation)
301 throws PiInterpreterException {
302 throw new PiInterpreterException(format(
303 "Invalid treatment for table '%s', %s: %s", tableId, explanation, treatment));
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800304 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700305}