blob: 2ee66aa22f6c053f07c146c09d2cfccf09b3dd5a [file] [log] [blame]
Yi Tseng0b809722017-11-03 10:23:26 -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.pipeliner;
18
Yi Tseng20f9e7b2018-05-24 23:27:39 +080019import org.onlab.packet.VlanId;
Yi Tseng0b809722017-11-03 10:23:26 -070020import org.onosproject.net.DeviceId;
Yi Tseng20f9e7b2018-05-24 23:27:39 +080021import org.onosproject.net.PortNumber;
Yi Tseng4fd28432018-02-01 14:48:03 -080022import org.onosproject.net.driver.Driver;
Yi Tseng0b809722017-11-03 10:23:26 -070023import org.onosproject.net.flow.DefaultFlowRule;
24import org.onosproject.net.flow.DefaultTrafficSelector;
25import org.onosproject.net.flow.DefaultTrafficTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070026import org.onosproject.net.flow.TrafficSelector;
27import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng20f9e7b2018-05-24 23:27:39 +080028import org.onosproject.net.flow.criteria.Criterion;
Yi Tseng0b809722017-11-03 10:23:26 -070029import org.onosproject.net.flow.criteria.PiCriterion;
Yi Tseng20f9e7b2018-05-24 23:27:39 +080030import org.onosproject.net.flow.criteria.VlanIdCriterion;
Yi Tseng0b809722017-11-03 10:23:26 -070031import org.onosproject.net.flow.instructions.Instruction;
32import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Yi Tseng20f9e7b2018-05-24 23:27:39 +080033import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Yi Tseng4fd28432018-02-01 14:48:03 -080034import org.onosproject.net.flowobjective.DefaultNextObjective;
Yi Tseng0b809722017-11-03 10:23:26 -070035import org.onosproject.net.flowobjective.NextObjective;
Yi Tseng6e9b6f52018-02-27 10:40:51 +010036import org.onosproject.net.flowobjective.Objective;
Yi Tseng0b809722017-11-03 10:23:26 -070037import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng1b154bd2017-11-20 17:48:19 -080038import org.onosproject.net.group.DefaultGroupBucket;
39import org.onosproject.net.group.DefaultGroupDescription;
Esin Karaman24fda8a2018-01-26 11:52:28 +000040import org.onosproject.net.group.DefaultGroupKey;
Yi Tseng1b154bd2017-11-20 17:48:19 -080041import org.onosproject.net.group.GroupBucket;
42import org.onosproject.net.group.GroupBuckets;
43import org.onosproject.net.group.GroupDescription;
Esin Karaman24fda8a2018-01-26 11:52:28 +000044import org.onosproject.net.group.GroupKey;
45import org.onosproject.net.pi.runtime.PiAction;
Yi Tseng1b154bd2017-11-20 17:48:19 -080046import org.onosproject.net.pi.runtime.PiActionGroupId;
Esin Karaman24fda8a2018-01-26 11:52:28 +000047import org.onosproject.net.pi.runtime.PiActionParam;
Yi Tseng1b154bd2017-11-20 17:48:19 -080048import org.onosproject.net.pi.runtime.PiGroupKey;
Yi Tseng43ee7e82018-04-12 16:37:34 +080049import org.onosproject.pipelines.fabric.FabricConstants;
Yi Tseng0b809722017-11-03 10:23:26 -070050import org.slf4j.Logger;
51
Yi Tseng1b154bd2017-11-20 17:48:19 -080052import java.util.List;
Carmelo Cascone58136812018-07-19 03:40:16 +020053import java.util.Optional;
Yi Tseng1b154bd2017-11-20 17:48:19 -080054import java.util.stream.Collectors;
Yi Tseng0b809722017-11-03 10:23:26 -070055
Yi Tseng0b809722017-11-03 10:23:26 -070056import static org.slf4j.LoggerFactory.getLogger;
57
58/**
59 * Handling next objective for fabric pipeliner.
60 */
61public class FabricNextPipeliner {
62 private static final Logger log = getLogger(FabricNextPipeliner.class);
Yi Tseng4fd28432018-02-01 14:48:03 -080063 private static final String NO_HASHED_TABLE = "noHashedTable";
Yi Tseng0b809722017-11-03 10:23:26 -070064
Yi Tseng0b809722017-11-03 10:23:26 -070065 protected DeviceId deviceId;
Yi Tseng4fd28432018-02-01 14:48:03 -080066 protected Driver driver;
Yi Tseng0b809722017-11-03 10:23:26 -070067
Yi Tseng4fd28432018-02-01 14:48:03 -080068 public FabricNextPipeliner(DeviceId deviceId, Driver driver) {
Yi Tseng0b809722017-11-03 10:23:26 -070069 this.deviceId = deviceId;
Yi Tseng4fd28432018-02-01 14:48:03 -080070 this.driver = driver;
Yi Tseng0b809722017-11-03 10:23:26 -070071 }
72
73 public PipelinerTranslationResult next(NextObjective nextObjective) {
74 PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
Yi Tseng1b154bd2017-11-20 17:48:19 -080075
Yi Tseng20f9e7b2018-05-24 23:27:39 +080076 processNextVlanMeta(nextObjective, resultBuilder);
77
Yi Tseng0b809722017-11-03 10:23:26 -070078 switch (nextObjective.type()) {
79 case SIMPLE:
Yi Tseng1b154bd2017-11-20 17:48:19 -080080 processSimpleNext(nextObjective, resultBuilder);
81 break;
82 case HASHED:
83 processHashedNext(nextObjective, resultBuilder);
Yi Tseng0b809722017-11-03 10:23:26 -070084 break;
Esin Karaman24fda8a2018-01-26 11:52:28 +000085 case BROADCAST:
86 processBroadcastNext(nextObjective, resultBuilder);
87 break;
Yi Tseng0b809722017-11-03 10:23:26 -070088 default:
89 log.warn("Unsupported next type {}", nextObjective);
90 resultBuilder.setError(ObjectiveError.UNSUPPORTED);
91 break;
92 }
93
Yi Tseng0b809722017-11-03 10:23:26 -070094 return resultBuilder.build();
95 }
96
Yi Tseng20f9e7b2018-05-24 23:27:39 +080097 private void processNextVlanMeta(NextObjective next,
98 PipelinerTranslationResult.Builder resultBuilder) {
99 TrafficSelector meta = next.meta();
100 if (meta == null) {
101 // do nothing if there is no metadata in the next objective.
102 return;
103 }
104 VlanIdCriterion vlanIdCriterion =
105 (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
106
107 if (vlanIdCriterion == null) {
108 // do nothing if we can't find vlan from next objective metadata.
109 return;
110 }
111
112 VlanId vlanId = vlanIdCriterion.vlanId();
113 TrafficSelector selector = buildNextIdSelector(next.id());
114 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
115 .setVlanId(vlanId)
116 .build();
117
118 resultBuilder.addFlowRule(DefaultFlowRule.builder()
119 .withSelector(selector)
120 .withTreatment(treatment)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800121 .forTable(FabricConstants.FABRIC_INGRESS_NEXT_VLAN_META)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800122 .makePermanent()
123 .withPriority(next.priority())
124 .forDevice(deviceId)
125 .fromApp(next.appId())
126 .build());
127 }
128
Yi Tseng1b154bd2017-11-20 17:48:19 -0800129 private void processSimpleNext(NextObjective next,
130 PipelinerTranslationResult.Builder resultBuilder) {
131
Yi Tseng0b809722017-11-03 10:23:26 -0700132 if (next.next().size() > 1) {
133 log.warn("Only one treatment in simple next objective");
Yi Tseng1b154bd2017-11-20 17:48:19 -0800134 resultBuilder.setError(ObjectiveError.BADPARAMS);
135 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700136 }
137
138 TrafficSelector selector = buildNextIdSelector(next.id());
139 TrafficTreatment treatment = next.next().iterator().next();
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800140 PortNumber outputPort = getOutputPort(treatment);
Yi Tseng0b809722017-11-03 10:23:26 -0700141
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800142 if (outputPort == null) {
Yi Tseng0b809722017-11-03 10:23:26 -0700143 log.warn("At least one output instruction in simple next objective");
Yi Tseng1b154bd2017-11-20 17:48:19 -0800144 resultBuilder.setError(ObjectiveError.BADPARAMS);
145 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700146 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800147
Yi Tseng1b154bd2017-11-20 17:48:19 -0800148 resultBuilder.addFlowRule(DefaultFlowRule.builder()
149 .withSelector(selector)
150 .withTreatment(treatment)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800151 .forTable(FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE)
Yi Tseng1b154bd2017-11-20 17:48:19 -0800152 .makePermanent()
153 .withPriority(next.priority())
154 .forDevice(deviceId)
155 .fromApp(next.appId())
156 .build());
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800157
158 if (includesPopVlanInst(treatment)) {
159 processVlanPopRule(outputPort, next, resultBuilder);
160 }
Yi Tseng1b154bd2017-11-20 17:48:19 -0800161 }
162
Carmelo Cascone58136812018-07-19 03:40:16 +0200163 private Optional<OutputInstruction> getOutputInstruction(TrafficTreatment treatment) {
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800164 return treatment.allInstructions()
165 .stream()
166 .filter(inst -> inst.type() == Instruction.Type.OUTPUT)
167 .map(inst -> (OutputInstruction) inst)
Carmelo Cascone58136812018-07-19 03:40:16 +0200168 .findFirst();
169 }
170
171 private PortNumber getOutputPort(TrafficTreatment treatment) {
172 return getOutputInstruction(treatment)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800173 .map(OutputInstruction::port)
174 .orElse(null);
175 }
176
177 private boolean includesPopVlanInst(TrafficTreatment treatment) {
178 return treatment.allInstructions()
179 .stream()
180 .filter(inst -> inst.type() == Instruction.Type.L2MODIFICATION)
181 .map(inst -> (L2ModificationInstruction) inst)
182 .anyMatch(inst -> inst.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP);
183 }
184
185 private void processVlanPopRule(PortNumber port, NextObjective next,
186 PipelinerTranslationResult.Builder resultBuilder) {
187 TrafficSelector meta = next.meta();
188 VlanIdCriterion vlanIdCriterion =
189 (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
190 VlanId vlanId = vlanIdCriterion.vlanId();
191
192 PiCriterion egressVlanTableMatch = PiCriterion.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800193 .matchExact(FabricConstants.STANDARD_METADATA_EGRESS_PORT,
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800194 (short) port.toLong())
195 .build();
196 // Add VLAN pop rule to egress pipeline table
197 TrafficSelector selector = DefaultTrafficSelector.builder()
198 .matchPi(egressVlanTableMatch)
199 .matchVlanId(vlanId)
200 .build();
201 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
202 .popVlan()
203 .build();
204 resultBuilder.addFlowRule(DefaultFlowRule.builder()
205 .withSelector(selector)
206 .withTreatment(treatment)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800207 .forTable(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800208 .makePermanent()
209 .withPriority(next.priority())
210 .forDevice(deviceId)
211 .fromApp(next.appId())
212 .build());
213 }
214
215 private void processHashedNext(NextObjective next, PipelinerTranslationResult.Builder resultBuilder) {
Yi Tseng4fd28432018-02-01 14:48:03 -0800216 boolean noHashedTable = Boolean.parseBoolean(driver.getProperty(NO_HASHED_TABLE));
217
218 if (noHashedTable) {
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800219 if (next.next().isEmpty()) {
Yi Tseng4fd28432018-02-01 14:48:03 -0800220 return;
221 }
222 // use first action if not support hashed group
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800223 TrafficTreatment treatment = next.next().iterator().next();
Yi Tseng4fd28432018-02-01 14:48:03 -0800224
225 NextObjective.Builder simpleNext = DefaultNextObjective.builder()
226 .addTreatment(treatment)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800227 .withId(next.id())
228 .fromApp(next.appId())
Yi Tseng4fd28432018-02-01 14:48:03 -0800229 .makePermanent()
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800230 .withMeta(next.meta())
231 .withPriority(next.priority())
Yi Tseng4fd28432018-02-01 14:48:03 -0800232 .withType(NextObjective.Type.SIMPLE);
233
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800234 if (next.context().isPresent()) {
235 processSimpleNext(simpleNext.add(next.context().get()), resultBuilder);
Yi Tseng4fd28432018-02-01 14:48:03 -0800236 } else {
237 processSimpleNext(simpleNext.add(), resultBuilder);
238 }
239 return;
240 }
241
Yi Tseng1b154bd2017-11-20 17:48:19 -0800242 // create hash groups
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800243 int groupId = next.id();
244 List<GroupBucket> bucketList = next.next().stream()
Yi Tseng1b154bd2017-11-20 17:48:19 -0800245 .map(DefaultGroupBucket::createSelectGroupBucket)
246 .collect(Collectors.toList());
247
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800248 // Egress VLAN handling
249 next.next().forEach(treatment -> {
250 PortNumber outputPort = getOutputPort(treatment);
251 if (includesPopVlanInst(treatment) && outputPort != null) {
252 processVlanPopRule(outputPort, next, resultBuilder);
253 }
254 });
255
256 if (bucketList.size() != next.next().size()) {
Yi Tseng1b154bd2017-11-20 17:48:19 -0800257 // some action not converted
258 // set error
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800259 log.warn("Expected bucket size {}, got {}", next.next().size(), bucketList.size());
Yi Tseng1b154bd2017-11-20 17:48:19 -0800260 resultBuilder.setError(ObjectiveError.BADPARAMS);
261 return;
262 }
263
264 GroupBuckets buckets = new GroupBuckets(bucketList);
Yi Tseng43ee7e82018-04-12 16:37:34 +0800265 PiGroupKey groupKey = new PiGroupKey(FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
266 FabricConstants.FABRIC_INGRESS_NEXT_ECMP_SELECTOR,
Yi Tseng1b154bd2017-11-20 17:48:19 -0800267 groupId);
268
269 resultBuilder.addGroup(new DefaultGroupDescription(deviceId,
270 GroupDescription.Type.SELECT,
271 buckets,
272 groupKey,
273 groupId,
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800274 next.appId()));
Yi Tseng1b154bd2017-11-20 17:48:19 -0800275
276 // flow
Yi Tseng6e9b6f52018-02-27 10:40:51 +0100277 // If operation is ADD_TO_EXIST or REMOVE_FROM_EXIST, means we modify
278 // group buckets only, no changes for flow rule
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800279 if (next.op() == Objective.Operation.ADD_TO_EXISTING ||
280 next.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
Yi Tseng6e9b6f52018-02-27 10:40:51 +0100281 return;
282 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800283 TrafficSelector selector = buildNextIdSelector(next.id());
Yi Tseng1b154bd2017-11-20 17:48:19 -0800284 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800285 .piTableAction(PiActionGroupId.of(next.id()))
Yi Tseng0b809722017-11-03 10:23:26 -0700286 .build();
Yi Tseng1b154bd2017-11-20 17:48:19 -0800287
288 resultBuilder.addFlowRule(DefaultFlowRule.builder()
289 .withSelector(selector)
290 .withTreatment(treatment)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800291 .forTable(FabricConstants.FABRIC_INGRESS_NEXT_HASHED)
Yi Tseng1b154bd2017-11-20 17:48:19 -0800292 .makePermanent()
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800293 .withPriority(next.priority())
Yi Tseng1b154bd2017-11-20 17:48:19 -0800294 .forDevice(deviceId)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800295 .fromApp(next.appId())
Yi Tseng1b154bd2017-11-20 17:48:19 -0800296 .build());
Yi Tseng0b809722017-11-03 10:23:26 -0700297 }
298
299 private TrafficSelector buildNextIdSelector(int nextId) {
Yi Tseng1b154bd2017-11-20 17:48:19 -0800300 PiCriterion nextIdCriterion = PiCriterion.builder()
Yi Tseng43ee7e82018-04-12 16:37:34 +0800301 .matchExact(FabricConstants.FABRIC_METADATA_NEXT_ID, nextId)
Yi Tseng0b809722017-11-03 10:23:26 -0700302 .build();
303 return DefaultTrafficSelector.builder()
Yi Tseng1b154bd2017-11-20 17:48:19 -0800304 .matchPi(nextIdCriterion)
Yi Tseng0b809722017-11-03 10:23:26 -0700305 .build();
306 }
Esin Karaman24fda8a2018-01-26 11:52:28 +0000307
308 private void processBroadcastNext(NextObjective next, PipelinerTranslationResult.Builder resultBuilder) {
Carmelo Cascone58136812018-07-19 03:40:16 +0200309 final GroupDescription allGroup = getAllGroup(next);
310 if (allGroup == null) {
311 // Error already logged.
Esin Karaman24fda8a2018-01-26 11:52:28 +0000312 resultBuilder.setError(ObjectiveError.BADPARAMS);
313 return;
314 }
315
Carmelo Cascone58136812018-07-19 03:40:16 +0200316 resultBuilder.addGroup(allGroup);
Esin Karaman24fda8a2018-01-26 11:52:28 +0000317 //flow rule
Carmelo Cascone58136812018-07-19 03:40:16 +0200318 final TrafficSelector selector = buildNextIdSelector(next.id());
319 final PiActionParam groupIdParam = new PiActionParam(
320 FabricConstants.GID, allGroup.givenGroupId());
Esin Karaman24fda8a2018-01-26 11:52:28 +0000321
Carmelo Cascone58136812018-07-19 03:40:16 +0200322 final PiAction setMcGroupAction = PiAction.builder()
Esin Karaman24fda8a2018-01-26 11:52:28 +0000323 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP)
324 .withParameter(groupIdParam)
325 .build();
Carmelo Cascone58136812018-07-19 03:40:16 +0200326 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Esin Karaman24fda8a2018-01-26 11:52:28 +0000327 .piTableAction(setMcGroupAction)
328 .build();
329
Carmelo Cascone58136812018-07-19 03:40:16 +0200330 resultBuilder.addFlowRule(
331 DefaultFlowRule.builder()
332 .withSelector(selector)
333 .withTreatment(treatment)
334 .forTable(FabricConstants.FABRIC_INGRESS_NEXT_MULTICAST)
335 .makePermanent()
336 .withPriority(next.priority())
337 .forDevice(deviceId)
338 .fromApp(next.appId())
339 .build());
Esin Karaman24fda8a2018-01-26 11:52:28 +0000340
341 // Egress VLAN handling
Carmelo Cascone58136812018-07-19 03:40:16 +0200342 next.next().forEach(t -> {
343 PortNumber outputPort = getOutputPort(t);
344 if (includesPopVlanInst(t) && outputPort != null) {
Esin Karaman24fda8a2018-01-26 11:52:28 +0000345 processVlanPopRule(outputPort, next, resultBuilder);
346 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200347 if (t.allInstructions().size() > 2) {
348 // More than OUTPUT and VLAN_POP...
349 log.warn("Some instructions of BROADCAST NextObjective might" +
350 "not have been applied, supported only " +
351 "OUTPUT and VLAN_POP, but found {}", t);
352 }
Esin Karaman24fda8a2018-01-26 11:52:28 +0000353 });
354 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200355
356 private GroupDescription getAllGroup(NextObjective next) {
357 final List<GroupBucket> bucketList = next.next().stream()
358 .map(this::getOutputInstruction)
359 .filter(Optional::isPresent)
360 .map(Optional::get)
361 .map(i -> DefaultTrafficTreatment.builder().add(i).build())
362 .map(DefaultGroupBucket::createAllGroupBucket)
363 .collect(Collectors.toList());
364
365 if (bucketList.size() != next.next().size()) {
366 log.warn("Got BROADCAST NextObjective with {} treatments but " +
367 "only {} have OUTPUT instructions, cannot " +
368 "translate to ALL groups",
369 next.next().size(), bucketList.size());
370 return null;
371 }
372
373 final int groupId = next.id();
374 final GroupBuckets buckets = new GroupBuckets(bucketList);
375 // Used DefaultGroupKey instead of PiGroupKey
376 // as we don't have any action profile to apply to the groups of ALL type
377 final GroupKey groupKey = new DefaultGroupKey(FabricPipeliner.KRYO.serialize(groupId));
378
379 return new DefaultGroupDescription(deviceId,
380 GroupDescription.Type.ALL,
381 buckets,
382 groupKey,
383 groupId,
384 next.appId());
385 }
Yi Tseng0b809722017-11-03 10:23:26 -0700386}