1f05cddf9SRui Paulo /* 2f05cddf9SRui Paulo * hostapd / IEEE 802.11 Management 3f05cddf9SRui Paulo * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> 4f05cddf9SRui Paulo * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 7f05cddf9SRui Paulo */ 8f05cddf9SRui Paulo 9f05cddf9SRui Paulo #include "utils/includes.h" 10f05cddf9SRui Paulo 11f05cddf9SRui Paulo #include "utils/common.h" 12f05cddf9SRui Paulo #include "common/ieee802_11_defs.h" 134bc52338SCy Schubert #include "common/ocv.h" 14*c1d255d3SCy Schubert #include "common/wpa_ctrl.h" 15f05cddf9SRui Paulo #include "hostapd.h" 16f05cddf9SRui Paulo #include "sta_info.h" 17f05cddf9SRui Paulo #include "ap_config.h" 18f05cddf9SRui Paulo #include "ap_drv_ops.h" 194bc52338SCy Schubert #include "wpa_auth.h" 20f05cddf9SRui Paulo #include "ieee802_11.h" 21f05cddf9SRui Paulo 22f05cddf9SRui Paulo 23f05cddf9SRui Paulo u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, 24f05cddf9SRui Paulo struct sta_info *sta, u8 *eid) 25f05cddf9SRui Paulo { 26f05cddf9SRui Paulo u8 *pos = eid; 27f05cddf9SRui Paulo u32 timeout, tu; 285b9c547cSRui Paulo struct os_reltime now, passed; 29f05cddf9SRui Paulo 30f05cddf9SRui Paulo *pos++ = WLAN_EID_TIMEOUT_INTERVAL; 31f05cddf9SRui Paulo *pos++ = 5; 32f05cddf9SRui Paulo *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; 335b9c547cSRui Paulo os_get_reltime(&now); 345b9c547cSRui Paulo os_reltime_sub(&now, &sta->sa_query_start, &passed); 35f05cddf9SRui Paulo tu = (passed.sec * 1000000 + passed.usec) / 1024; 36f05cddf9SRui Paulo if (hapd->conf->assoc_sa_query_max_timeout > tu) 37f05cddf9SRui Paulo timeout = hapd->conf->assoc_sa_query_max_timeout - tu; 38f05cddf9SRui Paulo else 39f05cddf9SRui Paulo timeout = 0; 40f05cddf9SRui Paulo if (timeout < hapd->conf->assoc_sa_query_max_timeout) 41f05cddf9SRui Paulo timeout++; /* add some extra time for local timers */ 42f05cddf9SRui Paulo WPA_PUT_LE32(pos, timeout); 43f05cddf9SRui Paulo pos += 4; 44f05cddf9SRui Paulo 45f05cddf9SRui Paulo return pos; 46f05cddf9SRui Paulo } 47f05cddf9SRui Paulo 48f05cddf9SRui Paulo 49f05cddf9SRui Paulo /* MLME-SAQuery.request */ 50f05cddf9SRui Paulo void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, 51f05cddf9SRui Paulo const u8 *addr, const u8 *trans_id) 52f05cddf9SRui Paulo { 534bc52338SCy Schubert #ifdef CONFIG_OCV 544bc52338SCy Schubert struct sta_info *sta; 554bc52338SCy Schubert #endif /* CONFIG_OCV */ 564bc52338SCy Schubert struct ieee80211_mgmt *mgmt; 574bc52338SCy Schubert u8 *oci_ie = NULL; 584bc52338SCy Schubert u8 oci_ie_len = 0; 59f05cddf9SRui Paulo u8 *end; 60f05cddf9SRui Paulo 61f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " 62f05cddf9SRui Paulo MACSTR, MAC2STR(addr)); 63f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", 64f05cddf9SRui Paulo trans_id, WLAN_SA_QUERY_TR_ID_LEN); 65f05cddf9SRui Paulo 664bc52338SCy Schubert #ifdef CONFIG_OCV 674bc52338SCy Schubert sta = ap_get_sta(hapd, addr); 684bc52338SCy Schubert if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { 694bc52338SCy Schubert struct wpa_channel_info ci; 704bc52338SCy Schubert 714bc52338SCy Schubert if (hostapd_drv_channel_info(hapd, &ci) != 0) { 724bc52338SCy Schubert wpa_printf(MSG_WARNING, 734bc52338SCy Schubert "Failed to get channel info for OCI element in SA Query Request"); 744bc52338SCy Schubert return; 754bc52338SCy Schubert } 76*c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 77*c1d255d3SCy Schubert if (hapd->conf->oci_freq_override_saquery_req) { 78*c1d255d3SCy Schubert wpa_printf(MSG_INFO, 79*c1d255d3SCy Schubert "TEST: Override OCI frequency %d -> %u MHz", 80*c1d255d3SCy Schubert ci.frequency, 81*c1d255d3SCy Schubert hapd->conf->oci_freq_override_saquery_req); 82*c1d255d3SCy Schubert ci.frequency = 83*c1d255d3SCy Schubert hapd->conf->oci_freq_override_saquery_req; 84*c1d255d3SCy Schubert } 85*c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 864bc52338SCy Schubert 874bc52338SCy Schubert oci_ie_len = OCV_OCI_EXTENDED_LEN; 884bc52338SCy Schubert oci_ie = os_zalloc(oci_ie_len); 894bc52338SCy Schubert if (!oci_ie) { 904bc52338SCy Schubert wpa_printf(MSG_WARNING, 914bc52338SCy Schubert "Failed to allocate buffer for OCI element in SA Query Request"); 924bc52338SCy Schubert return; 934bc52338SCy Schubert } 944bc52338SCy Schubert 954bc52338SCy Schubert if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { 964bc52338SCy Schubert os_free(oci_ie); 974bc52338SCy Schubert return; 984bc52338SCy Schubert } 994bc52338SCy Schubert } 1004bc52338SCy Schubert #endif /* CONFIG_OCV */ 1014bc52338SCy Schubert 1024bc52338SCy Schubert mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len); 1034bc52338SCy Schubert if (!mgmt) { 1044bc52338SCy Schubert wpa_printf(MSG_DEBUG, 1054bc52338SCy Schubert "Failed to allocate buffer for SA Query Response frame"); 1064bc52338SCy Schubert os_free(oci_ie); 1074bc52338SCy Schubert return; 1084bc52338SCy Schubert } 1094bc52338SCy Schubert 1104bc52338SCy Schubert mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 111f05cddf9SRui Paulo WLAN_FC_STYPE_ACTION); 1124bc52338SCy Schubert os_memcpy(mgmt->da, addr, ETH_ALEN); 1134bc52338SCy Schubert os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 1144bc52338SCy Schubert os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 1154bc52338SCy Schubert mgmt->u.action.category = WLAN_ACTION_SA_QUERY; 1164bc52338SCy Schubert mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; 1174bc52338SCy Schubert os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id, 118f05cddf9SRui Paulo WLAN_SA_QUERY_TR_ID_LEN); 1194bc52338SCy Schubert end = mgmt->u.action.u.sa_query_req.variable; 1204bc52338SCy Schubert #ifdef CONFIG_OCV 1214bc52338SCy Schubert if (oci_ie_len > 0) { 1224bc52338SCy Schubert os_memcpy(end, oci_ie, oci_ie_len); 1234bc52338SCy Schubert end += oci_ie_len; 1244bc52338SCy Schubert } 1254bc52338SCy Schubert #endif /* CONFIG_OCV */ 126*c1d255d3SCy Schubert if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0, NULL, 0, 0) 127*c1d255d3SCy Schubert < 0) 1285b9c547cSRui Paulo wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); 1294bc52338SCy Schubert 1304bc52338SCy Schubert os_free(mgmt); 1314bc52338SCy Schubert os_free(oci_ie); 132f05cddf9SRui Paulo } 133f05cddf9SRui Paulo 134f05cddf9SRui Paulo 135f05cddf9SRui Paulo static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, 136f05cddf9SRui Paulo const u8 *sa, const u8 *trans_id) 137f05cddf9SRui Paulo { 138f05cddf9SRui Paulo struct sta_info *sta; 1394bc52338SCy Schubert struct ieee80211_mgmt *resp; 1404bc52338SCy Schubert u8 *oci_ie = NULL; 1414bc52338SCy Schubert u8 oci_ie_len = 0; 142f05cddf9SRui Paulo u8 *end; 143f05cddf9SRui Paulo 144f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " 145f05cddf9SRui Paulo MACSTR, MAC2STR(sa)); 146f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", 147f05cddf9SRui Paulo trans_id, WLAN_SA_QUERY_TR_ID_LEN); 148f05cddf9SRui Paulo 149f05cddf9SRui Paulo sta = ap_get_sta(hapd, sa); 150f05cddf9SRui Paulo if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { 151f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " 152f05cddf9SRui Paulo "from unassociated STA " MACSTR, MAC2STR(sa)); 153f05cddf9SRui Paulo return; 154f05cddf9SRui Paulo } 155f05cddf9SRui Paulo 1564bc52338SCy Schubert #ifdef CONFIG_OCV 1574bc52338SCy Schubert if (wpa_auth_uses_ocv(sta->wpa_sm)) { 1584bc52338SCy Schubert struct wpa_channel_info ci; 1594bc52338SCy Schubert 1604bc52338SCy Schubert if (hostapd_drv_channel_info(hapd, &ci) != 0) { 1614bc52338SCy Schubert wpa_printf(MSG_WARNING, 1624bc52338SCy Schubert "Failed to get channel info for OCI element in SA Query Response"); 1634bc52338SCy Schubert return; 1644bc52338SCy Schubert } 165*c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 166*c1d255d3SCy Schubert if (hapd->conf->oci_freq_override_saquery_resp) { 167*c1d255d3SCy Schubert wpa_printf(MSG_INFO, 168*c1d255d3SCy Schubert "TEST: Override OCI frequency %d -> %u MHz", 169*c1d255d3SCy Schubert ci.frequency, 170*c1d255d3SCy Schubert hapd->conf->oci_freq_override_saquery_resp); 171*c1d255d3SCy Schubert ci.frequency = 172*c1d255d3SCy Schubert hapd->conf->oci_freq_override_saquery_resp; 173*c1d255d3SCy Schubert } 174*c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 1754bc52338SCy Schubert 1764bc52338SCy Schubert oci_ie_len = OCV_OCI_EXTENDED_LEN; 1774bc52338SCy Schubert oci_ie = os_zalloc(oci_ie_len); 1784bc52338SCy Schubert if (!oci_ie) { 1794bc52338SCy Schubert wpa_printf(MSG_WARNING, 1804bc52338SCy Schubert "Failed to allocate buffer for for OCI element in SA Query Response"); 1814bc52338SCy Schubert return; 1824bc52338SCy Schubert } 1834bc52338SCy Schubert 1844bc52338SCy Schubert if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { 1854bc52338SCy Schubert os_free(oci_ie); 1864bc52338SCy Schubert return; 1874bc52338SCy Schubert } 1884bc52338SCy Schubert } 1894bc52338SCy Schubert #endif /* CONFIG_OCV */ 1904bc52338SCy Schubert 1914bc52338SCy Schubert resp = os_zalloc(sizeof(*resp) + oci_ie_len); 1924bc52338SCy Schubert if (!resp) { 1934bc52338SCy Schubert wpa_printf(MSG_DEBUG, 1944bc52338SCy Schubert "Failed to allocate buffer for SA Query Response frame"); 1954bc52338SCy Schubert os_free(oci_ie); 1964bc52338SCy Schubert return; 1974bc52338SCy Schubert } 1984bc52338SCy Schubert 199f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " 200f05cddf9SRui Paulo MACSTR, MAC2STR(sa)); 201f05cddf9SRui Paulo 2024bc52338SCy Schubert resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 203f05cddf9SRui Paulo WLAN_FC_STYPE_ACTION); 2044bc52338SCy Schubert os_memcpy(resp->da, sa, ETH_ALEN); 2054bc52338SCy Schubert os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); 2064bc52338SCy Schubert os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); 2074bc52338SCy Schubert resp->u.action.category = WLAN_ACTION_SA_QUERY; 2084bc52338SCy Schubert resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; 2094bc52338SCy Schubert os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id, 210f05cddf9SRui Paulo WLAN_SA_QUERY_TR_ID_LEN); 2114bc52338SCy Schubert end = resp->u.action.u.sa_query_req.variable; 2124bc52338SCy Schubert #ifdef CONFIG_OCV 2134bc52338SCy Schubert if (oci_ie_len > 0) { 2144bc52338SCy Schubert os_memcpy(end, oci_ie, oci_ie_len); 2154bc52338SCy Schubert end += oci_ie_len; 2164bc52338SCy Schubert } 2174bc52338SCy Schubert #endif /* CONFIG_OCV */ 218*c1d255d3SCy Schubert if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0, NULL, 0, 0) 219*c1d255d3SCy Schubert < 0) 2205b9c547cSRui Paulo wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); 2214bc52338SCy Schubert 2224bc52338SCy Schubert os_free(resp); 2234bc52338SCy Schubert os_free(oci_ie); 224f05cddf9SRui Paulo } 225f05cddf9SRui Paulo 226f05cddf9SRui Paulo 2274bc52338SCy Schubert void ieee802_11_sa_query_action(struct hostapd_data *hapd, 2284bc52338SCy Schubert const struct ieee80211_mgmt *mgmt, 2294bc52338SCy Schubert size_t len) 230f05cddf9SRui Paulo { 231f05cddf9SRui Paulo struct sta_info *sta; 232f05cddf9SRui Paulo int i; 2334bc52338SCy Schubert const u8 *sa = mgmt->sa; 2344bc52338SCy Schubert const u8 action_type = mgmt->u.action.u.sa_query_resp.action; 2354bc52338SCy Schubert const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id; 2364bc52338SCy Schubert 2374bc52338SCy Schubert if (((const u8 *) mgmt) + len < 2384bc52338SCy Schubert mgmt->u.action.u.sa_query_resp.variable) { 2394bc52338SCy Schubert wpa_printf(MSG_DEBUG, 2404bc52338SCy Schubert "IEEE 802.11: Too short SA Query Action frame (len=%lu)", 2414bc52338SCy Schubert (unsigned long) len); 2424bc52338SCy Schubert return; 2434bc52338SCy Schubert } 244*c1d255d3SCy Schubert if (is_multicast_ether_addr(mgmt->da)) { 245*c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 246*c1d255d3SCy Schubert "IEEE 802.11: Ignore group-addressed SA Query frame (A1=" MACSTR " A2=" MACSTR ")", 247*c1d255d3SCy Schubert MAC2STR(mgmt->da), MAC2STR(mgmt->sa)); 248*c1d255d3SCy Schubert return; 249*c1d255d3SCy Schubert } 2504bc52338SCy Schubert 2514bc52338SCy Schubert sta = ap_get_sta(hapd, sa); 2524bc52338SCy Schubert 2534bc52338SCy Schubert #ifdef CONFIG_OCV 2544bc52338SCy Schubert if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { 2554bc52338SCy Schubert struct ieee802_11_elems elems; 2564bc52338SCy Schubert struct wpa_channel_info ci; 2574bc52338SCy Schubert int tx_chanwidth; 2584bc52338SCy Schubert int tx_seg1_idx; 2594bc52338SCy Schubert size_t ies_len; 2604bc52338SCy Schubert const u8 *ies; 2614bc52338SCy Schubert 2624bc52338SCy Schubert ies = mgmt->u.action.u.sa_query_resp.variable; 2634bc52338SCy Schubert ies_len = len - (ies - (u8 *) mgmt); 2644bc52338SCy Schubert if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == 2654bc52338SCy Schubert ParseFailed) { 2664bc52338SCy Schubert wpa_printf(MSG_DEBUG, 2674bc52338SCy Schubert "SA Query: Failed to parse elements"); 2684bc52338SCy Schubert return; 2694bc52338SCy Schubert } 2704bc52338SCy Schubert 2714bc52338SCy Schubert if (hostapd_drv_channel_info(hapd, &ci) != 0) { 2724bc52338SCy Schubert wpa_printf(MSG_WARNING, 2734bc52338SCy Schubert "Failed to get channel info to validate received OCI in SA Query Action frame"); 2744bc52338SCy Schubert return; 2754bc52338SCy Schubert } 2764bc52338SCy Schubert 2774bc52338SCy Schubert if (get_sta_tx_parameters(sta->wpa_sm, 2784bc52338SCy Schubert channel_width_to_int(ci.chanwidth), 2794bc52338SCy Schubert ci.seg1_idx, &tx_chanwidth, 2804bc52338SCy Schubert &tx_seg1_idx) < 0) 2814bc52338SCy Schubert return; 2824bc52338SCy Schubert 2834bc52338SCy Schubert if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, 284*c1d255d3SCy Schubert tx_chanwidth, tx_seg1_idx) != 285*c1d255d3SCy Schubert OCI_SUCCESS) { 286*c1d255d3SCy Schubert wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" 287*c1d255d3SCy Schubert MACSTR " frame=saquery%s error=%s", 288*c1d255d3SCy Schubert MAC2STR(sa), 289*c1d255d3SCy Schubert action_type == WLAN_SA_QUERY_REQUEST ? 290*c1d255d3SCy Schubert "req" : "resp", ocv_errorstr); 2914bc52338SCy Schubert return; 2924bc52338SCy Schubert } 2934bc52338SCy Schubert } 2944bc52338SCy Schubert #endif /* CONFIG_OCV */ 295f05cddf9SRui Paulo 296f05cddf9SRui Paulo if (action_type == WLAN_SA_QUERY_REQUEST) { 297*c1d255d3SCy Schubert if (sta) 298*c1d255d3SCy Schubert sta->post_csa_sa_query = 0; 299f05cddf9SRui Paulo ieee802_11_send_sa_query_resp(hapd, sa, trans_id); 300f05cddf9SRui Paulo return; 301f05cddf9SRui Paulo } 302f05cddf9SRui Paulo 303f05cddf9SRui Paulo if (action_type != WLAN_SA_QUERY_RESPONSE) { 304f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " 305f05cddf9SRui Paulo "Action %d", action_type); 306f05cddf9SRui Paulo return; 307f05cddf9SRui Paulo } 308f05cddf9SRui Paulo 309f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " 310f05cddf9SRui Paulo MACSTR, MAC2STR(sa)); 311f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", 312f05cddf9SRui Paulo trans_id, WLAN_SA_QUERY_TR_ID_LEN); 313f05cddf9SRui Paulo 314f05cddf9SRui Paulo /* MLME-SAQuery.confirm */ 315f05cddf9SRui Paulo 316f05cddf9SRui Paulo if (sta == NULL || sta->sa_query_trans_id == NULL) { 317f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " 318f05cddf9SRui Paulo "pending SA Query request found"); 319f05cddf9SRui Paulo return; 320f05cddf9SRui Paulo } 321f05cddf9SRui Paulo 322f05cddf9SRui Paulo for (i = 0; i < sta->sa_query_count; i++) { 323f05cddf9SRui Paulo if (os_memcmp(sta->sa_query_trans_id + 324f05cddf9SRui Paulo i * WLAN_SA_QUERY_TR_ID_LEN, 325f05cddf9SRui Paulo trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) 326f05cddf9SRui Paulo break; 327f05cddf9SRui Paulo } 328f05cddf9SRui Paulo 329f05cddf9SRui Paulo if (i >= sta->sa_query_count) { 330f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " 331f05cddf9SRui Paulo "transaction identifier found"); 332f05cddf9SRui Paulo return; 333f05cddf9SRui Paulo } 334f05cddf9SRui Paulo 335f05cddf9SRui Paulo hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, 336f05cddf9SRui Paulo HOSTAPD_LEVEL_DEBUG, 337f05cddf9SRui Paulo "Reply to pending SA Query received"); 338f05cddf9SRui Paulo ap_sta_stop_sa_query(hapd, sta); 339f05cddf9SRui Paulo } 340f05cddf9SRui Paulo 341f05cddf9SRui Paulo 3425b9c547cSRui Paulo static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) 343f05cddf9SRui Paulo { 344f05cddf9SRui Paulo *pos = 0x00; 3455b9c547cSRui Paulo 3465b9c547cSRui Paulo switch (idx) { 3475b9c547cSRui Paulo case 0: /* Bits 0-7 */ 3485b9c547cSRui Paulo if (hapd->iconf->obss_interval) 3495b9c547cSRui Paulo *pos |= 0x01; /* Bit 0 - Coexistence management */ 350780fb4a2SCy Schubert if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) 351780fb4a2SCy Schubert *pos |= 0x04; /* Bit 2 - Extended Channel Switching */ 3525b9c547cSRui Paulo break; 3535b9c547cSRui Paulo case 1: /* Bits 8-15 */ 3545b9c547cSRui Paulo if (hapd->conf->proxy_arp) 3555b9c547cSRui Paulo *pos |= 0x10; /* Bit 12 - Proxy ARP */ 35685732ac8SCy Schubert if (hapd->conf->coloc_intf_reporting) { 35785732ac8SCy Schubert /* Bit 13 - Collocated Interference Reporting */ 35885732ac8SCy Schubert *pos |= 0x20; 35985732ac8SCy Schubert } 3605b9c547cSRui Paulo break; 3615b9c547cSRui Paulo case 2: /* Bits 16-23 */ 362f05cddf9SRui Paulo if (hapd->conf->wnm_sleep_mode) 363f05cddf9SRui Paulo *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ 364f05cddf9SRui Paulo if (hapd->conf->bss_transition) 365f05cddf9SRui Paulo *pos |= 0x08; /* Bit 19 - BSS Transition */ 3665b9c547cSRui Paulo break; 3675b9c547cSRui Paulo case 3: /* Bits 24-31 */ 36885732ac8SCy Schubert #ifdef CONFIG_WNM_AP 369f05cddf9SRui Paulo *pos |= 0x02; /* Bit 25 - SSID List */ 37085732ac8SCy Schubert #endif /* CONFIG_WNM_AP */ 371f05cddf9SRui Paulo if (hapd->conf->time_advertisement == 2) 372f05cddf9SRui Paulo *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ 373f05cddf9SRui Paulo if (hapd->conf->interworking) 374f05cddf9SRui Paulo *pos |= 0x80; /* Bit 31 - Interworking */ 3755b9c547cSRui Paulo break; 3765b9c547cSRui Paulo case 4: /* Bits 32-39 */ 3775b9c547cSRui Paulo if (hapd->conf->qos_map_set_len) 3785b9c547cSRui Paulo *pos |= 0x01; /* Bit 32 - QoS Map */ 379f05cddf9SRui Paulo if (hapd->conf->tdls & TDLS_PROHIBIT) 380f05cddf9SRui Paulo *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ 3815b9c547cSRui Paulo if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { 3825b9c547cSRui Paulo /* Bit 39 - TDLS Channel Switching Prohibited */ 3835b9c547cSRui Paulo *pos |= 0x80; 3845b9c547cSRui Paulo } 3855b9c547cSRui Paulo break; 3865b9c547cSRui Paulo case 5: /* Bits 40-47 */ 3875b9c547cSRui Paulo #ifdef CONFIG_HS20 3885b9c547cSRui Paulo if (hapd->conf->hs20) 3895b9c547cSRui Paulo *pos |= 0x40; /* Bit 46 - WNM-Notification */ 3905b9c547cSRui Paulo #endif /* CONFIG_HS20 */ 391780fb4a2SCy Schubert #ifdef CONFIG_MBO 392780fb4a2SCy Schubert if (hapd->conf->mbo_enabled) 393780fb4a2SCy Schubert *pos |= 0x40; /* Bit 46 - WNM-Notification */ 394780fb4a2SCy Schubert #endif /* CONFIG_MBO */ 3955b9c547cSRui Paulo break; 3965b9c547cSRui Paulo case 6: /* Bits 48-55 */ 397f05cddf9SRui Paulo if (hapd->conf->ssid.utf8_ssid) 398f05cddf9SRui Paulo *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ 3995b9c547cSRui Paulo break; 40085732ac8SCy Schubert case 7: /* Bits 56-63 */ 40185732ac8SCy Schubert break; 402780fb4a2SCy Schubert case 8: /* Bits 64-71 */ 403780fb4a2SCy Schubert if (hapd->conf->ftm_responder) 404780fb4a2SCy Schubert *pos |= 0x40; /* Bit 70 - FTM responder */ 405780fb4a2SCy Schubert if (hapd->conf->ftm_initiator) 406780fb4a2SCy Schubert *pos |= 0x80; /* Bit 71 - FTM initiator */ 407780fb4a2SCy Schubert break; 40885732ac8SCy Schubert case 9: /* Bits 72-79 */ 40985732ac8SCy Schubert #ifdef CONFIG_FILS 41085732ac8SCy Schubert if ((hapd->conf->wpa & WPA_PROTO_RSN) && 41185732ac8SCy Schubert wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) 41285732ac8SCy Schubert *pos |= 0x01; 41385732ac8SCy Schubert #endif /* CONFIG_FILS */ 414*c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX 415*c1d255d3SCy Schubert if (hapd->iconf->ieee80211ax && 416*c1d255d3SCy Schubert hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP)) 417*c1d255d3SCy Schubert *pos |= 0x40; /* Bit 78 - TWT responder */ 418*c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */ 41985732ac8SCy Schubert break; 4204bc52338SCy Schubert case 10: /* Bits 80-87 */ 4214bc52338SCy Schubert #ifdef CONFIG_SAE 4224bc52338SCy Schubert if (hapd->conf->wpa && 4234bc52338SCy Schubert wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) { 4244bc52338SCy Schubert int in_use = hostapd_sae_pw_id_in_use(hapd->conf); 4254bc52338SCy Schubert 4264bc52338SCy Schubert if (in_use) 4274bc52338SCy Schubert *pos |= 0x02; /* Bit 81 - SAE Password 4284bc52338SCy Schubert * Identifiers In Use */ 4294bc52338SCy Schubert if (in_use == 2) 4304bc52338SCy Schubert *pos |= 0x04; /* Bit 82 - SAE Password 4314bc52338SCy Schubert * Identifiers Used Exclusively */ 4324bc52338SCy Schubert } 4334bc52338SCy Schubert #endif /* CONFIG_SAE */ 434*c1d255d3SCy Schubert if (hapd->conf->beacon_prot && 435*c1d255d3SCy Schubert (hapd->iface->drv_flags & 436*c1d255d3SCy Schubert WPA_DRIVER_FLAGS_BEACON_PROTECTION)) 437*c1d255d3SCy Schubert *pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */ 438*c1d255d3SCy Schubert break; 439*c1d255d3SCy Schubert case 11: /* Bits 88-95 */ 440*c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 441*c1d255d3SCy Schubert if (hapd->conf->wpa && 442*c1d255d3SCy Schubert wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && 443*c1d255d3SCy Schubert hostapd_sae_pk_exclusively(hapd->conf)) 444*c1d255d3SCy Schubert *pos |= 0x01; /* Bit 88 - SAE PK Exclusively */ 445*c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 4464bc52338SCy Schubert break; 4475b9c547cSRui Paulo } 4485b9c547cSRui Paulo } 4495b9c547cSRui Paulo 4505b9c547cSRui Paulo 4515b9c547cSRui Paulo u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) 4525b9c547cSRui Paulo { 4535b9c547cSRui Paulo u8 *pos = eid; 454*c1d255d3SCy Schubert u8 len = EXT_CAPA_MAX_LEN, i; 4555b9c547cSRui Paulo 4565b9c547cSRui Paulo if (len < hapd->iface->extended_capa_len) 4575b9c547cSRui Paulo len = hapd->iface->extended_capa_len; 4585b9c547cSRui Paulo 4595b9c547cSRui Paulo *pos++ = WLAN_EID_EXT_CAPAB; 4605b9c547cSRui Paulo *pos++ = len; 4615b9c547cSRui Paulo for (i = 0; i < len; i++, pos++) { 4625b9c547cSRui Paulo hostapd_ext_capab_byte(hapd, pos, i); 4635b9c547cSRui Paulo 4645b9c547cSRui Paulo if (i < hapd->iface->extended_capa_len) { 4655b9c547cSRui Paulo *pos &= ~hapd->iface->extended_capa_mask[i]; 4665b9c547cSRui Paulo *pos |= hapd->iface->extended_capa[i]; 4675b9c547cSRui Paulo } 468*c1d255d3SCy Schubert 469*c1d255d3SCy Schubert if (i < EXT_CAPA_MAX_LEN) { 470*c1d255d3SCy Schubert *pos &= ~hapd->conf->ext_capa_mask[i]; 471*c1d255d3SCy Schubert *pos |= hapd->conf->ext_capa[i]; 472*c1d255d3SCy Schubert } 4735b9c547cSRui Paulo } 4745b9c547cSRui Paulo 4755b9c547cSRui Paulo while (len > 0 && eid[1 + len] == 0) { 4765b9c547cSRui Paulo len--; 4775b9c547cSRui Paulo eid[1] = len; 4785b9c547cSRui Paulo } 4795b9c547cSRui Paulo if (len == 0) 4805b9c547cSRui Paulo return eid; 4815b9c547cSRui Paulo 4825b9c547cSRui Paulo return eid + 2 + len; 4835b9c547cSRui Paulo } 4845b9c547cSRui Paulo 4855b9c547cSRui Paulo 4865b9c547cSRui Paulo u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) 4875b9c547cSRui Paulo { 4885b9c547cSRui Paulo u8 *pos = eid; 4895b9c547cSRui Paulo u8 len = hapd->conf->qos_map_set_len; 4905b9c547cSRui Paulo 4915b9c547cSRui Paulo if (!len) 4925b9c547cSRui Paulo return eid; 4935b9c547cSRui Paulo 4945b9c547cSRui Paulo *pos++ = WLAN_EID_QOS_MAP_SET; 4955b9c547cSRui Paulo *pos++ = len; 4965b9c547cSRui Paulo os_memcpy(pos, hapd->conf->qos_map_set, len); 4975b9c547cSRui Paulo pos += len; 498f05cddf9SRui Paulo 499f05cddf9SRui Paulo return pos; 500f05cddf9SRui Paulo } 501f05cddf9SRui Paulo 502f05cddf9SRui Paulo 503f05cddf9SRui Paulo u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) 504f05cddf9SRui Paulo { 505f05cddf9SRui Paulo u8 *pos = eid; 506f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING 507f05cddf9SRui Paulo u8 *len; 508f05cddf9SRui Paulo 509f05cddf9SRui Paulo if (!hapd->conf->interworking) 510f05cddf9SRui Paulo return eid; 511f05cddf9SRui Paulo 512f05cddf9SRui Paulo *pos++ = WLAN_EID_INTERWORKING; 513f05cddf9SRui Paulo len = pos++; 514f05cddf9SRui Paulo 515f05cddf9SRui Paulo *pos = hapd->conf->access_network_type; 516f05cddf9SRui Paulo if (hapd->conf->internet) 517f05cddf9SRui Paulo *pos |= INTERWORKING_ANO_INTERNET; 518f05cddf9SRui Paulo if (hapd->conf->asra) 519f05cddf9SRui Paulo *pos |= INTERWORKING_ANO_ASRA; 520f05cddf9SRui Paulo if (hapd->conf->esr) 521f05cddf9SRui Paulo *pos |= INTERWORKING_ANO_ESR; 522f05cddf9SRui Paulo if (hapd->conf->uesa) 523f05cddf9SRui Paulo *pos |= INTERWORKING_ANO_UESA; 524f05cddf9SRui Paulo pos++; 525f05cddf9SRui Paulo 526f05cddf9SRui Paulo if (hapd->conf->venue_info_set) { 527f05cddf9SRui Paulo *pos++ = hapd->conf->venue_group; 528f05cddf9SRui Paulo *pos++ = hapd->conf->venue_type; 529f05cddf9SRui Paulo } 530f05cddf9SRui Paulo 531f05cddf9SRui Paulo if (!is_zero_ether_addr(hapd->conf->hessid)) { 532f05cddf9SRui Paulo os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); 533f05cddf9SRui Paulo pos += ETH_ALEN; 534f05cddf9SRui Paulo } 535f05cddf9SRui Paulo 536f05cddf9SRui Paulo *len = pos - len - 1; 537f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */ 538f05cddf9SRui Paulo 539f05cddf9SRui Paulo return pos; 540f05cddf9SRui Paulo } 541f05cddf9SRui Paulo 542f05cddf9SRui Paulo 543f05cddf9SRui Paulo u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) 544f05cddf9SRui Paulo { 545f05cddf9SRui Paulo u8 *pos = eid; 546f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING 547f05cddf9SRui Paulo 548f05cddf9SRui Paulo /* TODO: Separate configuration for ANQP? */ 549f05cddf9SRui Paulo if (!hapd->conf->interworking) 550f05cddf9SRui Paulo return eid; 551f05cddf9SRui Paulo 552f05cddf9SRui Paulo *pos++ = WLAN_EID_ADV_PROTO; 553f05cddf9SRui Paulo *pos++ = 2; 554f05cddf9SRui Paulo *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ 555f05cddf9SRui Paulo *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; 556f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */ 557f05cddf9SRui Paulo 558f05cddf9SRui Paulo return pos; 559f05cddf9SRui Paulo } 560f05cddf9SRui Paulo 561f05cddf9SRui Paulo 562f05cddf9SRui Paulo u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) 563f05cddf9SRui Paulo { 564f05cddf9SRui Paulo u8 *pos = eid; 565f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING 566f05cddf9SRui Paulo u8 *len; 567f05cddf9SRui Paulo unsigned int i, count; 568f05cddf9SRui Paulo 569f05cddf9SRui Paulo if (!hapd->conf->interworking || 570f05cddf9SRui Paulo hapd->conf->roaming_consortium == NULL || 571f05cddf9SRui Paulo hapd->conf->roaming_consortium_count == 0) 572f05cddf9SRui Paulo return eid; 573f05cddf9SRui Paulo 574f05cddf9SRui Paulo *pos++ = WLAN_EID_ROAMING_CONSORTIUM; 575f05cddf9SRui Paulo len = pos++; 576f05cddf9SRui Paulo 577f05cddf9SRui Paulo /* Number of ANQP OIs (in addition to the max 3 listed here) */ 578f05cddf9SRui Paulo if (hapd->conf->roaming_consortium_count > 3 + 255) 579f05cddf9SRui Paulo *pos++ = 255; 580f05cddf9SRui Paulo else if (hapd->conf->roaming_consortium_count > 3) 581f05cddf9SRui Paulo *pos++ = hapd->conf->roaming_consortium_count - 3; 582f05cddf9SRui Paulo else 583f05cddf9SRui Paulo *pos++ = 0; 584f05cddf9SRui Paulo 585f05cddf9SRui Paulo /* OU #1 and #2 Lengths */ 586f05cddf9SRui Paulo *pos = hapd->conf->roaming_consortium[0].len; 587f05cddf9SRui Paulo if (hapd->conf->roaming_consortium_count > 1) 588f05cddf9SRui Paulo *pos |= hapd->conf->roaming_consortium[1].len << 4; 589f05cddf9SRui Paulo pos++; 590f05cddf9SRui Paulo 591f05cddf9SRui Paulo if (hapd->conf->roaming_consortium_count > 3) 592f05cddf9SRui Paulo count = 3; 593f05cddf9SRui Paulo else 594f05cddf9SRui Paulo count = hapd->conf->roaming_consortium_count; 595f05cddf9SRui Paulo 596f05cddf9SRui Paulo for (i = 0; i < count; i++) { 597f05cddf9SRui Paulo os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, 598f05cddf9SRui Paulo hapd->conf->roaming_consortium[i].len); 599f05cddf9SRui Paulo pos += hapd->conf->roaming_consortium[i].len; 600f05cddf9SRui Paulo } 601f05cddf9SRui Paulo 602f05cddf9SRui Paulo *len = pos - len - 1; 603f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */ 604f05cddf9SRui Paulo 605f05cddf9SRui Paulo return pos; 606f05cddf9SRui Paulo } 607f05cddf9SRui Paulo 608f05cddf9SRui Paulo 609f05cddf9SRui Paulo u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) 610f05cddf9SRui Paulo { 611f05cddf9SRui Paulo if (hapd->conf->time_advertisement != 2) 612f05cddf9SRui Paulo return eid; 613f05cddf9SRui Paulo 614f05cddf9SRui Paulo if (hapd->time_adv == NULL && 615f05cddf9SRui Paulo hostapd_update_time_adv(hapd) < 0) 616f05cddf9SRui Paulo return eid; 617f05cddf9SRui Paulo 618f05cddf9SRui Paulo if (hapd->time_adv == NULL) 619f05cddf9SRui Paulo return eid; 620f05cddf9SRui Paulo 621f05cddf9SRui Paulo os_memcpy(eid, wpabuf_head(hapd->time_adv), 622f05cddf9SRui Paulo wpabuf_len(hapd->time_adv)); 623f05cddf9SRui Paulo eid += wpabuf_len(hapd->time_adv); 624f05cddf9SRui Paulo 625f05cddf9SRui Paulo return eid; 626f05cddf9SRui Paulo } 627f05cddf9SRui Paulo 628f05cddf9SRui Paulo 629f05cddf9SRui Paulo u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) 630f05cddf9SRui Paulo { 631f05cddf9SRui Paulo size_t len; 632f05cddf9SRui Paulo 63385732ac8SCy Schubert if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone) 634f05cddf9SRui Paulo return eid; 635f05cddf9SRui Paulo 636f05cddf9SRui Paulo len = os_strlen(hapd->conf->time_zone); 637f05cddf9SRui Paulo 638f05cddf9SRui Paulo *eid++ = WLAN_EID_TIME_ZONE; 639f05cddf9SRui Paulo *eid++ = len; 640f05cddf9SRui Paulo os_memcpy(eid, hapd->conf->time_zone, len); 641f05cddf9SRui Paulo eid += len; 642f05cddf9SRui Paulo 643f05cddf9SRui Paulo return eid; 644f05cddf9SRui Paulo } 645f05cddf9SRui Paulo 646f05cddf9SRui Paulo 647f05cddf9SRui Paulo int hostapd_update_time_adv(struct hostapd_data *hapd) 648f05cddf9SRui Paulo { 649f05cddf9SRui Paulo const int elen = 2 + 1 + 10 + 5 + 1; 650f05cddf9SRui Paulo struct os_time t; 651f05cddf9SRui Paulo struct os_tm tm; 652f05cddf9SRui Paulo u8 *pos; 653f05cddf9SRui Paulo 654f05cddf9SRui Paulo if (hapd->conf->time_advertisement != 2) 655f05cddf9SRui Paulo return 0; 656f05cddf9SRui Paulo 657f05cddf9SRui Paulo if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) 658f05cddf9SRui Paulo return -1; 659f05cddf9SRui Paulo 660f05cddf9SRui Paulo if (!hapd->time_adv) { 661f05cddf9SRui Paulo hapd->time_adv = wpabuf_alloc(elen); 662f05cddf9SRui Paulo if (hapd->time_adv == NULL) 663f05cddf9SRui Paulo return -1; 664f05cddf9SRui Paulo pos = wpabuf_put(hapd->time_adv, elen); 665f05cddf9SRui Paulo } else 666f05cddf9SRui Paulo pos = wpabuf_mhead_u8(hapd->time_adv); 667f05cddf9SRui Paulo 668f05cddf9SRui Paulo *pos++ = WLAN_EID_TIME_ADVERTISEMENT; 669f05cddf9SRui Paulo *pos++ = 1 + 10 + 5 + 1; 670f05cddf9SRui Paulo 671f05cddf9SRui Paulo *pos++ = 2; /* UTC time at which the TSF timer is 0 */ 672f05cddf9SRui Paulo 673f05cddf9SRui Paulo /* Time Value at TSF 0 */ 674f05cddf9SRui Paulo /* FIX: need to calculate this based on the current TSF value */ 675f05cddf9SRui Paulo WPA_PUT_LE16(pos, tm.year); /* Year */ 676f05cddf9SRui Paulo pos += 2; 677f05cddf9SRui Paulo *pos++ = tm.month; /* Month */ 678f05cddf9SRui Paulo *pos++ = tm.day; /* Day of month */ 679f05cddf9SRui Paulo *pos++ = tm.hour; /* Hours */ 680f05cddf9SRui Paulo *pos++ = tm.min; /* Minutes */ 681f05cddf9SRui Paulo *pos++ = tm.sec; /* Seconds */ 682f05cddf9SRui Paulo WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ 683f05cddf9SRui Paulo pos += 2; 684f05cddf9SRui Paulo *pos++ = 0; /* Reserved */ 685f05cddf9SRui Paulo 686f05cddf9SRui Paulo /* Time Error */ 687f05cddf9SRui Paulo /* TODO: fill in an estimate on the error */ 688f05cddf9SRui Paulo *pos++ = 0; 689f05cddf9SRui Paulo *pos++ = 0; 690f05cddf9SRui Paulo *pos++ = 0; 691f05cddf9SRui Paulo *pos++ = 0; 692f05cddf9SRui Paulo *pos++ = 0; 693f05cddf9SRui Paulo 694f05cddf9SRui Paulo *pos++ = hapd->time_update_counter++; 695f05cddf9SRui Paulo 696f05cddf9SRui Paulo return 0; 697f05cddf9SRui Paulo } 698f05cddf9SRui Paulo 699f05cddf9SRui Paulo 700f05cddf9SRui Paulo u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) 701f05cddf9SRui Paulo { 702f05cddf9SRui Paulo u8 *pos = eid; 703f05cddf9SRui Paulo 70485732ac8SCy Schubert #ifdef CONFIG_WNM_AP 705f05cddf9SRui Paulo if (hapd->conf->ap_max_inactivity > 0) { 706f05cddf9SRui Paulo unsigned int val; 707f05cddf9SRui Paulo *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; 708f05cddf9SRui Paulo *pos++ = 3; 709f05cddf9SRui Paulo val = hapd->conf->ap_max_inactivity; 710f05cddf9SRui Paulo if (val > 68000) 711f05cddf9SRui Paulo val = 68000; 712f05cddf9SRui Paulo val *= 1000; 713f05cddf9SRui Paulo val /= 1024; 714f05cddf9SRui Paulo if (val == 0) 715f05cddf9SRui Paulo val = 1; 716f05cddf9SRui Paulo if (val > 65535) 717f05cddf9SRui Paulo val = 65535; 718f05cddf9SRui Paulo WPA_PUT_LE16(pos, val); 719f05cddf9SRui Paulo pos += 2; 720f05cddf9SRui Paulo *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ 721f05cddf9SRui Paulo } 72285732ac8SCy Schubert #endif /* CONFIG_WNM_AP */ 723f05cddf9SRui Paulo 724f05cddf9SRui Paulo return pos; 725f05cddf9SRui Paulo } 726780fb4a2SCy Schubert 727780fb4a2SCy Schubert 728780fb4a2SCy Schubert #ifdef CONFIG_MBO 729780fb4a2SCy Schubert 7304bc52338SCy Schubert u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, 7314bc52338SCy Schubert size_t len, int delta) 7324bc52338SCy Schubert { 7334bc52338SCy Schubert u8 mbo[4]; 7344bc52338SCy Schubert 7354bc52338SCy Schubert mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT; 7364bc52338SCy Schubert mbo[1] = 2; 7374bc52338SCy Schubert /* Delta RSSI */ 7384bc52338SCy Schubert mbo[2] = delta; 7394bc52338SCy Schubert /* Retry delay */ 7404bc52338SCy Schubert mbo[3] = hapd->iconf->rssi_reject_assoc_timeout; 7414bc52338SCy Schubert 7424bc52338SCy Schubert return eid + mbo_add_ie(eid, len, mbo, 4); 7434bc52338SCy Schubert } 7444bc52338SCy Schubert 7454bc52338SCy Schubert 746780fb4a2SCy Schubert u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) 747780fb4a2SCy Schubert { 74885732ac8SCy Schubert u8 mbo[9], *mbo_pos = mbo; 749780fb4a2SCy Schubert u8 *pos = eid; 750780fb4a2SCy Schubert 75185732ac8SCy Schubert if (!hapd->conf->mbo_enabled && 75285732ac8SCy Schubert !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) 753780fb4a2SCy Schubert return eid; 754780fb4a2SCy Schubert 75585732ac8SCy Schubert if (hapd->conf->mbo_enabled) { 756780fb4a2SCy Schubert *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; 757780fb4a2SCy Schubert *mbo_pos++ = 1; 758780fb4a2SCy Schubert /* Not Cellular aware */ 759780fb4a2SCy Schubert *mbo_pos++ = 0; 76085732ac8SCy Schubert } 761780fb4a2SCy Schubert 76285732ac8SCy Schubert if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { 763780fb4a2SCy Schubert *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; 764780fb4a2SCy Schubert *mbo_pos++ = 1; 765780fb4a2SCy Schubert *mbo_pos++ = hapd->mbo_assoc_disallow; 766780fb4a2SCy Schubert } 767780fb4a2SCy Schubert 76885732ac8SCy Schubert if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { 76985732ac8SCy Schubert u8 ctrl; 77085732ac8SCy Schubert 77185732ac8SCy Schubert ctrl = OCE_RELEASE; 77285732ac8SCy Schubert if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) 77385732ac8SCy Schubert ctrl |= OCE_IS_STA_CFON; 77485732ac8SCy Schubert 77585732ac8SCy Schubert *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; 77685732ac8SCy Schubert *mbo_pos++ = 1; 77785732ac8SCy Schubert *mbo_pos++ = ctrl; 77885732ac8SCy Schubert } 77985732ac8SCy Schubert 780780fb4a2SCy Schubert pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); 781780fb4a2SCy Schubert 782780fb4a2SCy Schubert return pos; 783780fb4a2SCy Schubert } 784780fb4a2SCy Schubert 785780fb4a2SCy Schubert 786780fb4a2SCy Schubert u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) 787780fb4a2SCy Schubert { 78885732ac8SCy Schubert u8 len; 78985732ac8SCy Schubert 79085732ac8SCy Schubert if (!hapd->conf->mbo_enabled && 79185732ac8SCy Schubert !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) 792780fb4a2SCy Schubert return 0; 793780fb4a2SCy Schubert 794780fb4a2SCy Schubert /* 795780fb4a2SCy Schubert * MBO IE header (6) + Capability Indication attribute (3) + 796780fb4a2SCy Schubert * Association Disallowed attribute (3) = 12 797780fb4a2SCy Schubert */ 79885732ac8SCy Schubert len = 6; 79985732ac8SCy Schubert if (hapd->conf->mbo_enabled) 80085732ac8SCy Schubert len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); 80185732ac8SCy Schubert 80285732ac8SCy Schubert /* OCE capability indication attribute (3) */ 80385732ac8SCy Schubert if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) 80485732ac8SCy Schubert len += 3; 80585732ac8SCy Schubert 80685732ac8SCy Schubert return len; 807780fb4a2SCy Schubert } 808780fb4a2SCy Schubert 809780fb4a2SCy Schubert #endif /* CONFIG_MBO */ 810780fb4a2SCy Schubert 811780fb4a2SCy Schubert 81285732ac8SCy Schubert #ifdef CONFIG_OWE 81385732ac8SCy Schubert static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd) 81485732ac8SCy Schubert { 81585732ac8SCy Schubert return hapd->conf->owe_transition_ssid_len > 0 && 81685732ac8SCy Schubert !is_zero_ether_addr(hapd->conf->owe_transition_bssid); 81785732ac8SCy Schubert } 81885732ac8SCy Schubert #endif /* CONFIG_OWE */ 81985732ac8SCy Schubert 82085732ac8SCy Schubert 82185732ac8SCy Schubert size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd) 82285732ac8SCy Schubert { 82385732ac8SCy Schubert #ifdef CONFIG_OWE 82485732ac8SCy Schubert if (!hostapd_eid_owe_trans_enabled(hapd)) 82585732ac8SCy Schubert return 0; 82685732ac8SCy Schubert return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len; 82785732ac8SCy Schubert #else /* CONFIG_OWE */ 82885732ac8SCy Schubert return 0; 82985732ac8SCy Schubert #endif /* CONFIG_OWE */ 83085732ac8SCy Schubert } 83185732ac8SCy Schubert 83285732ac8SCy Schubert 83385732ac8SCy Schubert u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, 83485732ac8SCy Schubert size_t len) 83585732ac8SCy Schubert { 83685732ac8SCy Schubert #ifdef CONFIG_OWE 83785732ac8SCy Schubert u8 *pos = eid; 83885732ac8SCy Schubert size_t elen; 83985732ac8SCy Schubert 84085732ac8SCy Schubert if (hapd->conf->owe_transition_ifname[0] && 84185732ac8SCy Schubert !hostapd_eid_owe_trans_enabled(hapd)) 84285732ac8SCy Schubert hostapd_owe_trans_get_info(hapd); 84385732ac8SCy Schubert 84485732ac8SCy Schubert if (!hostapd_eid_owe_trans_enabled(hapd)) 84585732ac8SCy Schubert return pos; 84685732ac8SCy Schubert 84785732ac8SCy Schubert elen = hostapd_eid_owe_trans_len(hapd); 84885732ac8SCy Schubert if (len < elen) { 84985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 85085732ac8SCy Schubert "OWE: Not enough room in the buffer for OWE IE"); 85185732ac8SCy Schubert return pos; 85285732ac8SCy Schubert } 85385732ac8SCy Schubert 85485732ac8SCy Schubert *pos++ = WLAN_EID_VENDOR_SPECIFIC; 85585732ac8SCy Schubert *pos++ = elen - 2; 85685732ac8SCy Schubert WPA_PUT_BE24(pos, OUI_WFA); 85785732ac8SCy Schubert pos += 3; 85885732ac8SCy Schubert *pos++ = OWE_OUI_TYPE; 85985732ac8SCy Schubert os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN); 86085732ac8SCy Schubert pos += ETH_ALEN; 86185732ac8SCy Schubert *pos++ = hapd->conf->owe_transition_ssid_len; 86285732ac8SCy Schubert os_memcpy(pos, hapd->conf->owe_transition_ssid, 86385732ac8SCy Schubert hapd->conf->owe_transition_ssid_len); 86485732ac8SCy Schubert pos += hapd->conf->owe_transition_ssid_len; 86585732ac8SCy Schubert 86685732ac8SCy Schubert return pos; 86785732ac8SCy Schubert #else /* CONFIG_OWE */ 86885732ac8SCy Schubert return eid; 86985732ac8SCy Schubert #endif /* CONFIG_OWE */ 87085732ac8SCy Schubert } 87185732ac8SCy Schubert 87285732ac8SCy Schubert 873*c1d255d3SCy Schubert size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd) 874*c1d255d3SCy Schubert { 875*c1d255d3SCy Schubert #ifdef CONFIG_DPP2 876*c1d255d3SCy Schubert if (hapd->conf->dpp_configurator_connectivity) 877*c1d255d3SCy Schubert return 6; 878*c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 879*c1d255d3SCy Schubert return 0; 880*c1d255d3SCy Schubert } 881*c1d255d3SCy Schubert 882*c1d255d3SCy Schubert 883*c1d255d3SCy Schubert u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len) 884*c1d255d3SCy Schubert { 885*c1d255d3SCy Schubert u8 *pos = eid; 886*c1d255d3SCy Schubert 887*c1d255d3SCy Schubert #ifdef CONFIG_DPP2 888*c1d255d3SCy Schubert if (!hapd->conf->dpp_configurator_connectivity || len < 6) 889*c1d255d3SCy Schubert return pos; 890*c1d255d3SCy Schubert 891*c1d255d3SCy Schubert *pos++ = WLAN_EID_VENDOR_SPECIFIC; 892*c1d255d3SCy Schubert *pos++ = 4; 893*c1d255d3SCy Schubert WPA_PUT_BE24(pos, OUI_WFA); 894*c1d255d3SCy Schubert pos += 3; 895*c1d255d3SCy Schubert *pos++ = DPP_CC_OUI_TYPE; 896*c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 897*c1d255d3SCy Schubert 898*c1d255d3SCy Schubert return pos; 899*c1d255d3SCy Schubert } 900*c1d255d3SCy Schubert 901*c1d255d3SCy Schubert 902780fb4a2SCy Schubert void ap_copy_sta_supp_op_classes(struct sta_info *sta, 903780fb4a2SCy Schubert const u8 *supp_op_classes, 904780fb4a2SCy Schubert size_t supp_op_classes_len) 905780fb4a2SCy Schubert { 906780fb4a2SCy Schubert if (!supp_op_classes) 907780fb4a2SCy Schubert return; 908780fb4a2SCy Schubert os_free(sta->supp_op_classes); 909780fb4a2SCy Schubert sta->supp_op_classes = os_malloc(1 + supp_op_classes_len); 910780fb4a2SCy Schubert if (!sta->supp_op_classes) 911780fb4a2SCy Schubert return; 912780fb4a2SCy Schubert 913780fb4a2SCy Schubert sta->supp_op_classes[0] = supp_op_classes_len; 914780fb4a2SCy Schubert os_memcpy(sta->supp_op_classes + 1, supp_op_classes, 915780fb4a2SCy Schubert supp_op_classes_len); 916780fb4a2SCy Schubert } 91785732ac8SCy Schubert 91885732ac8SCy Schubert 91985732ac8SCy Schubert u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) 92085732ac8SCy Schubert { 92185732ac8SCy Schubert u8 *pos = eid; 92285732ac8SCy Schubert #ifdef CONFIG_FILS 92385732ac8SCy Schubert u8 *len; 92485732ac8SCy Schubert u16 fils_info = 0; 92585732ac8SCy Schubert size_t realms; 92685732ac8SCy Schubert struct fils_realm *realm; 92785732ac8SCy Schubert 92885732ac8SCy Schubert if (!(hapd->conf->wpa & WPA_PROTO_RSN) || 92985732ac8SCy Schubert !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) 93085732ac8SCy Schubert return pos; 93185732ac8SCy Schubert 93285732ac8SCy Schubert realms = dl_list_len(&hapd->conf->fils_realms); 93385732ac8SCy Schubert if (realms > 7) 93485732ac8SCy Schubert realms = 7; /* 3 bit count field limits this to max 7 */ 93585732ac8SCy Schubert 93685732ac8SCy Schubert *pos++ = WLAN_EID_FILS_INDICATION; 93785732ac8SCy Schubert len = pos++; 93885732ac8SCy Schubert /* TODO: B0..B2: Number of Public Key Identifiers */ 93985732ac8SCy Schubert if (hapd->conf->erp_domain) { 94085732ac8SCy Schubert /* B3..B5: Number of Realm Identifiers */ 94185732ac8SCy Schubert fils_info |= realms << 3; 94285732ac8SCy Schubert } 94385732ac8SCy Schubert /* TODO: B6: FILS IP Address Configuration */ 94485732ac8SCy Schubert if (hapd->conf->fils_cache_id_set) 94585732ac8SCy Schubert fils_info |= BIT(7); 94685732ac8SCy Schubert if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) 94785732ac8SCy Schubert fils_info |= BIT(8); /* HESSID Included */ 94885732ac8SCy Schubert /* FILS Shared Key Authentication without PFS Supported */ 94985732ac8SCy Schubert fils_info |= BIT(9); 95085732ac8SCy Schubert if (hapd->conf->fils_dh_group) { 95185732ac8SCy Schubert /* FILS Shared Key Authentication with PFS Supported */ 95285732ac8SCy Schubert fils_info |= BIT(10); 95385732ac8SCy Schubert } 95485732ac8SCy Schubert /* TODO: B11: FILS Public Key Authentication Supported */ 95585732ac8SCy Schubert /* B12..B15: Reserved */ 95685732ac8SCy Schubert WPA_PUT_LE16(pos, fils_info); 95785732ac8SCy Schubert pos += 2; 95885732ac8SCy Schubert if (hapd->conf->fils_cache_id_set) { 95985732ac8SCy Schubert os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN); 96085732ac8SCy Schubert pos += FILS_CACHE_ID_LEN; 96185732ac8SCy Schubert } 96285732ac8SCy Schubert if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) { 96385732ac8SCy Schubert os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); 96485732ac8SCy Schubert pos += ETH_ALEN; 96585732ac8SCy Schubert } 96685732ac8SCy Schubert 96785732ac8SCy Schubert dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, 96885732ac8SCy Schubert list) { 96985732ac8SCy Schubert if (realms == 0) 97085732ac8SCy Schubert break; 97185732ac8SCy Schubert realms--; 97285732ac8SCy Schubert os_memcpy(pos, realm->hash, 2); 97385732ac8SCy Schubert pos += 2; 97485732ac8SCy Schubert } 97585732ac8SCy Schubert *len = pos - len - 1; 97685732ac8SCy Schubert #endif /* CONFIG_FILS */ 97785732ac8SCy Schubert 97885732ac8SCy Schubert return pos; 97985732ac8SCy Schubert } 9804bc52338SCy Schubert 9814bc52338SCy Schubert 9824bc52338SCy Schubert #ifdef CONFIG_OCV 9834bc52338SCy Schubert int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, 9844bc52338SCy Schubert int ap_seg1_idx, int *bandwidth, int *seg1_idx) 9854bc52338SCy Schubert { 9864bc52338SCy Schubert int ht_40mhz = 0; 9874bc52338SCy Schubert int vht_80p80 = 0; 9884bc52338SCy Schubert int requested_bw; 9894bc52338SCy Schubert 9904bc52338SCy Schubert if (sta->ht_capabilities) 9914bc52338SCy Schubert ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info & 9924bc52338SCy Schubert HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); 9934bc52338SCy Schubert 9944bc52338SCy Schubert if (sta->vht_operation) { 9954bc52338SCy Schubert struct ieee80211_vht_operation *oper = sta->vht_operation; 9964bc52338SCy Schubert 9974bc52338SCy Schubert /* 9984bc52338SCy Schubert * If a VHT Operation element was present, use it to determine 9994bc52338SCy Schubert * the supported channel bandwidth. 10004bc52338SCy Schubert */ 10014bc52338SCy Schubert if (oper->vht_op_info_chwidth == 0) { 10024bc52338SCy Schubert requested_bw = ht_40mhz ? 40 : 20; 10034bc52338SCy Schubert } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) { 10044bc52338SCy Schubert requested_bw = 80; 10054bc52338SCy Schubert } else { 10064bc52338SCy Schubert int diff; 10074bc52338SCy Schubert 10084bc52338SCy Schubert requested_bw = 160; 10094bc52338SCy Schubert diff = abs((int) 10104bc52338SCy Schubert oper->vht_op_info_chan_center_freq_seg0_idx - 10114bc52338SCy Schubert (int) 10124bc52338SCy Schubert oper->vht_op_info_chan_center_freq_seg1_idx); 10134bc52338SCy Schubert vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx 10144bc52338SCy Schubert != 0 && diff > 16; 10154bc52338SCy Schubert } 10164bc52338SCy Schubert } else if (sta->vht_capabilities) { 10174bc52338SCy Schubert struct ieee80211_vht_capabilities *capab; 10184bc52338SCy Schubert int vht_chanwidth; 10194bc52338SCy Schubert 10204bc52338SCy Schubert capab = sta->vht_capabilities; 10214bc52338SCy Schubert 10224bc52338SCy Schubert /* 10234bc52338SCy Schubert * If only the VHT Capabilities element is present (e.g., for 10244bc52338SCy Schubert * normal clients), use it to determine the supported channel 10254bc52338SCy Schubert * bandwidth. 10264bc52338SCy Schubert */ 10274bc52338SCy Schubert vht_chanwidth = capab->vht_capabilities_info & 10284bc52338SCy Schubert VHT_CAP_SUPP_CHAN_WIDTH_MASK; 10294bc52338SCy Schubert vht_80p80 = capab->vht_capabilities_info & 10304bc52338SCy Schubert VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; 10314bc52338SCy Schubert 10324bc52338SCy Schubert /* TODO: Also take into account Extended NSS BW Support field */ 10334bc52338SCy Schubert requested_bw = vht_chanwidth ? 160 : 80; 10344bc52338SCy Schubert } else { 10354bc52338SCy Schubert requested_bw = ht_40mhz ? 40 : 20; 10364bc52338SCy Schubert } 10374bc52338SCy Schubert 10384bc52338SCy Schubert *bandwidth = requested_bw < ap_max_chanwidth ? 10394bc52338SCy Schubert requested_bw : ap_max_chanwidth; 10404bc52338SCy Schubert 10414bc52338SCy Schubert *seg1_idx = 0; 10424bc52338SCy Schubert if (ap_seg1_idx && vht_80p80) 10434bc52338SCy Schubert *seg1_idx = ap_seg1_idx; 10444bc52338SCy Schubert 10454bc52338SCy Schubert return 0; 10464bc52338SCy Schubert } 10474bc52338SCy Schubert #endif /* CONFIG_OCV */ 1048*c1d255d3SCy Schubert 1049*c1d255d3SCy Schubert 1050*c1d255d3SCy Schubert u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) 1051*c1d255d3SCy Schubert { 1052*c1d255d3SCy Schubert u8 *pos = eid; 1053*c1d255d3SCy Schubert bool sae_pk = false; 1054*c1d255d3SCy Schubert u16 capab = 0; 1055*c1d255d3SCy Schubert size_t flen; 1056*c1d255d3SCy Schubert 1057*c1d255d3SCy Schubert if (!(hapd->conf->wpa & WPA_PROTO_RSN)) 1058*c1d255d3SCy Schubert return eid; 1059*c1d255d3SCy Schubert 1060*c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1061*c1d255d3SCy Schubert sae_pk = hostapd_sae_pk_in_use(hapd->conf); 1062*c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1063*c1d255d3SCy Schubert 1064*c1d255d3SCy Schubert if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && 1065*c1d255d3SCy Schubert (hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 || 1066*c1d255d3SCy Schubert hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk) && 1067*c1d255d3SCy Schubert hapd->conf->sae_pwe != 3) { 1068*c1d255d3SCy Schubert capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); 1069*c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1070*c1d255d3SCy Schubert if (sae_pk) 1071*c1d255d3SCy Schubert capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK); 1072*c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1073*c1d255d3SCy Schubert } 1074*c1d255d3SCy Schubert 1075*c1d255d3SCy Schubert if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) 1076*c1d255d3SCy Schubert capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); 1077*c1d255d3SCy Schubert if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT) 1078*c1d255d3SCy Schubert capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); 1079*c1d255d3SCy Schubert if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG) 1080*c1d255d3SCy Schubert capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG); 1081*c1d255d3SCy Schubert 1082*c1d255d3SCy Schubert flen = (capab & 0xff00) ? 2 : 1; 1083*c1d255d3SCy Schubert if (len < 2 + flen || !capab) 1084*c1d255d3SCy Schubert return eid; /* no supported extended RSN capabilities */ 1085*c1d255d3SCy Schubert capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ 1086*c1d255d3SCy Schubert 1087*c1d255d3SCy Schubert *pos++ = WLAN_EID_RSNX; 1088*c1d255d3SCy Schubert *pos++ = flen; 1089*c1d255d3SCy Schubert *pos++ = capab & 0x00ff; 1090*c1d255d3SCy Schubert capab >>= 8; 1091*c1d255d3SCy Schubert if (capab) 1092*c1d255d3SCy Schubert *pos++ = capab; 1093*c1d255d3SCy Schubert 1094*c1d255d3SCy Schubert return pos; 1095*c1d255d3SCy Schubert } 1096