[PATCH 94/97] wpa_supplicant: Support overriding NAN potential availability

Andrei Otcheretianski andrei.otcheretianski at intel.com
Tue Apr 28 13:06:35 PDT 2026


Add a control interface API to configure NAN potential availability.
This overrides the internally calculated potential availability.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 wpa_supplicant/nan_supplicant.c   | 232 ++++++++++++++++++++++++++----
 wpa_supplicant/wpa_supplicant_i.h |   1 +
 2 files changed, 208 insertions(+), 25 deletions(-)

diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index d53cc206db..a7eabc3ff6 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -78,45 +78,51 @@ static int get_center(u8 channel, const u8 *center_channels,
 }
 
 
-static bool wpas_nan_valid_chan(struct wpa_supplicant *wpa_s,
-				enum hostapd_hw_mode mode,
-				u8 channel, int bw, u8 op_class, u8 *cf1)
+static u8 get_center_and_width(int bw, u8 channel, int *width)
 {
 	static const u8 nan_160mhz_5ghz_chans[] = { 50, 114, 163 };
 	static const u8 nan_80mhz_5ghz_chans[] =
 		{ 42, 58, 106, 122, 138, 155, 171 };
-	struct hostapd_hw_modes *hw_mode;
-	int width, span;
-	u8 c, center = 0;
-
-	hw_mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, mode, false);
-	if (!hw_mode)
-		return false;
 
 	switch (bw) {
 	case BW20:
-		width = 20;
-		center = channel;
-		break;
+		*width = 20;
+		return channel;
 	case BW40PLUS:
 	case BW40MINUS:
-		width = 40;
-		center = bw == BW40PLUS ? channel + 2 : channel - 2;
-		break;
+		*width = 40;
+		return bw == BW40PLUS ? channel + 2 : channel - 2;
 	case BW80:
-		width = 80;
-		center = get_center(channel, nan_80mhz_5ghz_chans,
-				    ARRAY_SIZE(nan_80mhz_5ghz_chans), width);
-		break;
+		*width = 80;
+		return get_center(channel, nan_80mhz_5ghz_chans,
+				  ARRAY_SIZE(nan_80mhz_5ghz_chans),
+				  *width);
 	case BW160:
-		width = 160;
-		center = get_center(channel, nan_160mhz_5ghz_chans,
-				    ARRAY_SIZE(nan_160mhz_5ghz_chans), width);
-		break;
+		*width = 160;
+		return get_center(channel, nan_160mhz_5ghz_chans,
+				  ARRAY_SIZE(nan_160mhz_5ghz_chans),
+				  *width);
 	default:
-		return false;
+		return 0;
 	}
 
+	return 0;
+}
+
+
+static bool wpas_nan_valid_chan(struct wpa_supplicant *wpa_s,
+				enum hostapd_hw_mode mode,
+				u8 channel, int bw, u8 op_class, u8 *cf1)
+{
+	struct hostapd_hw_modes *hw_mode;
+	int width, span;
+	u8 c, center;
+
+	hw_mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, mode, false);
+	if (!hw_mode)
+		return false;
+
+	center = get_center_and_width(bw, channel, &width);
 	if (!center)
 		return false;
 
@@ -838,6 +844,26 @@ static int wpas_nan_get_chans_cb(void *ctx, u8 map_id,
 
 	wpa_printf(MSG_DEBUG, "NAN: Get channels - map_id=%u", map_id);
 
+	/* Check if override is configured */
+	if (wpa_s->nan_override_potential_avail.n_chans > 0) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Using override potential availability (%u channels)",
+			   wpa_s->nan_override_potential_avail.n_chans);
+
+		chans->n_chans = wpa_s->nan_override_potential_avail.n_chans;
+		chans->chans = os_memdup(wpa_s->nan_override_potential_avail.chans,
+				      wpa_s->nan_override_potential_avail.n_chans *
+				      sizeof(struct nan_channel_info));
+		if (!chans->chans) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Failed to allocate memory for override channels");
+			chans->n_chans = 0;
+			return -1;
+		}
+
+		return 0;
+	}
+
 	/* Allocate one extra element so it will be 0 terminated int_array */
 	shared_freqs = os_calloc(wpa_s->num_multichan_concurrent + 1,
 				 sizeof(int));
@@ -1470,6 +1496,10 @@ void wpas_nan_deinit(struct wpa_supplicant *wpa_s)
 	wpabuf_free(wpa_s->nan_ulw_attr);
 	wpa_s->nan_ulw_attr = NULL;
 
+	os_free(wpa_s->nan_override_potential_avail.chans);
+	wpa_s->nan_override_potential_avail.chans = NULL;
+	wpa_s->nan_override_potential_avail.n_chans = 0;
+
 	wpa_s->nan = NULL;
 }
 
@@ -1519,6 +1549,155 @@ void wpas_nan_flush(struct wpa_supplicant *wpa_s)
 }
 
 
+static int wpas_nan_parse_override_potential_avail(struct wpa_supplicant *wpa_s,
+						   char *param)
+{
+	struct nan_channel_info *chans = NULL;
+	size_t n_chans = 0;
+	size_t capacity = 0;
+	char *pos, *end;
+
+	/* Empty string clears the override */
+	if (*param == '\0') {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Clearing override potential availability");
+		goto out;
+	}
+
+	/* Parse format: <op_class:0xbitmap:pref>,... */
+	pos = param;
+	while (pos && *pos) {
+		u8 op_class, pref;
+		u16 bitmap;
+		const struct oper_class_map *o = NULL;
+		int op, idx;
+
+		if (sscanf(pos, "%hhu:0x%hx:%hhu", &op_class, &bitmap, &pref) != 3) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Invalid override_potential_availability format at '%s'",
+				   pos);
+			os_free(chans);
+			return -1;
+		}
+
+		if (!op_class || op_class > 129 || pref > 3) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Invalid values in override_potential_availability");
+			os_free(chans);
+			return -1;
+		}
+
+		/* Find the operating class in global_op_class */
+		for (op = 0; global_op_class[op].op_class; op++) {
+			if (global_op_class[op].op_class == op_class) {
+				o = &global_op_class[op];
+				break;
+			}
+		}
+
+		if (!o) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Unknown operating class %d in override_potential_availability",
+				   op_class);
+			os_free(chans);
+			return -1;
+		}
+
+		/* Iterate through bitmap bits */
+		for (idx = 0; idx < 16 && bitmap; idx++) {
+			u8 chan, center;
+
+			if (!(bitmap & BIT(idx)))
+				continue;
+
+			chan = op_class_idx_to_chan(o, idx);
+			if (!chan) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Invalid channel index %d for op_class %d",
+					   idx, op_class);
+				os_free(chans);
+				return -1;
+			}
+
+			/*
+			 * Validate the channel. For zero preference only
+			 * check the very basic validity, but accept
+			 * "NOT ALLOWED" channels, as the user might want
+			 * to explicitly mark them as unavailable.
+			 */
+			if (pref && !wpas_nan_valid_chan(wpa_s, o->mode, chan,
+							 o->bw, o->op_class,
+							 &center)) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Channel %d (op_class %d) is not a valid NAN channel",
+					   chan, op_class);
+				os_free(chans);
+				return -1;
+			} else if (!pref) {
+				int width;
+
+				center = get_center_and_width(o->bw, chan,
+							      &width);
+				if (!center) {
+					wpa_printf(MSG_DEBUG,
+						   "NAN: Invalid channel %d for op_class %d",
+						   chan, op_class);
+					os_free(chans);
+					return -1;
+				}
+			}
+
+			/* Expand array if needed */
+			if (n_chans >= capacity) {
+				struct nan_channel_info *new_chans;
+
+				capacity = capacity ? capacity * 2 : 4;
+				new_chans = os_realloc_array(chans, capacity,
+							     sizeof(*chans));
+				if (!new_chans) {
+					wpa_printf(MSG_DEBUG,
+						   "NAN: Memory allocation failed");
+					os_free(chans);
+					return -1;
+				}
+				chans = new_chans;
+			}
+
+			/* Use center for wide channels */
+			chans[n_chans].op_class = op_class;
+			chans[n_chans].channel = (o->bw == BW80 ||
+						  o->bw == BW160) ?
+						 center : chan;
+			chans[n_chans].pref = pref;
+			n_chans++;
+		}
+
+		/* Move to next entry */
+		end = os_strchr(pos, ',');
+		if (end)
+			pos = end + 1;
+		else
+			break;
+	}
+
+	/* Sort channels by preference (higher preference first) */
+	if (n_chans > 1)
+		qsort(chans, n_chans, sizeof(*chans), nan_chan_info_cmp);
+
+out:
+	/* Free previous configuration */
+	os_free(wpa_s->nan_override_potential_avail.chans);
+	wpa_s->nan_override_potential_avail.chans = chans;
+	wpa_s->nan_override_potential_avail.n_chans = n_chans;
+	wpa_s->schedule_sequence_id++;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Configured %zu override potential availability channels",
+		   n_chans);
+	return 0;
+}
+
+
 int wpas_nan_set(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	struct nan_cluster_config *config = &wpa_s->nan_cluster_config;
@@ -1611,6 +1790,9 @@ int wpas_nan_set(struct wpa_supplicant *wpa_s, char *cmd)
 		return 0;
 	}
 
+	if (os_strcmp("override_potential_availability", cmd) == 0)
+		return wpas_nan_parse_override_potential_avail(wpa_s, param);
+
 	if (os_strcmp("bootstrap_config", cmd) == 0) {
 		u16 supported_methods, auto_accept_methods, comeback_timeout;
 
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 95c0f577d4..828795b196 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1743,6 +1743,7 @@ struct wpa_supplicant {
 	struct wpabuf *nan_ulw_attr;
 	struct wpa_freq_range_list nan_disallowed_freqs;
 	u16 nan_max_bw;
+	struct nan_channels nan_override_potential_avail;
 	unsigned int nan_ndi_ndp_refcount; /* Active NDP count on this NDI */
 	struct nan_gtk ndi_gtk;
 #endif /* CONFIG_NAN */
-- 
2.53.0




More information about the Hostap mailing list