blob: 10be314dad0d89eaf7c20ecc259fb09900ca6d40 [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);
528 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
529 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
530 } else {
531 log.error("don't know how to handle {}", sub);
532 return false;
533 }
534 }
535
536 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
537 String multicastServiceName) {
538 if (log.isTraceEnabled()) {
539 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
540 }
541 if (enableEapol) {
542 if (hasDefaultEapol(sub.port)) {
543 // remove EAPOL flow and throw exception so that we'll retry later
544 if (!isDefaultEapolPendingRemoval(sub.port)) {
545 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
546 }
547
548 if (waitForRemoval) {
549 // NOTE wait for removal is a flag only needed to make sure VOLTHA
550 // does not explode with the flows remove/add in the same batch
551 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
552 return false;
553 } else {
554 log.warn("continuing provisioning on {}", portWithName(sub.port));
555 }
556 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700557 }
558
559 // NOTE createMeters will return if the meters are not installed
560 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800561 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700562 return false;
563 }
564
565 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
566 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
567 sub.subscriberAndDeviceInformation);
568
569 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
570 && !isMacAddressAvailable(sub.device.id(), sub.port,
571 sub.subscriberAndDeviceInformation)) {
572 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
573 return false;
574 }
575
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800576 // NOTE that the EAPOL flows handling is based on the data-plane flows status
577 // always process them before
578 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
579
yasin sapli0823c932022-01-26 11:26:09 +0000580 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
581
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700582 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
583 sub.subscriberAndDeviceInformation, multicastServiceName);
584
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700585 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
586
587 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
588 return true;
589 }
590
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800591 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700592 String multicastServiceName) {
593
594 if (log.isTraceEnabled()) {
595 log.trace("Removal of subscriber on {} started",
596 portWithName(sub.port));
597 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800598 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700599
600 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
601
yasin sapli0823c932022-01-26 11:26:09 +0000602 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, sub.subscriberAndDeviceInformation);
603
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700604 if (enableEapol) {
605 // remove the tagged eapol
606 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800607 }
608 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700609
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800610 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
611
612 if (enableEapol) {
613
614 // if any of the services still has flows, return false
615 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
616 while (iter.hasNext()) {
617 UniTagInformation entry = iter.next();
Andrea Campanella87241ae2022-03-11 11:20:24 +0100618 if (areSubscriberFlowsPendingRemoval(sub.port, entry, enableEapol)) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800619 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
620 portWithName(sub.port), entry.getServiceName());
621 return false;
622 }
623 }
624
625 // once the flows are removed add the default one back
626 // (only if the port is ENABLED and still present on the device)
Matteo Scandolo49c42052021-11-23 13:12:29 -0800627 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
628
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700629 // NOTE we remove the subscriber when the port goes down
630 // but in that case we don't need to add default eapol
631 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
632 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
633 }
634 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700635 // FIXME check the return status of the flow and return accordingly
636 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
637 return true;
638 }
639
640 @Override
641 public boolean hasDefaultEapol(Port port) {
642 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
643 // NOTE we consider ERROR as a present EAPOL flow as ONOS
644 // will keep trying to add it
645 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
646 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
647 status.defaultEapolStatus == OltFlowsStatus.ERROR);
648 }
649
650 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
651 try {
652 cpStatusReadLock.lock();
653 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
654 OltPortStatus status = cpStatus.get(sk);
655 return status;
656 } finally {
657 cpStatusReadLock.unlock();
658 }
659 }
660
661 public boolean isDefaultEapolPendingRemoval(Port port) {
662 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
663 if (log.isTraceEnabled()) {
664 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
665 status, portWithName(port), defaultEapolUniTag);
666 }
667 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
668 }
669
670 @Override
671 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
672 OltPortStatus status = getOltPortStatus(port, uti);
673 if (log.isTraceEnabled()) {
674 log.trace("Status during DHCP flow check {} for port {} and service {}",
675 status, portWithName(port), uti.getServiceName());
676 }
677 return status != null &&
678 (status.dhcpStatus == OltFlowsStatus.ADDED ||
679 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
680 }
681
682 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000683 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
684 OltPortStatus status = getOltPortStatus(port, uti);
685 if (log.isTraceEnabled()) {
686 log.trace("Status during PPPoE flow check {} for port {} and service {}",
687 status, portWithName(port), uti.getServiceName());
688 }
689 return status != null &&
690 (status.pppoeStatus == OltFlowsStatus.ADDED ||
691 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
692 }
693
694 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700695 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
696
697 OltPortStatus status = getOltPortStatus(port, uti);
698 if (log.isTraceEnabled()) {
699 log.trace("Status during subscriber flow check {} for port {} and service {}",
700 status, portWithName(port), uti.getServiceName());
701 }
702 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
703 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
704 }
705
Andrea Campanella87241ae2022-03-11 11:20:24 +0100706 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti, boolean enableEapol) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800707 OltPortStatus status = getOltPortStatus(port, uti);
708 if (log.isTraceEnabled()) {
709 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
710 status, portWithName(port), uti);
711 }
Andrea Campanella87241ae2022-03-11 11:20:24 +0100712 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE ||
713 (enableEapol && status.subscriberEapolStatus == OltFlowsStatus.PENDING_REMOVE) ||
714 (uti.getIsDhcpRequired() && status.dhcpStatus == OltFlowsStatus.PENDING_REMOVE));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800715 }
716
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700717 @Override
718 public void purgeDeviceFlows(DeviceId deviceId) {
719 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800720 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700721
722 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800723 if (log.isTraceEnabled()) {
724 log.trace("Clearing cp status from device {}", deviceId);
725 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700726 try {
727 cpStatusWriteLock.lock();
728 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
729 while (iter.hasNext()) {
730 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
731 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
732 cpStatus.remove(entry.getKey());
733 }
734 }
735 } finally {
736 cpStatusWriteLock.unlock();
737 }
738
739 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800740 if (log.isTraceEnabled()) {
741 log.trace("Clearing provisioned subscribers from device {}", deviceId);
742 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700743 try {
744 provisionedSubscribersWriteLock.lock();
745 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
746 while (iter.hasNext()) {
747 Map.Entry<ServiceKey, Boolean> entry = iter.next();
748 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
749 provisionedSubscribers.remove(entry.getKey());
750 }
751 }
752 } finally {
753 provisionedSubscribersWriteLock.unlock();
754 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800755 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700756 }
757
758 @Override
759 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800760 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700761 try {
762 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800763 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700764 } finally {
765 provisionedSubscribersReadLock.unlock();
766 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800767
768 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
769 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
770 return true;
771 }
772 }
773 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700774 }
775
776 @Override
777 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
778 try {
779 provisionedSubscribersReadLock.lock();
780 Boolean provisioned = provisionedSubscribers.get(sk);
781 if (provisioned == null || !provisioned) {
782 return false;
783 }
784 } finally {
785 provisionedSubscribersReadLock.unlock();
786 }
787 return true;
788 }
789
790 @Override
791 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
792 try {
793 provisionedSubscribersWriteLock.lock();
794 provisionedSubscribers.put(sk, status);
795 } finally {
796 provisionedSubscribersWriteLock.unlock();
797 }
798 }
799
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800800 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700801 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
802
803 // create a subscriberKey for the EAPOL flow
804 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
Andrea Campanella87241ae2022-03-11 11:20:24 +0100805 OltFlowsStatus status = action == FlowOperation.ADD ?
806 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700807 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +0100808 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE,
809 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700810
Andrea Campanella87241ae2022-03-11 11:20:24 +0100811 } else {
812 updateConnectPointStatus(sk, OltFlowsStatus.NONE, status, OltFlowsStatus.NONE,
813 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700814 }
815
816 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
817 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
818
819 int techProfileId = getDefaultTechProfileId(sub.port);
820 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
821
822 // in the delete case the meter should still be there as we remove
823 // the meters only if no flows are pointing to them
824 if (meterId == null) {
825 log.debug("MeterId is null for BandwidthProfile {} on device {}",
826 bandwidthProfile, sub.device.id());
827 return false;
828 }
829
830 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
831 if (oltMeterId == null) {
832 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
833 oltBandwidthProfile, sub.device.id());
834 return false;
835 }
836
837 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
838 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
839
840 FilteringObjective.Builder eapolAction;
841
842 if (action == FlowOperation.ADD) {
843 eapolAction = filterBuilder.permit();
844 } else if (action == FlowOperation.REMOVE) {
845 eapolAction = filterBuilder.deny();
846 } else {
847 log.error("Operation {} not supported", action);
848 return false;
849 }
850
851 FilteringObjective.Builder baseEapol = eapolAction
852 .withKey(Criteria.matchInPort(sub.port.number()))
853 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
854
855 // NOTE we only need to add the treatment to install the flow,
856 // we can remove it based in the match
857 FilteringObjective.Builder eapol;
858
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530859 // Setting VlanId.NONE as cvlan in the metadata as the packet will be single tagged
860 // and cvlan should not be filled.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700861 TrafficTreatment treatment = treatmentBuilder
862 .meter(meterId)
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530863 .writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530864 VlanId.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700865 techProfileId, oltMeterId), 0)
866 .setOutput(PortNumber.CONTROLLER)
867 .pushVlan()
868 .setVlanId(vlanId)
869 .build();
870 eapol = baseEapol
871 .withMeta(treatment);
872
873 FilteringObjective eapolObjective = eapol
874 .fromApp(appId)
875 .withPriority(MAX_PRIORITY)
876 .add(new ObjectiveContext() {
877 @Override
878 public void onSuccess(Objective objective) {
879 log.info("EAPOL flow objective {} for {}",
880 completeFlowOpToString(action), portWithName(sub.port));
881 if (log.isTraceEnabled()) {
882 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
883 }
884 }
885
886 @Override
887 public void onError(Objective objective, ObjectiveError error) {
888 log.error("Cannot {} eapol flow for {} : {}", action,
889 portWithName(sub.port), error);
890
891 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
892 updateConnectPointStatus(sk,
Andrea Campanella87241ae2022-03-11 11:20:24 +0100893 OltFlowsStatus.ERROR, null, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700894 }
895 }
896 });
897
898 flowObjectiveService.filter(sub.device.id(), eapolObjective);
899
900 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
901 return true;
902 }
903
904 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800905 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700906 SubscriberAndDeviceInformation si) {
907 if (!enableEapol) {
908 return true;
909 }
910 // TODO verify we need an EAPOL flow for EACH service
911 AtomicBoolean success = new AtomicBoolean(true);
912 si.uniTagList().forEach(u -> {
913 // we assume that if subscriber flows are installed, tagged EAPOL is installed as well
914 boolean hasFlows = hasSubscriberFlows(sub.port, u);
915
916 // if the subscriber flows are present the EAPOL flow is present thus we don't need to install it,
917 // if the subscriber does not have flows the EAPOL flow is not present thus we don't need to remove it
918 if (action == FlowOperation.ADD && hasFlows ||
919 action == FlowOperation.REMOVE && !hasFlows) {
920 log.debug("Not {} EAPOL on {}({}) as subscriber flow status is {}", flowOpToString(action),
921 portWithName(sub.port), u.getServiceName(), hasFlows);
922 return;
923 }
924 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
925 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
926 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
927 u.getUpstreamOltBandwidthProfile(),
928 action, u.getPonCTag())) {
929 //
Andrea Campanella87241ae2022-03-11 11:20:24 +0100930 log.error("Failed to {} EAPOL with subscriber tags", action);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700931 //TODO this sets it for all services, maybe some services succeeded.
932 success.set(false);
933 }
934 });
935 return success.get();
936 }
937
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800938 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700939 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
940 if (uti.getIsIgmpRequired()) {
941 DeviceId deviceId = sub.device.id();
942 // if we reached here a meter already exists
943 MeterId meterId = oltMeterService
944 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
945 MeterId oltMeterId = oltMeterService
946 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
947
948 processIgmpFilteringObjectives(deviceId, sub.port,
949 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
950 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
951 }
952 });
953 }
954
955 private boolean checkSadisRunning() {
956 if (bpService == null) {
957 log.warn("Sadis is not running");
958 return false;
959 }
960 return true;
961 }
962
963 private int getDefaultTechProfileId(Port port) {
964 if (!checkSadisRunning()) {
965 return defaultTechProfileId;
966 }
967 if (port != null) {
968 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
969 if (info != null && info.uniTagList().size() == 1) {
970 return info.uniTagList().get(0).getTechnologyProfileId();
971 }
972 }
973 return defaultTechProfileId;
974 }
975
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700976 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
977 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
978
979 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
980 .withKey(Criteria.matchInPort(port.number()))
981 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
982 .withMeta(DefaultTrafficTreatment.builder()
983 .setOutput(PortNumber.CONTROLLER).build())
984 .fromApp(appId)
985 .withPriority(MAX_PRIORITY)
986 .add(new ObjectiveContext() {
987 @Override
988 public void onSuccess(Objective objective) {
989 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
990 }
991
992 @Override
993 public void onError(Objective objective, ObjectiveError error) {
994 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
995 error);
996 }
997 });
998
999 flowObjectiveService.filter(deviceId, lldp);
1000 }
1001
1002 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
1003 FlowOperation action,
1004 SubscriberAndDeviceInformation si) {
1005 si.uniTagList().forEach(uti -> {
1006
1007 if (!uti.getIsDhcpRequired()) {
1008 return;
1009 }
1010
1011 // if it's an ADD skip if flows are there,
1012 // if it's a DELETE skip if flows are not there
1013 boolean hasFlows = hasDhcpFlows(port, uti);
1014 if (action == FlowOperation.ADD && hasFlows ||
1015 action == FlowOperation.REMOVE && !hasFlows) {
1016 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1017 uti.getServiceName(), hasFlows);
1018 return;
1019 }
1020
1021 log.info("{} DHCP flows for subscriber on {} and service {}",
1022 flowOpToString(action), portWithName(port), uti.getServiceName());
1023
1024 // if we reached here a meter already exists
1025 MeterId meterId = oltMeterService
1026 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1027 MeterId oltMeterId = oltMeterService
1028 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1029
1030 if (enableDhcpV4) {
1031 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1032 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1033 uti);
1034 }
1035 if (enableDhcpV6) {
1036 log.error("DHCP V6 not supported for subscribers");
1037 }
1038 });
1039 }
1040
yasin sapli0823c932022-01-26 11:26:09 +00001041 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1042 FlowOperation action,
1043 SubscriberAndDeviceInformation si) {
1044 si.uniTagList().forEach(uti -> {
1045
1046 if (!uti.getIsPppoeRequired()) {
1047 return;
1048 }
1049
1050 // if it's an ADD skip if flows are there,
1051 // if it's a DELETE skip if flows are not there
1052 boolean hasFlows = hasPppoeFlows(port, uti);
1053 if (action == FlowOperation.ADD && hasFlows ||
1054 action == FlowOperation.REMOVE && !hasFlows) {
1055 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1056 uti.getServiceName(), hasFlows);
1057 return;
1058 }
1059
1060 log.info("{} PPPoE flows for subscriber on {} and service {}",
1061 flowOpToString(action), portWithName(port), uti.getServiceName());
1062
1063 // if we reached here a meter already exists
1064 MeterId meterId = oltMeterService
1065 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1066 MeterId oltMeterId = oltMeterService
1067 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1068
1069 if (enablePppoe) {
1070 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1071 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1072 (byte) uti.getUsPonCTagPriority());
1073 }
1074 });
1075 }
1076
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001077 // FIXME return boolean, if this fails we need to retry
1078 protected void handleSubscriberDataFlows(Device device, Port port,
1079 FlowOperation action,
1080 SubscriberAndDeviceInformation si, String multicastServiceName) {
1081
1082 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001083 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001084 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1085 si.id(), portWithName(port));
1086 return;
1087 }
1088 si.uniTagList().forEach(uti -> {
1089
1090 boolean hasFlows = hasSubscriberFlows(port, uti);
1091 if (action == FlowOperation.ADD && hasFlows ||
1092 action == FlowOperation.REMOVE && !hasFlows) {
1093 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1094 uti.getServiceName(), hasFlows);
1095 return;
1096 }
1097
1098 if (multicastServiceName.equals(uti.getServiceName())) {
1099 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1100 "dataplane flows are not needed",
1101 uti.getServiceName(), si.id(), portWithName(port));
1102 return;
1103 }
1104
1105 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1106 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001107 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1108 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1109 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001110 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001111
1112 // upstream flows
1113 MeterId usMeterId = oltMeterService
1114 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1115 MeterId oltUsMeterId = oltMeterService
1116 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301117
1118 if (FttbUtils.isFttbService(uti)) {
1119 processFttbUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action,
1120 usMeterId, oltUsMeterId, uti, si);
1121 } else {
1122 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1123 oltUsMeterId, uti);
1124 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001125
1126 // downstream flows
1127 MeterId dsMeterId = oltMeterService
1128 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1129 MeterId oltDsMeterId = oltMeterService
1130 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301131
1132 if (FttbUtils.isFttbService(uti)) {
1133 processFttbDownstreamDataFilteringObjects(device.id(), port, nniPort.get(),
1134 action, dsMeterId, oltDsMeterId, uti, si);
1135 } else {
1136 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1137 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1138 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001139 });
1140 }
1141
1142 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1143 FlowOperation action, FlowDirection direction,
1144 int udpSrc, int udpDst, EthType ethType, byte protocol,
1145 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1146 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1147 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1148
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301149 String serviceName = uti.getServiceName();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001150
1151 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1152 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001153 updateConnectPointStatus(sk, null, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001154
1155 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1156 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1157
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001158 if (meterId != null) {
1159 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001160 }
1161
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001162 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001163 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001164 .addCondition(Criteria.matchEthType(ethType))
1165 .addCondition(Criteria.matchIPProtocol(protocol))
1166 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1167 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001168 .fromApp(appId)
1169 .withPriority(MAX_PRIORITY);
1170
Andrea Campanella0e34f562020-06-11 10:47:10 +02001171 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001172 if (direction == FlowDirection.UPSTREAM) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301173 if (serviceName != null && serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC)) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301174 FttbUtils.addUpstreamDhcpCondition(dhcpBuilder, uti);
1175 FttbUtils.addUpstreamDhcpTreatment(treatmentBuilder, uti);
1176 } else {
1177 treatmentBuilder.setVlanId(uti.getPonCTag());
1178 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1179 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
1180 }
1181 if (uti.getUsPonCTagPriority() != -1) {
1182 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
1183 }
Andrea Campanella0e34f562020-06-11 10:47:10 +02001184 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301185 } else if (direction == FlowDirection.DOWNSTREAM) {
1186 // Down stream DHCP vid to be matched if OLT Sadis info contains Vlan id in nniDhcpTrapVid.
1187 Device device = deviceService.getDevice(deviceId);
1188 SubscriberAndDeviceInformation oltSub = subsService.get(device.serialNumber());
1189 VlanId nniDhcpTrapVid = oltSub.nniDhcpTrapVid();
1190
1191 if (nniDhcpTrapVid != null && !VlanId.vlanId(VlanId.NO_VID).equals(nniDhcpTrapVid)) {
1192 dhcpBuilder.addCondition(Criteria.matchVlanId(nniDhcpTrapVid));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001193 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001194 }
1195
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301196 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301197 // 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 +05301198 treatmentBuilder.writeMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301199 OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301200 uti.getTechnologyProfileId(), oltMeterId), 0);
1201 }
1202
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001203 dhcpBuilder.withMeta(treatmentBuilder
1204 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001205
1206
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001207 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001208 @Override
1209 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001210 log.info("{} DHCP {} filter for {}.",
1211 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1212 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001213 }
1214
1215 @Override
1216 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001217 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001218 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1219 portWithName(port),
1220 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001221 error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001222 updateConnectPointStatus(sk, null, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001223 }
1224 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001225 flowObjectiveService.filter(deviceId, dhcpUpstream);
1226 }
1227
1228 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1229 FlowOperation action, FlowDirection direction,
1230 MeterId meterId, MeterId oltMeterId, int techProfileId,
1231 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1232
1233 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1234 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1235 if (direction == FlowDirection.UPSTREAM) {
1236
1237 if (techProfileId != NONE_TP_ID) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301238 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(null,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001239 techProfileId, oltMeterId), 0);
1240 }
1241
1242
1243 if (meterId != null) {
1244 treatmentBuilder.meter(meterId);
1245 }
1246
1247 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1248 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1249 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001250 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1251 treatmentBuilder.setVlanId(cTag);
1252 }
1253
1254 if (vlanPcp != -1) {
1255 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1256 }
1257 }
1258
1259 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1260
1261 FilteringObjective igmp = filterBuilder
1262 .withKey(Criteria.matchInPort(port.number()))
1263 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1264 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1265 .withMeta(treatmentBuilder
1266 .setOutput(PortNumber.CONTROLLER).build())
1267 .fromApp(appId)
1268 .withPriority(MAX_PRIORITY)
1269 .add(new ObjectiveContext() {
1270 @Override
1271 public void onSuccess(Objective objective) {
1272 log.info("Igmp filter for {} {}.", portWithName(port), action);
1273 }
1274
1275 @Override
1276 public void onError(Objective objective, ObjectiveError error) {
1277 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1278 error);
1279 }
1280 });
1281
1282 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001283
1284 }
1285
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001286 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1287 FlowOperation action, FlowDirection direction,
1288 MeterId meterId, MeterId oltMeterId, int techProfileId,
1289 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001290
1291 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1292 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001293
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001294 if (meterId != null) {
1295 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001296 }
1297
1298 if (techProfileId != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301299 // Setting VlanId.NONE as cvlan as the packet will be single tagged and cvlan should not be filled.
1300 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
1301 techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001302 }
1303
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001304 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1305 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001306 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001307 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1308 .fromApp(appId)
1309 .withPriority(10000);
1310
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001311 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001312 treatmentBuilder.setVlanId(cTag);
1313 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1314 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1315 }
1316 if (vlanPcp != null) {
1317 treatmentBuilder.setVlanPcp(vlanPcp);
1318 }
1319 }
1320 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1321
1322 FilteringObjective pppoed = pppoedBuilder
1323 .add(new ObjectiveContext() {
1324 @Override
1325 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001326 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001327 }
1328
1329 @Override
1330 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001331 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1332 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001333 }
1334 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001335 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001336 }
1337
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001338 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1339 FlowOperation action,
1340 MeterId upstreamMeterId,
1341 MeterId upstreamOltMeterId,
1342 UniTagInformation uti) {
1343 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001344 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001345 .matchInPort(port.number())
1346 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001347 .build();
1348
Andrea Campanella327c5722020-01-30 11:34:13 +01001349 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1350 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001351 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001352 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001353 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001354 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001355 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1356 treatmentBuilder.popVlan();
1357 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001358
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001359 if (uti.getUsPonCTagPriority() != -1) {
1360 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001361
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001362 }
1363
1364 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001365 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001366
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001367 if (uti.getUsPonSTagPriority() != -1) {
1368 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001369 }
1370
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001371 treatmentBuilder.setOutput(nniPort.number())
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301372 .writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001373 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001374
yasin saplib4b8ee12021-06-13 18:25:20 +00001375 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1376
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001377 if (upstreamMeterId != null) {
1378 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001379 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1380 }
1381 if (upstreamOltMeterId != null) {
1382 treatmentBuilder.meter(upstreamOltMeterId);
1383 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001384 }
1385
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001386 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1387 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001388 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001389
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301390 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1391 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001392 }
1393
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001394 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1395 FlowOperation action,
1396 MeterId downstreamMeterId,
1397 MeterId downstreamOltMeterId,
1398 UniTagInformation uti,
1399 MacAddress macAddress) {
1400 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001401 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001402 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001403 .matchVlanId(uti.getPonSTag())
1404 .matchInPort(nniPort.number())
1405 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001406
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001407 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1408 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001409 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001410
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001411 if (uti.getDsPonCTagPriority() != -1) {
1412 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001413 }
1414
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001415 if (macAddress != null) {
1416 selectorBuilder.matchEthDst(macAddress);
1417 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001418
1419 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1420 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001421 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001422
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301423 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001424 uti.getTechnologyProfileId(),
1425 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001426
Andrea Campanella981e86c2021-03-12 11:35:33 +01001427 // Upstream pbit is used to remark inner vlan pbit.
1428 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1429 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1430 // all pbit acceptance are not widely supported by vendors even though present in
1431 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001432 if (uti.getUsPonCTagPriority() != -1) {
1433 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001434 }
1435
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001436 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1437 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1438 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001439 }
1440
yasin saplib4b8ee12021-06-13 18:25:20 +00001441 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1442
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001443 if (downstreamMeterId != null) {
1444 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001445 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001446 }
1447
yasin saplib4b8ee12021-06-13 18:25:20 +00001448 if (downstreamOltMeterId != null) {
1449 treatmentBuilder.meter(downstreamOltMeterId);
1450 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1451 }
1452
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001453 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1454 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001455
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301456 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1457 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanella600d2e22020-06-22 11:00:31 +02001458 }
1459
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001460 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1461 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001462 Integer priority,
1463 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001464 return DefaultForwardingObjective.builder()
1465 .withFlag(ForwardingObjective.Flag.VERSATILE)
1466 .withPriority(priority)
1467 .makePermanent()
1468 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001469 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001470 .fromApp(appId)
1471 .withTreatment(treatment);
1472 }
1473
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001474 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1475 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1476 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001477
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001478 si.uniTagList().forEach(uniTagInfo -> {
1479 if (uniTagInfo.getEnableMacLearning()) {
1480 requiresMacLearning.set(true);
1481 }
1482 });
1483
1484 return requiresMacLearning.get();
1485 }
1486
1487 /**
1488 * Checks whether the subscriber has the MacAddress configured or discovered.
1489 *
1490 * @param deviceId DeviceId for this subscriber
1491 * @param port Port for this subscriber
1492 * @param si SubscriberAndDeviceInformation
1493 * @return boolean
1494 */
1495 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1496 AtomicBoolean isConfigured = new AtomicBoolean();
1497 isConfigured.set(true);
1498
1499 si.uniTagList().forEach(uniTagInfo -> {
1500 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301501 boolean configureMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001502 boolean discoveredMac = false;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301503
1504 final VlanId vlan;
1505
1506 if (FttbUtils.isFttbDpuOrAncpService(uniTagInfo)) {
1507 // Using S tag, as C tag is replaced by Stag by ONU.
1508 vlan = uniTagInfo.getPonSTag();
1509 } else {
1510 vlan = uniTagInfo.getPonCTag();
1511 }
1512
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001513 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301514 .stream().filter(host -> host.vlan().equals(vlan)).findFirst();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001515 if (optHost.isPresent() && optHost.get().mac() != null) {
1516 discoveredMac = true;
1517 }
1518 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1519 log.debug("Awaiting for macAddress on {} for service {}",
1520 portWithName(port), uniTagInfo.getServiceName());
1521 isConfigured.set(false);
1522 }
1523 });
1524
1525 return isConfigured.get();
1526 }
1527
1528 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301529 boolean configuredMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001530 if (configuredMac) {
1531 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1532 } else if (uniTagInfo.getEnableMacLearning()) {
1533 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1534 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1535 if (optHost.isPresent() && optHost.get().mac() != null) {
1536 return optHost.get().mac();
1537 }
1538 }
1539 return null;
1540 }
1541
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001542 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001543 OltFlowsStatus subscriberEapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001544 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1545 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001546 if (log.isTraceEnabled()) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001547 log.trace("Updating cpStatus {} with values: eapolFlow={}, " +
1548 "subscriberEapolStatus={}, subscriberFlows={}, dhcpFlow={}",
1549 key, eapolStatus, subscriberEapolStatus, subscriberFlowsStatus, dhcpStatus);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001550 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001551 try {
1552 cpStatusWriteLock.lock();
1553 OltPortStatus status = cpStatus.get(key);
1554
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001555
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001556 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001557 // if we don't have status for the connectPoint
1558 // and we're only updating status to PENDING_REMOVE or ERROR
1559 // do not create it. This is because this case will only happen when a device is removed
1560 // and it's status cleaned
1561 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1562 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1563 statusesToIgnore.add(OltFlowsStatus.ERROR);
1564
1565 if (
1566 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1567 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1568 ) {
1569 if (log.isTraceEnabled()) {
1570 log.trace("Ignoring cpStatus update as status is meaningless");
1571 }
1572 return;
1573 }
1574
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001575 status = new OltPortStatus(
1576 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001577 subscriberEapolStatus != null ? subscriberEapolStatus : OltFlowsStatus.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001578 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001579 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1580 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001581 );
1582 } else {
1583 if (eapolStatus != null) {
1584 status.defaultEapolStatus = eapolStatus;
1585 }
1586 if (subscriberFlowsStatus != null) {
1587 status.subscriberFlowsStatus = subscriberFlowsStatus;
1588 }
1589 if (dhcpStatus != null) {
1590 status.dhcpStatus = dhcpStatus;
1591 }
1592 }
1593
1594 cpStatus.put(key, status);
1595 } finally {
1596 cpStatusWriteLock.unlock();
1597 }
1598 }
1599
1600 protected class InternalFlowListener implements FlowRuleListener {
1601 @Override
1602 public void event(FlowRuleEvent event) {
1603 if (appId.id() != (event.subject().appId())) {
1604 return;
1605 }
1606
1607 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1608 if (log.isTraceEnabled()) {
1609 log.trace("ignoring flow event {} " +
1610 "as not leader for {}", event, event.subject().deviceId());
1611 }
1612 return;
1613 }
1614
1615 switch (event.type()) {
1616 case RULE_ADDED:
1617 case RULE_REMOVED:
Andrea Campanella40d2b342022-02-04 18:13:37 +01001618 DeviceId deviceId = event.subject().deviceId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001619 Port port = getCpFromFlowRule(event.subject());
1620 if (port == null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001621 log.warn("Port is gone in ONOS, " +
1622 "manually creating it {}", event.subject());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301623 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(event.subject());
Andrea Campanella40d2b342022-02-04 18:13:37 +01001624 cpStatusReadLock.lock();
1625 Optional<ServiceKey> keyWithPort = cpStatus.keySet()
1626 .stream().filter(key -> key.getPort().connectPoint()
1627 .deviceId().equals(deviceId)
1628 && key.getPort().connectPoint().port()
1629 .equals(inPort)).findFirst();
1630 cpStatusReadLock.unlock();
1631 if (keyWithPort.isPresent()) {
1632 port = new DefaultPort(deviceService.getDevice(deviceId),
1633 inPort, false,
1634 DefaultAnnotations.builder()
1635 .set(AnnotationKeys.PORT_NAME,
1636 keyWithPort.get().getPort().name())
1637 .build());
1638 } else {
1639 log.warn("Can't find corresponding status for {}/{}", deviceId, inPort);
1640 return;
1641 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001642 }
1643 if (log.isTraceEnabled()) {
1644 log.trace("flow event {} on cp {}: {}", event.type(),
1645 portWithName(port), event.subject());
1646 }
1647 updateCpStatus(event.type(), port, event.subject());
1648 return;
1649 case RULE_ADD_REQUESTED:
1650 case RULE_REMOVE_REQUESTED:
1651 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1652 return;
1653 default:
1654 return;
1655 }
1656 }
1657
1658 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301659 OltFlowsStatus status = OltFlowServiceUtils.flowRuleStatusToOltFlowStatus(type);
1660 if (OltFlowServiceUtils.isDefaultEapolFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001661 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1662 defaultEapolUniTag);
1663 if (log.isTraceEnabled()) {
1664 log.trace("update defaultEapolStatus {} on {}", status, sk);
1665 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001666 updateConnectPointStatus(sk, status, null, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301667 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001668 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
1669 if (sk == null) {
1670 return;
1671 }
1672 if (log.isTraceEnabled()) {
1673 log.trace("update subscriberEapolStatus {} on {}", status, sk);
1674 }
1675 updateConnectPointStatus(sk, null, status, null, status, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301676 } else if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001677 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001678 if (sk == null) {
1679 return;
1680 }
1681 if (log.isTraceEnabled()) {
1682 log.trace("update dhcpStatus {} on {}", status, sk);
1683 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001684 updateConnectPointStatus(sk, null, null, null, status, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301685 } else if (OltFlowServiceUtils.isPppoeFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001686 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
yasin sapli0823c932022-01-26 11:26:09 +00001687 if (sk == null) {
1688 return;
1689 }
1690 if (log.isTraceEnabled()) {
1691 log.trace("update pppoeStatus {} on {}", status, sk);
1692 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001693 updateConnectPointStatus(sk, null, null, null, null, status);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301694 } else if (OltFlowServiceUtils.isDataFlow(flowRule)) {
1695 PortNumber number = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Andrea Campanella40d2b342022-02-04 18:13:37 +01001696 if (number == null) {
1697 log.error("Can't capture the port number from flow {}", flowRule);
1698 return;
1699 }
1700 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), number)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001701 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1702 return;
1703 }
1704
Andrea Campanella40d2b342022-02-04 18:13:37 +01001705 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001706 if (sk == null) {
1707 return;
1708 }
1709 if (log.isTraceEnabled()) {
1710 log.trace("update dataplaneStatus {} on {}", status, sk);
1711 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001712 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001713 }
1714 }
1715
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001716
Andrea Campanella87241ae2022-03-11 11:20:24 +01001717
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001718 private Port getCpFromFlowRule(FlowRule flowRule) {
1719 DeviceId deviceId = flowRule.deviceId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301720 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001721 if (inPort != null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001722 return deviceService.getPort(deviceId, inPort);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001723 }
1724 return null;
1725 }
1726
Andrea Campanella40d2b342022-02-04 18:13:37 +01001727 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule, Port flowPort) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001728 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1729
1730 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1731 if (si == null && !isNni) {
1732 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1733 return null;
1734 }
1735
1736 if (isNni) {
1737 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1738 }
1739
1740 Optional<UniTagInformation> found = Optional.empty();
1741 VlanId flowVlan = null;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301742 if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001743 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1744 L2ModificationInstruction.ModVlanIdInstruction instruction =
1745 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1746 flowVlan = instruction.vlanId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301747 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001748 // we need to make a special case for EAPOL as in the ATT workflow EAPOL flows don't match on tags
1749 L2ModificationInstruction.ModVlanIdInstruction instruction =
1750 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(2);
1751 flowVlan = instruction.vlanId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001752 } else {
1753 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1754 VlanIdCriterion vlanIdCriterion =
1755 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1756 if (vlanIdCriterion == null) {
1757 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1758 return null;
1759 }
1760 flowVlan = vlanIdCriterion.vlanId();
1761 }
1762
1763 VlanId finalFlowVlan = flowVlan;
1764 found = si.uniTagList().stream().filter(uti ->
1765 uti.getPonCTag().equals(finalFlowVlan) ||
1766 uti.getPonSTag().equals(finalFlowVlan) ||
1767 uti.getUniTagMatch().equals(finalFlowVlan)
1768 ).findFirst();
1769
1770
1771 if (found.isEmpty()) {
1772 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1773 }
1774
1775 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1776
1777 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001778 }
1779
1780 protected void bindSadisService(SadisService service) {
1781 this.subsService = service.getSubscriberInfoService();
1782 this.bpService = service.getBandwidthProfileService();
1783 log.info("Sadis service is loaded");
1784 }
1785
1786 protected void unbindSadisService(SadisService service) {
1787 this.subsService = null;
1788 this.bpService = null;
1789 log.info("Sadis service is unloaded");
1790 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301791
1792 private void processFttbUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1793 FlowOperation action,
1794 MeterId upstreamMeterId,
1795 MeterId upstreamOltMeterId,
1796 UniTagInformation uti,
1797 SubscriberAndDeviceInformation si) {
1798 String serviceName = uti.getServiceName();
1799 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1800 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1801 .matchInPort(port.number())
1802 .matchVlanId(uti.getPonCTag());
1803
1804 if (uti.getUsPonCTagPriority() != -1) {
1805 selectorBuilder.matchVlanPcp((byte) uti.getUsPonCTagPriority());
1806 }
1807
1808 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1809
1810 treatmentBuilder.setVlanId(uti.getPonSTag());
1811 if (uti.getUsPonSTagPriority() != -1) {
1812 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
1813 }
1814
1815 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1816 annotationBuilder.set(FTTB_SERVICE_NAME, serviceName);
1817 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_UPSTREAM);
1818
1819 if (upstreamMeterId != null) {
1820 treatmentBuilder.meter(upstreamMeterId);
1821 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1822 }
1823 if (upstreamOltMeterId != null) {
1824 treatmentBuilder.meter(upstreamOltMeterId);
1825 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
1826 }
1827
1828 VlanId innerVlan = null;
1829
1830 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1831 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1832 hostService, si, deviceId, port);
1833
1834 if (mac == null) {
1835 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1836 port, uti.getPonSTag(), serviceName);
1837 return;
1838 }
1839
1840 selectorBuilder.matchEthSrc(mac);
1841 innerVlan = VlanId.NONE;
1842
1843 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
1844 innerVlan = VlanId.ANY;
1845 }
1846
1847 treatmentBuilder.setOutput(nniPort.number()).writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1848 uti.getTechnologyProfileId(), nniPort.number()), 0L);
1849
1850 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1851 treatmentBuilder.build(), MIN_PRIORITY,
1852 annotationBuilder.build());
1853
1854 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1855 processForwardingRule(action, flowBuilder, context, deviceId);
1856 }
1857
1858 private void processFttbDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1859 FlowOperation action,
1860 MeterId downstreamMeterId,
1861 MeterId downstreamOltMeterId,
1862 UniTagInformation uti, SubscriberAndDeviceInformation si) {
1863 String serviceName = uti.getServiceName();
1864 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1865 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
1866 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1867 .matchVlanId(uti.getPonSTag())
1868 .matchInPort(nniPort.number());
1869
1870 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1871 .setVlanId(uti.getPonCTag())
1872 .setOutput(port.number());
1873
1874 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1875 annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
1876 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_DOWNSTREAM);
1877
1878 if (downstreamMeterId != null) {
1879 treatmentBuilder.meter(downstreamMeterId);
1880 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
1881 }
1882
1883 if (downstreamOltMeterId != null) {
1884 treatmentBuilder.meter(downstreamOltMeterId);
1885 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1886 }
1887
1888 VlanId innerVlan = null;
1889
1890 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1891 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1892 hostService, si, deviceId, port);
1893
1894 if (mac == null) {
1895 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1896 port, uti.getPonSTag(), serviceName);
1897 return;
1898 }
1899
1900 selectorBuilder.matchEthDst(mac);
1901 innerVlan = VlanId.NONE;
1902
1903 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
1904 innerVlan = VlanId.ANY;
1905 selectorBuilder.matchMetadata(uti.getPonSTag().toShort());
1906 }
1907
1908 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1909 uti.getTechnologyProfileId(),
1910 port.number()), 0);
1911
1912 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1913 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
1914
1915 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1916 processForwardingRule(action, flowBuilder, context, deviceId);
1917 }
1918
1919 private ObjectiveContext getSubscriberFlowBuilderContext(ServiceKey sk, FlowOperation action,
1920 FlowDirection flowDirection) {
1921 ObjectiveContext context = new ObjectiveContext() {
1922 @Override
1923 public void onSuccess(Objective objective) {
1924 log.info("{} {} Data plane filter for {}.",
1925 completeFlowOpToString(action), flowDirection, sk);
1926 }
1927
1928 @Override
1929 public void onError(Objective objective, ObjectiveError error) {
1930 log.info("{} Data plane filter for {} failed {} because {}.",
1931 flowDirection, sk, action, error);
1932 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
1933 }
1934 };
1935
1936 return context;
1937 }
1938
1939 private void processForwardingRule(FlowOperation action, DefaultForwardingObjective.Builder flowBuilder,
1940 ObjectiveContext context, DeviceId deviceId) {
1941 ForwardingObjective flow = null;
1942 if (action == FlowOperation.ADD) {
1943 flow = flowBuilder.add(context);
1944 } else if (action == FlowOperation.REMOVE) {
1945 flow = flowBuilder.remove(context);
1946 } else {
1947 log.error("Flow action not supported: {}", action);
1948 }
1949
1950 if (flow != null) {
1951 if (log.isTraceEnabled()) {
1952 log.trace("Forwarding rule {}", flow);
1953 }
1954 flowObjectiveService.forward(deviceId, flow);
1955 }
1956 }
1957}