blob: dba4557ac722164b0436f67e9f535d20bcf5750c [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) {
Saurav Das4ce45962015-11-24 23:21:05 -0800290 // Spring Open TTP converts simple nextObjective to flow-actions
291 // in a dummy group
292 TrafficTreatment treatment = nextObjective.next().iterator().next();
293 log.debug("Converting SIMPLE group for next objective id {} " +
294 "to {} flow-actions in device:{}", nextObjective.id(),
295 treatment.allInstructions().size(), deviceId);
296 flowObjectiveStore.putNextGroup(nextObjective.id(),
297 new SpringOpenGroup(null, treatment));
sangho834e4b02015-05-01 09:38:25 -0700298 }
299 break;
300 case HASHED:
Saurav Das8a0732e2015-11-20 15:27:53 -0800301 // we convert MPLS ECMP groups to flow-actions for a single
302 // bucket(output port).
303 boolean mplsEcmp = false;
304 if (nextObjective.meta() != null) {
305 for (Criterion c : nextObjective.meta().criteria()) {
306 if (c.type() == Type.MPLS_LABEL) {
307 mplsEcmp = true;
308 }
309 }
310 }
311 if (mplsEcmp) {
312 // covert to flow-actions in a dummy group by choosing the first bucket
313 log.debug("Converting HASHED group for next objective id {} " +
314 "to flow-actions in device:{}", nextObjective.id(),
315 deviceId);
316 TrafficTreatment treatment = nextObjective.next().iterator().next();
317 flowObjectiveStore.putNextGroup(nextObjective.id(),
318 new SpringOpenGroup(null, treatment));
319 } else {
320 // process as ECMP group
321 buckets = nextObjective
322 .next()
323 .stream()
324 .map((treatment) -> DefaultGroupBucket
325 .createSelectGroupBucket(treatment))
326 .collect(Collectors.toList());
327 if (!buckets.isEmpty()) {
328 final GroupKey key = new DefaultGroupKey(
329 appKryo.serialize(nextObjective.id()));
330 GroupDescription groupDescription = new DefaultGroupDescription(
331 deviceId,
332 GroupDescription.Type.SELECT,
333 new GroupBuckets(buckets),
334 key,
335 null,
336 nextObjective.appId());
337 log.debug("Creating HASHED group for next objective id {}"
338 + " in dev:{}", nextObjective.id(), deviceId);
339 pendingGroups.put(key, nextObjective);
340 groupService.addGroup(groupDescription);
341 }
sangho834e4b02015-05-01 09:38:25 -0700342 }
343 break;
344 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700345 buckets = nextObjective
346 .next()
347 .stream()
348 .map((treatment) -> DefaultGroupBucket
349 .createAllGroupBucket(treatment))
350 .collect(Collectors.toList());
351 if (!buckets.isEmpty()) {
352 final GroupKey key = new DefaultGroupKey(
353 appKryo.serialize(nextObjective
354 .id()));
355 GroupDescription groupDescription = new DefaultGroupDescription(
356 deviceId,
357 GroupDescription.Type.ALL,
358 new GroupBuckets(buckets),
359 key,
360 null,
361 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800362 log.debug("Creating BROADCAST group for next objective id {} "
363 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700364 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800365 groupService.addGroup(groupDescription);
Charles Chanc42e84e2015-10-20 16:24:19 -0700366 }
367 break;
sangho834e4b02015-05-01 09:38:25 -0700368 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700369 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700370 fail(nextObjective, ObjectiveError.UNSUPPORTED);
371 log.warn("Unsupported next objective type {}", nextObjective.type());
372 break;
373 default:
374 fail(nextObjective, ObjectiveError.UNKNOWN);
375 log.warn("Unknown next objective type {}", nextObjective.type());
376 }
377 }
378
379 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700380 log.debug("addBucketToGroup in {}: for next objective id {}",
381 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700382 Collection<TrafficTreatment> treatments = nextObjective.next();
383 TrafficTreatment treatment = treatments.iterator().next();
384 final GroupKey key = new DefaultGroupKey(
385 appKryo.serialize(nextObjective
386 .id()));
387 Group group = groupService.getGroup(deviceId, key);
388 if (group == null) {
389 log.warn("Group is not found in {} for {}", deviceId, key);
390 return;
391 }
392 GroupBucket bucket;
393 if (group.type() == GroupDescription.Type.INDIRECT) {
394 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
395 } else if (group.type() == GroupDescription.Type.SELECT) {
396 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700397 } else if (group.type() == GroupDescription.Type.ALL) {
398 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700399 } else {
400 log.warn("Unsupported Group type {}", group.type());
401 return;
402 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700403 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700404 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
405 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700406 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
407 }
408
409 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700410 log.debug("removeBucketFromGroup in {}: for next objective id {}",
411 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700412 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
413 if (nextGroup != null) {
414 Collection<TrafficTreatment> treatments = nextObjective.next();
415 TrafficTreatment treatment = treatments.iterator().next();
416 final GroupKey key = new DefaultGroupKey(
417 appKryo.serialize(nextObjective
418 .id()));
419 Group group = groupService.getGroup(deviceId, key);
420 if (group == null) {
421 log.warn("Group is not found in {} for {}", deviceId, key);
422 return;
423 }
424 GroupBucket bucket;
425 if (group.type() == GroupDescription.Type.INDIRECT) {
426 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
427 } else if (group.type() == GroupDescription.Type.SELECT) {
428 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700429 } else if (group.type() == GroupDescription.Type.ALL) {
430 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700431 } else {
432 log.warn("Unsupported Group type {}", group.type());
433 return;
434 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700435 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700436 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
437 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700438 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
439 }
440 }
441
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700442 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
443 switch (fwd.flag()) {
444 case SPECIFIC:
445 return processSpecific(fwd);
446 case VERSATILE:
447 return processVersatile(fwd);
448 default:
449 fail(fwd, ObjectiveError.UNKNOWN);
450 log.warn("Unknown forwarding flag {}", fwd.flag());
451 }
452 return Collections.emptySet();
453 }
454
455 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800456 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700457 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700458 EthTypeCriterion ethType =
459 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
460 if (ethType == null) {
461 log.error("Versatile forwarding objective must include ethType");
462 fail(fwd, ObjectiveError.UNKNOWN);
463 return Collections.emptySet();
464 }
465
Saurav Das8a0732e2015-11-20 15:27:53 -0800466 if (fwd.treatment() == null && fwd.nextId() == null) {
467 log.error("VERSATILE forwarding objective needs next objective ID "
468 + "or treatment.");
469 return Collections.emptySet();
470 }
471 // emulation of ACL table (for versatile fwd objective) requires
472 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700473 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
474 .builder();
475 treatmentBuilder.wipeDeferred();
476
477 if (fwd.nextId() != null) {
478 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700479 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800480 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
481 if (soGroup.dummy) {
482 // need to convert to flow-actions
483 for (Instruction ins : soGroup.treatment.allInstructions()) {
484 treatmentBuilder.add(ins);
485 }
486 } else {
487 GroupKey key = soGroup.key;
488 Group group = groupService.getGroup(deviceId, key);
489 if (group == null) {
490 log.warn("The group left!");
491 fail(fwd, ObjectiveError.GROUPMISSING);
492 return Collections.emptySet();
493 }
494 treatmentBuilder.deferred().group(group.id());
495 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700496 }
sangho1e575652015-05-14 00:39:53 -0700497 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800498 }
499
500 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700501 if (fwd.treatment().allInstructions().size() == 1 &&
502 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
503 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
504 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800505 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800506 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800507 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700508 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800509 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800510 for (Instruction ins : fwd.treatment().allInstructions()) {
511 treatmentBuilder.add(ins);
512 }
Saurav Das822c4e22015-10-23 10:51:11 -0700513 }
sangho1e575652015-05-14 00:39:53 -0700514 }
515
sangho1e575652015-05-14 00:39:53 -0700516 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
517 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700518 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800519 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700520
521 if (fwd.permanent()) {
522 ruleBuilder.makePermanent();
523 } else {
524 ruleBuilder.makeTemporary(fwd.timeout());
525 }
526
527 ruleBuilder.forTable(aclTableId);
528 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700529 }
530
Charles Chan68aa62d2015-11-09 16:37:23 -0800531 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700532 TrafficSelector selector = fwd.selector();
533 EthTypeCriterion ethType = (EthTypeCriterion) selector
534 .getCriterion(Criterion.Type.ETH_TYPE);
535 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800536 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
537 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
538 return false;
539 }
540 return true;
541 }
542
543 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
544 TrafficSelector selector = fwd.selector();
545 EthCriterion ethDst = (EthCriterion) selector
546 .getCriterion(Criterion.Type.ETH_DST);
547 VlanIdCriterion vlanId = (VlanIdCriterion) selector
548 .getCriterion(Criterion.Type.VLAN_VID);
549 if (ethDst == null && vlanId == null) {
550 return false;
551 }
552 return true;
553 }
554
555 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800556 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
557 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800558 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
559 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
560
561 if (isEthTypeObj) {
562 return processEthTypeSpecificObjective(fwd);
563 } else if (isEthDstObj) {
564 return processEthDstSpecificObjective(fwd);
565 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700566 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800567 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700568 fail(fwd, ObjectiveError.UNSUPPORTED);
569 return Collections.emptySet();
570 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800571 }
572
573 protected Collection<FlowRule>
574 processEthTypeSpecificObjective(ForwardingObjective fwd) {
575 TrafficSelector selector = fwd.selector();
576 EthTypeCriterion ethType = (EthTypeCriterion) selector
577 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700578
579 TrafficSelector.Builder filteredSelectorBuilder =
580 DefaultTrafficSelector.builder();
581 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700582 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700583 filteredSelectorBuilder = filteredSelectorBuilder
584 .matchEthType(Ethernet.TYPE_IPV4)
585 .matchIPDst(((IPCriterion) selector
586 .getCriterion(Criterion.Type.IPV4_DST))
587 .ip());
588 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800589 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
590 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700591 } else {
592 filteredSelectorBuilder = filteredSelectorBuilder
593 .matchEthType(Ethernet.MPLS_UNICAST)
594 .matchMplsLabel(((MplsCriterion)
595 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
596 //TODO: Add Match for BoS
597 //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
598 //}
599 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800600 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
601 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700602 }
603
604 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
605 .builder();
606 if (fwd.treatment() != null) {
607 for (Instruction i : fwd.treatment().allInstructions()) {
608 treatmentBuilder.add(i);
609 }
610 }
611
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700612 if (fwd.nextId() != null) {
613 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700614 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800615 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
616 if (soGroup.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800617 log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
618 + "in dev: {}", soGroup.treatment.allInstructions().size(),
619 fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800620 for (Instruction ins : soGroup.treatment.allInstructions()) {
621 treatmentBuilder.add(ins);
622 }
623 } else {
624 GroupKey key = soGroup.key;
625 Group group = groupService.getGroup(deviceId, key);
626 if (group == null) {
627 log.warn("The group left!");
628 fail(fwd, ObjectiveError.GROUPMISSING);
629 return Collections.emptySet();
630 }
631 treatmentBuilder.deferred().group(group.id());
632 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
Saurav Das4ce45962015-11-24 23:21:05 -0800633 + "for next:{} in dev: {}", group.id(), fwd.id(),
634 fwd.nextId(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700635 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700636 } else {
637 log.warn("processSpecific: No associated next objective object");
638 fail(fwd, ObjectiveError.GROUPMISSING);
639 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700640 }
641 }
642
643 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
644 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
645 .build();
646
647 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
648 .fromApp(fwd.appId()).withPriority(fwd.priority())
649 .forDevice(deviceId).withSelector(filteredSelector)
650 .withTreatment(treatment);
651
652 if (fwd.permanent()) {
653 ruleBuilder.makePermanent();
654 } else {
655 ruleBuilder.makeTemporary(fwd.timeout());
656 }
657
658 ruleBuilder.forTable(forTableId);
659 return Collections.singletonList(ruleBuilder.build());
660
661 }
662
Charles Chan68aa62d2015-11-09 16:37:23 -0800663 protected Collection<FlowRule>
664 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700665 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800666
667 // Build filtered selector
668 TrafficSelector selector = fwd.selector();
669 EthCriterion ethCriterion = (EthCriterion) selector
670 .getCriterion(Criterion.Type.ETH_DST);
671 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
672 .getCriterion(Criterion.Type.VLAN_VID);
673 TrafficSelector.Builder filteredSelectorBuilder =
674 DefaultTrafficSelector.builder();
675 // Do not match MacAddress for subnet broadcast entry
676 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
677 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800678 log.debug("processing L2 forwarding objective:{} in dev:{}",
679 fwd.id(), deviceId);
680 } else {
681 log.debug("processing L2 Broadcast forwarding objective:{} "
682 + "in dev:{} for vlan:{}",
683 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800684 }
685 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
686 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
687
688 // Build filtered treatment
689 TrafficTreatment.Builder treatmentBuilder =
690 DefaultTrafficTreatment.builder();
691 if (fwd.treatment() != null) {
692 treatmentBuilder.deferred();
693 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
694 }
695 if (fwd.nextId() != null) {
696 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
697 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800698 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
699 if (soGrp.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800700 log.debug("Adding {} flow-actions for fwd. obj. {} "
701 + "in dev: {}", soGrp.treatment.allInstructions().size(),
702 fwd.id(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800703 for (Instruction ins : soGrp.treatment.allInstructions()) {
Saurav Das4ce45962015-11-24 23:21:05 -0800704 treatmentBuilder.deferred().add(ins);
Saurav Das8a0732e2015-11-20 15:27:53 -0800705 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800706 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800707 GroupKey key = soGrp.key;
708 Group group = groupService.getGroup(deviceId, key);
709 if (group == null) {
710 log.warn("The group left!");
711 fail(fwd, ObjectiveError.GROUPMISSING);
712 return Collections.emptySet();
713 }
714 treatmentBuilder.deferred().group(group.id());
715 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
716 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800717 }
718 }
719 }
720 treatmentBuilder.immediate().transition(aclTableId);
721 TrafficTreatment filteredTreatment = treatmentBuilder.build();
722
723 // Build bridging table entries
724 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
725 flowRuleBuilder.fromApp(fwd.appId())
726 .withPriority(fwd.priority())
727 .forDevice(deviceId)
728 .withSelector(filteredSelector)
729 .withTreatment(filteredTreatment)
730 .forTable(dstMacTableId);
731 if (fwd.permanent()) {
732 flowRuleBuilder.makePermanent();
733 } else {
734 flowRuleBuilder.makeTemporary(fwd.timeout());
735 }
736 rules.add(flowRuleBuilder.build());
737
738 /*
739 // TODO Emulate source MAC table behavior
740 // Do not install source MAC table entry for subnet broadcast
741 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
742 // Build filtered selector
743 selector = fwd.selector();
744 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
745 filteredSelectorBuilder = DefaultTrafficSelector.builder();
746 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
747 filteredSelector = filteredSelectorBuilder.build();
748
749 // Build empty treatment. Apply existing instruction if match.
750 treatmentBuilder = DefaultTrafficTreatment.builder();
751 filteredTreatment = treatmentBuilder.build();
752
753 // Build bridging table entries
754 flowRuleBuilder = DefaultFlowRule.builder();
755 flowRuleBuilder.fromApp(fwd.appId())
756 .withPriority(fwd.priority())
757 .forDevice(deviceId)
758 .withSelector(filteredSelector)
759 .withTreatment(filteredTreatment)
760 .forTable(srcMacTableId)
761 .makePermanent();
762 rules.add(flowRuleBuilder.build());
763 }
764 */
765
766 return rules;
767 }
768
Saurav Das4ce45962015-11-24 23:21:05 -0800769 /*
770 * Note: CpqD switches do not handle MPLS-related operation properly
771 * for a packet with VLAN tag. We pop VLAN here as a workaround.
772 * Side effect: HostService learns redundant hosts with same MAC but
773 * different VLAN. No known side effect on the network reachability.
774 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800775 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
776 VlanIdCriterion vlanIdCriterion,
777 FilteringObjective filt,
778 VlanId assignedVlan,
779 ApplicationId applicationId) {
780 //handling untagged packets via assigned VLAN
781 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
782 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
783 }
784
Charles Chan68aa62d2015-11-09 16:37:23 -0800785 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700786 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
787 .builder();
788 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
789 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800790 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700791 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800792 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
793 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700794 treatmentIp.transition(ipv4UnicastTableId);
795 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
796 .withSelector(selectorIp.build())
797 .withTreatment(treatmentIp.build())
798 .withPriority(filt.priority()).fromApp(applicationId)
799 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800800 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700801 rules.add(ruleIp);
802
803 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
804 .builder();
805 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
806 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800807 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700808 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800809 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
810 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700811 treatmentMpls.transition(mplsTableId);
812 FlowRule ruleMpls = DefaultFlowRule.builder()
813 .forDevice(deviceId).withSelector(selectorMpls.build())
814 .withTreatment(treatmentMpls.build())
815 .withPriority(filt.priority()).fromApp(applicationId)
816 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800817 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700818 rules.add(ruleMpls);
819
820 return rules;
821 }
822
Charles Chan68aa62d2015-11-09 16:37:23 -0800823 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700824 FilteringObjective filt,
Charles Chan68aa62d2015-11-09 16:37:23 -0800825 VlanId assignedVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700826 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700827 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800828 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700829 TrafficSelector.Builder selector = DefaultTrafficSelector
830 .builder();
831 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
832 .builder();
833 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800834 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
835 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700836 selector.matchInPort(p.port());
837 treatment.deferred().popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800838 } else {
839 selector.matchInPort(p.port());
840 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700841 }
842 treatment.transition(tmacTableId);
843 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
844 .withSelector(selector.build())
845 .withTreatment(treatment.build())
846 .withPriority(filt.priority()).fromApp(applicationId)
847 .makePermanent().forTable(vlanTableId).build();
848 rules.add(rule);
849
850 return rules;
851 }
852
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700853 private void processFilter(FilteringObjective filt, boolean install,
854 ApplicationId applicationId) {
855 // This driver only processes filtering criteria defined with switch
856 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700857 if (filt.key().equals(Criteria.dummy())
858 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700859 log.warn("No key defined in filtering objective from app: {}. Not"
860 + "processing filtering objective", applicationId);
861 fail(filt, ObjectiveError.UNKNOWN);
862 return;
863 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800864
865 EthCriterion ethCriterion = null;
866 VlanIdCriterion vlanIdCriterion = null;
867
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700868 // convert filtering conditions for switch-intfs into flowrules
869 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800870
871 for (Criterion criterion : filt.conditions()) {
872 if (criterion.type() == Criterion.Type.ETH_DST) {
873 ethCriterion = (EthCriterion) criterion;
874 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
875 vlanIdCriterion = (VlanIdCriterion) criterion;
876 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700877 log.debug("driver does not process IP filtering rules as it " +
878 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700879 } else {
880 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800881 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700882 fail(filt, ObjectiveError.UNSUPPORTED);
883 }
884 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800885
886 VlanId assignedVlan = null;
887 if (vlanIdCriterion != null && vlanIdCriterion.vlanId() == VlanId.NONE) {
888 // Assign a VLAN ID to untagged packets
889 if (filt.meta() == null) {
890 log.error("Missing metadata in filtering objective required "
891 + "for vlan assignment in dev {}", deviceId);
892 fail(filt, ObjectiveError.BADPARAMS);
893 return;
894 }
895 for (Instruction i : filt.meta().allInstructions()) {
896 if (i instanceof ModVlanIdInstruction) {
897 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
898 }
899 }
900 if (assignedVlan == null) {
901 log.error("Driver requires an assigned vlan-id to tag incoming "
902 + "untagged packets. Not processing vlan filters on "
903 + "device {}", deviceId);
904 fail(filt, ObjectiveError.BADPARAMS);
905 return;
906 }
907 }
908
909 if (ethCriterion == null) {
910 log.debug("filtering objective missing dstMac, cannot program TMAC table");
911 } else {
912 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
913 vlanIdCriterion,
914 filt,
915 assignedVlan,
916 applicationId)) {
917 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
918 tmacRule, deviceId);
919 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
920 }
921 }
922
923 if (ethCriterion == null || vlanIdCriterion == null) {
924 log.debug("filtering objective missing dstMac or vlan, cannot program"
925 + "Vlan Table");
926 } else {
927 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
928 filt,
929 assignedVlan,
930 applicationId)) {
931 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
932 vlanRule, deviceId);
933 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
934 }
935 }
936
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700937 // apply filtering flow rules
938 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
939 @Override
940 public void onSuccess(FlowRuleOperations ops) {
941 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700942 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -0800943 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700944 }
945
946 @Override
947 public void onError(FlowRuleOperations ops) {
948 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700949 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800950 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700951 }
952 }));
953 }
954
955 protected void setTableMissEntries() {
956 // set all table-miss-entries
957 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -0800958 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
959 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700960 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800961 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700962 populateTableMissEntry(aclTableId, false, false, false, -1);
963 }
964
965 protected void populateTableMissEntry(int tableToAdd,
966 boolean toControllerNow,
967 boolean toControllerWrite,
968 boolean toTable, int tableToSend) {
969 TrafficSelector selector = DefaultTrafficSelector.builder().build();
970 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
971
972 if (toControllerNow) {
973 tBuilder.setOutput(PortNumber.CONTROLLER);
974 }
975
976 if (toControllerWrite) {
977 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
978 }
979
980 if (toTable) {
981 tBuilder.transition(tableToSend);
982 }
983
984 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
985 .withSelector(selector).withTreatment(tBuilder.build())
986 .withPriority(0).fromApp(appId).makePermanent()
987 .forTable(tableToAdd).build();
988
989 flowRuleService.applyFlowRules(flow);
990 }
991
992 private void pass(Objective obj) {
993 if (obj.context().isPresent()) {
994 obj.context().get().onSuccess(obj);
995 }
996 }
997
998 protected void fail(Objective obj, ObjectiveError error) {
999 if (obj.context().isPresent()) {
1000 obj.context().get().onError(obj, error);
1001 }
1002 }
1003
1004 private class InnerGroupListener implements GroupListener {
1005 @Override
1006 public void event(GroupEvent event) {
1007 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001008 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001009 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001010 GroupKey key = event.subject().appCookie();
1011
1012 NextObjective obj = pendingGroups.getIfPresent(key);
1013 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001014 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1015 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001016 flowObjectiveStore
1017 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001018 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001019 pass(obj);
1020 pendingGroups.invalidate(key);
1021 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001022 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1023 log.warn("InnerGroupListener: Group ADD "
1024 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001025 }
1026 }
1027 }
1028
1029 private class GroupChecker implements Runnable {
1030
1031 @Override
1032 public void run() {
1033 Set<GroupKey> keys = pendingGroups
1034 .asMap()
1035 .keySet()
1036 .stream()
1037 .filter(key -> groupService.getGroup(deviceId, key) != null)
1038 .collect(Collectors.toSet());
1039
1040 keys.stream()
1041 .forEach(key -> {
1042 NextObjective obj = pendingGroups
1043 .getIfPresent(key);
1044 if (obj == null) {
1045 return;
1046 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001047 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1048 deviceId,
1049 groupService.getGroup(deviceId, key).id(),
1050 obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001051 pass(obj);
1052 pendingGroups.invalidate(key);
Saurav Das8a0732e2015-11-20 15:27:53 -08001053 flowObjectiveStore.putNextGroup(
1054 obj.id(),
1055 new SpringOpenGroup(key, null));
1056 });
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001057 }
1058 }
1059
Saurav Das8a0732e2015-11-20 15:27:53 -08001060 /**
1061 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1062 * used to fetch the group from the Group Service, or it can be serve as storage
1063 * for Traffic Treatments which can be used as flow actions. In the latter
1064 * case, we refer to this as a dummy group.
1065 *
1066 */
1067 private class SpringOpenGroup implements NextGroup {
1068 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001069 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001070 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001071
Saurav Das8a0732e2015-11-20 15:27:53 -08001072 /**
1073 * Storage for a GroupKey or a TrafficTreatment. One of the params
1074 * to this constructor must be null.
1075 * @param key represents a GroupKey
1076 * @param treatment represents flow actions in a dummy group
1077 */
1078 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1079 if (key == null) {
1080 this.key = new DefaultGroupKey(new byte[]{0});
1081 this.treatment = treatment;
1082 this.dummy = true;
1083 } else {
1084 this.key = key;
1085 this.treatment = DefaultTrafficTreatment.builder().build();
1086 this.dummy = false;
1087 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001088 }
1089
Saurav Das822c4e22015-10-23 10:51:11 -07001090 @SuppressWarnings("unused")
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001091 public GroupKey key() {
1092 return key;
1093 }
1094
1095 @Override
1096 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001097 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001098 }
1099
1100 }
1101}