From 68ac45d53c53061cd25c14663627faf87979bb6e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 15 Jun 2020 21:20:44 +0300 Subject: [PATCH] GAS server: Support comeback delay from the request handler Allow GAS request handler function to request comeback delay before providing the response. Signed-off-by: Jouni Malinen --- src/common/gas_server.c | 123 +++++++++++++++++++++++++------- src/common/gas_server.h | 8 ++- wpa_supplicant/dpp_supplicant.c | 4 +- 3 files changed, 106 insertions(+), 29 deletions(-) diff --git a/src/common/gas_server.c b/src/common/gas_server.c index ca46758ce..5a1ea7899 100644 --- a/src/common/gas_server.c +++ b/src/common/gas_server.c @@ -1,6 +1,7 @@ /* * Generic advertisement service (GAS) server * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2020, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,8 +24,9 @@ struct gas_server_handler { struct dl_list list; u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN]; u8 adv_proto_id_len; - struct wpabuf * (*req_cb)(void *ctx, const u8 *sa, - const u8 *query, size_t query_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; struct gas_server *gas; @@ -39,6 +41,7 @@ struct gas_server_response { u8 dst[ETH_ALEN]; u8 dialog_token; struct gas_server_handler *handler; + u16 comeback_delay; }; struct gas_server { @@ -61,7 +64,8 @@ static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx) response, MAC2STR(response->dst), response->dialog_token, response->freq, response->frag_id, (unsigned long) response->offset, - (unsigned long) wpabuf_len(response->resp)); + (unsigned long) (response->resp ? + wpabuf_len(response->resp) : 0)); response->handler->status_cb(response->handler->ctx, response->resp, 0); response->resp = NULL; @@ -83,30 +87,27 @@ static void gas_server_free_response(struct gas_server_response *response) 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) + struct wpabuf *query_resp, u16 comeback_delay) { size_t max_len = (freq > 56160) ? 928 : 1400; size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2; size_t resp_frag_len; struct wpabuf *resp; - u16 comeback_delay; - struct gas_server_response *response; - if (!query_resp) + if (comeback_delay == 0 && !query_resp) return; - response = os_zalloc(sizeof(*response)); - if (!response) { - wpabuf_free(query_resp); - return; - } - wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response); response->freq = freq; response->handler = handler; os_memcpy(response->dst, da, ETH_ALEN); response->dialog_token = dialog_token; - if (hdr_len + wpabuf_len(query_resp) > max_len) { + if (comeback_delay) { + /* Need more time to prepare the response */ + resp_frag_len = 0; + response->comeback_delay = comeback_delay; + } else if (hdr_len + wpabuf_len(query_resp) > max_len) { /* Need to use comeback to initiate fragmentation */ comeback_delay = 1; resp_frag_len = 0; @@ -135,10 +136,12 @@ gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler, /* Query Response Length */ wpabuf_put_le16(resp, resp_frag_len); - if (!comeback_delay) + if (!comeback_delay && query_resp) wpabuf_put_buf(resp, query_resp); - if (comeback_delay) { + if (comeback_delay && !query_resp) { + wpa_printf(MSG_DEBUG, "GAS: No response available yet"); + } else if (comeback_delay) { wpa_printf(MSG_DEBUG, "GAS: Need to fragment query response"); } else { @@ -165,6 +168,7 @@ gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa, u16 query_req_len; struct gas_server_handler *handler; struct wpabuf *resp; + struct gas_server_response *response; wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame", data, len); @@ -210,8 +214,15 @@ gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa, pos, end - pos); } + response = os_zalloc(sizeof(*response)); + if (!response) + return -1; + + wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response); dl_list_for_each(handler, &gas->handlers, struct gas_server_handler, list) { + u16 comeback_delay = 0; + if (adv_proto_len < 1 + handler->adv_proto_id_len || os_memcmp(adv_proto + 1, handler->adv_proto_id, handler->adv_proto_id_len) != 0) @@ -219,17 +230,22 @@ gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa, wpa_printf(MSG_DEBUG, "GAS: Calling handler for the requested Advertisement Protocol ID"); - resp = handler->req_cb(handler->ctx, sa, query_req, - query_req_len); + resp = handler->req_cb(handler->ctx, response, sa, query_req, + query_req_len, &comeback_delay); wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler", resp); - gas_server_send_resp(gas, handler, sa, freq, dialog_token, - resp); + if (comeback_delay) + wpa_printf(MSG_DEBUG, + "GAS: Handler requested comeback delay: %u TU", + comeback_delay); + gas_server_send_resp(gas, handler, response, sa, freq, + dialog_token, resp, comeback_delay); return 0; } wpa_printf(MSG_DEBUG, "GAS: No registered handler for the requested Advertisement Protocol ID"); + gas_server_free_response(response); return -1; } @@ -243,6 +259,31 @@ gas_server_handle_rx_comeback_req(struct gas_server_response *response) size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2; size_t remaining, resp_frag_len; struct wpabuf *resp; + unsigned int wait_time = 0; + + if (!response->resp) { + resp = gas_build_comeback_resp(response->dialog_token, + WLAN_STATUS_SUCCESS, 0, 0, + response->comeback_delay, + handler->adv_proto_id_len); + if (!resp) { + dl_list_del(&response->list); + gas_server_free_response(response); + return; + } + + /* Advertisement Protocol element */ + wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */ + wpabuf_put_u8(resp, 0x7f); + /* Advertisement Protocol ID */ + wpabuf_put_data(resp, handler->adv_proto_id, + handler->adv_proto_id_len); + + /* Query Response Length */ + wpabuf_put_le16(resp, 0); + goto send_resp; + } remaining = wpabuf_len(response->resp) - response->offset; if (hdr_len + remaining > max_len) @@ -279,8 +320,11 @@ gas_server_handle_rx_comeback_req(struct gas_server_response *response) response->offset += resp_frag_len; - gas->tx(gas->ctx, response->freq, response->dst, resp, - remaining > resp_frag_len ? 2000 : 0); + if (remaining > resp_frag_len) + wait_time = 2000; + +send_resp: + gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time); wpabuf_free(resp); } @@ -359,12 +403,19 @@ int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa, static void gas_server_handle_tx_status(struct gas_server_response *response, int ack) { - if (ack && response->offset < wpabuf_len(response->resp)) { + if (ack && response->resp && + response->offset < wpabuf_len(response->resp)) { wpa_printf(MSG_DEBUG, "GAS: More fragments remaining - keep pending entry"); return; } + if (ack && !response->resp && response->comeback_delay) { + wpa_printf(MSG_DEBUG, + "GAS: Waiting for response - keep pending entry"); + return; + } + if (!ack) wpa_printf(MSG_DEBUG, "GAS: No ACK received - drop pending entry"); @@ -415,6 +466,27 @@ void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data, } +int gas_server_set_resp(struct gas_server *gas, void *resp_ctx, + struct wpabuf *resp) +{ + struct gas_server_response *tmp, *response = NULL; + + dl_list_for_each(tmp, &gas->responses, struct gas_server_response, + list) { + if (tmp == resp_ctx) { + response = tmp; + break; + } + } + + if (!response || response->resp) + return -1; + + response->resp = resp; + return 0; +} + + struct gas_server * gas_server_init(void *ctx, void (*tx)(void *ctx, int freq, const u8 *da, @@ -461,8 +533,9 @@ void gas_server_deinit(struct gas_server *gas) int 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), + (*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) diff --git a/src/common/gas_server.h b/src/common/gas_server.h index 299f529f7..2611ddedc 100644 --- a/src/common/gas_server.h +++ b/src/common/gas_server.h @@ -1,6 +1,7 @@ /* * Generic advertisement service (GAS) server * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2020, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,8 +23,9 @@ void gas_server_deinit(struct gas_server *gas); int 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), + (*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); @@ -32,6 +34,8 @@ int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa, int freq); void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data, size_t data_len, int ack); +int gas_server_set_resp(struct gas_server *gas, void *resp_ctx, + struct wpabuf *resp); #else /* CONFIG_GAS_SERVER */ diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index a41b3f5c0..66aad2916 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -2647,8 +2647,8 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, static struct wpabuf * -wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query, - size_t query_len) +wpas_dpp_gas_req_handler(void *ctx, void *resp_ctx, const u8 *sa, + const u8 *query, size_t query_len, u16 *comeback_delay) { struct wpa_supplicant *wpa_s = ctx; struct dpp_authentication *auth = wpa_s->dpp_auth;