• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 struct 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 
35 struct 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 
47 struct 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 
55 static void gas_server_free_response(struct gas_server_response *response);
56 
57 
gas_server_response_timeout(void * eloop_ctx,void * user_ctx)58 static 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 
gas_server_free_response(struct gas_server_response * response)77 static 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 
88 static void
gas_server_send_resp(struct gas_server * gas,struct gas_server_handler * handler,struct gas_server_response * response,const u8 * da,int freq,u8 dialog_token,struct wpabuf * query_resp,u16 comeback_delay)89 gas_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 
163 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)164 gas_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 
255 static void
gas_server_handle_rx_comeback_req(struct gas_server_response * response)256 gas_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 
328 send_resp:
329 	gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
330 	wpabuf_free(resp);
331 }
332 
333 
334 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)335 gas_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  */
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)367 int 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 
gas_server_handle_tx_status(struct gas_server_response * response,int ack)405 static 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 
gas_server_tx_status(struct gas_server * gas,const u8 * dst,const u8 * data,size_t data_len,int ack)436 void 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 
gas_server_set_resp(struct gas_server * gas,void * resp_ctx,struct wpabuf * resp)471 int 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 
gas_server_response_sent(struct gas_server * gas,void * resp_ctx)492 bool 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 
gas_server_init(void * ctx,void (* tx)(void * ctx,int freq,const u8 * da,struct wpabuf * buf,unsigned int wait_time))507 struct 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 
gas_server_deinit(struct gas_server * gas)526 void 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 
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,u16 * comeback_delay),void (* status_cb)(void * ctx,struct wpabuf * resp,int ok),void * ctx)550 int 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