blob: 220be864eb571bc9e923c4f795a4e12d7e4a7ae9 [file] [log] [blame]
Saurav Dasdecd7a62015-05-16 22:39:47 -07001package org.onosproject.driver.pipeline;
2
3import static org.slf4j.LoggerFactory.getLogger;
4
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.concurrent.ConcurrentHashMap;
9
10import org.onlab.osgi.ServiceDirectory;
11import org.onlab.packet.Ethernet;
12import org.onlab.packet.MacAddress;
13import org.onlab.packet.VlanId;
14import org.onlab.util.KryoNamespace;
15import org.onosproject.core.ApplicationId;
16import org.onosproject.core.CoreService;
17import org.onosproject.net.DeviceId;
18import org.onosproject.net.PortNumber;
19import org.onosproject.net.behaviour.NextGroup;
20import org.onosproject.net.behaviour.Pipeliner;
21import org.onosproject.net.behaviour.PipelinerContext;
22import org.onosproject.net.driver.AbstractHandlerBehaviour;
23import org.onosproject.net.flow.DefaultFlowRule;
24import org.onosproject.net.flow.DefaultTrafficSelector;
25import org.onosproject.net.flow.DefaultTrafficTreatment;
26import org.onosproject.net.flow.FlowRule;
27import org.onosproject.net.flow.FlowRuleOperations;
28import org.onosproject.net.flow.FlowRuleOperationsContext;
29import org.onosproject.net.flow.FlowRuleService;
30import org.onosproject.net.flow.TrafficSelector;
31import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.flow.criteria.Criteria;
33import org.onosproject.net.flow.criteria.Criterion;
34import org.onosproject.net.flow.criteria.EthCriterion;
35import org.onosproject.net.flow.criteria.EthTypeCriterion;
36import org.onosproject.net.flow.criteria.IPCriterion;
37import org.onosproject.net.flow.criteria.PortCriterion;
38import org.onosproject.net.flow.criteria.VlanIdCriterion;
39import org.onosproject.net.flowobjective.FilteringObjective;
40import org.onosproject.net.flowobjective.FlowObjectiveStore;
41import org.onosproject.net.flowobjective.ForwardingObjective;
42import org.onosproject.net.flowobjective.NextObjective;
43import org.onosproject.net.flowobjective.Objective;
44import org.onosproject.net.flowobjective.ObjectiveError;
45import org.slf4j.Logger;
46import org.onosproject.store.serializers.KryoNamespaces;
47
48/**
49 * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline
50 * does not forward IP traffic to next-hop groups. Instead it forwards traffic
51 * using OF FlowMod actions.
52 */
53public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipeliner {
54
55 protected static final int FILTER_TABLE = 0;
56 protected static final int FIB_TABLE = 1;
57
58 private static final int DROP_PRIORITY = 0;
59 private static final int DEFAULT_PRIORITY = 0x8000;
60 private static final int HIGHEST_PRIORITY = 0xffff;
61
62 private ServiceDirectory serviceDirectory;
63 protected FlowRuleService flowRuleService;
64 private CoreService coreService;
65 private FlowObjectiveStore flowObjectiveStore;
66 protected DeviceId deviceId;
67 protected ApplicationId appId;
68 private ApplicationId driverId;
69 private Collection<Filter> filters;
70 private Collection<ForwardingObjective> pendingVersatiles;
71
72 private KryoNamespace appKryo = new KryoNamespace.Builder()
73 .register(DummyGroup.class)
74 .register(KryoNamespaces.API)
75 .register(byte[].class)
76 .build();
77
78 private final Logger log = getLogger(getClass());
79
80 @Override
81 public void init(DeviceId deviceId, PipelinerContext context) {
82 this.serviceDirectory = context.directory();
83 this.deviceId = deviceId;
84 coreService = serviceDirectory.get(CoreService.class);
85 flowRuleService = serviceDirectory.get(FlowRuleService.class);
86 flowObjectiveStore = context.store();
87 driverId = coreService.registerApplication(
88 "org.onosproject.driver.OVSCorsaPipeline");
89 filters = Collections.newSetFromMap(new ConcurrentHashMap<Filter, Boolean>());
90 pendingVersatiles = Collections.newSetFromMap(
91 new ConcurrentHashMap<ForwardingObjective, Boolean>());
92 initializePipeline();
93 }
94
95 @Override
96 public void filter(FilteringObjective filteringObjective) {
97 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
98 processFilter(filteringObjective,
99 filteringObjective.op() == Objective.Operation.ADD,
100 filteringObjective.appId());
101 } else {
102 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
103 }
104 }
105
106 @Override
107 public void forward(ForwardingObjective fwd) {
108 Collection<FlowRule> rules;
109 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
110
111 rules = processForward(fwd);
112 switch (fwd.op()) {
113 case ADD:
114 rules.stream()
115 .filter(rule -> rule != null)
116 .forEach(flowOpsBuilder::add);
117 break;
118 case REMOVE:
119 rules.stream()
120 .filter(rule -> rule != null)
121 .forEach(flowOpsBuilder::remove);
122 break;
123 default:
124 fail(fwd, ObjectiveError.UNKNOWN);
125 log.warn("Unknown forwarding type {}", fwd.op());
126 }
127
128
129 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
130 @Override
131 public void onSuccess(FlowRuleOperations ops) {
132 pass(fwd);
133 }
134
135 @Override
136 public void onError(FlowRuleOperations ops) {
137 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
138 }
139 }));
140
141 }
142
143 @Override
144 public void next(NextObjective nextObjective) {
145 switch (nextObjective.type()) {
146 case SIMPLE:
147 Collection<TrafficTreatment> treatments = nextObjective.next();
148 if (treatments.size() != 1) {
149 log.error("Next Objectives of type Simple should only have a "
150 + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
151 fail(nextObjective, ObjectiveError.BADPARAMS);
152 return;
153 }
154 processSimpleNextObjective(nextObjective);
155 break;
156 case HASHED:
157 case BROADCAST:
158 case FAILOVER:
159 fail(nextObjective, ObjectiveError.UNSUPPORTED);
160 log.warn("Unsupported next objective type {}", nextObjective.type());
161 break;
162 default:
163 fail(nextObjective, ObjectiveError.UNKNOWN);
164 log.warn("Unknown next objective type {}", nextObjective.type());
165 }
166 }
167
168 private void pass(Objective obj) {
169 if (obj.context().isPresent()) {
170 obj.context().get().onSuccess(obj);
171 }
172 }
173
174 private void fail(Objective obj, ObjectiveError error) {
175 if (obj.context().isPresent()) {
176 obj.context().get().onError(obj, error);
177 }
178 }
179
180
181 private void initializePipeline() {
182 //Drop rules for both tables
183 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
184 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
185 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
186
187 treatment.drop();
188
189 FlowRule rule = DefaultFlowRule.builder()
190 .forDevice(deviceId)
191 .withSelector(selector.build())
192 .withTreatment(treatment.build())
193 .withPriority(DROP_PRIORITY)
194 .fromApp(driverId)
195 .makePermanent()
196 .forTable(FILTER_TABLE)
197 .build();
198 ops = ops.add(rule);
199
200 rule = DefaultFlowRule.builder().forDevice(deviceId)
201 .withSelector(selector.build())
202 .withTreatment(treatment.build())
203 .withPriority(DROP_PRIORITY)
204 .fromApp(driverId)
205 .makePermanent()
206 .forTable(FIB_TABLE)
207 .build();
208 ops = ops.add(rule);
209
210 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
211 @Override
212 public void onSuccess(FlowRuleOperations ops) {
213 log.info("Provisioned drop rules in both tables");
214 }
215
216 @Override
217 public void onError(FlowRuleOperations ops) {
218 log.info("Failed to provision drop rules");
219 }
220 }));
221 }
222
223 private void processFilter(FilteringObjective filt, boolean install,
224 ApplicationId applicationId) {
225 // This driver only processes filtering criteria defined with switch
226 // ports as the key
227 PortCriterion p; EthCriterion e = null; VlanIdCriterion v = null;
228 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
229 if (!filt.key().equals(Criteria.dummy()) &&
230 filt.key().type() == Criterion.Type.IN_PORT) {
231 p = (PortCriterion) filt.key();
232 } else {
233 log.warn("No key defined in filtering objective from app: {}. Not"
234 + "processing filtering objective", applicationId);
235 fail(filt, ObjectiveError.UNKNOWN);
236 return;
237 }
238
239 // convert filtering conditions for switch-intfs into flowrules
240 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
241 for (Criterion c : filt.conditions()) {
242 if (c.type() == Criterion.Type.ETH_DST) {
243 e = (EthCriterion) c;
244 } else if (c.type() == Criterion.Type.VLAN_VID) {
245 v = (VlanIdCriterion) c;
246 } else if (c.type() == Criterion.Type.IPV4_DST) {
247 ips.add((IPCriterion) c);
248 } else {
249 log.error("Unsupported filter {}", c);
250 fail(filt, ObjectiveError.UNSUPPORTED);
251 return;
252 }
253 }
254
255 log.debug("adding Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
256 p.port(), v.vlanId(), e.mac());
257 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
258 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
259 selector.matchInPort(p.port());
260 selector.matchVlanId(v.vlanId());
261 selector.matchEthDst(e.mac());
262 selector.matchEthType(Ethernet.TYPE_IPV4);
Saurav Das6c44a632015-05-30 22:05:22 -0700263 treatment.popVlan();
264 treatment.transition(FIB_TABLE); // all other IPs to the FIB table
Saurav Dasdecd7a62015-05-16 22:39:47 -0700265 FlowRule rule = DefaultFlowRule.builder()
266 .forDevice(deviceId)
267 .withSelector(selector.build())
268 .withTreatment(treatment.build())
269 .withPriority(DEFAULT_PRIORITY)
270 .fromApp(applicationId)
271 .makePermanent()
272 .forTable(FILTER_TABLE).build();
273 ops = ops.add(rule);
274
275 for (IPCriterion ipaddr : ips) {
Saurav Das6c44a632015-05-30 22:05:22 -0700276 log.debug("adding IP filtering rules in FILTER table: {}", ipaddr.ip());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700277 selector = DefaultTrafficSelector.builder();
278 treatment = DefaultTrafficTreatment.builder();
Saurav Das6c44a632015-05-30 22:05:22 -0700279 selector.matchInPort(p.port());
280 selector.matchVlanId(v.vlanId());
281 selector.matchEthDst(e.mac());
Saurav Dasdecd7a62015-05-16 22:39:47 -0700282 selector.matchEthType(Ethernet.TYPE_IPV4);
Saurav Das6c44a632015-05-30 22:05:22 -0700283 selector.matchIPDst(ipaddr.ip()); // router IPs to the controller
Saurav Dasdecd7a62015-05-16 22:39:47 -0700284 treatment.setOutput(PortNumber.CONTROLLER);
285 rule = DefaultFlowRule.builder()
286 .forDevice(deviceId)
287 .withSelector(selector.build())
288 .withTreatment(treatment.build())
289 .withPriority(HIGHEST_PRIORITY)
290 .fromApp(applicationId)
291 .makePermanent()
Saurav Das6c44a632015-05-30 22:05:22 -0700292 .forTable(FILTER_TABLE).build();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700293 ops = ops.add(rule);
294 }
295
296 // cache for later use
297 Filter filter = new Filter(p, e, v, ips);
298 filters.add(filter);
299 // apply any pending versatile forwarding objectives
300 for (ForwardingObjective fwd : pendingVersatiles) {
301 Collection<FlowRule> ret = processVersatilesWithFilters(filter, fwd);
302 for (FlowRule fr : ret) {
303 ops.add(fr);
304 }
305 }
306
307 ops = install ? ops.add(rule) : ops.remove(rule);
308 // apply filtering flow rules
309 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
310 @Override
311 public void onSuccess(FlowRuleOperations ops) {
312 log.info("Applied filtering rules");
313 pass(filt);
314 }
315
316 @Override
317 public void onError(FlowRuleOperations ops) {
318 log.info("Failed to apply filtering rules");
319 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
320 }
321 }));
322 }
323
324 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
325 switch (fwd.flag()) {
326 case SPECIFIC:
327 return processSpecific(fwd);
328 case VERSATILE:
329 return processVersatile(fwd);
330 default:
331 fail(fwd, ObjectiveError.UNKNOWN);
332 log.warn("Unknown forwarding flag {}", fwd.flag());
333 }
334 return Collections.emptySet();
335 }
336
337 /**
338 * SoftRouter has a single versatile table - the filter table. All versatile
339 * flow rules must include the filtering rules.
340 *
341 * @param fwd The forwarding objective of type versatile
342 * @return A collection of flow rules meant to be delivered to the flowrule
343 * subsystem. May return empty collection in case of failures.
344 */
345 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
346 if (filters.isEmpty()) {
347 pendingVersatiles.add(fwd);
348 return Collections.emptySet();
349 }
350 Collection<FlowRule> flowrules = new ArrayList<FlowRule>();
351 for (Filter filter : filters) {
352 flowrules.addAll(processVersatilesWithFilters(filter, fwd));
353 }
354 return flowrules;
355 }
356
357 private Collection<FlowRule> processVersatilesWithFilters(
358 Filter filt, ForwardingObjective fwd) {
359 log.info("Processing versatile forwarding objective");
360 Collection<FlowRule> flows = new ArrayList<FlowRule>();
361 TrafficSelector match = fwd.selector();
362 EthTypeCriterion ethType =
363 (EthTypeCriterion) match.getCriterion(Criterion.Type.ETH_TYPE);
364 if (ethType == null) {
365 log.error("Versatile forwarding objective must include ethType");
366 fail(fwd, ObjectiveError.UNKNOWN);
367 return Collections.emptySet();
368 }
369
alshabibcaf1ca22015-06-25 15:18:16 -0700370 if (ethType.ethType().toShort() == Ethernet.TYPE_ARP) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700371 // need to install ARP request & reply flow rules for each interface filter
372
373 // rule for ARP replies
374 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
375 selector.matchInPort(filt.port());
376 selector.matchVlanId(filt.vlanId());
377 selector.matchEthDst(filt.mac());
378 selector.matchEthType(Ethernet.TYPE_ARP);
379 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
380 .fromApp(fwd.appId())
381 .withPriority(fwd.priority())
382 .forDevice(deviceId)
383 .withSelector(selector.build())
384 .withTreatment(fwd.treatment())
385 .makePermanent()
386 .forTable(FILTER_TABLE);
387 flows.add(ruleBuilder.build());
388
389 //rule for ARP requests
390 selector = DefaultTrafficSelector.builder();
391 selector.matchInPort(filt.port());
392 selector.matchVlanId(filt.vlanId());
393 selector.matchEthDst(MacAddress.BROADCAST);
394 selector.matchEthType(Ethernet.TYPE_ARP);
395 ruleBuilder = DefaultFlowRule.builder()
396 .fromApp(fwd.appId())
397 .withPriority(fwd.priority())
398 .forDevice(deviceId)
399 .withSelector(selector.build())
400 .withTreatment(fwd.treatment())
401 .makePermanent()
402 .forTable(FILTER_TABLE);
403 flows.add(ruleBuilder.build());
404
405 return flows;
406 }
407 // not handling other versatile flows
408 return Collections.emptySet();
409 }
410
411 /**
412 * SoftRouter has a single specific table - the FIB Table. It emulates
413 * LPM matching of dstIP by using higher priority flows for longer prefixes.
414 * Flows are forwarded using flow-actions
415 *
416 * @param fwd The forwarding objective of type simple
417 * @return A collection of flow rules meant to be delivered to the flowrule
418 * subsystem. Typically the returned collection has a single flowrule.
419 * May return empty collection in case of failures.
420 *
421 */
422 private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
423 log.debug("Processing specific forwarding objective");
424 TrafficSelector selector = fwd.selector();
425 EthTypeCriterion ethType =
426 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
427 // XXX currently supporting only the L3 unicast table
alshabibcaf1ca22015-06-25 15:18:16 -0700428 if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
Saurav Dasdecd7a62015-05-16 22:39:47 -0700429 fail(fwd, ObjectiveError.UNSUPPORTED);
430 return Collections.emptySet();
431 }
432
433 TrafficSelector filteredSelector =
434 DefaultTrafficSelector.builder()
435 .matchEthType(Ethernet.TYPE_IPV4)
436 .matchIPDst(((IPCriterion)
437 selector.getCriterion(Criterion.Type.IPV4_DST)).ip())
438 .build();
439
440 TrafficTreatment tt = null;
441 if (fwd.nextId() != null) {
442 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
443 if (next == null) {
444 log.error("next-id {} does not exist in store", fwd.nextId());
445 return Collections.emptySet();
446 }
447 tt = appKryo.deserialize(next.data());
448 if (tt == null) {
449 log.error("Error in deserializing next-id {}", fwd.nextId());
450 return Collections.emptySet();
451 }
452 }
453
454 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
455 .fromApp(fwd.appId())
456 .withPriority(fwd.priority())
457 .forDevice(deviceId)
458 .withSelector(filteredSelector)
459 .withTreatment(tt);
460
461 if (fwd.permanent()) {
462 ruleBuilder.makePermanent();
463 } else {
464 ruleBuilder.makeTemporary(fwd.timeout());
465 }
466
467 ruleBuilder.forTable(FIB_TABLE);
468 return Collections.singletonList(ruleBuilder.build());
469 }
470
471 /**
472 * Next Objectives are stored as dummy groups for retrieval later
473 * when Forwarding Objectives reference the next objective id. At that point
474 * the dummy group is fetched from the distributed store and the enclosed
475 * treatment is applied as a flow rule action.
476 *
alshabibcaf1ca22015-06-25 15:18:16 -0700477 * @param nextObj the next objective of type simple
Saurav Dasdecd7a62015-05-16 22:39:47 -0700478 */
479 private void processSimpleNextObjective(NextObjective nextObj) {
480 // Simple next objective has a single treatment (not a collection)
Saurav Das6c44a632015-05-30 22:05:22 -0700481 TrafficTreatment treatment = nextObj.next().iterator().next();
Saurav Dasdecd7a62015-05-16 22:39:47 -0700482 flowObjectiveStore.putNextGroup(nextObj.id(),
Saurav Das6c44a632015-05-30 22:05:22 -0700483 new DummyGroup(treatment));
Saurav Dasdecd7a62015-05-16 22:39:47 -0700484 }
485
486 private class Filter {
487 private PortCriterion port;
488 private VlanIdCriterion vlan;
489 private EthCriterion eth;
490
491 @SuppressWarnings("unused")
492 private Collection<IPCriterion> ips;
493
494 public Filter(PortCriterion p, EthCriterion e, VlanIdCriterion v,
495 Collection<IPCriterion> ips) {
496 this.eth = e;
497 this.port = p;
498 this.vlan = v;
499 this.ips = ips;
500 }
501
502 public PortNumber port() {
503 return port.port();
504 }
505
506 public VlanId vlanId() {
507 return vlan.vlanId();
508 }
509
510 public MacAddress mac() {
511 return eth.mac();
512 }
513 }
514
515 private class DummyGroup implements NextGroup {
516 TrafficTreatment nextActions;
517
518 public DummyGroup(TrafficTreatment next) {
519 this.nextActions = next;
520 }
521
522 @Override
523 public byte[] data() {
524 return appKryo.serialize(nextActions);
525 }
526
527 }
528
529}