+ Allow to filter debug logs using tokens. Add more debug logs.
+ lldpctl can now output JSON.
+ Use netlink to gather interface information on Linux.
+ + Detect interface changes with netlink as well on Linux and
+ trigger interface updates in a timely manner.
+ Don't use ioctl for bridges anymore on Linux. The configure
option `--enable-oldies` allow to reenable their uses for
systems not supporting sysfs.
}
free(levent_hardware_fds(hardware));
}
+
+static void
+levent_iface_trigger(evutil_socket_t fd, short what, void *arg)
+{
+ struct lldpd *cfg = arg;
+ log_info("event",
+ "triggering update of all interfaces");
+ lldpd_update_localports(cfg);
+}
+
+static void
+levent_iface_recv(evutil_socket_t fd, short what, void *arg)
+{
+ struct lldpd *cfg = arg;
+ char buffer[100];
+ int n;
+
+ /* Discard the message */
+ while (1) {
+ n = read(fd, buffer, sizeof(buffer));
+ if (n == -1 &&
+ (errno == EWOULDBLOCK ||
+ errno == EAGAIN)) break;
+ if (n == -1) {
+ log_warn("event",
+ "unable to receive interface change notification message");
+ return;
+ }
+ if (n == 0) {
+ log_warnx("event",
+ "end of file reached while getting interface change notification message");
+ return;
+ }
+ }
+
+ /* Schedule local port update. We don't run it right away because we may
+ * receive a batch of events like this. */
+ struct timeval one_sec = {1, 0};
+ log_debug("event",
+ "received notification change, schedule an update of all interfaces in one second");
+ if (cfg->g_iface_timer_event == NULL) {
+ if ((cfg->g_iface_timer_event = evtimer_new(cfg->g_base,
+ levent_iface_trigger, cfg)) == NULL) {
+ log_warnx("event",
+ "unable to create a new event to trigger interface update");
+ return;
+ }
+ }
+ if (evtimer_add(cfg->g_iface_timer_event, &one_sec) == -1) {
+ log_warnx("event",
+ "unable to schedule interface updates");
+ return;
+ }
+}
+
+void
+levent_iface_subscribe(struct lldpd *cfg, int socket)
+{
+ log_debug("event", "subscribe to interface changes from socket %d",
+ socket);
+ evutil_make_socket_nonblocking(socket);
+ cfg->g_iface_event = event_new(cfg->g_base, socket,
+ EV_READ | EV_PERSIST, levent_iface_recv, cfg);
+ if (cfg->g_iface_event == NULL) {
+ log_warnx("event",
+ "unable to allocate a new event for interface changes");
+ return;
+ }
+ if (event_add(cfg->g_iface_event, NULL) == -1) {
+ log_warnx("event",
+ "unable to schedule new interface changes event");
+ event_free(cfg->g_iface_event);
+ cfg->g_iface_event = NULL;
+ return;
+ }
+}
iflinux_macphy(hardware);
}
+ if (cfg->g_iface_event == NULL) {
+ int s;
+ log_debug("interfaces", "subscribe to netlink notifications");
+ s = netlink_subscribe_changes();
+ if (s == -1) {
+ log_warnx("interfaces", "unable to subscribe to netlink notifications");
+ goto end;
+ }
+ levent_iface_subscribe(cfg, s);
+ }
+
end:
interfaces_free_devices(interfaces);
interfaces_free_addresses(addresses);
}
}
-static void
+void
lldpd_update_localports(struct lldpd *cfg)
{
struct lldpd_hardware *hardware;
*/
log_debug("loop", "start new loop");
LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
- log_debug("loop", "update information for local ports");
- lldpd_update_localports(cfg);
+ if (cfg->g_iface_event == NULL) {
+ log_debug("loop", "update information for local ports");
+ lldpd_update_localports(cfg);
+ }
log_debug("loop", "update information for local chassis");
lldpd_update_localchassis(cfg);
log_debug("loop", "send appropriate PDU on all interfaces");
/* Unix socket handling */
int g_ctl;
struct event *g_ctl_event;
+ struct event *g_iface_event; /* Triggered when there is an interface change */
+ struct event *g_iface_timer_event; /* Triggered one second after last interface change */
char *g_lsb_release;
void lldpd_recv(struct lldpd *, struct lldpd_hardware *, int);
void lldpd_loop(struct lldpd *);
int lldpd_main(int, char **);
+void lldpd_update_localports(struct lldpd *);
+
/* event.c */
void levent_loop(struct lldpd *);
void levent_hardware_release(struct lldpd_hardware *);
void levent_ctl_notify(char *, int, struct lldpd_port *);
void levent_send_now(struct lldpd *);
+void levent_iface_subscribe(struct lldpd *, int);
+
/* lldp.c */
int lldp_send(PROTO_SEND_SIG);
/* netlink.c */
struct interfaces_device_list *netlink_get_interfaces(void);
struct interfaces_address_list *netlink_get_addresses(void);
+int netlink_subscribe_changes(void);
#endif
#endif /* _LLDPD_H */
* Open a Netlink socket and connect to it.
*
* @param protocol Which protocol to use (eg NETLINK_ROUTE).
+ * @param groups Which groups we want to subscribe to
* @return The opened socket or -1 on error.
*/
static int
-netlink_connect(int protocol)
+netlink_connect(int protocol, unsigned groups)
{
int s;
struct sockaddr_nl local = {
.nl_family = AF_NETLINK,
.nl_pid = getpid(),
- .nl_groups = 0
+ .nl_groups = groups
};
/* Open Netlink socket */
log_warn("netlink", "unable to open netlink socket");
return -1;
}
- if (bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
+ if (groups && bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
log_warn("netlink", "unable to bind netlink socket");
close(s);
return -1;
struct interfaces_device_list *ifs;
struct interfaces_device *iface1, *iface2;
- if ((s = netlink_connect(NETLINK_ROUTE)) == -1)
+ if ((s = netlink_connect(NETLINK_ROUTE, 0)) == -1)
return NULL;
if (netlink_send(s, RTM_GETLINK, AF_PACKET) == -1) {
close(s);
int s;
struct interfaces_address_list *ifaddrs;
- if ((s = netlink_connect(NETLINK_ROUTE)) == -1)
+ if ((s = netlink_connect(NETLINK_ROUTE, 0)) == -1)
return NULL;
if (netlink_send(s, RTM_GETADDR, AF_UNSPEC) == -1) {
close(s);
close(s);
return ifaddrs;
}
+
+/**
+ * Subscribe to link changes.
+ *
+ * @return The socket we should listen to for changes.
+ */
+int
+netlink_subscribe_changes()
+{
+ log_debug("netlink", "listening on interface changes");
+ return netlink_connect(NETLINK_ROUTE, RTMGRP_LINK);
+}