aboutsummaryrefslogtreecommitdiffstats
path: root/hostapd/wme.c
blob: 727ee7ef50a311c4449ce12a353d0f9a210066fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/*
 * hostapd / WMM (Wi-Fi Multimedia)
 * Copyright 2002-2003, Instant802 Networks, Inc.
 * Copyright 2005-2006, Devicescape Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Alternatively, this software may be distributed under the terms of BSD
 * license.
 *
 * See README and COPYING for more details.
 */

#include "includes.h"

#include "hostapd.h"
#include "ieee802_11.h"
#include "wme.h"
#include "sta_info.h"
#include "driver.h"


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


static u8 wme_oui[3] = { 0x00, 0x50, 0xf2 };


/* Add WME Parameter Element to Beacon and Probe Response frames. */
u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid)
{
	u8 *pos = eid;
	struct wme_parameter_element *wme =
		(struct wme_parameter_element *) (pos + 2);
	int e;

	if (!hapd->conf->wme_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;

	/* 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);
	}

	pos = (u8 *) (wme + 1);
	eid[1] = pos - eid - 2; /* element length */

	return pos;
}


/* 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)
{
	struct wme_information_element *wme;

	wpa_hexdump(MSG_MSGDUMP, "WME IE", eid, len);

	if (len < sizeof(struct wme_information_element)) {
		wpa_printf(MSG_DEBUG, "Too short WME 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");
		return -1;
	}

	return 0;
}


/* 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.
 */
int hostapd_wme_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)
		hostapd_sta_set_flags(hapd, sta->addr, sta->flags,
				      WLAN_STA_WME, ~0);
	else
		hostapd_sta_set_flags(hapd, sta->addr, sta->flags,
				      0, ~WLAN_STA_WME);

	return 0;
}


static void wme_send_action(struct hostapd_data *hapd, const u8 *addr,
			    const struct wme_tspec_info_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;
	int len;

	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
		       HOSTAPD_LEVEL_DEBUG,
		       "action response - reason %d", status_code);
	os_memset(buf, 0, sizeof(buf));
	m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
					WLAN_FC_STYPE_ACTION);
	os_memcpy(m->da, addr, ETH_ALEN);
	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));
	len = ((u8 *) (t + 1)) - buf;

	if (hostapd_send_mgmt_frame(hapd, m, len, 0) < 0)
		perror("wme_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)
{
	/* 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 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;

	/* TODO: account for airtime and answer no to tspec setup requests
	 * when none left!! */

	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;

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


void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
			size_t len)
{
	int action_code;
	int left = len - IEEE80211_HDRLEN - 4;
	u8 *pos = ((u8 *) mgmt) + IEEE80211_HDRLEN + 4;
	struct ieee802_11_elems elems;
	struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);

	/* 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)) {
		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
			       HOSTAPD_LEVEL_DEBUG,
			       "wme action received is not from associated wme"
			       " station");
		/* TODO: respond with action frame refused status code */
		return;
	}

	/* extract the tspec info element */
	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 "
			       "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))
	{
		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
			       HOSTAPD_LEVEL_DEBUG,
			       "hostapd_wme_action - missing or wrong length "
			       "tspec");
		/* TODO: respond with action frame invalid parameters status
		 * code */
		return;
	}

	/* 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;
	switch (action_code) {
	case WME_ACTION_CODE_SETUP_REQUEST:
		wme_setup_request(hapd, mgmt, (struct wme_tspec_info_element *)
				  elems.wme_tspec, len);
		return;
#if 0
	/* TODO: needed for client implementation */
	case WME_ACTION_CODE_SETUP_RESPONSE:
		wme_setup_request(hapd, mgmt, len);
		return;
	/* TODO: handle station teardown requests */
	case WME_ACTION_CODE_TEARDOWN:
		wme_teardown(hapd, mgmt, len);
		return;
#endif
	}

	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
		       HOSTAPD_LEVEL_DEBUG,
		       "hostapd_wme_action - unknown action code %d",
		       action_code);
}