[PATCH 3/4] gas_query: retry GAS comeback requests instead of failing
Gustavo Bertoli
gubertoli at gmail.com
Tue Jun 23 08:57:16 PDT 2026
A transient TX failure, a missed comeback response, or a duplicate fragment
(a retransmit after a lost ACK) ended the whole GAS query, even though the
responder keeps its dialog entry alive. When comeback_recover is set, retry
the comeback request on the same dialog token in each of those cases instead
of giving up.
A stuck leg is bounded by a per-leg recovery watchdog:
GAS_QUERY_DPP_COMEBACK_TIMEOUT (5 s), armed on real progress (a received
comeback response) and not reset on a retransmission, so it fires once no
comeback response has arrived for that long. This assumes the announced
comeback delay stays well under it.
Signed-off-by: Gustavo Bertoli <gubertoli at gmail.com>
---
wpa_supplicant/gas_query.c | 74 +++++++++++++++++++++++++++++++-------
1 file changed, 62 insertions(+), 12 deletions(-)
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index e6f18e856..d00d25f49 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -26,6 +26,9 @@
/** GAS query timeout in seconds */
#define GAS_QUERY_TIMEOUT_PERIOD 2
+/* DPP GAS comeback recovery watchdog in seconds */
+#define GAS_QUERY_DPP_COMEBACK_TIMEOUT 5
+
/* GAS query wait-time / duration in ms */
#define GAS_QUERY_WAIT_TIME_INITIAL 1000
#define GAS_QUERY_WAIT_TIME_COMEBACK 150
@@ -268,18 +271,33 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
}
os_get_reltime(&query->last_oper);
+ if ((result == OFFCHANNEL_SEND_ACTION_NO_ACK ||
+ result == OFFCHANNEL_SEND_ACTION_FAILED) && query->wait_comeback &&
+ query->comeback_recover) {
+ /* Comeback request not delivered; retry (watchdog bounds the leg) */
+ wpa_printf(MSG_DEBUG, "GAS: No ACK to comeback request - retry");
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_register_timeout(0, GAS_QUERY_WAIT_TIME_COMEBACK * 1000,
+ gas_query_tx_comeback_timeout, gas, query);
+ return;
+ }
if (result == OFFCHANNEL_SEND_ACTION_SUCCESS ||
result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
- eloop_cancel_timeout(gas_query_timeout, gas, query);
- if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
- wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
- eloop_register_timeout(0, 250000,
- gas_query_timeout, gas, query);
- } else {
- eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
- gas_query_timeout, gas, query);
+ if (!(query->wait_comeback && query->comeback_recover)) {
+ /* Recovery watchdog tracks progress, not each TX; keep it */
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
+ wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+ eloop_register_timeout(0, 250000,
+ gas_query_timeout, gas, query);
+ } else {
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+ }
}
- if (query->wait_comeback && !query->retry) {
+ if (query->wait_comeback &&
+ (query->comeback_recover || !query->retry)) {
eloop_cancel_timeout(gas_query_rx_comeback_timeout,
gas, query);
eloop_register_timeout(
@@ -343,19 +361,28 @@ static void gas_query_tx_comeback_req(struct gas_query *gas,
struct wpabuf *req;
unsigned int wait_time;
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+
req = gas_build_comeback_req(query->dialog_token);
if (req == NULL) {
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
}
- wait_time = (query->retry || !query->offchannel_tx_started) ?
+ wait_time = (query->retry || !query->offchannel_tx_started ||
+ (query->comeback_recover && query->wait_comeback)) ?
GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
if (gas_query_tx(gas, query, req, wait_time) < 0) {
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
MACSTR, MAC2STR(query->addr));
- gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ if (query->wait_comeback && query->comeback_recover)
+ eloop_register_timeout(0,
+ GAS_QUERY_WAIT_TIME_COMEBACK * 1000,
+ gas_query_tx_comeback_timeout,
+ gas, query);
+ else
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
}
wpabuf_free(req);
@@ -371,7 +398,17 @@ static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
wpa_printf(MSG_DEBUG,
"GAS: No response to comeback request received (retry=%u)",
query->retry);
- if (gas->current != query || query->retry)
+ if (gas->current != query)
+ return;
+
+ if (query->wait_comeback && query->comeback_recover) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: Comeback response missed, retry same token");
+ gas_query_tx_comeback_req(gas, query);
+ return;
+ }
+
+ if (query->retry)
return;
dialog_token = gas_query_new_dialog_token(gas, query->addr);
if (dialog_token < 0)
@@ -420,6 +457,12 @@ static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
gas, query);
+ if (query->comeback_recover) {
+ /* Bound a stuck leg by a fixed recovery watchdog */
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_register_timeout(GAS_QUERY_DPP_COMEBACK_TIMEOUT, 0,
+ gas_query_timeout, gas, query);
+ }
}
@@ -496,11 +539,18 @@ static void gas_query_rx_comeback(struct gas_query *gas,
if (frag_id + 1 == query->next_frag_id) {
wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
"retry of previous fragment");
+ if (query->comeback_recover)
+ gas_query_tx_comeback_req(gas, query);
return;
}
gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
return;
}
+ if (query->comeback_recover) {
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_register_timeout(GAS_QUERY_DPP_COMEBACK_TIMEOUT, 0,
+ gas_query_timeout, gas, query);
+ }
query->next_frag_id++;
if (gas_query_append(query, resp, len) < 0) {
--
2.39.5
More information about the Hostap
mailing list