LCOV - code coverage report
Current view: top level - src/ap - gas_serv.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1443382998 Lines: 643 711 90.4 %
Date: 2015-09-27 Functions: 38 38 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Generic advertisement service (GAS) server
       3             :  * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
       4             :  *
       5             :  * This software may be distributed under the terms of the BSD license.
       6             :  * See README for more details.
       7             :  */
       8             : 
       9             : #include "includes.h"
      10             : 
      11             : #include "common.h"
      12             : #include "common/ieee802_11_defs.h"
      13             : #include "common/gas.h"
      14             : #include "utils/eloop.h"
      15             : #include "hostapd.h"
      16             : #include "ap_config.h"
      17             : #include "ap_drv_ops.h"
      18             : #include "sta_info.h"
      19             : #include "gas_serv.h"
      20             : 
      21             : 
      22           1 : static void convert_to_protected_dual(struct wpabuf *msg)
      23             : {
      24           1 :         u8 *categ = wpabuf_mhead_u8(msg);
      25           1 :         *categ = WLAN_ACTION_PROTECTED_DUAL;
      26           1 : }
      27             : 
      28             : 
      29             : static struct gas_dialog_info *
      30          17 : gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
      31             : {
      32             :         struct sta_info *sta;
      33          17 :         struct gas_dialog_info *dia = NULL;
      34             :         int i, j;
      35             : 
      36          17 :         sta = ap_get_sta(hapd, addr);
      37          17 :         if (!sta) {
      38             :                 /*
      39             :                  * We need a STA entry to be able to maintain state for
      40             :                  * the GAS query.
      41             :                  */
      42           8 :                 wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
      43             :                            "GAS query");
      44           8 :                 sta = ap_sta_add(hapd, addr);
      45           8 :                 if (!sta) {
      46           0 :                         wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
      47           0 :                                    " for GAS query", MAC2STR(addr));
      48           0 :                         return NULL;
      49             :                 }
      50           8 :                 sta->flags |= WLAN_STA_GAS;
      51             :                 /*
      52             :                  * The default inactivity is 300 seconds. We don't need
      53             :                  * it to be that long.
      54             :                  */
      55           8 :                 ap_sta_session_timeout(hapd, sta, 5);
      56             :         } else {
      57           9 :                 ap_sta_replenish_timeout(hapd, sta, 5);
      58             :         }
      59             : 
      60          17 :         if (sta->gas_dialog == NULL) {
      61           9 :                 sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
      62             :                                             sizeof(struct gas_dialog_info));
      63           9 :                 if (sta->gas_dialog == NULL)
      64           0 :                         return NULL;
      65             :         }
      66             : 
      67          50 :         for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
      68          24 :                 if (i == GAS_DIALOG_MAX)
      69           0 :                         i = 0;
      70          24 :                 if (sta->gas_dialog[i].valid)
      71           8 :                         continue;
      72          16 :                 dia = &sta->gas_dialog[i];
      73          16 :                 dia->valid = 1;
      74          16 :                 dia->dialog_token = dialog_token;
      75          16 :                 sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
      76          16 :                 return dia;
      77             :         }
      78             : 
      79           7 :         wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
      80             :                 MACSTR " dialog_token %u. Consider increasing "
      81           6 :                 "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
      82             : 
      83           1 :         return NULL;
      84             : }
      85             : 
      86             : 
      87             : struct gas_dialog_info *
      88          38 : gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
      89             :                      u8 dialog_token)
      90             : {
      91             :         struct sta_info *sta;
      92             :         int i;
      93             : 
      94          38 :         sta = ap_get_sta(hapd, addr);
      95          38 :         if (!sta) {
      96           6 :                 wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
      97           6 :                            MAC2STR(addr));
      98           1 :                 return NULL;
      99             :         }
     100          86 :         for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
     101          80 :                 if (sta->gas_dialog[i].dialog_token != dialog_token ||
     102          37 :                     !sta->gas_dialog[i].valid)
     103           6 :                         continue;
     104          37 :                 return &sta->gas_dialog[i];
     105             :         }
     106           0 :         wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
     107           0 :                    MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
     108           0 :         return NULL;
     109             : }
     110             : 
     111             : 
     112          24 : void gas_serv_dialog_clear(struct gas_dialog_info *dia)
     113             : {
     114          24 :         wpabuf_free(dia->sd_resp);
     115          24 :         os_memset(dia, 0, sizeof(*dia));
     116          24 : }
     117             : 
     118             : 
     119           7 : static void gas_serv_free_dialogs(struct hostapd_data *hapd,
     120             :                                   const u8 *sta_addr)
     121             : {
     122             :         struct sta_info *sta;
     123             :         int i;
     124             : 
     125           7 :         sta = ap_get_sta(hapd, sta_addr);
     126           7 :         if (sta == NULL || sta->gas_dialog == NULL)
     127           0 :                 return;
     128             : 
     129          63 :         for (i = 0; i < GAS_DIALOG_MAX; i++) {
     130          56 :                 if (sta->gas_dialog[i].valid)
     131           0 :                         return;
     132             :         }
     133             : 
     134           7 :         os_free(sta->gas_dialog);
     135           7 :         sta->gas_dialog = NULL;
     136             : }
     137             : 
     138             : 
     139             : #ifdef CONFIG_HS20
     140         363 : static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
     141             :                                    struct wpabuf *buf)
     142             : {
     143             :         u8 *len;
     144             : 
     145         363 :         len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
     146         363 :         wpabuf_put_be24(buf, OUI_WFA);
     147         363 :         wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
     148         363 :         wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
     149         363 :         wpabuf_put_u8(buf, 0); /* Reserved */
     150         363 :         wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
     151         363 :         if (hapd->conf->hs20_oper_friendly_name)
     152          21 :                 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
     153         363 :         if (hapd->conf->hs20_wan_metrics)
     154         361 :                 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
     155         363 :         if (hapd->conf->hs20_connection_capability)
     156         363 :                 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
     157         363 :         if (hapd->conf->nai_realm_data)
     158         359 :                 wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
     159         363 :         if (hapd->conf->hs20_operating_class)
     160         363 :                 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
     161         363 :         if (hapd->conf->hs20_osu_providers_count)
     162           4 :                 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
     163         363 :         if (hapd->conf->hs20_icons_count)
     164           4 :                 wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
     165         363 :         gas_anqp_set_element_len(buf, len);
     166         363 : }
     167             : #endif /* CONFIG_HS20 */
     168             : 
     169             : 
     170         188 : static void anqp_add_capab_list(struct hostapd_data *hapd,
     171             :                                 struct wpabuf *buf)
     172             : {
     173             :         u8 *len;
     174             : 
     175         188 :         len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
     176         188 :         wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
     177         188 :         if (hapd->conf->venue_name)
     178         188 :                 wpabuf_put_le16(buf, ANQP_VENUE_NAME);
     179         188 :         if (hapd->conf->network_auth_type)
     180          17 :                 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
     181         188 :         if (hapd->conf->roaming_consortium)
     182         188 :                 wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
     183         188 :         if (hapd->conf->ipaddr_type_configured)
     184          17 :                 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
     185         188 :         if (hapd->conf->nai_realm_data)
     186         186 :                 wpabuf_put_le16(buf, ANQP_NAI_REALM);
     187         188 :         if (hapd->conf->anqp_3gpp_cell_net)
     188         186 :                 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
     189         188 :         if (hapd->conf->domain_name)
     190         181 :                 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
     191             : #ifdef CONFIG_HS20
     192         188 :         anqp_add_hs_capab_list(hapd, buf);
     193             : #endif /* CONFIG_HS20 */
     194         188 :         gas_anqp_set_element_len(buf, len);
     195         188 : }
     196             : 
     197             : 
     198          23 : static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
     199             : {
     200          23 :         if (hapd->conf->venue_name) {
     201             :                 u8 *len;
     202             :                 unsigned int i;
     203          23 :                 len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
     204          23 :                 wpabuf_put_u8(buf, hapd->conf->venue_group);
     205          23 :                 wpabuf_put_u8(buf, hapd->conf->venue_type);
     206          68 :                 for (i = 0; i < hapd->conf->venue_name_count; i++) {
     207             :                         struct hostapd_lang_string *vn;
     208          45 :                         vn = &hapd->conf->venue_name[i];
     209          45 :                         wpabuf_put_u8(buf, 3 + vn->name_len);
     210          45 :                         wpabuf_put_data(buf, vn->lang, 3);
     211          45 :                         wpabuf_put_data(buf, vn->name, vn->name_len);
     212             :                 }
     213          23 :                 gas_anqp_set_element_len(buf, len);
     214             :         }
     215          23 : }
     216             : 
     217             : 
     218          19 : static void anqp_add_network_auth_type(struct hostapd_data *hapd,
     219             :                                        struct wpabuf *buf)
     220             : {
     221          19 :         if (hapd->conf->network_auth_type) {
     222          13 :                 wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
     223          13 :                 wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
     224          13 :                 wpabuf_put_data(buf, hapd->conf->network_auth_type,
     225          13 :                                 hapd->conf->network_auth_type_len);
     226             :         }
     227          19 : }
     228             : 
     229             : 
     230          30 : static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
     231             :                                         struct wpabuf *buf)
     232             : {
     233             :         unsigned int i;
     234             :         u8 *len;
     235             : 
     236          30 :         len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
     237         150 :         for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
     238             :                 struct hostapd_roaming_consortium *rc;
     239         120 :                 rc = &hapd->conf->roaming_consortium[i];
     240         120 :                 wpabuf_put_u8(buf, rc->len);
     241         120 :                 wpabuf_put_data(buf, rc->oi, rc->len);
     242             :         }
     243          30 :         gas_anqp_set_element_len(buf, len);
     244          30 : }
     245             : 
     246             : 
     247          19 : static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
     248             :                                                struct wpabuf *buf)
     249             : {
     250          19 :         if (hapd->conf->ipaddr_type_configured) {
     251          13 :                 wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
     252          13 :                 wpabuf_put_le16(buf, 1);
     253          13 :                 wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
     254             :         }
     255          19 : }
     256             : 
     257             : 
     258         307 : static void anqp_add_nai_realm_eap(struct wpabuf *buf,
     259             :                                    struct hostapd_nai_realm_data *realm)
     260             : {
     261             :         unsigned int i, j;
     262             : 
     263         307 :         wpabuf_put_u8(buf, realm->eap_method_count);
     264             : 
     265         622 :         for (i = 0; i < realm->eap_method_count; i++) {
     266         315 :                 struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
     267         315 :                 wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
     268         315 :                 wpabuf_put_u8(buf, eap->eap_method);
     269         315 :                 wpabuf_put_u8(buf, eap->num_auths);
     270         776 :                 for (j = 0; j < eap->num_auths; j++) {
     271         461 :                         wpabuf_put_u8(buf, eap->auth_id[j]);
     272         461 :                         wpabuf_put_u8(buf, 1);
     273         461 :                         wpabuf_put_u8(buf, eap->auth_val[j]);
     274             :                 }
     275             :         }
     276         307 : }
     277             : 
     278             : 
     279           3 : static void anqp_add_nai_realm_data(struct wpabuf *buf,
     280             :                                     struct hostapd_nai_realm_data *realm,
     281             :                                     unsigned int realm_idx)
     282             : {
     283             :         u8 *realm_data_len;
     284             : 
     285           3 :         wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
     286           3 :                    (int) os_strlen(realm->realm[realm_idx]));
     287           3 :         realm_data_len = wpabuf_put(buf, 2);
     288           3 :         wpabuf_put_u8(buf, realm->encoding);
     289           3 :         wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
     290           3 :         wpabuf_put_str(buf, realm->realm[realm_idx]);
     291           3 :         anqp_add_nai_realm_eap(buf, realm);
     292           3 :         gas_anqp_set_element_len(buf, realm_data_len);
     293           3 : }
     294             : 
     295             : 
     296           4 : static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
     297             :                                            struct wpabuf *buf,
     298             :                                            const u8 *home_realm,
     299             :                                            size_t home_realm_len)
     300             : {
     301             :         unsigned int i, j, k;
     302           4 :         u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
     303             :         struct hostapd_nai_realm_data *realm;
     304             :         const u8 *pos, *realm_name, *end;
     305             :         struct {
     306             :                 unsigned int realm_data_idx;
     307             :                 unsigned int realm_idx;
     308             :         } matches[10];
     309             : 
     310           4 :         pos = home_realm;
     311           4 :         end = pos + home_realm_len;
     312           4 :         if (pos + 1 > end) {
     313           1 :                 wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
     314             :                             home_realm, home_realm_len);
     315           1 :                 return -1;
     316             :         }
     317           3 :         num_realms = *pos++;
     318             : 
     319           6 :         for (i = 0; i < num_realms && num_matching < 10; i++) {
     320           3 :                 if (pos + 2 > end) {
     321           0 :                         wpa_hexdump(MSG_DEBUG,
     322             :                                     "Truncated NAI Home Realm Query",
     323             :                                     home_realm, home_realm_len);
     324           0 :                         return -1;
     325             :                 }
     326           3 :                 encoding = *pos++;
     327           3 :                 realm_len = *pos++;
     328           3 :                 if (pos + realm_len > end) {
     329           0 :                         wpa_hexdump(MSG_DEBUG,
     330             :                                     "Truncated NAI Home Realm Query",
     331             :                                     home_realm, home_realm_len);
     332           0 :                         return -1;
     333             :                 }
     334           3 :                 realm_name = pos;
     335          12 :                 for (j = 0; j < hapd->conf->nai_realm_count &&
     336           6 :                              num_matching < 10; j++) {
     337             :                         const u8 *rpos, *rend;
     338           6 :                         realm = &hapd->conf->nai_realm_data[j];
     339           6 :                         if (encoding != realm->encoding)
     340           0 :                                 continue;
     341             : 
     342           6 :                         rpos = realm_name;
     343          18 :                         while (rpos < realm_name + realm_len &&
     344             :                                num_matching < 10) {
     345          78 :                                 for (rend = rpos;
     346          66 :                                      rend < realm_name + realm_len; rend++) {
     347          66 :                                         if (*rend == ';')
     348           0 :                                                 break;
     349             :                                 }
     350          30 :                                 for (k = 0; k < MAX_NAI_REALMS &&
     351          18 :                                              realm->realm[k] &&
     352           6 :                                              num_matching < 10; k++) {
     353          12 :                                         if ((int) os_strlen(realm->realm[k]) !=
     354           9 :                                             rend - rpos ||
     355           3 :                                             os_strncmp((char *) rpos,
     356             :                                                        realm->realm[k],
     357             :                                                        rend - rpos) != 0)
     358           3 :                                                 continue;
     359           3 :                                         matches[num_matching].realm_data_idx =
     360             :                                                 j;
     361           3 :                                         matches[num_matching].realm_idx = k;
     362           3 :                                         num_matching++;
     363             :                                 }
     364           6 :                                 rpos = rend + 1;
     365             :                         }
     366             :                 }
     367           3 :                 pos += realm_len;
     368             :         }
     369             : 
     370           3 :         realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
     371           3 :         wpabuf_put_le16(buf, num_matching);
     372             : 
     373             :         /*
     374             :          * There are two ways to format. 1. each realm in a NAI Realm Data unit
     375             :          * 2. all realms that share the same EAP methods in a NAI Realm Data
     376             :          * unit. The first format is likely to be bigger in size than the
     377             :          * second, but may be easier to parse and process by the receiver.
     378             :          */
     379           6 :         for (i = 0; i < num_matching; i++) {
     380           3 :                 wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
     381             :                            matches[i].realm_data_idx, matches[i].realm_idx);
     382           3 :                 realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
     383           3 :                 anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
     384             :         }
     385           3 :         gas_anqp_set_element_len(buf, realm_list_len);
     386           3 :         return 0;
     387             : }
     388             : 
     389             : 
     390         174 : static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
     391             :                                const u8 *home_realm, size_t home_realm_len,
     392             :                                int nai_realm, int nai_home_realm)
     393             : {
     394         342 :         if (nai_realm && hapd->conf->nai_realm_data) {
     395             :                 u8 *len;
     396             :                 unsigned int i, j;
     397         168 :                 len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
     398         168 :                 wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
     399         472 :                 for (i = 0; i < hapd->conf->nai_realm_count; i++) {
     400             :                         u8 *realm_data_len, *realm_len;
     401             :                         struct hostapd_nai_realm_data *realm;
     402             : 
     403         304 :                         realm = &hapd->conf->nai_realm_data[i];
     404         304 :                         realm_data_len = wpabuf_put(buf, 2);
     405         304 :                         wpabuf_put_u8(buf, realm->encoding);
     406         304 :                         realm_len = wpabuf_put(buf, 1);
     407         610 :                         for (j = 0; realm->realm[j]; j++) {
     408         306 :                                 if (j > 0)
     409           2 :                                         wpabuf_put_u8(buf, ';');
     410         306 :                                 wpabuf_put_str(buf, realm->realm[j]);
     411             :                         }
     412         304 :                         *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
     413         304 :                         anqp_add_nai_realm_eap(buf, realm);
     414         304 :                         gas_anqp_set_element_len(buf, realm_data_len);
     415             :                 }
     416         168 :                 gas_anqp_set_element_len(buf, len);
     417           6 :         } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
     418           4 :                 hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
     419             :                                                 home_realm_len);
     420             :         }
     421         174 : }
     422             : 
     423             : 
     424          38 : static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
     425             :                                            struct wpabuf *buf)
     426             : {
     427          38 :         if (hapd->conf->anqp_3gpp_cell_net) {
     428          36 :                 wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
     429          36 :                 wpabuf_put_le16(buf,
     430          36 :                                 hapd->conf->anqp_3gpp_cell_net_len);
     431          36 :                 wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
     432          36 :                                 hapd->conf->anqp_3gpp_cell_net_len);
     433             :         }
     434          38 : }
     435             : 
     436             : 
     437         141 : static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
     438             : {
     439         141 :         if (hapd->conf->domain_name) {
     440         134 :                 wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
     441         134 :                 wpabuf_put_le16(buf, hapd->conf->domain_name_len);
     442         134 :                 wpabuf_put_data(buf, hapd->conf->domain_name,
     443         134 :                                 hapd->conf->domain_name_len);
     444             :         }
     445         141 : }
     446             : 
     447             : 
     448             : #ifdef CONFIG_HS20
     449             : 
     450          12 : static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
     451             :                                             struct wpabuf *buf)
     452             : {
     453          12 :         if (hapd->conf->hs20_oper_friendly_name) {
     454             :                 u8 *len;
     455             :                 unsigned int i;
     456           6 :                 len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
     457           6 :                 wpabuf_put_be24(buf, OUI_WFA);
     458           6 :                 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
     459           6 :                 wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
     460           6 :                 wpabuf_put_u8(buf, 0); /* Reserved */
     461          18 :                 for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
     462             :                 {
     463             :                         struct hostapd_lang_string *vn;
     464          12 :                         vn = &hapd->conf->hs20_oper_friendly_name[i];
     465          12 :                         wpabuf_put_u8(buf, 3 + vn->name_len);
     466          12 :                         wpabuf_put_data(buf, vn->lang, 3);
     467          12 :                         wpabuf_put_data(buf, vn->name, vn->name_len);
     468             :                 }
     469           6 :                 gas_anqp_set_element_len(buf, len);
     470             :         }
     471          12 : }
     472             : 
     473             : 
     474          38 : static void anqp_add_wan_metrics(struct hostapd_data *hapd,
     475             :                                  struct wpabuf *buf)
     476             : {
     477          38 :         if (hapd->conf->hs20_wan_metrics) {
     478          37 :                 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
     479          37 :                 wpabuf_put_be24(buf, OUI_WFA);
     480          37 :                 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
     481          37 :                 wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
     482          37 :                 wpabuf_put_u8(buf, 0); /* Reserved */
     483          37 :                 wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
     484          37 :                 gas_anqp_set_element_len(buf, len);
     485             :         }
     486          38 : }
     487             : 
     488             : 
     489          21 : static void anqp_add_connection_capability(struct hostapd_data *hapd,
     490             :                                            struct wpabuf *buf)
     491             : {
     492          21 :         if (hapd->conf->hs20_connection_capability) {
     493          21 :                 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
     494          21 :                 wpabuf_put_be24(buf, OUI_WFA);
     495          21 :                 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
     496          21 :                 wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
     497          21 :                 wpabuf_put_u8(buf, 0); /* Reserved */
     498          21 :                 wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
     499          21 :                                 hapd->conf->hs20_connection_capability_len);
     500          21 :                 gas_anqp_set_element_len(buf, len);
     501             :         }
     502          21 : }
     503             : 
     504             : 
     505          10 : static void anqp_add_operating_class(struct hostapd_data *hapd,
     506             :                                      struct wpabuf *buf)
     507             : {
     508          10 :         if (hapd->conf->hs20_operating_class) {
     509          10 :                 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
     510          10 :                 wpabuf_put_be24(buf, OUI_WFA);
     511          10 :                 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
     512          10 :                 wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
     513          10 :                 wpabuf_put_u8(buf, 0); /* Reserved */
     514          10 :                 wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
     515          10 :                                 hapd->conf->hs20_operating_class_len);
     516          10 :                 gas_anqp_set_element_len(buf, len);
     517             :         }
     518          10 : }
     519             : 
     520             : 
     521           2 : static void anqp_add_osu_provider(struct wpabuf *buf,
     522             :                                   struct hostapd_bss_config *bss,
     523             :                                   struct hs20_osu_provider *p)
     524             : {
     525             :         u8 *len, *len2, *count;
     526             :         unsigned int i;
     527             : 
     528           2 :         len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
     529             : 
     530             :         /* OSU Friendly Name Duples */
     531           2 :         len2 = wpabuf_put(buf, 2);
     532           6 :         for (i = 0; i < p->friendly_name_count; i++) {
     533           4 :                 struct hostapd_lang_string *s = &p->friendly_name[i];
     534           4 :                 wpabuf_put_u8(buf, 3 + s->name_len);
     535           4 :                 wpabuf_put_data(buf, s->lang, 3);
     536           4 :                 wpabuf_put_data(buf, s->name, s->name_len);
     537             :         }
     538           2 :         WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
     539             : 
     540             :         /* OSU Server URI */
     541           2 :         if (p->server_uri) {
     542           2 :                 wpabuf_put_u8(buf, os_strlen(p->server_uri));
     543           2 :                 wpabuf_put_str(buf, p->server_uri);
     544             :         } else
     545           0 :                 wpabuf_put_u8(buf, 0);
     546             : 
     547             :         /* OSU Method List */
     548           2 :         count = wpabuf_put(buf, 1);
     549           4 :         for (i = 0; p->method_list[i] >= 0; i++)
     550           2 :                 wpabuf_put_u8(buf, p->method_list[i]);
     551           2 :         *count = i;
     552             : 
     553             :         /* Icons Available */
     554           2 :         len2 = wpabuf_put(buf, 2);
     555           4 :         for (i = 0; i < p->icons_count; i++) {
     556             :                 size_t j;
     557           2 :                 struct hs20_icon *icon = NULL;
     558             : 
     559           4 :                 for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
     560           2 :                         if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
     561             :                             0)
     562           2 :                                 icon = &bss->hs20_icons[j];
     563             :                 }
     564           2 :                 if (!icon)
     565           0 :                         continue; /* icon info not found */
     566             : 
     567           2 :                 wpabuf_put_le16(buf, icon->width);
     568           2 :                 wpabuf_put_le16(buf, icon->height);
     569           2 :                 wpabuf_put_data(buf, icon->language, 3);
     570           2 :                 wpabuf_put_u8(buf, os_strlen(icon->type));
     571           2 :                 wpabuf_put_str(buf, icon->type);
     572           2 :                 wpabuf_put_u8(buf, os_strlen(icon->name));
     573           2 :                 wpabuf_put_str(buf, icon->name);
     574             :         }
     575           2 :         WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
     576             : 
     577             :         /* OSU_NAI */
     578           2 :         if (p->osu_nai) {
     579           1 :                 wpabuf_put_u8(buf, os_strlen(p->osu_nai));
     580           1 :                 wpabuf_put_str(buf, p->osu_nai);
     581             :         } else
     582           1 :                 wpabuf_put_u8(buf, 0);
     583             : 
     584             :         /* OSU Service Description Duples */
     585           2 :         len2 = wpabuf_put(buf, 2);
     586           6 :         for (i = 0; i < p->service_desc_count; i++) {
     587           4 :                 struct hostapd_lang_string *s = &p->service_desc[i];
     588           4 :                 wpabuf_put_u8(buf, 3 + s->name_len);
     589           4 :                 wpabuf_put_data(buf, s->lang, 3);
     590           4 :                 wpabuf_put_data(buf, s->name, s->name_len);
     591             :         }
     592           2 :         WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
     593             : 
     594           2 :         WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
     595           2 : }
     596             : 
     597             : 
     598          10 : static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
     599             :                                         struct wpabuf *buf)
     600             : {
     601          10 :         if (hapd->conf->hs20_osu_providers_count) {
     602             :                 size_t i;
     603           2 :                 u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
     604           2 :                 wpabuf_put_be24(buf, OUI_WFA);
     605           2 :                 wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
     606           2 :                 wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
     607           2 :                 wpabuf_put_u8(buf, 0); /* Reserved */
     608             : 
     609             :                 /* OSU SSID */
     610           2 :                 wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
     611           2 :                 wpabuf_put_data(buf, hapd->conf->osu_ssid,
     612           2 :                                 hapd->conf->osu_ssid_len);
     613             : 
     614             :                 /* Number of OSU Providers */
     615           2 :                 wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
     616             : 
     617           4 :                 for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
     618           2 :                         anqp_add_osu_provider(
     619             :                                 buf, hapd->conf,
     620           2 :                                 &hapd->conf->hs20_osu_providers[i]);
     621             :                 }
     622             : 
     623           2 :                 gas_anqp_set_element_len(buf, len);
     624             :         }
     625          10 : }
     626             : 
     627             : 
     628           3 : static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
     629             :                                       struct wpabuf *buf,
     630             :                                       const u8 *name, size_t name_len)
     631             : {
     632             :         struct hs20_icon *icon;
     633             :         size_t i;
     634             :         u8 *len;
     635             : 
     636           3 :         wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
     637             :                           name, name_len);
     638           3 :         for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
     639           3 :                 icon = &hapd->conf->hs20_icons[i];
     640           6 :                 if (name_len == os_strlen(icon->name) &&
     641           3 :                     os_memcmp(name, icon->name, name_len) == 0)
     642           3 :                         break;
     643             :         }
     644             : 
     645           3 :         if (i < hapd->conf->hs20_icons_count)
     646           3 :                 icon = &hapd->conf->hs20_icons[i];
     647             :         else
     648           0 :                 icon = NULL;
     649             : 
     650           3 :         len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
     651           3 :         wpabuf_put_be24(buf, OUI_WFA);
     652           3 :         wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
     653           3 :         wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
     654           3 :         wpabuf_put_u8(buf, 0); /* Reserved */
     655             : 
     656           3 :         if (icon) {
     657             :                 char *data;
     658             :                 size_t data_len;
     659             : 
     660           3 :                 data = os_readfile(icon->file, &data_len);
     661           3 :                 if (data == NULL || data_len > 65535) {
     662           0 :                         wpabuf_put_u8(buf, 2); /* Download Status:
     663             :                                                 * Unspecified file error */
     664           0 :                         wpabuf_put_u8(buf, 0);
     665           0 :                         wpabuf_put_le16(buf, 0);
     666             :                 } else {
     667           3 :                         wpabuf_put_u8(buf, 0); /* Download Status: Success */
     668           3 :                         wpabuf_put_u8(buf, os_strlen(icon->type));
     669           3 :                         wpabuf_put_str(buf, icon->type);
     670           3 :                         wpabuf_put_le16(buf, data_len);
     671           3 :                         wpabuf_put_data(buf, data, data_len);
     672             :                 }
     673           3 :                 os_free(data);
     674             :         } else {
     675           0 :                 wpabuf_put_u8(buf, 1); /* Download Status: File not found */
     676           0 :                 wpabuf_put_u8(buf, 0);
     677           0 :                 wpabuf_put_le16(buf, 0);
     678             :         }
     679             : 
     680           3 :         gas_anqp_set_element_len(buf, len);
     681           3 : }
     682             : 
     683             : #endif /* CONFIG_HS20 */
     684             : 
     685             : 
     686             : static struct wpabuf *
     687         208 : gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
     688             :                                 unsigned int request,
     689             :                                 const u8 *home_realm, size_t home_realm_len,
     690             :                                 const u8 *icon_name, size_t icon_name_len)
     691             : {
     692             :         struct wpabuf *buf;
     693             :         size_t len;
     694             : 
     695         208 :         len = 1400;
     696         208 :         if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
     697         174 :                 len += 1000;
     698         208 :         if (request & ANQP_REQ_ICON_REQUEST)
     699           3 :                 len += 65536;
     700             : 
     701         208 :         buf = wpabuf_alloc(len);
     702         208 :         if (buf == NULL)
     703           0 :                 return NULL;
     704             : 
     705         208 :         if (request & ANQP_REQ_CAPABILITY_LIST)
     706         188 :                 anqp_add_capab_list(hapd, buf);
     707         208 :         if (request & ANQP_REQ_VENUE_NAME)
     708          23 :                 anqp_add_venue_name(hapd, buf);
     709         208 :         if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
     710          19 :                 anqp_add_network_auth_type(hapd, buf);
     711         208 :         if (request & ANQP_REQ_ROAMING_CONSORTIUM)
     712          30 :                 anqp_add_roaming_consortium(hapd, buf);
     713         208 :         if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
     714          19 :                 anqp_add_ip_addr_type_availability(hapd, buf);
     715         208 :         if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
     716         174 :                 anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
     717             :                                    request & ANQP_REQ_NAI_REALM,
     718             :                                    request & ANQP_REQ_NAI_HOME_REALM);
     719         208 :         if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
     720          38 :                 anqp_add_3gpp_cellular_network(hapd, buf);
     721         208 :         if (request & ANQP_REQ_DOMAIN_NAME)
     722         141 :                 anqp_add_domain_name(hapd, buf);
     723             : 
     724             : #ifdef CONFIG_HS20
     725         208 :         if (request & ANQP_REQ_HS_CAPABILITY_LIST)
     726         175 :                 anqp_add_hs_capab_list(hapd, buf);
     727         208 :         if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
     728          12 :                 anqp_add_operator_friendly_name(hapd, buf);
     729         208 :         if (request & ANQP_REQ_WAN_METRICS)
     730          38 :                 anqp_add_wan_metrics(hapd, buf);
     731         208 :         if (request & ANQP_REQ_CONNECTION_CAPABILITY)
     732          21 :                 anqp_add_connection_capability(hapd, buf);
     733         208 :         if (request & ANQP_REQ_OPERATING_CLASS)
     734          10 :                 anqp_add_operating_class(hapd, buf);
     735         208 :         if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
     736          10 :                 anqp_add_osu_providers_list(hapd, buf);
     737         208 :         if (request & ANQP_REQ_ICON_REQUEST)
     738           3 :                 anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
     739             : #endif /* CONFIG_HS20 */
     740             : 
     741         208 :         return buf;
     742             : }
     743             : 
     744             : 
     745             : struct anqp_query_info {
     746             :         unsigned int request;
     747             :         const u8 *home_realm_query;
     748             :         size_t home_realm_query_len;
     749             :         const u8 *icon_name;
     750             :         size_t icon_name_len;
     751             :         int p2p_sd;
     752             : };
     753             : 
     754             : 
     755         894 : static void set_anqp_req(unsigned int bit, const char *name, int local,
     756             :                          struct anqp_query_info *qi)
     757             : {
     758         894 :         qi->request |= bit;
     759         894 :         if (local) {
     760         856 :                 wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
     761             :         } else {
     762          38 :                 wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
     763             :         }
     764         894 : }
     765             : 
     766             : 
     767         628 : static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
     768             :                                   struct anqp_query_info *qi)
     769             : {
     770         628 :         switch (info_id) {
     771             :         case ANQP_CAPABILITY_LIST:
     772         188 :                 set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
     773             :                              qi);
     774         188 :                 break;
     775             :         case ANQP_VENUE_NAME:
     776          23 :                 set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
     777          23 :                              hapd->conf->venue_name != NULL, qi);
     778          23 :                 break;
     779             :         case ANQP_NETWORK_AUTH_TYPE:
     780          19 :                 set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
     781          19 :                              hapd->conf->network_auth_type != NULL, qi);
     782          19 :                 break;
     783             :         case ANQP_ROAMING_CONSORTIUM:
     784          30 :                 set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
     785          30 :                              hapd->conf->roaming_consortium != NULL, qi);
     786          30 :                 break;
     787             :         case ANQP_IP_ADDR_TYPE_AVAILABILITY:
     788          19 :                 set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
     789             :                              "IP Addr Type Availability",
     790          19 :                              hapd->conf->ipaddr_type_configured, qi);
     791          19 :                 break;
     792             :         case ANQP_NAI_REALM:
     793         170 :                 set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
     794         170 :                              hapd->conf->nai_realm_data != NULL, qi);
     795         170 :                 break;
     796             :         case ANQP_3GPP_CELLULAR_NETWORK:
     797          38 :                 set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
     798             :                              "3GPP Cellular Network",
     799          38 :                              hapd->conf->anqp_3gpp_cell_net != NULL, qi);
     800          38 :                 break;
     801             :         case ANQP_DOMAIN_NAME:
     802         141 :                 set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
     803         141 :                              hapd->conf->domain_name != NULL, qi);
     804         141 :                 break;
     805             :         default:
     806           0 :                 wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
     807             :                            info_id);
     808           0 :                 break;
     809             :         }
     810         628 : }
     811             : 
     812             : 
     813         198 : static void rx_anqp_query_list(struct hostapd_data *hapd,
     814             :                                const u8 *pos, const u8 *end,
     815             :                                struct anqp_query_info *qi)
     816             : {
     817         198 :         wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
     818         198 :                    (unsigned int) (end - pos) / 2);
     819             : 
     820        1024 :         while (pos + 2 <= end) {
     821         628 :                 rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
     822         628 :                 pos += 2;
     823             :         }
     824         198 : }
     825             : 
     826             : 
     827             : #ifdef CONFIG_HS20
     828             : 
     829         266 : static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
     830             :                                   struct anqp_query_info *qi)
     831             : {
     832         266 :         switch (subtype) {
     833             :         case HS20_STYPE_CAPABILITY_LIST:
     834         175 :                 set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
     835             :                              1, qi);
     836         175 :                 break;
     837             :         case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
     838          12 :                 set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
     839             :                              "Operator Friendly Name",
     840          12 :                              hapd->conf->hs20_oper_friendly_name != NULL, qi);
     841          12 :                 break;
     842             :         case HS20_STYPE_WAN_METRICS:
     843          38 :                 set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
     844          38 :                              hapd->conf->hs20_wan_metrics != NULL, qi);
     845          38 :                 break;
     846             :         case HS20_STYPE_CONNECTION_CAPABILITY:
     847          21 :                 set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
     848             :                              "Connection Capability",
     849          21 :                              hapd->conf->hs20_connection_capability != NULL,
     850             :                              qi);
     851          21 :                 break;
     852             :         case HS20_STYPE_OPERATING_CLASS:
     853          10 :                 set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
     854          10 :                              hapd->conf->hs20_operating_class != NULL, qi);
     855          10 :                 break;
     856             :         case HS20_STYPE_OSU_PROVIDERS_LIST:
     857          10 :                 set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
     858          10 :                              hapd->conf->hs20_osu_providers_count, qi);
     859          10 :                 break;
     860             :         default:
     861           0 :                 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
     862             :                            subtype);
     863           0 :                 break;
     864             :         }
     865         266 : }
     866             : 
     867             : 
     868           4 : static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
     869             :                                       const u8 *pos, const u8 *end,
     870             :                                       struct anqp_query_info *qi)
     871             : {
     872           4 :         qi->request |= ANQP_REQ_NAI_HOME_REALM;
     873           4 :         qi->home_realm_query = pos;
     874           4 :         qi->home_realm_query_len = end - pos;
     875           4 :         if (hapd->conf->nai_realm_data != NULL) {
     876           4 :                 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
     877             :                            "(local)");
     878             :         } else {
     879           0 :                 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
     880             :                            "available");
     881             :         }
     882           4 : }
     883             : 
     884             : 
     885           3 : static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
     886             :                                     const u8 *pos, const u8 *end,
     887             :                                     struct anqp_query_info *qi)
     888             : {
     889           3 :         qi->request |= ANQP_REQ_ICON_REQUEST;
     890           3 :         qi->icon_name = pos;
     891           3 :         qi->icon_name_len = end - pos;
     892           3 :         if (hapd->conf->hs20_icons_count) {
     893           3 :                 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
     894             :                            "(local)");
     895             :         } else {
     896           0 :                 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
     897             :                            "available");
     898             :         }
     899           3 : }
     900             : 
     901             : 
     902         186 : static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
     903             :                                     const u8 *pos, const u8 *end,
     904             :                                     struct anqp_query_info *qi)
     905             : {
     906             :         u32 oui;
     907             :         u8 subtype;
     908             : 
     909         186 :         if (pos + 4 > end) {
     910           0 :                 wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
     911             :                            "Query element");
     912           0 :                 return;
     913             :         }
     914             : 
     915         186 :         oui = WPA_GET_BE24(pos);
     916         186 :         pos += 3;
     917         186 :         if (oui != OUI_WFA) {
     918           0 :                 wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
     919             :                            oui);
     920           0 :                 return;
     921             :         }
     922             : 
     923             : #ifdef CONFIG_P2P
     924           2 :         if (*pos == P2P_OUI_TYPE) {
     925             :                 /*
     926             :                  * This is for P2P SD and will be taken care of by the P2P
     927             :                  * implementation. This query needs to be ignored in the generic
     928             :                  * GAS server to avoid duplicated response.
     929             :                  */
     930           2 :                 wpa_printf(MSG_DEBUG,
     931             :                            "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
     932           2 :                            *pos);
     933           2 :                 qi->p2p_sd = 1;
     934           2 :                 return;
     935             :         }
     936             : #endif /* CONFIG_P2P */
     937             : 
     938         184 :         if (*pos != HS20_ANQP_OUI_TYPE) {
     939           0 :                 wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
     940           0 :                            *pos);
     941           0 :                 return;
     942             :         }
     943         184 :         pos++;
     944             : 
     945         184 :         if (pos + 1 >= end)
     946           0 :                 return;
     947             : 
     948         184 :         subtype = *pos++;
     949         184 :         pos++; /* Reserved */
     950         184 :         switch (subtype) {
     951             :         case HS20_STYPE_QUERY_LIST:
     952         177 :                 wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
     953         620 :                 while (pos < end) {
     954         266 :                         rx_anqp_hs_query_list(hapd, *pos, qi);
     955         266 :                         pos++;
     956             :                 }
     957         177 :                 break;
     958             :         case HS20_STYPE_NAI_HOME_REALM_QUERY:
     959           4 :                 rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
     960           4 :                 break;
     961             :         case HS20_STYPE_ICON_REQUEST:
     962           3 :                 rx_anqp_hs_icon_request(hapd, pos, end, qi);
     963           3 :                 break;
     964             :         default:
     965           0 :                 wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
     966             :                            "%u", subtype);
     967           0 :                 break;
     968             :         }
     969             : }
     970             : 
     971             : #endif /* CONFIG_HS20 */
     972             : 
     973             : 
     974         208 : static void gas_serv_req_local_processing(struct hostapd_data *hapd,
     975             :                                           const u8 *sa, u8 dialog_token,
     976             :                                           struct anqp_query_info *qi, int prot)
     977             : {
     978             :         struct wpabuf *buf, *tx_buf;
     979             : 
     980         208 :         buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
     981             :                                               qi->home_realm_query,
     982             :                                               qi->home_realm_query_len,
     983             :                                               qi->icon_name, qi->icon_name_len);
     984         208 :         wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
     985             :                         buf);
     986         208 :         if (!buf)
     987           0 :                 return;
     988             : #ifdef CONFIG_P2P
     989           2 :         if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
     990           2 :                 wpa_printf(MSG_DEBUG,
     991             :                            "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
     992           2 :                 wpabuf_free(buf);
     993           2 :                 return;
     994             :         }
     995             : #endif /* CONFIG_P2P */
     996             : 
     997         396 :         if (wpabuf_len(buf) > hapd->gas_frag_limit ||
     998         207 :             hapd->conf->gas_comeback_delay) {
     999             :                 struct gas_dialog_info *di;
    1000          17 :                 u16 comeback_delay = 1;
    1001             : 
    1002          17 :                 if (hapd->conf->gas_comeback_delay) {
    1003             :                         /* Testing - allow overriding of the delay value */
    1004           1 :                         comeback_delay = hapd->conf->gas_comeback_delay;
    1005             :                 }
    1006             : 
    1007          17 :                 wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
    1008             :                            "initial response - use GAS comeback");
    1009          17 :                 di = gas_dialog_create(hapd, sa, dialog_token);
    1010          17 :                 if (!di) {
    1011           7 :                         wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
    1012             :                                    "for " MACSTR " (dialog token %u)",
    1013           6 :                                    MAC2STR(sa), dialog_token);
    1014           1 :                         wpabuf_free(buf);
    1015           1 :                         tx_buf = gas_anqp_build_initial_resp_buf(
    1016             :                                 dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
    1017             :                                 0, NULL);
    1018             :                 } else {
    1019          16 :                         di->prot = prot;
    1020          16 :                         di->sd_resp = buf;
    1021          16 :                         di->sd_resp_pos = 0;
    1022          16 :                         tx_buf = gas_anqp_build_initial_resp_buf(
    1023             :                                 dialog_token, WLAN_STATUS_SUCCESS,
    1024             :                                 comeback_delay, NULL);
    1025             :                 }
    1026             :         } else {
    1027         189 :                 wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
    1028         189 :                 tx_buf = gas_anqp_build_initial_resp_buf(
    1029             :                         dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
    1030         189 :                 wpabuf_free(buf);
    1031             :         }
    1032         206 :         if (!tx_buf)
    1033           1 :                 return;
    1034         205 :         if (prot)
    1035           1 :                 convert_to_protected_dual(tx_buf);
    1036         410 :         hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
    1037         205 :                                 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
    1038         205 :         wpabuf_free(tx_buf);
    1039             : }
    1040             : 
    1041             : 
    1042         210 : static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
    1043             :                                         const u8 *sa,
    1044             :                                         const u8 *data, size_t len, int prot)
    1045             : {
    1046         210 :         const u8 *pos = data;
    1047         210 :         const u8 *end = data + len;
    1048             :         const u8 *next;
    1049             :         u8 dialog_token;
    1050             :         u16 slen;
    1051             :         struct anqp_query_info qi;
    1052             :         const u8 *adv_proto;
    1053             : 
    1054         210 :         if (len < 1 + 2)
    1055           3 :                 return;
    1056             : 
    1057         209 :         os_memset(&qi, 0, sizeof(qi));
    1058             : 
    1059         209 :         dialog_token = *pos++;
    1060        1463 :         wpa_msg(hapd->msg_ctx, MSG_DEBUG,
    1061             :                 "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
    1062        1254 :                 MAC2STR(sa), dialog_token);
    1063             : 
    1064         209 :         if (*pos != WLAN_EID_ADV_PROTO) {
    1065           0 :                 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
    1066           0 :                         "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
    1067           0 :                 return;
    1068             :         }
    1069         209 :         adv_proto = pos++;
    1070             : 
    1071         209 :         slen = *pos++;
    1072         209 :         next = pos + slen;
    1073         209 :         if (next > end || slen < 2) {
    1074           0 :                 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
    1075             :                         "GAS: Invalid IE in GAS Initial Request");
    1076           0 :                 return;
    1077             :         }
    1078         209 :         pos++; /* skip QueryRespLenLimit and PAME-BI */
    1079             : 
    1080         209 :         if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
    1081             :                 struct wpabuf *buf;
    1082           1 :                 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
    1083             :                         "GAS: Unsupported GAS advertisement protocol id %u",
    1084           1 :                         *pos);
    1085           1 :                 if (sa[0] & 0x01)
    1086           0 :                         return; /* Invalid source address - drop silently */
    1087           1 :                 buf = gas_build_initial_resp(
    1088             :                         dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
    1089           1 :                         0, 2 + slen + 2);
    1090           1 :                 if (buf == NULL)
    1091           0 :                         return;
    1092           1 :                 wpabuf_put_data(buf, adv_proto, 2 + slen);
    1093           1 :                 wpabuf_put_le16(buf, 0); /* Query Response Length */
    1094           1 :                 if (prot)
    1095           0 :                         convert_to_protected_dual(buf);
    1096           2 :                 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
    1097           1 :                                         wpabuf_head(buf), wpabuf_len(buf));
    1098           1 :                 wpabuf_free(buf);
    1099           1 :                 return;
    1100             :         }
    1101             : 
    1102         208 :         pos = next;
    1103             :         /* Query Request */
    1104         208 :         if (pos + 2 > end)
    1105           0 :                 return;
    1106         208 :         slen = WPA_GET_LE16(pos);
    1107         208 :         pos += 2;
    1108         208 :         if (pos + slen > end)
    1109           0 :                 return;
    1110         208 :         end = pos + slen;
    1111             : 
    1112             :         /* ANQP Query Request */
    1113         800 :         while (pos < end) {
    1114             :                 u16 info_id, elen;
    1115             : 
    1116         384 :                 if (pos + 4 > end)
    1117           0 :                         return;
    1118             : 
    1119         384 :                 info_id = WPA_GET_LE16(pos);
    1120         384 :                 pos += 2;
    1121         384 :                 elen = WPA_GET_LE16(pos);
    1122         384 :                 pos += 2;
    1123             : 
    1124         384 :                 if (pos + elen > end) {
    1125           0 :                         wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
    1126           0 :                         return;
    1127             :                 }
    1128             : 
    1129         384 :                 switch (info_id) {
    1130             :                 case ANQP_QUERY_LIST:
    1131         198 :                         rx_anqp_query_list(hapd, pos, pos + elen, &qi);
    1132         198 :                         break;
    1133             : #ifdef CONFIG_HS20
    1134             :                 case ANQP_VENDOR_SPECIFIC:
    1135         186 :                         rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
    1136         186 :                         break;
    1137             : #endif /* CONFIG_HS20 */
    1138             :                 default:
    1139           0 :                         wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
    1140             :                                    "Request element %u", info_id);
    1141           0 :                         break;
    1142             :                 }
    1143             : 
    1144         384 :                 pos += elen;
    1145             :         }
    1146             : 
    1147         208 :         gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
    1148             : }
    1149             : 
    1150             : 
    1151          38 : static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
    1152             :                                          const u8 *sa,
    1153             :                                          const u8 *data, size_t len, int prot)
    1154             : {
    1155             :         struct gas_dialog_info *dialog;
    1156             :         struct wpabuf *buf, *tx_buf;
    1157             :         u8 dialog_token;
    1158             :         size_t frag_len;
    1159          38 :         int more = 0;
    1160             : 
    1161          38 :         wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
    1162          38 :         if (len < 1)
    1163           0 :                 return;
    1164          38 :         dialog_token = *data;
    1165          38 :         wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
    1166             :                 dialog_token);
    1167             : 
    1168          38 :         dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
    1169          38 :         if (!dialog) {
    1170           7 :                 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
    1171             :                         "response fragment for " MACSTR " dialog token %u",
    1172           6 :                         MAC2STR(sa), dialog_token);
    1173             : 
    1174           1 :                 if (sa[0] & 0x01)
    1175           0 :                         return; /* Invalid source address - drop silently */
    1176           1 :                 tx_buf = gas_anqp_build_comeback_resp_buf(
    1177             :                         dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
    1178             :                         0, NULL);
    1179           1 :                 if (tx_buf == NULL)
    1180           0 :                         return;
    1181           1 :                 goto send_resp;
    1182             :         }
    1183             : 
    1184          37 :         frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
    1185          37 :         if (frag_len > hapd->gas_frag_limit) {
    1186          30 :                 frag_len = hapd->gas_frag_limit;
    1187          30 :                 more = 1;
    1188             :         }
    1189          37 :         wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
    1190             :                 (unsigned int) frag_len);
    1191          37 :         buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
    1192             :                                 dialog->sd_resp_pos, frag_len);
    1193          37 :         if (buf == NULL) {
    1194           0 :                 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
    1195             :                         "buffer");
    1196           0 :                 gas_serv_dialog_clear(dialog);
    1197           0 :                 return;
    1198             :         }
    1199          74 :         tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
    1200             :                                                   WLAN_STATUS_SUCCESS,
    1201          37 :                                                   dialog->sd_frag_id,
    1202             :                                                   more, 0, buf);
    1203          37 :         wpabuf_free(buf);
    1204          37 :         if (tx_buf == NULL) {
    1205           1 :                 gas_serv_dialog_clear(dialog);
    1206           1 :                 return;
    1207             :         }
    1208          72 :         wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
    1209             :                 "(frag_id %d more=%d frag_len=%d)",
    1210          36 :                 dialog->sd_frag_id, more, (int) frag_len);
    1211          36 :         dialog->sd_frag_id++;
    1212          36 :         dialog->sd_resp_pos += frag_len;
    1213             : 
    1214          36 :         if (more) {
    1215          58 :                 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
    1216             :                         "to be sent",
    1217          29 :                         (int) (wpabuf_len(dialog->sd_resp) -
    1218          29 :                                dialog->sd_resp_pos));
    1219             :         } else {
    1220           7 :                 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
    1221             :                         "SD response sent");
    1222           7 :                 gas_serv_dialog_clear(dialog);
    1223           7 :                 gas_serv_free_dialogs(hapd, sa);
    1224             :         }
    1225             : 
    1226             : send_resp:
    1227          37 :         if (prot)
    1228           0 :                 convert_to_protected_dual(tx_buf);
    1229          74 :         hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
    1230          37 :                                 wpabuf_head(tx_buf), wpabuf_len(tx_buf));
    1231          37 :         wpabuf_free(tx_buf);
    1232             : }
    1233             : 
    1234             : 
    1235         335 : static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
    1236             :                                       int freq)
    1237             : {
    1238         335 :         struct hostapd_data *hapd = ctx;
    1239             :         const struct ieee80211_mgmt *mgmt;
    1240             :         const u8 *sa, *data;
    1241             :         int prot;
    1242             : 
    1243         335 :         mgmt = (const struct ieee80211_mgmt *) buf;
    1244         335 :         if (len < IEEE80211_HDRLEN + 2)
    1245           0 :                 return;
    1246         336 :         if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
    1247           1 :             mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
    1248           0 :                 return;
    1249             :         /*
    1250             :          * Note: Public Action and Protected Dual of Public Action frames share
    1251             :          * the same payload structure, so it is fine to use definitions of
    1252             :          * Public Action frames to process both.
    1253             :          */
    1254         335 :         prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
    1255         335 :         sa = mgmt->sa;
    1256         335 :         len -= IEEE80211_HDRLEN + 1;
    1257         335 :         data = buf + IEEE80211_HDRLEN + 1;
    1258         335 :         switch (data[0]) {
    1259             :         case WLAN_PA_GAS_INITIAL_REQ:
    1260         210 :                 gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
    1261         210 :                 break;
    1262             :         case WLAN_PA_GAS_COMEBACK_REQ:
    1263          38 :                 gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
    1264          38 :                 break;
    1265             :         }
    1266             : }
    1267             : 
    1268             : 
    1269        2047 : int gas_serv_init(struct hostapd_data *hapd)
    1270             : {
    1271        2047 :         hapd->public_action_cb2 = gas_serv_rx_public_action;
    1272        2047 :         hapd->public_action_cb2_ctx = hapd;
    1273        2047 :         hapd->gas_frag_limit = 1400;
    1274        2047 :         if (hapd->conf->gas_frag_limit > 0)
    1275           0 :                 hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
    1276        2047 :         return 0;
    1277             : }
    1278             : 
    1279             : 
    1280        2055 : void gas_serv_deinit(struct hostapd_data *hapd)
    1281             : {
    1282        2055 : }

Generated by: LCOV version 1.10