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