blob: 2f6739509f3d7ec59077193c53b5fa04ff539b92 [file] [log] [blame]
Thomas Vachuska58de4162015-09-10 16:15:33 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Thomas Vachuska58de4162015-09-10 16:15:33 -07003 *
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;
Jonathan Hart29be97d2016-02-02 14:23:48 -080020import org.onlab.packet.IpPrefix;
Saurav Dasdecd7a62015-05-16 22:39:47 -070021import org.onlab.packet.VlanId;
22import org.onlab.util.KryoNamespace;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.net.DeviceId;
Saurav Dasdecd7a62015-05-16 22:39:47 -070026import org.onosproject.net.behaviour.NextGroup;
27import org.onosproject.net.behaviour.Pipeliner;
28import org.onosproject.net.behaviour.PipelinerContext;
29import org.onosproject.net.driver.AbstractHandlerBehaviour;
30import org.onosproject.net.flow.DefaultFlowRule;
31import org.onosproject.net.flow.DefaultTrafficSelector;
32import org.onosproject.net.flow.DefaultTrafficTreatment;
33import org.onosproject.net.flow.FlowRule;
34import org.onosproject.net.flow.FlowRuleOperations;
35import org.onosproject.net.flow.FlowRuleOperationsContext;
36import org.onosproject.net.flow.FlowRuleService;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flow.criteria.Criteria;
40import org.onosproject.net.flow.criteria.Criterion;
41import org.onosproject.net.flow.criteria.EthCriterion;
42import org.onosproject.net.flow.criteria.EthTypeCriterion;
43import org.onosproject.net.flow.criteria.IPCriterion;
44import org.onosproject.net.flow.criteria.PortCriterion;
45import org.onosproject.net.flow.criteria.VlanIdCriterion;
Saurav Das49cb5a12016-01-16 22:54:07 -080046import org.onosproject.net.flow.instructions.Instruction;
47import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Saurav Dasdecd7a62015-05-16 22:39:47 -070048import org.onosproject.net.flowobjective.FilteringObjective;
49import org.onosproject.net.flowobjective.FlowObjectiveStore;
50import org.onosproject.net.flowobjective.ForwardingObjective;
51import org.onosproject.net.flowobjective.NextObjective;
52import org.onosproject.net.flowobjective.Objective;
53import org.onosproject.net.flowobjective.ObjectiveError;
Saurav Dasdecd7a62015-05-16 22:39:47 -070054import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080055import org.slf4j.Logger;
56
57import java.util.ArrayList;
58import java.util.Collection;
59import java.util.Collections;
Saurav Das24431192016-03-07 19:13:00 -080060import java.util.List;
Sho SHIMIZU45906042016-01-13 23:05:54 -080061import java.util.Objects;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080062
Jonathan Hart29be97d2016-02-02 14:23:48 -080063import static org.onlab.util.Tools.delay;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080064import static org.slf4j.LoggerFactory.getLogger;
Saurav Dasdecd7a62015-05-16 22:39:47 -070065
66/**
67 * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline
68 * does not forward IP traffic to next-hop groups. Instead it forwards traffic
69 * using OF FlowMod actions.
70 */
71public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipeliner {
72
73 protected static final int FILTER_TABLE = 0;
74 protected static final int FIB_TABLE = 1;
75
76 private static final int DROP_PRIORITY = 0;
77 private static final int DEFAULT_PRIORITY = 0x8000;
78 private static final int HIGHEST_PRIORITY = 0xffff;
79
80 private ServiceDirectory serviceDirectory;
81 protected FlowRuleService flowRuleService;
82 private CoreService coreService;
83 private FlowObjectiveStore flowObjectiveStore;
84 protected DeviceId deviceId;
85 protected ApplicationId appId;
86 private ApplicationId driverId;
Saurav Dasdecd7a62015-05-16 22:39:47 -070087
88 private KryoNamespace appKryo = new KryoNamespace.Builder()
89 .register(DummyGroup.class)
90 .register(KryoNamespaces.API)
91 .register(byte[].class)
92 .build();
93
94 private final Logger log = getLogger(getClass());
95
96 @Override
97 public void init(DeviceId deviceId, PipelinerContext context) {
98 this.serviceDirectory = context.directory();
99 this.deviceId = deviceId;
100 coreService = serviceDirectory.get(CoreService.class);
101 flowRuleService = serviceDirectory.get(FlowRuleService.class);
102 flowObjectiveStore = context.store();
103 driverId = coreService.registerApplication(
Jonathan Harte54bdbf2015-11-17 11:29:37 -0800104 "org.onosproject.driver.SoftRouterPipeline");
Jonathan Hart6344f572015-12-15 08:26:25 -0800105
Saurav Dasdecd7a62015-05-16 22:39:47 -0700106 initializePipeline();
107 }
108
109 @Override
110 public void filter(FilteringObjective filteringObjective) {
111 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
112 processFilter(filteringObjective,
113 filteringObjective.op() == Objective.Operation.ADD,
114 filteringObjective.appId());
115 } else {
116 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
117 }
118 }
119
120 @Override
121 public void forward(ForwardingObjective fwd) {
122 Collection<FlowRule> rules;
123 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
124
125 rules = processForward(fwd);
126 switch (fwd.op()) {
127 case ADD:
128 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800129 .filter(Objects::nonNull)
Saurav Dasdecd7a62015-05-16 22:39:47 -0700130 .forEach(flowOpsBuilder::add);
131 break;
132 case REMOVE:
133 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800134 .filter(Objects::nonNull)
Saurav Dasdecd7a62015-05-16 22:39:47 -0700135 .forEach(flowOpsBuilder::remove);
136 break;
137 default:
138 fail(fwd, ObjectiveError.UNKNOWN);
139 log.warn("Unknown forwarding type {}", fwd.op());
140 }
141
142
143 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
144 @Override
145 public void onSuccess(FlowRuleOperations ops) {
146 pass(fwd);
147 }
148
149 @Override
150 public void onError(FlowRuleOperations ops) {
151 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
152 }
153 }));
154
155 }
156
157 @Override
158 public void next(NextObjective nextObjective) {
159 switch (nextObjective.type()) {
160 case SIMPLE:
161 Collection<TrafficTreatment> treatments = nextObjective.next();
162 if (treatments.size() != 1) {
163 log.error("Next Objectives of type Simple should only have a "
164 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
165 fail(nextObjective, ObjectiveError.BADPARAMS);
166 return;
167 }
168 processSimpleNextObjective(nextObjective);
169 break;
170 case HASHED:
171 case BROADCAST:
172 case FAILOVER:
173 fail(nextObjective, ObjectiveError.UNSUPPORTED);
174 log.warn("Unsupported next objective type {}", nextObjective.type());
175 break;
176 default:
177 fail(nextObjective, ObjectiveError.UNKNOWN);
178 log.warn("Unknown next objective type {}", nextObjective.type());
179 }
180 }
181
182 private void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -0800183 obj.context().ifPresent(context -> context.onSuccess(obj));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700184 }
185
186 private void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -0800187 obj.context().ifPresent(context -> context.onError(obj, error));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700188 }
189
Saurav Dasdecd7a62015-05-16 22:39:47 -0700190 private void initializePipeline() {
191 //Drop rules for both tables
192 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
193 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
194 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
195
196 treatment.drop();
197
198 FlowRule rule = DefaultFlowRule.builder()
199 .forDevice(deviceId)
200 .withSelector(selector.build())
201 .withTreatment(treatment.build())
202 .withPriority(DROP_PRIORITY)
203 .fromApp(driverId)
204 .makePermanent()
205 .forTable(FILTER_TABLE)
206 .build();
207 ops = ops.add(rule);
208
209 rule = DefaultFlowRule.builder().forDevice(deviceId)
210 .withSelector(selector.build())
211 .withTreatment(treatment.build())
212 .withPriority(DROP_PRIORITY)
213 .fromApp(driverId)
214 .makePermanent()
215 .forTable(FIB_TABLE)
216 .build();
217 ops = ops.add(rule);
218
219 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
220 @Override
221 public void onSuccess(FlowRuleOperations ops) {
222 log.info("Provisioned drop rules in both tables");
223 }
224
225 @Override
226 public void onError(FlowRuleOperations ops) {
227 log.info("Failed to provision drop rules");
228 }
229 }));
230 }
231
232 private void processFilter(FilteringObjective filt, boolean install,
233 ApplicationId applicationId) {
234 // This driver only processes filtering criteria defined with switch
235 // ports as the key
Jonathan Hart6344f572015-12-15 08:26:25 -0800236 PortCriterion p;
237 EthCriterion e = null;
238 VlanIdCriterion v = null;
239
Saurav Dasdecd7a62015-05-16 22:39:47 -0700240 if (!filt.key().equals(Criteria.dummy()) &&
241 filt.key().type() == Criterion.Type.IN_PORT) {
242 p = (PortCriterion) filt.key();
243 } else {
244 log.warn("No key defined in filtering objective from app: {}. Not"
245 + "processing filtering objective", applicationId);
246 fail(filt, ObjectiveError.UNKNOWN);
247 return;
248 }
249
250 // convert filtering conditions for switch-intfs into flowrules
251 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
252 for (Criterion c : filt.conditions()) {
gauravadc90042016-05-09 23:17:05 +0530253 if (c.type() == Criterion.Type.ETH_DST ||
254 c.type() == Criterion.Type.ETH_DST_MASKED) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700255 e = (EthCriterion) c;
256 } else if (c.type() == Criterion.Type.VLAN_VID) {
257 v = (VlanIdCriterion) c;
Saurav Dasdecd7a62015-05-16 22:39:47 -0700258 } else {
259 log.error("Unsupported filter {}", c);
260 fail(filt, ObjectiveError.UNSUPPORTED);
261 return;
262 }
263 }
264
265 log.debug("adding Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
266 p.port(), v.vlanId(), e.mac());
267 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
268 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
269 selector.matchInPort(p.port());
Jonathan Hart6344f572015-12-15 08:26:25 -0800270
gauravadc90042016-05-09 23:17:05 +0530271 //Multicast MAC
272 if (e.mask() != null) {
273 selector.matchEthDstMasked(e.mac(), e.mask());
274 } else {
275 selector.matchEthDst(e.mac());
276 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800277 selector.matchVlanId(v.vlanId());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700278 selector.matchEthType(Ethernet.TYPE_IPV4);
Jonathan Hartca47cd72015-12-13 12:31:09 -0800279 if (!v.vlanId().equals(VlanId.NONE)) {
280 treatment.popVlan();
281 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800282 treatment.transition(FIB_TABLE);
Saurav Dasdecd7a62015-05-16 22:39:47 -0700283 FlowRule rule = DefaultFlowRule.builder()
284 .forDevice(deviceId)
285 .withSelector(selector.build())
286 .withTreatment(treatment.build())
287 .withPriority(DEFAULT_PRIORITY)
288 .fromApp(applicationId)
289 .makePermanent()
290 .forTable(FILTER_TABLE).build();
291 ops = ops.add(rule);
292
Saurav Dasdecd7a62015-05-16 22:39:47 -0700293 ops = install ? ops.add(rule) : ops.remove(rule);
294 // apply filtering flow rules
295 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
296 @Override
297 public void onSuccess(FlowRuleOperations ops) {
298 log.info("Applied filtering rules");
299 pass(filt);
300 }
301
302 @Override
303 public void onError(FlowRuleOperations ops) {
304 log.info("Failed to apply filtering rules");
305 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
306 }
307 }));
308 }
309
310 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
311 switch (fwd.flag()) {
312 case SPECIFIC:
313 return processSpecific(fwd);
314 case VERSATILE:
315 return processVersatile(fwd);
316 default:
317 fail(fwd, ObjectiveError.UNKNOWN);
318 log.warn("Unknown forwarding flag {}", fwd.flag());
319 }
320 return Collections.emptySet();
321 }
322
323 /**
Saurav Das49cb5a12016-01-16 22:54:07 -0800324 * SoftRouter has a single versatile table - the filter table.
325 * This table can be used to filter entries that reach the next table (FIB table).
326 * It can also be used to punt packets to the controller and/or bypass
327 * the FIB table to forward out of a port.
Saurav Dasdecd7a62015-05-16 22:39:47 -0700328 *
329 * @param fwd The forwarding objective of type versatile
330 * @return A collection of flow rules meant to be delivered to the flowrule
331 * subsystem. May return empty collection in case of failures.
332 */
333 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800334 log.debug("Received versatile fwd: to next:{}", fwd.nextId());
Jonathan Hart6344f572015-12-15 08:26:25 -0800335 Collection<FlowRule> flowrules = new ArrayList<>();
Saurav Das49cb5a12016-01-16 22:54:07 -0800336 if (fwd.nextId() == null && fwd.treatment() == null) {
337 log.error("Forwarding objective {} from {} must contain "
338 + "nextId or Treatment", fwd.selector(), fwd.appId());
339 return Collections.emptySet();
340 }
341 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
342 if (fwd.treatment() != null) {
343 fwd.treatment().immediate().forEach(ins -> ttBuilder.add(ins));
344 }
345 //convert nextId to flow actions
346 if (fwd.nextId() != null) {
347 // only acceptable value is output to port
348 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
349 if (next == null) {
350 log.error("next-id {} does not exist in store", fwd.nextId());
351 return Collections.emptySet();
352 }
353 TrafficTreatment nt = appKryo.deserialize(next.data());
354 if (nt == null) {
355 log.error("Error in deserializing next-id {}", fwd.nextId());
356 return Collections.emptySet();
357 }
358 for (Instruction ins : nt.allInstructions()) {
359 if (ins instanceof OutputInstruction) {
360 ttBuilder.add(ins);
361 }
362 }
363 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800364
365 FlowRule rule = DefaultFlowRule.builder()
366 .withSelector(fwd.selector())
Saurav Das49cb5a12016-01-16 22:54:07 -0800367 .withTreatment(ttBuilder.build())
Jonathan Hart6344f572015-12-15 08:26:25 -0800368 .makePermanent()
369 .forDevice(deviceId)
370 .fromApp(fwd.appId())
371 .withPriority(fwd.priority())
372 .build();
373
374 flowrules.add(rule);
375
Saurav Dasdecd7a62015-05-16 22:39:47 -0700376 return flowrules;
377 }
378
Saurav Dasdecd7a62015-05-16 22:39:47 -0700379 /**
380 * SoftRouter has a single specific table - the FIB Table. It emulates
381 * LPM matching of dstIP by using higher priority flows for longer prefixes.
382 * Flows are forwarded using flow-actions
383 *
384 * @param fwd The forwarding objective of type simple
385 * @return A collection of flow rules meant to be delivered to the flowrule
386 * subsystem. Typically the returned collection has a single flowrule.
387 * May return empty collection in case of failures.
388 *
389 */
390 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800391 log.debug("Processing specific forwarding objective to next:{}", fwd.nextId());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700392 TrafficSelector selector = fwd.selector();
393 EthTypeCriterion ethType =
394 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
395 // XXX currently supporting only the L3 unicast table
alshabibcaf1ca22015-06-25 15:18:16 -0700396 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700397 fail(fwd, ObjectiveError.UNSUPPORTED);
398 return Collections.emptySet();
399 }
400
Jonathan Hart29be97d2016-02-02 14:23:48 -0800401 IpPrefix ipPrefix = ((IPCriterion)
402 selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
403
404 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder()
405 .matchEthType(Ethernet.TYPE_IPV4);
406
407 if (ipPrefix.prefixLength() != 0) {
408 filteredSelector.matchIPDst(ipPrefix);
409 }
Saurav Dasdecd7a62015-05-16 22:39:47 -0700410
411 TrafficTreatment tt = null;
412 if (fwd.nextId() != null) {
413 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
414 if (next == null) {
415 log.error("next-id {} does not exist in store", fwd.nextId());
416 return Collections.emptySet();
417 }
418 tt = appKryo.deserialize(next.data());
419 if (tt == null) {
420 log.error("Error in deserializing next-id {}", fwd.nextId());
421 return Collections.emptySet();
422 }
423 }
424
425 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
426 .fromApp(fwd.appId())
427 .withPriority(fwd.priority())
428 .forDevice(deviceId)
Jonathan Hart29be97d2016-02-02 14:23:48 -0800429 .withSelector(filteredSelector.build());
Jonathan Hart3930f632015-10-19 12:12:51 -0700430
431 if (tt != null) {
432 ruleBuilder.withTreatment(tt);
433 }
Saurav Dasdecd7a62015-05-16 22:39:47 -0700434
435 if (fwd.permanent()) {
436 ruleBuilder.makePermanent();
437 } else {
438 ruleBuilder.makeTemporary(fwd.timeout());
439 }
440
441 ruleBuilder.forTable(FIB_TABLE);
442 return Collections.singletonList(ruleBuilder.build());
443 }
444
445 /**
446 * Next Objectives are stored as dummy groups for retrieval later
447 * when Forwarding Objectives reference the next objective id. At that point
448 * the dummy group is fetched from the distributed store and the enclosed
449 * treatment is applied as a flow rule action.
450 *
alshabibcaf1ca22015-06-25 15:18:16 -0700451 * @param nextObj the next objective of type simple
Saurav Dasdecd7a62015-05-16 22:39:47 -0700452 */
453 private void processSimpleNextObjective(NextObjective nextObj) {
454 // Simple next objective has a single treatment (not a collection)
Saurav Das49cb5a12016-01-16 22:54:07 -0800455 log.debug("Received nextObj {}", nextObj.id());
456 // delay processing to emulate group creation
457 delay(50);
Saurav Das6c44a632015-05-30 22:05:22 -0700458 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700459 flowObjectiveStore.putNextGroup(nextObj.id(),
Saurav Das6c44a632015-05-30 22:05:22 -0700460 new DummyGroup(treatment));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700461 }
462
Saurav Dasdecd7a62015-05-16 22:39:47 -0700463 private class DummyGroup implements NextGroup {
464 TrafficTreatment nextActions;
465
466 public DummyGroup(TrafficTreatment next) {
467 this.nextActions = next;
468 }
469
470 @Override
471 public byte[] data() {
472 return appKryo.serialize(nextActions);
473 }
474
475 }
476
Saurav Das24431192016-03-07 19:13:00 -0800477 @Override
478 public List<String> getNextMappings(NextGroup nextGroup) {
479 // nextObjectives converted to flow-actions not groups
480 return Collections.emptyList();
481 }
482
Saurav Dasdecd7a62015-05-16 22:39:47 -0700483}