[OpenWrt-Devel,3/5] hostapd: Add ubus accounting

Message ID 20181008124520.30040-3-yshvedov@wimarksystems.com
State New
Delegated to: Felix Fietkau
Headers show
Series
  • [OpenWrt-Devel,1/5] hostapd: add ubus hostapd_iface object
Related show

Commit Message

Yury Shvedov Oct. 8, 2018, 12:45 p.m.
This implements ubus accounting events. This accounting works the same manner
as the RADIUS accounting, except the interim (see below).

There tree types of messages: start, stop and interim.
Start sent when new client connected. There just current time, address
and session_id of client in message.
Stop sent when client disconnected. This message contains extra counters
and terminate_cause.
Interim sent by timeout, configured with ubus_accounting_interval
variable. This message contains an information about all connected to
bss clients.

Payload example:

    {
      "clients": [
        {
          "session_time": 9,
          "address": "68:3e:34:f5:61:3f",
          "session_id": "A6367C8F6C0662C1",
          "accounting": {
            "rx_bytes": 2336,
            "tx_packets": 5,
            "tx_bytes": 533,
            "rx_packets": 17
          },
          "terminate_cause": 1
        }
      ],
      "device": "radio0",
      "bssid": "44:d1:fa:12:4c:74",
      "freq": 5260,
      "iface": "wlan0"
    }

Signed-off-by: Yury Shvedov <yshvedov@wimarksystems.com>
---
 .../hostapd/patches/600-ubus_support.patch    | 165 +++++++++++-
 .../services/hostapd/src/src/ap/ubus.c        | 238 +++++++++++++++++-
 .../services/hostapd/src/src/ap/ubus.h        |  14 ++
 3 files changed, 405 insertions(+), 12 deletions(-)

Patch

diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch
index c63b85c079..c0db5be4e9 100644
--- a/package/network/services/hostapd/patches/600-ubus_support.patch
+++ b/package/network/services/hostapd/patches/600-ubus_support.patch
@@ -99,7 +99,15 @@ 
  	hostapd_interface_deinit(iface);
  	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
  		   __func__, driver, drv_priv);
-@@ -3114,6 +3120,7 @@ void hostapd_set_state(struct hostapd_if
+@@ -3050,6 +3056,7 @@ void hostapd_new_assoc_sta(struct hostap
+ 		ap_sta_set_authorized(hapd, sta, 1);
+ 		os_get_reltime(&sta->connected_time);
+ 		accounting_sta_start(hapd, sta);
++		hostapd_ubus_event_sta_account_start(hapd,sta);
+ 	}
+ 
+ 	/* Start IEEE 802.1X authentication process for new stations */
+@@ -3114,6 +3121,7 @@ void hostapd_set_state(struct hostapd_if
  	wpa_printf(MSG_INFO, "%s: interface state %s->%s",
  		   iface->conf ? iface->conf->bss[0]->iface : "N/A",
  		   hostapd_state_text(iface->state), hostapd_state_text(s));
@@ -201,7 +209,15 @@ 
  
  	sta = ap_get_sta(hapd, mgmt->sa);
  	if (sta == NULL) {
-@@ -3630,6 +3657,8 @@ static void handle_deauth(struct hostapd
+@@ -3584,6 +3611,7 @@ static void handle_disassoc(struct hosta
+ 	/* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+ 	 * authenticated. */
+ 	accounting_sta_stop(hapd, sta);
++	hostapd_ubus_event_sta_account_stop(hapd, sta);
+ 	ieee802_1x_free_station(hapd, sta);
+ 	if (sta->ipaddr)
+ 		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+@@ -3630,6 +3658,8 @@ static void handle_deauth(struct hostapd
  		" reason_code=%d",
  		MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
  
@@ -210,7 +226,7 @@ 
  	sta = ap_get_sta(hapd, mgmt->sa);
  	if (sta == NULL) {
  		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
-@@ -3949,7 +3978,7 @@ int ieee802_11_mgmt(struct hostapd_data
+@@ -3949,7 +3979,7 @@ int ieee802_11_mgmt(struct hostapd_data
  
  
  	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
@@ -219,7 +235,7 @@ 
  		return 1;
  	}
  
-@@ -3969,17 +3998,17 @@ int ieee802_11_mgmt(struct hostapd_data
+@@ -3969,17 +3999,17 @@ int ieee802_11_mgmt(struct hostapd_data
  	switch (stype) {
  	case WLAN_FC_STYPE_AUTH:
  		wpa_printf(MSG_DEBUG, "mgmt::auth");
@@ -240,6 +256,14 @@ 
  		ret = 1;
  		break;
  	case WLAN_FC_STYPE_DISASSOC:
+@@ -4128,6 +4158,7 @@ static void handle_assoc_cb(struct hosta
+ 	/* Stop previous accounting session, if one is started, and allocate
+ 	 * new session id for the new session. */
+ 	accounting_sta_stop(hapd, sta);
++	hostapd_ubus_event_sta_account_stop(hapd, sta);
+ 
+ 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ 		       HOSTAPD_LEVEL_INFO,
 --- a/src/ap/beacon.c
 +++ b/src/ap/beacon.c
 @@ -720,7 +720,7 @@ void sta_track_claim_taxonomy_info(struc
@@ -309,7 +333,15 @@ 
  
  	if (addr == NULL) {
  		/*
-@@ -195,6 +199,12 @@ int hostapd_notif_assoc(struct hostapd_d
+@@ -164,6 +168,7 @@ int hostapd_notif_assoc(struct hostapd_d
+ 	if (sta) {
+ 		ap_sta_no_session_timeout(hapd, sta);
+ 		accounting_sta_stop(hapd, sta);
++		hostapd_ubus_event_sta_account_stop(hapd, sta);
+ 
+ 		/*
+ 		 * Make sure that the previously registered inactivity timer
+@@ -195,6 +200,12 @@ int hostapd_notif_assoc(struct hostapd_d
  		goto fail;
  	}
  
@@ -324,7 +356,15 @@ 
  		wpabuf_free(sta->p2p_ie);
 --- a/src/ap/sta_info.c
 +++ b/src/ap/sta_info.c
-@@ -415,6 +415,7 @@ void ap_handle_timer(void *eloop_ctx, vo
+@@ -162,6 +162,7 @@ void ap_free_sta(struct hostapd_data *ha
+ 	int set_beacon = 0;
+ 
+ 	accounting_sta_stop(hapd, sta);
++	hostapd_ubus_event_sta_account_stop(hapd, sta);
+ 
+ 	/* just in case */
+ 	ap_sta_set_authorized(hapd, sta, 0);
+@@ -415,6 +416,7 @@ void ap_handle_timer(void *eloop_ctx, vo
  			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
  			       "local deauth request");
  		ap_free_sta(hapd, sta);
@@ -332,7 +372,15 @@ 
  		return;
  	}
  
-@@ -562,6 +563,7 @@ skip_poll:
+@@ -535,6 +537,7 @@ skip_poll:
+ 			sta->acct_terminate_cause =
+ 				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ 		accounting_sta_stop(hapd, sta);
++		hostapd_ubus_event_sta_account_stop(hapd, sta);
+ 		ieee802_1x_free_station(hapd, sta);
+ 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ 			       HOSTAPD_LEVEL_INFO, "disassociated due to "
+@@ -562,6 +565,7 @@ skip_poll:
  			hapd, sta,
  			WLAN_REASON_PREV_AUTH_NOT_VALID);
  		ap_free_sta(hapd, sta);
@@ -340,7 +388,23 @@ 
  		break;
  	}
  }
-@@ -1223,6 +1225,7 @@ void ap_sta_set_authorized(struct hostap
+@@ -796,6 +800,7 @@ void ap_sta_disassociate(struct hostapd_
+ 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
+ 			       ap_handle_timer, hapd, sta);
+ 	accounting_sta_stop(hapd, sta);
++	hostapd_ubus_event_sta_account_stop(hapd, sta);
+ 	ieee802_1x_free_station(hapd, sta);
+ 
+ 	sta->disassoc_reason = reason;
+@@ -845,6 +850,7 @@ void ap_sta_deauthenticate(struct hostap
+ 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+ 			       ap_handle_timer, hapd, sta);
+ 	accounting_sta_stop(hapd, sta);
++	hostapd_ubus_event_sta_account_stop(hapd, sta);
+ 	ieee802_1x_free_station(hapd, sta);
+ 
+ 	sta->deauth_reason = reason;
+@@ -1223,6 +1229,7 @@ void ap_sta_set_authorized(struct hostap
  					  buf, ip_addr);
  	} else {
  		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
@@ -360,7 +424,7 @@ 
  
 --- a/hostapd/config_file.c
 +++ b/hostapd/config_file.c
-@@ -2663,6 +2663,11 @@ static int hostapd_config_fill(struct ho
+@@ -2663,6 +2663,13 @@ static int hostapd_config_fill(struct ho
  		   0) {
  		bss->radius_das_require_message_authenticator = atoi(pos);
  #endif /* CONFIG_NO_RADIUS */
@@ -368,6 +432,8 @@ 
 +	} else if (os_strcmp(buf, "uci_device") == 0) {
 +		os_free(bss->uci_device);
 +		bss->uci_device = os_strdup(pos);
++	} else if (os_strcmp(buf, "ubus_acct_interim_interval") == 0) {
++		bss->ubus_acct_interim_interval = atoi(pos);
 +#endif /* UBUS_SUPPORT */
  	} else if (os_strcmp(buf, "auth_algs") == 0) {
  		bss->auth_algs = atoi(pos);
@@ -384,12 +450,91 @@ 
  
 --- a/src/ap/ap_config.h
 +++ b/src/ap/ap_config.h
-@@ -470,6 +470,8 @@ struct hostapd_bss_config {
+@@ -470,6 +470,9 @@ struct hostapd_bss_config {
  	int pbc_in_m1;
  	char *server_id;
  
 +	char *uci_device;
++	int ubus_acct_interim_interval;
 +
  #define P2P_ENABLED BIT(0)
  #define P2P_GROUP_OWNER BIT(1)
  #define P2P_GROUP_FORMATION BIT(2)
+--- a/src/ap/accounting.c
++++ b/src/ap/accounting.c
+@@ -164,7 +164,7 @@ static struct radius_msg * accounting_ms
+ }
+ 
+ 
+-static int accounting_sta_update_stats(struct hostapd_data *hapd,
++int accounting_sta_update_stats(struct hostapd_data *hapd,
+ 				       struct sta_info *sta,
+ 				       struct hostap_sta_driver_data *data)
+ {
+--- a/src/ap/accounting.h
++++ b/src/ap/accounting.h
+@@ -10,19 +10,13 @@
+ #define ACCOUNTING_H
+ 
+ #ifdef CONFIG_NO_ACCOUNTING
+-static inline int accounting_sta_get_id(struct hostapd_data *hapd,
+-					struct sta_info *sta)
+-{
+-	return 0;
+-}
+-
+ static inline void accounting_sta_start(struct hostapd_data *hapd,
+ 					struct sta_info *sta)
+ {
+ }
+ 
+ static inline void accounting_sta_stop(struct hostapd_data *hapd,
+-				       struct sta_info *sta)
++					struct sta_info *sta)
+ {
+ }
+ 
+@@ -35,11 +29,32 @@ static inline void accounting_deinit(str
+ {
+ }
+ #else /* CONFIG_NO_ACCOUNTING */
+-int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
+ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
+ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
+ int accounting_init(struct hostapd_data *hapd);
+ void accounting_deinit(struct hostapd_data *hapd);
+ #endif /* CONFIG_NO_ACCOUNTING */
+ 
++#if defined (CONFIG_NO_ACCOUNTING ) && !defined (UBUS_SUPPORT)
++static inline int accounting_sta_get_id(struct hostapd_data *hapd,
++					struct sta_info *sta)
++{
++	return 0;
++}
++static int accounting_sta_update_stats(struct hostapd_data *hapd,
++					struct sta_info *sta,
++					struct hostap_sta_driver_data *data)
++{
++	return 0;
++}
++
++#else
++
++int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
++int accounting_sta_update_stats(struct hostapd_data *hapd,
++					struct sta_info *sta,
++					struct hostap_sta_driver_data *data);
++
++#endif /* CONFIG_NO_ACCOUNTING && UBUS_SUPPORT */
++
+ #endif /* ACCOUNTING_H */
+--- a/src/ap/sta_info.h
++++ b/src/ap/sta_info.h
+@@ -139,6 +139,7 @@ struct sta_info {
+ 	int acct_terminate_cause; /* Acct-Terminate-Cause */
+ 	int acct_interim_interval; /* Acct-Interim-Interval */
+ 	unsigned int acct_interim_errors;
++	int ubus_acct_session_started;
+ 
+ 	/* For extending 32-bit driver counters to 64-bit counters */
+ 	u32 last_rx_bytes_hi;
diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c
index 6d12126a1a..2ceb2de159 100644
--- a/package/network/services/hostapd/src/src/ap/ubus.c
+++ b/package/network/services/hostapd/src/src/ap/ubus.c
@@ -18,10 +18,14 @@ 
 #include "ubus.h"
 #include "ap_drv_ops.h"
 #include "beacon.h"
+#include "accounting.h"
+#include "radius/radius.h"
 #include "rrm.h"
 #include "wnm_ap.h"
 #include "taxonomy.h"
 
+#define ACCT_DEFAULT_UPDATE_INTERVAL 300
+
 static struct ubus_context *ctx;
 static struct blob_buf b;
 static int ctx_ref;
@@ -1099,6 +1103,21 @@  static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
 	return memcmp(k1, k2, ETH_ALEN);
 }
 
+static void hostapd_ubus_accounting_interim_update(void *eloop_ctx,	void *);
+static bool hostapd_ubus_accounting_init(struct hostapd_data *hapd)
+{
+	int interval = hapd->conf->ubus_acct_interim_interval;
+	if (!interval)
+		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+	eloop_register_timeout(interval, 0, hostapd_ubus_accounting_interim_update,
+			hapd, NULL);
+	return true;
+}
+static void hostapd_ubus_accounting_stop(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(hostapd_ubus_accounting_interim_update, hapd, NULL);
+}
+
 void hostapd_ubus_add_bss(struct hostapd_data *hapd)
 {
 	struct ubus_object *obj = &hapd->ubus.obj;
@@ -1113,9 +1132,16 @@  void hostapd_ubus_add_bss(struct hostapd_data *hapd)
 	if (!hostapd_ubus_init())
 		return;
 
+	if (obj->id) {
+		return;
+	}
+
 	if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
 		return;
 
+	if (!hostapd_ubus_accounting_init(hapd))
+		return;
+
 	avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
 	obj->name = name;
 	obj->type = &bss_object_type;
@@ -1133,6 +1159,8 @@  void hostapd_ubus_free_bss(struct hostapd_data *hapd)
 	if (!ctx)
 		return;
 
+	hostapd_ubus_accounting_stop(hapd);
+
 	if (obj->id) {
 		ubus_remove_object(ctx, obj);
 		hostapd_ubus_ref_dec();
@@ -1207,7 +1235,7 @@  int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_req
 			ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
 			ht_cap = blobmsg_open_table(&b, "ht_capabilities");
 			blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
-			ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");		
+			ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
 			blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
 			blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
 			blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
@@ -1218,7 +1246,7 @@  int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_req
 			}
 			blobmsg_close_array(&b, mcs_set);
 			blobmsg_close_table(&b, ht_cap_mcs_set);
-			blobmsg_close_table(&b, ht_cap);		
+			blobmsg_close_table(&b, ht_cap);
 		}
 		if(req->elems->vht_capabilities)
 		{
@@ -1294,3 +1322,209 @@  void hostapd_ubus_event_iface_state(struct hostapd_iface *iface, int s)
 	blobmsg_add_string(&b, "state", hostapd_state_text(s));
 	ubus_send_event(ctx, "hostapd.iface_state", b.head);
 }
+static void
+blobmsg_add_sta_data(struct blob_buf *buf, const char *name,
+		struct sta_info *sta,
+		const struct hostap_sta_driver_data *data)
+{
+	u64 bytes;
+	void *tbl = blobmsg_open_table(buf, name);
+
+	blobmsg_add_u32(buf, "rx_packets", data->rx_packets);
+	blobmsg_add_u32(buf, "tx_packets", data->tx_packets);
+
+	if (data->bytes_64bit)
+		bytes = data->rx_bytes;
+	else
+		bytes = ((u64) sta->last_rx_bytes_hi << 32) |
+			sta->last_rx_bytes_lo;
+	blobmsg_add_u64(buf, "rx_bytes", bytes);
+	if (data->bytes_64bit)
+		bytes = data->tx_bytes;
+	else
+		bytes = ((u64) sta->last_tx_bytes_hi << 32) |
+			sta->last_tx_bytes_lo;
+	blobmsg_add_u64(buf, "tx_bytes", bytes);
+	blobmsg_add_double(buf, "rssi", data->last_ack_rssi);
+	blobmsg_add_double(buf, "signal", data->signal);
+
+	blobmsg_close_table(buf, tbl);
+}
+static void
+blobmsg_add_reltime(struct blob_buf *buf, const char *name,
+		const struct os_reltime *reltime)
+{
+	blobmsg_add_u32(buf, name, reltime->sec);
+}
+static void
+blobmsg_add_session_id(struct blob_buf *buf, const char *name,
+		struct sta_info *sta)
+{
+	blobmsg_printf(buf, name, "%016llX",
+			(unsigned long long) sta->acct_session_id);
+}
+static void
+blobmsg_add_station_accounting(struct blob_buf *buf,
+		const char *name,
+		struct sta_info *sta,
+		const struct hostap_sta_driver_data *data,
+		const struct os_reltime *session_time,
+		int cause)
+{
+	void *tbl = blobmsg_open_table(buf, name);
+	blobmsg_add_macaddr(buf, "address", sta->addr);
+	blobmsg_add_session_id(buf, "session_id", sta);
+	if(data)
+		blobmsg_add_sta_data(buf, "accounting", sta, data);
+	if(session_time)
+		blobmsg_add_reltime(buf, "session_time", session_time);
+	if (sta->identity)
+		blobmsg_add_string(buf, "identity", sta->identity);
+	if (cause > 0)
+		blobmsg_add_u32(buf, "terminate_cause", cause);
+	blobmsg_close_table(buf, tbl);
+}
+static void hostapd_ubus_event_sta_account(struct hostapd_data *hapd,
+		struct sta_info *sta, const char *status,
+		const struct hostap_sta_driver_data *data,
+		const struct os_reltime *session_time,
+		int cause)
+{
+	void *arr;
+
+	if (!ctx)
+		return;
+	blob_buf_init(&b, 0);
+
+	arr = blobmsg_open_array(&b, "clients");
+	blobmsg_add_station_accounting(&b, NULL, sta, data, session_time, cause);
+	blobmsg_close_array(&b, arr);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+
+	blobmsg_add_hapd_id(&b, hapd);
+	ubus_notify(ctx, &hapd->ubus.obj, status, b.head, -1);
+}
+void hostapd_ubus_event_sta_account_start(struct hostapd_data *hapd,
+		struct sta_info *sta)
+{
+	if (sta->ubus_acct_session_started || !hapd->ubus.obj.has_subscribers)
+		return;
+#ifdef CONFIG_NO_ACCOUNTING
+	os_get_reltime(&sta->acct_session_start);
+	sta->last_rx_bytes_hi = 0;
+	sta->last_rx_bytes_lo = 0;
+	sta->last_tx_bytes_hi = 0;
+	sta->last_tx_bytes_lo = 0;
+	hostapd_drv_sta_clear_stats(hapd, sta->addr);
+#endif
+	sta->ubus_acct_session_started = 1;
+	hostapd_ubus_event_sta_account(hapd, sta, "start", NULL, NULL, 0);
+}
+void hostapd_ubus_event_sta_account_stop(struct hostapd_data *hapd,
+		struct sta_info *sta)
+{
+	struct hostap_sta_driver_data data, *pdata = NULL;
+	struct os_reltime now_r, diff;
+	int cause = sta->acct_terminate_cause;
+
+	if (!sta->ubus_acct_session_started || !hapd->ubus.obj.has_subscribers)
+		return;
+	sta->ubus_acct_session_started = 0;
+	if (!ctx)
+		return;
+	if (eloop_terminated())
+		cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+	os_get_reltime(&now_r);
+	os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
+	if (accounting_sta_update_stats(hapd, sta, &data) == 0)
+		pdata = &data;
+	hostapd_ubus_event_sta_account(hapd, sta, "stop", pdata, &diff, cause);
+}
+struct ubus_sta_acct_counter {
+	struct blob_buf *buf;
+	int counter;
+	struct os_reltime now;
+};
+static int hostapd_ubus_event_sta_account_interim(struct hostapd_data *hapd,
+		struct sta_info *sta, void *ctx)
+{
+	struct ubus_sta_acct_counter *counter = ctx;
+	struct os_reltime diff;
+	struct hostap_sta_driver_data data, *pdata = NULL;
+
+	if (!sta->ubus_acct_session_started)
+		return 0;
+	os_reltime_sub(&counter->now, &sta->acct_session_start, &diff);
+	if (accounting_sta_update_stats(hapd, sta, &data) == 0)
+		pdata = &data;
+	blobmsg_add_station_accounting(counter->buf, NULL, sta, pdata, &diff, 0);
+	++counter->counter;
+}
+static void hostapd_ubus_accounting_interim(struct hostapd_data *hapd,
+		struct sta_info *sta)
+{
+	struct ubus_sta_acct_counter counter = { &b, 0 };
+	void *arr;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+	blob_buf_init(&b, 0);
+
+	os_get_reltime(&counter.now);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+	blobmsg_add_hapd_id(&b, hapd);
+	arr = blobmsg_open_array(&b, "clients");
+
+	if (sta != NULL)
+		hostapd_ubus_event_sta_account_interim(hapd, sta, &counter);
+	else
+		ap_for_each_sta(hapd, hostapd_ubus_event_sta_account_interim, &counter);
+	blobmsg_close_array(&b, arr);
+	if (counter.counter > 0)
+		ubus_notify(ctx, &hapd->ubus.obj, "interim", b.head, -1);
+}
+static void hostapd_ubus_accounting_interim_update(void *eloop_ctx,
+		void *timer_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	hostapd_ubus_accounting_interim(hapd, NULL);
+	hostapd_ubus_accounting_init(hapd);
+}
+
+#ifdef CONFIG_NO_ACCOUNTING
+int accounting_sta_update_stats(struct hostapd_data *hapd,
+					   struct sta_info *sta,
+					   struct hostap_sta_driver_data *data)
+{
+	if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
+		return -1;
+
+	if (!data->bytes_64bit) {
+		/* Extend 32-bit counters from the driver to 64-bit counters */
+		if (sta->last_rx_bytes_lo > data->rx_bytes)
+			sta->last_rx_bytes_hi++;
+		sta->last_rx_bytes_lo = data->rx_bytes;
+
+		if (sta->last_tx_bytes_lo > data->tx_bytes)
+			sta->last_tx_bytes_hi++;
+		sta->last_tx_bytes_lo = data->tx_bytes;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			   HOSTAPD_LEVEL_DEBUG,
+			   "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
+			   data->rx_bytes, sta->last_rx_bytes_hi,
+			   sta->last_rx_bytes_lo,
+			   data->tx_bytes, sta->last_tx_bytes_hi,
+			   sta->last_tx_bytes_lo,
+			   data->bytes_64bit);
+
+	return 0;
+}
+int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	/* Copied from radius_gen_session_id */
+	return os_get_random((u8 *) &sta->acct_session_id,
+			sizeof(sta->acct_session_id));
+}
+#endif
diff --git a/package/network/services/hostapd/src/src/ap/ubus.h b/package/network/services/hostapd/src/src/ap/ubus.h
index 840b000180..cc4b5fcb0f 100644
--- a/package/network/services/hostapd/src/src/ap/ubus.h
+++ b/package/network/services/hostapd/src/src/ap/ubus.h
@@ -27,6 +27,7 @@  struct hostapd_ubus_request {
 
 struct hostapd_iface;
 struct hostapd_data;
+struct sta_info;
 
 #ifdef UBUS_SUPPORT
 
@@ -53,6 +54,11 @@  void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *
 
 void hostapd_ubus_event_iface_state(struct hostapd_iface *iface, int s);
 
+void hostapd_ubus_event_sta_account_start(struct hostapd_data *hapd,
+		struct sta_info *sta);
+void hostapd_ubus_event_sta_account_stop(struct hostapd_data *hapd,
+		struct sta_info *sta);
+
 #else
 
 struct hostapd_ubus_iface {};
@@ -86,6 +92,14 @@  static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *ty
 static inline void hostapd_ubus_event_iface_state(struct hostapd_iface *iface, int s)
 {
 }
+static inline void hostapd_ubus_event_sta_account_start(struct hostapd_data *hapd,
+		struct sta_info *sta)
+{
+}
+static inline void hostapd_ubus_event_sta_account_stop(struct hostapd_data *hapd,
+		struct sta_info *sta)
+{
+}
 #endif
 
 #endif