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