blob: 166ba7296026b1846824f013d18e5099639e3816 [file] [log] [blame]
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07003 *
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 */
16package org.onosproject.driver.pipeline;
17
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -070018import static java.util.concurrent.Executors.newScheduledThreadPool;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070019import static org.onlab.util.Tools.groupedThreads;
20import static org.slf4j.LoggerFactory.getLogger;
21
22import com.google.common.cache.Cache;
23import com.google.common.cache.CacheBuilder;
24import com.google.common.cache.RemovalCause;
25import com.google.common.cache.RemovalNotification;
26
Charles Chan5270ed02016-01-30 23:22:37 -080027import com.google.common.collect.ImmutableList;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070028import org.onlab.osgi.ServiceDirectory;
29import org.onlab.packet.Ethernet;
Charles Chan68aa62d2015-11-09 16:37:23 -080030import org.onlab.packet.MacAddress;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070031import org.onlab.packet.VlanId;
32import org.onlab.util.KryoNamespace;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.behaviour.NextGroup;
38import org.onosproject.net.behaviour.Pipeliner;
39import org.onosproject.net.behaviour.PipelinerContext;
40import org.onosproject.net.driver.AbstractHandlerBehaviour;
41import org.onosproject.net.flow.DefaultFlowRule;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.FlowRule;
45import org.onosproject.net.flow.FlowRuleOperations;
46import org.onosproject.net.flow.FlowRuleOperationsContext;
47import org.onosproject.net.flow.FlowRuleService;
48import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
50import org.onosproject.net.flow.criteria.Criteria;
51import org.onosproject.net.flow.criteria.Criterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080052import org.onosproject.net.flow.criteria.Criterion.Type;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070053import org.onosproject.net.flow.criteria.EthCriterion;
54import org.onosproject.net.flow.criteria.EthTypeCriterion;
55import org.onosproject.net.flow.criteria.IPCriterion;
Charles Chan188ebf52015-12-23 00:15:11 -080056import org.onosproject.net.flow.criteria.MplsBosCriterion;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070057import org.onosproject.net.flow.criteria.MplsCriterion;
58import org.onosproject.net.flow.criteria.PortCriterion;
59import org.onosproject.net.flow.criteria.VlanIdCriterion;
60import org.onosproject.net.flow.instructions.Instruction;
Saurav Das822c4e22015-10-23 10:51:11 -070061import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -050062import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Charles Chan68aa62d2015-11-09 16:37:23 -080063import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070064import org.onosproject.net.flowobjective.FilteringObjective;
65import org.onosproject.net.flowobjective.FlowObjectiveStore;
66import org.onosproject.net.flowobjective.ForwardingObjective;
67import org.onosproject.net.flowobjective.NextObjective;
68import org.onosproject.net.flowobjective.Objective;
69import org.onosproject.net.flowobjective.ObjectiveError;
70import org.onosproject.net.group.DefaultGroupBucket;
71import org.onosproject.net.group.DefaultGroupDescription;
72import org.onosproject.net.group.DefaultGroupKey;
73import org.onosproject.net.group.Group;
74import org.onosproject.net.group.GroupBucket;
75import org.onosproject.net.group.GroupBuckets;
76import org.onosproject.net.group.GroupDescription;
77import org.onosproject.net.group.GroupEvent;
78import org.onosproject.net.group.GroupKey;
79import org.onosproject.net.group.GroupListener;
80import org.onosproject.net.group.GroupService;
Saurav Das8a0732e2015-11-20 15:27:53 -080081import org.onosproject.store.serializers.KryoNamespaces;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070082import org.slf4j.Logger;
83
84import java.util.ArrayList;
85import java.util.Collection;
86import java.util.Collections;
87import java.util.List;
Sho SHIMIZU45906042016-01-13 23:05:54 -080088import java.util.Objects;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070089import java.util.Set;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070090import java.util.concurrent.ScheduledExecutorService;
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -070091import java.util.concurrent.ScheduledThreadPoolExecutor;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070092import java.util.concurrent.TimeUnit;
93import java.util.stream.Collectors;
94
95/**
96 * Driver for SPRING-OPEN pipeline.
97 */
98public class SpringOpenTTP extends AbstractHandlerBehaviour
99 implements Pipeliner {
100
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700101 /**
102 * GroupCheck delay.
103 */
104 private static final int CHECK_DELAY = 500;
105
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700106 // Default table ID - compatible with CpqD switch
107 private static final int TABLE_VLAN = 0;
108 private static final int TABLE_TMAC = 1;
109 private static final int TABLE_IPV4_UNICAST = 2;
110 private static final int TABLE_MPLS = 3;
Charles Chan68aa62d2015-11-09 16:37:23 -0800111 private static final int TABLE_DMAC = 4;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700112 private static final int TABLE_ACL = 5;
Charles Chan68aa62d2015-11-09 16:37:23 -0800113 private static final int TABLE_SMAC = 6;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700114
115 /**
116 * Set the default values. These variables will get overwritten based on the
117 * switch vendor type
118 */
119 protected int vlanTableId = TABLE_VLAN;
120 protected int tmacTableId = TABLE_TMAC;
121 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
122 protected int mplsTableId = TABLE_MPLS;
Charles Chan68aa62d2015-11-09 16:37:23 -0800123 protected int dstMacTableId = TABLE_DMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700124 protected int aclTableId = TABLE_ACL;
Charles Chan68aa62d2015-11-09 16:37:23 -0800125 protected int srcMacTableId = TABLE_SMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700126
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700127 private static final Logger log = getLogger(SpringOpenTTP.class);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700128
129 private ServiceDirectory serviceDirectory;
130 private FlowRuleService flowRuleService;
131 private CoreService coreService;
132 protected GroupService groupService;
133 protected FlowObjectiveStore flowObjectiveStore;
134 protected DeviceId deviceId;
135 private ApplicationId appId;
136
137 private Cache<GroupKey, NextObjective> pendingGroups;
138
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700139 private static final ScheduledExecutorService GROUP_CHECKER
140 = newScheduledThreadPool(2,
141 groupedThreads("onos/pipeliner",
142 "spring-open-%d", log));
143 static {
144 // ONOS-3579 workaround, let core threads die out on idle
145 if (GROUP_CHECKER instanceof ScheduledThreadPoolExecutor) {
146 ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) GROUP_CHECKER;
Ray Milkey3717e602018-02-01 13:49:47 -0800147 executor.setKeepAliveTime(CHECK_DELAY * 2L, TimeUnit.MILLISECONDS);
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700148 executor.allowCoreThreadTimeOut(true);
149 }
150 }
151
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700152 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das8a0732e2015-11-20 15:27:53 -0800153 .register(KryoNamespaces.API)
154 .register(GroupKey.class)
155 .register(DefaultGroupKey.class)
156 .register(TrafficTreatment.class)
157 .register(SpringOpenGroup.class)
Charles Chaneefdedf2016-05-23 16:45:45 -0700158 .build("SpringOpenTTP");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700159
160 @Override
161 public void init(DeviceId deviceId, PipelinerContext context) {
162 this.serviceDirectory = context.directory();
163 this.deviceId = deviceId;
164
165 pendingGroups = CacheBuilder
166 .newBuilder()
167 .expireAfterWrite(20, TimeUnit.SECONDS)
168 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
169 if (notification.getCause() == RemovalCause.EXPIRED) {
170 fail(notification.getValue(),
171 ObjectiveError.GROUPINSTALLATIONFAILED);
172 }
173 }).build();
174
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700175 coreService = serviceDirectory.get(CoreService.class);
176 flowRuleService = serviceDirectory.get(FlowRuleService.class);
177 groupService = serviceDirectory.get(GroupService.class);
178 flowObjectiveStore = context.store();
179
180 groupService.addListener(new InnerGroupListener());
181
182 appId = coreService
183 .registerApplication("org.onosproject.driver.SpringOpenTTP");
184
185 setTableMissEntries();
186 log.info("Spring Open TTP driver initialized");
187 }
188
189 @Override
190 public void filter(FilteringObjective filteringObjective) {
191 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
192 log.debug("processing PERMIT filter objective");
193 processFilter(filteringObjective,
194 filteringObjective.op() == Objective.Operation.ADD,
195 filteringObjective.appId());
196 } else {
197 log.debug("filter objective other than PERMIT not supported");
198 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
199 }
200 }
201
202 @Override
203 public void forward(ForwardingObjective fwd) {
204 Collection<FlowRule> rules;
205 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
206
207 rules = processForward(fwd);
208 switch (fwd.op()) {
209 case ADD:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800210 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700211 .forEach(flowBuilder::add);
212 break;
213 case REMOVE:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800214 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700215 .forEach(flowBuilder::remove);
216 break;
217 default:
218 fail(fwd, ObjectiveError.UNKNOWN);
219 log.warn("Unknown forwarding type {}", fwd.op());
220 }
221
222 flowRuleService.apply(flowBuilder
223 .build(new FlowRuleOperationsContext() {
224 @Override
225 public void onSuccess(FlowRuleOperations ops) {
226 pass(fwd);
Saurav Das8a0732e2015-11-20 15:27:53 -0800227 log.debug("Provisioned tables in {} successfully with "
228 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700229 }
230
231 @Override
232 public void onError(FlowRuleOperations ops) {
233 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700234 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800235 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700236 }
237 }));
238
239 }
240
241 @Override
242 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800243 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
244 switch (nextObjective.op()) {
245 case ADD:
sangho834e4b02015-05-01 09:38:25 -0700246 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800247 log.warn("Cannot add next {} that already exists in device {}",
248 nextObjective.id(), deviceId);
249 return;
250 }
251 log.debug("Processing NextObjective id{} in dev{} - add group",
252 nextObjective.id(), deviceId);
253 addGroup(nextObjective);
254 break;
255 case ADD_TO_EXISTING:
256 if (nextGroup != null) {
257 log.debug("Processing NextObjective id{} in dev{} - add bucket",
258 nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700259 addBucketToGroup(nextObjective);
260 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800261 log.warn("Cannot add to group that does not exist");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700262 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800263 break;
264 case REMOVE:
265 if (nextGroup == null) {
266 log.warn("Cannot remove next {} that does not exist in device {}",
267 nextObjective.id(), deviceId);
268 return;
269 }
270 log.debug("Processing NextObjective id{} in dev{} - remove group",
271 nextObjective.id(), deviceId);
272 removeGroup(nextObjective);
273 break;
274 case REMOVE_FROM_EXISTING:
275 if (nextGroup == null) {
276 log.warn("Cannot remove from next {} that does not exist in device {}",
277 nextObjective.id(), deviceId);
278 return;
279 }
280 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
281 nextObjective.id(), deviceId);
282 removeBucketFromGroup(nextObjective);
283 break;
284 default:
sangho834e4b02015-05-01 09:38:25 -0700285 log.warn("Unsupported operation {}", nextObjective.op());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700286 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700287 }
288
Daniele Moro06aac702021-07-19 22:39:22 +0200289 @Override
290 public void purgeAll(ApplicationId appId) {
291 flowRuleService.purgeFlowRules(deviceId, appId);
292 groupService.purgeGroupEntries(deviceId, appId);
293 }
294
sangho834e4b02015-05-01 09:38:25 -0700295 private void removeGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700296 log.debug("removeGroup in {}: for next objective id {}",
297 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700298 final GroupKey key = new DefaultGroupKey(
299 appKryo.serialize(nextObjective.id()));
300 groupService.removeGroup(deviceId, key, appId);
301 }
302
303 private void addGroup(NextObjective nextObjective) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700304 log.debug("addGroup with type{} for nextObjective id {}",
305 nextObjective.type(), nextObjective.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700306 List<GroupBucket> buckets;
sangho834e4b02015-05-01 09:38:25 -0700307 switch (nextObjective.type()) {
308 case SIMPLE:
sangho834e4b02015-05-01 09:38:25 -0700309 Collection<TrafficTreatment> treatments = nextObjective.next();
310 if (treatments.size() == 1) {
Saurav Das4ce45962015-11-24 23:21:05 -0800311 // Spring Open TTP converts simple nextObjective to flow-actions
312 // in a dummy group
313 TrafficTreatment treatment = nextObjective.next().iterator().next();
314 log.debug("Converting SIMPLE group for next objective id {} " +
315 "to {} flow-actions in device:{}", nextObjective.id(),
316 treatment.allInstructions().size(), deviceId);
317 flowObjectiveStore.putNextGroup(nextObjective.id(),
318 new SpringOpenGroup(null, treatment));
sangho834e4b02015-05-01 09:38:25 -0700319 }
320 break;
321 case HASHED:
Saurav Das8a0732e2015-11-20 15:27:53 -0800322 // we convert MPLS ECMP groups to flow-actions for a single
323 // bucket(output port).
324 boolean mplsEcmp = false;
325 if (nextObjective.meta() != null) {
326 for (Criterion c : nextObjective.meta().criteria()) {
327 if (c.type() == Type.MPLS_LABEL) {
328 mplsEcmp = true;
329 }
330 }
331 }
332 if (mplsEcmp) {
333 // covert to flow-actions in a dummy group by choosing the first bucket
334 log.debug("Converting HASHED group for next objective id {} " +
335 "to flow-actions in device:{}", nextObjective.id(),
336 deviceId);
337 TrafficTreatment treatment = nextObjective.next().iterator().next();
338 flowObjectiveStore.putNextGroup(nextObjective.id(),
339 new SpringOpenGroup(null, treatment));
340 } else {
341 // process as ECMP group
342 buckets = nextObjective
343 .next()
344 .stream()
Sho SHIMIZU07b3b0c2016-08-09 13:35:53 -0700345 .map(DefaultGroupBucket::createSelectGroupBucket)
Saurav Das8a0732e2015-11-20 15:27:53 -0800346 .collect(Collectors.toList());
347 if (!buckets.isEmpty()) {
348 final GroupKey key = new DefaultGroupKey(
349 appKryo.serialize(nextObjective.id()));
350 GroupDescription groupDescription = new DefaultGroupDescription(
351 deviceId,
352 GroupDescription.Type.SELECT,
353 new GroupBuckets(buckets),
354 key,
355 null,
356 nextObjective.appId());
357 log.debug("Creating HASHED group for next objective id {}"
358 + " in dev:{}", nextObjective.id(), deviceId);
359 pendingGroups.put(key, nextObjective);
360 groupService.addGroup(groupDescription);
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700361 verifyPendingGroupLater();
Saurav Das8a0732e2015-11-20 15:27:53 -0800362 }
sangho834e4b02015-05-01 09:38:25 -0700363 }
364 break;
365 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700366 buckets = nextObjective
367 .next()
368 .stream()
Sho SHIMIZU07b3b0c2016-08-09 13:35:53 -0700369 .map(DefaultGroupBucket::createAllGroupBucket)
Charles Chanc42e84e2015-10-20 16:24:19 -0700370 .collect(Collectors.toList());
371 if (!buckets.isEmpty()) {
372 final GroupKey key = new DefaultGroupKey(
373 appKryo.serialize(nextObjective
374 .id()));
375 GroupDescription groupDescription = new DefaultGroupDescription(
376 deviceId,
377 GroupDescription.Type.ALL,
378 new GroupBuckets(buckets),
379 key,
380 null,
381 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800382 log.debug("Creating BROADCAST group for next objective id {} "
383 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700384 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800385 groupService.addGroup(groupDescription);
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700386 verifyPendingGroupLater();
Charles Chanc42e84e2015-10-20 16:24:19 -0700387 }
388 break;
sangho834e4b02015-05-01 09:38:25 -0700389 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700390 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700391 fail(nextObjective, ObjectiveError.UNSUPPORTED);
392 log.warn("Unsupported next objective type {}", nextObjective.type());
393 break;
394 default:
395 fail(nextObjective, ObjectiveError.UNKNOWN);
396 log.warn("Unknown next objective type {}", nextObjective.type());
397 }
398 }
399
400 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700401 log.debug("addBucketToGroup in {}: for next objective id {}",
402 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700403 Collection<TrafficTreatment> treatments = nextObjective.next();
404 TrafficTreatment treatment = treatments.iterator().next();
405 final GroupKey key = new DefaultGroupKey(
406 appKryo.serialize(nextObjective
407 .id()));
408 Group group = groupService.getGroup(deviceId, key);
409 if (group == null) {
410 log.warn("Group is not found in {} for {}", deviceId, key);
411 return;
412 }
413 GroupBucket bucket;
414 if (group.type() == GroupDescription.Type.INDIRECT) {
415 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
416 } else if (group.type() == GroupDescription.Type.SELECT) {
417 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700418 } else if (group.type() == GroupDescription.Type.ALL) {
419 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700420 } else {
421 log.warn("Unsupported Group type {}", group.type());
422 return;
423 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700424 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700425 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
426 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700427 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
428 }
429
430 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700431 log.debug("removeBucketFromGroup in {}: for next objective id {}",
432 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700433 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
434 if (nextGroup != null) {
435 Collection<TrafficTreatment> treatments = nextObjective.next();
436 TrafficTreatment treatment = treatments.iterator().next();
437 final GroupKey key = new DefaultGroupKey(
438 appKryo.serialize(nextObjective
439 .id()));
440 Group group = groupService.getGroup(deviceId, key);
441 if (group == null) {
442 log.warn("Group is not found in {} for {}", deviceId, key);
443 return;
444 }
445 GroupBucket bucket;
446 if (group.type() == GroupDescription.Type.INDIRECT) {
447 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
448 } else if (group.type() == GroupDescription.Type.SELECT) {
449 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700450 } else if (group.type() == GroupDescription.Type.ALL) {
451 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700452 } else {
453 log.warn("Unsupported Group type {}", group.type());
454 return;
455 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700456 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700457 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
458 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700459 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
460 }
461 }
462
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700463 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
464 switch (fwd.flag()) {
465 case SPECIFIC:
466 return processSpecific(fwd);
467 case VERSATILE:
468 return processVersatile(fwd);
469 default:
470 fail(fwd, ObjectiveError.UNKNOWN);
471 log.warn("Unknown forwarding flag {}", fwd.flag());
472 }
473 return Collections.emptySet();
474 }
475
476 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800477 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700478 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700479 EthTypeCriterion ethType =
480 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
481 if (ethType == null) {
482 log.error("Versatile forwarding objective must include ethType");
483 fail(fwd, ObjectiveError.UNKNOWN);
484 return Collections.emptySet();
485 }
486
Saurav Das8a0732e2015-11-20 15:27:53 -0800487 if (fwd.treatment() == null && fwd.nextId() == null) {
488 log.error("VERSATILE forwarding objective needs next objective ID "
489 + "or treatment.");
490 return Collections.emptySet();
491 }
492 // emulation of ACL table (for versatile fwd objective) requires
493 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700494 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
495 .builder();
496 treatmentBuilder.wipeDeferred();
497
498 if (fwd.nextId() != null) {
499 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700500 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800501 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
502 if (soGroup.dummy) {
503 // need to convert to flow-actions
504 for (Instruction ins : soGroup.treatment.allInstructions()) {
505 treatmentBuilder.add(ins);
506 }
507 } else {
508 GroupKey key = soGroup.key;
509 Group group = groupService.getGroup(deviceId, key);
510 if (group == null) {
511 log.warn("The group left!");
512 fail(fwd, ObjectiveError.GROUPMISSING);
513 return Collections.emptySet();
514 }
515 treatmentBuilder.deferred().group(group.id());
516 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700517 }
sangho1e575652015-05-14 00:39:53 -0700518 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800519 }
520
521 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700522 if (fwd.treatment().allInstructions().size() == 1 &&
523 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
524 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
525 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan5270ed02016-01-30 23:22:37 -0800526 treatmentBuilder.popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800527 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800528 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800529 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700530 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800531 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800532 for (Instruction ins : fwd.treatment().allInstructions()) {
533 treatmentBuilder.add(ins);
534 }
Saurav Das822c4e22015-10-23 10:51:11 -0700535 }
sangho1e575652015-05-14 00:39:53 -0700536 }
537
sangho1e575652015-05-14 00:39:53 -0700538 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
539 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700540 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800541 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700542
543 if (fwd.permanent()) {
544 ruleBuilder.makePermanent();
545 } else {
546 ruleBuilder.makeTemporary(fwd.timeout());
547 }
548
549 ruleBuilder.forTable(aclTableId);
550 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700551 }
552
Charles Chan68aa62d2015-11-09 16:37:23 -0800553 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700554 TrafficSelector selector = fwd.selector();
555 EthTypeCriterion ethType = (EthTypeCriterion) selector
556 .getCriterion(Criterion.Type.ETH_TYPE);
557 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800558 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
559 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
560 return false;
561 }
562 return true;
563 }
564
565 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
566 TrafficSelector selector = fwd.selector();
567 EthCriterion ethDst = (EthCriterion) selector
568 .getCriterion(Criterion.Type.ETH_DST);
569 VlanIdCriterion vlanId = (VlanIdCriterion) selector
570 .getCriterion(Criterion.Type.VLAN_VID);
571 if (ethDst == null && vlanId == null) {
572 return false;
573 }
574 return true;
575 }
576
577 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800578 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
579 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800580 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
581 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
582
583 if (isEthTypeObj) {
584 return processEthTypeSpecificObjective(fwd);
585 } else if (isEthDstObj) {
586 return processEthDstSpecificObjective(fwd);
587 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700588 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800589 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700590 fail(fwd, ObjectiveError.UNSUPPORTED);
591 return Collections.emptySet();
592 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800593 }
594
595 protected Collection<FlowRule>
596 processEthTypeSpecificObjective(ForwardingObjective fwd) {
597 TrafficSelector selector = fwd.selector();
598 EthTypeCriterion ethType = (EthTypeCriterion) selector
599 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700600
601 TrafficSelector.Builder filteredSelectorBuilder =
602 DefaultTrafficSelector.builder();
603 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700604 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700605 filteredSelectorBuilder = filteredSelectorBuilder
606 .matchEthType(Ethernet.TYPE_IPV4)
607 .matchIPDst(((IPCriterion) selector
608 .getCriterion(Criterion.Type.IPV4_DST))
609 .ip());
610 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800611 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
612 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700613 } else {
614 filteredSelectorBuilder = filteredSelectorBuilder
615 .matchEthType(Ethernet.MPLS_UNICAST)
616 .matchMplsLabel(((MplsCriterion)
617 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
Charles Chan188ebf52015-12-23 00:15:11 -0800618 if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
619 filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion)
620 selector.getCriterion(Type.MPLS_BOS)).mplsBos());
621 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700622 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800623 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
624 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700625 }
626
627 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
628 .builder();
629 if (fwd.treatment() != null) {
630 for (Instruction i : fwd.treatment().allInstructions()) {
631 treatmentBuilder.add(i);
632 }
633 }
634
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700635 if (fwd.nextId() != null) {
636 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700637 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800638 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
639 if (soGroup.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800640 log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
641 + "in dev: {}", soGroup.treatment.allInstructions().size(),
642 fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800643 for (Instruction ins : soGroup.treatment.allInstructions()) {
644 treatmentBuilder.add(ins);
645 }
646 } else {
647 GroupKey key = soGroup.key;
648 Group group = groupService.getGroup(deviceId, key);
649 if (group == null) {
650 log.warn("The group left!");
651 fail(fwd, ObjectiveError.GROUPMISSING);
652 return Collections.emptySet();
653 }
654 treatmentBuilder.deferred().group(group.id());
655 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
Saurav Das4ce45962015-11-24 23:21:05 -0800656 + "for next:{} in dev: {}", group.id(), fwd.id(),
657 fwd.nextId(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700658 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700659 } else {
660 log.warn("processSpecific: No associated next objective object");
661 fail(fwd, ObjectiveError.GROUPMISSING);
662 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700663 }
664 }
665
666 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
667 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
668 .build();
669
670 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
671 .fromApp(fwd.appId()).withPriority(fwd.priority())
672 .forDevice(deviceId).withSelector(filteredSelector)
673 .withTreatment(treatment);
674
675 if (fwd.permanent()) {
676 ruleBuilder.makePermanent();
677 } else {
678 ruleBuilder.makeTemporary(fwd.timeout());
679 }
680
681 ruleBuilder.forTable(forTableId);
682 return Collections.singletonList(ruleBuilder.build());
683
684 }
685
Charles Chan68aa62d2015-11-09 16:37:23 -0800686 protected Collection<FlowRule>
687 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700688 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800689
690 // Build filtered selector
691 TrafficSelector selector = fwd.selector();
692 EthCriterion ethCriterion = (EthCriterion) selector
693 .getCriterion(Criterion.Type.ETH_DST);
694 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
695 .getCriterion(Criterion.Type.VLAN_VID);
696 TrafficSelector.Builder filteredSelectorBuilder =
697 DefaultTrafficSelector.builder();
698 // Do not match MacAddress for subnet broadcast entry
699 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
700 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800701 log.debug("processing L2 forwarding objective:{} in dev:{}",
702 fwd.id(), deviceId);
703 } else {
704 log.debug("processing L2 Broadcast forwarding objective:{} "
705 + "in dev:{} for vlan:{}",
706 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800707 }
708 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
709 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
710
711 // Build filtered treatment
712 TrafficTreatment.Builder treatmentBuilder =
713 DefaultTrafficTreatment.builder();
714 if (fwd.treatment() != null) {
715 treatmentBuilder.deferred();
716 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
717 }
718 if (fwd.nextId() != null) {
719 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
720 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800721 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
722 if (soGrp.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800723 log.debug("Adding {} flow-actions for fwd. obj. {} "
724 + "in dev: {}", soGrp.treatment.allInstructions().size(),
725 fwd.id(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800726 for (Instruction ins : soGrp.treatment.allInstructions()) {
Saurav Das4ce45962015-11-24 23:21:05 -0800727 treatmentBuilder.deferred().add(ins);
Saurav Das8a0732e2015-11-20 15:27:53 -0800728 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800729 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800730 GroupKey key = soGrp.key;
731 Group group = groupService.getGroup(deviceId, key);
732 if (group == null) {
733 log.warn("The group left!");
734 fail(fwd, ObjectiveError.GROUPMISSING);
735 return Collections.emptySet();
736 }
737 treatmentBuilder.deferred().group(group.id());
738 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
739 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800740 }
741 }
742 }
743 treatmentBuilder.immediate().transition(aclTableId);
744 TrafficTreatment filteredTreatment = treatmentBuilder.build();
745
746 // Build bridging table entries
747 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
748 flowRuleBuilder.fromApp(fwd.appId())
749 .withPriority(fwd.priority())
750 .forDevice(deviceId)
751 .withSelector(filteredSelector)
752 .withTreatment(filteredTreatment)
753 .forTable(dstMacTableId);
754 if (fwd.permanent()) {
755 flowRuleBuilder.makePermanent();
756 } else {
757 flowRuleBuilder.makeTemporary(fwd.timeout());
758 }
759 rules.add(flowRuleBuilder.build());
760
761 /*
762 // TODO Emulate source MAC table behavior
763 // Do not install source MAC table entry for subnet broadcast
764 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
765 // Build filtered selector
766 selector = fwd.selector();
767 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
768 filteredSelectorBuilder = DefaultTrafficSelector.builder();
769 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
770 filteredSelector = filteredSelectorBuilder.build();
771
772 // Build empty treatment. Apply existing instruction if match.
773 treatmentBuilder = DefaultTrafficTreatment.builder();
774 filteredTreatment = treatmentBuilder.build();
775
776 // Build bridging table entries
777 flowRuleBuilder = DefaultFlowRule.builder();
778 flowRuleBuilder.fromApp(fwd.appId())
779 .withPriority(fwd.priority())
780 .forDevice(deviceId)
781 .withSelector(filteredSelector)
782 .withTreatment(filteredTreatment)
783 .forTable(srcMacTableId)
784 .makePermanent();
785 rules.add(flowRuleBuilder.build());
786 }
787 */
788
789 return rules;
790 }
791
Saurav Das4ce45962015-11-24 23:21:05 -0800792 /*
793 * Note: CpqD switches do not handle MPLS-related operation properly
794 * for a packet with VLAN tag. We pop VLAN here as a workaround.
795 * Side effect: HostService learns redundant hosts with same MAC but
796 * different VLAN. No known side effect on the network reachability.
797 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800798 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
799 VlanIdCriterion vlanIdCriterion,
800 FilteringObjective filt,
801 VlanId assignedVlan,
802 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800803 if (vlanIdCriterion == null) {
804 return processEthDstOnlyFilter(ethCriterion, applicationId, filt.priority());
805 }
806
Charles Chan68aa62d2015-11-09 16:37:23 -0800807 //handling untagged packets via assigned VLAN
808 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
809 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
810 }
811
Charles Chan68aa62d2015-11-09 16:37:23 -0800812 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700813 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
814 .builder();
815 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
816 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800817 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700818 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800819 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
820 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700821 treatmentIp.transition(ipv4UnicastTableId);
822 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
823 .withSelector(selectorIp.build())
824 .withTreatment(treatmentIp.build())
825 .withPriority(filt.priority()).fromApp(applicationId)
826 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800827 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700828 rules.add(ruleIp);
829
830 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
831 .builder();
832 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
833 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800834 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700835 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800836 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
837 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700838 treatmentMpls.transition(mplsTableId);
839 FlowRule ruleMpls = DefaultFlowRule.builder()
840 .forDevice(deviceId).withSelector(selectorMpls.build())
841 .withTreatment(treatmentMpls.build())
842 .withPriority(filt.priority()).fromApp(applicationId)
843 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800844 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700845 rules.add(ruleMpls);
846
847 return rules;
848 }
849
Charles Chan5270ed02016-01-30 23:22:37 -0800850 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
851 ApplicationId applicationId, int priority) {
852 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
853 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
854 selector.matchEthType(Ethernet.TYPE_IPV4);
855 selector.matchEthDst(ethCriterion.mac());
856 treatment.transition(TABLE_IPV4_UNICAST);
857 FlowRule rule = DefaultFlowRule.builder()
858 .forDevice(deviceId)
859 .withSelector(selector.build())
860 .withTreatment(treatment.build())
861 .withPriority(priority)
862 .fromApp(applicationId)
863 .makePermanent()
864 .forTable(TABLE_TMAC).build();
865 return ImmutableList.<FlowRule>builder().add(rule).build();
866 }
867
Charles Chan68aa62d2015-11-09 16:37:23 -0800868 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700869 FilteringObjective filt,
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500870 VlanId assignedVlan, VlanId explicitlyAssignedVlan, VlanId pushedVlan,
871 boolean pushVlan, boolean popVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700872 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700873 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800874 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700875 TrafficSelector.Builder selector = DefaultTrafficSelector
876 .builder();
877 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
878 .builder();
879 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800880 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
881 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700882 selector.matchInPort(p.port());
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500883 if (popVlan) {
884 // Pop outer tag
885 treatment.immediate().popVlan();
886 }
887 if (explicitlyAssignedVlan != null && (!popVlan || !vlanIdCriterion.vlanId().equals(assignedVlan))) {
888 // Modify VLAN ID on single tagged packet or modify remaining tag after popping
889 // In the first case, do not set VLAN ID again to the already existing value
890 treatment.immediate().setVlanId(explicitlyAssignedVlan);
891 }
892 if (pushVlan) {
893 // Push new tag
894 treatment.immediate().pushVlan().setVlanId(pushedVlan);
895 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800896 } else {
897 selector.matchInPort(p.port());
898 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700899 }
900 treatment.transition(tmacTableId);
901 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
902 .withSelector(selector.build())
903 .withTreatment(treatment.build())
904 .withPriority(filt.priority()).fromApp(applicationId)
905 .makePermanent().forTable(vlanTableId).build();
906 rules.add(rule);
907
908 return rules;
909 }
910
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700911 private void processFilter(FilteringObjective filt, boolean install,
912 ApplicationId applicationId) {
913 // This driver only processes filtering criteria defined with switch
914 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700915 if (filt.key().equals(Criteria.dummy())
916 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700917 log.warn("No key defined in filtering objective from app: {}. Not"
918 + "processing filtering objective", applicationId);
919 fail(filt, ObjectiveError.UNKNOWN);
920 return;
921 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800922
923 EthCriterion ethCriterion = null;
924 VlanIdCriterion vlanIdCriterion = null;
925
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700926 // convert filtering conditions for switch-intfs into flowrules
927 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800928
929 for (Criterion criterion : filt.conditions()) {
930 if (criterion.type() == Criterion.Type.ETH_DST) {
931 ethCriterion = (EthCriterion) criterion;
932 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
933 vlanIdCriterion = (VlanIdCriterion) criterion;
934 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700935 log.debug("driver does not process IP filtering rules as it " +
936 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700937 } else {
938 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800939 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700940 fail(filt, ObjectiveError.UNSUPPORTED);
941 }
942 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800943
944 VlanId assignedVlan = null;
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500945 VlanId modifiedVlan = null;
946 VlanId pushedVlan = null;
947 boolean pushVlan = false;
948 boolean popVlan = false;
Charles Chane849c192016-01-11 18:28:54 -0800949 if (vlanIdCriterion != null) {
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500950 if (filt.meta() != null) {
951 for (Instruction i : filt.meta().allInstructions()) {
952 if (i instanceof L2ModificationInstruction) {
953 if (((L2ModificationInstruction) i).subtype()
954 .equals(L2ModificationInstruction.L2SubType.VLAN_PUSH)) {
955 pushVlan = true;
956 } else if (((L2ModificationInstruction) i).subtype()
957 .equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
958 if (modifiedVlan != null) {
959 log.error("Pop tag is not allowed after modify VLAN operation " +
960 "in filtering objective", deviceId);
961 fail(filt, ObjectiveError.BADPARAMS);
962 return;
963 }
964 popVlan = true;
965 }
966 }
967 if (i instanceof ModVlanIdInstruction) {
968 if (pushVlan && vlanIdCriterion.vlanId() != VlanId.NONE) {
969 // Modify VLAN should not appear after pushing a new tag
970 if (pushedVlan != null) {
971 log.error("Modify VLAN not allowed after push tag operation " +
972 "in filtering objective", deviceId);
973 fail(filt, ObjectiveError.BADPARAMS);
974 return;
975 }
976 pushedVlan = ((ModVlanIdInstruction) i).vlanId();
977 } else if (vlanIdCriterion.vlanId() == VlanId.NONE) {
978 // For untagged packets the pushed VLAN ID will be saved in assignedVlan
979 // just to ensure the driver works as designed for the fabric use case
980 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
981 } else {
982 // For tagged packets modifiedVlan will contain the modified value of existing tag
983 if (modifiedVlan != null) {
984 log.error("Driver does not allow multiple modify VLAN operations " +
985 "in the same filtering objective", deviceId);
986 fail(filt, ObjectiveError.BADPARAMS);
987 return;
988 }
989 modifiedVlan = ((ModVlanIdInstruction) i).vlanId();
990 }
991 }
992 }
993 }
Charles Chane849c192016-01-11 18:28:54 -0800994
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500995 // For VLAN cross-connect packets, use the configured VLAN unless there is an explicitly provided VLAN ID
996 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
997 if (assignedVlan == null) {
998 assignedVlan = vlanIdCriterion.vlanId();
999 }
Charles Chane849c192016-01-11 18:28:54 -08001000 // For untagged packets, assign a VLAN ID
1001 } else {
1002 if (filt.meta() == null) {
1003 log.error("Missing metadata in filtering objective required " +
1004 "for vlan assignment in dev {}", deviceId);
1005 fail(filt, ObjectiveError.BADPARAMS);
1006 return;
Charles Chan68aa62d2015-11-09 16:37:23 -08001007 }
Charles Chane849c192016-01-11 18:28:54 -08001008 if (assignedVlan == null) {
1009 log.error("Driver requires an assigned vlan-id to tag incoming "
1010 + "untagged packets. Not processing vlan filters on "
1011 + "device {}", deviceId);
1012 fail(filt, ObjectiveError.BADPARAMS);
1013 return;
1014 }
Charles Chan68aa62d2015-11-09 16:37:23 -08001015 }
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -05001016 if (pushVlan && popVlan) {
1017 log.error("Cannot push and pop vlan in the same filtering objective");
1018 fail(filt, ObjectiveError.BADPARAMS);
1019 return;
1020 }
1021 if (popVlan && vlanIdCriterion.vlanId() == VlanId.NONE) {
1022 log.error("Cannot pop vlan for untagged packets");
1023 fail(filt, ObjectiveError.BADPARAMS);
1024 return;
1025 }
1026 if ((pushVlan && pushedVlan == null) && vlanIdCriterion.vlanId() != VlanId.NONE) {
1027 log.error("No VLAN ID provided for push tag operation");
1028 fail(filt, ObjectiveError.BADPARAMS);
1029 return;
1030 }
Charles Chan68aa62d2015-11-09 16:37:23 -08001031 }
1032
1033 if (ethCriterion == null) {
1034 log.debug("filtering objective missing dstMac, cannot program TMAC table");
1035 } else {
1036 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
1037 vlanIdCriterion,
1038 filt,
1039 assignedVlan,
1040 applicationId)) {
1041 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
1042 tmacRule, deviceId);
1043 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
1044 }
1045 }
1046
Charles Chane849c192016-01-11 18:28:54 -08001047 if (vlanIdCriterion == null) {
1048 log.debug("filtering objective missing VLAN ID criterion, "
1049 + "cannot program VLAN Table");
Charles Chan68aa62d2015-11-09 16:37:23 -08001050 } else {
1051 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
1052 filt,
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -05001053 assignedVlan, modifiedVlan, pushedVlan,
1054 pushVlan, popVlan,
Charles Chan68aa62d2015-11-09 16:37:23 -08001055 applicationId)) {
1056 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
1057 vlanRule, deviceId);
1058 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
1059 }
1060 }
1061
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001062 // apply filtering flow rules
1063 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1064 @Override
1065 public void onSuccess(FlowRuleOperations ops) {
1066 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001067 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -08001068 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001069 }
1070
1071 @Override
1072 public void onError(FlowRuleOperations ops) {
1073 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001074 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -08001075 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001076 }
1077 }));
1078 }
1079
1080 protected void setTableMissEntries() {
1081 // set all table-miss-entries
1082 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -08001083 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
1084 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001085 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -08001086 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001087 populateTableMissEntry(aclTableId, false, false, false, -1);
1088 }
1089
1090 protected void populateTableMissEntry(int tableToAdd,
1091 boolean toControllerNow,
1092 boolean toControllerWrite,
1093 boolean toTable, int tableToSend) {
1094 TrafficSelector selector = DefaultTrafficSelector.builder().build();
1095 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1096
1097 if (toControllerNow) {
1098 tBuilder.setOutput(PortNumber.CONTROLLER);
1099 }
1100
1101 if (toControllerWrite) {
1102 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
1103 }
1104
1105 if (toTable) {
1106 tBuilder.transition(tableToSend);
1107 }
1108
1109 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
1110 .withSelector(selector).withTreatment(tBuilder.build())
1111 .withPriority(0).fromApp(appId).makePermanent()
1112 .forTable(tableToAdd).build();
1113
1114 flowRuleService.applyFlowRules(flow);
1115 }
1116
1117 private void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001118 obj.context().ifPresent(context -> context.onSuccess(obj));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001119 }
1120
1121 protected void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001122 obj.context().ifPresent(context -> context.onError(obj, error));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001123 }
1124
1125 private class InnerGroupListener implements GroupListener {
1126 @Override
1127 public void event(GroupEvent event) {
1128 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001129 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001130 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001131 GroupKey key = event.subject().appCookie();
1132
1133 NextObjective obj = pendingGroups.getIfPresent(key);
1134 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001135 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1136 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001137 flowObjectiveStore
1138 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001139 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001140 pass(obj);
1141 pendingGroups.invalidate(key);
1142 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001143 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1144 log.warn("InnerGroupListener: Group ADD "
1145 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001146 }
1147 }
1148 }
1149
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -07001150 private void verifyPendingGroupLater() {
1151 GROUP_CHECKER.schedule(new GroupChecker(), CHECK_DELAY, TimeUnit.MILLISECONDS);
1152 }
1153
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001154 private class GroupChecker implements Runnable {
1155
1156 @Override
1157 public void run() {
1158 Set<GroupKey> keys = pendingGroups
1159 .asMap()
1160 .keySet()
1161 .stream()
1162 .filter(key -> groupService.getGroup(deviceId, key) != null)
1163 .collect(Collectors.toSet());
1164
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -07001165 keys.forEach(key -> {
1166 NextObjective obj = pendingGroups
1167 .getIfPresent(key);
1168 if (obj == null) {
1169 return;
1170 }
1171 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1172 deviceId,
1173 groupService.getGroup(deviceId, key).id(),
1174 obj.id());
1175 pass(obj);
1176 pendingGroups.invalidate(key);
1177 flowObjectiveStore.putNextGroup(
1178 obj.id(),
1179 new SpringOpenGroup(key, null));
1180 });
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -07001181
1182 if (!pendingGroups.asMap().isEmpty()) {
1183 // Periodically execute only if entry remains in pendingGroups.
1184 // Iterating pendingGroups trigger cleanUp and expiration,
1185 // which will eventually empty the pendingGroups.
1186 verifyPendingGroupLater();
1187 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001188 }
1189 }
1190
Saurav Das8a0732e2015-11-20 15:27:53 -08001191 /**
1192 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1193 * used to fetch the group from the Group Service, or it can be serve as storage
1194 * for Traffic Treatments which can be used as flow actions. In the latter
1195 * case, we refer to this as a dummy group.
1196 *
1197 */
Charles Chane3ba6952016-05-02 11:02:36 -07001198 protected class SpringOpenGroup implements NextGroup {
Saurav Das8a0732e2015-11-20 15:27:53 -08001199 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001200 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001201 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001202
Saurav Das8a0732e2015-11-20 15:27:53 -08001203 /**
1204 * Storage for a GroupKey or a TrafficTreatment. One of the params
1205 * to this constructor must be null.
1206 * @param key represents a GroupKey
1207 * @param treatment represents flow actions in a dummy group
1208 */
1209 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1210 if (key == null) {
1211 this.key = new DefaultGroupKey(new byte[]{0});
1212 this.treatment = treatment;
1213 this.dummy = true;
1214 } else {
1215 this.key = key;
1216 this.treatment = DefaultTrafficTreatment.builder().build();
1217 this.dummy = false;
1218 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001219 }
1220
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001221 public GroupKey key() {
1222 return key;
1223 }
1224
Saurav Das77621792016-05-03 16:36:57 -07001225 public boolean dummy() {
1226 return dummy;
1227 }
1228
1229 public TrafficTreatment treatment() {
1230 return treatment;
1231 }
1232
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001233 @Override
1234 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001235 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001236 }
1237
1238 }
Saurav Das24431192016-03-07 19:13:00 -08001239
1240 @Override
1241 public List<String> getNextMappings(NextGroup nextGroup) {
1242 // TODO Implementation deferred to vendor
1243 return null;
1244 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001245}