blob: ca50df88e66773dc40c8d64eb5d42c6a07ae19b7 [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()
339 .map((treatment) -> DefaultGroupBucket
340 .createSelectGroupBucket(treatment))
341 .collect(Collectors.toList());
342 if (!buckets.isEmpty()) {
343 final GroupKey key = new DefaultGroupKey(
344 appKryo.serialize(nextObjective.id()));
345 GroupDescription groupDescription = new DefaultGroupDescription(
346 deviceId,
347 GroupDescription.Type.SELECT,
348 new GroupBuckets(buckets),
349 key,
350 null,
351 nextObjective.appId());
352 log.debug("Creating HASHED group for next objective id {}"
353 + " in dev:{}", nextObjective.id(), deviceId);
354 pendingGroups.put(key, nextObjective);
355 groupService.addGroup(groupDescription);
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700356 verifyPendingGroupLater();
Saurav Das8a0732e2015-11-20 15:27:53 -0800357 }
sangho834e4b02015-05-01 09:38:25 -0700358 }
359 break;
360 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700361 buckets = nextObjective
362 .next()
363 .stream()
364 .map((treatment) -> DefaultGroupBucket
365 .createAllGroupBucket(treatment))
366 .collect(Collectors.toList());
367 if (!buckets.isEmpty()) {
368 final GroupKey key = new DefaultGroupKey(
369 appKryo.serialize(nextObjective
370 .id()));
371 GroupDescription groupDescription = new DefaultGroupDescription(
372 deviceId,
373 GroupDescription.Type.ALL,
374 new GroupBuckets(buckets),
375 key,
376 null,
377 nextObjective.appId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800378 log.debug("Creating BROADCAST group for next objective id {} "
379 + "in device {}", nextObjective.id(), deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700380 pendingGroups.put(key, nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800381 groupService.addGroup(groupDescription);
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -0700382 verifyPendingGroupLater();
Charles Chanc42e84e2015-10-20 16:24:19 -0700383 }
384 break;
sangho834e4b02015-05-01 09:38:25 -0700385 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700386 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700387 fail(nextObjective, ObjectiveError.UNSUPPORTED);
388 log.warn("Unsupported next objective type {}", nextObjective.type());
389 break;
390 default:
391 fail(nextObjective, ObjectiveError.UNKNOWN);
392 log.warn("Unknown next objective type {}", nextObjective.type());
393 }
394 }
395
396 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700397 log.debug("addBucketToGroup in {}: for next objective id {}",
398 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700399 Collection<TrafficTreatment> treatments = nextObjective.next();
400 TrafficTreatment treatment = treatments.iterator().next();
401 final GroupKey key = new DefaultGroupKey(
402 appKryo.serialize(nextObjective
403 .id()));
404 Group group = groupService.getGroup(deviceId, key);
405 if (group == null) {
406 log.warn("Group is not found in {} for {}", deviceId, key);
407 return;
408 }
409 GroupBucket bucket;
410 if (group.type() == GroupDescription.Type.INDIRECT) {
411 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
412 } else if (group.type() == GroupDescription.Type.SELECT) {
413 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700414 } else if (group.type() == GroupDescription.Type.ALL) {
415 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700416 } else {
417 log.warn("Unsupported Group type {}", group.type());
418 return;
419 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700420 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700421 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
422 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700423 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
424 }
425
426 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700427 log.debug("removeBucketFromGroup in {}: for next objective id {}",
428 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700429 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
430 if (nextGroup != null) {
431 Collection<TrafficTreatment> treatments = nextObjective.next();
432 TrafficTreatment treatment = treatments.iterator().next();
433 final GroupKey key = new DefaultGroupKey(
434 appKryo.serialize(nextObjective
435 .id()));
436 Group group = groupService.getGroup(deviceId, key);
437 if (group == null) {
438 log.warn("Group is not found in {} for {}", deviceId, key);
439 return;
440 }
441 GroupBucket bucket;
442 if (group.type() == GroupDescription.Type.INDIRECT) {
443 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
444 } else if (group.type() == GroupDescription.Type.SELECT) {
445 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700446 } else if (group.type() == GroupDescription.Type.ALL) {
447 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700448 } else {
449 log.warn("Unsupported Group type {}", group.type());
450 return;
451 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700452 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700453 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
454 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700455 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
456 }
457 }
458
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700459 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
460 switch (fwd.flag()) {
461 case SPECIFIC:
462 return processSpecific(fwd);
463 case VERSATILE:
464 return processVersatile(fwd);
465 default:
466 fail(fwd, ObjectiveError.UNKNOWN);
467 log.warn("Unknown forwarding flag {}", fwd.flag());
468 }
469 return Collections.emptySet();
470 }
471
472 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800473 log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
sangho1e575652015-05-14 00:39:53 -0700474 TrafficSelector selector = fwd.selector();
sangho1e575652015-05-14 00:39:53 -0700475 EthTypeCriterion ethType =
476 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
477 if (ethType == null) {
478 log.error("Versatile forwarding objective must include ethType");
479 fail(fwd, ObjectiveError.UNKNOWN);
480 return Collections.emptySet();
481 }
482
Saurav Das8a0732e2015-11-20 15:27:53 -0800483 if (fwd.treatment() == null && fwd.nextId() == null) {
484 log.error("VERSATILE forwarding objective needs next objective ID "
485 + "or treatment.");
486 return Collections.emptySet();
487 }
488 // emulation of ACL table (for versatile fwd objective) requires
489 // overriding any previous instructions
sangho1e575652015-05-14 00:39:53 -0700490 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
491 .builder();
492 treatmentBuilder.wipeDeferred();
493
494 if (fwd.nextId() != null) {
495 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
sangho1e575652015-05-14 00:39:53 -0700496 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800497 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
498 if (soGroup.dummy) {
499 // need to convert to flow-actions
500 for (Instruction ins : soGroup.treatment.allInstructions()) {
501 treatmentBuilder.add(ins);
502 }
503 } else {
504 GroupKey key = soGroup.key;
505 Group group = groupService.getGroup(deviceId, key);
506 if (group == null) {
507 log.warn("The group left!");
508 fail(fwd, ObjectiveError.GROUPMISSING);
509 return Collections.emptySet();
510 }
511 treatmentBuilder.deferred().group(group.id());
512 log.debug("Adding OUTGROUP action");
sangho1e575652015-05-14 00:39:53 -0700513 }
sangho1e575652015-05-14 00:39:53 -0700514 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800515 }
516
517 if (fwd.treatment() != null) {
Saurav Das822c4e22015-10-23 10:51:11 -0700518 if (fwd.treatment().allInstructions().size() == 1 &&
519 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
520 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
521 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan5270ed02016-01-30 23:22:37 -0800522 treatmentBuilder.popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800523 treatmentBuilder.punt();
Charles Chan68aa62d2015-11-09 16:37:23 -0800524 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800525 treatmentBuilder.add(o);
Saurav Das822c4e22015-10-23 10:51:11 -0700526 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800527 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800528 for (Instruction ins : fwd.treatment().allInstructions()) {
529 treatmentBuilder.add(ins);
530 }
Saurav Das822c4e22015-10-23 10:51:11 -0700531 }
sangho1e575652015-05-14 00:39:53 -0700532 }
533
sangho1e575652015-05-14 00:39:53 -0700534 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
535 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700536 .forDevice(deviceId).withSelector(fwd.selector())
Saurav Das8a0732e2015-11-20 15:27:53 -0800537 .withTreatment(treatmentBuilder.build());
sangho1e575652015-05-14 00:39:53 -0700538
539 if (fwd.permanent()) {
540 ruleBuilder.makePermanent();
541 } else {
542 ruleBuilder.makeTemporary(fwd.timeout());
543 }
544
545 ruleBuilder.forTable(aclTableId);
546 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700547 }
548
Charles Chan68aa62d2015-11-09 16:37:23 -0800549 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700550 TrafficSelector selector = fwd.selector();
551 EthTypeCriterion ethType = (EthTypeCriterion) selector
552 .getCriterion(Criterion.Type.ETH_TYPE);
553 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800554 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
555 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
556 return false;
557 }
558 return true;
559 }
560
561 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
562 TrafficSelector selector = fwd.selector();
563 EthCriterion ethDst = (EthCriterion) selector
564 .getCriterion(Criterion.Type.ETH_DST);
565 VlanIdCriterion vlanId = (VlanIdCriterion) selector
566 .getCriterion(Criterion.Type.VLAN_VID);
567 if (ethDst == null && vlanId == null) {
568 return false;
569 }
570 return true;
571 }
572
573 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800574 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
575 fwd.id(), deviceId, fwd.nextId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800576 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
577 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
578
579 if (isEthTypeObj) {
580 return processEthTypeSpecificObjective(fwd);
581 } else if (isEthDstObj) {
582 return processEthDstSpecificObjective(fwd);
583 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700584 log.warn("processSpecific: Unsupported "
Saurav Das8a0732e2015-11-20 15:27:53 -0800585 + "forwarding objective criteria");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700586 fail(fwd, ObjectiveError.UNSUPPORTED);
587 return Collections.emptySet();
588 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800589 }
590
591 protected Collection<FlowRule>
592 processEthTypeSpecificObjective(ForwardingObjective fwd) {
593 TrafficSelector selector = fwd.selector();
594 EthTypeCriterion ethType = (EthTypeCriterion) selector
595 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700596
597 TrafficSelector.Builder filteredSelectorBuilder =
598 DefaultTrafficSelector.builder();
599 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700600 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700601 filteredSelectorBuilder = filteredSelectorBuilder
602 .matchEthType(Ethernet.TYPE_IPV4)
603 .matchIPDst(((IPCriterion) selector
604 .getCriterion(Criterion.Type.IPV4_DST))
605 .ip());
606 forTableId = ipv4UnicastTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800607 log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
608 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700609 } else {
610 filteredSelectorBuilder = filteredSelectorBuilder
611 .matchEthType(Ethernet.MPLS_UNICAST)
612 .matchMplsLabel(((MplsCriterion)
613 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
Charles Chan188ebf52015-12-23 00:15:11 -0800614 if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
615 filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion)
616 selector.getCriterion(Type.MPLS_BOS)).mplsBos());
617 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700618 forTableId = mplsTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800619 log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
620 fwd.id(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700621 }
622
623 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
624 .builder();
625 if (fwd.treatment() != null) {
626 for (Instruction i : fwd.treatment().allInstructions()) {
627 treatmentBuilder.add(i);
628 }
629 }
630
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700631 if (fwd.nextId() != null) {
632 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700633 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800634 SpringOpenGroup soGroup = appKryo.deserialize(next.data());
635 if (soGroup.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800636 log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
637 + "in dev: {}", soGroup.treatment.allInstructions().size(),
638 fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800639 for (Instruction ins : soGroup.treatment.allInstructions()) {
640 treatmentBuilder.add(ins);
641 }
642 } else {
643 GroupKey key = soGroup.key;
644 Group group = groupService.getGroup(deviceId, key);
645 if (group == null) {
646 log.warn("The group left!");
647 fail(fwd, ObjectiveError.GROUPMISSING);
648 return Collections.emptySet();
649 }
650 treatmentBuilder.deferred().group(group.id());
651 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
Saurav Das4ce45962015-11-24 23:21:05 -0800652 + "for next:{} in dev: {}", group.id(), fwd.id(),
653 fwd.nextId(), deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700654 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700655 } else {
656 log.warn("processSpecific: No associated next objective object");
657 fail(fwd, ObjectiveError.GROUPMISSING);
658 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700659 }
660 }
661
662 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
663 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
664 .build();
665
666 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
667 .fromApp(fwd.appId()).withPriority(fwd.priority())
668 .forDevice(deviceId).withSelector(filteredSelector)
669 .withTreatment(treatment);
670
671 if (fwd.permanent()) {
672 ruleBuilder.makePermanent();
673 } else {
674 ruleBuilder.makeTemporary(fwd.timeout());
675 }
676
677 ruleBuilder.forTable(forTableId);
678 return Collections.singletonList(ruleBuilder.build());
679
680 }
681
Charles Chan68aa62d2015-11-09 16:37:23 -0800682 protected Collection<FlowRule>
683 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700684 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800685
686 // Build filtered selector
687 TrafficSelector selector = fwd.selector();
688 EthCriterion ethCriterion = (EthCriterion) selector
689 .getCriterion(Criterion.Type.ETH_DST);
690 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
691 .getCriterion(Criterion.Type.VLAN_VID);
692 TrafficSelector.Builder filteredSelectorBuilder =
693 DefaultTrafficSelector.builder();
694 // Do not match MacAddress for subnet broadcast entry
695 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
696 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
Saurav Das8a0732e2015-11-20 15:27:53 -0800697 log.debug("processing L2 forwarding objective:{} in dev:{}",
698 fwd.id(), deviceId);
699 } else {
700 log.debug("processing L2 Broadcast forwarding objective:{} "
701 + "in dev:{} for vlan:{}",
702 fwd.id(), deviceId, vlanIdCriterion.vlanId());
Charles Chan68aa62d2015-11-09 16:37:23 -0800703 }
704 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
705 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
706
707 // Build filtered treatment
708 TrafficTreatment.Builder treatmentBuilder =
709 DefaultTrafficTreatment.builder();
710 if (fwd.treatment() != null) {
711 treatmentBuilder.deferred();
712 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
713 }
714 if (fwd.nextId() != null) {
715 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
716 if (next != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800717 SpringOpenGroup soGrp = appKryo.deserialize(next.data());
718 if (soGrp.dummy) {
Saurav Das4ce45962015-11-24 23:21:05 -0800719 log.debug("Adding {} flow-actions for fwd. obj. {} "
720 + "in dev: {}", soGrp.treatment.allInstructions().size(),
721 fwd.id(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800722 for (Instruction ins : soGrp.treatment.allInstructions()) {
Saurav Das4ce45962015-11-24 23:21:05 -0800723 treatmentBuilder.deferred().add(ins);
Saurav Das8a0732e2015-11-20 15:27:53 -0800724 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800725 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800726 GroupKey key = soGrp.key;
727 Group group = groupService.getGroup(deviceId, key);
728 if (group == null) {
729 log.warn("The group left!");
730 fail(fwd, ObjectiveError.GROUPMISSING);
731 return Collections.emptySet();
732 }
733 treatmentBuilder.deferred().group(group.id());
734 log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
735 + "in dev: {}", group.id(), fwd.id(), deviceId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800736 }
737 }
738 }
739 treatmentBuilder.immediate().transition(aclTableId);
740 TrafficTreatment filteredTreatment = treatmentBuilder.build();
741
742 // Build bridging table entries
743 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
744 flowRuleBuilder.fromApp(fwd.appId())
745 .withPriority(fwd.priority())
746 .forDevice(deviceId)
747 .withSelector(filteredSelector)
748 .withTreatment(filteredTreatment)
749 .forTable(dstMacTableId);
750 if (fwd.permanent()) {
751 flowRuleBuilder.makePermanent();
752 } else {
753 flowRuleBuilder.makeTemporary(fwd.timeout());
754 }
755 rules.add(flowRuleBuilder.build());
756
757 /*
758 // TODO Emulate source MAC table behavior
759 // Do not install source MAC table entry for subnet broadcast
760 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
761 // Build filtered selector
762 selector = fwd.selector();
763 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
764 filteredSelectorBuilder = DefaultTrafficSelector.builder();
765 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
766 filteredSelector = filteredSelectorBuilder.build();
767
768 // Build empty treatment. Apply existing instruction if match.
769 treatmentBuilder = DefaultTrafficTreatment.builder();
770 filteredTreatment = treatmentBuilder.build();
771
772 // Build bridging table entries
773 flowRuleBuilder = DefaultFlowRule.builder();
774 flowRuleBuilder.fromApp(fwd.appId())
775 .withPriority(fwd.priority())
776 .forDevice(deviceId)
777 .withSelector(filteredSelector)
778 .withTreatment(filteredTreatment)
779 .forTable(srcMacTableId)
780 .makePermanent();
781 rules.add(flowRuleBuilder.build());
782 }
783 */
784
785 return rules;
786 }
787
Saurav Das4ce45962015-11-24 23:21:05 -0800788 /*
789 * Note: CpqD switches do not handle MPLS-related operation properly
790 * for a packet with VLAN tag. We pop VLAN here as a workaround.
791 * Side effect: HostService learns redundant hosts with same MAC but
792 * different VLAN. No known side effect on the network reachability.
793 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800794 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
795 VlanIdCriterion vlanIdCriterion,
796 FilteringObjective filt,
797 VlanId assignedVlan,
798 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800799 if (vlanIdCriterion == null) {
800 return processEthDstOnlyFilter(ethCriterion, applicationId, filt.priority());
801 }
802
Charles Chan68aa62d2015-11-09 16:37:23 -0800803 //handling untagged packets via assigned VLAN
804 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
805 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
806 }
807
Charles Chan68aa62d2015-11-09 16:37:23 -0800808 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700809 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
810 .builder();
811 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
812 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800813 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700814 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800815 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
816 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700817 treatmentIp.transition(ipv4UnicastTableId);
818 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
819 .withSelector(selectorIp.build())
820 .withTreatment(treatmentIp.build())
821 .withPriority(filt.priority()).fromApp(applicationId)
822 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800823 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700824 rules.add(ruleIp);
825
826 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
827 .builder();
828 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
829 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800830 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700831 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800832 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
833 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700834 treatmentMpls.transition(mplsTableId);
835 FlowRule ruleMpls = DefaultFlowRule.builder()
836 .forDevice(deviceId).withSelector(selectorMpls.build())
837 .withTreatment(treatmentMpls.build())
838 .withPriority(filt.priority()).fromApp(applicationId)
839 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800840 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700841 rules.add(ruleMpls);
842
843 return rules;
844 }
845
Charles Chan5270ed02016-01-30 23:22:37 -0800846 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
847 ApplicationId applicationId, int priority) {
848 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
849 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
850 selector.matchEthType(Ethernet.TYPE_IPV4);
851 selector.matchEthDst(ethCriterion.mac());
852 treatment.transition(TABLE_IPV4_UNICAST);
853 FlowRule rule = DefaultFlowRule.builder()
854 .forDevice(deviceId)
855 .withSelector(selector.build())
856 .withTreatment(treatment.build())
857 .withPriority(priority)
858 .fromApp(applicationId)
859 .makePermanent()
860 .forTable(TABLE_TMAC).build();
861 return ImmutableList.<FlowRule>builder().add(rule).build();
862 }
863
Charles Chan68aa62d2015-11-09 16:37:23 -0800864 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700865 FilteringObjective filt,
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500866 VlanId assignedVlan, VlanId explicitlyAssignedVlan, VlanId pushedVlan,
867 boolean pushVlan, boolean popVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700868 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700869 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800870 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700871 TrafficSelector.Builder selector = DefaultTrafficSelector
872 .builder();
873 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
874 .builder();
875 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800876 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
877 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700878 selector.matchInPort(p.port());
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500879 if (popVlan) {
880 // Pop outer tag
881 treatment.immediate().popVlan();
882 }
883 if (explicitlyAssignedVlan != null && (!popVlan || !vlanIdCriterion.vlanId().equals(assignedVlan))) {
884 // Modify VLAN ID on single tagged packet or modify remaining tag after popping
885 // In the first case, do not set VLAN ID again to the already existing value
886 treatment.immediate().setVlanId(explicitlyAssignedVlan);
887 }
888 if (pushVlan) {
889 // Push new tag
890 treatment.immediate().pushVlan().setVlanId(pushedVlan);
891 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800892 } else {
893 selector.matchInPort(p.port());
894 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700895 }
896 treatment.transition(tmacTableId);
897 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
898 .withSelector(selector.build())
899 .withTreatment(treatment.build())
900 .withPriority(filt.priority()).fromApp(applicationId)
901 .makePermanent().forTable(vlanTableId).build();
902 rules.add(rule);
903
904 return rules;
905 }
906
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700907 private void processFilter(FilteringObjective filt, boolean install,
908 ApplicationId applicationId) {
909 // This driver only processes filtering criteria defined with switch
910 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700911 if (filt.key().equals(Criteria.dummy())
912 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700913 log.warn("No key defined in filtering objective from app: {}. Not"
914 + "processing filtering objective", applicationId);
915 fail(filt, ObjectiveError.UNKNOWN);
916 return;
917 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800918
919 EthCriterion ethCriterion = null;
920 VlanIdCriterion vlanIdCriterion = null;
921
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700922 // convert filtering conditions for switch-intfs into flowrules
923 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800924
925 for (Criterion criterion : filt.conditions()) {
926 if (criterion.type() == Criterion.Type.ETH_DST) {
927 ethCriterion = (EthCriterion) criterion;
928 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
929 vlanIdCriterion = (VlanIdCriterion) criterion;
930 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700931 log.debug("driver does not process IP filtering rules as it " +
932 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700933 } else {
934 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800935 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700936 fail(filt, ObjectiveError.UNSUPPORTED);
937 }
938 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800939
940 VlanId assignedVlan = null;
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500941 VlanId modifiedVlan = null;
942 VlanId pushedVlan = null;
943 boolean pushVlan = false;
944 boolean popVlan = false;
Charles Chane849c192016-01-11 18:28:54 -0800945 if (vlanIdCriterion != null) {
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500946 if (filt.meta() != null) {
947 for (Instruction i : filt.meta().allInstructions()) {
948 if (i instanceof L2ModificationInstruction) {
949 if (((L2ModificationInstruction) i).subtype()
950 .equals(L2ModificationInstruction.L2SubType.VLAN_PUSH)) {
951 pushVlan = true;
952 } else if (((L2ModificationInstruction) i).subtype()
953 .equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
954 if (modifiedVlan != null) {
955 log.error("Pop tag is not allowed after modify VLAN operation " +
956 "in filtering objective", deviceId);
957 fail(filt, ObjectiveError.BADPARAMS);
958 return;
959 }
960 popVlan = true;
961 }
962 }
963 if (i instanceof ModVlanIdInstruction) {
964 if (pushVlan && vlanIdCriterion.vlanId() != VlanId.NONE) {
965 // Modify VLAN should not appear after pushing a new tag
966 if (pushedVlan != null) {
967 log.error("Modify VLAN not allowed after push tag operation " +
968 "in filtering objective", deviceId);
969 fail(filt, ObjectiveError.BADPARAMS);
970 return;
971 }
972 pushedVlan = ((ModVlanIdInstruction) i).vlanId();
973 } else if (vlanIdCriterion.vlanId() == VlanId.NONE) {
974 // For untagged packets the pushed VLAN ID will be saved in assignedVlan
975 // just to ensure the driver works as designed for the fabric use case
976 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
977 } else {
978 // For tagged packets modifiedVlan will contain the modified value of existing tag
979 if (modifiedVlan != null) {
980 log.error("Driver does not allow multiple modify VLAN operations " +
981 "in the same filtering objective", deviceId);
982 fail(filt, ObjectiveError.BADPARAMS);
983 return;
984 }
985 modifiedVlan = ((ModVlanIdInstruction) i).vlanId();
986 }
987 }
988 }
989 }
Charles Chane849c192016-01-11 18:28:54 -0800990
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -0500991 // For VLAN cross-connect packets, use the configured VLAN unless there is an explicitly provided VLAN ID
992 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
993 if (assignedVlan == null) {
994 assignedVlan = vlanIdCriterion.vlanId();
995 }
Charles Chane849c192016-01-11 18:28:54 -0800996 // For untagged packets, assign a VLAN ID
997 } else {
998 if (filt.meta() == null) {
999 log.error("Missing metadata in filtering objective required " +
1000 "for vlan assignment in dev {}", deviceId);
1001 fail(filt, ObjectiveError.BADPARAMS);
1002 return;
Charles Chan68aa62d2015-11-09 16:37:23 -08001003 }
Charles Chane849c192016-01-11 18:28:54 -08001004 if (assignedVlan == null) {
1005 log.error("Driver requires an assigned vlan-id to tag incoming "
1006 + "untagged packets. Not processing vlan filters on "
1007 + "device {}", deviceId);
1008 fail(filt, ObjectiveError.BADPARAMS);
1009 return;
1010 }
Charles Chan68aa62d2015-11-09 16:37:23 -08001011 }
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -05001012 if (pushVlan && popVlan) {
1013 log.error("Cannot push and pop vlan in the same filtering objective");
1014 fail(filt, ObjectiveError.BADPARAMS);
1015 return;
1016 }
1017 if (popVlan && vlanIdCriterion.vlanId() == VlanId.NONE) {
1018 log.error("Cannot pop vlan for untagged packets");
1019 fail(filt, ObjectiveError.BADPARAMS);
1020 return;
1021 }
1022 if ((pushVlan && pushedVlan == null) && vlanIdCriterion.vlanId() != VlanId.NONE) {
1023 log.error("No VLAN ID provided for push tag operation");
1024 fail(filt, ObjectiveError.BADPARAMS);
1025 return;
1026 }
Charles Chan68aa62d2015-11-09 16:37:23 -08001027 }
1028
1029 if (ethCriterion == null) {
1030 log.debug("filtering objective missing dstMac, cannot program TMAC table");
1031 } else {
1032 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
1033 vlanIdCriterion,
1034 filt,
1035 assignedVlan,
1036 applicationId)) {
1037 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
1038 tmacRule, deviceId);
1039 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
1040 }
1041 }
1042
Charles Chane849c192016-01-11 18:28:54 -08001043 if (vlanIdCriterion == null) {
1044 log.debug("filtering objective missing VLAN ID criterion, "
1045 + "cannot program VLAN Table");
Charles Chan68aa62d2015-11-09 16:37:23 -08001046 } else {
1047 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
1048 filt,
Konstantinos Kanonakisa477e342016-06-03 13:27:27 -05001049 assignedVlan, modifiedVlan, pushedVlan,
1050 pushVlan, popVlan,
Charles Chan68aa62d2015-11-09 16:37:23 -08001051 applicationId)) {
1052 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
1053 vlanRule, deviceId);
1054 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
1055 }
1056 }
1057
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001058 // apply filtering flow rules
1059 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1060 @Override
1061 public void onSuccess(FlowRuleOperations ops) {
1062 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001063 log.debug("Provisioned tables in {} with fitering "
Saurav Das8a0732e2015-11-20 15:27:53 -08001064 + "rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001065 }
1066
1067 @Override
1068 public void onError(FlowRuleOperations ops) {
1069 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001070 log.warn("Failed to provision tables in {} with "
Saurav Das8a0732e2015-11-20 15:27:53 -08001071 + "fitering rules", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001072 }
1073 }));
1074 }
1075
1076 protected void setTableMissEntries() {
1077 // set all table-miss-entries
1078 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -08001079 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
1080 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001081 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -08001082 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001083 populateTableMissEntry(aclTableId, false, false, false, -1);
1084 }
1085
1086 protected void populateTableMissEntry(int tableToAdd,
1087 boolean toControllerNow,
1088 boolean toControllerWrite,
1089 boolean toTable, int tableToSend) {
1090 TrafficSelector selector = DefaultTrafficSelector.builder().build();
1091 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1092
1093 if (toControllerNow) {
1094 tBuilder.setOutput(PortNumber.CONTROLLER);
1095 }
1096
1097 if (toControllerWrite) {
1098 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
1099 }
1100
1101 if (toTable) {
1102 tBuilder.transition(tableToSend);
1103 }
1104
1105 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
1106 .withSelector(selector).withTreatment(tBuilder.build())
1107 .withPriority(0).fromApp(appId).makePermanent()
1108 .forTable(tableToAdd).build();
1109
1110 flowRuleService.applyFlowRules(flow);
1111 }
1112
1113 private void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001114 obj.context().ifPresent(context -> context.onSuccess(obj));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001115 }
1116
1117 protected void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001118 obj.context().ifPresent(context -> context.onError(obj, error));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001119 }
1120
1121 private class InnerGroupListener implements GroupListener {
1122 @Override
1123 public void event(GroupEvent event) {
1124 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001125 log.trace("InnerGroupListener: Group ADDED "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001126 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001127 GroupKey key = event.subject().appCookie();
1128
1129 NextObjective obj = pendingGroups.getIfPresent(key);
1130 if (obj != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001131 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1132 deviceId, event.subject().id(), obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001133 flowObjectiveStore
1134 .putNextGroup(obj.id(),
Saurav Das8a0732e2015-11-20 15:27:53 -08001135 new SpringOpenGroup(key, null));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001136 pass(obj);
1137 pendingGroups.invalidate(key);
1138 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001139 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
1140 log.warn("InnerGroupListener: Group ADD "
1141 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001142 }
1143 }
1144 }
1145
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -07001146 private void verifyPendingGroupLater() {
1147 GROUP_CHECKER.schedule(new GroupChecker(), CHECK_DELAY, TimeUnit.MILLISECONDS);
1148 }
1149
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001150 private class GroupChecker implements Runnable {
1151
1152 @Override
1153 public void run() {
1154 Set<GroupKey> keys = pendingGroups
1155 .asMap()
1156 .keySet()
1157 .stream()
1158 .filter(key -> groupService.getGroup(deviceId, key) != null)
1159 .collect(Collectors.toSet());
1160
1161 keys.stream()
1162 .forEach(key -> {
1163 NextObjective obj = pendingGroups
1164 .getIfPresent(key);
1165 if (obj == null) {
1166 return;
1167 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001168 log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1169 deviceId,
1170 groupService.getGroup(deviceId, key).id(),
1171 obj.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001172 pass(obj);
1173 pendingGroups.invalidate(key);
Saurav Das8a0732e2015-11-20 15:27:53 -08001174 flowObjectiveStore.putNextGroup(
1175 obj.id(),
1176 new SpringOpenGroup(key, null));
1177 });
Yuta HIGUCHI6a1ee2d2016-07-24 00:21:26 -07001178
1179 if (!pendingGroups.asMap().isEmpty()) {
1180 // Periodically execute only if entry remains in pendingGroups.
1181 // Iterating pendingGroups trigger cleanUp and expiration,
1182 // which will eventually empty the pendingGroups.
1183 verifyPendingGroupLater();
1184 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001185 }
1186 }
1187
Saurav Das8a0732e2015-11-20 15:27:53 -08001188 /**
1189 * SpringOpenGroup can either serve as storage for a GroupKey which can be
1190 * used to fetch the group from the Group Service, or it can be serve as storage
1191 * for Traffic Treatments which can be used as flow actions. In the latter
1192 * case, we refer to this as a dummy group.
1193 *
1194 */
Charles Chane3ba6952016-05-02 11:02:36 -07001195 protected class SpringOpenGroup implements NextGroup {
Saurav Das8a0732e2015-11-20 15:27:53 -08001196 private final boolean dummy;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001197 private final GroupKey key;
Saurav Das8a0732e2015-11-20 15:27:53 -08001198 private final TrafficTreatment treatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001199
Saurav Das8a0732e2015-11-20 15:27:53 -08001200 /**
1201 * Storage for a GroupKey or a TrafficTreatment. One of the params
1202 * to this constructor must be null.
1203 * @param key represents a GroupKey
1204 * @param treatment represents flow actions in a dummy group
1205 */
1206 public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1207 if (key == null) {
1208 this.key = new DefaultGroupKey(new byte[]{0});
1209 this.treatment = treatment;
1210 this.dummy = true;
1211 } else {
1212 this.key = key;
1213 this.treatment = DefaultTrafficTreatment.builder().build();
1214 this.dummy = false;
1215 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001216 }
1217
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001218 public GroupKey key() {
1219 return key;
1220 }
1221
Saurav Das77621792016-05-03 16:36:57 -07001222 public boolean dummy() {
1223 return dummy;
1224 }
1225
1226 public TrafficTreatment treatment() {
1227 return treatment;
1228 }
1229
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001230 @Override
1231 public byte[] data() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001232 return appKryo.serialize(this);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001233 }
1234
1235 }
Saurav Das24431192016-03-07 19:13:00 -08001236
1237 @Override
1238 public List<String> getNextMappings(NextGroup nextGroup) {
1239 // TODO Implementation deferred to vendor
1240 return null;
1241 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001242}