netdev: New function netdev_drain().
authorBen Pfaff <blp@nicira.com>
Mon, 14 Jul 2008 20:03:27 +0000 (13:03 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 18 Jul 2008 20:42:38 +0000 (13:42 -0700)
include/netdev.h
include/socket-util.h
lib/netdev.c
lib/socket-util.c

index 57b7cbd46542f455daff0d643ec2cd624cb114df..108f12c7f396bbe734995c87412aaa594c603e63 100644 (file)
@@ -63,6 +63,7 @@ int netdev_open(const char *name, int ethertype, struct netdev **);
 void netdev_close(struct netdev *);
 int netdev_recv(struct netdev *, struct buffer *);
 void netdev_recv_wait(struct netdev *);
+void netdev_drain(struct netdev *);
 int netdev_send(struct netdev *, const struct buffer *);
 const uint8_t *netdev_get_etheraddr(const struct netdev *);
 const char *netdev_get_name(const struct netdev *);
index c1afaa298d4eb652e1ece776f1e63f86714b710a..bc0cd83f33d2a5e380b6e1012770663d28eb1b9f 100644 (file)
@@ -41,5 +41,6 @@ int set_nonblocking(int fd);
 int lookup_ip(const char *host_name, struct in_addr *address);
 int get_socket_error(int sock);
 int check_connection_completion(int fd);
+int drain_rcvbuf(int fd);
 
 #endif /* socket-util.h */
index a96f782cd1bb6d08ab2f44e7aced084808172e94..82325616a546a0aa4ac25a01497fcc460eaa07c5 100644 (file)
@@ -59,6 +59,7 @@
 #include "openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "socket-util.h"
 
 #define THIS_MODULE VLM_netdev
 #include "vlog.h"
@@ -222,8 +223,6 @@ netdev_open(const char *name, int ethertype, struct netdev **netdev_)
     struct sockaddr sa;
     struct ifreq ifr;
     unsigned int ifindex;
-    socklen_t rcvbuf_len;
-    size_t rcvbuf;
     uint8_t etheraddr[ETH_ADDR_LEN];
     struct in_addr in4;
     struct in6_addr in6;
@@ -257,26 +256,15 @@ netdev_open(const char *name, int ethertype, struct netdev **netdev_)
         goto error;
     }
 
-    /* Between the socket() and bind() calls above, the socket receives all
-     * packets on all system interfaces.  We do not want to receive that
-     * data, but there is no way to avoid it.  So we must now drain out the
-     * receive queue.  There is no way to know how long the receive queue is,
-     * but we know that the total number of bytes queued does not exceed the
-     * receive buffer size, so we pull packets until none are left or we've
-     * read that many bytes. */
-    rcvbuf_len = sizeof rcvbuf;
-    if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &rcvbuf_len) < 0) {
-        VLOG_ERR("getsockopt(SO_RCVBUF) on %s device failed: %s",
-                 name, strerror(errno));
-        goto error;
-    }
-    while (rcvbuf > 0) {
-        char buffer;
-        ssize_t n_bytes = recv(fd, &buffer, 1, MSG_TRUNC | MSG_DONTWAIT);
-        if (n_bytes <= 0) {
-            break;
+    if (ethertype != NETDEV_ETH_TYPE_NONE) {
+        /* Between the socket() and bind() calls above, the socket receives all
+         * packets of the requested type on all system interfaces.  We do not
+         * want to receive that data, but there is no way to avoid it.  So we
+         * must now drain out the receive queue. */
+        error = drain_rcvbuf(fd);
+        if (error) {
+            goto error;
         }
-        rcvbuf -= n_bytes;
     }
 
     /* Get ethernet device index. */
@@ -432,6 +420,13 @@ netdev_recv_wait(struct netdev *netdev)
     poll_fd_wait(netdev->fd, POLLIN);
 }
 
+/* Discards all packets waiting to be received from 'netdev'. */
+void
+netdev_drain(struct netdev *netdev)
+{
+    drain_rcvbuf(netdev->fd);
+}
+
 /* Sends 'buffer' on 'netdev'.  Returns 0 if successful, otherwise a positive
  * errno value.  Returns EAGAIN without blocking if the packet cannot be queued
  * immediately.  Returns EMSGSIZE if a partial packet was transmitted or if
index 6061d266f1c7a35e42a1e4904a9b27f70d4649ab..20e0f2073bacb704c43cf63fbd832e3d0c2f1808 100644 (file)
@@ -118,3 +118,44 @@ check_connection_completion(int fd)
         return EAGAIN;
     }
 }
+
+/* Drain all the data currently in the receive queue of a datagram socket (and
+ * possibly additional data).  There is no way to know how many packets are in
+ * the receive queue, but we do know that the total number of bytes queued does
+ * not exceed the receive buffer size, so we pull packets until none are left
+ * or we've read that many bytes. */
+int
+drain_rcvbuf(int fd)
+{
+    socklen_t rcvbuf_len;
+    size_t rcvbuf;
+
+    rcvbuf_len = sizeof rcvbuf;
+    if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &rcvbuf_len) < 0) {
+        VLOG_ERR("getsockopt(SO_RCVBUF) failed: %s", strerror(errno));
+        return errno;
+    }
+    while (rcvbuf > 0) {
+        /* In Linux, specifying MSG_TRUNC in the flags argument causes the
+         * datagram length to be returned, even if that is longer than the
+         * buffer provided.  Thus, we can use a 1-byte buffer to discard the
+         * incoming datagram and still be able to account how many bytes were
+         * removed from the receive buffer.
+         *
+         * On other Unix-like OSes, MSG_TRUNC has no effect in the flags
+         * argument. */
+#ifdef __linux__
+#define BUFFER_SIZE 1
+#else
+#define BUFFER_SIZE 2048
+#endif
+        char buffer[BUFFER_SIZE];
+        ssize_t n_bytes = recv(fd, buffer, sizeof buffer,
+                               MSG_TRUNC | MSG_DONTWAIT);
+        if (n_bytes <= 0 || n_bytes >= rcvbuf) {
+            break;
+        }
+        rcvbuf -= n_bytes;
+    }
+    return 0;
+}