netdev: New function netdev_drain().
This commit is contained in:
1 parent
e55d52c988
commit
9dc091a35f
4 files changed
+59
-21
No files matched your search
@@ -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 *);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user