]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lldpd.c
Revert "Blacklist VMWare interfaces (as per ticket #40)."
[thirdparty/lldpd.git] / src / lldpd.c
1 /*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "lldpd.h"
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <signal.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <time.h>
26 #include <libgen.h>
27 #include <sys/utsname.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/select.h>
31 #include <sys/time.h>
32 #include <sys/ioctl.h>
33 #include <arpa/inet.h>
34 #include <net/if_arp.h>
35
36 #if LLDPD_FD_SETSIZE != FD_SETSIZE
37 # warning "FD_SETSIZE is set to an inconsistent value."
38 #endif
39
40 #ifdef USE_SNMP
41 #include <net-snmp/net-snmp-config.h>
42 #include <net-snmp/net-snmp-includes.h>
43 #include <net-snmp/agent/net-snmp-agent-includes.h>
44 #include <net-snmp/agent/snmp_vars.h>
45 #endif /* USE_SNMP */
46
47 static void usage(void);
48
49 static struct protocol protos[] =
50 {
51 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
52 LLDP_MULTICAST_ADDR },
53 #ifdef ENABLE_CDP
54 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
55 CDP_MULTICAST_ADDR },
56 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
57 CDP_MULTICAST_ADDR },
58 #endif
59 #ifdef ENABLE_SONMP
60 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
61 SONMP_MULTICAST_ADDR },
62 #endif
63 #ifdef ENABLE_EDP
64 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
65 EDP_MULTICAST_ADDR },
66 #endif
67 #ifdef ENABLE_FDP
68 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
69 FDP_MULTICAST_ADDR },
70 #endif
71 { 0, 0, "any", ' ', NULL, NULL, NULL,
72 {0,0,0,0,0,0} }
73 };
74
75 static void lldpd_update_localchassis(struct lldpd *);
76 static void lldpd_update_localports(struct lldpd *);
77 static void lldpd_cleanup(struct lldpd *);
78 static void lldpd_loop(struct lldpd *);
79 static void lldpd_shutdown(int);
80 static void lldpd_exit(void);
81 static void lldpd_send_all(struct lldpd *);
82 static void lldpd_recv_all(struct lldpd *);
83 static int lldpd_guess_type(struct lldpd *, char *, int);
84 static void lldpd_decode(struct lldpd *, char *, int,
85 struct lldpd_hardware *);
86 static void lldpd_update_chassis(struct lldpd_chassis *,
87 const struct lldpd_chassis *);
88 #ifdef ENABLE_LLDPMED
89 static void lldpd_med(struct lldpd_chassis *);
90 #endif
91
92 static char **saved_argv;
93 #ifdef HAVE___PROGNAME
94 extern const char *__progname;
95 #else
96 # define __progname "lldpd"
97 #endif
98
99 static void
100 usage(void)
101 {
102 fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
103
104 fprintf(stderr, "\n");
105
106 fprintf(stderr, "-d Do not daemonize.\n");
107 fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n");
108 fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n");
109 fprintf(stderr, "-S descr Override the default system description.\n");
110 fprintf(stderr, "-m IP Specify the management address of this system.\n");
111 #ifdef ENABLE_LLDPMED
112 fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
113 fprintf(stderr, " 1 Generic Endpoint (Class I)\n");
114 fprintf(stderr, " 2 Media Endpoint (Class II)\n");
115 fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n");
116 fprintf(stderr, " 4 Network Connectivity Device\n");
117 #endif
118 #ifdef USE_SNMP
119 fprintf(stderr, "-x Enable SNMP subagent.\n");
120 #endif
121 #ifdef ENABLE_LISTENVLAN
122 fprintf(stderr, "-v Listen on VLAN as well.\n");
123 #endif
124 fprintf(stderr, "\n");
125
126 #if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP
127 fprintf(stderr, "Additional protocol support.\n");
128 #ifdef ENABLE_CDP
129 fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
130 #endif
131 #ifdef ENABLE_EDP
132 fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
133 #endif
134 #ifdef ENABLE_FDP
135 fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
136 #endif
137 #ifdef ENABLE_SONMP
138 fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
139 #endif
140
141 fprintf(stderr, "\n");
142 #endif
143
144 fprintf(stderr, "see manual page lldpd(8) for more information\n");
145 exit(1);
146 }
147
148 struct lldpd_hardware *
149 lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops)
150 {
151 struct lldpd_hardware *hardware;
152 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
153 if ((strcmp(hardware->h_ifname, name) == 0) &&
154 (hardware->h_ifindex == index) &&
155 ((!ops) || (ops == hardware->h_ops)))
156 break;
157 }
158 return hardware;
159 }
160
161 struct lldpd_hardware *
162 lldpd_alloc_hardware(struct lldpd *cfg, char *name)
163 {
164 struct lldpd_hardware *hardware;
165
166 if ((hardware = (struct lldpd_hardware *)
167 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
168 return NULL;
169
170 strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
171 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
172 hardware->h_lport.p_chassis->c_refcount++;
173 TAILQ_INIT(&hardware->h_rports);
174
175 #ifdef ENABLE_LLDPMED
176 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
177 hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
178 if (!cfg->g_noinventory)
179 hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
180 }
181 #endif
182 #ifdef ENABLE_DOT1
183 TAILQ_INIT(&hardware->h_lport.p_vlans);
184 #endif
185 return hardware;
186 }
187
188 #ifdef ENABLE_DOT1
189 void
190 lldpd_vlan_cleanup(struct lldpd_port *port)
191 {
192 struct lldpd_vlan *vlan, *vlan_next;
193 for (vlan = TAILQ_FIRST(&port->p_vlans);
194 vlan != NULL;
195 vlan = vlan_next) {
196 free(vlan->v_name);
197 vlan_next = TAILQ_NEXT(vlan, v_entries);
198 TAILQ_REMOVE(&port->p_vlans, vlan, v_entries);
199 free(vlan);
200 }
201 }
202 #endif
203
204 /* If `all' is true, clear all information, including information that
205 are not refreshed periodically. Port should be freed manually. */
206 void
207 lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all)
208 {
209 #ifdef ENABLE_LLDPMED
210 int i;
211 if (all)
212 for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
213 free(port->p_med_location[i].data);
214 #endif
215 #ifdef ENABLE_DOT1
216 lldpd_vlan_cleanup(port);
217 #endif
218 free(port->p_id);
219 free(port->p_descr);
220 if (all) {
221 free(port->p_lastframe);
222 if (port->p_chassis) { /* chassis may not have been attributed, yet */
223 port->p_chassis->c_refcount--;
224 port->p_chassis = NULL;
225 }
226 }
227 }
228
229 void
230 lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
231 {
232 #ifdef ENABLE_LLDPMED
233 free(chassis->c_med_hw);
234 free(chassis->c_med_sw);
235 free(chassis->c_med_fw);
236 free(chassis->c_med_sn);
237 free(chassis->c_med_manuf);
238 free(chassis->c_med_model);
239 free(chassis->c_med_asset);
240 #endif
241 free(chassis->c_id);
242 free(chassis->c_name);
243 free(chassis->c_descr);
244 if (all)
245 free(chassis);
246 }
247
248 void
249 lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
250 {
251 struct lldpd_port *port, *port_next;
252 int del;
253 for (port = TAILQ_FIRST(&hardware->h_rports);
254 port != NULL;
255 port = port_next) {
256 port_next = TAILQ_NEXT(port, p_entries);
257 del = all;
258 if (!del &&
259 (time(NULL) - port->p_lastupdate > port->p_chassis->c_ttl)) {
260 hardware->h_rx_ageout_cnt++;
261 del = 1;
262 }
263 if (del) {
264 TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
265 lldpd_port_cleanup(cfg, port, 1);
266 free(port);
267 }
268 }
269 }
270
271 void
272 lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
273 {
274 int i;
275 lldpd_port_cleanup(cfg, &hardware->h_lport, 1);
276 /* If we have a dedicated cleanup function, use it. Otherwise,
277 we just free the hardware-dependent data and close all FD
278 in h_recvfds and h_sendfd. */
279 if (hardware->h_ops->cleanup)
280 hardware->h_ops->cleanup(cfg, hardware);
281 else {
282 free(hardware->h_data);
283 for (i=0; i < LLDPD_FD_SETSIZE; i++)
284 if (FD_ISSET(i, &hardware->h_recvfds))
285 close(i);
286 if (hardware->h_sendfd) close(hardware->h_sendfd);
287 }
288 free(hardware);
289 }
290
291 static void
292 lldpd_cleanup(struct lldpd *cfg)
293 {
294 struct lldpd_hardware *hardware, *hardware_next;
295 struct lldpd_chassis *chassis, *chassis_next;
296
297 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
298 hardware = hardware_next) {
299 hardware_next = TAILQ_NEXT(hardware, h_entries);
300 if (!hardware->h_flags) {
301 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
302 lldpd_remote_cleanup(cfg, hardware, 1);
303 lldpd_hardware_cleanup(cfg, hardware);
304 } else
305 lldpd_remote_cleanup(cfg, hardware, 0);
306 }
307
308 for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
309 chassis = chassis_next) {
310 chassis_next = TAILQ_NEXT(chassis, c_entries);
311 if (chassis->c_refcount == 0) {
312 TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
313 lldpd_chassis_cleanup(chassis, 1);
314 }
315 }
316 }
317
318 static int
319 lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
320 {
321 int i;
322 if (s < ETH_ALEN)
323 return -1;
324 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
325 if (!cfg->g_protocols[i].enabled)
326 continue;
327 if (cfg->g_protocols[i].guess == NULL) {
328 if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ALEN) == 0)
329 return cfg->g_protocols[i].mode;
330 } else {
331 if (cfg->g_protocols[i].guess(frame, s))
332 return cfg->g_protocols[i].mode;
333 }
334 }
335 return -1;
336 }
337
338 static void
339 lldpd_decode(struct lldpd *cfg, char *frame, int s,
340 struct lldpd_hardware *hardware)
341 {
342 int i, result;
343 struct lldpd_chassis *chassis, *ochassis = NULL;
344 struct lldpd_port *port, *oport = NULL;
345 int guess = LLDPD_MODE_LLDP;
346
347 /* Discard VLAN frames */
348 if ((s >= sizeof(struct ethhdr)) &&
349 (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
350 return;
351
352 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
353 if ((oport->p_lastframe != NULL) &&
354 (oport->p_lastframe->size == s) &&
355 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
356 /* Already received the same frame */
357 oport->p_lastupdate = time(NULL);
358 return;
359 }
360 }
361
362 guess = lldpd_guess_type(cfg, frame, s);
363 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
364 if (!cfg->g_protocols[i].enabled)
365 continue;
366 if (cfg->g_protocols[i].mode == guess) {
367 if ((result = cfg->g_protocols[i].decode(cfg, frame,
368 s, hardware, &chassis, &port)) == -1)
369 return;
370 chassis->c_protocol = port->p_protocol =
371 cfg->g_protocols[i].mode;
372 break;
373 }
374 }
375 if (cfg->g_protocols[i].mode == 0) {
376 LLOG_INFO("unable to guess frame type");
377 return;
378 }
379
380 /* Do we already have the same MSAP somewhere? */
381 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
382 if ((port->p_protocol == oport->p_protocol) &&
383 (port->p_id_subtype == oport->p_id_subtype) &&
384 (port->p_id_len == oport->p_id_len) &&
385 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
386 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
387 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
388 (memcmp(chassis->c_id, oport->p_chassis->c_id,
389 chassis->c_id_len) == 0)) {
390 ochassis = oport->p_chassis;
391 break;
392 }
393 }
394 /* No, but do we already know the system? */
395 if (!oport) {
396 TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) {
397 if ((chassis->c_protocol == ochassis->c_protocol) &&
398 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
399 (chassis->c_id_len == ochassis->c_id_len) &&
400 (memcmp(chassis->c_id, ochassis->c_id,
401 chassis->c_id_len) == 0))
402 break;
403 }
404 }
405
406 if (oport) {
407 /* The port is known, remove it before adding it back */
408 TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
409 lldpd_port_cleanup(cfg, oport, 1);
410 free(oport);
411 }
412 if (ochassis) {
413 lldpd_update_chassis(ochassis, chassis);
414 free(chassis);
415 chassis = ochassis;
416 } else {
417 /* Chassis not known, add it */
418 chassis->c_index = ++cfg->g_lastrid;
419 chassis->c_refcount = 0;
420 TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
421 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
422 LLOG_DEBUG("Currently, we know %d different systems", i);
423 }
424 /* Add port */
425 port->p_lastchange = port->p_lastupdate = time(NULL);
426 if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
427 sizeof(int))) != NULL) {
428 port->p_lastframe->size = s;
429 memcpy(port->p_lastframe->frame, frame, s);
430 }
431 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
432 port->p_chassis = chassis;
433 port->p_chassis->c_refcount++;
434 /* Several cases are possible :
435 1. chassis is new, its refcount was 0. It is now attached
436 to this port, its refcount is 1.
437 2. chassis already exists and was attached to another
438 port, we increase its refcount accordingly.
439 3. chassis already exists and was attached to the same
440 port, its refcount was decreased with
441 lldpd_port_cleanup() and is now increased again.
442
443 In all cases, if the port already existed, it has been
444 freed with lldpd_port_cleanup() and therefore, the refcount
445 of the chassis that was attached to it is decreased.
446 */
447 i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
448 LLOG_DEBUG("Currently, %s known %d neighbors",
449 hardware->h_ifname, i);
450 return;
451 }
452
453 /* Update chassis `ochassis' with values from `chassis'. */
454 static void
455 lldpd_update_chassis(struct lldpd_chassis *ochassis,
456 const struct lldpd_chassis *chassis) {
457 TAILQ_ENTRY(lldpd_chassis) entries;
458 /* We want to keep refcount, index and list stuff from the current
459 * chassis */
460 int refcount = ochassis->c_refcount;
461 int index = ochassis->c_index;
462 memcpy(&entries, &ochassis->c_entries,
463 sizeof(entries));
464 /* Make the copy */
465 lldpd_chassis_cleanup(ochassis, 0);
466 memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
467 /* Restore saved values */
468 ochassis->c_refcount = refcount;
469 ochassis->c_index = index;
470 memcpy(&ochassis->c_entries, &entries, sizeof(entries));
471 }
472
473 int
474 lldpd_callback_add(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG), void *data)
475 {
476 struct lldpd_callback *callback;
477 if ((callback = (struct lldpd_callback *)
478 malloc(sizeof(struct lldpd_callback))) == NULL)
479 return -1;
480 callback->fd = fd;
481 callback->function = fn;
482 callback->data = data;
483 TAILQ_INSERT_TAIL(&cfg->g_callbacks, callback, next);
484 return 0;
485 }
486
487 void
488 lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG))
489 {
490 struct lldpd_callback *callback, *callback_next;
491 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
492 callback;
493 callback = callback_next) {
494 callback_next = TAILQ_NEXT(callback, next);
495 if ((callback->fd == fd) &&
496 (callback->function = fn)) {
497 free(callback->data);
498 TAILQ_REMOVE(&cfg->g_callbacks, callback, next);
499 free(callback);
500 }
501 }
502 }
503
504 static void
505 lldpd_recv_all(struct lldpd *cfg)
506 {
507 struct lldpd_hardware *hardware;
508 struct lldpd_callback *callback, *callback_next;
509 fd_set rfds;
510 struct timeval tv;
511 #ifdef USE_SNMP
512 struct timeval snmptv;
513 int snmpblock = 0;
514 #endif
515 int rc, nfds, n;
516 char *buffer;
517
518 do {
519 tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
520 if (tv.tv_sec < 0)
521 tv.tv_sec = LLDPD_TX_DELAY;
522 if (tv.tv_sec >= cfg->g_delay)
523 tv.tv_sec = cfg->g_delay;
524 tv.tv_usec = 0;
525
526 FD_ZERO(&rfds);
527 nfds = -1;
528
529 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
530 /* Ignore if interface is down */
531 if ((hardware->h_flags & IFF_RUNNING) == 0)
532 continue;
533 /* This is quite expensive but we don't rely on internal
534 * structure of fd_set. */
535 for (n = 0; n < LLDPD_FD_SETSIZE; n++)
536 if (FD_ISSET(n, &hardware->h_recvfds)) {
537 FD_SET(n, &rfds);
538 if (nfds < n)
539 nfds = n;
540 }
541 }
542 TAILQ_FOREACH(callback, &cfg->g_callbacks, next) {
543 FD_SET(callback->fd, &rfds);
544 if (nfds < callback->fd)
545 nfds = callback->fd;
546 }
547
548 #ifdef USE_SNMP
549 if (cfg->g_snmp) {
550 snmpblock = 0;
551 memcpy(&snmptv, &tv, sizeof(struct timeval));
552 snmp_select_info(&nfds, &rfds, &snmptv, &snmpblock);
553 if (snmpblock == 0)
554 memcpy(&tv, &snmptv, sizeof(struct timeval));
555 }
556 #endif /* USE_SNMP */
557 if (nfds == -1) {
558 sleep(cfg->g_delay);
559 return;
560 }
561
562 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
563 if (rc == -1) {
564 if (errno == EINTR)
565 continue;
566 LLOG_WARN("failure on select");
567 break;
568 }
569 #ifdef USE_SNMP
570 if (cfg->g_snmp) {
571 if (rc > 0)
572 snmp_read(&rfds);
573 else if (rc == 0)
574 snmp_timeout();
575 }
576 #endif /* USE_SNMP */
577 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
578 for (n = 0; n < LLDPD_FD_SETSIZE; n++)
579 if ((FD_ISSET(n, &hardware->h_recvfds)) &&
580 (FD_ISSET(n, &rfds))) break;
581 if (n == LLDPD_FD_SETSIZE) continue;
582 if ((buffer = (char *)malloc(
583 hardware->h_mtu)) == NULL) {
584 LLOG_WARN("failed to alloc reception buffer");
585 continue;
586 }
587 if ((n = hardware->h_ops->recv(cfg, hardware,
588 n, buffer, hardware->h_mtu)) == -1) {
589 free(buffer);
590 continue;
591 }
592 hardware->h_rx_cnt++;
593 lldpd_decode(cfg, buffer, n, hardware);
594 free(buffer);
595 break;
596 }
597 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
598 callback;
599 callback = callback_next) {
600 /* Callback function can use TAILQ_REMOVE */
601 callback_next = TAILQ_NEXT(callback, next);
602 if (FD_ISSET(callback->fd, &rfds))
603 callback->function(cfg, callback);
604 }
605
606 #ifdef USE_SNMP
607 if (cfg->g_snmp) {
608 run_alarms();
609 netsnmp_check_outstanding_agent_requests();
610 }
611 #endif /* USE_SNMP */
612 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
613 }
614
615 static void
616 lldpd_send_all(struct lldpd *cfg)
617 {
618 struct lldpd_hardware *hardware;
619 struct lldpd_port *port;
620 int i, sent;
621
622 cfg->g_lastsent = time(NULL);
623 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
624 /* Ignore if interface is down */
625 if ((hardware->h_flags & IFF_RUNNING) == 0)
626 continue;
627
628 sent = 0;
629 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
630 if (!cfg->g_protocols[i].enabled)
631 continue;
632 /* We send only if we have at least one remote system
633 * speaking this protocol or if the protocol is forced */
634 if (cfg->g_protocols[i].enabled > 1) {
635 cfg->g_protocols[i].send(cfg, hardware);
636 sent++;
637 continue;
638 }
639 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
640 if (port->p_protocol ==
641 cfg->g_protocols[i].mode) {
642 cfg->g_protocols[i].send(cfg,
643 hardware);
644 sent++;
645 break;
646 }
647 }
648 }
649
650 if (!sent)
651 /* Nothing was sent for this port, let's speak LLDP */
652 cfg->g_protocols[0].send(cfg,
653 hardware);
654 }
655 }
656
657 #ifdef ENABLE_LLDPMED
658 static void
659 lldpd_med(struct lldpd_chassis *chassis)
660 {
661 free(chassis->c_med_hw);
662 free(chassis->c_med_fw);
663 free(chassis->c_med_sn);
664 free(chassis->c_med_manuf);
665 free(chassis->c_med_model);
666 free(chassis->c_med_asset);
667 chassis->c_med_hw = dmi_hw();
668 chassis->c_med_fw = dmi_fw();
669 chassis->c_med_sn = dmi_sn();
670 chassis->c_med_manuf = dmi_manuf();
671 chassis->c_med_model = dmi_model();
672 chassis->c_med_asset = dmi_asset();
673 }
674 #endif
675
676 static void
677 lldpd_update_localchassis(struct lldpd *cfg)
678 {
679 struct utsname un;
680 char *hp;
681 int f;
682 char status;
683 struct lldpd_hardware *hardware;
684
685 /* Set system name and description */
686 if (uname(&un) != 0)
687 fatal("failed to get system information");
688 if ((hp = priv_gethostbyname()) == NULL)
689 fatal("failed to get system name");
690 free(LOCAL_CHASSIS(cfg)->c_name);
691 free(LOCAL_CHASSIS(cfg)->c_descr);
692 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
693 fatal(NULL);
694 if (cfg->g_descr_override) {
695 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
696 cfg->g_descr_override) == -1)
697 fatal("failed to set full system description");
698 } else {
699 if (cfg->g_advertise_version) {
700 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
701 un.sysname, un.release, un.version, un.machine)
702 == -1)
703 fatal("failed to set full system description");
704 } else {
705 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
706 un.sysname) == -1)
707 fatal("failed to set minimal system description");
708 }
709 }
710
711 /* Check forwarding */
712 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
713 if ((read(f, &status, 1) == 1) && (status == '1')) {
714 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
715 }
716 close(f);
717 }
718 #ifdef ENABLE_LLDPMED
719 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
720 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
721 lldpd_med(LOCAL_CHASSIS(cfg));
722 free(LOCAL_CHASSIS(cfg)->c_med_sw);
723 if (cfg->g_advertise_version)
724 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
725 else
726 LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
727 #endif
728
729 /* Set chassis ID if needed */
730 if ((LOCAL_CHASSIS(cfg)->c_id == NULL) &&
731 (hardware = TAILQ_FIRST(&cfg->g_hardware))) {
732 if ((LOCAL_CHASSIS(cfg)->c_id =
733 malloc(sizeof(hardware->h_lladdr))) == NULL)
734 fatal(NULL);
735 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
736 LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
737 memcpy(LOCAL_CHASSIS(cfg)->c_id,
738 hardware->h_lladdr, sizeof(hardware->h_lladdr));
739 }
740 }
741
742 static void
743 lldpd_update_localports(struct lldpd *cfg)
744 {
745 struct ifaddrs *ifap;
746 struct lldpd_hardware *hardware;
747 lldpd_ifhandlers ifhs[] = {
748 lldpd_ifh_bond, /* Handle bond */
749 lldpd_ifh_eth, /* Handle classic ethernet interfaces */
750 #ifdef ENABLE_DOT1
751 lldpd_ifh_vlan, /* Handle VLAN */
752 #endif
753 lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
754 NULL
755 };
756 lldpd_ifhandlers *ifh;
757
758 /* h_flags is set to 0 for each port. If the port is updated, h_flags
759 * will be set to a non-zero value. This will allow us to clean up any
760 * non up-to-date port */
761 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
762 hardware->h_flags = 0;
763
764 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
765 if (getifaddrs(&ifap) != 0)
766 fatal("lldpd_update_localports: failed to get interface list");
767
768 /* We will run the list of interfaces through a list of interface
769 * handlers. Each handler will create or update some hardware port (and
770 * will set h_flags to a non zero value. The handler can use the list of
771 * interfaces but this is not mandatory. If the interface handler
772 * handles an interface from the list, it should set ifa_flags to 0 to
773 * let know the other handlers that it took care of this interface. This
774 * means that more specific handlers should be before less specific
775 * ones. */
776 for (ifh = ifhs; *ifh != NULL; ifh++)
777 (*ifh)(cfg, ifap);
778 freeifaddrs(ifap);
779 }
780
781 static void
782 lldpd_loop(struct lldpd *cfg)
783 {
784 /* Main loop.
785
786 1. Update local ports information
787 2. Clean unwanted (removed) local ports
788 3. Update local chassis information
789 4. Send packets
790 5. Receive packets
791 */
792 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
793 lldpd_update_localports(cfg);
794 lldpd_cleanup(cfg);
795 lldpd_update_localchassis(cfg);
796 lldpd_send_all(cfg);
797 lldpd_recv_all(cfg);
798 }
799
800 static void
801 lldpd_shutdown(int sig)
802 {
803 LLOG_INFO("signal received, exiting");
804 exit(0);
805 }
806
807 /* For signal handling */
808 static struct lldpd *gcfg = NULL;
809
810 static void
811 lldpd_exit()
812 {
813 struct lldpd_hardware *hardware, *hardware_next;
814 close(gcfg->g_ctl);
815 priv_ctl_cleanup();
816 for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL;
817 hardware = hardware_next) {
818 hardware_next = TAILQ_NEXT(hardware, h_entries);
819 lldpd_hardware_cleanup(gcfg, hardware);
820 }
821 #ifdef USE_SNMP
822 if (gcfg->g_snmp)
823 agent_shutdown();
824 #endif /* USE_SNMP */
825 }
826
827 int
828 lldpd_main(int argc, char *argv[])
829 {
830 struct lldpd *cfg;
831 struct lldpd_chassis *lchassis;
832 int ch, debug = 0;
833 #ifdef USE_SNMP
834 int snmp = 0;
835 char *agentx = NULL; /* AgentX socket */
836 #endif
837 char *mgmtp = NULL;
838 char *popt, opts[] =
839 #ifdef ENABLE_LISTENVLAN
840 "v"
841 #endif
842 "hkdxX:m:p:M:S:i@ ";
843 int i, found, advertise_version = 1;
844 #ifdef ENABLE_LISTENVLAN
845 int vlan = 0;
846 #endif
847 #ifdef ENABLE_LLDPMED
848 int lldpmed = 0, noinventory = 0;
849 #endif
850 char *descr_override = NULL;
851
852 saved_argv = argv;
853
854 /*
855 * Get and parse command line options
856 */
857 popt = strchr(opts, '@');
858 for (i=0; protos[i].mode != 0; i++)
859 *(popt++) = protos[i].arg;
860 *popt = '\0';
861 while ((ch = getopt(argc, argv, opts)) != -1) {
862 switch (ch) {
863 case 'h':
864 usage();
865 break;
866 #ifdef ENABLE_LISTENVLAN
867 case 'v':
868 vlan = 1;
869 break;
870 #endif
871 case 'd':
872 debug++;
873 break;
874 case 'm':
875 mgmtp = optarg;
876 break;
877 case 'k':
878 advertise_version = 0;
879 break;
880 #ifdef ENABLE_LLDPMED
881 case 'M':
882 lldpmed = atoi(optarg);
883 if ((lldpmed < 1) || (lldpmed > 4)) {
884 fprintf(stderr, "-M requires an argument between 1 and 4\n");
885 usage();
886 }
887 break;
888 case 'i':
889 noinventory = 1;
890 break;
891 #else
892 case 'M':
893 case 'i':
894 case 'P':
895 fprintf(stderr, "LLDP-MED support is not built-in\n");
896 usage();
897 break;
898 #endif
899 #ifdef USE_SNMP
900 case 'x':
901 snmp = 1;
902 break;
903 case 'X':
904 snmp = 1;
905 agentx = optarg;
906 break;
907 #else
908 case 'x':
909 case 'X':
910 fprintf(stderr, "SNMP support is not built-in\n");
911 usage();
912 #endif
913 break;
914 case 'S':
915 descr_override = strdup(optarg);
916 break;
917 default:
918 found = 0;
919 for (i=0; protos[i].mode != 0; i++) {
920 if (ch == protos[i].arg) {
921 protos[i].enabled++;
922 /* When an argument enable
923 several protocols, only the
924 first one can be forced. */
925 if (found && protos[i].enabled > 1)
926 protos[i].enabled = 1;
927 found = 1;
928 }
929 }
930 if (!found)
931 usage();
932 }
933 }
934
935 log_init(debug, __progname);
936 tzset(); /* Get timezone info before chroot */
937
938 if (!debug) {
939 int pid;
940 char *spid;
941 if (daemon(0, 0) != 0)
942 fatal("failed to detach daemon");
943 if ((pid = open(LLDPD_PID_FILE,
944 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
945 fatal("unable to open pid file " LLDPD_PID_FILE);
946 if (asprintf(&spid, "%d\n", getpid()) == -1)
947 fatal("unable to create pid file " LLDPD_PID_FILE);
948 if (write(pid, spid, strlen(spid)) == -1)
949 fatal("unable to write pid file " LLDPD_PID_FILE);
950 free(spid);
951 close(pid);
952 }
953
954 priv_init(PRIVSEP_CHROOT);
955
956 if ((cfg = (struct lldpd *)
957 calloc(1, sizeof(struct lldpd))) == NULL)
958 fatal(NULL);
959
960 cfg->g_mgmt_pattern = mgmtp;
961 cfg->g_advertise_version = advertise_version;
962 #ifdef ENABLE_LISTENVLAN
963 cfg->g_listen_vlans = vlan;
964 #endif
965
966 /* Get ioctl socket */
967 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
968 fatal("failed to get ioctl socket");
969 cfg->g_delay = LLDPD_TX_DELAY;
970
971 if (descr_override)
972 cfg->g_descr_override = descr_override;
973
974 /* Set system capabilities */
975 if ((lchassis = (struct lldpd_chassis*)
976 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
977 fatal(NULL);
978 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
979 LLDP_CAP_ROUTER;
980 #ifdef ENABLE_LLDPMED
981 if (lldpmed > 0) {
982 if (lldpmed == LLDPMED_CLASS_III)
983 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
984 lchassis->c_med_type = lldpmed;
985 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
986 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION |
987 LLDPMED_CAP_POLICY | LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_MDI_PD;
988 cfg->g_noinventory = noinventory;
989 } else
990 cfg->g_noinventory = 1;
991 #endif
992
993 /* Set TTL */
994 lchassis->c_ttl = LLDPD_TTL;
995
996 cfg->g_protocols = protos;
997 for (i=0; protos[i].mode != 0; i++)
998 if (protos[i].enabled > 1)
999 LLOG_INFO("protocol %s enabled and forced", protos[i].name);
1000 else if (protos[i].enabled)
1001 LLOG_INFO("protocol %s enabled", protos[i].name);
1002 else
1003 LLOG_INFO("protocol %s disabled", protos[i].name);
1004
1005 TAILQ_INIT(&cfg->g_hardware);
1006 TAILQ_INIT(&cfg->g_chassis);
1007 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
1008 lchassis->c_refcount++; /* We should always keep a reference to local chassis */
1009
1010 TAILQ_INIT(&cfg->g_callbacks);
1011
1012 #ifdef USE_SNMP
1013 if (snmp) {
1014 cfg->g_snmp = 1;
1015 agent_init(cfg, agentx, debug);
1016 }
1017 #endif /* USE_SNMP */
1018
1019 /* Create socket */
1020 if ((cfg->g_ctl = priv_ctl_create()) == -1)
1021 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
1022 if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
1023 fatalx("unable to add callback for control socket");
1024
1025 gcfg = cfg;
1026 if (atexit(lldpd_exit) != 0) {
1027 close(cfg->g_ctl);
1028 priv_ctl_cleanup();
1029 fatal("unable to set exit function");
1030 }
1031
1032 /* Signal handling */
1033 signal(SIGHUP, lldpd_shutdown);
1034 signal(SIGINT, lldpd_shutdown);
1035 signal(SIGTERM, lldpd_shutdown);
1036
1037 for (;;)
1038 lldpd_loop(cfg);
1039
1040 return (0);
1041 }