blob: 6aa7f915b13764aa3d7a521c3fe3cdb7b451bcba [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;
Saurav Dasdecd7a62015-05-16 22:39:47 -070020import org.onlab.packet.VlanId;
21import org.onlab.util.KryoNamespace;
22import org.onosproject.core.ApplicationId;
23import org.onosproject.core.CoreService;
24import org.onosproject.net.DeviceId;
Saurav Dasdecd7a62015-05-16 22:39:47 -070025import org.onosproject.net.behaviour.NextGroup;
26import org.onosproject.net.behaviour.Pipeliner;
27import org.onosproject.net.behaviour.PipelinerContext;
28import org.onosproject.net.driver.AbstractHandlerBehaviour;
29import org.onosproject.net.flow.DefaultFlowRule;
30import org.onosproject.net.flow.DefaultTrafficSelector;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.FlowRule;
33import org.onosproject.net.flow.FlowRuleOperations;
34import org.onosproject.net.flow.FlowRuleOperationsContext;
35import org.onosproject.net.flow.FlowRuleService;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.flow.criteria.Criteria;
39import org.onosproject.net.flow.criteria.Criterion;
40import org.onosproject.net.flow.criteria.EthCriterion;
41import org.onosproject.net.flow.criteria.EthTypeCriterion;
42import org.onosproject.net.flow.criteria.IPCriterion;
43import org.onosproject.net.flow.criteria.PortCriterion;
44import org.onosproject.net.flow.criteria.VlanIdCriterion;
Saurav Das49cb5a12016-01-16 22:54:07 -080045import org.onosproject.net.flow.instructions.Instruction;
46import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Saurav Dasdecd7a62015-05-16 22:39:47 -070047import 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;
Saurav Das49cb5a12016-01-16 22:54:07 -080055import static org.onlab.util.Tools.delay;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080056
57import java.util.ArrayList;
58import java.util.Collection;
59import java.util.Collections;
Sho SHIMIZU45906042016-01-13 23:05:54 -080060import java.util.Objects;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080061
62import static org.slf4j.LoggerFactory.getLogger;
Saurav Dasdecd7a62015-05-16 22:39:47 -070063
64/**
65 * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline
66 * does not forward IP traffic to next-hop groups. Instead it forwards traffic
67 * using OF FlowMod actions.
68 */
69public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipeliner {
70
71 protected static final int FILTER_TABLE = 0;
72 protected static final int FIB_TABLE = 1;
73
74 private static final int DROP_PRIORITY = 0;
75 private static final int DEFAULT_PRIORITY = 0x8000;
76 private static final int HIGHEST_PRIORITY = 0xffff;
77
78 private ServiceDirectory serviceDirectory;
79 protected FlowRuleService flowRuleService;
80 private CoreService coreService;
81 private FlowObjectiveStore flowObjectiveStore;
82 protected DeviceId deviceId;
83 protected ApplicationId appId;
84 private ApplicationId driverId;
Saurav Dasdecd7a62015-05-16 22:39:47 -070085
86 private KryoNamespace appKryo = new KryoNamespace.Builder()
87 .register(DummyGroup.class)
88 .register(KryoNamespaces.API)
89 .register(byte[].class)
90 .build();
91
92 private final Logger log = getLogger(getClass());
93
94 @Override
95 public void init(DeviceId deviceId, PipelinerContext context) {
96 this.serviceDirectory = context.directory();
97 this.deviceId = deviceId;
98 coreService = serviceDirectory.get(CoreService.class);
99 flowRuleService = serviceDirectory.get(FlowRuleService.class);
100 flowObjectiveStore = context.store();
101 driverId = coreService.registerApplication(
Jonathan Harte54bdbf2015-11-17 11:29:37 -0800102 "org.onosproject.driver.SoftRouterPipeline");
Jonathan Hart6344f572015-12-15 08:26:25 -0800103
Saurav Dasdecd7a62015-05-16 22:39:47 -0700104 initializePipeline();
105 }
106
107 @Override
108 public void filter(FilteringObjective filteringObjective) {
109 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
110 processFilter(filteringObjective,
111 filteringObjective.op() == Objective.Operation.ADD,
112 filteringObjective.appId());
113 } else {
114 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
115 }
116 }
117
118 @Override
119 public void forward(ForwardingObjective fwd) {
120 Collection<FlowRule> rules;
121 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
122
123 rules = processForward(fwd);
124 switch (fwd.op()) {
125 case ADD:
126 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800127 .filter(Objects::nonNull)
Saurav Dasdecd7a62015-05-16 22:39:47 -0700128 .forEach(flowOpsBuilder::add);
129 break;
130 case REMOVE:
131 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800132 .filter(Objects::nonNull)
Saurav Dasdecd7a62015-05-16 22:39:47 -0700133 .forEach(flowOpsBuilder::remove);
134 break;
135 default:
136 fail(fwd, ObjectiveError.UNKNOWN);
137 log.warn("Unknown forwarding type {}", fwd.op());
138 }
139
140
141 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
142 @Override
143 public void onSuccess(FlowRuleOperations ops) {
144 pass(fwd);
145 }
146
147 @Override
148 public void onError(FlowRuleOperations ops) {
149 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
150 }
151 }));
152
153 }
154
155 @Override
156 public void next(NextObjective nextObjective) {
157 switch (nextObjective.type()) {
158 case SIMPLE:
159 Collection<TrafficTreatment> treatments = nextObjective.next();
160 if (treatments.size() != 1) {
161 log.error("Next Objectives of type Simple should only have a "
162 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
163 fail(nextObjective, ObjectiveError.BADPARAMS);
164 return;
165 }
166 processSimpleNextObjective(nextObjective);
167 break;
168 case HASHED:
169 case BROADCAST:
170 case FAILOVER:
171 fail(nextObjective, ObjectiveError.UNSUPPORTED);
172 log.warn("Unsupported next objective type {}", nextObjective.type());
173 break;
174 default:
175 fail(nextObjective, ObjectiveError.UNKNOWN);
176 log.warn("Unknown next objective type {}", nextObjective.type());
177 }
178 }
179
180 private void pass(Objective obj) {
181 if (obj.context().isPresent()) {
182 obj.context().get().onSuccess(obj);
183 }
184 }
185
186 private void fail(Objective obj, ObjectiveError error) {
187 if (obj.context().isPresent()) {
188 obj.context().get().onError(obj, error);
189 }
190 }
191
Saurav Dasdecd7a62015-05-16 22:39:47 -0700192 private void initializePipeline() {
193 //Drop rules for both tables
194 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
195 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
196 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
197
198 treatment.drop();
199
200 FlowRule rule = DefaultFlowRule.builder()
201 .forDevice(deviceId)
202 .withSelector(selector.build())
203 .withTreatment(treatment.build())
204 .withPriority(DROP_PRIORITY)
205 .fromApp(driverId)
206 .makePermanent()
207 .forTable(FILTER_TABLE)
208 .build();
209 ops = ops.add(rule);
210
211 rule = DefaultFlowRule.builder().forDevice(deviceId)
212 .withSelector(selector.build())
213 .withTreatment(treatment.build())
214 .withPriority(DROP_PRIORITY)
215 .fromApp(driverId)
216 .makePermanent()
217 .forTable(FIB_TABLE)
218 .build();
219 ops = ops.add(rule);
220
221 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
222 @Override
223 public void onSuccess(FlowRuleOperations ops) {
224 log.info("Provisioned drop rules in both tables");
225 }
226
227 @Override
228 public void onError(FlowRuleOperations ops) {
229 log.info("Failed to provision drop rules");
230 }
231 }));
232 }
233
234 private void processFilter(FilteringObjective filt, boolean install,
235 ApplicationId applicationId) {
236 // This driver only processes filtering criteria defined with switch
237 // ports as the key
Jonathan Hart6344f572015-12-15 08:26:25 -0800238 PortCriterion p;
239 EthCriterion e = null;
240 VlanIdCriterion v = null;
241
Saurav Dasdecd7a62015-05-16 22:39:47 -0700242 if (!filt.key().equals(Criteria.dummy()) &&
243 filt.key().type() == Criterion.Type.IN_PORT) {
244 p = (PortCriterion) filt.key();
245 } else {
246 log.warn("No key defined in filtering objective from app: {}. Not"
247 + "processing filtering objective", applicationId);
248 fail(filt, ObjectiveError.UNKNOWN);
249 return;
250 }
251
252 // convert filtering conditions for switch-intfs into flowrules
253 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
254 for (Criterion c : filt.conditions()) {
255 if (c.type() == Criterion.Type.ETH_DST) {
256 e = (EthCriterion) c;
257 } else if (c.type() == Criterion.Type.VLAN_VID) {
258 v = (VlanIdCriterion) c;
Saurav Dasdecd7a62015-05-16 22:39:47 -0700259 } else {
260 log.error("Unsupported filter {}", c);
261 fail(filt, ObjectiveError.UNSUPPORTED);
262 return;
263 }
264 }
265
266 log.debug("adding Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
267 p.port(), v.vlanId(), e.mac());
268 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
269 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
270 selector.matchInPort(p.port());
Jonathan Hart6344f572015-12-15 08:26:25 -0800271
Saurav Dasdecd7a62015-05-16 22:39:47 -0700272 selector.matchEthDst(e.mac());
Jonathan Hart6344f572015-12-15 08:26:25 -0800273 selector.matchVlanId(v.vlanId());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700274 selector.matchEthType(Ethernet.TYPE_IPV4);
Jonathan Hartca47cd72015-12-13 12:31:09 -0800275 if (!v.vlanId().equals(VlanId.NONE)) {
276 treatment.popVlan();
277 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800278 treatment.transition(FIB_TABLE);
Saurav Dasdecd7a62015-05-16 22:39:47 -0700279 FlowRule rule = DefaultFlowRule.builder()
280 .forDevice(deviceId)
281 .withSelector(selector.build())
282 .withTreatment(treatment.build())
283 .withPriority(DEFAULT_PRIORITY)
284 .fromApp(applicationId)
285 .makePermanent()
286 .forTable(FILTER_TABLE).build();
287 ops = ops.add(rule);
288
Saurav Dasdecd7a62015-05-16 22:39:47 -0700289 ops = install ? ops.add(rule) : ops.remove(rule);
290 // apply filtering flow rules
291 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
292 @Override
293 public void onSuccess(FlowRuleOperations ops) {
294 log.info("Applied filtering rules");
295 pass(filt);
296 }
297
298 @Override
299 public void onError(FlowRuleOperations ops) {
300 log.info("Failed to apply filtering rules");
301 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
302 }
303 }));
304 }
305
306 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
307 switch (fwd.flag()) {
308 case SPECIFIC:
309 return processSpecific(fwd);
310 case VERSATILE:
311 return processVersatile(fwd);
312 default:
313 fail(fwd, ObjectiveError.UNKNOWN);
314 log.warn("Unknown forwarding flag {}", fwd.flag());
315 }
316 return Collections.emptySet();
317 }
318
319 /**
Saurav Das49cb5a12016-01-16 22:54:07 -0800320 * SoftRouter has a single versatile table - the filter table.
321 * This table can be used to filter entries that reach the next table (FIB table).
322 * It can also be used to punt packets to the controller and/or bypass
323 * the FIB table to forward out of a port.
Saurav Dasdecd7a62015-05-16 22:39:47 -0700324 *
325 * @param fwd The forwarding objective of type versatile
326 * @return A collection of flow rules meant to be delivered to the flowrule
327 * subsystem. May return empty collection in case of failures.
328 */
329 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800330 log.debug("Received versatile fwd: to next:{}", fwd.nextId());
Jonathan Hart6344f572015-12-15 08:26:25 -0800331 Collection<FlowRule> flowrules = new ArrayList<>();
Saurav Das49cb5a12016-01-16 22:54:07 -0800332 if (fwd.nextId() == null && fwd.treatment() == null) {
333 log.error("Forwarding objective {} from {} must contain "
334 + "nextId or Treatment", fwd.selector(), fwd.appId());
335 return Collections.emptySet();
336 }
337 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
338 if (fwd.treatment() != null) {
339 fwd.treatment().immediate().forEach(ins -> ttBuilder.add(ins));
340 }
341 //convert nextId to flow actions
342 if (fwd.nextId() != null) {
343 // only acceptable value is output to port
344 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
345 if (next == null) {
346 log.error("next-id {} does not exist in store", fwd.nextId());
347 return Collections.emptySet();
348 }
349 TrafficTreatment nt = appKryo.deserialize(next.data());
350 if (nt == null) {
351 log.error("Error in deserializing next-id {}", fwd.nextId());
352 return Collections.emptySet();
353 }
354 for (Instruction ins : nt.allInstructions()) {
355 if (ins instanceof OutputInstruction) {
356 ttBuilder.add(ins);
357 }
358 }
359 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800360
361 FlowRule rule = DefaultFlowRule.builder()
362 .withSelector(fwd.selector())
Saurav Das49cb5a12016-01-16 22:54:07 -0800363 .withTreatment(ttBuilder.build())
Jonathan Hart6344f572015-12-15 08:26:25 -0800364 .makePermanent()
365 .forDevice(deviceId)
366 .fromApp(fwd.appId())
367 .withPriority(fwd.priority())
368 .build();
369
370 flowrules.add(rule);
371
Saurav Dasdecd7a62015-05-16 22:39:47 -0700372 return flowrules;
373 }
374
Saurav Dasdecd7a62015-05-16 22:39:47 -0700375 /**
376 * SoftRouter has a single specific table - the FIB Table. It emulates
377 * LPM matching of dstIP by using higher priority flows for longer prefixes.
378 * Flows are forwarded using flow-actions
379 *
380 * @param fwd The forwarding objective of type simple
381 * @return A collection of flow rules meant to be delivered to the flowrule
382 * subsystem. Typically the returned collection has a single flowrule.
383 * May return empty collection in case of failures.
384 *
385 */
386 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800387 log.debug("Processing specific forwarding objective to next:{}", fwd.nextId());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700388 TrafficSelector selector = fwd.selector();
389 EthTypeCriterion ethType =
390 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
391 // XXX currently supporting only the L3 unicast table
alshabibcaf1ca22015-06-25 15:18:16 -0700392 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700393 fail(fwd, ObjectiveError.UNSUPPORTED);
394 return Collections.emptySet();
395 }
396
397 TrafficSelector filteredSelector =
398 DefaultTrafficSelector.builder()
399 .matchEthType(Ethernet.TYPE_IPV4)
400 .matchIPDst(((IPCriterion)
401 selector.getCriterion(Criterion.Type.IPV4_DST)).ip())
402 .build();
403
404 TrafficTreatment tt = null;
405 if (fwd.nextId() != null) {
406 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
407 if (next == null) {
408 log.error("next-id {} does not exist in store", fwd.nextId());
409 return Collections.emptySet();
410 }
411 tt = appKryo.deserialize(next.data());
412 if (tt == null) {
413 log.error("Error in deserializing next-id {}", fwd.nextId());
414 return Collections.emptySet();
415 }
416 }
417
418 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
419 .fromApp(fwd.appId())
420 .withPriority(fwd.priority())
421 .forDevice(deviceId)
Jonathan Hart3930f632015-10-19 12:12:51 -0700422 .withSelector(filteredSelector);
423
424 if (tt != null) {
425 ruleBuilder.withTreatment(tt);
426 }
Saurav Dasdecd7a62015-05-16 22:39:47 -0700427
428 if (fwd.permanent()) {
429 ruleBuilder.makePermanent();
430 } else {
431 ruleBuilder.makeTemporary(fwd.timeout());
432 }
433
434 ruleBuilder.forTable(FIB_TABLE);
435 return Collections.singletonList(ruleBuilder.build());
436 }
437
438 /**
439 * Next Objectives are stored as dummy groups for retrieval later
440 * when Forwarding Objectives reference the next objective id. At that point
441 * the dummy group is fetched from the distributed store and the enclosed
442 * treatment is applied as a flow rule action.
443 *
alshabibcaf1ca22015-06-25 15:18:16 -0700444 * @param nextObj the next objective of type simple
Saurav Dasdecd7a62015-05-16 22:39:47 -0700445 */
446 private void processSimpleNextObjective(NextObjective nextObj) {
447 // Simple next objective has a single treatment (not a collection)
Saurav Das49cb5a12016-01-16 22:54:07 -0800448 log.debug("Received nextObj {}", nextObj.id());
449 // delay processing to emulate group creation
450 delay(50);
Saurav Das6c44a632015-05-30 22:05:22 -0700451 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700452 flowObjectiveStore.putNextGroup(nextObj.id(),
Saurav Das6c44a632015-05-30 22:05:22 -0700453 new DummyGroup(treatment));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700454 }
455
Saurav Dasdecd7a62015-05-16 22:39:47 -0700456 private class DummyGroup implements NextGroup {
457 TrafficTreatment nextActions;
458
459 public DummyGroup(TrafficTreatment next) {
460 this.nextActions = next;
461 }
462
463 @Override
464 public byte[] data() {
465 return appKryo.serialize(nextActions);
466 }
467
468 }
469
470}