]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
Remove "listen on VLAN" feature.
[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>
29#include <sys/socket.h>
30#include <sys/select.h>
31#include <sys/time.h>
32#include <sys/ioctl.h>
33#include <arpa/inet.h>
43c02e7b 34#include <net/if_arp.h>
43c02e7b 35
bc598c23
VB
36#if LLDPD_FD_SETSIZE != FD_SETSIZE
37# warning "FD_SETSIZE is set to an inconsistent value."
38#endif
39
43c02e7b
VB
40#ifdef USE_SNMP
41#include <net-snmp/net-snmp-config.h>
42#include <net-snmp/net-snmp-includes.h>
43#include <net-snmp/agent/net-snmp-agent-includes.h>
44#include <net-snmp/agent/snmp_vars.h>
45#endif /* USE_SNMP */
46
8888d191 47static void usage(void);
43c02e7b 48
8888d191 49static struct protocol protos[] =
43c02e7b 50{
0c877af0 51 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
f2dcb180 52 LLDP_MULTICAST_ADDR },
4bad1937 53#ifdef ENABLE_CDP
43c02e7b 54 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
f2dcb180 55 CDP_MULTICAST_ADDR },
43c02e7b 56 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
f2dcb180 57 CDP_MULTICAST_ADDR },
4bad1937
VB
58#endif
59#ifdef ENABLE_SONMP
43c02e7b 60 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
f2dcb180 61 SONMP_MULTICAST_ADDR },
4bad1937
VB
62#endif
63#ifdef ENABLE_EDP
43c02e7b 64 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
f2dcb180 65 EDP_MULTICAST_ADDR },
4bad1937
VB
66#endif
67#ifdef ENABLE_FDP
031118c4 68 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
f2dcb180 69 FDP_MULTICAST_ADDR },
4bad1937 70#endif
43c02e7b 71 { 0, 0, "any", ' ', NULL, NULL, NULL,
f2dcb180 72 {0,0,0,0,0,0} }
43c02e7b
VB
73};
74
6e75df87
VB
75static void lldpd_update_localchassis(struct lldpd *);
76static void lldpd_update_localports(struct lldpd *);
77static void lldpd_cleanup(struct lldpd *);
8888d191
VB
78static void lldpd_loop(struct lldpd *);
79static void lldpd_shutdown(int);
6f1046d1 80static void lldpd_exit(void);
8888d191
VB
81static void lldpd_send_all(struct lldpd *);
82static void lldpd_recv_all(struct lldpd *);
83static int lldpd_guess_type(struct lldpd *, char *, int);
84static void lldpd_decode(struct lldpd *, char *, int,
0bc32943 85 struct lldpd_hardware *);
16f910e1
VB
86static void lldpd_update_chassis(struct lldpd_chassis *,
87 const struct lldpd_chassis *);
89840df0 88#ifdef ENABLE_LLDPMED
8888d191 89static void lldpd_med(struct lldpd_chassis *);
89840df0 90#endif
43c02e7b 91
8888d191 92static char **saved_argv;
6bb9c4e0
VB
93#ifdef HAVE___PROGNAME
94extern const char *__progname;
95#else
96# define __progname "lldpd"
97#endif
43c02e7b 98
8888d191 99static void
43c02e7b
VB
100usage(void)
101{
53132616
VB
102 fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
103
104 fprintf(stderr, "\n");
105
106 fprintf(stderr, "-d Do not daemonize.\n");
107 fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n");
108 fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n");
fde7a7ce 109 fprintf(stderr, "-S descr Override the default system description.\n");
53132616 110 fprintf(stderr, "-m IP Specify the management address of this system.\n");
8347587d 111#ifdef ENABLE_LLDPMED
53132616
VB
112 fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
113 fprintf(stderr, " 1 Generic Endpoint (Class I)\n");
114 fprintf(stderr, " 2 Media Endpoint (Class II)\n");
115 fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n");
116 fprintf(stderr, " 4 Network Connectivity Device\n");
8347587d
VB
117#endif
118#ifdef USE_SNMP
53132616 119 fprintf(stderr, "-x Enable SNMP subagent.\n");
53132616
VB
120#endif
121 fprintf(stderr, "\n");
122
8347587d
VB
123#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP
124 fprintf(stderr, "Additional protocol support.\n");
125#ifdef ENABLE_CDP
53132616 126 fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
8347587d
VB
127#endif
128#ifdef ENABLE_EDP
53132616 129 fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
8347587d
VB
130#endif
131#ifdef ENABLE_FDP
53132616 132 fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
8347587d
VB
133#endif
134#ifdef ENABLE_SONMP
53132616 135 fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
8347587d 136#endif
53132616
VB
137
138 fprintf(stderr, "\n");
8347587d 139#endif
53132616 140
e809a587 141 fprintf(stderr, "see manual page lldpd(8) for more information\n");
43c02e7b
VB
142 exit(1);
143}
144
6e75df87 145struct lldpd_hardware *
44002d66 146lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops)
43c02e7b 147{
6e75df87
VB
148 struct lldpd_hardware *hardware;
149 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
849954d7 150 if ((strcmp(hardware->h_ifname, name) == 0) &&
44002d66 151 (hardware->h_ifindex == index) &&
849954d7 152 ((!ops) || (ops == hardware->h_ops)))
6e75df87 153 break;
43c02e7b 154 }
6e75df87 155 return hardware;
43c02e7b
VB
156}
157
6e75df87
VB
158struct lldpd_hardware *
159lldpd_alloc_hardware(struct lldpd *cfg, char *name)
43c02e7b 160{
6e75df87 161 struct lldpd_hardware *hardware;
43c02e7b 162
6e75df87
VB
163 if ((hardware = (struct lldpd_hardware *)
164 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
165 return NULL;
43c02e7b 166
6e75df87
VB
167 strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
168 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
9df5ec3d 169 hardware->h_lport.p_chassis->c_refcount++;
6e75df87 170 TAILQ_INIT(&hardware->h_rports);
43c02e7b 171
6e75df87
VB
172#ifdef ENABLE_LLDPMED
173 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
174 hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
175 if (!cfg->g_noinventory)
176 hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
177 }
178#endif
179#ifdef ENABLE_DOT1
180 TAILQ_INIT(&hardware->h_lport.p_vlans);
181#endif
182 return hardware;
43c02e7b
VB
183}
184
a1347cd8 185#ifdef ENABLE_DOT1
43c02e7b
VB
186void
187lldpd_vlan_cleanup(struct lldpd_port *port)
188{
189 struct lldpd_vlan *vlan, *vlan_next;
190 for (vlan = TAILQ_FIRST(&port->p_vlans);
191 vlan != NULL;
192 vlan = vlan_next) {
193 free(vlan->v_name);
194 vlan_next = TAILQ_NEXT(vlan, v_entries);
195 TAILQ_REMOVE(&port->p_vlans, vlan, v_entries);
196 free(vlan);
197 }
198}
a1347cd8 199#endif
43c02e7b 200
a0edeaf8 201/* If `all' is true, clear all information, including information that
4e624dc2 202 are not refreshed periodically. Port should be freed manually. */
43c02e7b 203void
9898ac07 204lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all)
43c02e7b 205{
740593ff
VB
206#ifdef ENABLE_LLDPMED
207 int i;
a0edeaf8
VB
208 if (all)
209 for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
210 free(port->p_med_location[i].data);
740593ff 211#endif
a1347cd8 212#ifdef ENABLE_DOT1
43c02e7b 213 lldpd_vlan_cleanup(port);
a1347cd8 214#endif
43c02e7b
VB
215 free(port->p_id);
216 free(port->p_descr);
77507b69
VB
217 if (all) {
218 free(port->p_lastframe);
9898ac07 219 if (port->p_chassis) { /* chassis may not have been attributed, yet */
77507b69 220 port->p_chassis->c_refcount--;
9898ac07
V
221 port->p_chassis = NULL;
222 }
77507b69 223 }
43c02e7b
VB
224}
225
226void
77507b69 227lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
43c02e7b 228{
89840df0
VB
229#ifdef ENABLE_LLDPMED
230 free(chassis->c_med_hw);
517d524b 231 free(chassis->c_med_sw);
89840df0
VB
232 free(chassis->c_med_fw);
233 free(chassis->c_med_sn);
234 free(chassis->c_med_manuf);
235 free(chassis->c_med_model);
236 free(chassis->c_med_asset);
237#endif
43c02e7b
VB
238 free(chassis->c_id);
239 free(chassis->c_name);
240 free(chassis->c_descr);
77507b69
VB
241 if (all)
242 free(chassis);
43c02e7b
VB
243}
244
245void
77507b69 246lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
43c02e7b 247{
77507b69
VB
248 struct lldpd_port *port, *port_next;
249 int del;
250 for (port = TAILQ_FIRST(&hardware->h_rports);
251 port != NULL;
252 port = port_next) {
253 port_next = TAILQ_NEXT(port, p_entries);
254 del = all;
255 if (!del &&
256 (time(NULL) - port->p_lastupdate > port->p_chassis->c_ttl)) {
257 hardware->h_rx_ageout_cnt++;
258 del = 1;
259 }
260 if (del) {
261 TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
9898ac07 262 lldpd_port_cleanup(cfg, port, 1);
4e624dc2 263 free(port);
77507b69 264 }
43c02e7b 265 }
43c02e7b
VB
266}
267
d9be8ea0 268void
6e75df87 269lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
d9be8ea0 270{
6e75df87 271 int i;
9898ac07 272 lldpd_port_cleanup(cfg, &hardware->h_lport, 1);
6e75df87
VB
273 /* If we have a dedicated cleanup function, use it. Otherwise,
274 we just free the hardware-dependent data and close all FD
275 in h_recvfds and h_sendfd. */
276 if (hardware->h_ops->cleanup)
277 hardware->h_ops->cleanup(cfg, hardware);
278 else {
279 free(hardware->h_data);
bc598c23 280 for (i=0; i < LLDPD_FD_SETSIZE; i++)
6e75df87
VB
281 if (FD_ISSET(i, &hardware->h_recvfds))
282 close(i);
283 if (hardware->h_sendfd) close(hardware->h_sendfd);
284 }
d9be8ea0
VB
285 free(hardware);
286}
287
6e75df87 288static void
43c02e7b
VB
289lldpd_cleanup(struct lldpd *cfg)
290{
291 struct lldpd_hardware *hardware, *hardware_next;
aadc9936 292 struct lldpd_chassis *chassis, *chassis_next;
43c02e7b
VB
293
294 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
295 hardware = hardware_next) {
296 hardware_next = TAILQ_NEXT(hardware, h_entries);
6e75df87 297 if (!hardware->h_flags) {
43c02e7b 298 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
77507b69 299 lldpd_remote_cleanup(cfg, hardware, 1);
6e75df87 300 lldpd_hardware_cleanup(cfg, hardware);
77507b69
VB
301 } else
302 lldpd_remote_cleanup(cfg, hardware, 0);
43c02e7b 303 }
aadc9936
VB
304
305 for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
306 chassis = chassis_next) {
307 chassis_next = TAILQ_NEXT(chassis, c_entries);
308 if (chassis->c_refcount == 0) {
309 TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
310 lldpd_chassis_cleanup(chassis, 1);
311 }
312 }
43c02e7b
VB
313}
314
8888d191 315static int
43c02e7b
VB
316lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
317{
318 int i;
319 if (s < ETH_ALEN)
320 return -1;
321 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
322 if (!cfg->g_protocols[i].enabled)
323 continue;
324 if (cfg->g_protocols[i].guess == NULL) {
325 if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ALEN) == 0)
326 return cfg->g_protocols[i].mode;
327 } else {
328 if (cfg->g_protocols[i].guess(frame, s))
329 return cfg->g_protocols[i].mode;
330 }
331 }
332 return -1;
333}
334
8888d191 335static void
43c02e7b 336lldpd_decode(struct lldpd *cfg, char *frame, int s,
0bc32943 337 struct lldpd_hardware *hardware)
43c02e7b 338{
77507b69
VB
339 int i, result;
340 struct lldpd_chassis *chassis, *ochassis = NULL;
341 struct lldpd_port *port, *oport = NULL;
43c02e7b
VB
342 int guess = LLDPD_MODE_LLDP;
343
50a89ca7 344 /* Discard VLAN frames */
a8105c1b
VB
345 if ((s >= sizeof(struct ethhdr)) &&
346 (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
50a89ca7
VB
347 return;
348
77507b69
VB
349 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
350 if ((oport->p_lastframe != NULL) &&
351 (oport->p_lastframe->size == s) &&
352 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
353 /* Already received the same frame */
354 oport->p_lastupdate = time(NULL);
355 return;
356 }
43c02e7b
VB
357 }
358
f2dcb180
VB
359 guess = lldpd_guess_type(cfg, frame, s);
360 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
361 if (!cfg->g_protocols[i].enabled)
362 continue;
363 if (cfg->g_protocols[i].mode == guess) {
364 if ((result = cfg->g_protocols[i].decode(cfg, frame,
365 s, hardware, &chassis, &port)) == -1)
366 return;
77507b69
VB
367 chassis->c_protocol = port->p_protocol =
368 cfg->g_protocols[i].mode;
f2dcb180 369 break;
43c02e7b 370 }
f2dcb180
VB
371 }
372 if (cfg->g_protocols[i].mode == 0) {
373 LLOG_INFO("unable to guess frame type");
43c02e7b 374 return;
f2dcb180 375 }
43c02e7b 376
77507b69
VB
377 /* Do we already have the same MSAP somewhere? */
378 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
379 if ((port->p_protocol == oport->p_protocol) &&
380 (port->p_id_subtype == oport->p_id_subtype) &&
381 (port->p_id_len == oport->p_id_len) &&
382 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
383 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
384 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
385 (memcmp(chassis->c_id, oport->p_chassis->c_id,
386 chassis->c_id_len) == 0)) {
387 ochassis = oport->p_chassis;
388 break;
389 }
43c02e7b 390 }
77507b69
VB
391 /* No, but do we already know the system? */
392 if (!oport) {
393 TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) {
394 if ((chassis->c_protocol == ochassis->c_protocol) &&
395 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
396 (chassis->c_id_len == ochassis->c_id_len) &&
397 (memcmp(chassis->c_id, ochassis->c_id,
398 chassis->c_id_len) == 0))
399 break;
43c02e7b 400 }
43c02e7b 401 }
43c02e7b 402
77507b69
VB
403 if (oport) {
404 /* The port is known, remove it before adding it back */
405 TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
9898ac07 406 lldpd_port_cleanup(cfg, oport, 1);
4e624dc2 407 free(oport);
77507b69
VB
408 }
409 if (ochassis) {
16f910e1 410 lldpd_update_chassis(ochassis, chassis);
77507b69
VB
411 free(chassis);
412 chassis = ochassis;
413 } else {
414 /* Chassis not known, add it */
415 chassis->c_index = ++cfg->g_lastrid;
77507b69
VB
416 chassis->c_refcount = 0;
417 TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
f4c43902
VB
418 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
419 LLOG_DEBUG("Currently, we know %d different systems", i);
77507b69
VB
420 }
421 /* Add port */
422 port->p_lastchange = port->p_lastupdate = time(NULL);
423 if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
424 sizeof(int))) != NULL) {
425 port->p_lastframe->size = s;
426 memcpy(port->p_lastframe->frame, frame, s);
427 }
428 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
429 port->p_chassis = chassis;
430 port->p_chassis->c_refcount++;
9df5ec3d
VB
431 /* Several cases are possible :
432 1. chassis is new, its refcount was 0. It is now attached
433 to this port, its refcount is 1.
434 2. chassis already exists and was attached to another
435 port, we increase its refcount accordingly.
436 3. chassis already exists and was attached to the same
437 port, its refcount was decreased with
438 lldpd_port_cleanup() and is now increased again.
439
440 In all cases, if the port already existed, it has been
441 freed with lldpd_port_cleanup() and therefore, the refcount
442 of the chassis that was attached to it is decreased.
443 */
f4c43902
VB
444 i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
445 LLOG_DEBUG("Currently, %s known %d neighbors",
446 hardware->h_ifname, i);
43c02e7b
VB
447 return;
448}
449
16f910e1
VB
450/* Update chassis `ochassis' with values from `chassis'. */
451static void
452lldpd_update_chassis(struct lldpd_chassis *ochassis,
453 const struct lldpd_chassis *chassis) {
454 TAILQ_ENTRY(lldpd_chassis) entries;
455 /* We want to keep refcount, index and list stuff from the current
456 * chassis */
457 int refcount = ochassis->c_refcount;
458 int index = ochassis->c_index;
459 memcpy(&entries, &ochassis->c_entries,
460 sizeof(entries));
461 /* Make the copy */
462 lldpd_chassis_cleanup(ochassis, 0);
463 memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
464 /* Restore saved values */
465 ochassis->c_refcount = refcount;
466 ochassis->c_index = index;
467 memcpy(&ochassis->c_entries, &entries, sizeof(entries));
468}
469
77d7090e
VB
470int
471lldpd_callback_add(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG), void *data)
472{
473 struct lldpd_callback *callback;
474 if ((callback = (struct lldpd_callback *)
475 malloc(sizeof(struct lldpd_callback))) == NULL)
476 return -1;
477 callback->fd = fd;
478 callback->function = fn;
479 callback->data = data;
480 TAILQ_INSERT_TAIL(&cfg->g_callbacks, callback, next);
481 return 0;
482}
483
484void
485lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG))
486{
487 struct lldpd_callback *callback, *callback_next;
488 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
489 callback;
490 callback = callback_next) {
491 callback_next = TAILQ_NEXT(callback, next);
492 if ((callback->fd == fd) &&
493 (callback->function = fn)) {
494 free(callback->data);
495 TAILQ_REMOVE(&cfg->g_callbacks, callback, next);
496 free(callback);
497 }
498 }
499}
16f910e1 500
8888d191 501static void
43c02e7b
VB
502lldpd_recv_all(struct lldpd *cfg)
503{
504 struct lldpd_hardware *hardware;
77d7090e 505 struct lldpd_callback *callback, *callback_next;
43c02e7b
VB
506 fd_set rfds;
507 struct timeval tv;
43c02e7b 508#ifdef USE_SNMP
630b4134
VB
509 struct timeval snmptv;
510 int snmpblock = 0;
43c02e7b 511#endif
0bc32943 512 int rc, nfds, n;
43c02e7b
VB
513 char *buffer;
514
515 do {
516 tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
517 if (tv.tv_sec < 0)
518 tv.tv_sec = LLDPD_TX_DELAY;
519 if (tv.tv_sec >= cfg->g_delay)
520 tv.tv_sec = cfg->g_delay;
521 tv.tv_usec = 0;
522
523 FD_ZERO(&rfds);
524 nfds = -1;
525
526 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
527 /* Ignore if interface is down */
6e75df87 528 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b 529 continue;
6e75df87
VB
530 /* This is quite expensive but we don't rely on internal
531 * structure of fd_set. */
bc598c23 532 for (n = 0; n < LLDPD_FD_SETSIZE; n++)
6e75df87
VB
533 if (FD_ISSET(n, &hardware->h_recvfds)) {
534 FD_SET(n, &rfds);
535 if (nfds < n)
536 nfds = n;
537 }
43c02e7b 538 }
77d7090e
VB
539 TAILQ_FOREACH(callback, &cfg->g_callbacks, next) {
540 FD_SET(callback->fd, &rfds);
541 if (nfds < callback->fd)
542 nfds = callback->fd;
43c02e7b 543 }
43c02e7b
VB
544
545#ifdef USE_SNMP
630b4134
VB
546 if (cfg->g_snmp) {
547 snmpblock = 0;
548 memcpy(&snmptv, &tv, sizeof(struct timeval));
549 snmp_select_info(&nfds, &rfds, &snmptv, &snmpblock);
550 if (snmpblock == 0)
551 memcpy(&tv, &snmptv, sizeof(struct timeval));
552 }
43c02e7b
VB
553#endif /* USE_SNMP */
554 if (nfds == -1) {
555 sleep(cfg->g_delay);
556 return;
557 }
558
559 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
560 if (rc == -1) {
561 if (errno == EINTR)
562 continue;
563 LLOG_WARN("failure on select");
564 break;
565 }
566#ifdef USE_SNMP
567 if (cfg->g_snmp) {
568 if (rc > 0)
569 snmp_read(&rfds);
570 else if (rc == 0)
571 snmp_timeout();
572 }
573#endif /* USE_SNMP */
574 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
bc598c23 575 for (n = 0; n < LLDPD_FD_SETSIZE; n++)
6e75df87
VB
576 if ((FD_ISSET(n, &hardware->h_recvfds)) &&
577 (FD_ISSET(n, &rfds))) break;
bc598c23 578 if (n == LLDPD_FD_SETSIZE) continue;
6e75df87
VB
579 if ((buffer = (char *)malloc(
580 hardware->h_mtu)) == NULL) {
581 LLOG_WARN("failed to alloc reception buffer");
582 continue;
583 }
584 if ((n = hardware->h_ops->recv(cfg, hardware,
585 n, buffer, hardware->h_mtu)) == -1) {
43c02e7b 586 free(buffer);
6e75df87 587 continue;
43c02e7b 588 }
6e75df87
VB
589 hardware->h_rx_cnt++;
590 lldpd_decode(cfg, buffer, n, hardware);
591 free(buffer);
592 break;
43c02e7b 593 }
77d7090e
VB
594 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
595 callback;
596 callback = callback_next) {
597 /* Callback function can use TAILQ_REMOVE */
598 callback_next = TAILQ_NEXT(callback, next);
599 if (FD_ISSET(callback->fd, &rfds))
600 callback->function(cfg, callback);
43c02e7b
VB
601 }
602
603#ifdef USE_SNMP
604 if (cfg->g_snmp) {
605 run_alarms();
606 netsnmp_check_outstanding_agent_requests();
607 }
608#endif /* USE_SNMP */
609 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
610}
611
8888d191 612static void
43c02e7b
VB
613lldpd_send_all(struct lldpd *cfg)
614{
615 struct lldpd_hardware *hardware;
77507b69 616 struct lldpd_port *port;
0d86e62f 617 int i, sent;
f7db0dd8 618
43c02e7b
VB
619 cfg->g_lastsent = time(NULL);
620 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
621 /* Ignore if interface is down */
6e75df87 622 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b
VB
623 continue;
624
0d86e62f 625 sent = 0;
43c02e7b
VB
626 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
627 if (!cfg->g_protocols[i].enabled)
628 continue;
77507b69 629 /* We send only if we have at least one remote system
0c877af0
VB
630 * speaking this protocol or if the protocol is forced */
631 if (cfg->g_protocols[i].enabled > 1) {
632 cfg->g_protocols[i].send(cfg, hardware);
633 sent++;
634 continue;
635 }
77507b69
VB
636 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
637 if (port->p_protocol ==
638 cfg->g_protocols[i].mode) {
639 cfg->g_protocols[i].send(cfg,
640 hardware);
0c877af0 641 sent++;
77507b69
VB
642 break;
643 }
644 }
43c02e7b 645 }
77507b69
VB
646
647 if (!sent)
648 /* Nothing was sent for this port, let's speak LLDP */
649 cfg->g_protocols[0].send(cfg,
650 hardware);
43c02e7b
VB
651 }
652}
653
89840df0 654#ifdef ENABLE_LLDPMED
8888d191 655static void
89840df0
VB
656lldpd_med(struct lldpd_chassis *chassis)
657{
658 free(chassis->c_med_hw);
659 free(chassis->c_med_fw);
660 free(chassis->c_med_sn);
661 free(chassis->c_med_manuf);
662 free(chassis->c_med_model);
663 free(chassis->c_med_asset);
664 chassis->c_med_hw = dmi_hw();
665 chassis->c_med_fw = dmi_fw();
666 chassis->c_med_sn = dmi_sn();
667 chassis->c_med_manuf = dmi_manuf();
668 chassis->c_med_model = dmi_model();
669 chassis->c_med_asset = dmi_asset();
670}
671#endif
672
8888d191 673static void
6e75df87 674lldpd_update_localchassis(struct lldpd *cfg)
43c02e7b 675{
6e75df87
VB
676 struct utsname un;
677 char *hp;
43c02e7b
VB
678 int f;
679 char status;
6e75df87 680 struct lldpd_hardware *hardware;
43c02e7b
VB
681
682 /* Set system name and description */
6e75df87 683 if (uname(&un) != 0)
43c02e7b 684 fatal("failed to get system information");
b5562b23 685 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b 686 fatal("failed to get system name");
77507b69
VB
687 free(LOCAL_CHASSIS(cfg)->c_name);
688 free(LOCAL_CHASSIS(cfg)->c_descr);
689 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
249644a4 690 fatal(NULL);
40ce835b
ST
691 if (cfg->g_descr_override) {
692 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
693 cfg->g_descr_override) == -1)
de1b1b3a 694 fatal("failed to set full system description");
40ce835b
ST
695 } else {
696 if (cfg->g_advertise_version) {
697 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
698 un.sysname, un.release, un.version, un.machine)
699 == -1)
700 fatal("failed to set full system description");
701 } else {
702 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
703 un.sysname) == -1)
704 fatal("failed to set minimal system description");
705 }
706 }
43c02e7b
VB
707
708 /* Check forwarding */
b5562b23 709 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
4afe659e 710 if ((read(f, &status, 1) == 1) && (status == '1')) {
77507b69 711 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
4afe659e 712 }
43c02e7b
VB
713 close(f);
714 }
89840df0 715#ifdef ENABLE_LLDPMED
77507b69
VB
716 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
717 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
718 lldpd_med(LOCAL_CHASSIS(cfg));
719 free(LOCAL_CHASSIS(cfg)->c_med_sw);
de1b1b3a
VB
720 if (cfg->g_advertise_version)
721 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
722 else
723 LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
89840df0 724#endif
43c02e7b 725
6e75df87
VB
726 /* Set chassis ID if needed */
727 if ((LOCAL_CHASSIS(cfg)->c_id == NULL) &&
728 (hardware = TAILQ_FIRST(&cfg->g_hardware))) {
729 if ((LOCAL_CHASSIS(cfg)->c_id =
730 malloc(sizeof(hardware->h_lladdr))) == NULL)
731 fatal(NULL);
732 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
733 LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
734 memcpy(LOCAL_CHASSIS(cfg)->c_id,
735 hardware->h_lladdr, sizeof(hardware->h_lladdr));
736 }
737}
738
739static void
740lldpd_update_localports(struct lldpd *cfg)
741{
742 struct ifaddrs *ifap;
743 struct lldpd_hardware *hardware;
744 lldpd_ifhandlers ifhs[] = {
849954d7 745 lldpd_ifh_bond, /* Handle bond */
6e75df87 746 lldpd_ifh_eth, /* Handle classic ethernet interfaces */
5994b27d 747#ifdef ENABLE_DOT1
6e75df87 748 lldpd_ifh_vlan, /* Handle VLAN */
5994b27d 749#endif
6e75df87
VB
750 lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
751 NULL
752 };
753 lldpd_ifhandlers *ifh;
754
755 /* h_flags is set to 0 for each port. If the port is updated, h_flags
756 * will be set to a non-zero value. This will allow us to clean up any
757 * non up-to-date port */
43c02e7b
VB
758 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
759 hardware->h_flags = 0;
760
77507b69 761 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
6e75df87
VB
762 if (getifaddrs(&ifap) != 0)
763 fatal("lldpd_update_localports: failed to get interface list");
764
765 /* We will run the list of interfaces through a list of interface
766 * handlers. Each handler will create or update some hardware port (and
767 * will set h_flags to a non zero value. The handler can use the list of
768 * interfaces but this is not mandatory. If the interface handler
769 * handles an interface from the list, it should set ifa_flags to 0 to
770 * let know the other handlers that it took care of this interface. This
771 * means that more specific handlers should be before less specific
772 * ones. */
773 for (ifh = ifhs; *ifh != NULL; ifh++)
774 (*ifh)(cfg, ifap);
43c02e7b 775 freeifaddrs(ifap);
6e75df87 776}
43c02e7b 777
6e75df87
VB
778static void
779lldpd_loop(struct lldpd *cfg)
780{
781 /* Main loop.
782
783 1. Update local ports information
784 2. Clean unwanted (removed) local ports
785 3. Update local chassis information
786 4. Send packets
787 5. Receive packets
788 */
849954d7 789 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
6e75df87 790 lldpd_update_localports(cfg);
43c02e7b 791 lldpd_cleanup(cfg);
6e75df87 792 lldpd_update_localchassis(cfg);
43c02e7b
VB
793 lldpd_send_all(cfg);
794 lldpd_recv_all(cfg);
795}
796
8888d191 797static void
43c02e7b
VB
798lldpd_shutdown(int sig)
799{
800 LLOG_INFO("signal received, exiting");
801 exit(0);
802}
803
804/* For signal handling */
8888d191 805static struct lldpd *gcfg = NULL;
43c02e7b 806
8888d191 807static void
43c02e7b
VB
808lldpd_exit()
809{
6e75df87 810 struct lldpd_hardware *hardware, *hardware_next;
b5562b23
VB
811 close(gcfg->g_ctl);
812 priv_ctl_cleanup();
6e75df87
VB
813 for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL;
814 hardware = hardware_next) {
815 hardware_next = TAILQ_NEXT(hardware, h_entries);
816 lldpd_hardware_cleanup(gcfg, hardware);
43c02e7b
VB
817 }
818#ifdef USE_SNMP
819 if (gcfg->g_snmp)
820 agent_shutdown();
821#endif /* USE_SNMP */
822}
823
824int
2acc1418 825lldpd_main(int argc, char *argv[])
43c02e7b
VB
826{
827 struct lldpd *cfg;
77507b69 828 struct lldpd_chassis *lchassis;
e809a587
VB
829 int ch, debug = 0;
830#ifdef USE_SNMP
831 int snmp = 0;
bbea66e1 832 char *agentx = NULL; /* AgentX socket */
e809a587 833#endif
43c02e7b 834 char *mgmtp = NULL;
993a6e50 835 char *popt, opts[] =
40ce835b 836 "hkdxX:m:p:M:S:i@ ";
de1b1b3a 837 int i, found, advertise_version = 1;
89840df0 838#ifdef ENABLE_LLDPMED
e809a587 839 int lldpmed = 0, noinventory = 0;
89840df0 840#endif
40ce835b 841 char *descr_override = NULL;
43c02e7b
VB
842
843 saved_argv = argv;
844
845 /*
846 * Get and parse command line options
847 */
f0bd3505 848 popt = strchr(opts, '@');
0c877af0 849 for (i=0; protos[i].mode != 0; i++)
43c02e7b 850 *(popt++) = protos[i].arg;
43c02e7b
VB
851 *popt = '\0';
852 while ((ch = getopt(argc, argv, opts)) != -1) {
853 switch (ch) {
b162b740
VB
854 case 'h':
855 usage();
856 break;
43c02e7b
VB
857 case 'd':
858 debug++;
859 break;
860 case 'm':
861 mgmtp = optarg;
862 break;
de1b1b3a
VB
863 case 'k':
864 advertise_version = 0;
865 break;
e809a587 866#ifdef ENABLE_LLDPMED
115ff55c 867 case 'M':
89840df0 868 lldpmed = atoi(optarg);
e809a587
VB
869 if ((lldpmed < 1) || (lldpmed > 4)) {
870 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 871 usage();
e809a587 872 }
89840df0 873 break;
e809a587 874 case 'i':
e809a587 875 noinventory = 1;
115ff55c 876 break;
e809a587 877#else
115ff55c
VB
878 case 'M':
879 case 'i':
115ff55c 880 case 'P':
e809a587
VB
881 fprintf(stderr, "LLDP-MED support is not built-in\n");
882 usage();
e809a587 883 break;
115ff55c 884#endif
e809a587 885#ifdef USE_SNMP
bbea66e1
V
886 case 'x':
887 snmp = 1;
888 break;
889 case 'X':
43c02e7b 890 snmp = 1;
bbea66e1
V
891 agentx = optarg;
892 break;
e809a587 893#else
bbea66e1
V
894 case 'x':
895 case 'X':
e809a587
VB
896 fprintf(stderr, "SNMP support is not built-in\n");
897 usage();
898#endif
43c02e7b 899 break;
40ce835b
ST
900 case 'S':
901 descr_override = strdup(optarg);
902 break;
43c02e7b
VB
903 default:
904 found = 0;
905 for (i=0; protos[i].mode != 0; i++) {
43c02e7b 906 if (ch == protos[i].arg) {
0c877af0
VB
907 protos[i].enabled++;
908 /* When an argument enable
909 several protocols, only the
910 first one can be forced. */
911 if (found && protos[i].enabled > 1)
912 protos[i].enabled = 1;
43c02e7b
VB
913 found = 1;
914 }
915 }
916 if (!found)
917 usage();
918 }
919 }
115ff55c 920
6bb9c4e0 921 log_init(debug, __progname);
4b9a5a23 922 tzset(); /* Get timezone info before chroot */
a2993d83 923
eac2f38a
VB
924 if (!debug) {
925 int pid;
926 char *spid;
927 if (daemon(0, 0) != 0)
928 fatal("failed to detach daemon");
929 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 930 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
931 fatal("unable to open pid file " LLDPD_PID_FILE);
932 if (asprintf(&spid, "%d\n", getpid()) == -1)
933 fatal("unable to create pid file " LLDPD_PID_FILE);
934 if (write(pid, spid, strlen(spid)) == -1)
935 fatal("unable to write pid file " LLDPD_PID_FILE);
936 free(spid);
937 close(pid);
938 }
939
a2993d83 940 priv_init(PRIVSEP_CHROOT);
43c02e7b 941
43c02e7b
VB
942 if ((cfg = (struct lldpd *)
943 calloc(1, sizeof(struct lldpd))) == NULL)
944 fatal(NULL);
945
766f32b3 946 cfg->g_mgmt_pattern = mgmtp;
de1b1b3a 947 cfg->g_advertise_version = advertise_version;
43c02e7b
VB
948
949 /* Get ioctl socket */
950 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
951 fatal("failed to get ioctl socket");
952 cfg->g_delay = LLDPD_TX_DELAY;
953
40ce835b
ST
954 if (descr_override)
955 cfg->g_descr_override = descr_override;
956
43c02e7b 957 /* Set system capabilities */
77507b69
VB
958 if ((lchassis = (struct lldpd_chassis*)
959 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
960 fatal(NULL);
961 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 962 LLDP_CAP_ROUTER;
89840df0
VB
963#ifdef ENABLE_LLDPMED
964 if (lldpmed > 0) {
965 if (lldpmed == LLDPMED_CLASS_III)
77507b69
VB
966 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
967 lchassis->c_med_type = lldpmed;
968 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
4c0d2715
VB
969 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION |
970 LLDPMED_CAP_POLICY | LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_MDI_PD;
740593ff 971 cfg->g_noinventory = noinventory;
42bddd41
VB
972 } else
973 cfg->g_noinventory = 1;
89840df0 974#endif
43c02e7b
VB
975
976 /* Set TTL */
77507b69 977 lchassis->c_ttl = LLDPD_TTL;
43c02e7b
VB
978
979 cfg->g_protocols = protos;
43c02e7b 980 for (i=0; protos[i].mode != 0; i++)
0c877af0
VB
981 if (protos[i].enabled > 1)
982 LLOG_INFO("protocol %s enabled and forced", protos[i].name);
983 else if (protos[i].enabled)
43c02e7b 984 LLOG_INFO("protocol %s enabled", protos[i].name);
0c877af0 985 else
43c02e7b 986 LLOG_INFO("protocol %s disabled", protos[i].name);
43c02e7b
VB
987
988 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
989 TAILQ_INIT(&cfg->g_chassis);
990 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
9898ac07 991 lchassis->c_refcount++; /* We should always keep a reference to local chassis */
43c02e7b 992
77d7090e
VB
993 TAILQ_INIT(&cfg->g_callbacks);
994
43c02e7b
VB
995#ifdef USE_SNMP
996 if (snmp) {
997 cfg->g_snmp = 1;
bbea66e1 998 agent_init(cfg, agentx, debug);
43c02e7b
VB
999 }
1000#endif /* USE_SNMP */
1001
1002 /* Create socket */
6f1046d1 1003 if ((cfg->g_ctl = priv_ctl_create()) == -1)
b5562b23 1004 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
77d7090e
VB
1005 if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
1006 fatalx("unable to add callback for control socket");
43c02e7b 1007
43c02e7b
VB
1008 gcfg = cfg;
1009 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
1010 close(cfg->g_ctl);
1011 priv_ctl_cleanup();
43c02e7b
VB
1012 fatal("unable to set exit function");
1013 }
43c02e7b
VB
1014
1015 /* Signal handling */
b5562b23 1016 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
1017 signal(SIGINT, lldpd_shutdown);
1018 signal(SIGTERM, lldpd_shutdown);
1019
1020 for (;;)
1021 lldpd_loop(cfg);
1022
1023 return (0);
1024}