virtio-net: Fix MAC filter overflow handling

Overloading the promisc and allmulti flags for indicating filter
table overflow makes it difficult to track the actual requested
operating mode.  Split these out into separate flags.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 445976a..8c38454 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -16,7 +16,7 @@
 #include "qemu-timer.h"
 #include "virtio-net.h"
 
-#define VIRTIO_NET_VM_VERSION    8
+#define VIRTIO_NET_VM_VERSION    9
 
 #define MAC_TABLE_ENTRIES    32
 #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
@@ -37,6 +37,8 @@
     uint8_t allmulti;
     struct {
         int in_use;
+        uint8_t multi_overflow;
+        uint8_t uni_overflow;
         uint8_t *macs;
     } mac_table;
     uint32_t *vlans;
@@ -98,6 +100,8 @@
 
     /* Flush any MAC and VLAN filter table state */
     n->mac_table.in_use = 0;
+    n->mac_table.multi_overflow = 0;
+    n->mac_table.uni_overflow = 0;
     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
     memset(n->vlans, 0, MAX_VLAN >> 3);
 }
@@ -168,6 +172,8 @@
         return VIRTIO_NET_ERR;
 
     n->mac_table.in_use = 0;
+    n->mac_table.uni_overflow = 0;
+    n->mac_table.multi_overflow = 0;
     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
 
     mac_data.entries = ldl_le_p(elem->out_sg[1].iov_base);
@@ -181,8 +187,7 @@
                mac_data.entries * ETH_ALEN);
         n->mac_table.in_use += mac_data.entries;
     } else {
-        n->promisc = 1;
-        return VIRTIO_NET_OK;
+        n->mac_table.uni_overflow = 1;
     }
 
     mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base);
@@ -197,8 +202,9 @@
                    elem->out_sg[2].iov_base + sizeof(mac_data),
                    mac_data.entries * ETH_ALEN);
             n->mac_table.in_use += mac_data.entries;
-        } else
-            n->allmulti = 1;
+        } else {
+            n->mac_table.multi_overflow = 1;
+        }
     }
 
     return VIRTIO_NET_OK;
@@ -350,11 +356,13 @@
     if (ptr[0] & 1) { // multicast
         if (!memcmp(ptr, bcast, sizeof(bcast))) {
             return 1;
-        } else if (n->allmulti) {
+        } else if (n->allmulti || n->mac_table.multi_overflow) {
             return 1;
         }
     } else { // unicast
-        if (!memcmp(ptr, n->mac, ETH_ALEN)) {
+        if (n->mac_table.uni_overflow) {
+            return 1;
+        } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
             return 1;
         }
     }
@@ -532,6 +540,8 @@
     qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN);
     qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
     qemu_put_be32(f, 0); /* vnet-hdr placeholder */
+    qemu_put_byte(f, n->mac_table.multi_overflow);
+    qemu_put_byte(f, n->mac_table.uni_overflow);
 }
 
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -568,7 +578,7 @@
                             n->mac_table.in_use * ETH_ALEN);
         } else if (n->mac_table.in_use) {
             qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR);
-            n->promisc = 1;
+            n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1;
             n->mac_table.in_use = 0;
         }
     }
@@ -582,6 +592,11 @@
         exit(1);
     }
 
+    if (version_id >= 9) {
+        n->mac_table.multi_overflow = qemu_get_byte(f);
+        n->mac_table.uni_overflow = qemu_get_byte(f);
+    }
+
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);