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