]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/lldpd.c
build: bump cross-platform-actions/action from 0.27.0 to 0.28.0
[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"
bdfe4193 19#include "trace.h"
43c02e7b
VB
20
21#include <stdio.h>
22#include <unistd.h>
23#include <errno.h>
630374d4 24#include <limits.h>
43c02e7b
VB
25#include <signal.h>
26#include <sys/stat.h>
27#include <fcntl.h>
43c02e7b 28#include <time.h>
a2993d83 29#include <libgen.h>
e6b36c87 30#include <assert.h>
a73e04f4 31#include <sys/param.h>
43c02e7b
VB
32#include <sys/utsname.h>
33#include <sys/types.h>
c036b15d 34#include <sys/wait.h>
43c02e7b
VB
35#include <sys/socket.h>
36#include <sys/select.h>
37#include <sys/time.h>
38#include <sys/ioctl.h>
39#include <arpa/inet.h>
690b944c 40#include <netinet/if_ether.h>
2e91d1a1
VB
41#include <pwd.h>
42#include <grp.h>
43c02e7b 43
f36b76c1 44#if HAVE_VFORK_H
8b549648 45# include <vfork.h>
f36b76c1
VB
46#endif
47#if HAVE_WORKING_FORK
8b549648 48# define vfork fork
f36b76c1
VB
49#endif
50
8b549648 51static void usage(void);
43c02e7b 52
8b549648 53static struct protocol protos[] = {
0c877af0 54 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
8b549648
VB
55 { LLDP_ADDR_NEAREST_BRIDGE, LLDP_ADDR_NEAREST_NONTPMR_BRIDGE,
56 LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE } },
4bad1937 57#ifdef ENABLE_CDP
43c02e7b 58 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
8b549648 59 { CDP_MULTICAST_ADDR } },
43c02e7b 60 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
8b549648 61 { CDP_MULTICAST_ADDR } },
4bad1937
VB
62#endif
63#ifdef ENABLE_SONMP
43c02e7b 64 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
8b549648 65 { SONMP_MULTICAST_ADDR } },
4bad1937
VB
66#endif
67#ifdef ENABLE_EDP
43c02e7b 68 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
8b549648 69 { EDP_MULTICAST_ADDR } },
4bad1937
VB
70#endif
71#ifdef ENABLE_FDP
031118c4 72 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
8b549648 73 { FDP_MULTICAST_ADDR } },
4bad1937 74#endif
8b549648 75 { 0, 0, "any", ' ', NULL, NULL, NULL, { { 0, 0, 0, 0, 0, 0 } } }
43c02e7b
VB
76};
77
8b549648 78static char **saved_argv;
6bb9c4e0 79#ifdef HAVE___PROGNAME
8b549648 80extern const char *__progname;
6bb9c4e0 81#else
8b549648 82# define __progname "lldpd"
6bb9c4e0 83#endif
43c02e7b 84
8888d191 85static void
43c02e7b
VB
86usage(void)
87{
4593c0dc
VB
88 fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
89 fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
53132616
VB
90
91 fprintf(stderr, "\n");
92
93 fprintf(stderr, "-d Do not daemonize.\n");
5e64a80e 94 fprintf(stderr, "-r Receive-only mode\n");
53132616 95 fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n");
8b549648
VB
96 fprintf(stderr,
97 "-k Disable advertising of kernel release, version, machine.\n");
fde7a7ce 98 fprintf(stderr, "-S descr Override the default system description.\n");
d4afb919 99 fprintf(stderr, "-P name Override the default hardware platform.\n");
8b549648
VB
100 fprintf(stderr,
101 "-m IP Specify the IP management addresses of this system.\n");
102 fprintf(stderr,
103 "-u file Specify the Unix-domain socket used for communication with lldpctl(8).\n");
104 fprintf(stderr,
105 "-H mode Specify the behaviour when detecting multiple neighbors.\n");
fbda1f9f 106 fprintf(stderr, "-I iface Limit interfaces to use.\n");
384f5f59
DP
107 fprintf(stderr, "-C iface Limit interfaces to use for computing chassis ID.\n");
108 fprintf(stderr, "-L path Override path for lldpcli command.\n");
8b549648
VB
109 fprintf(stderr,
110 "-O file Override default configuration locations processed by lldpcli(8) at start.\n");
8347587d 111#ifdef ENABLE_LLDPMED
8b549648 112 fprintf(stderr,
6003c0d6 113 "-M class Enable emission of LLDP-MED frames. 'class' should be one of:\n");
53132616
VB
114 fprintf(stderr, " 1 Generic Endpoint (Class I)\n");
115 fprintf(stderr, " 2 Media Endpoint (Class II)\n");
116 fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n");
117 fprintf(stderr, " 4 Network Connectivity Device\n");
8347587d
VB
118#endif
119#ifdef USE_SNMP
53132616 120 fprintf(stderr, "-x Enable SNMP subagent.\n");
384f5f59 121 fprintf(stderr, "-X sock Specify the SNMP subagent socket.\n");
53132616
VB
122#endif
123 fprintf(stderr, "\n");
124
8b549648
VB
125#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || \
126 defined ENABLE_SONMP
8347587d 127 fprintf(stderr, "Additional protocol support.\n");
8b549648 128# ifdef ENABLE_CDP
53132616 129 fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
8b549648
VB
130# endif
131# ifdef ENABLE_EDP
53132616 132 fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
8b549648
VB
133# endif
134# ifdef ENABLE_FDP
53132616 135 fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
8b549648
VB
136# endif
137# ifdef ENABLE_SONMP
53132616 138 fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
8b549648 139# endif
53132616
VB
140
141 fprintf(stderr, "\n");
8347587d 142#endif
53132616 143
3071b40a 144 fprintf(stderr, "See manual page lldpd(8) for more information\n");
43c02e7b
VB
145 exit(1);
146}
147
6e75df87 148struct lldpd_hardware *
32945d6a 149lldpd_get_hardware(struct lldpd *cfg, char *name, int index)
43c02e7b 150{
6e75df87 151 struct lldpd_hardware *hardware;
8b549648 152 TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
0a78e14f
VB
153 if (strcmp(hardware->h_ifname, name) == 0) {
154 if (hardware->h_flags == 0) {
3d916d6d
VB
155 if (hardware->h_ifindex != 0 &&
156 hardware->h_ifindex != index) {
8b549648
VB
157 log_debug("interfaces",
158 "%s changed index: from %d to %d",
159 hardware->h_ifname, hardware->h_ifindex,
160 index);
3d916d6d
VB
161 hardware->h_ifindex_changed = 1;
162 }
0a78e14f
VB
163 hardware->h_ifindex = index;
164 break;
165 }
8b549648 166 if (hardware->h_ifindex == index) break;
0a78e14f 167 }
43c02e7b 168 }
6e75df87 169 return hardware;
43c02e7b
VB
170}
171
9da663f7
VB
172/**
173 * Allocate the default local port. This port will be cloned each time we need a
174 * new local port.
175 */
176static void
177lldpd_alloc_default_local_port(struct lldpd *cfg)
178{
179 struct lldpd_port *port;
180
8b549648 181 if ((port = (struct lldpd_port *)calloc(1, sizeof(struct lldpd_port))) == NULL)
9da663f7
VB
182 fatal("main", NULL);
183
184#ifdef ENABLE_DOT1
185 TAILQ_INIT(&port->p_vlans);
186 TAILQ_INIT(&port->p_ppvids);
187 TAILQ_INIT(&port->p_pids);
188#endif
189#ifdef ENABLE_CUSTOM
190 TAILQ_INIT(&port->p_custom_list);
191#endif
192 cfg->g_default_local_port = port;
193}
194
195/**
196 * Clone a given port. The destination needs to be already allocated.
197 */
198static int
199lldpd_clone_port(struct lldpd_port *destination, struct lldpd_port *source)
200{
201
202 u_int8_t *output = NULL;
203 ssize_t output_len;
204 struct lldpd_port *cloned = NULL;
8b549648 205 output_len = lldpd_port_serialize(source, (void **)&output);
9da663f7
VB
206 if (output_len == -1 ||
207 lldpd_port_unserialize(output, output_len, &cloned) <= 0) {
208 log_warnx("alloc", "unable to clone default port");
c31f0d8a
VB
209 free(output);
210 return -1;
9da663f7
VB
211 }
212 memcpy(destination, cloned, sizeof(struct lldpd_port));
c31f0d8a
VB
213 free(cloned);
214 free(output);
9da663f7
VB
215#ifdef ENABLE_DOT1
216 marshal_repair_tailq(lldpd_vlan, &destination->p_vlans, v_entries);
217 marshal_repair_tailq(lldpd_ppvid, &destination->p_ppvids, p_entries);
218 marshal_repair_tailq(lldpd_pi, &destination->p_pids, p_entries);
219#endif
220#ifdef ENABLE_CUSTOM
221 marshal_repair_tailq(lldpd_custom, &destination->p_custom_list, next);
222#endif
223 return 0;
9da663f7
VB
224}
225
6e75df87 226struct lldpd_hardware *
e12c2365 227lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
43c02e7b 228{
6e75df87 229 struct lldpd_hardware *hardware;
43c02e7b 230
6f8925be
VB
231 log_debug("alloc", "allocate a new local port (%s)", name);
232
8b549648
VB
233 if ((hardware = (struct lldpd_hardware *)calloc(1,
234 sizeof(struct lldpd_hardware))) == NULL)
6e75df87 235 return NULL;
43c02e7b 236
9da663f7
VB
237 /* Clone default local port */
238 if (lldpd_clone_port(&hardware->h_lport, cfg->g_default_local_port) == -1) {
239 log_warnx("alloc", "unable to clone default port");
240 free(hardware);
241 return NULL;
242 }
243
d6e889b6 244 hardware->h_cfg = cfg;
6e75df87 245 strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
e12c2365 246 hardware->h_ifindex = index;
6e75df87 247 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
9df5ec3d 248 hardware->h_lport.p_chassis->c_refcount++;
6e75df87 249 TAILQ_INIT(&hardware->h_rports);
43c02e7b 250
6e75df87
VB
251#ifdef ENABLE_LLDPMED
252 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
4b292b55 253 hardware->h_lport.p_med_cap_enabled = LLDP_MED_CAP_CAP;
8ec333bd 254 if (!cfg->g_config.c_noinventory)
4b292b55 255 hardware->h_lport.p_med_cap_enabled |= LLDP_MED_CAP_IV;
6e75df87
VB
256 }
257#endif
d6e889b6
VB
258
259 levent_hardware_init(hardware);
6e75df87 260 return hardware;
43c02e7b
VB
261}
262
e6b36c87
JV
263struct lldpd_mgmt *
264lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
265{
266 struct lldpd_mgmt *mgmt;
6f8925be
VB
267
268 log_debug("alloc", "allocate a new management address (family: %d)", family);
269
e6b36c87
JV
270 if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
271 errno = EAFNOSUPPORT;
272 return NULL;
273 }
274 if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
275 errno = EOVERFLOW;
276 return NULL;
277 }
278 mgmt = calloc(1, sizeof(struct lldpd_mgmt));
279 if (mgmt == NULL) {
280 errno = ENOMEM;
281 return NULL;
282 }
283 mgmt->m_family = family;
e6b36c87
JV
284 memcpy(&mgmt->m_addr, addrptr, addrsize);
285 mgmt->m_addrsize = addrsize;
286 mgmt->m_iface = iface;
287 return mgmt;
288}
289
d9be8ea0 290void
6e75df87 291lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
d9be8ea0 292{
6f8925be
VB
293 log_debug("alloc", "cleanup hardware port %s", hardware->h_ifname);
294
17d34115 295 free(hardware->h_lport_previous);
acb5f65b
VB
296 free(hardware->h_lchassis_previous_id);
297 free(hardware->h_lport_previous_id);
aa6e9297 298 free(hardware->h_ifdescr_previous);
4b292b55 299 lldpd_port_cleanup(&hardware->h_lport, 1);
dbfa89c6 300 if (hardware->h_ops && hardware->h_ops->cleanup)
6e75df87 301 hardware->h_ops->cleanup(cfg, hardware);
d6e889b6 302 levent_hardware_release(hardware);
d9be8ea0
VB
303 free(hardware);
304}
305
47820fc4 306static void
aa6e9297 307lldpd_ifdescr_neighbors(struct lldpd *cfg)
47820fc4 308{
bb37268d 309 if (!cfg->g_config.c_set_ifdescr) return;
47820fc4 310 struct lldpd_hardware *hardware;
8b549648 311 TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
47820fc4
VB
312 struct lldpd_port *port;
313 char *description;
314 const char *neighbor = NULL;
315 unsigned neighbors = 0;
8b549648 316 TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
47820fc4
VB
317 if (SMART_HIDDEN(port)) continue;
318 neighbors++;
319 neighbor = port->p_chassis->c_name;
320 }
321 if (neighbors == 0)
aa6e9297 322 description = strdup("");
12313820 323 else if (neighbors == 1 && neighbor && *neighbor != '\0') {
aa6e9297
VB
324 if (asprintf(&description, "%s", neighbor) == -1) {
325 continue;
47820fc4
VB
326 }
327 } else {
8b549648 328 if (asprintf(&description, "%d neighbor%s", neighbors,
aa6e9297
VB
329 (neighbors > 1) ? "s" : "") == -1) {
330 continue;
47820fc4
VB
331 }
332 }
aa6e9297
VB
333 if (hardware->h_ifdescr_previous == NULL ||
334 strcmp(hardware->h_ifdescr_previous, description)) {
335 priv_iface_description(hardware->h_ifname, description);
336 free(hardware->h_ifdescr_previous);
337 hardware->h_ifdescr_previous = description;
338 } else
339 free(description);
47820fc4
VB
340 }
341}
342
66879d46
VB
343static void
344lldpd_count_neighbors(struct lldpd *cfg)
345{
e7103aff 346#if HAVE_SETPROCTITLE
480f1c61
VB
347 struct lldpd_chassis *chassis;
348 const char *neighbor;
66879d46 349 unsigned neighbors = 0;
8b549648 350 TAILQ_FOREACH (chassis, &cfg->g_chassis, c_entries) {
480f1c61
VB
351 neighbors++;
352 neighbor = chassis->c_name;
66879d46 353 }
480f1c61
VB
354 neighbors--;
355 if (neighbors == 0)
bd36a4d3 356 setproctitle("no neighbor.");
12313820 357 else if (neighbors == 1 && neighbor && *neighbor != '\0')
bd36a4d3 358 setproctitle("connected to %s.", neighbor);
480f1c61 359 else
8b549648 360 setproctitle("%d neighbor%s.", neighbors, (neighbors > 1) ? "s" : "");
e7103aff 361#endif
aa6e9297 362 lldpd_ifdescr_neighbors(cfg);
66879d46
VB
363}
364
4e90a9e0 365static void
8b549648 366notify_clients_deletion(struct lldpd_hardware *hardware, struct lldpd_port *rport)
4e90a9e0 367{
8b549648
VB
368 TRACE(LLDPD_NEIGHBOR_DELETE(hardware->h_ifname, rport->p_chassis->c_name,
369 rport->p_descr));
370 levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_DELETED, rport);
25de85a4
VB
371#ifdef USE_SNMP
372 agent_notify(hardware, NEIGHBOR_CHANGE_DELETED, rport);
373#endif
4e90a9e0
VB
374}
375
579bedd5
VB
376static void
377lldpd_reset_timer(struct lldpd *cfg)
378{
379 /* Reset timer for ports that have been changed. */
380 struct lldpd_hardware *hardware;
8b549648 381 TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
17d34115
VB
382 /* We keep a flat copy of the local port to see if there is any
383 * change. To do this, we zero out fields that are not
384 * significant, marshal the port, then restore. */
579bedd5 385 struct lldpd_port *port = &hardware->h_lport;
196757ce
VB
386 /* Take the current flags into account to detect a change. */
387 port->_p_hardware_flags = hardware->h_flags;
579bedd5 388 u_int8_t *output = NULL;
324d73a3 389 ssize_t output_len;
28414fbd 390 char save[LLDPD_PORT_START_MARKER];
579bedd5 391 memcpy(save, port, sizeof(save));
109c41b9 392 /* coverity[sizeof_mismatch]
87dfd175 393 We intentionally partially memset port */
579bedd5 394 memset(port, 0, sizeof(save));
8b549648 395 output_len = lldpd_port_serialize(port, (void **)&output);
579bedd5
VB
396 memcpy(port, save, sizeof(save));
397 if (output_len == -1) {
398 log_warnx("localchassis",
399 "unable to serialize local port %s to check for differences",
400 hardware->h_ifname);
401 continue;
402 }
17d34115
VB
403
404 /* Compare with the previous value */
8b549648 405 if (!hardware->h_ifindex_changed && hardware->h_lport_previous &&
17d34115
VB
406 output_len == hardware->h_lport_previous_len &&
407 !memcmp(output, hardware->h_lport_previous, output_len)) {
8b549648 408 log_debug("localchassis", "no change detected for port %s",
579bedd5 409 hardware->h_ifname);
579bedd5
VB
410 } else {
411 log_debug("localchassis",
17d34115 412 "change detected for port %s, resetting its timer",
579bedd5 413 hardware->h_ifname);
3d916d6d 414 hardware->h_ifindex_changed = 0;
17d34115 415 levent_schedule_pdu(hardware);
579bedd5 416 }
17d34115
VB
417
418 /* Update the value */
419 free(hardware->h_lport_previous);
420 hardware->h_lport_previous = output;
421 hardware->h_lport_previous_len = output_len;
579bedd5
VB
422 }
423}
424
24cb9684
VB
425static void
426lldpd_all_chassis_cleanup(struct lldpd *cfg)
427{
428 struct lldpd_chassis *chassis, *chassis_next;
429 log_debug("localchassis", "cleanup all chassis");
430
8b549648 431 for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis; chassis = chassis_next) {
24cb9684
VB
432 chassis_next = TAILQ_NEXT(chassis, c_entries);
433 if (chassis->c_refcount == 0) {
434 TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
435 lldpd_chassis_cleanup(chassis, 1);
436 }
437 }
438}
439
3333d2a8 440void
43c02e7b
VB
441lldpd_cleanup(struct lldpd *cfg)
442{
443 struct lldpd_hardware *hardware, *hardware_next;
444
3333d2a8 445 log_debug("localchassis", "cleanup all ports");
6f8925be 446
43c02e7b
VB
447 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
448 hardware = hardware_next) {
449 hardware_next = TAILQ_NEXT(hardware, h_entries);
6e75df87 450 if (!hardware->h_flags) {
8b549648
VB
451 int m = cfg->g_config.c_perm_ifaces ?
452 pattern_match(hardware->h_ifname,
453 cfg->g_config.c_perm_ifaces, 0) :
0a78e14f
VB
454 0;
455 switch (m) {
8b9da822 456 case PATTERN_MATCH_DENIED:
8b549648
VB
457 log_debug("localchassis",
458 "delete non-permanent interface %s",
0a78e14f
VB
459 hardware->h_ifname);
460 TRACE(LLDPD_INTERFACES_DELETE(hardware->h_ifname));
461 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
8b549648
VB
462 lldpd_remote_cleanup(hardware, notify_clients_deletion,
463 1);
0a78e14f
VB
464 lldpd_hardware_cleanup(cfg, hardware);
465 break;
8b9da822
VB
466 case PATTERN_MATCH_ALLOWED:
467 case PATTERN_MATCH_ALLOWED_EXACT:
0a78e14f
VB
468 log_debug("localchassis", "do not delete %s, permanent",
469 hardware->h_ifname);
8b549648
VB
470 lldpd_remote_cleanup(hardware, notify_clients_deletion,
471 1);
0a78e14f
VB
472 break;
473 }
0da01fd6 474 } else {
7f0877f0
VB
475 lldpd_remote_cleanup(hardware, notify_clients_deletion,
476 !(hardware->h_flags & IFF_RUNNING));
0da01fd6 477 }
43c02e7b 478 }
aadc9936 479
3333d2a8 480 levent_schedule_cleanup(cfg);
24cb9684
VB
481 lldpd_all_chassis_cleanup(cfg);
482 lldpd_count_neighbors(cfg);
43c02e7b
VB
483}
484
d938c51f
VB
485/* Update chassis `ochassis' with values from `chassis'. The later one is not
486 expected to be part of a list! It will also be wiped from memory. */
566c635d 487static void
8b549648
VB
488lldpd_move_chassis(struct lldpd_chassis *ochassis, struct lldpd_chassis *chassis)
489{
5fd6695c
VB
490 struct lldpd_mgmt *mgmt, *mgmt_next;
491
566c635d
VB
492 /* We want to keep refcount, index and list stuff from the current
493 * chassis */
d938c51f 494 TAILQ_ENTRY(lldpd_chassis) entries;
566c635d
VB
495 int refcount = ochassis->c_refcount;
496 int index = ochassis->c_index;
8b549648 497 memcpy(&entries, &ochassis->c_entries, sizeof(entries));
566c635d 498 lldpd_chassis_cleanup(ochassis, 0);
d938c51f
VB
499
500 /* Make the copy. */
501 /* WARNING: this is a kludgy hack, we need in-place copy and cannot use
502 * marshaling. */
566c635d 503 memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
d938c51f
VB
504 TAILQ_INIT(&ochassis->c_mgmt);
505
506 /* Copy of management addresses */
8b549648 507 for (mgmt = TAILQ_FIRST(&chassis->c_mgmt); mgmt != NULL; mgmt = mgmt_next) {
d938c51f
VB
508 mgmt_next = TAILQ_NEXT(mgmt, m_entries);
509 TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries);
510 TAILQ_INSERT_TAIL(&ochassis->c_mgmt, mgmt, m_entries);
511 }
512
566c635d
VB
513 /* Restore saved values */
514 ochassis->c_refcount = refcount;
515 ochassis->c_index = index;
516 memcpy(&ochassis->c_entries, &entries, sizeof(entries));
d938c51f
VB
517
518 /* Get rid of the new chassis */
519 free(chassis);
566c635d
VB
520}
521
8888d191 522static int
43c02e7b
VB
523lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
524{
c2e8c8c7 525 size_t i, j;
8b549648 526 if (s < ETHER_ADDR_LEN) return -1;
c2e8c8c7 527 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
8b549648 528 if (!cfg->g_protocols[i].enabled) continue;
43c02e7b 529 if (cfg->g_protocols[i].guess == NULL) {
8b549648
VB
530 for (j = 0; j < sizeof(cfg->g_protocols[0].mac) /
531 sizeof(cfg->g_protocols[0].mac[0]);
c2e8c8c7 532 j++) {
8b549648
VB
533 if (memcmp(frame, cfg->g_protocols[i].mac[j],
534 ETHER_ADDR_LEN) == 0) {
535 log_debug("decode",
536 "guessed protocol is %s (from MAC address)",
c2e8c8c7
VB
537 cfg->g_protocols[i].name);
538 return cfg->g_protocols[i].mode;
539 }
6f8925be 540 }
43c02e7b 541 } else {
6f8925be 542 if (cfg->g_protocols[i].guess(frame, s)) {
8b549648
VB
543 log_debug("decode",
544 "guessed protocol is %s (from detector function)",
6f8925be 545 cfg->g_protocols[i].name);
43c02e7b 546 return cfg->g_protocols[i].mode;
6f8925be 547 }
43c02e7b
VB
548 }
549 }
550 return -1;
551}
552
8888d191 553static void
8b549648 554lldpd_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware)
43c02e7b 555{
281a5cd4 556 int i;
77507b69 557 struct lldpd_chassis *chassis, *ochassis = NULL;
4e90a9e0 558 struct lldpd_port *port, *oport = NULL, *aport;
43c02e7b
VB
559 int guess = LLDPD_MODE_LLDP;
560
8b549648 561 log_debug("decode", "decode a received frame on %s", hardware->h_ifname);
6f8925be 562
32f0deee 563 if (s < sizeof(struct ether_header) + 4) {
49697208 564 /* Too short, just discard it */
32f0deee 565 hardware->h_rx_discarded_cnt++;
50a89ca7 566 return;
32f0deee 567 }
02cf0357 568
49697208 569 /* Decapsulate VLAN frames */
02cf0357
VB
570 struct ether_header eheader;
571 memcpy(&eheader, frame, sizeof(struct ether_header));
572 if (eheader.ether_type == htons(ETHERTYPE_VLAN)) {
49697208 573 /* VLAN decapsulation means to shift 4 bytes left the frame from
4e5f34c5 574 * offset 2*ETHER_ADDR_LEN */
8b549648
VB
575 memmove(frame + 2 * ETHER_ADDR_LEN, frame + 2 * ETHER_ADDR_LEN + 4,
576 s - 2 * ETHER_ADDR_LEN);
49697208
VB
577 s -= 4;
578 }
50a89ca7 579
8b549648
VB
580 TAILQ_FOREACH (oport, &hardware->h_rports, p_entries) {
581 if ((oport->p_lastframe != NULL) && (oport->p_lastframe->size == s) &&
77507b69
VB
582 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
583 /* Already received the same frame */
6f8925be 584 log_debug("decode", "duplicate frame, no need to decode");
77507b69
VB
585 oport->p_lastupdate = time(NULL);
586 return;
587 }
43c02e7b
VB
588 }
589
f2dcb180 590 guess = lldpd_guess_type(cfg, frame, s);
8b549648
VB
591 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
592 if (!cfg->g_protocols[i].enabled) continue;
f2dcb180 593 if (cfg->g_protocols[i].mode == guess) {
6f8925be
VB
594 log_debug("decode", "using decode function for %s protocol",
595 cfg->g_protocols[i].name);
8b549648
VB
596 if (cfg->g_protocols[i].decode(cfg, frame, s, hardware,
597 &chassis, &port) == -1) {
598 log_debug("decode",
599 "function for %s protocol did not decode this frame",
6f8925be 600 cfg->g_protocols[i].name);
32f0deee 601 hardware->h_rx_discarded_cnt++;
f2dcb180 602 return;
6f8925be 603 }
77507b69
VB
604 chassis->c_protocol = port->p_protocol =
605 cfg->g_protocols[i].mode;
f2dcb180 606 break;
8b549648 607 }
f2dcb180
VB
608 }
609 if (cfg->g_protocols[i].mode == 0) {
6f8925be 610 log_debug("decode", "unable to guess frame type on %s",
ba5116b5 611 hardware->h_ifname);
43c02e7b 612 return;
f2dcb180 613 }
8b549648
VB
614 TRACE(LLDPD_FRAME_DECODED(hardware->h_ifname, cfg->g_protocols[i].name,
615 chassis->c_name, port->p_descr));
43c02e7b 616
77507b69 617 /* Do we already have the same MSAP somewhere? */
42589660 618 int count = 0;
6f8925be 619 log_debug("decode", "search for the same MSAP");
8b549648 620 TAILQ_FOREACH (oport, &hardware->h_rports, p_entries) {
42589660
VB
621 if (port->p_protocol == oport->p_protocol) {
622 count++;
623 if ((port->p_id_subtype == oport->p_id_subtype) &&
624 (port->p_id_len == oport->p_id_len) &&
625 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
626 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
627 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
628 (memcmp(chassis->c_id, oport->p_chassis->c_id,
8b549648 629 chassis->c_id_len) == 0)) {
42589660
VB
630 ochassis = oport->p_chassis;
631 log_debug("decode", "MSAP is already known");
632 break;
633 }
77507b69 634 }
43c02e7b 635 }
42589660 636 /* Do we have room for a new MSAP? */
ad21b578 637 if (!oport && cfg->g_config.c_max_neighbors) {
8b549648
VB
638 if (count == (cfg->g_config.c_max_neighbors - 1)) {
639 log_debug("decode",
640 "max neighbors %d reached for port %s, "
641 "dropping any new ones silently",
642 cfg->g_config.c_max_neighbors, hardware->h_ifname);
643 } else if (count > cfg->g_config.c_max_neighbors - 1) {
644 log_debug("decode",
645 "too many neighbors for port %s, drop this new one",
646 hardware->h_ifname);
647 lldpd_port_cleanup(port, 1);
648 lldpd_chassis_cleanup(chassis, 1);
649 free(port);
650 return;
651 }
42589660 652 }
77507b69
VB
653 /* No, but do we already know the system? */
654 if (!oport) {
6f8925be 655 log_debug("decode", "MSAP is unknown, search for the chassis");
8b549648 656 TAILQ_FOREACH (ochassis, &cfg->g_chassis, c_entries) {
77507b69
VB
657 if ((chassis->c_protocol == ochassis->c_protocol) &&
658 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
659 (chassis->c_id_len == ochassis->c_id_len) &&
8b549648
VB
660 (memcmp(chassis->c_id, ochassis->c_id, chassis->c_id_len) ==
661 0))
662 break;
43c02e7b 663 }
43c02e7b 664 }
43c02e7b 665
77507b69
VB
666 if (oport) {
667 /* The port is known, remove it before adding it back */
668 TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
4b292b55 669 lldpd_port_cleanup(oport, 1);
4e624dc2 670 free(oport);
77507b69
VB
671 }
672 if (ochassis) {
a9fe956f
VB
673 if (port->p_ttl == 0) {
674 /* Shutdown LLDPDU is special. We do not want to replace
675 * the chassis. Free the new chassis (which is mostly empty) */
676 log_debug("decode", "received a shutdown LLDPDU");
677 lldpd_chassis_cleanup(chassis, 1);
678 } else {
679 lldpd_move_chassis(ochassis, chassis);
680 }
77507b69
VB
681 chassis = ochassis;
682 } else {
683 /* Chassis not known, add it */
6f8925be 684 log_debug("decode", "unknown chassis, add it to the list");
77507b69 685 chassis->c_index = ++cfg->g_lastrid;
77507b69
VB
686 chassis->c_refcount = 0;
687 TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
8b549648
VB
688 i = 0;
689 TAILQ_FOREACH (ochassis, &cfg->g_chassis, c_entries)
690 i++;
6f8925be 691 log_debug("decode", "%d different systems are known", i);
77507b69
VB
692 }
693 /* Add port */
694 port->p_lastchange = port->p_lastupdate = time(NULL);
8b549648
VB
695 if ((port->p_lastframe = (struct lldpd_frame *)malloc(
696 s + sizeof(struct lldpd_frame))) != NULL) {
77507b69
VB
697 port->p_lastframe->size = s;
698 memcpy(port->p_lastframe->frame, frame, s);
699 }
700 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
701 port->p_chassis = chassis;
702 port->p_chassis->c_refcount++;
9df5ec3d
VB
703 /* Several cases are possible :
704 1. chassis is new, its refcount was 0. It is now attached
8b549648 705 to this port, its refcount is 1.
9df5ec3d 706 2. chassis already exists and was attached to another
8b549648 707 port, we increase its refcount accordingly.
9df5ec3d 708 3. chassis already exists and was attached to the same
8b549648
VB
709 port, its refcount was decreased with
710 lldpd_port_cleanup() and is now increased again.
9df5ec3d
VB
711
712 In all cases, if the port already existed, it has been
713 freed with lldpd_port_cleanup() and therefore, the refcount
714 of the chassis that was attached to it is decreased.
715 */
109c41b9 716 i = 0;
87dfd175
VB
717 /* coverity[use_after_free]
718 TAILQ_REMOVE does the right thing */
8b549648 719 TAILQ_FOREACH (aport, &hardware->h_rports, p_entries)
42b39485 720 i++;
8b549648 721 log_debug("decode", "%d neighbors for %s", i, hardware->h_ifname);
4e90a9e0 722
25de85a4
VB
723 if (!oport) hardware->h_insert_cnt++;
724
4e90a9e0 725 /* Notify */
8b549648 726 log_debug("decode", "send notifications for changes on %s", hardware->h_ifname);
bdfe4193 727 if (oport) {
8b549648
VB
728 TRACE(LLDPD_NEIGHBOR_UPDATE(hardware->h_ifname, chassis->c_name,
729 port->p_descr, i));
bdfe4193 730 levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_UPDATED, port);
25de85a4 731#ifdef USE_SNMP
bdfe4193 732 agent_notify(hardware, NEIGHBOR_CHANGE_UPDATED, port);
25de85a4 733#endif
bdfe4193 734 } else {
8b549648
VB
735 TRACE(LLDPD_NEIGHBOR_NEW(hardware->h_ifname, chassis->c_name,
736 port->p_descr, i));
bdfe4193
VB
737 levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_ADDED, port);
738#ifdef USE_SNMP
739 agent_notify(hardware, NEIGHBOR_CHANGE_ADDED, port);
740#endif
741 }
4e90a9e0 742
be511d00
VB
743#ifdef ENABLE_LLDPMED
744 if (!oport && port->p_chassis->c_med_type) {
b9de0ca6 745 /* New neighbor, fast start */
746 if (hardware->h_cfg->g_config.c_enable_fast_start &&
be511d00 747 !hardware->h_tx_fast) {
8b549648
VB
748 log_debug("decode",
749 "%s: entering fast start due to "
750 "new neighbor",
751 hardware->h_ifname);
b9de0ca6 752 hardware->h_tx_fast = hardware->h_cfg->g_config.c_tx_fast_init;
753 }
754
755 levent_schedule_pdu(hardware);
756 }
be511d00 757#endif
b9de0ca6 758
43c02e7b
VB
759 return;
760}
761
c036b15d
VB
762/* Get the output of lsb_release -s -d. This is a slow function. It should be
763 called once. It return NULL if any problem happens. Otherwise, this is a
764 statically allocated buffer. The result includes the trailing \n */
765static char *
8b549648
VB
766lldpd_get_lsb_release()
767{
c036b15d 768 static char release[1024];
f540397c
GS
769 char cmd[][12] = { "lsb_release", "-s", "-d" };
770 char *const command[] = { cmd[0], cmd[1], cmd[2], NULL };
c036b15d
VB
771 int pid, status, devnull, count;
772 int pipefd[2];
773
6f8925be
VB
774 log_debug("localchassis", "grab LSB release");
775
c036b15d 776 if (pipe(pipefd)) {
6f8925be 777 log_warn("localchassis", "unable to get a pair of pipes");
c036b15d
VB
778 return NULL;
779 }
780
45bf0bd0
VB
781 pid = vfork();
782 switch (pid) {
783 case -1:
6f8925be 784 log_warn("localchassis", "unable to fork");
c036b15d 785 return NULL;
c036b15d
VB
786 case 0:
787 /* Child, exec lsb_release */
788 close(pipefd[0]);
789 if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
790 dup2(devnull, STDIN_FILENO);
791 dup2(devnull, STDERR_FILENO);
792 dup2(pipefd[1], STDOUT_FILENO);
793 if (devnull > 2) close(devnull);
794 if (pipefd[1] > 2) close(pipefd[1]);
795 execvp("lsb_release", command);
796 }
45bf0bd0 797 _exit(127);
c036b15d
VB
798 break;
799 default:
800 /* Father, read the output from the children */
801 close(pipefd[1]);
802 count = 0;
803 do {
8b549648
VB
804 status =
805 read(pipefd[0], release + count, sizeof(release) - count);
c036b15d 806 if ((status == -1) && (errno == EINTR)) continue;
8b549648 807 if (status > 0) count += status;
c036b15d
VB
808 } while (count < sizeof(release) && (status > 0));
809 if (status < 0) {
6f8925be 810 log_info("localchassis", "unable to read from lsb_release");
c036b15d
VB
811 close(pipefd[0]);
812 waitpid(pid, &status, 0);
813 return NULL;
814 }
815 close(pipefd[0]);
816 if (count >= sizeof(release)) {
6f8925be 817 log_info("localchassis", "output of lsb_release is too large");
c036b15d
VB
818 waitpid(pid, &status, 0);
819 return NULL;
820 }
821 status = -1;
8b549648 822 if (waitpid(pid, &status, 0) != pid) return NULL;
c036b15d 823 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
8b549648
VB
824 log_info("localchassis",
825 "lsb_release information not available");
c036b15d
VB
826 return NULL;
827 }
828 if (!count) {
8b549648
VB
829 log_info("localchassis",
830 "lsb_release returned an empty string");
c036b15d
VB
831 return NULL;
832 }
833 release[count] = '\0';
834 return release;
835 }
836 /* Should not be here */
837 return NULL;
838}
839
ae87586a
MT
840/* Same like lldpd_get_lsb_release but reads /etc/os-release for PRETTY_NAME=. */
841static char *
8b549648
VB
842lldpd_get_os_release()
843{
ae87586a 844 static char release[1024];
9757bfbc
SK
845 char line[1024];
846 char *key, *val;
847 char *ptr1 = release;
ae87586a 848
6f8925be 849 log_debug("localchassis", "grab OS release");
f24382c4
VB
850 FILE *fp = fopen("/etc/os-release", "r");
851 if (!fp) {
852 log_debug("localchassis", "could not open /etc/os-release");
853 fp = fopen("/usr/lib/os-release", "r");
854 }
ae87586a 855 if (!fp) {
f24382c4
VB
856 log_info("localchassis",
857 "could not open either /etc/os-release or /usr/lib/os-release");
ae87586a
MT
858 return NULL;
859 }
860
e2b09091 861 while ((fgets(line, sizeof(line), fp) != NULL)) {
ae87586a 862 key = strtok(line, "=");
2151a7d0
EC
863 if (key == NULL) continue;
864
ae87586a
MT
865 val = strtok(NULL, "=");
866
e2b09091
VB
867 if (strncmp(key, "PRETTY_NAME", sizeof(line)) == 0) {
868 strlcpy(release, val, sizeof(line));
ae87586a
MT
869 break;
870 }
871 }
872 fclose(fp);
873
874 /* Remove trailing newline and all " in the string. */
dc21d049 875 ptr1 = release + strlen(release) - 1;
8b549648 876 while (ptr1 != release && ((*ptr1 == '"') || (*ptr1 == '\n'))) {
dc21d049
VB
877 *ptr1 = '\0';
878 ptr1--;
ae87586a 879 }
8b549648 880 if (release[0] == '"') return release + 1;
ae87586a
MT
881 return release;
882}
883
8482abe9 884static void
8b549648
VB
885lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask)
886{
42b39485 887 struct lldpd_port *port;
8b549648 888 int protocols[LLDPD_MODE_MAX + 1];
8482abe9
VB
889 char buffer[256];
890 int i, j, k, found;
42b39485
VB
891 unsigned int min;
892
8b549648 893 log_debug("smartfilter", "apply smart filter for port %s", hardware->h_ifname);
6f8925be 894
8482abe9 895 /* Compute the number of occurrences of each protocol */
8b549648
VB
896 for (i = 0; i <= LLDPD_MODE_MAX; i++)
897 protocols[i] = 0;
898 TAILQ_FOREACH (port, &hardware->h_rports, p_entries)
8482abe9
VB
899 protocols[port->p_protocol]++;
900
901 /* Turn the protocols[] array into an array of
902 enabled/disabled protocols. 1 means enabled, 0
903 means disabled. */
904 min = (unsigned int)-1;
905 for (i = 0; i <= LLDPD_MODE_MAX; i++)
8b549648 906 if (protocols[i] && (protocols[i] < min)) min = protocols[i];
8482abe9
VB
907 found = 0;
908 for (i = 0; i <= LLDPD_MODE_MAX; i++)
909 if ((protocols[i] == min) && !found) {
910 /* If we need a tie breaker, we take
911 the first protocol only */
8ec333bd 912 if (cfg->g_config.c_smart & mask &
8482abe9
VB
913 (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO))
914 found = 1;
915 protocols[i] = 1;
8b549648
VB
916 } else
917 protocols[i] = 0;
8482abe9
VB
918
919 /* We set the p_hidden flag to 1 if the protocol is disabled */
8b549648 920 TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
8482abe9 921 if (mask == SMART_OUTGOING)
8b549648 922 port->p_hidden_out = protocols[port->p_protocol] ? 0 : 1;
8482abe9 923 else
8b549648 924 port->p_hidden_in = protocols[port->p_protocol] ? 0 : 1;
8482abe9
VB
925 }
926
927 /* If we want only one neighbor, we take the first one */
8ec333bd 928 if (cfg->g_config.c_smart & mask &
8482abe9 929 (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
42b39485 930 found = 0;
8b549648 931 TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
8482abe9
VB
932 if (mask == SMART_OUTGOING) {
933 if (found) port->p_hidden_out = 1;
8b549648 934 if (!port->p_hidden_out) found = 1;
8482abe9
VB
935 }
936 if (mask == SMART_INCOMING) {
937 if (found) port->p_hidden_in = 1;
8b549648 938 if (!port->p_hidden_in) found = 1;
42b39485
VB
939 }
940 }
8482abe9 941 }
42b39485 942
8482abe9 943 /* Print a debug message summarizing the operation */
8b549648
VB
944 for (i = 0; i <= LLDPD_MODE_MAX; i++)
945 protocols[i] = 0;
8482abe9 946 k = j = 0;
8b549648 947 TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
8482abe9 948 if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) ||
8b549648 949 ((mask == SMART_INCOMING) && port->p_hidden_in))) {
8482abe9
VB
950 k++;
951 protocols[port->p_protocol] = 1;
42b39485 952 }
8482abe9
VB
953 j++;
954 }
955 buffer[0] = '\0';
8b549648
VB
956 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
957 if (cfg->g_protocols[i].enabled &&
958 protocols[cfg->g_protocols[i].mode]) {
959 if (strlen(buffer) + strlen(cfg->g_protocols[i].name) + 3 >
960 sizeof(buffer)) {
8482abe9
VB
961 /* Unlikely, our buffer is too small */
962 memcpy(buffer + sizeof(buffer) - 4, "...", 4);
963 break;
42b39485 964 }
247fb1a9
VB
965 if (buffer[0])
966 strncat(buffer, ", ",
967 sizeof(buffer) - strlen(buffer) - 1);
8b549648 968 strncat(buffer, cfg->g_protocols[i].name,
247fb1a9 969 sizeof(buffer) - strlen(buffer) - 1);
42b39485
VB
970 }
971 }
6f8925be 972 log_debug("smartfilter", "%s: %s: %d visible neighbors (out of %d)",
8b549648 973 hardware->h_ifname, (mask == SMART_OUTGOING) ? "out filter" : "in filter",
6f8925be 974 k, j);
8b549648
VB
975 log_debug("smartfilter", "%s: protocols: %s", hardware->h_ifname,
976 buffer[0] ? buffer : "(none)");
42b39485
VB
977}
978
566c635d
VB
979/* Hide unwanted ports depending on smart mode set by the user */
980static void
981lldpd_hide_all(struct lldpd *cfg)
982{
983 struct lldpd_hardware *hardware;
984
8b549648 985 if (!cfg->g_config.c_smart) return;
6f8925be 986 log_debug("smartfilter", "apply smart filter results on all ports");
8b549648 987 TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
8ec333bd 988 if (cfg->g_config.c_smart & SMART_INCOMING_FILTER)
566c635d 989 lldpd_hide_ports(cfg, hardware, SMART_INCOMING);
8ec333bd 990 if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER)
566c635d
VB
991 lldpd_hide_ports(cfg, hardware, SMART_OUTGOING);
992 }
993}
994
1e33fa1d
VB
995/* If PD device and PSE allocated power, echo back this change. If we have
996 * several LLDP neighbors, we use the latest updated. */
997static void
998lldpd_dot3_power_pd_pse(struct lldpd_hardware *hardware)
999{
1000#ifdef ENABLE_DOT3
1001 struct lldpd_port *port, *selected_port = NULL;
1002 /* Are we a PD device? */
8b549648
VB
1003 if (hardware->h_lport.p_power.devicetype != LLDP_DOT3_POWER_PD) return;
1004 TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
1005 if (port->p_hidden_in) continue;
5334d8c8 1006
8b549648
VB
1007 if (port->p_protocol != LLDPD_MODE_LLDP &&
1008 port->p_protocol != LLDPD_MODE_CDPV2)
1e33fa1d 1009 continue;
5334d8c8 1010
8b549648 1011 if (port->p_power.devicetype != LLDP_DOT3_POWER_PSE) continue;
1e33fa1d
VB
1012 if (!selected_port || port->p_lastupdate > selected_port->p_lastupdate)
1013 selected_port = port;
1014 }
8b549648
VB
1015 if (selected_port &&
1016 selected_port->p_power.allocated != hardware->h_lport.p_power.allocated) {
1017 log_info("receive",
1018 "for %s, PSE told us allocated is now %d instead of %d",
1019 hardware->h_ifname, selected_port->p_power.allocated,
1e33fa1d
VB
1020 hardware->h_lport.p_power.allocated);
1021 hardware->h_lport.p_power.allocated = selected_port->p_power.allocated;
1022 levent_schedule_pdu(hardware);
1023 }
5334d8c8 1024
8b549648
VB
1025# ifdef ENABLE_CDP
1026 if (selected_port &&
1027 selected_port->p_cdp_power.management_id !=
1028 hardware->h_lport.p_cdp_power.management_id) {
1029 hardware->h_lport.p_cdp_power.management_id =
1030 selected_port->p_cdp_power.management_id;
5334d8c8 1031 }
8b549648 1032# endif
5334d8c8 1033
1e33fa1d
VB
1034#endif
1035}
1036
d6e889b6
VB
1037void
1038lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd)
43c02e7b 1039{
d6e889b6
VB
1040 char *buffer = NULL;
1041 int n;
8b549648 1042 log_debug("receive", "receive a frame on %s", hardware->h_ifname);
d6e889b6 1043 if ((buffer = (char *)malloc(hardware->h_mtu)) == NULL) {
6f8925be 1044 log_warn("receive", "failed to alloc reception buffer");
d6e889b6
VB
1045 return;
1046 }
8b549648
VB
1047 if ((n = hardware->h_ops->recv(cfg, hardware, fd, buffer, hardware->h_mtu)) ==
1048 -1) {
3e2e693c 1049 log_debug("receive", "discard frame received on %s",
6f8925be 1050 hardware->h_ifname);
d6e889b6
VB
1051 free(buffer);
1052 return;
1053 }
e7331ce9
VB
1054 if (hardware->h_lport.p_disable_rx) {
1055 log_debug("receive", "RX disabled, ignore the frame on %s",
1056 hardware->h_ifname);
1057 free(buffer);
1058 return;
1059 }
e4ff3ed5
VB
1060 if (cfg->g_config.c_paused) {
1061 log_debug("receive", "paused, ignore the frame on %s",
8b549648 1062 hardware->h_ifname);
e4ff3ed5
VB
1063 free(buffer);
1064 return;
1065 }
d6e889b6 1066 hardware->h_rx_cnt++;
8b549648 1067 log_debug("receive", "decode received frame on %s", hardware->h_ifname);
bdfe4193 1068 TRACE(LLDPD_FRAME_RECEIVED(hardware->h_ifname, buffer, (size_t)n));
d6e889b6 1069 lldpd_decode(cfg, buffer, n, hardware);
755a219e 1070 lldpd_hide_all(cfg); /* Immediately hide */
1e33fa1d 1071 lldpd_dot3_power_pd_pse(hardware);
66879d46 1072 lldpd_count_neighbors(cfg);
d6e889b6 1073 free(buffer);
43c02e7b
VB
1074}
1075
e770b720
VB
1076static void
1077lldpd_send_shutdown(struct lldpd_hardware *hardware)
1078{
1079 struct lldpd *cfg = hardware->h_cfg;
1080 if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return;
e7331ce9 1081 if (hardware->h_lport.p_disable_tx) return;
8b549648 1082 if ((hardware->h_flags & IFF_RUNNING) == 0) return;
e770b720
VB
1083
1084 /* It's safe to call `lldp_send_shutdown()` because shutdown LLDPU will
1085 * only be emitted if LLDP was sent on that port. */
1086 if (lldp_send_shutdown(hardware->h_cfg, hardware) != 0)
1087 log_warnx("send", "unable to send shutdown LLDPDU on %s",
1088 hardware->h_ifname);
1089}
1090
579bedd5
VB
1091void
1092lldpd_send(struct lldpd_hardware *hardware)
43c02e7b 1093{
579bedd5 1094 struct lldpd *cfg = hardware->h_cfg;
77507b69 1095 struct lldpd_port *port;
0d86e62f 1096 int i, sent;
f7db0dd8 1097
e4ff3ed5 1098 if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return;
e7331ce9 1099 if (hardware->h_lport.p_disable_tx) return;
8b549648 1100 if ((hardware->h_flags & IFF_RUNNING) == 0) return;
6f8925be 1101
579bedd5
VB
1102 log_debug("send", "send PDU on %s", hardware->h_ifname);
1103 sent = 0;
8b549648
VB
1104 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
1105 if (!cfg->g_protocols[i].enabled) continue;
579bedd5
VB
1106 /* We send only if we have at least one remote system
1107 * speaking this protocol or if the protocol is forced */
1108 if (cfg->g_protocols[i].enabled > 1) {
1109 cfg->g_protocols[i].send(cfg, hardware);
1110 sent++;
43c02e7b 1111 continue;
43c02e7b 1112 }
8b549648 1113 TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
579bedd5
VB
1114 /* If this remote port is disabled, we don't
1115 * consider it */
8b549648
VB
1116 if (port->p_hidden_out) continue;
1117 if (port->p_protocol == cfg->g_protocols[i].mode) {
bdfe4193 1118 TRACE(LLDPD_FRAME_SEND(hardware->h_ifname,
8b549648 1119 cfg->g_protocols[i].name));
579bedd5 1120 log_debug("send", "send PDU on %s with protocol %s",
8b549648
VB
1121 hardware->h_ifname, cfg->g_protocols[i].name);
1122 cfg->g_protocols[i].send(cfg, hardware);
a54f6012 1123 hardware->h_lport.p_protocol = cfg->g_protocols[i].mode;
579bedd5 1124 sent++;
46baf627
VB
1125 break;
1126 }
46baf627 1127 }
43c02e7b 1128 }
579bedd5
VB
1129
1130 if (!sent) {
1131 /* Nothing was sent for this port, let's speak the first
1132 * available protocol. */
1133 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
1134 if (!cfg->g_protocols[i].enabled) continue;
bdfe4193 1135 TRACE(LLDPD_FRAME_SEND(hardware->h_ifname,
8b549648 1136 cfg->g_protocols[i].name));
579bedd5
VB
1137 log_debug("send", "fallback to protocol %s for %s",
1138 cfg->g_protocols[i].name, hardware->h_ifname);
8b549648 1139 cfg->g_protocols[i].send(cfg, hardware);
579bedd5
VB
1140 break;
1141 }
1142 if (cfg->g_protocols[i].mode == 0)
1143 log_warnx("send", "no protocol enabled, dunno what to send");
1144 }
43c02e7b
VB
1145}
1146
89840df0 1147#ifdef ENABLE_LLDPMED
8888d191 1148static void
8ef9c766 1149lldpd_med(struct lldpd *cfg, struct utsname *un)
89840df0 1150{
55606d4b 1151 static short int once = 0;
8ef9c766
HG
1152 if (!once && cfg) {
1153 LOCAL_CHASSIS(cfg)->c_med_hw = dmi_hw();
1154 LOCAL_CHASSIS(cfg)->c_med_fw = dmi_fw();
1155 LOCAL_CHASSIS(cfg)->c_med_sn = dmi_sn();
1156 LOCAL_CHASSIS(cfg)->c_med_manuf = dmi_manuf();
1157 LOCAL_CHASSIS(cfg)->c_med_model = dmi_model();
1158 LOCAL_CHASSIS(cfg)->c_med_asset = dmi_asset();
1159 if (un) {
1160 if (LOCAL_CHASSIS(cfg)->c_med_sw)
1161 free(LOCAL_CHASSIS(cfg)->c_med_sw);
1162
1163 if (cfg->g_config.c_advertise_version)
1164 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un->release);
1165 else
1166 LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
1167 }
55606d4b
VB
1168 once = 1;
1169 }
89840df0
VB
1170}
1171#endif
1172
e66b7f34 1173static int
c3e340b6 1174lldpd_routing_enabled(struct lldpd *cfg)
e66b7f34 1175{
c3e340b6 1176 int routing;
a2b113ef 1177
8b549648 1178 if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_ROUTER) == 0) return 0;
a2b113ef 1179
c3e340b6
VB
1180 if ((routing = interfaces_routing_enabled(cfg)) == -1) {
1181 log_debug("localchassis", "unable to check if routing is enabled");
1182 return 0;
1183 }
1184 return routing;
e66b7f34
VB
1185}
1186
e6f64ed9 1187void
6e75df87 1188lldpd_update_localchassis(struct lldpd *cfg)
43c02e7b 1189{
6e75df87
VB
1190 struct utsname un;
1191 char *hp;
43c02e7b 1192
6f8925be 1193 log_debug("localchassis", "update information for local chassis");
aa015c26 1194 assert(LOCAL_CHASSIS(cfg) != NULL);
6f8925be 1195
43c02e7b 1196 /* Set system name and description */
8b549648 1197 if (uname(&un) < 0) fatal("localchassis", "failed to get system information");
ce347d29 1198 if (cfg->g_config.c_hostname) {
8b549648
VB
1199 log_debug("localchassis", "use overridden system name `%s`",
1200 cfg->g_config.c_hostname);
ce347d29
JJ
1201 hp = cfg->g_config.c_hostname;
1202 } else {
1fa7d39f 1203 if ((hp = priv_gethostname()) == NULL)
ce347d29
JJ
1204 fatal("localchassis", "failed to get system name");
1205 }
77507b69
VB
1206 free(LOCAL_CHASSIS(cfg)->c_name);
1207 free(LOCAL_CHASSIS(cfg)->c_descr);
1208 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
6f8925be 1209 fatal("localchassis", NULL);
8b549648
VB
1210 if (cfg->g_config.c_description) {
1211 log_debug("localchassis", "use overridden description `%s`",
1212 cfg->g_config.c_description);
1213 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
8ec333bd 1214 cfg->g_config.c_description) == -1)
6f8925be 1215 fatal("localchassis", "failed to set full system description");
8b549648
VB
1216 } else {
1217 if (cfg->g_config.c_advertise_version) {
6f8925be 1218 log_debug("localchassis", "advertise system version");
8b549648
VB
1219 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s %s",
1220 cfg->g_lsb_release ? cfg->g_lsb_release : "",
1221 un.sysname, un.release, un.version, un.machine) == -1)
1222 fatal("localchassis",
1223 "failed to set full system description");
1224 } else {
6f8925be 1225 log_debug("localchassis", "do not advertise system version");
8b549648
VB
1226 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
1227 cfg->g_lsb_release ? cfg->g_lsb_release : un.sysname) ==
1228 -1)
1229 fatal("localchassis",
1230 "failed to set minimal system description");
1231 }
1232 }
96beef68
VB
1233 if (cfg->g_config.c_platform == NULL)
1234 cfg->g_config.c_platform = strdup(un.sysname);
43c02e7b 1235
4c8e6e37
ISN
1236 if (!cfg->g_config.c_cap_override) {
1237 /* Check routing */
1238 if (lldpd_routing_enabled(cfg)) {
8b549648
VB
1239 log_debug("localchassis",
1240 "routing is enabled, enable router capability");
4c8e6e37
ISN
1241 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_ROUTER;
1242 } else
1243 LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_ROUTER;
242845c7 1244
89840df0 1245#ifdef ENABLE_LLDPMED
4c8e6e37
ISN
1246 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
1247 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
1248 lldpd_med(cfg, &un);
89840df0 1249#endif
4c8e6e37
ISN
1250 if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_STATION) &&
1251 (LOCAL_CHASSIS(cfg)->c_cap_enabled == 0))
1252 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_STATION;
1253 else if (LOCAL_CHASSIS(cfg)->c_cap_enabled != LLDP_CAP_STATION)
1254 LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_STATION;
1255 }
43c02e7b 1256
b4ac8083
VB
1257 /* Set chassis ID if needed. This is only done if chassis ID
1258 has not been set previously (with the MAC address of an
1259 interface for example)
1260 */
8481f490
VB
1261 if (cfg->g_config.c_cid_string != NULL) {
1262 log_debug("localchassis", "use specified chassis ID string");
1263 free(LOCAL_CHASSIS(cfg)->c_id);
1264 if (!(LOCAL_CHASSIS(cfg)->c_id = strdup(cfg->g_config.c_cid_string)))
1265 fatal("localchassis", NULL);
1266 LOCAL_CHASSIS(cfg)->c_id_len = strlen(cfg->g_config.c_cid_string);
1267 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
1268 }
b4ac8083 1269 if (LOCAL_CHASSIS(cfg)->c_id == NULL) {
8b549648
VB
1270 log_debug("localchassis",
1271 "no chassis ID is currently set, use chassis name");
8481f490 1272 if (!(LOCAL_CHASSIS(cfg)->c_id = strdup(LOCAL_CHASSIS(cfg)->c_name)))
6f8925be 1273 fatal("localchassis", NULL);
8481f490
VB
1274 LOCAL_CHASSIS(cfg)->c_id_len = strlen(LOCAL_CHASSIS(cfg)->c_name);
1275 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
6e75df87
VB
1276 }
1277}
1278
0484f180 1279void
6e75df87
VB
1280lldpd_update_localports(struct lldpd *cfg)
1281{
6e75df87 1282 struct lldpd_hardware *hardware;
6e75df87 1283
9e5d99d4 1284 log_debug("localchassis", "update information for local ports");
6f8925be 1285
6e75df87
VB
1286 /* h_flags is set to 0 for each port. If the port is updated, h_flags
1287 * will be set to a non-zero value. This will allow us to clean up any
1288 * non up-to-date port */
8b549648
VB
1289 TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries)
1290 hardware->h_flags = 0;
43c02e7b 1291
bdfe4193 1292 TRACE(LLDPD_INTERFACES_UPDATE());
e12c2365 1293 interfaces_update(cfg);
bfdc2a8c 1294 lldpd_cleanup(cfg);
579bedd5 1295 lldpd_reset_timer(cfg);
6e75df87 1296}
43c02e7b 1297
d6e889b6 1298void
6e75df87
VB
1299lldpd_loop(struct lldpd *cfg)
1300{
1301 /* Main loop.
6e75df87 1302 1. Update local ports information
579bedd5 1303 2. Update local chassis information
6e75df87 1304 */
6f8925be 1305 log_debug("loop", "start new loop");
8b549648 1306 if (!cfg->g_config.c_cap_override) LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
579bedd5
VB
1307 /* Information for local ports is triggered even when it is possible to
1308 * update them on some other event because we want to refresh them if we
1309 * missed something. */
1310 log_debug("loop", "update information for local ports");
1311 lldpd_update_localports(cfg);
6f8925be 1312 log_debug("loop", "update information for local chassis");
6e75df87 1313 lldpd_update_localchassis(cfg);
66879d46 1314 lldpd_count_neighbors(cfg);
43c02e7b
VB
1315}
1316
8888d191 1317static void
d6e889b6 1318lldpd_exit(struct lldpd *cfg)
43c02e7b 1319{
a1c9d4be 1320 char *lockname = NULL;
6e75df87 1321 struct lldpd_hardware *hardware, *hardware_next;
9e5d99d4 1322 log_debug("main", "exit lldpd");
e770b720 1323
8b549648 1324 TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries)
e770b720
VB
1325 lldpd_send_shutdown(hardware);
1326
a1c9d4be
VB
1327 if (asprintf(&lockname, "%s.lock", cfg->g_ctlname) != -1) {
1328 priv_ctl_cleanup(lockname);
1329 free(lockname);
1330 }
d6e889b6 1331 close(cfg->g_ctl);
0262adbb 1332 priv_ctl_cleanup(cfg->g_ctlname);
9e5d99d4 1333 log_debug("main", "cleanup hardware information");
d6e889b6 1334 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
6e75df87
VB
1335 hardware = hardware_next) {
1336 hardware_next = TAILQ_NEXT(hardware, h_entries);
9e5d99d4 1337 log_debug("main", "cleanup interface %s", hardware->h_ifname);
ef3707da 1338 lldpd_remote_cleanup(hardware, NULL, 1);
d6e889b6 1339 lldpd_hardware_cleanup(cfg, hardware);
43c02e7b 1340 }
24cb9684 1341 interfaces_cleanup(cfg);
9c93725f 1342 lldpd_port_cleanup(cfg->g_default_local_port, 1);
24cb9684 1343 lldpd_all_chassis_cleanup(cfg);
9c93725f 1344 free(cfg->g_default_local_port);
13181ede 1345 free(cfg->g_config.c_platform);
f144d837 1346 levent_shutdown(cfg);
43c02e7b
VB
1347}
1348
51534ef3
VB
1349/**
1350 * Run lldpcli to configure lldpd.
1351 *
1352 * @return PID of running lldpcli or -1 if error.
1353 */
1354static pid_t
8b549648
VB
1355lldpd_configure(int use_syslog, int debug, const char *path, const char *ctlname,
1356 const char *config_path)
51534ef3 1357{
45bf0bd0 1358 pid_t lldpcli = vfork();
51534ef3
VB
1359 int devnull;
1360
9856f279
VB
1361 char sdebug[debug + 4];
1362 if (use_syslog)
1363 strlcpy(sdebug, "-s", 3);
1364 else {
1365 /* debug = 0 -> -sd */
1366 /* debug = 1 -> -sdd */
1367 /* debug = 2 -> -sddd */
1368 memset(sdebug, 'd', sizeof(sdebug));
1369 sdebug[debug + 3] = '\0';
8b549648
VB
1370 sdebug[0] = '-';
1371 sdebug[1] = 's';
9856f279 1372 }
45bf0bd0
VB
1373 log_debug("main", "invoke %s %s", path, sdebug);
1374
51534ef3
VB
1375 switch (lldpcli) {
1376 case -1:
1377 log_warn("main", "unable to fork");
1378 return -1;
1379 case 0:
1380 /* Child, exec lldpcli */
1381 if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
8b549648
VB
1382 dup2(devnull, STDIN_FILENO);
1383 dup2(devnull, STDOUT_FILENO);
51534ef3
VB
1384 if (devnull > 2) close(devnull);
1385
94f6ce25 1386 if (config_path) {
8b549648
VB
1387 execl(path, "lldpcli", sdebug, "-u", ctlname, "-C",
1388 config_path, "resume", (char *)NULL);
94f6ce25 1389 } else {
8b549648
VB
1390 execl(path, "lldpcli", sdebug, "-u", ctlname, "-C",
1391 SYSCONFDIR "/lldpd.conf", "-C",
1392 SYSCONFDIR "/lldpd.d", "resume", (char *)NULL);
94f6ce25
TT
1393 }
1394
45bf0bd0 1395 log_warn("main", "unable to execute %s", path);
8b549648
VB
1396 log_warnx("main",
1397 "configuration is incomplete, lldpd needs to be unpaused");
51534ef3 1398 }
45bf0bd0 1399 _exit(127);
51534ef3
VB
1400 break;
1401 default:
1402 /* Father, don't do anything stupid */
1403 return lldpcli;
1404 }
1405 /* Should not be here */
1406 return -1;
1407}
1408
8b549648
VB
1409struct intint {
1410 int a;
1411 int b;
1412};
1413static const struct intint filters[] = { { 0, 0 },
1414 { 1,
1415 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_OUTGOING_FILTER |
1416 SMART_OUTGOING_ONE_PROTO },
1417 { 2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO },
1418 { 3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1419 { 4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER },
1420 { 5, SMART_INCOMING_FILTER }, { 6, SMART_OUTGOING_FILTER },
1421 { 7,
1422 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1423 SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER |
1424 SMART_OUTGOING_ONE_PROTO },
1425 { 8,
1426 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1427 SMART_INCOMING_ONE_NEIGH },
1428 { 9,
1429 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER |
1430 SMART_OUTGOING_ONE_PROTO },
8482abe9
VB
1431 { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1432 { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH },
8b549648
VB
1433 { 12,
1434 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER |
1435 SMART_OUTGOING_ONE_NEIGH },
1436 { 13,
1437 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER },
1438 { 14,
1439 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_OUTGOING_FILTER |
1440 SMART_OUTGOING_ONE_NEIGH },
1441 { 15,
1442 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_OUTGOING_FILTER },
1443 { 16,
1444 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1445 SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER |
1446 SMART_OUTGOING_ONE_NEIGH },
1447 { 17,
1448 SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1449 SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER },
1450 { 18,
1451 SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1452 { 19,
1453 SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1454 { -1, 0 } };
8482abe9 1455
e66b7f34 1456#ifndef HOST_OS_OSX
322aafc9
VB
1457/**
1458 * Tell if we have been started by systemd.
1459 */
1460static int
1461lldpd_started_by_systemd()
1462{
8b549648 1463# ifdef HOST_OS_LINUX
322aafc9 1464 int fd = -1;
0800ce57
VB
1465 int ret = 0;
1466 size_t path_length;
50d0c223
VB
1467 union sockaddr_union {
1468 struct sockaddr sa;
1469 struct sockaddr_un sun;
1470 } socket_addr = {
1471 .sun.sun_family = AF_UNIX,
0800ce57 1472 };
50d0c223
VB
1473 const char *socket_path = getenv("NOTIFY_SOCKET");
1474 if (!socket_path || (socket_path[0] != '/' && socket_path[0] != '@') ||
1475 (path_length = strlen(socket_path)) < 2)
0800ce57
VB
1476 goto done;
1477
50d0c223 1478 if (path_length >= sizeof(socket_addr.sun.sun_path)) {
0800ce57
VB
1479 log_warnx("main", "systemd notification socket is too long");
1480 goto done;
1481 }
50d0c223
VB
1482 memcpy(socket_addr.sun.sun_path, socket_path, path_length);
1483
1484 /* Support for abstract socket */
1485 if (socket_addr.sun.sun_path[0] == '@') socket_addr.sun.sun_path[0] = 0;
322aafc9
VB
1486
1487 log_debug("main", "running with systemd, don't fork but signal ready");
50d0c223 1488 if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
322aafc9 1489 log_warn("main", "unable to open systemd notification socket %s",
50d0c223 1490 socket_path);
0800ce57
VB
1491 goto done;
1492 }
50d0c223
VB
1493 if (connect(fd, &socket_addr.sa,
1494 offsetof(struct sockaddr_un, sun_path) + path_length) != 0) {
0800ce57
VB
1495 log_warn("main", "unable to connect to systemd notification socket");
1496 goto done;
322aafc9 1497 }
f540397c 1498 char ready[] = "READY=1";
0800ce57
VB
1499 ssize_t written = write(fd, ready, sizeof ready - 1);
1500 if (written != (ssize_t)sizeof ready - 1) {
322aafc9 1501 log_warn("main", "unable to send notification to systemd");
0800ce57 1502 goto done;
322aafc9 1503 }
0800ce57
VB
1504 unsetenv("NOTIFY_SOCKET");
1505 ret = 1;
1506done:
1507 if (fd >= 0) close(fd);
1508 return ret;
8b549648 1509# else
322aafc9 1510 return 0;
8b549648 1511# endif
2591639f 1512}
e66b7f34 1513#endif
2591639f 1514
2472bfda
VB
1515#ifdef HOST_OS_LINUX
1516static void
1517version_convert(const char *sversion, unsigned iversion[], size_t n)
1518{
1519 const char *p = sversion;
1520 char *end;
1521 for (size_t i = 0; i < n; i++) {
1522 iversion[i] = strtol(p, &end, 10);
1523 if (*end != '.') break;
1524 p = end + 1;
1525 }
1526}
1527
1528static void
1529version_check(void)
1530{
1531 struct utsname uts;
1532 if (uname(&uts) == -1) return;
1533 unsigned version_min[3] = {};
1534 unsigned version_cur[3] = {};
1535 version_convert(uts.release, version_cur, 3);
1536 version_convert(MIN_LINUX_KERNEL_VERSION, version_min, 3);
1537 if (version_min[0] > version_cur[0] ||
1538 (version_min[0] == version_cur[0] && version_min[1] > version_cur[1]) ||
05a708dc
VB
1539 (version_min[0] == version_cur[0] && version_min[1] == version_cur[1] &&
1540 version_min[2] > version_cur[2])) {
2472bfda
VB
1541 log_warnx("lldpd", "minimal kernel version required is %s, got %s",
1542 MIN_LINUX_KERNEL_VERSION, uts.release);
8b549648
VB
1543 log_warnx("lldpd",
1544 "lldpd may be unable to detect bonds and bridges correctly");
1545# ifndef ENABLE_OLDIES
2472bfda 1546 log_warnx("lldpd", "consider recompiling with --enable-oldies option");
8b549648 1547# endif
2472bfda
VB
1548 }
1549}
1550#else
8b549648
VB
1551static void
1552version_check(void)
1553{
1554}
2472bfda
VB
1555#endif
1556
43c02e7b 1557int
1e0d651f 1558lldpd_main(int argc, char *argv[], char *envp[])
43c02e7b
VB
1559{
1560 struct lldpd *cfg;
77507b69 1561 struct lldpd_chassis *lchassis;
9856f279 1562 int ch, debug = 0, use_syslog = 1, daemonize = 1;
8a378b16 1563 const char *errstr;
e809a587
VB
1564#ifdef USE_SNMP
1565 int snmp = 0;
8b549648 1566 const char *agentx = NULL; /* AgentX socket */
e809a587 1567#endif
1c1c519f 1568 const char *ctlname = NULL;
d4e4c804 1569 char *mgmtp = NULL;
8481f490 1570 char *cidp = NULL;
ba85f9f4 1571 char *interfaces = NULL;
984bbcbc
VB
1572 /* We do not want more options here. Please add them in lldpcli instead
1573 * unless there is a very good reason. Most command-line options will
1574 * get deprecated at some point. */
8b549648
VB
1575 char *popt,
1576 opts[] = "H:vhkrdD:p:xX:m:u:4:6:I:C:p:M:P:S:iL:O:@ ";
de1b1b3a 1577 int i, found, advertise_version = 1;
89840df0 1578#ifdef ENABLE_LLDPMED
e809a587 1579 int lldpmed = 0, noinventory = 0;
d6d42d56 1580 int enable_fast_start = 1;
89840df0 1581#endif
d4afb919
VB
1582 char *descr_override = NULL;
1583 char *platform_override = NULL;
c036b15d 1584 char *lsb_release = NULL;
51534ef3 1585 const char *lldpcli = LLDPCLI_PATH;
a3c9f9ee 1586#ifndef HOST_OS_OSX
7b6cbfe2 1587 const char *pidfile = LLDPD_PID_FILE;
a3c9f9ee 1588#endif
8482abe9 1589 int smart = 15;
efa6a7a3 1590 int receiveonly = 0, version = 0;
2e91d1a1 1591 int ctl;
94f6ce25 1592 const char *config_file = NULL;
2e91d1a1 1593
28bf4022 1594#ifdef ENABLE_PRIVSEP
2e91d1a1
VB
1595 /* Non privileged user */
1596 struct passwd *user;
1597 struct group *group;
1598 uid_t uid;
1599 gid_t gid;
28bf4022 1600#endif
43c02e7b
VB
1601
1602 saved_argv = argv;
1603
1e0d651f
VB
1604#if HAVE_SETPROCTITLE_INIT
1605 setproctitle_init(argc, argv, envp);
1606#endif
1607
43c02e7b
VB
1608 /*
1609 * Get and parse command line options
1610 */
d68f1b81 1611 if ((popt = strchr(opts, '@')) != NULL) {
8b549648 1612 for (i = 0; protos[i].mode != 0 && *popt != '\0'; i++)
d68f1b81
VB
1613 *(popt++) = protos[i].arg;
1614 *popt = '\0';
1615 }
43c02e7b
VB
1616 while ((ch = getopt(argc, argv, opts)) != -1) {
1617 switch (ch) {
b162b740
VB
1618 case 'h':
1619 usage();
1620 break;
96e49a40 1621 case 'v':
efa6a7a3 1622 version++;
96e49a40 1623 break;
43c02e7b 1624 case 'd':
9856f279
VB
1625 if (daemonize)
1626 daemonize = 0;
1627 else if (use_syslog)
1628 use_syslog = 0;
1629 else
1630 debug++;
43c02e7b 1631 break;
9e5d99d4
VB
1632 case 'D':
1633 log_accept(optarg);
1634 break;
a3c9f9ee 1635#ifndef HOST_OS_OSX
7b6cbfe2
VB
1636 case 'p':
1637 pidfile = optarg;
1638 break;
a3c9f9ee 1639#endif
537a8043
VB
1640 case 'r':
1641 receiveonly = 1;
1642 break;
d4e4c804 1643 case 'm':
9b11faad
VB
1644 if (mgmtp) {
1645 fprintf(stderr, "-m can only be used once\n");
1646 usage();
1647 }
abc04205 1648 mgmtp = strdup(optarg);
43c02e7b 1649 break;
0262adbb 1650 case 'u':
9b11faad
VB
1651 if (ctlname) {
1652 fprintf(stderr, "-u can only be used once\n");
1653 usage();
1654 }
0262adbb
ZM
1655 ctlname = optarg;
1656 break;
ba85f9f4 1657 case 'I':
9b11faad
VB
1658 if (interfaces) {
1659 fprintf(stderr, "-I can only be used once\n");
1660 usage();
1661 }
abc04205 1662 interfaces = strdup(optarg);
ba85f9f4 1663 break;
5339e725 1664 case 'C':
9b11faad
VB
1665 if (cidp) {
1666 fprintf(stderr, "-C can only be used once\n");
1667 usage();
1668 }
abc04205 1669 cidp = strdup(optarg);
5339e725 1670 break;
51534ef3 1671 case 'L':
8b549648
VB
1672 if (strlen(optarg))
1673 lldpcli = optarg;
1674 else
1675 lldpcli = NULL;
037d4c8e 1676 break;
de1b1b3a
VB
1677 case 'k':
1678 advertise_version = 0;
1679 break;
e809a587 1680#ifdef ENABLE_LLDPMED
115ff55c 1681 case 'M':
8a378b16
VB
1682 lldpmed = strtonum(optarg, 1, 4, &errstr);
1683 if (errstr) {
8b549648
VB
1684 fprintf(stderr,
1685 "-M requires an argument between 1 and 4\n");
89840df0 1686 usage();
e809a587 1687 }
89840df0 1688 break;
e809a587 1689 case 'i':
e809a587 1690 noinventory = 1;
115ff55c 1691 break;
e809a587 1692#else
115ff55c
VB
1693 case 'M':
1694 case 'i':
e809a587
VB
1695 fprintf(stderr, "LLDP-MED support is not built-in\n");
1696 usage();
e809a587 1697 break;
115ff55c 1698#endif
e809a587 1699#ifdef USE_SNMP
bbea66e1
V
1700 case 'x':
1701 snmp = 1;
1702 break;
1703 case 'X':
9b11faad
VB
1704 if (agentx) {
1705 fprintf(stderr, "-X can only be used once\n");
1706 usage();
1707 }
43c02e7b 1708 snmp = 1;
bbea66e1
V
1709 agentx = optarg;
1710 break;
e809a587 1711#else
bbea66e1
V
1712 case 'x':
1713 case 'X':
e809a587
VB
1714 fprintf(stderr, "SNMP support is not built-in\n");
1715 usage();
1716#endif
43c02e7b 1717 break;
8b549648 1718 case 'S':
9b11faad
VB
1719 if (descr_override) {
1720 fprintf(stderr, "-S can only be used once\n");
1721 usage();
1722 }
8b549648
VB
1723 descr_override = strdup(optarg);
1724 break;
d4afb919 1725 case 'P':
9b11faad
VB
1726 if (platform_override) {
1727 fprintf(stderr, "-P can only be used once\n");
1728 usage();
1729 }
d4afb919
VB
1730 platform_override = strdup(optarg);
1731 break;
42b39485 1732 case 'H':
8b549648
VB
1733 smart = strtonum(optarg, 0,
1734 sizeof(filters) / sizeof(filters[0]), &errstr);
8a378b16 1735 if (errstr) {
8b549648
VB
1736 fprintf(stderr,
1737 "-H requires an int between 0 and %zu\n",
1738 sizeof(filters) / sizeof(filters[0]));
8a378b16
VB
1739 usage();
1740 }
42b39485 1741 break;
94f6ce25
TT
1742 case 'O':
1743 if (config_file) {
1744 fprintf(stderr, "-O can only be used once\n");
1745 usage();
1746 }
1747 config_file = optarg;
1748 break;
43c02e7b
VB
1749 default:
1750 found = 0;
8b549648 1751 for (i = 0; protos[i].mode != 0; i++) {
43c02e7b 1752 if (ch == protos[i].arg) {
b8a802bc
VB
1753 found = 1;
1754 protos[i].enabled++;
43c02e7b
VB
1755 }
1756 }
8b549648 1757 if (!found) usage();
43c02e7b
VB
1758 }
1759 }
8482abe9 1760
efa6a7a3
VB
1761 if (version) {
1762 version_display(stdout, "lldpd", version > 1);
1763 exit(0);
1764 }
1765
59ca5ddb 1766 if (ctlname == NULL) ctlname = LLDPD_CTL_SOCKET;
1c1c519f 1767
8482abe9 1768 /* Set correct smart mode */
8b549648
VB
1769 for (i = 0; (filters[i].a != -1) && (filters[i].a != smart); i++)
1770 ;
8482abe9
VB
1771 if (filters[i].a == -1) {
1772 fprintf(stderr, "Incorrect mode for -H\n");
1773 usage();
1774 }
1775 smart = filters[i].b;
6f8925be 1776
9856f279 1777 log_init(use_syslog, debug, __progname);
8b549648 1778 tzset(); /* Get timezone info before chroot */
52767c4d
VB
1779 if (use_syslog && daemonize) {
1780 /* So, we use syslog and we daemonize (or we are started by
7a730aff 1781 * systemd). No need to continue writing to stdout. */
52767c4d 1782 int fd;
e1926a99
VB
1783 /* coverity[resource_leak]
1784 fd may be leaked if < 2, it's expected */
52767c4d
VB
1785 if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
1786 dup2(fd, STDIN_FILENO);
1787 dup2(fd, STDOUT_FILENO);
1788 dup2(fd, STDERR_FILENO);
1789 if (fd > 2) close(fd);
1790 }
1791 }
f98e5666 1792 log_debug("main", "lldpd " PACKAGE_VERSION " starting...");
2472bfda 1793 version_check();
73061a21
VB
1794#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1795 fatalx("main", "fuzzing enabled, unsafe for production");
1796#endif
6f8925be 1797
2e91d1a1 1798 /* Grab uid and gid to use for priv sep */
28bf4022 1799#ifdef ENABLE_PRIVSEP
2e91d1a1 1800 if ((user = getpwnam(PRIVSEP_USER)) == NULL)
8b549648
VB
1801 fatalx("main",
1802 "no " PRIVSEP_USER
1803 " user for privilege separation, please create it");
2e91d1a1
VB
1804 uid = user->pw_uid;
1805 if ((group = getgrnam(PRIVSEP_GROUP)) == NULL)
8b549648
VB
1806 fatalx("main",
1807 "no " PRIVSEP_GROUP
1808 " group for privilege separation, please create it");
2e91d1a1 1809 gid = group->gr_gid;
28bf4022 1810#endif
2e91d1a1
VB
1811
1812 /* Create and setup socket */
8172214f 1813 int retry = 1;
6f8925be 1814 log_debug("main", "creating control socket");
0262adbb 1815 while ((ctl = ctl_create(ctlname)) == -1) {
8172214f
VB
1816 if (retry-- && errno == EADDRINUSE) {
1817 /* Check if a daemon is really listening */
1818 int tfd;
8b549648
VB
1819 log_info("main",
1820 "unable to create control socket because it already exists");
8172214f 1821 log_info("main", "check if another instance is running");
0262adbb 1822 if ((tfd = ctl_connect(ctlname)) != -1) {
8172214f
VB
1823 /* Another instance is running */
1824 close(tfd);
8b549648
VB
1825 log_warnx("main",
1826 "another instance is running, please stop it");
a87db231 1827 fatalx("main", "giving up");
8172214f
VB
1828 } else if (errno == ECONNREFUSED) {
1829 /* Nobody is listening */
8b549648
VB
1830 log_info("main",
1831 "old control socket is present, clean it");
0262adbb 1832 ctl_cleanup(ctlname);
8172214f
VB
1833 continue;
1834 }
8b549648
VB
1835 log_warn("main",
1836 "cannot determine if another daemon is already running");
a87db231 1837 fatalx("main", "giving up");
8172214f 1838 }
ba54dbd0 1839 log_warn("main", "unable to create control socket at %s", ctlname);
a87db231 1840 fatalx("main", "giving up");
2e91d1a1 1841 }
28bf4022 1842#ifdef ENABLE_PRIVSEP
0262adbb 1843 if (chown(ctlname, uid, gid) == -1)
6f8925be 1844 log_warn("main", "unable to chown control socket");
8b549648
VB
1845 if (chmod(ctlname, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) ==
1846 -1)
6f8925be 1847 log_warn("main", "unable to chmod control socket");
28bf4022 1848#endif
2e91d1a1 1849
a1c9d4be
VB
1850 /* Create associated advisory lock file */
1851 char *lockname = NULL;
1852 int fd;
1853 if (asprintf(&lockname, "%s.lock", ctlname) == -1)
1854 fatal("main", "cannot build lock name");
8b549648 1855 if ((fd = open(lockname, O_CREAT | O_RDWR, 0000)) == -1)
a1c9d4be
VB
1856 fatal("main", "cannot create lock file for control socket");
1857 close(fd);
1858#ifdef ENABLE_PRIVSEP
1859 if (chown(lockname, uid, gid) == -1)
1860 log_warn("main", "unable to chown control socket lock");
1861 if (chmod(lockname,
8b549648 1862 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) == -1)
a1c9d4be
VB
1863 log_warn("main", "unable to chmod control socket lock");
1864#endif
1865 free(lockname);
1866
595184b0
VB
1867 /* Disable SIGPIPE */
1868 signal(SIGPIPE, SIG_IGN);
1869
003620d3
ST
1870 /* Disable SIGHUP, until handlers are installed */
1871 signal(SIGHUP, SIG_IGN);
1872
7a730aff 1873 /* Daemonization, unless started by systemd or launchd or debug */
a14e6dca 1874#ifndef HOST_OS_OSX
17f7469e 1875 if (!lldpd_started_by_systemd() && daemonize) {
a14e6dca
VB
1876 int pid;
1877 char *spid;
269c0188 1878 log_debug("main", "going into background");
8b549648
VB
1879 if (daemon(0, 1) != 0) fatal("main", "failed to detach daemon");
1880 if ((pid = open(pidfile, O_TRUNC | O_CREAT | O_WRONLY, 0666)) == -1)
1881 fatal("main",
1882 "unable to open pid file " LLDPD_PID_FILE
a14e6dca
VB
1883 " (or the specified one)");
1884 if (asprintf(&spid, "%d\n", getpid()) == -1)
8b549648
VB
1885 fatal("main",
1886 "unable to create pid file " LLDPD_PID_FILE
a14e6dca
VB
1887 " (or the specified one)");
1888 if (write(pid, spid, strlen(spid)) == -1)
8b549648
VB
1889 fatal("main",
1890 "unable to write pid file " LLDPD_PID_FILE
a14e6dca
VB
1891 " (or the specified one)");
1892 free(spid);
1893 close(pid);
1894 }
1895#endif
1896
a14e6dca
VB
1897 /* Configuration with lldpcli */
1898 if (lldpcli) {
94f6ce25 1899 if (!config_file) {
8b549648
VB
1900 log_debug("main",
1901 "invoking lldpcli for default configuration locations");
94f6ce25 1902 } else {
8b549648
VB
1903 log_debug("main",
1904 "invoking lldpcli for user supplied configuration location");
94f6ce25 1905 }
8b549648
VB
1906 if (lldpd_configure(use_syslog, debug, lldpcli, ctlname, config_file) ==
1907 -1)
a14e6dca
VB
1908 fatal("main", "unable to spawn lldpcli");
1909 }
1910
ae87586a
MT
1911 /* Try to read system information from /etc/os-release if possible.
1912 Fall back to lsb_release for compatibility. */
6f8925be 1913 log_debug("main", "get OS/LSB release information");
ae87586a
MT
1914 lsb_release = lldpd_get_os_release();
1915 if (!lsb_release) {
1916 lsb_release = lldpd_get_lsb_release();
1917 }
c036b15d 1918
e47140de
VB
1919 log_debug("main", "initialize privilege separation");
1920#ifdef ENABLE_PRIVSEP
1921 priv_init(PRIVSEP_CHROOT, ctl, uid, gid);
1922#else
8c69002b 1923 priv_init();
e47140de
VB
1924#endif
1925
d6e889b6 1926 /* Initialization of global configuration */
8b549648 1927 if ((cfg = (struct lldpd *)calloc(1, sizeof(struct lldpd))) == NULL)
6f8925be 1928 fatal("main", NULL);
43c02e7b 1929
9da663f7 1930 lldpd_alloc_default_local_port(cfg);
0262adbb 1931 cfg->g_ctlname = ctlname;
2e91d1a1 1932 cfg->g_ctl = ctl;
8ec333bd
VB
1933 cfg->g_config.c_mgmt_pattern = mgmtp;
1934 cfg->g_config.c_cid_pattern = cidp;
1935 cfg->g_config.c_iface_pattern = interfaces;
1936 cfg->g_config.c_smart = smart;
8b549648 1937 if (lldpcli) cfg->g_config.c_paused = 1;
8ec333bd 1938 cfg->g_config.c_receiveonly = receiveonly;
74f55c2e 1939 cfg->g_config.c_tx_interval = LLDPD_TX_INTERVAL * 1000;
c10302a3 1940 cfg->g_config.c_tx_hold = LLDPD_TX_HOLD;
71b0f981 1941 cfg->g_config.c_ttl = cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold;
a73e04f4 1942 cfg->g_config.c_ttl = MIN((cfg->g_config.c_ttl + 999) / 1000, 65535);
42589660 1943 cfg->g_config.c_max_neighbors = LLDPD_MAX_NEIGHBORS;
be511d00 1944#ifdef ENABLE_LLDPMED
486a6133 1945 cfg->g_config.c_enable_fast_start = enable_fast_start;
b9de0ca6 1946 cfg->g_config.c_tx_fast_init = LLDPD_FAST_INIT;
1947 cfg->g_config.c_tx_fast_interval = LLDPD_FAST_TX_INTERVAL;
be511d00 1948#endif
d6e889b6
VB
1949#ifdef USE_SNMP
1950 cfg->g_snmp = snmp;
1951 cfg->g_snmp_agentx = agentx;
1952#endif /* USE_SNMP */
8b549648 1953 cfg->g_config.c_bond_slave_src_mac_type =
0e29d794 1954 LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED;
43c02e7b
VB
1955
1956 /* Get ioctl socket */
6f8925be 1957 log_debug("main", "get an ioctl socket");
43c02e7b 1958 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
6f8925be 1959 fatal("main", "failed to get ioctl socket");
43c02e7b 1960
c036b15d 1961 /* Description */
8b549648
VB
1962 if (!(cfg->g_config.c_advertise_version = advertise_version) && lsb_release &&
1963 lsb_release[strlen(lsb_release) - 1] == '\n')
c036b15d
VB
1964 lsb_release[strlen(lsb_release) - 1] = '\0';
1965 cfg->g_lsb_release = lsb_release;
8b549648 1966 if (descr_override) cfg->g_config.c_description = descr_override;
40ce835b 1967
8b549648 1968 if (platform_override) cfg->g_config.c_platform = platform_override;
d4afb919 1969
43c02e7b 1970 /* Set system capabilities */
6f8925be 1971 log_debug("main", "set system capabilities");
8b549648
VB
1972 if ((lchassis = (struct lldpd_chassis *)calloc(1,
1973 sizeof(struct lldpd_chassis))) == NULL)
6f8925be 1974 fatal("localchassis", NULL);
ca838758 1975 cfg->g_config.c_cap_advertise = 1;
4c8e6e37 1976 cfg->g_config.c_cap_override = 0;
8b549648
VB
1977 lchassis->c_cap_available =
1978 LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER | LLDP_CAP_STATION;
1c2217aa 1979 cfg->g_config.c_mgmt_advertise = 1;
e6b36c87 1980 TAILQ_INIT(&lchassis->c_mgmt);
89840df0
VB
1981#ifdef ENABLE_LLDPMED
1982 if (lldpmed > 0) {
4b292b55 1983 if (lldpmed == LLDP_MED_CLASS_III)
77507b69
VB
1984 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
1985 lchassis->c_med_type = lldpmed;
8b549648
VB
1986 lchassis->c_med_cap_available = LLDP_MED_CAP_CAP | LLDP_MED_CAP_IV |
1987 LLDP_MED_CAP_LOCATION | LLDP_MED_CAP_POLICY | LLDP_MED_CAP_MDI_PSE |
1988 LLDP_MED_CAP_MDI_PD;
8ec333bd 1989 cfg->g_config.c_noinventory = noinventory;
42bddd41 1990 } else
8ec333bd 1991 cfg->g_config.c_noinventory = 1;
89840df0 1992#endif
43c02e7b 1993
6f8925be 1994 log_debug("main", "initialize protocols");
43c02e7b 1995 cfg->g_protocols = protos;
8b549648 1996 for (i = 0; protos[i].mode != 0; i++) {
b8a802bc
VB
1997
1998 /* With -ll, disable LLDP */
8b549648 1999 if (protos[i].mode == LLDPD_MODE_LLDP) protos[i].enabled %= 3;
b8a802bc
VB
2000 /* With -ccc force CDPV2, enable CDPV1 */
2001 if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled == 3) {
2002 protos[i].enabled = 1;
2003 }
2004 /* With -cc force CDPV1, enable CDPV2 */
2005 if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 2) {
2006 protos[i].enabled = 1;
2007 }
2008
611aba00
MS
2009 /* With -cccc disable CDPV1, enable CDPV2 */
2010 if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled >= 4) {
2011 protos[i].enabled = 0;
2012 }
2013
2014 /* With -cccc disable CDPV1, enable CDPV2; -ccccc will force CDPv2 */
2015 if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 4) {
2016 protos[i].enabled = 1;
2017 }
2018
0c877af0 2019 if (protos[i].enabled > 1)
8b549648
VB
2020 log_info("main", "protocol %s enabled and forced",
2021 protos[i].name);
0c877af0 2022 else if (protos[i].enabled)
6f8925be 2023 log_info("main", "protocol %s enabled", protos[i].name);
0c877af0 2024 else
6f8925be 2025 log_info("main", "protocol %s disabled", protos[i].name);
8b549648 2026 }
43c02e7b
VB
2027
2028 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
2029 TAILQ_INIT(&cfg->g_chassis);
2030 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
9898ac07 2031 lchassis->c_refcount++; /* We should always keep a reference to local chassis */
43c02e7b 2032
d6e889b6 2033 /* Main loop */
6f8925be 2034 log_debug("main", "start main loop");
d6e889b6 2035 levent_loop(cfg);
24cb9684 2036 lchassis->c_refcount--;
d6e889b6 2037 lldpd_exit(cfg);
8262175d 2038 free(cfg);
43c02e7b
VB
2039
2040 return (0);
2041}