[PATCH 3/8] wifi_stats: hostapd configuration parameters

Iegor Sergieienkov iegor at nova-labs.com
Thu Aug 27 07:16:20 PDT 2026


Add the configuration parameters for the wifi_stats module:

 * wifi_stats_interval - driver polling interval in seconds, 1..3600,
   default 1
 * wifi_stats_wba_window - aggregation window in seconds, 1..3600,
   default 60
 * wifi_stats_span - how the aggregation span is determined, either
   report (default) or window
 * wifi_stats_ewma_weight - exponential weight used by AVG-EXP, 1..9,
   default 5
 * wifi_stats_wba_enabled - whether to use the WBA Connect-Info format,
   default 1
 * wifi_stats_metric_<metric>=<algorithm> - per-metric aggregation
   algorithm

The metric names are rxbitrate, txbitrate, rssi, frameloss and
frameretry. The algorithms are MIN, MAX, AVG-LIN, AVG-EXP, ACC and NONE;
frameloss and frameretry accept only ACC or NONE. The defaults are MAX
for the two bit rates, AVG-LIN for RSSI, and ACC for the two
counter-derived metrics.

Signed-off-by: Iegor Sergieienkov <iegor at nova-labs.com>
---
 hostapd/config_file.c | 116 ++++++++++++++++++++++++++++++++++++++++++
 hostapd/hostapd.conf  |  71 ++++++++++++++++++++++++++
 src/ap/ap_config.c    |   8 +++
 src/ap/ap_config.h    |  15 ++++++
 4 files changed, 210 insertions(+)

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 6484d6f7d..a6b6cd392 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -24,6 +24,9 @@
 #include "ap/wpa_auth.h"
 #include "ap/ap_config.h"
 #include "config_file.h"
+#ifdef CONFIG_WIFI_STATS
+#include "wifi_stats/wifi_stats.h"
+#endif /* CONFIG_WIFI_STATS */
 
 
 #ifndef CONFIG_NO_VLAN
@@ -2368,6 +2371,11 @@ static bool get_hexstream(const char *val, struct wpabuf **var,
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
+#ifdef CONFIG_WIFI_STATS
+#define WIFI_STATS_METRIC_PREFIX "wifi_stats_metric_"
+#endif /* CONFIG_WIFI_STATS */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
 			       const char *buf, char *pos, int line)
@@ -2505,6 +2513,114 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 	} else if (os_strcmp(buf, "config_id") == 0) {
 		os_free(bss->config_id);
 		bss->config_id = os_strdup(pos);
+#ifdef CONFIG_WIFI_STATS
+	} else if (os_strcmp(buf, "wifi_stats_interval") == 0) {
+		char *endptr;
+		unsigned long val = strtoul(pos, &endptr, 10);
+		if (endptr == pos || *endptr != '\0' ||
+		    val < WIFI_STATS_MIN_INTERVAL || val > WIFI_STATS_MAX_INTERVAL) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid wifi_stats_interval '%s' (must be %d-%d)",
+				   line, pos, WIFI_STATS_MIN_INTERVAL,
+				   WIFI_STATS_MAX_INTERVAL);
+			return 1;
+		}
+		conf->wifi_stats_interval = (unsigned int)val;
+	} else if (os_strcmp(buf, "wifi_stats_wba_window") == 0) {
+		char *endptr;
+		unsigned long val = strtoul(pos, &endptr, 10);
+		if (endptr == pos || *endptr != '\0' ||
+		    val < WIFI_STATS_MIN_WINDOW || val > WIFI_STATS_MAX_WINDOW) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid wifi_stats_wba_window '%s' (must be %d-%d seconds)",
+				   line, pos, WIFI_STATS_MIN_WINDOW,
+				   WIFI_STATS_MAX_WINDOW);
+			return 1;
+		}
+		conf->wifi_stats_wba_window = (unsigned int)val;
+	} else if (os_strcmp(buf, "wifi_stats_span") == 0) {
+		wifi_stats_span_mode_t mode =
+			wifi_stats_span_mode_from_str(pos);
+
+		if (mode >= WIFI_STATS_SPAN_COUNT) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid wifi_stats_span '%s' (must be report or window)",
+				   line, pos);
+			return 1;
+		}
+		conf->wifi_stats_span = mode;
+	} else if (os_strcmp(buf, "wifi_stats_ewma_weight") == 0) {
+		char *endptr;
+		unsigned long val = strtoul(pos, &endptr, 10);
+		if (endptr == pos || *endptr != '\0' ||
+		    val < WIFI_STATS_MIN_EWMA_WEIGHT ||
+		    val > WIFI_STATS_MAX_EWMA_WEIGHT) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid wifi_stats_ewma_weight '%s' (must be %d-%d)",
+				   line, pos, WIFI_STATS_MIN_EWMA_WEIGHT,
+				   WIFI_STATS_MAX_EWMA_WEIGHT);
+			return 1;
+		}
+		conf->wifi_stats_ewma_weight = (unsigned int)val;
+	} else if (os_strcmp(buf, "wifi_stats_wba_enabled") == 0) {
+		char *endptr;
+		long val = strtol(pos, &endptr, 10);
+		if (endptr == pos || *endptr != '\0' ||
+		    (val != 0 && val != 1)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid wifi_stats_wba_enabled '%s' (must be 0 or 1)",
+				   line, pos);
+			return 1;
+		}
+		conf->wifi_stats_wba_enabled = val;
+	} else if (os_strncmp(buf, WIFI_STATS_METRIC_PREFIX,
+			      os_strlen(WIFI_STATS_METRIC_PREFIX)) == 0) {
+		const char *metric_name = buf + os_strlen(WIFI_STATS_METRIC_PREFIX);
+		wifi_stats_metric_type_t metric_idx;
+		wifi_stats_agg_type_t algorithm;
+
+		metric_idx = wifi_stats_metric_type_from_str(metric_name);
+		if (metric_idx >= WIFI_STATS_METRIC_COUNT) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Unknown wifi_stats metric '%s'",
+				   line, metric_name);
+			return 1;
+		}
+
+		algorithm = wifi_stats_agg_type_from_str(pos);
+		if (algorithm >= WIFI_STATS_AGG_COUNT) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Unknown algorithm '%s'",
+				   line, pos);
+			return 1;
+		}
+
+		if (algorithm != WIFI_STATS_AGG_NONE) {
+			if (algorithm == WIFI_STATS_AGG_ACC &&
+			    metric_idx != WIFI_STATS_METRIC_FRAME_LOSS &&
+			    metric_idx != WIFI_STATS_METRIC_FRAME_RETRY) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: ACC algorithm only valid for frameloss/frameretry",
+					   line);
+				return 1;
+			}
+			if (algorithm != WIFI_STATS_AGG_ACC &&
+			    (metric_idx == WIFI_STATS_METRIC_FRAME_LOSS ||
+			     metric_idx == WIFI_STATS_METRIC_FRAME_RETRY)) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: frameloss/frameretry require ACC or NONE algorithm",
+					   line);
+				return 1;
+			}
+		}
+
+		conf->wifi_stats_metrics[metric_idx].configured = 1;
+		conf->wifi_stats_metrics[metric_idx].algorithm = algorithm;
+
+		wpa_printf(MSG_DEBUG,
+			   "wifi_stats: Configured metric=%s algorithm=%s",
+			   metric_name, pos);
+#endif /* CONFIG_WIFI_STATS */
 	} else if (os_strcmp(buf, "country_code") == 0) {
 		if (pos[0] < 'A' || pos[0] > 'Z' ||
 		    pos[1] < 'A' || pos[1] > 'Z') {
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 94021d31e..ab927497e 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -3704,3 +3704,74 @@ own_ip_addr=127.0.0.1
 #bridge=br-lan
 #wpa_key_mgmt=SAE
 #bssid=00:03:7f:12:84:85
+
+##### WiFi statistics and WBA Connect-Info ####################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_WIFI_STATS is set while compiling hostapd.
+#
+# When enabled, hostapd polls the driver periodically for per-station link
+# statistics and aggregates them over a sliding window. The aggregated values
+# are reported in the RADIUS Connect-Info attribute using the format defined
+# in draft-grayson-connectinfo-10 (Wireless Broadband Alliance).
+
+# Interval in seconds between driver polls for station statistics.
+# wifi_stats_interval is in 1..3600 range. Default is 1 second.
+#wifi_stats_interval=1
+
+# Length of the aggregation window in seconds. In wifi_stats_span=window mode,
+# samples collected within this window are aggregated into the values
+# reported in Connect-Info, and the window should be longer than
+# wifi_stats_interval; if it is not, aggregation is suppressed and all
+# metrics fall back to reporting instantaneous values. In
+# wifi_stats_span=report mode this value only bounds the accumulation period
+# and does not affect aggregation (see wifi_stats_span below).
+# wifi_stats_wba_window is in 1..3600 range. Default is 60 seconds.
+#wifi_stats_wba_window=60
+
+# How the aggregation span is determined.
+#  report - accumulate since the last accounting report was sent, as described
+#           in draft-grayson-connectinfo-10 Section 4.2. No sample history is
+#           retained. This is the default.
+#  window - aggregate over a fixed sliding window of wifi_stats_wba_window
+#           seconds, retaining the samples that fall within it.
+# In report mode wifi_stats_wba_window still bounds the accumulation period, so
+# that the reported span stays representable when no accounting server is
+# configured and nothing ever resets it.
+#wifi_stats_span=report
+
+# Exponential weight used by the AVG-EXP algorithm. The smoothing factor is
+# 1/(2^weight), so larger values smooth more heavily, and the weight is what
+# gets reported in place of a time window for AVG-EXP metrics.
+# wifi_stats_ewma_weight is in 1..9 range. Default is 5.
+#wifi_stats_ewma_weight=5
+
+# Whether to use the WBA Connect-Info format in the RADIUS Connect-Info
+# attribute. When disabled, the legacy "CONNECT <rate> Mbps <mode>" format is
+# used instead.
+# 0 = disabled, 1 = enabled (default)
+#wifi_stats_wba_enabled=1
+
+# Per-metric aggregation algorithm:
+# wifi_stats_metric_<metric>=<algorithm>
+#
+# <metric> is one of: rxbitrate, txbitrate, rssi, frameloss, frameretry
+#
+# <algorithm> is one of:
+#  MIN     - minimum value within the window
+#  MAX     - maximum value within the window
+#  AVG-LIN - linear (arithmetic mean) average within the window
+#  AVG-EXP - exponentially weighted moving average
+#  ACC     - accumulated percentage derived from driver counter deltas
+#  NONE    - no aggregation; report the most recent value
+#
+# The frameloss and frameretry metrics are derived from driver counters and
+# accept only ACC or NONE. The other metrics accept any algorithm except ACC.
+#
+# AVG-EXP reports its weight rather than a time window, so wifi_stats_wba_window
+# does not appear in the output for a metric using it.
+#
+# Defaults: rxbitrate=MAX, txbitrate=MAX, rssi=AVG-LIN, frameloss=ACC,
+# frameretry=ACC
+#wifi_stats_metric_rssi=AVG-LIN
+#wifi_stats_metric_frameloss=ACC
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 4e7d44da1..ad067a92c 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -318,6 +318,14 @@ struct hostapd_config * hostapd_config_defaults(void)
 
 	conf->disable_mcs15_rx = true;
 
+#ifdef CONFIG_WIFI_STATS
+	conf->wifi_stats_interval = WIFI_STATS_DEFAULT_INTERVAL;
+	conf->wifi_stats_wba_window = WIFI_STATS_DEFAULT_WINDOW;
+	conf->wifi_stats_wba_enabled = 1;
+	conf->wifi_stats_ewma_weight = WIFI_STATS_DEFAULT_EWMA_WEIGHT;
+	conf->wifi_stats_span = WIFI_STATS_SPAN_REPORT;
+#endif /* CONFIG_WIFI_STATS */
+
 	return conf;
 }
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 563639e76..feb095ecf 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -19,6 +19,9 @@
 #include "wps/wps.h"
 #include "fst/fst.h"
 #include "vlan.h"
+#ifdef CONFIG_WIFI_STATS
+#include "wifi_stats/wifi_stats.h"
+#endif /* CONFIG_WIFI_STATS */
 
 enum macaddr_acl {
 	ACCEPT_UNLESS_DENIED = 0,
@@ -1333,6 +1336,18 @@ struct hostapd_config {
 		int min_power;
 	} afc;
 #endif /* CONFIG_AFC */
+
+#ifdef CONFIG_WIFI_STATS
+	unsigned int wifi_stats_interval;
+	unsigned int wifi_stats_wba_window;
+	unsigned int wifi_stats_ewma_weight;
+	wifi_stats_span_mode_t wifi_stats_span;
+	int wifi_stats_wba_enabled;
+	struct wifi_stats_metric_config {
+		int configured;
+		wifi_stats_agg_type_t algorithm;
+	} wifi_stats_metrics[WIFI_STATS_METRIC_COUNT];
+#endif /* CONFIG_WIFI_STATS */
 };
 
 
-- 
2.43.0




More information about the Hostap mailing list