]> git.proxmox.com Git - ovs.git/blob - lib/lldp/lldpd.c
sparse: Add guards to prevent FreeBSD-incompatible #include order.
[ovs.git] / lib / lldp / lldpd.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2015 Nicira, Inc.
4 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <config.h>
20 #include "lldpd.h"
21 #include <sys/types.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <sys/wait.h>
34 #include <unistd.h>
35 #ifndef _WIN32
36 #include <grp.h>
37 #include <libgen.h>
38 #include <pwd.h>
39 #include <sys/select.h>
40 #include <sys/utsname.h>
41 #endif
42 #include "compiler.h"
43 #include "openvswitch/list.h"
44 #include "packets.h"
45 #include "timeval.h"
46
47 VLOG_DEFINE_THIS_MODULE(lldpd);
48
49 static struct protocol protos[] =
50 {
51 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
52 LLDP_MULTICAST_ADDR },
53 { 0, 0, "any", ' ', NULL, NULL, NULL,
54 { { { 0,0,0,0,0,0 } } } }
55 };
56
57 void lldpd_assign_cfg_to_protocols(struct lldpd *cfg)
58 {
59 cfg->g_protocols = protos;
60 }
61
62 struct lldpd_hardware *
63 lldpd_get_hardware(struct lldpd *cfg, char *name, int index,
64 struct lldpd_ops *ops)
65 {
66 struct lldpd_hardware *hw;
67
68 LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) {
69 if (!strcmp(hw->h_ifname, name) && hw->h_ifindex == index
70 && (!ops || ops == hw->h_ops)) {
71 return hw;
72 }
73 }
74
75 return NULL;
76 }
77
78 struct lldpd_hardware *
79 lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
80 {
81 struct lldpd_hardware *hw;
82
83 VLOG_DBG("allocate a new local hardware interface (%s)", name);
84
85 hw = xzalloc(sizeof *hw);
86 hw->h_cfg = cfg;
87 ovs_strlcpy(hw->h_ifname, name, sizeof hw->h_ifname);
88 hw->h_ifindex = index;
89 hw->h_lport.p_chassis = CONTAINER_OF(ovs_list_front(&cfg->g_chassis),
90 struct lldpd_chassis, list);
91 hw->h_lport.p_chassis->c_refcount++;
92 ovs_list_init(&hw->h_rports);
93
94 return hw;
95 }
96
97 struct lldpd_mgmt *
98 lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
99 {
100 struct lldpd_mgmt *mgmt;
101
102 VLOG_DBG("allocate a new management address (family: %d)", family);
103
104 if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
105 errno = EAFNOSUPPORT;
106 return NULL;
107 }
108 if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
109 errno = EOVERFLOW;
110 return NULL;
111 }
112 mgmt = xzalloc(sizeof *mgmt);
113 mgmt->m_family = family;
114 memcpy(&mgmt->m_addr, addrptr, addrsize);
115 mgmt->m_addrsize = addrsize;
116 mgmt->m_iface = iface;
117
118 return mgmt;
119 }
120
121 void
122 lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
123 {
124 VLOG_DBG("cleanup hardware port %s", hardware->h_ifname);
125
126 lldpd_port_cleanup(&hardware->h_lport, true);
127 if (hardware->h_ops && hardware->h_ops->cleanup) {
128 hardware->h_ops->cleanup(cfg, hardware);
129 }
130 free(hardware);
131 }
132
133 void
134 lldpd_cleanup(struct lldpd *cfg)
135 {
136 struct lldpd_hardware *hw, *hw_next;
137 struct lldpd_chassis *chassis, *chassis_next;
138
139 VLOG_DBG("cleanup all ports");
140
141 LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware) {
142 if (!hw->h_flags) {
143 ovs_list_remove(&hw->h_entries);
144 lldpd_remote_cleanup(hw, NULL, true);
145 lldpd_hardware_cleanup(cfg, hw);
146 } else {
147 lldpd_remote_cleanup(hw, NULL, false);
148 }
149 }
150
151 VLOG_DBG("cleanup all chassis");
152
153 LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis) {
154 if (chassis->c_refcount == 0) {
155 ovs_list_remove(&chassis->list);
156 lldpd_chassis_cleanup(chassis, 1);
157 }
158 }
159 }
160
161 /* Update chassis `ochassis' with values from `chassis'. The later one is not
162 * expected to be part of a list! It will also be wiped from memory.
163 */
164 static void
165 lldpd_move_chassis(struct lldpd_chassis *ochassis,
166 struct lldpd_chassis *chassis)
167 {
168 struct lldpd_mgmt *mgmt;
169 int refcount = ochassis->c_refcount;
170 int index = ochassis->c_index;
171 struct ovs_list listcopy;
172
173 /* We want to keep refcount, index and list stuff from the current chassis
174 */
175 memcpy(&listcopy, &ochassis->list, sizeof listcopy);
176 lldpd_chassis_cleanup(ochassis, 0);
177
178 /* Make the copy. */
179 /* WARNING: this is a kludgy hack, we need in-place copy and cannot use
180 * marshaling.
181 */
182 memcpy(ochassis, chassis, sizeof *ochassis);
183 ovs_list_init(&ochassis->c_mgmt);
184
185 /* Copy of management addresses */
186 LIST_FOR_EACH_POP (mgmt, m_entries, &chassis->c_mgmt) {
187 ovs_list_insert(&ochassis->c_mgmt, &mgmt->m_entries);
188 }
189
190 /* Restore saved values */
191 ochassis->c_refcount = refcount;
192 ochassis->c_index = index;
193 memcpy(&ochassis->list, &listcopy, sizeof ochassis->list);
194
195 /* Get rid of the new chassis */
196 free(chassis);
197 }
198
199 static int
200 lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
201 {
202 int i;
203
204 if (s < ETH_ADDR_LEN) {
205 return -1;
206 }
207
208 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
209 if (!cfg->g_protocols[i].enabled) {
210 continue;
211 }
212 if (cfg->g_protocols[i].guess == NULL) {
213 if (memcmp(frame, &cfg->g_protocols[i].mac, ETH_ADDR_LEN) == 0) {
214 VLOG_DBG("guessed protocol is %s (from MAC address)",
215 cfg->g_protocols[i].name);
216 return cfg->g_protocols[i].mode;
217 }
218 } else {
219 if (cfg->g_protocols[i].guess(frame, s)) {
220 VLOG_DBG("guessed protocol is %s (from detector function)",
221 cfg->g_protocols[i].name);
222 return cfg->g_protocols[i].mode;
223 }
224 }
225 }
226
227 return -1;
228 }
229
230 static void
231 lldpd_decode(struct lldpd *cfg, char *frame, int s,
232 struct lldpd_hardware *hw)
233 {
234 size_t listsize, i;
235 struct lldpd_chassis *chassis, *ochassis = NULL;
236 struct lldpd_port *port, *oport;
237 int guess = LLDPD_MODE_LLDP;
238 struct eth_header eheader;
239 int count = 0;
240 bool found = false;
241
242 VLOG_DBG("decode a received frame on %s size %d", hw->h_ifname,s);
243
244 if (s < sizeof(struct eth_header) + 4) {
245 /* Too short, just discard it */
246 return;
247 }
248
249 /* Decapsulate VLAN frames */
250 memcpy(&eheader, frame, sizeof eheader);
251 if (eheader.eth_type == htons(ETH_TYPE_VLAN)) {
252 /* VLAN decapsulation means to shift 4 bytes left the frame from
253 * offset 2 * ETH_ADDR_LEN
254 */
255 memmove(frame + 2 * ETH_ADDR_LEN, frame + 2 * ETH_ADDR_LEN + 4,
256 s - 2 * ETH_ADDR_LEN);
257 s -= 4;
258 }
259
260 LIST_FOR_EACH (oport, p_entries, &hw->h_rports) {
261 if (oport->p_lastframe &&
262 oport->p_lastframe->size == s &&
263 !memcmp(oport->p_lastframe->frame, frame, s)) {
264 /* Already received the same frame */
265 VLOG_DBG("duplicate frame, no need to decode");
266 oport->p_lastupdate = time_now();
267 return;
268 }
269 }
270
271 guess = lldpd_guess_type(cfg, frame, s);
272 VLOG_DBG("guessed %d enabled:%d", guess, cfg->g_protocols[0].enabled);
273
274 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
275 if (!cfg->g_protocols[i].enabled) {
276 continue;
277 }
278 if (cfg->g_protocols[i].mode == guess) {
279 VLOG_DBG("using decode function for %s protocol",
280 cfg->g_protocols[i].name);
281 if (cfg->g_protocols[i].decode(cfg, frame, s, hw, &chassis, &port)
282 == -1) {
283 VLOG_DBG("function for %s protocol did not "
284 "decode this frame",
285 cfg->g_protocols[i].name);
286 return;
287 }
288 chassis->c_protocol = port->p_protocol = cfg->g_protocols[i].mode;
289 break;
290 }
291 VLOG_DBG(" %"PRIuSIZE "mode:%d enabled:%d",
292 i, cfg->g_protocols[i].mode, cfg->g_protocols[i].enabled);
293 }
294 if (cfg->g_protocols[i].mode == 0) {
295 VLOG_DBG("unable to guess frame type on %s", hw->h_ifname);
296 return;
297 }
298
299 /* Do we already have the same MSAP somewhere? */
300 VLOG_DBG("search for the same MSAP");
301
302 LIST_FOR_EACH (oport, p_entries, &hw->h_rports) {
303 if (port->p_protocol == oport->p_protocol) {
304 count++;
305 if (port->p_id_subtype == oport->p_id_subtype &&
306 port->p_id_len == oport->p_id_len &&
307 !memcmp(port->p_id, oport->p_id, port->p_id_len) &&
308 chassis->c_id_subtype == oport->p_chassis->c_id_subtype &&
309 chassis->c_id_len == oport->p_chassis->c_id_len &&
310 !memcmp(chassis->c_id, oport->p_chassis->c_id,
311 chassis->c_id_len)) {
312 ochassis = oport->p_chassis;
313 VLOG_DBG("MSAP is already known");
314 found = true;
315 break;
316 }
317 }
318 }
319
320 if (!found) {
321 oport = NULL;
322 }
323
324 /* Do we have room for a new MSAP? */
325 if (!oport && cfg->g_config.c_max_neighbors) {
326 if (count == (cfg->g_config.c_max_neighbors - 1)) {
327 VLOG_DBG("max neighbors %d reached for port %s, "
328 "dropping any new ones silently",
329 cfg->g_config.c_max_neighbors,
330 hw->h_ifname);
331 } else if (count > cfg->g_config.c_max_neighbors - 1) {
332 VLOG_DBG("too many neighbors for port %s, drop this new one",
333 hw->h_ifname);
334 lldpd_port_cleanup(port, true);
335 lldpd_chassis_cleanup(chassis, true);
336 free(port);
337 return;
338 }
339 }
340
341 /* No, but do we already know the system? */
342 if (!oport) {
343 found = false;
344 VLOG_DBG("MSAP is unknown, search for the chassis");
345
346 LIST_FOR_EACH (ochassis, list, &cfg->g_chassis) {
347 if ((chassis->c_protocol == ochassis->c_protocol) &&
348 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
349 (chassis->c_id_len == ochassis->c_id_len) &&
350 (memcmp(chassis->c_id, ochassis->c_id,
351 chassis->c_id_len) == 0)) {
352 found = true;
353 break;
354 }
355 }
356
357 if (!found) {
358 ochassis = NULL;
359 }
360 }
361
362 if (oport) {
363 /* The port is known, remove it before adding it back */
364 ovs_list_remove(&oport->p_entries);
365 lldpd_port_cleanup(oport, 1);
366 free(oport);
367 }
368
369 if (ochassis) {
370 lldpd_move_chassis(ochassis, chassis);
371 chassis = ochassis;
372 } else {
373 /* Chassis not known, add it */
374 VLOG_DBG("unknown chassis, add it to the list");
375 chassis->c_index = ++cfg->g_lastrid;
376 chassis->c_refcount = 0;
377 ovs_list_push_back(&cfg->g_chassis, &chassis->list);
378 listsize = ovs_list_size(&cfg->g_chassis);
379 VLOG_DBG("%"PRIuSIZE " different systems are known", listsize);
380 }
381
382 /* Add port */
383 port->p_lastchange = port->p_lastupdate = time_now();
384 port->p_lastframe = xmalloc(s + sizeof(struct lldpd_frame));
385 port->p_lastframe->size = s;
386 memcpy(port->p_lastframe->frame, frame, s);
387 ovs_list_insert(&hw->h_rports, &port->p_entries);
388
389 port->p_chassis = chassis;
390 port->p_chassis->c_refcount++;
391 /* Several cases are possible :
392 * 1. chassis is new, its refcount was 0. It is now attached
393 * to this port, its refcount is 1.
394 * 2. chassis already exists and was attached to another
395 * port, we increase its refcount accordingly.
396 * 3. chassis already exists and was attached to the same
397 * port, its refcount was decreased with
398 * lldpd_port_cleanup() and is now increased again.
399 *
400 * In all cases, if the port already existed, it has been
401 * freed with lldpd_port_cleanup() and therefore, the refcount
402 * of the chassis that was attached to it is decreased.
403 */
404 i = ovs_list_size(&hw->h_rports);
405 VLOG_DBG("%"PRIuSIZE " neighbors for %s", i, hw->h_ifname);
406
407 if (!oport) {
408 hw->h_insert_cnt++;
409 }
410
411 return;
412 }
413
414 static void
415 lldpd_hide_ports(struct lldpd *cfg,
416 struct lldpd_hardware *hw,
417 int mask) {
418 struct lldpd_port *port;
419 int protocols[LLDPD_MODE_MAX + 1];
420 char buffer[256];
421 bool found = false;
422 int i, j, k;
423 unsigned int min;
424
425 VLOG_DBG("apply smart filter for port %s", hw->h_ifname);
426
427 /* Compute the number of occurrences of each protocol */
428 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
429 protocols[i] = 0;
430 }
431
432 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
433 protocols[port->p_protocol]++;
434 }
435
436 /* Turn the protocols[] array into an array of
437 * enabled/disabled protocols. 1 means enabled, 0
438 * means disabled.
439 */
440 min = (unsigned int) - 1;
441 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
442 if (protocols[i] && (protocols[i] < min)) {
443 min = protocols[i];
444 }
445 }
446 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
447 if (protocols[i] == min && !found) {
448 /* If we need a tie breaker, we take the first protocol only */
449 if (cfg->g_config.c_smart & mask &
450 (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO)) {
451 found = true;
452 }
453 protocols[i] = 1;
454 } else {
455 protocols[i] = 0;
456 }
457 }
458
459 /* We set the p_hidden flag to 1 if the protocol is disabled */
460 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
461 if (mask == SMART_OUTGOING) {
462 port->p_hidden_out = protocols[port->p_protocol] ? false : true;
463 } else {
464 port->p_hidden_in = protocols[port->p_protocol] ? false : true;
465 }
466 }
467
468 /* If we want only one neighbor, we take the first one */
469 if (cfg->g_config.c_smart & mask &
470 (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
471 found = false;
472
473 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
474 if (mask == SMART_OUTGOING) {
475 if (found) {
476 port->p_hidden_out = true;
477 }
478 if (!port->p_hidden_out) {
479 found = true;
480 }
481 }
482 if (mask == SMART_INCOMING) {
483 if (found) {
484 port->p_hidden_in = true;
485 }
486 if (!port->p_hidden_in) {
487 found = true;
488 }
489 }
490 }
491 }
492
493 /* Print a debug message summarizing the operation */
494 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
495 protocols[i] = 0;
496 }
497
498 k = j = 0;
499 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
500 if (!((mask == SMART_OUTGOING && port->p_hidden_out) ||
501 (mask == SMART_INCOMING && port->p_hidden_in))) {
502 k++;
503 protocols[port->p_protocol] = 1;
504 }
505 j++;
506 }
507
508 buffer[0] = '\0';
509 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
510 if (cfg->g_protocols[i].enabled &&
511 protocols[cfg->g_protocols[i].mode]) {
512 if (strlen(buffer) +
513 strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
514 /* Unlikely, our buffer is too small */
515 memcpy(buffer + sizeof(buffer) - 4, "...", 4);
516 break;
517 }
518 if (buffer[0]) {
519 strncat(buffer, ", ", 2);
520 strncat(buffer, cfg->g_protocols[i].name,
521 strlen(cfg->g_protocols[i].name));
522 }
523 }
524 }
525 VLOG_DBG("%s: %s: %d visible neighbors (out of %d)",
526 hw->h_ifname,
527 (mask == SMART_OUTGOING) ? "out filter" : "in filter",
528 k, j);
529 VLOG_DBG("%s: protocols: %s",
530 hw->h_ifname, buffer[0] ? buffer : "(none)");
531 }
532
533 /* Hide unwanted ports depending on smart mode set by the user */
534 static void
535 lldpd_hide_all(struct lldpd *cfg)
536 {
537 struct lldpd_hardware *hw;
538
539 if (!cfg->g_config.c_smart) {
540 return;
541 }
542
543 VLOG_DBG("apply smart filter results on all ports");
544
545 LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) {
546 if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) {
547 lldpd_hide_ports(cfg, hw, SMART_INCOMING);
548 }
549 if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) {
550 lldpd_hide_ports(cfg, hw, SMART_OUTGOING);
551 }
552 }
553 }
554
555 void
556 lldpd_recv(struct lldpd *cfg,
557 struct lldpd_hardware *hw,
558 char *buffer,
559 size_t bufSize)
560 {
561 int n = bufSize;
562
563 VLOG_DBG("receive a frame on %s", hw->h_ifname);
564 if (cfg->g_config.c_paused) {
565 VLOG_DBG("paused, ignore the frame on %s", hw->h_ifname);
566 return;
567 }
568 hw->h_rx_cnt++;
569 VLOG_DBG("decode received frame on %s h_rx_cnt=%" PRIu64,
570 hw->h_ifname, hw->h_rx_cnt);
571 lldpd_decode(cfg, buffer, n, hw);
572 lldpd_hide_all(cfg); /* Immediatly hide */
573 }
574
575 uint32_t
576 lldpd_send(struct lldpd_hardware *hw, struct dp_packet *p)
577 {
578 struct lldpd *cfg = hw->h_cfg;
579 struct lldpd_port *port;
580 int i, sent = 0;
581 int lldp_size = 0;
582
583 if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) {
584 return 0;
585 }
586 #ifndef _WIN32
587 if ((hw->h_flags & IFF_RUNNING) == 0) {
588 return 0;
589 }
590 #endif
591
592 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
593 if (!cfg->g_protocols[i].enabled) {
594 continue;
595 }
596
597 /* We send only if we have at least one remote system
598 * speaking this protocol or if the protocol is forced */
599 if (cfg->g_protocols[i].enabled > 1) {
600 if ((lldp_size = cfg->g_protocols[i].send(cfg, hw, p)) != -E2BIG) {
601 sent++;
602 continue;
603 } else {
604 VLOG_DBG("send PDU on %s failed E2BIG", hw->h_ifname);
605 continue;
606 }
607 }
608
609 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
610 /* If this remote port is disabled, we don't consider it */
611 if (port->p_hidden_out) {
612 continue;
613 }
614 if (port->p_protocol == cfg->g_protocols[i].mode) {
615 VLOG_DBG("send PDU on %s with protocol %s",
616 hw->h_ifname, cfg->g_protocols[i].name);
617 lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
618 sent++;
619 break;
620 }
621 }
622 }
623
624 if (!sent) {
625 /* Nothing was sent for this port, let's speak the first
626 * available protocol.
627 */
628 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
629 if (!cfg->g_protocols[i].enabled) {
630 continue;
631 }
632 VLOG_DBG("fallback to protocol %s for %s",
633 cfg->g_protocols[i].name, hw->h_ifname);
634 lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
635 break;
636 }
637 if (cfg->g_protocols[i].mode == 0) {
638 VLOG_WARN("no protocol enabled, dunno what to send");
639 }
640 }
641
642 return lldp_size;
643 }