diff mbox

Expose connected stations on DBus.

Message ID 1418867924-6334-1-git-send-email-mathieu.trudel-lapierre@canonical.com
State Changes Requested
Headers show

Commit Message

Mathieu Trudel-Lapierre Dec. 18, 2014, 1:58 a.m. UTC
Make it possible to list connected stations in AP mode over DBus, along
with some of their properties.

Signed-off-by: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>

---
 wpa_supplicant/dbus/dbus_new.c          |  247 ++++++++++++++++++++++++--
 wpa_supplicant/dbus/dbus_new.h          |   25 ++
 wpa_supplicant/dbus/dbus_new_handlers.c |  297 ++++++++++++++++++++++++++++++++
 wpa_supplicant/dbus/dbus_new_handlers.h |   32 +++
 wpa_supplicant/notify.c                 |    6 
 5 files changed, 592 insertions(+), 15 deletions(-)

Comments

Jouni Malinen Jan. 2, 2015, 4:01 p.m. UTC | #1
My first response was a bit too long for this mailing list, so dropping
the attached patch.. It is available from here:
http://w1.fi/p/0001-D-Bus-Expose-connected-stations-in-AP-P2P-GO-mode.patch


On Wed, Dec 17, 2014 at 08:58:44PM -0500, Mathieu Trudel-Lapierre wrote:
> Make it possible to list connected stations in AP mode over DBus, along
> with some of their properties.

Could you please also provide a matching update to the D-Bus interface
documentation in doc/dbus.doxygen?

As far as the changes are concerned, they did not seem to be on top of
the current development snapshot. Nothing too difficult there, so I
rebased this and did some small coding style cleanup to make it easier
to review. The attached patch shows the version I reviewed and tested
with.

The most critical issue here seems to be in the change to the existing
StaAuthorized signal. Adding a new parameter to it breaks existing users
of this event, so that does not look like an acceptable modification.
wpas_dbus_interface_signals[] would have needed to be updated to match
the addition of the properties argument for this signal. If this new
station properties information is needed in authorization signal, it
looks like a new signal would need to be defined for this purpose in
order to not break existing users.

This patch breaks build without CONFIG_AP=y, i.e., that needs to be
cleaned up with ifdef CONFIG_AP and/or static inline wrappers etc. to
make sure all build configurations continue to work.


Some more comments below regarding the changes:

> Index: b/wpa_supplicant/dbus/dbus_new.c
>  static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s,

> @@ -895,19 +900,33 @@ static void wpas_dbus_signal_sta(struct
>  	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
> -				      WPAS_DBUS_NEW_IFACE_INTERFACE, sig_name);
> +				      WPAS_DBUS_NEW_IFACE_INTERFACE,
> +				      sig_name);

Why? Looks unrelated to the added functionality and not really
consistent with the coding style in wpa_supplicant.

> +	dbus_message_iter_init_append(msg, &iter);
> +	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
> +					    &path))
> +		goto err;
> +
> +	if (properties) {
> +		if (!wpa_dbus_get_object_properties(iface, path,
> +						    WPAS_DBUS_NEW_IFACE_STA,
> +						    &iter))
> +			goto err;
> +	}

This is the part that breaks backwards compatibility.

> +static const struct wpa_dbus_signal_desc wpas_dbus_sta_signals[] = {
> +	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
> +	{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_STA,

Why would we add something new that is already documented as deprecated?

> Index: b/wpa_supplicant/dbus/dbus_new_handlers.c

> +dbus_bool_t wpas_dbus_getter_sta_flags(DBusMessageIter *iter, DBusError *error,
> +				       void *user_data)

> +	struct sta_info *sta;

> +	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
> +						&sta->flags,
> +						error);

sta->flags is a bitfield of the WLAN_STA_* values. These are internal to
hostapd/wpa_supplicant implementation and are subject to change whenever
needed. As such, it is not appropriate to expose those values as-is
through a D-Bus interface that is supposed to remain stable. If some of
these flags are really needed to be exposed, those specific flags
should be mapped into something new defined for the D-Bus interface in a
way that can be maintained for long period of time.

> + * Getter for "RxPackets" property.
> + */
> +dbus_bool_t wpas_dbus_getter_sta_rx_packets(DBusMessageIter *iter, DBusError *error,

> +        if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)

Fetching these values separately for each TX/RX statistics property
looks a bit inefficient since I'd assume GetAll() would be used to fetch
these most of the time (or a signal providing these in a group). Anyway,
I guess this can be acceptable if these is no convenient means for using
a single fetch for "grouped" use.
diff mbox

Patch

Index: b/wpa_supplicant/dbus/dbus_new.c
===================================================================
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -24,6 +24,7 @@ 
 #include "dbus_common_i.h"
 #include "dbus_new_handlers_p2p.h"
 #include "p2p/p2p.h"
+#include "ap/sta_info.h"
 
 #ifdef CONFIG_AP /* until needed by something else */
 
@@ -878,15 +879,19 @@  nomem:
  * Notify listeners about event related with station
  */
 static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s,
-				 const u8 *sta, const char *sig_name)
+				 const u8 *sta, const char *sig_name,
+                                 int properties)
 {
 	struct wpas_dbus_priv *iface;
 	DBusMessage *msg;
-	char sta_mac[WPAS_DBUS_OBJECT_PATH_MAX];
-	char *dev_mac;
+	DBusMessageIter iter;
+	char sta_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	char *path;
 
-	os_snprintf(sta_mac, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(sta));
-	dev_mac = sta_mac;
+	os_snprintf(sta_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(sta));
+	path = sta_obj_path;
 
 	iface = wpa_s->global->dbus;
 
@@ -895,19 +900,33 @@  static void wpas_dbus_signal_sta(struct
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
-				      WPAS_DBUS_NEW_IFACE_INTERFACE, sig_name);
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      sig_name);
 	if (msg == NULL)
 		return;
 
-	if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &dev_mac,
-				     DBUS_TYPE_INVALID))
-		dbus_connection_send(iface->con, msg, NULL);
-	else
-		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	dbus_message_iter_init_append(msg, &iter);
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path))
+		goto err;
+
+	if (properties) {
+		if (!wpa_dbus_get_object_properties(iface, path,
+						    WPAS_DBUS_NEW_IFACE_STA,
+						    &iter))
+			goto err;
+	}
+
+	wpa_printf(MSG_DEBUG, "dbus: Station MAC address '" MACSTR "' '%s'",
+		   MAC2STR(sta), sig_name);
+
+	dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
+	return;
 
-	wpa_printf(MSG_DEBUG, "dbus: Station MAC address '%s' '%s'",
-		   sta_mac, sig_name);
+err:
+	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	dbus_message_unref(msg);
 }
 
 
@@ -921,7 +940,7 @@  static void wpas_dbus_signal_sta(struct
 void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
 				     const u8 *sta)
 {
-	wpas_dbus_signal_sta(wpa_s, sta, "StaAuthorized");
+	wpas_dbus_signal_sta(wpa_s, sta, "StaAuthorized", TRUE);
 }
 
 
@@ -935,7 +954,7 @@  void wpas_dbus_signal_sta_authorized(str
 void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
 				       const u8 *sta)
 {
-	wpas_dbus_signal_sta(wpa_s, sta, "StaDeauthorized");
+	wpas_dbus_signal_sta(wpa_s, sta, "StaDeauthorized", FALSE);
 }
 
 
@@ -1817,6 +1836,9 @@  void wpas_dbus_signal_prop_changed(struc
 	case WPAS_DBUS_PROP_BSSS:
 		prop = "BSSs";
 		break;
+	case WPAS_DBUS_PROP_STAS:
+		prop = "Stations";
+		break;
 	case WPAS_DBUS_PROP_CURRENT_AUTH_MODE:
 		prop = "CurrentAuthMode";
 		break;
@@ -1900,6 +1922,39 @@  void wpas_dbus_bss_signal_prop_changed(s
 
 
 /**
+ * wpas_dbus_sta_signal_prop_changed - Signals change of STA property
+ * @wpa_s: %wpa_supplicant network interface data
+ * @property: indicates which property has changed
+ * @address: unique BSS identifier
+ *
+ * Sends PropertyChanged signals with path, interface, and arguments depending
+ * on which property has changed.
+ */
+void wpas_dbus_sta_signal_prop_changed(struct wpa_supplicant *wpa_s,
+				       enum wpas_dbus_bss_prop property,
+				       u8 address[ETH_ALEN])
+{
+	char path[WPAS_DBUS_OBJECT_PATH_MAX];
+	char *prop;
+
+	switch (property) {
+	case WPAS_DBUS_STA_PROP_ADDRESS:
+		prop = "Address";
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
+			   __func__, property);
+		return;
+	}
+
+	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(address));
+
+	wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
+				       WPAS_DBUS_NEW_IFACE_STA, prop);
+}
+/**
  * wpas_dbus_signal_debug_level_changed - Signals change of debug param
  * @global: wpa_global structure
  *
@@ -2434,6 +2489,164 @@  err:
 }
 
 
+static const struct wpa_dbus_property_desc wpas_dbus_sta_properties[] = {
+	{ "Address", WPAS_DBUS_NEW_IFACE_STA, "ay",
+	  wpas_dbus_getter_sta_address,
+	  NULL
+	},
+	{ "AID", WPAS_DBUS_NEW_IFACE_STA, "q",
+	  wpas_dbus_getter_sta_aid,
+	  NULL
+	},
+	{ "Flags", WPAS_DBUS_NEW_IFACE_STA, "u",
+	  wpas_dbus_getter_sta_flags,
+	  NULL
+	},
+	{ "Capabilities", WPAS_DBUS_NEW_IFACE_STA, "q",
+	  wpas_dbus_getter_sta_caps,
+	  NULL
+	},
+	{ "RxPackets", WPAS_DBUS_NEW_IFACE_STA, "t",
+	  wpas_dbus_getter_sta_rx_packets,
+	  NULL
+	},
+	{ "TxPackets", WPAS_DBUS_NEW_IFACE_STA, "t",
+	  wpas_dbus_getter_sta_tx_packets,
+	  NULL
+	},
+	{ "RxBytes", WPAS_DBUS_NEW_IFACE_STA, "t",
+	  wpas_dbus_getter_sta_rx_bytes,
+	  NULL
+	},
+	{ "TxBytes", WPAS_DBUS_NEW_IFACE_STA, "t",
+	  wpas_dbus_getter_sta_tx_bytes,
+	  NULL
+	},
+	{ NULL, NULL, NULL, NULL, NULL }
+};
+
+
+static const struct wpa_dbus_signal_desc wpas_dbus_sta_signals[] = {
+	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+	{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_STA,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_unregister_sta - Unregister a connected station from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: connected station bssid
+ * @id: unique station identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters STA representing object from dbus
+ */
+int wpas_dbus_unregister_sta(struct wpa_supplicant *wpa_s,
+			     const u8 *sta)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	char sta_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	os_snprintf(sta_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(sta));
+
+	wpa_printf(MSG_DEBUG, "dbus: Unregister STA object '%s'",
+		   sta_obj_path);
+	if (wpa_dbus_unregister_object_per_iface(ctrl_iface, sta_obj_path)) {
+		wpa_printf(MSG_ERROR, "dbus: Cannot unregister STA object %s",
+			   sta_obj_path);
+		return -1;
+	}
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STAS);
+
+	return 0;
+}
+
+
+/**
+ * wpas_dbus_register_sta - Register a scanned station with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: connection network station
+ * @id: unique STA identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers STA representing object with dbus
+ */
+int wpas_dbus_register_sta(struct wpa_supplicant *wpa_s,
+			   const u8 *sta)
+{
+	struct wpas_dbus_priv *ctrl_iface;
+	struct wpa_dbus_object_desc *obj_desc;
+	char sta_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+	struct sta_handler_args *arg;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s == NULL || wpa_s->global == NULL)
+		return 0;
+	ctrl_iface = wpa_s->global->dbus;
+	if (ctrl_iface == NULL)
+		return 0;
+
+	os_snprintf(sta_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(sta));
+
+	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+	if (!obj_desc) {
+		wpa_printf(MSG_ERROR, "Not enough memory "
+			   "to create object description");
+		goto err;
+	}
+
+	arg = os_zalloc(sizeof(struct sta_handler_args));
+	if (!arg) {
+		wpa_printf(MSG_ERROR, "Not enough memory "
+			   "to create arguments for handler");
+		goto err;
+	}
+	arg->wpa_s = wpa_s;
+	arg->sta = sta;
+
+	wpas_dbus_register(obj_desc, arg, wpa_dbus_free,
+			   NULL,
+			   wpas_dbus_sta_properties,
+			   wpas_dbus_sta_signals);
+
+	wpa_printf(MSG_DEBUG, "dbus: Register STA object '%s'",
+		   sta_obj_path);
+	if (wpa_dbus_register_object_per_iface(ctrl_iface, sta_obj_path,
+					       wpa_s->ifname, obj_desc)) {
+		wpa_printf(MSG_ERROR,
+			   "Cannot register STA dbus object %s.",
+			   sta_obj_path);
+		goto err;
+	}
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STAS);
+
+	return 0;
+
+err:
+	free_dbus_object_desc(obj_desc);
+	return -1;
+}
+
+
 static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
 	{ "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler) &wpas_dbus_handler_scan,
@@ -2896,6 +3109,10 @@  static const struct wpa_dbus_property_de
 	  wpas_dbus_getter_disconnect_reason,
 	  NULL
 	},
+	{ "Stations", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
+	  wpas_dbus_getter_stas,
+	  NULL
+	},
 	{ NULL, NULL, NULL, NULL, NULL }
 };
 
Index: b/wpa_supplicant/dbus/dbus_new_handlers.c
===================================================================
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -22,6 +22,10 @@ 
 #include "../bss.h"
 #include "../scan.h"
 #include "../autoscan.h"
+#include "../ap.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ap_drv_ops.h"
 #include "dbus_new_helpers.h"
 #include "dbus_new.h"
 #include "dbus_new_handlers.h"
@@ -3385,6 +3389,299 @@  dbus_bool_t wpas_dbus_getter_blobs(DBusM
 }
 
 
+/**
+ * wpas_dbus_getter_stas - Get connected stations for an interface
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: a list of stations
+ *
+ * Getter for "Stations" property.
+ */
+dbus_bool_t wpas_dbus_getter_stas(DBusMessageIter *iter, DBusError *error,
+				  void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+	char **paths = NULL;
+	unsigned int i = 0;
+	dbus_bool_t success = FALSE;
+
+	if (wpa_s->ap_iface == NULL)
+		goto out;
+
+	hapd = wpa_s->ap_iface->bss[0];
+
+	paths = os_calloc(hapd->num_sta, sizeof(char *));
+	if (!paths) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	/* Loop through scan results and append each result's object path */
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+		if (paths[i] == NULL) {
+			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+					     "no memory");
+			goto out;
+		}
+		/* Construct the object path for this BSS. */
+		os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR,
+			    wpa_s->dbus_new_path, MAC2STR(sta->addr));
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 paths, hapd->num_sta,
+							 error);
+
+out:
+	while (i)
+		os_free(paths[--i]);
+	os_free(paths);
+	return success;
+}
+
+
+/**
+ * wpas_dbus_getter_sta_address - Return the BSSID of a connected station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Address" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_address(DBusMessageIter *iter, DBusError *error,
+				         void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(args->wpa_s->ap_iface->bss[0], args->sta);
+	if (!sta)
+		return FALSE;
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      sta->addr, ETH_ALEN,
+						      error);
+}
+
+
+/**
+ * wpas_dbus_getter_sta_aid - Return the AID of a connected station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "AID" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_aid(DBusMessageIter *iter, DBusError *error,
+				     void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(args->wpa_s->ap_iface->bss[0], args->sta);
+	if (!sta)
+		return FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+						&sta->aid,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_sta_flags - Return the flags of a connected station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Flags" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_flags(DBusMessageIter *iter, DBusError *error,
+				       void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(args->wpa_s->ap_iface->bss[0], args->sta);
+	if (!sta)
+		return FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+						&sta->flags,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_sta_caps - Return the capabilities of a station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Capabilities" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_caps(DBusMessageIter *iter, DBusError *error,
+				      void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(args->wpa_s->ap_iface->bss[0], args->sta);
+	if (!sta)
+		return FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+						&sta->capability,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_rx_packets - Return the received packets for a station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "RxPackets" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_rx_packets(DBusMessageIter *iter, DBusError *error,
+				            void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+	struct hostap_sta_driver_data data;
+	struct hostapd_data *hapd;
+
+	if (!args->wpa_s->ap_iface)
+		return FALSE;
+
+	hapd = args->wpa_s->ap_iface->bss[0];
+	sta = ap_get_sta(hapd, args->sta);
+	if (!sta)
+		return FALSE;
+
+        if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+		return FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64,
+						&data.rx_packets,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_tx_packets - Return the transmitted packets for a station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "TxPackets" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_tx_packets(DBusMessageIter *iter, DBusError *error,
+				            void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+	struct hostap_sta_driver_data data;
+	struct hostapd_data *hapd;
+
+	if (!args->wpa_s->ap_iface)
+		return FALSE;
+
+	hapd = args->wpa_s->ap_iface->bss[0];
+	sta = ap_get_sta(hapd, args->sta);
+	if (!sta)
+		return FALSE;
+
+        if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+		return FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64,
+						&data.tx_packets,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_tx_bytes - Return the transmitted bytes for a station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "TxBytes" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_tx_bytes(DBusMessageIter *iter, DBusError *error,
+				          void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+	struct hostap_sta_driver_data data;
+	struct hostapd_data *hapd;
+
+	if (!args->wpa_s->ap_iface)
+		return FALSE;
+
+	hapd = args->wpa_s->ap_iface->bss[0];
+	sta = ap_get_sta(hapd, args->sta);
+	if (!sta)
+		return FALSE;
+
+        if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+		return FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64,
+						&data.tx_bytes,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_rx_bytes - Return the received bytes for a station
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "RxBytes" property.
+ */
+dbus_bool_t wpas_dbus_getter_sta_rx_bytes(DBusMessageIter *iter, DBusError *error,
+				          void *user_data)
+{
+	struct sta_handler_args *args = user_data;
+	struct sta_info *sta;
+	struct hostap_sta_driver_data data;
+	struct hostapd_data *hapd;
+
+	if (!args->wpa_s->ap_iface)
+		return FALSE;
+
+	hapd = args->wpa_s->ap_iface->bss[0];
+	sta = ap_get_sta(hapd, args->sta);
+	if (!sta)
+		return FALSE;
+
+        if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+		return FALSE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64,
+						&data.rx_bytes,
+						error);
+}
+
+
 static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
 				       DBusError *error, const char *func_name)
 {
Index: b/wpa_supplicant/dbus/dbus_new_handlers.h
===================================================================
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -20,6 +20,11 @@  struct bss_handler_args {
 	unsigned int id;
 };
 
+struct sta_handler_args {
+	struct wpa_supplicant *wpa_s;
+	const u8 *sta;
+};
+
 dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
 					     const int type,
 					     const void *val,
@@ -232,6 +237,33 @@  dbus_bool_t wpas_dbus_getter_pkcs11_modu
 dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
 				   void *user_data);
 
+dbus_bool_t wpas_dbus_getter_stas(DBusMessageIter *iter, DBusError *error,
+				  void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_address(DBusMessageIter *iter, DBusError *error,
+					 void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_aid(DBusMessageIter *iter, DBusError *error,
+				     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_flags(DBusMessageIter *iter, DBusError *error,
+				       void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_caps(DBusMessageIter *iter, DBusError *error,
+				      void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_rx_packets(DBusMessageIter *iter, DBusError *error,
+				            void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_tx_packets(DBusMessageIter *iter, DBusError *error,
+				            void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_tx_bytes(DBusMessageIter *iter, DBusError *error,
+				          void *user_data);
+
+dbus_bool_t wpas_dbus_getter_sta_rx_bytes(DBusMessageIter *iter, DBusError *error,
+				          void *user_data);
+
 dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
 				       void *user_data);
 
Index: b/wpa_supplicant/dbus/dbus_new.h
===================================================================
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -12,6 +12,7 @@ 
 
 #include "common/defs.h"
 #include "p2p/p2p.h"
+#include "ap/sta_info.h"
 
 struct wpa_global;
 struct wpa_supplicant;
@@ -28,6 +29,7 @@  enum wpas_dbus_prop {
 	WPAS_DBUS_PROP_CURRENT_NETWORK,
 	WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
 	WPAS_DBUS_PROP_BSSS,
+	WPAS_DBUS_PROP_STAS,
 	WPAS_DBUS_PROP_DISCONNECT_REASON,
 };
 
@@ -43,6 +45,10 @@  enum wpas_dbus_bss_prop {
 	WPAS_DBUS_BSS_PROP_IES,
 };
 
+enum wpas_dbus_sta_prop {
+	WPAS_DBUS_STA_PROP_ADDRESS,
+};
+
 #define WPAS_DBUS_OBJECT_PATH_MAX 150
 
 #define WPAS_DBUS_NEW_SERVICE		"fi.w1.wpa_supplicant1"
@@ -59,6 +65,9 @@  enum wpas_dbus_bss_prop {
 #define WPAS_DBUS_NEW_BSSIDS_PART "BSSs"
 #define WPAS_DBUS_NEW_IFACE_BSS	WPAS_DBUS_NEW_INTERFACE ".BSS"
 
+#define WPAS_DBUS_NEW_STAS_PART "Stations"
+#define WPAS_DBUS_NEW_IFACE_STA	WPAS_DBUS_NEW_INTERFACE ".Station"
+
 #define WPAS_DBUS_NEW_IFACE_P2PDEVICE	\
 		WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
 
@@ -157,6 +166,10 @@  int wpas_dbus_unregister_bss(struct wpa_
 			     u8 bssid[ETH_ALEN], unsigned int id);
 int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
 			   u8 bssid[ETH_ALEN], unsigned int id);
+int wpas_dbus_unregister_sta(struct wpa_supplicant *wpa_s,
+			     const u8 *sta);
+int wpas_dbus_register_sta(struct wpa_supplicant *wpa_s,
+			   const u8 *sta);
 void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
 				 const char *name);
 void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
@@ -316,6 +329,18 @@  static inline int wpas_dbus_register_bss
 {
 	return 0;
 }
+
+static inline int wpas_dbus_unregister_sta(struct wpa_supplicant *wpa_s,
+					   const u8 *sta)
+{
+	return 0;
+}
+
+static inline int wpas_dbus_register_sta(struct wpa_supplicant *wpa_s,
+					 const u8 *sta)
+{
+	return 0;
+}
 
 static inline void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
 					       const char *name)
Index: b/wpa_supplicant/notify.c
===================================================================
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -552,6 +552,9 @@  static void wpas_notify_ap_sta_authorize
 	wpas_dbus_signal_p2p_peer_joined(wpa_s, sta);
 #endif /* CONFIG_P2P */
 
+	/* Unregister the station */
+	wpas_dbus_register_sta(wpa_s, sta);
+
 	/* Notify listeners a new station has been authorized */
 	wpas_dbus_signal_sta_authorized(wpa_s, sta);
 }
@@ -576,6 +579,9 @@  static void wpas_notify_ap_sta_deauthori
 
 	/* Notify listeners a station has been deauthorized */
 	wpas_dbus_signal_sta_deauthorized(wpa_s, sta);
+
+	/* Unregister the station */
+	wpas_dbus_unregister_sta(wpa_s, sta);
 }