[PATCH v2 3/3] PR PASN: Handle lost M3 and lost M3 ACK (align with P2P GO Neg Confirm)
Emily Xia
xiayucheng at google.com
Fri Oct 30 02:23:15 PDT 2026
When PASN Auth3 (M3) is received by the Responder
(pr_pasn_handle_auth_3()) and the Responder immediately starts ranging,
if the 802.11 ACK for M3 is lost over the air (acked=0), the Initiator
previously gated pasn_result and get_ranging_params on (ret == 1 &&
acked) in pr_pasn_auth_tx_status(), freed pasn->frame, and never retried
M3 or started ranging. Because wpas_pr_pasn_auth_tx_status() returned 0,
the Initiator stayed wedged until the 10s wpas_pr_pasn_timeout expired
(PR_SESSION_END_NEG_FAILED) while the Responder was already ranging. On
the Responder side, an ACKed M2 followed by a lost M3 waited the full
10s failsafe timeout before tearing down.
Composing with the M1/M2 retry and replay recovery and aligning with P2P
GO Negotiation (p2p_go_neg_conf_cb and p2p_go_neg_resp_cb in
src/p2p/p2p_go_neg.c):
1. Initiator M3 retry, optimistic completion, and late M1 TX-status guard:
- Add per-peer u8 pasn_m3_retries to struct pr_device (under
CONFIG_PASN) and reset it to 0 in pr_reset_pasn_state() and
pr_pasn_initialize().
- In pr_pasn_auth_tx_status(), ignore stale TX status when
mgmt->u.auth.auth_transaction != pasn->trans_seq before calling
wpa_pasn_auth_tx_status() and return 0, so a delayed M1 TX status
arriving after M2 RX (on drivers that hold cfg80211_mgmt_tx_status()
until the off-channel dwell ends on receipt of the next RX frame)
does not free the M3 frame stored in pasn->frame, and a delayed M2
TX status arriving after M3 RX is ignored.
- In pr_pasn_auth_tx_status() (with PR_PASN_M3_MAX_RETRY = 1), when
wpa_pasn_auth_tx_status() returns 1 for M3:
* If !pasn->frame, PASN already completed and handed off to ranging
on the negotiated channel; ignore the duplicate/late Auth3 TX
status and return 0.
* On the first unacked M3 (!acked && pasn_m3_retries < 1),
increment pasn_m3_retries and return 2 while preserving
pasn->frame, so wpas_pr_pasn_auth_tx_status() schedules
wpas_pr_pasn_auth_retry_timeout() to retransmit M3 after 100ms
from eloop timer context (with 100ms wait matching initial M3).
If send_mgmt fails in timer context, pr_pasn_auth_retransmit()
frees pasn->frame and completes optimistically via
pr_pasn_complete_auth3().
* Otherwise (M3 ACKed, or the single retry was exhausted), complete
PASN via pr_pasn_complete_auth3() without gating on acked, log
when completing optimistically on an unacked M3, and free
pasn->frame.
- Extend the trans_seq guard in pr_pasn_auth_retransmit() to allow
WLAN_AUTH_TR_SEQ_PASN_AUTH3 alongside AUTH1 and AUTH2.
2. Responder 1000ms M3 wait timer, role/state helper, and timer symmetry:
- Keep the 3000ms recovery ROC and timeout on unacked Responder M2
(ret == 4) and duplicate-M1 M2 replay.
- Add PR_PASN_M3_WAIT_TIMEOUT_MS (1000ms) and
wpas_pr_pasn_m3_wait_timeout() in pr_supplicant.c, armed via
eloop_register_timeout(0, PR_PASN_M3_WAIT_TIMEOUT_MS * 1000, ...)
on ACKed Responder M2 TX status (ret == 0, trans_seq == AUTH2) so a
lost M3 after an ACKed M2 tears down in 1s rather than waiting for
the 10s failsafe timeout.
- Add wpas_pr_cancel_pasn_timeouts() cancelling wpas_pr_pasn_timeout,
wpas_pr_pasn_m3_wait_timeout, and wpas_pr_pasn_auth_retry_timeout
across all completion, timeout, and teardown paths (and cancel
wpas_pr_pasn_m3_wait_timeout when duplicate/new M1 refreshes the
Responder timer) so no stale timer can fire twice.
- Add pr_is_pasn_responder_waiting_m3(pr, addr) checking
dev->pasn_role == PR_ROLE_PASN_RESPONDER, dev->pasn != NULL, and
dev->pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2 (waiting for M3),
and use it in wpas_pr_pasn_auth_tx_status() and wpas_pr_pasn_auth_rx().
3. Return code semantics across Auth1, Auth2, and Auth3 handlers:
- Make pr_pasn_handle_auth_1(), pr_pasn_handle_auth_2(), and
pr_pasn_handle_auth_3() return 1 only when the frame is accepted and
a valid state transition occurs (M2 sent/replayed, M3 sent, or M3
accepted and ranging params requested), 0 when ignored, and < 0 on
error.
- Prevent wpas_pr_pasn_auth_rx() from prematurely cancelling active M1
or M3 retry timers on the initiator upon receiving late duplicate or
out-of-order Auth2 frames.
- Prevent wpas_pr_pasn_auth_rx() from resetting the responder's 1s M3
wait timer or 10s failsafe timeout when an incoming duplicate Auth1 is
ignored or its retransmission fails.
- Add clean error cleanup (cancel ROC, abort responder, notify
termination) in wpas_pr_check_pd_wdev_create() if interface creation
fails.
Signed-off-by: Emily Xia <xiayucheng at google.com>
Signed-off-by: Sunil Ravi <sunilravi at google.com>
---
Changes in v2:
- In wpas_pr_cancel_remain_on_channel_cb(), verify that the responder is
still waiting for M3 (pr_is_pasn_responder_waiting_m3) before restarting
the next ROC chunk; otherwise, cancel all active PASN timeouts and
abort cleanly.
- Adapt wpas_pr_schedule_responder_roc() call to match the 2-parameter
signature from Patch 2.
src/common/proximity_ranging.c | 191 +++++++++++++++++++++++++--------
src/common/proximity_ranging.h | 2 +
wpa_supplicant/pr_supplicant.c | 126 ++++++++++++++++------
3 files changed, 245 insertions(+), 74 deletions(-)
diff --git a/src/common/proximity_ranging.c b/src/common/proximity_ranging.c
index 4d1058032..44b45349e 100644
--- a/src/common/proximity_ranging.c
+++ b/src/common/proximity_ranging.c
@@ -145,6 +145,7 @@ void pr_reset_pasn_state(struct pr_data *pr, const u8 *addr)
if (dev) {
dev->pasn_role = PR_ROLE_IDLE;
#ifdef CONFIG_PASN
+ dev->pasn_m3_retries = 0;
if (dev->pasn)
wpa_pasn_reset(dev->pasn);
#endif /* CONFIG_PASN */
@@ -155,6 +156,7 @@ void pr_reset_pasn_state(struct pr_data *pr, const u8 *addr)
dl_list_for_each(dev, &pr->devices, struct pr_device, list) {
dev->pasn_role = PR_ROLE_IDLE;
#ifdef CONFIG_PASN
+ dev->pasn_m3_retries = 0;
if (dev->pasn)
wpa_pasn_reset(dev->pasn);
#endif /* CONFIG_PASN */
@@ -1820,6 +1822,7 @@ static int pr_pasn_initialize(struct pr_data *pr, struct pr_device *dev,
struct wpabuf *rsnxe;
struct pasn_data *pasn;
+ dev->pasn_m3_retries = 0;
if (dev->pasn) {
wpa_pasn_reset(dev->pasn);
} else {
@@ -2131,6 +2134,48 @@ out:
}
+#define PR_PASN_M3_MAX_RETRY 1
+
+static void pr_pasn_complete_auth3(struct pr_data *pr, struct pr_device *dev)
+{
+ u8 self_format_bw, peer_format_bw;
+
+ if (pr->cfg->pasn_result)
+ pr->cfg->pasn_result(pr->cfg->cb_ctx, dev->ranging_role,
+ dev->protocol_type, dev->final_op_class,
+ dev->final_op_channel, pr->cfg->country);
+
+ if (pr->cfg->get_ranging_params) {
+ if (dev->protocol_type & PR_EDCA_BASED_RANGING) {
+ self_format_bw = pr->cfg->edca_format_and_bw;
+ peer_format_bw = dev->edca_caps.edca_hw_caps &
+ EDCA_FORMAT_AND_BW_MASK;
+ } else if ((dev->protocol_type &
+ PR_NTB_SECURE_LTF_BASED_RANGING) ||
+ (dev->protocol_type & PR_NTB_OPEN_BASED_RANGING)) {
+ self_format_bw = pr->cfg->ntb_format_and_bw;
+ peer_format_bw = dev->ntb_caps.ntb_hw_caps &
+ NTB_FORMAT_AND_BW_MASK;
+ } else {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Invalid protocol type: %u",
+ dev->protocol_type);
+ return;
+ }
+
+ pr->cfg->get_ranging_params(pr->cfg->cb_ctx, pr->cfg->dev_addr,
+ dev->pr_device_addr,
+ dev->ranging_role,
+ dev->protocol_type,
+ dev->final_op_class,
+ dev->final_op_channel,
+ self_format_bw,
+ peer_format_bw);
+ pr_flush(pr);
+ }
+}
+
+
int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
bool acked)
{
@@ -2139,7 +2184,6 @@ int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
struct pasn_data *pasn;
const struct ieee80211_mgmt *mgmt =
(const struct ieee80211_mgmt *) data;
- u8 self_format_bw, peer_format_bw;
if (!pr || !data ||
data_len < offsetof(struct ieee80211_mgmt, u.auth.variable))
@@ -2158,6 +2202,19 @@ int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
wpa_printf(MSG_DEBUG, "PR PASN: TX status from " MACSTR " ack=%d",
MAC2STR(mgmt->da), acked);
+ /*
+ * Ignore stale TX status when trans_seq has already advanced
+ * so we do not free a newer pasn->frame (such as M3 queued during M2 RX).
+ */
+ if (le_to_host16(mgmt->u.auth.auth_transaction) != pasn->trans_seq) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Ignore stale Auth%u TX status "
+ "(trans_seq=%u) from " MACSTR,
+ le_to_host16(mgmt->u.auth.auth_transaction),
+ pasn->trans_seq, MAC2STR(mgmt->da));
+ return 0;
+ }
+
ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
/*
@@ -2181,41 +2238,40 @@ int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
*/
if (ret == 0 && pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2)
return acked ? 0 : 4;
- if (ret == 1 && acked && pr->cfg->pasn_result)
- pr->cfg->pasn_result(pr->cfg->cb_ctx, dev->ranging_role,
- dev->protocol_type, dev->final_op_class,
- dev->final_op_channel, pr->cfg->country);
- if (ret == 1 && acked && pr->cfg->get_ranging_params) {
- if (dev->protocol_type & PR_EDCA_BASED_RANGING) {
- self_format_bw = pr->cfg->edca_format_and_bw;
- peer_format_bw = dev->edca_caps.edca_hw_caps &
- EDCA_FORMAT_AND_BW_MASK;
- } else if ((dev->protocol_type &
- PR_NTB_SECURE_LTF_BASED_RANGING) ||
- (dev->protocol_type & PR_NTB_OPEN_BASED_RANGING)) {
- self_format_bw = pr->cfg->ntb_format_and_bw;
- peer_format_bw = dev->ntb_caps.ntb_hw_caps &
- NTB_FORMAT_AND_BW_MASK;
- } else {
- wpa_printf(MSG_INFO,
- "PR PASN: Invalid protocol type: %u",
- dev->protocol_type);
- goto out;
+ if (ret == 1) {
+ /*
+ * pasn->frame holds the serialized Auth3 buffer queued on M2
+ * RX and is freed below once PASN completes and hands off to
+ * ranging on the negotiated channel. A NULL frame here means
+ * PASN already completed and this is a duplicate/late Auth3
+ * TX status.
+ */
+ if (!pasn->frame) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Auth3 already completed for "
+ MACSTR ", ignore TX status",
+ MAC2STR(mgmt->da));
+ return 0;
}
-
- pr->cfg->get_ranging_params(pr->cfg->cb_ctx, pr->cfg->dev_addr,
- dev->pr_device_addr,
- dev->ranging_role,
- dev->protocol_type,
- dev->final_op_class,
- dev->final_op_channel,
- self_format_bw,
- peer_format_bw);
- pr_flush(pr);
+ if (!acked && dev->pasn_m3_retries < PR_PASN_M3_MAX_RETRY) {
+ dev->pasn_m3_retries++;
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Auth3 not acked by " MACSTR
+ ", schedule retry (%u/%u)",
+ MAC2STR(mgmt->da), dev->pasn_m3_retries,
+ PR_PASN_M3_MAX_RETRY);
+ return 2;
+ }
+ if (!acked) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Assume Auth3 TX was received by "
+ MACSTR " even though ACK was not reported",
+ MAC2STR(mgmt->da));
+ }
+ pr_pasn_complete_auth3(pr, dev);
}
-out:
wpabuf_free(pasn->frame);
pasn->frame = NULL;
@@ -2227,6 +2283,8 @@ int pr_pasn_auth_retransmit(struct pr_data *pr, const u8 *addr)
{
struct pr_device *dev;
struct pasn_data *pasn;
+ unsigned int wait_time;
+ int ret;
dev = pr_get_device(pr, addr);
if (!dev || !dev->pasn || !dev->pasn->frame)
@@ -2234,13 +2292,27 @@ int pr_pasn_auth_retransmit(struct pr_data *pr, const u8 *addr)
pasn = dev->pasn;
if (pasn->trans_seq != WLAN_AUTH_TR_SEQ_PASN_AUTH1 &&
- pasn->trans_seq != WLAN_AUTH_TR_SEQ_PASN_AUTH2)
+ pasn->trans_seq != WLAN_AUTH_TR_SEQ_PASN_AUTH2 &&
+ pasn->trans_seq != WLAN_AUTH_TR_SEQ_PASN_AUTH3)
return -1;
- wpa_printf(MSG_DEBUG, "PR PASN: retransmit Authentication frame %u to "
- MACSTR, pasn->trans_seq, MAC2STR(addr));
- return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame),
- wpabuf_len(pasn->frame), 0, pasn->freq, 1000);
+ wait_time = pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH3 ? 100 : 1000;
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: retransmit Authentication frame %u to " MACSTR,
+ pasn->trans_seq, MAC2STR(addr));
+ ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame),
+ wpabuf_len(pasn->frame), 0, pasn->freq,
+ wait_time);
+ if (ret < 0 && pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH3) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Auth3 retry TX failed, assume received by "
+ MACSTR, MAC2STR(addr));
+ wpabuf_free(pasn->frame);
+ pasn->frame = NULL;
+ pr_pasn_complete_auth3(pr, dev);
+ }
+
+ return ret;
}
@@ -2529,6 +2601,12 @@ fail:
}
+/*
+ * Returns:
+ * 1 if Auth1 is accepted and Auth2 is transmitted (initial or replayed)
+ * 0 if the frame is ignored
+ * < 0 on error
+ */
static int pr_pasn_handle_auth_1(struct pr_data *pr, struct pr_device *dev,
const struct ieee80211_mgmt *mgmt, size_t len,
int freq)
@@ -2575,8 +2653,9 @@ static int pr_pasn_handle_auth_1(struct pr_data *pr, struct pr_device *dev,
wpa_printf(MSG_DEBUG,
"PR PASN: Retransmit Auth2 to "
MACSTR, MAC2STR(mgmt->sa));
- pr_pasn_auth_retransmit(pr,
- dev->pr_device_addr);
+ if (pr_pasn_auth_retransmit(
+ pr, dev->pr_device_addr) == 0)
+ return 1;
return 0;
}
wpa_printf(MSG_DEBUG,
@@ -2646,7 +2725,7 @@ static int pr_pasn_handle_auth_1(struct pr_data *pr, struct pr_device *dev,
pr->cfg->negotiation_started(pr->cfg->cb_ctx, mgmt->sa,
dev->ranging_role,
dev->protocol_type);
- ret = 0;
+ ret = 1;
fail:
wpabuf_free(dev->ranging_wrapper);
@@ -2655,6 +2734,12 @@ fail:
}
+/*
+ * Returns:
+ * 1 if Auth2 is accepted and Auth3 is transmitted
+ * 0 if the frame is ignored
+ * < 0 on error
+ */
static int pr_pasn_handle_auth_2(struct pr_data *pr, struct pr_device *dev,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -2714,7 +2799,8 @@ static int pr_pasn_handle_auth_2(struct pr_data *pr, struct pr_device *dev,
wpa_printf(MSG_INFO, "PR PASN: Key configuration failed");
goto fail;
}
- ret = 0;
+ pr->auth_retry_count = 0;
+ ret = 1;
fail:
wpabuf_free(dev->ranging_wrapper);
@@ -2722,7 +2808,12 @@ fail:
return ret;
}
-
+/*
+ * Returns:
+ * 1 if Auth3 is accepted and ranging parameters are requested
+ * 0 if the frame is ignored
+ * < 0 on error
+ */
static int pr_pasn_handle_auth_3(struct pr_data *pr, struct pr_device *dev,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -2793,7 +2884,7 @@ static int pr_pasn_handle_auth_3(struct pr_data *pr, struct pr_device *dev,
self_format_bw,
peer_format_bw);
pr_flush(pr);
- return 0;
+ return 1;
fail:
/* Clear the keys as M3 processing failed */
@@ -2846,4 +2937,18 @@ int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt,
return -1;
}
+
+bool pr_is_pasn_responder_waiting_m3(struct pr_data *pr, const u8 *addr)
+{
+ struct pr_device *dev;
+
+ if (!pr || !addr)
+ return false;
+
+ dev = pr_get_device(pr, addr);
+ return dev && dev->pasn_role == PR_ROLE_PASN_RESPONDER &&
+ dev->pasn &&
+ dev->pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2;
+}
+
#endif /* CONFIG_PASN */
diff --git a/src/common/proximity_ranging.h b/src/common/proximity_ranging.h
index 1fb607ff3..9310d1b9d 100644
--- a/src/common/proximity_ranging.h
+++ b/src/common/proximity_ranging.h
@@ -430,6 +430,7 @@ struct pr_device {
struct pasn_data *pasn;
struct wpabuf *ranging_wrapper;
enum pr_pasn_role pasn_role;
+ u8 pasn_m3_retries;
#endif /* CONFIG_PASN */
u8 ranging_role;
@@ -683,5 +684,6 @@ int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
int pr_pasn_auth_retransmit(struct pr_data *pr, const u8 *addr);
int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt,
size_t len, int freq);
+bool pr_is_pasn_responder_waiting_m3(struct pr_data *pr, const u8 *addr);
#endif /* PROXIMITY_RANGING_H */
diff --git a/wpa_supplicant/pr_supplicant.c b/wpa_supplicant/pr_supplicant.c
index 0d3bab045..2e9df5d30 100644
--- a/wpa_supplicant/pr_supplicant.c
+++ b/wpa_supplicant/pr_supplicant.c
@@ -22,6 +22,7 @@
#ifdef CONFIG_PASN
static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_pr_pasn_m3_wait_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_pr_pasn_roc_total_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_pr_pasn_auth_work_done(struct wpa_supplicant *wpa_s);
static void wpas_pr_pasn_auth_retry_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -33,6 +34,8 @@ static void wpas_pr_cancel_roc(struct wpa_supplicant *wpa_s);
#define PR_PASN_M2_DWELL_MS 1000
/* Responder ROC duration (ms) on Auth2 TX failure to catch retry Auth1 */
#define PR_PASN_M2_RETRY_ROC_DURATION_MS 3000
+/* Responder wait timeout (ms) for M3 after Auth2 TX is ACKed */
+#define PR_PASN_M3_WAIT_TIMEOUT_MS 1000
/* Initiator PASN authentication timeout (s) */
#define PR_PASN_AUTH_TIMEOUT 10
/* Retry interval (ms) for unacked PASN Authentication frame 1 */
@@ -41,6 +44,13 @@ static void wpas_pr_cancel_roc(struct wpa_supplicant *wpa_s);
#define PR_PASN_AUTH1_ACKED_RETRY_INTERVAL_MS 512
/* Maximum number of PASN authentication frame 1 retransmissions */
#define PR_PASN_MAX_AUTH_RETRIES 3
+
+static void wpas_pr_cancel_pasn_timeouts(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_pr_pasn_m3_wait_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL);
+}
#endif /* CONFIG_PASN */
@@ -586,8 +596,7 @@ static void wpas_pr_ranging_params(void *ctx, const u8 *dev_addr,
* prematurely stop the PD wdev while ranging is in progress.
* Cleanup happens on COMPLETE event or session timeout.
*/
- eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
- eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
wpas_pr_pasn_auth_work_done(wpa_s);
wpas_pr_cancel_roc(wpa_s);
@@ -867,9 +876,8 @@ void wpas_pr_deinit(struct wpa_supplicant *wpa_s)
}
#ifdef CONFIG_PASN
- eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout, wpa_s, NULL);
- eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL);
#endif /* CONFIG_PASN */
eloop_cancel_timeout(wpas_pr_ranging_session_timeout, wpa_s, NULL);
}
@@ -1220,7 +1228,7 @@ static void wpas_pr_pasn_roc_start_cb(struct wpa_radio_work *work, int deinit)
wpa_printf(MSG_ERROR,
"PR PASN: Failed to start ROC for responder");
wpas_pr_pasn_roc_work_done(wpa_s);
- eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
wpas_pr_pd_stop(wpa_s);
wpas_pr_pasn_abort_responder(wpa_s);
wpas_notify_pr_ranging_terminated(wpa_s,
@@ -1264,7 +1272,7 @@ static void wpas_pr_schedule_responder_roc(struct wpa_supplicant *wpa_s,
return;
fail:
- eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
wpas_pr_pd_stop(wpa_s);
wpas_pr_pasn_abort_responder(wpa_s);
wpas_notify_pr_ranging_terminated(wpa_s, PR_SESSION_END_NEG_FAILED);
@@ -1294,10 +1302,21 @@ void wpas_pr_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
}
if (wpa_s->global->pr && wpa_s->global->pr->pr_pasn_params) {
+ if (pr_is_pasn_responder_waiting_m3(
+ wpa_s->global->pr,
+ wpa_s->global->pr->pr_pasn_params->peer_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Responder M2 retry ROC chunk expired, "
+ "restarting next chunk");
+ wpas_pr_schedule_responder_roc(wpa_s, freq);
+ return;
+ }
+
wpa_printf(MSG_DEBUG,
- "PR PASN: Responder M2 retry ROC chunk expired, "
- "restarting next chunk");
- wpas_pr_schedule_responder_roc(wpa_s, freq);
+ "PR PASN: Responder M2 retry ROC expired, "
+ "aborting PASN");
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
+ wpas_pr_pasn_timeout(wpa_s, NULL);
return;
}
@@ -1310,7 +1329,7 @@ static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
- eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
wpas_pr_cancel_roc(wpa_s);
if (wpa_s->pr_pasn_auth_work) {
@@ -1333,6 +1352,16 @@ static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx)
}
+static void wpas_pr_pasn_m3_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: M3 wait timeout expired after M2 TX");
+ wpas_pr_pasn_timeout(wpa_s, timeout_ctx);
+}
+
+
static void wpas_pr_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
{
int ret;
@@ -1343,9 +1372,7 @@ static void wpas_pr_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
if (deinit) {
if (work->started) {
- eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
- eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout,
- wpa_s, NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
wpa_s->pr_pasn_auth_work = NULL;
}
@@ -1449,6 +1476,8 @@ int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
pr->pr_pasn_params->continuous_ranging_session_time > 0)
roc_time_ms = pr->pr_pasn_params->continuous_ranging_session_time;
+ eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout, wpa_s,
+ NULL);
eloop_register_timeout(roc_time_ms / 1000,
(roc_time_ms % 1000) * 1000,
wpas_pr_pasn_roc_total_timeout,
@@ -1750,6 +1779,7 @@ int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
{
struct pr_data *pr = wpa_s->global->pr;
const struct ieee80211_mgmt *mgmt;
+ u16 auth_transaction;
int ret;
if (!wpa_s->pr_pasn_auth_work && is_zero_ether_addr(wpa_s->pd_addr) &&
@@ -1764,11 +1794,13 @@ int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
if (!ether_addr_equal(mgmt->da, pr->pr_pasn_params->peer_addr))
return -1;
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
ret = pr_pasn_auth_tx_status(pr, data, data_len, acked);
if (ret == 2) {
/*
- * M1 was not ACKed (peer may not be on-channel yet). Keep
- * retrying every 100ms across the 10s PASN timeout window.
+ * Unacked M1 (retried across the 10s PASN timeout window) or
+ * unacked M3 (retried once): schedule retransmission after
+ * 100ms from eloop timer context.
*/
eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s,
NULL);
@@ -1814,10 +1846,13 @@ int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
* no retry M1 or M3 arrives, similar to P2P GO Negotiation
* Response recovery.
*/
- if (pr && pr->pr_pasn_params) {
+ if (pr && pr->pr_pasn_params &&
+ pr_is_pasn_responder_waiting_m3(pr, mgmt->da)) {
wpas_pr_cancel_roc(wpa_s);
wpas_pr_schedule_responder_roc(
wpa_s, pr->pr_pasn_params->freq);
+ eloop_cancel_timeout(wpas_pr_pasn_m3_wait_timeout,
+ wpa_s, NULL);
eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s,
NULL);
eloop_register_timeout(
@@ -1826,6 +1861,21 @@ int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
}
return 0;
}
+ if (ret == 0 && auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2 &&
+ acked && pr_is_pasn_responder_waiting_m3(pr, mgmt->da)) {
+ /*
+ * M2 was ACKed by the initiator. Arm a 1s wait timer for M3
+ * so that if M3 is lost over the air, the responder tears down
+ * in 1s rather than waiting for the 10s failsafe timeout.
+ */
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: M2 ACKed, wait %d ms for M3",
+ PR_PASN_M3_WAIT_TIMEOUT_MS);
+ eloop_cancel_timeout(wpas_pr_pasn_m3_wait_timeout, wpa_s, NULL);
+ eloop_register_timeout(0, PR_PASN_M3_WAIT_TIMEOUT_MS * 1000,
+ wpas_pr_pasn_m3_wait_timeout, wpa_s,
+ NULL);
+ }
return ret < 0 ? -1 : 0;
}
@@ -1938,8 +1988,13 @@ int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s,
*/
if (wpa_s->pr_responder_mode) {
if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
- if (wpas_pr_check_pd_wdev_create(wpa_s) < 0)
+ if (wpas_pr_check_pd_wdev_create(wpa_s) < 0) {
+ wpas_pr_cancel_roc(wpa_s);
+ wpas_pr_pasn_abort_responder(wpa_s);
+ wpas_notify_pr_ranging_terminated(
+ wpa_s, PR_SESSION_END_NEG_FAILED);
return -1;
+ }
/*
* Cancel the total-budget timer first so it does not
@@ -1967,7 +2022,7 @@ int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_DEBUG,
"PR PASN: M1 processed, proceeding with PASN");
}
- } else if (!wpa_s->pr_pasn_auth_work) {
+ } else if (pr_is_pasn_responder_waiting_m3(pr, mgmt->sa)) {
/*
* Cancel any active post-M2-NACK recovery ROC on the responder
* when duplicate M1 or M3 arrives from the peer.
@@ -1978,39 +2033,49 @@ int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s,
ret = pr_pasn_auth_rx(pr, mgmt, len, freq);
if (ret < 0) {
- eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
- eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s,
- NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
wpas_pr_pasn_auth_work_done(wpa_s);
wpas_pr_cancel_roc(wpa_s);
wpas_pr_pd_stop(wpa_s);
wpas_pr_clear_ranging_params(wpa_s->global->pr);
wpas_notify_pr_ranging_terminated(wpa_s,
PR_SESSION_END_NEG_FAILED);
- } else if (len >= offsetof(struct ieee80211_mgmt, u.auth.variable)) {
+ return ret;
+ }
+
+ if (ret == 1) {
u16 auth_transaction;
auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
- if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1 &&
- !wpa_s->pr_pasn_auth_work) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
/*
* Arm or refresh the PASN negotiation timeout on the
- * responder so that if M2 TX fails or M3 never arrives,
- * the PD wdev and ranging params are cleaned up rather
- * than leaving the responder wedged.
+ * responder when a valid Auth1 is accepted (initial M1
+ * or duplicate M1 that replayed M2) so that if M2 TX
+ * fails or M3 never arrives, the PD wdev and ranging
+ * params are cleaned up rather than leaving the
+ * responder wedged. Cancel any M3 wait timer from a
+ * prior M2 attempt before replaying or restarting M2.
*/
+ eloop_cancel_timeout(wpas_pr_pasn_m3_wait_timeout,
+ wpa_s, NULL);
eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
eloop_register_timeout(PR_PASN_AUTH_TIMEOUT, 0,
wpas_pr_pasn_timeout, wpa_s,
NULL);
} else if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) {
- pr->auth_retry_count = 0;
+ /*
+ * Valid Auth2 accepted on the initiator at
+ * trans_seq == AUTH1 and M3 sent; cancel any pending
+ * M1 retry timer.
+ */
eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout,
wpa_s, NULL);
}
+ /* Auth3 session timeout are canceled in wpas_pr_ranging_params() callback */
}
- return ret;
+ return 0;
}
@@ -2045,8 +2110,7 @@ void wpas_pr_abort_ranging(struct wpa_supplicant *wpa_s)
/* Stop PD wdev and cleanup all ranging resources */
wpas_pr_pd_stop(wpa_s);
- eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
- eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL);
+ wpas_pr_cancel_pasn_timeouts(wpa_s);
/* Free ranging params so a new session can be started */
wpas_pr_clear_ranging_params(pr);
wpas_notify_pr_ranging_terminated(wpa_s, PR_SESSION_END_USER_ABORT);
--
2.56.0.385.gac671c819a-goog
More information about the Hostap
mailing list