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