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