blob: bc922b0e1db991441e79ea515b499830ff6e0b8d [file] [log] [blame]
alshabibf0e7e702015-05-30 18:22:36 -07001/*
Brian O'Connord6a135a2017-08-03 22:46:05 -07002 * Copyright 2016-present Open Networking Foundation
alshabibf0e7e702015-05-30 18:22:36 -07003 *
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 */
alshabib36a4d732016-06-01 16:03:59 -070016package org.opencord.olt.impl;
alshabibf0e7e702015-05-30 18:22:36 -070017
Saurav Das82b8e6d2018-10-04 15:25:12 -070018import static com.google.common.base.Preconditions.checkNotNull;
19import static com.google.common.base.Strings.isNullOrEmpty;
20import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
21import static org.onlab.util.Tools.get;
22import static org.onlab.util.Tools.groupedThreads;
23import static org.slf4j.LoggerFactory.getLogger;
24
25import java.util.AbstractMap;
Amit Ghoshe1d3f092018-10-09 19:44:33 +010026import java.util.Arrays;
Saurav Das82b8e6d2018-10-04 15:25:12 -070027import java.util.ArrayList;
28import java.util.Collection;
29import java.util.Dictionary;
30import java.util.List;
31import java.util.Map;
32import java.util.Optional;
33import java.util.Properties;
34import java.util.concurrent.CompletableFuture;
35import java.util.concurrent.ExecutorService;
36import java.util.concurrent.Executors;
37
alshabibf0e7e702015-05-30 18:22:36 -070038import org.apache.felix.scr.annotations.Activate;
39import org.apache.felix.scr.annotations.Component;
40import org.apache.felix.scr.annotations.Deactivate;
alshabibe0559672016-02-21 14:49:51 -080041import org.apache.felix.scr.annotations.Modified;
42import org.apache.felix.scr.annotations.Property;
alshabibf0e7e702015-05-30 18:22:36 -070043import org.apache.felix.scr.annotations.Reference;
44import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harte533a422015-10-20 17:31:24 -070045import org.apache.felix.scr.annotations.Service;
alshabibdec2e252016-01-15 12:20:25 -080046import org.onlab.packet.EthType;
Amit Ghosh95e2f652017-08-23 12:49:46 +010047import org.onlab.packet.IPv4;
48import org.onlab.packet.TpPort;
alshabibf0e7e702015-05-30 18:22:36 -070049import org.onlab.packet.VlanId;
Amit Ghosh95e2f652017-08-23 12:49:46 +010050import org.onlab.util.Tools;
alshabibe0559672016-02-21 14:49:51 -080051import org.onosproject.cfg.ComponentConfigService;
alshabibf0e7e702015-05-30 18:22:36 -070052import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
alshabib8e4fd2f2016-01-12 15:55:53 -080054import org.onosproject.event.AbstractListenerManager;
alshabib09753b52016-03-04 14:55:19 -080055import org.onosproject.mastership.MastershipService;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010056import org.onosproject.net.AnnotationKeys;
Jonathan Harte533a422015-10-20 17:31:24 -070057import org.onosproject.net.ConnectPoint;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010058import org.onosproject.net.Device;
alshabibf0e7e702015-05-30 18:22:36 -070059import org.onosproject.net.DeviceId;
alshabibdec2e252016-01-15 12:20:25 -080060import org.onosproject.net.Port;
alshabibf0e7e702015-05-30 18:22:36 -070061import org.onosproject.net.PortNumber;
62import org.onosproject.net.device.DeviceEvent;
63import org.onosproject.net.device.DeviceListener;
64import org.onosproject.net.device.DeviceService;
65import org.onosproject.net.flow.DefaultTrafficSelector;
66import org.onosproject.net.flow.DefaultTrafficTreatment;
67import org.onosproject.net.flow.TrafficSelector;
68import org.onosproject.net.flow.TrafficTreatment;
alshabibdec2e252016-01-15 12:20:25 -080069import org.onosproject.net.flow.criteria.Criteria;
70import org.onosproject.net.flowobjective.DefaultFilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070071import org.onosproject.net.flowobjective.DefaultForwardingObjective;
alshabibdec2e252016-01-15 12:20:25 -080072import org.onosproject.net.flowobjective.FilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070073import org.onosproject.net.flowobjective.FlowObjectiveService;
74import org.onosproject.net.flowobjective.ForwardingObjective;
alshabib3ea82642016-01-12 18:06:53 -080075import org.onosproject.net.flowobjective.Objective;
76import org.onosproject.net.flowobjective.ObjectiveContext;
77import org.onosproject.net.flowobjective.ObjectiveError;
Amit Ghoshe1d3f092018-10-09 19:44:33 +010078import org.onosproject.store.serializers.KryoNamespaces;
79import org.onosproject.store.service.ConsistentMultimap;
80import org.onosproject.store.service.Serializer;
81import org.onosproject.store.service.StorageService;
alshabib36a4d732016-06-01 16:03:59 -070082import org.opencord.olt.AccessDeviceEvent;
83import org.opencord.olt.AccessDeviceListener;
84import org.opencord.olt.AccessDeviceService;
Amit Ghosh31939522018-08-16 13:28:21 +010085import org.opencord.olt.AccessSubscriberId;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010086import org.opencord.sadis.SubscriberAndDeviceInformation;
87import org.opencord.sadis.SubscriberAndDeviceInformationService;
alshabibe0559672016-02-21 14:49:51 -080088import org.osgi.service.component.ComponentContext;
alshabibf0e7e702015-05-30 18:22:36 -070089import org.slf4j.Logger;
90
Amit Ghoshe1d3f092018-10-09 19:44:33 +010091
Saurav Das82b8e6d2018-10-04 15:25:12 -070092import com.google.common.collect.ImmutableMap;
93import com.google.common.collect.Maps;
alshabibf0e7e702015-05-30 18:22:36 -070094
95/**
Jonathan Harte533a422015-10-20 17:31:24 -070096 * Provisions rules on access devices.
alshabibf0e7e702015-05-30 18:22:36 -070097 */
Jonathan Harte533a422015-10-20 17:31:24 -070098@Service
alshabibf0e7e702015-05-30 18:22:36 -070099@Component(immediate = true)
alshabib8e4fd2f2016-01-12 15:55:53 -0800100public class Olt
101 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
102 implements AccessDeviceService {
Charles Chan54f110f2017-01-20 11:22:42 -0800103 private static final String APP_NAME = "org.opencord.olt";
alshabibe0559672016-02-21 14:49:51 -0800104
105 private static final short DEFAULT_VLAN = 0;
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100106 private static final String ADDITIONAL_VLANS = "additional-vlans";
alshabibe0559672016-02-21 14:49:51 -0800107
alshabibf0e7e702015-05-30 18:22:36 -0700108 private final Logger log = getLogger(getClass());
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected FlowObjectiveService flowObjectiveService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib09753b52016-03-04 14:55:19 -0800114 protected MastershipService mastershipService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibf0e7e702015-05-30 18:22:36 -0700117 protected DeviceService deviceService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected CoreService coreService;
121
Jonathan Harte533a422015-10-20 17:31:24 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe0559672016-02-21 14:49:51 -0800123 protected ComponentConfigService componentConfigService;
124
alshabib4ceaed32016-03-03 18:00:58 -0800125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100126 protected SubscriberAndDeviceInformationService subsService;
alshabibe0559672016-02-21 14:49:51 -0800127
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected StorageService storageService;
130
alshabibe0559672016-02-21 14:49:51 -0800131 @Property(name = "defaultVlan", intValue = DEFAULT_VLAN,
132 label = "Default VLAN RG<->ONU traffic")
133 private int defaultVlan = DEFAULT_VLAN;
134
Matt Jeanneret3f579262018-06-14 17:16:23 -0400135 @Property(name = "enableDhcpOnProvisioning", boolValue = true,
136 label = "Create the DHCP Flow rules when a subscriber is provisioned")
137 protected boolean enableDhcpOnProvisioning = false;
138
139 @Property(name = "enableIgmpOnProvisioning", boolValue = false,
140 label = "Create IGMP Flow rules when a subscriber is provisioned")
141 protected boolean enableIgmpOnProvisioning = false;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100142
alshabibf0e7e702015-05-30 18:22:36 -0700143 private final DeviceListener deviceListener = new InternalDeviceListener();
144
145 private ApplicationId appId;
146
Saurav Das82b8e6d2018-10-04 15:25:12 -0700147 private ExecutorService oltInstallers = Executors
148 .newFixedThreadPool(4, groupedThreads("onos/olt-service",
149 "olt-installer-%d"));
alshabibf0e7e702015-05-30 18:22:36 -0700150
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100151 private ConsistentMultimap<ConnectPoint, Map.Entry<VlanId, VlanId>> additionalVlans;
152
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700153 protected ExecutorService eventExecutor;
154
Saurav Das82b8e6d2018-10-04 15:25:12 -0700155 private Map<ConnectPoint, SubscriberAndDeviceInformation> programmedSubs;
156
alshabibf0e7e702015-05-30 18:22:36 -0700157 @Activate
alshabibe0559672016-02-21 14:49:51 -0800158 public void activate(ComponentContext context) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700159 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/olt", "events-%d", log));
alshabibe0559672016-02-21 14:49:51 -0800160 modified(context);
Charles Chan54f110f2017-01-20 11:22:42 -0800161 appId = coreService.registerApplication(APP_NAME);
alshabibe0559672016-02-21 14:49:51 -0800162 componentConfigService.registerProperties(getClass());
Saurav Das82b8e6d2018-10-04 15:25:12 -0700163 programmedSubs = Maps.newConcurrentMap();
alshabibc4dfe852015-06-05 13:35:13 -0700164
alshabib8e4fd2f2016-01-12 15:55:53 -0800165 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
166
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100167 // look for all provisioned devices in Sadis and create EAPOL flows for the
168 // UNI ports
169 Iterable<Device> devices = deviceService.getDevices();
170 for (Device d : devices) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700171 checkAndCreateDeviceFlows(d);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100172 }
alshabib4ceaed32016-03-03 18:00:58 -0800173
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100174 additionalVlans = storageService.<ConnectPoint, Map.Entry<VlanId, VlanId>>consistentMultimapBuilder()
175 .withName(ADDITIONAL_VLANS)
176 .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
177 AbstractMap.SimpleEntry.class))
178 .build();
179
alshabibba357492016-01-27 13:49:46 -0800180 deviceService.addListener(deviceListener);
181
alshabibf0e7e702015-05-30 18:22:36 -0700182 log.info("Started with Application ID {}", appId.id());
183 }
184
185 @Deactivate
186 public void deactivate() {
alshabibe0559672016-02-21 14:49:51 -0800187 componentConfigService.unregisterProperties(getClass(), false);
alshabib62e9ce72016-02-11 17:31:36 -0800188 deviceService.removeListener(deviceListener);
Jonathan Hart5f1c8142018-07-24 17:31:59 -0700189 eventDispatcher.removeSink(AccessDeviceEvent.class);
alshabibf0e7e702015-05-30 18:22:36 -0700190 log.info("Stopped");
191 }
192
alshabibe0559672016-02-21 14:49:51 -0800193 @Modified
194 public void modified(ComponentContext context) {
195 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
196
197 try {
198 String s = get(properties, "defaultVlan");
199 defaultVlan = isNullOrEmpty(s) ? DEFAULT_VLAN : Integer.parseInt(s.trim());
Amit Ghosh95e2f652017-08-23 12:49:46 +0100200
Matt Jeanneret3f579262018-06-14 17:16:23 -0400201 Boolean o = Tools.isPropertyEnabled(properties, "enableDhcpOnProvisioning");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100202 if (o != null) {
Matt Jeanneret3f579262018-06-14 17:16:23 -0400203 enableDhcpOnProvisioning = o;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100204 }
Matt Jeanneret3f579262018-06-14 17:16:23 -0400205
206 Boolean p = Tools.isPropertyEnabled(properties, "enableIgmpOnProvisioning");
207 if (p != null) {
208 enableIgmpOnProvisioning = p;
209 }
210
alshabibe0559672016-02-21 14:49:51 -0800211 } catch (Exception e) {
212 defaultVlan = DEFAULT_VLAN;
213 }
214 }
215
alshabib32232c82016-02-25 17:57:24 -0500216 @Override
Amit Ghosh31939522018-08-16 13:28:21 +0100217 public boolean provisionSubscriber(ConnectPoint port) {
Jonathan Hart94b90492018-04-24 14:02:25 -0700218 checkNotNull(deviceService.getPort(port.deviceId(), port.port()),
219 "Invalid connect point");
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100220 // Find the subscriber on this connect point
221 SubscriberAndDeviceInformation sub = getSubscriber(port);
222 if (sub == null) {
223 log.warn("No subscriber found for {}", port);
Amit Ghosh31939522018-08-16 13:28:21 +0100224 return false;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100225 }
Jonathan Harte533a422015-10-20 17:31:24 -0700226
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100227 // Get the uplink port
228 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
229 if (uplinkPort == null) {
230 log.warn("No uplink port found for OLT device {}", port.deviceId());
Amit Ghosh31939522018-08-16 13:28:21 +0100231 return false;
Jonathan Harte533a422015-10-20 17:31:24 -0700232 }
233
Matt Jeanneret3f579262018-06-14 17:16:23 -0400234 if (enableDhcpOnProvisioning) {
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700235 processDhcpFilteringObjectives(port.deviceId(), port.port(), true,
236 true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100237 }
Saurav Das82b8e6d2018-10-04 15:25:12 -0700238 log.info("Programming vlans for subscriber: {}", sub);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100239 Optional<VlanId> defaultVlan = Optional.empty();
Saurav Das82b8e6d2018-10-04 15:25:12 -0700240 provisionVlans(port.deviceId(), uplinkPort.number(), port.port(),
241 sub.cTag(), sub.sTag(), defaultVlan);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100242
Matt Jeanneret3f579262018-06-14 17:16:23 -0400243 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100244 processIgmpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100245 }
Saurav Das82b8e6d2018-10-04 15:25:12 -0700246 // cache subscriber info
247 programmedSubs.put(port, sub);
Amit Ghosh31939522018-08-16 13:28:21 +0100248 return true;
alshabibb7a9e172016-01-13 11:23:53 -0800249 }
250
251 @Override
Amit Ghosh31939522018-08-16 13:28:21 +0100252 public boolean removeSubscriber(ConnectPoint port) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100253 // Get the subscriber connected to this port from Sadis
254 SubscriberAndDeviceInformation subscriber = getSubscriber(port);
255 if (subscriber == null) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700256 log.warn("Subscriber on port {} not found in sadis .. checking "
257 + "local cache", port);
258 subscriber = programmedSubs.get(port);
259 if (subscriber == null) {
260 log.warn("Subscriber on port {} was not previously programmed", port);
261 return false;
262 }
alshabibbf23a1f2016-01-14 17:27:11 -0800263 }
264
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100265 // Get the uplink port
266 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
267 if (uplinkPort == null) {
268 log.warn("No uplink port found for OLT device {}", port.deviceId());
Amit Ghosh31939522018-08-16 13:28:21 +0100269 return false;
alshabib4ceaed32016-03-03 18:00:58 -0800270 }
271
Matt Jeanneret3f579262018-06-14 17:16:23 -0400272 if (enableDhcpOnProvisioning) {
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700273 processDhcpFilteringObjectives(port.deviceId(), port.port(), false,
274 true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100275 }
276
Saurav Das82b8e6d2018-10-04 15:25:12 -0700277 log.info("Removing programmed vlans for subscriber: {}", subscriber);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100278 Optional<VlanId> defaultVlan = Optional.empty();
Saurav Das82b8e6d2018-10-04 15:25:12 -0700279 unprovisionSubscriber(port.deviceId(), uplinkPort.number(), port.port(),
280 subscriber.cTag(), subscriber.sTag(), defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800281
Matt Jeanneret3f579262018-06-14 17:16:23 -0400282 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100283 processIgmpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100284 }
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100285
286 // Remove if there are any flows for the additional Vlans
287 Collection<? extends Map.Entry<VlanId, VlanId>> vlansList = additionalVlans.get(port).value();
288
289 // Remove the flows for the additional vlans for this subscriber
290 for (Map.Entry<VlanId, VlanId> vlans : vlansList) {
291 unprovisionTransparentFlows(port.deviceId(), uplinkPort.number(), port.port(),
292 vlans.getValue(), vlans.getKey());
293
294 // Remove it from the map also
295 additionalVlans.remove(port, vlans);
296 }
297
Saurav Das82b8e6d2018-10-04 15:25:12 -0700298 programmedSubs.remove(port);
Amit Ghosh31939522018-08-16 13:28:21 +0100299 return true;
alshabibbf23a1f2016-01-14 17:27:11 -0800300 }
301
Amit Ghosh31939522018-08-16 13:28:21 +0100302 @Override
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100303 public boolean provisionSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag, Optional<VlanId> cTag) {
Amit Ghosh31939522018-08-16 13:28:21 +0100304 // Check if we can find the connect point to which this subscriber is connected
305 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
306 if (subsPort == null) {
307 log.warn("ConnectPoint for {} not found", subscriberId);
308 return false;
309 }
310
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100311 if (!sTag.isPresent() && !cTag.isPresent()) {
312 return provisionSubscriber(subsPort);
313 } else if (sTag.isPresent() && cTag.isPresent()) {
314 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
315 if (uplinkPort == null) {
316 log.warn("No uplink port found for OLT device {}", subsPort.deviceId());
317 return false;
318 }
319
320 provisionTransparentFlows(subsPort.deviceId(), uplinkPort.number(), subsPort.port(),
321 cTag.get(), sTag.get());
322 return true;
323 } else {
324 log.warn("Provisioning failed for subscriber: {}", subscriberId);
325 return false;
326 }
Amit Ghosh31939522018-08-16 13:28:21 +0100327 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100328
alshabibe0559672016-02-21 14:49:51 -0800329 @Override
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100330 public boolean removeSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag, Optional<VlanId> cTag) {
Amit Ghosh31939522018-08-16 13:28:21 +0100331 // Check if we can find the connect point to which this subscriber is connected
332 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
333 if (subsPort == null) {
334 log.warn("ConnectPoint for {} not found", subscriberId);
335 return false;
336 }
337
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100338 if (!sTag.isPresent() && !cTag.isPresent()) {
339 return removeSubscriber(subsPort);
340 } else if (sTag.isPresent() && cTag.isPresent()) {
341 // Get the uplink port
342 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
343 if (uplinkPort == null) {
344 log.warn("No uplink port found for OLT device {}", subsPort.deviceId());
345 return false;
346 }
347
348 unprovisionTransparentFlows(subsPort.deviceId(), uplinkPort.number(), subsPort.port(),
349 cTag.get(), sTag.get());
350 return true;
351 } else {
352 log.warn("Removing subscriber failed for: {}", subscriberId);
353 return false;
354 }
Amit Ghosh31939522018-08-16 13:28:21 +0100355 }
356
357 @Override
358 public Collection<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> getSubscribers() {
359 ArrayList<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> subs = new ArrayList<>();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100360
Saurav Das82b8e6d2018-10-04 15:25:12 -0700361 // Get the subscribers for all the devices configured in sadis
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100362 // If the port is UNI, is enabled and exists in Sadis then copy it
363 for (Device d : deviceService.getDevices()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700364 if (getOltInfo(d) == null) {
365 continue; // not an olt, or not configured in sadis
366 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100367 for (Port p: deviceService.getPorts(d.id())) {
368 if (isUniPort(d, p) && p.isEnabled()) {
369 ConnectPoint cp = new ConnectPoint(d.id(), p.number());
370
371 SubscriberAndDeviceInformation sub = getSubscriber(cp);
372 if (sub != null) {
Amit Ghosh31939522018-08-16 13:28:21 +0100373 Map.Entry<VlanId, VlanId> vlans = new AbstractMap.SimpleEntry(sub.sTag(), sub.cTag());
374 subs.add(new AbstractMap.SimpleEntry(cp, vlans));
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100375 }
376 }
377 }
378 }
379
380 return subs;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800381 }
382
383 @Override
Saurav Das82b8e6d2018-10-04 15:25:12 -0700384 public ImmutableMap<ConnectPoint, SubscriberAndDeviceInformation> getProgSubs() {
385 return ImmutableMap.copyOf(programmedSubs);
386 }
387
388 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100389 public List<DeviceId> fetchOlts() {
390 // look through all the devices and find the ones that are OLTs as per Sadis
391 List<DeviceId> olts = new ArrayList<>();
392 Iterable<Device> devices = deviceService.getDevices();
393 for (Device d : devices) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700394 if (getOltInfo(d) != null) {
395 // So this is indeed an OLT device
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100396 olts.add(d.id());
397 }
398 }
399 return olts;
alshabibe0559672016-02-21 14:49:51 -0800400 }
401
Amit Ghosh31939522018-08-16 13:28:21 +0100402 /**
403 * Finds the connect point to which a subscriber is connected.
404 *
405 * @param id The id of the subscriber, this is the same ID as in Sadis
406 * @return Subscribers ConnectPoint if found else null
407 */
408 private ConnectPoint findSubscriberConnectPoint(String id) {
409
410 Iterable<Device> devices = deviceService.getDevices();
411 for (Device d : devices) {
412 for (Port p : deviceService.getPorts(d.id())) {
413 log.trace("Comparing {} with {}", p.annotations().value(AnnotationKeys.PORT_NAME), id);
414 if (p.annotations().value(AnnotationKeys.PORT_NAME).equals(id)) {
415 log.debug("Found on device {} port {}", d.id(), p.number());
416 return new ConnectPoint(d.id(), p.number());
417 }
418 }
419 }
420 return null;
421 }
422
alshabibbf23a1f2016-01-14 17:27:11 -0800423 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700424 PortNumber subscriberPort, VlanId cVlan,
425 VlanId sVlan, Optional<VlanId> defaultVlan) {
alshabibbf23a1f2016-01-14 17:27:11 -0800426
427 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
428 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
429
alshabib4ceaed32016-03-03 18:00:58 -0800430 ForwardingObjective.Builder upFwd = upBuilder(uplink, subscriberPort,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700431 cVlan, sVlan,
alshabib4ceaed32016-03-03 18:00:58 -0800432 defaultVlan);
433 ForwardingObjective.Builder downFwd = downBuilder(uplink, subscriberPort,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700434 cVlan, sVlan,
alshabib4ceaed32016-03-03 18:00:58 -0800435 defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800436
alshabib4ceaed32016-03-03 18:00:58 -0800437 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
438 @Override
439 public void onSuccess(Objective objective) {
440 upFuture.complete(null);
441 }
alshabibbf23a1f2016-01-14 17:27:11 -0800442
alshabib4ceaed32016-03-03 18:00:58 -0800443 @Override
444 public void onError(Objective objective, ObjectiveError error) {
445 upFuture.complete(error);
446 }
447 }));
448
449 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
450 @Override
451 public void onSuccess(Objective objective) {
452 downFuture.complete(null);
453 }
454
455 @Override
456 public void onError(Objective objective, ObjectiveError error) {
457 downFuture.complete(error);
458 }
459 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800460
461 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
462 if (upStatus == null && downStatus == null) {
463 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
464 deviceId,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700465 sVlan,
466 cVlan));
alshabibbf23a1f2016-01-14 17:27:11 -0800467 } else if (downStatus != null) {
468 log.error("Subscriber with vlan {} on device {} " +
469 "on port {} failed downstream uninstallation: {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700470 cVlan, deviceId, subscriberPort, downStatus);
alshabibbf23a1f2016-01-14 17:27:11 -0800471 } else if (upStatus != null) {
472 log.error("Subscriber with vlan {} on device {} " +
473 "on port {} failed upstream uninstallation: {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700474 cVlan, deviceId, subscriberPort, upStatus);
alshabibbf23a1f2016-01-14 17:27:11 -0800475 }
476 }, oltInstallers);
alshabibb7a9e172016-01-13 11:23:53 -0800477
Jonathan Harte533a422015-10-20 17:31:24 -0700478 }
479
480 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
481 PortNumber subscriberPort,
Jonathan Hart52998382015-11-10 16:09:22 -0800482 VlanId subscriberVlan, VlanId deviceVlan,
483 Optional<VlanId> defaultVlan) {
Jonathan Harte533a422015-10-20 17:31:24 -0700484
alshabib3ea82642016-01-12 18:06:53 -0800485 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
486 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
487
alshabib4ceaed32016-03-03 18:00:58 -0800488 ForwardingObjective.Builder upFwd = upBuilder(uplinkPort, subscriberPort,
489 subscriberVlan, deviceVlan,
490 defaultVlan);
Jonathan Harte533a422015-10-20 17:31:24 -0700491
492
alshabib4ceaed32016-03-03 18:00:58 -0800493 ForwardingObjective.Builder downFwd = downBuilder(uplinkPort, subscriberPort,
494 subscriberVlan, deviceVlan,
495 defaultVlan);
alshabib3ea82642016-01-12 18:06:53 -0800496
alshabibbf23a1f2016-01-14 17:27:11 -0800497 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
498 @Override
499 public void onSuccess(Objective objective) {
500 upFuture.complete(null);
501 }
502
503 @Override
504 public void onError(Objective objective, ObjectiveError error) {
505 upFuture.complete(error);
506 }
507 }));
508
alshabibbf23a1f2016-01-14 17:27:11 -0800509 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
510 @Override
511 public void onSuccess(Objective objective) {
512 downFuture.complete(null);
513 }
514
515 @Override
516 public void onError(Objective objective, ObjectiveError error) {
517 downFuture.complete(error);
518 }
519 }));
alshabib3ea82642016-01-12 18:06:53 -0800520
521 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
522 if (upStatus == null && downStatus == null) {
523 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
524 deviceId,
525 deviceVlan,
526 subscriberVlan));
alshabib50d9fc52016-02-12 15:47:20 -0800527
alshabib3ea82642016-01-12 18:06:53 -0800528 } else if (downStatus != null) {
529 log.error("Subscriber with vlan {} on device {} " +
530 "on port {} failed downstream installation: {}",
531 subscriberVlan, deviceId, subscriberPort, downStatus);
532 } else if (upStatus != null) {
533 log.error("Subscriber with vlan {} on device {} " +
534 "on port {} failed upstream installation: {}",
535 subscriberVlan, deviceId, subscriberPort, upStatus);
536 }
537 }, oltInstallers);
538
Jonathan Harte533a422015-10-20 17:31:24 -0700539 }
540
alshabib4ceaed32016-03-03 18:00:58 -0800541 private ForwardingObjective.Builder downBuilder(PortNumber uplinkPort,
542 PortNumber subscriberPort,
543 VlanId subscriberVlan,
544 VlanId deviceVlan,
545 Optional<VlanId> defaultVlan) {
546 TrafficSelector downstream = DefaultTrafficSelector.builder()
547 .matchVlanId(deviceVlan)
548 .matchInPort(uplinkPort)
549 .matchInnerVlanId(subscriberVlan)
550 .build();
551
552 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
553 .popVlan()
554 .setVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
555 .setOutput(subscriberPort)
556 .build();
557
558 return DefaultForwardingObjective.builder()
559 .withFlag(ForwardingObjective.Flag.VERSATILE)
560 .withPriority(1000)
561 .makePermanent()
562 .withSelector(downstream)
563 .fromApp(appId)
564 .withTreatment(downstreamTreatment);
565 }
566
567 private ForwardingObjective.Builder upBuilder(PortNumber uplinkPort,
568 PortNumber subscriberPort,
569 VlanId subscriberVlan,
570 VlanId deviceVlan,
571 Optional<VlanId> defaultVlan) {
572 TrafficSelector upstream = DefaultTrafficSelector.builder()
573 .matchVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
574 .matchInPort(subscriberPort)
575 .build();
576
577
578 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
579 .pushVlan()
580 .setVlanId(subscriberVlan)
581 .pushVlan()
582 .setVlanId(deviceVlan)
583 .setOutput(uplinkPort)
584 .build();
585
586 return DefaultForwardingObjective.builder()
587 .withFlag(ForwardingObjective.Flag.VERSATILE)
588 .withPriority(1000)
589 .makePermanent()
590 .withSelector(upstream)
591 .fromApp(appId)
592 .withTreatment(upstreamTreatment);
593 }
594
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100595 private void provisionTransparentFlows(DeviceId deviceId, PortNumber uplinkPort,
596 PortNumber subscriberPort,
597 VlanId innerVlan,
598 VlanId outerVlan) {
599
600 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
601 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
602
603 ForwardingObjective.Builder upFwd = transparentUpBuilder(uplinkPort, subscriberPort,
604 innerVlan, outerVlan);
605
606
607 ForwardingObjective.Builder downFwd = transparentDownBuilder(uplinkPort, subscriberPort,
608 innerVlan, outerVlan);
609
610 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
611
612 additionalVlans.put(cp, new AbstractMap.SimpleEntry(outerVlan, innerVlan));
613
614 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
615 @Override
616 public void onSuccess(Objective objective) {
617 upFuture.complete(null);
618 }
619
620 @Override
621 public void onError(Objective objective, ObjectiveError error) {
622 upFuture.complete(error);
623 }
624 }));
625
626 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
627 @Override
628 public void onSuccess(Objective objective) {
629 downFuture.complete(null);
630 }
631
632 @Override
633 public void onError(Objective objective, ObjectiveError error) {
634 downFuture.complete(error);
635 }
636 }));
637
638 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
639 if (downStatus != null) {
640 log.error("Flow with innervlan {} and outerVlan {} on device {} " +
641 "on port {} failed downstream installation: {}",
642 innerVlan, outerVlan, deviceId, subscriberPort, downStatus);
643 } else if (upStatus != null) {
644 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
645 "on port {} failed upstream installation: {}",
646 innerVlan, outerVlan, deviceId, subscriberPort, upStatus);
647 }
648 }, oltInstallers);
649
650 }
651
652 private ForwardingObjective.Builder transparentDownBuilder(PortNumber uplinkPort,
653 PortNumber subscriberPort,
654 VlanId innerVlan,
655 VlanId outerVlan) {
656 TrafficSelector downstream = DefaultTrafficSelector.builder()
657 .matchVlanId(outerVlan)
658 .matchInPort(uplinkPort)
659 .matchInnerVlanId(innerVlan)
660 .build();
661
662 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
663 .setOutput(subscriberPort)
664 .build();
665
666 return DefaultForwardingObjective.builder()
667 .withFlag(ForwardingObjective.Flag.VERSATILE)
668 .withPriority(1000)
669 .makePermanent()
670 .withSelector(downstream)
671 .fromApp(appId)
672 .withTreatment(downstreamTreatment);
673 }
674
675 private ForwardingObjective.Builder transparentUpBuilder(PortNumber uplinkPort,
676 PortNumber subscriberPort,
677 VlanId innerVlan,
678 VlanId outerVlan) {
679 TrafficSelector upstream = DefaultTrafficSelector.builder()
680 .matchVlanId(outerVlan)
681 .matchInPort(subscriberPort)
682 .matchInnerVlanId(innerVlan)
683 .build();
684
685 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
686 .setOutput(uplinkPort)
687 .build();
688
689 return DefaultForwardingObjective.builder()
690 .withFlag(ForwardingObjective.Flag.VERSATILE)
691 .withPriority(1000)
692 .makePermanent()
693 .withSelector(upstream)
694 .fromApp(appId)
695 .withTreatment(upstreamTreatment);
696 }
697
698 private void unprovisionTransparentFlows(DeviceId deviceId, PortNumber uplink,
699 PortNumber subscriberPort, VlanId innerVlan,
700 VlanId outerVlan) {
701
702 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
703
704 additionalVlans.remove(cp, new AbstractMap.SimpleEntry(outerVlan, innerVlan));
705
706 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
707 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
708
709 ForwardingObjective.Builder upFwd = transparentUpBuilder(uplink, subscriberPort,
710 innerVlan, outerVlan);
711 ForwardingObjective.Builder downFwd = transparentDownBuilder(uplink, subscriberPort,
712 innerVlan, outerVlan);
713
714
715 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
716 @Override
717 public void onSuccess(Objective objective) {
718 upFuture.complete(null);
719 }
720
721 @Override
722 public void onError(Objective objective, ObjectiveError error) {
723 upFuture.complete(error);
724 }
725 }));
726
727 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
728 @Override
729 public void onSuccess(Objective objective) {
730 downFuture.complete(null);
731 }
732
733 @Override
734 public void onError(Objective objective, ObjectiveError error) {
735 downFuture.complete(error);
736 }
737 }));
738
739 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
740 if (downStatus != null) {
741 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
742 "on port {} failed downstream uninstallation: {}",
743 innerVlan, outerVlan, deviceId, subscriberPort, downStatus);
744 } else if (upStatus != null) {
745 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
746 "on port {} failed upstream uninstallation: {}",
747 innerVlan, outerVlan, deviceId, subscriberPort, upStatus);
748 }
749 }, oltInstallers);
750
751 }
752
Saurav Das82b8e6d2018-10-04 15:25:12 -0700753 private void processEapolFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
alshabib09753b52016-03-04 14:55:19 -0800754 if (!mastershipService.isLocalMaster(devId)) {
755 return;
756 }
alshabibbb83aa22016-02-10 15:08:23 -0800757 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
758
759 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
alshabib50d9fc52016-02-12 15:47:20 -0800760 .withKey(Criteria.matchInPort(port))
alshabibdec2e252016-01-15 12:20:25 -0800761 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
762 .withMeta(DefaultTrafficTreatment.builder()
763 .setOutput(PortNumber.CONTROLLER).build())
764 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400765 .withPriority(10000)
alshabibdec2e252016-01-15 12:20:25 -0800766 .add(new ObjectiveContext() {
767 @Override
768 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700769 log.info("Eapol filter for {} on {} {}.",
770 devId, port, (install) ? "installed" : "removed");
alshabibdec2e252016-01-15 12:20:25 -0800771 }
772
773 @Override
774 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700775 log.info("Eapol filter for {} on {} failed {} because {}",
776 devId, port, (install) ? "installation" : "removal",
777 error);
alshabibdec2e252016-01-15 12:20:25 -0800778 }
779 });
780
alshabibdec2e252016-01-15 12:20:25 -0800781 flowObjectiveService.filter(devId, eapol);
alshabib000b6fc2016-02-01 17:25:00 -0800782
alshabibdec2e252016-01-15 12:20:25 -0800783 }
784
Jonathan Hart403372d2018-08-22 11:44:13 -0700785 /**
786 * Installs trap filtering objectives for particular traffic types on an
787 * NNI port.
788 *
789 * @param devId device ID
790 * @param port port number
791 * @param install true to install, false to remove
792 */
793 private void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
794 processLldpFilteringObjective(devId, port, install);
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700795 processDhcpFilteringObjectives(devId, port, install, false);
Jonathan Hart403372d2018-08-22 11:44:13 -0700796 }
797
798 private void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
799 if (!mastershipService.isLocalMaster(devId)) {
800 return;
801 }
802 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
803
804 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
805 .withKey(Criteria.matchInPort(port))
806 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
807 .withMeta(DefaultTrafficTreatment.builder()
808 .setOutput(PortNumber.CONTROLLER).build())
809 .fromApp(appId)
810 .withPriority(10000)
811 .add(new ObjectiveContext() {
812 @Override
813 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700814 log.info("LLDP filter for {} on {} {}.",
815 devId, port, (install) ? "installed" : "removed");
Jonathan Hart403372d2018-08-22 11:44:13 -0700816 }
817
818 @Override
819 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700820 log.info("LLDP filter for {} on {} failed {} because {}",
821 devId, port, (install) ? "installation" : "removal",
822 error);
Jonathan Hart403372d2018-08-22 11:44:13 -0700823 }
824 });
825
826 flowObjectiveService.filter(devId, lldp);
827
828 }
829
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700830 /**
831 * Trap dhcp packets to the controller.
832 *
833 * @param devId the device identifier
834 * @param port the port for which this trap flow is designated
835 * @param install true to install the flow, false to remove the flow
836 * @param upstream true if trapped packets are flowing upstream towards
837 * server, false if packets are flowing dowstream towards client
838 */
839 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port,
840 boolean install,
841 boolean upstream) {
Amit Ghosh95e2f652017-08-23 12:49:46 +0100842 if (!mastershipService.isLocalMaster(devId)) {
843 return;
844 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100845
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700846 int udpSrc = (upstream) ? 68 : 67;
847 int udpDst = (upstream) ? 67 : 68;
848
849 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
Amit Ghosh95e2f652017-08-23 12:49:46 +0100850 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
851 .withKey(Criteria.matchInPort(port))
852 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
853 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700854 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
855 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Amit Ghosh95e2f652017-08-23 12:49:46 +0100856 .withMeta(DefaultTrafficTreatment.builder()
857 .setOutput(PortNumber.CONTROLLER).build())
858 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400859 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100860 .add(new ObjectiveContext() {
861 @Override
862 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700863 log.info("DHCP filter for {} on {} {}.",
864 devId, port, (install) ? "installed" : "removed");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100865 }
866
867 @Override
868 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700869 log.info("DHCP filter for {} on {} failed {} because {}",
870 devId, port, (install) ? "installation" : "removal",
871 error);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100872 }
873 });
874
875 flowObjectiveService.filter(devId, dhcpUpstream);
876 }
877
878 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
879 if (!mastershipService.isLocalMaster(devId)) {
880 return;
881 }
882
883 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
884
885 builder = install ? builder.permit() : builder.deny();
886
887 FilteringObjective igmp = builder
888 .withKey(Criteria.matchInPort(port))
889 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
890 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
891 .withMeta(DefaultTrafficTreatment.builder()
892 .setOutput(PortNumber.CONTROLLER).build())
893 .fromApp(appId)
894 .withPriority(10000)
895 .add(new ObjectiveContext() {
896 @Override
897 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700898 log.info("Igmp filter for {} on {} {}.",
899 devId, port, (install) ? "installed" : "removed");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100900 }
901
902 @Override
903 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700904 log.info("Igmp filter for {} on {} failed {} because {}.",
905 devId, port, (install) ? "installation" : "removal",
906 error);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100907 }
908 });
909
910 flowObjectiveService.filter(devId, igmp);
911 }
912
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100913 /**
Jonathan Hart403372d2018-08-22 11:44:13 -0700914 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
915 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100916 *
917 * @param dev Device to look for
918 */
Jonathan Hart403372d2018-08-22 11:44:13 -0700919 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100920 // we create only for the ones we are master of
921 if (!mastershipService.isLocalMaster(dev.id())) {
922 return;
923 }
924 // check if this device is provisioned in Sadis
925 String devSerialNo = dev.serialNumber();
926 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
Jonathan Hart403372d2018-08-22 11:44:13 -0700927 log.debug("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100928
929 if (deviceInfo != null) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700930 // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100931 for (Port p : deviceService.getPorts(dev.id())) {
932 if (isUniPort(dev, p)) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700933 processEapolFilteringObjectives(dev.id(), p.number(), true);
Jonathan Hart403372d2018-08-22 11:44:13 -0700934 } else {
935 processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100936 }
937 }
938 }
939 }
940
Jonathan Hart403372d2018-08-22 11:44:13 -0700941
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100942 /**
943 * Get the uplink for of the OLT device.
944 *
945 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
946 * this logic needs to be changed
947 *
948 * @param dev Device to look for
949 * @return The uplink Port of the OLT
950 */
951 private Port getUplinkPort(Device dev) {
952 // check if this device is provisioned in Sadis
953 String devSerialNo = dev.serialNumber();
954 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
955 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
Saurav Das82b8e6d2018-10-04 15:25:12 -0700956 if (deviceInfo == null) {
957 log.warn("Device {} is not configured in SADIS .. cannot fetch device"
958 + " info", dev.id());
959 return null;
960 }
961 // Return the port that has been configured as the uplink port of this OLT in Sadis
962 for (Port p: deviceService.getPorts(dev.id())) {
963 if (p.number().toLong() == deviceInfo.uplinkPort()) {
964 log.debug("getUplinkPort: Found port {}", p);
965 return p;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100966 }
967 }
968
Saurav Das82b8e6d2018-10-04 15:25:12 -0700969 log.debug("getUplinkPort: No uplink port found for OLT {}", dev.id());
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100970 return null;
971 }
972
973 /**
974 * Return the subscriber on a port.
975 *
976 * @param port On which to find the subscriber
977 * @return subscriber if found else null
978 */
979 private SubscriberAndDeviceInformation getSubscriber(ConnectPoint port) {
980 String portName = deviceService.getPort(port).annotations()
981 .value(AnnotationKeys.PORT_NAME);
982
983 return subsService.get(portName);
984 }
985
986 private boolean isUniPort(Device d, Port p) {
987 Port ulPort = getUplinkPort(d);
988 if (ulPort != null) {
989 return (ulPort.number().toLong() != p.number().toLong());
990 }
991 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700992 }
993
Jonathan Hart4c538002018-08-23 10:11:54 -0700994 private SubscriberAndDeviceInformation getOltInfo(Device dev) {
995 String devSerialNo = dev.serialNumber();
996 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
997 return deviceInfo;
998 }
999
alshabibf0e7e702015-05-30 18:22:36 -07001000 private class InternalDeviceListener implements DeviceListener {
1001 @Override
1002 public void event(DeviceEvent event) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001003 eventExecutor.execute(() -> {
1004 DeviceId devId = event.subject().id();
1005 Device dev = event.subject();
Jonathan Hart4c538002018-08-23 10:11:54 -07001006
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001007 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
1008 return;
1009 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001010
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001011 if (getOltInfo(dev) == null) {
1012 log.debug("No device info found, this is not an OLT");
1013 return;
1014 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001015
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001016 log.debug("OLT got {} event for {}", event.type(), event.subject());
Jonathan Hart4c538002018-08-23 10:11:54 -07001017
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001018 switch (event.type()) {
1019 //TODO: Port handling and bookkeeping should be improved once
1020 // olt firmware handles correct behaviour.
1021 case PORT_ADDED:
1022 if (isUniPort(dev, event.port())) {
1023 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
Jonathan Hart4c538002018-08-23 10:11:54 -07001024
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001025 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001026 processEapolFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001027 }
1028 } else {
1029 checkAndCreateDeviceFlows(dev);
1030 }
1031 break;
1032 case PORT_REMOVED:
1033 if (isUniPort(dev, event.port())) {
1034 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001035 processEapolFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001036 removeSubscriber(new ConnectPoint(devId, event.port().number()));
1037 }
1038
1039 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
1040 }
1041
1042 break;
1043 case PORT_UPDATED:
1044 if (!isUniPort(dev, event.port())) {
1045 break;
1046 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001047
1048 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001049 processEapolFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001050 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
1051 } else {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001052 processEapolFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001053 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001054 }
alshabibbb83aa22016-02-10 15:08:23 -08001055 break;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001056 case DEVICE_ADDED:
alshabib7c190012016-02-09 18:22:33 -08001057 post(new AccessDeviceEvent(
1058 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
1059 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001060
1061 // Send UNI_ADDED events for all existing ports
1062 deviceService.getPorts(devId).stream()
1063 .filter(p -> isUniPort(dev, p))
1064 .filter(Port::isEnabled)
1065 .forEach(p -> post(new AccessDeviceEvent(
1066 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
1067
Jonathan Hart403372d2018-08-22 11:44:13 -07001068 checkAndCreateDeviceFlows(dev);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001069 break;
1070 case DEVICE_REMOVED:
1071 deviceService.getPorts(devId).stream()
1072 .filter(p -> isUniPort(dev, p))
1073 .forEach(p -> post(new AccessDeviceEvent(
1074 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
1075
alshabib7c190012016-02-09 18:22:33 -08001076 post(new AccessDeviceEvent(
1077 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
1078 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001079 break;
1080 case DEVICE_AVAILABILITY_CHANGED:
1081 if (deviceService.isAvailable(devId)) {
1082 post(new AccessDeviceEvent(
1083 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
1084 null, null));
1085 checkAndCreateDeviceFlows(dev);
1086 } else {
1087 post(new AccessDeviceEvent(
1088 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
1089 null, null));
1090 }
1091 break;
1092 case DEVICE_UPDATED:
1093 case DEVICE_SUSPENDED:
1094 case PORT_STATS_UPDATED:
1095 default:
1096 return;
1097 }
1098 });
alshabibf0e7e702015-05-30 18:22:36 -07001099 }
1100 }
alshabibf0e7e702015-05-30 18:22:36 -07001101}