xref: /freebsd/contrib/wpa/src/common/gas_server.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
185732ac8SCy Schubert /*
285732ac8SCy Schubert  * Generic advertisement service (GAS) server
385732ac8SCy Schubert  * Copyright (c) 2017, Qualcomm Atheros, Inc.
4c1d255d3SCy Schubert  * Copyright (c) 2020, The Linux Foundation
5*a90b9d01SCy Schubert  * Copyright (c) 2022, Qualcomm Innovation Center, Inc.
685732ac8SCy Schubert  *
785732ac8SCy Schubert  * This software may be distributed under the terms of the BSD license.
885732ac8SCy Schubert  * See README for more details.
985732ac8SCy Schubert  */
1085732ac8SCy Schubert 
1185732ac8SCy Schubert #include "includes.h"
1285732ac8SCy Schubert 
1385732ac8SCy Schubert #include "utils/common.h"
1485732ac8SCy Schubert #include "utils/list.h"
1585732ac8SCy Schubert #include "utils/eloop.h"
1685732ac8SCy Schubert #include "ieee802_11_defs.h"
1785732ac8SCy Schubert #include "gas.h"
1885732ac8SCy Schubert #include "gas_server.h"
1985732ac8SCy Schubert 
2085732ac8SCy Schubert 
2185732ac8SCy Schubert #define MAX_ADV_PROTO_ID_LEN 10
22*a90b9d01SCy Schubert #define GAS_QUERY_TIMEOUT 60
2385732ac8SCy Schubert 
2485732ac8SCy Schubert struct gas_server_handler {
2585732ac8SCy Schubert 	struct dl_list list;
2685732ac8SCy Schubert 	u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
2785732ac8SCy Schubert 	u8 adv_proto_id_len;
28c1d255d3SCy Schubert 	struct wpabuf * (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
29c1d255d3SCy Schubert 				  const u8 *query, size_t query_len,
30*a90b9d01SCy Schubert 				  int *comeback_delay);
3185732ac8SCy Schubert 	void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
3285732ac8SCy Schubert 	void *ctx;
3385732ac8SCy Schubert 	struct gas_server *gas;
3485732ac8SCy Schubert };
3585732ac8SCy Schubert 
3685732ac8SCy Schubert struct gas_server_response {
3785732ac8SCy Schubert 	struct dl_list list;
3885732ac8SCy Schubert 	size_t offset;
3985732ac8SCy Schubert 	u8 frag_id;
4085732ac8SCy Schubert 	struct wpabuf *resp;
4185732ac8SCy Schubert 	int freq;
4285732ac8SCy Schubert 	u8 dst[ETH_ALEN];
4385732ac8SCy Schubert 	u8 dialog_token;
4485732ac8SCy Schubert 	struct gas_server_handler *handler;
45c1d255d3SCy Schubert 	u16 comeback_delay;
46*a90b9d01SCy Schubert 	bool initial_resp_sent;
4785732ac8SCy Schubert };
4885732ac8SCy Schubert 
4985732ac8SCy Schubert struct gas_server {
5085732ac8SCy Schubert 	struct dl_list handlers; /* struct gas_server_handler::list */
5185732ac8SCy Schubert 	struct dl_list responses; /* struct gas_server_response::list */
5285732ac8SCy Schubert 	void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp,
5385732ac8SCy Schubert 		   unsigned int wait_time);
5485732ac8SCy Schubert 	void *ctx;
5585732ac8SCy Schubert };
5685732ac8SCy Schubert 
5785732ac8SCy Schubert static void gas_server_free_response(struct gas_server_response *response);
5885732ac8SCy Schubert 
5985732ac8SCy Schubert 
gas_server_response_timeout(void * eloop_ctx,void * user_ctx)6085732ac8SCy Schubert static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
6185732ac8SCy Schubert {
6285732ac8SCy Schubert 	struct gas_server_response *response = eloop_ctx;
6385732ac8SCy Schubert 
6485732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
6585732ac8SCy Schubert 		   " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
6685732ac8SCy Schubert 		   response, MAC2STR(response->dst), response->dialog_token,
6785732ac8SCy Schubert 		   response->freq, response->frag_id,
6885732ac8SCy Schubert 		   (unsigned long) response->offset,
69c1d255d3SCy Schubert 		   (unsigned long) (response->resp ?
70c1d255d3SCy Schubert 				    wpabuf_len(response->resp) : 0));
7185732ac8SCy Schubert 	response->handler->status_cb(response->handler->ctx,
7285732ac8SCy Schubert 				     response->resp, 0);
7385732ac8SCy Schubert 	response->resp = NULL;
7485732ac8SCy Schubert 	dl_list_del(&response->list);
7585732ac8SCy Schubert 	gas_server_free_response(response);
7685732ac8SCy Schubert }
7785732ac8SCy Schubert 
7885732ac8SCy Schubert 
gas_server_free_response(struct gas_server_response * response)7985732ac8SCy Schubert static void gas_server_free_response(struct gas_server_response *response)
8085732ac8SCy Schubert {
8185732ac8SCy Schubert 	if (!response)
8285732ac8SCy Schubert 		return;
8385732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response);
8485732ac8SCy Schubert 	eloop_cancel_timeout(gas_server_response_timeout, response, NULL);
8585732ac8SCy Schubert 	wpabuf_free(response->resp);
8685732ac8SCy Schubert 	os_free(response);
8785732ac8SCy Schubert }
8885732ac8SCy Schubert 
8985732ac8SCy Schubert 
9085732ac8SCy Schubert static void
gas_server_send_resp(struct gas_server * gas,struct gas_server_response * response,struct wpabuf * query_resp,u16 comeback_delay)91*a90b9d01SCy Schubert gas_server_send_resp(struct gas_server *gas,
92c1d255d3SCy Schubert 		     struct gas_server_response *response,
93c1d255d3SCy Schubert 		     struct wpabuf *query_resp, u16 comeback_delay)
9485732ac8SCy Schubert {
95*a90b9d01SCy Schubert 	struct gas_server_handler *handler = response->handler;
96*a90b9d01SCy Schubert 	size_t max_len = (response->freq > 56160) ? 928 : 1400;
9785732ac8SCy Schubert 	size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
9885732ac8SCy Schubert 	size_t resp_frag_len;
9985732ac8SCy Schubert 	struct wpabuf *resp;
10085732ac8SCy Schubert 
101c1d255d3SCy Schubert 	if (comeback_delay == 0 && !query_resp) {
102*a90b9d01SCy Schubert 		dl_list_del(&response->list);
103c1d255d3SCy Schubert 		gas_server_free_response(response);
10485732ac8SCy Schubert 		return;
10585732ac8SCy Schubert 	}
106c1d255d3SCy Schubert 
107c1d255d3SCy Schubert 	if (comeback_delay) {
108c1d255d3SCy Schubert 		/* Need more time to prepare the response */
109c1d255d3SCy Schubert 		resp_frag_len = 0;
110c1d255d3SCy Schubert 		response->comeback_delay = comeback_delay;
111c1d255d3SCy Schubert 	} else if (hdr_len + wpabuf_len(query_resp) > max_len) {
11285732ac8SCy Schubert 		/* Need to use comeback to initiate fragmentation */
11385732ac8SCy Schubert 		comeback_delay = 1;
11485732ac8SCy Schubert 		resp_frag_len = 0;
11585732ac8SCy Schubert 	} else {
11685732ac8SCy Schubert 		/* Full response fits into the initial response */
11785732ac8SCy Schubert 		comeback_delay = 0;
11885732ac8SCy Schubert 		resp_frag_len = wpabuf_len(query_resp);
11985732ac8SCy Schubert 	}
12085732ac8SCy Schubert 
121*a90b9d01SCy Schubert 	resp = gas_build_initial_resp(response->dialog_token,
122*a90b9d01SCy Schubert 				      WLAN_STATUS_SUCCESS,
12385732ac8SCy Schubert 				      comeback_delay,
12485732ac8SCy Schubert 				      handler->adv_proto_id_len +
12585732ac8SCy Schubert 				      resp_frag_len);
12685732ac8SCy Schubert 	if (!resp) {
12785732ac8SCy Schubert 		wpabuf_free(query_resp);
128*a90b9d01SCy Schubert 		dl_list_del(&response->list);
12985732ac8SCy Schubert 		gas_server_free_response(response);
13085732ac8SCy Schubert 		return;
13185732ac8SCy Schubert 	}
13285732ac8SCy Schubert 
13385732ac8SCy Schubert 	/* Advertisement Protocol element */
13485732ac8SCy Schubert 	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
13585732ac8SCy Schubert 	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
13685732ac8SCy Schubert 	wpabuf_put_u8(resp, 0x7f);
13785732ac8SCy Schubert 	/* Advertisement Protocol ID */
13885732ac8SCy Schubert 	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
13985732ac8SCy Schubert 
14085732ac8SCy Schubert 	/* Query Response Length */
14185732ac8SCy Schubert 	wpabuf_put_le16(resp, resp_frag_len);
142c1d255d3SCy Schubert 	if (!comeback_delay && query_resp)
14385732ac8SCy Schubert 		wpabuf_put_buf(resp, query_resp);
14485732ac8SCy Schubert 
145c1d255d3SCy Schubert 	if (comeback_delay && !query_resp) {
146c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "GAS: No response available yet");
147c1d255d3SCy Schubert 	} else if (comeback_delay) {
14885732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
14985732ac8SCy Schubert 			   "GAS: Need to fragment query response");
15085732ac8SCy Schubert 	} else {
15185732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
15285732ac8SCy Schubert 			   "GAS: Full query response fits in the GAS Initial Response frame");
15385732ac8SCy Schubert 	}
15485732ac8SCy Schubert 	response->offset = resp_frag_len;
15585732ac8SCy Schubert 	response->resp = query_resp;
156*a90b9d01SCy Schubert 	response->initial_resp_sent = true;
157*a90b9d01SCy Schubert 	gas->tx(gas->ctx, response->freq, response->dst, resp,
158*a90b9d01SCy Schubert 		comeback_delay ? 2000 : 0);
15985732ac8SCy Schubert 	wpabuf_free(resp);
16085732ac8SCy Schubert 	eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
16185732ac8SCy Schubert 			       gas_server_response_timeout, response, NULL);
16285732ac8SCy Schubert }
16385732ac8SCy Schubert 
16485732ac8SCy Schubert 
16585732ac8SCy Schubert static int
gas_server_rx_initial_req(struct gas_server * gas,const u8 * da,const u8 * sa,const u8 * bssid,int freq,u8 dialog_token,const u8 * data,size_t len)16685732ac8SCy Schubert gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
16785732ac8SCy Schubert 			  const u8 *bssid, int freq, u8 dialog_token,
16885732ac8SCy Schubert 			  const u8 *data, size_t len)
16985732ac8SCy Schubert {
17085732ac8SCy Schubert 	const u8 *pos, *end, *adv_proto, *query_req;
17185732ac8SCy Schubert 	u8 adv_proto_len;
17285732ac8SCy Schubert 	u16 query_req_len;
17385732ac8SCy Schubert 	struct gas_server_handler *handler;
17485732ac8SCy Schubert 	struct wpabuf *resp;
175c1d255d3SCy Schubert 	struct gas_server_response *response;
17685732ac8SCy Schubert 
17785732ac8SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
17885732ac8SCy Schubert 		    data, len);
17985732ac8SCy Schubert 	pos = data;
18085732ac8SCy Schubert 	end = data + len;
18185732ac8SCy Schubert 
18285732ac8SCy Schubert 	if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
18385732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
18485732ac8SCy Schubert 			   "GAS: No Advertisement Protocol element found");
18585732ac8SCy Schubert 		return -1;
18685732ac8SCy Schubert 	}
18785732ac8SCy Schubert 	pos++;
18885732ac8SCy Schubert 	adv_proto_len = *pos++;
18985732ac8SCy Schubert 	if (end - pos < adv_proto_len || adv_proto_len < 2) {
19085732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
19185732ac8SCy Schubert 			   "GAS: Truncated Advertisement Protocol element");
19285732ac8SCy Schubert 		return -1;
19385732ac8SCy Schubert 	}
19485732ac8SCy Schubert 
19585732ac8SCy Schubert 	adv_proto = pos;
19685732ac8SCy Schubert 	pos += adv_proto_len;
19785732ac8SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
19885732ac8SCy Schubert 		    adv_proto, adv_proto_len);
19985732ac8SCy Schubert 
20085732ac8SCy Schubert 	if (end - pos < 2) {
20185732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
20285732ac8SCy Schubert 		return -1;
20385732ac8SCy Schubert 	}
20485732ac8SCy Schubert 	query_req_len = WPA_GET_LE16(pos);
20585732ac8SCy Schubert 	pos += 2;
20685732ac8SCy Schubert 	if (end - pos < query_req_len) {
20785732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
20885732ac8SCy Schubert 		return -1;
20985732ac8SCy Schubert 	}
21085732ac8SCy Schubert 	query_req = pos;
21185732ac8SCy Schubert 	pos += query_req_len;
21285732ac8SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
21385732ac8SCy Schubert 		    query_req, query_req_len);
21485732ac8SCy Schubert 
21585732ac8SCy Schubert 	if (pos < end) {
21685732ac8SCy Schubert 		wpa_hexdump(MSG_MSGDUMP,
21785732ac8SCy Schubert 			    "GAS: Ignored extra data after Query Request field",
21885732ac8SCy Schubert 			    pos, end - pos);
21985732ac8SCy Schubert 	}
22085732ac8SCy Schubert 
221c1d255d3SCy Schubert 	response = os_zalloc(sizeof(*response));
222c1d255d3SCy Schubert 	if (!response)
223c1d255d3SCy Schubert 		return -1;
224c1d255d3SCy Schubert 
225c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
22685732ac8SCy Schubert 	dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
22785732ac8SCy Schubert 			 list) {
228*a90b9d01SCy Schubert 		int comeback_delay = 0;
229c1d255d3SCy Schubert 
23085732ac8SCy Schubert 		if (adv_proto_len < 1 + handler->adv_proto_id_len ||
23185732ac8SCy Schubert 		    os_memcmp(adv_proto + 1, handler->adv_proto_id,
23285732ac8SCy Schubert 			      handler->adv_proto_id_len) != 0)
23385732ac8SCy Schubert 			continue;
23485732ac8SCy Schubert 
235*a90b9d01SCy Schubert 		response->freq = freq;
236*a90b9d01SCy Schubert 		response->handler = handler;
237*a90b9d01SCy Schubert 		os_memcpy(response->dst, sa, ETH_ALEN);
238*a90b9d01SCy Schubert 		response->dialog_token = dialog_token;
239*a90b9d01SCy Schubert 		dl_list_add(&gas->responses, &response->list);
240*a90b9d01SCy Schubert 
24185732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
24285732ac8SCy Schubert 			   "GAS: Calling handler for the requested Advertisement Protocol ID");
243c1d255d3SCy Schubert 		resp = handler->req_cb(handler->ctx, response, sa, query_req,
244c1d255d3SCy Schubert 				       query_req_len, &comeback_delay);
24585732ac8SCy Schubert 		wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
24685732ac8SCy Schubert 				resp);
247*a90b9d01SCy Schubert 		if (comeback_delay < 0) {
248*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
249*a90b9d01SCy Schubert 				   "GAS: Handler requested short delay before sending out the initial response");
250*a90b9d01SCy Schubert 			return 0;
251*a90b9d01SCy Schubert 		}
252c1d255d3SCy Schubert 		if (comeback_delay)
253c1d255d3SCy Schubert 			wpa_printf(MSG_DEBUG,
254c1d255d3SCy Schubert 				   "GAS: Handler requested comeback delay: %u TU",
255c1d255d3SCy Schubert 				   comeback_delay);
256*a90b9d01SCy Schubert 		gas_server_send_resp(gas, response, resp, comeback_delay);
25785732ac8SCy Schubert 		return 0;
25885732ac8SCy Schubert 	}
25985732ac8SCy Schubert 
26085732ac8SCy Schubert 	wpa_printf(MSG_DEBUG,
26185732ac8SCy Schubert 		   "GAS: No registered handler for the requested Advertisement Protocol ID");
262c1d255d3SCy Schubert 	gas_server_free_response(response);
26385732ac8SCy Schubert 	return -1;
26485732ac8SCy Schubert }
26585732ac8SCy Schubert 
26685732ac8SCy Schubert 
26785732ac8SCy Schubert static void
gas_server_handle_rx_comeback_req(struct gas_server_response * response)26885732ac8SCy Schubert gas_server_handle_rx_comeback_req(struct gas_server_response *response)
26985732ac8SCy Schubert {
27085732ac8SCy Schubert 	struct gas_server_handler *handler = response->handler;
27185732ac8SCy Schubert 	struct gas_server *gas = handler->gas;
27285732ac8SCy Schubert 	size_t max_len = (response->freq > 56160) ? 928 : 1400;
27385732ac8SCy Schubert 	size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
27485732ac8SCy Schubert 	size_t remaining, resp_frag_len;
27585732ac8SCy Schubert 	struct wpabuf *resp;
276c1d255d3SCy Schubert 	unsigned int wait_time = 0;
277c1d255d3SCy Schubert 
278c1d255d3SCy Schubert 	if (!response->resp) {
279c1d255d3SCy Schubert 		resp = gas_build_comeback_resp(response->dialog_token,
280c1d255d3SCy Schubert 					       WLAN_STATUS_SUCCESS, 0, 0,
281c1d255d3SCy Schubert 					       response->comeback_delay,
282c1d255d3SCy Schubert 					       handler->adv_proto_id_len);
283c1d255d3SCy Schubert 		if (!resp) {
284c1d255d3SCy Schubert 			dl_list_del(&response->list);
285c1d255d3SCy Schubert 			gas_server_free_response(response);
286c1d255d3SCy Schubert 			return;
287c1d255d3SCy Schubert 		}
288c1d255d3SCy Schubert 
289c1d255d3SCy Schubert 		/* Advertisement Protocol element */
290c1d255d3SCy Schubert 		wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
291c1d255d3SCy Schubert 		wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
292c1d255d3SCy Schubert 		wpabuf_put_u8(resp, 0x7f);
293c1d255d3SCy Schubert 		/* Advertisement Protocol ID */
294c1d255d3SCy Schubert 		wpabuf_put_data(resp, handler->adv_proto_id,
295c1d255d3SCy Schubert 				handler->adv_proto_id_len);
296c1d255d3SCy Schubert 
297c1d255d3SCy Schubert 		/* Query Response Length */
298c1d255d3SCy Schubert 		wpabuf_put_le16(resp, 0);
299c1d255d3SCy Schubert 		goto send_resp;
300c1d255d3SCy Schubert 	}
30185732ac8SCy Schubert 
30285732ac8SCy Schubert 	remaining = wpabuf_len(response->resp) - response->offset;
30385732ac8SCy Schubert 	if (hdr_len + remaining > max_len)
30485732ac8SCy Schubert 		resp_frag_len = max_len - hdr_len;
30585732ac8SCy Schubert 	else
30685732ac8SCy Schubert 		resp_frag_len = remaining;
30785732ac8SCy Schubert 	wpa_printf(MSG_DEBUG,
30885732ac8SCy Schubert 		   "GAS: Sending out %u/%u remaining Query Response octets",
30985732ac8SCy Schubert 		   (unsigned int) resp_frag_len, (unsigned int) remaining);
31085732ac8SCy Schubert 
31185732ac8SCy Schubert 	resp = gas_build_comeback_resp(response->dialog_token,
31285732ac8SCy Schubert 				       WLAN_STATUS_SUCCESS,
31385732ac8SCy Schubert 				       response->frag_id++,
31485732ac8SCy Schubert 				       resp_frag_len < remaining, 0,
31585732ac8SCy Schubert 				       handler->adv_proto_id_len +
31685732ac8SCy Schubert 				       resp_frag_len);
31785732ac8SCy Schubert 	if (!resp) {
31885732ac8SCy Schubert 		dl_list_del(&response->list);
31985732ac8SCy Schubert 		gas_server_free_response(response);
32085732ac8SCy Schubert 		return;
32185732ac8SCy Schubert 	}
32285732ac8SCy Schubert 
32385732ac8SCy Schubert 	/* Advertisement Protocol element */
32485732ac8SCy Schubert 	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
32585732ac8SCy Schubert 	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
32685732ac8SCy Schubert 	wpabuf_put_u8(resp, 0x7f);
32785732ac8SCy Schubert 	/* Advertisement Protocol ID */
32885732ac8SCy Schubert 	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
32985732ac8SCy Schubert 
33085732ac8SCy Schubert 	/* Query Response Length */
33185732ac8SCy Schubert 	wpabuf_put_le16(resp, resp_frag_len);
33285732ac8SCy Schubert 	wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
33385732ac8SCy Schubert 			resp_frag_len);
33485732ac8SCy Schubert 
33585732ac8SCy Schubert 	response->offset += resp_frag_len;
33685732ac8SCy Schubert 
337c1d255d3SCy Schubert 	if (remaining > resp_frag_len)
338c1d255d3SCy Schubert 		wait_time = 2000;
339c1d255d3SCy Schubert 
340c1d255d3SCy Schubert send_resp:
341c1d255d3SCy Schubert 	gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
34285732ac8SCy Schubert 	wpabuf_free(resp);
34385732ac8SCy Schubert }
34485732ac8SCy Schubert 
34585732ac8SCy Schubert 
34685732ac8SCy Schubert static int
gas_server_rx_comeback_req(struct gas_server * gas,const u8 * da,const u8 * sa,const u8 * bssid,int freq,u8 dialog_token)34785732ac8SCy Schubert gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
34885732ac8SCy Schubert 			   const u8 *bssid, int freq, u8 dialog_token)
34985732ac8SCy Schubert {
35085732ac8SCy Schubert 	struct gas_server_response *response;
35185732ac8SCy Schubert 
35285732ac8SCy Schubert 	dl_list_for_each(response, &gas->responses, struct gas_server_response,
35385732ac8SCy Schubert 			 list) {
35485732ac8SCy Schubert 		if (response->dialog_token != dialog_token ||
355*a90b9d01SCy Schubert 		    !ether_addr_equal(sa, response->dst))
35685732ac8SCy Schubert 			continue;
35785732ac8SCy Schubert 		gas_server_handle_rx_comeback_req(response);
35885732ac8SCy Schubert 		return 0;
35985732ac8SCy Schubert 	}
36085732ac8SCy Schubert 
36185732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
36285732ac8SCy Schubert 		   " (dialog token %u)", MAC2STR(sa), dialog_token);
36385732ac8SCy Schubert 	return -1;
36485732ac8SCy Schubert }
36585732ac8SCy Schubert 
36685732ac8SCy Schubert 
36785732ac8SCy Schubert /**
36885732ac8SCy Schubert  * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
36985732ac8SCy Schubert  * @gas: GAS query data from gas_server_init()
37085732ac8SCy Schubert  * @da: Destination MAC address of the Action frame
37185732ac8SCy Schubert  * @sa: Source MAC address of the Action frame
37285732ac8SCy Schubert  * @bssid: BSSID of the Action frame
37385732ac8SCy Schubert  * @categ: Category of the Action frame
37485732ac8SCy Schubert  * @data: Payload of the Action frame
37585732ac8SCy Schubert  * @len: Length of @data
37685732ac8SCy Schubert  * @freq: Frequency (in MHz) on which the frame was received
37785732ac8SCy Schubert  * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not
37885732ac8SCy Schubert  */
gas_server_rx(struct gas_server * gas,const u8 * da,const u8 * sa,const u8 * bssid,u8 categ,const u8 * data,size_t len,int freq)37985732ac8SCy Schubert int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
38085732ac8SCy Schubert 		  const u8 *bssid, u8 categ, const u8 *data, size_t len,
38185732ac8SCy Schubert 		  int freq)
38285732ac8SCy Schubert {
38385732ac8SCy Schubert 	u8 action, dialog_token;
38485732ac8SCy Schubert 	const u8 *pos, *end;
38585732ac8SCy Schubert 
38685732ac8SCy Schubert 	if (!gas || len < 2)
38785732ac8SCy Schubert 		return -1;
38885732ac8SCy Schubert 
38985732ac8SCy Schubert 	if (categ == WLAN_ACTION_PROTECTED_DUAL)
39085732ac8SCy Schubert 		return -1; /* Not supported for now */
39185732ac8SCy Schubert 
39285732ac8SCy Schubert 	pos = data;
39385732ac8SCy Schubert 	end = data + len;
39485732ac8SCy Schubert 	action = *pos++;
39585732ac8SCy Schubert 	dialog_token = *pos++;
39685732ac8SCy Schubert 
39785732ac8SCy Schubert 	if (action != WLAN_PA_GAS_INITIAL_REQ &&
39885732ac8SCy Schubert 	    action != WLAN_PA_GAS_COMEBACK_REQ)
39985732ac8SCy Schubert 		return -1; /* Not a GAS request */
40085732ac8SCy Schubert 
40185732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
40285732ac8SCy Schubert 		   " SA=" MACSTR " BSSID=" MACSTR
40385732ac8SCy Schubert 		   " freq=%d dialog_token=%u len=%u",
40485732ac8SCy Schubert 		   action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
40585732ac8SCy Schubert 		   MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
40685732ac8SCy Schubert 		   (unsigned int) len);
40785732ac8SCy Schubert 
40885732ac8SCy Schubert 	if (action == WLAN_PA_GAS_INITIAL_REQ)
40985732ac8SCy Schubert 		return gas_server_rx_initial_req(gas, da, sa, bssid,
41085732ac8SCy Schubert 						 freq, dialog_token,
41185732ac8SCy Schubert 						 pos, end - pos);
41285732ac8SCy Schubert 	return gas_server_rx_comeback_req(gas, da, sa, bssid,
41385732ac8SCy Schubert 					  freq, dialog_token);
41485732ac8SCy Schubert }
41585732ac8SCy Schubert 
41685732ac8SCy Schubert 
gas_server_handle_tx_status(struct gas_server_response * response,int ack)41785732ac8SCy Schubert static void gas_server_handle_tx_status(struct gas_server_response *response,
41885732ac8SCy Schubert 					int ack)
41985732ac8SCy Schubert {
420c1d255d3SCy Schubert 	if (ack && response->resp &&
421c1d255d3SCy Schubert 	    response->offset < wpabuf_len(response->resp)) {
42285732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
42385732ac8SCy Schubert 			   "GAS: More fragments remaining - keep pending entry");
42485732ac8SCy Schubert 		return;
42585732ac8SCy Schubert 	}
42685732ac8SCy Schubert 
427c1d255d3SCy Schubert 	if (ack && !response->resp && response->comeback_delay) {
428c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
429c1d255d3SCy Schubert 			   "GAS: Waiting for response - keep pending entry");
430c1d255d3SCy Schubert 		return;
431c1d255d3SCy Schubert 	}
432c1d255d3SCy Schubert 
43385732ac8SCy Schubert 	if (!ack)
43485732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
43585732ac8SCy Schubert 			   "GAS: No ACK received - drop pending entry");
43685732ac8SCy Schubert 	else
43785732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
43885732ac8SCy Schubert 			   "GAS: Last fragment of the response sent out - drop pending entry");
43985732ac8SCy Schubert 
44085732ac8SCy Schubert 	response->handler->status_cb(response->handler->ctx,
44185732ac8SCy Schubert 				     response->resp, ack);
44285732ac8SCy Schubert 	response->resp = NULL;
44385732ac8SCy Schubert 	dl_list_del(&response->list);
44485732ac8SCy Schubert 	gas_server_free_response(response);
44585732ac8SCy Schubert }
44685732ac8SCy Schubert 
44785732ac8SCy Schubert 
gas_server_tx_status(struct gas_server * gas,const u8 * dst,const u8 * data,size_t data_len,int ack)44885732ac8SCy Schubert void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
44985732ac8SCy Schubert 			  size_t data_len, int ack)
45085732ac8SCy Schubert {
45185732ac8SCy Schubert 	const u8 *pos;
45285732ac8SCy Schubert 	u8 action, code, dialog_token;
45385732ac8SCy Schubert 	struct gas_server_response *response;
45485732ac8SCy Schubert 
45585732ac8SCy Schubert 	if (data_len < 24 + 3)
45685732ac8SCy Schubert 		return;
45785732ac8SCy Schubert 	pos = data + 24;
45885732ac8SCy Schubert 	action = *pos++;
45985732ac8SCy Schubert 	code = *pos++;
46085732ac8SCy Schubert 	dialog_token = *pos++;
46185732ac8SCy Schubert 	if (action != WLAN_ACTION_PUBLIC ||
46285732ac8SCy Schubert 	    (code != WLAN_PA_GAS_INITIAL_RESP &&
46385732ac8SCy Schubert 	     code != WLAN_PA_GAS_COMEBACK_RESP))
46485732ac8SCy Schubert 		return;
46585732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
46685732ac8SCy Schubert 		   " ack=%d %s dialog_token=%u",
46785732ac8SCy Schubert 		   MAC2STR(dst), ack,
46885732ac8SCy Schubert 		   code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
46985732ac8SCy Schubert 		   dialog_token);
47085732ac8SCy Schubert 	dl_list_for_each(response, &gas->responses, struct gas_server_response,
47185732ac8SCy Schubert 			 list) {
47285732ac8SCy Schubert 		if (response->dialog_token != dialog_token ||
473*a90b9d01SCy Schubert 		    !ether_addr_equal(dst, response->dst))
47485732ac8SCy Schubert 			continue;
47585732ac8SCy Schubert 		gas_server_handle_tx_status(response, ack);
47685732ac8SCy Schubert 		return;
47785732ac8SCy Schubert 	}
47885732ac8SCy Schubert 
47985732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
48085732ac8SCy Schubert }
48185732ac8SCy Schubert 
48285732ac8SCy Schubert 
gas_server_set_resp(struct gas_server * gas,void * resp_ctx,struct wpabuf * resp)483c1d255d3SCy Schubert int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
484c1d255d3SCy Schubert 			struct wpabuf *resp)
485c1d255d3SCy Schubert {
486c1d255d3SCy Schubert 	struct gas_server_response *tmp, *response = NULL;
487c1d255d3SCy Schubert 
488c1d255d3SCy Schubert 	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
489c1d255d3SCy Schubert 			 list) {
490c1d255d3SCy Schubert 		if (tmp == resp_ctx) {
491c1d255d3SCy Schubert 			response = tmp;
492c1d255d3SCy Schubert 			break;
493c1d255d3SCy Schubert 		}
494c1d255d3SCy Schubert 	}
495c1d255d3SCy Schubert 
496c1d255d3SCy Schubert 	if (!response || response->resp)
497c1d255d3SCy Schubert 		return -1;
498c1d255d3SCy Schubert 
499*a90b9d01SCy Schubert 	if (!response->initial_resp_sent) {
500*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "GAS: Send the delayed initial response");
501*a90b9d01SCy Schubert 		gas_server_send_resp(gas, response, resp, 0);
502*a90b9d01SCy Schubert 		return 0;
503*a90b9d01SCy Schubert 	}
504*a90b9d01SCy Schubert 
505c1d255d3SCy Schubert 	response->resp = resp;
506c1d255d3SCy Schubert 	return 0;
507c1d255d3SCy Schubert }
508c1d255d3SCy Schubert 
509c1d255d3SCy Schubert 
gas_server_set_comeback_delay(struct gas_server * gas,void * resp_ctx,u16 comeback_delay)510*a90b9d01SCy Schubert int gas_server_set_comeback_delay(struct gas_server *gas, void *resp_ctx,
511*a90b9d01SCy Schubert 				  u16 comeback_delay)
512*a90b9d01SCy Schubert {
513*a90b9d01SCy Schubert 	struct gas_server_response *tmp, *response = NULL;
514*a90b9d01SCy Schubert 
515*a90b9d01SCy Schubert 	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
516*a90b9d01SCy Schubert 			 list) {
517*a90b9d01SCy Schubert 		if (tmp == resp_ctx) {
518*a90b9d01SCy Schubert 			response = tmp;
519*a90b9d01SCy Schubert 			break;
520*a90b9d01SCy Schubert 		}
521*a90b9d01SCy Schubert 	}
522*a90b9d01SCy Schubert 
523*a90b9d01SCy Schubert 	if (!response || response->initial_resp_sent)
524*a90b9d01SCy Schubert 		return -1;
525*a90b9d01SCy Schubert 
526*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG,
527*a90b9d01SCy Schubert 		   "GAS: Send the delayed initial response with comeback delay %u",
528*a90b9d01SCy Schubert 		   comeback_delay);
529*a90b9d01SCy Schubert 	gas_server_send_resp(gas, response, NULL, comeback_delay);
530*a90b9d01SCy Schubert 
531*a90b9d01SCy Schubert 	return 0;
532*a90b9d01SCy Schubert }
533*a90b9d01SCy Schubert 
534*a90b9d01SCy Schubert 
gas_server_response_sent(struct gas_server * gas,void * resp_ctx)535c1d255d3SCy Schubert bool gas_server_response_sent(struct gas_server *gas, void *resp_ctx)
536c1d255d3SCy Schubert {
537c1d255d3SCy Schubert 	struct gas_server_response *tmp;
538c1d255d3SCy Schubert 
539c1d255d3SCy Schubert 	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
540c1d255d3SCy Schubert 			 list) {
541c1d255d3SCy Schubert 		if (tmp == resp_ctx)
542c1d255d3SCy Schubert 			return tmp->resp &&
543c1d255d3SCy Schubert 				tmp->offset == wpabuf_len(tmp->resp);
544c1d255d3SCy Schubert 	}
545c1d255d3SCy Schubert 
546c1d255d3SCy Schubert 	return false;
547c1d255d3SCy Schubert }
548c1d255d3SCy Schubert 
549c1d255d3SCy Schubert 
gas_server_init(void * ctx,void (* tx)(void * ctx,int freq,const u8 * da,struct wpabuf * buf,unsigned int wait_time))55085732ac8SCy Schubert struct gas_server * gas_server_init(void *ctx,
55185732ac8SCy Schubert 				    void (*tx)(void *ctx, int freq,
55285732ac8SCy Schubert 					       const u8 *da,
55385732ac8SCy Schubert 					       struct wpabuf *buf,
55485732ac8SCy Schubert 					       unsigned int wait_time))
55585732ac8SCy Schubert {
55685732ac8SCy Schubert 	struct gas_server *gas;
55785732ac8SCy Schubert 
55885732ac8SCy Schubert 	gas = os_zalloc(sizeof(*gas));
55985732ac8SCy Schubert 	if (!gas)
56085732ac8SCy Schubert 		return NULL;
56185732ac8SCy Schubert 	gas->ctx = ctx;
56285732ac8SCy Schubert 	gas->tx = tx;
56385732ac8SCy Schubert 	dl_list_init(&gas->handlers);
56485732ac8SCy Schubert 	dl_list_init(&gas->responses);
56585732ac8SCy Schubert 	return gas;
56685732ac8SCy Schubert }
56785732ac8SCy Schubert 
56885732ac8SCy Schubert 
gas_server_deinit(struct gas_server * gas)56985732ac8SCy Schubert void gas_server_deinit(struct gas_server *gas)
57085732ac8SCy Schubert {
57185732ac8SCy Schubert 	struct gas_server_handler *handler, *tmp;
57285732ac8SCy Schubert 	struct gas_server_response *response, *tmp_r;
57385732ac8SCy Schubert 
57485732ac8SCy Schubert 	if (!gas)
57585732ac8SCy Schubert 		return;
57685732ac8SCy Schubert 
57785732ac8SCy Schubert 	dl_list_for_each_safe(handler, tmp, &gas->handlers,
57885732ac8SCy Schubert 			      struct gas_server_handler, list) {
57985732ac8SCy Schubert 		dl_list_del(&handler->list);
58085732ac8SCy Schubert 		os_free(handler);
58185732ac8SCy Schubert 	}
58285732ac8SCy Schubert 
58385732ac8SCy Schubert 	dl_list_for_each_safe(response, tmp_r, &gas->responses,
58485732ac8SCy Schubert 			      struct gas_server_response, list) {
58585732ac8SCy Schubert 		dl_list_del(&response->list);
58685732ac8SCy Schubert 		gas_server_free_response(response);
58785732ac8SCy Schubert 	}
58885732ac8SCy Schubert 
58985732ac8SCy Schubert 	os_free(gas);
59085732ac8SCy Schubert }
59185732ac8SCy Schubert 
59285732ac8SCy Schubert 
gas_server_register(struct gas_server * gas,const u8 * adv_proto_id,u8 adv_proto_id_len,struct wpabuf * (* req_cb)(void * ctx,void * resp_ctx,const u8 * sa,const u8 * query,size_t query_len,int * comeback_delay),void (* status_cb)(void * ctx,struct wpabuf * resp,int ok),void * ctx)59385732ac8SCy Schubert int gas_server_register(struct gas_server *gas,
59485732ac8SCy Schubert 			const u8 *adv_proto_id, u8 adv_proto_id_len,
59585732ac8SCy Schubert 			struct wpabuf *
596c1d255d3SCy Schubert 			(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
597c1d255d3SCy Schubert 				  const u8 *query, size_t query_len,
598*a90b9d01SCy Schubert 				  int *comeback_delay),
59985732ac8SCy Schubert 			void (*status_cb)(void *ctx, struct wpabuf *resp,
60085732ac8SCy Schubert 					  int ok),
60185732ac8SCy Schubert 			void *ctx)
60285732ac8SCy Schubert {
60385732ac8SCy Schubert 	struct gas_server_handler *handler;
60485732ac8SCy Schubert 
60585732ac8SCy Schubert 	if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
60685732ac8SCy Schubert 		return -1;
60785732ac8SCy Schubert 	handler = os_zalloc(sizeof(*handler));
60885732ac8SCy Schubert 	if (!handler)
60985732ac8SCy Schubert 		return -1;
61085732ac8SCy Schubert 
61185732ac8SCy Schubert 	os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
61285732ac8SCy Schubert 	handler->adv_proto_id_len = adv_proto_id_len;
61385732ac8SCy Schubert 	handler->req_cb = req_cb;
61485732ac8SCy Schubert 	handler->status_cb = status_cb;
61585732ac8SCy Schubert 	handler->ctx = ctx;
61685732ac8SCy Schubert 	handler->gas = gas;
61785732ac8SCy Schubert 	dl_list_add(&gas->handlers, &handler->list);
61885732ac8SCy Schubert 
61985732ac8SCy Schubert 	return 0;
62085732ac8SCy Schubert }
621