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