]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
Add a receive-only mode with "-r" switch.
[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);
6b41bbd3 779 lldpd_hide_all(cfg); /* Immediatly hide */
6e75df87
VB
780 free(buffer);
781 break;
43c02e7b 782 }
77d7090e
VB
783 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
784 callback;
785 callback = callback_next) {
786 /* Callback function can use TAILQ_REMOVE */
787 callback_next = TAILQ_NEXT(callback, next);
788 if (FD_ISSET(callback->fd, &rfds))
789 callback->function(cfg, callback);
43c02e7b
VB
790 }
791
792#ifdef USE_SNMP
793 if (cfg->g_snmp) {
794 run_alarms();
795 netsnmp_check_outstanding_agent_requests();
796 }
797#endif /* USE_SNMP */
798 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
799}
800
8888d191 801static void
43c02e7b
VB
802lldpd_send_all(struct lldpd *cfg)
803{
804 struct lldpd_hardware *hardware;
77507b69 805 struct lldpd_port *port;
0d86e62f 806 int i, sent;
f7db0dd8 807
43c02e7b
VB
808 cfg->g_lastsent = time(NULL);
809 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
810 /* Ignore if interface is down */
6e75df87 811 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b
VB
812 continue;
813
0d86e62f 814 sent = 0;
43c02e7b
VB
815 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
816 if (!cfg->g_protocols[i].enabled)
817 continue;
77507b69 818 /* We send only if we have at least one remote system
0c877af0
VB
819 * speaking this protocol or if the protocol is forced */
820 if (cfg->g_protocols[i].enabled > 1) {
821 cfg->g_protocols[i].send(cfg, hardware);
822 sent++;
823 continue;
824 }
77507b69 825 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
42b39485
VB
826 /* If this remote port is disabled, we don't
827 * consider it */
8482abe9
VB
828 if (port->p_hidden_out &&
829 (cfg->g_smart & SMART_OUTGOING_FILTER))
42b39485 830 continue;
77507b69
VB
831 if (port->p_protocol ==
832 cfg->g_protocols[i].mode) {
833 cfg->g_protocols[i].send(cfg,
834 hardware);
0c877af0 835 sent++;
77507b69
VB
836 break;
837 }
838 }
43c02e7b 839 }
77507b69
VB
840
841 if (!sent)
842 /* Nothing was sent for this port, let's speak LLDP */
843 cfg->g_protocols[0].send(cfg,
844 hardware);
43c02e7b
VB
845 }
846}
847
89840df0 848#ifdef ENABLE_LLDPMED
8888d191 849static void
89840df0
VB
850lldpd_med(struct lldpd_chassis *chassis)
851{
852 free(chassis->c_med_hw);
853 free(chassis->c_med_fw);
854 free(chassis->c_med_sn);
855 free(chassis->c_med_manuf);
856 free(chassis->c_med_model);
857 free(chassis->c_med_asset);
858 chassis->c_med_hw = dmi_hw();
859 chassis->c_med_fw = dmi_fw();
860 chassis->c_med_sn = dmi_sn();
861 chassis->c_med_manuf = dmi_manuf();
862 chassis->c_med_model = dmi_model();
863 chassis->c_med_asset = dmi_asset();
864}
865#endif
866
8888d191 867static void
6e75df87 868lldpd_update_localchassis(struct lldpd *cfg)
43c02e7b 869{
6e75df87
VB
870 struct utsname un;
871 char *hp;
43c02e7b
VB
872 int f;
873 char status;
6e75df87 874 struct lldpd_hardware *hardware;
43c02e7b
VB
875
876 /* Set system name and description */
6e75df87 877 if (uname(&un) != 0)
43c02e7b 878 fatal("failed to get system information");
b5562b23 879 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b 880 fatal("failed to get system name");
77507b69
VB
881 free(LOCAL_CHASSIS(cfg)->c_name);
882 free(LOCAL_CHASSIS(cfg)->c_descr);
883 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
249644a4 884 fatal(NULL);
40ce835b
ST
885 if (cfg->g_descr_override) {
886 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
887 cfg->g_descr_override) == -1)
de1b1b3a 888 fatal("failed to set full system description");
40ce835b
ST
889 } else {
890 if (cfg->g_advertise_version) {
c036b15d
VB
891 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s%s %s %s",
892 cfg->g_lsb_release?cfg->g_lsb_release:"",
893 un.sysname, un.release, un.machine)
40ce835b
ST
894 == -1)
895 fatal("failed to set full system description");
896 } else {
897 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
c036b15d 898 cfg->g_lsb_release?cfg->g_lsb_release:un.sysname) == -1)
40ce835b
ST
899 fatal("failed to set minimal system description");
900 }
901 }
43c02e7b
VB
902
903 /* Check forwarding */
b5562b23 904 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
e5c5ae41
VB
905 if ((read(f, &status, 1) == 1) && (status == '1'))
906 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_ROUTER;
907 else
908 LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_ROUTER;
43c02e7b
VB
909 close(f);
910 }
89840df0 911#ifdef ENABLE_LLDPMED
77507b69
VB
912 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
913 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
914 lldpd_med(LOCAL_CHASSIS(cfg));
915 free(LOCAL_CHASSIS(cfg)->c_med_sw);
de1b1b3a
VB
916 if (cfg->g_advertise_version)
917 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
918 else
919 LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
89840df0 920#endif
43c02e7b 921
6e75df87
VB
922 /* Set chassis ID if needed */
923 if ((LOCAL_CHASSIS(cfg)->c_id == NULL) &&
924 (hardware = TAILQ_FIRST(&cfg->g_hardware))) {
925 if ((LOCAL_CHASSIS(cfg)->c_id =
926 malloc(sizeof(hardware->h_lladdr))) == NULL)
927 fatal(NULL);
928 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
929 LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
930 memcpy(LOCAL_CHASSIS(cfg)->c_id,
931 hardware->h_lladdr, sizeof(hardware->h_lladdr));
932 }
933}
934
935static void
936lldpd_update_localports(struct lldpd *cfg)
937{
938 struct ifaddrs *ifap;
939 struct lldpd_hardware *hardware;
940 lldpd_ifhandlers ifhs[] = {
849954d7 941 lldpd_ifh_bond, /* Handle bond */
6e75df87 942 lldpd_ifh_eth, /* Handle classic ethernet interfaces */
5994b27d 943#ifdef ENABLE_DOT1
6e75df87 944 lldpd_ifh_vlan, /* Handle VLAN */
5994b27d 945#endif
6e75df87
VB
946 lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
947 NULL
948 };
949 lldpd_ifhandlers *ifh;
950
951 /* h_flags is set to 0 for each port. If the port is updated, h_flags
952 * will be set to a non-zero value. This will allow us to clean up any
953 * non up-to-date port */
43c02e7b
VB
954 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
955 hardware->h_flags = 0;
956
77507b69 957 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
6e75df87
VB
958 if (getifaddrs(&ifap) != 0)
959 fatal("lldpd_update_localports: failed to get interface list");
960
961 /* We will run the list of interfaces through a list of interface
962 * handlers. Each handler will create or update some hardware port (and
963 * will set h_flags to a non zero value. The handler can use the list of
964 * interfaces but this is not mandatory. If the interface handler
965 * handles an interface from the list, it should set ifa_flags to 0 to
966 * let know the other handlers that it took care of this interface. This
967 * means that more specific handlers should be before less specific
968 * ones. */
969 for (ifh = ifhs; *ifh != NULL; ifh++)
970 (*ifh)(cfg, ifap);
43c02e7b 971 freeifaddrs(ifap);
6e75df87 972}
43c02e7b 973
6e75df87
VB
974static void
975lldpd_loop(struct lldpd *cfg)
976{
977 /* Main loop.
978
979 1. Update local ports information
980 2. Clean unwanted (removed) local ports
981 3. Update local chassis information
982 4. Send packets
983 5. Receive packets
42b39485 984 6. Update smart mode
6e75df87 985 */
849954d7 986 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
6e75df87 987 lldpd_update_localports(cfg);
43c02e7b 988 lldpd_cleanup(cfg);
6e75df87 989 lldpd_update_localchassis(cfg);
537a8043
VB
990 if (!cfg->g_receiveonly)
991 lldpd_send_all(cfg);
43c02e7b
VB
992 lldpd_recv_all(cfg);
993}
994
8888d191 995static void
43c02e7b
VB
996lldpd_shutdown(int sig)
997{
998 LLOG_INFO("signal received, exiting");
999 exit(0);
1000}
1001
1002/* For signal handling */
8888d191 1003static struct lldpd *gcfg = NULL;
43c02e7b 1004
8888d191 1005static void
43c02e7b
VB
1006lldpd_exit()
1007{
6e75df87 1008 struct lldpd_hardware *hardware, *hardware_next;
b5562b23
VB
1009 close(gcfg->g_ctl);
1010 priv_ctl_cleanup();
6e75df87
VB
1011 for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL;
1012 hardware = hardware_next) {
1013 hardware_next = TAILQ_NEXT(hardware, h_entries);
1014 lldpd_hardware_cleanup(gcfg, hardware);
43c02e7b
VB
1015 }
1016#ifdef USE_SNMP
1017 if (gcfg->g_snmp)
1018 agent_shutdown();
1019#endif /* USE_SNMP */
1020}
1021
8482abe9
VB
1022struct intint { int a; int b; };
1023static const struct intint filters[] = {
1024 { 0, 0 },
1025 { 1, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1026 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1027 { 2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO },
1028 { 3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1029 { 4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER },
1030 { 5, SMART_INCOMING_FILTER },
1031 { 6, SMART_OUTGOING_FILTER },
1032 { 7, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1033 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1034 { 8, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH },
1035 { 9, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1036 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1037 { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1038 { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH },
1039 { 12, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1040 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1041 { 13, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1042 SMART_OUTGOING_FILTER },
1043 { 14, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1044 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1045 { 15, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1046 SMART_OUTGOING_FILTER },
1047 { 16, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1048 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1049 { 17, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1050 SMART_OUTGOING_FILTER },
1051 { 18, SMART_INCOMING_FILTER |
1052 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1053 { 19, SMART_INCOMING_FILTER |
1054 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1055 { -1, 0 }
1056};
1057
43c02e7b 1058int
2acc1418 1059lldpd_main(int argc, char *argv[])
43c02e7b
VB
1060{
1061 struct lldpd *cfg;
77507b69 1062 struct lldpd_chassis *lchassis;
e809a587
VB
1063 int ch, debug = 0;
1064#ifdef USE_SNMP
1065 int snmp = 0;
bbea66e1 1066 char *agentx = NULL; /* AgentX socket */
e809a587 1067#endif
43c02e7b 1068 char *mgmtp = NULL;
993a6e50 1069 char *popt, opts[] =
537a8043 1070 "H:hkrdxX:m:p:M:S:i@ ";
de1b1b3a 1071 int i, found, advertise_version = 1;
89840df0 1072#ifdef ENABLE_LLDPMED
e809a587 1073 int lldpmed = 0, noinventory = 0;
89840df0 1074#endif
40ce835b 1075 char *descr_override = NULL;
c036b15d 1076 char *lsb_release = NULL;
8482abe9 1077 int smart = 15;
537a8043 1078 int receiveonly = 0;
43c02e7b
VB
1079
1080 saved_argv = argv;
1081
1082 /*
1083 * Get and parse command line options
1084 */
f0bd3505 1085 popt = strchr(opts, '@');
0c877af0 1086 for (i=0; protos[i].mode != 0; i++)
43c02e7b 1087 *(popt++) = protos[i].arg;
43c02e7b
VB
1088 *popt = '\0';
1089 while ((ch = getopt(argc, argv, opts)) != -1) {
1090 switch (ch) {
b162b740
VB
1091 case 'h':
1092 usage();
1093 break;
43c02e7b
VB
1094 case 'd':
1095 debug++;
1096 break;
537a8043
VB
1097 case 'r':
1098 receiveonly = 1;
1099 break;
43c02e7b
VB
1100 case 'm':
1101 mgmtp = optarg;
1102 break;
de1b1b3a
VB
1103 case 'k':
1104 advertise_version = 0;
1105 break;
e809a587 1106#ifdef ENABLE_LLDPMED
115ff55c 1107 case 'M':
89840df0 1108 lldpmed = atoi(optarg);
e809a587
VB
1109 if ((lldpmed < 1) || (lldpmed > 4)) {
1110 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 1111 usage();
e809a587 1112 }
89840df0 1113 break;
e809a587 1114 case 'i':
e809a587 1115 noinventory = 1;
115ff55c 1116 break;
e809a587 1117#else
115ff55c
VB
1118 case 'M':
1119 case 'i':
115ff55c 1120 case 'P':
e809a587
VB
1121 fprintf(stderr, "LLDP-MED support is not built-in\n");
1122 usage();
e809a587 1123 break;
115ff55c 1124#endif
e809a587 1125#ifdef USE_SNMP
bbea66e1
V
1126 case 'x':
1127 snmp = 1;
1128 break;
1129 case 'X':
43c02e7b 1130 snmp = 1;
bbea66e1
V
1131 agentx = optarg;
1132 break;
e809a587 1133#else
bbea66e1
V
1134 case 'x':
1135 case 'X':
e809a587
VB
1136 fprintf(stderr, "SNMP support is not built-in\n");
1137 usage();
1138#endif
43c02e7b 1139 break;
40ce835b
ST
1140 case 'S':
1141 descr_override = strdup(optarg);
1142 break;
42b39485 1143 case 'H':
8482abe9 1144 smart = atoi(optarg);
42b39485 1145 break;
43c02e7b
VB
1146 default:
1147 found = 0;
1148 for (i=0; protos[i].mode != 0; i++) {
43c02e7b 1149 if (ch == protos[i].arg) {
0c877af0
VB
1150 protos[i].enabled++;
1151 /* When an argument enable
1152 several protocols, only the
1153 first one can be forced. */
1154 if (found && protos[i].enabled > 1)
1155 protos[i].enabled = 1;
43c02e7b
VB
1156 found = 1;
1157 }
1158 }
1159 if (!found)
1160 usage();
1161 }
1162 }
8482abe9
VB
1163
1164 /* Set correct smart mode */
1165 for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
1166 if (filters[i].a == -1) {
1167 fprintf(stderr, "Incorrect mode for -H\n");
1168 usage();
1169 }
1170 smart = filters[i].b;
115ff55c 1171
6bb9c4e0 1172 log_init(debug, __progname);
4b9a5a23 1173 tzset(); /* Get timezone info before chroot */
a2993d83 1174
eac2f38a
VB
1175 if (!debug) {
1176 int pid;
1177 char *spid;
1178 if (daemon(0, 0) != 0)
1179 fatal("failed to detach daemon");
1180 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 1181 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
1182 fatal("unable to open pid file " LLDPD_PID_FILE);
1183 if (asprintf(&spid, "%d\n", getpid()) == -1)
1184 fatal("unable to create pid file " LLDPD_PID_FILE);
1185 if (write(pid, spid, strlen(spid)) == -1)
1186 fatal("unable to write pid file " LLDPD_PID_FILE);
1187 free(spid);
1188 close(pid);
1189 }
1190
c036b15d
VB
1191 lsb_release = lldpd_get_lsb_release();
1192
a2993d83 1193 priv_init(PRIVSEP_CHROOT);
43c02e7b 1194
43c02e7b
VB
1195 if ((cfg = (struct lldpd *)
1196 calloc(1, sizeof(struct lldpd))) == NULL)
1197 fatal(NULL);
1198
766f32b3 1199 cfg->g_mgmt_pattern = mgmtp;
42b39485 1200 cfg->g_smart = smart;
537a8043 1201 cfg->g_receiveonly = receiveonly;
43c02e7b
VB
1202
1203 /* Get ioctl socket */
1204 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
1205 fatal("failed to get ioctl socket");
1206 cfg->g_delay = LLDPD_TX_DELAY;
1207
c036b15d
VB
1208 /* Description */
1209 if (!(cfg->g_advertise_version = advertise_version))
1210 /* Remove the \n */
1211 lsb_release[strlen(lsb_release) - 1] = '\0';
1212 cfg->g_lsb_release = lsb_release;
40ce835b
ST
1213 if (descr_override)
1214 cfg->g_descr_override = descr_override;
1215
43c02e7b 1216 /* Set system capabilities */
77507b69
VB
1217 if ((lchassis = (struct lldpd_chassis*)
1218 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
1219 fatal(NULL);
1220 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 1221 LLDP_CAP_ROUTER;
89840df0
VB
1222#ifdef ENABLE_LLDPMED
1223 if (lldpmed > 0) {
1224 if (lldpmed == LLDPMED_CLASS_III)
77507b69
VB
1225 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
1226 lchassis->c_med_type = lldpmed;
1227 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
4c0d2715
VB
1228 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION |
1229 LLDPMED_CAP_POLICY | LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_MDI_PD;
740593ff 1230 cfg->g_noinventory = noinventory;
42bddd41
VB
1231 } else
1232 cfg->g_noinventory = 1;
89840df0 1233#endif
43c02e7b
VB
1234
1235 /* Set TTL */
77507b69 1236 lchassis->c_ttl = LLDPD_TTL;
43c02e7b
VB
1237
1238 cfg->g_protocols = protos;
43c02e7b 1239 for (i=0; protos[i].mode != 0; i++)
0c877af0
VB
1240 if (protos[i].enabled > 1)
1241 LLOG_INFO("protocol %s enabled and forced", protos[i].name);
1242 else if (protos[i].enabled)
43c02e7b 1243 LLOG_INFO("protocol %s enabled", protos[i].name);
0c877af0 1244 else
43c02e7b 1245 LLOG_INFO("protocol %s disabled", protos[i].name);
43c02e7b
VB
1246
1247 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
1248 TAILQ_INIT(&cfg->g_chassis);
1249 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
9898ac07 1250 lchassis->c_refcount++; /* We should always keep a reference to local chassis */
43c02e7b 1251
77d7090e
VB
1252 TAILQ_INIT(&cfg->g_callbacks);
1253
43c02e7b
VB
1254#ifdef USE_SNMP
1255 if (snmp) {
1256 cfg->g_snmp = 1;
bbea66e1 1257 agent_init(cfg, agentx, debug);
43c02e7b
VB
1258 }
1259#endif /* USE_SNMP */
1260
1261 /* Create socket */
6f1046d1 1262 if ((cfg->g_ctl = priv_ctl_create()) == -1)
b5562b23 1263 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
77d7090e
VB
1264 if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
1265 fatalx("unable to add callback for control socket");
43c02e7b 1266
43c02e7b
VB
1267 gcfg = cfg;
1268 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
1269 close(cfg->g_ctl);
1270 priv_ctl_cleanup();
43c02e7b
VB
1271 fatal("unable to set exit function");
1272 }
43c02e7b
VB
1273
1274 /* Signal handling */
b5562b23 1275 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
1276 signal(SIGINT, lldpd_shutdown);
1277 signal(SIGTERM, lldpd_shutdown);
1278
1279 for (;;)
1280 lldpd_loop(cfg);
1281
1282 return (0);
1283}