blob: ac00cf84f962994daf4e5a395b69553a5c22ca43 [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;
Ray Milkeyd17309c2018-10-18 09:34:54 -070071import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.DISCOVERY_DELAY_DEFAULT;
72import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROP_DISCOVERY_DELAY;
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070073import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROP_PROBE_RATE;
74import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROBE_RATE_DEFAULT;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080075
76/**
77 * Provider to pre-discover links and devices based on a specified network
78 * config.
79 */
80
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070081@Component(immediate = true,
82 property = {
83 PROP_PROBE_RATE + ":Integer=" + PROBE_RATE_DEFAULT,
Ray Milkeyd17309c2018-10-18 09:34:54 -070084 PROP_DISCOVERY_DELAY + ":Integer=" + DISCOVERY_DELAY_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070085 })
Ray Milkeyb7f0f642016-01-22 16:08:14 -080086public class NetworkConfigLinksProvider
87 extends AbstractProvider
Ayaka Koshibe48229222016-05-16 18:04:26 -070088 implements ProbedLinkProvider {
Ray Milkeyb7f0f642016-01-22 16:08:14 -080089
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -080091 protected LinkProviderRegistry providerRegistry;
92
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -080094 protected DeviceService deviceService;
95
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -080097 protected PacketService packetService;
98
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800100 protected MastershipService masterService;
101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800103 protected NetworkConfigRegistry netCfgService;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800106 protected CoreService coreService;
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ayaka Koshibe48229222016-05-16 18:04:26 -0700109 protected ClusterMetadataService metadataService;
110
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700111 /** LLDP and BDDP probe rate specified in millis. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700112 private int probeRate = PROBE_RATE_DEFAULT;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800113
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700114 /** Number of millis beyond which an LLDP packet will not be accepted. */
Ray Milkeyd17309c2018-10-18 09:34:54 -0700115 private int maxDiscoveryDelayMs = DISCOVERY_DELAY_DEFAULT;
Samuel Jero31e16f52018-09-21 10:34:28 -0400116
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800117 // Device link discovery helpers.
118 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
119
Ray Milkey957390e2016-02-09 10:02:46 -0800120 private final LinkDiscoveryContext context = new InternalDiscoveryContext();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800121
122 private LinkProviderService providerService;
123
124 private static final String PROVIDER_NAME =
125 "org.onosproject.provider.netcfglinks";
126 private final Logger log = LoggerFactory.getLogger(getClass());
127
128 private ApplicationId appId;
129 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
130 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
131 private final InternalConfigListener cfgListener = new InternalConfigListener();
132
Ray Milkeycd6ab182016-02-03 11:13:09 -0800133 protected Set<LinkKey> configuredLinks = new HashSet<>();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800134
135 public NetworkConfigLinksProvider() {
136 super(new ProviderId("lldp", PROVIDER_NAME));
137 }
138
Sho SHIMIZU9efeb812016-08-18 09:29:20 -0700139 private String buildSrcMac() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700140 String srcMac = ProbedLinkProvider.fingerprintMac(metadataService.getClusterMetadata());
141 String defMac = ProbedLinkProvider.defaultMac();
142 if (srcMac.equals(defMac)) {
143 log.warn("Couldn't generate fingerprint. Using default value {}", defMac);
144 return defMac;
145 }
146 log.trace("Generated MAC address {}", srcMac);
147 return srcMac;
148 }
149
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800150 private void createLinks() {
151 netCfgService.getSubjects(LinkKey.class)
152 .forEach(linkKey -> configuredLinks.add(linkKey));
153 }
154
155 @Activate
156 protected void activate() {
157 log.info("Activated");
158 appId = coreService.registerApplication(PROVIDER_NAME);
159 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
160 providerService = providerRegistry.register(this);
161 deviceService.addListener(deviceListener);
162 netCfgService.addListener(cfgListener);
163 requestIntercepts();
164 loadDevices();
165 createLinks();
166 }
167
168 @Deactivate
169 protected void deactivate() {
170 withdrawIntercepts();
171 providerRegistry.unregister(this);
Deepa Vaddireddy21f5ae82017-05-10 18:27:30 +0530172 deviceService.removeListener(deviceListener);
173 netCfgService.removeListener(cfgListener);
174 packetService.removeProcessor(packetProcessor);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800175 disable();
176 log.info("Deactivated");
177 }
178
179 /**
180 * Loads available devices and registers their ports to be probed.
181 */
182 private void loadDevices() {
183 deviceService.getAvailableDevices()
184 .forEach(d -> updateDevice(d)
185 .ifPresent(ld -> updatePorts(ld, d.id())));
186 }
187
188 private Optional<LinkDiscovery> updateDevice(Device device) {
189 if (device == null) {
190 return Optional.empty();
191 }
192
193 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
Ayaka Koshibe48229222016-05-16 18:04:26 -0700194 did -> new LinkDiscovery(device, context));
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800195 if (ld.isStopped()) {
196 ld.start();
197 }
198 return Optional.of(ld);
199 }
200
201 /**
202 * Updates ports of the specified device to the specified discovery helper.
203 */
204 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
205 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
206 }
207
208
209 private void updatePort(LinkDiscovery discoverer, Port port) {
210 if (port == null) {
211 return;
212 }
213 if (port.number().isLogical()) {
214 // silently ignore logical ports
215 return;
216 }
217
218 discoverer.addPort(port);
219 }
220
221 /**
222 * Disables link discovery processing.
223 */
224 private void disable() {
225
226 providerRegistry.unregister(this);
227 discoverers.values().forEach(LinkDiscovery::stop);
228 discoverers.clear();
229
230 providerService = null;
231 }
232
233 /**
234 * Provides processing context for the device link discovery helpers.
235 */
Ray Milkey957390e2016-02-09 10:02:46 -0800236 private class InternalDiscoveryContext implements LinkDiscoveryContext {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800237 @Override
238 public MastershipService mastershipService() {
239 return masterService;
240 }
241
242 @Override
243 public LinkProviderService providerService() {
244 return providerService;
245 }
246
247 @Override
248 public PacketService packetService() {
249 return packetService;
250 }
251
252 @Override
253 public long probeRate() {
254 return probeRate;
255 }
256
257 @Override
258 public boolean useBddp() {
259 return true;
260 }
261
262 @Override
263 public void touchLink(LinkKey key) {
Ray Milkey957390e2016-02-09 10:02:46 -0800264 }
265
266 @Override
267 public String fingerprint() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700268 return buildSrcMac();
Ray Milkey957390e2016-02-09 10:02:46 -0800269 }
270
271 @Override
272 public DeviceService deviceService() {
273 return deviceService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800274 }
Samuel Jero31e16f52018-09-21 10:34:28 -0400275
276 @Override
277 public String lldpSecret() {
278 return metadataService.getClusterMetadata().getClusterSecret();
279 }
280
281 @Override
282 public long maxDiscoveryDelay() {
283 return maxDiscoveryDelayMs;
284 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800285 }
286
Samuel Jero31e16f52018-09-21 10:34:28 -0400287 // true if *NOT* this cluster's own probe.
288 private boolean isOthercluster(String mac) {
289 // if we are using DEFAULT_MAC, clustering hadn't initialized, so conservative 'yes'
290 String ourMac = context.fingerprint();
291 if (ProbedLinkProvider.defaultMac().equalsIgnoreCase(ourMac)) {
292 return true;
293 }
294 return !mac.equalsIgnoreCase(ourMac);
295 }
296
297 //doesn't validate. Used just to decide if this is expected link.
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800298 LinkKey extractLinkKey(PacketContext packetContext) {
299 Ethernet eth = packetContext.inPacket().parsed();
300 if (eth == null) {
301 return null;
302 }
303
304 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
305 if (onoslldp != null) {
306 PortNumber srcPort = portNumber(onoslldp.getPort());
307 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
308 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
309 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
310
311 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
312 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
313 return LinkKey.linkKey(src, dst);
314 }
315 return null;
316 }
317
Samuel Jero31e16f52018-09-21 10:34:28 -0400318 private boolean verify(PacketContext packetContext) {
319 Ethernet eth = packetContext.inPacket().parsed();
320 if (eth == null) {
321 return false;
322 }
323
324 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
325 if (onoslldp != null) {
326 if (!isOthercluster(eth.getSourceMAC().toString())) {
327 return false;
328 }
329
330 if (!ONOSLLDP.verify(onoslldp, context.lldpSecret(), context.maxDiscoveryDelay())) {
331 log.warn("LLDP Packet failed to validate!");
332 return false;
333 }
334 return true;
335 }
336 return false;
337 }
338
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800339 /**
Ray Milkeycd6ab182016-02-03 11:13:09 -0800340 * Removes after stopping discovery helper for specified device.
341 * @param deviceId device to remove
342 */
343 private void removeDevice(final DeviceId deviceId) {
344 discoverers.computeIfPresent(deviceId, (did, ld) -> {
345 ld.stop();
346 return null;
347 });
348
349 }
350
351 /**
352 * Removes a port from the specified discovery helper.
353 * @param port the port
354 */
355 private void removePort(Port port) {
356 if (port.element() instanceof Device) {
357 Device d = (Device) port.element();
358 LinkDiscovery ld = discoverers.get(d.id());
359 if (ld != null) {
360 ld.removePort(port.number());
361 }
362 } else {
363 log.warn("Attempted to remove non-Device port", port);
364 }
365 }
366
367
368 /**
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800369 * Processes incoming packets.
370 */
371 private class InternalPacketProcessor implements PacketProcessor {
372 @Override
373 public void process(PacketContext context) {
374 if (context == null || context.isHandled()) {
375 return;
376 }
377
378 Ethernet eth = context.inPacket().parsed();
379 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
380 return;
381 }
382
383 InboundPacket inPacket = context.inPacket();
384 LinkKey linkKey = extractLinkKey(context);
385 if (linkKey != null) {
386 if (configuredLinks.contains(linkKey)) {
387 log.debug("Found configured link {}", linkKey);
388 LinkDiscovery ld = discoverers.get(inPacket.receivedFrom().deviceId());
389 if (ld == null) {
390 return;
391 }
392 if (ld.handleLldp(context)) {
393 context.block();
394 }
395 } else {
Samuel Jero31e16f52018-09-21 10:34:28 -0400396 if (verify(context)) {
397 log.debug("Found link that was not in the configuration {}", linkKey);
398 providerService.linkDetected(
399 new DefaultLinkDescription(linkKey.src(),
400 linkKey.dst(),
401 Link.Type.DIRECT,
402 DefaultLinkDescription.NOT_EXPECTED,
403 DefaultAnnotations.EMPTY));
404 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800405 }
406 }
407 }
408 }
409
410 /**
411 * Requests packet intercepts.
412 */
413 private void requestIntercepts() {
414 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
415 selector.matchEthType(TYPE_LLDP);
416 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
417 appId, Optional.empty());
418
419 selector.matchEthType(TYPE_BSN);
420
421 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
422 appId, Optional.empty());
423
424 }
425
426 /**
427 * Withdraws packet intercepts.
428 */
429 private void withdrawIntercepts() {
430 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
431 selector.matchEthType(TYPE_LLDP);
432 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
433 appId, Optional.empty());
434 selector.matchEthType(TYPE_BSN);
435 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
436 appId, Optional.empty());
437 }
438
439 /**
440 * Processes device events.
441 */
442 private class InternalDeviceListener implements DeviceListener {
443 @Override
444 public void event(DeviceEvent event) {
445 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
446 return;
447 }
448 Device device = event.subject();
449 Port port = event.port();
450 if (device == null) {
451 log.error("Device is null.");
452 return;
453 }
454 log.trace("{} {} {}", event.type(), event.subject(), event);
455 final DeviceId deviceId = device.id();
456 switch (event.type()) {
457 case DEVICE_ADDED:
458 case DEVICE_UPDATED:
459 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
460 break;
461 case PORT_ADDED:
462 case PORT_UPDATED:
463 if (port.isEnabled()) {
464 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
465 } else {
466 log.debug("Port down {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800467 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800468 providerService.linksVanished(new ConnectPoint(port.element().id(),
469 port.number()));
470 }
471 break;
472 case PORT_REMOVED:
473 log.debug("Port removed {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800474 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800475 providerService.linksVanished(new ConnectPoint(port.element().id(),
476 port.number()));
477 break;
478 case DEVICE_REMOVED:
479 case DEVICE_SUSPENDED:
480 log.debug("Device removed {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800481 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800482 providerService.linksVanished(deviceId);
483 break;
484 case DEVICE_AVAILABILITY_CHANGED:
485 if (deviceService.isAvailable(deviceId)) {
486 log.debug("Device up {}", deviceId);
487 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
488 } else {
489 log.debug("Device down {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800490 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800491 providerService.linksVanished(deviceId);
492 }
493 break;
494 case PORT_STATS_UPDATED:
495 break;
496 default:
497 log.debug("Unknown event {}", event);
498 }
499 }
500 }
501
502 private class InternalConfigListener implements NetworkConfigListener {
503
504 private void addLink(LinkKey linkKey) {
505 configuredLinks.add(linkKey);
506 }
507
508 private void removeLink(LinkKey linkKey) {
509 DefaultLinkDescription linkDescription =
510 new DefaultLinkDescription(linkKey.src(), linkKey.dst(),
511 Link.Type.DIRECT);
512 configuredLinks.remove(linkKey);
513 providerService.linkVanished(linkDescription);
514 }
515
516 @Override
517 public void event(NetworkConfigEvent event) {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800518 if (event.configClass().equals(BasicLinkConfig.class)) {
519 log.info("net config event of type {} for basic link {}",
520 event.type(), event.subject());
521 LinkKey linkKey = (LinkKey) event.subject();
522 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
523 addLink(linkKey);
524 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
525 removeLink(linkKey);
526 }
Charles Chane527eff2016-05-18 14:04:57 -0700527 log.info("Link reconfigured");
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800528 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800529 }
530 }
531
532}