blob: d2c3cd9261c37dc54da208ac223821e6e6228a3d [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;
45import org.onosproject.net.flowobjective.FilteringObjective;
46import org.onosproject.net.flowobjective.FlowObjectiveStore;
47import org.onosproject.net.flowobjective.ForwardingObjective;
48import org.onosproject.net.flowobjective.NextObjective;
49import org.onosproject.net.flowobjective.Objective;
50import org.onosproject.net.flowobjective.ObjectiveError;
Saurav Dasdecd7a62015-05-16 22:39:47 -070051import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080052import org.slf4j.Logger;
53
54import java.util.ArrayList;
55import java.util.Collection;
56import java.util.Collections;
Jonathan Harte54bdbf2015-11-17 11:29:37 -080057
58import static org.slf4j.LoggerFactory.getLogger;
Saurav Dasdecd7a62015-05-16 22:39:47 -070059
60/**
61 * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline
62 * does not forward IP traffic to next-hop groups. Instead it forwards traffic
63 * using OF FlowMod actions.
64 */
65public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipeliner {
66
67 protected static final int FILTER_TABLE = 0;
68 protected static final int FIB_TABLE = 1;
69
70 private static final int DROP_PRIORITY = 0;
71 private static final int DEFAULT_PRIORITY = 0x8000;
72 private static final int HIGHEST_PRIORITY = 0xffff;
73
74 private ServiceDirectory serviceDirectory;
75 protected FlowRuleService flowRuleService;
76 private CoreService coreService;
77 private FlowObjectiveStore flowObjectiveStore;
78 protected DeviceId deviceId;
79 protected ApplicationId appId;
80 private ApplicationId driverId;
Saurav Dasdecd7a62015-05-16 22:39:47 -070081
82 private KryoNamespace appKryo = new KryoNamespace.Builder()
83 .register(DummyGroup.class)
84 .register(KryoNamespaces.API)
85 .register(byte[].class)
86 .build();
87
88 private final Logger log = getLogger(getClass());
89
90 @Override
91 public void init(DeviceId deviceId, PipelinerContext context) {
92 this.serviceDirectory = context.directory();
93 this.deviceId = deviceId;
94 coreService = serviceDirectory.get(CoreService.class);
95 flowRuleService = serviceDirectory.get(FlowRuleService.class);
96 flowObjectiveStore = context.store();
97 driverId = coreService.registerApplication(
Jonathan Harte54bdbf2015-11-17 11:29:37 -080098 "org.onosproject.driver.SoftRouterPipeline");
Jonathan Hart6344f572015-12-15 08:26:25 -080099
Saurav Dasdecd7a62015-05-16 22:39:47 -0700100 initializePipeline();
101 }
102
103 @Override
104 public void filter(FilteringObjective filteringObjective) {
105 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
106 processFilter(filteringObjective,
107 filteringObjective.op() == Objective.Operation.ADD,
108 filteringObjective.appId());
109 } else {
110 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
111 }
112 }
113
114 @Override
115 public void forward(ForwardingObjective fwd) {
116 Collection<FlowRule> rules;
117 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
118
119 rules = processForward(fwd);
120 switch (fwd.op()) {
121 case ADD:
122 rules.stream()
123 .filter(rule -> rule != null)
124 .forEach(flowOpsBuilder::add);
125 break;
126 case REMOVE:
127 rules.stream()
128 .filter(rule -> rule != null)
129 .forEach(flowOpsBuilder::remove);
130 break;
131 default:
132 fail(fwd, ObjectiveError.UNKNOWN);
133 log.warn("Unknown forwarding type {}", fwd.op());
134 }
135
136
137 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
138 @Override
139 public void onSuccess(FlowRuleOperations ops) {
140 pass(fwd);
141 }
142
143 @Override
144 public void onError(FlowRuleOperations ops) {
145 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
146 }
147 }));
148
149 }
150
151 @Override
152 public void next(NextObjective nextObjective) {
153 switch (nextObjective.type()) {
154 case SIMPLE:
155 Collection<TrafficTreatment> treatments = nextObjective.next();
156 if (treatments.size() != 1) {
157 log.error("Next Objectives of type Simple should only have a "
158 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
159 fail(nextObjective, ObjectiveError.BADPARAMS);
160 return;
161 }
162 processSimpleNextObjective(nextObjective);
163 break;
164 case HASHED:
165 case BROADCAST:
166 case FAILOVER:
167 fail(nextObjective, ObjectiveError.UNSUPPORTED);
168 log.warn("Unsupported next objective type {}", nextObjective.type());
169 break;
170 default:
171 fail(nextObjective, ObjectiveError.UNKNOWN);
172 log.warn("Unknown next objective type {}", nextObjective.type());
173 }
174 }
175
176 private void pass(Objective obj) {
177 if (obj.context().isPresent()) {
178 obj.context().get().onSuccess(obj);
179 }
180 }
181
182 private void fail(Objective obj, ObjectiveError error) {
183 if (obj.context().isPresent()) {
184 obj.context().get().onError(obj, error);
185 }
186 }
187
Saurav Dasdecd7a62015-05-16 22:39:47 -0700188 private void initializePipeline() {
189 //Drop rules for both tables
190 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
191 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
192 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
193
194 treatment.drop();
195
196 FlowRule rule = DefaultFlowRule.builder()
197 .forDevice(deviceId)
198 .withSelector(selector.build())
199 .withTreatment(treatment.build())
200 .withPriority(DROP_PRIORITY)
201 .fromApp(driverId)
202 .makePermanent()
203 .forTable(FILTER_TABLE)
204 .build();
205 ops = ops.add(rule);
206
207 rule = DefaultFlowRule.builder().forDevice(deviceId)
208 .withSelector(selector.build())
209 .withTreatment(treatment.build())
210 .withPriority(DROP_PRIORITY)
211 .fromApp(driverId)
212 .makePermanent()
213 .forTable(FIB_TABLE)
214 .build();
215 ops = ops.add(rule);
216
217 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
218 @Override
219 public void onSuccess(FlowRuleOperations ops) {
220 log.info("Provisioned drop rules in both tables");
221 }
222
223 @Override
224 public void onError(FlowRuleOperations ops) {
225 log.info("Failed to provision drop rules");
226 }
227 }));
228 }
229
230 private void processFilter(FilteringObjective filt, boolean install,
231 ApplicationId applicationId) {
232 // This driver only processes filtering criteria defined with switch
233 // ports as the key
Jonathan Hart6344f572015-12-15 08:26:25 -0800234 PortCriterion p;
235 EthCriterion e = null;
236 VlanIdCriterion v = null;
237
Saurav Dasdecd7a62015-05-16 22:39:47 -0700238 if (!filt.key().equals(Criteria.dummy()) &&
239 filt.key().type() == Criterion.Type.IN_PORT) {
240 p = (PortCriterion) filt.key();
241 } else {
242 log.warn("No key defined in filtering objective from app: {}. Not"
243 + "processing filtering objective", applicationId);
244 fail(filt, ObjectiveError.UNKNOWN);
245 return;
246 }
247
248 // convert filtering conditions for switch-intfs into flowrules
249 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
250 for (Criterion c : filt.conditions()) {
251 if (c.type() == Criterion.Type.ETH_DST) {
252 e = (EthCriterion) c;
253 } else if (c.type() == Criterion.Type.VLAN_VID) {
254 v = (VlanIdCriterion) c;
Saurav Dasdecd7a62015-05-16 22:39:47 -0700255 } else {
256 log.error("Unsupported filter {}", c);
257 fail(filt, ObjectiveError.UNSUPPORTED);
258 return;
259 }
260 }
261
262 log.debug("adding Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
263 p.port(), v.vlanId(), e.mac());
264 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
265 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
266 selector.matchInPort(p.port());
Jonathan Hart6344f572015-12-15 08:26:25 -0800267
Saurav Dasdecd7a62015-05-16 22:39:47 -0700268 selector.matchEthDst(e.mac());
Jonathan Hart6344f572015-12-15 08:26:25 -0800269 selector.matchVlanId(v.vlanId());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700270 selector.matchEthType(Ethernet.TYPE_IPV4);
Jonathan Hartca47cd72015-12-13 12:31:09 -0800271 if (!v.vlanId().equals(VlanId.NONE)) {
272 treatment.popVlan();
273 }
Jonathan Hart6344f572015-12-15 08:26:25 -0800274 treatment.transition(FIB_TABLE);
Saurav Dasdecd7a62015-05-16 22:39:47 -0700275 FlowRule rule = DefaultFlowRule.builder()
276 .forDevice(deviceId)
277 .withSelector(selector.build())
278 .withTreatment(treatment.build())
279 .withPriority(DEFAULT_PRIORITY)
280 .fromApp(applicationId)
281 .makePermanent()
282 .forTable(FILTER_TABLE).build();
283 ops = ops.add(rule);
284
Saurav Dasdecd7a62015-05-16 22:39:47 -0700285 ops = install ? ops.add(rule) : ops.remove(rule);
286 // apply filtering flow rules
287 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
288 @Override
289 public void onSuccess(FlowRuleOperations ops) {
290 log.info("Applied filtering rules");
291 pass(filt);
292 }
293
294 @Override
295 public void onError(FlowRuleOperations ops) {
296 log.info("Failed to apply filtering rules");
297 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
298 }
299 }));
300 }
301
302 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
303 switch (fwd.flag()) {
304 case SPECIFIC:
305 return processSpecific(fwd);
306 case VERSATILE:
307 return processVersatile(fwd);
308 default:
309 fail(fwd, ObjectiveError.UNKNOWN);
310 log.warn("Unknown forwarding flag {}", fwd.flag());
311 }
312 return Collections.emptySet();
313 }
314
315 /**
316 * SoftRouter has a single versatile table - the filter table. All versatile
317 * flow rules must include the filtering rules.
318 *
319 * @param fwd The forwarding objective of type versatile
320 * @return A collection of flow rules meant to be delivered to the flowrule
321 * subsystem. May return empty collection in case of failures.
322 */
323 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Jonathan Hart6344f572015-12-15 08:26:25 -0800324 Collection<FlowRule> flowrules = new ArrayList<>();
325
326 FlowRule rule = DefaultFlowRule.builder()
327 .withSelector(fwd.selector())
328 .withTreatment(fwd.treatment())
329 .makePermanent()
330 .forDevice(deviceId)
331 .fromApp(fwd.appId())
332 .withPriority(fwd.priority())
333 .build();
334
335 flowrules.add(rule);
336
Saurav Dasdecd7a62015-05-16 22:39:47 -0700337 return flowrules;
338 }
339
Saurav Dasdecd7a62015-05-16 22:39:47 -0700340 /**
341 * SoftRouter has a single specific table - the FIB Table. It emulates
342 * LPM matching of dstIP by using higher priority flows for longer prefixes.
343 * Flows are forwarded using flow-actions
344 *
345 * @param fwd The forwarding objective of type simple
346 * @return A collection of flow rules meant to be delivered to the flowrule
347 * subsystem. Typically the returned collection has a single flowrule.
348 * May return empty collection in case of failures.
349 *
350 */
351 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
352 log.debug("Processing specific forwarding objective");
353 TrafficSelector selector = fwd.selector();
354 EthTypeCriterion ethType =
355 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
356 // XXX currently supporting only the L3 unicast table
alshabibcaf1ca22015-06-25 15:18:16 -0700357 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700358 fail(fwd, ObjectiveError.UNSUPPORTED);
359 return Collections.emptySet();
360 }
361
362 TrafficSelector filteredSelector =
363 DefaultTrafficSelector.builder()
364 .matchEthType(Ethernet.TYPE_IPV4)
365 .matchIPDst(((IPCriterion)
366 selector.getCriterion(Criterion.Type.IPV4_DST)).ip())
367 .build();
368
369 TrafficTreatment tt = null;
370 if (fwd.nextId() != null) {
371 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
372 if (next == null) {
373 log.error("next-id {} does not exist in store", fwd.nextId());
374 return Collections.emptySet();
375 }
376 tt = appKryo.deserialize(next.data());
377 if (tt == null) {
378 log.error("Error in deserializing next-id {}", fwd.nextId());
379 return Collections.emptySet();
380 }
381 }
382
383 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
384 .fromApp(fwd.appId())
385 .withPriority(fwd.priority())
386 .forDevice(deviceId)
Jonathan Hart3930f632015-10-19 12:12:51 -0700387 .withSelector(filteredSelector);
388
389 if (tt != null) {
390 ruleBuilder.withTreatment(tt);
391 }
Saurav Dasdecd7a62015-05-16 22:39:47 -0700392
393 if (fwd.permanent()) {
394 ruleBuilder.makePermanent();
395 } else {
396 ruleBuilder.makeTemporary(fwd.timeout());
397 }
398
399 ruleBuilder.forTable(FIB_TABLE);
400 return Collections.singletonList(ruleBuilder.build());
401 }
402
403 /**
404 * Next Objectives are stored as dummy groups for retrieval later
405 * when Forwarding Objectives reference the next objective id. At that point
406 * the dummy group is fetched from the distributed store and the enclosed
407 * treatment is applied as a flow rule action.
408 *
alshabibcaf1ca22015-06-25 15:18:16 -0700409 * @param nextObj the next objective of type simple
Saurav Dasdecd7a62015-05-16 22:39:47 -0700410 */
411 private void processSimpleNextObjective(NextObjective nextObj) {
412 // Simple next objective has a single treatment (not a collection)
Saurav Das6c44a632015-05-30 22:05:22 -0700413 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700414 flowObjectiveStore.putNextGroup(nextObj.id(),
Saurav Das6c44a632015-05-30 22:05:22 -0700415 new DummyGroup(treatment));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700416 }
417
Saurav Dasdecd7a62015-05-16 22:39:47 -0700418 private class DummyGroup implements NextGroup {
419 TrafficTreatment nextActions;
420
421 public DummyGroup(TrafficTreatment next) {
422 this.nextActions = next;
423 }
424
425 @Override
426 public byte[] data() {
427 return appKryo.serialize(nextActions);
428 }
429
430 }
431
432}