blob: 1277c75f3c5ac78df78ede647d4a9271f6873775 [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;
Charles Chan188ebf52015-12-23 00:15:11 -080054import org.onosproject.net.flow.criteria.MplsBosCriterion;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070055import org.onosproject.net.flow.criteria.MplsCriterion;
56import org.onosproject.net.flow.criteria.PortCriterion;
57import org.onosproject.net.flow.criteria.VlanIdCriterion;
58import org.onosproject.net.flow.instructions.Instruction;
Saurav Das822c4e22015-10-23 10:51:11 -070059import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chan68aa62d2015-11-09 16:37:23 -080060import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070061import org.onosproject.net.flowobjective.FilteringObjective;
62import org.onosproject.net.flowobjective.FlowObjectiveStore;
63import org.onosproject.net.flowobjective.ForwardingObjective;
64import org.onosproject.net.flowobjective.NextObjective;
65import org.onosproject.net.flowobjective.Objective;
66import org.onosproject.net.flowobjective.ObjectiveError;
67import org.onosproject.net.group.DefaultGroupBucket;
68import org.onosproject.net.group.DefaultGroupDescription;
69import org.onosproject.net.group.DefaultGroupKey;
70import org.onosproject.net.group.Group;
71import org.onosproject.net.group.GroupBucket;
72import org.onosproject.net.group.GroupBuckets;
73import org.onosproject.net.group.GroupDescription;
74import org.onosproject.net.group.GroupEvent;
75import org.onosproject.net.group.GroupKey;
76import org.onosproject.net.group.GroupListener;
77import org.onosproject.net.group.GroupService;
Saurav Das8a0732e2015-11-20 15:27:53 -080078import org.onosproject.store.serializers.KryoNamespaces;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070079import org.slf4j.Logger;
80
81import java.util.ArrayList;
82import java.util.Collection;
83import java.util.Collections;
84import java.util.List;
Sho SHIMIZU45906042016-01-13 23:05:54 -080085import java.util.Objects;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070086import java.util.Set;
87import java.util.concurrent.Executors;
88import java.util.concurrent.ScheduledExecutorService;
89import java.util.concurrent.TimeUnit;
90import java.util.stream.Collectors;
91
92/**
93 * Driver for SPRING-OPEN pipeline.
94 */
95public class SpringOpenTTP extends AbstractHandlerBehaviour
96 implements Pipeliner {
97
98 // Default table ID - compatible with CpqD switch
99 private static final int TABLE_VLAN = 0;
100 private static final int TABLE_TMAC = 1;
101 private static final int TABLE_IPV4_UNICAST = 2;
102 private static final int TABLE_MPLS = 3;
Charles Chan68aa62d2015-11-09 16:37:23 -0800103 private static final int TABLE_DMAC = 4;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700104 private static final int TABLE_ACL = 5;
Charles Chan68aa62d2015-11-09 16:37:23 -0800105 private static final int TABLE_SMAC = 6;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700106
107 /**
108 * Set the default values. These variables will get overwritten based on the
109 * switch vendor type
110 */
111 protected int vlanTableId = TABLE_VLAN;
112 protected int tmacTableId = TABLE_TMAC;
113 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
114 protected int mplsTableId = TABLE_MPLS;
Charles Chan68aa62d2015-11-09 16:37:23 -0800115 protected int dstMacTableId = TABLE_DMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700116 protected int aclTableId = TABLE_ACL;
Charles Chan68aa62d2015-11-09 16:37:23 -0800117 protected int srcMacTableId = TABLE_SMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700118
119 protected final Logger log = getLogger(getClass());
120
121 private ServiceDirectory serviceDirectory;
122 private FlowRuleService flowRuleService;
123 private CoreService coreService;
124 protected GroupService groupService;
125 protected FlowObjectiveStore flowObjectiveStore;
126 protected DeviceId deviceId;
127 private ApplicationId appId;
128
129 private Cache<GroupKey, NextObjective> pendingGroups;
130
131 private ScheduledExecutorService groupChecker = Executors
132 .newScheduledThreadPool(2,
133 groupedThreads("onos/pipeliner",
134 "spring-open-%d"));
135 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das8a0732e2015-11-20 15:27:53 -0800136 .register(KryoNamespaces.API)
137 .register(GroupKey.class)
138 .register(DefaultGroupKey.class)
139 .register(TrafficTreatment.class)
140 .register(SpringOpenGroup.class)
141 .register(byte[].class)
142 .build();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700143
144 @Override
145 public void init(DeviceId deviceId, PipelinerContext context) {
146 this.serviceDirectory = context.directory();
147 this.deviceId = deviceId;
148
149 pendingGroups = CacheBuilder
150 .newBuilder()
151 .expireAfterWrite(20, TimeUnit.SECONDS)
152 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
153 if (notification.getCause() == RemovalCause.EXPIRED) {
154 fail(notification.getValue(),
155 ObjectiveError.GROUPINSTALLATIONFAILED);
156 }
157 }).build();
158
159 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500,
160 TimeUnit.MILLISECONDS);
161
162 coreService = serviceDirectory.get(CoreService.class);
163 flowRuleService = serviceDirectory.get(FlowRuleService.class);
164 groupService = serviceDirectory.get(GroupService.class);
165 flowObjectiveStore = context.store();
166
167 groupService.addListener(new InnerGroupListener());
168
169 appId = coreService
170 .registerApplication("org.onosproject.driver.SpringOpenTTP");
171
172 setTableMissEntries();
173 log.info("Spring Open TTP driver initialized");
174 }
175
176 @Override
177 public void filter(FilteringObjective filteringObjective) {
178 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
179 log.debug("processing PERMIT filter objective");
180 processFilter(filteringObjective,
181 filteringObjective.op() == Objective.Operation.ADD,
182 filteringObjective.appId());
183 } else {
184 log.debug("filter objective other than PERMIT not supported");
185 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
186 }
187 }
188
189 @Override
190 public void forward(ForwardingObjective fwd) {
191 Collection<FlowRule> rules;
192 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
193
194 rules = processForward(fwd);
195 switch (fwd.op()) {
196 case ADD:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800197 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700198 .forEach(flowBuilder::add);
199 break;
200 case REMOVE:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800201 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700202 .forEach(flowBuilder::remove);
203 break;
204 default:
205 fail(fwd, ObjectiveError.UNKNOWN);
206 log.warn("Unknown forwarding type {}", fwd.op());
207 }
208
209 flowRuleService.apply(flowBuilder
210 .build(new FlowRuleOperationsContext() {
211 @Override
212 public void onSuccess(FlowRuleOperations ops) {
213 pass(fwd);
Saurav Das8a0732e2015-11-20 15:27:53 -0800214 log.debug("Provisioned tables in {} successfully with "
215 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700216 }
217
218 @Override
219 public void onError(FlowRuleOperations ops) {
220 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700221 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800222 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700223 }
224 }));
225
226 }
227
228 @Override
229 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800230 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
231 switch (nextObjective.op()) {
232 case ADD:
sangho834e4b02015-05-01 09:38:25 -0700233 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800234 log.warn("Cannot add next {} that already exists in device {}",
235 nextObjective.id(), deviceId);
236 return;
237 }
238 log.debug("Processing NextObjective id{} in dev{} - add group",
239 nextObjective.id(), deviceId);
240 addGroup(nextObjective);
241 break;
242 case ADD_TO_EXISTING:
243 if (nextGroup != null) {
244 log.debug("Processing NextObjective id{} in dev{} - add bucket",
245 nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700246 addBucketToGroup(nextObjective);
247 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800248 log.warn("Cannot add to group that does not exist");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700249 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800250 break;
251 case REMOVE:
252 if (nextGroup == null) {
253 log.warn("Cannot remove next {} that does not exist in device {}",
254 nextObjective.id(), deviceId);
255 return;
256 }
257 log.debug("Processing NextObjective id{} in dev{} - remove group",
258 nextObjective.id(), deviceId);
259 removeGroup(nextObjective);
260 break;
261 case REMOVE_FROM_EXISTING:
262 if (nextGroup == null) {
263 log.warn("Cannot remove from next {} that does not exist in device {}",
264 nextObjective.id(), deviceId);
265 return;
266 }
267 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
268 nextObjective.id(), deviceId);
269 removeBucketFromGroup(nextObjective);
270 break;
271 default:
sangho834e4b02015-05-01 09:38:25 -0700272 log.warn("Unsupported operation {}", nextObjective.op());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700273 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700274 }
275
sangho834e4b02015-05-01 09:38:25 -0700276 private void removeGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700277 log.debug("removeGroup in {}: for next objective id {}",
278 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700279 final GroupKey key = new DefaultGroupKey(
280 appKryo.serialize(nextObjective.id()));
281 groupService.removeGroup(deviceId, key, appId);
282 }
283
284 private void addGroup(NextObjective nextObjective) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700285 log.debug("addGroup with type{} for nextObjective id {}",
286 nextObjective.type(), nextObjective.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700287 List<GroupBucket> buckets;
sangho834e4b02015-05-01 09:38:25 -0700288 switch (nextObjective.type()) {
289 case SIMPLE:
sangho834e4b02015-05-01 09:38:25 -0700290 Collection<TrafficTreatment> treatments = nextObjective.next();
291 if (treatments.size() == 1) {
Saurav Das4ce45962015-11-24 23:21:05 -0800292 // Spring Open TTP converts simple nextObjective to flow-actions
293 // in a dummy group
294 TrafficTreatment treatment = nextObjective.next().iterator().next();
295 log.debug("Converting SIMPLE group for next objective id {} " +
296 "to {} flow-actions in device:{}", nextObjective.id(),
297 treatment.allInstructions().size(), deviceId);
298 flowObjectiveStore.putNextGroup(nextObjective.id(),
299 new SpringOpenGroup(null, treatment));
sangho834e4b02015-05-01 09:38:25 -0700300 }
301 break;
302 case HASHED:
Saurav Das8a0732e2015-11-20 15:27:53 -0800303 // we convert MPLS ECMP groups to flow-actions for a single
304 // bucket(output port).
305 boolean mplsEcmp = false;
306 if (nextObjective.meta() != null) {
307 for (Criterion c : nextObjective.meta().criteria()) {
308 if (c.type() == Type.MPLS_LABEL) {
309 mplsEcmp = true;
310 }
311 }
312 }
313 if (mplsEcmp) {
314 // covert to flow-actions in a dummy group by choosing the first bucket
315 log.debug("Converting HASHED group for next objective id {} " +
316 "to flow-actions in device:{}", nextObjective.id(),
317 deviceId);
318 TrafficTreatment treatment = nextObjective.next().iterator().next();
319 flowObjectiveStore.putNextGroup(nextObjective.id(),
320 new SpringOpenGroup(null, treatment));
321 } else {
322 // process as ECMP group
323 buckets = nextObjective
324 .next()
325 .stream()
326 .map((treatment) -> DefaultGroupBucket
327 .createSelectGroupBucket(treatment))
328 .collect(Collectors.toList());
329 if (!buckets.isEmpty()) {
330 final GroupKey key = new DefaultGroupKey(
331 appKryo.serialize(nextObjective.id()));
332 GroupDescription groupDescription = new DefaultGroupDescription(
333 deviceId,
334 GroupDescription.Type.SELECT,
335 new GroupBuckets(buckets),
336 key,
337 null,
338 nextObjective.appId());
339 log.debug("Creating HASHED group for next objective id {}"
340 + " in dev:{}", nextObjective.id(), deviceId);
341 pendingGroups.put(key, nextObjective);
342 groupService.addGroup(groupDescription);
343 }
sangho834e4b02015-05-01 09:38:25 -0700344 }
345 break;
346 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700347 buckets = nextObjective
348 .next()
349 .stream()
350 .map((treatment) -> DefaultGroupBucket
351 .createAllGroupBucket(treatment))
352 .collect(Collectors.toList());
353 if (!buckets.isEmpty()) {
354 final GroupKey key = new DefaultGroupKey(
355 appKryo.serialize(nextObjective
356 .id()));
357 GroupDescription groupDescription = new DefaultGroupDescription(
358 deviceId,
359 GroupDescription.Type.ALL,
360 new GroupBuckets(buckets),
361 key,
362 null,
363 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800364 log.debug("Creating BROADCAST group for next objective id {} "
365 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700366 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800367 groupService.addGroup(groupDescription);
Charles Chanc42e84e2015-10-20 16:24:19 -0700368 }
369 break;
sangho834e4b02015-05-01 09:38:25 -0700370 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700371 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700372 fail(nextObjective, ObjectiveError.UNSUPPORTED);
373 log.warn("Unsupported next objective type {}", nextObjective.type());
374 break;
375 default:
376 fail(nextObjective, ObjectiveError.UNKNOWN);
377 log.warn("Unknown next objective type {}", nextObjective.type());
378 }
379 }
380
381 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700382 log.debug("addBucketToGroup in {}: for next objective id {}",
383 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700384 Collection<TrafficTreatment> treatments = nextObjective.next();
385 TrafficTreatment treatment = treatments.iterator().next();
386 final GroupKey key = new DefaultGroupKey(
387 appKryo.serialize(nextObjective
388 .id()));
389 Group group = groupService.getGroup(deviceId, key);
390 if (group == null) {
391 log.warn("Group is not found in {} for {}", deviceId, key);
392 return;
393 }
394 GroupBucket bucket;
395 if (group.type() == GroupDescription.Type.INDIRECT) {
396 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
397 } else if (group.type() == GroupDescription.Type.SELECT) {
398 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700399 } else if (group.type() == GroupDescription.Type.ALL) {
400 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700401 } else {
402 log.warn("Unsupported Group type {}", group.type());
403 return;
404 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700405 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700406 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
407 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700408 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
409 }
410
411 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700412 log.debug("removeBucketFromGroup in {}: for next objective id {}",
413 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700414 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
415 if (nextGroup != null) {
416 Collection<TrafficTreatment> treatments = nextObjective.next();
417 TrafficTreatment treatment = treatments.iterator().next();
418 final GroupKey key = new DefaultGroupKey(
419 appKryo.serialize(nextObjective
420 .id()));
421 Group group = groupService.getGroup(deviceId, key);
422 if (group == null) {
423 log.warn("Group is not found in {} for {}", deviceId, key);
424 return;
425 }
426 GroupBucket bucket;
427 if (group.type() == GroupDescription.Type.INDIRECT) {
428 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
429 } else if (group.type() == GroupDescription.Type.SELECT) {
430 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700431 } else if (group.type() == GroupDescription.Type.ALL) {
432 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700433 } else {
434 log.warn("Unsupported Group type {}", group.type());
435 return;
436 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700437 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700438 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
439 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700440 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
441 }
442 }
443
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700444 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
445 switch (fwd.flag()) {
446 case SPECIFIC:
447 return processSpecific(fwd);
448 case VERSATILE:
449 return processVersatile(fwd);
450 default:
451 fail(fwd, ObjectiveError.UNKNOWN);
452 log.warn("Unknown forwarding flag {}", fwd.flag());
453 }
454 return Collections.emptySet();
455 }
456
457 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800458 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700459 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700460 EthTypeCriterion ethType =
461 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
462 if (ethType == null) {
463 log.error("Versatile forwarding objective must include ethType");
464 fail(fwd, ObjectiveError.UNKNOWN);
465 return Collections.emptySet();
466 }
467
Saurav Das8a0732e2015-11-20 15:27:53 -0800468 if (fwd.treatment() == null && fwd.nextId() == null) {
469 log.error("VERSATILE forwarding objective needs next objective ID "
470 + "or treatment.");
471 return Collections.emptySet();
472 }
473 // emulation of ACL table (for versatile fwd objective) requires
474 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700475 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
476 .builder();
477 treatmentBuilder.wipeDeferred();
478
479 if (fwd.nextId() != null) {
480 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700481 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800482 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
483 if (soGroup.dummy) {
484 // need to convert to flow-actions
485 for (Instruction ins : soGroup.treatment.allInstructions()) {
486 treatmentBuilder.add(ins);
487 }
488 } else {
489 GroupKey key = soGroup.key;
490 Group group = groupService.getGroup(deviceId, key);
491 if (group == null) {
492 log.warn("The group left!");
493 fail(fwd, ObjectiveError.GROUPMISSING);
494 return Collections.emptySet();
495 }
496 treatmentBuilder.deferred().group(group.id());
497 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700498 }
sangho1e575652015-05-14 00:39:53 -0700499 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800500 }
501
502 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700503 if (fwd.treatment().allInstructions().size() == 1 &&
504 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
505 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
506 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800507 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800508 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800509 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700510 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800511 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800512 for (Instruction ins : fwd.treatment().allInstructions()) {
513 treatmentBuilder.add(ins);
514 }
Saurav Das822c4e22015-10-23 10:51:11 -0700515 }
sangho1e575652015-05-14 00:39:53 -0700516 }
517
sangho1e575652015-05-14 00:39:53 -0700518 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
519 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700520 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800521 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700522
523 if (fwd.permanent()) {
524 ruleBuilder.makePermanent();
525 } else {
526 ruleBuilder.makeTemporary(fwd.timeout());
527 }
528
529 ruleBuilder.forTable(aclTableId);
530 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700531 }
532
Charles Chan68aa62d2015-11-09 16:37:23 -0800533 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700534 TrafficSelector selector = fwd.selector();
535 EthTypeCriterion ethType = (EthTypeCriterion) selector
536 .getCriterion(Criterion.Type.ETH_TYPE);
537 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800538 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
539 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
540 return false;
541 }
542 return true;
543 }
544
545 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
546 TrafficSelector selector = fwd.selector();
547 EthCriterion ethDst = (EthCriterion) selector
548 .getCriterion(Criterion.Type.ETH_DST);
549 VlanIdCriterion vlanId = (VlanIdCriterion) selector
550 .getCriterion(Criterion.Type.VLAN_VID);
551 if (ethDst == null && vlanId == null) {
552 return false;
553 }
554 return true;
555 }
556
557 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800558 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
559 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800560 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
561 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
562
563 if (isEthTypeObj) {
564 return processEthTypeSpecificObjective(fwd);
565 } else if (isEthDstObj) {
566 return processEthDstSpecificObjective(fwd);
567 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700568 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800569 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700570 fail(fwd, ObjectiveError.UNSUPPORTED);
571 return Collections.emptySet();
572 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800573 }
574
575 protected Collection<FlowRule>
576 processEthTypeSpecificObjective(ForwardingObjective fwd) {
577 TrafficSelector selector = fwd.selector();
578 EthTypeCriterion ethType = (EthTypeCriterion) selector
579 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700580
581 TrafficSelector.Builder filteredSelectorBuilder =
582 DefaultTrafficSelector.builder();
583 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700584 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700585 filteredSelectorBuilder = filteredSelectorBuilder
586 .matchEthType(Ethernet.TYPE_IPV4)
587 .matchIPDst(((IPCriterion) selector
588 .getCriterion(Criterion.Type.IPV4_DST))
589 .ip());
590 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800591 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
592 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700593 } else {
594 filteredSelectorBuilder = filteredSelectorBuilder
595 .matchEthType(Ethernet.MPLS_UNICAST)
596 .matchMplsLabel(((MplsCriterion)
597 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
Charles Chan188ebf52015-12-23 00:15:11 -0800598 if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
599 filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion)
600 selector.getCriterion(Type.MPLS_BOS)).mplsBos());
601 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700602 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800603 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
604 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700605 }
606
607 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
608 .builder();
609 if (fwd.treatment() != null) {
610 for (Instruction i : fwd.treatment().allInstructions()) {
611 treatmentBuilder.add(i);
612 }
613 }
614
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700615 if (fwd.nextId() != null) {
616 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700617 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800618 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
619 if (soGroup.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800620 log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
621 + "in dev: {}", soGroup.treatment.allInstructions().size(),
622 fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800623 for (Instruction ins : soGroup.treatment.allInstructions()) {
624 treatmentBuilder.add(ins);
625 }
626 } else {
627 GroupKey key = soGroup.key;
628 Group group = groupService.getGroup(deviceId, key);
629 if (group == null) {
630 log.warn("The group left!");
631 fail(fwd, ObjectiveError.GROUPMISSING);
632 return Collections.emptySet();
633 }
634 treatmentBuilder.deferred().group(group.id());
635 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
Saurav Das4ce45962015-11-24 23:21:05 -0800636 + "for next:{} in dev: {}", group.id(), fwd.id(),
637 fwd.nextId(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700638 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700639 } else {
640 log.warn("processSpecific: No associated next objective object");
641 fail(fwd, ObjectiveError.GROUPMISSING);
642 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700643 }
644 }
645
646 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
647 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
648 .build();
649
650 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
651 .fromApp(fwd.appId()).withPriority(fwd.priority())
652 .forDevice(deviceId).withSelector(filteredSelector)
653 .withTreatment(treatment);
654
655 if (fwd.permanent()) {
656 ruleBuilder.makePermanent();
657 } else {
658 ruleBuilder.makeTemporary(fwd.timeout());
659 }
660
661 ruleBuilder.forTable(forTableId);
662 return Collections.singletonList(ruleBuilder.build());
663
664 }
665
Charles Chan68aa62d2015-11-09 16:37:23 -0800666 protected Collection<FlowRule>
667 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700668 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800669
670 // Build filtered selector
671 TrafficSelector selector = fwd.selector();
672 EthCriterion ethCriterion = (EthCriterion) selector
673 .getCriterion(Criterion.Type.ETH_DST);
674 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
675 .getCriterion(Criterion.Type.VLAN_VID);
676 TrafficSelector.Builder filteredSelectorBuilder =
677 DefaultTrafficSelector.builder();
678 // Do not match MacAddress for subnet broadcast entry
679 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
680 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800681 log.debug("processing L2 forwarding objective:{} in dev:{}",
682 fwd.id(), deviceId);
683 } else {
684 log.debug("processing L2 Broadcast forwarding objective:{} "
685 + "in dev:{} for vlan:{}",
686 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800687 }
688 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
689 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
690
691 // Build filtered treatment
692 TrafficTreatment.Builder treatmentBuilder =
693 DefaultTrafficTreatment.builder();
694 if (fwd.treatment() != null) {
695 treatmentBuilder.deferred();
696 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
697 }
698 if (fwd.nextId() != null) {
699 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
700 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800701 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
702 if (soGrp.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800703 log.debug("Adding {} flow-actions for fwd. obj. {} "
704 + "in dev: {}", soGrp.treatment.allInstructions().size(),
705 fwd.id(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800706 for (Instruction ins : soGrp.treatment.allInstructions()) {
Saurav Das4ce45962015-11-24 23:21:05 -0800707 treatmentBuilder.deferred().add(ins);
Saurav Das8a0732e2015-11-20 15:27:53 -0800708 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800709 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800710 GroupKey key = soGrp.key;
711 Group group = groupService.getGroup(deviceId, key);
712 if (group == null) {
713 log.warn("The group left!");
714 fail(fwd, ObjectiveError.GROUPMISSING);
715 return Collections.emptySet();
716 }
717 treatmentBuilder.deferred().group(group.id());
718 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
719 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800720 }
721 }
722 }
723 treatmentBuilder.immediate().transition(aclTableId);
724 TrafficTreatment filteredTreatment = treatmentBuilder.build();
725
726 // Build bridging table entries
727 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
728 flowRuleBuilder.fromApp(fwd.appId())
729 .withPriority(fwd.priority())
730 .forDevice(deviceId)
731 .withSelector(filteredSelector)
732 .withTreatment(filteredTreatment)
733 .forTable(dstMacTableId);
734 if (fwd.permanent()) {
735 flowRuleBuilder.makePermanent();
736 } else {
737 flowRuleBuilder.makeTemporary(fwd.timeout());
738 }
739 rules.add(flowRuleBuilder.build());
740
741 /*
742 // TODO Emulate source MAC table behavior
743 // Do not install source MAC table entry for subnet broadcast
744 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
745 // Build filtered selector
746 selector = fwd.selector();
747 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
748 filteredSelectorBuilder = DefaultTrafficSelector.builder();
749 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
750 filteredSelector = filteredSelectorBuilder.build();
751
752 // Build empty treatment. Apply existing instruction if match.
753 treatmentBuilder = DefaultTrafficTreatment.builder();
754 filteredTreatment = treatmentBuilder.build();
755
756 // Build bridging table entries
757 flowRuleBuilder = DefaultFlowRule.builder();
758 flowRuleBuilder.fromApp(fwd.appId())
759 .withPriority(fwd.priority())
760 .forDevice(deviceId)
761 .withSelector(filteredSelector)
762 .withTreatment(filteredTreatment)
763 .forTable(srcMacTableId)
764 .makePermanent();
765 rules.add(flowRuleBuilder.build());
766 }
767 */
768
769 return rules;
770 }
771
Saurav Das4ce45962015-11-24 23:21:05 -0800772 /*
773 * Note: CpqD switches do not handle MPLS-related operation properly
774 * for a packet with VLAN tag. We pop VLAN here as a workaround.
775 * Side effect: HostService learns redundant hosts with same MAC but
776 * different VLAN. No known side effect on the network reachability.
777 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800778 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
779 VlanIdCriterion vlanIdCriterion,
780 FilteringObjective filt,
781 VlanId assignedVlan,
782 ApplicationId applicationId) {
783 //handling untagged packets via assigned VLAN
784 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
785 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
786 }
787
Charles Chan68aa62d2015-11-09 16:37:23 -0800788 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700789 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
790 .builder();
791 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
792 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800793 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700794 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800795 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
796 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700797 treatmentIp.transition(ipv4UnicastTableId);
798 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
799 .withSelector(selectorIp.build())
800 .withTreatment(treatmentIp.build())
801 .withPriority(filt.priority()).fromApp(applicationId)
802 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800803 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700804 rules.add(ruleIp);
805
806 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
807 .builder();
808 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
809 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800810 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700811 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800812 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
813 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700814 treatmentMpls.transition(mplsTableId);
815 FlowRule ruleMpls = DefaultFlowRule.builder()
816 .forDevice(deviceId).withSelector(selectorMpls.build())
817 .withTreatment(treatmentMpls.build())
818 .withPriority(filt.priority()).fromApp(applicationId)
819 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800820 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700821 rules.add(ruleMpls);
822
823 return rules;
824 }
825
Charles Chan68aa62d2015-11-09 16:37:23 -0800826 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700827 FilteringObjective filt,
Charles Chan68aa62d2015-11-09 16:37:23 -0800828 VlanId assignedVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700829 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700830 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800831 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700832 TrafficSelector.Builder selector = DefaultTrafficSelector
833 .builder();
834 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
835 .builder();
836 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800837 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
838 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700839 selector.matchInPort(p.port());
Charles Chan68aa62d2015-11-09 16:37:23 -0800840 } else {
841 selector.matchInPort(p.port());
842 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700843 }
844 treatment.transition(tmacTableId);
845 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
846 .withSelector(selector.build())
847 .withTreatment(treatment.build())
848 .withPriority(filt.priority()).fromApp(applicationId)
849 .makePermanent().forTable(vlanTableId).build();
850 rules.add(rule);
851
852 return rules;
853 }
854
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700855 private void processFilter(FilteringObjective filt, boolean install,
856 ApplicationId applicationId) {
857 // This driver only processes filtering criteria defined with switch
858 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700859 if (filt.key().equals(Criteria.dummy())
860 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700861 log.warn("No key defined in filtering objective from app: {}. Not"
862 + "processing filtering objective", applicationId);
863 fail(filt, ObjectiveError.UNKNOWN);
864 return;
865 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800866
867 EthCriterion ethCriterion = null;
868 VlanIdCriterion vlanIdCriterion = null;
869
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700870 // convert filtering conditions for switch-intfs into flowrules
871 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800872
873 for (Criterion criterion : filt.conditions()) {
874 if (criterion.type() == Criterion.Type.ETH_DST) {
875 ethCriterion = (EthCriterion) criterion;
876 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
877 vlanIdCriterion = (VlanIdCriterion) criterion;
878 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700879 log.debug("driver does not process IP filtering rules as it " +
880 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700881 } else {
882 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800883 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700884 fail(filt, ObjectiveError.UNSUPPORTED);
885 }
886 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800887
888 VlanId assignedVlan = null;
Charles Chane849c192016-01-11 18:28:54 -0800889 if (vlanIdCriterion != null) {
890 // For VLAN cross-connect packets, use the configured VLAN
891 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
892 assignedVlan = vlanIdCriterion.vlanId();
893
894 // For untagged packets, assign a VLAN ID
895 } else {
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;
Charles Chan68aa62d2015-11-09 16:37:23 -0800901 }
Charles Chane849c192016-01-11 18:28:54 -0800902 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 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800914 }
915 }
916
917 if (ethCriterion == null) {
918 log.debug("filtering objective missing dstMac, cannot program TMAC table");
919 } else {
920 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
921 vlanIdCriterion,
922 filt,
923 assignedVlan,
924 applicationId)) {
925 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
926 tmacRule, deviceId);
927 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
928 }
929 }
930
Charles Chane849c192016-01-11 18:28:54 -0800931 if (vlanIdCriterion == null) {
932 log.debug("filtering objective missing VLAN ID criterion, "
933 + "cannot program VLAN Table");
Charles Chan68aa62d2015-11-09 16:37:23 -0800934 } else {
935 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
936 filt,
937 assignedVlan,
938 applicationId)) {
939 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
940 vlanRule, deviceId);
941 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
942 }
943 }
944
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700945 // apply filtering flow rules
946 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
947 @Override
948 public void onSuccess(FlowRuleOperations ops) {
949 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700950 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -0800951 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700952 }
953
954 @Override
955 public void onError(FlowRuleOperations ops) {
956 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700957 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800958 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700959 }
960 }));
961 }
962
963 protected void setTableMissEntries() {
964 // set all table-miss-entries
965 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -0800966 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
967 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700968 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800969 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700970 populateTableMissEntry(aclTableId, false, false, false, -1);
971 }
972
973 protected void populateTableMissEntry(int tableToAdd,
974 boolean toControllerNow,
975 boolean toControllerWrite,
976 boolean toTable, int tableToSend) {
977 TrafficSelector selector = DefaultTrafficSelector.builder().build();
978 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
979
980 if (toControllerNow) {
981 tBuilder.setOutput(PortNumber.CONTROLLER);
982 }
983
984 if (toControllerWrite) {
985 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
986 }
987
988 if (toTable) {
989 tBuilder.transition(tableToSend);
990 }
991
992 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
993 .withSelector(selector).withTreatment(tBuilder.build())
994 .withPriority(0).fromApp(appId).makePermanent()
995 .forTable(tableToAdd).build();
996
997 flowRuleService.applyFlowRules(flow);
998 }
999
1000 private void pass(Objective obj) {
1001 if (obj.context().isPresent()) {
1002 obj.context().get().onSuccess(obj);
1003 }
1004 }
1005
1006 protected void fail(Objective obj, ObjectiveError error) {
1007 if (obj.context().isPresent()) {
1008 obj.context().get().onError(obj, error);
1009 }
1010 }
1011
1012 private class InnerGroupListener implements GroupListener {
1013 @Override
1014 public void event(GroupEvent event) {
1015 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001016 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001017 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001018 GroupKey key = event.subject().appCookie();
1019
1020 NextObjective obj = pendingGroups.getIfPresent(key);
1021 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001022 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1023 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001024 flowObjectiveStore
1025 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001026 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001027 pass(obj);
1028 pendingGroups.invalidate(key);
1029 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001030 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1031 log.warn("InnerGroupListener: Group ADD "
1032 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001033 }
1034 }
1035 }
1036
1037 private class GroupChecker implements Runnable {
1038
1039 @Override
1040 public void run() {
1041 Set<GroupKey> keys = pendingGroups
1042 .asMap()
1043 .keySet()
1044 .stream()
1045 .filter(key -> groupService.getGroup(deviceId, key) != null)
1046 .collect(Collectors.toSet());
1047
1048 keys.stream()
1049 .forEach(key -> {
1050 NextObjective obj = pendingGroups
1051 .getIfPresent(key);
1052 if (obj == null) {
1053 return;
1054 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001055 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1056 deviceId,
1057 groupService.getGroup(deviceId, key).id(),
1058 obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001059 pass(obj);
1060 pendingGroups.invalidate(key);
Saurav Das8a0732e2015-11-20 15:27:53 -08001061 flowObjectiveStore.putNextGroup(
1062 obj.id(),
1063 new SpringOpenGroup(key, null));
1064 });
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001065 }
1066 }
1067
Saurav Das8a0732e2015-11-20 15:27:53 -08001068 /**
1069 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1070 * used to fetch the group from the Group Service, or it can be serve as storage
1071 * for Traffic Treatments which can be used as flow actions. In the latter
1072 * case, we refer to this as a dummy group.
1073 *
1074 */
1075 private class SpringOpenGroup implements NextGroup {
1076 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001077 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001078 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001079
Saurav Das8a0732e2015-11-20 15:27:53 -08001080 /**
1081 * Storage for a GroupKey or a TrafficTreatment. One of the params
1082 * to this constructor must be null.
1083 * @param key represents a GroupKey
1084 * @param treatment represents flow actions in a dummy group
1085 */
1086 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1087 if (key == null) {
1088 this.key = new DefaultGroupKey(new byte[]{0});
1089 this.treatment = treatment;
1090 this.dummy = true;
1091 } else {
1092 this.key = key;
1093 this.treatment = DefaultTrafficTreatment.builder().build();
1094 this.dummy = false;
1095 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001096 }
1097
Saurav Das822c4e22015-10-23 10:51:11 -07001098 @SuppressWarnings("unused")
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001099 public GroupKey key() {
1100 return key;
1101 }
1102
1103 @Override
1104 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001105 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001106 }
1107
1108 }
1109}