diff --git a/hostapd/wme.c b/hostapd/wme.c index 50efd10c5..700410cc8 100644 --- a/hostapd/wme.c +++ b/hostapd/wme.c @@ -165,43 +165,102 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, } -/* 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 wmm_frame_exchange_time(int bytes, int data_rate, int encryption, - int cts_protection) -{ - /* 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; -} - - static void wmm_addts_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, struct wmm_tspec_element *tspec, size_t len) { - /* FIX: should not use floating point types */ - double medium_time, pps; + u8 *end = ((u8 *) mgmt) + len; + int medium_time, pps, duration; + int up, psb, dir, tid; + u16 val, surplus; - /* TODO: account for airtime and answer no to tspec setup requests - * when none left!! */ + if ((u8 *) (tspec + 1) > end) { + wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); + return; + } - pps = (le_to_host32(tspec->mean_data_rate) / 8.0) / - le_to_host16(tspec->nominal_msdu_size); - medium_time = (le_to_host16(tspec->surplus_bandwidth_allowance) / 8) * - pps * - wmm_frame_exchange_time(le_to_host16(tspec->nominal_msdu_size), - le_to_host32(tspec->minimum_phy_rate), - 0, 0); - tspec->medium_time = host_to_le16(medium_time * 1000000.0 / 32.0); + 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; + } + + /* Convert to 32 microseconds per second unit */ + tspec->medium_time = host_to_le16(medium_time / 32); 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; + +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); } diff --git a/hostapd/wme.h b/hostapd/wme.h index a1ca6d4e4..2f851aedc 100644 --- a/hostapd/wme.h +++ b/hostapd/wme.h @@ -16,91 +16,6 @@ #ifndef WME_H #define WME_H -/* - * WMM Information Element (used in (Re)Association Request frames; may also be - * used in Beacon frames) - */ -struct wmm_information_element { - /* Element ID: 221 (0xdd); Length: 7 */ - /* required fields for WMM version 1 */ - u8 oui[3]; /* 00:50:f2 */ - u8 oui_type; /* 2 */ - u8 oui_subtype; /* 0 */ - u8 version; /* 1 for WMM version 1.0 */ - u8 qos_info; /* AP/STA specific QoS info */ - -} __attribute__ ((packed)); - -#define WMM_AC_AIFSN_MASK 0x0f -#define WMM_AC_AIFNS_SHIFT 0 -#define WMM_AC_ACM 0x10 -#define WMM_AC_ACI_MASK 0x60 -#define WMM_AC_ACI_SHIFT 5 - -#define WMM_AC_ECWMIN_MASK 0x0f -#define WMM_AC_ECWMIN_SHIFT 0 -#define WMM_AC_ECWMAX_MASK 0xf0 -#define WMM_AC_ECWMAX_SHIFT 4 - -struct wmm_ac_parameter { - u8 aci_aifsn; /* AIFSN, ACM, ACI */ - u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ - le16 txop_limit; -} __attribute__ ((packed)); - -/* - * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association - * Response frmaes) - */ -struct wmm_parameter_element { - /* Element ID: 221 (0xdd); Length: 24 */ - /* required fields for WMM version 1 */ - u8 oui[3]; /* 00:50:f2 */ - u8 oui_type; /* 2 */ - u8 oui_subtype; /* 1 */ - u8 version; /* 1 for WMM version 1.0 */ - u8 qos_info; /* AP/STA specif QoS info */ - u8 reserved; /* 0 */ - struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ - -} __attribute__ ((packed)); - -/* WMM TSPEC Element */ -struct wmm_tspec_element { - u8 eid; /* 221 = 0xdd */ - u8 length; /* 6 + 55 = 61 */ - u8 oui[3]; /* 00:50:f2 */ - u8 oui_type; /* 2 */ - u8 oui_subtype; /* 2 */ - u8 version; /* 1 */ - /* WMM TSPEC body (55 octets): */ - u8 ts_info[3]; - le16 nominal_msdu_size; - le16 maximum_msdu_size; - le32 minimum_service_interval; - le32 maximum_service_interval; - le32 inactivity_interval; - le32 suspension_interval; - le32 service_start_time; - le32 minimum_data_rate; - le32 mean_data_rate; - le32 peak_data_rate; - le32 maximum_burst_size; - le32 delay_bound; - le32 minimum_phy_rate; - le16 surplus_bandwidth_allowance; - le16 medium_time; -} __attribute__ ((packed)); - - -/* Access Categories / ACI to AC coding */ -enum { - WMM_AC_BE = 0 /* Best Effort */, - WMM_AC_BK = 1 /* Background */, - WMM_AC_VI = 2 /* Video */, - WMM_AC_VO = 3 /* Voice */ -}; - struct ieee80211_mgmt; u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid); diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index f77e33f52..67c704d7a 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -584,6 +584,91 @@ struct mimo_pwr_save_action { /* 2 - Reserved */ #define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 +/* + * WMM Information Element (used in (Re)Association Request frames; may also be + * used in Beacon frames) + */ +struct wmm_information_element { + /* Element ID: 221 (0xdd); Length: 7 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 0 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + +} STRUCT_PACKED; + +#define WMM_AC_AIFSN_MASK 0x0f +#define WMM_AC_AIFNS_SHIFT 0 +#define WMM_AC_ACM 0x10 +#define WMM_AC_ACI_MASK 0x60 +#define WMM_AC_ACI_SHIFT 5 + +#define WMM_AC_ECWMIN_MASK 0x0f +#define WMM_AC_ECWMIN_SHIFT 0 +#define WMM_AC_ECWMAX_MASK 0xf0 +#define WMM_AC_ECWMAX_SHIFT 4 + +struct wmm_ac_parameter { + u8 aci_aifsn; /* AIFSN, ACM, ACI */ + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ + le16 txop_limit; +} STRUCT_PACKED; + +/* + * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association + * Response frmaes) + */ +struct wmm_parameter_element { + /* Element ID: 221 (0xdd); Length: 24 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 1 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specif QoS info */ + u8 reserved; /* 0 */ + struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ + +} STRUCT_PACKED; + +/* WMM TSPEC Element */ +struct wmm_tspec_element { + u8 eid; /* 221 = 0xdd */ + u8 length; /* 6 + 55 = 61 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 2 */ + u8 version; /* 1 */ + /* WMM TSPEC body (55 octets): */ + u8 ts_info[3]; + le16 nominal_msdu_size; + le16 maximum_msdu_size; + le32 minimum_service_interval; + le32 maximum_service_interval; + le32 inactivity_interval; + le32 suspension_interval; + le32 service_start_time; + le32 minimum_data_rate; + le32 mean_data_rate; + le32 peak_data_rate; + le32 maximum_burst_size; + le32 delay_bound; + le32 minimum_phy_rate; + le16 surplus_bandwidth_allowance; + le16 medium_time; +} STRUCT_PACKED; + + +/* Access Categories / ACI to AC coding */ +enum { + WMM_AC_BE = 0 /* Best Effort */, + WMM_AC_BK = 1 /* Background */, + WMM_AC_VI = 2 /* Video */, + WMM_AC_VO = 3 /* Voice */ +}; + #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c index 82d78977c..fd1fa5581 100644 --- a/wpa_supplicant/mlme.c +++ b/wpa_supplicant/mlme.c @@ -1012,6 +1012,59 @@ static int ieee80211_ft_assoc_resp(struct wpa_supplicant *wpa_s, } +static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + struct ieee80211_mgmt *mgmt; + struct wmm_tspec_element *tspec; + size_t alen; + int tid, up; + + wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC"); + mgmt = NULL; + alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; + + buf = wpabuf_alloc(alen + sizeof(*tspec)); + if (buf == NULL) + return; + + mgmt = wpabuf_put(buf, alen); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WMM; + mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ; + mgmt->u.action.u.wmm_action.dialog_token = 1; + mgmt->u.action.u.wmm_action.status_code = 0; + + tspec = wpabuf_put(buf, sizeof(*tspec)); + tspec->eid = WLAN_EID_VENDOR_SPECIFIC; + tspec->length = sizeof(*tspec) - 2; + tspec->oui[0] = 0x00; + tspec->oui[1] = 0x50; + tspec->oui[2] = 0xf2; + tspec->oui_type = 2; + tspec->oui_subtype = 2; + tspec->version = 1; + + tid = 1; + up = 6; /* Voice */ + tspec->ts_info[0] = (tid << 1) | + (WMM_TSPEC_DIRECTION_BI_DIRECTIONAL << 5) | + BIT(7); + tspec->ts_info[1] = up << 3; + tspec->nominal_msdu_size = host_to_le16(1530); + tspec->mean_data_rate = host_to_le32(128000); /* bits per second */ + tspec->minimum_phy_rate = host_to_le32(6000000); + tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */ + + ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); +} + + static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, @@ -1162,6 +1215,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len); ieee80211_associated(wpa_s); + + if (os_strcmp(wpa_s->driver->name, "test") == 0 && + elems.wmm && wpa_s->mlme.wmm_enabled) { + /* Test WMM-AC - send ADDTS for WMM TSPEC */ + ieee80211_tx_addts(wpa_s); + } } @@ -1788,6 +1847,119 @@ static void ieee80211_rx_mgmt_sa_query_action( #endif /* CONFIG_IEEE80211W */ +static void dump_tspec(struct wmm_tspec_element *tspec) +{ + int up, psb, dir, tid; + u16 val; + + 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->medium_time); + wpa_printf(MSG_DEBUG, "WMM: Medium Time: %u (= %u usec/sec)", + val, 32 * val); +} + + +static int is_wmm_tspec(const u8 *ie, size_t len) +{ + const struct wmm_tspec_element *tspec; + + if (len < sizeof(*tspec)) + return 0; + + tspec = (const struct wmm_tspec_element *) ie; + if (tspec->eid != WLAN_EID_VENDOR_SPECIFIC || + tspec->length < sizeof(*tspec) - 2 || + tspec->oui[0] != 0x00 || tspec->oui[1] != 0x50 || + tspec->oui[2] != 0xf2 || tspec->oui_type != 2 || + tspec->oui_subtype != 2 || tspec->version != 1) + return 0; + + return 1; +} + + +static void ieee80211_rx_addts_resp( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + size_t var_len) +{ + struct wmm_tspec_element *tspec; + + wpa_printf(MSG_DEBUG, "WMM: Received ADDTS Response"); + wpa_hexdump(MSG_MSGDUMP, "WMM: ADDTS Response IE(s)", + mgmt->u.action.u.wmm_action.variable, var_len); + if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len)) + return; + tspec = (struct wmm_tspec_element *) + mgmt->u.action.u.wmm_action.variable; + dump_tspec(tspec); +} + + +static void ieee80211_rx_delts( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + size_t var_len) +{ + struct wmm_tspec_element *tspec; + + wpa_printf(MSG_DEBUG, "WMM: Received DELTS"); + wpa_hexdump(MSG_MSGDUMP, "WMM: DELTS IE(s)", + mgmt->u.action.u.wmm_action.variable, var_len); + if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len)) + return; + tspec = (struct wmm_tspec_element *) + mgmt->u.action.u.wmm_action.variable; + dump_tspec(tspec); +} + + +static void ieee80211_rx_mgmt_wmm_action( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) +{ + size_t alen; + + alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; + if (len < alen) { + wpa_printf(MSG_DEBUG, "WMM: Received Action frame too short"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM: Received Action frame: Action Code %d, " + "Dialog Token %d, Status Code %d", + mgmt->u.action.u.wmm_action.action_code, + mgmt->u.action.u.wmm_action.dialog_token, + mgmt->u.action.u.wmm_action.status_code); + + switch (mgmt->u.action.u.wmm_action.action_code) { + case WMM_ACTION_CODE_ADDTS_RESP: + ieee80211_rx_addts_resp(wpa_s, mgmt, len, len - alen); + break; + case WMM_ACTION_CODE_DELTS: + ieee80211_rx_delts(wpa_s, mgmt, len, len - alen); + break; + default: + wpa_printf(MSG_DEBUG, "WMM: Unsupported Action Code %d", + mgmt->u.action.u.wmm_action.action_code); + break; + } +} + + static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, @@ -1809,6 +1981,9 @@ static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s, ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status); break; #endif /* CONFIG_IEEE80211W */ + case WLAN_ACTION_WMM: + ieee80211_rx_mgmt_wmm_action(wpa_s, mgmt, len, rx_status); + break; default: wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d", mgmt->u.action.category);