blob: bdf3eafa7690f2f436226c888f71262cb426ed12 [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;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020044import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
45import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
46import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070047import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.instruction;
48import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instruction;
Wailok Shumfb7e7872021-06-18 17:30:08 +080049import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2InstructionOrFail;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070050import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instructions;
Wailok Shumfb7e7872021-06-18 17:30:08 +080051import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.treatmentException;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070052
Carmelo Casconeb5324e72018-11-25 02:26:32 -080053/**
54 * Treatment translation logic.
55 */
Yi Tsengfa4a1c72017-11-03 10:22:38 -070056final class FabricTreatmentInterpreter {
Yi Tseng20f9e7b2018-05-24 23:27:39 +080057
Daniele Morof51d0c12019-07-30 10:43:10 -070058 private final FabricCapabilities capabilities;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080059 private static final ImmutableMap<PiTableId, PiActionId> NOP_ACTIONS =
60 ImmutableMap.<PiTableId, PiActionId>builder()
Carmelo Casconeb5324e72018-11-25 02:26:32 -080061 .put(FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4,
62 FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ROUTING_V4)
63 .put(FabricConstants.FABRIC_INGRESS_ACL_ACL,
64 FabricConstants.FABRIC_INGRESS_ACL_NOP_ACL)
65 .put(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
66 FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
67 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -070068
Daniele Morof51d0c12019-07-30 10:43:10 -070069
70 FabricTreatmentInterpreter(FabricCapabilities capabilities) {
71 this.capabilities = capabilities;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070072 }
73
Yi Tsengfa4a1c72017-11-03 10:22:38 -070074
Carmelo Casconeb5324e72018-11-25 02:26:32 -080075 static PiAction mapForwardingTreatment(TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -070076 throws PiInterpreterException {
Wailok Shumfb7e7872021-06-18 17:30:08 +080077 if (isNoAction(treatment)) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -080078 return nop(tableId);
Yi Tsengdf3eec52018-02-15 14:56:02 -080079 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -080080 treatmentException(
81 tableId, treatment,
82 "supports mapping only for empty/no-action treatments");
83 return null;
84 }
85
Wailok Shumfb7e7872021-06-18 17:30:08 +080086 PiAction mapPreNextTreatment(TrafficTreatment treatment, PiTableId tableId)
Carmelo Casconeb5324e72018-11-25 02:26:32 -080087 throws PiInterpreterException {
Wailok Shumfb7e7872021-06-18 17:30:08 +080088 if (tableId == FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_MPLS) {
89 return mapNextMplsTreatment(treatment, tableId);
90 } else if (tableId == FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -080091 return mapNextVlanTreatment(treatment, tableId);
Carmelo Casconeb5324e72018-11-25 02:26:32 -080092 }
93 throw new PiInterpreterException(format(
94 "Treatment mapping not supported for table '%s'", tableId));
95 }
96
Wailok Shumfb7e7872021-06-18 17:30:08 +080097 PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
98 throws PiInterpreterException {
99 if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_HASHED) {
100 return mapNextHashedOrSimpleTreatment(treatment, tableId, false);
101 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE) {
102 return mapNextHashedOrSimpleTreatment(treatment, tableId, true);
103 } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT) {
104 return mapNextXconnect(treatment, tableId);
105 }
106 throw new PiInterpreterException(format(
107 "Treatment mapping not supported for table '%s'", tableId));
108 }
109
110 private static PiAction mapNextMplsTreatment(TrafficTreatment treatment, PiTableId tableId)
111 throws PiInterpreterException {
112 final ModMplsLabelInstruction mplsLabel = (ModMplsLabelInstruction) l2Instruction(
113 treatment, MPLS_LABEL);
114 if (mplsLabel != null) {
115 return PiAction.builder()
116 .withParameter(new PiActionParam(FabricConstants.LABEL, mplsLabel.label().toInt()))
117 .withId(FabricConstants.FABRIC_INGRESS_PRE_NEXT_SET_MPLS_LABEL)
118 .build();
119 }
120 throw new PiInterpreterException("There is no MPLS instruction");
121 }
122
Daniele Morof51d0c12019-07-30 10:43:10 -0700123 private PiAction mapNextVlanTreatment(TrafficTreatment treatment, PiTableId tableId)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800124 throws PiInterpreterException {
Daniele Morof51d0c12019-07-30 10:43:10 -0700125 final List<ModVlanIdInstruction> modVlanIdInst = l2InstructionsOrFail(treatment, VLAN_ID, tableId)
126 .stream().map(i -> (ModVlanIdInstruction) i).collect(Collectors.toList());
127 if (modVlanIdInst.size() == 1) {
Wailok Shumfb7e7872021-06-18 17:30:08 +0800128 return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_PRE_NEXT_SET_VLAN)
Daniele Morof51d0c12019-07-30 10:43:10 -0700129 .withParameter(new PiActionParam(
130 FabricConstants.VLAN_ID,
131 modVlanIdInst.get(0).vlanId().toShort()))
132 .build();
133 }
Wailok Shumfb7e7872021-06-18 17:30:08 +0800134 // next_vlan has been moved to pre_next
Daniele Morof51d0c12019-07-30 10:43:10 -0700135 if (modVlanIdInst.size() == 2 && capabilities.supportDoubleVlanTerm()) {
136 return PiAction.builder()
Wailok Shumfb7e7872021-06-18 17:30:08 +0800137 .withId(FabricConstants.FABRIC_INGRESS_PRE_NEXT_SET_DOUBLE_VLAN)
Daniele Morof51d0c12019-07-30 10:43:10 -0700138 .withParameter(new PiActionParam(
139 FabricConstants.INNER_VLAN_ID,
140 modVlanIdInst.get(0).vlanId().toShort()))
141 .withParameter(new PiActionParam(
142 FabricConstants.OUTER_VLAN_ID,
143 modVlanIdInst.get(1).vlanId().toShort()))
144 .build();
145 }
146 throw new PiInterpreterException("Too many VLAN instructions");
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800147 }
148
149 private static PiAction mapNextHashedOrSimpleTreatment(
150 TrafficTreatment treatment, PiTableId tableId, boolean simple)
151 throws PiInterpreterException {
Wailok Shumfb7e7872021-06-18 17:30:08 +0800152 // Provide mapping for output_hashed and routing_hashed; multicast_hashed
153 // can only be invoked with PiAction, hence no mapping. outPort required for
154 // all actions. Presence of other instructions will determine which action to map to.
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800155 final PortNumber outPort = ((OutputInstruction) instructionOrFail(
156 treatment, OUTPUT, tableId)).port();
157 final ModEtherInstruction ethDst = (ModEtherInstruction) l2Instruction(
158 treatment, ETH_DST);
159 final ModEtherInstruction ethSrc = (ModEtherInstruction) l2Instruction(
160 treatment, ETH_SRC);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800161
162 final PiAction.Builder actionBuilder = PiAction.builder()
163 .withParameter(new PiActionParam(FabricConstants.PORT_NUM, outPort.toLong()));
164
165 if (ethDst != null && ethSrc != null) {
166 actionBuilder.withParameter(new PiActionParam(
167 FabricConstants.SMAC, ethSrc.mac().toBytes()));
168 actionBuilder.withParameter(new PiActionParam(
169 FabricConstants.DMAC, ethDst.mac().toBytes()));
Wailok Shumfb7e7872021-06-18 17:30:08 +0800170 // routing_hashed
171 return actionBuilder
172 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE
173 : FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
174 .build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800175 } else {
176 // output_hashed
177 return actionBuilder
178 .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE
179 : FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_HASHED)
180 .build();
181 }
182 }
183
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800184 private static PiAction mapNextXconnect(
185 TrafficTreatment treatment, PiTableId tableId)
186 throws PiInterpreterException {
187 final PortNumber outPort = ((OutputInstruction) instructionOrFail(
188 treatment, OUTPUT, tableId)).port();
189 return PiAction.builder()
190 .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_XCONNECT)
191 .withParameter(new PiActionParam(
192 FabricConstants.PORT_NUM, outPort.toLong()))
193 .build();
194 }
195
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800196 static PiAction mapAclTreatment(TrafficTreatment treatment, PiTableId tableId)
197 throws PiInterpreterException {
Wailok Shum4f51bde2021-06-11 22:48:41 +0800198 if (isDrop(treatment)) {
199 return drop(tableId);
200 }
Wailok Shumfb7e7872021-06-18 17:30:08 +0800201 if (isNoAction(treatment)) {
Wailok Shum4f51bde2021-06-11 22:48:41 +0800202 return nop(tableId);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800203 }
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700204 treatmentException(
205 tableId, treatment,
206 "unsupported treatment");
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800207
Daniele Moro01ca2ab2019-06-25 11:48:48 -0700208 // This function will never return null
209 return null;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700210 }
211
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700212
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800213 static PiAction mapEgressNextTreatment(
214 TrafficTreatment treatment, PiTableId tableId)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700215 throws PiInterpreterException {
pierventre48e78822020-12-15 17:34:54 +0100216 L2ModificationInstruction pushVlan = l2Instruction(treatment, VLAN_PUSH);
217 if (pushVlan != null) {
218 return PiAction.builder()
219 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_PUSH_VLAN)
220 .build();
221 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800222 l2InstructionOrFail(treatment, VLAN_POP, tableId);
223 return PiAction.builder()
224 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
225 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700226
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700227 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800228
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800229 private static PiAction nop(PiTableId tableId) throws PiInterpreterException {
230 if (!NOP_ACTIONS.containsKey(tableId)) {
231 throw new PiInterpreterException(format("table '%s' doe not specify a nop action", tableId));
232 }
233 return PiAction.builder().withId(NOP_ACTIONS.get(tableId)).build();
234 }
235
Wailok Shum4f51bde2021-06-11 22:48:41 +0800236 private static PiAction drop(PiTableId tableId) throws PiInterpreterException {
237 if (!tableId.equals(FabricConstants.FABRIC_INGRESS_ACL_ACL)) {
238 throw new PiInterpreterException(format("table '%s' doe not specify a nop action", tableId));
239 }
240 return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_ACL_DROP).build();
241 }
242
Wailok Shum4f51bde2021-06-11 22:48:41 +0800243 // NOTE: clearDeferred is used by the routing application to implement ACL drop and route black-holing
Wailok Shumfb7e7872021-06-18 17:30:08 +0800244 private static boolean isNoAction(TrafficTreatment treatment) {
Wailok Shum4f51bde2021-06-11 22:48:41 +0800245 return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
246 (treatment.allInstructions().isEmpty() && !treatment.clearedDeferred()) ||
247 (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
248 }
249
250 private static boolean isDrop(TrafficTreatment treatment) {
251 return treatment.allInstructions().isEmpty() && treatment.clearedDeferred();
252 }
253
Daniele Morof51d0c12019-07-30 10:43:10 -0700254 private static List<L2ModificationInstruction> l2InstructionsOrFail(
255 TrafficTreatment treatment,
256 L2ModificationInstruction.L2SubType subType, PiTableId tableId)
257 throws PiInterpreterException {
258 final List<L2ModificationInstruction> inst = l2Instructions(treatment, subType);
259 if (inst == null || inst.isEmpty()) {
260 treatmentException(tableId, treatment, format("missing %s instruction", subType));
261 }
262 return inst;
263 }
264
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800265 private static Instruction instructionOrFail(
266 TrafficTreatment treatment, Instruction.Type type, PiTableId tableId)
267 throws PiInterpreterException {
268 final Instruction inst = instruction(treatment, type);
269 if (inst == null) {
270 treatmentException(tableId, treatment, format("missing %s instruction", type));
271 }
272 return inst;
273 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700274}