blob: ecaaa3d8f81e2258e9fed4dffb42aaf38d1733c5 [file] [log] [blame]
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001/*
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07002 * Copyright 2021-present Open Networking Foundation
Andrea Campanellacbbb7952019-11-25 06:38:41 +00003 *
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 */
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070016
Andrea Campanellacbbb7952019-11-25 06:38:41 +000017package org.opencord.olt.impl;
18
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070019import com.google.common.collect.ImmutableMap;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000020import org.onlab.packet.EthType;
21import org.onlab.packet.IPv4;
22import org.onlab.packet.IPv6;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.TpPort;
25import org.onlab.packet.VlanId;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000026import org.onlab.util.KryoNamespace;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000027import org.onlab.util.Tools;
28import org.onosproject.cfg.ComponentConfigService;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
yasin saplib4b8ee12021-06-13 18:25:20 +000031import org.onosproject.net.Annotations;
Matteo Scandolo3a037a32020-04-01 12:17:50 -070032import org.onosproject.net.ConnectPoint;
yasin saplib4b8ee12021-06-13 18:25:20 +000033import org.onosproject.net.DefaultAnnotations;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070034import org.onosproject.net.Device;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000035import org.onosproject.net.DeviceId;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070036import org.onosproject.net.Host;
37import org.onosproject.net.Port;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000038import org.onosproject.net.PortNumber;
39import org.onosproject.net.device.DeviceService;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070042import org.onosproject.net.flow.FlowRule;
43import org.onosproject.net.flow.FlowRuleEvent;
44import org.onosproject.net.flow.FlowRuleListener;
45import org.onosproject.net.flow.FlowRuleService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000046import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.flow.criteria.Criteria;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070049import org.onosproject.net.flow.criteria.Criterion;
50import org.onosproject.net.flow.criteria.EthTypeCriterion;
51import org.onosproject.net.flow.criteria.IPProtocolCriterion;
52import org.onosproject.net.flow.criteria.PortCriterion;
53import org.onosproject.net.flow.criteria.UdpPortCriterion;
54import org.onosproject.net.flow.criteria.VlanIdCriterion;
55import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000056import org.onosproject.net.flowobjective.DefaultFilteringObjective;
57import org.onosproject.net.flowobjective.DefaultForwardingObjective;
58import org.onosproject.net.flowobjective.FilteringObjective;
59import org.onosproject.net.flowobjective.FlowObjectiveService;
60import org.onosproject.net.flowobjective.ForwardingObjective;
61import org.onosproject.net.flowobjective.Objective;
62import org.onosproject.net.flowobjective.ObjectiveContext;
63import org.onosproject.net.flowobjective.ObjectiveError;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070064import org.onosproject.net.host.HostService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000065import org.onosproject.net.meter.MeterId;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000066import org.onosproject.store.serializers.KryoNamespaces;
67import org.onosproject.store.service.Serializer;
68import org.onosproject.store.service.StorageService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000069import org.opencord.sadis.BandwidthProfileInformation;
70import org.opencord.sadis.BaseInformationService;
71import org.opencord.sadis.SadisService;
72import org.opencord.sadis.SubscriberAndDeviceInformation;
73import org.opencord.sadis.UniTagInformation;
74import org.osgi.service.component.ComponentContext;
75import org.osgi.service.component.annotations.Activate;
76import org.osgi.service.component.annotations.Component;
77import org.osgi.service.component.annotations.Deactivate;
78import org.osgi.service.component.annotations.Modified;
79import org.osgi.service.component.annotations.Reference;
80import org.osgi.service.component.annotations.ReferenceCardinality;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000081import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000082import org.slf4j.Logger;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070083import org.slf4j.LoggerFactory;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000084
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080085import java.util.ArrayList;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010086import java.util.Dictionary;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070087import java.util.HashMap;
Andrea Campanella61650a12022-01-24 18:09:44 -080088import java.util.HashSet;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070089import java.util.Iterator;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080090import java.util.List;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000091import java.util.Map;
Andrea Campanellabfb47af2021-06-03 11:09:45 +020092import java.util.Optional;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010093import java.util.Properties;
Andrea Campanella61650a12022-01-24 18:09:44 -080094import java.util.Set;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070095import java.util.concurrent.atomic.AtomicBoolean;
96import java.util.concurrent.locks.Lock;
97import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010098
99import static com.google.common.base.Strings.isNullOrEmpty;
100import static org.onlab.util.Tools.get;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700101import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
102import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
103import static org.opencord.olt.impl.OltUtils.flowOpToString;
104import static org.opencord.olt.impl.OltUtils.getPortName;
105import static org.opencord.olt.impl.OltUtils.portWithName;
106import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
107import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
108import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
109import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
110import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
111import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
112import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
113import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
114import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
115import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
116import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
117import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
118import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
119import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
120import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
121import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
122import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
123import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
124import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
125import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000126
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000127@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700128 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000129 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
130 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700131 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000132 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300133 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700134 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
135 // FIXME remove this option as potentially dangerous in production
136 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000137})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700138public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000139
140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
141 protected CoreService coreService;
142
143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700144 protected ComponentConfigService cfgService;
145
146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
147 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000148
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000149 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
150 bind = "bindSadisService",
151 unbind = "unbindSadisService",
152 policy = ReferencePolicy.DYNAMIC)
153 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000154
155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700156 protected OltMeterServiceInterface oltMeterService;
157
158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
159 protected OltDeviceServiceInterface oltDeviceService;
160
161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
162 protected FlowRuleService flowRuleService;
163
164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
165 protected HostService hostService;
166
167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000168 protected DeviceService deviceService;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000171 protected StorageService storageService;
172
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700173 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
174 protected BaseInformationService<BandwidthProfileInformation> bpService;
175
176 private static final String APP_NAME = "org.opencord.olt";
177 protected ApplicationId appId;
178 private static final Integer MAX_PRIORITY = 10000;
179 private static final Integer MIN_PRIORITY = 1000;
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800180 protected static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700181 private static final int NONE_TP_ID = -1;
182 private static final String V4 = "V4";
183 private static final String V6 = "V6";
184 private final Logger log = LoggerFactory.getLogger(getClass());
185
186 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
187 .setServiceName("defaultEapol").build();
188 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
189 .setServiceName("nni")
190 .setTechnologyProfileId(NONE_TP_ID)
191 .setPonCTag(VlanId.NONE)
192 .setUniTagMatch(VlanId.ANY)
193 .setUsPonCTagPriority(-1)
194 .build();
195
196 /**
197 * Connect Point status map.
198 * Used to keep track of which cp has flows that needs to be removed when the status changes.
199 */
200 protected Map<ServiceKey, OltPortStatus> cpStatus;
201 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
202 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
203 private final Lock cpStatusReadLock = cpStatusLock.readLock();
204
205 /**
206 * This map contains the subscriber that have been provisioned by the operator.
207 * They may or may not have flows, depending on the port status.
208 * The map is used to define whether flows need to be provisioned when a port comes up.
209 */
210 protected Map<ServiceKey, Boolean> provisionedSubscribers;
211 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
212 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
213 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
214
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000215 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700216 * Create DHCP trap flow on NNI port(s).
217 */
218 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000219
220 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700221 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000222 **/
223 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
224
225 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700226 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000227 **/
228 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
229
230 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700231 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000232 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700233 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000234
235 /**
236 * Send EAPOL authentication trap flows before subscriber provisioning.
237 **/
238 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
239
240 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300241 * Send PPPoED authentication trap flows before subscriber provisioning.
242 **/
243 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
244
245 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000246 * Default technology profile id that is used for authentication trap flows.
247 **/
248 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
249
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700250 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
251
252 public enum FlowOperation {
253 ADD,
254 REMOVE;
255
256
257 @Override
258 public String toString() {
259 return super.toString().toLowerCase();
260 }
261 }
262
263 public enum FlowDirection {
264 UPSTREAM,
265 DOWNSTREAM,
266 }
267
268 public enum OltFlowsStatus {
269 NONE,
270 PENDING_ADD,
271 ADDED,
272 PENDING_REMOVE,
273 REMOVED,
274 ERROR
275 }
276
277 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000278
279 @Activate
280 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700281 cfgService.registerProperties(getClass());
282 appId = coreService.registerApplication(APP_NAME);
283 internalFlowListener = new InternalFlowListener();
284
285 modified(context);
286
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000287 KryoNamespace serializer = KryoNamespace.newBuilder()
288 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700289 .register(OltFlowsStatus.class)
290 .register(FlowDirection.class)
291 .register(OltPortStatus.class)
292 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000293 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700294 .register(new ServiceKeySerializer(), ServiceKey.class)
295 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000296 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000297
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700298 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
299 .withName("volt-cp-status")
300 .withApplicationId(appId)
301 .withSerializer(Serializer.using(serializer))
302 .build().asJavaMap();
303
304 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
305 .withName("volt-provisioned-subscriber")
306 .withApplicationId(appId)
307 .withSerializer(Serializer.using(serializer))
308 .build().asJavaMap();
309
310 flowRuleService.addListener(internalFlowListener);
311
312 log.info("Started");
313 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000314
315 @Deactivate
316 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700317 cfgService.unregisterProperties(getClass(), false);
318 flowRuleService.removeListener(internalFlowListener);
319 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000320 }
321
322 @Modified
323 public void modified(ComponentContext context) {
324
325 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
326
Saurav Dasf62cea82020-08-26 17:43:04 -0700327 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000328 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700329 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000330 }
331
Andrea Campanella7c49b792020-05-11 11:36:53 +0200332 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000333 if (v4 != null) {
334 enableDhcpV4 = v4;
335 }
336
Andrea Campanella7c49b792020-05-11 11:36:53 +0200337 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000338 if (v6 != null) {
339 enableDhcpV6 = v6;
340 }
341
Saurav Dasf62cea82020-08-26 17:43:04 -0700342 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000343 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700344 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000345 }
346
Andrea Campanella7c49b792020-05-11 11:36:53 +0200347 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000348 if (eap != null) {
349 enableEapol = eap;
350 }
351
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300352 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
353 if (pppoe != null) {
354 enablePppoe = pppoe;
355 }
356
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700357 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
358 if (wait != null) {
359 waitForRemoval = wait;
360 }
361
Andrea Campanella7c49b792020-05-11 11:36:53 +0200362 String tpId = get(properties, DEFAULT_TP_ID);
363 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000364
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700365 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " +
366 "enableDhcpV6:{}, enableIgmpOnNni:{}, " +
367 "enableEapol:{}, enablePppoe:{}, defaultTechProfileId:{}," +
368 "waitForRemoval:{}",
369 enableDhcpOnNni, enableDhcpV4, enableDhcpV6,
370 enableIgmpOnNni, enableEapol, enablePppoe,
371 defaultTechProfileId, waitForRemoval);
Andrea Campanellafee86422020-06-04 16:01:27 +0200372
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000373 }
374
375 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700376 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
377 try {
378 cpStatusReadLock.lock();
379 return ImmutableMap.copyOf(cpStatus);
380 } finally {
381 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000382 }
383 }
384
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700385 @Override
386 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
387 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800388 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700389 Map<ServiceKey, UniTagInformation> subscribers =
390 new HashMap<>();
391 try {
392 cpStatusReadLock.lock();
393
394 cpStatus.forEach((sk, status) -> {
395 if (
396 // not NNI Port
397 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
398 sk.getPort().connectPoint().port()) &&
399 // not EAPOL flow
Andrea Campanella982fd332022-01-19 09:14:12 +0100400 !sk.getService().equals(defaultEapolUniTag) &&
401 !status.subscriberFlowsStatus.equals(OltFlowsStatus.PENDING_REMOVE)
402 && !status.subscriberFlowsStatus.equals(OltFlowsStatus.REMOVED)
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800403
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700404 ) {
405 subscribers.put(sk, sk.getService());
406 }
407 });
408
409 return ImmutableMap.copyOf(subscribers);
410 } finally {
411 cpStatusReadLock.unlock();
412 }
413 }
414
415 @Override
416 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
417 try {
418 provisionedSubscribersReadLock.lock();
419 return ImmutableMap.copyOf(provisionedSubscribers);
420 } finally {
421 provisionedSubscribersReadLock.unlock();
422 }
423 }
424
425 @Override
426 public void handleNniFlows(Device device, Port port, FlowOperation action) {
427
428 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800429 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700430 processLldpFilteringObjective(device.id(), port, action);
431
432 if (enableDhcpOnNni) {
433 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800434 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
435 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700436 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
437 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
438 null, null, nniUniTag);
439 }
440 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800441 log.debug("{} DHCPv6 trap flow on NNI {} for device {}", flowOpToString(action),
442 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700443 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
444 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
445 null, null, nniUniTag);
446 }
447 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800448 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700449 }
450
451 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800452 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700453 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
454 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
455 }
456
457 if (enablePppoe) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800458 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700459 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
460 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
461 }
462 }
463
464 @Override
465 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
466 String oltBandwidthProfileId) {
467
468 // we only need to something if EAPOL is enabled
469 if (!enableEapol) {
470 return true;
471 }
472
473 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
474 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
475 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
476 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
477 } else {
478 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
479 return false;
480 }
481
482 }
483
484 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
485
486 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
487 if (log.isTraceEnabled()) {
488 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
489 }
490 return false;
491 }
492 if (hasDefaultEapol(sub.port)) {
493 return true;
494 }
495 return handleEapolFlow(sub, bandwidthProfileId,
496 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
497
498 }
499
500 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
501 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
502 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
503 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
504 }
505
506 @Override
507 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
508 String multicastServiceName) {
509 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
510 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
511 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
512 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
513 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
514 } else {
515 log.error("don't know how to handle {}", sub);
516 return false;
517 }
518 }
519
520 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
521 String multicastServiceName) {
522 if (log.isTraceEnabled()) {
523 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
524 }
525 if (enableEapol) {
526 if (hasDefaultEapol(sub.port)) {
527 // remove EAPOL flow and throw exception so that we'll retry later
528 if (!isDefaultEapolPendingRemoval(sub.port)) {
529 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
530 }
531
532 if (waitForRemoval) {
533 // NOTE wait for removal is a flag only needed to make sure VOLTHA
534 // does not explode with the flows remove/add in the same batch
535 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
536 return false;
537 } else {
538 log.warn("continuing provisioning on {}", portWithName(sub.port));
539 }
540 }
541
542 }
543
544 // NOTE createMeters will return if the meters are not installed
545 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800546 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700547 return false;
548 }
549
550 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
551 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
552 sub.subscriberAndDeviceInformation);
553
554 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
555 && !isMacAddressAvailable(sub.device.id(), sub.port,
556 sub.subscriberAndDeviceInformation)) {
557 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
558 return false;
559 }
560
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800561 // NOTE that the EAPOL flows handling is based on the data-plane flows status
562 // always process them before
563 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
564
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700565 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
566 sub.subscriberAndDeviceInformation, multicastServiceName);
567
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700568 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
569
570 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
571 return true;
572 }
573
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800574 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700575 String multicastServiceName) {
576
577 if (log.isTraceEnabled()) {
578 log.trace("Removal of subscriber on {} started",
579 portWithName(sub.port));
580 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800581 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700582
583 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
584
585 if (enableEapol) {
586 // remove the tagged eapol
587 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800588 }
589 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700590
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800591 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
592
593 if (enableEapol) {
594
595 // if any of the services still has flows, return false
596 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
597 while (iter.hasNext()) {
598 UniTagInformation entry = iter.next();
599 if (areSubscriberFlowsPendingRemoval(sub.port, entry)) {
600 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
601 portWithName(sub.port), entry.getServiceName());
602 return false;
603 }
604 }
605
606 // once the flows are removed add the default one back
607 // (only if the port is ENABLED and still present on the device)
Matteo Scandolo49c42052021-11-23 13:12:29 -0800608 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
609
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700610 // NOTE we remove the subscriber when the port goes down
611 // but in that case we don't need to add default eapol
612 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
613 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
614 }
615 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700616 // FIXME check the return status of the flow and return accordingly
617 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
618 return true;
619 }
620
621 @Override
622 public boolean hasDefaultEapol(Port port) {
623 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
624 // NOTE we consider ERROR as a present EAPOL flow as ONOS
625 // will keep trying to add it
626 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
627 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
628 status.defaultEapolStatus == OltFlowsStatus.ERROR);
629 }
630
631 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
632 try {
633 cpStatusReadLock.lock();
634 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
635 OltPortStatus status = cpStatus.get(sk);
636 return status;
637 } finally {
638 cpStatusReadLock.unlock();
639 }
640 }
641
642 public boolean isDefaultEapolPendingRemoval(Port port) {
643 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
644 if (log.isTraceEnabled()) {
645 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
646 status, portWithName(port), defaultEapolUniTag);
647 }
648 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
649 }
650
651 @Override
652 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
653 OltPortStatus status = getOltPortStatus(port, uti);
654 if (log.isTraceEnabled()) {
655 log.trace("Status during DHCP flow check {} for port {} and service {}",
656 status, portWithName(port), uti.getServiceName());
657 }
658 return status != null &&
659 (status.dhcpStatus == OltFlowsStatus.ADDED ||
660 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
661 }
662
663 @Override
664 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
665
666 OltPortStatus status = getOltPortStatus(port, uti);
667 if (log.isTraceEnabled()) {
668 log.trace("Status during subscriber flow check {} for port {} and service {}",
669 status, portWithName(port), uti.getServiceName());
670 }
671 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
672 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
673 }
674
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800675 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti) {
676 OltPortStatus status = getOltPortStatus(port, uti);
677 if (log.isTraceEnabled()) {
678 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
679 status, portWithName(port), uti);
680 }
681 return status != null && status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE;
682 }
683
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700684 @Override
685 public void purgeDeviceFlows(DeviceId deviceId) {
686 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800687 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700688
689 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800690 if (log.isTraceEnabled()) {
691 log.trace("Clearing cp status from device {}", deviceId);
692 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700693 try {
694 cpStatusWriteLock.lock();
695 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
696 while (iter.hasNext()) {
697 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
698 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
699 cpStatus.remove(entry.getKey());
700 }
701 }
702 } finally {
703 cpStatusWriteLock.unlock();
704 }
705
706 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800707 if (log.isTraceEnabled()) {
708 log.trace("Clearing provisioned subscribers from device {}", deviceId);
709 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700710 try {
711 provisionedSubscribersWriteLock.lock();
712 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
713 while (iter.hasNext()) {
714 Map.Entry<ServiceKey, Boolean> entry = iter.next();
715 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
716 provisionedSubscribers.remove(entry.getKey());
717 }
718 }
719 } finally {
720 provisionedSubscribersWriteLock.unlock();
721 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800722 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700723 }
724
725 @Override
726 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800727 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700728 try {
729 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800730 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700731 } finally {
732 provisionedSubscribersReadLock.unlock();
733 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800734
735 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
736 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
737 return true;
738 }
739 }
740 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700741 }
742
743 @Override
744 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
745 try {
746 provisionedSubscribersReadLock.lock();
747 Boolean provisioned = provisionedSubscribers.get(sk);
748 if (provisioned == null || !provisioned) {
749 return false;
750 }
751 } finally {
752 provisionedSubscribersReadLock.unlock();
753 }
754 return true;
755 }
756
757 @Override
758 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
759 try {
760 provisionedSubscribersWriteLock.lock();
761 provisionedSubscribers.put(sk, status);
762 } finally {
763 provisionedSubscribersWriteLock.unlock();
764 }
765 }
766
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800767 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700768 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
769
770 // create a subscriberKey for the EAPOL flow
771 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
772
773 // NOTE we only need to keep track of the default EAPOL flow in the
774 // connectpoint status map
775 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
776 OltFlowsStatus status = action == FlowOperation.ADD ?
777 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
778 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE);
779
780 }
781
782 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
783 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
784
785 int techProfileId = getDefaultTechProfileId(sub.port);
786 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
787
788 // in the delete case the meter should still be there as we remove
789 // the meters only if no flows are pointing to them
790 if (meterId == null) {
791 log.debug("MeterId is null for BandwidthProfile {} on device {}",
792 bandwidthProfile, sub.device.id());
793 return false;
794 }
795
796 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
797 if (oltMeterId == null) {
798 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
799 oltBandwidthProfile, sub.device.id());
800 return false;
801 }
802
803 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
804 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
805
806 FilteringObjective.Builder eapolAction;
807
808 if (action == FlowOperation.ADD) {
809 eapolAction = filterBuilder.permit();
810 } else if (action == FlowOperation.REMOVE) {
811 eapolAction = filterBuilder.deny();
812 } else {
813 log.error("Operation {} not supported", action);
814 return false;
815 }
816
817 FilteringObjective.Builder baseEapol = eapolAction
818 .withKey(Criteria.matchInPort(sub.port.number()))
819 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
820
821 // NOTE we only need to add the treatment to install the flow,
822 // we can remove it based in the match
823 FilteringObjective.Builder eapol;
824
825 TrafficTreatment treatment = treatmentBuilder
826 .meter(meterId)
827 .writeMetadata(createTechProfValueForWriteMetadata(
828 vlanId,
829 techProfileId, oltMeterId), 0)
830 .setOutput(PortNumber.CONTROLLER)
831 .pushVlan()
832 .setVlanId(vlanId)
833 .build();
834 eapol = baseEapol
835 .withMeta(treatment);
836
837 FilteringObjective eapolObjective = eapol
838 .fromApp(appId)
839 .withPriority(MAX_PRIORITY)
840 .add(new ObjectiveContext() {
841 @Override
842 public void onSuccess(Objective objective) {
843 log.info("EAPOL flow objective {} for {}",
844 completeFlowOpToString(action), portWithName(sub.port));
845 if (log.isTraceEnabled()) {
846 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
847 }
848 }
849
850 @Override
851 public void onError(Objective objective, ObjectiveError error) {
852 log.error("Cannot {} eapol flow for {} : {}", action,
853 portWithName(sub.port), error);
854
855 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
856 updateConnectPointStatus(sk,
857 OltFlowsStatus.ERROR, null, null);
858 }
859 }
860 });
861
862 flowObjectiveService.filter(sub.device.id(), eapolObjective);
863
864 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
865 return true;
866 }
867
868 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800869 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700870 SubscriberAndDeviceInformation si) {
871 if (!enableEapol) {
872 return true;
873 }
874 // TODO verify we need an EAPOL flow for EACH service
875 AtomicBoolean success = new AtomicBoolean(true);
876 si.uniTagList().forEach(u -> {
877 // we assume that if subscriber flows are installed, tagged EAPOL is installed as well
878 boolean hasFlows = hasSubscriberFlows(sub.port, u);
879
880 // if the subscriber flows are present the EAPOL flow is present thus we don't need to install it,
881 // if the subscriber does not have flows the EAPOL flow is not present thus we don't need to remove it
882 if (action == FlowOperation.ADD && hasFlows ||
883 action == FlowOperation.REMOVE && !hasFlows) {
884 log.debug("Not {} EAPOL on {}({}) as subscriber flow status is {}", flowOpToString(action),
885 portWithName(sub.port), u.getServiceName(), hasFlows);
886 return;
887 }
888 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
889 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
890 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
891 u.getUpstreamOltBandwidthProfile(),
892 action, u.getPonCTag())) {
893 //
894 log.error("Failed to {} EAPOL with suscriber tags", action);
895 //TODO this sets it for all services, maybe some services succeeded.
896 success.set(false);
897 }
898 });
899 return success.get();
900 }
901
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800902 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700903 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
904 if (uti.getIsIgmpRequired()) {
905 DeviceId deviceId = sub.device.id();
906 // if we reached here a meter already exists
907 MeterId meterId = oltMeterService
908 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
909 MeterId oltMeterId = oltMeterService
910 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
911
912 processIgmpFilteringObjectives(deviceId, sub.port,
913 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
914 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
915 }
916 });
917 }
918
919 private boolean checkSadisRunning() {
920 if (bpService == null) {
921 log.warn("Sadis is not running");
922 return false;
923 }
924 return true;
925 }
926
927 private int getDefaultTechProfileId(Port port) {
928 if (!checkSadisRunning()) {
929 return defaultTechProfileId;
930 }
931 if (port != null) {
932 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
933 if (info != null && info.uniTagList().size() == 1) {
934 return info.uniTagList().get(0).getTechnologyProfileId();
935 }
936 }
937 return defaultTechProfileId;
938 }
939
940 protected Long createTechProfValueForWriteMetadata(VlanId cVlan, int techProfileId, MeterId upstreamOltMeterId) {
941 Long writeMetadata;
942
943 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
944 writeMetadata = (long) techProfileId << 32;
945 } else {
946 writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
947 }
948 if (upstreamOltMeterId == null) {
949 return writeMetadata;
950 } else {
951 return writeMetadata | upstreamOltMeterId.id();
952 }
953 }
954
955 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
956 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
957
958 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
959 .withKey(Criteria.matchInPort(port.number()))
960 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
961 .withMeta(DefaultTrafficTreatment.builder()
962 .setOutput(PortNumber.CONTROLLER).build())
963 .fromApp(appId)
964 .withPriority(MAX_PRIORITY)
965 .add(new ObjectiveContext() {
966 @Override
967 public void onSuccess(Objective objective) {
968 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
969 }
970
971 @Override
972 public void onError(Objective objective, ObjectiveError error) {
973 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
974 error);
975 }
976 });
977
978 flowObjectiveService.filter(deviceId, lldp);
979 }
980
981 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
982 FlowOperation action,
983 SubscriberAndDeviceInformation si) {
984 si.uniTagList().forEach(uti -> {
985
986 if (!uti.getIsDhcpRequired()) {
987 return;
988 }
989
990 // if it's an ADD skip if flows are there,
991 // if it's a DELETE skip if flows are not there
992 boolean hasFlows = hasDhcpFlows(port, uti);
993 if (action == FlowOperation.ADD && hasFlows ||
994 action == FlowOperation.REMOVE && !hasFlows) {
995 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
996 uti.getServiceName(), hasFlows);
997 return;
998 }
999
1000 log.info("{} DHCP flows for subscriber on {} and service {}",
1001 flowOpToString(action), portWithName(port), uti.getServiceName());
1002
1003 // if we reached here a meter already exists
1004 MeterId meterId = oltMeterService
1005 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1006 MeterId oltMeterId = oltMeterService
1007 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1008
1009 if (enableDhcpV4) {
1010 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1011 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1012 uti);
1013 }
1014 if (enableDhcpV6) {
1015 log.error("DHCP V6 not supported for subscribers");
1016 }
1017 });
1018 }
1019
1020 // FIXME return boolean, if this fails we need to retry
1021 protected void handleSubscriberDataFlows(Device device, Port port,
1022 FlowOperation action,
1023 SubscriberAndDeviceInformation si, String multicastServiceName) {
1024
1025 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001026 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001027 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1028 si.id(), portWithName(port));
1029 return;
1030 }
1031 si.uniTagList().forEach(uti -> {
1032
1033 boolean hasFlows = hasSubscriberFlows(port, uti);
1034 if (action == FlowOperation.ADD && hasFlows ||
1035 action == FlowOperation.REMOVE && !hasFlows) {
1036 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1037 uti.getServiceName(), hasFlows);
1038 return;
1039 }
1040
1041 if (multicastServiceName.equals(uti.getServiceName())) {
1042 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1043 "dataplane flows are not needed",
1044 uti.getServiceName(), si.id(), portWithName(port));
1045 return;
1046 }
1047
1048 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1049 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001050 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1051 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1052 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
1053 updateConnectPointStatus(sk, null, status, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001054
1055 // upstream flows
1056 MeterId usMeterId = oltMeterService
1057 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1058 MeterId oltUsMeterId = oltMeterService
1059 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
1060 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1061 oltUsMeterId, uti);
1062
1063 // downstream flows
1064 MeterId dsMeterId = oltMeterService
1065 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1066 MeterId oltDsMeterId = oltMeterService
1067 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
1068 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1069 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1070 });
1071 }
1072
1073 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1074 FlowOperation action, FlowDirection direction,
1075 int udpSrc, int udpDst, EthType ethType, byte protocol,
1076 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1077 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1078 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1079
1080
1081 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1082 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
1083 updateConnectPointStatus(sk, null, null, status);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001084
1085 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1086 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1087
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001088 if (meterId != null) {
1089 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001090 }
1091
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001092 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
1093 treatmentBuilder.writeMetadata(
1094 createTechProfValueForWriteMetadata(uti.getUniTagMatch(),
1095 uti.getTechnologyProfileId(), oltMeterId), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001096 }
1097
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001098 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001099 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001100 .addCondition(Criteria.matchEthType(ethType))
1101 .addCondition(Criteria.matchIPProtocol(protocol))
1102 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1103 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001104 .fromApp(appId)
1105 .withPriority(MAX_PRIORITY);
1106
Andrea Campanella0e34f562020-06-11 10:47:10 +02001107 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001108 if (direction == FlowDirection.UPSTREAM) {
1109 treatmentBuilder.setVlanId(uti.getPonCTag());
1110 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1111 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001112 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001113 if (uti.getUsPonCTagPriority() != -1) {
1114 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001115 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001116 }
1117
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001118 dhcpBuilder.withMeta(treatmentBuilder
1119 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001120
1121
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001122 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001123 @Override
1124 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001125 log.info("{} DHCP {} filter for {}.",
1126 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1127 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001128 }
1129
1130 @Override
1131 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001132 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001133 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1134 portWithName(port),
1135 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001136 error);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001137 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001138 }
1139 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001140 flowObjectiveService.filter(deviceId, dhcpUpstream);
1141 }
1142
1143 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1144 FlowOperation action, FlowDirection direction,
1145 MeterId meterId, MeterId oltMeterId, int techProfileId,
1146 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1147
1148 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1149 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1150 if (direction == FlowDirection.UPSTREAM) {
1151
1152 if (techProfileId != NONE_TP_ID) {
1153 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(null,
1154 techProfileId, oltMeterId), 0);
1155 }
1156
1157
1158 if (meterId != null) {
1159 treatmentBuilder.meter(meterId);
1160 }
1161
1162 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1163 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1164 }
1165
1166 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1167 treatmentBuilder.setVlanId(cTag);
1168 }
1169
1170 if (vlanPcp != -1) {
1171 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1172 }
1173 }
1174
1175 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1176
1177 FilteringObjective igmp = filterBuilder
1178 .withKey(Criteria.matchInPort(port.number()))
1179 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1180 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1181 .withMeta(treatmentBuilder
1182 .setOutput(PortNumber.CONTROLLER).build())
1183 .fromApp(appId)
1184 .withPriority(MAX_PRIORITY)
1185 .add(new ObjectiveContext() {
1186 @Override
1187 public void onSuccess(Objective objective) {
1188 log.info("Igmp filter for {} {}.", portWithName(port), action);
1189 }
1190
1191 @Override
1192 public void onError(Objective objective, ObjectiveError error) {
1193 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1194 error);
1195 }
1196 });
1197
1198 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001199
1200 }
1201
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001202 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1203 FlowOperation action, FlowDirection direction,
1204 MeterId meterId, MeterId oltMeterId, int techProfileId,
1205 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001206
1207 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1208 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001209
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001210 if (meterId != null) {
1211 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001212 }
1213
1214 if (techProfileId != NONE_TP_ID) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001215 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(cTag, techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001216 }
1217
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001218 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1219 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001220 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001221 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1222 .fromApp(appId)
1223 .withPriority(10000);
1224
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001225 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001226 treatmentBuilder.setVlanId(cTag);
1227 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1228 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1229 }
1230 if (vlanPcp != null) {
1231 treatmentBuilder.setVlanPcp(vlanPcp);
1232 }
1233 }
1234 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1235
1236 FilteringObjective pppoed = pppoedBuilder
1237 .add(new ObjectiveContext() {
1238 @Override
1239 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001240 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001241 }
1242
1243 @Override
1244 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001245 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1246 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001247 }
1248 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001249 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001250 }
1251
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001252 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1253 FlowOperation action,
1254 MeterId upstreamMeterId,
1255 MeterId upstreamOltMeterId,
1256 UniTagInformation uti) {
1257 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001258 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001259 .matchInPort(port.number())
1260 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001261 .build();
1262
Andrea Campanella327c5722020-01-30 11:34:13 +01001263 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1264 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001265 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001266 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001267 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001268 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001269 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1270 treatmentBuilder.popVlan();
1271 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001272
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001273 if (uti.getUsPonCTagPriority() != -1) {
1274 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001275
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001276 }
1277
1278 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001279 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001280
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001281 if (uti.getUsPonSTagPriority() != -1) {
1282 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001283 }
1284
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001285 treatmentBuilder.setOutput(nniPort.number())
1286 .writeMetadata(createMetadata(uti.getPonCTag(),
1287 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001288
yasin saplib4b8ee12021-06-13 18:25:20 +00001289 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1290
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001291 if (upstreamMeterId != null) {
1292 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001293 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1294 }
1295 if (upstreamOltMeterId != null) {
1296 treatmentBuilder.meter(upstreamOltMeterId);
1297 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001298 }
1299
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001300 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1301 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001302 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001303
1304 ObjectiveContext context = new ObjectiveContext() {
1305 @Override
1306 public void onSuccess(Objective objective) {
1307 log.info("{} Upstream Data plane filter for {}.",
1308 completeFlowOpToString(action), sk);
1309 }
1310
1311 @Override
1312 public void onError(Objective objective, ObjectiveError error) {
1313 log.error("Upstream Data plane filter for {} failed {} because {}.",
1314 sk, action, error);
1315 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
1316 }
1317 };
1318
1319 ForwardingObjective flow = null;
1320 if (action == FlowOperation.ADD) {
1321 flow = flowBuilder.add(context);
1322 } else if (action == FlowOperation.REMOVE) {
1323 flow = flowBuilder.remove(context);
1324 } else {
1325 log.error("Flow action not supported: {}", action);
1326 }
1327
1328 if (flow != null) {
1329 flowObjectiveService.forward(deviceId, flow);
1330 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001331 }
1332
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001333 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1334 FlowOperation action,
1335 MeterId downstreamMeterId,
1336 MeterId downstreamOltMeterId,
1337 UniTagInformation uti,
1338 MacAddress macAddress) {
1339 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001340 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001341 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001342 .matchVlanId(uti.getPonSTag())
1343 .matchInPort(nniPort.number())
1344 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001345
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001346 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1347 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001348 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001349
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001350 if (uti.getDsPonCTagPriority() != -1) {
1351 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001352 }
1353
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001354 if (macAddress != null) {
1355 selectorBuilder.matchEthDst(macAddress);
1356 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001357
1358 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1359 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001360 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001361
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001362 treatmentBuilder.writeMetadata(createMetadata(uti.getPonCTag(),
1363 uti.getTechnologyProfileId(),
1364 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001365
Andrea Campanella981e86c2021-03-12 11:35:33 +01001366 // Upstream pbit is used to remark inner vlan pbit.
1367 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1368 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1369 // all pbit acceptance are not widely supported by vendors even though present in
1370 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001371 if (uti.getUsPonCTagPriority() != -1) {
1372 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001373 }
1374
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001375 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1376 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1377 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001378 }
1379
yasin saplib4b8ee12021-06-13 18:25:20 +00001380 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1381
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001382 if (downstreamMeterId != null) {
1383 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001384 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001385 }
1386
yasin saplib4b8ee12021-06-13 18:25:20 +00001387 if (downstreamOltMeterId != null) {
1388 treatmentBuilder.meter(downstreamOltMeterId);
1389 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1390 }
1391
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001392 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1393 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001394
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001395 ObjectiveContext context = new ObjectiveContext() {
1396 @Override
1397 public void onSuccess(Objective objective) {
1398 log.info("{} Downstream Data plane filter for {}.",
1399 completeFlowOpToString(action), sk);
1400 }
1401
1402 @Override
1403 public void onError(Objective objective, ObjectiveError error) {
1404 log.info("Downstream Data plane filter for {} failed {} because {}.",
1405 sk, action, error);
1406 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
1407 }
1408 };
1409
1410 ForwardingObjective flow = null;
1411 if (action == FlowOperation.ADD) {
1412 flow = flowBuilder.add(context);
1413 } else if (action == FlowOperation.REMOVE) {
1414 flow = flowBuilder.remove(context);
1415 } else {
1416 log.error("Flow action not supported: {}", action);
1417 }
1418
1419 if (flow != null) {
1420 if (log.isTraceEnabled()) {
1421 log.trace("Forwarding rule {}", flow);
1422 }
1423 flowObjectiveService.forward(deviceId, flow);
1424 }
Andrea Campanella600d2e22020-06-22 11:00:31 +02001425 }
1426
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001427 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1428 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001429 Integer priority,
1430 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001431 return DefaultForwardingObjective.builder()
1432 .withFlag(ForwardingObjective.Flag.VERSATILE)
1433 .withPriority(priority)
1434 .makePermanent()
1435 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001436 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001437 .fromApp(appId)
1438 .withTreatment(treatment);
1439 }
1440
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001441 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
1442 if (techProfileId == NONE_TP_ID) {
Andrea Campanella7c49b792020-05-11 11:36:53 +02001443 techProfileId = DEFAULT_TP_ID_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001444 }
1445
1446 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
1447 }
1448
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001449 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1450 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1451 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001452
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001453 si.uniTagList().forEach(uniTagInfo -> {
1454 if (uniTagInfo.getEnableMacLearning()) {
1455 requiresMacLearning.set(true);
1456 }
1457 });
1458
1459 return requiresMacLearning.get();
1460 }
1461
1462 /**
1463 * Checks whether the subscriber has the MacAddress configured or discovered.
1464 *
1465 * @param deviceId DeviceId for this subscriber
1466 * @param port Port for this subscriber
1467 * @param si SubscriberAndDeviceInformation
1468 * @return boolean
1469 */
1470 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1471 AtomicBoolean isConfigured = new AtomicBoolean();
1472 isConfigured.set(true);
1473
1474 si.uniTagList().forEach(uniTagInfo -> {
1475 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
1476 boolean configureMac = isMacAddressValid(uniTagInfo);
1477 boolean discoveredMac = false;
1478 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1479 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1480 if (optHost.isPresent() && optHost.get().mac() != null) {
1481 discoveredMac = true;
1482 }
1483 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1484 log.debug("Awaiting for macAddress on {} for service {}",
1485 portWithName(port), uniTagInfo.getServiceName());
1486 isConfigured.set(false);
1487 }
1488 });
1489
1490 return isConfigured.get();
1491 }
1492
1493 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
1494 boolean configuredMac = isMacAddressValid(uniTagInfo);
1495 if (configuredMac) {
1496 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1497 } else if (uniTagInfo.getEnableMacLearning()) {
1498 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1499 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1500 if (optHost.isPresent() && optHost.get().mac() != null) {
1501 return optHost.get().mac();
1502 }
1503 }
1504 return null;
1505 }
1506
1507 private boolean isMacAddressValid(UniTagInformation tagInformation) {
1508 return tagInformation.getConfiguredMacAddress() != null &&
1509 !tagInformation.getConfiguredMacAddress().trim().equals("") &&
1510 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
1511 }
1512
1513 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
1514 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001515 if (log.isTraceEnabled()) {
1516 log.trace("Updating cpStatus {} with values: eapolFlow={}, subscriberFlows={}, dhcpFlow={}",
1517 key, eapolStatus, subscriberFlowsStatus, dhcpStatus);
1518 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001519 try {
1520 cpStatusWriteLock.lock();
1521 OltPortStatus status = cpStatus.get(key);
1522
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001523
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001524 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001525 // if we don't have status for the connectPoint
1526 // and we're only updating status to PENDING_REMOVE or ERROR
1527 // do not create it. This is because this case will only happen when a device is removed
1528 // and it's status cleaned
1529 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1530 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1531 statusesToIgnore.add(OltFlowsStatus.ERROR);
1532
1533 if (
1534 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1535 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1536 ) {
1537 if (log.isTraceEnabled()) {
1538 log.trace("Ignoring cpStatus update as status is meaningless");
1539 }
1540 return;
1541 }
1542
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001543 status = new OltPortStatus(
1544 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
1545 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
1546 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE
1547 );
1548 } else {
1549 if (eapolStatus != null) {
1550 status.defaultEapolStatus = eapolStatus;
1551 }
1552 if (subscriberFlowsStatus != null) {
1553 status.subscriberFlowsStatus = subscriberFlowsStatus;
1554 }
1555 if (dhcpStatus != null) {
1556 status.dhcpStatus = dhcpStatus;
1557 }
1558 }
1559
1560 cpStatus.put(key, status);
1561 } finally {
1562 cpStatusWriteLock.unlock();
1563 }
1564 }
1565
1566 protected class InternalFlowListener implements FlowRuleListener {
1567 @Override
1568 public void event(FlowRuleEvent event) {
1569 if (appId.id() != (event.subject().appId())) {
1570 return;
1571 }
1572
1573 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1574 if (log.isTraceEnabled()) {
1575 log.trace("ignoring flow event {} " +
1576 "as not leader for {}", event, event.subject().deviceId());
1577 }
1578 return;
1579 }
1580
1581 switch (event.type()) {
1582 case RULE_ADDED:
1583 case RULE_REMOVED:
1584 Port port = getCpFromFlowRule(event.subject());
1585 if (port == null) {
1586 log.error("Can't find port in flow {}", event.subject());
1587 return;
1588 }
1589 if (log.isTraceEnabled()) {
1590 log.trace("flow event {} on cp {}: {}", event.type(),
1591 portWithName(port), event.subject());
1592 }
1593 updateCpStatus(event.type(), port, event.subject());
1594 return;
1595 case RULE_ADD_REQUESTED:
1596 case RULE_REMOVE_REQUESTED:
1597 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1598 return;
1599 default:
1600 return;
1601 }
1602 }
1603
1604 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
1605 OltFlowsStatus status = flowRuleStatusToOltFlowStatus(type);
1606 if (isDefaultEapolFlow(flowRule)) {
1607 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1608 defaultEapolUniTag);
1609 if (log.isTraceEnabled()) {
1610 log.trace("update defaultEapolStatus {} on {}", status, sk);
1611 }
1612 updateConnectPointStatus(sk, status, null, null);
1613 } else if (isDhcpFlow(flowRule)) {
1614 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1615 if (sk == null) {
1616 return;
1617 }
1618 if (log.isTraceEnabled()) {
1619 log.trace("update dhcpStatus {} on {}", status, sk);
1620 }
1621 updateConnectPointStatus(sk, null, null, status);
1622 } else if (isDataFlow(flowRule)) {
1623
1624 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()),
1625 getCpFromFlowRule(flowRule).number())) {
1626 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1627 return;
1628 }
1629
1630 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1631 if (sk == null) {
1632 return;
1633 }
1634 if (log.isTraceEnabled()) {
1635 log.trace("update dataplaneStatus {} on {}", status, sk);
1636 }
1637 updateConnectPointStatus(sk, null, status, null);
1638 }
1639 }
1640
1641 private boolean isDefaultEapolFlow(FlowRule flowRule) {
1642 EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
1643 if (c == null) {
1644 return false;
1645 }
1646 if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
1647 AtomicBoolean isDefault = new AtomicBoolean(false);
1648 flowRule.treatment().allInstructions().forEach(instruction -> {
1649 if (instruction.type() == L2MODIFICATION) {
1650 L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
1651 if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
1652 L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
1653 (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
1654 if (vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
1655 isDefault.set(true);
1656 return;
1657 }
1658 }
1659 }
1660 });
1661 return isDefault.get();
1662 }
1663 return false;
1664 }
1665
1666 /**
1667 * Returns true if the flow is a DHCP flow.
1668 * Matches both upstream and downstream flows.
1669 *
1670 * @param flowRule The FlowRule to evaluate
1671 * @return boolean
1672 */
1673 private boolean isDhcpFlow(FlowRule flowRule) {
1674 IPProtocolCriterion ipCriterion = (IPProtocolCriterion) flowRule.selector()
1675 .getCriterion(Criterion.Type.IP_PROTO);
1676 if (ipCriterion == null) {
1677 return false;
1678 }
1679
1680 UdpPortCriterion src = (UdpPortCriterion) flowRule.selector().getCriterion(Criterion.Type.UDP_SRC);
1681
1682 if (src == null) {
1683 return false;
1684 }
1685 return ipCriterion.protocol() == IPv4.PROTOCOL_UDP &&
1686 (src.udpPort().toInt() == 68 || src.udpPort().toInt() == 67);
1687 }
1688
1689 private boolean isDataFlow(FlowRule flowRule) {
1690 // we consider subscriber flows the one that matches on VLAN_VID
1691 // method is valid only because it's the last check after EAPOL and DHCP.
1692 // this matches mcast flows as well, if we want to avoid that we can
1693 // filter out the elements that have groups in the treatment or
1694 // mcastIp in the selector
1695 // IPV4_DST:224.0.0.22/32
1696 // treatment=[immediate=[GROUP:0x1]]
1697
1698 return flowRule.selector().getCriterion(Criterion.Type.VLAN_VID) != null;
1699 }
1700
1701 private Port getCpFromFlowRule(FlowRule flowRule) {
1702 DeviceId deviceId = flowRule.deviceId();
1703 PortCriterion inPort = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
1704 if (inPort != null) {
1705 PortNumber port = inPort.port();
1706 return deviceService.getPort(deviceId, port);
1707 }
1708 return null;
1709 }
1710
1711 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule) {
1712 Port flowPort = getCpFromFlowRule(flowRule);
1713 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1714
1715 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1716 if (si == null && !isNni) {
1717 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1718 return null;
1719 }
1720
1721 if (isNni) {
1722 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1723 }
1724
1725 Optional<UniTagInformation> found = Optional.empty();
1726 VlanId flowVlan = null;
1727 if (isDhcpFlow(flowRule)) {
1728 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1729 L2ModificationInstruction.ModVlanIdInstruction instruction =
1730 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1731 flowVlan = instruction.vlanId();
1732 } else {
1733 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1734 VlanIdCriterion vlanIdCriterion =
1735 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1736 if (vlanIdCriterion == null) {
1737 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1738 return null;
1739 }
1740 flowVlan = vlanIdCriterion.vlanId();
1741 }
1742
1743 VlanId finalFlowVlan = flowVlan;
1744 found = si.uniTagList().stream().filter(uti ->
1745 uti.getPonCTag().equals(finalFlowVlan) ||
1746 uti.getPonSTag().equals(finalFlowVlan) ||
1747 uti.getUniTagMatch().equals(finalFlowVlan)
1748 ).findFirst();
1749
1750
1751 if (found.isEmpty()) {
1752 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1753 }
1754
1755 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1756
1757 }
1758
1759 private OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
1760 switch (type) {
1761 case RULE_ADD_REQUESTED:
1762 return OltFlowsStatus.PENDING_ADD;
1763 case RULE_ADDED:
1764 return OltFlowsStatus.ADDED;
1765 case RULE_REMOVE_REQUESTED:
1766 return OltFlowsStatus.PENDING_REMOVE;
1767 case RULE_REMOVED:
1768 return OltFlowsStatus.REMOVED;
1769 default:
1770 return OltFlowsStatus.NONE;
1771 }
1772 }
1773 }
1774
1775 protected void bindSadisService(SadisService service) {
1776 this.subsService = service.getSubscriberInfoService();
1777 this.bpService = service.getBandwidthProfileService();
1778 log.info("Sadis service is loaded");
1779 }
1780
1781 protected void unbindSadisService(SadisService service) {
1782 this.subsService = null;
1783 this.bpService = null;
1784 log.info("Sadis service is unloaded");
1785 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001786}