[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