aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--driver/modules/hostap_common.h1
-rw-r--r--hostapd/Makefile2
-rw-r--r--hostapd/beacon.c9
-rw-r--r--hostapd/config.c198
-rw-r--r--hostapd/config.h33
-rw-r--r--hostapd/hostapd.c4
-rw-r--r--hostapd/hostapd.conf118
-rw-r--r--hostapd/hostapd.h2
-rw-r--r--hostapd/ieee802_11.c68
-rw-r--r--hostapd/ieee802_11.h23
-rw-r--r--hostapd/wme.c271
-rw-r--r--hostapd/wme.h137
-rw-r--r--hostapd/wpa.c11
-rw-r--r--hostapd/wpa.h1
14 files changed, 877 insertions, 1 deletions
diff --git a/driver/modules/hostap_common.h b/driver/modules/hostap_common.h
index 51b1dc5..80c6215 100644
--- a/driver/modules/hostap_common.h
+++ b/driver/modules/hostap_common.h
@@ -51,6 +51,7 @@
#define WLAN_FC_STYPE_DISASSOC 10
#define WLAN_FC_STYPE_AUTH 11
#define WLAN_FC_STYPE_DEAUTH 12
+#define WLAN_FC_STYPE_ACTION 13
/* control */
#define WLAN_FC_STYPE_PSPOLL 10
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 66df843..bd30e63 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -36,7 +36,7 @@ OBJS = hostapd.o eloop.o ieee802_1x.o eapol_sm.o radius.o md5.o rc4.o md4.o \
common.o ieee802_11.o config.o ieee802_11_auth.o accounting.o \
sta_info.o radius_client.o sha1.o wpa.o aes_wrap.o ctrl_iface.o \
driver_conf.o os_$(CONFIG_OS).o preauth.o pmksa_cache.o beacon.o \
- hw_features.o
+ hw_features.o wme.o
HOBJS=hlr_auc_gw.o common.o os_$(CONFIG_OS).o milenage.o aes_wrap.o
diff --git a/hostapd/beacon.c b/hostapd/beacon.c
index 121ccc1..9791b06 100644
--- a/hostapd/beacon.c
+++ b/hostapd/beacon.c
@@ -20,6 +20,7 @@
#include "hostapd.h"
#include "ieee802_11.h"
#include "wpa.h"
+#include "wme.h"
#include "hostap_common.h"
#include "beacon.h"
#include "hw_features.h"
@@ -208,6 +209,10 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
+ /* Wi-Fi Wireless Multimedia Extensions */
+ if (hapd->conf->wme_enabled)
+ pos = hostapd_eid_wme(hapd, pos);
+
if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0)
perror("handle_probe_req: send");
@@ -288,6 +293,10 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
tailpos, NULL);
+ /* Wi-Fi Wireless Multimedia Extensions */
+ if (hapd->conf->wme_enabled)
+ tailpos = hostapd_eid_wme(hapd, tailpos);
+
tail_len = tailpos > tail ? tailpos - tail : 0;
if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len,
diff --git a/hostapd/config.c b/hostapd/config.c
index c16fe08..c4f76c2 100644
--- a/hostapd/config.c
+++ b/hostapd/config.c
@@ -28,6 +28,26 @@
#define MAX_STA_COUNT 2007
+/* convert floats with one decimal place to value*10 int, i.e.,
+ * "1.5" will return 15 */
+static int hostapd_config_read_int10(const char *value)
+{
+ int i, d;
+ char *pos;
+
+ i = atoi(value);
+ pos = strchr(value, '.');
+ d = 0;
+ if (pos) {
+ pos++;
+ if (*pos >= '0' && *pos <= '9')
+ d = *pos - '0';
+ }
+
+ return i * 10 + d;
+}
+
+
static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
{
bss->radius = (struct hostapd_radius_servers *) (bss + 1);
@@ -60,6 +80,16 @@ static struct hostapd_config * hostapd_config_defaults(void)
{
struct hostapd_config *conf;
struct hostapd_bss_config *bss;
+ int i;
+ const int aCWmin = 15, aCWmax = 1024;
+ const struct hostapd_wme_ac_params ac_bk =
+ { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ const struct hostapd_wme_ac_params ac_be =
+ { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+ const struct hostapd_wme_ac_params ac_vi = /* video traffic */
+ { aCWmin >> 1, aCWmin, 2, 3000 / 32, 1 };
+ const struct hostapd_wme_ac_params ac_vo = /* voice traffic */
+ { aCWmin >> 2, aCWmin >> 1, 2, 1500 / 32, 1 };
conf = wpa_zalloc(sizeof(*conf));
bss = wpa_zalloc(sizeof(*bss) + sizeof(struct hostapd_radius_servers));
@@ -87,6 +117,14 @@ static struct hostapd_config * hostapd_config_defaults(void)
conf->beacon_int = 100;
conf->send_probe_response = 1;
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ conf->tx_queue[i].aifs = -1; /* use hw default */
+
+ conf->wme_ac_params[0] = ac_be;
+ conf->wme_ac_params[1] = ac_bk;
+ conf->wme_ac_params[2] = ac_vi;
+ conf->wme_ac_params[3] = ac_vo;
+
return conf;
}
@@ -781,6 +819,152 @@ static int hostapd_parse_rates(int **rate_list, char *val)
}
+static int valid_cw(int cw)
+{
+ return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
+ cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023);
+}
+
+
+enum {
+ IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */
+ IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */
+ IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */
+ IEEE80211_TX_QUEUE_DATA3 = 3, /* used for EDCA AC_BK data */
+ IEEE80211_TX_QUEUE_DATA4 = 4,
+ IEEE80211_TX_QUEUE_AFTER_BEACON = 6,
+ IEEE80211_TX_QUEUE_BEACON = 7
+};
+
+static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
+ char *val)
+{
+ int num;
+ char *pos;
+ struct hostapd_tx_queue_params *queue;
+
+ /* skip 'tx_queue_' prefix */
+ pos = name + 9;
+ if (strncmp(pos, "data", 4) == 0 &&
+ pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
+ num = pos[4] - '0';
+ pos += 6;
+ } else if (strncmp(pos, "after_beacon_", 13) == 0) {
+ num = IEEE80211_TX_QUEUE_AFTER_BEACON;
+ pos += 13;
+ } else if (strncmp(pos, "beacon_", 7) == 0) {
+ num = IEEE80211_TX_QUEUE_BEACON;
+ pos += 7;
+ } else {
+ printf("Unknown tx_queue name '%s'\n", pos);
+ return -1;
+ }
+
+ queue = &conf->tx_queue[num];
+
+ if (strcmp(pos, "aifs") == 0) {
+ queue->aifs = atoi(val);
+ if (queue->aifs < 0 || queue->aifs > 255) {
+ printf("Invalid AIFS value %d\n", queue->aifs);
+ return -1;
+ }
+ } else if (strcmp(pos, "cwmin") == 0) {
+ queue->cwmin = atoi(val);
+ if (!valid_cw(queue->cwmin)) {
+ printf("Invalid cwMin value %d\n", queue->cwmin);
+ return -1;
+ }
+ } else if (strcmp(pos, "cwmax") == 0) {
+ queue->cwmax = atoi(val);
+ if (!valid_cw(queue->cwmax)) {
+ printf("Invalid cwMax value %d\n", queue->cwmax);
+ return -1;
+ }
+ } else if (strcmp(pos, "burst") == 0) {
+ queue->burst = hostapd_config_read_int10(val);
+ } else {
+ printf("Unknown tx_queue field '%s'\n", pos);
+ return -1;
+ }
+
+ queue->configured = 1;
+
+ return 0;
+}
+
+
+static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name,
+ char *val)
+{
+ int num, v;
+ char *pos;
+ struct hostapd_wme_ac_params *ac;
+
+ /* skip 'wme_ac_' prefix */
+ pos = name + 7;
+ if (strncmp(pos, "be_", 3) == 0) {
+ num = 0;
+ pos += 3;
+ } else if (strncmp(pos, "bk_", 3) == 0) {
+ num = 1;
+ pos += 3;
+ } else if (strncmp(pos, "vi_", 3) == 0) {
+ num = 2;
+ pos += 3;
+ } else if (strncmp(pos, "vo_", 3) == 0) {
+ num = 3;
+ pos += 3;
+ } else {
+ printf("Unknown wme name '%s'\n", pos);
+ return -1;
+ }
+
+ ac = &conf->wme_ac_params[num];
+
+ if (strcmp(pos, "aifs") == 0) {
+ v = atoi(val);
+ if (v < 1 || v > 255) {
+ printf("Invalid AIFS value %d\n", v);
+ return -1;
+ }
+ ac->aifs = v;
+ } else if (strcmp(pos, "cwmin") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 12) {
+ printf("Invalid cwMin value %d\n", v);
+ return -1;
+ }
+ ac->cwmin = v;
+ } else if (strcmp(pos, "cwmax") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 12) {
+ printf("Invalid cwMax value %d\n", v);
+ return -1;
+ }
+ ac->cwmax = v;
+ } else if (strcmp(pos, "txop_limit") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 0xffff) {
+ printf("Invalid txop value %d\n", v);
+ return -1;
+ }
+ ac->txopLimit = v;
+ } else if (strcmp(pos, "acm") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 1) {
+ printf("Invalid acm value %d\n", v);
+ return -1;
+ }
+ ac->admission_control_mandatory = v;
+ } else {
+ printf("Unknown wme_ac_ field '%s'\n", pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
struct hostapd_config * hostapd_config_read(const char *fname)
{
struct hostapd_config *conf;
@@ -1272,6 +1456,20 @@ struct hostapd_config * hostapd_config_read(const char *fname)
}
} else if (strcmp(buf, "dynamic_vlan") == 0) {
bss->ssid.dynamic_vlan = atoi(pos);
+ } else if (strncmp(buf, "tx_queue_", 9) == 0) {
+ if (hostapd_config_tx_queue(conf, buf, pos)) {
+ printf("Line %d: invalid TX queue item\n",
+ line);
+ errors++;
+ }
+ } else if (strcmp(buf, "wme_enabled") == 0) {
+ bss->wme_enabled = atoi(pos);
+ } else if (strncmp(buf, "wme_ac_", 7) == 0) {
+ if (hostapd_config_wme_ac(conf, buf, pos)) {
+ printf("Line %d: invalid wme ac item\n",
+ line);
+ errors++;
+ }
} else {
printf("Line %d: unknown configuration item '%s'\n",
line, buf);
diff --git a/hostapd/config.h b/hostapd/config.h
index 85d2dfb..bc1d234 100644
--- a/hostapd/config.h
+++ b/hostapd/config.h
@@ -87,6 +87,26 @@ struct hostapd_eap_user {
* nt_password_hash() */
};
+
+#define NUM_TX_QUEUES 8
+
+struct hostapd_tx_queue_params {
+ int aifs;
+ int cwmin;
+ int cwmax;
+ int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
+ int configured;
+};
+
+struct hostapd_wme_ac_params {
+ int cwmin;
+ int cwmax;
+ int aifs;
+ int txopLimit; /* in units of 32us */
+ int admission_control_mandatory;
+};
+
+
/**
* struct hostapd_bss_config - Per-BSS configuration
*/
@@ -207,6 +227,8 @@ struct hostapd_bss_config {
int ap_max_inactivity;
int ignore_broadcast_ssid;
+
+ int wme_enabled;
};
@@ -244,6 +266,17 @@ struct hostapd_config {
int *basic_rates;
const struct driver_ops *driver;
+
+ struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
+
+ /*
+ * WME AC parameters, in same order as 802.1D, i.e.
+ * 0 = BE (best effort)
+ * 1 = BK (background)
+ * 2 = VI (video)
+ * 3 = VO (voice)
+ */
+ struct hostapd_wme_ac_params wme_ac_params[4];
};
diff --git a/hostapd/hostapd.c b/hostapd/hostapd.c
index 427be70..d2544f5 100644
--- a/hostapd/hostapd.c
+++ b/hostapd/hostapd.c
@@ -34,6 +34,7 @@
#include "radius_server.h"
#include "wpa.h"
#include "preauth.h"
+#include "wme.h"
#include "ctrl_iface.h"
#include "tls.h"
#include "eap_sim_db.h"
@@ -220,6 +221,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
accounting_sta_start(hapd, sta);
+ hostapd_wme_sta_config(hapd, sta);
+
/* Start IEEE 802.1X authentication process for new stations */
ieee802_1x_new_station(hapd, sta);
if (reassoc)
@@ -269,6 +272,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->stakey = conf->stakey;
wconf->eapol_version = conf->eapol_version;
wconf->peerkey = conf->peerkey;
+ wconf->wme_enabled = conf->wme_enabled;
}
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 3c275f7..eab9b0e 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -102,6 +102,124 @@ macaddr_acl=0
# bit 1 = Shared Key Authentication (requires WEP)
auth_algs=3
+# TX queue parameters (EDCF / bursting)
+# default for all these fields: not set, use hardware defaults
+# tx_queue_<queue name>_<param>
+# queues: data0, data1, data2, data3, after_beacon, beacon
+# (data0 is the highest priority queue)
+# parameters:
+# aifs: AIFS (default 2)
+# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023)
+# cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); cwMax >= cwMin
+# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
+# bursting
+#
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# These parameters are used by the access point when transmitting frames
+# to the clients.
+#
+# Low priority / AC_BK = background
+#tx_queue_data3_aifs=7
+#tx_queue_data3_cwmin=15
+#tx_queue_data3_cwmax=1023
+#tx_queue_data3_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
+#
+# Normal priority / AC_BE = best effort
+#tx_queue_data2_aifs=3
+#tx_queue_data2_cwmin=15
+#tx_queue_data2_cwmax=63
+#tx_queue_data2_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
+#
+# High priority / AC_VI = video
+#tx_queue_data1_aifs=1
+#tx_queue_data1_cwmin=7
+#tx_queue_data1_cwmax=15
+#tx_queue_data1_burst=3.0
+# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
+#
+# Highest priority / AC_VO = voice
+#tx_queue_data0_aifs=1
+#tx_queue_data0_cwmin=3
+#tx_queue_data0_cwmax=7
+#tx_queue_data0_burst=1.5
+# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3
+#
+# Special queues; normally not user configurable
+#
+#tx_queue_after_beacon_aifs=2
+#tx_queue_after_beacon_cwmin=15
+#tx_queue_after_beacon_cwmax=1023
+#tx_queue_after_beacon_burst=0
+#
+#tx_queue_beacon_aifs=2
+#tx_queue_beacon_cwmin=3
+#tx_queue_beacon_cwmax=7
+#tx_queue_beacon_burst=1.5
+
+# 802.1D Tag to AC mappings
+# WMM specifies following mapping of data frames to different ACs. This mapping
+# can be configured using Linux QoS/tc and sch_pktpri.o module.
+# 802.1D Tag 802.1D Designation Access Category WMM Designation
+# 1 BK AC_BK Background
+# 2 - AC_BK Background
+# 0 BE AC_BE Best Effort
+# 3 EE AC_VI Video
+# 4 CL AC_VI Video
+# 5 VI AC_VI Video
+# 6 VO AC_VO Voice
+# 7 NC AC_VO Voice
+# Data frames with no priority information: AC_BE
+# Management frames: AC_VO
+# PS-Poll frames: AC_BE
+
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# for 802.11a or 802.11g networks
+# These parameters are sent to WMM clients when they associate.
+# The parameters will be used by WMM clients for frames transmitted to the
+# access point.
+#
+# note - txop_limit is in units of 32microseconds
+# note - acm is admission control mandatory flag. 0 = admission control not
+# required, 1 = mandatory
+# note - here cwMin and cmMax are in exponent form. the actual cw value used
+# will be (2^n)-1 where n is the value given here
+#
+wme_enabled=1
+#
+# Low priority / AC_BK = background
+wme_ac_bk_cwmin=4
+wme_ac_bk_cwmax=10
+wme_ac_bk_aifs=7
+wme_ac_bk_txop_limit=0
+wme_ac_bk_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
+#
+# Normal priority / AC_BE = best effort
+wme_ac_be_aifs=3
+wme_ac_be_cwmin=4
+wme_ac_be_cwmax=10
+wme_ac_be_txop_limit=0
+wme_ac_be_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
+#
+# High priority / AC_VI = video
+wme_ac_vi_aifs=2
+wme_ac_vi_cwmin=3
+wme_ac_vi_cwmax=4
+wme_ac_vi_txop_limit=94
+wme_ac_vi_acm=0
+# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
+#
+# Highest priority / AC_VO = voice
+wme_ac_vo_aifs=2
+wme_ac_vo_cwmin=2
+wme_ac_vo_cwmax=3
+wme_ac_vo_txop_limit=47
+wme_ac_vo_acm=0
+# Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102
+
# Associate as a station to another AP while still acting as an AP on the same
# channel.
#assoc_ap_addr=00:12:34:56:78:9a
diff --git a/hostapd/hostapd.h b/hostapd/hostapd.h
index 227ce98..017d9a8 100644
--- a/hostapd/hostapd.h
+++ b/hostapd/hostapd.h
@@ -152,6 +152,8 @@ struct hostapd_data {
void *ssl_ctx;
void *eap_sim_db_priv;
struct radius_server_data *radius_srv;
+
+ int parameter_set_count;
};
diff --git a/hostapd/ieee802_11.c b/hostapd/ieee802_11.c
index f8ae19a..f2e5334 100644
--- a/hostapd/ieee802_11.c
+++ b/hostapd/ieee802_11.c
@@ -31,6 +31,7 @@
#include "rc4.h"
#include "ieee802_1x.h"
#include "wpa.h"
+#include "wme.h"
#include "accounting.h"
#include "driver.h"
#include "hostap_common.h"
@@ -177,6 +178,31 @@ static int ieee802_11_parse_vendor_specific(struct hostapd_data *hapd,
elems->wpa_ie = pos;
elems->wpa_ie_len = elen;
break;
+ case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */
+ if (elen < 5) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS,
+ "short WME information element "
+ "ignored (len=%d)\n", elen);
+ return -1;
+ }
+ switch (pos[4]) {
+ case WME_OUI_SUBTYPE_INFORMATION_ELEMENT:
+ case WME_OUI_SUBTYPE_PARAMETER_ELEMENT:
+ elems->wme = pos;
+ elems->wme_len = elen;
+ break;
+ case WME_OUI_SUBTYPE_TSPEC_ELEMENT:
+ elems->wme_tspec = pos;
+ elems->wme_tspec_len = elen;
+ break;
+ default:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS,
+ "unknown WME information element"
+ " ignored (subtype=%d len=%d)\n",
+ pos[4], elen);
+ return -1;
+ }
+ break;
default:
HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS,
"Unknown Microsoft information element "
@@ -727,6 +753,18 @@ static void handle_assoc(struct hostapd_data *hapd,
goto fail;
}
+ sta->flags &= ~WLAN_STA_WME;
+ if (elems.wme && hapd->conf->wme_enabled) {
+ if (hostapd_eid_wme_valid(hapd, elems.wme, elems.wme_len))
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "invalid WME element in association "
+ "request");
+ else
+ sta->flags |= WLAN_STA_WME;
+ }
+
if (!elems.supp_rates) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -916,6 +954,8 @@ static void handle_assoc(struct hostapd_data *hapd,
p = hostapd_eid_supp_rates(hapd, mgmt->u.assoc_resp.variable);
/* Extended supported rates */
p = hostapd_eid_ext_supp_rates(hapd, p);
+ if (sta->flags && WLAN_STA_WME)
+ p = hostapd_eid_wme(hapd, p);
send_len += p - mgmt->u.assoc_resp.variable;
/* Request TX callback */
@@ -1123,6 +1163,30 @@ static void handle_beacon(struct hostapd_data *hapd,
}
+static void handle_action(struct hostapd_data *hapd,
+ struct ieee80211_mgmt *mgmt, size_t len)
+{
+ if (len < IEEE80211_HDRLEN + 1) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "handle_action - too short payload (len=%d)",
+ len);
+ return;
+ }
+
+ switch (mgmt->u.action.category) {
+ case WME_ACTION_CATEGORY:
+ hostapd_wme_action(hapd, mgmt, len);
+ return;
+ }
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "handle_action - unknown action category %d",
+ mgmt->u.action.category);
+}
+
+
/**
* ieee802_11_mgmt - process incoming IEEE 802.11 management frames
* @hapd: hostapd BSS data structure (the BSS to which the management frame was
@@ -1199,6 +1263,10 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype)
HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::deauth\n");
handle_deauth(hapd, mgmt, len);
break;
+ case WLAN_FC_STYPE_ACTION:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::action\n");
+ handle_action(hapd, mgmt, len);
+ break;
default:
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
diff --git a/hostapd/ieee802_11.h b/hostapd/ieee802_11.h
index 016d51f..691a63e 100644
--- a/hostapd/ieee802_11.h
+++ b/hostapd/ieee802_11.h
@@ -77,6 +77,25 @@ struct ieee80211_mgmt {
* FH Params, DS Params, CF Params, IBSS Params, TIM */
u8 variable[0];
} __attribute__ ((packed)) beacon;
+ struct {
+ u8 category;
+ union {
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u8 status_code;
+ u8 variable[0];
+ } __attribute__ ((packed)) wme_action;
+ struct{
+ u8 action_code;
+ u8 element_id;
+ u8 length;
+ u8 switch_mode;
+ u8 new_chan;
+ u8 switch_count;
+ } __attribute__ ((packed)) chan_switch;
+ } u;
+ } __attribute__ ((packed)) action;
} u;
} __attribute__ ((packed));
@@ -111,6 +130,10 @@ struct ieee802_11_elems {
u8 wpa_ie_len;
u8 *rsn_ie;
u8 rsn_ie_len;
+ u8 *wme;
+ u8 wme_len;
+ u8 *wme_tspec;
+ u8 wme_tspec_len;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
diff --git a/hostapd/wme.c b/hostapd/wme.c
new file mode 100644
index 0000000..31d0bf0
--- /dev/null
+++ b/hostapd/wme.c
@@ -0,0 +1,271 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "wme.h"
+#include "sta_info.h"
+#include "driver.h"
+#include "hostap_common.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;
+ u8 *pos;
+ size_t left;
+
+ wpa_hexdump(MSG_MSGDUMP, "WME IE", eid, len);
+
+ if (len < sizeof(struct wme_information_element)) {
+ printf("Too short WME IE (len=%d)\n", len);
+ return -1;
+ }
+
+ wme = (struct wme_information_element *) eid;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Validating WME IE: OUI "
+ "%02x:%02x:%02x OUI type %d OUI sub-type %d "
+ "version %d\n",
+ wme->oui[0], wme->oui[1], wme->oui[2], wme->oui_type,
+ wme->oui_subtype, wme->version);
+ if (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) {
+ printf("Unsupported WME IE OUI/Type/Subtype/Version\n");
+ return -1;
+ }
+
+ pos = (u8 *) (wme + 1);
+ left = len - sizeof(*wme);
+
+ 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, WLAN_STA_WME, ~0);
+ else
+ hostapd_sta_set_flags(hapd, sta->addr, 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);
+ memset(buf, 0, sizeof(buf));
+ m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ memcpy(m->da, addr, ETH_ALEN);
+ memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+ memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+ m->u.action.category = WME_ACTION_CATEGORY;
+ 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;
+ 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(hapd, 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);
+}
diff --git a/hostapd/wme.h b/hostapd/wme.h
new file mode 100644
index 0000000..d7c03be
--- /dev/null
+++ b/hostapd/wme.h
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#ifndef WME_H
+#define WME_H
+#include <endian.h>
+
+#define WME_OUI_TYPE 2
+#define WME_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+#define WME_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WME_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WME_VERSION 1
+
+#define WME_ACTION_CATEGORY 17
+#define WME_ACTION_CODE_SETUP_REQUEST 0
+#define WME_ACTION_CODE_SETUP_RESPONSE 1
+#define WME_ACTION_CODE_TEARDOWN 2
+
+#define WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED 0
+#define WME_SETUP_RESPONSE_STATUS_INVALID_PARAMETERS 1
+#define WME_SETUP_RESPONSE_STATUS_REFUSED 3
+
+#define WME_TSPEC_DIRECTION_UPLINK 0
+#define WME_TSPEC_DIRECTION_DOWNLINK 1
+#define WME_TSPEC_DIRECTION_BI_DIRECTIONAL 3
+
+extern inline u16 tsinfo(int tag1d, int contention_based, int direction)
+{
+ return (tag1d << 11) | (contention_based << 7) | (direction << 5) |
+ (tag1d << 1);
+}
+
+
+struct wme_information_element {
+ /* required fields for WME version 1 */
+ u8 oui[3];
+ u8 oui_type;
+ u8 oui_subtype;
+ u8 version;
+ u8 acInfo;
+
+} __attribute__ ((packed));
+
+struct wme_ac_parameter {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ /* byte 1 */
+ u8 aifsn:4,
+ acm:1,
+ aci:2,
+ reserved:1;
+
+ /* byte 2 */
+ u8 eCWmin:4,
+ eCWmax:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ /* byte 1 */
+ u8 reserved:1,
+ aci:2,
+ acm:1,
+ aifsn:4;
+
+ /* byte 2 */
+ u8 eCWmax:4,
+ eCWmin:4;
+#else
+#error "Please fix <endian.h>"
+#endif
+
+ /* bytes 3 & 4 */
+ u16 txopLimit;
+} __attribute__ ((packed));
+
+struct wme_parameter_element {
+ /* required fields for WME version 1 */
+ u8 oui[3];
+ u8 oui_type;
+ u8 oui_subtype;
+ u8 version;
+ u8 acInfo;
+ u8 reserved;
+ struct wme_ac_parameter ac[4];
+
+} __attribute__ ((packed));
+
+struct wme_tspec_info_element {
+ u8 eid;
+ u8 length;
+ u8 oui[3];
+ u8 oui_type;
+ u8 oui_subtype;
+ u8 version;
+ u16 ts_info;
+ u16 nominal_msdu_size;
+ u16 maximum_msdu_size;
+ u32 minimum_service_interval;
+ u32 maximum_service_interval;
+ u32 inactivity_interval;
+ u32 start_time;
+ u32 minimum_data_rate;
+ u32 mean_data_rate;
+ u32 maximum_burst_size;
+ u32 minimum_phy_rate;
+ u32 peak_data_rate;
+ u32 delay_bound;
+ u16 surplus_bandwidth_allowance;
+ u16 medium_time;
+} __attribute__ ((packed));
+
+
+/* Access Categories */
+enum {
+ WME_AC_BK = 1,
+ WME_AC_BE = 0,
+ WME_AC_VI = 2,
+ WME_AC_VO = 3
+};
+
+
+u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid);
+int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len);
+int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len);
+
+#endif /* WME_H */
diff --git a/hostapd/wpa.c b/hostapd/wpa.c
index 6cb7519..1a22682 100644
--- a/hostapd/wpa.c
+++ b/hostapd/wpa.c
@@ -19,6 +19,7 @@
#include "hostapd.h"
#include "eapol_sm.h"
#include "wpa.h"
+#include "wme.h"
#include "sha1.h"
#include "md5.h"
#include "rc4.h"
@@ -33,6 +34,12 @@
#define STATE_MACHINE_ADDR sm->addr
+#define RSN_NUM_REPLAY_COUNTERS_1 0
+#define RSN_NUM_REPLAY_COUNTERS_2 1
+#define RSN_NUM_REPLAY_COUNTERS_4 2
+#define RSN_NUM_REPLAY_COUNTERS_16 3
+
+
struct wpa_stakey_negotiation {
struct wpa_stakey_negotiation *next;
u8 initiator[ETH_ALEN];
@@ -622,6 +629,10 @@ static int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
capab |= WPA_CAPABILITY_PREAUTH;
if (conf->peerkey)
capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
+ if (conf->wme_enabled) {
+ /* 4 PTKSA replay counters when using WME */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
*pos++ = capab & 0xff;
*pos++ = capab >> 8;
diff --git a/hostapd/wpa.h b/hostapd/wpa.h
index d356033..0d09994 100644
--- a/hostapd/wpa.h
+++ b/hostapd/wpa.h
@@ -89,6 +89,7 @@ struct wpa_auth_config {
int stakey;
int eapol_version;
int peerkey;
+ int wme_enabled;
};
typedef enum {