blob: 345ef4743a5f73600dd1befe2f9cfb9bbec0bd5e [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;
Jonathan Harte533a422015-10-20 17:31:24 -070042import org.onosproject.net.config.ConfigFactory;
43import org.onosproject.net.config.NetworkConfigEvent;
44import org.onosproject.net.config.NetworkConfigListener;
45import org.onosproject.net.config.NetworkConfigRegistry;
46import org.onosproject.net.config.basics.SubjectFactories;
alshabibf0e7e702015-05-30 18:22:36 -070047import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
50import org.onosproject.net.flow.DefaultTrafficSelector;
51import org.onosproject.net.flow.DefaultTrafficTreatment;
52import org.onosproject.net.flow.TrafficSelector;
53import org.onosproject.net.flow.TrafficTreatment;
alshabibdec2e252016-01-15 12:20:25 -080054import org.onosproject.net.flow.criteria.Criteria;
55import org.onosproject.net.flowobjective.DefaultFilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070056import org.onosproject.net.flowobjective.DefaultForwardingObjective;
alshabibdec2e252016-01-15 12:20:25 -080057import org.onosproject.net.flowobjective.FilteringObjective;
alshabibf0e7e702015-05-30 18:22:36 -070058import org.onosproject.net.flowobjective.FlowObjectiveService;
59import org.onosproject.net.flowobjective.ForwardingObjective;
alshabib3ea82642016-01-12 18:06:53 -080060import org.onosproject.net.flowobjective.Objective;
61import org.onosproject.net.flowobjective.ObjectiveContext;
62import org.onosproject.net.flowobjective.ObjectiveError;
alshabibf3a573e2016-06-01 17:39:10 -070063import org.opencord.cordconfig.access.AccessDeviceConfig;
alshabib36a4d732016-06-01 16:03:59 -070064import org.opencord.olt.AccessDeviceEvent;
65import org.opencord.olt.AccessDeviceListener;
66import org.opencord.olt.AccessDeviceService;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010067import org.opencord.sadis.SubscriberAndDeviceInformation;
68import org.opencord.sadis.SubscriberAndDeviceInformationService;
alshabibe0559672016-02-21 14:49:51 -080069import org.osgi.service.component.ComponentContext;
alshabibf0e7e702015-05-30 18:22:36 -070070import org.slf4j.Logger;
71
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010072import java.util.AbstractMap;
73import java.util.ArrayList;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -080074import java.util.Collection;
alshabibe0559672016-02-21 14:49:51 -080075import java.util.Dictionary;
alshabibbb83aa22016-02-10 15:08:23 -080076import java.util.List;
Jonathan Harte533a422015-10-20 17:31:24 -070077import java.util.Map;
Jonathan Hart52998382015-11-10 16:09:22 -080078import java.util.Optional;
alshabibe0559672016-02-21 14:49:51 -080079import java.util.Properties;
alshabib3ea82642016-01-12 18:06:53 -080080import java.util.concurrent.CompletableFuture;
alshabib3ea82642016-01-12 18:06:53 -080081import java.util.concurrent.ExecutorService;
82import java.util.concurrent.Executors;
alshabibf0e7e702015-05-30 18:22:36 -070083
Jonathan Hart94b90492018-04-24 14:02:25 -070084import static com.google.common.base.Preconditions.checkNotNull;
alshabibe0559672016-02-21 14:49:51 -080085import static com.google.common.base.Strings.isNullOrEmpty;
86import static org.onlab.util.Tools.get;
alshabib3ea82642016-01-12 18:06:53 -080087import static org.onlab.util.Tools.groupedThreads;
alshabibf0e7e702015-05-30 18:22:36 -070088import static org.slf4j.LoggerFactory.getLogger;
89
90/**
Jonathan Harte533a422015-10-20 17:31:24 -070091 * Provisions rules on access devices.
alshabibf0e7e702015-05-30 18:22:36 -070092 */
Jonathan Harte533a422015-10-20 17:31:24 -070093@Service
alshabibf0e7e702015-05-30 18:22:36 -070094@Component(immediate = true)
alshabib8e4fd2f2016-01-12 15:55:53 -080095public class Olt
96 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
97 implements AccessDeviceService {
Charles Chan54f110f2017-01-20 11:22:42 -080098 private static final String APP_NAME = "org.opencord.olt";
alshabibe0559672016-02-21 14:49:51 -080099
100 private static final short DEFAULT_VLAN = 0;
101
alshabibf0e7e702015-05-30 18:22:36 -0700102 private final Logger log = getLogger(getClass());
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected FlowObjectiveService flowObjectiveService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib09753b52016-03-04 14:55:19 -0800108 protected MastershipService mastershipService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibf0e7e702015-05-30 18:22:36 -0700111 protected DeviceService deviceService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected CoreService coreService;
115
Jonathan Harte533a422015-10-20 17:31:24 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected NetworkConfigRegistry networkConfig;
118
alshabibe0559672016-02-21 14:49:51 -0800119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected ComponentConfigService componentConfigService;
121
alshabib4ceaed32016-03-03 18:00:58 -0800122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100123 protected SubscriberAndDeviceInformationService subsService;
alshabibe0559672016-02-21 14:49:51 -0800124
125 @Property(name = "defaultVlan", intValue = DEFAULT_VLAN,
126 label = "Default VLAN RG<->ONU traffic")
127 private int defaultVlan = DEFAULT_VLAN;
128
Matt Jeanneret3f579262018-06-14 17:16:23 -0400129 @Property(name = "enableDhcpOnProvisioning", boolValue = true,
130 label = "Create the DHCP Flow rules when a subscriber is provisioned")
131 protected boolean enableDhcpOnProvisioning = false;
132
133 @Property(name = "enableIgmpOnProvisioning", boolValue = false,
134 label = "Create IGMP Flow rules when a subscriber is provisioned")
135 protected boolean enableIgmpOnProvisioning = false;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100136
alshabibf0e7e702015-05-30 18:22:36 -0700137 private final DeviceListener deviceListener = new InternalDeviceListener();
138
139 private ApplicationId appId;
140
alshabib3ea82642016-01-12 18:06:53 -0800141 private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
alshabibbf23a1f2016-01-14 17:27:11 -0800142 groupedThreads("onos/olt-service",
143 "olt-installer-%d"));
Jonathan Harte533a422015-10-20 17:31:24 -0700144 private InternalNetworkConfigListener configListener =
145 new InternalNetworkConfigListener();
146 private static final Class<AccessDeviceConfig> CONFIG_CLASS =
147 AccessDeviceConfig.class;
148
149 private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory =
150 new ConfigFactory<DeviceId, AccessDeviceConfig>(
151 SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
alshabibbf23a1f2016-01-14 17:27:11 -0800152 @Override
153 public AccessDeviceConfig createConfig() {
154 return new AccessDeviceConfig();
155 }
156 };
157
alshabibf0e7e702015-05-30 18:22:36 -0700158
159 @Activate
alshabibe0559672016-02-21 14:49:51 -0800160 public void activate(ComponentContext context) {
161 modified(context);
Charles Chan54f110f2017-01-20 11:22:42 -0800162 appId = coreService.registerApplication(APP_NAME);
alshabibe0559672016-02-21 14:49:51 -0800163 componentConfigService.registerProperties(getClass());
alshabibc4dfe852015-06-05 13:35:13 -0700164
alshabib8e4fd2f2016-01-12 15:55:53 -0800165 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
166
Jonathan Harte533a422015-10-20 17:31:24 -0700167 networkConfig.registerConfigFactory(configFactory);
168 networkConfig.addListener(configListener);
169
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100170 // look for all provisioned devices in Sadis and create EAPOL flows for the
171 // UNI ports
172 Iterable<Device> devices = deviceService.getDevices();
173 for (Device d : devices) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700174 checkAndCreateDeviceFlows(d);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100175 }
alshabib4ceaed32016-03-03 18:00:58 -0800176
alshabibba357492016-01-27 13:49:46 -0800177 deviceService.addListener(deviceListener);
178
alshabibf0e7e702015-05-30 18:22:36 -0700179 log.info("Started with Application ID {}", appId.id());
180 }
181
182 @Deactivate
183 public void deactivate() {
alshabibe0559672016-02-21 14:49:51 -0800184 componentConfigService.unregisterProperties(getClass(), false);
alshabib62e9ce72016-02-11 17:31:36 -0800185 deviceService.removeListener(deviceListener);
Jonathan Harte533a422015-10-20 17:31:24 -0700186 networkConfig.removeListener(configListener);
187 networkConfig.unregisterConfigFactory(configFactory);
Jonathan Hart5f1c8142018-07-24 17:31:59 -0700188 eventDispatcher.removeSink(AccessDeviceEvent.class);
alshabibf0e7e702015-05-30 18:22:36 -0700189 log.info("Stopped");
190 }
191
alshabibe0559672016-02-21 14:49:51 -0800192 @Modified
193 public void modified(ComponentContext context) {
194 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
195
196 try {
197 String s = get(properties, "defaultVlan");
198 defaultVlan = isNullOrEmpty(s) ? DEFAULT_VLAN : Integer.parseInt(s.trim());
Amit Ghosh95e2f652017-08-23 12:49:46 +0100199
Matt Jeanneret3f579262018-06-14 17:16:23 -0400200 Boolean o = Tools.isPropertyEnabled(properties, "enableDhcpOnProvisioning");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100201 if (o != null) {
Matt Jeanneret3f579262018-06-14 17:16:23 -0400202 enableDhcpOnProvisioning = o;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100203 }
Matt Jeanneret3f579262018-06-14 17:16:23 -0400204
205 Boolean p = Tools.isPropertyEnabled(properties, "enableIgmpOnProvisioning");
206 if (p != null) {
207 enableIgmpOnProvisioning = p;
208 }
209
alshabibe0559672016-02-21 14:49:51 -0800210 } catch (Exception e) {
211 defaultVlan = DEFAULT_VLAN;
212 }
213 }
214
alshabib32232c82016-02-25 17:57:24 -0500215 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100216 public void provisionSubscriber(ConnectPoint port) {
Jonathan Hart94b90492018-04-24 14:02:25 -0700217 checkNotNull(deviceService.getPort(port.deviceId(), port.port()),
218 "Invalid connect point");
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100219 // Find the subscriber on this connect point
220 SubscriberAndDeviceInformation sub = getSubscriber(port);
221 if (sub == null) {
222 log.warn("No subscriber found for {}", port);
223 return;
224 }
Jonathan Harte533a422015-10-20 17:31:24 -0700225
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100226 // Get the uplink port
227 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
228 if (uplinkPort == null) {
229 log.warn("No uplink port found for OLT device {}", port.deviceId());
230 return;
Jonathan Harte533a422015-10-20 17:31:24 -0700231 }
232
Matt Jeanneret3f579262018-06-14 17:16:23 -0400233 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100234 processDhcpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100235 }
236
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100237 Optional<VlanId> defaultVlan = Optional.empty();
238 provisionVlans(port.deviceId(), uplinkPort.number(), port.port(), sub.cTag(), sub.sTag(),
239 defaultVlan);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100240
Matt Jeanneret3f579262018-06-14 17:16:23 -0400241 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100242 processIgmpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100243 }
alshabibb7a9e172016-01-13 11:23:53 -0800244 }
245
246 @Override
247 public void removeSubscriber(ConnectPoint port) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100248 // Get the subscriber connected to this port from Sadis
249 SubscriberAndDeviceInformation subscriber = getSubscriber(port);
250 if (subscriber == null) {
251 log.warn("Subscriber on port {} not found", port);
alshabibbf23a1f2016-01-14 17:27:11 -0800252 return;
253 }
254
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100255 // Get the uplink port
256 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
257 if (uplinkPort == null) {
258 log.warn("No uplink port found for OLT device {}", port.deviceId());
alshabib4ceaed32016-03-03 18:00:58 -0800259 return;
260 }
261
Matt Jeanneret3f579262018-06-14 17:16:23 -0400262 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100263 processDhcpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100264 }
265
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100266 Optional<VlanId> defaultVlan = Optional.empty();
267 unprovisionSubscriber(port.deviceId(), uplinkPort.number(), port.port(), subscriber.cTag(),
268 subscriber.sTag(), defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800269
Matt Jeanneret3f579262018-06-14 17:16:23 -0400270 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100271 processIgmpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100272 }
alshabibbf23a1f2016-01-14 17:27:11 -0800273 }
274
Amit Ghosh95e2f652017-08-23 12:49:46 +0100275
alshabibe0559672016-02-21 14:49:51 -0800276 @Override
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800277 public Collection<Map.Entry<ConnectPoint, VlanId>> getSubscribers() {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100278 ArrayList<Map.Entry<ConnectPoint, VlanId>> subs = new ArrayList<>();
279
280 // Get the subscribers for all the devices
281 // If the port is UNI, is enabled and exists in Sadis then copy it
282 for (Device d : deviceService.getDevices()) {
283 for (Port p: deviceService.getPorts(d.id())) {
284 if (isUniPort(d, p) && p.isEnabled()) {
285 ConnectPoint cp = new ConnectPoint(d.id(), p.number());
286
287 SubscriberAndDeviceInformation sub = getSubscriber(cp);
288 if (sub != null) {
289 subs.add(new AbstractMap.SimpleEntry(cp, sub.cTag()));
290 }
291 }
292 }
293 }
294
295 return subs;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800296 }
297
298 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100299 public List<DeviceId> fetchOlts() {
300 // look through all the devices and find the ones that are OLTs as per Sadis
301 List<DeviceId> olts = new ArrayList<>();
302 Iterable<Device> devices = deviceService.getDevices();
303 for (Device d : devices) {
304 String devSerialNo = d.serialNumber();
305 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
306
307 if (deviceInfo != null) {
308 // So this is indeed a OLT device
309 olts.add(d.id());
310 }
311 }
312 return olts;
alshabibe0559672016-02-21 14:49:51 -0800313 }
314
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700315 private void initializeUni(Port port) {
316 DeviceId deviceId = (DeviceId) port.element().id();
317
318 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, deviceId, port));
319
320 if (port.isEnabled()) {
321 processFilteringObjectives(deviceId, port.number(), true);
322 }
323 }
324
alshabibbf23a1f2016-01-14 17:27:11 -0800325 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
alshabib4ceaed32016-03-03 18:00:58 -0800326 PortNumber subscriberPort, VlanId subscriberVlan,
327 VlanId deviceVlan, Optional<VlanId> defaultVlan) {
alshabibbf23a1f2016-01-14 17:27:11 -0800328
329 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
330 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
331
alshabib4ceaed32016-03-03 18:00:58 -0800332 ForwardingObjective.Builder upFwd = upBuilder(uplink, subscriberPort,
333 subscriberVlan, deviceVlan,
334 defaultVlan);
335 ForwardingObjective.Builder downFwd = downBuilder(uplink, subscriberPort,
336 subscriberVlan, deviceVlan,
337 defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800338
339
alshabib4ceaed32016-03-03 18:00:58 -0800340 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
341 @Override
342 public void onSuccess(Objective objective) {
343 upFuture.complete(null);
344 }
alshabibbf23a1f2016-01-14 17:27:11 -0800345
alshabib4ceaed32016-03-03 18:00:58 -0800346 @Override
347 public void onError(Objective objective, ObjectiveError error) {
348 upFuture.complete(error);
349 }
350 }));
351
352 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
353 @Override
354 public void onSuccess(Objective objective) {
355 downFuture.complete(null);
356 }
357
358 @Override
359 public void onError(Objective objective, ObjectiveError error) {
360 downFuture.complete(error);
361 }
362 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800363
364 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
365 if (upStatus == null && downStatus == null) {
366 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
367 deviceId,
368 deviceVlan,
369 subscriberVlan));
370 } else if (downStatus != null) {
371 log.error("Subscriber with vlan {} on device {} " +
372 "on port {} failed downstream uninstallation: {}",
373 subscriberVlan, deviceId, subscriberPort, downStatus);
374 } else if (upStatus != null) {
375 log.error("Subscriber with vlan {} on device {} " +
376 "on port {} failed upstream uninstallation: {}",
377 subscriberVlan, deviceId, subscriberPort, upStatus);
378 }
379 }, oltInstallers);
alshabibb7a9e172016-01-13 11:23:53 -0800380
Jonathan Harte533a422015-10-20 17:31:24 -0700381 }
382
383 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
384 PortNumber subscriberPort,
Jonathan Hart52998382015-11-10 16:09:22 -0800385 VlanId subscriberVlan, VlanId deviceVlan,
386 Optional<VlanId> defaultVlan) {
Jonathan Harte533a422015-10-20 17:31:24 -0700387
alshabib3ea82642016-01-12 18:06:53 -0800388 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
389 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
390
alshabib4ceaed32016-03-03 18:00:58 -0800391 ForwardingObjective.Builder upFwd = upBuilder(uplinkPort, subscriberPort,
392 subscriberVlan, deviceVlan,
393 defaultVlan);
Jonathan Harte533a422015-10-20 17:31:24 -0700394
395
alshabib4ceaed32016-03-03 18:00:58 -0800396 ForwardingObjective.Builder downFwd = downBuilder(uplinkPort, subscriberPort,
397 subscriberVlan, deviceVlan,
398 defaultVlan);
alshabib3ea82642016-01-12 18:06:53 -0800399
alshabibbf23a1f2016-01-14 17:27:11 -0800400 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
401 @Override
402 public void onSuccess(Objective objective) {
403 upFuture.complete(null);
404 }
405
406 @Override
407 public void onError(Objective objective, ObjectiveError error) {
408 upFuture.complete(error);
409 }
410 }));
411
alshabibbf23a1f2016-01-14 17:27:11 -0800412 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
413 @Override
414 public void onSuccess(Objective objective) {
415 downFuture.complete(null);
416 }
417
418 @Override
419 public void onError(Objective objective, ObjectiveError error) {
420 downFuture.complete(error);
421 }
422 }));
alshabib3ea82642016-01-12 18:06:53 -0800423
424 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
425 if (upStatus == null && downStatus == null) {
426 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
427 deviceId,
428 deviceVlan,
429 subscriberVlan));
alshabib50d9fc52016-02-12 15:47:20 -0800430
alshabib3ea82642016-01-12 18:06:53 -0800431 } else if (downStatus != null) {
432 log.error("Subscriber with vlan {} on device {} " +
433 "on port {} failed downstream installation: {}",
434 subscriberVlan, deviceId, subscriberPort, downStatus);
435 } else if (upStatus != null) {
436 log.error("Subscriber with vlan {} on device {} " +
437 "on port {} failed upstream installation: {}",
438 subscriberVlan, deviceId, subscriberPort, upStatus);
439 }
440 }, oltInstallers);
441
Jonathan Harte533a422015-10-20 17:31:24 -0700442 }
443
alshabib4ceaed32016-03-03 18:00:58 -0800444 private ForwardingObjective.Builder downBuilder(PortNumber uplinkPort,
445 PortNumber subscriberPort,
446 VlanId subscriberVlan,
447 VlanId deviceVlan,
448 Optional<VlanId> defaultVlan) {
449 TrafficSelector downstream = DefaultTrafficSelector.builder()
450 .matchVlanId(deviceVlan)
451 .matchInPort(uplinkPort)
452 .matchInnerVlanId(subscriberVlan)
453 .build();
454
455 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
456 .popVlan()
457 .setVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
458 .setOutput(subscriberPort)
459 .build();
460
461 return DefaultForwardingObjective.builder()
462 .withFlag(ForwardingObjective.Flag.VERSATILE)
463 .withPriority(1000)
464 .makePermanent()
465 .withSelector(downstream)
466 .fromApp(appId)
467 .withTreatment(downstreamTreatment);
468 }
469
470 private ForwardingObjective.Builder upBuilder(PortNumber uplinkPort,
471 PortNumber subscriberPort,
472 VlanId subscriberVlan,
473 VlanId deviceVlan,
474 Optional<VlanId> defaultVlan) {
475 TrafficSelector upstream = DefaultTrafficSelector.builder()
476 .matchVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
477 .matchInPort(subscriberPort)
478 .build();
479
480
481 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
482 .pushVlan()
483 .setVlanId(subscriberVlan)
484 .pushVlan()
485 .setVlanId(deviceVlan)
486 .setOutput(uplinkPort)
487 .build();
488
489 return DefaultForwardingObjective.builder()
490 .withFlag(ForwardingObjective.Flag.VERSATILE)
491 .withPriority(1000)
492 .makePermanent()
493 .withSelector(upstream)
494 .fromApp(appId)
495 .withTreatment(upstreamTreatment);
496 }
497
alshabib50d9fc52016-02-12 15:47:20 -0800498 private void processFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
alshabib09753b52016-03-04 14:55:19 -0800499 if (!mastershipService.isLocalMaster(devId)) {
500 return;
501 }
alshabibbb83aa22016-02-10 15:08:23 -0800502 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
503
504 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
alshabib50d9fc52016-02-12 15:47:20 -0800505 .withKey(Criteria.matchInPort(port))
alshabibdec2e252016-01-15 12:20:25 -0800506 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
507 .withMeta(DefaultTrafficTreatment.builder()
508 .setOutput(PortNumber.CONTROLLER).build())
509 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400510 .withPriority(10000)
alshabibdec2e252016-01-15 12:20:25 -0800511 .add(new ObjectiveContext() {
512 @Override
513 public void onSuccess(Objective objective) {
514 log.info("Eapol filter for {} on {} installed.",
515 devId, port);
516 }
517
518 @Override
519 public void onError(Objective objective, ObjectiveError error) {
520 log.info("Eapol filter for {} on {} failed because {}",
521 devId, port, error);
522 }
523 });
524
alshabibdec2e252016-01-15 12:20:25 -0800525 flowObjectiveService.filter(devId, eapol);
alshabib000b6fc2016-02-01 17:25:00 -0800526
alshabibdec2e252016-01-15 12:20:25 -0800527 }
528
Jonathan Hart403372d2018-08-22 11:44:13 -0700529 /**
530 * Installs trap filtering objectives for particular traffic types on an
531 * NNI port.
532 *
533 * @param devId device ID
534 * @param port port number
535 * @param install true to install, false to remove
536 */
537 private void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
538 processLldpFilteringObjective(devId, port, install);
539 processDhcpFilteringObjectives(devId, port, install);
540 }
541
542 private void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
543 if (!mastershipService.isLocalMaster(devId)) {
544 return;
545 }
546 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
547
548 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
549 .withKey(Criteria.matchInPort(port))
550 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
551 .withMeta(DefaultTrafficTreatment.builder()
552 .setOutput(PortNumber.CONTROLLER).build())
553 .fromApp(appId)
554 .withPriority(10000)
555 .add(new ObjectiveContext() {
556 @Override
557 public void onSuccess(Objective objective) {
558 log.info("LLDP filter for {} on {} installed.",
559 devId, port);
560 }
561
562 @Override
563 public void onError(Objective objective, ObjectiveError error) {
564 log.info("LLDP filter for {} on {} failed because {}",
565 devId, port, error);
566 }
567 });
568
569 flowObjectiveService.filter(devId, lldp);
570
571 }
572
Amit Ghosh95e2f652017-08-23 12:49:46 +0100573 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
574 if (!mastershipService.isLocalMaster(devId)) {
575 return;
576 }
577 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
578
579 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
580 .withKey(Criteria.matchInPort(port))
581 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
582 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
583 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
584 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
585 .withMeta(DefaultTrafficTreatment.builder()
586 .setOutput(PortNumber.CONTROLLER).build())
587 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400588 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100589 .add(new ObjectiveContext() {
590 @Override
591 public void onSuccess(Objective objective) {
592 log.info("DHCP filter for {} on {} installed.",
593 devId, port);
594 }
595
596 @Override
597 public void onError(Objective objective, ObjectiveError error) {
598 log.info("DHCP filter for {} on {} failed because {}",
599 devId, port, error);
600 }
601 });
602
603 flowObjectiveService.filter(devId, dhcpUpstream);
604 }
605
606 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
607 if (!mastershipService.isLocalMaster(devId)) {
608 return;
609 }
610
611 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
612
613 builder = install ? builder.permit() : builder.deny();
614
615 FilteringObjective igmp = builder
616 .withKey(Criteria.matchInPort(port))
617 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
618 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
619 .withMeta(DefaultTrafficTreatment.builder()
620 .setOutput(PortNumber.CONTROLLER).build())
621 .fromApp(appId)
622 .withPriority(10000)
623 .add(new ObjectiveContext() {
624 @Override
625 public void onSuccess(Objective objective) {
626 log.info("Igmp filter for {} on {} installed.",
627 devId, port);
628 }
629
630 @Override
631 public void onError(Objective objective, ObjectiveError error) {
632 log.info("Igmp filter for {} on {} failed because {}.",
633 devId, port, error);
634 }
635 });
636
637 flowObjectiveService.filter(devId, igmp);
638 }
639
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100640 /**
Jonathan Hart403372d2018-08-22 11:44:13 -0700641 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
642 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100643 *
644 * @param dev Device to look for
645 */
Jonathan Hart403372d2018-08-22 11:44:13 -0700646 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100647 // we create only for the ones we are master of
648 if (!mastershipService.isLocalMaster(dev.id())) {
649 return;
650 }
651 // check if this device is provisioned in Sadis
652 String devSerialNo = dev.serialNumber();
653 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
Jonathan Hart403372d2018-08-22 11:44:13 -0700654 log.debug("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100655
656 if (deviceInfo != null) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700657 // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100658 for (Port p : deviceService.getPorts(dev.id())) {
659 if (isUniPort(dev, p)) {
660 processFilteringObjectives(dev.id(), p.number(), true);
Jonathan Hart403372d2018-08-22 11:44:13 -0700661 } else {
662 processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100663 }
664 }
665 }
666 }
667
Jonathan Hart403372d2018-08-22 11:44:13 -0700668
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100669 /**
670 * Get the uplink for of the OLT device.
671 *
672 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
673 * this logic needs to be changed
674 *
675 * @param dev Device to look for
676 * @return The uplink Port of the OLT
677 */
678 private Port getUplinkPort(Device dev) {
679 // check if this device is provisioned in Sadis
680 String devSerialNo = dev.serialNumber();
681 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
682 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
683
684 if (deviceInfo != null) {
685 // Return the port that has been configured as the uplink port of this OLT in Sadis
686 for (Port p: deviceService.getPorts(dev.id())) {
687 if (p.number().toLong() == deviceInfo.uplinkPort()) {
688 log.debug("getUplinkPort: Found port {}", p);
689 return p;
690 }
691 }
692 }
693
694 log.debug("getUplinkPort: No uplink port found for OLT {}", dev);
695 return null;
696 }
697
698 /**
699 * Return the subscriber on a port.
700 *
701 * @param port On which to find the subscriber
702 * @return subscriber if found else null
703 */
704 private SubscriberAndDeviceInformation getSubscriber(ConnectPoint port) {
705 String portName = deviceService.getPort(port).annotations()
706 .value(AnnotationKeys.PORT_NAME);
707
708 return subsService.get(portName);
709 }
710
711 private boolean isUniPort(Device d, Port p) {
712 Port ulPort = getUplinkPort(d);
713 if (ulPort != null) {
714 return (ulPort.number().toLong() != p.number().toLong());
715 }
716 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700717 }
718
alshabibf0e7e702015-05-30 18:22:36 -0700719 private class InternalDeviceListener implements DeviceListener {
720 @Override
721 public void event(DeviceEvent event) {
alshabib3ea82642016-01-12 18:06:53 -0800722 DeviceId devId = event.subject().id();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100723 Device dev = event.subject();
Jonathan Hart6960f822018-07-02 15:19:14 -0700724 if (event.type() != DeviceEvent.Type.PORT_STATS_UPDATED) {
725 log.debug("Olt got {} event for {}", event.type(), event.subject());
726 }
alshabibf0e7e702015-05-30 18:22:36 -0700727 switch (event.type()) {
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800728 //TODO: Port handling and bookkeeping should be improved once
alshabibdec2e252016-01-15 12:20:25 -0800729 // olt firmware handles correct behaviour.
alshabibf0e7e702015-05-30 18:22:36 -0700730 case PORT_ADDED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100731 if (isUniPort(dev, event.port())) {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700732 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
733
734 if (event.port().isEnabled()) {
735 processFilteringObjectives(devId, event.port().number(), true);
736 }
alshabibdec2e252016-01-15 12:20:25 -0800737 }
738 break;
739 case PORT_REMOVED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100740 if (isUniPort(dev, event.port())) {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700741 if (event.port().isEnabled()) {
742 processFilteringObjectives(devId, event.port().number(), false);
Nicolas Palpacuer33397af2018-08-16 14:32:26 -0400743 removeSubscriber(new ConnectPoint(devId, event.port().number()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700744 }
745
746 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800747 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700748
alshabibdec2e252016-01-15 12:20:25 -0800749 break;
alshabibf0e7e702015-05-30 18:22:36 -0700750 case PORT_UPDATED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100751 if (!isUniPort(dev, event.port())) {
alshabibbb83aa22016-02-10 15:08:23 -0800752 break;
753 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100754
alshabibbb83aa22016-02-10 15:08:23 -0800755 if (event.port().isEnabled()) {
alshabib50d9fc52016-02-12 15:47:20 -0800756 processFilteringObjectives(devId, event.port().number(), true);
Jonathan Hart6960f822018-07-02 15:19:14 -0700757 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800758 } else {
alshabib50d9fc52016-02-12 15:47:20 -0800759 processFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo480c3b12018-08-16 11:40:45 -0700760 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800761 }
alshabibf0e7e702015-05-30 18:22:36 -0700762 break;
763 case DEVICE_ADDED:
alshabib8e4fd2f2016-01-12 15:55:53 -0800764 post(new AccessDeviceEvent(
765 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
766 null, null));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700767
768 // Send UNI_ADDED events for all existing ports
769 deviceService.getPorts(devId).stream()
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100770 .filter(p -> isUniPort(dev, p))
Jonathan Hart98de8a72018-05-30 17:47:01 -0700771 .filter(Port::isEnabled)
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700772 .forEach(p -> post(new AccessDeviceEvent(
773 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
774
Jonathan Hart403372d2018-08-22 11:44:13 -0700775 checkAndCreateDeviceFlows(dev);
alshabib8e4fd2f2016-01-12 15:55:53 -0800776 break;
alshabibf0e7e702015-05-30 18:22:36 -0700777 case DEVICE_REMOVED:
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700778 deviceService.getPorts(devId).stream()
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100779 .filter(p -> isUniPort(dev, p))
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700780 .forEach(p -> post(new AccessDeviceEvent(
781 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
782
alshabib8e4fd2f2016-01-12 15:55:53 -0800783 post(new AccessDeviceEvent(
784 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
785 null, null));
786 break;
alshabib7c190012016-02-09 18:22:33 -0800787 case DEVICE_AVAILABILITY_CHANGED:
788 if (deviceService.isAvailable(devId)) {
789 post(new AccessDeviceEvent(
790 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
791 null, null));
Jonathan Hart403372d2018-08-22 11:44:13 -0700792 checkAndCreateDeviceFlows(dev);
alshabib7c190012016-02-09 18:22:33 -0800793 } else {
794 post(new AccessDeviceEvent(
795 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
796 null, null));
797 }
798 break;
alshabib8e4fd2f2016-01-12 15:55:53 -0800799 case DEVICE_UPDATED:
alshabibf0e7e702015-05-30 18:22:36 -0700800 case DEVICE_SUSPENDED:
alshabibf0e7e702015-05-30 18:22:36 -0700801 case PORT_STATS_UPDATED:
802 default:
803 return;
804 }
805 }
806 }
807
Jonathan Harte533a422015-10-20 17:31:24 -0700808 private class InternalNetworkConfigListener implements NetworkConfigListener {
809 @Override
810 public void event(NetworkConfigEvent event) {
811 switch (event.type()) {
812
alshabibbf23a1f2016-01-14 17:27:11 -0800813 case CONFIG_ADDED:
814 case CONFIG_UPDATED:
alshabibe0559672016-02-21 14:49:51 -0800815
alshabibbf23a1f2016-01-14 17:27:11 -0800816 break;
alshabibe0559672016-02-21 14:49:51 -0800817 case CONFIG_REGISTERED:
alshabibbf23a1f2016-01-14 17:27:11 -0800818 case CONFIG_UNREGISTERED:
alshabibe0559672016-02-21 14:49:51 -0800819 break;
alshabibbf23a1f2016-01-14 17:27:11 -0800820 case CONFIG_REMOVED:
821 default:
822 break;
Jonathan Harte533a422015-10-20 17:31:24 -0700823 }
824 }
alshabibe0559672016-02-21 14:49:51 -0800825
826 @Override
827 public boolean isRelevant(NetworkConfigEvent event) {
828 return event.configClass().equals(CONFIG_CLASS);
829 }
Jonathan Harte533a422015-10-20 17:31:24 -0700830 }
alshabibf0e7e702015-05-30 18:22:36 -0700831}