summary refs log tree commit diff stats
path: root/hw/virtio-net.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/virtio-net.c')
-rw-r--r--hw/virtio-net.c29
1 files changed, 25 insertions, 4 deletions
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 8c38454ae1..7863d5ddbf 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -37,6 +37,7 @@ typedef struct VirtIONet
     uint8_t allmulti;
     struct {
         int in_use;
+        int first_multi;
         uint8_t multi_overflow;
         uint8_t uni_overflow;
         uint8_t *macs;
@@ -100,6 +101,7 @@ static void virtio_net_reset(VirtIODevice *vdev)
 
     /* Flush any MAC and VLAN filter table state */
     n->mac_table.in_use = 0;
+    n->mac_table.first_multi = 0;
     n->mac_table.multi_overflow = 0;
     n->mac_table.uni_overflow = 0;
     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
@@ -172,6 +174,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
         return VIRTIO_NET_ERR;
 
     n->mac_table.in_use = 0;
+    n->mac_table.first_multi = 0;
     n->mac_table.uni_overflow = 0;
     n->mac_table.multi_overflow = 0;
     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
@@ -190,6 +193,8 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
         n->mac_table.uni_overflow = 1;
     }
 
+    n->mac_table.first_multi = n->mac_table.in_use;
+
     mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base);
 
     if (sizeof(mac_data.entries) +
@@ -359,17 +364,24 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
         } else if (n->allmulti || n->mac_table.multi_overflow) {
             return 1;
         }
+
+        for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
+            if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
+                return 1;
+            }
+        }
     } else { // unicast
         if (n->mac_table.uni_overflow) {
             return 1;
         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
             return 1;
         }
-    }
 
-    for (i = 0; i < n->mac_table.in_use; i++) {
-        if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN))
-            return 1;
+        for (i = 0; i < n->mac_table.first_multi; i++) {
+            if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
+                return 1;
+            }
+        }
     }
 
     return 0;
@@ -547,6 +559,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
 {
     VirtIONet *n = opaque;
+    int i;
 
     if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
         return -EINVAL;
@@ -597,6 +610,14 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
         n->mac_table.uni_overflow = qemu_get_byte(f);
     }
 
+    /* Find the first multicast entry in the saved MAC filter */
+    for (i = 0; i < n->mac_table.in_use; i++) {
+        if (n->mac_table.macs[i * ETH_ALEN] & 1) {
+            break;
+        }
+    }
+    n->mac_table.first_multi = i;
+
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);