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