[RFC PATCH 03/23] nl80211: Add MAC address filter support for remain-on-channel

Peddolla Harshavardhan Reddy peddolla.reddy at oss.qualcomm.com
Tue Mar 31 22:48:41 PDT 2026


Currently, the remain_on_channel driver interface does not support
filtering received frames by a specific MAC address. This prevents
use cases like Proximity Ranging from listening for frames addressed
to a custom MAC address that differs from the interface address.

Add a filter_addr parameter to the remain_on_channel driver operation
and its wpa_drv_remain_on_channel() wrapper. When provided, the
nl80211 driver adds NL80211_ATTR_MAC to the remain-on-channel command
to configure hardware-level frame filtering. A new driver capability
flag WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER, detected from the kernel's
NL80211_EXT_FEATURE_ROC_ADDR_FILTER, guards this behavior. All
existing callers are updated to pass NULL to maintain backward
compatibility.

Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy at oss.qualcomm.com>
---
 src/drivers/driver.h              | 10 +++++++++-
 src/drivers/driver_nl80211.c      | 27 +++++++++++++++++++++++----
 src/drivers/driver_nl80211_capa.c |  3 +++
 wpa_supplicant/dpp_supplicant.c   |  2 +-
 wpa_supplicant/driver_i.h         |  5 +++--
 wpa_supplicant/nan_supplicant.c   |  2 +-
 wpa_supplicant/offchannel.c       |  4 ++--
 wpa_supplicant/p2p_supplicant.c   |  2 +-
 8 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 1e4a3da35..268229edd 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2444,6 +2444,8 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS2_EPPKE				0x0000000400000000ULL
 /** Driver supports IEEE 802.1X authentication in Authentication frames */
 #define WPA_DRIVER_FLAGS2_802_1X_AUTH		0x0000000800000000ULL
+/** Driver supports MAC address filter for remain-on-channel */
+#define WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER	0x0000001000000000ULL
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -4312,6 +4314,8 @@ struct wpa_driver_ops {
 	 * @priv: Private driver interface data
 	 * @freq: Frequency (in MHz) of the channel
 	 * @duration: Duration in milliseconds
+	 * @filter_addr: MAC address to filter received frames (NULL for no
+	 *	filter)
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This command is used to request the driver to remain awake on the
@@ -4320,6 +4324,10 @@ struct wpa_driver_ops {
 	 * Probe Request frames may also be requested to be reported by calling
 	 * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
 	 *
+	 * If filter_addr is provided, the driver will configure an additional MAC
+	 * address for frame filtering. i.e., this new address would be used in
+	 * addition to the interface address.
+	 *
 	 * The driver may not be at the requested channel when this function
 	 * returns, i.e., the return code is only indicating whether the
 	 * request was accepted. The caller will need to wait until the
@@ -4330,7 +4338,7 @@ struct wpa_driver_ops {
 	 * executed.
 	 */
 	int (*remain_on_channel)(void *priv, unsigned int freq,
-				 unsigned int duration);
+				 unsigned int duration, const u8 *filter_addr);
 
 	/**
 	 * cancel_remain_on_channel - Cancel remain-on-channel operation
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 52c561dfc..297f363f4 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -9868,7 +9868,8 @@ static int nl80211_put_any_link_id(struct nl_msg *msg,
 
 
 static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
-						unsigned int duration)
+						unsigned int duration,
+						const u8 *filter_addr)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9884,12 +9885,30 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
 		return -1;
 	}
 
+	/* Add MAC address filter if provided and supported */
+	if (filter_addr) {
+		if (!(drv->capa.flags2 & WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER)) {
+			wpa_printf(MSG_ERROR,
+				   "nl80211: Driver does not support ROC address filter");
+			nlmsg_free(msg);
+			return -1;
+		}
+
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, filter_addr)) {
+			wpa_printf(MSG_ERROR,
+				   "nl80211: Failed to add MAC address filter");
+			nlmsg_free(msg);
+			return -1;
+		}
+	}
+
 	cookie = 0;
 	ret = send_and_recv_resp(drv, msg, cookie_handler, &cookie);
 	if (ret == 0) {
-		wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
-			   "0x%llx for freq=%u MHz duration=%u",
-			   (long long unsigned int) cookie, freq, duration);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Remain-on-channel cookie 0x%llx for freq=%u MHz duration=%u%s",
+			   (unsigned long long) cookie, freq, duration,
+			   filter_addr ? " (with MAC filter)" : "");
 		drv->remain_on_chan_cookie = cookie;
 		drv->pending_remain_on_chan = 1;
 		return 0;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 1ee021cd9..b8bfb6613 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -586,6 +586,9 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
 		capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_ROC_ADDR_FILTER))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER;
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
 		capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index ab9d81593..a55d0e7ab 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -1034,7 +1034,7 @@ static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit)
 	wpa_s->dpp_pending_listen_freq = lwork->freq;
 
 	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq,
-				      wpa_s->max_remain_on_chan) < 0) {
+				      wpa_s->max_remain_on_chan, NULL) < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to request the driver to remain on channel (%u MHz) for listen",
 			   lwork->freq);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 60eb31ca1..5a8298288 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -460,11 +460,12 @@ static inline int wpa_drv_if_remove(struct wpa_supplicant *wpa_s,
 
 static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s,
 					    unsigned int freq,
-					    unsigned int duration)
+					    unsigned int duration,
+					    const u8 *filter_addr)
 {
 	if (wpa_s->driver->remain_on_channel)
 		return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq,
-							duration);
+							duration, filter_addr);
 	return -1;
 }
 
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index 1eac1fb78..362dd75e3 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -523,7 +523,7 @@ static void wpas_nan_usd_start_listen_cb(struct wpa_radio_work *work,
 		duration = wpa_s->max_remain_on_chan;
 	wpa_printf(MSG_DEBUG, "NAN: Start listen on %u MHz for %u ms",
 		   lwork->freq, duration);
-	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) {
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration, NULL) < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "NAN: Failed to request the driver to remain on channel (%u MHz) for listen",
 			   lwork->freq);
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 9e591d7d6..688718cd7 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -124,7 +124,7 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
 #endif /* CONFIG_TESTING_OPTIONS */
 			if (wpa_drv_remain_on_channel(
 				    wpa_s, wpa_s->pending_action_freq,
-				    duration) < 0) {
+				    duration, NULL) < 0) {
 				wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
 					   "request driver to remain on "
 					   "channel (%u MHz) for Action Frame "
@@ -369,7 +369,7 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
 		wait_time += wpa_s->extra_roc_dur;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+	if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time, NULL) < 0) {
 		wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
 			   "to remain on channel (%u MHz) for Action "
 			   "Frame TX", freq);
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 154322f8a..b8a9d60b8 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -3253,7 +3253,7 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) {
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration, NULL) < 0) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
 			   "to remain on channel (%u MHz) for Listen "
 			   "state", lwork->freq);
-- 
2.34.1




More information about the Hostap mailing list