blob: 28d607169f1fc48ef668c57a6c0fbce490362b42 [file] [log] [blame]
alshabib0ccde6d2015-05-30 18:22:36 -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 */
16package org.onosproject.driver.pipeline;
17
alshabibfd430b62015-12-16 18:56:38 -080018import com.google.common.collect.Lists;
19import org.apache.commons.lang3.tuple.ImmutablePair;
20import org.apache.commons.lang3.tuple.Pair;
alshabib0ccde6d2015-05-30 18:22:36 -070021import org.onlab.osgi.ServiceDirectory;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070022import org.onlab.packet.EthType;
alshabibfd430b62015-12-16 18:56:38 -080023import org.onlab.packet.IPv4;
24import org.onlab.packet.VlanId;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070025import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
alshabib2cc73cb2015-06-30 20:26:56 -070027import org.onosproject.net.DefaultAnnotations;
28import org.onosproject.net.Device;
alshabib0ccde6d2015-05-30 18:22:36 -070029import org.onosproject.net.DeviceId;
alshabib2cc73cb2015-06-30 20:26:56 -070030import org.onosproject.net.MastershipRole;
alshabib0ccde6d2015-05-30 18:22:36 -070031import org.onosproject.net.PortNumber;
32import org.onosproject.net.behaviour.Pipeliner;
33import org.onosproject.net.behaviour.PipelinerContext;
alshabib2cc73cb2015-06-30 20:26:56 -070034import org.onosproject.net.device.DefaultDeviceDescription;
35import org.onosproject.net.device.DeviceDescription;
36import org.onosproject.net.device.DeviceProvider;
37import org.onosproject.net.device.DeviceProviderRegistry;
alshabib2cc73cb2015-06-30 20:26:56 -070038import org.onosproject.net.device.DeviceService;
alshabib0ccde6d2015-05-30 18:22:36 -070039import org.onosproject.net.driver.AbstractHandlerBehaviour;
40import org.onosproject.net.flow.DefaultFlowRule;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070041import org.onosproject.net.flow.DefaultTrafficSelector;
alshabib0ccde6d2015-05-30 18:22:36 -070042import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.FlowRule;
44import org.onosproject.net.flow.FlowRuleOperations;
45import org.onosproject.net.flow.FlowRuleOperationsContext;
46import org.onosproject.net.flow.FlowRuleService;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
alshabibfd430b62015-12-16 18:56:38 -080049import org.onosproject.net.flow.criteria.Criteria;
50import org.onosproject.net.flow.criteria.Criterion;
51import org.onosproject.net.flow.criteria.EthTypeCriterion;
52import org.onosproject.net.flow.criteria.IPProtocolCriterion;
53import org.onosproject.net.flow.criteria.PortCriterion;
54import org.onosproject.net.flow.instructions.Instruction;
alshabib0ccde6d2015-05-30 18:22:36 -070055import org.onosproject.net.flow.instructions.Instructions;
alshabibfd430b62015-12-16 18:56:38 -080056import org.onosproject.net.flow.instructions.L2ModificationInstruction;
alshabib0ccde6d2015-05-30 18:22:36 -070057import org.onosproject.net.flowobjective.FilteringObjective;
58import org.onosproject.net.flowobjective.ForwardingObjective;
59import org.onosproject.net.flowobjective.NextObjective;
alshabibfd430b62015-12-16 18:56:38 -080060import org.onosproject.net.flowobjective.Objective;
alshabib0ccde6d2015-05-30 18:22:36 -070061import org.onosproject.net.flowobjective.ObjectiveError;
alshabib2cc73cb2015-06-30 20:26:56 -070062import org.onosproject.net.provider.AbstractProvider;
63import org.onosproject.net.provider.ProviderId;
alshabib0ccde6d2015-05-30 18:22:36 -070064import org.slf4j.Logger;
65
alshabibfd430b62015-12-16 18:56:38 -080066import java.util.Collection;
67import java.util.List;
68import java.util.Optional;
69import java.util.stream.Collectors;
70
alshabib2cc73cb2015-06-30 20:26:56 -070071import static com.google.common.base.Preconditions.checkNotNull;
alshabib0ccde6d2015-05-30 18:22:36 -070072import static org.slf4j.LoggerFactory.getLogger;
73
74/**
Jonathan Hart64da69d2015-07-15 15:10:28 -070075 * Pipeliner for OLT device.
alshabib0ccde6d2015-05-30 18:22:36 -070076 */
alshabibfd430b62015-12-16 18:56:38 -080077
Jonathan Hartb92cc512015-11-16 23:05:21 -080078public class OltPipeline extends AbstractHandlerBehaviour implements Pipeliner {
alshabib0ccde6d2015-05-30 18:22:36 -070079
alshabibfd430b62015-12-16 18:56:38 -080080 private static final Integer QQ_TABLE = 1;
alshabib0ccde6d2015-05-30 18:22:36 -070081 private final Logger log = getLogger(getClass());
82
alshabib2cc73cb2015-06-30 20:26:56 -070083 static final ProviderId PID = new ProviderId("olt", "org.onosproject.olt", true);
84
85 static final String DEVICE = "isAccess";
86 static final String OLT = "true";
87
alshabib0ccde6d2015-05-30 18:22:36 -070088 private ServiceDirectory serviceDirectory;
89 private FlowRuleService flowRuleService;
90 private DeviceId deviceId;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070091 private CoreService coreService;
92
93 private ApplicationId appId;
alshabib0ccde6d2015-05-30 18:22:36 -070094
alshabib2cc73cb2015-06-30 20:26:56 -070095 private DeviceProvider provider = new AnnotationProvider();
96
alshabib0ccde6d2015-05-30 18:22:36 -070097 @Override
98 public void init(DeviceId deviceId, PipelinerContext context) {
alshabibfd430b62015-12-16 18:56:38 -080099 log.debug("Initiate OLT pipeline");
alshabib0ccde6d2015-05-30 18:22:36 -0700100 this.serviceDirectory = context.directory();
101 this.deviceId = deviceId;
alshabib2cc73cb2015-06-30 20:26:56 -0700102 DeviceProviderRegistry registry =
alshabibfd430b62015-12-16 18:56:38 -0800103 serviceDirectory.get(DeviceProviderRegistry.class);
alshabib0ccde6d2015-05-30 18:22:36 -0700104 flowRuleService = serviceDirectory.get(FlowRuleService.class);
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700105 coreService = serviceDirectory.get(CoreService.class);
alshabibb32cefe2015-06-08 18:15:05 -0700106
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700107 appId = coreService.registerApplication(
108 "org.onosproject.driver.OLTPipeline");
109
alshabibb32cefe2015-06-08 18:15:05 -0700110 }
111
alshabib0ccde6d2015-05-30 18:22:36 -0700112 @Override
113 public void filter(FilteringObjective filter) {
alshabibfd430b62015-12-16 18:56:38 -0800114 Instructions.OutputInstruction output;
alshabib0ccde6d2015-05-30 18:22:36 -0700115
alshabibfd430b62015-12-16 18:56:38 -0800116 if (filter.meta() != null && !filter.meta().immediate().isEmpty()) {
117 output = (Instructions.OutputInstruction) filter.meta().immediate().stream()
118 .filter(t -> t.type().equals(Instruction.Type.OUTPUT))
119 .limit(1)
120 .findFirst().get();
alshabib0ccde6d2015-05-30 18:22:36 -0700121
alshabibfd430b62015-12-16 18:56:38 -0800122 if (output != null && !output.port().equals(PortNumber.CONTROLLER)) {
123 fail(filter, ObjectiveError.UNSUPPORTED);
124 return;
alshabib0ccde6d2015-05-30 18:22:36 -0700125 }
alshabibfd430b62015-12-16 18:56:38 -0800126 } else {
127 fail(filter, ObjectiveError.BADPARAMS);
alshabib0ccde6d2015-05-30 18:22:36 -0700128 return;
129 }
130
alshabibfd430b62015-12-16 18:56:38 -0800131 if (filter.key().type() != Criterion.Type.IN_PORT) {
132 fail(filter, ObjectiveError.BADPARAMS);
133 return;
134 }
135
136 EthTypeCriterion ethType = (EthTypeCriterion)
137 filterForCriterion(filter.conditions(), Criterion.Type.ETH_TYPE);
138
139 if (ethType == null) {
140 fail(filter, ObjectiveError.BADPARAMS);
141 return;
142 }
143
144 if (ethType.ethType().equals(EthType.EtherType.EAPOL)) {
145 provisionEapol(filter, ethType, output);
146 } else if (ethType.ethType().equals(EthType.EtherType.IPV4)) {
147 IPProtocolCriterion ipProto = (IPProtocolCriterion)
148 filterForCriterion(filter.conditions(), Criterion.Type.IP_PROTO);
149 if (ipProto.protocol() == IPv4.PROTOCOL_IGMP) {
150 provisionIGMP(filter, ethType, ipProto, output);
151 }
152 } else {
153 fail(filter, ObjectiveError.UNSUPPORTED);
154 }
155
156 }
157
158
159 @Override
160 public void forward(ForwardingObjective fwd) {
alshabib0ccde6d2015-05-30 18:22:36 -0700161 TrafficTreatment treatment = fwd.treatment();
alshabib0ccde6d2015-05-30 18:22:36 -0700162
alshabibfd430b62015-12-16 18:56:38 -0800163 List<Instruction> instructions = treatment.allInstructions();
alshabib0ccde6d2015-05-30 18:22:36 -0700164
alshabibfd430b62015-12-16 18:56:38 -0800165 Optional<Instruction> vlanIntruction = instructions.stream()
166 .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
167 .filter(i -> ((L2ModificationInstruction) i).subtype() ==
168 L2ModificationInstruction.L2SubType.VLAN_PUSH ||
169 ((L2ModificationInstruction) i).subtype() ==
170 L2ModificationInstruction.L2SubType.VLAN_POP)
171 .findAny();
172
173 if (!vlanIntruction.isPresent()) {
174 fail(fwd, ObjectiveError.BADPARAMS);
175 return;
176 }
177
178 L2ModificationInstruction vlanIns =
179 (L2ModificationInstruction) vlanIntruction.get();
180
181 if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
182 installUpstreamRules(fwd);
183 } else if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) {
184 installDownstreamRules(fwd);
alshabib0ccde6d2015-05-30 18:22:36 -0700185 } else {
alshabibfd430b62015-12-16 18:56:38 -0800186 log.error("Unknown OLT operation: {}", fwd);
187 fail(fwd, ObjectiveError.UNSUPPORTED);
188 return;
alshabib0ccde6d2015-05-30 18:22:36 -0700189 }
190
alshabibfd430b62015-12-16 18:56:38 -0800191 pass(fwd);
alshabib0ccde6d2015-05-30 18:22:36 -0700192
alshabib0ccde6d2015-05-30 18:22:36 -0700193 }
194
195 @Override
196 public void next(NextObjective nextObjective) {
Jonathan Hart3e594642015-10-20 17:31:24 -0700197 throw new UnsupportedOperationException("OLT does not next hop.");
alshabib0ccde6d2015-05-30 18:22:36 -0700198 }
199
alshabibfd430b62015-12-16 18:56:38 -0800200 private void installDownstreamRules(ForwardingObjective fwd) {
201 List<Pair<Instruction, Instruction>> vlanOps =
202 vlanOps(fwd,
203 L2ModificationInstruction.L2SubType.VLAN_POP);
204
205 if (vlanOps == null) {
206 return;
207 }
208
209 Instruction output = fetchOutput(fwd, "downstream");
210
211 if (output == null) {
212 return;
213 }
214
215 Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
216
alshabibfa0dc662016-01-13 11:23:53 -0800217 TrafficSelector selector = fwd.selector();
218
219 Criterion outerVlan = selector.getCriterion(Criterion.Type.VLAN_VID);
220 Criterion innerVlan = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
221 Criterion inport = selector.getCriterion(Criterion.Type.IN_PORT);
222
223 if (outerVlan == null || innerVlan == null || inport == null) {
224 log.error("Forwarding objective is underspecified: {}", fwd);
225 fail(fwd, ObjectiveError.BADPARAMS);
226 return;
227 }
228
229 FlowRule.Builder outer = DefaultFlowRule.builder()
alshabibfd430b62015-12-16 18:56:38 -0800230 .forDevice(deviceId)
231 .fromApp(appId)
232 .makePermanent()
233 .withPriority(fwd.priority())
alshabibfa0dc662016-01-13 11:23:53 -0800234 .withSelector(buildSelector(inport, outerVlan))
alshabibfd430b62015-12-16 18:56:38 -0800235 .withTreatment(buildTreatment(popAndRewrite.getLeft(),
236 Instructions.transition(QQ_TABLE)));
alshabibfd430b62015-12-16 18:56:38 -0800237
alshabibfa0dc662016-01-13 11:23:53 -0800238 FlowRule.Builder inner = DefaultFlowRule.builder()
alshabibfd430b62015-12-16 18:56:38 -0800239 .forDevice(deviceId)
240 .fromApp(appId)
241 .forTable(QQ_TABLE)
242 .makePermanent()
243 .withPriority(fwd.priority())
alshabibfa0dc662016-01-13 11:23:53 -0800244 .withSelector(buildSelector(inport, innerVlan))
alshabibfd430b62015-12-16 18:56:38 -0800245 .withTreatment(buildTreatment(popAndRewrite.getRight(),
246 output));
247
248 applyRules(fwd, inner, outer);
249
250 }
251
252 private void installUpstreamRules(ForwardingObjective fwd) {
253 List<Pair<Instruction, Instruction>> vlanOps =
254 vlanOps(fwd,
255 L2ModificationInstruction.L2SubType.VLAN_PUSH);
256
257 if (vlanOps == null) {
258 return;
259 }
260
261 Instruction output = fetchOutput(fwd, "upstream");
262
263 if (output == null) {
264 return;
265 }
266
267 Pair<Instruction, Instruction> innerPair = vlanOps.remove(0);
268
269 Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
270
271 FlowRule.Builder inner = DefaultFlowRule.builder()
272 .forDevice(deviceId)
273 .fromApp(appId)
274 .makePermanent()
275 .withPriority(fwd.priority())
276 .withSelector(fwd.selector())
277 .withTreatment(buildTreatment(innerPair.getRight(),
278 Instructions.transition(QQ_TABLE)));
279
280 PortCriterion inPort = (PortCriterion)
281 fwd.selector().getCriterion(Criterion.Type.IN_PORT);
282
283 VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
284 innerPair.getRight()).vlanId();
285
286 FlowRule.Builder outer = DefaultFlowRule.builder()
287 .forDevice(deviceId)
288 .fromApp(appId)
289 .forTable(QQ_TABLE)
290 .makePermanent()
291 .withPriority(fwd.priority())
292 .withSelector(buildSelector(inPort,
293 Criteria.matchVlanId(cVlanId)))
294 .withTreatment(buildTreatment(outerPair.getLeft(),
295 outerPair.getRight(),
296 output));
297
298 applyRules(fwd, inner, outer);
299
300 }
301
302 private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
303 Instruction output = fwd.treatment().allInstructions().stream()
304 .filter(i -> i.type() == Instruction.Type.OUTPUT)
305 .findFirst().orElse(null);
306
307 if (output == null) {
308 log.error("OLT {} rule has no output", direction);
309 fail(fwd, ObjectiveError.BADPARAMS);
310 return null;
311 }
312 return output;
313 }
314
315 private List<Pair<Instruction, Instruction>> vlanOps(ForwardingObjective fwd,
316 L2ModificationInstruction.L2SubType type) {
317
318 List<Pair<Instruction, Instruction>> vlanOps = findVlanOps(
319 fwd.treatment().allInstructions(), type);
320
321 if (vlanOps == null) {
322 String direction = type == L2ModificationInstruction.L2SubType.VLAN_POP
323 ? "downstream" : "upstream";
324 log.error("Missing vlan operations in {} forwarding: {}", direction, fwd);
325 fail(fwd, ObjectiveError.BADPARAMS);
326 return null;
327 }
328 return vlanOps;
329 }
330
331
332 private List<Pair<Instruction, Instruction>> findVlanOps(List<Instruction> instructions,
333 L2ModificationInstruction.L2SubType type) {
334
335 List<Instruction> vlanPushs = findL2Instructions(
336 type,
337 instructions);
338 List<Instruction> vlanSets = findL2Instructions(
339 L2ModificationInstruction.L2SubType.VLAN_ID,
340 instructions);
341
342 if (vlanPushs.size() != vlanSets.size()) {
343 return null;
344 }
345
346 List<Pair<Instruction, Instruction>> pairs = Lists.newArrayList();
347
348 for (int i = 0; i < vlanPushs.size(); i++) {
349 pairs.add(new ImmutablePair<>(vlanPushs.get(i), vlanSets.get(i)));
350 }
351 return pairs;
352 }
353
354 private List<Instruction> findL2Instructions(L2ModificationInstruction.L2SubType subType,
355 List<Instruction> actions) {
356 return actions.stream()
357 .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
358 .filter(i -> ((L2ModificationInstruction) i).subtype() == subType)
359 .collect(Collectors.toList());
360 }
361
362 private void provisionEapol(FilteringObjective filter,
363 EthTypeCriterion ethType,
364 Instructions.OutputInstruction output) {
365
366 TrafficSelector selector = buildSelector(filter.key(), ethType);
367 TrafficTreatment treatment = buildTreatment(output);
368 buildAndApplyRule(filter, selector, treatment);
369
370 }
371
372 private void provisionIGMP(FilteringObjective filter, EthTypeCriterion ethType,
373 IPProtocolCriterion ipProto,
374 Instructions.OutputInstruction output) {
375 TrafficSelector selector = buildSelector(filter.key(), ethType, ipProto);
376 TrafficTreatment treatment = buildTreatment(output);
377 buildAndApplyRule(filter, selector, treatment);
378 }
379
380 private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
381 TrafficTreatment treatment) {
382 FlowRule rule = DefaultFlowRule.builder()
383 .forDevice(deviceId)
384 .forTable(0)
385 .fromApp(filter.appId())
386 .makePermanent()
387 .withSelector(selector)
388 .withTreatment(treatment)
389 .build();
390
391 FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
392
393 switch (filter.type()) {
394 case PERMIT:
395 opsBuilder.add(rule);
396 break;
397 case DENY:
398 opsBuilder.remove(rule);
399 break;
400 default:
401 log.warn("Unknown filter type : {}", filter.type());
402 fail(filter, ObjectiveError.UNSUPPORTED);
403 }
404
405 applyFlowRules(opsBuilder, filter);
406 }
407
408 private void applyRules(ForwardingObjective fwd,
409 FlowRule.Builder inner, FlowRule.Builder outer) {
410 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
411 switch (fwd.op()) {
412 case ADD:
413 builder.add(inner.build()).add(outer.build());
414 break;
415 case REMOVE:
416 builder.remove(inner.build()).remove(outer.build());
417 break;
418 case ADD_TO_EXISTING:
419 break;
420 case REMOVE_FROM_EXISTING:
421 break;
422 default:
423 log.warn("Unknown forwarding operation: {}", fwd.op());
424 }
425
426 applyFlowRules(builder, fwd);
427 }
428
429 private void applyFlowRules(FlowRuleOperations.Builder builder,
430 Objective objective) {
431 flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
432 @Override
433 public void onSuccess(FlowRuleOperations ops) {
434 pass(objective);
435 }
436
437 @Override
438 public void onError(FlowRuleOperations ops) {
439 fail(objective, ObjectiveError.FLOWINSTALLATIONFAILED);
440 }
441 }));
442 }
443
444 private Criterion filterForCriterion(Collection<Criterion> criteria, Criterion.Type type) {
445 return criteria.stream()
446 .filter(c -> c.type().equals(Criterion.Type.ETH_TYPE))
447 .limit(1)
448 .findFirst().orElse(null);
449 }
450
451 private TrafficSelector buildSelector(Criterion... criteria) {
452
453
454 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
455
456 for (Criterion c : criteria) {
457 sBuilder.add(c);
458 }
459
460 return sBuilder.build();
461 }
462
463 private TrafficTreatment buildTreatment(Instruction... instructions) {
464
465
466 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
467
468 for (Instruction i : instructions) {
469 tBuilder.add(i);
470 }
471
472 return tBuilder.build();
473 }
474
475
476 private void fail(Objective obj, ObjectiveError error) {
477 if (obj.context().isPresent()) {
478 obj.context().get().onError(obj, error);
479 }
480 }
481
482 private void pass(Objective obj) {
483 if (obj.context().isPresent()) {
484 obj.context().get().onSuccess(obj);
485 }
486 }
487
alshabib2cc73cb2015-06-30 20:26:56 -0700488 /**
489 * Build a device description.
Jonathan Hart3e594642015-10-20 17:31:24 -0700490 *
alshabib2cc73cb2015-06-30 20:26:56 -0700491 * @param deviceId a deviceId
alshabibfd430b62015-12-16 18:56:38 -0800492 * @param key the key of the annotation
493 * @param value the value for the annotation
alshabib2cc73cb2015-06-30 20:26:56 -0700494 * @return a device description
495 */
496 private DeviceDescription description(DeviceId deviceId, String key, String value) {
497 DeviceService deviceService = serviceDirectory.get(DeviceService.class);
498 Device device = deviceService.getDevice(deviceId);
499
500 checkNotNull(device, "Device not found in device service.");
501
502 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
503 if (value != null) {
504 builder.set(key, value);
505 } else {
506 builder.remove(key);
507 }
508 return new DefaultDeviceDescription(device.id().uri(), device.type(),
509 device.manufacturer(), device.hwVersion(),
510 device.swVersion(), device.serialNumber(),
511 device.chassisId(), builder.build());
512 }
513
514 /**
515 * Simple ancillary provider used to annotate device.
516 */
517 private static final class AnnotationProvider
518 extends AbstractProvider implements DeviceProvider {
519 private AnnotationProvider() {
520 super(PID);
521 }
522
523 @Override
524 public void triggerProbe(DeviceId deviceId) {
525 }
526
527 @Override
528 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
529 }
530
531 @Override
532 public boolean isReachable(DeviceId deviceId) {
533 return false;
534 }
535 }
536
alshabib0ccde6d2015-05-30 18:22:36 -0700537}