netdev: New function netdev_drain().

This commit is contained in:
Ben Pfaff committed 2008-07-18 13:42:38 -07:00
1 parent e55d52c988
commit 9dc091a35f
4 files changed
+59 -21

No files matched your search

+1
View 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 *);
+1
View 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 */
+16 -21
View 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
+41
View 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;
}