blob: f0ad4c1437c5c20e525da5d5a8663812fa91263a [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
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -070018import static java.util.concurrent.Executors.newScheduledThreadPool;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070019import static org.onlab.util.Tools.groupedThreads;
20import static org.slf4j.LoggerFactory.getLogger;
21
22import com.google.common.cache.Cache;
23import com.google.common.cache.CacheBuilder;
24import com.google.common.cache.RemovalCause;
25import com.google.common.cache.RemovalNotification;
26
Charles Chan5270ed02016-01-30 23:22:37 -080027import com.google.common.collect.ImmutableList;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070028import org.onlab.osgi.ServiceDirectory;
29import org.onlab.packet.Ethernet;
Charles Chan68aa62d2015-11-09 16:37:23 -080030import org.onlab.packet.MacAddress;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070031import org.onlab.packet.VlanId;
32import org.onlab.util.KryoNamespace;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.behaviour.NextGroup;
38import org.onosproject.net.behaviour.Pipeliner;
39import org.onosproject.net.behaviour.PipelinerContext;
40import org.onosproject.net.driver.AbstractHandlerBehaviour;
41import org.onosproject.net.flow.DefaultFlowRule;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.FlowRule;
45import org.onosproject.net.flow.FlowRuleOperations;
46import org.onosproject.net.flow.FlowRuleOperationsContext;
47import org.onosproject.net.flow.FlowRuleService;
48import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
50import org.onosproject.net.flow.criteria.Criteria;
51import org.onosproject.net.flow.criteria.Criterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080052import org.onosproject.net.flow.criteria.Criterion.Type;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070053import org.onosproject.net.flow.criteria.EthCriterion;
54import org.onosproject.net.flow.criteria.EthTypeCriterion;
55import org.onosproject.net.flow.criteria.IPCriterion;
Charles Chan188ebf52015-12-23 00:15:11 -080056import org.onosproject.net.flow.criteria.MplsBosCriterion;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070057import org.onosproject.net.flow.criteria.MplsCriterion;
58import org.onosproject.net.flow.criteria.PortCriterion;
59import org.onosproject.net.flow.criteria.VlanIdCriterion;
60import org.onosproject.net.flow.instructions.Instruction;
Saurav Das822c4e22015-10-23 10:51:11 -070061import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -050062import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Charles Chan68aa62d2015-11-09 16:37:23 -080063import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070064import org.onosproject.net.flowobjective.FilteringObjective;
65import org.onosproject.net.flowobjective.FlowObjectiveStore;
66import org.onosproject.net.flowobjective.ForwardingObjective;
67import org.onosproject.net.flowobjective.NextObjective;
68import org.onosproject.net.flowobjective.Objective;
69import org.onosproject.net.flowobjective.ObjectiveError;
70import org.onosproject.net.group.DefaultGroupBucket;
71import org.onosproject.net.group.DefaultGroupDescription;
72import org.onosproject.net.group.DefaultGroupKey;
73import org.onosproject.net.group.Group;
74import org.onosproject.net.group.GroupBucket;
75import org.onosproject.net.group.GroupBuckets;
76import org.onosproject.net.group.GroupDescription;
77import org.onosproject.net.group.GroupEvent;
78import org.onosproject.net.group.GroupKey;
79import org.onosproject.net.group.GroupListener;
80import org.onosproject.net.group.GroupService;
Saurav Das8a0732e2015-11-20 15:27:53 -080081import org.onosproject.store.serializers.KryoNamespaces;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070082import org.slf4j.Logger;
83
84import java.util.ArrayList;
85import java.util.Collection;
86import java.util.Collections;
87import java.util.List;
Sho SHIMIZU45906042016-01-13 23:05:54 -080088import java.util.Objects;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070089import java.util.Set;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070090import java.util.concurrent.ScheduledExecutorService;
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -070091import java.util.concurrent.ScheduledThreadPoolExecutor;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070092import java.util.concurrent.TimeUnit;
93import java.util.stream.Collectors;
94
95/**
96 * Driver for SPRING-OPEN pipeline.
97 */
98public class SpringOpenTTP extends AbstractHandlerBehaviour
99 implements Pipeliner {
100
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700101 /**
102 * GroupCheck delay.
103 */
104 private static final int CHECK_DELAY = 500;
105
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700106 // Default table ID - compatible with CpqD switch
107 private static final int TABLE_VLAN = 0;
108 private static final int TABLE_TMAC = 1;
109 private static final int TABLE_IPV4_UNICAST = 2;
110 private static final int TABLE_MPLS = 3;
Charles Chan68aa62d2015-11-09 16:37:23 -0800111 private static final int TABLE_DMAC = 4;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700112 private static final int TABLE_ACL = 5;
Charles Chan68aa62d2015-11-09 16:37:23 -0800113 private static final int TABLE_SMAC = 6;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700114
115 /**
116 * Set the default values. These variables will get overwritten based on the
117 * switch vendor type
118 */
119 protected int vlanTableId = TABLE_VLAN;
120 protected int tmacTableId = TABLE_TMAC;
121 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
122 protected int mplsTableId = TABLE_MPLS;
Charles Chan68aa62d2015-11-09 16:37:23 -0800123 protected int dstMacTableId = TABLE_DMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700124 protected int aclTableId = TABLE_ACL;
Charles Chan68aa62d2015-11-09 16:37:23 -0800125 protected int srcMacTableId = TABLE_SMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700126
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700127 private static final Logger log = getLogger(SpringOpenTTP.class);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700128
129 private ServiceDirectory serviceDirectory;
130 private FlowRuleService flowRuleService;
131 private CoreService coreService;
132 protected GroupService groupService;
133 protected FlowObjectiveStore flowObjectiveStore;
134 protected DeviceId deviceId;
135 private ApplicationId appId;
136
137 private Cache<GroupKey, NextObjective> pendingGroups;
138
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700139 private static final ScheduledExecutorService GROUP_CHECKER
140 = newScheduledThreadPool(2,
141 groupedThreads("onos/pipeliner",
142 "spring-open-%d", log));
143 static {
144 // ONOS-3579 workaround, let core threads die out on idle
145 if (GROUP_CHECKER instanceof ScheduledThreadPoolExecutor) {
146 ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) GROUP_CHECKER;
147 executor.setKeepAliveTime(CHECK_DELAY * 2, TimeUnit.MILLISECONDS);
148 executor.allowCoreThreadTimeOut(true);
149 }
150 }
151
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700152 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das8a0732e2015-11-20 15:27:53 -0800153 .register(KryoNamespaces.API)
154 .register(GroupKey.class)
155 .register(DefaultGroupKey.class)
156 .register(TrafficTreatment.class)
157 .register(SpringOpenGroup.class)
Charles Chaneefdedf2016-05-23 16:45:45 -0700158 .build("SpringOpenTTP");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700159
160 @Override
161 public void init(DeviceId deviceId, PipelinerContext context) {
162 this.serviceDirectory = context.directory();
163 this.deviceId = deviceId;
164
165 pendingGroups = CacheBuilder
166 .newBuilder()
167 .expireAfterWrite(20, TimeUnit.SECONDS)
168 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
169 if (notification.getCause() == RemovalCause.EXPIRED) {
170 fail(notification.getValue(),
171 ObjectiveError.GROUPINSTALLATIONFAILED);
172 }
173 }).build();
174
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700175 coreService = serviceDirectory.get(CoreService.class);
176 flowRuleService = serviceDirectory.get(FlowRuleService.class);
177 groupService = serviceDirectory.get(GroupService.class);
178 flowObjectiveStore = context.store();
179
180 groupService.addListener(new InnerGroupListener());
181
182 appId = coreService
183 .registerApplication("org.onosproject.driver.SpringOpenTTP");
184
185 setTableMissEntries();
186 log.info("Spring Open TTP driver initialized");
187 }
188
189 @Override
190 public void filter(FilteringObjective filteringObjective) {
191 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
192 log.debug("processing PERMIT filter objective");
193 processFilter(filteringObjective,
194 filteringObjective.op() == Objective.Operation.ADD,
195 filteringObjective.appId());
196 } else {
197 log.debug("filter objective other than PERMIT not supported");
198 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
199 }
200 }
201
202 @Override
203 public void forward(ForwardingObjective fwd) {
204 Collection<FlowRule> rules;
205 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
206
207 rules = processForward(fwd);
208 switch (fwd.op()) {
209 case ADD:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800210 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700211 .forEach(flowBuilder::add);
212 break;
213 case REMOVE:
Sho SHIMIZU45906042016-01-13 23:05:54 -0800214 rules.stream().filter(Objects::nonNull)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700215 .forEach(flowBuilder::remove);
216 break;
217 default:
218 fail(fwd, ObjectiveError.UNKNOWN);
219 log.warn("Unknown forwarding type {}", fwd.op());
220 }
221
222 flowRuleService.apply(flowBuilder
223 .build(new FlowRuleOperationsContext() {
224 @Override
225 public void onSuccess(FlowRuleOperations ops) {
226 pass(fwd);
Saurav Das8a0732e2015-11-20 15:27:53 -0800227 log.debug("Provisioned tables in {} successfully with "
228 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700229 }
230
231 @Override
232 public void onError(FlowRuleOperations ops) {
233 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700234 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -0800235 + "forwarding rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700236 }
237 }));
238
239 }
240
241 @Override
242 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800243 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
244 switch (nextObjective.op()) {
245 case ADD:
sangho834e4b02015-05-01 09:38:25 -0700246 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800247 log.warn("Cannot add next {} that already exists in device {}",
248 nextObjective.id(), deviceId);
249 return;
250 }
251 log.debug("Processing NextObjective id{} in dev{} - add group",
252 nextObjective.id(), deviceId);
253 addGroup(nextObjective);
254 break;
255 case ADD_TO_EXISTING:
256 if (nextGroup != null) {
257 log.debug("Processing NextObjective id{} in dev{} - add bucket",
258 nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700259 addBucketToGroup(nextObjective);
260 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800261 log.warn("Cannot add to group that does not exist");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700262 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800263 break;
264 case REMOVE:
265 if (nextGroup == null) {
266 log.warn("Cannot remove next {} that does not exist in device {}",
267 nextObjective.id(), deviceId);
268 return;
269 }
270 log.debug("Processing NextObjective id{} in dev{} - remove group",
271 nextObjective.id(), deviceId);
272 removeGroup(nextObjective);
273 break;
274 case REMOVE_FROM_EXISTING:
275 if (nextGroup == null) {
276 log.warn("Cannot remove from next {} that does not exist in device {}",
277 nextObjective.id(), deviceId);
278 return;
279 }
280 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
281 nextObjective.id(), deviceId);
282 removeBucketFromGroup(nextObjective);
283 break;
284 default:
sangho834e4b02015-05-01 09:38:25 -0700285 log.warn("Unsupported operation {}", nextObjective.op());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700286 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700287 }
288
sangho834e4b02015-05-01 09:38:25 -0700289 private void removeGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700290 log.debug("removeGroup in {}: for next objective id {}",
291 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700292 final GroupKey key = new DefaultGroupKey(
293 appKryo.serialize(nextObjective.id()));
294 groupService.removeGroup(deviceId, key, appId);
295 }
296
297 private void addGroup(NextObjective nextObjective) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700298 log.debug("addGroup with type{} for nextObjective id {}",
299 nextObjective.type(), nextObjective.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700300 List<GroupBucket> buckets;
sangho834e4b02015-05-01 09:38:25 -0700301 switch (nextObjective.type()) {
302 case SIMPLE:
sangho834e4b02015-05-01 09:38:25 -0700303 Collection<TrafficTreatment> treatments = nextObjective.next();
304 if (treatments.size() == 1) {
Saurav Das4ce45962015-11-24 23:21:05 -0800305 // Spring Open TTP converts simple nextObjective to flow-actions
306 // in a dummy group
307 TrafficTreatment treatment = nextObjective.next().iterator().next();
308 log.debug("Converting SIMPLE group for next objective id {} " +
309 "to {} flow-actions in device:{}", nextObjective.id(),
310 treatment.allInstructions().size(), deviceId);
311 flowObjectiveStore.putNextGroup(nextObjective.id(),
312 new SpringOpenGroup(null, treatment));
sangho834e4b02015-05-01 09:38:25 -0700313 }
314 break;
315 case HASHED:
Saurav Das8a0732e2015-11-20 15:27:53 -0800316 // we convert MPLS ECMP groups to flow-actions for a single
317 // bucket(output port).
318 boolean mplsEcmp = false;
319 if (nextObjective.meta() != null) {
320 for (Criterion c : nextObjective.meta().criteria()) {
321 if (c.type() == Type.MPLS_LABEL) {
322 mplsEcmp = true;
323 }
324 }
325 }
326 if (mplsEcmp) {
327 // covert to flow-actions in a dummy group by choosing the first bucket
328 log.debug("Converting HASHED group for next objective id {} " +
329 "to flow-actions in device:{}", nextObjective.id(),
330 deviceId);
331 TrafficTreatment treatment = nextObjective.next().iterator().next();
332 flowObjectiveStore.putNextGroup(nextObjective.id(),
333 new SpringOpenGroup(null, treatment));
334 } else {
335 // process as ECMP group
336 buckets = nextObjective
337 .next()
338 .stream()
Sho SHIMIZU07b3b0c2016-08-09 13:35:53 -0700339 .map(DefaultGroupBucket::createSelectGroupBucket)
Saurav Das8a0732e2015-11-20 15:27:53 -0800340 .collect(Collectors.toList());
341 if (!buckets.isEmpty()) {
342 final GroupKey key = new DefaultGroupKey(
343 appKryo.serialize(nextObjective.id()));
344 GroupDescription groupDescription = new DefaultGroupDescription(
345 deviceId,
346 GroupDescription.Type.SELECT,
347 new GroupBuckets(buckets),
348 key,
349 null,
350 nextObjective.appId());
351 log.debug("Creating HASHED group for next objective id {}"
352 + " in dev:{}", nextObjective.id(), deviceId);
353 pendingGroups.put(key, nextObjective);
354 groupService.addGroup(groupDescription);
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700355 verifyPendingGroupLater();
Saurav Das8a0732e2015-11-20 15:27:53 -0800356 }
sangho834e4b02015-05-01 09:38:25 -0700357 }
358 break;
359 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700360 buckets = nextObjective
361 .next()
362 .stream()
Sho SHIMIZU07b3b0c2016-08-09 13:35:53 -0700363 .map(DefaultGroupBucket::createAllGroupBucket)
Charles Chanc42e84e2015-10-20 16:24:19 -0700364 .collect(Collectors.toList());
365 if (!buckets.isEmpty()) {
366 final GroupKey key = new DefaultGroupKey(
367 appKryo.serialize(nextObjective
368 .id()));
369 GroupDescription groupDescription = new DefaultGroupDescription(
370 deviceId,
371 GroupDescription.Type.ALL,
372 new GroupBuckets(buckets),
373 key,
374 null,
375 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800376 log.debug("Creating BROADCAST group for next objective id {} "
377 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700378 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800379 groupService.addGroup(groupDescription);
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700380 verifyPendingGroupLater();
Charles Chanc42e84e2015-10-20 16:24:19 -0700381 }
382 break;
sangho834e4b02015-05-01 09:38:25 -0700383 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700384 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700385 fail(nextObjective, ObjectiveError.UNSUPPORTED);
386 log.warn("Unsupported next objective type {}", nextObjective.type());
387 break;
388 default:
389 fail(nextObjective, ObjectiveError.UNKNOWN);
390 log.warn("Unknown next objective type {}", nextObjective.type());
391 }
392 }
393
394 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700395 log.debug("addBucketToGroup in {}: for next objective id {}",
396 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700397 Collection<TrafficTreatment> treatments = nextObjective.next();
398 TrafficTreatment treatment = treatments.iterator().next();
399 final GroupKey key = new DefaultGroupKey(
400 appKryo.serialize(nextObjective
401 .id()));
402 Group group = groupService.getGroup(deviceId, key);
403 if (group == null) {
404 log.warn("Group is not found in {} for {}", deviceId, key);
405 return;
406 }
407 GroupBucket bucket;
408 if (group.type() == GroupDescription.Type.INDIRECT) {
409 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
410 } else if (group.type() == GroupDescription.Type.SELECT) {
411 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700412 } else if (group.type() == GroupDescription.Type.ALL) {
413 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700414 } else {
415 log.warn("Unsupported Group type {}", group.type());
416 return;
417 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700418 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700419 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
420 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700421 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
422 }
423
424 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700425 log.debug("removeBucketFromGroup in {}: for next objective id {}",
426 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700427 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
428 if (nextGroup != null) {
429 Collection<TrafficTreatment> treatments = nextObjective.next();
430 TrafficTreatment treatment = treatments.iterator().next();
431 final GroupKey key = new DefaultGroupKey(
432 appKryo.serialize(nextObjective
433 .id()));
434 Group group = groupService.getGroup(deviceId, key);
435 if (group == null) {
436 log.warn("Group is not found in {} for {}", deviceId, key);
437 return;
438 }
439 GroupBucket bucket;
440 if (group.type() == GroupDescription.Type.INDIRECT) {
441 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
442 } else if (group.type() == GroupDescription.Type.SELECT) {
443 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700444 } else if (group.type() == GroupDescription.Type.ALL) {
445 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700446 } else {
447 log.warn("Unsupported Group type {}", group.type());
448 return;
449 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700450 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700451 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
452 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700453 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
454 }
455 }
456
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700457 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
458 switch (fwd.flag()) {
459 case SPECIFIC:
460 return processSpecific(fwd);
461 case VERSATILE:
462 return processVersatile(fwd);
463 default:
464 fail(fwd, ObjectiveError.UNKNOWN);
465 log.warn("Unknown forwarding flag {}", fwd.flag());
466 }
467 return Collections.emptySet();
468 }
469
470 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800471 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700472 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700473 EthTypeCriterion ethType =
474 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
475 if (ethType == null) {
476 log.error("Versatile forwarding objective must include ethType");
477 fail(fwd, ObjectiveError.UNKNOWN);
478 return Collections.emptySet();
479 }
480
Saurav Das8a0732e2015-11-20 15:27:53 -0800481 if (fwd.treatment() == null && fwd.nextId() == null) {
482 log.error("VERSATILE forwarding objective needs next objective ID "
483 + "or treatment.");
484 return Collections.emptySet();
485 }
486 // emulation of ACL table (for versatile fwd objective) requires
487 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700488 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
489 .builder();
490 treatmentBuilder.wipeDeferred();
491
492 if (fwd.nextId() != null) {
493 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700494 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800495 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
496 if (soGroup.dummy) {
497 // need to convert to flow-actions
498 for (Instruction ins : soGroup.treatment.allInstructions()) {
499 treatmentBuilder.add(ins);
500 }
501 } else {
502 GroupKey key = soGroup.key;
503 Group group = groupService.getGroup(deviceId, key);
504 if (group == null) {
505 log.warn("The group left!");
506 fail(fwd, ObjectiveError.GROUPMISSING);
507 return Collections.emptySet();
508 }
509 treatmentBuilder.deferred().group(group.id());
510 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700511 }
sangho1e575652015-05-14 00:39:53 -0700512 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800513 }
514
515 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700516 if (fwd.treatment().allInstructions().size() == 1 &&
517 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
518 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
519 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan5270ed02016-01-30 23:22:37 -0800520 treatmentBuilder.popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800521 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800522 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800523 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700524 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800525 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800526 for (Instruction ins : fwd.treatment().allInstructions()) {
527 treatmentBuilder.add(ins);
528 }
Saurav Das822c4e22015-10-23 10:51:11 -0700529 }
sangho1e575652015-05-14 00:39:53 -0700530 }
531
sangho1e575652015-05-14 00:39:53 -0700532 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
533 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700534 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800535 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700536
537 if (fwd.permanent()) {
538 ruleBuilder.makePermanent();
539 } else {
540 ruleBuilder.makeTemporary(fwd.timeout());
541 }
542
543 ruleBuilder.forTable(aclTableId);
544 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700545 }
546
Charles Chan68aa62d2015-11-09 16:37:23 -0800547 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700548 TrafficSelector selector = fwd.selector();
549 EthTypeCriterion ethType = (EthTypeCriterion) selector
550 .getCriterion(Criterion.Type.ETH_TYPE);
551 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800552 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
553 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
554 return false;
555 }
556 return true;
557 }
558
559 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
560 TrafficSelector selector = fwd.selector();
561 EthCriterion ethDst = (EthCriterion) selector
562 .getCriterion(Criterion.Type.ETH_DST);
563 VlanIdCriterion vlanId = (VlanIdCriterion) selector
564 .getCriterion(Criterion.Type.VLAN_VID);
565 if (ethDst == null && vlanId == null) {
566 return false;
567 }
568 return true;
569 }
570
571 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800572 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
573 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800574 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
575 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
576
577 if (isEthTypeObj) {
578 return processEthTypeSpecificObjective(fwd);
579 } else if (isEthDstObj) {
580 return processEthDstSpecificObjective(fwd);
581 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700582 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800583 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700584 fail(fwd, ObjectiveError.UNSUPPORTED);
585 return Collections.emptySet();
586 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800587 }
588
589 protected Collection<FlowRule>
590 processEthTypeSpecificObjective(ForwardingObjective fwd) {
591 TrafficSelector selector = fwd.selector();
592 EthTypeCriterion ethType = (EthTypeCriterion) selector
593 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700594
595 TrafficSelector.Builder filteredSelectorBuilder =
596 DefaultTrafficSelector.builder();
597 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700598 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700599 filteredSelectorBuilder = filteredSelectorBuilder
600 .matchEthType(Ethernet.TYPE_IPV4)
601 .matchIPDst(((IPCriterion) selector
602 .getCriterion(Criterion.Type.IPV4_DST))
603 .ip());
604 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800605 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
606 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700607 } else {
608 filteredSelectorBuilder = filteredSelectorBuilder
609 .matchEthType(Ethernet.MPLS_UNICAST)
610 .matchMplsLabel(((MplsCriterion)
611 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
Charles Chan188ebf52015-12-23 00:15:11 -0800612 if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
613 filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion)
614 selector.getCriterion(Type.MPLS_BOS)).mplsBos());
615 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700616 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800617 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
618 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700619 }
620
621 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
622 .builder();
623 if (fwd.treatment() != null) {
624 for (Instruction i : fwd.treatment().allInstructions()) {
625 treatmentBuilder.add(i);
626 }
627 }
628
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700629 if (fwd.nextId() != null) {
630 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700631 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800632 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
633 if (soGroup.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800634 log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
635 + "in dev: {}", soGroup.treatment.allInstructions().size(),
636 fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800637 for (Instruction ins : soGroup.treatment.allInstructions()) {
638 treatmentBuilder.add(ins);
639 }
640 } else {
641 GroupKey key = soGroup.key;
642 Group group = groupService.getGroup(deviceId, key);
643 if (group == null) {
644 log.warn("The group left!");
645 fail(fwd, ObjectiveError.GROUPMISSING);
646 return Collections.emptySet();
647 }
648 treatmentBuilder.deferred().group(group.id());
649 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
Saurav Das4ce45962015-11-24 23:21:05 -0800650 + "for next:{} in dev: {}", group.id(), fwd.id(),
651 fwd.nextId(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700652 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700653 } else {
654 log.warn("processSpecific: No associated next objective object");
655 fail(fwd, ObjectiveError.GROUPMISSING);
656 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700657 }
658 }
659
660 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
661 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
662 .build();
663
664 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
665 .fromApp(fwd.appId()).withPriority(fwd.priority())
666 .forDevice(deviceId).withSelector(filteredSelector)
667 .withTreatment(treatment);
668
669 if (fwd.permanent()) {
670 ruleBuilder.makePermanent();
671 } else {
672 ruleBuilder.makeTemporary(fwd.timeout());
673 }
674
675 ruleBuilder.forTable(forTableId);
676 return Collections.singletonList(ruleBuilder.build());
677
678 }
679
Charles Chan68aa62d2015-11-09 16:37:23 -0800680 protected Collection<FlowRule>
681 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700682 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800683
684 // Build filtered selector
685 TrafficSelector selector = fwd.selector();
686 EthCriterion ethCriterion = (EthCriterion) selector
687 .getCriterion(Criterion.Type.ETH_DST);
688 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
689 .getCriterion(Criterion.Type.VLAN_VID);
690 TrafficSelector.Builder filteredSelectorBuilder =
691 DefaultTrafficSelector.builder();
692 // Do not match MacAddress for subnet broadcast entry
693 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
694 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800695 log.debug("processing L2 forwarding objective:{} in dev:{}",
696 fwd.id(), deviceId);
697 } else {
698 log.debug("processing L2 Broadcast forwarding objective:{} "
699 + "in dev:{} for vlan:{}",
700 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800701 }
702 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
703 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
704
705 // Build filtered treatment
706 TrafficTreatment.Builder treatmentBuilder =
707 DefaultTrafficTreatment.builder();
708 if (fwd.treatment() != null) {
709 treatmentBuilder.deferred();
710 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
711 }
712 if (fwd.nextId() != null) {
713 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
714 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800715 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
716 if (soGrp.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800717 log.debug("Adding {} flow-actions for fwd. obj. {} "
718 + "in dev: {}", soGrp.treatment.allInstructions().size(),
719 fwd.id(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800720 for (Instruction ins : soGrp.treatment.allInstructions()) {
Saurav Das4ce45962015-11-24 23:21:05 -0800721 treatmentBuilder.deferred().add(ins);
Saurav Das8a0732e2015-11-20 15:27:53 -0800722 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800723 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800724 GroupKey key = soGrp.key;
725 Group group = groupService.getGroup(deviceId, key);
726 if (group == null) {
727 log.warn("The group left!");
728 fail(fwd, ObjectiveError.GROUPMISSING);
729 return Collections.emptySet();
730 }
731 treatmentBuilder.deferred().group(group.id());
732 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
733 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800734 }
735 }
736 }
737 treatmentBuilder.immediate().transition(aclTableId);
738 TrafficTreatment filteredTreatment = treatmentBuilder.build();
739
740 // Build bridging table entries
741 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
742 flowRuleBuilder.fromApp(fwd.appId())
743 .withPriority(fwd.priority())
744 .forDevice(deviceId)
745 .withSelector(filteredSelector)
746 .withTreatment(filteredTreatment)
747 .forTable(dstMacTableId);
748 if (fwd.permanent()) {
749 flowRuleBuilder.makePermanent();
750 } else {
751 flowRuleBuilder.makeTemporary(fwd.timeout());
752 }
753 rules.add(flowRuleBuilder.build());
754
755 /*
756 // TODO Emulate source MAC table behavior
757 // Do not install source MAC table entry for subnet broadcast
758 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
759 // Build filtered selector
760 selector = fwd.selector();
761 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
762 filteredSelectorBuilder = DefaultTrafficSelector.builder();
763 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
764 filteredSelector = filteredSelectorBuilder.build();
765
766 // Build empty treatment. Apply existing instruction if match.
767 treatmentBuilder = DefaultTrafficTreatment.builder();
768 filteredTreatment = treatmentBuilder.build();
769
770 // Build bridging table entries
771 flowRuleBuilder = DefaultFlowRule.builder();
772 flowRuleBuilder.fromApp(fwd.appId())
773 .withPriority(fwd.priority())
774 .forDevice(deviceId)
775 .withSelector(filteredSelector)
776 .withTreatment(filteredTreatment)
777 .forTable(srcMacTableId)
778 .makePermanent();
779 rules.add(flowRuleBuilder.build());
780 }
781 */
782
783 return rules;
784 }
785
Saurav Das4ce45962015-11-24 23:21:05 -0800786 /*
787 * Note: CpqD switches do not handle MPLS-related operation properly
788 * for a packet with VLAN tag. We pop VLAN here as a workaround.
789 * Side effect: HostService learns redundant hosts with same MAC but
790 * different VLAN. No known side effect on the network reachability.
791 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800792 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
793 VlanIdCriterion vlanIdCriterion,
794 FilteringObjective filt,
795 VlanId assignedVlan,
796 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800797 if (vlanIdCriterion == null) {
798 return processEthDstOnlyFilter(ethCriterion, applicationId, filt.priority());
799 }
800
Charles Chan68aa62d2015-11-09 16:37:23 -0800801 //handling untagged packets via assigned VLAN
802 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
803 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
804 }
805
Charles Chan68aa62d2015-11-09 16:37:23 -0800806 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700807 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
808 .builder();
809 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
810 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800811 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700812 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800813 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
814 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700815 treatmentIp.transition(ipv4UnicastTableId);
816 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
817 .withSelector(selectorIp.build())
818 .withTreatment(treatmentIp.build())
819 .withPriority(filt.priority()).fromApp(applicationId)
820 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800821 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700822 rules.add(ruleIp);
823
824 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
825 .builder();
826 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
827 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800828 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700829 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800830 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
831 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700832 treatmentMpls.transition(mplsTableId);
833 FlowRule ruleMpls = DefaultFlowRule.builder()
834 .forDevice(deviceId).withSelector(selectorMpls.build())
835 .withTreatment(treatmentMpls.build())
836 .withPriority(filt.priority()).fromApp(applicationId)
837 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800838 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700839 rules.add(ruleMpls);
840
841 return rules;
842 }
843
Charles Chan5270ed02016-01-30 23:22:37 -0800844 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
845 ApplicationId applicationId, int priority) {
846 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
847 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
848 selector.matchEthType(Ethernet.TYPE_IPV4);
849 selector.matchEthDst(ethCriterion.mac());
850 treatment.transition(TABLE_IPV4_UNICAST);
851 FlowRule rule = DefaultFlowRule.builder()
852 .forDevice(deviceId)
853 .withSelector(selector.build())
854 .withTreatment(treatment.build())
855 .withPriority(priority)
856 .fromApp(applicationId)
857 .makePermanent()
858 .forTable(TABLE_TMAC).build();
859 return ImmutableList.<FlowRule>builder().add(rule).build();
860 }
861
Charles Chan68aa62d2015-11-09 16:37:23 -0800862 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700863 FilteringObjective filt,
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500864 VlanId assignedVlan, VlanId explicitlyAssignedVlan, VlanId pushedVlan,
865 boolean pushVlan, boolean popVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700866 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700867 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800868 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700869 TrafficSelector.Builder selector = DefaultTrafficSelector
870 .builder();
871 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
872 .builder();
873 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800874 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
875 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700876 selector.matchInPort(p.port());
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500877 if (popVlan) {
878 // Pop outer tag
879 treatment.immediate().popVlan();
880 }
881 if (explicitlyAssignedVlan != null && (!popVlan || !vlanIdCriterion.vlanId().equals(assignedVlan))) {
882 // Modify VLAN ID on single tagged packet or modify remaining tag after popping
883 // In the first case, do not set VLAN ID again to the already existing value
884 treatment.immediate().setVlanId(explicitlyAssignedVlan);
885 }
886 if (pushVlan) {
887 // Push new tag
888 treatment.immediate().pushVlan().setVlanId(pushedVlan);
889 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800890 } else {
891 selector.matchInPort(p.port());
892 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700893 }
894 treatment.transition(tmacTableId);
895 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
896 .withSelector(selector.build())
897 .withTreatment(treatment.build())
898 .withPriority(filt.priority()).fromApp(applicationId)
899 .makePermanent().forTable(vlanTableId).build();
900 rules.add(rule);
901
902 return rules;
903 }
904
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700905 private void processFilter(FilteringObjective filt, boolean install,
906 ApplicationId applicationId) {
907 // This driver only processes filtering criteria defined with switch
908 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700909 if (filt.key().equals(Criteria.dummy())
910 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700911 log.warn("No key defined in filtering objective from app: {}. Not"
912 + "processing filtering objective", applicationId);
913 fail(filt, ObjectiveError.UNKNOWN);
914 return;
915 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800916
917 EthCriterion ethCriterion = null;
918 VlanIdCriterion vlanIdCriterion = null;
919
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700920 // convert filtering conditions for switch-intfs into flowrules
921 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800922
923 for (Criterion criterion : filt.conditions()) {
924 if (criterion.type() == Criterion.Type.ETH_DST) {
925 ethCriterion = (EthCriterion) criterion;
926 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
927 vlanIdCriterion = (VlanIdCriterion) criterion;
928 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700929 log.debug("driver does not process IP filtering rules as it " +
930 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700931 } else {
932 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800933 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700934 fail(filt, ObjectiveError.UNSUPPORTED);
935 }
936 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800937
938 VlanId assignedVlan = null;
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500939 VlanId modifiedVlan = null;
940 VlanId pushedVlan = null;
941 boolean pushVlan = false;
942 boolean popVlan = false;
Charles Chane849c192016-01-11 18:28:54 -0800943 if (vlanIdCriterion != null) {
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500944 if (filt.meta() != null) {
945 for (Instruction i : filt.meta().allInstructions()) {
946 if (i instanceof L2ModificationInstruction) {
947 if (((L2ModificationInstruction) i).subtype()
948 .equals(L2ModificationInstruction.L2SubType.VLAN_PUSH)) {
949 pushVlan = true;
950 } else if (((L2ModificationInstruction) i).subtype()
951 .equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
952 if (modifiedVlan != null) {
953 log.error("Pop tag is not allowed after modify VLAN operation " +
954 "in filtering objective", deviceId);
955 fail(filt, ObjectiveError.BADPARAMS);
956 return;
957 }
958 popVlan = true;
959 }
960 }
961 if (i instanceof ModVlanIdInstruction) {
962 if (pushVlan && vlanIdCriterion.vlanId() != VlanId.NONE) {
963 // Modify VLAN should not appear after pushing a new tag
964 if (pushedVlan != null) {
965 log.error("Modify VLAN not allowed after push tag operation " +
966 "in filtering objective", deviceId);
967 fail(filt, ObjectiveError.BADPARAMS);
968 return;
969 }
970 pushedVlan = ((ModVlanIdInstruction) i).vlanId();
971 } else if (vlanIdCriterion.vlanId() == VlanId.NONE) {
972 // For untagged packets the pushed VLAN ID will be saved in assignedVlan
973 // just to ensure the driver works as designed for the fabric use case
974 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
975 } else {
976 // For tagged packets modifiedVlan will contain the modified value of existing tag
977 if (modifiedVlan != null) {
978 log.error("Driver does not allow multiple modify VLAN operations " +
979 "in the same filtering objective", deviceId);
980 fail(filt, ObjectiveError.BADPARAMS);
981 return;
982 }
983 modifiedVlan = ((ModVlanIdInstruction) i).vlanId();
984 }
985 }
986 }
987 }
Charles Chane849c192016-01-11 18:28:54 -0800988
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500989 // For VLAN cross-connect packets, use the configured VLAN unless there is an explicitly provided VLAN ID
990 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
991 if (assignedVlan == null) {
992 assignedVlan = vlanIdCriterion.vlanId();
993 }
Charles Chane849c192016-01-11 18:28:54 -0800994 // For untagged packets, assign a VLAN ID
995 } else {
996 if (filt.meta() == null) {
997 log.error("Missing metadata in filtering objective required " +
998 "for vlan assignment in dev {}", deviceId);
999 fail(filt, ObjectiveError.BADPARAMS);
1000 return;
Charles Chan68aa62d2015-11-09 16:37:23 -08001001 }
Charles Chane849c192016-01-11 18:28:54 -08001002 if (assignedVlan == null) {
1003 log.error("Driver requires an assigned vlan-id to tag incoming "
1004 + "untagged packets. Not processing vlan filters on "
1005 + "device {}", deviceId);
1006 fail(filt, ObjectiveError.BADPARAMS);
1007 return;
1008 }
Charles Chan68aa62d2015-11-09 16:37:23 -08001009 }
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -05001010 if (pushVlan && popVlan) {
1011 log.error("Cannot push and pop vlan in the same filtering objective");
1012 fail(filt, ObjectiveError.BADPARAMS);
1013 return;
1014 }
1015 if (popVlan && vlanIdCriterion.vlanId() == VlanId.NONE) {
1016 log.error("Cannot pop vlan for untagged packets");
1017 fail(filt, ObjectiveError.BADPARAMS);
1018 return;
1019 }
1020 if ((pushVlan && pushedVlan == null) && vlanIdCriterion.vlanId() != VlanId.NONE) {
1021 log.error("No VLAN ID provided for push tag operation");
1022 fail(filt, ObjectiveError.BADPARAMS);
1023 return;
1024 }
Charles Chan68aa62d2015-11-09 16:37:23 -08001025 }
1026
1027 if (ethCriterion == null) {
1028 log.debug("filtering objective missing dstMac, cannot program TMAC table");
1029 } else {
1030 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
1031 vlanIdCriterion,
1032 filt,
1033 assignedVlan,
1034 applicationId)) {
1035 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
1036 tmacRule, deviceId);
1037 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
1038 }
1039 }
1040
Charles Chane849c192016-01-11 18:28:54 -08001041 if (vlanIdCriterion == null) {
1042 log.debug("filtering objective missing VLAN ID criterion, "
1043 + "cannot program VLAN Table");
Charles Chan68aa62d2015-11-09 16:37:23 -08001044 } else {
1045 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
1046 filt,
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -05001047 assignedVlan, modifiedVlan, pushedVlan,
1048 pushVlan, popVlan,
Charles Chan68aa62d2015-11-09 16:37:23 -08001049 applicationId)) {
1050 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
1051 vlanRule, deviceId);
1052 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
1053 }
1054 }
1055
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001056 // apply filtering flow rules
1057 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1058 @Override
1059 public void onSuccess(FlowRuleOperations ops) {
1060 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001061 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -08001062 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001063 }
1064
1065 @Override
1066 public void onError(FlowRuleOperations ops) {
1067 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001068 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -08001069 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001070 }
1071 }));
1072 }
1073
1074 protected void setTableMissEntries() {
1075 // set all table-miss-entries
1076 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -08001077 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
1078 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001079 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -08001080 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001081 populateTableMissEntry(aclTableId, false, false, false, -1);
1082 }
1083
1084 protected void populateTableMissEntry(int tableToAdd,
1085 boolean toControllerNow,
1086 boolean toControllerWrite,
1087 boolean toTable, int tableToSend) {
1088 TrafficSelector selector = DefaultTrafficSelector.builder().build();
1089 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1090
1091 if (toControllerNow) {
1092 tBuilder.setOutput(PortNumber.CONTROLLER);
1093 }
1094
1095 if (toControllerWrite) {
1096 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
1097 }
1098
1099 if (toTable) {
1100 tBuilder.transition(tableToSend);
1101 }
1102
1103 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
1104 .withSelector(selector).withTreatment(tBuilder.build())
1105 .withPriority(0).fromApp(appId).makePermanent()
1106 .forTable(tableToAdd).build();
1107
1108 flowRuleService.applyFlowRules(flow);
1109 }
1110
1111 private void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001112 obj.context().ifPresent(context -> context.onSuccess(obj));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001113 }
1114
1115 protected void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001116 obj.context().ifPresent(context -> context.onError(obj, error));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001117 }
1118
1119 private class InnerGroupListener implements GroupListener {
1120 @Override
1121 public void event(GroupEvent event) {
1122 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001123 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001124 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001125 GroupKey key = event.subject().appCookie();
1126
1127 NextObjective obj = pendingGroups.getIfPresent(key);
1128 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001129 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1130 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001131 flowObjectiveStore
1132 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001133 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001134 pass(obj);
1135 pendingGroups.invalidate(key);
1136 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001137 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1138 log.warn("InnerGroupListener: Group ADD "
1139 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001140 }
1141 }
1142 }
1143
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -07001144 private void verifyPendingGroupLater() {
1145 GROUP_CHECKER.schedule(new GroupChecker(), CHECK_DELAY, TimeUnit.MILLISECONDS);
1146 }
1147
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001148 private class GroupChecker implements Runnable {
1149
1150 @Override
1151 public void run() {
1152 Set<GroupKey> keys = pendingGroups
1153 .asMap()
1154 .keySet()
1155 .stream()
1156 .filter(key -> groupService.getGroup(deviceId, key) != null)
1157 .collect(Collectors.toSet());
1158
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -07001159 keys.forEach(key -> {
1160 NextObjective obj = pendingGroups
1161 .getIfPresent(key);
1162 if (obj == null) {
1163 return;
1164 }
1165 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1166 deviceId,
1167 groupService.getGroup(deviceId, key).id(),
1168 obj.id());
1169 pass(obj);
1170 pendingGroups.invalidate(key);
1171 flowObjectiveStore.putNextGroup(
1172 obj.id(),
1173 new SpringOpenGroup(key, null));
1174 });
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -07001175
1176 if (!pendingGroups.asMap().isEmpty()) {
1177 // Periodically execute only if entry remains in pendingGroups.
1178 // Iterating pendingGroups trigger cleanUp and expiration,
1179 // which will eventually empty the pendingGroups.
1180 verifyPendingGroupLater();
1181 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001182 }
1183 }
1184
Saurav Das8a0732e2015-11-20 15:27:53 -08001185 /**
1186 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1187 * used to fetch the group from the Group Service, or it can be serve as storage
1188 * for Traffic Treatments which can be used as flow actions. In the latter
1189 * case, we refer to this as a dummy group.
1190 *
1191 */
Charles Chane3ba6952016-05-02 11:02:36 -07001192 protected class SpringOpenGroup implements NextGroup {
Saurav Das8a0732e2015-11-20 15:27:53 -08001193 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001194 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001195 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001196
Saurav Das8a0732e2015-11-20 15:27:53 -08001197 /**
1198 * Storage for a GroupKey or a TrafficTreatment. One of the params
1199 * to this constructor must be null.
1200 * @param key represents a GroupKey
1201 * @param treatment represents flow actions in a dummy group
1202 */
1203 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1204 if (key == null) {
1205 this.key = new DefaultGroupKey(new byte[]{0});
1206 this.treatment = treatment;
1207 this.dummy = true;
1208 } else {
1209 this.key = key;
1210 this.treatment = DefaultTrafficTreatment.builder().build();
1211 this.dummy = false;
1212 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001213 }
1214
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001215 public GroupKey key() {
1216 return key;
1217 }
1218
Saurav Das77621792016-05-03 16:36:57 -07001219 public boolean dummy() {
1220 return dummy;
1221 }
1222
1223 public TrafficTreatment treatment() {
1224 return treatment;
1225 }
1226
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001227 @Override
1228 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001229 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001230 }
1231
1232 }
Saurav Das24431192016-03-07 19:13:00 -08001233
1234 @Override
1235 public List<String> getNextMappings(NextGroup nextGroup) {
1236 // TODO Implementation deferred to vendor
1237 return null;
1238 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001239}