mlme.c revision 189251
1189251Ssam/* 2189251Ssam * WPA Supplicant - Client mode MLME 3189251Ssam * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> 4189251Ssam * Copyright (c) 2004, Instant802 Networks, Inc. 5189251Ssam * Copyright (c) 2005-2006, Devicescape Software, Inc. 6189251Ssam * 7189251Ssam * This program is free software; you can redistribute it and/or modify 8189251Ssam * it under the terms of the GNU General Public License version 2 as 9189251Ssam * published by the Free Software Foundation. 10189251Ssam * 11189251Ssam * Alternatively, this software may be distributed under the terms of BSD 12189251Ssam * license. 13189251Ssam * 14189251Ssam * See README and COPYING for more details. 15189251Ssam */ 16189251Ssam 17189251Ssam#include "includes.h" 18189251Ssam 19189251Ssam#include "common.h" 20189251Ssam#include "eloop.h" 21189251Ssam#include "config_ssid.h" 22189251Ssam#include "wpa_supplicant_i.h" 23189251Ssam#include "wpa.h" 24189251Ssam#include "drivers/driver.h" 25189251Ssam#include "ieee802_11_defs.h" 26189251Ssam#include "ieee802_11_common.h" 27189251Ssam#include "mlme.h" 28189251Ssam 29189251Ssam 30189251Ssam/* Timeouts and intervals in milliseconds */ 31189251Ssam#define IEEE80211_AUTH_TIMEOUT (200) 32189251Ssam#define IEEE80211_AUTH_MAX_TRIES 3 33189251Ssam#define IEEE80211_ASSOC_TIMEOUT (200) 34189251Ssam#define IEEE80211_ASSOC_MAX_TRIES 3 35189251Ssam#define IEEE80211_MONITORING_INTERVAL (2000) 36189251Ssam#define IEEE80211_PROBE_INTERVAL (60000) 37189251Ssam#define IEEE80211_RETRY_AUTH_INTERVAL (1000) 38189251Ssam#define IEEE80211_SCAN_INTERVAL (2000) 39189251Ssam#define IEEE80211_SCAN_INTERVAL_SLOW (15000) 40189251Ssam#define IEEE80211_IBSS_JOIN_TIMEOUT (20000) 41189251Ssam 42189251Ssam#define IEEE80211_PROBE_DELAY (33) 43189251Ssam#define IEEE80211_CHANNEL_TIME (33) 44189251Ssam#define IEEE80211_PASSIVE_CHANNEL_TIME (200) 45189251Ssam#define IEEE80211_SCAN_RESULT_EXPIRE (10000) 46189251Ssam#define IEEE80211_IBSS_MERGE_INTERVAL (30000) 47189251Ssam#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000) 48189251Ssam 49189251Ssam#define IEEE80211_IBSS_MAX_STA_ENTRIES 128 50189251Ssam 51189251Ssam 52189251Ssam#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) 53189251Ssam 54189251Ssam 55189251Ssamstruct ieee80211_sta_bss { 56189251Ssam struct ieee80211_sta_bss *next; 57189251Ssam struct ieee80211_sta_bss *hnext; 58189251Ssam 59189251Ssam u8 bssid[ETH_ALEN]; 60189251Ssam u8 ssid[MAX_SSID_LEN]; 61189251Ssam size_t ssid_len; 62189251Ssam u16 capability; /* host byte order */ 63189251Ssam int hw_mode; 64189251Ssam int channel; 65189251Ssam int freq; 66189251Ssam int rssi; 67189251Ssam u8 *ie; 68189251Ssam size_t ie_len; 69189251Ssam u8 *wpa_ie; 70189251Ssam size_t wpa_ie_len; 71189251Ssam u8 *rsn_ie; 72189251Ssam size_t rsn_ie_len; 73189251Ssam u8 *wmm_ie; 74189251Ssam size_t wmm_ie_len; 75189251Ssam u8 *mdie; 76189251Ssam size_t mdie_len; 77189251Ssam#define IEEE80211_MAX_SUPP_RATES 32 78189251Ssam u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; 79189251Ssam size_t supp_rates_len; 80189251Ssam int beacon_int; 81189251Ssam u64 timestamp; 82189251Ssam 83189251Ssam int probe_resp; 84189251Ssam struct os_time last_update; 85189251Ssam}; 86189251Ssam 87189251Ssam 88189251Ssamstatic void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, 89189251Ssam const u8 *dst, 90189251Ssam const u8 *ssid, size_t ssid_len); 91189251Ssamstatic struct ieee80211_sta_bss * 92189251Ssamieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid); 93189251Ssamstatic int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s); 94189251Ssamstatic int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s); 95189251Ssamstatic void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx); 96189251Ssamstatic void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx); 97189251Ssam 98189251Ssam 99189251Ssamstatic int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s, 100189251Ssam wpa_hw_mode phymode, int chan, 101189251Ssam int freq) 102189251Ssam{ 103189251Ssam size_t i; 104189251Ssam struct wpa_hw_modes *mode; 105189251Ssam 106189251Ssam for (i = 0; i < wpa_s->mlme.num_modes; i++) { 107189251Ssam mode = &wpa_s->mlme.modes[i]; 108189251Ssam if (mode->mode == phymode) { 109189251Ssam wpa_s->mlme.curr_rates = mode->rates; 110189251Ssam wpa_s->mlme.num_curr_rates = mode->num_rates; 111189251Ssam break; 112189251Ssam } 113189251Ssam } 114189251Ssam 115189251Ssam return wpa_drv_set_channel(wpa_s, phymode, chan, freq); 116189251Ssam} 117189251Ssam 118189251Ssam 119189251Ssam 120189251Ssam#if 0 /* FIX */ 121189251Ssamstatic int ecw2cw(int ecw) 122189251Ssam{ 123189251Ssam int cw = 1; 124189251Ssam while (ecw > 0) { 125189251Ssam cw <<= 1; 126189251Ssam ecw--; 127189251Ssam } 128189251Ssam return cw - 1; 129189251Ssam} 130189251Ssam#endif 131189251Ssam 132189251Ssam 133189251Ssamstatic void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s, 134189251Ssam u8 *wmm_param, size_t wmm_param_len) 135189251Ssam{ 136189251Ssam size_t left; 137189251Ssam int count; 138189251Ssam u8 *pos; 139189251Ssam 140189251Ssam if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) 141189251Ssam return; 142189251Ssam count = wmm_param[6] & 0x0f; 143189251Ssam if (count == wpa_s->mlme.wmm_last_param_set) 144189251Ssam return; 145189251Ssam wpa_s->mlme.wmm_last_param_set = count; 146189251Ssam 147189251Ssam pos = wmm_param + 8; 148189251Ssam left = wmm_param_len - 8; 149189251Ssam 150189251Ssam#if 0 /* FIX */ 151189251Ssam wmm_acm = 0; 152189251Ssam for (; left >= 4; left -= 4, pos += 4) { 153189251Ssam int aci = (pos[0] >> 5) & 0x03; 154189251Ssam int acm = (pos[0] >> 4) & 0x01; 155189251Ssam int queue; 156189251Ssam 157189251Ssam switch (aci) { 158189251Ssam case 1: 159189251Ssam queue = IEEE80211_TX_QUEUE_DATA3; 160189251Ssam if (acm) 161189251Ssam wmm_acm |= BIT(1) | BIT(2); 162189251Ssam break; 163189251Ssam case 2: 164189251Ssam queue = IEEE80211_TX_QUEUE_DATA1; 165189251Ssam if (acm) 166189251Ssam wmm_acm |= BIT(4) | BIT(5); 167189251Ssam break; 168189251Ssam case 3: 169189251Ssam queue = IEEE80211_TX_QUEUE_DATA0; 170189251Ssam if (acm) 171189251Ssam wmm_acm |= BIT(6) | BIT(7); 172189251Ssam break; 173189251Ssam case 0: 174189251Ssam default: 175189251Ssam queue = IEEE80211_TX_QUEUE_DATA2; 176189251Ssam if (acm) 177189251Ssam wpa_s->mlme.wmm_acm |= BIT(0) | BIT(3); 178189251Ssam break; 179189251Ssam } 180189251Ssam 181189251Ssam params.aifs = pos[0] & 0x0f; 182189251Ssam params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); 183189251Ssam params.cw_min = ecw2cw(pos[1] & 0x0f); 184189251Ssam /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ 185189251Ssam params.burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; 186189251Ssam wpa_printf(MSG_DEBUG, "MLME: WMM queue=%d aci=%d acm=%d " 187189251Ssam "aifs=%d cWmin=%d cWmax=%d burst=%d", 188189251Ssam queue, aci, acm, params.aifs, params.cw_min, 189189251Ssam params.cw_max, params.burst_time); 190189251Ssam /* TODO: handle ACM (block TX, fallback to next lowest allowed 191189251Ssam * AC for now) */ 192189251Ssam if (local->hw->conf_tx(local->mdev, queue, ¶ms)) { 193189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to set TX queue " 194189251Ssam "parameters for queue %d", queue); 195189251Ssam } 196189251Ssam } 197189251Ssam#endif 198189251Ssam} 199189251Ssam 200189251Ssam 201189251Ssamstatic void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc) 202189251Ssam{ 203189251Ssam if (wpa_s->mlme.associated == assoc && !assoc) 204189251Ssam return; 205189251Ssam 206189251Ssam wpa_s->mlme.associated = assoc; 207189251Ssam 208189251Ssam if (assoc) { 209189251Ssam union wpa_event_data data; 210189251Ssam os_memset(&data, 0, sizeof(data)); 211189251Ssam wpa_s->mlme.prev_bssid_set = 1; 212189251Ssam os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); 213189251Ssam data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies; 214189251Ssam data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len; 215189251Ssam data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies; 216189251Ssam data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len; 217189251Ssam wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); 218189251Ssam } else { 219189251Ssam wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); 220189251Ssam } 221189251Ssam os_get_time(&wpa_s->mlme.last_probe); 222189251Ssam} 223189251Ssam 224189251Ssam 225189251Ssamstatic int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf, 226189251Ssam size_t len) 227189251Ssam{ 228189251Ssam return wpa_drv_send_mlme(wpa_s, buf, len); 229189251Ssam} 230189251Ssam 231189251Ssam 232189251Ssamstatic void ieee80211_send_auth(struct wpa_supplicant *wpa_s, 233189251Ssam int transaction, u8 *extra, size_t extra_len, 234189251Ssam int encrypt) 235189251Ssam{ 236189251Ssam u8 *buf; 237189251Ssam size_t len; 238189251Ssam struct ieee80211_mgmt *mgmt; 239189251Ssam 240189251Ssam buf = os_malloc(sizeof(*mgmt) + 6 + extra_len); 241189251Ssam if (buf == NULL) { 242189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " 243189251Ssam "auth frame"); 244189251Ssam return; 245189251Ssam } 246189251Ssam 247189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 248189251Ssam len = 24 + 6; 249189251Ssam os_memset(mgmt, 0, 24 + 6); 250189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 251189251Ssam WLAN_FC_STYPE_AUTH); 252189251Ssam if (encrypt) 253189251Ssam mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP); 254189251Ssam os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 255189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 256189251Ssam os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 257189251Ssam mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg); 258189251Ssam mgmt->u.auth.auth_transaction = host_to_le16(transaction); 259189251Ssam wpa_s->mlme.auth_transaction = transaction + 1; 260189251Ssam mgmt->u.auth.status_code = host_to_le16(0); 261189251Ssam if (extra) { 262189251Ssam os_memcpy(buf + len, extra, extra_len); 263189251Ssam len += extra_len; 264189251Ssam } 265189251Ssam 266189251Ssam ieee80211_sta_tx(wpa_s, buf, len); 267189251Ssam os_free(buf); 268189251Ssam} 269189251Ssam 270189251Ssam 271189251Ssamstatic void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms) 272189251Ssam{ 273189251Ssam eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); 274189251Ssam eloop_register_timeout(ms / 1000, 1000 * (ms % 1000), 275189251Ssam ieee80211_sta_timer, wpa_s, NULL); 276189251Ssam} 277189251Ssam 278189251Ssam 279189251Ssamstatic void ieee80211_authenticate(struct wpa_supplicant *wpa_s) 280189251Ssam{ 281189251Ssam u8 *extra; 282189251Ssam size_t extra_len; 283189251Ssam 284189251Ssam wpa_s->mlme.auth_tries++; 285189251Ssam if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) { 286189251Ssam wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR 287189251Ssam " timed out", MAC2STR(wpa_s->bssid)); 288189251Ssam return; 289189251Ssam } 290189251Ssam 291189251Ssam wpa_s->mlme.state = IEEE80211_AUTHENTICATE; 292189251Ssam wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR, 293189251Ssam MAC2STR(wpa_s->bssid)); 294189251Ssam 295189251Ssam extra = NULL; 296189251Ssam extra_len = 0; 297189251Ssam 298189251Ssam#ifdef CONFIG_IEEE80211R 299189251Ssam if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || 300189251Ssam wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && 301189251Ssam wpa_s->mlme.ft_ies) { 302189251Ssam struct ieee80211_sta_bss *bss; 303189251Ssam struct rsn_mdie *mdie = NULL; 304189251Ssam bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); 305189251Ssam if (bss && bss->mdie_len >= 2 + sizeof(*mdie)) 306189251Ssam mdie = (struct rsn_mdie *) (bss->mdie + 2); 307189251Ssam if (mdie && 308189251Ssam os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md, 309189251Ssam MOBILITY_DOMAIN_ID_LEN) == 0) { 310189251Ssam wpa_printf(MSG_DEBUG, "MLME: Trying to use FT " 311189251Ssam "over-the-air"); 312189251Ssam wpa_s->mlme.auth_alg = WLAN_AUTH_FT; 313189251Ssam extra = wpa_s->mlme.ft_ies; 314189251Ssam extra_len = wpa_s->mlme.ft_ies_len; 315189251Ssam } 316189251Ssam } 317189251Ssam#endif /* CONFIG_IEEE80211R */ 318189251Ssam 319189251Ssam ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0); 320189251Ssam 321189251Ssam ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT); 322189251Ssam} 323189251Ssam 324189251Ssam 325189251Ssamstatic void ieee80211_send_assoc(struct wpa_supplicant *wpa_s) 326189251Ssam{ 327189251Ssam struct ieee80211_mgmt *mgmt; 328189251Ssam u8 *pos, *ies, *buf; 329189251Ssam int i, len; 330189251Ssam u16 capab; 331189251Ssam struct ieee80211_sta_bss *bss; 332189251Ssam int wmm = 0; 333189251Ssam size_t blen, buflen; 334189251Ssam 335189251Ssam if (wpa_s->mlme.curr_rates == NULL) { 336189251Ssam wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc"); 337189251Ssam return; 338189251Ssam } 339189251Ssam 340189251Ssam buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len + 341189251Ssam wpa_s->mlme.ssid_len; 342189251Ssam#ifdef CONFIG_IEEE80211R 343189251Ssam if (wpa_s->mlme.ft_ies) 344189251Ssam buflen += wpa_s->mlme.ft_ies_len; 345189251Ssam#endif /* CONFIG_IEEE80211R */ 346189251Ssam buf = os_malloc(buflen); 347189251Ssam if (buf == NULL) { 348189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " 349189251Ssam "assoc frame"); 350189251Ssam return; 351189251Ssam } 352189251Ssam blen = 0; 353189251Ssam 354189251Ssam capab = wpa_s->mlme.capab; 355189251Ssam if (wpa_s->mlme.phymode == WPA_MODE_IEEE80211G) { 356189251Ssam capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME | 357189251Ssam WLAN_CAPABILITY_SHORT_PREAMBLE; 358189251Ssam } 359189251Ssam bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); 360189251Ssam if (bss) { 361189251Ssam if (bss->capability & WLAN_CAPABILITY_PRIVACY) 362189251Ssam capab |= WLAN_CAPABILITY_PRIVACY; 363189251Ssam if (bss->wmm_ie) { 364189251Ssam wmm = 1; 365189251Ssam } 366189251Ssam } 367189251Ssam 368189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 369189251Ssam blen += 24; 370189251Ssam os_memset(mgmt, 0, 24); 371189251Ssam os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 372189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 373189251Ssam os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 374189251Ssam 375189251Ssam if (wpa_s->mlme.prev_bssid_set) { 376189251Ssam blen += 10; 377189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 378189251Ssam WLAN_FC_STYPE_REASSOC_REQ); 379189251Ssam mgmt->u.reassoc_req.capab_info = host_to_le16(capab); 380189251Ssam mgmt->u.reassoc_req.listen_interval = host_to_le16(1); 381189251Ssam os_memcpy(mgmt->u.reassoc_req.current_ap, 382189251Ssam wpa_s->mlme.prev_bssid, 383189251Ssam ETH_ALEN); 384189251Ssam } else { 385189251Ssam blen += 4; 386189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 387189251Ssam WLAN_FC_STYPE_ASSOC_REQ); 388189251Ssam mgmt->u.assoc_req.capab_info = host_to_le16(capab); 389189251Ssam mgmt->u.assoc_req.listen_interval = host_to_le16(1); 390189251Ssam } 391189251Ssam 392189251Ssam /* SSID */ 393189251Ssam ies = pos = buf + blen; 394189251Ssam blen += 2 + wpa_s->mlme.ssid_len; 395189251Ssam *pos++ = WLAN_EID_SSID; 396189251Ssam *pos++ = wpa_s->mlme.ssid_len; 397189251Ssam os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); 398189251Ssam 399189251Ssam len = wpa_s->mlme.num_curr_rates; 400189251Ssam if (len > 8) 401189251Ssam len = 8; 402189251Ssam pos = buf + blen; 403189251Ssam blen += len + 2; 404189251Ssam *pos++ = WLAN_EID_SUPP_RATES; 405189251Ssam *pos++ = len; 406189251Ssam for (i = 0; i < len; i++) { 407189251Ssam int rate = wpa_s->mlme.curr_rates[i].rate; 408189251Ssam *pos++ = (u8) (rate / 5); 409189251Ssam } 410189251Ssam 411189251Ssam if (wpa_s->mlme.num_curr_rates > len) { 412189251Ssam pos = buf + blen; 413189251Ssam blen += wpa_s->mlme.num_curr_rates - len + 2; 414189251Ssam *pos++ = WLAN_EID_EXT_SUPP_RATES; 415189251Ssam *pos++ = wpa_s->mlme.num_curr_rates - len; 416189251Ssam for (i = len; i < wpa_s->mlme.num_curr_rates; i++) { 417189251Ssam int rate = wpa_s->mlme.curr_rates[i].rate; 418189251Ssam *pos++ = (u8) (rate / 5); 419189251Ssam } 420189251Ssam } 421189251Ssam 422189251Ssam if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) { 423189251Ssam pos = buf + blen; 424189251Ssam blen += wpa_s->mlme.extra_ie_len; 425189251Ssam os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len); 426189251Ssam } 427189251Ssam 428189251Ssam#ifdef CONFIG_IEEE80211R 429189251Ssam if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || 430189251Ssam wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && 431189251Ssam wpa_s->mlme.auth_alg != WLAN_AUTH_FT && 432189251Ssam bss && bss->mdie && 433189251Ssam bss->mdie_len >= 2 + sizeof(struct rsn_mdie) && 434189251Ssam bss->mdie[1] >= sizeof(struct rsn_mdie)) { 435189251Ssam pos = buf + blen; 436189251Ssam blen += 2 + sizeof(struct rsn_mdie); 437189251Ssam *pos++ = WLAN_EID_MOBILITY_DOMAIN; 438189251Ssam *pos++ = sizeof(struct rsn_mdie); 439189251Ssam os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN); 440189251Ssam pos += MOBILITY_DOMAIN_ID_LEN; 441189251Ssam *pos++ = 0; /* FIX: copy from the target AP's MDIE */ 442189251Ssam } 443189251Ssam 444189251Ssam if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || 445189251Ssam wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && 446189251Ssam wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) { 447189251Ssam pos = buf + blen; 448189251Ssam os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len); 449189251Ssam pos += wpa_s->mlme.ft_ies_len; 450189251Ssam blen += wpa_s->mlme.ft_ies_len; 451189251Ssam } 452189251Ssam#endif /* CONFIG_IEEE80211R */ 453189251Ssam 454189251Ssam if (wmm && wpa_s->mlme.wmm_enabled) { 455189251Ssam pos = buf + blen; 456189251Ssam blen += 9; 457189251Ssam *pos++ = WLAN_EID_VENDOR_SPECIFIC; 458189251Ssam *pos++ = 7; /* len */ 459189251Ssam *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ 460189251Ssam *pos++ = 0x50; 461189251Ssam *pos++ = 0xf2; 462189251Ssam *pos++ = 2; /* WME */ 463189251Ssam *pos++ = 0; /* WME info */ 464189251Ssam *pos++ = 1; /* WME ver */ 465189251Ssam *pos++ = 0; 466189251Ssam } 467189251Ssam 468189251Ssam os_free(wpa_s->mlme.assocreq_ies); 469189251Ssam wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies; 470189251Ssam wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len); 471189251Ssam if (wpa_s->mlme.assocreq_ies) { 472189251Ssam os_memcpy(wpa_s->mlme.assocreq_ies, ies, 473189251Ssam wpa_s->mlme.assocreq_ies_len); 474189251Ssam } 475189251Ssam 476189251Ssam ieee80211_sta_tx(wpa_s, buf, blen); 477189251Ssam os_free(buf); 478189251Ssam} 479189251Ssam 480189251Ssam 481189251Ssamstatic void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason) 482189251Ssam{ 483189251Ssam u8 *buf; 484189251Ssam size_t len; 485189251Ssam struct ieee80211_mgmt *mgmt; 486189251Ssam 487189251Ssam buf = os_zalloc(sizeof(*mgmt)); 488189251Ssam if (buf == NULL) { 489189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " 490189251Ssam "deauth frame"); 491189251Ssam return; 492189251Ssam } 493189251Ssam 494189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 495189251Ssam len = 24; 496189251Ssam os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 497189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 498189251Ssam os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 499189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 500189251Ssam WLAN_FC_STYPE_DEAUTH); 501189251Ssam len += 2; 502189251Ssam mgmt->u.deauth.reason_code = host_to_le16(reason); 503189251Ssam 504189251Ssam ieee80211_sta_tx(wpa_s, buf, len); 505189251Ssam os_free(buf); 506189251Ssam} 507189251Ssam 508189251Ssam 509189251Ssamstatic void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason) 510189251Ssam{ 511189251Ssam u8 *buf; 512189251Ssam size_t len; 513189251Ssam struct ieee80211_mgmt *mgmt; 514189251Ssam 515189251Ssam buf = os_zalloc(sizeof(*mgmt)); 516189251Ssam if (buf == NULL) { 517189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " 518189251Ssam "disassoc frame"); 519189251Ssam return; 520189251Ssam } 521189251Ssam 522189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 523189251Ssam len = 24; 524189251Ssam os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 525189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 526189251Ssam os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 527189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 528189251Ssam WLAN_FC_STYPE_DISASSOC); 529189251Ssam len += 2; 530189251Ssam mgmt->u.disassoc.reason_code = host_to_le16(reason); 531189251Ssam 532189251Ssam ieee80211_sta_tx(wpa_s, buf, len); 533189251Ssam os_free(buf); 534189251Ssam} 535189251Ssam 536189251Ssam 537189251Ssamstatic int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s) 538189251Ssam{ 539189251Ssam struct ieee80211_sta_bss *bss; 540189251Ssam int res = 0; 541189251Ssam 542189251Ssam if (wpa_s->mlme.mixed_cell || 543189251Ssam wpa_s->mlme.key_mgmt != KEY_MGMT_NONE) 544189251Ssam return 0; 545189251Ssam 546189251Ssam bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); 547189251Ssam if (bss == NULL) 548189251Ssam return 0; 549189251Ssam 550189251Ssam if (ieee80211_sta_wep_configured(wpa_s) != 551189251Ssam !!(bss->capability & WLAN_CAPABILITY_PRIVACY)) 552189251Ssam res = 1; 553189251Ssam 554189251Ssam return res; 555189251Ssam} 556189251Ssam 557189251Ssam 558189251Ssamstatic void ieee80211_associate(struct wpa_supplicant *wpa_s) 559189251Ssam{ 560189251Ssam wpa_s->mlme.assoc_tries++; 561189251Ssam if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { 562189251Ssam wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR 563189251Ssam " timed out", MAC2STR(wpa_s->bssid)); 564189251Ssam return; 565189251Ssam } 566189251Ssam 567189251Ssam wpa_s->mlme.state = IEEE80211_ASSOCIATE; 568189251Ssam wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR, 569189251Ssam MAC2STR(wpa_s->bssid)); 570189251Ssam if (ieee80211_privacy_mismatch(wpa_s)) { 571189251Ssam wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy " 572189251Ssam "configuration and mixed-cell disabled - abort " 573189251Ssam "association"); 574189251Ssam return; 575189251Ssam } 576189251Ssam 577189251Ssam ieee80211_send_assoc(wpa_s); 578189251Ssam 579189251Ssam ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT); 580189251Ssam} 581189251Ssam 582189251Ssam 583189251Ssamstatic void ieee80211_associated(struct wpa_supplicant *wpa_s) 584189251Ssam{ 585189251Ssam int disassoc; 586189251Ssam 587189251Ssam /* TODO: start monitoring current AP signal quality and number of 588189251Ssam * missed beacons. Scan other channels every now and then and search 589189251Ssam * for better APs. */ 590189251Ssam /* TODO: remove expired BSSes */ 591189251Ssam 592189251Ssam wpa_s->mlme.state = IEEE80211_ASSOCIATED; 593189251Ssam 594189251Ssam#if 0 /* FIX */ 595189251Ssam sta = sta_info_get(local, wpa_s->bssid); 596189251Ssam if (sta == NULL) { 597189251Ssam wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR, 598189251Ssam MAC2STR(wpa_s->bssid)); 599189251Ssam disassoc = 1; 600189251Ssam } else { 601189251Ssam disassoc = 0; 602189251Ssam if (time_after(jiffies, 603189251Ssam sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { 604189251Ssam if (wpa_s->mlme.probereq_poll) { 605189251Ssam wpa_printf(MSG_DEBUG "MLME: No ProbeResp from " 606189251Ssam "current AP " MACSTR " - assume " 607189251Ssam "out of range", 608189251Ssam MAC2STR(wpa_s->bssid)); 609189251Ssam disassoc = 1; 610189251Ssam } else { 611189251Ssam ieee80211_send_probe_req( 612189251Ssam wpa_s->bssid, 613189251Ssam wpa_s->mlme.scan_ssid, 614189251Ssam wpa_s->mlme.scan_ssid_len); 615189251Ssam wpa_s->mlme.probereq_poll = 1; 616189251Ssam } 617189251Ssam } else { 618189251Ssam wpa_s->mlme.probereq_poll = 0; 619189251Ssam if (time_after(jiffies, wpa_s->mlme.last_probe + 620189251Ssam IEEE80211_PROBE_INTERVAL)) { 621189251Ssam wpa_s->mlme.last_probe = jiffies; 622189251Ssam ieee80211_send_probe_req(wpa_s->bssid, 623189251Ssam wpa_s->mlme.ssid, 624189251Ssam wpa_s->mlme.ssid_len); 625189251Ssam } 626189251Ssam } 627189251Ssam sta_info_release(local, sta); 628189251Ssam } 629189251Ssam#else 630189251Ssam disassoc = 0; 631189251Ssam#endif 632189251Ssam if (disassoc) { 633189251Ssam wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); 634189251Ssam ieee80211_reschedule_timer(wpa_s, 635189251Ssam IEEE80211_MONITORING_INTERVAL + 636189251Ssam 30000); 637189251Ssam } else { 638189251Ssam ieee80211_reschedule_timer(wpa_s, 639189251Ssam IEEE80211_MONITORING_INTERVAL); 640189251Ssam } 641189251Ssam} 642189251Ssam 643189251Ssam 644189251Ssamstatic void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, 645189251Ssam const u8 *dst, 646189251Ssam const u8 *ssid, size_t ssid_len) 647189251Ssam{ 648189251Ssam u8 *buf; 649189251Ssam size_t len; 650189251Ssam struct ieee80211_mgmt *mgmt; 651189251Ssam u8 *pos, *supp_rates; 652189251Ssam u8 *esupp_rates = NULL; 653189251Ssam int i; 654189251Ssam 655189251Ssam buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len); 656189251Ssam if (buf == NULL) { 657189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " 658189251Ssam "probe request"); 659189251Ssam return; 660189251Ssam } 661189251Ssam 662189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 663189251Ssam len = 24; 664189251Ssam os_memset(mgmt, 0, 24); 665189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 666189251Ssam WLAN_FC_STYPE_PROBE_REQ); 667189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 668189251Ssam if (dst) { 669189251Ssam os_memcpy(mgmt->da, dst, ETH_ALEN); 670189251Ssam os_memcpy(mgmt->bssid, dst, ETH_ALEN); 671189251Ssam } else { 672189251Ssam os_memset(mgmt->da, 0xff, ETH_ALEN); 673189251Ssam os_memset(mgmt->bssid, 0xff, ETH_ALEN); 674189251Ssam } 675189251Ssam pos = buf + len; 676189251Ssam len += 2 + ssid_len; 677189251Ssam *pos++ = WLAN_EID_SSID; 678189251Ssam *pos++ = ssid_len; 679189251Ssam os_memcpy(pos, ssid, ssid_len); 680189251Ssam 681189251Ssam supp_rates = buf + len; 682189251Ssam len += 2; 683189251Ssam supp_rates[0] = WLAN_EID_SUPP_RATES; 684189251Ssam supp_rates[1] = 0; 685189251Ssam for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) { 686189251Ssam struct wpa_rate_data *rate = &wpa_s->mlme.curr_rates[i]; 687189251Ssam if (esupp_rates) { 688189251Ssam pos = buf + len; 689189251Ssam len++; 690189251Ssam esupp_rates[1]++; 691189251Ssam } else if (supp_rates[1] == 8) { 692189251Ssam esupp_rates = pos; 693189251Ssam esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; 694189251Ssam esupp_rates[1] = 1; 695189251Ssam pos = &esupp_rates[2]; 696189251Ssam len += 3; 697189251Ssam } else { 698189251Ssam pos = buf + len; 699189251Ssam len++; 700189251Ssam supp_rates[1]++; 701189251Ssam } 702189251Ssam *pos++ = rate->rate / 5; 703189251Ssam } 704189251Ssam 705189251Ssam if (wpa_s->mlme.extra_probe_ie) { 706189251Ssam os_memcpy(pos, wpa_s->mlme.extra_probe_ie, 707189251Ssam wpa_s->mlme.extra_probe_ie_len); 708189251Ssam len += wpa_s->mlme.extra_probe_ie_len; 709189251Ssam } 710189251Ssam 711189251Ssam ieee80211_sta_tx(wpa_s, buf, len); 712189251Ssam os_free(buf); 713189251Ssam} 714189251Ssam 715189251Ssam 716189251Ssamstatic int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s) 717189251Ssam{ 718189251Ssam#if 0 /* FIX */ 719189251Ssam if (sdata == NULL || sdata->default_key == NULL || 720189251Ssam sdata->default_key->alg != ALG_WEP) 721189251Ssam return 0; 722189251Ssam return 1; 723189251Ssam#else 724189251Ssam return 0; 725189251Ssam#endif 726189251Ssam} 727189251Ssam 728189251Ssam 729189251Ssamstatic void ieee80211_auth_completed(struct wpa_supplicant *wpa_s) 730189251Ssam{ 731189251Ssam wpa_printf(MSG_DEBUG, "MLME: authenticated"); 732189251Ssam wpa_s->mlme.authenticated = 1; 733189251Ssam ieee80211_associate(wpa_s); 734189251Ssam} 735189251Ssam 736189251Ssam 737189251Ssamstatic void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s, 738189251Ssam struct ieee80211_mgmt *mgmt, 739189251Ssam size_t len, 740189251Ssam struct ieee80211_rx_status *rx_status) 741189251Ssam{ 742189251Ssam u8 *pos; 743189251Ssam struct ieee802_11_elems elems; 744189251Ssam 745189251Ssam wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge"); 746189251Ssam pos = mgmt->u.auth.variable; 747189251Ssam if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) 748189251Ssam == ParseFailed) { 749189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)"); 750189251Ssam return; 751189251Ssam } 752189251Ssam if (elems.challenge == NULL) { 753189251Ssam wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key " 754189251Ssam "auth frame"); 755189251Ssam return; 756189251Ssam } 757189251Ssam ieee80211_send_auth(wpa_s, 3, elems.challenge - 2, 758189251Ssam elems.challenge_len + 2, 1); 759189251Ssam} 760189251Ssam 761189251Ssam 762189251Ssamstatic void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s, 763189251Ssam struct ieee80211_mgmt *mgmt, 764189251Ssam size_t len, 765189251Ssam struct ieee80211_rx_status *rx_status) 766189251Ssam{ 767189251Ssam struct wpa_ssid *ssid = wpa_s->current_ssid; 768189251Ssam u16 auth_alg, auth_transaction, status_code; 769189251Ssam int adhoc; 770189251Ssam 771189251Ssam adhoc = ssid && ssid->mode == 1; 772189251Ssam 773189251Ssam if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) { 774189251Ssam wpa_printf(MSG_DEBUG, "MLME: authentication frame received " 775189251Ssam "from " MACSTR ", but not in authenticate state - " 776189251Ssam "ignored", MAC2STR(mgmt->sa)); 777189251Ssam return; 778189251Ssam } 779189251Ssam 780189251Ssam if (len < 24 + 6) { 781189251Ssam wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication " 782189251Ssam "frame received from " MACSTR " - ignored", 783189251Ssam (unsigned long) len, MAC2STR(mgmt->sa)); 784189251Ssam return; 785189251Ssam } 786189251Ssam 787189251Ssam if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { 788189251Ssam wpa_printf(MSG_DEBUG, "MLME: authentication frame received " 789189251Ssam "from unknown AP (SA=" MACSTR " BSSID=" MACSTR 790189251Ssam ") - ignored", 791189251Ssam MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); 792189251Ssam return; 793189251Ssam } 794189251Ssam 795189251Ssam if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) { 796189251Ssam wpa_printf(MSG_DEBUG, "MLME: authentication frame received " 797189251Ssam "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR 798189251Ssam ") - ignored", 799189251Ssam MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); 800189251Ssam return; 801189251Ssam } 802189251Ssam 803189251Ssam auth_alg = le_to_host16(mgmt->u.auth.auth_alg); 804189251Ssam auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); 805189251Ssam status_code = le_to_host16(mgmt->u.auth.status_code); 806189251Ssam 807189251Ssam wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR 808189251Ssam " (alg=%d transaction=%d status=%d)", 809189251Ssam MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code); 810189251Ssam 811189251Ssam if (adhoc) { 812189251Ssam /* IEEE 802.11 standard does not require authentication in IBSS 813189251Ssam * networks and most implementations do not seem to use it. 814189251Ssam * However, try to reply to authentication attempts if someone 815189251Ssam * has actually implemented this. 816189251Ssam * TODO: Could implement shared key authentication. */ 817189251Ssam if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { 818189251Ssam wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS " 819189251Ssam "authentication frame (alg=%d " 820189251Ssam "transaction=%d)", 821189251Ssam auth_alg, auth_transaction); 822189251Ssam return; 823189251Ssam } 824189251Ssam ieee80211_send_auth(wpa_s, 2, NULL, 0, 0); 825189251Ssam } 826189251Ssam 827189251Ssam if (auth_alg != wpa_s->mlme.auth_alg || 828189251Ssam auth_transaction != wpa_s->mlme.auth_transaction) { 829189251Ssam wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame " 830189251Ssam "(alg=%d transaction=%d)", 831189251Ssam auth_alg, auth_transaction); 832189251Ssam return; 833189251Ssam } 834189251Ssam 835189251Ssam if (status_code != WLAN_STATUS_SUCCESS) { 836189251Ssam wpa_printf(MSG_DEBUG, "MLME: AP denied authentication " 837189251Ssam "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg, 838189251Ssam status_code); 839189251Ssam if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { 840189251Ssam const int num_algs = 3; 841189251Ssam u8 algs[num_algs]; 842189251Ssam int i, pos; 843189251Ssam algs[0] = algs[1] = algs[2] = 0xff; 844189251Ssam if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_OPEN) 845189251Ssam algs[0] = WLAN_AUTH_OPEN; 846189251Ssam if (wpa_s->mlme.auth_algs & 847189251Ssam IEEE80211_AUTH_ALG_SHARED_KEY) 848189251Ssam algs[1] = WLAN_AUTH_SHARED_KEY; 849189251Ssam if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_LEAP) 850189251Ssam algs[2] = WLAN_AUTH_LEAP; 851189251Ssam if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN) 852189251Ssam pos = 0; 853189251Ssam else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY) 854189251Ssam pos = 1; 855189251Ssam else 856189251Ssam pos = 2; 857189251Ssam for (i = 0; i < num_algs; i++) { 858189251Ssam pos++; 859189251Ssam if (pos >= num_algs) 860189251Ssam pos = 0; 861189251Ssam if (algs[pos] == wpa_s->mlme.auth_alg || 862189251Ssam algs[pos] == 0xff) 863189251Ssam continue; 864189251Ssam if (algs[pos] == WLAN_AUTH_SHARED_KEY && 865189251Ssam !ieee80211_sta_wep_configured(wpa_s)) 866189251Ssam continue; 867189251Ssam wpa_s->mlme.auth_alg = algs[pos]; 868189251Ssam wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d " 869189251Ssam "for next try", 870189251Ssam wpa_s->mlme.auth_alg); 871189251Ssam break; 872189251Ssam } 873189251Ssam } 874189251Ssam return; 875189251Ssam } 876189251Ssam 877189251Ssam switch (wpa_s->mlme.auth_alg) { 878189251Ssam case WLAN_AUTH_OPEN: 879189251Ssam case WLAN_AUTH_LEAP: 880189251Ssam ieee80211_auth_completed(wpa_s); 881189251Ssam break; 882189251Ssam case WLAN_AUTH_SHARED_KEY: 883189251Ssam if (wpa_s->mlme.auth_transaction == 4) 884189251Ssam ieee80211_auth_completed(wpa_s); 885189251Ssam else 886189251Ssam ieee80211_auth_challenge(wpa_s, mgmt, len, 887189251Ssam rx_status); 888189251Ssam break; 889189251Ssam#ifdef CONFIG_IEEE80211R 890189251Ssam case WLAN_AUTH_FT: 891189251Ssam { 892189251Ssam union wpa_event_data data; 893189251Ssam os_memset(&data, 0, sizeof(data)); 894189251Ssam data.ft_ies.ies = mgmt->u.auth.variable; 895189251Ssam data.ft_ies.ies_len = len - 896189251Ssam (mgmt->u.auth.variable - (u8 *) mgmt); 897189251Ssam os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN); 898189251Ssam wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); 899189251Ssam ieee80211_auth_completed(wpa_s); 900189251Ssam break; 901189251Ssam } 902189251Ssam#endif /* CONFIG_IEEE80211R */ 903189251Ssam } 904189251Ssam} 905189251Ssam 906189251Ssam 907189251Ssamstatic void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s, 908189251Ssam struct ieee80211_mgmt *mgmt, 909189251Ssam size_t len, 910189251Ssam struct ieee80211_rx_status *rx_status) 911189251Ssam{ 912189251Ssam u16 reason_code; 913189251Ssam 914189251Ssam if (len < 24 + 2) { 915189251Ssam wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication " 916189251Ssam "frame received from " MACSTR " - ignored", 917189251Ssam (unsigned long) len, MAC2STR(mgmt->sa)); 918189251Ssam return; 919189251Ssam } 920189251Ssam 921189251Ssam if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { 922189251Ssam wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received " 923189251Ssam "from unknown AP (SA=" MACSTR " BSSID=" MACSTR 924189251Ssam ") - ignored", 925189251Ssam MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); 926189251Ssam return; 927189251Ssam } 928189251Ssam 929189251Ssam reason_code = le_to_host16(mgmt->u.deauth.reason_code); 930189251Ssam 931189251Ssam wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR 932189251Ssam " (reason=%d)", MAC2STR(mgmt->sa), reason_code); 933189251Ssam 934189251Ssam if (wpa_s->mlme.authenticated) 935189251Ssam wpa_printf(MSG_DEBUG, "MLME: deauthenticated"); 936189251Ssam 937189251Ssam if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE || 938189251Ssam wpa_s->mlme.state == IEEE80211_ASSOCIATE || 939189251Ssam wpa_s->mlme.state == IEEE80211_ASSOCIATED) { 940189251Ssam wpa_s->mlme.state = IEEE80211_AUTHENTICATE; 941189251Ssam ieee80211_reschedule_timer(wpa_s, 942189251Ssam IEEE80211_RETRY_AUTH_INTERVAL); 943189251Ssam } 944189251Ssam 945189251Ssam ieee80211_set_associated(wpa_s, 0); 946189251Ssam wpa_s->mlme.authenticated = 0; 947189251Ssam} 948189251Ssam 949189251Ssam 950189251Ssamstatic void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s, 951189251Ssam struct ieee80211_mgmt *mgmt, 952189251Ssam size_t len, 953189251Ssam struct ieee80211_rx_status *rx_status) 954189251Ssam{ 955189251Ssam u16 reason_code; 956189251Ssam 957189251Ssam if (len < 24 + 2) { 958189251Ssam wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation " 959189251Ssam "frame received from " MACSTR " - ignored", 960189251Ssam (unsigned long) len, MAC2STR(mgmt->sa)); 961189251Ssam return; 962189251Ssam } 963189251Ssam 964189251Ssam if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { 965189251Ssam wpa_printf(MSG_DEBUG, "MLME: disassociation frame received " 966189251Ssam "from unknown AP (SA=" MACSTR " BSSID=" MACSTR 967189251Ssam ") - ignored", 968189251Ssam MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); 969189251Ssam return; 970189251Ssam } 971189251Ssam 972189251Ssam reason_code = le_to_host16(mgmt->u.disassoc.reason_code); 973189251Ssam 974189251Ssam wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR 975189251Ssam " (reason=%d)", MAC2STR(mgmt->sa), reason_code); 976189251Ssam 977189251Ssam if (wpa_s->mlme.associated) 978189251Ssam wpa_printf(MSG_DEBUG, "MLME: disassociated"); 979189251Ssam 980189251Ssam if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) { 981189251Ssam wpa_s->mlme.state = IEEE80211_ASSOCIATE; 982189251Ssam ieee80211_reschedule_timer(wpa_s, 983189251Ssam IEEE80211_RETRY_AUTH_INTERVAL); 984189251Ssam } 985189251Ssam 986189251Ssam ieee80211_set_associated(wpa_s, 0); 987189251Ssam} 988189251Ssam 989189251Ssam 990189251Ssamstatic int ieee80211_ft_assoc_resp(struct wpa_supplicant *wpa_s, 991189251Ssam struct ieee802_11_elems *elems) 992189251Ssam{ 993189251Ssam#ifdef CONFIG_IEEE80211R 994189251Ssam const u8 *mobility_domain = NULL; 995189251Ssam const u8 *r0kh_id = NULL; 996189251Ssam size_t r0kh_id_len = 0; 997189251Ssam const u8 *r1kh_id = NULL; 998189251Ssam struct rsn_ftie *hdr; 999189251Ssam const u8 *pos, *end; 1000189251Ssam 1001189251Ssam if (elems->mdie && elems->mdie_len >= MOBILITY_DOMAIN_ID_LEN) 1002189251Ssam mobility_domain = elems->mdie; 1003189251Ssam if (elems->ftie && elems->ftie_len >= sizeof(struct rsn_ftie)) { 1004189251Ssam end = elems->ftie + elems->ftie_len; 1005189251Ssam hdr = (struct rsn_ftie *) elems->ftie; 1006189251Ssam pos = (const u8 *) (hdr + 1); 1007189251Ssam while (pos + 1 < end) { 1008189251Ssam if (pos + 2 + pos[1] > end) 1009189251Ssam break; 1010189251Ssam if (pos[0] == FTIE_SUBELEM_R1KH_ID && 1011189251Ssam pos[1] == FT_R1KH_ID_LEN) 1012189251Ssam r1kh_id = pos + 2; 1013189251Ssam else if (pos[0] == FTIE_SUBELEM_R0KH_ID && 1014189251Ssam pos[1] >= 1 && pos[1] <= FT_R0KH_ID_MAX_LEN) { 1015189251Ssam r0kh_id = pos + 2; 1016189251Ssam r0kh_id_len = pos[1]; 1017189251Ssam } 1018189251Ssam pos += 2 + pos[1]; 1019189251Ssam } 1020189251Ssam } 1021189251Ssam return wpa_sm_set_ft_params(wpa_s->wpa, mobility_domain, r0kh_id, 1022189251Ssam r0kh_id_len, r1kh_id); 1023189251Ssam#else /* CONFIG_IEEE80211R */ 1024189251Ssam return 0; 1025189251Ssam#endif /* CONFIG_IEEE80211R */ 1026189251Ssam} 1027189251Ssam 1028189251Ssam 1029189251Ssamstatic void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, 1030189251Ssam struct ieee80211_mgmt *mgmt, 1031189251Ssam size_t len, 1032189251Ssam struct ieee80211_rx_status *rx_status, 1033189251Ssam int reassoc) 1034189251Ssam{ 1035189251Ssam u8 rates[32]; 1036189251Ssam size_t rates_len; 1037189251Ssam u16 capab_info, status_code, aid; 1038189251Ssam struct ieee802_11_elems elems; 1039189251Ssam u8 *pos; 1040189251Ssam 1041189251Ssam /* AssocResp and ReassocResp have identical structure, so process both 1042189251Ssam * of them in this function. */ 1043189251Ssam 1044189251Ssam if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) { 1045189251Ssam wpa_printf(MSG_DEBUG, "MLME: association frame received from " 1046189251Ssam MACSTR ", but not in associate state - ignored", 1047189251Ssam MAC2STR(mgmt->sa)); 1048189251Ssam return; 1049189251Ssam } 1050189251Ssam 1051189251Ssam if (len < 24 + 6) { 1052189251Ssam wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association " 1053189251Ssam "frame received from " MACSTR " - ignored", 1054189251Ssam (unsigned long) len, MAC2STR(mgmt->sa)); 1055189251Ssam return; 1056189251Ssam } 1057189251Ssam 1058189251Ssam if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { 1059189251Ssam wpa_printf(MSG_DEBUG, "MLME: association frame received from " 1060189251Ssam "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " 1061189251Ssam "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); 1062189251Ssam return; 1063189251Ssam } 1064189251Ssam 1065189251Ssam capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info); 1066189251Ssam status_code = le_to_host16(mgmt->u.assoc_resp.status_code); 1067189251Ssam aid = le_to_host16(mgmt->u.assoc_resp.aid); 1068189251Ssam if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) 1069189251Ssam wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 " 1070189251Ssam "not set", aid); 1071189251Ssam aid &= ~(BIT(15) | BIT(14)); 1072189251Ssam 1073189251Ssam wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR 1074189251Ssam " (capab=0x%x status=%d aid=%d)", 1075189251Ssam reassoc ? "Rea" : "A", MAC2STR(mgmt->sa), 1076189251Ssam capab_info, status_code, aid); 1077189251Ssam 1078189251Ssam pos = mgmt->u.assoc_resp.variable; 1079189251Ssam if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) 1080189251Ssam == ParseFailed) { 1081189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp"); 1082189251Ssam return; 1083189251Ssam } 1084189251Ssam 1085189251Ssam if (status_code != WLAN_STATUS_SUCCESS) { 1086189251Ssam wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)", 1087189251Ssam status_code); 1088189251Ssam#ifdef CONFIG_IEEE80211W 1089189251Ssam if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && 1090189251Ssam elems.timeout_int && elems.timeout_int_len == 5 && 1091189251Ssam elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { 1092189251Ssam u32 tu, ms; 1093189251Ssam tu = WPA_GET_LE32(elems.timeout_int + 1); 1094189251Ssam ms = tu * 1024 / 1000; 1095189251Ssam wpa_printf(MSG_DEBUG, "MLME: AP rejected association " 1096189251Ssam "temporarily; comeback duration %u TU " 1097189251Ssam "(%u ms)", tu, ms); 1098189251Ssam if (ms > IEEE80211_ASSOC_TIMEOUT) { 1099189251Ssam wpa_printf(MSG_DEBUG, "MLME: Update timer " 1100189251Ssam "based on comeback duration"); 1101189251Ssam ieee80211_reschedule_timer(wpa_s, ms); 1102189251Ssam } 1103189251Ssam } 1104189251Ssam#endif /* CONFIG_IEEE80211W */ 1105189251Ssam return; 1106189251Ssam } 1107189251Ssam 1108189251Ssam if (elems.supp_rates == NULL) { 1109189251Ssam wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in " 1110189251Ssam "AssocResp"); 1111189251Ssam return; 1112189251Ssam } 1113189251Ssam 1114189251Ssam if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) { 1115189251Ssam if (!reassoc) { 1116189251Ssam wpa_printf(MSG_DEBUG, "MLME: AP tried to use " 1117189251Ssam "association, not reassociation, response " 1118189251Ssam "with FT"); 1119189251Ssam return; 1120189251Ssam } 1121189251Ssam if (wpa_ft_validate_reassoc_resp( 1122189251Ssam wpa_s->wpa, pos, len - (pos - (u8 *) mgmt), 1123189251Ssam mgmt->sa) < 0) { 1124189251Ssam wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc" 1125189251Ssam "Resp failed"); 1126189251Ssam return; 1127189251Ssam } 1128189251Ssam } else if (ieee80211_ft_assoc_resp(wpa_s, &elems) < 0) 1129189251Ssam return; 1130189251Ssam 1131189251Ssam wpa_printf(MSG_DEBUG, "MLME: associated"); 1132189251Ssam wpa_s->mlme.aid = aid; 1133189251Ssam wpa_s->mlme.ap_capab = capab_info; 1134189251Ssam 1135189251Ssam os_free(wpa_s->mlme.assocresp_ies); 1136189251Ssam wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt); 1137189251Ssam wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len); 1138189251Ssam if (wpa_s->mlme.assocresp_ies) { 1139189251Ssam os_memcpy(wpa_s->mlme.assocresp_ies, pos, 1140189251Ssam wpa_s->mlme.assocresp_ies_len); 1141189251Ssam } 1142189251Ssam 1143189251Ssam ieee80211_set_associated(wpa_s, 1); 1144189251Ssam 1145189251Ssam rates_len = elems.supp_rates_len; 1146189251Ssam if (rates_len > sizeof(rates)) 1147189251Ssam rates_len = sizeof(rates); 1148189251Ssam os_memcpy(rates, elems.supp_rates, rates_len); 1149189251Ssam if (elems.ext_supp_rates) { 1150189251Ssam size_t _len = elems.ext_supp_rates_len; 1151189251Ssam if (_len > sizeof(rates) - rates_len) 1152189251Ssam _len = sizeof(rates) - rates_len; 1153189251Ssam os_memcpy(rates + rates_len, elems.ext_supp_rates, _len); 1154189251Ssam rates_len += _len; 1155189251Ssam } 1156189251Ssam 1157189251Ssam if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) { 1158189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the " 1159189251Ssam "netstack"); 1160189251Ssam } 1161189251Ssam if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) < 1162189251Ssam 0) { 1163189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the " 1164189251Ssam "netstack"); 1165189251Ssam } 1166189251Ssam 1167189251Ssam /* Remove STA entry before adding a new one just in case to avoid 1168189251Ssam * problems with existing configuration (e.g., keys). */ 1169189251Ssam wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid); 1170189251Ssam if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) { 1171189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the " 1172189251Ssam "netstack"); 1173189251Ssam } 1174189251Ssam 1175189251Ssam#if 0 /* FIX? */ 1176189251Ssam sta->assoc_ap = 1; 1177189251Ssam 1178189251Ssam if (elems.wme && wpa_s->mlme.wmm_enabled) { 1179189251Ssam sta->flags |= WLAN_STA_WME; 1180189251Ssam ieee80211_sta_wmm_params(wpa_s, elems.wme, elems.wme_len); 1181189251Ssam } 1182189251Ssam#endif 1183189251Ssam 1184189251Ssam ieee80211_associated(wpa_s); 1185189251Ssam} 1186189251Ssam 1187189251Ssam 1188189251Ssam/* Caller must hold local->sta_bss_lock */ 1189189251Ssamstatic void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s, 1190189251Ssam struct ieee80211_sta_bss *bss) 1191189251Ssam{ 1192189251Ssam bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; 1193189251Ssam wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss; 1194189251Ssam} 1195189251Ssam 1196189251Ssam 1197189251Ssam/* Caller must hold local->sta_bss_lock */ 1198189251Ssamstatic void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s, 1199189251Ssam struct ieee80211_sta_bss *bss) 1200189251Ssam{ 1201189251Ssam struct ieee80211_sta_bss *b, *prev = NULL; 1202189251Ssam b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; 1203189251Ssam while (b) { 1204189251Ssam if (b == bss) { 1205189251Ssam if (prev == NULL) { 1206189251Ssam wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] 1207189251Ssam = bss->hnext; 1208189251Ssam } else { 1209189251Ssam prev->hnext = bss->hnext; 1210189251Ssam } 1211189251Ssam break; 1212189251Ssam } 1213189251Ssam prev = b; 1214189251Ssam b = b->hnext; 1215189251Ssam } 1216189251Ssam} 1217189251Ssam 1218189251Ssam 1219189251Ssamstatic struct ieee80211_sta_bss * 1220189251Ssamieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid) 1221189251Ssam{ 1222189251Ssam struct ieee80211_sta_bss *bss; 1223189251Ssam 1224189251Ssam bss = os_zalloc(sizeof(*bss)); 1225189251Ssam if (bss == NULL) 1226189251Ssam return NULL; 1227189251Ssam os_memcpy(bss->bssid, bssid, ETH_ALEN); 1228189251Ssam 1229189251Ssam /* TODO: order by RSSI? */ 1230189251Ssam bss->next = wpa_s->mlme.sta_bss_list; 1231189251Ssam wpa_s->mlme.sta_bss_list = bss; 1232189251Ssam __ieee80211_bss_hash_add(wpa_s, bss); 1233189251Ssam return bss; 1234189251Ssam} 1235189251Ssam 1236189251Ssam 1237189251Ssamstatic struct ieee80211_sta_bss * 1238189251Ssamieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid) 1239189251Ssam{ 1240189251Ssam struct ieee80211_sta_bss *bss; 1241189251Ssam 1242189251Ssam bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)]; 1243189251Ssam while (bss) { 1244189251Ssam if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) 1245189251Ssam break; 1246189251Ssam bss = bss->hnext; 1247189251Ssam } 1248189251Ssam return bss; 1249189251Ssam} 1250189251Ssam 1251189251Ssam 1252189251Ssamstatic void ieee80211_bss_free(struct wpa_supplicant *wpa_s, 1253189251Ssam struct ieee80211_sta_bss *bss) 1254189251Ssam{ 1255189251Ssam __ieee80211_bss_hash_del(wpa_s, bss); 1256189251Ssam os_free(bss->ie); 1257189251Ssam os_free(bss->wpa_ie); 1258189251Ssam os_free(bss->rsn_ie); 1259189251Ssam os_free(bss->wmm_ie); 1260189251Ssam os_free(bss->mdie); 1261189251Ssam os_free(bss); 1262189251Ssam} 1263189251Ssam 1264189251Ssam 1265189251Ssamstatic void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s) 1266189251Ssam{ 1267189251Ssam struct ieee80211_sta_bss *bss, *prev; 1268189251Ssam 1269189251Ssam bss = wpa_s->mlme.sta_bss_list; 1270189251Ssam wpa_s->mlme.sta_bss_list = NULL; 1271189251Ssam while (bss) { 1272189251Ssam prev = bss; 1273189251Ssam bss = bss->next; 1274189251Ssam ieee80211_bss_free(wpa_s, prev); 1275189251Ssam } 1276189251Ssam} 1277189251Ssam 1278189251Ssam 1279189251Ssamstatic void ieee80211_bss_info(struct wpa_supplicant *wpa_s, 1280189251Ssam struct ieee80211_mgmt *mgmt, 1281189251Ssam size_t len, 1282189251Ssam struct ieee80211_rx_status *rx_status, 1283189251Ssam int beacon) 1284189251Ssam{ 1285189251Ssam struct ieee802_11_elems elems; 1286189251Ssam size_t baselen; 1287189251Ssam int channel, invalid = 0, clen; 1288189251Ssam struct ieee80211_sta_bss *bss; 1289189251Ssam u64 timestamp; 1290189251Ssam u8 *pos, *ie_pos; 1291189251Ssam size_t ie_len; 1292189251Ssam 1293189251Ssam if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN)) 1294189251Ssam return; /* ignore ProbeResp to foreign address */ 1295189251Ssam 1296189251Ssam#if 0 1297189251Ssam wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR, 1298189251Ssam beacon ? "Beacon" : "Probe Response", 1299189251Ssam MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); 1300189251Ssam#endif 1301189251Ssam 1302189251Ssam baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; 1303189251Ssam if (baselen > len) 1304189251Ssam return; 1305189251Ssam 1306189251Ssam pos = mgmt->u.beacon.timestamp; 1307189251Ssam timestamp = WPA_GET_LE64(pos); 1308189251Ssam 1309189251Ssam#if 0 /* FIX */ 1310189251Ssam if (local->conf.mode == IW_MODE_ADHOC && beacon && 1311189251Ssam os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) { 1312189251Ssam#ifdef IEEE80211_IBSS_DEBUG 1313189251Ssam static unsigned long last_tsf_debug = 0; 1314189251Ssam u64 tsf; 1315189251Ssam if (local->hw->get_tsf) 1316189251Ssam tsf = local->hw->get_tsf(local->mdev); 1317189251Ssam else 1318189251Ssam tsf = -1LLU; 1319189251Ssam if (time_after(jiffies, last_tsf_debug + 5 * HZ)) { 1320189251Ssam wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID=" 1321189251Ssam MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld " 1322189251Ssam "@%ld", 1323189251Ssam MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), 1324189251Ssam tsf, timestamp, tsf - timestamp, jiffies); 1325189251Ssam last_tsf_debug = jiffies; 1326189251Ssam } 1327189251Ssam#endif /* IEEE80211_IBSS_DEBUG */ 1328189251Ssam } 1329189251Ssam#endif 1330189251Ssam 1331189251Ssam ie_pos = mgmt->u.beacon.variable; 1332189251Ssam ie_len = len - baselen; 1333189251Ssam if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed) 1334189251Ssam invalid = 1; 1335189251Ssam 1336189251Ssam#if 0 /* FIX */ 1337189251Ssam if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates && 1338189251Ssam os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 && 1339189251Ssam (sta = sta_info_get(local, mgmt->sa))) { 1340189251Ssam struct ieee80211_rate *rates; 1341189251Ssam size_t num_rates; 1342189251Ssam u32 supp_rates, prev_rates; 1343189251Ssam int i, j, oper_mode; 1344189251Ssam 1345189251Ssam rates = local->curr_rates; 1346189251Ssam num_rates = local->num_curr_rates; 1347189251Ssam oper_mode = wpa_s->mlme.sta_scanning ? 1348189251Ssam local->scan_oper_phymode : local->conf.phymode; 1349189251Ssam for (i = 0; i < local->hw->num_modes; i++) { 1350189251Ssam struct ieee80211_hw_modes *mode = &local->hw->modes[i]; 1351189251Ssam if (oper_mode == mode->mode) { 1352189251Ssam rates = mode->rates; 1353189251Ssam num_rates = mode->num_rates; 1354189251Ssam break; 1355189251Ssam } 1356189251Ssam } 1357189251Ssam 1358189251Ssam supp_rates = 0; 1359189251Ssam for (i = 0; i < elems.supp_rates_len + 1360189251Ssam elems.ext_supp_rates_len; i++) { 1361189251Ssam u8 rate = 0; 1362189251Ssam int own_rate; 1363189251Ssam if (i < elems.supp_rates_len) 1364189251Ssam rate = elems.supp_rates[i]; 1365189251Ssam else if (elems.ext_supp_rates) 1366189251Ssam rate = elems.ext_supp_rates 1367189251Ssam [i - elems.supp_rates_len]; 1368189251Ssam own_rate = 5 * (rate & 0x7f); 1369189251Ssam if (oper_mode == MODE_ATHEROS_TURBO) 1370189251Ssam own_rate *= 2; 1371189251Ssam for (j = 0; j < num_rates; j++) 1372189251Ssam if (rates[j].rate == own_rate) 1373189251Ssam supp_rates |= BIT(j); 1374189251Ssam } 1375189251Ssam 1376189251Ssam prev_rates = sta->supp_rates; 1377189251Ssam sta->supp_rates &= supp_rates; 1378189251Ssam if (sta->supp_rates == 0) { 1379189251Ssam /* No matching rates - this should not really happen. 1380189251Ssam * Make sure that at least one rate is marked 1381189251Ssam * supported to avoid issues with TX rate ctrl. */ 1382189251Ssam sta->supp_rates = wpa_s->mlme.supp_rates_bits; 1383189251Ssam } 1384189251Ssam if (sta->supp_rates != prev_rates) { 1385189251Ssam wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set " 1386189251Ssam "for " MACSTR " based on beacon info " 1387189251Ssam "(0x%x & 0x%x -> 0x%x)", 1388189251Ssam MAC2STR(sta->addr), prev_rates, 1389189251Ssam supp_rates, sta->supp_rates); 1390189251Ssam } 1391189251Ssam sta_info_release(local, sta); 1392189251Ssam } 1393189251Ssam#endif 1394189251Ssam 1395189251Ssam if (elems.ssid == NULL) 1396189251Ssam return; 1397189251Ssam 1398189251Ssam if (elems.ds_params && elems.ds_params_len == 1) 1399189251Ssam channel = elems.ds_params[0]; 1400189251Ssam else 1401189251Ssam channel = rx_status->channel; 1402189251Ssam 1403189251Ssam bss = ieee80211_bss_get(wpa_s, mgmt->bssid); 1404189251Ssam if (bss == NULL) { 1405189251Ssam bss = ieee80211_bss_add(wpa_s, mgmt->bssid); 1406189251Ssam if (bss == NULL) 1407189251Ssam return; 1408189251Ssam } else { 1409189251Ssam#if 0 1410189251Ssam /* TODO: order by RSSI? */ 1411189251Ssam spin_lock_bh(&local->sta_bss_lock); 1412189251Ssam list_move_tail(&bss->list, &local->sta_bss_list); 1413189251Ssam spin_unlock_bh(&local->sta_bss_lock); 1414189251Ssam#endif 1415189251Ssam } 1416189251Ssam 1417189251Ssam if (bss->probe_resp && beacon) { 1418189251Ssam /* Do not allow beacon to override data from Probe Response. */ 1419189251Ssam return; 1420189251Ssam } 1421189251Ssam 1422189251Ssam bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); 1423189251Ssam bss->capability = le_to_host16(mgmt->u.beacon.capab_info); 1424189251Ssam 1425189251Ssam if (bss->ie == NULL || bss->ie_len < ie_len) { 1426189251Ssam os_free(bss->ie); 1427189251Ssam bss->ie = os_malloc(ie_len); 1428189251Ssam } 1429189251Ssam if (bss->ie) { 1430189251Ssam os_memcpy(bss->ie, ie_pos, ie_len); 1431189251Ssam bss->ie_len = ie_len; 1432189251Ssam } 1433189251Ssam 1434189251Ssam if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) { 1435189251Ssam os_memcpy(bss->ssid, elems.ssid, elems.ssid_len); 1436189251Ssam bss->ssid_len = elems.ssid_len; 1437189251Ssam } 1438189251Ssam 1439189251Ssam bss->supp_rates_len = 0; 1440189251Ssam if (elems.supp_rates) { 1441189251Ssam clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; 1442189251Ssam if (clen > elems.supp_rates_len) 1443189251Ssam clen = elems.supp_rates_len; 1444189251Ssam os_memcpy(&bss->supp_rates[bss->supp_rates_len], 1445189251Ssam elems.supp_rates, clen); 1446189251Ssam bss->supp_rates_len += clen; 1447189251Ssam } 1448189251Ssam if (elems.ext_supp_rates) { 1449189251Ssam clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; 1450189251Ssam if (clen > elems.ext_supp_rates_len) 1451189251Ssam clen = elems.ext_supp_rates_len; 1452189251Ssam os_memcpy(&bss->supp_rates[bss->supp_rates_len], 1453189251Ssam elems.ext_supp_rates, clen); 1454189251Ssam bss->supp_rates_len += clen; 1455189251Ssam } 1456189251Ssam 1457189251Ssam if (elems.wpa_ie && 1458189251Ssam (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len || 1459189251Ssam os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) { 1460189251Ssam os_free(bss->wpa_ie); 1461189251Ssam bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2); 1462189251Ssam if (bss->wpa_ie) { 1463189251Ssam os_memcpy(bss->wpa_ie, elems.wpa_ie - 2, 1464189251Ssam elems.wpa_ie_len + 2); 1465189251Ssam bss->wpa_ie_len = elems.wpa_ie_len + 2; 1466189251Ssam } else 1467189251Ssam bss->wpa_ie_len = 0; 1468189251Ssam } else if (!elems.wpa_ie && bss->wpa_ie) { 1469189251Ssam os_free(bss->wpa_ie); 1470189251Ssam bss->wpa_ie = NULL; 1471189251Ssam bss->wpa_ie_len = 0; 1472189251Ssam } 1473189251Ssam 1474189251Ssam if (elems.rsn_ie && 1475189251Ssam (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len || 1476189251Ssam os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) { 1477189251Ssam os_free(bss->rsn_ie); 1478189251Ssam bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2); 1479189251Ssam if (bss->rsn_ie) { 1480189251Ssam os_memcpy(bss->rsn_ie, elems.rsn_ie - 2, 1481189251Ssam elems.rsn_ie_len + 2); 1482189251Ssam bss->rsn_ie_len = elems.rsn_ie_len + 2; 1483189251Ssam } else 1484189251Ssam bss->rsn_ie_len = 0; 1485189251Ssam } else if (!elems.rsn_ie && bss->rsn_ie) { 1486189251Ssam os_free(bss->rsn_ie); 1487189251Ssam bss->rsn_ie = NULL; 1488189251Ssam bss->rsn_ie_len = 0; 1489189251Ssam } 1490189251Ssam 1491189251Ssam if (elems.wme && 1492189251Ssam (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wme_len || 1493189251Ssam os_memcmp(bss->wmm_ie, elems.wme, elems.wme_len))) { 1494189251Ssam os_free(bss->wmm_ie); 1495189251Ssam bss->wmm_ie = os_malloc(elems.wme_len + 2); 1496189251Ssam if (bss->wmm_ie) { 1497189251Ssam os_memcpy(bss->wmm_ie, elems.wme - 2, 1498189251Ssam elems.wme_len + 2); 1499189251Ssam bss->wmm_ie_len = elems.wme_len + 2; 1500189251Ssam } else 1501189251Ssam bss->wmm_ie_len = 0; 1502189251Ssam } else if (!elems.wme && bss->wmm_ie) { 1503189251Ssam os_free(bss->wmm_ie); 1504189251Ssam bss->wmm_ie = NULL; 1505189251Ssam bss->wmm_ie_len = 0; 1506189251Ssam } 1507189251Ssam 1508189251Ssam#ifdef CONFIG_IEEE80211R 1509189251Ssam if (elems.mdie && 1510189251Ssam (bss->mdie == NULL || bss->mdie_len != elems.mdie_len || 1511189251Ssam os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) { 1512189251Ssam os_free(bss->mdie); 1513189251Ssam bss->mdie = os_malloc(elems.mdie_len + 2); 1514189251Ssam if (bss->mdie) { 1515189251Ssam os_memcpy(bss->mdie, elems.mdie - 2, 1516189251Ssam elems.mdie_len + 2); 1517189251Ssam bss->mdie_len = elems.mdie_len + 2; 1518189251Ssam } else 1519189251Ssam bss->mdie_len = 0; 1520189251Ssam } else if (!elems.mdie && bss->mdie) { 1521189251Ssam os_free(bss->mdie); 1522189251Ssam bss->mdie = NULL; 1523189251Ssam bss->mdie_len = 0; 1524189251Ssam } 1525189251Ssam#endif /* CONFIG_IEEE80211R */ 1526189251Ssam 1527189251Ssam bss->hw_mode = wpa_s->mlme.phymode; 1528189251Ssam bss->channel = channel; 1529189251Ssam bss->freq = wpa_s->mlme.freq; 1530189251Ssam if (channel != wpa_s->mlme.channel && 1531189251Ssam (wpa_s->mlme.phymode == WPA_MODE_IEEE80211G || 1532189251Ssam wpa_s->mlme.phymode == WPA_MODE_IEEE80211B) && 1533189251Ssam channel >= 1 && channel <= 14) { 1534189251Ssam static const int freq_list[] = { 1535189251Ssam 2412, 2417, 2422, 2427, 2432, 2437, 2442, 1536189251Ssam 2447, 2452, 2457, 2462, 2467, 2472, 2484 1537189251Ssam }; 1538189251Ssam /* IEEE 802.11g/b mode can receive packets from neighboring 1539189251Ssam * channels, so map the channel into frequency. */ 1540189251Ssam bss->freq = freq_list[channel - 1]; 1541189251Ssam } 1542189251Ssam bss->timestamp = timestamp; 1543189251Ssam os_get_time(&bss->last_update); 1544189251Ssam bss->rssi = rx_status->ssi; 1545189251Ssam if (!beacon) 1546189251Ssam bss->probe_resp++; 1547189251Ssam} 1548189251Ssam 1549189251Ssam 1550189251Ssamstatic void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s, 1551189251Ssam struct ieee80211_mgmt *mgmt, 1552189251Ssam size_t len, 1553189251Ssam struct ieee80211_rx_status *rx_status) 1554189251Ssam{ 1555189251Ssam ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0); 1556189251Ssam} 1557189251Ssam 1558189251Ssam 1559189251Ssamstatic void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s, 1560189251Ssam struct ieee80211_mgmt *mgmt, 1561189251Ssam size_t len, 1562189251Ssam struct ieee80211_rx_status *rx_status) 1563189251Ssam{ 1564189251Ssam int use_protection; 1565189251Ssam size_t baselen; 1566189251Ssam struct ieee802_11_elems elems; 1567189251Ssam 1568189251Ssam ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1); 1569189251Ssam 1570189251Ssam if (!wpa_s->mlme.associated || 1571189251Ssam os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) 1572189251Ssam return; 1573189251Ssam 1574189251Ssam /* Process beacon from the current BSS */ 1575189251Ssam baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; 1576189251Ssam if (baselen > len) 1577189251Ssam return; 1578189251Ssam 1579189251Ssam if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, 1580189251Ssam &elems, 0) == ParseFailed) 1581189251Ssam return; 1582189251Ssam 1583189251Ssam use_protection = 0; 1584189251Ssam if (elems.erp_info && elems.erp_info_len >= 1) { 1585189251Ssam use_protection = 1586189251Ssam (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0; 1587189251Ssam } 1588189251Ssam 1589189251Ssam if (use_protection != !!wpa_s->mlme.use_protection) { 1590189251Ssam wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR 1591189251Ssam ")", 1592189251Ssam use_protection ? "enabled" : "disabled", 1593189251Ssam MAC2STR(wpa_s->bssid)); 1594189251Ssam wpa_s->mlme.use_protection = use_protection ? 1 : 0; 1595189251Ssam wpa_s->mlme.cts_protect_erp_frames = use_protection; 1596189251Ssam } 1597189251Ssam 1598189251Ssam if (elems.wme && wpa_s->mlme.wmm_enabled) { 1599189251Ssam ieee80211_sta_wmm_params(wpa_s, elems.wme, 1600189251Ssam elems.wme_len); 1601189251Ssam } 1602189251Ssam} 1603189251Ssam 1604189251Ssam 1605189251Ssamstatic void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s, 1606189251Ssam struct ieee80211_mgmt *mgmt, 1607189251Ssam size_t len, 1608189251Ssam struct ieee80211_rx_status *rx_status) 1609189251Ssam{ 1610189251Ssam int tx_last_beacon, adhoc; 1611189251Ssam#if 0 /* FIX */ 1612189251Ssam struct ieee80211_mgmt *resp; 1613189251Ssam#endif 1614189251Ssam u8 *pos, *end; 1615189251Ssam struct wpa_ssid *ssid = wpa_s->current_ssid; 1616189251Ssam 1617189251Ssam adhoc = ssid && ssid->mode == 1; 1618189251Ssam 1619189251Ssam if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED || 1620189251Ssam len < 24 + 2 || wpa_s->mlme.probe_resp == NULL) 1621189251Ssam return; 1622189251Ssam 1623189251Ssam#if 0 /* FIX */ 1624189251Ssam if (local->hw->tx_last_beacon) 1625189251Ssam tx_last_beacon = local->hw->tx_last_beacon(local->mdev); 1626189251Ssam else 1627189251Ssam#endif 1628189251Ssam tx_last_beacon = 1; 1629189251Ssam 1630189251Ssam#ifdef IEEE80211_IBSS_DEBUG 1631189251Ssam wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR 1632189251Ssam " BSSID=" MACSTR " (tx_last_beacon=%d)", 1633189251Ssam MAC2STR(mgmt->sa), MAC2STR(mgmt->da), 1634189251Ssam MAC2STR(mgmt->bssid), tx_last_beacon); 1635189251Ssam#endif /* IEEE80211_IBSS_DEBUG */ 1636189251Ssam 1637189251Ssam if (!tx_last_beacon) 1638189251Ssam return; 1639189251Ssam 1640189251Ssam if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 && 1641189251Ssam os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) 1642189251Ssam return; 1643189251Ssam 1644189251Ssam end = ((u8 *) mgmt) + len; 1645189251Ssam pos = mgmt->u.probe_req.variable; 1646189251Ssam if (pos[0] != WLAN_EID_SSID || 1647189251Ssam pos + 2 + pos[1] > end) { 1648189251Ssam wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from " 1649189251Ssam MACSTR, MAC2STR(mgmt->sa)); 1650189251Ssam return; 1651189251Ssam } 1652189251Ssam if (pos[1] != 0 && 1653189251Ssam (pos[1] != wpa_s->mlme.ssid_len || 1654189251Ssam os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0)) 1655189251Ssam { 1656189251Ssam /* Ignore ProbeReq for foreign SSID */ 1657189251Ssam return; 1658189251Ssam } 1659189251Ssam 1660189251Ssam#if 0 /* FIX */ 1661189251Ssam /* Reply with ProbeResp */ 1662189251Ssam skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC); 1663189251Ssam if (skb == NULL) 1664189251Ssam return; 1665189251Ssam 1666189251Ssam resp = (struct ieee80211_mgmt *) skb->data; 1667189251Ssam os_memcpy(resp->da, mgmt->sa, ETH_ALEN); 1668189251Ssam#ifdef IEEE80211_IBSS_DEBUG 1669189251Ssam wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR, 1670189251Ssam MAC2STR(resp->da)); 1671189251Ssam#endif /* IEEE80211_IBSS_DEBUG */ 1672189251Ssam ieee80211_sta_tx(wpa_s, skb, 0, 1); 1673189251Ssam#endif 1674189251Ssam} 1675189251Ssam 1676189251Ssam 1677189251Ssam#ifdef CONFIG_IEEE80211R 1678189251Ssamstatic void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s, 1679189251Ssam struct ieee80211_mgmt *mgmt, 1680189251Ssam size_t len, 1681189251Ssam struct ieee80211_rx_status *rx_status) 1682189251Ssam{ 1683189251Ssam union wpa_event_data data; 1684189251Ssam u16 status; 1685189251Ssam u8 *sta_addr, *target_ap_addr; 1686189251Ssam 1687189251Ssam if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) { 1688189251Ssam wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame"); 1689189251Ssam return; 1690189251Ssam } 1691189251Ssam 1692189251Ssam /* 1693189251Ssam * Only FT Action Response is needed for now since reservation 1694189251Ssam * protocol is not supported. 1695189251Ssam */ 1696189251Ssam if (mgmt->u.action.u.ft_action_resp.action != 2) { 1697189251Ssam wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d", 1698189251Ssam mgmt->u.action.u.ft_action_resp.action); 1699189251Ssam return; 1700189251Ssam } 1701189251Ssam 1702189251Ssam status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code); 1703189251Ssam sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr; 1704189251Ssam target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr; 1705189251Ssam wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR 1706189251Ssam " TargetAP " MACSTR " Status Code %d", 1707189251Ssam MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); 1708189251Ssam if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { 1709189251Ssam wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR 1710189251Ssam " in FT Action Response", MAC2STR(sta_addr)); 1711189251Ssam return; 1712189251Ssam 1713189251Ssam } 1714189251Ssam 1715189251Ssam if (status) { 1716189251Ssam wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates " 1717189251Ssam "failure (status code %d)", status); 1718189251Ssam /* TODO: report error to FT code(?) */ 1719189251Ssam return; 1720189251Ssam } 1721189251Ssam 1722189251Ssam os_memset(&data, 0, sizeof(data)); 1723189251Ssam data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable; 1724189251Ssam data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable - 1725189251Ssam (u8 *) mgmt); 1726189251Ssam data.ft_ies.ft_action = 1; 1727189251Ssam os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN); 1728189251Ssam wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); 1729189251Ssam /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed 1730189251Ssam * successfully */ 1731189251Ssam wpa_s->mlme.prev_bssid_set = 1; 1732189251Ssam wpa_s->mlme.auth_alg = WLAN_AUTH_FT; 1733189251Ssam os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); 1734189251Ssam os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN); 1735189251Ssam ieee80211_associate(wpa_s); 1736189251Ssam} 1737189251Ssam#endif /* CONFIG_IEEE80211R */ 1738189251Ssam 1739189251Ssam 1740189251Ssam#ifdef CONFIG_IEEE80211W 1741189251Ssam 1742189251Ssam/* MLME-SAQuery.response */ 1743189251Ssamstatic int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s, 1744189251Ssam const u8 *addr, const u8 *trans_id) 1745189251Ssam{ 1746189251Ssam struct ieee80211_mgmt *mgmt; 1747189251Ssam int res; 1748189251Ssam size_t len; 1749189251Ssam 1750189251Ssam mgmt = os_zalloc(sizeof(*mgmt)); 1751189251Ssam if (mgmt == NULL) { 1752189251Ssam wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 1753189251Ssam "SA Query action frame"); 1754189251Ssam return -1; 1755189251Ssam } 1756189251Ssam 1757189251Ssam len = 24; 1758189251Ssam os_memcpy(mgmt->da, addr, ETH_ALEN); 1759189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 1760189251Ssam os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 1761189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 1762189251Ssam WLAN_FC_STYPE_ACTION); 1763189251Ssam mgmt->u.action.category = WLAN_ACTION_SA_QUERY; 1764189251Ssam mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE; 1765189251Ssam os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id, 1766189251Ssam WLAN_SA_QUERY_TR_ID_LEN); 1767189251Ssam len += 1 + sizeof(mgmt->u.action.u.sa_query_resp); 1768189251Ssam 1769189251Ssam res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len); 1770189251Ssam os_free(mgmt); 1771189251Ssam 1772189251Ssam return res; 1773189251Ssam} 1774189251Ssam 1775189251Ssam 1776189251Ssamstatic void ieee80211_rx_mgmt_sa_query_action( 1777189251Ssam struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, 1778189251Ssam struct ieee80211_rx_status *rx_status) 1779189251Ssam{ 1780189251Ssam if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) { 1781189251Ssam wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame"); 1782189251Ssam return; 1783189251Ssam } 1784189251Ssam 1785189251Ssam if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) { 1786189251Ssam wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d", 1787189251Ssam mgmt->u.action.u.sa_query_req.action); 1788189251Ssam return; 1789189251Ssam } 1790189251Ssam 1791189251Ssam if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { 1792189251Ssam wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown " 1793189251Ssam "source " MACSTR, MAC2STR(mgmt->sa)); 1794189251Ssam return; 1795189251Ssam } 1796189251Ssam 1797189251Ssam if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) { 1798189251Ssam wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during " 1799189251Ssam "association process"); 1800189251Ssam return; 1801189251Ssam } 1802189251Ssam 1803189251Ssam wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request"); 1804189251Ssam ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u. 1805189251Ssam sa_query_req.trans_id); 1806189251Ssam} 1807189251Ssam 1808189251Ssam#endif /* CONFIG_IEEE80211W */ 1809189251Ssam 1810189251Ssam 1811189251Ssamstatic void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s, 1812189251Ssam struct ieee80211_mgmt *mgmt, 1813189251Ssam size_t len, 1814189251Ssam struct ieee80211_rx_status *rx_status) 1815189251Ssam{ 1816189251Ssam wpa_printf(MSG_DEBUG, "MLME: received Action frame"); 1817189251Ssam 1818189251Ssam if (len < 25) 1819189251Ssam return; 1820189251Ssam 1821189251Ssam switch (mgmt->u.action.category) { 1822189251Ssam#ifdef CONFIG_IEEE80211R 1823189251Ssam case WLAN_ACTION_FT: 1824189251Ssam ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status); 1825189251Ssam break; 1826189251Ssam#endif /* CONFIG_IEEE80211R */ 1827189251Ssam#ifdef CONFIG_IEEE80211W 1828189251Ssam case WLAN_ACTION_SA_QUERY: 1829189251Ssam ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status); 1830189251Ssam break; 1831189251Ssam#endif /* CONFIG_IEEE80211W */ 1832189251Ssam default: 1833189251Ssam wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d", 1834189251Ssam mgmt->u.action.category); 1835189251Ssam break; 1836189251Ssam } 1837189251Ssam} 1838189251Ssam 1839189251Ssam 1840189251Ssamstatic void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s, 1841189251Ssam const u8 *buf, size_t len, 1842189251Ssam struct ieee80211_rx_status *rx_status) 1843189251Ssam{ 1844189251Ssam struct ieee80211_mgmt *mgmt; 1845189251Ssam u16 fc; 1846189251Ssam 1847189251Ssam if (len < 24) 1848189251Ssam return; 1849189251Ssam 1850189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 1851189251Ssam fc = le_to_host16(mgmt->frame_control); 1852189251Ssam 1853189251Ssam switch (WLAN_FC_GET_STYPE(fc)) { 1854189251Ssam case WLAN_FC_STYPE_PROBE_REQ: 1855189251Ssam ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status); 1856189251Ssam break; 1857189251Ssam case WLAN_FC_STYPE_PROBE_RESP: 1858189251Ssam ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status); 1859189251Ssam break; 1860189251Ssam case WLAN_FC_STYPE_BEACON: 1861189251Ssam ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); 1862189251Ssam break; 1863189251Ssam case WLAN_FC_STYPE_AUTH: 1864189251Ssam ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status); 1865189251Ssam break; 1866189251Ssam case WLAN_FC_STYPE_ASSOC_RESP: 1867189251Ssam ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0); 1868189251Ssam break; 1869189251Ssam case WLAN_FC_STYPE_REASSOC_RESP: 1870189251Ssam ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1); 1871189251Ssam break; 1872189251Ssam case WLAN_FC_STYPE_DEAUTH: 1873189251Ssam ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status); 1874189251Ssam break; 1875189251Ssam case WLAN_FC_STYPE_DISASSOC: 1876189251Ssam ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status); 1877189251Ssam break; 1878189251Ssam case WLAN_FC_STYPE_ACTION: 1879189251Ssam ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status); 1880189251Ssam break; 1881189251Ssam default: 1882189251Ssam wpa_printf(MSG_DEBUG, "MLME: received unknown management " 1883189251Ssam "frame - stype=%d", WLAN_FC_GET_STYPE(fc)); 1884189251Ssam break; 1885189251Ssam } 1886189251Ssam} 1887189251Ssam 1888189251Ssam 1889189251Ssamstatic void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s, 1890189251Ssam const u8 *buf, size_t len, 1891189251Ssam struct ieee80211_rx_status *rx_status) 1892189251Ssam{ 1893189251Ssam struct ieee80211_mgmt *mgmt; 1894189251Ssam u16 fc; 1895189251Ssam 1896189251Ssam if (len < 24) 1897189251Ssam return; 1898189251Ssam 1899189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 1900189251Ssam fc = le_to_host16(mgmt->frame_control); 1901189251Ssam 1902189251Ssam if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) { 1903189251Ssam if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { 1904189251Ssam ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, 1905189251Ssam len, rx_status); 1906189251Ssam } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) { 1907189251Ssam ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); 1908189251Ssam } 1909189251Ssam } 1910189251Ssam} 1911189251Ssam 1912189251Ssam 1913189251Ssamstatic int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s) 1914189251Ssam{ 1915189251Ssam int active = 0; 1916189251Ssam 1917189251Ssam#if 0 /* FIX */ 1918189251Ssam list_for_each(ptr, &local->sta_list) { 1919189251Ssam sta = list_entry(ptr, struct sta_info, list); 1920189251Ssam if (sta->dev == dev && 1921189251Ssam time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, 1922189251Ssam jiffies)) { 1923189251Ssam active++; 1924189251Ssam break; 1925189251Ssam } 1926189251Ssam } 1927189251Ssam#endif 1928189251Ssam 1929189251Ssam return active; 1930189251Ssam} 1931189251Ssam 1932189251Ssam 1933189251Ssamstatic void ieee80211_sta_expire(struct wpa_supplicant *wpa_s) 1934189251Ssam{ 1935189251Ssam#if 0 /* FIX */ 1936189251Ssam list_for_each_safe(ptr, n, &local->sta_list) { 1937189251Ssam sta = list_entry(ptr, struct sta_info, list); 1938189251Ssam if (time_after(jiffies, sta->last_rx + 1939189251Ssam IEEE80211_IBSS_INACTIVITY_LIMIT)) { 1940189251Ssam wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA " 1941189251Ssam MACSTR, MAC2STR(sta->addr)); 1942189251Ssam sta_info_free(local, sta, 1); 1943189251Ssam } 1944189251Ssam } 1945189251Ssam#endif 1946189251Ssam} 1947189251Ssam 1948189251Ssam 1949189251Ssamstatic void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s) 1950189251Ssam{ 1951189251Ssam ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); 1952189251Ssam 1953189251Ssam ieee80211_sta_expire(wpa_s); 1954189251Ssam if (ieee80211_sta_active_ibss(wpa_s)) 1955189251Ssam return; 1956189251Ssam 1957189251Ssam wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for " 1958189251Ssam "other IBSS networks with same SSID (merge)"); 1959189251Ssam ieee80211_sta_req_scan(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); 1960189251Ssam} 1961189251Ssam 1962189251Ssam 1963189251Ssamstatic void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx) 1964189251Ssam{ 1965189251Ssam struct wpa_supplicant *wpa_s = eloop_ctx; 1966189251Ssam 1967189251Ssam switch (wpa_s->mlme.state) { 1968189251Ssam case IEEE80211_DISABLED: 1969189251Ssam break; 1970189251Ssam case IEEE80211_AUTHENTICATE: 1971189251Ssam ieee80211_authenticate(wpa_s); 1972189251Ssam break; 1973189251Ssam case IEEE80211_ASSOCIATE: 1974189251Ssam ieee80211_associate(wpa_s); 1975189251Ssam break; 1976189251Ssam case IEEE80211_ASSOCIATED: 1977189251Ssam ieee80211_associated(wpa_s); 1978189251Ssam break; 1979189251Ssam case IEEE80211_IBSS_SEARCH: 1980189251Ssam ieee80211_sta_find_ibss(wpa_s); 1981189251Ssam break; 1982189251Ssam case IEEE80211_IBSS_JOINED: 1983189251Ssam ieee80211_sta_merge_ibss(wpa_s); 1984189251Ssam break; 1985189251Ssam default: 1986189251Ssam wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d", 1987189251Ssam wpa_s->mlme.state); 1988189251Ssam break; 1989189251Ssam } 1990189251Ssam 1991189251Ssam if (ieee80211_privacy_mismatch(wpa_s)) { 1992189251Ssam wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch " 1993189251Ssam "and mixed-cell disabled - disassociate"); 1994189251Ssam 1995189251Ssam ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED); 1996189251Ssam ieee80211_set_associated(wpa_s, 0); 1997189251Ssam } 1998189251Ssam} 1999189251Ssam 2000189251Ssam 2001189251Ssamstatic void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s) 2002189251Ssam{ 2003189251Ssam struct wpa_ssid *ssid = wpa_s->current_ssid; 2004189251Ssam if (ssid && ssid->mode != 0) 2005189251Ssam return; 2006189251Ssam 2007189251Ssam#if 0 /* FIX */ 2008189251Ssam if (local->hw->reset_tsf) { 2009189251Ssam /* Reset own TSF to allow time synchronization work. */ 2010189251Ssam local->hw->reset_tsf(local->mdev); 2011189251Ssam } 2012189251Ssam#endif 2013189251Ssam 2014189251Ssam wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */ 2015189251Ssam 2016189251Ssam 2017189251Ssam if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_OPEN) 2018189251Ssam wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; 2019189251Ssam else if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) 2020189251Ssam wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY; 2021189251Ssam else if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_LEAP) 2022189251Ssam wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP; 2023189251Ssam else 2024189251Ssam wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; 2025189251Ssam wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d", 2026189251Ssam wpa_s->mlme.auth_alg); 2027189251Ssam wpa_s->mlme.auth_transaction = -1; 2028189251Ssam wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0; 2029189251Ssam ieee80211_authenticate(wpa_s); 2030189251Ssam} 2031189251Ssam 2032189251Ssam 2033189251Ssamstatic int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s) 2034189251Ssam{ 2035189251Ssam#if 0 /* FIX */ 2036189251Ssam int m, c; 2037189251Ssam 2038189251Ssam for (m = 0; m < local->hw->num_modes; m++) { 2039189251Ssam struct ieee80211_hw_modes *mode = &local->hw->modes[m]; 2040189251Ssam if (mode->mode != local->conf.phymode) 2041189251Ssam continue; 2042189251Ssam for (c = 0; c < mode->num_channels; c++) { 2043189251Ssam struct ieee80211_channel *chan = &mode->channels[c]; 2044189251Ssam if (chan->flag & IEEE80211_CHAN_W_SCAN && 2045189251Ssam chan->chan == local->conf.channel) { 2046189251Ssam if (chan->flag & IEEE80211_CHAN_W_IBSS) 2047189251Ssam return 1; 2048189251Ssam break; 2049189251Ssam } 2050189251Ssam } 2051189251Ssam } 2052189251Ssam#endif 2053189251Ssam 2054189251Ssam return 0; 2055189251Ssam} 2056189251Ssam 2057189251Ssam 2058189251Ssamstatic int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s, 2059189251Ssam struct ieee80211_sta_bss *bss) 2060189251Ssam{ 2061189251Ssam int res = 0, rates, done = 0; 2062189251Ssam struct ieee80211_mgmt *mgmt; 2063189251Ssam#if 0 /* FIX */ 2064189251Ssam struct ieee80211_tx_control control; 2065189251Ssam struct ieee80211_rate *rate; 2066189251Ssam struct rate_control_extra extra; 2067189251Ssam#endif 2068189251Ssam u8 *pos, *buf; 2069189251Ssam size_t len; 2070189251Ssam 2071189251Ssam /* Remove possible STA entries from other IBSS networks. */ 2072189251Ssam#if 0 /* FIX */ 2073189251Ssam sta_info_flush(local, NULL); 2074189251Ssam 2075189251Ssam if (local->hw->reset_tsf) { 2076189251Ssam /* Reset own TSF to allow time synchronization work. */ 2077189251Ssam local->hw->reset_tsf(local->mdev); 2078189251Ssam } 2079189251Ssam#endif 2080189251Ssam os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN); 2081189251Ssam 2082189251Ssam#if 0 /* FIX */ 2083189251Ssam local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10; 2084189251Ssam 2085189251Ssam sdata->drop_unencrypted = bss->capability & 2086189251Ssam host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0; 2087189251Ssam#endif 2088189251Ssam 2089189251Ssam#if 0 /* FIX */ 2090189251Ssam os_memset(&rq, 0, sizeof(rq)); 2091189251Ssam rq.m = bss->freq * 100000; 2092189251Ssam rq.e = 1; 2093189251Ssam res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL); 2094189251Ssam#endif 2095189251Ssam 2096189251Ssam if (!ieee80211_ibss_allowed(wpa_s)) { 2097189251Ssam#if 0 /* FIX */ 2098189251Ssam wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d " 2099189251Ssam "(%d MHz)", local->conf.channel, 2100189251Ssam local->conf.freq); 2101189251Ssam#endif 2102189251Ssam return -1; 2103189251Ssam } 2104189251Ssam 2105189251Ssam /* Set beacon template based on scan results */ 2106189251Ssam buf = os_malloc(400); 2107189251Ssam len = 0; 2108189251Ssam do { 2109189251Ssam if (buf == NULL) 2110189251Ssam break; 2111189251Ssam 2112189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 2113189251Ssam len += 24 + sizeof(mgmt->u.beacon); 2114189251Ssam os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); 2115189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 2116189251Ssam WLAN_FC_STYPE_BEACON); 2117189251Ssam os_memset(mgmt->da, 0xff, ETH_ALEN); 2118189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 2119189251Ssam os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 2120189251Ssam#if 0 /* FIX */ 2121189251Ssam mgmt->u.beacon.beacon_int = 2122189251Ssam host_to_le16(local->conf.beacon_int); 2123189251Ssam#endif 2124189251Ssam mgmt->u.beacon.capab_info = host_to_le16(bss->capability); 2125189251Ssam 2126189251Ssam pos = buf + len; 2127189251Ssam len += 2 + wpa_s->mlme.ssid_len; 2128189251Ssam *pos++ = WLAN_EID_SSID; 2129189251Ssam *pos++ = wpa_s->mlme.ssid_len; 2130189251Ssam os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); 2131189251Ssam 2132189251Ssam rates = bss->supp_rates_len; 2133189251Ssam if (rates > 8) 2134189251Ssam rates = 8; 2135189251Ssam pos = buf + len; 2136189251Ssam len += 2 + rates; 2137189251Ssam *pos++ = WLAN_EID_SUPP_RATES; 2138189251Ssam *pos++ = rates; 2139189251Ssam os_memcpy(pos, bss->supp_rates, rates); 2140189251Ssam 2141189251Ssam pos = buf + len; 2142189251Ssam len += 2 + 1; 2143189251Ssam *pos++ = WLAN_EID_DS_PARAMS; 2144189251Ssam *pos++ = 1; 2145189251Ssam *pos++ = bss->channel; 2146189251Ssam 2147189251Ssam pos = buf + len; 2148189251Ssam len += 2 + 2; 2149189251Ssam *pos++ = WLAN_EID_IBSS_PARAMS; 2150189251Ssam *pos++ = 2; 2151189251Ssam /* FIX: set ATIM window based on scan results */ 2152189251Ssam *pos++ = 0; 2153189251Ssam *pos++ = 0; 2154189251Ssam 2155189251Ssam if (bss->supp_rates_len > 8) { 2156189251Ssam rates = bss->supp_rates_len - 8; 2157189251Ssam pos = buf + len; 2158189251Ssam len += 2 + rates; 2159189251Ssam *pos++ = WLAN_EID_EXT_SUPP_RATES; 2160189251Ssam *pos++ = rates; 2161189251Ssam os_memcpy(pos, &bss->supp_rates[8], rates); 2162189251Ssam } 2163189251Ssam 2164189251Ssam#if 0 /* FIX */ 2165189251Ssam os_memset(&control, 0, sizeof(control)); 2166189251Ssam control.pkt_type = PKT_PROBE_RESP; 2167189251Ssam os_memset(&extra, 0, sizeof(extra)); 2168189251Ssam extra.endidx = local->num_curr_rates; 2169189251Ssam rate = rate_control_get_rate(wpa_s, skb, &extra); 2170189251Ssam if (rate == NULL) { 2171189251Ssam wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX " 2172189251Ssam "rate for IBSS beacon"); 2173189251Ssam break; 2174189251Ssam } 2175189251Ssam control.tx_rate = (wpa_s->mlme.short_preamble && 2176189251Ssam (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? 2177189251Ssam rate->val2 : rate->val; 2178189251Ssam control.antenna_sel = local->conf.antenna_sel; 2179189251Ssam control.power_level = local->conf.power_level; 2180189251Ssam control.no_ack = 1; 2181189251Ssam control.retry_limit = 1; 2182189251Ssam control.rts_cts_duration = 0; 2183189251Ssam#endif 2184189251Ssam 2185189251Ssam#if 0 /* FIX */ 2186189251Ssam wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC); 2187189251Ssam if (wpa_s->mlme.probe_resp) { 2188189251Ssam mgmt = (struct ieee80211_mgmt *) 2189189251Ssam wpa_s->mlme.probe_resp->data; 2190189251Ssam mgmt->frame_control = 2191189251Ssam IEEE80211_FC(WLAN_FC_TYPE_MGMT, 2192189251Ssam WLAN_FC_STYPE_PROBE_RESP); 2193189251Ssam } else { 2194189251Ssam wpa_printf(MSG_DEBUG, "MLME: Could not allocate " 2195189251Ssam "ProbeResp template for IBSS"); 2196189251Ssam } 2197189251Ssam 2198189251Ssam if (local->hw->beacon_update && 2199189251Ssam local->hw->beacon_update(wpa_s, skb, &control) == 0) { 2200189251Ssam wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon " 2201189251Ssam "template based on scan results"); 2202189251Ssam skb = NULL; 2203189251Ssam } 2204189251Ssam 2205189251Ssam rates = 0; 2206189251Ssam for (i = 0; i < bss->supp_rates_len; i++) { 2207189251Ssam int rate = (bss->supp_rates[i] & 0x7f) * 5; 2208189251Ssam if (local->conf.phymode == MODE_ATHEROS_TURBO) 2209189251Ssam rate *= 2; 2210189251Ssam for (j = 0; j < local->num_curr_rates; j++) 2211189251Ssam if (local->curr_rates[j].rate == rate) 2212189251Ssam rates |= BIT(j); 2213189251Ssam } 2214189251Ssam wpa_s->mlme.supp_rates_bits = rates; 2215189251Ssam#endif 2216189251Ssam done = 1; 2217189251Ssam } while (0); 2218189251Ssam 2219189251Ssam os_free(buf); 2220189251Ssam if (!done) { 2221189251Ssam wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon " 2222189251Ssam "template"); 2223189251Ssam } 2224189251Ssam 2225189251Ssam wpa_s->mlme.state = IEEE80211_IBSS_JOINED; 2226189251Ssam ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); 2227189251Ssam 2228189251Ssam return res; 2229189251Ssam} 2230189251Ssam 2231189251Ssam 2232189251Ssam#if 0 /* FIX */ 2233189251Ssamstatic int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s) 2234189251Ssam{ 2235189251Ssam struct ieee80211_sta_bss *bss; 2236189251Ssam u8 bssid[ETH_ALEN], *pos; 2237189251Ssam int i; 2238189251Ssam 2239189251Ssam#if 0 2240189251Ssam /* Easier testing, use fixed BSSID. */ 2241189251Ssam os_memset(bssid, 0xfe, ETH_ALEN); 2242189251Ssam#else 2243189251Ssam /* Generate random, not broadcast, locally administered BSSID. Mix in 2244189251Ssam * own MAC address to make sure that devices that do not have proper 2245189251Ssam * random number generator get different BSSID. */ 2246189251Ssam os_get_random(bssid, ETH_ALEN); 2247189251Ssam for (i = 0; i < ETH_ALEN; i++) 2248189251Ssam bssid[i] ^= wpa_s->own_addr[i]; 2249189251Ssam bssid[0] &= ~0x01; 2250189251Ssam bssid[0] |= 0x02; 2251189251Ssam#endif 2252189251Ssam 2253189251Ssam wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID " 2254189251Ssam MACSTR "", MAC2STR(bssid)); 2255189251Ssam 2256189251Ssam bss = ieee80211_bss_add(wpa_s, bssid); 2257189251Ssam if (bss == NULL) 2258189251Ssam return -ENOMEM; 2259189251Ssam 2260189251Ssam#if 0 /* FIX */ 2261189251Ssam if (local->conf.beacon_int == 0) 2262189251Ssam local->conf.beacon_int = 100; 2263189251Ssam bss->beacon_int = local->conf.beacon_int; 2264189251Ssam bss->hw_mode = local->conf.phymode; 2265189251Ssam bss->channel = local->conf.channel; 2266189251Ssam bss->freq = local->conf.freq; 2267189251Ssam#endif 2268189251Ssam os_get_time(&bss->last_update); 2269189251Ssam bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS); 2270189251Ssam#if 0 /* FIX */ 2271189251Ssam if (sdata->default_key) { 2272189251Ssam bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY); 2273189251Ssam } else 2274189251Ssam sdata->drop_unencrypted = 0; 2275189251Ssam bss->supp_rates_len = local->num_curr_rates; 2276189251Ssam#endif 2277189251Ssam pos = bss->supp_rates; 2278189251Ssam#if 0 /* FIX */ 2279189251Ssam for (i = 0; i < local->num_curr_rates; i++) { 2280189251Ssam int rate = local->curr_rates[i].rate; 2281189251Ssam if (local->conf.phymode == MODE_ATHEROS_TURBO) 2282189251Ssam rate /= 2; 2283189251Ssam *pos++ = (u8) (rate / 5); 2284189251Ssam } 2285189251Ssam#endif 2286189251Ssam 2287189251Ssam return ieee80211_sta_join_ibss(wpa_s, bss); 2288189251Ssam} 2289189251Ssam#endif 2290189251Ssam 2291189251Ssam 2292189251Ssamstatic int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s) 2293189251Ssam{ 2294189251Ssam struct ieee80211_sta_bss *bss; 2295189251Ssam int found = 0; 2296189251Ssam u8 bssid[ETH_ALEN]; 2297189251Ssam int active_ibss; 2298189251Ssam struct os_time now; 2299189251Ssam 2300189251Ssam if (wpa_s->mlme.ssid_len == 0) 2301189251Ssam return -EINVAL; 2302189251Ssam 2303189251Ssam active_ibss = ieee80211_sta_active_ibss(wpa_s); 2304189251Ssam#ifdef IEEE80211_IBSS_DEBUG 2305189251Ssam wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)", 2306189251Ssam active_ibss); 2307189251Ssam#endif /* IEEE80211_IBSS_DEBUG */ 2308189251Ssam for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { 2309189251Ssam if (wpa_s->mlme.ssid_len != bss->ssid_len || 2310189251Ssam os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0 2311189251Ssam || !(bss->capability & WLAN_CAPABILITY_IBSS)) 2312189251Ssam continue; 2313189251Ssam#ifdef IEEE80211_IBSS_DEBUG 2314189251Ssam wpa_printf(MSG_DEBUG, " bssid=" MACSTR " found", 2315189251Ssam MAC2STR(bss->bssid)); 2316189251Ssam#endif /* IEEE80211_IBSS_DEBUG */ 2317189251Ssam os_memcpy(bssid, bss->bssid, ETH_ALEN); 2318189251Ssam found = 1; 2319189251Ssam if (active_ibss || 2320189251Ssam os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) 2321189251Ssam break; 2322189251Ssam } 2323189251Ssam 2324189251Ssam#ifdef IEEE80211_IBSS_DEBUG 2325189251Ssam wpa_printf(MSG_DEBUG, " sta_find_ibss: selected " MACSTR " current " 2326189251Ssam MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid)); 2327189251Ssam#endif /* IEEE80211_IBSS_DEBUG */ 2328189251Ssam if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 && 2329189251Ssam (bss = ieee80211_bss_get(wpa_s, bssid))) { 2330189251Ssam wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR 2331189251Ssam " based on configured SSID", 2332189251Ssam MAC2STR(bssid)); 2333189251Ssam return ieee80211_sta_join_ibss(wpa_s, bss); 2334189251Ssam } 2335189251Ssam#ifdef IEEE80211_IBSS_DEBUG 2336189251Ssam wpa_printf(MSG_DEBUG, " did not try to join ibss"); 2337189251Ssam#endif /* IEEE80211_IBSS_DEBUG */ 2338189251Ssam 2339189251Ssam /* Selected IBSS not found in current scan results - try to scan */ 2340189251Ssam os_get_time(&now); 2341189251Ssam#if 0 /* FIX */ 2342189251Ssam if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED && 2343189251Ssam !ieee80211_sta_active_ibss(wpa_s)) { 2344189251Ssam ieee80211_reschedule_timer(wpa_s, 2345189251Ssam IEEE80211_IBSS_MERGE_INTERVAL); 2346189251Ssam } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed + 2347189251Ssam IEEE80211_SCAN_INTERVAL)) { 2348189251Ssam wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS " 2349189251Ssam "to join"); 2350189251Ssam return ieee80211_sta_req_scan(wpa_s->mlme.ssid, 2351189251Ssam wpa_s->mlme.ssid_len); 2352189251Ssam } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) { 2353189251Ssam int interval = IEEE80211_SCAN_INTERVAL; 2354189251Ssam 2355189251Ssam if (time_after(jiffies, wpa_s->mlme.ibss_join_req + 2356189251Ssam IEEE80211_IBSS_JOIN_TIMEOUT)) { 2357189251Ssam if (wpa_s->mlme.create_ibss && 2358189251Ssam ieee80211_ibss_allowed(wpa_s)) 2359189251Ssam return ieee80211_sta_create_ibss(wpa_s); 2360189251Ssam if (wpa_s->mlme.create_ibss) { 2361189251Ssam wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed " 2362189251Ssam "on the configured channel %d " 2363189251Ssam "(%d MHz)", 2364189251Ssam local->conf.channel, 2365189251Ssam local->conf.freq); 2366189251Ssam } 2367189251Ssam 2368189251Ssam /* No IBSS found - decrease scan interval and continue 2369189251Ssam * scanning. */ 2370189251Ssam interval = IEEE80211_SCAN_INTERVAL_SLOW; 2371189251Ssam } 2372189251Ssam 2373189251Ssam wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; 2374189251Ssam ieee80211_reschedule_timer(wpa_s, interval); 2375189251Ssam return 0; 2376189251Ssam } 2377189251Ssam#endif 2378189251Ssam 2379189251Ssam return 0; 2380189251Ssam} 2381189251Ssam 2382189251Ssam 2383189251Ssamint ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid, 2384189251Ssam size_t *len) 2385189251Ssam{ 2386189251Ssam os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); 2387189251Ssam *len = wpa_s->mlme.ssid_len; 2388189251Ssam return 0; 2389189251Ssam} 2390189251Ssam 2391189251Ssam 2392189251Ssamint ieee80211_sta_associate(struct wpa_supplicant *wpa_s, 2393189251Ssam struct wpa_driver_associate_params *params) 2394189251Ssam{ 2395189251Ssam struct ieee80211_sta_bss *bss; 2396189251Ssam 2397189251Ssam wpa_s->mlme.bssid_set = 0; 2398189251Ssam wpa_s->mlme.freq = params->freq; 2399189251Ssam if (params->bssid) { 2400189251Ssam os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN); 2401189251Ssam if (!is_zero_ether_addr(params->bssid)) 2402189251Ssam wpa_s->mlme.bssid_set = 1; 2403189251Ssam bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); 2404189251Ssam if (bss) { 2405189251Ssam wpa_s->mlme.phymode = bss->hw_mode; 2406189251Ssam wpa_s->mlme.channel = bss->channel; 2407189251Ssam wpa_s->mlme.freq = bss->freq; 2408189251Ssam } 2409189251Ssam } 2410189251Ssam 2411189251Ssam#if 0 /* FIX */ 2412189251Ssam /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is 2413189251Ssam * not defined. */ 2414189251Ssam if (local->hw->conf_tx) { 2415189251Ssam struct ieee80211_tx_queue_params qparam; 2416189251Ssam int i; 2417189251Ssam 2418189251Ssam os_memset(&qparam, 0, sizeof(qparam)); 2419189251Ssam /* TODO: are these ok defaults for all hw_modes? */ 2420189251Ssam qparam.aifs = 2; 2421189251Ssam qparam.cw_min = 2422189251Ssam local->conf.phymode == MODE_IEEE80211B ? 31 : 15; 2423189251Ssam qparam.cw_max = 1023; 2424189251Ssam qparam.burst_time = 0; 2425189251Ssam for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++) 2426189251Ssam { 2427189251Ssam local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0, 2428189251Ssam &qparam); 2429189251Ssam } 2430189251Ssam /* IBSS uses different parameters for Beacon sending */ 2431189251Ssam qparam.cw_min++; 2432189251Ssam qparam.cw_min *= 2; 2433189251Ssam qparam.cw_min--; 2434189251Ssam local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam); 2435189251Ssam } 2436189251Ssam#endif 2437189251Ssam 2438189251Ssam if (wpa_s->mlme.ssid_len != params->ssid_len || 2439189251Ssam os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0) 2440189251Ssam wpa_s->mlme.prev_bssid_set = 0; 2441189251Ssam os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len); 2442189251Ssam os_memset(wpa_s->mlme.ssid + params->ssid_len, 0, 2443189251Ssam MAX_SSID_LEN - params->ssid_len); 2444189251Ssam wpa_s->mlme.ssid_len = params->ssid_len; 2445189251Ssam wpa_s->mlme.ssid_set = 1; 2446189251Ssam 2447189251Ssam os_free(wpa_s->mlme.extra_ie); 2448189251Ssam if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { 2449189251Ssam wpa_s->mlme.extra_ie = NULL; 2450189251Ssam wpa_s->mlme.extra_ie_len = 0; 2451189251Ssam } else { 2452189251Ssam wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len); 2453189251Ssam if (wpa_s->mlme.extra_ie == NULL) { 2454189251Ssam wpa_s->mlme.extra_ie_len = 0; 2455189251Ssam return -1; 2456189251Ssam } 2457189251Ssam os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie, 2458189251Ssam params->wpa_ie_len); 2459189251Ssam wpa_s->mlme.extra_ie_len = params->wpa_ie_len; 2460189251Ssam } 2461189251Ssam 2462189251Ssam wpa_s->mlme.key_mgmt = params->key_mgmt_suite; 2463189251Ssam 2464189251Ssam ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, 2465189251Ssam wpa_s->mlme.channel, wpa_s->mlme.freq); 2466189251Ssam 2467189251Ssam if (params->mode == 1 && !wpa_s->mlme.bssid_set) { 2468189251Ssam os_get_time(&wpa_s->mlme.ibss_join_req); 2469189251Ssam wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; 2470189251Ssam return ieee80211_sta_find_ibss(wpa_s); 2471189251Ssam } 2472189251Ssam 2473189251Ssam if (wpa_s->mlme.bssid_set) 2474189251Ssam ieee80211_sta_new_auth(wpa_s); 2475189251Ssam 2476189251Ssam return 0; 2477189251Ssam} 2478189251Ssam 2479189251Ssam 2480189251Ssamstatic void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s) 2481189251Ssam{ 2482189251Ssam wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel; 2483189251Ssam wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq; 2484189251Ssam wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode; 2485189251Ssam} 2486189251Ssam 2487189251Ssam 2488189251Ssamstatic int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s) 2489189251Ssam{ 2490189251Ssam wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel; 2491189251Ssam wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq; 2492189251Ssam wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode; 2493189251Ssam if (wpa_s->mlme.freq == 0) 2494189251Ssam return 0; 2495189251Ssam return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, 2496189251Ssam wpa_s->mlme.channel, 2497189251Ssam wpa_s->mlme.freq); 2498189251Ssam} 2499189251Ssam 2500189251Ssam 2501189251Ssamstatic int ieee80211_active_scan(struct wpa_supplicant *wpa_s) 2502189251Ssam{ 2503189251Ssam size_t m; 2504189251Ssam int c; 2505189251Ssam 2506189251Ssam for (m = 0; m < wpa_s->mlme.num_modes; m++) { 2507189251Ssam struct wpa_hw_modes *mode = &wpa_s->mlme.modes[m]; 2508189251Ssam if ((int) mode->mode != (int) wpa_s->mlme.phymode) 2509189251Ssam continue; 2510189251Ssam for (c = 0; c < mode->num_channels; c++) { 2511189251Ssam struct wpa_channel_data *chan = &mode->channels[c]; 2512189251Ssam if (chan->flag & WPA_CHAN_W_SCAN && 2513189251Ssam chan->chan == wpa_s->mlme.channel) { 2514189251Ssam if (chan->flag & WPA_CHAN_W_ACTIVE_SCAN) 2515189251Ssam return 1; 2516189251Ssam break; 2517189251Ssam } 2518189251Ssam } 2519189251Ssam } 2520189251Ssam 2521189251Ssam return 0; 2522189251Ssam} 2523189251Ssam 2524189251Ssam 2525189251Ssamstatic void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx) 2526189251Ssam{ 2527189251Ssam struct wpa_supplicant *wpa_s = eloop_ctx; 2528189251Ssam struct wpa_hw_modes *mode; 2529189251Ssam struct wpa_channel_data *chan; 2530189251Ssam int skip = 0; 2531189251Ssam int timeout = 0; 2532189251Ssam struct wpa_ssid *ssid = wpa_s->current_ssid; 2533189251Ssam int adhoc; 2534189251Ssam 2535189251Ssam if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL) 2536189251Ssam return; 2537189251Ssam 2538189251Ssam adhoc = ssid && ssid->mode == 1; 2539189251Ssam 2540189251Ssam switch (wpa_s->mlme.scan_state) { 2541189251Ssam case SCAN_SET_CHANNEL: 2542189251Ssam mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]; 2543189251Ssam if (wpa_s->mlme.scan_hw_mode_idx >= 2544189251Ssam (int) wpa_s->mlme.num_modes || 2545189251Ssam (wpa_s->mlme.scan_hw_mode_idx + 1 == 2546189251Ssam (int) wpa_s->mlme.num_modes 2547189251Ssam && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) { 2548189251Ssam if (ieee80211_sta_restore_oper_chan(wpa_s)) { 2549189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to " 2550189251Ssam "restore operational channel after " 2551189251Ssam "scan"); 2552189251Ssam } 2553189251Ssam wpa_printf(MSG_DEBUG, "MLME: scan completed"); 2554189251Ssam wpa_s->mlme.sta_scanning = 0; 2555189251Ssam os_get_time(&wpa_s->mlme.last_scan_completed); 2556189251Ssam wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); 2557189251Ssam if (adhoc) { 2558189251Ssam if (!wpa_s->mlme.bssid_set || 2559189251Ssam (wpa_s->mlme.state == 2560189251Ssam IEEE80211_IBSS_JOINED && 2561189251Ssam !ieee80211_sta_active_ibss(wpa_s))) 2562189251Ssam ieee80211_sta_find_ibss(wpa_s); 2563189251Ssam } 2564189251Ssam return; 2565189251Ssam } 2566189251Ssam skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode)); 2567189251Ssam chan = &mode->channels[wpa_s->mlme.scan_channel_idx]; 2568189251Ssam if (!(chan->flag & WPA_CHAN_W_SCAN) || 2569189251Ssam (adhoc && !(chan->flag & WPA_CHAN_W_IBSS)) || 2570189251Ssam (wpa_s->mlme.hw_modes & (1 << WPA_MODE_IEEE80211G) && 2571189251Ssam mode->mode == WPA_MODE_IEEE80211B && 2572189251Ssam wpa_s->mlme.scan_skip_11b)) 2573189251Ssam skip = 1; 2574189251Ssam 2575189251Ssam if (!skip) { 2576189251Ssam wpa_printf(MSG_MSGDUMP, 2577189251Ssam "MLME: scan channel %d (%d MHz)", 2578189251Ssam chan->chan, chan->freq); 2579189251Ssam 2580189251Ssam wpa_s->mlme.channel = chan->chan; 2581189251Ssam wpa_s->mlme.freq = chan->freq; 2582189251Ssam wpa_s->mlme.phymode = mode->mode; 2583189251Ssam if (ieee80211_sta_set_channel(wpa_s, mode->mode, 2584189251Ssam chan->chan, chan->freq)) 2585189251Ssam { 2586189251Ssam wpa_printf(MSG_DEBUG, "MLME: failed to set " 2587189251Ssam "channel %d (%d MHz) for scan", 2588189251Ssam chan->chan, chan->freq); 2589189251Ssam skip = 1; 2590189251Ssam } 2591189251Ssam } 2592189251Ssam 2593189251Ssam wpa_s->mlme.scan_channel_idx++; 2594189251Ssam if (wpa_s->mlme.scan_channel_idx >= 2595189251Ssam wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]. 2596189251Ssam num_channels) { 2597189251Ssam wpa_s->mlme.scan_hw_mode_idx++; 2598189251Ssam wpa_s->mlme.scan_channel_idx = 0; 2599189251Ssam } 2600189251Ssam 2601189251Ssam if (skip) { 2602189251Ssam timeout = 0; 2603189251Ssam break; 2604189251Ssam } 2605189251Ssam 2606189251Ssam timeout = IEEE80211_PROBE_DELAY; 2607189251Ssam wpa_s->mlme.scan_state = SCAN_SEND_PROBE; 2608189251Ssam break; 2609189251Ssam case SCAN_SEND_PROBE: 2610189251Ssam if (ieee80211_active_scan(wpa_s)) { 2611189251Ssam ieee80211_send_probe_req(wpa_s, NULL, 2612189251Ssam wpa_s->mlme.scan_ssid, 2613189251Ssam wpa_s->mlme.scan_ssid_len); 2614189251Ssam timeout = IEEE80211_CHANNEL_TIME; 2615189251Ssam } else { 2616189251Ssam timeout = IEEE80211_PASSIVE_CHANNEL_TIME; 2617189251Ssam } 2618189251Ssam wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; 2619189251Ssam break; 2620189251Ssam } 2621189251Ssam 2622189251Ssam eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000), 2623189251Ssam ieee80211_sta_scan_timer, wpa_s, NULL); 2624189251Ssam} 2625189251Ssam 2626189251Ssam 2627189251Ssamint ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, const u8 *ssid, 2628189251Ssam size_t ssid_len) 2629189251Ssam{ 2630189251Ssam if (ssid_len > MAX_SSID_LEN) 2631189251Ssam return -1; 2632189251Ssam 2633189251Ssam /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) 2634189251Ssam * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS 2635189251Ssam * BSSID: MACAddress 2636189251Ssam * SSID 2637189251Ssam * ScanType: ACTIVE, PASSIVE 2638189251Ssam * ProbeDelay: delay (in microseconds) to be used prior to transmitting 2639189251Ssam * a Probe frame during active scanning 2640189251Ssam * ChannelList 2641189251Ssam * MinChannelTime (>= ProbeDelay), in TU 2642189251Ssam * MaxChannelTime: (>= MinChannelTime), in TU 2643189251Ssam */ 2644189251Ssam 2645189251Ssam /* MLME-SCAN.confirm 2646189251Ssam * BSSDescriptionSet 2647189251Ssam * ResultCode: SUCCESS, INVALID_PARAMETERS 2648189251Ssam */ 2649189251Ssam 2650189251Ssam /* TODO: if assoc, move to power save mode for the duration of the 2651189251Ssam * scan */ 2652189251Ssam 2653189251Ssam if (wpa_s->mlme.sta_scanning) 2654189251Ssam return -1; 2655189251Ssam 2656189251Ssam wpa_printf(MSG_DEBUG, "MLME: starting scan"); 2657189251Ssam 2658189251Ssam ieee80211_sta_save_oper_chan(wpa_s); 2659189251Ssam 2660189251Ssam wpa_s->mlme.sta_scanning = 1; 2661189251Ssam /* TODO: stop TX queue? */ 2662189251Ssam 2663189251Ssam if (ssid) { 2664189251Ssam wpa_s->mlme.scan_ssid_len = ssid_len; 2665189251Ssam os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len); 2666189251Ssam } else 2667189251Ssam wpa_s->mlme.scan_ssid_len = 0; 2668189251Ssam wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not 2669189251Ssam * supported */ 2670189251Ssam wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; 2671189251Ssam wpa_s->mlme.scan_hw_mode_idx = 0; 2672189251Ssam wpa_s->mlme.scan_channel_idx = 0; 2673189251Ssam eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL); 2674189251Ssam 2675189251Ssam return 0; 2676189251Ssam} 2677189251Ssam 2678189251Ssam 2679189251Ssamstruct wpa_scan_results * 2680189251Ssamieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s) 2681189251Ssam{ 2682189251Ssam size_t ap_num = 0; 2683189251Ssam struct wpa_scan_results *res; 2684189251Ssam struct wpa_scan_res *r; 2685189251Ssam struct ieee80211_sta_bss *bss; 2686189251Ssam 2687189251Ssam res = os_zalloc(sizeof(*res)); 2688189251Ssam for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) 2689189251Ssam ap_num++; 2690189251Ssam res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *)); 2691189251Ssam if (res->res == NULL) { 2692189251Ssam os_free(res); 2693189251Ssam return NULL; 2694189251Ssam } 2695189251Ssam 2696189251Ssam for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { 2697189251Ssam r = os_zalloc(sizeof(*r) + bss->ie_len); 2698189251Ssam if (r == NULL) 2699189251Ssam break; 2700189251Ssam os_memcpy(r->bssid, bss->bssid, ETH_ALEN); 2701189251Ssam r->freq = bss->freq; 2702189251Ssam r->beacon_int = bss->beacon_int; 2703189251Ssam r->caps = bss->capability; 2704189251Ssam r->level = bss->rssi; 2705189251Ssam r->tsf = bss->timestamp; 2706189251Ssam if (bss->ie) { 2707189251Ssam r->ie_len = bss->ie_len; 2708189251Ssam os_memcpy(r + 1, bss->ie, bss->ie_len); 2709189251Ssam } 2710189251Ssam 2711189251Ssam res->res[res->num++] = r; 2712189251Ssam } 2713189251Ssam 2714189251Ssam return res; 2715189251Ssam} 2716189251Ssam 2717189251Ssam 2718189251Ssam#if 0 /* FIX */ 2719189251Ssamstruct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s, 2720189251Ssam struct sk_buff *skb, u8 *bssid, 2721189251Ssam u8 *addr) 2722189251Ssam{ 2723189251Ssam struct ieee80211_local *local = dev->priv; 2724189251Ssam struct list_head *ptr; 2725189251Ssam struct sta_info *sta; 2726189251Ssam struct wpa_supplicant *sta_dev = NULL; 2727189251Ssam 2728189251Ssam /* TODO: Could consider removing the least recently used entry and 2729189251Ssam * allow new one to be added. */ 2730189251Ssam if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { 2731189251Ssam if (net_ratelimit()) { 2732189251Ssam wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS " 2733189251Ssam "STA entry " MACSTR, MAC2STR(addr)); 2734189251Ssam } 2735189251Ssam return NULL; 2736189251Ssam } 2737189251Ssam 2738189251Ssam spin_lock_bh(&local->sub_if_lock); 2739189251Ssam list_for_each(ptr, &local->sub_if_list) { 2740189251Ssam sdata = list_entry(ptr, struct ieee80211_sub_if_data, list); 2741189251Ssam if (sdata->type == IEEE80211_SUB_IF_TYPE_STA && 2742189251Ssam os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { 2743189251Ssam sta_dev = sdata->dev; 2744189251Ssam break; 2745189251Ssam } 2746189251Ssam } 2747189251Ssam spin_unlock_bh(&local->sub_if_lock); 2748189251Ssam 2749189251Ssam if (sta_dev == NULL) 2750189251Ssam return NULL; 2751189251Ssam 2752189251Ssam wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR 2753189251Ssam " (dev=%s)", MAC2STR(addr), sta_dev->name); 2754189251Ssam 2755189251Ssam sta = sta_info_add(wpa_s, addr); 2756189251Ssam if (sta == NULL) { 2757189251Ssam return NULL; 2758189251Ssam } 2759189251Ssam 2760189251Ssam sta->dev = sta_dev; 2761189251Ssam sta->supp_rates = wpa_s->mlme.supp_rates_bits; 2762189251Ssam 2763189251Ssam rate_control_rate_init(local, sta); 2764189251Ssam 2765189251Ssam return sta; /* caller will call sta_info_release() */ 2766189251Ssam} 2767189251Ssam#endif 2768189251Ssam 2769189251Ssam 2770189251Ssamint ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason) 2771189251Ssam{ 2772189251Ssam wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason); 2773189251Ssam 2774189251Ssam ieee80211_send_deauth(wpa_s, reason); 2775189251Ssam ieee80211_set_associated(wpa_s, 0); 2776189251Ssam return 0; 2777189251Ssam} 2778189251Ssam 2779189251Ssam 2780189251Ssamint ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason) 2781189251Ssam{ 2782189251Ssam wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason); 2783189251Ssam 2784189251Ssam if (!wpa_s->mlme.associated) 2785189251Ssam return -1; 2786189251Ssam 2787189251Ssam ieee80211_send_disassoc(wpa_s, reason); 2788189251Ssam ieee80211_set_associated(wpa_s, 0); 2789189251Ssam return 0; 2790189251Ssam} 2791189251Ssam 2792189251Ssam 2793189251Ssamvoid ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, 2794189251Ssam struct ieee80211_rx_status *rx_status) 2795189251Ssam{ 2796189251Ssam struct ieee80211_mgmt *mgmt; 2797189251Ssam u16 fc; 2798189251Ssam const u8 *pos; 2799189251Ssam 2800189251Ssam /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */ 2801189251Ssam 2802189251Ssam if (wpa_s->mlme.sta_scanning) { 2803189251Ssam ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status); 2804189251Ssam return; 2805189251Ssam } 2806189251Ssam 2807189251Ssam if (len < 24) 2808189251Ssam return; 2809189251Ssam 2810189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 2811189251Ssam fc = le_to_host16(mgmt->frame_control); 2812189251Ssam 2813189251Ssam if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) 2814189251Ssam ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status); 2815189251Ssam else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { 2816189251Ssam if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) != 2817189251Ssam WLAN_FC_FROMDS) 2818189251Ssam return; 2819189251Ssam /* mgmt->sa is actually BSSID for FromDS data frames */ 2820189251Ssam if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) 2821189251Ssam return; 2822189251Ssam /* Skip IEEE 802.11 and LLC headers */ 2823189251Ssam pos = buf + 24 + 6; 2824189251Ssam if (WPA_GET_BE16(pos) != ETH_P_EAPOL) 2825189251Ssam return; 2826189251Ssam pos += 2; 2827189251Ssam /* mgmt->bssid is actually BSSID for SA data frames */ 2828189251Ssam wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid, 2829189251Ssam pos, buf + len - pos); 2830189251Ssam } 2831189251Ssam} 2832189251Ssam 2833189251Ssam 2834189251Ssamvoid ieee80211_sta_free_hw_features(struct wpa_hw_modes *hw_features, 2835189251Ssam size_t num_hw_features) 2836189251Ssam{ 2837189251Ssam size_t i; 2838189251Ssam 2839189251Ssam if (hw_features == NULL) 2840189251Ssam return; 2841189251Ssam 2842189251Ssam for (i = 0; i < num_hw_features; i++) { 2843189251Ssam os_free(hw_features[i].channels); 2844189251Ssam os_free(hw_features[i].rates); 2845189251Ssam } 2846189251Ssam 2847189251Ssam os_free(hw_features); 2848189251Ssam} 2849189251Ssam 2850189251Ssam 2851189251Ssamint ieee80211_sta_init(struct wpa_supplicant *wpa_s) 2852189251Ssam{ 2853189251Ssam u16 num_modes, flags; 2854189251Ssam 2855189251Ssam wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, 2856189251Ssam &flags); 2857189251Ssam if (wpa_s->mlme.modes == NULL) { 2858189251Ssam wpa_printf(MSG_ERROR, "MLME: Failed to read supported " 2859189251Ssam "channels and rates from the driver"); 2860189251Ssam return -1; 2861189251Ssam } 2862189251Ssam 2863189251Ssam wpa_s->mlme.num_modes = num_modes; 2864189251Ssam 2865189251Ssam wpa_s->mlme.hw_modes = 1 << WPA_MODE_IEEE80211A; 2866189251Ssam wpa_s->mlme.hw_modes |= 1 << WPA_MODE_IEEE80211B; 2867189251Ssam wpa_s->mlme.hw_modes |= 1 << WPA_MODE_IEEE80211G; 2868189251Ssam 2869189251Ssam return 0; 2870189251Ssam} 2871189251Ssam 2872189251Ssam 2873189251Ssamvoid ieee80211_sta_deinit(struct wpa_supplicant *wpa_s) 2874189251Ssam{ 2875189251Ssam eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); 2876189251Ssam eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL); 2877189251Ssam os_free(wpa_s->mlme.extra_ie); 2878189251Ssam wpa_s->mlme.extra_ie = NULL; 2879189251Ssam os_free(wpa_s->mlme.extra_probe_ie); 2880189251Ssam wpa_s->mlme.extra_probe_ie = NULL; 2881189251Ssam os_free(wpa_s->mlme.assocreq_ies); 2882189251Ssam wpa_s->mlme.assocreq_ies = NULL; 2883189251Ssam os_free(wpa_s->mlme.assocresp_ies); 2884189251Ssam wpa_s->mlme.assocresp_ies = NULL; 2885189251Ssam ieee80211_bss_list_deinit(wpa_s); 2886189251Ssam ieee80211_sta_free_hw_features(wpa_s->mlme.modes, 2887189251Ssam wpa_s->mlme.num_modes); 2888189251Ssam#ifdef CONFIG_IEEE80211R 2889189251Ssam os_free(wpa_s->mlme.ft_ies); 2890189251Ssam wpa_s->mlme.ft_ies = NULL; 2891189251Ssam wpa_s->mlme.ft_ies_len = 0; 2892189251Ssam#endif /* CONFIG_IEEE80211R */ 2893189251Ssam} 2894189251Ssam 2895189251Ssam 2896189251Ssam#ifdef CONFIG_IEEE80211R 2897189251Ssam 2898189251Ssamint ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, 2899189251Ssam const u8 *ies, size_t ies_len) 2900189251Ssam{ 2901189251Ssam if (md == NULL) { 2902189251Ssam wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain"); 2903189251Ssam os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN); 2904189251Ssam } else { 2905189251Ssam wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR, 2906189251Ssam MAC2STR(md)); 2907189251Ssam os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN); 2908189251Ssam } 2909189251Ssam 2910189251Ssam wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len); 2911189251Ssam os_free(wpa_s->mlme.ft_ies); 2912189251Ssam wpa_s->mlme.ft_ies = os_malloc(ies_len); 2913189251Ssam if (wpa_s->mlme.ft_ies == NULL) 2914189251Ssam return -1; 2915189251Ssam os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len); 2916189251Ssam wpa_s->mlme.ft_ies_len = ies_len; 2917189251Ssam 2918189251Ssam return 0; 2919189251Ssam} 2920189251Ssam 2921189251Ssam 2922189251Ssamint ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, 2923189251Ssam const u8 *target_ap, 2924189251Ssam const u8 *ies, size_t ies_len) 2925189251Ssam{ 2926189251Ssam u8 *buf; 2927189251Ssam size_t len; 2928189251Ssam struct ieee80211_mgmt *mgmt; 2929189251Ssam int res; 2930189251Ssam 2931189251Ssam /* 2932189251Ssam * Action frame payload: 2933189251Ssam * Category[1] = 6 (Fast BSS Transition) 2934189251Ssam * Action[1] = 1 (Fast BSS Transition Request) 2935189251Ssam * STA Address 2936189251Ssam * Target AP Address 2937189251Ssam * FT IEs 2938189251Ssam */ 2939189251Ssam 2940189251Ssam buf = os_zalloc(sizeof(*mgmt) + ies_len); 2941189251Ssam if (buf == NULL) { 2942189251Ssam wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 2943189251Ssam "FT action frame"); 2944189251Ssam return -1; 2945189251Ssam } 2946189251Ssam 2947189251Ssam mgmt = (struct ieee80211_mgmt *) buf; 2948189251Ssam len = 24; 2949189251Ssam os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 2950189251Ssam os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 2951189251Ssam os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 2952189251Ssam mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 2953189251Ssam WLAN_FC_STYPE_ACTION); 2954189251Ssam mgmt->u.action.category = WLAN_ACTION_FT; 2955189251Ssam mgmt->u.action.u.ft_action_req.action = action; 2956189251Ssam os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr, 2957189251Ssam ETH_ALEN); 2958189251Ssam os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap, 2959189251Ssam ETH_ALEN); 2960189251Ssam os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len); 2961189251Ssam len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len; 2962189251Ssam 2963189251Ssam wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d " 2964189251Ssam "Target AP=" MACSTR " body_len=%lu", 2965189251Ssam action, MAC2STR(target_ap), (unsigned long) ies_len); 2966189251Ssam 2967189251Ssam res = ieee80211_sta_tx(wpa_s, buf, len); 2968189251Ssam os_free(buf); 2969189251Ssam 2970189251Ssam return res; 2971189251Ssam} 2972189251Ssam 2973189251Ssam#endif /* CONFIG_IEEE80211R */ 2974189251Ssam 2975189251Ssam 2976189251Ssamint ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, const u8 *ies, 2977189251Ssam size_t ies_len) 2978189251Ssam{ 2979189251Ssam os_free(wpa_s->mlme.extra_probe_ie); 2980189251Ssam wpa_s->mlme.extra_probe_ie = NULL; 2981189251Ssam wpa_s->mlme.extra_probe_ie_len = 0; 2982189251Ssam 2983189251Ssam if (ies == NULL) 2984189251Ssam return 0; 2985189251Ssam 2986189251Ssam wpa_s->mlme.extra_probe_ie = os_malloc(ies_len); 2987189251Ssam if (wpa_s->mlme.extra_probe_ie == NULL) 2988189251Ssam return -1; 2989189251Ssam 2990189251Ssam os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len); 2991189251Ssam wpa_s->mlme.extra_probe_ie_len = ies_len; 2992189251Ssam 2993189251Ssam return 0; 2994189251Ssam} 2995