1/*
2 * Generic advertisement service (GAS) server
3 * Copyright (c) 2017, Qualcomm Atheros, Inc.
4 * Copyright (c) 2020, The Linux Foundation
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10#include "includes.h"
11
12#include "utils/common.h"
13#include "utils/list.h"
14#include "utils/eloop.h"
15#include "ieee802_11_defs.h"
16#include "gas.h"
17#include "gas_server.h"
18
19
20#define MAX_ADV_PROTO_ID_LEN 10
21#define GAS_QUERY_TIMEOUT 10
22
23struct gas_server_handler {
24	struct dl_list list;
25	u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
26	u8 adv_proto_id_len;
27	struct wpabuf * (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
28				  const u8 *query, size_t query_len,
29				  u16 *comeback_delay);
30	void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
31	void *ctx;
32	struct gas_server *gas;
33};
34
35struct gas_server_response {
36	struct dl_list list;
37	size_t offset;
38	u8 frag_id;
39	struct wpabuf *resp;
40	int freq;
41	u8 dst[ETH_ALEN];
42	u8 dialog_token;
43	struct gas_server_handler *handler;
44	u16 comeback_delay;
45};
46
47struct gas_server {
48	struct dl_list handlers; /* struct gas_server_handler::list */
49	struct dl_list responses; /* struct gas_server_response::list */
50	void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp,
51		   unsigned int wait_time);
52	void *ctx;
53};
54
55static void gas_server_free_response(struct gas_server_response *response);
56
57
58static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
59{
60	struct gas_server_response *response = eloop_ctx;
61
62	wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
63		   " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
64		   response, MAC2STR(response->dst), response->dialog_token,
65		   response->freq, response->frag_id,
66		   (unsigned long) response->offset,
67		   (unsigned long) (response->resp ?
68				    wpabuf_len(response->resp) : 0));
69	response->handler->status_cb(response->handler->ctx,
70				     response->resp, 0);
71	response->resp = NULL;
72	dl_list_del(&response->list);
73	gas_server_free_response(response);
74}
75
76
77static void gas_server_free_response(struct gas_server_response *response)
78{
79	if (!response)
80		return;
81	wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response);
82	eloop_cancel_timeout(gas_server_response_timeout, response, NULL);
83	wpabuf_free(response->resp);
84	os_free(response);
85}
86
87
88static void
89gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
90		     struct gas_server_response *response,
91		     const u8 *da, int freq, u8 dialog_token,
92		     struct wpabuf *query_resp, u16 comeback_delay)
93{
94	size_t max_len = (freq > 56160) ? 928 : 1400;
95	size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
96	size_t resp_frag_len;
97	struct wpabuf *resp;
98
99	if (comeback_delay == 0 && !query_resp) {
100		gas_server_free_response(response);
101		return;
102	}
103
104	response->freq = freq;
105	response->handler = handler;
106	os_memcpy(response->dst, da, ETH_ALEN);
107	response->dialog_token = dialog_token;
108	if (comeback_delay) {
109		/* Need more time to prepare the response */
110		resp_frag_len = 0;
111		response->comeback_delay = comeback_delay;
112	} else if (hdr_len + wpabuf_len(query_resp) > max_len) {
113		/* Need to use comeback to initiate fragmentation */
114		comeback_delay = 1;
115		resp_frag_len = 0;
116	} else {
117		/* Full response fits into the initial response */
118		comeback_delay = 0;
119		resp_frag_len = wpabuf_len(query_resp);
120	}
121
122	resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS,
123				      comeback_delay,
124				      handler->adv_proto_id_len +
125				      resp_frag_len);
126	if (!resp) {
127		wpabuf_free(query_resp);
128		gas_server_free_response(response);
129		return;
130	}
131
132	/* Advertisement Protocol element */
133	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
134	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
135	wpabuf_put_u8(resp, 0x7f);
136	/* Advertisement Protocol ID */
137	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
138
139	/* Query Response Length */
140	wpabuf_put_le16(resp, resp_frag_len);
141	if (!comeback_delay && query_resp)
142		wpabuf_put_buf(resp, query_resp);
143
144	if (comeback_delay && !query_resp) {
145		wpa_printf(MSG_DEBUG, "GAS: No response available yet");
146	} else if (comeback_delay) {
147		wpa_printf(MSG_DEBUG,
148			   "GAS: Need to fragment query response");
149	} else {
150		wpa_printf(MSG_DEBUG,
151			   "GAS: Full query response fits in the GAS Initial Response frame");
152	}
153	response->offset = resp_frag_len;
154	response->resp = query_resp;
155	dl_list_add(&gas->responses, &response->list);
156	gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0);
157	wpabuf_free(resp);
158	eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
159			       gas_server_response_timeout, response, NULL);
160}
161
162
163static int
164gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
165			  const u8 *bssid, int freq, u8 dialog_token,
166			  const u8 *data, size_t len)
167{
168	const u8 *pos, *end, *adv_proto, *query_req;
169	u8 adv_proto_len;
170	u16 query_req_len;
171	struct gas_server_handler *handler;
172	struct wpabuf *resp;
173	struct gas_server_response *response;
174
175	wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
176		    data, len);
177	pos = data;
178	end = data + len;
179
180	if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
181		wpa_printf(MSG_DEBUG,
182			   "GAS: No Advertisement Protocol element found");
183		return -1;
184	}
185	pos++;
186	adv_proto_len = *pos++;
187	if (end - pos < adv_proto_len || adv_proto_len < 2) {
188		wpa_printf(MSG_DEBUG,
189			   "GAS: Truncated Advertisement Protocol element");
190		return -1;
191	}
192
193	adv_proto = pos;
194	pos += adv_proto_len;
195	wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
196		    adv_proto, adv_proto_len);
197
198	if (end - pos < 2) {
199		wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
200		return -1;
201	}
202	query_req_len = WPA_GET_LE16(pos);
203	pos += 2;
204	if (end - pos < query_req_len) {
205		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
206		return -1;
207	}
208	query_req = pos;
209	pos += query_req_len;
210	wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
211		    query_req, query_req_len);
212
213	if (pos < end) {
214		wpa_hexdump(MSG_MSGDUMP,
215			    "GAS: Ignored extra data after Query Request field",
216			    pos, end - pos);
217	}
218
219	response = os_zalloc(sizeof(*response));
220	if (!response)
221		return -1;
222
223	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
224	dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
225			 list) {
226		u16 comeback_delay = 0;
227
228		if (adv_proto_len < 1 + handler->adv_proto_id_len ||
229		    os_memcmp(adv_proto + 1, handler->adv_proto_id,
230			      handler->adv_proto_id_len) != 0)
231			continue;
232
233		wpa_printf(MSG_DEBUG,
234			   "GAS: Calling handler for the requested Advertisement Protocol ID");
235		resp = handler->req_cb(handler->ctx, response, sa, query_req,
236				       query_req_len, &comeback_delay);
237		wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
238				resp);
239		if (comeback_delay)
240			wpa_printf(MSG_DEBUG,
241				   "GAS: Handler requested comeback delay: %u TU",
242				   comeback_delay);
243		gas_server_send_resp(gas, handler, response, sa, freq,
244				     dialog_token, resp, comeback_delay);
245		return 0;
246	}
247
248	wpa_printf(MSG_DEBUG,
249		   "GAS: No registered handler for the requested Advertisement Protocol ID");
250	gas_server_free_response(response);
251	return -1;
252}
253
254
255static void
256gas_server_handle_rx_comeback_req(struct gas_server_response *response)
257{
258	struct gas_server_handler *handler = response->handler;
259	struct gas_server *gas = handler->gas;
260	size_t max_len = (response->freq > 56160) ? 928 : 1400;
261	size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
262	size_t remaining, resp_frag_len;
263	struct wpabuf *resp;
264	unsigned int wait_time = 0;
265
266	if (!response->resp) {
267		resp = gas_build_comeback_resp(response->dialog_token,
268					       WLAN_STATUS_SUCCESS, 0, 0,
269					       response->comeback_delay,
270					       handler->adv_proto_id_len);
271		if (!resp) {
272			dl_list_del(&response->list);
273			gas_server_free_response(response);
274			return;
275		}
276
277		/* Advertisement Protocol element */
278		wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
279		wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
280		wpabuf_put_u8(resp, 0x7f);
281		/* Advertisement Protocol ID */
282		wpabuf_put_data(resp, handler->adv_proto_id,
283				handler->adv_proto_id_len);
284
285		/* Query Response Length */
286		wpabuf_put_le16(resp, 0);
287		goto send_resp;
288	}
289
290	remaining = wpabuf_len(response->resp) - response->offset;
291	if (hdr_len + remaining > max_len)
292		resp_frag_len = max_len - hdr_len;
293	else
294		resp_frag_len = remaining;
295	wpa_printf(MSG_DEBUG,
296		   "GAS: Sending out %u/%u remaining Query Response octets",
297		   (unsigned int) resp_frag_len, (unsigned int) remaining);
298
299	resp = gas_build_comeback_resp(response->dialog_token,
300				       WLAN_STATUS_SUCCESS,
301				       response->frag_id++,
302				       resp_frag_len < remaining, 0,
303				       handler->adv_proto_id_len +
304				       resp_frag_len);
305	if (!resp) {
306		dl_list_del(&response->list);
307		gas_server_free_response(response);
308		return;
309	}
310
311	/* Advertisement Protocol element */
312	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
313	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
314	wpabuf_put_u8(resp, 0x7f);
315	/* Advertisement Protocol ID */
316	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
317
318	/* Query Response Length */
319	wpabuf_put_le16(resp, resp_frag_len);
320	wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
321			resp_frag_len);
322
323	response->offset += resp_frag_len;
324
325	if (remaining > resp_frag_len)
326		wait_time = 2000;
327
328send_resp:
329	gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
330	wpabuf_free(resp);
331}
332
333
334static int
335gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
336			   const u8 *bssid, int freq, u8 dialog_token)
337{
338	struct gas_server_response *response;
339
340	dl_list_for_each(response, &gas->responses, struct gas_server_response,
341			 list) {
342		if (response->dialog_token != dialog_token ||
343		    os_memcmp(sa, response->dst, ETH_ALEN) != 0)
344			continue;
345		gas_server_handle_rx_comeback_req(response);
346		return 0;
347	}
348
349	wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
350		   " (dialog token %u)", MAC2STR(sa), dialog_token);
351	return -1;
352}
353
354
355/**
356 * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
357 * @gas: GAS query data from gas_server_init()
358 * @da: Destination MAC address of the Action frame
359 * @sa: Source MAC address of the Action frame
360 * @bssid: BSSID of the Action frame
361 * @categ: Category of the Action frame
362 * @data: Payload of the Action frame
363 * @len: Length of @data
364 * @freq: Frequency (in MHz) on which the frame was received
365 * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not
366 */
367int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
368		  const u8 *bssid, u8 categ, const u8 *data, size_t len,
369		  int freq)
370{
371	u8 action, dialog_token;
372	const u8 *pos, *end;
373
374	if (!gas || len < 2)
375		return -1;
376
377	if (categ == WLAN_ACTION_PROTECTED_DUAL)
378		return -1; /* Not supported for now */
379
380	pos = data;
381	end = data + len;
382	action = *pos++;
383	dialog_token = *pos++;
384
385	if (action != WLAN_PA_GAS_INITIAL_REQ &&
386	    action != WLAN_PA_GAS_COMEBACK_REQ)
387		return -1; /* Not a GAS request */
388
389	wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
390		   " SA=" MACSTR " BSSID=" MACSTR
391		   " freq=%d dialog_token=%u len=%u",
392		   action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
393		   MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
394		   (unsigned int) len);
395
396	if (action == WLAN_PA_GAS_INITIAL_REQ)
397		return gas_server_rx_initial_req(gas, da, sa, bssid,
398						 freq, dialog_token,
399						 pos, end - pos);
400	return gas_server_rx_comeback_req(gas, da, sa, bssid,
401					  freq, dialog_token);
402}
403
404
405static void gas_server_handle_tx_status(struct gas_server_response *response,
406					int ack)
407{
408	if (ack && response->resp &&
409	    response->offset < wpabuf_len(response->resp)) {
410		wpa_printf(MSG_DEBUG,
411			   "GAS: More fragments remaining - keep pending entry");
412		return;
413	}
414
415	if (ack && !response->resp && response->comeback_delay) {
416		wpa_printf(MSG_DEBUG,
417			   "GAS: Waiting for response - keep pending entry");
418		return;
419	}
420
421	if (!ack)
422		wpa_printf(MSG_DEBUG,
423			   "GAS: No ACK received - drop pending entry");
424	else
425		wpa_printf(MSG_DEBUG,
426			   "GAS: Last fragment of the response sent out - drop pending entry");
427
428	response->handler->status_cb(response->handler->ctx,
429				     response->resp, ack);
430	response->resp = NULL;
431	dl_list_del(&response->list);
432	gas_server_free_response(response);
433}
434
435
436void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
437			  size_t data_len, int ack)
438{
439	const u8 *pos;
440	u8 action, code, dialog_token;
441	struct gas_server_response *response;
442
443	if (data_len < 24 + 3)
444		return;
445	pos = data + 24;
446	action = *pos++;
447	code = *pos++;
448	dialog_token = *pos++;
449	if (action != WLAN_ACTION_PUBLIC ||
450	    (code != WLAN_PA_GAS_INITIAL_RESP &&
451	     code != WLAN_PA_GAS_COMEBACK_RESP))
452		return;
453	wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
454		   " ack=%d %s dialog_token=%u",
455		   MAC2STR(dst), ack,
456		   code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
457		   dialog_token);
458	dl_list_for_each(response, &gas->responses, struct gas_server_response,
459			 list) {
460		if (response->dialog_token != dialog_token ||
461		    os_memcmp(dst, response->dst, ETH_ALEN) != 0)
462			continue;
463		gas_server_handle_tx_status(response, ack);
464		return;
465	}
466
467	wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
468}
469
470
471int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
472			struct wpabuf *resp)
473{
474	struct gas_server_response *tmp, *response = NULL;
475
476	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
477			 list) {
478		if (tmp == resp_ctx) {
479			response = tmp;
480			break;
481		}
482	}
483
484	if (!response || response->resp)
485		return -1;
486
487	response->resp = resp;
488	return 0;
489}
490
491
492bool gas_server_response_sent(struct gas_server *gas, void *resp_ctx)
493{
494	struct gas_server_response *tmp;
495
496	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
497			 list) {
498		if (tmp == resp_ctx)
499			return tmp->resp &&
500				tmp->offset == wpabuf_len(tmp->resp);
501	}
502
503	return false;
504}
505
506
507struct gas_server * gas_server_init(void *ctx,
508				    void (*tx)(void *ctx, int freq,
509					       const u8 *da,
510					       struct wpabuf *buf,
511					       unsigned int wait_time))
512{
513	struct gas_server *gas;
514
515	gas = os_zalloc(sizeof(*gas));
516	if (!gas)
517		return NULL;
518	gas->ctx = ctx;
519	gas->tx = tx;
520	dl_list_init(&gas->handlers);
521	dl_list_init(&gas->responses);
522	return gas;
523}
524
525
526void gas_server_deinit(struct gas_server *gas)
527{
528	struct gas_server_handler *handler, *tmp;
529	struct gas_server_response *response, *tmp_r;
530
531	if (!gas)
532		return;
533
534	dl_list_for_each_safe(handler, tmp, &gas->handlers,
535			      struct gas_server_handler, list) {
536		dl_list_del(&handler->list);
537		os_free(handler);
538	}
539
540	dl_list_for_each_safe(response, tmp_r, &gas->responses,
541			      struct gas_server_response, list) {
542		dl_list_del(&response->list);
543		gas_server_free_response(response);
544	}
545
546	os_free(gas);
547}
548
549
550int gas_server_register(struct gas_server *gas,
551			const u8 *adv_proto_id, u8 adv_proto_id_len,
552			struct wpabuf *
553			(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
554				  const u8 *query, size_t query_len,
555				  u16 *comeback_delay),
556			void (*status_cb)(void *ctx, struct wpabuf *resp,
557					  int ok),
558			void *ctx)
559{
560	struct gas_server_handler *handler;
561
562	if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
563		return -1;
564	handler = os_zalloc(sizeof(*handler));
565	if (!handler)
566		return -1;
567
568	os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
569	handler->adv_proto_id_len = adv_proto_id_len;
570	handler->req_cb = req_cb;
571	handler->status_cb = status_cb;
572	handler->ctx = ctx;
573	handler->gas = gas;
574	dl_list_add(&gas->handlers, &handler->list);
575
576	return 0;
577}
578