de9289c8e9
This commit brings in cleaned up version of IEEE 802.11n implementation from Intel (1). The Intel tarball includes number of other changes, too, and only the changes specific to IEEE 802.11n are brought in here. In addition, this does not include all the changes (e.g., some of the configuration parameters are still missing and driver wrapper changes for mac80211 were not included). (1) http://www.kernel.org/pub/linux/kernel/people/chuyee/wireless/iwl4965_ap/hostap_0_6_0_intel_0.0.13.1.tgz
444 lines
12 KiB
C
444 lines
12 KiB
C
/*
|
|
* hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
|
|
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
|
|
* Copyright (c) 2005-2006, Devicescape Software, Inc.
|
|
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of BSD
|
|
* license.
|
|
*
|
|
* See README and COPYING for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
|
|
#include "hostapd.h"
|
|
#include "ieee802_11.h"
|
|
#include "wpa.h"
|
|
#include "wme.h"
|
|
#include "beacon.h"
|
|
#include "hw_features.h"
|
|
#include "driver.h"
|
|
#include "sta_info.h"
|
|
#include "ieee802_11h.h"
|
|
|
|
|
|
static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
|
|
{
|
|
u8 erp = 0;
|
|
|
|
if (hapd->iface->current_mode == NULL ||
|
|
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
|
return 0;
|
|
|
|
switch (hapd->iconf->cts_protection_type) {
|
|
case CTS_PROTECTION_FORCE_ENABLED:
|
|
erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION;
|
|
break;
|
|
case CTS_PROTECTION_FORCE_DISABLED:
|
|
erp = 0;
|
|
break;
|
|
case CTS_PROTECTION_AUTOMATIC:
|
|
if (hapd->iface->olbc)
|
|
erp |= ERP_INFO_USE_PROTECTION;
|
|
/* continue */
|
|
case CTS_PROTECTION_AUTOMATIC_NO_OLBC:
|
|
if (hapd->iface->num_sta_non_erp > 0) {
|
|
erp |= ERP_INFO_NON_ERP_PRESENT |
|
|
ERP_INFO_USE_PROTECTION;
|
|
}
|
|
break;
|
|
}
|
|
if (hapd->iface->num_sta_no_short_preamble > 0)
|
|
erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
|
|
|
|
return erp;
|
|
}
|
|
|
|
|
|
static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
|
|
{
|
|
*eid++ = WLAN_EID_DS_PARAMS;
|
|
*eid++ = 1;
|
|
*eid++ = hapd->iconf->channel;
|
|
return eid;
|
|
}
|
|
|
|
|
|
static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
|
|
{
|
|
if (hapd->iface->current_mode == NULL ||
|
|
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
|
return eid;
|
|
|
|
/* Set NonERP_present and use_protection bits if there
|
|
* are any associated NonERP stations. */
|
|
/* TODO: use_protection bit can be set to zero even if
|
|
* there are NonERP stations present. This optimization
|
|
* might be useful if NonERP stations are "quiet".
|
|
* See 802.11g/D6 E-1 for recommended practice.
|
|
* In addition, Non ERP present might be set, if AP detects Non ERP
|
|
* operation on other APs. */
|
|
|
|
/* Add ERP Information element */
|
|
*eid++ = WLAN_EID_ERP_INFO;
|
|
*eid++ = 1;
|
|
*eid++ = ieee802_11_erp_info(hapd);
|
|
|
|
return eid;
|
|
}
|
|
|
|
|
|
static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
|
|
int max_len)
|
|
{
|
|
u8 *pos = eid;
|
|
|
|
if ((!hapd->iconf->ieee80211d && !hapd->iface->dfs_enable) ||
|
|
max_len < 6)
|
|
return eid;
|
|
|
|
*pos++ = WLAN_EID_COUNTRY;
|
|
pos++; /* length will be set later */
|
|
os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
|
|
pos += 3;
|
|
|
|
if ((pos - eid) & 1)
|
|
*pos++ = 0; /* pad for 16-bit alignment */
|
|
|
|
eid[1] = (pos - eid) - 2;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static u8 * hostapd_eid_power_constraint(struct hostapd_data *hapd, u8 *eid)
|
|
|
|
{
|
|
if (!hapd->iface->dfs_enable)
|
|
return eid;
|
|
*eid++ = WLAN_EID_PWR_CONSTRAINT;
|
|
*eid++ = 1;
|
|
*eid++ = hapd->iface->pwr_const;
|
|
return eid;
|
|
}
|
|
|
|
|
|
static u8 * hostapd_eid_tpc_report(struct hostapd_data *hapd, u8 *eid)
|
|
|
|
{
|
|
if (!hapd->iface->dfs_enable)
|
|
return eid;
|
|
*eid++ = WLAN_EID_TPC_REPORT;
|
|
*eid++ = 2;
|
|
*eid++ = hapd->iface->tx_power; /* TX POWER */
|
|
*eid++ = 0; /* Link Margin */
|
|
return eid;
|
|
}
|
|
|
|
static u8 * hostapd_eid_channel_switch(struct hostapd_data *hapd, u8 *eid)
|
|
|
|
{
|
|
if (!hapd->iface->dfs_enable || !hapd->iface->channel_switch)
|
|
return eid;
|
|
*eid++ = WLAN_EID_CHANNEL_SWITCH;
|
|
*eid++ = 3;
|
|
*eid++ = CHAN_SWITCH_MODE_QUIET;
|
|
*eid++ = hapd->iface->channel_switch; /* New channel */
|
|
/* 0 - very soon; 1 - before next TBTT; num - after num beacons */
|
|
*eid++ = 0;
|
|
return eid;
|
|
}
|
|
|
|
|
|
static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len,
|
|
struct sta_info *sta)
|
|
{
|
|
const u8 *ie;
|
|
size_t ielen;
|
|
|
|
ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
|
|
if (ie == NULL || ielen > len)
|
|
return eid;
|
|
|
|
os_memcpy(eid, ie, ielen);
|
|
return eid + ielen;
|
|
}
|
|
|
|
|
|
void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
|
|
size_t len)
|
|
{
|
|
struct ieee80211_mgmt *resp;
|
|
struct ieee802_11_elems elems;
|
|
char *ssid;
|
|
u8 *pos, *epos, *ie;
|
|
size_t ssid_len, ie_len;
|
|
struct sta_info *sta = NULL;
|
|
|
|
ie = mgmt->u.probe_req.variable;
|
|
ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
|
|
|
|
if (!hapd->iconf->send_probe_response)
|
|
return;
|
|
|
|
if (ieee802_11_parse_elems(hapd, ie, ie_len, &elems, 0) == ParseFailed)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
|
|
MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
ssid = NULL;
|
|
ssid_len = 0;
|
|
|
|
if ((!elems.ssid || !elems.supp_rates)) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
|
|
"without SSID or supported rates element",
|
|
MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) {
|
|
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
|
|
"broadcast SSID ignored", MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
sta = ap_get_sta(hapd, mgmt->sa);
|
|
|
|
if (elems.ssid_len == 0 ||
|
|
(elems.ssid_len == hapd->conf->ssid.ssid_len &&
|
|
os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
|
|
0)) {
|
|
ssid = hapd->conf->ssid.ssid;
|
|
ssid_len = hapd->conf->ssid.ssid_len;
|
|
if (sta)
|
|
sta->ssid_probe = &hapd->conf->ssid;
|
|
}
|
|
|
|
if (!ssid) {
|
|
if (!(mgmt->da[0] & 0x01)) {
|
|
char ssid_txt[33];
|
|
ieee802_11_print_ssid(ssid_txt, elems.ssid,
|
|
elems.ssid_len);
|
|
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
|
|
" for foreign SSID '%s'",
|
|
MAC2STR(mgmt->sa), ssid_txt);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* TODO: verify that supp_rates contains at least one matching rate
|
|
* with AP configuration */
|
|
#define MAX_PROBERESP_LEN 768
|
|
resp = os_zalloc(MAX_PROBERESP_LEN);
|
|
if (resp == NULL)
|
|
return;
|
|
epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
|
|
|
|
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
|
WLAN_FC_STYPE_PROBE_RESP);
|
|
os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
|
|
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
|
|
|
|
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
|
|
resp->u.probe_resp.beacon_int =
|
|
host_to_le16(hapd->iconf->beacon_int);
|
|
|
|
/* hardware or low-level driver will setup seq_ctrl and timestamp */
|
|
resp->u.probe_resp.capab_info =
|
|
host_to_le16(hostapd_own_capab_info(hapd, sta, 1));
|
|
|
|
pos = resp->u.probe_resp.variable;
|
|
*pos++ = WLAN_EID_SSID;
|
|
*pos++ = ssid_len;
|
|
os_memcpy(pos, ssid, ssid_len);
|
|
pos += ssid_len;
|
|
|
|
/* Supported rates */
|
|
pos = hostapd_eid_supp_rates(hapd, pos);
|
|
|
|
/* DS Params */
|
|
pos = hostapd_eid_ds_params(hapd, pos);
|
|
|
|
pos = hostapd_eid_country(hapd, pos, epos - pos);
|
|
|
|
pos = hostapd_eid_power_constraint(hapd, pos);
|
|
pos = hostapd_eid_tpc_report(hapd, pos);
|
|
|
|
/* ERP Information element */
|
|
pos = hostapd_eid_erp_info(hapd, pos);
|
|
|
|
/* Extended supported rates */
|
|
pos = hostapd_eid_ext_supp_rates(hapd, pos);
|
|
|
|
pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
|
|
|
|
/* Wi-Fi Wireless Multimedia Extensions */
|
|
if (hapd->conf->wme_enabled)
|
|
pos = hostapd_eid_wme(hapd, pos);
|
|
|
|
#ifdef CONFIG_IEEE80211N
|
|
if (hapd->conf->ieee80211n) {
|
|
pos = hostapd_eid_ht_capabilities_info(hapd, pos);
|
|
pos = hostapd_eid_ht_operation(hapd, pos);
|
|
}
|
|
#endif /* CONFIG_IEEE80211N */
|
|
|
|
if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0)
|
|
perror("handle_probe_req: send");
|
|
|
|
os_free(resp);
|
|
|
|
wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s "
|
|
"SSID", MAC2STR(mgmt->sa),
|
|
elems.ssid_len == 0 ? "broadcast" : "our");
|
|
}
|
|
|
|
|
|
void ieee802_11_set_beacon(struct hostapd_data *hapd)
|
|
{
|
|
struct ieee80211_mgmt *head;
|
|
u8 *pos, *tail, *tailpos;
|
|
int preamble;
|
|
u16 capab_info;
|
|
size_t head_len, tail_len;
|
|
int cts_protection = ((ieee802_11_erp_info(hapd) &
|
|
ERP_INFO_USE_PROTECTION) ? 1 : 0);
|
|
|
|
#define BEACON_HEAD_BUF_SIZE 256
|
|
#define BEACON_TAIL_BUF_SIZE 512
|
|
head = os_zalloc(BEACON_HEAD_BUF_SIZE);
|
|
tailpos = tail = os_malloc(BEACON_TAIL_BUF_SIZE);
|
|
if (head == NULL || tail == NULL) {
|
|
wpa_printf(MSG_ERROR, "Failed to set beacon data");
|
|
os_free(head);
|
|
os_free(tail);
|
|
return;
|
|
}
|
|
|
|
head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
|
WLAN_FC_STYPE_BEACON);
|
|
head->duration = host_to_le16(0);
|
|
os_memset(head->da, 0xff, ETH_ALEN);
|
|
|
|
os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
|
|
os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
|
|
head->u.beacon.beacon_int =
|
|
host_to_le16(hapd->iconf->beacon_int);
|
|
|
|
/* hardware or low-level driver will setup seq_ctrl and timestamp */
|
|
capab_info = hostapd_own_capab_info(hapd, NULL, 0);
|
|
head->u.beacon.capab_info = host_to_le16(capab_info);
|
|
pos = &head->u.beacon.variable[0];
|
|
|
|
/* SSID */
|
|
*pos++ = WLAN_EID_SSID;
|
|
if (hapd->conf->ignore_broadcast_ssid == 2) {
|
|
/* clear the data, but keep the correct length of the SSID */
|
|
*pos++ = hapd->conf->ssid.ssid_len;
|
|
os_memset(pos, 0, hapd->conf->ssid.ssid_len);
|
|
pos += hapd->conf->ssid.ssid_len;
|
|
} else if (hapd->conf->ignore_broadcast_ssid) {
|
|
*pos++ = 0; /* empty SSID */
|
|
} else {
|
|
*pos++ = hapd->conf->ssid.ssid_len;
|
|
os_memcpy(pos, hapd->conf->ssid.ssid,
|
|
hapd->conf->ssid.ssid_len);
|
|
pos += hapd->conf->ssid.ssid_len;
|
|
}
|
|
|
|
/* Supported rates */
|
|
pos = hostapd_eid_supp_rates(hapd, pos);
|
|
|
|
/* DS Params */
|
|
pos = hostapd_eid_ds_params(hapd, pos);
|
|
|
|
head_len = pos - (u8 *) head;
|
|
|
|
tailpos = hostapd_eid_country(hapd, tailpos,
|
|
tail + BEACON_TAIL_BUF_SIZE - tailpos);
|
|
|
|
tailpos = hostapd_eid_power_constraint(hapd, tailpos);
|
|
tailpos = hostapd_eid_channel_switch(hapd, tailpos);
|
|
tailpos = hostapd_eid_tpc_report(hapd, tailpos);
|
|
|
|
/* ERP Information element */
|
|
tailpos = hostapd_eid_erp_info(hapd, tailpos);
|
|
|
|
/* Extended supported rates */
|
|
tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
|
|
|
|
tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
|
|
tailpos, NULL);
|
|
|
|
/* Wi-Fi Wireless Multimedia Extensions */
|
|
if (hapd->conf->wme_enabled)
|
|
tailpos = hostapd_eid_wme(hapd, tailpos);
|
|
|
|
#ifdef CONFIG_IEEE80211N
|
|
if (hapd->conf->ieee80211n) {
|
|
tailpos = hostapd_eid_ht_capabilities_info(hapd, tailpos);
|
|
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
|
|
|
|
if (hostapd_set_ht_capability(
|
|
hapd->conf->iface, hapd,
|
|
&hapd->conf->ht_capabilities.data)) {
|
|
wpa_printf(MSG_ERROR, "Could not set HT capabilities "
|
|
"for kernel driver");
|
|
}
|
|
if (hostapd_set_ht_operation(
|
|
hapd->conf->iface, hapd,
|
|
&hapd->conf->ht_operation.data))
|
|
wpa_printf(MSG_ERROR, "Could not set HT operation for "
|
|
"kernel driver");
|
|
}
|
|
#endif /* CONFIG_IEEE80211N */
|
|
|
|
tail_len = tailpos > tail ? tailpos - tail : 0;
|
|
|
|
if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len,
|
|
tail, tail_len))
|
|
wpa_printf(MSG_ERROR, "Failed to set beacon head/tail");
|
|
|
|
os_free(tail);
|
|
os_free(head);
|
|
|
|
if (hostapd_set_cts_protect(hapd, cts_protection))
|
|
wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
|
|
"driver");
|
|
|
|
if (hapd->iface->current_mode &&
|
|
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
|
|
hostapd_set_short_slot_time(hapd,
|
|
hapd->iface->num_sta_no_short_slot_time
|
|
> 0 ? 0 : 1))
|
|
wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option "
|
|
"in kernel driver");
|
|
|
|
if (hapd->iface->num_sta_no_short_preamble == 0 &&
|
|
hapd->iconf->preamble == SHORT_PREAMBLE)
|
|
preamble = SHORT_PREAMBLE;
|
|
else
|
|
preamble = LONG_PREAMBLE;
|
|
if (hostapd_set_preamble(hapd, preamble))
|
|
wpa_printf(MSG_ERROR, "Could not set preamble for kernel "
|
|
"driver");
|
|
}
|
|
|
|
|
|
void ieee802_11_set_beacons(struct hostapd_iface *iface)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < iface->num_bss; i++)
|
|
ieee802_11_set_beacon(iface->bss[i]);
|
|
}
|
|
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|