blob: 6092cec97ff90d64f5353e211ef6d61cb6b90d8d [file] [log] [blame]
Ray Milkeyb7f0f642016-01-22 16:08:14 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
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
Ray Milkeyb7f0f642016-01-22 16:08:14 -080018import org.onlab.packet.Ethernet;
19import org.onlab.packet.ONOSLLDP;
Ayaka Koshibe48229222016-05-16 18:04:26 -070020import org.onosproject.cluster.ClusterMetadataService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080021import org.onosproject.core.ApplicationId;
22import org.onosproject.core.CoreService;
23import org.onosproject.mastership.MastershipService;
24import org.onosproject.net.ConnectPoint;
25import org.onosproject.net.DefaultAnnotations;
26import org.onosproject.net.Device;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.Link;
29import org.onosproject.net.LinkKey;
30import org.onosproject.net.Port;
31import org.onosproject.net.PortNumber;
32import org.onosproject.net.config.NetworkConfigEvent;
33import org.onosproject.net.config.NetworkConfigListener;
34import org.onosproject.net.config.NetworkConfigRegistry;
35import org.onosproject.net.config.basics.BasicLinkConfig;
36import org.onosproject.net.device.DeviceEvent;
37import org.onosproject.net.device.DeviceListener;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.link.DefaultLinkDescription;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080042import org.onosproject.net.link.LinkProviderRegistry;
43import org.onosproject.net.link.LinkProviderService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044import org.onosproject.net.link.ProbedLinkProvider;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080045import org.onosproject.net.packet.InboundPacket;
46import org.onosproject.net.packet.PacketContext;
47import org.onosproject.net.packet.PacketPriority;
48import org.onosproject.net.packet.PacketProcessor;
49import org.onosproject.net.packet.PacketService;
50import org.onosproject.net.provider.AbstractProvider;
51import org.onosproject.net.provider.ProviderId;
Ray Milkey957390e2016-02-09 10:02:46 -080052import org.onosproject.provider.lldpcommon.LinkDiscovery;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070053import org.onosproject.provider.lldpcommon.LinkDiscoveryContext;
54import org.osgi.service.component.annotations.Activate;
55import org.osgi.service.component.annotations.Component;
56import org.osgi.service.component.annotations.Deactivate;
57import org.osgi.service.component.annotations.Reference;
58import org.osgi.service.component.annotations.ReferenceCardinality;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062import java.util.HashSet;
63import java.util.Map;
64import java.util.Optional;
65import java.util.Set;
66import java.util.concurrent.ConcurrentHashMap;
67
Ray Milkeyb7f0f642016-01-22 16:08:14 -080068import static org.onlab.packet.Ethernet.TYPE_BSN;
69import static org.onlab.packet.Ethernet.TYPE_LLDP;
70import static org.onosproject.net.PortNumber.portNumber;
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070071import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROP_PROBE_RATE;
72import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROBE_RATE_DEFAULT;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080073
74/**
75 * Provider to pre-discover links and devices based on a specified network
76 * config.
77 */
78
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070079@Component(immediate = true,
80 property = {
81 PROP_PROBE_RATE + ":Integer=" + PROBE_RATE_DEFAULT,
82 })
Ray Milkeyb7f0f642016-01-22 16:08:14 -080083public class NetworkConfigLinksProvider
84 extends AbstractProvider
Ayaka Koshibe48229222016-05-16 18:04:26 -070085 implements ProbedLinkProvider {
Ray Milkeyb7f0f642016-01-22 16:08:14 -080086
Ray Milkeyd84f89b2018-08-17 14:54:17 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -080088 protected LinkProviderRegistry providerRegistry;
89
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -080091 protected DeviceService deviceService;
92
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -080094 protected PacketService packetService;
95
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -080097 protected MastershipService masterService;
98
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800100 protected NetworkConfigRegistry netCfgService;
101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800103 protected CoreService coreService;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ayaka Koshibe48229222016-05-16 18:04:26 -0700106 protected ClusterMetadataService metadataService;
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 //@Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
109 // label = "LLDP and BDDP probe rate specified in millis")
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700110 private int probeRate = PROBE_RATE_DEFAULT;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800111
112 // Device link discovery helpers.
113 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
114
Ray Milkey957390e2016-02-09 10:02:46 -0800115 private final LinkDiscoveryContext context = new InternalDiscoveryContext();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800116
117 private LinkProviderService providerService;
118
119 private static final String PROVIDER_NAME =
120 "org.onosproject.provider.netcfglinks";
121 private final Logger log = LoggerFactory.getLogger(getClass());
122
123 private ApplicationId appId;
124 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
125 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
126 private final InternalConfigListener cfgListener = new InternalConfigListener();
127
Ray Milkeycd6ab182016-02-03 11:13:09 -0800128 protected Set<LinkKey> configuredLinks = new HashSet<>();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800129
130 public NetworkConfigLinksProvider() {
131 super(new ProviderId("lldp", PROVIDER_NAME));
132 }
133
Sho SHIMIZU9efeb812016-08-18 09:29:20 -0700134 private String buildSrcMac() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700135 String srcMac = ProbedLinkProvider.fingerprintMac(metadataService.getClusterMetadata());
136 String defMac = ProbedLinkProvider.defaultMac();
137 if (srcMac.equals(defMac)) {
138 log.warn("Couldn't generate fingerprint. Using default value {}", defMac);
139 return defMac;
140 }
141 log.trace("Generated MAC address {}", srcMac);
142 return srcMac;
143 }
144
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800145 private void createLinks() {
146 netCfgService.getSubjects(LinkKey.class)
147 .forEach(linkKey -> configuredLinks.add(linkKey));
148 }
149
150 @Activate
151 protected void activate() {
152 log.info("Activated");
153 appId = coreService.registerApplication(PROVIDER_NAME);
154 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
155 providerService = providerRegistry.register(this);
156 deviceService.addListener(deviceListener);
157 netCfgService.addListener(cfgListener);
158 requestIntercepts();
159 loadDevices();
160 createLinks();
161 }
162
163 @Deactivate
164 protected void deactivate() {
165 withdrawIntercepts();
166 providerRegistry.unregister(this);
Deepa Vaddireddy21f5ae82017-05-10 18:27:30 +0530167 deviceService.removeListener(deviceListener);
168 netCfgService.removeListener(cfgListener);
169 packetService.removeProcessor(packetProcessor);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800170 disable();
171 log.info("Deactivated");
172 }
173
174 /**
175 * Loads available devices and registers their ports to be probed.
176 */
177 private void loadDevices() {
178 deviceService.getAvailableDevices()
179 .forEach(d -> updateDevice(d)
180 .ifPresent(ld -> updatePorts(ld, d.id())));
181 }
182
183 private Optional<LinkDiscovery> updateDevice(Device device) {
184 if (device == null) {
185 return Optional.empty();
186 }
187
188 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
Ayaka Koshibe48229222016-05-16 18:04:26 -0700189 did -> new LinkDiscovery(device, context));
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800190 if (ld.isStopped()) {
191 ld.start();
192 }
193 return Optional.of(ld);
194 }
195
196 /**
197 * Updates ports of the specified device to the specified discovery helper.
198 */
199 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
200 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
201 }
202
203
204 private void updatePort(LinkDiscovery discoverer, Port port) {
205 if (port == null) {
206 return;
207 }
208 if (port.number().isLogical()) {
209 // silently ignore logical ports
210 return;
211 }
212
213 discoverer.addPort(port);
214 }
215
216 /**
217 * Disables link discovery processing.
218 */
219 private void disable() {
220
221 providerRegistry.unregister(this);
222 discoverers.values().forEach(LinkDiscovery::stop);
223 discoverers.clear();
224
225 providerService = null;
226 }
227
228 /**
229 * Provides processing context for the device link discovery helpers.
230 */
Ray Milkey957390e2016-02-09 10:02:46 -0800231 private class InternalDiscoveryContext implements LinkDiscoveryContext {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800232 @Override
233 public MastershipService mastershipService() {
234 return masterService;
235 }
236
237 @Override
238 public LinkProviderService providerService() {
239 return providerService;
240 }
241
242 @Override
243 public PacketService packetService() {
244 return packetService;
245 }
246
247 @Override
248 public long probeRate() {
249 return probeRate;
250 }
251
252 @Override
253 public boolean useBddp() {
254 return true;
255 }
256
257 @Override
258 public void touchLink(LinkKey key) {
Ray Milkey957390e2016-02-09 10:02:46 -0800259 }
260
261 @Override
262 public String fingerprint() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700263 return buildSrcMac();
Ray Milkey957390e2016-02-09 10:02:46 -0800264 }
265
266 @Override
267 public DeviceService deviceService() {
268 return deviceService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800269 }
270 }
271
272 LinkKey extractLinkKey(PacketContext packetContext) {
273 Ethernet eth = packetContext.inPacket().parsed();
274 if (eth == null) {
275 return null;
276 }
277
278 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
279 if (onoslldp != null) {
280 PortNumber srcPort = portNumber(onoslldp.getPort());
281 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
282 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
283 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
284
285 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
286 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
287 return LinkKey.linkKey(src, dst);
288 }
289 return null;
290 }
291
292 /**
Ray Milkeycd6ab182016-02-03 11:13:09 -0800293 * Removes after stopping discovery helper for specified device.
294 * @param deviceId device to remove
295 */
296 private void removeDevice(final DeviceId deviceId) {
297 discoverers.computeIfPresent(deviceId, (did, ld) -> {
298 ld.stop();
299 return null;
300 });
301
302 }
303
304 /**
305 * Removes a port from the specified discovery helper.
306 * @param port the port
307 */
308 private void removePort(Port port) {
309 if (port.element() instanceof Device) {
310 Device d = (Device) port.element();
311 LinkDiscovery ld = discoverers.get(d.id());
312 if (ld != null) {
313 ld.removePort(port.number());
314 }
315 } else {
316 log.warn("Attempted to remove non-Device port", port);
317 }
318 }
319
320
321 /**
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800322 * Processes incoming packets.
323 */
324 private class InternalPacketProcessor implements PacketProcessor {
325 @Override
326 public void process(PacketContext context) {
327 if (context == null || context.isHandled()) {
328 return;
329 }
330
331 Ethernet eth = context.inPacket().parsed();
332 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
333 return;
334 }
335
336 InboundPacket inPacket = context.inPacket();
337 LinkKey linkKey = extractLinkKey(context);
338 if (linkKey != null) {
339 if (configuredLinks.contains(linkKey)) {
340 log.debug("Found configured link {}", linkKey);
341 LinkDiscovery ld = discoverers.get(inPacket.receivedFrom().deviceId());
342 if (ld == null) {
343 return;
344 }
345 if (ld.handleLldp(context)) {
346 context.block();
347 }
348 } else {
349 log.debug("Found link that was not in the configuration {}", linkKey);
350 providerService.linkDetected(
351 new DefaultLinkDescription(linkKey.src(),
352 linkKey.dst(),
353 Link.Type.DIRECT,
354 DefaultLinkDescription.NOT_EXPECTED,
355 DefaultAnnotations.EMPTY));
356 }
357 }
358 }
359 }
360
361 /**
362 * Requests packet intercepts.
363 */
364 private void requestIntercepts() {
365 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
366 selector.matchEthType(TYPE_LLDP);
367 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
368 appId, Optional.empty());
369
370 selector.matchEthType(TYPE_BSN);
371
372 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
373 appId, Optional.empty());
374
375 }
376
377 /**
378 * Withdraws packet intercepts.
379 */
380 private void withdrawIntercepts() {
381 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
382 selector.matchEthType(TYPE_LLDP);
383 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
384 appId, Optional.empty());
385 selector.matchEthType(TYPE_BSN);
386 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
387 appId, Optional.empty());
388 }
389
390 /**
391 * Processes device events.
392 */
393 private class InternalDeviceListener implements DeviceListener {
394 @Override
395 public void event(DeviceEvent event) {
396 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
397 return;
398 }
399 Device device = event.subject();
400 Port port = event.port();
401 if (device == null) {
402 log.error("Device is null.");
403 return;
404 }
405 log.trace("{} {} {}", event.type(), event.subject(), event);
406 final DeviceId deviceId = device.id();
407 switch (event.type()) {
408 case DEVICE_ADDED:
409 case DEVICE_UPDATED:
410 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
411 break;
412 case PORT_ADDED:
413 case PORT_UPDATED:
414 if (port.isEnabled()) {
415 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
416 } else {
417 log.debug("Port down {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800418 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800419 providerService.linksVanished(new ConnectPoint(port.element().id(),
420 port.number()));
421 }
422 break;
423 case PORT_REMOVED:
424 log.debug("Port removed {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800425 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800426 providerService.linksVanished(new ConnectPoint(port.element().id(),
427 port.number()));
428 break;
429 case DEVICE_REMOVED:
430 case DEVICE_SUSPENDED:
431 log.debug("Device removed {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800432 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800433 providerService.linksVanished(deviceId);
434 break;
435 case DEVICE_AVAILABILITY_CHANGED:
436 if (deviceService.isAvailable(deviceId)) {
437 log.debug("Device up {}", deviceId);
438 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
439 } else {
440 log.debug("Device down {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800441 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800442 providerService.linksVanished(deviceId);
443 }
444 break;
445 case PORT_STATS_UPDATED:
446 break;
447 default:
448 log.debug("Unknown event {}", event);
449 }
450 }
451 }
452
453 private class InternalConfigListener implements NetworkConfigListener {
454
455 private void addLink(LinkKey linkKey) {
456 configuredLinks.add(linkKey);
457 }
458
459 private void removeLink(LinkKey linkKey) {
460 DefaultLinkDescription linkDescription =
461 new DefaultLinkDescription(linkKey.src(), linkKey.dst(),
462 Link.Type.DIRECT);
463 configuredLinks.remove(linkKey);
464 providerService.linkVanished(linkDescription);
465 }
466
467 @Override
468 public void event(NetworkConfigEvent event) {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800469 if (event.configClass().equals(BasicLinkConfig.class)) {
470 log.info("net config event of type {} for basic link {}",
471 event.type(), event.subject());
472 LinkKey linkKey = (LinkKey) event.subject();
473 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
474 addLink(linkKey);
475 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
476 removeLink(linkKey);
477 }
Charles Chane527eff2016-05-18 14:04:57 -0700478 log.info("Link reconfigured");
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800479 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800480 }
481 }
482
483}