]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
netlink: handle blocking read from netlink socket
authorVincent Bernat <vincent@bernat.ch>
Sun, 9 Jun 2019 06:13:06 +0000 (08:13 +0200)
committerVincent Bernat <vincent@bernat.ch>
Wed, 12 Jun 2019 19:50:58 +0000 (21:50 +0200)
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
src/daemon/event.c
src/daemon/interfaces-linux.c
src/daemon/lldpd.h
src/daemon/netlink.c

diff --git a/NEWS b/NEWS
index 608188e885ec45baf9d973b288dcc338d34a9070..2fb526265b6149ce76d5a73d2bc0849e3685b624 100644 (file)
--- 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:
index fc09e0c0fa7c193767557308fd6f57d31624d3d6..a82c19e8e6776bf0e19d930b21e87693391bf049 100644 (file)
@@ -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
index 9af41a58d363759c470a0216ed2b295d16734fe2..a8280c851ea9200e58cda2d3e93655192ca76c9c 100644 (file)
@@ -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;
                }
index 97306a2d47733e241d6ab512e0427b27df473f92..cf25dd678d379e6b8cb4e033e11aa8ca5cd68660 100644 (file)
@@ -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);
index 4e5e34d63695f6cb8e8986db21976b3820a05d0c..d3e26dec67db524f67f37fb8b5853a69d5f7cb22 100644 (file)
@@ -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) {