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