blob: 5f84b43ac9826454c46fe47bc000cb3eefd11bea [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.net.packet.PacketService;
98import org.onosproject.store.serializers.KryoNamespaces;
99import org.slf4j.Logger;
100
101import com.google.common.cache.Cache;
102import com.google.common.cache.CacheBuilder;
103import com.google.common.cache.RemovalCause;
104import com.google.common.cache.RemovalNotification;
105
106/**
107 * Driver for Broadcom's OF-DPA v2.0 TTP.
108 *
109 */
110public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
111
112 protected static final int PORT_TABLE = 0;
113 protected static final int VLAN_TABLE = 10;
114 protected static final int TMAC_TABLE = 20;
115 protected static final int UNICAST_ROUTING_TABLE = 30;
116 protected static final int MULTICAST_ROUTING_TABLE = 40;
117 protected static final int MPLS_TABLE_0 = 23;
118 protected static final int MPLS_TABLE_1 = 24;
119 protected static final int BRIDGING_TABLE = 50;
120 protected static final int ACL_TABLE = 60;
121 protected static final int MAC_LEARNING_TABLE = 254;
122 protected static final long OFPP_MAX = 0xffffff00L;
123
124 private static final int HIGHEST_PRIORITY = 0xffff;
Saurav Das2857f382015-11-03 14:39:27 -0800125 protected static final int DEFAULT_PRIORITY = 0x8000;
Saurav Das822c4e22015-10-23 10:51:11 -0700126 protected static final int LOWEST_PRIORITY = 0x0;
127
128 /*
Saurav Das822c4e22015-10-23 10:51:11 -0700129 * OFDPA requires group-id's to have a certain form.
130 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
131 * L3 Unicast Groups have <4bits-2><28bits-index>
Saurav Das8a0732e2015-11-20 15:27:53 -0800132 * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
133 * L3 ECMP Groups have <4bits-7><28bits-index>
134 * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index>
135 * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
Saurav Das822c4e22015-10-23 10:51:11 -0700136 */
137 private static final int L2INTERFACEMASK = 0x0;
138 private static final int L3UNICASTMASK = 0x20000000;
Saurav Das8a0732e2015-11-20 15:27:53 -0800139 private static final int MPLSINTERFACEMASK = 0x90000000;
Saurav Das822c4e22015-10-23 10:51:11 -0700140 private static final int L3ECMPMASK = 0x70000000;
Saurav Das4f980082015-11-05 13:39:15 -0800141 private static final int L2FLOODMASK = 0x40000000;
Saurav Das8a0732e2015-11-20 15:27:53 -0800142 private static final int L3VPNMASK = 0x92000000;
Saurav Das822c4e22015-10-23 10:51:11 -0700143
Saurav Das822c4e22015-10-23 10:51:11 -0700144 private final Logger log = getLogger(getClass());
145 private ServiceDirectory serviceDirectory;
146 protected FlowRuleService flowRuleService;
147 private CoreService coreService;
Saurav Das8a0732e2015-11-20 15:27:53 -0800148 protected GroupService groupService;
149 protected FlowObjectiveStore flowObjectiveStore;
Saurav Das822c4e22015-10-23 10:51:11 -0700150 protected DeviceId deviceId;
151 protected ApplicationId driverId;
152 protected PacketService packetService;
153 protected DeviceService deviceService;
Saurav Das8a0732e2015-11-20 15:27:53 -0800154 protected KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das822c4e22015-10-23 10:51:11 -0700155 .register(KryoNamespaces.API)
156 .register(GroupKey.class)
157 .register(DefaultGroupKey.class)
Saurav Das8a0732e2015-11-20 15:27:53 -0800158 .register(OfdpaNextGroup.class)
Saurav Das822c4e22015-10-23 10:51:11 -0700159 .register(byte[].class)
Saurav Das8a0732e2015-11-20 15:27:53 -0800160 .register(ArrayDeque.class)
Saurav Das822c4e22015-10-23 10:51:11 -0700161 .build();
162
Saurav Das4ce45962015-11-24 23:21:05 -0800163 private Cache<GroupKey, List<OfdpaNextGroup>> pendingNextObjectives;
Saurav Das8a0732e2015-11-20 15:27:53 -0800164 private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
Saurav Das822c4e22015-10-23 10:51:11 -0700165
166 private ScheduledExecutorService groupChecker =
167 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner",
168 "ofdpa2-%d"));
169 private Set<IPCriterion> sentIpFilters = Collections.newSetFromMap(
170 new ConcurrentHashMap<IPCriterion, Boolean>());
171
Saurav Das4f980082015-11-05 13:39:15 -0800172 // local stores for port-vlan mapping
173 Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<PortNumber, VlanId>();
174 Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId,
175 Set<PortNumber>>();
176
Saurav Das8a0732e2015-11-20 15:27:53 -0800177 // index number for group creation
178 AtomicInteger l3vpnindex = new AtomicInteger(0);
Saurav Das4f980082015-11-05 13:39:15 -0800179
180
Saurav Das822c4e22015-10-23 10:51:11 -0700181 @Override
182 public void init(DeviceId deviceId, PipelinerContext context) {
183 this.serviceDirectory = context.directory();
184 this.deviceId = deviceId;
185
186 pendingNextObjectives = CacheBuilder.newBuilder()
187 .expireAfterWrite(20, TimeUnit.SECONDS)
Saurav Das8a0732e2015-11-20 15:27:53 -0800188 .removalListener((
Saurav Das4ce45962015-11-24 23:21:05 -0800189 RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> {
Saurav Das8a0732e2015-11-20 15:27:53 -0800190 if (notification.getCause() == RemovalCause.EXPIRED) {
Saurav Das4ce45962015-11-24 23:21:05 -0800191 notification.getValue().forEach(ofdpaNextGrp ->
192 fail(ofdpaNextGrp.nextObj,
193 ObjectiveError.GROUPINSTALLATIONFAILED));
194
Saurav Das8a0732e2015-11-20 15:27:53 -0800195 }
Saurav Das822c4e22015-10-23 10:51:11 -0700196 }).build();
197
198 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
Saurav Das8a0732e2015-11-20 15:27:53 -0800199 pendingGroups = new ConcurrentHashMap<GroupKey, Set<GroupChainElem>>();
Saurav Das822c4e22015-10-23 10:51:11 -0700200
201 coreService = serviceDirectory.get(CoreService.class);
202 flowRuleService = serviceDirectory.get(FlowRuleService.class);
203 groupService = serviceDirectory.get(GroupService.class);
204 flowObjectiveStore = context.store();
205 packetService = serviceDirectory.get(PacketService.class);
206 deviceService = serviceDirectory.get(DeviceService.class);
Saurav Das822c4e22015-10-23 10:51:11 -0700207 groupService.addListener(new InnerGroupListener());
208
209 driverId = coreService.registerApplication(
210 "org.onosproject.driver.OFDPA2Pipeline");
211
212 // OF-DPA does not require initializing the pipeline as it puts default
213 // rules automatically in the hardware. However emulation of OFDPA in
214 // software switches does require table-miss-entries.
215 initializePipeline();
216
217 }
218
219 protected void initializePipeline() {
220
221 }
222
223 //////////////////////////////////////
224 // Flow Objectives
225 //////////////////////////////////////
226
227 @Override
228 public void filter(FilteringObjective filteringObjective) {
229 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
230 processFilter(filteringObjective,
231 filteringObjective.op() == Objective.Operation.ADD,
232 filteringObjective.appId());
233 } else {
234 // Note that packets that don't match the PERMIT filter are
235 // automatically denied. The DENY filter is used to deny packets
236 // that are otherwise permitted by the PERMIT filter.
237 // Use ACL table flow rules here for DENY filtering objectives
238 log.debug("filter objective other than PERMIT currently not supported");
239 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
240 }
241 }
242
243 @Override
244 public void forward(ForwardingObjective fwd) {
245 Collection<FlowRule> rules;
246 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
247
248 rules = processForward(fwd);
249 switch (fwd.op()) {
250 case ADD:
251 rules.stream()
252 .filter(rule -> rule != null)
253 .forEach(flowOpsBuilder::add);
254 break;
255 case REMOVE:
256 rules.stream()
257 .filter(rule -> rule != null)
258 .forEach(flowOpsBuilder::remove);
259 break;
260 default:
261 fail(fwd, ObjectiveError.UNKNOWN);
262 log.warn("Unknown forwarding type {}", fwd.op());
263 }
264
Saurav Das822c4e22015-10-23 10:51:11 -0700265 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
266 @Override
267 public void onSuccess(FlowRuleOperations ops) {
268 pass(fwd);
269 }
270
271 @Override
272 public void onError(FlowRuleOperations ops) {
273 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
274 }
275 }));
Saurav Das822c4e22015-10-23 10:51:11 -0700276 }
277
278 @Override
279 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800280 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
281 switch (nextObjective.op()) {
282 case ADD:
Saurav Das4f980082015-11-05 13:39:15 -0800283 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800284 log.warn("Cannot add next {} that already exists in device {}",
285 nextObjective.id(), deviceId);
286 return;
287 }
288 log.debug("Processing NextObjective id{} in dev{} - add group",
289 nextObjective.id(), deviceId);
290 addGroup(nextObjective);
291 break;
292 case ADD_TO_EXISTING:
293 if (nextGroup != null) {
294 log.debug("Processing NextObjective id{} in dev{} - add bucket",
295 nextObjective.id(), deviceId);
Saurav Das4f980082015-11-05 13:39:15 -0800296 addBucketToGroup(nextObjective);
297 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800298 // it is possible that group-chain has not been fully created yet
299 waitToAddBucketToGroup(nextObjective);
Saurav Das4f980082015-11-05 13:39:15 -0800300 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800301 break;
302 case REMOVE:
303 if (nextGroup == null) {
304 log.warn("Cannot remove next {} that does not exist in device {}",
305 nextObjective.id(), deviceId);
306 return;
307 }
308 log.debug("Processing NextObjective id{} in dev{} - remove group",
309 nextObjective.id(), deviceId);
310 removeGroup(nextObjective);
311 break;
312 case REMOVE_FROM_EXISTING:
313 if (nextGroup == null) {
314 log.warn("Cannot remove from next {} that does not exist in device {}",
315 nextObjective.id(), deviceId);
316 return;
317 }
318 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
319 nextObjective.id(), deviceId);
320 removeBucketFromGroup(nextObjective);
321 break;
322 default:
Saurav Das4f980082015-11-05 13:39:15 -0800323 log.warn("Unsupported operation {}", nextObjective.op());
Saurav Das822c4e22015-10-23 10:51:11 -0700324 }
325 }
326
327 //////////////////////////////////////
328 // Flow handling
329 //////////////////////////////////////
330
331 /**
332 * As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
333 * and IP addresses configured on switch ports happen in different tables.
334 * Note that IP filtering rules need to be added to the ACL table, as there
335 * is no mechanism to send to controller via IP table.
336 *
337 * @param filt the filtering objective
338 * @param install indicates whether to add or remove the objective
339 * @param applicationId the application that sent this objective
340 */
341 private void processFilter(FilteringObjective filt,
342 boolean install, ApplicationId applicationId) {
343 // This driver only processes filtering criteria defined with switch
344 // ports as the key
345 PortCriterion portCriterion = null;
346 EthCriterion ethCriterion = null;
347 VlanIdCriterion vidCriterion = null;
348 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
349 if (!filt.key().equals(Criteria.dummy()) &&
350 filt.key().type() == Criterion.Type.IN_PORT) {
351 portCriterion = (PortCriterion) filt.key();
352 } else {
353 log.warn("No key defined in filtering objective from app: {}. Not"
354 + "processing filtering objective", applicationId);
355 fail(filt, ObjectiveError.UNKNOWN);
356 return;
357 }
358 // convert filtering conditions for switch-intfs into flowrules
359 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
360 for (Criterion criterion : filt.conditions()) {
361 if (criterion.type() == Criterion.Type.ETH_DST) {
362 ethCriterion = (EthCriterion) criterion;
363 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
364 vidCriterion = (VlanIdCriterion) criterion;
365 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
366 ips.add((IPCriterion) criterion);
367 } else {
368 log.error("Unsupported filter {}", criterion);
369 fail(filt, ObjectiveError.UNSUPPORTED);
370 return;
371 }
372 }
373
Saurav Das0e99e2b2015-10-28 12:39:42 -0700374 VlanId assignedVlan = null;
375 if (vidCriterion != null && vidCriterion.vlanId() == VlanId.NONE) {
376 // untagged packets are assigned vlans in OF-DPA
377 if (filt.meta() == null) {
378 log.error("Missing metadata in filtering objective required "
379 + "for vlan assignment in dev {}", deviceId);
380 fail(filt, ObjectiveError.BADPARAMS);
381 return;
382 }
383 for (Instruction i : filt.meta().allInstructions()) {
384 if (i instanceof ModVlanIdInstruction) {
385 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
386 }
387 }
388 if (assignedVlan == null) {
389 log.error("Driver requires an assigned vlan-id to tag incoming "
390 + "untagged packets. Not processing vlan filters on "
391 + "device {}", deviceId);
392 fail(filt, ObjectiveError.BADPARAMS);
393 return;
394 }
395 }
396
Saurav Das822c4e22015-10-23 10:51:11 -0700397 if (ethCriterion == null) {
398 log.debug("filtering objective missing dstMac, cannot program TMAC table");
399 } else {
400 for (FlowRule tmacRule : processEthDstFilter(portCriterion, ethCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700401 vidCriterion, assignedVlan,
402 applicationId)) {
Saurav Das822c4e22015-10-23 10:51:11 -0700403 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
404 tmacRule, deviceId);
405 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
406 }
407 }
408
409 if (ethCriterion == null || vidCriterion == null) {
410 log.debug("filtering objective missing dstMac or vlan, cannot program"
411 + "Vlan Table");
412 } else {
413 for (FlowRule vlanRule : processVlanIdFilter(portCriterion, vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700414 assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700415 applicationId)) {
416 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
417 vlanRule, deviceId);
418 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
419 }
420 }
421
422 for (IPCriterion ipaddr : ips) {
423 // since we ignore port information for IP rules, and the same (gateway) IP
424 // can be configured on multiple ports, we make sure that we send
425 // only a single rule to the switch.
426 if (!sentIpFilters.contains(ipaddr)) {
427 sentIpFilters.add(ipaddr);
428 log.debug("adding IP filtering rules in ACL table {} for dev: {}",
429 ipaddr, deviceId);
430 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
431 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
432 selector.matchEthType(Ethernet.TYPE_IPV4);
433 selector.matchIPDst(ipaddr.ip());
434 treatment.setOutput(PortNumber.CONTROLLER);
435 FlowRule rule = DefaultFlowRule.builder()
436 .forDevice(deviceId)
437 .withSelector(selector.build())
438 .withTreatment(treatment.build())
439 .withPriority(HIGHEST_PRIORITY)
440 .fromApp(applicationId)
441 .makePermanent()
442 .forTable(ACL_TABLE).build();
443 ops = install ? ops.add(rule) : ops.remove(rule);
444 }
445 }
446
447 // apply filtering flow rules
448 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
449 @Override
450 public void onSuccess(FlowRuleOperations ops) {
451 log.info("Applied {} filtering rules in device {}",
452 ops.stages().get(0).size(), deviceId);
453 pass(filt);
454 }
455
456 @Override
457 public void onError(FlowRuleOperations ops) {
458 log.info("Failed to apply all filtering rules in dev {}", deviceId);
459 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
460 }
461 }));
462
463 }
464
465 /**
466 * Allows untagged packets into pipeline by assigning a vlan id.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700467 * Vlan assignment is done by the application.
Saurav Das822c4e22015-10-23 10:51:11 -0700468 * Allows tagged packets into pipeline as per configured port-vlan info.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700469 *
Saurav Das822c4e22015-10-23 10:51:11 -0700470 * @param portCriterion port on device for which this filter is programmed
471 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700472 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700473 * @param applicationId for application programming this filter
474 * @return list of FlowRule for port-vlan filters
475 */
476 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
477 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700478 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700479 ApplicationId applicationId) {
480 List<FlowRule> rules = new ArrayList<FlowRule>();
481 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
482 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
483 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800484 treatment.transition(TMAC_TABLE);
485
486 VlanId storeVlan = null;
Saurav Das822c4e22015-10-23 10:51:11 -0700487 if (vidCriterion.vlanId() == VlanId.NONE) {
488 // untagged packets are assigned vlans
Saurav Das0e99e2b2015-10-28 12:39:42 -0700489 treatment.pushVlan().setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800490 // XXX ofdpa will require an additional vlan match on the assigned vlan
491 // and it may not require the push. This is not in compliance with OF
492 // standard. Waiting on what the exact flows are going to look like.
Saurav Das4f980082015-11-05 13:39:15 -0800493 storeVlan = assignedVlan;
494 } else {
495 storeVlan = vidCriterion.vlanId();
Saurav Das822c4e22015-10-23 10:51:11 -0700496 }
Saurav Das822c4e22015-10-23 10:51:11 -0700497
498 // ofdpa cannot match on ALL portnumber, so we need to use separate
499 // rules for each port.
500 List<PortNumber> portnums = new ArrayList<PortNumber>();
501 if (portCriterion.port() == PortNumber.ALL) {
502 for (Port port : deviceService.getPorts(deviceId)) {
503 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
504 portnums.add(port.number());
505 }
506 }
507 } else {
508 portnums.add(portCriterion.port());
509 }
Saurav Das4f980082015-11-05 13:39:15 -0800510
Saurav Das822c4e22015-10-23 10:51:11 -0700511 for (PortNumber pnum : portnums) {
Saurav Das4f980082015-11-05 13:39:15 -0800512 // update storage
513 port2Vlan.put(pnum, storeVlan);
514 Set<PortNumber> vlanPorts = vlan2Port.get(storeVlan);
515 if (vlanPorts == null) {
516 vlanPorts = Collections.newSetFromMap(
517 new ConcurrentHashMap<PortNumber, Boolean>());
518 vlanPorts.add(pnum);
519 vlan2Port.put(storeVlan, vlanPorts);
520 } else {
521 vlanPorts.add(pnum);
522 }
523 // create rest of flowrule
Saurav Das822c4e22015-10-23 10:51:11 -0700524 selector.matchInPort(pnum);
525 FlowRule rule = DefaultFlowRule.builder()
526 .forDevice(deviceId)
527 .withSelector(selector.build())
528 .withTreatment(treatment.build())
529 .withPriority(DEFAULT_PRIORITY)
530 .fromApp(applicationId)
531 .makePermanent()
532 .forTable(VLAN_TABLE).build();
533 rules.add(rule);
534 }
535 return rules;
536 }
537
538 /**
539 * Allows routed packets with correct destination MAC to be directed
540 * to unicast-IP routing table or MPLS forwarding table.
Saurav Das822c4e22015-10-23 10:51:11 -0700541 *
542 * @param portCriterion port on device for which this filter is programmed
543 * @param ethCriterion dstMac of device for which is filter is programmed
544 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700545 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700546 * @param applicationId for application programming this filter
547 * @return list of FlowRule for port-vlan filters
548
549 */
550 protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
551 EthCriterion ethCriterion,
552 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700553 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700554 ApplicationId applicationId) {
555 //handling untagged packets via assigned VLAN
556 if (vidCriterion.vlanId() == VlanId.NONE) {
Saurav Das0e99e2b2015-10-28 12:39:42 -0700557 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
Saurav Das822c4e22015-10-23 10:51:11 -0700558 }
559 // ofdpa cannot match on ALL portnumber, so we need to use separate
560 // rules for each port.
561 List<PortNumber> portnums = new ArrayList<PortNumber>();
562 if (portCriterion.port() == PortNumber.ALL) {
563 for (Port port : deviceService.getPorts(deviceId)) {
564 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
565 portnums.add(port.number());
566 }
567 }
568 } else {
569 portnums.add(portCriterion.port());
570 }
571
572 List<FlowRule> rules = new ArrayList<FlowRule>();
573 for (PortNumber pnum : portnums) {
574 // for unicast IP packets
575 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
576 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
577 selector.matchInPort(pnum);
578 selector.matchVlanId(vidCriterion.vlanId());
579 selector.matchEthType(Ethernet.TYPE_IPV4);
580 selector.matchEthDst(ethCriterion.mac());
581 treatment.transition(UNICAST_ROUTING_TABLE);
582 FlowRule rule = DefaultFlowRule.builder()
583 .forDevice(deviceId)
584 .withSelector(selector.build())
585 .withTreatment(treatment.build())
586 .withPriority(DEFAULT_PRIORITY)
587 .fromApp(applicationId)
588 .makePermanent()
589 .forTable(TMAC_TABLE).build();
590 rules.add(rule);
591 //for MPLS packets
592 selector = DefaultTrafficSelector.builder();
593 treatment = DefaultTrafficTreatment.builder();
594 selector.matchInPort(pnum);
595 selector.matchVlanId(vidCriterion.vlanId());
596 selector.matchEthType(Ethernet.MPLS_UNICAST);
597 selector.matchEthDst(ethCriterion.mac());
598 treatment.transition(MPLS_TABLE_0);
599 rule = DefaultFlowRule.builder()
600 .forDevice(deviceId)
601 .withSelector(selector.build())
602 .withTreatment(treatment.build())
603 .withPriority(DEFAULT_PRIORITY)
604 .fromApp(applicationId)
605 .makePermanent()
606 .forTable(TMAC_TABLE).build();
607 rules.add(rule);
608 }
609 return rules;
610 }
611
612 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
613 switch (fwd.flag()) {
614 case SPECIFIC:
615 return processSpecific(fwd);
616 case VERSATILE:
617 return processVersatile(fwd);
618 default:
619 fail(fwd, ObjectiveError.UNKNOWN);
620 log.warn("Unknown forwarding flag {}", fwd.flag());
621 }
622 return Collections.emptySet();
623 }
624
625 /**
626 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
627 * ACL table.
628 * @param fwd the forwarding objective of type 'versatile'
629 * @return a collection of flow rules to be sent to the switch. An empty
630 * collection may be returned if there is a problem in processing
631 * the flow rule
632 */
633 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
634 log.info("Processing versatile forwarding objective");
635 TrafficSelector selector = fwd.selector();
636
637 EthTypeCriterion ethType =
638 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
639 if (ethType == null) {
640 log.error("Versatile forwarding objective must include ethType");
641 fail(fwd, ObjectiveError.BADPARAMS);
642 return Collections.emptySet();
643 }
644 if (fwd.nextId() == null && fwd.treatment() == null) {
645 log.error("Forwarding objective {} from {} must contain "
646 + "nextId or Treatment", fwd.selector(), fwd.appId());
647 return Collections.emptySet();
648 }
649 // XXX driver does not currently do type checking as per Tables 65-67 in
650 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
651 if (fwd.treatment() != null &&
652 fwd.treatment().allInstructions().size() == 1 &&
653 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
654 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
655 if (o.port() == PortNumber.CONTROLLER) {
656 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
657 .fromApp(fwd.appId())
658 .withPriority(fwd.priority())
659 .forDevice(deviceId)
660 .withSelector(fwd.selector())
661 .withTreatment(fwd.treatment())
662 .makePermanent()
663 .forTable(ACL_TABLE);
664 return Collections.singletonList(ruleBuilder.build());
665 } else {
666 log.warn("Only allowed treatments in versatile forwarding "
667 + "objectives are punts to the controller");
668 return Collections.emptySet();
669 }
670 }
671
672 if (fwd.nextId() != null) {
673 // XXX overide case
674 log.warn("versatile objective --> next Id not yet implemeted");
675 }
676 return Collections.emptySet();
677 }
678
679 /**
680 * In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table
Saurav Das8a0732e2015-11-20 15:27:53 -0800681 * (unicast or multicast) or the L2 table (mac + vlan) or the MPLS table.
Saurav Das822c4e22015-10-23 10:51:11 -0700682 *
683 * @param fwd the forwarding objective of type 'specific'
684 * @return a collection of flow rules. Typically there will be only one
685 * for this type of forwarding objective. An empty set may be
686 * returned if there is an issue in processing the objective.
687 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800688 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das4ce45962015-11-24 23:21:05 -0800689 log.trace("Processing specific fwd objective:{} in dev:{} with next:{}",
690 fwd.id(), deviceId, fwd.nextId());
691 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
692 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
693
694 if (isEthTypeObj) {
695 return processEthTypeSpecific(fwd);
696 } else if (isEthDstObj) {
697 return processEthDstSpecific(fwd);
698 } else {
699 log.warn("processSpecific: Unsupported forwarding objective "
700 + "criteria fwd:{} in dev:{}", fwd.nextId(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700701 fail(fwd, ObjectiveError.UNSUPPORTED);
702 return Collections.emptySet();
703 }
Saurav Das4ce45962015-11-24 23:21:05 -0800704 }
705
706 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
707 TrafficSelector selector = fwd.selector();
708 EthTypeCriterion ethType = (EthTypeCriterion) selector
709 .getCriterion(Criterion.Type.ETH_TYPE);
710 if ((ethType == null) ||
711 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
712 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
713 return false;
714 }
715 return true;
716 }
717
718 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
719 TrafficSelector selector = fwd.selector();
720 EthCriterion ethDst = (EthCriterion) selector
721 .getCriterion(Criterion.Type.ETH_DST);
722 VlanIdCriterion vlanId = (VlanIdCriterion) selector
723 .getCriterion(Criterion.Type.VLAN_VID);
724 if (ethDst == null && vlanId == null) {
725 return false;
726 }
727 return true;
728 }
729
730 /**
731 * Handles forwarding rules to the IP and MPLS tables.
732 *
733 * @param fwd the forwarding objective
734 * @return A collection of flow rules, or an empty set
735 */
736 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
737 TrafficSelector selector = fwd.selector();
738 EthTypeCriterion ethType =
739 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
Saurav Das822c4e22015-10-23 10:51:11 -0700740
Saurav Das8a0732e2015-11-20 15:27:53 -0800741 int forTableId = -1;
742 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
743 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
744 filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
745 .matchIPDst(((IPCriterion)
746 selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
747 forTableId = UNICAST_ROUTING_TABLE;
Saurav Das4ce45962015-11-24 23:21:05 -0800748 log.debug("processing IPv4 specific forwarding objective {} -> next:{}"
749 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800750 } else {
751 filteredSelector
752 .matchEthType(Ethernet.MPLS_UNICAST)
753 .matchMplsLabel(((MplsCriterion)
754 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
755 MplsBosCriterion bos = (MplsBosCriterion) selector
756 .getCriterion(Criterion.Type.MPLS_BOS);
757 if (bos != null) {
758 filteredSelector.matchMplsBos(bos.mplsBos());
759 }
760 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800761 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
762 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800763 }
Saurav Das822c4e22015-10-23 10:51:11 -0700764
765 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
Saurav Das8a0732e2015-11-20 15:27:53 -0800766 boolean popMpls = false;
767 if (fwd.treatment() != null) {
768 for (Instruction i : fwd.treatment().allInstructions()) {
769 tb.add(i);
770 if (i instanceof L2ModificationInstruction &&
771 ((L2ModificationInstruction) i).subtype() == L2SubType.MPLS_POP) {
772 popMpls = true;
773 }
774 }
775 }
Saurav Das822c4e22015-10-23 10:51:11 -0700776
777 if (fwd.nextId() != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800778 if (forTableId == MPLS_TABLE_1 && !popMpls) {
779 log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
780 + "is not implemented in OF-DPA yet. Aborting this flow "
781 + "in this device {}", deviceId);
782 // XXX We could convert to forwarding to a single-port, via a
783 // MPLS interface, or a MPLS SWAP (with-same) but that would
784 // have to be handled in the next-objective. Also the pop-mpls
785 // logic used here won't work in non-BoS case.
Saurav Das4ce45962015-11-24 23:21:05 -0800786 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Saurav Das8a0732e2015-11-20 15:27:53 -0800787 return Collections.emptySet();
788 }
789
Saurav Das822c4e22015-10-23 10:51:11 -0700790 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
Saurav Das8a0732e2015-11-20 15:27:53 -0800791 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
792 // we only need the top level group's key to point the flow to it
793 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
Saurav Das822c4e22015-10-23 10:51:11 -0700794 if (group == null) {
Saurav Das4ce45962015-11-24 23:21:05 -0800795 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
796 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700797 fail(fwd, ObjectiveError.GROUPMISSING);
798 return Collections.emptySet();
799 }
800 tb.deferred().group(group.id());
801 }
802 tb.transition(ACL_TABLE);
803 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
804 .fromApp(fwd.appId())
805 .withPriority(fwd.priority())
806 .forDevice(deviceId)
Saurav Das8a0732e2015-11-20 15:27:53 -0800807 .withSelector(filteredSelector.build())
808 .withTreatment(tb.build())
809 .forTable(forTableId);
Saurav Das822c4e22015-10-23 10:51:11 -0700810
811 if (fwd.permanent()) {
812 ruleBuilder.makePermanent();
813 } else {
814 ruleBuilder.makeTemporary(fwd.timeout());
815 }
816
Saurav Das822c4e22015-10-23 10:51:11 -0700817 return Collections.singletonList(ruleBuilder.build());
818 }
819
Saurav Das4ce45962015-11-24 23:21:05 -0800820 /**
821 * Handles forwarding rules to the L2 bridging table. Flow actions are not
822 * allowed in the bridging table - instead we use L2 Interface group or
823 * L2 flood group
824 *
825 * @param fwd the forwarding objective
826 * @return A collection of flow rules, or an empty set
827 */
828 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
829 List<FlowRule> rules = new ArrayList<>();
830
831 // Build filtered selector
832 TrafficSelector selector = fwd.selector();
833 EthCriterion ethCriterion = (EthCriterion) selector
834 .getCriterion(Criterion.Type.ETH_DST);
835 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
836 .getCriterion(Criterion.Type.VLAN_VID);
837
838 if (vlanIdCriterion == null) {
839 log.warn("Forwarding objective for bridging requires vlan. Not "
840 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
841 fail(fwd, ObjectiveError.BADPARAMS);
842 return Collections.emptySet();
843 }
844
845 TrafficSelector.Builder filteredSelectorBuilder =
846 DefaultTrafficSelector.builder();
847 // Do not match MacAddress for subnet broadcast entry
848 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
849 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
850 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
851 fwd.id(), fwd.nextId(), deviceId);
852 } else {
853 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
854 + "in dev:{} for vlan:{}",
855 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
856 }
857 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
858 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
859
860 if (fwd.treatment() != null) {
861 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
862 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
863 }
864
865 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
866 if (fwd.nextId() != null) {
867 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
868 if (next != null) {
869 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
870 // we only need the top level group's key to point the flow to it
871 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
872 if (group != null) {
873 treatmentBuilder.deferred().group(group.id());
874 } else {
875 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
876 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
877 fail(fwd, ObjectiveError.GROUPMISSING);
878 return Collections.emptySet();
879 }
880 }
881 }
882 treatmentBuilder.immediate().transition(ACL_TABLE);
883 TrafficTreatment filteredTreatment = treatmentBuilder.build();
884
885 // Build bridging table entries
886 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
887 flowRuleBuilder.fromApp(fwd.appId())
888 .withPriority(fwd.priority())
889 .forDevice(deviceId)
890 .withSelector(filteredSelector)
891 .withTreatment(filteredTreatment)
892 .forTable(BRIDGING_TABLE);
893 if (fwd.permanent()) {
894 flowRuleBuilder.makePermanent();
895 } else {
896 flowRuleBuilder.makeTemporary(fwd.timeout());
897 }
898 rules.add(flowRuleBuilder.build());
899 return rules;
900 }
901
Saurav Das822c4e22015-10-23 10:51:11 -0700902 private void pass(Objective obj) {
903 if (obj.context().isPresent()) {
904 obj.context().get().onSuccess(obj);
905 }
906 }
907
Saurav Das8a0732e2015-11-20 15:27:53 -0800908 protected void fail(Objective obj, ObjectiveError error) {
Saurav Das822c4e22015-10-23 10:51:11 -0700909 if (obj.context().isPresent()) {
910 obj.context().get().onError(obj, error);
911 }
912 }
913
914 //////////////////////////////////////
915 // Group handling
916 //////////////////////////////////////
917
Saurav Das4f980082015-11-05 13:39:15 -0800918 private void addGroup(NextObjective nextObjective) {
919 switch (nextObjective.type()) {
920 case SIMPLE:
921 Collection<TrafficTreatment> treatments = nextObjective.next();
922 if (treatments.size() != 1) {
923 log.error("Next Objectives of type Simple should only have a "
924 + "single Traffic Treatment. Next Objective Id:{}",
925 nextObjective.id());
926 fail(nextObjective, ObjectiveError.BADPARAMS);
927 return;
928 }
929 processSimpleNextObjective(nextObjective);
930 break;
931 case BROADCAST:
932 processBroadcastNextObjective(nextObjective);
933 break;
934 case HASHED:
935 processHashedNextObjective(nextObjective);
936 break;
937 case FAILOVER:
938 fail(nextObjective, ObjectiveError.UNSUPPORTED);
939 log.warn("Unsupported next objective type {}", nextObjective.type());
940 break;
941 default:
942 fail(nextObjective, ObjectiveError.UNKNOWN);
943 log.warn("Unknown next objective type {}", nextObjective.type());
944 }
945 }
946
Saurav Das822c4e22015-10-23 10:51:11 -0700947 /**
948 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
Saurav Das8a0732e2015-11-20 15:27:53 -0800949 * a chain of groups. The simple Next Objective passed
Saurav Das822c4e22015-10-23 10:51:11 -0700950 * in by the application has to be broken up into a group chain
Saurav Das8a0732e2015-11-20 15:27:53 -0800951 * comprising of an L3 Unicast Group that points to an L2 Interface
952 * Group which in-turn points to an output port. In some cases, the simple
953 * next Objective can just be an L2 interface without the need for chaining.
Saurav Das822c4e22015-10-23 10:51:11 -0700954 *
955 * @param nextObj the nextObjective of type SIMPLE
956 */
957 private void processSimpleNextObjective(NextObjective nextObj) {
Saurav Das822c4e22015-10-23 10:51:11 -0700958 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Das4ce45962015-11-24 23:21:05 -0800959 // determine if plain L2 or L3->L2
960 boolean plainL2 = true;
961 for (Instruction ins : treatment.allInstructions()) {
962 if (ins.type() == Instruction.Type.L2MODIFICATION) {
963 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
964 if (l2ins.subtype() == L2SubType.ETH_DST ||
965 l2ins.subtype() == L2SubType.ETH_SRC) {
966 plainL2 = false;
967 break;
968 }
969 }
970 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800971
Saurav Das4ce45962015-11-24 23:21:05 -0800972 if (plainL2) {
973 createL2InterfaceGroup(nextObj);
974 return;
975 }
976
977 // break up simple next objective to GroupChain objects
Saurav Das8a0732e2015-11-20 15:27:53 -0800978 GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
979 nextObj.appId(), false,
980 nextObj.meta());
981 if (groupInfo == null) {
982 log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
983 return;
984 }
985 // create object for local and distributed storage
986 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
987 gkeyChain.addFirst(groupInfo.innerGrpDesc.appCookie());
988 gkeyChain.addFirst(groupInfo.outerGrpDesc.appCookie());
989 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
990 Collections.singletonList(gkeyChain),
991 nextObj);
992
Saurav Das4ce45962015-11-24 23:21:05 -0800993 // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
994 updatePendingNextObjective(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp);
Saurav Das8a0732e2015-11-20 15:27:53 -0800995
996 // now we are ready to send the l2 groupDescription (inner), as all the stores
997 // that will get async replies have been updated. By waiting to update
998 // the stores, we prevent nasty race conditions.
999 groupService.addGroup(groupInfo.innerGrpDesc);
1000 }
1001
Saurav Das4ce45962015-11-24 23:21:05 -08001002 private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
1003 List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>();
1004 nextList.add(value);
1005 List<OfdpaNextGroup> ret = pendingNextObjectives.asMap()
1006 .putIfAbsent(key, nextList);
1007 if (ret != null) {
1008 ret.add(value);
1009 }
1010 }
1011
1012 /**
1013 * Creates a simple L2 Interface Group.
1014 *
1015 * @param nextObj the next Objective
1016 */
1017 private void createL2InterfaceGroup(NextObjective nextObj) {
1018 // only allowed actions are vlan pop and outport
1019 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1020 PortNumber portNum = null;
1021 for (Instruction ins : nextObj.next().iterator().next().allInstructions()) {
1022 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1023 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1024 switch (l2ins.subtype()) {
1025 case VLAN_POP:
1026 ttb.add(l2ins);
1027 break;
1028 default:
1029 break;
1030 }
1031 } else if (ins.type() == Instruction.Type.OUTPUT) {
1032 portNum = ((OutputInstruction) ins).port();
1033 ttb.add(ins);
1034 } else {
1035 log.warn("Driver does not handle this type of TrafficTreatment"
1036 + " instruction in simple nextObjectives: {}", ins.type());
1037 }
1038 }
1039 //use the vlanid associated with the port
1040 VlanId vlanid = port2Vlan.get(portNum);
1041
1042 if (vlanid == null && nextObj.meta() != null) {
1043 // use metadata vlan info if available
1044 Criterion vidCriterion = nextObj.meta().getCriterion(Type.VLAN_VID);
1045 if (vidCriterion != null) {
1046 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
1047 }
1048 }
1049
1050 if (vlanid == null) {
1051 log.error("Driver cannot process an L2/L3 group chain without "
1052 + "egress vlan information for dev: {} port:{}",
1053 deviceId, portNum);
1054 return;
1055 }
1056
1057 // assemble information for ofdpa l2interface group
1058 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum.toLong();
1059 // a globally unique groupkey that is different for ports in the same devices
1060 // but different for the same portnumber on different devices. Also different
1061 // for the various group-types created out of the same next objective.
1062 int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
1063 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
1064
1065 // create group description for the l2interfacegroup
1066 GroupBucket l2interfaceGroupBucket =
1067 DefaultGroupBucket.createIndirectGroupBucket(ttb.build());
1068 GroupDescription l2groupDescription =
1069 new DefaultGroupDescription(
1070 deviceId,
1071 GroupDescription.Type.INDIRECT,
1072 new GroupBuckets(Collections.singletonList(
1073 l2interfaceGroupBucket)),
1074 l2groupkey,
1075 l2groupId,
1076 nextObj.appId());
1077 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
1078 deviceId, Integer.toHexString(l2groupId),
1079 l2groupkey, nextObj.id());
1080
1081 // create object for local and distributed storage
1082 Deque<GroupKey> singleKey = new ArrayDeque<>();
1083 singleKey.addFirst(l2groupkey);
1084 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
1085 Collections.singletonList(singleKey),
1086 nextObj);
1087
1088 // store l2groupkey for the nextObjective that depends on it
1089 updatePendingNextObjective(l2groupkey, ofdpaGrp);
1090 // send the group description to the group service
1091 groupService.addGroup(l2groupDescription);
1092 }
1093
Saurav Das8a0732e2015-11-20 15:27:53 -08001094 /**
1095 * Creates one of two possible group-chains from the treatment
1096 * passed in. Depending on the MPLS boolean, this method either creates
1097 * an L3Unicast Group --> L2Interface Group, if mpls is false;
1098 * or MPLSInterface Group --> L2Interface Group, if mpls is true;
1099 * The returned 'inner' group description is always the L2 Interface group.
1100 *
1101 * @param treatment that needs to be broken up to create the group chain
1102 * @param nextId of the next objective that needs this group chain
1103 * @param appId of the application that sent this next objective
1104 * @param mpls determines if L3Unicast or MPLSInterface group is created
1105 * @param meta metadata passed in by the application as part of the nextObjective
1106 * @return GroupInfo containing the GroupDescription of the
1107 * L2Interface group(inner) and the GroupDescription of the (outer)
1108 * L3Unicast/MPLSInterface group. May return null if there is an
1109 * error in processing the chain
1110 */
1111 private GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
1112 ApplicationId appId, boolean mpls,
1113 TrafficSelector meta) {
Saurav Das822c4e22015-10-23 10:51:11 -07001114 // for the l2interface group, get vlan and port info
Saurav Das8a0732e2015-11-20 15:27:53 -08001115 // for the outer group, get the src/dst mac, and vlan info
1116 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
1117 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
Saurav Das822c4e22015-10-23 10:51:11 -07001118 VlanId vlanid = null;
1119 long portNum = 0;
Saurav Das4ce45962015-11-24 23:21:05 -08001120 boolean setVlan = false, popVlan = false;
Saurav Das822c4e22015-10-23 10:51:11 -07001121 for (Instruction ins : treatment.allInstructions()) {
1122 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1123 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1124 switch (l2ins.subtype()) {
1125 case ETH_DST:
Saurav Das8a0732e2015-11-20 15:27:53 -08001126 outerTtb.setEthDst(((ModEtherInstruction) l2ins).mac());
Saurav Das822c4e22015-10-23 10:51:11 -07001127 break;
1128 case ETH_SRC:
Saurav Das8a0732e2015-11-20 15:27:53 -08001129 outerTtb.setEthSrc(((ModEtherInstruction) l2ins).mac());
Saurav Das822c4e22015-10-23 10:51:11 -07001130 break;
1131 case VLAN_ID:
1132 vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
Saurav Das8a0732e2015-11-20 15:27:53 -08001133 outerTtb.setVlanId(vlanid);
Saurav Das4ce45962015-11-24 23:21:05 -08001134 setVlan = true;
Saurav Das8a0732e2015-11-20 15:27:53 -08001135 break;
1136 case VLAN_POP:
1137 innerTtb.popVlan();
Saurav Das4ce45962015-11-24 23:21:05 -08001138 popVlan = true;
Saurav Das822c4e22015-10-23 10:51:11 -07001139 break;
1140 case DEC_MPLS_TTL:
1141 case MPLS_LABEL:
1142 case MPLS_POP:
1143 case MPLS_PUSH:
1144 case VLAN_PCP:
Saurav Das822c4e22015-10-23 10:51:11 -07001145 case VLAN_PUSH:
1146 default:
1147 break;
1148 }
1149 } else if (ins.type() == Instruction.Type.OUTPUT) {
1150 portNum = ((OutputInstruction) ins).port().toLong();
Saurav Das8a0732e2015-11-20 15:27:53 -08001151 innerTtb.add(ins);
Saurav Das822c4e22015-10-23 10:51:11 -07001152 } else {
1153 log.warn("Driver does not handle this type of TrafficTreatment"
1154 + " instruction in nextObjectives: {}", ins.type());
1155 }
1156 }
1157
Saurav Das8a0732e2015-11-20 15:27:53 -08001158 if (vlanid == null) {
1159 //use the vlanid associated with the port
1160 vlanid = port2Vlan.get(PortNumber.portNumber(portNum));
1161 }
1162
Saurav Das4ce45962015-11-24 23:21:05 -08001163 if (vlanid == null && meta != null) {
1164 // use metadata if available
1165 Criterion vidCriterion = meta.getCriterion(Type.VLAN_VID);
1166 if (vidCriterion != null) {
1167 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
Saurav Das8a0732e2015-11-20 15:27:53 -08001168 }
1169 }
1170
1171 if (vlanid == null) {
1172 log.error("Driver cannot process an L2/L3 group chain without "
1173 + "egress vlan information for dev: {} port:{}",
1174 deviceId, portNum);
1175 return null;
1176 }
1177
Saurav Das4ce45962015-11-24 23:21:05 -08001178 if (!setVlan && !popVlan) {
1179 // untagged outgoing port
1180 TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
1181 temp.popVlan();
1182 innerTtb.build().allInstructions().forEach(i -> temp.add(i));
1183 innerTtb = temp;
1184 }
1185
Saurav Das822c4e22015-10-23 10:51:11 -07001186 // assemble information for ofdpa l2interface group
Saurav Das822c4e22015-10-23 10:51:11 -07001187 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
Saurav Das8a0732e2015-11-20 15:27:53 -08001188 // a globally unique groupkey that is different for ports in the same devices
1189 // but different for the same portnumber on different devices. Also different
1190 // for the various group-types created out of the same next objective.
1191 int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum);
1192 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
Saurav Das822c4e22015-10-23 10:51:11 -07001193
Saurav Das8a0732e2015-11-20 15:27:53 -08001194 // assemble information for outer group
1195 GroupDescription outerGrpDesc = null;
1196 if (mpls) {
1197 // outer group is MPLSInteface
1198 Integer mplsgroupId = MPLSINTERFACEMASK | (int) portNum;
1199 // using mplsinterfacemask in groupkey to differentiate from l2interface
1200 int mplsgk = MPLSINTERFACEMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
1201 final GroupKey mplsgroupkey = new DefaultGroupKey(appKryo.serialize(mplsgk));
1202 outerTtb.group(new DefaultGroupId(l2groupId));
1203 // create the mpls-interface group description to wait for the
1204 // l2 interface group to be processed
1205 GroupBucket mplsinterfaceGroupBucket =
1206 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
1207 outerGrpDesc = new DefaultGroupDescription(
1208 deviceId,
1209 GroupDescription.Type.INDIRECT,
1210 new GroupBuckets(Collections.singletonList(
1211 mplsinterfaceGroupBucket)),
1212 mplsgroupkey,
1213 mplsgroupId,
1214 appId);
1215 log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
1216 deviceId, Integer.toHexString(mplsgroupId),
1217 mplsgroupkey, nextId);
1218 } else {
1219 // outer group is L3Unicast
1220 Integer l3groupId = L3UNICASTMASK | (int) portNum;
1221 int l3gk = L3UNICASTMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
1222 final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
1223 outerTtb.group(new DefaultGroupId(l2groupId));
1224 // create the l3unicast group description to wait for the
1225 // l2 interface group to be processed
1226 GroupBucket l3unicastGroupBucket =
1227 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
1228 outerGrpDesc = new DefaultGroupDescription(
1229 deviceId,
1230 GroupDescription.Type.INDIRECT,
1231 new GroupBuckets(Collections.singletonList(
1232 l3unicastGroupBucket)),
1233 l3groupkey,
1234 l3groupId,
1235 appId);
1236 log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
1237 deviceId, Integer.toHexString(l3groupId),
1238 l3groupkey, nextId);
1239 }
Saurav Das822c4e22015-10-23 10:51:11 -07001240
Saurav Das8a0732e2015-11-20 15:27:53 -08001241 // store l2groupkey with the groupChainElem for the outer-group that depends on it
1242 GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1);
1243 Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1244 new ConcurrentHashMap<GroupChainElem, Boolean>());
1245 gceSet.add(gce);
1246 Set<GroupChainElem> retval = pendingGroups.putIfAbsent(l2groupkey, gceSet);
1247 if (retval != null) {
1248 retval.add(gce);
1249 }
Saurav Das822c4e22015-10-23 10:51:11 -07001250
Saurav Das8a0732e2015-11-20 15:27:53 -08001251 // create group description for the inner l2interfacegroup
1252 GroupBucket l2interfaceGroupBucket =
1253 DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
1254 GroupDescription l2groupDescription =
1255 new DefaultGroupDescription(
1256 deviceId,
1257 GroupDescription.Type.INDIRECT,
1258 new GroupBuckets(Collections.singletonList(
1259 l2interfaceGroupBucket)),
1260 l2groupkey,
1261 l2groupId,
1262 appId);
1263 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
1264 deviceId, Integer.toHexString(l2groupId),
1265 l2groupkey, nextId);
1266 return new GroupInfo(l2groupDescription, outerGrpDesc);
Saurav Das822c4e22015-10-23 10:51:11 -07001267
Saurav Das822c4e22015-10-23 10:51:11 -07001268 }
1269
1270 /**
Saurav Das4f980082015-11-05 13:39:15 -08001271 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
Saurav Das8a0732e2015-11-20 15:27:53 -08001272 * a chain of groups. The broadcast Next Objective passed in by the application
Saurav Das4f980082015-11-05 13:39:15 -08001273 * has to be broken up into a group chain comprising of an
1274 * L2 Flood group whose buckets point to L2 Interface groups.
1275 *
1276 * @param nextObj the nextObjective of type BROADCAST
1277 */
1278 private void processBroadcastNextObjective(NextObjective nextObj) {
1279 // break up broadcast next objective to multiple groups
1280 Collection<TrafficTreatment> buckets = nextObj.next();
1281
1282 // each treatment is converted to an L2 interface group
Saurav Das4f980082015-11-05 13:39:15 -08001283 VlanId vlanid = null;
Saurav Das8a0732e2015-11-20 15:27:53 -08001284 List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>();
1285 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
Saurav Das4f980082015-11-05 13:39:15 -08001286 for (TrafficTreatment treatment : buckets) {
1287 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
1288 PortNumber portNum = null;
1289 // ensure that the only allowed treatments are pop-vlan and output
1290 for (Instruction ins : treatment.allInstructions()) {
1291 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1292 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1293 switch (l2ins.subtype()) {
1294 case VLAN_POP:
1295 newTreatment.add(l2ins);
1296 break;
1297 default:
1298 log.debug("action {} not permitted for broadcast nextObj",
1299 l2ins.subtype());
1300 break;
1301 }
1302 } else if (ins.type() == Instruction.Type.OUTPUT) {
1303 portNum = ((OutputInstruction) ins).port();
1304 newTreatment.add(ins);
1305 } else {
1306 log.debug("TrafficTreatment of type {} not permitted in "
1307 + " broadcast nextObjective", ins.type());
1308 }
1309 }
1310
1311 // also ensure that all ports are in the same vlan
Saurav Das4ce45962015-11-24 23:21:05 -08001312 // XXX maybe HA issue here?
Saurav Das4f980082015-11-05 13:39:15 -08001313 VlanId thisvlanid = port2Vlan.get(portNum);
1314 if (vlanid == null) {
1315 vlanid = thisvlanid;
1316 } else {
1317 if (!vlanid.equals(thisvlanid)) {
1318 log.error("Driver requires all ports in a broadcast nextObj "
1319 + "to be in the same vlan. Different vlans found "
1320 + "{} and {}. Aborting group creation", vlanid, thisvlanid);
1321 return;
1322 }
1323 }
1324
Saurav Das8a0732e2015-11-20 15:27:53 -08001325 // assemble info for l2 interface group
1326 int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
Saurav Das4f980082015-11-05 13:39:15 -08001327 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
1328 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) |
1329 (int) portNum.toLong();
Saurav Das8a0732e2015-11-20 15:27:53 -08001330 GroupBucket l2interfaceGroupBucket =
Saurav Das4f980082015-11-05 13:39:15 -08001331 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
Saurav Das8a0732e2015-11-20 15:27:53 -08001332 GroupDescription l2interfaceGroupDescription =
1333 new DefaultGroupDescription(
1334 deviceId,
1335 GroupDescription.Type.INDIRECT,
1336 new GroupBuckets(Collections.singletonList(
1337 l2interfaceGroupBucket)),
1338 l2groupkey,
1339 l2groupId,
1340 nextObj.appId());
1341 log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
1342 deviceId, Integer.toHexString(l2groupId),
1343 l2groupkey, nextObj.id());
1344
1345 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1346 gkeyChain.addFirst(l2groupkey);
Saurav Das4f980082015-11-05 13:39:15 -08001347
1348 // store the info needed to create this group
Saurav Das8a0732e2015-11-20 15:27:53 -08001349 l2interfaceGroupDescs.add(l2interfaceGroupDescription);
1350 allGroupKeys.add(gkeyChain);
Saurav Das4f980082015-11-05 13:39:15 -08001351 }
1352
1353 // assemble info for l2 flood group
Saurav Das4f980082015-11-05 13:39:15 -08001354 Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id();
Saurav Das8a0732e2015-11-20 15:27:53 -08001355 int l2floodgk = L2FLOODMASK | nextObj.id() << 12;
1356 final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk));
1357 // collection of group buckets pointing to all the l2 interface groups
1358 List<GroupBucket> l2floodBuckets = new ArrayList<>();
1359 for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
Saurav Das4f980082015-11-05 13:39:15 -08001360 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Saurav Das8a0732e2015-11-20 15:27:53 -08001361 ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
1362 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
1363 l2floodBuckets.add(abucket);
Saurav Das4f980082015-11-05 13:39:15 -08001364 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001365 // create the l2flood group-description to wait for all the
1366 // l2interface groups to be processed
1367 GroupDescription l2floodGroupDescription =
1368 new DefaultGroupDescription(
1369 deviceId,
1370 GroupDescription.Type.ALL,
1371 new GroupBuckets(l2floodBuckets),
1372 l2floodgroupkey,
1373 l2floodgroupId,
1374 nextObj.appId());
1375 GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
1376 l2interfaceGroupDescs.size());
1377 log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
1378 deviceId, Integer.toHexString(l2floodgroupId),
1379 l2floodgroupkey, nextObj.id());
Saurav Das4f980082015-11-05 13:39:15 -08001380
1381 // create objects for local and distributed storage
Saurav Das8a0732e2015-11-20 15:27:53 -08001382 allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l2floodgroupkey));
1383 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
Saurav Das4f980082015-11-05 13:39:15 -08001384
1385 // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective
1386 // that depends on it
Saurav Das4ce45962015-11-24 23:21:05 -08001387 updatePendingNextObjective(l2floodgroupkey, ofdpaGrp);
Saurav Das4f980082015-11-05 13:39:15 -08001388
Saurav Das8a0732e2015-11-20 15:27:53 -08001389 for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
Saurav Das4f980082015-11-05 13:39:15 -08001390 // store all l2groupkeys with the groupChainElem for the l2floodgroup
1391 // that depends on it
Saurav Das8a0732e2015-11-20 15:27:53 -08001392 Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1393 new ConcurrentHashMap<GroupChainElem, Boolean>());
1394 gceSet.add(gce);
1395 Set<GroupChainElem> retval = pendingGroups.putIfAbsent(
1396 l2intGrpDesc.appCookie(), gceSet);
1397 if (retval != null) {
1398 retval.add(gce);
1399 }
Saurav Das4f980082015-11-05 13:39:15 -08001400
1401 // create and send groups for all l2 interface groups
Saurav Das8a0732e2015-11-20 15:27:53 -08001402 groupService.addGroup(l2intGrpDesc);
Saurav Das4f980082015-11-05 13:39:15 -08001403 }
1404 }
1405
Saurav Das8a0732e2015-11-20 15:27:53 -08001406 /**
1407 * Utility class for moving group information around.
1408 *
1409 */
Saurav Das4f980082015-11-05 13:39:15 -08001410 private class GroupInfo {
Saurav Das8a0732e2015-11-20 15:27:53 -08001411 private GroupDescription innerGrpDesc;
1412 private GroupDescription outerGrpDesc;
Saurav Das4f980082015-11-05 13:39:15 -08001413
Saurav Das8a0732e2015-11-20 15:27:53 -08001414 GroupInfo(GroupDescription innerGrpDesc, GroupDescription outerGrpDesc) {
1415 this.innerGrpDesc = innerGrpDesc;
1416 this.outerGrpDesc = outerGrpDesc;
Saurav Das4f980082015-11-05 13:39:15 -08001417 }
1418 }
1419
Saurav Das8a0732e2015-11-20 15:27:53 -08001420 /**
1421 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
1422 * a chain of groups. The hashed Next Objective passed in by the application
1423 * has to be broken up into a group chain comprising of an
1424 * L3 ECMP group as the top level group. Buckets of this group can point
1425 * to a variety of groups in a group chain, depending on the whether
1426 * MPLS labels are being pushed or not.
1427 * <p>
1428 * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
1429 * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
1430 * check the nextObjective meta.
1431 *
1432 * @param nextObj the nextObjective of type HASHED
1433 */
Saurav Das4f980082015-11-05 13:39:15 -08001434 private void processHashedNextObjective(NextObjective nextObj) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001435 // break up hashed next objective to multiple groups
1436 Collection<TrafficTreatment> buckets = nextObj.next();
1437
1438 // storage for all group keys in the chain of groups created
1439 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1440 List<GroupInfo> unsentGroups = new ArrayList<>();
1441 for (TrafficTreatment bucket : buckets) {
1442 //figure out how many labels are pushed in each bucket
1443 int labelsPushed = 0;
1444 MplsLabel innermostLabel = null;
1445 for (Instruction ins : bucket.allInstructions()) {
1446 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1447 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1448 if (l2ins.subtype() == L2SubType.MPLS_PUSH) {
1449 labelsPushed++;
1450 }
1451 if (l2ins.subtype() == L2SubType.MPLS_LABEL) {
1452 if (innermostLabel == null) {
1453 innermostLabel = ((ModMplsLabelInstruction) l2ins).mplsLabel();
1454 }
1455 }
1456 }
1457 }
1458
1459 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1460 // XXX we only deal with 0 and 1 label push right now
1461 if (labelsPushed == 0) {
1462 GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1463 nextObj.appId(), false,
1464 nextObj.meta());
1465 if (nolabelGroupInfo == null) {
1466 log.error("Could not process nextObj={} in dev:{}",
1467 nextObj.id(), deviceId);
1468 return;
1469 }
1470 gkeyChain.addFirst(nolabelGroupInfo.innerGrpDesc.appCookie());
1471 gkeyChain.addFirst(nolabelGroupInfo.outerGrpDesc.appCookie());
1472
1473 // we can't send the inner group description yet, as we have to
1474 // create the dependent ECMP group first. So we store..
1475 unsentGroups.add(nolabelGroupInfo);
1476
1477 } else if (labelsPushed == 1) {
1478 GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1479 nextObj.appId(), true,
1480 nextObj.meta());
1481 if (onelabelGroupInfo == null) {
1482 log.error("Could not process nextObj={} in dev:{}",
1483 nextObj.id(), deviceId);
1484 return;
1485 }
1486 // we need to add another group to this chain - the L3VPN group
1487 TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder();
1488 l3vpnTtb.pushMpls()
1489 .setMpls(innermostLabel)
1490 .setMplsBos(true)
1491 .copyTtlOut()
1492 .group(new DefaultGroupId(
1493 onelabelGroupInfo.outerGrpDesc.givenGroupId()));
1494 GroupBucket l3vpnGrpBkt =
1495 DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
1496 int l3vpngroupId = L3VPNMASK | l3vpnindex.incrementAndGet();
1497 int l3vpngk = L3VPNMASK | nextObj.id() << 12 | l3vpnindex.get();
1498 GroupKey l3vpngroupkey = new DefaultGroupKey(appKryo.serialize(l3vpngk));
1499 GroupDescription l3vpnGroupDesc =
1500 new DefaultGroupDescription(
1501 deviceId,
1502 GroupDescription.Type.INDIRECT,
1503 new GroupBuckets(Collections.singletonList(
1504 l3vpnGrpBkt)),
1505 l3vpngroupkey,
1506 l3vpngroupId,
1507 nextObj.appId());
1508 GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1);
1509 Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1510 new ConcurrentHashMap<GroupChainElem, Boolean>());
1511 gceSet.add(l3vpnGce);
1512 Set<GroupChainElem> retval = pendingGroups
1513 .putIfAbsent(onelabelGroupInfo.outerGrpDesc.appCookie(), gceSet);
1514 if (retval != null) {
1515 retval.add(l3vpnGce);
1516 }
1517
1518 gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie());
1519 gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie());
1520 gkeyChain.addFirst(l3vpngroupkey);
1521
1522 //now we can replace the outerGrpDesc with the one we just created
1523 onelabelGroupInfo.outerGrpDesc = l3vpnGroupDesc;
1524
1525 // we can't send the innermost group yet, as we have to create
1526 // the dependent ECMP group first. So we store ...
1527 unsentGroups.add(onelabelGroupInfo);
1528
1529 log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}",
1530 deviceId, Integer.toHexString(l3vpngroupId),
1531 l3vpngroupkey, nextObj.id());
1532
1533 } else {
1534 log.warn("Driver currently does not handle more than 1 MPLS "
1535 + "labels. Not processing nextObjective {}", nextObj);
1536 return;
1537 }
1538
1539 // all groups in this chain
1540 allGroupKeys.add(gkeyChain);
1541 }
1542
1543 // now we can create the outermost L3 ECMP group
1544 List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
1545 for (GroupInfo gi : unsentGroups) {
1546 // create ECMP bucket to point to the outer group
1547 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1548 ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
1549 GroupBucket sbucket = DefaultGroupBucket
1550 .createSelectGroupBucket(ttb.build());
1551 l3ecmpGroupBuckets.add(sbucket);
1552 }
1553 int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12;
1554 GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId));
1555 GroupDescription l3ecmpGroupDesc =
1556 new DefaultGroupDescription(
1557 deviceId,
1558 GroupDescription.Type.SELECT,
1559 new GroupBuckets(l3ecmpGroupBuckets),
1560 l3ecmpGroupKey,
1561 l3ecmpGroupId,
1562 nextObj.appId());
1563 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
1564 l3ecmpGroupBuckets.size());
1565
1566 // create objects for local and distributed storage
1567 allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey));
1568 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
1569
1570 // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
1571 // that depends on it
Saurav Das4ce45962015-11-24 23:21:05 -08001572 updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp);
Saurav Das8a0732e2015-11-20 15:27:53 -08001573
1574 log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
1575 deviceId, Integer.toHexString(l3ecmpGroupId),
1576 l3ecmpGroupKey, nextObj.id());
1577 // finally we are ready to send the innermost groups
1578 for (GroupInfo gi : unsentGroups) {
1579 log.debug("Sending innermost group {} in group chain on device {} ",
1580 Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
1581 Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1582 new ConcurrentHashMap<GroupChainElem, Boolean>());
1583 gceSet.add(l3ecmpGce);
1584 Set<GroupChainElem> retval = pendingGroups
1585 .putIfAbsent(gi.outerGrpDesc.appCookie(), gceSet);
1586 if (retval != null) {
1587 retval.add(l3ecmpGce);
1588 }
1589
1590 groupService.addGroup(gi.innerGrpDesc);
1591 }
1592
Saurav Das4f980082015-11-05 13:39:15 -08001593 }
1594
1595 private void addBucketToGroup(NextObjective nextObjective) {
1596 // TODO Auto-generated method stub
1597 }
1598
Saurav Das8a0732e2015-11-20 15:27:53 -08001599 private void waitToAddBucketToGroup(NextObjective nextObjective) {
1600 // TODO Auto-generated method stub
1601 }
1602
Saurav Das4f980082015-11-05 13:39:15 -08001603 private void removeBucketFromGroup(NextObjective nextObjective) {
1604 // TODO Auto-generated method stub
1605 }
1606
1607 private void removeGroup(NextObjective nextObjective) {
1608 // TODO Auto-generated method stub
1609 }
1610
1611 /**
Saurav Das822c4e22015-10-23 10:51:11 -07001612 * Processes next element of a group chain. Assumption is that if this
1613 * group points to another group, the latter has already been created
1614 * and this driver has received notification for it. A second assumption is
1615 * that if there is another group waiting for this group then the appropriate
1616 * stores already have the information to act upon the notification for the
1617 * creating of this group.
Saurav Das4f980082015-11-05 13:39:15 -08001618 * <p>
1619 * The processing of the GroupChainElement depends on the number of groups
1620 * this element is waiting on. For all group types other than SIMPLE, a
1621 * GroupChainElement could be waiting on multiple groups.
Saurav Das822c4e22015-10-23 10:51:11 -07001622 *
1623 * @param gce the group chain element to be processed next
1624 */
1625 private void processGroupChain(GroupChainElem gce) {
Saurav Das4f980082015-11-05 13:39:15 -08001626 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
1627 if (waitOnGroups != 0) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001628 log.debug("GCE: {} not ready to be processed", gce);
Saurav Das4f980082015-11-05 13:39:15 -08001629 return;
1630 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001631 log.debug("GCE: {} ready to be processed", gce);
1632 groupService.addGroup(gce.groupDescription);
Saurav Das4f980082015-11-05 13:39:15 -08001633 }
Saurav Das822c4e22015-10-23 10:51:11 -07001634
1635 private class GroupChecker implements Runnable {
1636 @Override
1637 public void run() {
1638 Set<GroupKey> keys = pendingGroups.keySet().stream()
1639 .filter(key -> groupService.getGroup(deviceId, key) != null)
1640 .collect(Collectors.toSet());
1641 Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream()
1642 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
1643 .collect(Collectors.toSet());
1644 keys.addAll(otherkeys);
1645
1646 keys.stream().forEach(key -> {
1647 //first check for group chain
Saurav Das8a0732e2015-11-20 15:27:53 -08001648 Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1649 if (gceSet != null) {
1650 for (GroupChainElem gce : gceSet) {
1651 log.info("Group service processed group key {} in device {}. "
1652 + "Processing next group in group chain with group id {}",
1653 key, deviceId,
1654 Integer.toHexString(gce.groupDescription.givenGroupId()));
1655 processGroupChain(gce);
1656 }
Saurav Das822c4e22015-10-23 10:51:11 -07001657 } else {
Saurav Das4ce45962015-11-24 23:21:05 -08001658 List<OfdpaNextGroup> objList = pendingNextObjectives.getIfPresent(key);
1659 if (objList != null) {
Saurav Das822c4e22015-10-23 10:51:11 -07001660 pendingNextObjectives.invalidate(key);
Saurav Das4ce45962015-11-24 23:21:05 -08001661 objList.forEach(obj -> {
1662 log.info("Group service processed group key {} in device:{}. "
1663 + "Done implementing next objective: {} <<-->> gid:{}",
1664 key, deviceId, obj.nextObjective().id(),
1665 Integer.toHexString(groupService.getGroup(deviceId, key)
1666 .givenGroupId()));
1667 pass(obj.nextObjective());
1668 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
1669 });
Saurav Das822c4e22015-10-23 10:51:11 -07001670 }
1671 }
1672 });
1673 }
1674 }
1675
1676 private class InnerGroupListener implements GroupListener {
1677 @Override
1678 public void event(GroupEvent event) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001679 log.trace("received group event of type {}", event.type());
Saurav Das822c4e22015-10-23 10:51:11 -07001680 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
1681 GroupKey key = event.subject().appCookie();
1682 // first check for group chain
Saurav Das8a0732e2015-11-20 15:27:53 -08001683 Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1684 if (gceSet != null) {
1685 for (GroupChainElem gce : gceSet) {
1686 log.info("group ADDED with group key {} .. "
1687 + "Processing next group in group chain with group key {}",
1688 key,
1689 gce.groupDescription.appCookie());
1690 processGroupChain(gce);
1691 }
Saurav Das822c4e22015-10-23 10:51:11 -07001692 } else {
Saurav Das4ce45962015-11-24 23:21:05 -08001693 List<OfdpaNextGroup> objList = pendingNextObjectives.getIfPresent(key);
1694 if (objList != null) {
Saurav Das822c4e22015-10-23 10:51:11 -07001695 pendingNextObjectives.invalidate(key);
Saurav Das4ce45962015-11-24 23:21:05 -08001696 objList.forEach(obj -> {
1697 log.info("group ADDED with key {} in dev {}.. Done implementing next "
1698 + "objective: {} <<-->> gid:{}",
1699 key, deviceId, obj.nextObjective().id(),
1700 Integer.toHexString(groupService.getGroup(deviceId, key)
1701 .givenGroupId()));
1702 pass(obj.nextObjective());
1703 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
1704 });
Saurav Das822c4e22015-10-23 10:51:11 -07001705 }
1706 }
1707 }
1708 }
1709 }
1710
1711 /**
Saurav Das8a0732e2015-11-20 15:27:53 -08001712 * Represents an entire group-chain that implements a Next-Objective from
1713 * the application. The objective is represented as a list of deques, where
1714 * each deque can is a separate chain of groups.
1715 * <p>
1716 * For example, an ECMP group with 3 buckets, where each bucket points to
1717 * a group chain of L3 Unicast and L2 interface groups will look like this:
1718 * <ul>
1719 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1720 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1721 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1722 * </ul>
1723 * where the first element of each deque is the same, representing the
1724 * top level ECMP group, while every other element represents a unique groupKey.
1725 * <p>
1726 * Also includes information about the next objective that
1727 * resulted in this group-chain.
Saurav Das4f980082015-11-05 13:39:15 -08001728 *
Saurav Das822c4e22015-10-23 10:51:11 -07001729 */
Saurav Das8a0732e2015-11-20 15:27:53 -08001730 private class OfdpaNextGroup implements NextGroup {
Saurav Das822c4e22015-10-23 10:51:11 -07001731 private final NextObjective nextObj;
Saurav Das8a0732e2015-11-20 15:27:53 -08001732 private final List<Deque<GroupKey>> gkeys;
Saurav Das822c4e22015-10-23 10:51:11 -07001733
Saurav Das8a0732e2015-11-20 15:27:53 -08001734 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
Saurav Das822c4e22015-10-23 10:51:11 -07001735 this.gkeys = gkeys;
1736 this.nextObj = nextObj;
1737 }
1738
1739 @SuppressWarnings("unused")
Saurav Das8a0732e2015-11-20 15:27:53 -08001740 public List<Deque<GroupKey>> groupKey() {
Saurav Das822c4e22015-10-23 10:51:11 -07001741 return gkeys;
1742 }
1743
1744 public NextObjective nextObjective() {
1745 return nextObj;
1746 }
1747
1748 @Override
1749 public byte[] data() {
1750 return appKryo.serialize(gkeys);
1751 }
1752
1753 }
1754
1755 /**
1756 * Represents a group element that is part of a chain of groups.
1757 * Stores enough information to create a Group Description to add the group
1758 * to the switch by requesting the Group Service. Objects instantiating this
1759 * class are meant to be temporary and live as long as it is needed to wait for
1760 * preceding groups in the group chain to be created.
1761 */
1762 private class GroupChainElem {
Saurav Das8a0732e2015-11-20 15:27:53 -08001763 private GroupDescription groupDescription;
Saurav Das4f980082015-11-05 13:39:15 -08001764 private AtomicInteger waitOnGroups;
Saurav Das822c4e22015-10-23 10:51:11 -07001765
Saurav Das8a0732e2015-11-20 15:27:53 -08001766 GroupChainElem(GroupDescription groupDescription, int waitOnGroups) {
1767 this.groupDescription = groupDescription;
Saurav Das4f980082015-11-05 13:39:15 -08001768 this.waitOnGroups = new AtomicInteger(waitOnGroups);
Saurav Das822c4e22015-10-23 10:51:11 -07001769 }
1770
Saurav Das4f980082015-11-05 13:39:15 -08001771 /**
1772 * This methods atomically decrements the counter for the number of
1773 * groups this GroupChainElement is waiting on, for notifications from
1774 * the Group Service. When this method returns a value of 0, this
1775 * GroupChainElement is ready to be processed.
1776 *
1777 * @return integer indication of the number of notifications being waited on
1778 */
1779 int decrementAndGetGroupsWaitedOn() {
1780 return waitOnGroups.decrementAndGet();
Saurav Das822c4e22015-10-23 10:51:11 -07001781 }
1782
Saurav Das4f980082015-11-05 13:39:15 -08001783 @Override
1784 public String toString() {
Saurav Das8a0732e2015-11-20 15:27:53 -08001785 return (Integer.toHexString(groupDescription.givenGroupId()) +
1786 " groupKey: " + groupDescription.appCookie() +
1787 " waiting-on-groups: " + waitOnGroups.get() +
1788 " device: " + deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -07001789 }
Saurav Das822c4e22015-10-23 10:51:11 -07001790 }
1791
1792}