]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
Change the way interface information are collected.
[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
VB
99struct lldpd_hardware *
100lldpd_get_hardware(struct lldpd *cfg, char *name)
43c02e7b 101{
6e75df87
VB
102 struct lldpd_hardware *hardware;
103 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
104 if (strcmp(hardware->h_ifname, name) == 0)
105 break;
43c02e7b 106 }
6e75df87 107 return hardware;
43c02e7b
VB
108}
109
6e75df87
VB
110struct lldpd_hardware *
111lldpd_alloc_hardware(struct lldpd *cfg, char *name)
43c02e7b 112{
6e75df87 113 struct lldpd_hardware *hardware;
43c02e7b 114
6e75df87
VB
115 if ((hardware = (struct lldpd_hardware *)
116 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
117 return NULL;
43c02e7b 118
6e75df87
VB
119 strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
120 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
121 TAILQ_INIT(&hardware->h_rports);
43c02e7b 122
6e75df87
VB
123#ifdef ENABLE_LLDPMED
124 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
125 hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
126 if (!cfg->g_noinventory)
127 hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
128 }
129#endif
130#ifdef ENABLE_DOT1
131 TAILQ_INIT(&hardware->h_lport.p_vlans);
132#endif
133 return hardware;
43c02e7b
VB
134}
135
a1347cd8 136#ifdef ENABLE_DOT1
43c02e7b
VB
137void
138lldpd_vlan_cleanup(struct lldpd_port *port)
139{
140 struct lldpd_vlan *vlan, *vlan_next;
141 for (vlan = TAILQ_FIRST(&port->p_vlans);
142 vlan != NULL;
143 vlan = vlan_next) {
144 free(vlan->v_name);
145 vlan_next = TAILQ_NEXT(vlan, v_entries);
146 TAILQ_REMOVE(&port->p_vlans, vlan, v_entries);
147 free(vlan);
148 }
149}
a1347cd8 150#endif
43c02e7b 151
a0edeaf8
VB
152/* If `all' is true, clear all information, including information that
153 are not refreshed periodically. If `all' is true, also free the
154 port. */
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--;
a0edeaf8 173 free(port);
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);
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);
347 }
348 if (ochassis) {
16f910e1 349 lldpd_update_chassis(ochassis, chassis);
77507b69
VB
350 free(chassis);
351 chassis = ochassis;
352 } else {
353 /* Chassis not known, add it */
354 chassis->c_index = ++cfg->g_lastrid;
355 port->p_chassis = chassis;
356 chassis->c_refcount = 0;
357 TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
f4c43902
VB
358 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
359 LLOG_DEBUG("Currently, we know %d different systems", i);
77507b69
VB
360 }
361 /* Add port */
362 port->p_lastchange = port->p_lastupdate = time(NULL);
363 if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
364 sizeof(int))) != NULL) {
365 port->p_lastframe->size = s;
366 memcpy(port->p_lastframe->frame, frame, s);
367 }
368 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
369 port->p_chassis = chassis;
370 port->p_chassis->c_refcount++;
f4c43902
VB
371 i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
372 LLOG_DEBUG("Currently, %s known %d neighbors",
373 hardware->h_ifname, i);
43c02e7b
VB
374 return;
375}
376
16f910e1
VB
377/* Update chassis `ochassis' with values from `chassis'. */
378static void
379lldpd_update_chassis(struct lldpd_chassis *ochassis,
380 const struct lldpd_chassis *chassis) {
381 TAILQ_ENTRY(lldpd_chassis) entries;
382 /* We want to keep refcount, index and list stuff from the current
383 * chassis */
384 int refcount = ochassis->c_refcount;
385 int index = ochassis->c_index;
386 memcpy(&entries, &ochassis->c_entries,
387 sizeof(entries));
388 /* Make the copy */
389 lldpd_chassis_cleanup(ochassis, 0);
390 memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
391 /* Restore saved values */
392 ochassis->c_refcount = refcount;
393 ochassis->c_index = index;
394 memcpy(&ochassis->c_entries, &entries, sizeof(entries));
395}
396
397
8888d191 398static void
43c02e7b
VB
399lldpd_recv_all(struct lldpd *cfg)
400{
401 struct lldpd_hardware *hardware;
402 struct lldpd_client *client, *client_next;
403 fd_set rfds;
404 struct timeval tv;
43c02e7b
VB
405#ifdef USE_SNMP
406 int fakeblock = 0;
407 struct timeval *tvp = &tv;
408#endif
0bc32943 409 int rc, nfds, n;
43c02e7b
VB
410 char *buffer;
411
412 do {
413 tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
414 if (tv.tv_sec < 0)
415 tv.tv_sec = LLDPD_TX_DELAY;
416 if (tv.tv_sec >= cfg->g_delay)
417 tv.tv_sec = cfg->g_delay;
418 tv.tv_usec = 0;
419
420 FD_ZERO(&rfds);
421 nfds = -1;
422
423 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
424 /* Ignore if interface is down */
6e75df87 425 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b 426 continue;
6e75df87
VB
427 /* This is quite expensive but we don't rely on internal
428 * structure of fd_set. */
429 for (n = 0; n < FD_SETSIZE; n++)
430 if (FD_ISSET(n, &hardware->h_recvfds)) {
431 FD_SET(n, &rfds);
432 if (nfds < n)
433 nfds = n;
434 }
43c02e7b
VB
435 }
436 TAILQ_FOREACH(client, &cfg->g_clients, next) {
437 FD_SET(client->fd, &rfds);
438 if (nfds < client->fd)
439 nfds = client->fd;
440 }
441 FD_SET(cfg->g_ctl, &rfds);
442 if (nfds < cfg->g_ctl)
443 nfds = cfg->g_ctl;
444
445#ifdef USE_SNMP
446 if (cfg->g_snmp)
447 snmp_select_info(&nfds, &rfds, tvp, &fakeblock);
448#endif /* USE_SNMP */
449 if (nfds == -1) {
450 sleep(cfg->g_delay);
451 return;
452 }
453
454 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
455 if (rc == -1) {
456 if (errno == EINTR)
457 continue;
458 LLOG_WARN("failure on select");
459 break;
460 }
461#ifdef USE_SNMP
462 if (cfg->g_snmp) {
463 if (rc > 0)
464 snmp_read(&rfds);
465 else if (rc == 0)
466 snmp_timeout();
467 }
468#endif /* USE_SNMP */
469 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
6e75df87
VB
470 for (n = 0; n < FD_SETSIZE; n++)
471 if ((FD_ISSET(n, &hardware->h_recvfds)) &&
472 (FD_ISSET(n, &rfds))) break;
473 if (n == FD_SETSIZE) continue;
474 if ((buffer = (char *)malloc(
475 hardware->h_mtu)) == NULL) {
476 LLOG_WARN("failed to alloc reception buffer");
477 continue;
478 }
479 if ((n = hardware->h_ops->recv(cfg, hardware,
480 n, buffer, hardware->h_mtu)) == -1) {
43c02e7b 481 free(buffer);
6e75df87 482 continue;
43c02e7b 483 }
6e75df87
VB
484 hardware->h_rx_cnt++;
485 lldpd_decode(cfg, buffer, n, hardware);
486 free(buffer);
487 break;
43c02e7b
VB
488 }
489 if (FD_ISSET(cfg->g_ctl, &rfds)) {
490 if (ctl_accept(cfg, cfg->g_ctl) == -1)
491 LLOG_WARN("unable to accept new client");
492 }
493 for (client = TAILQ_FIRST(&cfg->g_clients);
494 client != NULL;
495 client = client_next) {
496 client_next = TAILQ_NEXT(client, next);
497 if (FD_ISSET(client->fd, &rfds)) {
498 /* Got a message */
499 if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
500 NULL) {
501 LLOG_WARN("failed to alloc reception buffer");
502 continue;
503 }
504 if ((n = recv(client->fd, buffer,
505 MAX_HMSGSIZE, 0)) == -1) {
506 LLOG_WARN("error while receiving message");
507 free(buffer);
508 continue;
509 }
510 if (n > 0)
a552a72e 511 client_handle_client(cfg, client, buffer, n);
43c02e7b
VB
512 else
513 ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
514 free(buffer);
515 }
516 }
517
518#ifdef USE_SNMP
519 if (cfg->g_snmp) {
520 run_alarms();
521 netsnmp_check_outstanding_agent_requests();
522 }
523#endif /* USE_SNMP */
524 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
525}
526
8888d191 527static void
43c02e7b
VB
528lldpd_send_all(struct lldpd *cfg)
529{
530 struct lldpd_hardware *hardware;
77507b69
VB
531 struct lldpd_port *port;
532 int i, sent = 0;
f7db0dd8 533
43c02e7b
VB
534 cfg->g_lastsent = time(NULL);
535 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
536 /* Ignore if interface is down */
6e75df87 537 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b
VB
538 continue;
539
540 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
541 if (!cfg->g_protocols[i].enabled)
542 continue;
77507b69
VB
543 /* We send only if we have at least one remote system
544 * speaking this protocol */
545 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
546 if (port->p_protocol ==
547 cfg->g_protocols[i].mode) {
548 cfg->g_protocols[i].send(cfg,
549 hardware);
550 sent = 1;
551 break;
552 }
553 }
43c02e7b 554 }
77507b69
VB
555
556 if (!sent)
557 /* Nothing was sent for this port, let's speak LLDP */
558 cfg->g_protocols[0].send(cfg,
559 hardware);
43c02e7b
VB
560 }
561}
562
89840df0 563#ifdef ENABLE_LLDPMED
8888d191 564static void
89840df0
VB
565lldpd_med(struct lldpd_chassis *chassis)
566{
567 free(chassis->c_med_hw);
568 free(chassis->c_med_fw);
569 free(chassis->c_med_sn);
570 free(chassis->c_med_manuf);
571 free(chassis->c_med_model);
572 free(chassis->c_med_asset);
573 chassis->c_med_hw = dmi_hw();
574 chassis->c_med_fw = dmi_fw();
575 chassis->c_med_sn = dmi_sn();
576 chassis->c_med_manuf = dmi_manuf();
577 chassis->c_med_model = dmi_model();
578 chassis->c_med_asset = dmi_asset();
579}
580#endif
581
8888d191 582static void
6e75df87 583lldpd_update_localchassis(struct lldpd *cfg)
43c02e7b 584{
6e75df87
VB
585 struct utsname un;
586 char *hp;
43c02e7b
VB
587 int f;
588 char status;
6e75df87 589 struct lldpd_hardware *hardware;
43c02e7b
VB
590
591 /* Set system name and description */
6e75df87 592 if (uname(&un) != 0)
43c02e7b 593 fatal("failed to get system information");
b5562b23 594 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b 595 fatal("failed to get system name");
77507b69
VB
596 free(LOCAL_CHASSIS(cfg)->c_name);
597 free(LOCAL_CHASSIS(cfg)->c_descr);
598 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
249644a4 599 fatal(NULL);
77507b69 600 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
6e75df87 601 un.sysname, un.release, un.version, un.machine) == -1)
43c02e7b 602 fatal("failed to set system description");
43c02e7b
VB
603
604 /* Check forwarding */
77507b69 605 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
b5562b23 606 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
4afe659e 607 if ((read(f, &status, 1) == 1) && (status == '1')) {
77507b69 608 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
4afe659e 609 }
43c02e7b
VB
610 close(f);
611 }
89840df0 612#ifdef ENABLE_LLDPMED
77507b69
VB
613 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
614 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
615 lldpd_med(LOCAL_CHASSIS(cfg));
616 free(LOCAL_CHASSIS(cfg)->c_med_sw);
6e75df87 617 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
89840df0 618#endif
43c02e7b 619
6e75df87
VB
620 /* Set chassis ID if needed */
621 if ((LOCAL_CHASSIS(cfg)->c_id == NULL) &&
622 (hardware = TAILQ_FIRST(&cfg->g_hardware))) {
623 if ((LOCAL_CHASSIS(cfg)->c_id =
624 malloc(sizeof(hardware->h_lladdr))) == NULL)
625 fatal(NULL);
626 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
627 LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
628 memcpy(LOCAL_CHASSIS(cfg)->c_id,
629 hardware->h_lladdr, sizeof(hardware->h_lladdr));
630 }
631}
632
633static void
634lldpd_update_localports(struct lldpd *cfg)
635{
636 struct ifaddrs *ifap;
637 struct lldpd_hardware *hardware;
638 lldpd_ifhandlers ifhs[] = {
639 lldpd_ifh_eth, /* Handle classic ethernet interfaces */
640 lldpd_ifh_vlan, /* Handle VLAN */
641 lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
642 NULL
643 };
644 lldpd_ifhandlers *ifh;
645
646 /* h_flags is set to 0 for each port. If the port is updated, h_flags
647 * will be set to a non-zero value. This will allow us to clean up any
648 * non up-to-date port */
43c02e7b
VB
649 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
650 hardware->h_flags = 0;
651
77507b69 652 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
6e75df87
VB
653 if (getifaddrs(&ifap) != 0)
654 fatal("lldpd_update_localports: failed to get interface list");
655
656 /* We will run the list of interfaces through a list of interface
657 * handlers. Each handler will create or update some hardware port (and
658 * will set h_flags to a non zero value. The handler can use the list of
659 * interfaces but this is not mandatory. If the interface handler
660 * handles an interface from the list, it should set ifa_flags to 0 to
661 * let know the other handlers that it took care of this interface. This
662 * means that more specific handlers should be before less specific
663 * ones. */
664 for (ifh = ifhs; *ifh != NULL; ifh++)
665 (*ifh)(cfg, ifap);
43c02e7b 666 freeifaddrs(ifap);
6e75df87 667}
43c02e7b 668
6e75df87
VB
669static void
670lldpd_loop(struct lldpd *cfg)
671{
672 /* Main loop.
673
674 1. Update local ports information
675 2. Clean unwanted (removed) local ports
676 3. Update local chassis information
677 4. Send packets
678 5. Receive packets
679 */
680 lldpd_update_localports(cfg);
43c02e7b 681 lldpd_cleanup(cfg);
6e75df87 682 lldpd_update_localchassis(cfg);
43c02e7b
VB
683 lldpd_send_all(cfg);
684 lldpd_recv_all(cfg);
685}
686
8888d191 687static void
43c02e7b
VB
688lldpd_shutdown(int sig)
689{
690 LLOG_INFO("signal received, exiting");
691 exit(0);
692}
693
694/* For signal handling */
8888d191 695static struct lldpd *gcfg = NULL;
43c02e7b 696
8888d191 697static void
43c02e7b
VB
698lldpd_exit()
699{
6e75df87 700 struct lldpd_hardware *hardware, *hardware_next;
b5562b23
VB
701 close(gcfg->g_ctl);
702 priv_ctl_cleanup();
6e75df87
VB
703 for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL;
704 hardware = hardware_next) {
705 hardware_next = TAILQ_NEXT(hardware, h_entries);
706 lldpd_hardware_cleanup(gcfg, hardware);
43c02e7b
VB
707 }
708#ifdef USE_SNMP
709 if (gcfg->g_snmp)
710 agent_shutdown();
711#endif /* USE_SNMP */
712}
713
714int
715main(int argc, char *argv[])
716{
717 struct lldpd *cfg;
77507b69 718 struct lldpd_chassis *lchassis;
e809a587
VB
719 int ch, debug = 0;
720#ifdef USE_SNMP
721 int snmp = 0;
722#endif
43c02e7b 723 char *mgmtp = NULL;
71936c67 724 char *popt, opts[] = "dxm:p:M:i@ ";
f2dcb180 725 int i, found;
89840df0 726#ifdef ENABLE_LLDPMED
e809a587 727 int lldpmed = 0, noinventory = 0;
89840df0 728#endif
43c02e7b
VB
729
730 saved_argv = argv;
731
732 /*
733 * Get and parse command line options
734 */
735 popt = index(opts, '@');
736 for (i=0; protos[i].mode != 0; i++) {
737 if (protos[i].enabled == 1) continue;
738 *(popt++) = protos[i].arg;
739 }
740 *popt = '\0';
741 while ((ch = getopt(argc, argv, opts)) != -1) {
742 switch (ch) {
743 case 'd':
744 debug++;
745 break;
746 case 'm':
747 mgmtp = optarg;
748 break;
e809a587 749#ifdef ENABLE_LLDPMED
115ff55c 750 case 'M':
89840df0 751 lldpmed = atoi(optarg);
e809a587
VB
752 if ((lldpmed < 1) || (lldpmed > 4)) {
753 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 754 usage();
e809a587 755 }
89840df0 756 break;
e809a587 757 case 'i':
e809a587 758 noinventory = 1;
115ff55c 759 break;
e809a587 760#else
115ff55c
VB
761 case 'M':
762 case 'i':
115ff55c 763 case 'P':
e809a587
VB
764 fprintf(stderr, "LLDP-MED support is not built-in\n");
765 usage();
e809a587 766 break;
115ff55c 767#endif
43c02e7b 768 case 'x':
e809a587 769#ifdef USE_SNMP
43c02e7b 770 snmp = 1;
e809a587
VB
771#else
772 fprintf(stderr, "SNMP support is not built-in\n");
773 usage();
774#endif
43c02e7b
VB
775 break;
776 default:
777 found = 0;
778 for (i=0; protos[i].mode != 0; i++) {
779 if (protos[i].enabled) continue;
780 if (ch == protos[i].arg) {
781 protos[i].enabled = 1;
782 found = 1;
783 }
784 }
785 if (!found)
786 usage();
787 }
788 }
115ff55c 789
43c02e7b 790 log_init(debug);
a2993d83 791
eac2f38a
VB
792 if (!debug) {
793 int pid;
794 char *spid;
795 if (daemon(0, 0) != 0)
796 fatal("failed to detach daemon");
797 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 798 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
799 fatal("unable to open pid file " LLDPD_PID_FILE);
800 if (asprintf(&spid, "%d\n", getpid()) == -1)
801 fatal("unable to create pid file " LLDPD_PID_FILE);
802 if (write(pid, spid, strlen(spid)) == -1)
803 fatal("unable to write pid file " LLDPD_PID_FILE);
804 free(spid);
805 close(pid);
806 }
807
a2993d83 808 priv_init(PRIVSEP_CHROOT);
43c02e7b 809
43c02e7b
VB
810 if ((cfg = (struct lldpd *)
811 calloc(1, sizeof(struct lldpd))) == NULL)
812 fatal(NULL);
813
766f32b3 814 cfg->g_mgmt_pattern = mgmtp;
43c02e7b
VB
815
816 /* Get ioctl socket */
817 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
818 fatal("failed to get ioctl socket");
819 cfg->g_delay = LLDPD_TX_DELAY;
820
821 /* Set system capabilities */
77507b69
VB
822 if ((lchassis = (struct lldpd_chassis*)
823 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
824 fatal(NULL);
825 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 826 LLDP_CAP_ROUTER;
89840df0
VB
827#ifdef ENABLE_LLDPMED
828 if (lldpmed > 0) {
829 if (lldpmed == LLDPMED_CLASS_III)
77507b69
VB
830 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
831 lchassis->c_med_type = lldpmed;
832 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
115ff55c 833 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
740593ff 834 cfg->g_noinventory = noinventory;
42bddd41
VB
835 } else
836 cfg->g_noinventory = 1;
89840df0 837#endif
43c02e7b
VB
838
839 /* Set TTL */
77507b69 840 lchassis->c_ttl = LLDPD_TTL;
43c02e7b
VB
841
842 cfg->g_protocols = protos;
43c02e7b
VB
843 for (i=0; protos[i].mode != 0; i++)
844 if (protos[i].enabled) {
43c02e7b
VB
845 LLOG_INFO("protocol %s enabled", protos[i].name);
846 } else
847 LLOG_INFO("protocol %s disabled", protos[i].name);
43c02e7b
VB
848
849 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
850 TAILQ_INIT(&cfg->g_chassis);
851 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
852 lchassis->c_refcount++;
43c02e7b
VB
853
854#ifdef USE_SNMP
855 if (snmp) {
856 cfg->g_snmp = 1;
857 agent_init(cfg, debug);
858 }
859#endif /* USE_SNMP */
860
861 /* Create socket */
b5562b23
VB
862 if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
863 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
864 TAILQ_INIT(&cfg->g_clients);
43c02e7b 865
43c02e7b
VB
866 gcfg = cfg;
867 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
868 close(cfg->g_ctl);
869 priv_ctl_cleanup();
43c02e7b
VB
870 fatal("unable to set exit function");
871 }
43c02e7b
VB
872
873 /* Signal handling */
b5562b23 874 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
875 signal(SIGINT, lldpd_shutdown);
876 signal(SIGTERM, lldpd_shutdown);
877
878 for (;;)
879 lldpd_loop(cfg);
880
881 return (0);
882}