]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
Add "format" option.
[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
VB
50{
51 { LLDPD_MODE_LLDP, 1, "LLDP", ' ', 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
VB
510#ifdef USE_SNMP
511 int fakeblock = 0;
512 struct timeval *tvp = &tv;
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
548 if (cfg->g_snmp)
549 snmp_select_info(&nfds, &rfds, tvp, &fakeblock);
550#endif /* USE_SNMP */
551 if (nfds == -1) {
552 sleep(cfg->g_delay);
553 return;
554 }
555
556 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
557 if (rc == -1) {
558 if (errno == EINTR)
559 continue;
560 LLOG_WARN("failure on select");
561 break;
562 }
563#ifdef USE_SNMP
564 if (cfg->g_snmp) {
565 if (rc > 0)
566 snmp_read(&rfds);
567 else if (rc == 0)
568 snmp_timeout();
569 }
570#endif /* USE_SNMP */
571 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
bc598c23 572 for (n = 0; n < LLDPD_FD_SETSIZE; n++)
6e75df87
VB
573 if ((FD_ISSET(n, &hardware->h_recvfds)) &&
574 (FD_ISSET(n, &rfds))) break;
bc598c23 575 if (n == LLDPD_FD_SETSIZE) continue;
6e75df87
VB
576 if ((buffer = (char *)malloc(
577 hardware->h_mtu)) == NULL) {
578 LLOG_WARN("failed to alloc reception buffer");
579 continue;
580 }
581 if ((n = hardware->h_ops->recv(cfg, hardware,
582 n, buffer, hardware->h_mtu)) == -1) {
43c02e7b 583 free(buffer);
6e75df87 584 continue;
43c02e7b 585 }
6e75df87
VB
586 hardware->h_rx_cnt++;
587 lldpd_decode(cfg, buffer, n, hardware);
588 free(buffer);
589 break;
43c02e7b 590 }
77d7090e
VB
591 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
592 callback;
593 callback = callback_next) {
594 /* Callback function can use TAILQ_REMOVE */
595 callback_next = TAILQ_NEXT(callback, next);
596 if (FD_ISSET(callback->fd, &rfds))
597 callback->function(cfg, callback);
43c02e7b
VB
598 }
599
600#ifdef USE_SNMP
601 if (cfg->g_snmp) {
602 run_alarms();
603 netsnmp_check_outstanding_agent_requests();
604 }
605#endif /* USE_SNMP */
606 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
607}
608
8888d191 609static void
43c02e7b
VB
610lldpd_send_all(struct lldpd *cfg)
611{
612 struct lldpd_hardware *hardware;
77507b69 613 struct lldpd_port *port;
0d86e62f 614 int i, sent;
f7db0dd8 615
43c02e7b
VB
616 cfg->g_lastsent = time(NULL);
617 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
618 /* Ignore if interface is down */
6e75df87 619 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b
VB
620 continue;
621
0d86e62f 622 sent = 0;
43c02e7b
VB
623 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
624 if (!cfg->g_protocols[i].enabled)
625 continue;
77507b69
VB
626 /* We send only if we have at least one remote system
627 * speaking this protocol */
628 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
629 if (port->p_protocol ==
630 cfg->g_protocols[i].mode) {
631 cfg->g_protocols[i].send(cfg,
632 hardware);
633 sent = 1;
634 break;
635 }
636 }
43c02e7b 637 }
77507b69
VB
638
639 if (!sent)
640 /* Nothing was sent for this port, let's speak LLDP */
641 cfg->g_protocols[0].send(cfg,
642 hardware);
43c02e7b
VB
643 }
644}
645
89840df0 646#ifdef ENABLE_LLDPMED
8888d191 647static void
89840df0
VB
648lldpd_med(struct lldpd_chassis *chassis)
649{
650 free(chassis->c_med_hw);
651 free(chassis->c_med_fw);
652 free(chassis->c_med_sn);
653 free(chassis->c_med_manuf);
654 free(chassis->c_med_model);
655 free(chassis->c_med_asset);
656 chassis->c_med_hw = dmi_hw();
657 chassis->c_med_fw = dmi_fw();
658 chassis->c_med_sn = dmi_sn();
659 chassis->c_med_manuf = dmi_manuf();
660 chassis->c_med_model = dmi_model();
661 chassis->c_med_asset = dmi_asset();
662}
663#endif
664
8888d191 665static void
6e75df87 666lldpd_update_localchassis(struct lldpd *cfg)
43c02e7b 667{
6e75df87
VB
668 struct utsname un;
669 char *hp;
43c02e7b
VB
670 int f;
671 char status;
6e75df87 672 struct lldpd_hardware *hardware;
43c02e7b
VB
673
674 /* Set system name and description */
6e75df87 675 if (uname(&un) != 0)
43c02e7b 676 fatal("failed to get system information");
b5562b23 677 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b 678 fatal("failed to get system name");
77507b69
VB
679 free(LOCAL_CHASSIS(cfg)->c_name);
680 free(LOCAL_CHASSIS(cfg)->c_descr);
681 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
249644a4 682 fatal(NULL);
de1b1b3a
VB
683 if (cfg->g_advertise_version) {
684 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
685 un.sysname, un.release, un.version, un.machine) == -1)
686 fatal("failed to set full system description");
687 } else {
688 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s", un.sysname) == -1)
689 fatal("failed to set minimal system description");
690 }
43c02e7b
VB
691
692 /* Check forwarding */
b5562b23 693 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
4afe659e 694 if ((read(f, &status, 1) == 1) && (status == '1')) {
77507b69 695 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
4afe659e 696 }
43c02e7b
VB
697 close(f);
698 }
89840df0 699#ifdef ENABLE_LLDPMED
77507b69
VB
700 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
701 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
702 lldpd_med(LOCAL_CHASSIS(cfg));
703 free(LOCAL_CHASSIS(cfg)->c_med_sw);
de1b1b3a
VB
704 if (cfg->g_advertise_version)
705 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
706 else
707 LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
89840df0 708#endif
43c02e7b 709
6e75df87
VB
710 /* Set chassis ID if needed */
711 if ((LOCAL_CHASSIS(cfg)->c_id == NULL) &&
712 (hardware = TAILQ_FIRST(&cfg->g_hardware))) {
713 if ((LOCAL_CHASSIS(cfg)->c_id =
714 malloc(sizeof(hardware->h_lladdr))) == NULL)
715 fatal(NULL);
716 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
717 LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
718 memcpy(LOCAL_CHASSIS(cfg)->c_id,
719 hardware->h_lladdr, sizeof(hardware->h_lladdr));
720 }
721}
722
723static void
724lldpd_update_localports(struct lldpd *cfg)
725{
726 struct ifaddrs *ifap;
727 struct lldpd_hardware *hardware;
728 lldpd_ifhandlers ifhs[] = {
849954d7 729 lldpd_ifh_bond, /* Handle bond */
6e75df87 730 lldpd_ifh_eth, /* Handle classic ethernet interfaces */
5994b27d 731#ifdef ENABLE_DOT1
6e75df87 732 lldpd_ifh_vlan, /* Handle VLAN */
5994b27d 733#endif
6e75df87
VB
734 lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
735 NULL
736 };
737 lldpd_ifhandlers *ifh;
738
739 /* h_flags is set to 0 for each port. If the port is updated, h_flags
740 * will be set to a non-zero value. This will allow us to clean up any
741 * non up-to-date port */
43c02e7b
VB
742 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
743 hardware->h_flags = 0;
744
77507b69 745 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
6e75df87
VB
746 if (getifaddrs(&ifap) != 0)
747 fatal("lldpd_update_localports: failed to get interface list");
748
749 /* We will run the list of interfaces through a list of interface
750 * handlers. Each handler will create or update some hardware port (and
751 * will set h_flags to a non zero value. The handler can use the list of
752 * interfaces but this is not mandatory. If the interface handler
753 * handles an interface from the list, it should set ifa_flags to 0 to
754 * let know the other handlers that it took care of this interface. This
755 * means that more specific handlers should be before less specific
756 * ones. */
757 for (ifh = ifhs; *ifh != NULL; ifh++)
758 (*ifh)(cfg, ifap);
43c02e7b 759 freeifaddrs(ifap);
6e75df87 760}
43c02e7b 761
6e75df87
VB
762static void
763lldpd_loop(struct lldpd *cfg)
764{
765 /* Main loop.
766
767 1. Update local ports information
768 2. Clean unwanted (removed) local ports
769 3. Update local chassis information
770 4. Send packets
771 5. Receive packets
772 */
849954d7 773 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
6e75df87 774 lldpd_update_localports(cfg);
43c02e7b 775 lldpd_cleanup(cfg);
6e75df87 776 lldpd_update_localchassis(cfg);
43c02e7b
VB
777 lldpd_send_all(cfg);
778 lldpd_recv_all(cfg);
779}
780
8888d191 781static void
43c02e7b
VB
782lldpd_shutdown(int sig)
783{
784 LLOG_INFO("signal received, exiting");
785 exit(0);
786}
787
788/* For signal handling */
8888d191 789static struct lldpd *gcfg = NULL;
43c02e7b 790
8888d191 791static void
43c02e7b
VB
792lldpd_exit()
793{
6e75df87 794 struct lldpd_hardware *hardware, *hardware_next;
b5562b23
VB
795 close(gcfg->g_ctl);
796 priv_ctl_cleanup();
6e75df87
VB
797 for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL;
798 hardware = hardware_next) {
799 hardware_next = TAILQ_NEXT(hardware, h_entries);
800 lldpd_hardware_cleanup(gcfg, hardware);
43c02e7b
VB
801 }
802#ifdef USE_SNMP
803 if (gcfg->g_snmp)
804 agent_shutdown();
805#endif /* USE_SNMP */
806}
807
808int
2acc1418 809lldpd_main(int argc, char *argv[])
43c02e7b
VB
810{
811 struct lldpd *cfg;
77507b69 812 struct lldpd_chassis *lchassis;
e809a587
VB
813 int ch, debug = 0;
814#ifdef USE_SNMP
815 int snmp = 0;
bbea66e1 816 char *agentx = NULL; /* AgentX socket */
e809a587 817#endif
43c02e7b 818 char *mgmtp = NULL;
993a6e50
VB
819 char *popt, opts[] =
820#ifdef ENABLE_LISTENVLAN
821 "v"
822#endif
bbea66e1 823 "kdxX:m:p:M:i@ ";
de1b1b3a 824 int i, found, advertise_version = 1;
993a6e50
VB
825#ifdef ENABLE_LISTENVLAN
826 int vlan = 0;
827#endif
89840df0 828#ifdef ENABLE_LLDPMED
e809a587 829 int lldpmed = 0, noinventory = 0;
89840df0 830#endif
43c02e7b
VB
831
832 saved_argv = argv;
833
834 /*
835 * Get and parse command line options
836 */
f0bd3505 837 popt = strchr(opts, '@');
43c02e7b
VB
838 for (i=0; protos[i].mode != 0; i++) {
839 if (protos[i].enabled == 1) continue;
840 *(popt++) = protos[i].arg;
841 }
842 *popt = '\0';
843 while ((ch = getopt(argc, argv, opts)) != -1) {
844 switch (ch) {
993a6e50 845#ifdef ENABLE_LISTENVLAN
6a2fa591
VB
846 case 'v':
847 vlan = 1;
848 break;
993a6e50 849#endif
43c02e7b
VB
850 case 'd':
851 debug++;
852 break;
853 case 'm':
854 mgmtp = optarg;
855 break;
de1b1b3a
VB
856 case 'k':
857 advertise_version = 0;
858 break;
e809a587 859#ifdef ENABLE_LLDPMED
115ff55c 860 case 'M':
89840df0 861 lldpmed = atoi(optarg);
e809a587
VB
862 if ((lldpmed < 1) || (lldpmed > 4)) {
863 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 864 usage();
e809a587 865 }
89840df0 866 break;
e809a587 867 case 'i':
e809a587 868 noinventory = 1;
115ff55c 869 break;
e809a587 870#else
115ff55c
VB
871 case 'M':
872 case 'i':
115ff55c 873 case 'P':
e809a587
VB
874 fprintf(stderr, "LLDP-MED support is not built-in\n");
875 usage();
e809a587 876 break;
115ff55c 877#endif
e809a587 878#ifdef USE_SNMP
bbea66e1
V
879 case 'x':
880 snmp = 1;
881 break;
882 case 'X':
43c02e7b 883 snmp = 1;
bbea66e1
V
884 agentx = optarg;
885 break;
e809a587 886#else
bbea66e1
V
887 case 'x':
888 case 'X':
e809a587
VB
889 fprintf(stderr, "SNMP support is not built-in\n");
890 usage();
891#endif
43c02e7b
VB
892 break;
893 default:
894 found = 0;
895 for (i=0; protos[i].mode != 0; i++) {
896 if (protos[i].enabled) continue;
897 if (ch == protos[i].arg) {
898 protos[i].enabled = 1;
899 found = 1;
900 }
901 }
902 if (!found)
903 usage();
904 }
905 }
115ff55c 906
6bb9c4e0 907 log_init(debug, __progname);
4b9a5a23 908 tzset(); /* Get timezone info before chroot */
a2993d83 909
eac2f38a
VB
910 if (!debug) {
911 int pid;
912 char *spid;
913 if (daemon(0, 0) != 0)
914 fatal("failed to detach daemon");
915 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 916 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
917 fatal("unable to open pid file " LLDPD_PID_FILE);
918 if (asprintf(&spid, "%d\n", getpid()) == -1)
919 fatal("unable to create pid file " LLDPD_PID_FILE);
920 if (write(pid, spid, strlen(spid)) == -1)
921 fatal("unable to write pid file " LLDPD_PID_FILE);
922 free(spid);
923 close(pid);
924 }
925
a2993d83 926 priv_init(PRIVSEP_CHROOT);
43c02e7b 927
43c02e7b
VB
928 if ((cfg = (struct lldpd *)
929 calloc(1, sizeof(struct lldpd))) == NULL)
930 fatal(NULL);
931
766f32b3 932 cfg->g_mgmt_pattern = mgmtp;
de1b1b3a 933 cfg->g_advertise_version = advertise_version;
993a6e50 934#ifdef ENABLE_LISTENVLAN
6a2fa591 935 cfg->g_listen_vlans = vlan;
993a6e50 936#endif
43c02e7b
VB
937
938 /* Get ioctl socket */
939 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
940 fatal("failed to get ioctl socket");
941 cfg->g_delay = LLDPD_TX_DELAY;
942
943 /* Set system capabilities */
77507b69
VB
944 if ((lchassis = (struct lldpd_chassis*)
945 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
946 fatal(NULL);
947 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 948 LLDP_CAP_ROUTER;
89840df0
VB
949#ifdef ENABLE_LLDPMED
950 if (lldpmed > 0) {
951 if (lldpmed == LLDPMED_CLASS_III)
77507b69
VB
952 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
953 lchassis->c_med_type = lldpmed;
954 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
115ff55c 955 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
740593ff 956 cfg->g_noinventory = noinventory;
42bddd41
VB
957 } else
958 cfg->g_noinventory = 1;
89840df0 959#endif
43c02e7b
VB
960
961 /* Set TTL */
77507b69 962 lchassis->c_ttl = LLDPD_TTL;
43c02e7b
VB
963
964 cfg->g_protocols = protos;
43c02e7b
VB
965 for (i=0; protos[i].mode != 0; i++)
966 if (protos[i].enabled) {
43c02e7b
VB
967 LLOG_INFO("protocol %s enabled", protos[i].name);
968 } else
969 LLOG_INFO("protocol %s disabled", protos[i].name);
43c02e7b
VB
970
971 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
972 TAILQ_INIT(&cfg->g_chassis);
973 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
9898ac07 974 lchassis->c_refcount++; /* We should always keep a reference to local chassis */
43c02e7b 975
77d7090e
VB
976 TAILQ_INIT(&cfg->g_callbacks);
977
43c02e7b
VB
978#ifdef USE_SNMP
979 if (snmp) {
980 cfg->g_snmp = 1;
bbea66e1 981 agent_init(cfg, agentx, debug);
43c02e7b
VB
982 }
983#endif /* USE_SNMP */
984
985 /* Create socket */
6f1046d1 986 if ((cfg->g_ctl = priv_ctl_create()) == -1)
b5562b23 987 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
77d7090e
VB
988 if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
989 fatalx("unable to add callback for control socket");
43c02e7b 990
43c02e7b
VB
991 gcfg = cfg;
992 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
993 close(cfg->g_ctl);
994 priv_ctl_cleanup();
43c02e7b
VB
995 fatal("unable to set exit function");
996 }
43c02e7b
VB
997
998 /* Signal handling */
b5562b23 999 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
1000 signal(SIGINT, lldpd_shutdown);
1001 signal(SIGTERM, lldpd_shutdown);
1002
1003 for (;;)
1004 lldpd_loop(cfg);
1005
1006 return (0);
1007}