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