blob: 3a2c8186af06fb97db444173beedd1d1391db52e [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.
618 if (!isPortPresent || sub.port.isEnabled() || sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700619
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200620 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800621
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200622 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE,
623 sub.subscriberAndDeviceInformation);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800624
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200625 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
626
627 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
628
629
630 if (enableEapol) {
631
632 // if any of the services still has flows, return false
633 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
634 while (iter.hasNext()) {
635 UniTagInformation entry = iter.next();
636 if (areSubscriberFlowsPendingRemoval(sub.port, entry, enableEapol)) {
637 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
638 portWithName(sub.port), entry.getServiceName());
639 return false;
640 }
641 }
642
643 // once the flows are removed add the default one back
644 // (only if the port is ENABLED and still present on the device)
645 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
646
647 // NOTE we remove the subscriber when the port goes down
648 // but in that case we don't need to add default eapol
649 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
650 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800651 }
652 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200653 // FIXME check the return status of the flow and return accordingly
654 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
655 return true;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700656 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700657 return true;
658 }
659
660 @Override
661 public boolean hasDefaultEapol(Port port) {
662 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
663 // NOTE we consider ERROR as a present EAPOL flow as ONOS
664 // will keep trying to add it
665 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
666 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
667 status.defaultEapolStatus == OltFlowsStatus.ERROR);
668 }
669
670 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
671 try {
672 cpStatusReadLock.lock();
673 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
674 OltPortStatus status = cpStatus.get(sk);
675 return status;
676 } finally {
677 cpStatusReadLock.unlock();
678 }
679 }
680
681 public boolean isDefaultEapolPendingRemoval(Port port) {
682 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
683 if (log.isTraceEnabled()) {
684 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
685 status, portWithName(port), defaultEapolUniTag);
686 }
687 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
688 }
689
690 @Override
691 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
692 OltPortStatus status = getOltPortStatus(port, uti);
693 if (log.isTraceEnabled()) {
694 log.trace("Status during DHCP flow check {} for port {} and service {}",
695 status, portWithName(port), uti.getServiceName());
696 }
697 return status != null &&
698 (status.dhcpStatus == OltFlowsStatus.ADDED ||
699 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
700 }
701
702 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000703 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
704 OltPortStatus status = getOltPortStatus(port, uti);
705 if (log.isTraceEnabled()) {
706 log.trace("Status during PPPoE flow check {} for port {} and service {}",
707 status, portWithName(port), uti.getServiceName());
708 }
709 return status != null &&
710 (status.pppoeStatus == OltFlowsStatus.ADDED ||
711 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
712 }
713
714 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700715 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
716
717 OltPortStatus status = getOltPortStatus(port, uti);
718 if (log.isTraceEnabled()) {
719 log.trace("Status during subscriber flow check {} for port {} and service {}",
720 status, portWithName(port), uti.getServiceName());
721 }
722 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
723 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
724 }
725
Andrea Campanella87241ae2022-03-11 11:20:24 +0100726 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti, boolean enableEapol) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800727 OltPortStatus status = getOltPortStatus(port, uti);
728 if (log.isTraceEnabled()) {
729 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
730 status, portWithName(port), uti);
731 }
Andrea Campanella87241ae2022-03-11 11:20:24 +0100732 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE ||
733 (enableEapol && status.subscriberEapolStatus == OltFlowsStatus.PENDING_REMOVE) ||
734 (uti.getIsDhcpRequired() && status.dhcpStatus == OltFlowsStatus.PENDING_REMOVE));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800735 }
736
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700737 @Override
738 public void purgeDeviceFlows(DeviceId deviceId) {
739 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800740 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700741
742 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800743 if (log.isTraceEnabled()) {
744 log.trace("Clearing cp status from device {}", deviceId);
745 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700746 try {
747 cpStatusWriteLock.lock();
748 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
749 while (iter.hasNext()) {
750 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
751 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
752 cpStatus.remove(entry.getKey());
753 }
754 }
755 } finally {
756 cpStatusWriteLock.unlock();
757 }
758
759 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800760 if (log.isTraceEnabled()) {
761 log.trace("Clearing provisioned subscribers from device {}", deviceId);
762 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700763 try {
764 provisionedSubscribersWriteLock.lock();
765 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
766 while (iter.hasNext()) {
767 Map.Entry<ServiceKey, Boolean> entry = iter.next();
768 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
769 provisionedSubscribers.remove(entry.getKey());
770 }
771 }
772 } finally {
773 provisionedSubscribersWriteLock.unlock();
774 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800775 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700776 }
777
778 @Override
779 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800780 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700781 try {
782 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800783 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700784 } finally {
785 provisionedSubscribersReadLock.unlock();
786 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800787
788 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
789 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
790 return true;
791 }
792 }
793 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700794 }
795
796 @Override
797 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
798 try {
799 provisionedSubscribersReadLock.lock();
800 Boolean provisioned = provisionedSubscribers.get(sk);
801 if (provisioned == null || !provisioned) {
802 return false;
803 }
804 } finally {
805 provisionedSubscribersReadLock.unlock();
806 }
807 return true;
808 }
809
810 @Override
811 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
812 try {
813 provisionedSubscribersWriteLock.lock();
814 provisionedSubscribers.put(sk, status);
815 } finally {
816 provisionedSubscribersWriteLock.unlock();
817 }
818 }
819
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800820 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700821 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
822
823 // create a subscriberKey for the EAPOL flow
824 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
Andrea Campanella87241ae2022-03-11 11:20:24 +0100825 OltFlowsStatus status = action == FlowOperation.ADD ?
826 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700827 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +0100828 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE,
829 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700830
Andrea Campanella87241ae2022-03-11 11:20:24 +0100831 } else {
832 updateConnectPointStatus(sk, OltFlowsStatus.NONE, status, OltFlowsStatus.NONE,
833 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700834 }
835
836 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
837 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
838
839 int techProfileId = getDefaultTechProfileId(sub.port);
840 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
841
842 // in the delete case the meter should still be there as we remove
843 // the meters only if no flows are pointing to them
844 if (meterId == null) {
845 log.debug("MeterId is null for BandwidthProfile {} on device {}",
846 bandwidthProfile, sub.device.id());
847 return false;
848 }
849
850 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
851 if (oltMeterId == null) {
852 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
853 oltBandwidthProfile, sub.device.id());
854 return false;
855 }
856
857 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
858 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
859
860 FilteringObjective.Builder eapolAction;
861
862 if (action == FlowOperation.ADD) {
863 eapolAction = filterBuilder.permit();
864 } else if (action == FlowOperation.REMOVE) {
865 eapolAction = filterBuilder.deny();
866 } else {
867 log.error("Operation {} not supported", action);
868 return false;
869 }
870
871 FilteringObjective.Builder baseEapol = eapolAction
872 .withKey(Criteria.matchInPort(sub.port.number()))
873 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
874
875 // NOTE we only need to add the treatment to install the flow,
876 // we can remove it based in the match
877 FilteringObjective.Builder eapol;
878
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530879 // Setting VlanId.NONE as cvlan in the metadata as the packet will be single tagged
880 // and cvlan should not be filled.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700881 TrafficTreatment treatment = treatmentBuilder
882 .meter(meterId)
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530883 .writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530884 VlanId.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700885 techProfileId, oltMeterId), 0)
886 .setOutput(PortNumber.CONTROLLER)
887 .pushVlan()
888 .setVlanId(vlanId)
889 .build();
890 eapol = baseEapol
891 .withMeta(treatment);
892
893 FilteringObjective eapolObjective = eapol
894 .fromApp(appId)
895 .withPriority(MAX_PRIORITY)
896 .add(new ObjectiveContext() {
897 @Override
898 public void onSuccess(Objective objective) {
899 log.info("EAPOL flow objective {} for {}",
900 completeFlowOpToString(action), portWithName(sub.port));
901 if (log.isTraceEnabled()) {
902 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
903 }
904 }
905
906 @Override
907 public void onError(Objective objective, ObjectiveError error) {
908 log.error("Cannot {} eapol flow for {} : {}", action,
909 portWithName(sub.port), error);
910
911 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
912 updateConnectPointStatus(sk,
Andrea Campanella87241ae2022-03-11 11:20:24 +0100913 OltFlowsStatus.ERROR, null, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700914 }
915 }
916 });
917
918 flowObjectiveService.filter(sub.device.id(), eapolObjective);
919
920 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
921 return true;
922 }
923
924 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800925 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700926 SubscriberAndDeviceInformation si) {
927 if (!enableEapol) {
928 return true;
929 }
930 // TODO verify we need an EAPOL flow for EACH service
931 AtomicBoolean success = new AtomicBoolean(true);
932 si.uniTagList().forEach(u -> {
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200933 //Always act on the eapol flow
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700934 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
935 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
936 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
937 u.getUpstreamOltBandwidthProfile(),
938 action, u.getPonCTag())) {
939 //
Andrea Campanella87241ae2022-03-11 11:20:24 +0100940 log.error("Failed to {} EAPOL with subscriber tags", action);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700941 //TODO this sets it for all services, maybe some services succeeded.
942 success.set(false);
943 }
944 });
945 return success.get();
946 }
947
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800948 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700949 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
950 if (uti.getIsIgmpRequired()) {
951 DeviceId deviceId = sub.device.id();
952 // if we reached here a meter already exists
953 MeterId meterId = oltMeterService
954 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
955 MeterId oltMeterId = oltMeterService
956 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
957
958 processIgmpFilteringObjectives(deviceId, sub.port,
959 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
960 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
961 }
962 });
963 }
964
965 private boolean checkSadisRunning() {
966 if (bpService == null) {
967 log.warn("Sadis is not running");
968 return false;
969 }
970 return true;
971 }
972
973 private int getDefaultTechProfileId(Port port) {
974 if (!checkSadisRunning()) {
975 return defaultTechProfileId;
976 }
977 if (port != null) {
978 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
979 if (info != null && info.uniTagList().size() == 1) {
980 return info.uniTagList().get(0).getTechnologyProfileId();
981 }
982 }
983 return defaultTechProfileId;
984 }
985
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700986 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
987 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
988
989 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
990 .withKey(Criteria.matchInPort(port.number()))
991 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
992 .withMeta(DefaultTrafficTreatment.builder()
993 .setOutput(PortNumber.CONTROLLER).build())
994 .fromApp(appId)
995 .withPriority(MAX_PRIORITY)
996 .add(new ObjectiveContext() {
997 @Override
998 public void onSuccess(Objective objective) {
999 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
1000 }
1001
1002 @Override
1003 public void onError(Objective objective, ObjectiveError error) {
1004 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
1005 error);
1006 }
1007 });
1008
1009 flowObjectiveService.filter(deviceId, lldp);
1010 }
1011
1012 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
1013 FlowOperation action,
1014 SubscriberAndDeviceInformation si) {
1015 si.uniTagList().forEach(uti -> {
1016
1017 if (!uti.getIsDhcpRequired()) {
1018 return;
1019 }
1020
1021 // if it's an ADD skip if flows are there,
1022 // if it's a DELETE skip if flows are not there
1023 boolean hasFlows = hasDhcpFlows(port, uti);
1024 if (action == FlowOperation.ADD && hasFlows ||
1025 action == FlowOperation.REMOVE && !hasFlows) {
1026 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1027 uti.getServiceName(), hasFlows);
1028 return;
1029 }
1030
1031 log.info("{} DHCP flows for subscriber on {} and service {}",
1032 flowOpToString(action), portWithName(port), uti.getServiceName());
1033
1034 // if we reached here a meter already exists
1035 MeterId meterId = oltMeterService
1036 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1037 MeterId oltMeterId = oltMeterService
1038 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1039
1040 if (enableDhcpV4) {
1041 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1042 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1043 uti);
1044 }
1045 if (enableDhcpV6) {
1046 log.error("DHCP V6 not supported for subscribers");
1047 }
1048 });
1049 }
1050
yasin sapli0823c932022-01-26 11:26:09 +00001051 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1052 FlowOperation action,
1053 SubscriberAndDeviceInformation si) {
1054 si.uniTagList().forEach(uti -> {
1055
1056 if (!uti.getIsPppoeRequired()) {
1057 return;
1058 }
1059
1060 // if it's an ADD skip if flows are there,
1061 // if it's a DELETE skip if flows are not there
1062 boolean hasFlows = hasPppoeFlows(port, uti);
1063 if (action == FlowOperation.ADD && hasFlows ||
1064 action == FlowOperation.REMOVE && !hasFlows) {
1065 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1066 uti.getServiceName(), hasFlows);
1067 return;
1068 }
1069
1070 log.info("{} PPPoE flows for subscriber on {} and service {}",
1071 flowOpToString(action), portWithName(port), uti.getServiceName());
1072
1073 // if we reached here a meter already exists
1074 MeterId meterId = oltMeterService
1075 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1076 MeterId oltMeterId = oltMeterService
1077 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1078
1079 if (enablePppoe) {
1080 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1081 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1082 (byte) uti.getUsPonCTagPriority());
1083 }
1084 });
1085 }
1086
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001087 // FIXME return boolean, if this fails we need to retry
1088 protected void handleSubscriberDataFlows(Device device, Port port,
1089 FlowOperation action,
1090 SubscriberAndDeviceInformation si, String multicastServiceName) {
1091
1092 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001093 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001094 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1095 si.id(), portWithName(port));
1096 return;
1097 }
1098 si.uniTagList().forEach(uti -> {
1099
1100 boolean hasFlows = hasSubscriberFlows(port, uti);
1101 if (action == FlowOperation.ADD && hasFlows ||
1102 action == FlowOperation.REMOVE && !hasFlows) {
1103 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1104 uti.getServiceName(), hasFlows);
1105 return;
1106 }
1107
1108 if (multicastServiceName.equals(uti.getServiceName())) {
1109 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1110 "dataplane flows are not needed",
1111 uti.getServiceName(), si.id(), portWithName(port));
1112 return;
1113 }
1114
1115 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1116 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001117 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1118 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1119 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001120 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001121
1122 // upstream flows
1123 MeterId usMeterId = oltMeterService
1124 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1125 MeterId oltUsMeterId = oltMeterService
1126 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301127
1128 if (FttbUtils.isFttbService(uti)) {
1129 processFttbUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action,
1130 usMeterId, oltUsMeterId, uti, si);
1131 } else {
1132 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1133 oltUsMeterId, uti);
1134 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001135
1136 // downstream flows
1137 MeterId dsMeterId = oltMeterService
1138 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1139 MeterId oltDsMeterId = oltMeterService
1140 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301141
1142 if (FttbUtils.isFttbService(uti)) {
1143 processFttbDownstreamDataFilteringObjects(device.id(), port, nniPort.get(),
1144 action, dsMeterId, oltDsMeterId, uti, si);
1145 } else {
1146 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1147 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1148 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001149 });
1150 }
1151
1152 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1153 FlowOperation action, FlowDirection direction,
1154 int udpSrc, int udpDst, EthType ethType, byte protocol,
1155 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1156 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1157 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1158
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301159 String serviceName = uti.getServiceName();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001160
1161 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1162 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001163 updateConnectPointStatus(sk, null, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001164
1165 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1166 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1167
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001168 if (meterId != null) {
1169 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001170 }
1171
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001172 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001173 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001174 .addCondition(Criteria.matchEthType(ethType))
1175 .addCondition(Criteria.matchIPProtocol(protocol))
1176 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1177 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001178 .fromApp(appId)
1179 .withPriority(MAX_PRIORITY);
1180
Andrea Campanella0e34f562020-06-11 10:47:10 +02001181 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001182 if (direction == FlowDirection.UPSTREAM) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301183 if (serviceName != null && serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC)) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301184 FttbUtils.addUpstreamDhcpCondition(dhcpBuilder, uti);
1185 FttbUtils.addUpstreamDhcpTreatment(treatmentBuilder, uti);
1186 } else {
1187 treatmentBuilder.setVlanId(uti.getPonCTag());
1188 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1189 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
1190 }
1191 if (uti.getUsPonCTagPriority() != -1) {
1192 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
1193 }
Andrea Campanella0e34f562020-06-11 10:47:10 +02001194 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301195 } else if (direction == FlowDirection.DOWNSTREAM) {
1196 // Down stream DHCP vid to be matched if OLT Sadis info contains Vlan id in nniDhcpTrapVid.
1197 Device device = deviceService.getDevice(deviceId);
1198 SubscriberAndDeviceInformation oltSub = subsService.get(device.serialNumber());
1199 VlanId nniDhcpTrapVid = oltSub.nniDhcpTrapVid();
1200
1201 if (nniDhcpTrapVid != null && !VlanId.vlanId(VlanId.NO_VID).equals(nniDhcpTrapVid)) {
1202 dhcpBuilder.addCondition(Criteria.matchVlanId(nniDhcpTrapVid));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001203 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001204 }
1205
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301206 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301207 // 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 +05301208 treatmentBuilder.writeMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301209 OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301210 uti.getTechnologyProfileId(), oltMeterId), 0);
1211 }
1212
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001213 dhcpBuilder.withMeta(treatmentBuilder
1214 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001215
1216
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001217 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001218 @Override
1219 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001220 log.info("{} DHCP {} filter for {}.",
1221 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1222 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001223 }
1224
1225 @Override
1226 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001227 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001228 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1229 portWithName(port),
1230 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001231 error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001232 updateConnectPointStatus(sk, null, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001233 }
1234 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001235 flowObjectiveService.filter(deviceId, dhcpUpstream);
1236 }
1237
1238 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1239 FlowOperation action, FlowDirection direction,
1240 MeterId meterId, MeterId oltMeterId, int techProfileId,
1241 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1242
1243 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1244 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1245 if (direction == FlowDirection.UPSTREAM) {
1246
1247 if (techProfileId != NONE_TP_ID) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301248 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(null,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001249 techProfileId, oltMeterId), 0);
1250 }
1251
1252
1253 if (meterId != null) {
1254 treatmentBuilder.meter(meterId);
1255 }
1256
1257 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1258 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1259 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001260 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1261 treatmentBuilder.setVlanId(cTag);
1262 }
1263
1264 if (vlanPcp != -1) {
1265 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1266 }
1267 }
1268
1269 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1270
1271 FilteringObjective igmp = filterBuilder
1272 .withKey(Criteria.matchInPort(port.number()))
1273 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1274 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1275 .withMeta(treatmentBuilder
1276 .setOutput(PortNumber.CONTROLLER).build())
1277 .fromApp(appId)
1278 .withPriority(MAX_PRIORITY)
1279 .add(new ObjectiveContext() {
1280 @Override
1281 public void onSuccess(Objective objective) {
1282 log.info("Igmp filter for {} {}.", portWithName(port), action);
1283 }
1284
1285 @Override
1286 public void onError(Objective objective, ObjectiveError error) {
1287 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1288 error);
1289 }
1290 });
1291
1292 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001293
1294 }
1295
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001296 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1297 FlowOperation action, FlowDirection direction,
1298 MeterId meterId, MeterId oltMeterId, int techProfileId,
1299 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001300
1301 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1302 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001303
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001304 if (meterId != null) {
1305 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001306 }
1307
1308 if (techProfileId != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301309 // Setting VlanId.NONE as cvlan as the packet will be single tagged and cvlan should not be filled.
1310 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
1311 techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001312 }
1313
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001314 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1315 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001316 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001317 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1318 .fromApp(appId)
1319 .withPriority(10000);
1320
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001321 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001322 treatmentBuilder.setVlanId(cTag);
1323 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1324 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1325 }
1326 if (vlanPcp != null) {
1327 treatmentBuilder.setVlanPcp(vlanPcp);
1328 }
1329 }
1330 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1331
1332 FilteringObjective pppoed = pppoedBuilder
1333 .add(new ObjectiveContext() {
1334 @Override
1335 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001336 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001337 }
1338
1339 @Override
1340 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001341 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1342 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001343 }
1344 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001345 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001346 }
1347
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001348 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1349 FlowOperation action,
1350 MeterId upstreamMeterId,
1351 MeterId upstreamOltMeterId,
1352 UniTagInformation uti) {
1353 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001354 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001355 .matchInPort(port.number())
1356 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001357 .build();
1358
Andrea Campanella327c5722020-01-30 11:34:13 +01001359 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1360 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001361 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001362 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001363 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001364 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001365 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1366 treatmentBuilder.popVlan();
1367 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001368
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001369 if (uti.getUsPonCTagPriority() != -1) {
1370 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001371
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001372 }
1373
1374 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001375 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001376
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001377 if (uti.getUsPonSTagPriority() != -1) {
1378 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001379 }
1380
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001381 treatmentBuilder.setOutput(nniPort.number())
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301382 .writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001383 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001384
yasin saplib4b8ee12021-06-13 18:25:20 +00001385 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1386
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001387 if (upstreamMeterId != null) {
1388 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001389 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1390 }
1391 if (upstreamOltMeterId != null) {
1392 treatmentBuilder.meter(upstreamOltMeterId);
1393 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001394 }
1395
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001396 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1397 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001398 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001399
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301400 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1401 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001402 }
1403
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001404 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1405 FlowOperation action,
1406 MeterId downstreamMeterId,
1407 MeterId downstreamOltMeterId,
1408 UniTagInformation uti,
1409 MacAddress macAddress) {
1410 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001411 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001412 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001413 .matchVlanId(uti.getPonSTag())
1414 .matchInPort(nniPort.number())
1415 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001416
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001417 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1418 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001419 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001420
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001421 if (uti.getDsPonCTagPriority() != -1) {
1422 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001423 }
1424
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001425 if (macAddress != null) {
1426 selectorBuilder.matchEthDst(macAddress);
1427 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001428
1429 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1430 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001431 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001432
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301433 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001434 uti.getTechnologyProfileId(),
1435 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001436
Andrea Campanella981e86c2021-03-12 11:35:33 +01001437 // Upstream pbit is used to remark inner vlan pbit.
1438 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1439 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1440 // all pbit acceptance are not widely supported by vendors even though present in
1441 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001442 if (uti.getUsPonCTagPriority() != -1) {
1443 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001444 }
1445
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001446 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1447 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1448 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001449 }
1450
yasin saplib4b8ee12021-06-13 18:25:20 +00001451 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1452
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001453 if (downstreamMeterId != null) {
1454 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001455 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001456 }
1457
yasin saplib4b8ee12021-06-13 18:25:20 +00001458 if (downstreamOltMeterId != null) {
1459 treatmentBuilder.meter(downstreamOltMeterId);
1460 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1461 }
1462
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001463 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1464 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001465
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301466 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1467 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanella600d2e22020-06-22 11:00:31 +02001468 }
1469
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001470 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1471 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001472 Integer priority,
1473 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001474 return DefaultForwardingObjective.builder()
1475 .withFlag(ForwardingObjective.Flag.VERSATILE)
1476 .withPriority(priority)
1477 .makePermanent()
1478 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001479 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001480 .fromApp(appId)
1481 .withTreatment(treatment);
1482 }
1483
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001484 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1485 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1486 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001487
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001488 si.uniTagList().forEach(uniTagInfo -> {
1489 if (uniTagInfo.getEnableMacLearning()) {
1490 requiresMacLearning.set(true);
1491 }
1492 });
1493
1494 return requiresMacLearning.get();
1495 }
1496
1497 /**
1498 * Checks whether the subscriber has the MacAddress configured or discovered.
1499 *
1500 * @param deviceId DeviceId for this subscriber
1501 * @param port Port for this subscriber
1502 * @param si SubscriberAndDeviceInformation
1503 * @return boolean
1504 */
1505 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1506 AtomicBoolean isConfigured = new AtomicBoolean();
1507 isConfigured.set(true);
1508
1509 si.uniTagList().forEach(uniTagInfo -> {
1510 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301511 boolean configureMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001512 boolean discoveredMac = false;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301513
1514 final VlanId vlan;
1515
1516 if (FttbUtils.isFttbDpuOrAncpService(uniTagInfo)) {
1517 // Using S tag, as C tag is replaced by Stag by ONU.
1518 vlan = uniTagInfo.getPonSTag();
1519 } else {
1520 vlan = uniTagInfo.getPonCTag();
1521 }
1522
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001523 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301524 .stream().filter(host -> host.vlan().equals(vlan)).findFirst();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001525 if (optHost.isPresent() && optHost.get().mac() != null) {
1526 discoveredMac = true;
1527 }
1528 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1529 log.debug("Awaiting for macAddress on {} for service {}",
1530 portWithName(port), uniTagInfo.getServiceName());
1531 isConfigured.set(false);
1532 }
1533 });
1534
1535 return isConfigured.get();
1536 }
1537
1538 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301539 boolean configuredMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001540 if (configuredMac) {
1541 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1542 } else if (uniTagInfo.getEnableMacLearning()) {
1543 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1544 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1545 if (optHost.isPresent() && optHost.get().mac() != null) {
1546 return optHost.get().mac();
1547 }
1548 }
1549 return null;
1550 }
1551
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001552 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001553 OltFlowsStatus subscriberEapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001554 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1555 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001556 if (log.isTraceEnabled()) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001557 log.trace("Updating cpStatus {} with values: eapolFlow={}, " +
1558 "subscriberEapolStatus={}, subscriberFlows={}, dhcpFlow={}",
1559 key, eapolStatus, subscriberEapolStatus, subscriberFlowsStatus, dhcpStatus);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001560 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001561 try {
1562 cpStatusWriteLock.lock();
1563 OltPortStatus status = cpStatus.get(key);
1564
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001565
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001566 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001567 // if we don't have status for the connectPoint
1568 // and we're only updating status to PENDING_REMOVE or ERROR
1569 // do not create it. This is because this case will only happen when a device is removed
1570 // and it's status cleaned
1571 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1572 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1573 statusesToIgnore.add(OltFlowsStatus.ERROR);
1574
1575 if (
1576 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1577 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1578 ) {
1579 if (log.isTraceEnabled()) {
1580 log.trace("Ignoring cpStatus update as status is meaningless");
1581 }
1582 return;
1583 }
1584
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001585 status = new OltPortStatus(
1586 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001587 subscriberEapolStatus != null ? subscriberEapolStatus : OltFlowsStatus.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001588 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001589 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1590 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001591 );
1592 } else {
1593 if (eapolStatus != null) {
1594 status.defaultEapolStatus = eapolStatus;
1595 }
1596 if (subscriberFlowsStatus != null) {
1597 status.subscriberFlowsStatus = subscriberFlowsStatus;
1598 }
1599 if (dhcpStatus != null) {
1600 status.dhcpStatus = dhcpStatus;
1601 }
1602 }
1603
1604 cpStatus.put(key, status);
1605 } finally {
1606 cpStatusWriteLock.unlock();
1607 }
1608 }
1609
1610 protected class InternalFlowListener implements FlowRuleListener {
1611 @Override
1612 public void event(FlowRuleEvent event) {
1613 if (appId.id() != (event.subject().appId())) {
1614 return;
1615 }
1616
1617 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1618 if (log.isTraceEnabled()) {
1619 log.trace("ignoring flow event {} " +
1620 "as not leader for {}", event, event.subject().deviceId());
1621 }
1622 return;
1623 }
1624
1625 switch (event.type()) {
1626 case RULE_ADDED:
1627 case RULE_REMOVED:
Andrea Campanella40d2b342022-02-04 18:13:37 +01001628 DeviceId deviceId = event.subject().deviceId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001629 Port port = getCpFromFlowRule(event.subject());
1630 if (port == null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001631 log.warn("Port is gone in ONOS, " +
1632 "manually creating it {}", event.subject());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301633 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(event.subject());
Andrea Campanella40d2b342022-02-04 18:13:37 +01001634 cpStatusReadLock.lock();
1635 Optional<ServiceKey> keyWithPort = cpStatus.keySet()
1636 .stream().filter(key -> key.getPort().connectPoint()
1637 .deviceId().equals(deviceId)
1638 && key.getPort().connectPoint().port()
1639 .equals(inPort)).findFirst();
1640 cpStatusReadLock.unlock();
1641 if (keyWithPort.isPresent()) {
1642 port = new DefaultPort(deviceService.getDevice(deviceId),
1643 inPort, false,
1644 DefaultAnnotations.builder()
1645 .set(AnnotationKeys.PORT_NAME,
1646 keyWithPort.get().getPort().name())
1647 .build());
1648 } else {
1649 log.warn("Can't find corresponding status for {}/{}", deviceId, inPort);
1650 return;
1651 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001652 }
1653 if (log.isTraceEnabled()) {
1654 log.trace("flow event {} on cp {}: {}", event.type(),
1655 portWithName(port), event.subject());
1656 }
1657 updateCpStatus(event.type(), port, event.subject());
1658 return;
1659 case RULE_ADD_REQUESTED:
1660 case RULE_REMOVE_REQUESTED:
1661 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1662 return;
1663 default:
1664 return;
1665 }
1666 }
1667
1668 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301669 OltFlowsStatus status = OltFlowServiceUtils.flowRuleStatusToOltFlowStatus(type);
1670 if (OltFlowServiceUtils.isDefaultEapolFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001671 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1672 defaultEapolUniTag);
1673 if (log.isTraceEnabled()) {
1674 log.trace("update defaultEapolStatus {} on {}", status, sk);
1675 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001676 updateConnectPointStatus(sk, status, null, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301677 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001678 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
1679 if (sk == null) {
1680 return;
1681 }
1682 if (log.isTraceEnabled()) {
1683 log.trace("update subscriberEapolStatus {} on {}", status, sk);
1684 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +02001685 updateConnectPointStatus(sk, null, status, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301686 } else if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001687 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001688 if (sk == null) {
1689 return;
1690 }
1691 if (log.isTraceEnabled()) {
1692 log.trace("update dhcpStatus {} on {}", status, sk);
1693 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001694 updateConnectPointStatus(sk, null, null, null, status, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301695 } else if (OltFlowServiceUtils.isPppoeFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001696 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
yasin sapli0823c932022-01-26 11:26:09 +00001697 if (sk == null) {
1698 return;
1699 }
1700 if (log.isTraceEnabled()) {
1701 log.trace("update pppoeStatus {} on {}", status, sk);
1702 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001703 updateConnectPointStatus(sk, null, null, null, null, status);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301704 } else if (OltFlowServiceUtils.isDataFlow(flowRule)) {
1705 PortNumber number = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Andrea Campanella40d2b342022-02-04 18:13:37 +01001706 if (number == null) {
1707 log.error("Can't capture the port number from flow {}", flowRule);
1708 return;
1709 }
1710 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), number)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001711 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1712 return;
1713 }
1714
Andrea Campanella40d2b342022-02-04 18:13:37 +01001715 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001716 if (sk == null) {
1717 return;
1718 }
1719 if (log.isTraceEnabled()) {
1720 log.trace("update dataplaneStatus {} on {}", status, sk);
1721 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001722 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001723 }
1724 }
1725
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001726
Andrea Campanella87241ae2022-03-11 11:20:24 +01001727
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001728 private Port getCpFromFlowRule(FlowRule flowRule) {
1729 DeviceId deviceId = flowRule.deviceId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301730 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001731 if (inPort != null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001732 return deviceService.getPort(deviceId, inPort);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001733 }
1734 return null;
1735 }
1736
Andrea Campanella40d2b342022-02-04 18:13:37 +01001737 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule, Port flowPort) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001738 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1739
1740 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1741 if (si == null && !isNni) {
1742 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1743 return null;
1744 }
1745
1746 if (isNni) {
1747 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1748 }
1749
1750 Optional<UniTagInformation> found = Optional.empty();
1751 VlanId flowVlan = null;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301752 if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001753 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1754 L2ModificationInstruction.ModVlanIdInstruction instruction =
1755 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1756 flowVlan = instruction.vlanId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301757 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001758 // we need to make a special case for EAPOL as in the ATT workflow EAPOL flows don't match on tags
1759 L2ModificationInstruction.ModVlanIdInstruction instruction =
1760 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(2);
1761 flowVlan = instruction.vlanId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001762 } else {
1763 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1764 VlanIdCriterion vlanIdCriterion =
1765 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1766 if (vlanIdCriterion == null) {
1767 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1768 return null;
1769 }
1770 flowVlan = vlanIdCriterion.vlanId();
1771 }
1772
1773 VlanId finalFlowVlan = flowVlan;
1774 found = si.uniTagList().stream().filter(uti ->
1775 uti.getPonCTag().equals(finalFlowVlan) ||
1776 uti.getPonSTag().equals(finalFlowVlan) ||
1777 uti.getUniTagMatch().equals(finalFlowVlan)
1778 ).findFirst();
1779
1780
1781 if (found.isEmpty()) {
1782 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1783 }
1784
1785 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1786
1787 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001788 }
1789
1790 protected void bindSadisService(SadisService service) {
1791 this.subsService = service.getSubscriberInfoService();
1792 this.bpService = service.getBandwidthProfileService();
1793 log.info("Sadis service is loaded");
1794 }
1795
1796 protected void unbindSadisService(SadisService service) {
1797 this.subsService = null;
1798 this.bpService = null;
1799 log.info("Sadis service is unloaded");
1800 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301801
1802 private void processFttbUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1803 FlowOperation action,
1804 MeterId upstreamMeterId,
1805 MeterId upstreamOltMeterId,
1806 UniTagInformation uti,
1807 SubscriberAndDeviceInformation si) {
1808 String serviceName = uti.getServiceName();
1809 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1810 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1811 .matchInPort(port.number())
1812 .matchVlanId(uti.getPonCTag());
1813
1814 if (uti.getUsPonCTagPriority() != -1) {
1815 selectorBuilder.matchVlanPcp((byte) uti.getUsPonCTagPriority());
1816 }
1817
1818 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1819
1820 treatmentBuilder.setVlanId(uti.getPonSTag());
1821 if (uti.getUsPonSTagPriority() != -1) {
1822 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
1823 }
1824
1825 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1826 annotationBuilder.set(FTTB_SERVICE_NAME, serviceName);
1827 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_UPSTREAM);
1828
1829 if (upstreamMeterId != null) {
1830 treatmentBuilder.meter(upstreamMeterId);
1831 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1832 }
1833 if (upstreamOltMeterId != null) {
1834 treatmentBuilder.meter(upstreamOltMeterId);
1835 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
1836 }
1837
1838 VlanId innerVlan = null;
1839
1840 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1841 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1842 hostService, si, deviceId, port);
1843
1844 if (mac == null) {
1845 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1846 port, uti.getPonSTag(), serviceName);
1847 return;
1848 }
1849
1850 selectorBuilder.matchEthSrc(mac);
1851 innerVlan = VlanId.NONE;
1852
1853 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
1854 innerVlan = VlanId.ANY;
1855 }
1856
1857 treatmentBuilder.setOutput(nniPort.number()).writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1858 uti.getTechnologyProfileId(), nniPort.number()), 0L);
1859
1860 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1861 treatmentBuilder.build(), MIN_PRIORITY,
1862 annotationBuilder.build());
1863
1864 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1865 processForwardingRule(action, flowBuilder, context, deviceId);
1866 }
1867
1868 private void processFttbDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1869 FlowOperation action,
1870 MeterId downstreamMeterId,
1871 MeterId downstreamOltMeterId,
1872 UniTagInformation uti, SubscriberAndDeviceInformation si) {
1873 String serviceName = uti.getServiceName();
1874 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1875 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
1876 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1877 .matchVlanId(uti.getPonSTag())
1878 .matchInPort(nniPort.number());
1879
1880 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1881 .setVlanId(uti.getPonCTag())
1882 .setOutput(port.number());
1883
1884 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1885 annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
1886 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_DOWNSTREAM);
1887
1888 if (downstreamMeterId != null) {
1889 treatmentBuilder.meter(downstreamMeterId);
1890 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
1891 }
1892
1893 if (downstreamOltMeterId != null) {
1894 treatmentBuilder.meter(downstreamOltMeterId);
1895 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1896 }
1897
1898 VlanId innerVlan = null;
1899
1900 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1901 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1902 hostService, si, deviceId, port);
1903
1904 if (mac == null) {
1905 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1906 port, uti.getPonSTag(), serviceName);
1907 return;
1908 }
1909
1910 selectorBuilder.matchEthDst(mac);
1911 innerVlan = VlanId.NONE;
1912
1913 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
1914 innerVlan = VlanId.ANY;
1915 selectorBuilder.matchMetadata(uti.getPonSTag().toShort());
1916 }
1917
1918 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1919 uti.getTechnologyProfileId(),
1920 port.number()), 0);
1921
1922 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1923 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
1924
1925 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1926 processForwardingRule(action, flowBuilder, context, deviceId);
1927 }
1928
1929 private ObjectiveContext getSubscriberFlowBuilderContext(ServiceKey sk, FlowOperation action,
1930 FlowDirection flowDirection) {
1931 ObjectiveContext context = new ObjectiveContext() {
1932 @Override
1933 public void onSuccess(Objective objective) {
1934 log.info("{} {} Data plane filter for {}.",
1935 completeFlowOpToString(action), flowDirection, sk);
1936 }
1937
1938 @Override
1939 public void onError(Objective objective, ObjectiveError error) {
1940 log.info("{} Data plane filter for {} failed {} because {}.",
1941 flowDirection, sk, action, error);
1942 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
1943 }
1944 };
1945
1946 return context;
1947 }
1948
1949 private void processForwardingRule(FlowOperation action, DefaultForwardingObjective.Builder flowBuilder,
1950 ObjectiveContext context, DeviceId deviceId) {
1951 ForwardingObjective flow = null;
1952 if (action == FlowOperation.ADD) {
1953 flow = flowBuilder.add(context);
1954 } else if (action == FlowOperation.REMOVE) {
1955 flow = flowBuilder.remove(context);
1956 } else {
1957 log.error("Flow action not supported: {}", action);
1958 }
1959
1960 if (flow != null) {
1961 if (log.isTraceEnabled()) {
1962 log.trace("Forwarding rule {}", flow);
1963 }
1964 flowObjectiveService.forward(deviceId, flow);
1965 }
1966 }
1967}