blob: 12602b41e3d9f53f467f5cd5ba0226193368c8b5 [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;
40import org.onosproject.net.group.GroupBucket;
41import org.onosproject.net.group.GroupBuckets;
42import org.onosproject.net.group.GroupDescription;
Yi Tseng1b154bd2017-11-20 17:48:19 -080043import org.onosproject.net.pi.runtime.PiActionGroupId;
Yi Tseng1b154bd2017-11-20 17:48:19 -080044import org.onosproject.net.pi.runtime.PiGroupKey;
Yi Tseng0b809722017-11-03 10:23:26 -070045import org.slf4j.Logger;
46
Yi Tseng1b154bd2017-11-20 17:48:19 -080047import java.util.List;
Yi Tseng1b154bd2017-11-20 17:48:19 -080048import java.util.stream.Collectors;
Yi Tseng0b809722017-11-03 10:23:26 -070049
Yi Tseng27b9bc02018-04-12 14:52:40 +080050import static org.onosproject.pipelines.fabric.FabricConstants.ACT_PRF_FABRICINGRESS_NEXT_ECMP_SELECTOR_ID;
Yi Tseng1b154bd2017-11-20 17:48:19 -080051import static org.onosproject.pipelines.fabric.FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID;
Yi Tseng20f9e7b2018-05-24 23:27:39 +080052import static org.onosproject.pipelines.fabric.FabricConstants.HF_STANDARD_METADATA_EGRESS_PORT_ID;
53import static org.onosproject.pipelines.fabric.FabricConstants.TBL_EGRESS_VLAN_ID;
Yi Tseng1b154bd2017-11-20 17:48:19 -080054import static org.onosproject.pipelines.fabric.FabricConstants.TBL_HASHED_ID;
Yi Tseng1b154bd2017-11-20 17:48:19 -080055import static org.onosproject.pipelines.fabric.FabricConstants.TBL_SIMPLE_ID;
Yi Tseng20f9e7b2018-05-24 23:27:39 +080056import static org.onosproject.pipelines.fabric.FabricConstants.TBL_VLAN_META_ID;
Yi Tseng0b809722017-11-03 10:23:26 -070057import static org.slf4j.LoggerFactory.getLogger;
58
59/**
60 * Handling next objective for fabric pipeliner.
61 */
62public class FabricNextPipeliner {
63 private static final Logger log = getLogger(FabricNextPipeliner.class);
Yi Tseng4fd28432018-02-01 14:48:03 -080064 private static final String NO_HASHED_TABLE = "noHashedTable";
Yi Tseng0b809722017-11-03 10:23:26 -070065
Yi Tseng0b809722017-11-03 10:23:26 -070066 protected DeviceId deviceId;
Yi Tseng4fd28432018-02-01 14:48:03 -080067 protected Driver driver;
Yi Tseng0b809722017-11-03 10:23:26 -070068
Yi Tseng4fd28432018-02-01 14:48:03 -080069 public FabricNextPipeliner(DeviceId deviceId, Driver driver) {
Yi Tseng0b809722017-11-03 10:23:26 -070070 this.deviceId = deviceId;
Yi Tseng4fd28432018-02-01 14:48:03 -080071 this.driver = driver;
Yi Tseng0b809722017-11-03 10:23:26 -070072 }
73
74 public PipelinerTranslationResult next(NextObjective nextObjective) {
75 PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
Yi Tseng1b154bd2017-11-20 17:48:19 -080076
Yi Tseng20f9e7b2018-05-24 23:27:39 +080077 processNextVlanMeta(nextObjective, resultBuilder);
78
Yi Tseng0b809722017-11-03 10:23:26 -070079 switch (nextObjective.type()) {
80 case SIMPLE:
Yi Tseng1b154bd2017-11-20 17:48:19 -080081 processSimpleNext(nextObjective, resultBuilder);
82 break;
83 case HASHED:
84 processHashedNext(nextObjective, resultBuilder);
Yi Tseng0b809722017-11-03 10:23:26 -070085 break;
86 default:
87 log.warn("Unsupported next type {}", nextObjective);
88 resultBuilder.setError(ObjectiveError.UNSUPPORTED);
89 break;
90 }
91
Yi Tseng0b809722017-11-03 10:23:26 -070092 return resultBuilder.build();
93 }
94
Yi Tseng20f9e7b2018-05-24 23:27:39 +080095 private void processNextVlanMeta(NextObjective next,
96 PipelinerTranslationResult.Builder resultBuilder) {
97 TrafficSelector meta = next.meta();
98 if (meta == null) {
99 // do nothing if there is no metadata in the next objective.
100 return;
101 }
102 VlanIdCriterion vlanIdCriterion =
103 (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
104
105 if (vlanIdCriterion == null) {
106 // do nothing if we can't find vlan from next objective metadata.
107 return;
108 }
109
110 VlanId vlanId = vlanIdCriterion.vlanId();
111 TrafficSelector selector = buildNextIdSelector(next.id());
112 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
113 .setVlanId(vlanId)
114 .build();
115
116 resultBuilder.addFlowRule(DefaultFlowRule.builder()
117 .withSelector(selector)
118 .withTreatment(treatment)
119 .forTable(TBL_VLAN_META_ID)
120 .makePermanent()
121 .withPriority(next.priority())
122 .forDevice(deviceId)
123 .fromApp(next.appId())
124 .build());
125 }
126
Yi Tseng1b154bd2017-11-20 17:48:19 -0800127 private void processSimpleNext(NextObjective next,
128 PipelinerTranslationResult.Builder resultBuilder) {
129
Yi Tseng0b809722017-11-03 10:23:26 -0700130 if (next.next().size() > 1) {
131 log.warn("Only one treatment in simple next objective");
Yi Tseng1b154bd2017-11-20 17:48:19 -0800132 resultBuilder.setError(ObjectiveError.BADPARAMS);
133 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700134 }
135
136 TrafficSelector selector = buildNextIdSelector(next.id());
137 TrafficTreatment treatment = next.next().iterator().next();
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800138 PortNumber outputPort = getOutputPort(treatment);
Yi Tseng0b809722017-11-03 10:23:26 -0700139
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800140 if (outputPort == null) {
Yi Tseng0b809722017-11-03 10:23:26 -0700141 log.warn("At least one output instruction in simple next objective");
Yi Tseng1b154bd2017-11-20 17:48:19 -0800142 resultBuilder.setError(ObjectiveError.BADPARAMS);
143 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700144 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800145
Yi Tseng1b154bd2017-11-20 17:48:19 -0800146 resultBuilder.addFlowRule(DefaultFlowRule.builder()
147 .withSelector(selector)
148 .withTreatment(treatment)
149 .forTable(TBL_SIMPLE_ID)
150 .makePermanent()
151 .withPriority(next.priority())
152 .forDevice(deviceId)
153 .fromApp(next.appId())
154 .build());
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800155
156 if (includesPopVlanInst(treatment)) {
157 processVlanPopRule(outputPort, next, resultBuilder);
158 }
Yi Tseng1b154bd2017-11-20 17:48:19 -0800159 }
160
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800161 private PortNumber getOutputPort(TrafficTreatment treatment) {
162 return treatment.allInstructions()
163 .stream()
164 .filter(inst -> inst.type() == Instruction.Type.OUTPUT)
165 .map(inst -> (OutputInstruction) inst)
166 .findFirst()
167 .map(OutputInstruction::port)
168 .orElse(null);
169 }
170
171 private boolean includesPopVlanInst(TrafficTreatment treatment) {
172 return treatment.allInstructions()
173 .stream()
174 .filter(inst -> inst.type() == Instruction.Type.L2MODIFICATION)
175 .map(inst -> (L2ModificationInstruction) inst)
176 .anyMatch(inst -> inst.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP);
177 }
178
179 private void processVlanPopRule(PortNumber port, NextObjective next,
180 PipelinerTranslationResult.Builder resultBuilder) {
181 TrafficSelector meta = next.meta();
182 VlanIdCriterion vlanIdCriterion =
183 (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
184 VlanId vlanId = vlanIdCriterion.vlanId();
185
186 PiCriterion egressVlanTableMatch = PiCriterion.builder()
187 .matchExact(HF_STANDARD_METADATA_EGRESS_PORT_ID,
188 (short) port.toLong())
189 .build();
190 // Add VLAN pop rule to egress pipeline table
191 TrafficSelector selector = DefaultTrafficSelector.builder()
192 .matchPi(egressVlanTableMatch)
193 .matchVlanId(vlanId)
194 .build();
195 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
196 .popVlan()
197 .build();
198 resultBuilder.addFlowRule(DefaultFlowRule.builder()
199 .withSelector(selector)
200 .withTreatment(treatment)
201 .forTable(TBL_EGRESS_VLAN_ID)
202 .makePermanent()
203 .withPriority(next.priority())
204 .forDevice(deviceId)
205 .fromApp(next.appId())
206 .build());
207 }
208
209 private void processHashedNext(NextObjective next, PipelinerTranslationResult.Builder resultBuilder) {
Yi Tseng4fd28432018-02-01 14:48:03 -0800210 boolean noHashedTable = Boolean.parseBoolean(driver.getProperty(NO_HASHED_TABLE));
211
212 if (noHashedTable) {
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800213 if (next.next().isEmpty()) {
Yi Tseng4fd28432018-02-01 14:48:03 -0800214 return;
215 }
216 // use first action if not support hashed group
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800217 TrafficTreatment treatment = next.next().iterator().next();
Yi Tseng4fd28432018-02-01 14:48:03 -0800218
219 NextObjective.Builder simpleNext = DefaultNextObjective.builder()
220 .addTreatment(treatment)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800221 .withId(next.id())
222 .fromApp(next.appId())
Yi Tseng4fd28432018-02-01 14:48:03 -0800223 .makePermanent()
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800224 .withMeta(next.meta())
225 .withPriority(next.priority())
Yi Tseng4fd28432018-02-01 14:48:03 -0800226 .withType(NextObjective.Type.SIMPLE);
227
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800228 if (next.context().isPresent()) {
229 processSimpleNext(simpleNext.add(next.context().get()), resultBuilder);
Yi Tseng4fd28432018-02-01 14:48:03 -0800230 } else {
231 processSimpleNext(simpleNext.add(), resultBuilder);
232 }
233 return;
234 }
235
Yi Tseng1b154bd2017-11-20 17:48:19 -0800236 // create hash groups
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800237 int groupId = next.id();
238 List<GroupBucket> bucketList = next.next().stream()
Yi Tseng1b154bd2017-11-20 17:48:19 -0800239 .map(DefaultGroupBucket::createSelectGroupBucket)
240 .collect(Collectors.toList());
241
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800242 // Egress VLAN handling
243 next.next().forEach(treatment -> {
244 PortNumber outputPort = getOutputPort(treatment);
245 if (includesPopVlanInst(treatment) && outputPort != null) {
246 processVlanPopRule(outputPort, next, resultBuilder);
247 }
248 });
249
250 if (bucketList.size() != next.next().size()) {
Yi Tseng1b154bd2017-11-20 17:48:19 -0800251 // some action not converted
252 // set error
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800253 log.warn("Expected bucket size {}, got {}", next.next().size(), bucketList.size());
Yi Tseng1b154bd2017-11-20 17:48:19 -0800254 resultBuilder.setError(ObjectiveError.BADPARAMS);
255 return;
256 }
257
258 GroupBuckets buckets = new GroupBuckets(bucketList);
259 PiGroupKey groupKey = new PiGroupKey(TBL_HASHED_ID,
Yi Tseng27b9bc02018-04-12 14:52:40 +0800260 ACT_PRF_FABRICINGRESS_NEXT_ECMP_SELECTOR_ID,
Yi Tseng1b154bd2017-11-20 17:48:19 -0800261 groupId);
262
263 resultBuilder.addGroup(new DefaultGroupDescription(deviceId,
264 GroupDescription.Type.SELECT,
265 buckets,
266 groupKey,
267 groupId,
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800268 next.appId()));
Yi Tseng1b154bd2017-11-20 17:48:19 -0800269
270 // flow
Yi Tseng6e9b6f52018-02-27 10:40:51 +0100271 // If operation is ADD_TO_EXIST or REMOVE_FROM_EXIST, means we modify
272 // group buckets only, no changes for flow rule
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800273 if (next.op() == Objective.Operation.ADD_TO_EXISTING ||
274 next.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
Yi Tseng6e9b6f52018-02-27 10:40:51 +0100275 return;
276 }
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800277 TrafficSelector selector = buildNextIdSelector(next.id());
Yi Tseng1b154bd2017-11-20 17:48:19 -0800278 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800279 .piTableAction(PiActionGroupId.of(next.id()))
Yi Tseng0b809722017-11-03 10:23:26 -0700280 .build();
Yi Tseng1b154bd2017-11-20 17:48:19 -0800281
282 resultBuilder.addFlowRule(DefaultFlowRule.builder()
283 .withSelector(selector)
284 .withTreatment(treatment)
285 .forTable(TBL_HASHED_ID)
286 .makePermanent()
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800287 .withPriority(next.priority())
Yi Tseng1b154bd2017-11-20 17:48:19 -0800288 .forDevice(deviceId)
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800289 .fromApp(next.appId())
Yi Tseng1b154bd2017-11-20 17:48:19 -0800290 .build());
Yi Tseng0b809722017-11-03 10:23:26 -0700291 }
292
293 private TrafficSelector buildNextIdSelector(int nextId) {
Yi Tseng1b154bd2017-11-20 17:48:19 -0800294 PiCriterion nextIdCriterion = PiCriterion.builder()
295 .matchExact(HF_FABRIC_METADATA_NEXT_ID_ID, nextId)
Yi Tseng0b809722017-11-03 10:23:26 -0700296 .build();
297 return DefaultTrafficSelector.builder()
Yi Tseng1b154bd2017-11-20 17:48:19 -0800298 .matchPi(nextIdCriterion)
Yi Tseng0b809722017-11-03 10:23:26 -0700299 .build();
300 }
Yi Tseng0b809722017-11-03 10:23:26 -0700301}