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 <jouni@codeaurora.org>
This commit is contained in:
parent
608adae5ba
commit
68ac45d53c
3 changed files with 106 additions and 29 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Generic advertisement service (GAS) server
|
* Generic advertisement service (GAS) server
|
||||||
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||||
|
* Copyright (c) 2020, The Linux Foundation
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -23,8 +24,9 @@ struct gas_server_handler {
|
||||||
struct dl_list list;
|
struct dl_list list;
|
||||||
u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
|
u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
|
||||||
u8 adv_proto_id_len;
|
u8 adv_proto_id_len;
|
||||||
struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
|
struct wpabuf * (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
|
||||||
const u8 *query, size_t query_len);
|
const u8 *query, size_t query_len,
|
||||||
|
u16 *comeback_delay);
|
||||||
void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
|
void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
|
||||||
void *ctx;
|
void *ctx;
|
||||||
struct gas_server *gas;
|
struct gas_server *gas;
|
||||||
|
@ -39,6 +41,7 @@ struct gas_server_response {
|
||||||
u8 dst[ETH_ALEN];
|
u8 dst[ETH_ALEN];
|
||||||
u8 dialog_token;
|
u8 dialog_token;
|
||||||
struct gas_server_handler *handler;
|
struct gas_server_handler *handler;
|
||||||
|
u16 comeback_delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gas_server {
|
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, MAC2STR(response->dst), response->dialog_token,
|
||||||
response->freq, response->frag_id,
|
response->freq, response->frag_id,
|
||||||
(unsigned long) response->offset,
|
(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->handler->status_cb(response->handler->ctx,
|
||||||
response->resp, 0);
|
response->resp, 0);
|
||||||
response->resp = NULL;
|
response->resp = NULL;
|
||||||
|
@ -83,30 +87,27 @@ static void gas_server_free_response(struct gas_server_response *response)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
|
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,
|
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 max_len = (freq > 56160) ? 928 : 1400;
|
||||||
size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
|
size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
|
||||||
size_t resp_frag_len;
|
size_t resp_frag_len;
|
||||||
struct wpabuf *resp;
|
struct wpabuf *resp;
|
||||||
u16 comeback_delay;
|
|
||||||
struct gas_server_response *response;
|
|
||||||
|
|
||||||
if (!query_resp)
|
if (comeback_delay == 0 && !query_resp)
|
||||||
return;
|
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->freq = freq;
|
||||||
response->handler = handler;
|
response->handler = handler;
|
||||||
os_memcpy(response->dst, da, ETH_ALEN);
|
os_memcpy(response->dst, da, ETH_ALEN);
|
||||||
response->dialog_token = dialog_token;
|
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 */
|
/* Need to use comeback to initiate fragmentation */
|
||||||
comeback_delay = 1;
|
comeback_delay = 1;
|
||||||
resp_frag_len = 0;
|
resp_frag_len = 0;
|
||||||
|
@ -135,10 +136,12 @@ gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
|
||||||
|
|
||||||
/* Query Response Length */
|
/* Query Response Length */
|
||||||
wpabuf_put_le16(resp, resp_frag_len);
|
wpabuf_put_le16(resp, resp_frag_len);
|
||||||
if (!comeback_delay)
|
if (!comeback_delay && query_resp)
|
||||||
wpabuf_put_buf(resp, 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,
|
wpa_printf(MSG_DEBUG,
|
||||||
"GAS: Need to fragment query response");
|
"GAS: Need to fragment query response");
|
||||||
} else {
|
} else {
|
||||||
|
@ -165,6 +168,7 @@ gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
|
||||||
u16 query_req_len;
|
u16 query_req_len;
|
||||||
struct gas_server_handler *handler;
|
struct gas_server_handler *handler;
|
||||||
struct wpabuf *resp;
|
struct wpabuf *resp;
|
||||||
|
struct gas_server_response *response;
|
||||||
|
|
||||||
wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
|
wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
|
||||||
data, len);
|
data, len);
|
||||||
|
@ -210,8 +214,15 @@ gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
|
||||||
pos, end - pos);
|
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,
|
dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
|
||||||
list) {
|
list) {
|
||||||
|
u16 comeback_delay = 0;
|
||||||
|
|
||||||
if (adv_proto_len < 1 + handler->adv_proto_id_len ||
|
if (adv_proto_len < 1 + handler->adv_proto_id_len ||
|
||||||
os_memcmp(adv_proto + 1, handler->adv_proto_id,
|
os_memcmp(adv_proto + 1, handler->adv_proto_id,
|
||||||
handler->adv_proto_id_len) != 0)
|
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,
|
wpa_printf(MSG_DEBUG,
|
||||||
"GAS: Calling handler for the requested Advertisement Protocol ID");
|
"GAS: Calling handler for the requested Advertisement Protocol ID");
|
||||||
resp = handler->req_cb(handler->ctx, sa, query_req,
|
resp = handler->req_cb(handler->ctx, response, sa, query_req,
|
||||||
query_req_len);
|
query_req_len, &comeback_delay);
|
||||||
wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
|
wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
|
||||||
resp);
|
resp);
|
||||||
gas_server_send_resp(gas, handler, sa, freq, dialog_token,
|
if (comeback_delay)
|
||||||
resp);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG,
|
wpa_printf(MSG_DEBUG,
|
||||||
"GAS: No registered handler for the requested Advertisement Protocol ID");
|
"GAS: No registered handler for the requested Advertisement Protocol ID");
|
||||||
|
gas_server_free_response(response);
|
||||||
return -1;
|
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 hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
|
||||||
size_t remaining, resp_frag_len;
|
size_t remaining, resp_frag_len;
|
||||||
struct wpabuf *resp;
|
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;
|
remaining = wpabuf_len(response->resp) - response->offset;
|
||||||
if (hdr_len + remaining > max_len)
|
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;
|
response->offset += resp_frag_len;
|
||||||
|
|
||||||
gas->tx(gas->ctx, response->freq, response->dst, resp,
|
if (remaining > resp_frag_len)
|
||||||
remaining > resp_frag_len ? 2000 : 0);
|
wait_time = 2000;
|
||||||
|
|
||||||
|
send_resp:
|
||||||
|
gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
|
||||||
wpabuf_free(resp);
|
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,
|
static void gas_server_handle_tx_status(struct gas_server_response *response,
|
||||||
int ack)
|
int ack)
|
||||||
{
|
{
|
||||||
if (ack && response->offset < wpabuf_len(response->resp)) {
|
if (ack && response->resp &&
|
||||||
|
response->offset < wpabuf_len(response->resp)) {
|
||||||
wpa_printf(MSG_DEBUG,
|
wpa_printf(MSG_DEBUG,
|
||||||
"GAS: More fragments remaining - keep pending entry");
|
"GAS: More fragments remaining - keep pending entry");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ack && !response->resp && response->comeback_delay) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"GAS: Waiting for response - keep pending entry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ack)
|
if (!ack)
|
||||||
wpa_printf(MSG_DEBUG,
|
wpa_printf(MSG_DEBUG,
|
||||||
"GAS: No ACK received - drop pending entry");
|
"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,
|
struct gas_server * gas_server_init(void *ctx,
|
||||||
void (*tx)(void *ctx, int freq,
|
void (*tx)(void *ctx, int freq,
|
||||||
const u8 *da,
|
const u8 *da,
|
||||||
|
@ -461,8 +533,9 @@ void gas_server_deinit(struct gas_server *gas)
|
||||||
int gas_server_register(struct gas_server *gas,
|
int gas_server_register(struct gas_server *gas,
|
||||||
const u8 *adv_proto_id, u8 adv_proto_id_len,
|
const u8 *adv_proto_id, u8 adv_proto_id_len,
|
||||||
struct wpabuf *
|
struct wpabuf *
|
||||||
(*req_cb)(void *ctx, const u8 *sa,
|
(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
|
||||||
const u8 *query, size_t query_len),
|
const u8 *query, size_t query_len,
|
||||||
|
u16 *comeback_delay),
|
||||||
void (*status_cb)(void *ctx, struct wpabuf *resp,
|
void (*status_cb)(void *ctx, struct wpabuf *resp,
|
||||||
int ok),
|
int ok),
|
||||||
void *ctx)
|
void *ctx)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Generic advertisement service (GAS) server
|
* Generic advertisement service (GAS) server
|
||||||
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||||
|
* Copyright (c) 2020, The Linux Foundation
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* 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,
|
int gas_server_register(struct gas_server *gas,
|
||||||
const u8 *adv_proto_id, u8 adv_proto_id_len,
|
const u8 *adv_proto_id, u8 adv_proto_id_len,
|
||||||
struct wpabuf *
|
struct wpabuf *
|
||||||
(*req_cb)(void *ctx, const u8 *sa,
|
(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
|
||||||
const u8 *query, size_t query_len),
|
const u8 *query, size_t query_len,
|
||||||
|
u16 *comeback_delay),
|
||||||
void (*status_cb)(void *ctx, struct wpabuf *resp,
|
void (*status_cb)(void *ctx, struct wpabuf *resp,
|
||||||
int ok),
|
int ok),
|
||||||
void *ctx);
|
void *ctx);
|
||||||
|
@ -32,6 +34,8 @@ int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
|
||||||
int freq);
|
int freq);
|
||||||
void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
|
void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
|
||||||
size_t data_len, int ack);
|
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 */
|
#else /* CONFIG_GAS_SERVER */
|
||||||
|
|
||||||
|
|
|
@ -2647,8 +2647,8 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||||
|
|
||||||
|
|
||||||
static struct wpabuf *
|
static struct wpabuf *
|
||||||
wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query,
|
wpas_dpp_gas_req_handler(void *ctx, void *resp_ctx, const u8 *sa,
|
||||||
size_t query_len)
|
const u8 *query, size_t query_len, u16 *comeback_delay)
|
||||||
{
|
{
|
||||||
struct wpa_supplicant *wpa_s = ctx;
|
struct wpa_supplicant *wpa_s = ctx;
|
||||||
struct dpp_authentication *auth = wpa_s->dpp_auth;
|
struct dpp_authentication *auth = wpa_s->dpp_auth;
|
||||||
|
|
Loading…
Reference in a new issue