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