[PATCH v3 25/46] PR: Add dedicated PD wdev for PASN with custom MAC address
Kavita Kavita
kavita.kavita at oss.qualcomm.com
Wed May 13 02:59:49 PDT 2026
From: Peddolla Harshavardhan Reddy <peddolla.reddy at oss.qualcomm.com>
When PR PASN is triggered with a custom source address, create a
dedicated PD virtual interface (WPA_IF_PD) carrying that MAC before
the PASN exchange begins.
For the initiator role, the PD wdev is created via wpas_pr_start_pd()
before queuing the radio work. For the responder role, the ROC started
on the existing interface receives the incoming Auth1 frame, and the
PD wdev is created lazily on first M1 reception when a custom MAC was
requested. When no custom MAC is provided both roles fall back to the
existing interface.
wpas_pr_pd_stop() tears down the PD wdev on completion or failure.
A pd_addr field tracks the active PD wdev address and is used to route
set_key and TX status operations to the correct interface.
pr_responder_src_addr stores the requested MAC so it is available when
M1 arrives.
Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy at oss.qualcomm.com>
---
wpa_supplicant/pr_supplicant.c | 125 +++++++++++++++++++++++++++++-
wpa_supplicant/pr_supplicant.h | 5 ++
wpa_supplicant/wpa_supplicant_i.h | 10 +++
3 files changed, 138 insertions(+), 2 deletions(-)
diff --git a/wpa_supplicant/pr_supplicant.c b/wpa_supplicant/pr_supplicant.c
index 0c20be22f..6272c0b7c 100644
--- a/wpa_supplicant/pr_supplicant.c
+++ b/wpa_supplicant/pr_supplicant.c
@@ -514,6 +514,22 @@ void wpas_pr_deinit(struct wpa_supplicant *wpa_s)
}
+void wpas_pr_pd_stop(struct wpa_supplicant *wpa_s)
+{
+ if (is_zero_ether_addr(wpa_s->pd_addr)) {
+ wpa_printf(MSG_DEBUG, "PR: pd_stop: no active PD wdev");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "PR: Stopping PD wdev addr=" MACSTR,
+ MAC2STR(wpa_s->pd_addr));
+
+ wpa_drv_pd_stop(wpa_s);
+ os_memset(wpa_s->pd_addr, 0, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "PR: PD wdev stopped");
+}
+
+
void wpas_pr_update_dev_addr(struct wpa_supplicant *wpa_s)
{
pr_set_dev_addr(wpa_s->global->pr, wpa_s->own_addr);
@@ -546,6 +562,41 @@ void wpas_pr_set_dev_ik(struct wpa_supplicant *wpa_s, const u8 *dik,
#ifdef CONFIG_PASN
+static int wpas_pr_start_pd(struct wpa_supplicant *wpa_s, const u8 *src_addr)
+{
+ u8 pd_addr[ETH_ALEN];
+
+ if (!src_addr || is_zero_ether_addr(src_addr)) {
+ wpa_printf(MSG_ERROR, "PR: Invalid MAC address for PD wdev");
+ return -1;
+ }
+
+ if (!is_zero_ether_addr(wpa_s->pd_addr)) {
+ wpa_printf(MSG_ERROR,
+ "PR: PD wdev already active addr=" MACSTR,
+ MAC2STR(wpa_s->pd_addr));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "PR: Creating PD wdev with MAC " MACSTR,
+ MAC2STR(src_addr));
+
+ os_memset(pd_addr, 0, ETH_ALEN);
+ if (wpa_drv_pd_start(wpa_s, src_addr, pd_addr) < 0) {
+ wpa_printf(MSG_ERROR, "PR: Failed to create PD wdev");
+ return -1;
+ }
+
+ os_memcpy(wpa_s->pd_addr, pd_addr, ETH_ALEN);
+ pr_set_dev_addr(wpa_s->global->pr, pd_addr);
+
+ wpa_printf(MSG_DEBUG,
+ "PR: PD wdev created addr=" MACSTR, MAC2STR(pd_addr));
+ return 0;
+}
+
+
struct wpa_pr_pasn_auth_work {
u8 peer_addr[ETH_ALEN];
u8 auth_mode;
@@ -628,6 +679,7 @@ static void wpas_pr_schedule_responder_roc(struct wpa_supplicant *wpa_s,
fail:
wpa_s->pr_responder_mode = false;
+ os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN);
eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout, wpa_s, NULL);
}
@@ -644,6 +696,7 @@ static void wpas_pr_pasn_roc_total_timeout(void *eloop_ctx,
"PR PASN: Total ROC budget expired, "
"stopping responder listen");
wpa_s->pr_responder_mode = false;
+ os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN);
if (wpa_s->pr_roc_work) {
wpa_drv_cancel_remain_on_channel(wpa_s);
@@ -683,6 +736,7 @@ static void wpas_pr_pasn_roc_start_cb(struct wpa_radio_work *work, int deinit)
eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout,
wpa_s, NULL);
wpa_s->pr_responder_mode = false;
+ os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN);
os_free(rwork);
work->ctx = NULL;
return;
@@ -707,6 +761,7 @@ static void wpas_pr_pasn_roc_start_cb(struct wpa_radio_work *work, int deinit)
eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout,
wpa_s, NULL);
wpa_s->pr_responder_mode = false;
+ os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN);
os_free(rwork);
work->ctx = NULL;
radio_work_done(work);
@@ -755,6 +810,16 @@ static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx)
wpas_pr_pasn_cancel_auth_work(wpa_s);
wpa_s->pr_pasn_auth_work = NULL;
}
+
+ /*
+ * Stop the PD wdev only after radio_work_done() has
+ * fully returned. Calling wpas_pr_pd_stop() from inside
+ * the radio-work deinit callback would trigger a re-entrant
+ * radio_remove_works() → radio_work_free() on the same work item,
+ * causing a use-after-free / SIGSEGV.
+ */
+ wpas_pr_pd_stop(wpa_s);
+
wpa_printf(MSG_DEBUG, "PR: PASN timed out");
}
@@ -797,6 +862,8 @@ fail:
wpas_pr_pasn_free_auth_work(awork);
work->ctx = NULL;
radio_work_done(work);
+ /* Stop PD wdev after radio_work_done() to avoid use-after-free */
+ wpas_pr_pd_stop(wpa_s);
}
@@ -810,14 +877,31 @@ int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
if (pasn_role == PR_ROLE_PASN_RESPONDER) {
struct wpa_pr_pasn_roc_work *rwork;
+ bool has_src_addr = src_addr && !is_zero_ether_addr(src_addr);
+
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Scheduling ROC at freq %d for responder role%s",
+ freq, has_src_addr ? " with custom MAC" : "");
rwork = os_zalloc(sizeof(*rwork));
if (!rwork)
return -1;
rwork->freq = freq;
+ if (has_src_addr)
+ os_memcpy(rwork->src_addr, src_addr, ETH_ALEN);
+ /* else rwork->src_addr stays all-zeros (no MAC filter on ROC) */
+ /*
+ * Store state so wpas_pr_pasn_auth_rx can create the PD
+ * interface when M1 arrives. When no custom MAC is given the
+ * PD wdev is skipped and the existing interface is used.
+ */
wpa_s->pr_responder_mode = true;
+ if (has_src_addr)
+ os_memcpy(wpa_s->pr_responder_src_addr, src_addr,
+ ETH_ALEN);
+ /* else pr_responder_src_addr stays all-zeros */
if (radio_add_work(wpa_s, freq, "pr-pasn-roc", 0,
wpas_pr_pasn_roc_start_cb, rwork) < 0) {
@@ -825,6 +909,7 @@ int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
"PR PASN: Failed to schedule ROC for responder");
os_free(rwork);
wpa_s->pr_responder_mode = false;
+ os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN);
return -1;
}
@@ -839,12 +924,26 @@ int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
return 0;
}
+ /*
+ * PASN initiator role: create the PD wdev if src_addr is provided,
+ * then queue the radio work to send M1.
+ */
+ if (src_addr && !is_zero_ether_addr(src_addr)) {
+ if (wpas_pr_start_pd(wpa_s, src_addr) < 0) {
+ wpa_printf(MSG_ERROR,
+ "PR PASN: Failed to create PD wdev");
+ return -1;
+ }
+ }
+
wpas_pr_pasn_cancel_auth_work(wpa_s);
wpa_s->pr_pasn_auth_work = NULL;
awork = os_zalloc(sizeof(*awork));
- if (!awork)
+ if (!awork) {
+ wpas_pr_pd_stop(wpa_s);
return -1;
+ }
awork->freq = freq;
os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
@@ -856,6 +955,7 @@ int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
if (!radio_add_work(wpa_s, freq, "pr-pasn-start-auth", 1,
wpas_pr_pasn_auth_start_cb, awork)) {
wpas_pr_pasn_free_auth_work(awork);
+ wpas_pr_pd_stop(wpa_s);
return -1;
}
@@ -870,7 +970,8 @@ int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
{
struct pr_data *pr = wpa_s->global->pr;
- if (!wpa_s->pr_pasn_auth_work)
+ if (!wpa_s->pr_pasn_auth_work &&
+ is_zero_ether_addr(wpa_s->pd_addr))
return -1;
return pr_pasn_auth_tx_status(pr, data, data_len, acked);
@@ -898,6 +999,25 @@ int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s,
le_to_host16(mgmt->u.auth.auth_transaction);
if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
+ if (!is_zero_ether_addr(wpa_s->pr_responder_src_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: M1 received in responder "
+ "mode, creating PD wdev");
+
+ if (wpas_pr_start_pd(wpa_s,
+ wpa_s->pr_responder_src_addr) < 0) {
+ wpa_printf(MSG_ERROR,
+ "PR PASN: Failed to create "
+ "PD wdev for responder");
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: M1 received in responder "
+ "mode, no custom MAC - using "
+ "existing interface");
+ }
+
/*
* Cancel the total-budget timer first so it does not
* fire after we have already handed off to the PASN
@@ -924,6 +1044,7 @@ int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s,
/* Clear responder mode */
wpa_s->pr_responder_mode = false;
+ os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN);
wpa_printf(MSG_DEBUG,
"PR PASN: M1 processed, proceeding with "
diff --git a/wpa_supplicant/pr_supplicant.h b/wpa_supplicant/pr_supplicant.h
index 7cf29705e..747bb900a 100644
--- a/wpa_supplicant/pr_supplicant.h
+++ b/wpa_supplicant/pr_supplicant.h
@@ -17,6 +17,7 @@ int wpas_pr_init(struct wpa_global *global, struct wpa_supplicant *wpa_s,
const struct wpa_driver_capa *capa);
void wpas_pr_flush(struct wpa_supplicant *wpa_s);
void wpas_pr_deinit(struct wpa_supplicant *wpa_s);
+void wpas_pr_pd_stop(struct wpa_supplicant *wpa_s);
void wpas_pr_update_dev_addr(struct wpa_supplicant *wpa_s);
void wpas_pr_clear_dev_iks(struct wpa_supplicant *wpa_s);
void wpas_pr_set_dev_ik(struct wpa_supplicant *wpa_s, const u8 *dik,
@@ -56,6 +57,10 @@ static inline void wpas_pr_deinit(struct wpa_supplicant *wpa_s)
{
}
+static inline void wpas_pr_pd_stop(struct wpa_supplicant *wpa_s)
+{
+}
+
static inline void wpas_pr_update_dev_addr(struct wpa_supplicant *wpa_s)
{
}
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 85994de80..3394ab4cc 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1758,6 +1758,8 @@ struct wpa_supplicant {
u8 pmkid_anonce[NONCE_LEN];
bool pmkid_anonce_set;
#endif /* CONFIG_PMKSA_PRIVACY */
+ u8 pd_addr[ETH_ALEN];
+
/**
* pr_responder_mode - Waiting for PASN M1 as responder
*
@@ -1766,6 +1768,14 @@ struct wpa_supplicant {
* is created on M1 reception.
*/
bool pr_responder_mode;
+
+ /**
+ * pr_responder_src_addr - Source MAC address used for responder ROC
+ *
+ * Stored when responder mode is activated so that the dedicated PR
+ * interface can be created with the same address when M1 arrives.
+ */
+ u8 pr_responder_src_addr[ETH_ALEN];
};
--
2.34.1
More information about the Hostap
mailing list