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