]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/lldpd.c
lldpd: make fast start work only when receiving LLDP-MED LLDPDU
[thirdparty/lldpd.git] / src / daemon / lldpd.c
CommitLineData
4b292b55 1/* -*- mode: c; c-file-style: "openbsd" -*- */
43c02e7b
VB
2/*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
51434125 5 * Permission to use, copy, modify, and/or distribute this software for any
43c02e7b
VB
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include "lldpd.h"
19
20#include <stdio.h>
21#include <unistd.h>
22#include <errno.h>
23#include <signal.h>
24#include <sys/stat.h>
25#include <fcntl.h>
43c02e7b 26#include <time.h>
a2993d83 27#include <libgen.h>
e6b36c87 28#include <assert.h>
43c02e7b
VB
29#include <sys/utsname.h>
30#include <sys/types.h>
c036b15d 31#include <sys/wait.h>
43c02e7b
VB
32#include <sys/socket.h>
33#include <sys/select.h>
34#include <sys/time.h>
35#include <sys/ioctl.h>
36#include <arpa/inet.h>
690b944c 37#include <netinet/if_ether.h>
2e91d1a1
VB
38#include <pwd.h>
39#include <grp.h>
1b3eea3d
VB
40#if defined HOST_OS_FREEBSD || \
41 defined HOST_OS_DRAGONFLY || \
42 defined HOST_OS_OPENBSD || \
43 defined HOST_OS_NETBSD || \
e66b7f34 44 defined HOST_OS_OSX
690b944c 45# include <sys/param.h>
242845c7
VB
46# include <sys/sysctl.h>
47#endif
43c02e7b 48
8888d191 49static void usage(void);
43c02e7b 50
8888d191 51static struct protocol protos[] =
43c02e7b 52{
0c877af0 53 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
f2dcb180 54 LLDP_MULTICAST_ADDR },
4bad1937 55#ifdef ENABLE_CDP
43c02e7b 56 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
f2dcb180 57 CDP_MULTICAST_ADDR },
43c02e7b 58 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
f2dcb180 59 CDP_MULTICAST_ADDR },
4bad1937
VB
60#endif
61#ifdef ENABLE_SONMP
43c02e7b 62 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
f2dcb180 63 SONMP_MULTICAST_ADDR },
4bad1937
VB
64#endif
65#ifdef ENABLE_EDP
43c02e7b 66 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
f2dcb180 67 EDP_MULTICAST_ADDR },
4bad1937
VB
68#endif
69#ifdef ENABLE_FDP
031118c4 70 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
f2dcb180 71 FDP_MULTICAST_ADDR },
4bad1937 72#endif
43c02e7b 73 { 0, 0, "any", ' ', NULL, NULL, NULL,
f2dcb180 74 {0,0,0,0,0,0} }
43c02e7b
VB
75};
76
8888d191 77static char **saved_argv;
6bb9c4e0
VB
78#ifdef HAVE___PROGNAME
79extern const char *__progname;
80#else
81# define __progname "lldpd"
82#endif
43c02e7b 83
8888d191 84static void
43c02e7b
VB
85usage(void)
86{
4593c0dc
VB
87 fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
88 fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
53132616
VB
89
90 fprintf(stderr, "\n");
91
92 fprintf(stderr, "-d Do not daemonize.\n");
5e64a80e 93 fprintf(stderr, "-r Receive-only mode\n");
53132616
VB
94 fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n");
95 fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n");
fde7a7ce 96 fprintf(stderr, "-S descr Override the default system description.\n");
d4afb919 97 fprintf(stderr, "-P name Override the default hardware platform.\n");
d4e4c804 98 fprintf(stderr, "-m IP Specify the IPv4 management addresses of this system.\n");
0262adbb 99 fprintf(stderr, "-u file Specify the Unix-domain socket used for communication with lldpctl(8).\n");
42b39485 100 fprintf(stderr, "-H mode Specify the behaviour when detecting multiple neighbors.\n");
fbda1f9f 101 fprintf(stderr, "-I iface Limit interfaces to use.\n");
8347587d 102#ifdef ENABLE_LLDPMED
53132616
VB
103 fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
104 fprintf(stderr, " 1 Generic Endpoint (Class I)\n");
105 fprintf(stderr, " 2 Media Endpoint (Class II)\n");
106 fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n");
107 fprintf(stderr, " 4 Network Connectivity Device\n");
8347587d
VB
108#endif
109#ifdef USE_SNMP
53132616 110 fprintf(stderr, "-x Enable SNMP subagent.\n");
53132616
VB
111#endif
112 fprintf(stderr, "\n");
113
8347587d
VB
114#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP
115 fprintf(stderr, "Additional protocol support.\n");
116#ifdef ENABLE_CDP
53132616 117 fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
8347587d
VB
118#endif
119#ifdef ENABLE_EDP
53132616 120 fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
8347587d
VB
121#endif
122#ifdef ENABLE_FDP
53132616 123 fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
8347587d
VB
124#endif
125#ifdef ENABLE_SONMP
53132616 126 fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
8347587d 127#endif
53132616
VB
128
129 fprintf(stderr, "\n");
8347587d 130#endif
53132616 131
e809a587 132 fprintf(stderr, "see manual page lldpd(8) for more information\n");
43c02e7b
VB
133 exit(1);
134}
135
6e75df87 136struct lldpd_hardware *
44002d66 137lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops)
43c02e7b 138{
6e75df87
VB
139 struct lldpd_hardware *hardware;
140 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
849954d7 141 if ((strcmp(hardware->h_ifname, name) == 0) &&
44002d66 142 (hardware->h_ifindex == index) &&
849954d7 143 ((!ops) || (ops == hardware->h_ops)))
6e75df87 144 break;
43c02e7b 145 }
6e75df87 146 return hardware;
43c02e7b
VB
147}
148
6e75df87 149struct lldpd_hardware *
e12c2365 150lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
43c02e7b 151{
6e75df87 152 struct lldpd_hardware *hardware;
43c02e7b 153
6f8925be
VB
154 log_debug("alloc", "allocate a new local port (%s)", name);
155
6e75df87
VB
156 if ((hardware = (struct lldpd_hardware *)
157 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
158 return NULL;
43c02e7b 159
d6e889b6 160 hardware->h_cfg = cfg;
6e75df87 161 strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
e12c2365 162 hardware->h_ifindex = index;
6e75df87 163 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
9df5ec3d 164 hardware->h_lport.p_chassis->c_refcount++;
6e75df87 165 TAILQ_INIT(&hardware->h_rports);
43c02e7b 166
6e75df87
VB
167#ifdef ENABLE_LLDPMED
168 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
4b292b55 169 hardware->h_lport.p_med_cap_enabled = LLDP_MED_CAP_CAP;
8ec333bd 170 if (!cfg->g_config.c_noinventory)
4b292b55 171 hardware->h_lport.p_med_cap_enabled |= LLDP_MED_CAP_IV;
6e75df87
VB
172 }
173#endif
174#ifdef ENABLE_DOT1
175 TAILQ_INIT(&hardware->h_lport.p_vlans);
9757bfbc
SK
176 TAILQ_INIT(&hardware->h_lport.p_ppvids);
177 TAILQ_INIT(&hardware->h_lport.p_pids);
6e75df87 178#endif
d6e889b6
VB
179
180 levent_hardware_init(hardware);
6e75df87 181 return hardware;
43c02e7b
VB
182}
183
e6b36c87
JV
184struct lldpd_mgmt *
185lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
186{
187 struct lldpd_mgmt *mgmt;
6f8925be
VB
188
189 log_debug("alloc", "allocate a new management address (family: %d)", family);
190
e6b36c87
JV
191 if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
192 errno = EAFNOSUPPORT;
193 return NULL;
194 }
195 if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
196 errno = EOVERFLOW;
197 return NULL;
198 }
199 mgmt = calloc(1, sizeof(struct lldpd_mgmt));
200 if (mgmt == NULL) {
201 errno = ENOMEM;
202 return NULL;
203 }
204 mgmt->m_family = family;
205 assert(addrsize <= LLDPD_MGMT_MAXADDRSIZE);
206 memcpy(&mgmt->m_addr, addrptr, addrsize);
207 mgmt->m_addrsize = addrsize;
208 mgmt->m_iface = iface;
209 return mgmt;
210}
211
d9be8ea0 212void
6e75df87 213lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
d9be8ea0 214{
6f8925be
VB
215 log_debug("alloc", "cleanup hardware port %s", hardware->h_ifname);
216
4b292b55 217 lldpd_port_cleanup(&hardware->h_lport, 1);
6e75df87
VB
218 if (hardware->h_ops->cleanup)
219 hardware->h_ops->cleanup(cfg, hardware);
d6e889b6 220 levent_hardware_release(hardware);
d9be8ea0
VB
221 free(hardware);
222}
223
4e90a9e0
VB
224static void
225notify_clients_deletion(struct lldpd_hardware *hardware,
25de85a4 226 struct lldpd_port *rport)
4e90a9e0
VB
227{
228 levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_DELETED,
25de85a4
VB
229 rport);
230#ifdef USE_SNMP
231 agent_notify(hardware, NEIGHBOR_CHANGE_DELETED, rport);
232#endif
4e90a9e0
VB
233}
234
579bedd5
VB
235static void
236lldpd_reset_timer(struct lldpd *cfg)
237{
238 /* Reset timer for ports that have been changed. */
239 struct lldpd_hardware *hardware;
240 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
241 /* We need to compute a checksum of the local port. To do this,
242 * we zero out fields that are not significant, marshal the
243 * port, compute the checksum, then restore. */
244 struct lldpd_port *port = &hardware->h_lport;
245 u_int16_t cksum;
246 u_int8_t *output = NULL;
247 size_t output_len;
248 char save[offsetof(struct lldpd_port, p_id_subtype)];
249 memcpy(save, port, sizeof(save));
250 memset(port, 0, sizeof(save));
251 output_len = marshal_serialize(lldpd_port, port, (void**)&output);
252 memcpy(port, save, sizeof(save));
253 if (output_len == -1) {
254 log_warnx("localchassis",
255 "unable to serialize local port %s to check for differences",
256 hardware->h_ifname);
257 continue;
258 }
259 cksum = frame_checksum(output, output_len, 0);
260 free(output);
261 if (cksum != hardware->h_lport_cksum) {
262 log_info("localchassis",
263 "change detected for port %s, resetting its timer",
264 hardware->h_ifname);
265 hardware->h_lport_cksum = cksum;
266 levent_schedule_pdu(hardware);
267 } else {
268 log_debug("localchassis",
269 "no change detected for port %s",
270 hardware->h_ifname);
271 }
272 }
273}
274
3333d2a8 275void
43c02e7b
VB
276lldpd_cleanup(struct lldpd *cfg)
277{
278 struct lldpd_hardware *hardware, *hardware_next;
aadc9936 279 struct lldpd_chassis *chassis, *chassis_next;
43c02e7b 280
3333d2a8 281 log_debug("localchassis", "cleanup all ports");
6f8925be 282
43c02e7b
VB
283 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
284 hardware = hardware_next) {
285 hardware_next = TAILQ_NEXT(hardware, h_entries);
6e75df87 286 if (!hardware->h_flags) {
43c02e7b 287 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
4e90a9e0 288 lldpd_remote_cleanup(hardware, NULL);
6e75df87 289 lldpd_hardware_cleanup(cfg, hardware);
77507b69 290 } else
4e90a9e0 291 lldpd_remote_cleanup(hardware, notify_clients_deletion);
43c02e7b 292 }
aadc9936 293
3333d2a8 294 log_debug("localchassis", "cleanup all chassis");
6f8925be 295
aadc9936
VB
296 for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
297 chassis = chassis_next) {
298 chassis_next = TAILQ_NEXT(chassis, c_entries);
299 if (chassis->c_refcount == 0) {
300 TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
301 lldpd_chassis_cleanup(chassis, 1);
302 }
303 }
3333d2a8
VB
304
305 levent_schedule_cleanup(cfg);
43c02e7b
VB
306}
307
d938c51f
VB
308/* Update chassis `ochassis' with values from `chassis'. The later one is not
309 expected to be part of a list! It will also be wiped from memory. */
566c635d 310static void
d938c51f
VB
311lldpd_move_chassis(struct lldpd_chassis *ochassis,
312 struct lldpd_chassis *chassis) {
5fd6695c
VB
313 struct lldpd_mgmt *mgmt, *mgmt_next;
314
566c635d
VB
315 /* We want to keep refcount, index and list stuff from the current
316 * chassis */
d938c51f 317 TAILQ_ENTRY(lldpd_chassis) entries;
566c635d
VB
318 int refcount = ochassis->c_refcount;
319 int index = ochassis->c_index;
320 memcpy(&entries, &ochassis->c_entries,
321 sizeof(entries));
566c635d 322 lldpd_chassis_cleanup(ochassis, 0);
d938c51f
VB
323
324 /* Make the copy. */
325 /* WARNING: this is a kludgy hack, we need in-place copy and cannot use
326 * marshaling. */
566c635d 327 memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
d938c51f
VB
328 TAILQ_INIT(&ochassis->c_mgmt);
329
330 /* Copy of management addresses */
d938c51f
VB
331 for (mgmt = TAILQ_FIRST(&chassis->c_mgmt);
332 mgmt != NULL;
333 mgmt = mgmt_next) {
334 mgmt_next = TAILQ_NEXT(mgmt, m_entries);
335 TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries);
336 TAILQ_INSERT_TAIL(&ochassis->c_mgmt, mgmt, m_entries);
337 }
338
566c635d
VB
339 /* Restore saved values */
340 ochassis->c_refcount = refcount;
341 ochassis->c_index = index;
342 memcpy(&ochassis->c_entries, &entries, sizeof(entries));
d938c51f
VB
343
344 /* Get rid of the new chassis */
345 free(chassis);
566c635d
VB
346}
347
8888d191 348static int
43c02e7b
VB
349lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
350{
351 int i;
4e5f34c5 352 if (s < ETHER_ADDR_LEN)
43c02e7b
VB
353 return -1;
354 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
355 if (!cfg->g_protocols[i].enabled)
356 continue;
357 if (cfg->g_protocols[i].guess == NULL) {
4e5f34c5 358 if (memcmp(frame, cfg->g_protocols[i].mac, ETHER_ADDR_LEN) == 0) {
6f8925be
VB
359 log_debug("decode", "guessed protocol is %s (from MAC address)",
360 cfg->g_protocols[i].name);
43c02e7b 361 return cfg->g_protocols[i].mode;
6f8925be 362 }
43c02e7b 363 } else {
6f8925be
VB
364 if (cfg->g_protocols[i].guess(frame, s)) {
365 log_debug("decode", "guessed protocol is %s (from detector function)",
366 cfg->g_protocols[i].name);
43c02e7b 367 return cfg->g_protocols[i].mode;
6f8925be 368 }
43c02e7b
VB
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{
281a5cd4 378 int i;
77507b69 379 struct lldpd_chassis *chassis, *ochassis = NULL;
4e90a9e0 380 struct lldpd_port *port, *oport = NULL, *aport;
43c02e7b
VB
381 int guess = LLDPD_MODE_LLDP;
382
6f8925be
VB
383 log_debug("decode", "decode a received frame on %s",
384 hardware->h_ifname);
385
4e5f34c5 386 if (s < sizeof(struct ether_header) + 4)
49697208 387 /* Too short, just discard it */
50a89ca7 388 return;
49697208 389 /* Decapsulate VLAN frames */
4e5f34c5 390 if (((struct ether_header*)frame)->ether_type == htons(ETHERTYPE_VLAN)) {
49697208 391 /* VLAN decapsulation means to shift 4 bytes left the frame from
4e5f34c5
VB
392 * offset 2*ETHER_ADDR_LEN */
393 memmove(frame + 2*ETHER_ADDR_LEN, frame + 2*ETHER_ADDR_LEN + 4, s - 2*ETHER_ADDR_LEN);
49697208
VB
394 s -= 4;
395 }
50a89ca7 396
77507b69
VB
397 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
398 if ((oport->p_lastframe != NULL) &&
399 (oport->p_lastframe->size == s) &&
400 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
401 /* Already received the same frame */
6f8925be 402 log_debug("decode", "duplicate frame, no need to decode");
77507b69
VB
403 oport->p_lastupdate = time(NULL);
404 return;
405 }
43c02e7b
VB
406 }
407
f2dcb180
VB
408 guess = lldpd_guess_type(cfg, frame, s);
409 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
410 if (!cfg->g_protocols[i].enabled)
411 continue;
412 if (cfg->g_protocols[i].mode == guess) {
6f8925be
VB
413 log_debug("decode", "using decode function for %s protocol",
414 cfg->g_protocols[i].name);
281a5cd4
VB
415 if (cfg->g_protocols[i].decode(cfg, frame,
416 s, hardware, &chassis, &port) == -1) {
6f8925be
VB
417 log_debug("decode", "function for %s protocol did not decode this frame",
418 cfg->g_protocols[i].name);
f2dcb180 419 return;
6f8925be 420 }
77507b69
VB
421 chassis->c_protocol = port->p_protocol =
422 cfg->g_protocols[i].mode;
f2dcb180 423 break;
43c02e7b 424 }
f2dcb180
VB
425 }
426 if (cfg->g_protocols[i].mode == 0) {
6f8925be 427 log_debug("decode", "unable to guess frame type on %s",
ba5116b5 428 hardware->h_ifname);
43c02e7b 429 return;
f2dcb180 430 }
43c02e7b 431
77507b69 432 /* Do we already have the same MSAP somewhere? */
42589660 433 int count = 0;
6f8925be 434 log_debug("decode", "search for the same MSAP");
77507b69 435 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
42589660
VB
436 if (port->p_protocol == oport->p_protocol) {
437 count++;
438 if ((port->p_id_subtype == oport->p_id_subtype) &&
439 (port->p_id_len == oport->p_id_len) &&
440 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
441 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
442 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
443 (memcmp(chassis->c_id, oport->p_chassis->c_id,
444 chassis->c_id_len) == 0)) {
445 ochassis = oport->p_chassis;
446 log_debug("decode", "MSAP is already known");
447 break;
448 }
77507b69 449 }
43c02e7b 450 }
42589660
VB
451 /* Do we have room for a new MSAP? */
452 if (!oport && cfg->g_config.c_max_neighbors &&
8a771868 453 count > cfg->g_config.c_max_neighbors - 1) {
42589660
VB
454 log_info("decode",
455 "too many neighbors for port %s, drop this new one",
456 hardware->h_ifname);
457 lldpd_port_cleanup(port, 1);
458 lldpd_chassis_cleanup(chassis, 1);
459 free(port);
460 return;
461 }
77507b69
VB
462 /* No, but do we already know the system? */
463 if (!oport) {
6f8925be 464 log_debug("decode", "MSAP is unknown, search for the chassis");
77507b69
VB
465 TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) {
466 if ((chassis->c_protocol == ochassis->c_protocol) &&
467 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
468 (chassis->c_id_len == ochassis->c_id_len) &&
469 (memcmp(chassis->c_id, ochassis->c_id,
470 chassis->c_id_len) == 0))
471 break;
43c02e7b 472 }
43c02e7b 473 }
43c02e7b 474
77507b69
VB
475 if (oport) {
476 /* The port is known, remove it before adding it back */
477 TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
4b292b55 478 lldpd_port_cleanup(oport, 1);
4e624dc2 479 free(oport);
77507b69
VB
480 }
481 if (ochassis) {
d938c51f 482 lldpd_move_chassis(ochassis, chassis);
77507b69
VB
483 chassis = ochassis;
484 } else {
485 /* Chassis not known, add it */
6f8925be 486 log_debug("decode", "unknown chassis, add it to the list");
77507b69 487 chassis->c_index = ++cfg->g_lastrid;
77507b69
VB
488 chassis->c_refcount = 0;
489 TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
f4c43902 490 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
6f8925be 491 log_debug("decode", "%d different systems are known", i);
77507b69
VB
492 }
493 /* Add port */
494 port->p_lastchange = port->p_lastupdate = time(NULL);
495 if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
4e90a9e0 496 sizeof(struct lldpd_frame))) != NULL) {
77507b69
VB
497 port->p_lastframe->size = s;
498 memcpy(port->p_lastframe->frame, frame, s);
499 }
500 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
501 port->p_chassis = chassis;
502 port->p_chassis->c_refcount++;
9df5ec3d
VB
503 /* Several cases are possible :
504 1. chassis is new, its refcount was 0. It is now attached
505 to this port, its refcount is 1.
506 2. chassis already exists and was attached to another
507 port, we increase its refcount accordingly.
508 3. chassis already exists and was attached to the same
509 port, its refcount was decreased with
510 lldpd_port_cleanup() and is now increased again.
511
512 In all cases, if the port already existed, it has been
513 freed with lldpd_port_cleanup() and therefore, the refcount
514 of the chassis that was attached to it is decreased.
515 */
4e90a9e0 516 i = 0; TAILQ_FOREACH(aport, &hardware->h_rports, p_entries)
42b39485 517 i++;
6f8925be
VB
518 log_debug("decode", "%d neighbors for %s", i,
519 hardware->h_ifname);
4e90a9e0 520
25de85a4
VB
521 if (!oport) hardware->h_insert_cnt++;
522
4e90a9e0 523 /* Notify */
6f8925be
VB
524 log_debug("decode", "send notifications for changes on %s",
525 hardware->h_ifname);
25de85a4
VB
526 i = oport?NEIGHBOR_CHANGE_UPDATED:NEIGHBOR_CHANGE_ADDED;
527 levent_ctl_notify(hardware->h_ifname, i, port);
528#ifdef USE_SNMP
529 agent_notify(hardware, i, port);
530#endif
4e90a9e0 531
be511d00
VB
532#ifdef ENABLE_LLDPMED
533 if (!oport && port->p_chassis->c_med_type) {
b9de0ca6 534 /* New neighbor, fast start */
535 if (hardware->h_cfg->g_config.c_enable_fast_start &&
be511d00
VB
536 !hardware->h_tx_fast) {
537 log_debug("decode", "%s: entering fast start due to "
538 "new neighbor", hardware->h_ifname);
b9de0ca6 539 hardware->h_tx_fast = hardware->h_cfg->g_config.c_tx_fast_init;
540 }
541
542 levent_schedule_pdu(hardware);
543 }
be511d00 544#endif
b9de0ca6 545
43c02e7b
VB
546 return;
547}
548
c036b15d
VB
549/* Get the output of lsb_release -s -d. This is a slow function. It should be
550 called once. It return NULL if any problem happens. Otherwise, this is a
551 statically allocated buffer. The result includes the trailing \n */
552static char *
553lldpd_get_lsb_release() {
554 static char release[1024];
555 char *const command[] = { "lsb_release", "-s", "-d", NULL };
556 int pid, status, devnull, count;
557 int pipefd[2];
558
6f8925be
VB
559 log_debug("localchassis", "grab LSB release");
560
c036b15d 561 if (pipe(pipefd)) {
6f8925be 562 log_warn("localchassis", "unable to get a pair of pipes");
c036b15d
VB
563 return NULL;
564 }
565
566 if ((pid = fork()) < 0) {
6f8925be 567 log_warn("localchassis", "unable to fork");
c036b15d
VB
568 return NULL;
569 }
570 switch (pid) {
571 case 0:
572 /* Child, exec lsb_release */
573 close(pipefd[0]);
574 if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
575 dup2(devnull, STDIN_FILENO);
576 dup2(devnull, STDERR_FILENO);
577 dup2(pipefd[1], STDOUT_FILENO);
578 if (devnull > 2) close(devnull);
579 if (pipefd[1] > 2) close(pipefd[1]);
580 execvp("lsb_release", command);
581 }
582 exit(127);
583 break;
584 default:
585 /* Father, read the output from the children */
586 close(pipefd[1]);
587 count = 0;
588 do {
589 status = read(pipefd[0], release+count, sizeof(release)-count);
590 if ((status == -1) && (errno == EINTR)) continue;
591 if (status > 0)
592 count += status;
593 } while (count < sizeof(release) && (status > 0));
594 if (status < 0) {
6f8925be 595 log_info("localchassis", "unable to read from lsb_release");
c036b15d
VB
596 close(pipefd[0]);
597 waitpid(pid, &status, 0);
598 return NULL;
599 }
600 close(pipefd[0]);
601 if (count >= sizeof(release)) {
6f8925be 602 log_info("localchassis", "output of lsb_release is too large");
c036b15d
VB
603 waitpid(pid, &status, 0);
604 return NULL;
605 }
606 status = -1;
607 if (waitpid(pid, &status, 0) != pid)
608 return NULL;
609 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
6f8925be 610 log_info("localchassis", "lsb_release information not available");
c036b15d
VB
611 return NULL;
612 }
613 if (!count) {
6f8925be 614 log_info("localchassis", "lsb_release returned an empty string");
c036b15d
VB
615 return NULL;
616 }
617 release[count] = '\0';
618 return release;
619 }
620 /* Should not be here */
621 return NULL;
622}
623
ae87586a
MT
624/* Same like lldpd_get_lsb_release but reads /etc/os-release for PRETTY_NAME=. */
625static char *
626lldpd_get_os_release() {
627 static char release[1024];
9757bfbc
SK
628 char line[1024];
629 char *key, *val;
630 char *ptr1 = release;
ae87586a
MT
631
632 FILE *fp = fopen("/etc/os-release", "r");
6f8925be 633 log_debug("localchassis", "grab OS release");
ae87586a 634 if (!fp) {
6f8925be 635 log_info("localchassis", "could not open /etc/os-release");
ae87586a
MT
636 return NULL;
637 }
638
ae87586a
MT
639 while ((fgets(line, 1024, fp) != NULL)) {
640 key = strtok(line, "=");
641 val = strtok(NULL, "=");
642
643 if (strncmp(key, "PRETTY_NAME", 1024) == 0) {
644 strncpy(release, val, 1024);
645 break;
646 }
647 }
648 fclose(fp);
649
650 /* Remove trailing newline and all " in the string. */
dc21d049
VB
651 ptr1 = release + strlen(release) - 1;
652 while (ptr1 != release &&
653 ((*ptr1 == '"') || (*ptr1 == '\n'))) {
654 *ptr1 = '\0';
655 ptr1--;
ae87586a 656 }
dc21d049
VB
657 if (release[0] == '"')
658 return release+1;
ae87586a
MT
659 return release;
660}
661
8482abe9
VB
662static void
663lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) {
42b39485
VB
664 struct lldpd_port *port;
665 int protocols[LLDPD_MODE_MAX+1];
8482abe9
VB
666 char buffer[256];
667 int i, j, k, found;
42b39485
VB
668 unsigned int min;
669
6f8925be
VB
670 log_debug("smartfilter", "apply smart filter for port %s",
671 hardware->h_ifname);
672
8482abe9
VB
673 /* Compute the number of occurrences of each protocol */
674 for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
675 TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
676 protocols[port->p_protocol]++;
677
678 /* Turn the protocols[] array into an array of
679 enabled/disabled protocols. 1 means enabled, 0
680 means disabled. */
681 min = (unsigned int)-1;
682 for (i = 0; i <= LLDPD_MODE_MAX; i++)
683 if (protocols[i] && (protocols[i] < min))
684 min = protocols[i];
685 found = 0;
686 for (i = 0; i <= LLDPD_MODE_MAX; i++)
687 if ((protocols[i] == min) && !found) {
688 /* If we need a tie breaker, we take
689 the first protocol only */
8ec333bd 690 if (cfg->g_config.c_smart & mask &
8482abe9
VB
691 (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO))
692 found = 1;
693 protocols[i] = 1;
694 } else protocols[i] = 0;
695
696 /* We set the p_hidden flag to 1 if the protocol is disabled */
697 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
698 if (mask == SMART_OUTGOING)
699 port->p_hidden_out = protocols[port->p_protocol]?0:1;
700 else
701 port->p_hidden_in = protocols[port->p_protocol]?0:1;
702 }
703
704 /* If we want only one neighbor, we take the first one */
8ec333bd 705 if (cfg->g_config.c_smart & mask &
8482abe9 706 (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
42b39485 707 found = 0;
8482abe9
VB
708 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
709 if (mask == SMART_OUTGOING) {
710 if (found) port->p_hidden_out = 1;
711 if (!port->p_hidden_out)
712 found = 1;
713 }
714 if (mask == SMART_INCOMING) {
715 if (found) port->p_hidden_in = 1;
716 if (!port->p_hidden_in)
42b39485 717 found = 1;
42b39485
VB
718 }
719 }
8482abe9 720 }
42b39485 721
8482abe9
VB
722 /* Print a debug message summarizing the operation */
723 for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
724 k = j = 0;
725 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
726 if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) ||
727 ((mask == SMART_INCOMING) && port->p_hidden_in))) {
728 k++;
729 protocols[port->p_protocol] = 1;
42b39485 730 }
8482abe9
VB
731 j++;
732 }
733 buffer[0] = '\0';
734 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
735 if (cfg->g_protocols[i].enabled && protocols[cfg->g_protocols[i].mode]) {
736 if (strlen(buffer) +
737 strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
738 /* Unlikely, our buffer is too small */
739 memcpy(buffer + sizeof(buffer) - 4, "...", 4);
740 break;
42b39485 741 }
8482abe9 742 if (buffer[0])
1dfac346
VB
743 strncat(buffer, ", ", 2);
744 strncat(buffer, cfg->g_protocols[i].name, strlen(cfg->g_protocols[i].name));
42b39485
VB
745 }
746 }
6f8925be
VB
747 log_debug("smartfilter", "%s: %s: %d visible neighbors (out of %d)",
748 hardware->h_ifname,
749 (mask == SMART_OUTGOING)?"out filter":"in filter",
750 k, j);
751 log_debug("smartfilter", "%s: protocols: %s",
752 hardware->h_ifname, buffer[0]?buffer:"(none)");
42b39485
VB
753}
754
566c635d
VB
755/* Hide unwanted ports depending on smart mode set by the user */
756static void
757lldpd_hide_all(struct lldpd *cfg)
758{
759 struct lldpd_hardware *hardware;
760
8ec333bd 761 if (!cfg->g_config.c_smart)
566c635d 762 return;
6f8925be 763 log_debug("smartfilter", "apply smart filter results on all ports");
566c635d 764 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
8ec333bd 765 if (cfg->g_config.c_smart & SMART_INCOMING_FILTER)
566c635d 766 lldpd_hide_ports(cfg, hardware, SMART_INCOMING);
8ec333bd 767 if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER)
566c635d
VB
768 lldpd_hide_ports(cfg, hardware, SMART_OUTGOING);
769 }
770}
771
d6e889b6
VB
772void
773lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd)
43c02e7b 774{
d6e889b6
VB
775 char *buffer = NULL;
776 int n;
6f8925be
VB
777 log_debug("receive", "receive a frame on %s",
778 hardware->h_ifname);
d6e889b6 779 if ((buffer = (char *)malloc(hardware->h_mtu)) == NULL) {
6f8925be 780 log_warn("receive", "failed to alloc reception buffer");
d6e889b6
VB
781 return;
782 }
783 if ((n = hardware->h_ops->recv(cfg, hardware,
784 fd, buffer,
785 hardware->h_mtu)) == -1) {
3e2e693c 786 log_debug("receive", "discard frame received on %s",
6f8925be 787 hardware->h_ifname);
d6e889b6
VB
788 free(buffer);
789 return;
790 }
e4ff3ed5
VB
791 if (cfg->g_config.c_paused) {
792 log_debug("receive", "paused, ignore the frame on %s",
793 hardware->h_ifname);
794 free(buffer);
795 return;
796 }
d6e889b6 797 hardware->h_rx_cnt++;
6f8925be
VB
798 log_debug("receive", "decode received frame on %s",
799 hardware->h_ifname);
d6e889b6
VB
800 lldpd_decode(cfg, buffer, n, hardware);
801 lldpd_hide_all(cfg); /* Immediatly hide */
802 free(buffer);
43c02e7b
VB
803}
804
579bedd5
VB
805void
806lldpd_send(struct lldpd_hardware *hardware)
43c02e7b 807{
579bedd5 808 struct lldpd *cfg = hardware->h_cfg;
77507b69 809 struct lldpd_port *port;
0d86e62f 810 int i, sent;
f7db0dd8 811
e4ff3ed5 812 if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return;
579bedd5
VB
813 if ((hardware->h_flags & IFF_RUNNING) == 0)
814 return;
6f8925be 815
579bedd5
VB
816 log_debug("send", "send PDU on %s", hardware->h_ifname);
817 sent = 0;
818 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
819 if (!cfg->g_protocols[i].enabled)
820 continue;
821 /* We send only if we have at least one remote system
822 * speaking this protocol or if the protocol is forced */
823 if (cfg->g_protocols[i].enabled > 1) {
824 cfg->g_protocols[i].send(cfg, hardware);
825 sent++;
43c02e7b 826 continue;
43c02e7b 827 }
579bedd5
VB
828 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
829 /* If this remote port is disabled, we don't
830 * consider it */
831 if (port->p_hidden_out)
832 continue;
833 if (port->p_protocol ==
834 cfg->g_protocols[i].mode) {
835 log_debug("send", "send PDU on %s with protocol %s",
836 hardware->h_ifname,
837 cfg->g_protocols[i].name);
46baf627
VB
838 cfg->g_protocols[i].send(cfg,
839 hardware);
579bedd5 840 sent++;
46baf627
VB
841 break;
842 }
46baf627 843 }
43c02e7b 844 }
579bedd5
VB
845
846 if (!sent) {
847 /* Nothing was sent for this port, let's speak the first
848 * available protocol. */
849 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
850 if (!cfg->g_protocols[i].enabled) continue;
851 log_debug("send", "fallback to protocol %s for %s",
852 cfg->g_protocols[i].name, hardware->h_ifname);
853 cfg->g_protocols[i].send(cfg,
854 hardware);
855 break;
856 }
857 if (cfg->g_protocols[i].mode == 0)
858 log_warnx("send", "no protocol enabled, dunno what to send");
859 }
43c02e7b
VB
860}
861
89840df0 862#ifdef ENABLE_LLDPMED
8888d191 863static void
89840df0
VB
864lldpd_med(struct lldpd_chassis *chassis)
865{
55606d4b
VB
866 static short int once = 0;
867 if (!once) {
868 chassis->c_med_hw = dmi_hw();
869 chassis->c_med_fw = dmi_fw();
870 chassis->c_med_sn = dmi_sn();
871 chassis->c_med_manuf = dmi_manuf();
872 chassis->c_med_model = dmi_model();
873 chassis->c_med_asset = dmi_asset();
874 once = 1;
875 }
89840df0
VB
876}
877#endif
878
e66b7f34
VB
879static int
880lldpd_forwarding_enabled(void)
881{
882 int rc = 0;
883#if defined HOST_OS_LINUX
884 int f;
885 char status;
886 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
887 if ((read(f, &status, 1) == 1) && (status == '1'))
888 rc = 1;
889 close(f);
890 }
1b3eea3d 891#elif defined HOST_OS_FREEBSD || defined HOST_OS_OPENBSD || defined HOST_OS_NETBSD || defined HOST_OS_OSX || defined HOST_OS_DRAGONFLY
e66b7f34
VB
892 int n, mib[4] = {
893 CTL_NET,
894 PF_INET,
895 IPPROTO_IP,
896 IPCTL_FORWARDING
897 };
898 size_t len = sizeof(int);
899 if (sysctl(mib, 4, &n, &len, NULL, 0) != -1)
900 rc = (n == 1);
901#else
902#error Unsupported OS
903#endif
904 else log_debug("localchassis", "unable to check if forwarding is enabled");
905 return rc;
906}
907
8888d191 908static void
6e75df87 909lldpd_update_localchassis(struct lldpd *cfg)
43c02e7b 910{
6e75df87
VB
911 struct utsname un;
912 char *hp;
43c02e7b 913
6f8925be 914 log_debug("localchassis", "update information for local chassis");
aa015c26 915 assert(LOCAL_CHASSIS(cfg) != NULL);
6f8925be 916
43c02e7b 917 /* Set system name and description */
6e75df87 918 if (uname(&un) != 0)
6f8925be 919 fatal("localchassis", "failed to get system information");
b5562b23 920 if ((hp = priv_gethostbyname()) == NULL)
6f8925be 921 fatal("localchassis", "failed to get system name");
77507b69
VB
922 free(LOCAL_CHASSIS(cfg)->c_name);
923 free(LOCAL_CHASSIS(cfg)->c_descr);
924 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
6f8925be 925 fatal("localchassis", NULL);
8ec333bd 926 if (cfg->g_config.c_description) {
80c974a8 927 log_debug("localchassis", "use overridden description `%s`", cfg->g_config.c_description);
40ce835b 928 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
8ec333bd 929 cfg->g_config.c_description) == -1)
6f8925be 930 fatal("localchassis", "failed to set full system description");
40ce835b 931 } else {
8ec333bd 932 if (cfg->g_config.c_advertise_version) {
6f8925be 933 log_debug("localchassis", "advertise system version");
a9db9b44 934 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s %s",
c036b15d 935 cfg->g_lsb_release?cfg->g_lsb_release:"",
a9db9b44 936 un.sysname, un.release, un.version, un.machine)
40ce835b 937 == -1)
6f8925be 938 fatal("localchassis", "failed to set full system description");
40ce835b 939 } else {
6f8925be 940 log_debug("localchassis", "do not advertise system version");
40ce835b 941 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
c036b15d 942 cfg->g_lsb_release?cfg->g_lsb_release:un.sysname) == -1)
6f8925be 943 fatal("localchassis", "failed to set minimal system description");
40ce835b
ST
944 }
945 }
43c02e7b
VB
946
947 /* Check forwarding */
e66b7f34
VB
948 if (lldpd_forwarding_enabled()) {
949 log_debug("localchassis", "forwarding is enabled, enable router capability");
950 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_ROUTER;
951 } else
952 LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_ROUTER;
242845c7 953
89840df0 954#ifdef ENABLE_LLDPMED
77507b69
VB
955 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
956 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
957 lldpd_med(LOCAL_CHASSIS(cfg));
958 free(LOCAL_CHASSIS(cfg)->c_med_sw);
8ec333bd 959 if (cfg->g_config.c_advertise_version)
de1b1b3a
VB
960 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
961 else
962 LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
89840df0 963#endif
43c02e7b 964
b4ac8083
VB
965 /* Set chassis ID if needed. This is only done if chassis ID
966 has not been set previously (with the MAC address of an
967 interface for example)
968 */
969 if (LOCAL_CHASSIS(cfg)->c_id == NULL) {
6f8925be 970 log_debug("localchassis", "no chassis ID is currently set, use chassis name");
b4ac8083 971 if (!(LOCAL_CHASSIS(cfg)->c_id = strdup(LOCAL_CHASSIS(cfg)->c_name)))
6f8925be 972 fatal("localchassis", NULL);
b4ac8083
VB
973 LOCAL_CHASSIS(cfg)->c_id_len = strlen(LOCAL_CHASSIS(cfg)->c_name);
974 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
6e75df87
VB
975 }
976}
977
0484f180 978void
6e75df87
VB
979lldpd_update_localports(struct lldpd *cfg)
980{
6e75df87 981 struct lldpd_hardware *hardware;
6e75df87 982
9e5d99d4 983 log_debug("localchassis", "update information for local ports");
6f8925be 984
6e75df87
VB
985 /* h_flags is set to 0 for each port. If the port is updated, h_flags
986 * will be set to a non-zero value. This will allow us to clean up any
987 * non up-to-date port */
43c02e7b
VB
988 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
989 hardware->h_flags = 0;
990
e12c2365 991 interfaces_update(cfg);
bfdc2a8c 992 lldpd_cleanup(cfg);
579bedd5 993 lldpd_reset_timer(cfg);
6e75df87 994}
43c02e7b 995
d6e889b6 996void
6e75df87
VB
997lldpd_loop(struct lldpd *cfg)
998{
999 /* Main loop.
6e75df87 1000 1. Update local ports information
579bedd5 1001 2. Update local chassis information
6e75df87 1002 */
6f8925be 1003 log_debug("loop", "start new loop");
849954d7 1004 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
579bedd5
VB
1005 /* Information for local ports is triggered even when it is possible to
1006 * update them on some other event because we want to refresh them if we
1007 * missed something. */
1008 log_debug("loop", "update information for local ports");
1009 lldpd_update_localports(cfg);
6f8925be 1010 log_debug("loop", "update information for local chassis");
6e75df87 1011 lldpd_update_localchassis(cfg);
43c02e7b
VB
1012}
1013
8888d191 1014static void
d6e889b6 1015lldpd_exit(struct lldpd *cfg)
43c02e7b 1016{
6e75df87 1017 struct lldpd_hardware *hardware, *hardware_next;
9e5d99d4 1018 log_debug("main", "exit lldpd");
d6e889b6 1019 close(cfg->g_ctl);
0262adbb 1020 priv_ctl_cleanup(cfg->g_ctlname);
9e5d99d4 1021 log_debug("main", "cleanup hardware information");
d6e889b6 1022 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
6e75df87
VB
1023 hardware = hardware_next) {
1024 hardware_next = TAILQ_NEXT(hardware, h_entries);
9e5d99d4 1025 log_debug("main", "cleanup interface %s", hardware->h_ifname);
4e90a9e0 1026 lldpd_remote_cleanup(hardware, NULL);
d6e889b6 1027 lldpd_hardware_cleanup(cfg, hardware);
43c02e7b 1028 }
43c02e7b
VB
1029}
1030
51534ef3
VB
1031/**
1032 * Run lldpcli to configure lldpd.
1033 *
1034 * @return PID of running lldpcli or -1 if error.
1035 */
1036static pid_t
1037lldpd_configure(int debug, const char *path)
1038{
1039 pid_t lldpcli = fork();
1040 int devnull;
1041
1042 switch (lldpcli) {
1043 case -1:
1044 log_warn("main", "unable to fork");
1045 return -1;
1046 case 0:
1047 /* Child, exec lldpcli */
1048 if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
1049 char sdebug[debug + 3];
1050 memset(sdebug, 'd', debug + 3);
1051 sdebug[debug + 2] = '\0';
1052 sdebug[0] = '-'; sdebug[1] = 's';
1053
1054 dup2(devnull, STDIN_FILENO);
eb78d6e3 1055 dup2(devnull, STDOUT_FILENO);
51534ef3
VB
1056 if (devnull > 2) close(devnull);
1057
1058 log_debug("main", "invoke %s %s", path, sdebug);
1059 if (execl(path, "lldpcli", sdebug,
1060 "-c", SYSCONFDIR "/lldpd.conf",
1061 "-c", SYSCONFDIR "/lldpd.d",
e4ff3ed5 1062 "resume",
51534ef3
VB
1063 NULL) == -1) {
1064 log_warn("main", "unable to execute %s", path);
ba529c82 1065 log_warnx("main", "configuration is incomplete, lldpd needs to be unpaused");
51534ef3
VB
1066 }
1067 }
1068 exit(127);
1069 break;
1070 default:
1071 /* Father, don't do anything stupid */
1072 return lldpcli;
1073 }
1074 /* Should not be here */
1075 return -1;
1076}
1077
8482abe9
VB
1078struct intint { int a; int b; };
1079static const struct intint filters[] = {
1080 { 0, 0 },
1081 { 1, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1082 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1083 { 2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO },
1084 { 3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1085 { 4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER },
1086 { 5, SMART_INCOMING_FILTER },
1087 { 6, SMART_OUTGOING_FILTER },
1088 { 7, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1089 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1090 { 8, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH },
1091 { 9, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1092 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1093 { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1094 { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH },
1095 { 12, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1096 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1097 { 13, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1098 SMART_OUTGOING_FILTER },
1099 { 14, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1100 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1101 { 15, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1102 SMART_OUTGOING_FILTER },
1103 { 16, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1104 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1105 { 17, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1106 SMART_OUTGOING_FILTER },
1107 { 18, SMART_INCOMING_FILTER |
1108 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1109 { 19, SMART_INCOMING_FILTER |
1110 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1111 { -1, 0 }
1112};
1113
e66b7f34 1114#ifndef HOST_OS_OSX
2591639f
VB
1115/**
1116 * Tell if we have been started by upstart.
1117 */
1118static int
1119lldpd_started_by_upstart()
1120{
322aafc9 1121#ifdef HOST_OS_LINUX
2591639f
VB
1122 const char *upstartjob = getenv("UPSTART_JOB");
1123 if (!(upstartjob && !strcmp(upstartjob, "lldpd")))
1124 return 0;
1125 log_debug("main", "running with upstart, don't fork but stop");
1126 raise(SIGSTOP);
1127 return 1;
322aafc9
VB
1128#else
1129 return 0;
1130#endif
1131}
1132
1133/**
1134 * Tell if we have been started by systemd.
1135 */
1136static int
1137lldpd_started_by_systemd()
1138{
1139#ifdef HOST_OS_LINUX
1140 int fd = -1;
1141 const char *notifysocket = getenv("NOTIFY_SOCKET");
1142 if (!notifysocket ||
1143 !strchr("@/", notifysocket[0]) ||
1144 strlen(notifysocket) < 2)
1145 return 0;
1146
1147 log_debug("main", "running with systemd, don't fork but signal ready");
ceb37922 1148 if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
322aafc9
VB
1149 log_warn("main", "unable to open systemd notification socket %s",
1150 notifysocket);
1151 return 0;
1152 }
1153
1154 struct sockaddr_un su = { .sun_family = AF_UNIX };
1155 strlcpy(su.sun_path, notifysocket, sizeof(su.sun_path));
1156 if (notifysocket[0] == '@') su.sun_path[0] = 0;
1157
1158 struct iovec iov = {
1159 .iov_base = "READY=1",
506273e9 1160 .iov_len = strlen("READY=1")
322aafc9
VB
1161 };
1162 struct msghdr hdr = {
1163 .msg_name = &su,
1164 .msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(notifysocket),
1165 .msg_iov = &iov,
1166 .msg_iovlen = 1
1167 };
1168 if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) {
1169 log_warn("main", "unable to send notification to systemd");
1170 close(fd);
1171 return 0;
1172 }
1173 close(fd);
1174 return 1;
1175#else
1176 return 0;
1177#endif
2591639f 1178}
e66b7f34 1179#endif
2591639f 1180
43c02e7b 1181int
2acc1418 1182lldpd_main(int argc, char *argv[])
43c02e7b
VB
1183{
1184 struct lldpd *cfg;
77507b69 1185 struct lldpd_chassis *lchassis;
e809a587
VB
1186 int ch, debug = 0;
1187#ifdef USE_SNMP
1188 int snmp = 0;
bbea66e1 1189 char *agentx = NULL; /* AgentX socket */
e809a587 1190#endif
0262adbb 1191 char *ctlname = LLDPD_CTL_SOCKET;
d4e4c804 1192 char *mgmtp = NULL;
5339e725 1193 char *cidp = NULL;
ba85f9f4 1194 char *interfaces = NULL;
51534ef3 1195 char *popt, opts[] =
0262adbb 1196 "H:vhkrdD:xX:m:u:4:6:I:C:p:M:P:S:iL:@ ";
de1b1b3a 1197 int i, found, advertise_version = 1;
89840df0 1198#ifdef ENABLE_LLDPMED
e809a587 1199 int lldpmed = 0, noinventory = 0;
be511d00 1200 int enable_fast_start = 0;
89840df0 1201#endif
d4afb919
VB
1202 char *descr_override = NULL;
1203 char *platform_override = NULL;
c036b15d 1204 char *lsb_release = NULL;
51534ef3 1205 const char *lldpcli = LLDPCLI_PATH;
8482abe9 1206 int smart = 15;
537a8043 1207 int receiveonly = 0;
2e91d1a1
VB
1208 int ctl;
1209
1210 /* Non privileged user */
1211 struct passwd *user;
1212 struct group *group;
1213 uid_t uid;
1214 gid_t gid;
43c02e7b
VB
1215
1216 saved_argv = argv;
1217
1218 /*
1219 * Get and parse command line options
1220 */
f0bd3505 1221 popt = strchr(opts, '@');
0c877af0 1222 for (i=0; protos[i].mode != 0; i++)
43c02e7b 1223 *(popt++) = protos[i].arg;
43c02e7b
VB
1224 *popt = '\0';
1225 while ((ch = getopt(argc, argv, opts)) != -1) {
1226 switch (ch) {
b162b740
VB
1227 case 'h':
1228 usage();
1229 break;
96e49a40
VB
1230 case 'v':
1231 fprintf(stdout, "%s\n", PACKAGE_VERSION);
1232 exit(0);
1233 break;
43c02e7b
VB
1234 case 'd':
1235 debug++;
1236 break;
9e5d99d4
VB
1237 case 'D':
1238 log_accept(optarg);
1239 break;
537a8043
VB
1240 case 'r':
1241 receiveonly = 1;
1242 break;
d4e4c804 1243 case 'm':
43c02e7b
VB
1244 mgmtp = optarg;
1245 break;
0262adbb
ZM
1246 case 'u':
1247 ctlname = optarg;
1248 break;
ba85f9f4
VB
1249 case 'I':
1250 interfaces = optarg;
1251 break;
5339e725
VB
1252 case 'C':
1253 cidp = optarg;
1254 break;
51534ef3
VB
1255 case 'L':
1256 if (strlen(optarg)) lldpcli = optarg;
1257 else lldpcli = NULL;
de1b1b3a
VB
1258 case 'k':
1259 advertise_version = 0;
1260 break;
e809a587 1261#ifdef ENABLE_LLDPMED
115ff55c 1262 case 'M':
89840df0 1263 lldpmed = atoi(optarg);
e809a587
VB
1264 if ((lldpmed < 1) || (lldpmed > 4)) {
1265 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 1266 usage();
e809a587 1267 }
89840df0 1268 break;
e809a587 1269 case 'i':
e809a587 1270 noinventory = 1;
115ff55c 1271 break;
e809a587 1272#else
115ff55c
VB
1273 case 'M':
1274 case 'i':
e809a587
VB
1275 fprintf(stderr, "LLDP-MED support is not built-in\n");
1276 usage();
e809a587 1277 break;
115ff55c 1278#endif
e809a587 1279#ifdef USE_SNMP
bbea66e1
V
1280 case 'x':
1281 snmp = 1;
1282 break;
1283 case 'X':
43c02e7b 1284 snmp = 1;
bbea66e1
V
1285 agentx = optarg;
1286 break;
e809a587 1287#else
bbea66e1
V
1288 case 'x':
1289 case 'X':
e809a587
VB
1290 fprintf(stderr, "SNMP support is not built-in\n");
1291 usage();
1292#endif
43c02e7b 1293 break;
40ce835b
ST
1294 case 'S':
1295 descr_override = strdup(optarg);
1296 break;
d4afb919
VB
1297 case 'P':
1298 platform_override = strdup(optarg);
1299 break;
42b39485 1300 case 'H':
8482abe9 1301 smart = atoi(optarg);
42b39485 1302 break;
43c02e7b
VB
1303 default:
1304 found = 0;
1305 for (i=0; protos[i].mode != 0; i++) {
43c02e7b 1306 if (ch == protos[i].arg) {
43d10956
VB
1307 if (protos[i].enabled < 3) {
1308 found = 1;
1309 if (protos[i].enabled++ == 1)
1310 break;
1311 }
43c02e7b
VB
1312 }
1313 }
1314 if (!found)
1315 usage();
1316 }
1317 }
8482abe9
VB
1318
1319 /* Set correct smart mode */
1320 for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
1321 if (filters[i].a == -1) {
1322 fprintf(stderr, "Incorrect mode for -H\n");
1323 usage();
1324 }
1325 smart = filters[i].b;
6f8925be 1326
6bb9c4e0 1327 log_init(debug, __progname);
4b9a5a23 1328 tzset(); /* Get timezone info before chroot */
a2993d83 1329
6f8925be
VB
1330 log_debug("main", "lldpd starting...");
1331
2e91d1a1
VB
1332 /* Grab uid and gid to use for priv sep */
1333 if ((user = getpwnam(PRIVSEP_USER)) == NULL)
6f8925be 1334 fatal("main", "no " PRIVSEP_USER " user for privilege separation");
2e91d1a1
VB
1335 uid = user->pw_uid;
1336 if ((group = getgrnam(PRIVSEP_GROUP)) == NULL)
6f8925be 1337 fatal("main", "no " PRIVSEP_GROUP " group for privilege separation");
2e91d1a1
VB
1338 gid = group->gr_gid;
1339
1340 /* Create and setup socket */
8172214f 1341 int retry = 1;
6f8925be 1342 log_debug("main", "creating control socket");
0262adbb 1343 while ((ctl = ctl_create(ctlname)) == -1) {
8172214f
VB
1344 if (retry-- && errno == EADDRINUSE) {
1345 /* Check if a daemon is really listening */
1346 int tfd;
1347 log_info("main", "unable to create control socket because it already exists");
1348 log_info("main", "check if another instance is running");
0262adbb 1349 if ((tfd = ctl_connect(ctlname)) != -1) {
8172214f
VB
1350 /* Another instance is running */
1351 close(tfd);
1352 log_warnx("main", "another instance is running, please stop it");
1353 fatalx("giving up");
1354 } else if (errno == ECONNREFUSED) {
1355 /* Nobody is listening */
1356 log_info("main", "old control socket is present, clean it");
0262adbb 1357 ctl_cleanup(ctlname);
8172214f
VB
1358 continue;
1359 }
1360 log_warn("main", "cannot determine if another daemon is already running");
1361 fatalx("giving up");
1362 }
1363 log_warn("main", "unable to create control socket");
6f8925be 1364 fatalx("giving up");
2e91d1a1 1365 }
0262adbb 1366 if (chown(ctlname, uid, gid) == -1)
6f8925be 1367 log_warn("main", "unable to chown control socket");
0262adbb 1368 if (chmod(ctlname,
2e91d1a1
VB
1369 S_IRUSR | S_IWUSR | S_IXUSR |
1370 S_IRGRP | S_IWGRP | S_IXGRP) == -1)
6f8925be 1371 log_warn("main", "unable to chmod control socket");
2e91d1a1 1372
595184b0
VB
1373 /* Disable SIGPIPE */
1374 signal(SIGPIPE, SIG_IGN);
1375
51534ef3
VB
1376 /* Configuration with lldpcli */
1377 if (lldpcli) {
1378 log_debug("main", "invoking lldpcli for configuration");
1379 if (lldpd_configure(debug, lldpcli) == -1)
1380 fatal("main", "unable to spawn lldpcli");
1381 }
1382
e66b7f34
VB
1383 /* Daemonization, unless started by upstart, systemd or launchd or debug */
1384#ifndef HOST_OS_OSX
322aafc9
VB
1385 if (!lldpd_started_by_upstart() && !lldpd_started_by_systemd() &&
1386 !debug) {
eac2f38a
VB
1387 int pid;
1388 char *spid;
6f8925be 1389 log_debug("main", "daemonize");
eac2f38a 1390 if (daemon(0, 0) != 0)
6f8925be 1391 fatal("main", "failed to detach daemon");
eac2f38a 1392 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 1393 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
6f8925be 1394 fatal("main", "unable to open pid file " LLDPD_PID_FILE);
eac2f38a 1395 if (asprintf(&spid, "%d\n", getpid()) == -1)
6f8925be 1396 fatal("main", "unable to create pid file " LLDPD_PID_FILE);
eac2f38a 1397 if (write(pid, spid, strlen(spid)) == -1)
6f8925be 1398 fatal("main", "unable to write pid file " LLDPD_PID_FILE);
eac2f38a
VB
1399 free(spid);
1400 close(pid);
1401 }
e66b7f34 1402#endif
eac2f38a 1403
ae87586a
MT
1404 /* Try to read system information from /etc/os-release if possible.
1405 Fall back to lsb_release for compatibility. */
6f8925be 1406 log_debug("main", "get OS/LSB release information");
ae87586a
MT
1407 lsb_release = lldpd_get_os_release();
1408 if (!lsb_release) {
1409 lsb_release = lldpd_get_lsb_release();
1410 }
c036b15d 1411
6f8925be 1412 log_debug("main", "initialize privilege separation");
2e91d1a1 1413 priv_init(PRIVSEP_CHROOT, ctl, uid, gid);
43c02e7b 1414
d6e889b6 1415 /* Initialization of global configuration */
43c02e7b
VB
1416 if ((cfg = (struct lldpd *)
1417 calloc(1, sizeof(struct lldpd))) == NULL)
6f8925be 1418 fatal("main", NULL);
43c02e7b 1419
0262adbb 1420 cfg->g_ctlname = ctlname;
2e91d1a1 1421 cfg->g_ctl = ctl;
8ec333bd
VB
1422 cfg->g_config.c_mgmt_pattern = mgmtp;
1423 cfg->g_config.c_cid_pattern = cidp;
1424 cfg->g_config.c_iface_pattern = interfaces;
1425 cfg->g_config.c_smart = smart;
ba529c82
VB
1426 if (lldpcli)
1427 cfg->g_config.c_paused = 1;
8ec333bd 1428 cfg->g_config.c_receiveonly = receiveonly;
e4ff3ed5 1429 cfg->g_config.c_tx_interval = LLDPD_TX_INTERVAL;
42589660 1430 cfg->g_config.c_max_neighbors = LLDPD_MAX_NEIGHBORS;
b9de0ca6 1431 cfg->g_config.c_enable_fast_start = enable_fast_start;
be511d00 1432#ifdef ENABLE_LLDPMED
b9de0ca6 1433 cfg->g_config.c_tx_fast_init = LLDPD_FAST_INIT;
1434 cfg->g_config.c_tx_fast_interval = LLDPD_FAST_TX_INTERVAL;
be511d00 1435#endif
d6e889b6
VB
1436#ifdef USE_SNMP
1437 cfg->g_snmp = snmp;
1438 cfg->g_snmp_agentx = agentx;
1439#endif /* USE_SNMP */
43c02e7b
VB
1440
1441 /* Get ioctl socket */
6f8925be 1442 log_debug("main", "get an ioctl socket");
43c02e7b 1443 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
6f8925be 1444 fatal("main", "failed to get ioctl socket");
43c02e7b 1445
c036b15d 1446 /* Description */
dc21d049
VB
1447 if (!(cfg->g_config.c_advertise_version = advertise_version) &&
1448 lsb_release && lsb_release[strlen(lsb_release) - 1] == '\n')
c036b15d
VB
1449 lsb_release[strlen(lsb_release) - 1] = '\0';
1450 cfg->g_lsb_release = lsb_release;
40ce835b 1451 if (descr_override)
8ec333bd 1452 cfg->g_config.c_description = descr_override;
40ce835b 1453
d4afb919 1454 if (platform_override)
8ec333bd 1455 cfg->g_config.c_platform = platform_override;
d4afb919 1456
43c02e7b 1457 /* Set system capabilities */
6f8925be 1458 log_debug("main", "set system capabilities");
77507b69
VB
1459 if ((lchassis = (struct lldpd_chassis*)
1460 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
6f8925be 1461 fatal("localchassis", NULL);
77507b69 1462 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 1463 LLDP_CAP_ROUTER;
e6b36c87 1464 TAILQ_INIT(&lchassis->c_mgmt);
89840df0
VB
1465#ifdef ENABLE_LLDPMED
1466 if (lldpmed > 0) {
4b292b55 1467 if (lldpmed == LLDP_MED_CLASS_III)
77507b69
VB
1468 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
1469 lchassis->c_med_type = lldpmed;
4b292b55
VB
1470 lchassis->c_med_cap_available = LLDP_MED_CAP_CAP |
1471 LLDP_MED_CAP_IV | LLDP_MED_CAP_LOCATION |
1472 LLDP_MED_CAP_POLICY | LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_MDI_PD;
8ec333bd 1473 cfg->g_config.c_noinventory = noinventory;
42bddd41 1474 } else
8ec333bd 1475 cfg->g_config.c_noinventory = 1;
89840df0 1476#endif
43c02e7b
VB
1477
1478 /* Set TTL */
77507b69 1479 lchassis->c_ttl = LLDPD_TTL;
43c02e7b 1480
6f8925be 1481 log_debug("main", "initialize protocols");
43c02e7b 1482 cfg->g_protocols = protos;
43c02e7b 1483 for (i=0; protos[i].mode != 0; i++)
0c877af0 1484 if (protos[i].enabled > 1)
6f8925be 1485 log_info("main", "protocol %s enabled and forced", protos[i].name);
0c877af0 1486 else if (protos[i].enabled)
6f8925be 1487 log_info("main", "protocol %s enabled", protos[i].name);
0c877af0 1488 else
6f8925be 1489 log_info("main", "protocol %s disabled", protos[i].name);
43c02e7b
VB
1490
1491 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
1492 TAILQ_INIT(&cfg->g_chassis);
1493 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
9898ac07 1494 lchassis->c_refcount++; /* We should always keep a reference to local chassis */
43c02e7b 1495
d6e889b6 1496 /* Main loop */
6f8925be 1497 log_debug("main", "start main loop");
d6e889b6
VB
1498 levent_loop(cfg);
1499 lldpd_exit(cfg);
43c02e7b
VB
1500
1501 return (0);
1502}