blob: be1c00d7bd1b7179f1688498fcb073ca1f66cf13 [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;
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;
Sho SHIMIZU45906042016-01-13 23:05:54 -080060import java.util.Objects;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080061
Jonathan Hart29be97d2016-02-02 14:23:48 -080062import static org.onlab.util.Tools.delay;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080063import static org.slf4j.LoggerFactory.getLogger;
Saurav Dasdecd7a62015-05-16 22:39:47 -070064
65/**
66 * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline
67 * does not forward IP traffic to next-hop groups. Instead it forwards traffic
68 * using OF FlowMod actions.
69 */
70public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipeliner {
71
72 protected static final int FILTER_TABLE = 0;
73 protected static final int FIB_TABLE = 1;
74
75 private static final int DROP_PRIORITY = 0;
76 private static final int DEFAULT_PRIORITY = 0x8000;
77 private static final int HIGHEST_PRIORITY = 0xffff;
78
79 private ServiceDirectory serviceDirectory;
80 protected FlowRuleService flowRuleService;
81 private CoreService coreService;
82 private FlowObjectiveStore flowObjectiveStore;
83 protected DeviceId deviceId;
84 protected ApplicationId appId;
85 private ApplicationId driverId;
Saurav Dasdecd7a62015-05-16 22:39:47 -070086
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");
Jonathan Hart6344f572015-12-15 08:26:25 -0800104
Saurav Dasdecd7a62015-05-16 22:39:47 -0700105 initializePipeline();
106 }
107
108 @Override
109 public void filter(FilteringObjective filteringObjective) {
110 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
111 processFilter(filteringObjective,
112 filteringObjective.op() == Objective.Operation.ADD,
113 filteringObjective.appId());
114 } else {
115 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
116 }
117 }
118
119 @Override
120 public void forward(ForwardingObjective fwd) {
121 Collection<FlowRule> rules;
122 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
123
124 rules = processForward(fwd);
125 switch (fwd.op()) {
126 case ADD:
127 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800128 .filter(Objects::nonNull)
Saurav Dasdecd7a62015-05-16 22:39:47 -0700129 .forEach(flowOpsBuilder::add);
130 break;
131 case REMOVE:
132 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800133 .filter(Objects::nonNull)
Saurav Dasdecd7a62015-05-16 22:39:47 -0700134 .forEach(flowOpsBuilder::remove);
135 break;
136 default:
137 fail(fwd, ObjectiveError.UNKNOWN);
138 log.warn("Unknown forwarding type {}", fwd.op());
139 }
140
141
142 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
143 @Override
144 public void onSuccess(FlowRuleOperations ops) {
145 pass(fwd);
146 }
147
148 @Override
149 public void onError(FlowRuleOperations ops) {
150 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
151 }
152 }));
153
154 }
155
156 @Override
157 public void next(NextObjective nextObjective) {
158 switch (nextObjective.type()) {
159 case SIMPLE:
160 Collection<TrafficTreatment> treatments = nextObjective.next();
161 if (treatments.size() != 1) {
162 log.error("Next Objectives of type Simple should only have a "
163 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
164 fail(nextObjective, ObjectiveError.BADPARAMS);
165 return;
166 }
167 processSimpleNextObjective(nextObjective);
168 break;
169 case HASHED:
170 case BROADCAST:
171 case FAILOVER:
172 fail(nextObjective, ObjectiveError.UNSUPPORTED);
173 log.warn("Unsupported next objective type {}", nextObjective.type());
174 break;
175 default:
176 fail(nextObjective, ObjectiveError.UNKNOWN);
177 log.warn("Unknown next objective type {}", nextObjective.type());
178 }
179 }
180
181 private void pass(Objective obj) {
182 if (obj.context().isPresent()) {
183 obj.context().get().onSuccess(obj);
184 }
185 }
186
187 private void fail(Objective obj, ObjectiveError error) {
188 if (obj.context().isPresent()) {
189 obj.context().get().onError(obj, error);
190 }
191 }
192
Saurav Dasdecd7a62015-05-16 22:39:47 -0700193 private void initializePipeline() {
194 //Drop rules for both tables
195 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
196 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
197 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
198
199 treatment.drop();
200
201 FlowRule rule = DefaultFlowRule.builder()
202 .forDevice(deviceId)
203 .withSelector(selector.build())
204 .withTreatment(treatment.build())
205 .withPriority(DROP_PRIORITY)
206 .fromApp(driverId)
207 .makePermanent()
208 .forTable(FILTER_TABLE)
209 .build();
210 ops = ops.add(rule);
211
212 rule = DefaultFlowRule.builder().forDevice(deviceId)
213 .withSelector(selector.build())
214 .withTreatment(treatment.build())
215 .withPriority(DROP_PRIORITY)
216 .fromApp(driverId)
217 .makePermanent()
218 .forTable(FIB_TABLE)
219 .build();
220 ops = ops.add(rule);
221
222 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
223 @Override
224 public void onSuccess(FlowRuleOperations ops) {
225 log.info("Provisioned drop rules in both tables");
226 }
227
228 @Override
229 public void onError(FlowRuleOperations ops) {
230 log.info("Failed to provision drop rules");
231 }
232 }));
233 }
234
235 private void processFilter(FilteringObjective filt, boolean install,
236 ApplicationId applicationId) {
237 // This driver only processes filtering criteria defined with switch
238 // ports as the key
Jonathan Hart6344f572015-12-15 08:26:25 -0800239 PortCriterion p;
240 EthCriterion e = null;
241 VlanIdCriterion v = null;
242
Saurav Dasdecd7a62015-05-16 22:39:47 -0700243 if (!filt.key().equals(Criteria.dummy()) &&
244 filt.key().type() == Criterion.Type.IN_PORT) {
245 p = (PortCriterion) filt.key();
246 } else {
247 log.warn("No key defined in filtering objective from app: {}. Not"
248 + "processing filtering objective", applicationId);
249 fail(filt, ObjectiveError.UNKNOWN);
250 return;
251 }
252
253 // convert filtering conditions for switch-intfs into flowrules
254 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
255 for (Criterion c : filt.conditions()) {
256 if (c.type() == Criterion.Type.ETH_DST) {
257 e = (EthCriterion) c;
258 } else if (c.type() == Criterion.Type.VLAN_VID) {
259 v = (VlanIdCriterion) c;
Saurav Dasdecd7a62015-05-16 22:39:47 -0700260 } else {
261 log.error("Unsupported filter {}", c);
262 fail(filt, ObjectiveError.UNSUPPORTED);
263 return;
264 }
265 }
266
267 log.debug("adding Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
268 p.port(), v.vlanId(), e.mac());
269 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
270 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
271 selector.matchInPort(p.port());
Jonathan Hart6344f572015-12-15 08:26:25 -0800272
Saurav Dasdecd7a62015-05-16 22:39:47 -0700273 selector.matchEthDst(e.mac());
Jonathan Hart6344f572015-12-15 08:26:25 -0800274 selector.matchVlanId(v.vlanId());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700275 selector.matchEthType(Ethernet.TYPE_IPV4);
Jonathan Hartca47cd72015-12-13 12:31:09 -0800276 if (!v.vlanId().equals(VlanId.NONE)) {
277 treatment.popVlan();
278 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800279 treatment.transition(FIB_TABLE);
Saurav Dasdecd7a62015-05-16 22:39:47 -0700280 FlowRule rule = DefaultFlowRule.builder()
281 .forDevice(deviceId)
282 .withSelector(selector.build())
283 .withTreatment(treatment.build())
284 .withPriority(DEFAULT_PRIORITY)
285 .fromApp(applicationId)
286 .makePermanent()
287 .forTable(FILTER_TABLE).build();
288 ops = ops.add(rule);
289
Saurav Dasdecd7a62015-05-16 22:39:47 -0700290 ops = install ? ops.add(rule) : ops.remove(rule);
291 // apply filtering flow rules
292 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
293 @Override
294 public void onSuccess(FlowRuleOperations ops) {
295 log.info("Applied filtering rules");
296 pass(filt);
297 }
298
299 @Override
300 public void onError(FlowRuleOperations ops) {
301 log.info("Failed to apply filtering rules");
302 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
303 }
304 }));
305 }
306
307 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
308 switch (fwd.flag()) {
309 case SPECIFIC:
310 return processSpecific(fwd);
311 case VERSATILE:
312 return processVersatile(fwd);
313 default:
314 fail(fwd, ObjectiveError.UNKNOWN);
315 log.warn("Unknown forwarding flag {}", fwd.flag());
316 }
317 return Collections.emptySet();
318 }
319
320 /**
Saurav Das49cb5a12016-01-16 22:54:07 -0800321 * SoftRouter has a single versatile table - the filter table.
322 * This table can be used to filter entries that reach the next table (FIB table).
323 * It can also be used to punt packets to the controller and/or bypass
324 * the FIB table to forward out of a port.
Saurav Dasdecd7a62015-05-16 22:39:47 -0700325 *
326 * @param fwd The forwarding objective of type versatile
327 * @return A collection of flow rules meant to be delivered to the flowrule
328 * subsystem. May return empty collection in case of failures.
329 */
330 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800331 log.debug("Received versatile fwd: to next:{}", fwd.nextId());
Jonathan Hart6344f572015-12-15 08:26:25 -0800332 Collection<FlowRule> flowrules = new ArrayList<>();
Saurav Das49cb5a12016-01-16 22:54:07 -0800333 if (fwd.nextId() == null && fwd.treatment() == null) {
334 log.error("Forwarding objective {} from {} must contain "
335 + "nextId or Treatment", fwd.selector(), fwd.appId());
336 return Collections.emptySet();
337 }
338 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
339 if (fwd.treatment() != null) {
340 fwd.treatment().immediate().forEach(ins -> ttBuilder.add(ins));
341 }
342 //convert nextId to flow actions
343 if (fwd.nextId() != null) {
344 // only acceptable value is output to port
345 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
346 if (next == null) {
347 log.error("next-id {} does not exist in store", fwd.nextId());
348 return Collections.emptySet();
349 }
350 TrafficTreatment nt = appKryo.deserialize(next.data());
351 if (nt == null) {
352 log.error("Error in deserializing next-id {}", fwd.nextId());
353 return Collections.emptySet();
354 }
355 for (Instruction ins : nt.allInstructions()) {
356 if (ins instanceof OutputInstruction) {
357 ttBuilder.add(ins);
358 }
359 }
360 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800361
362 FlowRule rule = DefaultFlowRule.builder()
363 .withSelector(fwd.selector())
Saurav Das49cb5a12016-01-16 22:54:07 -0800364 .withTreatment(ttBuilder.build())
Jonathan Hart6344f572015-12-15 08:26:25 -0800365 .makePermanent()
366 .forDevice(deviceId)
367 .fromApp(fwd.appId())
368 .withPriority(fwd.priority())
369 .build();
370
371 flowrules.add(rule);
372
Saurav Dasdecd7a62015-05-16 22:39:47 -0700373 return flowrules;
374 }
375
Saurav Dasdecd7a62015-05-16 22:39:47 -0700376 /**
377 * SoftRouter has a single specific table - the FIB Table. It emulates
378 * LPM matching of dstIP by using higher priority flows for longer prefixes.
379 * Flows are forwarded using flow-actions
380 *
381 * @param fwd The forwarding objective of type simple
382 * @return A collection of flow rules meant to be delivered to the flowrule
383 * subsystem. Typically the returned collection has a single flowrule.
384 * May return empty collection in case of failures.
385 *
386 */
387 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800388 log.debug("Processing specific forwarding objective to next:{}", fwd.nextId());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700389 TrafficSelector selector = fwd.selector();
390 EthTypeCriterion ethType =
391 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
392 // XXX currently supporting only the L3 unicast table
alshabibcaf1ca22015-06-25 15:18:16 -0700393 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700394 fail(fwd, ObjectiveError.UNSUPPORTED);
395 return Collections.emptySet();
396 }
397
Jonathan Hart29be97d2016-02-02 14:23:48 -0800398 IpPrefix ipPrefix = ((IPCriterion)
399 selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
400
401 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder()
402 .matchEthType(Ethernet.TYPE_IPV4);
403
404 if (ipPrefix.prefixLength() != 0) {
405 filteredSelector.matchIPDst(ipPrefix);
406 }
Saurav Dasdecd7a62015-05-16 22:39:47 -0700407
408 TrafficTreatment tt = null;
409 if (fwd.nextId() != null) {
410 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
411 if (next == null) {
412 log.error("next-id {} does not exist in store", fwd.nextId());
413 return Collections.emptySet();
414 }
415 tt = appKryo.deserialize(next.data());
416 if (tt == null) {
417 log.error("Error in deserializing next-id {}", fwd.nextId());
418 return Collections.emptySet();
419 }
420 }
421
422 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
423 .fromApp(fwd.appId())
424 .withPriority(fwd.priority())
425 .forDevice(deviceId)
Jonathan Hart29be97d2016-02-02 14:23:48 -0800426 .withSelector(filteredSelector.build());
Jonathan Hart3930f632015-10-19 12:12:51 -0700427
428 if (tt != null) {
429 ruleBuilder.withTreatment(tt);
430 }
Saurav Dasdecd7a62015-05-16 22:39:47 -0700431
432 if (fwd.permanent()) {
433 ruleBuilder.makePermanent();
434 } else {
435 ruleBuilder.makeTemporary(fwd.timeout());
436 }
437
438 ruleBuilder.forTable(FIB_TABLE);
439 return Collections.singletonList(ruleBuilder.build());
440 }
441
442 /**
443 * Next Objectives are stored as dummy groups for retrieval later
444 * when Forwarding Objectives reference the next objective id. At that point
445 * the dummy group is fetched from the distributed store and the enclosed
446 * treatment is applied as a flow rule action.
447 *
alshabibcaf1ca22015-06-25 15:18:16 -0700448 * @param nextObj the next objective of type simple
Saurav Dasdecd7a62015-05-16 22:39:47 -0700449 */
450 private void processSimpleNextObjective(NextObjective nextObj) {
451 // Simple next objective has a single treatment (not a collection)
Saurav Das49cb5a12016-01-16 22:54:07 -0800452 log.debug("Received nextObj {}", nextObj.id());
453 // delay processing to emulate group creation
454 delay(50);
Saurav Das6c44a632015-05-30 22:05:22 -0700455 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700456 flowObjectiveStore.putNextGroup(nextObj.id(),
Saurav Das6c44a632015-05-30 22:05:22 -0700457 new DummyGroup(treatment));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700458 }
459
Saurav Dasdecd7a62015-05-16 22:39:47 -0700460 private class DummyGroup implements NextGroup {
461 TrafficTreatment nextActions;
462
463 public DummyGroup(TrafficTreatment next) {
464 this.nextActions = next;
465 }
466
467 @Override
468 public byte[] data() {
469 return appKryo.serialize(nextActions);
470 }
471
472 }
473
474}