blob: 87a9b9627c1a9f5080f1e1932be9b97733d6d4e4 [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) {
174 checkAndCreateEapolFlows(d);
175 }
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
Amit Ghosh95e2f652017-08-23 12:49:46 +0100529 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
530 if (!mastershipService.isLocalMaster(devId)) {
531 return;
532 }
533 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
534
535 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
536 .withKey(Criteria.matchInPort(port))
537 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
538 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
539 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
540 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
541 .withMeta(DefaultTrafficTreatment.builder()
542 .setOutput(PortNumber.CONTROLLER).build())
543 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400544 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100545 .add(new ObjectiveContext() {
546 @Override
547 public void onSuccess(Objective objective) {
548 log.info("DHCP filter for {} on {} installed.",
549 devId, port);
550 }
551
552 @Override
553 public void onError(Objective objective, ObjectiveError error) {
554 log.info("DHCP filter for {} on {} failed because {}",
555 devId, port, error);
556 }
557 });
558
559 flowObjectiveService.filter(devId, dhcpUpstream);
560 }
561
562 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
563 if (!mastershipService.isLocalMaster(devId)) {
564 return;
565 }
566
567 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
568
569 builder = install ? builder.permit() : builder.deny();
570
571 FilteringObjective igmp = builder
572 .withKey(Criteria.matchInPort(port))
573 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
574 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
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("Igmp filter for {} on {} installed.",
583 devId, port);
584 }
585
586 @Override
587 public void onError(Objective objective, ObjectiveError error) {
588 log.info("Igmp filter for {} on {} failed because {}.",
589 devId, port, error);
590 }
591 });
592
593 flowObjectiveService.filter(devId, igmp);
594 }
595
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100596 /**
597 * Creates EAPOL flows on the UNIs, if device is
598 * present in Sadis config.
599 *
600 * @param dev Device to look for
601 */
602 private void checkAndCreateEapolFlows(Device dev) {
603 // we create only for the ones we are master of
604 if (!mastershipService.isLocalMaster(dev.id())) {
605 return;
606 }
607 // check if this device is provisioned in Sadis
608 String devSerialNo = dev.serialNumber();
609 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
610 log.debug("checkAndCreateEapolFlows: deviceInfo {}", deviceInfo);
611
612 if (deviceInfo != null) {
613 // This is an OLT device as per Sadis, we need to create EAPOL flows
614 // on all it's UNIs
615 for (Port p : deviceService.getPorts(dev.id())) {
616 if (isUniPort(dev, p)) {
617 processFilteringObjectives(dev.id(), p.number(), true);
618 }
619 }
620 }
621 }
622
623 /**
624 * Get the uplink for of the OLT device.
625 *
626 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
627 * this logic needs to be changed
628 *
629 * @param dev Device to look for
630 * @return The uplink Port of the OLT
631 */
632 private Port getUplinkPort(Device dev) {
633 // check if this device is provisioned in Sadis
634 String devSerialNo = dev.serialNumber();
635 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
636 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
637
638 if (deviceInfo != null) {
639 // Return the port that has been configured as the uplink port of this OLT in Sadis
640 for (Port p: deviceService.getPorts(dev.id())) {
641 if (p.number().toLong() == deviceInfo.uplinkPort()) {
642 log.debug("getUplinkPort: Found port {}", p);
643 return p;
644 }
645 }
646 }
647
648 log.debug("getUplinkPort: No uplink port found for OLT {}", dev);
649 return null;
650 }
651
652 /**
653 * Return the subscriber on a port.
654 *
655 * @param port On which to find the subscriber
656 * @return subscriber if found else null
657 */
658 private SubscriberAndDeviceInformation getSubscriber(ConnectPoint port) {
659 String portName = deviceService.getPort(port).annotations()
660 .value(AnnotationKeys.PORT_NAME);
661
662 return subsService.get(portName);
663 }
664
665 private boolean isUniPort(Device d, Port p) {
666 Port ulPort = getUplinkPort(d);
667 if (ulPort != null) {
668 return (ulPort.number().toLong() != p.number().toLong());
669 }
670 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700671 }
672
alshabibf0e7e702015-05-30 18:22:36 -0700673 private class InternalDeviceListener implements DeviceListener {
674 @Override
675 public void event(DeviceEvent event) {
alshabib3ea82642016-01-12 18:06:53 -0800676 DeviceId devId = event.subject().id();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100677 Device dev = event.subject();
Jonathan Hart6960f822018-07-02 15:19:14 -0700678 if (event.type() != DeviceEvent.Type.PORT_STATS_UPDATED) {
679 log.debug("Olt got {} event for {}", event.type(), event.subject());
680 }
alshabibf0e7e702015-05-30 18:22:36 -0700681 switch (event.type()) {
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800682 //TODO: Port handling and bookkeeping should be improved once
alshabibdec2e252016-01-15 12:20:25 -0800683 // olt firmware handles correct behaviour.
alshabibf0e7e702015-05-30 18:22:36 -0700684 case PORT_ADDED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100685 if (isUniPort(dev, event.port())) {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700686 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
687
688 if (event.port().isEnabled()) {
689 processFilteringObjectives(devId, event.port().number(), true);
690 }
alshabibdec2e252016-01-15 12:20:25 -0800691 }
692 break;
693 case PORT_REMOVED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100694 if (isUniPort(dev, event.port())) {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700695 if (event.port().isEnabled()) {
696 processFilteringObjectives(devId, event.port().number(), false);
Nicolas Palpacuer33397af2018-08-16 14:32:26 -0400697 removeSubscriber(new ConnectPoint(devId, event.port().number()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700698 }
699
700 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800701 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700702
alshabibdec2e252016-01-15 12:20:25 -0800703 break;
alshabibf0e7e702015-05-30 18:22:36 -0700704 case PORT_UPDATED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100705 if (!isUniPort(dev, event.port())) {
alshabibbb83aa22016-02-10 15:08:23 -0800706 break;
707 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100708
alshabibbb83aa22016-02-10 15:08:23 -0800709 if (event.port().isEnabled()) {
alshabib50d9fc52016-02-12 15:47:20 -0800710 processFilteringObjectives(devId, event.port().number(), true);
Jonathan Hart6960f822018-07-02 15:19:14 -0700711 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800712 } else {
alshabib50d9fc52016-02-12 15:47:20 -0800713 processFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo480c3b12018-08-16 11:40:45 -0700714 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800715 }
alshabibf0e7e702015-05-30 18:22:36 -0700716 break;
717 case DEVICE_ADDED:
alshabib8e4fd2f2016-01-12 15:55:53 -0800718 post(new AccessDeviceEvent(
719 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
720 null, null));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700721
722 // Send UNI_ADDED events for all existing ports
723 deviceService.getPorts(devId).stream()
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100724 .filter(p -> isUniPort(dev, p))
Jonathan Hart98de8a72018-05-30 17:47:01 -0700725 .filter(Port::isEnabled)
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700726 .forEach(p -> post(new AccessDeviceEvent(
727 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
728
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100729 checkAndCreateEapolFlows(dev);
alshabib8e4fd2f2016-01-12 15:55:53 -0800730 break;
alshabibf0e7e702015-05-30 18:22:36 -0700731 case DEVICE_REMOVED:
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700732 deviceService.getPorts(devId).stream()
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100733 .filter(p -> isUniPort(dev, p))
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700734 .forEach(p -> post(new AccessDeviceEvent(
735 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
736
alshabib8e4fd2f2016-01-12 15:55:53 -0800737 post(new AccessDeviceEvent(
738 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
739 null, null));
740 break;
alshabib7c190012016-02-09 18:22:33 -0800741 case DEVICE_AVAILABILITY_CHANGED:
742 if (deviceService.isAvailable(devId)) {
743 post(new AccessDeviceEvent(
744 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
745 null, null));
746 } else {
747 post(new AccessDeviceEvent(
748 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
749 null, null));
750 }
751 break;
alshabib8e4fd2f2016-01-12 15:55:53 -0800752 case DEVICE_UPDATED:
alshabibf0e7e702015-05-30 18:22:36 -0700753 case DEVICE_SUSPENDED:
alshabibf0e7e702015-05-30 18:22:36 -0700754 case PORT_STATS_UPDATED:
755 default:
756 return;
757 }
758 }
759 }
760
Jonathan Harte533a422015-10-20 17:31:24 -0700761 private class InternalNetworkConfigListener implements NetworkConfigListener {
762 @Override
763 public void event(NetworkConfigEvent event) {
764 switch (event.type()) {
765
alshabibbf23a1f2016-01-14 17:27:11 -0800766 case CONFIG_ADDED:
767 case CONFIG_UPDATED:
alshabibe0559672016-02-21 14:49:51 -0800768
alshabibbf23a1f2016-01-14 17:27:11 -0800769 break;
alshabibe0559672016-02-21 14:49:51 -0800770 case CONFIG_REGISTERED:
alshabibbf23a1f2016-01-14 17:27:11 -0800771 case CONFIG_UNREGISTERED:
alshabibe0559672016-02-21 14:49:51 -0800772 break;
alshabibbf23a1f2016-01-14 17:27:11 -0800773 case CONFIG_REMOVED:
774 default:
775 break;
Jonathan Harte533a422015-10-20 17:31:24 -0700776 }
777 }
alshabibe0559672016-02-21 14:49:51 -0800778
779 @Override
780 public boolean isRelevant(NetworkConfigEvent event) {
781 return event.configClass().equals(CONFIG_CLASS);
782 }
Jonathan Harte533a422015-10-20 17:31:24 -0700783 }
alshabibf0e7e702015-05-30 18:22:36 -0700784}