blob: e2fdc5585542b490770ba08dc63ad26983632ea2 [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) {
Matteo Scandolo962a6ad2018-12-11 15:39:42 -0800275 // Get the subscriber connected to this port from the local cache
276 // as if we don't know about the subscriber there's no need to remove it
277 SubscriberAndDeviceInformation subscriber = programmedSubs.get(port);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100278 if (subscriber == null) {
Matteo Scandolo962a6ad2018-12-11 15:39:42 -0800279 log.warn("Subscriber on port {} was not previously programmed, no need to remove it", port);
280 return true;
alshabibbf23a1f2016-01-14 17:27:11 -0800281 }
282
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100283 // Get the uplink port
284 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
285 if (uplinkPort == null) {
286 log.warn("No uplink port found for OLT device {}", port.deviceId());
Amit Ghosh31939522018-08-16 13:28:21 +0100287 return false;
alshabib4ceaed32016-03-03 18:00:58 -0800288 }
289
Matt Jeanneret3f579262018-06-14 17:16:23 -0400290 if (enableDhcpOnProvisioning) {
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700291 processDhcpFilteringObjectives(port.deviceId(), port.port(), false,
292 true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100293 }
294
Saurav Das82b8e6d2018-10-04 15:25:12 -0700295 log.info("Removing programmed vlans for subscriber: {}", subscriber);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100296 Optional<VlanId> defaultVlan = Optional.empty();
Saurav Das82b8e6d2018-10-04 15:25:12 -0700297 unprovisionSubscriber(port.deviceId(), uplinkPort.number(), port.port(),
298 subscriber.cTag(), subscriber.sTag(), defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800299
Matt Jeanneret3f579262018-06-14 17:16:23 -0400300 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100301 processIgmpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100302 }
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100303
304 // Remove if there are any flows for the additional Vlans
305 Collection<? extends Map.Entry<VlanId, VlanId>> vlansList = additionalVlans.get(port).value();
306
307 // Remove the flows for the additional vlans for this subscriber
308 for (Map.Entry<VlanId, VlanId> vlans : vlansList) {
309 unprovisionTransparentFlows(port.deviceId(), uplinkPort.number(), port.port(),
310 vlans.getValue(), vlans.getKey());
311
312 // Remove it from the map also
313 additionalVlans.remove(port, vlans);
314 }
315
Saurav Das82b8e6d2018-10-04 15:25:12 -0700316 programmedSubs.remove(port);
Amit Ghosh31939522018-08-16 13:28:21 +0100317 return true;
alshabibbf23a1f2016-01-14 17:27:11 -0800318 }
319
Amit Ghosh31939522018-08-16 13:28:21 +0100320 @Override
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100321 public boolean provisionSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag, Optional<VlanId> cTag) {
Amit Ghosh31939522018-08-16 13:28:21 +0100322 // Check if we can find the connect point to which this subscriber is connected
323 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
324 if (subsPort == null) {
325 log.warn("ConnectPoint for {} not found", subscriberId);
326 return false;
327 }
328
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100329 if (!sTag.isPresent() && !cTag.isPresent()) {
330 return provisionSubscriber(subsPort);
331 } else if (sTag.isPresent() && cTag.isPresent()) {
332 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
333 if (uplinkPort == null) {
334 log.warn("No uplink port found for OLT device {}", subsPort.deviceId());
335 return false;
336 }
337
338 provisionTransparentFlows(subsPort.deviceId(), uplinkPort.number(), subsPort.port(),
339 cTag.get(), sTag.get());
340 return true;
341 } else {
342 log.warn("Provisioning failed for subscriber: {}", subscriberId);
343 return false;
344 }
Amit Ghosh31939522018-08-16 13:28:21 +0100345 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100346
alshabibe0559672016-02-21 14:49:51 -0800347 @Override
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100348 public boolean removeSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag, Optional<VlanId> cTag) {
Amit Ghosh31939522018-08-16 13:28:21 +0100349 // Check if we can find the connect point to which this subscriber is connected
350 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
351 if (subsPort == null) {
352 log.warn("ConnectPoint for {} not found", subscriberId);
353 return false;
354 }
355
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100356 if (!sTag.isPresent() && !cTag.isPresent()) {
357 return removeSubscriber(subsPort);
358 } else if (sTag.isPresent() && cTag.isPresent()) {
359 // Get the uplink port
360 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
361 if (uplinkPort == null) {
362 log.warn("No uplink port found for OLT device {}", subsPort.deviceId());
363 return false;
364 }
365
366 unprovisionTransparentFlows(subsPort.deviceId(), uplinkPort.number(), subsPort.port(),
367 cTag.get(), sTag.get());
368 return true;
369 } else {
370 log.warn("Removing subscriber failed for: {}", subscriberId);
371 return false;
372 }
Amit Ghosh31939522018-08-16 13:28:21 +0100373 }
374
375 @Override
376 public Collection<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> getSubscribers() {
377 ArrayList<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> subs = new ArrayList<>();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100378
Saurav Das82b8e6d2018-10-04 15:25:12 -0700379 // Get the subscribers for all the devices configured in sadis
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100380 // If the port is UNI, is enabled and exists in Sadis then copy it
381 for (Device d : deviceService.getDevices()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700382 if (getOltInfo(d) == null) {
383 continue; // not an olt, or not configured in sadis
384 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100385 for (Port p: deviceService.getPorts(d.id())) {
386 if (isUniPort(d, p) && p.isEnabled()) {
387 ConnectPoint cp = new ConnectPoint(d.id(), p.number());
388
389 SubscriberAndDeviceInformation sub = getSubscriber(cp);
390 if (sub != null) {
Amit Ghosh31939522018-08-16 13:28:21 +0100391 Map.Entry<VlanId, VlanId> vlans = new AbstractMap.SimpleEntry(sub.sTag(), sub.cTag());
392 subs.add(new AbstractMap.SimpleEntry(cp, vlans));
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100393 }
394 }
395 }
396 }
397
398 return subs;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800399 }
400
401 @Override
Saurav Das82b8e6d2018-10-04 15:25:12 -0700402 public ImmutableMap<ConnectPoint, SubscriberAndDeviceInformation> getProgSubs() {
403 return ImmutableMap.copyOf(programmedSubs);
404 }
405
406 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100407 public List<DeviceId> fetchOlts() {
408 // look through all the devices and find the ones that are OLTs as per Sadis
409 List<DeviceId> olts = new ArrayList<>();
410 Iterable<Device> devices = deviceService.getDevices();
411 for (Device d : devices) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700412 if (getOltInfo(d) != null) {
413 // So this is indeed an OLT device
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100414 olts.add(d.id());
415 }
416 }
417 return olts;
alshabibe0559672016-02-21 14:49:51 -0800418 }
419
Amit Ghosh31939522018-08-16 13:28:21 +0100420 /**
421 * Finds the connect point to which a subscriber is connected.
422 *
423 * @param id The id of the subscriber, this is the same ID as in Sadis
424 * @return Subscribers ConnectPoint if found else null
425 */
426 private ConnectPoint findSubscriberConnectPoint(String id) {
427
428 Iterable<Device> devices = deviceService.getDevices();
429 for (Device d : devices) {
430 for (Port p : deviceService.getPorts(d.id())) {
431 log.trace("Comparing {} with {}", p.annotations().value(AnnotationKeys.PORT_NAME), id);
432 if (p.annotations().value(AnnotationKeys.PORT_NAME).equals(id)) {
433 log.debug("Found on device {} port {}", d.id(), p.number());
434 return new ConnectPoint(d.id(), p.number());
435 }
436 }
437 }
438 return null;
439 }
440
alshabibbf23a1f2016-01-14 17:27:11 -0800441 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700442 PortNumber subscriberPort, VlanId cVlan,
443 VlanId sVlan, Optional<VlanId> defaultVlan) {
alshabibbf23a1f2016-01-14 17:27:11 -0800444
445 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
446 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
447
alshabib4ceaed32016-03-03 18:00:58 -0800448 ForwardingObjective.Builder upFwd = upBuilder(uplink, subscriberPort,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700449 cVlan, sVlan,
alshabib4ceaed32016-03-03 18:00:58 -0800450 defaultVlan);
451 ForwardingObjective.Builder downFwd = downBuilder(uplink, subscriberPort,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700452 cVlan, sVlan,
alshabib4ceaed32016-03-03 18:00:58 -0800453 defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800454
alshabib4ceaed32016-03-03 18:00:58 -0800455 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
456 @Override
457 public void onSuccess(Objective objective) {
458 upFuture.complete(null);
459 }
alshabibbf23a1f2016-01-14 17:27:11 -0800460
alshabib4ceaed32016-03-03 18:00:58 -0800461 @Override
462 public void onError(Objective objective, ObjectiveError error) {
463 upFuture.complete(error);
464 }
465 }));
466
467 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
468 @Override
469 public void onSuccess(Objective objective) {
470 downFuture.complete(null);
471 }
472
473 @Override
474 public void onError(Objective objective, ObjectiveError error) {
475 downFuture.complete(error);
476 }
477 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800478
479 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
480 if (upStatus == null && downStatus == null) {
481 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
482 deviceId,
Saurav Das82b8e6d2018-10-04 15:25:12 -0700483 sVlan,
484 cVlan));
alshabibbf23a1f2016-01-14 17:27:11 -0800485 } else if (downStatus != null) {
486 log.error("Subscriber with vlan {} on device {} " +
487 "on port {} failed downstream uninstallation: {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700488 cVlan, deviceId, subscriberPort, downStatus);
alshabibbf23a1f2016-01-14 17:27:11 -0800489 } else if (upStatus != null) {
490 log.error("Subscriber with vlan {} on device {} " +
491 "on port {} failed upstream uninstallation: {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700492 cVlan, deviceId, subscriberPort, upStatus);
alshabibbf23a1f2016-01-14 17:27:11 -0800493 }
494 }, oltInstallers);
alshabibb7a9e172016-01-13 11:23:53 -0800495
Jonathan Harte533a422015-10-20 17:31:24 -0700496 }
497
498 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
499 PortNumber subscriberPort,
Jonathan Hart52998382015-11-10 16:09:22 -0800500 VlanId subscriberVlan, VlanId deviceVlan,
501 Optional<VlanId> defaultVlan) {
Jonathan Harte533a422015-10-20 17:31:24 -0700502
alshabib3ea82642016-01-12 18:06:53 -0800503 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
504 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
505
alshabib4ceaed32016-03-03 18:00:58 -0800506 ForwardingObjective.Builder upFwd = upBuilder(uplinkPort, subscriberPort,
507 subscriberVlan, deviceVlan,
508 defaultVlan);
Jonathan Harte533a422015-10-20 17:31:24 -0700509
510
alshabib4ceaed32016-03-03 18:00:58 -0800511 ForwardingObjective.Builder downFwd = downBuilder(uplinkPort, subscriberPort,
512 subscriberVlan, deviceVlan,
513 defaultVlan);
alshabib3ea82642016-01-12 18:06:53 -0800514
alshabibbf23a1f2016-01-14 17:27:11 -0800515 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
516 @Override
517 public void onSuccess(Objective objective) {
518 upFuture.complete(null);
519 }
520
521 @Override
522 public void onError(Objective objective, ObjectiveError error) {
523 upFuture.complete(error);
524 }
525 }));
526
alshabibbf23a1f2016-01-14 17:27:11 -0800527 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
528 @Override
529 public void onSuccess(Objective objective) {
530 downFuture.complete(null);
531 }
532
533 @Override
534 public void onError(Objective objective, ObjectiveError error) {
535 downFuture.complete(error);
536 }
537 }));
alshabib3ea82642016-01-12 18:06:53 -0800538
539 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
540 if (upStatus == null && downStatus == null) {
541 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
542 deviceId,
543 deviceVlan,
544 subscriberVlan));
alshabib50d9fc52016-02-12 15:47:20 -0800545
alshabib3ea82642016-01-12 18:06:53 -0800546 } else if (downStatus != null) {
547 log.error("Subscriber with vlan {} on device {} " +
548 "on port {} failed downstream installation: {}",
549 subscriberVlan, deviceId, subscriberPort, downStatus);
550 } else if (upStatus != null) {
551 log.error("Subscriber with vlan {} on device {} " +
552 "on port {} failed upstream installation: {}",
553 subscriberVlan, deviceId, subscriberPort, upStatus);
554 }
555 }, oltInstallers);
556
Jonathan Harte533a422015-10-20 17:31:24 -0700557 }
558
alshabib4ceaed32016-03-03 18:00:58 -0800559 private ForwardingObjective.Builder downBuilder(PortNumber uplinkPort,
560 PortNumber subscriberPort,
561 VlanId subscriberVlan,
562 VlanId deviceVlan,
563 Optional<VlanId> defaultVlan) {
564 TrafficSelector downstream = DefaultTrafficSelector.builder()
565 .matchVlanId(deviceVlan)
566 .matchInPort(uplinkPort)
567 .matchInnerVlanId(subscriberVlan)
568 .build();
569
570 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
571 .popVlan()
572 .setVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
573 .setOutput(subscriberPort)
574 .build();
575
576 return DefaultForwardingObjective.builder()
577 .withFlag(ForwardingObjective.Flag.VERSATILE)
578 .withPriority(1000)
579 .makePermanent()
580 .withSelector(downstream)
581 .fromApp(appId)
582 .withTreatment(downstreamTreatment);
583 }
584
585 private ForwardingObjective.Builder upBuilder(PortNumber uplinkPort,
586 PortNumber subscriberPort,
587 VlanId subscriberVlan,
588 VlanId deviceVlan,
589 Optional<VlanId> defaultVlan) {
590 TrafficSelector upstream = DefaultTrafficSelector.builder()
591 .matchVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
592 .matchInPort(subscriberPort)
593 .build();
594
595
596 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
597 .pushVlan()
598 .setVlanId(subscriberVlan)
599 .pushVlan()
600 .setVlanId(deviceVlan)
601 .setOutput(uplinkPort)
602 .build();
603
604 return DefaultForwardingObjective.builder()
605 .withFlag(ForwardingObjective.Flag.VERSATILE)
606 .withPriority(1000)
607 .makePermanent()
608 .withSelector(upstream)
609 .fromApp(appId)
610 .withTreatment(upstreamTreatment);
611 }
612
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100613 private void provisionTransparentFlows(DeviceId deviceId, PortNumber uplinkPort,
614 PortNumber subscriberPort,
615 VlanId innerVlan,
616 VlanId outerVlan) {
617
618 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
619 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
620
621 ForwardingObjective.Builder upFwd = transparentUpBuilder(uplinkPort, subscriberPort,
622 innerVlan, outerVlan);
623
624
625 ForwardingObjective.Builder downFwd = transparentDownBuilder(uplinkPort, subscriberPort,
626 innerVlan, outerVlan);
627
628 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
629
630 additionalVlans.put(cp, new AbstractMap.SimpleEntry(outerVlan, innerVlan));
631
632 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
633 @Override
634 public void onSuccess(Objective objective) {
635 upFuture.complete(null);
636 }
637
638 @Override
639 public void onError(Objective objective, ObjectiveError error) {
640 upFuture.complete(error);
641 }
642 }));
643
644 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
645 @Override
646 public void onSuccess(Objective objective) {
647 downFuture.complete(null);
648 }
649
650 @Override
651 public void onError(Objective objective, ObjectiveError error) {
652 downFuture.complete(error);
653 }
654 }));
655
656 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
657 if (downStatus != null) {
658 log.error("Flow with innervlan {} and outerVlan {} on device {} " +
659 "on port {} failed downstream installation: {}",
660 innerVlan, outerVlan, deviceId, subscriberPort, downStatus);
661 } else if (upStatus != null) {
662 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
663 "on port {} failed upstream installation: {}",
664 innerVlan, outerVlan, deviceId, subscriberPort, upStatus);
665 }
666 }, oltInstallers);
667
668 }
669
670 private ForwardingObjective.Builder transparentDownBuilder(PortNumber uplinkPort,
671 PortNumber subscriberPort,
672 VlanId innerVlan,
673 VlanId outerVlan) {
674 TrafficSelector downstream = DefaultTrafficSelector.builder()
675 .matchVlanId(outerVlan)
676 .matchInPort(uplinkPort)
677 .matchInnerVlanId(innerVlan)
678 .build();
679
680 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
681 .setOutput(subscriberPort)
682 .build();
683
684 return DefaultForwardingObjective.builder()
685 .withFlag(ForwardingObjective.Flag.VERSATILE)
686 .withPriority(1000)
687 .makePermanent()
688 .withSelector(downstream)
689 .fromApp(appId)
690 .withTreatment(downstreamTreatment);
691 }
692
693 private ForwardingObjective.Builder transparentUpBuilder(PortNumber uplinkPort,
694 PortNumber subscriberPort,
695 VlanId innerVlan,
696 VlanId outerVlan) {
697 TrafficSelector upstream = DefaultTrafficSelector.builder()
698 .matchVlanId(outerVlan)
699 .matchInPort(subscriberPort)
700 .matchInnerVlanId(innerVlan)
701 .build();
702
703 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
704 .setOutput(uplinkPort)
705 .build();
706
707 return DefaultForwardingObjective.builder()
708 .withFlag(ForwardingObjective.Flag.VERSATILE)
709 .withPriority(1000)
710 .makePermanent()
711 .withSelector(upstream)
712 .fromApp(appId)
713 .withTreatment(upstreamTreatment);
714 }
715
716 private void unprovisionTransparentFlows(DeviceId deviceId, PortNumber uplink,
717 PortNumber subscriberPort, VlanId innerVlan,
718 VlanId outerVlan) {
719
720 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
721
722 additionalVlans.remove(cp, new AbstractMap.SimpleEntry(outerVlan, innerVlan));
723
724 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
725 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
726
727 ForwardingObjective.Builder upFwd = transparentUpBuilder(uplink, subscriberPort,
728 innerVlan, outerVlan);
729 ForwardingObjective.Builder downFwd = transparentDownBuilder(uplink, subscriberPort,
730 innerVlan, outerVlan);
731
732
733 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
734 @Override
735 public void onSuccess(Objective objective) {
736 upFuture.complete(null);
737 }
738
739 @Override
740 public void onError(Objective objective, ObjectiveError error) {
741 upFuture.complete(error);
742 }
743 }));
744
745 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
746 @Override
747 public void onSuccess(Objective objective) {
748 downFuture.complete(null);
749 }
750
751 @Override
752 public void onError(Objective objective, ObjectiveError error) {
753 downFuture.complete(error);
754 }
755 }));
756
757 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
758 if (downStatus != null) {
759 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
760 "on port {} failed downstream uninstallation: {}",
761 innerVlan, outerVlan, deviceId, subscriberPort, downStatus);
762 } else if (upStatus != null) {
763 log.error("Flow with innerVlan {} and outerVlan {} on device {} " +
764 "on port {} failed upstream uninstallation: {}",
765 innerVlan, outerVlan, deviceId, subscriberPort, upStatus);
766 }
767 }, oltInstallers);
768
769 }
770
Saurav Das82b8e6d2018-10-04 15:25:12 -0700771 private void processEapolFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
alshabib09753b52016-03-04 14:55:19 -0800772 if (!mastershipService.isLocalMaster(devId)) {
773 return;
774 }
alshabibbb83aa22016-02-10 15:08:23 -0800775 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
776
777 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
alshabib50d9fc52016-02-12 15:47:20 -0800778 .withKey(Criteria.matchInPort(port))
alshabibdec2e252016-01-15 12:20:25 -0800779 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
780 .withMeta(DefaultTrafficTreatment.builder()
781 .setOutput(PortNumber.CONTROLLER).build())
782 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400783 .withPriority(10000)
alshabibdec2e252016-01-15 12:20:25 -0800784 .add(new ObjectiveContext() {
785 @Override
786 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700787 log.info("Eapol filter for {} on {} {}.",
788 devId, port, (install) ? "installed" : "removed");
alshabibdec2e252016-01-15 12:20:25 -0800789 }
790
791 @Override
792 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700793 log.info("Eapol filter for {} on {} failed {} because {}",
794 devId, port, (install) ? "installation" : "removal",
795 error);
alshabibdec2e252016-01-15 12:20:25 -0800796 }
797 });
798
alshabibdec2e252016-01-15 12:20:25 -0800799 flowObjectiveService.filter(devId, eapol);
alshabib000b6fc2016-02-01 17:25:00 -0800800
alshabibdec2e252016-01-15 12:20:25 -0800801 }
802
Jonathan Hart403372d2018-08-22 11:44:13 -0700803 /**
804 * Installs trap filtering objectives for particular traffic types on an
805 * NNI port.
806 *
807 * @param devId device ID
808 * @param port port number
809 * @param install true to install, false to remove
810 */
811 private void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
812 processLldpFilteringObjective(devId, port, install);
Matteo Scandolo63460d12018-11-02 16:19:04 -0700813 if (enableDhcpOnProvisioning) {
814 processDhcpFilteringObjectives(devId, port, install, false);
815 }
Jonathan Hart403372d2018-08-22 11:44:13 -0700816 }
817
818 private void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
819 if (!mastershipService.isLocalMaster(devId)) {
820 return;
821 }
822 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
823
824 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
825 .withKey(Criteria.matchInPort(port))
826 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
827 .withMeta(DefaultTrafficTreatment.builder()
828 .setOutput(PortNumber.CONTROLLER).build())
829 .fromApp(appId)
830 .withPriority(10000)
831 .add(new ObjectiveContext() {
832 @Override
833 public void onSuccess(Objective objective) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700834 log.info("LLDP filter for device {} on port {} {}.",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700835 devId, port, (install) ? "installed" : "removed");
Jonathan Hart403372d2018-08-22 11:44:13 -0700836 }
837
838 @Override
839 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700840 log.info("LLDP filter for device {} on port {} failed {} because {}",
Saurav Das82b8e6d2018-10-04 15:25:12 -0700841 devId, port, (install) ? "installation" : "removal",
842 error);
Jonathan Hart403372d2018-08-22 11:44:13 -0700843 }
844 });
845
846 flowObjectiveService.filter(devId, lldp);
847
848 }
849
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700850 /**
851 * Trap dhcp packets to the controller.
852 *
853 * @param devId the device identifier
854 * @param port the port for which this trap flow is designated
855 * @param install true to install the flow, false to remove the flow
856 * @param upstream true if trapped packets are flowing upstream towards
857 * server, false if packets are flowing dowstream towards client
858 */
859 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port,
860 boolean install,
861 boolean upstream) {
Amit Ghosh95e2f652017-08-23 12:49:46 +0100862 if (!mastershipService.isLocalMaster(devId)) {
863 return;
864 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100865
Matteo Scandolo63460d12018-11-02 16:19:04 -0700866 if (enableDhcpV4) {
867 int udpSrc = (upstream) ? 68 : 67;
868 int udpDst = (upstream) ? 67 : 68;
869
870 EthType ethType = EthType.EtherType.IPV4.ethType();
871 byte protocol = IPv4.PROTOCOL_UDP;
872
873 this.addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType, protocol, install);
874 }
875
876 if (enableDhcpV6) {
877 int udpSrc = (upstream) ? 547 : 546;
878 int udpDst = (upstream) ? 546 : 547;
879
880 EthType ethType = EthType.EtherType.IPV6.ethType();
881 byte protocol = IPv6.PROTOCOL_UDP;
882
883 this.addDhcpFilteringObjectives(devId, port, udpSrc, udpDst, ethType, protocol, install);
884 }
885
886 }
887
888 private void addDhcpFilteringObjectives(DeviceId devId,
889 PortNumber port,
890 int udpSrc,
891 int udpDst,
892 EthType ethType,
893 byte protocol,
894 boolean install) {
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700895
896 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
Amit Ghosh95e2f652017-08-23 12:49:46 +0100897 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
898 .withKey(Criteria.matchInPort(port))
Matteo Scandolo63460d12018-11-02 16:19:04 -0700899 .addCondition(Criteria.matchEthType(ethType))
900 .addCondition(Criteria.matchIPProtocol(protocol))
Saurav Dasacc5eeb2018-10-11 10:58:01 -0700901 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(udpSrc)))
902 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(udpDst)))
Amit Ghosh95e2f652017-08-23 12:49:46 +0100903 .withMeta(DefaultTrafficTreatment.builder()
904 .setOutput(PortNumber.CONTROLLER).build())
905 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400906 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100907 .add(new ObjectiveContext() {
908 @Override
909 public void onSuccess(Objective objective) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700910 log.info("DHCP {} filter for device {} on port {} {}.",
911 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? "v4" : "v6",
912 devId, port, (install) ? "installed" : "removed");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100913 }
914
915 @Override
916 public void onError(Objective objective, ObjectiveError error) {
Matteo Scandolo63460d12018-11-02 16:19:04 -0700917 log.info("DHCP {} filter for device {} on port {} failed {} because {}",
918 (ethType.equals(EthType.EtherType.IPV4.ethType())) ? "v4" : "v6",
919 devId, port, (install) ? "installation" : "removal",
920 error);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100921 }
922 });
923
924 flowObjectiveService.filter(devId, dhcpUpstream);
925 }
926
927 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
928 if (!mastershipService.isLocalMaster(devId)) {
929 return;
930 }
931
932 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
933
934 builder = install ? builder.permit() : builder.deny();
935
936 FilteringObjective igmp = builder
937 .withKey(Criteria.matchInPort(port))
938 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
939 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
940 .withMeta(DefaultTrafficTreatment.builder()
941 .setOutput(PortNumber.CONTROLLER).build())
942 .fromApp(appId)
943 .withPriority(10000)
944 .add(new ObjectiveContext() {
945 @Override
946 public void onSuccess(Objective objective) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700947 log.info("Igmp filter for {} on {} {}.",
948 devId, port, (install) ? "installed" : "removed");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100949 }
950
951 @Override
952 public void onError(Objective objective, ObjectiveError error) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700953 log.info("Igmp filter for {} on {} failed {} because {}.",
954 devId, port, (install) ? "installation" : "removal",
955 error);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100956 }
957 });
958
959 flowObjectiveService.filter(devId, igmp);
960 }
961
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100962 /**
Jonathan Hart403372d2018-08-22 11:44:13 -0700963 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
964 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100965 *
966 * @param dev Device to look for
967 */
Jonathan Hart403372d2018-08-22 11:44:13 -0700968 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100969 // we create only for the ones we are master of
970 if (!mastershipService.isLocalMaster(dev.id())) {
971 return;
972 }
973 // check if this device is provisioned in Sadis
974 String devSerialNo = dev.serialNumber();
975 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
Jonathan Hart403372d2018-08-22 11:44:13 -0700976 log.debug("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100977
978 if (deviceInfo != null) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700979 // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100980 for (Port p : deviceService.getPorts(dev.id())) {
981 if (isUniPort(dev, p)) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700982 processEapolFilteringObjectives(dev.id(), p.number(), true);
Jonathan Hart403372d2018-08-22 11:44:13 -0700983 } else {
984 processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100985 }
986 }
987 }
988 }
989
Jonathan Hart403372d2018-08-22 11:44:13 -0700990
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100991 /**
992 * Get the uplink for of the OLT device.
993 *
994 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
995 * this logic needs to be changed
996 *
997 * @param dev Device to look for
998 * @return The uplink Port of the OLT
999 */
1000 private Port getUplinkPort(Device dev) {
1001 // check if this device is provisioned in Sadis
1002 String devSerialNo = dev.serialNumber();
1003 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
1004 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
Saurav Das82b8e6d2018-10-04 15:25:12 -07001005 if (deviceInfo == null) {
1006 log.warn("Device {} is not configured in SADIS .. cannot fetch device"
1007 + " info", dev.id());
1008 return null;
1009 }
1010 // Return the port that has been configured as the uplink port of this OLT in Sadis
1011 for (Port p: deviceService.getPorts(dev.id())) {
1012 if (p.number().toLong() == deviceInfo.uplinkPort()) {
1013 log.debug("getUplinkPort: Found port {}", p);
1014 return p;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001015 }
1016 }
1017
Saurav Das82b8e6d2018-10-04 15:25:12 -07001018 log.debug("getUplinkPort: No uplink port found for OLT {}", dev.id());
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001019 return null;
1020 }
1021
1022 /**
1023 * Return the subscriber on a port.
1024 *
Matteo Scandolo962a6ad2018-12-11 15:39:42 -08001025 * @param cp ConnectPoint on which to find the subscriber
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001026 * @return subscriber if found else null
1027 */
Matteo Scandolo962a6ad2018-12-11 15:39:42 -08001028 SubscriberAndDeviceInformation getSubscriber(ConnectPoint cp) {
1029 Port port = deviceService.getPort(cp);
1030 checkNotNull(port, "Invalid connect point");
1031 String portName = port.annotations().value(AnnotationKeys.PORT_NAME);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001032 return subsService.get(portName);
1033 }
1034
1035 private boolean isUniPort(Device d, Port p) {
1036 Port ulPort = getUplinkPort(d);
1037 if (ulPort != null) {
1038 return (ulPort.number().toLong() != p.number().toLong());
1039 }
1040 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001041 }
1042
Jonathan Hart4c538002018-08-23 10:11:54 -07001043 private SubscriberAndDeviceInformation getOltInfo(Device dev) {
1044 String devSerialNo = dev.serialNumber();
1045 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
1046 return deviceInfo;
1047 }
1048
alshabibf0e7e702015-05-30 18:22:36 -07001049 private class InternalDeviceListener implements DeviceListener {
1050 @Override
1051 public void event(DeviceEvent event) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001052 eventExecutor.execute(() -> {
1053 DeviceId devId = event.subject().id();
1054 Device dev = event.subject();
Jonathan Hart4c538002018-08-23 10:11:54 -07001055
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001056 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
1057 return;
1058 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001059
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001060 if (getOltInfo(dev) == null) {
1061 log.debug("No device info found, this is not an OLT");
1062 return;
1063 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001064
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001065 log.debug("OLT got {} event for {}", event.type(), event.subject());
Jonathan Hart4c538002018-08-23 10:11:54 -07001066
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001067 switch (event.type()) {
1068 //TODO: Port handling and bookkeeping should be improved once
1069 // olt firmware handles correct behaviour.
1070 case PORT_ADDED:
1071 if (isUniPort(dev, event.port())) {
1072 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
Jonathan Hart4c538002018-08-23 10:11:54 -07001073
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001074 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001075 processEapolFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001076 }
1077 } else {
1078 checkAndCreateDeviceFlows(dev);
1079 }
1080 break;
1081 case PORT_REMOVED:
1082 if (isUniPort(dev, event.port())) {
1083 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001084 processEapolFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001085 removeSubscriber(new ConnectPoint(devId, event.port().number()));
1086 }
1087
1088 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
1089 }
1090
1091 break;
1092 case PORT_UPDATED:
1093 if (!isUniPort(dev, event.port())) {
1094 break;
1095 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001096
1097 if (event.port().isEnabled()) {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001098 processEapolFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001099 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
1100 } else {
Saurav Das82b8e6d2018-10-04 15:25:12 -07001101 processEapolFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001102 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001103 }
alshabibbb83aa22016-02-10 15:08:23 -08001104 break;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001105 case DEVICE_ADDED:
alshabib7c190012016-02-09 18:22:33 -08001106 post(new AccessDeviceEvent(
1107 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
1108 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001109
1110 // Send UNI_ADDED events for all existing ports
1111 deviceService.getPorts(devId).stream()
1112 .filter(p -> isUniPort(dev, p))
1113 .filter(Port::isEnabled)
1114 .forEach(p -> post(new AccessDeviceEvent(
1115 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
1116
Jonathan Hart403372d2018-08-22 11:44:13 -07001117 checkAndCreateDeviceFlows(dev);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001118 break;
1119 case DEVICE_REMOVED:
1120 deviceService.getPorts(devId).stream()
1121 .filter(p -> isUniPort(dev, p))
1122 .forEach(p -> post(new AccessDeviceEvent(
1123 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
1124
alshabib7c190012016-02-09 18:22:33 -08001125 post(new AccessDeviceEvent(
1126 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
1127 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001128 break;
1129 case DEVICE_AVAILABILITY_CHANGED:
1130 if (deviceService.isAvailable(devId)) {
1131 post(new AccessDeviceEvent(
1132 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
1133 null, null));
1134 checkAndCreateDeviceFlows(dev);
1135 } else {
1136 post(new AccessDeviceEvent(
1137 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
1138 null, null));
1139 }
1140 break;
1141 case DEVICE_UPDATED:
1142 case DEVICE_SUSPENDED:
1143 case PORT_STATS_UPDATED:
1144 default:
1145 return;
1146 }
1147 });
alshabibf0e7e702015-05-30 18:22:36 -07001148 }
1149 }
alshabibf0e7e702015-05-30 18:22:36 -07001150}