blob: 03f63b18aebb45a602853b7cb4fa406a378ac932 [file] [log] [blame]
Thomas Vachuska58de4162015-09-10 16:15:33 -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 */
Saurav Dasdecd7a62015-05-16 22:39:47 -070016package org.onosproject.driver.pipeline;
17
Saurav Dasdecd7a62015-05-16 22:39:47 -070018import org.onlab.osgi.ServiceDirectory;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.MacAddress;
21import org.onlab.packet.VlanId;
22import org.onlab.util.KryoNamespace;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.PortNumber;
27import org.onosproject.net.behaviour.NextGroup;
28import org.onosproject.net.behaviour.Pipeliner;
29import org.onosproject.net.behaviour.PipelinerContext;
30import org.onosproject.net.driver.AbstractHandlerBehaviour;
31import org.onosproject.net.flow.DefaultFlowRule;
32import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.DefaultTrafficTreatment;
34import org.onosproject.net.flow.FlowRule;
35import org.onosproject.net.flow.FlowRuleOperations;
36import org.onosproject.net.flow.FlowRuleOperationsContext;
37import org.onosproject.net.flow.FlowRuleService;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.flow.criteria.Criteria;
41import org.onosproject.net.flow.criteria.Criterion;
42import org.onosproject.net.flow.criteria.EthCriterion;
43import org.onosproject.net.flow.criteria.EthTypeCriterion;
44import org.onosproject.net.flow.criteria.IPCriterion;
45import org.onosproject.net.flow.criteria.PortCriterion;
46import org.onosproject.net.flow.criteria.VlanIdCriterion;
47import org.onosproject.net.flowobjective.FilteringObjective;
48import org.onosproject.net.flowobjective.FlowObjectiveStore;
49import org.onosproject.net.flowobjective.ForwardingObjective;
50import org.onosproject.net.flowobjective.NextObjective;
51import org.onosproject.net.flowobjective.Objective;
52import org.onosproject.net.flowobjective.ObjectiveError;
Saurav Dasdecd7a62015-05-16 22:39:47 -070053import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080054import org.slf4j.Logger;
55
56import java.util.ArrayList;
57import java.util.Collection;
58import java.util.Collections;
59import java.util.concurrent.ConcurrentHashMap;
60
61import static org.slf4j.LoggerFactory.getLogger;
Saurav Dasdecd7a62015-05-16 22:39:47 -070062
63/**
64 * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline
65 * does not forward IP traffic to next-hop groups. Instead it forwards traffic
66 * using OF FlowMod actions.
67 */
68public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipeliner {
69
70 protected static final int FILTER_TABLE = 0;
71 protected static final int FIB_TABLE = 1;
72
73 private static final int DROP_PRIORITY = 0;
74 private static final int DEFAULT_PRIORITY = 0x8000;
75 private static final int HIGHEST_PRIORITY = 0xffff;
76
77 private ServiceDirectory serviceDirectory;
78 protected FlowRuleService flowRuleService;
79 private CoreService coreService;
80 private FlowObjectiveStore flowObjectiveStore;
81 protected DeviceId deviceId;
82 protected ApplicationId appId;
83 private ApplicationId driverId;
84 private Collection<Filter> filters;
85 private Collection<ForwardingObjective> pendingVersatiles;
86
87 private KryoNamespace appKryo = new KryoNamespace.Builder()
88 .register(DummyGroup.class)
89 .register(KryoNamespaces.API)
90 .register(byte[].class)
91 .build();
92
93 private final Logger log = getLogger(getClass());
94
95 @Override
96 public void init(DeviceId deviceId, PipelinerContext context) {
97 this.serviceDirectory = context.directory();
98 this.deviceId = deviceId;
99 coreService = serviceDirectory.get(CoreService.class);
100 flowRuleService = serviceDirectory.get(FlowRuleService.class);
101 flowObjectiveStore = context.store();
102 driverId = coreService.registerApplication(
Jonathan Harte54bdbf2015-11-17 11:29:37 -0800103 "org.onosproject.driver.SoftRouterPipeline");
Saurav Dasdecd7a62015-05-16 22:39:47 -0700104 filters = Collections.newSetFromMap(new ConcurrentHashMap<Filter, Boolean>());
105 pendingVersatiles = Collections.newSetFromMap(
106 new ConcurrentHashMap<ForwardingObjective, Boolean>());
107 initializePipeline();
108 }
109
110 @Override
111 public void filter(FilteringObjective filteringObjective) {
112 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
113 processFilter(filteringObjective,
114 filteringObjective.op() == Objective.Operation.ADD,
115 filteringObjective.appId());
116 } else {
117 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
118 }
119 }
120
121 @Override
122 public void forward(ForwardingObjective fwd) {
123 Collection<FlowRule> rules;
124 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
125
126 rules = processForward(fwd);
127 switch (fwd.op()) {
128 case ADD:
129 rules.stream()
130 .filter(rule -> rule != null)
131 .forEach(flowOpsBuilder::add);
132 break;
133 case REMOVE:
134 rules.stream()
135 .filter(rule -> rule != null)
136 .forEach(flowOpsBuilder::remove);
137 break;
138 default:
139 fail(fwd, ObjectiveError.UNKNOWN);
140 log.warn("Unknown forwarding type {}", fwd.op());
141 }
142
143
144 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
145 @Override
146 public void onSuccess(FlowRuleOperations ops) {
147 pass(fwd);
148 }
149
150 @Override
151 public void onError(FlowRuleOperations ops) {
152 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
153 }
154 }));
155
156 }
157
158 @Override
159 public void next(NextObjective nextObjective) {
160 switch (nextObjective.type()) {
161 case SIMPLE:
162 Collection<TrafficTreatment> treatments = nextObjective.next();
163 if (treatments.size() != 1) {
164 log.error("Next Objectives of type Simple should only have a "
165 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
166 fail(nextObjective, ObjectiveError.BADPARAMS);
167 return;
168 }
169 processSimpleNextObjective(nextObjective);
170 break;
171 case HASHED:
172 case BROADCAST:
173 case FAILOVER:
174 fail(nextObjective, ObjectiveError.UNSUPPORTED);
175 log.warn("Unsupported next objective type {}", nextObjective.type());
176 break;
177 default:
178 fail(nextObjective, ObjectiveError.UNKNOWN);
179 log.warn("Unknown next objective type {}", nextObjective.type());
180 }
181 }
182
183 private void pass(Objective obj) {
184 if (obj.context().isPresent()) {
185 obj.context().get().onSuccess(obj);
186 }
187 }
188
189 private void fail(Objective obj, ObjectiveError error) {
190 if (obj.context().isPresent()) {
191 obj.context().get().onError(obj, error);
192 }
193 }
194
195
196 private void initializePipeline() {
197 //Drop rules for both tables
198 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
199 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
200 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
201
202 treatment.drop();
203
204 FlowRule rule = DefaultFlowRule.builder()
205 .forDevice(deviceId)
206 .withSelector(selector.build())
207 .withTreatment(treatment.build())
208 .withPriority(DROP_PRIORITY)
209 .fromApp(driverId)
210 .makePermanent()
211 .forTable(FILTER_TABLE)
212 .build();
213 ops = ops.add(rule);
214
215 rule = DefaultFlowRule.builder().forDevice(deviceId)
216 .withSelector(selector.build())
217 .withTreatment(treatment.build())
218 .withPriority(DROP_PRIORITY)
219 .fromApp(driverId)
220 .makePermanent()
221 .forTable(FIB_TABLE)
222 .build();
223 ops = ops.add(rule);
224
225 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
226 @Override
227 public void onSuccess(FlowRuleOperations ops) {
228 log.info("Provisioned drop rules in both tables");
229 }
230
231 @Override
232 public void onError(FlowRuleOperations ops) {
233 log.info("Failed to provision drop rules");
234 }
235 }));
236 }
237
238 private void processFilter(FilteringObjective filt, boolean install,
239 ApplicationId applicationId) {
240 // This driver only processes filtering criteria defined with switch
241 // ports as the key
242 PortCriterion p; EthCriterion e = null; VlanIdCriterion v = null;
243 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
244 if (!filt.key().equals(Criteria.dummy()) &&
245 filt.key().type() == Criterion.Type.IN_PORT) {
246 p = (PortCriterion) filt.key();
247 } else {
248 log.warn("No key defined in filtering objective from app: {}. Not"
249 + "processing filtering objective", applicationId);
250 fail(filt, ObjectiveError.UNKNOWN);
251 return;
252 }
253
254 // convert filtering conditions for switch-intfs into flowrules
255 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
256 for (Criterion c : filt.conditions()) {
257 if (c.type() == Criterion.Type.ETH_DST) {
258 e = (EthCriterion) c;
259 } else if (c.type() == Criterion.Type.VLAN_VID) {
260 v = (VlanIdCriterion) c;
261 } else if (c.type() == Criterion.Type.IPV4_DST) {
262 ips.add((IPCriterion) c);
263 } else {
264 log.error("Unsupported filter {}", c);
265 fail(filt, ObjectiveError.UNSUPPORTED);
266 return;
267 }
268 }
269
270 log.debug("adding Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
271 p.port(), v.vlanId(), e.mac());
272 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
273 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
274 selector.matchInPort(p.port());
275 selector.matchVlanId(v.vlanId());
276 selector.matchEthDst(e.mac());
277 selector.matchEthType(Ethernet.TYPE_IPV4);
Jonathan Hartca47cd72015-12-13 12:31:09 -0800278 if (!v.vlanId().equals(VlanId.NONE)) {
279 treatment.popVlan();
280 }
Saurav Das6c44a632015-05-30 22:05:22 -0700281 treatment.transition(FIB_TABLE); // all other IPs to the FIB table
Saurav Dasdecd7a62015-05-16 22:39:47 -0700282 FlowRule rule = DefaultFlowRule.builder()
283 .forDevice(deviceId)
284 .withSelector(selector.build())
285 .withTreatment(treatment.build())
286 .withPriority(DEFAULT_PRIORITY)
287 .fromApp(applicationId)
288 .makePermanent()
289 .forTable(FILTER_TABLE).build();
290 ops = ops.add(rule);
291
292 for (IPCriterion ipaddr : ips) {
Saurav Das6c44a632015-05-30 22:05:22 -0700293 log.debug("adding IP filtering rules in FILTER table: {}", ipaddr.ip());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700294 selector = DefaultTrafficSelector.builder();
295 treatment = DefaultTrafficTreatment.builder();
Saurav Das6c44a632015-05-30 22:05:22 -0700296 selector.matchInPort(p.port());
297 selector.matchVlanId(v.vlanId());
298 selector.matchEthDst(e.mac());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700299 selector.matchEthType(Ethernet.TYPE_IPV4);
Saurav Das6c44a632015-05-30 22:05:22 -0700300 selector.matchIPDst(ipaddr.ip()); // router IPs to the controller
Saurav Dasdecd7a62015-05-16 22:39:47 -0700301 treatment.setOutput(PortNumber.CONTROLLER);
302 rule = DefaultFlowRule.builder()
303 .forDevice(deviceId)
304 .withSelector(selector.build())
305 .withTreatment(treatment.build())
306 .withPriority(HIGHEST_PRIORITY)
307 .fromApp(applicationId)
308 .makePermanent()
Saurav Das6c44a632015-05-30 22:05:22 -0700309 .forTable(FILTER_TABLE).build();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700310 ops = ops.add(rule);
311 }
312
313 // cache for later use
314 Filter filter = new Filter(p, e, v, ips);
315 filters.add(filter);
316 // apply any pending versatile forwarding objectives
317 for (ForwardingObjective fwd : pendingVersatiles) {
318 Collection<FlowRule> ret = processVersatilesWithFilters(filter, fwd);
319 for (FlowRule fr : ret) {
320 ops.add(fr);
321 }
322 }
323
324 ops = install ? ops.add(rule) : ops.remove(rule);
325 // apply filtering flow rules
326 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
327 @Override
328 public void onSuccess(FlowRuleOperations ops) {
329 log.info("Applied filtering rules");
330 pass(filt);
331 }
332
333 @Override
334 public void onError(FlowRuleOperations ops) {
335 log.info("Failed to apply filtering rules");
336 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
337 }
338 }));
339 }
340
341 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
342 switch (fwd.flag()) {
343 case SPECIFIC:
344 return processSpecific(fwd);
345 case VERSATILE:
346 return processVersatile(fwd);
347 default:
348 fail(fwd, ObjectiveError.UNKNOWN);
349 log.warn("Unknown forwarding flag {}", fwd.flag());
350 }
351 return Collections.emptySet();
352 }
353
354 /**
355 * SoftRouter has a single versatile table - the filter table. All versatile
356 * flow rules must include the filtering rules.
357 *
358 * @param fwd The forwarding objective of type versatile
359 * @return A collection of flow rules meant to be delivered to the flowrule
360 * subsystem. May return empty collection in case of failures.
361 */
362 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
363 if (filters.isEmpty()) {
364 pendingVersatiles.add(fwd);
365 return Collections.emptySet();
366 }
367 Collection<FlowRule> flowrules = new ArrayList<FlowRule>();
368 for (Filter filter : filters) {
369 flowrules.addAll(processVersatilesWithFilters(filter, fwd));
370 }
371 return flowrules;
372 }
373
374 private Collection<FlowRule> processVersatilesWithFilters(
375 Filter filt, ForwardingObjective fwd) {
376 log.info("Processing versatile forwarding objective");
377 Collection<FlowRule> flows = new ArrayList<FlowRule>();
378 TrafficSelector match = fwd.selector();
379 EthTypeCriterion ethType =
380 (EthTypeCriterion) match.getCriterion(Criterion.Type.ETH_TYPE);
381 if (ethType == null) {
382 log.error("Versatile forwarding objective must include ethType");
383 fail(fwd, ObjectiveError.UNKNOWN);
384 return Collections.emptySet();
385 }
386
alshabibcaf1ca22015-06-25 15:18:16 -0700387 if (ethType.ethType().toShort() == Ethernet.TYPE_ARP) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700388 // need to install ARP request & reply flow rules for each interface filter
389
390 // rule for ARP replies
391 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
392 selector.matchInPort(filt.port());
393 selector.matchVlanId(filt.vlanId());
394 selector.matchEthDst(filt.mac());
395 selector.matchEthType(Ethernet.TYPE_ARP);
396 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
397 .fromApp(fwd.appId())
398 .withPriority(fwd.priority())
399 .forDevice(deviceId)
400 .withSelector(selector.build())
401 .withTreatment(fwd.treatment())
402 .makePermanent()
403 .forTable(FILTER_TABLE);
404 flows.add(ruleBuilder.build());
405
406 //rule for ARP requests
407 selector = DefaultTrafficSelector.builder();
408 selector.matchInPort(filt.port());
409 selector.matchVlanId(filt.vlanId());
410 selector.matchEthDst(MacAddress.BROADCAST);
411 selector.matchEthType(Ethernet.TYPE_ARP);
412 ruleBuilder = DefaultFlowRule.builder()
413 .fromApp(fwd.appId())
414 .withPriority(fwd.priority())
415 .forDevice(deviceId)
416 .withSelector(selector.build())
417 .withTreatment(fwd.treatment())
418 .makePermanent()
419 .forTable(FILTER_TABLE);
420 flows.add(ruleBuilder.build());
421
422 return flows;
423 }
424 // not handling other versatile flows
425 return Collections.emptySet();
426 }
427
428 /**
429 * SoftRouter has a single specific table - the FIB Table. It emulates
430 * LPM matching of dstIP by using higher priority flows for longer prefixes.
431 * Flows are forwarded using flow-actions
432 *
433 * @param fwd The forwarding objective of type simple
434 * @return A collection of flow rules meant to be delivered to the flowrule
435 * subsystem. Typically the returned collection has a single flowrule.
436 * May return empty collection in case of failures.
437 *
438 */
439 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
440 log.debug("Processing specific forwarding objective");
441 TrafficSelector selector = fwd.selector();
442 EthTypeCriterion ethType =
443 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
444 // XXX currently supporting only the L3 unicast table
alshabibcaf1ca22015-06-25 15:18:16 -0700445 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700446 fail(fwd, ObjectiveError.UNSUPPORTED);
447 return Collections.emptySet();
448 }
449
450 TrafficSelector filteredSelector =
451 DefaultTrafficSelector.builder()
452 .matchEthType(Ethernet.TYPE_IPV4)
453 .matchIPDst(((IPCriterion)
454 selector.getCriterion(Criterion.Type.IPV4_DST)).ip())
455 .build();
456
457 TrafficTreatment tt = null;
458 if (fwd.nextId() != null) {
459 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
460 if (next == null) {
461 log.error("next-id {} does not exist in store", fwd.nextId());
462 return Collections.emptySet();
463 }
464 tt = appKryo.deserialize(next.data());
465 if (tt == null) {
466 log.error("Error in deserializing next-id {}", fwd.nextId());
467 return Collections.emptySet();
468 }
469 }
470
471 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
472 .fromApp(fwd.appId())
473 .withPriority(fwd.priority())
474 .forDevice(deviceId)
475 .withSelector(filteredSelector)
476 .withTreatment(tt);
477
478 if (fwd.permanent()) {
479 ruleBuilder.makePermanent();
480 } else {
481 ruleBuilder.makeTemporary(fwd.timeout());
482 }
483
484 ruleBuilder.forTable(FIB_TABLE);
485 return Collections.singletonList(ruleBuilder.build());
486 }
487
488 /**
489 * Next Objectives are stored as dummy groups for retrieval later
490 * when Forwarding Objectives reference the next objective id. At that point
491 * the dummy group is fetched from the distributed store and the enclosed
492 * treatment is applied as a flow rule action.
493 *
alshabibcaf1ca22015-06-25 15:18:16 -0700494 * @param nextObj the next objective of type simple
Saurav Dasdecd7a62015-05-16 22:39:47 -0700495 */
496 private void processSimpleNextObjective(NextObjective nextObj) {
497 // Simple next objective has a single treatment (not a collection)
Saurav Das6c44a632015-05-30 22:05:22 -0700498 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700499 flowObjectiveStore.putNextGroup(nextObj.id(),
Saurav Das6c44a632015-05-30 22:05:22 -0700500 new DummyGroup(treatment));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700501 }
502
503 private class Filter {
504 private PortCriterion port;
505 private VlanIdCriterion vlan;
506 private EthCriterion eth;
507
508 @SuppressWarnings("unused")
509 private Collection<IPCriterion> ips;
510
511 public Filter(PortCriterion p, EthCriterion e, VlanIdCriterion v,
512 Collection<IPCriterion> ips) {
513 this.eth = e;
514 this.port = p;
515 this.vlan = v;
516 this.ips = ips;
517 }
518
519 public PortNumber port() {
520 return port.port();
521 }
522
523 public VlanId vlanId() {
524 return vlan.vlanId();
525 }
526
527 public MacAddress mac() {
528 return eth.mac();
529 }
530 }
531
532 private class DummyGroup implements NextGroup {
533 TrafficTreatment nextActions;
534
535 public DummyGroup(TrafficTreatment next) {
536 this.nextActions = next;
537 }
538
539 @Override
540 public byte[] data() {
541 return appKryo.serialize(nextActions);
542 }
543
544 }
545
546}