blob: 47b7ef6d149c36f73aa83640aaa0d76b2b48e9e5 [file] [log] [blame]
Carmelo Casconeb5324e72018-11-25 02:26:32 -08001/*
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.pipeliner;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080018
19import com.google.common.collect.Lists;
Wailok Shumfb7e7872021-06-18 17:30:08 +080020import org.onlab.packet.MplsLabel;
Carmelo Casconed06a8512018-12-02 16:34:20 -080021import org.onlab.packet.VlanId;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080022import org.onosproject.net.DeviceId;
23import org.onosproject.net.PortNumber;
24import org.onosproject.net.flow.DefaultTrafficSelector;
25import org.onosproject.net.flow.DefaultTrafficTreatment;
26import org.onosproject.net.flow.TrafficSelector;
27import org.onosproject.net.flow.TrafficTreatment;
28import org.onosproject.net.flow.criteria.Criterion;
29import org.onosproject.net.flow.criteria.PiCriterion;
30import org.onosproject.net.flow.criteria.VlanIdCriterion;
31import org.onosproject.net.flow.instructions.Instruction;
Wailok Shumfb7e7872021-06-18 17:30:08 +080032import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
Carmelo Casconed06a8512018-12-02 16:34:20 -080033import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080034import org.onosproject.net.flowobjective.DefaultNextTreatment;
35import org.onosproject.net.flowobjective.NextObjective;
36import org.onosproject.net.flowobjective.NextTreatment;
37import org.onosproject.net.flowobjective.Objective;
38import org.onosproject.net.flowobjective.ObjectiveError;
39import org.onosproject.net.group.DefaultGroupBucket;
40import org.onosproject.net.group.DefaultGroupDescription;
41import org.onosproject.net.group.DefaultGroupKey;
42import org.onosproject.net.group.GroupBucket;
43import org.onosproject.net.group.GroupBuckets;
44import org.onosproject.net.group.GroupDescription;
45import org.onosproject.net.group.GroupKey;
46import org.onosproject.net.pi.model.PiTableId;
47import org.onosproject.net.pi.runtime.PiAction;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080048import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070049import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080050import org.onosproject.net.pi.runtime.PiGroupKey;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070051import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
Carmelo Cascone2102bfb2020-12-04 16:54:24 -080052import org.onosproject.pipelines.fabric.FabricConstants;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070053import org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080054
55import java.util.Collection;
56import java.util.List;
57import java.util.Objects;
58import java.util.Set;
59import java.util.stream.Collectors;
60
61import static java.lang.String.format;
Wailok Shumfb7e7872021-06-18 17:30:08 +080062import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.MPLS_LABEL;
Carmelo Casconed06a8512018-12-02 16:34:20 -080063import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080064import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070065import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.criterion;
66import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instruction;
67import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instructions;
68import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.outputPort;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080069
70/**
71 * ObjectiveTranslator implementation for NextObjective.
72 */
73class NextObjectiveTranslator
74 extends AbstractObjectiveTranslator<NextObjective> {
75
Carmelo Cascone45cc0862018-11-26 11:50:41 -080076 private static final String XCONNECT = "xconnect";
77
Carmelo Casconeb5324e72018-11-25 02:26:32 -080078 NextObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
79 super(deviceId, capabilities);
80 }
81
82 @Override
83 public ObjectiveTranslation doTranslate(NextObjective obj)
84 throws FabricPipelinerException {
85
86 final ObjectiveTranslation.Builder resultBuilder =
87 ObjectiveTranslation.builder();
88
89 switch (obj.type()) {
90 case SIMPLE:
91 simpleNext(obj, resultBuilder, false);
92 break;
93 case HASHED:
94 hashedNext(obj, resultBuilder);
95 break;
96 case BROADCAST:
Carmelo Cascone45cc0862018-11-26 11:50:41 -080097 if (isXconnect(obj)) {
98 xconnectNext(obj, resultBuilder);
99 } else {
100 multicastNext(obj, resultBuilder);
101 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800102 break;
103 default:
104 log.warn("Unsupported NextObjective type '{}'", obj);
105 return ObjectiveTranslation.ofError(ObjectiveError.UNSUPPORTED);
106 }
107
108 if (!isGroupModifyOp(obj)) {
Wailok Shumfb7e7872021-06-18 17:30:08 +0800109 // Generate next MPLS and VLAN rules.
110 nextMpls(obj, resultBuilder);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800111 nextVlan(obj, resultBuilder);
112 }
113
114 return resultBuilder.build();
115 }
116
Wailok Shumfb7e7872021-06-18 17:30:08 +0800117 private void nextMpls(NextObjective obj,
118 ObjectiveTranslation.Builder resultBuilder)
119 throws FabricPipelinerException {
120 // Next objective can contain only one mpls push and one mpls label
121 // instruction. Pipeliner does not support other configurations.
122
123 final List<List<ModMplsLabelInstruction>> mplsInstructions = defaultNextTreatments(
124 obj.nextTreatments(), false).stream()
125 .map(defaultNextTreatment -> l2Instructions(defaultNextTreatment.treatment(), MPLS_LABEL)
126 .stream().map(v -> (ModMplsLabelInstruction) v)
127 .collect(Collectors.toList()))
128 .filter(l -> !l.isEmpty())
129 .collect(Collectors.toList());
130
131 if (mplsInstructions.isEmpty()) {
132 // No need to apply next mpls table
133 return;
134 }
135
136 // We expect one mpls label for each treatment and the label has to be the same
137 final Set<MplsLabel> mplsLabels = mplsInstructions.stream()
138 .flatMap(Collection::stream)
139 .map(ModMplsLabelInstruction::label)
140 .collect(Collectors.toSet());
141 if (obj.nextTreatments().size() != mplsInstructions.size() ||
142 mplsLabels.size() != 1) {
143 throw new FabricPipelinerException(
144 "Inconsistent MPLS_LABEL instructions, cannot process " +
145 "next_mpls rule. It is required that all " +
146 "treatments have the same MPLS_LABEL instructions.");
147 }
148 final TrafficSelector selector = nextIdSelector(obj.id());
149 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
150 .setMpls(mplsLabels.iterator().next())
151 .build();
152
153 resultBuilder.addFlowRule(flowRule(
154 obj, FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_MPLS,
155 selector, treatment));
156 }
157
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800158 private void nextVlan(NextObjective obj,
159 ObjectiveTranslation.Builder resultBuilder)
160 throws FabricPipelinerException {
Daniele Morof51d0c12019-07-30 10:43:10 -0700161 // We expect NextObjective treatments to contain one or two VLAN instructions.
162 // If two, this treatment should be mapped to an action for double-vlan push.
163 // In fabric.p4, mapping next IDs to VLAN IDs is done by a direct table (next_vlan),
164 // for this reason, we also make sure that all treatments in the NextObjective
165 // have exactly the same VLAN instructions, as they will be mapped to a single action
Carmelo Casconed06a8512018-12-02 16:34:20 -0800166
Daniele Morof51d0c12019-07-30 10:43:10 -0700167 // Try to extract VLAN instructions in the treatment,
168 // later we check if we support multiple VLAN termination.
169 final List<List<ModVlanIdInstruction>> vlanInstructions = defaultNextTreatments(
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700170 obj.nextTreatments(), false).stream()
Daniele Morof51d0c12019-07-30 10:43:10 -0700171 .map(defaultNextTreatment ->
172 l2Instructions(defaultNextTreatment.treatment(), VLAN_ID)
173 .stream().map(v -> (ModVlanIdInstruction) v)
174 .collect(Collectors.toList()))
175 .filter(l -> !l.isEmpty())
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700176 .collect(Collectors.toList());
Daniele Morof51d0c12019-07-30 10:43:10 -0700177
Carmelo Casconed06a8512018-12-02 16:34:20 -0800178 final VlanIdCriterion vlanIdCriterion = obj.meta() == null ? null
179 : (VlanIdCriterion) criterion(obj.meta().criteria(), Criterion.Type.VLAN_VID);
Carmelo Casconed06a8512018-12-02 16:34:20 -0800180
Daniele Morof51d0c12019-07-30 10:43:10 -0700181 final List<VlanId> vlanIdList;
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700182 if (vlanInstructions.isEmpty() && vlanIdCriterion == null) {
183 // No VLAN_ID to apply.
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800184 return;
Daniele Morof51d0c12019-07-30 10:43:10 -0700185 }
186 if (!vlanInstructions.isEmpty()) {
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700187 // Give priority to what found in the instructions.
Daniele Morof51d0c12019-07-30 10:43:10 -0700188 // Expect the same VLAN ID (or two VLAN IDs in the same order) for all instructions.
189 final Set<List<VlanId>> vlanIds = vlanInstructions.stream()
190 .map(l -> l.stream().map(ModVlanIdInstruction::vlanId).collect(Collectors.toList()))
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700191 .collect(Collectors.toSet());
192 if (obj.nextTreatments().size() != vlanInstructions.size() ||
193 vlanIds.size() != 1) {
Carmelo Casconed06a8512018-12-02 16:34:20 -0800194 throw new FabricPipelinerException(
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700195 "Inconsistent VLAN_ID instructions, cannot process " +
196 "next_vlan rule. It is required that all " +
Daniele Morof51d0c12019-07-30 10:43:10 -0700197 "treatments have the same VLAN_ID instructions.");
Carmelo Casconed06a8512018-12-02 16:34:20 -0800198 }
Daniele Morof51d0c12019-07-30 10:43:10 -0700199 vlanIdList = vlanIds.iterator().next();
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700200 } else {
201 // Use the value in meta.
202 // FIXME: there should be no need to generate a next_vlan rule for
203 // the value found in meta. Meta describes the fields that were
204 // expected to be matched in previous pipeline stages, i.e.
205 // existing packet fields. But, for some reason, if we remove this
206 // rule, traffic is not forwarded at spines. We might need to look
207 // at the way default VLANs are handled in fabric.p4.
Daniele Morof51d0c12019-07-30 10:43:10 -0700208 vlanIdList = List.of(vlanIdCriterion.vlanId());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800209 }
210
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800211 final TrafficSelector selector = nextIdSelector(obj.id());
Daniele Morof51d0c12019-07-30 10:43:10 -0700212 final TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
213 vlanIdList.stream().forEach(vlanId -> treatmentBuilder.setVlanId(vlanId));
214 final TrafficTreatment treatment = treatmentBuilder.build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800215
216 resultBuilder.addFlowRule(flowRule(
Wailok Shumfb7e7872021-06-18 17:30:08 +0800217 obj, FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN,
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800218 selector, treatment));
219 }
220
221 private void simpleNext(NextObjective obj,
222 ObjectiveTranslation.Builder resultBuilder,
223 boolean forceSimple)
224 throws FabricPipelinerException {
225
226 if (capabilities.hasHashedTable()) {
227 // Use hashed table when possible.
228 hashedNext(obj, resultBuilder);
229 return;
230 }
231
232 if (obj.nextTreatments().isEmpty()) {
233 // Do nothing.
234 return;
235 } else if (!forceSimple && obj.nextTreatments().size() != 1) {
236 throw new FabricPipelinerException(format(
237 "SIMPLE NextObjective should contain only 1 treatment, found %d",
238 obj.nextTreatments().size()), ObjectiveError.BADPARAMS);
239 }
240
241 final TrafficSelector selector = nextIdSelector(obj.id());
242
Carmelo Casconed06a8512018-12-02 16:34:20 -0800243 final List<DefaultNextTreatment> treatments = defaultNextTreatments(
244 obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800245
246 if (forceSimple && treatments.size() > 1) {
pierventre4e02fb52020-08-13 16:35:15 +0200247 log.warn("Forcing SIMPLE behavior for NextObjective with {} treatments {}",
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800248 treatments.size(), obj);
249 }
250
251 // If not forcing, we are essentially extracting the only available treatment.
Carmelo Casconed06a8512018-12-02 16:34:20 -0800252 final TrafficTreatment treatment = defaultNextTreatments(
253 obj.nextTreatments(), true).get(0).treatment();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800254
255 resultBuilder.addFlowRule(flowRule(
256 obj, FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE,
257 selector, treatment));
258
259 handleEgress(obj, treatment, resultBuilder, false);
260 }
261
262 private void hashedNext(NextObjective obj,
263 ObjectiveTranslation.Builder resultBuilder)
264 throws FabricPipelinerException {
265
266 if (!capabilities.hasHashedTable()) {
267 simpleNext(obj, resultBuilder, true);
268 return;
269 }
270
271 // Updated result builder with hashed group.
272 final int groupId = selectGroup(obj, resultBuilder);
273
pierventrea137fba2021-06-14 20:28:35 +0200274 if (isGroupModifyOp(obj) || obj.op() == Objective.Operation.VERIFY) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800275 // No changes to flow rules.
276 return;
277 }
278
279 final TrafficSelector selector = nextIdSelector(obj.id());
280 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700281 .piTableAction(PiActionProfileGroupId.of(groupId))
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800282 .build();
283
284 resultBuilder.addFlowRule(flowRule(
285 obj, FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
286 selector, treatment));
287 }
288
289 private void handleEgress(NextObjective obj, TrafficTreatment treatment,
290 ObjectiveTranslation.Builder resultBuilder,
291 boolean strict)
292 throws FabricPipelinerException {
293 final PortNumber outPort = outputPort(treatment);
294 final Instruction popVlanInst = l2Instruction(treatment, VLAN_POP);
pierventre48e78822020-12-15 17:34:54 +0100295 if (outPort != null) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800296 if (strict && treatment.allInstructions().size() > 2) {
297 throw new FabricPipelinerException(
298 "Treatment contains instructions other " +
299 "than OUTPUT and VLAN_POP, cannot generate " +
300 "egress rules");
301 }
pierventre48e78822020-12-15 17:34:54 +0100302 // We cannot program if there are no proper metadata in the objective
303 if (obj.meta() != null && obj.meta().getCriterion(Criterion.Type.VLAN_VID) != null) {
304 egressVlan(outPort, obj, popVlanInst, resultBuilder);
305 } else {
Wailok Shumfb7e7872021-06-18 17:30:08 +0800306 log.debug("NextObjective {} is trying to program {} without {} information",
pierventre48e78822020-12-15 17:34:54 +0100307 obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
308 obj.meta() == null ? "metadata" : "vlanId");
309 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800310 }
311 }
312
pierventre48e78822020-12-15 17:34:54 +0100313 private void egressVlan(PortNumber outPort, NextObjective obj, Instruction popVlanInst,
314 ObjectiveTranslation.Builder resultBuilder)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800315 throws FabricPipelinerException {
316
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800317 final VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) criterion(
318 obj.meta(), Criterion.Type.VLAN_VID);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800319
320 final PiCriterion egressVlanTableMatch = PiCriterion.builder()
321 .matchExact(FabricConstants.HDR_EG_PORT, outPort.toLong())
322 .build();
323 final TrafficSelector selector = DefaultTrafficSelector.builder()
324 .matchPi(egressVlanTableMatch)
325 .matchVlanId(vlanIdCriterion.vlanId())
326 .build();
pierventre48e78822020-12-15 17:34:54 +0100327 final TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
328 if (popVlanInst == null) {
329 treatmentBuilder.pushVlan();
330 } else {
331 treatmentBuilder.popVlan();
332 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800333
334 resultBuilder.addFlowRule(flowRule(
335 obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
pierventre48e78822020-12-15 17:34:54 +0100336 selector, treatmentBuilder.build()));
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800337 }
338
339 private TrafficSelector nextIdSelector(int nextId) {
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800340 return nextIdSelectorBuilder(nextId).build();
341 }
342
343 private TrafficSelector.Builder nextIdSelectorBuilder(int nextId) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800344 final PiCriterion nextIdCriterion = PiCriterion.builder()
345 .matchExact(FabricConstants.HDR_NEXT_ID, nextId)
346 .build();
347 return DefaultTrafficSelector.builder()
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800348 .matchPi(nextIdCriterion);
349 }
350
351 private void xconnectNext(NextObjective obj, ObjectiveTranslation.Builder resultBuilder)
352 throws FabricPipelinerException {
353
354 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800355 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800356
357 final List<PortNumber> outPorts = defaultNextTreatments.stream()
358 .map(DefaultNextTreatment::treatment)
359 .map(FabricUtils::outputPort)
360 .filter(Objects::nonNull)
361 .collect(Collectors.toList());
362
363 if (outPorts.size() != 2) {
364 throw new FabricPipelinerException(format(
365 "Handling XCONNECT with %d treatments (ports), but expected is 2",
366 defaultNextTreatments.size()), ObjectiveError.UNSUPPORTED);
367 }
368
369 final PortNumber port1 = outPorts.get(0);
370 final PortNumber port2 = outPorts.get(1);
371 final TrafficSelector selector1 = nextIdSelectorBuilder(obj.id())
372 .matchInPort(port1)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800373 .build();
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800374 final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
375 .setOutput(port2)
376 .build();
377 final TrafficSelector selector2 = nextIdSelectorBuilder(obj.id())
378 .matchInPort(port2)
379 .build();
380 final TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
381 .setOutput(port1)
382 .build();
383
384 resultBuilder.addFlowRule(flowRule(
385 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
386 selector1, treatment1));
387 resultBuilder.addFlowRule(flowRule(
388 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
389 selector2, treatment2));
390
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800391 }
392
393 private void multicastNext(NextObjective obj,
394 ObjectiveTranslation.Builder resultBuilder)
395 throws FabricPipelinerException {
396
397 // Create ALL group that will be translated to a PRE multicast entry.
398 final int groupId = allGroup(obj, resultBuilder);
399
400 if (isGroupModifyOp(obj)) {
401 // No changes to flow rules.
402 return;
403 }
404
405 final TrafficSelector selector = nextIdSelector(obj.id());
406 final PiActionParam groupIdParam = new PiActionParam(
407 FabricConstants.GROUP_ID, groupId);
408 final PiAction setMcGroupAction = PiAction.builder()
409 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP_ID)
410 .withParameter(groupIdParam)
411 .build();
412 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
413 .piTableAction(setMcGroupAction)
414 .build();
415
416 resultBuilder.addFlowRule(flowRule(
417 obj, FabricConstants.FABRIC_INGRESS_NEXT_MULTICAST,
418 selector, treatment));
419 }
420
421 private int selectGroup(NextObjective obj,
422 ObjectiveTranslation.Builder resultBuilder)
423 throws FabricPipelinerException {
424
425 final PiTableId hashedTableId = FabricConstants.FABRIC_INGRESS_NEXT_HASHED;
426 final List<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800427 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800428 final List<TrafficTreatment> piTreatments = Lists.newArrayList();
429
430 for (DefaultNextTreatment t : defaultNextTreatments) {
431 // Map treatment to PI...
432 piTreatments.add(mapTreatmentToPiIfNeeded(t.treatment(), hashedTableId));
433 // ...and handle egress if necessary.
434 handleEgress(obj, t.treatment(), resultBuilder, false);
435 }
436
437 final List<GroupBucket> bucketList = piTreatments.stream()
438 .map(DefaultGroupBucket::createSelectGroupBucket)
439 .collect(Collectors.toList());
440
441 final int groupId = obj.id();
pierventrea137fba2021-06-14 20:28:35 +0200442 final PiGroupKey groupKey = (PiGroupKey) getGroupKey(obj);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800443
444 resultBuilder.addGroup(new DefaultGroupDescription(
445 deviceId,
446 GroupDescription.Type.SELECT,
447 new GroupBuckets(bucketList),
448 groupKey,
449 groupId,
450 obj.appId()));
451
452 return groupId;
453 }
454
455 private int allGroup(NextObjective obj,
456 ObjectiveTranslation.Builder resultBuilder)
457 throws FabricPipelinerException {
458
459 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800460 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800461 // No need to map treatments to PI as translation of ALL groups to PRE
462 // multicast entries is based solely on the output port.
463 for (DefaultNextTreatment t : defaultNextTreatments) {
464 handleEgress(obj, t.treatment(), resultBuilder, true);
465 }
466
467 // FIXME: this implementation supports only the case in which each
468 // switch interface is associated with only one VLAN, otherwise we would
469 // need to support replicating multiple times the same packet for the
470 // same port while setting different VLAN IDs. Hence, collect in a set.
471 final Set<PortNumber> outPorts = defaultNextTreatments.stream()
472 .map(DefaultNextTreatment::treatment)
473 .map(FabricUtils::outputPort)
474 .filter(Objects::nonNull)
475 .collect(Collectors.toSet());
476
477 if (outPorts.size() != defaultNextTreatments.size()) {
478 throw new FabricPipelinerException(format(
479 "Found BROADCAST NextObjective with %d treatments but " +
480 "found only %d distinct OUTPUT port numbers, cannot " +
481 "translate to ALL groups",
482 defaultNextTreatments.size(), outPorts.size()),
483 ObjectiveError.UNSUPPORTED);
484 }
485
486 final List<GroupBucket> bucketList = outPorts.stream()
487 .map(p -> DefaultTrafficTreatment.builder().setOutput(p).build())
488 .map(DefaultGroupBucket::createAllGroupBucket)
489 .collect(Collectors.toList());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800490
491 final int groupId = obj.id();
492 // Use DefaultGroupKey instead of PiGroupKey as we don't have any
493 // action profile to apply to the groups of ALL type.
pierventrea137fba2021-06-14 20:28:35 +0200494 final GroupKey groupKey = getGroupKey(obj);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800495
496 resultBuilder.addGroup(
497 new DefaultGroupDescription(
498 deviceId,
499 GroupDescription.Type.ALL,
500 new GroupBuckets(bucketList),
501 groupKey,
502 groupId,
503 obj.appId()));
504
505 return groupId;
506 }
507
Carmelo Casconed06a8512018-12-02 16:34:20 -0800508 private List<DefaultNextTreatment> defaultNextTreatments(
509 Collection<NextTreatment> nextTreatments, boolean strict)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800510 throws FabricPipelinerException {
511 final List<DefaultNextTreatment> defaultNextTreatments = Lists.newArrayList();
512 final List<NextTreatment> unsupportedNextTreatments = Lists.newArrayList();
513 for (NextTreatment n : nextTreatments) {
514 if (n.type() == NextTreatment.Type.TREATMENT) {
515 defaultNextTreatments.add((DefaultNextTreatment) n);
516 } else {
517 unsupportedNextTreatments.add(n);
518 }
519 }
Carmelo Casconed06a8512018-12-02 16:34:20 -0800520 if (strict && !unsupportedNextTreatments.isEmpty()) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800521 throw new FabricPipelinerException(format(
522 "Unsupported NextTreatments: %s",
523 unsupportedNextTreatments));
524 }
525 return defaultNextTreatments;
526 }
527
Carmelo Casconed06a8512018-12-02 16:34:20 -0800528 private TrafficTreatment getFirstDefaultNextTreatmentIfAny(
529 Collection<NextTreatment> nextTreatments)
530 throws FabricPipelinerException {
531 final Collection<DefaultNextTreatment> nexts = defaultNextTreatments(nextTreatments, false);
532 return nexts.isEmpty() ? null : nexts.iterator().next().treatment();
533 }
534
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800535 private boolean isGroupModifyOp(NextObjective obj) {
pierventre167d4482021-01-08 17:35:47 +0100536 // If operation is ADD_TO_EXIST, REMOVE_FROM_EXIST it means we modify
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800537 // group buckets only, no changes for flow rules.
538 return obj.op() == Objective.Operation.ADD_TO_EXISTING ||
pierventre167d4482021-01-08 17:35:47 +0100539 obj.op() == Objective.Operation.REMOVE_FROM_EXISTING;
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800540 }
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800541
542 private boolean isXconnect(NextObjective obj) {
543 return obj.appId().name().contains(XCONNECT);
544 }
pierventrea137fba2021-06-14 20:28:35 +0200545
546 // Builds up the group key based on the next objective type
547 public GroupKey getGroupKey(NextObjective objective) {
548 if (objective.type() == NextObjective.Type.HASHED || objective.type() == NextObjective.Type.SIMPLE) {
549 return new PiGroupKey(FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
550 FabricConstants.FABRIC_INGRESS_NEXT_HASHED_SELECTOR,
551 objective.id());
552 } else if (objective.type() == NextObjective.Type.BROADCAST) {
553 return new DefaultGroupKey(
554 FabricPipeliner.KRYO.serialize(objective.id()));
555 }
556 return null;
557 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800558}