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