blob: a53e0192920dab8c9a30abca560edd8cc4f74067 [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;
52import org.opencord.sadis.BandwidthProfileInformation;
53import org.opencord.sadis.BaseInformationService;
54import org.opencord.sadis.SadisService;
55import org.opencord.sadis.SubscriberAndDeviceInformation;
56import org.opencord.sadis.UniTagInformation;
57import org.osgi.service.component.ComponentContext;
58import org.osgi.service.component.annotations.Activate;
59import org.osgi.service.component.annotations.Component;
60import org.osgi.service.component.annotations.Deactivate;
61import org.osgi.service.component.annotations.Modified;
62import org.osgi.service.component.annotations.Reference;
63import org.osgi.service.component.annotations.ReferenceCardinality;
64import org.slf4j.Logger;
65
66import java.util.Dictionary;
Andrea Campanella3ce4d282020-06-09 13:46:58 +020067import java.util.Iterator;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000068import 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.opencord.olt.impl.OsgiPropertyConstants.*;
75import static org.slf4j.LoggerFactory.getLogger;
76
77/**
78 * Provisions flow rules on access devices.
79 */
80@Component(immediate = true, property = {
81 ENABLE_DHCP_ON_PROVISIONING + ":Boolean=" + ENABLE_DHCP_ON_PROVISIONING_DEFAULT,
82 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
83 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
84 ENABLE_IGMP_ON_PROVISIONING + ":Boolean=" + ENABLE_IGMP_ON_PROVISIONING_DEFAULT,
85 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
86 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT
87})
88public class OltFlowService implements AccessDeviceFlowService {
89
90 private static final String APP_NAME = "org.opencord.olt";
91 private static final int NONE_TP_ID = -1;
92 private static final int NO_PCP = -1;
93 private static final Integer MAX_PRIORITY = 10000;
94 private static final Integer MIN_PRIORITY = 1000;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000095 private static final String INSTALLED = "installed";
96 private static final String REMOVED = "removed";
97 private static final String INSTALLATION = "installation";
98 private static final String REMOVAL = "removal";
99 private static final String V4 = "V4";
100 private static final String V6 = "V6";
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000101
102 private final Logger log = getLogger(getClass());
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 protected FlowObjectiveService flowObjectiveService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
108 protected CoreService coreService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
111 protected MastershipService mastershipService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
114 protected SadisService sadisService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
117 protected DeviceService deviceService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
120 protected AccessDeviceMeterService oltMeterService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
123 protected ComponentConfigService componentConfigService;
124
125 /**
126 * Create the DHCP Flow rules when a subscriber is provisioned.
127 **/
128 protected boolean enableDhcpOnProvisioning = ENABLE_DHCP_ON_PROVISIONING_DEFAULT;
129
130 /**
131 * Enable flows for DHCP v4.
132 **/
133 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
134
135 /**
136 * Enable flows for DHCP v6.
137 **/
138 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
139
140 /**
141 * Create IGMP Flow rules when a subscriber is provisioned.
142 **/
143 protected boolean enableIgmpOnProvisioning = ENABLE_IGMP_ON_PROVISIONING_DEFAULT;
144
145 /**
146 * Send EAPOL authentication trap flows before subscriber provisioning.
147 **/
148 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
149
150 /**
151 * Default technology profile id that is used for authentication trap flows.
152 **/
153 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
154
155 protected ApplicationId appId;
156 protected BaseInformationService<BandwidthProfileInformation> bpService;
157 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700158 private Set<ConnectPoint> pendingAddEapol = Sets.newConcurrentHashSet();
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200159 private Set<SubscriberFlowInfo> pendingEapolForMeters = Sets.newConcurrentHashSet();;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000160
161 @Activate
162 public void activate(ComponentContext context) {
163 bpService = sadisService.getBandwidthProfileService();
164 subsService = sadisService.getSubscriberInfoService();
165 componentConfigService.registerProperties(getClass());
166 appId = coreService.getAppId(APP_NAME);
Andrea Campanellafee86422020-06-04 16:01:27 +0200167 log.info("started");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000168 }
169
170
171 @Deactivate
172 public void deactivate(ComponentContext context) {
173 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanellafee86422020-06-04 16:01:27 +0200174 log.info("stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000175 }
176
177 @Modified
178 public void modified(ComponentContext context) {
179
180 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
181
Andrea Campanella7c49b792020-05-11 11:36:53 +0200182 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_PROVISIONING);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000183 if (o != null) {
184 enableDhcpOnProvisioning = o;
185 }
186
Andrea Campanella7c49b792020-05-11 11:36:53 +0200187 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000188 if (v4 != null) {
189 enableDhcpV4 = v4;
190 }
191
Andrea Campanella7c49b792020-05-11 11:36:53 +0200192 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000193 if (v6 != null) {
194 enableDhcpV6 = v6;
195 }
196
Andrea Campanella7c49b792020-05-11 11:36:53 +0200197 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_PROVISIONING);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000198 if (p != null) {
199 enableIgmpOnProvisioning = p;
200 }
201
Andrea Campanella7c49b792020-05-11 11:36:53 +0200202 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000203 if (eap != null) {
204 enableEapol = eap;
205 }
206
Andrea Campanella7c49b792020-05-11 11:36:53 +0200207 String tpId = get(properties, DEFAULT_TP_ID);
208 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000209
Andrea Campanellafee86422020-06-04 16:01:27 +0200210 log.info("modified. Values = enableDhcpOnProvisioning: {}, enableDhcpV4: {}, " +
211 "enableDhcpV6:{}, enableIgmpOnProvisioning:{}, " +
212 "enableEapol{}, defaultTechProfileId: {}",
213 enableDhcpOnProvisioning, enableDhcpV4, enableDhcpV6,
214 enableIgmpOnProvisioning, enableEapol, defaultTechProfileId);
215
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000216 }
217
218 @Override
219 public void processDhcpFilteringObjectives(DeviceId devId, PortNumber port,
220 MeterId upstreamMeterId,
221 UniTagInformation tagInformation,
222 boolean install,
223 boolean upstream) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200224 //tagInformation can be none in case of NNI port.
225 //in that case relying on global flag
226 if (!enableDhcpOnProvisioning || (tagInformation != null
227 && !tagInformation.getIsDhcpRequired())) {
228 log.debug("Dhcp provisioning is disabled for port {} on device {}", devId, port);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000229 return;
230 }
231
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000232 int techProfileId = tagInformation != null ? tagInformation.getTechnologyProfileId() : NONE_TP_ID;
233 VlanId cTag = tagInformation != null ? tagInformation.getPonCTag() : VlanId.NONE;
234 VlanId unitagMatch = tagInformation != null ? tagInformation.getUniTagMatch() : VlanId.ANY;
235
236 if (enableDhcpV4) {
237 int udpSrc = (upstream) ? 68 : 67;
238 int udpDst = (upstream) ? 67 : 68;
239
240 EthType ethType = EthType.EtherType.IPV4.ethType();
241 byte protocol = IPv4.PROTOCOL_UDP;
242
Andrea Campanella7c49b792020-05-11 11:36:53 +0200243 addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000244 upstreamMeterId, techProfileId, protocol, cTag, unitagMatch, install);
245 }
246
247 if (enableDhcpV6) {
248 int udpSrc = (upstream) ? 547 : 546;
249 int udpDst = (upstream) ? 546 : 547;
250
251 EthType ethType = EthType.EtherType.IPV6.ethType();
252 byte protocol = IPv6.PROTOCOL_UDP;
253
Andrea Campanella7c49b792020-05-11 11:36:53 +0200254 addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000255 upstreamMeterId, techProfileId, protocol, cTag, unitagMatch, install);
256 }
257 }
258
259 private void addDhcpFilteringObjectives(DeviceId devId, PortNumber port, int udpSrc, int udpDst,
260 EthType ethType, MeterId upstreamMeterId, int techProfileId, byte protocol,
261 VlanId cTag, VlanId unitagMatch, boolean install) {
262
263 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
264 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
265
266 if (upstreamMeterId != null) {
267 treatmentBuilder.meter(upstreamMeterId);
268 }
269
270 if (techProfileId != NONE_TP_ID) {
271 treatmentBuilder.writeMetadata(createTechProfValueForWm(unitagMatch, techProfileId), 0);
272 }
273
274 FilteringObjective.Builder dhcpUpstreamBuilder = (install ? builder.permit() : builder.deny())
275 .withKey(Criteria.matchInPort(port))
276 .addCondition(Criteria.matchEthType(ethType))
277 .addCondition(Criteria.matchIPProtocol(protocol))
278 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
279 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
280 .withMeta(treatmentBuilder
281 .setOutput(PortNumber.CONTROLLER).build())
282 .fromApp(appId)
283 .withPriority(MAX_PRIORITY);
284
285 if (!VlanId.NONE.equals(cTag)) {
286 dhcpUpstreamBuilder.addCondition(Criteria.matchVlanId(cTag));
287 }
288
289 FilteringObjective dhcpUpstream = dhcpUpstreamBuilder.add(new ObjectiveContext() {
290 @Override
291 public void onSuccess(Objective objective) {
292 log.info("DHCP {} filter for device {} on port {} {}.",
293 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
294 devId, port, (install) ? INSTALLED : REMOVED);
295 }
296
297 @Override
298 public void onError(Objective objective, ObjectiveError error) {
299 log.info("DHCP {} filter for device {} on port {} failed {} because {}",
300 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
301 devId, port, (install) ? INSTALLATION : REMOVAL,
302 error);
303 }
304 });
305
306 flowObjectiveService.filter(devId, dhcpUpstream);
307
308 }
309
310 @Override
311 public void processIgmpFilteringObjectives(DeviceId devId, PortNumber port,
312 MeterId upstreamMeterId,
313 UniTagInformation tagInformation,
314 boolean install,
315 boolean upstream) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200316 //tagInformation can be none in case of NNI port.
317 //in that case relying on global flag
318 if (!enableIgmpOnProvisioning || (tagInformation != null
319 && !tagInformation.getIsIgmpRequired())) {
320 log.debug("Igmp provisioning is disabled for port {} on device {}", devId, port);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000321 return;
322 }
323
Matteo Scandolo34556e52020-05-08 12:34:13 -0700324 if (!upstream) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200325 log.debug("Direction on port {} for device {} " +
326 "is not Upstream, ignoring Igmp request", port, devId);
Matteo Scandolo34556e52020-05-08 12:34:13 -0700327 return;
328 }
329
Andrea Campanellafee86422020-06-04 16:01:27 +0200330 log.debug("{} IGMP flows on {}:{}", (install) ?
331 "Installing" : "Removing", devId, port);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000332 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
333 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
334 if (upstream) {
335
336 if (tagInformation.getTechnologyProfileId() != NONE_TP_ID) {
337 treatmentBuilder.writeMetadata(createTechProfValueForWm(null,
338 tagInformation.getTechnologyProfileId()), 0);
339 }
340
341
342 if (upstreamMeterId != null) {
343 treatmentBuilder.meter(upstreamMeterId);
344 }
345
346 if (!VlanId.NONE.equals(tagInformation.getPonCTag())) {
347 builder.addCondition(Criteria.matchVlanId(tagInformation.getPonCTag()));
348 }
349
350 if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
351 builder.addCondition(Criteria.matchVlanPcp((byte) tagInformation.getUsPonCTagPriority()));
352 }
353 }
354
355 builder = install ? builder.permit() : builder.deny();
356
357 FilteringObjective igmp = builder
358 .withKey(Criteria.matchInPort(port))
359 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
360 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
361 .withMeta(treatmentBuilder
362 .setOutput(PortNumber.CONTROLLER).build())
363 .fromApp(appId)
364 .withPriority(MAX_PRIORITY)
365 .add(new ObjectiveContext() {
366 @Override
367 public void onSuccess(Objective objective) {
368 log.info("Igmp filter for {} on {} {}.",
369 devId, port, (install) ? INSTALLED : REMOVED);
370 }
371
372 @Override
373 public void onError(Objective objective, ObjectiveError error) {
374 log.info("Igmp filter for {} on {} failed {} because {}.",
375 devId, port, (install) ? INSTALLATION : REMOVAL,
376 error);
377 }
378 });
379
380 flowObjectiveService.filter(devId, igmp);
381 }
382
383 @Override
384 public void processEapolFilteringObjectives(DeviceId devId, PortNumber portNumber, String bpId,
385 CompletableFuture<ObjectiveError> filterFuture,
386 VlanId vlanId, boolean install) {
387
388 if (!enableEapol) {
389 log.debug("Eapol filtering is disabled. Completing filteringFuture immediately for the device {}", devId);
390 if (filterFuture != null) {
391 filterFuture.complete(null);
392 }
393 return;
394 }
395
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000396 BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bpId);
397 if (bpInfo == null) {
398 log.warn("Bandwidth profile {} is not found. Authentication flow"
399 + " will not be installed", bpId);
400 if (filterFuture != null) {
401 filterFuture.complete(ObjectiveError.BADPARAMS);
402 }
403 return;
404 }
405
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700406 ConnectPoint cp = new ConnectPoint(devId, portNumber);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000407 if (install) {
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700408 boolean added = pendingAddEapol.add(cp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000409 if (!added) {
410 if (filterFuture != null) {
411 log.warn("The eapol flow is processing for the port {}. Ignoring this request", portNumber);
412 filterFuture.complete(null);
413 }
414 return;
415 }
Matteo Scandolo3a037a32020-04-01 12:17:50 -0700416 log.info("connectPoint added to pendingAddEapol map {}", cp.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000417 }
418
419 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
420 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
421 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
422
423 // check if meter exists and create it only for an install
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200424 final MeterId meterId = oltMeterService.getMeterIdFromBpMapping(devId, bpInfo.id());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000425 if (meterId == null) {
426 if (install) {
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200427 log.debug("Need to install meter for EAPOL with bwp {}", bpInfo.id());
428 SubscriberFlowInfo fi = new SubscriberFlowInfo(devId, null, cp.port(),
429 new UniTagInformation.Builder()
430 .setPonCTag(vlanId).build(),
431 null, null,
432 null, bpInfo.id());
433 pendingEapolForMeters.add(fi);
434
Andrea Campanella600d2e22020-06-22 11:00:31 +0200435 if (oltMeterService.isMeterPending(devId, bpInfo)) {
436 log.debug("Meter is already pending for EAPOL on {} with bp {}",
437 devId, bpInfo);
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200438 return;
439 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200440 oltMeterService.addToPendingMeters(devId, bpInfo);
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200441 MeterId innerMeterId = oltMeterService.createMeter(devId, bpInfo,
442 meterFuture);
443 fi.setUpMeterId(innerMeterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000444 } else {
445 // this case should not happen as the request to remove an eapol
446 // flow should mean that the flow points to a meter that exists.
447 // Nevertheless we can still delete the flow as we only need the
448 // correct 'match' to do so.
449 log.warn("Unknown meter id for bp {}, still proceeding with "
450 + "delete of eapol flow for {}/{}", bpInfo.id(), devId, portNumber);
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200451 SubscriberFlowInfo fi = new SubscriberFlowInfo(devId, null, cp.port(),
452 new UniTagInformation.Builder()
453 .setPonCTag(vlanId).build(),
454 null, meterId,
455 null, bpInfo.id());
456 handleEapol(filterFuture, install, cp, builder, treatmentBuilder, fi, meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000457 }
458 } else {
459 log.debug("Meter {} was previously created for bp {}", meterId, bpInfo.id());
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200460 SubscriberFlowInfo fi = new SubscriberFlowInfo(devId, null, cp.port(),
461 new UniTagInformation.Builder()
462 .setPonCTag(vlanId).build(),
463 null, meterId,
464 null, bpInfo.id());
465 handleEapol(filterFuture, install, cp, builder, treatmentBuilder, fi, meterId);
466 //No need for the future, meter is present.
467 return;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000468 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000469 meterFuture.thenAcceptAsync(result -> {
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200470 //for each pending eapol flow we check if the meter is there.
471 Iterator<SubscriberFlowInfo> eapIterator = pendingEapolForMeters.iterator();
472 while (eapIterator.hasNext()) {
473 SubscriberFlowInfo fi = eapIterator.next();
474 if (result == null) {
475 MeterId mId = oltMeterService
Andrea Campanella600d2e22020-06-22 11:00:31 +0200476 .getMeterIdFromBpMapping(devId, fi.getUpBpInfo());
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200477 if (mId != null) {
478 handleEapol(filterFuture, install, cp, builder, treatmentBuilder, fi, mId);
479 eapIterator.remove();
480 }
481 } else {
482 log.warn("Meter installation error while sending eapol trap flow. " +
483 "Result {} and MeterId {}", result, meterId);
484 eapIterator.remove();
485 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200486 oltMeterService.removeFromPendingMeters(devId, bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000487 }
488 });
489 }
490
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200491 private void handleEapol(CompletableFuture<ObjectiveError> filterFuture,
492 boolean install, ConnectPoint cp,
493 DefaultFilteringObjective.Builder builder,
494 TrafficTreatment.Builder treatmentBuilder,
495 SubscriberFlowInfo fi, MeterId mId) {
496 log.info("Meter {} for {} on {}/{} exists. {} EAPOL trap flow",
497 mId, fi.getUpBpInfo(), fi.getDevId(), fi.getUniPort(),
498 (install) ? "Installing" : "Removing");
499 int techProfileId = getDefaultTechProfileId(fi.getDevId(), fi.getUniPort());
500 // can happen in case of removal
501 if (mId != null) {
502 treatmentBuilder.meter(mId);
503 }
504 //Authentication trap flow uses only tech profile id as write metadata value
505 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
506 .withKey(Criteria.matchInPort(fi.getUniPort()))
507 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
508 .addCondition(Criteria.matchVlanId(fi.getTagInfo().getPonCTag()))
509 .withMeta(treatmentBuilder
510 .writeMetadata(createTechProfValueForWm(
511 fi.getTagInfo().getPonCTag(),
512 techProfileId), 0)
513 .setOutput(PortNumber.CONTROLLER).build())
514 .fromApp(appId)
515 .withPriority(MAX_PRIORITY)
516 .add(new ObjectiveContext() {
517 @Override
518 public void onSuccess(Objective objective) {
Andrea Campanella600d2e22020-06-22 11:00:31 +0200519 log.info("Eapol filter {} for {} on {} {} with meter {}.",
520 objective.id(), fi.getDevId(), fi.getUniPort(),
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200521 (install) ? INSTALLED : REMOVED, mId);
522 if (filterFuture != null) {
523 filterFuture.complete(null);
524 }
525 pendingAddEapol.remove(cp);
526 }
527
528 @Override
529 public void onError(Objective objective, ObjectiveError error) {
Andrea Campanella600d2e22020-06-22 11:00:31 +0200530 log.error("Eapol filter {} for {} on {} with meter {} " +
531 "failed {} because {}", objective.id(),
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200532 fi.getDevId(), fi.getUniPort(), mId,
533 (install) ? INSTALLATION : REMOVAL,
534 error);
535 if (filterFuture != null) {
536 filterFuture.complete(error);
537 }
538 pendingAddEapol.remove(cp);
539 }
540 });
541 flowObjectiveService.filter(fi.getDevId(), eapol);
542 }
543
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000544 /**
545 * Installs trap filtering objectives for particular traffic types on an
546 * NNI port.
547 *
548 * @param devId device ID
549 * @param port port number
550 * @param install true to install, false to remove
551 */
552 @Override
553 public void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
554 log.info("Sending flows for NNI port {} of the device {}", port, devId);
555 processLldpFilteringObjective(devId, port, install);
556 processDhcpFilteringObjectives(devId, port, null, null, install, false);
557 processIgmpFilteringObjectives(devId, port, null, null, install, false);
558 }
559
560
561 @Override
562 public void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000563 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
564
565 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
566 .withKey(Criteria.matchInPort(port))
567 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
568 .withMeta(DefaultTrafficTreatment.builder()
569 .setOutput(PortNumber.CONTROLLER).build())
570 .fromApp(appId)
571 .withPriority(MAX_PRIORITY)
572 .add(new ObjectiveContext() {
573 @Override
574 public void onSuccess(Objective objective) {
575 log.info("LLDP filter for device {} on port {} {}.",
576 devId, port, (install) ? INSTALLED : REMOVED);
577 }
578
579 @Override
580 public void onError(Objective objective, ObjectiveError error) {
581 log.info("LLDP filter for device {} on port {} failed {} because {}",
582 devId, port, (install) ? INSTALLATION : REMOVAL,
583 error);
584 }
585 });
586
587 flowObjectiveService.filter(devId, lldp);
588 }
589
590 @Override
591 public ForwardingObjective.Builder createTransparentBuilder(PortNumber uplinkPort,
592 PortNumber subscriberPort,
593 MeterId meterId,
594 UniTagInformation tagInfo,
595 boolean upstream) {
596
597 TrafficSelector selector = DefaultTrafficSelector.builder()
598 .matchVlanId(tagInfo.getPonSTag())
599 .matchInPort(upstream ? subscriberPort : uplinkPort)
600 .matchInnerVlanId(tagInfo.getPonCTag())
601 .build();
602
603 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
604 if (meterId != null) {
605 tBuilder.meter(meterId);
606 }
607
608 TrafficTreatment treatment = tBuilder
609 .setOutput(upstream ? uplinkPort : subscriberPort)
610 .writeMetadata(createMetadata(upstream ? tagInfo.getPonSTag() : tagInfo.getPonCTag(),
611 tagInfo.getTechnologyProfileId(), upstream ? uplinkPort : subscriberPort), 0)
612 .build();
613
614 return createForwardingObjectiveBuilder(selector, treatment, MIN_PRIORITY);
615 }
616
617 @Override
618 public ForwardingObjective.Builder createUpBuilder(PortNumber uplinkPort,
619 PortNumber subscriberPort,
620 MeterId upstreamMeterId,
621 UniTagInformation uniTagInformation) {
622
623 TrafficSelector selector = DefaultTrafficSelector.builder()
624 .matchInPort(subscriberPort)
625 .matchVlanId(uniTagInformation.getUniTagMatch())
626 .build();
627
Andrea Campanella327c5722020-01-30 11:34:13 +0100628 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
629 //if the subscriberVlan (cTag) is different than ANY it needs to set.
630 if (uniTagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
631 treatmentBuilder.pushVlan()
632 .setVlanId(uniTagInformation.getPonCTag());
633 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000634
635 if (uniTagInformation.getUsPonCTagPriority() != NO_PCP) {
636 treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonCTagPriority());
637 }
638
639 treatmentBuilder.pushVlan()
640 .setVlanId(uniTagInformation.getPonSTag());
641
642 if (uniTagInformation.getUsPonSTagPriority() != NO_PCP) {
643 treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonSTagPriority());
644 }
645
646 treatmentBuilder.setOutput(uplinkPort)
647 .writeMetadata(createMetadata(uniTagInformation.getPonCTag(),
648 uniTagInformation.getTechnologyProfileId(), uplinkPort), 0L);
649
650 if (upstreamMeterId != null) {
651 treatmentBuilder.meter(upstreamMeterId);
652 }
653
654 return createForwardingObjectiveBuilder(selector, treatmentBuilder.build(), MIN_PRIORITY);
655 }
656
657 @Override
658 public ForwardingObjective.Builder createDownBuilder(PortNumber uplinkPort,
659 PortNumber subscriberPort,
660 MeterId downstreamMeterId,
661 UniTagInformation tagInformation) {
Andrea Campanella327c5722020-01-30 11:34:13 +0100662
663 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000664 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
665 .matchVlanId(tagInformation.getPonSTag())
666 .matchInPort(uplinkPort)
Andrea Campanella090e4a02020-02-05 13:53:55 +0100667 .matchInnerVlanId(tagInformation.getPonCTag());
668
669
670 if (tagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
671 selectorBuilder.matchMetadata(tagInformation.getPonCTag().toShort());
672 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000673
674 if (tagInformation.getDsPonSTagPriority() != NO_PCP) {
675 selectorBuilder.matchVlanPcp((byte) tagInformation.getDsPonSTagPriority());
676 }
677
678 if (tagInformation.getConfiguredMacAddress() != null &&
Daniele Moro7cbf4312020-03-06 17:24:12 -0800679 !tagInformation.getConfiguredMacAddress().equals("") &&
680 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()))) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000681 selectorBuilder.matchEthDst(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
682 }
683
684 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
685 .popVlan()
Andrea Campanella327c5722020-01-30 11:34:13 +0100686 .setOutput(subscriberPort);
687
Andrea Campanella327c5722020-01-30 11:34:13 +0100688 treatmentBuilder.writeMetadata(createMetadata(tagInformation.getPonCTag(),
689 tagInformation.getTechnologyProfileId(),
690 subscriberPort), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000691
692 // to remark inner vlan header
693 if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
694 treatmentBuilder.setVlanPcp((byte) tagInformation.getUsPonCTagPriority());
695 }
696
Andrea Campanella9a779292020-02-03 19:19:09 +0100697 if (!VlanId.NONE.equals(tagInformation.getUniTagMatch()) &&
698 tagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000699 treatmentBuilder.setVlanId(tagInformation.getUniTagMatch());
700 }
701
702 if (downstreamMeterId != null) {
703 treatmentBuilder.meter(downstreamMeterId);
704 }
705
706 return createForwardingObjectiveBuilder(selectorBuilder.build(), treatmentBuilder.build(), MIN_PRIORITY);
707 }
708
Andrea Campanella600d2e22020-06-22 11:00:31 +0200709 @Override
710 public void clearDeviceState(DeviceId deviceId) {
711 pendingEapolForMeters.removeIf(fi -> fi.getDevId().equals(deviceId));
712 pendingAddEapol.removeIf(connectPoint -> connectPoint.deviceId().equals(deviceId));
713
714 }
715
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000716 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
717 TrafficTreatment treatment,
718 Integer priority) {
719 return DefaultForwardingObjective.builder()
720 .withFlag(ForwardingObjective.Flag.VERSATILE)
721 .withPriority(priority)
722 .makePermanent()
723 .withSelector(selector)
724 .fromApp(appId)
725 .withTreatment(treatment);
726 }
727
728 /**
729 * Returns the write metadata value including tech profile reference and innerVlan.
730 * For param cVlan, null can be sent
731 *
732 * @param cVlan c (customer) tag of one subscriber
733 * @param techProfileId tech profile id of one subscriber
734 * @return the write metadata value including tech profile reference and innerVlan
735 */
736 private Long createTechProfValueForWm(VlanId cVlan, int techProfileId) {
737 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
738 return (long) techProfileId << 32;
739 }
740 return ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
741 }
742
743 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
744 if (bandwidthProfile == null) {
745 return null;
746 }
747 return bpService.get(bandwidthProfile);
748 }
749
750 /**
751 * It will be used to support AT&T use case (for EAPOL flows).
752 * If multiple services are found in uniServiceList, returns default tech profile id
753 * If one service is found, returns the found one
754 *
755 * @param devId
756 * @param portNumber
757 * @return the default technology profile id
758 */
759 private int getDefaultTechProfileId(DeviceId devId, PortNumber portNumber) {
760 Port port = deviceService.getPort(devId, portNumber);
761 if (port != null) {
762 SubscriberAndDeviceInformation info = subsService.get(port.annotations().value(AnnotationKeys.PORT_NAME));
763 if (info != null && info.uniTagList().size() == 1) {
764 return info.uniTagList().get(0).getTechnologyProfileId();
765 }
766 }
767 return defaultTechProfileId;
768 }
769
770 /**
771 * Write metadata instruction value (metadata) is 8 bytes.
772 * <p>
773 * MS 2 bytes: C Tag
774 * Next 2 bytes: Technology Profile Id
775 * Next 4 bytes: Port number (uni or nni)
776 */
777 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
778 if (techProfileId == NONE_TP_ID) {
Andrea Campanella7c49b792020-05-11 11:36:53 +0200779 techProfileId = DEFAULT_TP_ID_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000780 }
781
782 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
783 }
784
785
786}