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