]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lldpd.c
16c757bc33fd9bf70ba9edfeff7e06b15fb8a244
[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)
101 {
102 struct lldpd_hardware *hardware;
103 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
104 if (strcmp(hardware->h_ifname, name) == 0)
105 break;
106 }
107 return hardware;
108 }
109
110 struct lldpd_hardware *
111 lldpd_alloc_hardware(struct lldpd *cfg, char *name)
112 {
113 struct lldpd_hardware *hardware;
114
115 if ((hardware = (struct lldpd_hardware *)
116 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
117 return NULL;
118
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);
122
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;
134 }
135
136 #ifdef ENABLE_DOT1
137 void
138 lldpd_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 }
150 #endif
151
152 /* If `all' is true, clear all information, including information that
153 are not refreshed periodically. Port should be freed manually. */
154 void
155 lldpd_port_cleanup(struct lldpd_port *port, int all)
156 {
157 #ifdef ENABLE_LLDPMED
158 int i;
159 if (all)
160 for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
161 free(port->p_med_location[i].data);
162 #endif
163 #ifdef ENABLE_DOT1
164 lldpd_vlan_cleanup(port);
165 #endif
166 free(port->p_id);
167 free(port->p_descr);
168 if (all) {
169 free(port->p_lastframe);
170 if (port->p_chassis) /* chassis may not have been attributed, yet */
171 port->p_chassis->c_refcount--;
172 }
173 }
174
175 void
176 lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
177 {
178 #ifdef ENABLE_LLDPMED
179 free(chassis->c_med_hw);
180 free(chassis->c_med_sw);
181 free(chassis->c_med_fw);
182 free(chassis->c_med_sn);
183 free(chassis->c_med_manuf);
184 free(chassis->c_med_model);
185 free(chassis->c_med_asset);
186 #endif
187 free(chassis->c_id);
188 free(chassis->c_name);
189 free(chassis->c_descr);
190 if (all)
191 free(chassis);
192 }
193
194 void
195 lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
196 {
197 struct lldpd_port *port, *port_next;
198 int del;
199 for (port = TAILQ_FIRST(&hardware->h_rports);
200 port != NULL;
201 port = port_next) {
202 port_next = TAILQ_NEXT(port, p_entries);
203 del = all;
204 if (!del &&
205 (time(NULL) - port->p_lastupdate > port->p_chassis->c_ttl)) {
206 hardware->h_rx_ageout_cnt++;
207 del = 1;
208 }
209 if (del) {
210 TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
211 lldpd_port_cleanup(port, 1);
212 free(port);
213 }
214 }
215 }
216
217 void
218 lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
219 {
220 int i;
221 lldpd_port_cleanup(&hardware->h_lport, 1);
222 /* If we have a dedicated cleanup function, use it. Otherwise,
223 we just free the hardware-dependent data and close all FD
224 in h_recvfds and h_sendfd. */
225 if (hardware->h_ops->cleanup)
226 hardware->h_ops->cleanup(cfg, hardware);
227 else {
228 free(hardware->h_data);
229 for (i=0; i < FD_SETSIZE; i++)
230 if (FD_ISSET(i, &hardware->h_recvfds))
231 close(i);
232 if (hardware->h_sendfd) close(hardware->h_sendfd);
233 }
234 free(hardware);
235 }
236
237 static void
238 lldpd_cleanup(struct lldpd *cfg)
239 {
240 struct lldpd_hardware *hardware, *hardware_next;
241
242 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
243 hardware = hardware_next) {
244 hardware_next = TAILQ_NEXT(hardware, h_entries);
245 if (!hardware->h_flags) {
246 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
247 lldpd_remote_cleanup(cfg, hardware, 1);
248 lldpd_hardware_cleanup(cfg, hardware);
249 } else
250 lldpd_remote_cleanup(cfg, hardware, 0);
251 }
252 }
253
254 static int
255 lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
256 {
257 int i;
258 if (s < ETH_ALEN)
259 return -1;
260 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
261 if (!cfg->g_protocols[i].enabled)
262 continue;
263 if (cfg->g_protocols[i].guess == NULL) {
264 if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ALEN) == 0)
265 return cfg->g_protocols[i].mode;
266 } else {
267 if (cfg->g_protocols[i].guess(frame, s))
268 return cfg->g_protocols[i].mode;
269 }
270 }
271 return -1;
272 }
273
274 static void
275 lldpd_decode(struct lldpd *cfg, char *frame, int s,
276 struct lldpd_hardware *hardware)
277 {
278 int i, result;
279 struct lldpd_chassis *chassis, *ochassis = NULL;
280 struct lldpd_port *port, *oport = NULL;
281 int guess = LLDPD_MODE_LLDP;
282
283 /* Discard VLAN frames */
284 if ((s >= sizeof(struct ethhdr)) &&
285 (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
286 return;
287
288 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
289 if ((oport->p_lastframe != NULL) &&
290 (oport->p_lastframe->size == s) &&
291 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
292 /* Already received the same frame */
293 oport->p_lastupdate = time(NULL);
294 return;
295 }
296 }
297
298 guess = lldpd_guess_type(cfg, frame, s);
299 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
300 if (!cfg->g_protocols[i].enabled)
301 continue;
302 if (cfg->g_protocols[i].mode == guess) {
303 if ((result = cfg->g_protocols[i].decode(cfg, frame,
304 s, hardware, &chassis, &port)) == -1)
305 return;
306 chassis->c_protocol = port->p_protocol =
307 cfg->g_protocols[i].mode;
308 break;
309 }
310 }
311 if (cfg->g_protocols[i].mode == 0) {
312 LLOG_INFO("unable to guess frame type");
313 return;
314 }
315
316 /* Do we already have the same MSAP somewhere? */
317 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
318 if ((port->p_protocol == oport->p_protocol) &&
319 (port->p_id_subtype == oport->p_id_subtype) &&
320 (port->p_id_len == oport->p_id_len) &&
321 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
322 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
323 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
324 (memcmp(chassis->c_id, oport->p_chassis->c_id,
325 chassis->c_id_len) == 0)) {
326 ochassis = oport->p_chassis;
327 break;
328 }
329 }
330 /* No, but do we already know the system? */
331 if (!oport) {
332 TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) {
333 if ((chassis->c_protocol == ochassis->c_protocol) &&
334 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
335 (chassis->c_id_len == ochassis->c_id_len) &&
336 (memcmp(chassis->c_id, ochassis->c_id,
337 chassis->c_id_len) == 0))
338 break;
339 }
340 }
341
342 if (oport) {
343 /* The port is known, remove it before adding it back */
344 TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
345 lldpd_port_cleanup(oport, 1);
346 free(oport);
347 }
348 if (ochassis) {
349 lldpd_update_chassis(ochassis, chassis);
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);
358 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
359 LLOG_DEBUG("Currently, we know %d different systems", i);
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++;
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);
374 return;
375 }
376
377 /* Update chassis `ochassis' with values from `chassis'. */
378 static void
379 lldpd_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
398 static void
399 lldpd_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;
405 #ifdef USE_SNMP
406 int fakeblock = 0;
407 struct timeval *tvp = &tv;
408 #endif
409 int rc, nfds, n;
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 */
425 if ((hardware->h_flags & IFF_RUNNING) == 0)
426 continue;
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 }
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) {
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) {
481 free(buffer);
482 continue;
483 }
484 hardware->h_rx_cnt++;
485 lldpd_decode(cfg, buffer, n, hardware);
486 free(buffer);
487 break;
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)
511 client_handle_client(cfg, client, buffer, n);
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
527 static void
528 lldpd_send_all(struct lldpd *cfg)
529 {
530 struct lldpd_hardware *hardware;
531 struct lldpd_port *port;
532 int i, sent = 0;
533
534 cfg->g_lastsent = time(NULL);
535 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
536 /* Ignore if interface is down */
537 if ((hardware->h_flags & IFF_RUNNING) == 0)
538 continue;
539
540 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
541 if (!cfg->g_protocols[i].enabled)
542 continue;
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 }
554 }
555
556 if (!sent)
557 /* Nothing was sent for this port, let's speak LLDP */
558 cfg->g_protocols[0].send(cfg,
559 hardware);
560 }
561 }
562
563 #ifdef ENABLE_LLDPMED
564 static void
565 lldpd_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
582 static void
583 lldpd_update_localchassis(struct lldpd *cfg)
584 {
585 struct utsname un;
586 char *hp;
587 int f;
588 char status;
589 struct lldpd_hardware *hardware;
590
591 /* Set system name and description */
592 if (uname(&un) != 0)
593 fatal("failed to get system information");
594 if ((hp = priv_gethostbyname()) == NULL)
595 fatal("failed to get system name");
596 free(LOCAL_CHASSIS(cfg)->c_name);
597 free(LOCAL_CHASSIS(cfg)->c_descr);
598 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
599 fatal(NULL);
600 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
601 un.sysname, un.release, un.version, un.machine) == -1)
602 fatal("failed to set system description");
603
604 /* Check forwarding */
605 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
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_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 */
649 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
650 hardware->h_flags = 0;
651
652 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
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);
666 freeifaddrs(ifap);
667 }
668
669 static void
670 lldpd_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);
681 lldpd_cleanup(cfg);
682 lldpd_update_localchassis(cfg);
683 lldpd_send_all(cfg);
684 lldpd_recv_all(cfg);
685 }
686
687 static void
688 lldpd_shutdown(int sig)
689 {
690 LLOG_INFO("signal received, exiting");
691 exit(0);
692 }
693
694 /* For signal handling */
695 static struct lldpd *gcfg = NULL;
696
697 static void
698 lldpd_exit()
699 {
700 struct lldpd_hardware *hardware, *hardware_next;
701 close(gcfg->g_ctl);
702 priv_ctl_cleanup();
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);
707 }
708 #ifdef USE_SNMP
709 if (gcfg->g_snmp)
710 agent_shutdown();
711 #endif /* USE_SNMP */
712 }
713
714 int
715 main(int argc, char *argv[])
716 {
717 struct lldpd *cfg;
718 struct lldpd_chassis *lchassis;
719 int ch, debug = 0;
720 #ifdef USE_SNMP
721 int snmp = 0;
722 #endif
723 char *mgmtp = NULL;
724 char *popt, opts[] = "dxm:p:M:i@ ";
725 int i, found;
726 #ifdef ENABLE_LLDPMED
727 int lldpmed = 0, noinventory = 0;
728 #endif
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;
749 #ifdef ENABLE_LLDPMED
750 case 'M':
751 lldpmed = atoi(optarg);
752 if ((lldpmed < 1) || (lldpmed > 4)) {
753 fprintf(stderr, "-M requires an argument between 1 and 4\n");
754 usage();
755 }
756 break;
757 case 'i':
758 noinventory = 1;
759 break;
760 #else
761 case 'M':
762 case 'i':
763 case 'P':
764 fprintf(stderr, "LLDP-MED support is not built-in\n");
765 usage();
766 break;
767 #endif
768 case 'x':
769 #ifdef USE_SNMP
770 snmp = 1;
771 #else
772 fprintf(stderr, "SNMP support is not built-in\n");
773 usage();
774 #endif
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 }
789
790 log_init(debug);
791
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,
798 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
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
808 priv_init(PRIVSEP_CHROOT);
809
810 if ((cfg = (struct lldpd *)
811 calloc(1, sizeof(struct lldpd))) == NULL)
812 fatal(NULL);
813
814 cfg->g_mgmt_pattern = mgmtp;
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 */
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 |
826 LLDP_CAP_ROUTER;
827 #ifdef ENABLE_LLDPMED
828 if (lldpmed > 0) {
829 if (lldpmed == LLDPMED_CLASS_III)
830 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
831 lchassis->c_med_type = lldpmed;
832 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
833 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
834 cfg->g_noinventory = noinventory;
835 } else
836 cfg->g_noinventory = 1;
837 #endif
838
839 /* Set TTL */
840 lchassis->c_ttl = LLDPD_TTL;
841
842 cfg->g_protocols = protos;
843 for (i=0; protos[i].mode != 0; i++)
844 if (protos[i].enabled) {
845 LLOG_INFO("protocol %s enabled", protos[i].name);
846 } else
847 LLOG_INFO("protocol %s disabled", protos[i].name);
848
849 TAILQ_INIT(&cfg->g_hardware);
850 TAILQ_INIT(&cfg->g_chassis);
851 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
852 lchassis->c_refcount++;
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 */
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);
865
866 gcfg = cfg;
867 if (atexit(lldpd_exit) != 0) {
868 close(cfg->g_ctl);
869 priv_ctl_cleanup();
870 fatal("unable to set exit function");
871 }
872
873 /* Signal handling */
874 signal(SIGHUP, lldpd_shutdown);
875 signal(SIGINT, lldpd_shutdown);
876 signal(SIGTERM, lldpd_shutdown);
877
878 for (;;)
879 lldpd_loop(cfg);
880
881 return (0);
882 }