blob: 31a44447b180e3965b86501bf0b7d401ee067b73 [file] [log] [blame]
Saurav Das100e3b82015-04-30 11:12:10 -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.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.List;
25import java.util.Set;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.Executors;
28import java.util.concurrent.ScheduledExecutorService;
29import java.util.concurrent.TimeUnit;
30import java.util.stream.Collectors;
31
32import org.onlab.osgi.ServiceDirectory;
33import org.onlab.packet.Ethernet;
34import org.onlab.packet.VlanId;
35import org.onlab.util.KryoNamespace;
36import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.core.DefaultGroupId;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.PortNumber;
41import org.onosproject.net.behaviour.NextGroup;
42import org.onosproject.net.behaviour.Pipeliner;
43import org.onosproject.net.behaviour.PipelinerContext;
44import org.onosproject.net.driver.AbstractHandlerBehaviour;
45import org.onosproject.net.flow.DefaultFlowRule;
46import org.onosproject.net.flow.DefaultTrafficSelector;
47import org.onosproject.net.flow.DefaultTrafficTreatment;
48import org.onosproject.net.flow.FlowRule;
49import org.onosproject.net.flow.FlowRuleOperations;
50import org.onosproject.net.flow.FlowRuleOperationsContext;
51import org.onosproject.net.flow.FlowRuleService;
52import org.onosproject.net.flow.TrafficSelector;
53import org.onosproject.net.flow.TrafficTreatment;
54import org.onosproject.net.flow.criteria.Criteria;
55import org.onosproject.net.flow.criteria.Criterion;
56import org.onosproject.net.flow.criteria.EthCriterion;
57import org.onosproject.net.flow.criteria.EthTypeCriterion;
58import org.onosproject.net.flow.criteria.IPCriterion;
59import org.onosproject.net.flow.criteria.PortCriterion;
60import org.onosproject.net.flow.criteria.VlanIdCriterion;
61import org.onosproject.net.flow.instructions.Instruction;
62import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
63import org.onosproject.net.flow.instructions.L2ModificationInstruction;
64import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
65import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
66import org.onosproject.net.flowobjective.FilteringObjective;
67import org.onosproject.net.flowobjective.FlowObjectiveStore;
68import org.onosproject.net.flowobjective.ForwardingObjective;
69import org.onosproject.net.flowobjective.NextObjective;
70import org.onosproject.net.flowobjective.Objective;
71import org.onosproject.net.flowobjective.ObjectiveError;
72import org.onosproject.net.group.DefaultGroupBucket;
73import org.onosproject.net.group.DefaultGroupDescription;
74import org.onosproject.net.group.DefaultGroupKey;
75import org.onosproject.net.group.Group;
76import org.onosproject.net.group.GroupBucket;
77import org.onosproject.net.group.GroupBuckets;
78import org.onosproject.net.group.GroupDescription;
79import org.onosproject.net.group.GroupEvent;
80import org.onosproject.net.group.GroupKey;
81import org.onosproject.net.group.GroupListener;
82import org.onosproject.net.group.GroupService;
83import org.slf4j.Logger;
84
85import com.google.common.cache.Cache;
86import com.google.common.cache.CacheBuilder;
87import com.google.common.cache.RemovalCause;
88import com.google.common.cache.RemovalNotification;
89
90/**
91 * Driver for Broadcom's OF-DPA v1.0 TTP.
92 *
93 */
94public class OFDPA1Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
95
96 protected static final int PORT_TABLE = 0;
97 protected static final int VLAN_TABLE = 10;
98 protected static final int TMAC_TABLE = 20;
99 protected static final int UNICAST_ROUTING_TABLE = 30;
100 protected static final int MULTICAST_ROUTING_TABLE = 40;
101 protected static final int BRIDGING_TABLE = 50;
102 protected static final int ACL_TABLE = 60;
103 protected static final int MAC_LEARNING_TABLE = 254;
104
105 @SuppressWarnings("unused")
106 private static final int HIGHEST_PRIORITY = 0xffff;
107 private static final int DEFAULT_PRIORITY = 0x8000;
108 private static final int LOWEST_PRIORITY = 0x0;
109
110 /*
111 * Group keys are normally generated by using the next Objective id. In the
112 * case of a next objective resulting in a group chain, each group derives a
113 * group key from the next objective id in the following way:
114 * The upper 4 bits of the group-key are used to denote the position of the
115 * group in the group chain. For example, in the chain
116 * group0 --> group1 --> group2 --> port
117 * group0's group key would have the upper 4 bits as 0, group1's upper four
118 * bits would be 1, and so on
119 */
120 private static final int GROUP0MASK = 0x0;
121 private static final int GROUP1MASK = 0x10000000;
122
123 /*
124 * OFDPA requires group-id's to have a certain form.
125 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
126 * L3 Unicast Groups have <4bits-2><28bits-index>
127 */
128 private static final int L2INTERFACEMASK = 0x0;
129 private static final int L3UNICASTMASK = 0x20000000;
130
131 private final Logger log = getLogger(getClass());
132 private ServiceDirectory serviceDirectory;
133 private FlowRuleService flowRuleService;
134 private CoreService coreService;
135 private GroupService groupService;
136 private FlowObjectiveStore flowObjectiveStore;
137 private DeviceId deviceId;
Saurav Dasc39f6032015-05-14 17:12:47 -0700138 private ApplicationId driverId;
Saurav Das100e3b82015-04-30 11:12:10 -0700139
140 private KryoNamespace appKryo = new KryoNamespace.Builder()
141 .register(GroupKey.class)
142 .register(DefaultGroupKey.class)
143 .register(OfdpaGroupChain.class)
144 .register(byte[].class)
145 .build();
146
147 private Cache<GroupKey, OfdpaGroupChain> pendingNextObjectives;
148 private ConcurrentHashMap<GroupKey, GroupChainElem> pendingGroups;
149
150 private ScheduledExecutorService groupChecker =
151 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner",
152 "ofdpa1-%d"));
153
154 @Override
155 public void init(DeviceId deviceId, PipelinerContext context) {
156 this.serviceDirectory = context.directory();
157 this.deviceId = deviceId;
158
159 pendingNextObjectives = CacheBuilder.newBuilder()
160 .expireAfterWrite(20, TimeUnit.SECONDS)
161 .removalListener((RemovalNotification<GroupKey, OfdpaGroupChain> notification) -> {
162 if (notification.getCause() == RemovalCause.EXPIRED) {
163 fail(notification.getValue().nextObjective(),
164 ObjectiveError.GROUPINSTALLATIONFAILED);
165 }
166 }).build();
167
168 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
169 pendingGroups = new ConcurrentHashMap<GroupKey, GroupChainElem>();
170
171 coreService = serviceDirectory.get(CoreService.class);
172 flowRuleService = serviceDirectory.get(FlowRuleService.class);
173 groupService = serviceDirectory.get(GroupService.class);
174 flowObjectiveStore = context.store();
175
176 groupService.addListener(new InnerGroupListener());
177
Saurav Dasc39f6032015-05-14 17:12:47 -0700178 driverId = coreService.registerApplication(
Saurav Das100e3b82015-04-30 11:12:10 -0700179 "org.onosproject.driver.OFDPA1Pipeline");
180
181 initializePipeline();
182
183 }
184
185 @Override
186 public void filter(FilteringObjective filteringObjective) {
187 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
188 processFilter(filteringObjective,
189 filteringObjective.op() == Objective.Operation.ADD,
190 filteringObjective.appId());
191 } else {
192 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
193 }
194 }
195
196 @Override
197 public void forward(ForwardingObjective fwd) {
198 Collection<FlowRule> rules;
199 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
200
201 rules = processForward(fwd);
202 switch (fwd.op()) {
203 case ADD:
204 rules.stream()
205 .filter(rule -> rule != null)
206 .forEach(flowOpsBuilder::add);
207 break;
208 case REMOVE:
209 rules.stream()
210 .filter(rule -> rule != null)
211 .forEach(flowOpsBuilder::remove);
212 break;
213 default:
214 fail(fwd, ObjectiveError.UNKNOWN);
215 log.warn("Unknown forwarding type {}", fwd.op());
216 }
217
218
219 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
220 @Override
221 public void onSuccess(FlowRuleOperations ops) {
222 pass(fwd);
223 }
224
225 @Override
226 public void onError(FlowRuleOperations ops) {
227 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
228 }
229 }));
230
231 }
232
233 @Override
234 public void next(NextObjective nextObjective) {
235 switch (nextObjective.type()) {
236 case SIMPLE:
237 Collection<TrafficTreatment> treatments = nextObjective.next();
238 if (treatments.size() != 1) {
239 log.error("Next Objectives of type Simple should only have a "
240 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
241 fail(nextObjective, ObjectiveError.BADPARAMS);
242 return;
243 }
244 processSimpleNextObjective(nextObjective);
245 break;
246 case HASHED:
247 case BROADCAST:
248 case FAILOVER:
249 fail(nextObjective, ObjectiveError.UNSUPPORTED);
250 log.warn("Unsupported next objective type {}", nextObjective.type());
251 break;
252 default:
253 fail(nextObjective, ObjectiveError.UNKNOWN);
254 log.warn("Unknown next objective type {}", nextObjective.type());
255 }
256 }
257
258 /**
259 * As per OFDPA 1.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
260 * and IP addresses configured on switch ports happen in different tables.
261 * Note that IP filtering rules need to be added to the ACL table, as there
262 * is no mechanism to send to controller via IP table.
263 *
264 * @param filt
265 * @param install
266 * @param applicationId
267 */
268 private void processFilter(FilteringObjective filt,
269 boolean install, ApplicationId applicationId) {
270 // This driver only processes filtering criteria defined with switch
271 // ports as the key
272 PortCriterion p = null; EthCriterion e = null; VlanIdCriterion v = null;
273 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
274 if (!filt.key().equals(Criteria.dummy()) &&
275 filt.key().type() == Criterion.Type.IN_PORT) {
276 p = (PortCriterion) filt.key();
277 } else {
278 log.warn("No key defined in filtering objective from app: {}. Not"
279 + "processing filtering objective", applicationId);
280 fail(filt, ObjectiveError.UNKNOWN);
281 return;
282 }
283 // convert filtering conditions for switch-intfs into flowrules
284 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
285 for (Criterion c : filt.conditions()) {
286 if (c.type() == Criterion.Type.ETH_DST) {
287 e = (EthCriterion) c;
288 } else if (c.type() == Criterion.Type.VLAN_VID) {
289 v = (VlanIdCriterion) c;
290 } else if (c.type() == Criterion.Type.IPV4_DST) {
291 ips.add((IPCriterion) c);
292 } else {
293 log.error("Unsupported filter {}", c);
294 fail(filt, ObjectiveError.UNSUPPORTED);
295 return;
296 }
297 }
298
299 log.debug("adding VLAN filtering rule in VLAN table: {}", e.mac());
300 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
301 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
302 selector.matchInPort(p.port());
303 selector.matchVlanId(v.vlanId());
304 treatment.transition(TMAC_TABLE);
305 FlowRule rule = DefaultFlowRule.builder()
306 .forDevice(deviceId)
307 .withSelector(selector.build())
308 .withTreatment(treatment.build())
309 .withPriority(DEFAULT_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700310 .fromApp(applicationId)
Saurav Das100e3b82015-04-30 11:12:10 -0700311 .makePermanent()
312 .forTable(VLAN_TABLE).build();
313 ops = ops.add(rule);
314
315 log.debug("adding MAC filtering rules in TMAC table: {}", e.mac());
316 selector = DefaultTrafficSelector.builder();
317 treatment = DefaultTrafficTreatment.builder();
318 selector.matchInPort(p.port());
319 selector.matchVlanId(v.vlanId());
320 selector.matchEthType(Ethernet.TYPE_IPV4);
321 selector.matchEthDst(e.mac());
322 treatment.transition(UNICAST_ROUTING_TABLE);
323 rule = DefaultFlowRule.builder()
324 .forDevice(deviceId)
325 .withSelector(selector.build())
326 .withTreatment(treatment.build())
327 .withPriority(DEFAULT_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700328 .fromApp(applicationId)
Saurav Das100e3b82015-04-30 11:12:10 -0700329 .makePermanent()
330 .forTable(TMAC_TABLE).build();
331 ops = ops.add(rule);
332
333 log.debug("adding IP filtering rules in ACL table");
334 for (IPCriterion ipaddr : ips) {
335 selector = DefaultTrafficSelector.builder();
336 treatment = DefaultTrafficTreatment.builder();
337 selector.matchEthType(Ethernet.TYPE_IPV4);
338 selector.matchIPDst(ipaddr.ip());
339 treatment.setOutput(PortNumber.CONTROLLER);
340 rule = DefaultFlowRule.builder()
341 .forDevice(deviceId)
342 .withSelector(selector.build())
343 .withTreatment(treatment.build())
Saurav Dasc39f6032015-05-14 17:12:47 -0700344 .withPriority(HIGHEST_PRIORITY)
345 .fromApp(applicationId)
Saurav Das100e3b82015-04-30 11:12:10 -0700346 .makePermanent()
347 .forTable(ACL_TABLE).build();
348 ops = ops.add(rule);
349 }
350
351 ops = install ? ops.add(rule) : ops.remove(rule);
352 // apply filtering flow rules
353 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
354 @Override
355 public void onSuccess(FlowRuleOperations ops) {
Saurav Das100e3b82015-04-30 11:12:10 -0700356 log.info("Applied filtering rules");
Saurav Dasf9ba4222015-05-07 17:13:59 -0700357 pass(filt);
Saurav Das100e3b82015-04-30 11:12:10 -0700358 }
359
360 @Override
361 public void onError(FlowRuleOperations ops) {
Saurav Das100e3b82015-04-30 11:12:10 -0700362 log.info("Failed to apply filtering rules");
Saurav Dasf9ba4222015-05-07 17:13:59 -0700363 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Saurav Das100e3b82015-04-30 11:12:10 -0700364 }
365 }));
366
367 }
368
369
370 /**
371 * As per the OFDPA 1.0 TTP, packets are sent out of ports by using
372 * a chain of groups, namely an L3 Unicast Group that points to an L2 Interface
373 * Group which in turns points to an output port. The Next Objective passed
374 * in by the application has to be broken up into a group chain
375 * to satisfy this TTP.
376 *
377 * @param nextObj the nextObjective of type SIMPLE
378 */
379 private void processSimpleNextObjective(NextObjective nextObj) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700380 // break up simple next objective to GroupChain objects
Saurav Das100e3b82015-04-30 11:12:10 -0700381 TrafficTreatment treatment = nextObj.next().iterator().next();
382 // for the l2interface group, get vlan and port info
383 // for the l3unicast group, get the src/dst mac and vlan info
384 TrafficTreatment.Builder l3utt = DefaultTrafficTreatment.builder();
385 TrafficTreatment.Builder l2itt = DefaultTrafficTreatment.builder();
386 VlanId vlanid = null;
387 long portNum = 0;
388 for (Instruction ins : treatment.allInstructions()) {
389 if (ins.type() == Instruction.Type.L2MODIFICATION) {
390 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
391 switch (l2ins.subtype()) {
392 case ETH_DST:
393 l3utt.setEthDst(((ModEtherInstruction) l2ins).mac());
394 break;
395 case ETH_SRC:
396 l3utt.setEthSrc(((ModEtherInstruction) l2ins).mac());
397 break;
398 case VLAN_ID:
399 vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
400 l3utt.setVlanId(vlanid);
401 break;
402 case DEC_MPLS_TTL:
403 case MPLS_LABEL:
404 case MPLS_POP:
405 case MPLS_PUSH:
406 case VLAN_PCP:
407 case VLAN_POP:
408 case VLAN_PUSH:
409 default:
410 break;
411 }
412 } else if (ins.type() == Instruction.Type.OUTPUT) {
413 portNum = ((OutputInstruction) ins).port().toLong();
414 l2itt.add(ins);
415 } else {
416 log.warn("Driver does not handle this type of TrafficTreatment"
417 + " instruction in nextObjectives: {}", ins.type());
418 }
419 }
420
421 // assemble information for ofdpa l2interface group
422 int l2gk = nextObj.id() | GROUP1MASK;
423 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
424 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
425
426 // assemble information for ofdpa l3unicast group
427 int l3gk = nextObj.id() | GROUP0MASK;
428 final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
429 Integer l3groupId = L3UNICASTMASK | (int) portNum;
430 l3utt.group(new DefaultGroupId(l2groupId));
431 GroupChainElem gce = new GroupChainElem(l3groupkey, l3groupId,
432 l3utt.build(), nextObj.appId());
433
434 // create object for local and distributed storage
435 List<GroupKey> gkeys = new ArrayList<GroupKey>();
436 gkeys.add(l3groupkey); // group0 in chain
437 gkeys.add(l2groupkey); // group1 in chain
438 OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj);
439
440 // store l2groupkey with the groupChainElem for the l3group that depends on it
441 pendingGroups.put(l2groupkey, gce);
442
443 // store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it
444 pendingNextObjectives.put(l3groupkey, ofdpaGrp);
445
446 // create group description for the ofdpa l2interfacegroup and send to groupservice
447 GroupBucket bucket =
448 DefaultGroupBucket.createIndirectGroupBucket(l2itt.build());
449 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
450 GroupDescription.Type.INDIRECT,
451 new GroupBuckets(Collections.singletonList(bucket)),
452 l2groupkey,
453 l2groupId,
454 nextObj.appId());
455 groupService.addGroup(groupDescription);
456 }
457
458 /**
459 * Processes next element of a group chain. Assumption is that if this
460 * group points to another group, the latter has already been created
461 * and this driver has received notification for it. A second assumption is
462 * that if there is another group waiting for this group then the appropriate
463 * stores already have the information to act upon the notification for the
464 * creating of this group.
465 *
466 * @param gce the group chain element to be processed next
467 */
468 private void processGroupChain(GroupChainElem gce) {
469 GroupBucket bucket = DefaultGroupBucket
470 .createIndirectGroupBucket(gce.getBucketActions());
471 GroupDescription groupDesc = new DefaultGroupDescription(deviceId,
472 GroupDescription.Type.INDIRECT,
473 new GroupBuckets(Collections.singletonList(bucket)),
474 gce.getGkey(),
475 gce.getGivenGroupId(),
476 gce.getAppId());
477 groupService.addGroup(groupDesc);
478 }
479
480 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
481 switch (fwd.flag()) {
482 case SPECIFIC:
483 return processSpecific(fwd);
484 case VERSATILE:
485 return processVersatile(fwd);
486 default:
487 fail(fwd, ObjectiveError.UNKNOWN);
488 log.warn("Unknown forwarding flag {}", fwd.flag());
489 }
490 return Collections.emptySet();
491 }
492
493 /**
494 * In the OF-DPA 1.0 pipeline, versatile forwarding objectives go to the
495 * ACL table.
496 * @param fwd the forwarding objective of type 'versatile'
497 * @return a collection of flow rules to be sent to the switch. An empty
498 * collection may be returned if there is a problem in processing
499 * the flow rule
500 */
501 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
502 log.info("Processing versatile forwarding objective");
503 TrafficSelector selector = fwd.selector();
504
505 EthTypeCriterion ethType =
506 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
507 if (ethType == null) {
508 log.error("Versatile forwarding objective must include ethType");
509 fail(fwd, ObjectiveError.UNKNOWN);
510 return Collections.emptySet();
511 }
512 if (ethType.ethType() == Ethernet.TYPE_ARP) {
513 log.warn("Installing ARP rule to table 60");
514
515 // currently need to punt from ACL table should use:
516 // OF apply-actions-instruction
517 // To use OF write-actions-instruction
518 /*TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
519 fwd.treatment().allInstructions().stream()
520 .forEach(ti -> tb.deferred().add(ti));*/
521
522 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
523 .fromApp(fwd.appId())
524 .withPriority(fwd.priority())
525 .forDevice(deviceId)
526 .withSelector(fwd.selector())
527 .withTreatment(fwd.treatment())
528 .makePermanent()
529 .forTable(ACL_TABLE);
530
531 // XXX bug in OFDPA
532 return Collections.singletonList(ruleBuilder.build());
533 }
534
535 // XXX not handling other versatile flows yet
536 return Collections.emptySet();
537 }
538
539 /**
540 * In the OF-DPA 1.0 pipeline, specific forwarding refers to the IP table
541 * (unicast or multicast) or the L2 table (mac + vlan).
542 *
543 * @param fwd the forwarding objective of type 'specific'
544 * @return a collection of flow rules. Typically there will be only one
545 * for this type of forwarding objective. An empty set may be
546 * returned if there is an issue in processing the objective.
547 */
548 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
549 log.debug("Processing specific forwarding objective");
550 TrafficSelector selector = fwd.selector();
551 EthTypeCriterion ethType =
552 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
553 // XXX currently supporting only the L3 unicast table
554 if (ethType == null || ethType.ethType() != Ethernet.TYPE_IPV4) {
555 fail(fwd, ObjectiveError.UNSUPPORTED);
556 return Collections.emptySet();
557 }
558
559 TrafficSelector filteredSelector =
560 DefaultTrafficSelector.builder()
561 .matchEthType(Ethernet.TYPE_IPV4)
562 .matchIPDst(
563 ((IPCriterion)
564 selector.getCriterion(Criterion.Type.IPV4_DST)).ip())
565 .build();
566
567 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
568
569 if (fwd.nextId() != null) {
570 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
571 List<GroupKey> gkeys = appKryo.deserialize(next.data());
572 Group group = groupService.getGroup(deviceId, gkeys.get(0));
573 if (group == null) {
574 log.warn("The group left!");
575 fail(fwd, ObjectiveError.GROUPMISSING);
576 return Collections.emptySet();
577 }
578 tb.group(group.id());
579 }
580
581 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
582 .fromApp(fwd.appId())
583 .withPriority(fwd.priority())
584 .forDevice(deviceId)
585 .withSelector(filteredSelector)
586 .withTreatment(tb.build());
587
588 if (fwd.permanent()) {
589 ruleBuilder.makePermanent();
590 } else {
591 ruleBuilder.makeTemporary(fwd.timeout());
592 }
593
594 ruleBuilder.forTable(UNICAST_ROUTING_TABLE);
595 return Collections.singletonList(ruleBuilder.build());
596 }
597
598 private void pass(Objective obj) {
599 if (obj.context().isPresent()) {
600 obj.context().get().onSuccess(obj);
601 }
602 }
603
604
605 private void fail(Objective obj, ObjectiveError error) {
606 if (obj.context().isPresent()) {
607 obj.context().get().onError(obj, error);
608 }
609 }
610
611
612 private void initializePipeline() {
613 processPortTable();
614 processVlanTable();
615 processTmacTable();
616 processIpTable();
617 //processMcastTable();
618 //processBridgingTable();
619 //processAclTable();
620 //processGroupTable();
621 }
622
623 private void processPortTable() {
624 //XXX is table miss entry enough or do we need to do the maskable in-port 0?
625 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
626 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
627 selector.matchInPort(PortNumber.portNumber(0)); // should be maskable?
628 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
629 treatment.transition(VLAN_TABLE);
630 FlowRule tmisse = DefaultFlowRule.builder()
631 .forDevice(deviceId)
632 .withSelector(selector.build())
633 .withTreatment(treatment.build())
634 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700635 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700636 .makePermanent()
637 .forTable(PORT_TABLE).build();
638 ops = ops.add(tmisse);
639
640 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
641 @Override
642 public void onSuccess(FlowRuleOperations ops) {
643 log.info("Initialized port table");
644 }
645
646 @Override
647 public void onError(FlowRuleOperations ops) {
648 log.info("Failed to initialize port table");
649 }
650 }));
651
652 }
653
654 private void processVlanTable() {
655 // make these up for now - should really be filtering rules
656 /*FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
657 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
658 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
659 selector.matchInPort(PortNumber.portNumber(10));
660 selector.matchVlanId(VlanId.vlanId((short) 100));
661 treatment.transition(TMAC_TABLE);
662 FlowRule rule = DefaultFlowRule.builder()
663 .forDevice(deviceId)
664 .withSelector(selector.build())
665 .withTreatment(treatment.build())
666 .withPriority(DEFAULT_PRIORITY)
667 .fromApp(appId)
668 .makePermanent()
669 .forTable(VLAN_TABLE).build();
670 ops = ops.add(rule);
671 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
672 @Override
673 public void onSuccess(FlowRuleOperations ops) {
674 log.info("Initialized vlan table");
675 }
676
677 @Override
678 public void onError(FlowRuleOperations ops) {
679 log.info("Failed to initialize vlan table");
680 }
681 }));*/
682
683 // Table miss entry is not required as ofdpa default is to drop
684 // In OF terms, the absence of a t.m.e. also implies drop
685 }
686
687
688 private void processTmacTable() {
689 // this is made up as well -- should be a filtering rule
690 /*selector.matchInPort(PortNumber.portNumber(10));
691 selector.matchVlanId(VlanId.vlanId((short) 100));
692 selector.matchEthType(Ethernet.TYPE_IPV4);
693 selector.matchEthDst(MacAddress.valueOf("00:00:00:ba:ba:00"));
694 treatment.transition(UNICAST_ROUTING_TABLE);
695 FlowRule rule = DefaultFlowRule.builder()
696 .forDevice(deviceId)
697 .withSelector(selector.build())
698 .withTreatment(treatment.build())
699 .withPriority(DEFAULT_PRIORITY)
700 .fromApp(appId)
701 .makePermanent()
702 .forTable(TMAC_TABLE).build();
703 ops = ops.add(rule);*/
704
705 //table miss entry
706 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
707 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
708 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
709 selector = DefaultTrafficSelector.builder();
710 treatment = DefaultTrafficTreatment.builder();
711 treatment.transition(BRIDGING_TABLE);
712 FlowRule rule = DefaultFlowRule.builder()
713 .forDevice(deviceId)
714 .withSelector(selector.build())
715 .withTreatment(treatment.build())
716 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700717 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700718 .makePermanent()
719 .forTable(TMAC_TABLE).build();
720 ops = ops.add(rule); // XXX bug in ofdpa
721 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
722 @Override
723 public void onSuccess(FlowRuleOperations ops) {
724 log.info("Initialized tmac table");
725 }
726
727 @Override
728 public void onError(FlowRuleOperations ops) {
729 log.info("Failed to initialize tmac table");
730 }
731 }));
732 }
733
734 private void processIpTable() {
735 //table miss entry
736 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
737 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
738 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
739 selector = DefaultTrafficSelector.builder();
740 treatment = DefaultTrafficTreatment.builder();
741 treatment.transition(ACL_TABLE);
742 FlowRule rule = DefaultFlowRule.builder()
743 .forDevice(deviceId)
744 .withSelector(selector.build())
745 .withTreatment(treatment.build())
746 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700747 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700748 .makePermanent()
749 .forTable(UNICAST_ROUTING_TABLE).build();
750 ops = ops.add(rule);
751 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
752 @Override
753 public void onSuccess(FlowRuleOperations ops) {
754 log.info("Initialized IP table");
755 }
756
757 @Override
758 public void onError(FlowRuleOperations ops) {
759 log.info("Failed to initialize unicast IP table");
760 }
761 }));
762 }
763
764 @SuppressWarnings("unused")
765 private void processGroupTable() {
766 // Creating a dummy L2 group as per OFDPA requirements
767 /* TrafficTreatment treatment = DefaultTrafficTreatment.builder()
768 .setOutput(PortNumber.portNumber(10))
769 .build();
770 NextObjective nextObjective = DefaultNextObjective.builder()
771 .addTreatment(treatment)
772 .fromApp(appId)
773 .withId(678) // dummy next objective id
774 .withType(NextObjective.Type.SIMPLE)
775 .add();
776 Integer l2groupId = 0x0064000a;
777 GroupBucket bucket =
778 DefaultGroupBucket.createIndirectGroupBucket(treatment);
779 final GroupKey key = new DefaultGroupKey(appKryo.serialize(678));
780 GroupDescription groupDescriptionl2
781 = new DefaultGroupDescription(deviceId,
782 GroupDescription.Type.INDIRECT,
783 new GroupBuckets(Collections
784 .singletonList(bucket)),
785 key,
786 l2groupId,
787 appId);
788 groupService.addGroup(groupDescriptionl2);*/
789 //pendingNextObjectives.put(key, nextObjective);
790
791 }
792
793 @SuppressWarnings("unused")
794 private void tryGroupChain() {
795 //Create a dummy L3 group as per OFDPA requirements
796 /*TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
797 .setEthDst(MacAddress.valueOf("00:00:00:aa:aa:aa"))
798 .setEthSrc(MacAddress.valueOf("00:00:00:dd:dd:dd"))
799 .setVlanId(VlanId.vlanId((short) 100))
800 .group(new DefaultGroupId(0x0064000a))
801 .build();
802 NextObjective nextObjective2 = DefaultNextObjective.builder()
803 .addTreatment(treatment2)
804 .fromApp(appId)
805 .withId(67800) // another dummy next objective id
806 .withType(NextObjective.Type.SIMPLE)
807 .add();
808 Integer l3groupId = 0x2000000a;
809 GroupBucket bucket2 = DefaultGroupBucket.createIndirectGroupBucket(treatment2);
810 final GroupKey key2 = new DefaultGroupKey(appKryo.serialize(67800));
811 GroupDescription groupDescriptionl3
812 = new DefaultGroupDescription(deviceId,
813 GroupDescription.Type.INDIRECT,
814 new GroupBuckets(Collections
815 .singletonList(bucket2)),
816 key2,
817 l3groupId,
818 appId);
819 groupService.addGroup(groupDescriptionl3);
820 pendingNextObjectives.put(key2, nextObjective2);
821 */
822 }
823
824 private class GroupChecker implements Runnable {
825 @Override
826 public void run() {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700827 Set<GroupKey> keys = pendingGroups.keySet().stream()
Saurav Das100e3b82015-04-30 11:12:10 -0700828 .filter(key -> groupService.getGroup(deviceId, key) != null)
829 .collect(Collectors.toSet());
830
831 keys.stream().forEach(key -> {
832 //first check for group chain
833 GroupChainElem gce = pendingGroups.remove(key);
834 if (gce != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700835 log.info("Group service processed group key {}. Processing next "
836 + "group in group chain with group key {}",
837 appKryo.deserialize(key.key()),
838 appKryo.deserialize(gce.getGkey().key()));
Saurav Das100e3b82015-04-30 11:12:10 -0700839 processGroupChain(gce);
840 } else {
841 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
Saurav Dasf9ba4222015-05-07 17:13:59 -0700842 log.info("Group service processed group key {}. Done implementing "
843 + "next objective: {}", appKryo.deserialize(key.key()),
844 obj.nextObjective().id());
845 if (obj != null) {
846 pass(obj.nextObjective());
847 pendingNextObjectives.invalidate(key);
848 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
Saurav Das100e3b82015-04-30 11:12:10 -0700849 }
Saurav Das100e3b82015-04-30 11:12:10 -0700850 }
851 });
852 }
853 }
854
Saurav Das100e3b82015-04-30 11:12:10 -0700855 private class InnerGroupListener implements GroupListener {
856 @Override
857 public void event(GroupEvent event) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700858 log.info("received group event of type {}", event.type());
Saurav Das100e3b82015-04-30 11:12:10 -0700859 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
860 GroupKey key = event.subject().appCookie();
861 // first check for group chain
862 GroupChainElem gce = pendingGroups.remove(key);
863 if (gce != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700864 log.info("group ADDED with group key {} .. "
865 + "Processing next group in group chain with group key {}",
866 appKryo.deserialize(key.key()),
867 appKryo.deserialize(gce.getGkey().key()));
Saurav Das100e3b82015-04-30 11:12:10 -0700868 processGroupChain(gce);
869 } else {
870 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
Saurav Das100e3b82015-04-30 11:12:10 -0700871 if (obj != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700872 log.info("group ADDED with key {}.. Done implementing next "
873 + "objective: {}",
874 appKryo.deserialize(key.key()), obj.nextObjective().id());
Saurav Das100e3b82015-04-30 11:12:10 -0700875 pass(obj.nextObjective());
876 pendingNextObjectives.invalidate(key);
Saurav Dasf9ba4222015-05-07 17:13:59 -0700877 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
Saurav Das100e3b82015-04-30 11:12:10 -0700878 }
879 }
880 }
881 }
882 }
883
884 /**
885 * Represents a group-chain that implements a Next-Objective from
886 * the application. Includes information about the next objective Id, and the
887 * group keys for the groups in the group chain. The chain is expected to
888 * look like group0 --> group 1 --> outPort. Information about the groups
889 * themselves can be fetched from the Group Service using the group keys from
890 * objects instantiating this class.
891 */
892 private class OfdpaGroupChain implements NextGroup {
893 private final NextObjective nextObj;
894 private final List<GroupKey> gkeys;
895
896 /** expected group chain: group0 --> group1 --> port. */
897 public OfdpaGroupChain(List<GroupKey> gkeys, NextObjective nextObj) {
898 this.gkeys = gkeys;
899 this.nextObj = nextObj;
900 }
901
902 @SuppressWarnings("unused")
903 public List<GroupKey> groupKeys() {
904 return gkeys;
905 }
906
907 public NextObjective nextObjective() {
908 return nextObj;
909 }
910
911 @Override
912 public byte[] data() {
913 return appKryo.serialize(gkeys);
914 }
915
916 }
917
918 /**
919 * Represents a group element that is part of a chain of groups.
920 * Stores enough information to create a Group Description to add the group
921 * to the switch by requesting the Group Service. Objects instantiating this
922 * class are meant to be temporary and live as long as it is needed to wait for
923 * preceding groups in the group chain to be created.
924 */
925 private class GroupChainElem {
926 private TrafficTreatment bucketActions;
927 private Integer givenGroupId;
928 private GroupKey gkey;
929 private ApplicationId appId;
930
931 public GroupChainElem(GroupKey gkey, Integer givenGroupId,
932 TrafficTreatment tr, ApplicationId appId) {
933 this.bucketActions = tr;
934 this.givenGroupId = givenGroupId;
935 this.gkey = gkey;
936 this.appId = appId;
937 }
938
939 public TrafficTreatment getBucketActions() {
940 return bucketActions;
941 }
942
943 public Integer getGivenGroupId() {
944 return givenGroupId;
945 }
946
947 public GroupKey getGkey() {
948 return gkey;
949 }
950
951 public ApplicationId getAppId() {
952 return appId;
953 }
954
955 }
956}