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