#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 void lldpd_cleanup(struct lldpd *);
static void lldpd_loop(struct lldpd *);
static void lldpd_shutdown(int);
-static void lldpd_exit();
+static void lldpd_exit(void);
static void lldpd_send_all(struct lldpd *);
static void lldpd_recv_all(struct lldpd *);
static int lldpd_guess_type(struct lldpd *, char *, int);
#endif
static char **saved_argv;
+#ifdef HAVE___PROGNAME
+extern const char *__progname;
+#else
+# define __progname "lldpd"
+#endif
static void
usage(void)
{
- extern const char *__progname;
- 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");
+ 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");
+ fprintf(stderr, "-x Enable SNMP subagent.\n");
+#ifdef ENABLE_LISTENVLAN
+ fprintf(stderr, "-v Listen on VLAN as well.\n");
+#endif
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "Protocol support. (Disabled by default)\n");
+ fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
+ fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
+ fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
+ fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
+
+ fprintf(stderr, "\n");
+
fprintf(stderr, "see manual page lldpd(8) for more information\n");
exit(1);
}
struct lldpd_hardware *
-lldpd_get_hardware(struct lldpd *cfg, char *name, struct lldpd_ops *ops)
+lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops)
{
struct lldpd_hardware *hardware;
TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
if ((strcmp(hardware->h_ifname, name) == 0) &&
+ (hardware->h_ifindex == index) &&
((!ops) || (ops == hardware->h_ops)))
break;
}
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);
memcpy(&ochassis->c_entries, &entries, sizeof(entries));
}
+int
+lldpd_callback_add(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG), void *data)
+{
+ struct lldpd_callback *callback;
+ if ((callback = (struct lldpd_callback *)
+ malloc(sizeof(struct lldpd_callback))) == NULL)
+ return -1;
+ callback->fd = fd;
+ callback->function = fn;
+ callback->data = data;
+ TAILQ_INSERT_TAIL(&cfg->g_callbacks, callback, next);
+ return 0;
+}
+
+void
+lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG))
+{
+ struct lldpd_callback *callback, *callback_next;
+ for (callback = TAILQ_FIRST(&cfg->g_callbacks);
+ callback;
+ callback = callback_next) {
+ callback_next = TAILQ_NEXT(callback, next);
+ if ((callback->fd == fd) &&
+ (callback->function = fn)) {
+ free(callback->data);
+ TAILQ_REMOVE(&cfg->g_callbacks, callback, next);
+ free(callback);
+ }
+ }
+}
static void
lldpd_recv_all(struct lldpd *cfg)
{
struct lldpd_hardware *hardware;
- struct lldpd_client *client, *client_next;
+ struct lldpd_callback *callback, *callback_next;
fd_set rfds;
struct timeval tv;
#ifdef USE_SNMP
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)
nfds = n;
}
}
- TAILQ_FOREACH(client, &cfg->g_clients, next) {
- FD_SET(client->fd, &rfds);
- if (nfds < client->fd)
- nfds = client->fd;
+ TAILQ_FOREACH(callback, &cfg->g_callbacks, next) {
+ FD_SET(callback->fd, &rfds);
+ if (nfds < callback->fd)
+ nfds = callback->fd;
}
- FD_SET(cfg->g_ctl, &rfds);
- if (nfds < cfg->g_ctl)
- nfds = cfg->g_ctl;
#ifdef USE_SNMP
if (cfg->g_snmp)
}
#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");
free(buffer);
break;
}
- if (FD_ISSET(cfg->g_ctl, &rfds)) {
- if (ctl_accept(cfg, cfg->g_ctl) == -1)
- LLOG_WARN("unable to accept new client");
- }
- for (client = TAILQ_FIRST(&cfg->g_clients);
- client != NULL;
- client = client_next) {
- client_next = TAILQ_NEXT(client, next);
- if (FD_ISSET(client->fd, &rfds)) {
- /* Got a message */
- if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
- NULL) {
- LLOG_WARN("failed to alloc reception buffer");
- continue;
- }
- if ((n = recv(client->fd, buffer,
- MAX_HMSGSIZE, 0)) == -1) {
- LLOG_WARN("error while receiving message");
- free(buffer);
- continue;
- }
- if (n > 0)
- client_handle_client(cfg, client, buffer, n);
- else
- ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
- free(buffer);
- }
+ for (callback = TAILQ_FIRST(&cfg->g_callbacks);
+ callback;
+ callback = callback_next) {
+ /* Callback function can use TAILQ_REMOVE */
+ callback_next = TAILQ_NEXT(callback, next);
+ if (FD_ISSET(callback->fd, &rfds))
+ callback->function(cfg, callback);
}
#ifdef USE_SNMP
{
struct lldpd_hardware *hardware;
struct lldpd_port *port;
- int i, sent = 0;
+ int i, sent;
cfg->g_lastsent = time(NULL);
TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
if ((hardware->h_flags & IFF_RUNNING) == 0)
continue;
+ sent = 0;
for (i=0; cfg->g_protocols[i].mode != 0; i++) {
if (!cfg->g_protocols[i].enabled)
continue;
free(LOCAL_CHASSIS(cfg)->c_descr);
if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
fatal(NULL);
- if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
- un.sysname, un.release, un.version, un.machine) == -1)
- fatal("failed to set system description");
+ if (cfg->g_advertise_version) {
+ if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
+ un.sysname, un.release, un.version, un.machine) == -1)
+ fatal("failed to set full system description");
+ } else {
+ if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s", un.sysname) == -1)
+ fatal("failed to set minimal system description");
+ }
/* Check forwarding */
if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
lldpd_med(LOCAL_CHASSIS(cfg));
free(LOCAL_CHASSIS(cfg)->c_med_sw);
- LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
+ if (cfg->g_advertise_version)
+ LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
+ else
+ LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
#endif
/* Set chassis ID if needed */
lldpd_ifhandlers ifhs[] = {
lldpd_ifh_bond, /* Handle bond */
lldpd_ifh_eth, /* Handle classic ethernet interfaces */
+#ifdef ENABLE_DOT1
lldpd_ifh_vlan, /* Handle VLAN */
+#endif
lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
NULL
};
}
int
-main(int argc, char *argv[])
+lldpd_main(int argc, char *argv[])
{
struct lldpd *cfg;
struct lldpd_chassis *lchassis;
int ch, debug = 0;
#ifdef USE_SNMP
int snmp = 0;
+ char *agentx = NULL; /* AgentX socket */
#endif
char *mgmtp = NULL;
- char *popt, opts[] = "dxm:p:M:i@ ";
- int i, found;
+ char *popt, opts[] =
+#ifdef ENABLE_LISTENVLAN
+ "v"
+#endif
+ "kdxX:m:p:M:i@ ";
+ int i, found, advertise_version = 1;
+#ifdef ENABLE_LISTENVLAN
+ int vlan = 0;
+#endif
#ifdef ENABLE_LLDPMED
int lldpmed = 0, noinventory = 0;
#endif
/*
* Get and parse command line options
*/
- popt = index(opts, '@');
+ popt = strchr(opts, '@');
for (i=0; protos[i].mode != 0; i++) {
if (protos[i].enabled == 1) continue;
*(popt++) = protos[i].arg;
*popt = '\0';
while ((ch = getopt(argc, argv, opts)) != -1) {
switch (ch) {
+#ifdef ENABLE_LISTENVLAN
+ case 'v':
+ vlan = 1;
+ break;
+#endif
case 'd':
debug++;
break;
case 'm':
mgmtp = optarg;
break;
+ case 'k':
+ advertise_version = 0;
+ break;
#ifdef ENABLE_LLDPMED
case 'M':
lldpmed = atoi(optarg);
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
}
}
- log_init(debug);
+ log_init(debug, __progname);
+ tzset(); /* Get timezone info before chroot */
if (!debug) {
int pid;
fatal(NULL);
cfg->g_mgmt_pattern = mgmtp;
+ cfg->g_advertise_version = advertise_version;
+#ifdef ENABLE_LISTENVLAN
+ cfg->g_listen_vlans = vlan;
+#endif
/* Get ioctl socket */
if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
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 */
/* Create socket */
- if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
+ if ((cfg->g_ctl = priv_ctl_create()) == -1)
fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
- TAILQ_INIT(&cfg->g_clients);
+ if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
+ fatalx("unable to add callback for control socket");
gcfg = cfg;
if (atexit(lldpd_exit) != 0) {