blob: 04ea49240a84200cb545d55aa5ff83dd00bba29c [file] [log] [blame]
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001/*
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07002 * Copyright 2021-present Open Networking Foundation
Andrea Campanellacbbb7952019-11-25 06:38:41 +00003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070016
Andrea Campanellacbbb7952019-11-25 06:38:41 +000017package org.opencord.olt.impl;
18
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070019import com.google.common.collect.ImmutableMap;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000020import org.onlab.packet.EthType;
21import org.onlab.packet.IPv4;
22import org.onlab.packet.IPv6;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.TpPort;
25import org.onlab.packet.VlanId;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000026import org.onlab.util.KryoNamespace;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000027import org.onlab.util.Tools;
28import org.onosproject.cfg.ComponentConfigService;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
Andrea Campanella40d2b342022-02-04 18:13:37 +010031import org.onosproject.net.AnnotationKeys;
yasin saplib4b8ee12021-06-13 18:25:20 +000032import org.onosproject.net.Annotations;
Matteo Scandolo3a037a32020-04-01 12:17:50 -070033import org.onosproject.net.ConnectPoint;
yasin saplib4b8ee12021-06-13 18:25:20 +000034import org.onosproject.net.DefaultAnnotations;
Andrea Campanella40d2b342022-02-04 18:13:37 +010035import org.onosproject.net.DefaultPort;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070036import org.onosproject.net.Device;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000037import org.onosproject.net.DeviceId;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070038import org.onosproject.net.Host;
39import org.onosproject.net.Port;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000040import org.onosproject.net.PortNumber;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070044import org.onosproject.net.flow.FlowRule;
45import org.onosproject.net.flow.FlowRuleEvent;
46import org.onosproject.net.flow.FlowRuleListener;
47import org.onosproject.net.flow.FlowRuleService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000048import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
50import org.onosproject.net.flow.criteria.Criteria;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070051import org.onosproject.net.flow.criteria.Criterion;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070052import org.onosproject.net.flow.criteria.VlanIdCriterion;
53import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000054import org.onosproject.net.flowobjective.DefaultFilteringObjective;
55import org.onosproject.net.flowobjective.DefaultForwardingObjective;
56import org.onosproject.net.flowobjective.FilteringObjective;
57import org.onosproject.net.flowobjective.FlowObjectiveService;
58import org.onosproject.net.flowobjective.ForwardingObjective;
59import org.onosproject.net.flowobjective.Objective;
60import org.onosproject.net.flowobjective.ObjectiveContext;
61import org.onosproject.net.flowobjective.ObjectiveError;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070062import org.onosproject.net.host.HostService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000063import org.onosproject.net.meter.MeterId;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000064import org.onosproject.store.serializers.KryoNamespaces;
65import org.onosproject.store.service.Serializer;
66import org.onosproject.store.service.StorageService;
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;
114import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
115import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
116import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
117import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
118import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
119import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
120import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
121import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
122import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
123import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
124import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
125import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
126import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
127import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
yasin sapli0823c932022-01-26 11:26:09 +0000128import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI;
129import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI_DEFAULT;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700130import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
131import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530132import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DIRECTION;
133import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DOWNSTREAM;
134import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_UPSTREAM;
135import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_ANCP_TRAFFIC;
136import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_DPU_MGMT_TRAFFIC;
137import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_NAME;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700138import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
139import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
140import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
141import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530142import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_SUBSCRIBER_TRAFFIC;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000143
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000144@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700145 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000146 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
147 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700148 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000149 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
yasin sapli0823c932022-01-26 11:26:09 +0000150 ENABLE_PPPOE_ON_NNI + ":Boolean=" + ENABLE_PPPOE_ON_NNI_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300151 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700152 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
153 // FIXME remove this option as potentially dangerous in production
154 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000155})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700156public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000157
158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
159 protected CoreService coreService;
160
161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700162 protected ComponentConfigService cfgService;
163
164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
165 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000166
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000167 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
168 bind = "bindSadisService",
169 unbind = "unbindSadisService",
170 policy = ReferencePolicy.DYNAMIC)
171 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700174 protected OltMeterServiceInterface oltMeterService;
175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
177 protected OltDeviceServiceInterface oltDeviceService;
178
179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
180 protected FlowRuleService flowRuleService;
181
182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
183 protected HostService hostService;
184
185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000186 protected DeviceService deviceService;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000189 protected StorageService storageService;
190
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700191 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
192 protected BaseInformationService<BandwidthProfileInformation> bpService;
193
194 private static final String APP_NAME = "org.opencord.olt";
195 protected ApplicationId appId;
196 private static final Integer MAX_PRIORITY = 10000;
197 private static final Integer MIN_PRIORITY = 1000;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530198 public static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700199 private static final int NONE_TP_ID = -1;
200 private static final String V4 = "V4";
201 private static final String V6 = "V6";
202 private final Logger log = LoggerFactory.getLogger(getClass());
203
204 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
205 .setServiceName("defaultEapol").build();
206 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
207 .setServiceName("nni")
208 .setTechnologyProfileId(NONE_TP_ID)
209 .setPonCTag(VlanId.NONE)
210 .setUniTagMatch(VlanId.ANY)
211 .setUsPonCTagPriority(-1)
212 .build();
213
214 /**
215 * Connect Point status map.
216 * Used to keep track of which cp has flows that needs to be removed when the status changes.
217 */
218 protected Map<ServiceKey, OltPortStatus> cpStatus;
219 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
220 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
221 private final Lock cpStatusReadLock = cpStatusLock.readLock();
222
223 /**
224 * This map contains the subscriber that have been provisioned by the operator.
225 * They may or may not have flows, depending on the port status.
226 * The map is used to define whether flows need to be provisioned when a port comes up.
227 */
228 protected Map<ServiceKey, Boolean> provisionedSubscribers;
229 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
230 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
231 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
232
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000233 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700234 * Create DHCP trap flow on NNI port(s).
235 */
236 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000237
238 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700239 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000240 **/
241 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
242
243 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700244 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000245 **/
246 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
247
248 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700249 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000250 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700251 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000252
253 /**
254 * Send EAPOL authentication trap flows before subscriber provisioning.
255 **/
256 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
257
258 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300259 * Send PPPoED authentication trap flows before subscriber provisioning.
260 **/
yasin sapli0823c932022-01-26 11:26:09 +0000261 protected boolean enablePppoeOnNni = ENABLE_PPPOE_ON_NNI_DEFAULT;
262
263 /**
264 * Enable flows for PPPoE if it is required in sadis config.
265 **/
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300266 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
267
268 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000269 * Default technology profile id that is used for authentication trap flows.
270 **/
271 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
272
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700273 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
274
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700275 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000276
277 @Activate
278 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700279 cfgService.registerProperties(getClass());
280 appId = coreService.registerApplication(APP_NAME);
281 internalFlowListener = new InternalFlowListener();
282
283 modified(context);
284
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000285 KryoNamespace serializer = KryoNamespace.newBuilder()
286 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700287 .register(OltFlowsStatus.class)
288 .register(FlowDirection.class)
289 .register(OltPortStatus.class)
290 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000291 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700292 .register(new ServiceKeySerializer(), ServiceKey.class)
293 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000294 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000295
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700296 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
297 .withName("volt-cp-status")
298 .withApplicationId(appId)
299 .withSerializer(Serializer.using(serializer))
300 .build().asJavaMap();
301
302 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
303 .withName("volt-provisioned-subscriber")
304 .withApplicationId(appId)
305 .withSerializer(Serializer.using(serializer))
306 .build().asJavaMap();
307
308 flowRuleService.addListener(internalFlowListener);
309
310 log.info("Started");
311 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000312
313 @Deactivate
314 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700315 cfgService.unregisterProperties(getClass(), false);
316 flowRuleService.removeListener(internalFlowListener);
317 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000318 }
319
320 @Modified
321 public void modified(ComponentContext context) {
322
323 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
324
Saurav Dasf62cea82020-08-26 17:43:04 -0700325 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000326 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700327 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000328 }
329
Andrea Campanella7c49b792020-05-11 11:36:53 +0200330 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000331 if (v4 != null) {
332 enableDhcpV4 = v4;
333 }
334
Andrea Campanella7c49b792020-05-11 11:36:53 +0200335 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000336 if (v6 != null) {
337 enableDhcpV6 = v6;
338 }
339
Saurav Dasf62cea82020-08-26 17:43:04 -0700340 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000341 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700342 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000343 }
344
Andrea Campanella7c49b792020-05-11 11:36:53 +0200345 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000346 if (eap != null) {
347 enableEapol = eap;
348 }
349
yasin sapli0823c932022-01-26 11:26:09 +0000350 Boolean pppoeInNni = Tools.isPropertyEnabled(properties, ENABLE_PPPOE_ON_NNI);
351 if (pppoeInNni != null) {
352 enablePppoeOnNni = pppoeInNni;
353 }
354
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300355 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
356 if (pppoe != null) {
357 enablePppoe = pppoe;
358 }
359
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700360 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
361 if (wait != null) {
362 waitForRemoval = wait;
363 }
364
Andrea Campanella7c49b792020-05-11 11:36:53 +0200365 String tpId = get(properties, DEFAULT_TP_ID);
366 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000367
yasin sapli0823c932022-01-26 11:26:09 +0000368 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " + "enableDhcpV6:{}, " +
369 "enableIgmpOnNni:{}, " + "enableEapol:{}, enablePppoeOnNni: {}, enablePppoe:{}, " +
370 "defaultTechProfileId:{}," + "waitForRemoval:{}",
371 enableDhcpOnNni, enableDhcpV4, enableDhcpV6, enableIgmpOnNni, enableEapol,
372 enablePppoeOnNni, enablePppoe, defaultTechProfileId, waitForRemoval);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000373 }
374
375 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700376 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
377 try {
378 cpStatusReadLock.lock();
379 return ImmutableMap.copyOf(cpStatus);
380 } finally {
381 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000382 }
383 }
384
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700385 @Override
386 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
387 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800388 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700389 Map<ServiceKey, UniTagInformation> subscribers =
390 new HashMap<>();
391 try {
392 cpStatusReadLock.lock();
393
394 cpStatus.forEach((sk, status) -> {
395 if (
396 // not NNI Port
397 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
398 sk.getPort().connectPoint().port()) &&
399 // not EAPOL flow
Andrea Campanella982fd332022-01-19 09:14:12 +0100400 !sk.getService().equals(defaultEapolUniTag) &&
401 !status.subscriberFlowsStatus.equals(OltFlowsStatus.PENDING_REMOVE)
402 && !status.subscriberFlowsStatus.equals(OltFlowsStatus.REMOVED)
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800403
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700404 ) {
405 subscribers.put(sk, sk.getService());
406 }
407 });
408
409 return ImmutableMap.copyOf(subscribers);
410 } finally {
411 cpStatusReadLock.unlock();
412 }
413 }
414
415 @Override
416 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
417 try {
418 provisionedSubscribersReadLock.lock();
419 return ImmutableMap.copyOf(provisionedSubscribers);
420 } finally {
421 provisionedSubscribersReadLock.unlock();
422 }
423 }
424
425 @Override
426 public void handleNniFlows(Device device, Port port, FlowOperation action) {
427
428 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800429 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700430 processLldpFilteringObjective(device.id(), port, action);
431
432 if (enableDhcpOnNni) {
433 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800434 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
435 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700436 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
437 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
438 null, null, nniUniTag);
439 }
440 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800441 log.debug("{} DHCPv6 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 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
445 null, null, nniUniTag);
446 }
447 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800448 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700449 }
450
451 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800452 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700453 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
454 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
455 }
456
yasin sapli0823c932022-01-26 11:26:09 +0000457 if (enablePppoeOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800458 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700459 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
460 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
461 }
462 }
463
464 @Override
465 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
466 String oltBandwidthProfileId) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700467 // we only need to something if EAPOL is enabled
468 if (!enableEapol) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100469 log.debug("Eapol is disabled for {}", portWithName(sub.port));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700470 return true;
471 }
472
473 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
474 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
475 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
476 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
477 } else {
478 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
479 return false;
480 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700481 }
482
483 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100484 log.debug("Adding default flows for {}, status {}", portWithName(sub.port), sub.status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700485 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
486 if (log.isTraceEnabled()) {
487 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
488 }
489 return false;
490 }
491 if (hasDefaultEapol(sub.port)) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100492 OltPortStatus status = getOltPortStatus(sub.port, defaultEapolUniTag);
493 log.debug("Eapol is already present for {} with status {}", portWithName(sub.port), status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700494 return true;
495 }
496 return handleEapolFlow(sub, bandwidthProfileId,
497 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
498
499 }
500
501 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
502 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
503 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
504 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
505 }
506
507 @Override
508 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
509 String multicastServiceName) {
510 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
511 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
512 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200513 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED ||
514 sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700515 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
516 } else {
517 log.error("don't know how to handle {}", sub);
518 return false;
519 }
520 }
521
522 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
523 String multicastServiceName) {
524 if (log.isTraceEnabled()) {
525 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
526 }
527 if (enableEapol) {
528 if (hasDefaultEapol(sub.port)) {
529 // remove EAPOL flow and throw exception so that we'll retry later
530 if (!isDefaultEapolPendingRemoval(sub.port)) {
531 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
532 }
533
534 if (waitForRemoval) {
535 // NOTE wait for removal is a flag only needed to make sure VOLTHA
536 // does not explode with the flows remove/add in the same batch
537 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
538 return false;
539 } else {
540 log.warn("continuing provisioning on {}", portWithName(sub.port));
541 }
542 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700543 }
544
545 // NOTE createMeters will return if the meters are not installed
546 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800547 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700548 return false;
549 }
550
551 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
552 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
553 sub.subscriberAndDeviceInformation);
554
555 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
556 && !isMacAddressAvailable(sub.device.id(), sub.port,
557 sub.subscriberAndDeviceInformation)) {
558 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
559 return false;
560 }
561
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800562 // NOTE that the EAPOL flows handling is based on the data-plane flows status
563 // always process them before
564 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
565
yasin sapli0823c932022-01-26 11:26:09 +0000566 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
567
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700568 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
569 sub.subscriberAndDeviceInformation, multicastServiceName);
570
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700571 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
572
573 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
574 return true;
575 }
576
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800577 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700578 String multicastServiceName) {
579
580 if (log.isTraceEnabled()) {
581 log.trace("Removal of subscriber on {} started",
582 portWithName(sub.port));
583 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800584 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200585 //If the port has been removed the device service will return null, while it will be true if it's just disabled
586 boolean isPortPresent = deviceService.getPort(new ConnectPoint(sub.device.id(), sub.port.number())) != null;
587 if (log.isTraceEnabled()) {
588 log.trace("Port {} present: ", portWithName(sub.port), isPortPresent);
589 }
590 // Always remove the EAPOL flow in case of port disable,remove or subscriber remove.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700591 if (enableEapol) {
592 // remove the tagged eapol
593 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200594 log.info("Removal of eapol flow for subscriber on {} completed", portWithName(sub.port));
595
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800596 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200597 // If the port is gone entirely (onu delete) or it's enabled (subscriber remove request) remove all the flows
598 // In the case the port is just disabled we remove only the EAPOL flow because the ONU disable only represents
599 // the UNI side of the ONU going down, either for RG cable detach or for an administrative decision, but the PON
600 // side is still up as nothing changed, so no need to add/remove flows, when and if the UNI comes up
601 // we will re-push the EAPOL flow to require the subscriber to auth again.
602 // When the subscriber is admin removed from REST or CLI we ignore the port status.
Andrea Campanella7ef88992022-05-17 12:38:00 +0200603 // Check the admin Status of the port
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200604 if (!isPortPresent || sub.port.isEnabled() || sub.status == DiscoveredSubscriber.Status.ADMIN_REMOVED) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700605
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200606 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800607
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200608 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE,
609 sub.subscriberAndDeviceInformation);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800610
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200611 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
612
613 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
614
615
616 if (enableEapol) {
617
618 // if any of the services still has flows, return false
619 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
620 while (iter.hasNext()) {
621 UniTagInformation entry = iter.next();
622 if (areSubscriberFlowsPendingRemoval(sub.port, entry, enableEapol)) {
623 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
624 portWithName(sub.port), entry.getServiceName());
625 return false;
626 }
627 }
628
629 // once the flows are removed add the default one back
630 // (only if the port is ENABLED and still present on the device)
631 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
632
633 // NOTE we remove the subscriber when the port goes down
634 // but in that case we don't need to add default eapol
635 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
636 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800637 }
638 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200639 // FIXME check the return status of the flow and return accordingly
640 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
641 return true;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700642 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700643 return true;
644 }
645
646 @Override
647 public boolean hasDefaultEapol(Port port) {
648 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
649 // NOTE we consider ERROR as a present EAPOL flow as ONOS
650 // will keep trying to add it
651 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
652 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
653 status.defaultEapolStatus == OltFlowsStatus.ERROR);
654 }
655
656 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
657 try {
658 cpStatusReadLock.lock();
659 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
660 OltPortStatus status = cpStatus.get(sk);
661 return status;
662 } finally {
663 cpStatusReadLock.unlock();
664 }
665 }
666
667 public boolean isDefaultEapolPendingRemoval(Port port) {
668 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
669 if (log.isTraceEnabled()) {
670 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
671 status, portWithName(port), defaultEapolUniTag);
672 }
673 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
674 }
675
676 @Override
677 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
678 OltPortStatus status = getOltPortStatus(port, uti);
679 if (log.isTraceEnabled()) {
680 log.trace("Status during DHCP flow check {} for port {} and service {}",
681 status, portWithName(port), uti.getServiceName());
682 }
683 return status != null &&
684 (status.dhcpStatus == OltFlowsStatus.ADDED ||
685 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
686 }
687
688 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000689 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
690 OltPortStatus status = getOltPortStatus(port, uti);
691 if (log.isTraceEnabled()) {
692 log.trace("Status during PPPoE flow check {} for port {} and service {}",
693 status, portWithName(port), uti.getServiceName());
694 }
695 return status != null &&
696 (status.pppoeStatus == OltFlowsStatus.ADDED ||
697 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
698 }
699
700 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700701 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
702
703 OltPortStatus status = getOltPortStatus(port, uti);
704 if (log.isTraceEnabled()) {
705 log.trace("Status during subscriber flow check {} for port {} and service {}",
706 status, portWithName(port), uti.getServiceName());
707 }
708 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
709 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
710 }
711
Andrea Campanella87241ae2022-03-11 11:20:24 +0100712 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti, boolean enableEapol) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800713 OltPortStatus status = getOltPortStatus(port, uti);
714 if (log.isTraceEnabled()) {
715 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
716 status, portWithName(port), uti);
717 }
Andrea Campanella87241ae2022-03-11 11:20:24 +0100718 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE ||
719 (enableEapol && status.subscriberEapolStatus == OltFlowsStatus.PENDING_REMOVE) ||
720 (uti.getIsDhcpRequired() && status.dhcpStatus == OltFlowsStatus.PENDING_REMOVE));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800721 }
722
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700723 @Override
724 public void purgeDeviceFlows(DeviceId deviceId) {
725 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800726 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700727
728 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800729 if (log.isTraceEnabled()) {
730 log.trace("Clearing cp status from device {}", deviceId);
731 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700732 try {
733 cpStatusWriteLock.lock();
734 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
735 while (iter.hasNext()) {
736 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
737 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
738 cpStatus.remove(entry.getKey());
739 }
740 }
741 } finally {
742 cpStatusWriteLock.unlock();
743 }
744
745 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800746 if (log.isTraceEnabled()) {
747 log.trace("Clearing provisioned subscribers from device {}", deviceId);
748 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700749 try {
750 provisionedSubscribersWriteLock.lock();
751 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
752 while (iter.hasNext()) {
753 Map.Entry<ServiceKey, Boolean> entry = iter.next();
754 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
755 provisionedSubscribers.remove(entry.getKey());
756 }
757 }
758 } finally {
759 provisionedSubscribersWriteLock.unlock();
760 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800761 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700762 }
763
764 @Override
765 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800766 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700767 try {
768 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800769 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700770 } finally {
771 provisionedSubscribersReadLock.unlock();
772 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800773
774 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
775 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
776 return true;
777 }
778 }
779 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700780 }
781
782 @Override
783 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
784 try {
785 provisionedSubscribersReadLock.lock();
786 Boolean provisioned = provisionedSubscribers.get(sk);
787 if (provisioned == null || !provisioned) {
788 return false;
789 }
790 } finally {
791 provisionedSubscribersReadLock.unlock();
792 }
793 return true;
794 }
795
796 @Override
797 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
798 try {
799 provisionedSubscribersWriteLock.lock();
800 provisionedSubscribers.put(sk, status);
801 } finally {
802 provisionedSubscribersWriteLock.unlock();
803 }
804 }
805
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800806 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700807 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
808
809 // create a subscriberKey for the EAPOL flow
810 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
Andrea Campanella87241ae2022-03-11 11:20:24 +0100811 OltFlowsStatus status = action == FlowOperation.ADD ?
812 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700813 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +0100814 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE,
815 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700816
Andrea Campanella87241ae2022-03-11 11:20:24 +0100817 } else {
818 updateConnectPointStatus(sk, OltFlowsStatus.NONE, status, OltFlowsStatus.NONE,
819 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700820 }
821
822 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
823 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
824
825 int techProfileId = getDefaultTechProfileId(sub.port);
826 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
827
828 // in the delete case the meter should still be there as we remove
829 // the meters only if no flows are pointing to them
830 if (meterId == null) {
831 log.debug("MeterId is null for BandwidthProfile {} on device {}",
832 bandwidthProfile, sub.device.id());
833 return false;
834 }
835
836 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
837 if (oltMeterId == null) {
838 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
839 oltBandwidthProfile, sub.device.id());
840 return false;
841 }
842
843 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
844 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
845
846 FilteringObjective.Builder eapolAction;
847
848 if (action == FlowOperation.ADD) {
849 eapolAction = filterBuilder.permit();
850 } else if (action == FlowOperation.REMOVE) {
851 eapolAction = filterBuilder.deny();
852 } else {
853 log.error("Operation {} not supported", action);
854 return false;
855 }
856
857 FilteringObjective.Builder baseEapol = eapolAction
858 .withKey(Criteria.matchInPort(sub.port.number()))
859 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
860
861 // NOTE we only need to add the treatment to install the flow,
862 // we can remove it based in the match
863 FilteringObjective.Builder eapol;
864
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530865 // Setting VlanId.NONE as cvlan in the metadata as the packet will be single tagged
866 // and cvlan should not be filled.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700867 TrafficTreatment treatment = treatmentBuilder
868 .meter(meterId)
Harsh Awasthic1e4bf52022-02-09 14:14:14 +0530869 .writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +0530870 VlanId.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700871 techProfileId, oltMeterId), 0)
872 .setOutput(PortNumber.CONTROLLER)
873 .pushVlan()
874 .setVlanId(vlanId)
875 .build();
876 eapol = baseEapol
877 .withMeta(treatment);
878
879 FilteringObjective eapolObjective = eapol
880 .fromApp(appId)
881 .withPriority(MAX_PRIORITY)
882 .add(new ObjectiveContext() {
883 @Override
884 public void onSuccess(Objective objective) {
885 log.info("EAPOL flow objective {} for {}",
886 completeFlowOpToString(action), portWithName(sub.port));
887 if (log.isTraceEnabled()) {
888 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
889 }
890 }
891
892 @Override
893 public void onError(Objective objective, ObjectiveError error) {
894 log.error("Cannot {} eapol flow for {} : {}", action,
895 portWithName(sub.port), error);
896
897 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
898 updateConnectPointStatus(sk,
Andrea Campanella87241ae2022-03-11 11:20:24 +0100899 OltFlowsStatus.ERROR, null, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700900 }
901 }
902 });
903
904 flowObjectiveService.filter(sub.device.id(), eapolObjective);
905
906 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
907 return true;
908 }
909
910 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800911 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700912 SubscriberAndDeviceInformation si) {
913 if (!enableEapol) {
914 return true;
915 }
916 // TODO verify we need an EAPOL flow for EACH service
917 AtomicBoolean success = new AtomicBoolean(true);
918 si.uniTagList().forEach(u -> {
Andrea Campanella34ce61a2022-04-28 18:55:46 +0200919 //Always act on the eapol flow
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700920 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
921 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
922 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
923 u.getUpstreamOltBandwidthProfile(),
924 action, u.getPonCTag())) {
925 //
Andrea Campanella87241ae2022-03-11 11:20:24 +0100926 log.error("Failed to {} EAPOL with subscriber tags", action);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700927 //TODO this sets it for all services, maybe some services succeeded.
928 success.set(false);
929 }
930 });
931 return success.get();
932 }
933
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800934 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700935 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
936 if (uti.getIsIgmpRequired()) {
937 DeviceId deviceId = sub.device.id();
938 // if we reached here a meter already exists
939 MeterId meterId = oltMeterService
940 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
941 MeterId oltMeterId = oltMeterService
942 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
943
944 processIgmpFilteringObjectives(deviceId, sub.port,
945 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
946 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
947 }
948 });
949 }
950
951 private boolean checkSadisRunning() {
952 if (bpService == null) {
953 log.warn("Sadis is not running");
954 return false;
955 }
956 return true;
957 }
958
959 private int getDefaultTechProfileId(Port port) {
960 if (!checkSadisRunning()) {
961 return defaultTechProfileId;
962 }
963 if (port != null) {
964 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
965 if (info != null && info.uniTagList().size() == 1) {
966 return info.uniTagList().get(0).getTechnologyProfileId();
967 }
968 }
969 return defaultTechProfileId;
970 }
971
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700972 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
973 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
974
975 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
976 .withKey(Criteria.matchInPort(port.number()))
977 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
978 .withMeta(DefaultTrafficTreatment.builder()
979 .setOutput(PortNumber.CONTROLLER).build())
980 .fromApp(appId)
981 .withPriority(MAX_PRIORITY)
982 .add(new ObjectiveContext() {
983 @Override
984 public void onSuccess(Objective objective) {
985 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
986 }
987
988 @Override
989 public void onError(Objective objective, ObjectiveError error) {
990 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
991 error);
992 }
993 });
994
995 flowObjectiveService.filter(deviceId, lldp);
996 }
997
998 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
999 FlowOperation action,
1000 SubscriberAndDeviceInformation si) {
1001 si.uniTagList().forEach(uti -> {
1002
1003 if (!uti.getIsDhcpRequired()) {
1004 return;
1005 }
1006
1007 // if it's an ADD skip if flows are there,
1008 // if it's a DELETE skip if flows are not there
1009 boolean hasFlows = hasDhcpFlows(port, uti);
1010 if (action == FlowOperation.ADD && hasFlows ||
1011 action == FlowOperation.REMOVE && !hasFlows) {
1012 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1013 uti.getServiceName(), hasFlows);
1014 return;
1015 }
1016
1017 log.info("{} DHCP flows for subscriber on {} and service {}",
1018 flowOpToString(action), portWithName(port), uti.getServiceName());
1019
1020 // if we reached here a meter already exists
1021 MeterId meterId = oltMeterService
1022 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1023 MeterId oltMeterId = oltMeterService
1024 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1025
1026 if (enableDhcpV4) {
1027 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1028 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1029 uti);
1030 }
1031 if (enableDhcpV6) {
1032 log.error("DHCP V6 not supported for subscribers");
1033 }
1034 });
1035 }
1036
yasin sapli0823c932022-01-26 11:26:09 +00001037 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1038 FlowOperation action,
1039 SubscriberAndDeviceInformation si) {
1040 si.uniTagList().forEach(uti -> {
1041
1042 if (!uti.getIsPppoeRequired()) {
1043 return;
1044 }
1045
1046 // if it's an ADD skip if flows are there,
1047 // if it's a DELETE skip if flows are not there
1048 boolean hasFlows = hasPppoeFlows(port, uti);
1049 if (action == FlowOperation.ADD && hasFlows ||
1050 action == FlowOperation.REMOVE && !hasFlows) {
1051 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1052 uti.getServiceName(), hasFlows);
1053 return;
1054 }
1055
1056 log.info("{} PPPoE flows for subscriber on {} and service {}",
1057 flowOpToString(action), portWithName(port), uti.getServiceName());
1058
1059 // if we reached here a meter already exists
1060 MeterId meterId = oltMeterService
1061 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1062 MeterId oltMeterId = oltMeterService
1063 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1064
1065 if (enablePppoe) {
1066 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1067 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1068 (byte) uti.getUsPonCTagPriority());
1069 }
1070 });
1071 }
1072
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001073 // FIXME return boolean, if this fails we need to retry
1074 protected void handleSubscriberDataFlows(Device device, Port port,
1075 FlowOperation action,
1076 SubscriberAndDeviceInformation si, String multicastServiceName) {
1077
1078 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001079 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001080 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1081 si.id(), portWithName(port));
1082 return;
1083 }
1084 si.uniTagList().forEach(uti -> {
1085
1086 boolean hasFlows = hasSubscriberFlows(port, uti);
1087 if (action == FlowOperation.ADD && hasFlows ||
1088 action == FlowOperation.REMOVE && !hasFlows) {
1089 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1090 uti.getServiceName(), hasFlows);
1091 return;
1092 }
1093
1094 if (multicastServiceName.equals(uti.getServiceName())) {
1095 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1096 "dataplane flows are not needed",
1097 uti.getServiceName(), si.id(), portWithName(port));
1098 return;
1099 }
1100
1101 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1102 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001103 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1104 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1105 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001106 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001107
1108 // upstream flows
1109 MeterId usMeterId = oltMeterService
1110 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1111 MeterId oltUsMeterId = oltMeterService
1112 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301113
1114 if (FttbUtils.isFttbService(uti)) {
1115 processFttbUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action,
1116 usMeterId, oltUsMeterId, uti, si);
1117 } else {
1118 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1119 oltUsMeterId, uti);
1120 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001121
1122 // downstream flows
1123 MeterId dsMeterId = oltMeterService
1124 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1125 MeterId oltDsMeterId = oltMeterService
1126 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301127
1128 if (FttbUtils.isFttbService(uti)) {
1129 processFttbDownstreamDataFilteringObjects(device.id(), port, nniPort.get(),
1130 action, dsMeterId, oltDsMeterId, uti, si);
1131 } else {
1132 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1133 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1134 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001135 });
1136 }
1137
1138 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1139 FlowOperation action, FlowDirection direction,
1140 int udpSrc, int udpDst, EthType ethType, byte protocol,
1141 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1142 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1143 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1144
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301145 String serviceName = uti.getServiceName();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001146
1147 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1148 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001149 updateConnectPointStatus(sk, null, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001150
1151 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1152 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1153
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001154 if (meterId != null) {
1155 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001156 }
1157
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001158 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001159 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001160 .addCondition(Criteria.matchEthType(ethType))
1161 .addCondition(Criteria.matchIPProtocol(protocol))
1162 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1163 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001164 .fromApp(appId)
1165 .withPriority(MAX_PRIORITY);
1166
Andrea Campanella0e34f562020-06-11 10:47:10 +02001167 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001168 if (direction == FlowDirection.UPSTREAM) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301169 if (serviceName != null && serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC)) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301170 FttbUtils.addUpstreamDhcpCondition(dhcpBuilder, uti);
1171 FttbUtils.addUpstreamDhcpTreatment(treatmentBuilder, uti);
1172 } else {
1173 treatmentBuilder.setVlanId(uti.getPonCTag());
1174 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1175 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
1176 }
1177 if (uti.getUsPonCTagPriority() != -1) {
1178 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
1179 }
Andrea Campanella0e34f562020-06-11 10:47:10 +02001180 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301181 } else if (direction == FlowDirection.DOWNSTREAM) {
1182 // Down stream DHCP vid to be matched if OLT Sadis info contains Vlan id in nniDhcpTrapVid.
1183 Device device = deviceService.getDevice(deviceId);
1184 SubscriberAndDeviceInformation oltSub = subsService.get(device.serialNumber());
1185 VlanId nniDhcpTrapVid = oltSub.nniDhcpTrapVid();
1186
1187 if (nniDhcpTrapVid != null && !VlanId.vlanId(VlanId.NO_VID).equals(nniDhcpTrapVid)) {
1188 dhcpBuilder.addCondition(Criteria.matchVlanId(nniDhcpTrapVid));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001189 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001190 }
1191
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301192 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301193 // 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 +05301194 treatmentBuilder.writeMetadata(
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301195 OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301196 uti.getTechnologyProfileId(), oltMeterId), 0);
1197 }
1198
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001199 dhcpBuilder.withMeta(treatmentBuilder
1200 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001201
1202
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001203 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001204 @Override
1205 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001206 log.info("{} DHCP {} filter for {}.",
1207 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1208 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001209 }
1210
1211 @Override
1212 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001213 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001214 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1215 portWithName(port),
1216 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001217 error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001218 updateConnectPointStatus(sk, null, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001219 }
1220 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001221 flowObjectiveService.filter(deviceId, dhcpUpstream);
1222 }
1223
1224 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1225 FlowOperation action, FlowDirection direction,
1226 MeterId meterId, MeterId oltMeterId, int techProfileId,
1227 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1228
1229 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1230 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1231 if (direction == FlowDirection.UPSTREAM) {
1232
1233 if (techProfileId != NONE_TP_ID) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301234 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(null,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001235 techProfileId, oltMeterId), 0);
1236 }
1237
1238
1239 if (meterId != null) {
1240 treatmentBuilder.meter(meterId);
1241 }
1242
1243 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1244 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1245 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001246 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1247 treatmentBuilder.setVlanId(cTag);
1248 }
1249
1250 if (vlanPcp != -1) {
1251 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1252 }
1253 }
1254
1255 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1256
1257 FilteringObjective igmp = filterBuilder
1258 .withKey(Criteria.matchInPort(port.number()))
1259 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1260 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1261 .withMeta(treatmentBuilder
1262 .setOutput(PortNumber.CONTROLLER).build())
1263 .fromApp(appId)
1264 .withPriority(MAX_PRIORITY)
1265 .add(new ObjectiveContext() {
1266 @Override
1267 public void onSuccess(Objective objective) {
1268 log.info("Igmp filter for {} {}.", portWithName(port), action);
1269 }
1270
1271 @Override
1272 public void onError(Objective objective, ObjectiveError error) {
1273 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1274 error);
1275 }
1276 });
1277
1278 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001279
1280 }
1281
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001282 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1283 FlowOperation action, FlowDirection direction,
1284 MeterId meterId, MeterId oltMeterId, int techProfileId,
1285 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001286
1287 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1288 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001289
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001290 if (meterId != null) {
1291 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001292 }
1293
1294 if (techProfileId != NONE_TP_ID) {
Harsh Awasthi498b5c62022-03-21 23:19:46 +05301295 // Setting VlanId.NONE as cvlan as the packet will be single tagged and cvlan should not be filled.
1296 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
1297 techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001298 }
1299
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001300 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1301 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001302 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001303 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1304 .fromApp(appId)
1305 .withPriority(10000);
1306
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001307 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001308 treatmentBuilder.setVlanId(cTag);
1309 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1310 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1311 }
1312 if (vlanPcp != null) {
1313 treatmentBuilder.setVlanPcp(vlanPcp);
1314 }
1315 }
1316 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1317
1318 FilteringObjective pppoed = pppoedBuilder
1319 .add(new ObjectiveContext() {
1320 @Override
1321 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001322 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001323 }
1324
1325 @Override
1326 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001327 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1328 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001329 }
1330 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001331 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001332 }
1333
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001334 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1335 FlowOperation action,
1336 MeterId upstreamMeterId,
1337 MeterId upstreamOltMeterId,
1338 UniTagInformation uti) {
1339 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001340 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001341 .matchInPort(port.number())
1342 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001343 .build();
1344
Andrea Campanella327c5722020-01-30 11:34:13 +01001345 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1346 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001347 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001348 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001349 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001350 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001351 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1352 treatmentBuilder.popVlan();
1353 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001354
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001355 if (uti.getUsPonCTagPriority() != -1) {
1356 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001357
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001358 }
1359
1360 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001361 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001362
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001363 if (uti.getUsPonSTagPriority() != -1) {
1364 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001365 }
1366
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001367 treatmentBuilder.setOutput(nniPort.number())
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301368 .writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001369 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001370
yasin saplib4b8ee12021-06-13 18:25:20 +00001371 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1372
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001373 if (upstreamMeterId != null) {
1374 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001375 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1376 }
1377 if (upstreamOltMeterId != null) {
1378 treatmentBuilder.meter(upstreamOltMeterId);
1379 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001380 }
1381
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001382 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1383 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001384 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001385
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301386 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1387 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001388 }
1389
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001390 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1391 FlowOperation action,
1392 MeterId downstreamMeterId,
1393 MeterId downstreamOltMeterId,
1394 UniTagInformation uti,
1395 MacAddress macAddress) {
1396 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001397 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001398 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001399 .matchVlanId(uti.getPonSTag())
1400 .matchInPort(nniPort.number())
1401 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001402
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001403 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1404 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001405 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001406
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001407 if (uti.getDsPonCTagPriority() != -1) {
1408 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001409 }
1410
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001411 if (macAddress != null) {
1412 selectorBuilder.matchEthDst(macAddress);
1413 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001414
1415 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1416 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001417 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001418
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301419 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(uti.getPonCTag(),
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001420 uti.getTechnologyProfileId(),
1421 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001422
Andrea Campanella981e86c2021-03-12 11:35:33 +01001423 // Upstream pbit is used to remark inner vlan pbit.
1424 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1425 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1426 // all pbit acceptance are not widely supported by vendors even though present in
1427 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001428 if (uti.getUsPonCTagPriority() != -1) {
1429 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001430 }
1431
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001432 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1433 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1434 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001435 }
1436
yasin saplib4b8ee12021-06-13 18:25:20 +00001437 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1438
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001439 if (downstreamMeterId != null) {
1440 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001441 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001442 }
1443
yasin saplib4b8ee12021-06-13 18:25:20 +00001444 if (downstreamOltMeterId != null) {
1445 treatmentBuilder.meter(downstreamOltMeterId);
1446 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1447 }
1448
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001449 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1450 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001451
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301452 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1453 processForwardingRule(action, flowBuilder, context, deviceId);
Andrea Campanella600d2e22020-06-22 11:00:31 +02001454 }
1455
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001456 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1457 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001458 Integer priority,
1459 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001460 return DefaultForwardingObjective.builder()
1461 .withFlag(ForwardingObjective.Flag.VERSATILE)
1462 .withPriority(priority)
1463 .makePermanent()
1464 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001465 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001466 .fromApp(appId)
1467 .withTreatment(treatment);
1468 }
1469
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001470 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1471 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1472 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001473
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001474 si.uniTagList().forEach(uniTagInfo -> {
1475 if (uniTagInfo.getEnableMacLearning()) {
1476 requiresMacLearning.set(true);
1477 }
1478 });
1479
1480 return requiresMacLearning.get();
1481 }
1482
1483 /**
1484 * Checks whether the subscriber has the MacAddress configured or discovered.
1485 *
1486 * @param deviceId DeviceId for this subscriber
1487 * @param port Port for this subscriber
1488 * @param si SubscriberAndDeviceInformation
1489 * @return boolean
1490 */
1491 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1492 AtomicBoolean isConfigured = new AtomicBoolean();
1493 isConfigured.set(true);
1494
1495 si.uniTagList().forEach(uniTagInfo -> {
1496 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301497 boolean configureMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001498 boolean discoveredMac = false;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301499
1500 final VlanId vlan;
1501
1502 if (FttbUtils.isFttbDpuOrAncpService(uniTagInfo)) {
1503 // Using S tag, as C tag is replaced by Stag by ONU.
1504 vlan = uniTagInfo.getPonSTag();
1505 } else {
1506 vlan = uniTagInfo.getPonCTag();
1507 }
1508
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001509 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301510 .stream().filter(host -> host.vlan().equals(vlan)).findFirst();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001511 if (optHost.isPresent() && optHost.get().mac() != null) {
1512 discoveredMac = true;
1513 }
1514 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1515 log.debug("Awaiting for macAddress on {} for service {}",
1516 portWithName(port), uniTagInfo.getServiceName());
1517 isConfigured.set(false);
1518 }
1519 });
1520
1521 return isConfigured.get();
1522 }
1523
1524 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301525 boolean configuredMac = OltFlowServiceUtils.isMacAddressValid(uniTagInfo);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001526 if (configuredMac) {
1527 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1528 } else if (uniTagInfo.getEnableMacLearning()) {
1529 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1530 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1531 if (optHost.isPresent() && optHost.get().mac() != null) {
1532 return optHost.get().mac();
1533 }
1534 }
1535 return null;
1536 }
1537
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001538 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001539 OltFlowsStatus subscriberEapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001540 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1541 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001542 if (log.isTraceEnabled()) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001543 log.trace("Updating cpStatus {} with values: eapolFlow={}, " +
1544 "subscriberEapolStatus={}, subscriberFlows={}, dhcpFlow={}",
1545 key, eapolStatus, subscriberEapolStatus, subscriberFlowsStatus, dhcpStatus);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001546 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001547 try {
1548 cpStatusWriteLock.lock();
1549 OltPortStatus status = cpStatus.get(key);
1550
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001551
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001552 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001553 // if we don't have status for the connectPoint
1554 // and we're only updating status to PENDING_REMOVE or ERROR
1555 // do not create it. This is because this case will only happen when a device is removed
1556 // and it's status cleaned
1557 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1558 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1559 statusesToIgnore.add(OltFlowsStatus.ERROR);
1560
1561 if (
1562 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1563 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1564 ) {
1565 if (log.isTraceEnabled()) {
1566 log.trace("Ignoring cpStatus update as status is meaningless");
1567 }
1568 return;
1569 }
1570
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001571 status = new OltPortStatus(
1572 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001573 subscriberEapolStatus != null ? subscriberEapolStatus : OltFlowsStatus.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001574 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001575 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1576 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001577 );
1578 } else {
1579 if (eapolStatus != null) {
1580 status.defaultEapolStatus = eapolStatus;
1581 }
1582 if (subscriberFlowsStatus != null) {
1583 status.subscriberFlowsStatus = subscriberFlowsStatus;
1584 }
1585 if (dhcpStatus != null) {
1586 status.dhcpStatus = dhcpStatus;
1587 }
1588 }
1589
1590 cpStatus.put(key, status);
1591 } finally {
1592 cpStatusWriteLock.unlock();
1593 }
1594 }
1595
1596 protected class InternalFlowListener implements FlowRuleListener {
1597 @Override
1598 public void event(FlowRuleEvent event) {
1599 if (appId.id() != (event.subject().appId())) {
1600 return;
1601 }
1602
1603 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1604 if (log.isTraceEnabled()) {
1605 log.trace("ignoring flow event {} " +
1606 "as not leader for {}", event, event.subject().deviceId());
1607 }
1608 return;
1609 }
1610
1611 switch (event.type()) {
1612 case RULE_ADDED:
1613 case RULE_REMOVED:
Andrea Campanella40d2b342022-02-04 18:13:37 +01001614 DeviceId deviceId = event.subject().deviceId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001615 Port port = getCpFromFlowRule(event.subject());
1616 if (port == null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001617 log.warn("Port is gone in ONOS, " +
1618 "manually creating it {}", event.subject());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301619 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(event.subject());
Andrea Campanella40d2b342022-02-04 18:13:37 +01001620 cpStatusReadLock.lock();
1621 Optional<ServiceKey> keyWithPort = cpStatus.keySet()
1622 .stream().filter(key -> key.getPort().connectPoint()
1623 .deviceId().equals(deviceId)
1624 && key.getPort().connectPoint().port()
1625 .equals(inPort)).findFirst();
1626 cpStatusReadLock.unlock();
1627 if (keyWithPort.isPresent()) {
1628 port = new DefaultPort(deviceService.getDevice(deviceId),
1629 inPort, false,
1630 DefaultAnnotations.builder()
1631 .set(AnnotationKeys.PORT_NAME,
1632 keyWithPort.get().getPort().name())
1633 .build());
1634 } else {
1635 log.warn("Can't find corresponding status for {}/{}", deviceId, inPort);
1636 return;
1637 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001638 }
1639 if (log.isTraceEnabled()) {
1640 log.trace("flow event {} on cp {}: {}", event.type(),
1641 portWithName(port), event.subject());
1642 }
1643 updateCpStatus(event.type(), port, event.subject());
1644 return;
1645 case RULE_ADD_REQUESTED:
1646 case RULE_REMOVE_REQUESTED:
1647 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1648 return;
1649 default:
1650 return;
1651 }
1652 }
1653
1654 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301655 OltFlowsStatus status = OltFlowServiceUtils.flowRuleStatusToOltFlowStatus(type);
1656 if (OltFlowServiceUtils.isDefaultEapolFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001657 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1658 defaultEapolUniTag);
1659 if (log.isTraceEnabled()) {
1660 log.trace("update defaultEapolStatus {} on {}", status, sk);
1661 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001662 updateConnectPointStatus(sk, status, null, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301663 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001664 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
1665 if (sk == null) {
1666 return;
1667 }
1668 if (log.isTraceEnabled()) {
1669 log.trace("update subscriberEapolStatus {} on {}", status, sk);
1670 }
Andrea Campanella34ce61a2022-04-28 18:55:46 +02001671 updateConnectPointStatus(sk, null, status, null, null, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301672 } else if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001673 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001674 if (sk == null) {
1675 return;
1676 }
1677 if (log.isTraceEnabled()) {
1678 log.trace("update dhcpStatus {} on {}", status, sk);
1679 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001680 updateConnectPointStatus(sk, null, null, null, status, null);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301681 } else if (OltFlowServiceUtils.isPppoeFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001682 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
yasin sapli0823c932022-01-26 11:26:09 +00001683 if (sk == null) {
1684 return;
1685 }
1686 if (log.isTraceEnabled()) {
1687 log.trace("update pppoeStatus {} on {}", status, sk);
1688 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001689 updateConnectPointStatus(sk, null, null, null, null, status);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301690 } else if (OltFlowServiceUtils.isDataFlow(flowRule)) {
1691 PortNumber number = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Andrea Campanella40d2b342022-02-04 18:13:37 +01001692 if (number == null) {
1693 log.error("Can't capture the port number from flow {}", flowRule);
1694 return;
1695 }
1696 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), number)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001697 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1698 return;
1699 }
1700
Andrea Campanella40d2b342022-02-04 18:13:37 +01001701 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001702 if (sk == null) {
1703 return;
1704 }
1705 if (log.isTraceEnabled()) {
1706 log.trace("update dataplaneStatus {} on {}", status, sk);
1707 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001708 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001709 }
1710 }
1711
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001712
Andrea Campanella87241ae2022-03-11 11:20:24 +01001713
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001714 private Port getCpFromFlowRule(FlowRule flowRule) {
1715 DeviceId deviceId = flowRule.deviceId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301716 PortNumber inPort = OltFlowServiceUtils.getPortNumberFromFlowRule(flowRule);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001717 if (inPort != null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001718 return deviceService.getPort(deviceId, inPort);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001719 }
1720 return null;
1721 }
1722
Andrea Campanella40d2b342022-02-04 18:13:37 +01001723 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule, Port flowPort) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001724 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1725
1726 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1727 if (si == null && !isNni) {
1728 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1729 return null;
1730 }
1731
1732 if (isNni) {
1733 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1734 }
1735
1736 Optional<UniTagInformation> found = Optional.empty();
1737 VlanId flowVlan = null;
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301738 if (OltFlowServiceUtils.isDhcpFlow(flowRule)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001739 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1740 L2ModificationInstruction.ModVlanIdInstruction instruction =
1741 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1742 flowVlan = instruction.vlanId();
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301743 } else if (OltFlowServiceUtils.isSubscriberEapolFlow(flowRule)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001744 // we need to make a special case for EAPOL as in the ATT workflow EAPOL flows don't match on tags
1745 L2ModificationInstruction.ModVlanIdInstruction instruction =
1746 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(2);
1747 flowVlan = instruction.vlanId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001748 } else {
1749 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1750 VlanIdCriterion vlanIdCriterion =
1751 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1752 if (vlanIdCriterion == null) {
1753 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1754 return null;
1755 }
1756 flowVlan = vlanIdCriterion.vlanId();
1757 }
1758
1759 VlanId finalFlowVlan = flowVlan;
1760 found = si.uniTagList().stream().filter(uti ->
1761 uti.getPonCTag().equals(finalFlowVlan) ||
1762 uti.getPonSTag().equals(finalFlowVlan) ||
1763 uti.getUniTagMatch().equals(finalFlowVlan)
1764 ).findFirst();
1765
1766
1767 if (found.isEmpty()) {
1768 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1769 }
1770
1771 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1772
1773 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001774 }
1775
1776 protected void bindSadisService(SadisService service) {
1777 this.subsService = service.getSubscriberInfoService();
1778 this.bpService = service.getBandwidthProfileService();
1779 log.info("Sadis service is loaded");
1780 }
1781
1782 protected void unbindSadisService(SadisService service) {
1783 this.subsService = null;
1784 this.bpService = null;
1785 log.info("Sadis service is unloaded");
1786 }
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301787
1788 private void processFttbUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1789 FlowOperation action,
1790 MeterId upstreamMeterId,
1791 MeterId upstreamOltMeterId,
1792 UniTagInformation uti,
1793 SubscriberAndDeviceInformation si) {
1794 String serviceName = uti.getServiceName();
1795 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1796 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1797 .matchInPort(port.number())
1798 .matchVlanId(uti.getPonCTag());
1799
1800 if (uti.getUsPonCTagPriority() != -1) {
1801 selectorBuilder.matchVlanPcp((byte) uti.getUsPonCTagPriority());
1802 }
1803
1804 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1805
1806 treatmentBuilder.setVlanId(uti.getPonSTag());
1807 if (uti.getUsPonSTagPriority() != -1) {
1808 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
1809 }
1810
1811 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1812 annotationBuilder.set(FTTB_SERVICE_NAME, serviceName);
1813 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_UPSTREAM);
1814
1815 if (upstreamMeterId != null) {
1816 treatmentBuilder.meter(upstreamMeterId);
1817 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1818 }
1819 if (upstreamOltMeterId != null) {
1820 treatmentBuilder.meter(upstreamOltMeterId);
1821 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
1822 }
1823
1824 VlanId innerVlan = null;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001825 treatmentBuilder.setOutput(nniPort.number());
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301826 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1827 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1828 hostService, si, deviceId, port);
1829
1830 if (mac == null) {
1831 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1832 port, uti.getPonSTag(), serviceName);
1833 return;
1834 }
1835
1836 selectorBuilder.matchEthSrc(mac);
Andrea Campanella7ef88992022-05-17 12:38:00 +02001837
1838 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
1839 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301840
1841 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
Andrea Campanella7ef88992022-05-17 12:38:00 +02001842 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1843 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301844 }
1845
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301846 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1847 treatmentBuilder.build(), MIN_PRIORITY,
1848 annotationBuilder.build());
1849
1850 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.UPSTREAM);
1851 processForwardingRule(action, flowBuilder, context, deviceId);
1852 }
1853
1854 private void processFttbDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1855 FlowOperation action,
1856 MeterId downstreamMeterId,
1857 MeterId downstreamOltMeterId,
1858 UniTagInformation uti, SubscriberAndDeviceInformation si) {
1859 String serviceName = uti.getServiceName();
1860 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1861 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
1862 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
1863 .matchVlanId(uti.getPonSTag())
1864 .matchInPort(nniPort.number());
1865
1866 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1867 .setVlanId(uti.getPonCTag())
1868 .setOutput(port.number());
1869
1870 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1871 annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
1872 annotationBuilder.set(FTTB_FLOW_DIRECTION, FTTB_FLOW_DOWNSTREAM);
1873
1874 if (downstreamMeterId != null) {
1875 treatmentBuilder.meter(downstreamMeterId);
1876 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
1877 }
1878
1879 if (downstreamOltMeterId != null) {
1880 treatmentBuilder.meter(downstreamOltMeterId);
1881 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1882 }
1883
1884 VlanId innerVlan = null;
1885
1886 if (serviceName.equals(FTTB_SERVICE_DPU_MGMT_TRAFFIC) || serviceName.equals(FTTB_SERVICE_DPU_ANCP_TRAFFIC)) {
1887 MacAddress mac = FttbUtils.getMacAddressFromDhcpEnabledUti(
1888 hostService, si, deviceId, port);
1889
1890 if (mac == null) {
1891 log.error("Mac address not found port:{}, vlan:{}, service:{}",
1892 port, uti.getPonSTag(), serviceName);
1893 return;
1894 }
1895
1896 selectorBuilder.matchEthDst(mac);
1897 innerVlan = VlanId.NONE;
Andrea Campanella7ef88992022-05-17 12:38:00 +02001898 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(innerVlan,
1899 uti.getTechnologyProfileId(),
1900 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301901
1902 } else if (serviceName.equals(FTTB_SERVICE_SUBSCRIBER_TRAFFIC)) {
Andrea Campanella7ef88992022-05-17 12:38:00 +02001903 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
1904 treatmentBuilder.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
1905 uti.getTechnologyProfileId(),
1906 port.number()), 0);
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301907 }
1908
Harsh Awasthic1e4bf52022-02-09 14:14:14 +05301909 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1910 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
1911
1912 ObjectiveContext context = getSubscriberFlowBuilderContext(sk, action, FlowDirection.DOWNSTREAM);
1913 processForwardingRule(action, flowBuilder, context, deviceId);
1914 }
1915
1916 private ObjectiveContext getSubscriberFlowBuilderContext(ServiceKey sk, FlowOperation action,
1917 FlowDirection flowDirection) {
1918 ObjectiveContext context = new ObjectiveContext() {
1919 @Override
1920 public void onSuccess(Objective objective) {
1921 log.info("{} {} Data plane filter for {}.",
1922 completeFlowOpToString(action), flowDirection, sk);
1923 }
1924
1925 @Override
1926 public void onError(Objective objective, ObjectiveError error) {
1927 log.info("{} Data plane filter for {} failed {} because {}.",
1928 flowDirection, sk, action, error);
1929 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
1930 }
1931 };
1932
1933 return context;
1934 }
1935
1936 private void processForwardingRule(FlowOperation action, DefaultForwardingObjective.Builder flowBuilder,
1937 ObjectiveContext context, DeviceId deviceId) {
1938 ForwardingObjective flow = null;
1939 if (action == FlowOperation.ADD) {
1940 flow = flowBuilder.add(context);
1941 } else if (action == FlowOperation.REMOVE) {
1942 flow = flowBuilder.remove(context);
1943 } else {
1944 log.error("Flow action not supported: {}", action);
1945 }
1946
1947 if (flow != null) {
1948 if (log.isTraceEnabled()) {
1949 log.trace("Forwarding rule {}", flow);
1950 }
1951 flowObjectiveService.forward(deviceId, flow);
1952 }
1953 }
1954}