blob: 4cd96ee51809d8d887deb0aaf6946f1235b3bf17 [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;
Gustavo Silva89e2f042022-08-01 09:58:04 -0300114import static org.opencord.olt.impl.OltUtils.getProgrammedSubscriber;
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200115import static org.opencord.olt.impl.OsgiPropertyConstants.*;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530116import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DIRECTION;
117import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DOWNSTREAM;
118import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_UPSTREAM;
119import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_ANCP_TRAFFIC;
120import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_MGMT_TRAFFIC;
121import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_NAME;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530122import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_SUBSCRIBER_TRAFFIC;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000123
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000124@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700125 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000126 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
127 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700128 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000129 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
yasin sapli0823c932022-01-26 11:26:09 +0000130 ENABLE_PPPOE_ON_NNI + ":Boolean=" + ENABLE_PPPOE_ON_NNI_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300131 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700132 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
133 // FIXME remove this option as potentially dangerous in production
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200134 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT,
135 REMOVE_FLOWS_ON_DISABLE + ":Boolean=" + REMOVE_FLOWS_ON_DISABLE_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000136})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700137public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
140 protected CoreService coreService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700143 protected ComponentConfigService cfgService;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
146 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000147
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000148 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
149 bind = "bindSadisService",
150 unbind = "unbindSadisService",
151 policy = ReferencePolicy.DYNAMIC)
152 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700155 protected OltMeterServiceInterface oltMeterService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY)
158 protected OltDeviceServiceInterface oltDeviceService;
159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
161 protected FlowRuleService flowRuleService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY)
164 protected HostService hostService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000167 protected DeviceService deviceService;
168
169 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000170 protected StorageService storageService;
171
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700172 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
173 protected BaseInformationService<BandwidthProfileInformation> bpService;
174
175 private static final String APP_NAME = "org.opencord.olt";
176 protected ApplicationId appId;
177 private static final Integer MAX_PRIORITY = 10000;
178 private static final Integer MIN_PRIORITY = 1000;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530179 public static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700180 private static final int NONE_TP_ID = -1;
181 private static final String V4 = "V4";
182 private static final String V6 = "V6";
183 private final Logger log = LoggerFactory.getLogger(getClass());
184
185 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
186 .setServiceName("defaultEapol").build();
187 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
188 .setServiceName("nni")
189 .setTechnologyProfileId(NONE_TP_ID)
190 .setPonCTag(VlanId.NONE)
191 .setUniTagMatch(VlanId.ANY)
192 .setUsPonCTagPriority(-1)
193 .build();
194
195 /**
196 * Connect Point status map.
197 * Used to keep track of which cp has flows that needs to be removed when the status changes.
198 */
199 protected Map<ServiceKey, OltPortStatus> cpStatus;
200 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
201 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
202 private final Lock cpStatusReadLock = cpStatusLock.readLock();
203
204 /**
205 * This map contains the subscriber that have been provisioned by the operator.
206 * They may or may not have flows, depending on the port status.
207 * The map is used to define whether flows need to be provisioned when a port comes up.
208 */
209 protected Map<ServiceKey, Boolean> provisionedSubscribers;
210 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
211 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
212 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
213
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000214 /**
amit.ghosh74a4bb22022-06-14 11:34:52 +0200215 * For storing the mapping of ConnectPoints to FTTB DPU MAC addresses.
216 */
217 protected Map<ConnectPoint, MacAddress> fttbMacAddresses;
218 private final ReentrantReadWriteLock fttbMacAddressesLock = new ReentrantReadWriteLock();
219 private final Lock fttbMacAddressesWriteLock = fttbMacAddressesLock.writeLock();
220
221 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700222 * Create DHCP trap flow on NNI port(s).
223 */
224 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000225
226 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700227 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000228 **/
229 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
230
231 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700232 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000233 **/
234 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
235
236 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700237 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000238 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700239 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000240
241 /**
242 * Send EAPOL authentication trap flows before subscriber provisioning.
243 **/
244 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
245
246 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300247 * Send PPPoED authentication trap flows before subscriber provisioning.
248 **/
yasin sapli0823c932022-01-26 11:26:09 +0000249 protected boolean enablePppoeOnNni = ENABLE_PPPOE_ON_NNI_DEFAULT;
250
251 /**
252 * Enable flows for PPPoE if it is required in sadis config.
253 **/
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300254 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
255
256 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000257 * Default technology profile id that is used for authentication trap flows.
258 **/
259 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
260
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700261 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
262
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200263 /**
264 * Removes all the flows on an ONU disable.
265 **/
266 protected boolean removeFlowsOnDisable = REMOVE_FLOWS_ON_DISABLE_DEFAULT;
267
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700268 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000269
270 @Activate
271 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700272 cfgService.registerProperties(getClass());
273 appId = coreService.registerApplication(APP_NAME);
Gustavo Silva89e2f042022-08-01 09:58:04 -0300274 internalFlowListener = new InternalFlowListener(this);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700275
276 modified(context);
277
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000278 KryoNamespace serializer = KryoNamespace.newBuilder()
279 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700280 .register(OltFlowsStatus.class)
281 .register(FlowDirection.class)
282 .register(OltPortStatus.class)
283 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000284 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700285 .register(new ServiceKeySerializer(), ServiceKey.class)
286 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000287 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000288
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700289 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
290 .withName("volt-cp-status")
291 .withApplicationId(appId)
292 .withSerializer(Serializer.using(serializer))
293 .build().asJavaMap();
294
295 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
296 .withName("volt-provisioned-subscriber")
297 .withApplicationId(appId)
298 .withSerializer(Serializer.using(serializer))
299 .build().asJavaMap();
300
amit.ghosh74a4bb22022-06-14 11:34:52 +0200301 KryoNamespace fttbMacSerializer = KryoNamespace.newBuilder()
302 .register(KryoNamespaces.API)
303 .register(ConnectPoint.class)
304 .register(MacAddress.class)
305 .build();
306
307 fttbMacAddresses = storageService.<ConnectPoint, MacAddress>consistentMapBuilder()
308 .withName("fttb-mac-addresses")
309 .withApplicationId(appId)
310 .withSerializer(Serializer.using(fttbMacSerializer))
311 .build().asJavaMap();
312
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700313 flowRuleService.addListener(internalFlowListener);
314
315 log.info("Started");
316 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000317
318 @Deactivate
319 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700320 cfgService.unregisterProperties(getClass(), false);
321 flowRuleService.removeListener(internalFlowListener);
322 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000323 }
324
325 @Modified
326 public void modified(ComponentContext context) {
327
328 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
329
Saurav Dasf62cea82020-08-26 17:43:04 -0700330 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000331 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700332 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000333 }
334
Andrea Campanella7c49b792020-05-11 11:36:53 +0200335 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000336 if (v4 != null) {
337 enableDhcpV4 = v4;
338 }
339
Andrea Campanella7c49b792020-05-11 11:36:53 +0200340 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000341 if (v6 != null) {
342 enableDhcpV6 = v6;
343 }
344
Saurav Dasf62cea82020-08-26 17:43:04 -0700345 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000346 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700347 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000348 }
349
Andrea Campanella7c49b792020-05-11 11:36:53 +0200350 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000351 if (eap != null) {
352 enableEapol = eap;
353 }
354
yasin sapli0823c932022-01-26 11:26:09 +0000355 Boolean pppoeInNni = Tools.isPropertyEnabled(properties, ENABLE_PPPOE_ON_NNI);
356 if (pppoeInNni != null) {
357 enablePppoeOnNni = pppoeInNni;
358 }
359
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300360 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
361 if (pppoe != null) {
362 enablePppoe = pppoe;
363 }
364
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700365 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
366 if (wait != null) {
367 waitForRemoval = wait;
368 }
369
Andrea Campanella7c49b792020-05-11 11:36:53 +0200370 String tpId = get(properties, DEFAULT_TP_ID);
371 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000372
Gustavo Silva3041f1c2022-07-01 16:17:10 -0300373 Boolean removeOnDisable = Tools.isPropertyEnabled(properties, REMOVE_FLOWS_ON_DISABLE);
374 if (removeOnDisable != null) {
375 removeFlowsOnDisable = removeOnDisable;
376 }
377
yasin sapli0823c932022-01-26 11:26:09 +0000378 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " + "enableDhcpV6:{}, " +
Gustavo Silva3041f1c2022-07-01 16:17:10 -0300379 "enableIgmpOnNni:{}, enableEapol:{}, enablePppoeOnNni: {}, enablePppoe:{}, " +
380 "defaultTechProfileId:{}, waitForRemoval:{}, removeFlowsOnDisable:{}",
yasin sapli0823c932022-01-26 11:26:09 +0000381 enableDhcpOnNni, enableDhcpV4, enableDhcpV6, enableIgmpOnNni, enableEapol,
Gustavo Silva3041f1c2022-07-01 16:17:10 -0300382 enablePppoeOnNni, enablePppoe, defaultTechProfileId, waitForRemoval, removeOnDisable);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000383 }
384
385 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700386 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
387 try {
388 cpStatusReadLock.lock();
389 return ImmutableMap.copyOf(cpStatus);
390 } finally {
391 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000392 }
393 }
394
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700395 @Override
396 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
397 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800398 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700399 Map<ServiceKey, UniTagInformation> subscribers =
400 new HashMap<>();
401 try {
402 cpStatusReadLock.lock();
403
404 cpStatus.forEach((sk, status) -> {
Gustavo Silva89e2f042022-08-01 09:58:04 -0300405 ConnectPoint cp = sk.getPort().connectPoint();
406 Device device = deviceService.getDevice(cp.deviceId());
407 boolean notNni = !oltDeviceService.isNniPort(device, cp.port());
408 boolean notEapol = !sk.getService().equals(defaultEapolUniTag);
409 boolean hasHsia = status.subscriberFlowsStatus.hasFlow();
410 boolean hasDhcp = status.dhcpStatus.hasFlow();
411 if (notNni && notEapol && (hasHsia || hasDhcp)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700412 subscribers.put(sk, sk.getService());
413 }
414 });
415
416 return ImmutableMap.copyOf(subscribers);
417 } finally {
418 cpStatusReadLock.unlock();
419 }
420 }
421
422 @Override
423 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
424 try {
425 provisionedSubscribersReadLock.lock();
426 return ImmutableMap.copyOf(provisionedSubscribers);
427 } finally {
428 provisionedSubscribersReadLock.unlock();
429 }
430 }
431
432 @Override
433 public void handleNniFlows(Device device, Port port, FlowOperation action) {
434
435 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800436 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700437 processLldpFilteringObjective(device.id(), port, action);
438
439 if (enableDhcpOnNni) {
440 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800441 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
442 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700443 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
444 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
445 null, null, nniUniTag);
446 }
447 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800448 log.debug("{} DHCPv6 trap flow on NNI {} for device {}", flowOpToString(action),
449 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700450 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
451 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
452 null, null, nniUniTag);
453 }
454 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800455 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700456 }
457
458 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800459 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700460 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
461 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
462 }
463
yasin sapli0823c932022-01-26 11:26:09 +0000464 if (enablePppoeOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800465 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700466 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
467 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
468 }
469 }
470
471 @Override
472 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
473 String oltBandwidthProfileId) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700474 // we only need to something if EAPOL is enabled
475 if (!enableEapol) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100476 log.debug("Eapol is disabled for {}", portWithName(sub.port));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700477 return true;
478 }
479
480 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
481 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
482 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
483 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
484 } else {
485 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
486 return false;
487 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700488 }
489
490 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100491 log.debug("Adding default flows for {}, status {}", portWithName(sub.port), sub.status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700492 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
493 if (log.isTraceEnabled()) {
494 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
495 }
496 return false;
497 }
498 if (hasDefaultEapol(sub.port)) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100499 OltPortStatus status = getOltPortStatus(sub.port, defaultEapolUniTag);
500 log.debug("Eapol is already present for {} with status {}", portWithName(sub.port), status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700501 return true;
502 }
503 return handleEapolFlow(sub, bandwidthProfileId,
504 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
505
506 }
507
508 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
509 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
510 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
511 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
512 }
513
514 @Override
515 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
516 String multicastServiceName) {
517 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
518 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
519 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200520 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED ||
521 sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700522 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
523 } else {
524 log.error("don't know how to handle {}", sub);
525 return false;
526 }
527 }
528
529 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
530 String multicastServiceName) {
531 if (log.isTraceEnabled()) {
532 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
533 }
534 if (enableEapol) {
535 if (hasDefaultEapol(sub.port)) {
536 // remove EAPOL flow and throw exception so that we'll retry later
537 if (!isDefaultEapolPendingRemoval(sub.port)) {
538 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
539 }
540
541 if (waitForRemoval) {
542 // NOTE wait for removal is a flag only needed to make sure VOLTHA
543 // does not explode with the flows remove/add in the same batch
544 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
545 return false;
546 } else {
547 log.warn("continuing provisioning on {}", portWithName(sub.port));
548 }
549 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700550 }
551
552 // NOTE createMeters will return if the meters are not installed
553 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800554 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700555 return false;
556 }
557
558 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
559 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
560 sub.subscriberAndDeviceInformation);
561
562 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
563 && !isMacAddressAvailable(sub.device.id(), sub.port,
564 sub.subscriberAndDeviceInformation)) {
565 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
566 return false;
567 }
568
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800569 // NOTE that the EAPOL flows handling is based on the data-plane flows status
570 // always process them before
571 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
572
yasin sapli0823c932022-01-26 11:26:09 +0000573 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
574
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700575 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
576 sub.subscriberAndDeviceInformation, multicastServiceName);
577
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700578 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
579
580 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
581 return true;
582 }
583
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800584 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700585 String multicastServiceName) {
586
587 if (log.isTraceEnabled()) {
588 log.trace("Removal of subscriber on {} started",
589 portWithName(sub.port));
590 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800591 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200592 //If the port has been removed the device service will return null, while it will be true if it's just disabled
593 boolean isPortPresent = deviceService.getPort(new ConnectPoint(sub.device.id(), sub.port.number())) != null;
594 if (log.isTraceEnabled()) {
595 log.trace("Port {} present: ", portWithName(sub.port), isPortPresent);
596 }
597 // Always remove the EAPOL flow in case of port disable,remove or subscriber remove.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700598 if (enableEapol) {
599 // remove the tagged eapol
600 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200601 log.info("Removal of eapol flow for subscriber on {} completed", portWithName(sub.port));
602
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800603 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200604 // If the port is gone entirely (onu delete) or it's enabled (subscriber remove request) remove all the flows
605 // In the case the port is just disabled we remove only the EAPOL flow because the ONU disable only represents
606 // the UNI side of the ONU going down, either for RG cable detach or for an administrative decision, but the PON
607 // side is still up as nothing changed, so no need to add/remove flows, when and if the UNI comes up
608 // we will re-push the EAPOL flow to require the subscriber to auth again.
609 // When the subscriber is admin removed from REST or CLI we ignore the port status.
Andrea Campanella7ef88992022-05-17 12:38:00 +0200610 // Check the admin Status of the port
Andrea Campanella833ce2b2022-06-28 16:36:23 +0200611 if ((!isPortPresent || sub.port.isEnabled() || sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED)
612 || removeFlowsOnDisable) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700613
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200614 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800615
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200616 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE,
617 sub.subscriberAndDeviceInformation);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800618
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200619 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
620
621 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
622
623
624 if (enableEapol) {
625
626 // if any of the services still has flows, return false
627 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
628 while (iter.hasNext()) {
629 UniTagInformation entry = iter.next();
630 if (areSubscriberFlowsPendingRemoval(sub.port, entry, enableEapol)) {
631 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
632 portWithName(sub.port), entry.getServiceName());
633 return false;
634 }
635 }
636
637 // once the flows are removed add the default one back
638 // (only if the port is ENABLED and still present on the device)
639 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
640
641 // NOTE we remove the subscriber when the port goes down
642 // but in that case we don't need to add default eapol
643 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
644 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800645 }
646 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200647 // FIXME check the return status of the flow and return accordingly
648 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
649 return true;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700650 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700651 return true;
652 }
653
654 @Override
655 public boolean hasDefaultEapol(Port port) {
656 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700657 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
Gustavo Silva34664e82022-09-19 13:28:09 -0300658 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700659 }
660
661 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
662 try {
663 cpStatusReadLock.lock();
664 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
665 OltPortStatus status = cpStatus.get(sk);
666 return status;
667 } finally {
668 cpStatusReadLock.unlock();
669 }
670 }
671
672 public boolean isDefaultEapolPendingRemoval(Port port) {
673 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
674 if (log.isTraceEnabled()) {
675 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
676 status, portWithName(port), defaultEapolUniTag);
677 }
678 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
679 }
680
681 @Override
682 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
683 OltPortStatus status = getOltPortStatus(port, uti);
684 if (log.isTraceEnabled()) {
685 log.trace("Status during DHCP flow check {} for port {} and service {}",
686 status, portWithName(port), uti.getServiceName());
687 }
688 return status != null &&
689 (status.dhcpStatus == OltFlowsStatus.ADDED ||
690 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
691 }
692
693 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000694 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
695 OltPortStatus status = getOltPortStatus(port, uti);
696 if (log.isTraceEnabled()) {
697 log.trace("Status during PPPoE flow check {} for port {} and service {}",
698 status, portWithName(port), uti.getServiceName());
699 }
700 return status != null &&
701 (status.pppoeStatus == OltFlowsStatus.ADDED ||
702 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
703 }
704
705 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700706 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
707
708 OltPortStatus status = getOltPortStatus(port, uti);
709 if (log.isTraceEnabled()) {
710 log.trace("Status during subscriber flow check {} for port {} and service {}",
711 status, portWithName(port), uti.getServiceName());
712 }
713 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
714 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
715 }
716
Andrea Campanella87241ae2022-03-11 11:20:24 +0100717 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti, boolean enableEapol) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800718 OltPortStatus status = getOltPortStatus(port, uti);
719 if (log.isTraceEnabled()) {
720 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
721 status, portWithName(port), uti);
722 }
Andrea Campanella87241ae2022-03-11 11:20:24 +0100723 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE ||
724 (enableEapol && status.subscriberEapolStatus == OltFlowsStatus.PENDING_REMOVE) ||
725 (uti.getIsDhcpRequired() && status.dhcpStatus == OltFlowsStatus.PENDING_REMOVE));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800726 }
727
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700728 @Override
729 public void purgeDeviceFlows(DeviceId deviceId) {
730 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800731 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700732
733 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800734 if (log.isTraceEnabled()) {
735 log.trace("Clearing cp status from device {}", deviceId);
736 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700737 try {
738 cpStatusWriteLock.lock();
739 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
740 while (iter.hasNext()) {
741 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
742 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
743 cpStatus.remove(entry.getKey());
744 }
745 }
746 } finally {
747 cpStatusWriteLock.unlock();
748 }
749
750 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800751 if (log.isTraceEnabled()) {
752 log.trace("Clearing provisioned subscribers from device {}", deviceId);
753 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700754 try {
755 provisionedSubscribersWriteLock.lock();
756 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
757 while (iter.hasNext()) {
758 Map.Entry<ServiceKey, Boolean> entry = iter.next();
759 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
760 provisionedSubscribers.remove(entry.getKey());
761 }
762 }
763 } finally {
764 provisionedSubscribersWriteLock.unlock();
765 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800766 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700767 }
768
769 @Override
770 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800771 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700772 try {
773 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800774 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700775 } finally {
776 provisionedSubscribersReadLock.unlock();
777 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800778
779 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
780 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
781 return true;
782 }
783 }
784 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700785 }
786
787 @Override
788 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
789 try {
790 provisionedSubscribersReadLock.lock();
791 Boolean provisioned = provisionedSubscribers.get(sk);
792 if (provisioned == null || !provisioned) {
793 return false;
794 }
795 } finally {
796 provisionedSubscribersReadLock.unlock();
797 }
798 return true;
799 }
800
801 @Override
802 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
803 try {
804 provisionedSubscribersWriteLock.lock();
805 provisionedSubscribers.put(sk, status);
806 } finally {
807 provisionedSubscribersWriteLock.unlock();
808 }
809 }
810
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800811 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700812 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
813
814 // create a subscriberKey for the EAPOL flow
815 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
Andrea Campanella87241ae2022-03-11 11:20:24 +0100816 OltFlowsStatus status = action == FlowOperation.ADD ?
817 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700818 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +0100819 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE,
820 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700821
Andrea Campanella87241ae2022-03-11 11:20:24 +0100822 } else {
823 updateConnectPointStatus(sk, OltFlowsStatus.NONE, status, OltFlowsStatus.NONE,
824 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700825 }
826
827 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
828 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
829
830 int techProfileId = getDefaultTechProfileId(sub.port);
831 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
832
833 // in the delete case the meter should still be there as we remove
834 // the meters only if no flows are pointing to them
835 if (meterId == null) {
836 log.debug("MeterId is null for BandwidthProfile {} on device {}",
837 bandwidthProfile, sub.device.id());
838 return false;
839 }
840
841 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
842 if (oltMeterId == null) {
843 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
844 oltBandwidthProfile, sub.device.id());
845 return false;
846 }
847
848 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
849 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
850
851 FilteringObjective.Builder eapolAction;
852
853 if (action == FlowOperation.ADD) {
854 eapolAction = filterBuilder.permit();
855 } else if (action == FlowOperation.REMOVE) {
856 eapolAction = filterBuilder.deny();
857 } else {
858 log.error("Operation {} not supported", action);
859 return false;
860 }
861
862 FilteringObjective.Builder baseEapol = eapolAction
863 .withKey(Criteria.matchInPort(sub.port.number()))
864 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
865
866 // NOTE we only need to add the treatment to install the flow,
867 // we can remove it based in the match
868 FilteringObjective.Builder eapol;
869
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530870 // Setting VlanId.NONE as cvlan in the metadata as the packet will be single tagged
871 // and cvlan should not be filled.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700872 TrafficTreatment treatment = treatmentBuilder
873 .meter(meterId)
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530874 .writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530875 VlanId.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700876 techProfileId, oltMeterId), 0)
877 .setOutput(PortNumber.CONTROLLER)
878 .pushVlan()
879 .setVlanId(vlanId)
880 .build();
881 eapol = baseEapol
882 .withMeta(treatment);
883
884 FilteringObjective eapolObjective = eapol
885 .fromApp(appId)
886 .withPriority(MAX_PRIORITY)
887 .add(new ObjectiveContext() {
888 @Override
889 public void onSuccess(Objective objective) {
890 log.info("EAPOL flow objective {} for {}",
891 completeFlowOpToString(action), portWithName(sub.port));
892 if (log.isTraceEnabled()) {
893 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
894 }
895 }
896
897 @Override
898 public void onError(Objective objective, ObjectiveError error) {
899 log.error("Cannot {} eapol flow for {} : {}", action,
900 portWithName(sub.port), error);
901
902 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
903 updateConnectPointStatus(sk,
Andrea Campanella87241ae2022-03-11 11:20:24 +0100904 OltFlowsStatus.ERROR, null, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700905 }
906 }
907 });
908
909 flowObjectiveService.filter(sub.device.id(), eapolObjective);
910
911 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
912 return true;
913 }
914
915 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800916 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700917 SubscriberAndDeviceInformation si) {
918 if (!enableEapol) {
919 return true;
920 }
921 // TODO verify we need an EAPOL flow for EACH service
922 AtomicBoolean success = new AtomicBoolean(true);
923 si.uniTagList().forEach(u -> {
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200924 //Always act on the eapol flow
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700925 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
926 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
927 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
928 u.getUpstreamOltBandwidthProfile(),
929 action, u.getPonCTag())) {
930 //
Andrea Campanella87241ae2022-03-11 11:20:24 +0100931 log.error("Failed to {} EAPOL with subscriber tags", action);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700932 //TODO this sets it for all services, maybe some services succeeded.
933 success.set(false);
934 }
935 });
936 return success.get();
937 }
938
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800939 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700940 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
941 if (uti.getIsIgmpRequired()) {
942 DeviceId deviceId = sub.device.id();
943 // if we reached here a meter already exists
944 MeterId meterId = oltMeterService
945 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
946 MeterId oltMeterId = oltMeterService
947 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
948
949 processIgmpFilteringObjectives(deviceId, sub.port,
950 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
951 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
952 }
953 });
954 }
955
956 private boolean checkSadisRunning() {
957 if (bpService == null) {
958 log.warn("Sadis is not running");
959 return false;
960 }
961 return true;
962 }
963
964 private int getDefaultTechProfileId(Port port) {
965 if (!checkSadisRunning()) {
966 return defaultTechProfileId;
967 }
968 if (port != null) {
969 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
970 if (info != null && info.uniTagList().size() == 1) {
971 return info.uniTagList().get(0).getTechnologyProfileId();
972 }
973 }
974 return defaultTechProfileId;
975 }
976
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700977 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
978 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
979
980 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
981 .withKey(Criteria.matchInPort(port.number()))
982 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
983 .withMeta(DefaultTrafficTreatment.builder()
984 .setOutput(PortNumber.CONTROLLER).build())
985 .fromApp(appId)
986 .withPriority(MAX_PRIORITY)
987 .add(new ObjectiveContext() {
988 @Override
989 public void onSuccess(Objective objective) {
990 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
991 }
992
993 @Override
994 public void onError(Objective objective, ObjectiveError error) {
995 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
996 error);
997 }
998 });
999
1000 flowObjectiveService.filter(deviceId, lldp);
1001 }
1002
1003 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
1004 FlowOperation action,
1005 SubscriberAndDeviceInformation si) {
1006 si.uniTagList().forEach(uti -> {
1007
1008 if (!uti.getIsDhcpRequired()) {
1009 return;
1010 }
1011
1012 // if it's an ADD skip if flows are there,
1013 // if it's a DELETE skip if flows are not there
1014 boolean hasFlows = hasDhcpFlows(port, uti);
1015 if (action == FlowOperation.ADD && hasFlows ||
1016 action == FlowOperation.REMOVE && !hasFlows) {
1017 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1018 uti.getServiceName(), hasFlows);
1019 return;
1020 }
1021
1022 log.info("{} DHCP flows for subscriber on {} and service {}",
1023 flowOpToString(action), portWithName(port), uti.getServiceName());
1024
1025 // if we reached here a meter already exists
1026 MeterId meterId = oltMeterService
1027 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1028 MeterId oltMeterId = oltMeterService
1029 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1030
1031 if (enableDhcpV4) {
1032 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1033 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1034 uti);
1035 }
1036 if (enableDhcpV6) {
1037 log.error("DHCP V6 not supported for subscribers");
1038 }
1039 });
1040 }
1041
yasin sapli0823c932022-01-26 11:26:09 +00001042 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1043 FlowOperation action,
1044 SubscriberAndDeviceInformation si) {
1045 si.uniTagList().forEach(uti -> {
1046
1047 if (!uti.getIsPppoeRequired()) {
1048 return;
1049 }
1050
1051 // if it's an ADD skip if flows are there,
1052 // if it's a DELETE skip if flows are not there
1053 boolean hasFlows = hasPppoeFlows(port, uti);
1054 if (action == FlowOperation.ADD && hasFlows ||
1055 action == FlowOperation.REMOVE && !hasFlows) {
1056 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1057 uti.getServiceName(), hasFlows);
1058 return;
1059 }
1060
1061 log.info("{} PPPoE flows for subscriber on {} and service {}",
1062 flowOpToString(action), portWithName(port), uti.getServiceName());
1063
1064 // if we reached here a meter already exists
1065 MeterId meterId = oltMeterService
1066 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1067 MeterId oltMeterId = oltMeterService
1068 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1069
1070 if (enablePppoe) {
1071 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1072 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1073 (byte) uti.getUsPonCTagPriority());
1074 }
1075 });
1076 }
1077
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001078 // FIXME return boolean, if this fails we need to retry
1079 protected void handleSubscriberDataFlows(Device device, Port port,
1080 FlowOperation action,
1081 SubscriberAndDeviceInformation si, String multicastServiceName) {
1082
1083 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001084 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001085 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1086 si.id(), portWithName(port));
1087 return;
1088 }
1089 si.uniTagList().forEach(uti -> {
1090
1091 boolean hasFlows = hasSubscriberFlows(port, uti);
1092 if (action == FlowOperation.ADD && hasFlows ||
1093 action == FlowOperation.REMOVE && !hasFlows) {
1094 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1095 uti.getServiceName(), hasFlows);
1096 return;
1097 }
1098
1099 if (multicastServiceName.equals(uti.getServiceName())) {
1100 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1101 "dataplane flows are not needed",
1102 uti.getServiceName(), si.id(), portWithName(port));
1103 return;
1104 }
1105
1106 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1107 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001108 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1109 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1110 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001111 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001112
1113 // upstream flows
1114 MeterId usMeterId = oltMeterService
1115 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1116 MeterId oltUsMeterId = oltMeterService
1117 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301118
1119 if (FttbUtils.isFttbService(uti)) {
1120 processFttbUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action,
1121 usMeterId, oltUsMeterId, uti, si);
1122 } else {
1123 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1124 oltUsMeterId, uti);
1125 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001126
1127 // downstream flows
1128 MeterId dsMeterId = oltMeterService
1129 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1130 MeterId oltDsMeterId = oltMeterService
1131 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301132
1133 if (FttbUtils.isFttbService(uti)) {
1134 processFttbDownstreamDataFilteringObjects(device.id(), port, nniPort.get(),
1135 action, dsMeterId, oltDsMeterId, uti, si);
1136 } else {
1137 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1138 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1139 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001140 });
1141 }
1142
1143 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1144 FlowOperation action, FlowDirection direction,
1145 int udpSrc, int udpDst, EthType ethType, byte protocol,
1146 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1147 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1148 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1149
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301150 String serviceName = uti.getServiceName();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001151
1152 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1153 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001154 updateConnectPointStatus(sk, null, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001155
1156 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1157 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1158
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001159 if (meterId != null) {
1160 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001161 }
1162
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001163 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001164 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001165 .addCondition(Criteria.matchEthType(ethType))
1166 .addCondition(Criteria.matchIPProtocol(protocol))
1167 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1168 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001169 .fromApp(appId)
1170 .withPriority(MAX_PRIORITY);
1171
Andrea Campanella0e34f562020-06-11 10:47:10 +02001172 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001173 if (direction == FlowDirection.UPSTREAM) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301174 if (serviceName != null && serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC)) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301175 FttbUtils.addUpstreamDhcpCondition(dhcpBuilder, uti);
1176 FttbUtils.addUpstreamDhcpTreatment(treatmentBuilder, uti);
1177 } else {
1178 treatmentBuilder.setVlanId(uti.getPonCTag());
1179 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1180 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
1181 }
1182 if (uti.getUsPonCTagPriority() != -1) {
1183 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
1184 }
Andrea Campanella0e34f562020-06-11 10:47:10 +02001185 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301186 } else if (direction == FlowDirection.DOWNSTREAM) {
1187 // Down stream DHCP vid to be matched if OLT Sadis info contains Vlan id in nniDhcpTrapVid.
1188 Device device = deviceService.getDevice(deviceId);
1189 SubscriberAndDeviceInformation oltSub = subsService.get(device.serialNumber());
1190 VlanId nniDhcpTrapVid = oltSub.nniDhcpTrapVid();
1191
1192 if (nniDhcpTrapVid != null && !VlanId.vlanId(VlanId.NO_VID).equals(nniDhcpTrapVid)) {
1193 dhcpBuilder.addCondition(Criteria.matchVlanId(nniDhcpTrapVid));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001194 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001195 }
1196
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301197 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301198 // 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 +05301199 treatmentBuilder.writeMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301200 OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301201 uti.getTechnologyProfileId(), oltMeterId), 0);
1202 }
1203
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001204 dhcpBuilder.withMeta(treatmentBuilder
1205 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001206
1207
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001208 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001209 @Override
1210 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001211 log.info("{} DHCP {} filter for {}.",
1212 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1213 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001214 }
1215
1216 @Override
1217 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001218 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001219 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1220 portWithName(port),
1221 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001222 error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001223 updateConnectPointStatus(sk, null, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001224 }
1225 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001226 flowObjectiveService.filter(deviceId, dhcpUpstream);
1227 }
1228
1229 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1230 FlowOperation action, FlowDirection direction,
1231 MeterId meterId, MeterId oltMeterId, int techProfileId,
1232 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1233
1234 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1235 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1236 if (direction == FlowDirection.UPSTREAM) {
1237
1238 if (techProfileId != NONE_TP_ID) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301239 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(null,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001240 techProfileId, oltMeterId), 0);
1241 }
1242
1243
1244 if (meterId != null) {
1245 treatmentBuilder.meter(meterId);
1246 }
1247
1248 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1249 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1250 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001251 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1252 treatmentBuilder.setVlanId(cTag);
1253 }
1254
1255 if (vlanPcp != -1) {
1256 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1257 }
1258 }
1259
1260 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1261
1262 FilteringObjective igmp = filterBuilder
1263 .withKey(Criteria.matchInPort(port.number()))
1264 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1265 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1266 .withMeta(treatmentBuilder
1267 .setOutput(PortNumber.CONTROLLER).build())
1268 .fromApp(appId)
1269 .withPriority(MAX_PRIORITY)
1270 .add(new ObjectiveContext() {
1271 @Override
1272 public void onSuccess(Objective objective) {
1273 log.info("Igmp filter for {} {}.", portWithName(port), action);
1274 }
1275
1276 @Override
1277 public void onError(Objective objective, ObjectiveError error) {
1278 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1279 error);
1280 }
1281 });
1282
1283 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001284
1285 }
1286
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001287 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1288 FlowOperation action, FlowDirection direction,
1289 MeterId meterId, MeterId oltMeterId, int techProfileId,
1290 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001291
1292 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1293 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001294
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001295 if (meterId != null) {
1296 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001297 }
1298
1299 if (techProfileId != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301300 // Setting VlanId.NONE as cvlan as the packet will be single tagged and cvlan should not be filled.
1301 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
1302 techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001303 }
1304
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001305 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1306 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001307 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001308 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1309 .fromApp(appId)
1310 .withPriority(10000);
1311
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001312 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001313 treatmentBuilder.setVlanId(cTag);
1314 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1315 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1316 }
1317 if (vlanPcp != null) {
1318 treatmentBuilder.setVlanPcp(vlanPcp);
1319 }
1320 }
1321 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1322
1323 FilteringObjective pppoed = pppoedBuilder
1324 .add(new ObjectiveContext() {
1325 @Override
1326 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001327 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001328 }
1329
1330 @Override
1331 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001332 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1333 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001334 }
1335 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001336 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001337 }
1338
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001339 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1340 FlowOperation action,
1341 MeterId upstreamMeterId,
1342 MeterId upstreamOltMeterId,
1343 UniTagInformation uti) {
1344 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001345 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001346 .matchInPort(port.number())
1347 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001348 .build();
1349
Andrea Campanella327c5722020-01-30 11:34:13 +01001350 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1351 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001352 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001353 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001354 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001355 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001356 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1357 treatmentBuilder.popVlan();
1358 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001359
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001360 if (uti.getUsPonCTagPriority() != -1) {
1361 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001362
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001363 }
1364
1365 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001366 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001367
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001368 if (uti.getUsPonSTagPriority() != -1) {
1369 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001370 }
1371
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001372 treatmentBuilder.setOutput(nniPort.number())
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301373 .writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001374 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001375
yasin saplib4b8ee12021-06-13 18:25:20 +00001376 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1377
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001378 if (upstreamMeterId != null) {
1379 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001380 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1381 }
1382 if (upstreamOltMeterId != null) {
1383 treatmentBuilder.meter(upstreamOltMeterId);
1384 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001385 }
1386
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001387 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1388 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001389 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001390
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301391 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1392 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001393 }
1394
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001395 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1396 FlowOperation action,
1397 MeterId downstreamMeterId,
1398 MeterId downstreamOltMeterId,
1399 UniTagInformation uti,
1400 MacAddress macAddress) {
1401 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001402 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001403 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001404 .matchVlanId(uti.getPonSTag())
1405 .matchInPort(nniPort.number())
1406 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001407
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001408 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1409 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001410 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001411
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001412 if (uti.getDsPonCTagPriority() != -1) {
1413 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001414 }
1415
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001416 if (macAddress != null) {
1417 selectorBuilder.matchEthDst(macAddress);
1418 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001419
1420 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1421 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001422 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001423
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301424 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001425 uti.getTechnologyProfileId(),
1426 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001427
Andrea Campanella981e86c2021-03-12 11:35:33 +01001428 // Upstream pbit is used to remark inner vlan pbit.
1429 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1430 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1431 // all pbit acceptance are not widely supported by vendors even though present in
1432 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001433 if (uti.getUsPonCTagPriority() != -1) {
1434 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001435 }
1436
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001437 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1438 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1439 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001440 }
1441
yasin saplib4b8ee12021-06-13 18:25:20 +00001442 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1443
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001444 if (downstreamMeterId != null) {
1445 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001446 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001447 }
1448
yasin saplib4b8ee12021-06-13 18:25:20 +00001449 if (downstreamOltMeterId != null) {
1450 treatmentBuilder.meter(downstreamOltMeterId);
1451 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1452 }
1453
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001454 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1455 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001456
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301457 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1458 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanella600d2e22020-06-22 11:00:31 +02001459 }
1460
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001461 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1462 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001463 Integer priority,
1464 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001465 return DefaultForwardingObjective.builder()
1466 .withFlag(ForwardingObjective.Flag.VERSATILE)
1467 .withPriority(priority)
1468 .makePermanent()
1469 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001470 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001471 .fromApp(appId)
1472 .withTreatment(treatment);
1473 }
1474
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001475 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1476 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1477 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001478
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001479 si.uniTagList().forEach(uniTagInfo -> {
1480 if (uniTagInfo.getEnableMacLearning()) {
1481 requiresMacLearning.set(true);
1482 }
1483 });
1484
1485 return requiresMacLearning.get();
1486 }
1487
1488 /**
1489 * Checks whether the subscriber has the MacAddress configured or discovered.
1490 *
1491 * @param deviceId DeviceId for this subscriber
1492 * @param port Port for this subscriber
1493 * @param si SubscriberAndDeviceInformation
1494 * @return boolean
1495 */
1496 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1497 AtomicBoolean isConfigured = new AtomicBoolean();
1498 isConfigured.set(true);
1499
1500 si.uniTagList().forEach(uniTagInfo -> {
1501 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301502 boolean configureMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001503 boolean discoveredMac = false;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301504
1505 final VlanId vlan;
1506
1507 if (FttbUtils.isFttbDpuOrAncpService(uniTagInfo)) {
1508 // Using S tag, as C tag is replaced by Stag by ONU.
1509 vlan = uniTagInfo.getPonSTag();
1510 } else {
1511 vlan = uniTagInfo.getPonCTag();
1512 }
1513
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001514 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301515 .stream().filter(host -> host.vlan().equals(vlan)).findFirst();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001516 if (optHost.isPresent() && optHost.get().mac() != null) {
1517 discoveredMac = true;
1518 }
1519 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1520 log.debug("Awaiting for macAddress on {} for service {}",
1521 portWithName(port), uniTagInfo.getServiceName());
1522 isConfigured.set(false);
1523 }
1524 });
1525
1526 return isConfigured.get();
1527 }
1528
1529 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301530 boolean configuredMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001531 if (configuredMac) {
1532 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1533 } else if (uniTagInfo.getEnableMacLearning()) {
1534 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1535 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1536 if (optHost.isPresent() && optHost.get().mac() != null) {
1537 return optHost.get().mac();
1538 }
1539 }
1540 return null;
1541 }
1542
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001543 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001544 OltFlowsStatus subscriberEapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001545 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1546 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001547 if (log.isTraceEnabled()) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001548 log.trace("Updating cpStatus {} with values: eapolFlow={}, " +
1549 "subscriberEapolStatus={}, subscriberFlows={}, dhcpFlow={}",
1550 key, eapolStatus, subscriberEapolStatus, subscriberFlowsStatus, dhcpStatus);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001551 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001552 try {
1553 cpStatusWriteLock.lock();
1554 OltPortStatus status = cpStatus.get(key);
1555
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001556
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001557 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001558 // if we don't have status for the connectPoint
1559 // and we're only updating status to PENDING_REMOVE or ERROR
1560 // do not create it. This is because this case will only happen when a device is removed
1561 // and it's status cleaned
1562 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1563 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1564 statusesToIgnore.add(OltFlowsStatus.ERROR);
1565
1566 if (
1567 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1568 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1569 ) {
1570 if (log.isTraceEnabled()) {
1571 log.trace("Ignoring cpStatus update as status is meaningless");
1572 }
1573 return;
1574 }
1575
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001576 status = new OltPortStatus(
1577 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001578 subscriberEapolStatus != null ? subscriberEapolStatus : OltFlowsStatus.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001579 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001580 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1581 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001582 );
1583 } else {
1584 if (eapolStatus != null) {
1585 status.defaultEapolStatus = eapolStatus;
1586 }
1587 if (subscriberFlowsStatus != null) {
1588 status.subscriberFlowsStatus = subscriberFlowsStatus;
1589 }
1590 if (dhcpStatus != null) {
1591 status.dhcpStatus = dhcpStatus;
1592 }
1593 }
1594
1595 cpStatus.put(key, status);
1596 } finally {
1597 cpStatusWriteLock.unlock();
1598 }
1599 }
1600
1601 protected class InternalFlowListener implements FlowRuleListener {
Gustavo Silva89e2f042022-08-01 09:58:04 -03001602
1603 private OltFlowServiceInterface oltFlowService;
1604
1605 public InternalFlowListener(OltFlowServiceInterface oltFlowService) {
1606 this.oltFlowService = oltFlowService;
1607 }
1608
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001609 @Override
1610 public void event(FlowRuleEvent event) {
1611 if (appId.id() != (event.subject().appId())) {
1612 return;
1613 }
1614
1615 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1616 if (log.isTraceEnabled()) {
1617 log.trace("ignoring flow event {} " +
1618 "as not leader for {}", event, event.subject().deviceId());
1619 }
1620 return;
1621 }
1622
1623 switch (event.type()) {
1624 case RULE_ADDED:
1625 case RULE_REMOVED:
Andrea Campanella40d2b342022-02-04 18:13:37 +01001626 DeviceId deviceId = event.subject().deviceId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001627 Port port = getCpFromFlowRule(event.subject());
1628 if (port == null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001629 log.warn("Port is gone in ONOS, " +
1630 "manually creating it {}", event.subject());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301631 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(event.subject());
Andrea Campanella40d2b342022-02-04 18:13:37 +01001632 cpStatusReadLock.lock();
1633 Optional<ServiceKey> keyWithPort = cpStatus.keySet()
1634 .stream().filter(key -> key.getPort().connectPoint()
1635 .deviceId().equals(deviceId)
1636 && key.getPort().connectPoint().port()
1637 .equals(inPort)).findFirst();
1638 cpStatusReadLock.unlock();
1639 if (keyWithPort.isPresent()) {
1640 port = new DefaultPort(deviceService.getDevice(deviceId),
1641 inPort, false,
1642 DefaultAnnotations.builder()
1643 .set(AnnotationKeys.PORT_NAME,
1644 keyWithPort.get().getPort().name())
1645 .build());
1646 } else {
1647 log.warn("Can't find corresponding status for {}/{}", deviceId, inPort);
1648 return;
1649 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001650 }
1651 if (log.isTraceEnabled()) {
1652 log.trace("flow event {} on cp {}: {}", event.type(),
1653 portWithName(port), event.subject());
1654 }
1655 updateCpStatus(event.type(), port, event.subject());
1656 return;
1657 case RULE_ADD_REQUESTED:
1658 case RULE_REMOVE_REQUESTED:
1659 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1660 return;
1661 default:
1662 return;
1663 }
1664 }
1665
1666 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301667 OltFlowsStatus status = OltFlowServiceUtils.flowRuleStatusToOltFlowStatus(type);
1668 if (OltFlowServiceUtils.isDefaultEapolFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001669 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1670 defaultEapolUniTag);
1671 if (log.isTraceEnabled()) {
1672 log.trace("update defaultEapolStatus {} on {}", status, sk);
1673 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001674 updateConnectPointStatus(sk, status, null, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301675 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001676 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
1677 if (sk == null) {
1678 return;
1679 }
1680 if (log.isTraceEnabled()) {
1681 log.trace("update subscriberEapolStatus {} on {}", status, sk);
1682 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +02001683 updateConnectPointStatus(sk, null, status, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301684 } else if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001685 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001686 if (sk == null) {
1687 return;
1688 }
1689 if (log.isTraceEnabled()) {
1690 log.trace("update dhcpStatus {} on {}", status, sk);
1691 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001692 updateConnectPointStatus(sk, null, null, null, status, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301693 } else if (OltFlowServiceUtils.isPppoeFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001694 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
yasin sapli0823c932022-01-26 11:26:09 +00001695 if (sk == null) {
1696 return;
1697 }
1698 if (log.isTraceEnabled()) {
1699 log.trace("update pppoeStatus {} on {}", status, sk);
1700 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001701 updateConnectPointStatus(sk, null, null, null, null, status);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301702 } else if (OltFlowServiceUtils.isDataFlow(flowRule)) {
1703 PortNumber number = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Andrea Campanella40d2b342022-02-04 18:13:37 +01001704 if (number == null) {
1705 log.error("Can't capture the port number from flow {}", flowRule);
1706 return;
1707 }
1708 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), number)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001709 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1710 return;
1711 }
1712
Andrea Campanella40d2b342022-02-04 18:13:37 +01001713 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001714 if (sk == null) {
1715 return;
1716 }
1717 if (log.isTraceEnabled()) {
1718 log.trace("update dataplaneStatus {} on {}", status, sk);
1719 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001720 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001721 }
1722 }
1723
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001724
Andrea Campanella87241ae2022-03-11 11:20:24 +01001725
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001726 private Port getCpFromFlowRule(FlowRule flowRule) {
1727 DeviceId deviceId = flowRule.deviceId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301728 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001729 if (inPort != null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001730 return deviceService.getPort(deviceId, inPort);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001731 }
1732 return null;
1733 }
1734
Andrea Campanella40d2b342022-02-04 18:13:37 +01001735 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule, Port flowPort) {
Gustavo Silva89e2f042022-08-01 09:58:04 -03001736 AccessDevicePort accessDevicePort = new AccessDevicePort(flowPort);
1737 SubscriberAndDeviceInformation si = getProgrammedSubscriber(oltFlowService, accessDevicePort);
1738 if (si == null) {
1739 log.debug("si not found in programmedSubscribers, getting it from sadis.");
1740 si = subsService.get(getPortName(flowPort));
1741 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001742
1743 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1744 if (si == null && !isNni) {
1745 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1746 return null;
1747 }
1748
1749 if (isNni) {
1750 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1751 }
1752
1753 Optional<UniTagInformation> found = Optional.empty();
1754 VlanId flowVlan = null;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301755 if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001756 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1757 L2ModificationInstruction.ModVlanIdInstruction instruction =
1758 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1759 flowVlan = instruction.vlanId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301760 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001761 // we need to make a special case for EAPOL as in the ATT workflow EAPOL flows don't match on tags
1762 L2ModificationInstruction.ModVlanIdInstruction instruction =
1763 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(2);
1764 flowVlan = instruction.vlanId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001765 } else {
1766 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1767 VlanIdCriterion vlanIdCriterion =
1768 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1769 if (vlanIdCriterion == null) {
1770 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1771 return null;
1772 }
1773 flowVlan = vlanIdCriterion.vlanId();
1774 }
1775
1776 VlanId finalFlowVlan = flowVlan;
1777 found = si.uniTagList().stream().filter(uti ->
1778 uti.getPonCTag().equals(finalFlowVlan) ||
1779 uti.getPonSTag().equals(finalFlowVlan) ||
1780 uti.getUniTagMatch().equals(finalFlowVlan)
1781 ).findFirst();
1782
1783
1784 if (found.isEmpty()) {
1785 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1786 }
1787
1788 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1789
1790 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001791 }
1792
1793 protected void bindSadisService(SadisService service) {
1794 this.subsService = service.getSubscriberInfoService();
1795 this.bpService = service.getBandwidthProfileService();
1796 log.info("Sadis service is loaded");
1797 }
1798
1799 protected void unbindSadisService(SadisService service) {
1800 this.subsService = null;
1801 this.bpService = null;
1802 log.info("Sadis service is unloaded");
1803 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301804
1805 private void processFttbUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1806 FlowOperation action,
1807 MeterId upstreamMeterId,
1808 MeterId upstreamOltMeterId,
1809 UniTagInformation uti,
1810 SubscriberAndDeviceInformation si) {
1811 String serviceName = uti.getServiceName();
1812 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1813 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1814 .matchInPort(port.number())
1815 .matchVlanId(uti.getPonCTag());
1816
1817 if (uti.getUsPonCTagPriority() != -1) {
1818 selectorBuilder.matchVlanPcp((byte) uti.getUsPonCTagPriority());
1819 }
1820
1821 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1822
1823 treatmentBuilder.setVlanId(uti.getPonSTag());
1824 if (uti.getUsPonSTagPriority() != -1) {
1825 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
1826 }
1827
1828 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1829 annotationBuilder.set(FTTB_SERVICE_NAME, serviceName);
1830 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_UPSTREAM);
1831
1832 if (upstreamMeterId != null) {
1833 treatmentBuilder.meter(upstreamMeterId);
1834 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1835 }
1836 if (upstreamOltMeterId != null) {
1837 treatmentBuilder.meter(upstreamOltMeterId);
1838 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
1839 }
1840
1841 VlanId innerVlan = null;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001842 treatmentBuilder.setOutput(nniPort.number());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301843 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
amit.ghosh74a4bb22022-06-14 11:34:52 +02001844 fttbMacAddressesWriteLock.lock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301845 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
amit.ghosh74a4bb22022-06-14 11:34:52 +02001846 hostService, si, deviceId, port, fttbMacAddresses);
1847 fttbMacAddressesWriteLock.unlock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301848
1849 if (mac == null) {
1850 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1851 port, uti.getPonSTag(), serviceName);
1852 return;
1853 }
1854
1855 selectorBuilder.matchEthSrc(mac);
Andrea Campanella7ef88992022-05-17 12:38:00 +02001856
1857 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
1858 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301859
1860 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
Andrea Campanella7ef88992022-05-17 12:38:00 +02001861 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1862 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301863 }
1864
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301865 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1866 treatmentBuilder.build(), MIN_PRIORITY,
1867 annotationBuilder.build());
1868
1869 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1870 processForwardingRule(action, flowBuilder, context, deviceId);
1871 }
1872
1873 private void processFttbDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1874 FlowOperation action,
1875 MeterId downstreamMeterId,
1876 MeterId downstreamOltMeterId,
1877 UniTagInformation uti, SubscriberAndDeviceInformation si) {
1878 String serviceName = uti.getServiceName();
1879 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1880 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
1881 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1882 .matchVlanId(uti.getPonSTag())
1883 .matchInPort(nniPort.number());
1884
1885 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1886 .setVlanId(uti.getPonCTag())
1887 .setOutput(port.number());
1888
1889 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1890 annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
1891 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_DOWNSTREAM);
1892
1893 if (downstreamMeterId != null) {
1894 treatmentBuilder.meter(downstreamMeterId);
1895 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
1896 }
1897
1898 if (downstreamOltMeterId != null) {
1899 treatmentBuilder.meter(downstreamOltMeterId);
1900 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1901 }
1902
1903 VlanId innerVlan = null;
1904
1905 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
amit.ghosh74a4bb22022-06-14 11:34:52 +02001906 fttbMacAddressesWriteLock.lock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301907 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
amit.ghosh74a4bb22022-06-14 11:34:52 +02001908 hostService, si, deviceId, port, fttbMacAddresses);
1909 fttbMacAddressesWriteLock.unlock();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301910
1911 if (mac == null) {
1912 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1913 port, uti.getPonSTag(), serviceName);
1914 return;
1915 }
1916
1917 selectorBuilder.matchEthDst(mac);
1918 innerVlan = VlanId.NONE;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001919 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1920 uti.getTechnologyProfileId(),
1921 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301922
1923 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
amit.ghosh4f0910e2022-06-20 15:53:21 +02001924 selectorBuilder.matchMetadata(uti.getPonSTag().toShort());
Andrea Campanella7ef88992022-05-17 12:38:00 +02001925 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1926 uti.getTechnologyProfileId(),
1927 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301928 }
1929
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301930 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1931 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
1932
1933 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1934 processForwardingRule(action, flowBuilder, context, deviceId);
1935 }
1936
1937 private ObjectiveContext getSubscriberFlowBuilderContext(ServiceKey sk, FlowOperation action,
1938 FlowDirection flowDirection) {
1939 ObjectiveContext context = new ObjectiveContext() {
1940 @Override
1941 public void onSuccess(Objective objective) {
1942 log.info("{} {} Data plane filter for {}.",
1943 completeFlowOpToString(action), flowDirection, sk);
1944 }
1945
1946 @Override
1947 public void onError(Objective objective, ObjectiveError error) {
1948 log.info("{} Data plane filter for {} failed {} because {}.",
1949 flowDirection, sk, action, error);
1950 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
1951 }
1952 };
1953
1954 return context;
1955 }
1956
1957 private void processForwardingRule(FlowOperation action, DefaultForwardingObjective.Builder flowBuilder,
1958 ObjectiveContext context, DeviceId deviceId) {
1959 ForwardingObjective flow = null;
1960 if (action == FlowOperation.ADD) {
1961 flow = flowBuilder.add(context);
1962 } else if (action == FlowOperation.REMOVE) {
1963 flow = flowBuilder.remove(context);
1964 } else {
1965 log.error("Flow action not supported: {}", action);
1966 }
1967
1968 if (flow != null) {
1969 if (log.isTraceEnabled()) {
1970 log.trace("Forwarding rule {}", flow);
1971 }
1972 flowObjectiveService.forward(deviceId, flow);
1973 }
1974 }
amit.ghosh74a4bb22022-06-14 11:34:52 +02001975}