blob: a15857f510f109a14ed5125cecb63aeaa32aa747 [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
110 // Device link discovery helpers.
111 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
112
Ray Milkey957390e2016-02-09 10:02:46 -0800113 private final LinkDiscoveryContext context = new InternalDiscoveryContext();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800114
115 private LinkProviderService providerService;
116
117 private static final String PROVIDER_NAME =
118 "org.onosproject.provider.netcfglinks";
119 private final Logger log = LoggerFactory.getLogger(getClass());
120
121 private ApplicationId appId;
122 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
123 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
124 private final InternalConfigListener cfgListener = new InternalConfigListener();
125
Ray Milkeycd6ab182016-02-03 11:13:09 -0800126 protected Set<LinkKey> configuredLinks = new HashSet<>();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800127
128 public NetworkConfigLinksProvider() {
129 super(new ProviderId("lldp", PROVIDER_NAME));
130 }
131
Sho SHIMIZU9efeb812016-08-18 09:29:20 -0700132 private String buildSrcMac() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700133 String srcMac = ProbedLinkProvider.fingerprintMac(metadataService.getClusterMetadata());
134 String defMac = ProbedLinkProvider.defaultMac();
135 if (srcMac.equals(defMac)) {
136 log.warn("Couldn't generate fingerprint. Using default value {}", defMac);
137 return defMac;
138 }
139 log.trace("Generated MAC address {}", srcMac);
140 return srcMac;
141 }
142
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800143 private void createLinks() {
144 netCfgService.getSubjects(LinkKey.class)
145 .forEach(linkKey -> configuredLinks.add(linkKey));
146 }
147
148 @Activate
149 protected void activate() {
150 log.info("Activated");
151 appId = coreService.registerApplication(PROVIDER_NAME);
152 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
153 providerService = providerRegistry.register(this);
154 deviceService.addListener(deviceListener);
155 netCfgService.addListener(cfgListener);
156 requestIntercepts();
157 loadDevices();
158 createLinks();
159 }
160
161 @Deactivate
162 protected void deactivate() {
163 withdrawIntercepts();
164 providerRegistry.unregister(this);
Deepa Vaddireddy21f5ae82017-05-10 18:27:30 +0530165 deviceService.removeListener(deviceListener);
166 netCfgService.removeListener(cfgListener);
167 packetService.removeProcessor(packetProcessor);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800168 disable();
169 log.info("Deactivated");
170 }
171
172 /**
173 * Loads available devices and registers their ports to be probed.
174 */
175 private void loadDevices() {
176 deviceService.getAvailableDevices()
177 .forEach(d -> updateDevice(d)
178 .ifPresent(ld -> updatePorts(ld, d.id())));
179 }
180
181 private Optional<LinkDiscovery> updateDevice(Device device) {
182 if (device == null) {
183 return Optional.empty();
184 }
185
186 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
Ayaka Koshibe48229222016-05-16 18:04:26 -0700187 did -> new LinkDiscovery(device, context));
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800188 if (ld.isStopped()) {
189 ld.start();
190 }
191 return Optional.of(ld);
192 }
193
194 /**
195 * Updates ports of the specified device to the specified discovery helper.
196 */
197 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
198 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
199 }
200
201
202 private void updatePort(LinkDiscovery discoverer, Port port) {
203 if (port == null) {
204 return;
205 }
206 if (port.number().isLogical()) {
207 // silently ignore logical ports
208 return;
209 }
210
211 discoverer.addPort(port);
212 }
213
214 /**
215 * Disables link discovery processing.
216 */
217 private void disable() {
218
219 providerRegistry.unregister(this);
220 discoverers.values().forEach(LinkDiscovery::stop);
221 discoverers.clear();
222
223 providerService = null;
224 }
225
226 /**
227 * Provides processing context for the device link discovery helpers.
228 */
Ray Milkey957390e2016-02-09 10:02:46 -0800229 private class InternalDiscoveryContext implements LinkDiscoveryContext {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800230 @Override
231 public MastershipService mastershipService() {
232 return masterService;
233 }
234
235 @Override
236 public LinkProviderService providerService() {
237 return providerService;
238 }
239
240 @Override
241 public PacketService packetService() {
242 return packetService;
243 }
244
245 @Override
246 public long probeRate() {
247 return probeRate;
248 }
249
250 @Override
251 public boolean useBddp() {
252 return true;
253 }
254
255 @Override
256 public void touchLink(LinkKey key) {
Ray Milkey957390e2016-02-09 10:02:46 -0800257 }
258
259 @Override
260 public String fingerprint() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700261 return buildSrcMac();
Ray Milkey957390e2016-02-09 10:02:46 -0800262 }
263
264 @Override
265 public DeviceService deviceService() {
266 return deviceService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800267 }
268 }
269
270 LinkKey extractLinkKey(PacketContext packetContext) {
271 Ethernet eth = packetContext.inPacket().parsed();
272 if (eth == null) {
273 return null;
274 }
275
276 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
277 if (onoslldp != null) {
278 PortNumber srcPort = portNumber(onoslldp.getPort());
279 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
280 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
281 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
282
283 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
284 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
285 return LinkKey.linkKey(src, dst);
286 }
287 return null;
288 }
289
290 /**
Ray Milkeycd6ab182016-02-03 11:13:09 -0800291 * Removes after stopping discovery helper for specified device.
292 * @param deviceId device to remove
293 */
294 private void removeDevice(final DeviceId deviceId) {
295 discoverers.computeIfPresent(deviceId, (did, ld) -> {
296 ld.stop();
297 return null;
298 });
299
300 }
301
302 /**
303 * Removes a port from the specified discovery helper.
304 * @param port the port
305 */
306 private void removePort(Port port) {
307 if (port.element() instanceof Device) {
308 Device d = (Device) port.element();
309 LinkDiscovery ld = discoverers.get(d.id());
310 if (ld != null) {
311 ld.removePort(port.number());
312 }
313 } else {
314 log.warn("Attempted to remove non-Device port", port);
315 }
316 }
317
318
319 /**
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800320 * Processes incoming packets.
321 */
322 private class InternalPacketProcessor implements PacketProcessor {
323 @Override
324 public void process(PacketContext context) {
325 if (context == null || context.isHandled()) {
326 return;
327 }
328
329 Ethernet eth = context.inPacket().parsed();
330 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
331 return;
332 }
333
334 InboundPacket inPacket = context.inPacket();
335 LinkKey linkKey = extractLinkKey(context);
336 if (linkKey != null) {
337 if (configuredLinks.contains(linkKey)) {
338 log.debug("Found configured link {}", linkKey);
339 LinkDiscovery ld = discoverers.get(inPacket.receivedFrom().deviceId());
340 if (ld == null) {
341 return;
342 }
343 if (ld.handleLldp(context)) {
344 context.block();
345 }
346 } else {
347 log.debug("Found link that was not in the configuration {}", linkKey);
348 providerService.linkDetected(
349 new DefaultLinkDescription(linkKey.src(),
350 linkKey.dst(),
351 Link.Type.DIRECT,
352 DefaultLinkDescription.NOT_EXPECTED,
353 DefaultAnnotations.EMPTY));
354 }
355 }
356 }
357 }
358
359 /**
360 * Requests packet intercepts.
361 */
362 private void requestIntercepts() {
363 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
364 selector.matchEthType(TYPE_LLDP);
365 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
366 appId, Optional.empty());
367
368 selector.matchEthType(TYPE_BSN);
369
370 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
371 appId, Optional.empty());
372
373 }
374
375 /**
376 * Withdraws packet intercepts.
377 */
378 private void withdrawIntercepts() {
379 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
380 selector.matchEthType(TYPE_LLDP);
381 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
382 appId, Optional.empty());
383 selector.matchEthType(TYPE_BSN);
384 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
385 appId, Optional.empty());
386 }
387
388 /**
389 * Processes device events.
390 */
391 private class InternalDeviceListener implements DeviceListener {
392 @Override
393 public void event(DeviceEvent event) {
394 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
395 return;
396 }
397 Device device = event.subject();
398 Port port = event.port();
399 if (device == null) {
400 log.error("Device is null.");
401 return;
402 }
403 log.trace("{} {} {}", event.type(), event.subject(), event);
404 final DeviceId deviceId = device.id();
405 switch (event.type()) {
406 case DEVICE_ADDED:
407 case DEVICE_UPDATED:
408 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
409 break;
410 case PORT_ADDED:
411 case PORT_UPDATED:
412 if (port.isEnabled()) {
413 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
414 } else {
415 log.debug("Port down {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800416 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800417 providerService.linksVanished(new ConnectPoint(port.element().id(),
418 port.number()));
419 }
420 break;
421 case PORT_REMOVED:
422 log.debug("Port removed {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800423 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800424 providerService.linksVanished(new ConnectPoint(port.element().id(),
425 port.number()));
426 break;
427 case DEVICE_REMOVED:
428 case DEVICE_SUSPENDED:
429 log.debug("Device removed {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800430 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800431 providerService.linksVanished(deviceId);
432 break;
433 case DEVICE_AVAILABILITY_CHANGED:
434 if (deviceService.isAvailable(deviceId)) {
435 log.debug("Device up {}", deviceId);
436 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
437 } else {
438 log.debug("Device down {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800439 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800440 providerService.linksVanished(deviceId);
441 }
442 break;
443 case PORT_STATS_UPDATED:
444 break;
445 default:
446 log.debug("Unknown event {}", event);
447 }
448 }
449 }
450
451 private class InternalConfigListener implements NetworkConfigListener {
452
453 private void addLink(LinkKey linkKey) {
454 configuredLinks.add(linkKey);
455 }
456
457 private void removeLink(LinkKey linkKey) {
458 DefaultLinkDescription linkDescription =
459 new DefaultLinkDescription(linkKey.src(), linkKey.dst(),
460 Link.Type.DIRECT);
461 configuredLinks.remove(linkKey);
462 providerService.linkVanished(linkDescription);
463 }
464
465 @Override
466 public void event(NetworkConfigEvent event) {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800467 if (event.configClass().equals(BasicLinkConfig.class)) {
468 log.info("net config event of type {} for basic link {}",
469 event.type(), event.subject());
470 LinkKey linkKey = (LinkKey) event.subject();
471 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
472 addLink(linkKey);
473 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
474 removeLink(linkKey);
475 }
Charles Chane527eff2016-05-18 14:04:57 -0700476 log.info("Link reconfigured");
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800477 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800478 }
479 }
480
481}