blob: 3c5108efcaf84023b257bcd9cac568aff3c5fdea [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
alshabibf0e7e702015-05-30 18:22:36 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
alshabibe0559672016-02-21 14:49:51 -080021import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
alshabibf0e7e702015-05-30 18:22:36 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harte533a422015-10-20 17:31:24 -070025import org.apache.felix.scr.annotations.Service;
alshabibdec2e252016-01-15 12:20:25 -080026import org.onlab.packet.EthType;
Amit Ghosh95e2f652017-08-23 12:49:46 +010027import org.onlab.packet.IPv4;
28import org.onlab.packet.TpPort;
alshabibf0e7e702015-05-30 18:22:36 -070029import org.onlab.packet.VlanId;
Amit Ghosh95e2f652017-08-23 12:49:46 +010030import org.onlab.util.Tools;
alshabibe0559672016-02-21 14:49:51 -080031import org.onosproject.cfg.ComponentConfigService;
alshabibf0e7e702015-05-30 18:22:36 -070032import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
alshabib8e4fd2f2016-01-12 15:55:53 -080034import org.onosproject.event.AbstractListenerManager;
alshabib09753b52016-03-04 14:55:19 -080035import org.onosproject.mastership.MastershipService;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010036import org.onosproject.net.AnnotationKeys;
Jonathan Harte533a422015-10-20 17:31:24 -070037import org.onosproject.net.ConnectPoint;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010038import org.onosproject.net.Device;
alshabibf0e7e702015-05-30 18:22:36 -070039import org.onosproject.net.DeviceId;
alshabibdec2e252016-01-15 12:20:25 -080040import org.onosproject.net.Port;
alshabibf0e7e702015-05-30 18:22:36 -070041import org.onosproject.net.PortNumber;
42import org.onosproject.net.device.DeviceEvent;
43import org.onosproject.net.device.DeviceListener;
44import org.onosproject.net.device.DeviceService;
45import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
alshabibdec2e252016-01-15 12:20:25 -080049import org.onosproject.net.flow.criteria.Criteria;
50import org.onosproject.net.flowobjective.DefaultFilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070051import org.onosproject.net.flowobjective.DefaultForwardingObjective;
alshabibdec2e252016-01-15 12:20:25 -080052import org.onosproject.net.flowobjective.FilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070053import org.onosproject.net.flowobjective.FlowObjectiveService;
54import org.onosproject.net.flowobjective.ForwardingObjective;
alshabib3ea82642016-01-12 18:06:53 -080055import org.onosproject.net.flowobjective.Objective;
56import org.onosproject.net.flowobjective.ObjectiveContext;
57import org.onosproject.net.flowobjective.ObjectiveError;
alshabib36a4d732016-06-01 16:03:59 -070058import org.opencord.olt.AccessDeviceEvent;
59import org.opencord.olt.AccessDeviceListener;
60import org.opencord.olt.AccessDeviceService;
Amit Ghosh31939522018-08-16 13:28:21 +010061import org.opencord.olt.AccessSubscriberId;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010062import org.opencord.sadis.SubscriberAndDeviceInformation;
63import org.opencord.sadis.SubscriberAndDeviceInformationService;
alshabibe0559672016-02-21 14:49:51 -080064import org.osgi.service.component.ComponentContext;
alshabibf0e7e702015-05-30 18:22:36 -070065import org.slf4j.Logger;
66
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010067import java.util.AbstractMap;
68import java.util.ArrayList;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -080069import java.util.Collection;
alshabibe0559672016-02-21 14:49:51 -080070import java.util.Dictionary;
alshabibbb83aa22016-02-10 15:08:23 -080071import java.util.List;
Jonathan Harte533a422015-10-20 17:31:24 -070072import java.util.Map;
Jonathan Hart52998382015-11-10 16:09:22 -080073import java.util.Optional;
alshabibe0559672016-02-21 14:49:51 -080074import java.util.Properties;
alshabib3ea82642016-01-12 18:06:53 -080075import java.util.concurrent.CompletableFuture;
alshabib3ea82642016-01-12 18:06:53 -080076import java.util.concurrent.ExecutorService;
77import java.util.concurrent.Executors;
alshabibf0e7e702015-05-30 18:22:36 -070078
Jonathan Hart94b90492018-04-24 14:02:25 -070079import static com.google.common.base.Preconditions.checkNotNull;
alshabibe0559672016-02-21 14:49:51 -080080import static com.google.common.base.Strings.isNullOrEmpty;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -070081import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
alshabibe0559672016-02-21 14:49:51 -080082import static org.onlab.util.Tools.get;
alshabib3ea82642016-01-12 18:06:53 -080083import static org.onlab.util.Tools.groupedThreads;
alshabibf0e7e702015-05-30 18:22:36 -070084import static org.slf4j.LoggerFactory.getLogger;
85
86/**
Jonathan Harte533a422015-10-20 17:31:24 -070087 * Provisions rules on access devices.
alshabibf0e7e702015-05-30 18:22:36 -070088 */
Jonathan Harte533a422015-10-20 17:31:24 -070089@Service
alshabibf0e7e702015-05-30 18:22:36 -070090@Component(immediate = true)
alshabib8e4fd2f2016-01-12 15:55:53 -080091public class Olt
92 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
93 implements AccessDeviceService {
Charles Chan54f110f2017-01-20 11:22:42 -080094 private static final String APP_NAME = "org.opencord.olt";
alshabibe0559672016-02-21 14:49:51 -080095
96 private static final short DEFAULT_VLAN = 0;
97
alshabibf0e7e702015-05-30 18:22:36 -070098 private final Logger log = getLogger(getClass());
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected FlowObjectiveService flowObjectiveService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib09753b52016-03-04 14:55:19 -0800104 protected MastershipService mastershipService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibf0e7e702015-05-30 18:22:36 -0700107 protected DeviceService deviceService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected CoreService coreService;
111
Jonathan Harte533a422015-10-20 17:31:24 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe0559672016-02-21 14:49:51 -0800113 protected ComponentConfigService componentConfigService;
114
alshabib4ceaed32016-03-03 18:00:58 -0800115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100116 protected SubscriberAndDeviceInformationService subsService;
alshabibe0559672016-02-21 14:49:51 -0800117
118 @Property(name = "defaultVlan", intValue = DEFAULT_VLAN,
119 label = "Default VLAN RG<->ONU traffic")
120 private int defaultVlan = DEFAULT_VLAN;
121
Matt Jeanneret3f579262018-06-14 17:16:23 -0400122 @Property(name = "enableDhcpOnProvisioning", boolValue = true,
123 label = "Create the DHCP Flow rules when a subscriber is provisioned")
124 protected boolean enableDhcpOnProvisioning = false;
125
126 @Property(name = "enableIgmpOnProvisioning", boolValue = false,
127 label = "Create IGMP Flow rules when a subscriber is provisioned")
128 protected boolean enableIgmpOnProvisioning = false;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100129
alshabibf0e7e702015-05-30 18:22:36 -0700130 private final DeviceListener deviceListener = new InternalDeviceListener();
131
132 private ApplicationId appId;
133
alshabib3ea82642016-01-12 18:06:53 -0800134 private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
alshabibbf23a1f2016-01-14 17:27:11 -0800135 groupedThreads("onos/olt-service",
136 "olt-installer-%d"));
alshabibf0e7e702015-05-30 18:22:36 -0700137
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700138 protected ExecutorService eventExecutor;
139
alshabibf0e7e702015-05-30 18:22:36 -0700140 @Activate
alshabibe0559672016-02-21 14:49:51 -0800141 public void activate(ComponentContext context) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700142 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/olt", "events-%d", log));
alshabibe0559672016-02-21 14:49:51 -0800143 modified(context);
Charles Chan54f110f2017-01-20 11:22:42 -0800144 appId = coreService.registerApplication(APP_NAME);
alshabibe0559672016-02-21 14:49:51 -0800145 componentConfigService.registerProperties(getClass());
alshabibc4dfe852015-06-05 13:35:13 -0700146
alshabib8e4fd2f2016-01-12 15:55:53 -0800147 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
148
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100149 // look for all provisioned devices in Sadis and create EAPOL flows for the
150 // UNI ports
151 Iterable<Device> devices = deviceService.getDevices();
152 for (Device d : devices) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700153 checkAndCreateDeviceFlows(d);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100154 }
alshabib4ceaed32016-03-03 18:00:58 -0800155
alshabibba357492016-01-27 13:49:46 -0800156 deviceService.addListener(deviceListener);
157
alshabibf0e7e702015-05-30 18:22:36 -0700158 log.info("Started with Application ID {}", appId.id());
159 }
160
161 @Deactivate
162 public void deactivate() {
alshabibe0559672016-02-21 14:49:51 -0800163 componentConfigService.unregisterProperties(getClass(), false);
alshabib62e9ce72016-02-11 17:31:36 -0800164 deviceService.removeListener(deviceListener);
Jonathan Hart5f1c8142018-07-24 17:31:59 -0700165 eventDispatcher.removeSink(AccessDeviceEvent.class);
alshabibf0e7e702015-05-30 18:22:36 -0700166 log.info("Stopped");
167 }
168
alshabibe0559672016-02-21 14:49:51 -0800169 @Modified
170 public void modified(ComponentContext context) {
171 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
172
173 try {
174 String s = get(properties, "defaultVlan");
175 defaultVlan = isNullOrEmpty(s) ? DEFAULT_VLAN : Integer.parseInt(s.trim());
Amit Ghosh95e2f652017-08-23 12:49:46 +0100176
Matt Jeanneret3f579262018-06-14 17:16:23 -0400177 Boolean o = Tools.isPropertyEnabled(properties, "enableDhcpOnProvisioning");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100178 if (o != null) {
Matt Jeanneret3f579262018-06-14 17:16:23 -0400179 enableDhcpOnProvisioning = o;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100180 }
Matt Jeanneret3f579262018-06-14 17:16:23 -0400181
182 Boolean p = Tools.isPropertyEnabled(properties, "enableIgmpOnProvisioning");
183 if (p != null) {
184 enableIgmpOnProvisioning = p;
185 }
186
alshabibe0559672016-02-21 14:49:51 -0800187 } catch (Exception e) {
188 defaultVlan = DEFAULT_VLAN;
189 }
190 }
191
alshabib32232c82016-02-25 17:57:24 -0500192 @Override
Amit Ghosh31939522018-08-16 13:28:21 +0100193 public boolean provisionSubscriber(ConnectPoint port) {
Jonathan Hart94b90492018-04-24 14:02:25 -0700194 checkNotNull(deviceService.getPort(port.deviceId(), port.port()),
195 "Invalid connect point");
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100196 // Find the subscriber on this connect point
197 SubscriberAndDeviceInformation sub = getSubscriber(port);
198 if (sub == null) {
199 log.warn("No subscriber found for {}", port);
Amit Ghosh31939522018-08-16 13:28:21 +0100200 return false;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100201 }
Jonathan Harte533a422015-10-20 17:31:24 -0700202
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100203 // Get the uplink port
204 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
205 if (uplinkPort == null) {
206 log.warn("No uplink port found for OLT device {}", port.deviceId());
Amit Ghosh31939522018-08-16 13:28:21 +0100207 return false;
Jonathan Harte533a422015-10-20 17:31:24 -0700208 }
209
Matt Jeanneret3f579262018-06-14 17:16:23 -0400210 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100211 processDhcpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100212 }
213
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100214 Optional<VlanId> defaultVlan = Optional.empty();
215 provisionVlans(port.deviceId(), uplinkPort.number(), port.port(), sub.cTag(), sub.sTag(),
216 defaultVlan);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100217
Matt Jeanneret3f579262018-06-14 17:16:23 -0400218 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100219 processIgmpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100220 }
Amit Ghosh31939522018-08-16 13:28:21 +0100221 return true;
alshabibb7a9e172016-01-13 11:23:53 -0800222 }
223
224 @Override
Amit Ghosh31939522018-08-16 13:28:21 +0100225 public boolean removeSubscriber(ConnectPoint port) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100226 // Get the subscriber connected to this port from Sadis
227 SubscriberAndDeviceInformation subscriber = getSubscriber(port);
228 if (subscriber == null) {
229 log.warn("Subscriber on port {} not found", port);
Amit Ghosh31939522018-08-16 13:28:21 +0100230 return false;
alshabibbf23a1f2016-01-14 17:27:11 -0800231 }
232
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100233 // Get the uplink port
234 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
235 if (uplinkPort == null) {
236 log.warn("No uplink port found for OLT device {}", port.deviceId());
Amit Ghosh31939522018-08-16 13:28:21 +0100237 return false;
alshabib4ceaed32016-03-03 18:00:58 -0800238 }
239
Matt Jeanneret3f579262018-06-14 17:16:23 -0400240 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100241 processDhcpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100242 }
243
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100244 Optional<VlanId> defaultVlan = Optional.empty();
245 unprovisionSubscriber(port.deviceId(), uplinkPort.number(), port.port(), subscriber.cTag(),
246 subscriber.sTag(), defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800247
Matt Jeanneret3f579262018-06-14 17:16:23 -0400248 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100249 processIgmpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100250 }
Amit Ghosh31939522018-08-16 13:28:21 +0100251 return true;
alshabibbf23a1f2016-01-14 17:27:11 -0800252 }
253
Amit Ghosh31939522018-08-16 13:28:21 +0100254 @Override
255 public boolean provisionSubscriber(AccessSubscriberId subscriberId) {
256 // Check if we can find the connect point to which this subscriber is connected
257 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
258 if (subsPort == null) {
259 log.warn("ConnectPoint for {} not found", subscriberId);
260 return false;
261 }
262
263 return provisionSubscriber(subsPort);
264 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100265
alshabibe0559672016-02-21 14:49:51 -0800266 @Override
Amit Ghosh31939522018-08-16 13:28:21 +0100267 public boolean removeSubscriber(AccessSubscriberId subscriberId) {
268 // Check if we can find the connect point to which this subscriber is connected
269 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
270 if (subsPort == null) {
271 log.warn("ConnectPoint for {} not found", subscriberId);
272 return false;
273 }
274
275 return removeSubscriber(subsPort);
276 }
277
278 @Override
279 public Collection<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> getSubscribers() {
280 ArrayList<Map.Entry<ConnectPoint, Map.Entry<VlanId, VlanId>>> subs = new ArrayList<>();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100281
282 // Get the subscribers for all the devices
283 // If the port is UNI, is enabled and exists in Sadis then copy it
284 for (Device d : deviceService.getDevices()) {
285 for (Port p: deviceService.getPorts(d.id())) {
286 if (isUniPort(d, p) && p.isEnabled()) {
287 ConnectPoint cp = new ConnectPoint(d.id(), p.number());
288
289 SubscriberAndDeviceInformation sub = getSubscriber(cp);
290 if (sub != null) {
Amit Ghosh31939522018-08-16 13:28:21 +0100291 Map.Entry<VlanId, VlanId> vlans = new AbstractMap.SimpleEntry(sub.sTag(), sub.cTag());
292 subs.add(new AbstractMap.SimpleEntry(cp, vlans));
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100293 }
294 }
295 }
296 }
297
298 return subs;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800299 }
300
301 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100302 public List<DeviceId> fetchOlts() {
303 // look through all the devices and find the ones that are OLTs as per Sadis
304 List<DeviceId> olts = new ArrayList<>();
305 Iterable<Device> devices = deviceService.getDevices();
306 for (Device d : devices) {
307 String devSerialNo = d.serialNumber();
308 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
309
310 if (deviceInfo != null) {
311 // So this is indeed a OLT device
312 olts.add(d.id());
313 }
314 }
315 return olts;
alshabibe0559672016-02-21 14:49:51 -0800316 }
317
Amit Ghosh31939522018-08-16 13:28:21 +0100318 /**
319 * Finds the connect point to which a subscriber is connected.
320 *
321 * @param id The id of the subscriber, this is the same ID as in Sadis
322 * @return Subscribers ConnectPoint if found else null
323 */
324 private ConnectPoint findSubscriberConnectPoint(String id) {
325
326 Iterable<Device> devices = deviceService.getDevices();
327 for (Device d : devices) {
328 for (Port p : deviceService.getPorts(d.id())) {
329 log.trace("Comparing {} with {}", p.annotations().value(AnnotationKeys.PORT_NAME), id);
330 if (p.annotations().value(AnnotationKeys.PORT_NAME).equals(id)) {
331 log.debug("Found on device {} port {}", d.id(), p.number());
332 return new ConnectPoint(d.id(), p.number());
333 }
334 }
335 }
336 return null;
337 }
338
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700339 private void initializeUni(Port port) {
340 DeviceId deviceId = (DeviceId) port.element().id();
341
342 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, deviceId, port));
343
344 if (port.isEnabled()) {
345 processFilteringObjectives(deviceId, port.number(), true);
346 }
347 }
348
alshabibbf23a1f2016-01-14 17:27:11 -0800349 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
alshabib4ceaed32016-03-03 18:00:58 -0800350 PortNumber subscriberPort, VlanId subscriberVlan,
351 VlanId deviceVlan, Optional<VlanId> defaultVlan) {
alshabibbf23a1f2016-01-14 17:27:11 -0800352
353 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
354 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
355
alshabib4ceaed32016-03-03 18:00:58 -0800356 ForwardingObjective.Builder upFwd = upBuilder(uplink, subscriberPort,
357 subscriberVlan, deviceVlan,
358 defaultVlan);
359 ForwardingObjective.Builder downFwd = downBuilder(uplink, subscriberPort,
360 subscriberVlan, deviceVlan,
361 defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800362
363
alshabib4ceaed32016-03-03 18:00:58 -0800364 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
365 @Override
366 public void onSuccess(Objective objective) {
367 upFuture.complete(null);
368 }
alshabibbf23a1f2016-01-14 17:27:11 -0800369
alshabib4ceaed32016-03-03 18:00:58 -0800370 @Override
371 public void onError(Objective objective, ObjectiveError error) {
372 upFuture.complete(error);
373 }
374 }));
375
376 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
377 @Override
378 public void onSuccess(Objective objective) {
379 downFuture.complete(null);
380 }
381
382 @Override
383 public void onError(Objective objective, ObjectiveError error) {
384 downFuture.complete(error);
385 }
386 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800387
388 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
389 if (upStatus == null && downStatus == null) {
390 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
391 deviceId,
392 deviceVlan,
393 subscriberVlan));
394 } else if (downStatus != null) {
395 log.error("Subscriber with vlan {} on device {} " +
396 "on port {} failed downstream uninstallation: {}",
397 subscriberVlan, deviceId, subscriberPort, downStatus);
398 } else if (upStatus != null) {
399 log.error("Subscriber with vlan {} on device {} " +
400 "on port {} failed upstream uninstallation: {}",
401 subscriberVlan, deviceId, subscriberPort, upStatus);
402 }
403 }, oltInstallers);
alshabibb7a9e172016-01-13 11:23:53 -0800404
Jonathan Harte533a422015-10-20 17:31:24 -0700405 }
406
407 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
408 PortNumber subscriberPort,
Jonathan Hart52998382015-11-10 16:09:22 -0800409 VlanId subscriberVlan, VlanId deviceVlan,
410 Optional<VlanId> defaultVlan) {
Jonathan Harte533a422015-10-20 17:31:24 -0700411
alshabib3ea82642016-01-12 18:06:53 -0800412 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
413 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
414
alshabib4ceaed32016-03-03 18:00:58 -0800415 ForwardingObjective.Builder upFwd = upBuilder(uplinkPort, subscriberPort,
416 subscriberVlan, deviceVlan,
417 defaultVlan);
Jonathan Harte533a422015-10-20 17:31:24 -0700418
419
alshabib4ceaed32016-03-03 18:00:58 -0800420 ForwardingObjective.Builder downFwd = downBuilder(uplinkPort, subscriberPort,
421 subscriberVlan, deviceVlan,
422 defaultVlan);
alshabib3ea82642016-01-12 18:06:53 -0800423
alshabibbf23a1f2016-01-14 17:27:11 -0800424 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
425 @Override
426 public void onSuccess(Objective objective) {
427 upFuture.complete(null);
428 }
429
430 @Override
431 public void onError(Objective objective, ObjectiveError error) {
432 upFuture.complete(error);
433 }
434 }));
435
alshabibbf23a1f2016-01-14 17:27:11 -0800436 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
437 @Override
438 public void onSuccess(Objective objective) {
439 downFuture.complete(null);
440 }
441
442 @Override
443 public void onError(Objective objective, ObjectiveError error) {
444 downFuture.complete(error);
445 }
446 }));
alshabib3ea82642016-01-12 18:06:53 -0800447
448 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
449 if (upStatus == null && downStatus == null) {
450 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
451 deviceId,
452 deviceVlan,
453 subscriberVlan));
alshabib50d9fc52016-02-12 15:47:20 -0800454
alshabib3ea82642016-01-12 18:06:53 -0800455 } else if (downStatus != null) {
456 log.error("Subscriber with vlan {} on device {} " +
457 "on port {} failed downstream installation: {}",
458 subscriberVlan, deviceId, subscriberPort, downStatus);
459 } else if (upStatus != null) {
460 log.error("Subscriber with vlan {} on device {} " +
461 "on port {} failed upstream installation: {}",
462 subscriberVlan, deviceId, subscriberPort, upStatus);
463 }
464 }, oltInstallers);
465
Jonathan Harte533a422015-10-20 17:31:24 -0700466 }
467
alshabib4ceaed32016-03-03 18:00:58 -0800468 private ForwardingObjective.Builder downBuilder(PortNumber uplinkPort,
469 PortNumber subscriberPort,
470 VlanId subscriberVlan,
471 VlanId deviceVlan,
472 Optional<VlanId> defaultVlan) {
473 TrafficSelector downstream = DefaultTrafficSelector.builder()
474 .matchVlanId(deviceVlan)
475 .matchInPort(uplinkPort)
476 .matchInnerVlanId(subscriberVlan)
477 .build();
478
479 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
480 .popVlan()
481 .setVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
482 .setOutput(subscriberPort)
483 .build();
484
485 return DefaultForwardingObjective.builder()
486 .withFlag(ForwardingObjective.Flag.VERSATILE)
487 .withPriority(1000)
488 .makePermanent()
489 .withSelector(downstream)
490 .fromApp(appId)
491 .withTreatment(downstreamTreatment);
492 }
493
494 private ForwardingObjective.Builder upBuilder(PortNumber uplinkPort,
495 PortNumber subscriberPort,
496 VlanId subscriberVlan,
497 VlanId deviceVlan,
498 Optional<VlanId> defaultVlan) {
499 TrafficSelector upstream = DefaultTrafficSelector.builder()
500 .matchVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
501 .matchInPort(subscriberPort)
502 .build();
503
504
505 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
506 .pushVlan()
507 .setVlanId(subscriberVlan)
508 .pushVlan()
509 .setVlanId(deviceVlan)
510 .setOutput(uplinkPort)
511 .build();
512
513 return DefaultForwardingObjective.builder()
514 .withFlag(ForwardingObjective.Flag.VERSATILE)
515 .withPriority(1000)
516 .makePermanent()
517 .withSelector(upstream)
518 .fromApp(appId)
519 .withTreatment(upstreamTreatment);
520 }
521
alshabib50d9fc52016-02-12 15:47:20 -0800522 private void processFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
alshabib09753b52016-03-04 14:55:19 -0800523 if (!mastershipService.isLocalMaster(devId)) {
524 return;
525 }
alshabibbb83aa22016-02-10 15:08:23 -0800526 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
527
528 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
alshabib50d9fc52016-02-12 15:47:20 -0800529 .withKey(Criteria.matchInPort(port))
alshabibdec2e252016-01-15 12:20:25 -0800530 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
531 .withMeta(DefaultTrafficTreatment.builder()
532 .setOutput(PortNumber.CONTROLLER).build())
533 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400534 .withPriority(10000)
alshabibdec2e252016-01-15 12:20:25 -0800535 .add(new ObjectiveContext() {
536 @Override
537 public void onSuccess(Objective objective) {
538 log.info("Eapol filter for {} on {} installed.",
539 devId, port);
540 }
541
542 @Override
543 public void onError(Objective objective, ObjectiveError error) {
544 log.info("Eapol filter for {} on {} failed because {}",
545 devId, port, error);
546 }
547 });
548
alshabibdec2e252016-01-15 12:20:25 -0800549 flowObjectiveService.filter(devId, eapol);
alshabib000b6fc2016-02-01 17:25:00 -0800550
alshabibdec2e252016-01-15 12:20:25 -0800551 }
552
Jonathan Hart403372d2018-08-22 11:44:13 -0700553 /**
554 * Installs trap filtering objectives for particular traffic types on an
555 * NNI port.
556 *
557 * @param devId device ID
558 * @param port port number
559 * @param install true to install, false to remove
560 */
561 private void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
562 processLldpFilteringObjective(devId, port, install);
563 processDhcpFilteringObjectives(devId, port, install);
564 }
565
566 private void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
567 if (!mastershipService.isLocalMaster(devId)) {
568 return;
569 }
570 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
571
572 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
573 .withKey(Criteria.matchInPort(port))
574 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
575 .withMeta(DefaultTrafficTreatment.builder()
576 .setOutput(PortNumber.CONTROLLER).build())
577 .fromApp(appId)
578 .withPriority(10000)
579 .add(new ObjectiveContext() {
580 @Override
581 public void onSuccess(Objective objective) {
582 log.info("LLDP filter for {} on {} installed.",
583 devId, port);
584 }
585
586 @Override
587 public void onError(Objective objective, ObjectiveError error) {
588 log.info("LLDP filter for {} on {} failed because {}",
589 devId, port, error);
590 }
591 });
592
593 flowObjectiveService.filter(devId, lldp);
594
595 }
596
Amit Ghosh95e2f652017-08-23 12:49:46 +0100597 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
598 if (!mastershipService.isLocalMaster(devId)) {
599 return;
600 }
601 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
602
603 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
604 .withKey(Criteria.matchInPort(port))
605 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
606 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
607 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
608 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
609 .withMeta(DefaultTrafficTreatment.builder()
610 .setOutput(PortNumber.CONTROLLER).build())
611 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400612 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100613 .add(new ObjectiveContext() {
614 @Override
615 public void onSuccess(Objective objective) {
616 log.info("DHCP filter for {} on {} installed.",
617 devId, port);
618 }
619
620 @Override
621 public void onError(Objective objective, ObjectiveError error) {
622 log.info("DHCP filter for {} on {} failed because {}",
623 devId, port, error);
624 }
625 });
626
627 flowObjectiveService.filter(devId, dhcpUpstream);
628 }
629
630 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
631 if (!mastershipService.isLocalMaster(devId)) {
632 return;
633 }
634
635 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
636
637 builder = install ? builder.permit() : builder.deny();
638
639 FilteringObjective igmp = builder
640 .withKey(Criteria.matchInPort(port))
641 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
642 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
643 .withMeta(DefaultTrafficTreatment.builder()
644 .setOutput(PortNumber.CONTROLLER).build())
645 .fromApp(appId)
646 .withPriority(10000)
647 .add(new ObjectiveContext() {
648 @Override
649 public void onSuccess(Objective objective) {
650 log.info("Igmp filter for {} on {} installed.",
651 devId, port);
652 }
653
654 @Override
655 public void onError(Objective objective, ObjectiveError error) {
656 log.info("Igmp filter for {} on {} failed because {}.",
657 devId, port, error);
658 }
659 });
660
661 flowObjectiveService.filter(devId, igmp);
662 }
663
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100664 /**
Jonathan Hart403372d2018-08-22 11:44:13 -0700665 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
666 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100667 *
668 * @param dev Device to look for
669 */
Jonathan Hart403372d2018-08-22 11:44:13 -0700670 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100671 // we create only for the ones we are master of
672 if (!mastershipService.isLocalMaster(dev.id())) {
673 return;
674 }
675 // check if this device is provisioned in Sadis
676 String devSerialNo = dev.serialNumber();
677 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
Jonathan Hart403372d2018-08-22 11:44:13 -0700678 log.debug("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100679
680 if (deviceInfo != null) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700681 // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100682 for (Port p : deviceService.getPorts(dev.id())) {
683 if (isUniPort(dev, p)) {
684 processFilteringObjectives(dev.id(), p.number(), true);
Jonathan Hart403372d2018-08-22 11:44:13 -0700685 } else {
686 processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100687 }
688 }
689 }
690 }
691
Jonathan Hart403372d2018-08-22 11:44:13 -0700692
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100693 /**
694 * Get the uplink for of the OLT device.
695 *
696 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
697 * this logic needs to be changed
698 *
699 * @param dev Device to look for
700 * @return The uplink Port of the OLT
701 */
702 private Port getUplinkPort(Device dev) {
703 // check if this device is provisioned in Sadis
704 String devSerialNo = dev.serialNumber();
705 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
706 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
707
708 if (deviceInfo != null) {
709 // Return the port that has been configured as the uplink port of this OLT in Sadis
710 for (Port p: deviceService.getPorts(dev.id())) {
711 if (p.number().toLong() == deviceInfo.uplinkPort()) {
712 log.debug("getUplinkPort: Found port {}", p);
713 return p;
714 }
715 }
716 }
717
718 log.debug("getUplinkPort: No uplink port found for OLT {}", dev);
719 return null;
720 }
721
722 /**
723 * Return the subscriber on a port.
724 *
725 * @param port On which to find the subscriber
726 * @return subscriber if found else null
727 */
728 private SubscriberAndDeviceInformation getSubscriber(ConnectPoint port) {
729 String portName = deviceService.getPort(port).annotations()
730 .value(AnnotationKeys.PORT_NAME);
731
732 return subsService.get(portName);
733 }
734
735 private boolean isUniPort(Device d, Port p) {
736 Port ulPort = getUplinkPort(d);
737 if (ulPort != null) {
738 return (ulPort.number().toLong() != p.number().toLong());
739 }
740 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700741 }
742
Jonathan Hart4c538002018-08-23 10:11:54 -0700743 private SubscriberAndDeviceInformation getOltInfo(Device dev) {
744 String devSerialNo = dev.serialNumber();
745 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
746 return deviceInfo;
747 }
748
alshabibf0e7e702015-05-30 18:22:36 -0700749 private class InternalDeviceListener implements DeviceListener {
750 @Override
751 public void event(DeviceEvent event) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700752 eventExecutor.execute(() -> {
753 DeviceId devId = event.subject().id();
754 Device dev = event.subject();
Jonathan Hart4c538002018-08-23 10:11:54 -0700755
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700756 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
757 return;
758 }
Jonathan Hart4c538002018-08-23 10:11:54 -0700759
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700760 if (getOltInfo(dev) == null) {
761 log.debug("No device info found, this is not an OLT");
762 return;
763 }
Jonathan Hart4c538002018-08-23 10:11:54 -0700764
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700765 log.debug("OLT got {} event for {}", event.type(), event.subject());
Jonathan Hart4c538002018-08-23 10:11:54 -0700766
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700767 switch (event.type()) {
768 //TODO: Port handling and bookkeeping should be improved once
769 // olt firmware handles correct behaviour.
770 case PORT_ADDED:
771 if (isUniPort(dev, event.port())) {
772 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
Jonathan Hart4c538002018-08-23 10:11:54 -0700773
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700774 if (event.port().isEnabled()) {
775 processFilteringObjectives(devId, event.port().number(), true);
776 }
777 } else {
778 checkAndCreateDeviceFlows(dev);
779 }
780 break;
781 case PORT_REMOVED:
782 if (isUniPort(dev, event.port())) {
783 if (event.port().isEnabled()) {
784 processFilteringObjectives(devId, event.port().number(), false);
785 removeSubscriber(new ConnectPoint(devId, event.port().number()));
786 }
787
788 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
789 }
790
791 break;
792 case PORT_UPDATED:
793 if (!isUniPort(dev, event.port())) {
794 break;
795 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700796
797 if (event.port().isEnabled()) {
798 processFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700799 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
800 } else {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700801 processFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700802 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700803 }
alshabibbb83aa22016-02-10 15:08:23 -0800804 break;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700805 case DEVICE_ADDED:
alshabib7c190012016-02-09 18:22:33 -0800806 post(new AccessDeviceEvent(
807 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
808 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700809
810 // Send UNI_ADDED events for all existing ports
811 deviceService.getPorts(devId).stream()
812 .filter(p -> isUniPort(dev, p))
813 .filter(Port::isEnabled)
814 .forEach(p -> post(new AccessDeviceEvent(
815 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
816
Jonathan Hart403372d2018-08-22 11:44:13 -0700817 checkAndCreateDeviceFlows(dev);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700818 break;
819 case DEVICE_REMOVED:
820 deviceService.getPorts(devId).stream()
821 .filter(p -> isUniPort(dev, p))
822 .forEach(p -> post(new AccessDeviceEvent(
823 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
824
alshabib7c190012016-02-09 18:22:33 -0800825 post(new AccessDeviceEvent(
826 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
827 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700828 break;
829 case DEVICE_AVAILABILITY_CHANGED:
830 if (deviceService.isAvailable(devId)) {
831 post(new AccessDeviceEvent(
832 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
833 null, null));
834 checkAndCreateDeviceFlows(dev);
835 } else {
836 post(new AccessDeviceEvent(
837 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
838 null, null));
839 }
840 break;
841 case DEVICE_UPDATED:
842 case DEVICE_SUSPENDED:
843 case PORT_STATS_UPDATED:
844 default:
845 return;
846 }
847 });
alshabibf0e7e702015-05-30 18:22:36 -0700848 }
849 }
alshabibf0e7e702015-05-30 18:22:36 -0700850}