]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lldpd.c
Reset "sent" flag for each port when sending.
[thirdparty/lldpd.git] / src / lldpd.c
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>
25 #include <time.h>
26 #include <libgen.h>
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>
34 #include <net/if_arp.h>
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
43 static void usage(void);
44
45 static struct protocol protos[] =
46 {
47 { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
48 LLDP_MULTICAST_ADDR },
49 #ifdef ENABLE_CDP
50 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
51 CDP_MULTICAST_ADDR },
52 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
53 CDP_MULTICAST_ADDR },
54 #endif
55 #ifdef ENABLE_SONMP
56 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
57 SONMP_MULTICAST_ADDR },
58 #endif
59 #ifdef ENABLE_EDP
60 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
61 EDP_MULTICAST_ADDR },
62 #endif
63 #ifdef ENABLE_FDP
64 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
65 FDP_MULTICAST_ADDR },
66 #endif
67 { 0, 0, "any", ' ', NULL, NULL, NULL,
68 {0,0,0,0,0,0} }
69 };
70
71 static void lldpd_update_localchassis(struct lldpd *);
72 static void lldpd_update_localports(struct lldpd *);
73 static void lldpd_cleanup(struct lldpd *);
74 static void lldpd_loop(struct lldpd *);
75 static void lldpd_shutdown(int);
76 static void lldpd_exit(void);
77 static void lldpd_send_all(struct lldpd *);
78 static void lldpd_recv_all(struct lldpd *);
79 static int lldpd_guess_type(struct lldpd *, char *, int);
80 static void lldpd_decode(struct lldpd *, char *, int,
81 struct lldpd_hardware *);
82 static void lldpd_update_chassis(struct lldpd_chassis *,
83 const struct lldpd_chassis *);
84 #ifdef ENABLE_LLDPMED
85 static void lldpd_med(struct lldpd_chassis *);
86 #endif
87
88 static char **saved_argv;
89 #ifdef HAVE___PROGNAME
90 extern const char *__progname;
91 #else
92 # define __progname "lldpd"
93 #endif
94
95 static void
96 usage(void)
97 {
98 fprintf(stderr, "usage: %s [options]\n", __progname);
99 fprintf(stderr, "see manual page lldpd(8) for more information\n");
100 exit(1);
101 }
102
103 struct lldpd_hardware *
104 lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops)
105 {
106 struct lldpd_hardware *hardware;
107 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
108 if ((strcmp(hardware->h_ifname, name) == 0) &&
109 (hardware->h_ifindex == index) &&
110 ((!ops) || (ops == hardware->h_ops)))
111 break;
112 }
113 return hardware;
114 }
115
116 struct lldpd_hardware *
117 lldpd_alloc_hardware(struct lldpd *cfg, char *name)
118 {
119 struct lldpd_hardware *hardware;
120
121 if ((hardware = (struct lldpd_hardware *)
122 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
123 return NULL;
124
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);
128
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;
140 }
141
142 #ifdef ENABLE_DOT1
143 void
144 lldpd_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 }
156 #endif
157
158 /* If `all' is true, clear all information, including information that
159 are not refreshed periodically. Port should be freed manually. */
160 void
161 lldpd_port_cleanup(struct lldpd_port *port, int all)
162 {
163 #ifdef ENABLE_LLDPMED
164 int i;
165 if (all)
166 for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
167 free(port->p_med_location[i].data);
168 #endif
169 #ifdef ENABLE_DOT1
170 lldpd_vlan_cleanup(port);
171 #endif
172 free(port->p_id);
173 free(port->p_descr);
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--;
178 }
179 }
180
181 void
182 lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
183 {
184 #ifdef ENABLE_LLDPMED
185 free(chassis->c_med_hw);
186 free(chassis->c_med_sw);
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
193 free(chassis->c_id);
194 free(chassis->c_name);
195 free(chassis->c_descr);
196 if (all)
197 free(chassis);
198 }
199
200 void
201 lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
202 {
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);
218 free(port);
219 }
220 }
221 }
222
223 void
224 lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
225 {
226 int i;
227 lldpd_port_cleanup(&hardware->h_lport, 1);
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 }
240 free(hardware);
241 }
242
243 static void
244 lldpd_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);
251 if (!hardware->h_flags) {
252 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
253 lldpd_remote_cleanup(cfg, hardware, 1);
254 lldpd_hardware_cleanup(cfg, hardware);
255 } else
256 lldpd_remote_cleanup(cfg, hardware, 0);
257 }
258 }
259
260 static int
261 lldpd_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
280 static void
281 lldpd_decode(struct lldpd *cfg, char *frame, int s,
282 struct lldpd_hardware *hardware)
283 {
284 int i, result;
285 struct lldpd_chassis *chassis, *ochassis = NULL;
286 struct lldpd_port *port, *oport = NULL;
287 int guess = LLDPD_MODE_LLDP;
288
289 /* Discard VLAN frames */
290 if ((s >= sizeof(struct ethhdr)) &&
291 (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
292 return;
293
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 }
302 }
303
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;
312 chassis->c_protocol = port->p_protocol =
313 cfg->g_protocols[i].mode;
314 break;
315 }
316 }
317 if (cfg->g_protocols[i].mode == 0) {
318 LLOG_INFO("unable to guess frame type");
319 return;
320 }
321
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 }
335 }
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;
345 }
346 }
347
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);
352 free(oport);
353 }
354 if (ochassis) {
355 lldpd_update_chassis(ochassis, chassis);
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);
364 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
365 LLOG_DEBUG("Currently, we know %d different systems", i);
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++;
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);
380 return;
381 }
382
383 /* Update chassis `ochassis' with values from `chassis'. */
384 static void
385 lldpd_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
403 int
404 lldpd_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
417 void
418 lldpd_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 }
433
434 static void
435 lldpd_recv_all(struct lldpd *cfg)
436 {
437 struct lldpd_hardware *hardware;
438 struct lldpd_callback *callback, *callback_next;
439 fd_set rfds;
440 struct timeval tv;
441 #ifdef USE_SNMP
442 int fakeblock = 0;
443 struct timeval *tvp = &tv;
444 #endif
445 int rc, nfds, n;
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 */
461 if ((hardware->h_flags & IFF_RUNNING) == 0)
462 continue;
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 }
471 }
472 TAILQ_FOREACH(callback, &cfg->g_callbacks, next) {
473 FD_SET(callback->fd, &rfds);
474 if (nfds < callback->fd)
475 nfds = callback->fd;
476 }
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) {
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) {
514 free(buffer);
515 continue;
516 }
517 hardware->h_rx_cnt++;
518 lldpd_decode(cfg, buffer, n, hardware);
519 free(buffer);
520 break;
521 }
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);
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
540 static void
541 lldpd_send_all(struct lldpd *cfg)
542 {
543 struct lldpd_hardware *hardware;
544 struct lldpd_port *port;
545 int i, sent;
546
547 cfg->g_lastsent = time(NULL);
548 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
549 /* Ignore if interface is down */
550 if ((hardware->h_flags & IFF_RUNNING) == 0)
551 continue;
552
553 sent = 0;
554 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
555 if (!cfg->g_protocols[i].enabled)
556 continue;
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 }
568 }
569
570 if (!sent)
571 /* Nothing was sent for this port, let's speak LLDP */
572 cfg->g_protocols[0].send(cfg,
573 hardware);
574 }
575 }
576
577 #ifdef ENABLE_LLDPMED
578 static void
579 lldpd_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
596 static void
597 lldpd_update_localchassis(struct lldpd *cfg)
598 {
599 struct utsname un;
600 char *hp;
601 int f;
602 char status;
603 struct lldpd_hardware *hardware;
604
605 /* Set system name and description */
606 if (uname(&un) != 0)
607 fatal("failed to get system information");
608 if ((hp = priv_gethostbyname()) == NULL)
609 fatal("failed to get system name");
610 free(LOCAL_CHASSIS(cfg)->c_name);
611 free(LOCAL_CHASSIS(cfg)->c_descr);
612 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
613 fatal(NULL);
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 }
622
623 /* Check forwarding */
624 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
625 if ((read(f, &status, 1) == 1) && (status == '1')) {
626 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
627 }
628 close(f);
629 }
630 #ifdef ENABLE_LLDPMED
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);
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");
639 #endif
640
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
654 static void
655 lldpd_update_localports(struct lldpd *cfg)
656 {
657 struct ifaddrs *ifap;
658 struct lldpd_hardware *hardware;
659 lldpd_ifhandlers ifhs[] = {
660 lldpd_ifh_bond, /* Handle bond */
661 lldpd_ifh_eth, /* Handle classic ethernet interfaces */
662 #ifdef ENABLE_DOT1
663 lldpd_ifh_vlan, /* Handle VLAN */
664 #endif
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 */
673 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
674 hardware->h_flags = 0;
675
676 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
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);
690 freeifaddrs(ifap);
691 }
692
693 static void
694 lldpd_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 */
704 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
705 lldpd_update_localports(cfg);
706 lldpd_cleanup(cfg);
707 lldpd_update_localchassis(cfg);
708 lldpd_send_all(cfg);
709 lldpd_recv_all(cfg);
710 }
711
712 static void
713 lldpd_shutdown(int sig)
714 {
715 LLOG_INFO("signal received, exiting");
716 exit(0);
717 }
718
719 /* For signal handling */
720 static struct lldpd *gcfg = NULL;
721
722 static void
723 lldpd_exit()
724 {
725 struct lldpd_hardware *hardware, *hardware_next;
726 close(gcfg->g_ctl);
727 priv_ctl_cleanup();
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);
732 }
733 #ifdef USE_SNMP
734 if (gcfg->g_snmp)
735 agent_shutdown();
736 #endif /* USE_SNMP */
737 }
738
739 int
740 lldpd_main(int argc, char *argv[])
741 {
742 struct lldpd *cfg;
743 struct lldpd_chassis *lchassis;
744 int ch, debug = 0;
745 #ifdef USE_SNMP
746 int snmp = 0;
747 #endif
748 char *mgmtp = NULL;
749 char *popt, opts[] =
750 #ifdef ENABLE_LISTENVLAN
751 "v"
752 #endif
753 "kdxm:p:M:i@ ";
754 int i, found, advertise_version = 1;
755 #ifdef ENABLE_LISTENVLAN
756 int vlan = 0;
757 #endif
758 #ifdef ENABLE_LLDPMED
759 int lldpmed = 0, noinventory = 0;
760 #endif
761
762 saved_argv = argv;
763
764 /*
765 * Get and parse command line options
766 */
767 popt = strchr(opts, '@');
768 for (i=0; protos[i].mode != 0; i++) {
769 if (protos[i].enabled == 1) continue;
770 *(popt++) = protos[i].arg;
771 }
772 *popt = '\0';
773 while ((ch = getopt(argc, argv, opts)) != -1) {
774 switch (ch) {
775 #ifdef ENABLE_LISTENVLAN
776 case 'v':
777 vlan = 1;
778 break;
779 #endif
780 case 'd':
781 debug++;
782 break;
783 case 'm':
784 mgmtp = optarg;
785 break;
786 case 'k':
787 advertise_version = 0;
788 break;
789 #ifdef ENABLE_LLDPMED
790 case 'M':
791 lldpmed = atoi(optarg);
792 if ((lldpmed < 1) || (lldpmed > 4)) {
793 fprintf(stderr, "-M requires an argument between 1 and 4\n");
794 usage();
795 }
796 break;
797 case 'i':
798 noinventory = 1;
799 break;
800 #else
801 case 'M':
802 case 'i':
803 case 'P':
804 fprintf(stderr, "LLDP-MED support is not built-in\n");
805 usage();
806 break;
807 #endif
808 case 'x':
809 #ifdef USE_SNMP
810 snmp = 1;
811 #else
812 fprintf(stderr, "SNMP support is not built-in\n");
813 usage();
814 #endif
815 break;
816 default:
817 found = 0;
818 for (i=0; protos[i].mode != 0; i++) {
819 if (protos[i].enabled) continue;
820 if (ch == protos[i].arg) {
821 protos[i].enabled = 1;
822 found = 1;
823 }
824 }
825 if (!found)
826 usage();
827 }
828 }
829
830 log_init(debug, __progname);
831
832 if (!debug) {
833 int pid;
834 char *spid;
835 if (daemon(0, 0) != 0)
836 fatal("failed to detach daemon");
837 if ((pid = open(LLDPD_PID_FILE,
838 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
839 fatal("unable to open pid file " LLDPD_PID_FILE);
840 if (asprintf(&spid, "%d\n", getpid()) == -1)
841 fatal("unable to create pid file " LLDPD_PID_FILE);
842 if (write(pid, spid, strlen(spid)) == -1)
843 fatal("unable to write pid file " LLDPD_PID_FILE);
844 free(spid);
845 close(pid);
846 }
847
848 priv_init(PRIVSEP_CHROOT);
849
850 if ((cfg = (struct lldpd *)
851 calloc(1, sizeof(struct lldpd))) == NULL)
852 fatal(NULL);
853
854 cfg->g_mgmt_pattern = mgmtp;
855 cfg->g_advertise_version = advertise_version;
856 #ifdef ENABLE_LISTENVLAN
857 cfg->g_listen_vlans = vlan;
858 #endif
859
860 /* Get ioctl socket */
861 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
862 fatal("failed to get ioctl socket");
863 cfg->g_delay = LLDPD_TX_DELAY;
864
865 /* Set system capabilities */
866 if ((lchassis = (struct lldpd_chassis*)
867 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
868 fatal(NULL);
869 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
870 LLDP_CAP_ROUTER;
871 #ifdef ENABLE_LLDPMED
872 if (lldpmed > 0) {
873 if (lldpmed == LLDPMED_CLASS_III)
874 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
875 lchassis->c_med_type = lldpmed;
876 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
877 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
878 cfg->g_noinventory = noinventory;
879 } else
880 cfg->g_noinventory = 1;
881 #endif
882
883 /* Set TTL */
884 lchassis->c_ttl = LLDPD_TTL;
885
886 cfg->g_protocols = protos;
887 for (i=0; protos[i].mode != 0; i++)
888 if (protos[i].enabled) {
889 LLOG_INFO("protocol %s enabled", protos[i].name);
890 } else
891 LLOG_INFO("protocol %s disabled", protos[i].name);
892
893 TAILQ_INIT(&cfg->g_hardware);
894 TAILQ_INIT(&cfg->g_chassis);
895 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
896 lchassis->c_refcount++;
897
898 TAILQ_INIT(&cfg->g_callbacks);
899
900 #ifdef USE_SNMP
901 if (snmp) {
902 cfg->g_snmp = 1;
903 agent_init(cfg, debug);
904 }
905 #endif /* USE_SNMP */
906
907 /* Create socket */
908 if ((cfg->g_ctl = priv_ctl_create()) == -1)
909 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
910 if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
911 fatalx("unable to add callback for control socket");
912
913 gcfg = cfg;
914 if (atexit(lldpd_exit) != 0) {
915 close(cfg->g_ctl);
916 priv_ctl_cleanup();
917 fatal("unable to set exit function");
918 }
919
920 /* Signal handling */
921 signal(SIGHUP, lldpd_shutdown);
922 signal(SIGINT, lldpd_shutdown);
923 signal(SIGTERM, lldpd_shutdown);
924
925 for (;;)
926 lldpd_loop(cfg);
927
928 return (0);
929 }