aboutgitcodebugslistschat
path: root/udp_vu.c
diff options
context:
space:
mode:
Diffstat (limited to 'udp_vu.c')
-rw-r--r--udp_vu.c52
1 files changed, 32 insertions, 20 deletions
diff --git a/udp_vu.c b/udp_vu.c
index 099677f..3520f89 100644
--- a/udp_vu.c
+++ b/udp_vu.c
@@ -37,17 +37,16 @@ static struct iovec iov_vu [VIRTQUEUE_MAX_SIZE];
static struct vu_virtq_element elem [VIRTQUEUE_MAX_SIZE];
/**
- * udp_vu_hdrlen() - return the size of the header in level 2 frame (UDP)
+ * udp_vu_hdrlen() - Sum size of all headers, from UDP to virtio-net
* @v6: Set for IPv6 packet
*
- * Return: return the size of the header
+ * Return: total size of virtio-net, Ethernet, IP, and UDP headers
*/
static size_t udp_vu_hdrlen(bool v6)
{
size_t hdrlen;
- hdrlen = sizeof(struct virtio_net_hdr_mrg_rxbuf) +
- sizeof(struct ethhdr) + sizeof(struct udphdr);
+ hdrlen = VNET_HLEN + sizeof(struct ethhdr) + sizeof(struct udphdr);
if (v6)
hdrlen += sizeof(struct ipv6hdr);
@@ -65,32 +64,40 @@ static size_t udp_vu_hdrlen(bool v6)
* @v6: Set for IPv6 connections
* @dlen: Size of received data (output)
*
- * Return: number of iov entries used to store the datagram
+ * Return: number of iov entries used to store the datagram, 0 if the datagram
+ * was discarded because the virtqueue is not ready, -1 on error
*/
static int udp_vu_sock_recv(const struct ctx *c, struct vu_virtq *vq, int s,
bool v6, ssize_t *dlen)
{
const struct vu_dev *vdev = c->vdev;
int iov_cnt, idx, iov_used;
+ size_t off, hdrlen, l2len;
struct msghdr msg = { 0 };
- size_t off, hdrlen;
ASSERT(!c->no_udp);
+ if (!vu_queue_enabled(vq) || !vu_queue_started(vq)) {
+ debug("Got UDP packet, but RX virtqueue not usable yet");
+
+ if (recvmsg(s, &msg, MSG_DONTWAIT) < 0)
+ debug_perror("Failed to discard datagram");
+
+ return 0;
+ }
+
/* compute L2 header length */
hdrlen = udp_vu_hdrlen(v6);
vu_init_elem(elem, iov_vu, VIRTQUEUE_MAX_SIZE);
iov_cnt = vu_collect(vdev, vq, elem, VIRTQUEUE_MAX_SIZE,
- IP_MAX_MTU + ETH_HLEN +
- sizeof(struct virtio_net_hdr_mrg_rxbuf),
- NULL);
+ IP_MAX_MTU + ETH_HLEN + VNET_HLEN, NULL);
if (iov_cnt == 0)
- return 0;
+ return -1;
/* reserve space for the headers */
- ASSERT(iov_vu[0].iov_len >= hdrlen);
+ ASSERT(iov_vu[0].iov_len >= MAX(hdrlen, ETH_ZLEN + VNET_HLEN));
iov_vu[0].iov_base = (char *)iov_vu[0].iov_base + hdrlen;
iov_vu[0].iov_len -= hdrlen;
@@ -101,7 +108,7 @@ static int udp_vu_sock_recv(const struct ctx *c, struct vu_virtq *vq, int s,
*dlen = recvmsg(s, &msg, 0);
if (*dlen < 0) {
vu_queue_rewind(vq, iov_cnt);
- return 0;
+ return -1;
}
/* restore the pointer to the headers address */
@@ -116,7 +123,11 @@ static int udp_vu_sock_recv(const struct ctx *c, struct vu_virtq *vq, int s,
iov_vu[idx].iov_len = off;
iov_used = idx + !!off;
- vu_set_vnethdr(vdev, iov_vu[0].iov_base, iov_used);
+ /* pad frame to 60 bytes: first buffer is at least ETH_ZLEN long */
+ l2len = *dlen + hdrlen - VNET_HLEN;
+ vu_pad(&iov_vu[0], l2len);
+
+ vu_set_vnethdr(iov_vu[0].iov_base, iov_used);
/* release unused buffers */
vu_queue_rewind(vq, iov_cnt - iov_used);
@@ -212,15 +223,16 @@ void udp_vu_sock_to_tap(const struct ctx *c, int s, int n, flow_sidx_t tosidx)
int iov_used;
iov_used = udp_vu_sock_recv(c, vq, s, v6, &dlen);
- if (iov_used <= 0)
+ if (iov_used < 0)
break;
- udp_vu_prepare(c, toside, dlen);
- if (*c->pcap) {
- udp_vu_csum(toside, iov_used);
- pcap_iov(iov_vu, iov_used,
- sizeof(struct virtio_net_hdr_mrg_rxbuf));
+ if (iov_used > 0) {
+ udp_vu_prepare(c, toside, dlen);
+ if (*c->pcap) {
+ udp_vu_csum(toside, iov_used);
+ pcap_iov(iov_vu, iov_used, VNET_HLEN);
+ }
+ vu_flush(vdev, vq, elem, iov_used);
}
- vu_flush(vdev, vq, elem, iov_used);
}
}