LCOV - code coverage report
Current view: top level - wpa_supplicant - gas_query.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1401264779 Lines: 274 325 84.3 %
Date: 2014-05-28 Functions: 21 22 95.5 %

          Line data    Source code
       1             : /*
       2             :  * Generic advertisement service (GAS) query
       3             :  * Copyright (c) 2009, Atheros Communications
       4             :  * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
       5             :  * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
       6             :  *
       7             :  * This software may be distributed under the terms of the BSD license.
       8             :  * See README for more details.
       9             :  */
      10             : 
      11             : #include "includes.h"
      12             : 
      13             : #include "common.h"
      14             : #include "utils/eloop.h"
      15             : #include "common/ieee802_11_defs.h"
      16             : #include "common/gas.h"
      17             : #include "common/wpa_ctrl.h"
      18             : #include "rsn_supp/wpa.h"
      19             : #include "wpa_supplicant_i.h"
      20             : #include "driver_i.h"
      21             : #include "offchannel.h"
      22             : #include "gas_query.h"
      23             : 
      24             : 
      25             : /** GAS query timeout in seconds */
      26             : #define GAS_QUERY_TIMEOUT_PERIOD 2
      27             : 
      28             : 
      29             : /**
      30             :  * struct gas_query_pending - Pending GAS query
      31             :  */
      32             : struct gas_query_pending {
      33             :         struct dl_list list;
      34             :         struct gas_query *gas;
      35             :         u8 addr[ETH_ALEN];
      36             :         u8 dialog_token;
      37             :         u8 next_frag_id;
      38             :         unsigned int wait_comeback:1;
      39             :         unsigned int offchannel_tx_started:1;
      40             :         int freq;
      41             :         u16 status_code;
      42             :         struct wpabuf *req;
      43             :         struct wpabuf *adv_proto;
      44             :         struct wpabuf *resp;
      45             :         struct os_reltime last_oper;
      46             :         void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
      47             :                    enum gas_query_result result,
      48             :                    const struct wpabuf *adv_proto,
      49             :                    const struct wpabuf *resp, u16 status_code);
      50             :         void *ctx;
      51             : };
      52             : 
      53             : /**
      54             :  * struct gas_query - Internal GAS query data
      55             :  */
      56             : struct gas_query {
      57             :         struct wpa_supplicant *wpa_s;
      58             :         struct dl_list pending; /* struct gas_query_pending */
      59             :         struct gas_query_pending *current;
      60             :         struct wpa_radio_work *work;
      61             : };
      62             : 
      63             : 
      64             : static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
      65             : static void gas_query_timeout(void *eloop_data, void *user_ctx);
      66             : 
      67             : 
      68         739 : static int ms_from_time(struct os_reltime *last)
      69             : {
      70             :         struct os_reltime now, res;
      71             : 
      72         739 :         os_get_reltime(&now);
      73         739 :         os_reltime_sub(&now, last, &res);
      74         739 :         return res.sec * 1000 + res.usec / 1000;
      75             : }
      76             : 
      77             : 
      78             : /**
      79             :  * gas_query_init - Initialize GAS query component
      80             :  * @wpa_s: Pointer to wpa_supplicant data
      81             :  * Returns: Pointer to GAS query data or %NULL on failure
      82             :  */
      83          69 : struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
      84             : {
      85             :         struct gas_query *gas;
      86             : 
      87          69 :         gas = os_zalloc(sizeof(*gas));
      88          69 :         if (gas == NULL)
      89           0 :                 return NULL;
      90             : 
      91          69 :         gas->wpa_s = wpa_s;
      92          69 :         dl_list_init(&gas->pending);
      93             : 
      94          69 :         return gas;
      95             : }
      96             : 
      97             : 
      98         187 : static const char * gas_result_txt(enum gas_query_result result)
      99             : {
     100         187 :         switch (result) {
     101             :         case GAS_QUERY_SUCCESS:
     102         175 :                 return "SUCCESS";
     103             :         case GAS_QUERY_FAILURE:
     104           3 :                 return "FAILURE";
     105             :         case GAS_QUERY_TIMEOUT:
     106           5 :                 return "TIMEOUT";
     107             :         case GAS_QUERY_PEER_ERROR:
     108           4 :                 return "PEER_ERROR";
     109             :         case GAS_QUERY_INTERNAL_ERROR:
     110           0 :                 return "INTERNAL_ERROR";
     111             :         case GAS_QUERY_CANCELLED:
     112           0 :                 return "CANCELLED";
     113             :         case GAS_QUERY_DELETED_AT_DEINIT:
     114           0 :                 return "DELETED_AT_DEINIT";
     115             :         }
     116             : 
     117           0 :         return "N/A";
     118             : }
     119             : 
     120             : 
     121         187 : static void gas_query_free(struct gas_query_pending *query, int del_list)
     122             : {
     123         187 :         struct gas_query *gas = query->gas;
     124             : 
     125         187 :         if (del_list)
     126           0 :                 dl_list_del(&query->list);
     127             : 
     128         187 :         if (gas->work && gas->work->ctx == query) {
     129         187 :                 radio_work_done(gas->work);
     130         187 :                 gas->work = NULL;
     131             :         }
     132             : 
     133         187 :         wpabuf_free(query->req);
     134         187 :         wpabuf_free(query->adv_proto);
     135         187 :         wpabuf_free(query->resp);
     136         187 :         os_free(query);
     137         187 : }
     138             : 
     139             : 
     140         187 : static void gas_query_done(struct gas_query *gas,
     141             :                            struct gas_query_pending *query,
     142             :                            enum gas_query_result result)
     143             : {
     144        1683 :         wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
     145             :                 " dialog_token=%u freq=%d status_code=%u result=%s",
     146        1309 :                 MAC2STR(query->addr), query->dialog_token, query->freq,
     147         187 :                 query->status_code, gas_result_txt(result));
     148         187 :         if (gas->current == query)
     149         187 :                 gas->current = NULL;
     150         187 :         if (query->offchannel_tx_started)
     151         187 :                 offchannel_send_action_done(gas->wpa_s);
     152         187 :         eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
     153         187 :         eloop_cancel_timeout(gas_query_timeout, gas, query);
     154         187 :         dl_list_del(&query->list);
     155         561 :         query->cb(query->ctx, query->addr, query->dialog_token, result,
     156         374 :                   query->adv_proto, query->resp, query->status_code);
     157         187 :         gas_query_free(query, 0);
     158         187 : }
     159             : 
     160             : 
     161             : /**
     162             :  * gas_query_deinit - Deinitialize GAS query component
     163             :  * @gas: GAS query data from gas_query_init()
     164             :  */
     165          78 : void gas_query_deinit(struct gas_query *gas)
     166             : {
     167             :         struct gas_query_pending *query, *next;
     168             : 
     169          78 :         if (gas == NULL)
     170          87 :                 return;
     171             : 
     172          69 :         dl_list_for_each_safe(query, next, &gas->pending,
     173             :                               struct gas_query_pending, list)
     174           0 :                 gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
     175             : 
     176          69 :         os_free(gas);
     177             : }
     178             : 
     179             : 
     180             : static struct gas_query_pending *
     181         412 : gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
     182             : {
     183             :         struct gas_query_pending *q;
     184         415 :         dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
     185         758 :                 if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
     186         379 :                     q->dialog_token == dialog_token)
     187         376 :                         return q;
     188             :         }
     189          36 :         return NULL;
     190             : }
     191             : 
     192             : 
     193         333 : static int gas_query_append(struct gas_query_pending *query, const u8 *data,
     194             :                             size_t len)
     195             : {
     196         333 :         if (wpabuf_resize(&query->resp, len) < 0) {
     197           0 :                 wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
     198           0 :                 return -1;
     199             :         }
     200         333 :         wpabuf_put_data(query->resp, data, len);
     201         333 :         return 0;
     202             : }
     203             : 
     204             : 
     205         363 : static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
     206             :                                 unsigned int freq, const u8 *dst,
     207             :                                 const u8 *src, const u8 *bssid,
     208             :                                 const u8 *data, size_t data_len,
     209             :                                 enum offchannel_send_action_result result)
     210             : {
     211             :         struct gas_query_pending *query;
     212         363 :         struct gas_query *gas = wpa_s->gas;
     213             :         int dur;
     214             : 
     215         363 :         if (gas->current == NULL) {
     216           0 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
     217             :                            MACSTR " result=%d - no query in progress",
     218           0 :                            freq, MAC2STR(dst), result);
     219           0 :                 return;
     220             :         }
     221             : 
     222         363 :         query = gas->current;
     223             : 
     224         363 :         dur = ms_from_time(&query->last_oper);
     225        2541 :         wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
     226             :                    " result=%d query=%p dialog_token=%u dur=%d ms",
     227        2541 :                    freq, MAC2STR(dst), result, query, query->dialog_token, dur);
     228         363 :         if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
     229           0 :                 wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
     230           0 :                 return;
     231             :         }
     232         363 :         os_get_reltime(&query->last_oper);
     233             : 
     234         363 :         if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
     235         363 :                 eloop_cancel_timeout(gas_query_timeout, gas, query);
     236         363 :                 eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
     237             :                                        gas_query_timeout, gas, query);
     238             :         }
     239         363 :         if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
     240           0 :                 eloop_cancel_timeout(gas_query_timeout, gas, query);
     241           0 :                 eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
     242             :         }
     243             : }
     244             : 
     245             : 
     246        1220 : static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
     247             : {
     248        1287 :         if (wpa_s->current_ssid == NULL ||
     249         134 :             wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
     250          67 :             os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
     251        1179 :                 return 0;
     252          41 :         return wpa_sm_pmf_enabled(wpa_s->wpa);
     253             : }
     254             : 
     255             : 
     256         364 : static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
     257             :                         struct wpabuf *req)
     258             : {
     259         364 :         int res, prot = pmf_in_use(gas->wpa_s, query->addr);
     260             : 
     261        2912 :         wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
     262        2184 :                    "freq=%d prot=%d", MAC2STR(query->addr),
     263         364 :                    (unsigned int) wpabuf_len(req), query->freq, prot);
     264         364 :         if (prot) {
     265           1 :                 u8 *categ = wpabuf_mhead_u8(req);
     266           1 :                 *categ = WLAN_ACTION_PROTECTED_DUAL;
     267             :         }
     268         364 :         os_get_reltime(&query->last_oper);
     269         728 :         res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
     270         364 :                                      gas->wpa_s->own_addr, query->addr,
     271         364 :                                      wpabuf_head(req), wpabuf_len(req), 1000,
     272             :                                      gas_query_tx_status, 0);
     273         364 :         if (res == 0)
     274         364 :                 query->offchannel_tx_started = 1;
     275         364 :         return res;
     276             : }
     277             : 
     278             : 
     279         177 : static void gas_query_tx_comeback_req(struct gas_query *gas,
     280             :                                       struct gas_query_pending *query)
     281             : {
     282             :         struct wpabuf *req;
     283             : 
     284         177 :         req = gas_build_comeback_req(query->dialog_token);
     285         177 :         if (req == NULL) {
     286           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     287         177 :                 return;
     288             :         }
     289             : 
     290         177 :         if (gas_query_tx(gas, query, req) < 0) {
     291           0 :                 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
     292           0 :                            MACSTR, MAC2STR(query->addr));
     293           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     294             :         }
     295             : 
     296         177 :         wpabuf_free(req);
     297             : }
     298             : 
     299             : 
     300          19 : static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
     301             : {
     302          19 :         struct gas_query *gas = eloop_data;
     303          19 :         struct gas_query_pending *query = user_ctx;
     304             : 
     305         114 :         wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
     306         114 :                    MAC2STR(query->addr));
     307          19 :         gas_query_tx_comeback_req(gas, query);
     308          19 : }
     309             : 
     310             : 
     311          19 : static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
     312             :                                             struct gas_query_pending *query,
     313             :                                             u16 comeback_delay)
     314             : {
     315             :         unsigned int secs, usecs;
     316             : 
     317          19 :         secs = (comeback_delay * 1024) / 1000000;
     318          19 :         usecs = comeback_delay * 1024 - secs * 1000000;
     319         114 :         wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
     320         114 :                    " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
     321          19 :         eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
     322          19 :         eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
     323             :                                gas, query);
     324          19 : }
     325             : 
     326             : 
     327         183 : static void gas_query_rx_initial(struct gas_query *gas,
     328             :                                  struct gas_query_pending *query,
     329             :                                  const u8 *adv_proto, const u8 *resp,
     330             :                                  size_t len, u16 comeback_delay)
     331             : {
     332        1464 :         wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
     333             :                    MACSTR " (dialog_token=%u comeback_delay=%u)",
     334        1281 :                    MAC2STR(query->addr), query->dialog_token, comeback_delay);
     335             : 
     336         183 :         query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
     337         183 :         if (query->adv_proto == NULL) {
     338           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     339           0 :                 return;
     340             :         }
     341             : 
     342         183 :         if (comeback_delay) {
     343          17 :                 query->wait_comeback = 1;
     344          17 :                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
     345          17 :                 return;
     346             :         }
     347             : 
     348             :         /* Query was completed without comeback mechanism */
     349         166 :         if (gas_query_append(query, resp, len) < 0) {
     350           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     351           0 :                 return;
     352             :         }
     353             : 
     354         166 :         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
     355             : }
     356             : 
     357             : 
     358         174 : static void gas_query_rx_comeback(struct gas_query *gas,
     359             :                                   struct gas_query_pending *query,
     360             :                                   const u8 *adv_proto, const u8 *resp,
     361             :                                   size_t len, u8 frag_id, u8 more_frags,
     362             :                                   u16 comeback_delay)
     363             : {
     364        1392 :         wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
     365             :                    MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
     366             :                    "comeback_delay=%u)",
     367        1218 :                    MAC2STR(query->addr), query->dialog_token, frag_id,
     368             :                    more_frags, comeback_delay);
     369             : 
     370         348 :         if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
     371         174 :             os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
     372             :                       wpabuf_len(query->adv_proto)) != 0) {
     373           6 :                 wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
     374             :                            "between initial and comeback response from "
     375           6 :                            MACSTR, MAC2STR(query->addr));
     376           1 :                 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
     377           1 :                 return;
     378             :         }
     379             : 
     380         173 :         if (comeback_delay) {
     381           3 :                 if (frag_id) {
     382           6 :                         wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
     383             :                                    "with non-zero frag_id and comeback_delay "
     384           6 :                                    "from " MACSTR, MAC2STR(query->addr));
     385           1 :                         gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
     386           1 :                         return;
     387             :                 }
     388           2 :                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
     389           2 :                 return;
     390             :         }
     391             : 
     392         170 :         if (frag_id != query->next_frag_id) {
     393          18 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
     394          18 :                            "from " MACSTR, MAC2STR(query->addr));
     395           3 :                 if (frag_id + 1 == query->next_frag_id) {
     396           1 :                         wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
     397             :                                    "retry of previous fragment");
     398           1 :                         return;
     399             :                 }
     400           2 :                 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
     401           2 :                 return;
     402             :         }
     403         167 :         query->next_frag_id++;
     404             : 
     405         167 :         if (gas_query_append(query, resp, len) < 0) {
     406           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     407           0 :                 return;
     408             :         }
     409             : 
     410         167 :         if (more_frags) {
     411         158 :                 gas_query_tx_comeback_req(gas, query);
     412         158 :                 return;
     413             :         }
     414             : 
     415           9 :         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
     416             : }
     417             : 
     418             : 
     419             : /**
     420             :  * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
     421             :  * @gas: GAS query data from gas_query_init()
     422             :  * @da: Destination MAC address of the Action frame
     423             :  * @sa: Source MAC address of the Action frame
     424             :  * @bssid: BSSID of the Action frame
     425             :  * @categ: Category of the Action frame
     426             :  * @data: Payload of the Action frame
     427             :  * @len: Length of @data
     428             :  * @freq: Frequency (in MHz) on which the frame was received
     429             :  * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
     430             :  */
     431         869 : int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
     432             :                  const u8 *bssid, u8 categ, const u8 *data, size_t len,
     433             :                  int freq)
     434             : {
     435             :         struct gas_query_pending *query;
     436         869 :         u8 action, dialog_token, frag_id = 0, more_frags = 0;
     437             :         u16 comeback_delay, resp_len;
     438             :         const u8 *pos, *adv_proto;
     439             :         int prot, pmf;
     440             : 
     441         869 :         if (gas == NULL || len < 4)
     442          13 :                 return -1;
     443             : 
     444         856 :         prot = categ == WLAN_ACTION_PROTECTED_DUAL;
     445         856 :         pmf = pmf_in_use(gas->wpa_s, bssid);
     446         856 :         if (prot && !pmf) {
     447           0 :                 wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
     448           0 :                 return 0;
     449             :         }
     450         856 :         if (!prot && pmf) {
     451           0 :                 wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
     452           0 :                 return 0;
     453             :         }
     454             : 
     455         856 :         pos = data;
     456         856 :         action = *pos++;
     457         856 :         dialog_token = *pos++;
     458             : 
     459         856 :         if (action != WLAN_PA_GAS_INITIAL_RESP &&
     460             :             action != WLAN_PA_GAS_COMEBACK_RESP)
     461         444 :                 return -1; /* Not a GAS response */
     462             : 
     463         412 :         query = gas_query_get_pending(gas, sa, dialog_token);
     464         412 :         if (query == NULL) {
     465         252 :                 wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
     466         216 :                            " dialog token %u", MAC2STR(sa), dialog_token);
     467          36 :                 return -1;
     468             :         }
     469             : 
     470        2256 :         wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
     471        2256 :                    ms_from_time(&query->last_oper), MAC2STR(sa));
     472             : 
     473         376 :         if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
     474           7 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
     475             :                            MACSTR " dialog token %u when waiting for comeback "
     476           6 :                            "response", MAC2STR(sa), dialog_token);
     477           1 :                 return 0;
     478             :         }
     479             : 
     480         375 :         if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
     481          21 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
     482             :                            MACSTR " dialog token %u when waiting for initial "
     483          18 :                            "response", MAC2STR(sa), dialog_token);
     484           3 :                 return 0;
     485             :         }
     486             : 
     487         372 :         query->status_code = WPA_GET_LE16(pos);
     488         372 :         pos += 2;
     489             : 
     490         372 :         if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
     491             :             action == WLAN_PA_GAS_COMEBACK_RESP) {
     492           2 :                 wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
     493         370 :         } else if (query->status_code != WLAN_STATUS_SUCCESS) {
     494          21 :                 wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
     495             :                            "%u failed - status code %u",
     496          21 :                            MAC2STR(sa), dialog_token, query->status_code);
     497           3 :                 gas_query_done(gas, query, GAS_QUERY_FAILURE);
     498           3 :                 return 0;
     499             :         }
     500             : 
     501         369 :         if (action == WLAN_PA_GAS_COMEBACK_RESP) {
     502         176 :                 if (pos + 1 > data + len)
     503           1 :                         return 0;
     504         175 :                 frag_id = *pos & 0x7f;
     505         175 :                 more_frags = (*pos & 0x80) >> 7;
     506         175 :                 pos++;
     507             :         }
     508             : 
     509             :         /* Comeback Delay */
     510         368 :         if (pos + 2 > data + len)
     511           1 :                 return 0;
     512         367 :         comeback_delay = WPA_GET_LE16(pos);
     513         367 :         pos += 2;
     514             : 
     515             :         /* Advertisement Protocol element */
     516         367 :         if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
     517          24 :                 wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
     518             :                            "Protocol element in the response from " MACSTR,
     519          24 :                            MAC2STR(sa));
     520           4 :                 return 0;
     521             :         }
     522             : 
     523         363 :         if (*pos != WLAN_EID_ADV_PROTO) {
     524           7 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
     525             :                            "Protocol element ID %u in response from " MACSTR,
     526           7 :                            *pos, MAC2STR(sa));
     527           1 :                 return 0;
     528             :         }
     529             : 
     530         362 :         adv_proto = pos;
     531         362 :         pos += 2 + pos[1];
     532             : 
     533             :         /* Query Response Length */
     534         362 :         if (pos + 2 > data + len) {
     535           2 :                 wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
     536           2 :                 return 0;
     537             :         }
     538         360 :         resp_len = WPA_GET_LE16(pos);
     539         360 :         pos += 2;
     540             : 
     541         360 :         if (pos + resp_len > data + len) {
     542          18 :                 wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
     543          18 :                            "response from " MACSTR, MAC2STR(sa));
     544           3 :                 return 0;
     545             :         }
     546             : 
     547         357 :         if (pos + resp_len < data + len) {
     548           7 :                 wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
     549             :                            "after Query Response from " MACSTR,
     550           1 :                            (unsigned int) (data + len - pos - resp_len),
     551           6 :                            MAC2STR(sa));
     552             :         }
     553             : 
     554         357 :         if (action == WLAN_PA_GAS_COMEBACK_RESP)
     555         174 :                 gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
     556             :                                       frag_id, more_frags, comeback_delay);
     557             :         else
     558         183 :                 gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
     559             :                                      comeback_delay);
     560             : 
     561         357 :         return 0;
     562             : }
     563             : 
     564             : 
     565           5 : static void gas_query_timeout(void *eloop_data, void *user_ctx)
     566             : {
     567           5 :         struct gas_query *gas = eloop_data;
     568           5 :         struct gas_query_pending *query = user_ctx;
     569             : 
     570          35 :         wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
     571             :                    " dialog token %u",
     572          35 :                    MAC2STR(query->addr), query->dialog_token);
     573           5 :         gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
     574           5 : }
     575             : 
     576             : 
     577         187 : static int gas_query_dialog_token_available(struct gas_query *gas,
     578             :                                             const u8 *dst, u8 dialog_token)
     579             : {
     580             :         struct gas_query_pending *q;
     581         190 :         dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
     582           6 :                 if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
     583           3 :                     dialog_token == q->dialog_token)
     584           0 :                         return 0;
     585             :         }
     586             : 
     587         187 :         return 1;
     588             : }
     589             : 
     590             : 
     591         187 : static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
     592             : {
     593         187 :         struct gas_query_pending *query = work->ctx;
     594         187 :         struct gas_query *gas = query->gas;
     595             : 
     596         187 :         if (deinit) {
     597           0 :                 if (work->started) {
     598           0 :                         gas->work = NULL;
     599           0 :                         gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
     600           0 :                         return;
     601             :                 }
     602             : 
     603           0 :                 gas_query_free(query, 1);
     604           0 :                 return;
     605             :         }
     606             : 
     607         187 :         gas->work = work;
     608             : 
     609         187 :         if (gas_query_tx(gas, query, query->req) < 0) {
     610           0 :                 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
     611           0 :                            MACSTR, MAC2STR(query->addr));
     612           0 :                 gas_query_free(query, 1);
     613           0 :                 return;
     614             :         }
     615         187 :         gas->current = query;
     616             : 
     617         187 :         wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
     618         187 :                    query->dialog_token);
     619         187 :         eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
     620             :                                gas_query_timeout, gas, query);
     621             : 
     622             : }
     623             : 
     624             : 
     625             : /**
     626             :  * gas_query_req - Request a GAS query
     627             :  * @gas: GAS query data from gas_query_init()
     628             :  * @dst: Destination MAC address for the query
     629             :  * @freq: Frequency (in MHz) for the channel on which to send the query
     630             :  * @req: GAS query payload (to be freed by gas_query module in case of success
     631             :  *      return)
     632             :  * @cb: Callback function for reporting GAS query result and response
     633             :  * @ctx: Context pointer to use with the @cb call
     634             :  * Returns: dialog token (>= 0) on success or -1 on failure
     635             :  */
     636         187 : int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
     637             :                   struct wpabuf *req,
     638             :                   void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
     639             :                              enum gas_query_result result,
     640             :                              const struct wpabuf *adv_proto,
     641             :                              const struct wpabuf *resp, u16 status_code),
     642             :                   void *ctx)
     643             : {
     644             :         struct gas_query_pending *query;
     645             :         int dialog_token;
     646             :         static int next_start = 0;
     647             : 
     648         187 :         if (wpabuf_len(req) < 3)
     649           0 :                 return -1;
     650             : 
     651         187 :         for (dialog_token = 0; dialog_token < 256; dialog_token++) {
     652         187 :                 if (gas_query_dialog_token_available(
     653         187 :                             gas, dst, (next_start + dialog_token) % 256))
     654         187 :                         break;
     655             :         }
     656         187 :         if (dialog_token == 256)
     657           0 :                 return -1; /* Too many pending queries */
     658         187 :         dialog_token = (next_start + dialog_token) % 256;
     659         187 :         next_start = (dialog_token + 1) % 256;
     660             : 
     661         187 :         query = os_zalloc(sizeof(*query));
     662         187 :         if (query == NULL)
     663           0 :                 return -1;
     664             : 
     665         187 :         query->gas = gas;
     666         187 :         os_memcpy(query->addr, dst, ETH_ALEN);
     667         187 :         query->dialog_token = dialog_token;
     668         187 :         query->freq = freq;
     669         187 :         query->cb = cb;
     670         187 :         query->ctx = ctx;
     671         187 :         query->req = req;
     672         187 :         dl_list_add(&gas->pending, &query->list);
     673             : 
     674         187 :         *(wpabuf_mhead_u8(req) + 2) = dialog_token;
     675             : 
     676        1496 :         wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
     677             :                 " dialog_token=%u freq=%d",
     678        1309 :                 MAC2STR(query->addr), query->dialog_token, query->freq);
     679             : 
     680         187 :         if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
     681             :                            query) < 0) {
     682           0 :                 gas_query_free(query, 1);
     683           0 :                 return -1;
     684             :         }
     685             : 
     686         187 :         return dialog_token;
     687             : }
     688             : 
     689             : 
     690             : /**
     691             :  * gas_query_cancel - Cancel a pending GAS query
     692             :  * @gas: GAS query data from gas_query_init()
     693             :  * @dst: Destination MAC address for the query
     694             :  * @dialog_token: Dialog token from gas_query_req()
     695             :  */
     696           0 : void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
     697             : {
     698             :         struct gas_query_pending *query;
     699             : 
     700           0 :         query = gas_query_get_pending(gas, dst, dialog_token);
     701           0 :         if (query)
     702           0 :                 gas_query_done(gas, query, GAS_QUERY_CANCELLED);
     703             : 
     704           0 : }

Generated by: LCOV version 1.10