blob: a2e91adf2096398f7d213b5473bc7d23f1827d23 [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
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;
Ayaka Koshibe48229222016-05-16 18:04:26 -070032import org.onosproject.cluster.ClusterMetadataService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080033import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.mastership.MastershipService;
36import org.onosproject.net.ConnectPoint;
37import org.onosproject.net.DefaultAnnotations;
38import org.onosproject.net.Device;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.Link;
41import org.onosproject.net.LinkKey;
42import org.onosproject.net.Port;
43import org.onosproject.net.PortNumber;
44import org.onosproject.net.config.NetworkConfigEvent;
45import org.onosproject.net.config.NetworkConfigListener;
46import org.onosproject.net.config.NetworkConfigRegistry;
47import org.onosproject.net.config.basics.BasicLinkConfig;
48import org.onosproject.net.device.DeviceEvent;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
51import org.onosproject.net.flow.DefaultTrafficSelector;
52import org.onosproject.net.flow.TrafficSelector;
53import org.onosproject.net.link.DefaultLinkDescription;
Ayaka Koshibe48229222016-05-16 18:04:26 -070054import org.onosproject.net.link.ProbedLinkProvider;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080055import org.onosproject.net.link.LinkProviderRegistry;
56import org.onosproject.net.link.LinkProviderService;
57import org.onosproject.net.packet.InboundPacket;
58import org.onosproject.net.packet.PacketContext;
59import org.onosproject.net.packet.PacketPriority;
60import org.onosproject.net.packet.PacketProcessor;
61import org.onosproject.net.packet.PacketService;
62import org.onosproject.net.provider.AbstractProvider;
63import org.onosproject.net.provider.ProviderId;
Ray Milkey957390e2016-02-09 10:02:46 -080064import org.onosproject.provider.lldpcommon.LinkDiscoveryContext;
65import org.onosproject.provider.lldpcommon.LinkDiscovery;
Ray Milkeyb7f0f642016-01-22 16:08:14 -080066import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
68
69import static org.onlab.packet.Ethernet.TYPE_BSN;
70import static org.onlab.packet.Ethernet.TYPE_LLDP;
71import static org.onosproject.net.PortNumber.portNumber;
72
73/**
74 * Provider to pre-discover links and devices based on a specified network
75 * config.
76 */
77
78@Component(immediate = true)
79public class NetworkConfigLinksProvider
80 extends AbstractProvider
Ayaka Koshibe48229222016-05-16 18:04:26 -070081 implements ProbedLinkProvider {
Ray Milkeyb7f0f642016-01-22 16:08:14 -080082
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected LinkProviderRegistry providerRegistry;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected DeviceService deviceService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected PacketService packetService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected MastershipService masterService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected NetworkConfigRegistry netCfgService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected CoreService coreService;
100
Ayaka Koshibe48229222016-05-16 18:04:26 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected ClusterMetadataService metadataService;
103
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800104 private static final String PROP_PROBE_RATE = "probeRate";
105 private static final int DEFAULT_PROBE_RATE = 3000;
106 @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
107 label = "LLDP and BDDP probe rate specified in millis")
108 private int probeRate = DEFAULT_PROBE_RATE;
109
Samuel Jero31e16f52018-09-21 10:34:28 -0400110 private static final String PROP_DISCOVERY_DELAY = "maxLLDPAge";
111 private static final int DEFAULT_DISCOVERY_DELAY = 1000;
112 @Property(name = PROP_DISCOVERY_DELAY, intValue = DEFAULT_DISCOVERY_DELAY,
113 label = "Number of millis beyond which an LLDP packet will not be accepted")
114 private int maxDiscoveryDelayMs = DEFAULT_DISCOVERY_DELAY;
115
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800116 // Device link discovery helpers.
117 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
118
Ray Milkey957390e2016-02-09 10:02:46 -0800119 private final LinkDiscoveryContext context = new InternalDiscoveryContext();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800120
121 private LinkProviderService providerService;
122
123 private static final String PROVIDER_NAME =
124 "org.onosproject.provider.netcfglinks";
125 private final Logger log = LoggerFactory.getLogger(getClass());
126
127 private ApplicationId appId;
128 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
129 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
130 private final InternalConfigListener cfgListener = new InternalConfigListener();
131
Ray Milkeycd6ab182016-02-03 11:13:09 -0800132 protected Set<LinkKey> configuredLinks = new HashSet<>();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800133
134 public NetworkConfigLinksProvider() {
135 super(new ProviderId("lldp", PROVIDER_NAME));
136 }
137
Sho SHIMIZU9efeb812016-08-18 09:29:20 -0700138 private String buildSrcMac() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700139 String srcMac = ProbedLinkProvider.fingerprintMac(metadataService.getClusterMetadata());
140 String defMac = ProbedLinkProvider.defaultMac();
141 if (srcMac.equals(defMac)) {
142 log.warn("Couldn't generate fingerprint. Using default value {}", defMac);
143 return defMac;
144 }
145 log.trace("Generated MAC address {}", srcMac);
146 return srcMac;
147 }
148
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800149 private void createLinks() {
150 netCfgService.getSubjects(LinkKey.class)
151 .forEach(linkKey -> configuredLinks.add(linkKey));
152 }
153
154 @Activate
155 protected void activate() {
156 log.info("Activated");
157 appId = coreService.registerApplication(PROVIDER_NAME);
158 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
159 providerService = providerRegistry.register(this);
160 deviceService.addListener(deviceListener);
161 netCfgService.addListener(cfgListener);
162 requestIntercepts();
163 loadDevices();
164 createLinks();
165 }
166
167 @Deactivate
168 protected void deactivate() {
169 withdrawIntercepts();
170 providerRegistry.unregister(this);
Deepa Vaddireddy21f5ae82017-05-10 18:27:30 +0530171 deviceService.removeListener(deviceListener);
172 netCfgService.removeListener(cfgListener);
173 packetService.removeProcessor(packetProcessor);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800174 disable();
175 log.info("Deactivated");
176 }
177
178 /**
179 * Loads available devices and registers their ports to be probed.
180 */
181 private void loadDevices() {
182 deviceService.getAvailableDevices()
183 .forEach(d -> updateDevice(d)
184 .ifPresent(ld -> updatePorts(ld, d.id())));
185 }
186
187 private Optional<LinkDiscovery> updateDevice(Device device) {
188 if (device == null) {
189 return Optional.empty();
190 }
191
192 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
Ayaka Koshibe48229222016-05-16 18:04:26 -0700193 did -> new LinkDiscovery(device, context));
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800194 if (ld.isStopped()) {
195 ld.start();
196 }
197 return Optional.of(ld);
198 }
199
200 /**
201 * Updates ports of the specified device to the specified discovery helper.
202 */
203 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
204 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
205 }
206
207
208 private void updatePort(LinkDiscovery discoverer, Port port) {
209 if (port == null) {
210 return;
211 }
212 if (port.number().isLogical()) {
213 // silently ignore logical ports
214 return;
215 }
216
217 discoverer.addPort(port);
218 }
219
220 /**
221 * Disables link discovery processing.
222 */
223 private void disable() {
224
225 providerRegistry.unregister(this);
226 discoverers.values().forEach(LinkDiscovery::stop);
227 discoverers.clear();
228
229 providerService = null;
230 }
231
232 /**
233 * Provides processing context for the device link discovery helpers.
234 */
Ray Milkey957390e2016-02-09 10:02:46 -0800235 private class InternalDiscoveryContext implements LinkDiscoveryContext {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800236 @Override
237 public MastershipService mastershipService() {
238 return masterService;
239 }
240
241 @Override
242 public LinkProviderService providerService() {
243 return providerService;
244 }
245
246 @Override
247 public PacketService packetService() {
248 return packetService;
249 }
250
251 @Override
252 public long probeRate() {
253 return probeRate;
254 }
255
256 @Override
257 public boolean useBddp() {
258 return true;
259 }
260
261 @Override
262 public void touchLink(LinkKey key) {
Ray Milkey957390e2016-02-09 10:02:46 -0800263 }
264
265 @Override
DongRyeol Chace65cc02018-07-23 15:02:28 +0900266 public void setTtl(LinkKey key, short ttl) {
267 }
268
269 @Override
Ray Milkey957390e2016-02-09 10:02:46 -0800270 public String fingerprint() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700271 return buildSrcMac();
Ray Milkey957390e2016-02-09 10:02:46 -0800272 }
273
274 @Override
275 public DeviceService deviceService() {
276 return deviceService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800277 }
Samuel Jero31e16f52018-09-21 10:34:28 -0400278
279 @Override
280 public String lldpSecret() {
281 return metadataService.getClusterMetadata().getClusterSecret();
282 }
283
284 @Override
285 public long maxDiscoveryDelay() {
286 return maxDiscoveryDelayMs;
287 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800288 }
289
Samuel Jero31e16f52018-09-21 10:34:28 -0400290 // true if *NOT* this cluster's own probe.
291 private boolean isOthercluster(String mac) {
292 // if we are using DEFAULT_MAC, clustering hadn't initialized, so conservative 'yes'
293 String ourMac = context.fingerprint();
294 if (ProbedLinkProvider.defaultMac().equalsIgnoreCase(ourMac)) {
295 return true;
296 }
297 return !mac.equalsIgnoreCase(ourMac);
298 }
299
300 //doesn't validate. Used just to decide if this is expected link.
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800301 LinkKey extractLinkKey(PacketContext packetContext) {
302 Ethernet eth = packetContext.inPacket().parsed();
303 if (eth == null) {
304 return null;
305 }
306
307 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
308 if (onoslldp != null) {
309 PortNumber srcPort = portNumber(onoslldp.getPort());
310 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
311 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
312 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
313
314 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
315 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
316 return LinkKey.linkKey(src, dst);
317 }
318 return null;
319 }
320
Samuel Jero31e16f52018-09-21 10:34:28 -0400321 private boolean verify(PacketContext packetContext) {
322 Ethernet eth = packetContext.inPacket().parsed();
323 if (eth == null) {
324 return false;
325 }
326
327 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
328 if (onoslldp != null) {
329 if (!isOthercluster(eth.getSourceMAC().toString())) {
330 return false;
331 }
332
333 if (!ONOSLLDP.verify(onoslldp, context.lldpSecret(), context.maxDiscoveryDelay())) {
334 log.warn("LLDP Packet failed to validate!");
335 return false;
336 }
337 return true;
338 }
339 return false;
340 }
341
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800342 /**
Ray Milkeycd6ab182016-02-03 11:13:09 -0800343 * Removes after stopping discovery helper for specified device.
344 * @param deviceId device to remove
345 */
346 private void removeDevice(final DeviceId deviceId) {
347 discoverers.computeIfPresent(deviceId, (did, ld) -> {
348 ld.stop();
349 return null;
350 });
351
352 }
353
354 /**
355 * Removes a port from the specified discovery helper.
356 * @param port the port
357 */
358 private void removePort(Port port) {
359 if (port.element() instanceof Device) {
360 Device d = (Device) port.element();
361 LinkDiscovery ld = discoverers.get(d.id());
362 if (ld != null) {
363 ld.removePort(port.number());
364 }
365 } else {
366 log.warn("Attempted to remove non-Device port", port);
367 }
368 }
369
370
371 /**
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800372 * Processes incoming packets.
373 */
374 private class InternalPacketProcessor implements PacketProcessor {
375 @Override
376 public void process(PacketContext context) {
377 if (context == null || context.isHandled()) {
378 return;
379 }
380
381 Ethernet eth = context.inPacket().parsed();
382 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
383 return;
384 }
385
386 InboundPacket inPacket = context.inPacket();
387 LinkKey linkKey = extractLinkKey(context);
388 if (linkKey != null) {
389 if (configuredLinks.contains(linkKey)) {
390 log.debug("Found configured link {}", linkKey);
391 LinkDiscovery ld = discoverers.get(inPacket.receivedFrom().deviceId());
392 if (ld == null) {
393 return;
394 }
395 if (ld.handleLldp(context)) {
396 context.block();
397 }
398 } else {
Samuel Jero31e16f52018-09-21 10:34:28 -0400399 if (verify(context)) {
400 log.debug("Found link that was not in the configuration {}", linkKey);
401 providerService.linkDetected(
402 new DefaultLinkDescription(linkKey.src(),
403 linkKey.dst(),
404 Link.Type.DIRECT,
405 DefaultLinkDescription.NOT_EXPECTED,
406 DefaultAnnotations.EMPTY));
407 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800408 }
409 }
410 }
411 }
412
413 /**
414 * Requests packet intercepts.
415 */
416 private void requestIntercepts() {
417 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
418 selector.matchEthType(TYPE_LLDP);
419 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
420 appId, Optional.empty());
421
422 selector.matchEthType(TYPE_BSN);
423
424 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
425 appId, Optional.empty());
426
427 }
428
429 /**
430 * Withdraws packet intercepts.
431 */
432 private void withdrawIntercepts() {
433 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
434 selector.matchEthType(TYPE_LLDP);
435 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
436 appId, Optional.empty());
437 selector.matchEthType(TYPE_BSN);
438 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
439 appId, Optional.empty());
440 }
441
442 /**
443 * Processes device events.
444 */
445 private class InternalDeviceListener implements DeviceListener {
446 @Override
447 public void event(DeviceEvent event) {
448 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
449 return;
450 }
451 Device device = event.subject();
452 Port port = event.port();
453 if (device == null) {
454 log.error("Device is null.");
455 return;
456 }
457 log.trace("{} {} {}", event.type(), event.subject(), event);
458 final DeviceId deviceId = device.id();
459 switch (event.type()) {
460 case DEVICE_ADDED:
461 case DEVICE_UPDATED:
462 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
463 break;
464 case PORT_ADDED:
465 case PORT_UPDATED:
466 if (port.isEnabled()) {
467 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
468 } else {
469 log.debug("Port down {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800470 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800471 providerService.linksVanished(new ConnectPoint(port.element().id(),
472 port.number()));
473 }
474 break;
475 case PORT_REMOVED:
476 log.debug("Port removed {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800477 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800478 providerService.linksVanished(new ConnectPoint(port.element().id(),
479 port.number()));
480 break;
481 case DEVICE_REMOVED:
482 case DEVICE_SUSPENDED:
483 log.debug("Device removed {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800484 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800485 providerService.linksVanished(deviceId);
486 break;
487 case DEVICE_AVAILABILITY_CHANGED:
488 if (deviceService.isAvailable(deviceId)) {
489 log.debug("Device up {}", deviceId);
490 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
491 } else {
492 log.debug("Device down {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800493 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800494 providerService.linksVanished(deviceId);
495 }
496 break;
497 case PORT_STATS_UPDATED:
498 break;
499 default:
500 log.debug("Unknown event {}", event);
501 }
502 }
503 }
504
505 private class InternalConfigListener implements NetworkConfigListener {
506
507 private void addLink(LinkKey linkKey) {
508 configuredLinks.add(linkKey);
509 }
510
511 private void removeLink(LinkKey linkKey) {
512 DefaultLinkDescription linkDescription =
513 new DefaultLinkDescription(linkKey.src(), linkKey.dst(),
514 Link.Type.DIRECT);
515 configuredLinks.remove(linkKey);
516 providerService.linkVanished(linkDescription);
517 }
518
519 @Override
520 public void event(NetworkConfigEvent event) {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800521 if (event.configClass().equals(BasicLinkConfig.class)) {
522 log.info("net config event of type {} for basic link {}",
523 event.type(), event.subject());
524 LinkKey linkKey = (LinkKey) event.subject();
525 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
526 addLink(linkKey);
527 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
528 removeLink(linkKey);
529 }
Charles Chane527eff2016-05-18 14:04:57 -0700530 log.info("Link reconfigured");
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800531 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800532 }
533 }
534
535}