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