blob: f438fee785569619711828ad9719409dcde38c53 [file] [log] [blame]
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001/*
Joey Armstrong7f6d6d22023-01-09 17:09:50 -05002 * Copyright 2021-2023 Open Networking Foundation (ONF) and the ONF Contributors
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;
Gustavo Silva29fb20e2022-05-26 09:59:54 -030067import org.opencord.olt.AccessDevicePort;
68import org.opencord.olt.DiscoveredSubscriber;
69import org.opencord.olt.OltDeviceServiceInterface;
70import org.opencord.olt.OltFlowServiceInterface;
71import org.opencord.olt.OltMeterServiceInterface;
72import org.opencord.olt.OltPortStatus;
73import org.opencord.olt.ServiceKey;
74import org.opencord.olt.OltFlowsStatus;
75import org.opencord.olt.FlowDirection;
76import org.opencord.olt.FlowOperation;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +053077import org.opencord.olt.impl.fttb.FttbUtils;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000078import org.opencord.sadis.BandwidthProfileInformation;
79import org.opencord.sadis.BaseInformationService;
80import org.opencord.sadis.SadisService;
81import org.opencord.sadis.SubscriberAndDeviceInformation;
82import org.opencord.sadis.UniTagInformation;
83import org.osgi.service.component.ComponentContext;
84import org.osgi.service.component.annotations.Activate;
85import org.osgi.service.component.annotations.Component;
86import org.osgi.service.component.annotations.Deactivate;
87import org.osgi.service.component.annotations.Modified;
88import org.osgi.service.component.annotations.Reference;
89import org.osgi.service.component.annotations.ReferenceCardinality;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000090import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000091import org.slf4j.Logger;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070092import org.slf4j.LoggerFactory;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000093
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080094import java.util.ArrayList;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010095import java.util.Dictionary;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070096import java.util.HashMap;
Andrea Campanella61650a12022-01-24 18:09:44 -080097import java.util.HashSet;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070098import java.util.Iterator;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080099import java.util.List;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000100import java.util.Map;
Andrea Campanellabfb47af2021-06-03 11:09:45 +0200101import java.util.Optional;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100102import java.util.Properties;
Andrea Campanella61650a12022-01-24 18:09:44 -0800103import java.util.Set;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700104import java.util.concurrent.atomic.AtomicBoolean;
105import java.util.concurrent.locks.Lock;
106import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100107
108import static com.google.common.base.Strings.isNullOrEmpty;
109import static org.onlab.util.Tools.get;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700110import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
111import static org.opencord.olt.impl.OltUtils.flowOpToString;
112import static org.opencord.olt.impl.OltUtils.getPortName;
113import static org.opencord.olt.impl.OltUtils.portWithName;
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200114import static org.opencord.olt.impl.OsgiPropertyConstants.*;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530115import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DIRECTION;
116import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DOWNSTREAM;
117import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_UPSTREAM;
118import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_ANCP_TRAFFIC;
119import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_MGMT_TRAFFIC;
120import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_NAME;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530121import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_SUBSCRIBER_TRAFFIC;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000122
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000123@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700124 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000125 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
126 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700127 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000128 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
yasin sapli0823c932022-01-26 11:26:09 +0000129 ENABLE_PPPOE_ON_NNI + ":Boolean=" + ENABLE_PPPOE_ON_NNI_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300130 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700131 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
132 // FIXME remove this option as potentially dangerous in production
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200133 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT,
134 REMOVE_FLOWS_ON_DISABLE + ":Boolean=" + REMOVE_FLOWS_ON_DISABLE_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000135})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700136public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
139 protected CoreService coreService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700142 protected ComponentConfigService cfgService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
145 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000146
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000147 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
148 bind = "bindSadisService",
149 unbind = "unbindSadisService",
150 policy = ReferencePolicy.DYNAMIC)
151 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700154 protected OltMeterServiceInterface oltMeterService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
157 protected OltDeviceServiceInterface oltDeviceService;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
160 protected FlowRuleService flowRuleService;
161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
163 protected HostService hostService;
164
165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000166 protected DeviceService deviceService;
167
168 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000169 protected StorageService storageService;
170
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700171 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
172 protected BaseInformationService<BandwidthProfileInformation> bpService;
173
174 private static final String APP_NAME = "org.opencord.olt";
175 protected ApplicationId appId;
176 private static final Integer MAX_PRIORITY = 10000;
177 private static final Integer MIN_PRIORITY = 1000;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530178 public static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700179 private static final int NONE_TP_ID = -1;
180 private static final String V4 = "V4";
181 private static final String V6 = "V6";
182 private final Logger log = LoggerFactory.getLogger(getClass());
183
184 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
185 .setServiceName("defaultEapol").build();
186 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
187 .setServiceName("nni")
188 .setTechnologyProfileId(NONE_TP_ID)
189 .setPonCTag(VlanId.NONE)
190 .setUniTagMatch(VlanId.ANY)
191 .setUsPonCTagPriority(-1)
192 .build();
193
194 /**
195 * Connect Point status map.
196 * Used to keep track of which cp has flows that needs to be removed when the status changes.
197 */
198 protected Map<ServiceKey, OltPortStatus> cpStatus;
199 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
200 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
201 private final Lock cpStatusReadLock = cpStatusLock.readLock();
202
203 /**
204 * This map contains the subscriber that have been provisioned by the operator.
205 * They may or may not have flows, depending on the port status.
206 * The map is used to define whether flows need to be provisioned when a port comes up.
207 */
208 protected Map<ServiceKey, Boolean> provisionedSubscribers;
209 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
210 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
211 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
212
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000213 /**
amit.ghosh74a4bb22022-06-14 11:34:52 +0200214 * For storing the mapping of ConnectPoints to FTTB DPU MAC addresses.
215 */
216 protected Map<ConnectPoint, MacAddress> fttbMacAddresses;
217 private final ReentrantReadWriteLock fttbMacAddressesLock = new ReentrantReadWriteLock();
218 private final Lock fttbMacAddressesWriteLock = fttbMacAddressesLock.writeLock();
219
220 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700221 * Create DHCP trap flow on NNI port(s).
222 */
223 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000224
225 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700226 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000227 **/
228 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
229
230 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700231 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000232 **/
233 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
234
235 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700236 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000237 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700238 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000239
240 /**
241 * Send EAPOL authentication trap flows before subscriber provisioning.
242 **/
243 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
244
245 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300246 * Send PPPoED authentication trap flows before subscriber provisioning.
247 **/
yasin sapli0823c932022-01-26 11:26:09 +0000248 protected boolean enablePppoeOnNni = ENABLE_PPPOE_ON_NNI_DEFAULT;
249
250 /**
251 * Enable flows for PPPoE if it is required in sadis config.
252 **/
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300253 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
254
255 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000256 * Default technology profile id that is used for authentication trap flows.
257 **/
258 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
259
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700260 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
261
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200262 /**
263 * Removes all the flows on an ONU disable.
264 **/
265 protected boolean removeFlowsOnDisable = REMOVE_FLOWS_ON_DISABLE_DEFAULT;
266
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700267 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000268
269 @Activate
270 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700271 cfgService.registerProperties(getClass());
272 appId = coreService.registerApplication(APP_NAME);
Mahir Gunyelc0bbe572024-03-19 12:58:20 -0700273 internalFlowListener = new InternalFlowListener();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700274
275 modified(context);
276
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000277 KryoNamespace serializer = KryoNamespace.newBuilder()
278 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700279 .register(OltFlowsStatus.class)
280 .register(FlowDirection.class)
281 .register(OltPortStatus.class)
282 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000283 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700284 .register(new ServiceKeySerializer(), ServiceKey.class)
285 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000286 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000287
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700288 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
289 .withName("volt-cp-status")
290 .withApplicationId(appId)
291 .withSerializer(Serializer.using(serializer))
292 .build().asJavaMap();
293
294 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
295 .withName("volt-provisioned-subscriber")
296 .withApplicationId(appId)
297 .withSerializer(Serializer.using(serializer))
298 .build().asJavaMap();
299
amit.ghosh74a4bb22022-06-14 11:34:52 +0200300 KryoNamespace fttbMacSerializer = KryoNamespace.newBuilder()
301 .register(KryoNamespaces.API)
302 .register(ConnectPoint.class)
303 .register(MacAddress.class)
304 .build();
305
306 fttbMacAddresses = storageService.<ConnectPoint, MacAddress>consistentMapBuilder()
307 .withName("fttb-mac-addresses")
308 .withApplicationId(appId)
309 .withSerializer(Serializer.using(fttbMacSerializer))
310 .build().asJavaMap();
311
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700312 flowRuleService.addListener(internalFlowListener);
313
314 log.info("Started");
315 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000316
317 @Deactivate
318 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700319 cfgService.unregisterProperties(getClass(), false);
320 flowRuleService.removeListener(internalFlowListener);
321 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000322 }
323
324 @Modified
325 public void modified(ComponentContext context) {
326
327 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
328
Saurav Dasf62cea82020-08-26 17:43:04 -0700329 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000330 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700331 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000332 }
333
Andrea Campanella7c49b792020-05-11 11:36:53 +0200334 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000335 if (v4 != null) {
336 enableDhcpV4 = v4;
337 }
338
Andrea Campanella7c49b792020-05-11 11:36:53 +0200339 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000340 if (v6 != null) {
341 enableDhcpV6 = v6;
342 }
343
Saurav Dasf62cea82020-08-26 17:43:04 -0700344 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000345 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700346 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000347 }
348
Andrea Campanella7c49b792020-05-11 11:36:53 +0200349 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000350 if (eap != null) {
351 enableEapol = eap;
352 }
353
yasin sapli0823c932022-01-26 11:26:09 +0000354 Boolean pppoeInNni = Tools.isPropertyEnabled(properties, ENABLE_PPPOE_ON_NNI);
355 if (pppoeInNni != null) {
356 enablePppoeOnNni = pppoeInNni;
357 }
358
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300359 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
360 if (pppoe != null) {
361 enablePppoe = pppoe;
362 }
363
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700364 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
365 if (wait != null) {
366 waitForRemoval = wait;
367 }
368
Andrea Campanella7c49b792020-05-11 11:36:53 +0200369 String tpId = get(properties, DEFAULT_TP_ID);
370 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000371
Gustavo Silva3041f1c2022-07-01 16:17:10 -0300372 Boolean removeOnDisable = Tools.isPropertyEnabled(properties, REMOVE_FLOWS_ON_DISABLE);
373 if (removeOnDisable != null) {
374 removeFlowsOnDisable = removeOnDisable;
375 }
376
yasin sapli0823c932022-01-26 11:26:09 +0000377 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " + "enableDhcpV6:{}, " +
Gustavo Silva3041f1c2022-07-01 16:17:10 -0300378 "enableIgmpOnNni:{}, enableEapol:{}, enablePppoeOnNni: {}, enablePppoe:{}, " +
379 "defaultTechProfileId:{}, waitForRemoval:{}, removeFlowsOnDisable:{}",
yasin sapli0823c932022-01-26 11:26:09 +0000380 enableDhcpOnNni, enableDhcpV4, enableDhcpV6, enableIgmpOnNni, enableEapol,
Gustavo Silva3041f1c2022-07-01 16:17:10 -0300381 enablePppoeOnNni, enablePppoe, defaultTechProfileId, waitForRemoval, removeOnDisable);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000382 }
383
384 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700385 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
386 try {
387 cpStatusReadLock.lock();
388 return ImmutableMap.copyOf(cpStatus);
389 } finally {
390 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000391 }
392 }
393
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700394 @Override
395 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
396 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800397 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700398 Map<ServiceKey, UniTagInformation> subscribers =
399 new HashMap<>();
400 try {
401 cpStatusReadLock.lock();
402
403 cpStatus.forEach((sk, status) -> {
Mahir Gunyelc0bbe572024-03-19 12:58:20 -0700404 if (
405 // not NNI Port
406 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
407 sk.getPort().connectPoint().port()) &&
408 // not EAPOL flow
409 !sk.getService().equals(defaultEapolUniTag) &&
410 !status.subscriberFlowsStatus.equals(OltFlowsStatus.PENDING_REMOVE)
411 && !status.subscriberFlowsStatus.equals(OltFlowsStatus.REMOVED)
412
413 ) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700414 subscribers.put(sk, sk.getService());
415 }
416 });
417
418 return ImmutableMap.copyOf(subscribers);
419 } finally {
420 cpStatusReadLock.unlock();
421 }
422 }
423
424 @Override
425 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
426 try {
427 provisionedSubscribersReadLock.lock();
428 return ImmutableMap.copyOf(provisionedSubscribers);
429 } finally {
430 provisionedSubscribersReadLock.unlock();
431 }
432 }
433
434 @Override
435 public void handleNniFlows(Device device, Port port, FlowOperation action) {
436
437 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800438 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700439 processLldpFilteringObjective(device.id(), port, action);
440
441 if (enableDhcpOnNni) {
442 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800443 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
444 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700445 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
446 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
447 null, null, nniUniTag);
448 }
449 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800450 log.debug("{} DHCPv6 trap flow on NNI {} for device {}", flowOpToString(action),
451 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700452 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
453 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
454 null, null, nniUniTag);
455 }
456 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800457 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700458 }
459
460 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800461 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700462 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
463 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
464 }
465
yasin sapli0823c932022-01-26 11:26:09 +0000466 if (enablePppoeOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800467 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700468 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
469 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
470 }
471 }
472
473 @Override
474 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
475 String oltBandwidthProfileId) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700476 // we only need to something if EAPOL is enabled
477 if (!enableEapol) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100478 log.debug("Eapol is disabled for {}", portWithName(sub.port));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700479 return true;
480 }
481
482 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
483 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
484 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
485 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
486 } else {
487 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
488 return false;
489 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700490 }
491
492 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100493 log.debug("Adding default flows for {}, status {}", portWithName(sub.port), sub.status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700494 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
495 if (log.isTraceEnabled()) {
496 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
497 }
498 return false;
499 }
500 if (hasDefaultEapol(sub.port)) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100501 OltPortStatus status = getOltPortStatus(sub.port, defaultEapolUniTag);
502 log.debug("Eapol is already present for {} with status {}", portWithName(sub.port), status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700503 return true;
504 }
505 return handleEapolFlow(sub, bandwidthProfileId,
506 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
507
508 }
509
510 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
511 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
512 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
513 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
514 }
515
516 @Override
517 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
518 String multicastServiceName) {
519 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
520 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
521 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200522 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED ||
523 sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700524 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
525 } else {
526 log.error("don't know how to handle {}", sub);
527 return false;
528 }
529 }
530
531 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
532 String multicastServiceName) {
533 if (log.isTraceEnabled()) {
534 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
535 }
536 if (enableEapol) {
537 if (hasDefaultEapol(sub.port)) {
538 // remove EAPOL flow and throw exception so that we'll retry later
539 if (!isDefaultEapolPendingRemoval(sub.port)) {
540 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
541 }
542
543 if (waitForRemoval) {
544 // NOTE wait for removal is a flag only needed to make sure VOLTHA
545 // does not explode with the flows remove/add in the same batch
546 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
547 return false;
548 } else {
549 log.warn("continuing provisioning on {}", portWithName(sub.port));
550 }
551 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700552 }
553
554 // NOTE createMeters will return if the meters are not installed
555 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800556 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700557 return false;
558 }
559
560 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
561 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
562 sub.subscriberAndDeviceInformation);
563
564 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
565 && !isMacAddressAvailable(sub.device.id(), sub.port,
566 sub.subscriberAndDeviceInformation)) {
567 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
568 return false;
569 }
570
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800571 // NOTE that the EAPOL flows handling is based on the data-plane flows status
572 // always process them before
573 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
574
yasin sapli0823c932022-01-26 11:26:09 +0000575 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
576
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700577 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
578 sub.subscriberAndDeviceInformation, multicastServiceName);
579
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700580 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
581
582 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
583 return true;
584 }
585
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800586 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700587 String multicastServiceName) {
588
589 if (log.isTraceEnabled()) {
590 log.trace("Removal of subscriber on {} started",
591 portWithName(sub.port));
592 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800593 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200594 //If the port has been removed the device service will return null, while it will be true if it's just disabled
595 boolean isPortPresent = deviceService.getPort(new ConnectPoint(sub.device.id(), sub.port.number())) != null;
596 if (log.isTraceEnabled()) {
597 log.trace("Port {} present: ", portWithName(sub.port), isPortPresent);
598 }
599 // Always remove the EAPOL flow in case of port disable,remove or subscriber remove.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700600 if (enableEapol) {
601 // remove the tagged eapol
602 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200603 log.info("Removal of eapol flow for subscriber on {} completed", portWithName(sub.port));
604
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800605 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200606 // If the port is gone entirely (onu delete) or it's enabled (subscriber remove request) remove all the flows
607 // In the case the port is just disabled we remove only the EAPOL flow because the ONU disable only represents
608 // the UNI side of the ONU going down, either for RG cable detach or for an administrative decision, but the PON
609 // side is still up as nothing changed, so no need to add/remove flows, when and if the UNI comes up
610 // we will re-push the EAPOL flow to require the subscriber to auth again.
611 // When the subscriber is admin removed from REST or CLI we ignore the port status.
Andrea Campanella7ef88992022-05-17 12:38:00 +0200612 // Check the admin Status of the port
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200613 if ((!isPortPresent || sub.port.isEnabled() || sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED)
614 || removeFlowsOnDisable) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700615
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200616 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800617
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200618 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE,
619 sub.subscriberAndDeviceInformation);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800620
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200621 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
622
623 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
624
625
626 if (enableEapol) {
627
628 // if any of the services still has flows, return false
629 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
630 while (iter.hasNext()) {
631 UniTagInformation entry = iter.next();
632 if (areSubscriberFlowsPendingRemoval(sub.port, entry, enableEapol)) {
633 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
634 portWithName(sub.port), entry.getServiceName());
635 return false;
636 }
637 }
638
639 // once the flows are removed add the default one back
640 // (only if the port is ENABLED and still present on the device)
641 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
642
643 // NOTE we remove the subscriber when the port goes down
644 // but in that case we don't need to add default eapol
645 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
646 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800647 }
648 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200649 // FIXME check the return status of the flow and return accordingly
650 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
651 return true;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700652 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700653 return true;
654 }
655
656 @Override
657 public boolean hasDefaultEapol(Port port) {
658 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700659 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
Gustavo Silva34664e82022-09-19 13:28:09 -0300660 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700661 }
662
663 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
664 try {
665 cpStatusReadLock.lock();
666 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
667 OltPortStatus status = cpStatus.get(sk);
668 return status;
669 } finally {
670 cpStatusReadLock.unlock();
671 }
672 }
673
674 public boolean isDefaultEapolPendingRemoval(Port port) {
675 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
676 if (log.isTraceEnabled()) {
677 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
678 status, portWithName(port), defaultEapolUniTag);
679 }
680 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
681 }
682
683 @Override
684 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
685 OltPortStatus status = getOltPortStatus(port, uti);
686 if (log.isTraceEnabled()) {
687 log.trace("Status during DHCP flow check {} for port {} and service {}",
688 status, portWithName(port), uti.getServiceName());
689 }
690 return status != null &&
691 (status.dhcpStatus == OltFlowsStatus.ADDED ||
692 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
693 }
694
695 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000696 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
697 OltPortStatus status = getOltPortStatus(port, uti);
698 if (log.isTraceEnabled()) {
699 log.trace("Status during PPPoE flow check {} for port {} and service {}",
700 status, portWithName(port), uti.getServiceName());
701 }
702 return status != null &&
703 (status.pppoeStatus == OltFlowsStatus.ADDED ||
704 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
705 }
706
707 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700708 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
709
710 OltPortStatus status = getOltPortStatus(port, uti);
711 if (log.isTraceEnabled()) {
712 log.trace("Status during subscriber flow check {} for port {} and service {}",
713 status, portWithName(port), uti.getServiceName());
714 }
715 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
716 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
717 }
718
Andrea Campanella87241ae2022-03-11 11:20:24 +0100719 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti, boolean enableEapol) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800720 OltPortStatus status = getOltPortStatus(port, uti);
721 if (log.isTraceEnabled()) {
722 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
723 status, portWithName(port), uti);
724 }
Andrea Campanella87241ae2022-03-11 11:20:24 +0100725 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE ||
726 (enableEapol && status.subscriberEapolStatus == OltFlowsStatus.PENDING_REMOVE) ||
727 (uti.getIsDhcpRequired() && status.dhcpStatus == OltFlowsStatus.PENDING_REMOVE));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800728 }
729
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700730 @Override
731 public void purgeDeviceFlows(DeviceId deviceId) {
732 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800733 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700734
735 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800736 if (log.isTraceEnabled()) {
737 log.trace("Clearing cp status from device {}", deviceId);
738 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700739 try {
740 cpStatusWriteLock.lock();
741 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
742 while (iter.hasNext()) {
743 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
744 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
745 cpStatus.remove(entry.getKey());
746 }
747 }
748 } finally {
749 cpStatusWriteLock.unlock();
750 }
751
752 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800753 if (log.isTraceEnabled()) {
754 log.trace("Clearing provisioned subscribers from device {}", deviceId);
755 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700756 try {
757 provisionedSubscribersWriteLock.lock();
758 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
759 while (iter.hasNext()) {
760 Map.Entry<ServiceKey, Boolean> entry = iter.next();
761 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
762 provisionedSubscribers.remove(entry.getKey());
763 }
764 }
765 } finally {
766 provisionedSubscribersWriteLock.unlock();
767 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800768 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700769 }
770
771 @Override
772 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800773 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700774 try {
775 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800776 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700777 } finally {
778 provisionedSubscribersReadLock.unlock();
779 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800780
781 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
782 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
783 return true;
784 }
785 }
786 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700787 }
788
789 @Override
790 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
791 try {
792 provisionedSubscribersReadLock.lock();
793 Boolean provisioned = provisionedSubscribers.get(sk);
794 if (provisioned == null || !provisioned) {
795 return false;
796 }
797 } finally {
798 provisionedSubscribersReadLock.unlock();
799 }
800 return true;
801 }
802
803 @Override
804 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
805 try {
806 provisionedSubscribersWriteLock.lock();
807 provisionedSubscribers.put(sk, status);
808 } finally {
809 provisionedSubscribersWriteLock.unlock();
810 }
811 }
812
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800813 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700814 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
815
816 // create a subscriberKey for the EAPOL flow
817 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
Andrea Campanella87241ae2022-03-11 11:20:24 +0100818 OltFlowsStatus status = action == FlowOperation.ADD ?
819 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700820 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +0100821 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE,
822 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700823
Andrea Campanella87241ae2022-03-11 11:20:24 +0100824 } else {
825 updateConnectPointStatus(sk, OltFlowsStatus.NONE, status, OltFlowsStatus.NONE,
826 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700827 }
828
829 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
830 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
831
832 int techProfileId = getDefaultTechProfileId(sub.port);
833 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
834
835 // in the delete case the meter should still be there as we remove
836 // the meters only if no flows are pointing to them
837 if (meterId == null) {
838 log.debug("MeterId is null for BandwidthProfile {} on device {}",
839 bandwidthProfile, sub.device.id());
840 return false;
841 }
842
843 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
844 if (oltMeterId == null) {
845 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
846 oltBandwidthProfile, sub.device.id());
847 return false;
848 }
849
850 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
851 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
852
853 FilteringObjective.Builder eapolAction;
854
855 if (action == FlowOperation.ADD) {
856 eapolAction = filterBuilder.permit();
857 } else if (action == FlowOperation.REMOVE) {
858 eapolAction = filterBuilder.deny();
859 } else {
860 log.error("Operation {} not supported", action);
861 return false;
862 }
863
864 FilteringObjective.Builder baseEapol = eapolAction
865 .withKey(Criteria.matchInPort(sub.port.number()))
866 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
867
868 // NOTE we only need to add the treatment to install the flow,
869 // we can remove it based in the match
870 FilteringObjective.Builder eapol;
871
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530872 // Setting VlanId.NONE as cvlan in the metadata as the packet will be single tagged
873 // and cvlan should not be filled.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700874 TrafficTreatment treatment = treatmentBuilder
875 .meter(meterId)
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530876 .writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530877 VlanId.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700878 techProfileId, oltMeterId), 0)
879 .setOutput(PortNumber.CONTROLLER)
880 .pushVlan()
881 .setVlanId(vlanId)
882 .build();
883 eapol = baseEapol
884 .withMeta(treatment);
885
886 FilteringObjective eapolObjective = eapol
887 .fromApp(appId)
888 .withPriority(MAX_PRIORITY)
889 .add(new ObjectiveContext() {
890 @Override
891 public void onSuccess(Objective objective) {
892 log.info("EAPOL flow objective {} for {}",
893 completeFlowOpToString(action), portWithName(sub.port));
894 if (log.isTraceEnabled()) {
895 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
896 }
897 }
898
899 @Override
900 public void onError(Objective objective, ObjectiveError error) {
901 log.error("Cannot {} eapol flow for {} : {}", action,
902 portWithName(sub.port), error);
903
904 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
905 updateConnectPointStatus(sk,
Andrea Campanella87241ae2022-03-11 11:20:24 +0100906 OltFlowsStatus.ERROR, null, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700907 }
908 }
909 });
910
911 flowObjectiveService.filter(sub.device.id(), eapolObjective);
912
913 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
914 return true;
915 }
916
917 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800918 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700919 SubscriberAndDeviceInformation si) {
920 if (!enableEapol) {
921 return true;
922 }
923 // TODO verify we need an EAPOL flow for EACH service
924 AtomicBoolean success = new AtomicBoolean(true);
925 si.uniTagList().forEach(u -> {
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200926 //Always act on the eapol flow
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700927 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
928 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
929 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
930 u.getUpstreamOltBandwidthProfile(),
931 action, u.getPonCTag())) {
932 //
Andrea Campanella87241ae2022-03-11 11:20:24 +0100933 log.error("Failed to {} EAPOL with subscriber tags", action);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700934 //TODO this sets it for all services, maybe some services succeeded.
935 success.set(false);
936 }
937 });
938 return success.get();
939 }
940
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800941 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700942 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
943 if (uti.getIsIgmpRequired()) {
944 DeviceId deviceId = sub.device.id();
945 // if we reached here a meter already exists
946 MeterId meterId = oltMeterService
947 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
948 MeterId oltMeterId = oltMeterService
949 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
950
951 processIgmpFilteringObjectives(deviceId, sub.port,
952 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
953 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
954 }
955 });
956 }
957
958 private boolean checkSadisRunning() {
959 if (bpService == null) {
960 log.warn("Sadis is not running");
961 return false;
962 }
963 return true;
964 }
965
966 private int getDefaultTechProfileId(Port port) {
967 if (!checkSadisRunning()) {
968 return defaultTechProfileId;
969 }
970 if (port != null) {
971 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
972 if (info != null && info.uniTagList().size() == 1) {
973 return info.uniTagList().get(0).getTechnologyProfileId();
974 }
975 }
976 return defaultTechProfileId;
977 }
978
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700979 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
980 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
981
982 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
983 .withKey(Criteria.matchInPort(port.number()))
984 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
985 .withMeta(DefaultTrafficTreatment.builder()
986 .setOutput(PortNumber.CONTROLLER).build())
987 .fromApp(appId)
988 .withPriority(MAX_PRIORITY)
989 .add(new ObjectiveContext() {
990 @Override
991 public void onSuccess(Objective objective) {
992 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
993 }
994
995 @Override
996 public void onError(Objective objective, ObjectiveError error) {
997 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
998 error);
999 }
1000 });
1001
1002 flowObjectiveService.filter(deviceId, lldp);
1003 }
1004
1005 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
1006 FlowOperation action,
1007 SubscriberAndDeviceInformation si) {
1008 si.uniTagList().forEach(uti -> {
1009
1010 if (!uti.getIsDhcpRequired()) {
1011 return;
1012 }
1013
1014 // if it's an ADD skip if flows are there,
1015 // if it's a DELETE skip if flows are not there
1016 boolean hasFlows = hasDhcpFlows(port, uti);
1017 if (action == FlowOperation.ADD && hasFlows ||
1018 action == FlowOperation.REMOVE && !hasFlows) {
1019 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1020 uti.getServiceName(), hasFlows);
1021 return;
1022 }
1023
1024 log.info("{} DHCP flows for subscriber on {} and service {}",
1025 flowOpToString(action), portWithName(port), uti.getServiceName());
1026
1027 // if we reached here a meter already exists
1028 MeterId meterId = oltMeterService
1029 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1030 MeterId oltMeterId = oltMeterService
1031 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1032
1033 if (enableDhcpV4) {
1034 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1035 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1036 uti);
1037 }
1038 if (enableDhcpV6) {
1039 log.error("DHCP V6 not supported for subscribers");
1040 }
1041 });
1042 }
1043
yasin sapli0823c932022-01-26 11:26:09 +00001044 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1045 FlowOperation action,
1046 SubscriberAndDeviceInformation si) {
1047 si.uniTagList().forEach(uti -> {
1048
1049 if (!uti.getIsPppoeRequired()) {
1050 return;
1051 }
1052
1053 // if it's an ADD skip if flows are there,
1054 // if it's a DELETE skip if flows are not there
1055 boolean hasFlows = hasPppoeFlows(port, uti);
1056 if (action == FlowOperation.ADD && hasFlows ||
1057 action == FlowOperation.REMOVE && !hasFlows) {
1058 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1059 uti.getServiceName(), hasFlows);
1060 return;
1061 }
1062
1063 log.info("{} PPPoE flows for subscriber on {} and service {}",
1064 flowOpToString(action), portWithName(port), uti.getServiceName());
1065
1066 // if we reached here a meter already exists
1067 MeterId meterId = oltMeterService
1068 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1069 MeterId oltMeterId = oltMeterService
1070 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1071
1072 if (enablePppoe) {
1073 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1074 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1075 (byte) uti.getUsPonCTagPriority());
1076 }
1077 });
1078 }
1079
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001080 // FIXME return boolean, if this fails we need to retry
1081 protected void handleSubscriberDataFlows(Device device, Port port,
1082 FlowOperation action,
1083 SubscriberAndDeviceInformation si, String multicastServiceName) {
1084
1085 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001086 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001087 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1088 si.id(), portWithName(port));
1089 return;
1090 }
1091 si.uniTagList().forEach(uti -> {
1092
1093 boolean hasFlows = hasSubscriberFlows(port, uti);
1094 if (action == FlowOperation.ADD && hasFlows ||
1095 action == FlowOperation.REMOVE && !hasFlows) {
1096 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1097 uti.getServiceName(), hasFlows);
1098 return;
1099 }
1100
1101 if (multicastServiceName.equals(uti.getServiceName())) {
1102 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1103 "dataplane flows are not needed",
1104 uti.getServiceName(), si.id(), portWithName(port));
1105 return;
1106 }
1107
1108 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1109 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001110 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1111 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1112 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001113 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001114
1115 // upstream flows
1116 MeterId usMeterId = oltMeterService
1117 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1118 MeterId oltUsMeterId = oltMeterService
1119 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301120
1121 if (FttbUtils.isFttbService(uti)) {
1122 processFttbUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action,
1123 usMeterId, oltUsMeterId, uti, si);
1124 } else {
1125 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1126 oltUsMeterId, uti);
1127 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001128
1129 // downstream flows
1130 MeterId dsMeterId = oltMeterService
1131 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1132 MeterId oltDsMeterId = oltMeterService
1133 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301134
1135 if (FttbUtils.isFttbService(uti)) {
1136 processFttbDownstreamDataFilteringObjects(device.id(), port, nniPort.get(),
1137 action, dsMeterId, oltDsMeterId, uti, si);
1138 } else {
1139 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1140 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1141 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001142 });
1143 }
1144
1145 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1146 FlowOperation action, FlowDirection direction,
1147 int udpSrc, int udpDst, EthType ethType, byte protocol,
1148 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1149 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1150 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1151
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301152 String serviceName = uti.getServiceName();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001153
1154 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1155 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001156 updateConnectPointStatus(sk, null, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001157
1158 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1159 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1160
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001161 if (meterId != null) {
1162 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001163 }
1164
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001165 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001166 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001167 .addCondition(Criteria.matchEthType(ethType))
1168 .addCondition(Criteria.matchIPProtocol(protocol))
1169 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1170 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001171 .fromApp(appId)
1172 .withPriority(MAX_PRIORITY);
1173
Andrea Campanella0e34f562020-06-11 10:47:10 +02001174 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001175 if (direction == FlowDirection.UPSTREAM) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301176 if (serviceName != null && serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC)) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301177 FttbUtils.addUpstreamDhcpCondition(dhcpBuilder, uti);
1178 FttbUtils.addUpstreamDhcpTreatment(treatmentBuilder, uti);
1179 } else {
1180 treatmentBuilder.setVlanId(uti.getPonCTag());
1181 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1182 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
1183 }
1184 if (uti.getUsPonCTagPriority() != -1) {
1185 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
1186 }
Andrea Campanella0e34f562020-06-11 10:47:10 +02001187 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301188 } else if (direction == FlowDirection.DOWNSTREAM) {
1189 // Down stream DHCP vid to be matched if OLT Sadis info contains Vlan id in nniDhcpTrapVid.
1190 Device device = deviceService.getDevice(deviceId);
1191 SubscriberAndDeviceInformation oltSub = subsService.get(device.serialNumber());
1192 VlanId nniDhcpTrapVid = oltSub.nniDhcpTrapVid();
1193
1194 if (nniDhcpTrapVid != null && !VlanId.vlanId(VlanId.NO_VID).equals(nniDhcpTrapVid)) {
1195 dhcpBuilder.addCondition(Criteria.matchVlanId(nniDhcpTrapVid));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001196 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001197 }
1198
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301199 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301200 // 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 +05301201 treatmentBuilder.writeMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301202 OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301203 uti.getTechnologyProfileId(), oltMeterId), 0);
1204 }
1205
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001206 dhcpBuilder.withMeta(treatmentBuilder
1207 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001208
1209
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001210 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001211 @Override
1212 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001213 log.info("{} DHCP {} filter for {}.",
1214 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1215 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001216 }
1217
1218 @Override
1219 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001220 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001221 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1222 portWithName(port),
1223 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001224 error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001225 updateConnectPointStatus(sk, null, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001226 }
1227 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001228 flowObjectiveService.filter(deviceId, dhcpUpstream);
1229 }
1230
1231 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1232 FlowOperation action, FlowDirection direction,
1233 MeterId meterId, MeterId oltMeterId, int techProfileId,
1234 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1235
1236 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1237 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1238 if (direction == FlowDirection.UPSTREAM) {
1239
1240 if (techProfileId != NONE_TP_ID) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301241 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(null,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001242 techProfileId, oltMeterId), 0);
1243 }
1244
1245
1246 if (meterId != null) {
1247 treatmentBuilder.meter(meterId);
1248 }
1249
1250 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1251 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1252 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001253 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1254 treatmentBuilder.setVlanId(cTag);
1255 }
1256
1257 if (vlanPcp != -1) {
1258 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1259 }
1260 }
1261
1262 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1263
1264 FilteringObjective igmp = filterBuilder
1265 .withKey(Criteria.matchInPort(port.number()))
1266 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1267 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1268 .withMeta(treatmentBuilder
1269 .setOutput(PortNumber.CONTROLLER).build())
1270 .fromApp(appId)
1271 .withPriority(MAX_PRIORITY)
1272 .add(new ObjectiveContext() {
1273 @Override
1274 public void onSuccess(Objective objective) {
1275 log.info("Igmp filter for {} {}.", portWithName(port), action);
1276 }
1277
1278 @Override
1279 public void onError(Objective objective, ObjectiveError error) {
1280 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1281 error);
1282 }
1283 });
1284
1285 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001286
1287 }
1288
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001289 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1290 FlowOperation action, FlowDirection direction,
1291 MeterId meterId, MeterId oltMeterId, int techProfileId,
1292 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001293
1294 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1295 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001296
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001297 if (meterId != null) {
1298 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001299 }
1300
1301 if (techProfileId != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301302 // Setting VlanId.NONE as cvlan as the packet will be single tagged and cvlan should not be filled.
1303 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
1304 techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001305 }
1306
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001307 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1308 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001309 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001310 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1311 .fromApp(appId)
1312 .withPriority(10000);
1313
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001314 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001315 treatmentBuilder.setVlanId(cTag);
1316 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1317 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1318 }
1319 if (vlanPcp != null) {
1320 treatmentBuilder.setVlanPcp(vlanPcp);
1321 }
1322 }
1323 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1324
1325 FilteringObjective pppoed = pppoedBuilder
1326 .add(new ObjectiveContext() {
1327 @Override
1328 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001329 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001330 }
1331
1332 @Override
1333 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001334 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1335 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001336 }
1337 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001338 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001339 }
1340
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001341 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1342 FlowOperation action,
1343 MeterId upstreamMeterId,
1344 MeterId upstreamOltMeterId,
1345 UniTagInformation uti) {
1346 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001347 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001348 .matchInPort(port.number())
1349 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001350 .build();
1351
Andrea Campanella327c5722020-01-30 11:34:13 +01001352 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1353 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001354 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001355 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001356 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001357 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001358 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1359 treatmentBuilder.popVlan();
1360 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001361
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001362 if (uti.getUsPonCTagPriority() != -1) {
1363 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001364
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001365 }
1366
1367 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001368 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001369
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001370 if (uti.getUsPonSTagPriority() != -1) {
1371 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001372 }
1373
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001374 treatmentBuilder.setOutput(nniPort.number())
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301375 .writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001376 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001377
yasin saplib4b8ee12021-06-13 18:25:20 +00001378 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1379
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001380 if (upstreamMeterId != null) {
1381 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001382 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1383 }
1384 if (upstreamOltMeterId != null) {
1385 treatmentBuilder.meter(upstreamOltMeterId);
1386 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001387 }
1388
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001389 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1390 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001391 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001392
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301393 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1394 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001395 }
1396
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001397 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1398 FlowOperation action,
1399 MeterId downstreamMeterId,
1400 MeterId downstreamOltMeterId,
1401 UniTagInformation uti,
1402 MacAddress macAddress) {
1403 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001404 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001405 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001406 .matchVlanId(uti.getPonSTag())
1407 .matchInPort(nniPort.number())
1408 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001409
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001410 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1411 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001412 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001413
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001414 if (uti.getDsPonCTagPriority() != -1) {
1415 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001416 }
1417
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001418 if (macAddress != null) {
1419 selectorBuilder.matchEthDst(macAddress);
1420 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001421
1422 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1423 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001424 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001425
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301426 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001427 uti.getTechnologyProfileId(),
1428 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001429
Andrea Campanella981e86c2021-03-12 11:35:33 +01001430 // Upstream pbit is used to remark inner vlan pbit.
1431 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1432 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1433 // all pbit acceptance are not widely supported by vendors even though present in
1434 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001435 if (uti.getUsPonCTagPriority() != -1) {
1436 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001437 }
1438
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001439 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1440 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1441 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001442 }
1443
yasin saplib4b8ee12021-06-13 18:25:20 +00001444 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1445
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001446 if (downstreamMeterId != null) {
1447 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001448 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001449 }
1450
yasin saplib4b8ee12021-06-13 18:25:20 +00001451 if (downstreamOltMeterId != null) {
1452 treatmentBuilder.meter(downstreamOltMeterId);
1453 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1454 }
1455
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001456 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1457 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001458
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301459 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1460 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanella600d2e22020-06-22 11:00:31 +02001461 }
1462
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001463 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1464 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001465 Integer priority,
1466 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001467 return DefaultForwardingObjective.builder()
1468 .withFlag(ForwardingObjective.Flag.VERSATILE)
1469 .withPriority(priority)
1470 .makePermanent()
1471 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001472 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001473 .fromApp(appId)
1474 .withTreatment(treatment);
1475 }
1476
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001477 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1478 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1479 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001480
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001481 si.uniTagList().forEach(uniTagInfo -> {
1482 if (uniTagInfo.getEnableMacLearning()) {
1483 requiresMacLearning.set(true);
1484 }
1485 });
1486
1487 return requiresMacLearning.get();
1488 }
1489
1490 /**
1491 * Checks whether the subscriber has the MacAddress configured or discovered.
1492 *
1493 * @param deviceId DeviceId for this subscriber
1494 * @param port Port for this subscriber
1495 * @param si SubscriberAndDeviceInformation
1496 * @return boolean
1497 */
1498 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1499 AtomicBoolean isConfigured = new AtomicBoolean();
1500 isConfigured.set(true);
1501
1502 si.uniTagList().forEach(uniTagInfo -> {
1503 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301504 boolean configureMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001505 boolean discoveredMac = false;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301506
1507 final VlanId vlan;
1508
1509 if (FttbUtils.isFttbDpuOrAncpService(uniTagInfo)) {
1510 // Using S tag, as C tag is replaced by Stag by ONU.
1511 vlan = uniTagInfo.getPonSTag();
1512 } else {
1513 vlan = uniTagInfo.getPonCTag();
1514 }
1515
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001516 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301517 .stream().filter(host -> host.vlan().equals(vlan)).findFirst();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001518 if (optHost.isPresent() && optHost.get().mac() != null) {
1519 discoveredMac = true;
1520 }
1521 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1522 log.debug("Awaiting for macAddress on {} for service {}",
1523 portWithName(port), uniTagInfo.getServiceName());
1524 isConfigured.set(false);
1525 }
1526 });
1527
1528 return isConfigured.get();
1529 }
1530
1531 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301532 boolean configuredMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001533 if (configuredMac) {
1534 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1535 } else if (uniTagInfo.getEnableMacLearning()) {
1536 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1537 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1538 if (optHost.isPresent() && optHost.get().mac() != null) {
1539 return optHost.get().mac();
1540 }
1541 }
1542 return null;
1543 }
1544
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001545 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001546 OltFlowsStatus subscriberEapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001547 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1548 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001549 if (log.isTraceEnabled()) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001550 log.trace("Updating cpStatus {} with values: eapolFlow={}, " +
1551 "subscriberEapolStatus={}, subscriberFlows={}, dhcpFlow={}",
1552 key, eapolStatus, subscriberEapolStatus, subscriberFlowsStatus, dhcpStatus);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001553 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001554 try {
1555 cpStatusWriteLock.lock();
1556 OltPortStatus status = cpStatus.get(key);
1557
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001558
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001559 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001560 // if we don't have status for the connectPoint
1561 // and we're only updating status to PENDING_REMOVE or ERROR
1562 // do not create it. This is because this case will only happen when a device is removed
1563 // and it's status cleaned
1564 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1565 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1566 statusesToIgnore.add(OltFlowsStatus.ERROR);
1567
1568 if (
1569 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1570 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1571 ) {
1572 if (log.isTraceEnabled()) {
1573 log.trace("Ignoring cpStatus update as status is meaningless");
1574 }
1575 return;
1576 }
1577
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001578 status = new OltPortStatus(
1579 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001580 subscriberEapolStatus != null ? subscriberEapolStatus : OltFlowsStatus.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001581 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001582 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1583 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001584 );
1585 } else {
1586 if (eapolStatus != null) {
1587 status.defaultEapolStatus = eapolStatus;
1588 }
1589 if (subscriberFlowsStatus != null) {
1590 status.subscriberFlowsStatus = subscriberFlowsStatus;
1591 }
1592 if (dhcpStatus != null) {
1593 status.dhcpStatus = dhcpStatus;
1594 }
1595 }
1596
1597 cpStatus.put(key, status);
1598 } finally {
1599 cpStatusWriteLock.unlock();
1600 }
1601 }
1602
1603 protected class InternalFlowListener implements FlowRuleListener {
1604 @Override
1605 public void event(FlowRuleEvent event) {
1606 if (appId.id() != (event.subject().appId())) {
1607 return;
1608 }
1609
1610 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1611 if (log.isTraceEnabled()) {
1612 log.trace("ignoring flow event {} " +
1613 "as not leader for {}", event, event.subject().deviceId());
1614 }
1615 return;
1616 }
1617
1618 switch (event.type()) {
1619 case RULE_ADDED:
1620 case RULE_REMOVED:
Andrea Campanella40d2b342022-02-04 18:13:37 +01001621 DeviceId deviceId = event.subject().deviceId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001622 Port port = getCpFromFlowRule(event.subject());
1623 if (port == null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001624 log.warn("Port is gone in ONOS, " +
1625 "manually creating it {}", event.subject());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301626 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(event.subject());
Andrea Campanella40d2b342022-02-04 18:13:37 +01001627 cpStatusReadLock.lock();
1628 Optional<ServiceKey> keyWithPort = cpStatus.keySet()
1629 .stream().filter(key -> key.getPort().connectPoint()
1630 .deviceId().equals(deviceId)
1631 && key.getPort().connectPoint().port()
1632 .equals(inPort)).findFirst();
1633 cpStatusReadLock.unlock();
1634 if (keyWithPort.isPresent()) {
1635 port = new DefaultPort(deviceService.getDevice(deviceId),
1636 inPort, false,
1637 DefaultAnnotations.builder()
1638 .set(AnnotationKeys.PORT_NAME,
1639 keyWithPort.get().getPort().name())
1640 .build());
1641 } else {
1642 log.warn("Can't find corresponding status for {}/{}", deviceId, inPort);
1643 return;
1644 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001645 }
1646 if (log.isTraceEnabled()) {
1647 log.trace("flow event {} on cp {}: {}", event.type(),
1648 portWithName(port), event.subject());
1649 }
1650 updateCpStatus(event.type(), port, event.subject());
1651 return;
1652 case RULE_ADD_REQUESTED:
1653 case RULE_REMOVE_REQUESTED:
1654 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1655 return;
1656 default:
1657 return;
1658 }
1659 }
1660
1661 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301662 OltFlowsStatus status = OltFlowServiceUtils.flowRuleStatusToOltFlowStatus(type);
1663 if (OltFlowServiceUtils.isDefaultEapolFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001664 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1665 defaultEapolUniTag);
1666 if (log.isTraceEnabled()) {
1667 log.trace("update defaultEapolStatus {} on {}", status, sk);
1668 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001669 updateConnectPointStatus(sk, status, null, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301670 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001671 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
1672 if (sk == null) {
1673 return;
1674 }
1675 if (log.isTraceEnabled()) {
1676 log.trace("update subscriberEapolStatus {} on {}", status, sk);
1677 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +02001678 updateConnectPointStatus(sk, null, status, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301679 } else if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001680 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001681 if (sk == null) {
1682 return;
1683 }
1684 if (log.isTraceEnabled()) {
1685 log.trace("update dhcpStatus {} on {}", status, sk);
1686 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001687 updateConnectPointStatus(sk, null, null, null, status, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301688 } else if (OltFlowServiceUtils.isPppoeFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001689 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
yasin sapli0823c932022-01-26 11:26:09 +00001690 if (sk == null) {
1691 return;
1692 }
1693 if (log.isTraceEnabled()) {
1694 log.trace("update pppoeStatus {} on {}", status, sk);
1695 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001696 updateConnectPointStatus(sk, null, null, null, null, status);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301697 } else if (OltFlowServiceUtils.isDataFlow(flowRule)) {
1698 PortNumber number = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Andrea Campanella40d2b342022-02-04 18:13:37 +01001699 if (number == null) {
1700 log.error("Can't capture the port number from flow {}", flowRule);
1701 return;
1702 }
1703 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), number)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001704 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1705 return;
1706 }
1707
Andrea Campanella40d2b342022-02-04 18:13:37 +01001708 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001709 if (sk == null) {
1710 return;
1711 }
1712 if (log.isTraceEnabled()) {
1713 log.trace("update dataplaneStatus {} on {}", status, sk);
1714 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001715 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001716 }
1717 }
1718
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001719
Andrea Campanella87241ae2022-03-11 11:20:24 +01001720
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001721 private Port getCpFromFlowRule(FlowRule flowRule) {
1722 DeviceId deviceId = flowRule.deviceId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301723 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001724 if (inPort != null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001725 return deviceService.getPort(deviceId, inPort);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001726 }
1727 return null;
1728 }
1729
Andrea Campanella40d2b342022-02-04 18:13:37 +01001730 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule, Port flowPort) {
Mahir Gunyelc0bbe572024-03-19 12:58:20 -07001731 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001732
1733 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1734 if (si == null && !isNni) {
1735 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1736 return null;
1737 }
1738
1739 if (isNni) {
1740 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1741 }
1742
1743 Optional<UniTagInformation> found = Optional.empty();
1744 VlanId flowVlan = null;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301745 if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001746 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1747 L2ModificationInstruction.ModVlanIdInstruction instruction =
1748 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1749 flowVlan = instruction.vlanId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301750 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001751 // we need to make a special case for EAPOL as in the ATT workflow EAPOL flows don't match on tags
1752 L2ModificationInstruction.ModVlanIdInstruction instruction =
1753 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(2);
1754 flowVlan = instruction.vlanId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001755 } else {
1756 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1757 VlanIdCriterion vlanIdCriterion =
1758 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1759 if (vlanIdCriterion == null) {
1760 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1761 return null;
1762 }
1763 flowVlan = vlanIdCriterion.vlanId();
1764 }
1765
1766 VlanId finalFlowVlan = flowVlan;
1767 found = si.uniTagList().stream().filter(uti ->
1768 uti.getPonCTag().equals(finalFlowVlan) ||
1769 uti.getPonSTag().equals(finalFlowVlan) ||
1770 uti.getUniTagMatch().equals(finalFlowVlan)
1771 ).findFirst();
1772
1773
1774 if (found.isEmpty()) {
1775 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1776 }
1777
1778 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1779
1780 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001781 }
1782
1783 protected void bindSadisService(SadisService service) {
1784 this.subsService = service.getSubscriberInfoService();
1785 this.bpService = service.getBandwidthProfileService();
1786 log.info("Sadis service is loaded");
1787 }
1788
1789 protected void unbindSadisService(SadisService service) {
1790 this.subsService = null;
1791 this.bpService = null;
1792 log.info("Sadis service is unloaded");
1793 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301794
1795 private void processFttbUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1796 FlowOperation action,
1797 MeterId upstreamMeterId,
1798 MeterId upstreamOltMeterId,
1799 UniTagInformation uti,
1800 SubscriberAndDeviceInformation si) {
1801 String serviceName = uti.getServiceName();
1802 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1803 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1804 .matchInPort(port.number())
1805 .matchVlanId(uti.getPonCTag());
1806
1807 if (uti.getUsPonCTagPriority() != -1) {
1808 selectorBuilder.matchVlanPcp((byte) uti.getUsPonCTagPriority());
1809 }
1810
1811 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1812
1813 treatmentBuilder.setVlanId(uti.getPonSTag());
1814 if (uti.getUsPonSTagPriority() != -1) {
1815 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
1816 }
1817
1818 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1819 annotationBuilder.set(FTTB_SERVICE_NAME, serviceName);
1820 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_UPSTREAM);
1821
1822 if (upstreamMeterId != null) {
1823 treatmentBuilder.meter(upstreamMeterId);
1824 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1825 }
1826 if (upstreamOltMeterId != null) {
1827 treatmentBuilder.meter(upstreamOltMeterId);
1828 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
1829 }
1830
1831 VlanId innerVlan = null;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001832 treatmentBuilder.setOutput(nniPort.number());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301833 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
amit.ghosh74a4bb22022-06-14 11:34:52 +02001834 fttbMacAddressesWriteLock.lock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301835 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
amit.ghosh74a4bb22022-06-14 11:34:52 +02001836 hostService, si, deviceId, port, fttbMacAddresses);
1837 fttbMacAddressesWriteLock.unlock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301838
1839 if (mac == null) {
1840 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1841 port, uti.getPonSTag(), serviceName);
1842 return;
1843 }
1844
1845 selectorBuilder.matchEthSrc(mac);
Andrea Campanella7ef88992022-05-17 12:38:00 +02001846
1847 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
1848 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301849
1850 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
Andrea Campanella7ef88992022-05-17 12:38:00 +02001851 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1852 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301853 }
1854
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301855 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1856 treatmentBuilder.build(), MIN_PRIORITY,
1857 annotationBuilder.build());
1858
1859 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1860 processForwardingRule(action, flowBuilder, context, deviceId);
1861 }
1862
1863 private void processFttbDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1864 FlowOperation action,
1865 MeterId downstreamMeterId,
1866 MeterId downstreamOltMeterId,
1867 UniTagInformation uti, SubscriberAndDeviceInformation si) {
1868 String serviceName = uti.getServiceName();
1869 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1870 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
1871 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1872 .matchVlanId(uti.getPonSTag())
1873 .matchInPort(nniPort.number());
1874
1875 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1876 .setVlanId(uti.getPonCTag())
1877 .setOutput(port.number());
1878
1879 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1880 annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
1881 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_DOWNSTREAM);
1882
1883 if (downstreamMeterId != null) {
1884 treatmentBuilder.meter(downstreamMeterId);
1885 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
1886 }
1887
1888 if (downstreamOltMeterId != null) {
1889 treatmentBuilder.meter(downstreamOltMeterId);
1890 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1891 }
1892
1893 VlanId innerVlan = null;
1894
1895 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
amit.ghosh74a4bb22022-06-14 11:34:52 +02001896 fttbMacAddressesWriteLock.lock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301897 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
amit.ghosh74a4bb22022-06-14 11:34:52 +02001898 hostService, si, deviceId, port, fttbMacAddresses);
1899 fttbMacAddressesWriteLock.unlock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301900
1901 if (mac == null) {
1902 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1903 port, uti.getPonSTag(), serviceName);
1904 return;
1905 }
1906
1907 selectorBuilder.matchEthDst(mac);
1908 innerVlan = VlanId.NONE;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001909 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1910 uti.getTechnologyProfileId(),
1911 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301912
1913 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
amit.ghosh4f0910e2022-06-20 15:53:21 +02001914 selectorBuilder.matchMetadata(uti.getPonSTag().toShort());
Andrea Campanella7ef88992022-05-17 12:38:00 +02001915 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1916 uti.getTechnologyProfileId(),
1917 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301918 }
1919
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301920 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1921 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
1922
1923 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1924 processForwardingRule(action, flowBuilder, context, deviceId);
1925 }
1926
1927 private ObjectiveContext getSubscriberFlowBuilderContext(ServiceKey sk, FlowOperation action,
1928 FlowDirection flowDirection) {
1929 ObjectiveContext context = new ObjectiveContext() {
1930 @Override
1931 public void onSuccess(Objective objective) {
1932 log.info("{} {} Data plane filter for {}.",
1933 completeFlowOpToString(action), flowDirection, sk);
1934 }
1935
1936 @Override
1937 public void onError(Objective objective, ObjectiveError error) {
1938 log.info("{} Data plane filter for {} failed {} because {}.",
1939 flowDirection, sk, action, error);
1940 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
1941 }
1942 };
1943
1944 return context;
1945 }
1946
1947 private void processForwardingRule(FlowOperation action, DefaultForwardingObjective.Builder flowBuilder,
1948 ObjectiveContext context, DeviceId deviceId) {
1949 ForwardingObjective flow = null;
1950 if (action == FlowOperation.ADD) {
1951 flow = flowBuilder.add(context);
1952 } else if (action == FlowOperation.REMOVE) {
1953 flow = flowBuilder.remove(context);
1954 } else {
1955 log.error("Flow action not supported: {}", action);
1956 }
1957
1958 if (flow != null) {
1959 if (log.isTraceEnabled()) {
1960 log.trace("Forwarding rule {}", flow);
1961 }
1962 flowObjectiveService.forward(deviceId, flow);
1963 }
1964 }
amit.ghosh74a4bb22022-06-14 11:34:52 +02001965}