1 /*
2 * hostapd / WMM (Wi-Fi Multimedia)
3 * Copyright 2002-2003, Instant802 Networks, Inc.
4 * Copyright 2005-2006, Devicescape Software, Inc.
5 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Alternatively, this software may be distributed under the terms of BSD
12 * license.
13 *
14 * See README and COPYING for more details.
15 */
16
17 #include "utils/includes.h"
18
19 #include "utils/common.h"
20 #include "common/ieee802_11_defs.h"
21 #include "common/ieee802_11_common.h"
22 #include "hostapd.h"
23 #include "ieee802_11.h"
24 #include "sta_info.h"
25 #include "ap_config.h"
26 #include "ap_drv_ops.h"
27 #include "wmm.h"
28
29
30 /* TODO: maintain separate sequence and fragment numbers for each AC
31 * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
32 * if only WMM stations are receiving a certain group */
33
34
wmm_aci_aifsn(int aifsn,int acm,int aci)35 static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
36 {
37 u8 ret;
38 ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
39 if (acm)
40 ret |= WMM_AC_ACM;
41 ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
42 return ret;
43 }
44
45
wmm_ecw(int ecwmin,int ecwmax)46 static inline u8 wmm_ecw(int ecwmin, int ecwmax)
47 {
48 return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
49 ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
50 }
51
52
53 /*
54 * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
55 * Response frames.
56 */
hostapd_eid_wmm(struct hostapd_data * hapd,u8 * eid)57 u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
58 {
59 u8 *pos = eid;
60 struct wmm_parameter_element *wmm =
61 (struct wmm_parameter_element *) (pos + 2);
62 int e;
63
64 if (!hapd->conf->wmm_enabled)
65 return eid;
66 eid[0] = WLAN_EID_VENDOR_SPECIFIC;
67 wmm->oui[0] = 0x00;
68 wmm->oui[1] = 0x50;
69 wmm->oui[2] = 0xf2;
70 wmm->oui_type = WMM_OUI_TYPE;
71 wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
72 wmm->version = WMM_VERSION;
73 wmm->qos_info = hapd->parameter_set_count & 0xf;
74
75 if (hapd->conf->wmm_uapsd &&
76 (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
77 wmm->qos_info |= 0x80;
78
79 wmm->reserved = 0;
80
81 /* fill in a parameter set record for each AC */
82 for (e = 0; e < 4; e++) {
83 struct wmm_ac_parameter *ac = &wmm->ac[e];
84 struct hostapd_wmm_ac_params *acp =
85 &hapd->iconf->wmm_ac_params[e];
86
87 ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
88 acp->admission_control_mandatory,
89 e);
90 ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
91 ac->txop_limit = host_to_le16(acp->txop_limit);
92 }
93
94 pos = (u8 *) (wmm + 1);
95 eid[1] = pos - eid - 2; /* element length */
96
97 return pos;
98 }
99
100
101 /*
102 * This function is called when a station sends an association request with
103 * WMM info element. The function returns 1 on success or 0 on any error in WMM
104 * element. eid does not include Element ID and Length octets.
105 */
hostapd_eid_wmm_valid(struct hostapd_data * hapd,const u8 * eid,size_t len)106 int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
107 {
108 struct wmm_information_element *wmm;
109
110 wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
111
112 if (len < sizeof(struct wmm_information_element)) {
113 wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
114 (unsigned long) len);
115 return 0;
116 }
117
118 wmm = (struct wmm_information_element *) eid;
119 wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x "
120 "OUI type %d OUI sub-type %d version %d QoS info 0x%x",
121 wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
122 wmm->oui_subtype, wmm->version, wmm->qos_info);
123 if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
124 wmm->version != WMM_VERSION) {
125 wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
126 return 0;
127 }
128
129 return 1;
130 }
131
132
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)133 static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
134 const struct wmm_tspec_element *tspec,
135 u8 action_code, u8 dialogue_token, u8 status_code)
136 {
137 u8 buf[256];
138 struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
139 struct wmm_tspec_element *t = (struct wmm_tspec_element *)
140 m->u.action.u.wmm_action.variable;
141 int len;
142
143 hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
144 HOSTAPD_LEVEL_DEBUG,
145 "action response - reason %d", status_code);
146 os_memset(buf, 0, sizeof(buf));
147 m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
148 WLAN_FC_STYPE_ACTION);
149 os_memcpy(m->da, addr, ETH_ALEN);
150 os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
151 os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
152 m->u.action.category = WLAN_ACTION_WMM;
153 m->u.action.u.wmm_action.action_code = action_code;
154 m->u.action.u.wmm_action.dialog_token = dialogue_token;
155 m->u.action.u.wmm_action.status_code = status_code;
156 os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
157 len = ((u8 *) (t + 1)) - buf;
158
159 if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
160 perror("wmm_send_action: send");
161 }
162
163
wmm_process_tspec(struct wmm_tspec_element * tspec)164 int wmm_process_tspec(struct wmm_tspec_element *tspec)
165 {
166 int medium_time, pps, duration;
167 int up, psb, dir, tid;
168 u16 val, surplus;
169
170 up = (tspec->ts_info[1] >> 3) & 0x07;
171 psb = (tspec->ts_info[1] >> 2) & 0x01;
172 dir = (tspec->ts_info[0] >> 5) & 0x03;
173 tid = (tspec->ts_info[0] >> 1) & 0x0f;
174 wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
175 up, psb, dir, tid);
176 val = le_to_host16(tspec->nominal_msdu_size);
177 wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
178 val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
179 wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
180 le_to_host32(tspec->mean_data_rate));
181 wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
182 le_to_host32(tspec->minimum_phy_rate));
183 val = le_to_host16(tspec->surplus_bandwidth_allowance);
184 wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
185 val >> 13, 10000 * (val & 0x1fff) / 0x2000);
186
187 val = le_to_host16(tspec->nominal_msdu_size);
188 if (val == 0) {
189 wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
190 return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
191 }
192 /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
193 pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
194 wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
195 pps);
196
197 if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
198 wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
199 return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
200 }
201
202 duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
203 (le_to_host32(tspec->minimum_phy_rate) / 1000000) +
204 50 /* FIX: proper SIFS + ACK duration */;
205
206 /* unsigned binary number with an implicit binary point after the
207 * leftmost 3 bits, i.e., 0x2000 = 1.0 */
208 surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
209 if (surplus <= 0x2000) {
210 wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
211 "greater than unity");
212 return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
213 }
214
215 medium_time = surplus * pps * duration / 0x2000;
216 wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
217
218 /*
219 * TODO: store list of granted (and still active) TSPECs and check
220 * whether there is available medium time for this request. For now,
221 * just refuse requests that would by themselves take very large
222 * portion of the available bandwidth.
223 */
224 if (medium_time > 750000) {
225 wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
226 "75%% of available bandwidth");
227 return WMM_ADDTS_STATUS_REFUSED;
228 }
229
230 /* Convert to 32 microseconds per second unit */
231 tspec->medium_time = host_to_le16(medium_time / 32);
232
233 return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
234 }
235
236
wmm_addts_req(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,struct wmm_tspec_element * tspec,size_t len)237 static void wmm_addts_req(struct hostapd_data *hapd,
238 const struct ieee80211_mgmt *mgmt,
239 struct wmm_tspec_element *tspec, size_t len)
240 {
241 const u8 *end = ((const u8 *) mgmt) + len;
242 int res;
243
244 if ((const u8 *) (tspec + 1) > end) {
245 wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
246 return;
247 }
248
249 wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
250 "from " MACSTR,
251 mgmt->u.action.u.wmm_action.dialog_token,
252 MAC2STR(mgmt->sa));
253
254 res = wmm_process_tspec(tspec);
255 wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
256
257 wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
258 mgmt->u.action.u.wmm_action.dialog_token, res);
259 }
260
261
hostapd_wmm_action(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)262 void hostapd_wmm_action(struct hostapd_data *hapd,
263 const struct ieee80211_mgmt *mgmt, size_t len)
264 {
265 int action_code;
266 int left = len - IEEE80211_HDRLEN - 4;
267 const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4;
268 struct ieee802_11_elems elems;
269 struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
270
271 /* check that the request comes from a valid station */
272 if (!sta ||
273 (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
274 (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
275 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
276 HOSTAPD_LEVEL_DEBUG,
277 "wmm action received is not from associated wmm"
278 " station");
279 /* TODO: respond with action frame refused status code */
280 return;
281 }
282
283 /* extract the tspec info element */
284 if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
285 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
286 HOSTAPD_LEVEL_DEBUG,
287 "hostapd_wmm_action - could not parse wmm "
288 "action");
289 /* TODO: respond with action frame invalid parameters status
290 * code */
291 return;
292 }
293
294 if (!elems.wmm_tspec ||
295 elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
296 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
297 HOSTAPD_LEVEL_DEBUG,
298 "hostapd_wmm_action - missing or wrong length "
299 "tspec");
300 /* TODO: respond with action frame invalid parameters status
301 * code */
302 return;
303 }
304
305 /* TODO: check the request is for an AC with ACM set, if not, refuse
306 * request */
307
308 action_code = mgmt->u.action.u.wmm_action.action_code;
309 switch (action_code) {
310 case WMM_ACTION_CODE_ADDTS_REQ:
311 wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
312 (elems.wmm_tspec - 2), len);
313 return;
314 #if 0
315 /* TODO: needed for client implementation */
316 case WMM_ACTION_CODE_ADDTS_RESP:
317 wmm_setup_request(hapd, mgmt, len);
318 return;
319 /* TODO: handle station teardown requests */
320 case WMM_ACTION_CODE_DELTS:
321 wmm_teardown(hapd, mgmt, len);
322 return;
323 #endif
324 }
325
326 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
327 HOSTAPD_LEVEL_DEBUG,
328 "hostapd_wmm_action - unknown action code %d",
329 action_code);
330 }
331