blob: 4b2f55bb7025203d06199d57459b9d84a6e7c222 [file] [log] [blame]
alshabib0ccde6d2015-05-30 18:22:36 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
16package org.onosproject.olt;
17
alshabib02cbe6a2016-01-14 17:27:11 -080018import com.google.common.collect.Maps;
19import com.google.common.collect.Sets;
alshabib0ccde6d2015-05-30 18:22:36 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
alshabib0ccde6d2015-05-30 18:22:36 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart3e594642015-10-20 17:31:24 -070025import org.apache.felix.scr.annotations.Service;
alshabibbb424232016-01-15 12:20:25 -080026import org.onlab.packet.EthType;
27import org.onlab.packet.IPv4;
alshabib0ccde6d2015-05-30 18:22:36 -070028import org.onlab.packet.VlanId;
alshabib0ccde6d2015-05-30 18:22:36 -070029import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
alshabib6b139b42016-01-12 15:55:53 -080031import org.onosproject.event.AbstractListenerManager;
Jonathan Hart3e594642015-10-20 17:31:24 -070032import org.onosproject.net.ConnectPoint;
alshabib0ccde6d2015-05-30 18:22:36 -070033import org.onosproject.net.DeviceId;
alshabibbb424232016-01-15 12:20:25 -080034import org.onosproject.net.Port;
alshabib0ccde6d2015-05-30 18:22:36 -070035import org.onosproject.net.PortNumber;
Jonathan Hart3e594642015-10-20 17:31:24 -070036import org.onosproject.net.config.ConfigFactory;
37import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigListener;
39import org.onosproject.net.config.NetworkConfigRegistry;
40import org.onosproject.net.config.basics.SubjectFactories;
alshabib0ccde6d2015-05-30 18:22:36 -070041import org.onosproject.net.device.DeviceEvent;
42import org.onosproject.net.device.DeviceListener;
43import org.onosproject.net.device.DeviceService;
44import org.onosproject.net.flow.DefaultTrafficSelector;
45import org.onosproject.net.flow.DefaultTrafficTreatment;
46import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
alshabibbb424232016-01-15 12:20:25 -080048import org.onosproject.net.flow.criteria.Criteria;
49import org.onosproject.net.flowobjective.DefaultFilteringObjective;
alshabib0ccde6d2015-05-30 18:22:36 -070050import org.onosproject.net.flowobjective.DefaultForwardingObjective;
alshabibbb424232016-01-15 12:20:25 -080051import org.onosproject.net.flowobjective.FilteringObjective;
alshabib0ccde6d2015-05-30 18:22:36 -070052import org.onosproject.net.flowobjective.FlowObjectiveService;
53import org.onosproject.net.flowobjective.ForwardingObjective;
alshabib49cadbd2016-01-12 18:06:53 -080054import org.onosproject.net.flowobjective.Objective;
55import org.onosproject.net.flowobjective.ObjectiveContext;
56import org.onosproject.net.flowobjective.ObjectiveError;
alshabib6b139b42016-01-12 15:55:53 -080057import org.onosproject.olt.api.AccessDeviceEvent;
58import org.onosproject.olt.api.AccessDeviceListener;
alshabib0ccde6d2015-05-30 18:22:36 -070059import org.slf4j.Logger;
60
Jonathan Hart3e594642015-10-20 17:31:24 -070061import java.util.Map;
Jonathan Hart48327842015-11-10 16:09:22 -080062import java.util.Optional;
alshabib02cbe6a2016-01-14 17:27:11 -080063import java.util.Set;
alshabib49cadbd2016-01-12 18:06:53 -080064import java.util.concurrent.CompletableFuture;
Jonathan Hart3e594642015-10-20 17:31:24 -070065import java.util.concurrent.ConcurrentHashMap;
alshabib49cadbd2016-01-12 18:06:53 -080066import java.util.concurrent.ExecutorService;
67import java.util.concurrent.Executors;
alshabib0ccde6d2015-05-30 18:22:36 -070068
alshabib49cadbd2016-01-12 18:06:53 -080069import static org.onlab.util.Tools.groupedThreads;
alshabib0ccde6d2015-05-30 18:22:36 -070070import static org.slf4j.LoggerFactory.getLogger;
71
72/**
Jonathan Hart3e594642015-10-20 17:31:24 -070073 * Provisions rules on access devices.
alshabib0ccde6d2015-05-30 18:22:36 -070074 */
Jonathan Hart3e594642015-10-20 17:31:24 -070075@Service
alshabib0ccde6d2015-05-30 18:22:36 -070076@Component(immediate = true)
alshabib6b139b42016-01-12 15:55:53 -080077public class Olt
78 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
79 implements AccessDeviceService {
alshabib0ccde6d2015-05-30 18:22:36 -070080 private final Logger log = getLogger(getClass());
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected FlowObjectiveService flowObjectiveService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DeviceService deviceService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected CoreService coreService;
90
Jonathan Hart3e594642015-10-20 17:31:24 -070091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected NetworkConfigRegistry networkConfig;
93
alshabib0ccde6d2015-05-30 18:22:36 -070094 private final DeviceListener deviceListener = new InternalDeviceListener();
95
96 private ApplicationId appId;
97
Jonathan Hart3e594642015-10-20 17:31:24 -070098 private static final VlanId DEFAULT_VLAN = VlanId.vlanId((short) 0);
alshabib17ff25f2015-06-01 10:57:31 -070099
alshabib49cadbd2016-01-12 18:06:53 -0800100 private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
alshabib02cbe6a2016-01-14 17:27:11 -0800101 groupedThreads("onos/olt-service",
102 "olt-installer-%d"));
alshabib1af3b712015-06-05 14:55:24 -0700103
Jonathan Hart3e594642015-10-20 17:31:24 -0700104 private Map<DeviceId, AccessDeviceData> oltData = new ConcurrentHashMap<>();
105
alshabib02cbe6a2016-01-14 17:27:11 -0800106 private Map<ConnectPoint, Set<ForwardingObjective.Builder>> objectives =
107 Maps.newConcurrentMap();
108
109 private Map<ConnectPoint, VlanId> subscribers = Maps.newConcurrentMap();
110
Jonathan Hart3e594642015-10-20 17:31:24 -0700111 private InternalNetworkConfigListener configListener =
112 new InternalNetworkConfigListener();
113 private static final Class<AccessDeviceConfig> CONFIG_CLASS =
114 AccessDeviceConfig.class;
115
116 private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory =
117 new ConfigFactory<DeviceId, AccessDeviceConfig>(
118 SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
alshabib02cbe6a2016-01-14 17:27:11 -0800119 @Override
120 public AccessDeviceConfig createConfig() {
121 return new AccessDeviceConfig();
122 }
123 };
124
alshabib0ccde6d2015-05-30 18:22:36 -0700125
126 @Activate
127 public void activate() {
alshabib1af3b712015-06-05 14:55:24 -0700128 appId = coreService.registerApplication("org.onosproject.olt");
alshabibbeb2dc92015-06-05 13:35:13 -0700129
alshabib6b139b42016-01-12 15:55:53 -0800130 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
131
Jonathan Hart3e594642015-10-20 17:31:24 -0700132 networkConfig.registerConfigFactory(configFactory);
133 networkConfig.addListener(configListener);
134
alshabibbb424232016-01-15 12:20:25 -0800135
Jonathan Hart3e594642015-10-20 17:31:24 -0700136 networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach(
137 subject -> {
138 AccessDeviceConfig config = networkConfig.getConfig(subject, AccessDeviceConfig.class);
139 if (config != null) {
140 AccessDeviceData data = config.getOlt();
141 oltData.put(data.deviceId(), data);
142 }
143 }
144 );
145
alshabibbb424232016-01-15 12:20:25 -0800146 oltData.keySet().stream()
147 .flatMap(did -> deviceService.getPorts(did).stream())
148 .filter(p -> oltData.get(p.element().id()).uplink() != p.number())
149 .filter(p -> p.isEnabled())
150 .forEach(p -> installFilteringObjectives((DeviceId) p.element().id(), p));
151
alshabib0ccde6d2015-05-30 18:22:36 -0700152 log.info("Started with Application ID {}", appId.id());
153 }
154
155 @Deactivate
156 public void deactivate() {
Jonathan Hart3e594642015-10-20 17:31:24 -0700157 networkConfig.removeListener(configListener);
158 networkConfig.unregisterConfigFactory(configFactory);
alshabib0ccde6d2015-05-30 18:22:36 -0700159 log.info("Stopped");
160 }
161
Jonathan Hart3e594642015-10-20 17:31:24 -0700162 @Override
163 public void provisionSubscriber(ConnectPoint port, VlanId vlan) {
164 AccessDeviceData olt = oltData.get(port.deviceId());
165
166 if (olt == null) {
167 log.warn("No data found for OLT device {}", port.deviceId());
168 return;
169 }
170
Jonathan Hart48327842015-11-10 16:09:22 -0800171 provisionVlans(olt.deviceId(), olt.uplink(), port.port(), vlan, olt.vlan(),
alshabibfa0dc662016-01-13 11:23:53 -0800172 olt.defaultVlan());
173 }
174
175 @Override
176 public void removeSubscriber(ConnectPoint port) {
alshabib02cbe6a2016-01-14 17:27:11 -0800177 AccessDeviceData olt = oltData.get(port.deviceId());
178
179 if (olt == null) {
180 log.warn("No data found for OLT device {}", port.deviceId());
181 return;
182 }
183
184 unprovisionSubscriber(olt.deviceId(), olt.uplink(), port.port(), olt.vlan());
185
186 }
187
188 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
189 PortNumber subscriberPort, VlanId deviceVlan) {
190
191 //FIXME: This method is slightly ugly but it'll do until we have a better
192 // way to remove flows from the flow store.
193
194 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
195 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
196
197 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
198
199 VlanId subscriberVlan = subscribers.remove(cp);
200
201 Set<ForwardingObjective.Builder> fwds = objectives.remove(cp);
202
203 if (fwds == null || fwds.size() != 2) {
204 log.warn("Unknown or incomplete subscriber at {}", cp);
205 return;
206 }
207
208
209 fwds.stream().forEach(
210 fwd -> flowObjectiveService.forward(deviceId,
alshabibbb424232016-01-15 12:20:25 -0800211 fwd.remove(new ObjectiveContext() {
212 @Override
213 public void onSuccess(Objective objective) {
214 upFuture.complete(null);
215 }
alshabib02cbe6a2016-01-14 17:27:11 -0800216
alshabibbb424232016-01-15 12:20:25 -0800217 @Override
218 public void onError(Objective objective, ObjectiveError error) {
219 upFuture.complete(error);
220 }
221 })));
alshabib02cbe6a2016-01-14 17:27:11 -0800222
223 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
224 if (upStatus == null && downStatus == null) {
225 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
226 deviceId,
227 deviceVlan,
228 subscriberVlan));
229 } else if (downStatus != null) {
230 log.error("Subscriber with vlan {} on device {} " +
231 "on port {} failed downstream uninstallation: {}",
232 subscriberVlan, deviceId, subscriberPort, downStatus);
233 } else if (upStatus != null) {
234 log.error("Subscriber with vlan {} on device {} " +
235 "on port {} failed upstream uninstallation: {}",
236 subscriberVlan, deviceId, subscriberPort, upStatus);
237 }
238 }, oltInstallers);
alshabibfa0dc662016-01-13 11:23:53 -0800239
Jonathan Hart3e594642015-10-20 17:31:24 -0700240 }
241
242 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
243 PortNumber subscriberPort,
Jonathan Hart48327842015-11-10 16:09:22 -0800244 VlanId subscriberVlan, VlanId deviceVlan,
245 Optional<VlanId> defaultVlan) {
Jonathan Hart3e594642015-10-20 17:31:24 -0700246
alshabib49cadbd2016-01-12 18:06:53 -0800247 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
248 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
249
Jonathan Hart3e594642015-10-20 17:31:24 -0700250 TrafficSelector upstream = DefaultTrafficSelector.builder()
Jonathan Hart48327842015-11-10 16:09:22 -0800251 .matchVlanId((defaultVlan.isPresent()) ? defaultVlan.get() : DEFAULT_VLAN)
Jonathan Hart3e594642015-10-20 17:31:24 -0700252 .matchInPort(subscriberPort)
253 .build();
254
255 TrafficSelector downstream = DefaultTrafficSelector.builder()
256 .matchVlanId(deviceVlan)
257 .matchInPort(uplinkPort)
alshabibfa0dc662016-01-13 11:23:53 -0800258 .matchInnerVlanId(subscriberVlan)
Jonathan Hart3e594642015-10-20 17:31:24 -0700259 .build();
260
261 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
alshabib49cadbd2016-01-12 18:06:53 -0800262 .pushVlan()
Jonathan Hart3e594642015-10-20 17:31:24 -0700263 .setVlanId(subscriberVlan)
264 .pushVlan()
265 .setVlanId(deviceVlan)
266 .setOutput(uplinkPort)
267 .build();
268
269 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
270 .popVlan()
Jonathan Hart48327842015-11-10 16:09:22 -0800271 .setVlanId((defaultVlan.isPresent()) ? defaultVlan.get() : DEFAULT_VLAN)
Jonathan Hart3e594642015-10-20 17:31:24 -0700272 .setOutput(subscriberPort)
273 .build();
274
275
alshabib02cbe6a2016-01-14 17:27:11 -0800276 ForwardingObjective.Builder upFwd = DefaultForwardingObjective.builder()
Jonathan Hart3e594642015-10-20 17:31:24 -0700277 .withFlag(ForwardingObjective.Flag.VERSATILE)
278 .withPriority(1000)
279 .makePermanent()
280 .withSelector(upstream)
281 .fromApp(appId)
alshabib02cbe6a2016-01-14 17:27:11 -0800282 .withTreatment(upstreamTreatment);
alshabib49cadbd2016-01-12 18:06:53 -0800283
Jonathan Hart3e594642015-10-20 17:31:24 -0700284
alshabib02cbe6a2016-01-14 17:27:11 -0800285 ForwardingObjective.Builder downFwd = DefaultForwardingObjective.builder()
Jonathan Hart3e594642015-10-20 17:31:24 -0700286 .withFlag(ForwardingObjective.Flag.VERSATILE)
287 .withPriority(1000)
288 .makePermanent()
289 .withSelector(downstream)
290 .fromApp(appId)
alshabib02cbe6a2016-01-14 17:27:11 -0800291 .withTreatment(downstreamTreatment);
alshabib49cadbd2016-01-12 18:06:53 -0800292
alshabib02cbe6a2016-01-14 17:27:11 -0800293 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
Jonathan Hart3e594642015-10-20 17:31:24 -0700294
alshabib02cbe6a2016-01-14 17:27:11 -0800295 subscribers.put(cp, subscriberVlan);
296 objectives.put(cp, Sets.newHashSet(upFwd, downFwd));
297
298
299 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
300 @Override
301 public void onSuccess(Objective objective) {
302 upFuture.complete(null);
303 }
304
305 @Override
306 public void onError(Objective objective, ObjectiveError error) {
307 upFuture.complete(error);
308 }
309 }));
310
311
312 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
313 @Override
314 public void onSuccess(Objective objective) {
315 downFuture.complete(null);
316 }
317
318 @Override
319 public void onError(Objective objective, ObjectiveError error) {
320 downFuture.complete(error);
321 }
322 }));
alshabib49cadbd2016-01-12 18:06:53 -0800323
324 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
325 if (upStatus == null && downStatus == null) {
326 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
327 deviceId,
328 deviceVlan,
329 subscriberVlan));
330 } else if (downStatus != null) {
331 log.error("Subscriber with vlan {} on device {} " +
332 "on port {} failed downstream installation: {}",
333 subscriberVlan, deviceId, subscriberPort, downStatus);
334 } else if (upStatus != null) {
335 log.error("Subscriber with vlan {} on device {} " +
336 "on port {} failed upstream installation: {}",
337 subscriberVlan, deviceId, subscriberPort, upStatus);
338 }
339 }, oltInstallers);
340
Jonathan Hart3e594642015-10-20 17:31:24 -0700341 }
342
alshabibbb424232016-01-15 12:20:25 -0800343 private void installFilteringObjectives(DeviceId devId, Port port) {
344 FilteringObjective eapol = DefaultFilteringObjective.builder()
345 .permit()
346 .withKey(Criteria.matchInPort(port.number()))
347 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
348 .withMeta(DefaultTrafficTreatment.builder()
349 .setOutput(PortNumber.CONTROLLER).build())
350 .fromApp(appId)
351 .withPriority(1000)
352 .add(new ObjectiveContext() {
353 @Override
354 public void onSuccess(Objective objective) {
355 log.info("Eapol filter for {} on {} installed.",
356 devId, port);
357 }
358
359 @Override
360 public void onError(Objective objective, ObjectiveError error) {
361 log.info("Eapol filter for {} on {} failed because {}",
362 devId, port, error);
363 }
364 });
365
366
367 FilteringObjective igmp = DefaultFilteringObjective.builder()
368 .permit()
369 .withKey(Criteria.matchInPort(port.number()))
370 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
371 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
372 .withMeta(DefaultTrafficTreatment.builder()
373 .setOutput(PortNumber.CONTROLLER).build())
374 .fromApp(appId)
375 .withPriority(1000)
376 .add(new ObjectiveContext() {
377 @Override
378 public void onSuccess(Objective objective) {
379 log.info("Igmp filter for {} on {} installed.",
380 devId, port);
381 }
382
383 @Override
384 public void onError(Objective objective, ObjectiveError error) {
385 log.info("Igmp filter for {} on {} failed because {}.",
386 devId, port, error);
387 }
388 });
389
390 flowObjectiveService.filter(devId, eapol);
391 flowObjectiveService.filter(devId, igmp);
392 }
393
alshabib0ccde6d2015-05-30 18:22:36 -0700394 private class InternalDeviceListener implements DeviceListener {
395 @Override
396 public void event(DeviceEvent event) {
alshabib49cadbd2016-01-12 18:06:53 -0800397 DeviceId devId = event.subject().id();
398 if (!oltData.containsKey(devId)) {
399 log.debug("Device {} is not an OLT", devId);
alshabib6b139b42016-01-12 15:55:53 -0800400 return;
401 }
alshabib0ccde6d2015-05-30 18:22:36 -0700402 switch (event.type()) {
alshabibbb424232016-01-15 12:20:25 -0800403 //TODO: Port handling and bookkeeping should be inproved once
404 // olt firmware handles correct behaviour.
alshabib0ccde6d2015-05-30 18:22:36 -0700405 case PORT_ADDED:
alshabibbb424232016-01-15 12:20:25 -0800406 if (event.port().isEnabled()) {
407 installFilteringObjectives(devId, event.port());
408 }
409 break;
410 case PORT_REMOVED:
411 AccessDeviceData olt = oltData.get(devId);
412 unprovisionSubscriber(devId, olt.uplink(),
413 event.port().number(),
414 olt.vlan());
415 installFilteringObjectives(devId, event.port());
416 break;
alshabib0ccde6d2015-05-30 18:22:36 -0700417 case PORT_UPDATED:
alshabib0ccde6d2015-05-30 18:22:36 -0700418 break;
419 case DEVICE_ADDED:
alshabib6b139b42016-01-12 15:55:53 -0800420 post(new AccessDeviceEvent(
421 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
422 null, null));
423 break;
alshabib0ccde6d2015-05-30 18:22:36 -0700424 case DEVICE_REMOVED:
alshabib6b139b42016-01-12 15:55:53 -0800425 post(new AccessDeviceEvent(
426 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
427 null, null));
428 break;
429 case DEVICE_UPDATED:
alshabib0ccde6d2015-05-30 18:22:36 -0700430 case DEVICE_SUSPENDED:
431 case DEVICE_AVAILABILITY_CHANGED:
alshabib0ccde6d2015-05-30 18:22:36 -0700432 case PORT_STATS_UPDATED:
433 default:
434 return;
435 }
436 }
437 }
438
Jonathan Hart3e594642015-10-20 17:31:24 -0700439 private class InternalNetworkConfigListener implements NetworkConfigListener {
440 @Override
441 public void event(NetworkConfigEvent event) {
442 switch (event.type()) {
443
alshabib02cbe6a2016-01-14 17:27:11 -0800444 case CONFIG_ADDED:
445 case CONFIG_UPDATED:
446 if (event.configClass().equals(CONFIG_CLASS)) {
447 AccessDeviceConfig config =
448 networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS);
449 if (config != null) {
450 oltData.put(config.getOlt().deviceId(), config.getOlt());
451 }
Jonathan Hart3e594642015-10-20 17:31:24 -0700452 }
alshabib02cbe6a2016-01-14 17:27:11 -0800453 break;
454 case CONFIG_UNREGISTERED:
455 case CONFIG_REMOVED:
456 default:
457 break;
Jonathan Hart3e594642015-10-20 17:31:24 -0700458 }
459 }
460 }
alshabib0ccde6d2015-05-30 18:22:36 -0700461
462}