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