summary refs log tree commit diff
path: root/src/platform/wifi
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-06-04 00:07:45 +0200
committerMichael Biebl <biebl@debian.org>2018-06-04 00:07:45 +0200
commit04bc9e1cd3544445d883ad29ea108c1645c8e7b7 (patch)
treed10c354b1b980ca8a7b9e48ec9019e8ed88bde2b /src/platform/wifi
parentee9c73a923909e23a649407be77e25235d769e25 (diff)
New upstream version 1.11.4 upstream/1.11.4
Diffstat (limited to 'src/platform/wifi')
-rw-r--r--src/platform/wifi/wifi-utils-nl80211.c380
-rw-r--r--src/platform/wifi/wifi-utils-private.h14
-rw-r--r--src/platform/wifi/wifi-utils-wext.c42
-rw-r--r--src/platform/wifi/wifi-utils.c61
-rw-r--r--src/platform/wifi/wifi-utils.h3
5 files changed, 141 insertions, 359 deletions
diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c
index a5f25b02..767e23a9 100644
--- a/src/platform/wifi/wifi-utils-nl80211.c
+++ b/src/platform/wifi/wifi-utils-nl80211.c
@@ -22,17 +22,17 @@
 
 #include "nm-default.h"
 
+#include "wifi-utils-nl80211.h"
+
 #include <errno.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
-#include <netlink/netlink.h>
-#include <netlink/msg.h>
 #include <linux/nl80211.h>
 
+#include "platform/nm-netlink.h"
 #include "wifi-utils-private.h"
-#include "wifi-utils-nl80211.h"
 #include "platform/nm-platform.h"
 #include "platform/nm-platform-utils.h"
 #include "nm-utils.h"
@@ -46,223 +46,14 @@
 		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
 	} G_STMT_END
 
-/*****************************************************************************/
-
-static int
-_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
-               const struct nla_policy *policy)
-{
-	return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy);
-}
-#define nla_parse(...) _nl_nla_parse(__VA_ARGS__)
-
-static int
-_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla,
-                      const struct nla_policy *policy)
-{
-	return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy);
-}
-#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__)
-
-/*****************************************************************************
- * Copied from libnl3/genl:
- *****************************************************************************/
-
-static void *
-genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family,
-             int hdrlen, int flags, uint8_t cmd, uint8_t version)
-{
-	struct nlmsghdr *nlh;
-	struct genlmsghdr hdr = {
-		.cmd = cmd,
-		.version = version,
-	};
-
-	nlh = nlmsg_put (msg, port, seq, family, GENL_HDRLEN + hdrlen, flags);
-	if (nlh == NULL)
-		return NULL;
-
-	memcpy (nlmsg_data (nlh), &hdr, sizeof (hdr));
-
-	return (char *) nlmsg_data (nlh) + GENL_HDRLEN;
-}
-
-static void *
-genlmsg_data (const struct genlmsghdr *gnlh)
-{
-	return ((unsigned char *) gnlh + GENL_HDRLEN);
-}
-
-static void *
-genlmsg_user_hdr (const struct genlmsghdr *gnlh)
-{
-	return genlmsg_data (gnlh);
-}
-
-static struct genlmsghdr *
-genlmsg_hdr (struct nlmsghdr *nlh)
-{
-	return nlmsg_data (nlh);
-}
-
-static void *
-genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen)
-{
-	return (char *) genlmsg_user_hdr (gnlh) + NLMSG_ALIGN (hdrlen);
-}
-
-static struct nlattr *
-genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen)
-{
-	return genlmsg_user_data (gnlh, hdrlen);
-}
-
-static int
-genlmsg_len (const struct genlmsghdr *gnlh)
-{
-	const struct nlmsghdr *nlh;
-
-	nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN);
-	return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN);
-}
-
-static int
-genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen)
-{
-	return genlmsg_len (gnlh) - NLMSG_ALIGN (hdrlen);
-}
-
-static int
-genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen)
-{
-	struct genlmsghdr *ghdr;
-
-	if (!nlmsg_valid_hdr (nlh, GENL_HDRLEN))
-		return 0;
-
-	ghdr = nlmsg_data (nlh);
-	if (genlmsg_len (ghdr) < NLMSG_ALIGN (hdrlen))
-		return 0;
-
-	return 1;
-}
-
-static int
-genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-               int maxtype, const struct nla_policy *policy)
-{
-	struct genlmsghdr *ghdr;
-
-	if (!genlmsg_valid_hdr (nlh, hdrlen))
-		return -NLE_MSG_TOOSHORT;
-
-	ghdr = nlmsg_data (nlh);
-	return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen),
-	                  genlmsg_attrlen (ghdr, hdrlen), policy);
-}
-
-/*****************************************************************************
- * Reimplementation of libnl3/genl functions:
- *****************************************************************************/
-
-static int
-probe_response (struct nl_msg *msg, void *arg)
-{
-	static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
-		[CTRL_ATTR_FAMILY_ID]    = { .type = NLA_U16 },
-		[CTRL_ATTR_FAMILY_NAME]  = { .type = NLA_STRING,
-		                            .maxlen = GENL_NAMSIZ },
-		[CTRL_ATTR_VERSION]      = { .type = NLA_U32 },
-		[CTRL_ATTR_HDRSIZE]      = { .type = NLA_U32 },
-		[CTRL_ATTR_MAXATTR]      = { .type = NLA_U32 },
-		[CTRL_ATTR_OPS]          = { .type = NLA_NESTED },
-		[CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
-	};
-	struct nlattr *tb[CTRL_ATTR_MAX+1];
-	struct nlmsghdr *nlh = nlmsg_hdr (msg);
-	gint32 *response_data = arg;
-
-	if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy))
-		return NL_SKIP;
-
-	if (tb[CTRL_ATTR_FAMILY_ID])
-		*response_data = nla_get_u16 (tb[CTRL_ATTR_FAMILY_ID]);
-
-	return NL_STOP;
-}
-
-static int
-genl_ctrl_resolve (struct nl_sock *sk, const char *name)
-{
-	struct nl_msg *msg;
-	struct nl_cb *cb, *orig;
-	int rc;
-	int result = -NLE_OBJ_NOTFOUND;
-	gint32 response_data = -1;
-
-	if (!(orig = nl_socket_get_cb (sk)))
-		goto out;
-
-	cb = nl_cb_clone (orig);
-	nl_cb_put (orig);
-	if (!cb)
-		goto out;
-
-	msg = nlmsg_alloc ();
-	if (!msg)
-		goto out_cb_free;
-
-	if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL,
-	                  0, 0, CTRL_CMD_GETFAMILY, 1))
-		goto out_msg_free;
-
-	if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0)
-		goto out_msg_free;
-
-	rc = nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, &response_data);
-	if (rc < 0)
-		goto out_msg_free;
-
-	rc = nl_send_auto_complete (sk, msg);
-	if (rc < 0)
-		goto out_msg_free;
-
-	rc = nl_recvmsgs (sk, cb);
-	if (rc < 0)
-		goto out_msg_free;
-
-	/* If search was successful, request may be ACKed after data */
-	rc = nl_wait_for_ack (sk);
-	if (rc < 0)
-		goto out_msg_free;
-
-	if (response_data > 0)
-		result = response_data;
-
-out_msg_free:
-	nlmsg_free (msg);
-out_cb_free:
-	nl_cb_put (cb);
-out:
-	if (result >= 0)
-		_LOGD (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result);
-	else
-		_LOGE (LOGD_WIFI, "genl_ctrl_resolve: failed resolve \"%s\"", name);
-	return result;
-}
-
-/*****************************************************************************
- * </libn-genl-3>
- *****************************************************************************/
-
 typedef struct {
 	WifiData parent;
 	struct nl_sock *nl_sock;
-	int id;
-	struct nl_cb *nl_cb;
 	guint32 *freqs;
+	int id;
 	int num_freqs;
 	int phy;
+	bool can_wowlan:1;
 } WifiDataNl80211;
 
 static int
@@ -292,19 +83,16 @@ error_handler (struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
 static struct nl_msg *
 _nl80211_alloc_msg (int id, int ifindex, int phy, guint32 cmd, guint32 flags)
 {
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 
 	msg = nlmsg_alloc ();
-	if (msg) {
-		genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0);
-		NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex);
-		if (phy != -1)
-			NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy);
-	}
-	return msg;
+	genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0);
+	NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex);
+	if (phy != -1)
+		NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy);
+	return g_steal_pointer (&msg);
 
- nla_put_failure:
-	nlmsg_free (msg);
+nla_put_failure:
 	return NULL;
 }
 
@@ -314,42 +102,37 @@ nl80211_alloc_msg (WifiDataNl80211 *nl80211, guint32 cmd, guint32 flags)
 	return _nl80211_alloc_msg (nl80211->id, nl80211->parent.ifindex, nl80211->phy, cmd, flags);
 }
 
-/* NOTE: this function consumes 'msg' */
 static int
 _nl80211_send_and_recv (struct nl_sock *nl_sock,
-                        struct nl_cb *nl_cb,
                         struct nl_msg *msg,
                         int (*valid_handler) (struct nl_msg *, void *),
                         void *valid_data)
 {
-	struct nl_cb *cb;
-	int err, done;
+	int err;
+	int done = 0;
+	const struct nl_cb cb = {
+		.err_cb     = error_handler,
+		.err_arg    = &done,
+		.finish_cb  = finish_handler,
+		.finish_arg = &done,
+		.ack_cb     = ack_handler,
+		.ack_arg    = &done,
+		.valid_cb   = valid_handler,
+		.valid_arg  = valid_data,
+	};
 
 	g_return_val_if_fail (msg != NULL, -ENOMEM);
 
-	cb = nl_cb_clone (nl_cb);
-	if (!cb) {
-		err = -ENOMEM;
-		goto out;
-	}
-
-	err = nl_send_auto_complete (nl_sock, msg);
+	err = nl_send_auto (nl_sock, msg);
 	if (err < 0)
-		goto out;
-
-	done = 0;
-	nl_cb_err (cb, NL_CB_CUSTOM, error_handler, &done);
-	nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done);
-	nl_cb_set (cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done);
-	if (valid_handler)
-		nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data);
+		return err;
 
 	/* Loop until one of our NL callbacks says we're done; on success
 	 * done will be 1, on error it will be < 0.
 	 */
 	while (!done) {
-		err = nl_recvmsgs (nl_sock, cb);
-		if (err && err != -NLE_AGAIN) {
+		err = nl_recvmsgs (nl_sock, &cb);
+		if (err < 0 && err != -EAGAIN) {
 			/* Kernel scan list can change while we are dumping it, as new scan
 			 * results from H/W can arrive. BSS info is assured to be consistent
 			 * and we don't need consistent view of whole scan list. Hence do
@@ -364,12 +147,9 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock,
 			break;
 		}
 	}
-	if (err == 0 && done < 0)
-		err = done;
 
- out:
-	nl_cb_put (cb);
-	nlmsg_free (msg);
+	if (err >= 0 && done < 0)
+		err = done;
 	return err;
 }
 
@@ -379,7 +159,7 @@ nl80211_send_and_recv (WifiDataNl80211 *nl80211,
                        int (*valid_handler) (struct nl_msg *, void *),
                        void *valid_data)
 {
-	return _nl80211_send_and_recv (nl80211->nl_sock, nl80211->nl_cb, msg,
+	return _nl80211_send_and_recv (nl80211->nl_sock, msg,
 	                               valid_handler, valid_data);
 }
 
@@ -390,8 +170,6 @@ wifi_nl80211_deinit (WifiData *parent)
 
 	if (nl80211->nl_sock)
 		nl_socket_free (nl80211->nl_sock);
-	if (nl80211->nl_cb)
-		nl_cb_put (nl80211->nl_cb);
 	g_free (nl80211->freqs);
 }
 
@@ -435,12 +213,12 @@ wifi_nl80211_get_mode (WifiData *data)
 	struct nl80211_iface_info iface_info = {
 		.mode = NM_802_11_MODE_UNKNOWN,
 	};
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_INTERFACE, 0);
 
 	if (nl80211_send_and_recv (nl80211, msg, nl80211_iface_info_handler,
-				   &iface_info) < 0)
+	                           &iface_info) < 0)
 		return NM_802_11_MODE_UNKNOWN;
 
 	return iface_info.mode;
@@ -450,7 +228,7 @@ static gboolean
 wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	int err;
 
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_INTERFACE, 0);
@@ -470,10 +248,9 @@ wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode)
 	}
 
 	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
-	return err ? FALSE : TRUE;
+	return err >= 0;
 
- nla_put_failure:
-	nlmsg_free (msg);
+nla_put_failure:
 	return FALSE;
 }
 
@@ -481,17 +258,16 @@ static gboolean
 wifi_nl80211_set_powersave (WifiData *data, guint32 powersave)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	int err;
 
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_POWER_SAVE, 0);
 	NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE,
 	             powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED);
 	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
-	return err ? FALSE : TRUE;
+	return err >= 0;
 
 nla_put_failure:
-	nlmsg_free (msg);
 	return FALSE;
 }
 
@@ -518,7 +294,7 @@ struct nl80211_bss_info {
 	gboolean valid;
 };
 
-#define WLAN_EID_SSID	0
+#define WLAN_EID_SSID 0
 
 static void
 find_ssid (guint8 *ies, guint32 ies_len,
@@ -618,7 +394,7 @@ static void
 nl80211_get_bss_info (WifiDataNl80211 *nl80211,
                       struct nl80211_bss_info *bss_info)
 {
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 
 	memset (bss_info, 0, sizeof (*bss_info));
 
@@ -742,7 +518,7 @@ static void
 nl80211_get_ap_info (WifiDataNl80211 *nl80211,
                      struct nl80211_station_info *sta_info)
 {
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	struct nl80211_bss_info bss_info;
 
 	memset (sta_info, 0, sizeof (*sta_info));
@@ -764,8 +540,7 @@ nl80211_get_ap_info (WifiDataNl80211 *nl80211,
 
 	return;
 
- nla_put_failure:
-	nlmsg_free (msg);
+nla_put_failure:
 	return;
 }
 
@@ -790,35 +565,37 @@ wifi_nl80211_get_qual (WifiData *data)
 	return sta_info.signal;
 }
 
-#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS
 static gboolean
 wifi_nl80211_indicate_addressing_running (WifiData *data, gboolean running)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	int err;
 
 	msg = nl80211_alloc_msg (nl80211,
-	                         running ? NL80211_CMD_CRIT_PROTOCOL_START :
-	                         NL80211_CMD_CRIT_PROTOCOL_STOP,
+	                         running
+	                           ? 98 /* NL80211_CMD_CRIT_PROTOCOL_START */
+	                           : 99 /* NL80211_CMD_CRIT_PROTOCOL_STOP */,
 	                         0);
 	/* Despite the DHCP name, we're using this for any type of IP addressing,
 	 * DHCPv4, DHCPv6, and IPv6 SLAAC.
 	 */
-	NLA_PUT_U16 (msg, NL80211_ATTR_CRIT_PROT_ID, NL80211_CRIT_PROTO_DHCP);
+	NLA_PUT_U16 (msg,
+	             179 /* NL80211_ATTR_CRIT_PROT_ID */,
+	             1 /* NL80211_CRIT_PROTO_DHCP */);
 	if (running) {
 		/* Give DHCP 5 seconds to complete */
-		NLA_PUT_U16 (msg, NL80211_ATTR_MAX_CRIT_PROT_DURATION, 5000);
+		NLA_PUT_U16 (msg,
+		             180 /* NL80211_ATTR_MAX_CRIT_PROT_DURATION */,
+		             5000);
 	}
 
 	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
-	return err ? FALSE : TRUE;
+	return err >= 0;
 
 nla_put_failure:
-	nlmsg_free (msg);
 	return FALSE;
 }
-#endif
 
 struct nl80211_wowlan_info {
 	gboolean enabled;
@@ -847,12 +624,14 @@ static gboolean
 wifi_nl80211_get_wowlan (WifiData *data)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	struct nl80211_wowlan_info info;
 
+	if (!nl80211->can_wowlan)
+		return FALSE;
+
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0);
 	nl80211_send_and_recv (nl80211, msg, nl80211_wowlan_handler, &info);
-
 	return info.enabled;
 }
 
@@ -1062,8 +841,22 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 WifiData *
 wifi_nl80211_init (int ifindex)
 {
+	static const WifiDataClass klass = {
+		.struct_size = sizeof (WifiDataNl80211),
+		.get_mode = wifi_nl80211_get_mode,
+		.set_mode = wifi_nl80211_set_mode,
+		.set_powersave = wifi_nl80211_set_powersave,
+		.get_freq = wifi_nl80211_get_freq,
+		.find_freq = wifi_nl80211_find_freq,
+		.get_bssid = wifi_nl80211_get_bssid,
+		.get_rate = wifi_nl80211_get_rate,
+		.get_qual = wifi_nl80211_get_qual,
+		.get_wowlan = wifi_nl80211_get_wowlan,
+		.indicate_addressing_running = wifi_nl80211_indicate_addressing_running,
+		.deinit = wifi_nl80211_deinit,
+	};
 	WifiDataNl80211 *nl80211;
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	struct nl80211_device_info device_info = {};
 	char ifname[IFNAMSIZ];
 
@@ -1073,19 +866,7 @@ wifi_nl80211_init (int ifindex)
 		nm_sprintf_buf (ifname, "if %d", ifindex);
 	}
 
-	nl80211 = wifi_data_new (ifindex, sizeof (*nl80211));
-	nl80211->parent.get_mode = wifi_nl80211_get_mode;
-	nl80211->parent.set_mode = wifi_nl80211_set_mode;
-	nl80211->parent.set_powersave = wifi_nl80211_set_powersave;
-	nl80211->parent.get_freq = wifi_nl80211_get_freq;
-	nl80211->parent.find_freq = wifi_nl80211_find_freq;
-	nl80211->parent.get_bssid = wifi_nl80211_get_bssid;
-	nl80211->parent.get_rate = wifi_nl80211_get_rate;
-	nl80211->parent.get_qual = wifi_nl80211_get_qual;
-#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS
-	nl80211->parent.indicate_addressing_running = wifi_nl80211_indicate_addressing_running;
-#endif
-	nl80211->parent.deinit = wifi_nl80211_deinit;
+	nl80211 = wifi_data_new (&klass, ifindex);
 
 	nl80211->nl_sock = nl_socket_alloc ();
 	if (nl80211->nl_sock == NULL)
@@ -1095,12 +876,10 @@ wifi_nl80211_init (int ifindex)
 		goto error;
 
 	nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211");
-	if (nl80211->id < 0)
-		goto error;
-
-	nl80211->nl_cb = nl_cb_alloc (NL_CB_DEFAULT);
-	if (nl80211->nl_cb == NULL)
+	if (nl80211->id < 0) {
+		_LOGD (LOGD_WIFI, "genl_ctrl_resolve: failed to resolve \"nl80211\"");
 		goto error;
+	}
 
 	nl80211->phy = -1;
 
@@ -1153,18 +932,15 @@ wifi_nl80211_init (int ifindex)
 	nl80211->freqs = device_info.freqs;
 	nl80211->num_freqs = device_info.num_freqs;
 	nl80211->parent.caps = device_info.caps;
-
-	if (device_info.can_wowlan)
-		nl80211->parent.get_wowlan = wifi_nl80211_get_wowlan;
+	nl80211->can_wowlan = device_info.can_wowlan;
 
 	_LOGI (LOGD_PLATFORM | LOGD_WIFI,
 	       "(%s): using nl80211 for WiFi device control",
 	       ifname);
-
 	return (WifiData *) nl80211;
 
 error:
-	wifi_utils_deinit ((WifiData *) nl80211);
+	wifi_utils_unref ((WifiData *) nl80211);
 	return NULL;
 }
 
diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h
index 11a0f060..59386514 100644
--- a/src/platform/wifi/wifi-utils-private.h
+++ b/src/platform/wifi/wifi-utils-private.h
@@ -24,9 +24,8 @@
 #include "nm-dbus-interface.h"
 #include "wifi-utils.h"
 
-struct WifiData {
-	int ifindex;
-	NMDeviceWifiCapabilities caps;
+typedef struct {
+	gsize struct_size;
 
 	NM80211Mode (*get_mode) (WifiData *data);
 
@@ -66,9 +65,14 @@ struct WifiData {
 	gboolean (*set_mesh_ssid) (WifiData *data, const guint8 *ssid, gsize len);
 
 	gboolean (*indicate_addressing_running) (WifiData *data, gboolean running);
+} WifiDataClass;
+
+struct WifiData {
+	const WifiDataClass *klass;
+	int ifindex;
+	NMDeviceWifiCapabilities caps;
 };
 
-gpointer wifi_data_new (int ifindex, gsize len);
-void wifi_data_free (WifiData *data);
+gpointer wifi_data_new (const WifiDataClass *klass, int ifindex);
 
 #endif  /* __WIFI_UTILS_PRIVATE_H__ */
diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c
index c4d3c999..c8744f79 100644
--- a/src/platform/wifi/wifi-utils-wext.c
+++ b/src/platform/wifi/wifi-utils-wext.c
@@ -21,17 +21,14 @@
 
 #include "nm-default.h"
 
+#include "wifi-utils-wext.h"
+
 #include <errno.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
 
-#include "wifi-utils-private.h"
-#include "wifi-utils-wext.h"
-#include "nm-utils.h"
-#include "platform/nm-platform-utils.h"
-
 /* Hacks necessary to #include wireless.h; yay for WEXT */
 #ifndef __user
 #define __user
@@ -41,6 +38,10 @@
 #include <sys/socket.h>
 #include <linux/wireless.h>
 
+#include "wifi-utils-private.h"
+#include "nm-utils.h"
+#include "platform/nm-platform-utils.h"
+
 typedef struct {
 	WifiData parent;
 	int fd;
@@ -628,6 +629,21 @@ wext_get_caps (WifiDataWext *wext, const char *ifname, struct iw_range *range)
 WifiData *
 wifi_wext_init (int ifindex, gboolean check_scan)
 {
+	static const WifiDataClass klass = {
+		.struct_size = sizeof (WifiDataWext),
+		.get_mode = wifi_wext_get_mode,
+		.set_mode = wifi_wext_set_mode,
+		.set_powersave = wifi_wext_set_powersave,
+		.get_freq = wifi_wext_get_freq,
+		.find_freq = wifi_wext_find_freq,
+		.get_bssid = wifi_wext_get_bssid,
+		.get_rate = wifi_wext_get_rate,
+		.get_qual = wifi_wext_get_qual,
+		.deinit = wifi_wext_deinit,
+		.get_mesh_channel = wifi_wext_get_mesh_channel,
+		.set_mesh_channel = wifi_wext_set_mesh_channel,
+		.set_mesh_ssid = wifi_wext_set_mesh_ssid,
+	};
 	WifiDataWext *wext;
 	struct iw_range range;
 	guint32 response_len = 0;
@@ -642,19 +658,7 @@ wifi_wext_init (int ifindex, gboolean check_scan)
 		return NULL;
 	}
 
-	wext = wifi_data_new (ifindex, sizeof (*wext));
-	wext->parent.get_mode = wifi_wext_get_mode;
-	wext->parent.set_mode = wifi_wext_set_mode;
-	wext->parent.set_powersave = wifi_wext_set_powersave;
-	wext->parent.get_freq = wifi_wext_get_freq;
-	wext->parent.find_freq = wifi_wext_find_freq;
-	wext->parent.get_bssid = wifi_wext_get_bssid;
-	wext->parent.get_rate = wifi_wext_get_rate;
-	wext->parent.get_qual = wifi_wext_get_qual;
-	wext->parent.deinit = wifi_wext_deinit;
-	wext->parent.get_mesh_channel = wifi_wext_get_mesh_channel;
-	wext->parent.set_mesh_channel = wifi_wext_set_mesh_channel;
-	wext->parent.set_mesh_ssid = wifi_wext_set_mesh_ssid;
+	wext = wifi_data_new (&klass, ifindex);
 
 	wext->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
 	if (wext->fd < 0)
@@ -730,7 +734,7 @@ wifi_wext_init (int ifindex, gboolean check_scan)
 	return (WifiData *) wext;
 
 error:
-	wifi_utils_deinit ((WifiData *) wext);
+	wifi_utils_unref ((WifiData *) wext);
 	return NULL;
 }
 
diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c
index d0052121..8818dc9d 100644
--- a/src/platform/wifi/wifi-utils.c
+++ b/src/platform/wifi/wifi-utils.c
@@ -38,22 +38,19 @@
 #include "platform/nm-platform-utils.h"
 
 gpointer
-wifi_data_new (int ifindex, gsize len)
+wifi_data_new (const WifiDataClass *klass, int ifindex)
 {
 	WifiData *data;
 
-	data = g_malloc0 (len);
+	nm_assert (klass);
+	nm_assert (klass->struct_size > sizeof (WifiData));
+
+	data = g_malloc0 (klass->struct_size);
+	data->klass = klass;
 	data->ifindex = ifindex;
 	return data;
 }
 
-void
-wifi_data_free (WifiData *data)
-{
-	memset (data, 0, sizeof (*data));
-	g_free (data);
-}
-
 /*****************************************************************************/
 
 WifiData *
@@ -85,14 +82,14 @@ wifi_utils_get_caps (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, NM_WIFI_DEVICE_CAP_NONE);
 
-	return data->caps;	
+	return data->caps;
 }
 
 NM80211Mode
 wifi_utils_get_mode (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, NM_802_11_MODE_UNKNOWN);
-	return data->get_mode (data);
+	return data->klass->get_mode (data);
 }
 
 gboolean
@@ -104,7 +101,7 @@ wifi_utils_set_mode (WifiData *data, const NM80211Mode mode)
 	                      || (mode == NM_802_11_MODE_ADHOC), FALSE);
 
 	/* nl80211 probably doesn't need this */
-	return data->set_mode ? data->set_mode (data, mode) : TRUE;
+	return data->klass->set_mode ? data->klass->set_mode (data, mode) : TRUE;
 }
 
 gboolean
@@ -112,14 +109,14 @@ wifi_utils_set_powersave (WifiData *data, guint32 powersave)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
 
-	return data->set_powersave ? data->set_powersave (data, powersave) : TRUE;
+	return data->klass->set_powersave ? data->klass->set_powersave (data, powersave) : TRUE;
 }
 
 guint32
 wifi_utils_get_freq (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-	return data->get_freq (data);
+	return data->klass->get_freq (data);
 }
 
 guint32
@@ -127,7 +124,7 @@ wifi_utils_find_freq (WifiData *data, const guint32 *freqs)
 {
 	g_return_val_if_fail (data != NULL, 0);
 	g_return_val_if_fail (freqs != NULL, 0);
-	return data->find_freq (data, freqs);
+	return data->klass->find_freq (data, freqs);
 }
 
 gboolean
@@ -137,38 +134,40 @@ wifi_utils_get_bssid (WifiData *data, guint8 *out_bssid)
 	g_return_val_if_fail (out_bssid != NULL, FALSE);
 
 	memset (out_bssid, 0, ETH_ALEN);
-	return data->get_bssid (data, out_bssid);
+	return data->klass->get_bssid (data, out_bssid);
 }
 
 guint32
 wifi_utils_get_rate (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-	return data->get_rate (data);
+	return data->klass->get_rate (data);
 }
 
 int
 wifi_utils_get_qual (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-	return data->get_qual (data);
+	return data->klass->get_qual (data);
 }
 
 gboolean
 wifi_utils_get_wowlan (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-	if (!data->get_wowlan)
+
+	if (!data->klass->get_wowlan)
 		return FALSE;
-	return data->get_wowlan (data);
+	return data->klass->get_wowlan (data);
 }
 
 void
-wifi_utils_deinit (WifiData *data)
+wifi_utils_unref (WifiData *data)
 {
 	g_return_if_fail (data != NULL);
-	data->deinit (data);
-	wifi_data_free (data);
+
+	data->klass->deinit (data);
+	g_free (data);
 }
 
 gboolean
@@ -191,8 +190,8 @@ guint32
 wifi_utils_get_mesh_channel (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
-	g_return_val_if_fail (data->get_mesh_channel != NULL, FALSE);
-	return data->get_mesh_channel (data);
+	g_return_val_if_fail (data->klass->get_mesh_channel != NULL, FALSE);
+	return data->klass->get_mesh_channel (data);
 }
 
 gboolean
@@ -200,24 +199,24 @@ wifi_utils_set_mesh_channel (WifiData *data, guint32 channel)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
 	g_return_val_if_fail (channel <= 13, FALSE);
-	g_return_val_if_fail (data->set_mesh_channel != NULL, FALSE);
-	return data->set_mesh_channel (data, channel);
+	g_return_val_if_fail (data->klass->set_mesh_channel != NULL, FALSE);
+	return data->klass->set_mesh_channel (data, channel);
 }
 
 gboolean
 wifi_utils_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
-	g_return_val_if_fail (data->set_mesh_ssid != NULL, FALSE);
-	return data->set_mesh_ssid (data, ssid, len);
+	g_return_val_if_fail (data->klass->set_mesh_ssid != NULL, FALSE);
+	return data->klass->set_mesh_ssid (data, ssid, len);
 }
 
 gboolean
 wifi_utils_indicate_addressing_running (WifiData *data, gboolean running)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
-	if (data->indicate_addressing_running)
-		return data->indicate_addressing_running (data, running);
+	if (data->klass->indicate_addressing_running)
+		return data->klass->indicate_addressing_running (data, running);
 	return FALSE;
 }
 
diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h
index 705717b0..8acc35fc 100644
--- a/src/platform/wifi/wifi-utils.h
+++ b/src/platform/wifi/wifi-utils.h
@@ -34,7 +34,7 @@ WifiData *wifi_utils_init (int ifindex, gboolean check_scan);
 
 int wifi_utils_get_ifindex (WifiData *data);
 
-void wifi_utils_deinit (WifiData *data);
+void wifi_utils_unref (WifiData *data);
 
 NMDeviceWifiCapabilities wifi_utils_get_caps (WifiData *data);
 
@@ -66,7 +66,6 @@ gboolean wifi_utils_get_wowlan (WifiData *data);
 
 gboolean wifi_utils_set_powersave (WifiData *data, guint32 powersave);
 
-
 /* OLPC Mesh-only functions */
 guint32 wifi_utils_get_mesh_channel (WifiData *data);