1169691Skan/* 2169691Skan * hostapd / WMM (Wi-Fi Multimedia) 3169691Skan * Copyright 2002-2003, Instant802 Networks, Inc. 4169691Skan * Copyright 2005-2006, Devicescape Software, Inc. 5169691Skan * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 6169691Skan * 7169691Skan * This program is free software; you can redistribute it and/or modify 8169691Skan * it under the terms of the GNU General Public License version 2 as 9169691Skan * published by the Free Software Foundation. 10169691Skan * 11169691Skan * Alternatively, this software may be distributed under the terms of BSD 12169691Skan * license. 13169691Skan * 14169691Skan * See README and COPYING for more details. 15169691Skan */ 16169691Skan 17169691Skan#include "utils/includes.h" 18169691Skan 19169691Skan#include "utils/common.h" 20169691Skan#include "common/ieee802_11_defs.h" 21169691Skan#include "common/ieee802_11_common.h" 22169691Skan#include "hostapd.h" 23169691Skan#include "ieee802_11.h" 24169691Skan#include "sta_info.h" 25169691Skan#include "ap_config.h" 26169691Skan#include "wmm.h" 27169691Skan 28169691Skan 29169691Skan/* TODO: maintain separate sequence and fragment numbers for each AC 30169691Skan * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA 31169691Skan * if only WMM stations are receiving a certain group */ 32169691Skan 33169691Skan 34169691Skanstatic inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) 35169691Skan{ 36169691Skan u8 ret; 37169691Skan ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK; 38169691Skan if (acm) 39169691Skan ret |= WMM_AC_ACM; 40169691Skan ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK; 41169691Skan return ret; 42169691Skan} 43169691Skan 44169691Skan 45169691Skanstatic inline u8 wmm_ecw(int ecwmin, int ecwmax) 46169691Skan{ 47169691Skan return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) | 48169691Skan ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK); 49169691Skan} 50169691Skan 51169691Skan 52169691Skan/* 53169691Skan * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association 54169691Skan * Response frames. 55169691Skan */ 56169691Skanu8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) 57169691Skan{ 58169691Skan u8 *pos = eid; 59169691Skan struct wmm_parameter_element *wmm = 60169691Skan (struct wmm_parameter_element *) (pos + 2); 61169691Skan int e; 62169691Skan 63169691Skan if (!hapd->conf->wmm_enabled) 64169691Skan return eid; 65169691Skan eid[0] = WLAN_EID_VENDOR_SPECIFIC; 66169691Skan wmm->oui[0] = 0x00; 67169691Skan wmm->oui[1] = 0x50; 68169691Skan wmm->oui[2] = 0xf2; 69169691Skan wmm->oui_type = WMM_OUI_TYPE; 70169691Skan wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT; 71169691Skan wmm->version = WMM_VERSION; 72169691Skan wmm->qos_info = hapd->parameter_set_count & 0xf; 73169691Skan 74169691Skan if (hapd->conf->wmm_uapsd) 75169691Skan wmm->qos_info |= 0x80; 76169691Skan 77169691Skan /* fill in a parameter set record for each AC */ 78169691Skan for (e = 0; e < 4; e++) { 79169691Skan struct wmm_ac_parameter *ac = &wmm->ac[e]; 80169691Skan struct hostapd_wmm_ac_params *acp = 81169691Skan &hapd->iconf->wmm_ac_params[e]; 82169691Skan 83169691Skan ac->aci_aifsn = wmm_aci_aifsn(acp->aifs, 84169691Skan acp->admission_control_mandatory, 85169691Skan e); 86169691Skan ac->cw = wmm_ecw(acp->cwmin, acp->cwmax); 87169691Skan ac->txop_limit = host_to_le16(acp->txop_limit); 88169691Skan } 89169691Skan 90169691Skan pos = (u8 *) (wmm + 1); 91169691Skan eid[1] = pos - eid - 2; /* element length */ 92169691Skan 93169691Skan return pos; 94169691Skan} 95169691Skan 96169691Skan 97169691Skan/* This function is called when a station sends an association request with 98169691Skan * WMM info element. The function returns zero on success or non-zero on any 99169691Skan * error in WMM element. eid does not include Element ID and Length octets. */ 100169691Skanint hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) 101169691Skan{ 102169691Skan struct wmm_information_element *wmm; 103169691Skan 104169691Skan wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len); 105169691Skan 106169691Skan if (len < sizeof(struct wmm_information_element)) { 107169691Skan wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", 108169691Skan (unsigned long) len); 109169691Skan return -1; 110169691Skan } 111169691Skan 112169691Skan wmm = (struct wmm_information_element *) eid; 113169691Skan wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x " 114169691Skan "OUI type %d OUI sub-type %d version %d QoS info 0x%x", 115169691Skan wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type, 116169691Skan wmm->oui_subtype, wmm->version, wmm->qos_info); 117169691Skan if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT || 118169691Skan wmm->version != WMM_VERSION) { 119169691Skan wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version"); 120169691Skan return -1; 121169691Skan } 122169691Skan 123169691Skan return 0; 124169691Skan} 125169691Skan 126169691Skan 127169691Skanstatic void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, 128169691Skan const struct wmm_tspec_element *tspec, 129169691Skan u8 action_code, u8 dialogue_token, u8 status_code) 130169691Skan{ 131169691Skan u8 buf[256]; 132169691Skan struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf; 133169691Skan struct wmm_tspec_element *t = (struct wmm_tspec_element *) 134169691Skan m->u.action.u.wmm_action.variable; 135169691Skan int len; 136169691Skan 137169691Skan hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, 138169691Skan HOSTAPD_LEVEL_DEBUG, 139169691Skan "action response - reason %d", status_code); 140169691Skan os_memset(buf, 0, sizeof(buf)); 141169691Skan m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 142169691Skan WLAN_FC_STYPE_ACTION); 143169691Skan os_memcpy(m->da, addr, ETH_ALEN); 144169691Skan os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); 145169691Skan os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); 146169691Skan m->u.action.category = WLAN_ACTION_WMM; 147169691Skan m->u.action.u.wmm_action.action_code = action_code; 148169691Skan m->u.action.u.wmm_action.dialog_token = dialogue_token; 149169691Skan m->u.action.u.wmm_action.status_code = status_code; 150169691Skan os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); 151169691Skan len = ((u8 *) (t + 1)) - buf; 152169691Skan 153169691Skan if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0) 154169691Skan perror("wmm_send_action: send"); 155169691Skan} 156169691Skan 157169691Skan 158169691Skanint wmm_process_tspec(struct wmm_tspec_element *tspec) 159169691Skan{ 160169691Skan int medium_time, pps, duration; 161169691Skan int up, psb, dir, tid; 162169691Skan u16 val, surplus; 163169691Skan 164169691Skan up = (tspec->ts_info[1] >> 3) & 0x07; 165169691Skan psb = (tspec->ts_info[1] >> 2) & 0x01; 166169691Skan dir = (tspec->ts_info[0] >> 5) & 0x03; 167169691Skan tid = (tspec->ts_info[0] >> 1) & 0x0f; 168169691Skan wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d", 169169691Skan up, psb, dir, tid); 170169691Skan val = le_to_host16(tspec->nominal_msdu_size); 171169691Skan wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s", 172169691Skan val & 0x7fff, val & 0x8000 ? " (fixed)" : ""); 173169691Skan wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps", 174169691Skan le_to_host32(tspec->mean_data_rate)); 175169691Skan wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps", 176169691Skan le_to_host32(tspec->minimum_phy_rate)); 177169691Skan val = le_to_host16(tspec->surplus_bandwidth_allowance); 178169691Skan wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u", 179169691Skan val >> 13, 10000 * (val & 0x1fff) / 0x2000); 180169691Skan 181169691Skan val = le_to_host16(tspec->nominal_msdu_size); 182169691Skan if (val == 0) { 183169691Skan wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)"); 184169691Skan return WMM_ADDTS_STATUS_INVALID_PARAMETERS; 185169691Skan } 186169691Skan /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */ 187169691Skan pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val; 188169691Skan wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d", 189169691Skan pps); 190169691Skan 191169691Skan if (le_to_host32(tspec->minimum_phy_rate) < 1000000) { 192169691Skan wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate"); 193169691Skan return WMM_ADDTS_STATUS_INVALID_PARAMETERS; 194169691Skan } 195169691Skan 196169691Skan duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 / 197169691Skan (le_to_host32(tspec->minimum_phy_rate) / 1000000) + 198169691Skan 50 /* FIX: proper SIFS + ACK duration */; 199169691Skan 200169691Skan /* unsigned binary number with an implicit binary point after the 201169691Skan * leftmost 3 bits, i.e., 0x2000 = 1.0 */ 202169691Skan surplus = le_to_host16(tspec->surplus_bandwidth_allowance); 203169691Skan if (surplus <= 0x2000) { 204169691Skan wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not " 205169691Skan "greater than unity"); 206169691Skan return WMM_ADDTS_STATUS_INVALID_PARAMETERS; 207169691Skan } 208169691Skan 209169691Skan medium_time = surplus * pps * duration / 0x2000; 210169691Skan wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); 211169691Skan 212169691Skan /* 213169691Skan * TODO: store list of granted (and still active) TSPECs and check 214169691Skan * whether there is available medium time for this request. For now, 215169691Skan * just refuse requests that would by themselves take very large 216169691Skan * portion of the available bandwidth. 217169691Skan */ 218169691Skan if (medium_time > 750000) { 219169691Skan wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over " 220169691Skan "75%% of available bandwidth"); 221169691Skan return WMM_ADDTS_STATUS_REFUSED; 222169691Skan } 223169691Skan 224169691Skan /* Convert to 32 microseconds per second unit */ 225169691Skan tspec->medium_time = host_to_le16(medium_time / 32); 226169691Skan 227169691Skan return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED; 228169691Skan} 229169691Skan 230169691Skan 231169691Skanstatic void wmm_addts_req(struct hostapd_data *hapd, 232169691Skan const struct ieee80211_mgmt *mgmt, 233169691Skan struct wmm_tspec_element *tspec, size_t len) 234169691Skan{ 235169691Skan const u8 *end = ((const u8 *) mgmt) + len; 236169691Skan int res; 237169691Skan 238169691Skan if ((const u8 *) (tspec + 1) > end) { 239169691Skan wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); 240169691Skan return; 241169691Skan } 242169691Skan 243169691Skan wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC " 244169691Skan "from " MACSTR, 245169691Skan mgmt->u.action.u.wmm_action.dialog_token, 246169691Skan MAC2STR(mgmt->sa)); 247169691Skan 248169691Skan res = wmm_process_tspec(tspec); 249169691Skan wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res); 250169691Skan 251169691Skan wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, 252169691Skan mgmt->u.action.u.wmm_action.dialog_token, res); 253169691Skan} 254169691Skan 255169691Skan 256169691Skanvoid hostapd_wmm_action(struct hostapd_data *hapd, 257169691Skan const struct ieee80211_mgmt *mgmt, size_t len) 258169691Skan{ 259169691Skan int action_code; 260169691Skan int left = len - IEEE80211_HDRLEN - 4; 261169691Skan const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4; 262169691Skan struct ieee802_11_elems elems; 263169691Skan struct sta_info *sta = ap_get_sta(hapd, mgmt->sa); 264169691Skan 265169691Skan /* check that the request comes from a valid station */ 266169691Skan if (!sta || 267169691Skan (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) != 268169691Skan (WLAN_STA_ASSOC | WLAN_STA_WMM)) { 269169691Skan hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, 270169691Skan HOSTAPD_LEVEL_DEBUG, 271169691Skan "wmm action received is not from associated wmm" 272169691Skan " station"); 273169691Skan /* TODO: respond with action frame refused status code */ 274169691Skan return; 275169691Skan } 276169691Skan 277169691Skan /* extract the tspec info element */ 278169691Skan if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { 279169691Skan hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, 280169691Skan HOSTAPD_LEVEL_DEBUG, 281169691Skan "hostapd_wmm_action - could not parse wmm " 282169691Skan "action"); 283169691Skan /* TODO: respond with action frame invalid parameters status 284169691Skan * code */ 285169691Skan return; 286169691Skan } 287169691Skan 288169691Skan if (!elems.wmm_tspec || 289169691Skan elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) { 290169691Skan hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, 291169691Skan HOSTAPD_LEVEL_DEBUG, 292169691Skan "hostapd_wmm_action - missing or wrong length " 293169691Skan "tspec"); 294169691Skan /* TODO: respond with action frame invalid parameters status 295169691Skan * code */ 296169691Skan return; 297169691Skan } 298169691Skan 299169691Skan /* TODO: check the request is for an AC with ACM set, if not, refuse 300169691Skan * request */ 301169691Skan 302169691Skan action_code = mgmt->u.action.u.wmm_action.action_code; 303169691Skan switch (action_code) { 304169691Skan case WMM_ACTION_CODE_ADDTS_REQ: 305169691Skan wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *) 306169691Skan (elems.wmm_tspec - 2), len); 307169691Skan return; 308169691Skan#if 0 309169691Skan /* TODO: needed for client implementation */ 310169691Skan case WMM_ACTION_CODE_ADDTS_RESP: 311169691Skan wmm_setup_request(hapd, mgmt, len); 312169691Skan return; 313169691Skan /* TODO: handle station teardown requests */ 314169691Skan case WMM_ACTION_CODE_DELTS: 315169691Skan wmm_teardown(hapd, mgmt, len); 316169691Skan return; 317169691Skan#endif 318169691Skan } 319169691Skan 320169691Skan hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, 321169691Skan HOSTAPD_LEVEL_DEBUG, 322169691Skan "hostapd_wmm_action - unknown action code %d", 323169691Skan action_code); 324169691Skan} 325169691Skan