blob: 88cd94298275f64c0f573f0021d49ed65fb932af [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;
yasin saplib4b8ee12021-06-13 18:25:20 +000031import org.onosproject.net.Annotations;
Matteo Scandolo3a037a32020-04-01 12:17:50 -070032import org.onosproject.net.ConnectPoint;
yasin saplib4b8ee12021-06-13 18:25:20 +000033import org.onosproject.net.DefaultAnnotations;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070034import org.onosproject.net.Device;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000035import org.onosproject.net.DeviceId;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070036import org.onosproject.net.Host;
37import org.onosproject.net.Port;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000038import org.onosproject.net.PortNumber;
39import org.onosproject.net.device.DeviceService;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070042import org.onosproject.net.flow.FlowRule;
43import org.onosproject.net.flow.FlowRuleEvent;
44import org.onosproject.net.flow.FlowRuleListener;
45import org.onosproject.net.flow.FlowRuleService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000046import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.flow.criteria.Criteria;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070049import org.onosproject.net.flow.criteria.Criterion;
50import org.onosproject.net.flow.criteria.EthTypeCriterion;
51import org.onosproject.net.flow.criteria.IPProtocolCriterion;
52import org.onosproject.net.flow.criteria.PortCriterion;
53import org.onosproject.net.flow.criteria.UdpPortCriterion;
54import org.onosproject.net.flow.criteria.VlanIdCriterion;
55import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000056import org.onosproject.net.flowobjective.DefaultFilteringObjective;
57import org.onosproject.net.flowobjective.DefaultForwardingObjective;
58import org.onosproject.net.flowobjective.FilteringObjective;
59import org.onosproject.net.flowobjective.FlowObjectiveService;
60import org.onosproject.net.flowobjective.ForwardingObjective;
61import org.onosproject.net.flowobjective.Objective;
62import org.onosproject.net.flowobjective.ObjectiveContext;
63import org.onosproject.net.flowobjective.ObjectiveError;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070064import org.onosproject.net.host.HostService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000065import org.onosproject.net.meter.MeterId;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000066import org.onosproject.store.serializers.KryoNamespaces;
67import org.onosproject.store.service.Serializer;
68import org.onosproject.store.service.StorageService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000069import org.opencord.sadis.BandwidthProfileInformation;
70import org.opencord.sadis.BaseInformationService;
71import org.opencord.sadis.SadisService;
72import org.opencord.sadis.SubscriberAndDeviceInformation;
73import org.opencord.sadis.UniTagInformation;
74import org.osgi.service.component.ComponentContext;
75import org.osgi.service.component.annotations.Activate;
76import org.osgi.service.component.annotations.Component;
77import org.osgi.service.component.annotations.Deactivate;
78import org.osgi.service.component.annotations.Modified;
79import org.osgi.service.component.annotations.Reference;
80import org.osgi.service.component.annotations.ReferenceCardinality;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000081import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000082import org.slf4j.Logger;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070083import org.slf4j.LoggerFactory;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000084
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080085import java.util.ArrayList;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010086import java.util.Dictionary;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070087import java.util.HashMap;
Andrea Campanella61650a12022-01-24 18:09:44 -080088import java.util.HashSet;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070089import java.util.Iterator;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080090import java.util.List;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000091import java.util.Map;
Andrea Campanellabfb47af2021-06-03 11:09:45 +020092import java.util.Optional;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010093import java.util.Properties;
Andrea Campanella61650a12022-01-24 18:09:44 -080094import java.util.Set;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070095import java.util.concurrent.atomic.AtomicBoolean;
96import java.util.concurrent.locks.Lock;
97import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010098
99import static com.google.common.base.Strings.isNullOrEmpty;
100import static org.onlab.util.Tools.get;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700101import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
102import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
103import static org.opencord.olt.impl.OltUtils.flowOpToString;
104import static org.opencord.olt.impl.OltUtils.getPortName;
105import static org.opencord.olt.impl.OltUtils.portWithName;
106import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
107import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
108import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
109import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
110import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
111import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
112import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
113import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
114import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
115import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
116import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
117import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
118import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
119import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
yasin sapli0823c932022-01-26 11:26:09 +0000120import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI;
121import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_ON_NNI_DEFAULT;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700122import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
123import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
124import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
125import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
126import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
127import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000128
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000129@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700130 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000131 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
132 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700133 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000134 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
yasin sapli0823c932022-01-26 11:26:09 +0000135 ENABLE_PPPOE_ON_NNI + ":Boolean=" + ENABLE_PPPOE_ON_NNI_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300136 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700137 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
138 // FIXME remove this option as potentially dangerous in production
139 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000140})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700141public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000142
143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
144 protected CoreService coreService;
145
146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700147 protected ComponentConfigService cfgService;
148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
150 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000151
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000152 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
153 bind = "bindSadisService",
154 unbind = "unbindSadisService",
155 policy = ReferencePolicy.DYNAMIC)
156 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000157
158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700159 protected OltMeterServiceInterface oltMeterService;
160
161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
162 protected OltDeviceServiceInterface oltDeviceService;
163
164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
165 protected FlowRuleService flowRuleService;
166
167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
168 protected HostService hostService;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000171 protected DeviceService deviceService;
172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000174 protected StorageService storageService;
175
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700176 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
177 protected BaseInformationService<BandwidthProfileInformation> bpService;
178
179 private static final String APP_NAME = "org.opencord.olt";
180 protected ApplicationId appId;
181 private static final Integer MAX_PRIORITY = 10000;
182 private static final Integer MIN_PRIORITY = 1000;
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800183 protected static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700184 private static final int NONE_TP_ID = -1;
185 private static final String V4 = "V4";
186 private static final String V6 = "V6";
187 private final Logger log = LoggerFactory.getLogger(getClass());
188
189 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
190 .setServiceName("defaultEapol").build();
191 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
192 .setServiceName("nni")
193 .setTechnologyProfileId(NONE_TP_ID)
194 .setPonCTag(VlanId.NONE)
195 .setUniTagMatch(VlanId.ANY)
196 .setUsPonCTagPriority(-1)
197 .build();
198
199 /**
200 * Connect Point status map.
201 * Used to keep track of which cp has flows that needs to be removed when the status changes.
202 */
203 protected Map<ServiceKey, OltPortStatus> cpStatus;
204 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
205 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
206 private final Lock cpStatusReadLock = cpStatusLock.readLock();
207
208 /**
209 * This map contains the subscriber that have been provisioned by the operator.
210 * They may or may not have flows, depending on the port status.
211 * The map is used to define whether flows need to be provisioned when a port comes up.
212 */
213 protected Map<ServiceKey, Boolean> provisionedSubscribers;
214 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
215 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
216 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
217
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000218 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700219 * Create DHCP trap flow on NNI port(s).
220 */
221 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000222
223 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700224 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000225 **/
226 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
227
228 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700229 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000230 **/
231 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
232
233 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700234 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000235 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700236 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000237
238 /**
239 * Send EAPOL authentication trap flows before subscriber provisioning.
240 **/
241 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
242
243 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300244 * Send PPPoED authentication trap flows before subscriber provisioning.
245 **/
yasin sapli0823c932022-01-26 11:26:09 +0000246 protected boolean enablePppoeOnNni = ENABLE_PPPOE_ON_NNI_DEFAULT;
247
248 /**
249 * Enable flows for PPPoE if it is required in sadis config.
250 **/
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300251 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
252
253 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000254 * Default technology profile id that is used for authentication trap flows.
255 **/
256 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
257
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700258 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
259
260 public enum FlowOperation {
261 ADD,
262 REMOVE;
263
264
265 @Override
266 public String toString() {
267 return super.toString().toLowerCase();
268 }
269 }
270
271 public enum FlowDirection {
272 UPSTREAM,
273 DOWNSTREAM,
274 }
275
276 public enum OltFlowsStatus {
277 NONE,
278 PENDING_ADD,
279 ADDED,
280 PENDING_REMOVE,
281 REMOVED,
282 ERROR
283 }
284
285 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000286
287 @Activate
288 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700289 cfgService.registerProperties(getClass());
290 appId = coreService.registerApplication(APP_NAME);
291 internalFlowListener = new InternalFlowListener();
292
293 modified(context);
294
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000295 KryoNamespace serializer = KryoNamespace.newBuilder()
296 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700297 .register(OltFlowsStatus.class)
298 .register(FlowDirection.class)
299 .register(OltPortStatus.class)
300 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000301 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700302 .register(new ServiceKeySerializer(), ServiceKey.class)
303 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000304 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000305
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700306 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
307 .withName("volt-cp-status")
308 .withApplicationId(appId)
309 .withSerializer(Serializer.using(serializer))
310 .build().asJavaMap();
311
312 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
313 .withName("volt-provisioned-subscriber")
314 .withApplicationId(appId)
315 .withSerializer(Serializer.using(serializer))
316 .build().asJavaMap();
317
318 flowRuleService.addListener(internalFlowListener);
319
320 log.info("Started");
321 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000322
323 @Deactivate
324 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700325 cfgService.unregisterProperties(getClass(), false);
326 flowRuleService.removeListener(internalFlowListener);
327 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000328 }
329
330 @Modified
331 public void modified(ComponentContext context) {
332
333 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
334
Saurav Dasf62cea82020-08-26 17:43:04 -0700335 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000336 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700337 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000338 }
339
Andrea Campanella7c49b792020-05-11 11:36:53 +0200340 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000341 if (v4 != null) {
342 enableDhcpV4 = v4;
343 }
344
Andrea Campanella7c49b792020-05-11 11:36:53 +0200345 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000346 if (v6 != null) {
347 enableDhcpV6 = v6;
348 }
349
Saurav Dasf62cea82020-08-26 17:43:04 -0700350 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000351 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700352 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000353 }
354
Andrea Campanella7c49b792020-05-11 11:36:53 +0200355 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000356 if (eap != null) {
357 enableEapol = eap;
358 }
359
yasin sapli0823c932022-01-26 11:26:09 +0000360 Boolean pppoeInNni = Tools.isPropertyEnabled(properties, ENABLE_PPPOE_ON_NNI);
361 if (pppoeInNni != null) {
362 enablePppoeOnNni = pppoeInNni;
363 }
364
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300365 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
366 if (pppoe != null) {
367 enablePppoe = pppoe;
368 }
369
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700370 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
371 if (wait != null) {
372 waitForRemoval = wait;
373 }
374
Andrea Campanella7c49b792020-05-11 11:36:53 +0200375 String tpId = get(properties, DEFAULT_TP_ID);
376 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000377
yasin sapli0823c932022-01-26 11:26:09 +0000378 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " + "enableDhcpV6:{}, " +
379 "enableIgmpOnNni:{}, " + "enableEapol:{}, enablePppoeOnNni: {}, enablePppoe:{}, " +
380 "defaultTechProfileId:{}," + "waitForRemoval:{}",
381 enableDhcpOnNni, enableDhcpV4, enableDhcpV6, enableIgmpOnNni, enableEapol,
382 enablePppoeOnNni, enablePppoe, defaultTechProfileId, waitForRemoval);
Andrea Campanellafee86422020-06-04 16:01:27 +0200383
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000384 }
385
386 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700387 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
388 try {
389 cpStatusReadLock.lock();
390 return ImmutableMap.copyOf(cpStatus);
391 } finally {
392 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000393 }
394 }
395
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700396 @Override
397 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
398 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800399 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700400 Map<ServiceKey, UniTagInformation> subscribers =
401 new HashMap<>();
402 try {
403 cpStatusReadLock.lock();
404
405 cpStatus.forEach((sk, status) -> {
406 if (
407 // not NNI Port
408 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
409 sk.getPort().connectPoint().port()) &&
410 // not EAPOL flow
Andrea Campanella982fd332022-01-19 09:14:12 +0100411 !sk.getService().equals(defaultEapolUniTag) &&
412 !status.subscriberFlowsStatus.equals(OltFlowsStatus.PENDING_REMOVE)
413 && !status.subscriberFlowsStatus.equals(OltFlowsStatus.REMOVED)
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800414
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700415 ) {
416 subscribers.put(sk, sk.getService());
417 }
418 });
419
420 return ImmutableMap.copyOf(subscribers);
421 } finally {
422 cpStatusReadLock.unlock();
423 }
424 }
425
426 @Override
427 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
428 try {
429 provisionedSubscribersReadLock.lock();
430 return ImmutableMap.copyOf(provisionedSubscribers);
431 } finally {
432 provisionedSubscribersReadLock.unlock();
433 }
434 }
435
436 @Override
437 public void handleNniFlows(Device device, Port port, FlowOperation action) {
438
439 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800440 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700441 processLldpFilteringObjective(device.id(), port, action);
442
443 if (enableDhcpOnNni) {
444 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800445 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
446 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700447 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
448 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
449 null, null, nniUniTag);
450 }
451 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800452 log.debug("{} DHCPv6 trap flow on NNI {} for device {}", flowOpToString(action),
453 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700454 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
455 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
456 null, null, nniUniTag);
457 }
458 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800459 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700460 }
461
462 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800463 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700464 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
465 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
466 }
467
yasin sapli0823c932022-01-26 11:26:09 +0000468 if (enablePppoeOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800469 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700470 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
471 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
472 }
473 }
474
475 @Override
476 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
477 String oltBandwidthProfileId) {
478
479 // we only need to something if EAPOL is enabled
480 if (!enableEapol) {
481 return true;
482 }
483
484 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
485 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
486 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
487 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
488 } else {
489 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
490 return false;
491 }
492
493 }
494
495 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
496
497 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
498 if (log.isTraceEnabled()) {
499 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
500 }
501 return false;
502 }
503 if (hasDefaultEapol(sub.port)) {
504 return true;
505 }
506 return handleEapolFlow(sub, bandwidthProfileId,
507 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
508
509 }
510
511 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
512 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
513 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
514 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
515 }
516
517 @Override
518 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
519 String multicastServiceName) {
520 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
521 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
522 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
523 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
524 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
525 } else {
526 log.error("don't know how to handle {}", sub);
527 return false;
528 }
529 }
530
531 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
532 String multicastServiceName) {
533 if (log.isTraceEnabled()) {
534 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
535 }
536 if (enableEapol) {
537 if (hasDefaultEapol(sub.port)) {
538 // remove EAPOL flow and throw exception so that we'll retry later
539 if (!isDefaultEapolPendingRemoval(sub.port)) {
540 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
541 }
542
543 if (waitForRemoval) {
544 // NOTE wait for removal is a flag only needed to make sure VOLTHA
545 // does not explode with the flows remove/add in the same batch
546 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
547 return false;
548 } else {
549 log.warn("continuing provisioning on {}", portWithName(sub.port));
550 }
551 }
552
553 }
554
555 // NOTE createMeters will return if the meters are not installed
556 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800557 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700558 return false;
559 }
560
561 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
562 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
563 sub.subscriberAndDeviceInformation);
564
565 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
566 && !isMacAddressAvailable(sub.device.id(), sub.port,
567 sub.subscriberAndDeviceInformation)) {
568 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
569 return false;
570 }
571
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800572 // NOTE that the EAPOL flows handling is based on the data-plane flows status
573 // always process them before
574 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
575
yasin sapli0823c932022-01-26 11:26:09 +0000576 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
577
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700578 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
579 sub.subscriberAndDeviceInformation, multicastServiceName);
580
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700581 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
582
583 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
584 return true;
585 }
586
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800587 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700588 String multicastServiceName) {
589
590 if (log.isTraceEnabled()) {
591 log.trace("Removal of subscriber on {} started",
592 portWithName(sub.port));
593 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800594 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700595
596 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
597
yasin sapli0823c932022-01-26 11:26:09 +0000598 handleSubscriberPppoeFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, sub.subscriberAndDeviceInformation);
599
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700600 if (enableEapol) {
601 // remove the tagged eapol
602 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800603 }
604 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700605
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800606 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
607
608 if (enableEapol) {
609
610 // if any of the services still has flows, return false
611 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
612 while (iter.hasNext()) {
613 UniTagInformation entry = iter.next();
614 if (areSubscriberFlowsPendingRemoval(sub.port, entry)) {
615 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
616 portWithName(sub.port), entry.getServiceName());
617 return false;
618 }
619 }
620
621 // once the flows are removed add the default one back
622 // (only if the port is ENABLED and still present on the device)
Matteo Scandolo49c42052021-11-23 13:12:29 -0800623 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
624
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700625 // NOTE we remove the subscriber when the port goes down
626 // but in that case we don't need to add default eapol
627 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
628 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
629 }
630 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700631 // FIXME check the return status of the flow and return accordingly
632 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
633 return true;
634 }
635
636 @Override
637 public boolean hasDefaultEapol(Port port) {
638 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
639 // NOTE we consider ERROR as a present EAPOL flow as ONOS
640 // will keep trying to add it
641 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
642 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
643 status.defaultEapolStatus == OltFlowsStatus.ERROR);
644 }
645
646 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
647 try {
648 cpStatusReadLock.lock();
649 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
650 OltPortStatus status = cpStatus.get(sk);
651 return status;
652 } finally {
653 cpStatusReadLock.unlock();
654 }
655 }
656
657 public boolean isDefaultEapolPendingRemoval(Port port) {
658 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
659 if (log.isTraceEnabled()) {
660 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
661 status, portWithName(port), defaultEapolUniTag);
662 }
663 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
664 }
665
666 @Override
667 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
668 OltPortStatus status = getOltPortStatus(port, uti);
669 if (log.isTraceEnabled()) {
670 log.trace("Status during DHCP flow check {} for port {} and service {}",
671 status, portWithName(port), uti.getServiceName());
672 }
673 return status != null &&
674 (status.dhcpStatus == OltFlowsStatus.ADDED ||
675 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
676 }
677
678 @Override
yasin sapli0823c932022-01-26 11:26:09 +0000679 public boolean hasPppoeFlows(Port port, UniTagInformation uti) {
680 OltPortStatus status = getOltPortStatus(port, uti);
681 if (log.isTraceEnabled()) {
682 log.trace("Status during PPPoE flow check {} for port {} and service {}",
683 status, portWithName(port), uti.getServiceName());
684 }
685 return status != null &&
686 (status.pppoeStatus == OltFlowsStatus.ADDED ||
687 status.pppoeStatus == OltFlowsStatus.PENDING_ADD);
688 }
689
690 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700691 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
692
693 OltPortStatus status = getOltPortStatus(port, uti);
694 if (log.isTraceEnabled()) {
695 log.trace("Status during subscriber flow check {} for port {} and service {}",
696 status, portWithName(port), uti.getServiceName());
697 }
698 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
699 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
700 }
701
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800702 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti) {
703 OltPortStatus status = getOltPortStatus(port, uti);
704 if (log.isTraceEnabled()) {
705 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
706 status, portWithName(port), uti);
707 }
708 return status != null && status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE;
709 }
710
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700711 @Override
712 public void purgeDeviceFlows(DeviceId deviceId) {
713 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800714 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700715
716 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800717 if (log.isTraceEnabled()) {
718 log.trace("Clearing cp status from device {}", deviceId);
719 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700720 try {
721 cpStatusWriteLock.lock();
722 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
723 while (iter.hasNext()) {
724 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
725 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
726 cpStatus.remove(entry.getKey());
727 }
728 }
729 } finally {
730 cpStatusWriteLock.unlock();
731 }
732
733 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800734 if (log.isTraceEnabled()) {
735 log.trace("Clearing provisioned subscribers from device {}", deviceId);
736 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700737 try {
738 provisionedSubscribersWriteLock.lock();
739 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
740 while (iter.hasNext()) {
741 Map.Entry<ServiceKey, Boolean> entry = iter.next();
742 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
743 provisionedSubscribers.remove(entry.getKey());
744 }
745 }
746 } finally {
747 provisionedSubscribersWriteLock.unlock();
748 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800749 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700750 }
751
752 @Override
753 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
Andrea Campanella61650a12022-01-24 18:09:44 -0800754 Set<Map.Entry<ServiceKey, Boolean>> subs;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700755 try {
756 provisionedSubscribersReadLock.lock();
Andrea Campanella61650a12022-01-24 18:09:44 -0800757 subs = new HashSet<>(provisionedSubscribers.entrySet());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700758 } finally {
759 provisionedSubscribersReadLock.unlock();
760 }
Andrea Campanella61650a12022-01-24 18:09:44 -0800761
762 for (Map.Entry<ServiceKey, Boolean> entry : subs) {
763 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
764 return true;
765 }
766 }
767 return false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700768 }
769
770 @Override
771 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
772 try {
773 provisionedSubscribersReadLock.lock();
774 Boolean provisioned = provisionedSubscribers.get(sk);
775 if (provisioned == null || !provisioned) {
776 return false;
777 }
778 } finally {
779 provisionedSubscribersReadLock.unlock();
780 }
781 return true;
782 }
783
784 @Override
785 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
786 try {
787 provisionedSubscribersWriteLock.lock();
788 provisionedSubscribers.put(sk, status);
789 } finally {
790 provisionedSubscribersWriteLock.unlock();
791 }
792 }
793
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800794 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700795 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
796
797 // create a subscriberKey for the EAPOL flow
798 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
799
800 // NOTE we only need to keep track of the default EAPOL flow in the
801 // connectpoint status map
802 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
803 OltFlowsStatus status = action == FlowOperation.ADD ?
804 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
yasin sapli0823c932022-01-26 11:26:09 +0000805 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE, OltFlowsStatus.NONE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700806
807 }
808
809 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
810 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
811
812 int techProfileId = getDefaultTechProfileId(sub.port);
813 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
814
815 // in the delete case the meter should still be there as we remove
816 // the meters only if no flows are pointing to them
817 if (meterId == null) {
818 log.debug("MeterId is null for BandwidthProfile {} on device {}",
819 bandwidthProfile, sub.device.id());
820 return false;
821 }
822
823 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
824 if (oltMeterId == null) {
825 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
826 oltBandwidthProfile, sub.device.id());
827 return false;
828 }
829
830 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
831 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
832
833 FilteringObjective.Builder eapolAction;
834
835 if (action == FlowOperation.ADD) {
836 eapolAction = filterBuilder.permit();
837 } else if (action == FlowOperation.REMOVE) {
838 eapolAction = filterBuilder.deny();
839 } else {
840 log.error("Operation {} not supported", action);
841 return false;
842 }
843
844 FilteringObjective.Builder baseEapol = eapolAction
845 .withKey(Criteria.matchInPort(sub.port.number()))
846 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
847
848 // NOTE we only need to add the treatment to install the flow,
849 // we can remove it based in the match
850 FilteringObjective.Builder eapol;
851
852 TrafficTreatment treatment = treatmentBuilder
853 .meter(meterId)
854 .writeMetadata(createTechProfValueForWriteMetadata(
855 vlanId,
856 techProfileId, oltMeterId), 0)
857 .setOutput(PortNumber.CONTROLLER)
858 .pushVlan()
859 .setVlanId(vlanId)
860 .build();
861 eapol = baseEapol
862 .withMeta(treatment);
863
864 FilteringObjective eapolObjective = eapol
865 .fromApp(appId)
866 .withPriority(MAX_PRIORITY)
867 .add(new ObjectiveContext() {
868 @Override
869 public void onSuccess(Objective objective) {
870 log.info("EAPOL flow objective {} for {}",
871 completeFlowOpToString(action), portWithName(sub.port));
872 if (log.isTraceEnabled()) {
873 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
874 }
875 }
876
877 @Override
878 public void onError(Objective objective, ObjectiveError error) {
879 log.error("Cannot {} eapol flow for {} : {}", action,
880 portWithName(sub.port), error);
881
882 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
883 updateConnectPointStatus(sk,
yasin sapli0823c932022-01-26 11:26:09 +0000884 OltFlowsStatus.ERROR, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700885 }
886 }
887 });
888
889 flowObjectiveService.filter(sub.device.id(), eapolObjective);
890
891 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
892 return true;
893 }
894
895 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800896 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700897 SubscriberAndDeviceInformation si) {
898 if (!enableEapol) {
899 return true;
900 }
901 // TODO verify we need an EAPOL flow for EACH service
902 AtomicBoolean success = new AtomicBoolean(true);
903 si.uniTagList().forEach(u -> {
904 // we assume that if subscriber flows are installed, tagged EAPOL is installed as well
905 boolean hasFlows = hasSubscriberFlows(sub.port, u);
906
907 // if the subscriber flows are present the EAPOL flow is present thus we don't need to install it,
908 // if the subscriber does not have flows the EAPOL flow is not present thus we don't need to remove it
909 if (action == FlowOperation.ADD && hasFlows ||
910 action == FlowOperation.REMOVE && !hasFlows) {
911 log.debug("Not {} EAPOL on {}({}) as subscriber flow status is {}", flowOpToString(action),
912 portWithName(sub.port), u.getServiceName(), hasFlows);
913 return;
914 }
915 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
916 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
917 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
918 u.getUpstreamOltBandwidthProfile(),
919 action, u.getPonCTag())) {
920 //
921 log.error("Failed to {} EAPOL with suscriber tags", action);
922 //TODO this sets it for all services, maybe some services succeeded.
923 success.set(false);
924 }
925 });
926 return success.get();
927 }
928
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800929 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700930 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
931 if (uti.getIsIgmpRequired()) {
932 DeviceId deviceId = sub.device.id();
933 // if we reached here a meter already exists
934 MeterId meterId = oltMeterService
935 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
936 MeterId oltMeterId = oltMeterService
937 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
938
939 processIgmpFilteringObjectives(deviceId, sub.port,
940 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
941 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
942 }
943 });
944 }
945
946 private boolean checkSadisRunning() {
947 if (bpService == null) {
948 log.warn("Sadis is not running");
949 return false;
950 }
951 return true;
952 }
953
954 private int getDefaultTechProfileId(Port port) {
955 if (!checkSadisRunning()) {
956 return defaultTechProfileId;
957 }
958 if (port != null) {
959 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
960 if (info != null && info.uniTagList().size() == 1) {
961 return info.uniTagList().get(0).getTechnologyProfileId();
962 }
963 }
964 return defaultTechProfileId;
965 }
966
967 protected Long createTechProfValueForWriteMetadata(VlanId cVlan, int techProfileId, MeterId upstreamOltMeterId) {
968 Long writeMetadata;
969
970 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
971 writeMetadata = (long) techProfileId << 32;
972 } else {
973 writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
974 }
975 if (upstreamOltMeterId == null) {
976 return writeMetadata;
977 } else {
978 return writeMetadata | upstreamOltMeterId.id();
979 }
980 }
981
982 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
983 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
984
985 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
986 .withKey(Criteria.matchInPort(port.number()))
987 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
988 .withMeta(DefaultTrafficTreatment.builder()
989 .setOutput(PortNumber.CONTROLLER).build())
990 .fromApp(appId)
991 .withPriority(MAX_PRIORITY)
992 .add(new ObjectiveContext() {
993 @Override
994 public void onSuccess(Objective objective) {
995 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
996 }
997
998 @Override
999 public void onError(Objective objective, ObjectiveError error) {
1000 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
1001 error);
1002 }
1003 });
1004
1005 flowObjectiveService.filter(deviceId, lldp);
1006 }
1007
1008 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
1009 FlowOperation action,
1010 SubscriberAndDeviceInformation si) {
1011 si.uniTagList().forEach(uti -> {
1012
1013 if (!uti.getIsDhcpRequired()) {
1014 return;
1015 }
1016
1017 // if it's an ADD skip if flows are there,
1018 // if it's a DELETE skip if flows are not there
1019 boolean hasFlows = hasDhcpFlows(port, uti);
1020 if (action == FlowOperation.ADD && hasFlows ||
1021 action == FlowOperation.REMOVE && !hasFlows) {
1022 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
1023 uti.getServiceName(), hasFlows);
1024 return;
1025 }
1026
1027 log.info("{} DHCP flows for subscriber on {} and service {}",
1028 flowOpToString(action), portWithName(port), uti.getServiceName());
1029
1030 // if we reached here a meter already exists
1031 MeterId meterId = oltMeterService
1032 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1033 MeterId oltMeterId = oltMeterService
1034 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1035
1036 if (enableDhcpV4) {
1037 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1038 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1039 uti);
1040 }
1041 if (enableDhcpV6) {
1042 log.error("DHCP V6 not supported for subscribers");
1043 }
1044 });
1045 }
1046
yasin sapli0823c932022-01-26 11:26:09 +00001047 protected void handleSubscriberPppoeFlows(DeviceId deviceId, Port port,
1048 FlowOperation action,
1049 SubscriberAndDeviceInformation si) {
1050 si.uniTagList().forEach(uti -> {
1051
1052 if (!uti.getIsPppoeRequired()) {
1053 return;
1054 }
1055
1056 // if it's an ADD skip if flows are there,
1057 // if it's a DELETE skip if flows are not there
1058 boolean hasFlows = hasPppoeFlows(port, uti);
1059 if (action == FlowOperation.ADD && hasFlows ||
1060 action == FlowOperation.REMOVE && !hasFlows) {
1061 log.debug("Not dealing with PPPoE {} on {} as PPPoE flow status is {}", action,
1062 uti.getServiceName(), hasFlows);
1063 return;
1064 }
1065
1066 log.info("{} PPPoE flows for subscriber on {} and service {}",
1067 flowOpToString(action), portWithName(port), uti.getServiceName());
1068
1069 // if we reached here a meter already exists
1070 MeterId meterId = oltMeterService
1071 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1072 MeterId oltMeterId = oltMeterService
1073 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1074
1075 if (enablePppoe) {
1076 processPPPoEDFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, meterId, oltMeterId,
1077 uti.getTechnologyProfileId(), uti.getPonCTag(), uti.getUniTagMatch(),
1078 (byte) uti.getUsPonCTagPriority());
1079 }
1080 });
1081 }
1082
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001083 // FIXME return boolean, if this fails we need to retry
1084 protected void handleSubscriberDataFlows(Device device, Port port,
1085 FlowOperation action,
1086 SubscriberAndDeviceInformation si, String multicastServiceName) {
1087
1088 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001089 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001090 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1091 si.id(), portWithName(port));
1092 return;
1093 }
1094 si.uniTagList().forEach(uti -> {
1095
1096 boolean hasFlows = hasSubscriberFlows(port, uti);
1097 if (action == FlowOperation.ADD && hasFlows ||
1098 action == FlowOperation.REMOVE && !hasFlows) {
1099 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1100 uti.getServiceName(), hasFlows);
1101 return;
1102 }
1103
1104 if (multicastServiceName.equals(uti.getServiceName())) {
1105 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1106 "dataplane flows are not needed",
1107 uti.getServiceName(), si.id(), portWithName(port));
1108 return;
1109 }
1110
1111 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1112 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001113 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1114 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1115 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
yasin sapli0823c932022-01-26 11:26:09 +00001116 updateConnectPointStatus(sk, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001117
1118 // upstream flows
1119 MeterId usMeterId = oltMeterService
1120 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1121 MeterId oltUsMeterId = oltMeterService
1122 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
1123 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1124 oltUsMeterId, uti);
1125
1126 // downstream flows
1127 MeterId dsMeterId = oltMeterService
1128 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1129 MeterId oltDsMeterId = oltMeterService
1130 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
1131 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1132 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1133 });
1134 }
1135
1136 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1137 FlowOperation action, FlowDirection direction,
1138 int udpSrc, int udpDst, EthType ethType, byte protocol,
1139 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1140 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1141 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1142
1143
1144 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1145 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
yasin sapli0823c932022-01-26 11:26:09 +00001146 updateConnectPointStatus(sk, null, null, status, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001147
1148 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1149 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1150
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001151 if (meterId != null) {
1152 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001153 }
1154
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001155 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
1156 treatmentBuilder.writeMetadata(
1157 createTechProfValueForWriteMetadata(uti.getUniTagMatch(),
1158 uti.getTechnologyProfileId(), oltMeterId), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001159 }
1160
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001161 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001162 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001163 .addCondition(Criteria.matchEthType(ethType))
1164 .addCondition(Criteria.matchIPProtocol(protocol))
1165 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1166 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001167 .fromApp(appId)
1168 .withPriority(MAX_PRIORITY);
1169
Andrea Campanella0e34f562020-06-11 10:47:10 +02001170 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001171 if (direction == FlowDirection.UPSTREAM) {
1172 treatmentBuilder.setVlanId(uti.getPonCTag());
1173 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1174 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001175 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001176 if (uti.getUsPonCTagPriority() != -1) {
1177 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001178 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001179 }
1180
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001181 dhcpBuilder.withMeta(treatmentBuilder
1182 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001183
1184
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001185 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001186 @Override
1187 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001188 log.info("{} DHCP {} filter for {}.",
1189 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1190 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001191 }
1192
1193 @Override
1194 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001195 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001196 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1197 portWithName(port),
1198 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001199 error);
yasin sapli0823c932022-01-26 11:26:09 +00001200 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR, null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001201 }
1202 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001203 flowObjectiveService.filter(deviceId, dhcpUpstream);
1204 }
1205
1206 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1207 FlowOperation action, FlowDirection direction,
1208 MeterId meterId, MeterId oltMeterId, int techProfileId,
1209 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1210
1211 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1212 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1213 if (direction == FlowDirection.UPSTREAM) {
1214
1215 if (techProfileId != NONE_TP_ID) {
1216 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(null,
1217 techProfileId, oltMeterId), 0);
1218 }
1219
1220
1221 if (meterId != null) {
1222 treatmentBuilder.meter(meterId);
1223 }
1224
1225 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1226 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1227 }
1228
1229 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1230 treatmentBuilder.setVlanId(cTag);
1231 }
1232
1233 if (vlanPcp != -1) {
1234 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1235 }
1236 }
1237
1238 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1239
1240 FilteringObjective igmp = filterBuilder
1241 .withKey(Criteria.matchInPort(port.number()))
1242 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1243 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1244 .withMeta(treatmentBuilder
1245 .setOutput(PortNumber.CONTROLLER).build())
1246 .fromApp(appId)
1247 .withPriority(MAX_PRIORITY)
1248 .add(new ObjectiveContext() {
1249 @Override
1250 public void onSuccess(Objective objective) {
1251 log.info("Igmp filter for {} {}.", portWithName(port), action);
1252 }
1253
1254 @Override
1255 public void onError(Objective objective, ObjectiveError error) {
1256 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1257 error);
1258 }
1259 });
1260
1261 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001262
1263 }
1264
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001265 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1266 FlowOperation action, FlowDirection direction,
1267 MeterId meterId, MeterId oltMeterId, int techProfileId,
1268 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001269
1270 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1271 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001272
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001273 if (meterId != null) {
1274 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001275 }
1276
1277 if (techProfileId != NONE_TP_ID) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001278 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(cTag, techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001279 }
1280
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001281 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1282 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001283 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001284 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1285 .fromApp(appId)
1286 .withPriority(10000);
1287
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001288 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001289 treatmentBuilder.setVlanId(cTag);
1290 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1291 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1292 }
1293 if (vlanPcp != null) {
1294 treatmentBuilder.setVlanPcp(vlanPcp);
1295 }
1296 }
1297 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1298
1299 FilteringObjective pppoed = pppoedBuilder
1300 .add(new ObjectiveContext() {
1301 @Override
1302 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001303 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001304 }
1305
1306 @Override
1307 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001308 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1309 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001310 }
1311 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001312 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001313 }
1314
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001315 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1316 FlowOperation action,
1317 MeterId upstreamMeterId,
1318 MeterId upstreamOltMeterId,
1319 UniTagInformation uti) {
1320 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001321 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001322 .matchInPort(port.number())
1323 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001324 .build();
1325
Andrea Campanella327c5722020-01-30 11:34:13 +01001326 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1327 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001328 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001329 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001330 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001331 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001332 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1333 treatmentBuilder.popVlan();
1334 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001335
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001336 if (uti.getUsPonCTagPriority() != -1) {
1337 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001338
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001339 }
1340
1341 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001342 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001343
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001344 if (uti.getUsPonSTagPriority() != -1) {
1345 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001346 }
1347
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001348 treatmentBuilder.setOutput(nniPort.number())
1349 .writeMetadata(createMetadata(uti.getPonCTag(),
1350 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001351
yasin saplib4b8ee12021-06-13 18:25:20 +00001352 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1353
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001354 if (upstreamMeterId != null) {
1355 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001356 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1357 }
1358 if (upstreamOltMeterId != null) {
1359 treatmentBuilder.meter(upstreamOltMeterId);
1360 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001361 }
1362
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001363 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1364 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001365 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001366
1367 ObjectiveContext context = new ObjectiveContext() {
1368 @Override
1369 public void onSuccess(Objective objective) {
1370 log.info("{} Upstream Data plane filter for {}.",
1371 completeFlowOpToString(action), sk);
1372 }
1373
1374 @Override
1375 public void onError(Objective objective, ObjectiveError error) {
1376 log.error("Upstream Data plane filter for {} failed {} because {}.",
1377 sk, action, error);
yasin sapli0823c932022-01-26 11:26:09 +00001378 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001379 }
1380 };
1381
1382 ForwardingObjective flow = null;
1383 if (action == FlowOperation.ADD) {
1384 flow = flowBuilder.add(context);
1385 } else if (action == FlowOperation.REMOVE) {
1386 flow = flowBuilder.remove(context);
1387 } else {
1388 log.error("Flow action not supported: {}", action);
1389 }
1390
1391 if (flow != null) {
1392 flowObjectiveService.forward(deviceId, flow);
1393 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001394 }
1395
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001396 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1397 FlowOperation action,
1398 MeterId downstreamMeterId,
1399 MeterId downstreamOltMeterId,
1400 UniTagInformation uti,
1401 MacAddress macAddress) {
1402 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001403 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001404 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001405 .matchVlanId(uti.getPonSTag())
1406 .matchInPort(nniPort.number())
1407 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001408
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001409 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1410 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001411 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001412
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001413 if (uti.getDsPonCTagPriority() != -1) {
1414 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001415 }
1416
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001417 if (macAddress != null) {
1418 selectorBuilder.matchEthDst(macAddress);
1419 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001420
1421 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1422 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001423 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001424
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001425 treatmentBuilder.writeMetadata(createMetadata(uti.getPonCTag(),
1426 uti.getTechnologyProfileId(),
1427 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001428
Andrea Campanella981e86c2021-03-12 11:35:33 +01001429 // Upstream pbit is used to remark inner vlan pbit.
1430 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1431 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1432 // all pbit acceptance are not widely supported by vendors even though present in
1433 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001434 if (uti.getUsPonCTagPriority() != -1) {
1435 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001436 }
1437
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001438 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1439 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1440 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001441 }
1442
yasin saplib4b8ee12021-06-13 18:25:20 +00001443 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1444
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001445 if (downstreamMeterId != null) {
1446 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001447 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001448 }
1449
yasin saplib4b8ee12021-06-13 18:25:20 +00001450 if (downstreamOltMeterId != null) {
1451 treatmentBuilder.meter(downstreamOltMeterId);
1452 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1453 }
1454
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001455 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1456 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001457
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001458 ObjectiveContext context = new ObjectiveContext() {
1459 @Override
1460 public void onSuccess(Objective objective) {
1461 log.info("{} Downstream Data plane filter for {}.",
1462 completeFlowOpToString(action), sk);
1463 }
1464
1465 @Override
1466 public void onError(Objective objective, ObjectiveError error) {
1467 log.info("Downstream Data plane filter for {} failed {} because {}.",
1468 sk, action, error);
yasin sapli0823c932022-01-26 11:26:09 +00001469 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001470 }
1471 };
1472
1473 ForwardingObjective flow = null;
1474 if (action == FlowOperation.ADD) {
1475 flow = flowBuilder.add(context);
1476 } else if (action == FlowOperation.REMOVE) {
1477 flow = flowBuilder.remove(context);
1478 } else {
1479 log.error("Flow action not supported: {}", action);
1480 }
1481
1482 if (flow != null) {
1483 if (log.isTraceEnabled()) {
1484 log.trace("Forwarding rule {}", flow);
1485 }
1486 flowObjectiveService.forward(deviceId, flow);
1487 }
Andrea Campanella600d2e22020-06-22 11:00:31 +02001488 }
1489
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001490 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1491 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001492 Integer priority,
1493 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001494 return DefaultForwardingObjective.builder()
1495 .withFlag(ForwardingObjective.Flag.VERSATILE)
1496 .withPriority(priority)
1497 .makePermanent()
1498 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001499 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001500 .fromApp(appId)
1501 .withTreatment(treatment);
1502 }
1503
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001504 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
1505 if (techProfileId == NONE_TP_ID) {
Andrea Campanella7c49b792020-05-11 11:36:53 +02001506 techProfileId = DEFAULT_TP_ID_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001507 }
1508
1509 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
1510 }
1511
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001512 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1513 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1514 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001515
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001516 si.uniTagList().forEach(uniTagInfo -> {
1517 if (uniTagInfo.getEnableMacLearning()) {
1518 requiresMacLearning.set(true);
1519 }
1520 });
1521
1522 return requiresMacLearning.get();
1523 }
1524
1525 /**
1526 * Checks whether the subscriber has the MacAddress configured or discovered.
1527 *
1528 * @param deviceId DeviceId for this subscriber
1529 * @param port Port for this subscriber
1530 * @param si SubscriberAndDeviceInformation
1531 * @return boolean
1532 */
1533 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1534 AtomicBoolean isConfigured = new AtomicBoolean();
1535 isConfigured.set(true);
1536
1537 si.uniTagList().forEach(uniTagInfo -> {
1538 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
1539 boolean configureMac = isMacAddressValid(uniTagInfo);
1540 boolean discoveredMac = false;
1541 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1542 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1543 if (optHost.isPresent() && optHost.get().mac() != null) {
1544 discoveredMac = true;
1545 }
1546 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1547 log.debug("Awaiting for macAddress on {} for service {}",
1548 portWithName(port), uniTagInfo.getServiceName());
1549 isConfigured.set(false);
1550 }
1551 });
1552
1553 return isConfigured.get();
1554 }
1555
1556 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
1557 boolean configuredMac = isMacAddressValid(uniTagInfo);
1558 if (configuredMac) {
1559 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1560 } else if (uniTagInfo.getEnableMacLearning()) {
1561 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1562 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1563 if (optHost.isPresent() && optHost.get().mac() != null) {
1564 return optHost.get().mac();
1565 }
1566 }
1567 return null;
1568 }
1569
1570 private boolean isMacAddressValid(UniTagInformation tagInformation) {
1571 return tagInformation.getConfiguredMacAddress() != null &&
1572 !tagInformation.getConfiguredMacAddress().trim().equals("") &&
1573 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
1574 }
1575
1576 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
yasin sapli0823c932022-01-26 11:26:09 +00001577 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus,
1578 OltFlowsStatus pppoeStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001579 if (log.isTraceEnabled()) {
1580 log.trace("Updating cpStatus {} with values: eapolFlow={}, subscriberFlows={}, dhcpFlow={}",
1581 key, eapolStatus, subscriberFlowsStatus, dhcpStatus);
1582 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001583 try {
1584 cpStatusWriteLock.lock();
1585 OltPortStatus status = cpStatus.get(key);
1586
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001587
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001588 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001589 // if we don't have status for the connectPoint
1590 // and we're only updating status to PENDING_REMOVE or ERROR
1591 // do not create it. This is because this case will only happen when a device is removed
1592 // and it's status cleaned
1593 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1594 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1595 statusesToIgnore.add(OltFlowsStatus.ERROR);
1596
1597 if (
1598 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1599 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1600 ) {
1601 if (log.isTraceEnabled()) {
1602 log.trace("Ignoring cpStatus update as status is meaningless");
1603 }
1604 return;
1605 }
1606
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001607 status = new OltPortStatus(
1608 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
1609 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
yasin sapli0823c932022-01-26 11:26:09 +00001610 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE,
1611 pppoeStatus != null ? pppoeStatus : OltFlowsStatus.NONE
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001612 );
1613 } else {
1614 if (eapolStatus != null) {
1615 status.defaultEapolStatus = eapolStatus;
1616 }
1617 if (subscriberFlowsStatus != null) {
1618 status.subscriberFlowsStatus = subscriberFlowsStatus;
1619 }
1620 if (dhcpStatus != null) {
1621 status.dhcpStatus = dhcpStatus;
1622 }
1623 }
1624
1625 cpStatus.put(key, status);
1626 } finally {
1627 cpStatusWriteLock.unlock();
1628 }
1629 }
1630
1631 protected class InternalFlowListener implements FlowRuleListener {
1632 @Override
1633 public void event(FlowRuleEvent event) {
1634 if (appId.id() != (event.subject().appId())) {
1635 return;
1636 }
1637
1638 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1639 if (log.isTraceEnabled()) {
1640 log.trace("ignoring flow event {} " +
1641 "as not leader for {}", event, event.subject().deviceId());
1642 }
1643 return;
1644 }
1645
1646 switch (event.type()) {
1647 case RULE_ADDED:
1648 case RULE_REMOVED:
1649 Port port = getCpFromFlowRule(event.subject());
1650 if (port == null) {
1651 log.error("Can't find port in flow {}", event.subject());
1652 return;
1653 }
1654 if (log.isTraceEnabled()) {
1655 log.trace("flow event {} on cp {}: {}", event.type(),
1656 portWithName(port), event.subject());
1657 }
1658 updateCpStatus(event.type(), port, event.subject());
1659 return;
1660 case RULE_ADD_REQUESTED:
1661 case RULE_REMOVE_REQUESTED:
1662 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1663 return;
1664 default:
1665 return;
1666 }
1667 }
1668
1669 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
1670 OltFlowsStatus status = flowRuleStatusToOltFlowStatus(type);
1671 if (isDefaultEapolFlow(flowRule)) {
1672 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1673 defaultEapolUniTag);
1674 if (log.isTraceEnabled()) {
1675 log.trace("update defaultEapolStatus {} on {}", status, sk);
1676 }
yasin sapli0823c932022-01-26 11:26:09 +00001677 updateConnectPointStatus(sk, status, null, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001678 } else if (isDhcpFlow(flowRule)) {
1679 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1680 if (sk == null) {
1681 return;
1682 }
1683 if (log.isTraceEnabled()) {
1684 log.trace("update dhcpStatus {} on {}", status, sk);
1685 }
yasin sapli0823c932022-01-26 11:26:09 +00001686 updateConnectPointStatus(sk, null, null, status, null);
1687 } else if (isPppoeFlow(flowRule)) {
1688 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1689 if (sk == null) {
1690 return;
1691 }
1692 if (log.isTraceEnabled()) {
1693 log.trace("update pppoeStatus {} on {}", status, sk);
1694 }
1695 updateConnectPointStatus(sk, null, null, null, status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001696 } else if (isDataFlow(flowRule)) {
1697
1698 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()),
1699 getCpFromFlowRule(flowRule).number())) {
1700 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1701 return;
1702 }
1703
1704 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1705 if (sk == null) {
1706 return;
1707 }
1708 if (log.isTraceEnabled()) {
1709 log.trace("update dataplaneStatus {} on {}", status, sk);
1710 }
yasin sapli0823c932022-01-26 11:26:09 +00001711 updateConnectPointStatus(sk, null, status, null, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001712 }
1713 }
1714
1715 private boolean isDefaultEapolFlow(FlowRule flowRule) {
1716 EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
1717 if (c == null) {
1718 return false;
1719 }
1720 if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
1721 AtomicBoolean isDefault = new AtomicBoolean(false);
1722 flowRule.treatment().allInstructions().forEach(instruction -> {
1723 if (instruction.type() == L2MODIFICATION) {
1724 L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
1725 if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
1726 L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
1727 (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
1728 if (vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
1729 isDefault.set(true);
1730 return;
1731 }
1732 }
1733 }
1734 });
1735 return isDefault.get();
1736 }
1737 return false;
1738 }
1739
1740 /**
1741 * Returns true if the flow is a DHCP flow.
1742 * Matches both upstream and downstream flows.
1743 *
1744 * @param flowRule The FlowRule to evaluate
1745 * @return boolean
1746 */
1747 private boolean isDhcpFlow(FlowRule flowRule) {
1748 IPProtocolCriterion ipCriterion = (IPProtocolCriterion) flowRule.selector()
1749 .getCriterion(Criterion.Type.IP_PROTO);
1750 if (ipCriterion == null) {
1751 return false;
1752 }
1753
1754 UdpPortCriterion src = (UdpPortCriterion) flowRule.selector().getCriterion(Criterion.Type.UDP_SRC);
1755
1756 if (src == null) {
1757 return false;
1758 }
1759 return ipCriterion.protocol() == IPv4.PROTOCOL_UDP &&
1760 (src.udpPort().toInt() == 68 || src.udpPort().toInt() == 67);
1761 }
1762
yasin sapli0823c932022-01-26 11:26:09 +00001763 private boolean isPppoeFlow(FlowRule flowRule) {
1764 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) flowRule.selector()
1765 .getCriterion(Criterion.Type.ETH_TYPE);
1766
1767 if (ethTypeCriterion == null) {
1768 return false;
1769 }
1770 return EthType.EtherType.PPPoED.ethType().equals(ethTypeCriterion.ethType());
1771 }
1772
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001773 private boolean isDataFlow(FlowRule flowRule) {
1774 // we consider subscriber flows the one that matches on VLAN_VID
1775 // method is valid only because it's the last check after EAPOL and DHCP.
1776 // this matches mcast flows as well, if we want to avoid that we can
1777 // filter out the elements that have groups in the treatment or
1778 // mcastIp in the selector
1779 // IPV4_DST:224.0.0.22/32
1780 // treatment=[immediate=[GROUP:0x1]]
1781
1782 return flowRule.selector().getCriterion(Criterion.Type.VLAN_VID) != null;
1783 }
1784
1785 private Port getCpFromFlowRule(FlowRule flowRule) {
1786 DeviceId deviceId = flowRule.deviceId();
1787 PortCriterion inPort = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
1788 if (inPort != null) {
1789 PortNumber port = inPort.port();
1790 return deviceService.getPort(deviceId, port);
1791 }
1792 return null;
1793 }
1794
1795 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule) {
1796 Port flowPort = getCpFromFlowRule(flowRule);
1797 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1798
1799 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1800 if (si == null && !isNni) {
1801 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1802 return null;
1803 }
1804
1805 if (isNni) {
1806 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1807 }
1808
1809 Optional<UniTagInformation> found = Optional.empty();
1810 VlanId flowVlan = null;
1811 if (isDhcpFlow(flowRule)) {
1812 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1813 L2ModificationInstruction.ModVlanIdInstruction instruction =
1814 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1815 flowVlan = instruction.vlanId();
1816 } else {
1817 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1818 VlanIdCriterion vlanIdCriterion =
1819 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1820 if (vlanIdCriterion == null) {
1821 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1822 return null;
1823 }
1824 flowVlan = vlanIdCriterion.vlanId();
1825 }
1826
1827 VlanId finalFlowVlan = flowVlan;
1828 found = si.uniTagList().stream().filter(uti ->
1829 uti.getPonCTag().equals(finalFlowVlan) ||
1830 uti.getPonSTag().equals(finalFlowVlan) ||
1831 uti.getUniTagMatch().equals(finalFlowVlan)
1832 ).findFirst();
1833
1834
1835 if (found.isEmpty()) {
1836 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1837 }
1838
1839 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1840
1841 }
1842
1843 private OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
1844 switch (type) {
1845 case RULE_ADD_REQUESTED:
1846 return OltFlowsStatus.PENDING_ADD;
1847 case RULE_ADDED:
1848 return OltFlowsStatus.ADDED;
1849 case RULE_REMOVE_REQUESTED:
1850 return OltFlowsStatus.PENDING_REMOVE;
1851 case RULE_REMOVED:
1852 return OltFlowsStatus.REMOVED;
1853 default:
1854 return OltFlowsStatus.NONE;
1855 }
1856 }
1857 }
1858
1859 protected void bindSadisService(SadisService service) {
1860 this.subsService = service.getSubscriberInfoService();
1861 this.bpService = service.getBandwidthProfileService();
1862 log.info("Sadis service is loaded");
1863 }
1864
1865 protected void unbindSadisService(SadisService service) {
1866 this.subsService = null;
1867 this.bpService = null;
1868 log.info("Sadis service is unloaded");
1869 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001870}