blob: 4dafd1688f85c58d72ba8051329258083a9f08c3 [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;
88import java.util.Iterator;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080089import java.util.List;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000090import java.util.Map;
Andrea Campanellabfb47af2021-06-03 11:09:45 +020091import java.util.Optional;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010092import java.util.Properties;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070093import java.util.concurrent.atomic.AtomicBoolean;
94import java.util.concurrent.locks.Lock;
95import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010096
97import static com.google.common.base.Strings.isNullOrEmpty;
98import static org.onlab.util.Tools.get;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070099import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
100import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
101import static org.opencord.olt.impl.OltUtils.flowOpToString;
102import static org.opencord.olt.impl.OltUtils.getPortName;
103import static org.opencord.olt.impl.OltUtils.portWithName;
104import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
105import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
106import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
107import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
108import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
109import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
110import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
111import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
112import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
113import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
114import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
115import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
116import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
117import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
118import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
119import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
120import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
121import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
122import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
123import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000124
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000125@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700126 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000127 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
128 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700129 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000130 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300131 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700132 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
133 // FIXME remove this option as potentially dangerous in production
134 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000135})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700136public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
139 protected CoreService coreService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700142 protected ComponentConfigService cfgService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
145 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000146
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000147 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
148 bind = "bindSadisService",
149 unbind = "unbindSadisService",
150 policy = ReferencePolicy.DYNAMIC)
151 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700154 protected OltMeterServiceInterface oltMeterService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
157 protected OltDeviceServiceInterface oltDeviceService;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
160 protected FlowRuleService flowRuleService;
161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
163 protected HostService hostService;
164
165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000166 protected DeviceService deviceService;
167
168 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000169 protected StorageService storageService;
170
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700171 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
172 protected BaseInformationService<BandwidthProfileInformation> bpService;
173
174 private static final String APP_NAME = "org.opencord.olt";
175 protected ApplicationId appId;
176 private static final Integer MAX_PRIORITY = 10000;
177 private static final Integer MIN_PRIORITY = 1000;
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800178 protected static final short EAPOL_DEFAULT_VLAN = 4091;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700179 private static final int NONE_TP_ID = -1;
180 private static final String V4 = "V4";
181 private static final String V6 = "V6";
182 private final Logger log = LoggerFactory.getLogger(getClass());
183
184 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
185 .setServiceName("defaultEapol").build();
186 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
187 .setServiceName("nni")
188 .setTechnologyProfileId(NONE_TP_ID)
189 .setPonCTag(VlanId.NONE)
190 .setUniTagMatch(VlanId.ANY)
191 .setUsPonCTagPriority(-1)
192 .build();
193
194 /**
195 * Connect Point status map.
196 * Used to keep track of which cp has flows that needs to be removed when the status changes.
197 */
198 protected Map<ServiceKey, OltPortStatus> cpStatus;
199 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
200 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
201 private final Lock cpStatusReadLock = cpStatusLock.readLock();
202
203 /**
204 * This map contains the subscriber that have been provisioned by the operator.
205 * They may or may not have flows, depending on the port status.
206 * The map is used to define whether flows need to be provisioned when a port comes up.
207 */
208 protected Map<ServiceKey, Boolean> provisionedSubscribers;
209 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
210 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
211 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
212
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000213 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700214 * Create DHCP trap flow on NNI port(s).
215 */
216 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000217
218 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700219 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000220 **/
221 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
222
223 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700224 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000225 **/
226 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
227
228 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700229 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000230 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700231 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000232
233 /**
234 * Send EAPOL authentication trap flows before subscriber provisioning.
235 **/
236 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
237
238 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300239 * Send PPPoED authentication trap flows before subscriber provisioning.
240 **/
241 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
242
243 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000244 * Default technology profile id that is used for authentication trap flows.
245 **/
246 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
247
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700248 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
249
250 public enum FlowOperation {
251 ADD,
252 REMOVE;
253
254
255 @Override
256 public String toString() {
257 return super.toString().toLowerCase();
258 }
259 }
260
261 public enum FlowDirection {
262 UPSTREAM,
263 DOWNSTREAM,
264 }
265
266 public enum OltFlowsStatus {
267 NONE,
268 PENDING_ADD,
269 ADDED,
270 PENDING_REMOVE,
271 REMOVED,
272 ERROR
273 }
274
275 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000276
277 @Activate
278 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700279 cfgService.registerProperties(getClass());
280 appId = coreService.registerApplication(APP_NAME);
281 internalFlowListener = new InternalFlowListener();
282
283 modified(context);
284
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000285 KryoNamespace serializer = KryoNamespace.newBuilder()
286 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700287 .register(OltFlowsStatus.class)
288 .register(FlowDirection.class)
289 .register(OltPortStatus.class)
290 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000291 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700292 .register(new ServiceKeySerializer(), ServiceKey.class)
293 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000294 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000295
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700296 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
297 .withName("volt-cp-status")
298 .withApplicationId(appId)
299 .withSerializer(Serializer.using(serializer))
300 .build().asJavaMap();
301
302 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
303 .withName("volt-provisioned-subscriber")
304 .withApplicationId(appId)
305 .withSerializer(Serializer.using(serializer))
306 .build().asJavaMap();
307
308 flowRuleService.addListener(internalFlowListener);
309
310 log.info("Started");
311 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000312
313 @Deactivate
314 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700315 cfgService.unregisterProperties(getClass(), false);
316 flowRuleService.removeListener(internalFlowListener);
317 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000318 }
319
320 @Modified
321 public void modified(ComponentContext context) {
322
323 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
324
Saurav Dasf62cea82020-08-26 17:43:04 -0700325 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000326 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700327 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000328 }
329
Andrea Campanella7c49b792020-05-11 11:36:53 +0200330 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000331 if (v4 != null) {
332 enableDhcpV4 = v4;
333 }
334
Andrea Campanella7c49b792020-05-11 11:36:53 +0200335 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000336 if (v6 != null) {
337 enableDhcpV6 = v6;
338 }
339
Saurav Dasf62cea82020-08-26 17:43:04 -0700340 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000341 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700342 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000343 }
344
Andrea Campanella7c49b792020-05-11 11:36:53 +0200345 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000346 if (eap != null) {
347 enableEapol = eap;
348 }
349
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300350 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
351 if (pppoe != null) {
352 enablePppoe = pppoe;
353 }
354
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700355 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
356 if (wait != null) {
357 waitForRemoval = wait;
358 }
359
Andrea Campanella7c49b792020-05-11 11:36:53 +0200360 String tpId = get(properties, DEFAULT_TP_ID);
361 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000362
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700363 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " +
364 "enableDhcpV6:{}, enableIgmpOnNni:{}, " +
365 "enableEapol:{}, enablePppoe:{}, defaultTechProfileId:{}," +
366 "waitForRemoval:{}",
367 enableDhcpOnNni, enableDhcpV4, enableDhcpV6,
368 enableIgmpOnNni, enableEapol, enablePppoe,
369 defaultTechProfileId, waitForRemoval);
Andrea Campanellafee86422020-06-04 16:01:27 +0200370
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000371 }
372
373 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700374 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
375 try {
376 cpStatusReadLock.lock();
377 return ImmutableMap.copyOf(cpStatus);
378 } finally {
379 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000380 }
381 }
382
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700383 @Override
384 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
385 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800386 // all the required information about subscribers
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700387 Map<ServiceKey, UniTagInformation> subscribers =
388 new HashMap<>();
389 try {
390 cpStatusReadLock.lock();
391
392 cpStatus.forEach((sk, status) -> {
393 if (
394 // not NNI Port
395 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
396 sk.getPort().connectPoint().port()) &&
397 // not EAPOL flow
Andrea Campanella982fd332022-01-19 09:14:12 +0100398 !sk.getService().equals(defaultEapolUniTag) &&
399 !status.subscriberFlowsStatus.equals(OltFlowsStatus.PENDING_REMOVE)
400 && !status.subscriberFlowsStatus.equals(OltFlowsStatus.REMOVED)
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800401
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700402 ) {
403 subscribers.put(sk, sk.getService());
404 }
405 });
406
407 return ImmutableMap.copyOf(subscribers);
408 } finally {
409 cpStatusReadLock.unlock();
410 }
411 }
412
413 @Override
414 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
415 try {
416 provisionedSubscribersReadLock.lock();
417 return ImmutableMap.copyOf(provisionedSubscribers);
418 } finally {
419 provisionedSubscribersReadLock.unlock();
420 }
421 }
422
423 @Override
424 public void handleNniFlows(Device device, Port port, FlowOperation action) {
425
426 // always handle the LLDP flow
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800427 log.debug("{} LLDP trap flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700428 processLldpFilteringObjective(device.id(), port, action);
429
430 if (enableDhcpOnNni) {
431 if (enableDhcpV4) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800432 log.debug("{} DHCPv4 trap flow on NNI {} for device {}", flowOpToString(action),
433 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700434 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
435 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
436 null, null, nniUniTag);
437 }
438 if (enableDhcpV6) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800439 log.debug("{} DHCPv6 trap flow on NNI {} for device {}", flowOpToString(action),
440 portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700441 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
442 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
443 null, null, nniUniTag);
444 }
445 } else {
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800446 log.debug("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700447 }
448
449 if (enableIgmpOnNni) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800450 log.debug("{} IGMP flow on NNI {} for device {}", flowOpToString(action), portWithName(port), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700451 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
452 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
453 }
454
455 if (enablePppoe) {
Matteo Scandolod7aa89c2021-12-07 10:21:34 -0800456 log.debug("{} PPPoE flow on NNI {} for device {}", flowOpToString(action), port.number(), device.id());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700457 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
458 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
459 }
460 }
461
462 @Override
463 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
464 String oltBandwidthProfileId) {
465
466 // we only need to something if EAPOL is enabled
467 if (!enableEapol) {
468 return true;
469 }
470
471 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
472 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
473 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
474 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
475 } else {
476 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
477 return false;
478 }
479
480 }
481
482 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
483
484 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
485 if (log.isTraceEnabled()) {
486 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
487 }
488 return false;
489 }
490 if (hasDefaultEapol(sub.port)) {
491 return true;
492 }
493 return handleEapolFlow(sub, bandwidthProfileId,
494 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
495
496 }
497
498 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
499 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
500 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
501 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
502 }
503
504 @Override
505 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
506 String multicastServiceName) {
507 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
508 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
509 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
510 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
511 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
512 } else {
513 log.error("don't know how to handle {}", sub);
514 return false;
515 }
516 }
517
518 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
519 String multicastServiceName) {
520 if (log.isTraceEnabled()) {
521 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
522 }
523 if (enableEapol) {
524 if (hasDefaultEapol(sub.port)) {
525 // remove EAPOL flow and throw exception so that we'll retry later
526 if (!isDefaultEapolPendingRemoval(sub.port)) {
527 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
528 }
529
530 if (waitForRemoval) {
531 // NOTE wait for removal is a flag only needed to make sure VOLTHA
532 // does not explode with the flows remove/add in the same batch
533 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
534 return false;
535 } else {
536 log.warn("continuing provisioning on {}", portWithName(sub.port));
537 }
538 }
539
540 }
541
542 // NOTE createMeters will return if the meters are not installed
543 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800544 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700545 return false;
546 }
547
548 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
549 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
550 sub.subscriberAndDeviceInformation);
551
552 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
553 && !isMacAddressAvailable(sub.device.id(), sub.port,
554 sub.subscriberAndDeviceInformation)) {
555 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
556 return false;
557 }
558
Matteo Scandolo8a91c0f2021-12-03 10:58:14 -0800559 // NOTE that the EAPOL flows handling is based on the data-plane flows status
560 // always process them before
561 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
562
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700563 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
564 sub.subscriberAndDeviceInformation, multicastServiceName);
565
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700566 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
567
568 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
569 return true;
570 }
571
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800572 protected boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700573 String multicastServiceName) {
574
575 if (log.isTraceEnabled()) {
576 log.trace("Removal of subscriber on {} started",
577 portWithName(sub.port));
578 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800579 SubscriberAndDeviceInformation si = sub.subscriberAndDeviceInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700580
581 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
582
583 if (enableEapol) {
584 // remove the tagged eapol
585 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800586 }
587 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700588
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800589 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
590
591 if (enableEapol) {
592
593 // if any of the services still has flows, return false
594 Iterator<UniTagInformation> iter = sub.subscriberAndDeviceInformation.uniTagList().iterator();
595 while (iter.hasNext()) {
596 UniTagInformation entry = iter.next();
597 if (areSubscriberFlowsPendingRemoval(sub.port, entry)) {
598 log.info("Subscriber {} still have flows on service {}, postpone default EAPOL installation.",
599 portWithName(sub.port), entry.getServiceName());
600 return false;
601 }
602 }
603
604 // once the flows are removed add the default one back
605 // (only if the port is ENABLED and still present on the device)
Matteo Scandolo49c42052021-11-23 13:12:29 -0800606 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
607
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700608 // NOTE we remove the subscriber when the port goes down
609 // but in that case we don't need to add default eapol
610 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
611 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
612 }
613 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700614 // FIXME check the return status of the flow and return accordingly
615 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
616 return true;
617 }
618
619 @Override
620 public boolean hasDefaultEapol(Port port) {
621 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
622 // NOTE we consider ERROR as a present EAPOL flow as ONOS
623 // will keep trying to add it
624 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
625 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
626 status.defaultEapolStatus == OltFlowsStatus.ERROR);
627 }
628
629 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
630 try {
631 cpStatusReadLock.lock();
632 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
633 OltPortStatus status = cpStatus.get(sk);
634 return status;
635 } finally {
636 cpStatusReadLock.unlock();
637 }
638 }
639
640 public boolean isDefaultEapolPendingRemoval(Port port) {
641 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
642 if (log.isTraceEnabled()) {
643 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
644 status, portWithName(port), defaultEapolUniTag);
645 }
646 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
647 }
648
649 @Override
650 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
651 OltPortStatus status = getOltPortStatus(port, uti);
652 if (log.isTraceEnabled()) {
653 log.trace("Status during DHCP flow check {} for port {} and service {}",
654 status, portWithName(port), uti.getServiceName());
655 }
656 return status != null &&
657 (status.dhcpStatus == OltFlowsStatus.ADDED ||
658 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
659 }
660
661 @Override
662 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
663
664 OltPortStatus status = getOltPortStatus(port, uti);
665 if (log.isTraceEnabled()) {
666 log.trace("Status during subscriber flow check {} for port {} and service {}",
667 status, portWithName(port), uti.getServiceName());
668 }
669 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
670 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
671 }
672
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800673 public boolean areSubscriberFlowsPendingRemoval(Port port, UniTagInformation uti) {
674 OltPortStatus status = getOltPortStatus(port, uti);
675 if (log.isTraceEnabled()) {
676 log.trace("Status during pending_remove flow check {} for port {} and UniTagInformation {}",
677 status, portWithName(port), uti);
678 }
679 return status != null && status.subscriberFlowsStatus == OltFlowsStatus.PENDING_REMOVE;
680 }
681
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700682 @Override
683 public void purgeDeviceFlows(DeviceId deviceId) {
684 log.debug("Purging flows on device {}", deviceId);
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800685 flowRuleService.purgeFlowRules(deviceId);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700686
687 // removing the status from the cpStatus map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800688 if (log.isTraceEnabled()) {
689 log.trace("Clearing cp status from device {}", deviceId);
690 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700691 try {
692 cpStatusWriteLock.lock();
693 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
694 while (iter.hasNext()) {
695 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
696 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
697 cpStatus.remove(entry.getKey());
698 }
699 }
700 } finally {
701 cpStatusWriteLock.unlock();
702 }
703
704 // removing subscribers from the provisioned map
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800705 if (log.isTraceEnabled()) {
706 log.trace("Clearing provisioned subscribers from device {}", deviceId);
707 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700708 try {
709 provisionedSubscribersWriteLock.lock();
710 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
711 while (iter.hasNext()) {
712 Map.Entry<ServiceKey, Boolean> entry = iter.next();
713 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
714 provisionedSubscribers.remove(entry.getKey());
715 }
716 }
717 } finally {
718 provisionedSubscribersWriteLock.unlock();
719 }
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800720 log.debug("Done clearing up device flows and subscribers");
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700721 }
722
723 @Override
724 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
725 // if any service is programmed on this port, returns true
726 AtomicBoolean provisioned = new AtomicBoolean(false);
727 try {
728 provisionedSubscribersReadLock.lock();
729 for (Map.Entry<ServiceKey, Boolean> entry : provisionedSubscribers.entrySet()) {
730 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
731 provisioned.set(true);
732 break;
733 }
734 }
735 } finally {
736 provisionedSubscribersReadLock.unlock();
737 }
738 return provisioned.get();
739 }
740
741 @Override
742 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
743 try {
744 provisionedSubscribersReadLock.lock();
745 Boolean provisioned = provisionedSubscribers.get(sk);
746 if (provisioned == null || !provisioned) {
747 return false;
748 }
749 } finally {
750 provisionedSubscribersReadLock.unlock();
751 }
752 return true;
753 }
754
755 @Override
756 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
757 try {
758 provisionedSubscribersWriteLock.lock();
759 provisionedSubscribers.put(sk, status);
760 } finally {
761 provisionedSubscribersWriteLock.unlock();
762 }
763 }
764
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800765 protected boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700766 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
767
768 // create a subscriberKey for the EAPOL flow
769 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
770
771 // NOTE we only need to keep track of the default EAPOL flow in the
772 // connectpoint status map
773 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
774 OltFlowsStatus status = action == FlowOperation.ADD ?
775 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
776 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE);
777
778 }
779
780 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
781 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
782
783 int techProfileId = getDefaultTechProfileId(sub.port);
784 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
785
786 // in the delete case the meter should still be there as we remove
787 // the meters only if no flows are pointing to them
788 if (meterId == null) {
789 log.debug("MeterId is null for BandwidthProfile {} on device {}",
790 bandwidthProfile, sub.device.id());
791 return false;
792 }
793
794 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
795 if (oltMeterId == null) {
796 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
797 oltBandwidthProfile, sub.device.id());
798 return false;
799 }
800
801 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
802 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
803
804 FilteringObjective.Builder eapolAction;
805
806 if (action == FlowOperation.ADD) {
807 eapolAction = filterBuilder.permit();
808 } else if (action == FlowOperation.REMOVE) {
809 eapolAction = filterBuilder.deny();
810 } else {
811 log.error("Operation {} not supported", action);
812 return false;
813 }
814
815 FilteringObjective.Builder baseEapol = eapolAction
816 .withKey(Criteria.matchInPort(sub.port.number()))
817 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
818
819 // NOTE we only need to add the treatment to install the flow,
820 // we can remove it based in the match
821 FilteringObjective.Builder eapol;
822
823 TrafficTreatment treatment = treatmentBuilder
824 .meter(meterId)
825 .writeMetadata(createTechProfValueForWriteMetadata(
826 vlanId,
827 techProfileId, oltMeterId), 0)
828 .setOutput(PortNumber.CONTROLLER)
829 .pushVlan()
830 .setVlanId(vlanId)
831 .build();
832 eapol = baseEapol
833 .withMeta(treatment);
834
835 FilteringObjective eapolObjective = eapol
836 .fromApp(appId)
837 .withPriority(MAX_PRIORITY)
838 .add(new ObjectiveContext() {
839 @Override
840 public void onSuccess(Objective objective) {
841 log.info("EAPOL flow objective {} for {}",
842 completeFlowOpToString(action), portWithName(sub.port));
843 if (log.isTraceEnabled()) {
844 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
845 }
846 }
847
848 @Override
849 public void onError(Objective objective, ObjectiveError error) {
850 log.error("Cannot {} eapol flow for {} : {}", action,
851 portWithName(sub.port), error);
852
853 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
854 updateConnectPointStatus(sk,
855 OltFlowsStatus.ERROR, null, null);
856 }
857 }
858 });
859
860 flowObjectiveService.filter(sub.device.id(), eapolObjective);
861
862 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
863 return true;
864 }
865
866 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800867 protected boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700868 SubscriberAndDeviceInformation si) {
869 if (!enableEapol) {
870 return true;
871 }
872 // TODO verify we need an EAPOL flow for EACH service
873 AtomicBoolean success = new AtomicBoolean(true);
874 si.uniTagList().forEach(u -> {
875 // we assume that if subscriber flows are installed, tagged EAPOL is installed as well
876 boolean hasFlows = hasSubscriberFlows(sub.port, u);
877
878 // if the subscriber flows are present the EAPOL flow is present thus we don't need to install it,
879 // if the subscriber does not have flows the EAPOL flow is not present thus we don't need to remove it
880 if (action == FlowOperation.ADD && hasFlows ||
881 action == FlowOperation.REMOVE && !hasFlows) {
882 log.debug("Not {} EAPOL on {}({}) as subscriber flow status is {}", flowOpToString(action),
883 portWithName(sub.port), u.getServiceName(), hasFlows);
884 return;
885 }
886 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
887 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
888 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
889 u.getUpstreamOltBandwidthProfile(),
890 action, u.getPonCTag())) {
891 //
892 log.error("Failed to {} EAPOL with suscriber tags", action);
893 //TODO this sets it for all services, maybe some services succeeded.
894 success.set(false);
895 }
896 });
897 return success.get();
898 }
899
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800900 protected void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700901 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
902 if (uti.getIsIgmpRequired()) {
903 DeviceId deviceId = sub.device.id();
904 // if we reached here a meter already exists
905 MeterId meterId = oltMeterService
906 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
907 MeterId oltMeterId = oltMeterService
908 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
909
910 processIgmpFilteringObjectives(deviceId, sub.port,
911 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
912 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
913 }
914 });
915 }
916
917 private boolean checkSadisRunning() {
918 if (bpService == null) {
919 log.warn("Sadis is not running");
920 return false;
921 }
922 return true;
923 }
924
925 private int getDefaultTechProfileId(Port port) {
926 if (!checkSadisRunning()) {
927 return defaultTechProfileId;
928 }
929 if (port != null) {
930 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
931 if (info != null && info.uniTagList().size() == 1) {
932 return info.uniTagList().get(0).getTechnologyProfileId();
933 }
934 }
935 return defaultTechProfileId;
936 }
937
938 protected Long createTechProfValueForWriteMetadata(VlanId cVlan, int techProfileId, MeterId upstreamOltMeterId) {
939 Long writeMetadata;
940
941 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
942 writeMetadata = (long) techProfileId << 32;
943 } else {
944 writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
945 }
946 if (upstreamOltMeterId == null) {
947 return writeMetadata;
948 } else {
949 return writeMetadata | upstreamOltMeterId.id();
950 }
951 }
952
953 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
954 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
955
956 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
957 .withKey(Criteria.matchInPort(port.number()))
958 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
959 .withMeta(DefaultTrafficTreatment.builder()
960 .setOutput(PortNumber.CONTROLLER).build())
961 .fromApp(appId)
962 .withPriority(MAX_PRIORITY)
963 .add(new ObjectiveContext() {
964 @Override
965 public void onSuccess(Objective objective) {
966 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
967 }
968
969 @Override
970 public void onError(Objective objective, ObjectiveError error) {
971 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
972 error);
973 }
974 });
975
976 flowObjectiveService.filter(deviceId, lldp);
977 }
978
979 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
980 FlowOperation action,
981 SubscriberAndDeviceInformation si) {
982 si.uniTagList().forEach(uti -> {
983
984 if (!uti.getIsDhcpRequired()) {
985 return;
986 }
987
988 // if it's an ADD skip if flows are there,
989 // if it's a DELETE skip if flows are not there
990 boolean hasFlows = hasDhcpFlows(port, uti);
991 if (action == FlowOperation.ADD && hasFlows ||
992 action == FlowOperation.REMOVE && !hasFlows) {
993 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
994 uti.getServiceName(), hasFlows);
995 return;
996 }
997
998 log.info("{} DHCP flows for subscriber on {} and service {}",
999 flowOpToString(action), portWithName(port), uti.getServiceName());
1000
1001 // if we reached here a meter already exists
1002 MeterId meterId = oltMeterService
1003 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
1004 MeterId oltMeterId = oltMeterService
1005 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
1006
1007 if (enableDhcpV4) {
1008 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
1009 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
1010 uti);
1011 }
1012 if (enableDhcpV6) {
1013 log.error("DHCP V6 not supported for subscribers");
1014 }
1015 });
1016 }
1017
1018 // FIXME return boolean, if this fails we need to retry
1019 protected void handleSubscriberDataFlows(Device device, Port port,
1020 FlowOperation action,
1021 SubscriberAndDeviceInformation si, String multicastServiceName) {
1022
1023 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
Matteo Scandolo97449bb2021-12-09 15:33:46 -08001024 if (nniPort == null || nniPort.isEmpty()) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001025 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
1026 si.id(), portWithName(port));
1027 return;
1028 }
1029 si.uniTagList().forEach(uti -> {
1030
1031 boolean hasFlows = hasSubscriberFlows(port, uti);
1032 if (action == FlowOperation.ADD && hasFlows ||
1033 action == FlowOperation.REMOVE && !hasFlows) {
1034 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1035 uti.getServiceName(), hasFlows);
1036 return;
1037 }
1038
1039 if (multicastServiceName.equals(uti.getServiceName())) {
1040 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1041 "dataplane flows are not needed",
1042 uti.getServiceName(), si.id(), portWithName(port));
1043 return;
1044 }
1045
1046 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1047 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
Matteo Scandolo80f5e972021-12-02 14:59:15 -08001048 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1049 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1050 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
1051 updateConnectPointStatus(sk, null, status, null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001052
1053 // upstream flows
1054 MeterId usMeterId = oltMeterService
1055 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1056 MeterId oltUsMeterId = oltMeterService
1057 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
1058 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1059 oltUsMeterId, uti);
1060
1061 // downstream flows
1062 MeterId dsMeterId = oltMeterService
1063 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1064 MeterId oltDsMeterId = oltMeterService
1065 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
1066 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1067 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1068 });
1069 }
1070
1071 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1072 FlowOperation action, FlowDirection direction,
1073 int udpSrc, int udpDst, EthType ethType, byte protocol,
1074 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1075 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1076 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1077
1078
1079 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1080 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
1081 updateConnectPointStatus(sk, null, null, status);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001082
1083 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1084 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1085
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001086 if (meterId != null) {
1087 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001088 }
1089
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001090 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
1091 treatmentBuilder.writeMetadata(
1092 createTechProfValueForWriteMetadata(uti.getUniTagMatch(),
1093 uti.getTechnologyProfileId(), oltMeterId), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001094 }
1095
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001096 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001097 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001098 .addCondition(Criteria.matchEthType(ethType))
1099 .addCondition(Criteria.matchIPProtocol(protocol))
1100 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1101 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001102 .fromApp(appId)
1103 .withPriority(MAX_PRIORITY);
1104
Andrea Campanella0e34f562020-06-11 10:47:10 +02001105 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001106 if (direction == FlowDirection.UPSTREAM) {
1107 treatmentBuilder.setVlanId(uti.getPonCTag());
1108 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1109 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001110 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001111 if (uti.getUsPonCTagPriority() != -1) {
1112 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001113 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001114 }
1115
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001116 dhcpBuilder.withMeta(treatmentBuilder
1117 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001118
1119
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001120 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001121 @Override
1122 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001123 log.info("{} DHCP {} filter for {}.",
1124 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1125 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001126 }
1127
1128 @Override
1129 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001130 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001131 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1132 portWithName(port),
1133 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001134 error);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001135 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001136 }
1137 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001138 flowObjectiveService.filter(deviceId, dhcpUpstream);
1139 }
1140
1141 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1142 FlowOperation action, FlowDirection direction,
1143 MeterId meterId, MeterId oltMeterId, int techProfileId,
1144 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1145
1146 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1147 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1148 if (direction == FlowDirection.UPSTREAM) {
1149
1150 if (techProfileId != NONE_TP_ID) {
1151 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(null,
1152 techProfileId, oltMeterId), 0);
1153 }
1154
1155
1156 if (meterId != null) {
1157 treatmentBuilder.meter(meterId);
1158 }
1159
1160 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1161 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1162 }
1163
1164 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1165 treatmentBuilder.setVlanId(cTag);
1166 }
1167
1168 if (vlanPcp != -1) {
1169 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1170 }
1171 }
1172
1173 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1174
1175 FilteringObjective igmp = filterBuilder
1176 .withKey(Criteria.matchInPort(port.number()))
1177 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1178 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1179 .withMeta(treatmentBuilder
1180 .setOutput(PortNumber.CONTROLLER).build())
1181 .fromApp(appId)
1182 .withPriority(MAX_PRIORITY)
1183 .add(new ObjectiveContext() {
1184 @Override
1185 public void onSuccess(Objective objective) {
1186 log.info("Igmp filter for {} {}.", portWithName(port), action);
1187 }
1188
1189 @Override
1190 public void onError(Objective objective, ObjectiveError error) {
1191 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1192 error);
1193 }
1194 });
1195
1196 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001197
1198 }
1199
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001200 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1201 FlowOperation action, FlowDirection direction,
1202 MeterId meterId, MeterId oltMeterId, int techProfileId,
1203 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001204
1205 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1206 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001207
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001208 if (meterId != null) {
1209 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001210 }
1211
1212 if (techProfileId != NONE_TP_ID) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001213 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(cTag, techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001214 }
1215
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001216 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1217 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001218 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001219 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1220 .fromApp(appId)
1221 .withPriority(10000);
1222
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001223 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001224 treatmentBuilder.setVlanId(cTag);
1225 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1226 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1227 }
1228 if (vlanPcp != null) {
1229 treatmentBuilder.setVlanPcp(vlanPcp);
1230 }
1231 }
1232 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1233
1234 FilteringObjective pppoed = pppoedBuilder
1235 .add(new ObjectiveContext() {
1236 @Override
1237 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001238 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001239 }
1240
1241 @Override
1242 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001243 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1244 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001245 }
1246 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001247 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001248 }
1249
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001250 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1251 FlowOperation action,
1252 MeterId upstreamMeterId,
1253 MeterId upstreamOltMeterId,
1254 UniTagInformation uti) {
1255 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001256 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001257 .matchInPort(port.number())
1258 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001259 .build();
1260
Andrea Campanella327c5722020-01-30 11:34:13 +01001261 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1262 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001263 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001264 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001265 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001266 }
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001267 if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
1268 treatmentBuilder.popVlan();
1269 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001270
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001271 if (uti.getUsPonCTagPriority() != -1) {
1272 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Maria Carmela Cascino067ee4d2021-11-02 13:14:43 +01001273
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001274 }
1275
1276 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001277 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001278
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001279 if (uti.getUsPonSTagPriority() != -1) {
1280 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001281 }
1282
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001283 treatmentBuilder.setOutput(nniPort.number())
1284 .writeMetadata(createMetadata(uti.getPonCTag(),
1285 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001286
yasin saplib4b8ee12021-06-13 18:25:20 +00001287 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1288
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001289 if (upstreamMeterId != null) {
1290 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001291 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1292 }
1293 if (upstreamOltMeterId != null) {
1294 treatmentBuilder.meter(upstreamOltMeterId);
1295 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001296 }
1297
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001298 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1299 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001300 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001301
1302 ObjectiveContext context = new ObjectiveContext() {
1303 @Override
1304 public void onSuccess(Objective objective) {
1305 log.info("{} Upstream Data plane filter for {}.",
1306 completeFlowOpToString(action), sk);
1307 }
1308
1309 @Override
1310 public void onError(Objective objective, ObjectiveError error) {
1311 log.error("Upstream Data plane filter for {} failed {} because {}.",
1312 sk, action, error);
1313 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
1314 }
1315 };
1316
1317 ForwardingObjective flow = null;
1318 if (action == FlowOperation.ADD) {
1319 flow = flowBuilder.add(context);
1320 } else if (action == FlowOperation.REMOVE) {
1321 flow = flowBuilder.remove(context);
1322 } else {
1323 log.error("Flow action not supported: {}", action);
1324 }
1325
1326 if (flow != null) {
1327 flowObjectiveService.forward(deviceId, flow);
1328 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001329 }
1330
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001331 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1332 FlowOperation action,
1333 MeterId downstreamMeterId,
1334 MeterId downstreamOltMeterId,
1335 UniTagInformation uti,
1336 MacAddress macAddress) {
1337 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001338 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001339 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001340 .matchVlanId(uti.getPonSTag())
1341 .matchInPort(nniPort.number())
1342 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001343
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001344 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1345 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001346 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001347
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001348 if (uti.getDsPonCTagPriority() != -1) {
1349 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001350 }
1351
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001352 if (macAddress != null) {
1353 selectorBuilder.matchEthDst(macAddress);
1354 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001355
1356 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1357 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001358 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001359
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001360 treatmentBuilder.writeMetadata(createMetadata(uti.getPonCTag(),
1361 uti.getTechnologyProfileId(),
1362 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001363
Andrea Campanella981e86c2021-03-12 11:35:33 +01001364 // Upstream pbit is used to remark inner vlan pbit.
1365 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1366 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1367 // all pbit acceptance are not widely supported by vendors even though present in
1368 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001369 if (uti.getUsPonCTagPriority() != -1) {
1370 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001371 }
1372
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001373 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1374 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1375 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001376 }
1377
yasin saplib4b8ee12021-06-13 18:25:20 +00001378 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1379
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001380 if (downstreamMeterId != null) {
1381 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001382 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001383 }
1384
yasin saplib4b8ee12021-06-13 18:25:20 +00001385 if (downstreamOltMeterId != null) {
1386 treatmentBuilder.meter(downstreamOltMeterId);
1387 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1388 }
1389
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001390 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1391 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001392
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001393 ObjectiveContext context = new ObjectiveContext() {
1394 @Override
1395 public void onSuccess(Objective objective) {
1396 log.info("{} Downstream Data plane filter for {}.",
1397 completeFlowOpToString(action), sk);
1398 }
1399
1400 @Override
1401 public void onError(Objective objective, ObjectiveError error) {
1402 log.info("Downstream Data plane filter for {} failed {} because {}.",
1403 sk, action, error);
1404 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
1405 }
1406 };
1407
1408 ForwardingObjective flow = null;
1409 if (action == FlowOperation.ADD) {
1410 flow = flowBuilder.add(context);
1411 } else if (action == FlowOperation.REMOVE) {
1412 flow = flowBuilder.remove(context);
1413 } else {
1414 log.error("Flow action not supported: {}", action);
1415 }
1416
1417 if (flow != null) {
1418 if (log.isTraceEnabled()) {
1419 log.trace("Forwarding rule {}", flow);
1420 }
1421 flowObjectiveService.forward(deviceId, flow);
1422 }
Andrea Campanella600d2e22020-06-22 11:00:31 +02001423 }
1424
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001425 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1426 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001427 Integer priority,
1428 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001429 return DefaultForwardingObjective.builder()
1430 .withFlag(ForwardingObjective.Flag.VERSATILE)
1431 .withPriority(priority)
1432 .makePermanent()
1433 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001434 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001435 .fromApp(appId)
1436 .withTreatment(treatment);
1437 }
1438
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001439 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
1440 if (techProfileId == NONE_TP_ID) {
Andrea Campanella7c49b792020-05-11 11:36:53 +02001441 techProfileId = DEFAULT_TP_ID_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001442 }
1443
1444 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
1445 }
1446
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001447 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1448 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1449 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001450
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001451 si.uniTagList().forEach(uniTagInfo -> {
1452 if (uniTagInfo.getEnableMacLearning()) {
1453 requiresMacLearning.set(true);
1454 }
1455 });
1456
1457 return requiresMacLearning.get();
1458 }
1459
1460 /**
1461 * Checks whether the subscriber has the MacAddress configured or discovered.
1462 *
1463 * @param deviceId DeviceId for this subscriber
1464 * @param port Port for this subscriber
1465 * @param si SubscriberAndDeviceInformation
1466 * @return boolean
1467 */
1468 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1469 AtomicBoolean isConfigured = new AtomicBoolean();
1470 isConfigured.set(true);
1471
1472 si.uniTagList().forEach(uniTagInfo -> {
1473 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
1474 boolean configureMac = isMacAddressValid(uniTagInfo);
1475 boolean discoveredMac = false;
1476 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1477 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1478 if (optHost.isPresent() && optHost.get().mac() != null) {
1479 discoveredMac = true;
1480 }
1481 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1482 log.debug("Awaiting for macAddress on {} for service {}",
1483 portWithName(port), uniTagInfo.getServiceName());
1484 isConfigured.set(false);
1485 }
1486 });
1487
1488 return isConfigured.get();
1489 }
1490
1491 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
1492 boolean configuredMac = isMacAddressValid(uniTagInfo);
1493 if (configuredMac) {
1494 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1495 } else if (uniTagInfo.getEnableMacLearning()) {
1496 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1497 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1498 if (optHost.isPresent() && optHost.get().mac() != null) {
1499 return optHost.get().mac();
1500 }
1501 }
1502 return null;
1503 }
1504
1505 private boolean isMacAddressValid(UniTagInformation tagInformation) {
1506 return tagInformation.getConfiguredMacAddress() != null &&
1507 !tagInformation.getConfiguredMacAddress().trim().equals("") &&
1508 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
1509 }
1510
1511 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
1512 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001513 if (log.isTraceEnabled()) {
1514 log.trace("Updating cpStatus {} with values: eapolFlow={}, subscriberFlows={}, dhcpFlow={}",
1515 key, eapolStatus, subscriberFlowsStatus, dhcpStatus);
1516 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001517 try {
1518 cpStatusWriteLock.lock();
1519 OltPortStatus status = cpStatus.get(key);
1520
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001521
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001522 if (status == null) {
Matteo Scandolo2542e5d2021-12-01 16:53:41 -08001523 // if we don't have status for the connectPoint
1524 // and we're only updating status to PENDING_REMOVE or ERROR
1525 // do not create it. This is because this case will only happen when a device is removed
1526 // and it's status cleaned
1527 List<OltFlowsStatus> statusesToIgnore = new ArrayList<>();
1528 statusesToIgnore.add(OltFlowsStatus.PENDING_REMOVE);
1529 statusesToIgnore.add(OltFlowsStatus.ERROR);
1530
1531 if (
1532 (statusesToIgnore.contains(subscriberFlowsStatus) && dhcpStatus == null) ||
1533 (subscriberFlowsStatus == null && statusesToIgnore.contains(dhcpStatus))
1534 ) {
1535 if (log.isTraceEnabled()) {
1536 log.trace("Ignoring cpStatus update as status is meaningless");
1537 }
1538 return;
1539 }
1540
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001541 status = new OltPortStatus(
1542 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
1543 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
1544 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE
1545 );
1546 } else {
1547 if (eapolStatus != null) {
1548 status.defaultEapolStatus = eapolStatus;
1549 }
1550 if (subscriberFlowsStatus != null) {
1551 status.subscriberFlowsStatus = subscriberFlowsStatus;
1552 }
1553 if (dhcpStatus != null) {
1554 status.dhcpStatus = dhcpStatus;
1555 }
1556 }
1557
1558 cpStatus.put(key, status);
1559 } finally {
1560 cpStatusWriteLock.unlock();
1561 }
1562 }
1563
1564 protected class InternalFlowListener implements FlowRuleListener {
1565 @Override
1566 public void event(FlowRuleEvent event) {
1567 if (appId.id() != (event.subject().appId())) {
1568 return;
1569 }
1570
1571 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1572 if (log.isTraceEnabled()) {
1573 log.trace("ignoring flow event {} " +
1574 "as not leader for {}", event, event.subject().deviceId());
1575 }
1576 return;
1577 }
1578
1579 switch (event.type()) {
1580 case RULE_ADDED:
1581 case RULE_REMOVED:
1582 Port port = getCpFromFlowRule(event.subject());
1583 if (port == null) {
1584 log.error("Can't find port in flow {}", event.subject());
1585 return;
1586 }
1587 if (log.isTraceEnabled()) {
1588 log.trace("flow event {} on cp {}: {}", event.type(),
1589 portWithName(port), event.subject());
1590 }
1591 updateCpStatus(event.type(), port, event.subject());
1592 return;
1593 case RULE_ADD_REQUESTED:
1594 case RULE_REMOVE_REQUESTED:
1595 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1596 return;
1597 default:
1598 return;
1599 }
1600 }
1601
1602 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
1603 OltFlowsStatus status = flowRuleStatusToOltFlowStatus(type);
1604 if (isDefaultEapolFlow(flowRule)) {
1605 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1606 defaultEapolUniTag);
1607 if (log.isTraceEnabled()) {
1608 log.trace("update defaultEapolStatus {} on {}", status, sk);
1609 }
1610 updateConnectPointStatus(sk, status, null, null);
1611 } else if (isDhcpFlow(flowRule)) {
1612 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1613 if (sk == null) {
1614 return;
1615 }
1616 if (log.isTraceEnabled()) {
1617 log.trace("update dhcpStatus {} on {}", status, sk);
1618 }
1619 updateConnectPointStatus(sk, null, null, status);
1620 } else if (isDataFlow(flowRule)) {
1621
1622 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()),
1623 getCpFromFlowRule(flowRule).number())) {
1624 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1625 return;
1626 }
1627
1628 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1629 if (sk == null) {
1630 return;
1631 }
1632 if (log.isTraceEnabled()) {
1633 log.trace("update dataplaneStatus {} on {}", status, sk);
1634 }
1635 updateConnectPointStatus(sk, null, status, null);
1636 }
1637 }
1638
1639 private boolean isDefaultEapolFlow(FlowRule flowRule) {
1640 EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
1641 if (c == null) {
1642 return false;
1643 }
1644 if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
1645 AtomicBoolean isDefault = new AtomicBoolean(false);
1646 flowRule.treatment().allInstructions().forEach(instruction -> {
1647 if (instruction.type() == L2MODIFICATION) {
1648 L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
1649 if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
1650 L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
1651 (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
1652 if (vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
1653 isDefault.set(true);
1654 return;
1655 }
1656 }
1657 }
1658 });
1659 return isDefault.get();
1660 }
1661 return false;
1662 }
1663
1664 /**
1665 * Returns true if the flow is a DHCP flow.
1666 * Matches both upstream and downstream flows.
1667 *
1668 * @param flowRule The FlowRule to evaluate
1669 * @return boolean
1670 */
1671 private boolean isDhcpFlow(FlowRule flowRule) {
1672 IPProtocolCriterion ipCriterion = (IPProtocolCriterion) flowRule.selector()
1673 .getCriterion(Criterion.Type.IP_PROTO);
1674 if (ipCriterion == null) {
1675 return false;
1676 }
1677
1678 UdpPortCriterion src = (UdpPortCriterion) flowRule.selector().getCriterion(Criterion.Type.UDP_SRC);
1679
1680 if (src == null) {
1681 return false;
1682 }
1683 return ipCriterion.protocol() == IPv4.PROTOCOL_UDP &&
1684 (src.udpPort().toInt() == 68 || src.udpPort().toInt() == 67);
1685 }
1686
1687 private boolean isDataFlow(FlowRule flowRule) {
1688 // we consider subscriber flows the one that matches on VLAN_VID
1689 // method is valid only because it's the last check after EAPOL and DHCP.
1690 // this matches mcast flows as well, if we want to avoid that we can
1691 // filter out the elements that have groups in the treatment or
1692 // mcastIp in the selector
1693 // IPV4_DST:224.0.0.22/32
1694 // treatment=[immediate=[GROUP:0x1]]
1695
1696 return flowRule.selector().getCriterion(Criterion.Type.VLAN_VID) != null;
1697 }
1698
1699 private Port getCpFromFlowRule(FlowRule flowRule) {
1700 DeviceId deviceId = flowRule.deviceId();
1701 PortCriterion inPort = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
1702 if (inPort != null) {
1703 PortNumber port = inPort.port();
1704 return deviceService.getPort(deviceId, port);
1705 }
1706 return null;
1707 }
1708
1709 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule) {
1710 Port flowPort = getCpFromFlowRule(flowRule);
1711 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1712
1713 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1714 if (si == null && !isNni) {
1715 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1716 return null;
1717 }
1718
1719 if (isNni) {
1720 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1721 }
1722
1723 Optional<UniTagInformation> found = Optional.empty();
1724 VlanId flowVlan = null;
1725 if (isDhcpFlow(flowRule)) {
1726 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1727 L2ModificationInstruction.ModVlanIdInstruction instruction =
1728 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1729 flowVlan = instruction.vlanId();
1730 } else {
1731 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1732 VlanIdCriterion vlanIdCriterion =
1733 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1734 if (vlanIdCriterion == null) {
1735 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1736 return null;
1737 }
1738 flowVlan = vlanIdCriterion.vlanId();
1739 }
1740
1741 VlanId finalFlowVlan = flowVlan;
1742 found = si.uniTagList().stream().filter(uti ->
1743 uti.getPonCTag().equals(finalFlowVlan) ||
1744 uti.getPonSTag().equals(finalFlowVlan) ||
1745 uti.getUniTagMatch().equals(finalFlowVlan)
1746 ).findFirst();
1747
1748
1749 if (found.isEmpty()) {
1750 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1751 }
1752
1753 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1754
1755 }
1756
1757 private OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
1758 switch (type) {
1759 case RULE_ADD_REQUESTED:
1760 return OltFlowsStatus.PENDING_ADD;
1761 case RULE_ADDED:
1762 return OltFlowsStatus.ADDED;
1763 case RULE_REMOVE_REQUESTED:
1764 return OltFlowsStatus.PENDING_REMOVE;
1765 case RULE_REMOVED:
1766 return OltFlowsStatus.REMOVED;
1767 default:
1768 return OltFlowsStatus.NONE;
1769 }
1770 }
1771 }
1772
1773 protected void bindSadisService(SadisService service) {
1774 this.subsService = service.getSubscriberInfoService();
1775 this.bpService = service.getBandwidthProfileService();
1776 log.info("Sadis service is loaded");
1777 }
1778
1779 protected void unbindSadisService(SadisService service) {
1780 this.subsService = null;
1781 this.bpService = null;
1782 log.info("Sadis service is unloaded");
1783 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001784}