2 * Generic advertisement service (GAS) server
3 * Copyright (c) 2017, Qualcomm Atheros, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
11 #include "utils/common.h"
12 #include "utils/list.h"
13 #include "utils/eloop.h"
14 #include "ieee802_11_defs.h"
16 #include "gas_server.h"
19 #define MAX_ADV_PROTO_ID_LEN 10
20 #define GAS_QUERY_TIMEOUT 10
22 struct gas_server_handler {
24 u8 adv_proto_id[MAX_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);
30 struct gas_server *gas;
33 struct gas_server_response {
41 struct gas_server_handler *handler;
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);
52 static void gas_server_free_response(struct gas_server_response *response);
55 static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
57 struct gas_server_response *response = eloop_ctx;
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,
67 response->resp = NULL;
68 dl_list_del(&response->list);
69 gas_server_free_response(response);
73 static void gas_server_free_response(struct gas_server_response *response)
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);
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)
89 size_t max_len = (freq > 56160) ? 928 : 1400;
90 size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
94 struct gas_server_response *response;
99 response = os_zalloc(sizeof(*response));
101 wpabuf_free(query_resp);
104 wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
105 response->freq = freq;
106 response->handler = handler;
107 os_memcpy(response->dst, da, ETH_ALEN);
108 response->dialog_token = dialog_token;
109 if (hdr_len + wpabuf_len(query_resp) > max_len) {
110 /* Need to use comeback to initiate fragmentation */
114 /* Full response fits into the initial response */
116 resp_frag_len = wpabuf_len(query_resp);
119 resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS,
121 handler->adv_proto_id_len +
124 wpabuf_free(query_resp);
125 gas_server_free_response(response);
129 /* Advertisement Protocol element */
130 wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
131 wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
132 wpabuf_put_u8(resp, 0x7f);
133 /* Advertisement Protocol ID */
134 wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
136 /* Query Response Length */
137 wpabuf_put_le16(resp, resp_frag_len);
139 wpabuf_put_buf(resp, query_resp);
141 if (comeback_delay) {
142 wpa_printf(MSG_DEBUG,
143 "GAS: Need to fragment query response");
145 wpa_printf(MSG_DEBUG,
146 "GAS: Full query response fits in the GAS Initial Response frame");
148 response->offset = resp_frag_len;
149 response->resp = query_resp;
150 dl_list_add(&gas->responses, &response->list);
151 gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0);
153 eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
154 gas_server_response_timeout, response, NULL);
159 gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
160 const u8 *bssid, int freq, u8 dialog_token,
161 const u8 *data, size_t len)
163 const u8 *pos, *end, *adv_proto, *query_req;
166 struct gas_server_handler *handler;
169 wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
174 if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
175 wpa_printf(MSG_DEBUG,
176 "GAS: No Advertisement Protocol element found");
180 adv_proto_len = *pos++;
181 if (end - pos < adv_proto_len || adv_proto_len < 2) {
182 wpa_printf(MSG_DEBUG,
183 "GAS: Truncated Advertisement Protocol element");
188 pos += adv_proto_len;
189 wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
190 adv_proto, adv_proto_len);
193 wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
196 query_req_len = WPA_GET_LE16(pos);
198 if (end - pos < query_req_len) {
199 wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
203 pos += query_req_len;
204 wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
205 query_req, query_req_len);
208 wpa_hexdump(MSG_MSGDUMP,
209 "GAS: Ignored extra data after Query Request field",
213 dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
215 if (adv_proto_len < 1 + handler->adv_proto_id_len ||
216 os_memcmp(adv_proto + 1, handler->adv_proto_id,
217 handler->adv_proto_id_len) != 0)
220 wpa_printf(MSG_DEBUG,
221 "GAS: Calling handler for the requested Advertisement Protocol ID");
222 resp = handler->req_cb(handler->ctx, sa, query_req,
224 wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
226 gas_server_send_resp(gas, handler, sa, freq, dialog_token,
231 wpa_printf(MSG_DEBUG,
232 "GAS: No registered handler for the requested Advertisement Protocol ID");
238 gas_server_handle_rx_comeback_req(struct gas_server_response *response)
240 struct gas_server_handler *handler = response->handler;
241 struct gas_server *gas = handler->gas;
242 size_t max_len = (response->freq > 56160) ? 928 : 1400;
243 size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
244 size_t remaining, resp_frag_len;
247 remaining = wpabuf_len(response->resp) - response->offset;
248 if (hdr_len + remaining > max_len)
249 resp_frag_len = max_len - hdr_len;
251 resp_frag_len = remaining;
252 wpa_printf(MSG_DEBUG,
253 "GAS: Sending out %u/%u remaining Query Response octets",
254 (unsigned int) resp_frag_len, (unsigned int) remaining);
256 resp = gas_build_comeback_resp(response->dialog_token,
259 resp_frag_len < remaining, 0,
260 handler->adv_proto_id_len +
263 dl_list_del(&response->list);
264 gas_server_free_response(response);
268 /* Advertisement Protocol element */
269 wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
270 wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
271 wpabuf_put_u8(resp, 0x7f);
272 /* Advertisement Protocol ID */
273 wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
275 /* Query Response Length */
276 wpabuf_put_le16(resp, resp_frag_len);
277 wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
280 response->offset += resp_frag_len;
282 gas->tx(gas->ctx, response->freq, response->dst, resp,
283 remaining > resp_frag_len ? 2000 : 0);
289 gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
290 const u8 *bssid, int freq, u8 dialog_token)
292 struct gas_server_response *response;
294 dl_list_for_each(response, &gas->responses, struct gas_server_response,
296 if (response->dialog_token != dialog_token ||
297 os_memcmp(sa, response->dst, ETH_ALEN) != 0)
299 gas_server_handle_rx_comeback_req(response);
303 wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
304 " (dialog token %u)", MAC2STR(sa), dialog_token);
310 * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
311 * @gas: GAS query data from gas_server_init()
312 * @da: Destination MAC address of the Action frame
313 * @sa: Source MAC address of the Action frame
314 * @bssid: BSSID of the Action frame
315 * @categ: Category of the Action frame
316 * @data: Payload of the Action frame
317 * @len: Length of @data
318 * @freq: Frequency (in MHz) on which the frame was received
319 * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not
321 int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
322 const u8 *bssid, u8 categ, const u8 *data, size_t len,
325 u8 action, dialog_token;
331 if (categ == WLAN_ACTION_PROTECTED_DUAL)
332 return -1; /* Not supported for now */
337 dialog_token = *pos++;
339 if (action != WLAN_PA_GAS_INITIAL_REQ &&
340 action != WLAN_PA_GAS_COMEBACK_REQ)
341 return -1; /* Not a GAS request */
343 wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
344 " SA=" MACSTR " BSSID=" MACSTR
345 " freq=%d dialog_token=%u len=%u",
346 action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
347 MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
350 if (action == WLAN_PA_GAS_INITIAL_REQ)
351 return gas_server_rx_initial_req(gas, da, sa, bssid,
354 return gas_server_rx_comeback_req(gas, da, sa, bssid,
359 static void gas_server_handle_tx_status(struct gas_server_response *response,
362 if (ack && response->offset < wpabuf_len(response->resp)) {
363 wpa_printf(MSG_DEBUG,
364 "GAS: More fragments remaining - keep pending entry");
369 wpa_printf(MSG_DEBUG,
370 "GAS: No ACK received - drop pending entry");
372 wpa_printf(MSG_DEBUG,
373 "GAS: Last fragment of the response sent out - drop pending entry");
375 response->handler->status_cb(response->handler->ctx,
376 response->resp, ack);
377 response->resp = NULL;
378 dl_list_del(&response->list);
379 gas_server_free_response(response);
383 void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
384 size_t data_len, int ack)
387 u8 action, code, dialog_token;
388 struct gas_server_response *response;
390 if (data_len < 24 + 3)
395 dialog_token = *pos++;
396 if (action != WLAN_ACTION_PUBLIC ||
397 (code != WLAN_PA_GAS_INITIAL_RESP &&
398 code != WLAN_PA_GAS_COMEBACK_RESP))
400 wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
401 " ack=%d %s dialog_token=%u",
403 code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
405 dl_list_for_each(response, &gas->responses, struct gas_server_response,
407 if (response->dialog_token != dialog_token ||
408 os_memcmp(dst, response->dst, ETH_ALEN) != 0)
410 gas_server_handle_tx_status(response, ack);
414 wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
418 struct gas_server * gas_server_init(void *ctx,
419 void (*tx)(void *ctx, int freq,
422 unsigned int wait_time))
424 struct gas_server *gas;
426 gas = os_zalloc(sizeof(*gas));
431 dl_list_init(&gas->handlers);
432 dl_list_init(&gas->responses);
437 void gas_server_deinit(struct gas_server *gas)
439 struct gas_server_handler *handler, *tmp;
440 struct gas_server_response *response, *tmp_r;
445 dl_list_for_each_safe(handler, tmp, &gas->handlers,
446 struct gas_server_handler, list) {
447 dl_list_del(&handler->list);
451 dl_list_for_each_safe(response, tmp_r, &gas->responses,
452 struct gas_server_response, list) {
453 dl_list_del(&response->list);
454 gas_server_free_response(response);
461 int gas_server_register(struct gas_server *gas,
462 const u8 *adv_proto_id, u8 adv_proto_id_len,
464 (*req_cb)(void *ctx, const u8 *sa,
465 const u8 *query, size_t query_len),
466 void (*status_cb)(void *ctx, struct wpabuf *resp,
470 struct gas_server_handler *handler;
472 if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
474 handler = os_zalloc(sizeof(*handler));
478 os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
479 handler->adv_proto_id_len = adv_proto_id_len;
480 handler->req_cb = req_cb;
481 handler->status_cb = status_cb;
484 dl_list_add(&gas->handlers, &handler->list);