blob: 48c3aa0a4b46dbdcae5f16d782489ed6a1d5d184 [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;
80import static org.onlab.util.Tools.get;
alshabib3ea82642016-01-12 18:06:53 -080081import static org.onlab.util.Tools.groupedThreads;
alshabibf0e7e702015-05-30 18:22:36 -070082import static org.slf4j.LoggerFactory.getLogger;
83
84/**
Jonathan Harte533a422015-10-20 17:31:24 -070085 * Provisions rules on access devices.
alshabibf0e7e702015-05-30 18:22:36 -070086 */
Jonathan Harte533a422015-10-20 17:31:24 -070087@Service
alshabibf0e7e702015-05-30 18:22:36 -070088@Component(immediate = true)
alshabib8e4fd2f2016-01-12 15:55:53 -080089public class Olt
90 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
91 implements AccessDeviceService {
Charles Chan54f110f2017-01-20 11:22:42 -080092 private static final String APP_NAME = "org.opencord.olt";
alshabibe0559672016-02-21 14:49:51 -080093
94 private static final short DEFAULT_VLAN = 0;
95
alshabibf0e7e702015-05-30 18:22:36 -070096 private final Logger log = getLogger(getClass());
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected FlowObjectiveService flowObjectiveService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib09753b52016-03-04 14:55:19 -0800102 protected MastershipService mastershipService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibf0e7e702015-05-30 18:22:36 -0700105 protected DeviceService deviceService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected CoreService coreService;
109
Jonathan Harte533a422015-10-20 17:31:24 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe0559672016-02-21 14:49:51 -0800111 protected ComponentConfigService componentConfigService;
112
alshabib4ceaed32016-03-03 18:00:58 -0800113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100114 protected SubscriberAndDeviceInformationService subsService;
alshabibe0559672016-02-21 14:49:51 -0800115
116 @Property(name = "defaultVlan", intValue = DEFAULT_VLAN,
117 label = "Default VLAN RG<->ONU traffic")
118 private int defaultVlan = DEFAULT_VLAN;
119
Matt Jeanneret3f579262018-06-14 17:16:23 -0400120 @Property(name = "enableDhcpOnProvisioning", boolValue = true,
121 label = "Create the DHCP Flow rules when a subscriber is provisioned")
122 protected boolean enableDhcpOnProvisioning = false;
123
124 @Property(name = "enableIgmpOnProvisioning", boolValue = false,
125 label = "Create IGMP Flow rules when a subscriber is provisioned")
126 protected boolean enableIgmpOnProvisioning = false;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100127
alshabibf0e7e702015-05-30 18:22:36 -0700128 private final DeviceListener deviceListener = new InternalDeviceListener();
129
130 private ApplicationId appId;
131
alshabib3ea82642016-01-12 18:06:53 -0800132 private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
alshabibbf23a1f2016-01-14 17:27:11 -0800133 groupedThreads("onos/olt-service",
134 "olt-installer-%d"));
alshabibf0e7e702015-05-30 18:22:36 -0700135
136 @Activate
alshabibe0559672016-02-21 14:49:51 -0800137 public void activate(ComponentContext context) {
138 modified(context);
Charles Chan54f110f2017-01-20 11:22:42 -0800139 appId = coreService.registerApplication(APP_NAME);
alshabibe0559672016-02-21 14:49:51 -0800140 componentConfigService.registerProperties(getClass());
alshabibc4dfe852015-06-05 13:35:13 -0700141
alshabib8e4fd2f2016-01-12 15:55:53 -0800142 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
143
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100144 // look for all provisioned devices in Sadis and create EAPOL flows for the
145 // UNI ports
146 Iterable<Device> devices = deviceService.getDevices();
147 for (Device d : devices) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700148 checkAndCreateDeviceFlows(d);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100149 }
alshabib4ceaed32016-03-03 18:00:58 -0800150
alshabibba357492016-01-27 13:49:46 -0800151 deviceService.addListener(deviceListener);
152
alshabibf0e7e702015-05-30 18:22:36 -0700153 log.info("Started with Application ID {}", appId.id());
154 }
155
156 @Deactivate
157 public void deactivate() {
alshabibe0559672016-02-21 14:49:51 -0800158 componentConfigService.unregisterProperties(getClass(), false);
alshabib62e9ce72016-02-11 17:31:36 -0800159 deviceService.removeListener(deviceListener);
Jonathan Hart5f1c8142018-07-24 17:31:59 -0700160 eventDispatcher.removeSink(AccessDeviceEvent.class);
alshabibf0e7e702015-05-30 18:22:36 -0700161 log.info("Stopped");
162 }
163
alshabibe0559672016-02-21 14:49:51 -0800164 @Modified
165 public void modified(ComponentContext context) {
166 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
167
168 try {
169 String s = get(properties, "defaultVlan");
170 defaultVlan = isNullOrEmpty(s) ? DEFAULT_VLAN : Integer.parseInt(s.trim());
Amit Ghosh95e2f652017-08-23 12:49:46 +0100171
Matt Jeanneret3f579262018-06-14 17:16:23 -0400172 Boolean o = Tools.isPropertyEnabled(properties, "enableDhcpOnProvisioning");
Amit Ghosh95e2f652017-08-23 12:49:46 +0100173 if (o != null) {
Matt Jeanneret3f579262018-06-14 17:16:23 -0400174 enableDhcpOnProvisioning = o;
Amit Ghosh95e2f652017-08-23 12:49:46 +0100175 }
Matt Jeanneret3f579262018-06-14 17:16:23 -0400176
177 Boolean p = Tools.isPropertyEnabled(properties, "enableIgmpOnProvisioning");
178 if (p != null) {
179 enableIgmpOnProvisioning = p;
180 }
181
alshabibe0559672016-02-21 14:49:51 -0800182 } catch (Exception e) {
183 defaultVlan = DEFAULT_VLAN;
184 }
185 }
186
alshabib32232c82016-02-25 17:57:24 -0500187 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100188 public void provisionSubscriber(ConnectPoint port) {
Jonathan Hart94b90492018-04-24 14:02:25 -0700189 checkNotNull(deviceService.getPort(port.deviceId(), port.port()),
190 "Invalid connect point");
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100191 // Find the subscriber on this connect point
192 SubscriberAndDeviceInformation sub = getSubscriber(port);
193 if (sub == null) {
194 log.warn("No subscriber found for {}", port);
195 return;
196 }
Jonathan Harte533a422015-10-20 17:31:24 -0700197
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100198 // Get the uplink port
199 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
200 if (uplinkPort == null) {
201 log.warn("No uplink port found for OLT device {}", port.deviceId());
202 return;
Jonathan Harte533a422015-10-20 17:31:24 -0700203 }
204
Matt Jeanneret3f579262018-06-14 17:16:23 -0400205 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100206 processDhcpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100207 }
208
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100209 Optional<VlanId> defaultVlan = Optional.empty();
210 provisionVlans(port.deviceId(), uplinkPort.number(), port.port(), sub.cTag(), sub.sTag(),
211 defaultVlan);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100212
Matt Jeanneret3f579262018-06-14 17:16:23 -0400213 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100214 processIgmpFilteringObjectives(port.deviceId(), port.port(), true);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100215 }
alshabibb7a9e172016-01-13 11:23:53 -0800216 }
217
218 @Override
219 public void removeSubscriber(ConnectPoint port) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100220 // Get the subscriber connected to this port from Sadis
221 SubscriberAndDeviceInformation subscriber = getSubscriber(port);
222 if (subscriber == null) {
223 log.warn("Subscriber on port {} not found", port);
alshabibbf23a1f2016-01-14 17:27:11 -0800224 return;
225 }
226
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100227 // Get the uplink port
228 Port uplinkPort = getUplinkPort(deviceService.getDevice(port.deviceId()));
229 if (uplinkPort == null) {
230 log.warn("No uplink port found for OLT device {}", port.deviceId());
alshabib4ceaed32016-03-03 18:00:58 -0800231 return;
232 }
233
Matt Jeanneret3f579262018-06-14 17:16:23 -0400234 if (enableDhcpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100235 processDhcpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100236 }
237
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100238 Optional<VlanId> defaultVlan = Optional.empty();
239 unprovisionSubscriber(port.deviceId(), uplinkPort.number(), port.port(), subscriber.cTag(),
240 subscriber.sTag(), defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800241
Matt Jeanneret3f579262018-06-14 17:16:23 -0400242 if (enableIgmpOnProvisioning) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100243 processIgmpFilteringObjectives(port.deviceId(), port.port(), false);
Amit Ghosh95e2f652017-08-23 12:49:46 +0100244 }
alshabibbf23a1f2016-01-14 17:27:11 -0800245 }
246
Amit Ghosh95e2f652017-08-23 12:49:46 +0100247
alshabibe0559672016-02-21 14:49:51 -0800248 @Override
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800249 public Collection<Map.Entry<ConnectPoint, VlanId>> getSubscribers() {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100250 ArrayList<Map.Entry<ConnectPoint, VlanId>> subs = new ArrayList<>();
251
252 // Get the subscribers for all the devices
253 // If the port is UNI, is enabled and exists in Sadis then copy it
254 for (Device d : deviceService.getDevices()) {
255 for (Port p: deviceService.getPorts(d.id())) {
256 if (isUniPort(d, p) && p.isEnabled()) {
257 ConnectPoint cp = new ConnectPoint(d.id(), p.number());
258
259 SubscriberAndDeviceInformation sub = getSubscriber(cp);
260 if (sub != null) {
261 subs.add(new AbstractMap.SimpleEntry(cp, sub.cTag()));
262 }
263 }
264 }
265 }
266
267 return subs;
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800268 }
269
270 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100271 public List<DeviceId> fetchOlts() {
272 // look through all the devices and find the ones that are OLTs as per Sadis
273 List<DeviceId> olts = new ArrayList<>();
274 Iterable<Device> devices = deviceService.getDevices();
275 for (Device d : devices) {
276 String devSerialNo = d.serialNumber();
277 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
278
279 if (deviceInfo != null) {
280 // So this is indeed a OLT device
281 olts.add(d.id());
282 }
283 }
284 return olts;
alshabibe0559672016-02-21 14:49:51 -0800285 }
286
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700287 private void initializeUni(Port port) {
288 DeviceId deviceId = (DeviceId) port.element().id();
289
290 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, deviceId, port));
291
292 if (port.isEnabled()) {
293 processFilteringObjectives(deviceId, port.number(), true);
294 }
295 }
296
alshabibbf23a1f2016-01-14 17:27:11 -0800297 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
alshabib4ceaed32016-03-03 18:00:58 -0800298 PortNumber subscriberPort, VlanId subscriberVlan,
299 VlanId deviceVlan, Optional<VlanId> defaultVlan) {
alshabibbf23a1f2016-01-14 17:27:11 -0800300
301 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
302 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
303
alshabib4ceaed32016-03-03 18:00:58 -0800304 ForwardingObjective.Builder upFwd = upBuilder(uplink, subscriberPort,
305 subscriberVlan, deviceVlan,
306 defaultVlan);
307 ForwardingObjective.Builder downFwd = downBuilder(uplink, subscriberPort,
308 subscriberVlan, deviceVlan,
309 defaultVlan);
alshabibbf23a1f2016-01-14 17:27:11 -0800310
311
alshabib4ceaed32016-03-03 18:00:58 -0800312 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
313 @Override
314 public void onSuccess(Objective objective) {
315 upFuture.complete(null);
316 }
alshabibbf23a1f2016-01-14 17:27:11 -0800317
alshabib4ceaed32016-03-03 18:00:58 -0800318 @Override
319 public void onError(Objective objective, ObjectiveError error) {
320 upFuture.complete(error);
321 }
322 }));
323
324 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
325 @Override
326 public void onSuccess(Objective objective) {
327 downFuture.complete(null);
328 }
329
330 @Override
331 public void onError(Objective objective, ObjectiveError error) {
332 downFuture.complete(error);
333 }
334 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800335
336 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
337 if (upStatus == null && downStatus == null) {
338 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
339 deviceId,
340 deviceVlan,
341 subscriberVlan));
342 } else if (downStatus != null) {
343 log.error("Subscriber with vlan {} on device {} " +
344 "on port {} failed downstream uninstallation: {}",
345 subscriberVlan, deviceId, subscriberPort, downStatus);
346 } else if (upStatus != null) {
347 log.error("Subscriber with vlan {} on device {} " +
348 "on port {} failed upstream uninstallation: {}",
349 subscriberVlan, deviceId, subscriberPort, upStatus);
350 }
351 }, oltInstallers);
alshabibb7a9e172016-01-13 11:23:53 -0800352
Jonathan Harte533a422015-10-20 17:31:24 -0700353 }
354
355 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
356 PortNumber subscriberPort,
Jonathan Hart52998382015-11-10 16:09:22 -0800357 VlanId subscriberVlan, VlanId deviceVlan,
358 Optional<VlanId> defaultVlan) {
Jonathan Harte533a422015-10-20 17:31:24 -0700359
alshabib3ea82642016-01-12 18:06:53 -0800360 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
361 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
362
alshabib4ceaed32016-03-03 18:00:58 -0800363 ForwardingObjective.Builder upFwd = upBuilder(uplinkPort, subscriberPort,
364 subscriberVlan, deviceVlan,
365 defaultVlan);
Jonathan Harte533a422015-10-20 17:31:24 -0700366
367
alshabib4ceaed32016-03-03 18:00:58 -0800368 ForwardingObjective.Builder downFwd = downBuilder(uplinkPort, subscriberPort,
369 subscriberVlan, deviceVlan,
370 defaultVlan);
alshabib3ea82642016-01-12 18:06:53 -0800371
alshabibbf23a1f2016-01-14 17:27:11 -0800372 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
373 @Override
374 public void onSuccess(Objective objective) {
375 upFuture.complete(null);
376 }
377
378 @Override
379 public void onError(Objective objective, ObjectiveError error) {
380 upFuture.complete(error);
381 }
382 }));
383
alshabibbf23a1f2016-01-14 17:27:11 -0800384 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
385 @Override
386 public void onSuccess(Objective objective) {
387 downFuture.complete(null);
388 }
389
390 @Override
391 public void onError(Objective objective, ObjectiveError error) {
392 downFuture.complete(error);
393 }
394 }));
alshabib3ea82642016-01-12 18:06:53 -0800395
396 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
397 if (upStatus == null && downStatus == null) {
398 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
399 deviceId,
400 deviceVlan,
401 subscriberVlan));
alshabib50d9fc52016-02-12 15:47:20 -0800402
alshabib3ea82642016-01-12 18:06:53 -0800403 } else if (downStatus != null) {
404 log.error("Subscriber with vlan {} on device {} " +
405 "on port {} failed downstream installation: {}",
406 subscriberVlan, deviceId, subscriberPort, downStatus);
407 } else if (upStatus != null) {
408 log.error("Subscriber with vlan {} on device {} " +
409 "on port {} failed upstream installation: {}",
410 subscriberVlan, deviceId, subscriberPort, upStatus);
411 }
412 }, oltInstallers);
413
Jonathan Harte533a422015-10-20 17:31:24 -0700414 }
415
alshabib4ceaed32016-03-03 18:00:58 -0800416 private ForwardingObjective.Builder downBuilder(PortNumber uplinkPort,
417 PortNumber subscriberPort,
418 VlanId subscriberVlan,
419 VlanId deviceVlan,
420 Optional<VlanId> defaultVlan) {
421 TrafficSelector downstream = DefaultTrafficSelector.builder()
422 .matchVlanId(deviceVlan)
423 .matchInPort(uplinkPort)
424 .matchInnerVlanId(subscriberVlan)
425 .build();
426
427 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
428 .popVlan()
429 .setVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
430 .setOutput(subscriberPort)
431 .build();
432
433 return DefaultForwardingObjective.builder()
434 .withFlag(ForwardingObjective.Flag.VERSATILE)
435 .withPriority(1000)
436 .makePermanent()
437 .withSelector(downstream)
438 .fromApp(appId)
439 .withTreatment(downstreamTreatment);
440 }
441
442 private ForwardingObjective.Builder upBuilder(PortNumber uplinkPort,
443 PortNumber subscriberPort,
444 VlanId subscriberVlan,
445 VlanId deviceVlan,
446 Optional<VlanId> defaultVlan) {
447 TrafficSelector upstream = DefaultTrafficSelector.builder()
448 .matchVlanId(defaultVlan.orElse(VlanId.vlanId((short) this.defaultVlan)))
449 .matchInPort(subscriberPort)
450 .build();
451
452
453 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
454 .pushVlan()
455 .setVlanId(subscriberVlan)
456 .pushVlan()
457 .setVlanId(deviceVlan)
458 .setOutput(uplinkPort)
459 .build();
460
461 return DefaultForwardingObjective.builder()
462 .withFlag(ForwardingObjective.Flag.VERSATILE)
463 .withPriority(1000)
464 .makePermanent()
465 .withSelector(upstream)
466 .fromApp(appId)
467 .withTreatment(upstreamTreatment);
468 }
469
alshabib50d9fc52016-02-12 15:47:20 -0800470 private void processFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
alshabib09753b52016-03-04 14:55:19 -0800471 if (!mastershipService.isLocalMaster(devId)) {
472 return;
473 }
alshabibbb83aa22016-02-10 15:08:23 -0800474 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
475
476 FilteringObjective eapol = (install ? builder.permit() : builder.deny())
alshabib50d9fc52016-02-12 15:47:20 -0800477 .withKey(Criteria.matchInPort(port))
alshabibdec2e252016-01-15 12:20:25 -0800478 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
479 .withMeta(DefaultTrafficTreatment.builder()
480 .setOutput(PortNumber.CONTROLLER).build())
481 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400482 .withPriority(10000)
alshabibdec2e252016-01-15 12:20:25 -0800483 .add(new ObjectiveContext() {
484 @Override
485 public void onSuccess(Objective objective) {
486 log.info("Eapol filter for {} on {} installed.",
487 devId, port);
488 }
489
490 @Override
491 public void onError(Objective objective, ObjectiveError error) {
492 log.info("Eapol filter for {} on {} failed because {}",
493 devId, port, error);
494 }
495 });
496
alshabibdec2e252016-01-15 12:20:25 -0800497 flowObjectiveService.filter(devId, eapol);
alshabib000b6fc2016-02-01 17:25:00 -0800498
alshabibdec2e252016-01-15 12:20:25 -0800499 }
500
Jonathan Hart403372d2018-08-22 11:44:13 -0700501 /**
502 * Installs trap filtering objectives for particular traffic types on an
503 * NNI port.
504 *
505 * @param devId device ID
506 * @param port port number
507 * @param install true to install, false to remove
508 */
509 private void processNniFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
510 processLldpFilteringObjective(devId, port, install);
511 processDhcpFilteringObjectives(devId, port, install);
512 }
513
514 private void processLldpFilteringObjective(DeviceId devId, PortNumber port, boolean install) {
515 if (!mastershipService.isLocalMaster(devId)) {
516 return;
517 }
518 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
519
520 FilteringObjective lldp = (install ? builder.permit() : builder.deny())
521 .withKey(Criteria.matchInPort(port))
522 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
523 .withMeta(DefaultTrafficTreatment.builder()
524 .setOutput(PortNumber.CONTROLLER).build())
525 .fromApp(appId)
526 .withPriority(10000)
527 .add(new ObjectiveContext() {
528 @Override
529 public void onSuccess(Objective objective) {
530 log.info("LLDP filter for {} on {} installed.",
531 devId, port);
532 }
533
534 @Override
535 public void onError(Objective objective, ObjectiveError error) {
536 log.info("LLDP filter for {} on {} failed because {}",
537 devId, port, error);
538 }
539 });
540
541 flowObjectiveService.filter(devId, lldp);
542
543 }
544
Amit Ghosh95e2f652017-08-23 12:49:46 +0100545 private void processDhcpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
546 if (!mastershipService.isLocalMaster(devId)) {
547 return;
548 }
549 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
550
551 FilteringObjective dhcpUpstream = (install ? builder.permit() : builder.deny())
552 .withKey(Criteria.matchInPort(port))
553 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
554 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
555 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
556 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
557 .withMeta(DefaultTrafficTreatment.builder()
558 .setOutput(PortNumber.CONTROLLER).build())
559 .fromApp(appId)
Matt Jeanneret3f579262018-06-14 17:16:23 -0400560 .withPriority(10000)
Amit Ghosh95e2f652017-08-23 12:49:46 +0100561 .add(new ObjectiveContext() {
562 @Override
563 public void onSuccess(Objective objective) {
564 log.info("DHCP filter for {} on {} installed.",
565 devId, port);
566 }
567
568 @Override
569 public void onError(Objective objective, ObjectiveError error) {
570 log.info("DHCP filter for {} on {} failed because {}",
571 devId, port, error);
572 }
573 });
574
575 flowObjectiveService.filter(devId, dhcpUpstream);
576 }
577
578 private void processIgmpFilteringObjectives(DeviceId devId, PortNumber port, boolean install) {
579 if (!mastershipService.isLocalMaster(devId)) {
580 return;
581 }
582
583 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
584
585 builder = install ? builder.permit() : builder.deny();
586
587 FilteringObjective igmp = builder
588 .withKey(Criteria.matchInPort(port))
589 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
590 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
591 .withMeta(DefaultTrafficTreatment.builder()
592 .setOutput(PortNumber.CONTROLLER).build())
593 .fromApp(appId)
594 .withPriority(10000)
595 .add(new ObjectiveContext() {
596 @Override
597 public void onSuccess(Objective objective) {
598 log.info("Igmp filter for {} on {} installed.",
599 devId, port);
600 }
601
602 @Override
603 public void onError(Objective objective, ObjectiveError error) {
604 log.info("Igmp filter for {} on {} failed because {}.",
605 devId, port, error);
606 }
607 });
608
609 flowObjectiveService.filter(devId, igmp);
610 }
611
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100612 /**
Jonathan Hart403372d2018-08-22 11:44:13 -0700613 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
614 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100615 *
616 * @param dev Device to look for
617 */
Jonathan Hart403372d2018-08-22 11:44:13 -0700618 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100619 // we create only for the ones we are master of
620 if (!mastershipService.isLocalMaster(dev.id())) {
621 return;
622 }
623 // check if this device is provisioned in Sadis
624 String devSerialNo = dev.serialNumber();
625 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
Jonathan Hart403372d2018-08-22 11:44:13 -0700626 log.debug("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100627
628 if (deviceInfo != null) {
Jonathan Hart403372d2018-08-22 11:44:13 -0700629 // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100630 for (Port p : deviceService.getPorts(dev.id())) {
631 if (isUniPort(dev, p)) {
632 processFilteringObjectives(dev.id(), p.number(), true);
Jonathan Hart403372d2018-08-22 11:44:13 -0700633 } else {
634 processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100635 }
636 }
637 }
638 }
639
Jonathan Hart403372d2018-08-22 11:44:13 -0700640
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100641 /**
642 * Get the uplink for of the OLT device.
643 *
644 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
645 * this logic needs to be changed
646 *
647 * @param dev Device to look for
648 * @return The uplink Port of the OLT
649 */
650 private Port getUplinkPort(Device dev) {
651 // check if this device is provisioned in Sadis
652 String devSerialNo = dev.serialNumber();
653 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
654 log.debug("getUplinkPort: deviceInfo {}", deviceInfo);
655
656 if (deviceInfo != null) {
657 // Return the port that has been configured as the uplink port of this OLT in Sadis
658 for (Port p: deviceService.getPorts(dev.id())) {
659 if (p.number().toLong() == deviceInfo.uplinkPort()) {
660 log.debug("getUplinkPort: Found port {}", p);
661 return p;
662 }
663 }
664 }
665
666 log.debug("getUplinkPort: No uplink port found for OLT {}", dev);
667 return null;
668 }
669
670 /**
671 * Return the subscriber on a port.
672 *
673 * @param port On which to find the subscriber
674 * @return subscriber if found else null
675 */
676 private SubscriberAndDeviceInformation getSubscriber(ConnectPoint port) {
677 String portName = deviceService.getPort(port).annotations()
678 .value(AnnotationKeys.PORT_NAME);
679
680 return subsService.get(portName);
681 }
682
683 private boolean isUniPort(Device d, Port p) {
684 Port ulPort = getUplinkPort(d);
685 if (ulPort != null) {
686 return (ulPort.number().toLong() != p.number().toLong());
687 }
688 return false;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700689 }
690
Jonathan Hart4c538002018-08-23 10:11:54 -0700691 private SubscriberAndDeviceInformation getOltInfo(Device dev) {
692 String devSerialNo = dev.serialNumber();
693 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
694 return deviceInfo;
695 }
696
alshabibf0e7e702015-05-30 18:22:36 -0700697 private class InternalDeviceListener implements DeviceListener {
698 @Override
699 public void event(DeviceEvent event) {
alshabib3ea82642016-01-12 18:06:53 -0800700 DeviceId devId = event.subject().id();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100701 Device dev = event.subject();
Jonathan Hart4c538002018-08-23 10:11:54 -0700702
703
704 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
705 return;
Jonathan Hart6960f822018-07-02 15:19:14 -0700706 }
Jonathan Hart4c538002018-08-23 10:11:54 -0700707
708 if (getOltInfo(dev) == null) {
709 log.debug("No device info found, this is not an OLT");
710 return;
711 }
712
713 log.debug("OLT got {} event for {}", event.type(), event.subject());
714
alshabibf0e7e702015-05-30 18:22:36 -0700715 switch (event.type()) {
Jonathan Hartfd6c1b32016-03-08 14:09:09 -0800716 //TODO: Port handling and bookkeeping should be improved once
alshabibdec2e252016-01-15 12:20:25 -0800717 // olt firmware handles correct behaviour.
alshabibf0e7e702015-05-30 18:22:36 -0700718 case PORT_ADDED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100719 if (isUniPort(dev, event.port())) {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700720 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
721
722 if (event.port().isEnabled()) {
723 processFilteringObjectives(devId, event.port().number(), true);
724 }
Jonathan Hart4c538002018-08-23 10:11:54 -0700725 } else {
726 checkAndCreateDeviceFlows(dev);
alshabibdec2e252016-01-15 12:20:25 -0800727 }
728 break;
729 case PORT_REMOVED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100730 if (isUniPort(dev, event.port())) {
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700731 if (event.port().isEnabled()) {
732 processFilteringObjectives(devId, event.port().number(), false);
Nicolas Palpacuer33397af2018-08-16 14:32:26 -0400733 removeSubscriber(new ConnectPoint(devId, event.port().number()));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700734 }
735
736 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800737 }
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700738
alshabibdec2e252016-01-15 12:20:25 -0800739 break;
alshabibf0e7e702015-05-30 18:22:36 -0700740 case PORT_UPDATED:
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100741 if (!isUniPort(dev, event.port())) {
alshabibbb83aa22016-02-10 15:08:23 -0800742 break;
743 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100744
alshabibbb83aa22016-02-10 15:08:23 -0800745 if (event.port().isEnabled()) {
alshabib50d9fc52016-02-12 15:47:20 -0800746 processFilteringObjectives(devId, event.port().number(), true);
Jonathan Hart6960f822018-07-02 15:19:14 -0700747 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800748 } else {
alshabib50d9fc52016-02-12 15:47:20 -0800749 processFilteringObjectives(devId, event.port().number(), false);
Matteo Scandolo480c3b12018-08-16 11:40:45 -0700750 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, event.port()));
alshabibbb83aa22016-02-10 15:08:23 -0800751 }
alshabibf0e7e702015-05-30 18:22:36 -0700752 break;
753 case DEVICE_ADDED:
alshabib8e4fd2f2016-01-12 15:55:53 -0800754 post(new AccessDeviceEvent(
755 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
756 null, null));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700757
758 // Send UNI_ADDED events for all existing ports
759 deviceService.getPorts(devId).stream()
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100760 .filter(p -> isUniPort(dev, p))
Jonathan Hart98de8a72018-05-30 17:47:01 -0700761 .filter(Port::isEnabled)
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700762 .forEach(p -> post(new AccessDeviceEvent(
763 AccessDeviceEvent.Type.UNI_ADDED, devId, p)));
764
Jonathan Hart403372d2018-08-22 11:44:13 -0700765 checkAndCreateDeviceFlows(dev);
alshabib8e4fd2f2016-01-12 15:55:53 -0800766 break;
alshabibf0e7e702015-05-30 18:22:36 -0700767 case DEVICE_REMOVED:
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700768 deviceService.getPorts(devId).stream()
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100769 .filter(p -> isUniPort(dev, p))
Jonathan Hart1d34c8b2018-05-05 15:37:28 -0700770 .forEach(p -> post(new AccessDeviceEvent(
771 AccessDeviceEvent.Type.UNI_REMOVED, devId, p)));
772
alshabib8e4fd2f2016-01-12 15:55:53 -0800773 post(new AccessDeviceEvent(
774 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
775 null, null));
776 break;
alshabib7c190012016-02-09 18:22:33 -0800777 case DEVICE_AVAILABILITY_CHANGED:
778 if (deviceService.isAvailable(devId)) {
779 post(new AccessDeviceEvent(
780 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
781 null, null));
Jonathan Hart403372d2018-08-22 11:44:13 -0700782 checkAndCreateDeviceFlows(dev);
alshabib7c190012016-02-09 18:22:33 -0800783 } else {
784 post(new AccessDeviceEvent(
785 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
786 null, null));
787 }
788 break;
alshabib8e4fd2f2016-01-12 15:55:53 -0800789 case DEVICE_UPDATED:
alshabibf0e7e702015-05-30 18:22:36 -0700790 case DEVICE_SUSPENDED:
alshabibf0e7e702015-05-30 18:22:36 -0700791 case PORT_STATS_UPDATED:
792 default:
793 return;
794 }
795 }
796 }
alshabibf0e7e702015-05-30 18:22:36 -0700797}