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