blob: cb1a6505f0a36b6f4818a3fbbc5d4b9e2ef10d06 [file] [log] [blame]
Saurav Das822c4e22015-10-23 10:51:11 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.driver.pipeline;
17
18import static org.onlab.util.Tools.groupedThreads;
19import static org.slf4j.LoggerFactory.getLogger;
20
Saurav Das8a0732e2015-11-20 15:27:53 -080021import java.util.ArrayDeque;
Saurav Das822c4e22015-10-23 10:51:11 -070022import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
Saurav Das8a0732e2015-11-20 15:27:53 -080025import java.util.Deque;
Saurav Das822c4e22015-10-23 10:51:11 -070026import java.util.List;
Saurav Das4f980082015-11-05 13:39:15 -080027import java.util.Map;
Saurav Das822c4e22015-10-23 10:51:11 -070028import java.util.Set;
29import java.util.concurrent.ConcurrentHashMap;
Saurav Das4ce45962015-11-24 23:21:05 -080030import java.util.concurrent.CopyOnWriteArrayList;
Saurav Das822c4e22015-10-23 10:51:11 -070031import java.util.concurrent.Executors;
32import java.util.concurrent.ScheduledExecutorService;
33import java.util.concurrent.TimeUnit;
Saurav Das4f980082015-11-05 13:39:15 -080034import java.util.concurrent.atomic.AtomicInteger;
Saurav Das822c4e22015-10-23 10:51:11 -070035import java.util.stream.Collectors;
36
37import org.onlab.osgi.ServiceDirectory;
Saurav Das822c4e22015-10-23 10:51:11 -070038import org.onlab.packet.Ethernet;
Saurav Das822c4e22015-10-23 10:51:11 -070039import org.onlab.packet.MacAddress;
40import org.onlab.packet.MplsLabel;
Saurav Das822c4e22015-10-23 10:51:11 -070041import org.onlab.packet.VlanId;
42import org.onlab.util.KryoNamespace;
43import org.onosproject.core.ApplicationId;
44import org.onosproject.core.CoreService;
45import org.onosproject.core.DefaultGroupId;
46import org.onosproject.net.DeviceId;
47import org.onosproject.net.Port;
48import org.onosproject.net.PortNumber;
49import org.onosproject.net.behaviour.NextGroup;
50import org.onosproject.net.behaviour.Pipeliner;
51import org.onosproject.net.behaviour.PipelinerContext;
52import org.onosproject.net.device.DeviceService;
53import org.onosproject.net.driver.AbstractHandlerBehaviour;
54import org.onosproject.net.flow.DefaultFlowRule;
55import org.onosproject.net.flow.DefaultTrafficSelector;
56import org.onosproject.net.flow.DefaultTrafficTreatment;
57import org.onosproject.net.flow.FlowRule;
58import org.onosproject.net.flow.FlowRuleOperations;
59import org.onosproject.net.flow.FlowRuleOperationsContext;
60import org.onosproject.net.flow.FlowRuleService;
61import org.onosproject.net.flow.TrafficSelector;
62import org.onosproject.net.flow.TrafficTreatment;
63import org.onosproject.net.flow.criteria.Criteria;
64import org.onosproject.net.flow.criteria.Criterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080065import org.onosproject.net.flow.criteria.Criterion.Type;
Saurav Das822c4e22015-10-23 10:51:11 -070066import org.onosproject.net.flow.criteria.EthCriterion;
67import org.onosproject.net.flow.criteria.EthTypeCriterion;
68import org.onosproject.net.flow.criteria.IPCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080069import org.onosproject.net.flow.criteria.MplsBosCriterion;
70import org.onosproject.net.flow.criteria.MplsCriterion;
Saurav Das822c4e22015-10-23 10:51:11 -070071import org.onosproject.net.flow.criteria.PortCriterion;
72import org.onosproject.net.flow.criteria.VlanIdCriterion;
73import org.onosproject.net.flow.instructions.Instruction;
74import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
75import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Saurav Das8a0732e2015-11-20 15:27:53 -080076import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
Saurav Das822c4e22015-10-23 10:51:11 -070077import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
Saurav Das8a0732e2015-11-20 15:27:53 -080078import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
Saurav Das822c4e22015-10-23 10:51:11 -070079import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
80import org.onosproject.net.flowobjective.FilteringObjective;
81import org.onosproject.net.flowobjective.FlowObjectiveStore;
82import org.onosproject.net.flowobjective.ForwardingObjective;
83import org.onosproject.net.flowobjective.NextObjective;
84import org.onosproject.net.flowobjective.Objective;
85import org.onosproject.net.flowobjective.ObjectiveError;
86import org.onosproject.net.group.DefaultGroupBucket;
87import org.onosproject.net.group.DefaultGroupDescription;
88import org.onosproject.net.group.DefaultGroupKey;
89import org.onosproject.net.group.Group;
90import org.onosproject.net.group.GroupBucket;
91import org.onosproject.net.group.GroupBuckets;
92import org.onosproject.net.group.GroupDescription;
93import org.onosproject.net.group.GroupEvent;
94import org.onosproject.net.group.GroupKey;
95import org.onosproject.net.group.GroupListener;
96import org.onosproject.net.group.GroupService;
Saurav Das822c4e22015-10-23 10:51:11 -070097import org.onosproject.store.serializers.KryoNamespaces;
98import org.slf4j.Logger;
99
100import com.google.common.cache.Cache;
101import com.google.common.cache.CacheBuilder;
102import com.google.common.cache.RemovalCause;
103import com.google.common.cache.RemovalNotification;
104
105/**
106 * Driver for Broadcom's OF-DPA v2.0 TTP.
107 *
108 */
109public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
110
111 protected static final int PORT_TABLE = 0;
112 protected static final int VLAN_TABLE = 10;
113 protected static final int TMAC_TABLE = 20;
114 protected static final int UNICAST_ROUTING_TABLE = 30;
115 protected static final int MULTICAST_ROUTING_TABLE = 40;
116 protected static final int MPLS_TABLE_0 = 23;
117 protected static final int MPLS_TABLE_1 = 24;
118 protected static final int BRIDGING_TABLE = 50;
119 protected static final int ACL_TABLE = 60;
120 protected static final int MAC_LEARNING_TABLE = 254;
121 protected static final long OFPP_MAX = 0xffffff00L;
122
123 private static final int HIGHEST_PRIORITY = 0xffff;
Saurav Das2857f382015-11-03 14:39:27 -0800124 protected static final int DEFAULT_PRIORITY = 0x8000;
Saurav Das822c4e22015-10-23 10:51:11 -0700125 protected static final int LOWEST_PRIORITY = 0x0;
126
127 /*
Saurav Das822c4e22015-10-23 10:51:11 -0700128 * OFDPA requires group-id's to have a certain form.
129 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
130 * L3 Unicast Groups have <4bits-2><28bits-index>
Saurav Das8a0732e2015-11-20 15:27:53 -0800131 * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
132 * L3 ECMP Groups have <4bits-7><28bits-index>
133 * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index>
134 * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
Saurav Das822c4e22015-10-23 10:51:11 -0700135 */
136 private static final int L2INTERFACEMASK = 0x0;
137 private static final int L3UNICASTMASK = 0x20000000;
Saurav Das8a0732e2015-11-20 15:27:53 -0800138 private static final int MPLSINTERFACEMASK = 0x90000000;
Saurav Das822c4e22015-10-23 10:51:11 -0700139 private static final int L3ECMPMASK = 0x70000000;
Saurav Das4f980082015-11-05 13:39:15 -0800140 private static final int L2FLOODMASK = 0x40000000;
Saurav Das8a0732e2015-11-20 15:27:53 -0800141 private static final int L3VPNMASK = 0x92000000;
Saurav Das822c4e22015-10-23 10:51:11 -0700142
Saurav Das822c4e22015-10-23 10:51:11 -0700143 private final Logger log = getLogger(getClass());
144 private ServiceDirectory serviceDirectory;
145 protected FlowRuleService flowRuleService;
146 private CoreService coreService;
Saurav Das8a0732e2015-11-20 15:27:53 -0800147 protected GroupService groupService;
148 protected FlowObjectiveStore flowObjectiveStore;
Saurav Das822c4e22015-10-23 10:51:11 -0700149 protected DeviceId deviceId;
150 protected ApplicationId driverId;
Saurav Das822c4e22015-10-23 10:51:11 -0700151 protected DeviceService deviceService;
Saurav Das8a0732e2015-11-20 15:27:53 -0800152 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das822c4e22015-10-23 10:51:11 -0700153 .register(KryoNamespaces.API)
154 .register(GroupKey.class)
155 .register(DefaultGroupKey.class)
Saurav Das8a0732e2015-11-20 15:27:53 -0800156 .register(OfdpaNextGroup.class)
Saurav Das822c4e22015-10-23 10:51:11 -0700157 .register(byte[].class)
Saurav Das8a0732e2015-11-20 15:27:53 -0800158 .register(ArrayDeque.class)
Saurav Das822c4e22015-10-23 10:51:11 -0700159 .build();
160
Saurav Das4ce45962015-11-24 23:21:05 -0800161 private Cache<GroupKey, List<OfdpaNextGroup>> pendingNextObjectives;
Saurav Das8a0732e2015-11-20 15:27:53 -0800162 private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
Saurav Das822c4e22015-10-23 10:51:11 -0700163
164 private ScheduledExecutorService groupChecker =
165 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner",
166 "ofdpa2-%d"));
167 private Set<IPCriterion> sentIpFilters = Collections.newSetFromMap(
168 new ConcurrentHashMap<IPCriterion, Boolean>());
169
Saurav Das4f980082015-11-05 13:39:15 -0800170 // local stores for port-vlan mapping
171 Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<PortNumber, VlanId>();
172 Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId,
173 Set<PortNumber>>();
174
Saurav Das423fe2b2015-12-04 10:52:59 -0800175 // local store for pending bucketAdds - by design there can only be one
176 // pending bucket for a group
177 ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>();
178
Saurav Das8a0732e2015-11-20 15:27:53 -0800179 // index number for group creation
180 AtomicInteger l3vpnindex = new AtomicInteger(0);
Saurav Das4f980082015-11-05 13:39:15 -0800181
182
Saurav Das822c4e22015-10-23 10:51:11 -0700183 @Override
184 public void init(DeviceId deviceId, PipelinerContext context) {
185 this.serviceDirectory = context.directory();
186 this.deviceId = deviceId;
187
188 pendingNextObjectives = CacheBuilder.newBuilder()
189 .expireAfterWrite(20, TimeUnit.SECONDS)
Saurav Das8a0732e2015-11-20 15:27:53 -0800190 .removalListener((
Saurav Das4ce45962015-11-24 23:21:05 -0800191 RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> {
Saurav Das8a0732e2015-11-20 15:27:53 -0800192 if (notification.getCause() == RemovalCause.EXPIRED) {
Saurav Das4ce45962015-11-24 23:21:05 -0800193 notification.getValue().forEach(ofdpaNextGrp ->
194 fail(ofdpaNextGrp.nextObj,
195 ObjectiveError.GROUPINSTALLATIONFAILED));
196
Saurav Das8a0732e2015-11-20 15:27:53 -0800197 }
Saurav Das822c4e22015-10-23 10:51:11 -0700198 }).build();
199
200 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
Saurav Das8a0732e2015-11-20 15:27:53 -0800201 pendingGroups = new ConcurrentHashMap<GroupKey, Set<GroupChainElem>>();
Saurav Das822c4e22015-10-23 10:51:11 -0700202
203 coreService = serviceDirectory.get(CoreService.class);
204 flowRuleService = serviceDirectory.get(FlowRuleService.class);
205 groupService = serviceDirectory.get(GroupService.class);
206 flowObjectiveStore = context.store();
Saurav Das822c4e22015-10-23 10:51:11 -0700207 deviceService = serviceDirectory.get(DeviceService.class);
Saurav Das822c4e22015-10-23 10:51:11 -0700208 groupService.addListener(new InnerGroupListener());
209
210 driverId = coreService.registerApplication(
211 "org.onosproject.driver.OFDPA2Pipeline");
212
213 // OF-DPA does not require initializing the pipeline as it puts default
214 // rules automatically in the hardware. However emulation of OFDPA in
215 // software switches does require table-miss-entries.
216 initializePipeline();
217
218 }
219
220 protected void initializePipeline() {
221
222 }
223
224 //////////////////////////////////////
225 // Flow Objectives
226 //////////////////////////////////////
227
228 @Override
229 public void filter(FilteringObjective filteringObjective) {
230 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
231 processFilter(filteringObjective,
232 filteringObjective.op() == Objective.Operation.ADD,
233 filteringObjective.appId());
234 } else {
235 // Note that packets that don't match the PERMIT filter are
236 // automatically denied. The DENY filter is used to deny packets
237 // that are otherwise permitted by the PERMIT filter.
238 // Use ACL table flow rules here for DENY filtering objectives
239 log.debug("filter objective other than PERMIT currently not supported");
240 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
241 }
242 }
243
244 @Override
245 public void forward(ForwardingObjective fwd) {
246 Collection<FlowRule> rules;
247 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
248
249 rules = processForward(fwd);
250 switch (fwd.op()) {
251 case ADD:
252 rules.stream()
253 .filter(rule -> rule != null)
254 .forEach(flowOpsBuilder::add);
255 break;
256 case REMOVE:
257 rules.stream()
258 .filter(rule -> rule != null)
259 .forEach(flowOpsBuilder::remove);
260 break;
261 default:
262 fail(fwd, ObjectiveError.UNKNOWN);
263 log.warn("Unknown forwarding type {}", fwd.op());
264 }
265
Saurav Das822c4e22015-10-23 10:51:11 -0700266 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
267 @Override
268 public void onSuccess(FlowRuleOperations ops) {
269 pass(fwd);
270 }
271
272 @Override
273 public void onError(FlowRuleOperations ops) {
274 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
275 }
276 }));
Saurav Das822c4e22015-10-23 10:51:11 -0700277 }
278
279 @Override
280 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800281 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
282 switch (nextObjective.op()) {
283 case ADD:
Saurav Das4f980082015-11-05 13:39:15 -0800284 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800285 log.warn("Cannot add next {} that already exists in device {}",
286 nextObjective.id(), deviceId);
287 return;
288 }
289 log.debug("Processing NextObjective id{} in dev{} - add group",
290 nextObjective.id(), deviceId);
291 addGroup(nextObjective);
292 break;
293 case ADD_TO_EXISTING:
294 if (nextGroup != null) {
295 log.debug("Processing NextObjective id{} in dev{} - add bucket",
296 nextObjective.id(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800297 addBucketToGroup(nextObjective, nextGroup);
Saurav Das4f980082015-11-05 13:39:15 -0800298 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800299 // it is possible that group-chain has not been fully created yet
Saurav Das423fe2b2015-12-04 10:52:59 -0800300 log.debug("Waiting to add bucket to group for next-id:{} in dev:{}",
301 nextObjective.id(), deviceId);
302 // by design only one pending bucket is allowed for the group
303 pendingBuckets.put(nextObjective.id(), nextObjective);
Saurav Das4f980082015-11-05 13:39:15 -0800304 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800305 break;
306 case REMOVE:
307 if (nextGroup == null) {
308 log.warn("Cannot remove next {} that does not exist in device {}",
309 nextObjective.id(), deviceId);
310 return;
311 }
312 log.debug("Processing NextObjective id{} in dev{} - remove group",
313 nextObjective.id(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800314 removeGroup(nextObjective, nextGroup);
Saurav Das8a0732e2015-11-20 15:27:53 -0800315 break;
316 case REMOVE_FROM_EXISTING:
317 if (nextGroup == null) {
318 log.warn("Cannot remove from next {} that does not exist in device {}",
319 nextObjective.id(), deviceId);
320 return;
321 }
322 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
323 nextObjective.id(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800324 removeBucketFromGroup(nextObjective, nextGroup);
Saurav Das8a0732e2015-11-20 15:27:53 -0800325 break;
326 default:
Saurav Das4f980082015-11-05 13:39:15 -0800327 log.warn("Unsupported operation {}", nextObjective.op());
Saurav Das822c4e22015-10-23 10:51:11 -0700328 }
329 }
330
331 //////////////////////////////////////
332 // Flow handling
333 //////////////////////////////////////
334
335 /**
336 * As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
337 * and IP addresses configured on switch ports happen in different tables.
338 * Note that IP filtering rules need to be added to the ACL table, as there
339 * is no mechanism to send to controller via IP table.
340 *
341 * @param filt the filtering objective
342 * @param install indicates whether to add or remove the objective
343 * @param applicationId the application that sent this objective
344 */
345 private void processFilter(FilteringObjective filt,
346 boolean install, ApplicationId applicationId) {
347 // This driver only processes filtering criteria defined with switch
348 // ports as the key
349 PortCriterion portCriterion = null;
350 EthCriterion ethCriterion = null;
351 VlanIdCriterion vidCriterion = null;
352 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
353 if (!filt.key().equals(Criteria.dummy()) &&
354 filt.key().type() == Criterion.Type.IN_PORT) {
355 portCriterion = (PortCriterion) filt.key();
356 } else {
357 log.warn("No key defined in filtering objective from app: {}. Not"
358 + "processing filtering objective", applicationId);
359 fail(filt, ObjectiveError.UNKNOWN);
360 return;
361 }
362 // convert filtering conditions for switch-intfs into flowrules
363 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
364 for (Criterion criterion : filt.conditions()) {
365 if (criterion.type() == Criterion.Type.ETH_DST) {
366 ethCriterion = (EthCriterion) criterion;
367 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
368 vidCriterion = (VlanIdCriterion) criterion;
369 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
370 ips.add((IPCriterion) criterion);
371 } else {
372 log.error("Unsupported filter {}", criterion);
373 fail(filt, ObjectiveError.UNSUPPORTED);
374 return;
375 }
376 }
377
Saurav Das0e99e2b2015-10-28 12:39:42 -0700378 VlanId assignedVlan = null;
379 if (vidCriterion != null && vidCriterion.vlanId() == VlanId.NONE) {
380 // untagged packets are assigned vlans in OF-DPA
381 if (filt.meta() == null) {
382 log.error("Missing metadata in filtering objective required "
383 + "for vlan assignment in dev {}", deviceId);
384 fail(filt, ObjectiveError.BADPARAMS);
385 return;
386 }
387 for (Instruction i : filt.meta().allInstructions()) {
388 if (i instanceof ModVlanIdInstruction) {
389 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
390 }
391 }
392 if (assignedVlan == null) {
393 log.error("Driver requires an assigned vlan-id to tag incoming "
394 + "untagged packets. Not processing vlan filters on "
395 + "device {}", deviceId);
396 fail(filt, ObjectiveError.BADPARAMS);
397 return;
398 }
399 }
400
Saurav Das822c4e22015-10-23 10:51:11 -0700401 if (ethCriterion == null) {
402 log.debug("filtering objective missing dstMac, cannot program TMAC table");
403 } else {
404 for (FlowRule tmacRule : processEthDstFilter(portCriterion, ethCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700405 vidCriterion, assignedVlan,
406 applicationId)) {
Saurav Das822c4e22015-10-23 10:51:11 -0700407 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
408 tmacRule, deviceId);
409 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
410 }
411 }
412
413 if (ethCriterion == null || vidCriterion == null) {
414 log.debug("filtering objective missing dstMac or vlan, cannot program"
415 + "Vlan Table");
416 } else {
417 for (FlowRule vlanRule : processVlanIdFilter(portCriterion, vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700418 assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700419 applicationId)) {
420 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
421 vlanRule, deviceId);
422 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
423 }
424 }
425
426 for (IPCriterion ipaddr : ips) {
427 // since we ignore port information for IP rules, and the same (gateway) IP
428 // can be configured on multiple ports, we make sure that we send
429 // only a single rule to the switch.
430 if (!sentIpFilters.contains(ipaddr)) {
431 sentIpFilters.add(ipaddr);
432 log.debug("adding IP filtering rules in ACL table {} for dev: {}",
433 ipaddr, deviceId);
434 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
435 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
436 selector.matchEthType(Ethernet.TYPE_IPV4);
437 selector.matchIPDst(ipaddr.ip());
438 treatment.setOutput(PortNumber.CONTROLLER);
439 FlowRule rule = DefaultFlowRule.builder()
440 .forDevice(deviceId)
441 .withSelector(selector.build())
442 .withTreatment(treatment.build())
443 .withPriority(HIGHEST_PRIORITY)
444 .fromApp(applicationId)
445 .makePermanent()
446 .forTable(ACL_TABLE).build();
447 ops = install ? ops.add(rule) : ops.remove(rule);
448 }
449 }
450
451 // apply filtering flow rules
452 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
453 @Override
454 public void onSuccess(FlowRuleOperations ops) {
455 log.info("Applied {} filtering rules in device {}",
456 ops.stages().get(0).size(), deviceId);
457 pass(filt);
458 }
459
460 @Override
461 public void onError(FlowRuleOperations ops) {
462 log.info("Failed to apply all filtering rules in dev {}", deviceId);
463 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
464 }
465 }));
466
467 }
468
469 /**
470 * Allows untagged packets into pipeline by assigning a vlan id.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700471 * Vlan assignment is done by the application.
Saurav Das822c4e22015-10-23 10:51:11 -0700472 * Allows tagged packets into pipeline as per configured port-vlan info.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700473 *
Saurav Das822c4e22015-10-23 10:51:11 -0700474 * @param portCriterion port on device for which this filter is programmed
475 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700476 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700477 * @param applicationId for application programming this filter
478 * @return list of FlowRule for port-vlan filters
479 */
480 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
481 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700482 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700483 ApplicationId applicationId) {
484 List<FlowRule> rules = new ArrayList<FlowRule>();
485 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
486 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
487 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800488 treatment.transition(TMAC_TABLE);
489
490 VlanId storeVlan = null;
Saurav Das822c4e22015-10-23 10:51:11 -0700491 if (vidCriterion.vlanId() == VlanId.NONE) {
492 // untagged packets are assigned vlans
Saurav Das0e99e2b2015-10-28 12:39:42 -0700493 treatment.pushVlan().setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800494 // XXX ofdpa will require an additional vlan match on the assigned vlan
495 // and it may not require the push. This is not in compliance with OF
496 // standard. Waiting on what the exact flows are going to look like.
Saurav Das4f980082015-11-05 13:39:15 -0800497 storeVlan = assignedVlan;
498 } else {
499 storeVlan = vidCriterion.vlanId();
Saurav Das822c4e22015-10-23 10:51:11 -0700500 }
Saurav Das822c4e22015-10-23 10:51:11 -0700501
502 // ofdpa cannot match on ALL portnumber, so we need to use separate
503 // rules for each port.
504 List<PortNumber> portnums = new ArrayList<PortNumber>();
505 if (portCriterion.port() == PortNumber.ALL) {
506 for (Port port : deviceService.getPorts(deviceId)) {
507 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
508 portnums.add(port.number());
509 }
510 }
511 } else {
512 portnums.add(portCriterion.port());
513 }
Saurav Das4f980082015-11-05 13:39:15 -0800514
Saurav Das822c4e22015-10-23 10:51:11 -0700515 for (PortNumber pnum : portnums) {
Saurav Das4f980082015-11-05 13:39:15 -0800516 // update storage
517 port2Vlan.put(pnum, storeVlan);
518 Set<PortNumber> vlanPorts = vlan2Port.get(storeVlan);
519 if (vlanPorts == null) {
520 vlanPorts = Collections.newSetFromMap(
521 new ConcurrentHashMap<PortNumber, Boolean>());
522 vlanPorts.add(pnum);
523 vlan2Port.put(storeVlan, vlanPorts);
524 } else {
525 vlanPorts.add(pnum);
526 }
527 // create rest of flowrule
Saurav Das822c4e22015-10-23 10:51:11 -0700528 selector.matchInPort(pnum);
529 FlowRule rule = DefaultFlowRule.builder()
530 .forDevice(deviceId)
531 .withSelector(selector.build())
532 .withTreatment(treatment.build())
533 .withPriority(DEFAULT_PRIORITY)
534 .fromApp(applicationId)
535 .makePermanent()
536 .forTable(VLAN_TABLE).build();
537 rules.add(rule);
538 }
539 return rules;
540 }
541
542 /**
543 * Allows routed packets with correct destination MAC to be directed
544 * to unicast-IP routing table or MPLS forwarding table.
Saurav Das822c4e22015-10-23 10:51:11 -0700545 *
546 * @param portCriterion port on device for which this filter is programmed
547 * @param ethCriterion dstMac of device for which is filter is programmed
548 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700549 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700550 * @param applicationId for application programming this filter
551 * @return list of FlowRule for port-vlan filters
552
553 */
554 protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
555 EthCriterion ethCriterion,
556 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700557 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700558 ApplicationId applicationId) {
559 //handling untagged packets via assigned VLAN
560 if (vidCriterion.vlanId() == VlanId.NONE) {
Saurav Das0e99e2b2015-10-28 12:39:42 -0700561 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
Saurav Das822c4e22015-10-23 10:51:11 -0700562 }
563 // ofdpa cannot match on ALL portnumber, so we need to use separate
564 // rules for each port.
565 List<PortNumber> portnums = new ArrayList<PortNumber>();
566 if (portCriterion.port() == PortNumber.ALL) {
567 for (Port port : deviceService.getPorts(deviceId)) {
568 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
569 portnums.add(port.number());
570 }
571 }
572 } else {
573 portnums.add(portCriterion.port());
574 }
575
576 List<FlowRule> rules = new ArrayList<FlowRule>();
577 for (PortNumber pnum : portnums) {
578 // for unicast IP packets
579 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
580 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
581 selector.matchInPort(pnum);
582 selector.matchVlanId(vidCriterion.vlanId());
583 selector.matchEthType(Ethernet.TYPE_IPV4);
584 selector.matchEthDst(ethCriterion.mac());
585 treatment.transition(UNICAST_ROUTING_TABLE);
586 FlowRule rule = DefaultFlowRule.builder()
587 .forDevice(deviceId)
588 .withSelector(selector.build())
589 .withTreatment(treatment.build())
590 .withPriority(DEFAULT_PRIORITY)
591 .fromApp(applicationId)
592 .makePermanent()
593 .forTable(TMAC_TABLE).build();
594 rules.add(rule);
595 //for MPLS packets
596 selector = DefaultTrafficSelector.builder();
597 treatment = DefaultTrafficTreatment.builder();
598 selector.matchInPort(pnum);
599 selector.matchVlanId(vidCriterion.vlanId());
600 selector.matchEthType(Ethernet.MPLS_UNICAST);
601 selector.matchEthDst(ethCriterion.mac());
602 treatment.transition(MPLS_TABLE_0);
603 rule = DefaultFlowRule.builder()
604 .forDevice(deviceId)
605 .withSelector(selector.build())
606 .withTreatment(treatment.build())
607 .withPriority(DEFAULT_PRIORITY)
608 .fromApp(applicationId)
609 .makePermanent()
610 .forTable(TMAC_TABLE).build();
611 rules.add(rule);
612 }
613 return rules;
614 }
615
616 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
617 switch (fwd.flag()) {
618 case SPECIFIC:
619 return processSpecific(fwd);
620 case VERSATILE:
621 return processVersatile(fwd);
622 default:
623 fail(fwd, ObjectiveError.UNKNOWN);
624 log.warn("Unknown forwarding flag {}", fwd.flag());
625 }
626 return Collections.emptySet();
627 }
628
629 /**
630 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
631 * ACL table.
632 * @param fwd the forwarding objective of type 'versatile'
633 * @return a collection of flow rules to be sent to the switch. An empty
634 * collection may be returned if there is a problem in processing
635 * the flow rule
636 */
637 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
638 log.info("Processing versatile forwarding objective");
639 TrafficSelector selector = fwd.selector();
640
641 EthTypeCriterion ethType =
642 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
643 if (ethType == null) {
644 log.error("Versatile forwarding objective must include ethType");
645 fail(fwd, ObjectiveError.BADPARAMS);
646 return Collections.emptySet();
647 }
648 if (fwd.nextId() == null && fwd.treatment() == null) {
649 log.error("Forwarding objective {} from {} must contain "
650 + "nextId or Treatment", fwd.selector(), fwd.appId());
651 return Collections.emptySet();
652 }
653 // XXX driver does not currently do type checking as per Tables 65-67 in
654 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
655 if (fwd.treatment() != null &&
656 fwd.treatment().allInstructions().size() == 1 &&
657 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
658 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
659 if (o.port() == PortNumber.CONTROLLER) {
660 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
661 .fromApp(fwd.appId())
662 .withPriority(fwd.priority())
663 .forDevice(deviceId)
664 .withSelector(fwd.selector())
665 .withTreatment(fwd.treatment())
666 .makePermanent()
667 .forTable(ACL_TABLE);
668 return Collections.singletonList(ruleBuilder.build());
669 } else {
670 log.warn("Only allowed treatments in versatile forwarding "
671 + "objectives are punts to the controller");
672 return Collections.emptySet();
673 }
674 }
675
676 if (fwd.nextId() != null) {
677 // XXX overide case
678 log.warn("versatile objective --> next Id not yet implemeted");
679 }
680 return Collections.emptySet();
681 }
682
683 /**
684 * In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table
Saurav Das8a0732e2015-11-20 15:27:53 -0800685 * (unicast or multicast) or the L2 table (mac + vlan) or the MPLS table.
Saurav Das822c4e22015-10-23 10:51:11 -0700686 *
687 * @param fwd the forwarding objective of type 'specific'
688 * @return a collection of flow rules. Typically there will be only one
689 * for this type of forwarding objective. An empty set may be
690 * returned if there is an issue in processing the objective.
691 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800692 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das4ce45962015-11-24 23:21:05 -0800693 log.trace("Processing specific fwd objective:{} in dev:{} with next:{}",
694 fwd.id(), deviceId, fwd.nextId());
695 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
696 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
697
698 if (isEthTypeObj) {
699 return processEthTypeSpecific(fwd);
700 } else if (isEthDstObj) {
701 return processEthDstSpecific(fwd);
702 } else {
703 log.warn("processSpecific: Unsupported forwarding objective "
704 + "criteria fwd:{} in dev:{}", fwd.nextId(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700705 fail(fwd, ObjectiveError.UNSUPPORTED);
706 return Collections.emptySet();
707 }
Saurav Das4ce45962015-11-24 23:21:05 -0800708 }
709
710 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
711 TrafficSelector selector = fwd.selector();
712 EthTypeCriterion ethType = (EthTypeCriterion) selector
713 .getCriterion(Criterion.Type.ETH_TYPE);
714 if ((ethType == null) ||
715 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
716 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
717 return false;
718 }
719 return true;
720 }
721
722 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
723 TrafficSelector selector = fwd.selector();
724 EthCriterion ethDst = (EthCriterion) selector
725 .getCriterion(Criterion.Type.ETH_DST);
726 VlanIdCriterion vlanId = (VlanIdCriterion) selector
727 .getCriterion(Criterion.Type.VLAN_VID);
728 if (ethDst == null && vlanId == null) {
729 return false;
730 }
731 return true;
732 }
733
734 /**
735 * Handles forwarding rules to the IP and MPLS tables.
736 *
737 * @param fwd the forwarding objective
738 * @return A collection of flow rules, or an empty set
739 */
740 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
741 TrafficSelector selector = fwd.selector();
742 EthTypeCriterion ethType =
743 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
Saurav Das822c4e22015-10-23 10:51:11 -0700744
Saurav Das8a0732e2015-11-20 15:27:53 -0800745 int forTableId = -1;
746 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
747 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
748 filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
749 .matchIPDst(((IPCriterion)
750 selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
751 forTableId = UNICAST_ROUTING_TABLE;
Saurav Das4ce45962015-11-24 23:21:05 -0800752 log.debug("processing IPv4 specific forwarding objective {} -> next:{}"
753 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800754 } else {
755 filteredSelector
756 .matchEthType(Ethernet.MPLS_UNICAST)
757 .matchMplsLabel(((MplsCriterion)
758 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
759 MplsBosCriterion bos = (MplsBosCriterion) selector
760 .getCriterion(Criterion.Type.MPLS_BOS);
761 if (bos != null) {
762 filteredSelector.matchMplsBos(bos.mplsBos());
763 }
764 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800765 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
766 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800767 }
Saurav Das822c4e22015-10-23 10:51:11 -0700768
769 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
Saurav Das8a0732e2015-11-20 15:27:53 -0800770 boolean popMpls = false;
771 if (fwd.treatment() != null) {
772 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chan7d10b162015-12-07 18:54:45 -0800773 /*
774 * NOTE: OF-DPA does not support immediate instruction in
775 * L3 unicast and MPLS table.
776 */
777 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -0800778 if (i instanceof L2ModificationInstruction &&
779 ((L2ModificationInstruction) i).subtype() == L2SubType.MPLS_POP) {
780 popMpls = true;
781 }
782 }
783 }
Saurav Das822c4e22015-10-23 10:51:11 -0700784
785 if (fwd.nextId() != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800786 if (forTableId == MPLS_TABLE_1 && !popMpls) {
787 log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
788 + "is not implemented in OF-DPA yet. Aborting this flow "
789 + "in this device {}", deviceId);
790 // XXX We could convert to forwarding to a single-port, via a
791 // MPLS interface, or a MPLS SWAP (with-same) but that would
792 // have to be handled in the next-objective. Also the pop-mpls
793 // logic used here won't work in non-BoS case.
Saurav Das4ce45962015-11-24 23:21:05 -0800794 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Saurav Das8a0732e2015-11-20 15:27:53 -0800795 return Collections.emptySet();
796 }
797
Saurav Das423fe2b2015-12-04 10:52:59 -0800798 NextGroup next = getGroupForNextObjective(fwd.nextId());
799 if (next != null) {
800 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
801 // we only need the top level group's key to point the flow to it
802 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
803 if (group == null) {
804 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
805 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
806 fail(fwd, ObjectiveError.GROUPMISSING);
807 return Collections.emptySet();
808 }
809 tb.deferred().group(group.id());
Saurav Das822c4e22015-10-23 10:51:11 -0700810 }
Saurav Das822c4e22015-10-23 10:51:11 -0700811 }
812 tb.transition(ACL_TABLE);
813 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
814 .fromApp(fwd.appId())
815 .withPriority(fwd.priority())
816 .forDevice(deviceId)
Saurav Das8a0732e2015-11-20 15:27:53 -0800817 .withSelector(filteredSelector.build())
818 .withTreatment(tb.build())
819 .forTable(forTableId);
Saurav Das822c4e22015-10-23 10:51:11 -0700820
821 if (fwd.permanent()) {
822 ruleBuilder.makePermanent();
823 } else {
824 ruleBuilder.makeTemporary(fwd.timeout());
825 }
826
Saurav Das822c4e22015-10-23 10:51:11 -0700827 return Collections.singletonList(ruleBuilder.build());
828 }
829
Saurav Das4ce45962015-11-24 23:21:05 -0800830 /**
831 * Handles forwarding rules to the L2 bridging table. Flow actions are not
832 * allowed in the bridging table - instead we use L2 Interface group or
833 * L2 flood group
834 *
835 * @param fwd the forwarding objective
836 * @return A collection of flow rules, or an empty set
837 */
838 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
839 List<FlowRule> rules = new ArrayList<>();
840
841 // Build filtered selector
842 TrafficSelector selector = fwd.selector();
843 EthCriterion ethCriterion = (EthCriterion) selector
844 .getCriterion(Criterion.Type.ETH_DST);
845 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
846 .getCriterion(Criterion.Type.VLAN_VID);
847
848 if (vlanIdCriterion == null) {
849 log.warn("Forwarding objective for bridging requires vlan. Not "
850 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
851 fail(fwd, ObjectiveError.BADPARAMS);
852 return Collections.emptySet();
853 }
854
855 TrafficSelector.Builder filteredSelectorBuilder =
856 DefaultTrafficSelector.builder();
857 // Do not match MacAddress for subnet broadcast entry
858 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
859 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
860 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
861 fwd.id(), fwd.nextId(), deviceId);
862 } else {
863 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
864 + "in dev:{} for vlan:{}",
865 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
866 }
867 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
868 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
869
870 if (fwd.treatment() != null) {
871 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
872 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
873 }
874
875 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
876 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800877 NextGroup next = getGroupForNextObjective(fwd.nextId());
Saurav Das4ce45962015-11-24 23:21:05 -0800878 if (next != null) {
879 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
880 // we only need the top level group's key to point the flow to it
881 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
882 if (group != null) {
883 treatmentBuilder.deferred().group(group.id());
884 } else {
885 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
886 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
887 fail(fwd, ObjectiveError.GROUPMISSING);
888 return Collections.emptySet();
889 }
890 }
891 }
892 treatmentBuilder.immediate().transition(ACL_TABLE);
893 TrafficTreatment filteredTreatment = treatmentBuilder.build();
894
895 // Build bridging table entries
896 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
897 flowRuleBuilder.fromApp(fwd.appId())
898 .withPriority(fwd.priority())
899 .forDevice(deviceId)
900 .withSelector(filteredSelector)
901 .withTreatment(filteredTreatment)
902 .forTable(BRIDGING_TABLE);
903 if (fwd.permanent()) {
904 flowRuleBuilder.makePermanent();
905 } else {
906 flowRuleBuilder.makeTemporary(fwd.timeout());
907 }
908 rules.add(flowRuleBuilder.build());
909 return rules;
910 }
911
Saurav Das423fe2b2015-12-04 10:52:59 -0800912 protected NextGroup getGroupForNextObjective(Integer nextId) {
913 NextGroup next = flowObjectiveStore.getNextGroup(nextId);
914 if (next != null) {
915 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
916 if (gkeys != null && !gkeys.isEmpty()) {
917 return next;
918 } else {
919 log.warn("Empty next group found in FlowObjective store for "
920 + "next-id:{} in dev:{}", nextId, deviceId);
921 }
922 } else {
923 log.warn("next-id {} not found in Flow objective store for dev:{}",
924 nextId, deviceId);
925 }
926 return null;
927 }
928
Saurav Das822c4e22015-10-23 10:51:11 -0700929 private void pass(Objective obj) {
930 if (obj.context().isPresent()) {
931 obj.context().get().onSuccess(obj);
932 }
933 }
934
Saurav Das8a0732e2015-11-20 15:27:53 -0800935 protected void fail(Objective obj, ObjectiveError error) {
Saurav Das822c4e22015-10-23 10:51:11 -0700936 if (obj.context().isPresent()) {
937 obj.context().get().onError(obj, error);
938 }
939 }
940
941 //////////////////////////////////////
942 // Group handling
943 //////////////////////////////////////
944
Saurav Das4f980082015-11-05 13:39:15 -0800945 private void addGroup(NextObjective nextObjective) {
946 switch (nextObjective.type()) {
947 case SIMPLE:
948 Collection<TrafficTreatment> treatments = nextObjective.next();
949 if (treatments.size() != 1) {
950 log.error("Next Objectives of type Simple should only have a "
951 + "single Traffic Treatment. Next Objective Id:{}",
952 nextObjective.id());
953 fail(nextObjective, ObjectiveError.BADPARAMS);
954 return;
955 }
956 processSimpleNextObjective(nextObjective);
957 break;
958 case BROADCAST:
959 processBroadcastNextObjective(nextObjective);
960 break;
961 case HASHED:
962 processHashedNextObjective(nextObjective);
963 break;
964 case FAILOVER:
965 fail(nextObjective, ObjectiveError.UNSUPPORTED);
966 log.warn("Unsupported next objective type {}", nextObjective.type());
967 break;
968 default:
969 fail(nextObjective, ObjectiveError.UNKNOWN);
970 log.warn("Unknown next objective type {}", nextObjective.type());
971 }
972 }
973
Saurav Das822c4e22015-10-23 10:51:11 -0700974 /**
975 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
Saurav Das8a0732e2015-11-20 15:27:53 -0800976 * a chain of groups. The simple Next Objective passed
Saurav Das822c4e22015-10-23 10:51:11 -0700977 * in by the application has to be broken up into a group chain
Saurav Das8a0732e2015-11-20 15:27:53 -0800978 * comprising of an L3 Unicast Group that points to an L2 Interface
979 * Group which in-turn points to an output port. In some cases, the simple
980 * next Objective can just be an L2 interface without the need for chaining.
Saurav Das822c4e22015-10-23 10:51:11 -0700981 *
982 * @param nextObj the nextObjective of type SIMPLE
983 */
984 private void processSimpleNextObjective(NextObjective nextObj) {
Saurav Das822c4e22015-10-23 10:51:11 -0700985 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Das4ce45962015-11-24 23:21:05 -0800986 // determine if plain L2 or L3->L2
987 boolean plainL2 = true;
988 for (Instruction ins : treatment.allInstructions()) {
989 if (ins.type() == Instruction.Type.L2MODIFICATION) {
990 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
991 if (l2ins.subtype() == L2SubType.ETH_DST ||
992 l2ins.subtype() == L2SubType.ETH_SRC) {
993 plainL2 = false;
994 break;
995 }
996 }
997 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800998
Saurav Das4ce45962015-11-24 23:21:05 -0800999 if (plainL2) {
1000 createL2InterfaceGroup(nextObj);
1001 return;
1002 }
1003
1004 // break up simple next objective to GroupChain objects
Saurav Das8a0732e2015-11-20 15:27:53 -08001005 GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
1006 nextObj.appId(), false,
1007 nextObj.meta());
1008 if (groupInfo == null) {
1009 log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
1010 return;
1011 }
1012 // create object for local and distributed storage
1013 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1014 gkeyChain.addFirst(groupInfo.innerGrpDesc.appCookie());
1015 gkeyChain.addFirst(groupInfo.outerGrpDesc.appCookie());
1016 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
1017 Collections.singletonList(gkeyChain),
1018 nextObj);
1019
Saurav Das4ce45962015-11-24 23:21:05 -08001020 // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
1021 updatePendingNextObjective(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp);
Saurav Das8a0732e2015-11-20 15:27:53 -08001022
1023 // now we are ready to send the l2 groupDescription (inner), as all the stores
1024 // that will get async replies have been updated. By waiting to update
1025 // the stores, we prevent nasty race conditions.
1026 groupService.addGroup(groupInfo.innerGrpDesc);
1027 }
1028
Saurav Das4ce45962015-11-24 23:21:05 -08001029 private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
1030 List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>();
1031 nextList.add(value);
1032 List<OfdpaNextGroup> ret = pendingNextObjectives.asMap()
1033 .putIfAbsent(key, nextList);
1034 if (ret != null) {
1035 ret.add(value);
1036 }
1037 }
1038
Saurav Das423fe2b2015-12-04 10:52:59 -08001039 private void updatePendingGroups(GroupKey gkey, GroupChainElem gce) {
1040 Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1041 new ConcurrentHashMap<GroupChainElem, Boolean>());
1042 gceSet.add(gce);
1043 Set<GroupChainElem> retval = pendingGroups.putIfAbsent(gkey, gceSet);
1044 if (retval != null) {
1045 retval.add(gce);
1046 }
1047 }
1048
Saurav Das4ce45962015-11-24 23:21:05 -08001049 /**
1050 * Creates a simple L2 Interface Group.
1051 *
1052 * @param nextObj the next Objective
1053 */
1054 private void createL2InterfaceGroup(NextObjective nextObj) {
1055 // only allowed actions are vlan pop and outport
1056 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1057 PortNumber portNum = null;
1058 for (Instruction ins : nextObj.next().iterator().next().allInstructions()) {
1059 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1060 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1061 switch (l2ins.subtype()) {
1062 case VLAN_POP:
1063 ttb.add(l2ins);
1064 break;
1065 default:
1066 break;
1067 }
1068 } else if (ins.type() == Instruction.Type.OUTPUT) {
1069 portNum = ((OutputInstruction) ins).port();
1070 ttb.add(ins);
1071 } else {
1072 log.warn("Driver does not handle this type of TrafficTreatment"
1073 + " instruction in simple nextObjectives: {}", ins.type());
1074 }
1075 }
1076 //use the vlanid associated with the port
1077 VlanId vlanid = port2Vlan.get(portNum);
1078
1079 if (vlanid == null && nextObj.meta() != null) {
1080 // use metadata vlan info if available
1081 Criterion vidCriterion = nextObj.meta().getCriterion(Type.VLAN_VID);
1082 if (vidCriterion != null) {
1083 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
1084 }
1085 }
1086
1087 if (vlanid == null) {
1088 log.error("Driver cannot process an L2/L3 group chain without "
1089 + "egress vlan information for dev: {} port:{}",
1090 deviceId, portNum);
1091 return;
1092 }
1093
1094 // assemble information for ofdpa l2interface group
1095 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum.toLong();
1096 // a globally unique groupkey that is different for ports in the same devices
1097 // but different for the same portnumber on different devices. Also different
1098 // for the various group-types created out of the same next objective.
1099 int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
1100 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
1101
1102 // create group description for the l2interfacegroup
1103 GroupBucket l2interfaceGroupBucket =
1104 DefaultGroupBucket.createIndirectGroupBucket(ttb.build());
1105 GroupDescription l2groupDescription =
1106 new DefaultGroupDescription(
1107 deviceId,
1108 GroupDescription.Type.INDIRECT,
1109 new GroupBuckets(Collections.singletonList(
1110 l2interfaceGroupBucket)),
1111 l2groupkey,
1112 l2groupId,
1113 nextObj.appId());
1114 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
1115 deviceId, Integer.toHexString(l2groupId),
1116 l2groupkey, nextObj.id());
1117
1118 // create object for local and distributed storage
1119 Deque<GroupKey> singleKey = new ArrayDeque<>();
1120 singleKey.addFirst(l2groupkey);
1121 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
1122 Collections.singletonList(singleKey),
1123 nextObj);
1124
1125 // store l2groupkey for the nextObjective that depends on it
1126 updatePendingNextObjective(l2groupkey, ofdpaGrp);
1127 // send the group description to the group service
1128 groupService.addGroup(l2groupDescription);
1129 }
1130
Saurav Das8a0732e2015-11-20 15:27:53 -08001131 /**
1132 * Creates one of two possible group-chains from the treatment
1133 * passed in. Depending on the MPLS boolean, this method either creates
1134 * an L3Unicast Group --> L2Interface Group, if mpls is false;
1135 * or MPLSInterface Group --> L2Interface Group, if mpls is true;
1136 * The returned 'inner' group description is always the L2 Interface group.
1137 *
1138 * @param treatment that needs to be broken up to create the group chain
1139 * @param nextId of the next objective that needs this group chain
1140 * @param appId of the application that sent this next objective
1141 * @param mpls determines if L3Unicast or MPLSInterface group is created
1142 * @param meta metadata passed in by the application as part of the nextObjective
1143 * @return GroupInfo containing the GroupDescription of the
1144 * L2Interface group(inner) and the GroupDescription of the (outer)
1145 * L3Unicast/MPLSInterface group. May return null if there is an
1146 * error in processing the chain
1147 */
1148 private GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
1149 ApplicationId appId, boolean mpls,
1150 TrafficSelector meta) {
Saurav Das822c4e22015-10-23 10:51:11 -07001151 // for the l2interface group, get vlan and port info
Saurav Das8a0732e2015-11-20 15:27:53 -08001152 // for the outer group, get the src/dst mac, and vlan info
1153 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
1154 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
Saurav Das822c4e22015-10-23 10:51:11 -07001155 VlanId vlanid = null;
1156 long portNum = 0;
Saurav Das4ce45962015-11-24 23:21:05 -08001157 boolean setVlan = false, popVlan = false;
Saurav Das822c4e22015-10-23 10:51:11 -07001158 for (Instruction ins : treatment.allInstructions()) {
1159 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1160 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1161 switch (l2ins.subtype()) {
1162 case ETH_DST:
Saurav Das8a0732e2015-11-20 15:27:53 -08001163 outerTtb.setEthDst(((ModEtherInstruction) l2ins).mac());
Saurav Das822c4e22015-10-23 10:51:11 -07001164 break;
1165 case ETH_SRC:
Saurav Das8a0732e2015-11-20 15:27:53 -08001166 outerTtb.setEthSrc(((ModEtherInstruction) l2ins).mac());
Saurav Das822c4e22015-10-23 10:51:11 -07001167 break;
1168 case VLAN_ID:
1169 vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
Saurav Das8a0732e2015-11-20 15:27:53 -08001170 outerTtb.setVlanId(vlanid);
Saurav Das4ce45962015-11-24 23:21:05 -08001171 setVlan = true;
Saurav Das8a0732e2015-11-20 15:27:53 -08001172 break;
1173 case VLAN_POP:
1174 innerTtb.popVlan();
Saurav Das4ce45962015-11-24 23:21:05 -08001175 popVlan = true;
Saurav Das822c4e22015-10-23 10:51:11 -07001176 break;
1177 case DEC_MPLS_TTL:
1178 case MPLS_LABEL:
1179 case MPLS_POP:
1180 case MPLS_PUSH:
1181 case VLAN_PCP:
Saurav Das822c4e22015-10-23 10:51:11 -07001182 case VLAN_PUSH:
1183 default:
1184 break;
1185 }
1186 } else if (ins.type() == Instruction.Type.OUTPUT) {
1187 portNum = ((OutputInstruction) ins).port().toLong();
Saurav Das8a0732e2015-11-20 15:27:53 -08001188 innerTtb.add(ins);
Saurav Das822c4e22015-10-23 10:51:11 -07001189 } else {
1190 log.warn("Driver does not handle this type of TrafficTreatment"
1191 + " instruction in nextObjectives: {}", ins.type());
1192 }
1193 }
1194
Saurav Das4ce45962015-11-24 23:21:05 -08001195 if (vlanid == null && meta != null) {
1196 // use metadata if available
1197 Criterion vidCriterion = meta.getCriterion(Type.VLAN_VID);
1198 if (vidCriterion != null) {
1199 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
Saurav Das8a0732e2015-11-20 15:27:53 -08001200 }
Charles Chan7d10b162015-12-07 18:54:45 -08001201 // if vlan is not set, use the vlan in metadata for outerTtb
1202 if (vlanid != null && !setVlan) {
1203 outerTtb.setVlanId(vlanid);
1204 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001205 }
1206
1207 if (vlanid == null) {
1208 log.error("Driver cannot process an L2/L3 group chain without "
1209 + "egress vlan information for dev: {} port:{}",
1210 deviceId, portNum);
1211 return null;
1212 }
1213
Saurav Das4ce45962015-11-24 23:21:05 -08001214 if (!setVlan && !popVlan) {
1215 // untagged outgoing port
1216 TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
1217 temp.popVlan();
1218 innerTtb.build().allInstructions().forEach(i -> temp.add(i));
1219 innerTtb = temp;
1220 }
1221
Saurav Das822c4e22015-10-23 10:51:11 -07001222 // assemble information for ofdpa l2interface group
Saurav Das822c4e22015-10-23 10:51:11 -07001223 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
Saurav Das8a0732e2015-11-20 15:27:53 -08001224 // a globally unique groupkey that is different for ports in the same devices
1225 // but different for the same portnumber on different devices. Also different
1226 // for the various group-types created out of the same next objective.
1227 int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum);
1228 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
Saurav Das822c4e22015-10-23 10:51:11 -07001229
Saurav Das8a0732e2015-11-20 15:27:53 -08001230 // assemble information for outer group
1231 GroupDescription outerGrpDesc = null;
1232 if (mpls) {
1233 // outer group is MPLSInteface
1234 Integer mplsgroupId = MPLSINTERFACEMASK | (int) portNum;
1235 // using mplsinterfacemask in groupkey to differentiate from l2interface
1236 int mplsgk = MPLSINTERFACEMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
1237 final GroupKey mplsgroupkey = new DefaultGroupKey(appKryo.serialize(mplsgk));
1238 outerTtb.group(new DefaultGroupId(l2groupId));
1239 // create the mpls-interface group description to wait for the
1240 // l2 interface group to be processed
1241 GroupBucket mplsinterfaceGroupBucket =
1242 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
1243 outerGrpDesc = new DefaultGroupDescription(
1244 deviceId,
1245 GroupDescription.Type.INDIRECT,
1246 new GroupBuckets(Collections.singletonList(
1247 mplsinterfaceGroupBucket)),
1248 mplsgroupkey,
1249 mplsgroupId,
1250 appId);
1251 log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
1252 deviceId, Integer.toHexString(mplsgroupId),
1253 mplsgroupkey, nextId);
1254 } else {
1255 // outer group is L3Unicast
1256 Integer l3groupId = L3UNICASTMASK | (int) portNum;
1257 int l3gk = L3UNICASTMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
1258 final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
1259 outerTtb.group(new DefaultGroupId(l2groupId));
1260 // create the l3unicast group description to wait for the
1261 // l2 interface group to be processed
1262 GroupBucket l3unicastGroupBucket =
1263 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
1264 outerGrpDesc = new DefaultGroupDescription(
1265 deviceId,
1266 GroupDescription.Type.INDIRECT,
1267 new GroupBuckets(Collections.singletonList(
1268 l3unicastGroupBucket)),
1269 l3groupkey,
1270 l3groupId,
1271 appId);
1272 log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
1273 deviceId, Integer.toHexString(l3groupId),
1274 l3groupkey, nextId);
1275 }
Saurav Das822c4e22015-10-23 10:51:11 -07001276
Saurav Das8a0732e2015-11-20 15:27:53 -08001277 // store l2groupkey with the groupChainElem for the outer-group that depends on it
Saurav Das423fe2b2015-12-04 10:52:59 -08001278 GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false);
1279 updatePendingGroups(l2groupkey, gce);
Saurav Das822c4e22015-10-23 10:51:11 -07001280
Saurav Das8a0732e2015-11-20 15:27:53 -08001281 // create group description for the inner l2interfacegroup
1282 GroupBucket l2interfaceGroupBucket =
1283 DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
1284 GroupDescription l2groupDescription =
1285 new DefaultGroupDescription(
1286 deviceId,
1287 GroupDescription.Type.INDIRECT,
1288 new GroupBuckets(Collections.singletonList(
1289 l2interfaceGroupBucket)),
1290 l2groupkey,
1291 l2groupId,
1292 appId);
1293 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
1294 deviceId, Integer.toHexString(l2groupId),
1295 l2groupkey, nextId);
1296 return new GroupInfo(l2groupDescription, outerGrpDesc);
Saurav Das822c4e22015-10-23 10:51:11 -07001297
Saurav Das822c4e22015-10-23 10:51:11 -07001298 }
1299
1300 /**
Saurav Das4f980082015-11-05 13:39:15 -08001301 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
Saurav Das8a0732e2015-11-20 15:27:53 -08001302 * a chain of groups. The broadcast Next Objective passed in by the application
Saurav Das4f980082015-11-05 13:39:15 -08001303 * has to be broken up into a group chain comprising of an
1304 * L2 Flood group whose buckets point to L2 Interface groups.
1305 *
1306 * @param nextObj the nextObjective of type BROADCAST
1307 */
1308 private void processBroadcastNextObjective(NextObjective nextObj) {
1309 // break up broadcast next objective to multiple groups
1310 Collection<TrafficTreatment> buckets = nextObj.next();
1311
1312 // each treatment is converted to an L2 interface group
Saurav Das4f980082015-11-05 13:39:15 -08001313 VlanId vlanid = null;
Saurav Das8a0732e2015-11-20 15:27:53 -08001314 List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>();
1315 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
Saurav Das4f980082015-11-05 13:39:15 -08001316 for (TrafficTreatment treatment : buckets) {
1317 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
1318 PortNumber portNum = null;
1319 // ensure that the only allowed treatments are pop-vlan and output
1320 for (Instruction ins : treatment.allInstructions()) {
1321 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1322 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1323 switch (l2ins.subtype()) {
1324 case VLAN_POP:
1325 newTreatment.add(l2ins);
1326 break;
1327 default:
1328 log.debug("action {} not permitted for broadcast nextObj",
1329 l2ins.subtype());
1330 break;
1331 }
1332 } else if (ins.type() == Instruction.Type.OUTPUT) {
1333 portNum = ((OutputInstruction) ins).port();
1334 newTreatment.add(ins);
1335 } else {
1336 log.debug("TrafficTreatment of type {} not permitted in "
1337 + " broadcast nextObjective", ins.type());
1338 }
1339 }
1340
1341 // also ensure that all ports are in the same vlan
Saurav Das4ce45962015-11-24 23:21:05 -08001342 // XXX maybe HA issue here?
Saurav Das4f980082015-11-05 13:39:15 -08001343 VlanId thisvlanid = port2Vlan.get(portNum);
1344 if (vlanid == null) {
1345 vlanid = thisvlanid;
1346 } else {
1347 if (!vlanid.equals(thisvlanid)) {
1348 log.error("Driver requires all ports in a broadcast nextObj "
1349 + "to be in the same vlan. Different vlans found "
1350 + "{} and {}. Aborting group creation", vlanid, thisvlanid);
1351 return;
1352 }
1353 }
1354
Saurav Das8a0732e2015-11-20 15:27:53 -08001355 // assemble info for l2 interface group
1356 int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
Saurav Das4f980082015-11-05 13:39:15 -08001357 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
1358 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) |
1359 (int) portNum.toLong();
Saurav Das8a0732e2015-11-20 15:27:53 -08001360 GroupBucket l2interfaceGroupBucket =
Saurav Das4f980082015-11-05 13:39:15 -08001361 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
Saurav Das8a0732e2015-11-20 15:27:53 -08001362 GroupDescription l2interfaceGroupDescription =
1363 new DefaultGroupDescription(
1364 deviceId,
1365 GroupDescription.Type.INDIRECT,
1366 new GroupBuckets(Collections.singletonList(
1367 l2interfaceGroupBucket)),
1368 l2groupkey,
1369 l2groupId,
1370 nextObj.appId());
1371 log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
1372 deviceId, Integer.toHexString(l2groupId),
1373 l2groupkey, nextObj.id());
1374
1375 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1376 gkeyChain.addFirst(l2groupkey);
Saurav Das4f980082015-11-05 13:39:15 -08001377
1378 // store the info needed to create this group
Saurav Das8a0732e2015-11-20 15:27:53 -08001379 l2interfaceGroupDescs.add(l2interfaceGroupDescription);
1380 allGroupKeys.add(gkeyChain);
Saurav Das4f980082015-11-05 13:39:15 -08001381 }
1382
1383 // assemble info for l2 flood group
Saurav Das4f980082015-11-05 13:39:15 -08001384 Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id();
Saurav Das8a0732e2015-11-20 15:27:53 -08001385 int l2floodgk = L2FLOODMASK | nextObj.id() << 12;
1386 final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk));
1387 // collection of group buckets pointing to all the l2 interface groups
1388 List<GroupBucket> l2floodBuckets = new ArrayList<>();
1389 for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
Saurav Das4f980082015-11-05 13:39:15 -08001390 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Saurav Das8a0732e2015-11-20 15:27:53 -08001391 ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
1392 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
1393 l2floodBuckets.add(abucket);
Saurav Das4f980082015-11-05 13:39:15 -08001394 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001395 // create the l2flood group-description to wait for all the
1396 // l2interface groups to be processed
1397 GroupDescription l2floodGroupDescription =
1398 new DefaultGroupDescription(
1399 deviceId,
1400 GroupDescription.Type.ALL,
1401 new GroupBuckets(l2floodBuckets),
1402 l2floodgroupkey,
1403 l2floodgroupId,
1404 nextObj.appId());
1405 GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
Saurav Das423fe2b2015-12-04 10:52:59 -08001406 l2interfaceGroupDescs.size(),
1407 false);
Saurav Das8a0732e2015-11-20 15:27:53 -08001408 log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
1409 deviceId, Integer.toHexString(l2floodgroupId),
1410 l2floodgroupkey, nextObj.id());
Saurav Das4f980082015-11-05 13:39:15 -08001411
1412 // create objects for local and distributed storage
Saurav Das8a0732e2015-11-20 15:27:53 -08001413 allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l2floodgroupkey));
1414 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
Saurav Das4f980082015-11-05 13:39:15 -08001415
1416 // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective
1417 // that depends on it
Saurav Das4ce45962015-11-24 23:21:05 -08001418 updatePendingNextObjective(l2floodgroupkey, ofdpaGrp);
Saurav Das4f980082015-11-05 13:39:15 -08001419
Saurav Das8a0732e2015-11-20 15:27:53 -08001420 for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
Saurav Das4f980082015-11-05 13:39:15 -08001421 // store all l2groupkeys with the groupChainElem for the l2floodgroup
1422 // that depends on it
Saurav Das423fe2b2015-12-04 10:52:59 -08001423 updatePendingGroups(l2intGrpDesc.appCookie(), gce);
1424 // send groups for all l2 interface groups
Saurav Das8a0732e2015-11-20 15:27:53 -08001425 groupService.addGroup(l2intGrpDesc);
Saurav Das4f980082015-11-05 13:39:15 -08001426 }
1427 }
1428
Saurav Das8a0732e2015-11-20 15:27:53 -08001429 /**
1430 * Utility class for moving group information around.
1431 *
1432 */
Saurav Das4f980082015-11-05 13:39:15 -08001433 private class GroupInfo {
Saurav Das8a0732e2015-11-20 15:27:53 -08001434 private GroupDescription innerGrpDesc;
1435 private GroupDescription outerGrpDesc;
Saurav Das4f980082015-11-05 13:39:15 -08001436
Saurav Das8a0732e2015-11-20 15:27:53 -08001437 GroupInfo(GroupDescription innerGrpDesc, GroupDescription outerGrpDesc) {
1438 this.innerGrpDesc = innerGrpDesc;
1439 this.outerGrpDesc = outerGrpDesc;
Saurav Das4f980082015-11-05 13:39:15 -08001440 }
1441 }
1442
Saurav Das8a0732e2015-11-20 15:27:53 -08001443 /**
1444 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
1445 * a chain of groups. The hashed Next Objective passed in by the application
1446 * has to be broken up into a group chain comprising of an
1447 * L3 ECMP group as the top level group. Buckets of this group can point
1448 * to a variety of groups in a group chain, depending on the whether
1449 * MPLS labels are being pushed or not.
1450 * <p>
1451 * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
1452 * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
Saurav Das423fe2b2015-12-04 10:52:59 -08001453 * check the nextObjective meta to see what is matching before being
1454 * sent to this nextObjective.
Saurav Das8a0732e2015-11-20 15:27:53 -08001455 *
1456 * @param nextObj the nextObjective of type HASHED
1457 */
Saurav Das4f980082015-11-05 13:39:15 -08001458 private void processHashedNextObjective(NextObjective nextObj) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001459 // storage for all group keys in the chain of groups created
1460 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1461 List<GroupInfo> unsentGroups = new ArrayList<>();
Saurav Das423fe2b2015-12-04 10:52:59 -08001462 createHashBucketChains(nextObj, allGroupKeys, unsentGroups);
1463
1464 // now we can create the outermost L3 ECMP group
1465 List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
1466 for (GroupInfo gi : unsentGroups) {
1467 // create ECMP bucket to point to the outer group
1468 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1469 ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
1470 GroupBucket sbucket = DefaultGroupBucket
1471 .createSelectGroupBucket(ttb.build());
1472 l3ecmpGroupBuckets.add(sbucket);
1473 }
1474 int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12;
1475 GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId));
1476 GroupDescription l3ecmpGroupDesc =
1477 new DefaultGroupDescription(
1478 deviceId,
1479 GroupDescription.Type.SELECT,
1480 new GroupBuckets(l3ecmpGroupBuckets),
1481 l3ecmpGroupKey,
1482 l3ecmpGroupId,
1483 nextObj.appId());
1484 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
1485 l3ecmpGroupBuckets.size(),
1486 false);
1487
1488 // create objects for local and distributed storage
1489 allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey));
1490 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
1491
1492 // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
1493 // that depends on it
1494 updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp);
1495
1496 log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
1497 deviceId, Integer.toHexString(l3ecmpGroupId),
1498 l3ecmpGroupKey, nextObj.id());
1499 // finally we are ready to send the innermost groups
1500 for (GroupInfo gi : unsentGroups) {
1501 log.debug("Sending innermost group {} in group chain on device {} ",
1502 Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
1503 updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce);
1504 groupService.addGroup(gi.innerGrpDesc);
1505 }
1506
1507 }
1508
1509 /**
1510 * Creates group chains for all buckets in a hashed group, and stores the
1511 * GroupInfos and GroupKeys for all the groups in the lists passed in, which
1512 * should be empty.
1513 * <p>
1514 * Does not create the top level ECMP group. Does not actually send the
1515 * groups to the groupService.
1516 *
1517 * @param nextObj the Next Objective with buckets that need to be converted
1518 * to group chains
1519 * @param allGroupKeys a list to store groupKey for each bucket-group-chain
1520 * @param unsentGroups a list to store GroupInfo for each bucket-group-chain
1521 */
1522 private void createHashBucketChains(NextObjective nextObj,
1523 List<Deque<GroupKey>> allGroupKeys,
1524 List<GroupInfo> unsentGroups) {
1525 // break up hashed next objective to multiple groups
1526 Collection<TrafficTreatment> buckets = nextObj.next();
1527
Saurav Das8a0732e2015-11-20 15:27:53 -08001528 for (TrafficTreatment bucket : buckets) {
1529 //figure out how many labels are pushed in each bucket
1530 int labelsPushed = 0;
1531 MplsLabel innermostLabel = null;
1532 for (Instruction ins : bucket.allInstructions()) {
1533 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1534 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1535 if (l2ins.subtype() == L2SubType.MPLS_PUSH) {
1536 labelsPushed++;
1537 }
1538 if (l2ins.subtype() == L2SubType.MPLS_LABEL) {
1539 if (innermostLabel == null) {
1540 innermostLabel = ((ModMplsLabelInstruction) l2ins).mplsLabel();
1541 }
1542 }
1543 }
1544 }
1545
1546 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1547 // XXX we only deal with 0 and 1 label push right now
1548 if (labelsPushed == 0) {
1549 GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1550 nextObj.appId(), false,
1551 nextObj.meta());
1552 if (nolabelGroupInfo == null) {
1553 log.error("Could not process nextObj={} in dev:{}",
1554 nextObj.id(), deviceId);
1555 return;
1556 }
1557 gkeyChain.addFirst(nolabelGroupInfo.innerGrpDesc.appCookie());
1558 gkeyChain.addFirst(nolabelGroupInfo.outerGrpDesc.appCookie());
1559
1560 // we can't send the inner group description yet, as we have to
1561 // create the dependent ECMP group first. So we store..
1562 unsentGroups.add(nolabelGroupInfo);
1563
1564 } else if (labelsPushed == 1) {
1565 GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1566 nextObj.appId(), true,
1567 nextObj.meta());
1568 if (onelabelGroupInfo == null) {
1569 log.error("Could not process nextObj={} in dev:{}",
1570 nextObj.id(), deviceId);
1571 return;
1572 }
1573 // we need to add another group to this chain - the L3VPN group
1574 TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder();
1575 l3vpnTtb.pushMpls()
1576 .setMpls(innermostLabel)
1577 .setMplsBos(true)
1578 .copyTtlOut()
1579 .group(new DefaultGroupId(
1580 onelabelGroupInfo.outerGrpDesc.givenGroupId()));
1581 GroupBucket l3vpnGrpBkt =
1582 DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
1583 int l3vpngroupId = L3VPNMASK | l3vpnindex.incrementAndGet();
1584 int l3vpngk = L3VPNMASK | nextObj.id() << 12 | l3vpnindex.get();
1585 GroupKey l3vpngroupkey = new DefaultGroupKey(appKryo.serialize(l3vpngk));
1586 GroupDescription l3vpnGroupDesc =
1587 new DefaultGroupDescription(
1588 deviceId,
1589 GroupDescription.Type.INDIRECT,
1590 new GroupBuckets(Collections.singletonList(
1591 l3vpnGrpBkt)),
1592 l3vpngroupkey,
1593 l3vpngroupId,
1594 nextObj.appId());
Saurav Das423fe2b2015-12-04 10:52:59 -08001595 GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1, false);
1596 updatePendingGroups(onelabelGroupInfo.outerGrpDesc.appCookie(), l3vpnGce);
Saurav Das8a0732e2015-11-20 15:27:53 -08001597
1598 gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie());
1599 gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie());
1600 gkeyChain.addFirst(l3vpngroupkey);
1601
1602 //now we can replace the outerGrpDesc with the one we just created
1603 onelabelGroupInfo.outerGrpDesc = l3vpnGroupDesc;
1604
1605 // we can't send the innermost group yet, as we have to create
1606 // the dependent ECMP group first. So we store ...
1607 unsentGroups.add(onelabelGroupInfo);
1608
1609 log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}",
1610 deviceId, Integer.toHexString(l3vpngroupId),
1611 l3vpngroupkey, nextObj.id());
1612
1613 } else {
1614 log.warn("Driver currently does not handle more than 1 MPLS "
Saurav Das423fe2b2015-12-04 10:52:59 -08001615 + "labels. Not processing nextObjective {}", nextObj.id());
Saurav Das8a0732e2015-11-20 15:27:53 -08001616 return;
1617 }
1618
1619 // all groups in this chain
1620 allGroupKeys.add(gkeyChain);
1621 }
Saurav Das423fe2b2015-12-04 10:52:59 -08001622 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001623
Saurav Das423fe2b2015-12-04 10:52:59 -08001624 /**
1625 * Adds a bucket to the top level group of a group-chain, and creates the chain.
1626 *
1627 * @param nextObjective the next group to add a bucket to
1628 * @param next the representation of the existing group-chain for this next objective
1629 */
1630 private void addBucketToGroup(NextObjective nextObjective, NextGroup next) {
1631 if (nextObjective.type() != NextObjective.Type.HASHED) {
1632 log.warn("AddBuckets not applied to nextType:{} in dev:{} for next:{}",
1633 nextObjective.type(), deviceId, nextObjective.id());
1634 return;
Saurav Das8a0732e2015-11-20 15:27:53 -08001635 }
Saurav Das423fe2b2015-12-04 10:52:59 -08001636 if (nextObjective.next().size() > 1) {
1637 log.warn("Only one bucket can be added at a time");
1638 return;
1639 }
1640 // storage for all group keys in the chain of groups created
1641 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1642 List<GroupInfo> unsentGroups = new ArrayList<>();
1643 createHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
1644
1645 // now we can create the outermost L3 ECMP group bucket to add
1646 GroupInfo gi = unsentGroups.get(0); // only one bucket, so only one group-chain
1647 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1648 ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
1649 GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build());
1650
1651 // recreate the original L3 ECMP group id and description
1652 int l3ecmpGroupId = L3ECMPMASK | nextObjective.id() << 12;
Saurav Das8a0732e2015-11-20 15:27:53 -08001653 GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId));
Saurav Das423fe2b2015-12-04 10:52:59 -08001654
1655 // Although GroupDescriptions are not necessary for adding buckets to
1656 // existing groups, we use one in the GroupChainElem. When the latter is
1657 // processed, the info will be extracted for the bucketAdd call to groupService
Saurav Das8a0732e2015-11-20 15:27:53 -08001658 GroupDescription l3ecmpGroupDesc =
1659 new DefaultGroupDescription(
1660 deviceId,
1661 GroupDescription.Type.SELECT,
Saurav Das423fe2b2015-12-04 10:52:59 -08001662 new GroupBuckets(Collections.singletonList(sbucket)),
Saurav Das8a0732e2015-11-20 15:27:53 -08001663 l3ecmpGroupKey,
1664 l3ecmpGroupId,
Saurav Das423fe2b2015-12-04 10:52:59 -08001665 nextObjective.appId());
1666 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, 1, true);
Saurav Das8a0732e2015-11-20 15:27:53 -08001667
Saurav Das423fe2b2015-12-04 10:52:59 -08001668 // update original NextGroup with new bucket-chain
1669 // don't need to update pendingNextObjectives -- group already exists
1670 Deque<GroupKey> newBucketChain = allGroupKeys.get(0);
1671 newBucketChain.addFirst(l3ecmpGroupKey);
1672 List<Deque<GroupKey>> allOriginalKeys = appKryo.deserialize(next.data());
1673 allOriginalKeys.add(newBucketChain);
1674 flowObjectiveStore.putNextGroup(nextObjective.id(),
1675 new OfdpaNextGroup(allOriginalKeys, nextObjective));
Saurav Das8a0732e2015-11-20 15:27:53 -08001676
Saurav Das423fe2b2015-12-04 10:52:59 -08001677 log.debug("Adding to L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
Saurav Das8a0732e2015-11-20 15:27:53 -08001678 deviceId, Integer.toHexString(l3ecmpGroupId),
Saurav Das423fe2b2015-12-04 10:52:59 -08001679 l3ecmpGroupKey, nextObjective.id());
1680 // send the innermost group
1681 log.debug("Sending innermost group {} in group chain on device {} ",
1682 Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
1683 updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce);
1684 groupService.addGroup(gi.innerGrpDesc);
Saurav Das8a0732e2015-11-20 15:27:53 -08001685
Saurav Das423fe2b2015-12-04 10:52:59 -08001686 }
1687
1688 /**
1689 * Removes the bucket in the top level group of a possible group-chain. Does
1690 * not remove the groups in a group-chain pointed to by this bucket, as they
1691 * may be in use (referenced by other groups) elsewhere.
1692 *
1693 * @param nextObjective the next group to remove a bucket from
1694 * @param next the representation of the existing group-chain for this next objective
1695 */
1696 private void removeBucketFromGroup(NextObjective nextObjective, NextGroup next) {
1697 if (nextObjective.type() != NextObjective.Type.HASHED) {
1698 log.warn("RemoveBuckets not applied to nextType:{} in dev:{} for next:{}",
1699 nextObjective.type(), deviceId, nextObjective.id());
1700 return;
1701 }
1702 Collection<TrafficTreatment> treatments = nextObjective.next();
1703 TrafficTreatment treatment = treatments.iterator().next();
1704 // find the bucket to remove by noting the outport, and figuring out the
1705 // top-level group in the group-chain that indirectly references the port
1706 PortNumber outport = null;
1707 for (Instruction ins : treatment.allInstructions()) {
1708 if (ins instanceof OutputInstruction) {
1709 outport = ((OutputInstruction) ins).port();
1710 break;
1711 }
1712 }
1713 if (outport == null) {
1714 log.error("next objective {} has no outport", nextObjective.id());
1715 return;
Saurav Das8a0732e2015-11-20 15:27:53 -08001716 }
1717
Saurav Das423fe2b2015-12-04 10:52:59 -08001718 List<Deque<GroupKey>> allgkeys = appKryo.deserialize(next.data());
1719 Deque<GroupKey> foundChain = null;
1720 int index = 0;
1721 for (Deque<GroupKey> gkeys : allgkeys) {
1722 GroupKey groupWithPort = gkeys.peekLast();
1723 Group group = groupService.getGroup(deviceId, groupWithPort);
1724 if (group == null) {
1725 log.warn("Inconsistent group chain");
1726 continue;
1727 }
1728 // last group in group chain should have a single bucket pointing to port
1729 List<Instruction> lastIns = group.buckets().buckets().iterator()
1730 .next().treatment().allInstructions();
1731 for (Instruction i : lastIns) {
1732 if (i instanceof OutputInstruction) {
1733 PortNumber lastport = ((OutputInstruction) i).port();
1734 if (lastport.equals(outport)) {
1735 foundChain = gkeys;
1736 break;
1737 }
1738 }
1739 }
1740 if (foundChain != null) {
1741 break;
1742 }
1743 index++;
1744 }
1745 if (foundChain != null) {
1746 //first groupkey is the one we want to modify
1747 GroupKey modGroupKey = foundChain.peekFirst();
1748 Group modGroup = groupService.getGroup(deviceId, modGroupKey);
1749 //second groupkey is the one we wish to remove the reference to
1750 GroupKey pointedGroupKey = null;
1751 int i = 0;
1752 for (GroupKey gk : foundChain) {
1753 if (i++ == 1) {
1754 pointedGroupKey = gk;
1755 break;
1756 }
1757 }
1758 Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey);
1759 GroupBucket bucket = DefaultGroupBucket.createSelectGroupBucket(
1760 DefaultTrafficTreatment.builder()
1761 .group(pointedGroup.id())
1762 .build());
1763 GroupBuckets removeBuckets = new GroupBuckets(Collections
1764 .singletonList(bucket));
1765 log.debug("Removing buckets from group id {} for next id {} in device {}",
1766 modGroup.id(), nextObjective.id(), deviceId);
1767 groupService.removeBucketsFromGroup(deviceId, modGroupKey,
1768 removeBuckets, modGroupKey,
1769 nextObjective.appId());
1770 //update store
1771 allgkeys.remove(index);
1772 flowObjectiveStore.putNextGroup(nextObjective.id(),
1773 new OfdpaNextGroup(allgkeys, nextObjective));
1774 } else {
1775 log.warn("Could not find appropriate group-chain for removing bucket"
1776 + " for next id {} in dev:{}", nextObjective.id(), deviceId);
1777 }
Saurav Das4f980082015-11-05 13:39:15 -08001778 }
1779
Saurav Das423fe2b2015-12-04 10:52:59 -08001780 /**
1781 * Removes all groups in multiple possible group-chains that represent the next
1782 * objective.
1783 *
1784 * @param nextObjective the next objective to remove
1785 * @param next the NextGroup that represents the existing group-chain for
1786 * this next objective
1787 */
1788 private void removeGroup(NextObjective nextObjective, NextGroup next) {
1789 List<Deque<GroupKey>> allgkeys = appKryo.deserialize(next.data());
1790 allgkeys.forEach(groupChain -> {
1791 groupChain.forEach(groupKey ->
1792 groupService.removeGroup(deviceId, groupKey, nextObjective.appId()));
1793 });
1794 flowObjectiveStore.removeNextGroup(nextObjective.id());
Saurav Das4f980082015-11-05 13:39:15 -08001795 }
1796
1797 /**
Saurav Das822c4e22015-10-23 10:51:11 -07001798 * Processes next element of a group chain. Assumption is that if this
1799 * group points to another group, the latter has already been created
1800 * and this driver has received notification for it. A second assumption is
1801 * that if there is another group waiting for this group then the appropriate
1802 * stores already have the information to act upon the notification for the
Saurav Das423fe2b2015-12-04 10:52:59 -08001803 * creation of this group.
Saurav Das4f980082015-11-05 13:39:15 -08001804 * <p>
1805 * The processing of the GroupChainElement depends on the number of groups
1806 * this element is waiting on. For all group types other than SIMPLE, a
1807 * GroupChainElement could be waiting on multiple groups.
Saurav Das822c4e22015-10-23 10:51:11 -07001808 *
1809 * @param gce the group chain element to be processed next
1810 */
1811 private void processGroupChain(GroupChainElem gce) {
Saurav Das4f980082015-11-05 13:39:15 -08001812 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
1813 if (waitOnGroups != 0) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001814 log.debug("GCE: {} not ready to be processed", gce);
Saurav Das4f980082015-11-05 13:39:15 -08001815 return;
1816 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001817 log.debug("GCE: {} ready to be processed", gce);
Saurav Das423fe2b2015-12-04 10:52:59 -08001818 if (gce.addBucketToGroup) {
1819 groupService.addBucketsToGroup(gce.groupDescription.deviceId(),
1820 gce.groupDescription.appCookie(),
1821 gce.groupDescription.buckets(),
1822 gce.groupDescription.appCookie(),
1823 gce.groupDescription.appId());
1824 } else {
1825 groupService.addGroup(gce.groupDescription);
1826 }
Saurav Das4f980082015-11-05 13:39:15 -08001827 }
Saurav Das822c4e22015-10-23 10:51:11 -07001828
1829 private class GroupChecker implements Runnable {
1830 @Override
1831 public void run() {
1832 Set<GroupKey> keys = pendingGroups.keySet().stream()
1833 .filter(key -> groupService.getGroup(deviceId, key) != null)
1834 .collect(Collectors.toSet());
1835 Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream()
1836 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
1837 .collect(Collectors.toSet());
1838 keys.addAll(otherkeys);
1839
Saurav Das423fe2b2015-12-04 10:52:59 -08001840 keys.stream().forEach(key ->
1841 processPendingGroupsOrNextObjectives(key, false));
1842 }
1843 }
1844
1845 private void processPendingGroupsOrNextObjectives(GroupKey key, boolean added) {
1846 //first check for group chain
1847 Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1848 if (gceSet != null) {
1849 for (GroupChainElem gce : gceSet) {
1850 log.info("Group service {} group key {} in device {}. "
1851 + "Processing next group in group chain with group id {}",
1852 (added) ? "ADDED" : "processed",
1853 key, deviceId,
1854 Integer.toHexString(gce.groupDescription.givenGroupId()));
1855 processGroupChain(gce);
1856 }
1857 } else {
1858 // otherwise chain complete - check for waiting nextObjectives
1859 List<OfdpaNextGroup> nextGrpList = pendingNextObjectives.getIfPresent(key);
1860 if (nextGrpList != null) {
1861 pendingNextObjectives.invalidate(key);
1862 nextGrpList.forEach(nextGrp -> {
1863 log.info("Group service {} group key {} in device:{}. "
1864 + "Done implementing next objective: {} <<-->> gid:{}",
1865 (added) ? "ADDED" : "processed",
1866 key, deviceId, nextGrp.nextObjective().id(),
1867 Integer.toHexString(groupService.getGroup(deviceId, key)
1868 .givenGroupId()));
1869 pass(nextGrp.nextObjective());
1870 flowObjectiveStore.putNextGroup(nextGrp.nextObjective().id(), nextGrp);
1871 // check if addBuckets waiting for this completion
1872 NextObjective pendBkt = pendingBuckets
1873 .remove(nextGrp.nextObjective().id());
1874 if (pendBkt != null) {
1875 addBucketToGroup(pendBkt, nextGrp);
Saurav Das8a0732e2015-11-20 15:27:53 -08001876 }
Saurav Das423fe2b2015-12-04 10:52:59 -08001877 });
1878 }
Saurav Das822c4e22015-10-23 10:51:11 -07001879 }
1880 }
1881
1882 private class InnerGroupListener implements GroupListener {
1883 @Override
1884 public void event(GroupEvent event) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001885 log.trace("received group event of type {}", event.type());
Saurav Das822c4e22015-10-23 10:51:11 -07001886 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
1887 GroupKey key = event.subject().appCookie();
Saurav Das423fe2b2015-12-04 10:52:59 -08001888 processPendingGroupsOrNextObjectives(key, true);
Saurav Das822c4e22015-10-23 10:51:11 -07001889 }
1890 }
1891 }
1892
1893 /**
Saurav Das8a0732e2015-11-20 15:27:53 -08001894 * Represents an entire group-chain that implements a Next-Objective from
1895 * the application. The objective is represented as a list of deques, where
Saurav Das423fe2b2015-12-04 10:52:59 -08001896 * each deque is a separate chain of groups.
Saurav Das8a0732e2015-11-20 15:27:53 -08001897 * <p>
1898 * For example, an ECMP group with 3 buckets, where each bucket points to
1899 * a group chain of L3 Unicast and L2 interface groups will look like this:
1900 * <ul>
1901 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1902 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1903 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1904 * </ul>
1905 * where the first element of each deque is the same, representing the
1906 * top level ECMP group, while every other element represents a unique groupKey.
1907 * <p>
1908 * Also includes information about the next objective that
1909 * resulted in this group-chain.
Saurav Das4f980082015-11-05 13:39:15 -08001910 *
Saurav Das822c4e22015-10-23 10:51:11 -07001911 */
Saurav Das8a0732e2015-11-20 15:27:53 -08001912 private class OfdpaNextGroup implements NextGroup {
Saurav Das822c4e22015-10-23 10:51:11 -07001913 private final NextObjective nextObj;
Saurav Das8a0732e2015-11-20 15:27:53 -08001914 private final List<Deque<GroupKey>> gkeys;
Saurav Das822c4e22015-10-23 10:51:11 -07001915
Saurav Das8a0732e2015-11-20 15:27:53 -08001916 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
Saurav Das822c4e22015-10-23 10:51:11 -07001917 this.gkeys = gkeys;
1918 this.nextObj = nextObj;
1919 }
1920
1921 @SuppressWarnings("unused")
Saurav Das8a0732e2015-11-20 15:27:53 -08001922 public List<Deque<GroupKey>> groupKey() {
Saurav Das822c4e22015-10-23 10:51:11 -07001923 return gkeys;
1924 }
1925
1926 public NextObjective nextObjective() {
1927 return nextObj;
1928 }
1929
1930 @Override
1931 public byte[] data() {
1932 return appKryo.serialize(gkeys);
1933 }
1934
1935 }
1936
1937 /**
1938 * Represents a group element that is part of a chain of groups.
1939 * Stores enough information to create a Group Description to add the group
1940 * to the switch by requesting the Group Service. Objects instantiating this
1941 * class are meant to be temporary and live as long as it is needed to wait for
1942 * preceding groups in the group chain to be created.
1943 */
1944 private class GroupChainElem {
Saurav Das8a0732e2015-11-20 15:27:53 -08001945 private GroupDescription groupDescription;
Saurav Das4f980082015-11-05 13:39:15 -08001946 private AtomicInteger waitOnGroups;
Saurav Das423fe2b2015-12-04 10:52:59 -08001947 private boolean addBucketToGroup;
Saurav Das822c4e22015-10-23 10:51:11 -07001948
Saurav Das423fe2b2015-12-04 10:52:59 -08001949 GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
1950 boolean addBucketToGroup) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001951 this.groupDescription = groupDescription;
Saurav Das4f980082015-11-05 13:39:15 -08001952 this.waitOnGroups = new AtomicInteger(waitOnGroups);
Saurav Das423fe2b2015-12-04 10:52:59 -08001953 this.addBucketToGroup = addBucketToGroup;
Saurav Das822c4e22015-10-23 10:51:11 -07001954 }
1955
Saurav Das4f980082015-11-05 13:39:15 -08001956 /**
1957 * This methods atomically decrements the counter for the number of
1958 * groups this GroupChainElement is waiting on, for notifications from
1959 * the Group Service. When this method returns a value of 0, this
1960 * GroupChainElement is ready to be processed.
1961 *
1962 * @return integer indication of the number of notifications being waited on
1963 */
1964 int decrementAndGetGroupsWaitedOn() {
1965 return waitOnGroups.decrementAndGet();
Saurav Das822c4e22015-10-23 10:51:11 -07001966 }
1967
Saurav Das4f980082015-11-05 13:39:15 -08001968 @Override
1969 public String toString() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001970 return (Integer.toHexString(groupDescription.givenGroupId()) +
1971 " groupKey: " + groupDescription.appCookie() +
1972 " waiting-on-groups: " + waitOnGroups.get() +
Saurav Das423fe2b2015-12-04 10:52:59 -08001973 " addBucketToGroup: " + addBucketToGroup +
Saurav Das8a0732e2015-11-20 15:27:53 -08001974 " device: " + deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -07001975 }
Saurav Das822c4e22015-10-23 10:51:11 -07001976 }
1977
1978}