blob: 2c41985b93c3936dd382a8967aea15d15fef48d8 [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;
36import org.onosproject.net.pi.runtime.PiAction;
37import org.onosproject.net.pi.runtime.PiActionParam;
Yi Tseng1b154bd2017-11-20 17:48:19 -080038import org.slf4j.Logger;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070039
40import java.util.List;
41
42import static java.lang.String.format;
43import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
44import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
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;
Yi Tseng1b154bd2017-11-20 17:48:19 -080047import static org.slf4j.LoggerFactory.getLogger;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070048
49
50final class FabricTreatmentInterpreter {
Yi Tseng1b154bd2017-11-20 17:48:19 -080051 private static final Logger log = getLogger(FabricTreatmentInterpreter.class);
Yi Tsengfa4a1c72017-11-03 10:22:38 -070052 private static final String INVALID_TREATMENT = "Invalid treatment for %s block: %s";
Yi Tseng20f9e7b2018-05-24 23:27:39 +080053 private static final PiAction NOP = PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +080054 .withId(FabricConstants.NOP)
Yi Tseng20f9e7b2018-05-24 23:27:39 +080055 .build();
56
57 private static final PiAction POP_VLAN = PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +080058 .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
Yi Tseng20f9e7b2018-05-24 23:27:39 +080059 .build();
Yi Tsengfa4a1c72017-11-03 10:22:38 -070060
61 // Hide default constructor
62 protected FabricTreatmentInterpreter() {
63 }
64
65 /*
66 * In Filtering block, we need to implement these actions:
67 *
68 * push_internal_vlan
69 * set_vlan
70 * nop
71 *
72 * Unsupported, using PiAction directly:
73 * set_forwarding_type
74 */
75
76 static PiAction mapFilteringTreatment(TrafficTreatment treatment)
77 throws PiInterpreterException {
78 List<Instruction> instructions = treatment.allInstructions();
79 Instruction noActInst = Instructions.createNoAction();
80 if (instructions.isEmpty() || instructions.contains(noActInst)) {
81 // nop
Yi Tseng20f9e7b2018-05-24 23:27:39 +080082 return NOP;
Yi Tsengfa4a1c72017-11-03 10:22:38 -070083 }
84
85 L2ModificationInstruction.ModVlanHeaderInstruction pushVlanInst = null;
86 ModVlanIdInstruction setVlanInst = null;
87
88 for (Instruction inst : instructions) {
89 if (inst.type() == L2MODIFICATION) {
90 L2ModificationInstruction l2Inst = (L2ModificationInstruction) inst;
91
92 if (l2Inst.subtype() == VLAN_PUSH) {
93 pushVlanInst = (L2ModificationInstruction.ModVlanHeaderInstruction) l2Inst;
94
95 } else if (l2Inst.subtype() == VLAN_ID) {
96 setVlanInst = (ModVlanIdInstruction) l2Inst;
97 }
98 }
99 }
100
101 if (setVlanInst == null) {
102 throw new PiInterpreterException(format(INVALID_TREATMENT, "filtering", treatment));
103 }
104
105 VlanId vlanId = setVlanInst.vlanId();
Yi Tseng43ee7e82018-04-12 16:37:34 +0800106 PiActionParam param = new PiActionParam(FabricConstants.NEW_VLAN_ID,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700107 ImmutableByteSequence.copyFrom(vlanId.toShort()));
108 PiActionId actionId;
109 if (pushVlanInst != null) {
110 // push_internal_vlan
Yi Tseng43ee7e82018-04-12 16:37:34 +0800111 actionId = FabricConstants.FABRIC_INGRESS_FILTERING_PUSH_INTERNAL_VLAN;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700112 } else {
Yi Tseng43ee7e82018-04-12 16:37:34 +0800113 actionId = FabricConstants.FABRIC_INGRESS_FILTERING_SET_VLAN;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700114 }
115
116 // set_vlan
117 return PiAction.builder()
118 .withId(actionId)
119 .withParameter(param)
120 .build();
121 }
122
123 /*
124 * In forwarding block, we need to implement these actions:
125 * duplicate_to_controller
126 *
127 * Unsupported, using PiAction directly:
128 * set_next_id
129 * push_mpls_and_next_v4
130 * push_mpls_and_next_v6
131 */
132
133 public static PiAction mapForwardingTreatment(TrafficTreatment treatment)
134 throws PiInterpreterException {
Yi Tsengdf3eec52018-02-15 14:56:02 -0800135 // Empty treatment, generate table entry with no action
136 if (treatment.equals(DefaultTrafficTreatment.emptyTreatment())) {
137 return null;
138 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700139 List<Instruction> insts = treatment.allInstructions();
140 OutputInstruction outInst = insts.stream()
141 .filter(inst -> inst.type() == OUTPUT)
142 .map(inst -> (OutputInstruction) inst)
143 .findFirst()
144 .orElse(null);
145
146 if (outInst == null) {
147 throw new PiInterpreterException(format(INVALID_TREATMENT, "forwarding", treatment));
148 }
149
150 PortNumber portNumber = outInst.port();
151 if (!portNumber.equals(PortNumber.CONTROLLER)) {
152 throw new PiInterpreterException(format("Unsupported port number %s," +
153 "supports punt action only",
154 portNumber));
155 }
156
157 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800158 .withId(FabricConstants.FABRIC_INGRESS_FORWARDING_DUPLICATE_TO_CONTROLLER)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700159 .build();
160 }
161
162 /*
163 * In Next block, we need to implement these actions:
164 * output
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700165 * set_vlan_output
Yi Tseng1b154bd2017-11-20 17:48:19 -0800166 * l3_routing
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800167 * l3_routing_vlan
Yi Tseng1b154bd2017-11-20 17:48:19 -0800168 * mpls_routing_v4
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700169 *
Yi Tseng1b154bd2017-11-20 17:48:19 -0800170 * Unsupported, need to find a way to implement it
171 * mpls_routing_v6
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700172 */
173
174 public static PiAction mapNextTreatment(TrafficTreatment treatment)
175 throws PiInterpreterException {
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800176 // TODO: refactor this method
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700177 List<Instruction> insts = treatment.allInstructions();
178 OutputInstruction outInst = null;
179 ModEtherInstruction modEthDstInst = null;
180 ModEtherInstruction modEthSrcInst = null;
181 ModVlanIdInstruction modVlanIdInst = null;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800182 ModMplsLabelInstruction modMplsInst = null;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700183
Yi Tseng1b154bd2017-11-20 17:48:19 -0800184 // TODO: add NextFunctionType (like ForwardingFunctionType)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700185 for (Instruction inst : insts) {
186 switch (inst.type()) {
187 case L2MODIFICATION:
188 L2ModificationInstruction l2Inst = (L2ModificationInstruction) inst;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800189 switch (l2Inst.subtype()) {
190 case ETH_SRC:
191 modEthSrcInst = (ModEtherInstruction) l2Inst;
192 break;
193 case ETH_DST:
194 modEthDstInst = (ModEtherInstruction) l2Inst;
195 break;
196 case VLAN_ID:
197 modVlanIdInst = (ModVlanIdInstruction) l2Inst;
198 break;
199 case MPLS_LABEL:
200 modMplsInst = (ModMplsLabelInstruction) l2Inst;
201 break;
202 default:
203 log.warn("Unsupported l2 instruction sub type: {}", l2Inst.subtype());
204 break;
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700205 }
206 break;
207 case OUTPUT:
208 outInst = (OutputInstruction) inst;
209 break;
210 default:
Yi Tseng1b154bd2017-11-20 17:48:19 -0800211 log.warn("Unsupported instruction sub type: {}", inst.type());
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700212 break;
213 }
214 }
215
216 if (outInst == null) {
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800217 // for vlan_meta table only
218 if (modVlanIdInst != null) {
219 // set_vlan
220 VlanId vlanId = modVlanIdInst.vlanId();
221 PiActionParam newVlanParam =
Yi Tseng43ee7e82018-04-12 16:37:34 +0800222 new PiActionParam(FabricConstants.NEW_VLAN_ID,
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800223 ImmutableByteSequence.copyFrom(vlanId.toShort()));
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800224 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800225 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800226 .withParameter(newVlanParam)
227 .build();
228 } else {
229 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
230 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700231 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800232
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700233 short portNum = (short) outInst.port().toLong();
Yi Tseng43ee7e82018-04-12 16:37:34 +0800234 PiActionParam portNumParam = new PiActionParam(FabricConstants.PORT_NUM,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700235 ImmutableByteSequence.copyFrom(portNum));
236 if (modEthDstInst == null && modEthSrcInst == null) {
237 if (modVlanIdInst != null) {
238 VlanId vlanId = modVlanIdInst.vlanId();
239 PiActionParam vlanParam =
Yi Tseng43ee7e82018-04-12 16:37:34 +0800240 new PiActionParam(FabricConstants.NEW_VLAN_ID,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700241 ImmutableByteSequence.copyFrom(vlanId.toShort()));
242 // set_vlan_output
243 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800244 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN_OUTPUT)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700245 .withParameters(ImmutableList.of(portNumParam, vlanParam))
246 .build();
247 } else {
248 // output
249 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800250 .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT)
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700251 .withParameter(portNumParam)
252 .build();
253 }
254 }
255
256 if (modEthDstInst != null && modEthSrcInst != null) {
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700257 MacAddress srcMac = modEthSrcInst.mac();
258 MacAddress dstMac = modEthDstInst.mac();
Yi Tseng43ee7e82018-04-12 16:37:34 +0800259 PiActionParam srcMacParam = new PiActionParam(FabricConstants.SMAC,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700260 ImmutableByteSequence.copyFrom(srcMac.toBytes()));
Yi Tseng43ee7e82018-04-12 16:37:34 +0800261 PiActionParam dstMacParam = new PiActionParam(FabricConstants.DMAC,
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700262 ImmutableByteSequence.copyFrom(dstMac.toBytes()));
Yi Tseng1b154bd2017-11-20 17:48:19 -0800263
264 if (modMplsInst != null) {
265 // MPLS routing
266 MplsLabel mplsLabel = modMplsInst.label();
267 try {
268 ImmutableByteSequence mplsValue =
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700269 ImmutableByteSequence.copyFrom(mplsLabel.toInt()).fit(20);
Yi Tseng43ee7e82018-04-12 16:37:34 +0800270 PiActionParam mplsParam = new PiActionParam(FabricConstants.LABEL, mplsValue);
Yi Tseng1b154bd2017-11-20 17:48:19 -0800271 return PiAction.builder()
272 // FIXME: fins a way to determine v4 or v6
Yi Tseng43ee7e82018-04-12 16:37:34 +0800273 .withId(FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_V4)
Yi Tseng1b154bd2017-11-20 17:48:19 -0800274 .withParameters(ImmutableList.of(portNumParam,
275 srcMacParam,
276 dstMacParam,
277 mplsParam))
278 .build();
279 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
280 // Basically this won't happened because we already limited
281 // size of mpls value to 20 bits (0xFFFFF)
282 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
283 }
284 }
285
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800286 if (modVlanIdInst != null) {
287 VlanId vlanId = modVlanIdInst.vlanId();
288 PiActionParam vlanParam =
Yi Tseng43ee7e82018-04-12 16:37:34 +0800289 new PiActionParam(FabricConstants.NEW_VLAN_ID,
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800290 ImmutableByteSequence.copyFrom(vlanId.toShort()));
291 // L3 routing and set VLAN
292 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800293 .withId(FabricConstants.FABRIC_INGRESS_NEXT_L3_ROUTING_VLAN)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800294 .withParameters(ImmutableList.of(srcMacParam, dstMacParam, portNumParam, vlanParam))
295 .build();
296 } else {
297 // L3 routing
298 return PiAction.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800299 .withId(FabricConstants.FABRIC_INGRESS_NEXT_L3_ROUTING)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800300 .withParameters(ImmutableList.of(portNumParam,
301 srcMacParam,
302 dstMacParam))
303 .build();
304 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700305 }
306
307 throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
308 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800309
310 public static PiAction mapEgressNextTreatment(TrafficTreatment treatment) {
311 // Pop VLAN action for now, may add new action to this control block in the future.
312 return treatment.allInstructions()
313 .stream()
314 .filter(inst -> inst.type() == Instruction.Type.L2MODIFICATION)
315 .map(inst -> (L2ModificationInstruction) inst)
316 .filter(inst -> inst.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP)
317 .findFirst()
318 .map(inst -> POP_VLAN)
319 .orElse(NOP);
320 }
Yi Tsengfa4a1c72017-11-03 10:22:38 -0700321}