blob: 6851e7e5f7bdb38f9e128769c329145466cd44fc [file] [log] [blame]
Ray Milkeyb7f0f642016-01-22 16:08:14 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Ray Milkeyb7f0f642016-01-22 16:08:14 -08003 *
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.provider.netcfglinks;
17
18import java.util.HashSet;
19import java.util.Map;
20import java.util.Optional;
21import java.util.Set;
22import java.util.concurrent.ConcurrentHashMap;
23
24import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Property;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.onlab.packet.Ethernet;
31import org.onlab.packet.ONOSLLDP;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.mastership.MastershipService;
35import org.onosproject.net.ConnectPoint;
36import org.onosproject.net.DefaultAnnotations;
37import org.onosproject.net.Device;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.Link;
40import org.onosproject.net.LinkKey;
41import org.onosproject.net.Port;
42import org.onosproject.net.PortNumber;
43import org.onosproject.net.config.NetworkConfigEvent;
44import org.onosproject.net.config.NetworkConfigListener;
45import org.onosproject.net.config.NetworkConfigRegistry;
46import org.onosproject.net.config.basics.BasicLinkConfig;
47import 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.TrafficSelector;
52import org.onosproject.net.link.DefaultLinkDescription;
53import org.onosproject.net.link.LinkProvider;
54import org.onosproject.net.link.LinkProviderRegistry;
55import org.onosproject.net.link.LinkProviderService;
56import org.onosproject.net.packet.InboundPacket;
57import org.onosproject.net.packet.PacketContext;
58import org.onosproject.net.packet.PacketPriority;
59import org.onosproject.net.packet.PacketProcessor;
60import org.onosproject.net.packet.PacketService;
61import org.onosproject.net.provider.AbstractProvider;
62import org.onosproject.net.provider.ProviderId;
Ray Milkey957390e2016-02-09 10:02:46 -080063import org.onosproject.provider.lldpcommon.LinkDiscoveryContext;
64import org.onosproject.provider.lldpcommon.LinkDiscovery;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080065import org.slf4j.Logger;
66import org.slf4j.LoggerFactory;
67
68import static org.onlab.packet.Ethernet.TYPE_BSN;
69import static org.onlab.packet.Ethernet.TYPE_LLDP;
70import static org.onosproject.net.PortNumber.portNumber;
71
72/**
73 * Provider to pre-discover links and devices based on a specified network
74 * config.
75 */
76
77@Component(immediate = true)
78public class NetworkConfigLinksProvider
79 extends AbstractProvider
80 implements LinkProvider {
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected LinkProviderRegistry providerRegistry;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DeviceService deviceService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected PacketService packetService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected MastershipService masterService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected NetworkConfigRegistry netCfgService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected CoreService coreService;
99
100 private static final String PROP_PROBE_RATE = "probeRate";
101 private static final int DEFAULT_PROBE_RATE = 3000;
102 @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
103 label = "LLDP and BDDP probe rate specified in millis")
104 private int probeRate = DEFAULT_PROBE_RATE;
105
106 // Device link discovery helpers.
107 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
108
Ray Milkey957390e2016-02-09 10:02:46 -0800109 private final LinkDiscoveryContext context = new InternalDiscoveryContext();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800110
111 private LinkProviderService providerService;
112
113 private static final String PROVIDER_NAME =
114 "org.onosproject.provider.netcfglinks";
115 private final Logger log = LoggerFactory.getLogger(getClass());
116
117 private ApplicationId appId;
118 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
119 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
120 private final InternalConfigListener cfgListener = new InternalConfigListener();
121
Ray Milkeycd6ab182016-02-03 11:13:09 -0800122 protected Set<LinkKey> configuredLinks = new HashSet<>();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800123
124 public NetworkConfigLinksProvider() {
125 super(new ProviderId("lldp", PROVIDER_NAME));
126 }
127
128 private void createLinks() {
129 netCfgService.getSubjects(LinkKey.class)
130 .forEach(linkKey -> configuredLinks.add(linkKey));
131 }
132
133 @Activate
134 protected void activate() {
135 log.info("Activated");
136 appId = coreService.registerApplication(PROVIDER_NAME);
137 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
138 providerService = providerRegistry.register(this);
139 deviceService.addListener(deviceListener);
140 netCfgService.addListener(cfgListener);
141 requestIntercepts();
142 loadDevices();
143 createLinks();
144 }
145
146 @Deactivate
147 protected void deactivate() {
148 withdrawIntercepts();
149 providerRegistry.unregister(this);
150 disable();
151 log.info("Deactivated");
152 }
153
154 /**
155 * Loads available devices and registers their ports to be probed.
156 */
157 private void loadDevices() {
158 deviceService.getAvailableDevices()
159 .forEach(d -> updateDevice(d)
160 .ifPresent(ld -> updatePorts(ld, d.id())));
161 }
162
163 private Optional<LinkDiscovery> updateDevice(Device device) {
164 if (device == null) {
165 return Optional.empty();
166 }
167
168 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
169 did -> new LinkDiscovery(device, context));
170 if (ld.isStopped()) {
171 ld.start();
172 }
173 return Optional.of(ld);
174 }
175
176 /**
177 * Updates ports of the specified device to the specified discovery helper.
178 */
179 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
180 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
181 }
182
183
184 private void updatePort(LinkDiscovery discoverer, Port port) {
185 if (port == null) {
186 return;
187 }
188 if (port.number().isLogical()) {
189 // silently ignore logical ports
190 return;
191 }
192
193 discoverer.addPort(port);
194 }
195
196 /**
197 * Disables link discovery processing.
198 */
199 private void disable() {
200
201 providerRegistry.unregister(this);
202 discoverers.values().forEach(LinkDiscovery::stop);
203 discoverers.clear();
204
205 providerService = null;
206 }
207
208 /**
209 * Provides processing context for the device link discovery helpers.
210 */
Ray Milkey957390e2016-02-09 10:02:46 -0800211 private class InternalDiscoveryContext implements LinkDiscoveryContext {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800212 @Override
213 public MastershipService mastershipService() {
214 return masterService;
215 }
216
217 @Override
218 public LinkProviderService providerService() {
219 return providerService;
220 }
221
222 @Override
223 public PacketService packetService() {
224 return packetService;
225 }
226
227 @Override
228 public long probeRate() {
229 return probeRate;
230 }
231
232 @Override
233 public boolean useBddp() {
234 return true;
235 }
236
237 @Override
238 public void touchLink(LinkKey key) {
Ray Milkey957390e2016-02-09 10:02:46 -0800239 }
240
241 @Override
242 public String fingerprint() {
243 return "";
244 }
245
246 @Override
247 public DeviceService deviceService() {
248 return deviceService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800249 }
250 }
251
252 LinkKey extractLinkKey(PacketContext packetContext) {
253 Ethernet eth = packetContext.inPacket().parsed();
254 if (eth == null) {
255 return null;
256 }
257
258 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
259 if (onoslldp != null) {
260 PortNumber srcPort = portNumber(onoslldp.getPort());
261 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
262 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
263 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
264
265 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
266 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
267 return LinkKey.linkKey(src, dst);
268 }
269 return null;
270 }
271
272 /**
Ray Milkeycd6ab182016-02-03 11:13:09 -0800273 * Removes after stopping discovery helper for specified device.
274 * @param deviceId device to remove
275 */
276 private void removeDevice(final DeviceId deviceId) {
277 discoverers.computeIfPresent(deviceId, (did, ld) -> {
278 ld.stop();
279 return null;
280 });
281
282 }
283
284 /**
285 * Removes a port from the specified discovery helper.
286 * @param port the port
287 */
288 private void removePort(Port port) {
289 if (port.element() instanceof Device) {
290 Device d = (Device) port.element();
291 LinkDiscovery ld = discoverers.get(d.id());
292 if (ld != null) {
293 ld.removePort(port.number());
294 }
295 } else {
296 log.warn("Attempted to remove non-Device port", port);
297 }
298 }
299
300
301 /**
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800302 * Processes incoming packets.
303 */
304 private class InternalPacketProcessor implements PacketProcessor {
305 @Override
306 public void process(PacketContext context) {
307 if (context == null || context.isHandled()) {
308 return;
309 }
310
311 Ethernet eth = context.inPacket().parsed();
312 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
313 return;
314 }
315
316 InboundPacket inPacket = context.inPacket();
317 LinkKey linkKey = extractLinkKey(context);
318 if (linkKey != null) {
319 if (configuredLinks.contains(linkKey)) {
320 log.debug("Found configured link {}", linkKey);
321 LinkDiscovery ld = discoverers.get(inPacket.receivedFrom().deviceId());
322 if (ld == null) {
323 return;
324 }
325 if (ld.handleLldp(context)) {
326 context.block();
327 }
328 } else {
329 log.debug("Found link that was not in the configuration {}", linkKey);
330 providerService.linkDetected(
331 new DefaultLinkDescription(linkKey.src(),
332 linkKey.dst(),
333 Link.Type.DIRECT,
334 DefaultLinkDescription.NOT_EXPECTED,
335 DefaultAnnotations.EMPTY));
336 }
337 }
338 }
339 }
340
341 /**
342 * Requests packet intercepts.
343 */
344 private void requestIntercepts() {
345 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
346 selector.matchEthType(TYPE_LLDP);
347 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
348 appId, Optional.empty());
349
350 selector.matchEthType(TYPE_BSN);
351
352 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
353 appId, Optional.empty());
354
355 }
356
357 /**
358 * Withdraws packet intercepts.
359 */
360 private void withdrawIntercepts() {
361 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
362 selector.matchEthType(TYPE_LLDP);
363 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
364 appId, Optional.empty());
365 selector.matchEthType(TYPE_BSN);
366 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
367 appId, Optional.empty());
368 }
369
370 /**
371 * Processes device events.
372 */
373 private class InternalDeviceListener implements DeviceListener {
374 @Override
375 public void event(DeviceEvent event) {
376 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
377 return;
378 }
379 Device device = event.subject();
380 Port port = event.port();
381 if (device == null) {
382 log.error("Device is null.");
383 return;
384 }
385 log.trace("{} {} {}", event.type(), event.subject(), event);
386 final DeviceId deviceId = device.id();
387 switch (event.type()) {
388 case DEVICE_ADDED:
389 case DEVICE_UPDATED:
390 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
391 break;
392 case PORT_ADDED:
393 case PORT_UPDATED:
394 if (port.isEnabled()) {
395 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
396 } else {
397 log.debug("Port down {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800398 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800399 providerService.linksVanished(new ConnectPoint(port.element().id(),
400 port.number()));
401 }
402 break;
403 case PORT_REMOVED:
404 log.debug("Port removed {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800405 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800406 providerService.linksVanished(new ConnectPoint(port.element().id(),
407 port.number()));
408 break;
409 case DEVICE_REMOVED:
410 case DEVICE_SUSPENDED:
411 log.debug("Device removed {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800412 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800413 providerService.linksVanished(deviceId);
414 break;
415 case DEVICE_AVAILABILITY_CHANGED:
416 if (deviceService.isAvailable(deviceId)) {
417 log.debug("Device up {}", deviceId);
418 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
419 } else {
420 log.debug("Device down {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800421 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800422 providerService.linksVanished(deviceId);
423 }
424 break;
425 case PORT_STATS_UPDATED:
426 break;
427 default:
428 log.debug("Unknown event {}", event);
429 }
430 }
431 }
432
433 private class InternalConfigListener implements NetworkConfigListener {
434
435 private void addLink(LinkKey linkKey) {
436 configuredLinks.add(linkKey);
437 }
438
439 private void removeLink(LinkKey linkKey) {
440 DefaultLinkDescription linkDescription =
441 new DefaultLinkDescription(linkKey.src(), linkKey.dst(),
442 Link.Type.DIRECT);
443 configuredLinks.remove(linkKey);
444 providerService.linkVanished(linkDescription);
445 }
446
447 @Override
448 public void event(NetworkConfigEvent event) {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800449 if (event.configClass().equals(BasicLinkConfig.class)) {
450 log.info("net config event of type {} for basic link {}",
451 event.type(), event.subject());
452 LinkKey linkKey = (LinkKey) event.subject();
453 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
454 addLink(linkKey);
455 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
456 removeLink(linkKey);
457 }
Charles Chane527eff2016-05-18 14:04:57 -0700458 log.info("Link reconfigured");
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800459 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800460 }
461 }
462
463}