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