blob: f17309e1095ac014fefce86413a1a0d8ae6b9cc8 [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;
Saurav Dasffc5bbc2015-08-18 23:30:19 -070034import org.onlab.packet.MplsLabel;
Saurav Das100e3b82015-04-30 11:12:10 -070035import org.onlab.packet.VlanId;
36import org.onlab.util.KryoNamespace;
37import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
39import org.onosproject.core.DefaultGroupId;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.PortNumber;
42import org.onosproject.net.behaviour.NextGroup;
43import org.onosproject.net.behaviour.Pipeliner;
44import org.onosproject.net.behaviour.PipelinerContext;
45import org.onosproject.net.driver.AbstractHandlerBehaviour;
46import org.onosproject.net.flow.DefaultFlowRule;
47import org.onosproject.net.flow.DefaultTrafficSelector;
48import org.onosproject.net.flow.DefaultTrafficTreatment;
49import org.onosproject.net.flow.FlowRule;
50import org.onosproject.net.flow.FlowRuleOperations;
51import org.onosproject.net.flow.FlowRuleOperationsContext;
52import org.onosproject.net.flow.FlowRuleService;
53import org.onosproject.net.flow.TrafficSelector;
54import org.onosproject.net.flow.TrafficTreatment;
55import org.onosproject.net.flow.criteria.Criteria;
56import org.onosproject.net.flow.criteria.Criterion;
57import org.onosproject.net.flow.criteria.EthCriterion;
58import org.onosproject.net.flow.criteria.EthTypeCriterion;
59import org.onosproject.net.flow.criteria.IPCriterion;
60import org.onosproject.net.flow.criteria.PortCriterion;
61import org.onosproject.net.flow.criteria.VlanIdCriterion;
62import org.onosproject.net.flow.instructions.Instruction;
63import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
64import org.onosproject.net.flow.instructions.L2ModificationInstruction;
65import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
66import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
67import org.onosproject.net.flowobjective.FilteringObjective;
68import org.onosproject.net.flowobjective.FlowObjectiveStore;
69import org.onosproject.net.flowobjective.ForwardingObjective;
70import org.onosproject.net.flowobjective.NextObjective;
71import org.onosproject.net.flowobjective.Objective;
72import org.onosproject.net.flowobjective.ObjectiveError;
73import org.onosproject.net.group.DefaultGroupBucket;
74import org.onosproject.net.group.DefaultGroupDescription;
75import org.onosproject.net.group.DefaultGroupKey;
76import org.onosproject.net.group.Group;
77import org.onosproject.net.group.GroupBucket;
78import org.onosproject.net.group.GroupBuckets;
79import org.onosproject.net.group.GroupDescription;
80import org.onosproject.net.group.GroupEvent;
81import org.onosproject.net.group.GroupKey;
82import org.onosproject.net.group.GroupListener;
83import org.onosproject.net.group.GroupService;
Saurav Das88329902015-05-27 23:22:32 -070084import org.onosproject.store.serializers.KryoNamespaces;
Saurav Das100e3b82015-04-30 11:12:10 -070085import org.slf4j.Logger;
86
87import com.google.common.cache.Cache;
88import com.google.common.cache.CacheBuilder;
89import com.google.common.cache.RemovalCause;
90import com.google.common.cache.RemovalNotification;
91
92/**
93 * Driver for Broadcom's OF-DPA v1.0 TTP.
94 *
95 */
96public class OFDPA1Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
97
98 protected static final int PORT_TABLE = 0;
99 protected static final int VLAN_TABLE = 10;
100 protected static final int TMAC_TABLE = 20;
101 protected static final int UNICAST_ROUTING_TABLE = 30;
102 protected static final int MULTICAST_ROUTING_TABLE = 40;
103 protected static final int BRIDGING_TABLE = 50;
104 protected static final int ACL_TABLE = 60;
105 protected static final int MAC_LEARNING_TABLE = 254;
106
Saurav Das100e3b82015-04-30 11:12:10 -0700107 private static final int HIGHEST_PRIORITY = 0xffff;
108 private static final int DEFAULT_PRIORITY = 0x8000;
Saurav Das558afec2015-05-31 17:12:48 -0700109 protected static final int LOWEST_PRIORITY = 0x0;
Saurav Das100e3b82015-04-30 11:12:10 -0700110
111 /*
112 * Group keys are normally generated by using the next Objective id. In the
113 * case of a next objective resulting in a group chain, each group derives a
114 * group key from the next objective id in the following way:
115 * The upper 4 bits of the group-key are used to denote the position of the
116 * group in the group chain. For example, in the chain
117 * group0 --> group1 --> group2 --> port
118 * group0's group key would have the upper 4 bits as 0, group1's upper four
119 * bits would be 1, and so on
120 */
121 private static final int GROUP0MASK = 0x0;
122 private static final int GROUP1MASK = 0x10000000;
123
124 /*
125 * OFDPA requires group-id's to have a certain form.
126 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
127 * L3 Unicast Groups have <4bits-2><28bits-index>
128 */
129 private static final int L2INTERFACEMASK = 0x0;
130 private static final int L3UNICASTMASK = 0x20000000;
131
132 private final Logger log = getLogger(getClass());
133 private ServiceDirectory serviceDirectory;
Saurav Das558afec2015-05-31 17:12:48 -0700134 protected FlowRuleService flowRuleService;
Saurav Das100e3b82015-04-30 11:12:10 -0700135 private CoreService coreService;
136 private GroupService groupService;
137 private FlowObjectiveStore flowObjectiveStore;
Saurav Das558afec2015-05-31 17:12:48 -0700138 protected DeviceId deviceId;
139 protected ApplicationId driverId;
Saurav Das100e3b82015-04-30 11:12:10 -0700140
141 private KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das88329902015-05-27 23:22:32 -0700142 .register(KryoNamespaces.API)
Saurav Das100e3b82015-04-30 11:12:10 -0700143 .register(GroupKey.class)
144 .register(DefaultGroupKey.class)
145 .register(OfdpaGroupChain.class)
146 .register(byte[].class)
147 .build();
148
149 private Cache<GroupKey, OfdpaGroupChain> pendingNextObjectives;
150 private ConcurrentHashMap<GroupKey, GroupChainElem> pendingGroups;
151
152 private ScheduledExecutorService groupChecker =
153 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner",
154 "ofdpa1-%d"));
155
156 @Override
157 public void init(DeviceId deviceId, PipelinerContext context) {
158 this.serviceDirectory = context.directory();
159 this.deviceId = deviceId;
160
161 pendingNextObjectives = CacheBuilder.newBuilder()
162 .expireAfterWrite(20, TimeUnit.SECONDS)
163 .removalListener((RemovalNotification<GroupKey, OfdpaGroupChain> notification) -> {
164 if (notification.getCause() == RemovalCause.EXPIRED) {
165 fail(notification.getValue().nextObjective(),
166 ObjectiveError.GROUPINSTALLATIONFAILED);
167 }
168 }).build();
169
170 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
171 pendingGroups = new ConcurrentHashMap<GroupKey, GroupChainElem>();
172
173 coreService = serviceDirectory.get(CoreService.class);
174 flowRuleService = serviceDirectory.get(FlowRuleService.class);
175 groupService = serviceDirectory.get(GroupService.class);
176 flowObjectiveStore = context.store();
177
178 groupService.addListener(new InnerGroupListener());
179
Saurav Dasc39f6032015-05-14 17:12:47 -0700180 driverId = coreService.registerApplication(
Saurav Das100e3b82015-04-30 11:12:10 -0700181 "org.onosproject.driver.OFDPA1Pipeline");
182
183 initializePipeline();
184
185 }
186
187 @Override
188 public void filter(FilteringObjective filteringObjective) {
189 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
190 processFilter(filteringObjective,
191 filteringObjective.op() == Objective.Operation.ADD,
192 filteringObjective.appId());
193 } else {
194 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
195 }
196 }
197
198 @Override
199 public void forward(ForwardingObjective fwd) {
200 Collection<FlowRule> rules;
201 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
202
203 rules = processForward(fwd);
204 switch (fwd.op()) {
205 case ADD:
206 rules.stream()
207 .filter(rule -> rule != null)
208 .forEach(flowOpsBuilder::add);
209 break;
210 case REMOVE:
211 rules.stream()
212 .filter(rule -> rule != null)
213 .forEach(flowOpsBuilder::remove);
214 break;
215 default:
216 fail(fwd, ObjectiveError.UNKNOWN);
217 log.warn("Unknown forwarding type {}", fwd.op());
218 }
219
220
221 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
222 @Override
223 public void onSuccess(FlowRuleOperations ops) {
224 pass(fwd);
225 }
226
227 @Override
228 public void onError(FlowRuleOperations ops) {
229 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
230 }
231 }));
232
233 }
234
235 @Override
236 public void next(NextObjective nextObjective) {
237 switch (nextObjective.type()) {
238 case SIMPLE:
239 Collection<TrafficTreatment> treatments = nextObjective.next();
240 if (treatments.size() != 1) {
241 log.error("Next Objectives of type Simple should only have a "
242 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
243 fail(nextObjective, ObjectiveError.BADPARAMS);
244 return;
245 }
246 processSimpleNextObjective(nextObjective);
247 break;
248 case HASHED:
249 case BROADCAST:
250 case FAILOVER:
251 fail(nextObjective, ObjectiveError.UNSUPPORTED);
252 log.warn("Unsupported next objective type {}", nextObjective.type());
253 break;
254 default:
255 fail(nextObjective, ObjectiveError.UNKNOWN);
256 log.warn("Unknown next objective type {}", nextObjective.type());
257 }
258 }
259
260 /**
261 * As per OFDPA 1.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
262 * and IP addresses configured on switch ports happen in different tables.
263 * Note that IP filtering rules need to be added to the ACL table, as there
264 * is no mechanism to send to controller via IP table.
265 *
266 * @param filt
267 * @param install
268 * @param applicationId
269 */
270 private void processFilter(FilteringObjective filt,
271 boolean install, ApplicationId applicationId) {
272 // This driver only processes filtering criteria defined with switch
273 // ports as the key
274 PortCriterion p = null; EthCriterion e = null; VlanIdCriterion v = null;
275 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
276 if (!filt.key().equals(Criteria.dummy()) &&
277 filt.key().type() == Criterion.Type.IN_PORT) {
278 p = (PortCriterion) filt.key();
279 } else {
280 log.warn("No key defined in filtering objective from app: {}. Not"
281 + "processing filtering objective", applicationId);
282 fail(filt, ObjectiveError.UNKNOWN);
283 return;
284 }
285 // convert filtering conditions for switch-intfs into flowrules
286 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
287 for (Criterion c : filt.conditions()) {
288 if (c.type() == Criterion.Type.ETH_DST) {
289 e = (EthCriterion) c;
290 } else if (c.type() == Criterion.Type.VLAN_VID) {
291 v = (VlanIdCriterion) c;
292 } else if (c.type() == Criterion.Type.IPV4_DST) {
293 ips.add((IPCriterion) c);
294 } else {
295 log.error("Unsupported filter {}", c);
296 fail(filt, ObjectiveError.UNSUPPORTED);
297 return;
298 }
299 }
300
301 log.debug("adding VLAN filtering rule in VLAN table: {}", e.mac());
302 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
303 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
304 selector.matchInPort(p.port());
305 selector.matchVlanId(v.vlanId());
306 treatment.transition(TMAC_TABLE);
307 FlowRule rule = DefaultFlowRule.builder()
308 .forDevice(deviceId)
309 .withSelector(selector.build())
310 .withTreatment(treatment.build())
311 .withPriority(DEFAULT_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700312 .fromApp(applicationId)
Saurav Das100e3b82015-04-30 11:12:10 -0700313 .makePermanent()
314 .forTable(VLAN_TABLE).build();
315 ops = ops.add(rule);
316
317 log.debug("adding MAC filtering rules in TMAC table: {}", e.mac());
318 selector = DefaultTrafficSelector.builder();
319 treatment = DefaultTrafficTreatment.builder();
320 selector.matchInPort(p.port());
321 selector.matchVlanId(v.vlanId());
322 selector.matchEthType(Ethernet.TYPE_IPV4);
323 selector.matchEthDst(e.mac());
324 treatment.transition(UNICAST_ROUTING_TABLE);
325 rule = DefaultFlowRule.builder()
326 .forDevice(deviceId)
327 .withSelector(selector.build())
328 .withTreatment(treatment.build())
329 .withPriority(DEFAULT_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700330 .fromApp(applicationId)
Saurav Das100e3b82015-04-30 11:12:10 -0700331 .makePermanent()
332 .forTable(TMAC_TABLE).build();
333 ops = ops.add(rule);
334
335 log.debug("adding IP filtering rules in ACL table");
336 for (IPCriterion ipaddr : ips) {
337 selector = DefaultTrafficSelector.builder();
338 treatment = DefaultTrafficTreatment.builder();
339 selector.matchEthType(Ethernet.TYPE_IPV4);
340 selector.matchIPDst(ipaddr.ip());
341 treatment.setOutput(PortNumber.CONTROLLER);
342 rule = DefaultFlowRule.builder()
343 .forDevice(deviceId)
344 .withSelector(selector.build())
345 .withTreatment(treatment.build())
Saurav Dasc39f6032015-05-14 17:12:47 -0700346 .withPriority(HIGHEST_PRIORITY)
347 .fromApp(applicationId)
Saurav Das100e3b82015-04-30 11:12:10 -0700348 .makePermanent()
349 .forTable(ACL_TABLE).build();
350 ops = ops.add(rule);
351 }
352
353 ops = install ? ops.add(rule) : ops.remove(rule);
354 // apply filtering flow rules
355 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
356 @Override
357 public void onSuccess(FlowRuleOperations ops) {
Saurav Das100e3b82015-04-30 11:12:10 -0700358 log.info("Applied filtering rules");
Saurav Dasf9ba4222015-05-07 17:13:59 -0700359 pass(filt);
Saurav Das100e3b82015-04-30 11:12:10 -0700360 }
361
362 @Override
363 public void onError(FlowRuleOperations ops) {
Saurav Das100e3b82015-04-30 11:12:10 -0700364 log.info("Failed to apply filtering rules");
Saurav Dasf9ba4222015-05-07 17:13:59 -0700365 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Saurav Das100e3b82015-04-30 11:12:10 -0700366 }
367 }));
368
369 }
370
371
372 /**
373 * As per the OFDPA 1.0 TTP, packets are sent out of ports by using
374 * a chain of groups, namely an L3 Unicast Group that points to an L2 Interface
375 * Group which in turns points to an output port. The Next Objective passed
376 * in by the application has to be broken up into a group chain
377 * to satisfy this TTP.
378 *
379 * @param nextObj the nextObjective of type SIMPLE
380 */
381 private void processSimpleNextObjective(NextObjective nextObj) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700382 // break up simple next objective to GroupChain objects
Saurav Das100e3b82015-04-30 11:12:10 -0700383 TrafficTreatment treatment = nextObj.next().iterator().next();
384 // for the l2interface group, get vlan and port info
385 // for the l3unicast group, get the src/dst mac and vlan info
386 TrafficTreatment.Builder l3utt = DefaultTrafficTreatment.builder();
387 TrafficTreatment.Builder l2itt = DefaultTrafficTreatment.builder();
388 VlanId vlanid = null;
389 long portNum = 0;
390 for (Instruction ins : treatment.allInstructions()) {
391 if (ins.type() == Instruction.Type.L2MODIFICATION) {
392 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
393 switch (l2ins.subtype()) {
394 case ETH_DST:
395 l3utt.setEthDst(((ModEtherInstruction) l2ins).mac());
396 break;
397 case ETH_SRC:
398 l3utt.setEthSrc(((ModEtherInstruction) l2ins).mac());
399 break;
400 case VLAN_ID:
401 vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
402 l3utt.setVlanId(vlanid);
403 break;
404 case DEC_MPLS_TTL:
405 case MPLS_LABEL:
406 case MPLS_POP:
407 case MPLS_PUSH:
408 case VLAN_PCP:
409 case VLAN_POP:
410 case VLAN_PUSH:
411 default:
412 break;
413 }
414 } else if (ins.type() == Instruction.Type.OUTPUT) {
415 portNum = ((OutputInstruction) ins).port().toLong();
416 l2itt.add(ins);
417 } else {
418 log.warn("Driver does not handle this type of TrafficTreatment"
419 + " instruction in nextObjectives: {}", ins.type());
420 }
421 }
422
423 // assemble information for ofdpa l2interface group
424 int l2gk = nextObj.id() | GROUP1MASK;
425 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
426 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
427
428 // assemble information for ofdpa l3unicast group
429 int l3gk = nextObj.id() | GROUP0MASK;
430 final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
431 Integer l3groupId = L3UNICASTMASK | (int) portNum;
432 l3utt.group(new DefaultGroupId(l2groupId));
433 GroupChainElem gce = new GroupChainElem(l3groupkey, l3groupId,
434 l3utt.build(), nextObj.appId());
435
436 // create object for local and distributed storage
437 List<GroupKey> gkeys = new ArrayList<GroupKey>();
438 gkeys.add(l3groupkey); // group0 in chain
439 gkeys.add(l2groupkey); // group1 in chain
440 OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj);
441
442 // store l2groupkey with the groupChainElem for the l3group that depends on it
443 pendingGroups.put(l2groupkey, gce);
444
445 // store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it
446 pendingNextObjectives.put(l3groupkey, ofdpaGrp);
447
448 // create group description for the ofdpa l2interfacegroup and send to groupservice
449 GroupBucket bucket =
450 DefaultGroupBucket.createIndirectGroupBucket(l2itt.build());
451 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
452 GroupDescription.Type.INDIRECT,
453 new GroupBuckets(Collections.singletonList(bucket)),
454 l2groupkey,
455 l2groupId,
456 nextObj.appId());
457 groupService.addGroup(groupDescription);
458 }
459
460 /**
461 * Processes next element of a group chain. Assumption is that if this
462 * group points to another group, the latter has already been created
463 * and this driver has received notification for it. A second assumption is
464 * that if there is another group waiting for this group then the appropriate
465 * stores already have the information to act upon the notification for the
466 * creating of this group.
467 *
468 * @param gce the group chain element to be processed next
469 */
470 private void processGroupChain(GroupChainElem gce) {
471 GroupBucket bucket = DefaultGroupBucket
472 .createIndirectGroupBucket(gce.getBucketActions());
473 GroupDescription groupDesc = new DefaultGroupDescription(deviceId,
474 GroupDescription.Type.INDIRECT,
475 new GroupBuckets(Collections.singletonList(bucket)),
476 gce.getGkey(),
477 gce.getGivenGroupId(),
478 gce.getAppId());
479 groupService.addGroup(groupDesc);
480 }
481
482 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
483 switch (fwd.flag()) {
484 case SPECIFIC:
485 return processSpecific(fwd);
486 case VERSATILE:
487 return processVersatile(fwd);
488 default:
489 fail(fwd, ObjectiveError.UNKNOWN);
490 log.warn("Unknown forwarding flag {}", fwd.flag());
491 }
492 return Collections.emptySet();
493 }
494
495 /**
496 * In the OF-DPA 1.0 pipeline, versatile forwarding objectives go to the
497 * ACL table.
498 * @param fwd the forwarding objective of type 'versatile'
499 * @return a collection of flow rules to be sent to the switch. An empty
500 * collection may be returned if there is a problem in processing
501 * the flow rule
502 */
503 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
504 log.info("Processing versatile forwarding objective");
505 TrafficSelector selector = fwd.selector();
506
507 EthTypeCriterion ethType =
508 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
509 if (ethType == null) {
510 log.error("Versatile forwarding objective must include ethType");
511 fail(fwd, ObjectiveError.UNKNOWN);
512 return Collections.emptySet();
513 }
alshabibcaf1ca22015-06-25 15:18:16 -0700514 if (ethType.ethType().toShort() == Ethernet.TYPE_ARP) {
Saurav Das100e3b82015-04-30 11:12:10 -0700515 log.warn("Installing ARP rule to table 60");
516
517 // currently need to punt from ACL table should use:
518 // OF apply-actions-instruction
519 // To use OF write-actions-instruction
520 /*TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
521 fwd.treatment().allInstructions().stream()
522 .forEach(ti -> tb.deferred().add(ti));*/
523
524 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
525 .fromApp(fwd.appId())
526 .withPriority(fwd.priority())
527 .forDevice(deviceId)
528 .withSelector(fwd.selector())
529 .withTreatment(fwd.treatment())
530 .makePermanent()
531 .forTable(ACL_TABLE);
532
Saurav Das100e3b82015-04-30 11:12:10 -0700533 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
alshabibcaf1ca22015-06-25 15:18:16 -0700555 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Das100e3b82015-04-30 11:12:10 -0700556 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
Saurav Das558afec2015-05-31 17:12:48 -0700613 protected void initializePipeline() {
Saurav Das100e3b82015-04-30 11:12:10 -0700614 processPortTable();
615 processVlanTable();
616 processTmacTable();
617 processIpTable();
618 //processMcastTable();
619 //processBridgingTable();
620 //processAclTable();
621 //processGroupTable();
Saurav Dasffc5bbc2015-08-18 23:30:19 -0700622 //processMplsTable();
623 }
624
625 protected void processMplsTable() {
626 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
627 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
628 selector.matchEthType(Ethernet.MPLS_UNICAST);
629 selector.matchMplsLabel(MplsLabel.mplsLabel(0xff));
630 selector.matchMplsBos(true);
631 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
632 treatment.popMpls(Ethernet.TYPE_IPV4);
633 treatment.transition(ACL_TABLE);
634 FlowRule test = DefaultFlowRule.builder().forDevice(deviceId)
635 .withSelector(selector.build()).withTreatment(treatment.build())
636 .withPriority(LOWEST_PRIORITY).fromApp(driverId).makePermanent()
637 .forTable(25).build();
638 ops = ops.add(test);
639
640 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
641 @Override
642 public void onSuccess(FlowRuleOperations ops) {
643 log.info("Initialized mpls table");
644 }
645
646 @Override
647 public void onError(FlowRuleOperations ops) {
648 log.info("Failed to initialize mpls table");
649 }
650 }));
651
Saurav Das100e3b82015-04-30 11:12:10 -0700652 }
653
Saurav Das558afec2015-05-31 17:12:48 -0700654 protected void processPortTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700655 //XXX is table miss entry enough or do we need to do the maskable in-port 0?
656 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
657 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
658 selector.matchInPort(PortNumber.portNumber(0)); // should be maskable?
659 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
660 treatment.transition(VLAN_TABLE);
661 FlowRule tmisse = DefaultFlowRule.builder()
662 .forDevice(deviceId)
663 .withSelector(selector.build())
664 .withTreatment(treatment.build())
665 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700666 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700667 .makePermanent()
668 .forTable(PORT_TABLE).build();
Saurav Das88329902015-05-27 23:22:32 -0700669 /*ops = ops.add(tmisse);
Saurav Das100e3b82015-04-30 11:12:10 -0700670
671 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
672 @Override
673 public void onSuccess(FlowRuleOperations ops) {
674 log.info("Initialized port table");
675 }
676
677 @Override
678 public void onError(FlowRuleOperations ops) {
679 log.info("Failed to initialize port table");
680 }
Saurav Das88329902015-05-27 23:22:32 -0700681 }));*/
Saurav Das100e3b82015-04-30 11:12:10 -0700682
683 }
684
685 private void processVlanTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700686 // Table miss entry is not required as ofdpa default is to drop
687 // In OF terms, the absence of a t.m.e. also implies drop
688 }
689
690
Saurav Das558afec2015-05-31 17:12:48 -0700691 protected void processTmacTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700692 //table miss entry
693 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
694 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
695 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
696 selector = DefaultTrafficSelector.builder();
697 treatment = DefaultTrafficTreatment.builder();
698 treatment.transition(BRIDGING_TABLE);
699 FlowRule rule = DefaultFlowRule.builder()
700 .forDevice(deviceId)
701 .withSelector(selector.build())
702 .withTreatment(treatment.build())
703 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700704 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700705 .makePermanent()
706 .forTable(TMAC_TABLE).build();
Saurav Das88329902015-05-27 23:22:32 -0700707 /*ops = ops.add(rule); // XXX bug in ofdpa
Saurav Das100e3b82015-04-30 11:12:10 -0700708 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
709 @Override
710 public void onSuccess(FlowRuleOperations ops) {
711 log.info("Initialized tmac table");
712 }
713
714 @Override
715 public void onError(FlowRuleOperations ops) {
716 log.info("Failed to initialize tmac table");
717 }
Saurav Das88329902015-05-27 23:22:32 -0700718 }));*/
Saurav Das100e3b82015-04-30 11:12:10 -0700719 }
720
Saurav Das558afec2015-05-31 17:12:48 -0700721 protected void processIpTable() {
Saurav Das100e3b82015-04-30 11:12:10 -0700722 //table miss entry
723 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
724 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
725 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
726 selector = DefaultTrafficSelector.builder();
727 treatment = DefaultTrafficTreatment.builder();
728 treatment.transition(ACL_TABLE);
729 FlowRule rule = DefaultFlowRule.builder()
730 .forDevice(deviceId)
731 .withSelector(selector.build())
732 .withTreatment(treatment.build())
733 .withPriority(LOWEST_PRIORITY)
Saurav Dasc39f6032015-05-14 17:12:47 -0700734 .fromApp(driverId)
Saurav Das100e3b82015-04-30 11:12:10 -0700735 .makePermanent()
736 .forTable(UNICAST_ROUTING_TABLE).build();
Saurav Das88329902015-05-27 23:22:32 -0700737 /*ops = ops.add(rule);
Saurav Das100e3b82015-04-30 11:12:10 -0700738 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
739 @Override
740 public void onSuccess(FlowRuleOperations ops) {
741 log.info("Initialized IP table");
742 }
743
744 @Override
745 public void onError(FlowRuleOperations ops) {
746 log.info("Failed to initialize unicast IP table");
747 }
Saurav Das88329902015-05-27 23:22:32 -0700748 }));*/
Saurav Das100e3b82015-04-30 11:12:10 -0700749 }
750
Saurav Das100e3b82015-04-30 11:12:10 -0700751 private class GroupChecker implements Runnable {
752 @Override
753 public void run() {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700754 Set<GroupKey> keys = pendingGroups.keySet().stream()
Saurav Das100e3b82015-04-30 11:12:10 -0700755 .filter(key -> groupService.getGroup(deviceId, key) != null)
756 .collect(Collectors.toSet());
Saurav Das558afec2015-05-31 17:12:48 -0700757 Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream()
758 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
759 .collect(Collectors.toSet());
760 keys.addAll(otherkeys);
Saurav Das100e3b82015-04-30 11:12:10 -0700761
762 keys.stream().forEach(key -> {
763 //first check for group chain
764 GroupChainElem gce = pendingGroups.remove(key);
765 if (gce != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700766 log.info("Group service processed group key {}. Processing next "
767 + "group in group chain with group key {}",
768 appKryo.deserialize(key.key()),
769 appKryo.deserialize(gce.getGkey().key()));
Saurav Das100e3b82015-04-30 11:12:10 -0700770 processGroupChain(gce);
771 } else {
772 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
Saurav Dasf9ba4222015-05-07 17:13:59 -0700773 log.info("Group service processed group key {}. Done implementing "
774 + "next objective: {}", appKryo.deserialize(key.key()),
775 obj.nextObjective().id());
776 if (obj != null) {
777 pass(obj.nextObjective());
778 pendingNextObjectives.invalidate(key);
779 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
Saurav Das100e3b82015-04-30 11:12:10 -0700780 }
Saurav Das100e3b82015-04-30 11:12:10 -0700781 }
782 });
783 }
784 }
785
Saurav Das100e3b82015-04-30 11:12:10 -0700786 private class InnerGroupListener implements GroupListener {
787 @Override
788 public void event(GroupEvent event) {
Saurav Das558afec2015-05-31 17:12:48 -0700789 log.debug("received group event of type {}", event.type());
Saurav Das100e3b82015-04-30 11:12:10 -0700790 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
791 GroupKey key = event.subject().appCookie();
792 // first check for group chain
793 GroupChainElem gce = pendingGroups.remove(key);
794 if (gce != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700795 log.info("group ADDED with group key {} .. "
796 + "Processing next group in group chain with group key {}",
797 appKryo.deserialize(key.key()),
798 appKryo.deserialize(gce.getGkey().key()));
Saurav Das100e3b82015-04-30 11:12:10 -0700799 processGroupChain(gce);
800 } else {
801 OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
Saurav Das100e3b82015-04-30 11:12:10 -0700802 if (obj != null) {
Saurav Dasf9ba4222015-05-07 17:13:59 -0700803 log.info("group ADDED with key {}.. Done implementing next "
804 + "objective: {}",
805 appKryo.deserialize(key.key()), obj.nextObjective().id());
Saurav Das100e3b82015-04-30 11:12:10 -0700806 pass(obj.nextObjective());
807 pendingNextObjectives.invalidate(key);
Saurav Dasf9ba4222015-05-07 17:13:59 -0700808 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
Saurav Das100e3b82015-04-30 11:12:10 -0700809 }
810 }
811 }
812 }
813 }
814
815 /**
816 * Represents a group-chain that implements a Next-Objective from
817 * the application. Includes information about the next objective Id, and the
818 * group keys for the groups in the group chain. The chain is expected to
819 * look like group0 --> group 1 --> outPort. Information about the groups
820 * themselves can be fetched from the Group Service using the group keys from
821 * objects instantiating this class.
822 */
823 private class OfdpaGroupChain implements NextGroup {
824 private final NextObjective nextObj;
825 private final List<GroupKey> gkeys;
826
827 /** expected group chain: group0 --> group1 --> port. */
828 public OfdpaGroupChain(List<GroupKey> gkeys, NextObjective nextObj) {
829 this.gkeys = gkeys;
830 this.nextObj = nextObj;
831 }
832
833 @SuppressWarnings("unused")
834 public List<GroupKey> groupKeys() {
835 return gkeys;
836 }
837
838 public NextObjective nextObjective() {
839 return nextObj;
840 }
841
842 @Override
843 public byte[] data() {
844 return appKryo.serialize(gkeys);
845 }
846
847 }
848
849 /**
850 * Represents a group element that is part of a chain of groups.
851 * Stores enough information to create a Group Description to add the group
852 * to the switch by requesting the Group Service. Objects instantiating this
853 * class are meant to be temporary and live as long as it is needed to wait for
854 * preceding groups in the group chain to be created.
855 */
856 private class GroupChainElem {
857 private TrafficTreatment bucketActions;
858 private Integer givenGroupId;
859 private GroupKey gkey;
860 private ApplicationId appId;
861
862 public GroupChainElem(GroupKey gkey, Integer givenGroupId,
863 TrafficTreatment tr, ApplicationId appId) {
864 this.bucketActions = tr;
865 this.givenGroupId = givenGroupId;
866 this.gkey = gkey;
867 this.appId = appId;
868 }
869
870 public TrafficTreatment getBucketActions() {
871 return bucketActions;
872 }
873
874 public Integer getGivenGroupId() {
875 return givenGroupId;
876 }
877
878 public GroupKey getGkey() {
879 return gkey;
880 }
881
882 public ApplicationId getAppId() {
883 return appId;
884 }
885
886 }
887}