blob: cbc538a6fedd3bc01800b6b18623005162c5f29e [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
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010085import java.util.Dictionary;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070086import java.util.HashMap;
87import java.util.Iterator;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000088import java.util.Map;
Andrea Campanellabfb47af2021-06-03 11:09:45 +020089import java.util.Optional;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010090import java.util.Properties;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070091import java.util.concurrent.atomic.AtomicBoolean;
92import java.util.concurrent.locks.Lock;
93import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010094
95import static com.google.common.base.Strings.isNullOrEmpty;
96import static org.onlab.util.Tools.get;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070097import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
98import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
99import static org.opencord.olt.impl.OltUtils.flowOpToString;
100import static org.opencord.olt.impl.OltUtils.getPortName;
101import static org.opencord.olt.impl.OltUtils.portWithName;
102import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
103import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
104import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
105import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
106import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
107import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
108import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
109import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
110import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
111import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
112import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
113import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
114import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
115import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
116import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
117import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
118import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
119import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
120import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
121import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000122
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000123@Component(immediate = true, property = {
Saurav Dasf62cea82020-08-26 17:43:04 -0700124 ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000125 ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
126 ENABLE_DHCP_V6 + ":Boolean=" + ENABLE_DHCP_V6_DEFAULT,
Saurav Dasf62cea82020-08-26 17:43:04 -0700127 ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000128 ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300129 ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700130 DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
131 // FIXME remove this option as potentially dangerous in production
132 WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000133})
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700134public class OltFlowService implements OltFlowServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
137 protected CoreService coreService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700140 protected ComponentConfigService cfgService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
143 protected FlowObjectiveService flowObjectiveService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000144
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000145 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
146 bind = "bindSadisService",
147 unbind = "unbindSadisService",
148 policy = ReferencePolicy.DYNAMIC)
149 protected volatile SadisService sadisService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700152 protected OltMeterServiceInterface oltMeterService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
155 protected OltDeviceServiceInterface oltDeviceService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY)
158 protected FlowRuleService flowRuleService;
159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
161 protected HostService hostService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000164 protected DeviceService deviceService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000167 protected StorageService storageService;
168
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700169 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
170 protected BaseInformationService<BandwidthProfileInformation> bpService;
171
172 private static final String APP_NAME = "org.opencord.olt";
173 protected ApplicationId appId;
174 private static final Integer MAX_PRIORITY = 10000;
175 private static final Integer MIN_PRIORITY = 1000;
176 private static final short EAPOL_DEFAULT_VLAN = 4091;
177 private static final int NONE_TP_ID = -1;
178 private static final String V4 = "V4";
179 private static final String V6 = "V6";
180 private final Logger log = LoggerFactory.getLogger(getClass());
181
182 protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
183 .setServiceName("defaultEapol").build();
184 protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
185 .setServiceName("nni")
186 .setTechnologyProfileId(NONE_TP_ID)
187 .setPonCTag(VlanId.NONE)
188 .setUniTagMatch(VlanId.ANY)
189 .setUsPonCTagPriority(-1)
190 .build();
191
192 /**
193 * Connect Point status map.
194 * Used to keep track of which cp has flows that needs to be removed when the status changes.
195 */
196 protected Map<ServiceKey, OltPortStatus> cpStatus;
197 private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
198 private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
199 private final Lock cpStatusReadLock = cpStatusLock.readLock();
200
201 /**
202 * This map contains the subscriber that have been provisioned by the operator.
203 * They may or may not have flows, depending on the port status.
204 * The map is used to define whether flows need to be provisioned when a port comes up.
205 */
206 protected Map<ServiceKey, Boolean> provisionedSubscribers;
207 private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
208 private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
209 private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
210
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000211 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700212 * Create DHCP trap flow on NNI port(s).
213 */
214 protected boolean enableDhcpOnNni = ENABLE_DHCP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000215
216 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700217 * Enable flows for DHCP v4 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000218 **/
219 protected boolean enableDhcpV4 = ENABLE_DHCP_V4_DEFAULT;
220
221 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700222 * Enable flows for DHCP v6 if dhcp is required in sadis config.
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000223 **/
224 protected boolean enableDhcpV6 = ENABLE_DHCP_V6_DEFAULT;
225
226 /**
Saurav Dasf62cea82020-08-26 17:43:04 -0700227 * Create IGMP trap flow on NNI port(s).
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000228 **/
Saurav Dasf62cea82020-08-26 17:43:04 -0700229 protected boolean enableIgmpOnNni = ENABLE_IGMP_ON_NNI_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000230
231 /**
232 * Send EAPOL authentication trap flows before subscriber provisioning.
233 **/
234 protected boolean enableEapol = ENABLE_EAPOL_DEFAULT;
235
236 /**
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300237 * Send PPPoED authentication trap flows before subscriber provisioning.
238 **/
239 protected boolean enablePppoe = ENABLE_PPPOE_DEFAULT;
240
241 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000242 * Default technology profile id that is used for authentication trap flows.
243 **/
244 protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
245
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700246 protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
247
248 public enum FlowOperation {
249 ADD,
250 REMOVE;
251
252
253 @Override
254 public String toString() {
255 return super.toString().toLowerCase();
256 }
257 }
258
259 public enum FlowDirection {
260 UPSTREAM,
261 DOWNSTREAM,
262 }
263
264 public enum OltFlowsStatus {
265 NONE,
266 PENDING_ADD,
267 ADDED,
268 PENDING_REMOVE,
269 REMOVED,
270 ERROR
271 }
272
273 protected InternalFlowListener internalFlowListener;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000274
275 @Activate
276 public void activate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700277 cfgService.registerProperties(getClass());
278 appId = coreService.registerApplication(APP_NAME);
279 internalFlowListener = new InternalFlowListener();
280
281 modified(context);
282
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000283 KryoNamespace serializer = KryoNamespace.newBuilder()
284 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700285 .register(OltFlowsStatus.class)
286 .register(FlowDirection.class)
287 .register(OltPortStatus.class)
288 .register(OltFlowsStatus.class)
Tunahan Sezenf0843b92021-04-30 07:13:16 +0000289 .register(AccessDevicePort.class)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700290 .register(new ServiceKeySerializer(), ServiceKey.class)
291 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000292 .build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000293
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700294 cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
295 .withName("volt-cp-status")
296 .withApplicationId(appId)
297 .withSerializer(Serializer.using(serializer))
298 .build().asJavaMap();
299
300 provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
301 .withName("volt-provisioned-subscriber")
302 .withApplicationId(appId)
303 .withSerializer(Serializer.using(serializer))
304 .build().asJavaMap();
305
306 flowRuleService.addListener(internalFlowListener);
307
308 log.info("Started");
309 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000310
311 @Deactivate
312 public void deactivate(ComponentContext context) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700313 cfgService.unregisterProperties(getClass(), false);
314 flowRuleService.removeListener(internalFlowListener);
315 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000316 }
317
318 @Modified
319 public void modified(ComponentContext context) {
320
321 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
322
Saurav Dasf62cea82020-08-26 17:43:04 -0700323 Boolean o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000324 if (o != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700325 enableDhcpOnNni = o;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000326 }
327
Andrea Campanella7c49b792020-05-11 11:36:53 +0200328 Boolean v4 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V4);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000329 if (v4 != null) {
330 enableDhcpV4 = v4;
331 }
332
Andrea Campanella7c49b792020-05-11 11:36:53 +0200333 Boolean v6 = Tools.isPropertyEnabled(properties, ENABLE_DHCP_V6);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000334 if (v6 != null) {
335 enableDhcpV6 = v6;
336 }
337
Saurav Dasf62cea82020-08-26 17:43:04 -0700338 Boolean p = Tools.isPropertyEnabled(properties, ENABLE_IGMP_ON_NNI);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000339 if (p != null) {
Saurav Dasf62cea82020-08-26 17:43:04 -0700340 enableIgmpOnNni = p;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000341 }
342
Andrea Campanella7c49b792020-05-11 11:36:53 +0200343 Boolean eap = Tools.isPropertyEnabled(properties, ENABLE_EAPOL);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000344 if (eap != null) {
345 enableEapol = eap;
346 }
347
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300348 Boolean pppoe = Tools.isPropertyEnabled(properties, ENABLE_PPPOE);
349 if (pppoe != null) {
350 enablePppoe = pppoe;
351 }
352
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700353 Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
354 if (wait != null) {
355 waitForRemoval = wait;
356 }
357
Andrea Campanella7c49b792020-05-11 11:36:53 +0200358 String tpId = get(properties, DEFAULT_TP_ID);
359 defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000360
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700361 log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " +
362 "enableDhcpV6:{}, enableIgmpOnNni:{}, " +
363 "enableEapol:{}, enablePppoe:{}, defaultTechProfileId:{}," +
364 "waitForRemoval:{}",
365 enableDhcpOnNni, enableDhcpV4, enableDhcpV6,
366 enableIgmpOnNni, enableEapol, enablePppoe,
367 defaultTechProfileId, waitForRemoval);
Andrea Campanellafee86422020-06-04 16:01:27 +0200368
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000369 }
370
371 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700372 public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
373 try {
374 cpStatusReadLock.lock();
375 return ImmutableMap.copyOf(cpStatus);
376 } finally {
377 cpStatusReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000378 }
379 }
380
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700381 @Override
382 public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
383 // NOTE we might want to remove this conversion and directly use cpStatus as it contains
384 // all the required information about suscribers
385 Map<ServiceKey, UniTagInformation> subscribers =
386 new HashMap<>();
387 try {
388 cpStatusReadLock.lock();
389
390 cpStatus.forEach((sk, status) -> {
391 if (
392 // not NNI Port
393 !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
394 sk.getPort().connectPoint().port()) &&
395 // not EAPOL flow
396 !sk.getService().equals(defaultEapolUniTag)
397 ) {
398 subscribers.put(sk, sk.getService());
399 }
400 });
401
402 return ImmutableMap.copyOf(subscribers);
403 } finally {
404 cpStatusReadLock.unlock();
405 }
406 }
407
408 @Override
409 public Map<ServiceKey, Boolean> getRequestedSubscribers() {
410 try {
411 provisionedSubscribersReadLock.lock();
412 return ImmutableMap.copyOf(provisionedSubscribers);
413 } finally {
414 provisionedSubscribersReadLock.unlock();
415 }
416 }
417
418 @Override
419 public void handleNniFlows(Device device, Port port, FlowOperation action) {
420
421 // always handle the LLDP flow
422 processLldpFilteringObjective(device.id(), port, action);
423
424 if (enableDhcpOnNni) {
425 if (enableDhcpV4) {
426 log.debug("Installing DHCPv4 trap flow on NNI {} for device {}", portWithName(port), device.id());
427 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
428 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
429 null, null, nniUniTag);
430 }
431 if (enableDhcpV6) {
432 log.debug("Installing DHCPv6 trap flow on NNI {} for device {}", portWithName(port), device.id());
433 processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
434 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
435 null, null, nniUniTag);
436 }
437 } else {
438 log.info("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
439 }
440
441 if (enableIgmpOnNni) {
442 log.debug("Installing IGMP flow on NNI {} for device {}", portWithName(port), device.id());
443 processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
444 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
445 }
446
447 if (enablePppoe) {
448 log.debug("Installing PPPoE flow on NNI {} for device {}", port.number(), device.id());
449 processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
450 null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
451 }
452 }
453
454 @Override
455 public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
456 String oltBandwidthProfileId) {
457
458 // we only need to something if EAPOL is enabled
459 if (!enableEapol) {
460 return true;
461 }
462
463 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
464 return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
465 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
466 return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
467 } else {
468 log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
469 return false;
470 }
471
472 }
473
474 private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
475
476 if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
477 if (log.isTraceEnabled()) {
478 log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
479 }
480 return false;
481 }
482 if (hasDefaultEapol(sub.port)) {
483 return true;
484 }
485 return handleEapolFlow(sub, bandwidthProfileId,
486 oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
487
488 }
489
490 private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
491 // NOTE that we are not checking for meters as they must have been created to install the flow in first place
492 return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
493 FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
494 }
495
496 @Override
497 public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
498 String multicastServiceName) {
499 // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
500 if (sub.status == DiscoveredSubscriber.Status.ADDED) {
501 return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
502 } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
503 return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
504 } else {
505 log.error("don't know how to handle {}", sub);
506 return false;
507 }
508 }
509
510 private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
511 String multicastServiceName) {
512 if (log.isTraceEnabled()) {
513 log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
514 }
515 if (enableEapol) {
516 if (hasDefaultEapol(sub.port)) {
517 // remove EAPOL flow and throw exception so that we'll retry later
518 if (!isDefaultEapolPendingRemoval(sub.port)) {
519 removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
520 }
521
522 if (waitForRemoval) {
523 // NOTE wait for removal is a flag only needed to make sure VOLTHA
524 // does not explode with the flows remove/add in the same batch
525 log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
526 return false;
527 } else {
528 log.warn("continuing provisioning on {}", portWithName(sub.port));
529 }
530 }
531
532 }
533
534 // NOTE createMeters will return if the meters are not installed
535 if (!oltMeterService.createMeters(sub.device.id(),
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800536 sub.subscriberAndDeviceInformation, multicastServiceName)) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700537 return false;
538 }
539
540 // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
541 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
542 sub.subscriberAndDeviceInformation);
543
544 if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
545 && !isMacAddressAvailable(sub.device.id(), sub.port,
546 sub.subscriberAndDeviceInformation)) {
547 log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
548 return false;
549 }
550
551 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
552 sub.subscriberAndDeviceInformation, multicastServiceName);
553
554 handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
555
556 handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
557
558 log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
559 return true;
560 }
561
562 private boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
563 String multicastServiceName) {
564
565 if (log.isTraceEnabled()) {
566 log.trace("Removal of subscriber on {} started",
567 portWithName(sub.port));
568 }
569 SubscriberAndDeviceInformation si = subsService.get(sub.portName());
570 if (si == null) {
571 log.error("Subscriber information not found in sadis for port {} during subscriber removal",
572 portWithName(sub.port));
573 // NOTE that we are returning true so that the subscriber is removed from the queue
574 // and we can move on provisioning others
575 return true;
576 }
577
578 handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
579
580 if (enableEapol) {
581 // remove the tagged eapol
582 handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
583
Matteo Scandolo49c42052021-11-23 13:12:29 -0800584 // and add the default one back (only if the port is ENABLED and still present on the device)
585 if (sub.port.isEnabled() && deviceService.getPort(sub.device.id(), sub.port.number()) != null) {
586
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700587 // NOTE we remove the subscriber when the port goes down
588 // but in that case we don't need to add default eapol
589 handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
590 FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
591 }
592 }
593 handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
594
595 handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
596
597 // FIXME check the return status of the flow and return accordingly
598 log.info("Removal of subscriber on {} completed", portWithName(sub.port));
599 return true;
600 }
601
602 @Override
603 public boolean hasDefaultEapol(Port port) {
604 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
605 // NOTE we consider ERROR as a present EAPOL flow as ONOS
606 // will keep trying to add it
607 return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
608 status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
609 status.defaultEapolStatus == OltFlowsStatus.ERROR);
610 }
611
612 private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
613 try {
614 cpStatusReadLock.lock();
615 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
616 OltPortStatus status = cpStatus.get(sk);
617 return status;
618 } finally {
619 cpStatusReadLock.unlock();
620 }
621 }
622
623 public boolean isDefaultEapolPendingRemoval(Port port) {
624 OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
625 if (log.isTraceEnabled()) {
626 log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
627 status, portWithName(port), defaultEapolUniTag);
628 }
629 return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
630 }
631
632 @Override
633 public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
634 OltPortStatus status = getOltPortStatus(port, uti);
635 if (log.isTraceEnabled()) {
636 log.trace("Status during DHCP flow check {} for port {} and service {}",
637 status, portWithName(port), uti.getServiceName());
638 }
639 return status != null &&
640 (status.dhcpStatus == OltFlowsStatus.ADDED ||
641 status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
642 }
643
644 @Override
645 public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
646
647 OltPortStatus status = getOltPortStatus(port, uti);
648 if (log.isTraceEnabled()) {
649 log.trace("Status during subscriber flow check {} for port {} and service {}",
650 status, portWithName(port), uti.getServiceName());
651 }
652 return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
653 status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
654 }
655
656 @Override
657 public void purgeDeviceFlows(DeviceId deviceId) {
658 log.debug("Purging flows on device {}", deviceId);
659 flowRuleService.purgeFlowRules(deviceId);
660
661 // removing the status from the cpStatus map
662 try {
663 cpStatusWriteLock.lock();
664 Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
665 while (iter.hasNext()) {
666 Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
667 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
668 cpStatus.remove(entry.getKey());
669 }
670 }
671 } finally {
672 cpStatusWriteLock.unlock();
673 }
674
675 // removing subscribers from the provisioned map
676 try {
677 provisionedSubscribersWriteLock.lock();
678 Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
679 while (iter.hasNext()) {
680 Map.Entry<ServiceKey, Boolean> entry = iter.next();
681 if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
682 provisionedSubscribers.remove(entry.getKey());
683 }
684 }
685 } finally {
686 provisionedSubscribersWriteLock.unlock();
687 }
688 }
689
690 @Override
691 public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
692 // if any service is programmed on this port, returns true
693 AtomicBoolean provisioned = new AtomicBoolean(false);
694 try {
695 provisionedSubscribersReadLock.lock();
696 for (Map.Entry<ServiceKey, Boolean> entry : provisionedSubscribers.entrySet()) {
697 if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
698 provisioned.set(true);
699 break;
700 }
701 }
702 } finally {
703 provisionedSubscribersReadLock.unlock();
704 }
705 return provisioned.get();
706 }
707
708 @Override
709 public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
710 try {
711 provisionedSubscribersReadLock.lock();
712 Boolean provisioned = provisionedSubscribers.get(sk);
713 if (provisioned == null || !provisioned) {
714 return false;
715 }
716 } finally {
717 provisionedSubscribersReadLock.unlock();
718 }
719 return true;
720 }
721
722 @Override
723 public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
724 try {
725 provisionedSubscribersWriteLock.lock();
726 provisionedSubscribers.put(sk, status);
727 } finally {
728 provisionedSubscribersWriteLock.unlock();
729 }
730 }
731
732 private boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
733 String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
734
735 // create a subscriberKey for the EAPOL flow
736 ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
737
738 // NOTE we only need to keep track of the default EAPOL flow in the
739 // connectpoint status map
740 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
741 OltFlowsStatus status = action == FlowOperation.ADD ?
742 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
743 updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE);
744
745 }
746
747 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
748 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
749
750 int techProfileId = getDefaultTechProfileId(sub.port);
751 MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
752
753 // in the delete case the meter should still be there as we remove
754 // the meters only if no flows are pointing to them
755 if (meterId == null) {
756 log.debug("MeterId is null for BandwidthProfile {} on device {}",
757 bandwidthProfile, sub.device.id());
758 return false;
759 }
760
761 MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
762 if (oltMeterId == null) {
763 log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
764 oltBandwidthProfile, sub.device.id());
765 return false;
766 }
767
768 log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
769 flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
770
771 FilteringObjective.Builder eapolAction;
772
773 if (action == FlowOperation.ADD) {
774 eapolAction = filterBuilder.permit();
775 } else if (action == FlowOperation.REMOVE) {
776 eapolAction = filterBuilder.deny();
777 } else {
778 log.error("Operation {} not supported", action);
779 return false;
780 }
781
782 FilteringObjective.Builder baseEapol = eapolAction
783 .withKey(Criteria.matchInPort(sub.port.number()))
784 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
785
786 // NOTE we only need to add the treatment to install the flow,
787 // we can remove it based in the match
788 FilteringObjective.Builder eapol;
789
790 TrafficTreatment treatment = treatmentBuilder
791 .meter(meterId)
792 .writeMetadata(createTechProfValueForWriteMetadata(
793 vlanId,
794 techProfileId, oltMeterId), 0)
795 .setOutput(PortNumber.CONTROLLER)
796 .pushVlan()
797 .setVlanId(vlanId)
798 .build();
799 eapol = baseEapol
800 .withMeta(treatment);
801
802 FilteringObjective eapolObjective = eapol
803 .fromApp(appId)
804 .withPriority(MAX_PRIORITY)
805 .add(new ObjectiveContext() {
806 @Override
807 public void onSuccess(Objective objective) {
808 log.info("EAPOL flow objective {} for {}",
809 completeFlowOpToString(action), portWithName(sub.port));
810 if (log.isTraceEnabled()) {
811 log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
812 }
813 }
814
815 @Override
816 public void onError(Objective objective, ObjectiveError error) {
817 log.error("Cannot {} eapol flow for {} : {}", action,
818 portWithName(sub.port), error);
819
820 if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
821 updateConnectPointStatus(sk,
822 OltFlowsStatus.ERROR, null, null);
823 }
824 }
825 });
826
827 flowObjectiveService.filter(sub.device.id(), eapolObjective);
828
829 log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
830 return true;
831 }
832
833 // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
834 private boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
835 SubscriberAndDeviceInformation si) {
836 if (!enableEapol) {
837 return true;
838 }
839 // TODO verify we need an EAPOL flow for EACH service
840 AtomicBoolean success = new AtomicBoolean(true);
841 si.uniTagList().forEach(u -> {
842 // we assume that if subscriber flows are installed, tagged EAPOL is installed as well
843 boolean hasFlows = hasSubscriberFlows(sub.port, u);
844
845 // if the subscriber flows are present the EAPOL flow is present thus we don't need to install it,
846 // if the subscriber does not have flows the EAPOL flow is not present thus we don't need to remove it
847 if (action == FlowOperation.ADD && hasFlows ||
848 action == FlowOperation.REMOVE && !hasFlows) {
849 log.debug("Not {} EAPOL on {}({}) as subscriber flow status is {}", flowOpToString(action),
850 portWithName(sub.port), u.getServiceName(), hasFlows);
851 return;
852 }
853 log.info("{} EAPOL flows for subscriber {} on {} and service {}",
854 flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
855 if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
856 u.getUpstreamOltBandwidthProfile(),
857 action, u.getPonCTag())) {
858 //
859 log.error("Failed to {} EAPOL with suscriber tags", action);
860 //TODO this sets it for all services, maybe some services succeeded.
861 success.set(false);
862 }
863 });
864 return success.get();
865 }
866
867 private void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
868 sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
869 if (uti.getIsIgmpRequired()) {
870 DeviceId deviceId = sub.device.id();
871 // if we reached here a meter already exists
872 MeterId meterId = oltMeterService
873 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
874 MeterId oltMeterId = oltMeterService
875 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
876
877 processIgmpFilteringObjectives(deviceId, sub.port,
878 action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
879 uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
880 }
881 });
882 }
883
884 private boolean checkSadisRunning() {
885 if (bpService == null) {
886 log.warn("Sadis is not running");
887 return false;
888 }
889 return true;
890 }
891
892 private int getDefaultTechProfileId(Port port) {
893 if (!checkSadisRunning()) {
894 return defaultTechProfileId;
895 }
896 if (port != null) {
897 SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
898 if (info != null && info.uniTagList().size() == 1) {
899 return info.uniTagList().get(0).getTechnologyProfileId();
900 }
901 }
902 return defaultTechProfileId;
903 }
904
905 protected Long createTechProfValueForWriteMetadata(VlanId cVlan, int techProfileId, MeterId upstreamOltMeterId) {
906 Long writeMetadata;
907
908 if (cVlan == null || VlanId.NONE.equals(cVlan)) {
909 writeMetadata = (long) techProfileId << 32;
910 } else {
911 writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
912 }
913 if (upstreamOltMeterId == null) {
914 return writeMetadata;
915 } else {
916 return writeMetadata | upstreamOltMeterId.id();
917 }
918 }
919
920 private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
921 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
922
923 FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
924 .withKey(Criteria.matchInPort(port.number()))
925 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
926 .withMeta(DefaultTrafficTreatment.builder()
927 .setOutput(PortNumber.CONTROLLER).build())
928 .fromApp(appId)
929 .withPriority(MAX_PRIORITY)
930 .add(new ObjectiveContext() {
931 @Override
932 public void onSuccess(Objective objective) {
933 log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
934 }
935
936 @Override
937 public void onError(Objective objective, ObjectiveError error) {
938 log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
939 error);
940 }
941 });
942
943 flowObjectiveService.filter(deviceId, lldp);
944 }
945
946 protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
947 FlowOperation action,
948 SubscriberAndDeviceInformation si) {
949 si.uniTagList().forEach(uti -> {
950
951 if (!uti.getIsDhcpRequired()) {
952 return;
953 }
954
955 // if it's an ADD skip if flows are there,
956 // if it's a DELETE skip if flows are not there
957 boolean hasFlows = hasDhcpFlows(port, uti);
958 if (action == FlowOperation.ADD && hasFlows ||
959 action == FlowOperation.REMOVE && !hasFlows) {
960 log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
961 uti.getServiceName(), hasFlows);
962 return;
963 }
964
965 log.info("{} DHCP flows for subscriber on {} and service {}",
966 flowOpToString(action), portWithName(port), uti.getServiceName());
967
968 // if we reached here a meter already exists
969 MeterId meterId = oltMeterService
970 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
971 MeterId oltMeterId = oltMeterService
972 .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
973
974 if (enableDhcpV4) {
975 processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
976 EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
977 uti);
978 }
979 if (enableDhcpV6) {
980 log.error("DHCP V6 not supported for subscribers");
981 }
982 });
983 }
984
985 // FIXME return boolean, if this fails we need to retry
986 protected void handleSubscriberDataFlows(Device device, Port port,
987 FlowOperation action,
988 SubscriberAndDeviceInformation si, String multicastServiceName) {
989
990 Optional<Port> nniPort = oltDeviceService.getNniPort(device);
991 if (nniPort.isEmpty()) {
992 log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
993 si.id(), portWithName(port));
994 return;
995 }
996 si.uniTagList().forEach(uti -> {
997
998 boolean hasFlows = hasSubscriberFlows(port, uti);
999 if (action == FlowOperation.ADD && hasFlows ||
1000 action == FlowOperation.REMOVE && !hasFlows) {
1001 log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
1002 uti.getServiceName(), hasFlows);
1003 return;
1004 }
1005
1006 if (multicastServiceName.equals(uti.getServiceName())) {
1007 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
1008 "dataplane flows are not needed",
1009 uti.getServiceName(), si.id(), portWithName(port));
1010 return;
1011 }
1012
1013 log.info("{} Data plane flows for subscriber {} on {} and service {}",
1014 flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
1015
1016 // upstream flows
1017 MeterId usMeterId = oltMeterService
1018 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
1019 MeterId oltUsMeterId = oltMeterService
1020 .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
1021 processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
1022 oltUsMeterId, uti);
1023
1024 // downstream flows
1025 MeterId dsMeterId = oltMeterService
1026 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
1027 MeterId oltDsMeterId = oltMeterService
1028 .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
1029 processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
1030 oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
1031 });
1032 }
1033
1034 private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
1035 FlowOperation action, FlowDirection direction,
1036 int udpSrc, int udpDst, EthType ethType, byte protocol,
1037 MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
1038 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
1039 log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
1040
1041
1042 OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
1043 OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
1044 updateConnectPointStatus(sk, null, null, status);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001045
1046 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1047 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1048
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001049 if (meterId != null) {
1050 treatmentBuilder.meter(meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001051 }
1052
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001053 if (uti.getTechnologyProfileId() != NONE_TP_ID) {
1054 treatmentBuilder.writeMetadata(
1055 createTechProfValueForWriteMetadata(uti.getUniTagMatch(),
1056 uti.getTechnologyProfileId(), oltMeterId), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001057 }
1058
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001059 FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001060 .withKey(Criteria.matchInPort(port.number()))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001061 .addCondition(Criteria.matchEthType(ethType))
1062 .addCondition(Criteria.matchIPProtocol(protocol))
1063 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
1064 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001065 .fromApp(appId)
1066 .withPriority(MAX_PRIORITY);
1067
Andrea Campanella0e34f562020-06-11 10:47:10 +02001068 //VLAN changes and PCP matching need to happen only in the upstream directions
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001069 if (direction == FlowDirection.UPSTREAM) {
1070 treatmentBuilder.setVlanId(uti.getPonCTag());
1071 if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
1072 dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
Andrea Campanella0e34f562020-06-11 10:47:10 +02001073 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001074 if (uti.getUsPonCTagPriority() != -1) {
1075 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001076 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001077 }
1078
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001079 dhcpBuilder.withMeta(treatmentBuilder
1080 .setOutput(PortNumber.CONTROLLER).build());
Andrea Campanella0e34f562020-06-11 10:47:10 +02001081
1082
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001083 FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001084 @Override
1085 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001086 log.info("{} DHCP {} filter for {}.",
1087 completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1088 portWithName(port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001089 }
1090
1091 @Override
1092 public void onError(Objective objective, ObjectiveError error) {
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001093 log.error("DHCP {} filter for {} failed {} because {}",
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001094 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
1095 portWithName(port),
1096 action,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001097 error);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001098 updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001099 }
1100 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001101 flowObjectiveService.filter(deviceId, dhcpUpstream);
1102 }
1103
1104 private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
1105 FlowOperation action, FlowDirection direction,
1106 MeterId meterId, MeterId oltMeterId, int techProfileId,
1107 VlanId cTag, VlanId unitagMatch, int vlanPcp) {
1108
1109 DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
1110 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1111 if (direction == FlowDirection.UPSTREAM) {
1112
1113 if (techProfileId != NONE_TP_ID) {
1114 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(null,
1115 techProfileId, oltMeterId), 0);
1116 }
1117
1118
1119 if (meterId != null) {
1120 treatmentBuilder.meter(meterId);
1121 }
1122
1123 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1124 filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1125 }
1126
1127 if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
1128 treatmentBuilder.setVlanId(cTag);
1129 }
1130
1131 if (vlanPcp != -1) {
1132 treatmentBuilder.setVlanPcp((byte) vlanPcp);
1133 }
1134 }
1135
1136 filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
1137
1138 FilteringObjective igmp = filterBuilder
1139 .withKey(Criteria.matchInPort(port.number()))
1140 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
1141 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
1142 .withMeta(treatmentBuilder
1143 .setOutput(PortNumber.CONTROLLER).build())
1144 .fromApp(appId)
1145 .withPriority(MAX_PRIORITY)
1146 .add(new ObjectiveContext() {
1147 @Override
1148 public void onSuccess(Objective objective) {
1149 log.info("Igmp filter for {} {}.", portWithName(port), action);
1150 }
1151
1152 @Override
1153 public void onError(Objective objective, ObjectiveError error) {
1154 log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
1155 error);
1156 }
1157 });
1158
1159 flowObjectiveService.filter(deviceId, igmp);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001160
1161 }
1162
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001163 private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
1164 FlowOperation action, FlowDirection direction,
1165 MeterId meterId, MeterId oltMeterId, int techProfileId,
1166 VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001167
1168 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
1169 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001170
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001171 if (meterId != null) {
1172 treatmentBuilder.meter(meterId);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001173 }
1174
1175 if (techProfileId != NONE_TP_ID) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001176 treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(cTag, techProfileId, oltMeterId), 0);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001177 }
1178
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001179 DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
1180 ? builder.permit() : builder.deny())
Tunahan Sezenf0843b92021-04-30 07:13:16 +00001181 .withKey(Criteria.matchInPort(port.number()))
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001182 .addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
1183 .fromApp(appId)
1184 .withPriority(10000);
1185
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001186 if (direction == FlowDirection.UPSTREAM) {
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001187 treatmentBuilder.setVlanId(cTag);
1188 if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
1189 pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
1190 }
1191 if (vlanPcp != null) {
1192 treatmentBuilder.setVlanPcp(vlanPcp);
1193 }
1194 }
1195 pppoedBuilder = pppoedBuilder.withMeta(treatmentBuilder.setOutput(PortNumber.CONTROLLER).build());
1196
1197 FilteringObjective pppoed = pppoedBuilder
1198 .add(new ObjectiveContext() {
1199 @Override
1200 public void onSuccess(Objective objective) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001201 log.info("PPPoED filter for {} {}.", portWithName(port), action);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001202 }
1203
1204 @Override
1205 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001206 log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
1207 action, error);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001208 }
1209 });
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001210 flowObjectiveService.filter(deviceId, pppoed);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001211 }
1212
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001213 private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1214 FlowOperation action,
1215 MeterId upstreamMeterId,
1216 MeterId upstreamOltMeterId,
1217 UniTagInformation uti) {
1218 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001219 TrafficSelector selector = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001220 .matchInPort(port.number())
1221 .matchVlanId(uti.getUniTagMatch())
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001222 .build();
1223
Andrea Campanella327c5722020-01-30 11:34:13 +01001224 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1225 //if the subscriberVlan (cTag) is different than ANY it needs to set.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001226 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
Andrea Campanella327c5722020-01-30 11:34:13 +01001227 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001228 .setVlanId(uti.getPonCTag());
Andrea Campanella327c5722020-01-30 11:34:13 +01001229 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001230
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001231 if (uti.getUsPonCTagPriority() != -1) {
1232 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001233 }
1234
1235 treatmentBuilder.pushVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001236 .setVlanId(uti.getPonSTag());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001237
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001238 if (uti.getUsPonSTagPriority() != -1) {
1239 treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001240 }
1241
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001242 treatmentBuilder.setOutput(nniPort.number())
1243 .writeMetadata(createMetadata(uti.getPonCTag(),
1244 uti.getTechnologyProfileId(), nniPort.number()), 0L);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001245
yasin saplib4b8ee12021-06-13 18:25:20 +00001246 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1247
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001248 if (upstreamMeterId != null) {
1249 treatmentBuilder.meter(upstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001250 annotationBuilder.set(UPSTREAM_ONU, upstreamMeterId.toString());
1251 }
1252 if (upstreamOltMeterId != null) {
1253 treatmentBuilder.meter(upstreamOltMeterId);
1254 annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001255 }
1256
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001257 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
1258 treatmentBuilder.build(), MIN_PRIORITY,
yasin saplib4b8ee12021-06-13 18:25:20 +00001259 annotationBuilder.build());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001260
1261 ObjectiveContext context = new ObjectiveContext() {
1262 @Override
1263 public void onSuccess(Objective objective) {
1264 log.info("{} Upstream Data plane filter for {}.",
1265 completeFlowOpToString(action), sk);
1266 }
1267
1268 @Override
1269 public void onError(Objective objective, ObjectiveError error) {
1270 log.error("Upstream Data plane filter for {} failed {} because {}.",
1271 sk, action, error);
1272 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
1273 }
1274 };
1275
1276 ForwardingObjective flow = null;
1277 if (action == FlowOperation.ADD) {
1278 flow = flowBuilder.add(context);
1279 } else if (action == FlowOperation.REMOVE) {
1280 flow = flowBuilder.remove(context);
1281 } else {
1282 log.error("Flow action not supported: {}", action);
1283 }
1284
1285 if (flow != null) {
1286 flowObjectiveService.forward(deviceId, flow);
1287 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001288 }
1289
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001290 private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
1291 FlowOperation action,
1292 MeterId downstreamMeterId,
1293 MeterId downstreamOltMeterId,
1294 UniTagInformation uti,
1295 MacAddress macAddress) {
1296 ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
Andrea Campanella327c5722020-01-30 11:34:13 +01001297 //subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001298 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001299 .matchVlanId(uti.getPonSTag())
1300 .matchInPort(nniPort.number())
1301 .matchInnerVlanId(uti.getPonCTag());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001302
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001303 if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1304 selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
Andrea Campanella090e4a02020-02-05 13:53:55 +01001305 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001306
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001307 if (uti.getDsPonCTagPriority() != -1) {
1308 selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001309 }
1310
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001311 if (macAddress != null) {
1312 selectorBuilder.matchEthDst(macAddress);
1313 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001314
1315 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
1316 .popVlan()
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001317 .setOutput(port.number());
Andrea Campanella327c5722020-01-30 11:34:13 +01001318
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001319 treatmentBuilder.writeMetadata(createMetadata(uti.getPonCTag(),
1320 uti.getTechnologyProfileId(),
1321 port.number()), 0);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001322
Andrea Campanella981e86c2021-03-12 11:35:33 +01001323 // Upstream pbit is used to remark inner vlan pbit.
1324 // Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
1325 // this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
1326 // all pbit acceptance are not widely supported by vendors even though present in
1327 // the OMCI spec.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001328 if (uti.getUsPonCTagPriority() != -1) {
1329 treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001330 }
1331
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001332 if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
1333 uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
1334 treatmentBuilder.setVlanId(uti.getUniTagMatch());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001335 }
1336
yasin saplib4b8ee12021-06-13 18:25:20 +00001337 DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
1338
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001339 if (downstreamMeterId != null) {
1340 treatmentBuilder.meter(downstreamMeterId);
yasin saplib4b8ee12021-06-13 18:25:20 +00001341 annotationBuilder.set(DOWNSTREAM_ONU, downstreamMeterId.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001342 }
1343
yasin saplib4b8ee12021-06-13 18:25:20 +00001344 if (downstreamOltMeterId != null) {
1345 treatmentBuilder.meter(downstreamOltMeterId);
1346 annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
1347 }
1348
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001349 DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
1350 treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001351
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001352 ObjectiveContext context = new ObjectiveContext() {
1353 @Override
1354 public void onSuccess(Objective objective) {
1355 log.info("{} Downstream Data plane filter for {}.",
1356 completeFlowOpToString(action), sk);
1357 }
1358
1359 @Override
1360 public void onError(Objective objective, ObjectiveError error) {
1361 log.info("Downstream Data plane filter for {} failed {} because {}.",
1362 sk, action, error);
1363 updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
1364 }
1365 };
1366
1367 ForwardingObjective flow = null;
1368 if (action == FlowOperation.ADD) {
1369 flow = flowBuilder.add(context);
1370 } else if (action == FlowOperation.REMOVE) {
1371 flow = flowBuilder.remove(context);
1372 } else {
1373 log.error("Flow action not supported: {}", action);
1374 }
1375
1376 if (flow != null) {
1377 if (log.isTraceEnabled()) {
1378 log.trace("Forwarding rule {}", flow);
1379 }
1380 flowObjectiveService.forward(deviceId, flow);
1381 }
Andrea Campanella600d2e22020-06-22 11:00:31 +02001382 }
1383
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001384 private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
1385 TrafficTreatment treatment,
yasin saplib4b8ee12021-06-13 18:25:20 +00001386 Integer priority,
1387 Annotations annotations) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001388 return DefaultForwardingObjective.builder()
1389 .withFlag(ForwardingObjective.Flag.VERSATILE)
1390 .withPriority(priority)
1391 .makePermanent()
1392 .withSelector(selector)
yasin saplib4b8ee12021-06-13 18:25:20 +00001393 .withAnnotations(annotations)
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001394 .fromApp(appId)
1395 .withTreatment(treatment);
1396 }
1397
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001398 private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
1399 if (techProfileId == NONE_TP_ID) {
Andrea Campanella7c49b792020-05-11 11:36:53 +02001400 techProfileId = DEFAULT_TP_ID_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001401 }
1402
1403 return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
1404 }
1405
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001406 private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
1407 AtomicBoolean requiresMacLearning = new AtomicBoolean();
1408 requiresMacLearning.set(false);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001409
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001410 si.uniTagList().forEach(uniTagInfo -> {
1411 if (uniTagInfo.getEnableMacLearning()) {
1412 requiresMacLearning.set(true);
1413 }
1414 });
1415
1416 return requiresMacLearning.get();
1417 }
1418
1419 /**
1420 * Checks whether the subscriber has the MacAddress configured or discovered.
1421 *
1422 * @param deviceId DeviceId for this subscriber
1423 * @param port Port for this subscriber
1424 * @param si SubscriberAndDeviceInformation
1425 * @return boolean
1426 */
1427 protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
1428 AtomicBoolean isConfigured = new AtomicBoolean();
1429 isConfigured.set(true);
1430
1431 si.uniTagList().forEach(uniTagInfo -> {
1432 boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
1433 boolean configureMac = isMacAddressValid(uniTagInfo);
1434 boolean discoveredMac = false;
1435 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1436 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1437 if (optHost.isPresent() && optHost.get().mac() != null) {
1438 discoveredMac = true;
1439 }
1440 if (isMacLearningEnabled && !configureMac && !discoveredMac) {
1441 log.debug("Awaiting for macAddress on {} for service {}",
1442 portWithName(port), uniTagInfo.getServiceName());
1443 isConfigured.set(false);
1444 }
1445 });
1446
1447 return isConfigured.get();
1448 }
1449
1450 protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
1451 boolean configuredMac = isMacAddressValid(uniTagInfo);
1452 if (configuredMac) {
1453 return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
1454 } else if (uniTagInfo.getEnableMacLearning()) {
1455 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
1456 .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
1457 if (optHost.isPresent() && optHost.get().mac() != null) {
1458 return optHost.get().mac();
1459 }
1460 }
1461 return null;
1462 }
1463
1464 private boolean isMacAddressValid(UniTagInformation tagInformation) {
1465 return tagInformation.getConfiguredMacAddress() != null &&
1466 !tagInformation.getConfiguredMacAddress().trim().equals("") &&
1467 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
1468 }
1469
1470 protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
1471 OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus) {
1472 try {
1473 cpStatusWriteLock.lock();
1474 OltPortStatus status = cpStatus.get(key);
1475
1476 if (status == null) {
1477 status = new OltPortStatus(
1478 eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
1479 subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
1480 dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE
1481 );
1482 } else {
1483 if (eapolStatus != null) {
1484 status.defaultEapolStatus = eapolStatus;
1485 }
1486 if (subscriberFlowsStatus != null) {
1487 status.subscriberFlowsStatus = subscriberFlowsStatus;
1488 }
1489 if (dhcpStatus != null) {
1490 status.dhcpStatus = dhcpStatus;
1491 }
1492 }
1493
1494 cpStatus.put(key, status);
1495 } finally {
1496 cpStatusWriteLock.unlock();
1497 }
1498 }
1499
1500 protected class InternalFlowListener implements FlowRuleListener {
1501 @Override
1502 public void event(FlowRuleEvent event) {
1503 if (appId.id() != (event.subject().appId())) {
1504 return;
1505 }
1506
1507 if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
1508 if (log.isTraceEnabled()) {
1509 log.trace("ignoring flow event {} " +
1510 "as not leader for {}", event, event.subject().deviceId());
1511 }
1512 return;
1513 }
1514
1515 switch (event.type()) {
1516 case RULE_ADDED:
1517 case RULE_REMOVED:
1518 Port port = getCpFromFlowRule(event.subject());
1519 if (port == null) {
1520 log.error("Can't find port in flow {}", event.subject());
1521 return;
1522 }
1523 if (log.isTraceEnabled()) {
1524 log.trace("flow event {} on cp {}: {}", event.type(),
1525 portWithName(port), event.subject());
1526 }
1527 updateCpStatus(event.type(), port, event.subject());
1528 return;
1529 case RULE_ADD_REQUESTED:
1530 case RULE_REMOVE_REQUESTED:
1531 // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
1532 return;
1533 default:
1534 return;
1535 }
1536 }
1537
1538 protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
1539 OltFlowsStatus status = flowRuleStatusToOltFlowStatus(type);
1540 if (isDefaultEapolFlow(flowRule)) {
1541 ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
1542 defaultEapolUniTag);
1543 if (log.isTraceEnabled()) {
1544 log.trace("update defaultEapolStatus {} on {}", status, sk);
1545 }
1546 updateConnectPointStatus(sk, status, null, null);
1547 } else if (isDhcpFlow(flowRule)) {
1548 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1549 if (sk == null) {
1550 return;
1551 }
1552 if (log.isTraceEnabled()) {
1553 log.trace("update dhcpStatus {} on {}", status, sk);
1554 }
1555 updateConnectPointStatus(sk, null, null, status);
1556 } else if (isDataFlow(flowRule)) {
1557
1558 if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()),
1559 getCpFromFlowRule(flowRule).number())) {
1560 // the NNI has data-plane for every subscriber, doesn't make sense to track them
1561 return;
1562 }
1563
1564 ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
1565 if (sk == null) {
1566 return;
1567 }
1568 if (log.isTraceEnabled()) {
1569 log.trace("update dataplaneStatus {} on {}", status, sk);
1570 }
1571 updateConnectPointStatus(sk, null, status, null);
1572 }
1573 }
1574
1575 private boolean isDefaultEapolFlow(FlowRule flowRule) {
1576 EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
1577 if (c == null) {
1578 return false;
1579 }
1580 if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
1581 AtomicBoolean isDefault = new AtomicBoolean(false);
1582 flowRule.treatment().allInstructions().forEach(instruction -> {
1583 if (instruction.type() == L2MODIFICATION) {
1584 L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
1585 if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
1586 L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
1587 (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
1588 if (vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
1589 isDefault.set(true);
1590 return;
1591 }
1592 }
1593 }
1594 });
1595 return isDefault.get();
1596 }
1597 return false;
1598 }
1599
1600 /**
1601 * Returns true if the flow is a DHCP flow.
1602 * Matches both upstream and downstream flows.
1603 *
1604 * @param flowRule The FlowRule to evaluate
1605 * @return boolean
1606 */
1607 private boolean isDhcpFlow(FlowRule flowRule) {
1608 IPProtocolCriterion ipCriterion = (IPProtocolCriterion) flowRule.selector()
1609 .getCriterion(Criterion.Type.IP_PROTO);
1610 if (ipCriterion == null) {
1611 return false;
1612 }
1613
1614 UdpPortCriterion src = (UdpPortCriterion) flowRule.selector().getCriterion(Criterion.Type.UDP_SRC);
1615
1616 if (src == null) {
1617 return false;
1618 }
1619 return ipCriterion.protocol() == IPv4.PROTOCOL_UDP &&
1620 (src.udpPort().toInt() == 68 || src.udpPort().toInt() == 67);
1621 }
1622
1623 private boolean isDataFlow(FlowRule flowRule) {
1624 // we consider subscriber flows the one that matches on VLAN_VID
1625 // method is valid only because it's the last check after EAPOL and DHCP.
1626 // this matches mcast flows as well, if we want to avoid that we can
1627 // filter out the elements that have groups in the treatment or
1628 // mcastIp in the selector
1629 // IPV4_DST:224.0.0.22/32
1630 // treatment=[immediate=[GROUP:0x1]]
1631
1632 return flowRule.selector().getCriterion(Criterion.Type.VLAN_VID) != null;
1633 }
1634
1635 private Port getCpFromFlowRule(FlowRule flowRule) {
1636 DeviceId deviceId = flowRule.deviceId();
1637 PortCriterion inPort = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
1638 if (inPort != null) {
1639 PortNumber port = inPort.port();
1640 return deviceService.getPort(deviceId, port);
1641 }
1642 return null;
1643 }
1644
1645 private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule) {
1646 Port flowPort = getCpFromFlowRule(flowRule);
1647 SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
1648
1649 Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
1650 if (si == null && !isNni) {
1651 log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
1652 return null;
1653 }
1654
1655 if (isNni) {
1656 return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
1657 }
1658
1659 Optional<UniTagInformation> found = Optional.empty();
1660 VlanId flowVlan = null;
1661 if (isDhcpFlow(flowRule)) {
1662 // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
1663 L2ModificationInstruction.ModVlanIdInstruction instruction =
1664 (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
1665 flowVlan = instruction.vlanId();
1666 } else {
1667 // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
1668 VlanIdCriterion vlanIdCriterion =
1669 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
1670 if (vlanIdCriterion == null) {
1671 log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
1672 return null;
1673 }
1674 flowVlan = vlanIdCriterion.vlanId();
1675 }
1676
1677 VlanId finalFlowVlan = flowVlan;
1678 found = si.uniTagList().stream().filter(uti ->
1679 uti.getPonCTag().equals(finalFlowVlan) ||
1680 uti.getPonSTag().equals(finalFlowVlan) ||
1681 uti.getUniTagMatch().equals(finalFlowVlan)
1682 ).findFirst();
1683
1684
1685 if (found.isEmpty()) {
1686 log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
1687 }
1688
1689 return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
1690
1691 }
1692
1693 private OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
1694 switch (type) {
1695 case RULE_ADD_REQUESTED:
1696 return OltFlowsStatus.PENDING_ADD;
1697 case RULE_ADDED:
1698 return OltFlowsStatus.ADDED;
1699 case RULE_REMOVE_REQUESTED:
1700 return OltFlowsStatus.PENDING_REMOVE;
1701 case RULE_REMOVED:
1702 return OltFlowsStatus.REMOVED;
1703 default:
1704 return OltFlowsStatus.NONE;
1705 }
1706 }
1707 }
1708
1709 protected void bindSadisService(SadisService service) {
1710 this.subsService = service.getSubscriberInfoService();
1711 this.bpService = service.getBandwidthProfileService();
1712 log.info("Sadis service is loaded");
1713 }
1714
1715 protected void unbindSadisService(SadisService service) {
1716 this.subsService = null;
1717 this.bpService = null;
1718 log.info("Sadis service is unloaded");
1719 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001720}