blob: 54f8df58243451347462b06fa3c87012f7df854d [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;
Saurav Das558afec2015-05-31 17:12:48 -0700108 protected static final int LOWEST_PRIORITY = 0x0;
Saurav Das100e3b82015-04-30 11:12:10 -0700109
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;
Saurav Das558afec2015-05-31 17:12:48 -0700133 protected FlowRuleService flowRuleService;
Saurav Das100e3b82015-04-30 11:12:10 -0700134 private CoreService coreService;
135 private GroupService groupService;
136 private FlowObjectiveStore flowObjectiveStore;
Saurav Das558afec2015-05-31 17:12:48 -0700137 protected DeviceId deviceId;
138 protected 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 }
alshabibcaf1ca22015-06-25 15:18:16 -0700513 if (ethType.ethType().toShort() == Ethernet.TYPE_ARP) {
Saurav Das100e3b82015-04-30 11:12:10 -0700514 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
Saurav Das100e3b82015-04-30 11:12:10 -0700532 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
alshabibcaf1ca22015-06-25 15:18:16 -0700554 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Das100e3b82015-04-30 11:12:10 -0700555 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 }
Saurav Das88329902015-05-27 23:22:32 -0700578 tb.deferred().group(group.id());
Saurav Das100e3b82015-04-30 11:12:10 -0700579 }
Saurav Das88329902015-05-27 23:22:32 -0700580 tb.transition(ACL_TABLE);
Saurav Das100e3b82015-04-30 11:12:10 -0700581 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
Saurav Das558afec2015-05-31 17:12:48 -0700612 protected void initializePipeline() {
Saurav Das100e3b82015-04-30 11:12:10 -0700613 processPortTable();
614 processVlanTable();
615 processTmacTable();
616 processIpTable();
617 //processMcastTable();
618 //processBridgingTable();
619 //processAclTable();
620 //processGroupTable();
621 }
622
Saurav Das558afec2015-05-31 17:12:48 -0700623 protected void processPortTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700624 //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();
Saurav Das88329902015-05-27 23:22:32 -0700638 /*ops = ops.add(tmisse);
Saurav Das100e3b82015-04-30 11:12:10 -0700639
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 }
Saurav Das88329902015-05-27 23:22:32 -0700650 }));*/
Saurav Das100e3b82015-04-30 11:12:10 -0700651
652 }
653
654 private void processVlanTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700655 // Table miss entry is not required as ofdpa default is to drop
656 // In OF terms, the absence of a t.m.e. also implies drop
657 }
658
659
Saurav Das558afec2015-05-31 17:12:48 -0700660 protected void processTmacTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700661 //table miss entry
662 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
663 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
664 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
665 selector = DefaultTrafficSelector.builder();
666 treatment = DefaultTrafficTreatment.builder();
667 treatment.transition(BRIDGING_TABLE);
668 FlowRule rule = DefaultFlowRule.builder()
669 .forDevice(deviceId)
670 .withSelector(selector.build())
671 .withTreatment(treatment.build())
672 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700673 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700674 .makePermanent()
675 .forTable(TMAC_TABLE).build();
Saurav Das88329902015-05-27 23:22:32 -0700676 /*ops = ops.add(rule); // XXX bug in ofdpa
Saurav Das100e3b82015-04-30 11:12:10 -0700677 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
678 @Override
679 public void onSuccess(FlowRuleOperations ops) {
680 log.info("Initialized tmac table");
681 }
682
683 @Override
684 public void onError(FlowRuleOperations ops) {
685 log.info("Failed to initialize tmac table");
686 }
Saurav Das88329902015-05-27 23:22:32 -0700687 }));*/
Saurav Das100e3b82015-04-30 11:12:10 -0700688 }
689
Saurav Das558afec2015-05-31 17:12:48 -0700690 protected void processIpTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700691 //table miss entry
692 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
693 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
694 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
695 selector = DefaultTrafficSelector.builder();
696 treatment = DefaultTrafficTreatment.builder();
697 treatment.transition(ACL_TABLE);
698 FlowRule rule = DefaultFlowRule.builder()
699 .forDevice(deviceId)
700 .withSelector(selector.build())
701 .withTreatment(treatment.build())
702 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700703 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700704 .makePermanent()
705 .forTable(UNICAST_ROUTING_TABLE).build();
Saurav Das88329902015-05-27 23:22:32 -0700706 /*ops = ops.add(rule);
Saurav Das100e3b82015-04-30 11:12:10 -0700707 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
708 @Override
709 public void onSuccess(FlowRuleOperations ops) {
710 log.info("Initialized IP table");
711 }
712
713 @Override
714 public void onError(FlowRuleOperations ops) {
715 log.info("Failed to initialize unicast IP table");
716 }
Saurav Das88329902015-05-27 23:22:32 -0700717 }));*/
Saurav Das100e3b82015-04-30 11:12:10 -0700718 }
719
Saurav Das100e3b82015-04-30 11:12:10 -0700720 private class GroupChecker implements Runnable {
721 @Override
722 public void run() {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700723 Set<GroupKey> keys = pendingGroups.keySet().stream()
Saurav Das100e3b82015-04-30 11:12:10 -0700724 .filter(key -> groupService.getGroup(deviceId, key) != null)
725 .collect(Collectors.toSet());
Saurav Das558afec2015-05-31 17:12:48 -0700726 Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream()
727 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
728 .collect(Collectors.toSet());
729 keys.addAll(otherkeys);
Saurav Das100e3b82015-04-30 11:12:10 -0700730
731 keys.stream().forEach(key -> {
732 //first check for group chain
733 GroupChainElem gce = pendingGroups.remove(key);
734 if (gce != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700735 log.info("Group service processed group key {}. Processing next "
736 + "group in group chain with group key {}",
737 appKryo.deserialize(key.key()),
738 appKryo.deserialize(gce.getGkey().key()));
Saurav Das100e3b82015-04-30 11:12:10 -0700739 processGroupChain(gce);
740 } else {
741 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
Saurav Dasf9ba4222015-05-07 17:13:59 -0700742 log.info("Group service processed group key {}. Done implementing "
743 + "next objective: {}", appKryo.deserialize(key.key()),
744 obj.nextObjective().id());
745 if (obj != null) {
746 pass(obj.nextObjective());
747 pendingNextObjectives.invalidate(key);
748 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
Saurav Das100e3b82015-04-30 11:12:10 -0700749 }
Saurav Das100e3b82015-04-30 11:12:10 -0700750 }
751 });
752 }
753 }
754
Saurav Das100e3b82015-04-30 11:12:10 -0700755 private class InnerGroupListener implements GroupListener {
756 @Override
757 public void event(GroupEvent event) {
Saurav Das558afec2015-05-31 17:12:48 -0700758 log.debug("received group event of type {}", event.type());
Saurav Das100e3b82015-04-30 11:12:10 -0700759 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
760 GroupKey key = event.subject().appCookie();
761 // first check for group chain
762 GroupChainElem gce = pendingGroups.remove(key);
763 if (gce != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700764 log.info("group ADDED with group key {} .. "
765 + "Processing next group in group chain with group key {}",
766 appKryo.deserialize(key.key()),
767 appKryo.deserialize(gce.getGkey().key()));
Saurav Das100e3b82015-04-30 11:12:10 -0700768 processGroupChain(gce);
769 } else {
770 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
Saurav Das100e3b82015-04-30 11:12:10 -0700771 if (obj != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700772 log.info("group ADDED with key {}.. Done implementing next "
773 + "objective: {}",
774 appKryo.deserialize(key.key()), obj.nextObjective().id());
Saurav Das100e3b82015-04-30 11:12:10 -0700775 pass(obj.nextObjective());
776 pendingNextObjectives.invalidate(key);
Saurav Dasf9ba4222015-05-07 17:13:59 -0700777 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
Saurav Das100e3b82015-04-30 11:12:10 -0700778 }
779 }
780 }
781 }
782 }
783
784 /**
785 * Represents a group-chain that implements a Next-Objective from
786 * the application. Includes information about the next objective Id, and the
787 * group keys for the groups in the group chain. The chain is expected to
788 * look like group0 --> group 1 --> outPort. Information about the groups
789 * themselves can be fetched from the Group Service using the group keys from
790 * objects instantiating this class.
791 */
792 private class OfdpaGroupChain implements NextGroup {
793 private final NextObjective nextObj;
794 private final List<GroupKey> gkeys;
795
796 /** expected group chain: group0 --> group1 --> port. */
797 public OfdpaGroupChain(List<GroupKey> gkeys, NextObjective nextObj) {
798 this.gkeys = gkeys;
799 this.nextObj = nextObj;
800 }
801
802 @SuppressWarnings("unused")
803 public List<GroupKey> groupKeys() {
804 return gkeys;
805 }
806
807 public NextObjective nextObjective() {
808 return nextObj;
809 }
810
811 @Override
812 public byte[] data() {
813 return appKryo.serialize(gkeys);
814 }
815
816 }
817
818 /**
819 * Represents a group element that is part of a chain of groups.
820 * Stores enough information to create a Group Description to add the group
821 * to the switch by requesting the Group Service. Objects instantiating this
822 * class are meant to be temporary and live as long as it is needed to wait for
823 * preceding groups in the group chain to be created.
824 */
825 private class GroupChainElem {
826 private TrafficTreatment bucketActions;
827 private Integer givenGroupId;
828 private GroupKey gkey;
829 private ApplicationId appId;
830
831 public GroupChainElem(GroupKey gkey, Integer givenGroupId,
832 TrafficTreatment tr, ApplicationId appId) {
833 this.bucketActions = tr;
834 this.givenGroupId = givenGroupId;
835 this.gkey = gkey;
836 this.appId = appId;
837 }
838
839 public TrafficTreatment getBucketActions() {
840 return bucketActions;
841 }
842
843 public Integer getGivenGroupId() {
844 return givenGroupId;
845 }
846
847 public GroupKey getGkey() {
848 return gkey;
849 }
850
851 public ApplicationId getAppId() {
852 return appId;
853 }
854
855 }
856}