struct iovec iov[2];
struct msghdr msg;
ssize_t retval;
+ int error;
ovs_assert(buf->allocated >= sizeof *nlmsghdr);
ofpbuf_clear(buf);
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. */
return E2BIG;
}
- nlmsghdr = ofpbuf_data(buf);
if (retval < sizeof *nlmsghdr
|| nlmsghdr->nlmsg_len < sizeof *nlmsghdr
|| nlmsghdr->nlmsg_len > retval) {
- VLOG_ERR_RL(&rl, "received invalid nlmsg (%"PRIuSIZE"d bytes < %"PRIuSIZE")",
+ VLOG_ERR_RL(&rl, "received invalid nlmsg (%"PRIuSIZE" bytes < %"PRIuSIZE")",
retval, sizeof *nlmsghdr);
return EPROTO;
}
return !error;
}
+/* Attempts to look ahead in 'buffer' to obtain the next reply that will be
+ * returned by nl_dump_next(). Returns true if successful, in which case
+ * 'reply' will be initialize to the message that will be obtained by the next
+ * call to nl_dump_next(), or false on failure. Failure doesn't necessarily
+ * mean that the nl_dump_next() will fail, only that it needs to obtain a new
+ * block of dump results from the kernel. */
+bool
+nl_dump_peek(struct ofpbuf *reply, struct ofpbuf *buffer)
+{
+ struct ofpbuf tmp = *buffer;
+ return nl_msg_next(&tmp, reply);
+}
+
/* Completes Netlink dump operation 'dump', which must have been initialized
* with nl_dump_start(). Returns 0 if the dump operation was error-free,
* otherwise a positive errno value describing the problem. */