blob: 37980f1acc00eb89d7bc606248bfcb3d3f8e9102 [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
217 FlowRule.Builder inner = DefaultFlowRule.builder()
218 .forDevice(deviceId)
219 .fromApp(appId)
220 .makePermanent()
221 .withPriority(fwd.priority())
222 .withSelector(fwd.selector())
223 .withTreatment(buildTreatment(popAndRewrite.getLeft(),
224 Instructions.transition(QQ_TABLE)));
225 PortCriterion inPort = (PortCriterion)
226 fwd.selector().getCriterion(Criterion.Type.IN_PORT);
227
228 FlowRule.Builder outer = DefaultFlowRule.builder()
229 .forDevice(deviceId)
230 .fromApp(appId)
231 .forTable(QQ_TABLE)
232 .makePermanent()
233 .withPriority(fwd.priority())
234 .withSelector(buildSelector(inPort))
235 .withTreatment(buildTreatment(popAndRewrite.getRight(),
236 output));
237
238 applyRules(fwd, inner, outer);
239
240 }
241
242 private void installUpstreamRules(ForwardingObjective fwd) {
243 List<Pair<Instruction, Instruction>> vlanOps =
244 vlanOps(fwd,
245 L2ModificationInstruction.L2SubType.VLAN_PUSH);
246
247 if (vlanOps == null) {
248 return;
249 }
250
251 Instruction output = fetchOutput(fwd, "upstream");
252
253 if (output == null) {
254 return;
255 }
256
257 Pair<Instruction, Instruction> innerPair = vlanOps.remove(0);
258
259 Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
260
261 FlowRule.Builder inner = DefaultFlowRule.builder()
262 .forDevice(deviceId)
263 .fromApp(appId)
264 .makePermanent()
265 .withPriority(fwd.priority())
266 .withSelector(fwd.selector())
267 .withTreatment(buildTreatment(innerPair.getRight(),
268 Instructions.transition(QQ_TABLE)));
269
270 PortCriterion inPort = (PortCriterion)
271 fwd.selector().getCriterion(Criterion.Type.IN_PORT);
272
273 VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
274 innerPair.getRight()).vlanId();
275
276 FlowRule.Builder outer = DefaultFlowRule.builder()
277 .forDevice(deviceId)
278 .fromApp(appId)
279 .forTable(QQ_TABLE)
280 .makePermanent()
281 .withPriority(fwd.priority())
282 .withSelector(buildSelector(inPort,
283 Criteria.matchVlanId(cVlanId)))
284 .withTreatment(buildTreatment(outerPair.getLeft(),
285 outerPair.getRight(),
286 output));
287
288 applyRules(fwd, inner, outer);
289
290 }
291
292 private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
293 Instruction output = fwd.treatment().allInstructions().stream()
294 .filter(i -> i.type() == Instruction.Type.OUTPUT)
295 .findFirst().orElse(null);
296
297 if (output == null) {
298 log.error("OLT {} rule has no output", direction);
299 fail(fwd, ObjectiveError.BADPARAMS);
300 return null;
301 }
302 return output;
303 }
304
305 private List<Pair<Instruction, Instruction>> vlanOps(ForwardingObjective fwd,
306 L2ModificationInstruction.L2SubType type) {
307
308 List<Pair<Instruction, Instruction>> vlanOps = findVlanOps(
309 fwd.treatment().allInstructions(), type);
310
311 if (vlanOps == null) {
312 String direction = type == L2ModificationInstruction.L2SubType.VLAN_POP
313 ? "downstream" : "upstream";
314 log.error("Missing vlan operations in {} forwarding: {}", direction, fwd);
315 fail(fwd, ObjectiveError.BADPARAMS);
316 return null;
317 }
318 return vlanOps;
319 }
320
321
322 private List<Pair<Instruction, Instruction>> findVlanOps(List<Instruction> instructions,
323 L2ModificationInstruction.L2SubType type) {
324
325 List<Instruction> vlanPushs = findL2Instructions(
326 type,
327 instructions);
328 List<Instruction> vlanSets = findL2Instructions(
329 L2ModificationInstruction.L2SubType.VLAN_ID,
330 instructions);
331
332 if (vlanPushs.size() != vlanSets.size()) {
333 return null;
334 }
335
336 List<Pair<Instruction, Instruction>> pairs = Lists.newArrayList();
337
338 for (int i = 0; i < vlanPushs.size(); i++) {
339 pairs.add(new ImmutablePair<>(vlanPushs.get(i), vlanSets.get(i)));
340 }
341 return pairs;
342 }
343
344 private List<Instruction> findL2Instructions(L2ModificationInstruction.L2SubType subType,
345 List<Instruction> actions) {
346 return actions.stream()
347 .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
348 .filter(i -> ((L2ModificationInstruction) i).subtype() == subType)
349 .collect(Collectors.toList());
350 }
351
352 private void provisionEapol(FilteringObjective filter,
353 EthTypeCriterion ethType,
354 Instructions.OutputInstruction output) {
355
356 TrafficSelector selector = buildSelector(filter.key(), ethType);
357 TrafficTreatment treatment = buildTreatment(output);
358 buildAndApplyRule(filter, selector, treatment);
359
360 }
361
362 private void provisionIGMP(FilteringObjective filter, EthTypeCriterion ethType,
363 IPProtocolCriterion ipProto,
364 Instructions.OutputInstruction output) {
365 TrafficSelector selector = buildSelector(filter.key(), ethType, ipProto);
366 TrafficTreatment treatment = buildTreatment(output);
367 buildAndApplyRule(filter, selector, treatment);
368 }
369
370 private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
371 TrafficTreatment treatment) {
372 FlowRule rule = DefaultFlowRule.builder()
373 .forDevice(deviceId)
374 .forTable(0)
375 .fromApp(filter.appId())
376 .makePermanent()
377 .withSelector(selector)
378 .withTreatment(treatment)
379 .build();
380
381 FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
382
383 switch (filter.type()) {
384 case PERMIT:
385 opsBuilder.add(rule);
386 break;
387 case DENY:
388 opsBuilder.remove(rule);
389 break;
390 default:
391 log.warn("Unknown filter type : {}", filter.type());
392 fail(filter, ObjectiveError.UNSUPPORTED);
393 }
394
395 applyFlowRules(opsBuilder, filter);
396 }
397
398 private void applyRules(ForwardingObjective fwd,
399 FlowRule.Builder inner, FlowRule.Builder outer) {
400 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
401 switch (fwd.op()) {
402 case ADD:
403 builder.add(inner.build()).add(outer.build());
404 break;
405 case REMOVE:
406 builder.remove(inner.build()).remove(outer.build());
407 break;
408 case ADD_TO_EXISTING:
409 break;
410 case REMOVE_FROM_EXISTING:
411 break;
412 default:
413 log.warn("Unknown forwarding operation: {}", fwd.op());
414 }
415
416 applyFlowRules(builder, fwd);
417 }
418
419 private void applyFlowRules(FlowRuleOperations.Builder builder,
420 Objective objective) {
421 flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
422 @Override
423 public void onSuccess(FlowRuleOperations ops) {
424 pass(objective);
425 }
426
427 @Override
428 public void onError(FlowRuleOperations ops) {
429 fail(objective, ObjectiveError.FLOWINSTALLATIONFAILED);
430 }
431 }));
432 }
433
434 private Criterion filterForCriterion(Collection<Criterion> criteria, Criterion.Type type) {
435 return criteria.stream()
436 .filter(c -> c.type().equals(Criterion.Type.ETH_TYPE))
437 .limit(1)
438 .findFirst().orElse(null);
439 }
440
441 private TrafficSelector buildSelector(Criterion... criteria) {
442
443
444 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
445
446 for (Criterion c : criteria) {
447 sBuilder.add(c);
448 }
449
450 return sBuilder.build();
451 }
452
453 private TrafficTreatment buildTreatment(Instruction... instructions) {
454
455
456 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
457
458 for (Instruction i : instructions) {
459 tBuilder.add(i);
460 }
461
462 return tBuilder.build();
463 }
464
465
466 private void fail(Objective obj, ObjectiveError error) {
467 if (obj.context().isPresent()) {
468 obj.context().get().onError(obj, error);
469 }
470 }
471
472 private void pass(Objective obj) {
473 if (obj.context().isPresent()) {
474 obj.context().get().onSuccess(obj);
475 }
476 }
477
alshabib2cc73cb2015-06-30 20:26:56 -0700478 /**
479 * Build a device description.
Jonathan Hart3e594642015-10-20 17:31:24 -0700480 *
alshabib2cc73cb2015-06-30 20:26:56 -0700481 * @param deviceId a deviceId
alshabibfd430b62015-12-16 18:56:38 -0800482 * @param key the key of the annotation
483 * @param value the value for the annotation
alshabib2cc73cb2015-06-30 20:26:56 -0700484 * @return a device description
485 */
486 private DeviceDescription description(DeviceId deviceId, String key, String value) {
487 DeviceService deviceService = serviceDirectory.get(DeviceService.class);
488 Device device = deviceService.getDevice(deviceId);
489
490 checkNotNull(device, "Device not found in device service.");
491
492 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
493 if (value != null) {
494 builder.set(key, value);
495 } else {
496 builder.remove(key);
497 }
498 return new DefaultDeviceDescription(device.id().uri(), device.type(),
499 device.manufacturer(), device.hwVersion(),
500 device.swVersion(), device.serialNumber(),
501 device.chassisId(), builder.build());
502 }
503
504 /**
505 * Simple ancillary provider used to annotate device.
506 */
507 private static final class AnnotationProvider
508 extends AbstractProvider implements DeviceProvider {
509 private AnnotationProvider() {
510 super(PID);
511 }
512
513 @Override
514 public void triggerProbe(DeviceId deviceId) {
515 }
516
517 @Override
518 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
519 }
520
521 @Override
522 public boolean isReachable(DeviceId deviceId) {
523 return false;
524 }
525 }
526
alshabib0ccde6d2015-05-30 18:22:36 -0700527}