blob: 4e8d326e1228e60fa934454ead4c49101d6c1aba [file] [log] [blame]
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -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 com.google.common.cache.Cache;
22import com.google.common.cache.CacheBuilder;
23import com.google.common.cache.RemovalCause;
24import com.google.common.cache.RemovalNotification;
25
26import org.onlab.osgi.ServiceDirectory;
27import org.onlab.packet.Ethernet;
28import org.onlab.packet.VlanId;
29import org.onlab.util.KryoNamespace;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.behaviour.NextGroup;
35import org.onosproject.net.behaviour.Pipeliner;
36import org.onosproject.net.behaviour.PipelinerContext;
37import org.onosproject.net.driver.AbstractHandlerBehaviour;
38import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.FlowRuleOperations;
43import org.onosproject.net.flow.FlowRuleOperationsContext;
44import org.onosproject.net.flow.FlowRuleService;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.flow.criteria.Criteria;
48import org.onosproject.net.flow.criteria.Criterion;
49import org.onosproject.net.flow.criteria.EthCriterion;
50import org.onosproject.net.flow.criteria.EthTypeCriterion;
51import org.onosproject.net.flow.criteria.IPCriterion;
52import org.onosproject.net.flow.criteria.MplsCriterion;
53import org.onosproject.net.flow.criteria.PortCriterion;
54import org.onosproject.net.flow.criteria.VlanIdCriterion;
55import org.onosproject.net.flow.instructions.Instruction;
56import org.onosproject.net.flowobjective.FilteringObjective;
57import org.onosproject.net.flowobjective.FlowObjectiveStore;
58import org.onosproject.net.flowobjective.ForwardingObjective;
59import org.onosproject.net.flowobjective.NextObjective;
60import org.onosproject.net.flowobjective.Objective;
61import org.onosproject.net.flowobjective.ObjectiveError;
62import org.onosproject.net.group.DefaultGroupBucket;
63import org.onosproject.net.group.DefaultGroupDescription;
64import org.onosproject.net.group.DefaultGroupKey;
65import org.onosproject.net.group.Group;
66import org.onosproject.net.group.GroupBucket;
67import org.onosproject.net.group.GroupBuckets;
68import org.onosproject.net.group.GroupDescription;
69import org.onosproject.net.group.GroupEvent;
70import org.onosproject.net.group.GroupKey;
71import org.onosproject.net.group.GroupListener;
72import org.onosproject.net.group.GroupService;
73import org.slf4j.Logger;
74
75import java.util.ArrayList;
76import java.util.Collection;
77import java.util.Collections;
78import java.util.List;
79import java.util.Set;
80import java.util.concurrent.Executors;
81import java.util.concurrent.ScheduledExecutorService;
82import java.util.concurrent.TimeUnit;
83import java.util.stream.Collectors;
84
85/**
86 * Driver for SPRING-OPEN pipeline.
87 */
88public class SpringOpenTTP extends AbstractHandlerBehaviour
89 implements Pipeliner {
90
91 // Default table ID - compatible with CpqD switch
92 private static final int TABLE_VLAN = 0;
93 private static final int TABLE_TMAC = 1;
94 private static final int TABLE_IPV4_UNICAST = 2;
95 private static final int TABLE_MPLS = 3;
96 private static final int TABLE_ACL = 5;
97
98 /**
99 * Set the default values. These variables will get overwritten based on the
100 * switch vendor type
101 */
102 protected int vlanTableId = TABLE_VLAN;
103 protected int tmacTableId = TABLE_TMAC;
104 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
105 protected int mplsTableId = TABLE_MPLS;
106 protected int aclTableId = TABLE_ACL;
107
108 protected final Logger log = getLogger(getClass());
109
110 private ServiceDirectory serviceDirectory;
111 private FlowRuleService flowRuleService;
112 private CoreService coreService;
113 protected GroupService groupService;
114 protected FlowObjectiveStore flowObjectiveStore;
115 protected DeviceId deviceId;
116 private ApplicationId appId;
117
118 private Cache<GroupKey, NextObjective> pendingGroups;
119
120 private ScheduledExecutorService groupChecker = Executors
121 .newScheduledThreadPool(2,
122 groupedThreads("onos/pipeliner",
123 "spring-open-%d"));
124 protected KryoNamespace appKryo = new KryoNamespace.Builder()
125 .register(GroupKey.class).register(DefaultGroupKey.class)
126 .register(SegmentRoutingGroup.class).register(byte[].class).build();
127
128 @Override
129 public void init(DeviceId deviceId, PipelinerContext context) {
130 this.serviceDirectory = context.directory();
131 this.deviceId = deviceId;
132
133 pendingGroups = CacheBuilder
134 .newBuilder()
135 .expireAfterWrite(20, TimeUnit.SECONDS)
136 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
137 if (notification.getCause() == RemovalCause.EXPIRED) {
138 fail(notification.getValue(),
139 ObjectiveError.GROUPINSTALLATIONFAILED);
140 }
141 }).build();
142
143 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500,
144 TimeUnit.MILLISECONDS);
145
146 coreService = serviceDirectory.get(CoreService.class);
147 flowRuleService = serviceDirectory.get(FlowRuleService.class);
148 groupService = serviceDirectory.get(GroupService.class);
149 flowObjectiveStore = context.store();
150
151 groupService.addListener(new InnerGroupListener());
152
153 appId = coreService
154 .registerApplication("org.onosproject.driver.SpringOpenTTP");
155
156 setTableMissEntries();
157 log.info("Spring Open TTP driver initialized");
158 }
159
160 @Override
161 public void filter(FilteringObjective filteringObjective) {
162 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
163 log.debug("processing PERMIT filter objective");
164 processFilter(filteringObjective,
165 filteringObjective.op() == Objective.Operation.ADD,
166 filteringObjective.appId());
167 } else {
168 log.debug("filter objective other than PERMIT not supported");
169 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
170 }
171 }
172
173 @Override
174 public void forward(ForwardingObjective fwd) {
175 Collection<FlowRule> rules;
176 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
177
178 rules = processForward(fwd);
179 switch (fwd.op()) {
180 case ADD:
181 rules.stream().filter(rule -> rule != null)
182 .forEach(flowBuilder::add);
183 break;
184 case REMOVE:
185 rules.stream().filter(rule -> rule != null)
186 .forEach(flowBuilder::remove);
187 break;
188 default:
189 fail(fwd, ObjectiveError.UNKNOWN);
190 log.warn("Unknown forwarding type {}", fwd.op());
191 }
192
193 flowRuleService.apply(flowBuilder
194 .build(new FlowRuleOperationsContext() {
195 @Override
196 public void onSuccess(FlowRuleOperations ops) {
197 pass(fwd);
198 }
199
200 @Override
201 public void onError(FlowRuleOperations ops) {
202 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
203 }
204 }));
205
206 }
207
208 @Override
209 public void next(NextObjective nextObjective) {
210 switch (nextObjective.type()) {
211 case SIMPLE:
212 log.debug("processing SIMPLE next objective");
213 Collection<TrafficTreatment> treatments = nextObjective.next();
214 if (treatments.size() == 1) {
215 TrafficTreatment treatment = treatments.iterator().next();
216 GroupBucket bucket = DefaultGroupBucket
217 .createIndirectGroupBucket(treatment);
218 final GroupKey key = new DefaultGroupKey(
219 appKryo.serialize(nextObjective
220 .id()));
221 GroupDescription groupDescription = new DefaultGroupDescription(
222 deviceId,
223 GroupDescription.Type.INDIRECT,
224 new GroupBuckets(
225 Collections.singletonList(bucket)),
226 key,
227 nextObjective.appId());
228 groupService.addGroup(groupDescription);
229 pendingGroups.put(key, nextObjective);
230 }
231 break;
232 case HASHED:
233 log.debug("processing HASHED next objective");
234 List<GroupBucket> buckets = nextObjective
235 .next()
236 .stream()
237 .map((treatment) -> DefaultGroupBucket
238 .createSelectGroupBucket(treatment))
239 .collect(Collectors.toList());
240 if (!buckets.isEmpty()) {
241 final GroupKey key = new DefaultGroupKey(
242 appKryo.serialize(nextObjective
243 .id()));
244 GroupDescription groupDescription = new DefaultGroupDescription(
245 deviceId,
246 GroupDescription.Type.SELECT,
247 new GroupBuckets(buckets),
248 key,
249 nextObjective.appId());
250 groupService.addGroup(groupDescription);
251 pendingGroups.put(key, nextObjective);
252 }
253 break;
254 case BROADCAST:
255 case FAILOVER:
256 log.debug("BROADCAST and FAILOVER next objectives not supported");
257 fail(nextObjective, ObjectiveError.UNSUPPORTED);
258 log.warn("Unsupported next objective type {}", nextObjective.type());
259 break;
260 default:
261 fail(nextObjective, ObjectiveError.UNKNOWN);
262 log.warn("Unknown next objective type {}", nextObjective.type());
263 }
264
265 }
266
267 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
268 switch (fwd.flag()) {
269 case SPECIFIC:
270 return processSpecific(fwd);
271 case VERSATILE:
272 return processVersatile(fwd);
273 default:
274 fail(fwd, ObjectiveError.UNKNOWN);
275 log.warn("Unknown forwarding flag {}", fwd.flag());
276 }
277 return Collections.emptySet();
278 }
279
280 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
281 fail(fwd, ObjectiveError.UNSUPPORTED);
282 return Collections.emptySet();
283 }
284
285 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
286 log.debug("Processing specific");
287 TrafficSelector selector = fwd.selector();
288 EthTypeCriterion ethType = (EthTypeCriterion) selector
289 .getCriterion(Criterion.Type.ETH_TYPE);
290 if ((ethType == null) ||
291 ((((short) ethType.ethType()) != Ethernet.TYPE_IPV4) &&
292 (((short) ethType.ethType()) != Ethernet.MPLS_UNICAST))) {
293 log.debug("processSpecific: Unsupported "
294 + "forwarding objective criteraia");
295 fail(fwd, ObjectiveError.UNSUPPORTED);
296 return Collections.emptySet();
297 }
298
299 TrafficSelector.Builder filteredSelectorBuilder =
300 DefaultTrafficSelector.builder();
301 int forTableId = -1;
302 if (((short) ethType.ethType()) == Ethernet.TYPE_IPV4) {
303 filteredSelectorBuilder = filteredSelectorBuilder
304 .matchEthType(Ethernet.TYPE_IPV4)
305 .matchIPDst(((IPCriterion) selector
306 .getCriterion(Criterion.Type.IPV4_DST))
307 .ip());
308 forTableId = ipv4UnicastTableId;
309 log.debug("processing IPv4 specific forwarding objective");
310 } else {
311 filteredSelectorBuilder = filteredSelectorBuilder
312 .matchEthType(Ethernet.MPLS_UNICAST)
313 .matchMplsLabel(((MplsCriterion)
314 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
315 //TODO: Add Match for BoS
316 //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
317 //}
318 forTableId = mplsTableId;
319 log.debug("processing MPLS specific forwarding objective");
320 }
321
322 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
323 .builder();
324 if (fwd.treatment() != null) {
325 for (Instruction i : fwd.treatment().allInstructions()) {
326 treatmentBuilder.add(i);
327 }
328 }
329
330 //TODO: Analyze the forwarding objective here to make
331 //device specific decision such as no ECMP groups in Dell
332 //switches.
333 if (fwd.nextId() != null) {
334 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
335
336 if (next != null) {
337 GroupKey key = appKryo.deserialize(next.data());
338
339 Group group = groupService.getGroup(deviceId, key);
340
341 if (group == null) {
342 log.warn("The group left!");
343 fail(fwd, ObjectiveError.GROUPMISSING);
344 return Collections.emptySet();
345 }
346 treatmentBuilder.group(group.id());
347 log.debug("Adding OUTGROUP action");
348 }
349 }
350
351 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
352 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
353 .build();
354
355 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
356 .fromApp(fwd.appId()).withPriority(fwd.priority())
357 .forDevice(deviceId).withSelector(filteredSelector)
358 .withTreatment(treatment);
359
360 if (fwd.permanent()) {
361 ruleBuilder.makePermanent();
362 } else {
363 ruleBuilder.makeTemporary(fwd.timeout());
364 }
365
366 ruleBuilder.forTable(forTableId);
367 return Collections.singletonList(ruleBuilder.build());
368
369 }
370
371 protected List<FlowRule> processEthDstFilter(Criterion c,
372 FilteringObjective filt,
373 ApplicationId applicationId) {
374 List<FlowRule> rules = new ArrayList<FlowRule>();
375 EthCriterion e = (EthCriterion) c;
376 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
377 .builder();
378 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
379 .builder();
380 selectorIp.matchEthDst(e.mac());
381 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
382 treatmentIp.transition(ipv4UnicastTableId);
383 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
384 .withSelector(selectorIp.build())
385 .withTreatment(treatmentIp.build())
386 .withPriority(filt.priority()).fromApp(applicationId)
387 .makePermanent().forTable(tmacTableId).build();
388 log.debug("adding IP ETH rule for MAC: {}", e.mac());
389 rules.add(ruleIp);
390
391 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
392 .builder();
393 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
394 .builder();
395 selectorMpls.matchEthDst(e.mac());
396 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
397 treatmentMpls.transition(mplsTableId);
398 FlowRule ruleMpls = DefaultFlowRule.builder()
399 .forDevice(deviceId).withSelector(selectorMpls.build())
400 .withTreatment(treatmentMpls.build())
401 .withPriority(filt.priority()).fromApp(applicationId)
402 .makePermanent().forTable(tmacTableId).build();
403 log.debug("adding MPLS ETH rule for MAC: {}", e.mac());
404 rules.add(ruleMpls);
405
406 return rules;
407 }
408
409 private void processFilter(FilteringObjective filt, boolean install,
410 ApplicationId applicationId) {
411 // This driver only processes filtering criteria defined with switch
412 // ports as the key
413 PortCriterion p;
414 if (!filt.key().equals(Criteria.dummy())
415 && filt.key().type() == Criterion.Type.IN_PORT) {
416 p = (PortCriterion) filt.key();
417 } else {
418 log.warn("No key defined in filtering objective from app: {}. Not"
419 + "processing filtering objective", applicationId);
420 fail(filt, ObjectiveError.UNKNOWN);
421 return;
422 }
423 // convert filtering conditions for switch-intfs into flowrules
424 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
425 for (Criterion c : filt.conditions()) {
426 if (c.type() == Criterion.Type.ETH_DST) {
427 for (FlowRule rule : processEthDstFilter(c,
428 filt,
429 applicationId)) {
430 ops = install ? ops.add(rule) : ops.remove(rule);
431 }
432 } else if (c.type() == Criterion.Type.VLAN_VID) {
433 VlanIdCriterion v = (VlanIdCriterion) c;
434 log.debug("adding rule for VLAN: {}", v.vlanId());
435 TrafficSelector.Builder selector = DefaultTrafficSelector
436 .builder();
437 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
438 .builder();
439 if (v.vlanId() != VlanId.NONE) {
440 selector.matchVlanId(v.vlanId());
441 selector.matchInPort(p.port());
442 treatment.deferred().popVlan();
443 }
444 treatment.transition(tmacTableId);
445 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
446 .withSelector(selector.build())
447 .withTreatment(treatment.build())
448 .withPriority(filt.priority()).fromApp(applicationId)
449 .makePermanent().forTable(vlanTableId).build();
450 ops = install ? ops.add(rule) : ops.remove(rule);
451 } else if (c.type() == Criterion.Type.IPV4_DST) {
452 IPCriterion ip = (IPCriterion) c;
453 log.debug("adding rule for IP: {}", ip.ip());
454 TrafficSelector.Builder selector = DefaultTrafficSelector
455 .builder();
456 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
457 .builder();
458 selector.matchEthType(Ethernet.TYPE_IPV4);
459 selector.matchIPDst(ip.ip());
460 treatment.transition(aclTableId);
461 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
462 .withSelector(selector.build())
463 .withTreatment(treatment.build())
464 .withPriority(filt.priority()).fromApp(applicationId)
465 .makePermanent().forTable(ipv4UnicastTableId).build();
466 ops = install ? ops.add(rule) : ops.remove(rule);
467 } else {
468 log.warn("Driver does not currently process filtering condition"
469 + " of type: {}", c.type());
470 fail(filt, ObjectiveError.UNSUPPORTED);
471 }
472 }
473 // apply filtering flow rules
474 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
475 @Override
476 public void onSuccess(FlowRuleOperations ops) {
477 pass(filt);
478 log.info("Provisioned tables for segment router");
479 }
480
481 @Override
482 public void onError(FlowRuleOperations ops) {
483 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
484 log.info("Failed to provision tables for segment router");
485 }
486 }));
487 }
488
489 protected void setTableMissEntries() {
490 // set all table-miss-entries
491 populateTableMissEntry(vlanTableId, true, false, false, -1);
492 populateTableMissEntry(tmacTableId, true, false, false, -1);
493 populateTableMissEntry(ipv4UnicastTableId, false, true, true,
494 aclTableId);
495 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
496 populateTableMissEntry(aclTableId, false, false, false, -1);
497 }
498
499 protected void populateTableMissEntry(int tableToAdd,
500 boolean toControllerNow,
501 boolean toControllerWrite,
502 boolean toTable, int tableToSend) {
503 TrafficSelector selector = DefaultTrafficSelector.builder().build();
504 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
505
506 if (toControllerNow) {
507 tBuilder.setOutput(PortNumber.CONTROLLER);
508 }
509
510 if (toControllerWrite) {
511 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
512 }
513
514 if (toTable) {
515 tBuilder.transition(tableToSend);
516 }
517
518 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
519 .withSelector(selector).withTreatment(tBuilder.build())
520 .withPriority(0).fromApp(appId).makePermanent()
521 .forTable(tableToAdd).build();
522
523 flowRuleService.applyFlowRules(flow);
524 }
525
526 private void pass(Objective obj) {
527 if (obj.context().isPresent()) {
528 obj.context().get().onSuccess(obj);
529 }
530 }
531
532 protected void fail(Objective obj, ObjectiveError error) {
533 if (obj.context().isPresent()) {
534 obj.context().get().onError(obj, error);
535 }
536 }
537
538 private class InnerGroupListener implements GroupListener {
539 @Override
540 public void event(GroupEvent event) {
541 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
542 GroupKey key = event.subject().appCookie();
543
544 NextObjective obj = pendingGroups.getIfPresent(key);
545 if (obj != null) {
546 flowObjectiveStore
547 .putNextGroup(obj.id(),
548 new SegmentRoutingGroup(key));
549 pass(obj);
550 pendingGroups.invalidate(key);
551 }
552 }
553 }
554 }
555
556 private class GroupChecker implements Runnable {
557
558 @Override
559 public void run() {
560 Set<GroupKey> keys = pendingGroups
561 .asMap()
562 .keySet()
563 .stream()
564 .filter(key -> groupService.getGroup(deviceId, key) != null)
565 .collect(Collectors.toSet());
566
567 keys.stream()
568 .forEach(key -> {
569 NextObjective obj = pendingGroups
570 .getIfPresent(key);
571 if (obj == null) {
572 return;
573 }
574 pass(obj);
575 pendingGroups.invalidate(key);
576 flowObjectiveStore.putNextGroup(obj.id(),
577 new SegmentRoutingGroup(
578 key));
579 });
580 }
581 }
582
583 private class SegmentRoutingGroup implements NextGroup {
584
585 private final GroupKey key;
586
587 public SegmentRoutingGroup(GroupKey key) {
588 this.key = key;
589 }
590
591 public GroupKey key() {
592 return key;
593 }
594
595 @Override
596 public byte[] data() {
597 return appKryo.serialize(key);
598 }
599
600 }
601}