blob: cea5c47ab6bc7fbb11abf9928c85116765f5f8d0 [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
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070017package org.onosproject.pipelines.fabric.impl.behaviour.pipeliner;
Yi Tseng0b809722017-11-03 10:23:26 -070018
19import com.google.common.collect.ImmutableList;
pierventre91eaff32021-06-14 20:28:35 +020020import com.google.common.collect.Lists;
21import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
Carmelo Casconedb347372021-05-26 19:30:30 +020023import org.onlab.packet.Ethernet;
Yi Tseng0b809722017-11-03 10:23:26 -070024import org.onlab.util.KryoNamespace;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080025import org.onlab.util.SharedExecutors;
Carmelo Casconedb347372021-05-26 19:30:30 +020026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
Yi Tseng0b809722017-11-03 10:23:26 -070028import org.onosproject.net.DeviceId;
29import org.onosproject.net.PortNumber;
30import org.onosproject.net.behaviour.NextGroup;
31import org.onosproject.net.behaviour.Pipeliner;
32import org.onosproject.net.behaviour.PipelinerContext;
Carmelo Casconedb347372021-05-26 19:30:30 +020033import org.onosproject.net.flow.DefaultFlowRule;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070036import org.onosproject.net.flow.FlowRule;
37import org.onosproject.net.flow.FlowRuleOperations;
Yi Tseng0b809722017-11-03 10:23:26 -070038import org.onosproject.net.flow.FlowRuleService;
Carmelo Casconedb347372021-05-26 19:30:30 +020039import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.flow.criteria.Criteria;
42import org.onosproject.net.flow.criteria.PiCriterion;
pierventre91eaff32021-06-14 20:28:35 +020043import org.onosproject.net.flowobjective.DefaultNextObjective;
Yi Tseng0b809722017-11-03 10:23:26 -070044import org.onosproject.net.flowobjective.FilteringObjective;
45import org.onosproject.net.flowobjective.FlowObjectiveStore;
46import org.onosproject.net.flowobjective.ForwardingObjective;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080047import org.onosproject.net.flowobjective.IdNextTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070048import org.onosproject.net.flowobjective.NextObjective;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080049import org.onosproject.net.flowobjective.NextTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070050import org.onosproject.net.flowobjective.Objective;
51import org.onosproject.net.flowobjective.ObjectiveError;
pierventre91eaff32021-06-14 20:28:35 +020052import org.onosproject.net.group.DefaultGroup;
53import org.onosproject.net.group.Group;
54import org.onosproject.net.group.GroupBucket;
55import org.onosproject.net.group.GroupBuckets;
Yi Tseng0b809722017-11-03 10:23:26 -070056import org.onosproject.net.group.GroupDescription;
pierventre91eaff32021-06-14 20:28:35 +020057import org.onosproject.net.group.GroupKey;
Yi Tseng0b809722017-11-03 10:23:26 -070058import org.onosproject.net.group.GroupService;
Carmelo Casconedb347372021-05-26 19:30:30 +020059import org.onosproject.net.pi.runtime.PiAction;
60import org.onosproject.net.pi.runtime.PiActionParam;
61import org.onosproject.pipelines.fabric.FabricConstants;
62import org.onosproject.pipelines.fabric.impl.FabricPipeconfLoader;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070063import org.onosproject.pipelines.fabric.impl.behaviour.AbstractFabricHandlerBehavior;
64import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
Yi Tseng0b809722017-11-03 10:23:26 -070065import org.onosproject.store.serializers.KryoNamespaces;
66import org.slf4j.Logger;
67
68import java.util.Collection;
pierventre91eaff32021-06-14 20:28:35 +020069import java.util.Collections;
Yi Tseng0b809722017-11-03 10:23:26 -070070import java.util.List;
pierventre91eaff32021-06-14 20:28:35 +020071import java.util.Map;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080072import java.util.Objects;
pierventre91eaff32021-06-14 20:28:35 +020073import java.util.Set;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080074import java.util.concurrent.CompletableFuture;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080075import java.util.concurrent.ExecutorService;
Yi Tseng1b154bd2017-11-20 17:48:19 -080076import java.util.stream.Collectors;
Yi Tseng0b809722017-11-03 10:23:26 -070077
Carmelo Casconeb5324e72018-11-25 02:26:32 -080078import static java.lang.String.format;
pierventre4e02fb52020-08-13 16:35:15 +020079import static org.onosproject.net.flowobjective.NextObjective.Type.SIMPLE;
Carmelo Casconedb347372021-05-26 19:30:30 +020080import static org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter.ONE;
81import static org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter.ZERO;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070082import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.outputPort;
Wailok Shumfac26d42021-06-18 17:30:08 +080083import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.FWD_IPV4_ROUTING;
84import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_INTERNAL;
Yi Tseng0b809722017-11-03 10:23:26 -070085import static org.slf4j.LoggerFactory.getLogger;
86
87/**
Carmelo Casconeb5324e72018-11-25 02:26:32 -080088 * Pipeliner implementation for fabric pipeline which uses ObjectiveTranslator
89 * implementations to translate flow objectives for the different blocks,
90 * filtering, forwarding and next.
Yi Tseng0b809722017-11-03 10:23:26 -070091 */
Carmelo Casconeb5324e72018-11-25 02:26:32 -080092public class FabricPipeliner extends AbstractFabricHandlerBehavior
93 implements Pipeliner {
94
Yi Tseng0b809722017-11-03 10:23:26 -070095 private static final Logger log = getLogger(FabricPipeliner.class);
Carmelo Casconedb347372021-05-26 19:30:30 +020096 private static final int DEFAULT_FLOW_PRIORITY = 100;
97 public static final int DEFAULT_VLAN = 4094;
Yi Tseng0b809722017-11-03 10:23:26 -070098
99 protected static final KryoNamespace KRYO = new KryoNamespace.Builder()
100 .register(KryoNamespaces.API)
101 .register(FabricNextGroup.class)
102 .build("FabricPipeliner");
103
Yi Tseng0b809722017-11-03 10:23:26 -0700104 protected DeviceId deviceId;
Carmelo Casconedb347372021-05-26 19:30:30 +0200105 protected ApplicationId appId;
Yi Tseng0b809722017-11-03 10:23:26 -0700106 protected FlowRuleService flowRuleService;
107 protected GroupService groupService;
108 protected FlowObjectiveStore flowObjectiveStore;
Carmelo Casconedb347372021-05-26 19:30:30 +0200109 protected CoreService coreService;
Yi Tseng0b809722017-11-03 10:23:26 -0700110
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800111 private FilteringObjectiveTranslator filteringTranslator;
112 private ForwardingObjectiveTranslator forwardingTranslator;
113 private NextObjectiveTranslator nextTranslator;
Charles Chan91ea9722018-08-30 15:56:32 -0700114
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800115 private final ExecutorService callbackExecutor = SharedExecutors.getPoolThreadExecutor();
Yi Tseng0b809722017-11-03 10:23:26 -0700116
Daniele Morof51d0c12019-07-30 10:43:10 -0700117 /**
118 * Creates a new instance of this behavior with the given capabilities.
119 *
120 * @param capabilities capabilities
121 */
122 public FabricPipeliner(FabricCapabilities capabilities) {
123 super(capabilities);
124 }
125
126 /**
127 * Create a new instance of this behaviour. Used by the abstract projectable
128 * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
129 */
130 public FabricPipeliner() {
131 super();
132 }
133
Yi Tseng0b809722017-11-03 10:23:26 -0700134 @Override
135 public void init(DeviceId deviceId, PipelinerContext context) {
136 this.deviceId = deviceId;
137 this.flowRuleService = context.directory().get(FlowRuleService.class);
138 this.groupService = context.directory().get(GroupService.class);
139 this.flowObjectiveStore = context.directory().get(FlowObjectiveStore.class);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800140 this.filteringTranslator = new FilteringObjectiveTranslator(deviceId, capabilities);
141 this.forwardingTranslator = new ForwardingObjectiveTranslator(deviceId, capabilities);
142 this.nextTranslator = new NextObjectiveTranslator(deviceId, capabilities);
Carmelo Casconedb347372021-05-26 19:30:30 +0200143 this.coreService = context.directory().get(CoreService.class);
144 this.appId = coreService.getAppId(FabricPipeconfLoader.PIPELINE_APP_NAME);
Carmelo Casconeffa7fed2021-06-01 18:31:57 -0700145
146 initializePipeline();
Carmelo Casconedb347372021-05-26 19:30:30 +0200147 }
148
149 protected void initializePipeline() {
150 // Set up rules for packet-out forwarding. We support only IPv4 routing.
Yi Tseng7a772872022-01-24 10:51:44 -0800151 final long cpuPort = capabilities.cpuPort().get();
Carmelo Casconedb347372021-05-26 19:30:30 +0200152 flowRuleService.applyFlowRules(
153 ingressVlanRule(cpuPort, false, DEFAULT_VLAN),
154 fwdClassifierRule(cpuPort, null, Ethernet.TYPE_IPV4, FWD_IPV4_ROUTING,
155 DEFAULT_FLOW_PRIORITY));
Yi Tseng0b809722017-11-03 10:23:26 -0700156 }
157
158 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800159 public void filter(FilteringObjective obj) {
160 final ObjectiveTranslation result = filteringTranslator.translate(obj);
161 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700162 }
163
164 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800165 public void forward(ForwardingObjective obj) {
166 final ObjectiveTranslation result = forwardingTranslator.translate(obj);
167 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700168 }
169
170 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800171 public void next(NextObjective obj) {
172 if (obj.op() == Objective.Operation.VERIFY) {
pierventre91eaff32021-06-14 20:28:35 +0200173 if (obj.type() != NextObjective.Type.HASHED) {
174 log.warn("VERIFY operation not yet supported for NextObjective {}, will return failure :(",
175 obj.type());
176 fail(obj, ObjectiveError.UNSUPPORTED);
177 return;
178 }
179
180 if (log.isTraceEnabled()) {
181 log.trace("Verify NextObjective {} in dev {}", obj, deviceId);
182 }
183 ObjectiveError error = handleVerify(obj);
184 if (error == null) {
185 success(obj);
186 } else {
187 fail(obj, error);
188 }
Yi Tseng1b154bd2017-11-20 17:48:19 -0800189 return;
190 }
191
pierventre4e02fb52020-08-13 16:35:15 +0200192 if (obj.op() == Objective.Operation.MODIFY && obj.type() != SIMPLE) {
193 log.warn("MODIFY operation not yet supported for NextObjective {}, will return failure :(",
194 obj.type());
195 if (log.isTraceEnabled()) {
196 log.trace("Objective {}", obj);
197 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800198 fail(obj, ObjectiveError.UNSUPPORTED);
Charles Chan91ea9722018-08-30 15:56:32 -0700199 return;
200 }
201
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800202 final ObjectiveTranslation result = nextTranslator.translate(obj);
203 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700204 }
205
206 @Override
Daniele Moro607fd0b2021-07-19 22:39:22 +0200207 public void purgeAll(ApplicationId appId) {
208 flowRuleService.purgeFlowRules(deviceId, appId);
209 groupService.purgeGroupEntries(deviceId, appId);
210 // TODO: should we purge also the FlowObjectiveStore?
211 }
212
213 @Override
Yi Tseng0b809722017-11-03 10:23:26 -0700214 public List<String> getNextMappings(NextGroup nextGroup) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800215 final FabricNextGroup fabricNextGroup = KRYO.deserialize(nextGroup.data());
216 return fabricNextGroup.nextMappings().stream()
217 .map(m -> format("%s -> %s", fabricNextGroup.type(), m))
Yi Tseng1b154bd2017-11-20 17:48:19 -0800218 .collect(Collectors.toList());
Yi Tseng0b809722017-11-03 10:23:26 -0700219 }
220
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800221 private void handleResult(Objective obj, ObjectiveTranslation result) {
222 if (result.error().isPresent()) {
223 fail(obj, result.error().get());
224 return;
225 }
226 processGroups(obj, result.groups());
227 processFlows(obj, result.flowRules());
228 if (obj instanceof NextObjective) {
229 handleNextGroup((NextObjective) obj);
230 }
231 success(obj);
Yi Tseng0b809722017-11-03 10:23:26 -0700232 }
233
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800234 private void handleNextGroup(NextObjective obj) {
pierventre91eaff32021-06-14 20:28:35 +0200235 // FIXME SDFAB-250 ADD_TO and REMOVE_FROM should update the content
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800236 switch (obj.op()) {
237 case REMOVE:
238 removeNextGroup(obj);
239 break;
240 case ADD:
241 case ADD_TO_EXISTING:
242 case REMOVE_FROM_EXISTING:
243 case MODIFY:
244 putNextGroup(obj);
245 break;
246 case VERIFY:
247 break;
248 default:
249 log.error("Unknown NextObjective operation '{}'", obj.op());
250 }
251 }
252
253 private void processFlows(Objective objective, Collection<FlowRule> flowRules) {
Yi Tseng0b809722017-11-03 10:23:26 -0700254 if (flowRules.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800255 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700256 }
pierventre4e02fb52020-08-13 16:35:15 +0200257
258 if (log.isTraceEnabled()) {
259 log.trace("Objective {} -> Flows {}", objective, flowRules);
260 }
261
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800262 final FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
263 switch (objective.op()) {
264 case ADD:
265 case ADD_TO_EXISTING:
pierventre4e02fb52020-08-13 16:35:15 +0200266 case MODIFY:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800267 flowRules.forEach(ops::add);
268 break;
269 case REMOVE:
270 case REMOVE_FROM_EXISTING:
271 flowRules.forEach(ops::remove);
272 break;
273 default:
pierventre4e02fb52020-08-13 16:35:15 +0200274 log.warn("Unsupported Objective operation {}", objective.op());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800275 return;
wu914ed232018-10-23 11:19:53 +0800276 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800277 flowRuleService.apply(ops.build());
Yi Tseng0b809722017-11-03 10:23:26 -0700278 }
279
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800280 private void processGroups(Objective objective, Collection<GroupDescription> groups) {
Yi Tseng0b809722017-11-03 10:23:26 -0700281 if (groups.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800282 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700283 }
pierventre4e02fb52020-08-13 16:35:15 +0200284
285 if (log.isTraceEnabled()) {
286 log.trace("Objective {} -> Groups {}", objective, groups);
287 }
288
Yi Tseng0b809722017-11-03 10:23:26 -0700289 switch (objective.op()) {
290 case ADD:
291 groups.forEach(groupService::addGroup);
292 break;
293 case REMOVE:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800294 groups.forEach(group -> groupService.removeGroup(
295 deviceId, group.appCookie(), objective.appId()));
Yi Tseng0b809722017-11-03 10:23:26 -0700296 break;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800297 case ADD_TO_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800298 groups.forEach(group -> groupService.addBucketsToGroup(
299 deviceId, group.appCookie(), group.buckets(),
300 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700301 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800302 break;
303 case REMOVE_FROM_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800304 groups.forEach(group -> groupService.removeBucketsFromGroup(
305 deviceId, group.appCookie(), group.buckets(),
306 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700307 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800308 break;
pierventre4e02fb52020-08-13 16:35:15 +0200309 case MODIFY:
310 // Modify is only supported for simple next objective
311 // Replace group bucket directly
312 groups.forEach(group -> groupService.setBucketsForGroup(
313 deviceId, group.appCookie(), group.buckets(),
314 group.appCookie(), group.appId())
315 );
316 break;
Yi Tseng0b809722017-11-03 10:23:26 -0700317 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800318 log.warn("Unsupported Objective operation {}", objective.op());
Yi Tseng0b809722017-11-03 10:23:26 -0700319 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800320 }
Yi Tseng0b809722017-11-03 10:23:26 -0700321
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800322 private void fail(Objective objective, ObjectiveError error) {
323 CompletableFuture.runAsync(
324 () -> objective.context().ifPresent(
325 ctx -> ctx.onError(objective, error)), callbackExecutor);
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800326
Yi Tseng0b809722017-11-03 10:23:26 -0700327 }
328
Charles Chan91ea9722018-08-30 15:56:32 -0700329
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800330 private void success(Objective objective) {
331 CompletableFuture.runAsync(
332 () -> objective.context().ifPresent(
333 ctx -> ctx.onSuccess(objective)), callbackExecutor);
Yi Tseng0b809722017-11-03 10:23:26 -0700334 }
335
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800336 private void removeNextGroup(NextObjective obj) {
337 final NextGroup removed = flowObjectiveStore.removeNextGroup(obj.id());
338 if (removed == null) {
Carmelo Casconedb347372021-05-26 19:30:30 +0200339 log.debug("NextGroup {} was not found in FlowObjectiveStore", obj);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800340 }
Charles Chan91ea9722018-08-30 15:56:32 -0700341 }
342
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800343 private void putNextGroup(NextObjective obj) {
344 final List<String> nextMappings = obj.nextTreatments().stream()
345 .map(this::nextTreatmentToMappingString)
346 .filter(Objects::nonNull)
347 .collect(Collectors.toList());
348 final FabricNextGroup nextGroup = new FabricNextGroup(obj.type(), nextMappings);
349 flowObjectiveStore.putNextGroup(obj.id(), nextGroup);
350 }
351
352 private String nextTreatmentToMappingString(NextTreatment n) {
353 switch (n.type()) {
354 case TREATMENT:
355 final PortNumber p = outputPort(n);
356 return p == null ? "UNKNOWN"
357 : format("OUTPUT:%s", p.toString());
358 case ID:
359 final IdNextTreatment id = (IdNextTreatment) n;
360 return format("NEXT_ID:%d", id.nextId());
Yi Tseng0b809722017-11-03 10:23:26 -0700361 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800362 log.warn("Unknown NextTreatment type '{}'", n.type());
363 return "???";
Yi Tseng0b809722017-11-03 10:23:26 -0700364 }
Yi Tseng0b809722017-11-03 10:23:26 -0700365 }
366
Carmelo Casconedb347372021-05-26 19:30:30 +0200367 public FlowRule ingressVlanRule(long port, boolean vlanValid, int vlanId) {
368 final TrafficSelector selector = DefaultTrafficSelector.builder()
369 .add(Criteria.matchInPort(PortNumber.portNumber(port)))
370 .add(PiCriterion.builder()
371 .matchExact(FabricConstants.HDR_VLAN_IS_VALID, vlanValid ? ONE : ZERO)
372 .build())
373 .build();
374 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
375 .piTableAction(PiAction.builder()
376 .withId(vlanValid ? FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT
377 : FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
378 .withParameter(new PiActionParam(FabricConstants.VLAN_ID, vlanId))
Wailok Shumfac26d42021-06-18 17:30:08 +0800379 .withParameter(new PiActionParam(FabricConstants.PORT_TYPE, PORT_TYPE_INTERNAL))
Carmelo Casconedb347372021-05-26 19:30:30 +0200380 .build())
381 .build();
382 return DefaultFlowRule.builder()
383 .withSelector(selector)
384 .withTreatment(treatment)
385 .forTable(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)
386 .makePermanent()
387 .withPriority(DEFAULT_FLOW_PRIORITY)
388 .forDevice(deviceId)
389 .fromApp(appId)
390 .build();
391 }
392
Yi Tseng7a772872022-01-24 10:51:44 -0800393 public FlowRule fwdClassifierRule(long port, Short ethType, short ipEthType, byte fwdType, int priority) {
Carmelo Casconedb347372021-05-26 19:30:30 +0200394 final TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
395 .matchInPort(PortNumber.portNumber(port))
396 .matchPi(PiCriterion.builder()
397 .matchExact(FabricConstants.HDR_IP_ETH_TYPE, ipEthType)
398 .build());
399 if (ethType != null) {
400 selectorBuilder.matchEthType(ethType);
401 }
402 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
403 .piTableAction(PiAction.builder()
404 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
405 .withParameter(new PiActionParam(FabricConstants.FWD_TYPE, fwdType))
406 .build())
407 .build();
408 return DefaultFlowRule.builder()
409 .withSelector(selectorBuilder.build())
410 .withTreatment(treatment)
411 .forTable(FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER)
412 .makePermanent()
413 .withPriority(priority)
414 .forDevice(deviceId)
415 .fromApp(appId)
416 .build();
417 }
418
pierventre91eaff32021-06-14 20:28:35 +0200419 private ObjectiveError handleVerify(NextObjective nextObjective) {
420 Map<GroupBucket, FlowRule> bucketsToFlows = getBucketToFlowMapping(nextObjective);
421 if (bucketsToFlows.isEmpty() && !nextObjective.nextTreatments().isEmpty()) {
422 log.warn("VERIFY failed due to translation error, bucketsToFlows is empty");
423 return ObjectiveError.BADPARAMS;
424 }
425
426 if (log.isTraceEnabled()) {
427 log.trace("Mapping bucketsToFlows {} has been generated ", bucketsToFlows);
428 }
429
430 GroupKey groupKey = nextTranslator.getGroupKey(nextObjective);
431 if (groupKey == null) {
432 log.warn("VERIFY failed due to translation error, unable to determine group key");
433 return ObjectiveError.BADPARAMS;
434 }
435 Group groupFromStore = groupService.getGroup(deviceId, groupKey);
436 if (groupFromStore == null) {
437 log.warn("VERIFY failed due to missing group in the store");
438 return ObjectiveError.GROUPMISSING;
439 }
440
441 // Looking for duplicate buckets - remove them by using a set and comparing size after/before
442 Set<GroupBucket> bucketsFromStore = Sets.newHashSet(groupFromStore.buckets().buckets());
443 if (groupFromStore.buckets().buckets().size() > bucketsFromStore.size()) {
444 log.warn("Duplicated buckets detected in device:{}, nextId:{}, before-size" +
445 ":{} after-size:{} .. correcting", deviceId,
446 nextObjective.id(), groupFromStore.buckets().buckets().size(), bucketsFromStore.size());
447 final GroupBuckets bucketToSet = new GroupBuckets(Lists.newArrayList(bucketsFromStore));
448 groupService.setBucketsForGroup(deviceId, groupKey, bucketToSet, groupKey, nextObjective.appId());
449 // Forge temporary the group to avoid race condition with the store
450 groupFromStore = new DefaultGroup(groupFromStore.id(), deviceId, groupFromStore.type(), bucketToSet);
451 }
452
453 // Looking for buckets missing in the group but defined in the next
454 Map<GroupBucket, FlowRule> toAdd = Maps.newHashMap();
455 for (Map.Entry<GroupBucket, FlowRule> entry : bucketsToFlows.entrySet()) {
456 if (!groupFromStore.buckets().buckets().contains(entry.getKey())) {
457 toAdd.put(entry.getKey(), entry.getValue());
458 }
459 }
460
461 // Looking for buckets missing in the next but defined in the group
462 // FIXME SDFAB-250 we cannot remove associated egress flows
463 List<GroupBucket> toRemove = Lists.newArrayList();
464 groupFromStore.buckets().buckets().forEach(bucket -> {
465 if (!bucketsToFlows.containsKey(bucket)) {
466 toRemove.add(bucket);
467 }
468 });
469
470 if (!toAdd.isEmpty() || !toRemove.isEmpty()) {
471 log.warn("Mismatch detected in device:{}, nextId:{}, groupFromTranslation-size:{} " +
472 "groupFromStore-size:{} toAdd-size:{} toRemove-size: {} .. correcting",
473 deviceId, nextObjective.id(), bucketsToFlows.size(), groupFromStore.buckets().buckets().size(),
474 toAdd.size(), toRemove.size());
475 }
476
477 if (!toAdd.isEmpty()) {
478 if (log.isTraceEnabled()) {
479 log.trace("Adding missing buckets {} and flows {}", toAdd.keySet(), toAdd.values());
480 }
481 final FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
482 final FlowRule dummyFlow = getDummyFlow(nextObjective);
483 toAdd.values().stream()
484 .filter(flowRule -> !flowRule.equals(dummyFlow))
485 .forEach(ops::add);
486 final GroupBuckets bucketsToAdd = new GroupBuckets(Lists.newArrayList(toAdd.keySet()));
487 groupService.addBucketsToGroup(deviceId, groupKey, bucketsToAdd, groupKey, nextObjective.appId());
488 flowRuleService.apply(ops.build());
489 }
490
491 if (!toRemove.isEmpty()) {
492 if (log.isTraceEnabled()) {
493 log.trace("Removing stale buckets {}", toRemove);
494 }
495 final GroupBuckets bucketsToRemove = new GroupBuckets(toRemove);
496 groupService.removeBucketsFromGroup(deviceId, groupKey, bucketsToRemove, groupKey,
497 nextObjective.appId());
498 }
499
500 return null;
501 }
502
503 private Map<GroupBucket, FlowRule> getBucketToFlowMapping(NextObjective nextObjective) {
504 Map<GroupBucket, FlowRule> mapping = Maps.newHashMap();
505 NextObjective newNextObjective;
506 ObjectiveTranslation result;
507 FlowRule dummyFlow = getDummyFlow(nextObjective);
508 FlowRule egFlow;
509 GroupBucket groupBucket;
510 GroupDescription group;
511 for (NextTreatment nextTreatment : nextObjective.nextTreatments()) {
512 newNextObjective = DefaultNextObjective.builder()
513 .withId(nextObjective.id())
514 .withType(nextObjective.type())
515 .fromApp(nextObjective.appId())
516 .withMeta(nextObjective.meta())
517 .addTreatment(nextTreatment)
518 .verify();
519 result = nextTranslator.translate(newNextObjective);
520 if ((result.groups().isEmpty() && result.flowRules().isEmpty()) ||
521 result.groups().size() > 1) {
522 return Collections.emptyMap();
523 }
524 group = result.groups().iterator().next();
525 egFlow = result.flowRules().stream()
526 .filter(flowRule -> flowRule.table().equals(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN))
527 .findFirst()
528 .orElse(null);
529 if (group.buckets().buckets().isEmpty() || group.buckets().buckets().size() > 1) {
530 return Collections.emptyMap();
531 }
532 groupBucket = group.buckets().buckets().iterator().next();
533 if (egFlow == null) {
534 mapping.put(groupBucket, dummyFlow);
535 } else {
536 mapping.put(groupBucket, egFlow);
537 }
538 }
539 return mapping;
540 }
541
542 private FlowRule getDummyFlow(NextObjective nextObjective) {
543 return DefaultFlowRule.builder()
544 .forDevice(deviceId)
545 .forTable(0)
546 .fromApp(nextObjective.appId())
547 .withPriority(1)
548 .withSelector(DefaultTrafficSelector.emptySelector())
549 .makePermanent()
550 .build();
551 }
552
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800553 /**
554 * NextGroup implementation.
555 */
556 private static class FabricNextGroup implements NextGroup {
pierventre91eaff32021-06-14 20:28:35 +0200557 // FIXME SDFAB-250 they are not very useful nor technically correct
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800558 private final NextObjective.Type type;
559 private final List<String> nextMappings;
560
561 FabricNextGroup(NextObjective.Type type, List<String> nextMappings) {
Yi Tseng0b809722017-11-03 10:23:26 -0700562 this.type = type;
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800563 this.nextMappings = ImmutableList.copyOf(nextMappings);
Yi Tseng0b809722017-11-03 10:23:26 -0700564 }
565
Charles Chan91ea9722018-08-30 15:56:32 -0700566 NextObjective.Type type() {
Yi Tseng0b809722017-11-03 10:23:26 -0700567 return type;
568 }
569
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800570 Collection<String> nextMappings() {
571 return nextMappings;
Yi Tseng0b809722017-11-03 10:23:26 -0700572 }
573
574 @Override
575 public byte[] data() {
576 return KRYO.serialize(this);
577 }
578 }
Yi Tseng0b809722017-11-03 10:23:26 -0700579}