blob: 6dab2967d98746c4f8e791297ee22e5c5ff1b257 [file] [log] [blame]
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.driver.pipeline;
17
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
Charles Chan5270ed02016-01-30 23:22:37 -080026import com.google.common.collect.ImmutableList;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070027import org.onlab.osgi.ServiceDirectory;
28import org.onlab.packet.Ethernet;
Charles Chan68aa62d2015-11-09 16:37:23 -080029import org.onlab.packet.MacAddress;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070030import org.onlab.packet.VlanId;
31import org.onlab.util.KryoNamespace;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.behaviour.NextGroup;
37import org.onosproject.net.behaviour.Pipeliner;
38import org.onosproject.net.behaviour.PipelinerContext;
39import org.onosproject.net.driver.AbstractHandlerBehaviour;
40import org.onosproject.net.flow.DefaultFlowRule;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.FlowRule;
44import org.onosproject.net.flow.FlowRuleOperations;
45import org.onosproject.net.flow.FlowRuleOperationsContext;
46import org.onosproject.net.flow.FlowRuleService;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.flow.criteria.Criteria;
50import org.onosproject.net.flow.criteria.Criterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080051import org.onosproject.net.flow.criteria.Criterion.Type;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070052import org.onosproject.net.flow.criteria.EthCriterion;
53import org.onosproject.net.flow.criteria.EthTypeCriterion;
54import org.onosproject.net.flow.criteria.IPCriterion;
Charles Chan188ebf52015-12-23 00:15:11 -080055import org.onosproject.net.flow.criteria.MplsBosCriterion;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070056import org.onosproject.net.flow.criteria.MplsCriterion;
57import org.onosproject.net.flow.criteria.PortCriterion;
58import org.onosproject.net.flow.criteria.VlanIdCriterion;
59import org.onosproject.net.flow.instructions.Instruction;
Saurav Das822c4e22015-10-23 10:51:11 -070060import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chan68aa62d2015-11-09 16:37:23 -080061import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070062import org.onosproject.net.flowobjective.FilteringObjective;
63import org.onosproject.net.flowobjective.FlowObjectiveStore;
64import org.onosproject.net.flowobjective.ForwardingObjective;
65import org.onosproject.net.flowobjective.NextObjective;
66import org.onosproject.net.flowobjective.Objective;
67import org.onosproject.net.flowobjective.ObjectiveError;
68import org.onosproject.net.group.DefaultGroupBucket;
69import org.onosproject.net.group.DefaultGroupDescription;
70import org.onosproject.net.group.DefaultGroupKey;
71import org.onosproject.net.group.Group;
72import org.onosproject.net.group.GroupBucket;
73import org.onosproject.net.group.GroupBuckets;
74import org.onosproject.net.group.GroupDescription;
75import org.onosproject.net.group.GroupEvent;
76import org.onosproject.net.group.GroupKey;
77import org.onosproject.net.group.GroupListener;
78import org.onosproject.net.group.GroupService;
Saurav Das8a0732e2015-11-20 15:27:53 -080079import org.onosproject.store.serializers.KryoNamespaces;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070080import org.slf4j.Logger;
81
82import java.util.ArrayList;
83import java.util.Collection;
84import java.util.Collections;
85import java.util.List;
Sho SHIMIZU45906042016-01-13 23:05:54 -080086import java.util.Objects;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070087import java.util.Set;
88import java.util.concurrent.Executors;
89import java.util.concurrent.ScheduledExecutorService;
90import java.util.concurrent.TimeUnit;
91import java.util.stream.Collectors;
92
93/**
94 * Driver for SPRING-OPEN pipeline.
95 */
96public class SpringOpenTTP extends AbstractHandlerBehaviour
97 implements Pipeliner {
98
99 // Default table ID - compatible with CpqD switch
100 private static final int TABLE_VLAN = 0;
101 private static final int TABLE_TMAC = 1;
102 private static final int TABLE_IPV4_UNICAST = 2;
103 private static final int TABLE_MPLS = 3;
Charles Chan68aa62d2015-11-09 16:37:23 -0800104 private static final int TABLE_DMAC = 4;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700105 private static final int TABLE_ACL = 5;
Charles Chan68aa62d2015-11-09 16:37:23 -0800106 private static final int TABLE_SMAC = 6;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700107
108 /**
109 * Set the default values. These variables will get overwritten based on the
110 * switch vendor type
111 */
112 protected int vlanTableId = TABLE_VLAN;
113 protected int tmacTableId = TABLE_TMAC;
114 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
115 protected int mplsTableId = TABLE_MPLS;
Charles Chan68aa62d2015-11-09 16:37:23 -0800116 protected int dstMacTableId = TABLE_DMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700117 protected int aclTableId = TABLE_ACL;
Charles Chan68aa62d2015-11-09 16:37:23 -0800118 protected int srcMacTableId = TABLE_SMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700119
120 protected final Logger log = getLogger(getClass());
121
122 private ServiceDirectory serviceDirectory;
123 private FlowRuleService flowRuleService;
124 private CoreService coreService;
125 protected GroupService groupService;
126 protected FlowObjectiveStore flowObjectiveStore;
127 protected DeviceId deviceId;
128 private ApplicationId appId;
129
130 private Cache<GroupKey, NextObjective> pendingGroups;
131
132 private ScheduledExecutorService groupChecker = Executors
133 .newScheduledThreadPool(2,
134 groupedThreads("onos/pipeliner",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700135 "spring-open-%d",
136 log));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700137 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das8a0732e2015-11-20 15:27:53 -0800138 .register(KryoNamespaces.API)
139 .register(GroupKey.class)
140 .register(DefaultGroupKey.class)
141 .register(TrafficTreatment.class)
142 .register(SpringOpenGroup.class)
143 .register(byte[].class)
144 .build();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700145
146 @Override
147 public void init(DeviceId deviceId, PipelinerContext context) {
148 this.serviceDirectory = context.directory();
149 this.deviceId = deviceId;
150
151 pendingGroups = CacheBuilder
152 .newBuilder()
153 .expireAfterWrite(20, TimeUnit.SECONDS)
154 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
155 if (notification.getCause() == RemovalCause.EXPIRED) {
156 fail(notification.getValue(),
157 ObjectiveError.GROUPINSTALLATIONFAILED);
158 }
159 }).build();
160
161 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500,
162 TimeUnit.MILLISECONDS);
163
164 coreService = serviceDirectory.get(CoreService.class);
165 flowRuleService = serviceDirectory.get(FlowRuleService.class);
166 groupService = serviceDirectory.get(GroupService.class);
167 flowObjectiveStore = context.store();
168
169 groupService.addListener(new InnerGroupListener());
170
171 appId = coreService
172 .registerApplication("org.onosproject.driver.SpringOpenTTP");
173
174 setTableMissEntries();
175 log.info("Spring Open TTP driver initialized");
176 }
177
178 @Override
179 public void filter(FilteringObjective filteringObjective) {
180 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
181 log.debug("processing PERMIT filter objective");
182 processFilter(filteringObjective,
183 filteringObjective.op() == Objective.Operation.ADD,
184 filteringObjective.appId());
185 } else {
186 log.debug("filter objective other than PERMIT not supported");
187 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
188 }
189 }
190
191 @Override
192 public void forward(ForwardingObjective fwd) {
193 Collection<FlowRule> rules;
194 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
195
196 rules = processForward(fwd);
197 switch (fwd.op()) {
198 case ADD:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800199 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700200 .forEach(flowBuilder::add);
201 break;
202 case REMOVE:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800203 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700204 .forEach(flowBuilder::remove);
205 break;
206 default:
207 fail(fwd, ObjectiveError.UNKNOWN);
208 log.warn("Unknown forwarding type {}", fwd.op());
209 }
210
211 flowRuleService.apply(flowBuilder
212 .build(new FlowRuleOperationsContext() {
213 @Override
214 public void onSuccess(FlowRuleOperations ops) {
215 pass(fwd);
Saurav Das8a0732e2015-11-20 15:27:53 -0800216 log.debug("Provisioned tables in {} successfully with "
217 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700218 }
219
220 @Override
221 public void onError(FlowRuleOperations ops) {
222 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700223 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800224 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700225 }
226 }));
227
228 }
229
230 @Override
231 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800232 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
233 switch (nextObjective.op()) {
234 case ADD:
sangho834e4b02015-05-01 09:38:25 -0700235 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800236 log.warn("Cannot add next {} that already exists in device {}",
237 nextObjective.id(), deviceId);
238 return;
239 }
240 log.debug("Processing NextObjective id{} in dev{} - add group",
241 nextObjective.id(), deviceId);
242 addGroup(nextObjective);
243 break;
244 case ADD_TO_EXISTING:
245 if (nextGroup != null) {
246 log.debug("Processing NextObjective id{} in dev{} - add bucket",
247 nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700248 addBucketToGroup(nextObjective);
249 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800250 log.warn("Cannot add to group that does not exist");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700251 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800252 break;
253 case REMOVE:
254 if (nextGroup == null) {
255 log.warn("Cannot remove next {} that does not exist in device {}",
256 nextObjective.id(), deviceId);
257 return;
258 }
259 log.debug("Processing NextObjective id{} in dev{} - remove group",
260 nextObjective.id(), deviceId);
261 removeGroup(nextObjective);
262 break;
263 case REMOVE_FROM_EXISTING:
264 if (nextGroup == null) {
265 log.warn("Cannot remove from next {} that does not exist in device {}",
266 nextObjective.id(), deviceId);
267 return;
268 }
269 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
270 nextObjective.id(), deviceId);
271 removeBucketFromGroup(nextObjective);
272 break;
273 default:
sangho834e4b02015-05-01 09:38:25 -0700274 log.warn("Unsupported operation {}", nextObjective.op());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700275 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700276 }
277
sangho834e4b02015-05-01 09:38:25 -0700278 private void removeGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700279 log.debug("removeGroup in {}: for next objective id {}",
280 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700281 final GroupKey key = new DefaultGroupKey(
282 appKryo.serialize(nextObjective.id()));
283 groupService.removeGroup(deviceId, key, appId);
284 }
285
286 private void addGroup(NextObjective nextObjective) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700287 log.debug("addGroup with type{} for nextObjective id {}",
288 nextObjective.type(), nextObjective.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700289 List<GroupBucket> buckets;
sangho834e4b02015-05-01 09:38:25 -0700290 switch (nextObjective.type()) {
291 case SIMPLE:
sangho834e4b02015-05-01 09:38:25 -0700292 Collection<TrafficTreatment> treatments = nextObjective.next();
293 if (treatments.size() == 1) {
Saurav Das4ce45962015-11-24 23:21:05 -0800294 // Spring Open TTP converts simple nextObjective to flow-actions
295 // in a dummy group
296 TrafficTreatment treatment = nextObjective.next().iterator().next();
297 log.debug("Converting SIMPLE group for next objective id {} " +
298 "to {} flow-actions in device:{}", nextObjective.id(),
299 treatment.allInstructions().size(), deviceId);
300 flowObjectiveStore.putNextGroup(nextObjective.id(),
301 new SpringOpenGroup(null, treatment));
sangho834e4b02015-05-01 09:38:25 -0700302 }
303 break;
304 case HASHED:
Saurav Das8a0732e2015-11-20 15:27:53 -0800305 // we convert MPLS ECMP groups to flow-actions for a single
306 // bucket(output port).
307 boolean mplsEcmp = false;
308 if (nextObjective.meta() != null) {
309 for (Criterion c : nextObjective.meta().criteria()) {
310 if (c.type() == Type.MPLS_LABEL) {
311 mplsEcmp = true;
312 }
313 }
314 }
315 if (mplsEcmp) {
316 // covert to flow-actions in a dummy group by choosing the first bucket
317 log.debug("Converting HASHED group for next objective id {} " +
318 "to flow-actions in device:{}", nextObjective.id(),
319 deviceId);
320 TrafficTreatment treatment = nextObjective.next().iterator().next();
321 flowObjectiveStore.putNextGroup(nextObjective.id(),
322 new SpringOpenGroup(null, treatment));
323 } else {
324 // process as ECMP group
325 buckets = nextObjective
326 .next()
327 .stream()
328 .map((treatment) -> DefaultGroupBucket
329 .createSelectGroupBucket(treatment))
330 .collect(Collectors.toList());
331 if (!buckets.isEmpty()) {
332 final GroupKey key = new DefaultGroupKey(
333 appKryo.serialize(nextObjective.id()));
334 GroupDescription groupDescription = new DefaultGroupDescription(
335 deviceId,
336 GroupDescription.Type.SELECT,
337 new GroupBuckets(buckets),
338 key,
339 null,
340 nextObjective.appId());
341 log.debug("Creating HASHED group for next objective id {}"
342 + " in dev:{}", nextObjective.id(), deviceId);
343 pendingGroups.put(key, nextObjective);
344 groupService.addGroup(groupDescription);
345 }
sangho834e4b02015-05-01 09:38:25 -0700346 }
347 break;
348 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700349 buckets = nextObjective
350 .next()
351 .stream()
352 .map((treatment) -> DefaultGroupBucket
353 .createAllGroupBucket(treatment))
354 .collect(Collectors.toList());
355 if (!buckets.isEmpty()) {
356 final GroupKey key = new DefaultGroupKey(
357 appKryo.serialize(nextObjective
358 .id()));
359 GroupDescription groupDescription = new DefaultGroupDescription(
360 deviceId,
361 GroupDescription.Type.ALL,
362 new GroupBuckets(buckets),
363 key,
364 null,
365 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800366 log.debug("Creating BROADCAST group for next objective id {} "
367 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700368 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800369 groupService.addGroup(groupDescription);
Charles Chanc42e84e2015-10-20 16:24:19 -0700370 }
371 break;
sangho834e4b02015-05-01 09:38:25 -0700372 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700373 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700374 fail(nextObjective, ObjectiveError.UNSUPPORTED);
375 log.warn("Unsupported next objective type {}", nextObjective.type());
376 break;
377 default:
378 fail(nextObjective, ObjectiveError.UNKNOWN);
379 log.warn("Unknown next objective type {}", nextObjective.type());
380 }
381 }
382
383 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700384 log.debug("addBucketToGroup in {}: for next objective id {}",
385 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700386 Collection<TrafficTreatment> treatments = nextObjective.next();
387 TrafficTreatment treatment = treatments.iterator().next();
388 final GroupKey key = new DefaultGroupKey(
389 appKryo.serialize(nextObjective
390 .id()));
391 Group group = groupService.getGroup(deviceId, key);
392 if (group == null) {
393 log.warn("Group is not found in {} for {}", deviceId, key);
394 return;
395 }
396 GroupBucket bucket;
397 if (group.type() == GroupDescription.Type.INDIRECT) {
398 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
399 } else if (group.type() == GroupDescription.Type.SELECT) {
400 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700401 } else if (group.type() == GroupDescription.Type.ALL) {
402 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700403 } else {
404 log.warn("Unsupported Group type {}", group.type());
405 return;
406 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700407 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700408 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
409 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700410 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
411 }
412
413 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700414 log.debug("removeBucketFromGroup in {}: for next objective id {}",
415 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700416 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
417 if (nextGroup != null) {
418 Collection<TrafficTreatment> treatments = nextObjective.next();
419 TrafficTreatment treatment = treatments.iterator().next();
420 final GroupKey key = new DefaultGroupKey(
421 appKryo.serialize(nextObjective
422 .id()));
423 Group group = groupService.getGroup(deviceId, key);
424 if (group == null) {
425 log.warn("Group is not found in {} for {}", deviceId, key);
426 return;
427 }
428 GroupBucket bucket;
429 if (group.type() == GroupDescription.Type.INDIRECT) {
430 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
431 } else if (group.type() == GroupDescription.Type.SELECT) {
432 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700433 } else if (group.type() == GroupDescription.Type.ALL) {
434 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700435 } else {
436 log.warn("Unsupported Group type {}", group.type());
437 return;
438 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700439 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700440 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
441 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700442 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
443 }
444 }
445
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700446 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
447 switch (fwd.flag()) {
448 case SPECIFIC:
449 return processSpecific(fwd);
450 case VERSATILE:
451 return processVersatile(fwd);
452 default:
453 fail(fwd, ObjectiveError.UNKNOWN);
454 log.warn("Unknown forwarding flag {}", fwd.flag());
455 }
456 return Collections.emptySet();
457 }
458
459 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800460 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700461 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700462 EthTypeCriterion ethType =
463 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
464 if (ethType == null) {
465 log.error("Versatile forwarding objective must include ethType");
466 fail(fwd, ObjectiveError.UNKNOWN);
467 return Collections.emptySet();
468 }
469
Saurav Das8a0732e2015-11-20 15:27:53 -0800470 if (fwd.treatment() == null && fwd.nextId() == null) {
471 log.error("VERSATILE forwarding objective needs next objective ID "
472 + "or treatment.");
473 return Collections.emptySet();
474 }
475 // emulation of ACL table (for versatile fwd objective) requires
476 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700477 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
478 .builder();
479 treatmentBuilder.wipeDeferred();
480
481 if (fwd.nextId() != null) {
482 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700483 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800484 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
485 if (soGroup.dummy) {
486 // need to convert to flow-actions
487 for (Instruction ins : soGroup.treatment.allInstructions()) {
488 treatmentBuilder.add(ins);
489 }
490 } else {
491 GroupKey key = soGroup.key;
492 Group group = groupService.getGroup(deviceId, key);
493 if (group == null) {
494 log.warn("The group left!");
495 fail(fwd, ObjectiveError.GROUPMISSING);
496 return Collections.emptySet();
497 }
498 treatmentBuilder.deferred().group(group.id());
499 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700500 }
sangho1e575652015-05-14 00:39:53 -0700501 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800502 }
503
504 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700505 if (fwd.treatment().allInstructions().size() == 1 &&
506 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
507 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
508 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan5270ed02016-01-30 23:22:37 -0800509 treatmentBuilder.popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800510 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800511 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800512 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700513 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800514 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800515 for (Instruction ins : fwd.treatment().allInstructions()) {
516 treatmentBuilder.add(ins);
517 }
Saurav Das822c4e22015-10-23 10:51:11 -0700518 }
sangho1e575652015-05-14 00:39:53 -0700519 }
520
sangho1e575652015-05-14 00:39:53 -0700521 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
522 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700523 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800524 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700525
526 if (fwd.permanent()) {
527 ruleBuilder.makePermanent();
528 } else {
529 ruleBuilder.makeTemporary(fwd.timeout());
530 }
531
532 ruleBuilder.forTable(aclTableId);
533 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700534 }
535
Charles Chan68aa62d2015-11-09 16:37:23 -0800536 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700537 TrafficSelector selector = fwd.selector();
538 EthTypeCriterion ethType = (EthTypeCriterion) selector
539 .getCriterion(Criterion.Type.ETH_TYPE);
540 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800541 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
542 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
543 return false;
544 }
545 return true;
546 }
547
548 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
549 TrafficSelector selector = fwd.selector();
550 EthCriterion ethDst = (EthCriterion) selector
551 .getCriterion(Criterion.Type.ETH_DST);
552 VlanIdCriterion vlanId = (VlanIdCriterion) selector
553 .getCriterion(Criterion.Type.VLAN_VID);
554 if (ethDst == null && vlanId == null) {
555 return false;
556 }
557 return true;
558 }
559
560 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800561 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
562 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800563 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
564 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
565
566 if (isEthTypeObj) {
567 return processEthTypeSpecificObjective(fwd);
568 } else if (isEthDstObj) {
569 return processEthDstSpecificObjective(fwd);
570 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700571 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800572 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700573 fail(fwd, ObjectiveError.UNSUPPORTED);
574 return Collections.emptySet();
575 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800576 }
577
578 protected Collection<FlowRule>
579 processEthTypeSpecificObjective(ForwardingObjective fwd) {
580 TrafficSelector selector = fwd.selector();
581 EthTypeCriterion ethType = (EthTypeCriterion) selector
582 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700583
584 TrafficSelector.Builder filteredSelectorBuilder =
585 DefaultTrafficSelector.builder();
586 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700587 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700588 filteredSelectorBuilder = filteredSelectorBuilder
589 .matchEthType(Ethernet.TYPE_IPV4)
590 .matchIPDst(((IPCriterion) selector
591 .getCriterion(Criterion.Type.IPV4_DST))
592 .ip());
593 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800594 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
595 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700596 } else {
597 filteredSelectorBuilder = filteredSelectorBuilder
598 .matchEthType(Ethernet.MPLS_UNICAST)
599 .matchMplsLabel(((MplsCriterion)
600 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
Charles Chan188ebf52015-12-23 00:15:11 -0800601 if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
602 filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion)
603 selector.getCriterion(Type.MPLS_BOS)).mplsBos());
604 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700605 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800606 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
607 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700608 }
609
610 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
611 .builder();
612 if (fwd.treatment() != null) {
613 for (Instruction i : fwd.treatment().allInstructions()) {
614 treatmentBuilder.add(i);
615 }
616 }
617
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700618 if (fwd.nextId() != null) {
619 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700620 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800621 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
622 if (soGroup.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800623 log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
624 + "in dev: {}", soGroup.treatment.allInstructions().size(),
625 fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800626 for (Instruction ins : soGroup.treatment.allInstructions()) {
627 treatmentBuilder.add(ins);
628 }
629 } else {
630 GroupKey key = soGroup.key;
631 Group group = groupService.getGroup(deviceId, key);
632 if (group == null) {
633 log.warn("The group left!");
634 fail(fwd, ObjectiveError.GROUPMISSING);
635 return Collections.emptySet();
636 }
637 treatmentBuilder.deferred().group(group.id());
638 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
Saurav Das4ce45962015-11-24 23:21:05 -0800639 + "for next:{} in dev: {}", group.id(), fwd.id(),
640 fwd.nextId(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700641 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700642 } else {
643 log.warn("processSpecific: No associated next objective object");
644 fail(fwd, ObjectiveError.GROUPMISSING);
645 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700646 }
647 }
648
649 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
650 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
651 .build();
652
653 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
654 .fromApp(fwd.appId()).withPriority(fwd.priority())
655 .forDevice(deviceId).withSelector(filteredSelector)
656 .withTreatment(treatment);
657
658 if (fwd.permanent()) {
659 ruleBuilder.makePermanent();
660 } else {
661 ruleBuilder.makeTemporary(fwd.timeout());
662 }
663
664 ruleBuilder.forTable(forTableId);
665 return Collections.singletonList(ruleBuilder.build());
666
667 }
668
Charles Chan68aa62d2015-11-09 16:37:23 -0800669 protected Collection<FlowRule>
670 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700671 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800672
673 // Build filtered selector
674 TrafficSelector selector = fwd.selector();
675 EthCriterion ethCriterion = (EthCriterion) selector
676 .getCriterion(Criterion.Type.ETH_DST);
677 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
678 .getCriterion(Criterion.Type.VLAN_VID);
679 TrafficSelector.Builder filteredSelectorBuilder =
680 DefaultTrafficSelector.builder();
681 // Do not match MacAddress for subnet broadcast entry
682 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
683 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800684 log.debug("processing L2 forwarding objective:{} in dev:{}",
685 fwd.id(), deviceId);
686 } else {
687 log.debug("processing L2 Broadcast forwarding objective:{} "
688 + "in dev:{} for vlan:{}",
689 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800690 }
691 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
692 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
693
694 // Build filtered treatment
695 TrafficTreatment.Builder treatmentBuilder =
696 DefaultTrafficTreatment.builder();
697 if (fwd.treatment() != null) {
698 treatmentBuilder.deferred();
699 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
700 }
701 if (fwd.nextId() != null) {
702 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
703 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800704 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
705 if (soGrp.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800706 log.debug("Adding {} flow-actions for fwd. obj. {} "
707 + "in dev: {}", soGrp.treatment.allInstructions().size(),
708 fwd.id(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800709 for (Instruction ins : soGrp.treatment.allInstructions()) {
Saurav Das4ce45962015-11-24 23:21:05 -0800710 treatmentBuilder.deferred().add(ins);
Saurav Das8a0732e2015-11-20 15:27:53 -0800711 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800712 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800713 GroupKey key = soGrp.key;
714 Group group = groupService.getGroup(deviceId, key);
715 if (group == null) {
716 log.warn("The group left!");
717 fail(fwd, ObjectiveError.GROUPMISSING);
718 return Collections.emptySet();
719 }
720 treatmentBuilder.deferred().group(group.id());
721 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
722 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800723 }
724 }
725 }
726 treatmentBuilder.immediate().transition(aclTableId);
727 TrafficTreatment filteredTreatment = treatmentBuilder.build();
728
729 // Build bridging table entries
730 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
731 flowRuleBuilder.fromApp(fwd.appId())
732 .withPriority(fwd.priority())
733 .forDevice(deviceId)
734 .withSelector(filteredSelector)
735 .withTreatment(filteredTreatment)
736 .forTable(dstMacTableId);
737 if (fwd.permanent()) {
738 flowRuleBuilder.makePermanent();
739 } else {
740 flowRuleBuilder.makeTemporary(fwd.timeout());
741 }
742 rules.add(flowRuleBuilder.build());
743
744 /*
745 // TODO Emulate source MAC table behavior
746 // Do not install source MAC table entry for subnet broadcast
747 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
748 // Build filtered selector
749 selector = fwd.selector();
750 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
751 filteredSelectorBuilder = DefaultTrafficSelector.builder();
752 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
753 filteredSelector = filteredSelectorBuilder.build();
754
755 // Build empty treatment. Apply existing instruction if match.
756 treatmentBuilder = DefaultTrafficTreatment.builder();
757 filteredTreatment = treatmentBuilder.build();
758
759 // Build bridging table entries
760 flowRuleBuilder = DefaultFlowRule.builder();
761 flowRuleBuilder.fromApp(fwd.appId())
762 .withPriority(fwd.priority())
763 .forDevice(deviceId)
764 .withSelector(filteredSelector)
765 .withTreatment(filteredTreatment)
766 .forTable(srcMacTableId)
767 .makePermanent();
768 rules.add(flowRuleBuilder.build());
769 }
770 */
771
772 return rules;
773 }
774
Saurav Das4ce45962015-11-24 23:21:05 -0800775 /*
776 * Note: CpqD switches do not handle MPLS-related operation properly
777 * for a packet with VLAN tag. We pop VLAN here as a workaround.
778 * Side effect: HostService learns redundant hosts with same MAC but
779 * different VLAN. No known side effect on the network reachability.
780 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800781 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
782 VlanIdCriterion vlanIdCriterion,
783 FilteringObjective filt,
784 VlanId assignedVlan,
785 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800786 if (vlanIdCriterion == null) {
787 return processEthDstOnlyFilter(ethCriterion, applicationId, filt.priority());
788 }
789
Charles Chan68aa62d2015-11-09 16:37:23 -0800790 //handling untagged packets via assigned VLAN
791 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
792 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
793 }
794
Charles Chan68aa62d2015-11-09 16:37:23 -0800795 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700796 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
797 .builder();
798 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
799 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800800 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700801 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800802 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
803 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700804 treatmentIp.transition(ipv4UnicastTableId);
805 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
806 .withSelector(selectorIp.build())
807 .withTreatment(treatmentIp.build())
808 .withPriority(filt.priority()).fromApp(applicationId)
809 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800810 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700811 rules.add(ruleIp);
812
813 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
814 .builder();
815 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
816 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800817 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700818 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800819 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
820 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700821 treatmentMpls.transition(mplsTableId);
822 FlowRule ruleMpls = DefaultFlowRule.builder()
823 .forDevice(deviceId).withSelector(selectorMpls.build())
824 .withTreatment(treatmentMpls.build())
825 .withPriority(filt.priority()).fromApp(applicationId)
826 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800827 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700828 rules.add(ruleMpls);
829
830 return rules;
831 }
832
Charles Chan5270ed02016-01-30 23:22:37 -0800833 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
834 ApplicationId applicationId, int priority) {
835 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
836 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
837 selector.matchEthType(Ethernet.TYPE_IPV4);
838 selector.matchEthDst(ethCriterion.mac());
839 treatment.transition(TABLE_IPV4_UNICAST);
840 FlowRule rule = DefaultFlowRule.builder()
841 .forDevice(deviceId)
842 .withSelector(selector.build())
843 .withTreatment(treatment.build())
844 .withPriority(priority)
845 .fromApp(applicationId)
846 .makePermanent()
847 .forTable(TABLE_TMAC).build();
848 return ImmutableList.<FlowRule>builder().add(rule).build();
849 }
850
Charles Chan68aa62d2015-11-09 16:37:23 -0800851 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700852 FilteringObjective filt,
Charles Chan68aa62d2015-11-09 16:37:23 -0800853 VlanId assignedVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700854 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700855 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800856 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700857 TrafficSelector.Builder selector = DefaultTrafficSelector
858 .builder();
859 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
860 .builder();
861 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800862 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
863 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700864 selector.matchInPort(p.port());
Charles Chan68aa62d2015-11-09 16:37:23 -0800865 } else {
866 selector.matchInPort(p.port());
867 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700868 }
869 treatment.transition(tmacTableId);
870 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
871 .withSelector(selector.build())
872 .withTreatment(treatment.build())
873 .withPriority(filt.priority()).fromApp(applicationId)
874 .makePermanent().forTable(vlanTableId).build();
875 rules.add(rule);
876
877 return rules;
878 }
879
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700880 private void processFilter(FilteringObjective filt, boolean install,
881 ApplicationId applicationId) {
882 // This driver only processes filtering criteria defined with switch
883 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700884 if (filt.key().equals(Criteria.dummy())
885 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700886 log.warn("No key defined in filtering objective from app: {}. Not"
887 + "processing filtering objective", applicationId);
888 fail(filt, ObjectiveError.UNKNOWN);
889 return;
890 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800891
892 EthCriterion ethCriterion = null;
893 VlanIdCriterion vlanIdCriterion = null;
894
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700895 // convert filtering conditions for switch-intfs into flowrules
896 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800897
898 for (Criterion criterion : filt.conditions()) {
899 if (criterion.type() == Criterion.Type.ETH_DST) {
900 ethCriterion = (EthCriterion) criterion;
901 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
902 vlanIdCriterion = (VlanIdCriterion) criterion;
903 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700904 log.debug("driver does not process IP filtering rules as it " +
905 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700906 } else {
907 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800908 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700909 fail(filt, ObjectiveError.UNSUPPORTED);
910 }
911 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800912
913 VlanId assignedVlan = null;
Charles Chane849c192016-01-11 18:28:54 -0800914 if (vlanIdCriterion != null) {
915 // For VLAN cross-connect packets, use the configured VLAN
916 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
917 assignedVlan = vlanIdCriterion.vlanId();
918
919 // For untagged packets, assign a VLAN ID
920 } else {
921 if (filt.meta() == null) {
922 log.error("Missing metadata in filtering objective required " +
923 "for vlan assignment in dev {}", deviceId);
924 fail(filt, ObjectiveError.BADPARAMS);
925 return;
Charles Chan68aa62d2015-11-09 16:37:23 -0800926 }
Charles Chane849c192016-01-11 18:28:54 -0800927 for (Instruction i : filt.meta().allInstructions()) {
928 if (i instanceof ModVlanIdInstruction) {
929 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
930 }
931 }
932 if (assignedVlan == null) {
933 log.error("Driver requires an assigned vlan-id to tag incoming "
934 + "untagged packets. Not processing vlan filters on "
935 + "device {}", deviceId);
936 fail(filt, ObjectiveError.BADPARAMS);
937 return;
938 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800939 }
940 }
941
942 if (ethCriterion == null) {
943 log.debug("filtering objective missing dstMac, cannot program TMAC table");
944 } else {
945 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
946 vlanIdCriterion,
947 filt,
948 assignedVlan,
949 applicationId)) {
950 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
951 tmacRule, deviceId);
952 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
953 }
954 }
955
Charles Chane849c192016-01-11 18:28:54 -0800956 if (vlanIdCriterion == null) {
957 log.debug("filtering objective missing VLAN ID criterion, "
958 + "cannot program VLAN Table");
Charles Chan68aa62d2015-11-09 16:37:23 -0800959 } else {
960 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
961 filt,
962 assignedVlan,
963 applicationId)) {
964 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
965 vlanRule, deviceId);
966 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
967 }
968 }
969
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700970 // apply filtering flow rules
971 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
972 @Override
973 public void onSuccess(FlowRuleOperations ops) {
974 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700975 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -0800976 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700977 }
978
979 @Override
980 public void onError(FlowRuleOperations ops) {
981 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700982 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800983 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700984 }
985 }));
986 }
987
988 protected void setTableMissEntries() {
989 // set all table-miss-entries
990 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -0800991 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
992 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700993 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800994 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700995 populateTableMissEntry(aclTableId, false, false, false, -1);
996 }
997
998 protected void populateTableMissEntry(int tableToAdd,
999 boolean toControllerNow,
1000 boolean toControllerWrite,
1001 boolean toTable, int tableToSend) {
1002 TrafficSelector selector = DefaultTrafficSelector.builder().build();
1003 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1004
1005 if (toControllerNow) {
1006 tBuilder.setOutput(PortNumber.CONTROLLER);
1007 }
1008
1009 if (toControllerWrite) {
1010 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
1011 }
1012
1013 if (toTable) {
1014 tBuilder.transition(tableToSend);
1015 }
1016
1017 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
1018 .withSelector(selector).withTreatment(tBuilder.build())
1019 .withPriority(0).fromApp(appId).makePermanent()
1020 .forTable(tableToAdd).build();
1021
1022 flowRuleService.applyFlowRules(flow);
1023 }
1024
1025 private void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001026 obj.context().ifPresent(context -> context.onSuccess(obj));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001027 }
1028
1029 protected void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001030 obj.context().ifPresent(context -> context.onError(obj, error));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001031 }
1032
1033 private class InnerGroupListener implements GroupListener {
1034 @Override
1035 public void event(GroupEvent event) {
1036 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001037 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001038 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001039 GroupKey key = event.subject().appCookie();
1040
1041 NextObjective obj = pendingGroups.getIfPresent(key);
1042 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001043 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1044 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001045 flowObjectiveStore
1046 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001047 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001048 pass(obj);
1049 pendingGroups.invalidate(key);
1050 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001051 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1052 log.warn("InnerGroupListener: Group ADD "
1053 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001054 }
1055 }
1056 }
1057
1058 private class GroupChecker implements Runnable {
1059
1060 @Override
1061 public void run() {
1062 Set<GroupKey> keys = pendingGroups
1063 .asMap()
1064 .keySet()
1065 .stream()
1066 .filter(key -> groupService.getGroup(deviceId, key) != null)
1067 .collect(Collectors.toSet());
1068
1069 keys.stream()
1070 .forEach(key -> {
1071 NextObjective obj = pendingGroups
1072 .getIfPresent(key);
1073 if (obj == null) {
1074 return;
1075 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001076 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1077 deviceId,
1078 groupService.getGroup(deviceId, key).id(),
1079 obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001080 pass(obj);
1081 pendingGroups.invalidate(key);
Saurav Das8a0732e2015-11-20 15:27:53 -08001082 flowObjectiveStore.putNextGroup(
1083 obj.id(),
1084 new SpringOpenGroup(key, null));
1085 });
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001086 }
1087 }
1088
Saurav Das8a0732e2015-11-20 15:27:53 -08001089 /**
1090 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1091 * used to fetch the group from the Group Service, or it can be serve as storage
1092 * for Traffic Treatments which can be used as flow actions. In the latter
1093 * case, we refer to this as a dummy group.
1094 *
1095 */
1096 private class SpringOpenGroup implements NextGroup {
1097 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001098 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001099 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001100
Saurav Das8a0732e2015-11-20 15:27:53 -08001101 /**
1102 * Storage for a GroupKey or a TrafficTreatment. One of the params
1103 * to this constructor must be null.
1104 * @param key represents a GroupKey
1105 * @param treatment represents flow actions in a dummy group
1106 */
1107 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1108 if (key == null) {
1109 this.key = new DefaultGroupKey(new byte[]{0});
1110 this.treatment = treatment;
1111 this.dummy = true;
1112 } else {
1113 this.key = key;
1114 this.treatment = DefaultTrafficTreatment.builder().build();
1115 this.dummy = false;
1116 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001117 }
1118
Saurav Das822c4e22015-10-23 10:51:11 -07001119 @SuppressWarnings("unused")
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001120 public GroupKey key() {
1121 return key;
1122 }
1123
1124 @Override
1125 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001126 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001127 }
1128
1129 }
Saurav Das24431192016-03-07 19:13:00 -08001130
1131 @Override
1132 public List<String> getNextMappings(NextGroup nextGroup) {
1133 // TODO Implementation deferred to vendor
1134 return null;
1135 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001136}