blob: 0d5423949920d88e56203218abde1ad58eb5eb1a [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
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",
135 "spring-open-%d"));
136 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das8a0732e2015-11-20 15:27:53 -0800137 .register(KryoNamespaces.API)
138 .register(GroupKey.class)
139 .register(DefaultGroupKey.class)
140 .register(TrafficTreatment.class)
141 .register(SpringOpenGroup.class)
142 .register(byte[].class)
143 .build();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700144
145 @Override
146 public void init(DeviceId deviceId, PipelinerContext context) {
147 this.serviceDirectory = context.directory();
148 this.deviceId = deviceId;
149
150 pendingGroups = CacheBuilder
151 .newBuilder()
152 .expireAfterWrite(20, TimeUnit.SECONDS)
153 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
154 if (notification.getCause() == RemovalCause.EXPIRED) {
155 fail(notification.getValue(),
156 ObjectiveError.GROUPINSTALLATIONFAILED);
157 }
158 }).build();
159
160 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500,
161 TimeUnit.MILLISECONDS);
162
163 coreService = serviceDirectory.get(CoreService.class);
164 flowRuleService = serviceDirectory.get(FlowRuleService.class);
165 groupService = serviceDirectory.get(GroupService.class);
166 flowObjectiveStore = context.store();
167
168 groupService.addListener(new InnerGroupListener());
169
170 appId = coreService
171 .registerApplication("org.onosproject.driver.SpringOpenTTP");
172
173 setTableMissEntries();
174 log.info("Spring Open TTP driver initialized");
175 }
176
177 @Override
178 public void filter(FilteringObjective filteringObjective) {
179 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
180 log.debug("processing PERMIT filter objective");
181 processFilter(filteringObjective,
182 filteringObjective.op() == Objective.Operation.ADD,
183 filteringObjective.appId());
184 } else {
185 log.debug("filter objective other than PERMIT not supported");
186 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
187 }
188 }
189
190 @Override
191 public void forward(ForwardingObjective fwd) {
192 Collection<FlowRule> rules;
193 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
194
195 rules = processForward(fwd);
196 switch (fwd.op()) {
197 case ADD:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800198 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700199 .forEach(flowBuilder::add);
200 break;
201 case REMOVE:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800202 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700203 .forEach(flowBuilder::remove);
204 break;
205 default:
206 fail(fwd, ObjectiveError.UNKNOWN);
207 log.warn("Unknown forwarding type {}", fwd.op());
208 }
209
210 flowRuleService.apply(flowBuilder
211 .build(new FlowRuleOperationsContext() {
212 @Override
213 public void onSuccess(FlowRuleOperations ops) {
214 pass(fwd);
Saurav Das8a0732e2015-11-20 15:27:53 -0800215 log.debug("Provisioned tables in {} successfully with "
216 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700217 }
218
219 @Override
220 public void onError(FlowRuleOperations ops) {
221 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700222 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800223 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700224 }
225 }));
226
227 }
228
229 @Override
230 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800231 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
232 switch (nextObjective.op()) {
233 case ADD:
sangho834e4b02015-05-01 09:38:25 -0700234 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800235 log.warn("Cannot add next {} that already exists in device {}",
236 nextObjective.id(), deviceId);
237 return;
238 }
239 log.debug("Processing NextObjective id{} in dev{} - add group",
240 nextObjective.id(), deviceId);
241 addGroup(nextObjective);
242 break;
243 case ADD_TO_EXISTING:
244 if (nextGroup != null) {
245 log.debug("Processing NextObjective id{} in dev{} - add bucket",
246 nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700247 addBucketToGroup(nextObjective);
248 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800249 log.warn("Cannot add to group that does not exist");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700250 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800251 break;
252 case REMOVE:
253 if (nextGroup == null) {
254 log.warn("Cannot remove next {} that does not exist in device {}",
255 nextObjective.id(), deviceId);
256 return;
257 }
258 log.debug("Processing NextObjective id{} in dev{} - remove group",
259 nextObjective.id(), deviceId);
260 removeGroup(nextObjective);
261 break;
262 case REMOVE_FROM_EXISTING:
263 if (nextGroup == null) {
264 log.warn("Cannot remove from next {} that does not exist in device {}",
265 nextObjective.id(), deviceId);
266 return;
267 }
268 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
269 nextObjective.id(), deviceId);
270 removeBucketFromGroup(nextObjective);
271 break;
272 default:
sangho834e4b02015-05-01 09:38:25 -0700273 log.warn("Unsupported operation {}", nextObjective.op());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700274 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700275 }
276
sangho834e4b02015-05-01 09:38:25 -0700277 private void removeGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700278 log.debug("removeGroup in {}: for next objective id {}",
279 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700280 final GroupKey key = new DefaultGroupKey(
281 appKryo.serialize(nextObjective.id()));
282 groupService.removeGroup(deviceId, key, appId);
283 }
284
285 private void addGroup(NextObjective nextObjective) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700286 log.debug("addGroup with type{} for nextObjective id {}",
287 nextObjective.type(), nextObjective.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700288 List<GroupBucket> buckets;
sangho834e4b02015-05-01 09:38:25 -0700289 switch (nextObjective.type()) {
290 case SIMPLE:
sangho834e4b02015-05-01 09:38:25 -0700291 Collection<TrafficTreatment> treatments = nextObjective.next();
292 if (treatments.size() == 1) {
Saurav Das4ce45962015-11-24 23:21:05 -0800293 // Spring Open TTP converts simple nextObjective to flow-actions
294 // in a dummy group
295 TrafficTreatment treatment = nextObjective.next().iterator().next();
296 log.debug("Converting SIMPLE group for next objective id {} " +
297 "to {} flow-actions in device:{}", nextObjective.id(),
298 treatment.allInstructions().size(), deviceId);
299 flowObjectiveStore.putNextGroup(nextObjective.id(),
300 new SpringOpenGroup(null, treatment));
sangho834e4b02015-05-01 09:38:25 -0700301 }
302 break;
303 case HASHED:
Saurav Das8a0732e2015-11-20 15:27:53 -0800304 // we convert MPLS ECMP groups to flow-actions for a single
305 // bucket(output port).
306 boolean mplsEcmp = false;
307 if (nextObjective.meta() != null) {
308 for (Criterion c : nextObjective.meta().criteria()) {
309 if (c.type() == Type.MPLS_LABEL) {
310 mplsEcmp = true;
311 }
312 }
313 }
314 if (mplsEcmp) {
315 // covert to flow-actions in a dummy group by choosing the first bucket
316 log.debug("Converting HASHED group for next objective id {} " +
317 "to flow-actions in device:{}", nextObjective.id(),
318 deviceId);
319 TrafficTreatment treatment = nextObjective.next().iterator().next();
320 flowObjectiveStore.putNextGroup(nextObjective.id(),
321 new SpringOpenGroup(null, treatment));
322 } else {
323 // process as ECMP group
324 buckets = nextObjective
325 .next()
326 .stream()
327 .map((treatment) -> DefaultGroupBucket
328 .createSelectGroupBucket(treatment))
329 .collect(Collectors.toList());
330 if (!buckets.isEmpty()) {
331 final GroupKey key = new DefaultGroupKey(
332 appKryo.serialize(nextObjective.id()));
333 GroupDescription groupDescription = new DefaultGroupDescription(
334 deviceId,
335 GroupDescription.Type.SELECT,
336 new GroupBuckets(buckets),
337 key,
338 null,
339 nextObjective.appId());
340 log.debug("Creating HASHED group for next objective id {}"
341 + " in dev:{}", nextObjective.id(), deviceId);
342 pendingGroups.put(key, nextObjective);
343 groupService.addGroup(groupDescription);
344 }
sangho834e4b02015-05-01 09:38:25 -0700345 }
346 break;
347 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700348 buckets = nextObjective
349 .next()
350 .stream()
351 .map((treatment) -> DefaultGroupBucket
352 .createAllGroupBucket(treatment))
353 .collect(Collectors.toList());
354 if (!buckets.isEmpty()) {
355 final GroupKey key = new DefaultGroupKey(
356 appKryo.serialize(nextObjective
357 .id()));
358 GroupDescription groupDescription = new DefaultGroupDescription(
359 deviceId,
360 GroupDescription.Type.ALL,
361 new GroupBuckets(buckets),
362 key,
363 null,
364 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800365 log.debug("Creating BROADCAST group for next objective id {} "
366 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700367 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800368 groupService.addGroup(groupDescription);
Charles Chanc42e84e2015-10-20 16:24:19 -0700369 }
370 break;
sangho834e4b02015-05-01 09:38:25 -0700371 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700372 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700373 fail(nextObjective, ObjectiveError.UNSUPPORTED);
374 log.warn("Unsupported next objective type {}", nextObjective.type());
375 break;
376 default:
377 fail(nextObjective, ObjectiveError.UNKNOWN);
378 log.warn("Unknown next objective type {}", nextObjective.type());
379 }
380 }
381
382 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700383 log.debug("addBucketToGroup in {}: for next objective id {}",
384 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700385 Collection<TrafficTreatment> treatments = nextObjective.next();
386 TrafficTreatment treatment = treatments.iterator().next();
387 final GroupKey key = new DefaultGroupKey(
388 appKryo.serialize(nextObjective
389 .id()));
390 Group group = groupService.getGroup(deviceId, key);
391 if (group == null) {
392 log.warn("Group is not found in {} for {}", deviceId, key);
393 return;
394 }
395 GroupBucket bucket;
396 if (group.type() == GroupDescription.Type.INDIRECT) {
397 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
398 } else if (group.type() == GroupDescription.Type.SELECT) {
399 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700400 } else if (group.type() == GroupDescription.Type.ALL) {
401 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700402 } else {
403 log.warn("Unsupported Group type {}", group.type());
404 return;
405 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700406 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700407 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
408 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700409 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
410 }
411
412 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700413 log.debug("removeBucketFromGroup in {}: for next objective id {}",
414 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700415 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
416 if (nextGroup != null) {
417 Collection<TrafficTreatment> treatments = nextObjective.next();
418 TrafficTreatment treatment = treatments.iterator().next();
419 final GroupKey key = new DefaultGroupKey(
420 appKryo.serialize(nextObjective
421 .id()));
422 Group group = groupService.getGroup(deviceId, key);
423 if (group == null) {
424 log.warn("Group is not found in {} for {}", deviceId, key);
425 return;
426 }
427 GroupBucket bucket;
428 if (group.type() == GroupDescription.Type.INDIRECT) {
429 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
430 } else if (group.type() == GroupDescription.Type.SELECT) {
431 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700432 } else if (group.type() == GroupDescription.Type.ALL) {
433 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700434 } else {
435 log.warn("Unsupported Group type {}", group.type());
436 return;
437 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700438 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700439 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
440 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700441 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
442 }
443 }
444
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700445 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
446 switch (fwd.flag()) {
447 case SPECIFIC:
448 return processSpecific(fwd);
449 case VERSATILE:
450 return processVersatile(fwd);
451 default:
452 fail(fwd, ObjectiveError.UNKNOWN);
453 log.warn("Unknown forwarding flag {}", fwd.flag());
454 }
455 return Collections.emptySet();
456 }
457
458 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800459 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700460 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700461 EthTypeCriterion ethType =
462 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
463 if (ethType == null) {
464 log.error("Versatile forwarding objective must include ethType");
465 fail(fwd, ObjectiveError.UNKNOWN);
466 return Collections.emptySet();
467 }
468
Saurav Das8a0732e2015-11-20 15:27:53 -0800469 if (fwd.treatment() == null && fwd.nextId() == null) {
470 log.error("VERSATILE forwarding objective needs next objective ID "
471 + "or treatment.");
472 return Collections.emptySet();
473 }
474 // emulation of ACL table (for versatile fwd objective) requires
475 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700476 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
477 .builder();
478 treatmentBuilder.wipeDeferred();
479
480 if (fwd.nextId() != null) {
481 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700482 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800483 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
484 if (soGroup.dummy) {
485 // need to convert to flow-actions
486 for (Instruction ins : soGroup.treatment.allInstructions()) {
487 treatmentBuilder.add(ins);
488 }
489 } else {
490 GroupKey key = soGroup.key;
491 Group group = groupService.getGroup(deviceId, key);
492 if (group == null) {
493 log.warn("The group left!");
494 fail(fwd, ObjectiveError.GROUPMISSING);
495 return Collections.emptySet();
496 }
497 treatmentBuilder.deferred().group(group.id());
498 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700499 }
sangho1e575652015-05-14 00:39:53 -0700500 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800501 }
502
503 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700504 if (fwd.treatment().allInstructions().size() == 1 &&
505 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
506 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
507 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan5270ed02016-01-30 23:22:37 -0800508 treatmentBuilder.popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800509 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800510 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800511 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700512 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800513 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800514 for (Instruction ins : fwd.treatment().allInstructions()) {
515 treatmentBuilder.add(ins);
516 }
Saurav Das822c4e22015-10-23 10:51:11 -0700517 }
sangho1e575652015-05-14 00:39:53 -0700518 }
519
sangho1e575652015-05-14 00:39:53 -0700520 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
521 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700522 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800523 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700524
525 if (fwd.permanent()) {
526 ruleBuilder.makePermanent();
527 } else {
528 ruleBuilder.makeTemporary(fwd.timeout());
529 }
530
531 ruleBuilder.forTable(aclTableId);
532 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700533 }
534
Charles Chan68aa62d2015-11-09 16:37:23 -0800535 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700536 TrafficSelector selector = fwd.selector();
537 EthTypeCriterion ethType = (EthTypeCriterion) selector
538 .getCriterion(Criterion.Type.ETH_TYPE);
539 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800540 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
541 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
542 return false;
543 }
544 return true;
545 }
546
547 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
548 TrafficSelector selector = fwd.selector();
549 EthCriterion ethDst = (EthCriterion) selector
550 .getCriterion(Criterion.Type.ETH_DST);
551 VlanIdCriterion vlanId = (VlanIdCriterion) selector
552 .getCriterion(Criterion.Type.VLAN_VID);
553 if (ethDst == null && vlanId == null) {
554 return false;
555 }
556 return true;
557 }
558
559 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800560 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
561 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800562 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
563 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
564
565 if (isEthTypeObj) {
566 return processEthTypeSpecificObjective(fwd);
567 } else if (isEthDstObj) {
568 return processEthDstSpecificObjective(fwd);
569 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700570 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800571 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700572 fail(fwd, ObjectiveError.UNSUPPORTED);
573 return Collections.emptySet();
574 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800575 }
576
577 protected Collection<FlowRule>
578 processEthTypeSpecificObjective(ForwardingObjective fwd) {
579 TrafficSelector selector = fwd.selector();
580 EthTypeCriterion ethType = (EthTypeCriterion) selector
581 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700582
583 TrafficSelector.Builder filteredSelectorBuilder =
584 DefaultTrafficSelector.builder();
585 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700586 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700587 filteredSelectorBuilder = filteredSelectorBuilder
588 .matchEthType(Ethernet.TYPE_IPV4)
589 .matchIPDst(((IPCriterion) selector
590 .getCriterion(Criterion.Type.IPV4_DST))
591 .ip());
592 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800593 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
594 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700595 } else {
596 filteredSelectorBuilder = filteredSelectorBuilder
597 .matchEthType(Ethernet.MPLS_UNICAST)
598 .matchMplsLabel(((MplsCriterion)
599 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
Charles Chan188ebf52015-12-23 00:15:11 -0800600 if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
601 filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion)
602 selector.getCriterion(Type.MPLS_BOS)).mplsBos());
603 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700604 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800605 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
606 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700607 }
608
609 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
610 .builder();
611 if (fwd.treatment() != null) {
612 for (Instruction i : fwd.treatment().allInstructions()) {
613 treatmentBuilder.add(i);
614 }
615 }
616
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700617 if (fwd.nextId() != null) {
618 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700619 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800620 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
621 if (soGroup.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800622 log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
623 + "in dev: {}", soGroup.treatment.allInstructions().size(),
624 fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800625 for (Instruction ins : soGroup.treatment.allInstructions()) {
626 treatmentBuilder.add(ins);
627 }
628 } else {
629 GroupKey key = soGroup.key;
630 Group group = groupService.getGroup(deviceId, key);
631 if (group == null) {
632 log.warn("The group left!");
633 fail(fwd, ObjectiveError.GROUPMISSING);
634 return Collections.emptySet();
635 }
636 treatmentBuilder.deferred().group(group.id());
637 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
Saurav Das4ce45962015-11-24 23:21:05 -0800638 + "for next:{} in dev: {}", group.id(), fwd.id(),
639 fwd.nextId(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700640 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700641 } else {
642 log.warn("processSpecific: No associated next objective object");
643 fail(fwd, ObjectiveError.GROUPMISSING);
644 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700645 }
646 }
647
648 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
649 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
650 .build();
651
652 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
653 .fromApp(fwd.appId()).withPriority(fwd.priority())
654 .forDevice(deviceId).withSelector(filteredSelector)
655 .withTreatment(treatment);
656
657 if (fwd.permanent()) {
658 ruleBuilder.makePermanent();
659 } else {
660 ruleBuilder.makeTemporary(fwd.timeout());
661 }
662
663 ruleBuilder.forTable(forTableId);
664 return Collections.singletonList(ruleBuilder.build());
665
666 }
667
Charles Chan68aa62d2015-11-09 16:37:23 -0800668 protected Collection<FlowRule>
669 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700670 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800671
672 // Build filtered selector
673 TrafficSelector selector = fwd.selector();
674 EthCriterion ethCriterion = (EthCriterion) selector
675 .getCriterion(Criterion.Type.ETH_DST);
676 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
677 .getCriterion(Criterion.Type.VLAN_VID);
678 TrafficSelector.Builder filteredSelectorBuilder =
679 DefaultTrafficSelector.builder();
680 // Do not match MacAddress for subnet broadcast entry
681 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
682 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800683 log.debug("processing L2 forwarding objective:{} in dev:{}",
684 fwd.id(), deviceId);
685 } else {
686 log.debug("processing L2 Broadcast forwarding objective:{} "
687 + "in dev:{} for vlan:{}",
688 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800689 }
690 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
691 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
692
693 // Build filtered treatment
694 TrafficTreatment.Builder treatmentBuilder =
695 DefaultTrafficTreatment.builder();
696 if (fwd.treatment() != null) {
697 treatmentBuilder.deferred();
698 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
699 }
700 if (fwd.nextId() != null) {
701 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
702 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800703 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
704 if (soGrp.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800705 log.debug("Adding {} flow-actions for fwd. obj. {} "
706 + "in dev: {}", soGrp.treatment.allInstructions().size(),
707 fwd.id(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800708 for (Instruction ins : soGrp.treatment.allInstructions()) {
Saurav Das4ce45962015-11-24 23:21:05 -0800709 treatmentBuilder.deferred().add(ins);
Saurav Das8a0732e2015-11-20 15:27:53 -0800710 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800711 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800712 GroupKey key = soGrp.key;
713 Group group = groupService.getGroup(deviceId, key);
714 if (group == null) {
715 log.warn("The group left!");
716 fail(fwd, ObjectiveError.GROUPMISSING);
717 return Collections.emptySet();
718 }
719 treatmentBuilder.deferred().group(group.id());
720 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
721 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800722 }
723 }
724 }
725 treatmentBuilder.immediate().transition(aclTableId);
726 TrafficTreatment filteredTreatment = treatmentBuilder.build();
727
728 // Build bridging table entries
729 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
730 flowRuleBuilder.fromApp(fwd.appId())
731 .withPriority(fwd.priority())
732 .forDevice(deviceId)
733 .withSelector(filteredSelector)
734 .withTreatment(filteredTreatment)
735 .forTable(dstMacTableId);
736 if (fwd.permanent()) {
737 flowRuleBuilder.makePermanent();
738 } else {
739 flowRuleBuilder.makeTemporary(fwd.timeout());
740 }
741 rules.add(flowRuleBuilder.build());
742
743 /*
744 // TODO Emulate source MAC table behavior
745 // Do not install source MAC table entry for subnet broadcast
746 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
747 // Build filtered selector
748 selector = fwd.selector();
749 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
750 filteredSelectorBuilder = DefaultTrafficSelector.builder();
751 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
752 filteredSelector = filteredSelectorBuilder.build();
753
754 // Build empty treatment. Apply existing instruction if match.
755 treatmentBuilder = DefaultTrafficTreatment.builder();
756 filteredTreatment = treatmentBuilder.build();
757
758 // Build bridging table entries
759 flowRuleBuilder = DefaultFlowRule.builder();
760 flowRuleBuilder.fromApp(fwd.appId())
761 .withPriority(fwd.priority())
762 .forDevice(deviceId)
763 .withSelector(filteredSelector)
764 .withTreatment(filteredTreatment)
765 .forTable(srcMacTableId)
766 .makePermanent();
767 rules.add(flowRuleBuilder.build());
768 }
769 */
770
771 return rules;
772 }
773
Saurav Das4ce45962015-11-24 23:21:05 -0800774 /*
775 * Note: CpqD switches do not handle MPLS-related operation properly
776 * for a packet with VLAN tag. We pop VLAN here as a workaround.
777 * Side effect: HostService learns redundant hosts with same MAC but
778 * different VLAN. No known side effect on the network reachability.
779 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800780 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
781 VlanIdCriterion vlanIdCriterion,
782 FilteringObjective filt,
783 VlanId assignedVlan,
784 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800785 if (vlanIdCriterion == null) {
786 return processEthDstOnlyFilter(ethCriterion, applicationId, filt.priority());
787 }
788
Charles Chan68aa62d2015-11-09 16:37:23 -0800789 //handling untagged packets via assigned VLAN
790 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
791 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
792 }
793
Charles Chan68aa62d2015-11-09 16:37:23 -0800794 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700795 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
796 .builder();
797 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
798 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800799 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700800 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800801 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
802 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700803 treatmentIp.transition(ipv4UnicastTableId);
804 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
805 .withSelector(selectorIp.build())
806 .withTreatment(treatmentIp.build())
807 .withPriority(filt.priority()).fromApp(applicationId)
808 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800809 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700810 rules.add(ruleIp);
811
812 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
813 .builder();
814 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
815 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800816 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700817 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800818 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
819 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700820 treatmentMpls.transition(mplsTableId);
821 FlowRule ruleMpls = DefaultFlowRule.builder()
822 .forDevice(deviceId).withSelector(selectorMpls.build())
823 .withTreatment(treatmentMpls.build())
824 .withPriority(filt.priority()).fromApp(applicationId)
825 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800826 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700827 rules.add(ruleMpls);
828
829 return rules;
830 }
831
Charles Chan5270ed02016-01-30 23:22:37 -0800832 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
833 ApplicationId applicationId, int priority) {
834 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
835 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
836 selector.matchEthType(Ethernet.TYPE_IPV4);
837 selector.matchEthDst(ethCriterion.mac());
838 treatment.transition(TABLE_IPV4_UNICAST);
839 FlowRule rule = DefaultFlowRule.builder()
840 .forDevice(deviceId)
841 .withSelector(selector.build())
842 .withTreatment(treatment.build())
843 .withPriority(priority)
844 .fromApp(applicationId)
845 .makePermanent()
846 .forTable(TABLE_TMAC).build();
847 return ImmutableList.<FlowRule>builder().add(rule).build();
848 }
849
Charles Chan68aa62d2015-11-09 16:37:23 -0800850 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700851 FilteringObjective filt,
Charles Chan68aa62d2015-11-09 16:37:23 -0800852 VlanId assignedVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700853 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700854 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800855 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700856 TrafficSelector.Builder selector = DefaultTrafficSelector
857 .builder();
858 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
859 .builder();
860 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800861 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
862 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700863 selector.matchInPort(p.port());
Charles Chan68aa62d2015-11-09 16:37:23 -0800864 } else {
865 selector.matchInPort(p.port());
866 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700867 }
868 treatment.transition(tmacTableId);
869 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
870 .withSelector(selector.build())
871 .withTreatment(treatment.build())
872 .withPriority(filt.priority()).fromApp(applicationId)
873 .makePermanent().forTable(vlanTableId).build();
874 rules.add(rule);
875
876 return rules;
877 }
878
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700879 private void processFilter(FilteringObjective filt, boolean install,
880 ApplicationId applicationId) {
881 // This driver only processes filtering criteria defined with switch
882 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700883 if (filt.key().equals(Criteria.dummy())
884 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700885 log.warn("No key defined in filtering objective from app: {}. Not"
886 + "processing filtering objective", applicationId);
887 fail(filt, ObjectiveError.UNKNOWN);
888 return;
889 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800890
891 EthCriterion ethCriterion = null;
892 VlanIdCriterion vlanIdCriterion = null;
893
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700894 // convert filtering conditions for switch-intfs into flowrules
895 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800896
897 for (Criterion criterion : filt.conditions()) {
898 if (criterion.type() == Criterion.Type.ETH_DST) {
899 ethCriterion = (EthCriterion) criterion;
900 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
901 vlanIdCriterion = (VlanIdCriterion) criterion;
902 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700903 log.debug("driver does not process IP filtering rules as it " +
904 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700905 } else {
906 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800907 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700908 fail(filt, ObjectiveError.UNSUPPORTED);
909 }
910 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800911
912 VlanId assignedVlan = null;
Charles Chane849c192016-01-11 18:28:54 -0800913 if (vlanIdCriterion != null) {
914 // For VLAN cross-connect packets, use the configured VLAN
915 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
916 assignedVlan = vlanIdCriterion.vlanId();
917
918 // For untagged packets, assign a VLAN ID
919 } else {
920 if (filt.meta() == null) {
921 log.error("Missing metadata in filtering objective required " +
922 "for vlan assignment in dev {}", deviceId);
923 fail(filt, ObjectiveError.BADPARAMS);
924 return;
Charles Chan68aa62d2015-11-09 16:37:23 -0800925 }
Charles Chane849c192016-01-11 18:28:54 -0800926 for (Instruction i : filt.meta().allInstructions()) {
927 if (i instanceof ModVlanIdInstruction) {
928 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
929 }
930 }
931 if (assignedVlan == null) {
932 log.error("Driver requires an assigned vlan-id to tag incoming "
933 + "untagged packets. Not processing vlan filters on "
934 + "device {}", deviceId);
935 fail(filt, ObjectiveError.BADPARAMS);
936 return;
937 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800938 }
939 }
940
941 if (ethCriterion == null) {
942 log.debug("filtering objective missing dstMac, cannot program TMAC table");
943 } else {
944 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
945 vlanIdCriterion,
946 filt,
947 assignedVlan,
948 applicationId)) {
949 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
950 tmacRule, deviceId);
951 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
952 }
953 }
954
Charles Chane849c192016-01-11 18:28:54 -0800955 if (vlanIdCriterion == null) {
956 log.debug("filtering objective missing VLAN ID criterion, "
957 + "cannot program VLAN Table");
Charles Chan68aa62d2015-11-09 16:37:23 -0800958 } else {
959 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
960 filt,
961 assignedVlan,
962 applicationId)) {
963 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
964 vlanRule, deviceId);
965 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
966 }
967 }
968
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700969 // apply filtering flow rules
970 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
971 @Override
972 public void onSuccess(FlowRuleOperations ops) {
973 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700974 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -0800975 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700976 }
977
978 @Override
979 public void onError(FlowRuleOperations ops) {
980 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700981 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800982 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700983 }
984 }));
985 }
986
987 protected void setTableMissEntries() {
988 // set all table-miss-entries
989 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -0800990 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
991 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700992 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800993 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700994 populateTableMissEntry(aclTableId, false, false, false, -1);
995 }
996
997 protected void populateTableMissEntry(int tableToAdd,
998 boolean toControllerNow,
999 boolean toControllerWrite,
1000 boolean toTable, int tableToSend) {
1001 TrafficSelector selector = DefaultTrafficSelector.builder().build();
1002 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1003
1004 if (toControllerNow) {
1005 tBuilder.setOutput(PortNumber.CONTROLLER);
1006 }
1007
1008 if (toControllerWrite) {
1009 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
1010 }
1011
1012 if (toTable) {
1013 tBuilder.transition(tableToSend);
1014 }
1015
1016 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
1017 .withSelector(selector).withTreatment(tBuilder.build())
1018 .withPriority(0).fromApp(appId).makePermanent()
1019 .forTable(tableToAdd).build();
1020
1021 flowRuleService.applyFlowRules(flow);
1022 }
1023
1024 private void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001025 obj.context().ifPresent(context -> context.onSuccess(obj));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001026 }
1027
1028 protected void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001029 obj.context().ifPresent(context -> context.onError(obj, error));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001030 }
1031
1032 private class InnerGroupListener implements GroupListener {
1033 @Override
1034 public void event(GroupEvent event) {
1035 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001036 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001037 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001038 GroupKey key = event.subject().appCookie();
1039
1040 NextObjective obj = pendingGroups.getIfPresent(key);
1041 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001042 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1043 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001044 flowObjectiveStore
1045 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001046 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001047 pass(obj);
1048 pendingGroups.invalidate(key);
1049 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001050 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1051 log.warn("InnerGroupListener: Group ADD "
1052 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001053 }
1054 }
1055 }
1056
1057 private class GroupChecker implements Runnable {
1058
1059 @Override
1060 public void run() {
1061 Set<GroupKey> keys = pendingGroups
1062 .asMap()
1063 .keySet()
1064 .stream()
1065 .filter(key -> groupService.getGroup(deviceId, key) != null)
1066 .collect(Collectors.toSet());
1067
1068 keys.stream()
1069 .forEach(key -> {
1070 NextObjective obj = pendingGroups
1071 .getIfPresent(key);
1072 if (obj == null) {
1073 return;
1074 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001075 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1076 deviceId,
1077 groupService.getGroup(deviceId, key).id(),
1078 obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001079 pass(obj);
1080 pendingGroups.invalidate(key);
Saurav Das8a0732e2015-11-20 15:27:53 -08001081 flowObjectiveStore.putNextGroup(
1082 obj.id(),
1083 new SpringOpenGroup(key, null));
1084 });
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001085 }
1086 }
1087
Saurav Das8a0732e2015-11-20 15:27:53 -08001088 /**
1089 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1090 * used to fetch the group from the Group Service, or it can be serve as storage
1091 * for Traffic Treatments which can be used as flow actions. In the latter
1092 * case, we refer to this as a dummy group.
1093 *
1094 */
1095 private class SpringOpenGroup implements NextGroup {
1096 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001097 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001098 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001099
Saurav Das8a0732e2015-11-20 15:27:53 -08001100 /**
1101 * Storage for a GroupKey or a TrafficTreatment. One of the params
1102 * to this constructor must be null.
1103 * @param key represents a GroupKey
1104 * @param treatment represents flow actions in a dummy group
1105 */
1106 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1107 if (key == null) {
1108 this.key = new DefaultGroupKey(new byte[]{0});
1109 this.treatment = treatment;
1110 this.dummy = true;
1111 } else {
1112 this.key = key;
1113 this.treatment = DefaultTrafficTreatment.builder().build();
1114 this.dummy = false;
1115 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001116 }
1117
Saurav Das822c4e22015-10-23 10:51:11 -07001118 @SuppressWarnings("unused")
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001119 public GroupKey key() {
1120 return key;
1121 }
1122
1123 @Override
1124 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001125 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001126 }
1127
1128 }
Saurav Das24431192016-03-07 19:13:00 -08001129
1130 @Override
1131 public List<String> getNextMappings(NextGroup nextGroup) {
1132 // TODO Implementation deferred to vendor
1133 return null;
1134 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001135}