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