WMM-AC: Fix hostapd processing of ADDTS Request and add test code

Calculate the estimated medium time using integer variables since there
is no real need to use floating point arithmetics here. In addition,
make sure there is no division by zero regardless of how invalid the
request from the station is. Reject invalid parameters and refuse
requests that would take most of the bandwidth by themselves.

Add test code into wpa_supplicant mlme.c to allow WMM-AC negotiation to
be tested with driver_test.
This commit is contained in:
Jouni Malinen 2009-03-06 20:12:31 +02:00 committed by Jouni Malinen
parent 5c3dd4ebbe
commit ed843aaa33
4 changed files with 346 additions and 112 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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) */

View file

@ -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);