wpa_supplicant: Initial Revision 0.8.X

Based on:
commit 0725cc7b7efc434910e89865c42eda7ce61bbf08
Author: Jouni Malinen <j@w1.fi>
Date:   Thu Apr 21 20:41:01 2011 +0300

    Enable CONFIG_DRIVER_NL80211=y in the default configuration

    nl80211 should be preferred over WEXT with any recent Linux
    kernel version.

Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/Makefile b/src/ap/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/ap/Makefile
@@ -0,0 +1,8 @@
+all:
+	@echo Nothing to be made.
+
+clean:
+	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
new file mode 100644
index 0000000..dbfb058
--- /dev/null
+++ b/src/ap/accounting.c
@@ -0,0 +1,505 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "drivers/driver.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "accounting.h"
+
+
+/* Default interval in seconds for polling TX/RX octets from the driver if
+ * STA is not using interim accounting. This detects wrap arounds for
+ * input/output octets and updates Acct-{Input,Output}-Gigawords. */
+#define ACCT_DEFAULT_UPDATE_INTERVAL 300
+
+static void accounting_sta_get_id(struct hostapd_data *hapd,
+				  struct sta_info *sta);
+
+
+static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  int status_type)
+{
+	struct radius_msg *msg;
+	char buf[128];
+	u8 *val;
+	size_t len;
+	int i;
+
+	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
+			     radius_client_get_id(hapd->radius));
+	if (msg == NULL) {
+		printf("Could not create net RADIUS packet\n");
+		return NULL;
+	}
+
+	if (sta) {
+		radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+		os_snprintf(buf, sizeof(buf), "%08X-%08X",
+			    sta->acct_session_id_hi, sta->acct_session_id_lo);
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+					 (u8 *) buf, os_strlen(buf))) {
+			printf("Could not add Acct-Session-Id\n");
+			goto fail;
+		}
+	} else {
+		radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+				       status_type)) {
+		printf("Could not add Acct-Status-Type\n");
+		goto fail;
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+				       hapd->conf->ieee802_1x ?
+				       RADIUS_ACCT_AUTHENTIC_RADIUS :
+				       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
+		printf("Could not add Acct-Authentic\n");
+		goto fail;
+	}
+
+	if (sta) {
+		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+		if (!val) {
+			os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
+				    MAC2STR(sta->addr));
+			val = (u8 *) buf;
+			len = os_strlen(buf);
+		}
+
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
+					 len)) {
+			printf("Could not add User-Name\n");
+			goto fail;
+		}
+	}
+
+	if (hapd->conf->own_ip_addr.af == AF_INET &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+		printf("Could not add NAS-IP-Address\n");
+		goto fail;
+	}
+
+#ifdef CONFIG_IPV6
+	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+		printf("Could not add NAS-IPv6-Address\n");
+		goto fail;
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (hapd->conf->nas_identifier &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+				 (u8 *) hapd->conf->nas_identifier,
+				 os_strlen(hapd->conf->nas_identifier))) {
+		printf("Could not add NAS-Identifier\n");
+		goto fail;
+	}
+
+	if (sta &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+		printf("Could not add NAS-Port\n");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		printf("Could not add Called-Station-Id\n");
+		goto fail;
+	}
+
+	if (sta) {
+		os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+			    MAC2STR(sta->addr));
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+					 (u8 *) buf, os_strlen(buf))) {
+			printf("Could not add Calling-Station-Id\n");
+			goto fail;
+		}
+
+		if (!radius_msg_add_attr_int32(
+			    msg, RADIUS_ATTR_NAS_PORT_TYPE,
+			    RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+			printf("Could not add NAS-Port-Type\n");
+			goto fail;
+		}
+
+		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+			    radius_sta_rate(hapd, sta) / 2,
+			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+			    radius_mode_txt(hapd));
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+					 (u8 *) buf, os_strlen(buf))) {
+			printf("Could not add Connect-Info\n");
+			goto fail;
+		}
+
+		for (i = 0; ; i++) {
+			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
+							  i);
+			if (val == NULL)
+				break;
+
+			if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
+						 val, len)) {
+				printf("Could not add Class\n");
+				goto fail;
+			}
+		}
+	}
+
+	return msg;
+
+ fail:
+	radius_msg_free(msg);
+	return NULL;
+}
+
+
+static int accounting_sta_update_stats(struct hostapd_data *hapd,
+				       struct sta_info *sta,
+				       struct hostap_sta_driver_data *data)
+{
+	if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
+		return -1;
+
+	if (sta->last_rx_bytes > data->rx_bytes)
+		sta->acct_input_gigawords++;
+	if (sta->last_tx_bytes > data->tx_bytes)
+		sta->acct_output_gigawords++;
+	sta->last_rx_bytes = data->rx_bytes;
+	sta->last_tx_bytes = data->tx_bytes;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
+		       "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
+		       "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
+		       sta->last_rx_bytes, sta->acct_input_gigawords,
+		       sta->last_tx_bytes, sta->acct_output_gigawords);
+
+	return 0;
+}
+
+
+static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	int interval;
+
+	if (sta->acct_interim_interval) {
+		accounting_sta_interim(hapd, sta);
+		interval = sta->acct_interim_interval;
+	} else {
+		struct hostap_sta_driver_data data;
+		accounting_sta_update_stats(hapd, sta, &data);
+		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+	}
+
+	eloop_register_timeout(interval, 0, accounting_interim_update,
+			       hapd, sta);
+}
+
+
+/**
+ * accounting_sta_start - Start STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct radius_msg *msg;
+	int interval;
+
+	if (sta->acct_session_started)
+		return;
+
+	accounting_sta_get_id(hapd, sta);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_INFO,
+		       "starting accounting session %08X-%08X",
+		       sta->acct_session_id_hi, sta->acct_session_id_lo);
+
+	time(&sta->acct_session_start);
+	sta->last_rx_bytes = sta->last_tx_bytes = 0;
+	sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
+	hostapd_drv_sta_clear_stats(hapd, sta->addr);
+
+	if (!hapd->conf->radius->acct_server)
+		return;
+
+	if (sta->acct_interim_interval)
+		interval = sta->acct_interim_interval;
+	else
+		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+	eloop_register_timeout(interval, 0, accounting_interim_update,
+			       hapd, sta);
+
+	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
+	if (msg)
+		radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+
+	sta->acct_session_started = 1;
+}
+
+
+static void accounting_sta_report(struct hostapd_data *hapd,
+				  struct sta_info *sta, int stop)
+{
+	struct radius_msg *msg;
+	int cause = sta->acct_terminate_cause;
+	struct hostap_sta_driver_data data;
+	struct os_time now;
+	u32 gigawords;
+
+	if (!hapd->conf->radius->acct_server)
+		return;
+
+	msg = accounting_msg(hapd, sta,
+			     stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
+			     RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
+	if (!msg) {
+		printf("Could not create RADIUS Accounting message\n");
+		return;
+	}
+
+	os_get_time(&now);
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+				       now.sec - sta->acct_session_start)) {
+		printf("Could not add Acct-Session-Time\n");
+		goto fail;
+	}
+
+	if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_INPUT_PACKETS,
+					       data.rx_packets)) {
+			printf("Could not add Acct-Input-Packets\n");
+			goto fail;
+		}
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+					       data.tx_packets)) {
+			printf("Could not add Acct-Output-Packets\n");
+			goto fail;
+		}
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_INPUT_OCTETS,
+					       data.rx_bytes)) {
+			printf("Could not add Acct-Input-Octets\n");
+			goto fail;
+		}
+		gigawords = sta->acct_input_gigawords;
+#if __WORDSIZE == 64
+		gigawords += data.rx_bytes >> 32;
+#endif
+		if (gigawords &&
+		    !radius_msg_add_attr_int32(
+			    msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+			    gigawords)) {
+			printf("Could not add Acct-Input-Gigawords\n");
+			goto fail;
+		}
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+					       data.tx_bytes)) {
+			printf("Could not add Acct-Output-Octets\n");
+			goto fail;
+		}
+		gigawords = sta->acct_output_gigawords;
+#if __WORDSIZE == 64
+		gigawords += data.tx_bytes >> 32;
+#endif
+		if (gigawords &&
+		    !radius_msg_add_attr_int32(
+			    msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+			    gigawords)) {
+			printf("Could not add Acct-Output-Gigawords\n");
+			goto fail;
+		}
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+				       now.sec)) {
+		printf("Could not add Event-Timestamp\n");
+		goto fail;
+	}
+
+	if (eloop_terminated())
+		cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+
+	if (stop && cause &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+				       cause)) {
+		printf("Could not add Acct-Terminate-Cause\n");
+		goto fail;
+	}
+
+	radius_client_send(hapd->radius, msg,
+			   stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+			   sta->addr);
+	return;
+
+ fail:
+	radius_msg_free(msg);
+}
+
+
+/**
+ * accounting_sta_interim - Send a interim STA accounting report
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (sta->acct_session_started)
+		accounting_sta_report(hapd, sta, 0);
+}
+
+
+/**
+ * accounting_sta_stop - Stop STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (sta->acct_session_started) {
+		accounting_sta_report(hapd, sta, 1);
+		eloop_cancel_timeout(accounting_interim_update, hapd, sta);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO,
+			       "stopped accounting session %08X-%08X",
+			       sta->acct_session_id_hi,
+			       sta->acct_session_id_lo);
+		sta->acct_session_started = 0;
+	}
+}
+
+
+static void accounting_sta_get_id(struct hostapd_data *hapd,
+				  struct sta_info *sta)
+{
+	sta->acct_session_id_lo = hapd->acct_session_id_lo++;
+	if (hapd->acct_session_id_lo == 0) {
+		hapd->acct_session_id_hi++;
+	}
+	sta->acct_session_id_hi = hapd->acct_session_id_hi;
+}
+
+
+/**
+ * accounting_receive - Process the RADIUS frames from Accounting Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+accounting_receive(struct radius_msg *msg, struct radius_msg *req,
+		   const u8 *shared_secret, size_t shared_secret_len,
+		   void *data)
+{
+	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
+		printf("Unknown RADIUS message code\n");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+		printf("Incoming RADIUS packet did not have correct "
+		       "Authenticator - dropped\n");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	return RADIUS_RX_PROCESSED;
+}
+
+
+static void accounting_report_state(struct hostapd_data *hapd, int on)
+{
+	struct radius_msg *msg;
+
+	if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
+		return;
+
+	/* Inform RADIUS server that accounting will start/stop so that the
+	 * server can close old accounting sessions. */
+	msg = accounting_msg(hapd, NULL,
+			     on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
+			     RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
+	if (!msg)
+		return;
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+				       RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
+	{
+		printf("Could not add Acct-Terminate-Cause\n");
+		radius_msg_free(msg);
+		return;
+	}
+
+	radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+}
+
+
+/**
+ * accounting_init: Initialize accounting
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int accounting_init(struct hostapd_data *hapd)
+{
+	struct os_time now;
+
+	/* Acct-Session-Id should be unique over reboots. If reliable clock is
+	 * not available, this could be replaced with reboot counter, etc. */
+	os_get_time(&now);
+	hapd->acct_session_id_hi = now.sec;
+
+	if (radius_client_register(hapd->radius, RADIUS_ACCT,
+				   accounting_receive, hapd))
+		return -1;
+
+	accounting_report_state(hapd, 1);
+
+	return 0;
+}
+
+
+/**
+ * accounting_deinit: Deinitilize accounting
+ * @hapd: hostapd BSS data
+ */
+void accounting_deinit(struct hostapd_data *hapd)
+{
+	accounting_report_state(hapd, 0);
+}
diff --git a/src/ap/accounting.h b/src/ap/accounting.h
new file mode 100644
index 0000000..f3d60f0
--- /dev/null
+++ b/src/ap/accounting.h
@@ -0,0 +1,45 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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 ACCOUNTING_H
+#define ACCOUNTING_H
+
+void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_NO_ACCOUNTING
+static inline void accounting_sta_start(struct hostapd_data *hapd,
+					struct sta_info *sta)
+{
+}
+
+static inline void accounting_sta_stop(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+}
+
+static inline int accounting_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void accounting_deinit(struct hostapd_data *hapd)
+{
+}
+#else /* CONFIG_NO_ACCOUNTING */
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
+int accounting_init(struct hostapd_data *hapd);
+void accounting_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_NO_ACCOUNTING */
+
+#endif /* ACCOUNTING_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
new file mode 100644
index 0000000..e77716b
--- /dev/null
+++ b/src/ap/ap_config.c
@@ -0,0 +1,627 @@
+/*
+ * hostapd / Configuration helper functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/sha1.h"
+#include "radius/radius_client.h"
+#include "common/ieee802_11_defs.h"
+#include "common/eapol_common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_config.h"
+
+
+static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
+{
+	struct hostapd_vlan *vlan, *prev;
+
+	vlan = bss->vlan;
+	prev = NULL;
+	while (vlan) {
+		prev = vlan;
+		vlan = vlan->next;
+		os_free(prev);
+	}
+
+	bss->vlan = NULL;
+}
+
+
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+{
+	bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
+	bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
+	bss->logger_syslog = (unsigned int) -1;
+	bss->logger_stdout = (unsigned int) -1;
+
+	bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
+
+	bss->wep_rekeying_period = 300;
+	/* use key0 in individual key and key1 in broadcast key */
+	bss->broadcast_key_idx_min = 1;
+	bss->broadcast_key_idx_max = 2;
+	bss->eap_reauth_period = 3600;
+
+	bss->wpa_group_rekey = 600;
+	bss->wpa_gmk_rekey = 86400;
+	bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+	bss->wpa_pairwise = WPA_CIPHER_TKIP;
+	bss->wpa_group = WPA_CIPHER_TKIP;
+	bss->rsn_pairwise = 0;
+
+	bss->max_num_sta = MAX_STA_COUNT;
+
+	bss->dtim_period = 2;
+
+	bss->radius_server_auth_port = 1812;
+	bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+	bss->eapol_version = EAPOL_VERSION;
+
+	bss->max_listen_interval = 65535;
+
+	bss->pwd_group = 19; /* ECC: GF(p=256) */
+
+#ifdef CONFIG_IEEE80211W
+	bss->assoc_sa_query_max_timeout = 1000;
+	bss->assoc_sa_query_retry_timeout = 201;
+#endif /* CONFIG_IEEE80211W */
+#ifdef EAP_SERVER_FAST
+	 /* both anonymous and authenticated provisioning */
+	bss->eap_fast_prov = 3;
+	bss->pac_key_lifetime = 7 * 24 * 60 * 60;
+	bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
+#endif /* EAP_SERVER_FAST */
+
+	/* Set to -1 as defaults depends on HT in setup */
+	bss->wmm_enabled = -1;
+
+#ifdef CONFIG_IEEE80211R
+	bss->ft_over_ds = 1;
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+struct hostapd_config * hostapd_config_defaults(void)
+{
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
+	struct hostapd_config *conf;
+	struct hostapd_bss_config *bss;
+	const int aCWmin = 4, aCWmax = 10;
+	const struct hostapd_wmm_ac_params ac_bk =
+		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+	const struct hostapd_wmm_ac_params ac_be =
+		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 1 };
+	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 };
+	const struct hostapd_tx_queue_params txq_bk =
+		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+	const struct hostapd_tx_queue_params txq_be =
+		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
+	const struct hostapd_tx_queue_params txq_vi =
+		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
+	const struct hostapd_tx_queue_params txq_vo =
+		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15};
+
+#undef ecw2cw
+
+	conf = os_zalloc(sizeof(*conf));
+	bss = os_zalloc(sizeof(*bss));
+	if (conf == NULL || bss == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+			   "configuration data.");
+		os_free(conf);
+		os_free(bss);
+		return NULL;
+	}
+
+	bss->radius = os_zalloc(sizeof(*bss->radius));
+	if (bss->radius == NULL) {
+		os_free(conf);
+		os_free(bss);
+		return NULL;
+	}
+
+	hostapd_config_defaults_bss(bss);
+
+	conf->num_bss = 1;
+	conf->bss = bss;
+
+	conf->beacon_int = 100;
+	conf->rts_threshold = -1; /* use driver default: 2347 */
+	conf->fragm_threshold = -1; /* user driver default: 2346 */
+	conf->send_probe_response = 1;
+
+	conf->wmm_ac_params[0] = ac_be;
+	conf->wmm_ac_params[1] = ac_bk;
+	conf->wmm_ac_params[2] = ac_vi;
+	conf->wmm_ac_params[3] = ac_vo;
+
+	conf->tx_queue[0] = txq_vo;
+	conf->tx_queue[1] = txq_vi;
+	conf->tx_queue[2] = txq_be;
+	conf->tx_queue[3] = txq_bk;
+
+	conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
+
+	return conf;
+}
+
+
+int hostapd_mac_comp(const void *a, const void *b)
+{
+	return os_memcmp(a, b, sizeof(macaddr));
+}
+
+
+int hostapd_mac_comp_empty(const void *a)
+{
+	macaddr empty = { 0 };
+	return os_memcmp(a, empty, sizeof(macaddr));
+}
+
+
+static int hostapd_config_read_wpa_psk(const char *fname,
+				       struct hostapd_ssid *ssid)
+{
+	FILE *f;
+	char buf[128], *pos;
+	int line = 0, ret = 0, len, ok;
+	u8 addr[ETH_ALEN];
+	struct hostapd_wpa_psk *psk;
+
+	if (!fname)
+		return 0;
+
+	f = fopen(fname, "r");
+	if (!f) {
+		wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		if (buf[0] == '\0')
+			continue;
+
+		if (hwaddr_aton(buf, addr)) {
+			wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on "
+				   "line %d in '%s'", buf, line, fname);
+			ret = -1;
+			break;
+		}
+
+		psk = os_zalloc(sizeof(*psk));
+		if (psk == NULL) {
+			wpa_printf(MSG_ERROR, "WPA PSK allocation failed");
+			ret = -1;
+			break;
+		}
+		if (is_zero_ether_addr(addr))
+			psk->group = 1;
+		else
+			os_memcpy(psk->addr, addr, ETH_ALEN);
+
+		pos = buf + 17;
+		if (*pos == '\0') {
+			wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
+				   line, fname);
+			os_free(psk);
+			ret = -1;
+			break;
+		}
+		pos++;
+
+		ok = 0;
+		len = os_strlen(pos);
+		if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
+			ok = 1;
+		else if (len >= 8 && len < 64) {
+			pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len,
+				    4096, psk->psk, PMK_LEN);
+			ok = 1;
+		}
+		if (!ok) {
+			wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in "
+				   "'%s'", pos, line, fname);
+			os_free(psk);
+			ret = -1;
+			break;
+		}
+
+		psk->next = ssid->wpa_psk;
+		ssid->wpa_psk = psk;
+	}
+
+	fclose(f);
+
+	return ret;
+}
+
+
+static int hostapd_derive_psk(struct hostapd_ssid *ssid)
+{
+	ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+	if (ssid->wpa_psk == NULL) {
+		wpa_printf(MSG_ERROR, "Unable to alloc space for PSK");
+		return -1;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "SSID",
+			  (u8 *) ssid->ssid, ssid->ssid_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
+			      (u8 *) ssid->wpa_passphrase,
+			      os_strlen(ssid->wpa_passphrase));
+	pbkdf2_sha1(ssid->wpa_passphrase,
+		    ssid->ssid, ssid->ssid_len,
+		    4096, ssid->wpa_psk->psk, PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
+			ssid->wpa_psk->psk, PMK_LEN);
+	return 0;
+}
+
+
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
+{
+	struct hostapd_ssid *ssid = &conf->ssid;
+
+	if (ssid->wpa_passphrase != NULL) {
+		if (ssid->wpa_psk != NULL) {
+			wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
+				   "instead of passphrase");
+		} else {
+			wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
+				   "passphrase");
+			if (hostapd_derive_psk(ssid) < 0)
+				return -1;
+		}
+		ssid->wpa_psk->group = 1;
+	}
+
+	if (ssid->wpa_psk_file) {
+		if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file,
+						&conf->ssid))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b)
+{
+	int i;
+
+	if (a->idx != b->idx || a->default_len != b->default_len)
+		return 1;
+	for (i = 0; i < NUM_WEP_KEYS; i++)
+		if (a->len[i] != b->len[i] ||
+		    os_memcmp(a->key[i], b->key[i], a->len[i]) != 0)
+			return 1;
+	return 0;
+}
+
+
+static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
+				       int num_servers)
+{
+	int i;
+
+	for (i = 0; i < num_servers; i++) {
+		os_free(servers[i].shared_secret);
+	}
+	os_free(servers);
+}
+
+
+static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+{
+	os_free(user->identity);
+	os_free(user->password);
+	os_free(user);
+}
+
+
+static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
+{
+	int i;
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		os_free(keys->key[i]);
+		keys->key[i] = NULL;
+	}
+}
+
+
+static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+{
+	struct hostapd_wpa_psk *psk, *prev;
+	struct hostapd_eap_user *user, *prev_user;
+
+	if (conf == NULL)
+		return;
+
+	psk = conf->ssid.wpa_psk;
+	while (psk) {
+		prev = psk;
+		psk = psk->next;
+		os_free(prev);
+	}
+
+	os_free(conf->ssid.wpa_passphrase);
+	os_free(conf->ssid.wpa_psk_file);
+	hostapd_config_free_wep(&conf->ssid.wep);
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	os_free(conf->ssid.vlan_tagged_interface);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	user = conf->eap_user;
+	while (user) {
+		prev_user = user;
+		user = user->next;
+		hostapd_config_free_eap_user(prev_user);
+	}
+
+	os_free(conf->dump_log_name);
+	os_free(conf->eap_req_id_text);
+	os_free(conf->accept_mac);
+	os_free(conf->deny_mac);
+	os_free(conf->nas_identifier);
+	hostapd_config_free_radius(conf->radius->auth_servers,
+				   conf->radius->num_auth_servers);
+	hostapd_config_free_radius(conf->radius->acct_servers,
+				   conf->radius->num_acct_servers);
+	os_free(conf->rsn_preauth_interfaces);
+	os_free(conf->ctrl_interface);
+	os_free(conf->ca_cert);
+	os_free(conf->server_cert);
+	os_free(conf->private_key);
+	os_free(conf->private_key_passwd);
+	os_free(conf->dh_file);
+	os_free(conf->pac_opaque_encr_key);
+	os_free(conf->eap_fast_a_id);
+	os_free(conf->eap_fast_a_id_info);
+	os_free(conf->eap_sim_db);
+	os_free(conf->radius_server_clients);
+	os_free(conf->test_socket);
+	os_free(conf->radius);
+	hostapd_config_free_vlan(conf);
+	if (conf->ssid.dyn_vlan_keys) {
+		struct hostapd_ssid *ssid = &conf->ssid;
+		size_t i;
+		for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
+			if (ssid->dyn_vlan_keys[i] == NULL)
+				continue;
+			hostapd_config_free_wep(ssid->dyn_vlan_keys[i]);
+			os_free(ssid->dyn_vlan_keys[i]);
+		}
+		os_free(ssid->dyn_vlan_keys);
+		ssid->dyn_vlan_keys = NULL;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	{
+		struct ft_remote_r0kh *r0kh, *r0kh_prev;
+		struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+		r0kh = conf->r0kh_list;
+		conf->r0kh_list = NULL;
+		while (r0kh) {
+			r0kh_prev = r0kh;
+			r0kh = r0kh->next;
+			os_free(r0kh_prev);
+		}
+
+		r1kh = conf->r1kh_list;
+		conf->r1kh_list = NULL;
+		while (r1kh) {
+			r1kh_prev = r1kh;
+			r1kh = r1kh->next;
+			os_free(r1kh_prev);
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_WPS
+	os_free(conf->wps_pin_requests);
+	os_free(conf->device_name);
+	os_free(conf->manufacturer);
+	os_free(conf->model_name);
+	os_free(conf->model_number);
+	os_free(conf->serial_number);
+	os_free(conf->config_methods);
+	os_free(conf->ap_pin);
+	os_free(conf->extra_cred);
+	os_free(conf->ap_settings);
+	os_free(conf->upnp_iface);
+	os_free(conf->friendly_name);
+	os_free(conf->manufacturer_url);
+	os_free(conf->model_description);
+	os_free(conf->model_url);
+	os_free(conf->upc);
+#endif /* CONFIG_WPS */
+}
+
+
+/**
+ * hostapd_config_free - Free hostapd configuration
+ * @conf: Configuration data from hostapd_config_read().
+ */
+void hostapd_config_free(struct hostapd_config *conf)
+{
+	size_t i;
+
+	if (conf == NULL)
+		return;
+
+	for (i = 0; i < conf->num_bss; i++)
+		hostapd_config_free_bss(&conf->bss[i]);
+	os_free(conf->bss);
+	os_free(conf->supported_rates);
+	os_free(conf->basic_rates);
+
+	os_free(conf);
+}
+
+
+/**
+ * hostapd_maclist_found - Find a MAC address from a list
+ * @list: MAC address list
+ * @num_entries: Number of addresses in the list
+ * @addr: Address to search for
+ * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed
+ * Returns: 1 if address is in the list or 0 if not.
+ *
+ * Perform a binary search for given MAC address from a pre-sorted list.
+ */
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+			  const u8 *addr, int *vlan_id)
+{
+	int start, end, middle, res;
+
+	start = 0;
+	end = num_entries - 1;
+
+	while (start <= end) {
+		middle = (start + end) / 2;
+		res = os_memcmp(list[middle].addr, addr, ETH_ALEN);
+		if (res == 0) {
+			if (vlan_id)
+				*vlan_id = list[middle].vlan_id;
+			return 1;
+		}
+		if (res < 0)
+			start = middle + 1;
+		else
+			end = middle - 1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_rate_found(int *list, int rate)
+{
+	int i;
+
+	if (list == NULL)
+		return 0;
+
+	for (i = 0; list[i] >= 0; i++)
+		if (list[i] == rate)
+			return 1;
+
+	return 0;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+	struct hostapd_vlan *v = vlan;
+	while (v) {
+		if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+			return v->ifname;
+		v = v->next;
+	}
+	return NULL;
+}
+
+
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+			   const u8 *addr, const u8 *prev_psk)
+{
+	struct hostapd_wpa_psk *psk;
+	int next_ok = prev_psk == NULL;
+
+	for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
+		if (next_ok &&
+		    (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0))
+			return psk->psk;
+
+		if (psk->psk == prev_psk)
+			next_ok = 1;
+	}
+
+	return NULL;
+}
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
+		     size_t identity_len, int phase2)
+{
+	struct hostapd_eap_user *user = conf->eap_user;
+
+#ifdef CONFIG_WPS
+	if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
+	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
+		static struct hostapd_eap_user wsc_enrollee;
+		os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
+		wsc_enrollee.methods[0].method = eap_server_get_type(
+			"WSC", &wsc_enrollee.methods[0].vendor);
+		return &wsc_enrollee;
+	}
+
+	if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
+	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
+		static struct hostapd_eap_user wsc_registrar;
+		os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
+		wsc_registrar.methods[0].method = eap_server_get_type(
+			"WSC", &wsc_registrar.methods[0].vendor);
+		wsc_registrar.password = (u8 *) conf->ap_pin;
+		wsc_registrar.password_len = conf->ap_pin ?
+			os_strlen(conf->ap_pin) : 0;
+		return &wsc_registrar;
+	}
+#endif /* CONFIG_WPS */
+
+	while (user) {
+		if (!phase2 && user->identity == NULL) {
+			/* Wildcard match */
+			break;
+		}
+
+		if (user->phase2 == !!phase2 && user->wildcard_prefix &&
+		    identity_len >= user->identity_len &&
+		    os_memcmp(user->identity, identity, user->identity_len) ==
+		    0) {
+			/* Wildcard prefix match */
+			break;
+		}
+
+		if (user->phase2 == !!phase2 &&
+		    user->identity_len == identity_len &&
+		    os_memcmp(user->identity, identity, identity_len) == 0)
+			break;
+		user = user->next;
+	}
+
+	return user;
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
new file mode 100644
index 0000000..25720b8
--- /dev/null
+++ b/src/ap/ap_config.h
@@ -0,0 +1,417 @@
+/*
+ * hostapd / Configuration definitions and helpers functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 HOSTAPD_CONFIG_H
+#define HOSTAPD_CONFIG_H
+
+#include "common/defs.h"
+#include "ip_addr.h"
+#include "common/wpa_common.h"
+#include "wps/wps.h"
+
+#define MAX_STA_COUNT 2007
+#define MAX_VLAN_ID 4094
+
+typedef u8 macaddr[ETH_ALEN];
+
+struct mac_acl_entry {
+	macaddr addr;
+	int vlan_id;
+};
+
+struct hostapd_radius_servers;
+struct ft_remote_r0kh;
+struct ft_remote_r1kh;
+
+#define HOSTAPD_MAX_SSID_LEN 32
+
+#define NUM_WEP_KEYS 4
+struct hostapd_wep_keys {
+	u8 idx;
+	u8 *key[NUM_WEP_KEYS];
+	size_t len[NUM_WEP_KEYS];
+	int keys_set;
+	size_t default_len; /* key length used for dynamic key generation */
+};
+
+typedef enum hostap_security_policy {
+	SECURITY_PLAINTEXT = 0,
+	SECURITY_STATIC_WEP = 1,
+	SECURITY_IEEE_802_1X = 2,
+	SECURITY_WPA_PSK = 3,
+	SECURITY_WPA = 4
+} secpolicy;
+
+struct hostapd_ssid {
+	char ssid[HOSTAPD_MAX_SSID_LEN + 1];
+	size_t ssid_len;
+	int ssid_set;
+
+	char vlan[IFNAMSIZ + 1];
+	secpolicy security_policy;
+
+	struct hostapd_wpa_psk *wpa_psk;
+	char *wpa_passphrase;
+	char *wpa_psk_file;
+
+	struct hostapd_wep_keys wep;
+
+#define DYNAMIC_VLAN_DISABLED 0
+#define DYNAMIC_VLAN_OPTIONAL 1
+#define DYNAMIC_VLAN_REQUIRED 2
+	int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	char *vlan_tagged_interface;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+	struct hostapd_wep_keys **dyn_vlan_keys;
+	size_t max_dyn_vlan_keys;
+};
+
+
+#define VLAN_ID_WILDCARD -1
+
+struct hostapd_vlan {
+	struct hostapd_vlan *next;
+	int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+	char ifname[IFNAMSIZ + 1];
+	int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#define DVLAN_CLEAN_BR 	0x1
+#define DVLAN_CLEAN_VLAN	0x2
+#define DVLAN_CLEAN_VLAN_PORT	0x4
+#define DVLAN_CLEAN_WLAN_PORT	0x8
+	int clean;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+#define PMK_LEN 32
+struct hostapd_wpa_psk {
+	struct hostapd_wpa_psk *next;
+	int group;
+	u8 psk[PMK_LEN];
+	u8 addr[ETH_ALEN];
+};
+
+#define EAP_USER_MAX_METHODS 8
+struct hostapd_eap_user {
+	struct hostapd_eap_user *next;
+	u8 *identity;
+	size_t identity_len;
+	struct {
+		int vendor;
+		u32 method;
+	} methods[EAP_USER_MAX_METHODS];
+	u8 *password;
+	size_t password_len;
+	int phase2;
+	int force_version;
+	unsigned int wildcard_prefix:1;
+	unsigned int password_hash:1; /* whether password is hashed with
+				       * nt_password_hash() */
+	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+};
+
+
+#define NUM_TX_QUEUES 4
+
+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 */
+};
+
+struct hostapd_wmm_ac_params {
+	int cwmin;
+	int cwmax;
+	int aifs;
+	int txop_limit; /* in units of 32us */
+	int admission_control_mandatory;
+};
+
+
+/**
+ * struct hostapd_bss_config - Per-BSS configuration
+ */
+struct hostapd_bss_config {
+	char iface[IFNAMSIZ + 1];
+	char bridge[IFNAMSIZ + 1];
+	char wds_bridge[IFNAMSIZ + 1];
+
+	enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
+
+	unsigned int logger_syslog; /* module bitfield */
+	unsigned int logger_stdout; /* module bitfield */
+
+	char *dump_log_name; /* file name for state dump (SIGUSR1) */
+
+	int max_num_sta; /* maximum number of STAs in station table */
+
+	int dtim_period;
+
+	int ieee802_1x; /* use IEEE 802.1X */
+	int eapol_version;
+	int eap_server; /* Use internal EAP server instead of external
+			 * RADIUS server */
+	struct hostapd_eap_user *eap_user;
+	char *eap_sim_db;
+	struct hostapd_ip_addr own_ip_addr;
+	char *nas_identifier;
+	struct hostapd_radius_servers *radius;
+	int acct_interim_interval;
+
+	struct hostapd_ssid ssid;
+
+	char *eap_req_id_text; /* optional displayable message sent with
+				* EAP Request-Identity */
+	size_t eap_req_id_text_len;
+	int eapol_key_index_workaround;
+
+	size_t default_wep_key_len;
+	int individual_wep_key_len;
+	int wep_rekeying_period;
+	int broadcast_key_idx_min, broadcast_key_idx_max;
+	int eap_reauth_period;
+
+	int ieee802_11f; /* use IEEE 802.11f (IAPP) */
+	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
+					* frames */
+
+	enum {
+		ACCEPT_UNLESS_DENIED = 0,
+		DENY_UNLESS_ACCEPTED = 1,
+		USE_EXTERNAL_RADIUS_AUTH = 2
+	} macaddr_acl;
+	struct mac_acl_entry *accept_mac;
+	int num_accept_mac;
+	struct mac_acl_entry *deny_mac;
+	int num_deny_mac;
+	int wds_sta;
+	int isolate;
+
+	int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
+			* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
+
+	int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+	int wpa_key_mgmt;
+#ifdef CONFIG_IEEE80211W
+	enum mfp_options ieee80211w;
+	/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
+	unsigned int assoc_sa_query_max_timeout;
+	/* dot11AssociationSAQueryRetryTimeout (in TUs) */
+	int assoc_sa_query_retry_timeout;
+#endif /* CONFIG_IEEE80211W */
+	int wpa_pairwise;
+	int wpa_group;
+	int wpa_group_rekey;
+	int wpa_strict_rekey;
+	int wpa_gmk_rekey;
+	int wpa_ptk_rekey;
+	int rsn_pairwise;
+	int rsn_preauth;
+	char *rsn_preauth_interfaces;
+	int peerkey;
+
+#ifdef CONFIG_IEEE80211R
+	/* IEEE 802.11r - Fast BSS Transition */
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 r1_key_holder[FT_R1KH_ID_LEN];
+	u32 r0_key_lifetime;
+	u32 reassociation_deadline;
+	struct ft_remote_r0kh *r0kh_list;
+	struct ft_remote_r1kh *r1kh_list;
+	int pmk_r1_push;
+	int ft_over_ds;
+#endif /* CONFIG_IEEE80211R */
+
+	char *ctrl_interface; /* directory for UNIX domain sockets */
+#ifndef CONFIG_NATIVE_WINDOWS
+	gid_t ctrl_interface_gid;
+#endif /* CONFIG_NATIVE_WINDOWS */
+	int ctrl_interface_gid_set;
+
+	char *ca_cert;
+	char *server_cert;
+	char *private_key;
+	char *private_key_passwd;
+	int check_crl;
+	char *dh_file;
+	u8 *pac_opaque_encr_key;
+	u8 *eap_fast_a_id;
+	size_t eap_fast_a_id_len;
+	char *eap_fast_a_id_info;
+	int eap_fast_prov;
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+	int eap_sim_aka_result_ind;
+	int tnc;
+	int fragment_size;
+	u16 pwd_group;
+
+	char *radius_server_clients;
+	int radius_server_auth_port;
+	int radius_server_ipv6;
+
+	char *test_socket; /* UNIX domain socket path for driver_test */
+
+	int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
+				 * address instead of individual address
+				 * (for driver_wired.c).
+				 */
+
+	int ap_max_inactivity;
+	int ignore_broadcast_ssid;
+
+	int wmm_enabled;
+	int wmm_uapsd;
+
+	struct hostapd_vlan *vlan, *vlan_tail;
+
+	macaddr bssid;
+
+	/*
+	 * Maximum listen interval that STAs can use when associating with this
+	 * BSS. If a STA tries to use larger value, the association will be
+	 * denied with status code 51.
+	 */
+	u16 max_listen_interval;
+
+	int okc; /* Opportunistic Key Caching */
+
+	int wps_state;
+#ifdef CONFIG_WPS
+	int ap_setup_locked;
+	u8 uuid[16];
+	char *wps_pin_requests;
+	char *device_name;
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+	u8 device_type[WPS_DEV_TYPE_LEN];
+	char *config_methods;
+	u8 os_version[4];
+	char *ap_pin;
+	int skip_cred_build;
+	u8 *extra_cred;
+	size_t extra_cred_len;
+	int wps_cred_processing;
+	u8 *ap_settings;
+	size_t ap_settings_len;
+	char *upnp_iface;
+	char *friendly_name;
+	char *manufacturer_url;
+	char *model_description;
+	char *model_url;
+	char *upc;
+	struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+#endif /* CONFIG_WPS */
+
+#define P2P_ENABLED BIT(0)
+#define P2P_GROUP_OWNER BIT(1)
+#define P2P_GROUP_FORMATION BIT(2)
+#define P2P_MANAGE BIT(3)
+#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
+	int p2p;
+
+	int disassoc_low_ack;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+	int tdls;
+	int disable_11n;
+};
+
+
+/**
+ * struct hostapd_config - Per-radio interface configuration
+ */
+struct hostapd_config {
+	struct hostapd_bss_config *bss, *last_bss;
+	size_t num_bss;
+
+	u16 beacon_int;
+	int rts_threshold;
+	int fragm_threshold;
+	u8 send_probe_response;
+	u8 channel;
+	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+	enum {
+		LONG_PREAMBLE = 0,
+		SHORT_PREAMBLE = 1
+	} preamble;
+	enum {
+		CTS_PROTECTION_AUTOMATIC = 0,
+		CTS_PROTECTION_FORCE_ENABLED = 1,
+		CTS_PROTECTION_FORCE_DISABLED = 2,
+		CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3,
+	} cts_protection_type;
+
+	int *supported_rates;
+	int *basic_rates;
+
+	const struct wpa_driver_ops *driver;
+
+	int ap_table_max_size;
+	int ap_table_expiration_time;
+
+	char country[3]; /* first two octets: country code as described in
+			  * ISO/IEC 3166-1. Third octet:
+			  * ' ' (ascii 32): all environments
+			  * 'O': Outdoor environemnt only
+			  * 'I': Indoor environment only
+			  */
+
+	int ieee80211d;
+
+	struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
+
+	/*
+	 * WMM 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_wmm_ac_params wmm_ac_params[4];
+
+	int ht_op_mode_fixed;
+	u16 ht_capab;
+	int ieee80211n;
+	int secondary_channel;
+	int require_ht;
+};
+
+
+int hostapd_mac_comp(const void *a, const void *b);
+int hostapd_mac_comp_empty(const void *a);
+struct hostapd_config * hostapd_config_defaults(void);
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free(struct hostapd_config *conf);
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+			  const u8 *addr, int *vlan_id);
+int hostapd_rate_found(int *list, int rate);
+int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
+			struct hostapd_wep_keys *b);
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+			   const u8 *addr, const u8 *prev_psk);
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+					int vlan_id);
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
+		     size_t identity_len, int phase2);
+
+#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
new file mode 100644
index 0000000..0b6836c
--- /dev/null
+++ b/src/ap/ap_drv_ops.c
@@ -0,0 +1,632 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+
+
+u32 hostapd_sta_flags_to_drv(u32 flags)
+{
+	int res = 0;
+	if (flags & WLAN_STA_AUTHORIZED)
+		res |= WPA_STA_AUTHORIZED;
+	if (flags & WLAN_STA_WMM)
+		res |= WPA_STA_WMM;
+	if (flags & WLAN_STA_SHORT_PREAMBLE)
+		res |= WPA_STA_SHORT_PREAMBLE;
+	if (flags & WLAN_STA_MFP)
+		res |= WPA_STA_MFP;
+	return res;
+}
+
+
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+{
+	struct wpabuf *beacon, *proberesp, *assocresp = NULL;
+	int ret;
+
+	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+		return 0;
+
+	beacon = hapd->wps_beacon_ie;
+	proberesp = hapd->wps_probe_resp_ie;
+
+#ifdef CONFIG_P2P
+	if (hapd->wps_beacon_ie == NULL && hapd->p2p_beacon_ie == NULL)
+		beacon = NULL;
+	else {
+		beacon = wpabuf_alloc((hapd->wps_beacon_ie ?
+				       wpabuf_len(hapd->wps_beacon_ie) : 0) +
+				      (hapd->p2p_beacon_ie ?
+				       wpabuf_len(hapd->p2p_beacon_ie) : 0));
+		if (beacon == NULL)
+			return -1;
+		if (hapd->wps_beacon_ie)
+			wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
+		if (hapd->p2p_beacon_ie)
+			wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
+	}
+
+	if (hapd->wps_probe_resp_ie == NULL && hapd->p2p_probe_resp_ie == NULL)
+		proberesp = NULL;
+	else {
+		proberesp = wpabuf_alloc(
+			(hapd->wps_probe_resp_ie ?
+			 wpabuf_len(hapd->wps_probe_resp_ie) : 0) +
+			(hapd->p2p_probe_resp_ie ?
+			 wpabuf_len(hapd->p2p_probe_resp_ie) : 0));
+		if (proberesp == NULL) {
+			wpabuf_free(beacon);
+			return -1;
+		}
+		if (hapd->wps_probe_resp_ie)
+			wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
+		if (hapd->p2p_probe_resp_ie)
+			wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE) {
+		struct wpabuf *a;
+
+		a = wpabuf_alloc(100 + (beacon ? wpabuf_len(beacon) : 0));
+		if (a) {
+			u8 *start, *p;
+			if (beacon)
+				wpabuf_put_buf(a, beacon);
+			if (beacon != hapd->wps_beacon_ie)
+				wpabuf_free(beacon);
+			start = wpabuf_put(a, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(a, p - start);
+			beacon = a;
+		}
+
+		a = wpabuf_alloc(100 + (proberesp ? wpabuf_len(proberesp) :
+					0));
+		if (a) {
+			u8 *start, *p;
+			if (proberesp)
+				wpabuf_put_buf(a, proberesp);
+			if (proberesp != hapd->wps_probe_resp_ie)
+				wpabuf_free(proberesp);
+			start = wpabuf_put(a, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(a, p - start);
+			proberesp = a;
+		}
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WPS2
+	if (hapd->conf->wps_state)
+		assocresp = wps_build_assoc_resp_ie();
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE) {
+		struct wpabuf *a;
+		a = wpabuf_alloc(100 + (assocresp ? wpabuf_len(assocresp) :
+					0));
+		if (a) {
+			u8 *start, *p;
+			start = wpabuf_put(a, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(a, p - start);
+			if (assocresp) {
+				wpabuf_put_buf(a, assocresp);
+				wpabuf_free(assocresp);
+			}
+			assocresp = a;
+		}
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+					  assocresp);
+
+	if (beacon != hapd->wps_beacon_ie)
+		wpabuf_free(beacon);
+	if (proberesp != hapd->wps_probe_resp_ie)
+		wpabuf_free(proberesp);
+	wpabuf_free(assocresp);
+
+	return ret;
+}
+
+
+int hostapd_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized)
+{
+	if (authorized) {
+		return hostapd_sta_set_flags(hapd, sta->addr,
+					     hostapd_sta_flags_to_drv(
+						     sta->flags),
+					     WPA_STA_AUTHORIZED, ~0);
+	}
+
+	return hostapd_sta_set_flags(hapd, sta->addr,
+				     hostapd_sta_flags_to_drv(sta->flags),
+				     0, ~WPA_STA_AUTHORIZED);
+}
+
+
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int set_flags, total_flags, flags_and, flags_or;
+	total_flags = hostapd_sta_flags_to_drv(sta->flags);
+	set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
+	if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+	     sta->auth_alg == WLAN_AUTH_FT) &&
+	    sta->flags & WLAN_STA_AUTHORIZED)
+		set_flags |= WPA_STA_AUTHORIZED;
+	flags_or = total_flags & set_flags;
+	flags_and = total_flags | ~set_flags;
+	return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+				     flags_or, flags_and);
+}
+
+
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+			      int enabled)
+{
+	struct wpa_bss_params params;
+	os_memset(&params, 0, sizeof(params));
+	params.ifname = ifname;
+	params.enabled = enabled;
+	if (enabled) {
+		params.wpa = hapd->conf->wpa;
+		params.ieee802_1x = hapd->conf->ieee802_1x;
+		params.wpa_group = hapd->conf->wpa_group;
+		params.wpa_pairwise = hapd->conf->wpa_pairwise;
+		params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+		params.rsn_preauth = hapd->conf->rsn_preauth;
+#ifdef CONFIG_IEEE80211W
+		params.ieee80211w = hapd->conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+	}
+	return hostapd_set_ieee8021x(hapd, &params);
+}
+
+
+static int hostapd_set_ap_isolate(struct hostapd_data *hapd, int value)
+{
+	if (hapd->driver == NULL || hapd->driver->set_intra_bss == NULL)
+		return 0;
+	return hapd->driver->set_intra_bss(hapd->drv_priv, !value);
+}
+
+
+int hostapd_set_bss_params(struct hostapd_data *hapd, int use_protection)
+{
+	int ret = 0;
+	int preamble;
+#ifdef CONFIG_IEEE80211N
+	u8 buf[60], *ht_capab, *ht_oper, *pos;
+
+	pos = buf;
+	ht_capab = pos;
+	pos = hostapd_eid_ht_capabilities(hapd, pos);
+	ht_oper = pos;
+	pos = hostapd_eid_ht_operation(hapd, pos);
+	if (pos > ht_oper && ht_oper > ht_capab &&
+	    hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1],
+				  ht_oper + 2, ht_oper[1])) {
+		wpa_printf(MSG_ERROR, "Could not set HT capabilities "
+			   "for kernel driver");
+		ret = -1;
+	}
+
+#endif /* CONFIG_IEEE80211N */
+
+	if (hostapd_set_cts_protect(hapd, use_protection)) {
+		wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
+			   "driver");
+		ret = -1;
+	}
+
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    hostapd_set_short_slot_time(hapd,
+					hapd->iface->num_sta_no_short_slot_time
+					> 0 ? 0 : 1)) {
+		wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option "
+			   "in kernel driver");
+		ret = -1;
+	}
+
+	if (hapd->iface->num_sta_no_short_preamble == 0 &&
+	    hapd->iconf->preamble == SHORT_PREAMBLE)
+		preamble = SHORT_PREAMBLE;
+	else
+		preamble = LONG_PREAMBLE;
+	if (hostapd_set_preamble(hapd, preamble)) {
+		wpa_printf(MSG_ERROR, "Could not set preamble for kernel "
+			   "driver");
+		ret = -1;
+	}
+
+	if (hostapd_set_ap_isolate(hapd, hapd->conf->isolate) &&
+	    hapd->conf->isolate) {
+		wpa_printf(MSG_ERROR, "Could not enable AP isolation in "
+			   "kernel driver");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+{
+	char force_ifname[IFNAMSIZ];
+	u8 if_addr[ETH_ALEN];
+	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+			      NULL, NULL, force_ifname, if_addr, NULL);
+}
+
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
+{
+	return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
+}
+
+
+int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+			int val)
+{
+	const char *bridge = NULL;
+
+	if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+		return 0;
+	if (hapd->conf->wds_bridge[0])
+		bridge = hapd->conf->wds_bridge;
+	else if (hapd->conf->bridge[0])
+		bridge = hapd->conf->bridge;
+	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+					 bridge);
+}
+
+
+int hostapd_sta_add(struct hostapd_data *hapd,
+		    const u8 *addr, u16 aid, u16 capability,
+		    const u8 *supp_rates, size_t supp_rates_len,
+		    u16 listen_interval,
+		    const struct ieee80211_ht_capabilities *ht_capab)
+{
+	struct hostapd_sta_add_params params;
+
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->sta_add == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.addr = addr;
+	params.aid = aid;
+	params.capability = capability;
+	params.supp_rates = supp_rates;
+	params.supp_rates_len = supp_rates_len;
+	params.listen_interval = listen_interval;
+	params.ht_capabilities = ht_capab;
+	return hapd->driver->sta_add(hapd->drv_priv, &params);
+}
+
+
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
+{
+	if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
+		return 0;
+	return hapd->driver->set_privacy(hapd->drv_priv, enabled);
+}
+
+
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+			     size_t elem_len)
+{
+	if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
+		return 0;
+	return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
+}
+
+
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
+		return 0;
+	return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
+		return 0;
+	return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		   const char *ifname, const u8 *addr, void *bss_ctx,
+		   void **drv_priv, char *force_ifname, u8 *if_addr,
+		   const char *bridge)
+{
+	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+		return -1;
+	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+				    bss_ctx, drv_priv, force_ifname, if_addr,
+				    bridge);
+}
+
+
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		      const char *ifname)
+{
+	if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
+		return -1;
+	return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
+}
+
+
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+			  struct wpa_bss_params *params)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+		return 0;
+	return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
+}
+
+
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+		       const u8 *addr, int idx, u8 *seq)
+{
+	if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
+		return 0;
+	return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
+					seq);
+}
+
+
+int hostapd_flush(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->flush == NULL)
+		return 0;
+	return hapd->driver->flush(hapd->drv_priv);
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+		     int channel, int ht_enabled, int sec_channel_offset)
+{
+	struct hostapd_freq_params data;
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->set_freq == NULL)
+		return 0;
+	os_memset(&data, 0, sizeof(data));
+	data.mode = mode;
+	data.freq = freq;
+	data.channel = channel;
+	data.ht_enabled = ht_enabled;
+	data.sec_channel_offset = sec_channel_offset;
+	return hapd->driver->set_freq(hapd->drv_priv, &data);
+}
+
+int hostapd_set_rts(struct hostapd_data *hapd, int rts)
+{
+	if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
+		return 0;
+	return hapd->driver->set_rts(hapd->drv_priv, rts);
+}
+
+
+int hostapd_set_frag(struct hostapd_data *hapd, int frag)
+{
+	if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
+		return 0;
+	return hapd->driver->set_frag(hapd->drv_priv, frag);
+}
+
+
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+			  int total_flags, int flags_or, int flags_and)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
+		return 0;
+	return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
+					   flags_or, flags_and);
+}
+
+
+int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
+			  int *basic_rates, int mode)
+{
+	if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL)
+		return 0;
+	return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates,
+					   basic_rates, mode);
+}
+
+
+int hostapd_set_country(struct hostapd_data *hapd, const char *country)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->set_country == NULL)
+		return 0;
+	return hapd->driver->set_country(hapd->drv_priv, country);
+}
+
+
+int hostapd_set_cts_protect(struct hostapd_data *hapd, int value)
+{
+	if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL)
+		return 0;
+	return hapd->driver->set_cts_protect(hapd->drv_priv, value);
+}
+
+
+int hostapd_set_preamble(struct hostapd_data *hapd, int value)
+{
+	if (hapd->driver == NULL || hapd->driver->set_preamble == NULL)
+		return 0;
+	return hapd->driver->set_preamble(hapd->drv_priv, value);
+}
+
+
+int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value)
+{
+	if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL)
+		return 0;
+	return hapd->driver->set_short_slot_time(hapd->drv_priv, value);
+}
+
+
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+				int cw_min, int cw_max, int burst_time)
+{
+	if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
+		return 0;
+	return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
+						 cw_min, cw_max, burst_time);
+}
+
+
+int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
+			   const u8 *mask)
+{
+	if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL)
+		return 1;
+	return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask);
+}
+
+
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+			    u16 *flags)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->get_hw_feature_data == NULL)
+		return NULL;
+	return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
+						 flags);
+}
+
+
+int hostapd_driver_commit(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->commit == NULL)
+		return 0;
+	return hapd->driver->commit(hapd->drv_priv);
+}
+
+
+int hostapd_set_ht_params(struct hostapd_data *hapd,
+			  const u8 *ht_capab, size_t ht_capab_len,
+			  const u8 *ht_oper, size_t ht_oper_len)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL ||
+	    ht_capab == NULL || ht_oper == NULL)
+		return 0;
+	return hapd->driver->set_ht_params(hapd->drv_priv,
+					   ht_capab, ht_capab_len,
+					   ht_oper, ht_oper_len);
+}
+
+
+int hostapd_drv_none(struct hostapd_data *hapd)
+{
+	return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
+}
+
+
+int hostapd_driver_scan(struct hostapd_data *hapd,
+			struct wpa_driver_scan_params *params)
+{
+	if (hapd->driver && hapd->driver->scan2)
+		return hapd->driver->scan2(hapd->drv_priv, params);
+	return -1;
+}
+
+
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+	struct hostapd_data *hapd)
+{
+	if (hapd->driver && hapd->driver->get_scan_results2)
+		return hapd->driver->get_scan_results2(hapd->drv_priv);
+	return NULL;
+}
+
+
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			   int duration)
+{
+	if (hapd->driver && hapd->driver->set_noa)
+		return hapd->driver->set_noa(hapd->drv_priv, count, start,
+					     duration);
+	return -1;
+}
+
+
+int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
+			enum wpa_alg alg, const u8 *addr,
+			int key_idx, int set_tx,
+			const u8 *seq, size_t seq_len,
+			const u8 *key, size_t key_len)
+{
+	if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+		return 0;
+	return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
+				     key_idx, set_tx, seq, seq_len, key,
+				     key_len);
+}
+
+
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+			  const void *msg, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+		return 0;
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+}
+
+
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+			   const u8 *addr, int reason)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+		return 0;
+	return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+					reason);
+}
+
+
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+			     const u8 *addr, int reason)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+		return 0;
+	return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+					  reason);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
new file mode 100644
index 0000000..36bb826
--- /dev/null
+++ b/src/ap/ap_drv_ops.h
@@ -0,0 +1,197 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 AP_DRV_OPS
+#define AP_DRV_OPS
+
+enum wpa_driver_if_type;
+struct wpa_bss_params;
+struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
+
+u32 hostapd_sta_flags_to_drv(u32 flags);
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized);
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+			      int enabled);
+int hostapd_set_bss_params(struct hostapd_data *hapd, int use_protection);
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+			int val);
+int hostapd_sta_add(struct hostapd_data *hapd,
+		    const u8 *addr, u16 aid, u16 capability,
+		    const u8 *supp_rates, size_t supp_rates_len,
+		    u16 listen_interval,
+		    const struct ieee80211_ht_capabilities *ht_capab);
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+			     size_t elem_len);
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		   const char *ifname, const u8 *addr, void *bss_ctx,
+		   void **drv_priv, char *force_ifname, u8 *if_addr,
+		   const char *bridge);
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		      const char *ifname);
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+			  struct wpa_bss_params *params);
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+		       const u8 *addr, int idx, u8 *seq);
+int hostapd_flush(struct hostapd_data *hapd);
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+		     int channel, int ht_enabled, int sec_channel_offset);
+int hostapd_set_rts(struct hostapd_data *hapd, int rts);
+int hostapd_set_frag(struct hostapd_data *hapd, int frag);
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+			  int total_flags, int flags_or, int flags_and);
+int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
+			  int *basic_rates, int mode);
+int hostapd_set_country(struct hostapd_data *hapd, const char *country);
+int hostapd_set_cts_protect(struct hostapd_data *hapd, int value);
+int hostapd_set_preamble(struct hostapd_data *hapd, int value);
+int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value);
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+				int cw_min, int cw_max, int burst_time);
+int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
+			   const u8 *mask);
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+			    u16 *flags);
+int hostapd_driver_commit(struct hostapd_data *hapd);
+int hostapd_set_ht_params(struct hostapd_data *hapd,
+			  const u8 *ht_capab, size_t ht_capab_len,
+			  const u8 *ht_oper, size_t ht_oper_len);
+int hostapd_drv_none(struct hostapd_data *hapd);
+int hostapd_driver_scan(struct hostapd_data *hapd,
+			struct wpa_driver_scan_params *params);
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+	struct hostapd_data *hapd);
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			   int duration);
+int hostapd_drv_set_key(const char *ifname,
+			struct hostapd_data *hapd,
+			enum wpa_alg alg, const u8 *addr,
+			int key_idx, int set_tx,
+			const u8 *seq, size_t seq_len,
+			const u8 *key, size_t key_len);
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+			  const void *msg, size_t len);
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+			   const u8 *addr, int reason);
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+			     const u8 *addr, int reason);
+
+
+#include "drivers/driver.h"
+
+static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
+						  int enabled)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->hapd_set_countermeasures == NULL)
+		return 0;
+	return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+static inline int hostapd_drv_set_sta_vlan(const char *ifname,
+					   struct hostapd_data *hapd,
+					   const u8 *addr, int vlan_id)
+{
+	if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+		return 0;
+	return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
+					  vlan_id);
+}
+
+static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
+					    const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+		return 0;
+	return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+					 const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+		return 0;
+	return hapd->driver->sta_remove(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
+					      const u8 *addr, const u8 *data,
+					      size_t data_len, int encrypt,
+					      u32 flags)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+		return 0;
+	return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+					     data_len, encrypt,
+					     hapd->own_addr, flags);
+}
+
+static inline int hostapd_drv_read_sta_data(
+	struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
+	const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+		return -1;
+	return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
+}
+
+static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
+					      const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+		return 0;
+	return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_set_beacon(struct hostapd_data *hapd,
+					 const u8 *head, size_t head_len,
+					 const u8 *tail, size_t tail_len,
+					 int dtim_period, int beacon_int)
+{
+	if (hapd->driver == NULL || hapd->driver->set_beacon == NULL)
+		return 0;
+	return hapd->driver->set_beacon(hapd->drv_priv,
+					head, head_len, tail, tail_len,
+					dtim_period, beacon_int);
+}
+
+static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
+						  const u8 *mac, int accepted,
+						  u32 session_timeout)
+{
+	if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
+		return 0;
+	return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
+						 session_timeout);
+}
+
+static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
+						    const u8 *mac)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->set_radius_acl_expire == NULL)
+		return 0;
+	return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
+}
+
+#endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
new file mode 100644
index 0000000..9b9fc9e
--- /dev/null
+++ b/src/ap/ap_list.c
@@ -0,0 +1,399 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ap_list.h"
+
+
+/* AP list is a double linked list with head->prev pointing to the end of the
+ * list and tail->next = NULL. Entries are moved to the head of the list
+ * whenever a beacon has been received from the AP in question. The tail entry
+ * in this link will thus be the least recently used entry. */
+
+
+static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	int i;
+
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+	    iface->conf->channel != ap->channel)
+		return 0;
+
+	if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
+		return 1;
+
+	for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+		int rate = (ap->supported_rates[i] & 0x7f) * 5;
+		if (rate == 60 || rate == 90 || rate > 110)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
+{
+	struct ap_info *s;
+
+	s = iface->ap_hash[STA_HASH(ap)];
+	while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+		s = s->hnext;
+	return s;
+}
+
+
+static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	if (iface->ap_list) {
+		ap->prev = iface->ap_list->prev;
+		iface->ap_list->prev = ap;
+	} else
+		ap->prev = ap;
+	ap->next = iface->ap_list;
+	iface->ap_list = ap;
+}
+
+
+static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	if (iface->ap_list == ap)
+		iface->ap_list = ap->next;
+	else
+		ap->prev->next = ap->next;
+
+	if (ap->next)
+		ap->next->prev = ap->prev;
+	else if (iface->ap_list)
+		iface->ap_list->prev = ap->prev;
+}
+
+
+static void ap_ap_iter_list_add(struct hostapd_iface *iface,
+				struct ap_info *ap)
+{
+	if (iface->ap_iter_list) {
+		ap->iter_prev = iface->ap_iter_list->iter_prev;
+		iface->ap_iter_list->iter_prev = ap;
+	} else
+		ap->iter_prev = ap;
+	ap->iter_next = iface->ap_iter_list;
+	iface->ap_iter_list = ap;
+}
+
+
+static void ap_ap_iter_list_del(struct hostapd_iface *iface,
+				struct ap_info *ap)
+{
+	if (iface->ap_iter_list == ap)
+		iface->ap_iter_list = ap->iter_next;
+	else
+		ap->iter_prev->iter_next = ap->iter_next;
+
+	if (ap->iter_next)
+		ap->iter_next->iter_prev = ap->iter_prev;
+	else if (iface->ap_iter_list)
+		iface->ap_iter_list->iter_prev = ap->iter_prev;
+}
+
+
+static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
+	iface->ap_hash[STA_HASH(ap->addr)] = ap;
+}
+
+
+static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	struct ap_info *s;
+
+	s = iface->ap_hash[STA_HASH(ap->addr)];
+	if (s == NULL) return;
+	if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+		iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
+		return;
+	}
+
+	while (s->hnext != NULL &&
+	       os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+		s = s->hnext;
+	if (s->hnext != NULL)
+		s->hnext = s->hnext->hnext;
+	else
+		printf("AP: could not remove AP " MACSTR " from hash table\n",
+		       MAC2STR(ap->addr));
+}
+
+
+static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	ap_ap_hash_del(iface, ap);
+	ap_ap_list_del(iface, ap);
+	ap_ap_iter_list_del(iface, ap);
+
+	iface->num_ap--;
+	os_free(ap);
+}
+
+
+static void hostapd_free_aps(struct hostapd_iface *iface)
+{
+	struct ap_info *ap, *prev;
+
+	ap = iface->ap_list;
+
+	while (ap) {
+		prev = ap;
+		ap = ap->next;
+		ap_free_ap(iface, prev);
+	}
+
+	iface->ap_list = NULL;
+}
+
+
+int ap_ap_for_each(struct hostapd_iface *iface,
+		   int (*func)(struct ap_info *s, void *data), void *data)
+{
+	struct ap_info *s;
+	int ret = 0;
+
+	s = iface->ap_list;
+
+	while (s) {
+		ret = func(s, data);
+		if (ret)
+			break;
+		s = s->next;
+	}
+
+	return ret;
+}
+
+
+static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
+{
+	struct ap_info *ap;
+
+	ap = os_zalloc(sizeof(struct ap_info));
+	if (ap == NULL)
+		return NULL;
+
+	/* initialize AP info data */
+	os_memcpy(ap->addr, addr, ETH_ALEN);
+	ap_ap_list_add(iface, ap);
+	iface->num_ap++;
+	ap_ap_hash_add(iface, ap);
+	ap_ap_iter_list_add(iface, ap);
+
+	if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
+		wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
+			   MACSTR " from AP table", MAC2STR(ap->prev->addr));
+		ap_free_ap(iface, ap->prev);
+	}
+
+	return ap;
+}
+
+
+void ap_list_process_beacon(struct hostapd_iface *iface,
+			    const struct ieee80211_mgmt *mgmt,
+			    struct ieee802_11_elems *elems,
+			    struct hostapd_frame_info *fi)
+{
+	struct ap_info *ap;
+	struct os_time now;
+	int new_ap = 0;
+	size_t len;
+	int set_beacon = 0;
+
+	if (iface->conf->ap_table_max_size < 1)
+		return;
+
+	ap = ap_get_ap(iface, mgmt->bssid);
+	if (!ap) {
+		ap = ap_ap_add(iface, mgmt->bssid);
+		if (!ap) {
+			printf("Failed to allocate AP information entry\n");
+			return;
+		}
+		new_ap = 1;
+	}
+
+	ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
+	ap->capability = le_to_host16(mgmt->u.beacon.capab_info);
+
+	if (elems->ssid) {
+		len = elems->ssid_len;
+		if (len >= sizeof(ap->ssid))
+			len = sizeof(ap->ssid) - 1;
+		os_memcpy(ap->ssid, elems->ssid, len);
+		ap->ssid[len] = '\0';
+		ap->ssid_len = len;
+	}
+
+	os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX);
+	len = 0;
+	if (elems->supp_rates) {
+		len = elems->supp_rates_len;
+		if (len > WLAN_SUPP_RATES_MAX)
+			len = WLAN_SUPP_RATES_MAX;
+		os_memcpy(ap->supported_rates, elems->supp_rates, len);
+	}
+	if (elems->ext_supp_rates) {
+		int len2;
+		if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX)
+			len2 = WLAN_SUPP_RATES_MAX - len;
+		else
+			len2 = elems->ext_supp_rates_len;
+		os_memcpy(ap->supported_rates + len, elems->ext_supp_rates,
+			  len2);
+	}
+
+	ap->wpa = elems->wpa_ie != NULL;
+
+	if (elems->erp_info && elems->erp_info_len == 1)
+		ap->erp = elems->erp_info[0];
+	else
+		ap->erp = -1;
+
+	if (elems->ds_params && elems->ds_params_len == 1)
+		ap->channel = elems->ds_params[0];
+	else if (fi)
+		ap->channel = fi->channel;
+
+	if (elems->ht_capabilities)
+		ap->ht_support = 1;
+	else
+		ap->ht_support = 0;
+
+	ap->num_beacons++;
+	os_get_time(&now);
+	ap->last_beacon = now.sec;
+	if (fi) {
+		ap->ssi_signal = fi->ssi_signal;
+		ap->datarate = fi->datarate;
+	}
+
+	if (!new_ap && ap != iface->ap_list) {
+		/* move AP entry into the beginning of the list so that the
+		 * oldest entry is always in the end of the list */
+		ap_ap_list_del(iface, ap);
+		ap_ap_list_add(iface, ap);
+	}
+
+	if (!iface->olbc &&
+	    ap_list_beacon_olbc(iface, ap)) {
+		iface->olbc = 1;
+		wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable "
+			   "protection", MAC2STR(ap->addr));
+		set_beacon++;
+	}
+
+#ifdef CONFIG_IEEE80211N
+	if (!iface->olbc_ht && !ap->ht_support) {
+		iface->olbc_ht = 1;
+		hostapd_ht_operation_update(iface);
+		wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
+			   " - enable protection", MAC2STR(ap->addr));
+		set_beacon++;
+	}
+#endif /* CONFIG_IEEE80211N */
+
+	if (set_beacon)
+		ieee802_11_set_beacons(iface);
+}
+
+
+static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_iface *iface = eloop_ctx;
+	struct os_time now;
+	struct ap_info *ap;
+	int set_beacon = 0;
+
+	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
+
+	if (!iface->ap_list)
+		return;
+
+	os_get_time(&now);
+
+	while (iface->ap_list) {
+		ap = iface->ap_list->prev;
+		if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
+		    now.sec)
+			break;
+
+		ap_free_ap(iface, ap);
+	}
+
+	if (iface->olbc || iface->olbc_ht) {
+		int olbc = 0;
+		int olbc_ht = 0;
+
+		ap = iface->ap_list;
+		while (ap && (olbc == 0 || olbc_ht == 0)) {
+			if (ap_list_beacon_olbc(iface, ap))
+				olbc = 1;
+			if (!ap->ht_support)
+				olbc_ht = 1;
+			ap = ap->next;
+		}
+		if (!olbc && iface->olbc) {
+			wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
+			iface->olbc = 0;
+			set_beacon++;
+		}
+#ifdef CONFIG_IEEE80211N
+		if (!olbc_ht && iface->olbc_ht) {
+			wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
+			iface->olbc_ht = 0;
+			hostapd_ht_operation_update(iface);
+			set_beacon++;
+		}
+#endif /* CONFIG_IEEE80211N */
+	}
+
+	if (set_beacon)
+		ieee802_11_set_beacons(iface);
+}
+
+
+int ap_list_init(struct hostapd_iface *iface)
+{
+	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
+	return 0;
+}
+
+
+void ap_list_deinit(struct hostapd_iface *iface)
+{
+	eloop_cancel_timeout(ap_list_timer, iface, NULL);
+	hostapd_free_aps(iface);
+}
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
new file mode 100644
index 0000000..6df8981
--- /dev/null
+++ b/src/ap/ap_list.h
@@ -0,0 +1,78 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 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 AP_LIST_H
+#define AP_LIST_H
+
+struct ap_info {
+	/* Note: next/prev pointers are updated whenever a new beacon is
+	 * received because these are used to find the least recently used
+	 * entries. iter_next/iter_prev are updated only when adding new BSSes
+	 * and when removing old ones. These should be used when iterating
+	 * through the table in a manner that allows beacons to be received
+	 * during the iteration. */
+	struct ap_info *next; /* next entry in AP list */
+	struct ap_info *prev; /* previous entry in AP list */
+	struct ap_info *hnext; /* next entry in hash table list */
+	struct ap_info *iter_next; /* next entry in AP iteration list */
+	struct ap_info *iter_prev; /* previous entry in AP iteration list */
+	u8 addr[6];
+	u16 beacon_int;
+	u16 capability;
+	u8 supported_rates[WLAN_SUPP_RATES_MAX];
+	u8 ssid[33];
+	size_t ssid_len;
+	int wpa;
+	int erp; /* ERP Info or -1 if ERP info element not present */
+
+	int channel;
+	int datarate; /* in 100 kbps */
+	int ssi_signal;
+
+	int ht_support;
+
+	unsigned int num_beacons; /* number of beacon frames received */
+	os_time_t last_beacon;
+
+	int already_seen; /* whether API call AP-NEW has already fetched
+			   * information about this AP */
+};
+
+struct ieee802_11_elems;
+struct hostapd_frame_info;
+
+struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *sta);
+int ap_ap_for_each(struct hostapd_iface *iface,
+		   int (*func)(struct ap_info *s, void *data), void *data);
+void ap_list_process_beacon(struct hostapd_iface *iface,
+			    const struct ieee80211_mgmt *mgmt,
+			    struct ieee802_11_elems *elems,
+			    struct hostapd_frame_info *fi);
+#ifdef NEED_AP_MLME
+int ap_list_init(struct hostapd_iface *iface);
+void ap_list_deinit(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline int ap_list_init(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline void ap_list_deinit(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* AP_LIST_H */
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
new file mode 100644
index 0000000..2b09b11
--- /dev/null
+++ b/src/ap/ap_mlme.c
@@ -0,0 +1,184 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static const char * mlme_auth_alg_str(int alg)
+{
+	switch (alg) {
+	case WLAN_AUTH_OPEN:
+		return "OPEN_SYSTEM";
+	case WLAN_AUTH_SHARED_KEY:
+		return "SHARED_KEY";
+	case WLAN_AUTH_FT:
+		return "FT";
+	}
+
+	return "unknown";
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+/**
+ * mlme_authenticate_indication - Report the establishment of an authentication
+ * relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * authentication relationship with a specific peer MAC entity that
+ * resulted from an authentication procedure that was initiated by
+ * that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY)
+ */
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
+		       MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
+	if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_deauthenticate_indication - Report the invalidation of an
+ * authentication relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Deauthentication frame
+ *
+ * MLME calls this function as a result of the invalidation of an
+ * authentication relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 reason_code)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
+		       MAC2STR(sta->addr), reason_code);
+	mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_associate_indication - Report the establishment of an association with
+ * a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * association with a specific peer MAC entity that resulted from an
+ * association procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-ASSOCIATE.indication(" MACSTR ")",
+		       MAC2STR(sta->addr));
+	if (sta->auth_alg != WLAN_AUTH_FT)
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_reassociate_indication - Report the establishment of an reassociation
+ * with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * reassociation with a specific peer MAC entity that resulted from a
+ * reassociation procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ *
+ * sta->previous_ap contains the "Current AP" information from ReassocReq.
+ */
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-REASSOCIATE.indication(" MACSTR ")",
+		       MAC2STR(sta->addr));
+	if (sta->auth_alg != WLAN_AUTH_FT)
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_disassociate_indication - Report disassociation with a specific peer
+ * MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Disassociation frame
+ *
+ * MLME calls this function as a result of the invalidation of an association
+ * relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta, u16 reason_code)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
+		       MAC2STR(sta->addr), reason_code);
+	mlme_deletekeys_request(hapd, sta);
+}
+
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+				       const u8 *addr)
+{
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-MichaelMICFailure.indication(" MACSTR ")",
+		       MAC2STR(addr));
+}
+
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DELETEKEYS.request(" MACSTR ")",
+		       MAC2STR(sta->addr));
+
+	if (sta->wpa_sm)
+		wpa_remove_ptk(sta->wpa_sm);
+}
diff --git a/src/ap/ap_mlme.h b/src/ap/ap_mlme.h
new file mode 100644
index 0000000..c77a939
--- /dev/null
+++ b/src/ap/ap_mlme.h
@@ -0,0 +1,40 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, 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 MLME_H
+#define MLME_H
+
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta);
+
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 reason_code);
+
+void mlme_associate_indication(struct hostapd_data *hapd,
+			       struct sta_info *sta);
+
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+				 struct sta_info *sta);
+
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta, u16 reason_code);
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+				       const u8 *addr);
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* MLME_H */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
new file mode 100644
index 0000000..7c87fde
--- /dev/null
+++ b/src/ap/authsrv.c
@@ -0,0 +1,217 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "eap_server/eap_sim_db.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius_server.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "authsrv.h"
+
+
+#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
+#define EAP_SIM_DB
+#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
+
+
+#ifdef EAP_SIM_DB
+static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
+				 struct sta_info *sta, void *ctx)
+{
+	if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
+		return 1;
+	return 0;
+}
+
+
+static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
+#ifdef RADIUS_SERVER
+		radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
+#endif /* RADIUS_SERVER */
+	}
+}
+#endif /* EAP_SIM_DB */
+
+
+#ifdef RADIUS_SERVER
+
+static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
+				       size_t identity_len, int phase2,
+				       struct eap_user *user)
+{
+	const struct hostapd_eap_user *eap_user;
+	int i, count;
+
+	eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
+	if (eap_user == NULL)
+		return -1;
+
+	if (user == NULL)
+		return 0;
+
+	os_memset(user, 0, sizeof(*user));
+	count = EAP_USER_MAX_METHODS;
+	if (count > EAP_MAX_METHODS)
+		count = EAP_MAX_METHODS;
+	for (i = 0; i < count; i++) {
+		user->methods[i].vendor = eap_user->methods[i].vendor;
+		user->methods[i].method = eap_user->methods[i].method;
+	}
+
+	if (eap_user->password) {
+		user->password = os_malloc(eap_user->password_len);
+		if (user->password == NULL)
+			return -1;
+		os_memcpy(user->password, eap_user->password,
+			  eap_user->password_len);
+		user->password_len = eap_user->password_len;
+		user->password_hash = eap_user->password_hash;
+	}
+	user->force_version = eap_user->force_version;
+	user->ttls_auth = eap_user->ttls_auth;
+
+	return 0;
+}
+
+
+static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
+{
+	struct radius_server_conf srv;
+	struct hostapd_bss_config *conf = hapd->conf;
+	os_memset(&srv, 0, sizeof(srv));
+	srv.client_file = conf->radius_server_clients;
+	srv.auth_port = conf->radius_server_auth_port;
+	srv.conf_ctx = conf;
+	srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
+	srv.ssl_ctx = hapd->ssl_ctx;
+	srv.msg_ctx = hapd->msg_ctx;
+	srv.pac_opaque_encr_key = conf->pac_opaque_encr_key;
+	srv.eap_fast_a_id = conf->eap_fast_a_id;
+	srv.eap_fast_a_id_len = conf->eap_fast_a_id_len;
+	srv.eap_fast_a_id_info = conf->eap_fast_a_id_info;
+	srv.eap_fast_prov = conf->eap_fast_prov;
+	srv.pac_key_lifetime = conf->pac_key_lifetime;
+	srv.pac_key_refresh_time = conf->pac_key_refresh_time;
+	srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+	srv.tnc = conf->tnc;
+	srv.wps = hapd->wps;
+	srv.ipv6 = conf->radius_server_ipv6;
+	srv.get_eap_user = hostapd_radius_get_eap_user;
+	srv.eap_req_id_text = conf->eap_req_id_text;
+	srv.eap_req_id_text_len = conf->eap_req_id_text_len;
+	srv.pwd_group = conf->pwd_group;
+
+	hapd->radius_srv = radius_server_init(&srv);
+	if (hapd->radius_srv == NULL) {
+		wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* RADIUS_SERVER */
+
+
+int authsrv_init(struct hostapd_data *hapd)
+{
+#ifdef EAP_TLS_FUNCS
+	if (hapd->conf->eap_server &&
+	    (hapd->conf->ca_cert || hapd->conf->server_cert ||
+	     hapd->conf->dh_file)) {
+		struct tls_connection_params params;
+
+		hapd->ssl_ctx = tls_init(NULL);
+		if (hapd->ssl_ctx == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize TLS");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+
+		os_memset(&params, 0, sizeof(params));
+		params.ca_cert = hapd->conf->ca_cert;
+		params.client_cert = hapd->conf->server_cert;
+		params.private_key = hapd->conf->private_key;
+		params.private_key_passwd = hapd->conf->private_key_passwd;
+		params.dh_file = hapd->conf->dh_file;
+
+		if (tls_global_set_params(hapd->ssl_ctx, &params)) {
+			wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+
+		if (tls_global_set_verify(hapd->ssl_ctx,
+					  hapd->conf->check_crl)) {
+			wpa_printf(MSG_ERROR, "Failed to enable check_crl");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+	if (hapd->conf->eap_sim_db) {
+		hapd->eap_sim_db_priv =
+			eap_sim_db_init(hapd->conf->eap_sim_db,
+					hostapd_sim_db_cb, hapd);
+		if (hapd->eap_sim_db_priv == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
+				   "database interface");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* EAP_SIM_DB */
+
+#ifdef RADIUS_SERVER
+	if (hapd->conf->radius_server_clients &&
+	    hostapd_setup_radius_srv(hapd))
+		return -1;
+#endif /* RADIUS_SERVER */
+
+	return 0;
+}
+
+
+void authsrv_deinit(struct hostapd_data *hapd)
+{
+#ifdef RADIUS_SERVER
+	radius_server_deinit(hapd->radius_srv);
+	hapd->radius_srv = NULL;
+#endif /* RADIUS_SERVER */
+
+#ifdef EAP_TLS_FUNCS
+	if (hapd->ssl_ctx) {
+		tls_deinit(hapd->ssl_ctx);
+		hapd->ssl_ctx = NULL;
+	}
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+	if (hapd->eap_sim_db_priv) {
+		eap_sim_db_deinit(hapd->eap_sim_db_priv);
+		hapd->eap_sim_db_priv = NULL;
+	}
+#endif /* EAP_SIM_DB */
+}
diff --git a/src/ap/authsrv.h b/src/ap/authsrv.h
new file mode 100644
index 0000000..be3051e
--- /dev/null
+++ b/src/ap/authsrv.h
@@ -0,0 +1,21 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 AUTHSRV_H
+#define AUTHSRV_H
+
+int authsrv_init(struct hostapd_data *hapd);
+void authsrv_deinit(struct hostapd_data *hapd);
+
+#endif /* AUTHSRV_H */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
new file mode 100644
index 0000000..5544925
--- /dev/null
+++ b/src/ap/beacon.c
@@ -0,0 +1,535 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "wps/wps_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+
+
+static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
+{
+	u8 erp = 0;
+
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return 0;
+
+	switch (hapd->iconf->cts_protection_type) {
+	case CTS_PROTECTION_FORCE_ENABLED:
+		erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION;
+		break;
+	case CTS_PROTECTION_FORCE_DISABLED:
+		erp = 0;
+		break;
+	case CTS_PROTECTION_AUTOMATIC:
+		if (hapd->iface->olbc)
+			erp |= ERP_INFO_USE_PROTECTION;
+		/* continue */
+	case CTS_PROTECTION_AUTOMATIC_NO_OLBC:
+		if (hapd->iface->num_sta_non_erp > 0) {
+			erp |= ERP_INFO_NON_ERP_PRESENT |
+				ERP_INFO_USE_PROTECTION;
+		}
+		break;
+	}
+	if (hapd->iface->num_sta_no_short_preamble > 0 ||
+	    hapd->iconf->preamble == LONG_PREAMBLE)
+		erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
+
+	return erp;
+}
+
+
+static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
+{
+	*eid++ = WLAN_EID_DS_PARAMS;
+	*eid++ = 1;
+	*eid++ = hapd->iconf->channel;
+	return eid;
+}
+
+
+static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
+{
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return eid;
+
+	/* Set NonERP_present and use_protection bits if there
+	 * are any associated NonERP stations. */
+	/* TODO: use_protection bit can be set to zero even if
+	 * there are NonERP stations present. This optimization
+	 * might be useful if NonERP stations are "quiet".
+	 * See 802.11g/D6 E-1 for recommended practice.
+	 * In addition, Non ERP present might be set, if AP detects Non ERP
+	 * operation on other APs. */
+
+	/* Add ERP Information element */
+	*eid++ = WLAN_EID_ERP_INFO;
+	*eid++ = 1;
+	*eid++ = ieee802_11_erp_info(hapd);
+
+	return eid;
+}
+
+
+static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
+				    struct hostapd_channel_data *start,
+				    struct hostapd_channel_data *prev)
+{
+	if (end - pos < 3)
+		return pos;
+
+	/* first channel number */
+	*pos++ = start->chan;
+	/* number of channels */
+	*pos++ = (prev->chan - start->chan) / chan_spacing + 1;
+	/* maximum transmit power level */
+	*pos++ = start->max_tx_power;
+
+	return pos;
+}
+
+
+static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
+				int max_len)
+{
+	u8 *pos = eid;
+	u8 *end = eid + max_len;
+	int i;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *start, *prev;
+	int chan_spacing = 1;
+
+	if (!hapd->iconf->ieee80211d || max_len < 6 ||
+	    hapd->iface->current_mode == NULL)
+		return eid;
+
+	*pos++ = WLAN_EID_COUNTRY;
+	pos++; /* length will be set later */
+	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
+	pos += 3;
+
+	mode = hapd->iface->current_mode;
+	if (mode->mode == HOSTAPD_MODE_IEEE80211A)
+		chan_spacing = 4;
+
+	start = prev = NULL;
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (start && prev &&
+		    prev->chan + chan_spacing == chan->chan &&
+		    start->max_tx_power == chan->max_tx_power) {
+			prev = chan;
+			continue; /* can use same entry */
+		}
+
+		if (start) {
+			pos = hostapd_eid_country_add(pos, end, chan_spacing,
+						      start, prev);
+			start = NULL;
+		}
+
+		/* Start new group */
+		start = prev = chan;
+	}
+
+	if (start) {
+		pos = hostapd_eid_country_add(pos, end, chan_spacing,
+					      start, prev);
+	}
+
+	if ((pos - eid) & 1) {
+		if (end - pos < 1)
+			return eid;
+		*pos++ = 0; /* pad for 16-bit alignment */
+	}
+
+	eid[1] = (pos - eid) - 2;
+
+	return pos;
+}
+
+
+static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len,
+			    struct sta_info *sta)
+{
+	const u8 *ie;
+	size_t ielen;
+
+	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+	if (ie == NULL || ielen > len)
+		return eid;
+
+	os_memcpy(eid, ie, ielen);
+	return eid + ielen;
+}
+
+
+void handle_probe_req(struct hostapd_data *hapd,
+		      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct ieee80211_mgmt *resp;
+	struct ieee802_11_elems elems;
+	char *ssid;
+	u8 *pos, *epos;
+	const u8 *ie;
+	size_t ssid_len, ie_len;
+	struct sta_info *sta = NULL;
+	size_t buflen;
+	size_t i;
+
+	ie = mgmt->u.probe_req.variable;
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+		return;
+	ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+
+	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
+		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+					    mgmt->sa, ie, ie_len) > 0)
+			return;
+
+	if (!hapd->iconf->send_probe_response)
+		return;
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+
+	ssid = NULL;
+	ssid_len = 0;
+
+	if ((!elems.ssid || !elems.supp_rates)) {
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
+			   "without SSID or supported rates element",
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+
+#ifdef CONFIG_P2P
+	if (hapd->p2p && elems.wps_ie) {
+		struct wpabuf *wps;
+		wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+		if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
+			wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+				   "due to mismatch with Requested Device "
+				   "Type");
+			wpabuf_free(wps);
+			return;
+		}
+		wpabuf_free(wps);
+	}
+#endif /* CONFIG_P2P */
+
+	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) {
+		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
+		return;
+	}
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+	    elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+	    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+		      P2P_WILDCARD_SSID_LEN) == 0) {
+		/* Process P2P Wildcard SSID like Wildcard SSID */
+		elems.ssid_len = 0;
+	}
+#endif /* CONFIG_P2P */
+
+	if (elems.ssid_len == 0 ||
+	    (elems.ssid_len == hapd->conf->ssid.ssid_len &&
+	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
+	     0)) {
+		ssid = hapd->conf->ssid.ssid;
+		ssid_len = hapd->conf->ssid.ssid_len;
+		if (sta)
+			sta->ssid_probe = &hapd->conf->ssid;
+	}
+
+	if (!ssid) {
+		if (!(mgmt->da[0] & 0x01)) {
+			char ssid_txt[33];
+			ieee802_11_print_ssid(ssid_txt, elems.ssid,
+					      elems.ssid_len);
+			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+				   " for foreign SSID '%s' (DA " MACSTR ")",
+				   MAC2STR(mgmt->sa), ssid_txt,
+				   MAC2STR(mgmt->da));
+		}
+		return;
+	}
+
+	/* TODO: verify that supp_rates contains at least one matching rate
+	 * with AP configuration */
+#define MAX_PROBERESP_LEN 768
+	buflen = MAX_PROBERESP_LEN;
+#ifdef CONFIG_WPS
+	if (hapd->wps_probe_resp_ie)
+		buflen += wpabuf_len(hapd->wps_probe_resp_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (hapd->p2p_probe_resp_ie)
+		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
+	resp = os_zalloc(buflen);
+	if (resp == NULL)
+		return;
+	epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
+
+	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_PROBE_RESP);
+	os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
+	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+
+	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+	resp->u.probe_resp.beacon_int =
+		host_to_le16(hapd->iconf->beacon_int);
+
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	resp->u.probe_resp.capab_info =
+		host_to_le16(hostapd_own_capab_info(hapd, sta, 1));
+
+	pos = resp->u.probe_resp.variable;
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = ssid_len;
+	os_memcpy(pos, ssid, ssid_len);
+	pos += ssid_len;
+
+	/* Supported rates */
+	pos = hostapd_eid_supp_rates(hapd, pos);
+
+	/* DS Params */
+	pos = hostapd_eid_ds_params(hapd, pos);
+
+	pos = hostapd_eid_country(hapd, pos, epos - pos);
+
+	/* ERP Information element */
+	pos = hostapd_eid_erp_info(hapd, pos);
+
+	/* Extended supported rates */
+	pos = hostapd_eid_ext_supp_rates(hapd, pos);
+
+	/* RSN, MDIE, WPA */
+	pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
+
+#ifdef CONFIG_IEEE80211N
+	pos = hostapd_eid_ht_capabilities(hapd, pos);
+	pos = hostapd_eid_ht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211N */
+
+	pos = hostapd_eid_ext_capab(hapd, pos);
+
+	/* Wi-Fi Alliance WMM */
+	pos = hostapd_eid_wmm(hapd, pos);
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
+			  wpabuf_len(hapd->wps_probe_resp_ie));
+		pos += wpabuf_len(hapd->wps_probe_resp_ie);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && elems.p2p &&
+	    hapd->p2p_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
+			  wpabuf_len(hapd->p2p_probe_resp_ie));
+		pos += wpabuf_len(hapd->p2p_probe_resp_ie);
+	}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    P2P_MANAGE)
+		pos = hostapd_eid_p2p_manage(hapd, pos);
+#endif /* CONFIG_P2P_MANAGER */
+
+	if (hostapd_drv_send_mlme(hapd, resp, pos - (u8 *) resp) < 0)
+		perror("handle_probe_req: send");
+
+	os_free(resp);
+
+	wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
+		   "SSID", MAC2STR(mgmt->sa),
+		   elems.ssid_len == 0 ? "broadcast" : "our");
+}
+
+
+void ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+	struct ieee80211_mgmt *head;
+	u8 *pos, *tail, *tailpos;
+	u16 capab_info;
+	size_t head_len, tail_len;
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) == P2P_ENABLED)
+		goto no_beacon;
+#endif /* CONFIG_P2P */
+
+#define BEACON_HEAD_BUF_SIZE 256
+#define BEACON_TAIL_BUF_SIZE 512
+	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
+	tail_len = BEACON_TAIL_BUF_SIZE;
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_beacon_ie)
+		tail_len += wpabuf_len(hapd->wps_beacon_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (hapd->p2p_beacon_ie)
+		tail_len += wpabuf_len(hapd->p2p_beacon_ie);
+#endif /* CONFIG_P2P */
+	tailpos = tail = os_malloc(tail_len);
+	if (head == NULL || tail == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to set beacon data");
+		os_free(head);
+		os_free(tail);
+		return;
+	}
+
+	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_BEACON);
+	head->duration = host_to_le16(0);
+	os_memset(head->da, 0xff, ETH_ALEN);
+
+	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+	head->u.beacon.beacon_int =
+		host_to_le16(hapd->iconf->beacon_int);
+
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	capab_info = hostapd_own_capab_info(hapd, NULL, 0);
+	head->u.beacon.capab_info = host_to_le16(capab_info);
+	pos = &head->u.beacon.variable[0];
+
+	/* SSID */
+	*pos++ = WLAN_EID_SSID;
+	if (hapd->conf->ignore_broadcast_ssid == 2) {
+		/* clear the data, but keep the correct length of the SSID */
+		*pos++ = hapd->conf->ssid.ssid_len;
+		os_memset(pos, 0, hapd->conf->ssid.ssid_len);
+		pos += hapd->conf->ssid.ssid_len;
+	} else if (hapd->conf->ignore_broadcast_ssid) {
+		*pos++ = 0; /* empty SSID */
+	} else {
+		*pos++ = hapd->conf->ssid.ssid_len;
+		os_memcpy(pos, hapd->conf->ssid.ssid,
+			  hapd->conf->ssid.ssid_len);
+		pos += hapd->conf->ssid.ssid_len;
+	}
+
+	/* Supported rates */
+	pos = hostapd_eid_supp_rates(hapd, pos);
+
+	/* DS Params */
+	pos = hostapd_eid_ds_params(hapd, pos);
+
+	head_len = pos - (u8 *) head;
+
+	tailpos = hostapd_eid_country(hapd, tailpos,
+				      tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
+	/* ERP Information element */
+	tailpos = hostapd_eid_erp_info(hapd, tailpos);
+
+	/* Extended supported rates */
+	tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
+
+	/* RSN, MDIE, WPA */
+	tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
+				  tailpos, NULL);
+
+#ifdef CONFIG_IEEE80211N
+	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
+	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+#endif /* CONFIG_IEEE80211N */
+
+	tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+
+	/* Wi-Fi Alliance WMM */
+	tailpos = hostapd_eid_wmm(hapd, tailpos);
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
+		os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
+			  wpabuf_len(hapd->wps_beacon_ie));
+		tailpos += wpabuf_len(hapd->wps_beacon_ie);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
+		os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
+			  wpabuf_len(hapd->p2p_beacon_ie));
+		tailpos += wpabuf_len(hapd->p2p_beacon_ie);
+	}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    P2P_MANAGE)
+		tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
+#endif /* CONFIG_P2P_MANAGER */
+
+	tail_len = tailpos > tail ? tailpos - tail : 0;
+
+	if (hostapd_drv_set_beacon(hapd, (u8 *) head, head_len,
+				   tail, tail_len, hapd->conf->dtim_period,
+				   hapd->iconf->beacon_int))
+		wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM "
+			   "period");
+
+	os_free(tail);
+	os_free(head);
+
+#ifdef CONFIG_P2P
+no_beacon:
+#endif /* CONFIG_P2P */
+	hostapd_set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) &
+					ERP_INFO_USE_PROTECTION));
+}
+
+
+void ieee802_11_set_beacons(struct hostapd_iface *iface)
+{
+	size_t i;
+	for (i = 0; i < iface->num_bss; i++)
+		ieee802_11_set_beacon(iface->bss[i]);
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
new file mode 100644
index 0000000..c1510e1
--- /dev/null
+++ b/src/ap/beacon.h
@@ -0,0 +1,36 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 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 BEACON_H
+#define BEACON_H
+
+struct ieee80211_mgmt;
+
+void handle_probe_req(struct hostapd_data *hapd,
+		      const struct ieee80211_mgmt *mgmt, size_t len);
+#ifdef NEED_AP_MLME
+void ieee802_11_set_beacon(struct hostapd_data *hapd);
+void ieee802_11_set_beacons(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline void ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+}
+
+static inline void ieee802_11_set_beacons(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
new file mode 100644
index 0000000..d348dc1
--- /dev/null
+++ b/src/ap/ctrl_iface_ap.c
@@ -0,0 +1,108 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+#include "p2p_hostapd.h"
+#include "ctrl_iface_ap.h"
+
+
+static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      char *buf, size_t buflen)
+{
+	int len, res, ret;
+
+	if (sta == NULL) {
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (ret < 0 || (size_t) ret >= buflen)
+			return 0;
+		return ret;
+	}
+
+	len = 0;
+	ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
+			  MAC2STR(sta->addr));
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
+				      buflen - len);
+	if (res >= 0)
+		len += res;
+	res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+
+	return len;
+}
+
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+				 char *buf, size_t buflen)
+{
+	return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
+}
+
+
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+			   char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	int ret;
+
+	if (hwaddr_aton(txtaddr, addr)) {
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (ret < 0 || (size_t) ret >= buflen)
+			return 0;
+		return ret;
+	}
+	return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
+					  buf, buflen);
+}
+
+
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	int ret;
+
+	if (hwaddr_aton(txtaddr, addr) ||
+	    (sta = ap_get_sta(hapd, addr)) == NULL) {
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (ret < 0 || (size_t) ret >= buflen)
+			return 0;
+		return ret;
+	}		
+	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
new file mode 100644
index 0000000..8690bea
--- /dev/null
+++ b/src/ap/ctrl_iface_ap.h
@@ -0,0 +1,25 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 CTRL_IFACE_AP_H
+#define CTRL_IFACE_AP_H
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+				 char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+			   char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen);
+
+#endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
new file mode 100644
index 0000000..02b7ecf
--- /dev/null
+++ b/src/ap/drv_callbacks.c
@@ -0,0 +1,539 @@
+/*
+ * hostapd / Callback functions for driver wrappers
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "radius/radius.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "tkip_countermeasures.h"
+#include "iapp.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "wps_hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+
+
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+			const u8 *ie, size_t ielen, int reassoc)
+{
+	struct sta_info *sta;
+	int new_assoc, res;
+	struct ieee802_11_elems elems;
+#ifdef CONFIG_P2P
+	const u8 *all_ies = ie;
+	size_t all_ies_len = ielen;
+#endif /* CONFIG_P2P */
+
+	if (addr == NULL) {
+		/*
+		 * This could potentially happen with unexpected event from the
+		 * driver wrapper. This was seen at least in one case where the
+		 * driver ended up being set to station mode while hostapd was
+		 * running, so better make sure we stop processing such an
+		 * event here.
+		 */
+		wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with "
+			   "no address");
+		return -1;
+	}
+	random_add_randomness(addr, ETH_ALEN);
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "associated");
+
+	ieee802_11_parse_elems(ie, ielen, &elems, 0);
+	if (elems.wps_ie) {
+		ie = elems.wps_ie - 2;
+		ielen = elems.wps_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
+	} else if (elems.rsn_ie) {
+		ie = elems.rsn_ie - 2;
+		ielen = elems.rsn_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
+	} else if (elems.wpa_ie) {
+		ie = elems.wpa_ie - 2;
+		ielen = elems.wpa_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+	} else {
+		ie = NULL;
+		ielen = 0;
+		wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in "
+			   "(Re)AssocReq");
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta) {
+		accounting_sta_stop(hapd, sta);
+	} else {
+		sta = ap_sta_add(hapd, addr);
+		if (sta == NULL)
+			return -1;
+	}
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+
+#ifdef CONFIG_P2P
+	if (elems.p2p) {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = ieee802_11_vendor_ie_concat(all_ies, all_ies_len,
+							  P2P_IE_VENDOR_TYPE);
+	}
+#endif /* CONFIG_P2P */
+
+	if (hapd->conf->wpa) {
+		if (ie == NULL || ielen == 0) {
+			if (hapd->conf->wps_state) {
+				wpa_printf(MSG_DEBUG, "STA did not include "
+					   "WPA/RSN IE in (Re)Association "
+					   "Request - possible WPS use");
+				sta->flags |= WLAN_STA_MAYBE_WPS;
+				goto skip_wpa_check;
+			}
+
+			wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
+			return -1;
+		}
+		if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
+		    os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+			sta->flags |= WLAN_STA_WPS;
+			goto skip_wpa_check;
+		}
+
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
+				   "machine");
+			return -1;
+		}
+		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+					  ie, ielen, NULL, 0);
+		if (res != WPA_IE_OK) {
+			int resp;
+			wpa_printf(MSG_DEBUG, "WPA/RSN information element "
+				   "rejected? (res %u)", res);
+			wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
+			if (res == WPA_INVALID_GROUP)
+				resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+			else if (res == WPA_INVALID_PAIRWISE)
+				resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+			else if (res == WPA_INVALID_AKMP)
+				resp = WLAN_REASON_AKMP_NOT_VALID;
+#ifdef CONFIG_IEEE80211W
+			else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+				resp = WLAN_REASON_INVALID_IE;
+			else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+				resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+#endif /* CONFIG_IEEE80211W */
+			else
+				resp = WLAN_REASON_INVALID_IE;
+			hostapd_drv_sta_disassoc(hapd, sta->addr, resp);
+			ap_free_sta(hapd, sta);
+			return -1;
+		}
+	} else if (hapd->conf->wps_state) {
+#ifdef CONFIG_WPS_STRICT
+		if (ie) {
+			struct wpabuf *wps;
+			wps = ieee802_11_vendor_ie_concat(ie, ielen,
+							  WPS_IE_VENDOR_TYPE);
+			if (wps && wps_validate_assoc_req(wps) < 0) {
+				hostapd_drv_sta_disassoc(
+					hapd, sta->addr,
+					WLAN_REASON_INVALID_IE);
+				ap_free_sta(hapd, sta);
+				wpabuf_free(wps);
+				return -1;
+			}
+			wpabuf_free(wps);
+		}
+#endif /* CONFIG_WPS_STRICT */
+		if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
+		    os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+			sta->flags |= WLAN_STA_WPS;
+		} else
+			sta->flags |= WLAN_STA_MAYBE_WPS;
+	}
+skip_wpa_check:
+
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+	hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+#ifdef CONFIG_P2P
+	p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
+			      all_ies, all_ies_len);
+#endif /* CONFIG_P2P */
+
+	return 0;
+}
+
+
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	if (addr == NULL) {
+		/*
+		 * This could potentially happen with unexpected event from the
+		 * driver wrapper. This was seen at least in one case where the
+		 * driver ended up reporting a station mode event while hostapd
+		 * was running, so better make sure we stop processing such an
+		 * event here.
+		 */
+		wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event "
+			   "with no address");
+		return;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "disassociated");
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Disassociation notification for "
+			   "unknown STA " MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+		MAC2STR(sta->addr));
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	ap_free_sta(hapd, sta);
+}
+
+
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (!sta || !hapd->conf->disassoc_low_ack)
+		return;
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
+		       "missing ACKs");
+	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+	if (sta)
+		ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
+
+
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
+			 const u8 *ie, size_t ie_len)
+{
+	size_t i;
+	int ret = 0;
+
+	if (sa == NULL || ie == NULL)
+		return -1;
+
+	random_add_randomness(sa, ETH_ALEN);
+	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
+		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+					    sa, ie, ie_len) > 0) {
+			ret = 1;
+			break;
+		}
+	}
+	return ret;
+}
+
+
+#ifdef HOSTAPD
+
+#ifdef NEED_AP_MLME
+
+static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+{
+	u16 fc, type, stype;
+
+	/*
+	 * PS-Poll frames are 16 bytes. All other frames are
+	 * 24 bytes or longer.
+	 */
+	if (len < 16)
+		return NULL;
+
+	fc = le_to_host16(hdr->frame_control);
+	type = WLAN_FC_GET_TYPE(fc);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	switch (type) {
+	case WLAN_FC_TYPE_DATA:
+		if (len < 24)
+			return NULL;
+		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+		case WLAN_FC_FROMDS | WLAN_FC_TODS:
+		case WLAN_FC_TODS:
+			return hdr->addr1;
+		case WLAN_FC_FROMDS:
+			return hdr->addr2;
+		default:
+			return NULL;
+		}
+	case WLAN_FC_TYPE_CTRL:
+		if (stype != WLAN_FC_STYPE_PSPOLL)
+			return NULL;
+		return hdr->addr1;
+	case WLAN_FC_TYPE_MGMT:
+		return hdr->addr3;
+	default:
+		return NULL;
+	}
+}
+
+
+#define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+					    const u8 *bssid)
+{
+	size_t i;
+
+	if (bssid == NULL)
+		return NULL;
+	if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
+	    bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
+		return HAPD_BROADCAST;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+			return iface->bss[i];
+	}
+
+	return NULL;
+}
+
+
+static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+					const u8 *frame, size_t len)
+{
+	const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
+	u16 fc = le_to_host16(hdr->frame_control);
+	hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+	if (hapd == NULL || hapd == HAPD_BROADCAST)
+		return;
+
+	ieee802_11_rx_from_unknown(hapd, hdr->addr2,
+				   (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+				   (WLAN_FC_TODS | WLAN_FC_FROMDS));
+}
+
+
+static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	const struct ieee80211_hdr *hdr;
+	const u8 *bssid;
+	struct hostapd_frame_info fi;
+
+	hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
+	bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
+	if (bssid == NULL)
+		return;
+
+	hapd = get_hapd_bssid(iface, bssid);
+	if (hapd == NULL) {
+		u16 fc;
+		fc = le_to_host16(hdr->frame_control);
+
+		/*
+		 * Drop frames to unknown BSSIDs except for Beacon frames which
+		 * could be used to update neighbor information.
+		 */
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+			hapd = iface->bss[0];
+		else
+			return;
+	}
+
+	os_memset(&fi, 0, sizeof(fi));
+	fi.datarate = rx_mgmt->datarate;
+	fi.ssi_signal = rx_mgmt->ssi_signal;
+
+	if (hapd == HAPD_BROADCAST) {
+		size_t i;
+		for (i = 0; i < iface->num_bss; i++)
+			ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+					rx_mgmt->frame_len, &fi);
+	} else
+		ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+
+	random_add_randomness(&fi, sizeof(fi));
+}
+
+
+static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+			       size_t len, u16 stype, int ok)
+{
+	struct ieee80211_hdr *hdr;
+	hdr = (struct ieee80211_hdr *) buf;
+	hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+	if (hapd == NULL || hapd == HAPD_BROADCAST)
+		return;
+	ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
+		   " - adding a new STA", MAC2STR(addr));
+	sta = ap_sta_add(hapd, addr);
+	if (sta) {
+		hostapd_new_assoc_sta(hapd, sta, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+				   const u8 *data, size_t data_len)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		if (ap_get_sta(iface->bss[j], src)) {
+			hapd = iface->bss[j];
+			break;
+		}
+	}
+
+	ieee802_1x_receive(hapd, src, data, data_len);
+}
+
+
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+			  union wpa_event_data *data)
+{
+	struct hostapd_data *hapd = ctx;
+
+	switch (event) {
+	case EVENT_MICHAEL_MIC_FAILURE:
+		michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
+		break;
+	case EVENT_SCAN_RESULTS:
+		if (hapd->iface->scan_cb)
+			hapd->iface->scan_cb(hapd->iface);
+		break;
+#ifdef CONFIG_IEEE80211R
+	case EVENT_FT_RRB_RX:
+		wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src,
+			      data->ft_rrb_rx.data, data->ft_rrb_rx.data_len);
+		break;
+#endif /* CONFIG_IEEE80211R */
+	case EVENT_WPS_BUTTON_PUSHED:
+		hostapd_wps_button_pushed(hapd, NULL);
+		break;
+#ifdef NEED_AP_MLME
+	case EVENT_TX_STATUS:
+		switch (data->tx_status.type) {
+		case WLAN_FC_TYPE_MGMT:
+			hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
+					   data->tx_status.data_len,
+					   data->tx_status.stype,
+					   data->tx_status.ack);
+			break;
+		case WLAN_FC_TYPE_DATA:
+			hostapd_tx_status(hapd, data->tx_status.dst,
+					  data->tx_status.data,
+					  data->tx_status.data_len,
+					  data->tx_status.ack);
+			break;
+		}
+		break;
+	case EVENT_RX_FROM_UNKNOWN:
+		hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame,
+					    data->rx_from_unknown.len);
+		break;
+	case EVENT_RX_MGMT:
+		hostapd_mgmt_rx(hapd, &data->rx_mgmt);
+		break;
+#endif /* NEED_AP_MLME */
+	case EVENT_RX_PROBE_REQ:
+		if (data->rx_probe_req.sa == NULL ||
+		    data->rx_probe_req.ie == NULL)
+			break;
+		hostapd_probe_req_rx(hapd, data->rx_probe_req.sa,
+				     data->rx_probe_req.ie,
+				     data->rx_probe_req.ie_len);
+		break;
+	case EVENT_NEW_STA:
+		hostapd_event_new_sta(hapd, data->new_sta.addr);
+		break;
+	case EVENT_EAPOL_RX:
+		hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
+				       data->eapol_rx.data,
+				       data->eapol_rx.data_len);
+		break;
+	case EVENT_ASSOC:
+		hostapd_notif_assoc(hapd, data->assoc_info.addr,
+				    data->assoc_info.req_ies,
+				    data->assoc_info.req_ies_len,
+				    data->assoc_info.reassoc);
+		break;
+	case EVENT_DISASSOC:
+		if (data)
+			hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
+		break;
+	case EVENT_DEAUTH:
+		if (data)
+			hostapd_notif_disassoc(hapd, data->deauth_info.addr);
+		break;
+	case EVENT_STATION_LOW_ACK:
+		if (!data)
+			break;
+		hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+		break;
+	}
+}
+
+#endif /* HOSTAPD */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
new file mode 100644
index 0000000..4e5eb01
--- /dev/null
+++ b/src/ap/hostapd.c
@@ -0,0 +1,929 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius_client.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "authsrv.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "ap_list.h"
+#include "beacon.h"
+#include "iapp.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+#include "vlan_init.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "hw_features.h"
+#include "wpa_auth_glue.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+
+extern int wpa_debug_level;
+
+
+static void hostapd_reload_bss(struct hostapd_data *hapd)
+{
+#ifndef CONFIG_NO_RADIUS
+	radius_client_reconfig(hapd->radius, hapd->conf->radius);
+#endif /* CONFIG_NO_RADIUS */
+
+	if (hostapd_setup_wpa_psk(hapd->conf)) {
+		wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
+			   "after reloading configuration");
+	}
+
+	if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+	else
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+	if (hapd->conf->wpa && hapd->wpa_auth == NULL)
+		hostapd_setup_wpa(hapd);
+	else if (hapd->conf->wpa) {
+		const u8 *wpa_ie;
+		size_t wpa_ie_len;
+		hostapd_reconfig_wpa(hapd);
+		wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+		if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len))
+			wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+				   "the kernel driver.");
+	} else if (hapd->wpa_auth) {
+		wpa_deinit(hapd->wpa_auth);
+		hapd->wpa_auth = NULL;
+		hostapd_set_privacy(hapd, 0);
+		hostapd_setup_encryption(hapd->conf->iface, hapd);
+		hostapd_set_generic_elem(hapd, (u8 *) "", 0);
+	}
+
+	ieee802_11_set_beacon(hapd);
+	hostapd_update_wps(hapd);
+
+	if (hapd->conf->ssid.ssid_set &&
+	    hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid,
+			     hapd->conf->ssid.ssid_len)) {
+		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+		/* try to continue */
+	}
+	wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	struct hostapd_config *newconf, *oldconf;
+	size_t j;
+
+	if (iface->config_read_cb == NULL)
+		return -1;
+	newconf = iface->config_read_cb(iface->config_fname);
+	if (newconf == NULL)
+		return -1;
+
+	/*
+	 * Deauthenticate all stations since the new configuration may not
+	 * allow them to use the BSS anymore.
+	 */
+	for (j = 0; j < iface->num_bss; j++) {
+		hostapd_flush_old_stations(iface->bss[j]);
+
+#ifndef CONFIG_NO_RADIUS
+		/* TODO: update dynamic data based on changed configuration
+		 * items (e.g., open/close sockets, etc.) */
+		radius_client_flush(iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+	}
+
+	oldconf = hapd->iconf;
+	iface->conf = newconf;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		hapd->iconf = newconf;
+		hapd->conf = &newconf->bss[j];
+		hostapd_reload_bss(hapd);
+	}
+
+	hostapd_config_free(oldconf);
+
+
+	return 0;
+}
+
+
+static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
+					      char *ifname)
+{
+	int i;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
+					0, NULL, 0, NULL, 0)) {
+			wpa_printf(MSG_DEBUG, "Failed to clear default "
+				   "encryption keys (ifname=%s keyidx=%d)",
+				   ifname, i);
+		}
+	}
+#ifdef CONFIG_IEEE80211W
+	if (hapd->conf->ieee80211w) {
+		for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
+			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
+						NULL, i, 0, NULL,
+						0, NULL, 0)) {
+				wpa_printf(MSG_DEBUG, "Failed to clear "
+					   "default mgmt encryption keys "
+					   "(ifname=%s keyidx=%d)", ifname, i);
+			}
+		}
+	}
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd)
+{
+	hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface);
+	return 0;
+}
+
+
+static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
+{
+	int errors = 0, idx;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	idx = ssid->wep.idx;
+	if (ssid->wep.default_len &&
+	    hostapd_drv_set_key(hapd->conf->iface,
+				hapd, WPA_ALG_WEP, broadcast_ether_addr, idx,
+				1, NULL, 0, ssid->wep.key[idx],
+				ssid->wep.len[idx])) {
+		wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
+		errors++;
+	}
+
+	if (ssid->dyn_vlan_keys) {
+		size_t i;
+		for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
+			const char *ifname;
+			struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i];
+			if (key == NULL)
+				continue;
+			ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+							    i);
+			if (ifname == NULL)
+				continue;
+
+			idx = key->idx;
+			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+						broadcast_ether_addr, idx, 1,
+						NULL, 0, key->key[idx],
+						key->len[idx])) {
+				wpa_printf(MSG_WARNING, "Could not set "
+					   "dynamic VLAN WEP encryption.");
+				errors++;
+			}
+		}
+	}
+
+	return errors;
+}
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * This gets called in a loop for each BSS between calls to
+ * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
+ * is deinitialized. Most of the modules that are initialized in
+ * hostapd_setup_bss() are deinitialized here.
+ */
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+	if (hapd->iface->ctrl_iface_deinit)
+		hapd->iface->ctrl_iface_deinit(hapd);
+
+	iapp_deinit(hapd->iapp);
+	hapd->iapp = NULL;
+	accounting_deinit(hapd);
+	hostapd_deinit_wpa(hapd);
+	vlan_deinit(hapd);
+	hostapd_acl_deinit(hapd);
+#ifndef CONFIG_NO_RADIUS
+	radius_client_deinit(hapd->radius);
+	hapd->radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+	hostapd_deinit_wps(hapd);
+
+	authsrv_deinit(hapd);
+
+	if (hapd->interface_added &&
+	    hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+		wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+			   hapd->conf->iface);
+	}
+
+	os_free(hapd->probereq_cb);
+	hapd->probereq_cb = NULL;
+
+#ifdef CONFIG_P2P
+	wpabuf_free(hapd->p2p_beacon_ie);
+	hapd->p2p_beacon_ie = NULL;
+	wpabuf_free(hapd->p2p_probe_resp_ie);
+	hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+}
+
+
+/**
+ * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup
+ * @iface: Pointer to interface data
+ *
+ * This function is called before per-BSS data structures are deinitialized
+ * with hostapd_cleanup().
+ */
+static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
+{
+}
+
+
+/**
+ * hostapd_cleanup_iface - Complete per-interface cleanup
+ * @iface: Pointer to interface data
+ *
+ * This function is called after per-BSS data structures are deinitialized
+ * with hostapd_cleanup().
+ */
+static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+{
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = NULL;
+	os_free(iface->current_rates);
+	iface->current_rates = NULL;
+	ap_list_deinit(iface);
+	hostapd_config_free(iface->conf);
+	iface->conf = NULL;
+
+	os_free(iface->config_fname);
+	os_free(iface->bss);
+	os_free(iface);
+}
+
+
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
+{
+	int i;
+
+	hostapd_broadcast_wep_set(hapd);
+
+	if (hapd->conf->ssid.wep.default_len) {
+		hostapd_set_privacy(hapd, 1);
+		return 0;
+	}
+
+	for (i = 0; i < 4; i++) {
+		if (hapd->conf->ssid.wep.key[i] &&
+		    hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
+					i == hapd->conf->ssid.wep.idx, NULL, 0,
+					hapd->conf->ssid.wep.key[i],
+					hapd->conf->ssid.wep.len[i])) {
+			wpa_printf(MSG_WARNING, "Could not set WEP "
+				   "encryption.");
+			return -1;
+		}
+		if (hapd->conf->ssid.wep.key[i] &&
+		    i == hapd->conf->ssid.wep.idx)
+			hostapd_set_privacy(hapd, 1);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd)
+{
+	int ret = 0;
+	u8 addr[ETH_ALEN];
+
+	if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "Flushing old station entries");
+	if (hostapd_flush(hapd)) {
+		wpa_printf(MSG_WARNING, "Could not connect to kernel driver.");
+		ret = -1;
+	}
+	wpa_printf(MSG_DEBUG, "Deauthenticate all stations");
+	os_memset(addr, 0xff, ETH_ALEN);
+	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_free_stas(hapd);
+
+	return ret;
+}
+
+
+/**
+ * hostapd_validate_bssid_configuration - Validate BSSID configuration
+ * @iface: Pointer to interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to validate that the configured BSSIDs are valid.
+ */
+static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
+{
+	u8 mask[ETH_ALEN] = { 0 };
+	struct hostapd_data *hapd = iface->bss[0];
+	unsigned int i = iface->conf->num_bss, bits = 0, j;
+	int res;
+	int auto_addr = 0;
+
+	if (hostapd_drv_none(hapd))
+		return 0;
+
+	/* Generate BSSID mask that is large enough to cover the BSSIDs. */
+
+	/* Determine the bits necessary to cover the number of BSSIDs. */
+	for (i--; i; i >>= 1)
+		bits++;
+
+	/* Determine the bits necessary to any configured BSSIDs,
+	   if they are higher than the number of BSSIDs. */
+	for (j = 0; j < iface->conf->num_bss; j++) {
+		if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) {
+			if (j)
+				auto_addr++;
+			continue;
+		}
+
+		for (i = 0; i < ETH_ALEN; i++) {
+			mask[i] |=
+				iface->conf->bss[j].bssid[i] ^
+				hapd->own_addr[i];
+		}
+	}
+
+	if (!auto_addr)
+		goto skip_mask_ext;
+
+	for (i = 0; i < ETH_ALEN && mask[i] == 0; i++)
+		;
+	j = 0;
+	if (i < ETH_ALEN) {
+		j = (5 - i) * 8;
+
+		while (mask[i] != 0) {
+			mask[i] >>= 1;
+			j++;
+		}
+	}
+
+	if (bits < j)
+		bits = j;
+
+	if (bits > 40) {
+		wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
+			   bits);
+		return -1;
+	}
+
+	os_memset(mask, 0xff, ETH_ALEN);
+	j = bits / 8;
+	for (i = 5; i > 5 - j; i--)
+		mask[i] = 0;
+	j = bits % 8;
+	while (j--)
+		mask[i] <<= 1;
+
+skip_mask_ext:
+	wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
+		   (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
+
+	res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask);
+	if (res == 0)
+		return 0;
+
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask "
+			   MACSTR " for start address " MACSTR ".",
+			   MAC2STR(mask), MAC2STR(hapd->own_addr));
+		return -1;
+	}
+
+	if (!auto_addr)
+		return 0;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) {
+			wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR
+				   " for start address " MACSTR ".",
+				   MAC2STR(mask), MAC2STR(hapd->own_addr));
+			wpa_printf(MSG_ERROR, "Start address must be the "
+				   "first address in the block (i.e., addr "
+				   "AND mask == addr).");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int mac_in_conf(struct hostapd_config *conf, const void *a)
+{
+	size_t i;
+
+	for (i = 0; i < conf->num_bss; i++) {
+		if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+
+
+/**
+ * hostapd_setup_bss - Per-BSS setup (initialization)
+ * @hapd: Pointer to BSS data
+ * @first: Whether this BSS is the first BSS of an interface
+ *
+ * This function is used to initialize all per-BSS data structures and
+ * resources. This gets called in a loop for each BSS when an interface is
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	u8 ssid[HOSTAPD_MAX_SSID_LEN + 1];
+	int ssid_len, set_ssid;
+	char force_ifname[IFNAMSIZ];
+	u8 if_addr[ETH_ALEN];
+
+	if (!first) {
+		if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) {
+			/* Allocate the next available BSSID. */
+			do {
+				inc_byte_array(hapd->own_addr, ETH_ALEN);
+			} while (mac_in_conf(hapd->iconf, hapd->own_addr));
+		} else {
+			/* Allocate the configured BSSID. */
+			os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN);
+
+			if (hostapd_mac_comp(hapd->own_addr,
+					     hapd->iface->bss[0]->own_addr) ==
+			    0) {
+				wpa_printf(MSG_ERROR, "BSS '%s' may not have "
+					   "BSSID set to the MAC address of "
+					   "the radio", hapd->conf->iface);
+				return -1;
+			}
+		}
+
+		hapd->interface_added = 1;
+		if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+				   hapd->conf->iface, hapd->own_addr, hapd,
+				   &hapd->drv_priv, force_ifname, if_addr,
+				   hapd->conf->bridge[0] ? hapd->conf->bridge :
+				   NULL)) {
+			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+				   MACSTR ")", MAC2STR(hapd->own_addr));
+			return -1;
+		}
+	}
+
+	if (conf->wmm_enabled < 0)
+		conf->wmm_enabled = hapd->iconf->ieee80211n;
+
+	hostapd_flush_old_stations(hapd);
+	hostapd_set_privacy(hapd, 0);
+
+	hostapd_broadcast_wep_clear(hapd);
+	if (hostapd_setup_encryption(hapd->conf->iface, hapd))
+		return -1;
+
+	/*
+	 * Fetch the SSID from the system and use it or,
+	 * if one was specified in the config file, verify they
+	 * match.
+	 */
+	ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
+	if (ssid_len < 0) {
+		wpa_printf(MSG_ERROR, "Could not read SSID from system");
+		return -1;
+	}
+	if (conf->ssid.ssid_set) {
+		/*
+		 * If SSID is specified in the config file and it differs
+		 * from what is being used then force installation of the
+		 * new SSID.
+		 */
+		set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len ||
+			    os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0);
+	} else {
+		/*
+		 * No SSID in the config file; just use the one we got
+		 * from the system.
+		 */
+		set_ssid = 0;
+		conf->ssid.ssid_len = ssid_len;
+		os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
+		conf->ssid.ssid[conf->ssid.ssid_len] = '\0';
+	}
+
+	if (!hostapd_drv_none(hapd)) {
+		wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
+			   " and ssid '%s'",
+			   hapd->conf->iface, MAC2STR(hapd->own_addr),
+			   hapd->conf->ssid.ssid);
+	}
+
+	if (hostapd_setup_wpa_psk(conf)) {
+		wpa_printf(MSG_ERROR, "WPA-PSK setup failed.");
+		return -1;
+	}
+
+	/* Set SSID for the kernel driver (to be used in beacon and probe
+	 * response frames) */
+	if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid,
+					 conf->ssid.ssid_len)) {
+		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+		return -1;
+	}
+
+	if (wpa_debug_level == MSG_MSGDUMP)
+		conf->radius->msg_dumps = 1;
+#ifndef CONFIG_NO_RADIUS
+	hapd->radius = radius_client_init(hapd, conf->radius);
+	if (hapd->radius == NULL) {
+		wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
+		return -1;
+	}
+#endif /* CONFIG_NO_RADIUS */
+
+	if (hostapd_acl_init(hapd)) {
+		wpa_printf(MSG_ERROR, "ACL initialization failed.");
+		return -1;
+	}
+	if (hostapd_init_wps(hapd, conf))
+		return -1;
+
+	if (authsrv_init(hapd) < 0)
+		return -1;
+
+	if (ieee802_1x_init(hapd)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
+		return -1;
+	}
+
+	if (hapd->conf->wpa && hostapd_setup_wpa(hapd))
+		return -1;
+
+	if (accounting_init(hapd)) {
+		wpa_printf(MSG_ERROR, "Accounting initialization failed.");
+		return -1;
+	}
+
+	if (hapd->conf->ieee802_11f &&
+	    (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) {
+		wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization "
+			   "failed.");
+		return -1;
+	}
+
+	if (hapd->iface->ctrl_iface_init &&
+	    hapd->iface->ctrl_iface_init(hapd)) {
+		wpa_printf(MSG_ERROR, "Failed to setup control interface");
+		return -1;
+	}
+
+	if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
+		wpa_printf(MSG_ERROR, "VLAN initialization failed.");
+		return -1;
+	}
+
+	ieee802_11_set_beacon(hapd);
+
+	if (hapd->driver && hapd->driver->set_operstate)
+		hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+	return 0;
+}
+
+
+static void hostapd_tx_queue_params(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	int i;
+	struct hostapd_tx_queue_params *p;
+
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		p = &iface->conf->tx_queue[i];
+
+		if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
+						p->cwmax, p->burst)) {
+			wpa_printf(MSG_DEBUG, "Failed to set TX queue "
+				   "parameters for queue %d.", i);
+			/* Continue anyway */
+		}
+	}
+}
+
+
+static int setup_interface(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	size_t i;
+	char country[4];
+
+	/*
+	 * Make sure that all BSSes get configured with a pointer to the same
+	 * driver interface.
+	 */
+	for (i = 1; i < iface->num_bss; i++) {
+		iface->bss[i]->driver = hapd->driver;
+		iface->bss[i]->drv_priv = hapd->drv_priv;
+	}
+
+	if (hostapd_validate_bssid_configuration(iface))
+		return -1;
+
+	if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
+		os_memcpy(country, hapd->iconf->country, 3);
+		country[3] = '\0';
+		if (hostapd_set_country(hapd, country) < 0) {
+			wpa_printf(MSG_ERROR, "Failed to set country code");
+			return -1;
+		}
+	}
+
+	if (hostapd_get_hw_features(iface)) {
+		/* Not all drivers support this yet, so continue without hw
+		 * feature data. */
+	} else {
+		int ret = hostapd_select_hw_mode(iface);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "Could not select hw_mode and "
+				   "channel. (%d)", ret);
+			return -1;
+		}
+		ret = hostapd_check_ht_capab(iface);
+		if (ret < 0)
+			return -1;
+		if (ret == 1) {
+			wpa_printf(MSG_DEBUG, "Interface initialization will "
+				   "be completed in a callback");
+			return 0;
+		}
+	}
+	return hostapd_setup_interface_complete(iface, 0);
+}
+
+
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	size_t j;
+	u8 *prev_addr;
+
+	if (err) {
+		wpa_printf(MSG_ERROR, "Interface initialization failed");
+		eloop_terminate();
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Completing interface initialization");
+	if (hapd->iconf->channel) {
+		iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
+		wpa_printf(MSG_DEBUG, "Mode: %s  Channel: %d  "
+			   "Frequency: %d MHz",
+			   hostapd_hw_mode_txt(hapd->iconf->hw_mode),
+			   hapd->iconf->channel, iface->freq);
+
+		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+				     hapd->iconf->channel,
+				     hapd->iconf->ieee80211n,
+				     hapd->iconf->secondary_channel)) {
+			wpa_printf(MSG_ERROR, "Could not set channel for "
+				   "kernel driver");
+			return -1;
+		}
+	}
+
+	if (iface->current_mode) {
+		if (hostapd_prepare_rates(hapd, iface->current_mode)) {
+			wpa_printf(MSG_ERROR, "Failed to prepare rates "
+				   "table.");
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Failed to prepare rates table.");
+			return -1;
+		}
+	}
+
+	if (hapd->iconf->rts_threshold > -1 &&
+	    hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
+		wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
+			   "kernel driver");
+		return -1;
+	}
+
+	if (hapd->iconf->fragm_threshold > -1 &&
+	    hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) {
+		wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
+			   "for kernel driver");
+		return -1;
+	}
+
+	prev_addr = hapd->own_addr;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (j)
+			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
+		if (hostapd_setup_bss(hapd, j == 0))
+			return -1;
+		if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
+			prev_addr = hapd->own_addr;
+	}
+
+	hostapd_tx_queue_params(iface);
+
+	ap_list_init(iface);
+
+	if (hostapd_driver_commit(hapd) < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
+			   "configuration", __func__);
+		return -1;
+	}
+
+	if (hapd->setup_complete_cb)
+		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+
+	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+		   iface->bss[0]->conf->iface);
+
+	return 0;
+}
+
+
+/**
+ * hostapd_setup_interface - Setup of an interface
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initializes the driver interface, validates the configuration,
+ * and sets driver parameters based on the configuration.
+ * Flushes old stations, sets the channel, encryption,
+ * beacons, and WDS links based on the configuration.
+ */
+int hostapd_setup_interface(struct hostapd_iface *iface)
+{
+	int ret;
+
+	ret = setup_interface(iface);
+	if (ret) {
+		wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
+			   iface->bss[0]->conf->iface);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * hostapd_alloc_bss_data - Allocate and initialize per-BSS data
+ * @hapd_iface: Pointer to interface data
+ * @conf: Pointer to per-interface configuration
+ * @bss: Pointer to per-BSS configuration for this BSS
+ * Returns: Pointer to allocated BSS data
+ *
+ * This function is used to allocate per-BSS data structure. This data will be
+ * freed after hostapd_cleanup() is called for it during interface
+ * deinitialization.
+ */
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+		       struct hostapd_config *conf,
+		       struct hostapd_bss_config *bss)
+{
+	struct hostapd_data *hapd;
+
+	hapd = os_zalloc(sizeof(*hapd));
+	if (hapd == NULL)
+		return NULL;
+
+	hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
+	hapd->iconf = conf;
+	hapd->conf = bss;
+	hapd->iface = hapd_iface;
+	hapd->driver = hapd->iconf->driver;
+
+	return hapd;
+}
+
+
+void hostapd_interface_deinit(struct hostapd_iface *iface)
+{
+	size_t j;
+
+	if (iface == NULL)
+		return;
+
+	hostapd_cleanup_iface_pre(iface);
+	for (j = 0; j < iface->num_bss; j++) {
+		struct hostapd_data *hapd = iface->bss[j];
+		hostapd_free_stas(hapd);
+		hostapd_flush_old_stations(hapd);
+		hostapd_cleanup(hapd);
+	}
+}
+
+
+void hostapd_interface_free(struct hostapd_iface *iface)
+{
+	size_t j;
+	for (j = 0; j < iface->num_bss; j++)
+		os_free(iface->bss[j]);
+	hostapd_cleanup_iface(iface);
+}
+
+
+/**
+ * hostapd_new_assoc_sta - Notify that a new station associated with the AP
+ * @hapd: Pointer to BSS data
+ * @sta: Pointer to the associated STA data
+ * @reassoc: 1 to indicate this was a re-association; 0 = first association
+ *
+ * This function will be called whenever a station associates with the AP. It
+ * can be called from ieee802_11.c for drivers that export MLME to hostapd and
+ * from drv_callbacks.c based on driver events for drivers that take care of
+ * management frames (IEEE 802.11 authentication and association) internally.
+ */
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   int reassoc)
+{
+	if (hapd->tkip_countermeasures) {
+		hostapd_drv_sta_deauth(hapd, sta->addr,
+				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+		return;
+	}
+
+	hostapd_prune_associations(hapd, sta->addr);
+
+	/* IEEE 802.11F (IAPP) */
+	if (hapd->conf->ieee802_11f)
+		iapp_new_station(hapd->iapp, sta);
+
+#ifdef CONFIG_P2P
+	if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
+		sta->no_p2p_set = 1;
+		hapd->num_sta_no_p2p++;
+		if (hapd->num_sta_no_p2p == 1)
+			hostapd_p2p_non_p2p_sta_connected(hapd);
+	}
+#endif /* CONFIG_P2P */
+
+	/* Start accounting here, if IEEE 802.1X and WPA are not used.
+	 * IEEE 802.1X/WPA code will start accounting after the station has
+	 * been authorized. */
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+		accounting_sta_start(hapd, sta);
+
+	/* Start IEEE 802.1X authentication process for new stations */
+	ieee802_1x_new_station(hapd, sta);
+	if (reassoc) {
+		if (sta->auth_alg != WLAN_AUTH_FT &&
+		    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
+			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
+	} else
+		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
new file mode 100644
index 0000000..d4501a1
--- /dev/null
+++ b/src/ap/hostapd.h
@@ -0,0 +1,262 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 HOSTAPD_H
+#define HOSTAPD_H
+
+#include "common/defs.h"
+
+struct wpa_driver_ops;
+struct wpa_ctrl_dst;
+struct radius_server_data;
+struct upnp_wps_device_sm;
+struct hapd_interfaces;
+struct hostapd_data;
+struct sta_info;
+struct hostap_sta_driver_data;
+struct ieee80211_ht_capabilities;
+struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
+
+struct hostapd_probereq_cb {
+	int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len);
+	void *ctx;
+};
+
+#define HOSTAPD_RATE_BASIC 0x00000001
+
+struct hostapd_rate_data {
+	int rate; /* rate in 100 kbps */
+	int flags; /* HOSTAPD_RATE_ flags */
+};
+
+struct hostapd_frame_info {
+	u32 channel;
+	u32 datarate;
+	u32 ssi_signal;
+};
+
+
+/**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+struct hostapd_data {
+	struct hostapd_iface *iface;
+	struct hostapd_config *iconf;
+	struct hostapd_bss_config *conf;
+	int interface_added; /* virtual interface added for this BSS */
+
+	u8 own_addr[ETH_ALEN];
+
+	int num_sta; /* number of entries in sta_list */
+	struct sta_info *sta_list; /* STA info list head */
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+	struct sta_info *sta_hash[STA_HASH_SIZE];
+
+	/*
+	 * Bitfield for indicating which AIDs are allocated. Only AID values
+	 * 1-2007 are used and as such, the bit at index 0 corresponds to AID
+	 * 1.
+	 */
+#define AID_WORDS ((2008 + 31) / 32)
+	u32 sta_aid[AID_WORDS];
+
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+
+	void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta, int reassoc);
+
+	void *msg_ctx; /* ctx for wpa_msg() calls */
+
+	struct radius_client_data *radius;
+	u32 acct_session_id_hi, acct_session_id_lo;
+
+	struct iapp_data *iapp;
+
+	struct hostapd_cached_radius_acl *acl_cache;
+	struct hostapd_acl_query_data *acl_queries;
+
+	struct wpa_authenticator *wpa_auth;
+	struct eapol_authenticator *eapol_auth;
+
+	struct rsn_preauth_interface *preauth_iface;
+	time_t michael_mic_failure;
+	int michael_mic_failures;
+	int tkip_countermeasures;
+
+	int ctrl_sock;
+	struct wpa_ctrl_dst *ctrl_dst;
+
+	void *ssl_ctx;
+	void *eap_sim_db_priv;
+	struct radius_server_data *radius_srv;
+
+	int parameter_set_count;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	struct full_dynamic_vlan *full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	struct l2_packet_data *l2;
+	struct wps_context *wps;
+
+	struct wpabuf *wps_beacon_ie;
+	struct wpabuf *wps_probe_resp_ie;
+#ifdef CONFIG_WPS
+	unsigned int ap_pin_failures;
+	struct upnp_wps_device_sm *wps_upnp;
+	unsigned int ap_pin_lockout_time;
+#endif /* CONFIG_WPS */
+
+	struct hostapd_probereq_cb *probereq_cb;
+	size_t num_probereq_cb;
+
+	void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+				 int freq);
+	void *public_action_cb_ctx;
+
+	int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
+				int freq);
+	void *vendor_action_cb_ctx;
+
+	void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
+				   const u8 *uuid_e);
+	void *wps_reg_success_cb_ctx;
+
+	void (*wps_event_cb)(void *ctx, enum wps_event event,
+			     union wps_event_data *data);
+	void *wps_event_cb_ctx;
+
+	void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
+				  int authorized);
+	void *sta_authorized_cb_ctx;
+
+	void (*setup_complete_cb)(void *ctx);
+	void *setup_complete_cb_ctx;
+
+#ifdef CONFIG_P2P
+	struct p2p_data *p2p;
+	struct p2p_group *p2p_group;
+	struct wpabuf *p2p_beacon_ie;
+	struct wpabuf *p2p_probe_resp_ie;
+
+	/* Number of non-P2P association stations */
+	int num_sta_no_p2p;
+
+	/* Periodic NoA (used only when no non-P2P clients in the group) */
+	int noa_enabled;
+	int noa_start;
+	int noa_duration;
+#endif /* CONFIG_P2P */
+};
+
+
+/**
+ * struct hostapd_iface - hostapd per-interface data structure
+ */
+struct hostapd_iface {
+	struct hapd_interfaces *interfaces;
+	void *owner;
+	int (*reload_config)(struct hostapd_iface *iface);
+	struct hostapd_config * (*config_read_cb)(const char *config_fname);
+	char *config_fname;
+	struct hostapd_config *conf;
+
+	size_t num_bss;
+	struct hostapd_data **bss;
+
+	int num_ap; /* number of entries in ap_list */
+	struct ap_info *ap_list; /* AP info list head */
+	struct ap_info *ap_hash[STA_HASH_SIZE];
+	struct ap_info *ap_iter_list;
+
+	unsigned int drv_flags;
+	struct hostapd_hw_modes *hw_features;
+	int num_hw_features;
+	struct hostapd_hw_modes *current_mode;
+	/* Rates that are currently used (i.e., filtered copy of
+	 * current_mode->channels */
+	int num_rates;
+	struct hostapd_rate_data *current_rates;
+	int freq;
+
+	u16 hw_flags;
+
+	/* Number of associated Non-ERP stations (i.e., stations using 802.11b
+	 * in 802.11g BSS) */
+	int num_sta_non_erp;
+
+	/* Number of associated stations that do not support Short Slot Time */
+	int num_sta_no_short_slot_time;
+
+	/* Number of associated stations that do not support Short Preamble */
+	int num_sta_no_short_preamble;
+
+	int olbc; /* Overlapping Legacy BSS Condition */
+
+	/* Number of HT associated stations that do not support greenfield */
+	int num_sta_ht_no_gf;
+
+	/* Number of associated non-HT stations */
+	int num_sta_no_ht;
+
+	/* Number of HT associated stations 20 MHz */
+	int num_sta_ht_20mhz;
+
+	/* Overlapping BSS information */
+	int olbc_ht;
+
+	u16 ht_op_mode;
+	void (*scan_cb)(struct hostapd_iface *iface);
+
+	int (*ctrl_iface_init)(struct hostapd_data *hapd);
+	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+
+	int (*for_each_interface)(struct hapd_interfaces *interfaces,
+				  int (*cb)(struct hostapd_iface *iface,
+					    void *ctx), void *ctx);
+};
+
+/* hostapd.c */
+int hostapd_reload_config(struct hostapd_iface *iface);
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+		       struct hostapd_config *conf,
+		       struct hostapd_bss_config *bss);
+int hostapd_setup_interface(struct hostapd_iface *iface);
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
+void hostapd_interface_deinit(struct hostapd_iface *iface);
+void hostapd_interface_free(struct hostapd_iface *iface);
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   int reassoc);
+
+/* utils.c */
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+				 int (*cb)(void *ctx, const u8 *sa,
+					   const u8 *ie, size_t ie_len),
+				 void *ctx);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
+
+/* drv_callbacks.c (TODO: move to somewhere else?) */
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+			const u8 *ie, size_t ielen, int reassoc);
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
+			 const u8 *ie, size_t ie_len);
+
+#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
new file mode 100644
index 0000000..f3fffd7
--- /dev/null
+++ b/src/ap/hw_features.c
@@ -0,0 +1,751 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "hw_features.h"
+
+
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+			      size_t num_hw_features)
+{
+	size_t i;
+
+	if (hw_features == NULL)
+		return;
+
+	for (i = 0; i < num_hw_features; i++) {
+		os_free(hw_features[i].channels);
+		os_free(hw_features[i].rates);
+	}
+
+	os_free(hw_features);
+}
+
+
+int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	int ret = 0, i, j;
+	u16 num_modes, flags;
+	struct hostapd_hw_modes *modes;
+
+	if (hostapd_drv_none(hapd))
+		return -1;
+	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
+	if (modes == NULL) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Fetching hardware channel/rate support not "
+			       "supported.");
+		return -1;
+	}
+
+	iface->hw_flags = flags;
+
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = modes;
+	iface->num_hw_features = num_modes;
+
+	for (i = 0; i < num_modes; i++) {
+		struct hostapd_hw_modes *feature = &modes[i];
+		/* set flag for channels we can use in current regulatory
+		 * domain */
+		for (j = 0; j < feature->num_channels; j++) {
+			/*
+			 * Disable all channels that are marked not to allow
+			 * IBSS operation or active scanning. In addition,
+			 * disable all channels that require radar detection,
+			 * since that (in addition to full DFS) is not yet
+			 * supported.
+			 */
+			if (feature->channels[j].flag &
+			    (HOSTAPD_CHAN_NO_IBSS |
+			     HOSTAPD_CHAN_PASSIVE_SCAN |
+			     HOSTAPD_CHAN_RADAR))
+				feature->channels[j].flag |=
+					HOSTAPD_CHAN_DISABLED;
+			if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+				continue;
+			wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
+				   "chan=%d freq=%d MHz max_tx_power=%d dBm",
+				   feature->mode,
+				   feature->channels[j].chan,
+				   feature->channels[j].freq,
+				   feature->channels[j].max_tx_power);
+		}
+	}
+
+	return ret;
+}
+
+
+int hostapd_prepare_rates(struct hostapd_data *hapd,
+			  struct hostapd_hw_modes *mode)
+{
+	int i, num_basic_rates = 0;
+	int basic_rates_a[] = { 60, 120, 240, -1 };
+	int basic_rates_b[] = { 10, 20, -1 };
+	int basic_rates_g[] = { 10, 20, 55, 110, -1 };
+	int *basic_rates;
+
+	if (hapd->iconf->basic_rates)
+		basic_rates = hapd->iconf->basic_rates;
+	else switch (mode->mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		basic_rates = basic_rates_a;
+		break;
+	case HOSTAPD_MODE_IEEE80211B:
+		basic_rates = basic_rates_b;
+		break;
+	case HOSTAPD_MODE_IEEE80211G:
+		basic_rates = basic_rates_g;
+		break;
+	default:
+		return -1;
+	}
+
+	if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates,
+				  basic_rates, mode->mode)) {
+		wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel "
+			   "module");
+	}
+
+	os_free(hapd->iface->current_rates);
+	hapd->iface->num_rates = 0;
+
+	hapd->iface->current_rates =
+		os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data));
+	if (!hapd->iface->current_rates) {
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
+			   "table.");
+		return -1;
+	}
+
+	for (i = 0; i < mode->num_rates; i++) {
+		struct hostapd_rate_data *rate;
+
+		if (hapd->iconf->supported_rates &&
+		    !hostapd_rate_found(hapd->iconf->supported_rates,
+					mode->rates[i]))
+			continue;
+
+		rate = &hapd->iface->current_rates[hapd->iface->num_rates];
+		rate->rate = mode->rates[i];
+		if (hostapd_rate_found(basic_rates, rate->rate)) {
+			rate->flags |= HOSTAPD_RATE_BASIC;
+			num_basic_rates++;
+		}
+		wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
+			   hapd->iface->num_rates, rate->rate, rate->flags);
+		hapd->iface->num_rates++;
+	}
+
+	if ((hapd->iface->num_rates == 0 || num_basic_rates == 0) &&
+	    (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) {
+		wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
+			   "rate sets (%d,%d).",
+			   hapd->iface->num_rates, num_basic_rates);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211N
+static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
+{
+	int sec_chan, ok, j, first;
+	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+			  184, 192 };
+	size_t k;
+
+	if (!iface->conf->secondary_channel)
+		return 1; /* HT40 not used */
+
+	sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
+	wpa_printf(MSG_DEBUG, "HT40: control channel: %d  "
+		   "secondary channel: %d",
+		   iface->conf->channel, sec_chan);
+
+	/* Verify that HT40 secondary channel is an allowed 20 MHz
+	 * channel */
+	ok = 0;
+	for (j = 0; j < iface->current_mode->num_channels; j++) {
+		struct hostapd_channel_data *chan =
+			&iface->current_mode->channels[j];
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    chan->chan == sec_chan) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+			   sec_chan);
+		return 0;
+	}
+
+	/*
+	 * Verify that HT40 primary,secondary channel pair is allowed per
+	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
+	 * 2.4 GHz rules allow all cases where the secondary channel fits into
+	 * the list of allowed channels (already checked above).
+	 */
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 1;
+
+	if (iface->conf->secondary_channel > 0)
+		first = iface->conf->channel;
+	else
+		first = sec_chan;
+
+	ok = 0;
+	for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
+		if (first == allowed[k]) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
+			   iface->conf->channel,
+			   iface->conf->secondary_channel);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
+{
+	if (iface->conf->secondary_channel > 0) {
+		iface->conf->channel += 4;
+		iface->conf->secondary_channel = -1;
+	} else {
+		iface->conf->channel -= 4;
+		iface->conf->secondary_channel = 1;
+	}
+}
+
+
+static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
+					int *pri_chan, int *sec_chan)
+{
+	struct ieee80211_ht_operation *oper;
+	struct ieee802_11_elems elems;
+
+	*pri_chan = *sec_chan = 0;
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (elems.ht_operation &&
+	    elems.ht_operation_len >= sizeof(*oper)) {
+		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+		*pri_chan = oper->control_chan;
+		if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
+			int sec = oper->ht_param &
+				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+				*sec_chan = *pri_chan + 4;
+			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+				*sec_chan = *pri_chan - 4;
+		}
+	}
+}
+
+
+static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
+				     struct wpa_scan_results *scan_res)
+{
+	int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
+	int bss_pri_chan, bss_sec_chan;
+	size_t i;
+	int match;
+
+	pri_chan = iface->conf->channel;
+	sec_chan = iface->conf->secondary_channel * 4;
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+
+	/*
+	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
+	 * channel, but not on selected PRI channel.
+	 */
+	pri_bss = sec_bss = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		if (bss->freq == pri_freq)
+			pri_bss++;
+		else if (bss->freq == sec_freq)
+			sec_bss++;
+	}
+	if (sec_bss && !pri_bss) {
+		wpa_printf(MSG_INFO, "Switch own primary and secondary "
+			   "channel to get secondary channel with no Beacons "
+			   "from other BSSes");
+		ieee80211n_switch_pri_sec(iface);
+	}
+
+	/*
+	 * Match PRI/SEC channel with any existing HT40 BSS on the same
+	 * channels that we are about to use (if already mixed order in
+	 * existing BSSes, use own preference).
+	 */
+	match = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+		if (pri_chan == bss_pri_chan &&
+		    sec_chan == bss_sec_chan) {
+			match = 1;
+			break;
+		}
+	}
+	if (!match) {
+		for (i = 0; i < scan_res->num; i++) {
+			struct wpa_scan_res *bss = scan_res->res[i];
+			ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
+						    &bss_sec_chan);
+			if (pri_chan == bss_sec_chan &&
+			    sec_chan == bss_pri_chan) {
+				wpa_printf(MSG_INFO, "Switch own primary and "
+					   "secondary channel due to BSS "
+					   "overlap with " MACSTR,
+					   MAC2STR(bss->bssid));
+				ieee80211n_switch_pri_sec(iface);
+				break;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
+				      struct wpa_scan_results *scan_res)
+{
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	size_t i;
+
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		int pri = bss->freq;
+		int sec = pri;
+		int sec_chan, pri_chan;
+
+		ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+		if (sec_chan) {
+			if (sec_chan < pri_chan)
+				sec = pri - 20;
+			else
+				sec = pri + 20;
+		}
+
+		if ((pri < affected_start || pri > affected_end) &&
+		    (sec < affected_start || sec > affected_end))
+			continue; /* not within affected channel range */
+
+		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+			   " freq=%d pri=%d sec=%d",
+			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+		if (sec_chan) {
+			if (pri_freq != pri || sec_freq != sec) {
+				wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
+					   "mismatch with BSS " MACSTR
+					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+					   MAC2STR(bss->bssid),
+					   pri, sec, pri_chan,
+					   sec > pri ? '+' : '-',
+					   pri_freq, sec_freq);
+				return 0;
+			}
+		}
+
+		/* TODO: 40 MHz intolerant */
+	}
+
+	return 1;
+}
+
+
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+	size_t i;
+
+	if (res == NULL)
+		return;
+
+	for (i = 0; i < res->num; i++)
+		os_free(res->res[i]);
+	os_free(res->res);
+	os_free(res);
+}
+
+
+static void ieee80211n_check_scan(struct hostapd_iface *iface)
+{
+	struct wpa_scan_results *scan_res;
+	int oper40;
+	int res;
+
+	/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
+	 * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
+
+	iface->scan_cb = NULL;
+
+	scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+	if (scan_res == NULL) {
+		hostapd_setup_interface_complete(iface, 1);
+		return;
+	}
+
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
+		oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
+	else
+		oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
+	wpa_scan_results_free(scan_res);
+
+	if (!oper40) {
+		wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
+			   "channel pri=%d sec=%d based on overlapping BSSes",
+			   iface->conf->channel,
+			   iface->conf->channel +
+			   iface->conf->secondary_channel * 4);
+		iface->conf->secondary_channel = 0;
+		iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+	}
+
+	res = ieee80211n_allowed_ht40_channel_pair(iface);
+	hostapd_setup_interface_complete(iface, !res);
+}
+
+
+static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+{
+	struct wpa_driver_scan_params params;
+
+	if (!iface->conf->secondary_channel)
+		return 0; /* HT40 not used */
+
+	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
+		   "40 MHz channel");
+	os_memset(&params, 0, sizeof(params));
+	/* TODO: scan only the needed frequency */
+	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to request a scan of "
+			   "neighboring BSSes");
+		return -1;
+	}
+
+	iface->scan_cb = ieee80211n_check_scan;
+	return 1;
+}
+
+
+static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
+{
+	u16 hw = iface->current_mode->ht_capab;
+	u16 conf = iface->conf->ht_capab;
+
+	if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
+	    !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [LDPC]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+	    !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [HT40*]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
+	    (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [SMPS-*]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
+	    !(hw & HT_CAP_INFO_GREEN_FIELD)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [GF]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
+	    !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [SHORT-GI-20]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
+	    !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [SHORT-GI-40]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [TX-STBC]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
+	    (hw & HT_CAP_INFO_RX_STBC_MASK)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [RX-STBC*]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_DELAYED_BA) &&
+	    !(hw & HT_CAP_INFO_DELAYED_BA)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [DELAYED-BA]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
+	    !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [MAX-AMSDU-7935]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
+	    !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [DSSS_CCK-40]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [PSMP]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
+	    !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [LSIG-TXOP-PROT]");
+		return 0;
+	}
+
+	return 1;
+}
+
+#endif /* CONFIG_IEEE80211N */
+
+
+int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211N
+	int ret;
+	if (!iface->conf->ieee80211n)
+		return 0;
+	if (!ieee80211n_supported_ht_capab(iface))
+		return -1;
+	ret = ieee80211n_check_40mhz(iface);
+	if (ret)
+		return ret;
+	if (!ieee80211n_allowed_ht40_channel_pair(iface))
+		return -1;
+#endif /* CONFIG_IEEE80211N */
+
+	return 0;
+}
+
+
+/**
+ * hostapd_select_hw_mode - Select the hardware mode
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Sets up the hardware mode, channel, rates, and passive scanning
+ * based on the configuration.
+ */
+int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+	int i, j, ok;
+
+	if (iface->num_hw_features < 1)
+		return -1;
+
+	iface->current_mode = NULL;
+	for (i = 0; i < iface->num_hw_features; i++) {
+		struct hostapd_hw_modes *mode = &iface->hw_features[i];
+		if (mode->mode == iface->conf->hw_mode) {
+			iface->current_mode = mode;
+			break;
+		}
+	}
+
+	if (iface->current_mode == NULL) {
+		wpa_printf(MSG_ERROR, "Hardware does not support configured "
+			   "mode");
+		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "Hardware does not support configured mode "
+			       "(%d)", (int) iface->conf->hw_mode);
+		return -1;
+	}
+
+	ok = 0;
+	for (j = 0; j < iface->current_mode->num_channels; j++) {
+		struct hostapd_channel_data *chan =
+			&iface->current_mode->channels[j];
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    (chan->chan == iface->conf->channel)) {
+			ok = 1;
+			break;
+		}
+	}
+	if (ok && iface->conf->secondary_channel) {
+		int sec_ok = 0;
+		int sec_chan = iface->conf->channel +
+			iface->conf->secondary_channel * 4;
+		for (j = 0; j < iface->current_mode->num_channels; j++) {
+			struct hostapd_channel_data *chan =
+				&iface->current_mode->channels[j];
+			if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+			    (chan->chan == sec_chan)) {
+				sec_ok = 1;
+				break;
+			}
+		}
+		if (!sec_ok) {
+			hostapd_logger(iface->bss[0], NULL,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Configured HT40 secondary channel "
+				       "(%d) not found from the channel list "
+				       "of current mode (%d) %s",
+				       sec_chan, iface->current_mode->mode,
+				       hostapd_hw_mode_txt(
+					       iface->current_mode->mode));
+			ok = 0;
+		}
+	}
+	if (iface->conf->channel == 0) {
+		/* TODO: could request a scan of neighboring BSSes and select
+		 * the channel automatically */
+		wpa_printf(MSG_ERROR, "Channel not configured "
+			   "(hw_mode/channel in hostapd.conf)");
+		return -1;
+	}
+	if (ok == 0 && iface->conf->channel != 0) {
+		hostapd_logger(iface->bss[0], NULL,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "Configured channel (%d) not found from the "
+			       "channel list of current mode (%d) %s",
+			       iface->conf->channel,
+			       iface->current_mode->mode,
+			       hostapd_hw_mode_txt(iface->current_mode->mode));
+		iface->current_mode = NULL;
+	}
+
+	if (iface->current_mode == NULL) {
+		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "Hardware does not support configured channel");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+const char * hostapd_hw_mode_txt(int mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		return "IEEE 802.11a";
+	case HOSTAPD_MODE_IEEE80211B:
+		return "IEEE 802.11b";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "IEEE 802.11g";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+	int i;
+
+	if (!hapd->iface->current_mode)
+		return 0;
+
+	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+		struct hostapd_channel_data *ch =
+			&hapd->iface->current_mode->channels[i];
+		if (ch->chan == chan)
+			return ch->freq;
+	}
+
+	return 0;
+}
+
+
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
+{
+	int i;
+
+	if (!hapd->iface->current_mode)
+		return 0;
+
+	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+		struct hostapd_channel_data *ch =
+			&hapd->iface->current_mode->channels[i];
+		if (ch->freq == freq)
+			return ch->chan;
+	}
+
+	return 0;
+}
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
new file mode 100644
index 0000000..88c2322
--- /dev/null
+++ b/src/ap/hw_features.h
@@ -0,0 +1,70 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * 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 HW_FEATURES_H
+#define HW_FEATURES_H
+
+#ifdef NEED_AP_MLME
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+			      size_t num_hw_features);
+int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_select_hw_mode(struct hostapd_iface *iface);
+const char * hostapd_hw_mode_txt(int mode);
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
+int hostapd_check_ht_capab(struct hostapd_iface *iface);
+int hostapd_prepare_rates(struct hostapd_data *hapd,
+			  struct hostapd_hw_modes *mode);
+#else /* NEED_AP_MLME */
+static inline void
+hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+			 size_t num_hw_features)
+{
+}
+
+static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+	return -1;
+}
+
+static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+	return -1;
+}
+
+static inline const char * hostapd_hw_mode_txt(int mode)
+{
+	return NULL;
+}
+
+static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+	return -1;
+}
+
+static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline int hostapd_prepare_rates(struct hostapd_data *hapd,
+					struct hostapd_hw_modes *mode)
+{
+	return 0;
+}
+
+#endif /* NEED_AP_MLME */
+
+#endif /* HW_FEATURES_H */
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
new file mode 100644
index 0000000..115d91e
--- /dev/null
+++ b/src/ap/iapp.c
@@ -0,0 +1,535 @@
+/*
+ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
+ * and IEEE has withdrawn it. In other words, it is likely better to look at
+ * using some other mechanism for AP-to-AP communication than extending the
+ * implementation here.
+ */
+
+/* TODO:
+ * Level 1: no administrative or security support
+ *	(e.g., static BSSID to IP address mapping in each AP)
+ * Level 2: support for dynamic mapping of BSSID to IP address
+ * Level 3: support for encryption and authentication of IAPP messages
+ * - add support for MOVE-notify and MOVE-response (this requires support for
+ *   finding out IP address for previous AP using RADIUS)
+ * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
+ *   reassociation to another AP
+ * - implement counters etc. for IAPP MIB
+ * - verify endianness of fields in IAPP messages; are they big-endian as
+ *   used here?
+ * - RADIUS connection for AP registration and BSSID to IP address mapping
+ * - TCP connection for IAPP MOVE, CACHE
+ * - broadcast ESP for IAPP ADD-notify
+ * - ESP for IAPP MOVE messages
+ * - security block sending/processing
+ * - IEEE 802.11 context transfer
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+#ifdef USE_KERNEL_HEADERS
+#include <linux/if_packet.h>
+#else /* USE_KERNEL_HEADERS */
+#include <netpacket/packet.h>
+#endif /* USE_KERNEL_HEADERS */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "iapp.h"
+
+
+#define IAPP_MULTICAST "224.0.1.178"
+#define IAPP_UDP_PORT 3517
+#define IAPP_TCP_PORT 3517
+
+struct iapp_hdr {
+	u8 version;
+	u8 command;
+	be16 identifier;
+	be16 length;
+	/* followed by length-6 octets of data */
+} __attribute__ ((packed));
+
+#define IAPP_VERSION 0
+
+enum IAPP_COMMAND {
+	IAPP_CMD_ADD_notify = 0,
+	IAPP_CMD_MOVE_notify = 1,
+	IAPP_CMD_MOVE_response = 2,
+	IAPP_CMD_Send_Security_Block = 3,
+	IAPP_CMD_ACK_Security_Block = 4,
+	IAPP_CMD_CACHE_notify = 5,
+	IAPP_CMD_CACHE_response = 6,
+};
+
+
+/* ADD-notify - multicast UDP on the local LAN */
+struct iapp_add_notify {
+	u8 addr_len; /* ETH_ALEN */
+	u8 reserved;
+	u8 mac_addr[ETH_ALEN];
+	be16 seq_num;
+} __attribute__ ((packed));
+
+
+/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
+struct iapp_layer2_update {
+	u8 da[ETH_ALEN]; /* broadcast */
+	u8 sa[ETH_ALEN]; /* STA addr */
+	be16 len; /* 6 */
+	u8 dsap; /* null DSAP address */
+	u8 ssap; /* null SSAP address, CR=Response */
+	u8 control;
+	u8 xid_info[3];
+} __attribute__ ((packed));
+
+
+/* MOVE-notify - unicast TCP */
+struct iapp_move_notify {
+	u8 addr_len; /* ETH_ALEN */
+	u8 reserved;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+	u16 ctx_block_len;
+	/* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+
+/* MOVE-response - unicast TCP */
+struct iapp_move_response {
+	u8 addr_len; /* ETH_ALEN */
+	u8 status;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+	u16 ctx_block_len;
+	/* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+enum {
+	IAPP_MOVE_SUCCESSFUL = 0,
+	IAPP_MOVE_DENIED = 1,
+	IAPP_MOVE_STALE_MOVE = 2,
+};
+
+
+/* CACHE-notify */
+struct iapp_cache_notify {
+	u8 addr_len; /* ETH_ALEN */
+	u8 reserved;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+	u8 current_ap[ETH_ALEN];
+	u16 ctx_block_len;
+	/* ctx_block_len bytes of context block followed by 16-bit context
+	 * timeout */
+} __attribute__ ((packed));
+
+
+/* CACHE-response - unicast TCP */
+struct iapp_cache_response {
+	u8 addr_len; /* ETH_ALEN */
+	u8 status;
+	u8 mac_addr[ETH_ALEN];
+	u16 seq_num;
+} __attribute__ ((packed));
+
+enum {
+	IAPP_CACHE_SUCCESSFUL = 0,
+	IAPP_CACHE_STALE_CACHE = 1,
+};
+
+
+/* Send-Security-Block - unicast TCP */
+struct iapp_send_security_block {
+	u8 iv[8];
+	u16 sec_block_len;
+	/* followed by sec_block_len bytes of security block */
+} __attribute__ ((packed));
+
+
+/* ACK-Security-Block - unicast TCP */
+struct iapp_ack_security_block {
+	u8 iv[8];
+	u8 new_ap_ack_authenticator[48];
+} __attribute__ ((packed));
+
+
+struct iapp_data {
+	struct hostapd_data *hapd;
+	u16 identifier; /* next IAPP identifier */
+	struct in_addr own, multicast;
+	int udp_sock;
+	int packet_sock;
+};
+
+
+static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
+{
+	char buf[128];
+	struct iapp_hdr *hdr;
+	struct iapp_add_notify *add;
+	struct sockaddr_in addr;
+
+	/* Send IAPP ADD-notify to remove possible association from other APs
+	 */
+
+	hdr = (struct iapp_hdr *) buf;
+	hdr->version = IAPP_VERSION;
+	hdr->command = IAPP_CMD_ADD_notify;
+	hdr->identifier = host_to_be16(iapp->identifier++);
+	hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
+
+	add = (struct iapp_add_notify *) (hdr + 1);
+	add->addr_len = ETH_ALEN;
+	add->reserved = 0;
+	os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
+
+	add->seq_num = host_to_be16(seq_num);
+	
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = iapp->multicast.s_addr;
+	addr.sin_port = htons(IAPP_UDP_PORT);
+	if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
+		   (struct sockaddr *) &addr, sizeof(addr)) < 0)
+		perror("sendto[IAPP-ADD]");
+}
+
+
+static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
+{
+	struct iapp_layer2_update msg;
+
+	/* Send Level 2 Update Frame to update forwarding tables in layer 2
+	 * bridge devices */
+
+	/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
+	 * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
+
+	os_memset(msg.da, 0xff, ETH_ALEN);
+	os_memcpy(msg.sa, addr, ETH_ALEN);
+	msg.len = host_to_be16(6);
+	msg.dsap = 0; /* NULL DSAP address */
+	msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
+	msg.control = 0xaf; /* XID response lsb.1111F101.
+			     * F=0 (no poll command; unsolicited frame) */
+	msg.xid_info[0] = 0x81; /* XID format identifier */
+	msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
+	msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
+				   * FIX: what is correct RW with 802.11? */
+
+	if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
+		perror("send[L2 Update]");
+}
+
+
+/**
+ * iapp_new_station - IAPP processing for a new STA
+ * @iapp: IAPP data
+ * @sta: The associated station
+ */
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
+{
+	struct ieee80211_mgmt *assoc;
+	u16 seq;
+
+	if (iapp == NULL)
+		return;
+
+	assoc = sta->last_assoc_req;
+	seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0;
+
+	/* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
+	hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
+	iapp_send_layer2_update(iapp, sta->addr);
+	iapp_send_add(iapp, sta->addr, seq);
+
+	if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) ==
+	    WLAN_FC_STYPE_REASSOC_REQ) {
+		/* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
+		 *                   Context Block, Timeout)
+		 */
+		/* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
+		 * IP address */
+	}
+}
+
+
+static void iapp_process_add_notify(struct iapp_data *iapp,
+				    struct sockaddr_in *from,
+				    struct iapp_hdr *hdr, int len)
+{
+	struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
+	struct sta_info *sta;
+
+	if (len != sizeof(*add)) {
+		printf("Invalid IAPP-ADD packet length %d (expected %lu)\n",
+		       len, (unsigned long) sizeof(*add));
+		return;
+	}
+
+	sta = ap_get_sta(iapp->hapd, add->mac_addr);
+
+	/* IAPP-ADD.indication(MAC Address, Sequence Number) */
+	hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_INFO,
+		       "Received IAPP ADD-notify (seq# %d) from %s:%d%s",
+		       be_to_host16(add->seq_num),
+		       inet_ntoa(from->sin_addr), ntohs(from->sin_port),
+		       sta ? "" : " (STA not found)");
+
+	if (!sta)
+		return;
+
+	/* TODO: could use seq_num to try to determine whether last association
+	 * to this AP is newer than the one advertised in IAPP-ADD. Although,
+	 * this is not really a reliable verification. */
+
+	hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Removing STA due to IAPP ADD-notify");
+	ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
+}
+
+
+/**
+ * iapp_receive_udp - Process IAPP UDP frames
+ * @sock: File descriptor for the socket
+ * @eloop_ctx: IAPP data (struct iapp_data *)
+ * @sock_ctx: Not used
+ */
+static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct iapp_data *iapp = eloop_ctx;
+	int len, hlen;
+	unsigned char buf[128];
+	struct sockaddr_in from;
+	socklen_t fromlen;
+	struct iapp_hdr *hdr;
+
+	/* Handle incoming IAPP frames (over UDP/IP) */
+
+	fromlen = sizeof(from);
+	len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (len < 0) {
+		perror("recvfrom");
+		return;
+	}
+
+	if (from.sin_addr.s_addr == iapp->own.s_addr)
+		return; /* ignore own IAPP messages */
+
+	hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Received %d byte IAPP frame from %s%s\n",
+		       len, inet_ntoa(from.sin_addr),
+		       len < (int) sizeof(*hdr) ? " (too short)" : "");
+
+	if (len < (int) sizeof(*hdr))
+		return;
+
+	hdr = (struct iapp_hdr *) buf;
+	hlen = be_to_host16(hdr->length);
+	hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "RX: version=%d command=%d id=%d len=%d\n",
+		       hdr->version, hdr->command,
+		       be_to_host16(hdr->identifier), hlen);
+	if (hdr->version != IAPP_VERSION) {
+		printf("Dropping IAPP frame with unknown version %d\n",
+		       hdr->version);
+		return;
+	}
+	if (hlen > len) {
+		printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len);
+		return;
+	}
+	if (hlen < len) {
+		printf("Ignoring %d extra bytes from IAPP frame\n",
+		       len - hlen);
+		len = hlen;
+	}
+
+	switch (hdr->command) {
+	case IAPP_CMD_ADD_notify:
+		iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr));
+		break;
+	case IAPP_CMD_MOVE_notify:
+		/* TODO: MOVE is using TCP; so move this to TCP handler once it
+		 * is implemented.. */
+		/* IAPP-MOVE.indication(MAC Address, New BSSID,
+		 * Sequence Number, AP Address, Context Block) */
+		/* TODO: process */
+		break;
+	default:
+		printf("Unknown IAPP command %d\n", hdr->command);
+		break;
+	}
+}
+
+
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
+{
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+	int ifindex;
+	struct sockaddr_in *paddr, uaddr;
+	struct iapp_data *iapp;
+	struct ip_mreqn mreq;
+
+	iapp = os_zalloc(sizeof(*iapp));
+	if (iapp == NULL)
+		return NULL;
+	iapp->hapd = hapd;
+	iapp->udp_sock = iapp->packet_sock = -1;
+
+	/* TODO:
+	 * open socket for sending and receiving IAPP frames over TCP
+	 */
+
+	iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (iapp->udp_sock < 0) {
+		perror("socket[PF_INET,SOCK_DGRAM]");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+	if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
+		perror("ioctl(SIOCGIFINDEX)");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	ifindex = ifr.ifr_ifindex;
+
+	if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
+		perror("ioctl(SIOCGIFADDR)");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+	if (paddr->sin_family != AF_INET) {
+		printf("Invalid address family %i (SIOCGIFADDR)\n",
+		       paddr->sin_family);
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	iapp->own.s_addr = paddr->sin_addr.s_addr;
+
+	if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
+		perror("ioctl(SIOCGIFBRDADDR)");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+	if (paddr->sin_family != AF_INET) {
+		printf("Invalid address family %i (SIOCGIFBRDADDR)\n",
+		       paddr->sin_family);
+		iapp_deinit(iapp);
+		return NULL;
+	}
+	inet_aton(IAPP_MULTICAST, &iapp->multicast);
+
+	os_memset(&uaddr, 0, sizeof(uaddr));
+	uaddr.sin_family = AF_INET;
+	uaddr.sin_port = htons(IAPP_UDP_PORT);
+	if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
+		 sizeof(uaddr)) < 0) {
+		perror("bind[UDP]");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	os_memset(&mreq, 0, sizeof(mreq));
+	mreq.imr_multiaddr = iapp->multicast;
+	mreq.imr_address.s_addr = INADDR_ANY;
+	mreq.imr_ifindex = 0;
+	if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
+		       sizeof(mreq)) < 0) {
+		perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (iapp->packet_sock < 0) {
+		perror("socket[PF_PACKET,SOCK_RAW]");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifindex;
+	if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
+		 sizeof(addr)) < 0) {
+		perror("bind[PACKET]");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
+				     iapp, NULL)) {
+		printf("Could not register read socket for IAPP.\n");
+		iapp_deinit(iapp);
+		return NULL;
+	}
+
+	printf("IEEE 802.11F (IAPP) using interface %s\n", iface);
+
+	/* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
+	 * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
+	 * be openned only after receiving Initiate-Accept. If Initiate-Reject
+	 * is received, IAPP is not started. */
+
+	return iapp;
+}
+
+
+void iapp_deinit(struct iapp_data *iapp)
+{
+	struct ip_mreqn mreq;
+
+	if (iapp == NULL)
+		return;
+
+	if (iapp->udp_sock >= 0) {
+		os_memset(&mreq, 0, sizeof(mreq));
+		mreq.imr_multiaddr = iapp->multicast;
+		mreq.imr_address.s_addr = INADDR_ANY;
+		mreq.imr_ifindex = 0;
+		if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
+			       &mreq, sizeof(mreq)) < 0) {
+			perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]");
+		}
+
+		eloop_unregister_read_sock(iapp->udp_sock);
+		close(iapp->udp_sock);
+	}
+	if (iapp->packet_sock >= 0) {
+		eloop_unregister_read_sock(iapp->packet_sock);
+		close(iapp->packet_sock);
+	}
+	os_free(iapp);
+}
diff --git a/src/ap/iapp.h b/src/ap/iapp.h
new file mode 100644
index 0000000..5fc01cb
--- /dev/null
+++ b/src/ap/iapp.h
@@ -0,0 +1,45 @@
+/*
+ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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 IAPP_H
+#define IAPP_H
+
+struct iapp_data;
+
+#ifdef CONFIG_IAPP
+
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta);
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface);
+void iapp_deinit(struct iapp_data *iapp);
+
+#else /* CONFIG_IAPP */
+
+static inline void iapp_new_station(struct iapp_data *iapp,
+				    struct sta_info *sta)
+{
+}
+
+static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
+					   const char *iface)
+{
+	return NULL;
+}
+
+static inline void iapp_deinit(struct iapp_data *iapp)
+{
+}
+
+#endif /* CONFIG_IAPP */
+
+#endif /* IAPP_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
new file mode 100644
index 0000000..4d8dd25
--- /dev/null
+++ b/src/ap/ieee802_11.c
@@ -0,0 +1,1882 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "hostapd.h"
+#include "beacon.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "ap_list.h"
+#include "accounting.h"
+#include "ap_config.h"
+#include "ap_mlme.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	int i, num, count;
+
+	if (hapd->iface->current_rates == NULL)
+		return eid;
+
+	*pos++ = WLAN_EID_SUPP_RATES;
+	num = hapd->iface->num_rates;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+		num++;
+	if (num > 8) {
+		/* rest of the rates are encoded in Extended supported
+		 * rates element */
+		num = 8;
+	}
+
+	*pos++ = num;
+	count = 0;
+	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
+	     i++) {
+		count++;
+		*pos = hapd->iface->current_rates[i].rate / 5;
+		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+			*pos |= 0x80;
+		pos++;
+	}
+
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+	    hapd->iface->num_rates < 8)
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	int i, num, count;
+
+	if (hapd->iface->current_rates == NULL)
+		return eid;
+
+	num = hapd->iface->num_rates;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+		num++;
+	if (num <= 8)
+		return eid;
+	num -= 8;
+
+	*pos++ = WLAN_EID_EXT_SUPP_RATES;
+	*pos++ = num;
+	count = 0;
+	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
+	     i++) {
+		count++;
+		if (count <= 8)
+			continue; /* already in SuppRates IE */
+		*pos = hapd->iface->current_rates[i].rate / 5;
+		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+			*pos |= 0x80;
+		pos++;
+	}
+
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+	    hapd->iface->num_rates >= 8)
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+
+	return pos;
+}
+
+
+u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
+			   int probe)
+{
+	int capab = WLAN_CAPABILITY_ESS;
+	int privacy;
+
+	if (hapd->iface->num_sta_no_short_preamble == 0 &&
+	    hapd->iconf->preamble == SHORT_PREAMBLE)
+		capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+	privacy = hapd->conf->ssid.wep.keys_set;
+
+	if (hapd->conf->ieee802_1x &&
+	    (hapd->conf->default_wep_key_len ||
+	     hapd->conf->individual_wep_key_len))
+		privacy = 1;
+
+	if (hapd->conf->wpa)
+		privacy = 1;
+
+	if (sta) {
+		int policy, def_klen;
+		if (probe && sta->ssid_probe) {
+			policy = sta->ssid_probe->security_policy;
+			def_klen = sta->ssid_probe->wep.default_len;
+		} else {
+			policy = sta->ssid->security_policy;
+			def_klen = sta->ssid->wep.default_len;
+		}
+		privacy = policy != SECURITY_PLAINTEXT;
+		if (policy == SECURITY_IEEE_802_1X && def_klen == 0)
+			privacy = 0;
+	}
+
+	if (privacy)
+		capab |= WLAN_CAPABILITY_PRIVACY;
+
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    hapd->iface->num_sta_no_short_slot_time == 0)
+		capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+	return capab;
+}
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+	if ((hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) ==
+	    0)
+		return eid;
+
+	*pos++ = WLAN_EID_EXT_CAPAB;
+	*pos++ = 5;
+	*pos++ = 0x00;
+	*pos++ = 0x00;
+	*pos++ = 0x00;
+	*pos++ = 0x00;
+	*pos = 0x00;
+	if (hapd->conf->tdls & TDLS_PROHIBIT)
+		*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+	if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
+		*pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
+	pos++;
+
+	return pos;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+					    struct sta_info *sta, u8 *eid)
+{
+	u8 *pos = eid;
+	u32 timeout, tu;
+	struct os_time now, passed;
+
+	*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+	*pos++ = 5;
+	*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+	os_get_time(&now);
+	os_time_sub(&now, &sta->sa_query_start, &passed);
+	tu = (passed.sec * 1000000 + passed.usec) / 1024;
+	if (hapd->conf->assoc_sa_query_max_timeout > tu)
+		timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+	else
+		timeout = 0;
+	if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+		timeout++; /* add some extra time for local timers */
+	WPA_PUT_LE32(pos, timeout);
+	pos += 4;
+
+	return pos;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
+{
+	int i;
+	if (len > HOSTAPD_MAX_SSID_LEN)
+		len = HOSTAPD_MAX_SSID_LEN;
+	for (i = 0; i < len; i++) {
+		if (ssid[i] >= 32 && ssid[i] < 127)
+			buf[i] = ssid[i];
+		else
+			buf[i] = '.';
+	}
+	buf[len] = '\0';
+}
+
+
+static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 auth_transaction, const u8 *challenge,
+			   int iswep)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "authentication (shared key, transaction %d)",
+		       auth_transaction);
+
+	if (auth_transaction == 1) {
+		if (!sta->challenge) {
+			/* Generate a pseudo-random challenge */
+			u8 key[8];
+			struct os_time now;
+			int r;
+			sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
+			if (sta->challenge == NULL)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			os_get_time(&now);
+			r = os_random();
+			os_memcpy(key, &now.sec, 4);
+			os_memcpy(key + 4, &r, 4);
+			rc4_skip(key, sizeof(key), 0,
+				 sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
+		}
+		return 0;
+	}
+
+	if (auth_transaction != 3)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* Transaction 3 */
+	if (!iswep || !sta->challenge || !challenge ||
+	    os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "shared key authentication - invalid "
+			       "challenge-response");
+		return WLAN_STATUS_CHALLENGE_FAIL;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "authentication OK (shared key)");
+#ifdef IEEE80211_REQUIRE_AUTH_ACK
+	/* Station will be marked authenticated if it ACKs the
+	 * authentication reply. */
+#else
+	sta->flags |= WLAN_STA_AUTH;
+	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+#endif
+	os_free(sta->challenge);
+	sta->challenge = NULL;
+
+	return 0;
+}
+
+
+static void send_auth_reply(struct hostapd_data *hapd,
+			    const u8 *dst, const u8 *bssid,
+			    u16 auth_alg, u16 auth_transaction, u16 resp,
+			    const u8 *ies, size_t ies_len)
+{
+	struct ieee80211_mgmt *reply;
+	u8 *buf;
+	size_t rlen;
+
+	rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
+	buf = os_zalloc(rlen);
+	if (buf == NULL)
+		return;
+
+	reply = (struct ieee80211_mgmt *) buf;
+	reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					    WLAN_FC_STYPE_AUTH);
+	os_memcpy(reply->da, dst, ETH_ALEN);
+	os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(reply->bssid, bssid, ETH_ALEN);
+
+	reply->u.auth.auth_alg = host_to_le16(auth_alg);
+	reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
+	reply->u.auth.status_code = host_to_le16(resp);
+
+	if (ies && ies_len)
+		os_memcpy(reply->u.auth.variable, ies, ies_len);
+
+	wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
+		   " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
+		   MAC2STR(dst), auth_alg, auth_transaction,
+		   resp, (unsigned long) ies_len);
+	if (hostapd_drv_send_mlme(hapd, reply, rlen) < 0)
+		perror("send_auth_reply: send");
+
+	os_free(buf);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+				  u16 auth_transaction, u16 status,
+				  const u8 *ies, size_t ies_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction,
+			status, ies, ies_len);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL)
+		return;
+
+	hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+	sta->flags |= WLAN_STA_AUTH;
+	mlme_authenticate_indication(hapd, sta);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static void handle_auth(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	u16 resp = WLAN_STATUS_SUCCESS;
+	struct sta_info *sta = NULL;
+	int res;
+	u16 fc;
+	const u8 *challenge = NULL;
+	u32 session_timeout, acct_interim_interval;
+	int vlan_id = 0;
+	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+	size_t resp_ies_len = 0;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+		printf("handle_auth - too short payload (len=%lu)\n",
+		       (unsigned long) len);
+		return;
+	}
+
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+	fc = le_to_host16(mgmt->frame_control);
+
+	if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
+	    2 + WLAN_AUTH_CHALLENGE_LEN &&
+	    mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
+	    mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
+		challenge = &mgmt->u.auth.variable[2];
+
+	wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
+		   "auth_transaction=%d status_code=%d wep=%d%s",
+		   MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+		   status_code, !!(fc & WLAN_FC_ISWEP),
+		   challenge ? " challenge" : "");
+
+	if (hapd->tkip_countermeasures) {
+		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+		goto fail;
+	}
+
+	if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
+	       auth_alg == WLAN_AUTH_OPEN) ||
+#ifdef CONFIG_IEEE80211R
+	      (hapd->conf->wpa &&
+	       (hapd->conf->wpa_key_mgmt &
+		(WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) &&
+	       auth_alg == WLAN_AUTH_FT) ||
+#endif /* CONFIG_IEEE80211R */
+	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
+	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
+		printf("Unsupported authentication algorithm (%d)\n",
+		       auth_alg);
+		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+		goto fail;
+	}
+
+	if (!(auth_transaction == 1 ||
+	      (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
+		printf("Unknown authentication transaction number (%d)\n",
+		       auth_transaction);
+		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+		goto fail;
+	}
+
+	if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+		printf("Station " MACSTR " not allowed to authenticate.\n",
+		       MAC2STR(mgmt->sa));
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
+				      &session_timeout,
+				      &acct_interim_interval, &vlan_id);
+	if (res == HOSTAPD_ACL_REJECT) {
+		printf("Station " MACSTR " not allowed to authenticate.\n",
+		       MAC2STR(mgmt->sa));
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	if (res == HOSTAPD_ACL_PENDING) {
+		wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+			   " waiting for an external authentication",
+			   MAC2STR(mgmt->sa));
+		/* Authentication code will re-send the authentication frame
+		 * after it has received (and cached) information from the
+		 * external source. */
+		return;
+	}
+
+	sta = ap_sta_add(hapd, mgmt->sa);
+	if (!sta) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (vlan_id > 0) {
+		if (hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+					       vlan_id) == NULL) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
+				       "%d received from RADIUS server",
+				       vlan_id);
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		sta->vlan_id = vlan_id;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+	}
+
+	sta->flags &= ~WLAN_STA_PREAUTH;
+	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+
+	if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+		sta->acct_interim_interval = acct_interim_interval;
+	if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+		ap_sta_session_timeout(hapd, sta, session_timeout);
+	else
+		ap_sta_no_session_timeout(hapd, sta);
+
+	switch (auth_alg) {
+	case WLAN_AUTH_OPEN:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication OK (open system)");
+#ifdef IEEE80211_REQUIRE_AUTH_ACK
+		/* Station will be marked authenticated if it ACKs the
+		 * authentication reply. */
+#else
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = WLAN_AUTH_OPEN;
+		mlme_authenticate_indication(hapd, sta);
+#endif
+		break;
+	case WLAN_AUTH_SHARED_KEY:
+		resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
+				       fc & WLAN_FC_ISWEP);
+		sta->auth_alg = WLAN_AUTH_SHARED_KEY;
+		mlme_authenticate_indication(hapd, sta);
+		if (sta->challenge && auth_transaction == 1) {
+			resp_ies[0] = WLAN_EID_CHALLENGE;
+			resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
+			os_memcpy(resp_ies + 2, sta->challenge,
+				  WLAN_AUTH_CHALLENGE_LEN);
+			resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
+		}
+		break;
+#ifdef CONFIG_IEEE80211R
+	case WLAN_AUTH_FT:
+		sta->auth_alg = WLAN_AUTH_FT;
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
+				   "state machine");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
+				    auth_transaction, mgmt->u.auth.variable,
+				    len - IEEE80211_HDRLEN -
+				    sizeof(mgmt->u.auth),
+				    handle_auth_ft_finish, hapd);
+		/* handle_auth_ft_finish() callback will complete auth. */
+		return;
+#endif /* CONFIG_IEEE80211R */
+	}
+
+ fail:
+	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
+			auth_transaction + 1, resp, resp_ies, resp_ies_len);
+}
+
+
+static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int i, j = 32, aid;
+
+	/* get a unique AID */
+	if (sta->aid > 0) {
+		wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+		return 0;
+	}
+
+	for (i = 0; i < AID_WORDS; i++) {
+		if (hapd->sta_aid[i] == (u32) -1)
+			continue;
+		for (j = 0; j < 32; j++) {
+			if (!(hapd->sta_aid[i] & BIT(j)))
+				break;
+		}
+		if (j < 32)
+			break;
+	}
+	if (j == 32)
+		return -1;
+	aid = i * 32 + j + 1;
+	if (aid > 2007)
+		return -1;
+
+	sta->aid = aid;
+	hapd->sta_aid[i] |= BIT(j);
+	wpa_printf(MSG_DEBUG, "  new AID %d", sta->aid);
+	return 0;
+}
+
+
+static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ssid_ie, size_t ssid_ie_len)
+{
+	if (ssid_ie == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
+	    os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
+		char ssid_txt[33];
+		ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Station tried to associate with unknown SSID "
+			       "'%s'", ssid_txt);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
+		     const u8 *wmm_ie, size_t wmm_ie_len)
+{
+	sta->flags &= ~WLAN_STA_WMM;
+	if (wmm_ie && hapd->conf->wmm_enabled) {
+		if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "invalid WMM element in association "
+				       "request");
+		else
+			sta->flags |= WLAN_STA_WMM;
+	}
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+			   struct ieee802_11_elems *elems)
+{
+	if (!elems->supp_rates) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "No supported rates element in AssocReq");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (elems->supp_rates_len > sizeof(sta->supported_rates)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Invalid supported rates element length %d",
+			       elems->supp_rates_len);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+	os_memcpy(sta->supported_rates, elems->supp_rates,
+		  elems->supp_rates_len);
+	sta->supported_rates_len = elems->supp_rates_len;
+
+	if (elems->ext_supp_rates) {
+		if (elems->supp_rates_len + elems->ext_supp_rates_len >
+		    sizeof(sta->supported_rates)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Invalid supported rates element length"
+				       " %d+%d", elems->supp_rates_len,
+				       elems->ext_supp_rates_len);
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		os_memcpy(sta->supported_rates + elems->supp_rates_len,
+			  elems->ext_supp_rates, elems->ext_supp_rates_len);
+		sta->supported_rates_len += elems->ext_supp_rates_len;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *ies, size_t ies_len, int reassoc)
+{
+	struct ieee802_11_elems elems;
+	u16 resp;
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station sent an invalid "
+			       "association request");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	resp = copy_supp_rates(hapd, sta, &elems);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+#ifdef CONFIG_IEEE80211N
+	resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities,
+				 elems.ht_capabilities_len);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+	    !(sta->flags & WLAN_STA_HT)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station does not support "
+			       "mandatory HT PHY - reject association");
+		return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+	}
+#endif /* CONFIG_IEEE80211N */
+
+	if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
+		wpa_ie = elems.rsn_ie;
+		wpa_ie_len = elems.rsn_ie_len;
+	} else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
+		   elems.wpa_ie) {
+		wpa_ie = elems.wpa_ie;
+		wpa_ie_len = elems.wpa_ie_len;
+	} else {
+		wpa_ie = NULL;
+		wpa_ie_len = 0;
+	}
+
+#ifdef CONFIG_WPS
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+	if (hapd->conf->wps_state && elems.wps_ie) {
+		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
+			   "Request - assume WPS is used");
+		sta->flags |= WLAN_STA_WPS;
+		wpabuf_free(sta->wps_ie);
+		sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+							  WPS_IE_VENDOR_TYPE);
+		wpa_ie = NULL;
+		wpa_ie_len = 0;
+		if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
+				   "(Re)Association Request - reject");
+			return WLAN_STATUS_INVALID_IE;
+		}
+	} else if (hapd->conf->wps_state && wpa_ie == NULL) {
+		wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
+			   "(Re)Association Request - possible WPS use");
+		sta->flags |= WLAN_STA_MAYBE_WPS;
+	} else
+#endif /* CONFIG_WPS */
+	if (hapd->conf->wpa && wpa_ie == NULL) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "No WPA/RSN IE in association request");
+		return WLAN_STATUS_INVALID_IE;
+	}
+
+	if (hapd->conf->wpa && wpa_ie) {
+		int res;
+		wpa_ie -= 2;
+		wpa_ie_len += 2;
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+				   "state machine");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+					  wpa_ie, wpa_ie_len,
+					  elems.mdie, elems.mdie_len);
+		if (res == WPA_INVALID_GROUP)
+			resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+		else if (res == WPA_INVALID_PAIRWISE)
+			resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+		else if (res == WPA_INVALID_AKMP)
+			resp = WLAN_STATUS_AKMP_NOT_VALID;
+		else if (res == WPA_ALLOC_FAIL)
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+#ifdef CONFIG_IEEE80211W
+		else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+		else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+#endif /* CONFIG_IEEE80211W */
+		else if (res == WPA_INVALID_MDIE)
+			resp = WLAN_STATUS_INVALID_MDIE;
+		else if (res != WPA_IE_OK)
+			resp = WLAN_STATUS_INVALID_IE;
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+#ifdef CONFIG_IEEE80211W
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    sta->sa_query_count > 0)
+			ap_check_sa_query_timeout(hapd, sta);
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+			/*
+			 * STA has already been associated with MFP and SA
+			 * Query timeout has not been reached. Reject the
+			 * association attempt temporarily and start SA Query,
+			 * if one is not pending.
+			 */
+
+			if (sta->sa_query_count == 0)
+				ap_sta_start_sa_query(hapd, sta);
+
+			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+		}
+
+		if (wpa_auth_uses_mfp(sta->wpa_sm))
+			sta->flags |= WLAN_STA_MFP;
+		else
+			sta->flags &= ~WLAN_STA_MFP;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+		if (sta->auth_alg == WLAN_AUTH_FT) {
+			if (!reassoc) {
+				wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
+					   "to use association (not "
+					   "re-association) with FT auth_alg",
+					   MAC2STR(sta->addr));
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+
+			resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
+						       ies_len);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211N
+		if ((sta->flags & WLAN_STA_HT) &&
+		    wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station tried to use TKIP with HT "
+				       "association");
+			return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+		}
+#endif /* CONFIG_IEEE80211N */
+	} else
+		wpa_auth_sta_no_wpa(sta->wpa_sm);
+
+#ifdef CONFIG_P2P
+	if (elems.p2p) {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+							  P2P_IE_VENDOR_TYPE);
+
+	} else {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = NULL;
+	}
+
+	p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
+			u16 reason_code)
+{
+	int send_len;
+	struct ieee80211_mgmt reply;
+
+	os_memset(&reply, 0, sizeof(reply));
+	reply.frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
+	os_memcpy(reply.da, addr, ETH_ALEN);
+	os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
+
+	send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
+	reply.u.deauth.reason_code = host_to_le16(reason_code);
+
+	if (hostapd_drv_send_mlme(hapd, &reply, send_len) < 0)
+		wpa_printf(MSG_INFO, "Failed to send deauth: %s",
+			   strerror(errno));
+}
+
+
+static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+			    u16 status_code, int reassoc, const u8 *ies,
+			    size_t ies_len)
+{
+	int send_len;
+	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+	struct ieee80211_mgmt *reply;
+	u8 *p;
+
+	os_memset(buf, 0, sizeof(buf));
+	reply = (struct ieee80211_mgmt *) buf;
+	reply->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+			     (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+			      WLAN_FC_STYPE_ASSOC_RESP));
+	os_memcpy(reply->da, sta->addr, ETH_ALEN);
+	os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
+
+	send_len = IEEE80211_HDRLEN;
+	send_len += sizeof(reply->u.assoc_resp);
+	reply->u.assoc_resp.capab_info =
+		host_to_le16(hostapd_own_capab_info(hapd, sta, 0));
+	reply->u.assoc_resp.status_code = host_to_le16(status_code);
+	reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0)
+					       | BIT(14) | BIT(15));
+	/* Supported rates */
+	p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
+	/* Extended supported rates */
+	p = hostapd_eid_ext_supp_rates(hapd, p);
+
+#ifdef CONFIG_IEEE80211R
+	if (status_code == WLAN_STATUS_SUCCESS) {
+		/* IEEE 802.11r: Mobility Domain Information, Fast BSS
+		 * Transition Information, RSN, [RIC Response] */
+		p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
+						buf + sizeof(buf) - p,
+						sta->auth_alg, ies, ies_len);
+	}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+		p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211N
+	p = hostapd_eid_ht_capabilities(hapd, p);
+	p = hostapd_eid_ht_operation(hapd, p);
+#endif /* CONFIG_IEEE80211N */
+
+	p = hostapd_eid_ext_capab(hapd, p);
+
+	if (sta->flags & WLAN_STA_WMM)
+		p = hostapd_eid_wmm(hapd, p);
+
+#ifdef CONFIG_WPS
+	if (sta->flags & WLAN_STA_WPS) {
+		struct wpabuf *wps = wps_build_assoc_resp_ie();
+		if (wps) {
+			os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
+			p += wpabuf_len(wps);
+			wpabuf_free(wps);
+		}
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if (sta->p2p_ie) {
+		struct wpabuf *p2p_resp_ie;
+		enum p2p_status_code status;
+		switch (status_code) {
+		case WLAN_STATUS_SUCCESS:
+			status = P2P_SC_SUCCESS;
+			break;
+		case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+			status = P2P_SC_FAIL_LIMIT_REACHED;
+			break;
+		default:
+			status = P2P_SC_FAIL_INVALID_PARAMS;
+			break;
+		}
+		p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
+		if (p2p_resp_ie) {
+			os_memcpy(p, wpabuf_head(p2p_resp_ie),
+				  wpabuf_len(p2p_resp_ie));
+			p += wpabuf_len(p2p_resp_ie);
+			wpabuf_free(p2p_resp_ie);
+		}
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE)
+		p = hostapd_eid_p2p_manage(hapd, p);
+#endif /* CONFIG_P2P_MANAGER */
+
+	send_len += p - reply->u.assoc_resp.variable;
+
+	if (hostapd_drv_send_mlme(hapd, reply, send_len) < 0)
+		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
+			   strerror(errno));
+}
+
+
+static void handle_assoc(struct hostapd_data *hapd,
+			 const struct ieee80211_mgmt *mgmt, size_t len,
+			 int reassoc)
+{
+	u16 capab_info, listen_interval;
+	u16 resp = WLAN_STATUS_SUCCESS;
+	const u8 *pos;
+	int left, i;
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+				      sizeof(mgmt->u.assoc_req))) {
+		printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
+		       "\n", reassoc, (unsigned long) len);
+		return;
+	}
+
+	if (reassoc) {
+		capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
+		listen_interval = le_to_host16(
+			mgmt->u.reassoc_req.listen_interval);
+		wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
+			   " capab_info=0x%02x listen_interval=%d current_ap="
+			   MACSTR,
+			   MAC2STR(mgmt->sa), capab_info, listen_interval,
+			   MAC2STR(mgmt->u.reassoc_req.current_ap));
+		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		pos = mgmt->u.reassoc_req.variable;
+	} else {
+		capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+		listen_interval = le_to_host16(
+			mgmt->u.assoc_req.listen_interval);
+		wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
+			   " capab_info=0x%02x listen_interval=%d",
+			   MAC2STR(mgmt->sa), capab_info, listen_interval);
+		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		pos = mgmt->u.assoc_req.variable;
+	}
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211R
+	if (sta && sta->auth_alg == WLAN_AUTH_FT &&
+	    (sta->flags & WLAN_STA_AUTH) == 0) {
+		wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
+			   "prior to authentication since it is using "
+			   "over-the-DS FT", MAC2STR(mgmt->sa));
+	} else
+#endif /* CONFIG_IEEE80211R */
+	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station tried to "
+			       "associate before authentication "
+			       "(aid=%d flags=0x%x)",
+			       sta ? sta->aid : -1,
+			       sta ? sta->flags : 0);
+		send_deauth(hapd, mgmt->sa,
+			    WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+		return;
+	}
+
+	if (hapd->tkip_countermeasures) {
+		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+		goto fail;
+	}
+
+	if (listen_interval > hapd->conf->max_listen_interval) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Too large Listen Interval (%d)",
+			       listen_interval);
+		resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
+		goto fail;
+	}
+
+	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
+	 * is used */
+	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	if (hostapd_get_aid(hapd, sta) < 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		goto fail;
+	}
+
+	sta->capability = capab_info;
+	sta->listen_interval = listen_interval;
+
+	if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		sta->flags |= WLAN_STA_NONERP;
+	for (i = 0; i < sta->supported_rates_len; i++) {
+		if ((sta->supported_rates[i] & 0x7f) > 22) {
+			sta->flags &= ~WLAN_STA_NONERP;
+			break;
+		}
+	}
+	if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
+		sta->nonerp_set = 1;
+		hapd->iface->num_sta_non_erp++;
+		if (hapd->iface->num_sta_non_erp == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+	if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
+	    !sta->no_short_slot_time_set) {
+		sta->no_short_slot_time_set = 1;
+		hapd->iface->num_sta_no_short_slot_time++;
+		if (hapd->iface->current_mode->mode ==
+		    HOSTAPD_MODE_IEEE80211G &&
+		    hapd->iface->num_sta_no_short_slot_time == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+	if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+		sta->flags |= WLAN_STA_SHORT_PREAMBLE;
+	else
+		sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+
+	if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
+	    !sta->no_short_preamble_set) {
+		sta->no_short_preamble_set = 1;
+		hapd->iface->num_sta_no_short_preamble++;
+		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_preamble == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+#ifdef CONFIG_IEEE80211N
+	update_ht_state(hapd, sta);
+#endif /* CONFIG_IEEE80211N */
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "association OK (aid %d)", sta->aid);
+	/* Station will be marked associated, after it acknowledges AssocResp
+	 */
+	sta->flags |= WLAN_STA_ASSOC_REQ_OK;
+
+#ifdef CONFIG_IEEE80211W
+	if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
+		wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
+			   "SA Query procedure", reassoc ? "re" : "");
+		/* TODO: Send a protected Disassociate frame to the STA using
+		 * the old key and Reason Code "Previous Authentication no
+		 * longer valid". Make sure this is only sent protected since
+		 * unprotected frame would be received by the STA that is now
+		 * trying to associate.
+		 */
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	if (reassoc) {
+		os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap,
+			  ETH_ALEN);
+	}
+
+	if (sta->last_assoc_req)
+		os_free(sta->last_assoc_req);
+	sta->last_assoc_req = os_malloc(len);
+	if (sta->last_assoc_req)
+		os_memcpy(sta->last_assoc_req, mgmt, len);
+
+	/* Make sure that the previously registered inactivity timer will not
+	 * remove the STA immediately. */
+	sta->timeout_next = STA_NULLFUNC;
+
+ fail:
+	send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+}
+
+
+static void handle_disassoc(struct hostapd_data *hapd,
+			    const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
+		printf("handle_disassoc - too short payload (len=%lu)\n",
+		       (unsigned long) len);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
+		   MAC2STR(mgmt->sa),
+		   le_to_host16(mgmt->u.disassoc.reason_code));
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL) {
+		printf("Station " MACSTR " trying to disassociate, but it "
+		       "is not associated.\n", MAC2STR(mgmt->sa));
+		return;
+	}
+
+	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+		MAC2STR(sta->addr));
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "disassociated");
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	/* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+	 * authenticated. */
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(sta);
+	hostapd_drv_sta_remove(hapd, sta->addr);
+
+	if (sta->timeout_next == STA_NULLFUNC ||
+	    sta->timeout_next == STA_DISASSOC) {
+		sta->timeout_next = STA_DEAUTH;
+		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+	}
+
+	mlme_disassociate_indication(
+		hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
+}
+
+
+static void handle_deauth(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
+		wpa_msg(hapd, MSG_DEBUG, "handle_deauth - too short payload "
+			"(len=%lu)", (unsigned long) len);
+		return;
+	}
+
+	wpa_msg(hapd, MSG_DEBUG, "deauthentication: STA=" MACSTR
+		" reason_code=%d",
+		MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL) {
+		wpa_msg(hapd, MSG_DEBUG, "Station " MACSTR " trying to "
+			"deauthenticate, but it is not authenticated",
+			MAC2STR(mgmt->sa));
+		return;
+	}
+
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+			WLAN_STA_ASSOC_REQ_OK);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+		MAC2STR(sta->addr));
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+	mlme_deauthenticate_indication(
+		hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	ap_free_sta(hapd, sta);
+}
+
+
+static void handle_beacon(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  struct hostapd_frame_info *fi)
+{
+	struct ieee802_11_elems elems;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
+		printf("handle_beacon - too short payload (len=%lu)\n",
+		       (unsigned long) len);
+		return;
+	}
+
+	(void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
+				      len - (IEEE80211_HDRLEN +
+					     sizeof(mgmt->u.beacon)), &elems,
+				      0);
+
+	ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+				  const u8 *addr, const u8 *trans_id)
+{
+	struct ieee80211_mgmt mgmt;
+	u8 *end;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+		   MACSTR, MAC2STR(addr));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+	os_memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt.da, addr, ETH_ALEN);
+	os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+	mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+	mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+	os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+		  WLAN_SA_QUERY_TR_ID_LEN);
+	end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+	if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
+		perror("ieee802_11_send_sa_query_req: send");
+}
+
+
+static void hostapd_sa_query_request(struct hostapd_data *hapd,
+				     const struct ieee80211_mgmt *mgmt)
+{
+	struct sta_info *sta;
+	struct ieee80211_mgmt resp;
+	u8 *end;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+		   MACSTR, MAC2STR(mgmt->sa));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    mgmt->u.action.u.sa_query_resp.trans_id,
+		    WLAN_SA_QUERY_TR_ID_LEN);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+			   "from unassociated STA " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+		   MACSTR, MAC2STR(mgmt->sa));
+
+	os_memset(&resp, 0, sizeof(resp));
+	resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_ACTION);
+	os_memcpy(resp.da, mgmt->sa, ETH_ALEN);
+	os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
+	resp.u.action.category = WLAN_ACTION_SA_QUERY;
+	resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+	os_memcpy(resp.u.action.u.sa_query_req.trans_id,
+		  mgmt->u.action.u.sa_query_req.trans_id,
+		  WLAN_SA_QUERY_TR_ID_LEN);
+	end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+	if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp) < 0)
+		perror("hostapd_sa_query_request: send");
+}
+
+
+static void hostapd_sa_query_action(struct hostapd_data *hapd,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len)
+{
+	struct sta_info *sta;
+	const u8 *end;
+	int i;
+
+	end = mgmt->u.action.u.sa_query_resp.trans_id +
+		WLAN_SA_QUERY_TR_ID_LEN;
+	if (((u8 *) mgmt) + len < end) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
+			   "frame (len=%lu)", (unsigned long) len);
+		return;
+	}
+
+	if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) {
+		hostapd_sa_query_request(hapd, mgmt);
+		return;
+	}
+
+	if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+			   "Action %d", mgmt->u.action.u.sa_query_resp.action);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+		   MACSTR, MAC2STR(mgmt->sa));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    mgmt->u.action.u.sa_query_resp.trans_id,
+		    WLAN_SA_QUERY_TR_ID_LEN);
+
+	/* MLME-SAQuery.confirm */
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL || sta->sa_query_trans_id == NULL) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+			   "pending SA Query request found");
+		return;
+	}
+
+	for (i = 0; i < sta->sa_query_count; i++) {
+		if (os_memcmp(sta->sa_query_trans_id +
+			      i * WLAN_SA_QUERY_TR_ID_LEN,
+			      mgmt->u.action.u.sa_query_resp.trans_id,
+			      WLAN_SA_QUERY_TR_ID_LEN) == 0)
+			break;
+	}
+
+	if (i >= sta->sa_query_count) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+			   "transaction identifier found");
+		return;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Reply to pending SA Query received");
+	ap_sta_stop_sa_query(hapd, sta);
+}
+
+
+static int robust_action_frame(u8 category)
+{
+	return category != WLAN_ACTION_PUBLIC &&
+		category != WLAN_ACTION_HT;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static void handle_action(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + 1) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "handle_action - too short payload (len=%lu)",
+			       (unsigned long) len);
+		return;
+	}
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211W
+	if (sta && (sta->flags & WLAN_STA_MFP) &&
+	    !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
+	      robust_action_frame(mgmt->u.action.category))) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Dropped unprotected Robust Action frame from "
+			       "an MFP STA");
+		return;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	switch (mgmt->u.action.category) {
+#ifdef CONFIG_IEEE80211R
+	case WLAN_ACTION_FT:
+	{
+		if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+			wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action "
+				   "frame from unassociated STA " MACSTR,
+				   MAC2STR(mgmt->sa));
+			return;
+		}
+
+		if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+				     len - IEEE80211_HDRLEN))
+			break;
+
+		return;
+	}
+#endif /* CONFIG_IEEE80211R */
+	case WLAN_ACTION_WMM:
+		hostapd_wmm_action(hapd, mgmt, len);
+		return;
+#ifdef CONFIG_IEEE80211W
+	case WLAN_ACTION_SA_QUERY:
+		hostapd_sa_query_action(hapd, mgmt, len);
+		return;
+#endif /* CONFIG_IEEE80211W */
+	case WLAN_ACTION_PUBLIC:
+		if (hapd->public_action_cb) {
+			hapd->public_action_cb(hapd->public_action_cb_ctx,
+					       (u8 *) mgmt, len,
+					       hapd->iface->freq);
+			return;
+		}
+		break;
+	case WLAN_ACTION_VENDOR_SPECIFIC:
+		if (hapd->vendor_action_cb) {
+			if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
+						   (u8 *) mgmt, len,
+						   hapd->iface->freq) == 0)
+				return;
+		}
+		break;
+	}
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "handle_action - unknown action category %d or invalid "
+		       "frame",
+		       mgmt->u.action.category);
+	if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) &&
+	    !(mgmt->sa[0] & 0x01)) {
+		struct ieee80211_mgmt *resp;
+
+		/*
+		 * IEEE 802.11-REVma/D9.0 - 7.3.1.11
+		 * Return the Action frame to the source without change
+		 * except that MSB of the Category set to 1.
+		 */
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
+			   "frame back to sender");
+		resp = os_malloc(len);
+		if (resp == NULL)
+			return;
+		os_memcpy(resp, mgmt, len);
+		os_memcpy(resp->da, resp->sa, ETH_ALEN);
+		os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+		resp->u.action.category |= 0x80;
+
+		hostapd_drv_send_mlme(hapd, resp, len);
+		os_free(resp);
+	}
+}
+
+
+/**
+ * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
+ * @hapd: hostapd BSS data structure (the BSS to which the management frame was
+ * sent to)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @fi: meta data about received frame (signal level, etc.)
+ *
+ * Process all incoming IEEE 802.11 management frames. This will be called for
+ * each frame received from the kernel driver through wlan#ap interface. In
+ * addition, it can be called to re-inserted pending frames (e.g., when using
+ * external RADIUS server as an MAC ACL).
+ */
+void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+		     struct hostapd_frame_info *fi)
+{
+	struct ieee80211_mgmt *mgmt;
+	int broadcast;
+	u16 fc, stype;
+
+	if (len < 24)
+		return;
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (stype == WLAN_FC_STYPE_BEACON) {
+		handle_beacon(hapd, mgmt, len, fi);
+		return;
+	}
+
+	broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
+		mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff &&
+		mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
+
+	if (!broadcast &&
+	    os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+		printf("MGMT: BSSID=" MACSTR " not our address\n",
+		       MAC2STR(mgmt->bssid));
+		return;
+	}
+
+
+	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+		handle_probe_req(hapd, mgmt, len);
+		return;
+	}
+
+	if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "MGMT: DA=" MACSTR " not our address",
+			       MAC2STR(mgmt->da));
+		return;
+	}
+
+	switch (stype) {
+	case WLAN_FC_STYPE_AUTH:
+		wpa_printf(MSG_DEBUG, "mgmt::auth");
+		handle_auth(hapd, mgmt, len);
+		break;
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
+		handle_assoc(hapd, mgmt, len, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
+		handle_assoc(hapd, mgmt, len, 1);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		wpa_printf(MSG_DEBUG, "mgmt::disassoc");
+		handle_disassoc(hapd, mgmt, len);
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		wpa_msg(hapd, MSG_DEBUG, "mgmt::deauth");
+		handle_deauth(hapd, mgmt, len);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		wpa_printf(MSG_DEBUG, "mgmt::action");
+		handle_action(hapd, mgmt, len);
+		break;
+	default:
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "unknown mgmt frame subtype %d", stype);
+		break;
+	}
+}
+
+
+static void handle_auth_cb(struct hostapd_data *hapd,
+			   const struct ieee80211_mgmt *mgmt,
+			   size_t len, int ok)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	struct sta_info *sta;
+
+	if (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_NOTICE,
+			       "did not acknowledge authentication response");
+		return;
+	}
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+		printf("handle_auth_cb - too short payload (len=%lu)\n",
+		       (unsigned long) len);
+		return;
+	}
+
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		printf("handle_auth_cb: STA " MACSTR " not found\n",
+		       MAC2STR(mgmt->da));
+		return;
+	}
+
+	if (status_code == WLAN_STATUS_SUCCESS &&
+	    ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+	     (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "authenticated");
+		sta->flags |= WLAN_STA_AUTH;
+	}
+}
+
+
+static void handle_assoc_cb(struct hostapd_data *hapd,
+			    const struct ieee80211_mgmt *mgmt,
+			    size_t len, int reassoc, int ok)
+{
+	u16 status;
+	struct sta_info *sta;
+	int new_assoc = 1;
+	struct ieee80211_ht_capabilities ht_cap;
+
+	if (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "did not acknowledge association response");
+		return;
+	}
+
+	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
+				      sizeof(mgmt->u.assoc_resp))) {
+		printf("handle_assoc_cb(reassoc=%d) - too short payload "
+		       "(len=%lu)\n", reassoc, (unsigned long) len);
+		return;
+	}
+
+	if (reassoc)
+		status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+	else
+		status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		printf("handle_assoc_cb: STA " MACSTR " not found\n",
+		       MAC2STR(mgmt->da));
+		return;
+	}
+
+	if (status != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	/* Stop previous accounting session, if one is started, and allocate
+	 * new session id for the new session. */
+	accounting_sta_stop(hapd, sta);
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "associated (aid %d)",
+		       sta->aid);
+
+	if (sta->flags & WLAN_STA_ASSOC)
+		new_assoc = 0;
+	sta->flags |= WLAN_STA_ASSOC;
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+	    sta->auth_alg == WLAN_AUTH_FT) {
+		/*
+		 * Open, static WEP, or FT protocol; no separate authorization
+		 * step.
+		 */
+		ap_sta_set_authorized(hapd, sta, 1);
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+	}
+
+	if (reassoc)
+		mlme_reassociate_indication(hapd, sta);
+	else
+		mlme_associate_indication(hapd, sta);
+
+#ifdef CONFIG_IEEE80211W
+	sta->sa_query_timed_out = 0;
+#endif /* CONFIG_IEEE80211W */
+
+	/*
+	 * Remove the STA entry in order to make sure the STA PS state gets
+	 * cleared and configuration gets updated in case of reassociation back
+	 * to the same AP.
+	 */
+	hostapd_drv_sta_remove(hapd, sta->addr);
+
+#ifdef CONFIG_IEEE80211N
+	if (sta->flags & WLAN_STA_HT)
+		hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
+#endif /* CONFIG_IEEE80211N */
+
+	if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+			    sta->supported_rates, sta->supported_rates_len,
+			    sta->listen_interval,
+			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_NOTICE,
+			       "Could not add STA to kernel driver");
+	}
+
+	if (sta->flags & WLAN_STA_WDS)
+		hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+
+	if (sta->eapol_sm == NULL) {
+		/*
+		 * This STA does not use RADIUS server for EAP authentication,
+		 * so bind it to the selected VLAN interface now, since the
+		 * interface selection is not going to change anymore.
+		 */
+		if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
+			goto fail;
+	} else if (sta->vlan_id) {
+		/* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
+		if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
+			goto fail;
+	}
+
+	hostapd_set_sta_flags(hapd, sta);
+
+	if (sta->auth_alg == WLAN_AUTH_FT)
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+	else
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+	hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
+
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+ fail:
+	/* Copy of the association request is not needed anymore */
+	if (sta->last_assoc_req) {
+		os_free(sta->last_assoc_req);
+		sta->last_assoc_req = NULL;
+	}
+}
+
+
+/**
+ * ieee802_11_mgmt_cb - Process management frame TX status callback
+ * @hapd: hostapd BSS data structure (the BSS from which the management frame
+ * was sent from)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @stype: management frame subtype from frame control field
+ * @ok: Whether the frame was ACK'ed
+ */
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+			u16 stype, int ok)
+{
+	const struct ieee80211_mgmt *mgmt;
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+	switch (stype) {
+	case WLAN_FC_STYPE_AUTH:
+		wpa_printf(MSG_DEBUG, "mgmt::auth cb");
+		handle_auth_cb(hapd, mgmt, len, ok);
+		break;
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
+		handle_assoc_cb(hapd, mgmt, len, 0, ok);
+		break;
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
+		handle_assoc_cb(hapd, mgmt, len, 1, ok);
+		break;
+	case WLAN_FC_STYPE_PROBE_RESP:
+		wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		/* ignore */
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		wpa_printf(MSG_DEBUG, "mgmt::action cb");
+		break;
+	default:
+		printf("unknown mgmt cb frame subtype %d\n", stype);
+		break;
+	}
+}
+
+
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+		       const u8 *buf, size_t len, int ack)
+{
+	struct sta_info *sta;
+	struct hostapd_iface *iface = hapd->iface;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL && iface->num_bss > 1) {
+		size_t j;
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			sta = ap_get_sta(hapd, addr);
+			if (sta)
+				break;
+		}
+	}
+	if (sta == NULL)
+		return;
+	if (sta->flags & WLAN_STA_PENDING_POLL) {
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
+			   "activity poll", MAC2STR(sta->addr),
+			   ack ? "ACKed" : "did not ACK");
+		if (ack)
+			sta->flags &= ~WLAN_STA_PENDING_POLL;
+	}
+
+	ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+}
+
+
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+				int wds)
+{
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, src);
+	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+		if (wds && !(sta->flags & WLAN_STA_WDS)) {
+			wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
+				   "STA " MACSTR " (aid %u)",
+				   MAC2STR(sta->addr), sta->aid);
+			sta->flags |= WLAN_STA_WDS;
+			hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+		}
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
+		   MACSTR, MAC2STR(src));
+	if (src[0] & 0x01) {
+		/* Broadcast bit set in SA?! Ignore the frame silently. */
+		return;
+	}
+
+	if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
+		wpa_printf(MSG_DEBUG, "Association Response to the STA has "
+			   "already been sent, but no TX status yet known - "
+			   "ignore Class 3 frame issue with " MACSTR,
+			   MAC2STR(src));
+		return;
+	}
+
+	if (sta && (sta->flags & WLAN_STA_AUTH))
+		hostapd_drv_sta_disassoc(
+			hapd, src,
+			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+	else
+		hostapd_drv_sta_deauth(
+			hapd, src,
+			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+}
+
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
new file mode 100644
index 0000000..157198c
--- /dev/null
+++ b/src/ap/ieee802_11.h
@@ -0,0 +1,68 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 IEEE802_11_H
+#define IEEE802_11_H
+
+struct hostapd_iface;
+struct hostapd_data;
+struct sta_info;
+struct hostapd_frame_info;
+struct ieee80211_ht_capabilities;
+
+void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+		     struct hostapd_frame_info *fi);
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+			u16 stype, int ok);
+void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len);
+#ifdef NEED_AP_MLME
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen);
+#else /* NEED_AP_MLME */
+static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
+				     size_t buflen)
+{
+	return 0;
+}
+
+static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
+					 struct sta_info *sta,
+					 char *buf, size_t buflen)
+{
+	return 0;
+}
+#endif /* NEED_AP_MLME */
+u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
+			   int probe);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+int hostapd_ht_operation_update(struct hostapd_iface *iface);
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+				  const u8 *addr, const u8 *trans_id);
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+			  struct ieee80211_ht_capabilities *ht_cap,
+			  struct ieee80211_ht_capabilities *neg_ht_cap);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ht_capab, size_t ht_capab_len);
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+		       const u8 *buf, size_t len, int ack);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+				int wds);
+
+#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
new file mode 100644
index 0000000..b933263
--- /dev/null
+++ b/src/ap/ieee802_11_auth.c
@@ -0,0 +1,524 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Access control list for IEEE 802.11 authentication can uses statically
+ * configured ACL from configuration files or an external RADIUS server.
+ * Results from external RADIUS queries are cached to allow faster
+ * authentication frame processing.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+
+#define RADIUS_ACL_TIMEOUT 30
+
+
+struct hostapd_cached_radius_acl {
+	os_time_t timestamp;
+	macaddr addr;
+	int accepted; /* HOSTAPD_ACL_* */
+	struct hostapd_cached_radius_acl *next;
+	u32 session_timeout;
+	u32 acct_interim_interval;
+	int vlan_id;
+};
+
+
+struct hostapd_acl_query_data {
+	os_time_t timestamp;
+	u8 radius_id;
+	macaddr addr;
+	u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
+	size_t auth_msg_len;
+	struct hostapd_acl_query_data *next;
+};
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
+{
+	struct hostapd_cached_radius_acl *prev;
+
+	while (acl_cache) {
+		prev = acl_cache;
+		acl_cache = acl_cache->next;
+		os_free(prev);
+	}
+}
+
+
+static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
+				 u32 *session_timeout,
+				 u32 *acct_interim_interval, int *vlan_id)
+{
+	struct hostapd_cached_radius_acl *entry;
+	struct os_time now;
+
+	os_get_time(&now);
+	entry = hapd->acl_cache;
+
+	while (entry) {
+		if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+			if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
+				return -1; /* entry has expired */
+			if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+				if (session_timeout)
+					*session_timeout =
+						entry->session_timeout;
+			if (acct_interim_interval)
+				*acct_interim_interval =
+					entry->acct_interim_interval;
+			if (vlan_id)
+				*vlan_id = entry->vlan_id;
+			return entry->accepted;
+		}
+
+		entry = entry->next;
+	}
+
+	return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
+{
+	if (query == NULL)
+		return;
+	os_free(query->auth_msg);
+	os_free(query);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
+				    struct hostapd_acl_query_data *query)
+{
+	struct radius_msg *msg;
+	char buf[128];
+
+	query->radius_id = radius_client_get_id(hapd->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
+	if (msg == NULL)
+		return -1;
+
+	radius_msg_make_authenticator(msg, addr, ETH_ALEN);
+
+	os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
+				 os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add User-Name");
+		goto fail;
+	}
+
+	if (!radius_msg_add_attr_user_password(
+		    msg, (u8 *) buf, os_strlen(buf),
+		    hapd->conf->radius->auth_server->shared_secret,
+		    hapd->conf->radius->auth_server->shared_secret_len)) {
+		wpa_printf(MSG_DEBUG, "Could not add User-Password");
+		goto fail;
+	}
+
+	if (hapd->conf->own_ip_addr.af == AF_INET &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+		wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address");
+		goto fail;
+	}
+
+#ifdef CONFIG_IPV6
+	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+		wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address");
+		goto fail;
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (hapd->conf->nas_identifier &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+				 (u8 *) hapd->conf->nas_identifier,
+				 os_strlen(hapd->conf->nas_identifier))) {
+		wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(addr));
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
+		goto fail;
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+		wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
+		goto fail;
+	}
+
+	radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+	return 0;
+
+ fail:
+	radius_msg_free(msg);
+	return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_allowed_address - Check whether a specified STA can be authenticated
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @msg: Authentication message
+ * @len: Length of msg in octets
+ * @session_timeout: Buffer for returning session timeout (from RADIUS)
+ * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
+ * @vlan_id: Buffer for returning VLAN ID
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ */
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			    const u8 *msg, size_t len, u32 *session_timeout,
+			    u32 *acct_interim_interval, int *vlan_id)
+{
+	if (session_timeout)
+		*session_timeout = 0;
+	if (acct_interim_interval)
+		*acct_interim_interval = 0;
+	if (vlan_id)
+		*vlan_id = 0;
+
+	if (hostapd_maclist_found(hapd->conf->accept_mac,
+				  hapd->conf->num_accept_mac, addr, vlan_id))
+		return HOSTAPD_ACL_ACCEPT;
+
+	if (hostapd_maclist_found(hapd->conf->deny_mac,
+				  hapd->conf->num_deny_mac, addr, vlan_id))
+		return HOSTAPD_ACL_REJECT;
+
+	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+		return HOSTAPD_ACL_ACCEPT;
+	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+		return HOSTAPD_ACL_REJECT;
+
+	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
+#ifdef CONFIG_NO_RADIUS
+		return HOSTAPD_ACL_REJECT;
+#else /* CONFIG_NO_RADIUS */
+		struct hostapd_acl_query_data *query;
+
+		/* Check whether ACL cache has an entry for this station */
+		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
+						acct_interim_interval,
+						vlan_id);
+		if (res == HOSTAPD_ACL_ACCEPT ||
+		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+			return res;
+		if (res == HOSTAPD_ACL_REJECT)
+			return HOSTAPD_ACL_REJECT;
+
+		query = hapd->acl_queries;
+		while (query) {
+			if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+				/* pending query in RADIUS retransmit queue;
+				 * do not generate a new one */
+				return HOSTAPD_ACL_PENDING;
+			}
+			query = query->next;
+		}
+
+		if (!hapd->conf->radius->auth_server)
+			return HOSTAPD_ACL_REJECT;
+
+		/* No entry in the cache - query external RADIUS server */
+		query = os_zalloc(sizeof(*query));
+		if (query == NULL) {
+			wpa_printf(MSG_ERROR, "malloc for query data failed");
+			return HOSTAPD_ACL_REJECT;
+		}
+		time(&query->timestamp);
+		os_memcpy(query->addr, addr, ETH_ALEN);
+		if (hostapd_radius_acl_query(hapd, addr, query)) {
+			wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
+				   "for ACL query.");
+			hostapd_acl_query_free(query);
+			return HOSTAPD_ACL_REJECT;
+		}
+
+		query->auth_msg = os_malloc(len);
+		if (query->auth_msg == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+				   "auth frame.");
+			hostapd_acl_query_free(query);
+			return HOSTAPD_ACL_REJECT;
+		}
+		os_memcpy(query->auth_msg, msg, len);
+		query->auth_msg_len = len;
+		query->next = hapd->acl_queries;
+		hapd->acl_queries = query;
+
+		/* Queued data will be processed in hostapd_acl_recv_radius()
+		 * when RADIUS server replies to the sent Access-Request. */
+		return HOSTAPD_ACL_PENDING;
+#endif /* CONFIG_NO_RADIUS */
+	}
+
+	return HOSTAPD_ACL_REJECT;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
+{
+	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
+
+	prev = NULL;
+	entry = hapd->acl_cache;
+
+	while (entry) {
+		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
+				   " has expired.", MAC2STR(entry->addr));
+			if (prev)
+				prev->next = entry->next;
+			else
+				hapd->acl_cache = entry->next;
+			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
+			tmp = entry;
+			entry = entry->next;
+			os_free(tmp);
+			continue;
+		}
+
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
+				       os_time_t now)
+{
+	struct hostapd_acl_query_data *prev, *entry, *tmp;
+
+	prev = NULL;
+	entry = hapd->acl_queries;
+
+	while (entry) {
+		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+			wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
+				   " has expired.", MAC2STR(entry->addr));
+			if (prev)
+				prev->next = entry->next;
+			else
+				hapd->acl_queries = entry->next;
+
+			tmp = entry;
+			entry = entry->next;
+			hostapd_acl_query_free(tmp);
+			continue;
+		}
+
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+/**
+ * hostapd_acl_expire - ACL cache expiration callback
+ * @eloop_ctx: struct hostapd_data *
+ * @timeout_ctx: Not used
+ */
+static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct os_time now;
+
+	os_get_time(&now);
+	hostapd_acl_expire_cache(hapd, now.sec);
+	hostapd_acl_expire_queries(hapd, now.sec);
+
+	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
+}
+
+
+/**
+ * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
+ * was processed here) or RADIUS_RX_UNKNOWN if not.
+ */
+static RadiusRxResult
+hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
+			const u8 *shared_secret, size_t shared_secret_len,
+			void *data)
+{
+	struct hostapd_data *hapd = data;
+	struct hostapd_acl_query_data *query, *prev;
+	struct hostapd_cached_radius_acl *cache;
+	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+	query = hapd->acl_queries;
+	prev = NULL;
+	while (query) {
+		if (query->radius_id == hdr->identifier)
+			break;
+		prev = query;
+		query = query->next;
+	}
+	if (query == NULL)
+		return RADIUS_RX_UNKNOWN;
+
+	wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
+		   "message (id=%d)", query->radius_id);
+
+	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
+			   "correct authenticator - dropped\n");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+	    hdr->code != RADIUS_CODE_ACCESS_REJECT) {
+		wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
+			   "query", hdr->code);
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	/* Insert Accept/Reject info into ACL cache */
+	cache = os_zalloc(sizeof(*cache));
+	if (cache == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
+		goto done;
+	}
+	time(&cache->timestamp);
+	os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
+	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+					      &cache->session_timeout) == 0)
+			cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
+		else
+			cache->accepted = HOSTAPD_ACL_ACCEPT;
+
+		if (radius_msg_get_attr_int32(
+			    msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+			    &cache->acct_interim_interval) == 0 &&
+		    cache->acct_interim_interval < 60) {
+			wpa_printf(MSG_DEBUG, "Ignored too small "
+				   "Acct-Interim-Interval %d for STA " MACSTR,
+				   cache->acct_interim_interval,
+				   MAC2STR(query->addr));
+			cache->acct_interim_interval = 0;
+		}
+
+		cache->vlan_id = radius_msg_get_vlanid(msg);
+	} else
+		cache->accepted = HOSTAPD_ACL_REJECT;
+	cache->next = hapd->acl_cache;
+	hapd->acl_cache = cache;
+
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+	hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
+					cache->session_timeout);
+#else /* CONFIG_DRIVER_RADIUS_ACL */
+#ifdef NEED_AP_MLME
+	/* Re-send original authentication frame for 802.11 processing */
+	wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
+		   "successful RADIUS ACL query");
+	ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_DRIVER_RADIUS_ACL */
+
+ done:
+	if (prev == NULL)
+		hapd->acl_queries = query->next;
+	else
+		prev->next = query->next;
+
+	hostapd_acl_query_free(query);
+
+	return RADIUS_RX_PROCESSED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_acl_init: Initialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int hostapd_acl_init(struct hostapd_data *hapd)
+{
+#ifndef CONFIG_NO_RADIUS
+	if (radius_client_register(hapd->radius, RADIUS_AUTH,
+				   hostapd_acl_recv_radius, hapd))
+		return -1;
+
+	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
+#endif /* CONFIG_NO_RADIUS */
+
+	return 0;
+}
+
+
+/**
+ * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ */
+void hostapd_acl_deinit(struct hostapd_data *hapd)
+{
+	struct hostapd_acl_query_data *query, *prev;
+
+#ifndef CONFIG_NO_RADIUS
+	eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
+
+	hostapd_acl_cache_free(hapd->acl_cache);
+#endif /* CONFIG_NO_RADIUS */
+
+	query = hapd->acl_queries;
+	while (query) {
+		prev = query;
+		query = query->next;
+		hostapd_acl_query_free(prev);
+	}
+}
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
new file mode 100644
index 0000000..b2971e5
--- /dev/null
+++ b/src/ap/ieee802_11_auth.h
@@ -0,0 +1,31 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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 IEEE802_11_AUTH_H
+#define IEEE802_11_AUTH_H
+
+enum {
+	HOSTAPD_ACL_REJECT = 0,
+	HOSTAPD_ACL_ACCEPT = 1,
+	HOSTAPD_ACL_PENDING = 2,
+	HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+};
+
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			    const u8 *msg, size_t len, u32 *session_timeout,
+			    u32 *acct_interim_interval, int *vlan_id);
+int hostapd_acl_init(struct hostapd_data *hapd);
+void hostapd_acl_deinit(struct hostapd_data *hapd);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
new file mode 100644
index 0000000..3dce5cb
--- /dev/null
+++ b/src/ap/ieee802_11_ht.c
@@ -0,0 +1,267 @@
+/*
+ * hostapd / IEEE 802.11n HT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008, Intel Corporation
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_ht_capabilities *cap;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+	    hapd->conf->disable_11n)
+		return eid;
+
+	*pos++ = WLAN_EID_HT_CAP;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_ht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
+	cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
+	os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
+		  16);
+
+	/* TODO: ht_extended_capabilities (now fully disabled) */
+	/* TODO: tx_bf_capability_info (now fully disabled) */
+	/* TODO: asel_capabilities (now fully disabled) */
+
+ 	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_ht_operation *oper;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+		return eid;
+
+	*pos++ = WLAN_EID_HT_OPERATION;
+	*pos++ = sizeof(*oper);
+
+	oper = (struct ieee80211_ht_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	oper->control_chan = hapd->iconf->channel;
+	oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
+	if (hapd->iconf->secondary_channel == 1)
+		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
+			HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+	if (hapd->iconf->secondary_channel == -1)
+		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+			HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+
+	pos += sizeof(*oper);
+
+	return pos;
+}
+
+
+/*
+op_mode
+Set to 0 (HT pure) under the followign conditions
+	- all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
+	- all STAs in the BSS are 20 MHz HT in 20 MHz BSS
+Set to 1 (HT non-member protection) if there may be non-HT STAs
+	in both the primary and the secondary channel
+Set to 2 if only HT STAs are associated in BSS,
+	however and at least one 20 MHz HT STA is associated
+Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
+*/
+int hostapd_ht_operation_update(struct hostapd_iface *iface)
+{
+	u16 cur_op_mode, new_op_mode;
+	int op_mode_changes = 0;
+
+	if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
+		   __func__, iface->ht_op_mode);
+
+	if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)
+	    && iface->num_sta_ht_no_gf) {
+		iface->ht_op_mode |=
+			HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+		op_mode_changes++;
+	} else if ((iface->ht_op_mode &
+		    HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+		   iface->num_sta_ht_no_gf == 0) {
+		iface->ht_op_mode &=
+			~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+		op_mode_changes++;
+	}
+
+	if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+	    (iface->num_sta_no_ht || iface->olbc_ht)) {
+		iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+		op_mode_changes++;
+	} else if ((iface->ht_op_mode &
+		    HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+		   (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
+		iface->ht_op_mode &=
+			~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+		op_mode_changes++;
+	}
+
+	new_op_mode = 0;
+	if (iface->num_sta_no_ht)
+		new_op_mode = OP_MODE_MIXED;
+	else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
+		 && iface->num_sta_ht_20mhz)
+		new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+	else if (iface->olbc_ht)
+		new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+	else
+		new_op_mode = OP_MODE_PURE;
+
+	cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+	if (cur_op_mode != new_op_mode) {
+		iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+		iface->ht_op_mode |= new_op_mode;
+		op_mode_changes++;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
+		   __func__, iface->ht_op_mode, op_mode_changes);
+
+	return op_mode_changes;
+}
+
+
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ht_capab, size_t ht_capab_len)
+{
+	/* Disable HT caps for STAs associated to no-HT BSSes. */
+	if (!ht_capab ||
+	    ht_capab_len < sizeof(struct ieee80211_ht_capabilities) ||
+	    hapd->conf->disable_11n) {
+		sta->flags &= ~WLAN_STA_HT;
+		os_free(sta->ht_capabilities);
+		sta->ht_capabilities = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (sta->ht_capabilities == NULL) {
+		sta->ht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+		if (sta->ht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_HT;
+	os_memcpy(sta->ht_capabilities, ht_capab,
+		  sizeof(struct ieee80211_ht_capabilities));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	u16 ht_capab;
+
+	ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info);
+	wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: "
+		   "0x%04x", MAC2STR(sta->addr), ht_capab);
+	if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) {
+		if (!sta->no_ht_gf_set) {
+			sta->no_ht_gf_set = 1;
+			hapd->iface->num_sta_ht_no_gf++;
+		}
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num "
+			   "of non-gf stations %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_ht_no_gf);
+	}
+	if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
+		if (!sta->ht_20mhz_set) {
+			sta->ht_20mhz_set = 1;
+			hapd->iface->num_sta_ht_20mhz++;
+		}
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of "
+			   "20MHz HT STAs %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_ht_20mhz);
+	}
+}
+
+
+static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (!sta->no_ht_set) {
+		sta->no_ht_set = 1;
+		hapd->iface->num_sta_no_ht++;
+	}
+	if (hapd->iconf->ieee80211n) {
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of "
+			   "non-HT stations %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_no_ht);
+	}
+}
+
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
+		update_sta_ht(hapd, sta);
+	else
+		update_sta_no_ht(hapd, sta);
+
+	if (hostapd_ht_operation_update(hapd->iface) > 0)
+		ieee802_11_set_beacons(hapd->iface);
+}
+
+
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+			  struct ieee80211_ht_capabilities *ht_cap,
+			  struct ieee80211_ht_capabilities *neg_ht_cap)
+{
+	u16 cap;
+
+	if (ht_cap == NULL)
+		return;
+	os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
+	cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
+	cap &= hapd->iconf->ht_capab;
+	cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED);
+
+	/*
+	 * STBC needs to be handled specially
+	 * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+	 * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+	 */
+	if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
+		cap &= ~HT_CAP_INFO_TX_STBC;
+	if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
+		cap &= ~HT_CAP_INFO_RX_STBC_MASK;
+
+	neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
new file mode 100644
index 0000000..ac0c127
--- /dev/null
+++ b/src/ap/ieee802_1x.c
@@ -0,0 +1,2085 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "pmksa_cache_auth.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_1x.h"
+
+
+static void ieee802_1x_finished(struct hostapd_data *hapd,
+				struct sta_info *sta, int success);
+
+
+static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
+			    u8 type, const u8 *data, size_t datalen)
+{
+	u8 *buf;
+	struct ieee802_1x_hdr *xhdr;
+	size_t len;
+	int encrypt = 0;
+
+	len = sizeof(*xhdr) + datalen;
+	buf = os_zalloc(len);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "malloc() failed for "
+			   "ieee802_1x_send(len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	xhdr = (struct ieee802_1x_hdr *) buf;
+	xhdr->version = hapd->conf->eapol_version;
+	xhdr->type = type;
+	xhdr->length = host_to_be16(datalen);
+
+	if (datalen > 0 && data != NULL)
+		os_memcpy(xhdr + 1, data, datalen);
+
+	if (wpa_auth_pairwise_set(sta->wpa_sm))
+		encrypt = 1;
+	if (sta->flags & WLAN_STA_PREAUTH) {
+		rsn_preauth_send(hapd, sta, buf, len);
+	} else {
+		hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len,
+					    encrypt, sta->flags);
+	}
+
+	os_free(buf);
+}
+
+
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+				   struct sta_info *sta, int authorized)
+{
+	int res;
+
+	if (sta->flags & WLAN_STA_PREAUTH)
+		return;
+
+	if (authorized) {
+		if (!ap_sta_is_authorized(sta))
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+		ap_sta_set_authorized(hapd, sta, 1);
+		res = hostapd_set_authorized(hapd, sta, 1);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "authorizing port");
+	} else {
+		if (ap_sta_is_authorized(sta) && (sta->flags & WLAN_STA_ASSOC))
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				AP_STA_DISCONNECTED MACSTR,
+				MAC2STR(sta->addr));
+		ap_sta_set_authorized(hapd, sta, 0);
+		res = hostapd_set_authorized(hapd, sta, 0);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
+	}
+
+	if (res && errno != ENOENT) {
+		printf("Could not set station " MACSTR " flags for kernel "
+		       "driver (errno=%d).\n", MAC2STR(sta->addr), errno);
+	}
+
+	if (authorized)
+		accounting_sta_start(hapd, sta);
+}
+
+
+static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  int idx, int broadcast,
+				  u8 *key_data, size_t key_len)
+{
+	u8 *buf, *ekey;
+	struct ieee802_1x_hdr *hdr;
+	struct ieee802_1x_eapol_key *key;
+	size_t len, ekey_len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	len = sizeof(*key) + key_len;
+	buf = os_zalloc(sizeof(*hdr) + len);
+	if (buf == NULL)
+		return;
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+	key->type = EAPOL_KEY_TYPE_RC4;
+	key->key_length = htons(key_len);
+	wpa_get_ntp_timestamp(key->replay_counter);
+
+	if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
+		wpa_printf(MSG_ERROR, "Could not get random numbers");
+		os_free(buf);
+		return;
+	}
+
+	key->key_index = idx | (broadcast ? 0 : BIT(7));
+	if (hapd->conf->eapol_key_index_workaround) {
+		/* According to some information, WinXP Supplicant seems to
+		 * interpret bit7 as an indication whether the key is to be
+		 * activated, so make it possible to enable workaround that
+		 * sets this bit for all keys. */
+		key->key_index |= BIT(7);
+	}
+
+	/* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and
+	 * MSK[32..63] is used to sign the message. */
+	if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) {
+		wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting "
+			   "and signing EAPOL-Key");
+		os_free(buf);
+		return;
+	}
+	os_memcpy((u8 *) (key + 1), key_data, key_len);
+	ekey_len = sizeof(key->key_iv) + 32;
+	ekey = os_malloc(ekey_len);
+	if (ekey == NULL) {
+		wpa_printf(MSG_ERROR, "Could not encrypt key");
+		os_free(buf);
+		return;
+	}
+	os_memcpy(ekey, key->key_iv, sizeof(key->key_iv));
+	os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32);
+	rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len);
+	os_free(ekey);
+
+	/* This header is needed here for HMAC-MD5, but it will be regenerated
+	 * in ieee802_1x_send() */
+	hdr->version = hapd->conf->eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = host_to_be16(len);
+	hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
+		 key->key_signature);
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR
+		   " (%s index=%d)", MAC2STR(sm->addr),
+		   broadcast ? "broadcast" : "unicast", idx);
+	ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len);
+	if (sta->eapol_sm)
+		sta->eapol_sm->dot1xAuthEapolFramesTx++;
+	os_free(buf);
+}
+
+
+#ifndef CONFIG_NO_VLAN
+static struct hostapd_wep_keys *
+ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
+{
+	struct hostapd_wep_keys *key;
+
+	key = os_zalloc(sizeof(*key));
+	if (key == NULL)
+		return NULL;
+
+	key->default_len = hapd->conf->default_wep_key_len;
+
+	if (key->idx >= hapd->conf->broadcast_key_idx_max ||
+	    key->idx < hapd->conf->broadcast_key_idx_min)
+		key->idx = hapd->conf->broadcast_key_idx_min;
+	else
+		key->idx++;
+
+	if (!key->key[key->idx])
+		key->key[key->idx] = os_malloc(key->default_len);
+	if (key->key[key->idx] == NULL ||
+	    random_get_bytes(key->key[key->idx], key->default_len)) {
+		printf("Could not generate random WEP key (dynamic VLAN).\n");
+		os_free(key->key[key->idx]);
+		key->key[key->idx] = NULL;
+		os_free(key);
+		return NULL;
+	}
+	key->len[key->idx] = key->default_len;
+
+	wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n",
+		   ifname, key->idx);
+	wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)",
+			key->key[key->idx], key->len[key->idx]);
+
+	if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+				broadcast_ether_addr, key->idx, 1,
+				NULL, 0, key->key[key->idx],
+				key->len[key->idx]))
+		printf("Could not set dynamic VLAN WEP encryption key.\n");
+
+	hostapd_set_drv_ieee8021x(hapd, ifname, 1);
+
+	return key;
+}
+
+
+static struct hostapd_wep_keys *
+ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid,
+		     size_t vlan_id)
+{
+	const char *ifname;
+
+	if (vlan_id == 0)
+		return &ssid->wep;
+
+	if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys &&
+	    ssid->dyn_vlan_keys[vlan_id])
+		return ssid->dyn_vlan_keys[vlan_id];
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group "
+		   "state machine for VLAN ID %lu",
+		   (unsigned long) vlan_id);
+
+	ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+	if (ifname == NULL) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - "
+			   "cannot create group key state machine",
+			   (unsigned long) vlan_id);
+		return NULL;
+	}
+
+	if (ssid->dyn_vlan_keys == NULL) {
+		int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
+		ssid->dyn_vlan_keys = os_zalloc(size);
+		if (ssid->dyn_vlan_keys == NULL)
+			return NULL;
+		ssid->max_dyn_vlan_keys = vlan_id;
+	}
+
+	if (ssid->max_dyn_vlan_keys < vlan_id) {
+		struct hostapd_wep_keys **na;
+		int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
+		na = os_realloc(ssid->dyn_vlan_keys, size);
+		if (na == NULL)
+			return NULL;
+		ssid->dyn_vlan_keys = na;
+		os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0,
+			  (vlan_id - ssid->max_dyn_vlan_keys) *
+			  sizeof(ssid->dyn_vlan_keys[0]));
+		ssid->max_dyn_vlan_keys = vlan_id;
+	}
+
+	ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname);
+
+	return ssid->dyn_vlan_keys[vlan_id];
+}
+#endif /* CONFIG_NO_VLAN */
+
+
+void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+#ifndef CONFIG_NO_VLAN
+	struct hostapd_wep_keys *key = NULL;
+	int vlan_id;
+#endif /* CONFIG_NO_VLAN */
+
+	if (sm == NULL || !sm->eap_if->eapKeyData)
+		return;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
+		   MAC2STR(sta->addr));
+
+#ifndef CONFIG_NO_VLAN
+	vlan_id = sta->vlan_id;
+	if (vlan_id < 0 || vlan_id > MAX_VLAN_ID)
+		vlan_id = 0;
+
+	if (vlan_id) {
+		key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id);
+		if (key && key->key[key->idx])
+			ieee802_1x_tx_key_one(hapd, sta, key->idx, 1,
+					      key->key[key->idx],
+					      key->len[key->idx]);
+	} else
+#endif /* CONFIG_NO_VLAN */
+	if (eapol->default_wep_key) {
+		ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
+				      eapol->default_wep_key,
+				      hapd->conf->default_wep_key_len);
+	}
+
+	if (hapd->conf->individual_wep_key_len > 0) {
+		u8 *ikey;
+		ikey = os_malloc(hapd->conf->individual_wep_key_len);
+		if (ikey == NULL ||
+		    random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
+		{
+			wpa_printf(MSG_ERROR, "Could not generate random "
+				   "individual WEP key.");
+			os_free(ikey);
+			return;
+		}
+
+		wpa_hexdump_key(MSG_DEBUG, "Individual WEP key",
+				ikey, hapd->conf->individual_wep_key_len);
+
+		ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey,
+				      hapd->conf->individual_wep_key_len);
+
+		/* TODO: set encryption in TX callback, i.e., only after STA
+		 * has ACKed EAPOL-Key frame */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+					sta->addr, 0, 1, NULL, 0, ikey,
+					hapd->conf->individual_wep_key_len)) {
+			wpa_printf(MSG_ERROR, "Could not set individual WEP "
+				   "encryption.");
+		}
+
+		os_free(ikey);
+	}
+}
+
+
+const char *radius_mode_txt(struct hostapd_data *hapd)
+{
+	switch (hapd->iface->conf->hw_mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		return "802.11a";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "802.11g";
+	case HOSTAPD_MODE_IEEE80211B:
+	default:
+		return "802.11b";
+	}
+}
+
+
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int i;
+	u8 rate = 0;
+
+	for (i = 0; i < sta->supported_rates_len; i++)
+		if ((sta->supported_rates[i] & 0x7f) > rate)
+			rate = sta->supported_rates[i] & 0x7f;
+
+	return rate;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
+				      struct eapol_state_machine *sm,
+				      const u8 *eap, size_t len)
+{
+	const u8 *identity;
+	size_t identity_len;
+
+	if (len <= sizeof(struct eap_hdr) ||
+	    eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY)
+		return;
+
+	identity = eap_get_identity(sm->eap, &identity_len);
+	if (identity == NULL)
+		return;
+
+	/* Save station identity for future RADIUS packets */
+	os_free(sm->identity);
+	sm->identity = os_malloc(identity_len + 1);
+	if (sm->identity == NULL) {
+		sm->identity_len = 0;
+		return;
+	}
+
+	os_memcpy(sm->identity, identity, identity_len);
+	sm->identity_len = identity_len;
+	sm->identity[identity_len] = '\0';
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
+	sm->dot1xAuthEapolRespIdFramesRx++;
+}
+
+
+static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  const u8 *eap, size_t len)
+{
+	struct radius_msg *msg;
+	char buf[128];
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	ieee802_1x_learn_identity(hapd, sm, eap, len);
+
+	wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
+		   "packet");
+
+	sm->radius_identifier = radius_client_get_id(hapd->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+			     sm->radius_identifier);
+	if (msg == NULL) {
+		printf("Could not create net RADIUS packet\n");
+		return;
+	}
+
+	radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+	if (sm->identity &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+				 sm->identity, sm->identity_len)) {
+		printf("Could not add User-Name\n");
+		goto fail;
+	}
+
+	if (hapd->conf->own_ip_addr.af == AF_INET &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+		printf("Could not add NAS-IP-Address\n");
+		goto fail;
+	}
+
+#ifdef CONFIG_IPV6
+	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+		printf("Could not add NAS-IPv6-Address\n");
+		goto fail;
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (hapd->conf->nas_identifier &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+				 (u8 *) hapd->conf->nas_identifier,
+				 os_strlen(hapd->conf->nas_identifier))) {
+		printf("Could not add NAS-Identifier\n");
+		goto fail;
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+		printf("Could not add NAS-Port\n");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
+	buf[sizeof(buf) - 1] = '\0';
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		printf("Could not add Called-Station-Id\n");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(sta->addr));
+	buf[sizeof(buf) - 1] = '\0';
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		printf("Could not add Calling-Station-Id\n");
+		goto fail;
+	}
+
+	/* TODO: should probably check MTU from driver config; 2304 is max for
+	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
+	 */
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+		printf("Could not add Framed-MTU\n");
+		goto fail;
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+		printf("Could not add NAS-Port-Type\n");
+		goto fail;
+	}
+
+	if (sta->flags & WLAN_STA_PREAUTH) {
+		os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
+			   sizeof(buf));
+	} else {
+		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+			    radius_sta_rate(hapd, sta) / 2,
+			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+			    radius_mode_txt(hapd));
+		buf[sizeof(buf) - 1] = '\0';
+	}
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		printf("Could not add Connect-Info\n");
+		goto fail;
+	}
+
+	if (eap && !radius_msg_add_eap(msg, eap, len)) {
+		printf("Could not add EAP-Message\n");
+		goto fail;
+	}
+
+	/* State attribute must be copied if and only if this packet is
+	 * Access-Request reply to the previous Access-Challenge */
+	if (sm->last_recv_radius &&
+	    radius_msg_get_hdr(sm->last_recv_radius)->code ==
+	    RADIUS_CODE_ACCESS_CHALLENGE) {
+		int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
+					       RADIUS_ATTR_STATE);
+		if (res < 0) {
+			printf("Could not copy State attribute from previous "
+			       "Access-Challenge\n");
+			goto fail;
+		}
+		if (res > 0) {
+			wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
+		}
+	}
+
+	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
+		goto fail;
+
+	return;
+
+ fail:
+	radius_msg_free(msg);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void handle_eap_response(struct hostapd_data *hapd,
+				struct sta_info *sta, struct eap_hdr *eap,
+				size_t len)
+{
+	u8 type, *data;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	if (sm == NULL)
+		return;
+
+	data = (u8 *) (eap + 1);
+
+	if (len < sizeof(*eap) + 1) {
+		printf("handle_eap_response: too short response data\n");
+		return;
+	}
+
+	sm->eap_type_supp = type = data[0];
+
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+		       "id=%d len=%d) from STA: EAP Response-%s (%d)",
+		       eap->code, eap->identifier, be_to_host16(eap->length),
+		       eap_server_get_name(0, type), type);
+
+	sm->dot1xAuthEapolRespFramesRx++;
+
+	wpabuf_free(sm->eap_if->eapRespData);
+	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+	sm->eapolEap = TRUE;
+}
+
+
+/* Process incoming EAP packet from Supplicant */
+static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
+		       u8 *buf, size_t len)
+{
+	struct eap_hdr *eap;
+	u16 eap_len;
+
+	if (len < sizeof(*eap)) {
+		printf("   too short EAP packet\n");
+		return;
+	}
+
+	eap = (struct eap_hdr *) buf;
+
+	eap_len = be_to_host16(eap->length);
+	wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d",
+		   eap->code, eap->identifier, eap_len);
+	if (eap_len < sizeof(*eap)) {
+		wpa_printf(MSG_DEBUG, "   Invalid EAP length");
+		return;
+	} else if (eap_len > len) {
+		wpa_printf(MSG_DEBUG, "   Too short frame to contain this EAP "
+			   "packet");
+		return;
+	} else if (eap_len < len) {
+		wpa_printf(MSG_DEBUG, "   Ignoring %lu extra bytes after EAP "
+			   "packet", (unsigned long) len - eap_len);
+	}
+
+	switch (eap->code) {
+	case EAP_CODE_REQUEST:
+		wpa_printf(MSG_DEBUG, " (request)");
+		return;
+	case EAP_CODE_RESPONSE:
+		wpa_printf(MSG_DEBUG, " (response)");
+		handle_eap_response(hapd, sta, eap, eap_len);
+		break;
+	case EAP_CODE_SUCCESS:
+		wpa_printf(MSG_DEBUG, " (success)");
+		return;
+	case EAP_CODE_FAILURE:
+		wpa_printf(MSG_DEBUG, " (failure)");
+		return;
+	default:
+		wpa_printf(MSG_DEBUG, " (unknown code)");
+		return;
+	}
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int flags = 0;
+	if (sta->flags & WLAN_STA_PREAUTH)
+		flags |= EAPOL_SM_PREAUTH;
+	if (sta->wpa_sm) {
+		flags |= EAPOL_SM_USES_WPA;
+		if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
+			flags |= EAPOL_SM_FROM_PMKSA_CACHE;
+	}
+	return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
+				sta->wps_ie, sta->p2p_ie, sta);
+}
+
+
+/**
+ * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
+ * @hapd: hostapd BSS data
+ * @sa: Source address (sender of the EAPOL frame)
+ * @buf: EAPOL frame
+ * @len: Length of buf in octets
+ *
+ * This function is called for each incoming EAPOL frame from the interface
+ */
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+			size_t len)
+{
+	struct sta_info *sta;
+	struct ieee802_1x_hdr *hdr;
+	struct ieee802_1x_eapol_key *key;
+	u16 datalen;
+	struct rsn_pmksa_cache_entry *pmksa;
+	int key_mgmt;
+
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+	    !hapd->conf->wps_state)
+		return;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
+		   (unsigned long) len, MAC2STR(sa));
+	sta = ap_get_sta(hapd, sa);
+	if (!sta || !(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH))) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
+			   "associated/Pre-authenticating STA");
+		return;
+	}
+
+	if (len < sizeof(*hdr)) {
+		printf("   too short IEEE 802.1X packet\n");
+		return;
+	}
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	datalen = be_to_host16(hdr->length);
+	wpa_printf(MSG_DEBUG, "   IEEE 802.1X: version=%d type=%d length=%d",
+		   hdr->version, hdr->type, datalen);
+
+	if (len - sizeof(*hdr) < datalen) {
+		printf("   frame too short for this IEEE 802.1X packet\n");
+		if (sta->eapol_sm)
+			sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
+		return;
+	}
+	if (len - sizeof(*hdr) > datalen) {
+		wpa_printf(MSG_DEBUG, "   ignoring %lu extra octets after "
+			   "IEEE 802.1X packet",
+			   (unsigned long) len - sizeof(*hdr) - datalen);
+	}
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
+		sta->eapol_sm->dot1xAuthEapolFramesRx++;
+	}
+
+	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+	if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
+	    hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+	    (key->type == EAPOL_KEY_TYPE_WPA ||
+	     key->type == EAPOL_KEY_TYPE_RSN)) {
+		wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr,
+			    sizeof(*hdr) + datalen);
+		return;
+	}
+
+	if (!hapd->conf->ieee802_1x &&
+	    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+			   "802.1X not enabled and WPS not used");
+		return;
+	}
+
+	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+			   "STA is using PSK");
+		return;
+	}
+
+	if (!sta->eapol_sm) {
+		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+		if (!sta->eapol_sm)
+			return;
+
+#ifdef CONFIG_WPS
+		if (!hapd->conf->ieee802_1x &&
+		    ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+		     WLAN_STA_MAYBE_WPS)) {
+			/*
+			 * Delay EAPOL frame transmission until a possible WPS
+			 * STA initiates the handshake with EAPOL-Start.
+			 */
+			sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+		}
+#endif /* CONFIG_WPS */
+
+		sta->eapol_sm->eap_if->portEnabled = TRUE;
+	}
+
+	/* since we support version 1, we can ignore version field and proceed
+	 * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
+	/* TODO: actually, we are not version 1 anymore.. However, Version 2
+	 * does not change frame contents, so should be ok to process frames
+	 * more or less identically. Some changes might be needed for
+	 * verification of fields. */
+
+	switch (hdr->type) {
+	case IEEE802_1X_TYPE_EAP_PACKET:
+		handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_START:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start "
+			       "from STA");
+		sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+		pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+		if (pmksa) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG, "cached PMKSA "
+				       "available - ignore it since "
+				       "STA sent EAPOL-Start");
+			wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
+		}
+		sta->eapol_sm->eapolStart = TRUE;
+		sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+		eap_server_clear_identity(sta->eapol_sm->eap);
+		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff "
+			       "from STA");
+		sta->acct_terminate_cause =
+			RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+		accounting_sta_stop(hapd, sta);
+		sta->eapol_sm->eapolLogoff = TRUE;
+		sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
+		eap_server_clear_identity(sta->eapol_sm->eap);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_KEY:
+		wpa_printf(MSG_DEBUG, "   EAPOL-Key");
+		if (!ap_sta_is_authorized(sta)) {
+			wpa_printf(MSG_DEBUG, "   Dropped key data from "
+				   "unauthorized Supplicant");
+			break;
+		}
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+		wpa_printf(MSG_DEBUG, "   EAPOL-Encapsulated-ASF-Alert");
+		/* TODO: implement support for this; show data */
+		break;
+
+	default:
+		wpa_printf(MSG_DEBUG, "   unknown IEEE 802.1X packet type");
+		sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
+		break;
+	}
+
+	eapol_auth_step(sta->eapol_sm);
+}
+
+
+/**
+ * ieee802_1x_new_station - Start IEEE 802.1X authentication
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ *
+ * This function is called to start IEEE 802.1X authentication when a new
+ * station completes IEEE 802.11 association.
+ */
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+	int reassoc = 1;
+	int force_1x = 0;
+	int key_mgmt;
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->conf->wpa &&
+	    (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+		/*
+		 * Need to enable IEEE 802.1X/EAPOL state machines for possible
+		 * WPS handshake even if IEEE 802.1X/EAPOL is not used for
+		 * authentication in this BSS.
+		 */
+		force_1x = 1;
+	}
+#endif /* CONFIG_WPS */
+
+	if (!force_1x && !hapd->conf->ieee802_1x) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
+			   "802.1X not enabled or forced for WPS");
+		return;
+	}
+
+	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+		return;
+	}
+
+	if (sta->eapol_sm == NULL) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "start authentication");
+		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+		if (sta->eapol_sm == NULL) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO,
+				       "failed to allocate state machine");
+			return;
+		}
+		reassoc = 0;
+	}
+
+#ifdef CONFIG_WPS
+	sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+	if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) {
+		/*
+		 * Delay EAPOL frame transmission until a possible WPS
+		 * initiates the handshake with EAPOL-Start.
+		 */
+		sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+	}
+#endif /* CONFIG_WPS */
+
+	sta->eapol_sm->eap_if->portEnabled = TRUE;
+
+#ifdef CONFIG_IEEE80211R
+	if (sta->auth_alg == WLAN_AUTH_FT) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from FT - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing FT information from R0KH. */
+		sta->eapol_sm->keyRun = TRUE;
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = TRUE;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		/* TODO: get vlan_id from R0KH using RRB message */
+		return;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+	if (pmksa) {
+		int old_vlanid;
+
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing PMKSA information in the cache. */
+		sta->eapol_sm->keyRun = TRUE;
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = TRUE;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		old_vlanid = sta->vlan_id;
+		pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm);
+		if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+			sta->vlan_id = 0;
+		ap_sta_bind_vlan(hapd, sta, old_vlanid);
+	} else {
+		if (reassoc) {
+			/*
+			 * Force EAPOL state machines to start
+			 * re-authentication without having to wait for the
+			 * Supplicant to send EAPOL-Start.
+			 */
+			sta->eapol_sm->reAuthenticate = TRUE;
+		}
+		eapol_auth_step(sta->eapol_sm);
+	}
+}
+
+
+void ieee802_1x_free_station(struct sta_info *sta)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	sta->eapol_sm = NULL;
+
+#ifndef CONFIG_NO_RADIUS
+	radius_msg_free(sm->last_recv_radius);
+	radius_free_class(&sm->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+	os_free(sm->identity);
+	eapol_auth_free(sm);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
+					  struct sta_info *sta)
+{
+	u8 *eap;
+	size_t len;
+	struct eap_hdr *hdr;
+	int eap_type = -1;
+	char buf[64];
+	struct radius_msg *msg;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL || sm->last_recv_radius == NULL) {
+		if (sm)
+			sm->eap_if->aaaEapNoReq = TRUE;
+		return;
+	}
+
+	msg = sm->last_recv_radius;
+
+	eap = radius_msg_get_eap(msg, &len);
+	if (eap == NULL) {
+		/* RFC 3579, Chap. 2.6.3:
+		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
+		 * attribute */
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "could not extract "
+			       "EAP-Message from RADIUS message");
+		sm->eap_if->aaaEapNoReq = TRUE;
+		return;
+	}
+
+	if (len < sizeof(*hdr)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "too short EAP packet "
+			       "received from authentication server");
+		os_free(eap);
+		sm->eap_if->aaaEapNoReq = TRUE;
+		return;
+	}
+
+	if (len > sizeof(*hdr))
+		eap_type = eap[sizeof(*hdr)];
+
+	hdr = (struct eap_hdr *) eap;
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		if (eap_type >= 0)
+			sm->eap_type_authsrv = eap_type;
+		os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
+			    eap_type >= 0 ? eap_server_get_name(0, eap_type) :
+			    "??",
+			    eap_type);
+		break;
+	case EAP_CODE_RESPONSE:
+		os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+			    eap_type >= 0 ? eap_server_get_name(0, eap_type) :
+			    "??",
+			    eap_type);
+		break;
+	case EAP_CODE_SUCCESS:
+		os_strlcpy(buf, "EAP Success", sizeof(buf));
+		break;
+	case EAP_CODE_FAILURE:
+		os_strlcpy(buf, "EAP Failure", sizeof(buf));
+		break;
+	default:
+		os_strlcpy(buf, "unknown EAP code", sizeof(buf));
+		break;
+	}
+	buf[sizeof(buf) - 1] = '\0';
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d "
+		       "id=%d len=%d) from RADIUS server: %s",
+		       hdr->code, hdr->identifier, be_to_host16(hdr->length),
+		       buf);
+	sm->eap_if->aaaEapReq = TRUE;
+
+	wpabuf_free(sm->eap_if->aaaEapReqData);
+	sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len);
+}
+
+
+static void ieee802_1x_get_keys(struct hostapd_data *hapd,
+				struct sta_info *sta, struct radius_msg *msg,
+				struct radius_msg *req,
+				const u8 *shared_secret,
+				size_t shared_secret_len)
+{
+	struct radius_ms_mppe_keys *keys;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	if (sm == NULL)
+		return;
+
+	keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+				      shared_secret_len);
+
+	if (keys && keys->send && keys->recv) {
+		size_t len = keys->send_len + keys->recv_len;
+		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+				keys->send, keys->send_len);
+		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+				keys->recv, keys->recv_len);
+
+		os_free(sm->eap_if->aaaEapKeyData);
+		sm->eap_if->aaaEapKeyData = os_malloc(len);
+		if (sm->eap_if->aaaEapKeyData) {
+			os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv,
+				  keys->recv_len);
+			os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len,
+				  keys->send, keys->send_len);
+			sm->eap_if->aaaEapKeyDataLen = len;
+			sm->eap_if->aaaEapKeyAvailable = TRUE;
+		}
+	}
+
+	if (keys) {
+		os_free(keys->send);
+		os_free(keys->recv);
+		os_free(keys);
+	}
+}
+
+
+static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  struct radius_msg *msg)
+{
+	u8 *class;
+	size_t class_len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	int count, i;
+	struct radius_attr_data *nclass;
+	size_t nclass_count;
+
+	if (!hapd->conf->radius->acct_server || hapd->radius == NULL ||
+	    sm == NULL)
+		return;
+
+	radius_free_class(&sm->radius_class);
+	count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1);
+	if (count <= 0)
+		return;
+
+	nclass = os_zalloc(count * sizeof(struct radius_attr_data));
+	if (nclass == NULL)
+		return;
+
+	nclass_count = 0;
+
+	class = NULL;
+	for (i = 0; i < count; i++) {
+		do {
+			if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
+						    &class, &class_len,
+						    class) < 0) {
+				i = count;
+				break;
+			}
+		} while (class_len < 1);
+
+		nclass[nclass_count].data = os_malloc(class_len);
+		if (nclass[nclass_count].data == NULL)
+			break;
+
+		os_memcpy(nclass[nclass_count].data, class, class_len);
+		nclass[nclass_count].len = class_len;
+		nclass_count++;
+	}
+
+	sm->radius_class.attr = nclass;
+	sm->radius_class.count = nclass_count;
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class "
+		   "attributes for " MACSTR,
+		   (unsigned long) sm->radius_class.count,
+		   MAC2STR(sta->addr));
+}
+
+
+/* Update sta->identity based on User-Name attribute in Access-Accept */
+static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
+					   struct sta_info *sta,
+					   struct radius_msg *msg)
+{
+	u8 *buf, *identity;
+	size_t len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
+				    NULL) < 0)
+		return;
+
+	identity = os_malloc(len + 1);
+	if (identity == NULL)
+		return;
+
+	os_memcpy(identity, buf, len);
+	identity[len] = '\0';
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with "
+		       "User-Name from Access-Accept '%s'",
+		       sm->identity ? (char *) sm->identity : "N/A",
+		       (char *) identity);
+
+	os_free(sm->identity);
+	sm->identity = identity;
+	sm->identity_len = len;
+}
+
+
+struct sta_id_search {
+	u8 identifier;
+	struct eapol_state_machine *sm;
+};
+
+
+static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
+					       struct sta_info *sta,
+					       void *ctx)
+{
+	struct sta_id_search *id_search = ctx;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm && sm->radius_identifier >= 0 &&
+	    sm->radius_identifier == id_search->identifier) {
+		id_search->sm = sm;
+		return 1;
+	}
+	return 0;
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
+{
+	struct sta_id_search id_search;
+	id_search.identifier = identifier;
+	id_search.sm = NULL;
+	ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
+	return id_search.sm;
+}
+
+
+/**
+ * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
+			const u8 *shared_secret, size_t shared_secret_len,
+			void *data)
+{
+	struct hostapd_data *hapd = data;
+	struct sta_info *sta;
+	u32 session_timeout = 0, termination_action, acct_interim_interval;
+	int session_timeout_set, old_vlanid = 0;
+	struct eapol_state_machine *sm;
+	int override_eapReq = 0;
+	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+	sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching "
+			   "station for this RADIUS message");
+		return RADIUS_RX_UNKNOWN;
+	}
+	sta = sm->sta;
+
+	/* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+	 * present when packet contains an EAP-Message attribute */
+	if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+				0) < 0 &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without "
+			   "Message-Authenticator since it does not include "
+			   "EAP-Message");
+	} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+				     req, 1)) {
+		printf("Incoming RADIUS packet did not have correct "
+		       "Message-Authenticator - dropped\n");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+	    hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+	    hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
+		printf("Unknown RADIUS message code\n");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	sm->radius_identifier = -1;
+	wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR,
+		   MAC2STR(sta->addr));
+
+	radius_msg_free(sm->last_recv_radius);
+	sm->last_recv_radius = msg;
+
+	session_timeout_set =
+		!radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+					   &session_timeout);
+	if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
+				      &termination_action))
+		termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
+
+	if (hapd->conf->acct_interim_interval == 0 &&
+	    hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+	    radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+				      &acct_interim_interval) == 0) {
+		if (acct_interim_interval < 60) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO,
+				       "ignored too small "
+				       "Acct-Interim-Interval %d",
+				       acct_interim_interval);
+		} else
+			sta->acct_interim_interval = acct_interim_interval;
+	}
+
+
+	switch (hdr->code) {
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+			sta->vlan_id = 0;
+#ifndef CONFIG_NO_VLAN
+		else {
+			old_vlanid = sta->vlan_id;
+			sta->vlan_id = radius_msg_get_vlanid(msg);
+		}
+		if (sta->vlan_id > 0 &&
+		    hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+					       sta->vlan_id)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "VLAN ID %d", sta->vlan_id);
+		} else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) {
+			sta->eapol_sm->authFail = TRUE;
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO, "authentication "
+				       "server did not include required VLAN "
+				       "ID in Access-Accept");
+			break;
+		}
+#endif /* CONFIG_NO_VLAN */
+
+		if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
+			break;
+
+		/* RFC 3580, Ch. 3.17 */
+		if (session_timeout_set && termination_action ==
+		    RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+			sm->reAuthPeriod = session_timeout;
+		} else if (session_timeout_set)
+			ap_sta_session_timeout(hapd, sta, session_timeout);
+
+		sm->eap_if->aaaSuccess = TRUE;
+		override_eapReq = 1;
+		ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
+				    shared_secret_len);
+		ieee802_1x_store_radius_class(hapd, sta, msg);
+		ieee802_1x_update_sta_identity(hapd, sta, msg);
+		if (sm->eap_if->eapKeyAvailable &&
+		    wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
+				       session_timeout_set ?
+				       (int) session_timeout : -1, sm) == 0) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Added PMKSA cache entry");
+		}
+		break;
+	case RADIUS_CODE_ACCESS_REJECT:
+		sm->eap_if->aaaFail = TRUE;
+		override_eapReq = 1;
+		break;
+	case RADIUS_CODE_ACCESS_CHALLENGE:
+		sm->eap_if->aaaEapReq = TRUE;
+		if (session_timeout_set) {
+			/* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
+			sm->eap_if->aaaMethodTimeout = session_timeout;
+			hostapd_logger(hapd, sm->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "using EAP timeout of %d seconds (from "
+				       "RADIUS)",
+				       sm->eap_if->aaaMethodTimeout);
+		} else {
+			/*
+			 * Use dynamic retransmission behavior per EAP
+			 * specification.
+			 */
+			sm->eap_if->aaaMethodTimeout = 0;
+		}
+		break;
+	}
+
+	ieee802_1x_decapsulate_radius(hapd, sta);
+	if (override_eapReq)
+		sm->eap_if->aaaEapReq = FALSE;
+
+	eapol_auth_step(sm);
+
+	return RADIUS_RX_QUEUED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	if (sm == NULL)
+		return;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "aborting authentication");
+
+#ifndef CONFIG_NO_RADIUS
+	radius_msg_free(sm->last_recv_radius);
+	sm->last_recv_radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+	if (sm->eap_if->eapTimeout) {
+		/*
+		 * Disconnect the STA since it did not reply to the last EAP
+		 * request and we cannot continue EAP processing (EAP-Failure
+		 * could only be sent if the EAP peer actually replied).
+		 */
+		sm->eap_if->portEnabled = FALSE;
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+
+
+static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
+{
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+	if (hapd->conf->default_wep_key_len < 1)
+		return 0;
+
+	os_free(eapol->default_wep_key);
+	eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
+	if (eapol->default_wep_key == NULL ||
+	    random_get_bytes(eapol->default_wep_key,
+			     hapd->conf->default_wep_key_len)) {
+		printf("Could not generate random WEP key.\n");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
+			eapol->default_wep_key,
+			hapd->conf->default_wep_key_len);
+
+	return 0;
+}
+
+
+static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
+					struct sta_info *sta, void *ctx)
+{
+	if (sta->eapol_sm) {
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		eapol_auth_step(sta->eapol_sm);
+	}
+	return 0;
+}
+
+
+static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+	if (eapol->default_wep_key_idx >= 3)
+		eapol->default_wep_key_idx =
+			hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
+	else
+		eapol->default_wep_key_idx++;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
+		   eapol->default_wep_key_idx);
+		      
+	if (ieee802_1x_rekey_broadcast(hapd)) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "failed to generate a "
+			       "new broadcast key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return;
+	}
+
+	/* TODO: Could setup key for RX here, but change default TX keyid only
+	 * after new broadcast key has been sent to all stations. */
+	if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+				broadcast_ether_addr,
+				eapol->default_wep_key_idx, 1, NULL, 0,
+				eapol->default_wep_key,
+				hapd->conf->default_wep_key_len)) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING, "failed to configure a "
+			       "new broadcast key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return;
+	}
+
+	ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL);
+
+	if (hapd->conf->wep_rekeying_period > 0) {
+		eloop_register_timeout(hapd->conf->wep_rekeying_period, 0,
+				       ieee802_1x_rekey, hapd, NULL);
+	}
+}
+
+
+static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
+				  const u8 *data, size_t datalen)
+{
+#ifdef CONFIG_WPS
+	struct sta_info *sta = sta_ctx;
+
+	if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+	    WLAN_STA_MAYBE_WPS) {
+		const u8 *identity;
+		size_t identity_len;
+		struct eapol_state_machine *sm = sta->eapol_sm;
+
+		identity = eap_get_identity(sm->eap, &identity_len);
+		if (identity &&
+		    ((identity_len == WSC_ID_ENROLLEE_LEN &&
+		      os_memcmp(identity, WSC_ID_ENROLLEE,
+				WSC_ID_ENROLLEE_LEN) == 0) ||
+		     (identity_len == WSC_ID_REGISTRAR_LEN &&
+		      os_memcmp(identity, WSC_ID_REGISTRAR,
+				WSC_ID_REGISTRAR_LEN) == 0))) {
+			wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> "
+				   "WLAN_STA_WPS");
+			sta->flags |= WLAN_STA_WPS;
+		}
+	}
+#endif /* CONFIG_WPS */
+
+	ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
+}
+
+
+static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
+				const u8 *data, size_t datalen)
+{
+#ifndef CONFIG_NO_RADIUS
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+
+	ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
+#endif /* CONFIG_NO_RADIUS */
+}
+
+
+static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
+				 int preauth)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+	if (preauth)
+		rsn_preauth_finished(hapd, sta, success);
+	else
+		ieee802_1x_finished(hapd, sta, success);
+}
+
+
+static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
+				   size_t identity_len, int phase2,
+				   struct eap_user *user)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct hostapd_eap_user *eap_user;
+	int i, count;
+
+	eap_user = hostapd_get_eap_user(hapd->conf, identity,
+					identity_len, phase2);
+	if (eap_user == NULL)
+		return -1;
+
+	os_memset(user, 0, sizeof(*user));
+	user->phase2 = phase2;
+	count = EAP_USER_MAX_METHODS;
+	if (count > EAP_MAX_METHODS)
+		count = EAP_MAX_METHODS;
+	for (i = 0; i < count; i++) {
+		user->methods[i].vendor = eap_user->methods[i].vendor;
+		user->methods[i].method = eap_user->methods[i].method;
+	}
+
+	if (eap_user->password) {
+		user->password = os_malloc(eap_user->password_len);
+		if (user->password == NULL)
+			return -1;
+		os_memcpy(user->password, eap_user->password,
+			  eap_user->password_len);
+		user->password_len = eap_user->password_len;
+	}
+	user->force_version = eap_user->force_version;
+	user->ttls_auth = eap_user->ttls_auth;
+
+	return 0;
+}
+
+
+static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL || sta->eapol_sm == NULL)
+		return 0;
+	return 1;
+}
+
+
+static void ieee802_1x_logger(void *ctx, const u8 *addr,
+			      eapol_logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+	struct hostapd_data *hapd = ctx;
+	int hlevel;
+
+	switch (level) {
+	case EAPOL_LOGGER_WARNING:
+		hlevel = HOSTAPD_LEVEL_WARNING;
+		break;
+	case EAPOL_LOGGER_INFO:
+		hlevel = HOSTAPD_LEVEL_INFO;
+		break;
+	case EAPOL_LOGGER_DEBUG:
+	default:
+		hlevel = HOSTAPD_LEVEL_DEBUG;
+		break;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s",
+		       txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
+					   int authorized)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+	ieee802_1x_set_sta_authorized(hapd, sta, authorized);
+}
+
+
+static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+	ieee802_1x_abort_auth(hapd, sta);
+}
+
+
+static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+	ieee802_1x_tx_key(hapd, sta);
+}
+
+
+static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
+				   enum eapol_event type)
+{
+	/* struct hostapd_data *hapd = ctx; */
+	struct sta_info *sta = sta_ctx;
+	switch (type) {
+	case EAPOL_AUTH_SM_CHANGE:
+		wpa_auth_sm_notify(sta->wpa_sm);
+		break;
+	case EAPOL_AUTH_REAUTHENTICATE:
+		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+		break;
+	}
+}
+
+
+int ieee802_1x_init(struct hostapd_data *hapd)
+{
+	int i;
+	struct eapol_auth_config conf;
+	struct eapol_auth_cb cb;
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.ctx = hapd;
+	conf.eap_reauth_period = hapd->conf->eap_reauth_period;
+	conf.wpa = hapd->conf->wpa;
+	conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
+	conf.eap_server = hapd->conf->eap_server;
+	conf.ssl_ctx = hapd->ssl_ctx;
+	conf.msg_ctx = hapd->msg_ctx;
+	conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
+	conf.eap_req_id_text = hapd->conf->eap_req_id_text;
+	conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+	conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
+	conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
+	conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
+	conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info;
+	conf.eap_fast_prov = hapd->conf->eap_fast_prov;
+	conf.pac_key_lifetime = hapd->conf->pac_key_lifetime;
+	conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
+	conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
+	conf.tnc = hapd->conf->tnc;
+	conf.wps = hapd->wps;
+	conf.fragment_size = hapd->conf->fragment_size;
+	conf.pwd_group = hapd->conf->pwd_group;
+
+	os_memset(&cb, 0, sizeof(cb));
+	cb.eapol_send = ieee802_1x_eapol_send;
+	cb.aaa_send = ieee802_1x_aaa_send;
+	cb.finished = _ieee802_1x_finished;
+	cb.get_eap_user = ieee802_1x_get_eap_user;
+	cb.sta_entry_alive = ieee802_1x_sta_entry_alive;
+	cb.logger = ieee802_1x_logger;
+	cb.set_port_authorized = ieee802_1x_set_port_authorized;
+	cb.abort_auth = _ieee802_1x_abort_auth;
+	cb.tx_key = _ieee802_1x_tx_key;
+	cb.eapol_event = ieee802_1x_eapol_event;
+
+	hapd->eapol_auth = eapol_auth_init(&conf, &cb);
+	if (hapd->eapol_auth == NULL)
+		return -1;
+
+	if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
+	    hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+		return -1;
+
+#ifndef CONFIG_NO_RADIUS
+	if (radius_client_register(hapd->radius, RADIUS_AUTH,
+				   ieee802_1x_receive_auth, hapd))
+		return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+	if (hapd->conf->default_wep_key_len) {
+		for (i = 0; i < 4; i++)
+			hostapd_drv_set_key(hapd->conf->iface, hapd,
+					    WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+					    NULL, 0);
+
+		ieee802_1x_rekey(hapd, NULL);
+
+		if (hapd->eapol_auth->default_wep_key == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void ieee802_1x_deinit(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
+
+	if (hapd->driver != NULL &&
+	    (hapd->conf->ieee802_1x || hapd->conf->wpa))
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+	eapol_auth_deinit(hapd->eapol_auth);
+	hapd->eapol_auth = NULL;
+}
+
+
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			 const u8 *buf, size_t len, int ack)
+{
+	struct ieee80211_hdr *hdr;
+	struct ieee802_1x_hdr *xhdr;
+	struct ieee802_1x_eapol_key *key;
+	u8 *pos;
+	const unsigned char rfc1042_hdr[ETH_ALEN] =
+		{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+	if (sta == NULL)
+		return -1;
+	if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr))
+		return 0;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	pos = (u8 *) (hdr + 1);
+	if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0)
+		return 0;
+	pos += sizeof(rfc1042_hdr);
+	if (WPA_GET_BE16(pos) != ETH_P_PAE)
+		return 0;
+	pos += 2;
+
+	xhdr = (struct ieee802_1x_hdr *) pos;
+	pos += sizeof(*xhdr);
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d "
+		   "type=%d length=%d - ack=%d",
+		   MAC2STR(sta->addr), xhdr->version, xhdr->type,
+		   be_to_host16(xhdr->length), ack);
+
+	if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+	    pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+		const struct wpa_eapol_key *wpa;
+		wpa = (const struct wpa_eapol_key *) pos;
+		if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+		    wpa->type == EAPOL_KEY_TYPE_WPA)
+			wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+						     sta->wpa_sm, ack);
+	}
+
+	/* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
+	 * or Authenticator state machines, but EAPOL-Key packets are not
+	 * retransmitted in case of failure. Try to re-sent failed EAPOL-Key
+	 * packets couple of times because otherwise STA keys become
+	 * unsynchronized with AP. */
+	if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack &&
+	    pos + sizeof(*key) <= buf + len) {
+		key = (struct ieee802_1x_eapol_key *) pos;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key "
+			       "frame (%scast index=%d)",
+			       key->key_index & BIT(7) ? "uni" : "broad",
+			       key->key_index & ~BIT(7));
+		/* TODO: re-send EAPOL-Key couple of times (with short delay
+		 * between them?). If all attempt fail, report error and
+		 * deauthenticate STA so that it will get new keys when
+		 * authenticating again (e.g., after returning in range).
+		 * Separate limit/transmit state needed both for unicast and
+		 * broadcast keys(?) */
+	}
+	/* TODO: could move unicast key configuration from ieee802_1x_tx_key()
+	 * to here and change the key only if the EAPOL-Key packet was Acked.
+	 */
+
+	return 1;
+}
+
+
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
+{
+	if (sm == NULL || sm->identity == NULL)
+		return NULL;
+
+	*len = sm->identity_len;
+	return sm->identity;
+}
+
+
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+				 int idx)
+{
+	if (sm == NULL || sm->radius_class.attr == NULL ||
+	    idx >= (int) sm->radius_class.count)
+		return NULL;
+
+	*len = sm->radius_class.attr[idx].len;
+	return sm->radius_class.attr[idx].data;
+}
+
+
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
+{
+	if (sm == NULL)
+		return NULL;
+
+	*len = sm->eap_if->eapKeyDataLen;
+	return sm->eap_if->eapKeyData;
+}
+
+
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+				    int enabled)
+{
+	if (sm == NULL)
+		return;
+	sm->eap_if->portEnabled = enabled ? TRUE : FALSE;
+	eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+				  int valid)
+{
+	if (sm == NULL)
+		return;
+	sm->portValid = valid ? TRUE : FALSE;
+	eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth)
+{
+	if (sm == NULL)
+		return;
+	if (pre_auth)
+		sm->flags |= EAPOL_SM_PREAUTH;
+	else
+		sm->flags &= ~EAPOL_SM_PREAUTH;
+}
+
+
+static const char * bool_txt(Boolean bool)
+{
+	return bool ? "TRUE" : "FALSE";
+}
+
+
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return 0;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xPaePortNumber=%d\n"
+			  "dot1xPaePortProtocolVersion=%d\n"
+			  "dot1xPaePortCapabilities=1\n"
+			  "dot1xPaePortInitialize=%d\n"
+			  "dot1xPaePortReauthenticate=FALSE\n",
+			  sta->aid,
+			  EAPOL_VERSION,
+			  sm->initialize);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	/* dot1xAuthConfigTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthPaeState=%d\n"
+			  "dot1xAuthBackendAuthState=%d\n"
+			  "dot1xAuthAdminControlledDirections=%d\n"
+			  "dot1xAuthOperControlledDirections=%d\n"
+			  "dot1xAuthAuthControlledPortStatus=%d\n"
+			  "dot1xAuthAuthControlledPortControl=%d\n"
+			  "dot1xAuthQuietPeriod=%u\n"
+			  "dot1xAuthServerTimeout=%u\n"
+			  "dot1xAuthReAuthPeriod=%u\n"
+			  "dot1xAuthReAuthEnabled=%s\n"
+			  "dot1xAuthKeyTxEnabled=%s\n",
+			  sm->auth_pae_state + 1,
+			  sm->be_auth_state + 1,
+			  sm->adminControlledDirections,
+			  sm->operControlledDirections,
+			  sm->authPortStatus,
+			  sm->portControl,
+			  sm->quietPeriod,
+			  sm->serverTimeout,
+			  sm->reAuthPeriod,
+			  bool_txt(sm->reAuthEnabled),
+			  bool_txt(sm->keyTxEnabled));
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	/* dot1xAuthStatsTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthEapolFramesRx=%u\n"
+			  "dot1xAuthEapolFramesTx=%u\n"
+			  "dot1xAuthEapolStartFramesRx=%u\n"
+			  "dot1xAuthEapolLogoffFramesRx=%u\n"
+			  "dot1xAuthEapolRespIdFramesRx=%u\n"
+			  "dot1xAuthEapolRespFramesRx=%u\n"
+			  "dot1xAuthEapolReqIdFramesTx=%u\n"
+			  "dot1xAuthEapolReqFramesTx=%u\n"
+			  "dot1xAuthInvalidEapolFramesRx=%u\n"
+			  "dot1xAuthEapLengthErrorFramesRx=%u\n"
+			  "dot1xAuthLastEapolFrameVersion=%u\n"
+			  "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
+			  sm->dot1xAuthEapolFramesRx,
+			  sm->dot1xAuthEapolFramesTx,
+			  sm->dot1xAuthEapolStartFramesRx,
+			  sm->dot1xAuthEapolLogoffFramesRx,
+			  sm->dot1xAuthEapolRespIdFramesRx,
+			  sm->dot1xAuthEapolRespFramesRx,
+			  sm->dot1xAuthEapolReqIdFramesTx,
+			  sm->dot1xAuthEapolReqFramesTx,
+			  sm->dot1xAuthInvalidEapolFramesRx,
+			  sm->dot1xAuthEapLengthErrorFramesRx,
+			  sm->dot1xAuthLastEapolFrameVersion,
+			  MAC2STR(sm->addr));
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	/* dot1xAuthDiagTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthEntersConnecting=%u\n"
+			  "dot1xAuthEapLogoffsWhileConnecting=%u\n"
+			  "dot1xAuthEntersAuthenticating=%u\n"
+			  "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthFailWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
+			  "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
+			  "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
+			  "dot1xAuthBackendResponses=%u\n"
+			  "dot1xAuthBackendAccessChallenges=%u\n"
+			  "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
+			  "dot1xAuthBackendAuthSuccesses=%u\n"
+			  "dot1xAuthBackendAuthFails=%u\n",
+			  sm->authEntersConnecting,
+			  sm->authEapLogoffsWhileConnecting,
+			  sm->authEntersAuthenticating,
+			  sm->authAuthSuccessesWhileAuthenticating,
+			  sm->authAuthTimeoutsWhileAuthenticating,
+			  sm->authAuthFailWhileAuthenticating,
+			  sm->authAuthEapStartsWhileAuthenticating,
+			  sm->authAuthEapLogoffWhileAuthenticating,
+			  sm->authAuthReauthsWhileAuthenticated,
+			  sm->authAuthEapStartsWhileAuthenticated,
+			  sm->authAuthEapLogoffWhileAuthenticated,
+			  sm->backendResponses,
+			  sm->backendAccessChallenges,
+			  sm->backendOtherRequestsToSupplicant,
+			  sm->backendAuthSuccesses,
+			  sm->backendAuthFails);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	/* dot1xAuthSessionStatsTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  /* TODO: dot1xAuthSessionOctetsRx */
+			  /* TODO: dot1xAuthSessionOctetsTx */
+			  /* TODO: dot1xAuthSessionFramesRx */
+			  /* TODO: dot1xAuthSessionFramesTx */
+			  "dot1xAuthSessionId=%08X-%08X\n"
+			  "dot1xAuthSessionAuthenticMethod=%d\n"
+			  "dot1xAuthSessionTime=%u\n"
+			  "dot1xAuthSessionTerminateCause=999\n"
+			  "dot1xAuthSessionUserName=%s\n",
+			  sta->acct_session_id_hi, sta->acct_session_id_lo,
+			  (wpa_key_mgmt_wpa_ieee8021x(
+				   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
+			  1 : 2,
+			  (unsigned int) (time(NULL) -
+					  sta->acct_session_start),
+			  sm->identity);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+static void ieee802_1x_finished(struct hostapd_data *hapd,
+				struct sta_info *sta, int success)
+{
+	const u8 *key;
+	size_t len;
+	/* TODO: get PMKLifetime from WPA parameters */
+	static const int dot11RSNAConfigPMKLifetime = 43200;
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &len);
+	if (success && key && len >= PMK_LEN &&
+	    wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime,
+			       sta->eapol_sm) == 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Added PMKSA cache entry (IEEE 802.1X)");
+	}
+
+#ifdef CONFIG_WPS
+	if (!success && (sta->flags & WLAN_STA_WPS)) {
+		/*
+		 * Many devices require deauthentication after WPS provisioning
+		 * and some may not be be able to do that themselves, so
+		 * disconnect the client here.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Force disconnection after "
+			   "EAP-Failure");
+		/* Add a small sleep to increase likelihood of previously
+		 * requested EAP-Failure TX getting out before this should the
+		 * driver reorder operations.
+		 */
+		os_sleep(0, 10000);
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+#endif /* CONFIG_WPS */
+}
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
new file mode 100644
index 0000000..1a4d2eb
--- /dev/null
+++ b/src/ap/ieee802_1x.h
@@ -0,0 +1,89 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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 IEEE802_1X_H
+#define IEEE802_1X_H
+
+struct hostapd_data;
+struct sta_info;
+struct eapol_state_machine;
+struct hostapd_config;
+struct hostapd_bss_config;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* RFC 3580, 4. RC4 EAPOL-Key Frame */
+
+struct ieee802_1x_eapol_key {
+	u8 type;
+	u16 key_length;
+	u8 replay_counter[8]; /* does not repeat within the life of the keying
+			       * material used to encrypt the Key field;
+			       * 64-bit NTP timestamp MAY be used here */
+	u8 key_iv[16]; /* cryptographically random number */
+	u8 key_index; /* key flag in the most significant bit:
+		       * 0 = broadcast (default key),
+		       * 1 = unicast (key mapping key); key index is in the
+		       * 7 least significant bits */
+	u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with
+			       * MS-MPPE-Send-Key as the key */
+
+	/* followed by key: if packet body length = 44 + key length, then the
+	 * key field (of key_length bytes) contains the key in encrypted form;
+	 * if packet body length = 44, key field is absent and key_length
+	 * represents the number of least significant octets from
+	 * MS-MPPE-Send-Key attribute to be used as the keying material;
+	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+			size_t len);
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_free_station(struct sta_info *sta);
+
+void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+				   struct sta_info *sta, int authorized);
+void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
+int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_deinit(struct hostapd_data *hapd);
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			 const u8 *buf, size_t len, int ack);
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+				 int idx);
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+				    int enabled);
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+				  int valid);
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth);
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen);
+void hostapd_get_ntp_timestamp(u8 *buf);
+char *eap_type_text(u8 type);
+
+const char *radius_mode_txt(struct hostapd_data *hapd);
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* IEEE802_1X_H */
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
new file mode 100644
index 0000000..6f8b778
--- /dev/null
+++ b/src/ap/p2p_hostapd.c
@@ -0,0 +1,120 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			    char *buf, size_t buflen)
+{
+	if (sta->p2p_ie == NULL)
+		return 0;
+
+	return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
+
+
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			int duration)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
+		   "duration=%d", count, start, duration);
+
+	if (count == 0) {
+		hapd->noa_enabled = 0;
+		hapd->noa_start = 0;
+		hapd->noa_duration = 0;
+	}
+
+	if (count != 255) {
+		wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
+			   "NoA parameters");
+		return hostapd_driver_set_noa(hapd, count, start, duration);
+	}
+
+	hapd->noa_enabled = 1;
+	hapd->noa_start = start;
+	hapd->noa_duration = duration;
+
+	if (hapd->num_sta_no_p2p == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
+			   "periodic NoA parameters");
+		return hostapd_driver_set_noa(hapd, count, start, duration);
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
+		   "periodic NoA");
+
+	return 0;
+}
+
+
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
+
+	if (hapd->noa_enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
+		hostapd_driver_set_noa(hapd, 0, 0, 0);
+	}
+}
+
+
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
+
+	if (hapd->noa_enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
+		hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
+				       hapd->noa_duration);
+	}
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_P2P_MANAGER
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 bitmap;
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	*eid++ = 4 + 3 + 1;
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = P2P_OUI_TYPE;
+
+	*eid++ = P2P_ATTR_MANAGEABILITY;
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	bitmap = P2P_MAN_DEVICE_MANAGEMENT;
+	if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
+		bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
+	bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
+	*eid++ = bitmap;
+
+	return eid;
+}
+#endif /* CONFIG_P2P_MANAGER */
diff --git a/src/ap/p2p_hostapd.h b/src/ap/p2p_hostapd.h
new file mode 100644
index 0000000..95b31d9
--- /dev/null
+++ b/src/ap/p2p_hostapd.h
@@ -0,0 +1,41 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * 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 P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			    char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  char *buf, size_t buflen)
+{
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
new file mode 100644
index 0000000..b8fa5a9
--- /dev/null
+++ b/src/ap/peerkey_auth.c
@@ -0,0 +1,402 @@
+/*
+ * hostapd - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+#ifdef CONFIG_PEERKEY
+
+static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx)
+{
+#if 0
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_stsl_negotiation *neg = timeout_ctx;
+#endif
+
+	/* TODO: ? */
+}
+
+
+struct wpa_stsl_search {
+	const u8 *addr;
+	struct wpa_state_machine *sm;
+};
+
+
+static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx)
+{
+	struct wpa_stsl_search *search = ctx;
+	if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) {
+		search->sm = sm;
+		return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
+			       struct wpa_state_machine *sm, const u8 *peer,
+			       u16 mui, u16 error_type)
+{
+	u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN +
+	       2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)];
+	u8 *pos;
+	struct rsn_error_kde error;
+
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			"Sending SMK Error");
+
+	pos = kde;
+
+	if (peer) {
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN,
+				  NULL, 0);
+	}
+
+	error.mui = host_to_be16(mui);
+	error.error_type = host_to_be16(error_type);
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR,
+			  (u8 *) &error, sizeof(error), NULL, 0);
+
+	__wpa_send_eapol(wpa_auth, sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR,
+			 NULL, NULL, kde, pos - kde, 0, 0, 0);
+}
+
+
+void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+{
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_stsl_search search;
+	u8 *buf, *pos;
+	size_t buf_len;
+
+	if (wpa_parse_kde_ies((const u8 *) (key + 1),
+			      WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
+		return;
+	}
+
+	if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
+	    kde.mac_addr_len < ETH_ALEN) {
+		wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
+			   "SMK M1");
+		return;
+	}
+
+	/* Initiator = sm->addr; Peer = kde.mac_addr */
+
+	search.addr = kde.mac_addr;
+	search.sm = NULL;
+	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+	    0 || search.sm == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
+			   " aborted - STA not associated anymore",
+			   MAC2STR(kde.mac_addr));
+		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
+				   STK_ERR_STA_NR);
+		/* FIX: wpa_stsl_remove(wpa_auth, neg); */
+		return;
+	}
+
+	buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+	buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return;
+	/* Initiator RSN IE */
+	os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len);
+	pos = buf + kde.rsn_ie_len;
+	/* Initiator MAC Address */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN,
+			  NULL, 0);
+
+	/* SMK M2:
+	 * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+	 *           MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE)
+	 */
+
+	wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG,
+			"Sending SMK M2");
+
+	__wpa_send_eapol(wpa_auth, search.sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE,
+			 NULL, key->key_nonce, buf, pos - buf, 0, 0, 0);
+
+	os_free(buf);
+}
+
+
+static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm,
+			    struct wpa_eapol_key *key,
+			    struct wpa_eapol_ie_parse *kde,
+			    const u8 *smk)
+{
+	u8 *buf, *pos;
+	size_t buf_len;
+	u32 lifetime;
+
+	/* SMK M4:
+	 * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce,
+	 *           MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE,
+	 *           Lifetime KDE)
+	 */
+
+	buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN +
+		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+	pos = buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return;
+
+	/* Initiator MAC Address */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN,
+			  NULL, 0);
+
+	/* Initiator Nonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN,
+			  NULL, 0);
+
+	/* SMK with PNonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
+			  key->key_nonce, WPA_NONCE_LEN);
+
+	/* Lifetime */
+	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"Sending SMK M4");
+
+	__wpa_send_eapol(wpa_auth, sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE,
+			 NULL, key->key_nonce, buf, pos - buf, 0, 1, 0);
+
+	os_free(buf);
+}
+
+
+static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm,
+			    struct wpa_eapol_key *key,
+			    struct wpa_eapol_ie_parse *kde,
+			    const u8 *smk, const u8 *peer)
+{
+	u8 *buf, *pos;
+	size_t buf_len;
+	u32 lifetime;
+
+	/* SMK M5:
+	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+	 *           MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE,
+	 *                             Lifetime KDE))
+	 */
+
+	buf_len = kde->rsn_ie_len +
+		2 + RSN_SELECTOR_LEN + ETH_ALEN +
+		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
+		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+	pos = buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return;
+
+	/* Peer RSN IE */
+	os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len);
+	pos = buf + kde->rsn_ie_len;
+
+	/* Peer MAC Address */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
+
+	/* PNonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce,
+			  WPA_NONCE_LEN, NULL, 0);
+
+	/* SMK and INonce */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
+			  kde->nonce, WPA_NONCE_LEN);
+
+	/* Lifetime */
+	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"Sending SMK M5");
+
+	__wpa_send_eapol(wpa_auth, sm,
+			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			 WPA_KEY_INFO_SMK_MESSAGE,
+			 NULL, kde->nonce, buf, pos - buf, 0, 1, 0);
+
+	os_free(buf);
+}
+
+
+void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+{
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_stsl_search search;
+	u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
+
+	if (wpa_parse_kde_ies((const u8 *) (key + 1),
+			      WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
+		return;
+	}
+
+	if (kde.rsn_ie == NULL ||
+	    kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+	    kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) {
+		wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or "
+			   "Nonce KDE in SMK M3");
+		return;
+	}
+
+	/* Peer = sm->addr; Initiator = kde.mac_addr;
+	 * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */
+
+	search.addr = kde.mac_addr;
+	search.sm = NULL;
+	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+	    0 || search.sm == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
+			   " aborted - STA not associated anymore",
+			   MAC2STR(kde.mac_addr));
+		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
+				   STK_ERR_STA_NR);
+		/* FIX: wpa_stsl_remove(wpa_auth, neg); */
+		return;
+	}
+
+	if (random_get_bytes(smk, PMK_LEN)) {
+		wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
+		return;
+	}
+
+	/* SMK = PRF-256(Random number, "SMK Derivation",
+	 *               AA || Time || INonce || PNonce)
+	 */
+	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+	pos = buf + ETH_ALEN;
+	wpa_get_ntp_timestamp(pos);
+	pos += 8;
+	os_memcpy(pos, kde.nonce, WPA_NONCE_LEN);
+	pos += WPA_NONCE_LEN;
+	os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN);
+#ifdef CONFIG_IEEE80211W
+	sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
+		   smk, PMK_LEN);
+#else /* CONFIG_IEEE80211W */
+	sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
+		 smk, PMK_LEN);
+#endif /* CONFIG_IEEE80211W */
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN);
+
+	wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk);
+	wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr);
+
+	/* Authenticator does not need SMK anymore and it is required to forget
+	 * it. */
+	os_memset(smk, 0, sizeof(*smk));
+}
+
+
+void wpa_smk_error(struct wpa_authenticator *wpa_auth,
+		   struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+{
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_stsl_search search;
+	struct rsn_error_kde error;
+	u16 mui, error_type;
+
+	if (wpa_parse_kde_ies((const u8 *) (key + 1),
+			      WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+		return;
+	}
+
+	if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+	    kde.error == NULL || kde.error_len < sizeof(error)) {
+		wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in "
+			   "SMK Error");
+		return;
+	}
+
+	search.addr = kde.mac_addr;
+	search.sm = NULL;
+	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+	    0 || search.sm == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not "
+			   "associated for SMK Error message from " MACSTR,
+			   MAC2STR(kde.mac_addr), MAC2STR(sm->addr));
+		return;
+	}
+
+	os_memcpy(&error, kde.error, sizeof(error));
+	mui = be_to_host16(error.mui);
+	error_type = be_to_host16(error.error_type);
+	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			 "STA reported SMK Error: Peer " MACSTR
+			 " MUI %d Error Type %d",
+			 MAC2STR(kde.mac_addr), mui, error_type);
+
+	wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
+}
+
+
+int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
+		    struct wpa_stsl_negotiation *neg)
+{
+	struct wpa_stsl_negotiation *pos, *prev;
+
+	if (wpa_auth == NULL)
+		return -1;
+	pos = wpa_auth->stsl_negotiations;
+	prev = NULL;
+	while (pos) {
+		if (pos == neg) {
+			if (prev)
+				prev->next = pos->next;
+			else
+				wpa_auth->stsl_negotiations = pos->next;
+
+			eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos);
+			os_free(pos);
+			return 0;
+		}
+		prev = pos;
+		pos = pos->next;
+	}
+
+	return -1;
+}
+
+#endif /* CONFIG_PEERKEY */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
new file mode 100644
index 0000000..22f44b7
--- /dev/null
+++ b/src/ap/pmksa_cache_auth.c
@@ -0,0 +1,425 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "pmksa_cache_auth.h"
+
+
+static const int pmksa_cache_max_entries = 1024;
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_pmksa_cache {
+#define PMKID_HASH_SIZE 128
+#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
+	struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
+	struct rsn_pmksa_cache_entry *pmksa;
+	int pmksa_count;
+
+	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
+	void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+	if (entry == NULL)
+		return;
+	os_free(entry->identity);
+#ifndef CONFIG_NO_RADIUS
+	radius_free_class(&entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	os_free(entry);
+}
+
+
+static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+				   struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos, *prev;
+
+	pmksa->pmksa_count--;
+	pmksa->free_cb(entry, pmksa->ctx);
+	pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
+	prev = NULL;
+	while (pos) {
+		if (pos == entry) {
+			if (prev != NULL) {
+				prev->hnext = pos->hnext;
+			} else {
+				pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
+					pos->hnext;
+			}
+			break;
+		}
+		prev = pos;
+		pos = pos->hnext;
+	}
+
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (pos == entry) {
+			if (prev != NULL)
+				prev->next = pos->next;
+			else
+				pmksa->pmksa = pos->next;
+			break;
+		}
+		prev = pos;
+		pos = pos->next;
+	}
+	_pmksa_cache_free_entry(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct rsn_pmksa_cache *pmksa = eloop_ctx;
+	struct os_time now;
+
+	os_get_time(&now);
+	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
+		struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+		pmksa->pmksa = entry->next;
+		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+			   MACSTR, MAC2STR(entry->spa));
+		pmksa_cache_free_entry(pmksa, entry);
+	}
+
+	pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+	int sec;
+	struct os_time now;
+
+	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+	if (pmksa->pmksa == NULL)
+		return;
+	os_get_time(&now);
+	sec = pmksa->pmksa->expiration - now.sec;
+	if (sec < 0)
+		sec = 0;
+	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+}
+
+
+static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+					struct eapol_state_machine *eapol)
+{
+	if (eapol == NULL)
+		return;
+
+	if (eapol->identity) {
+		entry->identity = os_malloc(eapol->identity_len);
+		if (entry->identity) {
+			entry->identity_len = eapol->identity_len;
+			os_memcpy(entry->identity, eapol->identity,
+				  eapol->identity_len);
+		}
+	}
+
+#ifndef CONFIG_NO_RADIUS
+	radius_copy_class(&entry->radius_class, &eapol->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+	entry->eap_type_authsrv = eapol->eap_type_authsrv;
+	entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
+}
+
+
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+			       struct eapol_state_machine *eapol)
+{
+	if (entry == NULL || eapol == NULL)
+		return;
+
+	if (entry->identity) {
+		os_free(eapol->identity);
+		eapol->identity = os_malloc(entry->identity_len);
+		if (eapol->identity) {
+			eapol->identity_len = entry->identity_len;
+			os_memcpy(eapol->identity, entry->identity,
+				  entry->identity_len);
+		}
+		wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
+				  eapol->identity, eapol->identity_len);
+	}
+
+#ifndef CONFIG_NO_RADIUS
+	radius_free_class(&eapol->radius_class);
+	radius_copy_class(&eapol->radius_class, &entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	if (eapol->radius_class.attr) {
+		wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
+			   "PMKSA", (unsigned long) eapol->radius_class.count);
+	}
+
+	eapol->eap_type_authsrv = entry->eap_type_authsrv;
+	((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+}
+
+
+static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
+				   struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos, *prev;
+
+	/* Add the new entry; order by expiration time */
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (pos->expiration > entry->expiration)
+			break;
+		prev = pos;
+		pos = pos->next;
+	}
+	if (prev == NULL) {
+		entry->next = pmksa->pmksa;
+		pmksa->pmksa = entry;
+	} else {
+		entry->next = prev->next;
+		prev->next = entry;
+	}
+	entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
+	pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
+
+	pmksa->pmksa_count++;
+	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+		   MAC2STR(entry->spa));
+	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
+}
+
+
+/**
+ * pmksa_cache_auth_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Supplicant,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+		     const u8 *pmk, size_t pmk_len,
+		const u8 *aa, const u8 *spa, int session_timeout,
+		struct eapol_state_machine *eapol, int akmp)
+{
+	struct rsn_pmksa_cache_entry *entry, *pos;
+	struct os_time now;
+
+	if (pmk_len > PMK_LEN)
+		return NULL;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return NULL;
+	os_memcpy(entry->pmk, pmk, pmk_len);
+	entry->pmk_len = pmk_len;
+	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+		  wpa_key_mgmt_sha256(akmp));
+	os_get_time(&now);
+	entry->expiration = now.sec;
+	if (session_timeout > 0)
+		entry->expiration += session_timeout;
+	else
+		entry->expiration += dot11RSNAConfigPMKLifetime;
+	entry->akmp = akmp;
+	os_memcpy(entry->spa, spa, ETH_ALEN);
+	pmksa_cache_from_eapol_data(entry, eapol);
+
+	/* Replace an old entry for the same STA (if found) with the new entry
+	 */
+	pos = pmksa_cache_auth_get(pmksa, spa, NULL);
+	if (pos)
+		pmksa_cache_free_entry(pmksa, pos);
+
+	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+		/* Remove the oldest entry to make room for the new entry */
+		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
+			   "entry (for " MACSTR ") to make room for new one",
+			   MAC2STR(pmksa->pmksa->spa));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+
+	pmksa_cache_link_entry(pmksa, entry);
+
+	return entry;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+		    const struct rsn_pmksa_cache_entry *old_entry,
+		    const u8 *aa, const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return NULL;
+	os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+	os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
+	entry->pmk_len = old_entry->pmk_len;
+	entry->expiration = old_entry->expiration;
+	entry->akmp = old_entry->akmp;
+	os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
+	entry->opportunistic = 1;
+	if (old_entry->identity) {
+		entry->identity = os_malloc(old_entry->identity_len);
+		if (entry->identity) {
+			entry->identity_len = old_entry->identity_len;
+			os_memcpy(entry->identity, old_entry->identity,
+				  old_entry->identity_len);
+		}
+	}
+#ifndef CONFIG_NO_RADIUS
+	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	entry->eap_type_authsrv = old_entry->eap_type_authsrv;
+	entry->vlan_id = old_entry->vlan_id;
+	entry->opportunistic = 1;
+
+	pmksa_cache_link_entry(pmksa, entry);
+
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
+{
+	struct rsn_pmksa_cache_entry *entry, *prev;
+	int i;
+
+	if (pmksa == NULL)
+		return;
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		prev = entry;
+		entry = entry->next;
+		_pmksa_cache_free_entry(prev);
+	}
+	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+	for (i = 0; i < PMKID_HASH_SIZE; i++)
+		pmksa->pmkid[i] = NULL;
+	os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_auth_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @spa: Supplicant address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+		     const u8 *spa, const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (pmkid)
+		entry = pmksa->pmkid[PMKID_HASH(pmkid)];
+	else
+		entry = pmksa->pmksa;
+	while (entry) {
+		if ((spa == NULL ||
+		     os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+		    (pmkid == NULL ||
+		     os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+			return entry;
+		entry = pmkid ? entry->hnext : entry->next;
+	}
+	return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: PMKID
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ *
+ * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+	struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+	const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	u8 new_pmkid[PMKID_LEN];
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+			continue;
+		rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
+			  wpa_key_mgmt_sha256(entry->akmp));
+		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
+			return entry;
+		entry = entry->next;
+	}
+	return NULL;
+}
+
+
+/**
+ * pmksa_cache_auth_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				      void *ctx), void *ctx)
+{
+	struct rsn_pmksa_cache *pmksa;
+
+	pmksa = os_zalloc(sizeof(*pmksa));
+	if (pmksa) {
+		pmksa->free_cb = free_cb;
+		pmksa->ctx = ctx;
+	}
+
+	return pmksa;
+}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
new file mode 100644
index 0000000..9628b13
--- /dev/null
+++ b/src/ap/pmksa_cache_auth.h
@@ -0,0 +1,64 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * 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 PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+#include "radius/radius.h"
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+	struct rsn_pmksa_cache_entry *next, *hnext;
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN];
+	size_t pmk_len;
+	os_time_t expiration;
+	int akmp; /* WPA_KEY_MGMT_* */
+	u8 spa[ETH_ALEN];
+
+	u8 *identity;
+	size_t identity_len;
+	struct radius_class_data radius_class;
+	u8 eap_type_authsrv;
+	int vlan_id;
+	int opportunistic;
+};
+
+struct rsn_pmksa_cache;
+
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				      void *ctx), void *ctx);
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+		     const u8 *spa, const u8 *pmkid);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+	struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
+	const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+		     const u8 *pmk, size_t pmk_len,
+		     const u8 *aa, const u8 *spa, int session_timeout,
+		     struct eapol_state_machine *eapol, int akmp);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+		    const struct rsn_pmksa_cache_entry *old_entry,
+		    const u8 *aa, const u8 *pmkid);
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+			       struct eapol_state_machine *eapol);
+
+#endif /* PMKSA_CACHE_H */
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
new file mode 100644
index 0000000..8e13315
--- /dev/null
+++ b/src/ap/preauth_auth.c
@@ -0,0 +1,279 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#ifdef CONFIG_RSN_PREAUTH
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_1x.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+
+#ifndef ETH_P_PREAUTH
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+#endif /* ETH_P_PREAUTH */
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_preauth_interface {
+	struct rsn_preauth_interface *next;
+	struct hostapd_data *hapd;
+	struct l2_packet_data *l2;
+	char *ifname;
+	int ifindex;
+};
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+				const u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface = ctx;
+	struct hostapd_data *hapd = piface->hapd;
+	struct ieee802_1x_hdr *hdr;
+	struct sta_info *sta;
+	struct l2_ethhdr *ethhdr;
+
+	wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
+		   "from interface '%s'", piface->ifname);
+	if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
+			   "(len=%lu)", (unsigned long) len);
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *) buf;
+	hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+
+	if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
+			   MACSTR, MAC2STR(ethhdr->h_dest));
+		return;
+	}
+
+	sta = ap_get_sta(hapd, ethhdr->h_source);
+	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association "
+			   "STA " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+	if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
+		sta = ap_sta_add(hapd, ethhdr->h_source);
+		if (sta == NULL)
+			return;
+		sta->flags = WLAN_STA_PREAUTH;
+
+		ieee802_1x_new_station(hapd, sta);
+		if (sta->eapol_sm == NULL) {
+			ap_free_sta(hapd, sta);
+			sta = NULL;
+		} else {
+			sta->eapol_sm->radius_identifier = -1;
+			sta->eapol_sm->portValid = TRUE;
+			sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
+		}
+	}
+	if (sta == NULL)
+		return;
+	sta->preauth_iface = piface;
+	ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
+			   len - sizeof(*ethhdr));
+}
+
+
+static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
+{
+	struct rsn_preauth_interface *piface;
+
+	wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname);
+
+	piface = os_zalloc(sizeof(*piface));
+	if (piface == NULL)
+		return -1;
+	piface->hapd = hapd;
+
+	piface->ifname = os_strdup(ifname);
+	if (piface->ifname == NULL) {
+		goto fail1;
+	}
+
+	piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
+				    rsn_preauth_receive, piface, 1);
+	if (piface->l2 == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to open register layer 2 access "
+			   "to ETH_P_PREAUTH");
+		goto fail2;
+	}
+
+	piface->next = hapd->preauth_iface;
+	hapd->preauth_iface = piface;
+	return 0;
+
+fail2:
+	os_free(piface->ifname);
+fail1:
+	os_free(piface);
+	return -1;
+}
+
+
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+	struct rsn_preauth_interface *piface, *prev;
+
+	piface = hapd->preauth_iface;
+	hapd->preauth_iface = NULL;
+	while (piface) {
+		prev = piface;
+		piface = piface->next;
+		l2_packet_deinit(prev->l2);
+		os_free(prev->ifname);
+		os_free(prev);
+	}
+}
+
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+	char *tmp, *start, *end;
+
+	if (hapd->conf->rsn_preauth_interfaces == NULL)
+		return 0;
+
+	tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
+	if (tmp == NULL)
+		return -1;
+	start = tmp;
+	for (;;) {
+		while (*start == ' ')
+			start++;
+		if (*start == '\0')
+			break;
+		end = os_strchr(start, ' ');
+		if (end)
+			*end = '\0';
+
+		if (rsn_preauth_iface_add(hapd, start)) {
+			rsn_preauth_iface_deinit(hapd);
+			os_free(tmp);
+			return -1;
+		}
+
+		if (end)
+			start = end + 1;
+		else
+			break;
+	}
+	os_free(tmp);
+	return 0;
+}
+
+
+static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for "
+		   MACSTR, MAC2STR(sta->addr));
+	ap_free_sta(hapd, sta);
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success)
+{
+	const u8 *key;
+	size_t len;
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+		       HOSTAPD_LEVEL_INFO, "pre-authentication %s",
+		       success ? "succeeded" : "failed");
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &len);
+	if (len > PMK_LEN)
+		len = PMK_LEN;
+	if (success && key) {
+		if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len,
+					       sta->addr,
+					       dot11RSNAConfigPMKLifetime,
+					       sta->eapol_sm) == 0) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "added PMKSA cache entry (pre-auth)");
+		} else {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "failed to add PMKSA cache entry "
+				       "(pre-auth)");
+		}
+	}
+
+	/*
+	 * Finish STA entry removal from timeout in order to avoid freeing
+	 * STA data before the caller has finished processing.
+	 */
+	eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta);
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface;
+	struct l2_ethhdr *ethhdr;
+
+	piface = hapd->preauth_iface;
+	while (piface) {
+		if (piface == sta->preauth_iface)
+			break;
+		piface = piface->next;
+	}
+
+	if (piface == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication "
+			   "interface for " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+
+	ethhdr = os_malloc(sizeof(*ethhdr) + len);
+	if (ethhdr == NULL)
+		return;
+
+	os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
+	os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
+	ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH);
+	os_memcpy(ethhdr + 1, buf, len);
+
+	if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr,
+			   sizeof(*ethhdr) + len) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to send preauth packet using "
+			   "l2_packet_send\n");
+	}
+	os_free(ethhdr);
+}
+
+
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
diff --git a/src/ap/preauth_auth.h b/src/ap/preauth_auth.h
new file mode 100644
index 0000000..5348bee
--- /dev/null
+++ b/src/ap/preauth_auth.h
@@ -0,0 +1,58 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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 PREAUTH_H
+#define PREAUTH_H
+
+#ifdef CONFIG_RSN_PREAUTH
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd);
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success);
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len);
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_RSN_PREAUTH */
+
+static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void rsn_preauth_finished(struct hostapd_data *hapd,
+					struct sta_info *sta,
+					int success)
+{
+}
+
+static inline void rsn_preauth_send(struct hostapd_data *hapd,
+				    struct sta_info *sta,
+				    u8 *buf, size_t len)
+{
+}
+
+static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
+
+#endif /* PREAUTH_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
new file mode 100644
index 0000000..e829447
--- /dev/null
+++ b/src/ap/sta_info.c
@@ -0,0 +1,796 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "drivers/driver.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ap_mlme.h"
+#include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+				       struct sta_info *sta);
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+#ifdef CONFIG_IEEE80211W
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_IEEE80211W */
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+			      void *ctx),
+		    void *ctx)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (cb(hapd, sta, ctx))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+	struct sta_info *s;
+
+	s = hapd->sta_hash[STA_HASH(sta)];
+	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+		s = s->hnext;
+	return s;
+}
+
+
+static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sta_info *tmp;
+
+	if (hapd->sta_list == sta) {
+		hapd->sta_list = sta->next;
+		return;
+	}
+
+	tmp = hapd->sta_list;
+	while (tmp != NULL && tmp->next != sta)
+		tmp = tmp->next;
+	if (tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from "
+			   "list.", MAC2STR(sta->addr));
+	} else
+		tmp->next = sta->next;
+}
+
+
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
+	hapd->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+
+static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sta_info *s;
+
+	s = hapd->sta_hash[STA_HASH(sta->addr)];
+	if (s == NULL) return;
+	if (os_memcmp(s->addr, sta->addr, 6) == 0) {
+		hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+		return;
+	}
+
+	while (s->hnext != NULL &&
+	       os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+		s = s->hnext;
+	if (s->hnext != NULL)
+		s->hnext = s->hnext->hnext;
+	else
+		wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR
+			   " from hash table", MAC2STR(sta->addr));
+}
+
+
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int set_beacon = 0;
+
+	accounting_sta_stop(hapd, sta);
+
+	/* just in case */
+	ap_sta_set_authorized(hapd, sta, 0);
+
+	if (sta->flags & WLAN_STA_WDS)
+		hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0);
+
+	if (!(sta->flags & WLAN_STA_PREAUTH))
+		hostapd_drv_sta_remove(hapd, sta->addr);
+
+	ap_sta_hash_del(hapd, sta);
+	ap_sta_list_del(hapd, sta);
+
+	if (sta->aid > 0)
+		hapd->sta_aid[(sta->aid - 1) / 32] &=
+			~BIT((sta->aid - 1) % 32);
+
+	hapd->num_sta--;
+	if (sta->nonerp_set) {
+		sta->nonerp_set = 0;
+		hapd->iface->num_sta_non_erp--;
+		if (hapd->iface->num_sta_non_erp == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_short_slot_time_set) {
+		sta->no_short_slot_time_set = 0;
+		hapd->iface->num_sta_no_short_slot_time--;
+		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_slot_time == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_short_preamble_set) {
+		sta->no_short_preamble_set = 0;
+		hapd->iface->num_sta_no_short_preamble--;
+		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_preamble == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_ht_gf_set) {
+		sta->no_ht_gf_set = 0;
+		hapd->iface->num_sta_ht_no_gf--;
+	}
+
+	if (sta->no_ht_set) {
+		sta->no_ht_set = 0;
+		hapd->iface->num_sta_no_ht--;
+	}
+
+	if (sta->ht_20mhz_set) {
+		sta->ht_20mhz_set = 0;
+		hapd->iface->num_sta_ht_20mhz--;
+	}
+
+#ifdef CONFIG_P2P
+	if (sta->no_p2p_set) {
+		sta->no_p2p_set = 0;
+		hapd->num_sta_no_p2p--;
+		if (hapd->num_sta_no_p2p == 0)
+			hostapd_p2p_non_p2p_sta_disconnected(hapd);
+	}
+#endif /* CONFIG_P2P */
+
+#if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N)
+	if (hostapd_ht_operation_update(hapd->iface) > 0)
+		set_beacon++;
+#endif /* NEED_AP_MLME && CONFIG_IEEE80211N */
+
+	if (set_beacon)
+		ieee802_11_set_beacons(hapd->iface);
+
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+
+	ieee802_1x_free_station(sta);
+	wpa_auth_sta_deinit(sta->wpa_sm);
+	rsn_preauth_free_station(hapd, sta);
+#ifndef CONFIG_NO_RADIUS
+	radius_client_flush_auth(hapd->radius, sta->addr);
+#endif /* CONFIG_NO_RADIUS */
+
+	os_free(sta->last_assoc_req);
+	os_free(sta->challenge);
+
+#ifdef CONFIG_IEEE80211W
+	os_free(sta->sa_query_trans_id);
+	eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_P2P
+	p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+	wpabuf_free(sta->wps_ie);
+	wpabuf_free(sta->p2p_ie);
+
+	os_free(sta->ht_capabilities);
+
+	os_free(sta);
+}
+
+
+void hostapd_free_stas(struct hostapd_data *hapd)
+{
+	struct sta_info *sta, *prev;
+
+	sta = hapd->sta_list;
+
+	while (sta) {
+		prev = sta;
+		if (sta->flags & WLAN_STA_AUTH) {
+			mlme_deauthenticate_indication(
+				hapd, sta, WLAN_REASON_UNSPECIFIED);
+		}
+		sta = sta->next;
+		wpa_printf(MSG_DEBUG, "Removing station " MACSTR,
+			   MAC2STR(prev->addr));
+		ap_free_sta(hapd, prev);
+	}
+}
+
+
+/**
+ * ap_handle_timer - Per STA timer handler
+ * @eloop_ctx: struct hostapd_data *
+ * @timeout_ctx: struct sta_info *
+ *
+ * This function is called to check station activity and to remove inactive
+ * stations.
+ */
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	unsigned long next_time = 0;
+
+	if (sta->timeout_next == STA_REMOVE) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+			       "local deauth request");
+		ap_free_sta(hapd, sta);
+		return;
+	}
+
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    (sta->timeout_next == STA_NULLFUNC ||
+	     sta->timeout_next == STA_DISASSOC)) {
+		int inactive_sec;
+		inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
+		if (inactive_sec == -1) {
+			wpa_msg(hapd, MSG_DEBUG, "Check inactivity: Could not "
+				"get station info rom kernel driver for "
+				MACSTR, MAC2STR(sta->addr));
+		} else if (inactive_sec < hapd->conf->ap_max_inactivity &&
+			   sta->flags & WLAN_STA_ASSOC) {
+			/* station activity detected; reset timeout state */
+			wpa_msg(hapd, MSG_DEBUG, "Station " MACSTR " has been "
+				"active %is ago",
+				MAC2STR(sta->addr), inactive_sec);
+			sta->timeout_next = STA_NULLFUNC;
+			next_time = hapd->conf->ap_max_inactivity -
+				inactive_sec;
+		} else {
+			wpa_msg(hapd, MSG_DEBUG, "Station " MACSTR " has been "
+				"inactive too long: %d sec, max allowed: %d",
+				MAC2STR(sta->addr), inactive_sec,
+				hapd->conf->ap_max_inactivity);
+		}
+	}
+
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    sta->timeout_next == STA_DISASSOC &&
+	    !(sta->flags & WLAN_STA_PENDING_POLL)) {
+		wpa_msg(hapd, MSG_DEBUG, "Station " MACSTR " has ACKed data "
+			"poll", MAC2STR(sta->addr));
+		/* data nullfunc frame poll did not produce TX errors; assume
+		 * station ACKed it */
+		sta->timeout_next = STA_NULLFUNC;
+		next_time = hapd->conf->ap_max_inactivity;
+	}
+
+	if (next_time) {
+		eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
+				       sta);
+		return;
+	}
+
+	if (sta->timeout_next == STA_NULLFUNC &&
+	    (sta->flags & WLAN_STA_ASSOC)) {
+#ifndef CONFIG_NATIVE_WINDOWS
+		/* send data frame to poll STA and check whether this frame
+		 * is ACKed */
+		struct ieee80211_hdr hdr;
+
+		wpa_printf(MSG_DEBUG, "  Polling STA with data frame");
+		sta->flags |= WLAN_STA_PENDING_POLL;
+
+		os_memset(&hdr, 0, sizeof(hdr));
+		if (hapd->driver &&
+		    os_strcmp(hapd->driver->name, "hostap") == 0) {
+			/*
+			 * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
+			 * but it is apparently not retried so TX Exc events
+			 * are not received for it.
+			 */
+			hdr.frame_control =
+				IEEE80211_FC(WLAN_FC_TYPE_DATA,
+					     WLAN_FC_STYPE_DATA);
+		} else {
+			hdr.frame_control =
+				IEEE80211_FC(WLAN_FC_TYPE_DATA,
+					     WLAN_FC_STYPE_NULLFUNC);
+		}
+
+		hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+		os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN);
+		os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr,
+			  ETH_ALEN);
+		os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN);
+
+		if (hostapd_drv_send_mlme(hapd, &hdr, sizeof(hdr)) < 0)
+			perror("ap_handle_timer: send");
+#endif /* CONFIG_NATIVE_WINDOWS */
+	} else if (sta->timeout_next != STA_REMOVE) {
+		int deauth = sta->timeout_next == STA_DEAUTH;
+
+		wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR,
+			   deauth ? "deauthentication" : "disassociation",
+			   MAC2STR(sta->addr));
+
+		if (deauth) {
+			hostapd_drv_sta_deauth(
+				hapd, sta->addr,
+				WLAN_REASON_PREV_AUTH_NOT_VALID);
+		} else {
+			hostapd_drv_sta_disassoc(
+				hapd, sta->addr,
+				WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+		}
+	}
+
+	switch (sta->timeout_next) {
+	case STA_NULLFUNC:
+		sta->timeout_next = STA_DISASSOC;
+		eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+		break;
+	case STA_DISASSOC:
+		sta->flags &= ~WLAN_STA_ASSOC;
+		ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+		if (!sta->acct_terminate_cause)
+			sta->acct_terminate_cause =
+				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+		accounting_sta_stop(hapd, sta);
+		ieee802_1x_free_station(sta);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "disassociated due to "
+			       "inactivity");
+		sta->timeout_next = STA_DEAUTH;
+		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+		mlme_disassociate_indication(
+			hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+		break;
+	case STA_DEAUTH:
+	case STA_REMOVE:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+			       "inactivity");
+		if (!sta->acct_terminate_cause)
+			sta->acct_terminate_cause =
+				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+		mlme_deauthenticate_indication(
+			hapd, sta,
+			WLAN_REASON_PREV_AUTH_NOT_VALID);
+		ap_free_sta(hapd, sta);
+		break;
+	}
+}
+
+
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	u8 addr[ETH_ALEN];
+
+	if (!(sta->flags & WLAN_STA_AUTH))
+		return;
+
+	mlme_deauthenticate_indication(hapd, sta,
+				       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+		       "session timeout");
+	sta->acct_terminate_cause =
+		RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
+	os_memcpy(addr, sta->addr, ETH_ALEN);
+	ap_free_sta(hapd, sta);
+	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			    u32 session_timeout)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d "
+		       "seconds", session_timeout);
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+	eloop_register_timeout(session_timeout, 0, ap_handle_session_timer,
+			       hapd, sta);
+}
+
+
+void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+}
+
+
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		return sta;
+
+	wpa_printf(MSG_DEBUG, "  New STA");
+	if (hapd->num_sta >= hapd->conf->max_num_sta) {
+		/* FIX: might try to remove some old STAs first? */
+		wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)",
+			   hapd->num_sta, hapd->conf->max_num_sta);
+		return NULL;
+	}
+
+	sta = os_zalloc(sizeof(struct sta_info));
+	if (sta == NULL) {
+		wpa_printf(MSG_ERROR, "malloc failed");
+		return NULL;
+	}
+	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+
+	/* initialize STA info data */
+	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+			       ap_handle_timer, hapd, sta);
+	os_memcpy(sta->addr, addr, ETH_ALEN);
+	sta->next = hapd->sta_list;
+	hapd->sta_list = sta;
+	hapd->num_sta++;
+	ap_sta_hash_add(hapd, sta);
+	sta->ssid = &hapd->conf->ssid;
+	ap_sta_remove_in_other_bss(hapd, sta);
+
+	return sta;
+}
+
+
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+
+	wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
+		   MAC2STR(sta->addr));
+	if (hostapd_drv_sta_remove(hapd, sta->addr) &&
+	    sta->flags & WLAN_STA_ASSOC) {
+		wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR
+			   " from kernel driver.", MAC2STR(sta->addr));
+		return -1;
+	}
+	return 0;
+}
+
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		struct sta_info *sta2;
+		/* bss should always be set during operation, but it may be
+		 * NULL during reconfiguration. Assume the STA is not
+		 * associated to another BSS in that case to avoid NULL pointer
+		 * dereferences. */
+		if (bss == hapd || bss == NULL)
+			continue;
+		sta2 = ap_get_sta(bss, sta->addr);
+		if (!sta2)
+			continue;
+
+		ap_sta_disconnect(bss, sta2, sta2->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+			 u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	sta->flags &= ~WLAN_STA_ASSOC;
+	ap_sta_remove(hapd, sta);
+	sta->timeout_next = STA_DEAUTH;
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
+			       ap_handle_timer, hapd, sta);
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(sta);
+
+	mlme_disassociate_indication(hapd, sta, reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	ap_sta_remove(hapd, sta);
+	sta->timeout_next = STA_REMOVE;
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+			       ap_handle_timer, hapd, sta);
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(sta);
+
+	mlme_deauthenticate_indication(hapd, sta, reason);
+}
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		     int old_vlanid)
+{
+#ifndef CONFIG_NO_VLAN
+	const char *iface;
+	struct hostapd_vlan *vlan = NULL;
+	int ret;
+
+	/*
+	 * Do not proceed furthur if the vlan id remains same. We do not want
+	 * duplicate dynamic vlan entries.
+	 */
+	if (sta->vlan_id == old_vlanid)
+		return 0;
+
+	/*
+	 * During 1x reauth, if the vlan id changes, then remove the old id and
+	 * proceed furthur to add the new one.
+	 */
+	if (old_vlanid > 0)
+		vlan_remove_dynamic(hapd, old_vlanid);
+
+	iface = hapd->conf->iface;
+	if (sta->ssid->vlan[0])
+		iface = sta->ssid->vlan;
+
+	if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+		sta->vlan_id = 0;
+	else if (sta->vlan_id > 0) {
+		vlan = hapd->conf->vlan;
+		while (vlan) {
+			if (vlan->vlan_id == sta->vlan_id ||
+			    vlan->vlan_id == VLAN_ID_WILDCARD) {
+				iface = vlan->ifname;
+				break;
+			}
+			vlan = vlan->next;
+		}
+	}
+
+	if (sta->vlan_id > 0 && vlan == NULL) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
+			       "binding station to (vlan_id=%d)",
+			       sta->vlan_id);
+		return -1;
+	} else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
+		vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
+		if (vlan == NULL) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "could not add "
+				       "dynamic VLAN interface for vlan_id=%d",
+				       sta->vlan_id);
+			return -1;
+		}
+
+		iface = vlan->ifname;
+		if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "could not "
+				       "configure encryption for dynamic VLAN "
+				       "interface for vlan_id=%d",
+				       sta->vlan_id);
+		}
+
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
+			       "interface '%s'", iface);
+	} else if (vlan && vlan->vlan_id == sta->vlan_id) {
+		if (sta->vlan_id > 0) {
+			vlan->dynamic_vlan++;
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "updated existing "
+				       "dynamic VLAN interface '%s'", iface);
+		}
+
+		/*
+		 * Update encryption configuration for statically generated
+		 * VLAN interface. This is only used for static WEP
+		 * configuration for the case where hostapd did not yet know
+		 * which keys are to be used when the interface was added.
+		 */
+		if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "could not "
+				       "configure encryption for VLAN "
+				       "interface for vlan_id=%d",
+				       sta->vlan_id);
+		}
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "binding station to interface "
+		       "'%s'", iface);
+
+	if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
+		wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
+
+	ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+	if (ret < 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
+			       "entry to vlan_id=%d", sta->vlan_id);
+	}
+	return ret;
+#else /* CONFIG_NO_VLAN */
+	return 0;
+#endif /* CONFIG_NO_VLAN */
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	u32 tu;
+	struct os_time now, passed;
+	os_get_time(&now);
+	os_time_sub(&now, &sta->sa_query_start, &passed);
+	tu = (passed.sec * 1000000 + passed.usec) / 1024;
+	if (hapd->conf->assoc_sa_query_max_timeout < tu) {
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "association SA Query timed out");
+		sta->sa_query_timed_out = 1;
+		os_free(sta->sa_query_trans_id);
+		sta->sa_query_trans_id = NULL;
+		sta->sa_query_count = 0;
+		eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	unsigned int timeout, sec, usec;
+	u8 *trans_id, *nbuf;
+
+	if (sta->sa_query_count > 0 &&
+	    ap_check_sa_query_timeout(hapd, sta))
+		return;
+
+	nbuf = os_realloc(sta->sa_query_trans_id,
+			  (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN);
+	if (nbuf == NULL)
+		return;
+	if (sta->sa_query_count == 0) {
+		/* Starting a new SA Query procedure */
+		os_get_time(&sta->sa_query_start);
+	}
+	trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
+	sta->sa_query_trans_id = nbuf;
+	sta->sa_query_count++;
+
+	os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+	timeout = hapd->conf->assoc_sa_query_retry_timeout;
+	sec = ((timeout / 1000) * 1024) / 1000;
+	usec = (timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta);
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "association SA Query attempt %d", sta->sa_query_count);
+
+#ifdef NEED_AP_MLME
+	ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	ap_sa_query_timer(hapd, sta);
+}
+
+
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+	os_free(sta->sa_query_trans_id);
+	sta->sa_query_trans_id = NULL;
+	sta->sa_query_count = 0;
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+			   int authorized)
+{
+	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
+		return;
+
+	if (authorized)
+		sta->flags |= WLAN_STA_AUTHORIZED;
+	else
+		sta->flags &= ~WLAN_STA_AUTHORIZED;
+
+	if (hapd->sta_authorized_cb)
+		hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+					sta->addr, authorized);
+}
+
+
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *addr, u16 reason)
+{
+
+	if (sta == NULL && addr)
+		sta = ap_get_sta(hapd, addr);
+
+	if (addr)
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+
+	if (sta == NULL)
+		return;
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta);
+	sta->timeout_next = STA_REMOVE;
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
new file mode 100644
index 0000000..9ec4fe3
--- /dev/null
+++ b/src/ap/sta_info.h
@@ -0,0 +1,165 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 STA_INFO_H
+#define STA_INFO_H
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3)
+#define WLAN_STA_PERM BIT(4)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WMM BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#define WLAN_STA_NONERP BIT(31)
+
+/* Maximum number of supported rates (from both Supported Rates and Extended
+ * Supported Rates IEs). */
+#define WLAN_SUPP_RATES_MAX 32
+
+
+struct sta_info {
+	struct sta_info *next; /* next entry in sta list */
+	struct sta_info *hnext; /* next entry in hash table list */
+	u8 addr[6];
+	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+	u32 flags; /* Bitfield of WLAN_STA_* */
+	u16 capability;
+	u16 listen_interval; /* or beacon_int for APs */
+	u8 supported_rates[WLAN_SUPP_RATES_MAX];
+	int supported_rates_len;
+
+	unsigned int nonerp_set:1;
+	unsigned int no_short_slot_time_set:1;
+	unsigned int no_short_preamble_set:1;
+	unsigned int no_ht_gf_set:1;
+	unsigned int no_ht_set:1;
+	unsigned int ht_20mhz_set:1;
+	unsigned int no_p2p_set:1;
+
+	u16 auth_alg;
+	u8 previous_ap[6];
+
+	enum {
+		STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
+	} timeout_next;
+
+	/* IEEE 802.1X related data */
+	struct eapol_state_machine *eapol_sm;
+
+	/* IEEE 802.11f (IAPP) related data */
+	struct ieee80211_mgmt *last_assoc_req;
+
+	u32 acct_session_id_hi;
+	u32 acct_session_id_lo;
+	time_t acct_session_start;
+	int acct_session_started;
+	int acct_terminate_cause; /* Acct-Terminate-Cause */
+	int acct_interim_interval; /* Acct-Interim-Interval */
+
+	unsigned long last_rx_bytes;
+	unsigned long last_tx_bytes;
+	u32 acct_input_gigawords; /* Acct-Input-Gigawords */
+	u32 acct_output_gigawords; /* Acct-Output-Gigawords */
+
+	u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+	struct wpa_state_machine *wpa_sm;
+	struct rsn_preauth_interface *preauth_iface;
+
+	struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */
+	struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
+
+	int vlan_id;
+
+	struct ieee80211_ht_capabilities *ht_capabilities;
+
+#ifdef CONFIG_IEEE80211W
+	int sa_query_count; /* number of pending SA Query requests;
+			     * 0 = no SA Query in progress */
+	int sa_query_timed_out;
+	u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+				* sa_query_count octets of pending SA Query
+				* transaction identifiers */
+	struct os_time sa_query_start;
+#endif /* CONFIG_IEEE80211W */
+
+	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
+};
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
+ * passed since last received frame from the station, a nullfunc data frame is
+ * sent to the station. If this frame is not acknowledged and no other frames
+ * have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+#define AP_MAX_INACTIVITY (5 * 60)
+#define AP_DISASSOC_DELAY (1)
+#define AP_DEAUTH_DELAY (1)
+/* Number of seconds to keep STA entry with Authenticated flag after it has
+ * been disassociated. */
+#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
+/* Number of seconds to keep STA entry after it has been deauthenticated. */
+#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
+
+
+struct hostapd_data;
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+			      void *ctx),
+		    void *ctx);
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_free_stas(struct hostapd_data *hapd);
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			    u32 session_timeout);
+void ap_sta_no_session_timeout(struct hostapd_data *hapd,
+			       struct sta_info *sta);
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+			 u16 reason);
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason);
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		     int old_vlanid);
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *addr, u16 reason);
+
+void ap_sta_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized);
+static inline int ap_sta_is_authorized(struct sta_info *sta)
+{
+	return sta->flags & WLAN_STA_AUTHORIZED;
+}
+
+#endif /* STA_INFO_H */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
new file mode 100644
index 0000000..1925217
--- /dev/null
+++ b/src/ap/tkip_countermeasures.c
@@ -0,0 +1,94 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "wpa_auth.h"
+#include "ap_drv_ops.h"
+#include "tkip_countermeasures.h"
+
+
+static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
+						void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	hapd->tkip_countermeasures = 0;
+	hostapd_drv_set_countermeasures(hapd, 0);
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
+}
+
+
+static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
+
+	wpa_auth_countermeasures_start(hapd->wpa_auth);
+	hapd->tkip_countermeasures = 1;
+	hostapd_drv_set_countermeasures(hapd, 1);
+	wpa_gtk_rekey(hapd->wpa_auth);
+	eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+	eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
+			       hapd, NULL);
+	for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
+		hostapd_drv_sta_deauth(hapd, sta->addr,
+				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+		ap_sta_set_authorized(hapd, sta, 0);
+		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+		hostapd_drv_sta_remove(hapd, sta->addr);
+	}
+}
+
+
+void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
+{
+	time_t now;
+
+	if (addr && local) {
+		struct sta_info *sta = ap_get_sta(hapd, addr);
+		if (sta != NULL) {
+			wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
+			hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Michael MIC failure detected in "
+				       "received frame");
+			mlme_michaelmicfailure_indication(hapd, addr);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "for not associated STA (" MACSTR
+				   ") ignored", MAC2STR(addr));
+			return;
+		}
+	}
+
+	time(&now);
+	if (now > hapd->michael_mic_failure + 60) {
+		hapd->michael_mic_failures = 1;
+	} else {
+		hapd->michael_mic_failures++;
+		if (hapd->michael_mic_failures > 1)
+			ieee80211_tkip_countermeasures_start(hapd);
+	}
+	hapd->michael_mic_failure = now;
+}
diff --git a/src/ap/tkip_countermeasures.h b/src/ap/tkip_countermeasures.h
new file mode 100644
index 0000000..5a1afce
--- /dev/null
+++ b/src/ap/tkip_countermeasures.h
@@ -0,0 +1,20 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 TKIP_COUNTERMEASURES_H
+#define TKIP_COUNTERMEASURES_H
+
+void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+
+#endif /* TKIP_COUNTERMEASURES_H */
diff --git a/src/ap/utils.c b/src/ap/utils.c
new file mode 100644
index 0000000..0ff48ae
--- /dev/null
+++ b/src/ap/utils.c
@@ -0,0 +1,88 @@
+/*
+ * AP mode helper functions
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "common.h"
+#include "common/ieee802_11_defs.h"
+#include "sta_info.h"
+#include "hostapd.h"
+
+
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+				 int (*cb)(void *ctx, const u8 *sa,
+					   const u8 *ie, size_t ie_len),
+				 void *ctx)
+{
+	struct hostapd_probereq_cb *n;
+
+	n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) *
+		       sizeof(struct hostapd_probereq_cb));
+	if (n == NULL)
+		return -1;
+
+	hapd->probereq_cb = n;
+	n = &hapd->probereq_cb[hapd->num_probereq_cb];
+	hapd->num_probereq_cb++;
+
+	n->cb = cb;
+	n->ctx = ctx;
+
+	return 0;
+}
+
+
+struct prune_data {
+	struct hostapd_data *hapd;
+	const u8 *addr;
+};
+
+static int prune_associations(struct hostapd_iface *iface, void *ctx)
+{
+	struct prune_data *data = ctx;
+	struct sta_info *osta;
+	struct hostapd_data *ohapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		ohapd = iface->bss[j];
+		if (ohapd == data->hapd)
+			continue;
+		osta = ap_get_sta(ohapd, data->addr);
+		if (!osta)
+			continue;
+
+		ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
+	}
+
+	return 0;
+}
+
+/**
+ * hostapd_prune_associations - Remove extraneous associations
+ * @hapd: Pointer to BSS data for the most recent association
+ * @addr: Associated STA address
+ *
+ * This function looks through all radios and BSS's for previous
+ * (stale) associations of STA. If any are found they are removed.
+ */
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct prune_data data;
+	data.hapd = hapd;
+	data.addr = addr;
+	if (hapd->iface->for_each_interface)
+		hapd->iface->for_each_interface(hapd->iface->interfaces,
+						prune_associations, &data);
+}
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
new file mode 100644
index 0000000..f2f766f
--- /dev/null
+++ b/src/ap/vlan_init.c
@@ -0,0 +1,905 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "vlan_init.h"
+
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_vlan.h>
+#include <linux/if_bridge.h>
+
+#include "drivers/priv_netlink.h"
+#include "utils/eloop.h"
+
+
+struct full_dynamic_vlan {
+	int s; /* socket on which to listen for new/removed interfaces. */
+};
+
+
+static int ifconfig_helper(const char *if_name, int up)
+{
+	int fd;
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+	if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+			   "for interface %s: %s",
+			   __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	if (up)
+		ifr.ifr_flags |= IFF_UP;
+	else
+		ifr.ifr_flags &= ~IFF_UP;
+
+	if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+			   "for interface %s (up=%d): %s",
+			   __func__, if_name, up, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int ifconfig_up(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+	return ifconfig_helper(if_name, 1);
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+	return ifconfig_helper(if_name, 0);
+}
+
+
+/*
+ * These are only available in recent linux headers (without the leading
+ * underscore).
+ */
+#define _GET_VLAN_REALDEV_NAME_CMD	8
+#define _GET_VLAN_VID_CMD		9
+
+/* This value should be 256 ONLY. If it is something else, then hostapd
+ * might crash!, as this value has been hard-coded in 2.4.x kernel
+ * bridging code.
+ */
+#define MAX_BR_PORTS      		256
+
+static int br_delif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_DEL_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (__caddr_t) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+		/* No error if interface already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add interface 'if_name' to the bridge 'br_name'
+
+	returns -1 on error
+	returns 1 if the interface is already part of the bridge
+	returns 0 otherwise
+*/
+static int br_addif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_ADD_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (__caddr_t) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		if (errno == EBUSY) {
+			/* The interface is already added. */
+			close(fd);
+			return 1;
+		}
+
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[2];
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_DEL_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+		/* No error if bridge already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a bridge with the name 'br_name'.
+
+	returns -1 on error
+	returns 1 if the bridge already exists
+	returns 0 otherwise
+*/
+static int br_addbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[4];
+	struct ifreq ifr;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_ADD_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+ 		if (errno == EEXIST) {
+			/* The bridge is already added. */
+			close(fd);
+			return 1;
+		} else {
+			wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+				   "failed for %s: %s",
+				   __func__, br_name, strerror(errno));
+			close(fd);
+			return -1;
+		}
+	}
+
+	/* Decrease forwarding delay to avoid EAPOL timeouts. */
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+	arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+	arg[1] = 1;
+	arg[2] = 0;
+	arg[3] = 0;
+	ifr.ifr_data = (char *) &arg;
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: "
+			   "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		/* Continue anyway */
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int br_getnumports(const char *br_name)
+{
+	int fd;
+	int i;
+	int port_cnt = 0;
+	unsigned long arg[4];
+	int ifindices[MAX_BR_PORTS];
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_GET_PORT_LIST;
+	arg[1] = (unsigned long) ifindices;
+	arg[2] = MAX_BR_PORTS;
+	arg[3] = 0;
+
+	os_memset(ifindices, 0, sizeof(ifindices));
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (__caddr_t) arg;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+			   "failed for %s: %s",
+			   __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_BR_PORTS; i++) {
+		if (ifindices[i] > 0) {
+			port_cnt++;
+		}
+	}
+
+	close(fd);
+	return port_cnt;
+}
+
+
+static int vlan_rem(const char *if_name)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.cmd = DEL_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+			   "%s", __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a vlan interface with VLAN ID 'vid' and tagged interface
+	'if_name'.
+
+	returns -1 on error
+	returns 1 if the interface already exists
+	returns 0 otherwise
+*/
+static int vlan_add(const char *if_name, int vid)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+		   if_name, vid);
+	ifconfig_up(if_name);
+
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	/* Determine if a suitable vlan device already exists. */
+
+	os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+		    vid);
+
+	if_request.cmd = _GET_VLAN_VID_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
+
+		if (if_request.u.VID == vid) {
+			if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
+
+			if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+			    os_strncmp(if_request.u.device2, if_name,
+				       sizeof(if_request.u.device2)) == 0) {
+				close(fd);
+				wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
+					   "if_name %s exists already",
+					   if_request.device1);
+				return 1;
+			}
+		}
+	}
+
+	/* A suitable vlan device does not already exist, add one. */
+
+	os_memset(&if_request, 0, sizeof(if_request));
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.u.VID = vid;
+	if_request.cmd = ADD_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
+			   "%s",
+			   __func__, if_request.device1, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int vlan_set_name_type(unsigned int name_type)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+		   name_type);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	if_request.u.name_type = name_type;
+	if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
+			   "name_type=%u failed: %s",
+			   __func__, name_type, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	char br_name[IFNAMSIZ];
+	struct hostapd_vlan *vlan = hapd->conf->vlan;
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+	while (vlan) {
+		if (os_strcmp(ifname, vlan->ifname) == 0) {
+
+			os_snprintf(br_name, sizeof(br_name), "brvlan%d",
+				    vlan->vlan_id);
+
+			if (!br_addbr(br_name))
+				vlan->clean |= DVLAN_CLEAN_BR;
+
+			ifconfig_up(br_name);
+
+			if (tagged_interface) {
+
+				if (!vlan_add(tagged_interface, vlan->vlan_id))
+					vlan->clean |= DVLAN_CLEAN_VLAN;
+
+				os_snprintf(vlan_ifname, sizeof(vlan_ifname),
+					    "vlan%d", vlan->vlan_id);
+
+				if (!br_addif(br_name, vlan_ifname))
+					vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
+
+				ifconfig_up(vlan_ifname);
+			}
+
+			if (!br_addif(br_name, ifname))
+				vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+
+			ifconfig_up(ifname);
+
+			break;
+		}
+		vlan = vlan->next;
+	}
+}
+
+
+static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	char br_name[IFNAMSIZ];
+	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+	first = prev = vlan;
+
+	while (vlan) {
+		if (os_strcmp(ifname, vlan->ifname) == 0) {
+			os_snprintf(br_name, sizeof(br_name), "brvlan%d",
+				    vlan->vlan_id);
+
+			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+				br_delif(br_name, vlan->ifname);
+
+			if (tagged_interface) {
+				os_snprintf(vlan_ifname, sizeof(vlan_ifname),
+					    "vlan%d", vlan->vlan_id);
+				if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
+					br_delif(br_name, vlan_ifname);
+				ifconfig_down(vlan_ifname);
+
+				if (vlan->clean & DVLAN_CLEAN_VLAN)
+					vlan_rem(vlan_ifname);
+			}
+
+			if ((vlan->clean & DVLAN_CLEAN_BR) &&
+			    br_getnumports(br_name) == 0) {
+				ifconfig_down(br_name);
+				br_delbr(br_name);
+			}
+
+			if (vlan == first) {
+				hapd->conf->vlan = vlan->next;
+			} else {
+				prev->next = vlan->next;
+			}
+			os_free(vlan);
+
+			break;
+		}
+		prev = vlan;
+		vlan = vlan->next;
+	}
+}
+
+
+static void
+vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+		  struct hostapd_data *hapd)
+{
+	struct ifinfomsg *ifi;
+	int attrlen, nlmsg_len, rta_len;
+	struct rtattr *attr;
+
+	if (len < sizeof(*ifi))
+		return;
+
+	ifi = NLMSG_DATA(h);
+
+	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+	attrlen = h->nlmsg_len - nlmsg_len;
+	if (attrlen < 0)
+		return;
+
+	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		char ifname[IFNAMSIZ + 1];
+
+		if (attr->rta_type == IFLA_IFNAME) {
+			int n = attr->rta_len - rta_len;
+			if (n < 0)
+				break;
+
+			os_memset(ifname, 0, sizeof(ifname));
+
+			if ((size_t) n > sizeof(ifname))
+				n = sizeof(ifname);
+			os_memcpy(ifname, ((char *) attr) + rta_len, n);
+
+			if (del)
+				vlan_dellink(ifname, hapd);
+			else
+				vlan_newlink(ifname, hapd);
+		}
+
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	char buf[8192];
+	int left;
+	struct sockaddr_nl from;
+	socklen_t fromlen;
+	struct nlmsghdr *h;
+	struct hostapd_data *hapd = eloop_ctx;
+
+	fromlen = sizeof(from);
+	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+			(struct sockaddr *) &from, &fromlen);
+	if (left < 0) {
+		if (errno != EINTR && errno != EAGAIN)
+			wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+				   __func__, strerror(errno));
+		return;
+	}
+
+	h = (struct nlmsghdr *) buf;
+	while (left >= (int) sizeof(*h)) {
+		int len, plen;
+
+		len = h->nlmsg_len;
+		plen = len - sizeof(*h);
+		if (len > left || plen < 0) {
+			wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
+				   "message: len=%d left=%d plen=%d",
+				   len, left, plen);
+			break;
+		}
+
+		switch (h->nlmsg_type) {
+		case RTM_NEWLINK:
+			vlan_read_ifnames(h, plen, 0, hapd);
+			break;
+		case RTM_DELLINK:
+			vlan_read_ifnames(h, plen, 1, hapd);
+			break;
+		}
+
+		len = NLMSG_ALIGN(len);
+		left -= len;
+		h = (struct nlmsghdr *) ((char *) h + len);
+	}
+
+	if (left > 0) {
+		wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+			   "netlink message", __func__, left);
+	}
+}
+
+
+static struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd)
+{
+	struct sockaddr_nl local;
+	struct full_dynamic_vlan *priv;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+
+	vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+
+	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (priv->s < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+			   "NETLINK_ROUTE) failed: %s",
+			   __func__, strerror(errno));
+		os_free(priv);
+		return NULL;
+	}
+
+	os_memset(&local, 0, sizeof(local));
+	local.nl_family = AF_NETLINK;
+	local.nl_groups = RTMGRP_LINK;
+	if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+			   __func__, strerror(errno));
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
+	{
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+{
+	if (priv == NULL)
+		return;
+	eloop_unregister_read_sock(priv->s);
+	close(priv->s);
+	os_free(priv);
+}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+			      struct hostapd_ssid *mssid, const char *dyn_vlan)
+{
+        int i;
+
+        if (dyn_vlan == NULL)
+		return 0;
+
+	/* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
+	 * functions for setting up dynamic broadcast keys. */
+	for (i = 0; i < 4; i++) {
+		if (mssid->wep.key[i] &&
+		    hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
+					i == mssid->wep.idx, NULL, 0,
+					mssid->wep.key[i], mssid->wep.len[i]))
+		{
+			wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
+				   "encryption for dynamic VLAN");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int vlan_dynamic_add(struct hostapd_data *hapd,
+			    struct hostapd_vlan *vlan)
+{
+	while (vlan) {
+		if (vlan->vlan_id != VLAN_ID_WILDCARD) {
+			if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
+				if (errno != EEXIST) {
+					wpa_printf(MSG_ERROR, "VLAN: Could "
+						   "not add VLAN %s: %s",
+						   vlan->ifname,
+						   strerror(errno));
+					return -1;
+				}
+			}
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+			ifconfig_up(vlan->ifname);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+		}
+
+		vlan = vlan->next;
+	}
+
+	return 0;
+}
+
+
+static void vlan_dynamic_remove(struct hostapd_data *hapd,
+				struct hostapd_vlan *vlan)
+{
+	struct hostapd_vlan *next;
+
+	while (vlan) {
+		next = vlan->next;
+
+		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
+		    hostapd_vlan_if_remove(hapd, vlan->ifname)) {
+			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
+				   "iface: %s: %s",
+				   vlan->ifname, strerror(errno));
+		}
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		if (vlan->clean)
+			vlan_dellink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+		vlan = next;
+	}
+}
+
+
+int vlan_init(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	if (vlan_dynamic_add(hapd, hapd->conf->vlan))
+		return -1;
+
+        return 0;
+}
+
+
+void vlan_deinit(struct hostapd_data *hapd)
+{
+	vlan_dynamic_remove(hapd, hapd->conf->vlan);
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+}
+
+
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+				       struct hostapd_vlan *vlan,
+				       int vlan_id)
+{
+	struct hostapd_vlan *n;
+	char *ifname, *pos;
+
+	if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
+	    vlan->vlan_id != VLAN_ID_WILDCARD)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
+		   __func__, vlan_id, vlan->ifname);
+	ifname = os_strdup(vlan->ifname);
+	if (ifname == NULL)
+		return NULL;
+	pos = os_strchr(ifname, '#');
+	if (pos == NULL) {
+		os_free(ifname);
+		return NULL;
+	}
+	*pos++ = '\0';
+
+	n = os_zalloc(sizeof(*n));
+	if (n == NULL) {
+		os_free(ifname);
+		return NULL;
+	}
+
+	n->vlan_id = vlan_id;
+	n->dynamic_vlan = 1;
+
+	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
+		    pos);
+	os_free(ifname);
+
+	if (hostapd_vlan_if_add(hapd, n->ifname)) {
+		os_free(n);
+		return NULL;
+	}
+
+	n->next = hapd->conf->vlan;
+	hapd->conf->vlan = n;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	ifconfig_up(n->ifname);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	return n;
+}
+
+
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+	struct hostapd_vlan *vlan;
+
+	if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
+		return 1;
+
+	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id);
+
+	vlan = hapd->conf->vlan;
+	while (vlan) {
+		if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
+			vlan->dynamic_vlan--;
+			break;
+		}
+		vlan = vlan->next;
+	}
+
+	if (vlan == NULL)
+		return 1;
+
+	if (vlan->dynamic_vlan == 0)
+		hostapd_vlan_if_remove(hapd, vlan->ifname);
+
+	return 0;
+}
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
new file mode 100644
index 0000000..382d5de
--- /dev/null
+++ b/src/ap/vlan_init.h
@@ -0,0 +1,59 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005, 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 VLAN_INIT_H
+#define VLAN_INIT_H
+
+#ifndef CONFIG_NO_VLAN
+int vlan_init(struct hostapd_data *hapd);
+void vlan_deinit(struct hostapd_data *hapd);
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+				       struct hostapd_vlan *vlan,
+				       int vlan_id);
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+			      struct hostapd_ssid *mssid,
+			      const char *dyn_vlan);
+#else /* CONFIG_NO_VLAN */
+static inline int vlan_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void vlan_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+						     struct hostapd_vlan *vlan,
+						     int vlan_id)
+{
+	return NULL;
+}
+
+static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+	return -1;
+}
+
+static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+					    struct hostapd_ssid *mssid,
+					    const char *dyn_vlan)
+{
+	return -1;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_INIT_H */
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
new file mode 100644
index 0000000..a6d9b89
--- /dev/null
+++ b/src/ap/wmm.c
@@ -0,0 +1,327 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wmm.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 WMM stations are receiving a certain group */
+
+
+static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
+{
+	u8 ret;
+	ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
+	if (acm)
+		ret |= WMM_AC_ACM;
+	ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
+	return ret;
+}
+
+
+static inline u8 wmm_ecw(int ecwmin, int ecwmax)
+{
+	return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
+		((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
+}
+
+
+/*
+ * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
+ * Response frames.
+ */
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	struct wmm_parameter_element *wmm =
+		(struct wmm_parameter_element *) (pos + 2);
+	int e;
+
+	if (!hapd->conf->wmm_enabled)
+		return eid;
+	eid[0] = WLAN_EID_VENDOR_SPECIFIC;
+	wmm->oui[0] = 0x00;
+	wmm->oui[1] = 0x50;
+	wmm->oui[2] = 0xf2;
+	wmm->oui_type = WMM_OUI_TYPE;
+	wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
+	wmm->version = WMM_VERSION;
+	wmm->qos_info = hapd->parameter_set_count & 0xf;
+
+	if (hapd->conf->wmm_uapsd)
+		wmm->qos_info |= 0x80;
+
+	wmm->reserved = 0;
+
+	/* fill in a parameter set record for each AC */
+	for (e = 0; e < 4; e++) {
+		struct wmm_ac_parameter *ac = &wmm->ac[e];
+		struct hostapd_wmm_ac_params *acp =
+			&hapd->iconf->wmm_ac_params[e];
+
+		ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
+					      acp->admission_control_mandatory,
+					      e);
+		ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
+		ac->txop_limit = host_to_le16(acp->txop_limit);
+	}
+
+	pos = (u8 *) (wmm + 1);
+	eid[1] = pos - eid - 2; /* element length */
+
+	return pos;
+}
+
+
+/* This function is called when a station sends an association request with
+ * WMM info element. The function returns zero on success or non-zero on any
+ * error in WMM element. eid does not include Element ID and Length octets. */
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
+{
+	struct wmm_information_element *wmm;
+
+	wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
+
+	if (len < sizeof(struct wmm_information_element)) {
+		wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+
+	wmm = (struct wmm_information_element *) eid;
+	wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x  "
+		   "OUI type %d  OUI sub-type %d  version %d  QoS info 0x%x",
+		   wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
+		   wmm->oui_subtype, wmm->version, wmm->qos_info);
+	if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
+	    wmm->version != WMM_VERSION) {
+		wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void 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)
+{
+	u8 buf[256];
+	struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
+	struct wmm_tspec_element *t = (struct wmm_tspec_element *)
+		m->u.action.u.wmm_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.wmm_action.action_code = action_code;
+	m->u.action.u.wmm_action.dialog_token = dialogue_token;
+	m->u.action.u.wmm_action.status_code = status_code;
+	os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
+	len = ((u8 *) (t + 1)) - buf;
+
+	if (hostapd_drv_send_mlme(hapd, m, len) < 0)
+		perror("wmm_send_action: send");
+}
+
+
+int wmm_process_tspec(struct wmm_tspec_element *tspec)
+{
+	int medium_time, pps, duration;
+	int up, psb, dir, tid;
+	u16 val, surplus;
+
+	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)");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+	/* 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");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+
+	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");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+
+	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");
+		return WMM_ADDTS_STATUS_REFUSED;
+	}
+
+	/* Convert to 32 microseconds per second unit */
+	tspec->medium_time = host_to_le16(medium_time / 32);
+
+	return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
+}
+
+
+static void wmm_addts_req(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt,
+			  struct wmm_tspec_element *tspec, size_t len)
+{
+	const u8 *end = ((const u8 *) mgmt) + len;
+	int res;
+
+	if ((const u8 *) (tspec + 1) > end) {
+		wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
+		return;
+	}
+
+	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));
+
+	res = wmm_process_tspec(tspec);
+	wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
+
+	wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
+			mgmt->u.action.u.wmm_action.dialog_token, res);
+}
+
+
+void hostapd_wmm_action(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	int action_code;
+	int left = len - IEEE80211_HDRLEN - 4;
+	const u8 *pos = ((const 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_WMM)) !=
+	    (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "wmm action received is not from associated wmm"
+			       " 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_wmm_action - could not parse wmm "
+			       "action");
+		/* TODO: respond with action frame invalid parameters status
+		 * code */
+		return;
+	}
+
+	if (!elems.wmm_tspec ||
+	    elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "hostapd_wmm_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.wmm_action.action_code;
+	switch (action_code) {
+	case WMM_ACTION_CODE_ADDTS_REQ:
+		wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
+			      (elems.wmm_tspec - 2), len);
+		return;
+#if 0
+	/* TODO: needed for client implementation */
+	case WMM_ACTION_CODE_ADDTS_RESP:
+		wmm_setup_request(hapd, mgmt, len);
+		return;
+	/* TODO: handle station teardown requests */
+	case WMM_ACTION_CODE_DELTS:
+		wmm_teardown(hapd, mgmt, len);
+		return;
+#endif
+	}
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "hostapd_wmm_action - unknown action code %d",
+		       action_code);
+}
diff --git a/src/ap/wmm.h b/src/ap/wmm.h
new file mode 100644
index 0000000..96b04e8
--- /dev/null
+++ b/src/ap/wmm.h
@@ -0,0 +1,29 @@
+/*
+ * 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
+
+struct ieee80211_mgmt;
+struct wmm_tspec_element;
+
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid);
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid,
+			  size_t len);
+void hostapd_wmm_action(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len);
+int wmm_process_tspec(struct wmm_tspec_element *tspec);
+
+#endif /* WME_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
new file mode 100644
index 0000000..cfb2cad
--- /dev/null
+++ b/src/ap/wpa_auth.c
@@ -0,0 +1,2838 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/state_machine.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+#define STATE_MACHINE_DATA struct wpa_state_machine
+#define STATE_MACHINE_DEBUG_PREFIX "WPA"
+#define STATE_MACHINE_ADDR sm->addr
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_sm_step(struct wpa_state_machine *sm);
+static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len);
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group);
+static void wpa_request_new_ptk(struct wpa_state_machine *sm);
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+				       struct wpa_group *group);
+
+static const u32 dot11RSNAConfigGroupUpdateCount = 4;
+static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
+static const u32 eapol_key_timeout_first = 100; /* ms */
+static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+static const int dot11RSNAConfigPMKReauthThreshold = 70;
+static const int dot11RSNAConfigSATimeout = 60;
+
+
+static inline void wpa_auth_mic_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb.mic_failure_report)
+		wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+}
+
+
+static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, wpa_eapol_variable var,
+				      int value)
+{
+	if (wpa_auth->cb.set_eapol)
+		wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
+}
+
+
+static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
+				     const u8 *addr, wpa_eapol_variable var)
+{
+	if (wpa_auth->cb.get_eapol == NULL)
+		return -1;
+	return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
+}
+
+
+static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
+					  const u8 *addr, const u8 *prev_psk)
+{
+	if (wpa_auth->cb.get_psk == NULL)
+		return NULL;
+	return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk);
+}
+
+
+static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
+				   const u8 *addr, u8 *msk, size_t *len)
+{
+	if (wpa_auth->cb.get_msk == NULL)
+		return -1;
+	return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+				   int vlan_id,
+				   enum wpa_alg alg, const u8 *addr, int idx,
+				   u8 *key, size_t key_len)
+{
+	if (wpa_auth->cb.set_key == NULL)
+		return -1;
+	return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+				    key, key_len);
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, int idx, u8 *seq)
+{
+	if (wpa_auth->cb.get_seqnum == NULL)
+		return -1;
+	return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+}
+
+
+static inline int
+wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		    const u8 *data, size_t data_len, int encrypt)
+{
+	if (wpa_auth->cb.send_eapol == NULL)
+		return -1;
+	return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
+				       encrypt);
+}
+
+
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
+			  void *cb_ctx)
+{
+	if (wpa_auth->cb.for_each_sta == NULL)
+		return 0;
+	return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
+}
+
+
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+			   int (*cb)(struct wpa_authenticator *a, void *ctx),
+			   void *cb_ctx)
+{
+	if (wpa_auth->cb.for_each_auth == NULL)
+		return 0;
+	return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
+}
+
+
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		     logger_level level, const char *txt)
+{
+	if (wpa_auth->cb.logger == NULL)
+		return;
+	wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
+}
+
+
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		      logger_level level, const char *fmt, ...)
+{
+	char *format;
+	int maxlen;
+	va_list ap;
+
+	if (wpa_auth->cb.logger == NULL)
+		return;
+
+	maxlen = os_strlen(fmt) + 100;
+	format = os_malloc(maxlen);
+	if (!format)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(format, maxlen, fmt, ap);
+	va_end(ap);
+
+	wpa_auth_logger(wpa_auth, addr, level, format);
+
+	os_free(format);
+}
+
+
+static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
+			       const u8 *addr)
+{
+	if (wpa_auth->cb.disconnect == NULL)
+		return;
+	wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
+				WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
+{
+	int ret = 0;
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		ret = 1;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
+		ret = 1;
+#endif /* CONFIG_IEEE80211W */
+	return ret;
+}
+
+
+static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+	if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
+		wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
+			   "initialization.");
+	} else {
+		wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+		wpa_hexdump_key(MSG_DEBUG, "GMK",
+				wpa_auth->group->GMK, WPA_GMK_LEN);
+	}
+
+	if (wpa_auth->conf.wpa_gmk_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+				       wpa_rekey_gmk, wpa_auth, NULL);
+	}
+}
+
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_group *group;
+
+	wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+	for (group = wpa_auth->group; group; group = group->next) {
+		group->GTKReKey = TRUE;
+		do {
+			group->changed = FALSE;
+			wpa_group_sm_step(wpa_auth, group);
+		} while (group->changed);
+	}
+
+	if (wpa_auth->conf.wpa_group_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
+				       0, wpa_rekey_gtk, wpa_auth, NULL);
+	}
+}
+
+
+static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_state_machine *sm = timeout_ctx;
+
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+	wpa_request_new_ptk(sm);
+	wpa_sm_step(sm);
+}
+
+
+static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	if (sm->pmksa == ctx)
+		sm->pmksa = NULL;
+	return 0;
+}
+
+
+static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+				   void *ctx)
+{
+	struct wpa_authenticator *wpa_auth = ctx;
+	wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
+}
+
+
+static void wpa_group_set_key_len(struct wpa_group *group, int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+		group->GTK_len = 16;
+		break;
+	case WPA_CIPHER_TKIP:
+		group->GTK_len = 32;
+		break;
+	case WPA_CIPHER_WEP104:
+		group->GTK_len = 13;
+		break;
+	case WPA_CIPHER_WEP40:
+		group->GTK_len = 5;
+		break;
+	}
+}
+
+
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+					  struct wpa_group *group)
+{
+	u8 buf[ETH_ALEN + 8 + sizeof(group)];
+	u8 rkey[32];
+
+	if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
+
+	/*
+	 * Counter = PRF-256(Random number, "Init Counter",
+	 *                   Local MAC Address || Time)
+	 */
+	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+	wpa_get_ntp_timestamp(buf + ETH_ALEN);
+	os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+	if (random_get_bytes(rkey, sizeof(rkey)) < 0)
+		return -1;
+
+	if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
+		     group->Counter, WPA_NONCE_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+			group->Counter, WPA_NONCE_LEN);
+
+	return 0;
+}
+
+
+static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
+					 int vlan_id)
+{
+	struct wpa_group *group;
+
+	group = os_zalloc(sizeof(struct wpa_group));
+	if (group == NULL)
+		return NULL;
+
+	group->GTKAuthenticator = TRUE;
+	group->vlan_id = vlan_id;
+
+	wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+
+	if (random_pool_ready() != 1) {
+		wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+			   "for secure operations - update keys later when "
+			   "the first station connects");
+	}
+
+	/*
+	 * Set initial GMK/Counter value here. The actual values that will be
+	 * used in negotiations will be set once the first station tries to
+	 * connect. This allows more time for collecting additional randomness
+	 * on embedded devices.
+	 */
+	if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
+			   "initialization.");
+		os_free(group);
+		return NULL;
+	}
+
+	group->GInit = TRUE;
+	wpa_group_sm_step(wpa_auth, group);
+	group->GInit = FALSE;
+	wpa_group_sm_step(wpa_auth, group);
+
+	return group;
+}
+
+
+/**
+ * wpa_init - Initialize WPA authenticator
+ * @addr: Authenticator address
+ * @conf: Configuration for WPA authenticator
+ * @cb: Callback functions for WPA authenticator
+ * Returns: Pointer to WPA authenticator data or %NULL on failure
+ */
+struct wpa_authenticator * wpa_init(const u8 *addr,
+				    struct wpa_auth_config *conf,
+				    struct wpa_auth_callbacks *cb)
+{
+	struct wpa_authenticator *wpa_auth;
+
+	wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
+	if (wpa_auth == NULL)
+		return NULL;
+	os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+	os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
+
+	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+	wpa_auth->group = wpa_group_init(wpa_auth, 0);
+	if (wpa_auth->group == NULL) {
+		os_free(wpa_auth->wpa_ie);
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+	wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
+						wpa_auth);
+	if (wpa_auth->pmksa == NULL) {
+		wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+		os_free(wpa_auth->wpa_ie);
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
+	if (wpa_auth->ft_pmk_cache == NULL) {
+		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+		os_free(wpa_auth->wpa_ie);
+		pmksa_cache_auth_deinit(wpa_auth->pmksa);
+		os_free(wpa_auth);
+		return NULL;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (wpa_auth->conf.wpa_gmk_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+				       wpa_rekey_gmk, wpa_auth, NULL);
+	}
+
+	if (wpa_auth->conf.wpa_group_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
+				       wpa_rekey_gtk, wpa_auth, NULL);
+	}
+
+	return wpa_auth;
+}
+
+
+/**
+ * wpa_deinit - Deinitialize WPA authenticator
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ */
+void wpa_deinit(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group, *prev;
+
+	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+#ifdef CONFIG_PEERKEY
+	while (wpa_auth->stsl_negotiations)
+		wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
+#endif /* CONFIG_PEERKEY */
+
+	pmksa_cache_auth_deinit(wpa_auth->pmksa);
+
+#ifdef CONFIG_IEEE80211R
+	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
+	wpa_auth->ft_pmk_cache = NULL;
+#endif /* CONFIG_IEEE80211R */
+
+	os_free(wpa_auth->wpa_ie);
+
+	group = wpa_auth->group;
+	while (group) {
+		prev = group;
+		group = group->next;
+		os_free(prev);
+	}
+
+	os_free(wpa_auth);
+}
+
+
+/**
+ * wpa_reconfig - Update WPA authenticator configuration
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ * @conf: Configuration for WPA authenticator
+ */
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+		 struct wpa_auth_config *conf)
+{
+	struct wpa_group *group;
+	if (wpa_auth == NULL)
+		return 0;
+
+	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+		return -1;
+	}
+
+	/*
+	 * Reinitialize GTK to make sure it is suitable for the new
+	 * configuration.
+	 */
+	group = wpa_auth->group;
+	wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+	group->GInit = TRUE;
+	wpa_group_sm_step(wpa_auth, group);
+	group->GInit = FALSE;
+	wpa_group_sm_step(wpa_auth, group);
+
+	return 0;
+}
+
+
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	struct wpa_state_machine *sm;
+
+	sm = os_zalloc(sizeof(struct wpa_state_machine));
+	if (sm == NULL)
+		return NULL;
+	os_memcpy(sm->addr, addr, ETH_ALEN);
+
+	sm->wpa_auth = wpa_auth;
+	sm->group = wpa_auth->group;
+
+	return sm;
+}
+
+
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm)
+{
+	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+		return -1;
+
+#ifdef CONFIG_IEEE80211R
+	if (sm->ft_completed) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"FT authentication already completed - do not "
+				"start 4-way handshake");
+		return 0;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (sm->started) {
+		os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
+		sm->ReAuthenticationRequest = TRUE;
+		return wpa_sm_step(sm);
+	}
+
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			"start authentication");
+	sm->started = 1;
+
+	sm->Init = TRUE;
+	if (wpa_sm_step(sm) == 1)
+		return 1; /* should not really happen */
+	sm->Init = FALSE;
+	sm->AuthenticationRequest = TRUE;
+	return wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+{
+	/* WPA/RSN was not used - clear WPA state. This is needed if the STA
+	 * reassociates back to the same AP while the previous entry for the
+	 * STA has not yet been removed. */
+	if (sm == NULL)
+		return;
+
+	sm->wpa_key_mgmt = 0;
+}
+
+
+static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+{
+	if (sm->GUpdateStationKeys) {
+		sm->group->GKeyDoneStations--;
+		sm->GUpdateStationKeys = FALSE;
+	}
+#ifdef CONFIG_IEEE80211R
+	os_free(sm->assoc_resp_ftie);
+#endif /* CONFIG_IEEE80211R */
+	os_free(sm->last_rx_eapol_key);
+	os_free(sm->wpa_ie);
+	os_free(sm);
+}
+
+
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return;
+
+	if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"strict rekeying - force GTK rekey since STA "
+				"is leaving");
+		eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
+		eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
+				       NULL);
+	}
+
+	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+	sm->pending_1_of_4_timeout = 0;
+	eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
+	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+	if (sm->in_step_loop) {
+		/* Must not free state machine while wpa_sm_step() is running.
+		 * Freeing will be completed in the end of wpa_sm_step(). */
+		wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state "
+			   "machine deinit for " MACSTR, MAC2STR(sm->addr));
+		sm->pending_deinit = 1;
+	} else
+		wpa_free_sta_sm(sm);
+}
+
+
+static void wpa_request_new_ptk(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return;
+
+	sm->PTKRequest = TRUE;
+	sm->PTK_valid = 0;
+}
+
+
+static int wpa_replay_counter_valid(struct wpa_state_machine *sm,
+				    const u8 *replay_counter)
+{
+	int i;
+	for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+		if (!sm->key_replay[i].valid)
+			break;
+		if (os_memcmp(replay_counter, sm->key_replay[i].counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+			       struct wpa_state_machine *sm,
+			       struct wpa_eapol_ie_parse *kde)
+{
+	struct wpa_ie_data ie;
+	struct rsn_mdie *mdie;
+
+	if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
+	    ie.num_pmkid != 1 || ie.pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
+			   "FT 4-way handshake message 2/4");
+		return -1;
+	}
+
+	os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+		    sm->sup_pmk_r1_name, PMKID_LEN);
+
+	if (!kde->mdie || !kde->ftie) {
+		wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake "
+			   "message 2/4", kde->mdie ? "FTIE" : "MDIE");
+		return -1;
+	}
+
+	mdie = (struct rsn_mdie *) (kde->mdie + 2);
+	if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+	    os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+		return -1;
+	}
+
+	if (sm->assoc_resp_ftie &&
+	    (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+	     os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+		       2 + sm->assoc_resp_ftie[1]) != 0)) {
+		wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+			    kde->ftie, kde->ftie_len);
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+			    sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+		 struct wpa_state_machine *sm,
+		 u8 *data, size_t data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u16 key_info, key_data_length;
+	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
+	       SMK_M1, SMK_M3, SMK_ERROR } msg;
+	char *msgtxt;
+	struct wpa_eapol_ie_parse kde;
+	int ft;
+	const u8 *eapol_key_ie;
+	size_t eapol_key_ie_len;
+
+	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+		return;
+
+	if (data_len < sizeof(*hdr) + sizeof(*key))
+		return;
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	key_data_length = WPA_GET_BE16(key->key_data_length);
+	if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
+		wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
+			   "key_data overflow (%d > %lu)",
+			   key_data_length,
+			   (unsigned long) (data_len - sizeof(*hdr) -
+					    sizeof(*key)));
+		return;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		if (key->type != EAPOL_KEY_TYPE_RSN) {
+			wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+				   "unexpected type %d in RSN mode",
+				   key->type);
+			return;
+		}
+	} else {
+		if (key->type != EAPOL_KEY_TYPE_WPA) {
+			wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+				   "unexpected type %d in WPA mode",
+				   key->type);
+			return;
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
+		    WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
+		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+	/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
+	 * are set */
+
+	if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
+	    (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
+		if (key_info & WPA_KEY_INFO_ERROR) {
+			msg = SMK_ERROR;
+			msgtxt = "SMK Error";
+		} else {
+			msg = SMK_M1;
+			msgtxt = "SMK M1";
+		}
+	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		msg = SMK_M3;
+		msgtxt = "SMK M3";
+	} else if (key_info & WPA_KEY_INFO_REQUEST) {
+		msg = REQUEST;
+		msgtxt = "Request";
+	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+		msg = GROUP_2;
+		msgtxt = "2/2 Group";
+	} else if (key_data_length == 0) {
+		msg = PAIRWISE_4;
+		msgtxt = "4/4 Pairwise";
+	} else {
+		msg = PAIRWISE_2;
+		msgtxt = "2/4 Pairwise";
+	}
+
+	/* TODO: key_info type validation for PeerKey */
+	if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
+	    msg == GROUP_2) {
+		u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+		if (sm->pairwise == WPA_CIPHER_CCMP) {
+			if (wpa_use_aes_cmac(sm) &&
+			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+				wpa_auth_logger(wpa_auth, sm->addr,
+						LOGGER_WARNING,
+						"advertised support for "
+						"AES-128-CMAC, but did not "
+						"use it");
+				return;
+			}
+
+			if (!wpa_use_aes_cmac(sm) &&
+			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+				wpa_auth_logger(wpa_auth, sm->addr,
+						LOGGER_WARNING,
+						"did not use HMAC-SHA1-AES "
+						"with CCMP");
+				return;
+			}
+		}
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		if (sm->req_replay_counter_used &&
+		    os_memcmp(key->replay_counter, sm->req_replay_counter,
+			      WPA_REPLAY_COUNTER_LEN) <= 0) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+					"received EAPOL-Key request with "
+					"replayed counter");
+			return;
+		}
+	}
+
+	if (!(key_info & WPA_KEY_INFO_REQUEST) &&
+	    !wpa_replay_counter_valid(sm, key->replay_counter)) {
+		int i;
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+				 "received EAPOL-Key %s with unexpected "
+				 "replay counter", msgtxt);
+		for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+			if (!sm->key_replay[i].valid)
+				break;
+			wpa_hexdump(MSG_DEBUG, "pending replay counter",
+				    sm->key_replay[i].counter,
+				    WPA_REPLAY_COUNTER_LEN);
+		}
+		wpa_hexdump(MSG_DEBUG, "received replay counter",
+			    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+		return;
+	}
+
+	switch (msg) {
+	case PAIRWISE_2:
+		if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+		    sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 2/4 in "
+					 "invalid state (%d) - dropped",
+					 sm->wpa_ptk_state);
+			return;
+		}
+		random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
+		if (sm->group->reject_4way_hs_for_entropy) {
+			/*
+			 * The system did not have enough entropy to generate
+			 * strong random numbers. Reject the first 4-way
+			 * handshake(s) and collect some entropy based on the
+			 * information from it. Once enough entropy is
+			 * available, the next atempt will trigger GMK/Key
+			 * Counter update and the station will be allowed to
+			 * continue.
+			 */
+			wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
+				   "collect more entropy for random number "
+				   "generation");
+			sm->group->reject_4way_hs_for_entropy = FALSE;
+			random_mark_pool_ready();
+			sm->group->first_sta_seen = FALSE;
+			wpa_sta_disconnect(wpa_auth, sm->addr);
+			return;
+		}
+		if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
+				      &kde) < 0) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 2/4 with "
+					 "invalid Key Data contents");
+			return;
+		}
+		if (kde.rsn_ie) {
+			eapol_key_ie = kde.rsn_ie;
+			eapol_key_ie_len = kde.rsn_ie_len;
+		} else {
+			eapol_key_ie = kde.wpa_ie;
+			eapol_key_ie_len = kde.wpa_ie_len;
+		}
+		ft = sm->wpa == WPA_VERSION_WPA2 &&
+			wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+		if (sm->wpa_ie == NULL ||
+		    wpa_compare_rsn_ie(ft,
+				       sm->wpa_ie, sm->wpa_ie_len,
+				       eapol_key_ie, eapol_key_ie_len)) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"WPA IE from (Re)AssocReq did not "
+					"match with msg 2/4");
+			if (sm->wpa_ie) {
+				wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+					    sm->wpa_ie, sm->wpa_ie_len);
+			}
+			wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+				    eapol_key_ie, eapol_key_ie_len);
+			/* MLME-DEAUTHENTICATE.request */
+			wpa_sta_disconnect(wpa_auth, sm->addr);
+			return;
+		}
+#ifdef CONFIG_IEEE80211R
+		if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+			wpa_sta_disconnect(wpa_auth, sm->addr);
+			return;
+		}
+#endif /* CONFIG_IEEE80211R */
+		break;
+	case PAIRWISE_4:
+		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
+		    !sm->PTK_valid) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 4/4 in "
+					 "invalid state (%d) - dropped",
+					 sm->wpa_ptk_state);
+			return;
+		}
+		break;
+	case GROUP_2:
+		if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
+		    || !sm->PTK_valid) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "received EAPOL-Key msg 2/2 in "
+					 "invalid state (%d) - dropped",
+					 sm->wpa_ptk_group_state);
+			return;
+		}
+		break;
+#ifdef CONFIG_PEERKEY
+	case SMK_M1:
+	case SMK_M3:
+	case SMK_ERROR:
+		if (!wpa_auth->conf.peerkey) {
+			wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
+				   "PeerKey use disabled - ignoring message");
+			return;
+		}
+		if (!sm->PTK_valid) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key msg SMK in "
+					"invalid state - dropped");
+			return;
+		}
+		break;
+#else /* CONFIG_PEERKEY */
+	case SMK_M1:
+	case SMK_M3:
+	case SMK_ERROR:
+		return; /* STSL disabled - ignore SMK messages */
+#endif /* CONFIG_PEERKEY */
+	case REQUEST:
+		break;
+	}
+
+	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			 "received EAPOL-Key frame (%s)", msgtxt);
+
+	if (key_info & WPA_KEY_INFO_ACK) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"received invalid EAPOL-Key: Key Ack set");
+		return;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_MIC)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"received invalid EAPOL-Key: Key MIC not set");
+		return;
+	}
+
+	sm->MICVerified = FALSE;
+	if (sm->PTK_valid) {
+		if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key with invalid MIC");
+			return;
+		}
+		sm->MICVerified = TRUE;
+		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+		sm->pending_1_of_4_timeout = 0;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		if (sm->MICVerified) {
+			sm->req_replay_counter_used = 1;
+			os_memcpy(sm->req_replay_counter, key->replay_counter,
+				  WPA_REPLAY_COUNTER_LEN);
+		} else {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key request with "
+					"invalid MIC");
+			return;
+		}
+
+		/*
+		 * TODO: should decrypt key data field if encryption was used;
+		 * even though MAC address KDE is not normally encrypted,
+		 * supplicant is allowed to encrypt it.
+		 */
+		if (msg == SMK_ERROR) {
+#ifdef CONFIG_PEERKEY
+			wpa_smk_error(wpa_auth, sm, key);
+#endif /* CONFIG_PEERKEY */
+			return;
+		} else if (key_info & WPA_KEY_INFO_ERROR) {
+			/* Supplicant reported a Michael MIC error */
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key Error Request "
+					"(STA detected Michael MIC failure)");
+			wpa_auth_mic_failure_report(wpa_auth, sm->addr);
+			sm->dot11RSNAStatsTKIPRemoteMICFailures++;
+			wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+			/* Error report is not a request for a new key
+			 * handshake, but since Authenticator may do it, let's
+			 * change the keys now anyway. */
+			wpa_request_new_ptk(sm);
+		} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key Request for new "
+					"4-Way Handshake");
+			wpa_request_new_ptk(sm);
+#ifdef CONFIG_PEERKEY
+		} else if (msg == SMK_M1) {
+			wpa_smk_m1(wpa_auth, sm, key);
+#endif /* CONFIG_PEERKEY */
+		} else if (key_data_length > 0 &&
+			   wpa_parse_kde_ies((const u8 *) (key + 1),
+					     key_data_length, &kde) == 0 &&
+			   kde.mac_addr) {
+		} else {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key Request for GTK "
+					"rekeying");
+			/* FIX: why was this triggering PTK rekeying for the
+			 * STA that requested Group Key rekeying?? */
+			/* wpa_request_new_ptk(sta->wpa_sm); */
+			eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+			wpa_rekey_gtk(wpa_auth, NULL);
+		}
+	} else {
+		/* Do not allow the same key replay counter to be reused. This
+		 * does also invalidate all other pending replay counters if
+		 * retransmissions were used, i.e., we will only process one of
+		 * the pending replies and ignore rest if more than one is
+		 * received. */
+		sm->key_replay[0].valid = FALSE;
+	}
+
+#ifdef CONFIG_PEERKEY
+	if (msg == SMK_M3) {
+		wpa_smk_m3(wpa_auth, sm, key);
+		return;
+	}
+#endif /* CONFIG_PEERKEY */
+
+	os_free(sm->last_rx_eapol_key);
+	sm->last_rx_eapol_key = os_malloc(data_len);
+	if (sm->last_rx_eapol_key == NULL)
+		return;
+	os_memcpy(sm->last_rx_eapol_key, data, data_len);
+	sm->last_rx_eapol_key_len = data_len;
+
+	sm->EAPOLKeyReceived = TRUE;
+	sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+	sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
+	os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
+	wpa_sm_step(sm);
+}
+
+
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+			  const u8 *gnonce, u8 *gtk, size_t gtk_len)
+{
+	u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+	u8 *pos;
+	int ret = 0;
+
+	/* GTK = PRF-X(GMK, "Group key expansion",
+	 *	AA || GNonce || Time || random data)
+	 * The example described in the IEEE 802.11 standard uses only AA and
+	 * GNonce as inputs here. Add some more entropy since this derivation
+	 * is done only at the Authenticator and as such, does not need to be
+	 * exactly same.
+	 */
+	os_memcpy(data, addr, ETH_ALEN);
+	os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+	pos = data + ETH_ALEN + WPA_NONCE_LEN;
+	wpa_get_ntp_timestamp(pos);
+	pos += 8;
+	if (random_get_bytes(pos, 16) < 0)
+		ret = -1;
+
+#ifdef CONFIG_IEEE80211W
+	sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
+#else /* CONFIG_IEEE80211W */
+	if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+	    < 0)
+		ret = -1;
+#endif /* CONFIG_IEEE80211W */
+
+	return ret;
+}
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_state_machine *sm = timeout_ctx;
+
+	sm->pending_1_of_4_timeout = 0;
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
+	sm->TimeoutEvt = TRUE;
+	wpa_sm_step(sm);
+}
+
+
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm, int key_info,
+		      const u8 *key_rsc, const u8 *nonce,
+		      const u8 *kde, size_t kde_len,
+		      int keyidx, int encr, int force_version)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	size_t len;
+	int alg;
+	int key_data_len, pad_len = 0;
+	u8 *buf, *pos;
+	int version, pairwise;
+	int i;
+
+	len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
+
+	if (force_version)
+		version = force_version;
+	else if (wpa_use_aes_cmac(sm))
+		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+	else if (sm->pairwise == WPA_CIPHER_CCMP)
+		version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+
+	wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
+		   "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
+		   "encr=%d)",
+		   version,
+		   (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_MIC) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_ACK) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0,
+		   pairwise, (unsigned long) kde_len, keyidx, encr);
+
+	key_data_len = kde_len;
+
+	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
+		pad_len = key_data_len % 8;
+		if (pad_len)
+			pad_len = 8 - pad_len;
+		key_data_len += pad_len + 8;
+	}
+
+	len += key_data_len;
+
+	hdr = os_zalloc(len);
+	if (hdr == NULL)
+		return;
+	hdr->version = wpa_auth->conf.eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = host_to_be16(len  - sizeof(*hdr));
+	key = (struct wpa_eapol_key *) (hdr + 1);
+
+	key->type = sm->wpa == WPA_VERSION_WPA2 ?
+		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+	key_info |= version;
+	if (encr && sm->wpa == WPA_VERSION_WPA2)
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+	if (sm->wpa != WPA_VERSION_WPA2)
+		key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
+	WPA_PUT_BE16(key->key_info, key_info);
+
+	alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
+	switch (alg) {
+	case WPA_CIPHER_CCMP:
+		WPA_PUT_BE16(key->key_length, 16);
+		break;
+	case WPA_CIPHER_TKIP:
+		WPA_PUT_BE16(key->key_length, 32);
+		break;
+	case WPA_CIPHER_WEP40:
+		WPA_PUT_BE16(key->key_length, 5);
+		break;
+	case WPA_CIPHER_WEP104:
+		WPA_PUT_BE16(key->key_length, 13);
+		break;
+	}
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+		WPA_PUT_BE16(key->key_length, 0);
+
+	/* FIX: STSL: what to use as key_replay_counter? */
+	for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+		sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+		os_memcpy(sm->key_replay[i].counter,
+			  sm->key_replay[i - 1].counter,
+			  WPA_REPLAY_COUNTER_LEN);
+	}
+	inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
+	os_memcpy(key->replay_counter, sm->key_replay[0].counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	sm->key_replay[0].valid = TRUE;
+
+	if (nonce)
+		os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
+
+	if (key_rsc)
+		os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
+
+	if (kde && !encr) {
+		os_memcpy(key + 1, kde, kde_len);
+		WPA_PUT_BE16(key->key_data_length, kde_len);
+	} else if (encr && kde) {
+		buf = os_zalloc(key_data_len);
+		if (buf == NULL) {
+			os_free(hdr);
+			return;
+		}
+		pos = buf;
+		os_memcpy(pos, kde, kde_len);
+		pos += kde_len;
+
+		if (pad_len)
+			*pos++ = 0xdd;
+
+		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+				buf, key_data_len);
+		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf,
+				     (u8 *) (key + 1))) {
+				os_free(hdr);
+				os_free(buf);
+				return;
+			}
+			WPA_PUT_BE16(key->key_data_length, key_data_len);
+		} else {
+			u8 ek[32];
+			os_memcpy(key->key_iv,
+				  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
+			inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+			os_memcpy(ek, key->key_iv, 16);
+			os_memcpy(ek + 16, sm->PTK.kek, 16);
+			os_memcpy(key + 1, buf, key_data_len);
+			rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len);
+			WPA_PUT_BE16(key->key_data_length, key_data_len);
+		}
+		os_free(buf);
+	}
+
+	if (key_info & WPA_KEY_INFO_MIC) {
+		if (!sm->PTK_valid) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					"PTK not valid when sending EAPOL-Key "
+					"frame");
+			os_free(hdr);
+			return;
+		}
+		wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len,
+				  key->key_mic);
+	}
+
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx,
+			   1);
+	wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
+			    sm->pairwise_set);
+	os_free(hdr);
+}
+
+
+static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+			   struct wpa_state_machine *sm, int key_info,
+			   const u8 *key_rsc, const u8 *nonce,
+			   const u8 *kde, size_t kde_len,
+			   int keyidx, int encr)
+{
+	int timeout_ms;
+	int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+	int ctr;
+
+	if (sm == NULL)
+		return;
+
+	__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
+			 keyidx, encr, 0);
+
+	ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+	if (ctr == 1 && wpa_auth->conf.tx_status)
+		timeout_ms = eapol_key_timeout_first;
+	else
+		timeout_ms = eapol_key_timeout_subseq;
+	if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+		sm->pending_1_of_4_timeout = 1;
+	wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
+		   "counter %d)", timeout_ms, ctr);
+	eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
+			       wpa_send_eapol_timeout, wpa_auth, sm);
+}
+
+
+static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u16 key_info;
+	int ret = 0;
+	u8 mic[16];
+
+	if (data_len < sizeof(*hdr) + sizeof(*key))
+		return -1;
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	os_memcpy(mic, key->key_mic, 16);
+	os_memset(key->key_mic, 0, 16);
+	if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK,
+			      data, data_len, key->key_mic) ||
+	    os_memcmp(mic, key->key_mic, 16) != 0)
+		ret = -1;
+	os_memcpy(key->key_mic, mic, 16);
+	return ret;
+}
+
+
+void wpa_remove_ptk(struct wpa_state_machine *sm)
+{
+	sm->PTK_valid = FALSE;
+	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+	wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
+	sm->pairwise_set = FALSE;
+	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+}
+
+
+int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
+{
+	int remove_ptk = 1;
+
+	if (sm == NULL)
+		return -1;
+
+	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			 "event %d notification", event);
+
+	switch (event) {
+	case WPA_AUTH:
+	case WPA_ASSOC:
+		break;
+	case WPA_DEAUTH:
+	case WPA_DISASSOC:
+		sm->DeauthenticationRequest = TRUE;
+		break;
+	case WPA_REAUTH:
+	case WPA_REAUTH_EAPOL:
+		if (!sm->started) {
+			/*
+			 * When using WPS, we may end up here if the STA
+			 * manages to re-associate without the previous STA
+			 * entry getting removed. Consequently, we need to make
+			 * sure that the WPA state machines gets initialized
+			 * properly at this point.
+			 */
+			wpa_printf(MSG_DEBUG, "WPA state machine had not been "
+				   "started - initialize now");
+			sm->started = 1;
+			sm->Init = TRUE;
+			if (wpa_sm_step(sm) == 1)
+				return 1; /* should not really happen */
+			sm->Init = FALSE;
+			sm->AuthenticationRequest = TRUE;
+			break;
+		}
+		if (sm->GUpdateStationKeys) {
+			/*
+			 * Reauthentication cancels the pending group key
+			 * update for this STA.
+			 */
+			sm->group->GKeyDoneStations--;
+			sm->GUpdateStationKeys = FALSE;
+			sm->PtkGroupInit = TRUE;
+		}
+		sm->ReAuthenticationRequest = TRUE;
+		break;
+	case WPA_ASSOC_FT:
+#ifdef CONFIG_IEEE80211R
+		wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
+			   "after association");
+		wpa_ft_install_ptk(sm);
+
+		/* Using FT protocol, not WPA auth state machine */
+		sm->ft_completed = 1;
+		return 0;
+#else /* CONFIG_IEEE80211R */
+		break;
+#endif /* CONFIG_IEEE80211R */
+	}
+
+#ifdef CONFIG_IEEE80211R
+	sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_frame_prot && event == WPA_AUTH)
+		remove_ptk = 0;
+#endif /* CONFIG_IEEE80211W */
+
+	if (remove_ptk) {
+		sm->PTK_valid = FALSE;
+		os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+		if (event != WPA_REAUTH_EAPOL)
+			wpa_remove_ptk(sm);
+	}
+
+	return wpa_sm_step(sm);
+}
+
+
+static enum wpa_alg wpa_alg_enum(int alg)
+{
+	switch (alg) {
+	case WPA_CIPHER_CCMP:
+		return WPA_ALG_CCMP;
+	case WPA_CIPHER_TKIP:
+		return WPA_ALG_TKIP;
+	case WPA_CIPHER_WEP104:
+	case WPA_CIPHER_WEP40:
+		return WPA_ALG_WEP;
+	default:
+		return WPA_ALG_NONE;
+	}
+}
+
+
+SM_STATE(WPA_PTK, INITIALIZE)
+{
+	SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
+	if (sm->Init) {
+		/* Init flag is not cleared here, so avoid busy
+		 * loop by claiming nothing changed. */
+		sm->changed = FALSE;
+	}
+
+	sm->keycount = 0;
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = FALSE;
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = FALSE;
+	if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
+	       * Local AA > Remote AA)) */) {
+		sm->Pair = TRUE;
+	}
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0);
+	wpa_remove_ptk(sm);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
+	sm->TimeoutCtr = 0;
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+				   WPA_EAPOL_authorized, 0);
+	}
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECT)
+{
+	SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
+	sm->Disconnect = FALSE;
+	wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECTED)
+{
+	SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk);
+	sm->DeauthenticationRequest = FALSE;
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION)
+{
+	SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk);
+	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+	sm->PTK_valid = FALSE;
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto,
+			   1);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1);
+	sm->AuthenticationRequest = FALSE;
+}
+
+
+static void wpa_group_first_station(struct wpa_authenticator *wpa_auth,
+				    struct wpa_group *group)
+{
+	/*
+	 * System has run bit further than at the time hostapd was started
+	 * potentially very early during boot up. This provides better chances
+	 * of collecting more randomness on embedded systems. Re-initialize the
+	 * GMK and Counter here to improve their strength if there was not
+	 * enough entropy available immediately after system startup.
+	 */
+	wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first "
+		   "station");
+	if (random_pool_ready() != 1) {
+		wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+			   "to proceed - reject first 4-way handshake");
+		group->reject_4way_hs_for_entropy = TRUE;
+	}
+	wpa_group_init_gmk_and_counter(wpa_auth, group);
+	wpa_gtk_update(wpa_auth, group);
+	wpa_group_config_group_keys(wpa_auth, group);
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION2)
+{
+	SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+
+	if (!sm->group->first_sta_seen) {
+		wpa_group_first_station(sm->wpa_auth, sm->group);
+		sm->group->first_sta_seen = TRUE;
+	}
+
+	os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+		    WPA_NONCE_LEN);
+	inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+	sm->ReAuthenticationRequest = FALSE;
+	/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
+	 * logical place than INITIALIZE since AUTHENTICATION2 can be
+	 * re-entered on ReAuthenticationRequest without going through
+	 * INITIALIZE. */
+	sm->TimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK, INITPMK)
+{
+	u8 msk[2 * PMK_LEN];
+	size_t len = 2 * PMK_LEN;
+
+	SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
+#ifdef CONFIG_IEEE80211R
+	sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R */
+	if (sm->pmksa) {
+		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
+		os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
+	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+		wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
+			   "(len=%lu)", (unsigned long) len);
+		os_memcpy(sm->PMK, msk, PMK_LEN);
+#ifdef CONFIG_IEEE80211R
+		if (len >= 2 * PMK_LEN) {
+			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+			sm->xxkey_len = PMK_LEN;
+		}
+#endif /* CONFIG_IEEE80211R */
+	} else {
+		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK");
+	}
+
+	sm->req_replay_counter_used = 0;
+	/* IEEE 802.11i does not set keyRun to FALSE, but not doing this
+	 * will break reauthentication since EAPOL state machines may not be
+	 * get into AUTHENTICATING state that clears keyRun before WPA state
+	 * machine enters AUTHENTICATION2 state and goes immediately to INITPMK
+	 * state and takes PMK from the previously used AAA Key. This will
+	 * eventually fail in 4-Way Handshake because Supplicant uses PMK
+	 * derived from the new AAA Key. Setting keyRun = FALSE here seems to
+	 * be good workaround for this issue. */
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0);
+}
+
+
+SM_STATE(WPA_PTK, INITPSK)
+{
+	const u8 *psk;
+	SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
+	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL);
+	if (psk) {
+		os_memcpy(sm->PMK, psk, PMK_LEN);
+#ifdef CONFIG_IEEE80211R
+		os_memcpy(sm->xxkey, psk, PMK_LEN);
+		sm->xxkey_len = PMK_LEN;
+#endif /* CONFIG_IEEE80211R */
+	}
+	sm->req_replay_counter_used = 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKSTART)
+{
+	u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL;
+	size_t pmkid_len = 0;
+
+	SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
+	sm->PTKRequest = FALSE;
+	sm->TimeoutEvt = FALSE;
+
+	sm->TimeoutCtr++;
+	if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/4 msg of 4-Way Handshake");
+	/*
+	 * TODO: Could add PMKID even with WPA2-PSK, but only if there is only
+	 * one possible PSK for this STA.
+	 */
+	if (sm->wpa == WPA_VERSION_WPA2 &&
+	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
+		pmkid = buf;
+		pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+		pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
+		pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
+		RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
+		if (sm->pmksa)
+			os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+				  sm->pmksa->pmkid, PMKID_LEN);
+		else {
+			/*
+			 * Calculate PMKID since no PMKSA cache entry was
+			 * available with pre-calculated PMKID.
+			 */
+			rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
+				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
+				  wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+		}
+	}
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+		       sm->ANonce, pmkid, pmkid_len, 0, 0);
+}
+
+
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+			  struct wpa_ptk *ptk)
+{
+	size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64;
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
+#endif /* CONFIG_IEEE80211R */
+
+	wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+		       sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce,
+		       (u8 *) ptk, ptk_len,
+		       wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+
+	return 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+{
+	struct wpa_ptk PTK;
+	int ok = 0;
+	const u8 *pmk = NULL;
+
+	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
+	sm->EAPOLKeyReceived = FALSE;
+
+	/* WPA with IEEE 802.1X: use the derived PMK from EAP
+	 * WPA-PSK: iterate through possible PSKs and select the one matching
+	 * the packet */
+	for (;;) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
+			if (pmk == NULL)
+				break;
+		} else
+			pmk = sm->PMK;
+
+		wpa_derive_ptk(sm, pmk, &PTK);
+
+		if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+				       sm->last_rx_eapol_key_len) == 0) {
+			ok = 1;
+			break;
+		}
+
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+			break;
+	}
+
+	if (!ok) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"invalid MIC in msg 2/4 of 4-Way Handshake");
+		return;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		/*
+		 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+		 * with the value we derived.
+		 */
+		if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+			      WPA_PMK_NAME_LEN) != 0) {
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					"PMKR1Name mismatch in FT 4-way "
+					"handshake");
+			wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
+				    "Supplicant",
+				    sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
+			wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+				    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+			return;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	sm->pending_1_of_4_timeout = 0;
+	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		/* PSK may have changed from the previous choice, so update
+		 * state machine data based on whatever PSK was selected here.
+		 */
+		os_memcpy(sm->PMK, pmk, PMK_LEN);
+	}
+
+	sm->MICVerified = TRUE;
+
+	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	sm->PTK_valid = TRUE;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
+{
+	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk);
+	sm->TimeoutCtr = 0;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+static int ieee80211w_kde_len(struct wpa_state_machine *sm)
+{
+	if (sm->mgmt_frame_prot) {
+		return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde);
+	}
+
+	return 0;
+}
+
+
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_igtk_kde igtk;
+	struct wpa_group *gsm = sm->group;
+
+	if (!sm->mgmt_frame_prot)
+		return pos;
+
+	igtk.keyid[0] = gsm->GN_igtk;
+	igtk.keyid[1] = 0;
+	if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+	    wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
+		os_memset(igtk.pn, 0, sizeof(igtk.pn));
+	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
+			  (const u8 *) &igtk, sizeof(igtk), NULL, 0);
+
+	return pos;
+}
+
+#else /* CONFIG_IEEE80211W */
+
+static int ieee80211w_kde_len(struct wpa_state_machine *sm)
+{
+	return 0;
+}
+
+
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+	return pos;
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
+{
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+	size_t gtk_len, kde_len;
+	struct wpa_group *gsm = sm->group;
+	u8 *wpa_ie;
+	int wpa_ie_len, secure, keyidx, encr = 0;
+
+	SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
+	sm->TimeoutEvt = FALSE;
+
+	sm->TimeoutCtr++;
+	if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+	   GTK[GN], IGTK, [FTIE], [TIE * 2])
+	 */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+	/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+	wpa_ie = sm->wpa_auth->wpa_ie;
+	wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+	if (sm->wpa == WPA_VERSION_WPA &&
+	    (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+	    wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+		/* WPA-only STA, remove RSN IE */
+		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		wpa_ie_len = wpa_ie[1] + 2;
+	}
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 3/4 msg of 4-Way Handshake");
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		/* WPA2 send GTK in the 4-way handshake */
+		secure = 1;
+		gtk = gsm->GTK[gsm->GN - 1];
+		gtk_len = gsm->GTK_len;
+		keyidx = gsm->GN;
+		_rsc = rsc;
+		encr = 1;
+	} else {
+		/* WPA does not include GTK in msg 3/4 */
+		secure = 0;
+		gtk = NULL;
+		gtk_len = 0;
+		keyidx = 0;
+		_rsc = NULL;
+	}
+
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+	if (gtk)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+		kde_len += 300; /* FTIE + 2 * TIE */
+	}
+#endif /* CONFIG_IEEE80211R */
+	kde = os_malloc(kde_len);
+	if (kde == NULL)
+		return;
+
+	pos = kde;
+	os_memcpy(pos, wpa_ie, wpa_ie_len);
+	pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert "
+				   "PMKR1Name into RSN IE in EAPOL-Key data");
+			os_free(kde);
+			return;
+		}
+		pos += res;
+	}
+#endif /* CONFIG_IEEE80211R */
+	if (gtk) {
+		u8 hdr[2];
+		hdr[0] = keyidx & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gtk_len);
+	}
+	pos = ieee80211w_kde_add(sm, pos);
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		struct wpa_auth_config *conf;
+
+		conf = &sm->wpa_auth->conf;
+		res = wpa_write_ftie(conf, conf->r0_key_holder,
+				     conf->r0_key_holder_len,
+				     NULL, NULL, pos, kde + kde_len - pos,
+				     NULL, 0);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+				   "into EAPOL-Key Key Data");
+			os_free(kde);
+			return;
+		}
+		pos += res;
+
+		/* TIE[ReassociationDeadline] (TU) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+		WPA_PUT_LE32(pos, conf->reassociation_deadline);
+		pos += 4;
+
+		/* TIE[KeyLifetime] (seconds) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+		pos += 4;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+		       WPA_KEY_INFO_KEY_TYPE,
+		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+	os_free(kde);
+}
+
+
+SM_STATE(WPA_PTK, PTKINITDONE)
+{
+	SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
+	sm->EAPOLKeyReceived = FALSE;
+	if (sm->Pair) {
+		enum wpa_alg alg;
+		int klen;
+		if (sm->pairwise == WPA_CIPHER_TKIP) {
+			alg = WPA_ALG_TKIP;
+			klen = 32;
+		} else {
+			alg = WPA_ALG_CCMP;
+			klen = 16;
+		}
+		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+				     sm->PTK.tk1, klen)) {
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+			return;
+		}
+		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+		sm->pairwise_set = TRUE;
+
+		if (sm->wpa_auth->conf.wpa_ptk_rekey) {
+			eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+			eloop_register_timeout(sm->wpa_auth->conf.
+					       wpa_ptk_rekey, 0, wpa_rekey_ptk,
+					       sm->wpa_auth, sm);
+		}
+
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+					   WPA_EAPOL_authorized, 1);
+		}
+	}
+
+	if (0 /* IBSS == TRUE */) {
+		sm->keycount++;
+		if (sm->keycount == 2) {
+			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+					   WPA_EAPOL_portValid, 1);
+		}
+	} else {
+		wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid,
+				   1);
+	}
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1);
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = TRUE;
+	else
+		sm->has_GTK = TRUE;
+	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+			 "pairwise key handshake completed (%s)",
+			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+
+#ifdef CONFIG_IEEE80211R
+	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+SM_STEP(WPA_PTK)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+
+	if (sm->Init)
+		SM_ENTER(WPA_PTK, INITIALIZE);
+	else if (sm->Disconnect
+		 /* || FIX: dot11RSNAConfigSALifetime timeout */) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"WPA_PTK: sm->Disconnect");
+		SM_ENTER(WPA_PTK, DISCONNECT);
+	}
+	else if (sm->DeauthenticationRequest)
+		SM_ENTER(WPA_PTK, DISCONNECTED);
+	else if (sm->AuthenticationRequest)
+		SM_ENTER(WPA_PTK, AUTHENTICATION);
+	else if (sm->ReAuthenticationRequest)
+		SM_ENTER(WPA_PTK, AUTHENTICATION2);
+	else if (sm->PTKRequest)
+		SM_ENTER(WPA_PTK, PTKSTART);
+	else switch (sm->wpa_ptk_state) {
+	case WPA_PTK_INITIALIZE:
+		break;
+	case WPA_PTK_DISCONNECT:
+		SM_ENTER(WPA_PTK, DISCONNECTED);
+		break;
+	case WPA_PTK_DISCONNECTED:
+		SM_ENTER(WPA_PTK, INITIALIZE);
+		break;
+	case WPA_PTK_AUTHENTICATION:
+		SM_ENTER(WPA_PTK, AUTHENTICATION2);
+		break;
+	case WPA_PTK_AUTHENTICATION2:
+		if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+		    wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
+				       WPA_EAPOL_keyRun) > 0)
+			SM_ENTER(WPA_PTK, INITPMK);
+		else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
+			 /* FIX: && 802.1X::keyRun */)
+			SM_ENTER(WPA_PTK, INITPSK);
+		break;
+	case WPA_PTK_INITPMK:
+		if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
+				       WPA_EAPOL_keyAvailable) > 0)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		else {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+					"INITPMK - keyAvailable = false");
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		}
+		break;
+	case WPA_PTK_INITPSK:
+		if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL))
+			SM_ENTER(WPA_PTK, PTKSTART);
+		else {
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+					"no PSK configured for the STA");
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		}
+		break;
+	case WPA_PTK_PTKSTART:
+		if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+		    sm->EAPOLKeyPairwise)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->TimeoutCtr >
+			 (int) dot11RSNAConfigPairwiseUpdateCount) {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "PTKSTART: Retry limit %d reached",
+					 dot11RSNAConfigPairwiseUpdateCount);
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		} else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		break;
+	case WPA_PTK_PTKCALCNEGOTIATING:
+		if (sm->MICVerified)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2);
+		else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+			 sm->EAPOLKeyPairwise)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		break;
+	case WPA_PTK_PTKCALCNEGOTIATING2:
+		SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+		break;
+	case WPA_PTK_PTKINITNEGOTIATING:
+		if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+		    sm->EAPOLKeyPairwise && sm->MICVerified)
+			SM_ENTER(WPA_PTK, PTKINITDONE);
+		else if (sm->TimeoutCtr >
+			 (int) dot11RSNAConfigPairwiseUpdateCount) {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "PTKINITNEGOTIATING: Retry limit %d "
+					 "reached",
+					 dot11RSNAConfigPairwiseUpdateCount);
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		} else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+		break;
+	case WPA_PTK_PTKINITDONE:
+		break;
+	}
+}
+
+
+SM_STATE(WPA_PTK_GROUP, IDLE)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group);
+	if (sm->Init) {
+		/* Init flag is not cleared here, so avoid busy
+		 * loop by claiming nothing changed. */
+		sm->changed = FALSE;
+	}
+	sm->GTimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
+{
+	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_group *gsm = sm->group;
+	u8 *kde, *pos, hdr[2];
+	size_t kde_len;
+
+	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+
+	sm->GTimeoutCtr++;
+	if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = FALSE;
+	sm->TimeoutEvt = FALSE;
+	/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
+		wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/2 msg of Group Key Handshake");
+
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+			ieee80211w_kde_len(sm);
+		kde = os_malloc(kde_len);
+		if (kde == NULL)
+			return;
+
+		pos = kde;
+		hdr[0] = gsm->GN & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+		pos = ieee80211w_kde_add(sm, pos);
+	} else {
+		kde = gsm->GTK[gsm->GN - 1];
+		pos = kde + gsm->GTK_len;
+	}
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+		       WPA_KEY_INFO_ACK |
+		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+		       rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1);
+	if (sm->wpa == WPA_VERSION_WPA2)
+		os_free(kde);
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
+	sm->EAPOLKeyReceived = FALSE;
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = FALSE;
+	sm->GTimeoutCtr = 0;
+	/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+			 "group key handshake completed (%s)",
+			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+	sm->has_GTK = TRUE;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, KEYERROR)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = FALSE;
+	sm->Disconnect = TRUE;
+}
+
+
+SM_STEP(WPA_PTK_GROUP)
+{
+	if (sm->Init || sm->PtkGroupInit) {
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		sm->PtkGroupInit = FALSE;
+	} else switch (sm->wpa_ptk_group_state) {
+	case WPA_PTK_GROUP_IDLE:
+		if (sm->GUpdateStationKeys ||
+		    (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
+			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+		break;
+	case WPA_PTK_GROUP_REKEYNEGOTIATING:
+		if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+		    !sm->EAPOLKeyPairwise && sm->MICVerified)
+			SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
+		else if (sm->GTimeoutCtr >
+			 (int) dot11RSNAConfigGroupUpdateCount)
+			SM_ENTER(WPA_PTK_GROUP, KEYERROR);
+		else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+		break;
+	case WPA_PTK_GROUP_KEYERROR:
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		break;
+	case WPA_PTK_GROUP_REKEYESTABLISHED:
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		break;
+	}
+}
+
+
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	int ret = 0;
+
+	os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+	inc_byte_array(group->Counter, WPA_NONCE_LEN);
+	if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+			   wpa_auth->addr, group->GNonce,
+			   group->GTK[group->GN - 1], group->GTK_len) < 0)
+		ret = -1;
+	wpa_hexdump_key(MSG_DEBUG, "GTK",
+			group->GTK[group->GN - 1], group->GTK_len);
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+		inc_byte_array(group->Counter, WPA_NONCE_LEN);
+		if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+				   wpa_auth->addr, group->GNonce,
+				   group->IGTK[group->GN_igtk - 4],
+				   WPA_IGTK_LEN) < 0)
+			ret = -1;
+		wpa_hexdump_key(MSG_DEBUG, "IGTK",
+				group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	return ret;
+}
+
+
+static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
+			       struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
+		   "GTK_INIT (VLAN-ID %d)", group->vlan_id);
+	group->changed = FALSE; /* GInit is not cleared here; avoid loop */
+	group->wpa_group_state = WPA_GROUP_GTK_INIT;
+
+	/* GTK[0..N] = 0 */
+	os_memset(group->GTK, 0, sizeof(group->GTK));
+	group->GN = 1;
+	group->GM = 2;
+#ifdef CONFIG_IEEE80211W
+	group->GN_igtk = 4;
+	group->GM_igtk = 5;
+#endif /* CONFIG_IEEE80211W */
+	/* GTK[GN] = CalcGTK() */
+	wpa_gtk_update(wpa_auth, group);
+}
+
+
+static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
+{
+	if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"Not in PTKINITDONE; skip Group Key update");
+		sm->GUpdateStationKeys = FALSE;
+		return 0;
+	}
+	if (sm->GUpdateStationKeys) {
+		/*
+		 * This should not really happen, so add a debug log entry.
+		 * Since we clear the GKeyDoneStations before the loop, the
+		 * station needs to be counted here anyway.
+		 */
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"GUpdateStationKeys was already set when "
+				"marking station for GTK rekeying");
+	}
+
+	sm->group->GKeyDoneStations++;
+	sm->GUpdateStationKeys = TRUE;
+
+	wpa_sm_step(sm);
+	return 0;
+}
+
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group)
+{
+	int tmp;
+
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
+		   "SETKEYS (VLAN-ID %d)", group->vlan_id);
+	group->changed = TRUE;
+	group->wpa_group_state = WPA_GROUP_SETKEYS;
+	group->GTKReKey = FALSE;
+	tmp = group->GM;
+	group->GM = group->GN;
+	group->GN = tmp;
+#ifdef CONFIG_IEEE80211W
+	tmp = group->GM_igtk;
+	group->GM_igtk = group->GN_igtk;
+	group->GN_igtk = tmp;
+#endif /* CONFIG_IEEE80211W */
+	/* "GKeyDoneStations = GNoStations" is done in more robust way by
+	 * counting the STAs that are marked with GUpdateStationKeys instead of
+	 * including all STAs that could be in not-yet-completed state. */
+	wpa_gtk_update(wpa_auth, group);
+
+	if (group->GKeyDoneStations) {
+		wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected "
+			   "GKeyDoneStations=%d when starting new GTK rekey",
+			   group->GKeyDoneStations);
+		group->GKeyDoneStations = 0;
+	}
+	wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL);
+	wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
+		   group->GKeyDoneStations);
+}
+
+
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+				       struct wpa_group *group)
+{
+	int ret = 0;
+
+	if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+			     wpa_alg_enum(wpa_auth->conf.wpa_group),
+			     broadcast_ether_addr, group->GN,
+			     group->GTK[group->GN - 1], group->GTK_len) < 0)
+		ret = -1;
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+	    wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
+			     broadcast_ether_addr, group->GN_igtk,
+			     group->IGTK[group->GN_igtk - 4],
+			     WPA_IGTK_LEN) < 0)
+		ret = -1;
+#endif /* CONFIG_IEEE80211W */
+
+	return ret;
+}
+
+
+static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
+				 struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
+		   "SETKEYSDONE (VLAN-ID %d)", group->vlan_id);
+	group->changed = TRUE;
+	group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
+
+	if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group)
+{
+	if (group->GInit) {
+		wpa_group_gtk_init(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
+		   group->GTKAuthenticator) {
+		wpa_group_setkeysdone(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE &&
+		   group->GTKReKey) {
+		wpa_group_setkeys(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_SETKEYS) {
+		if (group->GKeyDoneStations == 0)
+			wpa_group_setkeysdone(wpa_auth, group);
+		else if (group->GTKReKey)
+			wpa_group_setkeys(wpa_auth, group);
+	}
+}
+
+
+static int wpa_sm_step(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return 0;
+
+	if (sm->in_step_loop) {
+		/* This should not happen, but if it does, make sure we do not
+		 * end up freeing the state machine too early by exiting the
+		 * recursive call. */
+		wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively");
+		return 0;
+	}
+
+	sm->in_step_loop = 1;
+	do {
+		if (sm->pending_deinit)
+			break;
+
+		sm->changed = FALSE;
+		sm->wpa_auth->group->changed = FALSE;
+
+		SM_STEP_RUN(WPA_PTK);
+		if (sm->pending_deinit)
+			break;
+		SM_STEP_RUN(WPA_PTK_GROUP);
+		if (sm->pending_deinit)
+			break;
+		wpa_group_sm_step(sm->wpa_auth, sm->group);
+	} while (sm->changed || sm->wpa_auth->group->changed);
+	sm->in_step_loop = 0;
+
+	if (sm->pending_deinit) {
+		wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state "
+			   "machine deinit for " MACSTR, MAC2STR(sm->addr));
+		wpa_free_sta_sm(sm);
+		return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+	wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sm_notify(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return;
+	eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
+
+
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
+{
+	int tmp, i;
+	struct wpa_group *group;
+
+	if (wpa_auth == NULL)
+		return;
+
+	group = wpa_auth->group;
+
+	for (i = 0; i < 2; i++) {
+		tmp = group->GM;
+		group->GM = group->GN;
+		group->GN = tmp;
+#ifdef CONFIG_IEEE80211W
+		tmp = group->GM_igtk;
+		group->GM_igtk = group->GN_igtk;
+		group->GN_igtk = tmp;
+#endif /* CONFIG_IEEE80211W */
+		wpa_gtk_update(wpa_auth, group);
+	}
+}
+
+
+static const char * wpa_bool_txt(int bool)
+{
+	return bool ? "TRUE" : "FALSE";
+}
+
+
+static int wpa_cipher_bits(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+		return 128;
+	case WPA_CIPHER_TKIP:
+		return 256;
+	case WPA_CIPHER_WEP104:
+		return 104;
+	case WPA_CIPHER_WEP40:
+		return 40;
+	default:
+		return 0;
+	}
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	char pmkid_txt[PMKID_LEN * 2 + 1];
+#ifdef CONFIG_RSN_PREAUTH
+	const int preauth = 1;
+#else /* CONFIG_RSN_PREAUTH */
+	const int preauth = 0;
+#endif /* CONFIG_RSN_PREAUTH */
+
+	if (wpa_auth == NULL)
+		return len;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot11RSNAOptionImplemented=TRUE\n"
+			  "dot11RSNAPreauthenticationImplemented=%s\n"
+			  "dot11RSNAEnabled=%s\n"
+			  "dot11RSNAPreauthenticationEnabled=%s\n",
+			  wpa_bool_txt(preauth),
+			  wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
+			  wpa_bool_txt(wpa_auth->conf.rsn_preauth));
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+			 wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN);
+
+	ret = os_snprintf(
+		buf + len, buflen - len,
+		"dot11RSNAConfigVersion=%u\n"
+		"dot11RSNAConfigPairwiseKeysSupported=9999\n"
+		/* FIX: dot11RSNAConfigGroupCipher */
+		/* FIX: dot11RSNAConfigGroupRekeyMethod */
+		/* FIX: dot11RSNAConfigGroupRekeyTime */
+		/* FIX: dot11RSNAConfigGroupRekeyPackets */
+		"dot11RSNAConfigGroupRekeyStrict=%u\n"
+		"dot11RSNAConfigGroupUpdateCount=%u\n"
+		"dot11RSNAConfigPairwiseUpdateCount=%u\n"
+		"dot11RSNAConfigGroupCipherSize=%u\n"
+		"dot11RSNAConfigPMKLifetime=%u\n"
+		"dot11RSNAConfigPMKReauthThreshold=%u\n"
+		"dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n"
+		"dot11RSNAConfigSATimeout=%u\n"
+		"dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAPMKIDUsed=%s\n"
+		"dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNATKIPCounterMeasuresInvoked=%u\n"
+		"dot11RSNA4WayHandshakeFailures=%u\n"
+		"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+		RSN_VERSION,
+		!!wpa_auth->conf.wpa_strict_rekey,
+		dot11RSNAConfigGroupUpdateCount,
+		dot11RSNAConfigPairwiseUpdateCount,
+		wpa_cipher_bits(wpa_auth->conf.wpa_group),
+		dot11RSNAConfigPMKLifetime,
+		dot11RSNAConfigPMKReauthThreshold,
+		dot11RSNAConfigSATimeout,
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected),
+		pmkid_txt,
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
+		wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
+		wpa_auth->dot11RSNA4WayHandshakeFailures);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	/* TODO: dot11RSNAConfigPairwiseCiphersTable */
+	/* TODO: dot11RSNAConfigAuthenticationSuitesTable */
+
+	/* Private MIB */
+	ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
+			  wpa_auth->group->wpa_group_state);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	u32 pairwise = 0;
+
+	if (sm == NULL)
+		return 0;
+
+	/* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
+
+	/* dot11RSNAStatsEntry */
+
+	if (sm->wpa == WPA_VERSION_WPA) {
+		if (sm->pairwise == WPA_CIPHER_CCMP)
+			pairwise = WPA_CIPHER_SUITE_CCMP;
+		else if (sm->pairwise == WPA_CIPHER_TKIP)
+			pairwise = WPA_CIPHER_SUITE_TKIP;
+		else if (sm->pairwise == WPA_CIPHER_WEP104)
+			pairwise = WPA_CIPHER_SUITE_WEP104;
+		else if (sm->pairwise == WPA_CIPHER_WEP40)
+			pairwise = WPA_CIPHER_SUITE_WEP40;
+		else if (sm->pairwise == WPA_CIPHER_NONE)
+			pairwise = WPA_CIPHER_SUITE_NONE;
+	} else if (sm->wpa == WPA_VERSION_WPA2) {
+		if (sm->pairwise == WPA_CIPHER_CCMP)
+			pairwise = RSN_CIPHER_SUITE_CCMP;
+		else if (sm->pairwise == WPA_CIPHER_TKIP)
+			pairwise = RSN_CIPHER_SUITE_TKIP;
+		else if (sm->pairwise == WPA_CIPHER_WEP104)
+			pairwise = RSN_CIPHER_SUITE_WEP104;
+		else if (sm->pairwise == WPA_CIPHER_WEP40)
+			pairwise = RSN_CIPHER_SUITE_WEP40;
+		else if (sm->pairwise == WPA_CIPHER_NONE)
+			pairwise = RSN_CIPHER_SUITE_NONE;
+	} else
+		return 0;
+
+	ret = os_snprintf(
+		buf + len, buflen - len,
+		/* TODO: dot11RSNAStatsIndex */
+		"dot11RSNAStatsSTAAddress=" MACSTR "\n"
+		"dot11RSNAStatsVersion=1\n"
+		"dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
+		/* TODO: dot11RSNAStatsTKIPICVErrors */
+		"dot11RSNAStatsTKIPLocalMICFailures=%u\n"
+		"dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
+		/* TODO: dot11RSNAStatsCCMPReplays */
+		/* TODO: dot11RSNAStatsCCMPDecryptErrors */
+		/* TODO: dot11RSNAStatsTKIPReplays */,
+		MAC2STR(sm->addr),
+		RSN_SUITE_ARG(pairwise),
+		sm->dot11RSNAStatsTKIPLocalMICFailures,
+		sm->dot11RSNAStatsTKIPRemoteMICFailures);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	/* Private MIB */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "hostapdWPAPTKState=%d\n"
+			  "hostapdWPAPTKGroupState=%d\n",
+			  sm->wpa_ptk_state,
+			  sm->wpa_ptk_group_state);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+{
+	if (wpa_auth)
+		wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++;
+}
+
+
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm)
+{
+	return sm && sm->pairwise_set;
+}
+
+
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
+{
+	return sm->pairwise;
+}
+
+
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return -1;
+	return sm->wpa_key_mgmt;
+}
+
+
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return sm->wpa;
+}
+
+
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+			     struct rsn_pmksa_cache_entry *entry)
+{
+	if (sm == NULL || sm->pmksa != entry)
+		return -1;
+	sm->pmksa = NULL;
+	return 0;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm)
+{
+	return sm ? sm->pmksa : NULL;
+}
+
+
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm)
+{
+	if (sm)
+		sm->dot11RSNAStatsTKIPLocalMICFailures++;
+}
+
+
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
+{
+	if (wpa_auth == NULL)
+		return NULL;
+	*len = wpa_auth->wpa_ie_len;
+	return wpa_auth->wpa_ie;
+}
+
+
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       int session_timeout, struct eapol_state_machine *eapol)
+{
+	if (sm == NULL || sm->wpa != WPA_VERSION_WPA2)
+		return -1;
+
+	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+				 sm->wpa_auth->addr, sm->addr, session_timeout,
+				 eapol, sm->wpa_key_mgmt))
+		return 0;
+
+	return -1;
+}
+
+
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+			       const u8 *pmk, size_t len, const u8 *sta_addr,
+			       int session_timeout,
+			       struct eapol_state_machine *eapol)
+{
+	if (wpa_auth == NULL)
+		return -1;
+
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
+				 sta_addr, session_timeout, eapol,
+				 WPA_KEY_MGMT_IEEE8021X))
+		return 0;
+
+	return -1;
+}
+
+
+static struct wpa_group *
+wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (wpa_auth == NULL || wpa_auth->group == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
+		   vlan_id);
+	group = wpa_group_init(wpa_auth, vlan_id);
+	if (group == NULL)
+		return NULL;
+
+	group->next = wpa_auth->group->next;
+	wpa_auth->group->next = group;
+
+	return group;
+}
+
+
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (sm == NULL || sm->wpa_auth == NULL)
+		return 0;
+
+	group = sm->wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL) {
+		group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
+		if (group == NULL)
+			return -1;
+	}
+
+	if (sm->group == group)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
+		   "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
+
+	sm->group = group;
+	return 0;
+}
+
+
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+				  struct wpa_state_machine *sm, int ack)
+{
+	if (wpa_auth == NULL || sm == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
+		   " ack=%d", MAC2STR(sm->addr), ack);
+	if (sm->pending_1_of_4_timeout && ack) {
+		/*
+		 * Some deployed supplicant implementations update their SNonce
+		 * for each EAPOL-Key 2/4 message even within the same 4-way
+		 * handshake and then fail to use the first SNonce when
+		 * deriving the PTK. This results in unsuccessful 4-way
+		 * handshake whenever the relatively short initial timeout is
+		 * reached and EAPOL-Key 1/4 is retransmitted. Try to work
+		 * around this by increasing the timeout now that we know that
+		 * the station has received the frame.
+		 */
+		int timeout_ms = eapol_key_timeout_subseq;
+		wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 "
+			   "timeout by %u ms because of acknowledged frame",
+			   timeout_ms);
+		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+		eloop_register_timeout(timeout_ms / 1000,
+				       (timeout_ms % 1000) * 1000,
+				       wpa_send_eapol_timeout, wpa_auth, sm);
+	}
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
new file mode 100644
index 0000000..b3e1ff0
--- /dev/null
+++ b/src/ap/wpa_auth.h
@@ -0,0 +1,285 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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 WPA_AUTH_H
+#define WPA_AUTH_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition
+ */
+struct ft_rrb_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */
+	le16 action_length; /* little endian length of action_frame */
+	u8 ap_address[ETH_ALEN];
+	/*
+	 * Followed by action_length bytes of FT Action frame (from Category
+	 * field to the end of Action Frame body.
+	 */
+} STRUCT_PACKED;
+
+#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1
+
+#define FT_PACKET_REQUEST 0
+#define FT_PACKET_RESPONSE 1
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
+#define FT_PACKET_R0KH_R1KH_PULL 200
+#define FT_PACKET_R0KH_R1KH_RESP 201
+#define FT_PACKET_R0KH_R1KH_PUSH 202
+
+#define FT_R0KH_R1KH_PULL_DATA_LEN 44
+#define FT_R0KH_R1KH_RESP_DATA_LEN 76
+#define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+
+struct ft_r0kh_r1kh_pull_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
+	le16 data_length; /* little endian length of data (44) */
+	u8 ap_address[ETH_ALEN];
+
+	u8 nonce[16];
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 r1kh_id[FT_R1KH_ID_LEN];
+	u8 s1kh_id[ETH_ALEN];
+	u8 pad[4]; /* 8-octet boundary for AES key wrap */
+	u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+struct ft_r0kh_r1kh_resp_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
+	le16 data_length; /* little endian length of data (76) */
+	u8 ap_address[ETH_ALEN];
+
+	u8 nonce[16]; /* copied from pull */
+	u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
+	u8 s1kh_id[ETH_ALEN]; /* copied from pull */
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	le16 pairwise;
+	u8 pad[2]; /* 8-octet boundary for AES key wrap */
+	u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+struct ft_r0kh_r1kh_push_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
+	le16 data_length; /* little endian length of data (88) */
+	u8 ap_address[ETH_ALEN];
+
+	/* Encrypted with AES key-wrap */
+	u8 timestamp[4]; /* current time in seconds since unix epoch, little
+			  * endian */
+	u8 r1kh_id[FT_R1KH_ID_LEN];
+	u8 s1kh_id[ETH_ALEN];
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	le16 pairwise;
+	u8 pad[6]; /* 8-octet boundary for AES key wrap */
+	u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* per STA state machine data */
+
+struct wpa_authenticator;
+struct wpa_state_machine;
+struct rsn_pmksa_cache_entry;
+struct eapol_state_machine;
+
+
+struct ft_remote_r0kh {
+	struct ft_remote_r0kh *next;
+	u8 addr[ETH_ALEN];
+	u8 id[FT_R0KH_ID_MAX_LEN];
+	size_t id_len;
+	u8 key[16];
+};
+
+
+struct ft_remote_r1kh {
+	struct ft_remote_r1kh *next;
+	u8 addr[ETH_ALEN];
+	u8 id[FT_R1KH_ID_LEN];
+	u8 key[16];
+};
+
+
+struct wpa_auth_config {
+	int wpa;
+	int wpa_key_mgmt;
+	int wpa_pairwise;
+	int wpa_group;
+	int wpa_group_rekey;
+	int wpa_strict_rekey;
+	int wpa_gmk_rekey;
+	int wpa_ptk_rekey;
+	int rsn_pairwise;
+	int rsn_preauth;
+	int eapol_version;
+	int peerkey;
+	int wmm_enabled;
+	int wmm_uapsd;
+	int okc;
+	int tx_status;
+#ifdef CONFIG_IEEE80211W
+	enum mfp_options ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+#define SSID_LEN 32
+	u8 ssid[SSID_LEN];
+	size_t ssid_len;
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
+	size_t r0_key_holder_len;
+	u8 r1_key_holder[FT_R1KH_ID_LEN];
+	u32 r0_key_lifetime;
+	u32 reassociation_deadline;
+	struct ft_remote_r0kh *r0kh_list;
+	struct ft_remote_r1kh *r1kh_list;
+	int pmk_r1_push;
+	int ft_over_ds;
+#endif /* CONFIG_IEEE80211R */
+};
+
+typedef enum {
+	LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING
+} logger_level;
+
+typedef enum {
+	WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized,
+	WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable,
+	WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
+} wpa_eapol_variable;
+
+struct wpa_auth_callbacks {
+	void *ctx;
+	void (*logger)(void *ctx, const u8 *addr, logger_level level,
+		       const char *txt);
+	void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
+	void (*mic_failure_report)(void *ctx, const u8 *addr);
+	void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
+			  int value);
+	int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
+	const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk);
+	int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
+	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
+		       const u8 *addr, int idx, u8 *key, size_t key_len);
+	int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
+	int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
+			  size_t data_len, int encrypt);
+	int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+						 void *ctx), void *cb_ctx);
+	int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
+						  void *ctx), void *cb_ctx);
+	int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
+			  size_t data_len);
+#ifdef CONFIG_IEEE80211R
+	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+	int (*send_ft_action)(void *ctx, const u8 *dst,
+			      const u8 *data, size_t data_len);
+#endif /* CONFIG_IEEE80211R */
+};
+
+struct wpa_authenticator * wpa_init(const u8 *addr,
+				    struct wpa_auth_config *conf,
+				    struct wpa_auth_callbacks *cb);
+void wpa_deinit(struct wpa_authenticator *wpa_auth);
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+		 struct wpa_auth_config *conf);
+
+enum {
+	WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
+	WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
+	WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
+	WPA_INVALID_MDIE, WPA_INVALID_PROTO
+};
+	
+int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+			struct wpa_state_machine *sm,
+			const u8 *wpa_ie, size_t wpa_ie_len,
+			const u8 *mdie, size_t mdie_len);
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr);
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm);
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm);
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+		 struct wpa_state_machine *sm,
+		 u8 *data, size_t data_len);
+typedef enum {
+	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+	WPA_REAUTH_EAPOL, WPA_ASSOC_FT
+} wpa_event;
+void wpa_remove_ptk(struct wpa_state_machine *sm);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event);
+void wpa_auth_sm_notify(struct wpa_state_machine *sm);
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+			     struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm);
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
+			       size_t *len);
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       int session_timeout, struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+			       const u8 *pmk, size_t len, const u8 *sta_addr,
+			       int session_timeout,
+			       struct eapol_state_machine *eapol);
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+				  struct wpa_state_machine *sm, int ack);
+
+#ifdef CONFIG_IEEE80211R
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+				 size_t max_len, int auth_alg,
+				 const u8 *req_ies, size_t req_ies_len);
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+			 u16 auth_transaction, const u8 *ies, size_t ies_len,
+			 void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+				    u16 auth_transaction, u16 resp,
+				    const u8 *ies, size_t ies_len),
+			 void *ctx);
+u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+			    size_t ies_len);
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		  const u8 *data, size_t data_len);
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+#endif /* CONFIG_IEEE80211R */
+
+#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
new file mode 100644
index 0000000..65f5f4c
--- /dev/null
+++ b/src/ap/wpa_auth_ft.c
@@ -0,0 +1,1779 @@
+/*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wmm.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+
+#ifdef CONFIG_IEEE80211R
+
+struct wpa_ft_ies {
+	const u8 *mdie;
+	size_t mdie_len;
+	const u8 *ftie;
+	size_t ftie_len;
+	const u8 *r1kh_id;
+	const u8 *gtk;
+	size_t gtk_len;
+	const u8 *r0kh_id;
+	size_t r0kh_id_len;
+	const u8 *rsn;
+	size_t rsn_len;
+	const u8 *rsn_pmkid;
+	const u8 *ric;
+	size_t ric_len;
+};
+
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+			    struct wpa_ft_ies *parse);
+
+
+static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
+			   const u8 *data, size_t data_len)
+{
+	if (wpa_auth->cb.send_ether == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
+	return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
+				       data, data_len);
+}
+
+
+static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
+			      const u8 *dst, const u8 *data, size_t data_len)
+{
+	if (wpa_auth->cb.send_ft_action == NULL)
+		return -1;
+	return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
+					   data, data_len);
+}
+
+
+static struct wpa_state_machine *
+wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+	if (wpa_auth->cb.add_sta == NULL)
+		return NULL;
+	return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
+}
+
+
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+	u8 capab;
+	if (len < 2 + sizeof(struct rsn_mdie))
+		return -1;
+
+	*pos++ = WLAN_EID_MOBILITY_DOMAIN;
+	*pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
+	os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+	pos += MOBILITY_DOMAIN_ID_LEN;
+	capab = 0;
+	if (conf->ft_over_ds)
+		capab |= RSN_FT_CAPAB_FT_OVER_DS;
+	*pos++ = capab;
+
+	return pos - buf;
+}
+
+
+int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
+		   size_t r0kh_id_len,
+		   const u8 *anonce, const u8 *snonce,
+		   u8 *buf, size_t len, const u8 *subelem,
+		   size_t subelem_len)
+{
+	u8 *pos = buf, *ielen;
+	struct rsn_ftie *hdr;
+
+	if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+	    subelem_len)
+		return -1;
+
+	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+	ielen = pos++;
+
+	hdr = (struct rsn_ftie *) pos;
+	os_memset(hdr, 0, sizeof(*hdr));
+	pos += sizeof(*hdr);
+	WPA_PUT_LE16(hdr->mic_control, 0);
+	if (anonce)
+		os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+	if (snonce)
+		os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+
+	/* Optional Parameters */
+	*pos++ = FTIE_SUBELEM_R1KH_ID;
+	*pos++ = FT_R1KH_ID_LEN;
+	os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN);
+	pos += FT_R1KH_ID_LEN;
+
+	if (r0kh_id) {
+		*pos++ = FTIE_SUBELEM_R0KH_ID;
+		*pos++ = r0kh_id_len;
+		os_memcpy(pos, r0kh_id, r0kh_id_len);
+		pos += r0kh_id_len;
+	}
+
+	if (subelem) {
+		os_memcpy(pos, subelem, subelem_len);
+		pos += subelem_len;
+	}
+
+	*ielen = pos - buf - 2;
+
+	return pos - buf;
+}
+
+
+struct wpa_ft_pmk_r0_sa {
+	struct wpa_ft_pmk_r0_sa *next;
+	u8 pmk_r0[PMK_LEN];
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 spa[ETH_ALEN];
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+	int pmk_r1_pushed;
+};
+
+struct wpa_ft_pmk_r1_sa {
+	struct wpa_ft_pmk_r1_sa *next;
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 spa[ETH_ALEN];
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+};
+
+struct wpa_ft_pmk_cache {
+	struct wpa_ft_pmk_r0_sa *pmk_r0;
+	struct wpa_ft_pmk_r1_sa *pmk_r1;
+};
+
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
+{
+	struct wpa_ft_pmk_cache *cache;
+
+	cache = os_zalloc(sizeof(*cache));
+
+	return cache;
+}
+
+
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
+{
+	struct wpa_ft_pmk_r0_sa *r0, *r0prev;
+	struct wpa_ft_pmk_r1_sa *r1, *r1prev;
+
+	r0 = cache->pmk_r0;
+	while (r0) {
+		r0prev = r0;
+		r0 = r0->next;
+		os_memset(r0prev->pmk_r0, 0, PMK_LEN);
+		os_free(r0prev);
+	}
+
+	r1 = cache->pmk_r1;
+	while (r1) {
+		r1prev = r1;
+		r1 = r1->next;
+		os_memset(r1prev->pmk_r1, 0, PMK_LEN);
+		os_free(r1prev);
+	}
+
+	os_free(cache);
+}
+
+
+static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r0,
+			       const u8 *pmk_r0_name, int pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0;
+
+	/* TODO: add expiration and limit on number of entries in cache */
+
+	r0 = os_zalloc(sizeof(*r0));
+	if (r0 == NULL)
+		return -1;
+
+	os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
+	os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+	os_memcpy(r0->spa, spa, ETH_ALEN);
+	r0->pairwise = pairwise;
+
+	r0->next = cache->pmk_r0;
+	cache->pmk_r0 = r0;
+
+	return 0;
+}
+
+
+static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r0_name,
+			       u8 *pmk_r0, int *pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0;
+
+	r0 = cache->pmk_r0;
+	while (r0) {
+		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
+		    os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN)
+		    == 0) {
+			os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
+			if (pairwise)
+				*pairwise = r0->pairwise;
+			return 0;
+		}
+
+		r0 = r0->next;
+	}
+
+	return -1;
+}
+
+
+static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r1,
+			       const u8 *pmk_r1_name, int pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r1_sa *r1;
+
+	/* TODO: add expiration and limit on number of entries in cache */
+
+	r1 = os_zalloc(sizeof(*r1));
+	if (r1 == NULL)
+		return -1;
+
+	os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
+	os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+	os_memcpy(r1->spa, spa, ETH_ALEN);
+	r1->pairwise = pairwise;
+
+	r1->next = cache->pmk_r1;
+	cache->pmk_r1 = r1;
+
+	return 0;
+}
+
+
+static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r1_name,
+			       u8 *pmk_r1, int *pairwise)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r1_sa *r1;
+
+	r1 = cache->pmk_r1;
+	while (r1) {
+		if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
+		    os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN)
+		    == 0) {
+			os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
+			if (pairwise)
+				*pairwise = r1->pairwise;
+			return 0;
+		}
+
+		r1 = r1->next;
+	}
+
+	return -1;
+}
+
+
+static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
+			      const u8 *s1kh_id, const u8 *r0kh_id,
+			      size_t r0kh_id_len, const u8 *pmk_r0_name)
+{
+	struct ft_remote_r0kh *r0kh;
+	struct ft_r0kh_r1kh_pull_frame frame, f;
+
+	r0kh = wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		if (r0kh->id_len == r0kh_id_len &&
+		    os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0)
+			break;
+		r0kh = r0kh->next;
+	}
+	if (r0kh == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
+		   "address " MACSTR, MAC2STR(r0kh->addr));
+
+	os_memset(&frame, 0, sizeof(frame));
+	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
+	frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
+	os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+
+	/* aes_wrap() does not support inplace encryption, so use a temporary
+	 * buffer for the data. */
+	if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+			   "nonce");
+		return -1;
+	}
+	os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+	os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+
+	if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+		     f.nonce, frame.nonce) < 0)
+		return -1;
+
+	wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+
+	return 0;
+}
+
+
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+			   struct wpa_ptk *ptk, size_t ptk_len)
+{
+	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN];
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
+	const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
+	size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
+	const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
+	const u8 *ssid = sm->wpa_auth->conf.ssid;
+	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+
+
+	if (sm->xxkey_len == 0) {
+		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+			   "derivation");
+		return -1;
+	}
+
+	wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+			  r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+			    sm->pairwise);
+
+	wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+			  pmk_r1, sm->pmk_r1_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
+			    sm->pairwise);
+
+	wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+			  sm->wpa_auth->addr, sm->pmk_r1_name,
+			  (u8 *) ptk, ptk_len, ptk_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
+	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+	return 0;
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, int idx, u8 *seq)
+{
+	if (wpa_auth->cb.get_seqnum == NULL)
+		return -1;
+	return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+}
+
+
+static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len, pad_len;
+	const u8 *key;
+	size_t key_len;
+	u8 keybuf[32];
+
+	key_len = gsm->GTK_len;
+	if (key_len > sizeof(keybuf))
+		return NULL;
+
+	/*
+	 * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
+	 * than 16 bytes.
+	 */
+	pad_len = key_len % 8;
+	if (pad_len)
+		pad_len = 8 - pad_len;
+	if (key_len + pad_len < 16)
+		pad_len += 8;
+	if (pad_len) {
+		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+		os_memset(keybuf + key_len, 0, pad_len);
+		keybuf[key_len] = 0xdd;
+		key_len += pad_len;
+		key = keybuf;
+	} else
+		key = gsm->GTK[gsm->GN - 1];
+
+	/*
+	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+	 * Key[5..32].
+	 */
+	subelem_len = 13 + key_len + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	subelem[0] = FTIE_SUBELEM_GTK;
+	subelem[1] = 11 + key_len + 8;
+	/* Key ID in B0-B1 of Key Info */
+	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+	subelem[4] = gsm->GTK_len;
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
+	if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
+		os_free(subelem);
+		return NULL;
+	}
+
+	*len = subelem_len;
+	return subelem;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem, *pos;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len;
+
+	/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
+	 * Key[16+8] */
+	subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	pos = subelem;
+	*pos++ = FTIE_SUBELEM_IGTK;
+	*pos++ = subelem_len - 2;
+	WPA_PUT_LE16(pos, gsm->GN_igtk);
+	pos += 2;
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
+	pos += 6;
+	*pos++ = WPA_IGTK_LEN;
+	if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
+		     gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+		os_free(subelem);
+		return NULL;
+	}
+
+	*len = subelem_len;
+	return subelem;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
+				const u8 *ies, size_t ies_len)
+{
+	struct ieee802_11_elems parse;
+	struct rsn_rdie *rdie;
+
+	wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
+		   id, descr_count);
+	wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
+		    ies, ies_len);
+
+	if (end - pos < (int) sizeof(*rdie)) {
+		wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
+		return pos;
+	}
+
+	*pos++ = WLAN_EID_RIC_DATA;
+	*pos++ = sizeof(*rdie);
+	rdie = (struct rsn_rdie *) pos;
+	rdie->id = id;
+	rdie->descr_count = 0;
+	rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+	pos += sizeof(*rdie);
+
+	if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
+		rdie->status_code =
+			host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+		return pos;
+	}
+
+#ifdef NEED_AP_MLME
+	if (parse.wmm_tspec) {
+		struct wmm_tspec_element *tspec;
+		int res;
+
+		if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
+			wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
+				   "(%d)", (int) parse.wmm_tspec_len);
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+			return pos;
+		}
+		if (end - pos < (int) sizeof(*tspec)) {
+			wpa_printf(MSG_ERROR, "FT: Not enough room for "
+				   "response TSPEC");
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+			return pos;
+		}
+		tspec = (struct wmm_tspec_element *) pos;
+		os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+		res = wmm_process_tspec(tspec);
+		wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
+		if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
+		else if (res == WMM_ADDTS_STATUS_REFUSED)
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
+		else {
+			/* TSPEC accepted; include updated TSPEC in response */
+			rdie->descr_count = 1;
+			pos += sizeof(*tspec);
+		}
+		return pos;
+	}
+#endif /* NEED_AP_MLME */
+
+	wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
+	rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+	return pos;
+}
+
+
+static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
+{
+	const u8 *rpos, *start;
+	const struct rsn_rdie *rdie;
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
+
+	rpos = ric;
+	while (rpos + sizeof(*rdie) < ric + ric_len) {
+		if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
+		    rpos + 2 + rpos[1] > ric + ric_len)
+			break;
+		rdie = (const struct rsn_rdie *) (rpos + 2);
+		rpos += 2 + rpos[1];
+		start = rpos;
+
+		while (rpos + 2 <= ric + ric_len &&
+		       rpos + 2 + rpos[1] <= ric + ric_len) {
+			if (rpos[0] == WLAN_EID_RIC_DATA)
+				break;
+			rpos += 2 + rpos[1];
+		}
+		pos = wpa_ft_process_rdie(pos, end, rdie->id,
+					  rdie->descr_count,
+					  start, rpos - start);
+	}
+
+	return pos;
+}
+
+
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+				 size_t max_len, int auth_alg,
+				 const u8 *req_ies, size_t req_ies_len)
+{
+	u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+	size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+	int res;
+	struct wpa_auth_config *conf;
+	struct rsn_ftie *_ftie;
+	struct wpa_ft_ies parse;
+	u8 *ric_start;
+	u8 *anonce, *snonce;
+
+	if (sm == NULL)
+		return pos;
+
+	conf = &sm->wpa_auth->conf;
+
+	if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+	    sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK)
+		return pos;
+
+	end = pos + max_len;
+
+	if (auth_alg == WLAN_AUTH_FT) {
+		/*
+		 * RSN (only present if this is a Reassociation Response and
+		 * part of a fast BSS transition)
+		 */
+		res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
+		if (res < 0)
+			return pos;
+		rsnie = pos;
+		rsnie_len = res;
+		pos += res;
+	}
+
+	/* Mobility Domain Information */
+	res = wpa_write_mdie(conf, pos, end - pos);
+	if (res < 0)
+		return pos;
+	mdie = pos;
+	mdie_len = res;
+	pos += res;
+
+	/* Fast BSS Transition Information */
+	if (auth_alg == WLAN_AUTH_FT) {
+		subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+		r0kh_id = sm->r0kh_id;
+		r0kh_id_len = sm->r0kh_id_len;
+		anonce = sm->ANonce;
+		snonce = sm->SNonce;
+#ifdef CONFIG_IEEE80211W
+		if (sm->mgmt_frame_prot) {
+			u8 *igtk;
+			size_t igtk_len;
+			u8 *nbuf;
+			igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
+			if (igtk == NULL) {
+				os_free(subelem);
+				return pos;
+			}
+			nbuf = os_realloc(subelem, subelem_len + igtk_len);
+			if (nbuf == NULL) {
+				os_free(subelem);
+				os_free(igtk);
+				return pos;
+			}
+			subelem = nbuf;
+			os_memcpy(subelem + subelem_len, igtk, igtk_len);
+			subelem_len += igtk_len;
+			os_free(igtk);
+		}
+#endif /* CONFIG_IEEE80211W */
+	} else {
+		r0kh_id = conf->r0_key_holder;
+		r0kh_id_len = conf->r0_key_holder_len;
+		anonce = NULL;
+		snonce = NULL;
+	}
+	res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
+			     end - pos, subelem, subelem_len);
+	os_free(subelem);
+	if (res < 0)
+		return pos;
+	ftie = pos;
+	ftie_len = res;
+	pos += res;
+
+	os_free(sm->assoc_resp_ftie);
+	sm->assoc_resp_ftie = os_malloc(ftie_len);
+	if (sm->assoc_resp_ftie)
+		os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+
+	_ftie = (struct rsn_ftie *) (ftie + 2);
+	if (auth_alg == WLAN_AUTH_FT)
+		_ftie->mic_control[1] = 3; /* Information element count */
+
+	ric_start = pos;
+	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+		pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len);
+		if (auth_alg == WLAN_AUTH_FT)
+			_ftie->mic_control[1] +=
+				ieee802_11_ie_count(ric_start,
+						    pos - ric_start);
+	}
+	if (ric_start == pos)
+		ric_start = NULL;
+
+	if (auth_alg == WLAN_AUTH_FT &&
+	    wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6,
+		       mdie, mdie_len, ftie, ftie_len,
+		       rsnie, rsnie_len,
+		       ric_start, ric_start ? pos - ric_start : 0,
+		       _ftie->mic) < 0)
+		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+
+	return pos;
+}
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+			     struct wpa_ft_ies *parse)
+{
+	const u8 *end, *pos;
+
+	parse->ftie = ie;
+	parse->ftie_len = ie_len;
+
+	pos = ie + sizeof(struct rsn_ftie);
+	end = ie + ie_len;
+
+	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+		switch (pos[0]) {
+		case FTIE_SUBELEM_R1KH_ID:
+			if (pos[1] != FT_R1KH_ID_LEN) {
+				wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+					   "length in FTIE: %d", pos[1]);
+				return -1;
+			}
+			parse->r1kh_id = pos + 2;
+			break;
+		case FTIE_SUBELEM_GTK:
+			parse->gtk = pos + 2;
+			parse->gtk_len = pos[1];
+			break;
+		case FTIE_SUBELEM_R0KH_ID:
+			if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+				wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+					   "length in FTIE: %d", pos[1]);
+				return -1;
+			}
+			parse->r0kh_id = pos + 2;
+			parse->r0kh_id_len = pos[1];
+			break;
+		}
+
+		pos += 2 + pos[1];
+	}
+
+	return 0;
+}
+
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+			    struct wpa_ft_ies *parse)
+{
+	const u8 *end, *pos;
+	struct wpa_ie_data data;
+	int ret;
+	const struct rsn_ftie *ftie;
+	int prot_ie_count = 0;
+
+	os_memset(parse, 0, sizeof(*parse));
+	if (ies == NULL)
+		return 0;
+
+	pos = ies;
+	end = ies + ies_len;
+	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+		switch (pos[0]) {
+		case WLAN_EID_RSN:
+			parse->rsn = pos + 2;
+			parse->rsn_len = pos[1];
+			ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+						   parse->rsn_len + 2,
+						   &data);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+					   "RSN IE: %d", ret);
+				return -1;
+			}
+			if (data.num_pmkid == 1 && data.pmkid)
+				parse->rsn_pmkid = data.pmkid;
+			break;
+		case WLAN_EID_MOBILITY_DOMAIN:
+			parse->mdie = pos + 2;
+			parse->mdie_len = pos[1];
+			break;
+		case WLAN_EID_FAST_BSS_TRANSITION:
+			if (pos[1] < sizeof(*ftie))
+				return -1;
+			ftie = (const struct rsn_ftie *) (pos + 2);
+			prot_ie_count = ftie->mic_control[1];
+			if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+				return -1;
+			break;
+		case WLAN_EID_RIC_DATA:
+			if (parse->ric == NULL)
+				parse->ric = pos;
+		}
+
+		pos += 2 + pos[1];
+	}
+
+	if (prot_ie_count == 0)
+		return 0; /* no MIC */
+
+	/*
+	 * Check that the protected IE count matches with IEs included in the
+	 * frame.
+	 */
+	if (parse->rsn)
+		prot_ie_count--;
+	if (parse->mdie)
+		prot_ie_count--;
+	if (parse->ftie)
+		prot_ie_count--;
+	if (prot_ie_count < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+			   "the protected IE count");
+		return -1;
+	}
+
+	if (prot_ie_count == 0 && parse->ric) {
+		wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+			   "included in protected IE count");
+		return -1;
+	}
+
+	/* Determine the end of the RIC IE(s) */
+	pos = parse->ric;
+	while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+	       prot_ie_count) {
+		prot_ie_count--;
+		pos += 2 + pos[1];
+	}
+	parse->ric_len = pos - parse->ric;
+	if (prot_ie_count) {
+		wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+			   "frame", (int) prot_ie_count);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+				   int vlan_id,
+				   enum wpa_alg alg, const u8 *addr, int idx,
+				   u8 *key, size_t key_len)
+{
+	if (wpa_auth->cb.set_key == NULL)
+		return -1;
+	return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+				    key, key_len);
+}
+
+
+void wpa_ft_install_ptk(struct wpa_state_machine *sm)
+{
+	enum wpa_alg alg;
+	int klen;
+
+	/* MLME-SETKEYS.request(PTK) */
+	if (sm->pairwise == WPA_CIPHER_TKIP) {
+		alg = WPA_ALG_TKIP;
+		klen = 32;
+	} else if (sm->pairwise == WPA_CIPHER_CCMP) {
+		alg = WPA_ALG_CCMP;
+		klen = 16;
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
+			   "PTK configuration", sm->pairwise);
+		return;
+	}
+
+	/* FIX: add STA entry to kernel/driver here? The set_key will fail
+	 * most likely without this.. At the moment, STA entry is added only
+	 * after association has been completed. This function will be called
+	 * again after association to get the PTK configured, but that could be
+	 * optimized by adding the STA entry earlier.
+	 */
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+			     sm->PTK.tk1, klen))
+		return;
+
+	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+	sm->pairwise_set = TRUE;
+}
+
+
+static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+				   const u8 *ies, size_t ies_len,
+				   u8 **resp_ies, size_t *resp_ies_len)
+{
+	struct rsn_mdie *mdie;
+	struct rsn_ftie *ftie;
+	u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	struct wpa_auth_config *conf;
+	struct wpa_ft_ies parse;
+	size_t buflen, ptk_len;
+	int ret;
+	u8 *pos, *end;
+	int pairwise;
+
+	*resp_ies = NULL;
+	*resp_ies_len = 0;
+
+	sm->pmk_r1_name_valid = 0;
+	conf = &sm->wpa_auth->conf;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
+		    ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain,
+		      sm->wpa_auth->conf.mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return WLAN_STATUS_INVALID_MDIE;
+	}
+
+	ftie = (struct rsn_ftie *) parse.ftie;
+	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
+		    parse.r0kh_id, parse.r0kh_id_len);
+	os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+	sm->r0kh_id_len = parse.r0kh_id_len;
+
+	if (parse.rsn_pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
+		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+	wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+			       sm->wpa_auth->conf.r1_key_holder, sm->addr,
+			       pmk_r1_name);
+	wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
+		    pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
+		    &pairwise) < 0) {
+		if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id,
+				       sm->r0kh_id_len, parse.rsn_pmkid) < 0) {
+			wpa_printf(MSG_DEBUG, "FT: Did not have matching "
+				   "PMK-R1 and unknown R0KH-ID");
+			return WLAN_STATUS_INVALID_PMKID;
+		}
+
+		/*
+		 * TODO: Should return "status pending" (and the caller should
+		 * not send out response now). The real response will be sent
+		 * once the response from R0KH is received.
+		 */
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
+	sm->pmk_r1_name_valid = 1;
+	os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+			   "ANonce");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+		    sm->SNonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
+		    sm->ANonce, WPA_NONCE_LEN);
+
+	ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48;
+	wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+			  sm->wpa_auth->addr, pmk_r1_name,
+			  (u8 *) &sm->PTK, ptk_len, ptk_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
+			(u8 *) &sm->PTK, ptk_len);
+	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+	sm->pairwise = pairwise;
+	wpa_ft_install_ptk(sm);
+
+	buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+		2 + FT_R1KH_ID_LEN + 200;
+	*resp_ies = os_zalloc(buflen);
+	if (*resp_ies == NULL) {
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	pos = *resp_ies;
+	end = *resp_ies + buflen;
+
+	ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
+	if (ret < 0) {
+		os_free(*resp_ies);
+		*resp_ies = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pos += ret;
+
+	ret = wpa_write_mdie(conf, pos, end - pos);
+	if (ret < 0) {
+		os_free(*resp_ies);
+		*resp_ies = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pos += ret;
+
+	ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
+			     sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
+	if (ret < 0) {
+		os_free(*resp_ies);
+		*resp_ies = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pos += ret;
+
+	*resp_ies_len = pos - *resp_ies;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+			 u16 auth_transaction, const u8 *ies, size_t ies_len,
+			 void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+				    u16 auth_transaction, u16 status,
+				    const u8 *ies, size_t ies_len),
+			 void *ctx)
+{
+	u16 status;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
+			   "WPA SM not available");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
+		   " BSSID=" MACSTR " transaction=%d",
+		   MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
+	status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+					 &resp_ies_len);
+
+	wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
+		   " auth_transaction=%d status=%d",
+		   MAC2STR(sm->addr), auth_transaction + 1, status);
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+	cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
+	   resp_ies, resp_ies_len);
+	os_free(resp_ies);
+}
+
+
+u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+			    size_t ies_len)
+{
+	struct wpa_ft_ies parse;
+	struct rsn_mdie *mdie;
+	struct rsn_ftie *ftie;
+	u8 mic[16];
+	unsigned int count;
+
+	if (sm == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (parse.rsn == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (parse.rsn_pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
+	{
+		wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
+			   "with the PMKR1Name derived from auth request");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain,
+		      sm->wpa_auth->conf.mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return WLAN_STATUS_INVALID_MDIE;
+	}
+
+	ftie = (struct rsn_ftie *) parse.ftie;
+	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+			    ftie->snonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+			    sm->SNonce, WPA_NONCE_LEN);
+		return -1;
+	}
+
+	if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+			    ftie->anonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+			    sm->ANonce, WPA_NONCE_LEN);
+		return -1;
+	}
+
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	if (parse.r0kh_id_len != sm->r0kh_id_len ||
+	    os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+			   "the current R0KH-ID");
+		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+			    parse.r0kh_id, parse.r0kh_id_len);
+		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+
+	if (parse.r1kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+		return -1;
+	}
+
+	if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
+		      FT_R1KH_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+			   "ReassocReq");
+		wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
+			    parse.r1kh_id, FT_R1KH_ID_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
+			    sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+		return -1;
+	}
+
+	if (parse.rsn_pmkid == NULL ||
+	    os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+		wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+			   "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+		return -1;
+	}
+
+	count = 3;
+	if (parse.ric)
+		count++;
+	if (ftie->mic_control[1] != count) {
+		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+			   "Control: received %u expected %u",
+			   ftie->mic_control[1], count);
+		return -1;
+	}
+
+	if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5,
+		       parse.mdie - 2, parse.mdie_len + 2,
+		       parse.ftie - 2, parse.ftie_len + 2,
+		       parse.rsn - 2, parse.rsn_len + 2,
+		       parse.ric, parse.ric_len,
+		       mic) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (os_memcmp(mic, ftie->mic, 16) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
+{
+	const u8 *sta_addr, *target_ap;
+	const u8 *ies;
+	size_t ies_len;
+	u8 action;
+	struct ft_rrb_frame *frame;
+
+	if (sm == NULL)
+		return -1;
+
+	/*
+	 * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+	 * FT Request action frame body[variable]
+	 */
+
+	if (len < 14) {
+		wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
+			   "(len=%lu)", (unsigned long) len);
+		return -1;
+	}
+
+	action = data[1];
+	sta_addr = data + 2;
+	target_ap = data + 8;
+	ies = data + 14;
+	ies_len = len - 14;
+
+	wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
+		   " Target AP=" MACSTR " Action=%d)",
+		   MAC2STR(sta_addr), MAC2STR(target_ap), action);
+
+	if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
+			   "STA=" MACSTR " STA-Address=" MACSTR,
+			   MAC2STR(sm->addr), MAC2STR(sta_addr));
+		return -1;
+	}
+
+	/*
+	 * Do some sanity checking on the target AP address (not own and not
+	 * broadcast. This could be extended to filter based on a list of known
+	 * APs in the MD (if such a list were configured).
+	 */
+	if ((target_ap[0] & 0x01) ||
+	    os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
+			   "frame");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+
+	/* RRB - Forward action frame to the target AP */
+	frame = os_malloc(sizeof(*frame) + len);
+	frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame->packet_type = FT_PACKET_REQUEST;
+	frame->action_length = host_to_le16(len);
+	os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
+	os_memcpy(frame + 1, data, len);
+
+	wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
+			sizeof(*frame) + len);
+	os_free(frame);
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
+				 const u8 *current_ap, const u8 *sta_addr,
+				 const u8 *body, size_t len)
+{
+	struct wpa_state_machine *sm;
+	u16 status;
+	u8 *resp_ies, *pos;
+	size_t resp_ies_len, rlen;
+	struct ft_rrb_frame *frame;
+
+	sm = wpa_ft_add_sta(wpa_auth, sta_addr);
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
+			   "RRB Request");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
+
+	status = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+					 &resp_ies_len);
+
+	wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
+		   " CurrentAP=" MACSTR " status=%d",
+		   MAC2STR(sm->addr), MAC2STR(current_ap), status);
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+
+	/* RRB - Forward action frame response to the Current AP */
+
+	/*
+	 * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+	 * Status_Code[2] FT Request action frame body[variable]
+	 */
+	rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
+
+	frame = os_malloc(sizeof(*frame) + rlen);
+	frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame->packet_type = FT_PACKET_RESPONSE;
+	frame->action_length = host_to_le16(rlen);
+	os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
+	pos = (u8 *) (frame + 1);
+	*pos++ = WLAN_ACTION_FT;
+	*pos++ = 2; /* Action: Response */
+	os_memcpy(pos, sta_addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	WPA_PUT_LE16(pos, status);
+	pos += 2;
+	if (resp_ies) {
+		os_memcpy(pos, resp_ies, resp_ies_len);
+		os_free(resp_ies);
+	}
+
+	wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
+			sizeof(*frame) + rlen);
+	os_free(frame);
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *data, size_t data_len)
+{
+	struct ft_r0kh_r1kh_pull_frame *frame, f;
+	struct ft_remote_r1kh *r1kh;
+	struct ft_r0kh_r1kh_resp_frame resp, r;
+	u8 pmk_r0[PMK_LEN];
+	int pairwise;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
+
+	if (data_len < sizeof(*frame))
+		return -1;
+
+	r1kh = wpa_auth->conf.r1kh_list;
+	while (r1kh) {
+		if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
+			break;
+		r1kh = r1kh->next;
+	}
+	if (r1kh == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
+			   "PMK-R1 pull source address " MACSTR,
+			   MAC2STR(src_addr));
+		return -1;
+	}
+
+	frame = (struct ft_r0kh_r1kh_pull_frame *) data;
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+		       frame->nonce, f.nonce) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+			   "request from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
+		    f.nonce, sizeof(f.nonce));
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
+		    f.pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+		   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
+
+	os_memset(&resp, 0, sizeof(resp));
+	resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
+	resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
+	os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
+
+	/* aes_wrap() does not support inplace encryption, so use a temporary
+	 * buffer for the data. */
+	os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
+	os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
+	os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
+	if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
+				&pairwise) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
+			   "PMK-R1 pull");
+		return -1;
+	}
+
+	wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
+			  r.pmk_r1, r.pmk_r1_name);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	r.pairwise = host_to_le16(pairwise);
+
+	if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+		     r.nonce, resp.nonce) < 0) {
+		os_memset(pmk_r0, 0, PMK_LEN);
+		return -1;
+	}
+
+	os_memset(pmk_r0, 0, PMK_LEN);
+
+	wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *data, size_t data_len)
+{
+	struct ft_r0kh_r1kh_resp_frame *frame, f;
+	struct ft_remote_r0kh *r0kh;
+	int pairwise;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
+
+	if (data_len < sizeof(*frame))
+		return -1;
+
+	r0kh = wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+			break;
+		r0kh = r0kh->next;
+	}
+	if (r0kh == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+			   "PMK-R0 pull response source address " MACSTR,
+			   MAC2STR(src_addr));
+		return -1;
+	}
+
+	frame = (struct ft_r0kh_r1kh_resp_frame *) data;
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+		       frame->nonce, f.nonce) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+			   "response from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+	if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
+	    != 0) {
+		wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
+			   "matching R1KH-ID");
+		return -1;
+	}
+
+	/* TODO: verify that <nonce,s1kh_id> matches with a pending request
+	 * and call this requests callback function to finish request
+	 * processing */
+
+	pairwise = le_to_host16(f.pairwise);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
+		    f.nonce, sizeof(f.nonce));
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+		   MACSTR " pairwise=0x%x",
+		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
+			f.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
+			f.pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+			    pairwise);
+	os_memset(f.pmk_r1, 0, PMK_LEN);
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *data, size_t data_len)
+{
+	struct ft_r0kh_r1kh_push_frame *frame, f;
+	struct ft_remote_r0kh *r0kh;
+	struct os_time now;
+	os_time_t tsend;
+	int pairwise;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
+
+	if (data_len < sizeof(*frame))
+		return -1;
+
+	r0kh = wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+			break;
+		r0kh = r0kh->next;
+	}
+	if (r0kh == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+			   "PMK-R0 push source address " MACSTR,
+			   MAC2STR(src_addr));
+		return -1;
+	}
+
+	frame = (struct ft_r0kh_r1kh_push_frame *) data;
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+		       frame->timestamp, f.timestamp) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
+			   MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+	os_get_time(&now);
+	tsend = WPA_GET_LE32(f.timestamp);
+	if ((now.sec > tsend && now.sec - tsend > 60) ||
+	    (now.sec < tsend && tsend - now.sec > 60)) {
+		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
+			   "timestamp: sender time %d own time %d\n",
+			   (int) tsend, (int) now.sec);
+		return -1;
+	}
+
+	if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
+	    != 0) {
+		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
+			   "R1KH-ID (received " MACSTR " own " MACSTR ")",
+			   MAC2STR(f.r1kh_id),
+			   MAC2STR(wpa_auth->conf.r1_key_holder));
+		return -1;
+	}
+
+	pairwise = le_to_host16(f.pairwise);
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
+		   MACSTR " pairwise=0x%x",
+		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
+			f.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
+			f.pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+			    pairwise);
+	os_memset(f.pmk_r1, 0, PMK_LEN);
+
+	return 0;
+}
+
+
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		  const u8 *data, size_t data_len)
+{
+	struct ft_rrb_frame *frame;
+	u16 alen;
+	const u8 *pos, *end, *start;
+	u8 action;
+	const u8 *sta_addr, *target_ap_addr;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
+		   MAC2STR(src_addr));
+
+	if (data_len < sizeof(*frame)) {
+		wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
+			   (unsigned long) data_len);
+		return -1;
+	}
+
+	pos = data;
+	frame = (struct ft_rrb_frame *) pos;
+	pos += sizeof(*frame);
+
+	alen = le_to_host16(frame->action_length);
+	wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
+		   "action_length=%d ap_address=" MACSTR,
+		   frame->frame_type, frame->packet_type, alen,
+		   MAC2STR(frame->ap_address));
+
+	if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
+		/* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
+		wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
+			   "unrecognized type %d", frame->frame_type);
+		return -1;
+	}
+
+	if (alen > data_len - sizeof(*frame)) {
+		wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
+			   "frame");
+		return -1;
+	}
+
+	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
+		return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
+	if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
+		return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
+	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
+		return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
+
+	if (alen < 1 + 1 + 2 * ETH_ALEN) {
+		wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
+			   "room for Action Frame body); alen=%lu",
+			   (unsigned long) alen);
+		return -1;
+	}
+	start = pos;
+	end = pos + alen;
+
+	if (*pos != WLAN_ACTION_FT) {
+		wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
+			   "%d", *pos);
+		return -1;
+	}
+
+	pos++;
+	action = *pos++;
+	sta_addr = pos;
+	pos += ETH_ALEN;
+	target_ap_addr = pos;
+	pos += ETH_ALEN;
+	wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
+		   MACSTR " target_ap_addr=" MACSTR,
+		   action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
+
+	if (frame->packet_type == FT_PACKET_REQUEST) {
+		wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
+
+		if (action != 1) {
+			wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
+				   "RRB Request", action);
+			return -1;
+		}
+
+		if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
+				   "RRB Request does not match with own "
+				   "address");
+			return -1;
+		}
+
+		if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
+					  sta_addr, pos, end - pos) < 0)
+			return -1;
+	} else if (frame->packet_type == FT_PACKET_RESPONSE) {
+		u16 status_code;
+
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
+				   "code in RRB Response");
+			return -1;
+		}
+		status_code = WPA_GET_LE16(pos);
+		pos += 2;
+
+		wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
+			   "(status_code=%d)", status_code);
+
+		if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
+			return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
+			   "packet_type %d", frame->packet_type);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+				   struct wpa_ft_pmk_r0_sa *pmk_r0,
+				   struct ft_remote_r1kh *r1kh,
+				   const u8 *s1kh_id, int pairwise)
+{
+	struct ft_r0kh_r1kh_push_frame frame, f;
+	struct os_time now;
+
+	os_memset(&frame, 0, sizeof(frame));
+	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
+	frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
+	os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+
+	/* aes_wrap() does not support inplace encryption, so use a temporary
+	 * buffer for the data. */
+	os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
+	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+	os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
+			  s1kh_id, f.pmk_r1, f.pmk_r1_name);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	os_get_time(&now);
+	WPA_PUT_LE32(f.timestamp, now.sec);
+	f.pairwise = host_to_le16(pairwise);
+	if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+		     f.timestamp, frame.timestamp) < 0)
+		return;
+
+	wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+}
+
+
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	struct wpa_ft_pmk_r0_sa *r0;
+	struct ft_remote_r1kh *r1kh;
+
+	if (!wpa_auth->conf.pmk_r1_push)
+		return;
+
+	r0 = wpa_auth->ft_pmk_cache->pmk_r0;
+	while (r0) {
+		if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
+			break;
+		r0 = r0->next;
+	}
+
+	if (r0 == NULL || r0->pmk_r1_pushed)
+		return;
+	r0->pmk_r1_pushed = 1;
+
+	wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
+		   "for STA " MACSTR, MAC2STR(addr));
+
+	r1kh = wpa_auth->conf.r1kh_list;
+	while (r1kh) {
+		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
+		r1kh = r1kh->next;
+	}
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
new file mode 100644
index 0000000..b35b7ba
--- /dev/null
+++ b/src/ap/wpa_auth_glue.c
@@ -0,0 +1,571 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "eap_server/eap.h"
+#include "l2_packet/l2_packet.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "preauth_auth.h"
+#include "sta_info.h"
+#include "tkip_countermeasures.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "wpa_auth.h"
+
+
+static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+				  struct wpa_auth_config *wconf)
+{
+	wconf->wpa = conf->wpa;
+	wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+	wconf->wpa_pairwise = conf->wpa_pairwise;
+	wconf->wpa_group = conf->wpa_group;
+	wconf->wpa_group_rekey = conf->wpa_group_rekey;
+	wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
+	wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
+	wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+	wconf->rsn_pairwise = conf->rsn_pairwise;
+	wconf->rsn_preauth = conf->rsn_preauth;
+	wconf->eapol_version = conf->eapol_version;
+	wconf->peerkey = conf->peerkey;
+	wconf->wmm_enabled = conf->wmm_enabled;
+	wconf->wmm_uapsd = conf->wmm_uapsd;
+	wconf->okc = conf->okc;
+#ifdef CONFIG_IEEE80211W
+	wconf->ieee80211w = conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+	wconf->ssid_len = conf->ssid.ssid_len;
+	if (wconf->ssid_len > SSID_LEN)
+		wconf->ssid_len = SSID_LEN;
+	os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
+	os_memcpy(wconf->mobility_domain, conf->mobility_domain,
+		  MOBILITY_DOMAIN_ID_LEN);
+	if (conf->nas_identifier &&
+	    os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) {
+		wconf->r0_key_holder_len = os_strlen(conf->nas_identifier);
+		os_memcpy(wconf->r0_key_holder, conf->nas_identifier,
+			  wconf->r0_key_holder_len);
+	}
+	os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
+	wconf->r0_key_lifetime = conf->r0_key_lifetime;
+	wconf->reassociation_deadline = conf->reassociation_deadline;
+	wconf->r0kh_list = conf->r0kh_list;
+	wconf->r1kh_list = conf->r1kh_list;
+	wconf->pmk_r1_push = conf->pmk_r1_push;
+	wconf->ft_over_ds = conf->ft_over_ds;
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr,
+				    logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+	struct hostapd_data *hapd = ctx;
+	int hlevel;
+
+	switch (level) {
+	case LOGGER_WARNING:
+		hlevel = HOSTAPD_LEVEL_WARNING;
+		break;
+	case LOGGER_INFO:
+		hlevel = HOSTAPD_LEVEL_INFO;
+		break;
+	case LOGGER_DEBUG:
+	default:
+		hlevel = HOSTAPD_LEVEL_DEBUG;
+		break;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
+					u16 reason)
+{
+	struct hostapd_data *hapd = ctx;
+	wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
+		   "STA " MACSTR " reason %d",
+		   __func__, MAC2STR(addr), reason);
+	ap_sta_disconnect(hapd, NULL, addr, reason);
+}
+
+
+static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	michael_mic_failure(hapd, addr, 0);
+}
+
+
+static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
+				       wpa_eapol_variable var, int value)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta == NULL)
+		return;
+	switch (var) {
+	case WPA_EAPOL_portEnabled:
+		ieee802_1x_notify_port_enabled(sta->eapol_sm, value);
+		break;
+	case WPA_EAPOL_portValid:
+		ieee802_1x_notify_port_valid(sta->eapol_sm, value);
+		break;
+	case WPA_EAPOL_authorized:
+		ieee802_1x_set_sta_authorized(hapd, sta, value);
+		break;
+	case WPA_EAPOL_portControl_Auto:
+		if (sta->eapol_sm)
+			sta->eapol_sm->portControl = Auto;
+		break;
+	case WPA_EAPOL_keyRun:
+		if (sta->eapol_sm)
+			sta->eapol_sm->keyRun = value ? TRUE : FALSE;
+		break;
+	case WPA_EAPOL_keyAvailable:
+		if (sta->eapol_sm)
+			sta->eapol_sm->eap_if->eapKeyAvailable =
+				value ? TRUE : FALSE;
+		break;
+	case WPA_EAPOL_keyDone:
+		if (sta->eapol_sm)
+			sta->eapol_sm->keyDone = value ? TRUE : FALSE;
+		break;
+	case WPA_EAPOL_inc_EapolFramesTx:
+		if (sta->eapol_sm)
+			sta->eapol_sm->dot1xAuthEapolFramesTx++;
+		break;
+	}
+}
+
+
+static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
+				      wpa_eapol_variable var)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta == NULL || sta->eapol_sm == NULL)
+		return -1;
+	switch (var) {
+	case WPA_EAPOL_keyRun:
+		return sta->eapol_sm->keyRun;
+	case WPA_EAPOL_keyAvailable:
+		return sta->eapol_sm->eap_if->eapKeyAvailable;
+	default:
+		return -1;
+	}
+}
+
+
+static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+					   const u8 *prev_psk)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_get_psk(hapd->conf, addr, prev_psk);
+}
+
+
+static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
+				    size_t *len)
+{
+	struct hostapd_data *hapd = ctx;
+	const u8 *key;
+	size_t keylen;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL)
+		return -1;
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+	if (key == NULL)
+		return -1;
+
+	if (keylen > *len)
+		keylen = *len;
+	os_memcpy(msk, key, keylen);
+	*len = keylen;
+
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+				    const u8 *addr, int idx, u8 *key,
+				    size_t key_len)
+{
+	struct hostapd_data *hapd = ctx;
+	const char *ifname = hapd->conf->iface;
+
+	if (vlan_id > 0) {
+		ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+		if (ifname == NULL)
+			return -1;
+	}
+
+	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+				   key, key_len);
+}
+
+
+static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx,
+				       u8 *seq)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq);
+}
+
+
+static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
+				       const u8 *data, size_t data_len,
+				       int encrypt)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	u32 flags = 0;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		flags = hostapd_sta_flags_to_drv(sta->flags);
+
+	return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
+					   encrypt, flags);
+}
+
+
+static int hostapd_wpa_auth_for_each_sta(
+	void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx),
+	void *cb_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx))
+			return 1;
+	}
+	return 0;
+}
+
+
+struct wpa_auth_iface_iter_data {
+	int (*cb)(struct wpa_authenticator *sm, void *ctx);
+	void *cb_ctx;
+};
+
+static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_iface_iter_data *data = ctx;
+	size_t i;
+	for (i = 0; i < iface->num_bss; i++) {
+		if (iface->bss[i]->wpa_auth &&
+		    data->cb(iface->bss[i]->wpa_auth, data->cb_ctx))
+			return 1;
+	}
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_for_each_auth(
+	void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx),
+	void *cb_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct wpa_auth_iface_iter_data data;
+	if (hapd->iface->for_each_interface == NULL)
+		return -1;
+	data.cb = cb;
+	data.cb_ctx = cb_ctx;
+	return hapd->iface->for_each_interface(hapd->iface->interfaces,
+					       wpa_auth_iface_iter, &data);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+struct wpa_auth_ft_iface_iter_data {
+	struct hostapd_data *src_hapd;
+	const u8 *dst;
+	const u8 *data;
+	size_t data_len;
+};
+
+
+static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_ft_iface_iter_data *idata = ctx;
+	struct hostapd_data *hapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd == idata->src_hapd)
+			continue;
+		if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
+				   "locally managed BSS " MACSTR "@%s -> "
+				   MACSTR "@%s",
+				   MAC2STR(idata->src_hapd->own_addr),
+				   idata->src_hapd->conf->iface,
+				   MAC2STR(hapd->own_addr), hapd->conf->iface);
+			wpa_ft_rrb_rx(hapd->wpa_auth,
+				      idata->src_hapd->own_addr,
+				      idata->data, idata->data_len);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
+				       const u8 *data, size_t data_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct l2_ethhdr *buf;
+	int ret;
+
+#ifdef CONFIG_IEEE80211R
+	if (proto == ETH_P_RRB && hapd->iface->for_each_interface) {
+		int res;
+		struct wpa_auth_ft_iface_iter_data idata;
+		idata.src_hapd = hapd;
+		idata.dst = dst;
+		idata.data = data;
+		idata.data_len = data_len;
+		res = hapd->iface->for_each_interface(hapd->iface->interfaces,
+						      hostapd_wpa_auth_ft_iter,
+						      &idata);
+		if (res == 1)
+			return data_len;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (hapd->driver && hapd->driver->send_ether)
+		return hapd->driver->send_ether(hapd->drv_priv, dst,
+						hapd->own_addr, proto,
+						data, data_len);
+	if (hapd->l2 == NULL)
+		return -1;
+
+	buf = os_malloc(sizeof(*buf) + data_len);
+	if (buf == NULL)
+		return -1;
+	os_memcpy(buf->h_dest, dst, ETH_ALEN);
+	os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN);
+	buf->h_proto = host_to_be16(proto);
+	os_memcpy(buf + 1, data, data_len);
+	ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf,
+			     sizeof(*buf) + data_len);
+	os_free(buf);
+	return -1;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
+					   const u8 *data, size_t data_len)
+{
+	struct hostapd_data *hapd = ctx;
+	int res;
+	struct ieee80211_mgmt *m;
+	size_t mlen;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL || sta->wpa_sm == NULL)
+		return -1;
+
+	m = os_zalloc(sizeof(*m) + data_len);
+	if (m == NULL)
+		return -1;
+	mlen = ((u8 *) &m->u - (u8 *) m) + data_len;
+	m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					WLAN_FC_STYPE_ACTION);
+	os_memcpy(m->da, dst, ETH_ALEN);
+	os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+	os_memcpy(&m->u, data, data_len);
+
+	res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen);
+	os_free(m);
+	return res;
+}
+
+
+static struct wpa_state_machine *
+hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_sta_add(hapd, sta_addr);
+	if (sta == NULL)
+		return NULL;
+	if (sta->wpa_sm) {
+		sta->auth_alg = WLAN_AUTH_FT;
+		return sta->wpa_sm;
+	}
+
+	sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr);
+	if (sta->wpa_sm == NULL) {
+		ap_free_sta(hapd, sta);
+		return NULL;
+	}
+	sta->auth_alg = WLAN_AUTH_FT;
+
+	return sta->wpa_sm;
+}
+
+
+static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct l2_ethhdr *ethhdr;
+	if (len < sizeof(*ethhdr))
+		return;
+	ethhdr = (struct l2_ethhdr *) buf;
+	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+		   MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
+	wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
+		      len - sizeof(*ethhdr));
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+int hostapd_setup_wpa(struct hostapd_data *hapd)
+{
+	struct wpa_auth_config _conf;
+	struct wpa_auth_callbacks cb;
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	hostapd_wpa_auth_conf(hapd->conf, &_conf);
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+		_conf.tx_status = 1;
+	os_memset(&cb, 0, sizeof(cb));
+	cb.ctx = hapd;
+	cb.logger = hostapd_wpa_auth_logger;
+	cb.disconnect = hostapd_wpa_auth_disconnect;
+	cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
+	cb.set_eapol = hostapd_wpa_auth_set_eapol;
+	cb.get_eapol = hostapd_wpa_auth_get_eapol;
+	cb.get_psk = hostapd_wpa_auth_get_psk;
+	cb.get_msk = hostapd_wpa_auth_get_msk;
+	cb.set_key = hostapd_wpa_auth_set_key;
+	cb.get_seqnum = hostapd_wpa_auth_get_seqnum;
+	cb.send_eapol = hostapd_wpa_auth_send_eapol;
+	cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
+	cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
+	cb.send_ether = hostapd_wpa_auth_send_ether;
+#ifdef CONFIG_IEEE80211R
+	cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
+	cb.add_sta = hostapd_wpa_auth_add_sta;
+#endif /* CONFIG_IEEE80211R */
+	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
+	if (hapd->wpa_auth == NULL) {
+		wpa_printf(MSG_ERROR, "WPA initialization failed.");
+		return -1;
+	}
+
+	if (hostapd_set_privacy(hapd, 1)) {
+		wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked "
+			   "for interface %s", hapd->conf->iface);
+		return -1;
+	}
+
+	wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+	if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) {
+		wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+			   "the kernel driver.");
+		return -1;
+	}
+
+	if (rsn_preauth_iface_init(hapd)) {
+		wpa_printf(MSG_ERROR, "Initialization of RSN "
+			   "pre-authentication failed.");
+		return -1;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (!hostapd_drv_none(hapd)) {
+		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
+					  hapd->conf->bridge :
+					  hapd->conf->iface, NULL, ETH_P_RRB,
+					  hostapd_rrb_receive, hapd, 1);
+		if (hapd->l2 == NULL &&
+		    (hapd->driver == NULL ||
+		     hapd->driver->send_ether == NULL)) {
+			wpa_printf(MSG_ERROR, "Failed to open l2_packet "
+				   "interface");
+			return -1;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	return 0;
+
+}
+
+
+void hostapd_reconfig_wpa(struct hostapd_data *hapd)
+{
+	struct wpa_auth_config wpa_auth_conf;
+	hostapd_wpa_auth_conf(hapd->conf, &wpa_auth_conf);
+	wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
+}
+
+
+void hostapd_deinit_wpa(struct hostapd_data *hapd)
+{
+	rsn_preauth_iface_deinit(hapd);
+	if (hapd->wpa_auth) {
+		wpa_deinit(hapd->wpa_auth);
+		hapd->wpa_auth = NULL;
+
+		if (hostapd_set_privacy(hapd, 0)) {
+			wpa_printf(MSG_DEBUG, "Could not disable "
+				   "PrivacyInvoked for interface %s",
+				   hapd->conf->iface);
+		}
+
+		if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+			wpa_printf(MSG_DEBUG, "Could not remove generic "
+				   "information element from interface %s",
+				   hapd->conf->iface);
+		}
+	}
+	ieee802_1x_deinit(hapd);
+
+#ifdef CONFIG_IEEE80211R
+	l2_packet_deinit(hapd->l2);
+#endif /* CONFIG_IEEE80211R */
+}
diff --git a/src/ap/wpa_auth_glue.h b/src/ap/wpa_auth_glue.h
new file mode 100644
index 0000000..79d7e05
--- /dev/null
+++ b/src/ap/wpa_auth_glue.h
@@ -0,0 +1,22 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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 WPA_AUTH_GLUE_H
+#define WPA_AUTH_GLUE_H
+
+int hostapd_setup_wpa(struct hostapd_data *hapd);
+void hostapd_reconfig_wpa(struct hostapd_data *hapd);
+void hostapd_deinit_wpa(struct hostapd_data *hapd);
+
+#endif /* WPA_AUTH_GLUE_H */
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
new file mode 100644
index 0000000..67a5c3b
--- /dev/null
+++ b/src/ap/wpa_auth_i.h
@@ -0,0 +1,234 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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 WPA_AUTH_I_H
+#define WPA_AUTH_I_H
+
+/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
+#define RSNA_MAX_EAPOL_RETRIES 4
+
+struct wpa_group;
+
+struct wpa_stsl_negotiation {
+	struct wpa_stsl_negotiation *next;
+	u8 initiator[ETH_ALEN];
+	u8 peer[ETH_ALEN];
+};
+
+
+struct wpa_state_machine {
+	struct wpa_authenticator *wpa_auth;
+	struct wpa_group *group;
+
+	u8 addr[ETH_ALEN];
+
+	enum {
+		WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
+		WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
+		WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
+		WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
+		WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
+	} wpa_ptk_state;
+
+	enum {
+		WPA_PTK_GROUP_IDLE = 0,
+		WPA_PTK_GROUP_REKEYNEGOTIATING,
+		WPA_PTK_GROUP_REKEYESTABLISHED,
+		WPA_PTK_GROUP_KEYERROR
+	} wpa_ptk_group_state;
+
+	Boolean Init;
+	Boolean DeauthenticationRequest;
+	Boolean AuthenticationRequest;
+	Boolean ReAuthenticationRequest;
+	Boolean Disconnect;
+	int TimeoutCtr;
+	int GTimeoutCtr;
+	Boolean TimeoutEvt;
+	Boolean EAPOLKeyReceived;
+	Boolean EAPOLKeyPairwise;
+	Boolean EAPOLKeyRequest;
+	Boolean MICVerified;
+	Boolean GUpdateStationKeys;
+	u8 ANonce[WPA_NONCE_LEN];
+	u8 SNonce[WPA_NONCE_LEN];
+	u8 PMK[PMK_LEN];
+	struct wpa_ptk PTK;
+	Boolean PTK_valid;
+	Boolean pairwise_set;
+	int keycount;
+	Boolean Pair;
+	struct {
+		u8 counter[WPA_REPLAY_COUNTER_LEN];
+		Boolean valid;
+	} key_replay[RSNA_MAX_EAPOL_RETRIES];
+	Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
+	Boolean PTKRequest; /* not in IEEE 802.11i state machine */
+	Boolean has_GTK;
+	Boolean PtkGroupInit; /* init request for PTK Group state machine */
+
+	u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
+	size_t last_rx_eapol_key_len;
+
+	unsigned int changed:1;
+	unsigned int in_step_loop:1;
+	unsigned int pending_deinit:1;
+	unsigned int started:1;
+	unsigned int mgmt_frame_prot:1;
+#ifdef CONFIG_IEEE80211R
+	unsigned int ft_completed:1;
+	unsigned int pmk_r1_name_valid:1;
+#endif /* CONFIG_IEEE80211R */
+
+	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	int req_replay_counter_used;
+
+	u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	enum {
+		WPA_VERSION_NO_WPA = 0 /* WPA not used */,
+		WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
+		WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
+	} wpa;
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	u32 dot11RSNAStatsTKIPLocalMICFailures;
+	u32 dot11RSNAStatsTKIPRemoteMICFailures;
+
+#ifdef CONFIG_IEEE80211R
+	u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+	size_t xxkey_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
+					   * Request */
+	u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
+	size_t r0kh_id_len;
+	u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
+					       * message 2/4 */
+	u8 *assoc_resp_ftie;
+#endif /* CONFIG_IEEE80211R */
+
+	int pending_1_of_4_timeout;
+};
+
+
+/* per group key state machine data */
+struct wpa_group {
+	struct wpa_group *next;
+	int vlan_id;
+
+	Boolean GInit;
+	int GKeyDoneStations;
+	Boolean GTKReKey;
+	int GTK_len;
+	int GN, GM;
+	Boolean GTKAuthenticator;
+	u8 Counter[WPA_NONCE_LEN];
+
+	enum {
+		WPA_GROUP_GTK_INIT = 0,
+		WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE
+	} wpa_group_state;
+
+	u8 GMK[WPA_GMK_LEN];
+	u8 GTK[2][WPA_GTK_MAX_LEN];
+	u8 GNonce[WPA_NONCE_LEN];
+	Boolean changed;
+	Boolean first_sta_seen;
+	Boolean reject_4way_hs_for_entropy;
+#ifdef CONFIG_IEEE80211W
+	u8 IGTK[2][WPA_IGTK_LEN];
+	int GN_igtk, GM_igtk;
+#endif /* CONFIG_IEEE80211W */
+};
+
+
+struct wpa_ft_pmk_cache;
+
+/* per authenticator data */
+struct wpa_authenticator {
+	struct wpa_group *group;
+
+	unsigned int dot11RSNAStatsTKIPRemoteMICFailures;
+	u32 dot11RSNAAuthenticationSuiteSelected;
+	u32 dot11RSNAPairwiseCipherSelected;
+	u32 dot11RSNAGroupCipherSelected;
+	u8 dot11RSNAPMKIDUsed[PMKID_LEN];
+	u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */
+	u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */
+	u32 dot11RSNAGroupCipherRequested; /* FIX: update */
+	unsigned int dot11RSNATKIPCounterMeasuresInvoked;
+	unsigned int dot11RSNA4WayHandshakeFailures;
+
+	struct wpa_stsl_negotiation *stsl_negotiations;
+
+	struct wpa_auth_config conf;
+	struct wpa_auth_callbacks cb;
+
+	u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	u8 addr[ETH_ALEN];
+
+	struct rsn_pmksa_cache *pmksa;
+	struct wpa_ft_pmk_cache *ft_pmk_cache;
+};
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+		     const u8 *pmkid);
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		     logger_level level, const char *txt);
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		      logger_level level, const char *fmt, ...);
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm, int key_info,
+		      const u8 *key_rsc, const u8 *nonce,
+		      const u8 *kde, size_t kde_len,
+		      int keyidx, int encr, int force_version);
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
+			  void *cb_ctx);
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+			   int (*cb)(struct wpa_authenticator *a, void *ctx),
+			   void *cb_ctx);
+
+#ifdef CONFIG_PEERKEY
+int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
+		    struct wpa_stsl_negotiation *neg);
+void wpa_smk_error(struct wpa_authenticator *wpa_auth,
+		   struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
+int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
+		   size_t r0kh_id_len,
+		   const u8 *anonce, const u8 *snonce,
+		   u8 *buf, size_t len, const u8 *subelem,
+		   size_t subelem_len);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+			   struct wpa_ptk *ptk, size_t ptk_len);
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
+void wpa_ft_install_ptk(struct wpa_state_machine *sm);
+#endif /* CONFIG_IEEE80211R */
+
+#endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
new file mode 100644
index 0000000..5e8d134
--- /dev/null
+++ b/src/ap/wpa_auth_ie.c
@@ -0,0 +1,824 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_ie.h"
+#include "wpa_auth_i.h"
+
+
+#ifdef CONFIG_RSN_TESTING
+int rsn_testing = 0;
+#endif /* CONFIG_RSN_TESTING */
+
+
+static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	struct wpa_ie_hdr *hdr;
+	int num_suites;
+	u8 *pos, *count;
+
+	hdr = (struct wpa_ie_hdr *) buf;
+	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+	WPA_PUT_LE16(hdr->version, WPA_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	if (conf->wpa_group == WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+	} else if (conf->wpa_group == WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+	} else if (conf->wpa_group == WPA_CIPHER_WEP104) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
+	} else if (conf->wpa_group == WPA_CIPHER_WEP40) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
+	} else {
+		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+			   conf->wpa_group);
+		return -1;
+	}
+	pos += WPA_SELECTOR_LEN;
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+	if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_pairwise & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+			   conf->wpa_pairwise);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+			   conf->wpa_key_mgmt);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	/* WPA Capabilities; use defaults, so no need to include it */
+
+	hdr->len = (pos - buf) - 2;
+
+	return pos - buf;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+		     const u8 *pmkid)
+{
+	struct rsn_ie_hdr *hdr;
+	int num_suites;
+	u8 *pos, *count;
+	u16 capab;
+
+	hdr = (struct rsn_ie_hdr *) buf;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	if (conf->wpa_group == WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	} else if (conf->wpa_group == WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+	} else if (conf->wpa_group == WPA_CIPHER_WEP104) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
+	} else if (conf->wpa_group == WPA_CIPHER_WEP40) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
+	} else {
+		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+			   conf->wpa_group);
+		return -1;
+	}
+	pos += RSN_SELECTOR_LEN;
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (conf->rsn_pairwise & WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->rsn_pairwise & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+			   conf->rsn_pairwise);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+			   conf->wpa_key_mgmt);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	/* RSN Capabilities */
+	capab = 0;
+	if (conf->rsn_preauth)
+		capab |= WPA_CAPABILITY_PREAUTH;
+	if (conf->peerkey)
+		capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
+	if (conf->wmm_enabled) {
+		/* 4 PTKSA replay counters when using WMM */
+		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+	}
+#ifdef CONFIG_IEEE80211W
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		capab |= WPA_CAPABILITY_MFPC;
+		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+			capab |= WPA_CAPABILITY_MFPR;
+	}
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing)
+		capab |= BIT(8) | BIT(14) | BIT(15);
+#endif /* CONFIG_RSN_TESTING */
+	WPA_PUT_LE16(pos, capab);
+	pos += 2;
+
+	if (pmkid) {
+		if (pos + 2 + PMKID_LEN > buf + len)
+			return -1;
+		/* PMKID Count */
+		WPA_PUT_LE16(pos, 1);
+		pos += 2;
+		os_memcpy(pos, pmkid, PMKID_LEN);
+		pos += PMKID_LEN;
+	}
+
+#ifdef CONFIG_IEEE80211W
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		if (pos + 2 + 4 > buf + len)
+			return -1;
+		if (pmkid == NULL) {
+			/* PMKID Count */
+			WPA_PUT_LE16(pos, 0);
+			pos += 2;
+		}
+
+		/* Management Group Cipher Suite */
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+		pos += RSN_SELECTOR_LEN;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		/*
+		 * Fill in any defined fields and add extra data to the end of
+		 * the element.
+		 */
+		int pmkid_count_set = pmkid != NULL;
+		if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+			pmkid_count_set = 1;
+		/* PMKID Count */
+		WPA_PUT_LE16(pos, 0);
+		pos += 2;
+		if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+			/* Management Group Cipher Suite */
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+			pos += RSN_SELECTOR_LEN;
+		}
+
+		os_memset(pos, 0x12, 17);
+		pos += 17;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	hdr->len = (pos - buf) - 2;
+
+	return pos - buf;
+}
+
+
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
+{
+	u8 *pos, buf[128];
+	int res;
+
+	pos = buf;
+
+	if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
+		res = wpa_write_rsn_ie(&wpa_auth->conf,
+				       pos, buf + sizeof(buf) - pos, NULL);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (wpa_auth->conf.wpa_key_mgmt &
+	    (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) {
+		res = wpa_write_mdie(&wpa_auth->conf, pos,
+				     buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+#endif /* CONFIG_IEEE80211R */
+	if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
+		res = wpa_write_wpa_ie(&wpa_auth->conf,
+				       pos, buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+
+	os_free(wpa_auth->wpa_ie);
+	wpa_auth->wpa_ie = os_malloc(pos - buf);
+	if (wpa_auth->wpa_ie == NULL)
+		return -1;
+	os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
+	wpa_auth->wpa_ie_len = pos - buf;
+
+	return 0;
+}
+
+
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+		 const u8 *data2, size_t data2_len)
+{
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = RSN_SELECTOR_LEN + data_len + data2_len;
+	RSN_SELECTOR_PUT(pos, kde);
+	pos += RSN_SELECTOR_LEN;
+	os_memcpy(pos, data, data_len);
+	pos += data_len;
+	if (data2) {
+		os_memcpy(pos, data2, data2_len);
+		pos += data2_len;
+	}
+	return pos;
+}
+
+
+struct wpa_auth_okc_iter_data {
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *aa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+
+static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_okc_iter_data *data = ctx;
+	data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa,
+					  data->pmkid);
+	if (data->pmksa)
+		return 1;
+	return 0;
+}
+
+
+int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+			struct wpa_state_machine *sm,
+			const u8 *wpa_ie, size_t wpa_ie_len,
+			const u8 *mdie, size_t mdie_len)
+{
+	struct wpa_ie_data data;
+	int ciphers, key_mgmt, res, version;
+	u32 selector;
+	size_t i;
+	const u8 *pmkid = NULL;
+
+	if (wpa_auth == NULL || sm == NULL)
+		return WPA_NOT_ENABLED;
+
+	if (wpa_ie == NULL || wpa_ie_len < 1)
+		return WPA_INVALID_IE;
+
+	if (wpa_ie[0] == WLAN_EID_RSN)
+		version = WPA_PROTO_RSN;
+	else
+		version = WPA_PROTO_WPA;
+
+	if (!(wpa_auth->conf.wpa & version)) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR,
+			   version, MAC2STR(sm->addr));
+		return WPA_INVALID_PROTO;
+	}
+
+	if (version == WPA_PROTO_RSN) {
+		res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+
+		selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		if (0) {
+		}
+#ifdef CONFIG_IEEE80211R
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+			selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
+			selector = RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+			selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+		selector = RSN_CIPHER_SUITE_CCMP;
+		if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+			selector = RSN_CIPHER_SUITE_CCMP;
+		else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
+			selector = RSN_CIPHER_SUITE_TKIP;
+		else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
+			selector = RSN_CIPHER_SUITE_WEP104;
+		else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
+			selector = RSN_CIPHER_SUITE_WEP40;
+		else if (data.pairwise_cipher & WPA_CIPHER_NONE)
+			selector = RSN_CIPHER_SUITE_NONE;
+		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+		selector = RSN_CIPHER_SUITE_CCMP;
+		if (data.group_cipher & WPA_CIPHER_CCMP)
+			selector = RSN_CIPHER_SUITE_CCMP;
+		else if (data.group_cipher & WPA_CIPHER_TKIP)
+			selector = RSN_CIPHER_SUITE_TKIP;
+		else if (data.group_cipher & WPA_CIPHER_WEP104)
+			selector = RSN_CIPHER_SUITE_WEP104;
+		else if (data.group_cipher & WPA_CIPHER_WEP40)
+			selector = RSN_CIPHER_SUITE_WEP40;
+		else if (data.group_cipher & WPA_CIPHER_NONE)
+			selector = RSN_CIPHER_SUITE_NONE;
+		wpa_auth->dot11RSNAGroupCipherSelected = selector;
+	} else {
+		res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
+
+		selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+			selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+		selector = WPA_CIPHER_SUITE_TKIP;
+		if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+			selector = WPA_CIPHER_SUITE_CCMP;
+		else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
+			selector = WPA_CIPHER_SUITE_TKIP;
+		else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
+			selector = WPA_CIPHER_SUITE_WEP104;
+		else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
+			selector = WPA_CIPHER_SUITE_WEP40;
+		else if (data.pairwise_cipher & WPA_CIPHER_NONE)
+			selector = WPA_CIPHER_SUITE_NONE;
+		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+		selector = WPA_CIPHER_SUITE_TKIP;
+		if (data.group_cipher & WPA_CIPHER_CCMP)
+			selector = WPA_CIPHER_SUITE_CCMP;
+		else if (data.group_cipher & WPA_CIPHER_TKIP)
+			selector = WPA_CIPHER_SUITE_TKIP;
+		else if (data.group_cipher & WPA_CIPHER_WEP104)
+			selector = WPA_CIPHER_SUITE_WEP104;
+		else if (data.group_cipher & WPA_CIPHER_WEP40)
+			selector = WPA_CIPHER_SUITE_WEP40;
+		else if (data.group_cipher & WPA_CIPHER_NONE)
+			selector = WPA_CIPHER_SUITE_NONE;
+		wpa_auth->dot11RSNAGroupCipherSelected = selector;
+	}
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from "
+			   MACSTR " (res=%d)", MAC2STR(sm->addr), res);
+		wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len);
+		return WPA_INVALID_IE;
+	}
+
+	if (data.group_cipher != wpa_auth->conf.wpa_group) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from "
+			   MACSTR, data.group_cipher, MAC2STR(sm->addr));
+		return WPA_INVALID_GROUP;
+	}
+
+	key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+	if (!key_mgmt) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
+			   MACSTR, data.key_mgmt, MAC2STR(sm->addr));
+		return WPA_INVALID_AKMP;
+	}
+	if (0) {
+	}
+#ifdef CONFIG_IEEE80211R
+	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+	else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+	else
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+	if (version == WPA_PROTO_RSN)
+		ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+	else
+		ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
+	if (!ciphers) {
+		wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) "
+			   "from " MACSTR,
+			   version == WPA_PROTO_RSN ? "RSN" : "WPA",
+			   data.pairwise_cipher, MAC2STR(sm->addr));
+		return WPA_INVALID_PAIRWISE;
+	}
+
+#ifdef CONFIG_IEEE80211W
+	if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+		if (!(data.capabilities & WPA_CAPABILITY_MFPC)) {
+			wpa_printf(MSG_DEBUG, "Management frame protection "
+				   "required, but client did not enable it");
+			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		}
+
+		if (ciphers & WPA_CIPHER_TKIP) {
+			wpa_printf(MSG_DEBUG, "Management frame protection "
+				   "cannot use TKIP");
+			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		}
+
+		if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+			wpa_printf(MSG_DEBUG, "Unsupported management group "
+				   "cipher %d", data.mgmt_group_cipher);
+			return WPA_INVALID_MGMT_GROUP_CIPHER;
+		}
+	}
+
+	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+	    !(data.capabilities & WPA_CAPABILITY_MFPC))
+		sm->mgmt_frame_prot = 0;
+	else
+		sm->mgmt_frame_prot = 1;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
+			wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
+				   "MDIE not included");
+			return WPA_INVALID_MDIE;
+		}
+		if (os_memcmp(mdie, wpa_auth->conf.mobility_domain,
+			      MOBILITY_DOMAIN_ID_LEN) != 0) {
+			wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown "
+				    "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
+			return WPA_INVALID_MDIE;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (ciphers & WPA_CIPHER_CCMP)
+		sm->pairwise = WPA_CIPHER_CCMP;
+	else
+		sm->pairwise = WPA_CIPHER_TKIP;
+
+	/* TODO: clear WPA/WPA2 state if STA changes from one to another */
+	if (wpa_ie[0] == WLAN_EID_RSN)
+		sm->wpa = WPA_VERSION_WPA2;
+	else
+		sm->wpa = WPA_VERSION_WPA;
+
+	sm->pmksa = NULL;
+	for (i = 0; i < data.num_pmkid; i++) {
+		wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
+			    &data.pmkid[i * PMKID_LEN], PMKID_LEN);
+		sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+						 &data.pmkid[i * PMKID_LEN]);
+		if (sm->pmksa) {
+			pmkid = sm->pmksa->pmkid;
+			break;
+		}
+	}
+	for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc &&
+		     i < data.num_pmkid; i++) {
+		struct wpa_auth_okc_iter_data idata;
+		idata.pmksa = NULL;
+		idata.aa = wpa_auth->addr;
+		idata.spa = sm->addr;
+		idata.pmkid = &data.pmkid[i * PMKID_LEN];
+		wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata);
+		if (idata.pmksa) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "OKC match for PMKID");
+			sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa,
+							idata.pmksa,
+							wpa_auth->addr,
+							idata.pmkid);
+			pmkid = idata.pmkid;
+			break;
+		}
+	}
+	if (sm->pmksa) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "PMKID found from PMKSA cache "
+				 "eap_type=%d vlan_id=%d",
+				 sm->pmksa->eap_type_authsrv,
+				 sm->pmksa->vlan_id);
+		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
+	}
+
+	if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
+		os_free(sm->wpa_ie);
+		sm->wpa_ie = os_malloc(wpa_ie_len);
+		if (sm->wpa_ie == NULL)
+			return WPA_ALLOC_FAIL;
+	}
+	os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
+	sm->wpa_ie_len = wpa_ie_len;
+
+	return WPA_IE_OK;
+}
+
+
+/**
+ * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+			     struct wpa_eapol_ie_parse *ie)
+{
+	if (pos[1] == 0)
+		return 1;
+
+	if (pos[1] >= 6 &&
+	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
+	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+		ie->wpa_ie = pos;
+		ie->wpa_ie_len = pos[1] + 2;
+		return 0;
+	}
+
+	if (pos + 1 + RSN_SELECTOR_LEN < end &&
+	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+#ifdef CONFIG_PEERKEY
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+		ie->error = pos + 2 + RSN_SELECTOR_LEN;
+		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211W
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+		return 0;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	return 0;
+}
+
+
+/**
+ * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
+{
+	const u8 *pos, *end;
+	int ret = 0;
+
+	os_memset(ie, 0, sizeof(*ie));
+	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+		if (pos[0] == 0xdd &&
+		    ((pos == buf + len - 1) || pos[1] == 0)) {
+			/* Ignore padding */
+			break;
+		}
+		if (pos + 2 + pos[1] > end) {
+			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+				   "underflow (ie=%d len=%d pos=%d)",
+				   pos[0], pos[1], (int) (pos - buf));
+			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
+					buf, len);
+			ret = -1;
+			break;
+		}
+		if (*pos == WLAN_EID_RSN) {
+			ie->rsn_ie = pos;
+			ie->rsn_ie_len = pos[1] + 2;
+#ifdef CONFIG_IEEE80211R
+		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+			ie->mdie = pos;
+			ie->mdie_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+			ie->ftie = pos;
+			ie->ftie_len = pos[1] + 2;
+#endif /* CONFIG_IEEE80211R */
+		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+			ret = wpa_parse_generic(pos, end, ie);
+			if (ret < 0)
+				break;
+			if (ret > 0) {
+				ret = 0;
+				break;
+			}
+		} else {
+			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+				    "Key Data IE", pos, 2 + pos[1]);
+		}
+	}
+
+	return ret;
+}
+
+
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
+{
+	return sm ? sm->mgmt_frame_prot : 0;
+}
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
new file mode 100644
index 0000000..61d4cb4
--- /dev/null
+++ b/src/ap/wpa_auth_ie.h
@@ -0,0 +1,56 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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 WPA_AUTH_IE_H
+#define WPA_AUTH_IE_H
+
+struct wpa_eapol_ie_parse {
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+	const u8 *rsn_ie;
+	size_t rsn_ie_len;
+	const u8 *pmkid;
+	const u8 *gtk;
+	size_t gtk_len;
+	const u8 *mac_addr;
+	size_t mac_addr_len;
+#ifdef CONFIG_PEERKEY
+	const u8 *smk;
+	size_t smk_len;
+	const u8 *nonce;
+	size_t nonce_len;
+	const u8 *lifetime;
+	size_t lifetime_len;
+	const u8 *error;
+	size_t error_len;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+	const u8 *igtk;
+	size_t igtk_len;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+	const u8 *mdie;
+	size_t mdie_len;
+	const u8 *ftie;
+	size_t ftie_len;
+#endif /* CONFIG_IEEE80211R */
+};
+
+int wpa_parse_kde_ies(const u8 *buf, size_t len,
+		      struct wpa_eapol_ie_parse *ie);
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+		 const u8 *data2, size_t data2_len);
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth);
+
+#endif /* WPA_AUTH_IE_H */
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
new file mode 100644
index 0000000..fcbd89b
--- /dev/null
+++ b/src/ap/wps_hostapd.c
@@ -0,0 +1,1380 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "crypto/dh_groups.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_dev_attr.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+
+
+#ifdef CONFIG_WPS_UPNP
+#include "wps/wps_upnp.h"
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+				 struct wps_context *wps);
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_WPS_UPNP */
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
+				    const u8 *ie, size_t ie_len);
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+
+
+struct wps_for_each_data {
+	int (*func)(struct hostapd_data *h, void *ctx);
+	void *ctx;
+};
+
+
+static int wps_for_each(struct hostapd_iface *iface, void *ctx)
+{
+	struct wps_for_each_data *data = ctx;
+	size_t j;
+
+	if (iface == NULL)
+		return 0;
+	for (j = 0; j < iface->num_bss; j++) {
+		struct hostapd_data *hapd = iface->bss[j];
+		int ret = data->func(hapd, data->ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wps_for_each(struct hostapd_data *hapd,
+				int (*func)(struct hostapd_data *h, void *ctx),
+				void *ctx)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct wps_for_each_data data;
+	data.func = func;
+	data.ctx = ctx;
+	if (iface->for_each_interface == NULL)
+		return wps_for_each(iface, &data);
+	return iface->for_each_interface(iface->interfaces, wps_for_each,
+					 &data);
+}
+
+
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+				  size_t psk_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct hostapd_wpa_psk *p;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA "
+		   MACSTR, MAC2STR(mac_addr));
+	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+	if (psk_len != PMK_LEN) {
+		wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
+			   (unsigned long) psk_len);
+		return -1;
+	}
+
+	/* Add the new PSK to runtime PSK list */
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+	os_memcpy(p->addr, mac_addr, ETH_ALEN);
+	os_memcpy(p->psk, psk, PMK_LEN);
+
+	p->next = ssid->wpa_psk;
+	ssid->wpa_psk = p;
+
+	if (ssid->wpa_psk_file) {
+		FILE *f;
+		char hex[PMK_LEN * 2 + 1];
+		/* Add the new PSK to PSK list file */
+		f = fopen(ssid->wpa_psk_file, "a");
+		if (f == NULL) {
+			wpa_printf(MSG_DEBUG, "Failed to add the PSK to "
+				   "'%s'", ssid->wpa_psk_file);
+			return -1;
+		}
+
+		wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
+		fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex);
+		fclose(f);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
+				 struct wpabuf *probe_resp_ie)
+{
+	struct hostapd_data *hapd = ctx;
+	wpabuf_free(hapd->wps_beacon_ie);
+	hapd->wps_beacon_ie = beacon_ie;
+	wpabuf_free(hapd->wps_probe_resp_ie);
+	hapd->wps_probe_resp_ie = probe_resp_ie;
+	ieee802_11_set_beacon(hapd);
+	return hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+				      const struct wps_device_data *dev)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40], txt[400];
+	int len;
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
+	len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
+			  "%s " MACSTR " [%s|%s|%s|%s|%s|%s]",
+			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
+			  dev->manufacturer, dev->model_name,
+			  dev->model_number, dev->serial_number,
+			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					       sizeof(devtype)));
+	if (len > 0 && len < (int) sizeof(txt))
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
+
+	if (hapd->conf->wps_pin_requests) {
+		FILE *f;
+		struct os_time t;
+		f = fopen(hapd->conf->wps_pin_requests, "a");
+		if (f == NULL)
+			return;
+		os_get_time(&t);
+		fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
+			"\t%s\n",
+			t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
+			dev->manufacturer, dev->model_name, dev->model_number,
+			dev->serial_number,
+			wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					     sizeof(devtype)));
+		fclose(f);
+	}
+}
+
+
+static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
+				       const u8 *uuid_e)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
+		MAC2STR(mac_addr), uuid);
+	if (hapd->wps_reg_success_cb)
+		hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
+					 mac_addr, uuid_e);
+}
+
+
+static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
+					 const u8 *uuid_e,
+					 const u8 *pri_dev_type,
+					 u16 config_methods,
+					 u16 dev_password_id, u8 request_type,
+					 const char *dev_name)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40];
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	if (dev_name == NULL)
+		dev_name = "";
+	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
+		     " %s %s 0x%x %u %u [%s]",
+		     MAC2STR(addr), uuid,
+		     wps_dev_type_bin2str(pri_dev_type, devtype,
+					  sizeof(devtype)),
+		     config_methods, dev_password_id, request_type, dev_name);
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+	return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+static void wps_reload_config(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_iface *iface = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
+	if (iface->reload_config(iface) < 0) {
+		wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
+			   "configuration");
+	}
+}
+
+
+static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
+{
+	const struct wps_credential *cred = ctx;
+	FILE *oconf, *nconf;
+	size_t len, i;
+	char *tmp_fname;
+	char buf[1024];
+	int multi_bss;
+	int wpa;
+
+	if (hapd->wps == NULL)
+		return 0;
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+			cred->cred_attr, cred->cred_attr_len);
+
+	wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
+	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
+		   cred->auth_type);
+	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
+	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+			cred->key, cred->key_len);
+	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
+		   MAC2STR(cred->mac_addr));
+
+	if ((hapd->conf->wps_cred_processing == 1 ||
+	     hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
+		size_t blen = cred->cred_attr_len * 2 + 1;
+		char *_buf = os_malloc(blen);
+		if (_buf) {
+			wpa_snprintf_hex(_buf, blen,
+					 cred->cred_attr, cred->cred_attr_len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s",
+				WPS_EVENT_NEW_AP_SETTINGS, _buf);
+			os_free(_buf);
+		}
+	} else
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
+
+	if (hapd->conf->wps_cred_processing == 1)
+		return 0;
+
+	os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
+	hapd->wps->ssid_len = cred->ssid_len;
+	hapd->wps->encr_types = cred->encr_type;
+	hapd->wps->auth_types = cred->auth_type;
+	if (cred->key_len == 0) {
+		os_free(hapd->wps->network_key);
+		hapd->wps->network_key = NULL;
+		hapd->wps->network_key_len = 0;
+	} else {
+		if (hapd->wps->network_key == NULL ||
+		    hapd->wps->network_key_len < cred->key_len) {
+			hapd->wps->network_key_len = 0;
+			os_free(hapd->wps->network_key);
+			hapd->wps->network_key = os_malloc(cred->key_len);
+			if (hapd->wps->network_key == NULL)
+				return -1;
+		}
+		hapd->wps->network_key_len = cred->key_len;
+		os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
+	}
+	hapd->wps->wps_state = WPS_STATE_CONFIGURED;
+
+	len = os_strlen(hapd->iface->config_fname) + 5;
+	tmp_fname = os_malloc(len);
+	if (tmp_fname == NULL)
+		return -1;
+	os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
+
+	oconf = fopen(hapd->iface->config_fname, "r");
+	if (oconf == NULL) {
+		wpa_printf(MSG_WARNING, "WPS: Could not open current "
+			   "configuration file");
+		os_free(tmp_fname);
+		return -1;
+	}
+
+	nconf = fopen(tmp_fname, "w");
+	if (nconf == NULL) {
+		wpa_printf(MSG_WARNING, "WPS: Could not write updated "
+			   "configuration file");
+		os_free(tmp_fname);
+		fclose(oconf);
+		return -1;
+	}
+
+	fprintf(nconf, "# WPS configuration - START\n");
+
+	fprintf(nconf, "wps_state=2\n");
+
+	fprintf(nconf, "ssid=");
+	for (i = 0; i < cred->ssid_len; i++)
+		fputc(cred->ssid[i], nconf);
+	fprintf(nconf, "\n");
+
+	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+		wpa = 3;
+	else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+		wpa = 2;
+	else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+		wpa = 1;
+	else
+		wpa = 0;
+
+	if (wpa) {
+		char *prefix;
+		fprintf(nconf, "wpa=%d\n", wpa);
+
+		fprintf(nconf, "wpa_key_mgmt=");
+		prefix = "";
+		if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
+			fprintf(nconf, "WPA-EAP");
+			prefix = " ";
+		}
+		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+			fprintf(nconf, "%sWPA-PSK", prefix);
+		fprintf(nconf, "\n");
+
+		fprintf(nconf, "wpa_pairwise=");
+		prefix = "";
+		if (cred->encr_type & WPS_ENCR_AES) {
+			fprintf(nconf, "CCMP");
+			prefix = " ";
+		}
+		if (cred->encr_type & WPS_ENCR_TKIP) {
+			fprintf(nconf, "%sTKIP", prefix);
+		}
+		fprintf(nconf, "\n");
+
+		if (cred->key_len >= 8 && cred->key_len < 64) {
+			fprintf(nconf, "wpa_passphrase=");
+			for (i = 0; i < cred->key_len; i++)
+				fputc(cred->key[i], nconf);
+			fprintf(nconf, "\n");
+		} else if (cred->key_len == 64) {
+			fprintf(nconf, "wpa_psk=");
+			for (i = 0; i < cred->key_len; i++)
+				fputc(cred->key[i], nconf);
+			fprintf(nconf, "\n");
+		} else {
+			wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
+				   "for WPA/WPA2",
+				   (unsigned long) cred->key_len);
+		}
+
+		fprintf(nconf, "auth_algs=1\n");
+	} else {
+		if ((cred->auth_type & WPS_AUTH_OPEN) &&
+		    (cred->auth_type & WPS_AUTH_SHARED))
+			fprintf(nconf, "auth_algs=3\n");
+		else if (cred->auth_type & WPS_AUTH_SHARED)
+			fprintf(nconf, "auth_algs=2\n");
+		else
+			fprintf(nconf, "auth_algs=1\n");
+
+		if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) {
+			int key_idx = cred->key_idx;
+			if (key_idx)
+				key_idx--;
+			fprintf(nconf, "wep_default_key=%d\n", key_idx);
+			fprintf(nconf, "wep_key%d=", key_idx);
+			if (cred->key_len == 10 || cred->key_len == 26) {
+				/* WEP key as a hex string */
+				for (i = 0; i < cred->key_len; i++)
+					fputc(cred->key[i], nconf);
+			} else {
+				/* Raw WEP key; convert to hex */
+				for (i = 0; i < cred->key_len; i++)
+					fprintf(nconf, "%02x", cred->key[i]);
+			}
+			fprintf(nconf, "\n");
+		}
+	}
+
+	fprintf(nconf, "# WPS configuration - END\n");
+
+	multi_bss = 0;
+	while (fgets(buf, sizeof(buf), oconf)) {
+		if (os_strncmp(buf, "bss=", 4) == 0)
+			multi_bss = 1;
+		if (!multi_bss &&
+		    (str_starts(buf, "ssid=") ||
+		     str_starts(buf, "auth_algs=") ||
+		     str_starts(buf, "wep_default_key=") ||
+		     str_starts(buf, "wep_key") ||
+		     str_starts(buf, "wps_state=") ||
+		     str_starts(buf, "wpa=") ||
+		     str_starts(buf, "wpa_psk=") ||
+		     str_starts(buf, "wpa_pairwise=") ||
+		     str_starts(buf, "rsn_pairwise=") ||
+		     str_starts(buf, "wpa_key_mgmt=") ||
+		     str_starts(buf, "wpa_passphrase="))) {
+			fprintf(nconf, "#WPS# %s", buf);
+		} else
+			fprintf(nconf, "%s", buf);
+	}
+
+	fclose(nconf);
+	fclose(oconf);
+
+	if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
+		wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
+			   "configuration file: %s", strerror(errno));
+		os_free(tmp_fname);
+		return -1;
+	}
+
+	os_free(tmp_fname);
+
+	/* Schedule configuration reload after short period of time to allow
+	 * EAP-WSC to be finished.
+	 */
+	eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+			       NULL);
+
+	wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
+
+	return 0;
+}
+
+
+static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
+}
+
+
+static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	if (hapd->conf->ap_setup_locked)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+	hapd->wps->ap_setup_locked = 0;
+	wps_registrar_update_ie(hapd->wps->registrar);
+}
+
+
+static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_event_pwd_auth_fail *data = ctx;
+
+	if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+		return 0;
+
+	/*
+	 * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
+	 * for some time if this happens multiple times to slow down brute
+	 * force attacks.
+	 */
+	hapd->ap_pin_failures++;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
+		   hapd->ap_pin_failures);
+	if (hapd->ap_pin_failures < 3)
+		return 0;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
+	hapd->wps->ap_setup_locked = 1;
+
+	wps_registrar_update_ie(hapd->wps->registrar);
+
+	if (!hapd->conf->ap_setup_locked) {
+		if (hapd->ap_pin_lockout_time == 0)
+			hapd->ap_pin_lockout_time = 60;
+		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+			 (hapd->ap_pin_failures % 3) == 0)
+			hapd->ap_pin_lockout_time *= 2;
+
+		wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
+			   hapd->ap_pin_lockout_time);
+		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+		eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
+				       hostapd_wps_reenable_ap_pin, hapd,
+				       NULL);
+	}
+
+	return 0;
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+				  struct wps_event_pwd_auth_fail *data)
+{
+	hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
+}
+
+
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+	"No Error", /* WPS_EI_NO_ERROR */
+	"TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+	"WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+				   struct wps_event_fail *fail)
+{
+	if (fail->error_indication > 0 &&
+	    fail->error_indication < NUM_WPS_EI_VALUES) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+			fail->msg, fail->config_error, fail->error_indication,
+			wps_event_fail_reason[fail->error_indication]);
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d",
+			fail->msg, fail->config_error);
+	}
+}
+
+
+static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
+				 union wps_event_data *data)
+{
+	struct hostapd_data *hapd = ctx;
+
+	switch (event) {
+	case WPS_EV_M2D:
+		break;
+	case WPS_EV_FAIL:
+		hostapd_wps_event_fail(hapd, &data->fail);
+		break;
+	case WPS_EV_SUCCESS:
+		break;
+	case WPS_EV_PWD_AUTH_FAIL:
+		hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
+		break;
+	case WPS_EV_PBC_OVERLAP:
+		break;
+	case WPS_EV_PBC_TIMEOUT:
+		break;
+	case WPS_EV_ER_AP_ADD:
+		break;
+	case WPS_EV_ER_AP_REMOVE:
+		break;
+	case WPS_EV_ER_ENROLLEE_ADD:
+		break;
+	case WPS_EV_ER_ENROLLEE_REMOVE:
+		break;
+	case WPS_EV_ER_AP_SETTINGS:
+		break;
+	case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+		break;
+	}
+	if (hapd->wps_event_cb)
+		hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
+}
+
+
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
+{
+	wpabuf_free(hapd->wps_beacon_ie);
+	hapd->wps_beacon_ie = NULL;
+
+	wpabuf_free(hapd->wps_probe_resp_ie);
+	hapd->wps_probe_resp_ie = NULL;
+
+	hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
+{
+	const u8 **uuid = ctx;
+	size_t j;
+
+	if (iface == NULL)
+		return 0;
+	for (j = 0; j < iface->num_bss; j++) {
+		struct hostapd_data *hapd = iface->bss[j];
+		if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) {
+			*uuid = hapd->wps->uuid;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static const u8 * get_own_uuid(struct hostapd_iface *iface)
+{
+	const u8 *uuid;
+	if (iface->for_each_interface == NULL)
+		return NULL;
+	uuid = NULL;
+	iface->for_each_interface(iface->interfaces, get_uuid_cb, &uuid);
+	return uuid;
+}
+
+
+static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
+{
+	int *count= ctx;
+	(*count)++;
+	return 0;
+}
+
+
+static int interface_count(struct hostapd_iface *iface)
+{
+	int count = 0;
+	if (iface->for_each_interface == NULL)
+		return 0;
+	iface->for_each_interface(iface->interfaces, count_interface_cb,
+				  &count);
+	return count;
+}
+
+
+static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
+				      struct wps_context *wps)
+{
+	int i;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+		wpabuf_free(wps->dev.vendor_ext[i]);
+		wps->dev.vendor_ext[i] = NULL;
+
+		if (hapd->conf->wps_vendor_ext[i] == NULL)
+			continue;
+
+		wps->dev.vendor_ext[i] =
+			wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
+		if (wps->dev.vendor_ext[i] == NULL) {
+			while (--i >= 0)
+				wpabuf_free(wps->dev.vendor_ext[i]);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+		     struct hostapd_bss_config *conf)
+{
+	struct wps_context *wps;
+	struct wps_registrar_config cfg;
+
+	if (conf->wps_state == 0) {
+		hostapd_wps_clear_ies(hapd);
+		return 0;
+	}
+
+	wps = os_zalloc(sizeof(*wps));
+	if (wps == NULL)
+		return -1;
+
+	wps->cred_cb = hostapd_wps_cred_cb;
+	wps->event_cb = hostapd_wps_event_cb;
+	wps->cb_ctx = hapd;
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	wps->wps_state = hapd->conf->wps_state;
+	wps->ap_setup_locked = hapd->conf->ap_setup_locked;
+	if (is_nil_uuid(hapd->conf->uuid)) {
+		const u8 *uuid;
+		uuid = get_own_uuid(hapd->iface);
+		if (uuid) {
+			os_memcpy(wps->uuid, uuid, UUID_LEN);
+			wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
+				    "interface", wps->uuid, UUID_LEN);
+		} else {
+			uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
+			wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+				    "address", wps->uuid, UUID_LEN);
+		}
+	} else {
+		os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+		wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
+			    wps->uuid, UUID_LEN);
+	}
+	wps->ssid_len = hapd->conf->ssid.ssid_len;
+	os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
+	wps->ap = 1;
+	os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
+	wps->dev.device_name = hapd->conf->device_name ?
+		os_strdup(hapd->conf->device_name) : NULL;
+	wps->dev.manufacturer = hapd->conf->manufacturer ?
+		os_strdup(hapd->conf->manufacturer) : NULL;
+	wps->dev.model_name = hapd->conf->model_name ?
+		os_strdup(hapd->conf->model_name) : NULL;
+	wps->dev.model_number = hapd->conf->model_number ?
+		os_strdup(hapd->conf->model_number) : NULL;
+	wps->dev.serial_number = hapd->conf->serial_number ?
+		os_strdup(hapd->conf->serial_number) : NULL;
+	wps->config_methods =
+		wps_config_methods_str2bin(hapd->conf->config_methods);
+#ifdef CONFIG_WPS2
+	if ((wps->config_methods &
+	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+		wpa_printf(MSG_INFO, "WPS: Converting display to "
+			   "virtual_display for WPS 2.0 compliance");
+		wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+	}
+	if ((wps->config_methods &
+	     (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+	      WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+		wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+			   "virtual_push_button for WPS 2.0 compliance");
+		wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+	}
+#endif /* CONFIG_WPS2 */
+	os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+		  WPS_DEV_TYPE_LEN);
+
+	if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
+		os_free(wps);
+		return -1;
+	}
+
+	wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
+	wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+		WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+
+	if (conf->wpa & WPA_PROTO_RSN) {
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+			wps->auth_types |= WPS_AUTH_WPA2PSK;
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			wps->auth_types |= WPS_AUTH_WPA2;
+
+		if (conf->rsn_pairwise & WPA_CIPHER_CCMP)
+			wps->encr_types |= WPS_ENCR_AES;
+		if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
+			wps->encr_types |= WPS_ENCR_TKIP;
+	}
+
+	if (conf->wpa & WPA_PROTO_WPA) {
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+			wps->auth_types |= WPS_AUTH_WPAPSK;
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			wps->auth_types |= WPS_AUTH_WPA;
+
+		if (conf->wpa_pairwise & WPA_CIPHER_CCMP)
+			wps->encr_types |= WPS_ENCR_AES;
+		if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
+			wps->encr_types |= WPS_ENCR_TKIP;
+	}
+
+	if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
+		wps->encr_types |= WPS_ENCR_NONE;
+		wps->auth_types |= WPS_AUTH_OPEN;
+	} else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) {
+		wps->encr_types |= WPS_ENCR_WEP;
+		if (conf->auth_algs & WPA_AUTH_ALG_OPEN)
+			wps->auth_types |= WPS_AUTH_OPEN;
+		if (conf->auth_algs & WPA_AUTH_ALG_SHARED)
+			wps->auth_types |= WPS_AUTH_SHARED;
+	} else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) {
+		wps->auth_types |= WPS_AUTH_OPEN;
+		if (conf->default_wep_key_len)
+			wps->encr_types |= WPS_ENCR_WEP;
+		else
+			wps->encr_types |= WPS_ENCR_NONE;
+	}
+
+	if (conf->ssid.wpa_psk_file) {
+		/* Use per-device PSKs */
+	} else if (conf->ssid.wpa_passphrase) {
+		wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
+		wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
+	} else if (conf->ssid.wpa_psk) {
+		wps->network_key = os_malloc(2 * PMK_LEN + 1);
+		if (wps->network_key == NULL) {
+			os_free(wps);
+			return -1;
+		}
+		wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
+				 conf->ssid.wpa_psk->psk, PMK_LEN);
+		wps->network_key_len = 2 * PMK_LEN;
+	} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
+		wps->network_key = os_malloc(conf->ssid.wep.len[0]);
+		if (wps->network_key == NULL) {
+			os_free(wps);
+			return -1;
+		}
+		os_memcpy(wps->network_key, conf->ssid.wep.key[0],
+			  conf->ssid.wep.len[0]);
+		wps->network_key_len = conf->ssid.wep.len[0];
+	}
+
+	if (conf->ssid.wpa_psk) {
+		os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
+		wps->psk_set = 1;
+	}
+
+	if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
+		/* Override parameters to enable security by default */
+		wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+		wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+	}
+
+	wps->ap_settings = conf->ap_settings;
+	wps->ap_settings_len = conf->ap_settings_len;
+
+	cfg.new_psk_cb = hostapd_wps_new_psk_cb;
+	cfg.set_ie_cb = hostapd_wps_set_ie_cb;
+	cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
+	cfg.reg_success_cb = hostapd_wps_reg_success_cb;
+	cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
+	cfg.cb_ctx = hapd;
+	cfg.skip_cred_build = conf->skip_cred_build;
+	cfg.extra_cred = conf->extra_cred;
+	cfg.extra_cred_len = conf->extra_cred_len;
+	cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) &&
+		conf->skip_cred_build;
+	if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
+		cfg.static_wep_only = 1;
+	cfg.dualband = interface_count(hapd->iface) > 1;
+	if (cfg.dualband)
+		wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+
+	wps->registrar = wps_registrar_init(wps, &cfg);
+	if (wps->registrar == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
+		os_free(wps->network_key);
+		os_free(wps);
+		return -1;
+	}
+
+#ifdef CONFIG_WPS_UPNP
+	wps->friendly_name = hapd->conf->friendly_name;
+	wps->manufacturer_url = hapd->conf->manufacturer_url;
+	wps->model_description = hapd->conf->model_description;
+	wps->model_url = hapd->conf->model_url;
+	wps->upc = hapd->conf->upc;
+
+	if (hostapd_wps_upnp_init(hapd, wps) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
+		wps_registrar_deinit(wps->registrar);
+		os_free(wps->network_key);
+		os_free(wps);
+		return -1;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+	hapd->wps = wps;
+
+	return 0;
+}
+
+
+void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	if (hapd->wps == NULL)
+		return;
+#ifdef CONFIG_WPS_UPNP
+	hostapd_wps_upnp_deinit(hapd);
+#endif /* CONFIG_WPS_UPNP */
+	wps_registrar_deinit(hapd->wps->registrar);
+	os_free(hapd->wps->network_key);
+	wps_device_data_free(&hapd->wps->dev);
+	wpabuf_free(hapd->wps->dh_pubkey);
+	wpabuf_free(hapd->wps->dh_privkey);
+	wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
+	wpabuf_free(hapd->wps->oob_conf.dev_password);
+	wps_free_pending_msgs(hapd->wps->upnp_msgs);
+	os_free(hapd->wps);
+	hapd->wps = NULL;
+	hostapd_wps_clear_ies(hapd);
+}
+
+
+void hostapd_update_wps(struct hostapd_data *hapd)
+{
+	if (hapd->wps == NULL)
+		return;
+
+#ifdef CONFIG_WPS_UPNP
+	hapd->wps->friendly_name = hapd->conf->friendly_name;
+	hapd->wps->manufacturer_url = hapd->conf->manufacturer_url;
+	hapd->wps->model_description = hapd->conf->model_description;
+	hapd->wps->model_url = hapd->conf->model_url;
+	hapd->wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+	hostapd_wps_set_vendor_ext(hapd, hapd->wps);
+
+	if (hapd->conf->wps_state)
+		wps_registrar_update_ie(hapd->wps->registrar);
+	else
+		hostapd_deinit_wps(hapd);
+}
+
+
+struct wps_add_pin_data {
+	const u8 *addr;
+	const u8 *uuid;
+	const u8 *pin;
+	size_t pin_len;
+	int timeout;
+	int added;
+};
+
+
+static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_add_pin_data *data = ctx;
+	int ret;
+
+	if (hapd->wps == NULL)
+		return 0;
+	ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
+				    data->uuid, data->pin, data->pin_len,
+				    data->timeout);
+	if (ret == 0)
+		data->added++;
+	return ret;
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+			const char *uuid, const char *pin, int timeout)
+{
+	u8 u[UUID_LEN];
+	struct wps_add_pin_data data;
+
+	data.addr = addr;
+	data.uuid = u;
+	data.pin = (const u8 *) pin;
+	data.pin_len = os_strlen(pin);
+	data.timeout = timeout;
+	data.added = 0;
+
+	if (os_strcmp(uuid, "any") == 0)
+		data.uuid = NULL;
+	else {
+		if (uuid_str2bin(uuid, u))
+			return -1;
+		data.uuid = u;
+	}
+	if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
+		return -1;
+	return data.added ? 0 : -1;
+}
+
+
+static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
+{
+	const u8 *p2p_dev_addr = ctx;
+	if (hapd->wps == NULL)
+		return 0;
+	return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+			      const u8 *p2p_dev_addr)
+{
+	return hostapd_wps_for_each(hapd, wps_button_pushed,
+				    (void *) p2p_dev_addr);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+			  char *path, char *method, char *name)
+{
+	struct wps_context *wps = hapd->wps;
+	struct oob_device_data *oob_dev;
+
+	oob_dev = wps_get_oob_device(device_type);
+	if (oob_dev == NULL)
+		return -1;
+	oob_dev->device_path = path;
+	oob_dev->device_name = name;
+	wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+	if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) {
+		/*
+		 * Use pre-configured DH keys in order to be able to write the
+		 * key hash into the OOB file.
+		 */
+		wpabuf_free(wps->dh_pubkey);
+		wpabuf_free(wps->dh_privkey);
+		wps->dh_privkey = NULL;
+		wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
+					 &wps->dh_privkey);
+		wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+		if (wps->dh_pubkey == NULL) {
+			wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+				   "Diffie-Hellman handshake");
+			return -1;
+		}
+	}
+
+	if (wps_process_oob(wps, oob_dev, 1) < 0)
+		goto error;
+
+	if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+	     wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+	    hostapd_wps_add_pin(hapd, NULL, "any",
+				wpabuf_head(wps->oob_conf.dev_password), 0) <
+	    0)
+		goto error;
+
+	return 0;
+
+error:
+	wpabuf_free(wps->dh_pubkey);
+	wps->dh_pubkey = NULL;
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = NULL;
+	return -1;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
+				    const u8 *ie, size_t ie_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct wpabuf *wps_ie;
+	struct ieee802_11_elems elems;
+
+	if (hapd->wps == NULL)
+		return 0;
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
+			   MACSTR, MAC2STR(addr));
+		return 0;
+	}
+
+	if (elems.ssid && elems.ssid_len > 0 &&
+	    (elems.ssid_len != hapd->conf->ssid.ssid_len ||
+	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
+	     0))
+		return 0; /* Not for us */
+
+	wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+	if (wps_ie == NULL)
+		return 0;
+	if (wps_validate_probe_req(wps_ie, addr) < 0) {
+		wpabuf_free(wps_ie);
+		return 0;
+	}
+
+	if (wpabuf_len(wps_ie) > 0) {
+		int p2p_wildcard = 0;
+#ifdef CONFIG_P2P
+		if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+		    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+			      P2P_WILDCARD_SSID_LEN) == 0)
+			p2p_wildcard = 1;
+#endif /* CONFIG_P2P */
+		wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+					   p2p_wildcard);
+#ifdef CONFIG_WPS_UPNP
+		/* FIX: what exactly should be included in the WLANEvent?
+		 * WPS attributes? Full ProbeReq frame? */
+		if (!p2p_wildcard)
+			upnp_wps_device_send_wlan_event(
+				hapd->wps_upnp, addr,
+				UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
+#endif /* CONFIG_WPS_UPNP */
+	}
+
+	wpabuf_free(wps_ie);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+
+static int hostapd_rx_req_put_wlan_response(
+	void *priv, enum upnp_wps_wlanevent_type ev_type,
+	const u8 *mac_addr, const struct wpabuf *msg,
+	enum wps_msg_type msg_type)
+{
+	struct hostapd_data *hapd = priv;
+	struct sta_info *sta;
+	struct upnp_pending_message *p;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr="
+		   MACSTR, ev_type, MAC2STR(mac_addr));
+	wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage",
+		    wpabuf_head(msg), wpabuf_len(msg));
+	if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected "
+			   "PutWLANResponse WLANEventType %d", ev_type);
+		return -1;
+	}
+
+	/*
+	 * EAP response to ongoing to WPS Registration. Send it to EAP-WSC
+	 * server implementation for delivery to the peer.
+	 */
+
+	sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
+	if (!sta) {
+		/*
+		 * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
+		 * Pick STA that is in an ongoing WPS registration without
+		 * checking the MAC address.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based "
+			   "on NewWLANEventMAC; try wildcard match");
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS))
+				break;
+		}
+	}
+#endif /* CONFIG_WPS_STRICT */
+
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
+		return 0;
+	}
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+	os_memcpy(p->addr, sta->addr, ETH_ALEN);
+	p->msg = wpabuf_dup(msg);
+	p->type = msg_type;
+	p->next = hapd->wps->upnp_msgs;
+	hapd->wps->upnp_msgs = p;
+
+	return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap);
+}
+
+
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+				 struct wps_context *wps)
+{
+	struct upnp_wps_device_ctx *ctx;
+
+	if (!hapd->conf->upnp_iface)
+		return 0;
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return -1;
+
+	ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response;
+	if (hapd->conf->ap_pin)
+		ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
+
+	hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
+					      hapd->conf->upnp_iface);
+	if (hapd->wps_upnp == NULL)
+		return -1;
+	wps->wps_upnp = hapd->wps_upnp;
+
+	return 0;
+}
+
+
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
+{
+	upnp_wps_device_deinit(hapd->wps_upnp, hapd);
+}
+
+#endif /* CONFIG_WPS_UPNP */
+
+
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+			    char *buf, size_t buflen)
+{
+	if (hapd->wps == NULL)
+		return 0;
+	return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
+}
+
+
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+	hostapd_wps_ap_pin_disable(hapd);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+	hapd->ap_pin_failures = 0;
+	hapd->conf->ap_setup_locked = 0;
+	if (hapd->wps->ap_setup_locked) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+		hapd->wps->ap_setup_locked = 0;
+		wps_registrar_update_ie(hapd->wps->registrar);
+	}
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0,
+				       hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
+{
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = NULL;
+#ifdef CONFIG_WPS_UPNP
+	upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
+#endif /* CONFIG_WPS_UPNP */
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	return 0;
+}
+
+
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+	hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
+}
+
+
+struct wps_ap_pin_data {
+	char pin_txt[9];
+	int timeout;
+};
+
+
+static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_ap_pin_data *data = ctx;
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = os_strdup(data->pin_txt);
+#ifdef CONFIG_WPS_UPNP
+	upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
+#endif /* CONFIG_WPS_UPNP */
+	hostapd_wps_ap_pin_enable(hapd, data->timeout);
+	return 0;
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+	unsigned int pin;
+	struct wps_ap_pin_data data;
+
+	pin = wps_generate_pin();
+	os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%u", pin);
+	data.timeout = timeout;
+	hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+	return hapd->conf->ap_pin;
+}
+
+
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
+{
+	return hapd->conf->ap_pin;
+}
+
+
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+			   int timeout)
+{
+	struct wps_ap_pin_data data;
+	int ret;
+
+	ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
+	if (ret < 0 || ret >= (int) sizeof(data.pin_txt))
+		return -1;
+	data.timeout = timeout;
+	return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+}
+
+
+static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
+{
+	if (hapd->wps)
+		wps_registrar_update_ie(hapd->wps->registrar);
+	return 0;
+}
+
+
+void hostapd_wps_update_ie(struct hostapd_data *hapd)
+{
+	hostapd_wps_for_each(hapd, wps_update_ie, NULL);
+}
+
+
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+			  const char *auth, const char *encr, const char *key)
+{
+	struct wps_credential cred;
+	size_t len;
+
+	os_memset(&cred, 0, sizeof(cred));
+
+	len = os_strlen(ssid);
+	if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+	    hexstr2bin(ssid, cred.ssid, len / 2))
+		return -1;
+	cred.ssid_len = len / 2;
+
+	if (os_strncmp(auth, "OPEN", 4) == 0)
+		cred.auth_type = WPS_AUTH_OPEN;
+	else if (os_strncmp(auth, "WPAPSK", 6) == 0)
+		cred.auth_type = WPS_AUTH_WPAPSK;
+	else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
+		cred.auth_type = WPS_AUTH_WPA2PSK;
+	else
+		return -1;
+
+	if (encr) {
+		if (os_strncmp(encr, "NONE", 4) == 0)
+			cred.encr_type = WPS_ENCR_NONE;
+		else if (os_strncmp(encr, "WEP", 3) == 0)
+			cred.encr_type = WPS_ENCR_WEP;
+		else if (os_strncmp(encr, "TKIP", 4) == 0)
+			cred.encr_type = WPS_ENCR_TKIP;
+		else if (os_strncmp(encr, "CCMP", 4) == 0)
+			cred.encr_type = WPS_ENCR_AES;
+		else
+			return -1;
+	} else
+		cred.encr_type = WPS_ENCR_NONE;
+
+	if (key) {
+		len = os_strlen(key);
+		if ((len & 1) || len > 2 * sizeof(cred.key) ||
+		    hexstr2bin(key, cred.key, len / 2))
+			return -1;
+		cred.key_len = len / 2;
+	}
+
+	return wps_registrar_config_ap(hapd->wps->registrar, &cred);
+}
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
new file mode 100644
index 0000000..338a220
--- /dev/null
+++ b/src/ap/wps_hostapd.h
@@ -0,0 +1,72 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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 WPS_HOSTAPD_H
+#define WPS_HOSTAPD_H
+
+#ifdef CONFIG_WPS
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+		     struct hostapd_bss_config *conf);
+void hostapd_deinit_wps(struct hostapd_data *hapd);
+void hostapd_update_wps(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+			const char *uuid, const char *pin, int timeout);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+			      const u8 *p2p_dev_addr);
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+			  char *path, char *method, char *name);
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+			    char *buf, size_t buflen);
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+			   int timeout);
+void hostapd_wps_update_ie(struct hostapd_data *hapd);
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+			  const char *auth, const char *encr, const char *key);
+
+#else /* CONFIG_WPS */
+
+static inline int hostapd_init_wps(struct hostapd_data *hapd,
+				   struct hostapd_bss_config *conf)
+{
+	return 0;
+}
+
+static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_update_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
+					  const u8 *addr,
+					  char *buf, size_t buflen)
+{
+	return 0;
+}
+
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+					    const u8 *p2p_dev_addr)
+{
+	return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_HOSTAPD_H */