diff mbox

[4/4] net: convert NetClientState to QOM

Message ID 1341226613-15577-5-git-send-email-stefanha@linux.vnet.ibm.com
State New
Headers show

Commit Message

Stefan Hajnoczi July 2, 2012, 10:56 a.m. UTC
This patch makes all net clients QOM objects.  This is the first step to
converting the net subsystem to the QEMU Object Model.  At this point
net clients are not yet visible in the QOM tree and there are many
simplifications that can still be made to drop ad-hoc code in favor of
QOM.

The major changes are:

1. NetClientInfo becomes NetClientClass.  The "virtual functions" (aka
   callbacks) for net clients are defined here and overridden by
   NetClient child classes.

2. NetClientState becomes a QOM object.  I have not renamed it to
   NetClient yet because that would add too much noise, see later
   patches.

3. NICState becomes a QOM object with NetClient as its base class.  In
   other words, a NIC is-a net client.  NICState will also be renamed in
   later patches but doing so now would be too noisy.

All net backends in net/ and NICs in hw/ must be converted to NetClient
subclasses.  This makes up the bulk of this patch and was done
automatically using a script.  Unfortunately there is no clean way to
split these changes into separate patches.

The net_client_type enum has been mostly dropped.  For now users have
been replaced with object_dynamic_cast() to test the type of an object.
The cleaner solution in later patches is to design polymorphic
interfaces instead of sprinkling code that tests the specific type of an
object throughout the net subsystem.

Note that extrafx_eth.c uses tabs and checkpatch.pl will complain.  The
rest of the hunks are clean.
---
 hw/cadence_gem.c        |   27 +++++++----
 hw/dp8393x.c            |   41 ++++++++++++----
 hw/e1000.c              |   32 +++++++++----
 hw/eepro100.c           |   27 ++++++++---
 hw/etraxfs_eth.c        |   27 +++++++----
 hw/lan9118.c            |   27 +++++++----
 hw/lance.c              |   18 +------
 hw/mcf_fec.c            |   32 ++++++++++---
 hw/milkymist-minimac2.c |   26 +++++++---
 hw/mipsnet.c            |   25 +++++++---
 hw/musicpal.c           |   26 +++++++---
 hw/ne2000-isa.c         |   17 +------
 hw/ne2000.c             |   23 ++++++---
 hw/ne2000.h             |    2 +
 hw/opencores_eth.c      |   28 +++++++----
 hw/pcnet-pci.c          |   18 +------
 hw/pcnet.c              |   36 ++++++++++++--
 hw/pcnet.h              |    3 +-
 hw/rtl8139.c            |   26 +++++++---
 hw/smc91c111.c          |   26 +++++++---
 hw/spapr_llan.c         |   24 +++++++---
 hw/stellaris_enet.c     |   26 +++++++---
 hw/usb/dev-network.c    |   25 +++++++---
 hw/virtio-net.c         |   34 ++++++++++----
 hw/xen_nic.c            |   29 +++++++++---
 hw/xgmac.c              |   26 +++++++---
 hw/xilinx_axienet.c     |   26 +++++++---
 hw/xilinx_ethlite.c     |   26 +++++++---
 net.c                   |  120 ++++++++++++++++++++++++++++++++---------------
 net.h                   |   33 +++++++++----
 net/dump.c              |   30 +++++++++---
 net/hub.c               |   51 ++++++++++++--------
 net/slirp.c             |   29 +++++++++---
 net/socket.c            |   80 ++++++++++++++++++-------------
 net/tap-win32.c         |   29 ++++++++++--
 net/tap.c               |   80 +++++++++++++++++--------------
 net/vde.c               |   28 +++++++++--
 37 files changed, 810 insertions(+), 373 deletions(-)
diff mbox

Patch

diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
index 2245013..34b8bb2 100644
--- a/hw/cadence_gem.c
+++ b/hw/cadence_gem.c
@@ -1160,13 +1160,23 @@  static void gem_set_link(NetClientState *nc)
     phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque);
 }
 
-static NetClientInfo net_gem_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = gem_can_receive,
-    .receive = gem_receive,
-    .cleanup = gem_cleanup,
-    .link_status_changed = gem_set_link,
+#define TYPE_GEM_NET_CLIENT "gem-net-client"
+
+static void gem_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = gem_can_receive;
+    ncc->receive = gem_receive;
+    ncc->cleanup = gem_cleanup;
+    ncc->link_status_changed = gem_set_link;
+}
+
+static TypeInfo gem_net_client_info = {
+    .name = TYPE_GEM_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = gem_net_client_class_init,
 };
 
 static int gem_init(SysBusDevice *dev)
@@ -1182,7 +1192,7 @@  static int gem_init(SysBusDevice *dev)
     sysbus_init_irq(dev, &s->irq);
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
 
-    s->nic = qemu_new_nic(&net_gem_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_GEM_NET_CLIENT, &s->conf,
             object_get_typename(OBJECT(dev)), dev->qdev.id, s);
 
     return 0;
@@ -1228,6 +1238,7 @@  static TypeInfo gem_info = {
 static void gem_register_types(void)
 {
     type_register_static(&gem_info);
+    type_register_static(&gem_net_client_info);
 }
 
 type_init(gem_register_types)
diff --git a/hw/dp8393x.c b/hw/dp8393x.c
index 29ae40a..503af67 100644
--- a/hw/dp8393x.c
+++ b/hw/dp8393x.c
@@ -172,6 +172,10 @@  typedef struct dp8393xState {
     void* mem_opaque;
 } dp8393xState;
 
+static int nic_can_receive(NetClientState *nc);
+static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf,
+                           size_t size);
+
 static void dp8393x_update_irq(dp8393xState *s)
 {
     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
@@ -408,9 +412,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->nic->nc.info->can_receive(&s->nic->nc)) {
+            if (nic_can_receive(&s->nic->nc)) {
                 s->loopback_packet = 1;
-                s->nic->nc.info->receive(&s->nic->nc, s->tx_buffer, tx_len);
+                nic_receive(&s->nic->nc, s->tx_buffer, tx_len);
             }
         } else {
             /* Transmit packet */
@@ -871,12 +875,23 @@  static void nic_cleanup(NetClientState *nc)
     g_free(s);
 }
 
-static NetClientInfo net_dp83932_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = nic_can_receive,
-    .receive = nic_receive,
-    .cleanup = nic_cleanup,
+#define TYPE_DP83932_NET_CLIENT "dp83932-net-client"
+
+static void dp83932_net_client_class_init(ObjectClass *klass,
+                                          void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = nic_can_receive;
+    ncc->receive = nic_receive;
+    ncc->cleanup = nic_cleanup;
+}
+
+static TypeInfo dp83932_net_client_info = {
+    .name = TYPE_DP83932_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = dp83932_net_client_class_init,
 };
 
 void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift,
@@ -901,7 +916,8 @@  void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift,
     s->conf.macaddr = nd->macaddr;
     s->conf.peer = nd->netdev;
 
-    s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
+    s->nic = qemu_new_nic(TYPE_DP83932_NET_CLIENT, &s->conf, nd->model,
+                          nd->name, s);
 
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     qemu_register_reset(nic_reset, s);
@@ -911,3 +927,10 @@  void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift,
                           "dp8393x", 0x40 << it_shift);
     memory_region_add_subregion(address_space, base, &s->mmio);
 }
+
+static void dp83932_register_types(void)
+{
+    type_register_static(&dp83932_net_client_info);
+}
+
+type_init(dp83932_register_types)
diff --git a/hw/e1000.c b/hw/e1000.c
index cf1e124..f729305 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -144,6 +144,9 @@  enum {
     defreg(VET),
 };
 
+static ssize_t e1000_receive(NetClientState *nc, const uint8_t *buf,
+                             size_t size);
+
 static void
 e1000_link_down(E1000State *s)
 {
@@ -446,7 +449,7 @@  static void
 e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
 {
     if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) {
-        s->nic->nc.info->receive(&s->nic->nc, buf, size);
+        e1000_receive(&s->nic->nc, buf, size);
     } else {
         qemu_send_packet(&s->nic->nc, buf, size);
     }
@@ -1205,13 +1208,23 @@  pci_e1000_uninit(PCIDevice *dev)
     return 0;
 }
 
-static NetClientInfo net_e1000_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = e1000_can_receive,
-    .receive = e1000_receive,
-    .cleanup = e1000_cleanup,
-    .link_status_changed = e1000_set_link_status,
+#define TYPE_E1000_NET_CLIENT "e1000-net-client"
+
+static void e1000_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = e1000_can_receive;
+    ncc->receive = e1000_receive;
+    ncc->cleanup = e1000_cleanup;
+    ncc->link_status_changed = e1000_set_link_status;
+}
+
+static TypeInfo e1000_net_client_info = {
+    .name = TYPE_E1000_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = e1000_net_client_class_init,
 };
 
 static int pci_e1000_init(PCIDevice *pci_dev)
@@ -1246,7 +1259,7 @@  static int pci_e1000_init(PCIDevice *pci_dev)
     checksum = (uint16_t) EEPROM_SUM - checksum;
     d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
 
-    d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
+    d->nic = qemu_new_nic(TYPE_E1000_NET_CLIENT, &d->conf,
                           object_get_typename(OBJECT(d)), d->dev.qdev.id, d);
 
     qemu_format_nic_info_str(&d->nic->nc, macaddr);
@@ -1297,6 +1310,7 @@  static TypeInfo e1000_info = {
 static void e1000_register_types(void)
 {
     type_register_static(&e1000_info);
+    type_register_static(&e1000_net_client_info);
 }
 
 type_init(e1000_register_types)
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 0217795..8ee15d3 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -1844,12 +1844,23 @@  static int pci_nic_uninit(PCIDevice *pci_dev)
     return 0;
 }
 
-static NetClientInfo net_eepro100_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = nic_can_receive,
-    .receive = nic_receive,
-    .cleanup = nic_cleanup,
+#define TYPE_EEPRO100_NET_CLIENT "eepro100-net-client"
+
+static void eepro100_net_client_class_init(ObjectClass *klass,
+                                           void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = nic_can_receive;
+    ncc->receive = nic_receive;
+    ncc->cleanup = nic_cleanup;
+}
+
+static TypeInfo eepro100_net_client_info = {
+    .name = TYPE_EEPRO100_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = eepro100_net_client_class_init,
 };
 
 static int e100_nic_init(PCIDevice *pci_dev)
@@ -1884,7 +1895,7 @@  static int e100_nic_init(PCIDevice *pci_dev)
 
     nic_reset(s);
 
-    s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_EEPRO100_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
 
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
@@ -2102,6 +2113,8 @@  static void eepro100_register_types(void)
         
         type_register(&type_info);
     }
+
+    type_register_static(&eepro100_net_client_info);
 }
 
 type_init(eepro100_register_types)
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 1ca5849..5470642 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -578,13 +578,23 @@  static void eth_cleanup(NetClientState *nc)
         g_free(eth);
 }
 
-static NetClientInfo net_etraxfs_info = {
-	.type = NET_CLIENT_TYPE_NIC,
-	.size = sizeof(NICState),
-	.can_receive = eth_can_receive,
-	.receive = eth_receive,
-	.cleanup = eth_cleanup,
-	.link_status_changed = eth_set_link,
+#define TYPE_ETRAXFS_NET_CLIENT "etraxfs-net-client"
+
+static void etraxfs_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+	NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+	ncc->can_receive = eth_can_receive;
+	ncc->receive = eth_receive;
+	ncc->cleanup = eth_cleanup;
+	ncc->link_status_changed = eth_set_link;
+}
+
+static TypeInfo etraxfs_net_client_info = {
+	.name = TYPE_ETRAXFS_NET_CLIENT,
+	.parent = TYPE_NIC_NET_CLIENT,
+	.instance_size = sizeof(NICState),
+	.class_init = etraxfs_net_client_class_init,
 };
 
 static int fs_eth_init(SysBusDevice *dev)
@@ -604,7 +614,7 @@  static int fs_eth_init(SysBusDevice *dev)
 	sysbus_init_mmio(dev, &s->mmio);
 
 	qemu_macaddr_default_if_unset(&s->conf.macaddr);
-	s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf,
+	s->nic = qemu_new_nic(TYPE_ETRAXFS_NET_CLIENT, &s->conf,
 			      object_get_typename(OBJECT(s)), dev->qdev.id, s);
 	qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
@@ -640,6 +650,7 @@  static TypeInfo etraxfs_eth_info = {
 static void etraxfs_eth_register_types(void)
 {
     type_register_static(&etraxfs_eth_info);
+    type_register_static(&etraxfs_net_client_info);
 }
 
 type_init(etraxfs_eth_register_types)
diff --git a/hw/lan9118.c b/hw/lan9118.c
index 6a37895..b891675 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -1309,13 +1309,23 @@  static void lan9118_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_lan9118_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = lan9118_can_receive,
-    .receive = lan9118_receive,
-    .cleanup = lan9118_cleanup,
-    .link_status_changed = lan9118_set_link,
+#define TYPE_LAN9118_NET_CLIENT "lan9118-net-client"
+
+static void lan9118_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = lan9118_can_receive;
+    ncc->receive = lan9118_receive;
+    ncc->cleanup = lan9118_cleanup;
+    ncc->link_status_changed = lan9118_set_link;
+}
+
+static TypeInfo lan9118_net_client_info = {
+    .name = TYPE_LAN9118_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = lan9118_net_client_class_init,
 };
 
 static int lan9118_init1(SysBusDevice *dev)
@@ -1331,7 +1341,7 @@  static int lan9118_init1(SysBusDevice *dev)
     sysbus_init_irq(dev, &s->irq);
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
 
-    s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_LAN9118_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     s->eeprom[0] = 0xa5;
@@ -1376,6 +1386,7 @@  static TypeInfo lan9118_info = {
 static void lan9118_register_types(void)
 {
     type_register_static(&lan9118_info);
+    type_register_static(&lan9118_net_client_info);
 }
 
 /* Legacy helper function.  Should go away when machine config files are
diff --git a/hw/lance.c b/hw/lance.c
index 1aedb19..25daca7 100644
--- a/hw/lance.c
+++ b/hw/lance.c
@@ -85,22 +85,6 @@  static const MemoryRegionOps lance_mem_ops = {
     },
 };
 
-static void lance_cleanup(NetClientState *nc)
-{
-    PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    pcnet_common_cleanup(d);
-}
-
-static NetClientInfo net_lance_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = pcnet_can_receive,
-    .receive = pcnet_receive,
-    .link_status_changed = pcnet_set_link_status,
-    .cleanup = lance_cleanup,
-};
-
 static const VMStateDescription vmstate_lance = {
     .name = "pcnet",
     .version_id = 3,
@@ -127,7 +111,7 @@  static int lance_init(SysBusDevice *dev)
 
     s->phys_mem_read = ledma_memory_read;
     s->phys_mem_write = ledma_memory_write;
-    return pcnet_common_init(&dev->qdev, s, &net_lance_info);
+    return pcnet_common_init(&dev->qdev, s);
 }
 
 static void lance_reset(DeviceState *dev)
diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c
index 2730178..2957805 100644
--- a/hw/mcf_fec.c
+++ b/hw/mcf_fec.c
@@ -449,12 +449,22 @@  static void mcf_fec_cleanup(NetClientState *nc)
     g_free(s);
 }
 
-static NetClientInfo net_mcf_fec_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = mcf_fec_can_receive,
-    .receive = mcf_fec_receive,
-    .cleanup = mcf_fec_cleanup,
+#define TYPE_MCF_FEC_NET_CLIENT "mcf-fec-net-client"
+
+static void mcf_fec_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = mcf_fec_can_receive;
+    ncc->receive = mcf_fec_receive;
+    ncc->cleanup = mcf_fec_cleanup;
+}
+
+static TypeInfo mcf_fec_net_client_info = {
+    .name = TYPE_MCF_FEC_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = mcf_fec_net_client_class_init,
 };
 
 void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd,
@@ -474,7 +484,15 @@  void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd,
     s->conf.macaddr = nd->macaddr;
     s->conf.peer = nd->netdev;
 
-    s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s);
+    s->nic = qemu_new_nic(TYPE_MCF_FEC_NET_CLIENT, &s->conf, nd->model,
+                          nd->name, s);
 
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 }
+
+static void mcf_fec_register_types(void)
+{
+    type_register_static(&mcf_fec_net_client_info);
+}
+
+type_init(mcf_fec_register_types)
diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c
index 0369894..3c80c35 100644
--- a/hw/milkymist-minimac2.c
+++ b/hw/milkymist-minimac2.c
@@ -447,12 +447,23 @@  static void milkymist_minimac2_reset(DeviceState *d)
     s->phy_regs[R_PHY_ID2] = 0x161a;
 }
 
-static NetClientInfo net_milkymist_minimac2_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = minimac2_can_rx,
-    .receive = minimac2_rx,
-    .cleanup = minimac2_cleanup,
+#define TYPE_MILKYMIST_MINIMAC2_NET_CLIENT "milkymist-minimac2-net-client"
+
+static void milkymist_minimac2_net_client_class_init(ObjectClass *klass,
+                                                     void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = minimac2_can_rx;
+    ncc->receive = minimac2_rx;
+    ncc->cleanup = minimac2_cleanup;
+}
+
+static TypeInfo milkymist_minimac2_net_client_info = {
+    .name = TYPE_MILKYMIST_MINIMAC2_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = milkymist_minimac2_net_client_class_init,
 };
 
 static int milkymist_minimac2_init(SysBusDevice *dev)
@@ -478,7 +489,7 @@  static int milkymist_minimac2_init(SysBusDevice *dev)
     sysbus_add_memory(dev, s->buffers_base, &s->buffers);
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_MILKYMIST_MINIMAC2_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
@@ -545,6 +556,7 @@  static TypeInfo milkymist_minimac2_info = {
 static void milkymist_minimac2_register_types(void)
 {
     type_register_static(&milkymist_minimac2_info);
+    type_register_static(&milkymist_minimac2_net_client_info);
 }
 
 type_init(milkymist_minimac2_register_types)
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index 9cde90a..df06219 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -216,12 +216,22 @@  static void mipsnet_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_mipsnet_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = mipsnet_can_receive,
-    .receive = mipsnet_receive,
-    .cleanup = mipsnet_cleanup,
+#define TYPE_MIPSNET_NET_CLIENT "mipsnet-net-client"
+
+static void mipsnet_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = mipsnet_can_receive;
+    ncc->receive = mipsnet_receive;
+    ncc->cleanup = mipsnet_cleanup;
+}
+
+static TypeInfo mipsnet_net_client_info = {
+    .name = TYPE_MIPSNET_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = mipsnet_net_client_class_init,
 };
 
 static const MemoryRegionOps mipsnet_ioport_ops = {
@@ -239,7 +249,7 @@  static int mipsnet_sysbus_init(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->io);
     sysbus_init_irq(dev, &s->irq);
 
-    s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_MIPSNET_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
@@ -279,6 +289,7 @@  static TypeInfo mipsnet_info = {
 static void mipsnet_register_types(void)
 {
     type_register_static(&mipsnet_info);
+    type_register_static(&mipsnet_net_client_info);
 }
 
 type_init(mipsnet_register_types)
diff --git a/hw/musicpal.c b/hw/musicpal.c
index 10d4f38..539d1ce 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -373,12 +373,23 @@  static void eth_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_mv88w8618_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = eth_can_receive,
-    .receive = eth_receive,
-    .cleanup = eth_cleanup,
+#define TYPE_MV88W8618_NET_CLIENT "mv88w8618-net-client"
+
+static void mv88w8618_net_client_class_init(ObjectClass *klass,
+                                            void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = eth_can_receive;
+    ncc->receive = eth_receive;
+    ncc->cleanup = eth_cleanup;
+}
+
+static TypeInfo mv88w8618_net_client_info = {
+    .name = TYPE_MV88W8618_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = mv88w8618_net_client_class_init,
 };
 
 static int mv88w8618_eth_init(SysBusDevice *dev)
@@ -386,7 +397,7 @@  static int mv88w8618_eth_init(SysBusDevice *dev)
     mv88w8618_eth_state *s = FROM_SYSBUS(mv88w8618_eth_state, dev);
 
     sysbus_init_irq(dev, &s->irq);
-    s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_MV88W8618_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     memory_region_init_io(&s->iomem, &mv88w8618_eth_ops, s, "mv88w8618-eth",
                           MP_ETH_SIZE);
@@ -1691,6 +1702,7 @@  static void musicpal_register_types(void)
     type_register_static(&musicpal_lcd_info);
     type_register_static(&musicpal_gpio_info);
     type_register_static(&musicpal_key_info);
+    type_register_static(&mv88w8618_net_client_info);
 }
 
 type_init(musicpal_register_types)
diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c
index ff4b885..2b4cd61 100644
--- a/hw/ne2000-isa.c
+++ b/hw/ne2000-isa.c
@@ -36,21 +36,6 @@  typedef struct ISANE2000State {
     NE2000State ne2000;
 } ISANE2000State;
 
-static void isa_ne2000_cleanup(NetClientState *nc)
-{
-    NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    s->nic = NULL;
-}
-
-static NetClientInfo net_ne2000_isa_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = ne2000_can_receive,
-    .receive = ne2000_receive,
-    .cleanup = isa_ne2000_cleanup,
-};
-
 static const VMStateDescription vmstate_isa_ne2000 = {
     .name = "ne2000",
     .version_id = 2,
@@ -75,7 +60,7 @@  static int isa_ne2000_initfn(ISADevice *dev)
     qemu_macaddr_default_if_unset(&s->c.macaddr);
     ne2000_reset(s);
 
-    s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c,
+    s->nic = qemu_new_nic(TYPE_NE2000_NET_CLIENT, &s->c,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
 
diff --git a/hw/ne2000.c b/hw/ne2000.c
index e8b1d68..d998601 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -710,12 +710,20 @@  static void ne2000_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_ne2000_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = ne2000_can_receive,
-    .receive = ne2000_receive,
-    .cleanup = ne2000_cleanup,
+static void ne2000_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = ne2000_can_receive;
+    ncc->receive = ne2000_receive;
+    ncc->cleanup = ne2000_cleanup;
+}
+
+static TypeInfo ne2000_net_client_info = {
+    .name = TYPE_NE2000_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = ne2000_net_client_class_init,
 };
 
 static int pci_ne2000_init(PCIDevice *pci_dev)
@@ -735,7 +743,7 @@  static int pci_ne2000_init(PCIDevice *pci_dev)
     qemu_macaddr_default_if_unset(&s->c.macaddr);
     ne2000_reset(s);
 
-    s->nic = qemu_new_nic(&net_ne2000_info, &s->c,
+    s->nic = qemu_new_nic(TYPE_NE2000_NET_CLIENT, &s->c,
                           object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
 
@@ -784,6 +792,7 @@  static TypeInfo ne2000_info = {
 static void ne2000_register_types(void)
 {
     type_register_static(&ne2000_info);
+    type_register_static(&ne2000_net_client_info);
 }
 
 type_init(ne2000_register_types)
diff --git a/hw/ne2000.h b/hw/ne2000.h
index 1e7ab07..2d790cb 100644
--- a/hw/ne2000.h
+++ b/hw/ne2000.h
@@ -28,6 +28,8 @@  typedef struct NE2000State {
     uint8_t mem[NE2000_MEM_SIZE];
 } NE2000State;
 
+#define TYPE_NE2000_NET_CLIENT "ne2000-net-client"
+
 void ne2000_setup_io(NE2000State *s, unsigned size);
 extern const VMStateDescription vmstate_ne2000;
 void ne2000_reset(NE2000State *s);
diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c
index 990ec37..40b4059 100644
--- a/hw/opencores_eth.c
+++ b/hw/opencores_eth.c
@@ -466,13 +466,24 @@  static void open_eth_cleanup(NetClientState *nc)
 {
 }
 
-static NetClientInfo net_open_eth_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = open_eth_can_receive,
-    .receive = open_eth_receive,
-    .cleanup = open_eth_cleanup,
-    .link_status_changed = open_eth_set_link_status,
+#define TYPE_OPEN_ETH_NET_CLIENT "open-eth-net-client"
+
+static void open_eth_net_client_class_init(ObjectClass *klass,
+                                           void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = open_eth_can_receive;
+    ncc->receive = open_eth_receive;
+    ncc->cleanup = open_eth_cleanup;
+    ncc->link_status_changed = open_eth_set_link_status;
+}
+
+static TypeInfo open_eth_net_client_info = {
+    .name = TYPE_OPEN_ETH_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = open_eth_net_client_class_init,
 };
 
 static void open_eth_start_xmit(OpenEthState *s, desc *tx)
@@ -691,7 +702,7 @@  static int sysbus_open_eth_init(SysBusDevice *dev)
 
     sysbus_init_irq(dev, &s->irq);
 
-    s->nic = qemu_new_nic(&net_open_eth_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_OPEN_ETH_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
     return 0;
 }
@@ -728,6 +739,7 @@  static TypeInfo open_eth_info = {
 static void open_eth_register_types(void)
 {
     type_register_static(&open_eth_info);
+    type_register_static(&open_eth_net_client_info);
 }
 
 type_init(open_eth_register_types)
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
index 8bbad47..e8ffa78 100644
--- a/hw/pcnet-pci.c
+++ b/hw/pcnet-pci.c
@@ -264,13 +264,6 @@  static void pci_physical_memory_read(void *dma_opaque, target_phys_addr_t addr,
     pci_dma_read(dma_opaque, addr, buf, len);
 }
 
-static void pci_pcnet_cleanup(NetClientState *nc)
-{
-    PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    pcnet_common_cleanup(d);
-}
-
 static int pci_pcnet_uninit(PCIDevice *dev)
 {
     PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev);
@@ -283,15 +276,6 @@  static int pci_pcnet_uninit(PCIDevice *dev)
     return 0;
 }
 
-static NetClientInfo net_pci_pcnet_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = pcnet_can_receive,
-    .receive = pcnet_receive,
-    .link_status_changed = pcnet_set_link_status,
-    .cleanup = pci_pcnet_cleanup,
-};
-
 static int pci_pcnet_init(PCIDevice *pci_dev)
 {
     PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev);
@@ -330,7 +314,7 @@  static int pci_pcnet_init(PCIDevice *pci_dev)
     s->phys_mem_write = pci_physical_memory_write;
     s->dma_opaque = pci_dev;
 
-    return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info);
+    return pcnet_common_init(&pci_dev->qdev, s);
 }
 
 static void pci_reset(DeviceState *dev)
diff --git a/hw/pcnet.c b/hw/pcnet.c
index 40820b3..9444b97 100644
--- a/hw/pcnet.c
+++ b/hw/pcnet.c
@@ -1716,12 +1716,34 @@  const VMStateDescription vmstate_pcnet = {
     }
 };
 
-void pcnet_common_cleanup(PCNetState *d)
+static void pcnet_common_cleanup(NetClientState *nc)
 {
+    NICState *nic = DO_UPCAST(NICState, nc, nc);
+    PCNetState *d = nic->opaque;
+
     d->nic = NULL;
 }
 
-int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
+#define TYPE_PCNET_NET_CLIENT "pcnet-net-client"
+
+static void pcnet_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = pcnet_can_receive;
+    ncc->receive = pcnet_receive;
+    ncc->link_status_changed = pcnet_set_link_status;
+    ncc->cleanup = pcnet_common_cleanup;
+}
+
+static TypeInfo pcnet_net_client_info = {
+    .name = TYPE_PCNET_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = pcnet_net_client_class_init,
+};
+
+int pcnet_common_init(DeviceState *dev, PCNetState *s)
 {
     int i;
     uint16_t checksum;
@@ -1729,7 +1751,8 @@  int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
     s->poll_timer = qemu_new_timer_ns(vm_clock, pcnet_poll_timer, s);
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s);
+    s->nic = qemu_new_nic(TYPE_PCNET_NET_CLIENT, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
     add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
@@ -1765,3 +1788,10 @@  int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
 
     return 0;
 }
+
+static void pcnet_register_types(void)
+{
+    type_register_static(&pcnet_net_client_info);
+}
+
+type_init(pcnet_register_types)
diff --git a/hw/pcnet.h b/hw/pcnet.h
index d0af54a..c9b408d 100644
--- a/hw/pcnet.h
+++ b/hw/pcnet.h
@@ -60,6 +60,5 @@  uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap);
 int pcnet_can_receive(NetClientState *nc);
 ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_);
 void pcnet_set_link_status(NetClientState *nc);
-void pcnet_common_cleanup(PCNetState *d);
-int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info);
+int pcnet_common_init(DeviceState *dev, PCNetState *s);
 extern const VMStateDescription vmstate_pcnet;
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index b31a649..8150a67 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3452,12 +3452,23 @@  static int pci_rtl8139_uninit(PCIDevice *dev)
     return 0;
 }
 
-static NetClientInfo net_rtl8139_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = rtl8139_can_receive,
-    .receive = rtl8139_receive,
-    .cleanup = rtl8139_cleanup,
+#define TYPE_RTL8139_NET_CLIENT "rtl8139-net-client"
+
+static void rtl8139_net_client_class_init(ObjectClass *klass,
+                                          void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = rtl8139_can_receive;
+    ncc->receive = rtl8139_receive;
+    ncc->cleanup = rtl8139_cleanup;
+}
+
+static TypeInfo rtl8139_net_client_info = {
+    .name = TYPE_RTL8139_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = rtl8139_net_client_class_init,
 };
 
 static int pci_rtl8139_init(PCIDevice *dev)
@@ -3489,7 +3500,7 @@  static int pci_rtl8139_init(PCIDevice *dev)
     s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8;
     s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
 
-    s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_RTL8139_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
@@ -3538,6 +3549,7 @@  static TypeInfo rtl8139_info = {
 static void rtl8139_register_types(void)
 {
     type_register_static(&rtl8139_info);
+    type_register_static(&rtl8139_net_client_info);
 }
 
 type_init(rtl8139_register_types)
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
index aaa2c60..4337462 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -735,12 +735,23 @@  static void smc91c111_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_smc91c111_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = smc91c111_can_receive,
-    .receive = smc91c111_receive,
-    .cleanup = smc91c111_cleanup,
+#define TYPE_SMC91C111_NET_CLIENT "smc91c111-net-client"
+
+static void smc91c111_net_client_class_init(ObjectClass *klass,
+                                            void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = smc91c111_can_receive;
+    ncc->receive = smc91c111_receive;
+    ncc->cleanup = smc91c111_cleanup;
+}
+
+static TypeInfo smc91c111_net_client_info = {
+    .name = TYPE_SMC91C111_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = smc91c111_net_client_class_init,
 };
 
 static int smc91c111_init1(SysBusDevice *dev)
@@ -751,7 +762,7 @@  static int smc91c111_init1(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->mmio);
     sysbus_init_irq(dev, &s->irq);
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_SMC91C111_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     /* ??? Save/restore.  */
@@ -784,6 +795,7 @@  static TypeInfo smc91c111_info = {
 static void smc91c111_register_types(void)
 {
     type_register_static(&smc91c111_info);
+    type_register_static(&smc91c111_net_client_info);
 }
 
 /* Legacy helper function.  Should go away when machine config files are
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index 76fa0b5..54c8b38 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -175,11 +175,22 @@  static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
     return size;
 }
 
-static NetClientInfo net_spapr_vlan_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = spapr_vlan_can_receive,
-    .receive = spapr_vlan_receive,
+#define TYPE_SPAPR_VLAN_NET_CLIENT "spapr-vlan-net-client"
+
+static void spapr_vlan_net_client_class_init(ObjectClass *klass,
+                                             void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = spapr_vlan_can_receive;
+    ncc->receive = spapr_vlan_receive;
+}
+
+static TypeInfo spapr_vlan_net_client_info = {
+    .name = TYPE_SPAPR_VLAN_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = spapr_vlan_net_client_class_init,
 };
 
 static void spapr_vlan_reset(VIOsPAPRDevice *sdev)
@@ -197,7 +208,7 @@  static int spapr_vlan_init(VIOsPAPRDevice *sdev)
 
     qemu_macaddr_default_if_unset(&dev->nicconf.macaddr);
 
-    dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf,
+    dev->nic = qemu_new_nic(TYPE_SPAPR_VLAN_NET_CLIENT, &dev->nicconf,
                             object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev);
     qemu_format_nic_info_str(&dev->nic->nc, dev->nicconf.macaddr.a);
 
@@ -515,6 +526,7 @@  static void spapr_vlan_register_types(void)
                              h_add_logical_lan_buffer);
     spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl);
     type_register_static(&spapr_vlan_info);
+    type_register_static(&spapr_vlan_net_client_info);
 }
 
 type_init(spapr_vlan_register_types)
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
index fcc6cd0..a892712 100644
--- a/hw/stellaris_enet.c
+++ b/hw/stellaris_enet.c
@@ -392,12 +392,23 @@  static void stellaris_enet_cleanup(NetClientState *nc)
     g_free(s);
 }
 
-static NetClientInfo net_stellaris_enet_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = stellaris_enet_can_receive,
-    .receive = stellaris_enet_receive,
-    .cleanup = stellaris_enet_cleanup,
+#define TYPE_STELLARIS_ENET_NET_CLIENT "stellaris-enet-net-client"
+
+static void stellaris_enet_net_client_class_init(ObjectClass *klass,
+                                                 void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = stellaris_enet_can_receive;
+    ncc->receive = stellaris_enet_receive;
+    ncc->cleanup = stellaris_enet_cleanup;
+}
+
+static TypeInfo stellaris_enet_net_client_info = {
+    .name = TYPE_STELLARIS_ENET_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = stellaris_enet_net_client_class_init,
 };
 
 static int stellaris_enet_init(SysBusDevice *dev)
@@ -410,7 +421,7 @@  static int stellaris_enet_init(SysBusDevice *dev)
     sysbus_init_irq(dev, &s->irq);
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
 
-    s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_STELLARIS_ENET_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
@@ -444,6 +455,7 @@  static TypeInfo stellaris_enet_info = {
 static void stellaris_enet_register_types(void)
 {
     type_register_static(&stellaris_enet_info);
+    type_register_static(&stellaris_enet_net_client_info);
 }
 
 type_init(stellaris_enet_register_types)
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 2d03639..e8464ed 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1312,12 +1312,22 @@  static void usb_net_handle_destroy(USBDevice *dev)
     qemu_del_net_client(&s->nic->nc);
 }
 
-static NetClientInfo net_usbnet_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = usbnet_can_receive,
-    .receive = usbnet_receive,
-    .cleanup = usbnet_cleanup,
+#define TYPE_USB_NET_CLIENT "usb-net-client"
+
+static void usb_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = usbnet_can_receive;
+    ncc->receive = usbnet_receive;
+    ncc->cleanup = usbnet_cleanup;
+}
+
+static TypeInfo usb_net_client_info = {
+    .name = TYPE_USB_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = usb_net_client_class_init,
 };
 
 static int usb_net_initfn(USBDevice *dev)
@@ -1337,7 +1347,7 @@  static int usb_net_initfn(USBDevice *dev)
     s->vendorid = 0x1234;
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_USB_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
@@ -1422,6 +1432,7 @@  static void usb_net_register_types(void)
 {
     type_register_static(&net_info);
     usb_legacy_register("usb-net", "net", usb_net_init);
+    type_register_static(&usb_net_client_info);
 }
 
 type_init(usb_net_register_types)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 1575879..cdde3fb 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -988,13 +988,23 @@  static void virtio_net_cleanup(NetClientState *nc)
     n->nic = NULL;
 }
 
-static NetClientInfo net_virtio_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = virtio_net_can_receive,
-    .receive = virtio_net_receive,
-        .cleanup = virtio_net_cleanup,
-    .link_status_changed = virtio_net_set_link_status,
+#define TYPE_VIRTIO_NET_CLIENT "virtio-net-client"
+
+static void virtio_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = virtio_net_can_receive;
+    ncc->receive = virtio_net_receive;
+    ncc->cleanup = virtio_net_cleanup;
+    ncc->link_status_changed = virtio_net_set_link_status;
+}
+
+static TypeInfo virtio_net_client_info = {
+    .name = TYPE_VIRTIO_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = virtio_net_client_class_init,
 };
 
 VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
@@ -1035,7 +1045,8 @@  VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
     memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac));
     n->status = VIRTIO_NET_S_LINK_UP;
 
-    n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n);
+    n->nic = qemu_new_nic(TYPE_VIRTIO_NET_CLIENT, conf,
+                          object_get_typename(OBJECT(dev)), dev->id, n);
 
     qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a);
 
@@ -1081,3 +1092,10 @@  void virtio_net_exit(VirtIODevice *vdev)
     qemu_del_net_client(&n->nic->nc);
     virtio_cleanup(&n->vdev);
 }
+
+static void virtio_net_register_types(void)
+{
+    type_register_static(&virtio_net_client_info);
+}
+
+type_init(virtio_net_register_types)
diff --git a/hw/xen_nic.c b/hw/xen_nic.c
index ba4a45c..d8b7fa5 100644
--- a/hw/xen_nic.c
+++ b/hw/xen_nic.c
@@ -303,11 +303,21 @@  static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
 
 /* ------------------------------------------------------------- */
 
-static NetClientInfo net_xen_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = net_rx_ok,
-    .receive = net_rx_packet,
+#define TYPE_XEN_NET_CLIENT "xen-net-client"
+
+static void xen_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = net_rx_ok;
+    ncc->receive = net_rx_packet;
+}
+
+static TypeInfo xen_net_client_info = {
+    .name = TYPE_XEN_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = xen_net_client_class_init,
 };
 
 static int net_init(struct XenDevice *xendev)
@@ -330,7 +340,7 @@  static int net_init(struct XenDevice *xendev)
 
     netdev->conf.peer = NULL;
 
-    netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
+    netdev->nic = qemu_new_nic(TYPE_XEN_NET_CLIENT, &netdev->conf,
                                "xen", NULL, netdev);
 
     snprintf(netdev->nic->nc.info_str, sizeof(netdev->nic->nc.info_str),
@@ -439,3 +449,10 @@  struct XenDevOps xen_netdev_ops = {
     .disconnect = net_disconnect,
     .free       = net_free,
 };
+
+static void xen_net_register_types(void)
+{
+    type_register_static(&xen_net_client_info);
+}
+
+type_init(xen_net_register_types)
diff --git a/hw/xgmac.c b/hw/xgmac.c
index b2cae49..41264dc 100644
--- a/hw/xgmac.c
+++ b/hw/xgmac.c
@@ -370,12 +370,23 @@  static void eth_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_xgmac_enet_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = eth_can_rx,
-    .receive = eth_rx,
-    .cleanup = eth_cleanup,
+#define TYPE_XGMAC_ENET_NET_CLIENT "xgmac-enet-net-client"
+
+static void xgmac_enet_net_client_class_init(ObjectClass *klass,
+                                             void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = eth_can_rx;
+    ncc->receive = eth_rx;
+    ncc->cleanup = eth_cleanup;
+}
+
+static TypeInfo xgmac_enet_net_client_info = {
+    .name = TYPE_XGMAC_ENET_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = xgmac_enet_net_client_class_init,
 };
 
 static int xgmac_enet_init(SysBusDevice *dev)
@@ -389,7 +400,7 @@  static int xgmac_enet_init(SysBusDevice *dev)
     sysbus_init_irq(dev, &s->mci_irq);
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_XGMAC_ENET_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
@@ -428,6 +439,7 @@  static TypeInfo xgmac_enet_info = {
 static void xgmac_enet_register_types(void)
 {
     type_register_static(&xgmac_enet_info);
+    type_register_static(&xgmac_enet_net_client_info);
 }
 
 type_init(xgmac_enet_register_types)
diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c
index 984071a..6e12a06 100644
--- a/hw/xilinx_axienet.c
+++ b/hw/xilinx_axienet.c
@@ -831,12 +831,23 @@  axienet_stream_push(void *opaque, uint8_t *buf, size_t size, uint32_t *hdr)
     enet_update_irq(s);
 }
 
-static NetClientInfo net_xilinx_enet_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = eth_can_rx,
-    .receive = eth_rx,
-    .cleanup = eth_cleanup,
+#define TYPE_XILINX_AXI_NET_CLIENT "xilinx-axi-net-client"
+
+static void xilinx_axi_net_client_class_init(ObjectClass *klass,
+                                             void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = eth_can_rx;
+    ncc->receive = eth_rx;
+    ncc->cleanup = eth_cleanup;
+}
+
+static TypeInfo xilinx_axi_net_client_info = {
+    .name = TYPE_XILINX_AXI_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = xilinx_axi_net_client_class_init,
 };
 
 static int xilinx_enet_init(SysBusDevice *dev)
@@ -855,7 +866,7 @@  static int xilinx_enet_init(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->iomem);
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_XILINX_AXI_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
@@ -898,6 +909,7 @@  static TypeInfo xilinx_enet_info = {
 static void xilinx_enet_register_types(void)
 {
     type_register_static(&xilinx_enet_info);
+    type_register_static(&xilinx_axi_net_client_info);
 }
 
 type_init(xilinx_enet_register_types)
diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c
index f261d89..9a88165 100644
--- a/hw/xilinx_ethlite.c
+++ b/hw/xilinx_ethlite.c
@@ -201,12 +201,23 @@  static void eth_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_xilinx_ethlite_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = eth_can_rx,
-    .receive = eth_rx,
-    .cleanup = eth_cleanup,
+#define TYPE_XILINX_ETHLITE_NET_CLIENT "xilinx-ethlite-net-client"
+
+static void xilinx_ethlite_net_client_class_init(ObjectClass *klass,
+                                                 void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->can_receive = eth_can_rx;
+    ncc->receive = eth_rx;
+    ncc->cleanup = eth_cleanup;
+}
+
+static TypeInfo xilinx_ethlite_net_client_info = {
+    .name = TYPE_XILINX_ETHLITE_NET_CLIENT,
+    .parent = TYPE_NIC_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = xilinx_ethlite_net_client_class_init,
 };
 
 static int xilinx_ethlite_init(SysBusDevice *dev)
@@ -221,7 +232,7 @@  static int xilinx_ethlite_init(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->mmio);
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
+    s->nic = qemu_new_nic(TYPE_XILINX_ETHLITE_NET_CLIENT, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     return 0;
@@ -253,6 +264,7 @@  static TypeInfo xilinx_ethlite_info = {
 static void xilinx_ethlite_register_types(void)
 {
     type_register_static(&xilinx_ethlite_info);
+    type_register_static(&xilinx_ethlite_net_client_info);
 }
 
 type_init(xilinx_ethlite_register_types)
diff --git a/net.c b/net.c
index 8c27ff2..2228bdc 100644
--- a/net.c
+++ b/net.c
@@ -155,8 +155,10 @@  void qemu_macaddr_default_if_unset(MACAddr *macaddr)
 
 static void notify_link_status_changed(NetClientState *nc)
 {
-    if (nc->info->link_status_changed) {
-        nc->info->link_status_changed(nc);
+    NetClientClass *klass = NET_CLIENT_GET_CLASS(nc);
+
+    if (klass->link_status_changed) {
+        klass->link_status_changed(nc);
     }
 }
 
@@ -188,18 +190,15 @@  static char *assign_name(NetClientState *nc1, const char *model)
     return g_strdup(buf);
 }
 
-NetClientState *qemu_new_net_client(NetClientInfo *info,
+NetClientState *qemu_new_net_client(const char *typename,
                                     NetClientState *peer,
                                     const char *model,
                                     const char *name)
 {
     NetClientState *nc;
 
-    assert(info->size >= sizeof(NetClientState));
-
-    nc = g_malloc0(info->size);
+    nc = NET_CLIENT(object_new(typename));
 
-    nc->info = info;
     nc->model = g_strdup(model);
     if (name) {
         nc->name = g_strdup(name);
@@ -219,7 +218,7 @@  NetClientState *qemu_new_net_client(NetClientInfo *info,
     return nc;
 }
 
-NICState *qemu_new_nic(NetClientInfo *info,
+NICState *qemu_new_nic(const char *typename,
                        NICConf *conf,
                        const char *model,
                        const char *name,
@@ -228,12 +227,9 @@  NICState *qemu_new_nic(NetClientInfo *info,
     NetClientState *nc;
     NICState *nic;
 
-    assert(info->type == NET_CLIENT_TYPE_NIC);
-    assert(info->size >= sizeof(NICState));
-
-    nc = qemu_new_net_client(info, conf->peer, model, name);
+    nc = qemu_new_net_client(typename, conf->peer, model, name);
 
-    nic = DO_UPCAST(NICState, nc, nc);
+    nic = NIC_NET_CLIENT(nc);
     nic->conf = conf;
     nic->opaque = opaque;
 
@@ -242,10 +238,12 @@  NICState *qemu_new_nic(NetClientInfo *info,
 
 static void qemu_cleanup_net_client(NetClientState *nc)
 {
+    NetClientClass *klass = NET_CLIENT_GET_CLASS(nc);
+
     QTAILQ_REMOVE(&net_clients, nc, next);
 
-    if (nc->info->cleanup) {
-        nc->info->cleanup(nc);
+    if (klass->cleanup) {
+        klass->cleanup(nc);
     }
 }
 
@@ -259,14 +257,15 @@  static void qemu_free_net_client(NetClientState *nc)
     }
     g_free(nc->name);
     g_free(nc->model);
-    g_free(nc);
+    object_delete(OBJECT(nc));
 }
 
 void qemu_del_net_client(NetClientState *nc)
 {
     /* If there is a peer NIC, delete and cleanup client, but do not free. */
-    if (nc->peer && nc->peer->info->type == NET_CLIENT_TYPE_NIC) {
-        NICState *nic = DO_UPCAST(NICState, nc, nc->peer);
+    if (nc->peer && object_dynamic_cast(OBJECT(nc->peer),
+                                        TYPE_NIC_NET_CLIENT)) {
+        NICState *nic = NIC_NET_CLIENT(nc->peer);
         if (nic->peer_deleted) {
             return;
         }
@@ -279,8 +278,8 @@  void qemu_del_net_client(NetClientState *nc)
     }
 
     /* If this is a peer NIC and peer has already been deleted, free it now. */
-    if (nc->peer && nc->info->type == NET_CLIENT_TYPE_NIC) {
-        NICState *nic = DO_UPCAST(NICState, nc, nc);
+    if (nc->peer && object_dynamic_cast(OBJECT(nc), TYPE_NIC_NET_CLIENT)) {
+        NICState *nic = NIC_NET_CLIENT(nc);
         if (nic->peer_deleted) {
             qemu_free_net_client(nc->peer);
         }
@@ -295,22 +294,26 @@  void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
     NetClientState *nc;
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_TYPE_NIC) {
-            func(DO_UPCAST(NICState, nc, nc), opaque);
+        if (object_dynamic_cast(OBJECT(nc), TYPE_NIC_NET_CLIENT)) {
+            func(NIC_NET_CLIENT(nc), opaque);
         }
     }
 }
 
 int qemu_can_send_packet(NetClientState *sender)
 {
+    NetClientClass *klass;
+
     if (!sender->peer) {
         return 1;
     }
 
     if (sender->peer->receive_disabled) {
         return 0;
-    } else if (sender->peer->info->can_receive &&
-               !sender->peer->info->can_receive(sender->peer)) {
+    }
+
+    klass = NET_CLIENT_GET_CLASS(sender->peer);
+    if (klass->can_receive && !klass->can_receive(sender->peer)) {
         return 0;
     }
     return 1;
@@ -323,6 +326,7 @@  ssize_t qemu_deliver_packet(NetClientState *sender,
                             void *opaque)
 {
     NetClientState *nc = opaque;
+    NetClientClass *klass;
     ssize_t ret;
 
     if (nc->link_down) {
@@ -333,10 +337,11 @@  ssize_t qemu_deliver_packet(NetClientState *sender,
         return 0;
     }
 
-    if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
-        ret = nc->info->receive_raw(nc, data, size);
+    klass = NET_CLIENT_GET_CLASS(nc);
+    if (flags & QEMU_NET_PACKET_FLAG_RAW && klass->receive_raw) {
+        ret = klass->receive_raw(nc, data, size);
     } else {
-        ret = nc->info->receive(nc, data, size);
+        ret = klass->receive(nc, data, size);
     }
 
     if (ret == 0) {
@@ -405,12 +410,13 @@  ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size)
 static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
                                int iovcnt)
 {
+    NetClientClass *klass = NET_CLIENT_GET_CLASS(nc);
     uint8_t buffer[4096];
     size_t offset;
 
     offset = iov_to_buf(iov, iovcnt, buffer, 0, sizeof(buffer));
 
-    return nc->info->receive(nc, buffer, offset);
+    return klass->receive(nc, buffer, offset);
 }
 
 ssize_t qemu_deliver_packet_iov(NetClientState *sender,
@@ -420,13 +426,14 @@  ssize_t qemu_deliver_packet_iov(NetClientState *sender,
                                 void *opaque)
 {
     NetClientState *nc = opaque;
+    NetClientClass *klass = NET_CLIENT_GET_CLASS(nc);
 
     if (nc->link_down) {
         return iov_size(iov, iovcnt);
     }
 
-    if (nc->info->receive_iov) {
-        return nc->info->receive_iov(nc, iov, iovcnt);
+    if (klass->receive_iov) {
+        return klass->receive_iov(nc, iov, iovcnt);
     } else {
         return nc_sendv_compat(nc, iov, iovcnt);
     }
@@ -457,8 +464,10 @@  qemu_sendv_packet(NetClientState *nc, const struct iovec *iov, int iovcnt)
 
 void qemu_net_poll(NetClientState *nc, bool enable)
 {
-    if (nc->info->poll) {
-        nc->info->poll(nc, enable);
+    NetClientClass *klass = NET_CLIENT_CLASS(nc);
+
+    if (klass->poll) {
+        klass->poll(nc, enable);
     }
 }
 
@@ -467,8 +476,9 @@  NetClientState *qemu_find_netdev(const char *id)
     NetClientState *nc;
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_TYPE_NIC)
+        if (object_dynamic_cast(OBJECT(nc), TYPE_NIC_NET_CLIENT)) {
             continue;
+        }
         if (!strcmp(nc->name, id)) {
             return nc;
         }
@@ -1088,29 +1098,29 @@  void qmp_netdev_del(const char *id, Error **errp)
 
 void print_net_client(Monitor *mon, NetClientState *vc)
 {
+    NetClientClass *klass = NET_CLIENT_GET_CLASS(vc);
+
     monitor_printf(mon, "%s: type=%s,%s\n", vc->name,
-                   net_client_types[vc->info->type].type, vc->info_str);
+                   klass->type_str, vc->info_str);
 }
 
 void do_info_network(Monitor *mon)
 {
     NetClientState *nc, *peer;
-    net_client_type type;
 
     net_hub_info(mon);
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
         peer = nc->peer;
-        type = nc->info->type;
 
         if (net_hub_port_peer_nc(nc)) {
             continue;
         }
 
-        if (!peer || type == NET_CLIENT_TYPE_NIC) {
+        if (!peer || object_dynamic_cast(OBJECT(nc), TYPE_NIC_NET_CLIENT)) {
             print_net_client(mon, nc);
         } /* else it's a netdev connected to a NIC, printed with the NIC */
-        if (peer && type == NET_CLIENT_TYPE_NIC) {
+        if (peer && object_dynamic_cast(OBJECT(nc), TYPE_NIC_NET_CLIENT)) {
             monitor_printf(mon, " \\ ");
             print_net_client(mon, peer);
         }
@@ -1179,7 +1189,8 @@  void net_check_clients(void)
     QTAILQ_FOREACH(nc, &net_clients, next) {
         if (!nc->peer) {
             fprintf(stderr, "Warning: %s %s has no peer\n",
-                    nc->info->type == NET_CLIENT_TYPE_NIC ? "nic" : "netdev",
+                    object_dynamic_cast(OBJECT(nc),
+                        TYPE_NIC_NET_CLIENT) ? "nic" : "netdev",
                     nc->name);
         }
     }
@@ -1291,3 +1302,34 @@  unsigned compute_mcast_idx(const uint8_t *ep)
     }
     return crc >> 26;
 }
+
+static TypeInfo net_client_info = {
+    .name = TYPE_NET_CLIENT,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(NetClientState),
+    .class_size = sizeof(NetClientClass),
+    .abstract = true,
+};
+
+static void nic_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "nic";
+}
+
+static TypeInfo nic_net_client_info = {
+    .name = TYPE_NIC_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(NICState),
+    .class_init = nic_net_client_class_init,
+    .abstract = true,
+};
+
+static void net_client_register_types(void)
+{
+    type_register_static(&net_client_info);
+    type_register_static(&nic_net_client_info);
+}
+
+type_init(net_client_register_types)
diff --git a/net.h b/net.h
index 64d5d0e..efce535 100644
--- a/net.h
+++ b/net.h
@@ -1,6 +1,7 @@ 
 #ifndef QEMU_NET_H
 #define QEMU_NET_H
 
+#include "qemu/object.h"
 #include "qemu-queue.h"
 #include "qemu-common.h"
 #include "qdict.h"
@@ -42,6 +43,14 @@  typedef enum {
     NET_CLIENT_TYPE_MAX
 } net_client_type;
 
+#define TYPE_NET_CLIENT "net-client"
+#define NET_CLIENT_GET_CLASS(obj) \
+   OBJECT_GET_CLASS(NetClientClass, obj, TYPE_NET_CLIENT)
+#define NET_CLIENT_CLASS(klass) \
+   OBJECT_CLASS_CHECK(NetClientClass, klass, TYPE_NET_CLIENT)
+#define NET_CLIENT(obj) \
+   OBJECT_CHECK(NetClientState, obj, TYPE_NET_CLIENT)
+
 typedef void (NetPoll)(NetClientState *, bool enable);
 typedef int (NetCanReceive)(NetClientState *);
 typedef ssize_t (NetReceive)(NetClientState *, const uint8_t *, size_t);
@@ -49,9 +58,10 @@  typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int);
 typedef void (NetCleanup) (NetClientState *);
 typedef void (LinkStatusChanged)(NetClientState *);
 
-typedef struct NetClientInfo {
-    net_client_type type;
-    size_t size;
+typedef struct NetClientClass {
+    ObjectClass parent;
+
+    const char *type_str; /* human-readable name for "info network" */
     NetReceive *receive;
     NetReceive *receive_raw;
     NetReceiveIOV *receive_iov;
@@ -59,10 +69,11 @@  typedef struct NetClientInfo {
     NetCleanup *cleanup;
     LinkStatusChanged *link_status_changed;
     NetPoll *poll;
-} NetClientInfo;
+} NetClientClass;
+
+typedef struct NetClientState {
+    Object parent;
 
-struct NetClientState {
-    NetClientInfo *info;
     int link_down;
     QTAILQ_ENTRY(NetClientState) next;
     NetClientState *peer;
@@ -71,7 +82,11 @@  struct NetClientState {
     char *name;
     char info_str[256];
     unsigned receive_disabled : 1;
-};
+} NetClientState;
+
+#define TYPE_NIC_NET_CLIENT "nic-net-client"
+#define NIC_NET_CLIENT(obj) \
+   OBJECT_CHECK(NICState, obj, TYPE_NIC_NET_CLIENT)
 
 typedef struct NICState {
     NetClientState nc;
@@ -81,11 +96,11 @@  typedef struct NICState {
 } NICState;
 
 NetClientState *qemu_find_netdev(const char *id);
-NetClientState *qemu_new_net_client(NetClientInfo *info,
+NetClientState *qemu_new_net_client(const char *typename,
                                     NetClientState *peer,
                                     const char *model,
                                     const char *name);
-NICState *qemu_new_nic(NetClientInfo *info,
+NICState *qemu_new_nic(const char *typename,
                        NICConf *conf,
                        const char *model,
                        const char *name,
diff --git a/net/dump.c b/net/dump.c
index 76f3664..eb39ad7 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -93,11 +93,22 @@  static void dump_cleanup(NetClientState *nc)
     close(s->fd);
 }
 
-static NetClientInfo net_dump_info = {
-    .type = NET_CLIENT_TYPE_DUMP,
-    .size = sizeof(DumpState),
-    .receive = dump_receive,
-    .cleanup = dump_cleanup,
+#define TYPE_DUMP_NET_CLIENT "dump-net-client"
+
+static void net_dump_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "dump",
+    ncc->receive = dump_receive;
+    ncc->cleanup = dump_cleanup;
+}
+
+static TypeInfo net_dump_info = {
+    .name = TYPE_DUMP_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(DumpState),
+    .class_init = net_dump_class_init,
 };
 
 static int net_dump_init(NetClientState *peer, const char *device,
@@ -129,7 +140,7 @@  static int net_dump_init(NetClientState *peer, const char *device,
         return -1;
     }
 
-    nc = qemu_new_net_client(&net_dump_info, peer, device, name);
+    nc = qemu_new_net_client(TYPE_DUMP_NET_CLIENT, peer, device, name);
 
     snprintf(nc->info_str, sizeof(nc->info_str),
              "dump to %s (len=%d)", filename, len);
@@ -169,3 +180,10 @@  int net_init_dump(QemuOpts *opts, const char *name, NetClientState *peer)
 
     return net_dump_init(peer, "dump", name, file, len);
 }
+
+static void net_dump_register_types(void)
+{
+    type_register_static(&net_dump_info);
+}
+
+type_init(net_dump_register_types)
diff --git a/net/hub.c b/net/hub.c
index 001f818..5cb196a 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -128,13 +128,27 @@  static void net_hub_port_cleanup(NetClientState *nc)
     QLIST_REMOVE(port, next);
 }
 
-static NetClientInfo net_hub_port_info = {
-    .type = NET_CLIENT_TYPE_HUB,
-    .size = sizeof(NetHubPort),
-    .can_receive = net_hub_port_can_receive,
-    .receive = net_hub_port_receive,
-    .receive_iov = net_hub_port_receive_iov,
-    .cleanup = net_hub_port_cleanup,
+#define TYPE_HUB_PORT_NET_CLIENT "hub-port-net-client"
+#define HUB_PORT(obj) \
+    OBJECT_CHECK(NetHubPort, obj, TYPE_HUB_PORT_NET_CLIENT)
+
+static void net_hub_port_net_client_class_init(ObjectClass *klass,
+                                               void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "hubport",
+    ncc->can_receive = net_hub_port_can_receive;
+    ncc->receive = net_hub_port_receive;
+    ncc->receive_iov = net_hub_port_receive_iov;
+    ncc->cleanup = net_hub_port_cleanup;
+}
+
+static TypeInfo hub_port_net_client_info = {
+    .name = TYPE_HUB_PORT_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(NetHubPort),
+    .class_init = net_hub_port_net_client_class_init,
 };
 
 static NetHubPort *net_hub_port_new(NetHub *hub)
@@ -146,8 +160,8 @@  static NetHubPort *net_hub_port_new(NetHub *hub)
 
     snprintf(name, sizeof name, "hub%uport%u", hub->id, id);
 
-    nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
-    port = DO_UPCAST(NetHubPort, nc, nc);
+    nc = qemu_new_net_client(TYPE_HUB_PORT_NET_CLIENT, NULL, "hub", name);
+    port = HUB_PORT(nc);
     port->id = id;
     port->hub = hub;
 
@@ -309,18 +323,10 @@  void net_hub_check_clients(void)
                 continue;
             }
 
-            switch (peer->info->type) {
-            case NET_CLIENT_TYPE_NIC:
+            if (object_dynamic_cast(OBJECT(peer), TYPE_NIC_NET_CLIENT)) {
                 has_nic = 1;
-                break;
-            case NET_CLIENT_TYPE_USER:
-            case NET_CLIENT_TYPE_TAP:
-            case NET_CLIENT_TYPE_SOCKET:
-            case NET_CLIENT_TYPE_VDE:
+            } else {
                 has_host_dev = 1;
-                break;
-            default:
-                break;
             }
         }
         if (has_host_dev && !has_nic) {
@@ -333,3 +339,10 @@  void net_hub_check_clients(void)
         }
     }
 }
+
+static void net_hub_register_types(void)
+{
+    type_register_static(&hub_port_net_client_info);
+}
+
+type_init(net_hub_register_types)
diff --git a/net/slirp.c b/net/slirp.c
index 1e5cabb..990268c 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -121,11 +121,22 @@  static void net_slirp_cleanup(NetClientState *nc)
     QTAILQ_REMOVE(&slirp_stacks, s, entry);
 }
 
-static NetClientInfo net_slirp_info = {
-    .type = NET_CLIENT_TYPE_USER,
-    .size = sizeof(SlirpState),
-    .receive = net_slirp_receive,
-    .cleanup = net_slirp_cleanup,
+#define TYPE_SLIRP_NET_CLIENT "slirp-net-client"
+
+static void net_slirp_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "user";
+    ncc->receive = net_slirp_receive;
+    ncc->cleanup = net_slirp_cleanup;
+}
+
+static TypeInfo net_slirp_info = {
+    .name = TYPE_SLIRP_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(SlirpState),
+    .class_init = net_slirp_class_init,
 };
 
 static int net_slirp_init(NetClientState *peer, const char *model,
@@ -231,7 +242,7 @@  static int net_slirp_init(NetClientState *peer, const char *model,
     }
 #endif
 
-    nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
+    nc = qemu_new_net_client(TYPE_SLIRP_NET_CLIENT, peer, model, name);
 
     snprintf(nc->info_str, sizeof(nc->info_str),
              "net=%s,restrict=%s", inet_ntoa(net),
@@ -768,3 +779,9 @@  int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret
     return 1;
 }
 
+static void net_slirp_register_types(void)
+{
+    type_register_static(&net_slirp_info);
+}
+
+type_init(net_slirp_register_types)
diff --git a/net/socket.c b/net/socket.c
index 9b15479..f3489cb 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -33,6 +33,10 @@ 
 #include "qemu-option.h"
 #include "qemu_socket.h"
 
+#define TYPE_SOCKET_NET_CLIENT "socket-net-client"
+#define SOCKET_NET_CLIENT(obj) \
+    OBJECT_CHECK(NetSocketState, obj, TYPE_SOCKET_NET_CLIENT)
+
 typedef struct NetSocketState {
     NetClientState nc;
     int listen_fd;
@@ -42,6 +46,7 @@  typedef struct NetSocketState {
     unsigned int packet_len;
     uint8_t buf[4096];
     struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
+    bool is_dgram;
 } NetSocketState;
 
 static void net_socket_accept(void *opaque);
@@ -49,20 +54,17 @@  static void net_socket_accept(void *opaque);
 /* XXX: we consider we can send the whole packet without blocking */
 static ssize_t net_socket_receive(NetClientState *nc, const uint8_t *buf, size_t size)
 {
-    NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
-    uint32_t len;
-    len = htonl(size);
-
-    send_all(s->fd, (const uint8_t *)&len, sizeof(len));
-    return send_all(s->fd, buf, size);
-}
+    NetSocketState *s = SOCKET_NET_CLIENT(nc);
 
-static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+    if (s->is_dgram) {
+        return sendto(s->fd, (const void *)buf, size, 0,
+                      (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
+    } else {
+        uint32_t len = htonl(size);
 
-    return sendto(s->fd, (const void *)buf, size, 0,
-                  (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
+        send_all(s->fd, (const uint8_t *)&len, sizeof(len));
+        return send_all(s->fd, buf, size);
+    }
 }
 
 static void net_socket_send(void *opaque)
@@ -241,7 +243,7 @@  fail:
 
 static void net_socket_cleanup(NetClientState *nc)
 {
-    NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+    NetSocketState *s = SOCKET_NET_CLIENT(nc);
     if (s->fd != -1) {
         qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
         close(s->fd);
@@ -254,13 +256,6 @@  static void net_socket_cleanup(NetClientState *nc)
     }
 }
 
-static NetClientInfo net_dgram_socket_info = {
-    .type = NET_CLIENT_TYPE_SOCKET,
-    .size = sizeof(NetSocketState),
-    .receive = net_socket_receive_dgram,
-    .cleanup = net_socket_cleanup,
-};
-
 static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
                                                 const char *model,
                                                 const char *name,
@@ -303,17 +298,18 @@  static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
         }
     }
 
-    nc = qemu_new_net_client(&net_dgram_socket_info, peer, model, name);
+    nc = qemu_new_net_client(TYPE_SOCKET_NET_CLIENT, peer, model, name);
 
     snprintf(nc->info_str, sizeof(nc->info_str),
             "socket: fd=%d (%s mcast=%s:%d)",
             fd, is_connected ? "cloned" : "",
             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
 
-    s = DO_UPCAST(NetSocketState, nc, nc);
+    s = SOCKET_NET_CLIENT(nc);
 
     s->fd = fd;
     s->listen_fd = -1;
+    s->is_dgram = true;
 
     qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
 
@@ -335,13 +331,6 @@  static void net_socket_connect(void *opaque)
     qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
 }
 
-static NetClientInfo net_socket_info = {
-    .type = NET_CLIENT_TYPE_SOCKET,
-    .size = sizeof(NetSocketState),
-    .receive = net_socket_receive,
-    .cleanup = net_socket_cleanup,
-};
-
 static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
                                                  const char *model,
                                                  const char *name,
@@ -350,11 +339,11 @@  static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
     NetClientState *nc;
     NetSocketState *s;
 
-    nc = qemu_new_net_client(&net_socket_info, peer, model, name);
+    nc = qemu_new_net_client(TYPE_SOCKET_NET_CLIENT, peer, model, name);
 
     snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd);
 
-    s = DO_UPCAST(NetSocketState, nc, nc);
+    s = SOCKET_NET_CLIENT(nc);
 
     s->fd = fd;
     s->listen_fd = -1;
@@ -456,8 +445,8 @@  static int net_socket_listen_init(NetClientState *peer,
         return -1;
     }
 
-    nc = qemu_new_net_client(&net_socket_info, peer, model, name);
-    s = DO_UPCAST(NetSocketState, nc, nc);
+    nc = qemu_new_net_client(TYPE_SOCKET_NET_CLIENT, peer, model, name);
+    s = SOCKET_NET_CLIENT(nc);
     s->fd = -1;
     s->listen_fd = fd;
     s->nc.link_down = true;
@@ -546,6 +535,7 @@  static int net_socket_mcast_init(NetClientState *peer,
     if (!s)
         return -1;
 
+    s->is_dgram = true;
     s->dgram_dst = saddr;
 
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
@@ -598,6 +588,7 @@  static int net_socket_udp_init(NetClientState *peer,
         return -1;
     }
 
+    s->is_dgram = true;
     s->dgram_dst = raddr;
 
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
@@ -706,3 +697,26 @@  int net_init_socket(QemuOpts *opts, const char *name, NetClientState *peer)
     }
     return 0;
 }
+
+static void net_socket_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "socket";
+    ncc->receive = net_socket_receive;
+    ncc->cleanup = net_socket_cleanup;
+}
+
+static TypeInfo net_socket_info = {
+    .name = TYPE_SOCKET_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(NetSocketState),
+    .class_init = net_socket_class_init,
+};
+
+static void net_socket_register_types(void)
+{
+    type_register_static(&net_socket_info);
+}
+
+type_init(net_socket_register_types)
diff --git a/net/tap-win32.c b/net/tap-win32.c
index ebb36e8..7a3ba37 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -666,11 +666,23 @@  static void tap_win32_send(void *opaque)
     }
 }
 
-static NetClientInfo net_tap_win32_info = {
-    .type = NET_CLIENT_TYPE_TAP,
-    .size = sizeof(TAPState),
-    .receive = tap_receive,
-    .cleanup = tap_cleanup,
+#define TYPE_TAP_WIN32_NET_CLIENT "tap-win32-net-client"
+
+static void tap_win32_net_client_class_init(ObjectClass *klass,
+                                            void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "tap";
+    ncc->receive = tap_receive;
+    ncc->cleanup = tap_cleanup;
+}
+
+static TypeInfo tap_win32_net_client_info = {
+    .name = TYPE_TAP_WIN32_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(TAPState),
+    .class_init = tap_win32_net_client_class_init,
 };
 
 static int tap_win32_init(NetClientState *peer, const char *model,
@@ -749,3 +761,10 @@  struct vhost_net *tap_get_vhost_net(NetClientState *nc)
 {
     return NULL;
 }
+
+static void tap_win32_register_types(void)
+{
+    type_register_static(&tap_win32_net_client_info);
+}
+
+type_init(tap_win32_register_types)
diff --git a/net/tap.c b/net/tap.c
index d5bdfa0..86810e7 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -49,6 +49,10 @@ 
  */
 #define TAP_BUFSIZE (4096 + 65536)
 
+#define TYPE_TAP_NET_CLIENT "tap-net-client"
+#define TAP_NET_CLIENT(obj) \
+    OBJECT_CHECK(TAPState, obj, TYPE_TAP_NET_CLIENT)
+
 typedef struct TAPState {
     NetClientState nc;
     int fd;
@@ -118,7 +122,7 @@  static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt
 static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov,
                                int iovcnt)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
     const struct iovec *iovp = iov;
     struct iovec iov_copy[iovcnt + 1];
     struct virtio_net_hdr_mrg_rxbuf hdr = { };
@@ -136,7 +140,7 @@  static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov,
 
 static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t size)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
     struct iovec iov[2];
     int iovcnt = 0;
     struct virtio_net_hdr_mrg_rxbuf hdr = { };
@@ -156,7 +160,7 @@  static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t si
 
 static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
     struct iovec iov[1];
 
     if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
@@ -185,7 +189,7 @@  ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
 
 static void tap_send_completed(NetClientState *nc, ssize_t len)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
     tap_read_poll(s, 1);
 }
 
@@ -216,36 +220,29 @@  static void tap_send(void *opaque)
 
 int tap_has_ufo(NetClientState *nc)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
-
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    TAPState *s = TAP_NET_CLIENT(nc);
 
     return s->has_ufo;
 }
 
 int tap_has_vnet_hdr(NetClientState *nc)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
-
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    TAPState *s = TAP_NET_CLIENT(nc);
 
     return !!s->host_vnet_hdr_len;
 }
 
 int tap_has_vnet_hdr_len(NetClientState *nc, int len)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
-
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    TAPState *s = TAP_NET_CLIENT(nc);
 
     return tap_probe_vnet_hdr_len(s->fd, len);
 }
 
 void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
 
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
            len == sizeof(struct virtio_net_hdr));
 
@@ -255,11 +252,10 @@  void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 
 void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
 
     using_vnet_hdr = using_vnet_hdr != 0;
 
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
     assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
 
     s->using_vnet_hdr = using_vnet_hdr;
@@ -268,7 +264,7 @@  void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
 void tap_set_offload(NetClientState *nc, int csum, int tso4,
                      int tso6, int ecn, int ufo)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
     if (s->fd < 0) {
         return;
     }
@@ -278,7 +274,7 @@  void tap_set_offload(NetClientState *nc, int csum, int tso4,
 
 static void tap_cleanup(NetClientState *nc)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
 
     if (s->vhost_net) {
         vhost_net_cleanup(s->vhost_net);
@@ -298,33 +294,41 @@  static void tap_cleanup(NetClientState *nc)
 
 static void tap_poll(NetClientState *nc, bool enable)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    TAPState *s = TAP_NET_CLIENT(nc);
     tap_read_poll(s, enable);
     tap_write_poll(s, enable);
 }
 
 bool net_is_tap_client(NetClientState *nc)
 {
-    return nc->info->type == NET_CLIENT_TYPE_TAP;
+    return object_dynamic_cast(OBJECT(nc), TYPE_TAP_NET_CLIENT);
 }
 
 int tap_get_fd(NetClientState *nc)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    TAPState *s = TAP_NET_CLIENT(nc);
     return s->fd;
 }
 
 /* fd support */
 
-static NetClientInfo net_tap_info = {
-    .type = NET_CLIENT_TYPE_TAP,
-    .size = sizeof(TAPState),
-    .receive = tap_receive,
-    .receive_raw = tap_receive_raw,
-    .receive_iov = tap_receive_iov,
-    .poll = tap_poll,
-    .cleanup = tap_cleanup,
+static void tap_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "tap";
+    ncc->receive = tap_receive;
+    ncc->receive_raw = tap_receive_raw;
+    ncc->receive_iov = tap_receive_iov;
+    ncc->poll = tap_poll;
+    ncc->cleanup = tap_cleanup;
+}
+
+static TypeInfo tap_net_client_info = {
+    .name = TYPE_TAP_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(TAPState),
+    .class_init = tap_net_client_class_init,
 };
 
 static TAPState *net_tap_fd_init(NetClientState *peer,
@@ -336,9 +340,9 @@  static TAPState *net_tap_fd_init(NetClientState *peer,
     NetClientState *nc;
     TAPState *s;
 
-    nc = qemu_new_net_client(&net_tap_info, peer, model, name);
+    nc = qemu_new_net_client(TYPE_TAP_NET_CLIENT, peer, model, name);
 
-    s = DO_UPCAST(TAPState, nc, nc);
+    s = TAP_NET_CLIENT(nc);
 
     s->fd = fd;
     s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
@@ -715,7 +719,13 @@  int net_init_tap(QemuOpts *opts, const char *name, NetClientState *peer)
 
 VHostNetState *tap_get_vhost_net(NetClientState *nc)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    TAPState *s = TAP_NET_CLIENT(nc);
     return s->vhost_net;
 }
+
+static void tap_register_types(void)
+{
+    type_register_static(&tap_net_client_info);
+}
+
+type_init(tap_register_types)
diff --git a/net/vde.c b/net/vde.c
index 48fb010..fbda843 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -68,11 +68,22 @@  static void vde_cleanup(NetClientState *nc)
     vde_close(s->vde);
 }
 
-static NetClientInfo net_vde_info = {
-    .type = NET_CLIENT_TYPE_VDE,
-    .size = sizeof(VDEState),
-    .receive = vde_receive,
-    .cleanup = vde_cleanup,
+#define TYPE_VDE_NET_CLIENT "vde-net-client"
+
+static void vde_net_client_class_init(ObjectClass *klass, void *class_data)
+{
+    NetClientClass *ncc = NET_CLIENT_CLASS(klass);
+
+    ncc->type_str = "vde";
+    ncc->receive = vde_receive;
+    ncc->cleanup = vde_cleanup;
+}
+
+static TypeInfo vde_net_client_info = {
+    .name = TYPE_VDE_NET_CLIENT,
+    .parent = TYPE_NET_CLIENT,
+    .instance_size = sizeof(VDEState),
+    .class_init = vde_net_client_class_init,
 };
 
 static int net_vde_init(NetClientState *peer, const char *model,
@@ -128,3 +139,10 @@  int net_init_vde(QemuOpts *opts, const char *name, NetClientState *peer)
 
     return 0;
 }
+
+static void vde_register_types(void)
+{
+    type_register_static(&vde_net_client_info);
+}
+
+type_init(vde_register_types)