From 9dc091a35fe3420eff2942014555a6d561d3378d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 14 Jul 2008 13:03:27 -0700 Subject: [PATCH] netdev: New function netdev_drain(). --- include/netdev.h | 1 + include/socket-util.h | 1 + lib/netdev.c | 37 ++++++++++++++++--------------------- lib/socket-util.c | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/include/netdev.h b/include/netdev.h index 57b7cbd4..108f12c7 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -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 *); diff --git a/include/socket-util.h b/include/socket-util.h index c1afaa29..bc0cd83f 100644 --- a/include/socket-util.h +++ b/include/socket-util.h @@ -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 */ diff --git a/lib/netdev.c b/lib/netdev.c index a96f782c..82325616 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -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 diff --git a/lib/socket-util.c b/lib/socket-util.c index 6061d266..20e0f207 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -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; +} -- 2.30.2