2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
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.
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.
27 #include <sys/utsname.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/select.h>
32 #include <sys/ioctl.h>
33 #include <arpa/inet.h>
34 #include <net/if_arp.h>
36 #if LLDPD_FD_SETSIZE != FD_SETSIZE
37 # warning "FD_SETSIZE is set to an inconsistent value."
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>
47 static void usage(void);
49 static struct protocol protos
[] =
51 { LLDPD_MODE_LLDP
, 1, "LLDP", ' ', lldp_send
, lldp_decode
, NULL
,
52 LLDP_MULTICAST_ADDR
},
54 { LLDPD_MODE_CDPV1
, 0, "CDPv1", 'c', cdpv1_send
, cdp_decode
, cdpv1_guess
,
56 { LLDPD_MODE_CDPV2
, 0, "CDPv2", 'c', cdpv2_send
, cdp_decode
, cdpv2_guess
,
60 { LLDPD_MODE_SONMP
, 0, "SONMP", 's', sonmp_send
, sonmp_decode
, NULL
,
61 SONMP_MULTICAST_ADDR
},
64 { LLDPD_MODE_EDP
, 0, "EDP", 'e', edp_send
, edp_decode
, NULL
,
68 { LLDPD_MODE_FDP
, 0, "FDP", 'f', fdp_send
, cdp_decode
, NULL
,
71 { 0, 0, "any", ' ', NULL
, NULL
, NULL
,
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
*);
89 static void lldpd_med(struct lldpd_chassis
*);
92 static char **saved_argv
;
93 #ifdef HAVE___PROGNAME
94 extern const char *__progname
;
96 # define __progname "lldpd"
102 fprintf(stderr
, "Usage: %s [OPTIONS ...]\n", __progname
);
104 fprintf(stderr
, "\n");
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 fprintf(stderr
, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
111 fprintf(stderr
, " 1 Generic Endpoint (Class I)\n");
112 fprintf(stderr
, " 2 Media Endpoint (Class II)\n");
113 fprintf(stderr
, " 3 Communication Device Endpoints (Class III)\n");
114 fprintf(stderr
, " 4 Network Connectivity Device\n");
115 fprintf(stderr
, "-x Enable SNMP subagent.\n");
116 #ifdef ENABLE_LISTENVLAN
117 fprintf(stderr
, "-v Listen on VLAN as well.\n");
119 fprintf(stderr
, "\n");
121 fprintf(stderr
, "Protocol support. (Disabled by default)\n");
122 fprintf(stderr
, "-c Enable the support of CDP protocol. (Cisco)\n");
123 fprintf(stderr
, "-e Enable the support of EDP protocol. (Extreme)\n");
124 fprintf(stderr
, "-f Enable the support of FDP protocol. (Foundry)\n");
125 fprintf(stderr
, "-s Enable the support of SONMP protocol. (Nortel)\n");
127 fprintf(stderr
, "\n");
129 fprintf(stderr
, "see manual page lldpd(8) for more information\n");
133 struct lldpd_hardware
*
134 lldpd_get_hardware(struct lldpd
*cfg
, char *name
, int index
, struct lldpd_ops
*ops
)
136 struct lldpd_hardware
*hardware
;
137 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
138 if ((strcmp(hardware
->h_ifname
, name
) == 0) &&
139 (hardware
->h_ifindex
== index
) &&
140 ((!ops
) || (ops
== hardware
->h_ops
)))
146 struct lldpd_hardware
*
147 lldpd_alloc_hardware(struct lldpd
*cfg
, char *name
)
149 struct lldpd_hardware
*hardware
;
151 if ((hardware
= (struct lldpd_hardware
*)
152 calloc(1, sizeof(struct lldpd_hardware
))) == NULL
)
155 strlcpy(hardware
->h_ifname
, name
, sizeof(hardware
->h_ifname
));
156 hardware
->h_lport
.p_chassis
= LOCAL_CHASSIS(cfg
);
157 hardware
->h_lport
.p_chassis
->c_refcount
++;
158 TAILQ_INIT(&hardware
->h_rports
);
160 #ifdef ENABLE_LLDPMED
161 if (LOCAL_CHASSIS(cfg
)->c_med_cap_available
) {
162 hardware
->h_lport
.p_med_cap_enabled
= LLDPMED_CAP_CAP
;
163 if (!cfg
->g_noinventory
)
164 hardware
->h_lport
.p_med_cap_enabled
|= LLDPMED_CAP_IV
;
168 TAILQ_INIT(&hardware
->h_lport
.p_vlans
);
175 lldpd_vlan_cleanup(struct lldpd_port
*port
)
177 struct lldpd_vlan
*vlan
, *vlan_next
;
178 for (vlan
= TAILQ_FIRST(&port
->p_vlans
);
182 vlan_next
= TAILQ_NEXT(vlan
, v_entries
);
183 TAILQ_REMOVE(&port
->p_vlans
, vlan
, v_entries
);
189 /* If `all' is true, clear all information, including information that
190 are not refreshed periodically. Port should be freed manually. */
192 lldpd_port_cleanup(struct lldpd
*cfg
, struct lldpd_port
*port
, int all
)
194 #ifdef ENABLE_LLDPMED
197 for (i
=0; i
< LLDPMED_LOCFORMAT_LAST
; i
++)
198 free(port
->p_med_location
[i
].data
);
201 lldpd_vlan_cleanup(port
);
206 free(port
->p_lastframe
);
207 if (port
->p_chassis
) { /* chassis may not have been attributed, yet */
208 port
->p_chassis
->c_refcount
--;
209 port
->p_chassis
= NULL
;
215 lldpd_chassis_cleanup(struct lldpd_chassis
*chassis
, int all
)
217 #ifdef ENABLE_LLDPMED
218 free(chassis
->c_med_hw
);
219 free(chassis
->c_med_sw
);
220 free(chassis
->c_med_fw
);
221 free(chassis
->c_med_sn
);
222 free(chassis
->c_med_manuf
);
223 free(chassis
->c_med_model
);
224 free(chassis
->c_med_asset
);
227 free(chassis
->c_name
);
228 free(chassis
->c_descr
);
234 lldpd_remote_cleanup(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
, int all
)
236 struct lldpd_port
*port
, *port_next
;
238 for (port
= TAILQ_FIRST(&hardware
->h_rports
);
241 port_next
= TAILQ_NEXT(port
, p_entries
);
244 (time(NULL
) - port
->p_lastupdate
> port
->p_chassis
->c_ttl
)) {
245 hardware
->h_rx_ageout_cnt
++;
249 TAILQ_REMOVE(&hardware
->h_rports
, port
, p_entries
);
250 lldpd_port_cleanup(cfg
, port
, 1);
257 lldpd_hardware_cleanup(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
260 lldpd_port_cleanup(cfg
, &hardware
->h_lport
, 1);
261 /* If we have a dedicated cleanup function, use it. Otherwise,
262 we just free the hardware-dependent data and close all FD
263 in h_recvfds and h_sendfd. */
264 if (hardware
->h_ops
->cleanup
)
265 hardware
->h_ops
->cleanup(cfg
, hardware
);
267 free(hardware
->h_data
);
268 for (i
=0; i
< LLDPD_FD_SETSIZE
; i
++)
269 if (FD_ISSET(i
, &hardware
->h_recvfds
))
271 if (hardware
->h_sendfd
) close(hardware
->h_sendfd
);
277 lldpd_cleanup(struct lldpd
*cfg
)
279 struct lldpd_hardware
*hardware
, *hardware_next
;
280 struct lldpd_chassis
*chassis
, *chassis_next
;
282 for (hardware
= TAILQ_FIRST(&cfg
->g_hardware
); hardware
!= NULL
;
283 hardware
= hardware_next
) {
284 hardware_next
= TAILQ_NEXT(hardware
, h_entries
);
285 if (!hardware
->h_flags
) {
286 TAILQ_REMOVE(&cfg
->g_hardware
, hardware
, h_entries
);
287 lldpd_remote_cleanup(cfg
, hardware
, 1);
288 lldpd_hardware_cleanup(cfg
, hardware
);
290 lldpd_remote_cleanup(cfg
, hardware
, 0);
293 for (chassis
= TAILQ_FIRST(&cfg
->g_chassis
); chassis
;
294 chassis
= chassis_next
) {
295 chassis_next
= TAILQ_NEXT(chassis
, c_entries
);
296 if (chassis
->c_refcount
== 0) {
297 TAILQ_REMOVE(&cfg
->g_chassis
, chassis
, c_entries
);
298 lldpd_chassis_cleanup(chassis
, 1);
304 lldpd_guess_type(struct lldpd
*cfg
, char *frame
, int s
)
309 for (i
=0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
310 if (!cfg
->g_protocols
[i
].enabled
)
312 if (cfg
->g_protocols
[i
].guess
== NULL
) {
313 if (memcmp(frame
, cfg
->g_protocols
[i
].mac
, ETH_ALEN
) == 0)
314 return cfg
->g_protocols
[i
].mode
;
316 if (cfg
->g_protocols
[i
].guess(frame
, s
))
317 return cfg
->g_protocols
[i
].mode
;
324 lldpd_decode(struct lldpd
*cfg
, char *frame
, int s
,
325 struct lldpd_hardware
*hardware
)
328 struct lldpd_chassis
*chassis
, *ochassis
= NULL
;
329 struct lldpd_port
*port
, *oport
= NULL
;
330 int guess
= LLDPD_MODE_LLDP
;
332 /* Discard VLAN frames */
333 if ((s
>= sizeof(struct ethhdr
)) &&
334 (((struct ethhdr
*)frame
)->h_proto
== htons(ETHERTYPE_VLAN
)))
337 TAILQ_FOREACH(oport
, &hardware
->h_rports
, p_entries
) {
338 if ((oport
->p_lastframe
!= NULL
) &&
339 (oport
->p_lastframe
->size
== s
) &&
340 (memcmp(oport
->p_lastframe
->frame
, frame
, s
) == 0)) {
341 /* Already received the same frame */
342 oport
->p_lastupdate
= time(NULL
);
347 guess
= lldpd_guess_type(cfg
, frame
, s
);
348 for (i
=0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
349 if (!cfg
->g_protocols
[i
].enabled
)
351 if (cfg
->g_protocols
[i
].mode
== guess
) {
352 if ((result
= cfg
->g_protocols
[i
].decode(cfg
, frame
,
353 s
, hardware
, &chassis
, &port
)) == -1)
355 chassis
->c_protocol
= port
->p_protocol
=
356 cfg
->g_protocols
[i
].mode
;
360 if (cfg
->g_protocols
[i
].mode
== 0) {
361 LLOG_INFO("unable to guess frame type");
365 /* Do we already have the same MSAP somewhere? */
366 TAILQ_FOREACH(oport
, &hardware
->h_rports
, p_entries
) {
367 if ((port
->p_protocol
== oport
->p_protocol
) &&
368 (port
->p_id_subtype
== oport
->p_id_subtype
) &&
369 (port
->p_id_len
== oport
->p_id_len
) &&
370 (memcmp(port
->p_id
, oport
->p_id
, port
->p_id_len
) == 0) &&
371 (chassis
->c_id_subtype
== oport
->p_chassis
->c_id_subtype
) &&
372 (chassis
->c_id_len
== oport
->p_chassis
->c_id_len
) &&
373 (memcmp(chassis
->c_id
, oport
->p_chassis
->c_id
,
374 chassis
->c_id_len
) == 0)) {
375 ochassis
= oport
->p_chassis
;
379 /* No, but do we already know the system? */
381 TAILQ_FOREACH(ochassis
, &cfg
->g_chassis
, c_entries
) {
382 if ((chassis
->c_protocol
== ochassis
->c_protocol
) &&
383 (chassis
->c_id_subtype
== ochassis
->c_id_subtype
) &&
384 (chassis
->c_id_len
== ochassis
->c_id_len
) &&
385 (memcmp(chassis
->c_id
, ochassis
->c_id
,
386 chassis
->c_id_len
) == 0))
392 /* The port is known, remove it before adding it back */
393 TAILQ_REMOVE(&hardware
->h_rports
, oport
, p_entries
);
394 lldpd_port_cleanup(cfg
, oport
, 1);
398 lldpd_update_chassis(ochassis
, chassis
);
402 /* Chassis not known, add it */
403 chassis
->c_index
= ++cfg
->g_lastrid
;
404 chassis
->c_refcount
= 0;
405 TAILQ_INSERT_TAIL(&cfg
->g_chassis
, chassis
, c_entries
);
406 i
= 0; TAILQ_FOREACH(ochassis
, &cfg
->g_chassis
, c_entries
) i
++;
407 LLOG_DEBUG("Currently, we know %d different systems", i
);
410 port
->p_lastchange
= port
->p_lastupdate
= time(NULL
);
411 if ((port
->p_lastframe
= (struct lldpd_frame
*)malloc(s
+
412 sizeof(int))) != NULL
) {
413 port
->p_lastframe
->size
= s
;
414 memcpy(port
->p_lastframe
->frame
, frame
, s
);
416 TAILQ_INSERT_TAIL(&hardware
->h_rports
, port
, p_entries
);
417 port
->p_chassis
= chassis
;
418 port
->p_chassis
->c_refcount
++;
419 /* Several cases are possible :
420 1. chassis is new, its refcount was 0. It is now attached
421 to this port, its refcount is 1.
422 2. chassis already exists and was attached to another
423 port, we increase its refcount accordingly.
424 3. chassis already exists and was attached to the same
425 port, its refcount was decreased with
426 lldpd_port_cleanup() and is now increased again.
428 In all cases, if the port already existed, it has been
429 freed with lldpd_port_cleanup() and therefore, the refcount
430 of the chassis that was attached to it is decreased.
432 i
= 0; TAILQ_FOREACH(oport
, &hardware
->h_rports
, p_entries
) i
++;
433 LLOG_DEBUG("Currently, %s known %d neighbors",
434 hardware
->h_ifname
, i
);
438 /* Update chassis `ochassis' with values from `chassis'. */
440 lldpd_update_chassis(struct lldpd_chassis
*ochassis
,
441 const struct lldpd_chassis
*chassis
) {
442 TAILQ_ENTRY(lldpd_chassis
) entries
;
443 /* We want to keep refcount, index and list stuff from the current
445 int refcount
= ochassis
->c_refcount
;
446 int index
= ochassis
->c_index
;
447 memcpy(&entries
, &ochassis
->c_entries
,
450 lldpd_chassis_cleanup(ochassis
, 0);
451 memcpy(ochassis
, chassis
, sizeof(struct lldpd_chassis
));
452 /* Restore saved values */
453 ochassis
->c_refcount
= refcount
;
454 ochassis
->c_index
= index
;
455 memcpy(&ochassis
->c_entries
, &entries
, sizeof(entries
));
459 lldpd_callback_add(struct lldpd
*cfg
, int fd
, void(*fn
)(CALLBACK_SIG
), void *data
)
461 struct lldpd_callback
*callback
;
462 if ((callback
= (struct lldpd_callback
*)
463 malloc(sizeof(struct lldpd_callback
))) == NULL
)
466 callback
->function
= fn
;
467 callback
->data
= data
;
468 TAILQ_INSERT_TAIL(&cfg
->g_callbacks
, callback
, next
);
473 lldpd_callback_del(struct lldpd
*cfg
, int fd
, void(*fn
)(CALLBACK_SIG
))
475 struct lldpd_callback
*callback
, *callback_next
;
476 for (callback
= TAILQ_FIRST(&cfg
->g_callbacks
);
478 callback
= callback_next
) {
479 callback_next
= TAILQ_NEXT(callback
, next
);
480 if ((callback
->fd
== fd
) &&
481 (callback
->function
= fn
)) {
482 free(callback
->data
);
483 TAILQ_REMOVE(&cfg
->g_callbacks
, callback
, next
);
490 lldpd_recv_all(struct lldpd
*cfg
)
492 struct lldpd_hardware
*hardware
;
493 struct lldpd_callback
*callback
, *callback_next
;
498 struct timeval
*tvp
= &tv
;
504 tv
.tv_sec
= cfg
->g_delay
- (time(NULL
) - cfg
->g_lastsent
);
506 tv
.tv_sec
= LLDPD_TX_DELAY
;
507 if (tv
.tv_sec
>= cfg
->g_delay
)
508 tv
.tv_sec
= cfg
->g_delay
;
514 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
515 /* Ignore if interface is down */
516 if ((hardware
->h_flags
& IFF_RUNNING
) == 0)
518 /* This is quite expensive but we don't rely on internal
519 * structure of fd_set. */
520 for (n
= 0; n
< LLDPD_FD_SETSIZE
; n
++)
521 if (FD_ISSET(n
, &hardware
->h_recvfds
)) {
527 TAILQ_FOREACH(callback
, &cfg
->g_callbacks
, next
) {
528 FD_SET(callback
->fd
, &rfds
);
529 if (nfds
< callback
->fd
)
535 snmp_select_info(&nfds
, &rfds
, tvp
, &fakeblock
);
536 #endif /* USE_SNMP */
542 rc
= select(nfds
+ 1, &rfds
, NULL
, NULL
, &tv
);
546 LLOG_WARN("failure on select");
556 #endif /* USE_SNMP */
557 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
558 for (n
= 0; n
< LLDPD_FD_SETSIZE
; n
++)
559 if ((FD_ISSET(n
, &hardware
->h_recvfds
)) &&
560 (FD_ISSET(n
, &rfds
))) break;
561 if (n
== LLDPD_FD_SETSIZE
) continue;
562 if ((buffer
= (char *)malloc(
563 hardware
->h_mtu
)) == NULL
) {
564 LLOG_WARN("failed to alloc reception buffer");
567 if ((n
= hardware
->h_ops
->recv(cfg
, hardware
,
568 n
, buffer
, hardware
->h_mtu
)) == -1) {
572 hardware
->h_rx_cnt
++;
573 lldpd_decode(cfg
, buffer
, n
, hardware
);
577 for (callback
= TAILQ_FIRST(&cfg
->g_callbacks
);
579 callback
= callback_next
) {
580 /* Callback function can use TAILQ_REMOVE */
581 callback_next
= TAILQ_NEXT(callback
, next
);
582 if (FD_ISSET(callback
->fd
, &rfds
))
583 callback
->function(cfg
, callback
);
589 netsnmp_check_outstanding_agent_requests();
591 #endif /* USE_SNMP */
592 } while ((rc
!= 0) || (time(NULL
) - cfg
->g_lastsent
< cfg
->g_delay
));
596 lldpd_send_all(struct lldpd
*cfg
)
598 struct lldpd_hardware
*hardware
;
599 struct lldpd_port
*port
;
602 cfg
->g_lastsent
= time(NULL
);
603 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
604 /* Ignore if interface is down */
605 if ((hardware
->h_flags
& IFF_RUNNING
) == 0)
609 for (i
=0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
610 if (!cfg
->g_protocols
[i
].enabled
)
612 /* We send only if we have at least one remote system
613 * speaking this protocol */
614 TAILQ_FOREACH(port
, &hardware
->h_rports
, p_entries
) {
615 if (port
->p_protocol
==
616 cfg
->g_protocols
[i
].mode
) {
617 cfg
->g_protocols
[i
].send(cfg
,
626 /* Nothing was sent for this port, let's speak LLDP */
627 cfg
->g_protocols
[0].send(cfg
,
632 #ifdef ENABLE_LLDPMED
634 lldpd_med(struct lldpd_chassis
*chassis
)
636 free(chassis
->c_med_hw
);
637 free(chassis
->c_med_fw
);
638 free(chassis
->c_med_sn
);
639 free(chassis
->c_med_manuf
);
640 free(chassis
->c_med_model
);
641 free(chassis
->c_med_asset
);
642 chassis
->c_med_hw
= dmi_hw();
643 chassis
->c_med_fw
= dmi_fw();
644 chassis
->c_med_sn
= dmi_sn();
645 chassis
->c_med_manuf
= dmi_manuf();
646 chassis
->c_med_model
= dmi_model();
647 chassis
->c_med_asset
= dmi_asset();
652 lldpd_update_localchassis(struct lldpd
*cfg
)
658 struct lldpd_hardware
*hardware
;
660 /* Set system name and description */
662 fatal("failed to get system information");
663 if ((hp
= priv_gethostbyname()) == NULL
)
664 fatal("failed to get system name");
665 free(LOCAL_CHASSIS(cfg
)->c_name
);
666 free(LOCAL_CHASSIS(cfg
)->c_descr
);
667 if ((LOCAL_CHASSIS(cfg
)->c_name
= strdup(hp
)) == NULL
)
669 if (cfg
->g_advertise_version
) {
670 if (asprintf(&LOCAL_CHASSIS(cfg
)->c_descr
, "%s %s %s %s",
671 un
.sysname
, un
.release
, un
.version
, un
.machine
) == -1)
672 fatal("failed to set full system description");
674 if (asprintf(&LOCAL_CHASSIS(cfg
)->c_descr
, "%s", un
.sysname
) == -1)
675 fatal("failed to set minimal system description");
678 /* Check forwarding */
679 if ((f
= priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
680 if ((read(f
, &status
, 1) == 1) && (status
== '1')) {
681 LOCAL_CHASSIS(cfg
)->c_cap_enabled
= LLDP_CAP_ROUTER
;
685 #ifdef ENABLE_LLDPMED
686 if (LOCAL_CHASSIS(cfg
)->c_cap_available
& LLDP_CAP_TELEPHONE
)
687 LOCAL_CHASSIS(cfg
)->c_cap_enabled
|= LLDP_CAP_TELEPHONE
;
688 lldpd_med(LOCAL_CHASSIS(cfg
));
689 free(LOCAL_CHASSIS(cfg
)->c_med_sw
);
690 if (cfg
->g_advertise_version
)
691 LOCAL_CHASSIS(cfg
)->c_med_sw
= strdup(un
.release
);
693 LOCAL_CHASSIS(cfg
)->c_med_sw
= strdup("Unknown");
696 /* Set chassis ID if needed */
697 if ((LOCAL_CHASSIS(cfg
)->c_id
== NULL
) &&
698 (hardware
= TAILQ_FIRST(&cfg
->g_hardware
))) {
699 if ((LOCAL_CHASSIS(cfg
)->c_id
=
700 malloc(sizeof(hardware
->h_lladdr
))) == NULL
)
702 LOCAL_CHASSIS(cfg
)->c_id_subtype
= LLDP_CHASSISID_SUBTYPE_LLADDR
;
703 LOCAL_CHASSIS(cfg
)->c_id_len
= sizeof(hardware
->h_lladdr
);
704 memcpy(LOCAL_CHASSIS(cfg
)->c_id
,
705 hardware
->h_lladdr
, sizeof(hardware
->h_lladdr
));
710 lldpd_update_localports(struct lldpd
*cfg
)
712 struct ifaddrs
*ifap
;
713 struct lldpd_hardware
*hardware
;
714 lldpd_ifhandlers ifhs
[] = {
715 lldpd_ifh_bond
, /* Handle bond */
716 lldpd_ifh_eth
, /* Handle classic ethernet interfaces */
718 lldpd_ifh_vlan
, /* Handle VLAN */
720 lldpd_ifh_mgmt
, /* Handle management address (if not already handled) */
723 lldpd_ifhandlers
*ifh
;
725 /* h_flags is set to 0 for each port. If the port is updated, h_flags
726 * will be set to a non-zero value. This will allow us to clean up any
727 * non up-to-date port */
728 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
)
729 hardware
->h_flags
= 0;
731 LOCAL_CHASSIS(cfg
)->c_mgmt
.s_addr
= INADDR_ANY
;
732 if (getifaddrs(&ifap
) != 0)
733 fatal("lldpd_update_localports: failed to get interface list");
735 /* We will run the list of interfaces through a list of interface
736 * handlers. Each handler will create or update some hardware port (and
737 * will set h_flags to a non zero value. The handler can use the list of
738 * interfaces but this is not mandatory. If the interface handler
739 * handles an interface from the list, it should set ifa_flags to 0 to
740 * let know the other handlers that it took care of this interface. This
741 * means that more specific handlers should be before less specific
743 for (ifh
= ifhs
; *ifh
!= NULL
; ifh
++)
749 lldpd_loop(struct lldpd
*cfg
)
753 1. Update local ports information
754 2. Clean unwanted (removed) local ports
755 3. Update local chassis information
759 LOCAL_CHASSIS(cfg
)->c_cap_enabled
= 0;
760 lldpd_update_localports(cfg
);
762 lldpd_update_localchassis(cfg
);
768 lldpd_shutdown(int sig
)
770 LLOG_INFO("signal received, exiting");
774 /* For signal handling */
775 static struct lldpd
*gcfg
= NULL
;
780 struct lldpd_hardware
*hardware
, *hardware_next
;
783 for (hardware
= TAILQ_FIRST(&gcfg
->g_hardware
); hardware
!= NULL
;
784 hardware
= hardware_next
) {
785 hardware_next
= TAILQ_NEXT(hardware
, h_entries
);
786 lldpd_hardware_cleanup(gcfg
, hardware
);
791 #endif /* USE_SNMP */
795 lldpd_main(int argc
, char *argv
[])
798 struct lldpd_chassis
*lchassis
;
802 char *agentx
= NULL
; /* AgentX socket */
806 #ifdef ENABLE_LISTENVLAN
810 int i
, found
, advertise_version
= 1;
811 #ifdef ENABLE_LISTENVLAN
814 #ifdef ENABLE_LLDPMED
815 int lldpmed
= 0, noinventory
= 0;
821 * Get and parse command line options
823 popt
= strchr(opts
, '@');
824 for (i
=0; protos
[i
].mode
!= 0; i
++) {
825 if (protos
[i
].enabled
== 1) continue;
826 *(popt
++) = protos
[i
].arg
;
829 while ((ch
= getopt(argc
, argv
, opts
)) != -1) {
831 #ifdef ENABLE_LISTENVLAN
843 advertise_version
= 0;
845 #ifdef ENABLE_LLDPMED
847 lldpmed
= atoi(optarg
);
848 if ((lldpmed
< 1) || (lldpmed
> 4)) {
849 fprintf(stderr
, "-M requires an argument between 1 and 4\n");
860 fprintf(stderr
, "LLDP-MED support is not built-in\n");
875 fprintf(stderr
, "SNMP support is not built-in\n");
881 for (i
=0; protos
[i
].mode
!= 0; i
++) {
882 if (protos
[i
].enabled
) continue;
883 if (ch
== protos
[i
].arg
) {
884 protos
[i
].enabled
= 1;
893 log_init(debug
, __progname
);
894 tzset(); /* Get timezone info before chroot */
899 if (daemon(0, 0) != 0)
900 fatal("failed to detach daemon");
901 if ((pid
= open(LLDPD_PID_FILE
,
902 O_TRUNC
| O_CREAT
| O_WRONLY
, 0644)) == -1)
903 fatal("unable to open pid file " LLDPD_PID_FILE
);
904 if (asprintf(&spid
, "%d\n", getpid()) == -1)
905 fatal("unable to create pid file " LLDPD_PID_FILE
);
906 if (write(pid
, spid
, strlen(spid
)) == -1)
907 fatal("unable to write pid file " LLDPD_PID_FILE
);
912 priv_init(PRIVSEP_CHROOT
);
914 if ((cfg
= (struct lldpd
*)
915 calloc(1, sizeof(struct lldpd
))) == NULL
)
918 cfg
->g_mgmt_pattern
= mgmtp
;
919 cfg
->g_advertise_version
= advertise_version
;
920 #ifdef ENABLE_LISTENVLAN
921 cfg
->g_listen_vlans
= vlan
;
924 /* Get ioctl socket */
925 if ((cfg
->g_sock
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1)
926 fatal("failed to get ioctl socket");
927 cfg
->g_delay
= LLDPD_TX_DELAY
;
929 /* Set system capabilities */
930 if ((lchassis
= (struct lldpd_chassis
*)
931 calloc(1, sizeof(struct lldpd_chassis
))) == NULL
)
933 lchassis
->c_cap_available
= LLDP_CAP_BRIDGE
| LLDP_CAP_WLAN
|
935 #ifdef ENABLE_LLDPMED
937 if (lldpmed
== LLDPMED_CLASS_III
)
938 lchassis
->c_cap_available
|= LLDP_CAP_TELEPHONE
;
939 lchassis
->c_med_type
= lldpmed
;
940 lchassis
->c_med_cap_available
= LLDPMED_CAP_CAP
|
941 LLDPMED_CAP_IV
| LLDPMED_CAP_LOCATION
;
942 cfg
->g_noinventory
= noinventory
;
944 cfg
->g_noinventory
= 1;
948 lchassis
->c_ttl
= LLDPD_TTL
;
950 cfg
->g_protocols
= protos
;
951 for (i
=0; protos
[i
].mode
!= 0; i
++)
952 if (protos
[i
].enabled
) {
953 LLOG_INFO("protocol %s enabled", protos
[i
].name
);
955 LLOG_INFO("protocol %s disabled", protos
[i
].name
);
957 TAILQ_INIT(&cfg
->g_hardware
);
958 TAILQ_INIT(&cfg
->g_chassis
);
959 TAILQ_INSERT_TAIL(&cfg
->g_chassis
, lchassis
, c_entries
);
960 lchassis
->c_refcount
++; /* We should always keep a reference to local chassis */
962 TAILQ_INIT(&cfg
->g_callbacks
);
967 agent_init(cfg
, agentx
, debug
);
969 #endif /* USE_SNMP */
972 if ((cfg
->g_ctl
= priv_ctl_create()) == -1)
973 fatalx("unable to create control socket " LLDPD_CTL_SOCKET
);
974 if (lldpd_callback_add(cfg
, cfg
->g_ctl
, ctl_accept
, NULL
) != 0)
975 fatalx("unable to add callback for control socket");
978 if (atexit(lldpd_exit
) != 0) {
981 fatal("unable to set exit function");
984 /* Signal handling */
985 signal(SIGHUP
, lldpd_shutdown
);
986 signal(SIGINT
, lldpd_shutdown
);
987 signal(SIGTERM
, lldpd_shutdown
);