blob: 3f683ef43489f2df83fe609932ddad3001c85840 [file] [log] [blame]
Gamze Abaka1b7816e2019-11-25 06:38:41 +00001/*
2 * Copyright 2016-present Open Networking Foundation
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.opencord.olt.impl;
17
18import com.google.common.collect.Sets;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
27import org.onlab.packet.EthType;
28import org.onlab.packet.IPv4;
29import org.onlab.packet.IPv6;
30import org.onlab.packet.MacAddress;
31import org.onlab.packet.TpPort;
32import org.onlab.packet.VlanId;
33import org.onlab.util.Tools;
34import org.onosproject.cfg.ComponentConfigService;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
37import org.onosproject.mastership.MastershipService;
38import org.onosproject.net.AnnotationKeys;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.Port;
41import org.onosproject.net.PortNumber;
42import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.flow.DefaultTrafficSelector;
44import org.onosproject.net.flow.DefaultTrafficTreatment;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.flow.criteria.Criteria;
48import org.onosproject.net.flowobjective.DefaultFilteringObjective;
49import org.onosproject.net.flowobjective.DefaultForwardingObjective;
50import org.onosproject.net.flowobjective.FilteringObjective;
51import org.onosproject.net.flowobjective.FlowObjectiveService;
52import org.onosproject.net.flowobjective.ForwardingObjective;
53import org.onosproject.net.flowobjective.Objective;
54import org.onosproject.net.flowobjective.ObjectiveContext;
55import org.onosproject.net.flowobjective.ObjectiveError;
56import org.onosproject.net.meter.MeterId;
57import org.opencord.olt.internalapi.AccessDeviceFlowService;
58import org.opencord.olt.internalapi.AccessDeviceMeterService;
59import org.opencord.sadis.BandwidthProfileInformation;
60import org.opencord.sadis.BaseInformationService;
61import org.opencord.sadis.SadisService;
62import org.opencord.sadis.SubscriberAndDeviceInformation;
63import org.opencord.sadis.UniTagInformation;
64import org.osgi.service.component.ComponentContext;
65import org.slf4j.Logger;
66
67import java.util.Dictionary;
68import java.util.Properties;
69import java.util.Set;
70import java.util.concurrent.CompletableFuture;
71
72import static com.google.common.base.Strings.isNullOrEmpty;
73import static org.onlab.util.Tools.get;
74import static org.slf4j.LoggerFactory.getLogger;
75
76@Service
77@Component(immediate = true)
78public class OltFlowService implements AccessDeviceFlowService {
79
80 private static final String APP_NAME = "org.opencord.olt";
81 private static final int NONE_TP_ID = -1;
82 private static final int NO_PCP = -1;
83 private static final Integer MAX_PRIORITY = 10000;
84 private static final Integer MIN_PRIORITY = 1000;
85 private static final int DEFAULT_TP_ID = 64;
86 private static final String INSTALLED = "installed";
87 private static final String REMOVED = "removed";
88 private static final String INSTALLATION = "installation";
89 private static final String REMOVAL = "removal";
90 private static final String V4 = "V4";
91 private static final String V6 = "V6";
92 private static final String NO_MAC = "A4:23:05:00:00:00";
93
94 private final Logger log = getLogger(getClass());
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected FlowObjectiveService flowObjectiveService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected MastershipService mastershipService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected SadisService sadisService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DeviceService deviceService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected AccessDeviceMeterService oltMeterService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected ComponentConfigService componentConfigService;
116
117 @Property(name = "enableIgmpOnProvisioning", boolValue = false,
118 label = "Create IGMP Flow rules when an NNI port is up")
119 protected boolean enableIgmpOnProvisioning = false;
120
121 @Property(name = "enableDhcpOnProvisioning", boolValue = true,
122 label = "Create the DHCP Flow rules when an NNI port is up")
123 protected boolean enableDhcpOnProvisioning = false;
124
125 @Property(name = "enableDhcpV4", boolValue = true,
126 label = "Enable flows for DHCP v4")
127 protected boolean enableDhcpV4 = true;
128
129 @Property(name = "enableDhcpV6", boolValue = false,
130 label = "Enable flows for DHCP v6")
131 protected boolean enableDhcpV6 = false;
132
133 @Property(name = "enableEapol", boolValue = false,
134 label = "Send EAPOL authentication trap flows before subscriber provisioning")
135 protected boolean enableEapol = false;
136
137 @Property(name = "defaultTechProfileId", intValue = DEFAULT_TP_ID,
138 label = "Default technology profile id that is used for authentication trap flows")
139 protected int defaultTechProfileId = DEFAULT_TP_ID;
140
141 protected ApplicationId appId;
142 protected BaseInformationService<BandwidthProfileInformation> bpService;
143 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
144 private Set<PortNumber> pendingAddEapol = Sets.newConcurrentHashSet();
145
146 @Activate
147 public void activate(ComponentContext context) {
148 bpService = sadisService.getBandwidthProfileService();
149 subsService = sadisService.getSubscriberInfoService();
150 componentConfigService.registerProperties(getClass());
151 appId = coreService.getAppId(APP_NAME);
152 log.info("Olt Flow Service started");
153 }
154
155
156 @Deactivate
157 public void deactivate(ComponentContext context) {
158 componentConfigService.unregisterProperties(getClass(), false);
159 log.info("Olt flow service stopped");
160 }
161
162 @Modified
163 public void modified(ComponentContext context) {
164
165 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
166
167 Boolean o = Tools.isPropertyEnabled(properties, "enableDhcpOnProvisioning");
168 if (o != null) {
169 enableDhcpOnProvisioning = o;
170 }
171
172 Boolean v4 = Tools.isPropertyEnabled(properties, "enableDhcpV4");
173 if (v4 != null) {
174 enableDhcpV4 = v4;
175 }
176
177 Boolean v6 = Tools.isPropertyEnabled(properties, "enableDhcpV6");
178 if (v6 != null) {
179 enableDhcpV6 = v6;
180 }
181
182 Boolean p = Tools.isPropertyEnabled(properties, "enableIgmpOnProvisioning");
183 if (p != null) {
184 enableIgmpOnProvisioning = p;
185 }
186
187 Boolean eap = Tools.isPropertyEnabled(properties, "enableEapol");
188 if (eap != null) {
189 enableEapol = eap;
190 }
191
192 String tpId = get(properties, "defaultTechProfileId");
193 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID : Integer.parseInt(tpId.trim());
194
195 }
196
197 @Override
198 public void processDhcpFilteringObjectives(DeviceId devId, PortNumber port,
199 MeterId upstreamMeterId,
200 UniTagInformation tagInformation,
201 boolean install,
202 boolean upstream) {
203 if (!enableDhcpOnProvisioning && !upstream) {
204 log.debug("Dhcp provisioning is disabled.");
205 return;
206 }
207
208 if (!mastershipService.isLocalMaster(devId)) {
209 return;
210 }
211
212 int techProfileId = tagInformation != null ? tagInformation.getTechnologyProfileId() : NONE_TP_ID;
213 VlanId cTag = tagInformation != null ? tagInformation.getPonCTag() : VlanId.NONE;
214 VlanId unitagMatch = tagInformation != null ? tagInformation.getUniTagMatch() : VlanId.ANY;
215
216 if (enableDhcpV4) {
217 int udpSrc = (upstream) ? 68 : 67;
218 int udpDst = (upstream) ? 67 : 68;
219
220 EthType ethType = EthType.EtherType.IPV4.ethType();
221 byte protocol = IPv4.PROTOCOL_UDP;
222
223 this.addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType,
224 upstreamMeterId, techProfileId, protocol, cTag, unitagMatch, install);
225 }
226
227 if (enableDhcpV6) {
228 int udpSrc = (upstream) ? 547 : 546;
229 int udpDst = (upstream) ? 546 : 547;
230
231 EthType ethType = EthType.EtherType.IPV6.ethType();
232 byte protocol = IPv6.PROTOCOL_UDP;
233
234 this.addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType,
235 upstreamMeterId, techProfileId, protocol, cTag, unitagMatch, install);
236 }
237 }
238
239 private void addDhcpFilteringObjectives(DeviceId devId, PortNumber port, int udpSrc, int udpDst,
240 EthType ethType, MeterId upstreamMeterId, int techProfileId, byte protocol,
241 VlanId cTag, VlanId unitagMatch, boolean install) {
242
243 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
244 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
245
246 if (upstreamMeterId != null) {
247 treatmentBuilder.meter(upstreamMeterId);
248 }
249
250 if (techProfileId != NONE_TP_ID) {
251 treatmentBuilder.writeMetadata(createTechProfValueForWm(unitagMatch, techProfileId), 0);
252 }
253
254 FilteringObjective.Builder dhcpUpstreamBuilder = (install ? builder.permit() : builder.deny())
255 .withKey(Criteria.matchInPort(port))
256 .addCondition(Criteria.matchEthType(ethType))
257 .addCondition(Criteria.matchIPProtocol(protocol))
258 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
259 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
260 .withMeta(treatmentBuilder
261 .setOutput(PortNumber.CONTROLLER).build())
262 .fromApp(appId)
263 .withPriority(MAX_PRIORITY);
264
265 if (!VlanId.NONE.equals(cTag)) {
266 dhcpUpstreamBuilder.addCondition(Criteria.matchVlanId(cTag));
267 }
268
269 FilteringObjective dhcpUpstream = dhcpUpstreamBuilder.add(new ObjectiveContext() {
270 @Override
271 public void onSuccess(Objective objective) {
272 log.info("DHCP {} filter for device {} on port {} {}.",
273 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
274 devId, port, (install) ? INSTALLED : REMOVED);
275 }
276
277 @Override
278 public void onError(Objective objective, ObjectiveError error) {
279 log.info("DHCP {} filter for device {} on port {} failed {} because {}",
280 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
281 devId, port, (install) ? INSTALLATION : REMOVAL,
282 error);
283 }
284 });
285
286 flowObjectiveService.filter(devId, dhcpUpstream);
287
288 }
289
290 @Override
291 public void processIgmpFilteringObjectives(DeviceId devId, PortNumber port,
292 MeterId upstreamMeterId,
293 UniTagInformation tagInformation,
294 boolean install,
295 boolean upstream) {
296 if (!enableIgmpOnProvisioning && !upstream) {
297 log.debug("Igmp provisioning is disabled.");
298 return;
299 }
300
301 if (!mastershipService.isLocalMaster(devId)) {
302 return;
303 }
304
305 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
306 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
307 if (upstream) {
308
309 if (tagInformation.getTechnologyProfileId() != NONE_TP_ID) {
310 treatmentBuilder.writeMetadata(createTechProfValueForWm(null,
311 tagInformation.getTechnologyProfileId()), 0);
312 }
313
314
315 if (upstreamMeterId != null) {
316 treatmentBuilder.meter(upstreamMeterId);
317 }
318
319 if (!VlanId.NONE.equals(tagInformation.getPonCTag())) {
320 builder.addCondition(Criteria.matchVlanId(tagInformation.getPonCTag()));
321 }
322
323 if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
324 builder.addCondition(Criteria.matchVlanPcp((byte) tagInformation.getUsPonCTagPriority()));
325 }
326 }
327
328 builder = install ? builder.permit() : builder.deny();
329
330 FilteringObjective igmp = builder
331 .withKey(Criteria.matchInPort(port))
332 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
333 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
334 .withMeta(treatmentBuilder
335 .setOutput(PortNumber.CONTROLLER).build())
336 .fromApp(appId)
337 .withPriority(MAX_PRIORITY)
338 .add(new ObjectiveContext() {
339 @Override
340 public void onSuccess(Objective objective) {
341 log.info("Igmp filter for {} on {} {}.",
342 devId, port, (install) ? INSTALLED : REMOVED);
343 }
344
345 @Override
346 public void onError(Objective objective, ObjectiveError error) {
347 log.info("Igmp filter for {} on {} failed {} because {}.",
348 devId, port, (install) ? INSTALLATION : REMOVAL,
349 error);
350 }
351 });
352
353 flowObjectiveService.filter(devId, igmp);
354 }
355
356 @Override
357 public void processEapolFilteringObjectives(DeviceId devId, PortNumber portNumber, String bpId,
358 CompletableFuture<ObjectiveError> filterFuture,
359 VlanId vlanId, boolean install) {
360
361 if (!enableEapol) {
362 log.debug("Eapol filtering is disabled. Completing filteringFuture immediately for the device {}", devId);
363 if (filterFuture != null) {
364 filterFuture.complete(null);
365 }
366 return;
367 }
368
369 if (!mastershipService.isLocalMaster(devId)) {
370 log.warn("The master of the device {} is another instance", devId);
371 if (filterFuture != null) {
372 filterFuture.complete(ObjectiveError.DEVICEMISSING);
373 }
374 return;
375 }
376
377 BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bpId);
378 if (bpInfo == null) {
379 log.warn("Bandwidth profile {} is not found. Authentication flow"
380 + " will not be installed", bpId);
381 if (filterFuture != null) {
382 filterFuture.complete(ObjectiveError.BADPARAMS);
383 }
384 return;
385 }
386
387 if (install) {
388 boolean added = pendingAddEapol.add(portNumber);
389 if (!added) {
390 if (filterFuture != null) {
391 log.warn("The eapol flow is processing for the port {}. Ignoring this request", portNumber);
392 filterFuture.complete(null);
393 }
394 return;
395 }
396 log.info("portNumber added to pendingAddEapol map {}", portNumber);
397 }
398
399 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
400 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
401 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
402
403 // check if meter exists and create it only for an install
404 MeterId meterId = oltMeterService.getMeterIdFromBpMapping(devId, bpInfo.id());
405 if (meterId == null) {
406 if (install) {
407 meterId = oltMeterService.createMeter(devId, bpInfo, meterFuture);
408 treatmentBuilder.meter(meterId);
409 } else {
410 // this case should not happen as the request to remove an eapol
411 // flow should mean that the flow points to a meter that exists.
412 // Nevertheless we can still delete the flow as we only need the
413 // correct 'match' to do so.
414 log.warn("Unknown meter id for bp {}, still proceeding with "
415 + "delete of eapol flow for {}/{}", bpInfo.id(), devId, portNumber);
416 meterFuture.complete(null);
417 }
418 } else {
419 log.debug("Meter {} was previously created for bp {}", meterId, bpInfo.id());
420 treatmentBuilder.meter(meterId);
421 meterFuture.complete(null);
422 }
423
424 final MeterId mId = meterId;
425 meterFuture.thenAcceptAsync(result -> {
426 if (result == null) {
427 log.info("Meter {} for {} on {}/{} exists. {} EAPOL trap flow",
428 mId, bpId, devId, portNumber,
429 (install) ? "Installing" : "Removing");
430 int techProfileId = getDefaultTechProfileId(devId, portNumber);
431
432 //Authentication trap flow uses only tech profile id as write metadata value
433 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
434 .withKey(Criteria.matchInPort(portNumber))
435 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
436 .addCondition(Criteria.matchVlanId(vlanId))
437 .withMeta(treatmentBuilder
438 .writeMetadata(createTechProfValueForWm(vlanId, techProfileId), 0)
439 .setOutput(PortNumber.CONTROLLER).build())
440 .fromApp(appId)
441 .withPriority(MAX_PRIORITY)
442 .add(new ObjectiveContext() {
443 @Override
444 public void onSuccess(Objective objective) {
445 log.info("Eapol filter for {} on {} {} with meter {}.",
446 devId, portNumber, (install) ? INSTALLED : REMOVED, mId);
447 if (filterFuture != null) {
448 filterFuture.complete(null);
449 }
450 pendingAddEapol.remove(portNumber);
451 }
452
453 @Override
454 public void onError(Objective objective, ObjectiveError error) {
455 log.info("Eapol filter for {} on {} with meter {} failed {} because {}",
456 devId, portNumber, mId, (install) ? INSTALLATION : REMOVAL,
457 error);
458 if (filterFuture != null) {
459 filterFuture.complete(error);
460 }
461 pendingAddEapol.remove(portNumber);
462 }
463 });
464
465 flowObjectiveService.filter(devId, eapol);
466 } else {
467 log.warn("Meter installation error while sending eapol trap flow. " +
468 "Result {} and MeterId {}", result, mId);
469 }
470 });
471 }
472
473 /**
474 * Installs trap filtering objectives for particular traffic types on an
475 * NNI port.
476 *
477 * @param devId device ID
478 * @param port port number
479 * @param install true to install, false to remove
480 */
481 @Override
482 public void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
483 log.info("Sending flows for NNI port {} of the device {}", port, devId);
484 processLldpFilteringObjective(devId, port, install);
485 processDhcpFilteringObjectives(devId, port, null, null, install, false);
486 processIgmpFilteringObjectives(devId, port, null, null, install, false);
487 }
488
489
490 @Override
491 public void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
492 if (!mastershipService.isLocalMaster(devId)) {
493 return;
494 }
495 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
496
497 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
498 .withKey(Criteria.matchInPort(port))
499 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
500 .withMeta(DefaultTrafficTreatment.builder()
501 .setOutput(PortNumber.CONTROLLER).build())
502 .fromApp(appId)
503 .withPriority(MAX_PRIORITY)
504 .add(new ObjectiveContext() {
505 @Override
506 public void onSuccess(Objective objective) {
507 log.info("LLDP filter for device {} on port {} {}.",
508 devId, port, (install) ? INSTALLED : REMOVED);
509 }
510
511 @Override
512 public void onError(Objective objective, ObjectiveError error) {
513 log.info("LLDP filter for device {} on port {} failed {} because {}",
514 devId, port, (install) ? INSTALLATION : REMOVAL,
515 error);
516 }
517 });
518
519 flowObjectiveService.filter(devId, lldp);
520 }
521
522 @Override
523 public ForwardingObjective.Builder createTransparentBuilder(PortNumber uplinkPort,
524 PortNumber subscriberPort,
525 MeterId meterId,
526 UniTagInformation tagInfo,
527 boolean upstream) {
528
529 TrafficSelector selector = DefaultTrafficSelector.builder()
530 .matchVlanId(tagInfo.getPonSTag())
531 .matchInPort(upstream ? subscriberPort : uplinkPort)
532 .matchInnerVlanId(tagInfo.getPonCTag())
533 .build();
534
535 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
536 if (meterId != null) {
537 tBuilder.meter(meterId);
538 }
539
540 TrafficTreatment treatment = tBuilder
541 .setOutput(upstream ? uplinkPort : subscriberPort)
542 .writeMetadata(createMetadata(upstream ? tagInfo.getPonSTag() : tagInfo.getPonCTag(),
543 tagInfo.getTechnologyProfileId(), upstream ? uplinkPort : subscriberPort), 0)
544 .build();
545
546 return createForwardingObjectiveBuilder(selector, treatment, MIN_PRIORITY);
547 }
548
549 @Override
550 public ForwardingObjective.Builder createUpBuilder(PortNumber uplinkPort,
551 PortNumber subscriberPort,
552 MeterId upstreamMeterId,
553 UniTagInformation uniTagInformation) {
554
555 TrafficSelector selector = DefaultTrafficSelector.builder()
556 .matchInPort(subscriberPort)
557 .matchVlanId(uniTagInformation.getUniTagMatch())
558 .build();
559
560 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
561 .pushVlan()
562 .setVlanId(uniTagInformation.getPonCTag());
563
564 if (uniTagInformation.getUsPonCTagPriority() != NO_PCP) {
565 treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonCTagPriority());
566 }
567
568 treatmentBuilder.pushVlan()
569 .setVlanId(uniTagInformation.getPonSTag());
570
571 if (uniTagInformation.getUsPonSTagPriority() != NO_PCP) {
572 treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonSTagPriority());
573 }
574
575 treatmentBuilder.setOutput(uplinkPort)
576 .writeMetadata(createMetadata(uniTagInformation.getPonCTag(),
577 uniTagInformation.getTechnologyProfileId(), uplinkPort), 0L);
578
579 if (upstreamMeterId != null) {
580 treatmentBuilder.meter(upstreamMeterId);
581 }
582
583 return createForwardingObjectiveBuilder(selector, treatmentBuilder.build(), MIN_PRIORITY);
584 }
585
586 @Override
587 public ForwardingObjective.Builder createDownBuilder(PortNumber uplinkPort,
588 PortNumber subscriberPort,
589 MeterId downstreamMeterId,
590 UniTagInformation tagInformation) {
591 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
592 .matchVlanId(tagInformation.getPonSTag())
593 .matchInPort(uplinkPort)
594 .matchInnerVlanId(tagInformation.getPonCTag())
595 .matchMetadata(tagInformation.getPonCTag().toShort());
596
597 if (tagInformation.getDsPonSTagPriority() != NO_PCP) {
598 selectorBuilder.matchVlanPcp((byte) tagInformation.getDsPonSTagPriority());
599 }
600
601 if (tagInformation.getConfiguredMacAddress() != null &&
602 !NO_MAC.equals(tagInformation.getConfiguredMacAddress())) {
603 selectorBuilder.matchEthDst(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
604 }
605
606 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
607 .popVlan()
608 .setOutput(subscriberPort)
609 .writeMetadata(createMetadata(tagInformation.getPonCTag(), tagInformation.getTechnologyProfileId(),
610 subscriberPort), 0);
611
612 // to remark inner vlan header
613 if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
614 treatmentBuilder.setVlanPcp((byte) tagInformation.getUsPonCTagPriority());
615 }
616
617 if (!VlanId.NONE.equals(tagInformation.getUniTagMatch())) {
618 treatmentBuilder.setVlanId(tagInformation.getUniTagMatch());
619 }
620
621 if (downstreamMeterId != null) {
622 treatmentBuilder.meter(downstreamMeterId);
623 }
624
625 return createForwardingObjectiveBuilder(selectorBuilder.build(), treatmentBuilder.build(), MIN_PRIORITY);
626 }
627
628 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
629 TrafficTreatment treatment,
630 Integer priority) {
631 return DefaultForwardingObjective.builder()
632 .withFlag(ForwardingObjective.Flag.VERSATILE)
633 .withPriority(priority)
634 .makePermanent()
635 .withSelector(selector)
636 .fromApp(appId)
637 .withTreatment(treatment);
638 }
639
640 /**
641 * Returns the write metadata value including tech profile reference and innerVlan.
642 * For param cVlan, null can be sent
643 *
644 * @param cVlan c (customer) tag of one subscriber
645 * @param techProfileId tech profile id of one subscriber
646 * @return the write metadata value including tech profile reference and innerVlan
647 */
648 private Long createTechProfValueForWm(VlanId cVlan, int techProfileId) {
649 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
650 return (long) techProfileId << 32;
651 }
652 return ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
653 }
654
655 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
656 if (bandwidthProfile == null) {
657 return null;
658 }
659 return bpService.get(bandwidthProfile);
660 }
661
662 /**
663 * It will be used to support AT&T use case (for EAPOL flows).
664 * If multiple services are found in uniServiceList, returns default tech profile id
665 * If one service is found, returns the found one
666 *
667 * @param devId
668 * @param portNumber
669 * @return the default technology profile id
670 */
671 private int getDefaultTechProfileId(DeviceId devId, PortNumber portNumber) {
672 Port port = deviceService.getPort(devId, portNumber);
673 if (port != null) {
674 SubscriberAndDeviceInformation info = subsService.get(port.annotations().value(AnnotationKeys.PORT_NAME));
675 if (info != null && info.uniTagList().size() == 1) {
676 return info.uniTagList().get(0).getTechnologyProfileId();
677 }
678 }
679 return defaultTechProfileId;
680 }
681
682 /**
683 * Write metadata instruction value (metadata) is 8 bytes.
684 * <p>
685 * MS 2 bytes: C Tag
686 * Next 2 bytes: Technology Profile Id
687 * Next 4 bytes: Port number (uni or nni)
688 */
689 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
690 if (techProfileId == NONE_TP_ID) {
691 techProfileId = DEFAULT_TP_ID;
692 }
693
694 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
695 }
696
697
698}