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 1443382998 Lines: 284 335 84.8 %
Date: 2015-09-27 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         793 : static int ms_from_time(struct os_reltime *last)
      69             : {
      70             :         struct os_reltime now, res;
      71             : 
      72         793 :         os_get_reltime(&now);
      73         793 :         os_reltime_sub(&now, last, &res);
      74         793 :         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         576 : struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
      84             : {
      85             :         struct gas_query *gas;
      86             : 
      87         576 :         gas = os_zalloc(sizeof(*gas));
      88         576 :         if (gas == NULL)
      89           1 :                 return NULL;
      90             : 
      91         575 :         gas->wpa_s = wpa_s;
      92         575 :         dl_list_init(&gas->pending);
      93             : 
      94         575 :         return gas;
      95             : }
      96             : 
      97             : 
      98         215 : static const char * gas_result_txt(enum gas_query_result result)
      99             : {
     100         215 :         switch (result) {
     101             :         case GAS_QUERY_SUCCESS:
     102         198 :                 return "SUCCESS";
     103             :         case GAS_QUERY_FAILURE:
     104           3 :                 return "FAILURE";
     105             :         case GAS_QUERY_TIMEOUT:
     106          10 :                 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         217 : static void gas_query_free(struct gas_query_pending *query, int del_list)
     122             : {
     123         217 :         struct gas_query *gas = query->gas;
     124             : 
     125         217 :         if (del_list)
     126           2 :                 dl_list_del(&query->list);
     127             : 
     128         217 :         if (gas->work && gas->work->ctx == query) {
     129         215 :                 radio_work_done(gas->work);
     130         215 :                 gas->work = NULL;
     131             :         }
     132             : 
     133         217 :         wpabuf_free(query->req);
     134         217 :         wpabuf_free(query->adv_proto);
     135         217 :         wpabuf_free(query->resp);
     136         217 :         os_free(query);
     137         217 : }
     138             : 
     139             : 
     140         215 : static void gas_query_done(struct gas_query *gas,
     141             :                            struct gas_query_pending *query,
     142             :                            enum gas_query_result result)
     143             : {
     144        1935 :         wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
     145             :                 " dialog_token=%u freq=%d status_code=%u result=%s",
     146        1505 :                 MAC2STR(query->addr), query->dialog_token, query->freq,
     147         215 :                 query->status_code, gas_result_txt(result));
     148         215 :         if (gas->current == query)
     149         215 :                 gas->current = NULL;
     150         215 :         if (query->offchannel_tx_started)
     151         215 :                 offchannel_send_action_done(gas->wpa_s);
     152         215 :         eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
     153         215 :         eloop_cancel_timeout(gas_query_timeout, gas, query);
     154         215 :         dl_list_del(&query->list);
     155         645 :         query->cb(query->ctx, query->addr, query->dialog_token, result,
     156         430 :                   query->adv_proto, query->resp, query->status_code);
     157         215 :         gas_query_free(query, 0);
     158         215 : }
     159             : 
     160             : 
     161             : /**
     162             :  * gas_query_deinit - Deinitialize GAS query component
     163             :  * @gas: GAS query data from gas_query_init()
     164             :  */
     165         612 : void gas_query_deinit(struct gas_query *gas)
     166             : {
     167             :         struct gas_query_pending *query, *next;
     168             : 
     169         612 :         if (gas == NULL)
     170         649 :                 return;
     171             : 
     172         575 :         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         575 :         os_free(gas);
     177             : }
     178             : 
     179             : 
     180             : static struct gas_query_pending *
     181         487 : gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
     182             : {
     183             :         struct gas_query_pending *q;
     184         490 :         dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
     185         806 :                 if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
     186         403 :                     q->dialog_token == dialog_token)
     187         400 :                         return q;
     188             :         }
     189          87 :         return NULL;
     190             : }
     191             : 
     192             : 
     193         356 : static int gas_query_append(struct gas_query_pending *query, const u8 *data,
     194             :                             size_t len)
     195             : {
     196         356 :         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         356 :         wpabuf_put_data(query->resp, data, len);
     201         356 :         return 0;
     202             : }
     203             : 
     204             : 
     205         393 : 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         393 :         struct gas_query *gas = wpa_s->gas;
     213             :         int dur;
     214             : 
     215         393 :         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         393 :         query = gas->current;
     223             : 
     224         393 :         dur = ms_from_time(&query->last_oper);
     225        2751 :         wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
     226             :                    " result=%d query=%p dialog_token=%u dur=%d ms",
     227        2751 :                    freq, MAC2STR(dst), result, query, query->dialog_token, dur);
     228         393 :         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         393 :         os_get_reltime(&query->last_oper);
     233             : 
     234         393 :         if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
     235         393 :                 eloop_cancel_timeout(gas_query_timeout, gas, query);
     236         393 :                 eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
     237             :                                        gas_query_timeout, gas, query);
     238             :         }
     239         393 :         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        1710 : static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
     247             : {
     248        1799 :         if (wpa_s->current_ssid == NULL ||
     249         177 :             wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
     250          88 :             os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
     251        1668 :                 return 0;
     252          42 :         return wpa_sm_pmf_enabled(wpa_s->wpa);
     253             : }
     254             : 
     255             : 
     256         393 : static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
     257             :                         struct wpabuf *req)
     258             : {
     259             :         unsigned int wait_time;
     260         393 :         int res, prot = pmf_in_use(gas->wpa_s, query->addr);
     261             : 
     262        3144 :         wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
     263        2358 :                    "freq=%d prot=%d", MAC2STR(query->addr),
     264         393 :                    (unsigned int) wpabuf_len(req), query->freq, prot);
     265         393 :         if (prot) {
     266           1 :                 u8 *categ = wpabuf_mhead_u8(req);
     267           1 :                 *categ = WLAN_ACTION_PROTECTED_DUAL;
     268             :         }
     269         393 :         os_get_reltime(&query->last_oper);
     270         393 :         wait_time = 1000;
     271         786 :         if (gas->wpa_s->max_remain_on_chan &&
     272         393 :             wait_time > gas->wpa_s->max_remain_on_chan)
     273           0 :                 wait_time = gas->wpa_s->max_remain_on_chan;
     274         786 :         res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
     275         393 :                                      gas->wpa_s->own_addr, query->addr,
     276         393 :                                      wpabuf_head(req), wpabuf_len(req),
     277             :                                      wait_time, gas_query_tx_status, 0);
     278         393 :         if (res == 0)
     279         393 :                 query->offchannel_tx_started = 1;
     280         393 :         return res;
     281             : }
     282             : 
     283             : 
     284         178 : static void gas_query_tx_comeback_req(struct gas_query *gas,
     285             :                                       struct gas_query_pending *query)
     286             : {
     287             :         struct wpabuf *req;
     288             : 
     289         178 :         req = gas_build_comeback_req(query->dialog_token);
     290         178 :         if (req == NULL) {
     291           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     292         178 :                 return;
     293             :         }
     294             : 
     295         178 :         if (gas_query_tx(gas, query, req) < 0) {
     296           0 :                 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
     297           0 :                            MACSTR, MAC2STR(query->addr));
     298           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     299             :         }
     300             : 
     301         178 :         wpabuf_free(req);
     302             : }
     303             : 
     304             : 
     305          20 : static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
     306             : {
     307          20 :         struct gas_query *gas = eloop_data;
     308          20 :         struct gas_query_pending *query = user_ctx;
     309             : 
     310         120 :         wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
     311         120 :                    MAC2STR(query->addr));
     312          20 :         gas_query_tx_comeback_req(gas, query);
     313          20 : }
     314             : 
     315             : 
     316          20 : static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
     317             :                                             struct gas_query_pending *query,
     318             :                                             u16 comeback_delay)
     319             : {
     320             :         unsigned int secs, usecs;
     321             : 
     322          20 :         secs = (comeback_delay * 1024) / 1000000;
     323          20 :         usecs = comeback_delay * 1024 - secs * 1000000;
     324         120 :         wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
     325         120 :                    " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
     326          20 :         eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
     327          20 :         eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
     328             :                                gas, query);
     329          20 : }
     330             : 
     331             : 
     332         207 : static void gas_query_rx_initial(struct gas_query *gas,
     333             :                                  struct gas_query_pending *query,
     334             :                                  const u8 *adv_proto, const u8 *resp,
     335             :                                  size_t len, u16 comeback_delay)
     336             : {
     337        1656 :         wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
     338             :                    MACSTR " (dialog_token=%u comeback_delay=%u)",
     339        1449 :                    MAC2STR(query->addr), query->dialog_token, comeback_delay);
     340             : 
     341         207 :         query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
     342         207 :         if (query->adv_proto == NULL) {
     343           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     344           0 :                 return;
     345             :         }
     346             : 
     347         207 :         if (comeback_delay) {
     348          18 :                 query->wait_comeback = 1;
     349          18 :                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
     350          18 :                 return;
     351             :         }
     352             : 
     353             :         /* Query was completed without comeback mechanism */
     354         189 :         if (gas_query_append(query, resp, len) < 0) {
     355           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     356           0 :                 return;
     357             :         }
     358             : 
     359         189 :         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
     360             : }
     361             : 
     362             : 
     363         174 : static void gas_query_rx_comeback(struct gas_query *gas,
     364             :                                   struct gas_query_pending *query,
     365             :                                   const u8 *adv_proto, const u8 *resp,
     366             :                                   size_t len, u8 frag_id, u8 more_frags,
     367             :                                   u16 comeback_delay)
     368             : {
     369        1392 :         wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
     370             :                    MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
     371             :                    "comeback_delay=%u)",
     372        1218 :                    MAC2STR(query->addr), query->dialog_token, frag_id,
     373             :                    more_frags, comeback_delay);
     374             : 
     375         348 :         if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
     376         174 :             os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
     377             :                       wpabuf_len(query->adv_proto)) != 0) {
     378           6 :                 wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
     379             :                            "between initial and comeback response from "
     380           6 :                            MACSTR, MAC2STR(query->addr));
     381           1 :                 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
     382           1 :                 return;
     383             :         }
     384             : 
     385         173 :         if (comeback_delay) {
     386           3 :                 if (frag_id) {
     387           6 :                         wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
     388             :                                    "with non-zero frag_id and comeback_delay "
     389           6 :                                    "from " MACSTR, MAC2STR(query->addr));
     390           1 :                         gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
     391           1 :                         return;
     392             :                 }
     393           2 :                 gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
     394           2 :                 return;
     395             :         }
     396             : 
     397         170 :         if (frag_id != query->next_frag_id) {
     398          18 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
     399          18 :                            "from " MACSTR, MAC2STR(query->addr));
     400           3 :                 if (frag_id + 1 == query->next_frag_id) {
     401           1 :                         wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
     402             :                                    "retry of previous fragment");
     403           1 :                         return;
     404             :                 }
     405           2 :                 gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
     406           2 :                 return;
     407             :         }
     408         167 :         query->next_frag_id++;
     409             : 
     410         167 :         if (gas_query_append(query, resp, len) < 0) {
     411           0 :                 gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
     412           0 :                 return;
     413             :         }
     414             : 
     415         167 :         if (more_frags) {
     416         158 :                 gas_query_tx_comeback_req(gas, query);
     417         158 :                 return;
     418             :         }
     419             : 
     420           9 :         gas_query_done(gas, query, GAS_QUERY_SUCCESS);
     421             : }
     422             : 
     423             : 
     424             : /**
     425             :  * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
     426             :  * @gas: GAS query data from gas_query_init()
     427             :  * @da: Destination MAC address of the Action frame
     428             :  * @sa: Source MAC address of the Action frame
     429             :  * @bssid: BSSID of the Action frame
     430             :  * @categ: Category of the Action frame
     431             :  * @data: Payload of the Action frame
     432             :  * @len: Length of @data
     433             :  * @freq: Frequency (in MHz) on which the frame was received
     434             :  * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
     435             :  */
     436        1335 : int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
     437             :                  const u8 *bssid, u8 categ, const u8 *data, size_t len,
     438             :                  int freq)
     439             : {
     440             :         struct gas_query_pending *query;
     441        1335 :         u8 action, dialog_token, frag_id = 0, more_frags = 0;
     442             :         u16 comeback_delay, resp_len;
     443             :         const u8 *pos, *adv_proto;
     444             :         int prot, pmf;
     445             :         unsigned int left;
     446             : 
     447        1335 :         if (gas == NULL || len < 4)
     448          18 :                 return -1;
     449             : 
     450        1317 :         prot = categ == WLAN_ACTION_PROTECTED_DUAL;
     451        1317 :         pmf = pmf_in_use(gas->wpa_s, bssid);
     452        1317 :         if (prot && !pmf) {
     453           0 :                 wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
     454           0 :                 return 0;
     455             :         }
     456        1317 :         if (!prot && pmf) {
     457           0 :                 wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
     458           0 :                 return 0;
     459             :         }
     460             : 
     461        1317 :         pos = data;
     462        1317 :         action = *pos++;
     463        1317 :         dialog_token = *pos++;
     464             : 
     465        1317 :         if (action != WLAN_PA_GAS_INITIAL_RESP &&
     466             :             action != WLAN_PA_GAS_COMEBACK_RESP)
     467         830 :                 return -1; /* Not a GAS response */
     468             : 
     469         487 :         query = gas_query_get_pending(gas, sa, dialog_token);
     470         487 :         if (query == NULL) {
     471         609 :                 wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
     472         522 :                            " dialog token %u", MAC2STR(sa), dialog_token);
     473          87 :                 return -1;
     474             :         }
     475             : 
     476        2400 :         wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
     477        2400 :                    ms_from_time(&query->last_oper), MAC2STR(sa));
     478             : 
     479         400 :         if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
     480           7 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
     481             :                            MACSTR " dialog token %u when waiting for comeback "
     482           6 :                            "response", MAC2STR(sa), dialog_token);
     483           1 :                 return 0;
     484             :         }
     485             : 
     486         399 :         if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
     487          21 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
     488             :                            MACSTR " dialog token %u when waiting for initial "
     489          18 :                            "response", MAC2STR(sa), dialog_token);
     490           3 :                 return 0;
     491             :         }
     492             : 
     493         396 :         query->status_code = WPA_GET_LE16(pos);
     494         396 :         pos += 2;
     495             : 
     496         396 :         if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
     497             :             action == WLAN_PA_GAS_COMEBACK_RESP) {
     498           2 :                 wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
     499         394 :         } else if (query->status_code != WLAN_STATUS_SUCCESS) {
     500          21 :                 wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
     501             :                            "%u failed - status code %u",
     502          21 :                            MAC2STR(sa), dialog_token, query->status_code);
     503           3 :                 gas_query_done(gas, query, GAS_QUERY_FAILURE);
     504           3 :                 return 0;
     505             :         }
     506             : 
     507         393 :         if (action == WLAN_PA_GAS_COMEBACK_RESP) {
     508         176 :                 if (pos + 1 > data + len)
     509           1 :                         return 0;
     510         175 :                 frag_id = *pos & 0x7f;
     511         175 :                 more_frags = (*pos & 0x80) >> 7;
     512         175 :                 pos++;
     513             :         }
     514             : 
     515             :         /* Comeback Delay */
     516         392 :         if (pos + 2 > data + len)
     517           1 :                 return 0;
     518         391 :         comeback_delay = WPA_GET_LE16(pos);
     519         391 :         pos += 2;
     520             : 
     521             :         /* Advertisement Protocol element */
     522         391 :         if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
     523          24 :                 wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
     524             :                            "Protocol element in the response from " MACSTR,
     525          24 :                            MAC2STR(sa));
     526           4 :                 return 0;
     527             :         }
     528             : 
     529         387 :         if (*pos != WLAN_EID_ADV_PROTO) {
     530           7 :                 wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
     531             :                            "Protocol element ID %u in response from " MACSTR,
     532           7 :                            *pos, MAC2STR(sa));
     533           1 :                 return 0;
     534             :         }
     535             : 
     536         386 :         adv_proto = pos;
     537         386 :         pos += 2 + pos[1];
     538             : 
     539             :         /* Query Response Length */
     540         386 :         if (pos + 2 > data + len) {
     541           2 :                 wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
     542           2 :                 return 0;
     543             :         }
     544         384 :         resp_len = WPA_GET_LE16(pos);
     545         384 :         pos += 2;
     546             : 
     547         384 :         left = data + len - pos;
     548         384 :         if (resp_len > left) {
     549          18 :                 wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
     550          18 :                            "response from " MACSTR, MAC2STR(sa));
     551           3 :                 return 0;
     552             :         }
     553             : 
     554         381 :         if (resp_len < left) {
     555           6 :                 wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
     556             :                            "after Query Response from " MACSTR,
     557           6 :                            left - resp_len, MAC2STR(sa));
     558             :         }
     559             : 
     560         381 :         if (action == WLAN_PA_GAS_COMEBACK_RESP)
     561         174 :                 gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
     562             :                                       frag_id, more_frags, comeback_delay);
     563             :         else
     564         207 :                 gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
     565             :                                      comeback_delay);
     566             : 
     567         381 :         return 0;
     568             : }
     569             : 
     570             : 
     571          10 : static void gas_query_timeout(void *eloop_data, void *user_ctx)
     572             : {
     573          10 :         struct gas_query *gas = eloop_data;
     574          10 :         struct gas_query_pending *query = user_ctx;
     575             : 
     576          70 :         wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
     577             :                    " dialog token %u",
     578          70 :                    MAC2STR(query->addr), query->dialog_token);
     579          10 :         gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
     580          10 : }
     581             : 
     582             : 
     583         217 : static int gas_query_dialog_token_available(struct gas_query *gas,
     584             :                                             const u8 *dst, u8 dialog_token)
     585             : {
     586             :         struct gas_query_pending *q;
     587         220 :         dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
     588           6 :                 if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
     589           3 :                     dialog_token == q->dialog_token)
     590           0 :                         return 0;
     591             :         }
     592             : 
     593         217 :         return 1;
     594             : }
     595             : 
     596             : 
     597         217 : static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
     598             : {
     599         217 :         struct gas_query_pending *query = work->ctx;
     600         217 :         struct gas_query *gas = query->gas;
     601         217 :         struct wpa_supplicant *wpa_s = gas->wpa_s;
     602             : 
     603         217 :         if (deinit) {
     604           2 :                 if (work->started) {
     605           0 :                         gas->work = NULL;
     606           0 :                         gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
     607           0 :                         return;
     608             :                 }
     609             : 
     610           2 :                 gas_query_free(query, 1);
     611           2 :                 return;
     612             :         }
     613             : 
     614         215 :         if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
     615           0 :                 wpa_msg(wpa_s, MSG_INFO,
     616             :                         "Failed to assign random MAC address for GAS");
     617           0 :                 gas_query_free(query, 1);
     618           0 :                 radio_work_done(work);
     619           0 :                 return;
     620             :         }
     621             : 
     622         215 :         gas->work = work;
     623             : 
     624         215 :         if (gas_query_tx(gas, query, query->req) < 0) {
     625           0 :                 wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
     626           0 :                            MACSTR, MAC2STR(query->addr));
     627           0 :                 gas_query_free(query, 1);
     628           0 :                 return;
     629             :         }
     630         215 :         gas->current = query;
     631             : 
     632         215 :         wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
     633         215 :                    query->dialog_token);
     634         215 :         eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
     635             :                                gas_query_timeout, gas, query);
     636             : 
     637             : }
     638             : 
     639             : 
     640             : /**
     641             :  * gas_query_req - Request a GAS query
     642             :  * @gas: GAS query data from gas_query_init()
     643             :  * @dst: Destination MAC address for the query
     644             :  * @freq: Frequency (in MHz) for the channel on which to send the query
     645             :  * @req: GAS query payload (to be freed by gas_query module in case of success
     646             :  *      return)
     647             :  * @cb: Callback function for reporting GAS query result and response
     648             :  * @ctx: Context pointer to use with the @cb call
     649             :  * Returns: dialog token (>= 0) on success or -1 on failure
     650             :  */
     651         217 : int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
     652             :                   struct wpabuf *req,
     653             :                   void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
     654             :                              enum gas_query_result result,
     655             :                              const struct wpabuf *adv_proto,
     656             :                              const struct wpabuf *resp, u16 status_code),
     657             :                   void *ctx)
     658             : {
     659             :         struct gas_query_pending *query;
     660             :         int dialog_token;
     661             :         static int next_start = 0;
     662             : 
     663         217 :         if (wpabuf_len(req) < 3)
     664           0 :                 return -1;
     665             : 
     666         217 :         for (dialog_token = 0; dialog_token < 256; dialog_token++) {
     667         217 :                 if (gas_query_dialog_token_available(
     668         217 :                             gas, dst, (next_start + dialog_token) % 256))
     669         217 :                         break;
     670             :         }
     671         217 :         if (dialog_token == 256)
     672           0 :                 return -1; /* Too many pending queries */
     673         217 :         dialog_token = (next_start + dialog_token) % 256;
     674         217 :         next_start = (dialog_token + 1) % 256;
     675             : 
     676         217 :         query = os_zalloc(sizeof(*query));
     677         217 :         if (query == NULL)
     678           0 :                 return -1;
     679             : 
     680         217 :         query->gas = gas;
     681         217 :         os_memcpy(query->addr, dst, ETH_ALEN);
     682         217 :         query->dialog_token = dialog_token;
     683         217 :         query->freq = freq;
     684         217 :         query->cb = cb;
     685         217 :         query->ctx = ctx;
     686         217 :         query->req = req;
     687         217 :         dl_list_add(&gas->pending, &query->list);
     688             : 
     689         217 :         *(wpabuf_mhead_u8(req) + 2) = dialog_token;
     690             : 
     691        1736 :         wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
     692             :                 " dialog_token=%u freq=%d",
     693        1519 :                 MAC2STR(query->addr), query->dialog_token, query->freq);
     694             : 
     695         217 :         if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
     696             :                            query) < 0) {
     697           0 :                 gas_query_free(query, 1);
     698           0 :                 return -1;
     699             :         }
     700             : 
     701         217 :         return dialog_token;
     702             : }
     703             : 
     704             : 
     705             : /**
     706             :  * gas_query_cancel - Cancel a pending GAS query
     707             :  * @gas: GAS query data from gas_query_init()
     708             :  * @dst: Destination MAC address for the query
     709             :  * @dialog_token: Dialog token from gas_query_req()
     710             :  */
     711           0 : void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
     712             : {
     713             :         struct gas_query_pending *query;
     714             : 
     715           0 :         query = gas_query_get_pending(gas, dst, dialog_token);
     716           0 :         if (query)
     717           0 :                 gas_query_done(gas, query, GAS_QUERY_CANCELLED);
     718             : 
     719           0 : }

Generated by: LCOV version 1.10