blob: 0d1d9a8e38eebfedc8faf0e03743d264e51bbecf [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;
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);
165 disable();
166 log.info("Deactivated");
167 }
168
169 /**
170 * Loads available devices and registers their ports to be probed.
171 */
172 private void loadDevices() {
173 deviceService.getAvailableDevices()
174 .forEach(d -> updateDevice(d)
175 .ifPresent(ld -> updatePorts(ld, d.id())));
176 }
177
178 private Optional<LinkDiscovery> updateDevice(Device device) {
179 if (device == null) {
180 return Optional.empty();
181 }
182
183 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
Ayaka Koshibe48229222016-05-16 18:04:26 -0700184 did -> new LinkDiscovery(device, context));
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800185 if (ld.isStopped()) {
186 ld.start();
187 }
188 return Optional.of(ld);
189 }
190
191 /**
192 * Updates ports of the specified device to the specified discovery helper.
193 */
194 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
195 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
196 }
197
198
199 private void updatePort(LinkDiscovery discoverer, Port port) {
200 if (port == null) {
201 return;
202 }
203 if (port.number().isLogical()) {
204 // silently ignore logical ports
205 return;
206 }
207
208 discoverer.addPort(port);
209 }
210
211 /**
212 * Disables link discovery processing.
213 */
214 private void disable() {
215
216 providerRegistry.unregister(this);
217 discoverers.values().forEach(LinkDiscovery::stop);
218 discoverers.clear();
219
220 providerService = null;
221 }
222
223 /**
224 * Provides processing context for the device link discovery helpers.
225 */
Ray Milkey957390e2016-02-09 10:02:46 -0800226 private class InternalDiscoveryContext implements LinkDiscoveryContext {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800227 @Override
228 public MastershipService mastershipService() {
229 return masterService;
230 }
231
232 @Override
233 public LinkProviderService providerService() {
234 return providerService;
235 }
236
237 @Override
238 public PacketService packetService() {
239 return packetService;
240 }
241
242 @Override
243 public long probeRate() {
244 return probeRate;
245 }
246
247 @Override
248 public boolean useBddp() {
249 return true;
250 }
251
252 @Override
253 public void touchLink(LinkKey key) {
Ray Milkey957390e2016-02-09 10:02:46 -0800254 }
255
256 @Override
257 public String fingerprint() {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700258 return buildSrcMac();
Ray Milkey957390e2016-02-09 10:02:46 -0800259 }
260
261 @Override
262 public DeviceService deviceService() {
263 return deviceService;
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800264 }
265 }
266
267 LinkKey extractLinkKey(PacketContext packetContext) {
268 Ethernet eth = packetContext.inPacket().parsed();
269 if (eth == null) {
270 return null;
271 }
272
273 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
274 if (onoslldp != null) {
275 PortNumber srcPort = portNumber(onoslldp.getPort());
276 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
277 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
278 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
279
280 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
281 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
282 return LinkKey.linkKey(src, dst);
283 }
284 return null;
285 }
286
287 /**
Ray Milkeycd6ab182016-02-03 11:13:09 -0800288 * Removes after stopping discovery helper for specified device.
289 * @param deviceId device to remove
290 */
291 private void removeDevice(final DeviceId deviceId) {
292 discoverers.computeIfPresent(deviceId, (did, ld) -> {
293 ld.stop();
294 return null;
295 });
296
297 }
298
299 /**
300 * Removes a port from the specified discovery helper.
301 * @param port the port
302 */
303 private void removePort(Port port) {
304 if (port.element() instanceof Device) {
305 Device d = (Device) port.element();
306 LinkDiscovery ld = discoverers.get(d.id());
307 if (ld != null) {
308 ld.removePort(port.number());
309 }
310 } else {
311 log.warn("Attempted to remove non-Device port", port);
312 }
313 }
314
315
316 /**
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800317 * Processes incoming packets.
318 */
319 private class InternalPacketProcessor implements PacketProcessor {
320 @Override
321 public void process(PacketContext context) {
322 if (context == null || context.isHandled()) {
323 return;
324 }
325
326 Ethernet eth = context.inPacket().parsed();
327 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
328 return;
329 }
330
331 InboundPacket inPacket = context.inPacket();
332 LinkKey linkKey = extractLinkKey(context);
333 if (linkKey != null) {
334 if (configuredLinks.contains(linkKey)) {
335 log.debug("Found configured link {}", linkKey);
336 LinkDiscovery ld = discoverers.get(inPacket.receivedFrom().deviceId());
337 if (ld == null) {
338 return;
339 }
340 if (ld.handleLldp(context)) {
341 context.block();
342 }
343 } else {
344 log.debug("Found link that was not in the configuration {}", linkKey);
345 providerService.linkDetected(
346 new DefaultLinkDescription(linkKey.src(),
347 linkKey.dst(),
348 Link.Type.DIRECT,
349 DefaultLinkDescription.NOT_EXPECTED,
350 DefaultAnnotations.EMPTY));
351 }
352 }
353 }
354 }
355
356 /**
357 * Requests packet intercepts.
358 */
359 private void requestIntercepts() {
360 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
361 selector.matchEthType(TYPE_LLDP);
362 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
363 appId, Optional.empty());
364
365 selector.matchEthType(TYPE_BSN);
366
367 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
368 appId, Optional.empty());
369
370 }
371
372 /**
373 * Withdraws packet intercepts.
374 */
375 private void withdrawIntercepts() {
376 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
377 selector.matchEthType(TYPE_LLDP);
378 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
379 appId, Optional.empty());
380 selector.matchEthType(TYPE_BSN);
381 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
382 appId, Optional.empty());
383 }
384
385 /**
386 * Processes device events.
387 */
388 private class InternalDeviceListener implements DeviceListener {
389 @Override
390 public void event(DeviceEvent event) {
391 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
392 return;
393 }
394 Device device = event.subject();
395 Port port = event.port();
396 if (device == null) {
397 log.error("Device is null.");
398 return;
399 }
400 log.trace("{} {} {}", event.type(), event.subject(), event);
401 final DeviceId deviceId = device.id();
402 switch (event.type()) {
403 case DEVICE_ADDED:
404 case DEVICE_UPDATED:
405 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
406 break;
407 case PORT_ADDED:
408 case PORT_UPDATED:
409 if (port.isEnabled()) {
410 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
411 } else {
412 log.debug("Port down {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800413 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800414 providerService.linksVanished(new ConnectPoint(port.element().id(),
415 port.number()));
416 }
417 break;
418 case PORT_REMOVED:
419 log.debug("Port removed {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800420 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800421 providerService.linksVanished(new ConnectPoint(port.element().id(),
422 port.number()));
423 break;
424 case DEVICE_REMOVED:
425 case DEVICE_SUSPENDED:
426 log.debug("Device removed {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800427 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800428 providerService.linksVanished(deviceId);
429 break;
430 case DEVICE_AVAILABILITY_CHANGED:
431 if (deviceService.isAvailable(deviceId)) {
432 log.debug("Device up {}", deviceId);
433 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
434 } else {
435 log.debug("Device down {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800436 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800437 providerService.linksVanished(deviceId);
438 }
439 break;
440 case PORT_STATS_UPDATED:
441 break;
442 default:
443 log.debug("Unknown event {}", event);
444 }
445 }
446 }
447
448 private class InternalConfigListener implements NetworkConfigListener {
449
450 private void addLink(LinkKey linkKey) {
451 configuredLinks.add(linkKey);
452 }
453
454 private void removeLink(LinkKey linkKey) {
455 DefaultLinkDescription linkDescription =
456 new DefaultLinkDescription(linkKey.src(), linkKey.dst(),
457 Link.Type.DIRECT);
458 configuredLinks.remove(linkKey);
459 providerService.linkVanished(linkDescription);
460 }
461
462 @Override
463 public void event(NetworkConfigEvent event) {
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800464 if (event.configClass().equals(BasicLinkConfig.class)) {
465 log.info("net config event of type {} for basic link {}",
466 event.type(), event.subject());
467 LinkKey linkKey = (LinkKey) event.subject();
468 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
469 addLink(linkKey);
470 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
471 removeLink(linkKey);
472 }
Charles Chane527eff2016-05-18 14:04:57 -0700473 log.info("Link reconfigured");
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800474 }
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800475 }
476 }
477
478}