@@ -502,6 +502,8 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
return 0;
}
+int buffer_fits_in_virtqueue_top(VirtQueue *vq, int size);
+
static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size_t size, int raw)
{
VirtIONet *n = vc->opaque;
@@ -518,6 +520,10 @@ static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size
hdr_len = n->mergeable_rx_bufs ?
sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
+ /* drop packet instead of truncating it */
+ if (!n->mergeable_rx_bufs && !buffer_fits_in_virtqueue_top(n->rx_vq, hdr_len + size))
+ return;
+
offset = i = 0;
while (offset < size) {
@@ -531,7 +537,7 @@ static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size
virtqueue_pop(n->rx_vq, &elem) == 0) {
if (i == 0)
return -1;
- fprintf(stderr, "virtio-net truncating packet\n");
+ fprintf(stderr, "virtio-net truncating packet: mergable_rx_bufs: %d\n", n->mergeable_rx_bufs);
exit(1);
}
@@ -356,6 +356,28 @@ int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
return 0;
}
+/* buffer_fits_in_virtqueue_top: returns true if a 'size' byte buffer could fit in the
+ * input descriptors that virtqueue_pop() would have returned
+ */
+int buffer_fits_in_virtqueue_top(VirtQueue *vq, int size);
+
+int buffer_fits_in_virtqueue_top(VirtQueue *vq, int size)
+{
+ unsigned int i;
+ int input_iov_len_sum;
+
+ if (!virtqueue_num_heads(vq, vq->last_avail_idx))
+ return 0;
+
+ input_iov_len_sum = 0;
+ i = virtqueue_get_head(vq, vq->last_avail_idx);
+ do {
+ if (vring_desc_flags(vq, i) & VRING_DESC_F_WRITE)
+ input_iov_len_sum += vring_desc_len(vq, i);
+ } while ((i = virtqueue_next_desc(vq, i)) != vq->vring.num);
+ return input_iov_len_sum >= size;
+}
+
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
{
unsigned int i, head, max;