blob: cf3c7e89136719dc31dd40492c4501d36c1cbe1b [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
21import java.nio.ByteBuffer;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.List;
Saurav Das4f980082015-11-05 13:39:15 -080026import java.util.Map;
Saurav Das822c4e22015-10-23 10:51:11 -070027import java.util.Set;
28import java.util.concurrent.ConcurrentHashMap;
29import java.util.concurrent.Executors;
30import java.util.concurrent.ScheduledExecutorService;
31import java.util.concurrent.TimeUnit;
Saurav Das4f980082015-11-05 13:39:15 -080032import java.util.concurrent.atomic.AtomicInteger;
Saurav Das822c4e22015-10-23 10:51:11 -070033import java.util.stream.Collectors;
34
35import org.onlab.osgi.ServiceDirectory;
36import org.onlab.packet.Data;
37import org.onlab.packet.Ethernet;
38import org.onlab.packet.IPv4;
39import org.onlab.packet.IpPrefix;
40import org.onlab.packet.MPLS;
41import org.onlab.packet.MacAddress;
42import org.onlab.packet.MplsLabel;
43import org.onlab.packet.UDP;
44import org.onlab.packet.VlanId;
45import org.onlab.util.KryoNamespace;
46import org.onosproject.core.ApplicationId;
47import org.onosproject.core.CoreService;
48import org.onosproject.core.DefaultGroupId;
49import org.onosproject.net.DeviceId;
50import org.onosproject.net.Port;
51import org.onosproject.net.PortNumber;
52import org.onosproject.net.behaviour.NextGroup;
53import org.onosproject.net.behaviour.Pipeliner;
54import org.onosproject.net.behaviour.PipelinerContext;
55import org.onosproject.net.device.DeviceService;
56import org.onosproject.net.driver.AbstractHandlerBehaviour;
57import org.onosproject.net.flow.DefaultFlowRule;
58import org.onosproject.net.flow.DefaultTrafficSelector;
59import org.onosproject.net.flow.DefaultTrafficTreatment;
60import org.onosproject.net.flow.FlowRule;
61import org.onosproject.net.flow.FlowRuleOperations;
62import org.onosproject.net.flow.FlowRuleOperationsContext;
63import org.onosproject.net.flow.FlowRuleService;
64import org.onosproject.net.flow.TrafficSelector;
65import org.onosproject.net.flow.TrafficTreatment;
66import org.onosproject.net.flow.criteria.Criteria;
67import org.onosproject.net.flow.criteria.Criterion;
68import org.onosproject.net.flow.criteria.EthCriterion;
69import org.onosproject.net.flow.criteria.EthTypeCriterion;
70import org.onosproject.net.flow.criteria.IPCriterion;
71import 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;
76import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
77import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
78import org.onosproject.net.flowobjective.FilteringObjective;
79import org.onosproject.net.flowobjective.FlowObjectiveStore;
80import org.onosproject.net.flowobjective.ForwardingObjective;
81import org.onosproject.net.flowobjective.NextObjective;
82import org.onosproject.net.flowobjective.Objective;
83import org.onosproject.net.flowobjective.ObjectiveError;
84import org.onosproject.net.group.DefaultGroupBucket;
85import org.onosproject.net.group.DefaultGroupDescription;
86import org.onosproject.net.group.DefaultGroupKey;
87import org.onosproject.net.group.Group;
88import org.onosproject.net.group.GroupBucket;
89import org.onosproject.net.group.GroupBuckets;
90import org.onosproject.net.group.GroupDescription;
91import org.onosproject.net.group.GroupEvent;
92import org.onosproject.net.group.GroupKey;
93import org.onosproject.net.group.GroupListener;
94import org.onosproject.net.group.GroupService;
95import org.onosproject.net.packet.DefaultOutboundPacket;
96import org.onosproject.net.packet.OutboundPacket;
97import org.onosproject.net.packet.PacketContext;
98import org.onosproject.net.packet.PacketProcessor;
99import org.onosproject.net.packet.PacketService;
100import org.onosproject.store.serializers.KryoNamespaces;
101import org.slf4j.Logger;
102
103import com.google.common.cache.Cache;
104import com.google.common.cache.CacheBuilder;
105import com.google.common.cache.RemovalCause;
106import com.google.common.cache.RemovalNotification;
107
108/**
109 * Driver for Broadcom's OF-DPA v2.0 TTP.
110 *
111 */
112public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
113
114 protected static final int PORT_TABLE = 0;
115 protected static final int VLAN_TABLE = 10;
116 protected static final int TMAC_TABLE = 20;
117 protected static final int UNICAST_ROUTING_TABLE = 30;
118 protected static final int MULTICAST_ROUTING_TABLE = 40;
119 protected static final int MPLS_TABLE_0 = 23;
120 protected static final int MPLS_TABLE_1 = 24;
121 protected static final int BRIDGING_TABLE = 50;
122 protected static final int ACL_TABLE = 60;
123 protected static final int MAC_LEARNING_TABLE = 254;
124 protected static final long OFPP_MAX = 0xffffff00L;
125
126 private static final int HIGHEST_PRIORITY = 0xffff;
Saurav Das2857f382015-11-03 14:39:27 -0800127 protected static final int DEFAULT_PRIORITY = 0x8000;
Saurav Das822c4e22015-10-23 10:51:11 -0700128 protected static final int LOWEST_PRIORITY = 0x0;
129
130 /*
131 * Group keys are normally generated by using the next Objective id. In the
132 * case of a next objective resulting in a group chain, each group derives a
133 * group key from the next objective id in the following way:
134 * The upper 4 bits of the group-key are used to denote the position of the
135 * group in the group chain. For example, in the chain
136 * group0 --> group1 --> group2 --> port
137 * group0's group key would have the upper 4 bits as 0, group1's upper four
138 * bits would be 1, and so on
139 */
140 private static final int GROUP0MASK = 0x0;
141 private static final int GROUP1MASK = 0x10000000;
142
143 /*
144 * OFDPA requires group-id's to have a certain form.
145 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
146 * L3 Unicast Groups have <4bits-2><28bits-index>
147 */
148 private static final int L2INTERFACEMASK = 0x0;
149 private static final int L3UNICASTMASK = 0x20000000;
150 //private static final int MPLSINTERFACEMASK = 0x90000000;
151 private static final int L3ECMPMASK = 0x70000000;
Saurav Das4f980082015-11-05 13:39:15 -0800152 private static final int L2FLOODMASK = 0x40000000;
Saurav Das822c4e22015-10-23 10:51:11 -0700153
Saurav Das822c4e22015-10-23 10:51:11 -0700154 private final Logger log = getLogger(getClass());
155 private ServiceDirectory serviceDirectory;
156 protected FlowRuleService flowRuleService;
157 private CoreService coreService;
158 private GroupService groupService;
159 private FlowObjectiveStore flowObjectiveStore;
160 protected DeviceId deviceId;
161 protected ApplicationId driverId;
162 protected PacketService packetService;
163 protected DeviceService deviceService;
164 private InternalPacketProcessor processor = new InternalPacketProcessor();
165 private KryoNamespace appKryo = new KryoNamespace.Builder()
166 .register(KryoNamespaces.API)
167 .register(GroupKey.class)
168 .register(DefaultGroupKey.class)
169 .register(OfdpaGroupChain.class)
170 .register(byte[].class)
171 .build();
172
173 private Cache<GroupKey, OfdpaGroupChain> pendingNextObjectives;
174 private ConcurrentHashMap<GroupKey, GroupChainElem> pendingGroups;
175
176 private ScheduledExecutorService groupChecker =
177 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner",
178 "ofdpa2-%d"));
179 private Set<IPCriterion> sentIpFilters = Collections.newSetFromMap(
180 new ConcurrentHashMap<IPCriterion, Boolean>());
181
Saurav Das4f980082015-11-05 13:39:15 -0800182 // local stores for port-vlan mapping
183 Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<PortNumber, VlanId>();
184 Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId,
185 Set<PortNumber>>();
186
187
188
Saurav Das822c4e22015-10-23 10:51:11 -0700189 @Override
190 public void init(DeviceId deviceId, PipelinerContext context) {
191 this.serviceDirectory = context.directory();
192 this.deviceId = deviceId;
193
194 pendingNextObjectives = CacheBuilder.newBuilder()
195 .expireAfterWrite(20, TimeUnit.SECONDS)
196 .removalListener((RemovalNotification<GroupKey, OfdpaGroupChain> notification) -> {
197 if (notification.getCause() == RemovalCause.EXPIRED) {
198 fail(notification.getValue().nextObjective(),
199 ObjectiveError.GROUPINSTALLATIONFAILED);
200 }
201 }).build();
202
203 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
204 pendingGroups = new ConcurrentHashMap<GroupKey, GroupChainElem>();
205
206 coreService = serviceDirectory.get(CoreService.class);
207 flowRuleService = serviceDirectory.get(FlowRuleService.class);
208 groupService = serviceDirectory.get(GroupService.class);
209 flowObjectiveStore = context.store();
210 packetService = serviceDirectory.get(PacketService.class);
211 deviceService = serviceDirectory.get(DeviceService.class);
212 packetService.addProcessor(processor, PacketProcessor.director(2));
213 groupService.addListener(new InnerGroupListener());
214
215 driverId = coreService.registerApplication(
216 "org.onosproject.driver.OFDPA2Pipeline");
217
218 // OF-DPA does not require initializing the pipeline as it puts default
219 // rules automatically in the hardware. However emulation of OFDPA in
220 // software switches does require table-miss-entries.
221 initializePipeline();
222
223 }
224
225 protected void initializePipeline() {
226
227 }
228
229 //////////////////////////////////////
230 // Flow Objectives
231 //////////////////////////////////////
232
233 @Override
234 public void filter(FilteringObjective filteringObjective) {
235 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
236 processFilter(filteringObjective,
237 filteringObjective.op() == Objective.Operation.ADD,
238 filteringObjective.appId());
239 } else {
240 // Note that packets that don't match the PERMIT filter are
241 // automatically denied. The DENY filter is used to deny packets
242 // that are otherwise permitted by the PERMIT filter.
243 // Use ACL table flow rules here for DENY filtering objectives
244 log.debug("filter objective other than PERMIT currently not supported");
245 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
246 }
247 }
248
249 @Override
250 public void forward(ForwardingObjective fwd) {
251 Collection<FlowRule> rules;
252 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
253
254 rules = processForward(fwd);
255 switch (fwd.op()) {
256 case ADD:
257 rules.stream()
258 .filter(rule -> rule != null)
259 .forEach(flowOpsBuilder::add);
260 break;
261 case REMOVE:
262 rules.stream()
263 .filter(rule -> rule != null)
264 .forEach(flowOpsBuilder::remove);
265 break;
266 default:
267 fail(fwd, ObjectiveError.UNKNOWN);
268 log.warn("Unknown forwarding type {}", fwd.op());
269 }
270
271
272 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
273 @Override
274 public void onSuccess(FlowRuleOperations ops) {
275 pass(fwd);
276 }
277
278 @Override
279 public void onError(FlowRuleOperations ops) {
280 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
281 }
282 }));
283
284 }
285
286 @Override
287 public void next(NextObjective nextObjective) {
Saurav Das4f980082015-11-05 13:39:15 -0800288 log.debug("Processing NextObjective id{} op{}", nextObjective.id(),
289 nextObjective.op());
290 if (nextObjective.op() == Objective.Operation.REMOVE) {
291 if (nextObjective.next().isEmpty()) {
292 removeGroup(nextObjective);
293 } else {
294 removeBucketFromGroup(nextObjective);
Saurav Das822c4e22015-10-23 10:51:11 -0700295 }
Saurav Das4f980082015-11-05 13:39:15 -0800296 } else if (nextObjective.op() == Objective.Operation.ADD) {
297 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
298 if (nextGroup != null) {
299 addBucketToGroup(nextObjective);
300 } else {
301 addGroup(nextObjective);
302 }
303 } else {
304 log.warn("Unsupported operation {}", nextObjective.op());
Saurav Das822c4e22015-10-23 10:51:11 -0700305 }
306 }
307
308 //////////////////////////////////////
309 // Flow handling
310 //////////////////////////////////////
311
Saurav Das4f980082015-11-05 13:39:15 -0800312
Saurav Das822c4e22015-10-23 10:51:11 -0700313 /**
314 * As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
315 * and IP addresses configured on switch ports happen in different tables.
316 * Note that IP filtering rules need to be added to the ACL table, as there
317 * is no mechanism to send to controller via IP table.
318 *
319 * @param filt the filtering objective
320 * @param install indicates whether to add or remove the objective
321 * @param applicationId the application that sent this objective
322 */
323 private void processFilter(FilteringObjective filt,
324 boolean install, ApplicationId applicationId) {
325 // This driver only processes filtering criteria defined with switch
326 // ports as the key
327 PortCriterion portCriterion = null;
328 EthCriterion ethCriterion = null;
329 VlanIdCriterion vidCriterion = null;
330 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
331 if (!filt.key().equals(Criteria.dummy()) &&
332 filt.key().type() == Criterion.Type.IN_PORT) {
333 portCriterion = (PortCriterion) filt.key();
334 } else {
335 log.warn("No key defined in filtering objective from app: {}. Not"
336 + "processing filtering objective", applicationId);
337 fail(filt, ObjectiveError.UNKNOWN);
338 return;
339 }
340 // convert filtering conditions for switch-intfs into flowrules
341 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
342 for (Criterion criterion : filt.conditions()) {
343 if (criterion.type() == Criterion.Type.ETH_DST) {
344 ethCriterion = (EthCriterion) criterion;
345 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
346 vidCriterion = (VlanIdCriterion) criterion;
347 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
348 ips.add((IPCriterion) criterion);
349 } else {
350 log.error("Unsupported filter {}", criterion);
351 fail(filt, ObjectiveError.UNSUPPORTED);
352 return;
353 }
354 }
355
Saurav Das0e99e2b2015-10-28 12:39:42 -0700356 VlanId assignedVlan = null;
357 if (vidCriterion != null && vidCriterion.vlanId() == VlanId.NONE) {
358 // untagged packets are assigned vlans in OF-DPA
359 if (filt.meta() == null) {
360 log.error("Missing metadata in filtering objective required "
361 + "for vlan assignment in dev {}", deviceId);
362 fail(filt, ObjectiveError.BADPARAMS);
363 return;
364 }
365 for (Instruction i : filt.meta().allInstructions()) {
366 if (i instanceof ModVlanIdInstruction) {
367 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
368 }
369 }
370 if (assignedVlan == null) {
371 log.error("Driver requires an assigned vlan-id to tag incoming "
372 + "untagged packets. Not processing vlan filters on "
373 + "device {}", deviceId);
374 fail(filt, ObjectiveError.BADPARAMS);
375 return;
376 }
377 }
378
Saurav Das822c4e22015-10-23 10:51:11 -0700379 if (ethCriterion == null) {
380 log.debug("filtering objective missing dstMac, cannot program TMAC table");
381 } else {
382 for (FlowRule tmacRule : processEthDstFilter(portCriterion, ethCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700383 vidCriterion, assignedVlan,
384 applicationId)) {
Saurav Das822c4e22015-10-23 10:51:11 -0700385 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
386 tmacRule, deviceId);
387 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
388 }
389 }
390
391 if (ethCriterion == null || vidCriterion == null) {
392 log.debug("filtering objective missing dstMac or vlan, cannot program"
393 + "Vlan Table");
394 } else {
395 for (FlowRule vlanRule : processVlanIdFilter(portCriterion, vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700396 assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700397 applicationId)) {
398 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
399 vlanRule, deviceId);
400 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
401 }
402 }
403
404 for (IPCriterion ipaddr : ips) {
405 // since we ignore port information for IP rules, and the same (gateway) IP
406 // can be configured on multiple ports, we make sure that we send
407 // only a single rule to the switch.
408 if (!sentIpFilters.contains(ipaddr)) {
409 sentIpFilters.add(ipaddr);
410 log.debug("adding IP filtering rules in ACL table {} for dev: {}",
411 ipaddr, deviceId);
412 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
413 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
414 selector.matchEthType(Ethernet.TYPE_IPV4);
415 selector.matchIPDst(ipaddr.ip());
416 treatment.setOutput(PortNumber.CONTROLLER);
417 FlowRule rule = DefaultFlowRule.builder()
418 .forDevice(deviceId)
419 .withSelector(selector.build())
420 .withTreatment(treatment.build())
421 .withPriority(HIGHEST_PRIORITY)
422 .fromApp(applicationId)
423 .makePermanent()
424 .forTable(ACL_TABLE).build();
425 ops = install ? ops.add(rule) : ops.remove(rule);
426 }
427 }
428
429 // apply filtering flow rules
430 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
431 @Override
432 public void onSuccess(FlowRuleOperations ops) {
433 log.info("Applied {} filtering rules in device {}",
434 ops.stages().get(0).size(), deviceId);
435 pass(filt);
436 }
437
438 @Override
439 public void onError(FlowRuleOperations ops) {
440 log.info("Failed to apply all filtering rules in dev {}", deviceId);
441 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
442 }
443 }));
444
445 }
446
447 /**
448 * Allows untagged packets into pipeline by assigning a vlan id.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700449 * Vlan assignment is done by the application.
Saurav Das822c4e22015-10-23 10:51:11 -0700450 * Allows tagged packets into pipeline as per configured port-vlan info.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700451 *
Saurav Das822c4e22015-10-23 10:51:11 -0700452 * @param portCriterion port on device for which this filter is programmed
453 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700454 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700455 * @param applicationId for application programming this filter
456 * @return list of FlowRule for port-vlan filters
457 */
458 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
459 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700460 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700461 ApplicationId applicationId) {
462 List<FlowRule> rules = new ArrayList<FlowRule>();
463 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
464 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
465 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800466 treatment.transition(TMAC_TABLE);
467
468 VlanId storeVlan = null;
Saurav Das822c4e22015-10-23 10:51:11 -0700469 if (vidCriterion.vlanId() == VlanId.NONE) {
470 // untagged packets are assigned vlans
Saurav Das0e99e2b2015-10-28 12:39:42 -0700471 treatment.pushVlan().setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800472 // XXX ofdpa will require an additional vlan match on the assigned vlan
473 // and it may not require the push. This is not in compliance with OF
474 // standard. Waiting on what the exact flows are going to look like.
Saurav Das4f980082015-11-05 13:39:15 -0800475 storeVlan = assignedVlan;
476 } else {
477 storeVlan = vidCriterion.vlanId();
Saurav Das822c4e22015-10-23 10:51:11 -0700478 }
Saurav Das822c4e22015-10-23 10:51:11 -0700479
480 // ofdpa cannot match on ALL portnumber, so we need to use separate
481 // rules for each port.
482 List<PortNumber> portnums = new ArrayList<PortNumber>();
483 if (portCriterion.port() == PortNumber.ALL) {
484 for (Port port : deviceService.getPorts(deviceId)) {
485 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
486 portnums.add(port.number());
487 }
488 }
489 } else {
490 portnums.add(portCriterion.port());
491 }
Saurav Das4f980082015-11-05 13:39:15 -0800492
Saurav Das822c4e22015-10-23 10:51:11 -0700493 for (PortNumber pnum : portnums) {
Saurav Das4f980082015-11-05 13:39:15 -0800494 // update storage
495 port2Vlan.put(pnum, storeVlan);
496 Set<PortNumber> vlanPorts = vlan2Port.get(storeVlan);
497 if (vlanPorts == null) {
498 vlanPorts = Collections.newSetFromMap(
499 new ConcurrentHashMap<PortNumber, Boolean>());
500 vlanPorts.add(pnum);
501 vlan2Port.put(storeVlan, vlanPorts);
502 } else {
503 vlanPorts.add(pnum);
504 }
505 // create rest of flowrule
Saurav Das822c4e22015-10-23 10:51:11 -0700506 selector.matchInPort(pnum);
507 FlowRule rule = DefaultFlowRule.builder()
508 .forDevice(deviceId)
509 .withSelector(selector.build())
510 .withTreatment(treatment.build())
511 .withPriority(DEFAULT_PRIORITY)
512 .fromApp(applicationId)
513 .makePermanent()
514 .forTable(VLAN_TABLE).build();
515 rules.add(rule);
516 }
517 return rules;
518 }
519
520 /**
521 * Allows routed packets with correct destination MAC to be directed
522 * to unicast-IP routing table or MPLS forwarding table.
523 * XXX need to add rule for multicast routing.
524 *
525 * @param portCriterion port on device for which this filter is programmed
526 * @param ethCriterion dstMac of device for which is filter is programmed
527 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700528 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700529 * @param applicationId for application programming this filter
530 * @return list of FlowRule for port-vlan filters
531
532 */
533 protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
534 EthCriterion ethCriterion,
535 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700536 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700537 ApplicationId applicationId) {
538 //handling untagged packets via assigned VLAN
539 if (vidCriterion.vlanId() == VlanId.NONE) {
Saurav Das0e99e2b2015-10-28 12:39:42 -0700540 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
Saurav Das822c4e22015-10-23 10:51:11 -0700541 }
542 // ofdpa cannot match on ALL portnumber, so we need to use separate
543 // rules for each port.
544 List<PortNumber> portnums = new ArrayList<PortNumber>();
545 if (portCriterion.port() == PortNumber.ALL) {
546 for (Port port : deviceService.getPorts(deviceId)) {
547 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
548 portnums.add(port.number());
549 }
550 }
551 } else {
552 portnums.add(portCriterion.port());
553 }
554
555 List<FlowRule> rules = new ArrayList<FlowRule>();
556 for (PortNumber pnum : portnums) {
557 // for unicast IP packets
558 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
559 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
560 selector.matchInPort(pnum);
561 selector.matchVlanId(vidCriterion.vlanId());
562 selector.matchEthType(Ethernet.TYPE_IPV4);
563 selector.matchEthDst(ethCriterion.mac());
564 treatment.transition(UNICAST_ROUTING_TABLE);
565 FlowRule rule = DefaultFlowRule.builder()
566 .forDevice(deviceId)
567 .withSelector(selector.build())
568 .withTreatment(treatment.build())
569 .withPriority(DEFAULT_PRIORITY)
570 .fromApp(applicationId)
571 .makePermanent()
572 .forTable(TMAC_TABLE).build();
573 rules.add(rule);
574 //for MPLS packets
575 selector = DefaultTrafficSelector.builder();
576 treatment = DefaultTrafficTreatment.builder();
577 selector.matchInPort(pnum);
578 selector.matchVlanId(vidCriterion.vlanId());
579 selector.matchEthType(Ethernet.MPLS_UNICAST);
580 selector.matchEthDst(ethCriterion.mac());
581 treatment.transition(MPLS_TABLE_0);
582 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 }
592 return rules;
593 }
594
595 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
596 switch (fwd.flag()) {
597 case SPECIFIC:
598 return processSpecific(fwd);
599 case VERSATILE:
600 return processVersatile(fwd);
601 default:
602 fail(fwd, ObjectiveError.UNKNOWN);
603 log.warn("Unknown forwarding flag {}", fwd.flag());
604 }
605 return Collections.emptySet();
606 }
607
608 /**
609 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
610 * ACL table.
611 * @param fwd the forwarding objective of type 'versatile'
612 * @return a collection of flow rules to be sent to the switch. An empty
613 * collection may be returned if there is a problem in processing
614 * the flow rule
615 */
616 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
617 log.info("Processing versatile forwarding objective");
618 TrafficSelector selector = fwd.selector();
619
620 EthTypeCriterion ethType =
621 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
622 if (ethType == null) {
623 log.error("Versatile forwarding objective must include ethType");
624 fail(fwd, ObjectiveError.BADPARAMS);
625 return Collections.emptySet();
626 }
627 if (fwd.nextId() == null && fwd.treatment() == null) {
628 log.error("Forwarding objective {} from {} must contain "
629 + "nextId or Treatment", fwd.selector(), fwd.appId());
630 return Collections.emptySet();
631 }
632 // XXX driver does not currently do type checking as per Tables 65-67 in
633 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
634 if (fwd.treatment() != null &&
635 fwd.treatment().allInstructions().size() == 1 &&
636 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
637 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
638 if (o.port() == PortNumber.CONTROLLER) {
639 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
640 .fromApp(fwd.appId())
641 .withPriority(fwd.priority())
642 .forDevice(deviceId)
643 .withSelector(fwd.selector())
644 .withTreatment(fwd.treatment())
645 .makePermanent()
646 .forTable(ACL_TABLE);
647 return Collections.singletonList(ruleBuilder.build());
648 } else {
649 log.warn("Only allowed treatments in versatile forwarding "
650 + "objectives are punts to the controller");
651 return Collections.emptySet();
652 }
653 }
654
655 if (fwd.nextId() != null) {
656 // XXX overide case
657 log.warn("versatile objective --> next Id not yet implemeted");
658 }
659 return Collections.emptySet();
660 }
661
662 /**
663 * In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table
664 * (unicast or multicast) or the L2 table (mac + vlan).
665 *
666 * @param fwd the forwarding objective of type 'specific'
667 * @return a collection of flow rules. Typically there will be only one
668 * for this type of forwarding objective. An empty set may be
669 * returned if there is an issue in processing the objective.
670 */
671 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
672 log.debug("Processing specific forwarding objective");
673 TrafficSelector selector = fwd.selector();
674 EthTypeCriterion ethType =
675 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
676 // XXX currently supporting only the L3 unicast table
677 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
678 fail(fwd, ObjectiveError.UNSUPPORTED);
679 return Collections.emptySet();
680 }
681
682 TrafficSelector filteredSelector =
683 DefaultTrafficSelector.builder()
684 .matchEthType(Ethernet.TYPE_IPV4)
685 .matchIPDst(
686 ((IPCriterion)
687 selector.getCriterion(Criterion.Type.IPV4_DST)).ip())
688 .build();
689
690 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
691
692 if (fwd.nextId() != null) {
693 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
694 List<GroupKey> gkeys = appKryo.deserialize(next.data());
695 Group group = groupService.getGroup(deviceId, gkeys.get(0));
696 if (group == null) {
697 log.warn("The group left!");
698 fail(fwd, ObjectiveError.GROUPMISSING);
699 return Collections.emptySet();
700 }
701 tb.deferred().group(group.id());
702 }
703 tb.transition(ACL_TABLE);
704 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
705 .fromApp(fwd.appId())
706 .withPriority(fwd.priority())
707 .forDevice(deviceId)
708 .withSelector(filteredSelector)
709 .withTreatment(tb.build());
710
711 if (fwd.permanent()) {
712 ruleBuilder.makePermanent();
713 } else {
714 ruleBuilder.makeTemporary(fwd.timeout());
715 }
716
717 ruleBuilder.forTable(UNICAST_ROUTING_TABLE);
718 return Collections.singletonList(ruleBuilder.build());
719 }
720
721 private void pass(Objective obj) {
722 if (obj.context().isPresent()) {
723 obj.context().get().onSuccess(obj);
724 }
725 }
726
727 private void fail(Objective obj, ObjectiveError error) {
728 if (obj.context().isPresent()) {
729 obj.context().get().onError(obj, error);
730 }
731 }
732
733 //////////////////////////////////////
734 // Group handling
735 //////////////////////////////////////
736
Saurav Das4f980082015-11-05 13:39:15 -0800737 private void addGroup(NextObjective nextObjective) {
738 switch (nextObjective.type()) {
739 case SIMPLE:
740 Collection<TrafficTreatment> treatments = nextObjective.next();
741 if (treatments.size() != 1) {
742 log.error("Next Objectives of type Simple should only have a "
743 + "single Traffic Treatment. Next Objective Id:{}",
744 nextObjective.id());
745 fail(nextObjective, ObjectiveError.BADPARAMS);
746 return;
747 }
748 processSimpleNextObjective(nextObjective);
749 break;
750 case BROADCAST:
751 processBroadcastNextObjective(nextObjective);
752 break;
753 case HASHED:
754 processHashedNextObjective(nextObjective);
755 break;
756 case FAILOVER:
757 fail(nextObjective, ObjectiveError.UNSUPPORTED);
758 log.warn("Unsupported next objective type {}", nextObjective.type());
759 break;
760 default:
761 fail(nextObjective, ObjectiveError.UNKNOWN);
762 log.warn("Unknown next objective type {}", nextObjective.type());
763 }
764 }
765
Saurav Das822c4e22015-10-23 10:51:11 -0700766 /**
767 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
768 * a chain of groups, namely an L3 Unicast Group that points to an L2 Interface
Saurav Das4f980082015-11-05 13:39:15 -0800769 * Group which in-turn points to an output port. The Next Objective passed
Saurav Das822c4e22015-10-23 10:51:11 -0700770 * in by the application has to be broken up into a group chain
771 * to satisfy this TTP.
772 *
773 * @param nextObj the nextObjective of type SIMPLE
774 */
775 private void processSimpleNextObjective(NextObjective nextObj) {
776 // break up simple next objective to GroupChain objects
777 TrafficTreatment treatment = nextObj.next().iterator().next();
778 // for the l2interface group, get vlan and port info
779 // for the l3unicast group, get the src/dst mac and vlan info
780 TrafficTreatment.Builder l3utt = DefaultTrafficTreatment.builder();
781 TrafficTreatment.Builder l2itt = DefaultTrafficTreatment.builder();
782 VlanId vlanid = null;
783 long portNum = 0;
784 for (Instruction ins : treatment.allInstructions()) {
785 if (ins.type() == Instruction.Type.L2MODIFICATION) {
786 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
787 switch (l2ins.subtype()) {
788 case ETH_DST:
789 l3utt.setEthDst(((ModEtherInstruction) l2ins).mac());
790 break;
791 case ETH_SRC:
792 l3utt.setEthSrc(((ModEtherInstruction) l2ins).mac());
793 break;
794 case VLAN_ID:
795 vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
796 l3utt.setVlanId(vlanid);
797 break;
798 case DEC_MPLS_TTL:
799 case MPLS_LABEL:
800 case MPLS_POP:
801 case MPLS_PUSH:
802 case VLAN_PCP:
803 case VLAN_POP:
804 case VLAN_PUSH:
805 default:
806 break;
807 }
808 } else if (ins.type() == Instruction.Type.OUTPUT) {
809 portNum = ((OutputInstruction) ins).port().toLong();
810 l2itt.add(ins);
811 } else {
812 log.warn("Driver does not handle this type of TrafficTreatment"
813 + " instruction in nextObjectives: {}", ins.type());
814 }
815 }
816
817 // assemble information for ofdpa l2interface group
818 int l2gk = nextObj.id() | GROUP1MASK;
819 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
820 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
821
822 // assemble information for ofdpa l3unicast group
823 int l3gk = nextObj.id() | GROUP0MASK;
824 final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
825 Integer l3groupId = L3UNICASTMASK | (int) portNum;
826 l3utt.group(new DefaultGroupId(l2groupId));
827 GroupChainElem gce = new GroupChainElem(l3groupkey, l3groupId,
Saurav Das4f980082015-11-05 13:39:15 -0800828 GroupDescription.Type.INDIRECT,
829 Collections.singletonList(l3utt.build()),
830 nextObj.appId(), 1);
Saurav Das822c4e22015-10-23 10:51:11 -0700831
832 // create object for local and distributed storage
833 List<GroupKey> gkeys = new ArrayList<GroupKey>();
834 gkeys.add(l3groupkey); // group0 in chain
835 gkeys.add(l2groupkey); // group1 in chain
836 OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj);
837
838 // store l2groupkey with the groupChainElem for the l3group that depends on it
839 pendingGroups.put(l2groupkey, gce);
840
841 // store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it
842 pendingNextObjectives.put(l3groupkey, ofdpaGrp);
843
844 // create group description for the ofdpa l2interfacegroup and send to groupservice
845 GroupBucket bucket =
846 DefaultGroupBucket.createIndirectGroupBucket(l2itt.build());
847 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
848 GroupDescription.Type.INDIRECT,
849 new GroupBuckets(Collections.singletonList(bucket)),
850 l2groupkey,
851 l2groupId,
852 nextObj.appId());
853 groupService.addGroup(groupDescription);
854 }
855
856 /**
Saurav Das4f980082015-11-05 13:39:15 -0800857 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
858 * a chain of groups. The Next Objective passed in by the application
859 * has to be broken up into a group chain comprising of an
860 * L2 Flood group whose buckets point to L2 Interface groups.
861 *
862 * @param nextObj the nextObjective of type BROADCAST
863 */
864 private void processBroadcastNextObjective(NextObjective nextObj) {
865 // break up broadcast next objective to multiple groups
866 Collection<TrafficTreatment> buckets = nextObj.next();
867
868 // each treatment is converted to an L2 interface group
869 int indicator = 0;
870 VlanId vlanid = null;
871 List<GroupInfo> groupInfoCollection = new ArrayList<>();
872 for (TrafficTreatment treatment : buckets) {
873 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
874 PortNumber portNum = null;
875 // ensure that the only allowed treatments are pop-vlan and output
876 for (Instruction ins : treatment.allInstructions()) {
877 if (ins.type() == Instruction.Type.L2MODIFICATION) {
878 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
879 switch (l2ins.subtype()) {
880 case VLAN_POP:
881 newTreatment.add(l2ins);
882 break;
883 default:
884 log.debug("action {} not permitted for broadcast nextObj",
885 l2ins.subtype());
886 break;
887 }
888 } else if (ins.type() == Instruction.Type.OUTPUT) {
889 portNum = ((OutputInstruction) ins).port();
890 newTreatment.add(ins);
891 } else {
892 log.debug("TrafficTreatment of type {} not permitted in "
893 + " broadcast nextObjective", ins.type());
894 }
895 }
896
897 // also ensure that all ports are in the same vlan
898 VlanId thisvlanid = port2Vlan.get(portNum);
899 if (vlanid == null) {
900 vlanid = thisvlanid;
901 } else {
902 if (!vlanid.equals(thisvlanid)) {
903 log.error("Driver requires all ports in a broadcast nextObj "
904 + "to be in the same vlan. Different vlans found "
905 + "{} and {}. Aborting group creation", vlanid, thisvlanid);
906 return;
907 }
908 }
909
910 // assemble info for all l2 interface groups
911 indicator += GROUP1MASK;
912 int l2gk = nextObj.id() | indicator;
913 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
914 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) |
915 (int) portNum.toLong();
916 GroupBucket newbucket =
917 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
918
919 // store the info needed to create this group
920 groupInfoCollection.add(new GroupInfo(l2groupId, l2groupkey, newbucket));
921 }
922
923 // assemble info for l2 flood group
924 int l2floodgk = nextObj.id() | GROUP0MASK;
925 final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk));
926 Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id();
927 // collection of treatment with groupids of l2 interface groups
928 List<TrafficTreatment> floodtt = new ArrayList<>();
929 for (GroupInfo gi : groupInfoCollection) {
930 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
931 ttb.group(new DefaultGroupId(gi.groupId));
932 floodtt.add(ttb.build());
933 }
934 GroupChainElem gce = new GroupChainElem(l2floodgroupkey, l2floodgroupId,
935 GroupDescription.Type.ALL,
936 floodtt,
937 nextObj.appId(),
938 groupInfoCollection.size());
939
940 // create objects for local and distributed storage
941 List<GroupKey> gkeys = new ArrayList<GroupKey>();
942 gkeys.add(l2floodgroupkey); // group0 in chain
943 OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj);
944
945 // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective
946 // that depends on it
947 pendingNextObjectives.put(l2floodgroupkey, ofdpaGrp);
948
949 for (GroupInfo gi : groupInfoCollection) {
950 // store all l2groupkeys with the groupChainElem for the l2floodgroup
951 // that depends on it
952 pendingGroups.put(gi.groupKey, gce);
953
954 // create and send groups for all l2 interface groups
955 GroupDescription groupDescription =
956 new DefaultGroupDescription(
957 deviceId,
958 GroupDescription.Type.INDIRECT,
959 new GroupBuckets(Collections.singletonList(gi.groupBucket)),
960 gi.groupKey,
961 gi.groupId,
962 nextObj.appId());
963 groupService.addGroup(groupDescription);
964 }
965 }
966
967 private class GroupInfo {
968 private Integer groupId;
969 private GroupKey groupKey;
970 private GroupBucket groupBucket;
971
972 GroupInfo(Integer groupId, GroupKey groupKey, GroupBucket groupBucket) {
973 this.groupBucket = groupBucket;
974 this.groupId = groupId;
975 this.groupKey = groupKey;
976 }
977 }
978
979 private void processHashedNextObjective(NextObjective nextObj) {
980 // TODO Auto-generated method stub
981 }
982
983 private void addBucketToGroup(NextObjective nextObjective) {
984 // TODO Auto-generated method stub
985 }
986
987 private void removeBucketFromGroup(NextObjective nextObjective) {
988 // TODO Auto-generated method stub
989 }
990
991 private void removeGroup(NextObjective nextObjective) {
992 // TODO Auto-generated method stub
993 }
994
995 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700996 * Processes next element of a group chain. Assumption is that if this
997 * group points to another group, the latter has already been created
998 * and this driver has received notification for it. A second assumption is
999 * that if there is another group waiting for this group then the appropriate
1000 * stores already have the information to act upon the notification for the
1001 * creating of this group.
Saurav Das4f980082015-11-05 13:39:15 -08001002 * <p>
1003 * The processing of the GroupChainElement depends on the number of groups
1004 * this element is waiting on. For all group types other than SIMPLE, a
1005 * GroupChainElement could be waiting on multiple groups.
Saurav Das822c4e22015-10-23 10:51:11 -07001006 *
1007 * @param gce the group chain element to be processed next
1008 */
1009 private void processGroupChain(GroupChainElem gce) {
Saurav Das4f980082015-11-05 13:39:15 -08001010 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
1011 if (waitOnGroups != 0) {
1012 log.debug("GCE: {} waiting on {} groups. Not processing yet",
1013 gce, waitOnGroups);
1014 return;
1015 }
1016 List<GroupBucket> buckets = new ArrayList<>();
1017 switch (gce.groupType) {
1018 case INDIRECT:
1019 GroupBucket ibucket = DefaultGroupBucket
1020 .createIndirectGroupBucket(gce.bucketActions.iterator().next());
1021 buckets.add(ibucket);
1022 break;
1023 case ALL:
1024 for (TrafficTreatment tt : gce.bucketActions) {
1025 GroupBucket abucket = DefaultGroupBucket
1026 .createAllGroupBucket(tt);
1027 buckets.add(abucket);
1028 }
1029 break;
1030 case SELECT:
1031 for (TrafficTreatment tt : gce.bucketActions) {
1032 GroupBucket sbucket = DefaultGroupBucket
1033 .createSelectGroupBucket(tt);
1034 buckets.add(sbucket);
1035 }
1036 break;
1037 case FAILOVER:
1038 default:
1039 log.error("Unknown or unimplemented GroupChainElem {}", gce);
1040 }
Saurav Das822c4e22015-10-23 10:51:11 -07001041
Saurav Das4f980082015-11-05 13:39:15 -08001042 if (buckets.size() > 0) {
1043 GroupDescription groupDesc = new DefaultGroupDescription(
1044 deviceId, gce.groupType,
1045 new GroupBuckets(buckets),
1046 gce.gkey,
1047 gce.givenGroupId,
1048 gce.appId);
1049 groupService.addGroup(groupDesc);
1050 }
1051 }
Saurav Das822c4e22015-10-23 10:51:11 -07001052
1053 private class GroupChecker implements Runnable {
1054 @Override
1055 public void run() {
1056 Set<GroupKey> keys = pendingGroups.keySet().stream()
1057 .filter(key -> groupService.getGroup(deviceId, key) != null)
1058 .collect(Collectors.toSet());
1059 Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream()
1060 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
1061 .collect(Collectors.toSet());
1062 keys.addAll(otherkeys);
1063
1064 keys.stream().forEach(key -> {
1065 //first check for group chain
1066 GroupChainElem gce = pendingGroups.remove(key);
1067 if (gce != null) {
1068 log.info("Group service processed group key {}. Processing next "
1069 + "group in group chain with group key {}",
1070 appKryo.deserialize(key.key()),
Saurav Das4f980082015-11-05 13:39:15 -08001071 appKryo.deserialize(gce.gkey.key()));
Saurav Das822c4e22015-10-23 10:51:11 -07001072 processGroupChain(gce);
1073 } else {
1074 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
1075 log.info("Group service processed group key {}. Done implementing "
1076 + "next objective: {}", appKryo.deserialize(key.key()),
1077 obj.nextObjective().id());
1078 if (obj != null) {
1079 pass(obj.nextObjective());
1080 pendingNextObjectives.invalidate(key);
1081 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
1082 }
1083 }
1084 });
1085 }
1086 }
1087
1088 private class InnerGroupListener implements GroupListener {
1089 @Override
1090 public void event(GroupEvent event) {
1091 log.debug("received group event of type {}", event.type());
1092 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
1093 GroupKey key = event.subject().appCookie();
1094 // first check for group chain
1095 GroupChainElem gce = pendingGroups.remove(key);
1096 if (gce != null) {
1097 log.info("group ADDED with group key {} .. "
1098 + "Processing next group in group chain with group key {}",
1099 appKryo.deserialize(key.key()),
Saurav Das4f980082015-11-05 13:39:15 -08001100 appKryo.deserialize(gce.gkey.key()));
Saurav Das822c4e22015-10-23 10:51:11 -07001101 processGroupChain(gce);
1102 } else {
1103 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
1104 if (obj != null) {
1105 log.info("group ADDED with key {}.. Done implementing next "
1106 + "objective: {}",
1107 appKryo.deserialize(key.key()), obj.nextObjective().id());
1108 pass(obj.nextObjective());
1109 pendingNextObjectives.invalidate(key);
1110 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
1111 }
1112 }
1113 }
1114 }
1115 }
1116
1117 /**
1118 * Represents a group-chain that implements a Next-Objective from
1119 * the application. Includes information about the next objective Id, and the
1120 * group keys for the groups in the group chain. The chain is expected to
1121 * look like group0 --> group 1 --> outPort. Information about the groups
1122 * themselves can be fetched from the Group Service using the group keys from
1123 * objects instantiating this class.
Saurav Das4f980082015-11-05 13:39:15 -08001124 *
1125 * XXX Revisit this - since the forwarding objective only ever needs the
1126 * groupkey of the top-level group in the group chain, why store a series
1127 * of groupkeys. Also the group-chain list only works for 1-to-1 chaining,
1128 * not for 1-to-many chaining.
Saurav Das822c4e22015-10-23 10:51:11 -07001129 */
1130 private class OfdpaGroupChain implements NextGroup {
1131 private final NextObjective nextObj;
1132 private final List<GroupKey> gkeys;
1133
1134 /** expected group chain: group0 --> group1 --> port. */
1135 public OfdpaGroupChain(List<GroupKey> gkeys, NextObjective nextObj) {
1136 this.gkeys = gkeys;
1137 this.nextObj = nextObj;
1138 }
1139
1140 @SuppressWarnings("unused")
1141 public List<GroupKey> groupKeys() {
1142 return gkeys;
1143 }
1144
1145 public NextObjective nextObjective() {
1146 return nextObj;
1147 }
1148
1149 @Override
1150 public byte[] data() {
1151 return appKryo.serialize(gkeys);
1152 }
1153
1154 }
1155
1156 /**
1157 * Represents a group element that is part of a chain of groups.
1158 * Stores enough information to create a Group Description to add the group
1159 * to the switch by requesting the Group Service. Objects instantiating this
1160 * class are meant to be temporary and live as long as it is needed to wait for
1161 * preceding groups in the group chain to be created.
1162 */
1163 private class GroupChainElem {
Saurav Das4f980082015-11-05 13:39:15 -08001164 private Collection<TrafficTreatment> bucketActions;
Saurav Das822c4e22015-10-23 10:51:11 -07001165 private Integer givenGroupId;
Saurav Das4f980082015-11-05 13:39:15 -08001166 private GroupDescription.Type groupType;
Saurav Das822c4e22015-10-23 10:51:11 -07001167 private GroupKey gkey;
1168 private ApplicationId appId;
Saurav Das4f980082015-11-05 13:39:15 -08001169 private AtomicInteger waitOnGroups;
Saurav Das822c4e22015-10-23 10:51:11 -07001170
Saurav Das4f980082015-11-05 13:39:15 -08001171 GroupChainElem(GroupKey gkey, Integer givenGroupId,
1172 GroupDescription.Type groupType,
1173 Collection<TrafficTreatment> tr, ApplicationId appId,
1174 int waitOnGroups) {
Saurav Das822c4e22015-10-23 10:51:11 -07001175 this.bucketActions = tr;
1176 this.givenGroupId = givenGroupId;
Saurav Das4f980082015-11-05 13:39:15 -08001177 this.groupType = groupType;
Saurav Das822c4e22015-10-23 10:51:11 -07001178 this.gkey = gkey;
1179 this.appId = appId;
Saurav Das4f980082015-11-05 13:39:15 -08001180 this.waitOnGroups = new AtomicInteger(waitOnGroups);
Saurav Das822c4e22015-10-23 10:51:11 -07001181 }
1182
Saurav Das4f980082015-11-05 13:39:15 -08001183 /**
1184 * This methods atomically decrements the counter for the number of
1185 * groups this GroupChainElement is waiting on, for notifications from
1186 * the Group Service. When this method returns a value of 0, this
1187 * GroupChainElement is ready to be processed.
1188 *
1189 * @return integer indication of the number of notifications being waited on
1190 */
1191 int decrementAndGetGroupsWaitedOn() {
1192 return waitOnGroups.decrementAndGet();
Saurav Das822c4e22015-10-23 10:51:11 -07001193 }
1194
Saurav Das4f980082015-11-05 13:39:15 -08001195 @Override
1196 public String toString() {
1197 return Integer.toHexString(givenGroupId);
Saurav Das822c4e22015-10-23 10:51:11 -07001198 }
1199
1200 }
1201
1202 //////////////////////////////////////
1203 // Test code to be used for future
1204 // static-flow-pusher app
1205 //////////////////////////////////////
1206
1207 public void processStaticFlows() {
1208 //processPortTable();
1209 processGroupTable();
1210 processVlanTable();
1211 processTmacTable();
1212 processIpTable();
1213 //processMcastTable();
1214 //processBridgingTable();
1215 processAclTable();
1216 sendPackets();
1217 processMplsTable();
1218 }
1219
1220 protected void processGroupTable() {
1221 TrafficTreatment.Builder act = DefaultTrafficTreatment.builder();
1222
1223 act.popVlan(); // to send out untagged packets
1224 act.setOutput(PortNumber.portNumber(24));
1225 GroupBucket bucket =
1226 DefaultGroupBucket.createIndirectGroupBucket(act.build());
1227 final GroupKey groupkey = new DefaultGroupKey(appKryo.serialize(500));
1228 Integer groupId = 0x00c80018; //l2 interface, vlan 200, port 24
1229 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
1230 GroupDescription.Type.INDIRECT,
1231 new GroupBuckets(Collections.singletonList(bucket)),
1232 groupkey,
1233 groupId,
1234 driverId);
1235 groupService.addGroup(groupDescription);
1236
1237 TrafficTreatment.Builder act2 = DefaultTrafficTreatment.builder();
1238 act2.setOutput(PortNumber.portNumber(40));
1239 GroupBucket bucket2 = DefaultGroupBucket.createIndirectGroupBucket(act2.build());
1240 final GroupKey groupkey2 = new DefaultGroupKey(appKryo.serialize(502));
1241 Integer groupId2 = 0x00c50028; //l2 interface, vlan 197, port 40
1242 GroupDescription groupDescription2 = new DefaultGroupDescription(deviceId,
1243 GroupDescription.Type.INDIRECT,
1244 new GroupBuckets(Collections.singletonList(bucket2)),
1245 groupkey2,
1246 groupId2,
1247 driverId);
1248 groupService.addGroup(groupDescription2);
1249
1250 while (groupService.getGroup(deviceId, groupkey2) == null) {
1251 try {
1252 Thread.sleep(500);
1253 } catch (InterruptedException e) {
1254 // TODO Auto-generated catch block
1255 e.printStackTrace();
1256 }
1257 }
1258
1259 //Now for L3 Unicast group
1260 TrafficTreatment.Builder act3 = DefaultTrafficTreatment.builder();
1261 act3.setEthDst(MacAddress.valueOf(0x2020));
1262 act3.setEthSrc(MacAddress.valueOf(0x1010));
1263 act3.setVlanId(VlanId.vlanId((short) 200));
1264 act3.group(new DefaultGroupId(0x00c80018)); // point to L2 interface
1265 // MPLS interface group - does not work for popping single label
1266 //Integer secGroupId = MPLSINTERFACEMASK | 38; // 0x90000026
1267 Integer groupId3 = L3UNICASTMASK | 1; // 0x20000001
1268 GroupBucket bucket3 =
1269 DefaultGroupBucket.createIndirectGroupBucket(act3.build());
1270 final GroupKey groupkey3 = new DefaultGroupKey(appKryo.serialize(503));
1271 GroupDescription groupDescription3 = new DefaultGroupDescription(deviceId,
1272 GroupDescription.Type.INDIRECT,
1273 new GroupBuckets(Collections.singletonList(bucket3)),
1274 groupkey3,
1275 groupId3,
1276 driverId);
1277 groupService.addGroup(groupDescription3);
1278
1279 //Another L3 Unicast group
1280 TrafficTreatment.Builder act4 = DefaultTrafficTreatment.builder();
1281 act4.setEthDst(MacAddress.valueOf(0x3030));
1282 act4.setEthSrc(MacAddress.valueOf(0x1010));
1283 act4.setVlanId(VlanId.vlanId((short) 197));
1284 act4.group(new DefaultGroupId(0x00c50028)); // point to L2 interface
1285 Integer groupId4 = L3UNICASTMASK | 2; // 0x20000002
1286 GroupBucket bucket4 =
1287 DefaultGroupBucket.createIndirectGroupBucket(act4.build());
1288 final GroupKey groupkey4 = new DefaultGroupKey(appKryo.serialize(504));
1289 GroupDescription groupDescription4 = new DefaultGroupDescription(deviceId,
1290 GroupDescription.Type.INDIRECT,
1291 new GroupBuckets(Collections.singletonList(bucket4)),
1292 groupkey4,
1293 groupId4,
1294 driverId);
1295 groupService.addGroup(groupDescription4);
1296
1297 while (groupService.getGroup(deviceId, groupkey4) == null) {
1298 try {
1299 Thread.sleep(500);
1300 } catch (InterruptedException e) {
1301 // TODO Auto-generated catch block
1302 e.printStackTrace();
1303 }
1304 }
1305
1306 // L3 ecmp group
1307 TrafficTreatment.Builder act5 = DefaultTrafficTreatment.builder();
1308 act5.group(new DefaultGroupId(0x20000001));
1309 TrafficTreatment.Builder act6 = DefaultTrafficTreatment.builder();
1310 act6.group(new DefaultGroupId(0x20000002));
1311 GroupBucket buckete1 =
1312 DefaultGroupBucket.createSelectGroupBucket(act5.build());
1313 GroupBucket buckete2 =
1314 DefaultGroupBucket.createSelectGroupBucket(act6.build());
1315 List<GroupBucket> bktlist = new ArrayList<GroupBucket>();
1316 bktlist.add(buckete1);
1317 bktlist.add(buckete2);
1318 final GroupKey groupkey5 = new DefaultGroupKey(appKryo.serialize(505));
1319 Integer groupId5 = L3ECMPMASK | 5; // 0x70000005
1320 GroupDescription groupDescription5 = new DefaultGroupDescription(deviceId,
1321 GroupDescription.Type.SELECT,
1322 new GroupBuckets(bktlist),
1323 groupkey5,
1324 groupId5,
1325 driverId);
1326 groupService.addGroup(groupDescription5);
1327
1328
1329 }
1330
1331 @SuppressWarnings("deprecation")
1332 protected void processMplsTable() {
1333 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1334 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
1335 selector.matchEthType(Ethernet.MPLS_UNICAST);
1336 selector.matchMplsLabel(MplsLabel.mplsLabel(0xff)); //255
1337 selector.matchMplsBos(true);
1338 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1339 treatment.decMplsTtl(); // nw_ttl does not work
1340 treatment.copyTtlIn();
1341 treatment.popMpls(Ethernet.TYPE_IPV4);
1342 treatment.deferred().group(new DefaultGroupId(0x20000001)); // point to L3 Unicast
1343 //treatment.deferred().group(new DefaultGroupId(0x70000005)); // point to L3 ECMP
1344 treatment.transition(ACL_TABLE);
1345 FlowRule test = DefaultFlowRule.builder().forDevice(deviceId)
1346 .withSelector(selector.build()).withTreatment(treatment.build())
1347 .withPriority(DEFAULT_PRIORITY).fromApp(driverId).makePermanent()
1348 .forTable(24).build();
1349 ops = ops.add(test);
1350
1351 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1352 @Override
1353 public void onSuccess(FlowRuleOperations ops) {
1354 log.info("Initialized mpls table");
1355 }
1356
1357 @Override
1358 public void onError(FlowRuleOperations ops) {
1359 log.info("Failed to initialize mpls table");
1360 }
1361 }));
1362
1363 }
1364
1365 protected void processPortTable() {
1366 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1367 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
1368 selector.matchInPort(PortNumber.portNumber(0)); // should be maskable?
1369 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1370 treatment.transition(VLAN_TABLE);
1371 FlowRule tmisse = DefaultFlowRule.builder()
1372 .forDevice(deviceId)
1373 .withSelector(selector.build())
1374 .withTreatment(treatment.build())
1375 .withPriority(LOWEST_PRIORITY)
1376 .fromApp(driverId)
1377 .makePermanent()
1378 .forTable(PORT_TABLE).build();
1379 ops = ops.add(tmisse);
1380
1381 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1382 @Override
1383 public void onSuccess(FlowRuleOperations ops) {
1384 log.info("Initialized port table");
1385 }
1386
1387 @Override
1388 public void onError(FlowRuleOperations ops) {
1389 log.info("Failed to initialize port table");
1390 }
1391 }));
1392
1393 }
1394
1395 private void processVlanTable() {
1396 // Table miss entry is not required as ofdpa default is to drop
1397 // In OF terms, the absence of a t.m.e. also implies drop
1398 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1399 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
1400 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1401 selector.matchInPort(PortNumber.portNumber(12));
1402 selector.matchVlanId(VlanId.vlanId((short) 100));
1403 treatment.transition(TMAC_TABLE);
1404 FlowRule rule = DefaultFlowRule.builder()
1405 .forDevice(deviceId)
1406 .withSelector(selector.build())
1407 .withTreatment(treatment.build())
1408 .withPriority(DEFAULT_PRIORITY)
1409 .fromApp(driverId)
1410 .makePermanent()
1411 .forTable(VLAN_TABLE).build();
1412 ops = ops.add(rule);
1413 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1414 @Override
1415 public void onSuccess(FlowRuleOperations ops) {
1416 log.info("Initialized vlan table");
1417 }
1418
1419 @Override
1420 public void onError(FlowRuleOperations ops) {
1421 log.info("Failed to initialize vlan table");
1422 }
1423 }));
1424 }
1425
1426 protected void processTmacTable() {
1427 //table miss entry
1428 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1429 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
1430 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1431 selector.matchInPort(PortNumber.portNumber(12));
1432 selector.matchVlanId(VlanId.vlanId((short) 100));
1433 selector.matchEthType(Ethernet.TYPE_IPV4);
1434 selector.matchEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
1435 treatment.transition(UNICAST_ROUTING_TABLE);
1436 FlowRule rule = DefaultFlowRule.builder()
1437 .forDevice(deviceId)
1438 .withSelector(selector.build())
1439 .withTreatment(treatment.build())
1440 .withPriority(DEFAULT_PRIORITY)
1441 .fromApp(driverId)
1442 .makePermanent()
1443 .forTable(TMAC_TABLE).build();
1444 ops = ops.add(rule);
1445
1446 selector.matchEthType(Ethernet.MPLS_UNICAST);
1447 treatment.transition(MPLS_TABLE_0);
1448 FlowRule rulempls = DefaultFlowRule.builder()
1449 .forDevice(deviceId)
1450 .withSelector(selector.build())
1451 .withTreatment(treatment.build())
1452 .withPriority(DEFAULT_PRIORITY)
1453 .fromApp(driverId)
1454 .makePermanent()
1455 .forTable(TMAC_TABLE).build();
1456 ops = ops.add(rulempls);
1457
1458 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1459 @Override
1460 public void onSuccess(FlowRuleOperations ops) {
1461 log.info("Initialized tmac table");
1462 }
1463
1464 @Override
1465 public void onError(FlowRuleOperations ops) {
1466 log.info("Failed to initialize tmac table");
1467 }
1468 }));
1469 }
1470
1471 protected void processIpTable() {
1472 //table miss entry
1473 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1474 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
1475 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1476 selector.matchEthType(Ethernet.TYPE_IPV4);
1477 selector.matchIPDst(IpPrefix.valueOf("2.0.0.0/16"));
1478 treatment.deferred().group(new DefaultGroupId(0x20000001));
1479 treatment.transition(ACL_TABLE);
1480 FlowRule rule = DefaultFlowRule.builder()
1481 .forDevice(deviceId)
1482 .withSelector(selector.build())
1483 .withTreatment(treatment.build())
1484 .withPriority(30000)
1485 .fromApp(driverId)
1486 .makePermanent()
1487 .forTable(UNICAST_ROUTING_TABLE).build();
1488 ops = ops.add(rule);
1489 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1490 @Override
1491 public void onSuccess(FlowRuleOperations ops) {
1492 log.info("Initialized IP table");
1493 }
1494
1495 @Override
1496 public void onError(FlowRuleOperations ops) {
1497 log.info("Failed to initialize unicast IP table");
1498 }
1499 }));
1500 }
1501
1502 protected void processAclTable() {
1503 //table miss entry
1504 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1505 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
1506 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1507 selector.matchEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
1508 treatment.deferred().group(new DefaultGroupId(0x20000001));
1509 FlowRule rule = DefaultFlowRule.builder()
1510 .forDevice(deviceId)
1511 .withSelector(selector.build())
1512 .withTreatment(treatment.build())
1513 .withPriority(60000)
1514 .fromApp(driverId)
1515 .makePermanent()
1516 .forTable(ACL_TABLE).build();
1517 ops = ops.add(rule);
1518 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1519 @Override
1520 public void onSuccess(FlowRuleOperations ops) {
1521 log.info("Initialized Acl table");
1522 }
1523
1524 @Override
1525 public void onError(FlowRuleOperations ops) {
1526 log.info("Failed to initialize Acl table");
1527 }
1528 }));
1529 }
1530
1531 private void sendPackets() {
1532 Ethernet eth = new Ethernet();
1533 eth.setDestinationMACAddress("00:00:00:00:00:02");
1534 eth.setSourceMACAddress("00:00:00:11:22:33");
1535 eth.setVlanID((short) 100);
1536 eth.setEtherType(Ethernet.MPLS_UNICAST);
1537 MPLS mplsPkt = new MPLS();
1538 mplsPkt.setLabel(255);
1539 mplsPkt.setTtl((byte) 5);
1540
1541 IPv4 ipv4 = new IPv4();
1542
1543 ipv4.setDestinationAddress("4.0.5.6");
1544 ipv4.setSourceAddress("1.0.2.3");
1545 ipv4.setTtl((byte) 64);
1546 ipv4.setChecksum((short) 0);
1547
1548 UDP udp = new UDP();
1549 udp.setDestinationPort(666);
1550 udp.setSourcePort(333);
1551 udp.setPayload(new Data(new byte[]{(byte) 1, (byte) 2}));
1552 udp.setChecksum((short) 0);
1553
1554 ipv4.setPayload(udp);
1555 mplsPkt.setPayload(ipv4);
1556 eth.setPayload(mplsPkt);
1557
1558 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1559 .setOutput(PortNumber.portNumber(24))
1560 .build();
1561 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
1562 treatment,
1563 ByteBuffer.wrap(eth.serialize()));
1564
1565
1566 Ethernet eth2 = new Ethernet();
1567 eth2.setDestinationMACAddress("00:00:00:00:00:02");
1568 eth2.setSourceMACAddress("00:00:00:11:22:33");
1569 eth2.setVlanID((short) 100);
1570 eth2.setEtherType(Ethernet.TYPE_IPV4);
1571
1572 IPv4 ipv42 = new IPv4();
1573 ipv42.setDestinationAddress("2.0.0.2");
1574 ipv42.setSourceAddress("1.0.9.9");
1575 ipv42.setTtl((byte) 64);
1576 ipv42.setChecksum((short) 0);
1577
1578 UDP udp2 = new UDP();
1579 udp2.setDestinationPort(999);
1580 udp2.setSourcePort(333);
1581 udp2.setPayload(new Data(new byte[]{(byte) 1, (byte) 2}));
1582 udp2.setChecksum((short) 0);
1583
1584 ipv42.setPayload(udp2);
1585 eth2.setPayload(ipv42);
1586
1587 TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
1588 .setOutput(PortNumber.portNumber(26))
1589 .build();
1590 OutboundPacket packet2 = new DefaultOutboundPacket(deviceId,
1591 treatment2,
1592 ByteBuffer.wrap(eth2.serialize()));
1593
1594
1595 log.info("Emitting packets now");
1596 packetService.emit(packet);
1597 packetService.emit(packet);
1598 packetService.emit(packet2);
1599 packetService.emit(packet);
1600 packetService.emit(packet);
1601 log.info("Done emitting packets");
1602 }
1603
1604 private class InternalPacketProcessor implements PacketProcessor {
1605
1606 @Override
1607 public void process(PacketContext context) {
1608
1609
1610 }
1611 }
1612
1613}