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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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