blob: de3ecbfc4d19f7bfcc9f0f800892e82cd768055b [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;
pierventre48e78822020-12-15 17:34:54 +010041import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.*;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070042import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.instruction;
43import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instruction;
44import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instructions;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070045
Carmelo Casconeb5324e72018-11-25 02:26:32 -080046/**
47 * Treatment translation logic.
48 */
Yi Tsengfa4a1c72017-11-03 10:22:38 -070049final class FabricTreatmentInterpreter {
Yi Tseng20f9e7b2018-05-24 23:27:39 +080050
Daniele Morof51d0c12019-07-30 10:43:10 -070051 private final FabricCapabilities capabilities;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080052 private static final ImmutableMap<PiTableId, PiActionId> NOP_ACTIONS =
53 ImmutableMap.<PiTableId, PiActionId>builder()
54 .put(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
55 FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT)
56 .put(FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4,
57 FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ROUTING_V4)
58 .put(FabricConstants.FABRIC_INGRESS_ACL_ACL,
59 FabricConstants.FABRIC_INGRESS_ACL_NOP_ACL)
60 .put(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
61 FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
62 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -070063
Daniele Morof51d0c12019-07-30 10:43:10 -070064
65 FabricTreatmentInterpreter(FabricCapabilities capabilities) {
66 this.capabilities = capabilities;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070067 }
68
Yi Tseng47eac892018-07-11 02:17:04 +080069 static PiAction mapFilteringTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -070070 throws PiInterpreterException {
Carmelo Casconeb5324e72018-11-25 02:26:32 -080071
72 if (!tableId.equals(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)) {
73 // Mapping for other tables of the filtering block must be handled
74 // in the pipeliner.
75 tableException(tableId);
Yi Tsengfa4a1c72017-11-03 10:22:38 -070076 }
77
Daniele Morof51d0c12019-07-30 10:43:10 -070078 // VLAN_POP action is equivalent to the permit action (VLANs pop is done anyway)
79 if (isNoAction(treatment) || isFilteringPopAction(treatment)) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -080080 // Permit action if table is ingress_port_vlan;
81 return nop(tableId);
Yi Tsengfa4a1c72017-11-03 10:22:38 -070082 }
83
Carmelo Casconeb5324e72018-11-25 02:26:32 -080084 final ModVlanIdInstruction setVlanInst = (ModVlanIdInstruction) l2InstructionOrFail(
85 treatment, VLAN_ID, tableId);
Yi Tsengfa4a1c72017-11-03 10:22:38 -070086 return PiAction.builder()
Carmelo Casconeb5324e72018-11-25 02:26:32 -080087 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
88 .withParameter(new PiActionParam(
89 FabricConstants.VLAN_ID, setVlanInst.vlanId().toShort()))
Yi Tsengfa4a1c72017-11-03 10:22:38 -070090 .build();
91 }
92
Yi Tsengfa4a1c72017-11-03 10:22:38 -070093
Carmelo Casconeb5324e72018-11-25 02:26:32 -080094 static PiAction mapForwardingTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -070095 throws PiInterpreterException {
Carmelo Casconeb5324e72018-11-25 02:26:32 -080096 if (isNoAction(treatment)) {
97 return nop(tableId);
Yi Tsengdf3eec52018-02-15 14:56:02 -080098 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -080099 treatmentException(
100 tableId, treatment,
101 "supports mapping only for empty/no-action treatments");
102 return null;
103 }
104
Daniele Morof51d0c12019-07-30 10:43:10 -0700105 PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800106 throws PiInterpreterException {
107 if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN) {
108 return mapNextVlanTreatment(treatment, tableId);
109 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_HASHED) {
110 return mapNextHashedOrSimpleTreatment(treatment, tableId, false);
111 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE) {
112 return mapNextHashedOrSimpleTreatment(treatment, tableId, true);
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800113 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT) {
114 return mapNextXconnect(treatment, tableId);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800115 }
116 throw new PiInterpreterException(format(
117 "Treatment mapping not supported for table '%s'", tableId));
118 }
119
Daniele Morof51d0c12019-07-30 10:43:10 -0700120 private PiAction mapNextVlanTreatment(TrafficTreatment treatment, PiTableId tableId)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800121 throws PiInterpreterException {
Daniele Morof51d0c12019-07-30 10:43:10 -0700122 final List<ModVlanIdInstruction> modVlanIdInst = l2InstructionsOrFail(treatment, VLAN_ID, tableId)
123 .stream().map(i -> (ModVlanIdInstruction) i).collect(Collectors.toList());
124 if (modVlanIdInst.size() == 1) {
125 return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
126 .withParameter(new PiActionParam(
127 FabricConstants.VLAN_ID,
128 modVlanIdInst.get(0).vlanId().toShort()))
129 .build();
130 }
131 if (modVlanIdInst.size() == 2 && capabilities.supportDoubleVlanTerm()) {
132 return PiAction.builder()
133 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_DOUBLE_VLAN)
134 .withParameter(new PiActionParam(
135 FabricConstants.INNER_VLAN_ID,
136 modVlanIdInst.get(0).vlanId().toShort()))
137 .withParameter(new PiActionParam(
138 FabricConstants.OUTER_VLAN_ID,
139 modVlanIdInst.get(1).vlanId().toShort()))
140 .build();
141 }
142 throw new PiInterpreterException("Too many VLAN instructions");
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800143 }
144
145 private static PiAction mapNextHashedOrSimpleTreatment(
146 TrafficTreatment treatment, PiTableId tableId, boolean simple)
147 throws PiInterpreterException {
148 // Provide mapping for output_hashed, routing_hashed, and
149 // mpls_routing_hashed. multicast_hashed can only be invoked with
150 // PiAction, hence no mapping. outPort required for all actions. Presence
151 // of other instructions will determine which action to map to.
152 final PortNumber outPort = ((OutputInstruction) instructionOrFail(
153 treatment, OUTPUT, tableId)).port();
154 final ModEtherInstruction ethDst = (ModEtherInstruction) l2Instruction(
155 treatment, ETH_DST);
156 final ModEtherInstruction ethSrc = (ModEtherInstruction) l2Instruction(
157 treatment, ETH_SRC);
158 final Instruction mplsPush = l2Instruction(
159 treatment, MPLS_PUSH);
160 final ModMplsLabelInstruction mplsLabel = (ModMplsLabelInstruction) l2Instruction(
161 treatment, MPLS_LABEL);
162
163 final PiAction.Builder actionBuilder = PiAction.builder()
164 .withParameter(new PiActionParam(FabricConstants.PORT_NUM, outPort.toLong()));
165
166 if (ethDst != null && ethSrc != null) {
167 actionBuilder.withParameter(new PiActionParam(
168 FabricConstants.SMAC, ethSrc.mac().toBytes()));
169 actionBuilder.withParameter(new PiActionParam(
170 FabricConstants.DMAC, ethDst.mac().toBytes()));
171 if (mplsLabel != null) {
172 // mpls_routing_hashed
173 return actionBuilder
174 .withParameter(new PiActionParam(FabricConstants.LABEL, mplsLabel.label().toInt()))
175 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_SIMPLE
176 : FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_HASHED)
177 .build();
178 } else {
179 // routing_hashed
180 return actionBuilder
181 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE
182 : FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
183 .build();
184 }
185 } else {
186 // output_hashed
187 return actionBuilder
188 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE
189 : FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_HASHED)
190 .build();
191 }
192 }
193
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800194 private static PiAction mapNextXconnect(
195 TrafficTreatment treatment, PiTableId tableId)
196 throws PiInterpreterException {
197 final PortNumber outPort = ((OutputInstruction) instructionOrFail(
198 treatment, OUTPUT, tableId)).port();
199 return PiAction.builder()
200 .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_XCONNECT)
201 .withParameter(new PiActionParam(
202 FabricConstants.PORT_NUM, outPort.toLong()))
203 .build();
204 }
205
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800206 static PiAction mapAclTreatment(TrafficTreatment treatment, PiTableId tableId)
207 throws PiInterpreterException {
208 if (isNoAction(treatment)) {
209 return nop(tableId);
210 }
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700211 treatmentException(
212 tableId, treatment,
213 "unsupported treatment");
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800214
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700215 // This function will never return null
216 return null;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700217 }
218
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700219
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800220 static PiAction mapEgressNextTreatment(
221 TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700222 throws PiInterpreterException {
pierventre48e78822020-12-15 17:34:54 +0100223 L2ModificationInstruction pushVlan = l2Instruction(treatment, VLAN_PUSH);
224 if (pushVlan != null) {
225 return PiAction.builder()
226 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_PUSH_VLAN)
227 .build();
228 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800229 l2InstructionOrFail(treatment, VLAN_POP, tableId);
230 return PiAction.builder()
231 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
232 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700233
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700234 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800235
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800236 private static PiAction nop(PiTableId tableId) throws PiInterpreterException {
237 if (!NOP_ACTIONS.containsKey(tableId)) {
238 throw new PiInterpreterException(format("table '%s' doe not specify a nop action", tableId));
239 }
240 return PiAction.builder().withId(NOP_ACTIONS.get(tableId)).build();
241 }
242
243 private static boolean isNoAction(TrafficTreatment treatment) {
pierventre167d4482021-01-08 17:35:47 +0100244 // Empty treatment OR
245 // No instructions OR
246 // Empty treatment AND writeMetadata
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800247 return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
pierventre167d4482021-01-08 17:35:47 +0100248 treatment.allInstructions().isEmpty() ||
249 (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800250 }
251
Daniele Morof51d0c12019-07-30 10:43:10 -0700252 private static boolean isFilteringPopAction(TrafficTreatment treatment) {
253 return l2Instruction(treatment, VLAN_POP) != null;
254 }
255
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800256 private static Instruction l2InstructionOrFail(
257 TrafficTreatment treatment,
258 L2ModificationInstruction.L2SubType subType, PiTableId tableId)
259 throws PiInterpreterException {
260 final Instruction inst = l2Instruction(treatment, subType);
261 if (inst == null) {
262 treatmentException(tableId, treatment, format("missing %s instruction", subType));
263 }
264 return inst;
265 }
266
Daniele Morof51d0c12019-07-30 10:43:10 -0700267 private static List<L2ModificationInstruction> l2InstructionsOrFail(
268 TrafficTreatment treatment,
269 L2ModificationInstruction.L2SubType subType, PiTableId tableId)
270 throws PiInterpreterException {
271 final List<L2ModificationInstruction> inst = l2Instructions(treatment, subType);
272 if (inst == null || inst.isEmpty()) {
273 treatmentException(tableId, treatment, format("missing %s instruction", subType));
274 }
275 return inst;
276 }
277
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800278 private static Instruction instructionOrFail(
279 TrafficTreatment treatment, Instruction.Type type, PiTableId tableId)
280 throws PiInterpreterException {
281 final Instruction inst = instruction(treatment, type);
282 if (inst == null) {
283 treatmentException(tableId, treatment, format("missing %s instruction", type));
284 }
285 return inst;
286 }
287
288 private static void tableException(PiTableId tableId)
289 throws PiInterpreterException {
290 throw new PiInterpreterException(format("Table '%s' not supported", tableId));
291 }
292
293 private static void treatmentException(
294 PiTableId tableId, TrafficTreatment treatment, String explanation)
295 throws PiInterpreterException {
296 throw new PiInterpreterException(format(
297 "Invalid treatment for table '%s', %s: %s", tableId, explanation, treatment));
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800298 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700299}