diff options
Diffstat (limited to 'hostapd/wme.c')
-rw-r--r-- | hostapd/wme.c | 285 |
1 files changed, 179 insertions, 106 deletions
diff --git a/hostapd/wme.c b/hostapd/wme.c index 727ee7e..f2bbbd9 100644 --- a/hostapd/wme.c +++ b/hostapd/wme.c @@ -24,47 +24,63 @@ /* 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 WME stations are receiving a certain group */ + * if only WMM stations are receiving a certain group */ -static u8 wme_oui[3] = { 0x00, 0x50, 0xf2 }; +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 WME Parameter Element to Beacon and Probe Response frames. */ -u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid) +/* + * 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 wme_parameter_element *wme = - (struct wme_parameter_element *) (pos + 2); + struct wmm_parameter_element *wmm = + (struct wmm_parameter_element *) (pos + 2); int e; - if (!hapd->conf->wme_enabled) + if (!hapd->conf->wmm_enabled) return eid; eid[0] = WLAN_EID_VENDOR_SPECIFIC; - wme->oui[0] = 0x00; - wme->oui[1] = 0x50; - wme->oui[2] = 0xf2; - wme->oui_type = WME_OUI_TYPE; - wme->oui_subtype = WME_OUI_SUBTYPE_PARAMETER_ELEMENT; - wme->version = WME_VERSION; - wme->acInfo = hapd->parameter_set_count & 0xf; + 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 wme_ac_parameter *ac = &wme->ac[e]; - struct hostapd_wme_ac_params *acp = - &hapd->iconf->wme_ac_params[e]; - - ac->aifsn = acp->aifs; - ac->acm = acp->admission_control_mandatory; - ac->aci = e; - ac->reserved = 0; - ac->eCWmin = acp->cwmin; - ac->eCWmax = acp->cwmax; - ac->txopLimit = host_to_le16(acp->txopLimit); + 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 *) (wme + 1); + pos = (u8 *) (wmm + 1); eid[1] = pos - eid - 2; /* element length */ return pos; @@ -72,31 +88,28 @@ u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid) /* This function is called when a station sends an association request with - * WME info element. The function returns zero on success or non-zero on any - * error in WME element. eid does not include Element ID and Length octets. */ -int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len) + * 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 wme_information_element *wme; + struct wmm_information_element *wmm; - wpa_hexdump(MSG_MSGDUMP, "WME IE", eid, len); + wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len); - if (len < sizeof(struct wme_information_element)) { - wpa_printf(MSG_DEBUG, "Too short WME IE (len=%lu)", + if (len < sizeof(struct wmm_information_element)) { + wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", (unsigned long) len); return -1; } - wme = (struct wme_information_element *) eid; - wpa_printf(MSG_DEBUG, "Validating WME IE: OUI %02x:%02x:%02x " - "OUI type %d OUI sub-type %d version %d", - wme->oui[0], wme->oui[1], wme->oui[2], wme->oui_type, - wme->oui_subtype, wme->version); - if (os_memcmp(wme->oui, wme_oui, sizeof(wme_oui)) != 0 || - wme->oui_type != WME_OUI_TYPE || - wme->oui_subtype != WME_OUI_SUBTYPE_INFORMATION_ELEMENT || - wme->version != WME_VERSION) { - wpa_printf(MSG_DEBUG, "Unsupported WME IE OUI/Type/Subtype/" - "Version"); + 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; } @@ -105,31 +118,30 @@ int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len) /* This function is called when a station sends an ACK frame for an AssocResp - * frame (status=success) and the matching AssocReq contained a WME element. + * frame (status=success) and the matching AssocReq contained a WMM element. */ -int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta) +int hostapd_wmm_sta_config(struct hostapd_data *hapd, struct sta_info *sta) { - /* update kernel STA data for WME related items (WLAN_STA_WPA flag) */ - if (sta->flags & WLAN_STA_WME) + /* 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_WME, ~0); + WLAN_STA_WMM, ~0); else hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - 0, ~WLAN_STA_WME); + 0, ~WLAN_STA_WMM); return 0; } -static void wme_send_action(struct hostapd_data *hapd, const u8 *addr, - const struct wme_tspec_info_element *tspec, +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 wme_tspec_info_element *t = - (struct wme_tspec_info_element *) - m->u.action.u.wme_action.variable; + 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, @@ -142,55 +154,117 @@ static void wme_send_action(struct hostapd_data *hapd, const u8 *addr, 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.wme_action.action_code = action_code; - m->u.action.u.wme_action.dialog_token = dialogue_token; - m->u.action.u.wme_action.status_code = status_code; - os_memcpy(t, tspec, sizeof(struct wme_tspec_info_element)); + 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("wme_send_action: send"); + perror("wmm_send_action: send"); } -/* given frame data payload size in bytes, and data_rate in bits per second - * returns time to complete frame exchange */ -/* FIX: should not use floating point types */ -static double wme_frame_exchange_time(int bytes, int data_rate, int encryption, - int cts_protection) +static void wmm_addts_req(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, + struct wmm_tspec_element *tspec, size_t len) { - /* TODO: account for MAC/PHY headers correctly */ - /* TODO: account for encryption headers */ - /* TODO: account for WDS headers */ - /* TODO: account for CTS protection */ - /* TODO: account for SIFS + ACK at minimum PHY rate */ - return (bytes + 400) * 8.0 / data_rate; -} + u8 *end = ((u8 *) mgmt) + len; + int medium_time, pps, duration; + int up, psb, dir, tid; + u16 val, surplus; + if ((u8 *) (tspec + 1) > end) { + wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); + return; + } -static void wme_setup_request(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, - struct wme_tspec_info_element *tspec, size_t len) -{ - /* FIX: should not use floating point types */ - double medium_time, pps; + 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)); + + 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)"); + goto invalid; + } + /* 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"); + goto invalid; + } + + 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"); + goto invalid; + } + + 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"); + wmm_send_action(hapd, mgmt->sa, tspec, + WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, + WMM_ADDTS_STATUS_REFUSED); + return; + } - /* TODO: account for airtime and answer no to tspec setup requests - * when none left!! */ + /* Convert to 32 microseconds per second unit */ + tspec->medium_time = host_to_le16(medium_time / 32); - pps = (tspec->mean_data_rate / 8.0) / tspec->nominal_msdu_size; - medium_time = (tspec->surplus_bandwidth_allowance / 8) * pps * - wme_frame_exchange_time(tspec->nominal_msdu_size, - tspec->minimum_phy_rate, 0, 0); - tspec->medium_time = medium_time * 1000000.0 / 32.0; + wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, + WMM_ADDTS_STATUS_ADMISSION_ACCEPTED); + return; - wme_send_action(hapd, mgmt->sa, tspec, WME_ACTION_CODE_SETUP_RESPONSE, - mgmt->u.action.u.wme_action.dialog_token, - WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED); +invalid: + wmm_send_action(hapd, mgmt->sa, tspec, + WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, + WMM_ADDTS_STATUS_INVALID_PARAMETERS); } -void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, +void hostapd_wmm_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, size_t len) { int action_code; @@ -201,11 +275,11 @@ void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, /* check that the request comes from a valid station */ if (!sta || - (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WME)) != - (WLAN_STA_ASSOC | WLAN_STA_WME)) { + (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, - "wme action received is not from associated wme" + "wmm action received is not from associated wmm" " station"); /* TODO: respond with action frame refused status code */ return; @@ -215,19 +289,18 @@ void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "hostapd_wme_action - could not parse wme " + "hostapd_wmm_action - could not parse wmm " "action"); /* TODO: respond with action frame invalid parameters status * code */ return; } - if (!elems.wme_tspec || - elems.wme_tspec_len != (sizeof(struct wme_tspec_info_element) - 2)) - { + 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_wme_action - missing or wrong length " + "hostapd_wmm_action - missing or wrong length " "tspec"); /* TODO: respond with action frame invalid parameters status * code */ @@ -237,26 +310,26 @@ void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, /* TODO: check the request is for an AC with ACM set, if not, refuse * request */ - action_code = mgmt->u.action.u.wme_action.action_code; + action_code = mgmt->u.action.u.wmm_action.action_code; switch (action_code) { - case WME_ACTION_CODE_SETUP_REQUEST: - wme_setup_request(hapd, mgmt, (struct wme_tspec_info_element *) - elems.wme_tspec, len); + 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 WME_ACTION_CODE_SETUP_RESPONSE: - wme_setup_request(hapd, mgmt, len); + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_setup_request(hapd, mgmt, len); return; /* TODO: handle station teardown requests */ - case WME_ACTION_CODE_TEARDOWN: - wme_teardown(hapd, mgmt, len); + case WMM_ACTION_CODE_DELTS: + wmm_teardown(hapd, mgmt, len); return; #endif } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "hostapd_wme_action - unknown action code %d", + "hostapd_wmm_action - unknown action code %d", action_code); } |