blob: 8ac5eec8ad6f2e0ce7779397e15b98d570ecdc67 [file] [log] [blame]
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001/*
2 * Copyright 2015 Open Networking Laboratory
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 */
16package org.onosproject.driver.pipeline;
17
18import static org.onlab.util.Tools.groupedThreads;
19import static org.slf4j.LoggerFactory.getLogger;
20
21import com.google.common.cache.Cache;
22import com.google.common.cache.CacheBuilder;
23import com.google.common.cache.RemovalCause;
24import com.google.common.cache.RemovalNotification;
25
26import org.onlab.osgi.ServiceDirectory;
27import org.onlab.packet.Ethernet;
Charles Chan68aa62d2015-11-09 16:37:23 -080028import org.onlab.packet.MacAddress;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070029import org.onlab.packet.VlanId;
30import org.onlab.util.KryoNamespace;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.behaviour.NextGroup;
36import org.onosproject.net.behaviour.Pipeliner;
37import org.onosproject.net.behaviour.PipelinerContext;
38import org.onosproject.net.driver.AbstractHandlerBehaviour;
39import org.onosproject.net.flow.DefaultFlowRule;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.FlowRule;
43import org.onosproject.net.flow.FlowRuleOperations;
44import org.onosproject.net.flow.FlowRuleOperationsContext;
45import org.onosproject.net.flow.FlowRuleService;
46import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.flow.criteria.Criteria;
49import org.onosproject.net.flow.criteria.Criterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080050import org.onosproject.net.flow.criteria.Criterion.Type;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070051import org.onosproject.net.flow.criteria.EthCriterion;
52import org.onosproject.net.flow.criteria.EthTypeCriterion;
53import org.onosproject.net.flow.criteria.IPCriterion;
54import org.onosproject.net.flow.criteria.MplsCriterion;
55import org.onosproject.net.flow.criteria.PortCriterion;
56import org.onosproject.net.flow.criteria.VlanIdCriterion;
57import org.onosproject.net.flow.instructions.Instruction;
Saurav Das822c4e22015-10-23 10:51:11 -070058import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chan68aa62d2015-11-09 16:37:23 -080059import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070060import org.onosproject.net.flowobjective.FilteringObjective;
61import org.onosproject.net.flowobjective.FlowObjectiveStore;
62import org.onosproject.net.flowobjective.ForwardingObjective;
63import org.onosproject.net.flowobjective.NextObjective;
64import org.onosproject.net.flowobjective.Objective;
65import org.onosproject.net.flowobjective.ObjectiveError;
66import org.onosproject.net.group.DefaultGroupBucket;
67import org.onosproject.net.group.DefaultGroupDescription;
68import org.onosproject.net.group.DefaultGroupKey;
69import org.onosproject.net.group.Group;
70import org.onosproject.net.group.GroupBucket;
71import org.onosproject.net.group.GroupBuckets;
72import org.onosproject.net.group.GroupDescription;
73import org.onosproject.net.group.GroupEvent;
74import org.onosproject.net.group.GroupKey;
75import org.onosproject.net.group.GroupListener;
76import org.onosproject.net.group.GroupService;
Saurav Das8a0732e2015-11-20 15:27:53 -080077import org.onosproject.store.serializers.KryoNamespaces;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070078import org.slf4j.Logger;
79
80import java.util.ArrayList;
81import java.util.Collection;
82import java.util.Collections;
83import java.util.List;
84import java.util.Set;
85import java.util.concurrent.Executors;
86import java.util.concurrent.ScheduledExecutorService;
87import java.util.concurrent.TimeUnit;
88import java.util.stream.Collectors;
89
90/**
91 * Driver for SPRING-OPEN pipeline.
92 */
93public class SpringOpenTTP extends AbstractHandlerBehaviour
94 implements Pipeliner {
95
96 // Default table ID - compatible with CpqD switch
97 private static final int TABLE_VLAN = 0;
98 private static final int TABLE_TMAC = 1;
99 private static final int TABLE_IPV4_UNICAST = 2;
100 private static final int TABLE_MPLS = 3;
Charles Chan68aa62d2015-11-09 16:37:23 -0800101 private static final int TABLE_DMAC = 4;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700102 private static final int TABLE_ACL = 5;
Charles Chan68aa62d2015-11-09 16:37:23 -0800103 private static final int TABLE_SMAC = 6;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700104
105 /**
106 * Set the default values. These variables will get overwritten based on the
107 * switch vendor type
108 */
109 protected int vlanTableId = TABLE_VLAN;
110 protected int tmacTableId = TABLE_TMAC;
111 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
112 protected int mplsTableId = TABLE_MPLS;
Charles Chan68aa62d2015-11-09 16:37:23 -0800113 protected int dstMacTableId = TABLE_DMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700114 protected int aclTableId = TABLE_ACL;
Charles Chan68aa62d2015-11-09 16:37:23 -0800115 protected int srcMacTableId = TABLE_SMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700116
117 protected final Logger log = getLogger(getClass());
118
119 private ServiceDirectory serviceDirectory;
120 private FlowRuleService flowRuleService;
121 private CoreService coreService;
122 protected GroupService groupService;
123 protected FlowObjectiveStore flowObjectiveStore;
124 protected DeviceId deviceId;
125 private ApplicationId appId;
126
127 private Cache<GroupKey, NextObjective> pendingGroups;
128
129 private ScheduledExecutorService groupChecker = Executors
130 .newScheduledThreadPool(2,
131 groupedThreads("onos/pipeliner",
132 "spring-open-%d"));
133 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das8a0732e2015-11-20 15:27:53 -0800134 .register(KryoNamespaces.API)
135 .register(GroupKey.class)
136 .register(DefaultGroupKey.class)
137 .register(TrafficTreatment.class)
138 .register(SpringOpenGroup.class)
139 .register(byte[].class)
140 .build();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700141
142 @Override
143 public void init(DeviceId deviceId, PipelinerContext context) {
144 this.serviceDirectory = context.directory();
145 this.deviceId = deviceId;
146
147 pendingGroups = CacheBuilder
148 .newBuilder()
149 .expireAfterWrite(20, TimeUnit.SECONDS)
150 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
151 if (notification.getCause() == RemovalCause.EXPIRED) {
152 fail(notification.getValue(),
153 ObjectiveError.GROUPINSTALLATIONFAILED);
154 }
155 }).build();
156
157 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500,
158 TimeUnit.MILLISECONDS);
159
160 coreService = serviceDirectory.get(CoreService.class);
161 flowRuleService = serviceDirectory.get(FlowRuleService.class);
162 groupService = serviceDirectory.get(GroupService.class);
163 flowObjectiveStore = context.store();
164
165 groupService.addListener(new InnerGroupListener());
166
167 appId = coreService
168 .registerApplication("org.onosproject.driver.SpringOpenTTP");
169
170 setTableMissEntries();
171 log.info("Spring Open TTP driver initialized");
172 }
173
174 @Override
175 public void filter(FilteringObjective filteringObjective) {
176 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
177 log.debug("processing PERMIT filter objective");
178 processFilter(filteringObjective,
179 filteringObjective.op() == Objective.Operation.ADD,
180 filteringObjective.appId());
181 } else {
182 log.debug("filter objective other than PERMIT not supported");
183 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
184 }
185 }
186
187 @Override
188 public void forward(ForwardingObjective fwd) {
189 Collection<FlowRule> rules;
190 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
191
192 rules = processForward(fwd);
193 switch (fwd.op()) {
194 case ADD:
195 rules.stream().filter(rule -> rule != null)
196 .forEach(flowBuilder::add);
197 break;
198 case REMOVE:
199 rules.stream().filter(rule -> rule != null)
200 .forEach(flowBuilder::remove);
201 break;
202 default:
203 fail(fwd, ObjectiveError.UNKNOWN);
204 log.warn("Unknown forwarding type {}", fwd.op());
205 }
206
207 flowRuleService.apply(flowBuilder
208 .build(new FlowRuleOperationsContext() {
209 @Override
210 public void onSuccess(FlowRuleOperations ops) {
211 pass(fwd);
Saurav Das8a0732e2015-11-20 15:27:53 -0800212 log.debug("Provisioned tables in {} successfully with "
213 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700214 }
215
216 @Override
217 public void onError(FlowRuleOperations ops) {
218 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700219 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800220 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700221 }
222 }));
223
224 }
225
226 @Override
227 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800228 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
229 switch (nextObjective.op()) {
230 case ADD:
sangho834e4b02015-05-01 09:38:25 -0700231 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800232 log.warn("Cannot add next {} that already exists in device {}",
233 nextObjective.id(), deviceId);
234 return;
235 }
236 log.debug("Processing NextObjective id{} in dev{} - add group",
237 nextObjective.id(), deviceId);
238 addGroup(nextObjective);
239 break;
240 case ADD_TO_EXISTING:
241 if (nextGroup != null) {
242 log.debug("Processing NextObjective id{} in dev{} - add bucket",
243 nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700244 addBucketToGroup(nextObjective);
245 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800246 log.warn("Cannot add to group that does not exist");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700247 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800248 break;
249 case REMOVE:
250 if (nextGroup == null) {
251 log.warn("Cannot remove next {} that does not exist in device {}",
252 nextObjective.id(), deviceId);
253 return;
254 }
255 log.debug("Processing NextObjective id{} in dev{} - remove group",
256 nextObjective.id(), deviceId);
257 removeGroup(nextObjective);
258 break;
259 case REMOVE_FROM_EXISTING:
260 if (nextGroup == null) {
261 log.warn("Cannot remove from next {} that does not exist in device {}",
262 nextObjective.id(), deviceId);
263 return;
264 }
265 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
266 nextObjective.id(), deviceId);
267 removeBucketFromGroup(nextObjective);
268 break;
269 default:
sangho834e4b02015-05-01 09:38:25 -0700270 log.warn("Unsupported operation {}", nextObjective.op());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700271 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700272 }
273
sangho834e4b02015-05-01 09:38:25 -0700274 private void removeGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700275 log.debug("removeGroup in {}: for next objective id {}",
276 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700277 final GroupKey key = new DefaultGroupKey(
278 appKryo.serialize(nextObjective.id()));
279 groupService.removeGroup(deviceId, key, appId);
280 }
281
282 private void addGroup(NextObjective nextObjective) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700283 log.debug("addGroup with type{} for nextObjective id {}",
284 nextObjective.type(), nextObjective.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700285 List<GroupBucket> buckets;
sangho834e4b02015-05-01 09:38:25 -0700286 switch (nextObjective.type()) {
287 case SIMPLE:
sangho834e4b02015-05-01 09:38:25 -0700288 Collection<TrafficTreatment> treatments = nextObjective.next();
289 if (treatments.size() == 1) {
290 TrafficTreatment treatment = treatments.iterator().next();
291 GroupBucket bucket = DefaultGroupBucket
292 .createIndirectGroupBucket(treatment);
293 final GroupKey key = new DefaultGroupKey(
294 appKryo.serialize(nextObjective
295 .id()));
296 GroupDescription groupDescription = new DefaultGroupDescription(
297 deviceId,
298 GroupDescription.Type.INDIRECT,
299 new GroupBuckets(
300 Collections.singletonList(bucket)),
301 key,
Saurav Das100e3b82015-04-30 11:12:10 -0700302 null,
sangho834e4b02015-05-01 09:38:25 -0700303 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800304 log.debug("Creating SIMPLE group for next objective id {} "
305 + "in dev:{}", nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700306 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800307 groupService.addGroup(groupDescription);
sangho834e4b02015-05-01 09:38:25 -0700308 }
309 break;
310 case HASHED:
Saurav Das8a0732e2015-11-20 15:27:53 -0800311 // we convert MPLS ECMP groups to flow-actions for a single
312 // bucket(output port).
313 boolean mplsEcmp = false;
314 if (nextObjective.meta() != null) {
315 for (Criterion c : nextObjective.meta().criteria()) {
316 if (c.type() == Type.MPLS_LABEL) {
317 mplsEcmp = true;
318 }
319 }
320 }
321 if (mplsEcmp) {
322 // covert to flow-actions in a dummy group by choosing the first bucket
323 log.debug("Converting HASHED group for next objective id {} " +
324 "to flow-actions in device:{}", nextObjective.id(),
325 deviceId);
326 TrafficTreatment treatment = nextObjective.next().iterator().next();
327 flowObjectiveStore.putNextGroup(nextObjective.id(),
328 new SpringOpenGroup(null, treatment));
329 } else {
330 // process as ECMP group
331 buckets = nextObjective
332 .next()
333 .stream()
334 .map((treatment) -> DefaultGroupBucket
335 .createSelectGroupBucket(treatment))
336 .collect(Collectors.toList());
337 if (!buckets.isEmpty()) {
338 final GroupKey key = new DefaultGroupKey(
339 appKryo.serialize(nextObjective.id()));
340 GroupDescription groupDescription = new DefaultGroupDescription(
341 deviceId,
342 GroupDescription.Type.SELECT,
343 new GroupBuckets(buckets),
344 key,
345 null,
346 nextObjective.appId());
347 log.debug("Creating HASHED group for next objective id {}"
348 + " in dev:{}", nextObjective.id(), deviceId);
349 pendingGroups.put(key, nextObjective);
350 groupService.addGroup(groupDescription);
351 }
sangho834e4b02015-05-01 09:38:25 -0700352 }
353 break;
354 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700355 buckets = nextObjective
356 .next()
357 .stream()
358 .map((treatment) -> DefaultGroupBucket
359 .createAllGroupBucket(treatment))
360 .collect(Collectors.toList());
361 if (!buckets.isEmpty()) {
362 final GroupKey key = new DefaultGroupKey(
363 appKryo.serialize(nextObjective
364 .id()));
365 GroupDescription groupDescription = new DefaultGroupDescription(
366 deviceId,
367 GroupDescription.Type.ALL,
368 new GroupBuckets(buckets),
369 key,
370 null,
371 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800372 log.debug("Creating BROADCAST group for next objective id {} "
373 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700374 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800375 groupService.addGroup(groupDescription);
Charles Chanc42e84e2015-10-20 16:24:19 -0700376 }
377 break;
sangho834e4b02015-05-01 09:38:25 -0700378 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700379 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700380 fail(nextObjective, ObjectiveError.UNSUPPORTED);
381 log.warn("Unsupported next objective type {}", nextObjective.type());
382 break;
383 default:
384 fail(nextObjective, ObjectiveError.UNKNOWN);
385 log.warn("Unknown next objective type {}", nextObjective.type());
386 }
387 }
388
389 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700390 log.debug("addBucketToGroup in {}: for next objective id {}",
391 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700392 Collection<TrafficTreatment> treatments = nextObjective.next();
393 TrafficTreatment treatment = treatments.iterator().next();
394 final GroupKey key = new DefaultGroupKey(
395 appKryo.serialize(nextObjective
396 .id()));
397 Group group = groupService.getGroup(deviceId, key);
398 if (group == null) {
399 log.warn("Group is not found in {} for {}", deviceId, key);
400 return;
401 }
402 GroupBucket bucket;
403 if (group.type() == GroupDescription.Type.INDIRECT) {
404 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
405 } else if (group.type() == GroupDescription.Type.SELECT) {
406 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700407 } else if (group.type() == GroupDescription.Type.ALL) {
408 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700409 } else {
410 log.warn("Unsupported Group type {}", group.type());
411 return;
412 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700413 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700414 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
415 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700416 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
417 }
418
419 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700420 log.debug("removeBucketFromGroup in {}: for next objective id {}",
421 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700422 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
423 if (nextGroup != null) {
424 Collection<TrafficTreatment> treatments = nextObjective.next();
425 TrafficTreatment treatment = treatments.iterator().next();
426 final GroupKey key = new DefaultGroupKey(
427 appKryo.serialize(nextObjective
428 .id()));
429 Group group = groupService.getGroup(deviceId, key);
430 if (group == null) {
431 log.warn("Group is not found in {} for {}", deviceId, key);
432 return;
433 }
434 GroupBucket bucket;
435 if (group.type() == GroupDescription.Type.INDIRECT) {
436 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
437 } else if (group.type() == GroupDescription.Type.SELECT) {
438 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700439 } else if (group.type() == GroupDescription.Type.ALL) {
440 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700441 } else {
442 log.warn("Unsupported Group type {}", group.type());
443 return;
444 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700445 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700446 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
447 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700448 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
449 }
450 }
451
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700452 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
453 switch (fwd.flag()) {
454 case SPECIFIC:
455 return processSpecific(fwd);
456 case VERSATILE:
457 return processVersatile(fwd);
458 default:
459 fail(fwd, ObjectiveError.UNKNOWN);
460 log.warn("Unknown forwarding flag {}", fwd.flag());
461 }
462 return Collections.emptySet();
463 }
464
465 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800466 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700467 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700468 EthTypeCriterion ethType =
469 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
470 if (ethType == null) {
471 log.error("Versatile forwarding objective must include ethType");
472 fail(fwd, ObjectiveError.UNKNOWN);
473 return Collections.emptySet();
474 }
475
Saurav Das8a0732e2015-11-20 15:27:53 -0800476 if (fwd.treatment() == null && fwd.nextId() == null) {
477 log.error("VERSATILE forwarding objective needs next objective ID "
478 + "or treatment.");
479 return Collections.emptySet();
480 }
481 // emulation of ACL table (for versatile fwd objective) requires
482 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700483 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
484 .builder();
485 treatmentBuilder.wipeDeferred();
486
487 if (fwd.nextId() != null) {
488 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700489 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800490 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
491 if (soGroup.dummy) {
492 // need to convert to flow-actions
493 for (Instruction ins : soGroup.treatment.allInstructions()) {
494 treatmentBuilder.add(ins);
495 }
496 } else {
497 GroupKey key = soGroup.key;
498 Group group = groupService.getGroup(deviceId, key);
499 if (group == null) {
500 log.warn("The group left!");
501 fail(fwd, ObjectiveError.GROUPMISSING);
502 return Collections.emptySet();
503 }
504 treatmentBuilder.deferred().group(group.id());
505 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700506 }
sangho1e575652015-05-14 00:39:53 -0700507 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800508 }
509
510 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700511 if (fwd.treatment().allInstructions().size() == 1 &&
512 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
513 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
514 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800515 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800516 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800517 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700518 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800519 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800520 for (Instruction ins : fwd.treatment().allInstructions()) {
521 treatmentBuilder.add(ins);
522 }
Saurav Das822c4e22015-10-23 10:51:11 -0700523 }
sangho1e575652015-05-14 00:39:53 -0700524 }
525
sangho1e575652015-05-14 00:39:53 -0700526 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
527 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700528 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800529 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700530
531 if (fwd.permanent()) {
532 ruleBuilder.makePermanent();
533 } else {
534 ruleBuilder.makeTemporary(fwd.timeout());
535 }
536
537 ruleBuilder.forTable(aclTableId);
538 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700539 }
540
Charles Chan68aa62d2015-11-09 16:37:23 -0800541 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700542 TrafficSelector selector = fwd.selector();
543 EthTypeCriterion ethType = (EthTypeCriterion) selector
544 .getCriterion(Criterion.Type.ETH_TYPE);
545 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800546 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
547 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
548 return false;
549 }
550 return true;
551 }
552
553 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
554 TrafficSelector selector = fwd.selector();
555 EthCriterion ethDst = (EthCriterion) selector
556 .getCriterion(Criterion.Type.ETH_DST);
557 VlanIdCriterion vlanId = (VlanIdCriterion) selector
558 .getCriterion(Criterion.Type.VLAN_VID);
559 if (ethDst == null && vlanId == null) {
560 return false;
561 }
562 return true;
563 }
564
565 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800566 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
567 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800568 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
569 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
570
571 if (isEthTypeObj) {
572 return processEthTypeSpecificObjective(fwd);
573 } else if (isEthDstObj) {
574 return processEthDstSpecificObjective(fwd);
575 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700576 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800577 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700578 fail(fwd, ObjectiveError.UNSUPPORTED);
579 return Collections.emptySet();
580 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800581 }
582
583 protected Collection<FlowRule>
584 processEthTypeSpecificObjective(ForwardingObjective fwd) {
585 TrafficSelector selector = fwd.selector();
586 EthTypeCriterion ethType = (EthTypeCriterion) selector
587 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700588
589 TrafficSelector.Builder filteredSelectorBuilder =
590 DefaultTrafficSelector.builder();
591 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700592 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700593 filteredSelectorBuilder = filteredSelectorBuilder
594 .matchEthType(Ethernet.TYPE_IPV4)
595 .matchIPDst(((IPCriterion) selector
596 .getCriterion(Criterion.Type.IPV4_DST))
597 .ip());
598 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800599 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
600 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700601 } else {
602 filteredSelectorBuilder = filteredSelectorBuilder
603 .matchEthType(Ethernet.MPLS_UNICAST)
604 .matchMplsLabel(((MplsCriterion)
605 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
606 //TODO: Add Match for BoS
607 //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
608 //}
609 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800610 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
611 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700612 }
613
614 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
615 .builder();
616 if (fwd.treatment() != null) {
617 for (Instruction i : fwd.treatment().allInstructions()) {
618 treatmentBuilder.add(i);
619 }
620 }
621
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700622 if (fwd.nextId() != null) {
623 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700624 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800625 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
626 if (soGroup.dummy) {
627 log.debug("Adding flow-actions for fwd. obj. {} "
628 + "in dev: {}", fwd.id(), deviceId);
629 for (Instruction ins : soGroup.treatment.allInstructions()) {
630 treatmentBuilder.add(ins);
631 }
632 } else {
633 GroupKey key = soGroup.key;
634 Group group = groupService.getGroup(deviceId, key);
635 if (group == null) {
636 log.warn("The group left!");
637 fail(fwd, ObjectiveError.GROUPMISSING);
638 return Collections.emptySet();
639 }
640 treatmentBuilder.deferred().group(group.id());
641 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
642 + "in dev: {}", group.id(), fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700643 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700644 } else {
645 log.warn("processSpecific: No associated next objective object");
646 fail(fwd, ObjectiveError.GROUPMISSING);
647 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700648 }
649 }
650
651 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
652 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
653 .build();
654
655 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
656 .fromApp(fwd.appId()).withPriority(fwd.priority())
657 .forDevice(deviceId).withSelector(filteredSelector)
658 .withTreatment(treatment);
659
660 if (fwd.permanent()) {
661 ruleBuilder.makePermanent();
662 } else {
663 ruleBuilder.makeTemporary(fwd.timeout());
664 }
665
666 ruleBuilder.forTable(forTableId);
667 return Collections.singletonList(ruleBuilder.build());
668
669 }
670
Charles Chan68aa62d2015-11-09 16:37:23 -0800671 protected Collection<FlowRule>
672 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700673 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800674
675 // Build filtered selector
676 TrafficSelector selector = fwd.selector();
677 EthCriterion ethCriterion = (EthCriterion) selector
678 .getCriterion(Criterion.Type.ETH_DST);
679 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
680 .getCriterion(Criterion.Type.VLAN_VID);
681 TrafficSelector.Builder filteredSelectorBuilder =
682 DefaultTrafficSelector.builder();
683 // Do not match MacAddress for subnet broadcast entry
684 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
685 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800686 log.debug("processing L2 forwarding objective:{} in dev:{}",
687 fwd.id(), deviceId);
688 } else {
689 log.debug("processing L2 Broadcast forwarding objective:{} "
690 + "in dev:{} for vlan:{}",
691 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800692 }
693 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
694 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
695
696 // Build filtered treatment
697 TrafficTreatment.Builder treatmentBuilder =
698 DefaultTrafficTreatment.builder();
699 if (fwd.treatment() != null) {
700 treatmentBuilder.deferred();
701 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
702 }
703 if (fwd.nextId() != null) {
704 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
705 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800706 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
707 if (soGrp.dummy) {
708 log.debug("Adding flow-actions for fwd. obj. {} "
709 + "in dev: {}", fwd.id(), deviceId);
710 for (Instruction ins : soGrp.treatment.allInstructions()) {
711 treatmentBuilder.add(ins);
712 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800713 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800714 GroupKey key = soGrp.key;
715 Group group = groupService.getGroup(deviceId, key);
716 if (group == null) {
717 log.warn("The group left!");
718 fail(fwd, ObjectiveError.GROUPMISSING);
719 return Collections.emptySet();
720 }
721 treatmentBuilder.deferred().group(group.id());
722 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
723 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800724 }
725 }
726 }
727 treatmentBuilder.immediate().transition(aclTableId);
728 TrafficTreatment filteredTreatment = treatmentBuilder.build();
729
730 // Build bridging table entries
731 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
732 flowRuleBuilder.fromApp(fwd.appId())
733 .withPriority(fwd.priority())
734 .forDevice(deviceId)
735 .withSelector(filteredSelector)
736 .withTreatment(filteredTreatment)
737 .forTable(dstMacTableId);
738 if (fwd.permanent()) {
739 flowRuleBuilder.makePermanent();
740 } else {
741 flowRuleBuilder.makeTemporary(fwd.timeout());
742 }
743 rules.add(flowRuleBuilder.build());
744
745 /*
746 // TODO Emulate source MAC table behavior
747 // Do not install source MAC table entry for subnet broadcast
748 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
749 // Build filtered selector
750 selector = fwd.selector();
751 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
752 filteredSelectorBuilder = DefaultTrafficSelector.builder();
753 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
754 filteredSelector = filteredSelectorBuilder.build();
755
756 // Build empty treatment. Apply existing instruction if match.
757 treatmentBuilder = DefaultTrafficTreatment.builder();
758 filteredTreatment = treatmentBuilder.build();
759
760 // Build bridging table entries
761 flowRuleBuilder = DefaultFlowRule.builder();
762 flowRuleBuilder.fromApp(fwd.appId())
763 .withPriority(fwd.priority())
764 .forDevice(deviceId)
765 .withSelector(filteredSelector)
766 .withTreatment(filteredTreatment)
767 .forTable(srcMacTableId)
768 .makePermanent();
769 rules.add(flowRuleBuilder.build());
770 }
771 */
772
773 return rules;
774 }
775
776 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
777 VlanIdCriterion vlanIdCriterion,
778 FilteringObjective filt,
779 VlanId assignedVlan,
780 ApplicationId applicationId) {
781 //handling untagged packets via assigned VLAN
782 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
783 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
784 }
785
786 /*
787 * Note: CpqD switches do not handle MPLS-related operation properly
788 * for a packet with VLAN tag. We pop VLAN here as a workaround.
789 * Side effect: HostService learns redundant hosts with same MAC but
790 * different VLAN. No known side effect on the network reachability.
791 */
792 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700793 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
794 .builder();
795 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
796 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800797 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700798 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800799 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
800 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700801 treatmentIp.transition(ipv4UnicastTableId);
802 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
803 .withSelector(selectorIp.build())
804 .withTreatment(treatmentIp.build())
805 .withPriority(filt.priority()).fromApp(applicationId)
806 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800807 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700808 rules.add(ruleIp);
809
810 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
811 .builder();
812 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
813 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800814 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700815 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800816 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
817 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700818 treatmentMpls.transition(mplsTableId);
819 FlowRule ruleMpls = DefaultFlowRule.builder()
820 .forDevice(deviceId).withSelector(selectorMpls.build())
821 .withTreatment(treatmentMpls.build())
822 .withPriority(filt.priority()).fromApp(applicationId)
823 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800824 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700825 rules.add(ruleMpls);
826
827 return rules;
828 }
829
Charles Chan68aa62d2015-11-09 16:37:23 -0800830 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700831 FilteringObjective filt,
Charles Chan68aa62d2015-11-09 16:37:23 -0800832 VlanId assignedVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700833 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700834 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800835 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700836 TrafficSelector.Builder selector = DefaultTrafficSelector
837 .builder();
838 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
839 .builder();
840 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800841 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
842 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700843 selector.matchInPort(p.port());
844 treatment.deferred().popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800845 } else {
846 selector.matchInPort(p.port());
847 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700848 }
849 treatment.transition(tmacTableId);
850 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
851 .withSelector(selector.build())
852 .withTreatment(treatment.build())
853 .withPriority(filt.priority()).fromApp(applicationId)
854 .makePermanent().forTable(vlanTableId).build();
855 rules.add(rule);
856
857 return rules;
858 }
859
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700860 private void processFilter(FilteringObjective filt, boolean install,
861 ApplicationId applicationId) {
862 // This driver only processes filtering criteria defined with switch
863 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700864 if (filt.key().equals(Criteria.dummy())
865 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700866 log.warn("No key defined in filtering objective from app: {}. Not"
867 + "processing filtering objective", applicationId);
868 fail(filt, ObjectiveError.UNKNOWN);
869 return;
870 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800871
872 EthCriterion ethCriterion = null;
873 VlanIdCriterion vlanIdCriterion = null;
874
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700875 // convert filtering conditions for switch-intfs into flowrules
876 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800877
878 for (Criterion criterion : filt.conditions()) {
879 if (criterion.type() == Criterion.Type.ETH_DST) {
880 ethCriterion = (EthCriterion) criterion;
881 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
882 vlanIdCriterion = (VlanIdCriterion) criterion;
883 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700884 log.debug("driver does not process IP filtering rules as it " +
885 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700886 } else {
887 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800888 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700889 fail(filt, ObjectiveError.UNSUPPORTED);
890 }
891 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800892
893 VlanId assignedVlan = null;
894 if (vlanIdCriterion != null && vlanIdCriterion.vlanId() == VlanId.NONE) {
895 // Assign a VLAN ID to untagged packets
896 if (filt.meta() == null) {
897 log.error("Missing metadata in filtering objective required "
898 + "for vlan assignment in dev {}", deviceId);
899 fail(filt, ObjectiveError.BADPARAMS);
900 return;
901 }
902 for (Instruction i : filt.meta().allInstructions()) {
903 if (i instanceof ModVlanIdInstruction) {
904 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
905 }
906 }
907 if (assignedVlan == null) {
908 log.error("Driver requires an assigned vlan-id to tag incoming "
909 + "untagged packets. Not processing vlan filters on "
910 + "device {}", deviceId);
911 fail(filt, ObjectiveError.BADPARAMS);
912 return;
913 }
914 }
915
916 if (ethCriterion == null) {
917 log.debug("filtering objective missing dstMac, cannot program TMAC table");
918 } else {
919 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
920 vlanIdCriterion,
921 filt,
922 assignedVlan,
923 applicationId)) {
924 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
925 tmacRule, deviceId);
926 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
927 }
928 }
929
930 if (ethCriterion == null || vlanIdCriterion == null) {
931 log.debug("filtering objective missing dstMac or vlan, cannot program"
932 + "Vlan Table");
933 } else {
934 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
935 filt,
936 assignedVlan,
937 applicationId)) {
938 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
939 vlanRule, deviceId);
940 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
941 }
942 }
943
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700944 // apply filtering flow rules
945 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
946 @Override
947 public void onSuccess(FlowRuleOperations ops) {
948 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700949 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -0800950 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700951 }
952
953 @Override
954 public void onError(FlowRuleOperations ops) {
955 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700956 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800957 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700958 }
959 }));
960 }
961
962 protected void setTableMissEntries() {
963 // set all table-miss-entries
964 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -0800965 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
966 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700967 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800968 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700969 populateTableMissEntry(aclTableId, false, false, false, -1);
970 }
971
972 protected void populateTableMissEntry(int tableToAdd,
973 boolean toControllerNow,
974 boolean toControllerWrite,
975 boolean toTable, int tableToSend) {
976 TrafficSelector selector = DefaultTrafficSelector.builder().build();
977 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
978
979 if (toControllerNow) {
980 tBuilder.setOutput(PortNumber.CONTROLLER);
981 }
982
983 if (toControllerWrite) {
984 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
985 }
986
987 if (toTable) {
988 tBuilder.transition(tableToSend);
989 }
990
991 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
992 .withSelector(selector).withTreatment(tBuilder.build())
993 .withPriority(0).fromApp(appId).makePermanent()
994 .forTable(tableToAdd).build();
995
996 flowRuleService.applyFlowRules(flow);
997 }
998
999 private void pass(Objective obj) {
1000 if (obj.context().isPresent()) {
1001 obj.context().get().onSuccess(obj);
1002 }
1003 }
1004
1005 protected void fail(Objective obj, ObjectiveError error) {
1006 if (obj.context().isPresent()) {
1007 obj.context().get().onError(obj, error);
1008 }
1009 }
1010
1011 private class InnerGroupListener implements GroupListener {
1012 @Override
1013 public void event(GroupEvent event) {
1014 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001015 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001016 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001017 GroupKey key = event.subject().appCookie();
1018
1019 NextObjective obj = pendingGroups.getIfPresent(key);
1020 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001021 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1022 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001023 flowObjectiveStore
1024 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001025 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001026 pass(obj);
1027 pendingGroups.invalidate(key);
1028 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001029 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1030 log.warn("InnerGroupListener: Group ADD "
1031 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001032 }
1033 }
1034 }
1035
1036 private class GroupChecker implements Runnable {
1037
1038 @Override
1039 public void run() {
1040 Set<GroupKey> keys = pendingGroups
1041 .asMap()
1042 .keySet()
1043 .stream()
1044 .filter(key -> groupService.getGroup(deviceId, key) != null)
1045 .collect(Collectors.toSet());
1046
1047 keys.stream()
1048 .forEach(key -> {
1049 NextObjective obj = pendingGroups
1050 .getIfPresent(key);
1051 if (obj == null) {
1052 return;
1053 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001054 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1055 deviceId,
1056 groupService.getGroup(deviceId, key).id(),
1057 obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001058 pass(obj);
1059 pendingGroups.invalidate(key);
Saurav Das8a0732e2015-11-20 15:27:53 -08001060 flowObjectiveStore.putNextGroup(
1061 obj.id(),
1062 new SpringOpenGroup(key, null));
1063 });
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001064 }
1065 }
1066
Saurav Das8a0732e2015-11-20 15:27:53 -08001067 /**
1068 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1069 * used to fetch the group from the Group Service, or it can be serve as storage
1070 * for Traffic Treatments which can be used as flow actions. In the latter
1071 * case, we refer to this as a dummy group.
1072 *
1073 */
1074 private class SpringOpenGroup implements NextGroup {
1075 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001076 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001077 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001078
Saurav Das8a0732e2015-11-20 15:27:53 -08001079 /**
1080 * Storage for a GroupKey or a TrafficTreatment. One of the params
1081 * to this constructor must be null.
1082 * @param key represents a GroupKey
1083 * @param treatment represents flow actions in a dummy group
1084 */
1085 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1086 if (key == null) {
1087 this.key = new DefaultGroupKey(new byte[]{0});
1088 this.treatment = treatment;
1089 this.dummy = true;
1090 } else {
1091 this.key = key;
1092 this.treatment = DefaultTrafficTreatment.builder().build();
1093 this.dummy = false;
1094 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001095 }
1096
Saurav Das822c4e22015-10-23 10:51:11 -07001097 @SuppressWarnings("unused")
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001098 public GroupKey key() {
1099 return key;
1100 }
1101
1102 @Override
1103 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001104 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001105 }
1106
1107 }
1108}