Patchwork [v3,3/3] net: convert NetClientState to QOM

login
register
mail settings
Submitter Stefan Hajnoczi
Date Oct. 24, 2012, 7:08 a.m.
Message ID <1351062480-4828-4-git-send-email-stefanha@redhat.com>
Download mbox | patch
Permalink /patch/193707/
State New
Headers show

Comments

Stefan Hajnoczi - Oct. 24, 2012, 7:08 a.m.
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.

3. NICState becomes a QOM object with NetClientState as its base class.
   In other words, a NIC is-a net client.

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 dropped.  For now users have been replaced
with object_dynamic_cast() to test the type of an object.  The cleaner solution
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.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 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            |  28 +++++++----
 hw/smc91c111.c          |  26 ++++++++---
 hw/spapr_llan.c         |  24 +++++++---
 hw/stellaris_enet.c     |  26 ++++++++---
 hw/usb/dev-network.c    |  25 +++++++---
 hw/vhost_net.c          |   5 +-
 hw/virtio-net.c         |  42 ++++++++++++-----
 hw/xen_nic.c            |  29 +++++++++---
 hw/xgmac.c              |  26 ++++++++---
 hw/xilinx_axienet.c     |  26 ++++++++---
 hw/xilinx_ethlite.c     |  26 ++++++++---
 net.c                   | 122 ++++++++++++++++++++++++++++++++----------------
 net.h                   |  33 +++++++++----
 net/dump.c              |  30 +++++++++---
 net/hub.c               |  64 +++++++++++++++----------
 net/slirp.c             |  29 +++++++++---
 net/socket.c            |  74 +++++++++++++++++++----------
 net/tap-win32.c         |  27 +++++++++--
 net/tap.c               |  77 +++++++++++++++---------------
 net/tap.h               |   4 ++
 net/vde.c               |  28 +++++++++--
 39 files changed, 824 insertions(+), 382 deletions(-)

Patch

diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
index 967f625..0b633e8 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_OPTIONS_KIND_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 4fa6ecc..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_OPTIONS_KIND_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 63fee10..1d5f2f3 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)
 {
@@ -447,7 +450,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);
     }
@@ -1220,13 +1223,23 @@  pci_e1000_uninit(PCIDevice *dev)
     qemu_del_net_client(&d->nic->nc);
 }
 
-static NetClientInfo net_e1000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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)
@@ -1261,7 +1274,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);
@@ -1312,6 +1325,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 5b23116..9b82a70 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -1852,12 +1852,23 @@  static void pci_nic_uninit(PCIDevice *pci_dev)
     qemu_del_net_client(&s->nic->nc);
 }
 
-static NetClientInfo net_eepro100_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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)
@@ -1892,7 +1903,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);
@@ -2110,6 +2121,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 b124f5b..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_OPTIONS_KIND_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 ceaf96f..162ad00 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -1311,13 +1311,23 @@  static void lan9118_cleanup(NetClientState *nc)
     s->nic = NULL;
 }
 
-static NetClientInfo net_lan9118_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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)
@@ -1333,7 +1343,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;
@@ -1378,6 +1388,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 9b98bb8..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_OPTIONS_KIND_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 2fec5bc..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_OPTIONS_KIND_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 b483a02..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_OPTIONS_KIND_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 28063b1..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_OPTIONS_KIND_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 159d3c3..963a468 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_OPTIONS_KIND_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);
@@ -1692,6 +1703,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 69982a9..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_OPTIONS_KIND_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 15605c4..1b7d1d8 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_OPTIONS_KIND_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);
 
@@ -783,6 +791,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 8c15969..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_OPTIONS_KIND_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 48fd447..808a713 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 void pci_pcnet_uninit(PCIDevice *dev)
 {
     PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev);
@@ -282,15 +275,6 @@  static void pci_pcnet_uninit(PCIDevice *dev)
     qemu_del_net_client(&d->state.nic->nc);
 }
 
-static NetClientInfo net_pci_pcnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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);
@@ -329,7 +313,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 6b28fea..1fdbf71 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3468,13 +3468,24 @@  static void rtl8139_set_link_status(NetClientState *nc)
     rtl8139_update_irq(s);
 }
 
-static NetClientInfo net_rtl8139_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = rtl8139_can_receive,
-    .receive = rtl8139_receive,
-    .cleanup = rtl8139_cleanup,
-    .link_status_changed = rtl8139_set_link_status,
+#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;
+    ncc->link_status_changed = rtl8139_set_link_status;
+}
+
+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)
@@ -3506,7 +3517,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);
 
@@ -3555,6 +3566,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 d6ef302..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_OPTIONS_KIND_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 bd3f131..bacec94 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_OPTIONS_KIND_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);
 
@@ -518,6 +529,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 bc97280..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_OPTIONS_KIND_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 e4a4359..6fa27a9 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1329,12 +1329,22 @@  static void usb_net_handle_destroy(USBDevice *dev)
     qemu_del_net_client(&s->nic->nc);
 }
 
-static NetClientInfo net_usbnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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)
@@ -1354,7 +1364,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),
@@ -1439,6 +1449,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/vhost_net.c b/hw/vhost_net.c
index 6737600..52ede17 100644
--- a/hw/vhost_net.c
+++ b/hw/vhost_net.c
@@ -82,10 +82,9 @@  void vhost_net_ack_features(struct vhost_net *net, unsigned features)
 
 static int vhost_net_get_fd(NetClientState *backend)
 {
-    switch (backend->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    if (object_dynamic_cast(OBJECT(backend), TYPE_TAP_NET_CLIENT)) {
         return tap_get_fd(backend);
-    default:
+    } else {
         fprintf(stderr, "vhost-net requires tap backend\n");
         return -EBADFD;
     }
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 50ba728..067ee74 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -108,7 +108,7 @@  static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
     if (!n->nic->nc.peer) {
         return;
     }
-    if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (object_dynamic_cast(OBJECT(n->nic->nc.peer), TYPE_TAP_NET_CLIENT)) {
         return;
     }
 
@@ -205,7 +205,7 @@  static int peer_has_vnet_hdr(VirtIONet *n)
     if (!n->nic->nc.peer)
         return 0;
 
-    if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP)
+    if (object_dynamic_cast(OBJECT(n->nic->nc.peer), TYPE_TAP_NET_CLIENT))
         return 0;
 
     n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer);
@@ -249,7 +249,7 @@  static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
     }
 
     if (!n->nic->nc.peer ||
-        n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+        object_dynamic_cast(OBJECT(n->nic->nc.peer), TYPE_TAP_NET_CLIENT)) {
         return features;
     }
     if (!tap_get_vhost_net(n->nic->nc.peer)) {
@@ -288,7 +288,7 @@  static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
                         (features >> VIRTIO_NET_F_GUEST_UFO)  & 1);
     }
     if (!n->nic->nc.peer ||
-        n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+        object_dynamic_cast(OBJECT(n->nic->nc.peer), TYPE_TAP_NET_CLIENT)) {
         return;
     }
     if (!tap_get_vhost_net(n->nic->nc.peer)) {
@@ -990,13 +990,23 @@  static void virtio_net_cleanup(NetClientState *nc)
     n->nic = NULL;
 }
 
-static NetClientInfo net_virtio_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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,
@@ -1037,7 +1047,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);
 
@@ -1083,3 +1094,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 cf7d559..0512b66 100644
--- a/hw/xen_nic.c
+++ b/hw/xen_nic.c
@@ -300,11 +300,21 @@  static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
 
 /* ------------------------------------------------------------- */
 
-static NetClientInfo net_xen_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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)
@@ -327,7 +337,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),
@@ -437,3 +447,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 a91ef60..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_OPTIONS_KIND_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 eec155d..7cd3f9b 100644
--- a/hw/xilinx_axienet.c
+++ b/hw/xilinx_axienet.c
@@ -830,12 +830,23 @@  axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr)
     enet_update_irq(s);
 }
 
-static NetClientInfo net_xilinx_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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)
@@ -848,7 +859,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);
 
@@ -905,6 +916,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 56ca620..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_OPTIONS_KIND_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 67d2616..edab653 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_OPTIONS_KIND_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_OPTIONS_KIND_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_OPTIONS_KIND_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_OPTIONS_KIND_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) {
@@ -410,12 +415,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, 0, buffer, sizeof(buffer));
 
-    return nc->info->receive(nc, buffer, offset);
+    return klass->receive(nc, buffer, offset);
 }
 
 ssize_t qemu_deliver_packet_iov(NetClientState *sender,
@@ -425,6 +431,7 @@  ssize_t qemu_deliver_packet_iov(NetClientState *sender,
                                 void *opaque)
 {
     NetClientState *nc = opaque;
+    NetClientClass *klass = NET_CLIENT_GET_CLASS(nc);
     int ret;
 
     if (nc->link_down) {
@@ -435,8 +442,8 @@  ssize_t qemu_deliver_packet_iov(NetClientState *sender,
         return 0;
     }
 
-    if (nc->info->receive_iov) {
-        ret = nc->info->receive_iov(nc, iov, iovcnt);
+    if (klass->receive_iov) {
+        ret = klass->receive_iov(nc, iov, iovcnt);
     } else {
         ret = nc_sendv_compat(nc, iov, iovcnt);
     }
@@ -473,8 +480,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_GET_CLASS(nc);
+
+    if (klass->poll) {
+        klass->poll(nc, enable);
     }
 }
 
@@ -483,8 +492,9 @@  NetClientState *qemu_find_netdev(const char *id)
     NetClientState *nc;
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
+        if (object_dynamic_cast(OBJECT(nc), TYPE_NIC_NET_CLIENT)) {
             continue;
+        }
         if (!strcmp(nc->name, id)) {
             return nc;
         }
@@ -852,30 +862,30 @@  void qmp_netdev_del(const char *id, Error **errp)
 
 void print_net_client(Monitor *mon, NetClientState *nc)
 {
+    NetClientClass *klass = NET_CLIENT_GET_CLASS(nc);
+
     monitor_printf(mon, "%s: type=%s,%s\n", nc->name,
-                   NetClientOptionsKind_lookup[nc->info->type], nc->info_str);
+                   klass->type_str, nc->info_str);
 }
 
 void do_info_network(Monitor *mon)
 {
     NetClientState *nc, *peer;
-    NetClientOptionsKind type;
 
     net_hub_info(mon);
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
         peer = nc->peer;
-        type = nc->info->type;
 
         /* Skip if already printed in hub info */
         if (net_hub_id_for_client(nc, NULL) == 0) {
             continue;
         }
 
-        if (!peer || type == NET_CLIENT_OPTIONS_KIND_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_OPTIONS_KIND_NIC) {
+        if (peer && object_dynamic_cast(OBJECT(nc), TYPE_NIC_NET_CLIENT)) {
             monitor_printf(mon, " \\ ");
             print_net_client(mon, peer);
         }
@@ -944,8 +954,9 @@  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_OPTIONS_KIND_NIC ?
-                    "nic" : "netdev", nc->name);
+                    object_dynamic_cast(OBJECT(nc),
+                        TYPE_NIC_NET_CLIENT) ? "nic" : "netdev",
+                    nc->name);
         }
     }
 
@@ -1056,3 +1067,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 763f926..66cef37 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"
@@ -29,6 +30,14 @@  typedef struct NICConf {
 
 /* Net clients */
 
+#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);
@@ -36,9 +45,10 @@  typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int);
 typedef void (NetCleanup) (NetClientState *);
 typedef void (LinkStatusChanged)(NetClientState *);
 
-typedef struct NetClientInfo {
-    NetClientOptionsKind 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;
@@ -46,10 +56,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;
@@ -58,7 +69,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;
@@ -68,11 +83,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 e0a5d74..cd28d02 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_OPTIONS_KIND_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);
@@ -183,3 +194,10 @@  int net_init_dump(const NetClientOptions *opts, const char *name,
 
     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 be41301..218d7b0 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -129,13 +129,27 @@  static void net_hub_port_cleanup(NetClientState *nc)
     QLIST_REMOVE(port, next);
 }
 
-static NetClientInfo net_hub_port_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
-    .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, const char *name)
@@ -151,8 +165,8 @@  static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
         name = default_name;
     }
 
-    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;
 
@@ -262,12 +276,13 @@  int net_hub_id_for_client(NetClientState *nc, int *id)
 {
     NetHubPort *port;
 
-    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
-        port = DO_UPCAST(NetHubPort, nc, nc);
-    } else if (nc->peer != NULL && nc->peer->info->type ==
-            NET_CLIENT_OPTIONS_KIND_HUBPORT) {
-        port = DO_UPCAST(NetHubPort, nc, nc->peer);
-    } else {
+    port = (NetHubPort *)object_dynamic_cast(OBJECT(nc),
+                                             TYPE_HUB_PORT_NET_CLIENT);
+    if (!port && nc->peer) {
+        port = (NetHubPort *)object_dynamic_cast(OBJECT(nc->peer),
+                                                 TYPE_HUB_PORT_NET_CLIENT);
+    }
+    if (!port) {
         return -ENOENT;
     }
 
@@ -314,18 +329,10 @@  void net_hub_check_clients(void)
                 continue;
             }
 
-            switch (peer->info->type) {
-            case NET_CLIENT_OPTIONS_KIND_NIC:
+            if (object_dynamic_cast(OBJECT(peer), TYPE_NIC_NET_CLIENT)) {
                 has_nic = 1;
-                break;
-            case NET_CLIENT_OPTIONS_KIND_USER:
-            case NET_CLIENT_OPTIONS_KIND_TAP:
-            case NET_CLIENT_OPTIONS_KIND_SOCKET:
-            case NET_CLIENT_OPTIONS_KIND_VDE:
+            } else {
                 has_host_dev = 1;
-                break;
-            default:
-                break;
             }
         }
         if (has_host_dev && !has_nic) {
@@ -338,3 +345,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 bf86a44..ec0a6c5 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -123,11 +123,22 @@  static void net_slirp_cleanup(NetClientState *nc)
     QTAILQ_REMOVE(&slirp_stacks, s, entry);
 }
 
-static NetClientInfo net_slirp_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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,
@@ -233,7 +244,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),
@@ -761,3 +772,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 b75d567..6d40419 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -33,6 +33,10 @@ 
 #include "qemu_socket.h"
 #include "iov.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;
@@ -44,6 +48,8 @@  typedef struct NetSocketState {
     uint8_t buf[4096];
     struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
     IOHandler *send_fn;           /* differs between SOCK_STREAM/SOCK_DGRAM */
+    ssize_t (*receive_fn)(struct NetSocketState *s, const uint8_t *buf,
+                          size_t size);
     bool read_poll;               /* waiting to receive data? */
     bool write_poll;              /* waiting to transmit data? */
 } NetSocketState;
@@ -89,9 +95,9 @@  static void net_socket_writable(void *opaque)
     qemu_flush_queued_packets(&s->nc);
 }
 
-static ssize_t net_socket_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t net_socket_receive_stream(NetSocketState *s, const uint8_t *buf,
+                                         size_t size)
 {
-    NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
     uint32_t len = htonl(size);
     struct iovec iov[] = {
         {
@@ -124,9 +130,9 @@  static ssize_t net_socket_receive(NetClientState *nc, const uint8_t *buf, size_t
     return size;
 }
 
-static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t net_socket_receive_dgram(NetSocketState *s, const uint8_t *buf,
+                                        size_t size)
 {
-    NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
     ssize_t ret;
 
     do {
@@ -142,6 +148,13 @@  static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf,
     return ret;
 }
 
+static ssize_t net_socket_receive(NetClientState *nc, const uint8_t *buf,
+                                  size_t size)
+{
+    NetSocketState *s = SOCKET_NET_CLIENT(nc);
+    return s->receive_fn(s, buf, size);
+}
+
 static void net_socket_send(void *opaque)
 {
     NetSocketState *s = opaque;
@@ -320,7 +333,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) {
         net_socket_read_poll(s, false);
         net_socket_write_poll(s, false);
@@ -334,13 +347,6 @@  static void net_socket_cleanup(NetClientState *nc)
     }
 }
 
-static NetClientInfo net_dgram_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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,
@@ -383,18 +389,19 @@  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->send_fn = net_socket_send_dgram;
+    s->receive_fn = net_socket_receive_dgram;
     net_socket_read_poll(s, true);
 
     /* mcast: save bound address as dst */
@@ -413,16 +420,10 @@  static void net_socket_connect(void *opaque)
 {
     NetSocketState *s = opaque;
     s->send_fn = net_socket_send;
+    s->receive_fn = net_socket_receive_stream;
     net_socket_read_poll(s, true);
 }
 
-static NetClientInfo net_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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,
@@ -431,11 +432,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;
@@ -537,8 +538,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;
@@ -753,3 +754,26 @@  int net_init_socket(const NetClientOptions *opts, const char *name,
     }
     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 22dad3f..35d9b32 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -667,11 +667,21 @@  static void tap_win32_send(void *opaque)
     }
 }
 
-static NetClientInfo net_tap_win32_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_TAP,
-    .size = sizeof(TAPState),
-    .receive = tap_receive,
-    .cleanup = tap_cleanup,
+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_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,
@@ -752,3 +762,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 df89caa..7102b91 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -119,7 +119,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 = { };
@@ -137,7 +137,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 = { };
@@ -157,7 +157,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) {
@@ -186,7 +186,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);
 }
 
@@ -217,36 +217,26 @@  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_OPTIONS_KIND_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_OPTIONS_KIND_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_OPTIONS_KIND_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_OPTIONS_KIND_TAP);
     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
            len == sizeof(struct virtio_net_hdr));
 
@@ -256,11 +246,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_OPTIONS_KIND_TAP);
     assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
 
     s->using_vnet_hdr = using_vnet_hdr;
@@ -269,7 +258,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;
     }
@@ -279,7 +268,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);
@@ -299,28 +288,36 @@  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);
 }
 
 int tap_get_fd(NetClientState *nc)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    TAPState *s = TAP_NET_CLIENT(nc);
     return s->fd;
 }
 
 /* fd support */
 
-static NetClientInfo net_tap_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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,
@@ -332,9 +329,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;
@@ -711,7 +708,13 @@  int net_init_tap(const NetClientOptions *opts, const char *name,
 
 VHostNetState *tap_get_vhost_net(NetClientState *nc)
 {
-    TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_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/tap.h b/net/tap.h
index d44d83a..d014470 100644
--- a/net/tap.h
+++ b/net/tap.h
@@ -32,6 +32,10 @@ 
 #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
 #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
 
+#define TYPE_TAP_NET_CLIENT "tap-net-client"
+#define TAP_NET_CLIENT(obj) \
+    OBJECT_CHECK(TAPState, obj, TYPE_TAP_NET_CLIENT)
+
 int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required);
 
 ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen);
diff --git a/net/vde.c b/net/vde.c
index 275bda9..dee9652 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -67,11 +67,22 @@  static void vde_cleanup(NetClientState *nc)
     vde_close(s->vde);
 }
 
-static NetClientInfo net_vde_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_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,
@@ -125,3 +136,10 @@  int net_init_vde(const NetClientOptions *opts, const char *name,
 
     return 0;
 }
+
+static void vde_register_types(void)
+{
+    type_register_static(&vde_net_client_info);
+}
+
+type_init(vde_register_types)