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