blob: f1236c994b2bf4181ff4d149c589083e19bd17eb [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;
Matteo Scandolo63460d12018-11-02 16:19:04 -070048import org.onlab.packet.IPv6;
Amit Ghosh95e2f652017-08-23 12:49:46 +010049import org.onlab.packet.TpPort;
alshabibf0e7e702015-05-30 18:22:36 -070050import org.onlab.packet.VlanId;
Amit Ghosh95e2f652017-08-23 12:49:46 +010051import org.onlab.util.Tools;
alshabibe0559672016-02-21 14:49:51 -080052import org.onosproject.cfg.ComponentConfigService;
alshabibf0e7e702015-05-30 18:22:36 -070053import org.onosproject.core.ApplicationId;
54import org.onosproject.core.CoreService;
alshabib8e4fd2f2016-01-12 15:55:53 -080055import org.onosproject.event.AbstractListenerManager;
alshabib09753b52016-03-04 14:55:19 -080056import org.onosproject.mastership.MastershipService;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010057import org.onosproject.net.AnnotationKeys;
Jonathan Harte533a422015-10-20 17:31:24 -070058import org.onosproject.net.ConnectPoint;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010059import org.onosproject.net.Device;
alshabibf0e7e702015-05-30 18:22:36 -070060import org.onosproject.net.DeviceId;
alshabibdec2e252016-01-15 12:20:25 -080061import org.onosproject.net.Port;
alshabibf0e7e702015-05-30 18:22:36 -070062import org.onosproject.net.PortNumber;
63import org.onosproject.net.device.DeviceEvent;
64import org.onosproject.net.device.DeviceListener;
65import org.onosproject.net.device.DeviceService;
66import org.onosproject.net.flow.DefaultTrafficSelector;
67import org.onosproject.net.flow.DefaultTrafficTreatment;
68import org.onosproject.net.flow.TrafficSelector;
69import org.onosproject.net.flow.TrafficTreatment;
alshabibdec2e252016-01-15 12:20:25 -080070import org.onosproject.net.flow.criteria.Criteria;
71import org.onosproject.net.flowobjective.DefaultFilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070072import org.onosproject.net.flowobjective.DefaultForwardingObjective;
alshabibdec2e252016-01-15 12:20:25 -080073import org.onosproject.net.flowobjective.FilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070074import org.onosproject.net.flowobjective.FlowObjectiveService;
75import org.onosproject.net.flowobjective.ForwardingObjective;
alshabib3ea82642016-01-12 18:06:53 -080076import org.onosproject.net.flowobjective.Objective;
77import org.onosproject.net.flowobjective.ObjectiveContext;
78import org.onosproject.net.flowobjective.ObjectiveError;
Amit Ghoshe1d3f092018-10-09 19:44:33 +010079import org.onosproject.store.serializers.KryoNamespaces;
80import org.onosproject.store.service.ConsistentMultimap;
81import org.onosproject.store.service.Serializer;
82import org.onosproject.store.service.StorageService;
alshabib36a4d732016-06-01 16:03:59 -070083import org.opencord.olt.AccessDeviceEvent;
84import org.opencord.olt.AccessDeviceListener;
85import org.opencord.olt.AccessDeviceService;
Amit Ghosh31939522018-08-16 13:28:21 +010086import org.opencord.olt.AccessSubscriberId;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010087import org.opencord.sadis.SubscriberAndDeviceInformation;
88import org.opencord.sadis.SubscriberAndDeviceInformationService;
alshabibe0559672016-02-21 14:49:51 -080089import org.osgi.service.component.ComponentContext;
alshabibf0e7e702015-05-30 18:22:36 -070090import org.slf4j.Logger;
91
Amit Ghoshe1d3f092018-10-09 19:44:33 +010092
Saurav Das82b8e6d2018-10-04 15:25:12 -070093import com.google.common.collect.ImmutableMap;
94import com.google.common.collect.Maps;
alshabibf0e7e702015-05-30 18:22:36 -070095
96/**
Jonathan Harte533a422015-10-20 17:31:24 -070097 * Provisions rules on access devices.
alshabibf0e7e702015-05-30 18:22:36 -070098 */
Jonathan Harte533a422015-10-20 17:31:24 -070099@Service
alshabibf0e7e702015-05-30 18:22:36 -0700100@Component(immediate = true)
alshabib8e4fd2f2016-01-12 15:55:53 -0800101public class Olt
102 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
103 implements AccessDeviceService {
Charles Chan54f110f2017-01-20 11:22:42 -0800104 private static final String APP_NAME = "org.opencord.olt";
alshabibe0559672016-02-21 14:49:51 -0800105
106 private static final short DEFAULT_VLAN = 0;
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100107 private static final String ADDITIONAL_VLANS = "additional-vlans";
alshabibe0559672016-02-21 14:49:51 -0800108
alshabibf0e7e702015-05-30 18:22:36 -0700109 private final Logger log = getLogger(getClass());
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected FlowObjectiveService flowObjectiveService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib09753b52016-03-04 14:55:19 -0800115 protected MastershipService mastershipService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibf0e7e702015-05-30 18:22:36 -0700118 protected DeviceService deviceService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected CoreService coreService;
122
Jonathan Harte533a422015-10-20 17:31:24 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe0559672016-02-21 14:49:51 -0800124 protected ComponentConfigService componentConfigService;
125
alshabib4ceaed32016-03-03 18:00:58 -0800126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100127 protected SubscriberAndDeviceInformationService subsService;
alshabibe0559672016-02-21 14:49:51 -0800128
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected StorageService storageService;
131
alshabibe0559672016-02-21 14:49:51 -0800132 @Property(name = "defaultVlan", intValue = DEFAULT_VLAN,
133 label = "Default VLAN RG<->ONU traffic")
134 private int defaultVlan = DEFAULT_VLAN;
135
Matt Jeanneret3f579262018-06-14 17:16:23 -0400136 @Property(name = "enableDhcpOnProvisioning", boolValue = true,
137 label = "Create the DHCP Flow rules when a subscriber is provisioned")
138 protected boolean enableDhcpOnProvisioning = false;
139
Matteo Scandolo63460d12018-11-02 16:19:04 -0700140 @Property(name = "enableDhcpV4", boolValue = true,
141 label = "Enable flows for DHCP v4")
142 protected boolean enableDhcpV4 = true;
143
144 @Property(name = "enableDhcpV6", boolValue = true,
145 label = "Enable flows for DHCP v6")
146 protected boolean enableDhcpV6 = false;
147
Matt Jeanneret3f579262018-06-14 17:16:23 -0400148 @Property(name = "enableIgmpOnProvisioning", boolValue = false,
149 label = "Create IGMP Flow rules when a subscriber is provisioned")
150 protected boolean enableIgmpOnProvisioning = false;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100151
alshabibf0e7e702015-05-30 18:22:36 -0700152 private final DeviceListener deviceListener = new InternalDeviceListener();
153
154 private ApplicationId appId;
155
Saurav Das82b8e6d2018-10-04 15:25:12 -0700156 private ExecutorService oltInstallers = Executors
157 .newFixedThreadPool(4, groupedThreads("onos/olt-service",
158 "olt-installer-%d"));
alshabibf0e7e702015-05-30 18:22:36 -0700159
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100160 private ConsistentMultimap<ConnectPoint, Map.Entry<VlanId, VlanId>> additionalVlans;
161
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700162 protected ExecutorService eventExecutor;
163
Saurav Das82b8e6d2018-10-04 15:25:12 -0700164 private Map<ConnectPoint, SubscriberAndDeviceInformation> programmedSubs;
165
alshabibf0e7e702015-05-30 18:22:36 -0700166 @Activate
alshabibe0559672016-02-21 14:49:51 -0800167 public void activate(ComponentContext context) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700168 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/olt", "events-%d", log));
alshabibe0559672016-02-21 14:49:51 -0800169 modified(context);
Charles Chan54f110f2017-01-20 11:22:42 -0800170 appId = coreService.registerApplication(APP_NAME);
alshabibe0559672016-02-21 14:49:51 -0800171 componentConfigService.registerProperties(getClass());
Saurav Das82b8e6d2018-10-04 15:25:12 -0700172 programmedSubs = Maps.newConcurrentMap();
alshabibc4dfe852015-06-05 13:35:13 -0700173
alshabib8e4fd2f2016-01-12 15:55:53 -0800174 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
175
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100176 // look for all provisioned devices in Sadis and create EAPOL flows for the
177 // UNI ports
178 Iterable<Device> devices = deviceService.getDevices();
179 for (Device d : devices) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700180 checkAndCreateDeviceFlows(d);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100181 }
alshabib4ceaed32016-03-03 18:00:58 -0800182
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100183 additionalVlans = storageService.<ConnectPoint, Map.Entry<VlanId, VlanId>>consistentMultimapBuilder()
184 .withName(ADDITIONAL_VLANS)
185 .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
186 AbstractMap.SimpleEntry.class))
187 .build();
188
alshabibba357492016-01-27 13:49:46 -0800189 deviceService.addListener(deviceListener);
190
alshabibf0e7e702015-05-30 18:22:36 -0700191 log.info("Started with Application ID {}", appId.id());
192 }
193
194 @Deactivate
195 public void deactivate() {
alshabibe0559672016-02-21 14:49:51 -0800196 componentConfigService.unregisterProperties(getClass(), false);
alshabib62e9ce72016-02-11 17:31:36 -0800197 deviceService.removeListener(deviceListener);
Jonathan Hart5f1c8142018-07-24 17:31:59 -0700198 eventDispatcher.removeSink(AccessDeviceEvent.class);
alshabibf0e7e702015-05-30 18:22:36 -0700199 log.info("Stopped");
200 }
201
alshabibe0559672016-02-21 14:49:51 -0800202 @Modified
203 public void modified(ComponentContext context) {
204 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
205
206 try {
207 String s = get(properties, "defaultVlan");
208 defaultVlan = isNullOrEmpty(s) ? DEFAULT_VLAN : Integer.parseInt(s.trim());
Amit Ghosh95e2f652017-08-23 12:49:46 +0100209
Matt Jeanneret3f579262018-06-14 17:16:23 -0400210 Boolean o = Tools.isPropertyEnabled(properties, "enableDhcpOnProvisioning");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100211 if (o != null) {
Matt Jeanneret3f579262018-06-14 17:16:23 -0400212 enableDhcpOnProvisioning = o;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100213 }
Matt Jeanneret3f579262018-06-14 17:16:23 -0400214
Matteo Scandolo63460d12018-11-02 16:19:04 -0700215 Boolean v4 = Tools.isPropertyEnabled(properties, "enableDhcpV4");
216 if (v4 != null) {
217 enableDhcpV4 = v4;
218 }
219
220 Boolean v6 = Tools.isPropertyEnabled(properties, "enableDhcpV6");
221 if (v6 != null) {
222 enableDhcpV6 = v6;
223 }
224
Matt Jeanneret3f579262018-06-14 17:16:23 -0400225 Boolean p = Tools.isPropertyEnabled(properties, "enableIgmpOnProvisioning");
226 if (p != null) {
227 enableIgmpOnProvisioning = p;
228 }
229
Matteo Scandolo63460d12018-11-02 16:19:04 -0700230 log.info("DHCP Settings [enableDhcpOnProvisioning: {}, enableDhcpV4: {}, enableDhcpV6: {}]",
231 enableDhcpOnProvisioning, enableDhcpV4, enableDhcpV6);
232
alshabibe0559672016-02-21 14:49:51 -0800233 } catch (Exception e) {
234 defaultVlan = DEFAULT_VLAN;
235 }
236 }
237
alshabib32232c82016-02-25 17:57:24 -0500238 @Override
Amit Ghosh31939522018-08-16 13:28:21 +0100239 public boolean provisionSubscriber(ConnectPoint port) {
Jonathan Hart94b90492018-04-24 14:02:25 -0700240 checkNotNull(deviceService.getPort(port.deviceId(), port.port()),
241 "Invalid connect point");
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100242 // Find the subscriber on this connect point
243 SubscriberAndDeviceInformation sub = getSubscriber(port);
244 if (sub == null) {
245 log.warn("No subscriber found for {}", port);
Amit Ghosh31939522018-08-16 13:28:21 +0100246 return false;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100247 }
Jonathan Harte533a422015-10-20 17:31:24 -0700248
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100249 // Get the uplink port
250 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
251 if (uplinkPort == null) {
252 log.warn("No uplink port found for OLT device {}", port.deviceId());
Amit Ghosh31939522018-08-16 13:28:21 +0100253 return false;
Jonathan Harte533a422015-10-20 17:31:24 -0700254 }
255
Matt Jeanneret3f579262018-06-14 17:16:23 -0400256 if (enableDhcpOnProvisioning) {
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700257 processDhcpFilteringObjectives(port.deviceId(), port.port(), true,
258 true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100259 }
Saurav Das82b8e6d2018-10-04 15:25:12 -0700260 log.info("Programming vlans for subscriber: {}", sub);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100261 Optional<VlanId> defaultVlan = Optional.empty();
Saurav Das82b8e6d2018-10-04 15:25:12 -0700262 provisionVlans(port.deviceId(), uplinkPort.number(), port.port(),
263 sub.cTag(), sub.sTag(), defaultVlan);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100264
Matt Jeanneret3f579262018-06-14 17:16:23 -0400265 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100266 processIgmpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100267 }
Saurav Das82b8e6d2018-10-04 15:25:12 -0700268 // cache subscriber info
269 programmedSubs.put(port, sub);
Amit Ghosh31939522018-08-16 13:28:21 +0100270 return true;
alshabibb7a9e172016-01-13 11:23:53 -0800271 }
272
273 @Override
Amit Ghosh31939522018-08-16 13:28:21 +0100274 public boolean removeSubscriber(ConnectPoint port) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100275 // Get the subscriber connected to this port from Sadis
276 SubscriberAndDeviceInformation subscriber = getSubscriber(port);
277 if (subscriber == null) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700278 log.warn("Subscriber on port {} not found in sadis .. checking "
279 + "local cache", port);
280 subscriber = programmedSubs.get(port);
281 if (subscriber == null) {
282 log.warn("Subscriber on port {} was not previously programmed", port);
283 return false;
284 }
alshabibbf23a1f2016-01-14 17:27:11 -0800285 }
286
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100287 // Get the uplink port
288 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
289 if (uplinkPort == null) {
290 log.warn("No uplink port found for OLT device {}", port.deviceId());
Amit Ghosh31939522018-08-16 13:28:21 +0100291 return false;
alshabib4ceaed32016-03-03 18:00:58 -0800292 }
293
Matt Jeanneret3f579262018-06-14 17:16:23 -0400294 if (enableDhcpOnProvisioning) {
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700295 processDhcpFilteringObjectives(port.deviceId(), port.port(), false,
296 true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100297 }
298
Saurav Das82b8e6d2018-10-04 15:25:12 -0700299 log.info("Removing programmed vlans for subscriber: {}", subscriber);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100300 Optional<VlanId> defaultVlan = Optional.empty();
Saurav Das82b8e6d2018-10-04 15:25:12 -0700301 unprovisionSubscriber(port.deviceId(), uplinkPort.number(), port.port(),
302 subscriber.cTag(), subscriber.sTag(), defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800303
Matt Jeanneret3f579262018-06-14 17:16:23 -0400304 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100305 processIgmpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100306 }
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100307
308 // Remove if there are any flows for the additional Vlans
309 Collection<? extends Map.Entry<VlanId, VlanId>> vlansList = additionalVlans.get(port).value();
310
311 // Remove the flows for the additional vlans for this subscriber
312 for (Map.Entry<VlanId, VlanId> vlans : vlansList) {
313 unprovisionTransparentFlows(port.deviceId(), uplinkPort.number(), port.port(),
314 vlans.getValue(), vlans.getKey());
315
316 // Remove it from the map also
317 additionalVlans.remove(port, vlans);
318 }
319
Saurav Das82b8e6d2018-10-04 15:25:12 -0700320 programmedSubs.remove(port);
Amit Ghosh31939522018-08-16 13:28:21 +0100321 return true;
alshabibbf23a1f2016-01-14 17:27:11 -0800322 }
323
Amit Ghosh31939522018-08-16 13:28:21 +0100324 @Override
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100325 public boolean provisionSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag, Optional<VlanId> cTag) {
Amit Ghosh31939522018-08-16 13:28:21 +0100326 // Check if we can find the connect point to which this subscriber is connected
327 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
328 if (subsPort == null) {
329 log.warn("ConnectPoint for {} not found", subscriberId);
330 return false;
331 }
332
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100333 if (!sTag.isPresent() && !cTag.isPresent()) {
334 return provisionSubscriber(subsPort);
335 } else if (sTag.isPresent() && cTag.isPresent()) {
336 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
337 if (uplinkPort == null) {
338 log.warn("No uplink port found for OLT device {}", subsPort.deviceId());
339 return false;
340 }
341
342 provisionTransparentFlows(subsPort.deviceId(), uplinkPort.number(), subsPort.port(),
343 cTag.get(), sTag.get());
344 return true;
345 } else {
346 log.warn("Provisioning failed for subscriber: {}", subscriberId);
347 return false;
348 }
Amit Ghosh31939522018-08-16 13:28:21 +0100349 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100350
alshabibe0559672016-02-21 14:49:51 -0800351 @Override
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100352 public boolean removeSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag, Optional<VlanId> cTag) {
Amit Ghosh31939522018-08-16 13:28:21 +0100353 // Check if we can find the connect point to which this subscriber is connected
354 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
355 if (subsPort == null) {
356 log.warn("ConnectPoint for {} not found", subscriberId);
357 return false;
358 }
359
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100360 if (!sTag.isPresent() && !cTag.isPresent()) {
361 return removeSubscriber(subsPort);
362 } else if (sTag.isPresent() && cTag.isPresent()) {
363 // Get the uplink port
364 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
365 if (uplinkPort == null) {
366 log.warn("No uplink port found for OLT device {}", subsPort.deviceId());
367 return false;
368 }
369
370 unprovisionTransparentFlows(subsPort.deviceId(), uplinkPort.number(), subsPort.port(),
371 cTag.get(), sTag.get());
372 return true;
373 } else {
374 log.warn("Removing subscriber failed for: {}", subscriberId);
375 return false;
376 }
Amit Ghosh31939522018-08-16 13:28:21 +0100377 }
378
379 @Override
380 public Collection<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> getSubscribers() {
381 ArrayList<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> subs = new ArrayList<>();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100382
Saurav Das82b8e6d2018-10-04 15:25:12 -0700383 // Get the subscribers for all the devices configured in sadis
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100384 // If the port is UNI, is enabled and exists in Sadis then copy it
385 for (Device d : deviceService.getDevices()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700386 if (getOltInfo(d) == null) {
387 continue; // not an olt, or not configured in sadis
388 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100389 for (Port p: deviceService.getPorts(d.id())) {
390 if (isUniPort(d, p) && p.isEnabled()) {
391 ConnectPoint cp = new ConnectPoint(d.id(), p.number());
392
393 SubscriberAndDeviceInformation sub = getSubscriber(cp);
394 if (sub != null) {
Amit Ghosh31939522018-08-16 13:28:21 +0100395 Map.Entry<VlanId, VlanId> vlans = new AbstractMap.SimpleEntry(sub.sTag(), sub.cTag());
396 subs.add(new AbstractMap.SimpleEntry(cp, vlans));
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100397 }
398 }
399 }
400 }
401
402 return subs;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800403 }
404
405 @Override
Saurav Das82b8e6d2018-10-04 15:25:12 -0700406 public ImmutableMap<ConnectPoint, SubscriberAndDeviceInformation> getProgSubs() {
407 return ImmutableMap.copyOf(programmedSubs);
408 }
409
410 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100411 public List<DeviceId> fetchOlts() {
412 // look through all the devices and find the ones that are OLTs as per Sadis
413 List<DeviceId> olts = new ArrayList<>();
414 Iterable<Device> devices = deviceService.getDevices();
415 for (Device d : devices) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700416 if (getOltInfo(d) != null) {
417 // So this is indeed an OLT device
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100418 olts.add(d.id());
419 }
420 }
421 return olts;
alshabibe0559672016-02-21 14:49:51 -0800422 }
423
Amit Ghosh31939522018-08-16 13:28:21 +0100424 /**
425 * Finds the connect point to which a subscriber is connected.
426 *
427 * @param id The id of the subscriber, this is the same ID as in Sadis
428 * @return Subscribers ConnectPoint if found else null
429 */
430 private ConnectPoint findSubscriberConnectPoint(String id) {
431
432 Iterable<Device> devices = deviceService.getDevices();
433 for (Device d : devices) {
434 for (Port p : deviceService.getPorts(d.id())) {
435 log.trace("Comparing {} with {}", p.annotations().value(AnnotationKeys.PORT_NAME), id);
436 if (p.annotations().value(AnnotationKeys.PORT_NAME).equals(id)) {
437 log.debug("Found on device {} port {}", d.id(), p.number());
438 return new ConnectPoint(d.id(), p.number());
439 }
440 }
441 }
442 return null;
443 }
444
alshabibbf23a1f2016-01-14 17:27:11 -0800445 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700446 PortNumber subscriberPort, VlanId cVlan,
447 VlanId sVlan, Optional<VlanId> defaultVlan) {
alshabibbf23a1f2016-01-14 17:27:11 -0800448
449 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
450 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
451
alshabib4ceaed32016-03-03 18:00:58 -0800452 ForwardingObjective.Builder upFwd = upBuilder(uplink, subscriberPort,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700453 cVlan, sVlan,
alshabib4ceaed32016-03-03 18:00:58 -0800454 defaultVlan);
455 ForwardingObjective.Builder downFwd = downBuilder(uplink, subscriberPort,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700456 cVlan, sVlan,
alshabib4ceaed32016-03-03 18:00:58 -0800457 defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800458
alshabib4ceaed32016-03-03 18:00:58 -0800459 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
460 @Override
461 public void onSuccess(Objective objective) {
462 upFuture.complete(null);
463 }
alshabibbf23a1f2016-01-14 17:27:11 -0800464
alshabib4ceaed32016-03-03 18:00:58 -0800465 @Override
466 public void onError(Objective objective, ObjectiveError error) {
467 upFuture.complete(error);
468 }
469 }));
470
471 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
472 @Override
473 public void onSuccess(Objective objective) {
474 downFuture.complete(null);
475 }
476
477 @Override
478 public void onError(Objective objective, ObjectiveError error) {
479 downFuture.complete(error);
480 }
481 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800482
483 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
484 if (upStatus == null && downStatus == null) {
485 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
486 deviceId,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700487 sVlan,
488 cVlan));
alshabibbf23a1f2016-01-14 17:27:11 -0800489 } else if (downStatus != null) {
490 log.error("Subscriber with vlan {} on device {} " +
491 "on port {} failed downstream uninstallation: {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700492 cVlan, deviceId, subscriberPort, downStatus);
alshabibbf23a1f2016-01-14 17:27:11 -0800493 } else if (upStatus != null) {
494 log.error("Subscriber with vlan {} on device {} " +
495 "on port {} failed upstream uninstallation: {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700496 cVlan, deviceId, subscriberPort, upStatus);
alshabibbf23a1f2016-01-14 17:27:11 -0800497 }
498 }, oltInstallers);
alshabibb7a9e172016-01-13 11:23:53 -0800499
Jonathan Harte533a422015-10-20 17:31:24 -0700500 }
501
502 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
503 PortNumber subscriberPort,
Jonathan Hart52998382015-11-10 16:09:22 -0800504 VlanId subscriberVlan, VlanId deviceVlan,
505 Optional<VlanId> defaultVlan) {
Jonathan Harte533a422015-10-20 17:31:24 -0700506
alshabib3ea82642016-01-12 18:06:53 -0800507 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
508 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
509
alshabib4ceaed32016-03-03 18:00:58 -0800510 ForwardingObjective.Builder upFwd = upBuilder(uplinkPort, subscriberPort,
511 subscriberVlan, deviceVlan,
512 defaultVlan);
Jonathan Harte533a422015-10-20 17:31:24 -0700513
514
alshabib4ceaed32016-03-03 18:00:58 -0800515 ForwardingObjective.Builder downFwd = downBuilder(uplinkPort, subscriberPort,
516 subscriberVlan, deviceVlan,
517 defaultVlan);
alshabib3ea82642016-01-12 18:06:53 -0800518
alshabibbf23a1f2016-01-14 17:27:11 -0800519 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
520 @Override
521 public void onSuccess(Objective objective) {
522 upFuture.complete(null);
523 }
524
525 @Override
526 public void onError(Objective objective, ObjectiveError error) {
527 upFuture.complete(error);
528 }
529 }));
530
alshabibbf23a1f2016-01-14 17:27:11 -0800531 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
532 @Override
533 public void onSuccess(Objective objective) {
534 downFuture.complete(null);
535 }
536
537 @Override
538 public void onError(Objective objective, ObjectiveError error) {
539 downFuture.complete(error);
540 }
541 }));
alshabib3ea82642016-01-12 18:06:53 -0800542
543 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
544 if (upStatus == null && downStatus == null) {
545 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
546 deviceId,
547 deviceVlan,
548 subscriberVlan));
alshabib50d9fc52016-02-12 15:47:20 -0800549
alshabib3ea82642016-01-12 18:06:53 -0800550 } else if (downStatus != null) {
551 log.error("Subscriber with vlan {} on device {} " +
552 "on port {} failed downstream installation: {}",
553 subscriberVlan, deviceId, subscriberPort, downStatus);
554 } else if (upStatus != null) {
555 log.error("Subscriber with vlan {} on device {} " +
556 "on port {} failed upstream installation: {}",
557 subscriberVlan, deviceId, subscriberPort, upStatus);
558 }
559 }, oltInstallers);
560
Jonathan Harte533a422015-10-20 17:31:24 -0700561 }
562
alshabib4ceaed32016-03-03 18:00:58 -0800563 private ForwardingObjective.Builder downBuilder(PortNumber uplinkPort,
564 PortNumber subscriberPort,
565 VlanId subscriberVlan,
566 VlanId deviceVlan,
567 Optional<VlanId> defaultVlan) {
568 TrafficSelector downstream = DefaultTrafficSelector.builder()
569 .matchVlanId(deviceVlan)
570 .matchInPort(uplinkPort)
571 .matchInnerVlanId(subscriberVlan)
572 .build();
573
574 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
575 .popVlan()
576 .setVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
577 .setOutput(subscriberPort)
578 .build();
579
580 return DefaultForwardingObjective.builder()
581 .withFlag(ForwardingObjective.Flag.VERSATILE)
582 .withPriority(1000)
583 .makePermanent()
584 .withSelector(downstream)
585 .fromApp(appId)
586 .withTreatment(downstreamTreatment);
587 }
588
589 private ForwardingObjective.Builder upBuilder(PortNumber uplinkPort,
590 PortNumber subscriberPort,
591 VlanId subscriberVlan,
592 VlanId deviceVlan,
593 Optional<VlanId> defaultVlan) {
594 TrafficSelector upstream = DefaultTrafficSelector.builder()
595 .matchVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
596 .matchInPort(subscriberPort)
597 .build();
598
599
600 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
601 .pushVlan()
602 .setVlanId(subscriberVlan)
603 .pushVlan()
604 .setVlanId(deviceVlan)
605 .setOutput(uplinkPort)
606 .build();
607
608 return DefaultForwardingObjective.builder()
609 .withFlag(ForwardingObjective.Flag.VERSATILE)
610 .withPriority(1000)
611 .makePermanent()
612 .withSelector(upstream)
613 .fromApp(appId)
614 .withTreatment(upstreamTreatment);
615 }
616
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100617 private void provisionTransparentFlows(DeviceId deviceId, PortNumber uplinkPort,
618 PortNumber subscriberPort,
619 VlanId innerVlan,
620 VlanId outerVlan) {
621
622 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
623 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
624
625 ForwardingObjective.Builder upFwd = transparentUpBuilder(uplinkPort, subscriberPort,
626 innerVlan, outerVlan);
627
628
629 ForwardingObjective.Builder downFwd = transparentDownBuilder(uplinkPort, subscriberPort,
630 innerVlan, outerVlan);
631
632 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
633
634 additionalVlans.put(cp, new AbstractMap.SimpleEntry(outerVlan, innerVlan));
635
636 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
637 @Override
638 public void onSuccess(Objective objective) {
639 upFuture.complete(null);
640 }
641
642 @Override
643 public void onError(Objective objective, ObjectiveError error) {
644 upFuture.complete(error);
645 }
646 }));
647
648 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
649 @Override
650 public void onSuccess(Objective objective) {
651 downFuture.complete(null);
652 }
653
654 @Override
655 public void onError(Objective objective, ObjectiveError error) {
656 downFuture.complete(error);
657 }
658 }));
659
660 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
661 if (downStatus != null) {
662 log.error("Flow with innervlan {} and outerVlan {} on device {} " +
663 "on port {} failed downstream installation: {}",
664 innerVlan, outerVlan, deviceId, subscriberPort, downStatus);
665 } else if (upStatus != null) {
666 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
667 "on port {} failed upstream installation: {}",
668 innerVlan, outerVlan, deviceId, subscriberPort, upStatus);
669 }
670 }, oltInstallers);
671
672 }
673
674 private ForwardingObjective.Builder transparentDownBuilder(PortNumber uplinkPort,
675 PortNumber subscriberPort,
676 VlanId innerVlan,
677 VlanId outerVlan) {
678 TrafficSelector downstream = DefaultTrafficSelector.builder()
679 .matchVlanId(outerVlan)
680 .matchInPort(uplinkPort)
681 .matchInnerVlanId(innerVlan)
682 .build();
683
684 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
685 .setOutput(subscriberPort)
686 .build();
687
688 return DefaultForwardingObjective.builder()
689 .withFlag(ForwardingObjective.Flag.VERSATILE)
690 .withPriority(1000)
691 .makePermanent()
692 .withSelector(downstream)
693 .fromApp(appId)
694 .withTreatment(downstreamTreatment);
695 }
696
697 private ForwardingObjective.Builder transparentUpBuilder(PortNumber uplinkPort,
698 PortNumber subscriberPort,
699 VlanId innerVlan,
700 VlanId outerVlan) {
701 TrafficSelector upstream = DefaultTrafficSelector.builder()
702 .matchVlanId(outerVlan)
703 .matchInPort(subscriberPort)
704 .matchInnerVlanId(innerVlan)
705 .build();
706
707 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
708 .setOutput(uplinkPort)
709 .build();
710
711 return DefaultForwardingObjective.builder()
712 .withFlag(ForwardingObjective.Flag.VERSATILE)
713 .withPriority(1000)
714 .makePermanent()
715 .withSelector(upstream)
716 .fromApp(appId)
717 .withTreatment(upstreamTreatment);
718 }
719
720 private void unprovisionTransparentFlows(DeviceId deviceId, PortNumber uplink,
721 PortNumber subscriberPort, VlanId innerVlan,
722 VlanId outerVlan) {
723
724 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
725
726 additionalVlans.remove(cp, new AbstractMap.SimpleEntry(outerVlan, innerVlan));
727
728 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
729 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
730
731 ForwardingObjective.Builder upFwd = transparentUpBuilder(uplink, subscriberPort,
732 innerVlan, outerVlan);
733 ForwardingObjective.Builder downFwd = transparentDownBuilder(uplink, subscriberPort,
734 innerVlan, outerVlan);
735
736
737 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
738 @Override
739 public void onSuccess(Objective objective) {
740 upFuture.complete(null);
741 }
742
743 @Override
744 public void onError(Objective objective, ObjectiveError error) {
745 upFuture.complete(error);
746 }
747 }));
748
749 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
750 @Override
751 public void onSuccess(Objective objective) {
752 downFuture.complete(null);
753 }
754
755 @Override
756 public void onError(Objective objective, ObjectiveError error) {
757 downFuture.complete(error);
758 }
759 }));
760
761 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
762 if (downStatus != null) {
763 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
764 "on port {} failed downstream uninstallation: {}",
765 innerVlan, outerVlan, deviceId, subscriberPort, downStatus);
766 } else if (upStatus != null) {
767 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
768 "on port {} failed upstream uninstallation: {}",
769 innerVlan, outerVlan, deviceId, subscriberPort, upStatus);
770 }
771 }, oltInstallers);
772
773 }
774
Saurav Das82b8e6d2018-10-04 15:25:12 -0700775 private void processEapolFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
alshabib09753b52016-03-04 14:55:19 -0800776 if (!mastershipService.isLocalMaster(devId)) {
777 return;
778 }
alshabibbb83aa22016-02-10 15:08:23 -0800779 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
780
781 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
alshabib50d9fc52016-02-12 15:47:20 -0800782 .withKey(Criteria.matchInPort(port))
alshabibdec2e252016-01-15 12:20:25 -0800783 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
784 .withMeta(DefaultTrafficTreatment.builder()
785 .setOutput(PortNumber.CONTROLLER).build())
786 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400787 .withPriority(10000)
alshabibdec2e252016-01-15 12:20:25 -0800788 .add(new ObjectiveContext() {
789 @Override
790 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700791 log.info("Eapol filter for {} on {} {}.",
792 devId, port, (install) ? "installed" : "removed");
alshabibdec2e252016-01-15 12:20:25 -0800793 }
794
795 @Override
796 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700797 log.info("Eapol filter for {} on {} failed {} because {}",
798 devId, port, (install) ? "installation" : "removal",
799 error);
alshabibdec2e252016-01-15 12:20:25 -0800800 }
801 });
802
alshabibdec2e252016-01-15 12:20:25 -0800803 flowObjectiveService.filter(devId, eapol);
alshabib000b6fc2016-02-01 17:25:00 -0800804
alshabibdec2e252016-01-15 12:20:25 -0800805 }
806
Jonathan Hart403372d2018-08-22 11:44:13 -0700807 /**
808 * Installs trap filtering objectives for particular traffic types on an
809 * NNI port.
810 *
811 * @param devId device ID
812 * @param port port number
813 * @param install true to install, false to remove
814 */
815 private void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
816 processLldpFilteringObjective(devId, port, install);
Matteo Scandolo63460d12018-11-02 16:19:04 -0700817 if (enableDhcpOnProvisioning) {
818 processDhcpFilteringObjectives(devId, port, install, false);
819 }
Jonathan Hart403372d2018-08-22 11:44:13 -0700820 }
821
822 private void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
823 if (!mastershipService.isLocalMaster(devId)) {
824 return;
825 }
826 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
827
828 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
829 .withKey(Criteria.matchInPort(port))
830 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
831 .withMeta(DefaultTrafficTreatment.builder()
832 .setOutput(PortNumber.CONTROLLER).build())
833 .fromApp(appId)
834 .withPriority(10000)
835 .add(new ObjectiveContext() {
836 @Override
837 public void onSuccess(Objective objective) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700838 log.info("LLDP filter for device {} on port {} {}.",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700839 devId, port, (install) ? "installed" : "removed");
Jonathan Hart403372d2018-08-22 11:44:13 -0700840 }
841
842 @Override
843 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700844 log.info("LLDP filter for device {} on port {} failed {} because {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700845 devId, port, (install) ? "installation" : "removal",
846 error);
Jonathan Hart403372d2018-08-22 11:44:13 -0700847 }
848 });
849
850 flowObjectiveService.filter(devId, lldp);
851
852 }
853
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700854 /**
855 * Trap dhcp packets to the controller.
856 *
857 * @param devId the device identifier
858 * @param port the port for which this trap flow is designated
859 * @param install true to install the flow, false to remove the flow
860 * @param upstream true if trapped packets are flowing upstream towards
861 * server, false if packets are flowing dowstream towards client
862 */
863 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port,
864 boolean install,
865 boolean upstream) {
Amit Ghosh95e2f652017-08-23 12:49:46 +0100866 if (!mastershipService.isLocalMaster(devId)) {
867 return;
868 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100869
Matteo Scandolo63460d12018-11-02 16:19:04 -0700870 if (enableDhcpV4) {
871 int udpSrc = (upstream) ? 68 : 67;
872 int udpDst = (upstream) ? 67 : 68;
873
874 EthType ethType = EthType.EtherType.IPV4.ethType();
875 byte protocol = IPv4.PROTOCOL_UDP;
876
877 this.addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType, protocol, install);
878 }
879
880 if (enableDhcpV6) {
881 int udpSrc = (upstream) ? 547 : 546;
882 int udpDst = (upstream) ? 546 : 547;
883
884 EthType ethType = EthType.EtherType.IPV6.ethType();
885 byte protocol = IPv6.PROTOCOL_UDP;
886
887 this.addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType, protocol, install);
888 }
889
890 }
891
892 private void addDhcpFilteringObjectives(DeviceId devId,
893 PortNumber port,
894 int udpSrc,
895 int udpDst,
896 EthType ethType,
897 byte protocol,
898 boolean install) {
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700899
900 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
Amit Ghosh95e2f652017-08-23 12:49:46 +0100901 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
902 .withKey(Criteria.matchInPort(port))
Matteo Scandolo63460d12018-11-02 16:19:04 -0700903 .addCondition(Criteria.matchEthType(ethType))
904 .addCondition(Criteria.matchIPProtocol(protocol))
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700905 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
906 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Amit Ghosh95e2f652017-08-23 12:49:46 +0100907 .withMeta(DefaultTrafficTreatment.builder()
908 .setOutput(PortNumber.CONTROLLER).build())
909 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400910 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100911 .add(new ObjectiveContext() {
912 @Override
913 public void onSuccess(Objective objective) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700914 log.info("DHCP {} filter for device {} on port {} {}.",
915 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? "v4" : "v6",
916 devId, port, (install) ? "installed" : "removed");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100917 }
918
919 @Override
920 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700921 log.info("DHCP {} filter for device {} on port {} failed {} because {}",
922 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? "v4" : "v6",
923 devId, port, (install) ? "installation" : "removal",
924 error);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100925 }
926 });
927
928 flowObjectiveService.filter(devId, dhcpUpstream);
929 }
930
931 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
932 if (!mastershipService.isLocalMaster(devId)) {
933 return;
934 }
935
936 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
937
938 builder = install ? builder.permit() : builder.deny();
939
940 FilteringObjective igmp = builder
941 .withKey(Criteria.matchInPort(port))
942 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
943 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
944 .withMeta(DefaultTrafficTreatment.builder()
945 .setOutput(PortNumber.CONTROLLER).build())
946 .fromApp(appId)
947 .withPriority(10000)
948 .add(new ObjectiveContext() {
949 @Override
950 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700951 log.info("Igmp filter for {} on {} {}.",
952 devId, port, (install) ? "installed" : "removed");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100953 }
954
955 @Override
956 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700957 log.info("Igmp filter for {} on {} failed {} because {}.",
958 devId, port, (install) ? "installation" : "removal",
959 error);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100960 }
961 });
962
963 flowObjectiveService.filter(devId, igmp);
964 }
965
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100966 /**
Jonathan Hart403372d2018-08-22 11:44:13 -0700967 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
968 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100969 *
970 * @param dev Device to look for
971 */
Jonathan Hart403372d2018-08-22 11:44:13 -0700972 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100973 // we create only for the ones we are master of
974 if (!mastershipService.isLocalMaster(dev.id())) {
975 return;
976 }
977 // check if this device is provisioned in Sadis
978 String devSerialNo = dev.serialNumber();
979 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
Jonathan Hart403372d2018-08-22 11:44:13 -0700980 log.debug("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100981
982 if (deviceInfo != null) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700983 // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100984 for (Port p : deviceService.getPorts(dev.id())) {
985 if (isUniPort(dev, p)) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700986 processEapolFilteringObjectives(dev.id(), p.number(), true);
Jonathan Hart403372d2018-08-22 11:44:13 -0700987 } else {
988 processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100989 }
990 }
991 }
992 }
993
Jonathan Hart403372d2018-08-22 11:44:13 -0700994
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100995 /**
996 * Get the uplink for of the OLT device.
997 *
998 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
999 * this logic needs to be changed
1000 *
1001 * @param dev Device to look for
1002 * @return The uplink Port of the OLT
1003 */
1004 private Port getUplinkPort(Device dev) {
1005 // check if this device is provisioned in Sadis
1006 String devSerialNo = dev.serialNumber();
1007 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
1008 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
Saurav Das82b8e6d2018-10-04 15:25:12 -07001009 if (deviceInfo == null) {
1010 log.warn("Device {} is not configured in SADIS .. cannot fetch device"
1011 + " info", dev.id());
1012 return null;
1013 }
1014 // Return the port that has been configured as the uplink port of this OLT in Sadis
1015 for (Port p: deviceService.getPorts(dev.id())) {
1016 if (p.number().toLong() == deviceInfo.uplinkPort()) {
1017 log.debug("getUplinkPort: Found port {}", p);
1018 return p;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001019 }
1020 }
1021
Saurav Das82b8e6d2018-10-04 15:25:12 -07001022 log.debug("getUplinkPort: No uplink port found for OLT {}", dev.id());
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001023 return null;
1024 }
1025
1026 /**
1027 * Return the subscriber on a port.
1028 *
1029 * @param port On which to find the subscriber
1030 * @return subscriber if found else null
1031 */
1032 private SubscriberAndDeviceInformation getSubscriber(ConnectPoint port) {
1033 String portName = deviceService.getPort(port).annotations()
1034 .value(AnnotationKeys.PORT_NAME);
1035
1036 return subsService.get(portName);
1037 }
1038
1039 private boolean isUniPort(Device d, Port p) {
1040 Port ulPort = getUplinkPort(d);
1041 if (ulPort != null) {
1042 return (ulPort.number().toLong() != p.number().toLong());
1043 }
1044 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001045 }
1046
Jonathan Hart4c538002018-08-23 10:11:54 -07001047 private SubscriberAndDeviceInformation getOltInfo(Device dev) {
1048 String devSerialNo = dev.serialNumber();
1049 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
1050 return deviceInfo;
1051 }
1052
alshabibf0e7e702015-05-30 18:22:36 -07001053 private class InternalDeviceListener implements DeviceListener {
1054 @Override
1055 public void event(DeviceEvent event) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001056 eventExecutor.execute(() -> {
1057 DeviceId devId = event.subject().id();
1058 Device dev = event.subject();
Jonathan Hart4c538002018-08-23 10:11:54 -07001059
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001060 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
1061 return;
1062 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001063
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001064 if (getOltInfo(dev) == null) {
1065 log.debug("No device info found, this is not an OLT");
1066 return;
1067 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001068
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001069 log.debug("OLT got {} event for {}", event.type(), event.subject());
Jonathan Hart4c538002018-08-23 10:11:54 -07001070
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001071 switch (event.type()) {
1072 //TODO: Port handling and bookkeeping should be improved once
1073 // olt firmware handles correct behaviour.
1074 case PORT_ADDED:
1075 if (isUniPort(dev, event.port())) {
1076 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
Jonathan Hart4c538002018-08-23 10:11:54 -07001077
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001078 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001079 processEapolFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001080 }
1081 } else {
1082 checkAndCreateDeviceFlows(dev);
1083 }
1084 break;
1085 case PORT_REMOVED:
1086 if (isUniPort(dev, event.port())) {
1087 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001088 processEapolFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001089 removeSubscriber(new ConnectPoint(devId, event.port().number()));
1090 }
1091
1092 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
1093 }
1094
1095 break;
1096 case PORT_UPDATED:
1097 if (!isUniPort(dev, event.port())) {
1098 break;
1099 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001100
1101 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001102 processEapolFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001103 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
1104 } else {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001105 processEapolFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001106 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001107 }
alshabibbb83aa22016-02-10 15:08:23 -08001108 break;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001109 case DEVICE_ADDED:
alshabib7c190012016-02-09 18:22:33 -08001110 post(new AccessDeviceEvent(
1111 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
1112 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001113
1114 // Send UNI_ADDED events for all existing ports
1115 deviceService.getPorts(devId).stream()
1116 .filter(p -> isUniPort(dev, p))
1117 .filter(Port::isEnabled)
1118 .forEach(p -> post(new AccessDeviceEvent(
1119 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
1120
Jonathan Hart403372d2018-08-22 11:44:13 -07001121 checkAndCreateDeviceFlows(dev);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001122 break;
1123 case DEVICE_REMOVED:
1124 deviceService.getPorts(devId).stream()
1125 .filter(p -> isUniPort(dev, p))
1126 .forEach(p -> post(new AccessDeviceEvent(
1127 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
1128
alshabib7c190012016-02-09 18:22:33 -08001129 post(new AccessDeviceEvent(
1130 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
1131 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001132 break;
1133 case DEVICE_AVAILABILITY_CHANGED:
1134 if (deviceService.isAvailable(devId)) {
1135 post(new AccessDeviceEvent(
1136 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
1137 null, null));
1138 checkAndCreateDeviceFlows(dev);
1139 } else {
1140 post(new AccessDeviceEvent(
1141 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
1142 null, null));
1143 }
1144 break;
1145 case DEVICE_UPDATED:
1146 case DEVICE_SUSPENDED:
1147 case PORT_STATS_UPDATED:
1148 default:
1149 return;
1150 }
1151 });
alshabibf0e7e702015-05-30 18:22:36 -07001152 }
1153 }
alshabibf0e7e702015-05-30 18:22:36 -07001154}