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