blob: b4dca749ae5eec33763a34c497e4b4ba8ed4b2d6 [file] [log] [blame]
Andrea Campanellacbbb7952019-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.onlab.packet.EthType;
20import org.onlab.packet.IPv4;
21import org.onlab.packet.IPv6;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.TpPort;
24import org.onlab.packet.VlanId;
25import org.onlab.util.Tools;
26import org.onosproject.cfg.ComponentConfigService;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.mastership.MastershipService;
30import org.onosproject.net.AnnotationKeys;
Matteo Scandolo3a037a32020-04-01 12:17:50 -070031import org.onosproject.net.ConnectPoint;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000032import org.onosproject.net.DeviceId;
33import org.onosproject.net.Port;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.device.DeviceService;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.flow.criteria.Criteria;
41import org.onosproject.net.flowobjective.DefaultFilteringObjective;
42import org.onosproject.net.flowobjective.DefaultForwardingObjective;
43import org.onosproject.net.flowobjective.FilteringObjective;
44import org.onosproject.net.flowobjective.FlowObjectiveService;
45import org.onosproject.net.flowobjective.ForwardingObjective;
46import org.onosproject.net.flowobjective.Objective;
47import org.onosproject.net.flowobjective.ObjectiveContext;
48import org.onosproject.net.flowobjective.ObjectiveError;
49import org.onosproject.net.meter.MeterId;
50import org.opencord.olt.internalapi.AccessDeviceFlowService;
51import org.opencord.olt.internalapi.AccessDeviceMeterService;
Andrea Campanella3ce4d282020-06-09 13:46:58 +020052import org.opencord.olt.internalapi.DeviceBandwidthProfile;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000053import org.opencord.sadis.BandwidthProfileInformation;
54import org.opencord.sadis.BaseInformationService;
55import org.opencord.sadis.SadisService;
56import org.opencord.sadis.SubscriberAndDeviceInformation;
57import org.opencord.sadis.UniTagInformation;
58import org.osgi.service.component.ComponentContext;
59import org.osgi.service.component.annotations.Activate;
60import org.osgi.service.component.annotations.Component;
61import org.osgi.service.component.annotations.Deactivate;
62import org.osgi.service.component.annotations.Modified;
63import org.osgi.service.component.annotations.Reference;
64import org.osgi.service.component.annotations.ReferenceCardinality;
65import org.slf4j.Logger;
66
67import java.util.Dictionary;
Andrea Campanella3ce4d282020-06-09 13:46:58 +020068import java.util.Iterator;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000069import java.util.Properties;
70import java.util.Set;
71import java.util.concurrent.CompletableFuture;
72
73import static com.google.common.base.Strings.isNullOrEmpty;
74import static org.onlab.util.Tools.get;
75import static org.opencord.olt.impl.OsgiPropertyConstants.*;
76import static org.slf4j.LoggerFactory.getLogger;
77
78/**
79 * Provisions flow rules on access devices.
80 */
81@Component(immediate = true, property = {
82 ENABLE_DHCP_ON_PROVISIONING + ":Boolean=" + ENABLE_DHCP_ON_PROVISIONING_DEFAULT,
83 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
84 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
85 ENABLE_IGMP_ON_PROVISIONING + ":Boolean=" + ENABLE_IGMP_ON_PROVISIONING_DEFAULT,
86 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
87 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT
88})
89public class OltFlowService implements AccessDeviceFlowService {
90
91 private static final String APP_NAME = "org.opencord.olt";
92 private static final int NONE_TP_ID = -1;
93 private static final int NO_PCP = -1;
94 private static final Integer MAX_PRIORITY = 10000;
95 private static final Integer MIN_PRIORITY = 1000;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000096 private static final String INSTALLED = "installed";
97 private static final String REMOVED = "removed";
98 private static final String INSTALLATION = "installation";
99 private static final String REMOVAL = "removal";
100 private static final String V4 = "V4";
101 private static final String V6 = "V6";
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000102
103 private final Logger log = getLogger(getClass());
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected FlowObjectiveService flowObjectiveService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
109 protected CoreService coreService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
112 protected MastershipService mastershipService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
115 protected SadisService sadisService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
118 protected DeviceService deviceService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
121 protected AccessDeviceMeterService oltMeterService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
124 protected ComponentConfigService componentConfigService;
125
126 /**
127 * Create the DHCP Flow rules when a subscriber is provisioned.
128 **/
129 protected boolean enableDhcpOnProvisioning = ENABLE_DHCP_ON_PROVISIONING_DEFAULT;
130
131 /**
132 * Enable flows for DHCP v4.
133 **/
134 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
135
136 /**
137 * Enable flows for DHCP v6.
138 **/
139 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
140
141 /**
142 * Create IGMP Flow rules when a subscriber is provisioned.
143 **/
144 protected boolean enableIgmpOnProvisioning = ENABLE_IGMP_ON_PROVISIONING_DEFAULT;
145
146 /**
147 * Send EAPOL authentication trap flows before subscriber provisioning.
148 **/
149 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
150
151 /**
152 * Default technology profile id that is used for authentication trap flows.
153 **/
154 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
155
156 protected ApplicationId appId;
157 protected BaseInformationService<BandwidthProfileInformation> bpService;
158 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700159 private Set<ConnectPoint> pendingAddEapol = Sets.newConcurrentHashSet();
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200160 private Set<SubscriberFlowInfo> pendingEapolForMeters = Sets.newConcurrentHashSet();;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000161
162 @Activate
163 public void activate(ComponentContext context) {
164 bpService = sadisService.getBandwidthProfileService();
165 subsService = sadisService.getSubscriberInfoService();
166 componentConfigService.registerProperties(getClass());
167 appId = coreService.getAppId(APP_NAME);
Andrea Campanellafee86422020-06-04 16:01:27 +0200168 log.info("started");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000169 }
170
171
172 @Deactivate
173 public void deactivate(ComponentContext context) {
174 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanellafee86422020-06-04 16:01:27 +0200175 log.info("stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000176 }
177
178 @Modified
179 public void modified(ComponentContext context) {
180
181 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
182
Andrea Campanella7c49b792020-05-11 11:36:53 +0200183 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_PROVISIONING);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000184 if (o != null) {
185 enableDhcpOnProvisioning = o;
186 }
187
Andrea Campanella7c49b792020-05-11 11:36:53 +0200188 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000189 if (v4 != null) {
190 enableDhcpV4 = v4;
191 }
192
Andrea Campanella7c49b792020-05-11 11:36:53 +0200193 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000194 if (v6 != null) {
195 enableDhcpV6 = v6;
196 }
197
Andrea Campanella7c49b792020-05-11 11:36:53 +0200198 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_PROVISIONING);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000199 if (p != null) {
200 enableIgmpOnProvisioning = p;
201 }
202
Andrea Campanella7c49b792020-05-11 11:36:53 +0200203 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000204 if (eap != null) {
205 enableEapol = eap;
206 }
207
Andrea Campanella7c49b792020-05-11 11:36:53 +0200208 String tpId = get(properties, DEFAULT_TP_ID);
209 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000210
Andrea Campanellafee86422020-06-04 16:01:27 +0200211 log.info("modified. Values = enableDhcpOnProvisioning: {}, enableDhcpV4: {}, " +
212 "enableDhcpV6:{}, enableIgmpOnProvisioning:{}, " +
213 "enableEapol{}, defaultTechProfileId: {}",
214 enableDhcpOnProvisioning, enableDhcpV4, enableDhcpV6,
215 enableIgmpOnProvisioning, enableEapol, defaultTechProfileId);
216
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000217 }
218
219 @Override
220 public void processDhcpFilteringObjectives(DeviceId devId, PortNumber port,
221 MeterId upstreamMeterId,
222 UniTagInformation tagInformation,
223 boolean install,
224 boolean upstream) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200225 //tagInformation can be none in case of NNI port.
226 //in that case relying on global flag
227 if (!enableDhcpOnProvisioning || (tagInformation != null
228 && !tagInformation.getIsDhcpRequired())) {
229 log.debug("Dhcp provisioning is disabled for port {} on device {}", devId, port);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000230 return;
231 }
232
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000233 int techProfileId = tagInformation != null ? tagInformation.getTechnologyProfileId() : NONE_TP_ID;
234 VlanId cTag = tagInformation != null ? tagInformation.getPonCTag() : VlanId.NONE;
235 VlanId unitagMatch = tagInformation != null ? tagInformation.getUniTagMatch() : VlanId.ANY;
236
237 if (enableDhcpV4) {
238 int udpSrc = (upstream) ? 68 : 67;
239 int udpDst = (upstream) ? 67 : 68;
240
241 EthType ethType = EthType.EtherType.IPV4.ethType();
242 byte protocol = IPv4.PROTOCOL_UDP;
243
Andrea Campanella7c49b792020-05-11 11:36:53 +0200244 addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000245 upstreamMeterId, techProfileId, protocol, cTag, unitagMatch, install);
246 }
247
248 if (enableDhcpV6) {
249 int udpSrc = (upstream) ? 547 : 546;
250 int udpDst = (upstream) ? 546 : 547;
251
252 EthType ethType = EthType.EtherType.IPV6.ethType();
253 byte protocol = IPv6.PROTOCOL_UDP;
254
Andrea Campanella7c49b792020-05-11 11:36:53 +0200255 addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000256 upstreamMeterId, techProfileId, protocol, cTag, unitagMatch, install);
257 }
258 }
259
260 private void addDhcpFilteringObjectives(DeviceId devId, PortNumber port, int udpSrc, int udpDst,
261 EthType ethType, MeterId upstreamMeterId, int techProfileId, byte protocol,
262 VlanId cTag, VlanId unitagMatch, boolean install) {
263
264 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
265 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
266
267 if (upstreamMeterId != null) {
268 treatmentBuilder.meter(upstreamMeterId);
269 }
270
271 if (techProfileId != NONE_TP_ID) {
272 treatmentBuilder.writeMetadata(createTechProfValueForWm(unitagMatch, techProfileId), 0);
273 }
274
275 FilteringObjective.Builder dhcpUpstreamBuilder = (install ? builder.permit() : builder.deny())
276 .withKey(Criteria.matchInPort(port))
277 .addCondition(Criteria.matchEthType(ethType))
278 .addCondition(Criteria.matchIPProtocol(protocol))
279 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
280 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
281 .withMeta(treatmentBuilder
282 .setOutput(PortNumber.CONTROLLER).build())
283 .fromApp(appId)
284 .withPriority(MAX_PRIORITY);
285
286 if (!VlanId.NONE.equals(cTag)) {
287 dhcpUpstreamBuilder.addCondition(Criteria.matchVlanId(cTag));
288 }
289
290 FilteringObjective dhcpUpstream = dhcpUpstreamBuilder.add(new ObjectiveContext() {
291 @Override
292 public void onSuccess(Objective objective) {
293 log.info("DHCP {} filter for device {} on port {} {}.",
294 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
295 devId, port, (install) ? INSTALLED : REMOVED);
296 }
297
298 @Override
299 public void onError(Objective objective, ObjectiveError error) {
300 log.info("DHCP {} filter for device {} on port {} failed {} because {}",
301 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
302 devId, port, (install) ? INSTALLATION : REMOVAL,
303 error);
304 }
305 });
306
307 flowObjectiveService.filter(devId, dhcpUpstream);
308
309 }
310
311 @Override
312 public void processIgmpFilteringObjectives(DeviceId devId, PortNumber port,
313 MeterId upstreamMeterId,
314 UniTagInformation tagInformation,
315 boolean install,
316 boolean upstream) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200317 //tagInformation can be none in case of NNI port.
318 //in that case relying on global flag
319 if (!enableIgmpOnProvisioning || (tagInformation != null
320 && !tagInformation.getIsIgmpRequired())) {
321 log.debug("Igmp provisioning is disabled for port {} on device {}", devId, port);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000322 return;
323 }
324
Matteo Scandolo34556e52020-05-08 12:34:13 -0700325 if (!upstream) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200326 log.debug("Direction on port {} for device {} " +
327 "is not Upstream, ignoring Igmp request", port, devId);
Matteo Scandolo34556e52020-05-08 12:34:13 -0700328 return;
329 }
330
Andrea Campanellafee86422020-06-04 16:01:27 +0200331 log.debug("{} IGMP flows on {}:{}", (install) ?
332 "Installing" : "Removing", devId, port);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000333 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
334 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
335 if (upstream) {
336
337 if (tagInformation.getTechnologyProfileId() != NONE_TP_ID) {
338 treatmentBuilder.writeMetadata(createTechProfValueForWm(null,
339 tagInformation.getTechnologyProfileId()), 0);
340 }
341
342
343 if (upstreamMeterId != null) {
344 treatmentBuilder.meter(upstreamMeterId);
345 }
346
347 if (!VlanId.NONE.equals(tagInformation.getPonCTag())) {
348 builder.addCondition(Criteria.matchVlanId(tagInformation.getPonCTag()));
349 }
350
351 if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
352 builder.addCondition(Criteria.matchVlanPcp((byte) tagInformation.getUsPonCTagPriority()));
353 }
354 }
355
356 builder = install ? builder.permit() : builder.deny();
357
358 FilteringObjective igmp = builder
359 .withKey(Criteria.matchInPort(port))
360 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
361 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
362 .withMeta(treatmentBuilder
363 .setOutput(PortNumber.CONTROLLER).build())
364 .fromApp(appId)
365 .withPriority(MAX_PRIORITY)
366 .add(new ObjectiveContext() {
367 @Override
368 public void onSuccess(Objective objective) {
369 log.info("Igmp filter for {} on {} {}.",
370 devId, port, (install) ? INSTALLED : REMOVED);
371 }
372
373 @Override
374 public void onError(Objective objective, ObjectiveError error) {
375 log.info("Igmp filter for {} on {} failed {} because {}.",
376 devId, port, (install) ? INSTALLATION : REMOVAL,
377 error);
378 }
379 });
380
381 flowObjectiveService.filter(devId, igmp);
382 }
383
384 @Override
385 public void processEapolFilteringObjectives(DeviceId devId, PortNumber portNumber, String bpId,
386 CompletableFuture<ObjectiveError> filterFuture,
387 VlanId vlanId, boolean install) {
388
389 if (!enableEapol) {
390 log.debug("Eapol filtering is disabled. Completing filteringFuture immediately for the device {}", devId);
391 if (filterFuture != null) {
392 filterFuture.complete(null);
393 }
394 return;
395 }
396
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000397 BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bpId);
398 if (bpInfo == null) {
399 log.warn("Bandwidth profile {} is not found. Authentication flow"
400 + " will not be installed", bpId);
401 if (filterFuture != null) {
402 filterFuture.complete(ObjectiveError.BADPARAMS);
403 }
404 return;
405 }
406
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700407 ConnectPoint cp = new ConnectPoint(devId, portNumber);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000408 if (install) {
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700409 boolean added = pendingAddEapol.add(cp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000410 if (!added) {
411 if (filterFuture != null) {
412 log.warn("The eapol flow is processing for the port {}. Ignoring this request", portNumber);
413 filterFuture.complete(null);
414 }
415 return;
416 }
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700417 log.info("connectPoint added to pendingAddEapol map {}", cp.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000418 }
419
420 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
421 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
422 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
423
424 // check if meter exists and create it only for an install
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200425 final MeterId meterId = oltMeterService.getMeterIdFromBpMapping(devId, bpInfo.id());
426 DeviceBandwidthProfile dm = new DeviceBandwidthProfile(devId, bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000427 if (meterId == null) {
428 if (install) {
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200429 log.debug("Need to install meter for EAPOL with bwp {}", bpInfo.id());
430 SubscriberFlowInfo fi = new SubscriberFlowInfo(devId, null, cp.port(),
431 new UniTagInformation.Builder()
432 .setPonCTag(vlanId).build(),
433 null, null,
434 null, bpInfo.id());
435 pendingEapolForMeters.add(fi);
436
437 if (oltMeterService.isMeterPending(dm)) {
438 log.debug("Meter {} is already pending for EAPOL", dm);
439 return;
440 }
441 oltMeterService.addToPendingMeters(dm);
442 MeterId innerMeterId = oltMeterService.createMeter(devId, bpInfo,
443 meterFuture);
444 fi.setUpMeterId(innerMeterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000445 } else {
446 // this case should not happen as the request to remove an eapol
447 // flow should mean that the flow points to a meter that exists.
448 // Nevertheless we can still delete the flow as we only need the
449 // correct 'match' to do so.
450 log.warn("Unknown meter id for bp {}, still proceeding with "
451 + "delete of eapol flow for {}/{}", bpInfo.id(), devId, portNumber);
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200452 SubscriberFlowInfo fi = new SubscriberFlowInfo(devId, null, cp.port(),
453 new UniTagInformation.Builder()
454 .setPonCTag(vlanId).build(),
455 null, meterId,
456 null, bpInfo.id());
457 handleEapol(filterFuture, install, cp, builder, treatmentBuilder, fi, meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000458 }
459 } else {
460 log.debug("Meter {} was previously created for bp {}", meterId, bpInfo.id());
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200461 SubscriberFlowInfo fi = new SubscriberFlowInfo(devId, null, cp.port(),
462 new UniTagInformation.Builder()
463 .setPonCTag(vlanId).build(),
464 null, meterId,
465 null, bpInfo.id());
466 handleEapol(filterFuture, install, cp, builder, treatmentBuilder, fi, meterId);
467 //No need for the future, meter is present.
468 return;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000469 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000470 meterFuture.thenAcceptAsync(result -> {
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200471 //for each pending eapol flow we check if the meter is there.
472 Iterator<SubscriberFlowInfo> eapIterator = pendingEapolForMeters.iterator();
473 while (eapIterator.hasNext()) {
474 SubscriberFlowInfo fi = eapIterator.next();
475 if (result == null) {
476 MeterId mId = oltMeterService
477 .getMeterIdFromBpMapping(dm.getDevId(), fi.getUpBpInfo());
478 if (mId != null) {
479 handleEapol(filterFuture, install, cp, builder, treatmentBuilder, fi, mId);
480 eapIterator.remove();
481 }
482 } else {
483 log.warn("Meter installation error while sending eapol trap flow. " +
484 "Result {} and MeterId {}", result, meterId);
485 eapIterator.remove();
486 }
487 oltMeterService.removeFromPendingMeters(new DeviceBandwidthProfile(
488 dm.getDevId(), dm.getBwInfo()));
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000489 }
490 });
491 }
492
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200493 private void handleEapol(CompletableFuture<ObjectiveError> filterFuture,
494 boolean install, ConnectPoint cp,
495 DefaultFilteringObjective.Builder builder,
496 TrafficTreatment.Builder treatmentBuilder,
497 SubscriberFlowInfo fi, MeterId mId) {
498 log.info("Meter {} for {} on {}/{} exists. {} EAPOL trap flow",
499 mId, fi.getUpBpInfo(), fi.getDevId(), fi.getUniPort(),
500 (install) ? "Installing" : "Removing");
501 int techProfileId = getDefaultTechProfileId(fi.getDevId(), fi.getUniPort());
502 // can happen in case of removal
503 if (mId != null) {
504 treatmentBuilder.meter(mId);
505 }
506 //Authentication trap flow uses only tech profile id as write metadata value
507 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
508 .withKey(Criteria.matchInPort(fi.getUniPort()))
509 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
510 .addCondition(Criteria.matchVlanId(fi.getTagInfo().getPonCTag()))
511 .withMeta(treatmentBuilder
512 .writeMetadata(createTechProfValueForWm(
513 fi.getTagInfo().getPonCTag(),
514 techProfileId), 0)
515 .setOutput(PortNumber.CONTROLLER).build())
516 .fromApp(appId)
517 .withPriority(MAX_PRIORITY)
518 .add(new ObjectiveContext() {
519 @Override
520 public void onSuccess(Objective objective) {
521 log.info("Eapol filter for {} on {} {} with meter {}.",
522 fi.getDevId(), fi.getUniPort(),
523 (install) ? INSTALLED : REMOVED, mId);
524 if (filterFuture != null) {
525 filterFuture.complete(null);
526 }
527 pendingAddEapol.remove(cp);
528 }
529
530 @Override
531 public void onError(Objective objective, ObjectiveError error) {
532 log.error("Eapol filter for {} on {} with meter {} " +
533 "failed {} because {}",
534 fi.getDevId(), fi.getUniPort(), mId,
535 (install) ? INSTALLATION : REMOVAL,
536 error);
537 if (filterFuture != null) {
538 filterFuture.complete(error);
539 }
540 pendingAddEapol.remove(cp);
541 }
542 });
543 flowObjectiveService.filter(fi.getDevId(), eapol);
544 }
545
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000546 /**
547 * Installs trap filtering objectives for particular traffic types on an
548 * NNI port.
549 *
550 * @param devId device ID
551 * @param port port number
552 * @param install true to install, false to remove
553 */
554 @Override
555 public void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
556 log.info("Sending flows for NNI port {} of the device {}", port, devId);
557 processLldpFilteringObjective(devId, port, install);
558 processDhcpFilteringObjectives(devId, port, null, null, install, false);
559 processIgmpFilteringObjectives(devId, port, null, null, install, false);
560 }
561
562
563 @Override
564 public void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000565 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
566
567 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
568 .withKey(Criteria.matchInPort(port))
569 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
570 .withMeta(DefaultTrafficTreatment.builder()
571 .setOutput(PortNumber.CONTROLLER).build())
572 .fromApp(appId)
573 .withPriority(MAX_PRIORITY)
574 .add(new ObjectiveContext() {
575 @Override
576 public void onSuccess(Objective objective) {
577 log.info("LLDP filter for device {} on port {} {}.",
578 devId, port, (install) ? INSTALLED : REMOVED);
579 }
580
581 @Override
582 public void onError(Objective objective, ObjectiveError error) {
583 log.info("LLDP filter for device {} on port {} failed {} because {}",
584 devId, port, (install) ? INSTALLATION : REMOVAL,
585 error);
586 }
587 });
588
589 flowObjectiveService.filter(devId, lldp);
590 }
591
592 @Override
593 public ForwardingObjective.Builder createTransparentBuilder(PortNumber uplinkPort,
594 PortNumber subscriberPort,
595 MeterId meterId,
596 UniTagInformation tagInfo,
597 boolean upstream) {
598
599 TrafficSelector selector = DefaultTrafficSelector.builder()
600 .matchVlanId(tagInfo.getPonSTag())
601 .matchInPort(upstream ? subscriberPort : uplinkPort)
602 .matchInnerVlanId(tagInfo.getPonCTag())
603 .build();
604
605 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
606 if (meterId != null) {
607 tBuilder.meter(meterId);
608 }
609
610 TrafficTreatment treatment = tBuilder
611 .setOutput(upstream ? uplinkPort : subscriberPort)
612 .writeMetadata(createMetadata(upstream ? tagInfo.getPonSTag() : tagInfo.getPonCTag(),
613 tagInfo.getTechnologyProfileId(), upstream ? uplinkPort : subscriberPort), 0)
614 .build();
615
616 return createForwardingObjectiveBuilder(selector, treatment, MIN_PRIORITY);
617 }
618
619 @Override
620 public ForwardingObjective.Builder createUpBuilder(PortNumber uplinkPort,
621 PortNumber subscriberPort,
622 MeterId upstreamMeterId,
623 UniTagInformation uniTagInformation) {
624
625 TrafficSelector selector = DefaultTrafficSelector.builder()
626 .matchInPort(subscriberPort)
627 .matchVlanId(uniTagInformation.getUniTagMatch())
628 .build();
629
Andrea Campanella327c5722020-01-30 11:34:13 +0100630 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
631 //if the subscriberVlan (cTag) is different than ANY it needs to set.
632 if (uniTagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
633 treatmentBuilder.pushVlan()
634 .setVlanId(uniTagInformation.getPonCTag());
635 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000636
637 if (uniTagInformation.getUsPonCTagPriority() != NO_PCP) {
638 treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonCTagPriority());
639 }
640
641 treatmentBuilder.pushVlan()
642 .setVlanId(uniTagInformation.getPonSTag());
643
644 if (uniTagInformation.getUsPonSTagPriority() != NO_PCP) {
645 treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonSTagPriority());
646 }
647
648 treatmentBuilder.setOutput(uplinkPort)
649 .writeMetadata(createMetadata(uniTagInformation.getPonCTag(),
650 uniTagInformation.getTechnologyProfileId(), uplinkPort), 0L);
651
652 if (upstreamMeterId != null) {
653 treatmentBuilder.meter(upstreamMeterId);
654 }
655
656 return createForwardingObjectiveBuilder(selector, treatmentBuilder.build(), MIN_PRIORITY);
657 }
658
659 @Override
660 public ForwardingObjective.Builder createDownBuilder(PortNumber uplinkPort,
661 PortNumber subscriberPort,
662 MeterId downstreamMeterId,
663 UniTagInformation tagInformation) {
Andrea Campanella327c5722020-01-30 11:34:13 +0100664
665 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000666 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
667 .matchVlanId(tagInformation.getPonSTag())
668 .matchInPort(uplinkPort)
Andrea Campanella090e4a02020-02-05 13:53:55 +0100669 .matchInnerVlanId(tagInformation.getPonCTag());
670
671
672 if (tagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
673 selectorBuilder.matchMetadata(tagInformation.getPonCTag().toShort());
674 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000675
676 if (tagInformation.getDsPonSTagPriority() != NO_PCP) {
677 selectorBuilder.matchVlanPcp((byte) tagInformation.getDsPonSTagPriority());
678 }
679
680 if (tagInformation.getConfiguredMacAddress() != null &&
Daniele Moro7cbf4312020-03-06 17:24:12 -0800681 !tagInformation.getConfiguredMacAddress().equals("") &&
682 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()))) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000683 selectorBuilder.matchEthDst(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
684 }
685
686 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
687 .popVlan()
Andrea Campanella327c5722020-01-30 11:34:13 +0100688 .setOutput(subscriberPort);
689
Andrea Campanella327c5722020-01-30 11:34:13 +0100690 treatmentBuilder.writeMetadata(createMetadata(tagInformation.getPonCTag(),
691 tagInformation.getTechnologyProfileId(),
692 subscriberPort), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000693
694 // to remark inner vlan header
695 if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
696 treatmentBuilder.setVlanPcp((byte) tagInformation.getUsPonCTagPriority());
697 }
698
Andrea Campanella9a779292020-02-03 19:19:09 +0100699 if (!VlanId.NONE.equals(tagInformation.getUniTagMatch()) &&
700 tagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000701 treatmentBuilder.setVlanId(tagInformation.getUniTagMatch());
702 }
703
704 if (downstreamMeterId != null) {
705 treatmentBuilder.meter(downstreamMeterId);
706 }
707
708 return createForwardingObjectiveBuilder(selectorBuilder.build(), treatmentBuilder.build(), MIN_PRIORITY);
709 }
710
711 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
712 TrafficTreatment treatment,
713 Integer priority) {
714 return DefaultForwardingObjective.builder()
715 .withFlag(ForwardingObjective.Flag.VERSATILE)
716 .withPriority(priority)
717 .makePermanent()
718 .withSelector(selector)
719 .fromApp(appId)
720 .withTreatment(treatment);
721 }
722
723 /**
724 * Returns the write metadata value including tech profile reference and innerVlan.
725 * For param cVlan, null can be sent
726 *
727 * @param cVlan c (customer) tag of one subscriber
728 * @param techProfileId tech profile id of one subscriber
729 * @return the write metadata value including tech profile reference and innerVlan
730 */
731 private Long createTechProfValueForWm(VlanId cVlan, int techProfileId) {
732 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
733 return (long) techProfileId << 32;
734 }
735 return ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
736 }
737
738 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
739 if (bandwidthProfile == null) {
740 return null;
741 }
742 return bpService.get(bandwidthProfile);
743 }
744
745 /**
746 * It will be used to support AT&T use case (for EAPOL flows).
747 * If multiple services are found in uniServiceList, returns default tech profile id
748 * If one service is found, returns the found one
749 *
750 * @param devId
751 * @param portNumber
752 * @return the default technology profile id
753 */
754 private int getDefaultTechProfileId(DeviceId devId, PortNumber portNumber) {
755 Port port = deviceService.getPort(devId, portNumber);
756 if (port != null) {
757 SubscriberAndDeviceInformation info = subsService.get(port.annotations().value(AnnotationKeys.PORT_NAME));
758 if (info != null && info.uniTagList().size() == 1) {
759 return info.uniTagList().get(0).getTechnologyProfileId();
760 }
761 }
762 return defaultTechProfileId;
763 }
764
765 /**
766 * Write metadata instruction value (metadata) is 8 bytes.
767 * <p>
768 * MS 2 bytes: C Tag
769 * Next 2 bytes: Technology Profile Id
770 * Next 4 bytes: Port number (uni or nni)
771 */
772 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
773 if (techProfileId == NONE_TP_ID) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200774 techProfileId = DEFAULT_TP_ID_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000775 }
776
777 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
778 }
779
780
781}