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