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 *);
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 */
#include "openflow.h"
#include "packets.h"
#include "poll-loop.h"
+#include "socket-util.h"
#define THIS_MODULE VLM_netdev
#include "vlog.h"
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;
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. */
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
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;
+}