[PATCH 1/3] wpa: Import all files for KRACK patch.

Christian Mauderer christian.mauderer at embedded-brains.de
Wed Nov 8 14:10:46 UTC 2017


From: Christian Mauderer <Christian.Mauderer at embedded-brains.de>

---
 freebsd/contrib/wpa/src/ap/pmksa_cache_auth.h      |   67 +
 freebsd/contrib/wpa/src/ap/wmm.h                   |   23 +
 freebsd/contrib/wpa/src/ap/wpa_auth.c              | 3502 ++++++++++++++++++++
 freebsd/contrib/wpa/src/ap/wpa_auth.h              |  328 ++
 freebsd/contrib/wpa/src/ap/wpa_auth_ft.c           | 1794 ++++++++++
 freebsd/contrib/wpa/src/ap/wpa_auth_i.h            |  259 ++
 freebsd/contrib/wpa/src/ap/wpa_auth_ie.h           |   57 +
 freebsd/contrib/wpa/src/eapol_auth/eapol_auth_sm.h |  102 +
 freebsd/contrib/wpa/src/radius/radius.h            |  322 ++
 freebsd/contrib/wpa/src/rsn_supp/tdls.c            | 3010 +++++++++++++++++
 freebsd/contrib/wpa/src/utils/bitfield.h           |   21 +
 freebsd/contrib/wpa/wpa_supplicant/wnm_sta.c       | 1150 +++++++
 12 files changed, 10635 insertions(+)
 create mode 100644 freebsd/contrib/wpa/src/ap/pmksa_cache_auth.h
 create mode 100644 freebsd/contrib/wpa/src/ap/wmm.h
 create mode 100644 freebsd/contrib/wpa/src/ap/wpa_auth.c
 create mode 100644 freebsd/contrib/wpa/src/ap/wpa_auth.h
 create mode 100644 freebsd/contrib/wpa/src/ap/wpa_auth_ft.c
 create mode 100644 freebsd/contrib/wpa/src/ap/wpa_auth_i.h
 create mode 100644 freebsd/contrib/wpa/src/ap/wpa_auth_ie.h
 create mode 100644 freebsd/contrib/wpa/src/eapol_auth/eapol_auth_sm.h
 create mode 100644 freebsd/contrib/wpa/src/radius/radius.h
 create mode 100644 freebsd/contrib/wpa/src/rsn_supp/tdls.c
 create mode 100644 freebsd/contrib/wpa/src/utils/bitfield.h
 create mode 100644 freebsd/contrib/wpa/wpa_supplicant/wnm_sta.c

diff --git a/freebsd/contrib/wpa/src/ap/pmksa_cache_auth.h b/freebsd/contrib/wpa/src/ap/pmksa_cache_auth.h
new file mode 100644
index 00000000000..8b7be1291b5
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/pmksa_cache_auth.h
@@ -0,0 +1,67 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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 wpabuf *cui;
+	struct radius_class_data radius_class;
+	u8 eap_type_authsrv;
+	int vlan_id;
+	int opportunistic;
+
+	u32 acct_multi_session_id_hi;
+	u32 acct_multi_session_id_lo;
+};
+
+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 *kck, size_t kck_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);
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+			    struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+					   struct radius_das_attrs *attr);
+
+#endif /* PMKSA_CACHE_H */
diff --git a/freebsd/contrib/wpa/src/ap/wmm.h b/freebsd/contrib/wpa/src/ap/wmm.h
new file mode 100644
index 00000000000..b70b8636038
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/wmm.h
@@ -0,0 +1,23 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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/freebsd/contrib/wpa/src/ap/wpa_auth.c b/freebsd/contrib/wpa/src/ap/wpa_auth.c
new file mode 100644
index 00000000000..ed48bbf6818
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/wpa_auth.c
@@ -0,0 +1,3502 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * IEEE 802.11 RSN / WPA Authenticator
+ * Copyright (c) 2004-2015, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/state_machine.h"
+#include "utils/bitfield.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(int akmp, 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 int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+			  const u8 *pmk, struct wpa_ptk *ptk);
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+			   struct wpa_group *group);
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+static void wpa_group_put(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 */
+static const u32 eapol_key_timeout_first_group = 500; /* ms */
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+static const int dot11RSNAConfigPMKReauthThreshold = 70;
+static const int dot11RSNAConfigSATimeout = 60;
+
+
+static inline int wpa_auth_mic_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb.mic_failure_report)
+		return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+	return 0;
+}
+
+
+static inline void wpa_auth_psk_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb.psk_failure_report)
+		wpa_auth->cb.psk_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 *p2p_dev_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, p2p_dev_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);
+}
+
+
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr)
+{
+	if (wpa_auth->cb.start_ampe == NULL)
+		return -1;
+	return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
+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_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
+	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 */
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+		ret = 1;
+	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, *next;
+
+	wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+	group = wpa_auth->group;
+	while (group) {
+		wpa_group_get(wpa_auth, group);
+
+		group->GTKReKey = TRUE;
+		do {
+			group->changed = FALSE;
+			wpa_group_sm_step(wpa_auth, group);
+		} while (group->changed);
+
+		next = group->next;
+		wpa_group_put(wpa_auth, group);
+		group = next;
+	}
+
+	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 int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+					  struct wpa_group *group)
+{
+	u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
+	u8 rkey[32];
+	unsigned long ptr;
+
+	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);
+	ptr = (unsigned long) group;
+	os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
+	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, int delay_init)
+{
+	struct wpa_group *group;
+
+	group = os_zalloc(sizeof(struct wpa_group));
+	if (group == NULL)
+		return NULL;
+
+	group->GTKAuthenticator = TRUE;
+	group->vlan_id = vlan_id;
+	group->GTK_len = wpa_cipher_key_len(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;
+	if (delay_init) {
+		wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start "
+			   "until Beacon frames have been configured");
+		/* Initialization is completed in wpa_init_keys(). */
+	} else {
+		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, 1);
+	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->group);
+		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->group);
+		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);
+	}
+
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(conf->ip_addr_start)) {
+		int count = WPA_GET_BE32(conf->ip_addr_end) -
+			WPA_GET_BE32(conf->ip_addr_start) + 1;
+		if (count > 1000)
+			count = 1000;
+		if (count > 0)
+			wpa_auth->ip_pool = bitfield_alloc(count);
+	}
+#endif /* CONFIG_P2P */
+
+	return wpa_auth;
+}
+
+
+int wpa_init_keys(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group = wpa_auth->group;
+
+	wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial "
+		   "keys");
+	wpa_group_sm_step(wpa_auth, group);
+	group->GInit = FALSE;
+	wpa_group_sm_step(wpa_auth, group);
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * 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 */
+
+#ifdef CONFIG_P2P
+	bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
+	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;
+	group->GTK_len = wpa_cipher_key_len(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,
+		  const u8 *p2p_dev_addr)
+{
+	struct wpa_state_machine *sm;
+
+	if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return NULL;
+
+	sm = os_zalloc(sizeof(struct wpa_state_machine));
+	if (sm == NULL)
+		return NULL;
+	os_memcpy(sm->addr, addr, ETH_ALEN);
+	if (p2p_dev_addr)
+		os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+
+	sm->wpa_auth = wpa_auth;
+	sm->group = wpa_auth->group;
+	wpa_group_get(sm->wpa_auth, sm->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");
+		/* Go to PTKINITDONE state to allow GTK rekeying */
+		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+		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)
+{
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr)) {
+		u32 start;
+		wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+			   "address %u.%u.%u.%u from " MACSTR,
+			   sm->ip_addr[0], sm->ip_addr[1],
+			   sm->ip_addr[2], sm->ip_addr[3],
+			   MAC2STR(sm->addr));
+		start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+		bitfield_clear(sm->wpa_auth->ip_pool,
+			       WPA_GET_BE32(sm->ip_addr) - start);
+	}
+#endif /* CONFIG_P2P */
+	if (sm->GUpdateStationKeys) {
+		sm->group->GKeyDoneStations--;
+		sm->GUpdateStationKeys = FALSE;
+	}
+#ifdef CONFIG_IEEE80211R
+	os_free(sm->assoc_resp_ftie);
+	wpabuf_free(sm->ft_pending_req_ies);
+#endif /* CONFIG_IEEE80211R */
+	os_free(sm->last_rx_eapol_key);
+	os_free(sm->wpa_ie);
+	wpa_group_put(sm->wpa_auth, sm->group);
+	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_key_replay_counter *ctr,
+				    const u8 *replay_counter)
+{
+	int i;
+	for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+		if (!ctr[i].valid)
+			break;
+		if (os_memcmp(replay_counter, ctr[i].counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
+					    const u8 *replay_counter)
+{
+	int i;
+	for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+		if (ctr[i].valid &&
+		    (replay_counter == NULL ||
+		     os_memcmp(replay_counter, ctr[i].counter,
+			       WPA_REPLAY_COUNTER_LEN) == 0))
+			ctr[i].valid = FALSE;
+	}
+}
+
+
+#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 */
+
+
+static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+				    struct wpa_state_machine *sm, int group)
+{
+	/* Supplicant reported a Michael MIC error */
+	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			 "received EAPOL-Key Error Request "
+			 "(STA detected Michael MIC failure (group=%d))",
+			 group);
+
+	if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"ignore Michael MIC failure report since "
+				"group cipher is not TKIP");
+	} else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"ignore Michael MIC failure report since "
+				"pairwise cipher is not TKIP");
+	} else {
+		if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
+			return 1; /* STA entry was removed */
+		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);
+	return 0;
+}
+
+
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+			      size_t data_len)
+{
+	struct wpa_ptk PTK;
+	int ok = 0;
+	const u8 *pmk = NULL;
+
+	for (;;) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+					       sm->p2p_dev_addr, pmk);
+			if (pmk == NULL)
+				break;
+		} else
+			pmk = sm->PMK;
+
+		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+		    == 0) {
+			ok = 1;
+			break;
+		}
+
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+			break;
+	}
+
+	if (!ok) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Earlier SNonce did not result in matching MIC");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Earlier SNonce resulted in matching MIC");
+	sm->alt_snonce_valid = 0;
+	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	sm->PTK_valid = TRUE;
+
+	return 0;
+}
+
+
+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;
+	struct wpa_eapol_key_192 *key192;
+	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, *key_data;
+	size_t eapol_key_ie_len, keyhdrlen, mic_len;
+
+	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+		return;
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	if (data_len < sizeof(*hdr) + keyhdrlen)
+		return;
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	if (mic_len == 24) {
+		key_data = (const u8 *) (key192 + 1);
+		key_data_length = WPA_GET_BE16(key192->key_data_length);
+	} else {
+		key_data = (const u8 *) (key + 1);
+		key_data_length = WPA_GET_BE16(key->key_data_length);
+	}
+	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
+		   " key_info=0x%x type=%u key_data_length=%u",
+		   MAC2STR(sm->addr), key_info, key->type, key_data_length);
+	if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
+		wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
+			   "key_data overflow (%d > %lu)",
+			   key_data_length,
+			   (unsigned long) (data_len - sizeof(*hdr) -
+					    keyhdrlen));
+		return;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		if (key->type == EAPOL_KEY_TYPE_WPA) {
+			/*
+			 * Some deployed station implementations seem to send
+			 * msg 4/4 with incorrect type value in WPA2 mode.
+			 */
+			wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key "
+				   "with unexpected WPA type in RSN mode");
+		} else 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 ||
+		    sm->pairwise == WPA_CIPHER_GCMP) {
+			if (wpa_use_aes_cmac(sm) &&
+			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
+			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+			    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/GCMP");
+				return;
+			}
+		}
+
+		if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+			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, key->replay_counter)) {
+		int i;
+
+		if (msg == PAIRWISE_2 &&
+		    wpa_replay_counter_valid(sm->prev_key_replay,
+					     key->replay_counter) &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+		    os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
+		{
+			/*
+			 * Some supplicant implementations (e.g., Windows XP
+			 * WZC) update SNonce for each EAPOL-Key 2/4. This
+			 * breaks the workaround on accepting any of the
+			 * pending requests, so allow the SNonce to be updated
+			 * even if we have already sent out EAPOL-Key 3/4.
+			 */
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "Process SNonce update from STA "
+					 "based on retransmitted EAPOL-Key "
+					 "1/4");
+			sm->update_snonce = 1;
+			os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+			sm->alt_snonce_valid = TRUE;
+			os_memcpy(sm->alt_replay_counter,
+				  sm->key_replay[0].counter,
+				  WPA_REPLAY_COUNTER_LEN);
+			goto continue_processing;
+		}
+
+		if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+		    os_memcmp(key->replay_counter, sm->alt_replay_counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0) {
+			/*
+			 * Supplicant may still be using the old SNonce since
+			 * there was two EAPOL-Key 2/4 messages and they had
+			 * different SNonce values.
+			 */
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
+			goto continue_processing;
+		}
+
+		if (msg == PAIRWISE_2 &&
+		    wpa_replay_counter_valid(sm->prev_key_replay,
+					     key->replay_counter) &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "ignore retransmitted EAPOL-Key %s - "
+					 "SNonce did not change", msgtxt);
+		} else {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "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;
+	}
+
+continue_processing:
+	switch (msg) {
+	case PAIRWISE_2:
+		if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+		    sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
+		    (!sm->update_snonce ||
+		     sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
+			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");
+			random_mark_pool_ready();
+			wpa_sta_disconnect(wpa_auth, sm->addr);
+			return;
+		}
+		if (wpa_parse_kde_ies(key_data, 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 if (kde.osen) {
+			eapol_key_ie = kde.osen;
+			eapol_key_ie_len = kde.osen_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 */
+#ifdef CONFIG_P2P
+		if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+		    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+			int idx;
+			wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+				   "EAPOL-Key exchange");
+			idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+			if (idx >= 0) {
+				u32 start = WPA_GET_BE32(wpa_auth->conf.
+							 ip_addr_start);
+				bitfield_set(wpa_auth->ip_pool, idx);
+				WPA_PUT_BE32(sm->ip_addr, start + idx);
+				wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+					   "address %u.%u.%u.%u to " MACSTR,
+					   sm->ip_addr[0], sm->ip_addr[1],
+					   sm->ip_addr[2], sm->ip_addr[3],
+					   MAC2STR(sm->addr));
+			}
+		}
+#endif /* CONFIG_P2P */
+		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 && !sm->update_snonce) {
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+				       data_len) &&
+		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+		     wpa_try_alt_snonce(sm, 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_data, key_data_length);
+#endif /* CONFIG_PEERKEY */
+			return;
+		} else if (key_info & WPA_KEY_INFO_ERROR) {
+			if (wpa_receive_error_report(
+				    wpa_auth, sm,
+				    !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
+				return; /* STA entry was removed */
+		} 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, key_data,
+				   key_data_length);
+#endif /* CONFIG_PEERKEY */
+		} else if (key_data_length > 0 &&
+			   wpa_parse_kde_ies(key_data, 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");
+			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. */
+		wpa_replay_counter_mark_invalid(sm->key_replay,
+						key->replay_counter);
+
+		if (msg == PAIRWISE_2) {
+			/*
+			 * Maintain a copy of the pending EAPOL-Key frames in
+			 * case the EAPOL-Key frame was retransmitted. This is
+			 * needed to allow EAPOL-Key msg 2/4 reply to another
+			 * pending msg 1/4 to update the SNonce to work around
+			 * unexpected supplicant behavior.
+			 */
+			os_memcpy(sm->prev_key_replay, sm->key_replay,
+				  sizeof(sm->key_replay));
+		} else {
+			os_memset(sm->prev_key_replay, 0,
+				  sizeof(sm->prev_key_replay));
+		}
+
+		/*
+		 * Make sure old valid counters are not accepted anymore and
+		 * do not get copied again.
+		 */
+		wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
+	}
+
+#ifdef CONFIG_PEERKEY
+	if (msg == SMK_M3) {
+		wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
+		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->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
+	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;
+	struct wpa_eapol_key_192 *key192;
+	size_t len, mic_len, keyhdrlen;
+	int alg;
+	int key_data_len, pad_len = 0;
+	u8 *buf, *pos;
+	int version, pairwise;
+	int i;
+	u8 *key_data;
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
+
+	if (force_version)
+		version = force_version;
+	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+	else if (wpa_use_aes_cmac(sm))
+		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+	else if (sm->pairwise != WPA_CIPHER_TKIP)
+		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 ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+	     wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+	     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);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
+
+	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;
+	WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
+	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);
+	wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+		    key->replay_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_data, kde, kde_len);
+		if (mic_len == 24)
+			WPA_PUT_BE16(key192->key_data_length, kde_len);
+		else
+			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 ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+		    wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+				     (key_data_len - 8) / 8, buf, key_data)) {
+				os_free(hdr);
+				os_free(buf);
+				return;
+			}
+			if (mic_len == 24)
+				WPA_PUT_BE16(key192->key_data_length,
+					     key_data_len);
+			else
+				WPA_PUT_BE16(key->key_data_length,
+					     key_data_len);
+#ifndef CONFIG_NO_RC4
+		} else if (sm->PTK.kek_len == 16) {
+			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, sm->PTK.kek_len);
+			os_memcpy(key_data, buf, key_data_len);
+			rc4_skip(ek, 32, 256, key_data, key_data_len);
+			if (mic_len == 24)
+				WPA_PUT_BE16(key192->key_data_length,
+					     key_data_len);
+			else
+				WPA_PUT_BE16(key->key_data_length,
+					     key_data_len);
+#endif /* CONFIG_NO_RC4 */
+		} else {
+			os_free(hdr);
+			os_free(buf);
+			return;
+		}
+		os_free(buf);
+	}
+
+	if (key_info & WPA_KEY_INFO_MIC) {
+		u8 *key_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;
+		}
+
+		key_mic = key192->key_mic; /* same offset for key and key192 */
+		wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+				  sm->wpa_key_mgmt, version,
+				  (u8 *) hdr, len, key_mic);
+#ifdef CONFIG_TESTING_OPTIONS
+		if (!pairwise &&
+		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
+		    drand48() <
+		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"Corrupting group EAPOL-Key Key MIC");
+			key_mic[0]++;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	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 = pairwise ? eapol_key_timeout_first :
+			eapol_key_timeout_first_group;
+	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(int akmp, struct wpa_ptk *PTK, u8 *data,
+			      size_t data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
+	u16 key_info;
+	int ret = 0;
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = wpa_mic_len(akmp);
+
+	if (data_len < sizeof(*hdr) + sizeof(*key))
+		return -1;
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	os_memcpy(mic, key192->key_mic, mic_len);
+	os_memset(key192->key_mic, 0, mic_len);
+	if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+			      key_info & WPA_KEY_INFO_TYPE_MASK,
+			      data, data_len, key192->key_mic) ||
+	    os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
+		ret = -1;
+	os_memcpy(key192->key_mic, mic, mic_len);
+	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, enum 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:
+#ifdef CONFIG_MESH
+		/* PTKs are derived through AMPE */
+		if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+			/* not mesh */
+			break;
+		}
+		return 0;
+#endif /* CONFIG_MESH */
+	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);
+	}
+
+	if (sm->in_step_loop) {
+		/*
+		 * wpa_sm_step() is already running - avoid recursive call to
+		 * it by making the existing loop process the new update.
+		 */
+		sm->changed = TRUE;
+		return 0;
+	}
+	return wpa_sm_step(sm);
+}
+
+
+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_ensure_init(struct wpa_authenticator *wpa_auth,
+				  struct wpa_group *group)
+{
+	if (group->first_sta_seen)
+		return;
+	/*
+	 * 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;
+	} else {
+		group->first_sta_seen = TRUE;
+		group->reject_4way_hs_for_entropy = FALSE;
+	}
+
+	if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
+	    wpa_gtk_update(wpa_auth, group) < 0 ||
+	    wpa_group_config_group_keys(wpa_auth, group) < 0) {
+		wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
+		group->first_sta_seen = FALSE;
+		group->reject_4way_hs_for_entropy = TRUE;
+	}
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION2)
+{
+	SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+
+	wpa_group_ensure_init(sm->wpa_auth, sm->group);
+	sm->ReAuthenticationRequest = FALSE;
+
+	/*
+	 * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
+	 * ambiguous. The Authenticator state machine uses a counter that is
+	 * incremented by one for each 4-way handshake. However, the security
+	 * analysis of 4-way handshake points out that unpredictable nonces
+	 * help in preventing precomputation attacks. Instead of the state
+	 * machine definition, use an unpredictable nonce value here to provide
+	 * stronger protection against potential precomputation attacks.
+	 */
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
+			   "ANonce.");
+		sm->Disconnect = TRUE;
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+		    WPA_NONCE_LEN);
+	/* 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, get_msk: %p",
+			   sm->wpa_auth->cb.get_msk);
+		sm->Disconnect = TRUE;
+		return;
+	}
+	os_memset(msk, 0, sizeof(msk));
+
+	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, sm->p2p_dev_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->alt_snonce_valid = 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) &&
+	    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
+		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 if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+			/* No KCK available to derive PMKID */
+			pmkid = NULL;
+		} 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 *snonce,
+			  const u8 *pmk, struct wpa_ptk *ptk)
+{
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
+#endif /* CONFIG_IEEE80211R */
+
+	return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
+			      ptk, sm->wpa_key_mgmt, sm->pairwise);
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+{
+	struct wpa_ptk PTK;
+	int ok = 0, psk_found = 0;
+	const u8 *pmk = NULL;
+
+	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
+	sm->EAPOLKeyReceived = FALSE;
+	sm->update_snonce = 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,
+					       sm->p2p_dev_addr, pmk);
+			if (pmk == NULL)
+				break;
+			psk_found = 1;
+		} else
+			pmk = sm->PMK;
+
+		wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
+
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &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");
+		if (psk_found)
+			wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
+		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_const(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) {
+		size_t len;
+		len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+		return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
+	}
+
+	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;
+	u8 rsc[WPA_KEY_RSC_LEN];
+	size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+	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, rsc) < 0)
+		os_memset(igtk.pn, 0, sizeof(igtk.pn));
+	else
+		os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random IGTK to each STA to prevent use of
+		 * IGTK in the BSS.
+		 */
+		if (random_get_bytes(igtk.igtk, len) < 0)
+			return pos;
+	}
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
+			  (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+			  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, dummy_gtk[32];
+	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 and possible MDIE */
+		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+			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;
+		if (sm->wpa_auth->conf.disable_gtk) {
+			/*
+			 * Provide unique random GTK to each STA to prevent use
+			 * of GTK in the BSS.
+			 */
+			if (random_get_bytes(dummy_gtk, gtk_len) < 0)
+				return;
+			gtk = dummy_gtk;
+		}
+		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;
+		if (sm->rx_eapol_key_secure) {
+			/*
+			 * It looks like Windows 7 supplicant tries to use
+			 * Secure bit in msg 2/4 after having reported Michael
+			 * MIC failure and it then rejects the 4-way handshake
+			 * if msg 3/4 does not set Secure bit. Work around this
+			 * by setting the Secure bit here even in the case of
+			 * WPA if the supplicant used it first.
+			 */
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					"STA used Secure bit in WPA msg 2/4 - "
+					"set Secure for 3/4 as workaround");
+			secure = 1;
+		}
+	}
+
+	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 */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0)
+		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
+	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 */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0) {
+		u8 addr[3 * 4];
+		os_memcpy(addr, sm->ip_addr, 4);
+		os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+		os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+				  addr, sizeof(addr), NULL, 0);
+	}
+#endif /* CONFIG_P2P */
+
+	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 = wpa_cipher_to_alg(sm->pairwise);
+		int klen = wpa_cipher_key_len(sm->pairwise);
+		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+				     sm->PTK.tk, 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, sm->p2p_dev_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->update_snonce)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else 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;
+	const u8 *kde;
+	u8 *kde_buf = NULL, *pos, hdr[2];
+	size_t kde_len;
+	u8 *gtk, dummy_gtk[32];
+
+	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");
+
+	gtk = gsm->GTK[gsm->GN - 1];
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0)
+			return;
+		gtk = dummy_gtk;
+	}
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+			ieee80211w_kde_len(sm);
+		kde_buf = os_malloc(kde_len);
+		if (kde_buf == NULL)
+			return;
+
+		kde = pos = kde_buf;
+		hdr[0] = gsm->GN & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gsm->GTK_len);
+		pos = ieee80211w_kde_add(sm, pos);
+		kde_len = pos - kde;
+	} else {
+		kde = gtk;
+		kde_len = 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, kde_len, gsm->GN, 1);
+
+	os_free(kde_buf);
+}
+
+
+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) {
+		size_t len;
+		len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+		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], len) < 0)
+			ret = -1;
+		wpa_hexdump_key(MSG_DEBUG, "IGTK",
+				group->IGTK[group->GN_igtk - 4], 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 (ctx != NULL && ctx != sm->group)
+		return 0;
+
+	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");
+	}
+
+	/* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
+	if (sm->is_wnmsleep)
+		return 0;
+
+	sm->group->GKeyDoneStations++;
+	sm->GUpdateStationKeys = TRUE;
+
+	wpa_sm_step(sm);
+	return 0;
+}
+
+
+#ifdef CONFIG_WNM
+/* update GTK when exiting WNM-Sleep Mode */
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
+{
+	if (sm == NULL || sm->is_wnmsleep)
+		return;
+
+	wpa_group_update_sta(sm, NULL);
+}
+
+
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
+{
+	if (sm)
+		sm->is_wnmsleep = !!flag;
+}
+
+
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_group *gsm = sm->group;
+	u8 *start = pos;
+
+	/*
+	 * GTK subelement:
+	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+	 * Key[5..32]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_GTK;
+	*pos++ = 11 + gsm->GTK_len;
+	/* Key ID in B0-B1 of Key Info */
+	WPA_PUT_LE16(pos, gsm->GN & 0x03);
+	pos += 2;
+	*pos++ = gsm->GTK_len;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
+		return 0;
+	pos += 8;
+	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+	pos += gsm->GTK_len;
+
+	wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
+			gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+
+	return pos - start;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_group *gsm = sm->group;
+	u8 *start = pos;
+	size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+	/*
+	 * IGTK subelement:
+	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_IGTK;
+	*pos++ = 2 + 6 + len;
+	WPA_PUT_LE16(pos, gsm->GN_igtk);
+	pos += 2;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
+		return 0;
+	pos += 6;
+
+	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+	pos += len;
+
+	wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN_igtk);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
+			gsm->IGTK[gsm->GN_igtk - 4], len);
+
+	return pos - start;
+}
+#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_WNM */
+
+
+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, group);
+	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_cipher_to_alg(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) {
+		enum wpa_alg alg;
+		size_t len;
+
+		alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher);
+		len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+		if (ret == 0 &&
+		    wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+				     broadcast_ether_addr, group->GN_igtk,
+				     group->IGTK[group->GN_igtk - 4], len) < 0)
+			ret = -1;
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	return ret;
+}
+
+
+static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	if (sm->group == ctx) {
+		wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
+			   " for discconnection due to fatal failure",
+			   MAC2STR(sm->addr));
+		sm->Disconnect = TRUE;
+	}
+
+	return 0;
+}
+
+
+static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
+				    struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE");
+	group->changed = TRUE;
+	group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
+	wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
+}
+
+
+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) {
+		wpa_group_fatal_failure(wpa_auth, group);
+		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_FATAL_FAILURE) {
+		/* Do not allow group operations */
+	} 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);
+		wpa_group_config_group_keys(wpa_auth, group);
+	}
+}
+
+
+static const char * wpa_bool_txt(int val)
+{
+	return val ? "TRUE" : "FALSE";
+}
+
+
+#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 (os_snprintf_error(buflen - len, ret))
+		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_key_len(wpa_auth->conf.wpa_group) * 8,
+		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 (os_snprintf_error(buflen - len, ret))
+		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 (os_snprintf_error(buflen - len, ret))
+		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 */
+
+	pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
+				       WPA_PROTO_RSN : WPA_PROTO_WPA,
+				       sm->pairwise);
+	if (pairwise == 0)
+		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 (os_snprintf_error(buflen - len, ret))
+		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 (os_snprintf_error(buflen - len, ret))
+		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 ||
+	    sm->wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+				 sm->PTK.kck, sm->PTK.kck_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,
+				 NULL, 0,
+				 wpa_auth->addr,
+				 sta_addr, session_timeout, eapol,
+				 WPA_KEY_MGMT_IEEE8021X))
+		return 0;
+
+	return -1;
+}
+
+
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk)
+{
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+				 NULL, 0,
+				 wpa_auth->addr, addr, 0, NULL,
+				 WPA_KEY_MGMT_SAE))
+		return 0;
+
+	return -1;
+}
+
+
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
+		return;
+	pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+	if (pmksa) {
+		wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
+			   MACSTR " based on request", MAC2STR(sta_addr));
+		pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
+	}
+}
+
+
+/*
+ * Remove and free the group from wpa_authenticator. This is triggered by a
+ * callback to make sure nobody is currently iterating the group list while it
+ * gets modified.
+ */
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+			   struct wpa_group *group)
+{
+	struct wpa_group *prev = wpa_auth->group;
+
+	wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
+		   group->vlan_id);
+
+	while (prev) {
+		if (prev->next == group) {
+			/* This never frees the special first group as needed */
+			prev->next = group->next;
+			os_free(group);
+			break;
+		}
+		prev = prev->next;
+	}
+
+}
+
+
+/* Increase the reference counter for group */
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references++;
+}
+
+
+/* Decrease the reference counter and maybe free the group */
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references--;
+	if (group->references)
+		return;
+	wpa_group_free(wpa_auth, group);
+}
+
+
+/*
+ * Add a group that has its references counter set to zero. Caller needs to
+ * call wpa_group_get() on the return value to mark the entry in use.
+ */
+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, 0);
+	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;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
+		   "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
+
+	wpa_group_get(sm->wpa_auth, group);
+	wpa_group_put(sm->wpa_auth, sm->group);
+	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);
+	}
+}
+
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
+}
+
+
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+	if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+		return -1;
+	os_memcpy(addr, sm->ip_addr, 4);
+	return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr)
+{
+	return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group;
+
+	if (!wpa_auth)
+		return;
+	for (group = wpa_auth->group; group; group = group->next)
+		wpa_group_config_group_keys(wpa_auth, group);
+}
diff --git a/freebsd/contrib/wpa/src/ap/wpa_auth.h b/freebsd/contrib/wpa/src/ap/wpa_auth.h
new file mode 100644
index 00000000000..fd04f169433
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/wpa_auth.h
@@ -0,0 +1,328 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2015, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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"
+#include "common/ieee802_11_defs.h"
+
+#define MAX_OWN_IE_OVERRIDE 256
+
+#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
+#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
+
+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[FT_R0KH_R1KH_PULL_NONCE_LEN];
+	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[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* 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 disable_pmksa_caching;
+	int okc;
+	int tx_status;
+#ifdef CONFIG_IEEE80211W
+	enum mfp_options ieee80211w;
+	int group_mgmt_cipher;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+	u8 ssid[SSID_MAX_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 */
+	int disable_gtk;
+	int ap_mlme;
+#ifdef CONFIG_TESTING_OPTIONS
+	double corrupt_gtk_rekey_mic_probability;
+	u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
+	size_t own_ie_override_len;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
+};
+
+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);
+	int (*mic_failure_report)(void *ctx, const u8 *addr);
+	void (*psk_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 *p2p_dev_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);
+	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
+			 size_t tspec_ielen);
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_MESH
+	int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
+};
+
+struct wpa_authenticator * wpa_init(const u8 *addr,
+				    struct wpa_auth_config *conf,
+				    struct wpa_auth_callbacks *cb);
+int wpa_init_keys(struct wpa_authenticator *wpa_auth);
+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_validate_osen(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm,
+		      const u8 *osen_ie, size_t osen_ie_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,
+		  const u8 *p2p_dev_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);
+enum wpa_event {
+	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+	WPA_REAUTH_EAPOL, WPA_ASSOC_FT
+};
+void wpa_remove_ptk(struct wpa_state_machine *sm);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum 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_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk);
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr);
+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 */
+
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm);
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
+
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr);
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
+
+#endif /* WPA_AUTH_H */
diff --git a/freebsd/contrib/wpa/src/ap/wpa_auth_ft.c b/freebsd/contrib/wpa/src/ap/wpa_auth_ft.c
new file mode 100644
index 00000000000..32622b4db52
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/wpa_auth_ft.c
@@ -0,0 +1,1794 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2004-2015, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.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"
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+				     const u8 *current_ap, const u8 *sta_addr,
+				     u16 status, const u8 *resp_ies,
+				     size_t resp_ies_len);
+
+
+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);
+}
+
+
+static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr,
+			    u8 *tspec_ie, size_t tspec_ielen)
+{
+	if (wpa_auth->cb.add_tspec == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+		return -1;
+	}
+	return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
+				      tspec_ielen);
+}
+
+
+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_const(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_const(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_state_machine *sm,
+			      const u8 *ies, size_t ies_len,
+			      const u8 *pmk_r0_name)
+{
+	struct ft_remote_r0kh *r0kh;
+	struct ft_r0kh_r1kh_pull_frame frame, f;
+
+	r0kh = sm->wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		if (r0kh->id_len == sm->r0kh_id_len &&
+		    os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
+		    0)
+			break;
+		r0kh = r0kh->next;
+	}
+	if (r0kh == NULL) {
+		wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		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, sm->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, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+			   "nonce");
+		return -1;
+	}
+	os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
+		  FT_R0KH_R1KH_PULL_NONCE_LEN);
+	os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+	os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+	os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
+	os_memset(f.pad, 0, sizeof(f.pad));
+
+	if (aes_wrap(r0kh->key, sizeof(r0kh->key),
+		     (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+		     f.nonce, frame.nonce) < 0)
+		return -1;
+
+	wpabuf_free(sm->ft_pending_req_ies);
+	sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+	if (sm->ft_pending_req_ies == NULL)
+		return -1;
+
+	wpa_ft_rrb_send(sm->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)
+{
+	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);
+
+	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+				 sm->wpa_auth->addr, sm->pmk_r1_name,
+				 ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
+}
+
+
+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 && key_len < sizeof(keybuf)) {
+		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, sm->PTK.kek_len, 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, sm->PTK.kek_len, 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(struct wpa_state_machine *sm,
+				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;
+	}
+
+	if (parse.wmm_tspec) {
+		struct wmm_tspec_element *tspec;
+
+		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));
+	}
+
+#ifdef NEED_AP_MLME
+	if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
+		int res;
+
+		res = wmm_process_tspec((struct wmm_tspec_element *) pos);
+		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(struct wmm_tspec_element);
+		}
+		return pos;
+	}
+#endif /* NEED_AP_MLME */
+
+	if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
+		int res;
+
+		res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
+				       sizeof(struct wmm_tspec_element));
+		if (res >= 0) {
+			if (res)
+				rdie->status_code = host_to_le16(res);
+			else {
+				/* TSPEC accepted; include updated TSPEC in
+				 * response */
+				rdie->descr_count = 1;
+				pos += sizeof(struct wmm_tspec_element);
+			}
+			return pos;
+		}
+	}
+
+	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(struct wpa_state_machine *sm, 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(sm, 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 (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		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(sm, 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->PTK.kck_len, 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 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) */
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+	if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
+		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.tk, klen))
+		return;
+
+	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+	sm->pairwise_set = TRUE;
+}
+
+
+static int 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;
+	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, ies, ies_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;
+		}
+
+		return -1; /* Status pending */
+	}
+
+	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);
+
+	if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+			      sm->wpa_auth->addr, pmk_r1_name,
+			      &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+			      pairwise) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	sm->pairwise = pairwise;
+	sm->PTK_valid = TRUE;
+	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;
+	int res;
+
+	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);
+	sm->ft_pending_cb = cb;
+	sm->ft_pending_cb_ctx = ctx;
+	sm->ft_pending_auth_transaction = auth_transaction;
+	res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+				      &resp_ies_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
+		return;
+	}
+	status = res;
+
+	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[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = 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_const(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_const(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_const(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_const(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 += ieee802_11_ie_count(parse.ric, parse.ric_len);
+	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->PTK.kck_len, 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_const(mic, ftie->mic, mic_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
+		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+			    ftie->mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+			    parse.mdie - 2, parse.mdie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
+			    parse.ftie - 2, parse.ftie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+			    parse.rsn - 2, parse.rsn_len + 2);
+		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);
+	if (frame == NULL)
+		return -1;
+	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 void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+				     u16 auth_transaction, u16 resp,
+				     const u8 *ies, size_t ies_len)
+{
+	struct wpa_state_machine *sm = ctx;
+	wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
+		   MAC2STR(sm->addr));
+	wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
+				  WLAN_STATUS_SUCCESS, ies, ies_len);
+}
+
+
+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;
+	size_t resp_ies_len;
+	int res;
+
+	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);
+
+	sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+	sm->ft_pending_cb_ctx = sm;
+	os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+	res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+				      &resp_ies_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
+		return 0;
+	}
+	status = res;
+
+	res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
+					resp_ies, resp_ies_len);
+	os_free(resp_ies);
+	return res;
+}
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+				     const u8 *current_ap, const u8 *sta_addr,
+				     u16 status, const u8 *resp_ies,
+				     size_t resp_ies_len)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	size_t rlen;
+	struct ft_rrb_frame *frame;
+	u8 *pos;
+
+	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);
+	if (frame == NULL)
+		return -1;
+	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);
+
+	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 f;
+	const u8 *crypt;
+	u8 *plain;
+	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(f))
+		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;
+	}
+
+	crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
+		       (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+		       crypt, plain) < 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);
+	os_memset(r.pad, 0, sizeof(r.pad));
+
+	if (aes_wrap(r1kh->key, sizeof(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 void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+	int res;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+	u16 status;
+
+	res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+				      wpabuf_len(sm->ft_pending_req_ies),
+				      &resp_ies, &resp_ies_len);
+	wpabuf_free(sm->ft_pending_req_ies);
+	sm->ft_pending_req_ies = NULL;
+	if (res < 0)
+		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	status = res;
+	wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+		   " - status %u", MAC2STR(sm->addr), status);
+
+	sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+			  sm->ft_pending_auth_transaction + 1, status,
+			  resp_ies, resp_ies_len);
+	os_free(resp_ies);
+}
+
+
+static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+
+	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
+		return 0;
+	if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
+		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
+		return 0;
+	if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
+		   MACSTR " - process from timeout", MAC2STR(sm->addr));
+	eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+	return 1;
+}
+
+
+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 f;
+	const u8 *crypt;
+	u8 *plain;
+	struct ft_remote_r0kh *r0kh;
+	int pairwise, res;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
+
+	if (data_len < sizeof(f))
+		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;
+	}
+
+	crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+		       (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+		       crypt, plain) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+			   "response from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+	if (os_memcmp_const(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;
+	}
+
+	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);
+
+	res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+				  pairwise);
+	wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
+	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
+	os_memset(f.pmk_r1, 0, PMK_LEN);
+
+	return res ? 0 : -1;
+}
+
+
+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 f;
+	const u8 *crypt;
+	u8 *plain;
+	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(f))
+		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;
+	}
+
+	crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+				       timestamp);
+	/* aes_unwrap() does not support inplace decryption, so use a temporary
+	 * buffer for the data. */
+	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+		       (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+		       crypt, plain) < 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_const(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;
+	}
+
+	if (end > pos) {
+		wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end",
+			    pos, end - pos);
+	}
+
+	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;
+	const u8 *plain;
+	u8 *crypt;
+
+	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);
+	os_memset(f.pad, 0, sizeof(f.pad));
+	plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+					     timestamp);
+	crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
+					   timestamp);
+	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+		     (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+		     plain, crypt) < 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/freebsd/contrib/wpa/src/ap/wpa_auth_i.h b/freebsd/contrib/wpa/src/ap/wpa_auth_i.h
new file mode 100644
index 00000000000..57b098f2ed7
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/wpa_auth_i.h
@@ -0,0 +1,259 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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];
+	u8 p2p_dev_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 alt_SNonce[WPA_NONCE_LEN];
+	u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	u8 PMK[PMK_LEN];
+	struct wpa_ptk PTK;
+	Boolean PTK_valid;
+	Boolean pairwise_set;
+	int keycount;
+	Boolean Pair;
+	struct wpa_key_replay_counter {
+		u8 counter[WPA_REPLAY_COUNTER_LEN];
+		Boolean valid;
+	} key_replay[RSNA_MAX_EAPOL_RETRIES],
+		prev_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;
+	unsigned int rx_eapol_key_secure:1;
+	unsigned int update_snonce:1;
+	unsigned int alt_snonce_valid:1;
+#ifdef CONFIG_IEEE80211R
+	unsigned int ft_completed:1;
+	unsigned int pmk_r1_name_valid:1;
+#endif /* CONFIG_IEEE80211R */
+	unsigned int is_wnmsleep:1;
+
+	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;
+
+	void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+			      u16 auth_transaction, u16 status,
+			      const u8 *ies, size_t ies_len);
+	void *ft_pending_cb_ctx;
+	struct wpabuf *ft_pending_req_ies;
+	u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+	u8 ft_pending_auth_transaction;
+	u8 ft_pending_current_ap[ETH_ALEN];
+#endif /* CONFIG_IEEE80211R */
+
+	int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+	u8 ip_addr[4];
+#endif /* CONFIG_P2P */
+};
+
+
+/* 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_FATAL_FAILURE
+	} 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_MAX_LEN];
+	int GN_igtk, GM_igtk;
+#endif /* CONFIG_IEEE80211W */
+	/* Number of references except those in struct wpa_group->next */
+	unsigned int references;
+};
+
+
+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;
+
+#ifdef CONFIG_P2P
+	struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
+};
+
+
+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,
+		   const u8 *key_data, size_t key_data_len);
+void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len);
+void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len);
+#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);
+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/freebsd/contrib/wpa/src/ap/wpa_auth_ie.h b/freebsd/contrib/wpa/src/ap/wpa_auth_ie.h
new file mode 100644
index 00000000000..d2067ba3112
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/wpa_auth_ie.h
@@ -0,0 +1,57 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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 */
+#ifdef CONFIG_P2P
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
+
+	const u8 *osen;
+	size_t osen_len;
+};
+
+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/freebsd/contrib/wpa/src/eapol_auth/eapol_auth_sm.h b/freebsd/contrib/wpa/src/eapol_auth/eapol_auth_sm.h
new file mode 100644
index 00000000000..e1974e4354d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eapol_auth/eapol_auth_sm.h
@@ -0,0 +1,102 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine
+ * Copyright (c) 2002-2015, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAPOL_AUTH_SM_H
+#define EAPOL_AUTH_SM_H
+
+#define EAPOL_SM_PREAUTH BIT(0)
+#define EAPOL_SM_WAIT_START BIT(1)
+#define EAPOL_SM_USES_WPA BIT(2)
+#define EAPOL_SM_FROM_PMKSA_CACHE BIT(3)
+
+struct eapol_auth_config {
+	int eap_reauth_period;
+	int wpa;
+	int individual_wep_key_len;
+	int eap_server;
+	void *ssl_ctx;
+	void *msg_ctx;
+	void *eap_sim_db_priv;
+	char *eap_req_id_text; /* a copy of this will be allocated */
+	size_t eap_req_id_text_len;
+	int erp_send_reauth_start;
+	char *erp_domain; /* a copy of this will be allocated */
+	int erp; /* Whether ERP is enabled on authentication server */
+	unsigned int tls_session_lifetime;
+	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;
+	struct wps_context *wps;
+	int fragment_size;
+	u16 pwd_group;
+	int pbc_in_m1;
+	const u8 *server_id;
+	size_t server_id_len;
+
+	/* Opaque context pointer to owner data for callback functions */
+	void *ctx;
+};
+
+struct eap_user;
+struct eap_server_erp_key;
+
+typedef enum {
+	EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING
+} eapol_logger_level;
+
+enum eapol_event {
+	EAPOL_AUTH_SM_CHANGE,
+	EAPOL_AUTH_REAUTHENTICATE
+};
+
+struct eapol_auth_cb {
+	void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data,
+			   size_t datalen);
+	void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data,
+			 size_t datalen);
+	void (*finished)(void *ctx, void *sta_ctx, int success, int preauth,
+			 int remediation);
+	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+			    int phase2, struct eap_user *user);
+	int (*sta_entry_alive)(void *ctx, const u8 *addr);
+	void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level,
+		       const char *txt);
+	void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized);
+	void (*abort_auth)(void *ctx, void *sta_ctx);
+	void (*tx_key)(void *ctx, void *sta_ctx);
+	void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type);
+	struct eap_server_erp_key * (*erp_get_key)(void *ctx,
+						   const char *keyname);
+	int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
+};
+
+
+struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
+					     struct eapol_auth_cb *cb);
+void eapol_auth_deinit(struct eapol_authenticator *eapol);
+struct eapol_state_machine *
+eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
+		 int flags, const struct wpabuf *assoc_wps_ie,
+		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+		 const char *identity, const char *radius_cui);
+void eapol_auth_free(struct eapol_state_machine *sm);
+void eapol_auth_step(struct eapol_state_machine *sm);
+int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
+			  size_t buflen);
+int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx);
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm);
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+			const char *value);
+
+#endif /* EAPOL_AUTH_SM_H */
diff --git a/freebsd/contrib/wpa/src/radius/radius.h b/freebsd/contrib/wpa/src/radius/radius.h
new file mode 100644
index 00000000000..5977339e08d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/radius/radius.h
@@ -0,0 +1,322 @@
+/*
+ * RADIUS message processing
+ * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RADIUS_H
+#define RADIUS_H
+
+/* RFC 2865 - RADIUS */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct radius_hdr {
+	u8 code;
+	u8 identifier;
+	be16 length; /* including this header */
+	u8 authenticator[16];
+	/* followed by length-20 octets of attributes */
+} STRUCT_PACKED;
+
+enum { RADIUS_CODE_ACCESS_REQUEST = 1,
+       RADIUS_CODE_ACCESS_ACCEPT = 2,
+       RADIUS_CODE_ACCESS_REJECT = 3,
+       RADIUS_CODE_ACCOUNTING_REQUEST = 4,
+       RADIUS_CODE_ACCOUNTING_RESPONSE = 5,
+       RADIUS_CODE_ACCESS_CHALLENGE = 11,
+       RADIUS_CODE_STATUS_SERVER = 12,
+       RADIUS_CODE_STATUS_CLIENT = 13,
+       RADIUS_CODE_DISCONNECT_REQUEST = 40,
+       RADIUS_CODE_DISCONNECT_ACK = 41,
+       RADIUS_CODE_DISCONNECT_NAK = 42,
+       RADIUS_CODE_COA_REQUEST = 43,
+       RADIUS_CODE_COA_ACK = 44,
+       RADIUS_CODE_COA_NAK = 45,
+       RADIUS_CODE_RESERVED = 255
+};
+
+struct radius_attr_hdr {
+	u8 type;
+	u8 length; /* including this header */
+	/* followed by length-2 octets of attribute value */
+} STRUCT_PACKED;
+
+#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+
+enum { RADIUS_ATTR_USER_NAME = 1,
+       RADIUS_ATTR_USER_PASSWORD = 2,
+       RADIUS_ATTR_NAS_IP_ADDRESS = 4,
+       RADIUS_ATTR_NAS_PORT = 5,
+       RADIUS_ATTR_FRAMED_MTU = 12,
+       RADIUS_ATTR_REPLY_MESSAGE = 18,
+       RADIUS_ATTR_STATE = 24,
+       RADIUS_ATTR_CLASS = 25,
+       RADIUS_ATTR_VENDOR_SPECIFIC = 26,
+       RADIUS_ATTR_SESSION_TIMEOUT = 27,
+       RADIUS_ATTR_IDLE_TIMEOUT = 28,
+       RADIUS_ATTR_TERMINATION_ACTION = 29,
+       RADIUS_ATTR_CALLED_STATION_ID = 30,
+       RADIUS_ATTR_CALLING_STATION_ID = 31,
+       RADIUS_ATTR_NAS_IDENTIFIER = 32,
+       RADIUS_ATTR_PROXY_STATE = 33,
+       RADIUS_ATTR_ACCT_STATUS_TYPE = 40,
+       RADIUS_ATTR_ACCT_DELAY_TIME = 41,
+       RADIUS_ATTR_ACCT_INPUT_OCTETS = 42,
+       RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43,
+       RADIUS_ATTR_ACCT_SESSION_ID = 44,
+       RADIUS_ATTR_ACCT_AUTHENTIC = 45,
+       RADIUS_ATTR_ACCT_SESSION_TIME = 46,
+       RADIUS_ATTR_ACCT_INPUT_PACKETS = 47,
+       RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48,
+       RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49,
+       RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50,
+       RADIUS_ATTR_ACCT_LINK_COUNT = 51,
+       RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52,
+       RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53,
+       RADIUS_ATTR_EVENT_TIMESTAMP = 55,
+       RADIUS_ATTR_NAS_PORT_TYPE = 61,
+       RADIUS_ATTR_TUNNEL_TYPE = 64,
+       RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+       RADIUS_ATTR_TUNNEL_PASSWORD = 69,
+       RADIUS_ATTR_CONNECT_INFO = 77,
+       RADIUS_ATTR_EAP_MESSAGE = 79,
+       RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
+       RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
+       RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
+       RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
+       RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
+       RADIUS_ATTR_ERROR_CAUSE = 101,
+       RADIUS_ATTR_EAP_KEY_NAME = 102,
+       RADIUS_ATTR_OPERATOR_NAME = 126,
+       RADIUS_ATTR_LOCATION_INFO = 127,
+       RADIUS_ATTR_LOCATION_DATA = 128,
+       RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES = 129,
+       RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130,
+       RADIUS_ATTR_LOCATION_CAPABLE = 131,
+       RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132,
+       RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177,
+       RADIUS_ATTR_WLAN_HESSID = 181,
+       RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186,
+       RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
+       RADIUS_ATTR_WLAN_AKM_SUITE = 188,
+       RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189,
+};
+
+
+/* Termination-Action */
+#define RADIUS_TERMINATION_ACTION_DEFAULT 0
+#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1
+
+/* NAS-Port-Type */
+#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19
+
+/* Acct-Status-Type */
+#define RADIUS_ACCT_STATUS_TYPE_START 1
+#define RADIUS_ACCT_STATUS_TYPE_STOP 2
+#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8
+
+/* Acct-Authentic */
+#define RADIUS_ACCT_AUTHENTIC_RADIUS 1
+#define RADIUS_ACCT_AUTHENTIC_LOCAL 2
+#define RADIUS_ACCT_AUTHENTIC_REMOTE 3
+
+/* Acct-Terminate-Cause */
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3
+#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4
+#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14
+#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15
+#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17
+#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18
+
+#define RADIUS_TUNNEL_TAGS 32
+
+/* Tunnel-Type */
+#define RADIUS_TUNNEL_TYPE_PPTP 1
+#define RADIUS_TUNNEL_TYPE_L2TP 3
+#define RADIUS_TUNNEL_TYPE_IPIP 7
+#define RADIUS_TUNNEL_TYPE_GRE 10
+#define RADIUS_TUNNEL_TYPE_VLAN 13
+
+/* Tunnel-Medium-Type */
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2
+#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6
+
+
+struct radius_attr_vendor {
+	u8 vendor_type;
+	u8 vendor_length;
+} STRUCT_PACKED;
+
+#define RADIUS_VENDOR_ID_CISCO 9
+#define RADIUS_CISCO_AV_PAIR 1
+
+/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+
+enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
+       RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
+};
+
+
+/* Hotspot 2.0 - WFA Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_WFA 40808
+
+enum {
+	RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1,
+	RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2,
+	RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
+	RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
+	RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5,
+};
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+struct radius_ms_mppe_keys {
+	u8 *send;
+	size_t send_len;
+	u8 *recv;
+	size_t recv_len;
+};
+
+
+struct radius_msg;
+
+/* Default size to be allocated for new RADIUS messages */
+#define RADIUS_DEFAULT_MSG_SIZE 1024
+
+/* Default size to be allocated for attribute array */
+#define RADIUS_DEFAULT_ATTR_COUNT 16
+
+
+/* MAC address ASCII format for IEEE 802.1X use
+ * (draft-congdon-radius-8021x-20.txt) */
+#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X"
+/* MAC address ASCII format for non-802.1X use */
+#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg);
+struct wpabuf * radius_msg_get_buf(struct radius_msg *msg);
+struct radius_msg * radius_msg_new(u8 code, u8 identifier);
+void radius_msg_free(struct radius_msg *msg);
+void radius_msg_dump(struct radius_msg *msg);
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len);
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+			  size_t secret_len, const u8 *req_authenticator);
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len,
+			       const struct radius_hdr *req_hdr);
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+			    size_t secret_len);
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+				 size_t secret_len,
+				 const u8 *req_authenticator);
+int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len);
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len);
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
+					     const u8 *data, size_t data_len);
+struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
+		       size_t data_len);
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg);
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len, struct radius_msg *sent_msg,
+		      int auth);
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len, const u8 *req_auth);
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+			 u8 type);
+void radius_msg_make_authenticator(struct radius_msg *msg,
+				   const u8 *data, size_t len);
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+		       const u8 *secret, size_t secret_len);
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+			  const u8 *secret, size_t secret_len);
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+			     const u8 *req_authenticator,
+			     const u8 *secret, size_t secret_len,
+			     const u8 *send_key, size_t send_key_len,
+			     const u8 *recv_key, size_t recv_key_len);
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+		       size_t len);
+int radius_user_password_hide(struct radius_msg *msg,
+			      const u8 *data, size_t data_len,
+			      const u8 *secret, size_t secret_len,
+			      u8 *buf, size_t buf_len);
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+				  const u8 *data, size_t data_len,
+				  const u8 *secret, size_t secret_len);
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
+int radius_msg_get_vlanid(struct radius_msg *msg);
+char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
+				      const u8 *secret, size_t secret_len,
+				      struct radius_msg *sent_msg, size_t n);
+
+static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
+					    u32 value)
+{
+	u32 val = htonl(value);
+	return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL;
+}
+
+static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type,
+					    u32 *value)
+{
+	u32 val;
+	int res;
+	res = radius_msg_get_attr(msg, type, (u8 *) &val, 4);
+	if (res != 4)
+		return -1;
+
+	*value = ntohl(val);
+	return 0;
+}
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+			    size_t *len, const u8 *start);
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len);
+
+
+struct radius_attr_data {
+	u8 *data;
+	size_t len;
+};
+
+struct radius_class_data {
+	struct radius_attr_data *attr;
+	size_t count;
+};
+
+void radius_free_class(struct radius_class_data *c);
+int radius_copy_class(struct radius_class_data *dst,
+		      const struct radius_class_data *src);
+
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs);
+
+#endif /* RADIUS_H */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/tdls.c b/freebsd/contrib/wpa/src/rsn_supp/tdls.c
new file mode 100644
index 00000000000..8d0359d1cad
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/tdls.c
@@ -0,0 +1,3010 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant - TDLS
+ * Copyright (c) 2010-2011, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/os.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "rsn_supp/wpa_i.h"
+#include "drivers/driver.h"
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_TDLS_TESTING
+#define TDLS_TESTING_LONG_FRAME BIT(0)
+#define TDLS_TESTING_ALT_RSN_IE BIT(1)
+#define TDLS_TESTING_DIFF_BSSID BIT(2)
+#define TDLS_TESTING_SHORT_LIFETIME BIT(3)
+#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
+#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
+#define TDLS_TESTING_LONG_LIFETIME BIT(6)
+#define TDLS_TESTING_CONCURRENT_INIT BIT(7)
+#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
+#define TDLS_TESTING_DECLINE_RESP BIT(9)
+#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
+#define TDLS_TESTING_WRONG_MIC BIT(11)
+unsigned int tdls_testing = 0;
+#endif /* CONFIG_TDLS_TESTING */
+
+#define TPK_LIFETIME 43200 /* 12 hours */
+#define TPK_M1_RETRY_COUNT 3
+#define TPK_M1_TIMEOUT 5000 /* in milliseconds */
+#define TPK_M2_RETRY_COUNT 10
+#define TPK_M2_TIMEOUT 500 /* in milliseconds */
+
+#define TDLS_MIC_LEN		16
+
+#define TDLS_TIMEOUT_LEN	4
+
+struct wpa_tdls_ftie {
+	u8 ie_type; /* FTIE */
+	u8 ie_len;
+	u8 mic_ctrl[2];
+	u8 mic[TDLS_MIC_LEN];
+	u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
+	u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
+	/* followed by optional elements */
+} STRUCT_PACKED;
+
+struct wpa_tdls_timeoutie {
+	u8 ie_type; /* Timeout IE */
+	u8 ie_len;
+	u8 interval_type;
+	u8 value[TDLS_TIMEOUT_LEN];
+} STRUCT_PACKED;
+
+struct wpa_tdls_lnkid {
+	u8 ie_type; /* Link Identifier IE */
+	u8 ie_len;
+	u8 bssid[ETH_ALEN];
+	u8 init_sta[ETH_ALEN];
+	u8 resp_sta[ETH_ALEN];
+} STRUCT_PACKED;
+
+/* TDLS frame headers as per IEEE Std 802.11z-2010 */
+struct wpa_tdls_frame {
+	u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
+	u8 category; /* Category */
+	u8 action; /* Action (enum tdls_frame_type) */
+} STRUCT_PACKED;
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
+static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
+				       struct wpa_tdls_peer *peer);
+static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
+				  u16 reason_code);
+
+
+#define TDLS_MAX_IE_LEN 80
+#define IEEE80211_MAX_SUPP_RATES 32
+
+struct wpa_tdls_peer {
+	struct wpa_tdls_peer *next;
+	unsigned int reconfig_key:1;
+	int initiator; /* whether this end was initiator for TDLS setup */
+	u8 addr[ETH_ALEN]; /* other end MAC address */
+	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+	u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
+	u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
+	size_t rsnie_i_len;
+	u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
+	size_t rsnie_p_len;
+	u32 lifetime;
+	int cipher; /* Selected cipher (WPA_CIPHER_*) */
+	u8 dtoken;
+
+	struct tpk {
+		u8 kck[16]; /* TPK-KCK */
+		u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
+	} tpk;
+	int tpk_set;
+	int tpk_success;
+	int tpk_in_progress;
+
+	struct tpk_timer {
+		u8 dest[ETH_ALEN];
+		int count;      /* Retry Count */
+		int timer;      /* Timeout in milliseconds */
+		u8 action_code; /* TDLS frame type */
+		u8 dialog_token;
+		u16 status_code;
+		u32 peer_capab;
+		int buf_len;    /* length of TPK message for retransmission */
+		u8 *buf;        /* buffer for TPK message */
+	} sm_tmr;
+
+	u16 capability;
+
+	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+	size_t supp_rates_len;
+
+	struct ieee80211_ht_capabilities *ht_capabilities;
+	struct ieee80211_vht_capabilities *vht_capabilities;
+
+	u8 qos_info;
+
+	u16 aid;
+
+	u8 *ext_capab;
+	size_t ext_capab_len;
+
+	u8 *supp_channels;
+	size_t supp_channels_len;
+
+	u8 *supp_oper_classes;
+	size_t supp_oper_classes_len;
+
+	u8 wmm_capable;
+
+	/* channel switch currently enabled */
+	int chan_switch_enabled;
+};
+
+
+static int wpa_tdls_get_privacy(struct wpa_sm *sm)
+{
+	/*
+	 * Get info needed from supplicant to check if the current BSS supports
+	 * security. Other than OPEN mode, rest are considered secured
+	 * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
+	 */
+	return sm->pairwise_cipher != WPA_CIPHER_NONE;
+}
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+	os_memcpy(pos, ie, ie_len);
+	return pos + ie_len;
+}
+
+
+static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
+			   0, 0, NULL, 0, NULL, 0) < 0) {
+		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
+			   "the driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	u8 key_len;
+	u8 rsc[6];
+	enum wpa_alg alg;
+
+	os_memset(rsc, 0, 6);
+
+	switch (peer->cipher) {
+	case WPA_CIPHER_CCMP:
+		alg = WPA_ALG_CCMP;
+		key_len = 16;
+		break;
+	case WPA_CIPHER_NONE:
+		wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
+			   "NONE - do not use pairwise keys");
+		return -1;
+	default:
+		wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
+			   sm->pairwise_cipher);
+		return -1;
+	}
+
+	if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
+			   rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
+		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
+			   "driver");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
+				 u8 action_code, u8 dialog_token,
+				 u16 status_code, u32 peer_capab,
+				 int initiator, const u8 *buf, size_t len)
+{
+	return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
+				     status_code, peer_capab, initiator, buf,
+				     len);
+}
+
+
+static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
+			     u8 dialog_token, u16 status_code, u32 peer_capab,
+			     int initiator, const u8 *msg, size_t msg_len)
+{
+	struct wpa_tdls_peer *peer;
+
+	wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
+		   "dialog_token=%u status_code=%u peer_capab=%u initiator=%d "
+		   "msg_len=%u",
+		   MAC2STR(dest), action_code, dialog_token, status_code,
+		   peer_capab, initiator, (unsigned int) msg_len);
+
+	if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
+				  status_code, peer_capab, initiator, msg,
+				  msg_len)) {
+		wpa_printf(MSG_INFO, "TDLS: Failed to send message "
+			   "(action_code=%u)", action_code);
+		return -1;
+	}
+
+	if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
+	    action_code == WLAN_TDLS_TEARDOWN ||
+	    action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
+	    action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
+		return 0; /* No retries */
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+			   "retry " MACSTR, MAC2STR(dest));
+		return 0;
+	}
+
+	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+		peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
+		peer->sm_tmr.timer = TPK_M2_TIMEOUT;
+	} else {
+		peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
+		peer->sm_tmr.timer = TPK_M1_TIMEOUT;
+	}
+
+	/* Copy message to resend on timeout */
+	os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
+	peer->sm_tmr.action_code = action_code;
+	peer->sm_tmr.dialog_token = dialog_token;
+	peer->sm_tmr.status_code = status_code;
+	peer->sm_tmr.peer_capab = peer_capab;
+	peer->sm_tmr.buf_len = msg_len;
+	os_free(peer->sm_tmr.buf);
+	peer->sm_tmr.buf = os_malloc(msg_len);
+	if (peer->sm_tmr.buf == NULL)
+		return -1;
+	os_memcpy(peer->sm_tmr.buf, msg, msg_len);
+
+	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
+		   "(action_code=%u)", action_code);
+	eloop_register_timeout(peer->sm_tmr.timer / 1000,
+			       (peer->sm_tmr.timer % 1000) * 1000,
+			       wpa_tdls_tpk_retry_timeout, sm, peer);
+	return 0;
+}
+
+
+static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+				u16 reason_code)
+{
+	int ret;
+
+	ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
+	/* disable the link after teardown was sent */
+	wpa_tdls_disable_peer_link(sm, peer);
+
+	return ret;
+}
+
+
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+
+	struct wpa_sm *sm = eloop_ctx;
+	struct wpa_tdls_peer *peer = timeout_ctx;
+
+	if (peer->sm_tmr.count) {
+		peer->sm_tmr.count--;
+
+		wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
+			   "(action_code=%u)",
+			   peer->sm_tmr.action_code);
+
+		if (peer->sm_tmr.buf == NULL) {
+			wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
+				   "for action_code=%u",
+				   peer->sm_tmr.action_code);
+			eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
+					     peer);
+			return;
+		}
+
+		/* resend TPK Handshake Message to Peer */
+		if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
+					  peer->sm_tmr.action_code,
+					  peer->sm_tmr.dialog_token,
+					  peer->sm_tmr.status_code,
+					  peer->sm_tmr.peer_capab,
+					  peer->initiator,
+					  peer->sm_tmr.buf,
+					  peer->sm_tmr.buf_len)) {
+			wpa_printf(MSG_INFO, "TDLS: Failed to retry "
+				   "transmission");
+		}
+
+		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+		eloop_register_timeout(peer->sm_tmr.timer / 1000,
+				       (peer->sm_tmr.timer % 1000) * 1000,
+				       wpa_tdls_tpk_retry_timeout, sm, peer);
+	} else {
+		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+		wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	}
+}
+
+
+static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
+					      struct wpa_tdls_peer *peer,
+					      u8 action_code)
+{
+	if (action_code == peer->sm_tmr.action_code) {
+		wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
+			   "action_code=%u", action_code);
+
+		/* Cancel Timeout registered */
+		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+		/* free all resources meant for retry */
+		os_free(peer->sm_tmr.buf);
+		peer->sm_tmr.buf = NULL;
+
+		peer->sm_tmr.count = 0;
+		peer->sm_tmr.timer = 0;
+		peer->sm_tmr.buf_len = 0;
+		peer->sm_tmr.action_code = 0xff;
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
+			   "(Unknown action_code=%u)", action_code);
+	}
+}
+
+
+static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
+				  const u8 *own_addr, const u8 *bssid)
+{
+	u8 key_input[SHA256_MAC_LEN];
+	const u8 *nonce[2];
+	size_t len[2];
+	u8 data[3 * ETH_ALEN];
+
+	/* IEEE Std 802.11z-2010 8.5.9.1:
+	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+	 */
+	len[0] = WPA_NONCE_LEN;
+	len[1] = WPA_NONCE_LEN;
+	if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
+		nonce[0] = peer->inonce;
+		nonce[1] = peer->rnonce;
+	} else {
+		nonce[0] = peer->rnonce;
+		nonce[1] = peer->inonce;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
+	sha256_vector(2, nonce, len, key_input);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
+			key_input, SHA256_MAC_LEN);
+
+	/*
+	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
+	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
+	 * TODO: is N_KEY really included in KDF Context and if so, in which
+	 * presentation format (little endian 16-bit?) is it used? It gets
+	 * added by the KDF anyway..
+	 */
+
+	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
+		os_memcpy(data, own_addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
+	} else {
+		os_memcpy(data, peer->addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
+	}
+	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
+
+	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
+		   (u8 *) &peer->tpk, sizeof(peer->tpk));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
+			peer->tpk.kck, sizeof(peer->tpk.kck));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
+			peer->tpk.tk, sizeof(peer->tpk.tk));
+	peer->tpk_set = 1;
+}
+
+
+/**
+ * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
+ * @kck: TPK-KCK
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @rsnie: Pointer to the beginning of RSN IE used for handshake
+ * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
+			     const u8 *rsnie, const u8 *timeoutie,
+			     const u8 *ftie, u8 *mic)
+{
+	u8 *buf, *pos;
+	struct wpa_tdls_ftie *_ftie;
+	const struct wpa_tdls_lnkid *_lnkid;
+	int ret;
+	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
+		2 + timeoutie[1] + 2 + ftie[1];
+	buf = os_zalloc(len);
+	if (!buf) {
+		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+		return -1;
+	}
+
+	pos = buf;
+	_lnkid = (const struct wpa_tdls_lnkid *) lnkid;
+	/* 1) TDLS initiator STA MAC address */
+	os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 2) TDLS responder STA MAC address */
+	os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 3) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 4) Link Identifier IE */
+	os_memcpy(pos, lnkid, 2 + lnkid[1]);
+	pos += 2 + lnkid[1];
+	/* 5) RSN IE */
+	os_memcpy(pos, rsnie, 2 + rsnie[1]);
+	pos += 2 + rsnie[1];
+	/* 6) Timeout Interval IE */
+	os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
+	pos += 2 + timeoutie[1];
+	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, ftie, 2 + ftie[1]);
+	_ftie = (struct wpa_tdls_ftie *) pos;
+	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+	pos += 2 + ftie[1];
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+	ret = omac1_aes_128(kck, buf, pos - buf, mic);
+	os_free(buf);
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	return ret;
+}
+
+
+/**
+ * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
+ * @kck: TPK-KCK
+ * @trans_seq: Transaction Sequence Number (4 - Teardown)
+ * @rcode: Reason code for Teardown
+ * @dtoken: Dialog Token used for that particular link
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
+				     u8 dtoken, const u8 *lnkid,
+				     const u8 *ftie, u8 *mic)
+{
+	u8 *buf, *pos;
+	struct wpa_tdls_ftie *_ftie;
+	int ret;
+	int len;
+
+	if (lnkid == NULL)
+		return -1;
+
+	len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
+		sizeof(trans_seq) + 2 + ftie[1];
+
+	buf = os_zalloc(len);
+	if (!buf) {
+		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+		return -1;
+	}
+
+	pos = buf;
+	/* 1) Link Identifier IE */
+	os_memcpy(pos, lnkid, 2 + lnkid[1]);
+	pos += 2 + lnkid[1];
+	/* 2) Reason Code */
+	WPA_PUT_LE16(pos, rcode);
+	pos += sizeof(rcode);
+	/* 3) Dialog token */
+	*pos++ = dtoken;
+	/* 4) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, ftie, 2 + ftie[1]);
+	_ftie = (struct wpa_tdls_ftie *) pos;
+	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+	pos += 2 + ftie[1];
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+	ret = omac1_aes_128(kck, buf, pos - buf, mic);
+	os_free(buf);
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	return ret;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
+					  struct wpa_tdls_peer *peer,
+					  const u8 *lnkid, const u8 *timeoutie,
+					  const struct wpa_tdls_ftie *ftie)
+{
+	u8 mic[16];
+
+	if (peer->tpk_set) {
+		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
+				  peer->rsnie_p, timeoutie, (u8 *) ftie,
+				  mic);
+		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
+			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
+				   "dropping packet");
+			wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
+				    ftie->mic, 16);
+			wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
+				    mic, 16);
+			return -1;
+		}
+	} else {
+		wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
+			   "TPK not set - dropping packet");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic_teardown(
+	u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
+	const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
+{
+	u8 mic[16];
+
+	if (peer->tpk_set) {
+		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
+					  dtoken, lnkid, (u8 *) ftie, mic);
+		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
+			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
+				   "dropping packet");
+			return -1;
+		}
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
+			   "MIC, TPK not set - dropping packet");
+		return -1;
+	}
+	return 0;
+}
+
+
+static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_sm *sm = eloop_ctx;
+	struct wpa_tdls_peer *peer = timeout_ctx;
+
+	/*
+	 * On TPK lifetime expiration, we have an option of either tearing down
+	 * the direct link or trying to re-initiate it. The selection of what
+	 * to do is not strictly speaking controlled by our role in the expired
+	 * link, but for now, use that to select whether to renew or tear down
+	 * the link.
+	 */
+
+	if (peer->initiator) {
+		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+			   " - try to renew", MAC2STR(peer->addr));
+		wpa_tdls_start(sm, peer->addr);
+	} else {
+		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+			   " - tear down", MAC2STR(peer->addr));
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	}
+}
+
+
+static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
+					   struct wpa_tdls_peer *peer)
+{
+	struct wpa_tdls_peer *cur, *prev;
+
+	cur = sm->tdls;
+	prev = NULL;
+	while (cur && cur != peer) {
+		prev = cur;
+		cur = cur->next;
+	}
+
+	if (cur != peer) {
+		wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
+			   " to remove it from the list",
+			   MAC2STR(peer->addr));
+		return;
+	}
+
+	if (prev)
+		prev->next = peer->next;
+	else
+		sm->tdls = peer->next;
+}
+
+
+static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
+		   MAC2STR(peer->addr));
+	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+	peer->reconfig_key = 0;
+	peer->initiator = 0;
+	peer->tpk_in_progress = 0;
+	os_free(peer->sm_tmr.buf);
+	peer->sm_tmr.buf = NULL;
+	os_free(peer->ht_capabilities);
+	peer->ht_capabilities = NULL;
+	os_free(peer->vht_capabilities);
+	peer->vht_capabilities = NULL;
+	os_free(peer->ext_capab);
+	peer->ext_capab = NULL;
+	os_free(peer->supp_channels);
+	peer->supp_channels = NULL;
+	os_free(peer->supp_oper_classes);
+	peer->supp_oper_classes = NULL;
+	peer->rsnie_i_len = peer->rsnie_p_len = 0;
+	peer->cipher = 0;
+	peer->qos_info = 0;
+	peer->wmm_capable = 0;
+	peer->tpk_set = peer->tpk_success = 0;
+	peer->chan_switch_enabled = 0;
+	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
+	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
+	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
+}
+
+
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	wpa_tdls_peer_clear(sm, peer);
+	wpa_tdls_peer_remove_from_list(sm, peer);
+	os_free(peer);
+}
+
+
+static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+			    struct wpa_tdls_lnkid *lnkid)
+{
+	lnkid->ie_type = WLAN_EID_LINK_ID;
+	lnkid->ie_len = 3 * ETH_ALEN;
+	os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
+	if (peer->initiator) {
+		os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
+		os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
+	} else {
+		os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
+		os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
+	}
+}
+
+
+static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
+				  u16 reason_code)
+{
+	struct wpa_tdls_peer *peer;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_lnkid lnkid;
+	u8 dialog_token;
+	u8 *rbuf, *pos;
+	int ielen;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	/* Find the node and free from the list */
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+			   "Teardown " MACSTR, MAC2STR(addr));
+		return 0;
+	}
+
+	/* Cancel active channel switch before teardown */
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
+			   " to base channel", MAC2STR(addr));
+		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+	}
+
+	dialog_token = peer->dtoken;
+
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
+		   MAC2STR(addr));
+
+	ielen = 0;
+	if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
+		/* To add FTIE for Teardown request and compute MIC */
+		ielen += sizeof(*ftie);
+#ifdef CONFIG_TDLS_TESTING
+		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+			ielen += 170;
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	rbuf = os_zalloc(ielen + 1);
+	if (rbuf == NULL)
+		return -1;
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+		goto skip_ies;
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	/* Using the recent nonce which should be for CONFIRM frame */
+	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+	pos = (u8 *) (ftie + 1);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
+		    (u8 *) ftie, pos - (u8 *) ftie);
+
+	/* compute MIC before sending */
+	wpa_tdls_linkid(sm, peer, &lnkid);
+	wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
+				  dialog_token, (u8 *) &lnkid, (u8 *) ftie,
+				  ftie->mic);
+
+skip_ies:
+	/* TODO: register for a Timeout handler, if Teardown is not received at
+	 * the other end, then try again another time */
+
+	/* request driver to send Teardown using this FTIE */
+	wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
+			  reason_code, 0, peer->initiator, rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return 0;
+}
+
+
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
+		   " for link Teardown", MAC2STR(addr));
+		return -1;
+	}
+
+	if (!peer->tpk_success) {
+		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+		   " not connected - cannot Teardown link", MAC2STR(addr));
+		return -1;
+	}
+
+	return wpa_tdls_do_teardown(sm, peer, reason_code);
+}
+
+
+static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
+				       struct wpa_tdls_peer *peer)
+{
+	wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+	wpa_tdls_peer_free(sm, peer);
+}
+
+
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (!peer || !peer->tpk_success) {
+		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+			   " not connected - cannot teardown unreachable link",
+			   MAC2STR(addr));
+		return;
+	}
+
+	if (wpa_tdls_is_external_setup(sm)) {
+		/*
+		 * Get us on the base channel, disable the link, send a
+		 * teardown packet through the AP, and then reset link data.
+		 */
+		if (peer->chan_switch_enabled)
+			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+		wpa_tdls_send_teardown(sm, addr,
+				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
+		wpa_tdls_peer_free(sm, peer);
+	} else {
+		wpa_tdls_disable_peer_link(sm, peer);
+	}
+}
+
+
+const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return "disabled";
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL)
+		return "peer does not exist";
+
+	if (!peer->tpk_success)
+		return "peer not connected";
+
+	return "connected";
+}
+
+
+static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
+				  const u8 *buf, size_t len)
+{
+	struct wpa_tdls_peer *peer = NULL;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_lnkid *lnkid;
+	struct wpa_eapol_ie_parse kde;
+	u16 reason_code;
+	const u8 *pos;
+	int ielen;
+
+	/* Find the node and free from the list */
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+			   "Teardown " MACSTR, MAC2STR(src_addr));
+		return 0;
+	}
+
+	pos = buf;
+	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+	reason_code = WPA_GET_LE16(pos);
+	pos += 2;
+
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
+		   " (reason code %u)", MAC2STR(src_addr), reason_code);
+
+	ielen = len - (pos - buf); /* start of IE in buf */
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
+			   "Teardown");
+		return -1;
+	}
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+		goto skip_ftie;
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
+		return -1;
+	}
+
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+	/* Process MIC check to see if TDLS Teardown is right */
+	if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
+						    peer->dtoken, peer,
+						    (u8 *) lnkid, ftie) < 0) {
+		wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
+			   "Teardown Request from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+
+skip_ftie:
+	/*
+	 * Request the driver to disable the direct link and clear associated
+	 * keys.
+	 */
+	wpa_tdls_disable_peer_link(sm, peer);
+	return 0;
+}
+
+
+/**
+ * wpa_tdls_send_error - To send suitable TDLS status response with
+ *	appropriate status code mentioning reason for error/failure.
+ * @dst 	- MAC addr of Peer station
+ * @tdls_action - TDLS frame type for which error code is sent
+ * @initiator   - was this end the initiator of the connection
+ * @status 	- status code mentioning reason
+ */
+
+static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
+			       u8 tdls_action, u8 dialog_token, int initiator,
+			       u16 status)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
+		   " (action=%u status=%u)",
+		   MAC2STR(dst), tdls_action, status);
+	return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
+				 0, initiator, NULL, 0);
+}
+
+
+static struct wpa_tdls_peer *
+wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (existing)
+		*existing = 0;
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
+			if (existing)
+				*existing = 1;
+			return peer; /* re-use existing entry */
+		}
+	}
+
+	wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
+		   MAC2STR(addr));
+
+	peer = os_zalloc(sizeof(*peer));
+	if (peer == NULL)
+		return NULL;
+
+	os_memcpy(peer->addr, addr, ETH_ALEN);
+	peer->next = sm->tdls;
+	sm->tdls = peer;
+
+	return peer;
+}
+
+
+static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
+				struct wpa_tdls_peer *peer)
+{
+	size_t buf_len;
+	struct wpa_tdls_timeoutie timeoutie;
+	u16 rsn_capab;
+	struct wpa_tdls_ftie *ftie;
+	u8 *rbuf, *pos, *count_pos;
+	u16 count;
+	struct rsn_ie_hdr *hdr;
+	int status;
+
+	if (!wpa_tdls_get_privacy(sm)) {
+		wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
+		peer->rsnie_i_len = 0;
+		goto skip_rsnie;
+	}
+
+	/*
+	 * TPK Handshake Message 1:
+	 * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
+	 * Timeout Interval IE))
+	 */
+
+	/* Filling RSN IE */
+	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+
+	pos = (u8 *) (hdr + 1);
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+	count_pos = pos;
+	pos += 2;
+
+	count = 0;
+
+	/*
+	 * AES-CCMP is the default Encryption preferred for TDLS, so
+	 * RSN IE is filled only with CCMP CIPHER
+	 * Note: TKIP is not used to encrypt TDLS link.
+	 *
+	 * Regardless of the cipher used on the AP connection, select CCMP
+	 * here.
+	 */
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+	count++;
+
+	WPA_PUT_LE16(count_pos, count);
+
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+	pos += RSN_SELECTOR_LEN;
+
+	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+		wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
+			   "testing");
+		rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	WPA_PUT_LE16(pos, rsn_capab);
+	pos += 2;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+		/* Number of PMKIDs */
+		*pos++ = 0x00;
+		*pos++ = 0x00;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	hdr->len = (pos - peer->rsnie_i) - 2;
+	peer->rsnie_i_len = pos - peer->rsnie_i;
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+		    peer->rsnie_i, peer->rsnie_i_len);
+
+skip_rsnie:
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm))
+		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+			sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+	if (wpa_tdls_get_privacy(sm) &&
+	    (tdls_testing & TDLS_TESTING_LONG_FRAME))
+		buf_len += 170;
+	if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
+		buf_len += sizeof(struct wpa_tdls_lnkid);
+#endif /* CONFIG_TDLS_TESTING */
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL) {
+		wpa_tdls_peer_free(sm, peer);
+		return -1;
+	}
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+
+	/* Initiator RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+	if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"TDLS: Failed to get random data for initiator Nonce");
+		os_free(rbuf);
+		wpa_tdls_peer_free(sm, peer);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
+		    peer->inonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
+		    (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
+
+	pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* Lifetime */
+	peer->lifetime = TPK_LIFETIME;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
+			   "lifetime");
+		peer->lifetime = 301;
+	}
+	if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
+			   "lifetime");
+		peer->lifetime = 0xffffffff;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), peer->lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+
+skip_ies:
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
+			   "Link Identifier");
+		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
+		wpa_tdls_linkid(sm, peer, l);
+		l->bssid[5] ^= 0x01;
+		pos += sizeof(*l);
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
+		   "Handshake Message 1 (peer " MACSTR ")",
+		   MAC2STR(peer->addr));
+
+	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
+				   1, 0, 0, peer->initiator, rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
+				const unsigned char *src_addr, u8 dtoken,
+				struct wpa_tdls_lnkid *lnkid,
+				const struct wpa_tdls_peer *peer)
+{
+	u8 *rbuf, *pos;
+	size_t buf_len;
+	u32 lifetime;
+	struct wpa_tdls_timeoutie timeoutie;
+	struct wpa_tdls_ftie *ftie;
+	int status;
+
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm)) {
+		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+		 * Lifetime */
+		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+			sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+			buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL)
+		return -1;
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+
+	/* Peer RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	/* TODO: ftie->mic_control to set 2-RESPONSE */
+	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
+		    (u8 *) ftie, sizeof(*ftie));
+
+	pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* Lifetime */
+	lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+			   "lifetime in response");
+		lifetime++;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
+		   lifetime);
+
+	/* compute MIC before sending */
+	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
+			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+		ftie->mic[0] ^= 0x01;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+skip_ies:
+	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
+				   dtoken, 0, 0, peer->initiator, rbuf,
+				   pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
+				const unsigned char *src_addr, u8 dtoken,
+				struct wpa_tdls_lnkid *lnkid,
+				const struct wpa_tdls_peer *peer)
+{
+	u8 *rbuf, *pos;
+	size_t buf_len;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_timeoutie timeoutie;
+	u32 lifetime;
+	int status;
+	u32 peer_capab = 0;
+
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm)) {
+		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+		 * Lifetime */
+		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+			sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+			buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL)
+		return -1;
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+
+	/* Peer RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+	ftie = (struct wpa_tdls_ftie *) pos;
+	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+	/*TODO: ftie->mic_control to set 3-CONFIRM */
+	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+	pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+			   "FTIE");
+		ftie->ie_len += 170;
+		*pos++ = 255; /* FTIE subelem */
+		*pos++ = 168; /* FTIE subelem length */
+		pos += 168;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* Lifetime */
+	lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+			   "lifetime in confirm");
+		lifetime++;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
+		   lifetime);
+
+	/* compute MIC before sending */
+	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
+			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+		ftie->mic[0] ^= 0x01;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+skip_ies:
+
+	if (peer->vht_capabilities)
+		peer_capab |= TDLS_PEER_VHT;
+	if (peer->ht_capabilities)
+		peer_capab |= TDLS_PEER_HT;
+	if (peer->wmm_capable)
+		peer_capab |= TDLS_PEER_WMM;
+
+	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
+				   dtoken, 0, peer_capab, peer->initiator,
+				   rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
+					    struct wpa_tdls_peer *peer,
+					    u8 dialog_token)
+{
+	size_t buf_len = 0;
+	struct wpa_tdls_timeoutie timeoutie;
+	u16 rsn_capab;
+	u8 *rbuf, *pos, *count_pos;
+	u16 count;
+	struct rsn_ie_hdr *hdr;
+	int status;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
+		   "(peer " MACSTR ")", MAC2STR(peer->addr));
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_rsn_ies;
+
+	/* Filling RSN IE */
+	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+	count_pos = pos;
+	pos += 2;
+	count = 0;
+
+	/*
+	* AES-CCMP is the default encryption preferred for TDLS, so
+	* RSN IE is filled only with CCMP cipher suite.
+	* Note: TKIP is not used to encrypt TDLS link.
+	*
+	* Regardless of the cipher used on the AP connection, select CCMP
+	* here.
+	*/
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+	count++;
+	WPA_PUT_LE16(count_pos, count);
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+	pos += RSN_SELECTOR_LEN;
+
+	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+	WPA_PUT_LE16(pos, rsn_capab);
+	pos += 2;
+	hdr->len = (pos - (u8 *) hdr) - 2;
+	peer->rsnie_i_len = pos - peer->rsnie_i;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
+		    (u8 *) hdr, hdr->len + 2);
+skip_rsn_ies:
+	buf_len = 0;
+	if (wpa_tdls_get_privacy(sm)) {
+		/* Peer RSN IE, Lifetime */
+		buf_len += peer->rsnie_i_len +
+			sizeof(struct wpa_tdls_timeoutie);
+	}
+	rbuf = os_zalloc(buf_len + 1);
+	if (rbuf == NULL) {
+		wpa_tdls_peer_free(sm, peer);
+		return -1;
+	}
+	pos = rbuf;
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_ies;
+	/* Initiator RSN IE */
+	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+	/* Lifetime */
+	peer->lifetime = TPK_LIFETIME;
+	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+				     sizeof(timeoutie), peer->lifetime);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+skip_ies:
+	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
+				   dialog_token, 0, 0, 0, rbuf, pos - rbuf);
+	os_free(rbuf);
+
+	return status;
+}
+
+
+static int
+wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_eapol_ie_parse kde;
+	const struct wpa_tdls_lnkid *lnkid;
+	struct wpa_tdls_peer *peer;
+	size_t min_req_len = sizeof(struct wpa_tdls_frame) +
+		1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
+	u8 dialog_token;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
+		   MAC2STR(addr));
+
+	if (len < min_req_len) {
+		wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
+			   "%d", (int) len);
+		return -1;
+	}
+
+	dialog_token = buf[sizeof(struct wpa_tdls_frame)];
+
+	/*
+	 * Some APs will tack on a weird IE to the end of a TDLS
+	 * discovery request packet. This needn't fail the response,
+	 * since the required IE are verified separately.
+	 */
+	if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
+				     len - (sizeof(struct wpa_tdls_frame) + 1),
+				     &kde) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
+	}
+
+	if (!kde.lnkid) {
+		wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
+			   "Request");
+		return -1;
+	}
+
+	lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
+			   " BSS " MACSTR, MAC2STR(lnkid->bssid));
+		return -1;
+	}
+
+	peer = wpa_tdls_add_peer(sm, addr, NULL);
+	if (peer == NULL)
+		return -1;
+
+	return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
+}
+
+
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
+{
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
+		   MACSTR, MAC2STR(addr));
+	return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
+				 1, 0, 0, 1, NULL, 0);
+}
+
+
+static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
+			   struct wpa_tdls_peer *peer)
+{
+	if (!kde->supp_rates) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
+		return -1;
+	}
+	peer->supp_rates_len = merge_byte_arrays(
+		peer->supp_rates, sizeof(peer->supp_rates),
+		kde->supp_rates + 2, kde->supp_rates_len - 2,
+		kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
+		kde->ext_supp_rates_len - 2);
+	return 0;
+}
+
+
+static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
+			      struct wpa_tdls_peer *peer)
+{
+	if (!kde->ht_capabilities) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
+			   "received");
+		return 0;
+	}
+
+	if (!peer->ht_capabilities) {
+		peer->ht_capabilities =
+                        os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+		if (peer->ht_capabilities == NULL)
+                        return -1;
+	}
+
+	os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
+                  sizeof(struct ieee80211_ht_capabilities));
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
+		    (u8 *) peer->ht_capabilities,
+		    sizeof(struct ieee80211_ht_capabilities));
+
+	return 0;
+}
+
+
+static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
+			      struct wpa_tdls_peer *peer)
+{
+	if (!kde->vht_capabilities) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
+			   "received");
+		return 0;
+	}
+
+	if (!peer->vht_capabilities) {
+		peer->vht_capabilities =
+                        os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (peer->vht_capabilities == NULL)
+                        return -1;
+	}
+
+	os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
+                  sizeof(struct ieee80211_vht_capabilities));
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
+		    (u8 *) peer->vht_capabilities,
+		    sizeof(struct ieee80211_vht_capabilities));
+
+	return 0;
+}
+
+
+static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
+			       struct wpa_tdls_peer *peer)
+{
+	if (!kde->ext_capab) {
+		wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
+			   "received");
+		return 0;
+	}
+
+	if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
+		/* Need to allocate buffer to fit the new information */
+		os_free(peer->ext_capab);
+		peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
+		if (peer->ext_capab == NULL)
+			return -1;
+	}
+
+	peer->ext_capab_len = kde->ext_capab_len - 2;
+	os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
+
+	return 0;
+}
+
+
+static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
+			       struct wpa_tdls_peer *peer)
+{
+	struct wmm_information_element *wmm;
+
+	if (!kde->wmm) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
+		return 0;
+	}
+
+	if (kde->wmm_len < sizeof(struct wmm_information_element)) {
+		wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
+		return -1;
+	}
+
+	wmm = (struct wmm_information_element *) kde->wmm;
+	peer->qos_info = wmm->qos_info;
+
+	peer->wmm_capable = 1;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
+	return 0;
+}
+
+
+static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
+				   struct wpa_tdls_peer *peer)
+{
+	if (!kde->supp_channels) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
+		return 0;
+	}
+
+	if (!peer->supp_channels ||
+	    peer->supp_channels_len < kde->supp_channels_len) {
+		os_free(peer->supp_channels);
+		peer->supp_channels = os_zalloc(kde->supp_channels_len);
+		if (peer->supp_channels == NULL)
+			return -1;
+	}
+
+	peer->supp_channels_len = kde->supp_channels_len;
+
+	os_memcpy(peer->supp_channels, kde->supp_channels,
+		  peer->supp_channels_len);
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
+		    (u8 *) peer->supp_channels, peer->supp_channels_len);
+	return 0;
+}
+
+
+static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
+				       struct wpa_tdls_peer *peer)
+{
+	if (!kde->supp_oper_classes) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
+		return 0;
+	}
+
+	if (!peer->supp_oper_classes ||
+	    peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
+		os_free(peer->supp_oper_classes);
+		peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
+		if (peer->supp_oper_classes == NULL)
+			return -1;
+	}
+
+	peer->supp_oper_classes_len = kde->supp_oper_classes_len;
+	os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
+		  peer->supp_oper_classes_len);
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
+		    (u8 *) peer->supp_oper_classes,
+		    peer->supp_oper_classes_len);
+	return 0;
+}
+
+
+static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+				int add)
+{
+	return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
+				       peer->capability,
+				       peer->supp_rates, peer->supp_rates_len,
+				       peer->ht_capabilities,
+				       peer->vht_capabilities,
+				       peer->qos_info, peer->wmm_capable,
+				       peer->ext_capab, peer->ext_capab_len,
+				       peer->supp_channels,
+				       peer->supp_channels_len,
+				       peer->supp_oper_classes,
+				       peer->supp_oper_classes_len);
+}
+
+
+static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_tdls_peer *peer;
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_ie_data ie;
+	int cipher;
+	const u8 *cpos;
+	struct wpa_tdls_ftie *ftie = NULL;
+	struct wpa_tdls_timeoutie *timeoutie;
+	struct wpa_tdls_lnkid *lnkid;
+	u32 lifetime = 0;
+#if 0
+	struct rsn_ie_hdr *hdr;
+	u8 *pos;
+	u16 rsn_capab;
+	u16 rsn_ver;
+#endif
+	u8 dtoken;
+	u16 ielen;
+	u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	int tdls_prohibited = sm->tdls_prohibited;
+	int existing_peer = 0;
+
+	if (len < 3 + 3)
+		return -1;
+
+	cpos = buf;
+	cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+	/* driver had already verified the frame format */
+	dtoken = *cpos++; /* dialog token */
+
+	wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
+
+	peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
+	if (peer == NULL)
+		goto error;
+
+	/* If found, use existing entry instead of adding a new one;
+	 * how to handle the case where both ends initiate at the
+	 * same time? */
+	if (existing_peer) {
+		if (peer->tpk_success) {
+			wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
+				   "direct link is enabled - tear down the "
+				   "old link first");
+			wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+			wpa_tdls_peer_clear(sm, peer);
+		} else if (peer->initiator) {
+			/*
+			 * An entry is already present, so check if we already
+			 * sent a TDLS Setup Request. If so, compare MAC
+			 * addresses and let the STA with the lower MAC address
+			 * continue as the initiator. The other negotiation is
+			 * terminated.
+			 */
+			if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
+				wpa_printf(MSG_DEBUG, "TDLS: Discard request "
+					   "from peer with higher address "
+					   MACSTR, MAC2STR(src_addr));
+				return -1;
+			} else {
+				wpa_printf(MSG_DEBUG, "TDLS: Accept request "
+					   "from peer with lower address "
+					   MACSTR " (terminate previously "
+					   "initiated negotiation",
+					   MAC2STR(src_addr));
+				wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+						 peer->addr);
+				wpa_tdls_peer_clear(sm, peer);
+			}
+		}
+	}
+
+	/* capability information */
+	peer->capability = WPA_GET_LE16(cpos);
+	cpos += 2;
+
+	ielen = len - (cpos - buf); /* start of IE in buf */
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+			   "TPK M1");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
+		    kde.lnkid, kde.lnkid_len);
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
+		status = WLAN_STATUS_REQUEST_DECLINED;
+		goto error;
+	}
+
+	wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
+		   MAC2STR(src_addr));
+
+	if (copy_supp_rates(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_ht_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_vht_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_ext_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_supp_channels(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
+		goto error;
+
+	peer->qos_info = kde.qosinfo;
+
+	/* Overwrite with the qos_info obtained in WMM IE */
+	if (copy_peer_wmm_capab(&kde, peer) < 0)
+		goto error;
+
+	peer->aid = kde.aid;
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+		peer = wpa_tdls_add_peer(sm, src_addr, NULL);
+		if (peer == NULL)
+			goto error;
+		wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
+			   "TDLS setup - send own request");
+		peer->initiator = 1;
+		wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+					NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0);
+		wpa_tdls_send_tpk_m1(sm, peer);
+	}
+
+	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+	    tdls_prohibited) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+			   "on TDLS");
+		tdls_prohibited = 0;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	if (tdls_prohibited) {
+		wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
+		status = WLAN_STATUS_REQUEST_DECLINED;
+		goto error;
+	}
+
+	if (!wpa_tdls_get_privacy(sm)) {
+		if (kde.rsn_ie) {
+			wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
+				   "security is disabled");
+			status = WLAN_STATUS_SECURITY_DISABLED;
+			goto error;
+		}
+		goto skip_rsn;
+	}
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+	    kde.rsn_ie == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto error;
+	}
+
+	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+		wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
+			   "TPK M1");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	cipher = ie.pairwise_cipher;
+	if (cipher & WPA_CIPHER_CCMP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+		cipher = WPA_CIPHER_CCMP;
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
+		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+		goto error;
+	}
+
+	if ((ie.capabilities &
+	     (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
+	    WPA_CAPABILITY_PEERKEY_ENABLED) {
+		wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
+			   "TPK M1");
+		status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
+		goto error;
+	}
+
+	/* Lifetime */
+	if (kde.key_lifetime == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+	lifetime = WPA_GET_LE32(timeoutie->value);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
+	if (lifetime < 300) {
+		wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+
+skip_rsn:
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+		if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
+			/*
+			 * The request frame from us is going to win, so do not
+			 * replace information based on this request frame from
+			 * the peer.
+			 */
+			goto skip_rsn_check;
+		}
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	peer->initiator = 0; /* Need to check */
+	peer->dtoken = dtoken;
+
+	if (!wpa_tdls_get_privacy(sm)) {
+		peer->rsnie_i_len = 0;
+		peer->rsnie_p_len = 0;
+		peer->cipher = WPA_CIPHER_NONE;
+		goto skip_rsn_check;
+	}
+
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+	os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+	peer->rsnie_i_len = kde.rsn_ie_len;
+	peer->cipher = cipher;
+
+	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
+		/*
+		 * There is no point in updating the RNonce for every obtained
+		 * TPK M1 frame (e.g., retransmission due to timeout) with the
+		 * same INonce (SNonce in FTIE). However, if the TPK M1 is
+		 * retransmitted with a different INonce, update the RNonce
+		 * since this is for a new TDLS session.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: New TPK M1 INonce - generate new RNonce");
+		os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
+		if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
+			wpa_msg(sm->ctx->ctx, MSG_WARNING,
+				"TDLS: Failed to get random data for responder nonce");
+			goto error;
+		}
+	}
+
+#if 0
+	/* get version info from RSNIE received from Peer */
+	hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
+	rsn_ver = WPA_GET_LE16(hdr->version);
+
+	/* use min(peer's version, out version) */
+	if (rsn_ver > RSN_VERSION)
+		rsn_ver = RSN_VERSION;
+
+	hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
+
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, rsn_ver);
+	pos = (u8 *) (hdr + 1);
+
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+	/* Include only the selected cipher in pairwise cipher suite */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	if (cipher == WPA_CIPHER_CCMP)
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	pos += RSN_SELECTOR_LEN;
+
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+	pos += RSN_SELECTOR_LEN;
+
+	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+	WPA_PUT_LE16(pos, rsn_capab);
+	pos += 2;
+
+	hdr->len = (pos - peer->rsnie_p) - 2;
+	peer->rsnie_p_len = pos - peer->rsnie_p;
+#endif
+
+	/* temp fix: validation of RSNIE later */
+	os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
+	peer->rsnie_p_len = peer->rsnie_i_len;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+		    peer->rsnie_p, peer->rsnie_p_len);
+
+	peer->lifetime = lifetime;
+
+	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+skip_rsn_check:
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
+		goto skip_add_peer;
+#endif /* CONFIG_TDLS_TESTING */
+
+	/* add supported rates, capabilities, and qos_info to the TDLS peer */
+	if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
+		goto error;
+
+#ifdef CONFIG_TDLS_TESTING
+skip_add_peer:
+#endif /* CONFIG_TDLS_TESTING */
+	peer->tpk_in_progress = 1;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
+	if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
+		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+		goto error;
+	}
+
+	return 0;
+
+error:
+	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
+			    status);
+	if (peer)
+		wpa_tdls_peer_free(sm, peer);
+	return -1;
+}
+
+
+static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	peer->tpk_success = 1;
+	peer->tpk_in_progress = 0;
+	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+	if (wpa_tdls_get_privacy(sm)) {
+		u32 lifetime = peer->lifetime;
+		/*
+		 * Start the initiator process a bit earlier to avoid race
+		 * condition with the responder sending teardown request.
+		 */
+		if (lifetime > 3 && peer->initiator)
+			lifetime -= 3;
+		eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
+				       sm, peer);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
+			   "expiration");
+		eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+	}
+#endif /* CONFIG_TDLS_TESTING */
+	}
+
+	if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
+		wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
+			   "driver");
+		return -1;
+	}
+	peer->reconfig_key = 0;
+
+	return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
+}
+
+
+static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_tdls_peer *peer;
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_ie_data ie;
+	int cipher;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_timeoutie *timeoutie;
+	struct wpa_tdls_lnkid *lnkid;
+	u32 lifetime;
+	u8 dtoken;
+	int ielen;
+	u16 status;
+	const u8 *pos;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
+		   "(Peer " MACSTR ")", MAC2STR(src_addr));
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+			   "TPK M2: " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+	if (!peer->initiator) {
+		/*
+		 * This may happen if both devices try to initiate TDLS at the
+		 * same time and we accept the TPK M1 from the peer in
+		 * wpa_tdls_process_tpk_m1() and clear our previous state.
+		 */
+		wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
+			   "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
+
+	if (len < 3 + 2 + 1) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	pos = buf;
+	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+	status = WPA_GET_LE16(pos);
+	pos += 2 /* status code */;
+
+	if (status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
+			   status);
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* TODO: need to verify dialog token matches here or in kernel */
+	dtoken = *pos++; /* dialog token */
+
+	wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
+
+	if (len < 3 + 2 + 1 + 2) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	/* capability information */
+	peer->capability = WPA_GET_LE16(pos);
+	pos += 2;
+
+	ielen = len - (pos - buf); /* start of IE in buf */
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
+
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
+		status = WLAN_STATUS_REQUEST_DECLINED;
+		goto error;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+			   "TPK M2");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
+		    kde.lnkid, kde.lnkid_len);
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
+		status = WLAN_STATUS_NOT_IN_SAME_BSS;
+		goto error;
+	}
+
+	if (copy_supp_rates(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_ht_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_vht_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_ext_capab(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_supp_channels(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
+		goto error;
+
+	peer->qos_info = kde.qosinfo;
+
+	/* Overwrite with the qos_info obtained in WMM IE */
+	if (copy_peer_wmm_capab(&kde, peer) < 0)
+		goto error;
+
+	peer->aid = kde.aid;
+
+	if (!wpa_tdls_get_privacy(sm)) {
+		peer->rsnie_p_len = 0;
+		peer->cipher = WPA_CIPHER_NONE;
+		goto skip_rsn;
+	}
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+	    kde.rsn_ie == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
+		    kde.rsn_ie, kde.rsn_ie_len);
+
+	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+		wpa_printf(MSG_INFO,
+			   "TDLS: Too long Responder RSN IE in TPK M2");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	/*
+	 * FIX: bitwise comparison of RSN IE is not the correct way of
+	 * validation this. It can be different, but certain fields must
+	 * match. Since we list only a single pairwise cipher in TPK M1, the
+	 * memcmp is likely to work in most cases, though.
+	 */
+	if (kde.rsn_ie_len != peer->rsnie_i_len ||
+	    os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
+			   "not match with RSN IE used in TPK M1");
+		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
+			    peer->rsnie_i, peer->rsnie_i_len);
+		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
+			    kde.rsn_ie, kde.rsn_ie_len);
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
+	cipher = ie.pairwise_cipher;
+	if (cipher == WPA_CIPHER_CCMP) {
+		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+		cipher = WPA_CIPHER_CCMP;
+	} else {
+		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
+		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+		goto error;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
+		    kde.ftie, sizeof(*ftie));
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
+			   "not match with FTIE SNonce used in TPK M1");
+		/* Silently discard the frame */
+		return -1;
+	}
+
+	/* Responder Nonce and RSN IE */
+	os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
+	os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
+	peer->rsnie_p_len = kde.rsn_ie_len;
+	peer->cipher = cipher;
+
+	/* Lifetime */
+	if (kde.key_lifetime == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+	lifetime = WPA_GET_LE32(timeoutie->value);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
+		   lifetime);
+	if (lifetime != peer->lifetime) {
+		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+			   "TPK M2 (expected %u)", lifetime, peer->lifetime);
+		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+		goto error;
+	}
+
+	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+	/* Process MIC check to see if TPK M2 is right */
+	if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
+					   (u8 *) timeoutie, ftie) < 0) {
+		/* Discard the frame */
+		wpa_tdls_del_key(sm, peer);
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	if (wpa_tdls_set_key(sm, peer) < 0) {
+		/*
+		 * Some drivers may not be able to config the key prior to full
+		 * STA entry having been configured.
+		 */
+		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
+			   "STA entry is complete");
+		peer->reconfig_key = 1;
+	}
+
+skip_rsn:
+	peer->dtoken = dtoken;
+
+	/* add supported rates, capabilities, and qos_info to the TDLS peer */
+	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+		goto error;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
+		   "TPK Handshake Message 3");
+	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
+		goto error;
+
+	if (!peer->tpk_success) {
+		/*
+		 * Enable Link only when tpk_success is 0, signifying that this
+		 * processing of TPK M2 frame is not because of a retransmission
+		 * during TDLS setup handshake.
+		 */
+		ret = wpa_tdls_enable_link(sm, peer);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
+			wpa_tdls_do_teardown(
+				sm, peer,
+				WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+		}
+	}
+	return ret;
+
+error:
+	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
+			    status);
+	wpa_tdls_disable_peer_link(sm, peer);
+	return -1;
+}
+
+
+static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_tdls_peer *peer;
+	struct wpa_eapol_ie_parse kde;
+	struct wpa_tdls_ftie *ftie;
+	struct wpa_tdls_timeoutie *timeoutie;
+	struct wpa_tdls_lnkid *lnkid;
+	int ielen;
+	u16 status;
+	const u8 *pos;
+	u32 lifetime;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
+		   "(Peer " MACSTR ")", MAC2STR(src_addr));
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+	if (peer == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+			   "TPK M3: " MACSTR, MAC2STR(src_addr));
+		return -1;
+	}
+	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
+
+	if (len < 3 + 3)
+		goto error;
+	pos = buf;
+	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+	status = WPA_GET_LE16(pos);
+
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
+			   status);
+		goto error;
+	}
+	pos += 2 /* status code */ + 1 /* dialog token */;
+
+	ielen = len - (pos - buf); /* start of IE in buf */
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs piggy-back broken IEs to the end
+	 * of a TDLS Confirm packet, which will fail the link if we don't ignore
+	 * this error.
+	 */
+	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
+	}
+
+	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
+		    (u8 *) kde.lnkid, kde.lnkid_len);
+	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
+		goto error;
+	}
+
+	if (!wpa_tdls_get_privacy(sm))
+		goto skip_rsn;
+
+	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+		wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
+		    kde.ftie, sizeof(*ftie));
+	ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+	if (kde.rsn_ie == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
+		goto error;
+	}
+	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
+		    kde.rsn_ie, kde.rsn_ie_len);
+	if (kde.rsn_ie_len != peer->rsnie_p_len ||
+	    os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
+		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
+			   "with the one sent in TPK M2");
+		goto error;
+	}
+
+	if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
+		wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
+			   "not match with FTIE ANonce used in TPK M2");
+		goto error;
+	}
+
+	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
+			   "match with FTIE SNonce used in TPK M1");
+		goto error;
+	}
+
+	if (kde.key_lifetime == NULL) {
+		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
+		goto error;
+	}
+	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+	wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
+		    (u8 *) timeoutie, sizeof(*timeoutie));
+	lifetime = WPA_GET_LE32(timeoutie->value);
+	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
+		   lifetime);
+	if (lifetime != peer->lifetime) {
+		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+			   "TPK M3 (expected %u)", lifetime, peer->lifetime);
+		goto error;
+	}
+
+	if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
+					   (u8 *) timeoutie, ftie) < 0) {
+		wpa_tdls_del_key(sm, peer);
+		goto error;
+	}
+
+	if (wpa_tdls_set_key(sm, peer) < 0) {
+		/*
+		 * Some drivers may not be able to config the key prior to full
+		 * STA entry having been configured.
+		 */
+		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
+			   "STA entry is complete");
+		peer->reconfig_key = 1;
+	}
+
+skip_rsn:
+	/* add supported rates, capabilities, and qos_info to the TDLS peer */
+	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+		goto error;
+
+	if (!peer->tpk_success) {
+		/*
+		 * Enable Link only when tpk_success is 0, signifying that this
+		 * processing of TPK M3 frame is not because of a retransmission
+		 * during TDLS setup handshake.
+		 */
+		ret = wpa_tdls_enable_link(sm, peer);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
+			goto error;
+		}
+	}
+	return ret;
+error:
+	wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	return -1;
+}
+
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
+{
+	struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
+
+	os_memset(lifetime, 0, ie_len);
+	lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
+	lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
+	lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
+	WPA_PUT_LE32(lifetime->value, tsecs);
+	os_memcpy(pos, ie, ie_len);
+	return pos + ie_len;
+}
+
+
+/**
+ * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send TPK Handshake Message 1 info to driver to start TDLS
+ * handshake with the peer.
+ */
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+	int tdls_prohibited = sm->tdls_prohibited;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+#ifdef CONFIG_TDLS_TESTING
+	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+	    tdls_prohibited) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+			   "on TDLS");
+		tdls_prohibited = 0;
+	}
+#endif /* CONFIG_TDLS_TESTING */
+
+	if (tdls_prohibited) {
+		wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
+			   "reject request to start setup");
+		return -1;
+	}
+
+	peer = wpa_tdls_add_peer(sm, addr, NULL);
+	if (peer == NULL)
+		return -1;
+
+	if (peer->tpk_in_progress) {
+		wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
+		return 0;
+	}
+
+	peer->initiator = 1;
+
+	/* add the peer to the driver as a "setup in progress" peer */
+	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+				    NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	peer->tpk_in_progress = 1;
+
+	if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL || !peer->tpk_success)
+		return;
+
+	if (sm->tdls_external_setup) {
+		/*
+		 * Disable previous link to allow renegotiation to be completed
+		 * on AP path.
+		 */
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	}
+}
+
+
+/**
+ * wpa_supplicant_rx_tdls - Receive TDLS data frame
+ *
+ * This function is called to receive TDLS (ethertype = 0x890d) data frames.
+ */
+static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
+				   const u8 *buf, size_t len)
+{
+	struct wpa_sm *sm = ctx;
+	struct wpa_tdls_frame *tf;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
+		    buf, len);
+
+	if (sm->tdls_disabled || !sm->tdls_supported) {
+		wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
+			   "or unsupported by driver");
+		return;
+	}
+
+	if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
+		return;
+	}
+
+	if (len < sizeof(*tf)) {
+		wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
+		return;
+	}
+
+	/* Check to make sure its a valid encapsulated TDLS frame */
+	tf = (struct wpa_tdls_frame *) buf;
+	if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
+	    tf->category != WLAN_ACTION_TDLS) {
+		wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
+			   "category=%u action=%u",
+			   tf->payloadtype, tf->category, tf->action);
+		return;
+	}
+
+	switch (tf->action) {
+	case WLAN_TDLS_SETUP_REQUEST:
+		wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_SETUP_RESPONSE:
+		wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_SETUP_CONFIRM:
+		wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_TEARDOWN:
+		wpa_tdls_recv_teardown(sm, src_addr, buf, len);
+		break;
+	case WLAN_TDLS_DISCOVERY_REQUEST:
+		wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
+		break;
+	default:
+		/* Kernel code will process remaining frames */
+		wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
+			   tf->action);
+		break;
+	}
+}
+
+
+/**
+ * wpa_tdls_init - Initialize driver interface parameters for TDLS
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters for TDLS.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_tdls_init(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return -1;
+
+	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
+				     sm->ifname,
+				     sm->own_addr,
+				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
+				     sm, 0);
+	if (sm->l2_tdls == NULL) {
+		wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
+			   "connection");
+		return -1;
+	}
+
+	/*
+	 * Drivers that support TDLS but don't implement the get_capa callback
+	 * are assumed to perform everything internally
+	 */
+	if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
+				 &sm->tdls_external_setup,
+				 &sm->tdls_chan_switch) < 0) {
+		sm->tdls_supported = 1;
+		sm->tdls_external_setup = 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
+		   "driver", sm->tdls_supported ? "" : " not");
+	wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
+		   sm->tdls_external_setup ? "external" : "internal");
+	wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
+		   sm->tdls_chan_switch ? "supports" : "does not support");
+
+	return 0;
+}
+
+
+void wpa_tdls_teardown_peers(struct wpa_sm *sm)
+{
+	struct wpa_tdls_peer *peer, *tmp;
+
+	if (!sm)
+		return;
+	peer = sm->tdls;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
+
+	while (peer) {
+		tmp = peer->next;
+		wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
+			   MAC2STR(peer->addr));
+		if (sm->tdls_external_setup)
+			wpa_tdls_do_teardown(sm, peer,
+					     WLAN_REASON_DEAUTH_LEAVING);
+		else
+			wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+
+		peer = tmp;
+	}
+}
+
+
+static void wpa_tdls_remove_peers(struct wpa_sm *sm)
+{
+	struct wpa_tdls_peer *peer, *tmp;
+
+	peer = sm->tdls;
+
+	while (peer) {
+		int res;
+		tmp = peer->next;
+		res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+		wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
+			   MAC2STR(peer->addr), res);
+		wpa_tdls_peer_free(sm, peer);
+		peer = tmp;
+	}
+}
+
+
+/**
+ * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
+ *
+ * This function is called to recover driver interface parameters for TDLS
+ * and frees resources allocated for it.
+ */
+void wpa_tdls_deinit(struct wpa_sm *sm)
+{
+	if (sm == NULL)
+		return;
+
+	if (sm->l2_tdls)
+		l2_packet_deinit(sm->l2_tdls);
+	sm->l2_tdls = NULL;
+
+	wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_assoc(struct wpa_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
+	wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_disassoc(struct wpa_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
+	wpa_tdls_remove_peers(sm);
+}
+
+
+static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
+{
+	/* bit 38 - TDLS Prohibited */
+	return !!(elems->ext_capab[2 + 4] & 0x40);
+}
+
+
+static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
+{
+	/* bit 39 - TDLS Channel Switch Prohibited */
+	return !!(elems->ext_capab[2 + 4] & 0x80);
+}
+
+
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+	struct ieee802_11_elems elems;
+
+	sm->tdls_prohibited = 0;
+	sm->tdls_chan_switch_prohibited = 0;
+
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
+	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+		return;
+
+	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
+		   sm->tdls_prohibited ? "prohibited" : "allowed");
+	sm->tdls_chan_switch_prohibited =
+		wpa_tdls_chan_switch_prohibited(&elems);
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
+		   sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
+}
+
+
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+	struct ieee802_11_elems elems;
+
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
+	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+		return;
+
+	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
+		wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
+			   "(Re)Association Response IEs");
+		sm->tdls_prohibited = 1;
+	}
+
+	if (!sm->tdls_chan_switch_prohibited &&
+	    wpa_tdls_chan_switch_prohibited(&elems)) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
+		sm->tdls_chan_switch_prohibited = 1;
+	}
+}
+
+
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
+	sm->tdls_disabled = !enabled;
+}
+
+
+int wpa_tdls_is_external_setup(struct wpa_sm *sm)
+{
+	return sm->tdls_external_setup;
+}
+
+
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+				u8 oper_class,
+				struct hostapd_freq_params *freq_params)
+{
+	struct wpa_tdls_peer *peer;
+	int ret;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	if (!sm->tdls_chan_switch) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Channel switching not supported by the driver");
+		return -1;
+	}
+
+	if (sm->tdls_chan_switch_prohibited) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
+		return -1;
+	}
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL || !peer->tpk_success) {
+		wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
+			   " not found for channel switching", MAC2STR(addr));
+		return -1;
+	}
+
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+			   " already has channel switching enabled",
+			   MAC2STR(addr));
+		return 0;
+	}
+
+	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
+						oper_class, freq_params);
+	if (!ret)
+		peer->chan_switch_enabled = 1;
+
+	return ret;
+}
+
+
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (!peer || !peer->chan_switch_enabled) {
+		wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
+			   MACSTR, MAC2STR(addr));
+		return -1;
+	}
+
+	/* ignore the return value */
+	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+
+	peer->chan_switch_enabled = 0;
+	return 0;
+}
diff --git a/freebsd/contrib/wpa/src/utils/bitfield.h b/freebsd/contrib/wpa/src/utils/bitfield.h
new file mode 100644
index 00000000000..7050a208c8f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/bitfield.h
@@ -0,0 +1,21 @@
+/*
+ * Bitfield
+ * Copyright (c) 2013, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BITFIELD_H
+#define BITFIELD_H
+
+struct bitfield;
+
+struct bitfield * bitfield_alloc(size_t max_bits);
+void bitfield_free(struct bitfield *bf);
+void bitfield_set(struct bitfield *bf, size_t bit);
+void bitfield_clear(struct bitfield *bf, size_t bit);
+int bitfield_is_set(struct bitfield *bf, size_t bit);
+int bitfield_get_first_zero(struct bitfield *bf);
+
+#endif /* BITFIELD_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wnm_sta.c b/freebsd/contrib/wpa/wpa_supplicant/wnm_sta.c
new file mode 100644
index 00000000000..ea737e31c25
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wnm_sta.c
@@ -0,0 +1,1150 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant - WNM
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "ctrl_iface.h"
+#include "bss.h"
+#include "wnm_sta.h"
+#include "hs20_supplicant.h"
+
+#define MAX_TFS_IE_LEN  1024
+#define WNM_MAX_NEIGHBOR_REPORT 10
+
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
+				   u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+	return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
+				   const u8 *addr, u8 *buf, u16 *buf_len,
+				   enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.request */
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+				 u8 action, u16 intval, struct wpabuf *tfs_req)
+{
+	struct ieee80211_mgmt *mgmt;
+	int res;
+	size_t len;
+	struct wnm_sleep_element *wnmsleep_ie;
+	u8 *wnmtfs_ie;
+	u8 wnmsleep_ie_len;
+	u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
+	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
+		WNM_SLEEP_TFS_REQ_IE_NONE;
+
+	wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
+		   "action=%s to " MACSTR,
+		   action == 0 ? "enter" : "exit",
+		   MAC2STR(wpa_s->bssid));
+
+	/* WNM-Sleep Mode IE */
+	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+	wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
+	if (wnmsleep_ie == NULL)
+		return -1;
+	wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
+	wnmsleep_ie->len = wnmsleep_ie_len - 2;
+	wnmsleep_ie->action_type = action;
+	wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
+	wnmsleep_ie->intval = host_to_le16(intval);
+	wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
+		    (u8 *) wnmsleep_ie, wnmsleep_ie_len);
+
+	/* TFS IE(s) */
+	if (tfs_req) {
+		wnmtfs_ie_len = wpabuf_len(tfs_req);
+		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
+		if (wnmtfs_ie == NULL) {
+			os_free(wnmsleep_ie);
+			return -1;
+		}
+		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
+	} else {
+		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+		if (wnmtfs_ie == NULL) {
+			os_free(wnmsleep_ie);
+			return -1;
+		}
+		if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
+					    tfs_oper)) {
+			wnmtfs_ie_len = 0;
+			os_free(wnmtfs_ie);
+			wnmtfs_ie = NULL;
+		}
+	}
+	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
+		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
+
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
+	if (mgmt == NULL) {
+		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+			   "WNM-Sleep Request action frame");
+		os_free(wnmsleep_ie);
+		os_free(wnmtfs_ie);
+		return -1;
+	}
+
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
+	mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
+	os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
+		  wnmsleep_ie_len);
+	/* copy TFS IE here */
+	if (wnmtfs_ie_len > 0) {
+		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
+			  wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
+	}
+
+	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
+		wnmtfs_ie_len;
+
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
+			   "(action=%d, intval=%d)", action, intval);
+
+	os_free(wnmsleep_ie);
+	os_free(wnmtfs_ie);
+	os_free(mgmt);
+
+	return res;
+}
+
+
+static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
+					 u8 *tfsresp_ie_start,
+					 u8 *tfsresp_ie_end)
+{
+	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
+			 wpa_s->bssid, NULL, NULL);
+	/* remove GTK/IGTK ?? */
+
+	/* set the TFS Resp IE(s) */
+	if (tfsresp_ie_start && tfsresp_ie_end &&
+	    tfsresp_ie_end - tfsresp_ie_start >= 0) {
+		u16 tfsresp_ie_len;
+		tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
+			tfsresp_ie_start;
+		wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
+		/* pass the TFS Resp IE(s) to driver for processing */
+		if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
+					    tfsresp_ie_start,
+					    &tfsresp_ie_len,
+					    WNM_SLEEP_TFS_RESP_IE_SET))
+			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
+	}
+}
+
+
+static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
+					const u8 *frm, u16 key_len_total)
+{
+	u8 *ptr, *end;
+	u8 gtk_len;
+
+	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
+			 NULL, NULL);
+
+	/* Install GTK/IGTK */
+
+	/* point to key data field */
+	ptr = (u8 *) frm + 1 + 2;
+	end = ptr + key_len_total;
+	wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
+
+	while (ptr + 1 < end) {
+		if (ptr + 2 + ptr[1] > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
+				   "length");
+			if (end > ptr) {
+				wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
+					    ptr, end - ptr);
+			}
+			break;
+		}
+		if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
+			if (ptr[1] < 11 + 5) {
+				wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
+					   "subelem");
+				break;
+			}
+			gtk_len = *(ptr + 4);
+			if (ptr[1] < 11 + gtk_len ||
+			    gtk_len < 5 || gtk_len > 32) {
+				wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
+					   "subelem");
+				break;
+			}
+			wpa_wnmsleep_install_key(
+				wpa_s->wpa,
+				WNM_SLEEP_SUBELEM_GTK,
+				ptr);
+			ptr += 13 + gtk_len;
+#ifdef CONFIG_IEEE80211W
+		} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
+			if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
+				wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
+					   "subelem");
+				break;
+			}
+			wpa_wnmsleep_install_key(wpa_s->wpa,
+						 WNM_SLEEP_SUBELEM_IGTK, ptr);
+			ptr += 10 + WPA_IGTK_LEN;
+#endif /* CONFIG_IEEE80211W */
+		} else
+			break; /* skip the loop */
+	}
+}
+
+
+static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
+					const u8 *frm, int len)
+{
+	/*
+	 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
+	 * WNM-Sleep Mode IE | TFS Response IE
+	 */
+	u8 *pos = (u8 *) frm; /* point to payload after the action field */
+	u16 key_len_total;
+	struct wnm_sleep_element *wnmsleep_ie = NULL;
+	/* multiple TFS Resp IE (assuming consecutive) */
+	u8 *tfsresp_ie_start = NULL;
+	u8 *tfsresp_ie_end = NULL;
+	size_t left;
+
+	if (len < 3)
+		return;
+	key_len_total = WPA_GET_LE16(frm + 1);
+
+	wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
+		   frm[0], key_len_total);
+	left = len - 3;
+	if (key_len_total > left) {
+		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
+		return;
+	}
+	pos += 3 + key_len_total;
+	while (pos - frm < len) {
+		u8 ie_len = *(pos + 1);
+		if (pos + 2 + ie_len > frm + len) {
+			wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
+			break;
+		}
+		wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
+		if (*pos == WLAN_EID_WNMSLEEP)
+			wnmsleep_ie = (struct wnm_sleep_element *) pos;
+		else if (*pos == WLAN_EID_TFS_RESP) {
+			if (!tfsresp_ie_start)
+				tfsresp_ie_start = pos;
+			tfsresp_ie_end = pos;
+		} else
+			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
+		pos += ie_len + 2;
+	}
+
+	if (!wnmsleep_ie) {
+		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+		return;
+	}
+
+	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
+	    wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
+		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
+			   "frame (action=%d, intval=%d)",
+			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
+		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
+			wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
+						     tfsresp_ie_end);
+		} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+			wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
+			   "(action=%d, intval=%d)",
+			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
+		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
+					 wpa_s->bssid, NULL, NULL);
+		else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
+					 wpa_s->bssid, NULL, NULL);
+	}
+}
+
+
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
+	}
+
+	wpa_s->wnm_num_neighbor_report = 0;
+	os_free(wpa_s->wnm_neighbor_report_elements);
+	wpa_s->wnm_neighbor_report_elements = NULL;
+}
+
+
+static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
+					   u8 id, u8 elen, const u8 *pos)
+{
+	switch (id) {
+	case WNM_NEIGHBOR_TSF:
+		if (elen < 2 + 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
+			break;
+		}
+		rep->tsf_offset = WPA_GET_LE16(pos);
+		rep->beacon_int = WPA_GET_LE16(pos + 2);
+		rep->tsf_present = 1;
+		break;
+	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
+		if (elen < 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
+				   "country string");
+			break;
+		}
+		os_memcpy(rep->country, pos, 2);
+		rep->country_present = 1;
+		break;
+	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
+		if (elen < 1) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
+				   "candidate");
+			break;
+		}
+		rep->preference = pos[0];
+		rep->preference_present = 1;
+		break;
+	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
+		rep->bss_term_tsf = WPA_GET_LE64(pos);
+		rep->bss_term_dur = WPA_GET_LE16(pos + 8);
+		rep->bss_term_present = 1;
+		break;
+	case WNM_NEIGHBOR_BEARING:
+		if (elen < 8) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
+				   "bearing");
+			break;
+		}
+		rep->bearing = WPA_GET_LE16(pos);
+		rep->distance = WPA_GET_LE32(pos + 2);
+		rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
+		rep->bearing_present = 1;
+		break;
+	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
+		if (elen < 1) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
+				   "pilot");
+			break;
+		}
+		os_free(rep->meas_pilot);
+		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
+		if (rep->meas_pilot == NULL)
+			break;
+		rep->meas_pilot->measurement_pilot = pos[0];
+		rep->meas_pilot->subelem_len = elen - 1;
+		os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
+		break;
+	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
+		if (elen < 5) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
+				   "capabilities");
+			break;
+		}
+		os_memcpy(rep->rm_capab, pos, 5);
+		rep->rm_capab_present = 1;
+		break;
+	case WNM_NEIGHBOR_MULTIPLE_BSSID:
+		if (elen < 1) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
+			break;
+		}
+		os_free(rep->mul_bssid);
+		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
+		if (rep->mul_bssid == NULL)
+			break;
+		rep->mul_bssid->max_bssid_indicator = pos[0];
+		rep->mul_bssid->subelem_len = elen - 1;
+		os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
+		break;
+	}
+}
+
+
+static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
+{
+	struct wpa_bss *bss = wpa_s->current_bss;
+	const char *country = NULL;
+
+	if (bss) {
+		const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
+
+		if (elem && elem[1] >= 2)
+			country = (const char *) (elem + 2);
+	}
+
+	return ieee80211_chan_to_freq(country, op_class, chan);
+}
+
+
+static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
+				      const u8 *pos, u8 len,
+				      struct neighbor_report *rep)
+{
+	u8 left = len;
+
+	if (left < 13) {
+		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
+		return;
+	}
+
+	os_memcpy(rep->bssid, pos, ETH_ALEN);
+	rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
+	rep->regulatory_class = *(pos + 10);
+	rep->channel_number = *(pos + 11);
+	rep->phy_type = *(pos + 12);
+
+	pos += 13;
+	left -= 13;
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
+		left -= 2;
+		if (elen > left) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Truncated neighbor report subelement");
+			break;
+		}
+		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
+		left -= elen;
+		pos += elen;
+	}
+
+	rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
+				     rep->channel_number);
+}
+
+
+static struct wpa_bss *
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
+{
+
+	u8 i;
+	struct wpa_bss *bss = wpa_s->current_bss;
+	struct wpa_bss *target;
+
+	if (!bss)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
+		   MAC2STR(wpa_s->bssid), bss->level);
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->preference_present && nei->preference == 0) {
+			wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
+				   MAC2STR(nei->bssid));
+			continue;
+		}
+
+		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
+		if (!target) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) not found in scan results",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
+		if (bss->ssid_len != target->ssid_len ||
+		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
+			/*
+			 * TODO: Could consider allowing transition to another
+			 * ESS if PMF was enabled for the association.
+			 */
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) in different ESS",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
+		if (target->level < bss->level && target->level < -80) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) does not have sufficient signal level (%d)",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1,
+				   target->level);
+			continue;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Found an acceptable preferred transition candidate BSS "
+			   MACSTR " (RSSI %d)",
+			   MAC2STR(nei->bssid), target->level);
+		return target;
+	}
+
+	return NULL;
+}
+
+
+static void wnm_send_bss_transition_mgmt_resp(
+	struct wpa_supplicant *wpa_s, u8 dialog_token,
+	enum bss_trans_mgmt_status_code status, u8 delay,
+	const u8 *target_bssid)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t len;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
+		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
+		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+	if (!wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Current BSS not known - drop response");
+		return;
+	}
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	os_memset(&buf, 0, sizeof(buf));
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
+	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
+	mgmt->u.action.u.bss_tm_resp.status_code = status;
+	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
+	pos = mgmt->u.action.u.bss_tm_resp.variable;
+	if (target_bssid) {
+		os_memcpy(pos, target_bssid, ETH_ALEN);
+		pos += ETH_ALEN;
+	} else if (status == WNM_BSS_TM_ACCEPT) {
+		/*
+		 * P802.11-REVmc clarifies that the Target BSSID field is always
+		 * present when status code is zero, so use a fake value here if
+		 * no BSSID is yet known.
+		 */
+		os_memset(pos, 0, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	len = pos - (u8 *) &mgmt->u.action.category;
+
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to send BSS Transition Management Response");
+	}
+}
+
+
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
+{
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
+
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return 0;
+
+	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+			      &wpa_s->scan_trigger_time)) {
+		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
+		wnm_deallocate_memory(wpa_s);
+		return 0;
+	}
+
+	if (!wpa_s->current_bss ||
+	    os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
+		      ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
+		return 0;
+	}
+
+	/* Compare the Neighbor Report and scan results */
+	bss = compare_scan_neighbor_results(wpa_s);
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
+		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
+		goto send_bss_resp_fail;
+	}
+
+	/* Associate to the network */
+	/* Send the BSS Management Response - Accept */
+	if (wpa_s->wnm_reply) {
+		wpa_s->wnm_reply = 0;
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  WNM_BSS_TM_ACCEPT,
+						  0, bss->bssid);
+	}
+
+	if (bss == wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Already associated with the preferred candidate");
+		return 1;
+	}
+
+	wpa_s->reassociate = 1;
+	wpa_supplicant_connect(wpa_s, bss, ssid);
+	wnm_deallocate_memory(wpa_s);
+	return 1;
+
+send_bss_resp_fail:
+	if (!reply_on_fail)
+		return 0;
+
+	/* Send reject response for all the failures */
+
+	if (wpa_s->wnm_reply) {
+		wpa_s->wnm_reply = 0;
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  status, 0, NULL);
+	}
+	wnm_deallocate_memory(wpa_s);
+
+	return 0;
+}
+
+
+static int cand_pref_compar(const void *a, const void *b)
+{
+	const struct neighbor_report *aa = a;
+	const struct neighbor_report *bb = b;
+
+	if (!aa->preference_present && !bb->preference_present)
+		return 0;
+	if (!aa->preference_present)
+		return 1;
+	if (!bb->preference_present)
+		return -1;
+	if (bb->preference > aa->preference)
+		return 1;
+	if (bb->preference < aa->preference)
+		return -1;
+	return 0;
+}
+
+
+static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+	qsort(wpa_s->wnm_neighbor_report_elements,
+	      wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
+	      cand_pref_compar);
+}
+
+
+static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		wpa_printf(MSG_DEBUG, "%u: " MACSTR
+			   " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
+			   i, MAC2STR(nei->bssid), nei->bssid_info,
+			   nei->regulatory_class,
+			   nei->channel_number, nei->phy_type,
+			   nei->preference_present ? nei->preference : -1,
+			   nei->freq);
+	}
+}
+
+
+static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
+{
+	unsigned int i;
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
+		int j;
+
+		for (j = 0; j < mode->num_channels; j++) {
+			struct hostapd_channel_data *chan;
+
+			chan = &mode->channels[j];
+			if (chan->freq == freq &&
+			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
+{
+	int *freqs;
+	int num_freqs = 0;
+	unsigned int i;
+
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+
+	if (wpa_s->hw.modes == NULL)
+		return;
+
+	os_free(wpa_s->next_scan_freqs);
+	wpa_s->next_scan_freqs = NULL;
+
+	freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
+	if (freqs == NULL)
+		return;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->freq <= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Unknown neighbor operating frequency for "
+				   MACSTR " - scan all channels",
+				   MAC2STR(nei->bssid));
+			os_free(freqs);
+			return;
+		}
+		if (chan_supported(wpa_s, nei->freq))
+			add_freq(freqs, &num_freqs, nei->freq);
+	}
+
+	if (num_freqs == 0) {
+		os_free(freqs);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Scan %d frequencies based on transition candidate list",
+		   num_freqs);
+	wpa_s->next_scan_freqs = freqs;
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
+					     const u8 *pos, const u8 *end,
+					     int reply)
+{
+	unsigned int beacon_int;
+	u8 valid_int;
+
+	if (pos + 5 > end)
+		return;
+
+	if (wpa_s->current_bss)
+		beacon_int = wpa_s->current_bss->beacon_int;
+	else
+		beacon_int = 100; /* best guess */
+
+	wpa_s->wnm_dialog_token = pos[0];
+	wpa_s->wnm_mode = pos[1];
+	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
+	valid_int = pos[4];
+	wpa_s->wnm_reply = reply;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
+		   "dialog_token=%u request_mode=0x%x "
+		   "disassoc_timer=%u validity_interval=%u",
+		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
+		   wpa_s->wnm_dissoc_timer, valid_int);
+
+	pos += 5;
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+		if (pos + 12 > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
+			return;
+		}
+		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
+		pos += 12; /* BSS Termination Duration */
+	}
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
+		char url[256];
+
+		if (pos + 1 > end || pos + 1 + pos[0] > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
+				   "Management Request (URL)");
+			return;
+		}
+		os_memcpy(url, pos + 1, pos[0]);
+		url[pos[0]] = '\0';
+		pos += 1 + pos[0];
+
+		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
+			wpa_sm_pmf_enabled(wpa_s->wpa),
+			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
+	}
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
+			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
+		if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
+			/* TODO: mark current BSS less preferred for
+			 * selection */
+			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		}
+	}
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
+		unsigned int valid_ms;
+
+		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
+		wnm_deallocate_memory(wpa_s);
+		wpa_s->wnm_neighbor_report_elements = os_calloc(
+			WNM_MAX_NEIGHBOR_REPORT,
+			sizeof(struct neighbor_report));
+		if (wpa_s->wnm_neighbor_report_elements == NULL)
+			return;
+
+		while (pos + 2 <= end &&
+		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
+		{
+			u8 tag = *pos++;
+			u8 len = *pos++;
+
+			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
+				   tag);
+			if (pos + len > end) {
+				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
+				return;
+			}
+			if (tag == WLAN_EID_NEIGHBOR_REPORT) {
+				struct neighbor_report *rep;
+				rep = &wpa_s->wnm_neighbor_report_elements[
+					wpa_s->wnm_num_neighbor_report];
+				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
+			}
+
+			pos += len;
+			wpa_s->wnm_num_neighbor_report++;
+		}
+		wnm_sort_cand_list(wpa_s);
+		wnm_dump_cand_list(wpa_s);
+		valid_ms = valid_int * beacon_int * 128 / 125;
+		wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
+			   valid_ms);
+		os_get_reltime(&wpa_s->wnm_cand_valid_until);
+		wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
+		wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
+		wpa_s->wnm_cand_valid_until.sec +=
+			wpa_s->wnm_cand_valid_until.usec / 1000000;
+		wpa_s->wnm_cand_valid_until.usec %= 1000000;
+		os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
+
+		if (wpa_s->last_scan_res_used > 0) {
+			struct os_reltime now;
+
+			os_get_reltime(&now);
+			if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
+				wpa_printf(MSG_DEBUG,
+					   "WNM: Try to use recent scan results");
+				if (wnm_scan_process(wpa_s, 0) > 0)
+					return;
+				wpa_printf(MSG_DEBUG,
+					   "WNM: No match in previous scan results - try a new scan");
+			}
+		}
+
+		wnm_set_scan_freqs(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	} else if (reply) {
+		enum bss_trans_mgmt_status_code status;
+		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
+			status = WNM_BSS_TM_ACCEPT;
+		else {
+			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
+			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
+		}
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  status, 0, NULL);
+	}
+}
+
+
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+				       u8 query_reason)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t len;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
+		   MACSTR " query_reason=%u",
+		   MAC2STR(wpa_s->bssid), query_reason);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	os_memset(&buf, 0, sizeof(buf));
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
+	mgmt->u.action.u.bss_tm_query.dialog_token = 1;
+	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
+	pos = mgmt->u.action.u.bss_tm_query.variable;
+
+	len = pos - (u8 *) &mgmt->u.action.category;
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+
+	return ret;
+}
+
+
+static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
+					    const u8 *sa, const u8 *data,
+					    int len)
+{
+	const u8 *pos, *end, *next;
+	u8 ie, ie_len;
+
+	pos = data;
+	end = data + len;
+
+	while (pos + 1 < end) {
+		ie = *pos++;
+		ie_len = *pos++;
+		wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
+			   ie, ie_len);
+		if (ie_len > end - pos) {
+			wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
+				   "subelement");
+			break;
+		}
+		next = pos + ie_len;
+		if (ie_len < 4) {
+			pos = next;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
+			   WPA_GET_BE24(pos), pos[3]);
+
+#ifdef CONFIG_HS20
+		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
+		    WPA_GET_BE24(pos) == OUI_WFA &&
+		    pos[3] == HS20_WNM_SUB_REM_NEEDED) {
+			/* Subscription Remediation subelement */
+			const u8 *ie_end;
+			u8 url_len;
+			char *url;
+			u8 osu_method;
+
+			wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
+				   "subelement");
+			ie_end = pos + ie_len;
+			pos += 4;
+			url_len = *pos++;
+			if (url_len == 0) {
+				wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
+				url = NULL;
+				osu_method = 1;
+			} else {
+				if (pos + url_len + 1 > ie_end) {
+					wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
+						   url_len,
+						   (int) (ie_end - pos));
+					break;
+				}
+				url = os_malloc(url_len + 1);
+				if (url == NULL)
+					break;
+				os_memcpy(url, pos, url_len);
+				url[url_len] = '\0';
+				osu_method = pos[url_len];
+			}
+			hs20_rx_subscription_remediation(wpa_s, url,
+							 osu_method);
+			os_free(url);
+			pos = next;
+			continue;
+		}
+
+		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
+		    WPA_GET_BE24(pos) == OUI_WFA &&
+		    pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
+			const u8 *ie_end;
+			u8 url_len;
+			char *url;
+			u8 code;
+			u16 reauth_delay;
+
+			ie_end = pos + ie_len;
+			pos += 4;
+			code = *pos++;
+			reauth_delay = WPA_GET_LE16(pos);
+			pos += 2;
+			url_len = *pos++;
+			wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
+				   "Imminent - Reason Code %u   "
+				   "Re-Auth Delay %u  URL Length %u",
+				   code, reauth_delay, url_len);
+			if (pos + url_len > ie_end)
+				break;
+			url = os_malloc(url_len + 1);
+			if (url == NULL)
+				break;
+			os_memcpy(url, pos, url_len);
+			url[url_len] = '\0';
+			hs20_rx_deauth_imminent_notice(wpa_s, code,
+						       reauth_delay, url);
+			os_free(url);
+			pos = next;
+			continue;
+		}
+#endif /* CONFIG_HS20 */
+
+		pos = next;
+	}
+}
+
+
+static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
+					const u8 *sa, const u8 *frm, int len)
+{
+	const u8 *pos, *end;
+	u8 dialog_token, type;
+
+	/* Dialog Token [1] | Type [1] | Subelements */
+
+	if (len < 2 || sa == NULL)
+		return;
+	end = frm + len;
+	pos = frm;
+	dialog_token = *pos++;
+	type = *pos++;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
+		"(dialog_token %u type %u sa " MACSTR ")",
+		dialog_token, type, MAC2STR(sa));
+	wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
+		    pos, end - pos);
+
+	if (wpa_s->wpa_state != WPA_COMPLETED ||
+	    os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
+			"from our AP - ignore it");
+		return;
+	}
+
+	switch (type) {
+	case 1:
+		ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
+		break;
+	default:
+		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
+			"WNM-Notification type %u", type);
+		break;
+	}
+}
+
+
+void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
+			      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	const u8 *pos, *end;
+	u8 act;
+
+	if (len < IEEE80211_HDRLEN + 2)
+		return;
+
+	pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+	act = *pos++;
+	end = ((const u8 *) mgmt) + len;
+
+	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
+		   act, MAC2STR(mgmt->sa));
+	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+	    os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
+			   "frame");
+		return;
+	}
+
+	switch (act) {
+	case WNM_BSS_TRANS_MGMT_REQ:
+		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
+						 !(mgmt->da[0] & 0x01));
+		break;
+	case WNM_SLEEP_MODE_RESP:
+		ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
+		break;
+	case WNM_NOTIFICATION_REQ:
+		ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "WNM: Unknown request");
+		break;
+	}
+}
-- 
2.12.3




More information about the devel mailing list