summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/dp8393x.c22
-rw-r--r--hw/e1000.c30
-rw-r--r--hw/eepro100.c23
-rw-r--r--hw/etraxfs_eth.c14
-rw-r--r--hw/mcf_fec.c11
-rw-r--r--hw/mipsnet.c16
-rw-r--r--hw/musicpal.c11
-rw-r--r--hw/ne2000.c25
-rw-r--r--hw/pci-hotplug.c7
-rw-r--r--hw/pcnet.c17
-rw-r--r--hw/qdev.c9
-rw-r--r--hw/rtl8139.c39
-rw-r--r--hw/smc91c111.c18
-rw-r--r--hw/stellaris_enet.c20
-rw-r--r--hw/usb-net.c18
-rw-r--r--hw/virtio-net.c154
-rw-r--r--hw/virtio-net.h14
-rw-r--r--hw/xen_nic.c26
18 files changed, 299 insertions, 175 deletions
diff --git a/hw/dp8393x.c b/hw/dp8393x.c
index 5aa12119cb..cff84aa0a1 100644
--- a/hw/dp8393x.c
+++ b/hw/dp8393x.c
@@ -407,9 +407,9 @@ static void do_transmit_packets(dp8393xState *s)
         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
             /* Loopback */
             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
-            if (s->vc->fd_can_read(s)) {
+            if (s->vc->can_receive(s->vc)) {
                 s->loopback_packet = 1;
-                s->vc->fd_read(s, s->tx_buffer, tx_len);
+                s->vc->receive(s->vc, s->tx_buffer, tx_len);
             }
         } else {
             /* Transmit packet */
@@ -676,9 +676,9 @@ static CPUWriteMemoryFunc *dp8393x_write[3] = {
     dp8393x_writel,
 };
 
-static int nic_can_receive(void *opaque)
+static int nic_can_receive(VLANClientState *vc)
 {
-    dp8393xState *s = opaque;
+    dp8393xState *s = vc->opaque;
 
     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
         return 0;
@@ -725,10 +725,10 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
     return -1;
 }
 
-static void nic_receive(void *opaque, const uint8_t * buf, int size)
+static ssize_t nic_receive(VLANClientState *vc, const uint8_t * buf, size_t size)
 {
     uint16_t data[10];
-    dp8393xState *s = opaque;
+    dp8393xState *s = vc->opaque;
     int packet_type;
     uint32_t available, address;
     int width, rx_len = size;
@@ -742,7 +742,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
     packet_type = receive_filter(s, buf, size);
     if (packet_type < 0) {
         DPRINTF("packet not for netcard\n");
-        return;
+        return -1;
     }
 
     /* XXX: Check byte ordering */
@@ -755,7 +755,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
         s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
         if (data[0 * width] & 0x1) {
             /* Still EOL ; stop reception */
-            return;
+            return -1;
         } else {
             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
         }
@@ -833,6 +833,8 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
 
     /* Done */
     dp8393x_update_irq(s);
+
+    return size;
 }
 
 static void nic_reset(void *opaque)
@@ -888,8 +890,8 @@ void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift,
     s->watchdog = qemu_new_timer(vm_clock, dp8393x_watchdog, s);
     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
 
-    s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-                                 nic_receive, nic_can_receive, nic_cleanup, s);
+    s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name, nic_can_receive,
+                                 nic_receive, NULL, nic_cleanup, s);
 
     qemu_format_nic_info_str(s->vc, nd->macaddr);
     qemu_register_reset(nic_reset, 0, s);
diff --git a/hw/e1000.c b/hw/e1000.c
index 03fad4cc94..eed02a69f7 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -598,17 +598,17 @@ e1000_set_link_status(VLANClientState *vc)
 }
 
 static int
-e1000_can_receive(void *opaque)
+e1000_can_receive(VLANClientState *vc)
 {
-    E1000State *s = opaque;
+    E1000State *s = vc->opaque;
 
     return (s->mac_reg[RCTL] & E1000_RCTL_EN);
 }
 
-static void
-e1000_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t
+e1000_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    E1000State *s = opaque;
+    E1000State *s = vc->opaque;
     struct e1000_rx_desc desc;
     target_phys_addr_t base;
     unsigned int n, rdt;
@@ -617,16 +617,16 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
     uint8_t vlan_status = 0, vlan_offset = 0;
 
     if (!(s->mac_reg[RCTL] & E1000_RCTL_EN))
-        return;
+        return -1;
 
     if (size > s->rxbuf_size) {
-        DBGOUT(RX, "packet too large for buffers (%d > %d)\n", size,
-               s->rxbuf_size);
-        return;
+        DBGOUT(RX, "packet too large for buffers (%lu > %d)\n",
+               (unsigned long)size, s->rxbuf_size);
+        return -1;
     }
 
     if (!receive_filter(s, buf, size))
-        return;
+        return size;
 
     if (vlan_enabled(s) && is_vlan_packet(s, buf)) {
         vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(buf + 14)));
@@ -641,7 +641,7 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
     do {
         if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) {
             set_ics(s, 0, E1000_ICS_RXO);
-            return;
+            return -1;
         }
         base = ((uint64_t)s->mac_reg[RDBAH] << 32) + s->mac_reg[RDBAL] +
                sizeof(desc) * s->mac_reg[RDH];
@@ -665,7 +665,7 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
             DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
                    rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
             set_ics(s, 0, E1000_ICS_RXO);
-            return;
+            return -1;
         }
     } while (desc.buffer_addr == 0);
 
@@ -683,6 +683,8 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
         n |= E1000_ICS_RXDMT0;
 
     set_ics(s, 0, n);
+
+    return size;
 }
 
 static uint32_t
@@ -1119,8 +1121,8 @@ static void pci_e1000_init(PCIDevice *pci_dev)
     d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
 
     d->vc = qdev_get_vlan_client(&d->dev.qdev,
-                                 e1000_receive, e1000_can_receive,
-                                 e1000_cleanup, d);
+                                 e1000_can_receive, e1000_receive,
+                                 NULL, e1000_cleanup, d);
     d->vc->link_status_changed = e1000_set_link_status;
 
     qemu_format_nic_info_str(d->vc, macaddr);
diff --git a/hw/eepro100.c b/hw/eepro100.c
index fcb091c9f4..a6355dc754 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -1433,21 +1433,21 @@ static void pci_mmio_map(PCIDevice * pci_dev, int region_num,
     }
 }
 
-static int nic_can_receive(void *opaque)
+static int nic_can_receive(VLANClientState *vc)
 {
-    EEPRO100State *s = opaque;
+    EEPRO100State *s = vc->opaque;
     logout("%p\n", s);
     return get_ru_state(s) == ru_ready;
     //~ return !eepro100_buffer_full(s);
 }
 
-static void nic_receive(void *opaque, const uint8_t * buf, int size)
+static ssize_t nic_receive(VLANClientState *vc, const uint8_t * buf, size_t size)
 {
     /* TODO:
      * - Magic packets should set bit 30 in power management driver register.
      * - Interesting packets should set bit 29 in power management driver register.
      */
-    EEPRO100State *s = opaque;
+    EEPRO100State *s = vc->opaque;
     uint16_t rfd_status = 0xa000;
     static const uint8_t broadcast_macaddr[6] =
         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -1458,18 +1458,18 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
     if (s->configuration[8] & 0x80) {
         /* CSMA is disabled. */
         logout("%p received while CSMA is disabled\n", s);
-        return;
+        return -1;
     } else if (size < 64 && (s->configuration[7] & 1)) {
         /* Short frame and configuration byte 7/0 (discard short receive) set:
          * Short frame is discarded */
         logout("%p received short frame (%d byte)\n", s, size);
         s->statistics.rx_short_frame_errors++;
-        //~ return;
+        //~ return -1;
     } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & 8)) {
         /* Long frame and configuration byte 18/3 (long receive ok) not set:
          * Long frames are discarded. */
         logout("%p received long frame (%d byte), ignored\n", s, size);
-        return;
+        return -1;
     } else if (memcmp(buf, s->macaddr, 6) == 0) {       // !!!
         /* Frame matches individual address. */
         /* TODO: check configuration byte 15/4 (ignore U/L). */
@@ -1485,7 +1485,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
         assert(!(s->configuration[21] & BIT(3)));
         int mcast_idx = compute_mcast_idx(buf);
         if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) {
-            return;
+            return size;
         }
         rfd_status |= 0x0002;
     } else if (s->configuration[15] & 1) {
@@ -1495,7 +1495,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
     } else {
         logout("%p received frame, ignored, len=%d,%s\n", s, size,
                nic_dump(buf, size));
-        return;
+        return size;
     }
 
     if (get_ru_state(s) != ru_ready) {
@@ -1503,7 +1503,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
         logout("no ressources, state=%u\n", get_ru_state(s));
         s->statistics.rx_resource_errors++;
         //~ assert(!"no ressources");
-        return;
+        return -1;
     }
     //~ !!!
 //~ $3 = {status = 0x0, command = 0xc000, link = 0x2d220, rx_buf_addr = 0x207dc, count = 0x0, size = 0x5f8, packet = {0x0 <repeats 1518 times>}}
@@ -1540,6 +1540,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
         /* S bit is set. */
         set_ru_state(s, ru_suspended);
     }
+    return size;
 }
 
 static int nic_load(QEMUFile * f, void *opaque, int version_id)
@@ -1766,7 +1767,7 @@ static void nic_init(PCIDevice *pci_dev, uint32_t device)
     nic_reset(s);
 
     s->vc = qdev_get_vlan_client(&d->dev.qdev,
-                                 nic_receive, nic_can_receive,
+                                 nic_can_receive, nic_receive, NULL,
                                  nic_cleanup, s);
 
     qemu_format_nic_info_str(s->vc, s->macaddr);
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 68b8de38eb..c7df44ee45 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -496,21 +496,21 @@ static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa)
 	return match;
 }
 
-static int eth_can_receive(void *opaque)
+static int eth_can_receive(VLANClientState *vc)
 {
 	return 1;
 }
 
-static void eth_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t eth_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
 	unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-	struct fs_eth *eth = opaque;
+	struct fs_eth *eth = vc->opaque;
 	int use_ma0 = eth->regs[RW_REC_CTRL] & 1;
 	int use_ma1 = eth->regs[RW_REC_CTRL] & 2;
 	int r_bcast = eth->regs[RW_REC_CTRL] & 8;
 
 	if (size < 12)
-		return;
+		return -1;
 
 	D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n",
 		 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
@@ -521,10 +521,12 @@ static void eth_receive(void *opaque, const uint8_t *buf, int size)
 	    && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6))
 	    && (!r_bcast || memcmp(buf, sa_bcast, 6))
 	    && !eth_match_groupaddr(eth, buf))
-		return;
+		return size;
 
 	/* FIXME: Find another way to pass on the fake csum.  */
 	etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1);
+
+        return size;
 }
 
 static int eth_tx_push(void *opaque, unsigned char *buf, int len)
@@ -593,7 +595,7 @@ void *etraxfs_eth_init(NICInfo *nd, CPUState *env,
 	cpu_register_physical_memory (base, 0x5c, eth->ethregs);
 
 	eth->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-				       eth_receive, eth_can_receive,
+				       eth_can_receive, eth_receive, NULL,
 				       eth_cleanup, eth);
 	eth->vc->opaque = eth;
 	eth->vc->link_status_changed = eth_set_link;
diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c
index 6c0acc5789..179ec19e07 100644
--- a/hw/mcf_fec.c
+++ b/hw/mcf_fec.c
@@ -347,15 +347,15 @@ static void mcf_fec_write(void *opaque, target_phys_addr_t addr, uint32_t value)
     mcf_fec_update(s);
 }
 
-static int mcf_fec_can_receive(void *opaque)
+static int mcf_fec_can_receive(VLANClientState *vc)
 {
-    mcf_fec_state *s = (mcf_fec_state *)opaque;
+    mcf_fec_state *s = vc->opaque;
     return s->rx_enabled;
 }
 
-static void mcf_fec_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t mcf_fec_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    mcf_fec_state *s = (mcf_fec_state *)opaque;
+    mcf_fec_state *s = vc->opaque;
     mcf_fec_bd bd;
     uint32_t flags = 0;
     uint32_t addr;
@@ -426,6 +426,7 @@ static void mcf_fec_receive(void *opaque, const uint8_t *buf, int size)
     s->rx_descriptor = addr;
     mcf_fec_enable_rx(s);
     mcf_fec_update(s);
+    return size;
 }
 
 static CPUReadMemoryFunc *mcf_fec_readfn[] = {
@@ -462,7 +463,7 @@ void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq)
     cpu_register_physical_memory(base, 0x400, s->mmio_index);
 
     s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-                                 mcf_fec_receive, mcf_fec_can_receive,
+                                 mcf_fec_can_receive, mcf_fec_receive, NULL,
                                  mcf_fec_cleanup, s);
     memcpy(s->macaddr, nd->macaddr, 6);
     qemu_format_nic_info_str(s->vc, s->macaddr);
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index e842984219..803522949b 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -66,24 +66,24 @@ static int mipsnet_buffer_full(MIPSnetState *s)
     return 0;
 }
 
-static int mipsnet_can_receive(void *opaque)
+static int mipsnet_can_receive(VLANClientState *vc)
 {
-    MIPSnetState *s = opaque;
+    MIPSnetState *s = vc->opaque;
 
     if (s->busy)
         return 0;
     return !mipsnet_buffer_full(s);
 }
 
-static void mipsnet_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t mipsnet_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    MIPSnetState *s = opaque;
+    MIPSnetState *s = vc->opaque;
 
 #ifdef DEBUG_MIPSNET_RECEIVE
     printf("mipsnet: receiving len=%d\n", size);
 #endif
-    if (!mipsnet_can_receive(opaque))
-        return;
+    if (!mipsnet_can_receive(vc))
+        return -1;
 
     s->busy = 1;
 
@@ -98,6 +98,8 @@ static void mipsnet_receive(void *opaque, const uint8_t *buf, int size)
     /* Now we can signal we have received something. */
     s->intctl |= MIPSNET_INTCTL_RXDONE;
     mipsnet_update_irq(s);
+
+    return size;
 }
 
 static uint32_t mipsnet_ioport_read(void *opaque, uint32_t addr)
@@ -262,7 +264,7 @@ void mipsnet_init (int base, qemu_irq irq, NICInfo *nd)
     s->irq = irq;
     if (nd && nd->vlan) {
         s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-                                     mipsnet_receive, mipsnet_can_receive,
+                                     mipsnet_can_receive, mipsnet_receive, NULL,
                                      mipsnet_cleanup, s);
     } else {
         s->vc = NULL;
diff --git a/hw/musicpal.c b/hw/musicpal.c
index 9389af9589..8c70a2bec8 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -557,14 +557,14 @@ static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc)
     le32_to_cpus(&desc->next);
 }
 
-static int eth_can_receive(void *opaque)
+static int eth_can_receive(VLANClientState *vc)
 {
     return 1;
 }
 
-static void eth_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t eth_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    mv88w8618_eth_state *s = opaque;
+    mv88w8618_eth_state *s = vc->opaque;
     uint32_t desc_addr;
     mv88w8618_rx_desc desc;
     int i;
@@ -586,11 +586,12 @@ static void eth_receive(void *opaque, const uint8_t *buf, int size)
                 if (s->icr & s->imr)
                     qemu_irq_raise(s->irq);
                 eth_rx_desc_put(desc_addr, &desc);
-                return;
+                return size;
             }
             desc_addr = desc.next;
         } while (desc_addr != s->rx_queue[i]);
     }
+    return size;
 }
 
 static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc)
@@ -753,7 +754,7 @@ static void mv88w8618_eth_init(SysBusDevice *dev)
 
     sysbus_init_irq(dev, &s->irq);
     s->vc = qdev_get_vlan_client(&dev->qdev,
-                                 eth_receive, eth_can_receive,
+                                 eth_can_receive, eth_receive, NULL,
                                  eth_cleanup, s);
     s->mmio_index = cpu_register_io_memory(0, mv88w8618_eth_readfn,
                                            mv88w8618_eth_writefn, s);
diff --git a/hw/ne2000.c b/hw/ne2000.c
index 2af0d109b9..f5ae9d7394 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -213,9 +213,9 @@ static int ne2000_buffer_full(NE2000State *s)
     return 0;
 }
 
-static int ne2000_can_receive(void *opaque)
+static int ne2000_can_receive(VLANClientState *vc)
 {
-    NE2000State *s = opaque;
+    NE2000State *s = vc->opaque;
 
     if (s->cmd & E8390_STOP)
         return 1;
@@ -224,9 +224,10 @@ static int ne2000_can_receive(void *opaque)
 
 #define MIN_BUF_SIZE 60
 
-static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t ne2000_receive(VLANClientState *vc, const uint8_t *buf, size_t size_)
 {
-    NE2000State *s = opaque;
+    NE2000State *s = vc->opaque;
+    int size = size_;
     uint8_t *p;
     unsigned int total_len, next, avail, len, index, mcast_idx;
     uint8_t buf1[60];
@@ -238,7 +239,7 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
 #endif
 
     if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
-        return;
+        return -1;
 
     /* XXX: check this */
     if (s->rxcr & 0x10) {
@@ -247,14 +248,14 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
         if (!memcmp(buf,  broadcast_macaddr, 6)) {
             /* broadcast address */
             if (!(s->rxcr & 0x04))
-                return;
+                return size;
         } else if (buf[0] & 0x01) {
             /* multicast */
             if (!(s->rxcr & 0x08))
-                return;
+                return size;
             mcast_idx = compute_mcast_idx(buf);
             if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
-                return;
+                return size;
         } else if (s->mem[0] == buf[0] &&
                    s->mem[2] == buf[1] &&
                    s->mem[4] == buf[2] &&
@@ -263,7 +264,7 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
                    s->mem[10] == buf[5]) {
             /* match */
         } else {
-            return;
+            return size;
         }
     }
 
@@ -316,6 +317,8 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
     /* now we can signal we have received something */
     s->isr |= ENISR_RX;
     ne2000_update_irq(s);
+
+    return size_;
 }
 
 static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
@@ -757,7 +760,7 @@ void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd)
     ne2000_reset(s);
 
     s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-                                 ne2000_receive, ne2000_can_receive,
+                                 ne2000_can_receive, ne2000_receive, NULL,
                                  isa_ne2000_cleanup, s);
 
     qemu_format_nic_info_str(s->vc, s->macaddr);
@@ -821,7 +824,7 @@ static void pci_ne2000_init(PCIDevice *pci_dev)
     qdev_get_macaddr(&d->dev.qdev, s->macaddr);
     ne2000_reset(s);
     s->vc = qdev_get_vlan_client(&d->dev.qdev,
-                                 ne2000_receive, ne2000_can_receive,
+                                 ne2000_can_receive, ne2000_receive, NULL,
                                  ne2000_cleanup, s);
 
     qemu_format_nic_info_str(s->vc, s->macaddr);
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index 4d18ea2fca..abe5bae4ac 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -33,11 +33,12 @@
 #include "virtio-blk.h"
 
 #if defined(TARGET_I386) || defined(TARGET_X86_64)
-static PCIDevice *qemu_pci_hot_add_nic(PCIBus *pci_bus, const char *opts)
+static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, PCIBus *pci_bus,
+                                       const char *opts)
 {
     int ret;
 
-    ret = net_client_init("nic", opts);
+    ret = net_client_init(mon, "nic", opts);
     if (ret < 0)
         return NULL;
     return pci_nic_init(pci_bus, &nd_table[ret], -1, "rtl8139");
@@ -149,7 +150,7 @@ void pci_device_hot_add(Monitor *mon, const char *pci_addr, const char *type,
     }
 
     if (strcmp(type, "nic") == 0)
-        dev = qemu_pci_hot_add_nic(pci_bus, opts);
+        dev = qemu_pci_hot_add_nic(mon, pci_bus, opts);
     else if (strcmp(type, "storage") == 0)
         dev = qemu_pci_hot_add_storage(mon, pci_bus, opts);
     else
diff --git a/hw/pcnet.c b/hw/pcnet.c
index c44ba7edf4..b5793ff246 100644
--- a/hw/pcnet.c
+++ b/hw/pcnet.c
@@ -1062,9 +1062,9 @@ static int pcnet_tdte_poll(PCNetState *s)
     return !!(CSR_CXST(s) & 0x8000);
 }
 
-static int pcnet_can_receive(void *opaque)
+static int pcnet_can_receive(VLANClientState *vc)
 {
-    PCNetState *s = opaque;
+    PCNetState *s = vc->opaque;
     if (CSR_STOP(s) || CSR_SPND(s))
         return 0;
 
@@ -1076,16 +1076,17 @@ static int pcnet_can_receive(void *opaque)
 
 #define MIN_BUF_SIZE 60
 
-static void pcnet_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t pcnet_receive(VLANClientState *vc, const uint8_t *buf, size_t size_)
 {
-    PCNetState *s = opaque;
+    PCNetState *s = vc->opaque;
     int is_padr = 0, is_bcast = 0, is_ladr = 0;
     uint8_t buf1[60];
     int remaining;
     int crc_err = 0;
+    int size = size_;
 
     if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size)
-        return;
+        return -1;
 
 #ifdef PCNET_DEBUG
     printf("pcnet_receive size=%d\n", size);
@@ -1252,6 +1253,8 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size)
 
     pcnet_poll(s);
     pcnet_update_irq(s);
+
+    return size_;
 }
 
 static void pcnet_transmit(PCNetState *s)
@@ -1302,7 +1305,7 @@ static void pcnet_transmit(PCNetState *s)
                 if (BCR_SWSTYLE(s) == 1)
                     add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
                 s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
-                pcnet_receive(s, s->buffer, s->xmit_pos);
+                pcnet_receive(s->vc, s->buffer, s->xmit_pos);
                 s->looptest = 0;
             } else
                 if (s->vc)
@@ -1952,7 +1955,7 @@ static void pcnet_common_init(DeviceState *dev, PCNetState *s,
 
     qdev_get_macaddr(dev, s->macaddr);
     s->vc = qdev_get_vlan_client(dev,
-                                 pcnet_receive, pcnet_can_receive,
+                                 pcnet_can_receive, pcnet_receive, NULL,
                                  cleanup, s);
     pcnet_h_reset(s);
     register_savevm("pcnet", -1, 2, pcnet_save, pcnet_load, s);
diff --git a/hw/qdev.c b/hw/qdev.c
index 636dc78e59..d23298ca08 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -258,15 +258,16 @@ void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
 }
 
 VLANClientState *qdev_get_vlan_client(DeviceState *dev,
-                                      IOReadHandler *fd_read,
-                                      IOCanRWHandler *fd_can_read,
+                                      NetCanReceive *can_receive,
+                                      NetReceive *receive,
+                                      NetReceiveIOV *receive_iov,
                                       NetCleanup *cleanup,
                                       void *opaque)
 {
     NICInfo *nd = dev->nd;
     assert(nd);
-    return qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-                                fd_read, fd_can_read, cleanup, opaque);
+    return qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
+                                receive, receive_iov, cleanup, opaque);
 }
 
 
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index ea27dcfea0..de5a68fc99 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -790,9 +790,9 @@ static inline target_phys_addr_t rtl8139_addr64(uint32_t low, uint32_t high)
 #endif
 }
 
-static int rtl8139_can_receive(void *opaque)
+static int rtl8139_can_receive(VLANClientState *vc)
 {
-    RTL8139State *s = opaque;
+    RTL8139State *s = vc->opaque;
     int avail;
 
     /* Receive (drop) packets if card is disabled.  */
@@ -812,9 +812,10 @@ static int rtl8139_can_receive(void *opaque)
     }
 }
 
-static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int do_interrupt)
+static ssize_t rtl8139_do_receive(VLANClientState *vc, const uint8_t *buf, size_t size_, int do_interrupt)
 {
-    RTL8139State *s = opaque;
+    RTL8139State *s = vc->opaque;
+    int size = size_;
 
     uint32_t packet_header = 0;
 
@@ -828,7 +829,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
     if (!s->clock_enabled)
     {
         DEBUG_PRINT(("RTL8139: stopped ==========================\n"));
-        return;
+        return -1;
     }
 
     /* first check if receiver is enabled */
@@ -836,7 +837,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
     if (!rtl8139_receiver_enabled(s))
     {
         DEBUG_PRINT(("RTL8139: receiver disabled ================\n"));
-        return;
+        return -1;
     }
 
     /* XXX: check this */
@@ -854,7 +855,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
                 /* update tally counter */
                 ++s->tally_counters.RxERR;
 
-                return;
+                return size;
             }
 
             packet_header |= RxBroadcast;
@@ -873,7 +874,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
                 /* update tally counter */
                 ++s->tally_counters.RxERR;
 
-                return;
+                return size;
             }
 
             int mcast_idx = compute_mcast_idx(buf);
@@ -885,7 +886,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
                 /* update tally counter */
                 ++s->tally_counters.RxERR;
 
-                return;
+                return size;
             }
 
             packet_header |= RxMulticast;
@@ -909,7 +910,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
                 /* update tally counter */
                 ++s->tally_counters.RxERR;
 
-                return;
+                return size;
             }
 
             packet_header |= RxPhysical;
@@ -926,7 +927,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
             /* update tally counter */
             ++s->tally_counters.RxERR;
 
-            return;
+            return size;
         }
     }
 
@@ -993,7 +994,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
             ++s->tally_counters.MissPkt;
 
             rtl8139_update_irq(s);
-            return;
+            return size_;
         }
 
         uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;
@@ -1013,7 +1014,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
             ++s->tally_counters.MissPkt;
 
             rtl8139_update_irq(s);
-            return;
+            return size_;
         }
 
         target_phys_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);
@@ -1118,7 +1119,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
             s->IntrStatus |= RxOverflow;
             ++s->RxMissed;
             rtl8139_update_irq(s);
-            return;
+            return size_;
         }
 
         packet_header |= RxStatusOK;
@@ -1156,11 +1157,13 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d
     {
         rtl8139_update_irq(s);
     }
+
+    return size_;
 }
 
-static void rtl8139_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t rtl8139_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    rtl8139_do_receive(opaque, buf, size, 1);
+    return rtl8139_do_receive(vc, buf, size, 1);
 }
 
 static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
@@ -1758,7 +1761,7 @@ static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size
     if (TxLoopBack == (s->TxConfig & TxLoopBack))
     {
         DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n"));
-        rtl8139_do_receive(s, buf, size, do_interrupt);
+        rtl8139_do_receive(s->vc, buf, size, do_interrupt);
     }
     else
     {
@@ -3479,7 +3482,7 @@ static void pci_rtl8139_init(PCIDevice *dev)
     qemu_register_reset(rtl8139_reset, 0, s);
     rtl8139_reset(s);
     s->vc = qdev_get_vlan_client(&dev->qdev,
-                                 rtl8139_receive, rtl8139_can_receive,
+                                 rtl8139_can_receive, rtl8139_receive, NULL,
                                  rtl8139_cleanup, s);
 
     qemu_format_nic_info_str(s->vc, s->macaddr);
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
index 38cbd016e9..93a1fae0dc 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -591,9 +591,9 @@ static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
     return val;
 }
 
-static int smc91c111_can_receive(void *opaque)
+static int smc91c111_can_receive(VLANClientState *vc)
 {
-    smc91c111_state *s = (smc91c111_state *)opaque;
+    smc91c111_state *s = vc->opaque;
 
     if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
         return 1;
@@ -602,9 +602,9 @@ static int smc91c111_can_receive(void *opaque)
     return 1;
 }
 
-static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t smc91c111_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    smc91c111_state *s = (smc91c111_state *)opaque;
+    smc91c111_state *s = vc->opaque;
     int status;
     int packetsize;
     uint32_t crc;
@@ -612,7 +612,7 @@ static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
     uint8_t *p;
 
     if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
-        return;
+        return -1;
     /* Short packets are padded with zeros.  Receiving a packet
        < 64 bytes long is considered an error condition.  */
     if (size < 64)
@@ -625,10 +625,10 @@ static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
         packetsize += 4;
     /* TODO: Flag overrun and receive errors.  */
     if (packetsize > 2048)
-        return;
+        return -1;
     packetnum = smc91c111_allocate_packet(s);
     if (packetnum == 0x80)
-        return;
+        return -1;
     s->rx_fifo[s->rx_fifo_len++] = packetnum;
 
     p = &s->data[packetnum][0];
@@ -676,6 +676,8 @@ static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
     /* TODO: Raise early RX interrupt?  */
     s->int_level |= INT_RCV;
     smc91c111_update(s);
+
+    return size;
 }
 
 static CPUReadMemoryFunc *smc91c111_readfn[] = {
@@ -711,7 +713,7 @@ static void smc91c111_init1(SysBusDevice *dev)
     smc91c111_reset(s);
 
     s->vc = qdev_get_vlan_client(&dev->qdev,
-                                 smc91c111_receive, smc91c111_can_receive,
+                                 smc91c111_can_receive, smc91c111_receive, NULL,
                                  smc91c111_cleanup, s);
     qemu_format_nic_info_str(s->vc, s->macaddr);
     /* ??? Save/restore.  */
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
index 36fabd3260..f5b83e445c 100644
--- a/hw/stellaris_enet.c
+++ b/hw/stellaris_enet.c
@@ -78,18 +78,18 @@ static void stellaris_enet_update(stellaris_enet_state *s)
 }
 
 /* TODO: Implement MAC address filtering.  */
-static void stellaris_enet_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t stellaris_enet_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+    stellaris_enet_state *s = vc->opaque;
     int n;
     uint8_t *p;
     uint32_t crc;
 
     if ((s->rctl & SE_RCTL_RXEN) == 0)
-        return;
+        return -1;
     if (s->np >= 31) {
         DPRINTF("Packet dropped\n");
-        return;
+        return -1;
     }
 
     DPRINTF("Received packet len=%d\n", size);
@@ -116,11 +116,13 @@ static void stellaris_enet_receive(void *opaque, const uint8_t *buf, int size)
 
     s->ris |= SE_INT_RX;
     stellaris_enet_update(s);
+
+    return size;
 }
 
-static int stellaris_enet_can_receive(void *opaque)
+static int stellaris_enet_can_receive(VLANClientState *vc)
 {
-    stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+    stellaris_enet_state *s = vc->opaque;
 
     if ((s->rctl & SE_RCTL_RXEN) == 0)
         return 1;
@@ -128,9 +130,9 @@ static int stellaris_enet_can_receive(void *opaque)
     return (s->np < 31);
 }
 
-static uint32_t stellaris_enet_read(void *opaque, target_phys_addr_t offset)
+static uint32_t stellaris_enet_read(VLANClientState *vc, target_phys_addr_t offset)
 {
-    stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+    stellaris_enet_state *s = vc->opaque;
     uint32_t val;
 
     switch (offset) {
@@ -405,8 +407,8 @@ static void stellaris_enet_init(SysBusDevice *dev)
     qdev_get_macaddr(&dev->qdev, s->macaddr);
 
     s->vc = qdev_get_vlan_client(&dev->qdev,
-                                 stellaris_enet_receive,
                                  stellaris_enet_can_receive,
+                                 stellaris_enet_receive, NULL,
                                  stellaris_enet_cleanup, s);
     qemu_format_nic_info_str(s->vc, s->macaddr);
 
diff --git a/hw/usb-net.c b/hw/usb-net.c
index 9e6442506f..0e80ca6923 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1369,17 +1369,17 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
     return ret;
 }
 
-static void usbnet_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t usbnet_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    USBNetState *s = opaque;
+    USBNetState *s = vc->opaque;
     struct rndis_packet_msg_type *msg;
 
     if (s->rndis) {
         msg = (struct rndis_packet_msg_type *) s->in_buf;
         if (!s->rndis_state == RNDIS_DATA_INITIALIZED)
-            return;
+            return -1;
         if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf))
-            return;
+            return -1;
 
         memset(msg, 0, sizeof(struct rndis_packet_msg_type));
         msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG);
@@ -1398,16 +1398,17 @@ static void usbnet_receive(void *opaque, const uint8_t *buf, int size)
         s->in_len = size + sizeof(struct rndis_packet_msg_type);
     } else {
         if (size > sizeof(s->in_buf))
-            return;
+            return -1;
         memcpy(s->in_buf, buf, size);
         s->in_len = size;
     }
     s->in_ptr = 0;
+    return size;
 }
 
-static int usbnet_can_receive(void *opaque)
+static int usbnet_can_receive(VLANClientState *vc)
 {
-    USBNetState *s = opaque;
+    USBNetState *s = vc->opaque;
 
     if (s->rndis && !s->rndis_state == RNDIS_DATA_INITIALIZED)
         return 1;
@@ -1458,8 +1459,9 @@ USBDevice *usb_net_init(NICInfo *nd)
     pstrcpy(s->dev.devname, sizeof(s->dev.devname),
                     "QEMU USB Network Interface");
     s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-                                 usbnet_receive,
                                  usbnet_can_receive,
+                                 usbnet_receive,
+                                 NULL,
                                  usbnet_cleanup, s);
 
     qemu_format_nic_info_str(s->vc, s->mac);
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 60aa6dab1b..d584287a51 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -16,9 +16,9 @@
 #include "qemu-timer.h"
 #include "virtio-net.h"
 
-#define VIRTIO_NET_VM_VERSION    6
+#define VIRTIO_NET_VM_VERSION    10
 
-#define MAC_TABLE_ENTRIES    32
+#define MAC_TABLE_ENTRIES    64
 #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
 
 typedef struct VirtIONet
@@ -33,10 +33,17 @@ typedef struct VirtIONet
     QEMUTimer *tx_timer;
     int tx_timer_active;
     int mergeable_rx_bufs;
-    int promisc;
-    int allmulti;
+    uint8_t promisc;
+    uint8_t allmulti;
+    uint8_t alluni;
+    uint8_t nomulti;
+    uint8_t nouni;
+    uint8_t nobcast;
     struct {
         int in_use;
+        int first_multi;
+        uint8_t multi_overflow;
+        uint8_t uni_overflow;
         uint8_t *macs;
     } mac_table;
     uint32_t *vlans;
@@ -95,9 +102,16 @@ static void virtio_net_reset(VirtIODevice *vdev)
     /* Reset back to compatibility mode */
     n->promisc = 1;
     n->allmulti = 0;
+    n->alluni = 0;
+    n->nomulti = 0;
+    n->nouni = 0;
+    n->nobcast = 0;
 
     /* 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);
     memset(n->vlans, 0, MAX_VLAN >> 3);
 }
@@ -108,7 +122,8 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev)
                         (1 << VIRTIO_NET_F_STATUS) |
                         (1 << VIRTIO_NET_F_CTRL_VQ) |
                         (1 << VIRTIO_NET_F_CTRL_RX) |
-                        (1 << VIRTIO_NET_F_CTRL_VLAN);
+                        (1 << VIRTIO_NET_F_CTRL_VLAN) |
+                        (1 << VIRTIO_NET_F_CTRL_RX_EXTRA);
 
     return features;
 }
@@ -151,6 +166,14 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
         n->promisc = on;
     else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLMULTI)
         n->allmulti = on;
+    else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLUNI)
+        n->alluni = on;
+    else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOMULTI)
+        n->nomulti = on;
+    else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOUNI)
+        n->nouni = on;
+    else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOBCAST)
+        n->nobcast = on;
     else
         return VIRTIO_NET_ERR;
 
@@ -168,6 +191,9 @@ 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);
 
     mac_data.entries = ldl_le_p(elem->out_sg[1].iov_base);
@@ -181,10 +207,11 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
                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;
     }
 
+    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) +
@@ -197,8 +224,9 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
                    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;
@@ -269,6 +297,9 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 
 static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
 {
+    VirtIONet *n = to_virtio_net(vdev);
+
+    qemu_flush_queued_packets(n->vc);
 }
 
 static int do_virtio_net_can_receive(VirtIONet *n, int bufsize)
@@ -288,9 +319,9 @@ static int do_virtio_net_can_receive(VirtIONet *n, int bufsize)
     return 1;
 }
 
-static int virtio_net_can_receive(void *opaque)
+static int virtio_net_can_receive(VLANClientState *vc)
 {
-    VirtIONet *n = opaque;
+    VirtIONet *n = vc->opaque;
 
     return do_virtio_net_can_receive(n, VIRTIO_NET_MAX_BUFSIZE);
 }
@@ -344,34 +375,50 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
             return 0;
     }
 
-    if ((ptr[0] & 1) && n->allmulti)
-        return 1;
-
-    if (!memcmp(ptr, bcast, sizeof(bcast)))
-        return 1;
-
-    if (!memcmp(ptr, n->mac, ETH_ALEN))
-        return 1;
+    if (ptr[0] & 1) { // multicast
+        if (!memcmp(ptr, bcast, sizeof(bcast))) {
+            return !n->nobcast;
+        } else if (n->nomulti) {
+            return 0;
+        } else if (n->allmulti || n->mac_table.multi_overflow) {
+            return 1;
+        }
 
-    for (i = 0; i < n->mac_table.in_use; i++) {
-        if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN))
+        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->nouni) {
+            return 0;
+        } else if (n->alluni || n->mac_table.uni_overflow) {
+            return 1;
+        } else if (!memcmp(ptr, n->mac, 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;
 }
 
-static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
+static ssize_t virtio_net_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    VirtIONet *n = opaque;
+    VirtIONet *n = vc->opaque;
     struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
     size_t hdr_len, offset, i;
 
     if (!do_virtio_net_can_receive(n, size))
-        return;
+        return 0;
 
     if (!receive_filter(n, buf, size))
-        return;
+        return size;
 
     /* hdr_len refers to the header we supply to the guest */
     hdr_len = n->mergeable_rx_bufs ?
@@ -389,7 +436,7 @@ static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
         if ((i != 0 && !n->mergeable_rx_bufs) ||
             virtqueue_pop(n->rx_vq, &elem) == 0) {
             if (i == 0)
-                return;
+                return -1;
             fprintf(stderr, "virtio-net truncating packet\n");
             exit(1);
         }
@@ -431,6 +478,8 @@ static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
 
     virtqueue_flush(n->rx_vq, i);
     virtio_notify(&n->vdev, n->rx_vq);
+
+    return size;
 }
 
 /* TX */
@@ -518,16 +567,24 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, n->tx_timer_active);
     qemu_put_be32(f, n->mergeable_rx_bufs);
     qemu_put_be16(f, n->status);
-    qemu_put_be32(f, n->promisc);
-    qemu_put_be32(f, n->allmulti);
+    qemu_put_byte(f, n->promisc);
+    qemu_put_byte(f, n->allmulti);
     qemu_put_be32(f, n->mac_table.in_use);
     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);
+    qemu_put_byte(f, n->alluni);
+    qemu_put_byte(f, n->nomulti);
+    qemu_put_byte(f, n->nouni);
+    qemu_put_byte(f, n->nobcast);
 }
 
 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;
@@ -542,8 +599,13 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
         n->status = qemu_get_be16(f);
 
     if (version_id >= 4) {
-        n->promisc = qemu_get_be32(f);
-        n->allmulti = qemu_get_be32(f);
+        if (version_id < 8) {
+            n->promisc = qemu_get_be32(f);
+            n->allmulti = qemu_get_be32(f);
+        } else {
+            n->promisc = qemu_get_byte(f);
+            n->allmulti = qemu_get_byte(f);
+        }
     }
 
     if (version_id >= 5) {
@@ -554,7 +616,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
                             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;
         }
     }
@@ -562,6 +624,32 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
     if (version_id >= 6)
         qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
 
+    if (version_id >= 7 && qemu_get_be32(f)) {
+        fprintf(stderr,
+                "virtio-net: saved image requires vnet header support\n");
+        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 (version_id >= 10) {
+        n->alluni = qemu_get_byte(f);
+        n->nomulti = qemu_get_byte(f);
+        n->nouni = qemu_get_byte(f);
+        n->nobcast = 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);
@@ -602,12 +690,12 @@ VirtIODevice *virtio_net_init(DeviceState *dev)
     n->vdev.reset = virtio_net_reset;
     n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
     n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
-    n->ctrl_vq = virtio_add_queue(&n->vdev, 16, virtio_net_handle_ctrl);
+    n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl);
     qdev_get_macaddr(dev, n->mac);
     n->status = VIRTIO_NET_S_LINK_UP;
     n->vc = qdev_get_vlan_client(dev,
-                                 virtio_net_receive,
                                  virtio_net_can_receive,
+                                 virtio_net_receive, NULL,
                                  virtio_net_cleanup, n);
     n->vc->link_status_changed = virtio_net_set_link_status;
 
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 390fe10224..2085181673 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -43,6 +43,7 @@
 #define VIRTIO_NET_F_CTRL_VQ    17      /* Control channel available */
 #define VIRTIO_NET_F_CTRL_RX    18      /* Control channel RX mode support */
 #define VIRTIO_NET_F_CTRL_VLAN  19      /* Control channel VLAN filtering */
+#define VIRTIO_NET_F_CTRL_RX_EXTRA 20   /* Extra RX mode control support */
 
 #define VIRTIO_NET_S_LINK_UP    1       /* Link is up */
 
@@ -103,14 +104,19 @@ typedef uint8_t virtio_net_ctrl_ack;
 #define VIRTIO_NET_ERR    1
 
 /*
- * Control the RX mode, ie. promisucous and allmulti.  PROMISC and
- * ALLMULTI commands require an "out" sg entry containing a 1 byte
- * state value, zero = disable, non-zero = enable.  These commands
- * are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Control the RX mode, ie. promisucous, allmulti, etc...
+ * All commands require an "out" sg entry containing a 1 byte
+ * state value, zero = disable, non-zero = enable.  Commands
+ * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
  */
 #define VIRTIO_NET_CTRL_RX_MODE    0
  #define VIRTIO_NET_CTRL_RX_MODE_PROMISC      0
  #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI     1
+ #define VIRTIO_NET_CTRL_RX_MODE_ALLUNI       2
+ #define VIRTIO_NET_CTRL_RX_MODE_NOMULTI      3
+ #define VIRTIO_NET_CTRL_RX_MODE_NOUNI        4
+ #define VIRTIO_NET_CTRL_RX_MODE_NOBCAST      5
 
 /*
  * Control the MAC filter table.
diff --git a/hw/xen_nic.c b/hw/xen_nic.c
index 4206132aea..9a3c870c2d 100644
--- a/hw/xen_nic.c
+++ b/hw/xen_nic.c
@@ -223,9 +223,9 @@ static void net_rx_response(struct XenNetDev *netdev,
 
 #define NET_IP_ALIGN 2
 
-static int net_rx_ok(void *opaque)
+static int net_rx_ok(VLANClientState *vc)
 {
-    struct XenNetDev *netdev = opaque;
+    struct XenNetDev *netdev = vc->opaque;
     RING_IDX rc, rp;
 
     if (netdev->xendev.be_state != XenbusStateConnected)
@@ -243,15 +243,15 @@ static int net_rx_ok(void *opaque)
     return 1;
 }
 
-static void net_rx_packet(void *opaque, const uint8_t *buf, int size)
+static ssize_t net_rx_packet(VLANClientState *vc, const uint8_t *buf, size_t size)
 {
-    struct XenNetDev *netdev = opaque;
+    struct XenNetDev *netdev = vc->opaque;
     netif_rx_request_t rxreq;
     RING_IDX rc, rp;
     void *page;
 
     if (netdev->xendev.be_state != XenbusStateConnected)
-	return;
+	return -1;
 
     rc = netdev->rx_ring.req_cons;
     rp = netdev->rx_ring.sring->req_prod;
@@ -259,12 +259,12 @@ static void net_rx_packet(void *opaque, const uint8_t *buf, int size)
 
     if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
 	xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n");
-	return;
+	return -1;
     }
     if (size > XC_PAGE_SIZE - NET_IP_ALIGN) {
-	xen_be_printf(&netdev->xendev, 0, "packet too big (%d > %ld)",
-		      size, XC_PAGE_SIZE - NET_IP_ALIGN);
-	return;
+	xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)",
+		      (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN);
+	return -1;
     }
 
     memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
@@ -277,11 +277,13 @@ static void net_rx_packet(void *opaque, const uint8_t *buf, int size)
 	xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n",
                       rxreq.gref);
 	net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
-	return;
+	return -1;
     }
     memcpy(page + NET_IP_ALIGN, buf, size);
     xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
     net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
+
+    return size;
 }
 
 /* ------------------------------------------------------------- */
@@ -301,8 +303,8 @@ static int net_init(struct XenDevice *xendev)
 
     vlan = qemu_find_vlan(netdev->xendev.dev);
     netdev->vs = qemu_new_vlan_client(vlan, "xen", NULL,
-                                      net_rx_packet, net_rx_ok, NULL,
-                                      netdev);
+                                      net_rx_ok, net_rx_packet, NULL,
+                                      NULL, netdev);
     snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str),
              "nic: xenbus vif macaddr=%s", netdev->mac);