#include <arpa/inet.h>
#include <net/if_arp.h>
+#if LLDPD_FD_SETSIZE != FD_SETSIZE
+# warning "FD_SETSIZE is set to an inconsistent value."
+#endif
+
#ifdef USE_SNMP
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
static struct protocol protos[] =
{
- { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
+ { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
LLDP_MULTICAST_ADDR },
#ifdef ENABLE_CDP
{ LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
static void
usage(void)
{
- fprintf(stderr, "usage: %s [options]\n", __progname);
+ fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
+
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "-d Do not daemonize.\n");
+ fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n");
+ fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n");
+ fprintf(stderr, "-m IP Specify the management address of this system.\n");
+#ifdef ENABLE_LLDPMED
+ fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
+ fprintf(stderr, " 1 Generic Endpoint (Class I)\n");
+ fprintf(stderr, " 2 Media Endpoint (Class II)\n");
+ fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n");
+ fprintf(stderr, " 4 Network Connectivity Device\n");
+#endif
+#ifdef USE_SNMP
+ fprintf(stderr, "-x Enable SNMP subagent.\n");
+#endif
+#ifdef ENABLE_LISTENVLAN
+ fprintf(stderr, "-v Listen on VLAN as well.\n");
+#endif
+ fprintf(stderr, "\n");
+
+#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP
+ fprintf(stderr, "Additional protocol support.\n");
+#ifdef ENABLE_CDP
+ fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
+#endif
+#ifdef ENABLE_EDP
+ fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
+#endif
+#ifdef ENABLE_FDP
+ fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
+#endif
+#ifdef ENABLE_SONMP
+ fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
+#endif
+
+ fprintf(stderr, "\n");
+#endif
+
fprintf(stderr, "see manual page lldpd(8) for more information\n");
exit(1);
}
strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
+ hardware->h_lport.p_chassis->c_refcount++;
TAILQ_INIT(&hardware->h_rports);
#ifdef ENABLE_LLDPMED
/* If `all' is true, clear all information, including information that
are not refreshed periodically. Port should be freed manually. */
void
-lldpd_port_cleanup(struct lldpd_port *port, int all)
+lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all)
{
#ifdef ENABLE_LLDPMED
int i;
free(port->p_descr);
if (all) {
free(port->p_lastframe);
- if (port->p_chassis) /* chassis may not have been attributed, yet */
+ if (port->p_chassis) { /* chassis may not have been attributed, yet */
port->p_chassis->c_refcount--;
+ port->p_chassis = NULL;
+ }
}
}
}
if (del) {
TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
- lldpd_port_cleanup(port, 1);
+ lldpd_port_cleanup(cfg, port, 1);
free(port);
}
}
lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
{
int i;
- lldpd_port_cleanup(&hardware->h_lport, 1);
+ lldpd_port_cleanup(cfg, &hardware->h_lport, 1);
/* If we have a dedicated cleanup function, use it. Otherwise,
we just free the hardware-dependent data and close all FD
in h_recvfds and h_sendfd. */
hardware->h_ops->cleanup(cfg, hardware);
else {
free(hardware->h_data);
- for (i=0; i < FD_SETSIZE; i++)
+ for (i=0; i < LLDPD_FD_SETSIZE; i++)
if (FD_ISSET(i, &hardware->h_recvfds))
close(i);
if (hardware->h_sendfd) close(hardware->h_sendfd);
lldpd_cleanup(struct lldpd *cfg)
{
struct lldpd_hardware *hardware, *hardware_next;
+ struct lldpd_chassis *chassis, *chassis_next;
for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
hardware = hardware_next) {
} else
lldpd_remote_cleanup(cfg, hardware, 0);
}
+
+ for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
+ chassis = chassis_next) {
+ chassis_next = TAILQ_NEXT(chassis, c_entries);
+ if (chassis->c_refcount == 0) {
+ TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
+ lldpd_chassis_cleanup(chassis, 1);
+ }
+ }
}
static int
if (oport) {
/* The port is known, remove it before adding it back */
TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
- lldpd_port_cleanup(oport, 1);
+ lldpd_port_cleanup(cfg, oport, 1);
free(oport);
}
if (ochassis) {
} else {
/* Chassis not known, add it */
chassis->c_index = ++cfg->g_lastrid;
- port->p_chassis = chassis;
chassis->c_refcount = 0;
TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
port->p_chassis = chassis;
port->p_chassis->c_refcount++;
+ /* Several cases are possible :
+ 1. chassis is new, its refcount was 0. It is now attached
+ to this port, its refcount is 1.
+ 2. chassis already exists and was attached to another
+ port, we increase its refcount accordingly.
+ 3. chassis already exists and was attached to the same
+ port, its refcount was decreased with
+ lldpd_port_cleanup() and is now increased again.
+
+ In all cases, if the port already existed, it has been
+ freed with lldpd_port_cleanup() and therefore, the refcount
+ of the chassis that was attached to it is decreased.
+ */
i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
LLOG_DEBUG("Currently, %s known %d neighbors",
hardware->h_ifname, i);
continue;
/* This is quite expensive but we don't rely on internal
* structure of fd_set. */
- for (n = 0; n < FD_SETSIZE; n++)
+ for (n = 0; n < LLDPD_FD_SETSIZE; n++)
if (FD_ISSET(n, &hardware->h_recvfds)) {
FD_SET(n, &rfds);
if (nfds < n)
}
#endif /* USE_SNMP */
TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
- for (n = 0; n < FD_SETSIZE; n++)
+ for (n = 0; n < LLDPD_FD_SETSIZE; n++)
if ((FD_ISSET(n, &hardware->h_recvfds)) &&
(FD_ISSET(n, &rfds))) break;
- if (n == FD_SETSIZE) continue;
+ if (n == LLDPD_FD_SETSIZE) continue;
if ((buffer = (char *)malloc(
hardware->h_mtu)) == NULL) {
LLOG_WARN("failed to alloc reception buffer");
if (!cfg->g_protocols[i].enabled)
continue;
/* We send only if we have at least one remote system
- * speaking this protocol */
+ * speaking this protocol or if the protocol is forced */
+ if (cfg->g_protocols[i].enabled > 1) {
+ cfg->g_protocols[i].send(cfg, hardware);
+ sent++;
+ continue;
+ }
TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
if (port->p_protocol ==
cfg->g_protocols[i].mode) {
cfg->g_protocols[i].send(cfg,
hardware);
- sent = 1;
+ sent++;
break;
}
}
int ch, debug = 0;
#ifdef USE_SNMP
int snmp = 0;
+ char *agentx = NULL; /* AgentX socket */
#endif
char *mgmtp = NULL;
char *popt, opts[] =
#ifdef ENABLE_LISTENVLAN
"v"
#endif
- "kdxm:p:M:i@ ";
+ "hkdxX:m:p:M:i@ ";
int i, found, advertise_version = 1;
#ifdef ENABLE_LISTENVLAN
int vlan = 0;
* Get and parse command line options
*/
popt = strchr(opts, '@');
- for (i=0; protos[i].mode != 0; i++) {
- if (protos[i].enabled == 1) continue;
+ for (i=0; protos[i].mode != 0; i++)
*(popt++) = protos[i].arg;
- }
*popt = '\0';
while ((ch = getopt(argc, argv, opts)) != -1) {
switch (ch) {
+ case 'h':
+ usage();
+ break;
#ifdef ENABLE_LISTENVLAN
case 'v':
vlan = 1;
usage();
break;
#endif
- case 'x':
#ifdef USE_SNMP
+ case 'x':
snmp = 1;
+ break;
+ case 'X':
+ snmp = 1;
+ agentx = optarg;
+ break;
#else
+ case 'x':
+ case 'X':
fprintf(stderr, "SNMP support is not built-in\n");
usage();
#endif
default:
found = 0;
for (i=0; protos[i].mode != 0; i++) {
- if (protos[i].enabled) continue;
if (ch == protos[i].arg) {
- protos[i].enabled = 1;
+ protos[i].enabled++;
+ /* When an argument enable
+ several protocols, only the
+ first one can be forced. */
+ if (found && protos[i].enabled > 1)
+ protos[i].enabled = 1;
found = 1;
}
}
}
log_init(debug, __progname);
+ tzset(); /* Get timezone info before chroot */
if (!debug) {
int pid;
cfg->g_protocols = protos;
for (i=0; protos[i].mode != 0; i++)
- if (protos[i].enabled) {
+ if (protos[i].enabled > 1)
+ LLOG_INFO("protocol %s enabled and forced", protos[i].name);
+ else if (protos[i].enabled)
LLOG_INFO("protocol %s enabled", protos[i].name);
- } else
+ else
LLOG_INFO("protocol %s disabled", protos[i].name);
TAILQ_INIT(&cfg->g_hardware);
TAILQ_INIT(&cfg->g_chassis);
TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
- lchassis->c_refcount++;
+ lchassis->c_refcount++; /* We should always keep a reference to local chassis */
TAILQ_INIT(&cfg->g_callbacks);
#ifdef USE_SNMP
if (snmp) {
cfg->g_snmp = 1;
- agent_init(cfg, debug);
+ agent_init(cfg, agentx, debug);
}
#endif /* USE_SNMP */