From f4cd9dec86fa8b3c2cef1902f0cb88011411f47e Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sun, 9 Jun 2019 08:13:06 +0200 Subject: [PATCH] netlink: handle blocking read from netlink socket It seems it is possible to run into a condition where the netlink socket is not available for read. Set the MSG_DONTWAIT flag and fetch an error if there is any. Fix #333 --- NEWS | 1 + src/daemon/event.c | 26 ++++++++++++++++++++++++++ src/daemon/interfaces-linux.c | 25 +------------------------ src/daemon/lldpd.h | 3 +++ src/daemon/netlink.c | 14 ++++++++------ 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/NEWS b/NEWS index 608188e8..2fb52626 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ lldpd (1.0.4) * Fix: + Better compliance for statsTLVsUnrecognizedTotal and statsAgeoutsTotal counters. + + On Linux, handle rare blocking case in Netlink code. lldpd (1.0.3) * Fix: diff --git a/src/daemon/event.c b/src/daemon/event.c index fc09e0c0..a82c19e8 100644 --- a/src/daemon/event.c +++ b/src/daemon/event.c @@ -914,3 +914,29 @@ levent_make_socket_blocking(int fd) } return 0; } + +#ifdef HOST_OS_LINUX +/* Receive and log error from a socket when there is suspicion of an error. */ +void +levent_recv_error(int fd, const char *source) +{ + do { + ssize_t n; + char buf[1024] = {}; + struct msghdr msg = { + .msg_control = buf, + .msg_controllen = sizeof(buf) + }; + if ((n = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT)) <= 0) { + return; + } + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL) + log_warnx("event", "received unknown error on %s", + source); + else + log_warnx("event", "received error (level=%d/type=%d) on %s", + cmsg->cmsg_level, cmsg->cmsg_type, source); + } while (1); +} +#endif diff --git a/src/daemon/interfaces-linux.c b/src/daemon/interfaces-linux.c index 9af41a58..a8280c85 100644 --- a/src/daemon/interfaces-linux.c +++ b/src/daemon/interfaces-linux.c @@ -71,29 +71,6 @@ iflinux_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, buffer, size); } -static void -iflinux_error_recv(struct lldpd_hardware *hardware, int fd) -{ - do { - ssize_t n; - char buf[1024] = {}; - struct msghdr msg = { - .msg_control = buf, - .msg_controllen = sizeof(buf) - }; - if ((n = recvmsg(fd, &msg, MSG_ERRQUEUE)) <= 0) { - return; - } - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg == NULL) - log_warnx("interfaces", "received unknown error on %s", - hardware->h_ifname); - else - log_warnx("interfaces", "received error (level=%d/type=%d) on %s", - cmsg->cmsg_level, cmsg->cmsg_type, hardware->h_ifname); - } while (1); -} - static int iflinux_generic_recv(struct lldpd_hardware *hardware, int fd, char *buffer, size_t size, @@ -110,7 +87,7 @@ retry: &fromlen)) == -1) { if (errno == EAGAIN && retry == 0) { /* There may be an error queued in the socket. Clear it and retry. */ - iflinux_error_recv(hardware, fd); + levent_recv_error(fd, hardware->h_ifname); retry++; goto retry; } diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h index 97306a2d..cf25dd67 100644 --- a/src/daemon/lldpd.h +++ b/src/daemon/lldpd.h @@ -127,6 +127,9 @@ void levent_schedule_pdu(struct lldpd_hardware *); void levent_schedule_cleanup(struct lldpd *); int levent_make_socket_nonblocking(int); int levent_make_socket_blocking(int); +#ifdef HOST_OS_LINUX +void levent_recv_error(int, const char*); +#endif /* lldp.c */ int lldp_send_shutdown(PROTO_SEND_SIG); diff --git a/src/daemon/netlink.c b/src/daemon/netlink.c index 4e5e34d6..d3e26dec 100644 --- a/src/daemon/netlink.c +++ b/src/daemon/netlink.c @@ -442,8 +442,7 @@ netlink_recv(struct lldpd *cfg, struct interfaces_device_list *ifs, struct interfaces_address_list *ifas) { - int end = 0, ret = 0; - int flags = MSG_PEEK | MSG_TRUNC; + int end = 0, ret = 0, flags, retry = 0; struct iovec iov; int link_update = 0; int s = cfg->g_netlink->nl_socket; @@ -471,12 +470,16 @@ netlink_recv(struct lldpd *cfg, .msg_name = &peer, .msg_namelen = sizeof(struct sockaddr_nl) }; - + flags = MSG_PEEK | MSG_TRUNC; retry: - len = recvmsg(s, &rtnl_reply, flags); + len = recvmsg(s, &rtnl_reply, flags | MSG_DONTWAIT); if (len == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { - log_debug("netlink", "should have received something, but didn't"); + if (retry++ == 0) { + levent_recv_error(s, "netlink socket"); + goto retry; + } + log_warnx("netlink", "should have received something, but didn't"); ret = 0; goto out; } @@ -650,7 +653,6 @@ retry: msg->nlmsg_type, msg->nlmsg_len); } } - flags = MSG_PEEK | MSG_TRUNC; } end: if (link_update) { -- 2.39.5