blob: 4d452e6a12f54ad9c6bf14570b31efc8f36a5fc4 [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;
52import org.onosproject.net.flow.criteria.EthTypeCriterion;
53import org.onosproject.net.flow.criteria.IPProtocolCriterion;
54import org.onosproject.net.flow.criteria.PortCriterion;
55import org.onosproject.net.flow.criteria.UdpPortCriterion;
56import org.onosproject.net.flow.criteria.VlanIdCriterion;
57import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000058import org.onosproject.net.flowobjective.DefaultFilteringObjective;
59import org.onosproject.net.flowobjective.DefaultForwardingObjective;
60import org.onosproject.net.flowobjective.FilteringObjective;
61import org.onosproject.net.flowobjective.FlowObjectiveService;
62import org.onosproject.net.flowobjective.ForwardingObjective;
63import org.onosproject.net.flowobjective.Objective;
64import org.onosproject.net.flowobjective.ObjectiveContext;
65import org.onosproject.net.flowobjective.ObjectiveError;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070066import org.onosproject.net.host.HostService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000067import org.onosproject.net.meter.MeterId;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000068import org.onosproject.store.serializers.KryoNamespaces;
69import org.onosproject.store.service.Serializer;
70import org.onosproject.store.service.StorageService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000071import org.opencord.sadis.BandwidthProfileInformation;
72import org.opencord.sadis.BaseInformationService;
73import org.opencord.sadis.SadisService;
74import org.opencord.sadis.SubscriberAndDeviceInformation;
75import org.opencord.sadis.UniTagInformation;
76import org.osgi.service.component.ComponentContext;
77import org.osgi.service.component.annotations.Activate;
78import org.osgi.service.component.annotations.Component;
79import org.osgi.service.component.annotations.Deactivate;
80import org.osgi.service.component.annotations.Modified;
81import org.osgi.service.component.annotations.Reference;
82import org.osgi.service.component.annotations.ReferenceCardinality;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000083import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000084import org.slf4j.Logger;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070085import org.slf4j.LoggerFactory;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000086
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080087import java.util.ArrayList;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010088import java.util.Dictionary;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070089import java.util.HashMap;
Andrea Campanella61650a12022-01-24 18:09:44 -080090import java.util.HashSet;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070091import java.util.Iterator;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080092import java.util.List;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000093import java.util.Map;
Andrea Campanellabfb47af2021-06-03 11:09:45 +020094import java.util.Optional;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010095import java.util.Properties;
Andrea Campanella61650a12022-01-24 18:09:44 -080096import java.util.Set;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070097import java.util.concurrent.atomic.AtomicBoolean;
98import java.util.concurrent.locks.Lock;
99import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100100
101import static com.google.common.base.Strings.isNullOrEmpty;
102import static org.onlab.util.Tools.get;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700103import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
104import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
105import static org.opencord.olt.impl.OltUtils.flowOpToString;
106import static org.opencord.olt.impl.OltUtils.getPortName;
107import static org.opencord.olt.impl.OltUtils.portWithName;
108import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
109import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
110import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
111import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
112import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
113import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
114import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
115import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
116import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
117import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
118import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
119import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
120import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
121import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
yasin sapli0823c932022-01-26 11:26:09 +0000122import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI;
123import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI_DEFAULT;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700124import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
125import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
126import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
127import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
128import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
129import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000130
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000131@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700132 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000133 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
134 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700135 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000136 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
yasin sapli0823c932022-01-26 11:26:09 +0000137 ENABLE_PPPOE_ON_NNI + ":Boolean=" + ENABLE_PPPOE_ON_NNI_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300138 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700139 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
140 // FIXME remove this option as potentially dangerous in production
141 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000142})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700143public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
146 protected CoreService coreService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700149 protected ComponentConfigService cfgService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
152 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000153
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000154 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
155 bind = "bindSadisService",
156 unbind = "unbindSadisService",
157 policy = ReferencePolicy.DYNAMIC)
158 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700161 protected OltMeterServiceInterface oltMeterService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY)
164 protected OltDeviceServiceInterface oltDeviceService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY)
167 protected FlowRuleService flowRuleService;
168
169 @Reference(cardinality = ReferenceCardinality.MANDATORY)
170 protected HostService hostService;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000173 protected DeviceService deviceService;
174
175 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000176 protected StorageService storageService;
177
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700178 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
179 protected BaseInformationService<BandwidthProfileInformation> bpService;
180
181 private static final String APP_NAME = "org.opencord.olt";
182 protected ApplicationId appId;
183 private static final Integer MAX_PRIORITY = 10000;
184 private static final Integer MIN_PRIORITY = 1000;
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800185 protected static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700186 private static final int NONE_TP_ID = -1;
187 private static final String V4 = "V4";
188 private static final String V6 = "V6";
189 private final Logger log = LoggerFactory.getLogger(getClass());
190
191 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
192 .setServiceName("defaultEapol").build();
193 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
194 .setServiceName("nni")
195 .setTechnologyProfileId(NONE_TP_ID)
196 .setPonCTag(VlanId.NONE)
197 .setUniTagMatch(VlanId.ANY)
198 .setUsPonCTagPriority(-1)
199 .build();
200
201 /**
202 * Connect Point status map.
203 * Used to keep track of which cp has flows that needs to be removed when the status changes.
204 */
205 protected Map<ServiceKey, OltPortStatus> cpStatus;
206 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
207 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
208 private final Lock cpStatusReadLock = cpStatusLock.readLock();
209
210 /**
211 * This map contains the subscriber that have been provisioned by the operator.
212 * They may or may not have flows, depending on the port status.
213 * The map is used to define whether flows need to be provisioned when a port comes up.
214 */
215 protected Map<ServiceKey, Boolean> provisionedSubscribers;
216 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
217 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
218 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
219
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000220 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700221 * Create DHCP trap flow on NNI port(s).
222 */
223 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000224
225 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700226 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000227 **/
228 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
229
230 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700231 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000232 **/
233 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
234
235 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700236 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000237 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700238 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000239
240 /**
241 * Send EAPOL authentication trap flows before subscriber provisioning.
242 **/
243 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
244
245 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300246 * Send PPPoED authentication trap flows before subscriber provisioning.
247 **/
yasin sapli0823c932022-01-26 11:26:09 +0000248 protected boolean enablePppoeOnNni = ENABLE_PPPOE_ON_NNI_DEFAULT;
249
250 /**
251 * Enable flows for PPPoE if it is required in sadis config.
252 **/
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300253 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
254
255 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000256 * Default technology profile id that is used for authentication trap flows.
257 **/
258 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
259
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700260 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
261
262 public enum FlowOperation {
263 ADD,
264 REMOVE;
265
266
267 @Override
268 public String toString() {
269 return super.toString().toLowerCase();
270 }
271 }
272
273 public enum FlowDirection {
274 UPSTREAM,
275 DOWNSTREAM,
276 }
277
278 public enum OltFlowsStatus {
279 NONE,
280 PENDING_ADD,
281 ADDED,
282 PENDING_REMOVE,
283 REMOVED,
284 ERROR
285 }
286
287 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000288
289 @Activate
290 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700291 cfgService.registerProperties(getClass());
292 appId = coreService.registerApplication(APP_NAME);
293 internalFlowListener = new InternalFlowListener();
294
295 modified(context);
296
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000297 KryoNamespace serializer = KryoNamespace.newBuilder()
298 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700299 .register(OltFlowsStatus.class)
300 .register(FlowDirection.class)
301 .register(OltPortStatus.class)
302 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000303 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700304 .register(new ServiceKeySerializer(), ServiceKey.class)
305 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000306 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000307
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700308 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
309 .withName("volt-cp-status")
310 .withApplicationId(appId)
311 .withSerializer(Serializer.using(serializer))
312 .build().asJavaMap();
313
314 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
315 .withName("volt-provisioned-subscriber")
316 .withApplicationId(appId)
317 .withSerializer(Serializer.using(serializer))
318 .build().asJavaMap();
319
320 flowRuleService.addListener(internalFlowListener);
321
322 log.info("Started");
323 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000324
325 @Deactivate
326 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700327 cfgService.unregisterProperties(getClass(), false);
328 flowRuleService.removeListener(internalFlowListener);
329 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000330 }
331
332 @Modified
333 public void modified(ComponentContext context) {
334
335 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
336
Saurav Dasf62cea82020-08-26 17:43:04 -0700337 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000338 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700339 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000340 }
341
Andrea Campanella7c49b792020-05-11 11:36:53 +0200342 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000343 if (v4 != null) {
344 enableDhcpV4 = v4;
345 }
346
Andrea Campanella7c49b792020-05-11 11:36:53 +0200347 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000348 if (v6 != null) {
349 enableDhcpV6 = v6;
350 }
351
Saurav Dasf62cea82020-08-26 17:43:04 -0700352 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000353 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700354 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000355 }
356
Andrea Campanella7c49b792020-05-11 11:36:53 +0200357 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000358 if (eap != null) {
359 enableEapol = eap;
360 }
361
yasin sapli0823c932022-01-26 11:26:09 +0000362 Boolean pppoeInNni = Tools.isPropertyEnabled(properties, ENABLE_PPPOE_ON_NNI);
363 if (pppoeInNni != null) {
364 enablePppoeOnNni = pppoeInNni;
365 }
366
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300367 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
368 if (pppoe != null) {
369 enablePppoe = pppoe;
370 }
371
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700372 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
373 if (wait != null) {
374 waitForRemoval = wait;
375 }
376
Andrea Campanella7c49b792020-05-11 11:36:53 +0200377 String tpId = get(properties, DEFAULT_TP_ID);
378 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000379
yasin sapli0823c932022-01-26 11:26:09 +0000380 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " + "enableDhcpV6:{}, " +
381 "enableIgmpOnNni:{}, " + "enableEapol:{}, enablePppoeOnNni: {}, enablePppoe:{}, " +
382 "defaultTechProfileId:{}," + "waitForRemoval:{}",
383 enableDhcpOnNni, enableDhcpV4, enableDhcpV6, enableIgmpOnNni, enableEapol,
384 enablePppoeOnNni, enablePppoe, defaultTechProfileId, waitForRemoval);
Andrea Campanellafee86422020-06-04 16:01:27 +0200385
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000386 }
387
388 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700389 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
390 try {
391 cpStatusReadLock.lock();
392 return ImmutableMap.copyOf(cpStatus);
393 } finally {
394 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000395 }
396 }
397
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700398 @Override
399 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
400 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800401 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700402 Map<ServiceKey, UniTagInformation> subscribers =
403 new HashMap<>();
404 try {
405 cpStatusReadLock.lock();
406
407 cpStatus.forEach((sk, status) -> {
408 if (
409 // not NNI Port
410 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
411 sk.getPort().connectPoint().port()) &&
412 // not EAPOL flow
Andrea Campanella982fd332022-01-19 09:14:12 +0100413 !sk.getService().equals(defaultEapolUniTag) &&
414 !status.subscriberFlowsStatus.equals(OltFlowsStatus.PENDING_REMOVE)
415 && !status.subscriberFlowsStatus.equals(OltFlowsStatus.REMOVED)
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800416
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700417 ) {
418 subscribers.put(sk, sk.getService());
419 }
420 });
421
422 return ImmutableMap.copyOf(subscribers);
423 } finally {
424 cpStatusReadLock.unlock();
425 }
426 }
427
428 @Override
429 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
430 try {
431 provisionedSubscribersReadLock.lock();
432 return ImmutableMap.copyOf(provisionedSubscribers);
433 } finally {
434 provisionedSubscribersReadLock.unlock();
435 }
436 }
437
438 @Override
439 public void handleNniFlows(Device device, Port port, FlowOperation action) {
440
441 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800442 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700443 processLldpFilteringObjective(device.id(), port, action);
444
445 if (enableDhcpOnNni) {
446 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800447 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
448 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700449 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
450 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
451 null, null, nniUniTag);
452 }
453 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800454 log.debug("{} DHCPv6 trap flow on NNI {} for device {}", flowOpToString(action),
455 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700456 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
457 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
458 null, null, nniUniTag);
459 }
460 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800461 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700462 }
463
464 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800465 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700466 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
467 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
468 }
469
yasin sapli0823c932022-01-26 11:26:09 +0000470 if (enablePppoeOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800471 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700472 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
473 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
474 }
475 }
476
477 @Override
478 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
479 String oltBandwidthProfileId) {
480
481 // we only need to something if EAPOL is enabled
482 if (!enableEapol) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100483 log.debug("Eapol is disabled for {}", portWithName(sub.port));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700484 return true;
485 }
486
487 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
488 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
489 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
490 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
491 } else {
492 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
493 return false;
494 }
495
496 }
497
498 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100499 log.debug("Adding default flows for {}, status {}", portWithName(sub.port), sub.status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700500 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
501 if (log.isTraceEnabled()) {
502 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
503 }
504 return false;
505 }
506 if (hasDefaultEapol(sub.port)) {
Andrea Campanellaccb32862022-02-17 16:29:10 +0100507 OltPortStatus status = getOltPortStatus(sub.port, defaultEapolUniTag);
508 log.debug("Eapol is already present for {} with status {}", portWithName(sub.port), status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700509 return true;
510 }
511 return handleEapolFlow(sub, bandwidthProfileId,
512 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
513
514 }
515
516 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
517 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
518 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
519 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
520 }
521
522 @Override
523 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
524 String multicastServiceName) {
525 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
526 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
527 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
528 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
529 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
530 } else {
531 log.error("don't know how to handle {}", sub);
532 return false;
533 }
534 }
535
536 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
537 String multicastServiceName) {
538 if (log.isTraceEnabled()) {
539 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
540 }
541 if (enableEapol) {
542 if (hasDefaultEapol(sub.port)) {
543 // remove EAPOL flow and throw exception so that we'll retry later
544 if (!isDefaultEapolPendingRemoval(sub.port)) {
545 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
546 }
547
548 if (waitForRemoval) {
549 // NOTE wait for removal is a flag only needed to make sure VOLTHA
550 // does not explode with the flows remove/add in the same batch
551 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
552 return false;
553 } else {
554 log.warn("continuing provisioning on {}", portWithName(sub.port));
555 }
556 }
557
558 }
559
560 // NOTE createMeters will return if the meters are not installed
561 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800562 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700563 return false;
564 }
565
566 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
567 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
568 sub.subscriberAndDeviceInformation);
569
570 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
571 && !isMacAddressAvailable(sub.device.id(), sub.port,
572 sub.subscriberAndDeviceInformation)) {
573 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
574 return false;
575 }
576
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800577 // NOTE that the EAPOL flows handling is based on the data-plane flows status
578 // always process them before
579 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
580
yasin sapli0823c932022-01-26 11:26:09 +0000581 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
582
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700583 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
584 sub.subscriberAndDeviceInformation, multicastServiceName);
585
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700586 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
587
588 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
589 return true;
590 }
591
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800592 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700593 String multicastServiceName) {
594
595 if (log.isTraceEnabled()) {
596 log.trace("Removal of subscriber on {} started",
597 portWithName(sub.port));
598 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800599 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700600
601 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
602
yasin sapli0823c932022-01-26 11:26:09 +0000603 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, sub.subscriberAndDeviceInformation);
604
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700605 if (enableEapol) {
606 // remove the tagged eapol
607 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800608 }
609 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700610
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800611 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
612
613 if (enableEapol) {
614
615 // if any of the services still has flows, return false
616 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
617 while (iter.hasNext()) {
618 UniTagInformation entry = iter.next();
Andrea Campanella87241ae2022-03-11 11:20:24 +0100619 if (areSubscriberFlowsPendingRemoval(sub.port, entry, enableEapol)) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800620 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
621 portWithName(sub.port), entry.getServiceName());
622 return false;
623 }
624 }
625
626 // once the flows are removed add the default one back
627 // (only if the port is ENABLED and still present on the device)
Matteo Scandolo49c42052021-11-23 13:12:29 -0800628 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
629
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700630 // NOTE we remove the subscriber when the port goes down
631 // but in that case we don't need to add default eapol
632 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
633 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
634 }
635 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700636 // FIXME check the return status of the flow and return accordingly
637 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
638 return true;
639 }
640
641 @Override
642 public boolean hasDefaultEapol(Port port) {
643 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
644 // NOTE we consider ERROR as a present EAPOL flow as ONOS
645 // will keep trying to add it
646 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
647 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
648 status.defaultEapolStatus == OltFlowsStatus.ERROR);
649 }
650
651 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
652 try {
653 cpStatusReadLock.lock();
654 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
655 OltPortStatus status = cpStatus.get(sk);
656 return status;
657 } finally {
658 cpStatusReadLock.unlock();
659 }
660 }
661
662 public boolean isDefaultEapolPendingRemoval(Port port) {
663 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
664 if (log.isTraceEnabled()) {
665 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
666 status, portWithName(port), defaultEapolUniTag);
667 }
668 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
669 }
670
671 @Override
672 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
673 OltPortStatus status = getOltPortStatus(port, uti);
674 if (log.isTraceEnabled()) {
675 log.trace("Status during DHCP flow check {} for port {} and service {}",
676 status, portWithName(port), uti.getServiceName());
677 }
678 return status != null &&
679 (status.dhcpStatus == OltFlowsStatus.ADDED ||
680 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
681 }
682
683 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000684 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
685 OltPortStatus status = getOltPortStatus(port, uti);
686 if (log.isTraceEnabled()) {
687 log.trace("Status during PPPoE flow check {} for port {} and service {}",
688 status, portWithName(port), uti.getServiceName());
689 }
690 return status != null &&
691 (status.pppoeStatus == OltFlowsStatus.ADDED ||
692 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
693 }
694
695 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700696 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
697
698 OltPortStatus status = getOltPortStatus(port, uti);
699 if (log.isTraceEnabled()) {
700 log.trace("Status during subscriber flow check {} for port {} and service {}",
701 status, portWithName(port), uti.getServiceName());
702 }
703 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
704 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
705 }
706
Andrea Campanella87241ae2022-03-11 11:20:24 +0100707 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti, boolean enableEapol) {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800708 OltPortStatus status = getOltPortStatus(port, uti);
709 if (log.isTraceEnabled()) {
710 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
711 status, portWithName(port), uti);
712 }
Andrea Campanella87241ae2022-03-11 11:20:24 +0100713 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE ||
714 (enableEapol && status.subscriberEapolStatus == OltFlowsStatus.PENDING_REMOVE) ||
715 (uti.getIsDhcpRequired() && status.dhcpStatus == OltFlowsStatus.PENDING_REMOVE));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800716 }
717
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700718 @Override
719 public void purgeDeviceFlows(DeviceId deviceId) {
720 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800721 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700722
723 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800724 if (log.isTraceEnabled()) {
725 log.trace("Clearing cp status from device {}", deviceId);
726 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700727 try {
728 cpStatusWriteLock.lock();
729 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
730 while (iter.hasNext()) {
731 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
732 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
733 cpStatus.remove(entry.getKey());
734 }
735 }
736 } finally {
737 cpStatusWriteLock.unlock();
738 }
739
740 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800741 if (log.isTraceEnabled()) {
742 log.trace("Clearing provisioned subscribers from device {}", deviceId);
743 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700744 try {
745 provisionedSubscribersWriteLock.lock();
746 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
747 while (iter.hasNext()) {
748 Map.Entry<ServiceKey, Boolean> entry = iter.next();
749 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
750 provisionedSubscribers.remove(entry.getKey());
751 }
752 }
753 } finally {
754 provisionedSubscribersWriteLock.unlock();
755 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800756 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700757 }
758
759 @Override
760 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800761 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700762 try {
763 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800764 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700765 } finally {
766 provisionedSubscribersReadLock.unlock();
767 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800768
769 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
770 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
771 return true;
772 }
773 }
774 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700775 }
776
777 @Override
778 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
779 try {
780 provisionedSubscribersReadLock.lock();
781 Boolean provisioned = provisionedSubscribers.get(sk);
782 if (provisioned == null || !provisioned) {
783 return false;
784 }
785 } finally {
786 provisionedSubscribersReadLock.unlock();
787 }
788 return true;
789 }
790
791 @Override
792 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
793 try {
794 provisionedSubscribersWriteLock.lock();
795 provisionedSubscribers.put(sk, status);
796 } finally {
797 provisionedSubscribersWriteLock.unlock();
798 }
799 }
800
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800801 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700802 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
803
804 // create a subscriberKey for the EAPOL flow
805 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
Andrea Campanella87241ae2022-03-11 11:20:24 +0100806 OltFlowsStatus status = action == FlowOperation.ADD ?
807 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700808 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
Andrea Campanella87241ae2022-03-11 11:20:24 +0100809 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE,
810 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700811
Andrea Campanella87241ae2022-03-11 11:20:24 +0100812 } else {
813 updateConnectPointStatus(sk, OltFlowsStatus.NONE, status, OltFlowsStatus.NONE,
814 OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700815 }
816
817 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
818 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
819
820 int techProfileId = getDefaultTechProfileId(sub.port);
821 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
822
823 // in the delete case the meter should still be there as we remove
824 // the meters only if no flows are pointing to them
825 if (meterId == null) {
826 log.debug("MeterId is null for BandwidthProfile {} on device {}",
827 bandwidthProfile, sub.device.id());
828 return false;
829 }
830
831 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
832 if (oltMeterId == null) {
833 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
834 oltBandwidthProfile, sub.device.id());
835 return false;
836 }
837
838 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
839 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
840
841 FilteringObjective.Builder eapolAction;
842
843 if (action == FlowOperation.ADD) {
844 eapolAction = filterBuilder.permit();
845 } else if (action == FlowOperation.REMOVE) {
846 eapolAction = filterBuilder.deny();
847 } else {
848 log.error("Operation {} not supported", action);
849 return false;
850 }
851
852 FilteringObjective.Builder baseEapol = eapolAction
853 .withKey(Criteria.matchInPort(sub.port.number()))
854 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
855
856 // NOTE we only need to add the treatment to install the flow,
857 // we can remove it based in the match
858 FilteringObjective.Builder eapol;
859
860 TrafficTreatment treatment = treatmentBuilder
861 .meter(meterId)
862 .writeMetadata(createTechProfValueForWriteMetadata(
863 vlanId,
864 techProfileId, oltMeterId), 0)
865 .setOutput(PortNumber.CONTROLLER)
866 .pushVlan()
867 .setVlanId(vlanId)
868 .build();
869 eapol = baseEapol
870 .withMeta(treatment);
871
872 FilteringObjective eapolObjective = eapol
873 .fromApp(appId)
874 .withPriority(MAX_PRIORITY)
875 .add(new ObjectiveContext() {
876 @Override
877 public void onSuccess(Objective objective) {
878 log.info("EAPOL flow objective {} for {}",
879 completeFlowOpToString(action), portWithName(sub.port));
880 if (log.isTraceEnabled()) {
881 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
882 }
883 }
884
885 @Override
886 public void onError(Objective objective, ObjectiveError error) {
887 log.error("Cannot {} eapol flow for {} : {}", action,
888 portWithName(sub.port), error);
889
890 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
891 updateConnectPointStatus(sk,
Andrea Campanella87241ae2022-03-11 11:20:24 +0100892 OltFlowsStatus.ERROR, null, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700893 }
894 }
895 });
896
897 flowObjectiveService.filter(sub.device.id(), eapolObjective);
898
899 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
900 return true;
901 }
902
903 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800904 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700905 SubscriberAndDeviceInformation si) {
906 if (!enableEapol) {
907 return true;
908 }
909 // TODO verify we need an EAPOL flow for EACH service
910 AtomicBoolean success = new AtomicBoolean(true);
911 si.uniTagList().forEach(u -> {
912 // we assume that if subscriber flows are installed, tagged EAPOL is installed as well
913 boolean hasFlows = hasSubscriberFlows(sub.port, u);
914
915 // if the subscriber flows are present the EAPOL flow is present thus we don't need to install it,
916 // if the subscriber does not have flows the EAPOL flow is not present thus we don't need to remove it
917 if (action == FlowOperation.ADD && hasFlows ||
918 action == FlowOperation.REMOVE && !hasFlows) {
919 log.debug("Not {} EAPOL on {}({}) as subscriber flow status is {}", flowOpToString(action),
920 portWithName(sub.port), u.getServiceName(), hasFlows);
921 return;
922 }
923 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
924 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
925 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
926 u.getUpstreamOltBandwidthProfile(),
927 action, u.getPonCTag())) {
928 //
Andrea Campanella87241ae2022-03-11 11:20:24 +0100929 log.error("Failed to {} EAPOL with subscriber tags", action);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700930 //TODO this sets it for all services, maybe some services succeeded.
931 success.set(false);
932 }
933 });
934 return success.get();
935 }
936
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800937 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700938 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
939 if (uti.getIsIgmpRequired()) {
940 DeviceId deviceId = sub.device.id();
941 // if we reached here a meter already exists
942 MeterId meterId = oltMeterService
943 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
944 MeterId oltMeterId = oltMeterService
945 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
946
947 processIgmpFilteringObjectives(deviceId, sub.port,
948 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
949 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
950 }
951 });
952 }
953
954 private boolean checkSadisRunning() {
955 if (bpService == null) {
956 log.warn("Sadis is not running");
957 return false;
958 }
959 return true;
960 }
961
962 private int getDefaultTechProfileId(Port port) {
963 if (!checkSadisRunning()) {
964 return defaultTechProfileId;
965 }
966 if (port != null) {
967 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
968 if (info != null && info.uniTagList().size() == 1) {
969 return info.uniTagList().get(0).getTechnologyProfileId();
970 }
971 }
972 return defaultTechProfileId;
973 }
974
975 protected Long createTechProfValueForWriteMetadata(VlanId cVlan, int techProfileId, MeterId upstreamOltMeterId) {
976 Long writeMetadata;
977
978 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
979 writeMetadata = (long) techProfileId << 32;
980 } else {
981 writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
982 }
983 if (upstreamOltMeterId == null) {
984 return writeMetadata;
985 } else {
986 return writeMetadata | upstreamOltMeterId.id();
987 }
988 }
989
990 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
991 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
992
993 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
994 .withKey(Criteria.matchInPort(port.number()))
995 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
996 .withMeta(DefaultTrafficTreatment.builder()
997 .setOutput(PortNumber.CONTROLLER).build())
998 .fromApp(appId)
999 .withPriority(MAX_PRIORITY)
1000 .add(new ObjectiveContext() {
1001 @Override
1002 public void onSuccess(Objective objective) {
1003 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
1004 }
1005
1006 @Override
1007 public void onError(Objective objective, ObjectiveError error) {
1008 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
1009 error);
1010 }
1011 });
1012
1013 flowObjectiveService.filter(deviceId, lldp);
1014 }
1015
1016 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
1017 FlowOperation action,
1018 SubscriberAndDeviceInformation si) {
1019 si.uniTagList().forEach(uti -> {
1020
1021 if (!uti.getIsDhcpRequired()) {
1022 return;
1023 }
1024
1025 // if it's an ADD skip if flows are there,
1026 // if it's a DELETE skip if flows are not there
1027 boolean hasFlows = hasDhcpFlows(port, uti);
1028 if (action == FlowOperation.ADD && hasFlows ||
1029 action == FlowOperation.REMOVE && !hasFlows) {
1030 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1031 uti.getServiceName(), hasFlows);
1032 return;
1033 }
1034
1035 log.info("{} DHCP flows for subscriber on {} and service {}",
1036 flowOpToString(action), portWithName(port), uti.getServiceName());
1037
1038 // if we reached here a meter already exists
1039 MeterId meterId = oltMeterService
1040 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1041 MeterId oltMeterId = oltMeterService
1042 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1043
1044 if (enableDhcpV4) {
1045 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1046 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1047 uti);
1048 }
1049 if (enableDhcpV6) {
1050 log.error("DHCP V6 not supported for subscribers");
1051 }
1052 });
1053 }
1054
yasin sapli0823c932022-01-26 11:26:09 +00001055 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1056 FlowOperation action,
1057 SubscriberAndDeviceInformation si) {
1058 si.uniTagList().forEach(uti -> {
1059
1060 if (!uti.getIsPppoeRequired()) {
1061 return;
1062 }
1063
1064 // if it's an ADD skip if flows are there,
1065 // if it's a DELETE skip if flows are not there
1066 boolean hasFlows = hasPppoeFlows(port, uti);
1067 if (action == FlowOperation.ADD && hasFlows ||
1068 action == FlowOperation.REMOVE && !hasFlows) {
1069 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1070 uti.getServiceName(), hasFlows);
1071 return;
1072 }
1073
1074 log.info("{} PPPoE flows for subscriber on {} and service {}",
1075 flowOpToString(action), portWithName(port), uti.getServiceName());
1076
1077 // if we reached here a meter already exists
1078 MeterId meterId = oltMeterService
1079 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1080 MeterId oltMeterId = oltMeterService
1081 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1082
1083 if (enablePppoe) {
1084 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1085 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1086 (byte) uti.getUsPonCTagPriority());
1087 }
1088 });
1089 }
1090
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001091 // FIXME return boolean, if this fails we need to retry
1092 protected void handleSubscriberDataFlows(Device device, Port port,
1093 FlowOperation action,
1094 SubscriberAndDeviceInformation si, String multicastServiceName) {
1095
1096 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001097 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001098 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1099 si.id(), portWithName(port));
1100 return;
1101 }
1102 si.uniTagList().forEach(uti -> {
1103
1104 boolean hasFlows = hasSubscriberFlows(port, uti);
1105 if (action == FlowOperation.ADD && hasFlows ||
1106 action == FlowOperation.REMOVE && !hasFlows) {
1107 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1108 uti.getServiceName(), hasFlows);
1109 return;
1110 }
1111
1112 if (multicastServiceName.equals(uti.getServiceName())) {
1113 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1114 "dataplane flows are not needed",
1115 uti.getServiceName(), si.id(), portWithName(port));
1116 return;
1117 }
1118
1119 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1120 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001121 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1122 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1123 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001124 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001125
1126 // upstream flows
1127 MeterId usMeterId = oltMeterService
1128 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1129 MeterId oltUsMeterId = oltMeterService
1130 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
1131 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1132 oltUsMeterId, uti);
1133
1134 // downstream flows
1135 MeterId dsMeterId = oltMeterService
1136 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1137 MeterId oltDsMeterId = oltMeterService
1138 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
1139 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1140 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1141 });
1142 }
1143
1144 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1145 FlowOperation action, FlowDirection direction,
1146 int udpSrc, int udpDst, EthType ethType, byte protocol,
1147 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1148 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1149 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1150
1151
1152 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1153 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
Andrea Campanella87241ae2022-03-11 11:20:24 +01001154 updateConnectPointStatus(sk, null, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001155
1156 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1157 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1158
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001159 if (meterId != null) {
1160 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001161 }
1162
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001163 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
1164 treatmentBuilder.writeMetadata(
1165 createTechProfValueForWriteMetadata(uti.getUniTagMatch(),
1166 uti.getTechnologyProfileId(), oltMeterId), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001167 }
1168
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001169 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001170 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001171 .addCondition(Criteria.matchEthType(ethType))
1172 .addCondition(Criteria.matchIPProtocol(protocol))
1173 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1174 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001175 .fromApp(appId)
1176 .withPriority(MAX_PRIORITY);
1177
Andrea Campanella0e34f562020-06-11 10:47:10 +02001178 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001179 if (direction == FlowDirection.UPSTREAM) {
1180 treatmentBuilder.setVlanId(uti.getPonCTag());
1181 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1182 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001183 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001184 if (uti.getUsPonCTagPriority() != -1) {
1185 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001186 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001187 }
1188
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001189 dhcpBuilder.withMeta(treatmentBuilder
1190 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001191
1192
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001193 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001194 @Override
1195 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001196 log.info("{} DHCP {} filter for {}.",
1197 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1198 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001199 }
1200
1201 @Override
1202 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001203 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001204 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1205 portWithName(port),
1206 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001207 error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001208 updateConnectPointStatus(sk, null, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001209 }
1210 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001211 flowObjectiveService.filter(deviceId, dhcpUpstream);
1212 }
1213
1214 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1215 FlowOperation action, FlowDirection direction,
1216 MeterId meterId, MeterId oltMeterId, int techProfileId,
1217 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1218
1219 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1220 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1221 if (direction == FlowDirection.UPSTREAM) {
1222
1223 if (techProfileId != NONE_TP_ID) {
1224 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(null,
1225 techProfileId, oltMeterId), 0);
1226 }
1227
1228
1229 if (meterId != null) {
1230 treatmentBuilder.meter(meterId);
1231 }
1232
1233 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1234 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1235 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001236 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1237 treatmentBuilder.setVlanId(cTag);
1238 }
1239
1240 if (vlanPcp != -1) {
1241 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1242 }
1243 }
1244
1245 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1246
1247 FilteringObjective igmp = filterBuilder
1248 .withKey(Criteria.matchInPort(port.number()))
1249 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1250 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1251 .withMeta(treatmentBuilder
1252 .setOutput(PortNumber.CONTROLLER).build())
1253 .fromApp(appId)
1254 .withPriority(MAX_PRIORITY)
1255 .add(new ObjectiveContext() {
1256 @Override
1257 public void onSuccess(Objective objective) {
1258 log.info("Igmp filter for {} {}.", portWithName(port), action);
1259 }
1260
1261 @Override
1262 public void onError(Objective objective, ObjectiveError error) {
1263 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1264 error);
1265 }
1266 });
1267
1268 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001269
1270 }
1271
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001272 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1273 FlowOperation action, FlowDirection direction,
1274 MeterId meterId, MeterId oltMeterId, int techProfileId,
1275 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001276
1277 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1278 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001279
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001280 if (meterId != null) {
1281 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001282 }
1283
1284 if (techProfileId != NONE_TP_ID) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001285 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(cTag, techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001286 }
1287
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001288 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1289 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001290 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001291 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1292 .fromApp(appId)
1293 .withPriority(10000);
1294
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001295 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001296 treatmentBuilder.setVlanId(cTag);
1297 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1298 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1299 }
1300 if (vlanPcp != null) {
1301 treatmentBuilder.setVlanPcp(vlanPcp);
1302 }
1303 }
1304 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1305
1306 FilteringObjective pppoed = pppoedBuilder
1307 .add(new ObjectiveContext() {
1308 @Override
1309 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001310 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001311 }
1312
1313 @Override
1314 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001315 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1316 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001317 }
1318 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001319 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001320 }
1321
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001322 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1323 FlowOperation action,
1324 MeterId upstreamMeterId,
1325 MeterId upstreamOltMeterId,
1326 UniTagInformation uti) {
1327 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001328 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001329 .matchInPort(port.number())
1330 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001331 .build();
1332
Andrea Campanella327c5722020-01-30 11:34:13 +01001333 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1334 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001335 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001336 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001337 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001338 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001339 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1340 treatmentBuilder.popVlan();
1341 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001342
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001343 if (uti.getUsPonCTagPriority() != -1) {
1344 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001345
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001346 }
1347
1348 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001349 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001350
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001351 if (uti.getUsPonSTagPriority() != -1) {
1352 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001353 }
1354
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001355 treatmentBuilder.setOutput(nniPort.number())
1356 .writeMetadata(createMetadata(uti.getPonCTag(),
1357 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001358
yasin saplib4b8ee12021-06-13 18:25:20 +00001359 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1360
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001361 if (upstreamMeterId != null) {
1362 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001363 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1364 }
1365 if (upstreamOltMeterId != null) {
1366 treatmentBuilder.meter(upstreamOltMeterId);
1367 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001368 }
1369
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001370 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1371 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001372 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001373
1374 ObjectiveContext context = new ObjectiveContext() {
1375 @Override
1376 public void onSuccess(Objective objective) {
1377 log.info("{} Upstream Data plane filter for {}.",
1378 completeFlowOpToString(action), sk);
1379 }
1380
1381 @Override
1382 public void onError(Objective objective, ObjectiveError error) {
1383 log.error("Upstream Data plane filter for {} failed {} because {}.",
1384 sk, action, error);
Andrea Campanella87241ae2022-03-11 11:20:24 +01001385 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001386 }
1387 };
1388
1389 ForwardingObjective flow = null;
1390 if (action == FlowOperation.ADD) {
1391 flow = flowBuilder.add(context);
1392 } else if (action == FlowOperation.REMOVE) {
1393 flow = flowBuilder.remove(context);
1394 } else {
1395 log.error("Flow action not supported: {}", action);
1396 }
1397
1398 if (flow != null) {
1399 flowObjectiveService.forward(deviceId, flow);
1400 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001401 }
1402
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001403 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1404 FlowOperation action,
1405 MeterId downstreamMeterId,
1406 MeterId downstreamOltMeterId,
1407 UniTagInformation uti,
1408 MacAddress macAddress) {
1409 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001410 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001411 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001412 .matchVlanId(uti.getPonSTag())
1413 .matchInPort(nniPort.number())
1414 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001415
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001416 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1417 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001418 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001419
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001420 if (uti.getDsPonCTagPriority() != -1) {
1421 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001422 }
1423
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001424 if (macAddress != null) {
1425 selectorBuilder.matchEthDst(macAddress);
1426 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001427
1428 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1429 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001430 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001431
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001432 treatmentBuilder.writeMetadata(createMetadata(uti.getPonCTag(),
1433 uti.getTechnologyProfileId(),
1434 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001435
Andrea Campanella981e86c2021-03-12 11:35:33 +01001436 // Upstream pbit is used to remark inner vlan pbit.
1437 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1438 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1439 // all pbit acceptance are not widely supported by vendors even though present in
1440 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001441 if (uti.getUsPonCTagPriority() != -1) {
1442 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001443 }
1444
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001445 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1446 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1447 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001448 }
1449
yasin saplib4b8ee12021-06-13 18:25:20 +00001450 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1451
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001452 if (downstreamMeterId != null) {
1453 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001454 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001455 }
1456
yasin saplib4b8ee12021-06-13 18:25:20 +00001457 if (downstreamOltMeterId != null) {
1458 treatmentBuilder.meter(downstreamOltMeterId);
1459 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1460 }
1461
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001462 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1463 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001464
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001465 ObjectiveContext context = new ObjectiveContext() {
1466 @Override
1467 public void onSuccess(Objective objective) {
1468 log.info("{} Downstream Data plane filter for {}.",
1469 completeFlowOpToString(action), sk);
1470 }
1471
1472 @Override
1473 public void onError(Objective objective, ObjectiveError error) {
1474 log.info("Downstream Data plane filter for {} failed {} because {}.",
Andrea Campanella87241ae2022-03-11 11:20:24 +01001475 sk, action, error);
1476 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001477 }
1478 };
1479
1480 ForwardingObjective flow = null;
1481 if (action == FlowOperation.ADD) {
1482 flow = flowBuilder.add(context);
1483 } else if (action == FlowOperation.REMOVE) {
1484 flow = flowBuilder.remove(context);
1485 } else {
1486 log.error("Flow action not supported: {}", action);
1487 }
1488
1489 if (flow != null) {
1490 if (log.isTraceEnabled()) {
1491 log.trace("Forwarding rule {}", flow);
1492 }
1493 flowObjectiveService.forward(deviceId, flow);
1494 }
Andrea Campanella600d2e22020-06-22 11:00:31 +02001495 }
1496
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001497 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1498 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001499 Integer priority,
1500 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001501 return DefaultForwardingObjective.builder()
1502 .withFlag(ForwardingObjective.Flag.VERSATILE)
1503 .withPriority(priority)
1504 .makePermanent()
1505 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001506 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001507 .fromApp(appId)
1508 .withTreatment(treatment);
1509 }
1510
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001511 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
1512 if (techProfileId == NONE_TP_ID) {
Andrea Campanella7c49b792020-05-11 11:36:53 +02001513 techProfileId = DEFAULT_TP_ID_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001514 }
1515
1516 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
1517 }
1518
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001519 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1520 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1521 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001522
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001523 si.uniTagList().forEach(uniTagInfo -> {
1524 if (uniTagInfo.getEnableMacLearning()) {
1525 requiresMacLearning.set(true);
1526 }
1527 });
1528
1529 return requiresMacLearning.get();
1530 }
1531
1532 /**
1533 * Checks whether the subscriber has the MacAddress configured or discovered.
1534 *
1535 * @param deviceId DeviceId for this subscriber
1536 * @param port Port for this subscriber
1537 * @param si SubscriberAndDeviceInformation
1538 * @return boolean
1539 */
1540 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1541 AtomicBoolean isConfigured = new AtomicBoolean();
1542 isConfigured.set(true);
1543
1544 si.uniTagList().forEach(uniTagInfo -> {
1545 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
1546 boolean configureMac = isMacAddressValid(uniTagInfo);
1547 boolean discoveredMac = false;
1548 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1549 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1550 if (optHost.isPresent() && optHost.get().mac() != null) {
1551 discoveredMac = true;
1552 }
1553 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1554 log.debug("Awaiting for macAddress on {} for service {}",
1555 portWithName(port), uniTagInfo.getServiceName());
1556 isConfigured.set(false);
1557 }
1558 });
1559
1560 return isConfigured.get();
1561 }
1562
1563 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
1564 boolean configuredMac = isMacAddressValid(uniTagInfo);
1565 if (configuredMac) {
1566 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1567 } else if (uniTagInfo.getEnableMacLearning()) {
1568 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1569 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1570 if (optHost.isPresent() && optHost.get().mac() != null) {
1571 return optHost.get().mac();
1572 }
1573 }
1574 return null;
1575 }
1576
1577 private boolean isMacAddressValid(UniTagInformation tagInformation) {
1578 return tagInformation.getConfiguredMacAddress() != null &&
1579 !tagInformation.getConfiguredMacAddress().trim().equals("") &&
1580 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
1581 }
1582
1583 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001584 OltFlowsStatus subscriberEapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001585 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1586 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001587 if (log.isTraceEnabled()) {
Andrea Campanella87241ae2022-03-11 11:20:24 +01001588 log.trace("Updating cpStatus {} with values: eapolFlow={}, " +
1589 "subscriberEapolStatus={}, subscriberFlows={}, dhcpFlow={}",
1590 key, eapolStatus, subscriberEapolStatus, subscriberFlowsStatus, dhcpStatus);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001591 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001592 try {
1593 cpStatusWriteLock.lock();
1594 OltPortStatus status = cpStatus.get(key);
1595
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001596
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001597 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001598 // if we don't have status for the connectPoint
1599 // and we're only updating status to PENDING_REMOVE or ERROR
1600 // do not create it. This is because this case will only happen when a device is removed
1601 // and it's status cleaned
1602 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1603 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1604 statusesToIgnore.add(OltFlowsStatus.ERROR);
1605
1606 if (
1607 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1608 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1609 ) {
1610 if (log.isTraceEnabled()) {
1611 log.trace("Ignoring cpStatus update as status is meaningless");
1612 }
1613 return;
1614 }
1615
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001616 status = new OltPortStatus(
1617 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
Andrea Campanella87241ae2022-03-11 11:20:24 +01001618 subscriberEapolStatus != null ? subscriberEapolStatus : OltFlowsStatus.NONE,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001619 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001620 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1621 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001622 );
1623 } else {
1624 if (eapolStatus != null) {
1625 status.defaultEapolStatus = eapolStatus;
1626 }
1627 if (subscriberFlowsStatus != null) {
1628 status.subscriberFlowsStatus = subscriberFlowsStatus;
1629 }
1630 if (dhcpStatus != null) {
1631 status.dhcpStatus = dhcpStatus;
1632 }
1633 }
1634
1635 cpStatus.put(key, status);
1636 } finally {
1637 cpStatusWriteLock.unlock();
1638 }
1639 }
1640
1641 protected class InternalFlowListener implements FlowRuleListener {
1642 @Override
1643 public void event(FlowRuleEvent event) {
1644 if (appId.id() != (event.subject().appId())) {
1645 return;
1646 }
1647
1648 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1649 if (log.isTraceEnabled()) {
1650 log.trace("ignoring flow event {} " +
1651 "as not leader for {}", event, event.subject().deviceId());
1652 }
1653 return;
1654 }
1655
1656 switch (event.type()) {
1657 case RULE_ADDED:
1658 case RULE_REMOVED:
Andrea Campanella40d2b342022-02-04 18:13:37 +01001659 DeviceId deviceId = event.subject().deviceId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001660 Port port = getCpFromFlowRule(event.subject());
1661 if (port == null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001662 log.warn("Port is gone in ONOS, " +
1663 "manually creating it {}", event.subject());
1664 PortNumber inPort = getPortNumberFromFlowRule(event.subject());
1665 cpStatusReadLock.lock();
1666 Optional<ServiceKey> keyWithPort = cpStatus.keySet()
1667 .stream().filter(key -> key.getPort().connectPoint()
1668 .deviceId().equals(deviceId)
1669 && key.getPort().connectPoint().port()
1670 .equals(inPort)).findFirst();
1671 cpStatusReadLock.unlock();
1672 if (keyWithPort.isPresent()) {
1673 port = new DefaultPort(deviceService.getDevice(deviceId),
1674 inPort, false,
1675 DefaultAnnotations.builder()
1676 .set(AnnotationKeys.PORT_NAME,
1677 keyWithPort.get().getPort().name())
1678 .build());
1679 } else {
1680 log.warn("Can't find corresponding status for {}/{}", deviceId, inPort);
1681 return;
1682 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001683 }
1684 if (log.isTraceEnabled()) {
1685 log.trace("flow event {} on cp {}: {}", event.type(),
1686 portWithName(port), event.subject());
1687 }
1688 updateCpStatus(event.type(), port, event.subject());
1689 return;
1690 case RULE_ADD_REQUESTED:
1691 case RULE_REMOVE_REQUESTED:
1692 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1693 return;
1694 default:
1695 return;
1696 }
1697 }
1698
1699 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
1700 OltFlowsStatus status = flowRuleStatusToOltFlowStatus(type);
1701 if (isDefaultEapolFlow(flowRule)) {
1702 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1703 defaultEapolUniTag);
1704 if (log.isTraceEnabled()) {
1705 log.trace("update defaultEapolStatus {} on {}", status, sk);
1706 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001707 updateConnectPointStatus(sk, status, null, null, null, null);
1708 } else if (isSubscriberEapolFlow(flowRule)) {
1709 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
1710 if (sk == null) {
1711 return;
1712 }
1713 if (log.isTraceEnabled()) {
1714 log.trace("update subscriberEapolStatus {} on {}", status, sk);
1715 }
1716 updateConnectPointStatus(sk, null, status, null, status, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001717 } else if (isDhcpFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001718 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001719 if (sk == null) {
1720 return;
1721 }
1722 if (log.isTraceEnabled()) {
1723 log.trace("update dhcpStatus {} on {}", status, sk);
1724 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001725 updateConnectPointStatus(sk, null, null, null, status, null);
yasin sapli0823c932022-01-26 11:26:09 +00001726 } else if (isPppoeFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001727 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
yasin sapli0823c932022-01-26 11:26:09 +00001728 if (sk == null) {
1729 return;
1730 }
1731 if (log.isTraceEnabled()) {
1732 log.trace("update pppoeStatus {} on {}", status, sk);
1733 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001734 updateConnectPointStatus(sk, null, null, null, null, status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001735 } else if (isDataFlow(flowRule)) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001736 PortNumber number = getPortNumberFromFlowRule(flowRule);
1737 if (number == null) {
1738 log.error("Can't capture the port number from flow {}", flowRule);
1739 return;
1740 }
1741 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), number)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001742 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1743 return;
1744 }
1745
Andrea Campanella40d2b342022-02-04 18:13:37 +01001746 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule, port);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001747 if (sk == null) {
1748 return;
1749 }
1750 if (log.isTraceEnabled()) {
1751 log.trace("update dataplaneStatus {} on {}", status, sk);
1752 }
Andrea Campanella87241ae2022-03-11 11:20:24 +01001753 updateConnectPointStatus(sk, null, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001754 }
1755 }
1756
1757 private boolean isDefaultEapolFlow(FlowRule flowRule) {
1758 EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
1759 if (c == null) {
1760 return false;
1761 }
1762 if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
1763 AtomicBoolean isDefault = new AtomicBoolean(false);
1764 flowRule.treatment().allInstructions().forEach(instruction -> {
1765 if (instruction.type() == L2MODIFICATION) {
1766 L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
1767 if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
1768 L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
1769 (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
1770 if (vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
1771 isDefault.set(true);
1772 return;
1773 }
1774 }
1775 }
1776 });
1777 return isDefault.get();
1778 }
1779 return false;
1780 }
1781
1782 /**
1783 * Returns true if the flow is a DHCP flow.
1784 * Matches both upstream and downstream flows.
1785 *
1786 * @param flowRule The FlowRule to evaluate
1787 * @return boolean
1788 */
1789 private boolean isDhcpFlow(FlowRule flowRule) {
1790 IPProtocolCriterion ipCriterion = (IPProtocolCriterion) flowRule.selector()
1791 .getCriterion(Criterion.Type.IP_PROTO);
1792 if (ipCriterion == null) {
1793 return false;
1794 }
1795
1796 UdpPortCriterion src = (UdpPortCriterion) flowRule.selector().getCriterion(Criterion.Type.UDP_SRC);
1797
1798 if (src == null) {
1799 return false;
1800 }
1801 return ipCriterion.protocol() == IPv4.PROTOCOL_UDP &&
1802 (src.udpPort().toInt() == 68 || src.udpPort().toInt() == 67);
1803 }
1804
yasin sapli0823c932022-01-26 11:26:09 +00001805 private boolean isPppoeFlow(FlowRule flowRule) {
1806 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) flowRule.selector()
1807 .getCriterion(Criterion.Type.ETH_TYPE);
1808
1809 if (ethTypeCriterion == null) {
1810 return false;
1811 }
1812 return EthType.EtherType.PPPoED.ethType().equals(ethTypeCriterion.ethType());
1813 }
1814
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001815 private boolean isDataFlow(FlowRule flowRule) {
1816 // we consider subscriber flows the one that matches on VLAN_VID
1817 // method is valid only because it's the last check after EAPOL and DHCP.
1818 // this matches mcast flows as well, if we want to avoid that we can
1819 // filter out the elements that have groups in the treatment or
1820 // mcastIp in the selector
1821 // IPV4_DST:224.0.0.22/32
1822 // treatment=[immediate=[GROUP:0x1]]
1823
1824 return flowRule.selector().getCriterion(Criterion.Type.VLAN_VID) != null;
1825 }
1826
Andrea Campanella87241ae2022-03-11 11:20:24 +01001827 private boolean isSubscriberEapolFlow(FlowRule flowRule) {
1828 EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
1829 if (c == null) {
1830 return false;
1831 }
1832 if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
1833 AtomicBoolean isSubscriber = new AtomicBoolean(false);
1834 flowRule.treatment().allInstructions().forEach(instruction -> {
1835 if (instruction.type() == L2MODIFICATION) {
1836 L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
1837 if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
1838 L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
1839 (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
1840 if (!vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
1841 isSubscriber.set(true);
1842 return;
1843 }
1844 }
1845 }
1846 });
1847 return isSubscriber.get();
1848 }
1849 return false;
1850 }
1851
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001852 private Port getCpFromFlowRule(FlowRule flowRule) {
1853 DeviceId deviceId = flowRule.deviceId();
Andrea Campanella40d2b342022-02-04 18:13:37 +01001854 PortNumber inPort = getPortNumberFromFlowRule(flowRule);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001855 if (inPort != null) {
Andrea Campanella40d2b342022-02-04 18:13:37 +01001856 return deviceService.getPort(deviceId, inPort);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001857 }
1858 return null;
1859 }
1860
Andrea Campanella40d2b342022-02-04 18:13:37 +01001861 private PortNumber getPortNumberFromFlowRule(FlowRule flowRule) {
1862 PortCriterion inPort = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
1863 if (inPort != null) {
1864 return inPort.port();
1865 }
1866 return null;
1867 }
1868
1869 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule, Port flowPort) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001870 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1871
1872 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1873 if (si == null && !isNni) {
1874 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1875 return null;
1876 }
1877
1878 if (isNni) {
1879 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1880 }
1881
1882 Optional<UniTagInformation> found = Optional.empty();
1883 VlanId flowVlan = null;
1884 if (isDhcpFlow(flowRule)) {
1885 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1886 L2ModificationInstruction.ModVlanIdInstruction instruction =
1887 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1888 flowVlan = instruction.vlanId();
Andrea Campanella87241ae2022-03-11 11:20:24 +01001889 } else if (isSubscriberEapolFlow(flowRule)) {
1890 // we need to make a special case for EAPOL as in the ATT workflow EAPOL flows don't match on tags
1891 L2ModificationInstruction.ModVlanIdInstruction instruction =
1892 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(2);
1893 flowVlan = instruction.vlanId();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001894 } else {
1895 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1896 VlanIdCriterion vlanIdCriterion =
1897 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1898 if (vlanIdCriterion == null) {
1899 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1900 return null;
1901 }
1902 flowVlan = vlanIdCriterion.vlanId();
1903 }
1904
1905 VlanId finalFlowVlan = flowVlan;
1906 found = si.uniTagList().stream().filter(uti ->
1907 uti.getPonCTag().equals(finalFlowVlan) ||
1908 uti.getPonSTag().equals(finalFlowVlan) ||
1909 uti.getUniTagMatch().equals(finalFlowVlan)
1910 ).findFirst();
1911
1912
1913 if (found.isEmpty()) {
1914 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1915 }
1916
1917 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1918
1919 }
1920
1921 private OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
1922 switch (type) {
1923 case RULE_ADD_REQUESTED:
1924 return OltFlowsStatus.PENDING_ADD;
1925 case RULE_ADDED:
1926 return OltFlowsStatus.ADDED;
1927 case RULE_REMOVE_REQUESTED:
1928 return OltFlowsStatus.PENDING_REMOVE;
1929 case RULE_REMOVED:
1930 return OltFlowsStatus.REMOVED;
1931 default:
1932 return OltFlowsStatus.NONE;
1933 }
1934 }
1935 }
1936
1937 protected void bindSadisService(SadisService service) {
1938 this.subsService = service.getSubscriberInfoService();
1939 this.bpService = service.getBandwidthProfileService();
1940 log.info("Sadis service is loaded");
1941 }
1942
1943 protected void unbindSadisService(SadisService service) {
1944 this.subsService = null;
1945 this.bpService = null;
1946 log.info("Sadis service is unloaded");
1947 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001948}