blob: ebe0505af339b9d7d303e94efeff517ef80690d7 [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;
Andrea Campanella40d2b342022-02-04 18:13:37 +010031import org.onosproject.net.AnnotationKeys;
yasin saplib4b8ee12021-06-13 18:25:20 +000032import org.onosproject.net.Annotations;
Matteo Scandolo3a037a32020-04-01 12:17:50 -070033import org.onosproject.net.ConnectPoint;
yasin saplib4b8ee12021-06-13 18:25:20 +000034import org.onosproject.net.DefaultAnnotations;
Andrea Campanella40d2b342022-02-04 18:13:37 +010035import org.onosproject.net.DefaultPort;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070036import org.onosproject.net.Device;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000037import org.onosproject.net.DeviceId;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070038import org.onosproject.net.Host;
39import org.onosproject.net.Port;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000040import org.onosproject.net.PortNumber;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070044import org.onosproject.net.flow.FlowRule;
45import org.onosproject.net.flow.FlowRuleEvent;
46import org.onosproject.net.flow.FlowRuleListener;
47import org.onosproject.net.flow.FlowRuleService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000048import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
50import org.onosproject.net.flow.criteria.Criteria;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070051import org.onosproject.net.flow.criteria.Criterion;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070052import org.onosproject.net.flow.criteria.VlanIdCriterion;
53import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000054import org.onosproject.net.flowobjective.DefaultFilteringObjective;
55import org.onosproject.net.flowobjective.DefaultForwardingObjective;
56import org.onosproject.net.flowobjective.FilteringObjective;
57import org.onosproject.net.flowobjective.FlowObjectiveService;
58import org.onosproject.net.flowobjective.ForwardingObjective;
59import org.onosproject.net.flowobjective.Objective;
60import org.onosproject.net.flowobjective.ObjectiveContext;
61import org.onosproject.net.flowobjective.ObjectiveError;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070062import org.onosproject.net.host.HostService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000063import org.onosproject.net.meter.MeterId;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000064import org.onosproject.store.serializers.KryoNamespaces;
65import org.onosproject.store.service.Serializer;
66import org.onosproject.store.service.StorageService;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +053067import org.opencord.olt.impl.fttb.FttbUtils;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000068import org.opencord.sadis.BandwidthProfileInformation;
69import org.opencord.sadis.BaseInformationService;
70import org.opencord.sadis.SadisService;
71import org.opencord.sadis.SubscriberAndDeviceInformation;
72import org.opencord.sadis.UniTagInformation;
73import org.osgi.service.component.ComponentContext;
74import org.osgi.service.component.annotations.Activate;
75import org.osgi.service.component.annotations.Component;
76import org.osgi.service.component.annotations.Deactivate;
77import org.osgi.service.component.annotations.Modified;
78import org.osgi.service.component.annotations.Reference;
79import org.osgi.service.component.annotations.ReferenceCardinality;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000080import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000081import org.slf4j.Logger;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070082import org.slf4j.LoggerFactory;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000083
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080084import java.util.ArrayList;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010085import java.util.Dictionary;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070086import java.util.HashMap;
Andrea Campanella61650a12022-01-24 18:09:44 -080087import java.util.HashSet;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070088import java.util.Iterator;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080089import java.util.List;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000090import java.util.Map;
Andrea Campanellabfb47af2021-06-03 11:09:45 +020091import java.util.Optional;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010092import java.util.Properties;
Andrea Campanella61650a12022-01-24 18:09:44 -080093import java.util.Set;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070094import java.util.concurrent.atomic.AtomicBoolean;
95import java.util.concurrent.locks.Lock;
96import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010097
98import static com.google.common.base.Strings.isNullOrEmpty;
99import static org.onlab.util.Tools.get;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700100import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
101import static org.opencord.olt.impl.OltUtils.flowOpToString;
102import static org.opencord.olt.impl.OltUtils.getPortName;
103import static org.opencord.olt.impl.OltUtils.portWithName;
104import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
105import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
106import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
107import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
108import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
109import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
110import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
111import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
112import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
113import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
114import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
115import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
116import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
117import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
yasin sapli0823c932022-01-26 11:26:09 +0000118import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI;
119import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI_DEFAULT;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700120import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
121import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530122import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DIRECTION;
123import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DOWNSTREAM;
124import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_UPSTREAM;
125import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_ANCP_TRAFFIC;
126import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_MGMT_TRAFFIC;
127import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_NAME;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700128import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
129import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
130import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
131import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530132import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_SUBSCRIBER_TRAFFIC;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000133
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000134@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700135 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000136 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
137 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700138 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000139 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
yasin sapli0823c932022-01-26 11:26:09 +0000140 ENABLE_PPPOE_ON_NNI + ":Boolean=" + ENABLE_PPPOE_ON_NNI_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300141 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700142 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
143 // FIXME remove this option as potentially dangerous in production
144 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000145})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700146public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
149 protected CoreService coreService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700152 protected ComponentConfigService cfgService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
155 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000156
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000157 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
158 bind = "bindSadisService",
159 unbind = "unbindSadisService",
160 policy = ReferencePolicy.DYNAMIC)
161 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700164 protected OltMeterServiceInterface oltMeterService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY)
167 protected OltDeviceServiceInterface oltDeviceService;
168
169 @Reference(cardinality = ReferenceCardinality.MANDATORY)
170 protected FlowRuleService flowRuleService;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY)
173 protected HostService hostService;
174
175 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000176 protected DeviceService deviceService;
177
178 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000179 protected StorageService storageService;
180
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700181 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
182 protected BaseInformationService<BandwidthProfileInformation> bpService;
183
184 private static final String APP_NAME = "org.opencord.olt";
185 protected ApplicationId appId;
186 private static final Integer MAX_PRIORITY = 10000;
187 private static final Integer MIN_PRIORITY = 1000;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530188 public static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700189 private static final int NONE_TP_ID = -1;
190 private static final String V4 = "V4";
191 private static final String V6 = "V6";
192 private final Logger log = LoggerFactory.getLogger(getClass());
193
194 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
195 .setServiceName("defaultEapol").build();
196 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
197 .setServiceName("nni")
198 .setTechnologyProfileId(NONE_TP_ID)
199 .setPonCTag(VlanId.NONE)
200 .setUniTagMatch(VlanId.ANY)
201 .setUsPonCTagPriority(-1)
202 .build();
203
204 /**
205 * Connect Point status map.
206 * Used to keep track of which cp has flows that needs to be removed when the status changes.
207 */
208 protected Map<ServiceKey, OltPortStatus> cpStatus;
209 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
210 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
211 private final Lock cpStatusReadLock = cpStatusLock.readLock();
212
213 /**
214 * This map contains the subscriber that have been provisioned by the operator.
215 * They may or may not have flows, depending on the port status.
216 * The map is used to define whether flows need to be provisioned when a port comes up.
217 */
218 protected Map<ServiceKey, Boolean> provisionedSubscribers;
219 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
220 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
221 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
222
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000223 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700224 * Create DHCP trap flow on NNI port(s).
225 */
226 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000227
228 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700229 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000230 **/
231 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
232
233 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700234 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000235 **/
236 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
237
238 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700239 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000240 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700241 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000242
243 /**
244 * Send EAPOL authentication trap flows before subscriber provisioning.
245 **/
246 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
247
248 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300249 * Send PPPoED authentication trap flows before subscriber provisioning.
250 **/
yasin sapli0823c932022-01-26 11:26:09 +0000251 protected boolean enablePppoeOnNni = ENABLE_PPPOE_ON_NNI_DEFAULT;
252
253 /**
254 * Enable flows for PPPoE if it is required in sadis config.
255 **/
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300256 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
257
258 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000259 * Default technology profile id that is used for authentication trap flows.
260 **/
261 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
262
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700263 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
264
265 public enum FlowOperation {
266 ADD,
267 REMOVE;
268
269
270 @Override
271 public String toString() {
272 return super.toString().toLowerCase();
273 }
274 }
275
276 public enum FlowDirection {
277 UPSTREAM,
278 DOWNSTREAM,
279 }
280
281 public enum OltFlowsStatus {
282 NONE,
283 PENDING_ADD,
284 ADDED,
285 PENDING_REMOVE,
286 REMOVED,
287 ERROR
288 }
289
290 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000291
292 @Activate
293 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700294 cfgService.registerProperties(getClass());
295 appId = coreService.registerApplication(APP_NAME);
296 internalFlowListener = new InternalFlowListener();
297
298 modified(context);
299
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000300 KryoNamespace serializer = KryoNamespace.newBuilder()
301 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700302 .register(OltFlowsStatus.class)
303 .register(FlowDirection.class)
304 .register(OltPortStatus.class)
305 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000306 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700307 .register(new ServiceKeySerializer(), ServiceKey.class)
308 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000309 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000310
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700311 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
312 .withName("volt-cp-status")
313 .withApplicationId(appId)
314 .withSerializer(Serializer.using(serializer))
315 .build().asJavaMap();
316
317 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
318 .withName("volt-provisioned-subscriber")
319 .withApplicationId(appId)
320 .withSerializer(Serializer.using(serializer))
321 .build().asJavaMap();
322
323 flowRuleService.addListener(internalFlowListener);
324
325 log.info("Started");
326 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000327
328 @Deactivate
329 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700330 cfgService.unregisterProperties(getClass(), false);
331 flowRuleService.removeListener(internalFlowListener);
332 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000333 }
334
335 @Modified
336 public void modified(ComponentContext context) {
337
338 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
339
Saurav Dasf62cea82020-08-26 17:43:04 -0700340 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000341 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700342 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000343 }
344
Andrea Campanella7c49b792020-05-11 11:36:53 +0200345 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000346 if (v4 != null) {
347 enableDhcpV4 = v4;
348 }
349
Andrea Campanella7c49b792020-05-11 11:36:53 +0200350 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000351 if (v6 != null) {
352 enableDhcpV6 = v6;
353 }
354
Saurav Dasf62cea82020-08-26 17:43:04 -0700355 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000356 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700357 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000358 }
359
Andrea Campanella7c49b792020-05-11 11:36:53 +0200360 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000361 if (eap != null) {
362 enableEapol = eap;
363 }
364
yasin sapli0823c932022-01-26 11:26:09 +0000365 Boolean pppoeInNni = Tools.isPropertyEnabled(properties, ENABLE_PPPOE_ON_NNI);
366 if (pppoeInNni != null) {
367 enablePppoeOnNni = pppoeInNni;
368 }
369
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300370 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
371 if (pppoe != null) {
372 enablePppoe = pppoe;
373 }
374
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700375 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
376 if (wait != null) {
377 waitForRemoval = wait;
378 }
379
Andrea Campanella7c49b792020-05-11 11:36:53 +0200380 String tpId = get(properties, DEFAULT_TP_ID);
381 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000382
yasin sapli0823c932022-01-26 11:26:09 +0000383 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " + "enableDhcpV6:{}, " +
384 "enableIgmpOnNni:{}, " + "enableEapol:{}, enablePppoeOnNni: {}, enablePppoe:{}, " +
385 "defaultTechProfileId:{}," + "waitForRemoval:{}",
386 enableDhcpOnNni, enableDhcpV4, enableDhcpV6, enableIgmpOnNni, enableEapol,
387 enablePppoeOnNni, enablePppoe, defaultTechProfileId, waitForRemoval);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000388 }
389
390 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700391 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
392 try {
393 cpStatusReadLock.lock();
394 return ImmutableMap.copyOf(cpStatus);
395 } finally {
396 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000397 }
398 }
399
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700400 @Override
401 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
402 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800403 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700404 Map<ServiceKey, UniTagInformation> subscribers =
405 new HashMap<>();
406 try {
407 cpStatusReadLock.lock();
408
409 cpStatus.forEach((sk, status) -> {
410 if (
411 // not NNI Port
412 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
413 sk.getPort().connectPoint().port()) &&
414 // not EAPOL flow
Andrea Campanella982fd332022-01-19 09:14:12 +0100415 !sk.getService().equals(defaultEapolUniTag) &&
416 !status.subscriberFlowsStatus.equals(OltFlowsStatus.PENDING_REMOVE)
417 && !status.subscriberFlowsStatus.equals(OltFlowsStatus.REMOVED)
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800418
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700419 ) {
420 subscribers.put(sk, sk.getService());
421 }
422 });
423
424 return ImmutableMap.copyOf(subscribers);
425 } finally {
426 cpStatusReadLock.unlock();
427 }
428 }
429
430 @Override
431 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
432 try {
433 provisionedSubscribersReadLock.lock();
434 return ImmutableMap.copyOf(provisionedSubscribers);
435 } finally {
436 provisionedSubscribersReadLock.unlock();
437 }
438 }
439
440 @Override
441 public void handleNniFlows(Device device, Port port, FlowOperation action) {
442
443 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800444 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700445 processLldpFilteringObjective(device.id(), port, action);
446
447 if (enableDhcpOnNni) {
448 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800449 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
450 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700451 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
452 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
453 null, null, nniUniTag);
454 }
455 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800456 log.debug("{} DHCPv6 trap flow on NNI {} for device {}", flowOpToString(action),
457 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700458 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
459 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
460 null, null, nniUniTag);
461 }
462 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800463 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700464 }
465
466 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800467 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700468 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
469 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
470 }
471
yasin sapli0823c932022-01-26 11:26:09 +0000472 if (enablePppoeOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800473 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700474 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
475 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
476 }
477 }
478
479 @Override
480 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
481 String oltBandwidthProfileId) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700482 // we only need to something if EAPOL is enabled
483 if (!enableEapol) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100484 log.debug("Eapol is disabled for {}", portWithName(sub.port));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700485 return true;
486 }
487
488 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
489 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
490 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
491 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
492 } else {
493 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
494 return false;
495 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700496 }
497
498 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100499 log.debug("Adding default flows for {}, status {}", portWithName(sub.port), sub.status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700500 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
501 if (log.isTraceEnabled()) {
502 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
503 }
504 return false;
505 }
506 if (hasDefaultEapol(sub.port)) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100507 OltPortStatus status = getOltPortStatus(sub.port, defaultEapolUniTag);
508 log.debug("Eapol is already present for {} with status {}", portWithName(sub.port), status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700509 return true;
510 }
511 return handleEapolFlow(sub, bandwidthProfileId,
512 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
513
514 }
515
516 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
517 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
518 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
519 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
520 }
521
522 @Override
523 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
524 String multicastServiceName) {
525 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
526 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
527 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200528 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED ||
529 sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700530 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
531 } else {
532 log.error("don't know how to handle {}", sub);
533 return false;
534 }
535 }
536
537 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
538 String multicastServiceName) {
539 if (log.isTraceEnabled()) {
540 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
541 }
542 if (enableEapol) {
543 if (hasDefaultEapol(sub.port)) {
544 // remove EAPOL flow and throw exception so that we'll retry later
545 if (!isDefaultEapolPendingRemoval(sub.port)) {
546 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
547 }
548
549 if (waitForRemoval) {
550 // NOTE wait for removal is a flag only needed to make sure VOLTHA
551 // does not explode with the flows remove/add in the same batch
552 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
553 return false;
554 } else {
555 log.warn("continuing provisioning on {}", portWithName(sub.port));
556 }
557 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700558 }
559
560 // NOTE createMeters will return if the meters are not installed
561 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800562 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700563 return false;
564 }
565
566 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
567 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
568 sub.subscriberAndDeviceInformation);
569
570 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
571 && !isMacAddressAvailable(sub.device.id(), sub.port,
572 sub.subscriberAndDeviceInformation)) {
573 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
574 return false;
575 }
576
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800577 // NOTE that the EAPOL flows handling is based on the data-plane flows status
578 // always process them before
579 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
580
yasin sapli0823c932022-01-26 11:26:09 +0000581 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
582
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700583 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
584 sub.subscriberAndDeviceInformation, multicastServiceName);
585
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700586 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
587
588 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
589 return true;
590 }
591
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800592 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700593 String multicastServiceName) {
594
595 if (log.isTraceEnabled()) {
596 log.trace("Removal of subscriber on {} started",
597 portWithName(sub.port));
598 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800599 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200600 //If the port has been removed the device service will return null, while it will be true if it's just disabled
601 boolean isPortPresent = deviceService.getPort(new ConnectPoint(sub.device.id(), sub.port.number())) != null;
602 if (log.isTraceEnabled()) {
603 log.trace("Port {} present: ", portWithName(sub.port), isPortPresent);
604 }
605 // Always remove the EAPOL flow in case of port disable,remove or subscriber remove.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700606 if (enableEapol) {
607 // remove the tagged eapol
608 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200609 log.info("Removal of eapol flow for subscriber on {} completed", portWithName(sub.port));
610
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800611 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200612 // If the port is gone entirely (onu delete) or it's enabled (subscriber remove request) remove all the flows
613 // In the case the port is just disabled we remove only the EAPOL flow because the ONU disable only represents
614 // the UNI side of the ONU going down, either for RG cable detach or for an administrative decision, but the PON
615 // side is still up as nothing changed, so no need to add/remove flows, when and if the UNI comes up
616 // we will re-push the EAPOL flow to require the subscriber to auth again.
617 // When the subscriber is admin removed from REST or CLI we ignore the port status.
Andrea Campanella7ef88992022-05-17 12:38:00 +0200618 // Check the admin Status of the port
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200619 if (!isPortPresent || sub.port.isEnabled() || sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700620
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200621 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800622
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200623 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE,
624 sub.subscriberAndDeviceInformation);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800625
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200626 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
627
628 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
629
630
631 if (enableEapol) {
632
633 // if any of the services still has flows, return false
634 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
635 while (iter.hasNext()) {
636 UniTagInformation entry = iter.next();
637 if (areSubscriberFlowsPendingRemoval(sub.port, entry, enableEapol)) {
638 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
639 portWithName(sub.port), entry.getServiceName());
640 return false;
641 }
642 }
643
644 // once the flows are removed add the default one back
645 // (only if the port is ENABLED and still present on the device)
646 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
647
648 // NOTE we remove the subscriber when the port goes down
649 // but in that case we don't need to add default eapol
650 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
651 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800652 }
653 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200654 // FIXME check the return status of the flow and return accordingly
655 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
656 return true;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700657 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700658 return true;
659 }
660
661 @Override
662 public boolean hasDefaultEapol(Port port) {
663 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
664 // NOTE we consider ERROR as a present EAPOL flow as ONOS
665 // will keep trying to add it
666 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
667 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
668 status.defaultEapolStatus == OltFlowsStatus.ERROR);
669 }
670
671 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
672 try {
673 cpStatusReadLock.lock();
674 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
675 OltPortStatus status = cpStatus.get(sk);
676 return status;
677 } finally {
678 cpStatusReadLock.unlock();
679 }
680 }
681
682 public boolean isDefaultEapolPendingRemoval(Port port) {
683 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
684 if (log.isTraceEnabled()) {
685 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
686 status, portWithName(port), defaultEapolUniTag);
687 }
688 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
689 }
690
691 @Override
692 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
693 OltPortStatus status = getOltPortStatus(port, uti);
694 if (log.isTraceEnabled()) {
695 log.trace("Status during DHCP flow check {} for port {} and service {}",
696 status, portWithName(port), uti.getServiceName());
697 }
698 return status != null &&
699 (status.dhcpStatus == OltFlowsStatus.ADDED ||
700 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
701 }
702
703 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000704 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
705 OltPortStatus status = getOltPortStatus(port, uti);
706 if (log.isTraceEnabled()) {
707 log.trace("Status during PPPoE flow check {} for port {} and service {}",
708 status, portWithName(port), uti.getServiceName());
709 }
710 return status != null &&
711 (status.pppoeStatus == OltFlowsStatus.ADDED ||
712 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
713 }
714
715 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700716 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
717
718 OltPortStatus status = getOltPortStatus(port, uti);
719 if (log.isTraceEnabled()) {
720 log.trace("Status during subscriber flow check {} for port {} and service {}",
721 status, portWithName(port), uti.getServiceName());
722 }
723 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
724 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
725 }
726
Andrea Campanella87241ae2022-03-11 11:20:24 +0100727 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti, boolean enableEapol) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800728 OltPortStatus status = getOltPortStatus(port, uti);
729 if (log.isTraceEnabled()) {
730 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
731 status, portWithName(port), uti);
732 }
Andrea Campanella87241ae2022-03-11 11:20:24 +0100733 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE ||
734 (enableEapol && status.subscriberEapolStatus == OltFlowsStatus.PENDING_REMOVE) ||
735 (uti.getIsDhcpRequired() && status.dhcpStatus == OltFlowsStatus.PENDING_REMOVE));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800736 }
737
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700738 @Override
739 public void purgeDeviceFlows(DeviceId deviceId) {
740 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800741 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700742
743 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800744 if (log.isTraceEnabled()) {
745 log.trace("Clearing cp status from device {}", deviceId);
746 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700747 try {
748 cpStatusWriteLock.lock();
749 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
750 while (iter.hasNext()) {
751 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
752 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
753 cpStatus.remove(entry.getKey());
754 }
755 }
756 } finally {
757 cpStatusWriteLock.unlock();
758 }
759
760 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800761 if (log.isTraceEnabled()) {
762 log.trace("Clearing provisioned subscribers from device {}", deviceId);
763 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700764 try {
765 provisionedSubscribersWriteLock.lock();
766 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
767 while (iter.hasNext()) {
768 Map.Entry<ServiceKey, Boolean> entry = iter.next();
769 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
770 provisionedSubscribers.remove(entry.getKey());
771 }
772 }
773 } finally {
774 provisionedSubscribersWriteLock.unlock();
775 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800776 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700777 }
778
779 @Override
780 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800781 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700782 try {
783 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800784 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700785 } finally {
786 provisionedSubscribersReadLock.unlock();
787 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800788
789 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
790 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
791 return true;
792 }
793 }
794 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700795 }
796
797 @Override
798 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
799 try {
800 provisionedSubscribersReadLock.lock();
801 Boolean provisioned = provisionedSubscribers.get(sk);
802 if (provisioned == null || !provisioned) {
803 return false;
804 }
805 } finally {
806 provisionedSubscribersReadLock.unlock();
807 }
808 return true;
809 }
810
811 @Override
812 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
813 try {
814 provisionedSubscribersWriteLock.lock();
815 provisionedSubscribers.put(sk, status);
816 } finally {
817 provisionedSubscribersWriteLock.unlock();
818 }
819 }
820
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800821 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700822 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
823
824 // create a subscriberKey for the EAPOL flow
825 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
Andrea Campanella87241ae2022-03-11 11:20:24 +0100826 OltFlowsStatus status = action == FlowOperation.ADD ?
827 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700828 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +0100829 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE,
830 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700831
Andrea Campanella87241ae2022-03-11 11:20:24 +0100832 } else {
833 updateConnectPointStatus(sk, OltFlowsStatus.NONE, status, OltFlowsStatus.NONE,
834 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700835 }
836
837 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
838 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
839
840 int techProfileId = getDefaultTechProfileId(sub.port);
841 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
842
843 // in the delete case the meter should still be there as we remove
844 // the meters only if no flows are pointing to them
845 if (meterId == null) {
846 log.debug("MeterId is null for BandwidthProfile {} on device {}",
847 bandwidthProfile, sub.device.id());
848 return false;
849 }
850
851 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
852 if (oltMeterId == null) {
853 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
854 oltBandwidthProfile, sub.device.id());
855 return false;
856 }
857
858 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
859 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
860
861 FilteringObjective.Builder eapolAction;
862
863 if (action == FlowOperation.ADD) {
864 eapolAction = filterBuilder.permit();
865 } else if (action == FlowOperation.REMOVE) {
866 eapolAction = filterBuilder.deny();
867 } else {
868 log.error("Operation {} not supported", action);
869 return false;
870 }
871
872 FilteringObjective.Builder baseEapol = eapolAction
873 .withKey(Criteria.matchInPort(sub.port.number()))
874 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
875
876 // NOTE we only need to add the treatment to install the flow,
877 // we can remove it based in the match
878 FilteringObjective.Builder eapol;
879
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530880 // Setting VlanId.NONE as cvlan in the metadata as the packet will be single tagged
881 // and cvlan should not be filled.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700882 TrafficTreatment treatment = treatmentBuilder
883 .meter(meterId)
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530884 .writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530885 VlanId.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700886 techProfileId, oltMeterId), 0)
887 .setOutput(PortNumber.CONTROLLER)
888 .pushVlan()
889 .setVlanId(vlanId)
890 .build();
891 eapol = baseEapol
892 .withMeta(treatment);
893
894 FilteringObjective eapolObjective = eapol
895 .fromApp(appId)
896 .withPriority(MAX_PRIORITY)
897 .add(new ObjectiveContext() {
898 @Override
899 public void onSuccess(Objective objective) {
900 log.info("EAPOL flow objective {} for {}",
901 completeFlowOpToString(action), portWithName(sub.port));
902 if (log.isTraceEnabled()) {
903 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
904 }
905 }
906
907 @Override
908 public void onError(Objective objective, ObjectiveError error) {
909 log.error("Cannot {} eapol flow for {} : {}", action,
910 portWithName(sub.port), error);
911
912 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
913 updateConnectPointStatus(sk,
Andrea Campanella87241ae2022-03-11 11:20:24 +0100914 OltFlowsStatus.ERROR, null, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700915 }
916 }
917 });
918
919 flowObjectiveService.filter(sub.device.id(), eapolObjective);
920
921 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
922 return true;
923 }
924
925 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800926 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700927 SubscriberAndDeviceInformation si) {
928 if (!enableEapol) {
929 return true;
930 }
931 // TODO verify we need an EAPOL flow for EACH service
932 AtomicBoolean success = new AtomicBoolean(true);
933 si.uniTagList().forEach(u -> {
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200934 //Always act on the eapol flow
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700935 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
936 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
937 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
938 u.getUpstreamOltBandwidthProfile(),
939 action, u.getPonCTag())) {
940 //
Andrea Campanella87241ae2022-03-11 11:20:24 +0100941 log.error("Failed to {} EAPOL with subscriber tags", action);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700942 //TODO this sets it for all services, maybe some services succeeded.
943 success.set(false);
944 }
945 });
946 return success.get();
947 }
948
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800949 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700950 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
951 if (uti.getIsIgmpRequired()) {
952 DeviceId deviceId = sub.device.id();
953 // if we reached here a meter already exists
954 MeterId meterId = oltMeterService
955 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
956 MeterId oltMeterId = oltMeterService
957 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
958
959 processIgmpFilteringObjectives(deviceId, sub.port,
960 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
961 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
962 }
963 });
964 }
965
966 private boolean checkSadisRunning() {
967 if (bpService == null) {
968 log.warn("Sadis is not running");
969 return false;
970 }
971 return true;
972 }
973
974 private int getDefaultTechProfileId(Port port) {
975 if (!checkSadisRunning()) {
976 return defaultTechProfileId;
977 }
978 if (port != null) {
979 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
980 if (info != null && info.uniTagList().size() == 1) {
981 return info.uniTagList().get(0).getTechnologyProfileId();
982 }
983 }
984 return defaultTechProfileId;
985 }
986
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700987 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
988 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
989
990 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
991 .withKey(Criteria.matchInPort(port.number()))
992 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
993 .withMeta(DefaultTrafficTreatment.builder()
994 .setOutput(PortNumber.CONTROLLER).build())
995 .fromApp(appId)
996 .withPriority(MAX_PRIORITY)
997 .add(new ObjectiveContext() {
998 @Override
999 public void onSuccess(Objective objective) {
1000 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
1001 }
1002
1003 @Override
1004 public void onError(Objective objective, ObjectiveError error) {
1005 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
1006 error);
1007 }
1008 });
1009
1010 flowObjectiveService.filter(deviceId, lldp);
1011 }
1012
1013 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
1014 FlowOperation action,
1015 SubscriberAndDeviceInformation si) {
1016 si.uniTagList().forEach(uti -> {
1017
1018 if (!uti.getIsDhcpRequired()) {
1019 return;
1020 }
1021
1022 // if it's an ADD skip if flows are there,
1023 // if it's a DELETE skip if flows are not there
1024 boolean hasFlows = hasDhcpFlows(port, uti);
1025 if (action == FlowOperation.ADD && hasFlows ||
1026 action == FlowOperation.REMOVE && !hasFlows) {
1027 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1028 uti.getServiceName(), hasFlows);
1029 return;
1030 }
1031
1032 log.info("{} DHCP flows for subscriber on {} and service {}",
1033 flowOpToString(action), portWithName(port), uti.getServiceName());
1034
1035 // if we reached here a meter already exists
1036 MeterId meterId = oltMeterService
1037 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1038 MeterId oltMeterId = oltMeterService
1039 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1040
1041 if (enableDhcpV4) {
1042 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1043 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1044 uti);
1045 }
1046 if (enableDhcpV6) {
1047 log.error("DHCP V6 not supported for subscribers");
1048 }
1049 });
1050 }
1051
yasin sapli0823c932022-01-26 11:26:09 +00001052 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1053 FlowOperation action,
1054 SubscriberAndDeviceInformation si) {
1055 si.uniTagList().forEach(uti -> {
1056
1057 if (!uti.getIsPppoeRequired()) {
1058 return;
1059 }
1060
1061 // if it's an ADD skip if flows are there,
1062 // if it's a DELETE skip if flows are not there
1063 boolean hasFlows = hasPppoeFlows(port, uti);
1064 if (action == FlowOperation.ADD && hasFlows ||
1065 action == FlowOperation.REMOVE && !hasFlows) {
1066 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1067 uti.getServiceName(), hasFlows);
1068 return;
1069 }
1070
1071 log.info("{} PPPoE flows for subscriber on {} and service {}",
1072 flowOpToString(action), portWithName(port), uti.getServiceName());
1073
1074 // if we reached here a meter already exists
1075 MeterId meterId = oltMeterService
1076 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1077 MeterId oltMeterId = oltMeterService
1078 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1079
1080 if (enablePppoe) {
1081 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1082 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1083 (byte) uti.getUsPonCTagPriority());
1084 }
1085 });
1086 }
1087
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001088 // FIXME return boolean, if this fails we need to retry
1089 protected void handleSubscriberDataFlows(Device device, Port port,
1090 FlowOperation action,
1091 SubscriberAndDeviceInformation si, String multicastServiceName) {
1092
1093 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001094 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001095 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1096 si.id(), portWithName(port));
1097 return;
1098 }
1099 si.uniTagList().forEach(uti -> {
1100
1101 boolean hasFlows = hasSubscriberFlows(port, uti);
1102 if (action == FlowOperation.ADD && hasFlows ||
1103 action == FlowOperation.REMOVE && !hasFlows) {
1104 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1105 uti.getServiceName(), hasFlows);
1106 return;
1107 }
1108
1109 if (multicastServiceName.equals(uti.getServiceName())) {
1110 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1111 "dataplane flows are not needed",
1112 uti.getServiceName(), si.id(), portWithName(port));
1113 return;
1114 }
1115
1116 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1117 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001118 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1119 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1120 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001121 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001122
1123 // upstream flows
1124 MeterId usMeterId = oltMeterService
1125 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1126 MeterId oltUsMeterId = oltMeterService
1127 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301128
1129 if (FttbUtils.isFttbService(uti)) {
1130 processFttbUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action,
1131 usMeterId, oltUsMeterId, uti, si);
1132 } else {
1133 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1134 oltUsMeterId, uti);
1135 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001136
1137 // downstream flows
1138 MeterId dsMeterId = oltMeterService
1139 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1140 MeterId oltDsMeterId = oltMeterService
1141 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301142
1143 if (FttbUtils.isFttbService(uti)) {
1144 processFttbDownstreamDataFilteringObjects(device.id(), port, nniPort.get(),
1145 action, dsMeterId, oltDsMeterId, uti, si);
1146 } else {
1147 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1148 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1149 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001150 });
1151 }
1152
1153 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1154 FlowOperation action, FlowDirection direction,
1155 int udpSrc, int udpDst, EthType ethType, byte protocol,
1156 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1157 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1158 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1159
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301160 String serviceName = uti.getServiceName();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001161
1162 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1163 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001164 updateConnectPointStatus(sk, null, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001165
1166 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1167 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1168
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001169 if (meterId != null) {
1170 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001171 }
1172
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001173 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001174 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001175 .addCondition(Criteria.matchEthType(ethType))
1176 .addCondition(Criteria.matchIPProtocol(protocol))
1177 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1178 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001179 .fromApp(appId)
1180 .withPriority(MAX_PRIORITY);
1181
Andrea Campanella0e34f562020-06-11 10:47:10 +02001182 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001183 if (direction == FlowDirection.UPSTREAM) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301184 if (serviceName != null && serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC)) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301185 FttbUtils.addUpstreamDhcpCondition(dhcpBuilder, uti);
1186 FttbUtils.addUpstreamDhcpTreatment(treatmentBuilder, uti);
1187 } else {
1188 treatmentBuilder.setVlanId(uti.getPonCTag());
1189 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1190 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
1191 }
1192 if (uti.getUsPonCTagPriority() != -1) {
1193 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
1194 }
Andrea Campanella0e34f562020-06-11 10:47:10 +02001195 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301196 } else if (direction == FlowDirection.DOWNSTREAM) {
1197 // Down stream DHCP vid to be matched if OLT Sadis info contains Vlan id in nniDhcpTrapVid.
1198 Device device = deviceService.getDevice(deviceId);
1199 SubscriberAndDeviceInformation oltSub = subsService.get(device.serialNumber());
1200 VlanId nniDhcpTrapVid = oltSub.nniDhcpTrapVid();
1201
1202 if (nniDhcpTrapVid != null && !VlanId.vlanId(VlanId.NO_VID).equals(nniDhcpTrapVid)) {
1203 dhcpBuilder.addCondition(Criteria.matchVlanId(nniDhcpTrapVid));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001204 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001205 }
1206
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301207 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301208 // Setting VlanId.NONE as cvlan, as the packet will be single tagged and cvlan should not be filled.
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301209 treatmentBuilder.writeMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301210 OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301211 uti.getTechnologyProfileId(), oltMeterId), 0);
1212 }
1213
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001214 dhcpBuilder.withMeta(treatmentBuilder
1215 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001216
1217
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001218 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001219 @Override
1220 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001221 log.info("{} DHCP {} filter for {}.",
1222 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1223 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001224 }
1225
1226 @Override
1227 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001228 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001229 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1230 portWithName(port),
1231 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001232 error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001233 updateConnectPointStatus(sk, null, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001234 }
1235 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001236 flowObjectiveService.filter(deviceId, dhcpUpstream);
1237 }
1238
1239 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1240 FlowOperation action, FlowDirection direction,
1241 MeterId meterId, MeterId oltMeterId, int techProfileId,
1242 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1243
1244 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1245 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1246 if (direction == FlowDirection.UPSTREAM) {
1247
1248 if (techProfileId != NONE_TP_ID) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301249 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(null,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001250 techProfileId, oltMeterId), 0);
1251 }
1252
1253
1254 if (meterId != null) {
1255 treatmentBuilder.meter(meterId);
1256 }
1257
1258 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1259 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1260 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001261 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1262 treatmentBuilder.setVlanId(cTag);
1263 }
1264
1265 if (vlanPcp != -1) {
1266 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1267 }
1268 }
1269
1270 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1271
1272 FilteringObjective igmp = filterBuilder
1273 .withKey(Criteria.matchInPort(port.number()))
1274 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1275 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1276 .withMeta(treatmentBuilder
1277 .setOutput(PortNumber.CONTROLLER).build())
1278 .fromApp(appId)
1279 .withPriority(MAX_PRIORITY)
1280 .add(new ObjectiveContext() {
1281 @Override
1282 public void onSuccess(Objective objective) {
1283 log.info("Igmp filter for {} {}.", portWithName(port), action);
1284 }
1285
1286 @Override
1287 public void onError(Objective objective, ObjectiveError error) {
1288 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1289 error);
1290 }
1291 });
1292
1293 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001294
1295 }
1296
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001297 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1298 FlowOperation action, FlowDirection direction,
1299 MeterId meterId, MeterId oltMeterId, int techProfileId,
1300 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001301
1302 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1303 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001304
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001305 if (meterId != null) {
1306 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001307 }
1308
1309 if (techProfileId != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301310 // Setting VlanId.NONE as cvlan as the packet will be single tagged and cvlan should not be filled.
1311 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
1312 techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001313 }
1314
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001315 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1316 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001317 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001318 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1319 .fromApp(appId)
1320 .withPriority(10000);
1321
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001322 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001323 treatmentBuilder.setVlanId(cTag);
1324 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1325 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1326 }
1327 if (vlanPcp != null) {
1328 treatmentBuilder.setVlanPcp(vlanPcp);
1329 }
1330 }
1331 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1332
1333 FilteringObjective pppoed = pppoedBuilder
1334 .add(new ObjectiveContext() {
1335 @Override
1336 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001337 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001338 }
1339
1340 @Override
1341 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001342 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1343 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001344 }
1345 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001346 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001347 }
1348
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001349 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1350 FlowOperation action,
1351 MeterId upstreamMeterId,
1352 MeterId upstreamOltMeterId,
1353 UniTagInformation uti) {
1354 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001355 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001356 .matchInPort(port.number())
1357 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001358 .build();
1359
Andrea Campanella327c5722020-01-30 11:34:13 +01001360 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1361 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001362 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001363 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001364 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001365 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001366 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1367 treatmentBuilder.popVlan();
1368 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001369
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001370 if (uti.getUsPonCTagPriority() != -1) {
1371 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001372
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001373 }
1374
1375 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001376 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001377
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001378 if (uti.getUsPonSTagPriority() != -1) {
1379 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001380 }
1381
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001382 treatmentBuilder.setOutput(nniPort.number())
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301383 .writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001384 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001385
yasin saplib4b8ee12021-06-13 18:25:20 +00001386 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1387
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001388 if (upstreamMeterId != null) {
1389 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001390 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1391 }
1392 if (upstreamOltMeterId != null) {
1393 treatmentBuilder.meter(upstreamOltMeterId);
1394 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001395 }
1396
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001397 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1398 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001399 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001400
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301401 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1402 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001403 }
1404
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001405 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1406 FlowOperation action,
1407 MeterId downstreamMeterId,
1408 MeterId downstreamOltMeterId,
1409 UniTagInformation uti,
1410 MacAddress macAddress) {
1411 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001412 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001413 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001414 .matchVlanId(uti.getPonSTag())
1415 .matchInPort(nniPort.number())
1416 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001417
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001418 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1419 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001420 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001421
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001422 if (uti.getDsPonCTagPriority() != -1) {
1423 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001424 }
1425
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001426 if (macAddress != null) {
1427 selectorBuilder.matchEthDst(macAddress);
1428 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001429
1430 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1431 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001432 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001433
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301434 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001435 uti.getTechnologyProfileId(),
1436 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001437
Andrea Campanella981e86c2021-03-12 11:35:33 +01001438 // Upstream pbit is used to remark inner vlan pbit.
1439 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1440 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1441 // all pbit acceptance are not widely supported by vendors even though present in
1442 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001443 if (uti.getUsPonCTagPriority() != -1) {
1444 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001445 }
1446
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001447 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1448 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1449 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001450 }
1451
yasin saplib4b8ee12021-06-13 18:25:20 +00001452 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1453
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001454 if (downstreamMeterId != null) {
1455 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001456 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001457 }
1458
yasin saplib4b8ee12021-06-13 18:25:20 +00001459 if (downstreamOltMeterId != null) {
1460 treatmentBuilder.meter(downstreamOltMeterId);
1461 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1462 }
1463
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001464 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1465 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001466
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301467 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1468 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanella600d2e22020-06-22 11:00:31 +02001469 }
1470
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001471 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1472 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001473 Integer priority,
1474 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001475 return DefaultForwardingObjective.builder()
1476 .withFlag(ForwardingObjective.Flag.VERSATILE)
1477 .withPriority(priority)
1478 .makePermanent()
1479 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001480 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001481 .fromApp(appId)
1482 .withTreatment(treatment);
1483 }
1484
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001485 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1486 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1487 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001488
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001489 si.uniTagList().forEach(uniTagInfo -> {
1490 if (uniTagInfo.getEnableMacLearning()) {
1491 requiresMacLearning.set(true);
1492 }
1493 });
1494
1495 return requiresMacLearning.get();
1496 }
1497
1498 /**
1499 * Checks whether the subscriber has the MacAddress configured or discovered.
1500 *
1501 * @param deviceId DeviceId for this subscriber
1502 * @param port Port for this subscriber
1503 * @param si SubscriberAndDeviceInformation
1504 * @return boolean
1505 */
1506 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1507 AtomicBoolean isConfigured = new AtomicBoolean();
1508 isConfigured.set(true);
1509
1510 si.uniTagList().forEach(uniTagInfo -> {
1511 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301512 boolean configureMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001513 boolean discoveredMac = false;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301514
1515 final VlanId vlan;
1516
1517 if (FttbUtils.isFttbDpuOrAncpService(uniTagInfo)) {
1518 // Using S tag, as C tag is replaced by Stag by ONU.
1519 vlan = uniTagInfo.getPonSTag();
1520 } else {
1521 vlan = uniTagInfo.getPonCTag();
1522 }
1523
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001524 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301525 .stream().filter(host -> host.vlan().equals(vlan)).findFirst();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001526 if (optHost.isPresent() && optHost.get().mac() != null) {
1527 discoveredMac = true;
1528 }
1529 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1530 log.debug("Awaiting for macAddress on {} for service {}",
1531 portWithName(port), uniTagInfo.getServiceName());
1532 isConfigured.set(false);
1533 }
1534 });
1535
1536 return isConfigured.get();
1537 }
1538
1539 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301540 boolean configuredMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001541 if (configuredMac) {
1542 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1543 } else if (uniTagInfo.getEnableMacLearning()) {
1544 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1545 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1546 if (optHost.isPresent() && optHost.get().mac() != null) {
1547 return optHost.get().mac();
1548 }
1549 }
1550 return null;
1551 }
1552
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001553 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001554 OltFlowsStatus subscriberEapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001555 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1556 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001557 if (log.isTraceEnabled()) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001558 log.trace("Updating cpStatus {} with values: eapolFlow={}, " +
1559 "subscriberEapolStatus={}, subscriberFlows={}, dhcpFlow={}",
1560 key, eapolStatus, subscriberEapolStatus, subscriberFlowsStatus, dhcpStatus);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001561 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001562 try {
1563 cpStatusWriteLock.lock();
1564 OltPortStatus status = cpStatus.get(key);
1565
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001566
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001567 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001568 // if we don't have status for the connectPoint
1569 // and we're only updating status to PENDING_REMOVE or ERROR
1570 // do not create it. This is because this case will only happen when a device is removed
1571 // and it's status cleaned
1572 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1573 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1574 statusesToIgnore.add(OltFlowsStatus.ERROR);
1575
1576 if (
1577 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1578 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1579 ) {
1580 if (log.isTraceEnabled()) {
1581 log.trace("Ignoring cpStatus update as status is meaningless");
1582 }
1583 return;
1584 }
1585
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001586 status = new OltPortStatus(
1587 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001588 subscriberEapolStatus != null ? subscriberEapolStatus : OltFlowsStatus.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001589 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001590 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1591 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001592 );
1593 } else {
1594 if (eapolStatus != null) {
1595 status.defaultEapolStatus = eapolStatus;
1596 }
1597 if (subscriberFlowsStatus != null) {
1598 status.subscriberFlowsStatus = subscriberFlowsStatus;
1599 }
1600 if (dhcpStatus != null) {
1601 status.dhcpStatus = dhcpStatus;
1602 }
1603 }
1604
1605 cpStatus.put(key, status);
1606 } finally {
1607 cpStatusWriteLock.unlock();
1608 }
1609 }
1610
1611 protected class InternalFlowListener implements FlowRuleListener {
1612 @Override
1613 public void event(FlowRuleEvent event) {
1614 if (appId.id() != (event.subject().appId())) {
1615 return;
1616 }
1617
1618 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1619 if (log.isTraceEnabled()) {
1620 log.trace("ignoring flow event {} " +
1621 "as not leader for {}", event, event.subject().deviceId());
1622 }
1623 return;
1624 }
1625
1626 switch (event.type()) {
1627 case RULE_ADDED:
1628 case RULE_REMOVED:
Andrea Campanella40d2b342022-02-04 18:13:37 +01001629 DeviceId deviceId = event.subject().deviceId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001630 Port port = getCpFromFlowRule(event.subject());
1631 if (port == null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001632 log.warn("Port is gone in ONOS, " +
1633 "manually creating it {}", event.subject());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301634 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(event.subject());
Andrea Campanella40d2b342022-02-04 18:13:37 +01001635 cpStatusReadLock.lock();
1636 Optional<ServiceKey> keyWithPort = cpStatus.keySet()
1637 .stream().filter(key -> key.getPort().connectPoint()
1638 .deviceId().equals(deviceId)
1639 && key.getPort().connectPoint().port()
1640 .equals(inPort)).findFirst();
1641 cpStatusReadLock.unlock();
1642 if (keyWithPort.isPresent()) {
1643 port = new DefaultPort(deviceService.getDevice(deviceId),
1644 inPort, false,
1645 DefaultAnnotations.builder()
1646 .set(AnnotationKeys.PORT_NAME,
1647 keyWithPort.get().getPort().name())
1648 .build());
1649 } else {
1650 log.warn("Can't find corresponding status for {}/{}", deviceId, inPort);
1651 return;
1652 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001653 }
1654 if (log.isTraceEnabled()) {
1655 log.trace("flow event {} on cp {}: {}", event.type(),
1656 portWithName(port), event.subject());
1657 }
1658 updateCpStatus(event.type(), port, event.subject());
1659 return;
1660 case RULE_ADD_REQUESTED:
1661 case RULE_REMOVE_REQUESTED:
1662 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1663 return;
1664 default:
1665 return;
1666 }
1667 }
1668
1669 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301670 OltFlowsStatus status = OltFlowServiceUtils.flowRuleStatusToOltFlowStatus(type);
1671 if (OltFlowServiceUtils.isDefaultEapolFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001672 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1673 defaultEapolUniTag);
1674 if (log.isTraceEnabled()) {
1675 log.trace("update defaultEapolStatus {} on {}", status, sk);
1676 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001677 updateConnectPointStatus(sk, status, null, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301678 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001679 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
1680 if (sk == null) {
1681 return;
1682 }
1683 if (log.isTraceEnabled()) {
1684 log.trace("update subscriberEapolStatus {} on {}", status, sk);
1685 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +02001686 updateConnectPointStatus(sk, null, status, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301687 } else if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001688 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001689 if (sk == null) {
1690 return;
1691 }
1692 if (log.isTraceEnabled()) {
1693 log.trace("update dhcpStatus {} on {}", status, sk);
1694 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001695 updateConnectPointStatus(sk, null, null, null, status, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301696 } else if (OltFlowServiceUtils.isPppoeFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001697 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
yasin sapli0823c932022-01-26 11:26:09 +00001698 if (sk == null) {
1699 return;
1700 }
1701 if (log.isTraceEnabled()) {
1702 log.trace("update pppoeStatus {} on {}", status, sk);
1703 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001704 updateConnectPointStatus(sk, null, null, null, null, status);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301705 } else if (OltFlowServiceUtils.isDataFlow(flowRule)) {
1706 PortNumber number = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Andrea Campanella40d2b342022-02-04 18:13:37 +01001707 if (number == null) {
1708 log.error("Can't capture the port number from flow {}", flowRule);
1709 return;
1710 }
1711 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), number)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001712 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1713 return;
1714 }
1715
Andrea Campanella40d2b342022-02-04 18:13:37 +01001716 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001717 if (sk == null) {
1718 return;
1719 }
1720 if (log.isTraceEnabled()) {
1721 log.trace("update dataplaneStatus {} on {}", status, sk);
1722 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001723 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001724 }
1725 }
1726
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001727
Andrea Campanella87241ae2022-03-11 11:20:24 +01001728
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001729 private Port getCpFromFlowRule(FlowRule flowRule) {
1730 DeviceId deviceId = flowRule.deviceId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301731 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001732 if (inPort != null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001733 return deviceService.getPort(deviceId, inPort);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001734 }
1735 return null;
1736 }
1737
Andrea Campanella40d2b342022-02-04 18:13:37 +01001738 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule, Port flowPort) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001739 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1740
1741 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1742 if (si == null && !isNni) {
1743 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1744 return null;
1745 }
1746
1747 if (isNni) {
1748 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1749 }
1750
1751 Optional<UniTagInformation> found = Optional.empty();
1752 VlanId flowVlan = null;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301753 if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001754 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1755 L2ModificationInstruction.ModVlanIdInstruction instruction =
1756 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1757 flowVlan = instruction.vlanId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301758 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001759 // we need to make a special case for EAPOL as in the ATT workflow EAPOL flows don't match on tags
1760 L2ModificationInstruction.ModVlanIdInstruction instruction =
1761 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(2);
1762 flowVlan = instruction.vlanId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001763 } else {
1764 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1765 VlanIdCriterion vlanIdCriterion =
1766 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1767 if (vlanIdCriterion == null) {
1768 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1769 return null;
1770 }
1771 flowVlan = vlanIdCriterion.vlanId();
1772 }
1773
1774 VlanId finalFlowVlan = flowVlan;
1775 found = si.uniTagList().stream().filter(uti ->
1776 uti.getPonCTag().equals(finalFlowVlan) ||
1777 uti.getPonSTag().equals(finalFlowVlan) ||
1778 uti.getUniTagMatch().equals(finalFlowVlan)
1779 ).findFirst();
1780
1781
1782 if (found.isEmpty()) {
1783 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1784 }
1785
1786 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1787
1788 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001789 }
1790
1791 protected void bindSadisService(SadisService service) {
1792 this.subsService = service.getSubscriberInfoService();
1793 this.bpService = service.getBandwidthProfileService();
1794 log.info("Sadis service is loaded");
1795 }
1796
1797 protected void unbindSadisService(SadisService service) {
1798 this.subsService = null;
1799 this.bpService = null;
1800 log.info("Sadis service is unloaded");
1801 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301802
1803 private void processFttbUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1804 FlowOperation action,
1805 MeterId upstreamMeterId,
1806 MeterId upstreamOltMeterId,
1807 UniTagInformation uti,
1808 SubscriberAndDeviceInformation si) {
1809 String serviceName = uti.getServiceName();
1810 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1811 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1812 .matchInPort(port.number())
1813 .matchVlanId(uti.getPonCTag());
1814
1815 if (uti.getUsPonCTagPriority() != -1) {
1816 selectorBuilder.matchVlanPcp((byte) uti.getUsPonCTagPriority());
1817 }
1818
1819 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1820
1821 treatmentBuilder.setVlanId(uti.getPonSTag());
1822 if (uti.getUsPonSTagPriority() != -1) {
1823 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
1824 }
1825
1826 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1827 annotationBuilder.set(FTTB_SERVICE_NAME, serviceName);
1828 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_UPSTREAM);
1829
1830 if (upstreamMeterId != null) {
1831 treatmentBuilder.meter(upstreamMeterId);
1832 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1833 }
1834 if (upstreamOltMeterId != null) {
1835 treatmentBuilder.meter(upstreamOltMeterId);
1836 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
1837 }
1838
1839 VlanId innerVlan = null;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001840 treatmentBuilder.setOutput(nniPort.number());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301841 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1842 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1843 hostService, si, deviceId, port);
1844
1845 if (mac == null) {
1846 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1847 port, uti.getPonSTag(), serviceName);
1848 return;
1849 }
1850
1851 selectorBuilder.matchEthSrc(mac);
Andrea Campanella7ef88992022-05-17 12:38:00 +02001852
1853 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
1854 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301855
1856 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
Andrea Campanella7ef88992022-05-17 12:38:00 +02001857 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1858 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301859 }
1860
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301861 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1862 treatmentBuilder.build(), MIN_PRIORITY,
1863 annotationBuilder.build());
1864
1865 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1866 processForwardingRule(action, flowBuilder, context, deviceId);
1867 }
1868
1869 private void processFttbDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1870 FlowOperation action,
1871 MeterId downstreamMeterId,
1872 MeterId downstreamOltMeterId,
1873 UniTagInformation uti, SubscriberAndDeviceInformation si) {
1874 String serviceName = uti.getServiceName();
1875 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1876 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
1877 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1878 .matchVlanId(uti.getPonSTag())
1879 .matchInPort(nniPort.number());
1880
1881 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1882 .setVlanId(uti.getPonCTag())
1883 .setOutput(port.number());
1884
1885 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1886 annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
1887 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_DOWNSTREAM);
1888
1889 if (downstreamMeterId != null) {
1890 treatmentBuilder.meter(downstreamMeterId);
1891 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
1892 }
1893
1894 if (downstreamOltMeterId != null) {
1895 treatmentBuilder.meter(downstreamOltMeterId);
1896 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1897 }
1898
1899 VlanId innerVlan = null;
1900
1901 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1902 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1903 hostService, si, deviceId, port);
1904
1905 if (mac == null) {
1906 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1907 port, uti.getPonSTag(), serviceName);
1908 return;
1909 }
1910
1911 selectorBuilder.matchEthDst(mac);
1912 innerVlan = VlanId.NONE;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001913 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1914 uti.getTechnologyProfileId(),
1915 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301916
1917 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
Andrea Campanella7ef88992022-05-17 12:38:00 +02001918 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
1919 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1920 uti.getTechnologyProfileId(),
1921 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301922 }
1923
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301924 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1925 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
1926
1927 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1928 processForwardingRule(action, flowBuilder, context, deviceId);
1929 }
1930
1931 private ObjectiveContext getSubscriberFlowBuilderContext(ServiceKey sk, FlowOperation action,
1932 FlowDirection flowDirection) {
1933 ObjectiveContext context = new ObjectiveContext() {
1934 @Override
1935 public void onSuccess(Objective objective) {
1936 log.info("{} {} Data plane filter for {}.",
1937 completeFlowOpToString(action), flowDirection, sk);
1938 }
1939
1940 @Override
1941 public void onError(Objective objective, ObjectiveError error) {
1942 log.info("{} Data plane filter for {} failed {} because {}.",
1943 flowDirection, sk, action, error);
1944 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
1945 }
1946 };
1947
1948 return context;
1949 }
1950
1951 private void processForwardingRule(FlowOperation action, DefaultForwardingObjective.Builder flowBuilder,
1952 ObjectiveContext context, DeviceId deviceId) {
1953 ForwardingObjective flow = null;
1954 if (action == FlowOperation.ADD) {
1955 flow = flowBuilder.add(context);
1956 } else if (action == FlowOperation.REMOVE) {
1957 flow = flowBuilder.remove(context);
1958 } else {
1959 log.error("Flow action not supported: {}", action);
1960 }
1961
1962 if (flow != null) {
1963 if (log.isTraceEnabled()) {
1964 log.trace("Forwarding rule {}", flow);
1965 }
1966 flowObjectiveService.forward(deviceId, flow);
1967 }
1968 }
1969}