## Convenience library for lldpd and tests
liblldpd_la_SOURCES = frame.h frame.c lldpd.c lldp.c cdp.c sonmp.c edp.c \
- interfaces.c client.c priv.c privsep_fdpass.c dmi.c ctl-server.c
+ interfaces.c client.c priv.c privsep_fdpass.c dmi.c event.c
liblldpd_la_CFLAGS = @LIBEVENT_CFLAGS@
liblldpd_la_LIBADD = libcommon.la @LIBEVENT_LIBS@
# Add SNMP support if needed
}
void
-agent_init(struct lldpd *cfg, char *agentx, int debug)
+agent_init(struct lldpd *cfg, char *agentx)
{
int rc;
{ 0, NULL } };
int
-client_handle_client(struct lldpd *cfg, struct lldpd_callback *callback,
+client_handle_client(struct lldpd *cfg, int fd,
enum hmsg_type type, void *buffer, int n)
{
struct client_handle *ch;
if (ch->type == type) {
answer = NULL; len = 0;
len = ch->handle(cfg, &type, buffer, n, &answer);
- if ((sent = ctl_msg_send(callback->fd, type, answer, len)) == -1)
+ if ((sent = ctl_msg_send(fd, type, answer, len)) == -1)
LLOG_WARN("unable to send answer to client");
free(answer);
return sent;
+++ /dev/null
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "lldpd.h"
-
-#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-static void
-ctl_callback(struct lldpd *cfg, struct lldpd_callback *callback)
-{
- enum hmsg_type type;
- void *buffer = NULL;
- int n;
-
- if ((n = ctl_msg_recv(callback->fd, &type, &buffer)) == -1 ||
- client_handle_client(cfg, callback, type, buffer, n) == -1) {
- close(callback->fd);
- lldpd_callback_del(cfg, callback->fd, ctl_callback);
- }
- free(buffer);
-}
-
-void
-ctl_accept(struct lldpd *cfg, struct lldpd_callback *callback)
-{
- int s;
- if ((s = accept(callback->fd, NULL, NULL)) == -1) {
- LLOG_WARN("unable to accept connection from socket");
- return;
- }
- if (lldpd_callback_add(cfg, s, ctl_callback, NULL) != 0) {
- LLOG_WARN("unable to add callback for new client");
- close(s);
- }
- return;
-}
--- /dev/null
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lldpd.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <event2/event.h>
+
+static void
+levent_log_cb(int severity, const char *msg)
+{
+ switch (severity) {
+ case _EVENT_LOG_DEBUG: log_debug("libevent[debug]: %s", msg); break;
+ case _EVENT_LOG_MSG: log_info ("libevent[info]: %s", msg); break;
+ case _EVENT_LOG_WARN: log_warnx("libevent[warn]: %s", msg); break;
+ case _EVENT_LOG_ERR: log_warnx("libevent[error]: %s", msg); break;
+ }
+}
+
+#if LLDPD_FD_SETSIZE != FD_SETSIZE
+# warning "FD_SETSIZE is set to an inconsistent value."
+#endif
+
+struct lldpd_events {
+ TAILQ_ENTRY(lldpd_events) next;
+ struct event *ev;
+ void *more;
+};
+TAILQ_HEAD(ev_l, lldpd_events);
+
+#define levent_snmp_fds(cfg) ((struct ev_l*)(cfg)->g_snmp_fds)
+#define levent_client_fds(cfg) ((struct ev_l*)(cfg)->g_ctl_clients)
+#define levent_hardware_fds(hardware) ((struct ev_l*)(hardware)->h_recv)
+
+#ifdef USE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/snmp_vars.h>
+
+static void levent_snmp_update(struct lldpd *);
+
+/*
+ * Callback function when we have something to read from SNMP.
+ *
+ * This function is called because we have a read event on one SNMP
+ * file descriptor. When need to call snmp_read() on it.
+ */
+static void
+levent_snmp_read(evutil_socket_t fd, short what, void *arg)
+{
+ (void)what;
+ struct lldpd *cfg = arg;
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+ snmp_read(&fdset);
+ levent_snmp_update(cfg);
+}
+
+/*
+ * Callback function for a SNMP timeout.
+ *
+ * A SNMP timeout has occurred. Call `snmp_timeout()` to handle it.
+ */
+static void
+levent_snmp_timeout(evutil_socket_t fd, short what, void *arg)
+{
+ (void)what; (void)fd;
+ struct lldpd *cfg = arg;
+ snmp_timeout();
+ levent_snmp_update(cfg);
+}
+
+/*
+ * Watch a new SNMP FD.
+ *
+ * @param base The libevent base we are working on.
+ * @param fd The file descriptor we want to watch.
+ *
+ * The file descriptor is appended to the list of file descriptors we
+ * want to watch.
+ */
+static void
+levent_snmp_add_fd(struct lldpd *cfg, int fd)
+{
+ struct event_base *base = cfg->g_base;
+ struct lldpd_events *snmpfd = calloc(1, sizeof(struct lldpd_events));
+ if (!snmpfd) {
+ LLOG_WARN("unable to allocate memory for new SNMP event");
+ return;
+ }
+ evutil_make_socket_nonblocking(fd);
+ if ((snmpfd->ev = event_new(base, fd,
+ EV_READ | EV_PERSIST,
+ levent_snmp_read,
+ cfg)) == NULL) {
+ LLOG_WARNX("unable to allocate a new SNMP event for FD %d", fd);
+ free(snmpfd);
+ return;
+ }
+ if (event_add(snmpfd->ev, NULL) == -1) {
+ LLOG_WARNX("unable to schedule new SNMP event for FD %d", fd);
+ event_free(snmpfd->ev);
+ free(snmpfd);
+ return;
+ }
+ TAILQ_INSERT_TAIL(levent_snmp_fds(cfg), snmpfd, next);
+}
+
+/*
+ * Update SNMP event loop.
+ *
+ * New events are added and some other are removed. This function
+ * should be called every time a SNMP event happens: either when
+ * handling a SNMP packet, a SNMP timeout or when sending a SNMP
+ * packet. This function will keep libevent in sync with NetSNMP.
+ *
+ * @param base The libevent base we are working on.
+ */
+static void
+levent_snmp_update(struct lldpd *cfg)
+{
+ int maxfd = 0;
+ int block = 1;
+ fd_set fdset;
+ struct timeval timeout;
+ static int howmany = 0;
+
+ /* snmp_select_info() can be tricky to understand. We set `block` to
+ 1 to means that we don't request a timeout. snmp_select_info()
+ will reset `block` to 0 if it wants us to setup a timeout. In
+ this timeout, `snmp_timeout()` should be invoked.
+
+ Each FD in `fdset` will need to be watched for reading. If one of
+ them become active, `snmp_read()` should be called on it.
+ */
+
+ FD_ZERO(&fdset);
+ snmp_select_info(&maxfd, &fdset, &timeout, &block);
+
+ /* We need to untrack any event whose FD is not in `fdset`
+ anymore */
+ int added = 0, removed = 0, current = 0;
+ struct lldpd_events *snmpfd, *snmpfd_next;
+ for (snmpfd = TAILQ_FIRST(levent_snmp_fds(cfg));
+ snmpfd;
+ snmpfd = snmpfd_next) {
+ snmpfd_next = TAILQ_NEXT(snmpfd, next);
+ if (event_get_fd(snmpfd->ev) >= maxfd ||
+ (!FD_ISSET(event_get_fd(snmpfd->ev), &fdset))) {
+ event_free(snmpfd->ev);
+ TAILQ_REMOVE(levent_snmp_fds(cfg), snmpfd, next);
+ free(snmpfd);
+ removed++;
+ } else {
+ FD_CLR(event_get_fd(snmpfd->ev), &fdset);
+ current++;
+ }
+ }
+
+ /* Invariant: FD in `fdset` are not in list of FD */
+ for (int fd = 0; fd < maxfd; fd++) {
+ if (FD_ISSET(fd, &fdset)) {
+ levent_snmp_add_fd(cfg, fd);
+ added++;
+ }
+ }
+ current += added;
+ if (howmany != current) {
+ LLOG_DEBUG("added %d events, removed %d events, total of %d events",
+ added, removed, current);
+ howmany = current;
+ }
+
+ /* If needed, handle timeout */
+ if (evtimer_add(cfg->g_snmp_timeout, block?NULL:&timeout) == -1)
+ LLOG_WARNX("unable to schedule timeout function for SNMP");
+}
+#endif /* USE_SNMP */
+
+static void
+levent_ctl_recv(evutil_socket_t fd, short what, void *arg)
+{
+ (void)what;
+ struct lldpd_events *cfd = arg;
+ struct lldpd *cfg = cfd->more;
+ enum hmsg_type type;
+ void *buffer = NULL;
+ int n;
+
+ if ((n = ctl_msg_recv(fd, &type, &buffer)) == -1 ||
+ client_handle_client(cfg, fd, type, buffer, n) == -1) {
+ close(fd);
+ event_free(cfd->ev);
+ TAILQ_REMOVE(levent_client_fds(cfg), cfd, next);
+ free(cfd);
+ }
+ free(buffer);
+}
+
+static void
+levent_ctl_accept(evutil_socket_t fd, short what, void *arg)
+{
+ (void)what;
+ struct lldpd *cfg = arg;
+ struct lldpd_events *cfd = NULL;
+ int s;
+ if ((s = accept(fd, NULL, NULL)) == -1) {
+ LLOG_WARN("unable to accept connection from socket");
+ return;
+ }
+ cfd = calloc(1, sizeof(struct lldpd_events));
+ if (!cfd) {
+ LLOG_WARNX("unable to allocate memory for new client");
+ close(s);
+ return;
+ }
+ evutil_make_socket_nonblocking(s);
+ if ((cfd->ev = event_new(cfg->g_base, s,
+ EV_READ | EV_PERSIST,
+ levent_ctl_recv,
+ cfd)) == NULL) {
+ LLOG_WARNX("unable to allocate a new event for new client");
+ free(cfd);
+ close(s);
+ return;
+ }
+ cfd->more = cfg;
+ if (event_add(cfd->ev, NULL) == -1) {
+ LLOG_WARNX("unable to schedule new event for new client");
+ event_free(cfd->ev);
+ free(cfd);
+ close(s);
+ return;
+ }
+ TAILQ_INSERT_TAIL(levent_client_fds(cfg), cfd, next);
+}
+
+static void
+levent_dump(evutil_socket_t fd, short what, void *arg)
+{
+ (void)fd; (void)what;
+ struct event_base *base = arg;
+ event_base_dump_events(base, stderr);
+}
+static void
+levent_stop(evutil_socket_t fd, short what, void *arg)
+{
+ (void)fd; (void)what;
+ struct event_base *base = arg;
+ event_base_loopbreak(base);
+}
+
+static void
+levent_update_and_send(evutil_socket_t fd, short what, void *arg)
+{
+ (void)fd; (void)what;
+ struct lldpd *cfg = arg;
+ struct timeval tv = {cfg->g_delay, 0};
+ lldpd_loop(cfg);
+ event_add(cfg->g_main_loop, &tv);
+}
+
+static void
+levent_init(struct lldpd *cfg)
+{
+ /* Setup libevent */
+ event_set_log_callback(levent_log_cb);
+ if (!(cfg->g_base = event_base_new()))
+ fatalx("unable to create a new libevent base");
+ LLOG_INFO("libevent %s initialized with %s method",
+ event_get_version(),
+ event_base_get_method(cfg->g_base));
+
+ /* Setup SNMP */
+#ifdef USE_SNMP
+ if (cfg->g_snmp) {
+ agent_init(cfg, cfg->g_snmp_agentx);
+ cfg->g_snmp_timeout = evtimer_new(cfg->g_base,
+ levent_snmp_timeout,
+ cfg);
+ if (!cfg->g_snmp_timeout)
+ fatalx("unable to setup timeout function for SNMP");
+ if ((cfg->g_snmp_fds =
+ malloc(sizeof(struct ev_l))) == NULL)
+ fatalx("unable to allocate memory for SNMP events");
+ TAILQ_INIT(levent_snmp_fds(cfg));
+ }
+#endif
+
+ /* Setup loop that will run every 30 seconds. */
+ if (!(cfg->g_main_loop = event_new(cfg->g_base, -1, 0,
+ levent_update_and_send,
+ cfg)))
+ fatalx("unable to setup main timer");
+ event_active(cfg->g_main_loop, EV_TIMEOUT, 1);
+
+ /* Setup unix socket */
+ if ((cfg->g_ctl_clients =
+ malloc(sizeof(struct ev_l))) == NULL)
+ fatalx("unable to allocate memory for control socket events");
+ TAILQ_INIT(levent_client_fds(cfg));
+ evutil_make_socket_nonblocking(cfg->g_ctl);
+ if ((cfg->g_ctl_event = event_new(cfg->g_base, cfg->g_ctl,
+ EV_READ|EV_PERSIST, levent_ctl_accept, cfg)) == NULL)
+ fatalx("unable to setup control socket event");
+ event_add(cfg->g_ctl_event, NULL);
+
+ /* Signals */
+ evsignal_add(evsignal_new(cfg->g_base, SIGUSR1,
+ levent_dump, cfg->g_base),
+ NULL);
+ evsignal_add(evsignal_new(cfg->g_base, SIGHUP,
+ levent_stop, cfg->g_base),
+ NULL);
+ evsignal_add(evsignal_new(cfg->g_base, SIGINT,
+ levent_stop, cfg->g_base),
+ NULL);
+ evsignal_add(evsignal_new(cfg->g_base, SIGTERM,
+ levent_stop, cfg->g_base),
+ NULL);
+}
+
+/* Initialize libevent and start the event loop */
+void
+levent_loop(struct lldpd *cfg)
+{
+ levent_init(cfg);
+
+ /* libevent loop */
+ do {
+ if (event_base_got_break(cfg->g_base) ||
+ event_base_got_exit(cfg->g_base))
+ break;
+#ifdef USE_SNMP
+ if (cfg->g_snmp) {
+ run_alarms();
+ netsnmp_check_outstanding_agent_requests();
+ /* run_alarms() may establish new connections and then
+ synchronously modify the set of SNMP FD. We need to
+ update them. */
+ levent_snmp_update(cfg);
+ }
+#endif
+ } while (event_base_loop(cfg->g_base, EVLOOP_ONCE) == 0);
+
+#ifdef USE_SNMP
+ if (cfg->g_snmp)
+ agent_shutdown();
+#endif /* USE_SNMP */
+
+}
+
+static void
+levent_hardware_recv(evutil_socket_t fd, short what, void *arg)
+{
+ (void)what;
+ struct lldpd_hardware *hardware = arg;
+ struct lldpd *cfg = hardware->h_cfg;
+ lldpd_recv(cfg, hardware, fd);
+}
+
+void
+levent_hardware_init(struct lldpd_hardware *hardware)
+{
+ if ((hardware->h_recv =
+ malloc(sizeof(struct ev_l))) == NULL) {
+ LLOG_WARNX("unable to allocate memory for %s",
+ hardware->h_ifname);
+ return;
+ }
+ TAILQ_INIT(levent_hardware_fds(hardware));
+}
+
+void
+levent_hardware_add_fd(struct lldpd_hardware *hardware, int fd)
+{
+ if (!hardware->h_recv) return;
+
+ struct lldpd_events *hfd = calloc(1, sizeof(struct lldpd_events));
+ if (!hfd) {
+ LLOG_WARNX("unable to allocate new event for %s",
+ hardware->h_ifname);
+ return;
+ }
+ evutil_make_socket_nonblocking(fd);
+ if ((hfd->ev = event_new(hardware->h_cfg->g_base, fd,
+ EV_READ | EV_PERSIST,
+ levent_hardware_recv,
+ hardware)) == NULL) {
+ LLOG_WARNX("unable to allocate a new event for %s",
+ hardware->h_ifname);
+ free(hfd);
+ return;
+ }
+ if (event_add(hfd->ev, NULL) == -1) {
+ LLOG_WARNX("unable to schedule new event for %s",
+ hardware->h_ifname);
+ event_free(hfd->ev);
+ free(hfd);
+ return;
+ }
+ TAILQ_INSERT_TAIL(levent_hardware_fds(hardware), hfd, next);
+}
+
+void
+levent_hardware_release(struct lldpd_hardware *hardware)
+{
+ if (!hardware->h_recv) return;
+
+ struct lldpd_events *ev, *ev_next;
+ for (ev = TAILQ_FIRST(levent_hardware_fds(hardware));
+ ev;
+ ev = ev_next) {
+ ev_next = TAILQ_NEXT(ev, next);
+ /* We may close several time the same FD. This is harmless. */
+ close(event_get_fd(ev->ev));
+ event_free(ev->ev);
+ TAILQ_REMOVE(levent_hardware_fds(hardware), ev, next);
+ free(ev);
+ }
+ free(levent_hardware_fds(hardware));
+}
}
}
-static void
-iface_fds_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
-{
- int i;
- for (i=0; i < LLDPD_FD_SETSIZE; i++)
- if (FD_ISSET(i, &hardware->h_recvfds)) {
- FD_CLR(i, &hardware->h_recvfds);
- close(i);
- }
-}
-
-
static int
iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
{
if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
return -1;
- hardware->h_sendfd = fd;
-
- /* fd to receive is the same as fd to send */
- FD_SET(fd, &hardware->h_recvfds);
+ hardware->h_sendfd = fd; /* Send */
/* Set filter */
if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
iface_multicast(cfg, hardware->h_ifname, 0);
+ levent_hardware_add_fd(hardware, fd); /* Receive */
LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname,
fd);
return 0;
static int
iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
{
- close(hardware->h_sendfd);
iface_multicast(cfg, hardware->h_ifname, 1);
return 0;
}
if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
return -1;
hardware->h_sendfd = fd;
- FD_SET(fd, &hardware->h_recvfds);
+ levent_hardware_add_fd(hardware, fd);
if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
close(fd);
return status;
close(hardware->h_sendfd);
return -1;
}
- FD_SET(fd, &hardware->h_recvfds);
if ((status = iface_set_filter(mastername, fd)) != 0) {
close(hardware->h_sendfd);
close(fd);
}
iface_multicast(cfg, mastername, 0);
+ levent_hardware_add_fd(hardware, fd);
LLOG_DEBUG("interface %s initialized (fd=%d,master=%s[%d])",
hardware->h_ifname,
hardware->h_sendfd,
static int
iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
{
- iface_fds_close(cfg, hardware); /* h_sendfd is here too */
iface_multicast(cfg, hardware->h_ifname, 1);
iface_multicast(cfg, (char*)hardware->h_data, 1);
free(hardware->h_data);
#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>
-#include <net-snmp/agent/net-snmp-agent-includes.h>
-#include <net-snmp/agent/snmp_vars.h>
-#endif /* USE_SNMP */
-
static void usage(void);
static struct protocol protos[] =
calloc(1, sizeof(struct lldpd_hardware))) == NULL)
return NULL;
+ hardware->h_cfg = cfg;
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_lport.p_ppvids);
TAILQ_INIT(&hardware->h_lport.p_pids);
#endif
+
+ levent_hardware_init(hardware);
return hardware;
}
void
lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
{
- int i;
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. */
if (hardware->h_ops->cleanup)
hardware->h_ops->cleanup(cfg, hardware);
- else {
- free(hardware->h_data);
- 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);
- }
+ levent_hardware_release(hardware);
free(hardware);
}
return release;
}
-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_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) {
struct lldpd_port *port;
}
}
-static void
-lldpd_recv_all(struct lldpd *cfg)
+void
+lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd)
{
- struct lldpd_hardware *hardware;
- struct lldpd_callback *callback, *callback_next;
- fd_set rfds;
- struct timeval tv;
-#ifdef USE_SNMP
- struct timeval snmptv;
- int snmpblock = 0;
-#endif
- int rc, nfds, n;
- char *buffer;
-
- do {
- tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
- if (tv.tv_sec < 0)
- /* We did not send packets in a long time,
- just give up receive for now. */
- break;
- if (tv.tv_sec >= cfg->g_delay)
- tv.tv_sec = cfg->g_delay;
- tv.tv_usec = 0;
-
- FD_ZERO(&rfds);
- nfds = -1;
-
- TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
- /* Ignore if interface is down */
- if ((hardware->h_flags & IFF_RUNNING) == 0)
- continue;
- /* This is quite expensive but we don't rely on internal
- * structure of fd_set. */
- 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(callback, &cfg->g_callbacks, next) {
- FD_SET(callback->fd, &rfds);
- if (nfds < callback->fd)
- nfds = callback->fd;
- }
-
-#ifdef USE_SNMP
- if (cfg->g_snmp) {
- snmpblock = 0;
- memcpy(&snmptv, &tv, sizeof(struct timeval));
- snmp_select_info(&nfds, &rfds, &snmptv, &snmpblock);
- if (snmpblock == 0)
- memcpy(&tv, &snmptv, sizeof(struct timeval));
- }
-#endif /* USE_SNMP */
- if (nfds == -1) {
- sleep(cfg->g_delay);
- return;
- }
-
- rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
- if (rc == -1) {
- if (errno == EINTR)
- continue;
- LLOG_WARN("failure on select");
- break;
- }
-#ifdef USE_SNMP
- if (cfg->g_snmp) {
- if (rc > 0)
- snmp_read(&rfds);
- else if (rc == 0)
- snmp_timeout();
- }
-#endif /* USE_SNMP */
- TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
- for (n = 0; n < LLDPD_FD_SETSIZE; n++)
- if ((FD_ISSET(n, &hardware->h_recvfds)) &&
- (FD_ISSET(n, &rfds))) break;
- if (n == LLDPD_FD_SETSIZE) continue;
- if ((buffer = (char *)malloc(
- hardware->h_mtu)) == NULL) {
- LLOG_WARN("failed to alloc reception buffer");
- continue;
- }
- if ((n = hardware->h_ops->recv(cfg, hardware,
- n, buffer, hardware->h_mtu)) == -1) {
- free(buffer);
- continue;
- }
- hardware->h_rx_cnt++;
- lldpd_decode(cfg, buffer, n, hardware);
- lldpd_hide_all(cfg); /* Immediatly hide */
- free(buffer);
- break;
- }
- 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
- if (cfg->g_snmp) {
- run_alarms();
- netsnmp_check_outstanding_agent_requests();
- }
-#endif /* USE_SNMP */
- } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
+ char *buffer = NULL;
+ int n;
+ if ((buffer = (char *)malloc(hardware->h_mtu)) == NULL) {
+ LLOG_WARN("failed to alloc reception buffer");
+ return;
+ }
+ if ((n = hardware->h_ops->recv(cfg, hardware,
+ fd, buffer,
+ hardware->h_mtu)) == -1) {
+ free(buffer);
+ return;
+ }
+ hardware->h_rx_cnt++;
+ lldpd_decode(cfg, buffer, n, hardware);
+ lldpd_hide_all(cfg); /* Immediatly hide */
+ free(buffer);
}
static void
freeifaddrs(ifap);
}
-static void
+void
lldpd_loop(struct lldpd *cfg)
{
/* Main loop.
2. Clean unwanted (removed) local ports
3. Update local chassis information
4. Send packets
- 5. Receive packets
- 6. Update smart mode
+ 5. Update events
*/
LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
lldpd_update_localports(cfg);
lldpd_cleanup(cfg);
lldpd_update_localchassis(cfg);
lldpd_send_all(cfg);
- lldpd_recv_all(cfg);
}
static void
-lldpd_shutdown(int sig)
-{
- LLOG_INFO("signal received, exiting");
- exit(0);
-}
-
-/* For signal handling */
-static struct lldpd *gcfg = NULL;
-
-static void
-lldpd_exit()
+lldpd_exit(struct lldpd *cfg)
{
struct lldpd_hardware *hardware, *hardware_next;
- close(gcfg->g_ctl);
+ close(cfg->g_ctl);
priv_ctl_cleanup();
- for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL;
+ for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
hardware = hardware_next) {
hardware_next = TAILQ_NEXT(hardware, h_entries);
- lldpd_remote_cleanup(gcfg, hardware, 1);
- lldpd_hardware_cleanup(gcfg, hardware);
+ lldpd_remote_cleanup(cfg, hardware, 1);
+ lldpd_hardware_cleanup(cfg, hardware);
}
-#ifdef USE_SNMP
- if (gcfg->g_snmp)
- agent_shutdown();
-#endif /* USE_SNMP */
}
struct intint { int a; int b; };
priv_init(PRIVSEP_CHROOT);
+ /* Initialization of global configuration */
if ((cfg = (struct lldpd *)
calloc(1, sizeof(struct lldpd))) == NULL)
fatal(NULL);
cfg->g_interfaces = interfaces;
cfg->g_smart = smart;
cfg->g_receiveonly = receiveonly;
+#ifdef USE_SNMP
+ cfg->g_snmp = snmp;
+ cfg->g_snmp_agentx = agentx;
+#endif /* USE_SNMP */
/* Get ioctl socket */
if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
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, agentx, debug);
- }
-#endif /* USE_SNMP */
-
/* Create socket */
if ((cfg->g_ctl = priv_ctl_create()) == -1)
fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
- 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) {
- close(cfg->g_ctl);
- priv_ctl_cleanup();
- fatal("unable to set exit function");
- }
-
- /* Signal handling */
- signal(SIGHUP, lldpd_shutdown);
- signal(SIGINT, lldpd_shutdown);
- signal(SIGTERM, lldpd_shutdown);
- for (;;)
- lldpd_loop(cfg);
+ /* Main loop */
+ levent_loop(cfg);
+ lldpd_exit(cfg);
return (0);
}
#endif
#include "marshal.h"
+/* We don't want to import event2/event.h. We only need those as
+ opaque structs. */
+struct event;
+struct event_base;
+
#define SYSFS_CLASS_NET "/sys/class/net/"
#define SYSFS_CLASS_DMI "/sys/class/dmi/id/"
#define LLDPD_TTL 120
struct lldpd_hardware {
TAILQ_ENTRY(lldpd_hardware) h_entries;
- fd_set h_recvfds; /* FD for reception */
+ struct lldpd *h_cfg; /* Pointer to main configuration */
+ void *h_recv; /* FD for reception */
int h_sendfd; /* FD for sending, only used by h_ops */
struct lldpd_ops *h_ops; /* Hardware-dependent functions */
void *h_data; /* Hardware-dependent data */
MARSHAL_IGNORE(lldpd_hardware, h_entries.tqe_prev)
MARSHAL_IGNORE(lldpd_hardware, h_ops)
MARSHAL_IGNORE(lldpd_hardware, h_data)
+MARSHAL_IGNORE(lldpd_hardware, h_cfg)
MARSHAL_SUBSTRUCT(lldpd_hardware, lldpd_port, h_lport)
MARSHAL_SUBTQ(lldpd_hardware, lldpd_port, h_rports)
MARSHAL_END;
SMART_OUTGOING_ONE_NEIGH)
#define SMART_HIDDEN(port) (port->p_hidden_in)
-
-#define CALLBACK_SIG struct lldpd*, struct lldpd_callback*
-struct lldpd_callback {
- TAILQ_ENTRY(lldpd_callback) next;
- int fd; /* FD that will trigger this callback */
- void(*function)(CALLBACK_SIG); /* Function called */
- void *data; /* Optional data for this callback*/
-};
-
struct lldpd {
int g_sock;
int g_delay;
+ struct event_base *g_base;
+#ifdef USE_SNMP
+#endif
+
struct protocol *g_protocols;
time_t g_lastsent;
int g_lastrid;
int g_smart;
int g_receiveonly;
+ struct event *g_main_loop;
#ifdef USE_SNMP
int g_snmp;
+ struct event *g_snmp_timeout;
+ void *g_snmp_fds;
+ char *g_snmp_agentx;
#endif /* USE_SNMP */
/* Unix socket handling */
int g_ctl;
-
- TAILQ_HEAD(, lldpd_callback) g_callbacks;
+ struct event *g_ctl_event;
+ void *g_ctl_clients;
char *g_mgmt_pattern;
char *g_cid_pattern;
struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface);
void lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *);
void lldpd_chassis_cleanup(struct lldpd_chassis *, int);
-int lldpd_callback_add(struct lldpd *, int, void(*fn)(CALLBACK_SIG), void *);
-void lldpd_callback_del(struct lldpd *, int, void(*fn)(CALLBACK_SIG));
+void lldpd_recv(struct lldpd *, struct lldpd_hardware *, int);
+void lldpd_loop(struct lldpd *);
int lldpd_main(int, char **);
+/* event.c */
+void levent_loop(struct lldpd *);
+void levent_hardware_init(struct lldpd_hardware *);
+void levent_hardware_add_fd(struct lldpd_hardware *, int);
+void levent_hardware_release(struct lldpd_hardware *);
+
/* lldp.c */
int lldp_send(PROTO_SEND_SIG);
int lldp_decode(PROTO_DECODE_SIG);
int ctl_create(char *);
int ctl_connect(char *);
void ctl_cleanup(char *);
-void ctl_accept(struct lldpd *, struct lldpd_callback *);
int ctl_msg_send(int, enum hmsg_type, void *, size_t);
int ctl_msg_recv(int, enum hmsg_type *, void **);
int ctl_msg_send_recv(int, enum hmsg_type,
/* agent.c */
void agent_shutdown(void);
-void agent_init(struct lldpd *, char *, int);
+void agent_init(struct lldpd *, char *);
/* agent_priv.c */
void agent_priv_register_domain(void);
void *, int, void **);
};
-int client_handle_client(struct lldpd *, struct lldpd_callback *,
+int client_handle_client(struct lldpd *, int,
enum hmsg_type, void *, int);
/* priv.c */