netlink-socket: Work around upstream kernel Netlink bug.
[cascardo/ovs.git] / lib / netlink-socket.c
index 215ddc4..93757cf 100644 (file)
@@ -310,6 +310,7 @@ nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
     struct iovec iov[2];
     struct msghdr msg;
     ssize_t retval;
+    int error;
 
     ovs_assert(buf->allocated >= sizeof *nlmsghdr);
     ofpbuf_clear(buf);
@@ -323,12 +324,23 @@ nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
     msg.msg_iov = iov;
     msg.msg_iovlen = 2;
 
+    /* Receive a Netlink message from the kernel.
+     *
+     * This works around a kernel bug in which the kernel returns an error code
+     * as if it were the number of bytes read.  It doesn't actually modify
+     * anything in the receive buffer in that case, so we can initialize the
+     * Netlink header with an impossible message length and then, upon success,
+     * check whether it changed. */
+    nlmsghdr = ofpbuf_base(buf);
     do {
+        nlmsghdr->nlmsg_len = UINT32_MAX;
         retval = recvmsg(sock->fd, &msg, wait ? 0 : MSG_DONTWAIT);
-    } while (retval < 0 && errno == EINTR);
-
-    if (retval < 0) {
-        int error = errno;
+        error = (retval < 0 ? errno
+                 : retval == 0 ? ECONNRESET /* not possible? */
+                 : nlmsghdr->nlmsg_len != UINT32_MAX ? 0
+                 : -retval);
+    } while (error == EINTR);
+    if (error) {
         if (error == ENOBUFS) {
             /* Socket receive buffer overflow dropped one or more messages that
              * the kernel tried to send to us. */
@@ -343,7 +355,6 @@ nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
         return E2BIG;
     }
 
-    nlmsghdr = ofpbuf_data(buf);
     if (retval < sizeof *nlmsghdr
         || nlmsghdr->nlmsg_len < sizeof *nlmsghdr
         || nlmsghdr->nlmsg_len > retval) {