f238cf9f42
hostapd will now go through the RIC Request and process each RDIE. Only WMM TSPEC requests are currently supported; all other request descriptors will be declined. RIC Response is written by hostapd and verified by wpa_supplicant (MIC validation). wpa_supplicant does not yet have code to notify the driver about the resource request results.
334 lines
10 KiB
C
334 lines
10 KiB
C
/*
|
|
* hostapd / WMM (Wi-Fi Multimedia)
|
|
* Copyright 2002-2003, Instant802 Networks, Inc.
|
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
|
*
|
|
* 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"
|
|
|
|
#include "hostapd.h"
|
|
#include "ieee802_11.h"
|
|
#include "wme.h"
|
|
#include "sta_info.h"
|
|
#include "driver_i.h"
|
|
|
|
|
|
/* TODO: maintain separate sequence and fragment numbers for each AC
|
|
* TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
|
|
* if only WMM stations are receiving a certain group */
|
|
|
|
|
|
static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
|
|
{
|
|
u8 ret;
|
|
ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
|
|
if (acm)
|
|
ret |= WMM_AC_ACM;
|
|
ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static inline u8 wmm_ecw(int ecwmin, int ecwmax)
|
|
{
|
|
return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
|
|
((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
|
|
}
|
|
|
|
|
|
/*
|
|
* Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
|
|
* Response frames.
|
|
*/
|
|
u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
|
|
{
|
|
u8 *pos = eid;
|
|
struct wmm_parameter_element *wmm =
|
|
(struct wmm_parameter_element *) (pos + 2);
|
|
int e;
|
|
|
|
if (!hapd->conf->wmm_enabled)
|
|
return eid;
|
|
eid[0] = WLAN_EID_VENDOR_SPECIFIC;
|
|
wmm->oui[0] = 0x00;
|
|
wmm->oui[1] = 0x50;
|
|
wmm->oui[2] = 0xf2;
|
|
wmm->oui_type = WMM_OUI_TYPE;
|
|
wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
|
|
wmm->version = WMM_VERSION;
|
|
wmm->qos_info = hapd->parameter_set_count & 0xf;
|
|
|
|
/* fill in a parameter set record for each AC */
|
|
for (e = 0; e < 4; e++) {
|
|
struct wmm_ac_parameter *ac = &wmm->ac[e];
|
|
struct hostapd_wmm_ac_params *acp =
|
|
&hapd->iconf->wmm_ac_params[e];
|
|
|
|
ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
|
|
acp->admission_control_mandatory,
|
|
e);
|
|
ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
|
|
ac->txop_limit = host_to_le16(acp->txop_limit);
|
|
}
|
|
|
|
pos = (u8 *) (wmm + 1);
|
|
eid[1] = pos - eid - 2; /* element length */
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
/* This function is called when a station sends an association request with
|
|
* WMM info element. The function returns zero on success or non-zero on any
|
|
* error in WMM element. eid does not include Element ID and Length octets. */
|
|
int hostapd_eid_wmm_valid(struct hostapd_data *hapd, u8 *eid, size_t len)
|
|
{
|
|
struct wmm_information_element *wmm;
|
|
|
|
wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
|
|
|
|
if (len < sizeof(struct wmm_information_element)) {
|
|
wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
|
|
(unsigned long) len);
|
|
return -1;
|
|
}
|
|
|
|
wmm = (struct wmm_information_element *) eid;
|
|
wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x "
|
|
"OUI type %d OUI sub-type %d version %d QoS info 0x%x",
|
|
wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
|
|
wmm->oui_subtype, wmm->version, wmm->qos_info);
|
|
if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
|
|
wmm->version != WMM_VERSION) {
|
|
wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* This function is called when a station sends an ACK frame for an AssocResp
|
|
* frame (status=success) and the matching AssocReq contained a WMM element.
|
|
*/
|
|
int hostapd_wmm_sta_config(struct hostapd_data *hapd, struct sta_info *sta)
|
|
{
|
|
/* update kernel STA data for WMM related items (WLAN_STA_WPA flag) */
|
|
if (sta->flags & WLAN_STA_WMM)
|
|
hostapd_sta_set_flags(hapd, sta->addr, sta->flags,
|
|
WLAN_STA_WMM, ~0);
|
|
else
|
|
hostapd_sta_set_flags(hapd, sta->addr, sta->flags,
|
|
0, ~WLAN_STA_WMM);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
|
|
const struct wmm_tspec_element *tspec,
|
|
u8 action_code, u8 dialogue_token, u8 status_code)
|
|
{
|
|
u8 buf[256];
|
|
struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
|
|
struct wmm_tspec_element *t = (struct wmm_tspec_element *)
|
|
m->u.action.u.wmm_action.variable;
|
|
int len;
|
|
|
|
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
|
|
HOSTAPD_LEVEL_DEBUG,
|
|
"action response - reason %d", status_code);
|
|
os_memset(buf, 0, sizeof(buf));
|
|
m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
|
WLAN_FC_STYPE_ACTION);
|
|
os_memcpy(m->da, addr, ETH_ALEN);
|
|
os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
|
|
os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
|
|
m->u.action.category = WLAN_ACTION_WMM;
|
|
m->u.action.u.wmm_action.action_code = action_code;
|
|
m->u.action.u.wmm_action.dialog_token = dialogue_token;
|
|
m->u.action.u.wmm_action.status_code = status_code;
|
|
os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
|
|
len = ((u8 *) (t + 1)) - buf;
|
|
|
|
if (hostapd_send_mgmt_frame(hapd, m, len, 0) < 0)
|
|
perror("wmm_send_action: send");
|
|
}
|
|
|
|
|
|
int wmm_process_tspec(struct wmm_tspec_element *tspec)
|
|
{
|
|
int medium_time, pps, duration;
|
|
int up, psb, dir, tid;
|
|
u16 val, surplus;
|
|
|
|
up = (tspec->ts_info[1] >> 3) & 0x07;
|
|
psb = (tspec->ts_info[1] >> 2) & 0x01;
|
|
dir = (tspec->ts_info[0] >> 5) & 0x03;
|
|
tid = (tspec->ts_info[0] >> 1) & 0x0f;
|
|
wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
|
|
up, psb, dir, tid);
|
|
val = le_to_host16(tspec->nominal_msdu_size);
|
|
wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
|
|
val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
|
|
wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
|
|
le_to_host32(tspec->mean_data_rate));
|
|
wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
|
|
le_to_host32(tspec->minimum_phy_rate));
|
|
val = le_to_host16(tspec->surplus_bandwidth_allowance);
|
|
wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
|
|
val >> 13, 10000 * (val & 0x1fff) / 0x2000);
|
|
|
|
val = le_to_host16(tspec->nominal_msdu_size);
|
|
if (val == 0) {
|
|
wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
|
|
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
|
|
}
|
|
/* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
|
|
pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
|
|
wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
|
|
pps);
|
|
|
|
if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
|
|
wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
|
|
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
|
|
}
|
|
|
|
duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
|
|
(le_to_host32(tspec->minimum_phy_rate) / 1000000) +
|
|
50 /* FIX: proper SIFS + ACK duration */;
|
|
|
|
/* unsigned binary number with an implicit binary point after the
|
|
* leftmost 3 bits, i.e., 0x2000 = 1.0 */
|
|
surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
|
|
if (surplus <= 0x2000) {
|
|
wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
|
|
"greater than unity");
|
|
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
|
|
}
|
|
|
|
medium_time = surplus * pps * duration / 0x2000;
|
|
wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
|
|
|
|
/*
|
|
* TODO: store list of granted (and still active) TSPECs and check
|
|
* whether there is available medium time for this request. For now,
|
|
* just refuse requests that would by themselves take very large
|
|
* portion of the available bandwidth.
|
|
*/
|
|
if (medium_time > 750000) {
|
|
wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
|
|
"75%% of available bandwidth");
|
|
return WMM_ADDTS_STATUS_REFUSED;
|
|
}
|
|
|
|
/* Convert to 32 microseconds per second unit */
|
|
tspec->medium_time = host_to_le16(medium_time / 32);
|
|
|
|
return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
|
|
}
|
|
|
|
|
|
static void wmm_addts_req(struct hostapd_data *hapd,
|
|
struct ieee80211_mgmt *mgmt,
|
|
struct wmm_tspec_element *tspec, size_t len)
|
|
{
|
|
u8 *end = ((u8 *) mgmt) + len;
|
|
int res;
|
|
|
|
if ((u8 *) (tspec + 1) > end) {
|
|
wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
|
|
"from " MACSTR,
|
|
mgmt->u.action.u.wmm_action.dialog_token,
|
|
MAC2STR(mgmt->sa));
|
|
|
|
res = wmm_process_tspec(tspec);
|
|
wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
|
|
|
|
wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
|
|
mgmt->u.action.u.wmm_action.dialog_token, res);
|
|
}
|
|
|
|
|
|
void hostapd_wmm_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
|
|
size_t len)
|
|
{
|
|
int action_code;
|
|
int left = len - IEEE80211_HDRLEN - 4;
|
|
u8 *pos = ((u8 *) mgmt) + IEEE80211_HDRLEN + 4;
|
|
struct ieee802_11_elems elems;
|
|
struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
|
|
|
|
/* check that the request comes from a valid station */
|
|
if (!sta ||
|
|
(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
|
|
(WLAN_STA_ASSOC | WLAN_STA_WMM)) {
|
|
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
|
HOSTAPD_LEVEL_DEBUG,
|
|
"wmm action received is not from associated wmm"
|
|
" station");
|
|
/* TODO: respond with action frame refused status code */
|
|
return;
|
|
}
|
|
|
|
/* extract the tspec info element */
|
|
if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
|
|
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
|
HOSTAPD_LEVEL_DEBUG,
|
|
"hostapd_wmm_action - could not parse wmm "
|
|
"action");
|
|
/* TODO: respond with action frame invalid parameters status
|
|
* code */
|
|
return;
|
|
}
|
|
|
|
if (!elems.wmm_tspec ||
|
|
elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
|
|
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
|
HOSTAPD_LEVEL_DEBUG,
|
|
"hostapd_wmm_action - missing or wrong length "
|
|
"tspec");
|
|
/* TODO: respond with action frame invalid parameters status
|
|
* code */
|
|
return;
|
|
}
|
|
|
|
/* TODO: check the request is for an AC with ACM set, if not, refuse
|
|
* request */
|
|
|
|
action_code = mgmt->u.action.u.wmm_action.action_code;
|
|
switch (action_code) {
|
|
case WMM_ACTION_CODE_ADDTS_REQ:
|
|
wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
|
|
(elems.wmm_tspec - 2), len);
|
|
return;
|
|
#if 0
|
|
/* TODO: needed for client implementation */
|
|
case WMM_ACTION_CODE_ADDTS_RESP:
|
|
wmm_setup_request(hapd, mgmt, len);
|
|
return;
|
|
/* TODO: handle station teardown requests */
|
|
case WMM_ACTION_CODE_DELTS:
|
|
wmm_teardown(hapd, mgmt, len);
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
|
HOSTAPD_LEVEL_DEBUG,
|
|
"hostapd_wmm_action - unknown action code %d",
|
|
action_code);
|
|
}
|