blob: 755c178243f90be6bd2e9824273cfaff21c1ad2f [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 Ghosh1ed9aef2018-07-17 17:08:16 +010061import org.opencord.sadis.SubscriberAndDeviceInformation;
62import org.opencord.sadis.SubscriberAndDeviceInformationService;
alshabibe0559672016-02-21 14:49:51 -080063import org.osgi.service.component.ComponentContext;
alshabibf0e7e702015-05-30 18:22:36 -070064import org.slf4j.Logger;
65
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010066import java.util.AbstractMap;
67import java.util.ArrayList;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -080068import java.util.Collection;
alshabibe0559672016-02-21 14:49:51 -080069import java.util.Dictionary;
alshabibbb83aa22016-02-10 15:08:23 -080070import java.util.List;
Jonathan Harte533a422015-10-20 17:31:24 -070071import java.util.Map;
Jonathan Hart52998382015-11-10 16:09:22 -080072import java.util.Optional;
alshabibe0559672016-02-21 14:49:51 -080073import java.util.Properties;
alshabib3ea82642016-01-12 18:06:53 -080074import java.util.concurrent.CompletableFuture;
alshabib3ea82642016-01-12 18:06:53 -080075import java.util.concurrent.ExecutorService;
76import java.util.concurrent.Executors;
alshabibf0e7e702015-05-30 18:22:36 -070077
Jonathan Hart94b90492018-04-24 14:02:25 -070078import static com.google.common.base.Preconditions.checkNotNull;
alshabibe0559672016-02-21 14:49:51 -080079import static com.google.common.base.Strings.isNullOrEmpty;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -070080import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
alshabibe0559672016-02-21 14:49:51 -080081import static org.onlab.util.Tools.get;
alshabib3ea82642016-01-12 18:06:53 -080082import static org.onlab.util.Tools.groupedThreads;
alshabibf0e7e702015-05-30 18:22:36 -070083import static org.slf4j.LoggerFactory.getLogger;
84
85/**
Jonathan Harte533a422015-10-20 17:31:24 -070086 * Provisions rules on access devices.
alshabibf0e7e702015-05-30 18:22:36 -070087 */
Jonathan Harte533a422015-10-20 17:31:24 -070088@Service
alshabibf0e7e702015-05-30 18:22:36 -070089@Component(immediate = true)
alshabib8e4fd2f2016-01-12 15:55:53 -080090public class Olt
91 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
92 implements AccessDeviceService {
Charles Chan54f110f2017-01-20 11:22:42 -080093 private static final String APP_NAME = "org.opencord.olt";
alshabibe0559672016-02-21 14:49:51 -080094
95 private static final short DEFAULT_VLAN = 0;
96
alshabibf0e7e702015-05-30 18:22:36 -070097 private final Logger log = getLogger(getClass());
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected FlowObjectiveService flowObjectiveService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib09753b52016-03-04 14:55:19 -0800103 protected MastershipService mastershipService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibf0e7e702015-05-30 18:22:36 -0700106 protected DeviceService deviceService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected CoreService coreService;
110
Jonathan Harte533a422015-10-20 17:31:24 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe0559672016-02-21 14:49:51 -0800112 protected ComponentConfigService componentConfigService;
113
alshabib4ceaed32016-03-03 18:00:58 -0800114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100115 protected SubscriberAndDeviceInformationService subsService;
alshabibe0559672016-02-21 14:49:51 -0800116
117 @Property(name = "defaultVlan", intValue = DEFAULT_VLAN,
118 label = "Default VLAN RG<->ONU traffic")
119 private int defaultVlan = DEFAULT_VLAN;
120
Matt Jeanneret3f579262018-06-14 17:16:23 -0400121 @Property(name = "enableDhcpOnProvisioning", boolValue = true,
122 label = "Create the DHCP Flow rules when a subscriber is provisioned")
123 protected boolean enableDhcpOnProvisioning = false;
124
125 @Property(name = "enableIgmpOnProvisioning", boolValue = false,
126 label = "Create IGMP Flow rules when a subscriber is provisioned")
127 protected boolean enableIgmpOnProvisioning = false;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100128
alshabibf0e7e702015-05-30 18:22:36 -0700129 private final DeviceListener deviceListener = new InternalDeviceListener();
130
131 private ApplicationId appId;
132
alshabib3ea82642016-01-12 18:06:53 -0800133 private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
alshabibbf23a1f2016-01-14 17:27:11 -0800134 groupedThreads("onos/olt-service",
135 "olt-installer-%d"));
alshabibf0e7e702015-05-30 18:22:36 -0700136
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700137 protected ExecutorService eventExecutor;
138
alshabibf0e7e702015-05-30 18:22:36 -0700139 @Activate
alshabibe0559672016-02-21 14:49:51 -0800140 public void activate(ComponentContext context) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700141 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/olt", "events-%d", log));
alshabibe0559672016-02-21 14:49:51 -0800142 modified(context);
Charles Chan54f110f2017-01-20 11:22:42 -0800143 appId = coreService.registerApplication(APP_NAME);
alshabibe0559672016-02-21 14:49:51 -0800144 componentConfigService.registerProperties(getClass());
alshabibc4dfe852015-06-05 13:35:13 -0700145
alshabib8e4fd2f2016-01-12 15:55:53 -0800146 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
147
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100148 // look for all provisioned devices in Sadis and create EAPOL flows for the
149 // UNI ports
150 Iterable<Device> devices = deviceService.getDevices();
151 for (Device d : devices) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700152 checkAndCreateDeviceFlows(d);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100153 }
alshabib4ceaed32016-03-03 18:00:58 -0800154
alshabibba357492016-01-27 13:49:46 -0800155 deviceService.addListener(deviceListener);
156
alshabibf0e7e702015-05-30 18:22:36 -0700157 log.info("Started with Application ID {}", appId.id());
158 }
159
160 @Deactivate
161 public void deactivate() {
alshabibe0559672016-02-21 14:49:51 -0800162 componentConfigService.unregisterProperties(getClass(), false);
alshabib62e9ce72016-02-11 17:31:36 -0800163 deviceService.removeListener(deviceListener);
Jonathan Hart5f1c8142018-07-24 17:31:59 -0700164 eventDispatcher.removeSink(AccessDeviceEvent.class);
alshabibf0e7e702015-05-30 18:22:36 -0700165 log.info("Stopped");
166 }
167
alshabibe0559672016-02-21 14:49:51 -0800168 @Modified
169 public void modified(ComponentContext context) {
170 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
171
172 try {
173 String s = get(properties, "defaultVlan");
174 defaultVlan = isNullOrEmpty(s) ? DEFAULT_VLAN : Integer.parseInt(s.trim());
Amit Ghosh95e2f652017-08-23 12:49:46 +0100175
Matt Jeanneret3f579262018-06-14 17:16:23 -0400176 Boolean o = Tools.isPropertyEnabled(properties, "enableDhcpOnProvisioning");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100177 if (o != null) {
Matt Jeanneret3f579262018-06-14 17:16:23 -0400178 enableDhcpOnProvisioning = o;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100179 }
Matt Jeanneret3f579262018-06-14 17:16:23 -0400180
181 Boolean p = Tools.isPropertyEnabled(properties, "enableIgmpOnProvisioning");
182 if (p != null) {
183 enableIgmpOnProvisioning = p;
184 }
185
alshabibe0559672016-02-21 14:49:51 -0800186 } catch (Exception e) {
187 defaultVlan = DEFAULT_VLAN;
188 }
189 }
190
alshabib32232c82016-02-25 17:57:24 -0500191 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100192 public void provisionSubscriber(ConnectPoint port) {
Jonathan Hart94b90492018-04-24 14:02:25 -0700193 checkNotNull(deviceService.getPort(port.deviceId(), port.port()),
194 "Invalid connect point");
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100195 // Find the subscriber on this connect point
196 SubscriberAndDeviceInformation sub = getSubscriber(port);
197 if (sub == null) {
198 log.warn("No subscriber found for {}", port);
199 return;
200 }
Jonathan Harte533a422015-10-20 17:31:24 -0700201
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100202 // Get the uplink port
203 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
204 if (uplinkPort == null) {
205 log.warn("No uplink port found for OLT device {}", port.deviceId());
206 return;
Jonathan Harte533a422015-10-20 17:31:24 -0700207 }
208
Matt Jeanneret3f579262018-06-14 17:16:23 -0400209 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100210 processDhcpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100211 }
212
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100213 Optional<VlanId> defaultVlan = Optional.empty();
214 provisionVlans(port.deviceId(), uplinkPort.number(), port.port(), sub.cTag(), sub.sTag(),
215 defaultVlan);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100216
Matt Jeanneret3f579262018-06-14 17:16:23 -0400217 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100218 processIgmpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100219 }
alshabibb7a9e172016-01-13 11:23:53 -0800220 }
221
222 @Override
223 public void removeSubscriber(ConnectPoint port) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100224 // Get the subscriber connected to this port from Sadis
225 SubscriberAndDeviceInformation subscriber = getSubscriber(port);
226 if (subscriber == null) {
227 log.warn("Subscriber on port {} not found", port);
alshabibbf23a1f2016-01-14 17:27:11 -0800228 return;
229 }
230
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100231 // Get the uplink port
232 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
233 if (uplinkPort == null) {
234 log.warn("No uplink port found for OLT device {}", port.deviceId());
alshabib4ceaed32016-03-03 18:00:58 -0800235 return;
236 }
237
Matt Jeanneret3f579262018-06-14 17:16:23 -0400238 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100239 processDhcpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100240 }
241
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100242 Optional<VlanId> defaultVlan = Optional.empty();
243 unprovisionSubscriber(port.deviceId(), uplinkPort.number(), port.port(), subscriber.cTag(),
244 subscriber.sTag(), defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800245
Matt Jeanneret3f579262018-06-14 17:16:23 -0400246 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100247 processIgmpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100248 }
alshabibbf23a1f2016-01-14 17:27:11 -0800249 }
250
Amit Ghosh95e2f652017-08-23 12:49:46 +0100251
alshabibe0559672016-02-21 14:49:51 -0800252 @Override
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800253 public Collection<Map.Entry<ConnectPoint, VlanId>> getSubscribers() {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100254 ArrayList<Map.Entry<ConnectPoint, VlanId>> subs = new ArrayList<>();
255
256 // Get the subscribers for all the devices
257 // If the port is UNI, is enabled and exists in Sadis then copy it
258 for (Device d : deviceService.getDevices()) {
259 for (Port p: deviceService.getPorts(d.id())) {
260 if (isUniPort(d, p) && p.isEnabled()) {
261 ConnectPoint cp = new ConnectPoint(d.id(), p.number());
262
263 SubscriberAndDeviceInformation sub = getSubscriber(cp);
264 if (sub != null) {
265 subs.add(new AbstractMap.SimpleEntry(cp, sub.cTag()));
266 }
267 }
268 }
269 }
270
271 return subs;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800272 }
273
274 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100275 public List<DeviceId> fetchOlts() {
276 // look through all the devices and find the ones that are OLTs as per Sadis
277 List<DeviceId> olts = new ArrayList<>();
278 Iterable<Device> devices = deviceService.getDevices();
279 for (Device d : devices) {
280 String devSerialNo = d.serialNumber();
281 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
282
283 if (deviceInfo != null) {
284 // So this is indeed a OLT device
285 olts.add(d.id());
286 }
287 }
288 return olts;
alshabibe0559672016-02-21 14:49:51 -0800289 }
290
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700291 private void initializeUni(Port port) {
292 DeviceId deviceId = (DeviceId) port.element().id();
293
294 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, deviceId, port));
295
296 if (port.isEnabled()) {
297 processFilteringObjectives(deviceId, port.number(), true);
298 }
299 }
300
alshabibbf23a1f2016-01-14 17:27:11 -0800301 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
alshabib4ceaed32016-03-03 18:00:58 -0800302 PortNumber subscriberPort, VlanId subscriberVlan,
303 VlanId deviceVlan, Optional<VlanId> defaultVlan) {
alshabibbf23a1f2016-01-14 17:27:11 -0800304
305 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
306 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
307
alshabib4ceaed32016-03-03 18:00:58 -0800308 ForwardingObjective.Builder upFwd = upBuilder(uplink, subscriberPort,
309 subscriberVlan, deviceVlan,
310 defaultVlan);
311 ForwardingObjective.Builder downFwd = downBuilder(uplink, subscriberPort,
312 subscriberVlan, deviceVlan,
313 defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800314
315
alshabib4ceaed32016-03-03 18:00:58 -0800316 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
317 @Override
318 public void onSuccess(Objective objective) {
319 upFuture.complete(null);
320 }
alshabibbf23a1f2016-01-14 17:27:11 -0800321
alshabib4ceaed32016-03-03 18:00:58 -0800322 @Override
323 public void onError(Objective objective, ObjectiveError error) {
324 upFuture.complete(error);
325 }
326 }));
327
328 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
329 @Override
330 public void onSuccess(Objective objective) {
331 downFuture.complete(null);
332 }
333
334 @Override
335 public void onError(Objective objective, ObjectiveError error) {
336 downFuture.complete(error);
337 }
338 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800339
340 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
341 if (upStatus == null && downStatus == null) {
342 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
343 deviceId,
344 deviceVlan,
345 subscriberVlan));
346 } else if (downStatus != null) {
347 log.error("Subscriber with vlan {} on device {} " +
348 "on port {} failed downstream uninstallation: {}",
349 subscriberVlan, deviceId, subscriberPort, downStatus);
350 } else if (upStatus != null) {
351 log.error("Subscriber with vlan {} on device {} " +
352 "on port {} failed upstream uninstallation: {}",
353 subscriberVlan, deviceId, subscriberPort, upStatus);
354 }
355 }, oltInstallers);
alshabibb7a9e172016-01-13 11:23:53 -0800356
Jonathan Harte533a422015-10-20 17:31:24 -0700357 }
358
359 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
360 PortNumber subscriberPort,
Jonathan Hart52998382015-11-10 16:09:22 -0800361 VlanId subscriberVlan, VlanId deviceVlan,
362 Optional<VlanId> defaultVlan) {
Jonathan Harte533a422015-10-20 17:31:24 -0700363
alshabib3ea82642016-01-12 18:06:53 -0800364 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
365 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
366
alshabib4ceaed32016-03-03 18:00:58 -0800367 ForwardingObjective.Builder upFwd = upBuilder(uplinkPort, subscriberPort,
368 subscriberVlan, deviceVlan,
369 defaultVlan);
Jonathan Harte533a422015-10-20 17:31:24 -0700370
371
alshabib4ceaed32016-03-03 18:00:58 -0800372 ForwardingObjective.Builder downFwd = downBuilder(uplinkPort, subscriberPort,
373 subscriberVlan, deviceVlan,
374 defaultVlan);
alshabib3ea82642016-01-12 18:06:53 -0800375
alshabibbf23a1f2016-01-14 17:27:11 -0800376 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
377 @Override
378 public void onSuccess(Objective objective) {
379 upFuture.complete(null);
380 }
381
382 @Override
383 public void onError(Objective objective, ObjectiveError error) {
384 upFuture.complete(error);
385 }
386 }));
387
alshabibbf23a1f2016-01-14 17:27:11 -0800388 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
389 @Override
390 public void onSuccess(Objective objective) {
391 downFuture.complete(null);
392 }
393
394 @Override
395 public void onError(Objective objective, ObjectiveError error) {
396 downFuture.complete(error);
397 }
398 }));
alshabib3ea82642016-01-12 18:06:53 -0800399
400 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
401 if (upStatus == null && downStatus == null) {
402 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
403 deviceId,
404 deviceVlan,
405 subscriberVlan));
alshabib50d9fc52016-02-12 15:47:20 -0800406
alshabib3ea82642016-01-12 18:06:53 -0800407 } else if (downStatus != null) {
408 log.error("Subscriber with vlan {} on device {} " +
409 "on port {} failed downstream installation: {}",
410 subscriberVlan, deviceId, subscriberPort, downStatus);
411 } else if (upStatus != null) {
412 log.error("Subscriber with vlan {} on device {} " +
413 "on port {} failed upstream installation: {}",
414 subscriberVlan, deviceId, subscriberPort, upStatus);
415 }
416 }, oltInstallers);
417
Jonathan Harte533a422015-10-20 17:31:24 -0700418 }
419
alshabib4ceaed32016-03-03 18:00:58 -0800420 private ForwardingObjective.Builder downBuilder(PortNumber uplinkPort,
421 PortNumber subscriberPort,
422 VlanId subscriberVlan,
423 VlanId deviceVlan,
424 Optional<VlanId> defaultVlan) {
425 TrafficSelector downstream = DefaultTrafficSelector.builder()
426 .matchVlanId(deviceVlan)
427 .matchInPort(uplinkPort)
428 .matchInnerVlanId(subscriberVlan)
429 .build();
430
431 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
432 .popVlan()
433 .setVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
434 .setOutput(subscriberPort)
435 .build();
436
437 return DefaultForwardingObjective.builder()
438 .withFlag(ForwardingObjective.Flag.VERSATILE)
439 .withPriority(1000)
440 .makePermanent()
441 .withSelector(downstream)
442 .fromApp(appId)
443 .withTreatment(downstreamTreatment);
444 }
445
446 private ForwardingObjective.Builder upBuilder(PortNumber uplinkPort,
447 PortNumber subscriberPort,
448 VlanId subscriberVlan,
449 VlanId deviceVlan,
450 Optional<VlanId> defaultVlan) {
451 TrafficSelector upstream = DefaultTrafficSelector.builder()
452 .matchVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
453 .matchInPort(subscriberPort)
454 .build();
455
456
457 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
458 .pushVlan()
459 .setVlanId(subscriberVlan)
460 .pushVlan()
461 .setVlanId(deviceVlan)
462 .setOutput(uplinkPort)
463 .build();
464
465 return DefaultForwardingObjective.builder()
466 .withFlag(ForwardingObjective.Flag.VERSATILE)
467 .withPriority(1000)
468 .makePermanent()
469 .withSelector(upstream)
470 .fromApp(appId)
471 .withTreatment(upstreamTreatment);
472 }
473
alshabib50d9fc52016-02-12 15:47:20 -0800474 private void processFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
alshabib09753b52016-03-04 14:55:19 -0800475 if (!mastershipService.isLocalMaster(devId)) {
476 return;
477 }
alshabibbb83aa22016-02-10 15:08:23 -0800478 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
479
480 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
alshabib50d9fc52016-02-12 15:47:20 -0800481 .withKey(Criteria.matchInPort(port))
alshabibdec2e252016-01-15 12:20:25 -0800482 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
483 .withMeta(DefaultTrafficTreatment.builder()
484 .setOutput(PortNumber.CONTROLLER).build())
485 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400486 .withPriority(10000)
alshabibdec2e252016-01-15 12:20:25 -0800487 .add(new ObjectiveContext() {
488 @Override
489 public void onSuccess(Objective objective) {
490 log.info("Eapol filter for {} on {} installed.",
491 devId, port);
492 }
493
494 @Override
495 public void onError(Objective objective, ObjectiveError error) {
496 log.info("Eapol filter for {} on {} failed because {}",
497 devId, port, error);
498 }
499 });
500
alshabibdec2e252016-01-15 12:20:25 -0800501 flowObjectiveService.filter(devId, eapol);
alshabib000b6fc2016-02-01 17:25:00 -0800502
alshabibdec2e252016-01-15 12:20:25 -0800503 }
504
Jonathan Hart403372d2018-08-22 11:44:13 -0700505 /**
506 * Installs trap filtering objectives for particular traffic types on an
507 * NNI port.
508 *
509 * @param devId device ID
510 * @param port port number
511 * @param install true to install, false to remove
512 */
513 private void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
514 processLldpFilteringObjective(devId, port, install);
515 processDhcpFilteringObjectives(devId, port, install);
516 }
517
518 private void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
519 if (!mastershipService.isLocalMaster(devId)) {
520 return;
521 }
522 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
523
524 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
525 .withKey(Criteria.matchInPort(port))
526 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
527 .withMeta(DefaultTrafficTreatment.builder()
528 .setOutput(PortNumber.CONTROLLER).build())
529 .fromApp(appId)
530 .withPriority(10000)
531 .add(new ObjectiveContext() {
532 @Override
533 public void onSuccess(Objective objective) {
534 log.info("LLDP filter for {} on {} installed.",
535 devId, port);
536 }
537
538 @Override
539 public void onError(Objective objective, ObjectiveError error) {
540 log.info("LLDP filter for {} on {} failed because {}",
541 devId, port, error);
542 }
543 });
544
545 flowObjectiveService.filter(devId, lldp);
546
547 }
548
Amit Ghosh95e2f652017-08-23 12:49:46 +0100549 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
550 if (!mastershipService.isLocalMaster(devId)) {
551 return;
552 }
553 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
554
555 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
556 .withKey(Criteria.matchInPort(port))
557 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
558 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
559 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
560 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
561 .withMeta(DefaultTrafficTreatment.builder()
562 .setOutput(PortNumber.CONTROLLER).build())
563 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400564 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100565 .add(new ObjectiveContext() {
566 @Override
567 public void onSuccess(Objective objective) {
568 log.info("DHCP filter for {} on {} installed.",
569 devId, port);
570 }
571
572 @Override
573 public void onError(Objective objective, ObjectiveError error) {
574 log.info("DHCP filter for {} on {} failed because {}",
575 devId, port, error);
576 }
577 });
578
579 flowObjectiveService.filter(devId, dhcpUpstream);
580 }
581
582 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
583 if (!mastershipService.isLocalMaster(devId)) {
584 return;
585 }
586
587 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
588
589 builder = install ? builder.permit() : builder.deny();
590
591 FilteringObjective igmp = builder
592 .withKey(Criteria.matchInPort(port))
593 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
594 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
595 .withMeta(DefaultTrafficTreatment.builder()
596 .setOutput(PortNumber.CONTROLLER).build())
597 .fromApp(appId)
598 .withPriority(10000)
599 .add(new ObjectiveContext() {
600 @Override
601 public void onSuccess(Objective objective) {
602 log.info("Igmp filter for {} on {} installed.",
603 devId, port);
604 }
605
606 @Override
607 public void onError(Objective objective, ObjectiveError error) {
608 log.info("Igmp filter for {} on {} failed because {}.",
609 devId, port, error);
610 }
611 });
612
613 flowObjectiveService.filter(devId, igmp);
614 }
615
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100616 /**
Jonathan Hart403372d2018-08-22 11:44:13 -0700617 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
618 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100619 *
620 * @param dev Device to look for
621 */
Jonathan Hart403372d2018-08-22 11:44:13 -0700622 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100623 // we create only for the ones we are master of
624 if (!mastershipService.isLocalMaster(dev.id())) {
625 return;
626 }
627 // check if this device is provisioned in Sadis
628 String devSerialNo = dev.serialNumber();
629 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
Jonathan Hart403372d2018-08-22 11:44:13 -0700630 log.debug("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100631
632 if (deviceInfo != null) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700633 // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100634 for (Port p : deviceService.getPorts(dev.id())) {
635 if (isUniPort(dev, p)) {
636 processFilteringObjectives(dev.id(), p.number(), true);
Jonathan Hart403372d2018-08-22 11:44:13 -0700637 } else {
638 processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100639 }
640 }
641 }
642 }
643
Jonathan Hart403372d2018-08-22 11:44:13 -0700644
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100645 /**
646 * Get the uplink for of the OLT device.
647 *
648 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
649 * this logic needs to be changed
650 *
651 * @param dev Device to look for
652 * @return The uplink Port of the OLT
653 */
654 private Port getUplinkPort(Device dev) {
655 // check if this device is provisioned in Sadis
656 String devSerialNo = dev.serialNumber();
657 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
658 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
659
660 if (deviceInfo != null) {
661 // Return the port that has been configured as the uplink port of this OLT in Sadis
662 for (Port p: deviceService.getPorts(dev.id())) {
663 if (p.number().toLong() == deviceInfo.uplinkPort()) {
664 log.debug("getUplinkPort: Found port {}", p);
665 return p;
666 }
667 }
668 }
669
670 log.debug("getUplinkPort: No uplink port found for OLT {}", dev);
671 return null;
672 }
673
674 /**
675 * Return the subscriber on a port.
676 *
677 * @param port On which to find the subscriber
678 * @return subscriber if found else null
679 */
680 private SubscriberAndDeviceInformation getSubscriber(ConnectPoint port) {
681 String portName = deviceService.getPort(port).annotations()
682 .value(AnnotationKeys.PORT_NAME);
683
684 return subsService.get(portName);
685 }
686
687 private boolean isUniPort(Device d, Port p) {
688 Port ulPort = getUplinkPort(d);
689 if (ulPort != null) {
690 return (ulPort.number().toLong() != p.number().toLong());
691 }
692 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700693 }
694
Jonathan Hart4c538002018-08-23 10:11:54 -0700695 private SubscriberAndDeviceInformation getOltInfo(Device dev) {
696 String devSerialNo = dev.serialNumber();
697 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
698 return deviceInfo;
699 }
700
alshabibf0e7e702015-05-30 18:22:36 -0700701 private class InternalDeviceListener implements DeviceListener {
702 @Override
703 public void event(DeviceEvent event) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700704 eventExecutor.execute(() -> {
705 DeviceId devId = event.subject().id();
706 Device dev = event.subject();
Jonathan Hart4c538002018-08-23 10:11:54 -0700707
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700708 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
709 return;
710 }
Jonathan Hart4c538002018-08-23 10:11:54 -0700711
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700712 if (getOltInfo(dev) == null) {
713 log.debug("No device info found, this is not an OLT");
714 return;
715 }
Jonathan Hart4c538002018-08-23 10:11:54 -0700716
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700717 log.debug("OLT got {} event for {}", event.type(), event.subject());
Jonathan Hart4c538002018-08-23 10:11:54 -0700718
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700719 switch (event.type()) {
720 //TODO: Port handling and bookkeeping should be improved once
721 // olt firmware handles correct behaviour.
722 case PORT_ADDED:
723 if (isUniPort(dev, event.port())) {
724 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
Jonathan Hart4c538002018-08-23 10:11:54 -0700725
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700726 if (event.port().isEnabled()) {
727 processFilteringObjectives(devId, event.port().number(), true);
728 }
729 } else {
730 checkAndCreateDeviceFlows(dev);
731 }
732 break;
733 case PORT_REMOVED:
734 if (isUniPort(dev, event.port())) {
735 if (event.port().isEnabled()) {
736 processFilteringObjectives(devId, event.port().number(), false);
737 removeSubscriber(new ConnectPoint(devId, event.port().number()));
738 }
739
740 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
741 }
742
743 break;
744 case PORT_UPDATED:
745 if (!isUniPort(dev, event.port())) {
746 break;
747 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700748
749 if (event.port().isEnabled()) {
750 processFilteringObjectives(devId, event.port().number(), true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700751 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
752 } else {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700753 processFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700754 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700755 }
alshabibbb83aa22016-02-10 15:08:23 -0800756 break;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700757 case DEVICE_ADDED:
alshabib7c190012016-02-09 18:22:33 -0800758 post(new AccessDeviceEvent(
759 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
760 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700761
762 // Send UNI_ADDED events for all existing ports
763 deviceService.getPorts(devId).stream()
764 .filter(p -> isUniPort(dev, p))
765 .filter(Port::isEnabled)
766 .forEach(p -> post(new AccessDeviceEvent(
767 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
768
Jonathan Hart403372d2018-08-22 11:44:13 -0700769 checkAndCreateDeviceFlows(dev);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700770 break;
771 case DEVICE_REMOVED:
772 deviceService.getPorts(devId).stream()
773 .filter(p -> isUniPort(dev, p))
774 .forEach(p -> post(new AccessDeviceEvent(
775 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
776
alshabib7c190012016-02-09 18:22:33 -0800777 post(new AccessDeviceEvent(
778 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
779 null, null));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700780 break;
781 case DEVICE_AVAILABILITY_CHANGED:
782 if (deviceService.isAvailable(devId)) {
783 post(new AccessDeviceEvent(
784 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
785 null, null));
786 checkAndCreateDeviceFlows(dev);
787 } else {
788 post(new AccessDeviceEvent(
789 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
790 null, null));
791 }
792 break;
793 case DEVICE_UPDATED:
794 case DEVICE_SUSPENDED:
795 case PORT_STATS_UPDATED:
796 default:
797 return;
798 }
799 });
alshabibf0e7e702015-05-30 18:22:36 -0700800 }
801 }
alshabibf0e7e702015-05-30 18:22:36 -0700802}