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 1475438200 Lines: 357 373 95.7 %
Date: 2016-10-02 Functions: 24 24 100.0 %

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

Generated by: LCOV version 1.10