[wpa_supplicant] Cumulative patch from b8491ae5a

Also revert local solution for encrypted IMSI and use the upstream version.

Bug: 134177972
Test: Device boots up and connects to WPA3/OWE wifi networks, run traffic.
Test: Able to turn on/off softap, associate wifi STA, run traffic.
Test: Regression test passed (Bug: 137653009)
Change-Id: Ibf6b6ef3495287156c397daa89d02923f981889b
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 9b07ee1..48f8f23 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -20,6 +20,7 @@
 CFLAGS += -DCONFIG_PROXYARP
 CFLAGS += -DCONFIG_IPV6
 CFLAGS += -DCONFIG_IAPP
+CFLAGS += -DCONFIG_AIRTIME_POLICY
 
 LIB_OBJS= \
 	accounting.o \
@@ -27,6 +28,7 @@
 	ap_drv_ops.o \
 	ap_list.o \
 	ap_mlme.o \
+	airtime_policy.o \
 	authsrv.o \
 	beacon.o \
 	bss_load.o \
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 3b45075..11178a1 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -594,12 +594,12 @@
 	    iface->conf->secondary_channel)
 		n_chans = 2;
 
-	if (iface->conf->ieee80211ac) {
-		switch (iface->conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_80MHZ:
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CHANWIDTH_80MHZ:
 			n_chans = 4;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
+		case CHANWIDTH_160MHZ:
 			n_chans = 8;
 			break;
 		}
@@ -607,7 +607,7 @@
 
 	bw = num_chan_to_bw(n_chans);
 
-	/* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */
+	/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
 
 	wpa_printf(MSG_DEBUG,
 		   "ACS: Survey analysis for selected bandwidth %d MHz", bw);
@@ -647,9 +647,9 @@
 		}
 
 		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
-		    iface->conf->ieee80211ac) {
-			if (iface->conf->vht_oper_chwidth ==
-			    VHT_CHANWIDTH_80MHZ &&
+		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+			if (hostapd_get_oper_chwidth(iface->conf) ==
+			    CHANWIDTH_80MHZ &&
 			    !acs_usable_vht80_chan(chan)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for VHT80",
@@ -657,8 +657,8 @@
 				continue;
 			}
 
-			if (iface->conf->vht_oper_chwidth ==
-			    VHT_CHANWIDTH_160MHZ &&
+			if (hostapd_get_oper_chwidth(iface->conf) ==
+			    CHANWIDTH_160MHZ &&
 			    !acs_usable_vht160_chan(chan)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for VHT160",
@@ -783,20 +783,20 @@
 }
 
 
-static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
+static void acs_adjust_center_freq(struct hostapd_iface *iface)
 {
 	int offset;
 
 	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
 
-	switch (iface->conf->vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
+	switch (hostapd_get_oper_chwidth(iface->conf)) {
+	case CHANWIDTH_USE_HT:
 		offset = 2 * iface->conf->secondary_channel;
 		break;
-	case VHT_CHANWIDTH_80MHZ:
+	case CHANWIDTH_80MHZ:
 		offset = 6;
 		break;
-	case VHT_CHANWIDTH_160MHZ:
+	case CHANWIDTH_160MHZ:
 		offset = 14;
 		break;
 	default:
@@ -807,8 +807,8 @@
 		return;
 	}
 
-	iface->conf->vht_oper_centr_freq_seg0_idx =
-		iface->conf->channel + offset;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+					     iface->conf->channel + offset);
 }
 
 
@@ -863,8 +863,8 @@
 
 	iface->conf->channel = ideal_chan->chan;
 
-	if (iface->conf->ieee80211ac)
-		acs_adjust_vht_center_freq(iface);
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
+		acs_adjust_center_freq(iface);
 
 	err = 0;
 fail:
diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
new file mode 100644
index 0000000..f56ca5b
--- /dev/null
+++ b/src/ap/airtime_policy.c
@@ -0,0 +1,269 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * 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 "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "airtime_policy.h"
+
+/* Idea:
+ * Two modes of airtime enforcement:
+ * 1. Static weights: specify weights per MAC address with a per-BSS default
+ * 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
+ *    enforce relative total shares between BSSes.
+ *
+ * - Periodic per-station callback to update queue status.
+ *
+ * Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
+ * keep them updated in sta_info.
+ *
+ * - Separate periodic per-bss (or per-iface?) callback to update weights.
+ *
+ * Just need to loop through all interfaces, count sum the active stations (or
+ * should the per-STA callback just adjust that for the BSS?) and calculate new
+ * weights.
+ */
+
+static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
+					     unsigned int *sec,
+					     unsigned int *usec)
+{
+	unsigned int update_int = iface->conf->airtime_update_interval;
+
+	if (!update_int) {
+		wpa_printf(MSG_ERROR,
+			   "Airtime policy: Invalid airtime policy update interval %u",
+			   update_int);
+		return -1;
+	}
+
+	*sec = update_int / 1000;
+	*usec = (update_int % 1000) * 1000;
+
+	return 0;
+}
+
+
+static void set_new_backlog_time(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 struct os_reltime *now)
+{
+	sta->backlogged_until = *now;
+	sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
+		AIRTIME_BACKLOG_EXPIRY_FACTOR;
+	while (sta->backlogged_until.usec >= 1000000) {
+		sta->backlogged_until.sec++;
+		sta->backlogged_until.usec -= 1000000;
+	}
+}
+
+
+static void count_backlogged_sta(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	struct hostap_sta_driver_data data = {};
+	unsigned int num_backlogged = 0;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
+			continue;
+
+		if (data.backlog_bytes > 0)
+			set_new_backlog_time(hapd, sta, &now);
+		if (os_reltime_before(&now, &sta->backlogged_until))
+			num_backlogged++;
+	}
+	hapd->num_backlogged_sta = num_backlogged;
+}
+
+
+static int sta_set_airtime_weight(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  unsigned int weight)
+{
+	int ret = 0;
+
+	if (weight != sta->airtime_weight &&
+	    (ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
+		return ret;
+
+	sta->airtime_weight = weight;
+	return ret;
+}
+
+
+static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next)
+		sta_set_airtime_weight(hapd, sta, weight);
+}
+
+
+static unsigned int get_airtime_quantum(unsigned int max_wt)
+{
+	unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
+
+	if (quantum < AIRTIME_QUANTUM_MIN)
+		quantum = AIRTIME_QUANTUM_MIN;
+	else if (quantum > AIRTIME_QUANTUM_MAX)
+		quantum = AIRTIME_QUANTUM_MAX;
+
+	return quantum;
+}
+
+
+static void update_airtime_weights(void *eloop_data, void *user_data)
+{
+	struct hostapd_iface *iface = eloop_data;
+	struct hostapd_data *bss;
+	unsigned int sec, usec;
+	unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
+		wt_sum = 0;
+	unsigned int quantum;
+	Boolean all_div_min = TRUE;
+	Boolean apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
+	int wt, num_bss = 0, max_wt = 0;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+
+		count_backlogged_sta(bss);
+		if (!bss->num_backlogged_sta)
+			continue;
+
+		if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
+			num_sta_min = bss->num_backlogged_sta;
+
+		num_sta_prod *= bss->num_backlogged_sta;
+		num_sta_sum += bss->num_backlogged_sta;
+		wt_sum += bss->conf->airtime_weight;
+		num_bss++;
+	}
+
+	if (num_sta_min) {
+		for (i = 0; i < iface->num_bss; i++) {
+			bss = iface->bss[i];
+			if (!bss->started || !bss->conf->airtime_weight)
+				continue;
+
+			/* Check if we can divide all sta numbers by the
+			 * smallest number to keep weights as small as possible.
+			 * This is a lazy way to avoid having to factor
+			 * integers. */
+			if (bss->num_backlogged_sta &&
+			    bss->num_backlogged_sta % num_sta_min > 0)
+				all_div_min = FALSE;
+
+			/* If we're in LIMIT mode, we only apply the weight
+			 * scaling when the BSS(es) marked as limited would a
+			 * larger share than the relative BSS weights indicates
+			 * it should. */
+			if (!apply_limit && bss->conf->airtime_limit) {
+				if (bss->num_backlogged_sta * wt_sum >
+				    bss->conf->airtime_weight * num_sta_sum)
+					apply_limit = TRUE;
+			}
+		}
+		if (all_div_min)
+			num_sta_prod /= num_sta_min;
+	}
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+
+		/* We only set the calculated weight if the BSS has active
+		 * stations and there are other active interfaces as well -
+		 * otherwise we just set a unit weight. This ensures that
+		 * the weights are set reasonably when stations transition from
+		 * inactive to active. */
+		if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
+			wt = bss->conf->airtime_weight * num_sta_prod /
+				bss->num_backlogged_sta;
+		else
+			wt = 1;
+
+		bss->airtime_weight = wt;
+		if (wt > max_wt)
+			max_wt = wt;
+	}
+
+	quantum = get_airtime_quantum(max_wt);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+		set_sta_weights(bss, bss->airtime_weight * quantum);
+	}
+
+	if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+		return;
+
+	eloop_register_timeout(sec, usec, update_airtime_weights, iface,
+			       NULL);
+}
+
+
+static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+	struct airtime_sta_weight *wt;
+
+	wt = hapd->conf->airtime_weight_list;
+	while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
+		wt = wt->next;
+
+	return wt ? wt->weight : hapd->conf->airtime_weight;
+}
+
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	unsigned int weight;
+
+	if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+		weight = get_weight_for_sta(hapd, sta->addr);
+		if (weight)
+			return sta_set_airtime_weight(hapd, sta, weight);
+	}
+	return 0;
+}
+
+
+int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+	unsigned int sec, usec;
+
+	if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
+		return 0;
+
+	if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+		return -1;
+
+	eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
+	return 0;
+}
+
+
+void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+	eloop_cancel_timeout(update_airtime_weights, iface, NULL);
+}
diff --git a/src/ap/airtime_policy.h b/src/ap/airtime_policy.h
new file mode 100644
index 0000000..c2a9b00
--- /dev/null
+++ b/src/ap/airtime_policy.h
@@ -0,0 +1,48 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AIRTIME_POLICY_H
+#define AIRTIME_POLICY_H
+
+struct hostapd_iface;
+
+#ifdef CONFIG_AIRTIME_POLICY
+
+#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
+#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
+
+/* scale quantum so this becomes the effective quantum after applying the max
+ * weight, but never go below min or above max */
+#define AIRTIME_QUANTUM_MIN 8 /* usec */
+#define AIRTIME_QUANTUM_MAX 256 /* usec */
+#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
+int airtime_policy_update_init(struct hostapd_iface *iface);
+void airtime_policy_update_deinit(struct hostapd_iface *iface);
+
+#else /* CONFIG_AIRTIME_POLICY */
+
+static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
+					 struct sta_info *sta)
+{
+	return -1;
+}
+
+static inline int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+	return -1;
+}
+
+static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+}
+
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#endif /* AIRTIME_POLICY_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index e640e99..a061bd8 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -13,12 +13,14 @@
 #include "crypto/tls.h"
 #include "radius/radius_client.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_1x_defs.h"
 #include "common/eapol_common.h"
 #include "common/dhcp.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_server/eap.h"
 #include "wpa_auth.h"
 #include "sta_info.h"
+#include "airtime_policy.h"
 #include "ap_config.h"
 
 
@@ -138,6 +140,11 @@
 	bss->hs20_release = (HS20_VERSION >> 4) + 1;
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MACSEC
+	bss->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+	bss->macsec_port = 1;
+#endif /* CONFIG_MACSEC */
+
 	/* Default to strict CRL checking. */
 	bss->check_crl_strict = 1;
 }
@@ -236,6 +243,13 @@
 	conf->acs_num_scans = 5;
 #endif /* CONFIG_ACS */
 
+#ifdef CONFIG_IEEE80211AX
+	conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+		HE_OPERATION_RTS_THRESHOLD_OFFSET;
+	/* Set default basic MCS/NSS set to single stream MCS 0-7 */
+	conf->he_op.he_basic_mcs_nss_set = 0xfffc;
+#endif /* CONFIG_IEEE80211AX */
+
 	/* The third octet of the country string uses an ASCII space character
 	 * by default to indicate that the regulations encompass all
 	 * environments for the current frequency band in the country. */
@@ -244,6 +258,10 @@
 	conf->rssi_reject_assoc_rssi = 0;
 	conf->rssi_reject_assoc_timeout = 30;
 
+#ifdef CONFIG_AIRTIME_POLICY
+	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+#endif /* CONFIG_AIRTIME_POLICY */
+
 	return conf;
 }
 
@@ -559,8 +577,26 @@
 }
 
 
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_controller_conf_free(struct dpp_controller_conf *conf)
+{
+	struct dpp_controller_conf *prev;
+
+	while (conf) {
+		prev = conf;
+		conf = conf->next;
+		os_free(prev);
+	}
+}
+#endif /* CONFIG_DPP2 */
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
+#if defined(CONFIG_WPS) || defined(CONFIG_HS20)
+	size_t i;
+#endif
+
 	if (conf == NULL)
 		return;
 
@@ -593,8 +629,11 @@
 	os_free(conf->ctrl_interface);
 	os_free(conf->ca_cert);
 	os_free(conf->server_cert);
+	os_free(conf->server_cert2);
 	os_free(conf->private_key);
+	os_free(conf->private_key2);
 	os_free(conf->private_key_passwd);
+	os_free(conf->private_key_passwd2);
 	os_free(conf->check_cert_subject);
 	os_free(conf->ocsp_stapling_response);
 	os_free(conf->ocsp_stapling_response_multi);
@@ -653,12 +692,8 @@
 	os_free(conf->model_description);
 	os_free(conf->model_url);
 	os_free(conf->upc);
-	{
-		unsigned int i;
-
-		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
-			wpabuf_free(conf->wps_vendor_ext[i]);
-	}
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+		wpabuf_free(conf->wps_vendor_ext[i]);
 	wpabuf_free(conf->wps_nfc_dh_pubkey);
 	wpabuf_free(conf->wps_nfc_dh_privkey);
 	wpabuf_free(conf->wps_nfc_dev_pw);
@@ -684,7 +719,6 @@
 	os_free(conf->hs20_operating_class);
 	os_free(conf->hs20_icons);
 	if (conf->hs20_osu_providers) {
-		size_t i;
 		for (i = 0; i < conf->hs20_osu_providers_count; i++) {
 			struct hs20_osu_provider *p;
 			size_t j;
@@ -702,8 +736,6 @@
 		os_free(conf->hs20_osu_providers);
 	}
 	if (conf->hs20_operator_icon) {
-		size_t i;
-
 		for (i = 0; i < conf->hs20_operator_icon_count; i++)
 			os_free(conf->hs20_operator_icon[i]);
 		os_free(conf->hs20_operator_icon);
@@ -740,10 +772,27 @@
 	os_free(conf->dpp_connector);
 	wpabuf_free(conf->dpp_netaccesskey);
 	wpabuf_free(conf->dpp_csign);
+#ifdef CONFIG_DPP2
+	hostapd_dpp_controller_conf_free(conf->dpp_controller);
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 
 	hostapd_config_free_sae_passwords(conf);
 
+#ifdef CONFIG_AIRTIME_POLICY
+	{
+		struct airtime_sta_weight *wt, *wt_prev;
+
+		wt = conf->airtime_weight_list;
+		conf->airtime_weight_list = NULL;
+		while (wt) {
+			wt_prev = wt;
+			wt = wt->next;
+			os_free(wt_prev);
+		}
+	}
+#endif /* CONFIG_AIRTIME_POLICY */
+
 	os_free(conf);
 }
 
@@ -1140,6 +1189,13 @@
 		return -1;
 	}
 
+#ifdef CONFIG_AIRTIME_POLICY
+	if (full_config && conf->airtime_mode > AIRTIME_MODE_STATIC &&
+	    !conf->airtime_update_interval) {
+		wpa_printf(MSG_ERROR, "Airtime update interval cannot be zero");
+		return -1;
+	}
+#endif /* CONFIG_AIRTIME_POLICY */
 	for (i = 0; i < NUM_TX_QUEUES; i++) {
 		if (hostapd_config_check_cw(conf, i))
 			return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 509677a..eebf898 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -15,6 +15,7 @@
 #include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "crypto/sha256.h"
 #include "wps/wps.h"
 #include "fst/fst.h"
 #include "vlan.h"
@@ -252,6 +253,18 @@
 	int vlan_id;
 };
 
+struct dpp_controller_conf {
+	struct dpp_controller_conf *next;
+	u8 pkhash[SHA256_MAC_LEN];
+	struct hostapd_ip_addr ipaddr;
+};
+
+struct airtime_sta_weight {
+	struct airtime_sta_weight *next;
+	unsigned int weight;
+	u8 addr[ETH_ALEN];
+};
+
 /**
  * struct hostapd_bss_config - Per-BSS configuration
  */
@@ -390,8 +403,11 @@
 
 	char *ca_cert;
 	char *server_cert;
+	char *server_cert2;
 	char *private_key;
+	char *private_key2;
 	char *private_key_passwd;
+	char *private_key_passwd2;
 	char *check_cert_subject;
 	int check_crl;
 	int check_crl_strict;
@@ -410,6 +426,8 @@
 	int eap_fast_prov;
 	int pac_key_lifetime;
 	int pac_key_refresh_time;
+	int eap_teap_auth;
+	int eap_teap_pac_no_inner;
 	int eap_sim_aka_result_ind;
 	int tnc;
 	int fragment_size;
@@ -570,6 +588,7 @@
 	int osen;
 	int proxy_arp;
 	int na_mcast_to_ucast;
+
 #ifdef CONFIG_HS20
 	int hs20;
 	int hs20_release;
@@ -692,6 +711,9 @@
 	struct wpabuf *dpp_netaccesskey;
 	unsigned int dpp_netaccesskey_expiry;
 	struct wpabuf *dpp_csign;
+#ifdef CONFIG_DPP2
+	struct dpp_controller_conf *dpp_controller;
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 
 #ifdef CONFIG_OWE
@@ -709,6 +731,100 @@
 #define BACKHAUL_BSS 1
 #define FRONTHAUL_BSS 2
 	int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
+
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_weight;
+	int airtime_limit;
+	struct airtime_sta_weight *airtime_weight_list;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_MACSEC
+	/**
+	 * macsec_policy - Determines the policy for MACsec secure session
+	 *
+	 * 0: MACsec not in use (default)
+	 * 1: MACsec enabled - Should secure, accept key server's advice to
+	 *    determine whether to use a secure session or not.
+	 */
+	int macsec_policy;
+
+	/**
+	 * macsec_integ_only - Determines how MACsec are transmitted
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: Encrypt traffic (default)
+	 * 1: Integrity only
+	 */
+	int macsec_integ_only;
+
+	/**
+	 * macsec_replay_protect - Enable MACsec replay protection
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: Replay protection disabled (default)
+	 * 1: Replay protection enabled
+	 */
+	int macsec_replay_protect;
+
+	/**
+	 * macsec_replay_window - MACsec replay protection window
+	 *
+	 * A window in which replay is tolerated, to allow receipt of frames
+	 * that have been misordered by the network.
+	 *
+	 * This setting applies only when MACsec replay protection active, i.e.,
+	 *  - macsec_replay_protect is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: No replay window, strict check (default)
+	 * 1..2^32-1: number of packets that could be misordered
+	 */
+	u32 macsec_replay_window;
+
+	/**
+	 * macsec_port - MACsec port (in SCI)
+	 *
+	 * Port component of the SCI.
+	 *
+	 * Range: 1-65534 (default: 1)
+	 */
+	int macsec_port;
+
+	/**
+	 * mka_priority - Priority of MKA Actor
+	 *
+	 * Range: 0-255 (default: 255)
+	 */
+	int mka_priority;
+
+	/**
+	 * mka_ckn - MKA pre-shared CKN
+	 */
+#define MACSEC_CKN_MAX_LEN 32
+	size_t mka_ckn_len;
+	u8 mka_ckn[MACSEC_CKN_MAX_LEN];
+
+	/**
+	 * mka_cak - MKA pre-shared CAK
+	 */
+#define MACSEC_CAK_MAX_LEN 32
+	size_t mka_cak_len;
+	u8 mka_cak[MACSEC_CAK_MAX_LEN];
+
+#define MKA_PSK_SET_CKN BIT(0)
+#define MKA_PSK_SET_CAK BIT(1)
+#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
+	/**
+	 * mka_psk_set - Whether mka_ckn and mka_cak are set
+	 */
+	u8 mka_psk_set;
+#endif /* CONFIG_MACSEC */
 };
 
 /**
@@ -727,7 +843,20 @@
 	u8 he_bss_color;
 	u8 he_default_pe_duration;
 	u8 he_twt_required;
-	u8 he_rts_threshold;
+	u16 he_rts_threshold;
+	u16 he_basic_mcs_nss_set;
+};
+
+/**
+ * struct spatial_reuse - Spatial reuse
+ */
+struct spatial_reuse {
+	u8 sr_control;
+	u8 non_srg_obss_pd_max_offset;
+	u8 srg_obss_pd_min_offset;
+	u8 srg_obss_pd_max_offset;
+	u8 srg_obss_color_bitmap;
+	u8 srg_obss_color_partial_bitmap;
 };
 
 /**
@@ -852,6 +981,10 @@
 	struct he_phy_capabilities_info he_phy_capab;
 	struct he_operation he_op;
 	struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
+	struct spatial_reuse spr;
+	u8 he_oper_chwidth;
+	u8 he_oper_centr_freq_seg0_idx;
+	u8 he_oper_centr_freq_seg1_idx;
 #endif /* CONFIG_IEEE80211AX */
 
 	/* VHT enable/disable config from CHAN_SWITCH */
@@ -861,9 +994,83 @@
 
 	int rssi_reject_assoc_rssi;
 	int rssi_reject_assoc_timeout;
+
+#ifdef CONFIG_AIRTIME_POLICY
+	enum {
+		AIRTIME_MODE_OFF = 0,
+		AIRTIME_MODE_STATIC = 1,
+		AIRTIME_MODE_DYNAMIC = 2,
+		AIRTIME_MODE_LIMIT = 3,
+		__AIRTIME_MODE_MAX,
+	} airtime_mode;
+	unsigned int airtime_update_interval;
+#define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1)
+#endif /* CONFIG_AIRTIME_POLICY */
 };
 
 
+static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_chwidth;
+}
+
+static inline void
+hostapd_set_oper_chwidth(struct hostapd_config *conf, u8 oper_chwidth)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_chwidth = oper_chwidth;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_centr_freq_seg0_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
+				     u8 oper_centr_freq_seg0_idx)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg1_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_centr_freq_seg1_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
+				     u8 oper_centr_freq_seg1_idx)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+}
+
+
 int hostapd_mac_comp(const void *a, const void *b);
 struct hostapd_config * hostapd_config_defaults(void);
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 067cf86..c0ededa 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -413,6 +413,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
+		    const struct ieee80211_he_capabilities *he_capab,
+		    size_t he_capab_len,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set)
 {
@@ -432,6 +434,8 @@
 	params.listen_interval = listen_interval;
 	params.ht_capabilities = ht_capab;
 	params.vht_capabilities = vht_capab;
+	params.he_capab = he_capab;
+	params.he_capab_len = he_capab_len;
 	params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
 	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
@@ -537,17 +541,20 @@
 
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 		     int freq, int channel, int ht_enabled, int vht_enabled,
-		     int sec_channel_offset, int vht_oper_chwidth,
+		     int he_enabled,
+		     int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1)
 {
 	struct hostapd_freq_params data;
+	struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
 
 	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
-				    vht_enabled, sec_channel_offset,
-				    vht_oper_chwidth,
+				    vht_enabled, he_enabled, sec_channel_offset,
+				    oper_chwidth,
 				    center_segment0, center_segment1,
-				    hapd->iface->current_mode ?
-				    hapd->iface->current_mode->vht_capab : 0))
+				    cmode ? cmode->vht_capab : 0,
+				    cmode ?
+				    &cmode->he_capab[IEEE80211_MODE_AP] : NULL))
 		return -1;
 
 	if (hapd->driver == NULL)
@@ -583,6 +590,16 @@
 }
 
 
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+				   unsigned int weight)
+{
+	if (!hapd->driver || !hapd->driver->sta_set_airtime_weight)
+		return 0;
+	return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr,
+						    weight);
+}
+
+
 int hostapd_set_country(struct hostapd_data *hapd, const char *country)
 {
 	if (hapd->driver == NULL ||
@@ -775,14 +792,16 @@
 int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
-			  int sec_channel_offset, int vht_oper_chwidth,
+			  int he_enabled,
+			  int sec_channel_offset, int oper_chwidth,
 			  int center_segment0, int center_segment1)
 {
 	struct hostapd_data *hapd = iface->bss[0];
 	struct hostapd_freq_params data;
 	int res;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
 
-	if (!hapd->driver || !hapd->driver->start_dfs_cac)
+	if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
 		return 0;
 
 	if (!iface->conf->ieee80211h) {
@@ -792,10 +811,11 @@
 	}
 
 	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
-				    vht_enabled, sec_channel_offset,
-				    vht_oper_chwidth, center_segment0,
+				    vht_enabled, he_enabled, sec_channel_offset,
+				    oper_chwidth, center_segment0,
 				    center_segment1,
-				    iface->current_mode->vht_capab)) {
+				    cmode->vht_capab,
+				    &cmode->he_capab[IEEE80211_MODE_AP])) {
 		wpa_printf(MSG_ERROR, "Can't set freq params");
 		return -1;
 	}
@@ -919,15 +939,17 @@
 	if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
 		params.ch_width = 40;
 
-	/* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
+	/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
 	 */
-	if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
-		if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+	if ((hapd->iface->conf->ieee80211ax ||
+	     hapd->iface->conf->ieee80211ac) &&
+	    params.ht40_enabled) {
+		u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
+
+		if (oper_chwidth == CHANWIDTH_80MHZ)
 			params.ch_width = 80;
-		else if (hapd->iface->conf->vht_oper_chwidth ==
-			 VHT_CHANWIDTH_160MHZ ||
-			 hapd->iface->conf->vht_oper_chwidth ==
-			 VHT_CHANWIDTH_80P80MHZ)
+		else if (oper_chwidth == CHANWIDTH_160MHZ ||
+			 oper_chwidth == CHANWIDTH_80P80MHZ)
 			params.ch_width = 160;
 	}
 
@@ -936,3 +958,13 @@
 
 	return ret;
 }
+
+
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+			     u16 reason_code, const u8 *ie, size_t ielen)
+{
+	if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code,
+					  ie, ielen);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index de40171..ca7f7ab 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -41,6 +41,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
+		    const struct ieee80211_he_capabilities *he_capab,
+		    size_t he_capab_len,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
@@ -61,12 +63,14 @@
 int hostapd_flush(struct hostapd_data *hapd);
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 		     int freq, int channel, int ht_enabled, int vht_enabled,
-		     int sec_channel_offset, int vht_oper_chwidth,
+		     int he_enabled, int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
 int hostapd_set_frag(struct hostapd_data *hapd, int frag);
 int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
 			  int total_flags, int flags_or, int flags_and);
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+				   unsigned int weight);
 int hostapd_set_country(struct hostapd_data *hapd, const char *country);
 int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
 				int cw_min, int cw_max, int burst_time);
@@ -122,9 +126,12 @@
 int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
-			  int sec_channel_offset, int vht_oper_chwidth,
+			  int he_enabled,
+			  int sec_channel_offset, int oper_chwidth,
 			  int center_segment0, int center_segment1);
 int hostapd_drv_do_acs(struct hostapd_data *hapd);
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+			     u16 reason_code, const u8 *ie, size_t ielen);
 
 
 #include "drivers/driver.h"
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index eced6c7..b3d9107 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -120,6 +120,8 @@
 	srv.eap_fast_prov = conf->eap_fast_prov;
 	srv.pac_key_lifetime = conf->pac_key_lifetime;
 	srv.pac_key_refresh_time = conf->pac_key_refresh_time;
+	srv.eap_teap_auth = conf->eap_teap_auth;
+	srv.eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner;
 	srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
 	srv.tnc = conf->tnc;
 	srv.wps = hapd->wps;
@@ -195,7 +197,8 @@
 #ifdef EAP_TLS_FUNCS
 	if (hapd->conf->eap_server &&
 	    (hapd->conf->ca_cert || hapd->conf->server_cert ||
-	     hapd->conf->private_key || hapd->conf->dh_file)) {
+	     hapd->conf->private_key || hapd->conf->dh_file ||
+	     hapd->conf->server_cert2 || hapd->conf->private_key2)) {
 		struct tls_config conf;
 		struct tls_connection_params params;
 
@@ -224,8 +227,11 @@
 		os_memset(&params, 0, sizeof(params));
 		params.ca_cert = hapd->conf->ca_cert;
 		params.client_cert = hapd->conf->server_cert;
+		params.client_cert2 = hapd->conf->server_cert2;
 		params.private_key = hapd->conf->private_key;
+		params.private_key2 = hapd->conf->private_key2;
 		params.private_key_passwd = hapd->conf->private_key_passwd;
+		params.private_key_passwd2 = hapd->conf->private_key_passwd2;
 		params.dh_file = hapd->conf->dh_file;
 		params.openssl_ciphers = hapd->conf->openssl_ciphers;
 		params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 3e62991..a51b949 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -347,7 +347,7 @@
 
 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
 					  hapd->iconf->secondary_channel,
-					  hapd->iconf->vht_oper_chwidth,
+					  hostapd_get_oper_chwidth(hapd->iconf),
 					  &op_class, &channel) ==
 	    NUM_HOSTAPD_MODES)
 		return eid;
@@ -398,7 +398,8 @@
 	if (hapd->iconf->ieee80211ax) {
 		buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
 			3 + sizeof(struct ieee80211_he_operation) +
-			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
+			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+			3 + sizeof(struct ieee80211_spatial_reuse);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -509,9 +510,10 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		pos = hostapd_eid_he_capab(hapd, pos);
+		pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
 		pos = hostapd_eid_he_operation(hapd, pos);
 		pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
+		pos = hostapd_eid_spatial_reuse(hapd, pos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -593,7 +595,7 @@
 
 	pos = ssid_list;
 	end = ssid_list + ssid_list_len;
-	while (end - pos >= 1) {
+	while (end - pos >= 2) {
 		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[1] == 0)
@@ -1088,7 +1090,8 @@
 	if (hapd->iconf->ieee80211ax) {
 		tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
 			3 + sizeof(struct ieee80211_he_operation) +
-			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
+			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+			3 + sizeof(struct ieee80211_spatial_reuse);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1223,9 +1226,11 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		tailpos = hostapd_eid_he_capab(hapd, tailpos);
+		tailpos = hostapd_eid_he_capab(hapd, tailpos,
+					       IEEE80211_MODE_AP);
 		tailpos = hostapd_eid_he_operation(hapd, tailpos);
 		tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
+		tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1394,6 +1399,7 @@
 	struct hostapd_freq_params freq;
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_config *iconf = iface->conf;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
 	struct wpabuf *beacon, *proberesp, *assocresp;
 	int res, ret = -1;
 
@@ -1417,15 +1423,16 @@
 	params.reenable = hapd->reenable_beacon;
 	hapd->reenable_beacon = 0;
 
-	if (iface->current_mode &&
+	if (cmode &&
 	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
 				    iconf->channel, iconf->ieee80211n,
-				    iconf->ieee80211ac,
+				    iconf->ieee80211ac, iconf->ieee80211ax,
 				    iconf->secondary_channel,
-				    iconf->vht_oper_chwidth,
-				    iconf->vht_oper_centr_freq_seg0_idx,
-				    iconf->vht_oper_centr_freq_seg1_idx,
-				    iface->current_mode->vht_capab) == 0)
+				    hostapd_get_oper_chwidth(iconf),
+				    hostapd_get_oper_centr_freq_seg0_idx(iconf),
+				    hostapd_get_oper_centr_freq_seg1_idx(iconf),
+				    cmode->vht_capab,
+				    &cmode->he_capab[IEEE80211_MODE_AP]) == 0)
 		params.freq = &freq;
 
 	res = hostapd_drv_set_ap(hapd, &params);
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index c693715..2c4953d 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -712,6 +712,7 @@
 			  "secondary_channel=%d\n"
 			  "ieee80211n=%d\n"
 			  "ieee80211ac=%d\n"
+			  "ieee80211ax=%d\n"
 			  "beacon_int=%u\n"
 			  "dtim_period=%d\n",
 			  iface->conf->channel,
@@ -720,6 +721,7 @@
 			  iface->conf->ieee80211n && !hapd->conf->disable_11n,
 			  iface->conf->ieee80211ac &&
 			  !hapd->conf->disable_11ac,
+			  iface->conf->ieee80211ax,
 			  iface->conf->beacon_int,
 			  hapd->conf->dtim_period);
 	if (os_snprintf_error(buflen - len, ret))
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 79cd00f..ac23c2b 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -28,17 +28,17 @@
 	if (iface->conf->ieee80211n && iface->conf->secondary_channel)
 		n_chans = 2;
 
-	if (iface->conf->ieee80211ac) {
-		switch (iface->conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_USE_HT:
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CHANWIDTH_USE_HT:
 			break;
-		case VHT_CHANWIDTH_80MHZ:
+		case CHANWIDTH_80MHZ:
 			n_chans = 4;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
+		case CHANWIDTH_160MHZ:
 			n_chans = 8;
 			break;
-		case VHT_CHANWIDTH_80P80MHZ:
+		case CHANWIDTH_80P80MHZ:
 			n_chans = 4;
 			*seg1 = 4;
 			break;
@@ -188,8 +188,8 @@
  * The function assumes HT40+ operation.
  * Make sure to adjust the following variables after calling this:
  *  - hapd->secondary_channel
- *  - hapd->vht_oper_centr_freq_seg0_idx
- *  - hapd->vht_oper_centr_freq_seg1_idx
+ *  - hapd->vht/he_oper_centr_freq_seg0_idx
+ *  - hapd->vht/he_oper_centr_freq_seg1_idx
  */
 static int dfs_find_channel(struct hostapd_iface *iface,
 			    struct hostapd_channel_data **ret_chan,
@@ -232,44 +232,44 @@
 }
 
 
-static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
-				       struct hostapd_channel_data *chan,
-				       int secondary_channel,
-				       u8 *vht_oper_centr_freq_seg0_idx,
-				       u8 *vht_oper_centr_freq_seg1_idx)
+static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+				   struct hostapd_channel_data *chan,
+				   int secondary_channel,
+				   u8 *oper_centr_freq_seg0_idx,
+				   u8 *oper_centr_freq_seg1_idx)
 {
-	if (!iface->conf->ieee80211ac)
+	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
 		return;
 
 	if (!chan)
 		return;
 
-	*vht_oper_centr_freq_seg1_idx = 0;
+	*oper_centr_freq_seg1_idx = 0;
 
-	switch (iface->conf->vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
+	switch (hostapd_get_oper_chwidth(iface->conf)) {
+	case CHANWIDTH_USE_HT:
 		if (secondary_channel == 1)
-			*vht_oper_centr_freq_seg0_idx = chan->chan + 2;
+			*oper_centr_freq_seg0_idx = chan->chan + 2;
 		else if (secondary_channel == -1)
-			*vht_oper_centr_freq_seg0_idx = chan->chan - 2;
+			*oper_centr_freq_seg0_idx = chan->chan - 2;
 		else
-			*vht_oper_centr_freq_seg0_idx = chan->chan;
+			*oper_centr_freq_seg0_idx = chan->chan;
 		break;
-	case VHT_CHANWIDTH_80MHZ:
-		*vht_oper_centr_freq_seg0_idx = chan->chan + 6;
+	case CHANWIDTH_80MHZ:
+		*oper_centr_freq_seg0_idx = chan->chan + 6;
 		break;
-	case VHT_CHANWIDTH_160MHZ:
-		*vht_oper_centr_freq_seg0_idx = chan->chan + 14;
+	case CHANWIDTH_160MHZ:
+		*oper_centr_freq_seg0_idx = chan->chan + 14;
 		break;
 	default:
 		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
-		*vht_oper_centr_freq_seg0_idx = 0;
+		*oper_centr_freq_seg0_idx = 0;
 		break;
 	}
 
 	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
-		   *vht_oper_centr_freq_seg0_idx,
-		   *vht_oper_centr_freq_seg1_idx);
+		   *oper_centr_freq_seg0_idx,
+		   *oper_centr_freq_seg1_idx);
 }
 
 
@@ -288,24 +288,24 @@
 	if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
 		channel_no -= 4;
 
-	/* VHT */
-	if (iface->conf->ieee80211ac) {
-		switch (iface->conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_USE_HT:
+	/* VHT/HE */
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CHANWIDTH_USE_HT:
 			break;
-		case VHT_CHANWIDTH_80MHZ:
-			channel_no =
-				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+		case CHANWIDTH_80MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 6;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
-			channel_no =
-				iface->conf->vht_oper_centr_freq_seg0_idx - 14;
+		case CHANWIDTH_160MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 14;
 			break;
-		case VHT_CHANWIDTH_80P80MHZ:
-			channel_no =
-				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
-			chan_seg1 =
-				iface->conf->vht_oper_centr_freq_seg1_idx - 6;
+		case CHANWIDTH_80P80MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 6;
+			chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
+				iface->conf) - 6;
 			break;
 		default:
 			wpa_printf(MSG_INFO,
@@ -348,7 +348,7 @@
 			   mode->num_channels, channel_no, iface->conf->channel,
 			   iface->conf->ieee80211n,
 			   iface->conf->secondary_channel,
-			   iface->conf->vht_oper_chwidth);
+			   hostapd_get_oper_chwidth(iface->conf));
 
 		for (i = 0; i < mode->num_channels; i++) {
 			wpa_printf(MSG_DEBUG, "Available channel: %d",
@@ -435,8 +435,8 @@
 static struct hostapd_channel_data *
 dfs_get_valid_channel(struct hostapd_iface *iface,
 		      int *secondary_channel,
-		      u8 *vht_oper_centr_freq_seg0_idx,
-		      u8 *vht_oper_centr_freq_seg1_idx,
+		      u8 *oper_centr_freq_seg0_idx,
+		      u8 *oper_centr_freq_seg1_idx,
 		      int skip_radar)
 {
 	struct hostapd_hw_modes *mode;
@@ -447,8 +447,8 @@
 
 	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
 	*secondary_channel = 0;
-	*vht_oper_centr_freq_seg0_idx = 0;
-	*vht_oper_centr_freq_seg1_idx = 0;
+	*oper_centr_freq_seg0_idx = 0;
+	*oper_centr_freq_seg1_idx = 0;
 
 	if (iface->current_mode == NULL)
 		return NULL;
@@ -473,10 +473,10 @@
 	else
 		*secondary_channel = 0;
 
-	dfs_adjust_vht_center_freq(iface, chan,
-				   *secondary_channel,
-				   vht_oper_centr_freq_seg0_idx,
-				   vht_oper_centr_freq_seg1_idx);
+	dfs_adjust_center_freq(iface, chan,
+			       *secondary_channel,
+			       oper_centr_freq_seg0_idx,
+			       oper_centr_freq_seg1_idx);
 
 	return chan;
 }
@@ -724,8 +724,8 @@
 			iface->freq = channel->freq;
 			iface->conf->channel = channel->chan;
 			iface->conf->secondary_channel = sec;
-			iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
-			iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
+			hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+			hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
 		}
 	} while (res);
 
@@ -736,20 +736,19 @@
 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
 		iface->freq,
 		iface->conf->channel, iface->conf->secondary_channel,
-		iface->conf->vht_oper_chwidth,
-		iface->conf->vht_oper_centr_freq_seg0_idx,
-		iface->conf->vht_oper_centr_freq_seg1_idx,
+		hostapd_get_oper_chwidth(iface->conf),
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
 		iface->dfs_cac_ms / 1000);
 
-	res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-				    iface->freq,
-				    iface->conf->channel,
-				    iface->conf->ieee80211n,
-				    iface->conf->ieee80211ac,
-				    iface->conf->secondary_channel,
-				    iface->conf->vht_oper_chwidth,
-				    iface->conf->vht_oper_centr_freq_seg0_idx,
-				    iface->conf->vht_oper_centr_freq_seg1_idx);
+	res = hostapd_start_dfs_cac(
+		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+		iface->conf->ieee80211n, iface->conf->ieee80211ac,
+		iface->conf->ieee80211ax,
+		iface->conf->secondary_channel,
+		hostapd_get_oper_chwidth(iface->conf),
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
 
 	if (res) {
 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
@@ -842,16 +841,16 @@
 {
 	struct hostapd_channel_data *channel;
 	int secondary_channel;
-	u8 vht_oper_centr_freq_seg0_idx = 0;
-	u8 vht_oper_centr_freq_seg1_idx = 0;
+	u8 oper_centr_freq_seg0_idx = 0;
+	u8 oper_centr_freq_seg1_idx = 0;
 	int skip_radar = 0;
 	int err = 1;
 
 	/* Radar detected during active CAC */
 	iface->cac_started = 0;
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
-					&vht_oper_centr_freq_seg0_idx,
-					&vht_oper_centr_freq_seg1_idx,
+					&oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
 					skip_radar);
 
 	if (!channel) {
@@ -868,10 +867,10 @@
 	iface->freq = channel->freq;
 	iface->conf->channel = channel->chan;
 	iface->conf->secondary_channel = secondary_channel;
-	iface->conf->vht_oper_centr_freq_seg0_idx =
-		vht_oper_centr_freq_seg0_idx;
-	iface->conf->vht_oper_centr_freq_seg1_idx =
-		vht_oper_centr_freq_seg1_idx;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+					     oper_centr_freq_seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+					     oper_centr_freq_seg1_idx);
 	err = 0;
 
 	hostapd_setup_interface_complete(iface, err);
@@ -883,12 +882,13 @@
 {
 	struct hostapd_channel_data *channel;
 	int secondary_channel;
-	u8 vht_oper_centr_freq_seg0_idx;
-	u8 vht_oper_centr_freq_seg1_idx;
+	u8 oper_centr_freq_seg0_idx;
+	u8 oper_centr_freq_seg1_idx;
 	int skip_radar = 1;
 	struct csa_settings csa_settings;
 	unsigned int i;
 	int err = 1;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
 
 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
 		   __func__, iface->cac_started ? "yes" : "no",
@@ -911,8 +911,8 @@
 
 	/* Perform channel switch/CSA */
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
-					&vht_oper_centr_freq_seg0_idx,
-					&vht_oper_centr_freq_seg1_idx,
+					&oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
 					skip_radar);
 
 	if (!channel) {
@@ -923,8 +923,8 @@
 		 */
 		skip_radar = 0;
 		channel = dfs_get_valid_channel(iface, &secondary_channel,
-						&vht_oper_centr_freq_seg0_idx,
-						&vht_oper_centr_freq_seg1_idx,
+						&oper_centr_freq_seg0_idx,
+						&oper_centr_freq_seg1_idx,
 						skip_radar);
 		if (!channel) {
 			wpa_printf(MSG_INFO,
@@ -936,10 +936,10 @@
 		iface->freq = channel->freq;
 		iface->conf->channel = channel->chan;
 		iface->conf->secondary_channel = secondary_channel;
-		iface->conf->vht_oper_centr_freq_seg0_idx =
-			vht_oper_centr_freq_seg0_idx;
-		iface->conf->vht_oper_centr_freq_seg1_idx =
-			vht_oper_centr_freq_seg1_idx;
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+						     oper_centr_freq_seg0_idx);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+						     oper_centr_freq_seg1_idx);
 
 		hostapd_disable_iface(iface);
 		hostapd_enable_iface(iface);
@@ -962,11 +962,13 @@
 				      channel->chan,
 				      iface->conf->ieee80211n,
 				      iface->conf->ieee80211ac,
+				      iface->conf->ieee80211ax,
 				      secondary_channel,
-				      iface->conf->vht_oper_chwidth,
-				      vht_oper_centr_freq_seg0_idx,
-				      vht_oper_centr_freq_seg1_idx,
-				      iface->current_mode->vht_capab);
+				      hostapd_get_oper_chwidth(iface->conf),
+				      oper_centr_freq_seg0_idx,
+				      oper_centr_freq_seg1_idx,
+				      cmode->vht_capab,
+				      &cmode->he_capab[IEEE80211_MODE_AP]);
 
 	if (err) {
 		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
@@ -986,10 +988,10 @@
 		iface->freq = channel->freq;
 		iface->conf->channel = channel->chan;
 		iface->conf->secondary_channel = secondary_channel;
-		iface->conf->vht_oper_centr_freq_seg0_idx =
-			vht_oper_centr_freq_seg0_idx;
-		iface->conf->vht_oper_centr_freq_seg1_idx =
-			vht_oper_centr_freq_seg1_idx;
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+						     oper_centr_freq_seg0_idx);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+						     oper_centr_freq_seg1_idx);
 
 		hostapd_disable_iface(iface);
 		hostapd_enable_iface(iface);
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 75edbc9..697c3ba 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -16,6 +16,7 @@
 #include "hostapd.h"
 #include "ap_drv_ops.h"
 #include "gas_query_ap.h"
+#include "gas_serv.h"
 #include "wpa_auth.h"
 #include "dpp_hostapd.h"
 
@@ -557,6 +558,14 @@
 	 * received hash values */
 	dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
 				r_bootstrap, &own_bi, &peer_bi);
+#ifdef CONFIG_DPP2
+	if (!own_bi) {
+		if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+					src, hdr, buf, len, freq, i_bootstrap,
+					r_bootstrap) == 0)
+			return;
+	}
+#endif /* CONFIG_DPP2 */
 	if (!own_bi) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"No matching own bootstrapping key found - ignore message");
@@ -1357,6 +1366,12 @@
 	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
 		" freq=%u type=%d", MAC2STR(src), freq, type);
 
+#ifdef CONFIG_DPP2
+	if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+				src, hdr, buf, len, freq, NULL, NULL) == 0)
+		return;
+#endif /* CONFIG_DPP2 */
+
 	switch (type) {
 	case DPP_PA_AUTHENTICATION_REQ:
 		hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
@@ -1410,7 +1425,8 @@
 
 struct wpabuf *
 hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
-			    const u8 *query, size_t query_len)
+			    const u8 *query, size_t query_len,
+			    const u8 *data, size_t data_len)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
 	struct wpabuf *resp;
@@ -1418,6 +1434,13 @@
 	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
 	if (!auth || !auth->auth_success ||
 	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+#ifdef CONFIG_DPP2
+		if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
+				     data_len) == 0) {
+			/* Response will be forwarded once received over TCP */
+			return NULL;
+		}
+#endif /* CONFIG_DPP2 */
 		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
 		return NULL;
 	}
@@ -1609,11 +1632,67 @@
 }
 
 
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_relay_tx(void *ctx, const u8 *addr, unsigned int freq,
+				 const u8 *msg, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	u8 *buf;
+
+	wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
+		   MAC2STR(addr), freq);
+	buf = os_malloc(2 + len);
+	if (!buf)
+		return;
+	buf[0] = WLAN_ACTION_PUBLIC;
+	buf[1] = WLAN_PA_VENDOR_SPECIFIC;
+	os_memcpy(buf + 2, msg, len);
+	hostapd_drv_send_action(hapd, freq, 0, addr, buf, 2 + len);
+	os_free(buf);
+}
+
+
+static void hostapd_dpp_relay_gas_resp_tx(void *ctx, const u8 *addr,
+					  u8 dialog_token, int prot,
+					  struct wpabuf *buf)
+{
+	struct hostapd_data *hapd = ctx;
+
+	gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static int hostapd_dpp_add_controllers(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_DPP2
+	struct dpp_controller_conf *ctrl;
+	struct dpp_relay_config config;
+
+	os_memset(&config, 0, sizeof(config));
+	config.cb_ctx = hapd;
+	config.tx = hostapd_dpp_relay_tx;
+	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+	for (ctrl = hapd->conf->dpp_controller; ctrl; ctrl = ctrl->next) {
+		config.ipaddr = &ctrl->ipaddr;
+		config.pkhash = ctrl->pkhash;
+		if (dpp_relay_add_controller(hapd->iface->interfaces->dpp,
+					     &config) < 0)
+			return -1;
+	}
+#endif /* CONFIG_DPP2 */
+
+	return 0;
+}
+
+
 int hostapd_dpp_init(struct hostapd_data *hapd)
 {
 	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
 	hapd->dpp_init_done = 1;
-	return 0;
+	return hostapd_dpp_add_controllers(hapd);
 }
 
 
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 449ca16..c1ec5d7 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -19,7 +19,8 @@
 			   const u8 *data, size_t data_len, int ok);
 struct wpabuf *
 hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
-			    const u8 *query, size_t query_len);
+			    const u8 *query, size_t query_len,
+			    const u8 *data, size_t data_len);
 void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
 int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 952a3d5..3158768 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -772,7 +772,8 @@
 
 
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
-			     int offset, int width, int cf1, int cf2)
+			     int offset, int width, int cf1, int cf2,
+			     int finished)
 {
 	/* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */
 
@@ -783,7 +784,8 @@
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
-		       "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       finished ? "had" : "starting",
 		       freq, ht, hapd->iconf->ch_switch_vht_config, offset,
 		       width, channel_width_to_string(width), cf1, cf2);
 
@@ -806,19 +808,19 @@
 
 	switch (width) {
 	case CHAN_WIDTH_80:
-		chwidth = VHT_CHANWIDTH_80MHZ;
+		chwidth = CHANWIDTH_80MHZ;
 		break;
 	case CHAN_WIDTH_80P80:
-		chwidth = VHT_CHANWIDTH_80P80MHZ;
+		chwidth = CHANWIDTH_80P80MHZ;
 		break;
 	case CHAN_WIDTH_160:
-		chwidth = VHT_CHANWIDTH_160MHZ;
+		chwidth = CHANWIDTH_160MHZ;
 		break;
 	case CHAN_WIDTH_20_NOHT:
 	case CHAN_WIDTH_20:
 	case CHAN_WIDTH_40:
 	default:
-		chwidth = VHT_CHANWIDTH_USE_HT;
+		chwidth = CHANWIDTH_USE_HT;
 		break;
 	}
 
@@ -851,13 +853,22 @@
 	hapd->iconf->ch_switch_vht_config = 0;
 
 	hapd->iconf->secondary_channel = offset;
-	hapd->iconf->vht_oper_chwidth = chwidth;
-	hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
-	hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
+	hostapd_set_oper_chwidth(hapd->iconf, chwidth);
+	hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
 
 	is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
 				  hapd->iface->num_hw_features);
 
+	wpa_msg(hapd->msg_ctx, MSG_INFO,
+		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d",
+		finished ? WPA_EVENT_CHANNEL_SWITCH :
+		WPA_EVENT_CHANNEL_SWITCH_STARTED,
+		freq, ht, offset, channel_width_to_string(width),
+		cf1, cf2, is_dfs);
+	if (!finished)
+		return;
+
 	if (hapd->csa_in_progress &&
 	    freq == hapd->cs_freq_params.freq) {
 		hostapd_cleanup_cs_params(hapd);
@@ -949,28 +960,31 @@
 		goto out;
 	}
 
-	if (hapd->iface->conf->ieee80211ac) {
+	if (hapd->iface->conf->ieee80211ac || hapd->iface->conf->ieee80211ax) {
 		/* set defaults for backwards compatibility */
-		hapd->iconf->vht_oper_centr_freq_seg1_idx = 0;
-		hapd->iconf->vht_oper_centr_freq_seg0_idx = 0;
-		hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
+		hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, 0);
+		hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_USE_HT);
 		if (acs_res->ch_width == 80) {
-			hapd->iconf->vht_oper_centr_freq_seg0_idx =
-				acs_res->vht_seg0_center_ch;
-			hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			hostapd_set_oper_centr_freq_seg0_idx(
+				hapd->iconf, acs_res->vht_seg0_center_ch);
+			hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_80MHZ);
 		} else if (acs_res->ch_width == 160) {
 			if (acs_res->vht_seg1_center_ch == 0) {
-				hapd->iconf->vht_oper_centr_freq_seg0_idx =
-					acs_res->vht_seg0_center_ch;
-				hapd->iconf->vht_oper_chwidth =
-					VHT_CHANWIDTH_160MHZ;
+				hostapd_set_oper_centr_freq_seg0_idx(
+					hapd->iconf,
+					acs_res->vht_seg0_center_ch);
+				hostapd_set_oper_chwidth(hapd->iconf,
+							 CHANWIDTH_160MHZ);
 			} else {
-				hapd->iconf->vht_oper_centr_freq_seg0_idx =
-					acs_res->vht_seg0_center_ch;
-				hapd->iconf->vht_oper_centr_freq_seg1_idx =
-					acs_res->vht_seg1_center_ch;
-				hapd->iconf->vht_oper_chwidth =
-					VHT_CHANWIDTH_80P80MHZ;
+				hostapd_set_oper_centr_freq_seg0_idx(
+					hapd->iconf,
+					acs_res->vht_seg0_center_ch);
+				hostapd_set_oper_centr_freq_seg1_idx(
+					hapd->iconf,
+					acs_res->vht_seg1_center_ch);
+				hostapd_set_oper_chwidth(hapd->iconf,
+							 CHANWIDTH_80P80MHZ);
 			}
 		}
 	}
@@ -1575,6 +1589,73 @@
 }
 
 
+#ifdef CONFIG_OWE
+static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd,
+				      const u8 *peer, const u8 *ie,
+				      size_t ie_len)
+{
+	u16 status;
+	struct sta_info *sta;
+	struct ieee802_11_elems elems;
+
+	if (!hapd || !hapd->wpa_auth) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid hapd context");
+		return -1;
+	}
+	if (!peer) {
+		wpa_printf(MSG_DEBUG, "OWE: Peer unknown");
+		return -1;
+	}
+	if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) {
+		wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured");
+		status = WLAN_STATUS_AKMP_NOT_VALID;
+		goto err;
+	}
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "OWE: Failed to parse OWE IE for "
+			   MACSTR, MAC2STR(peer));
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto err;
+	}
+	status = owe_validate_request(hapd, peer, elems.rsn_ie,
+				      elems.rsn_ie_len,
+				      elems.owe_dh, elems.owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto err;
+
+	sta = ap_get_sta(hapd, peer);
+	if (sta) {
+		ap_sta_no_session_timeout(hapd, sta);
+		accounting_sta_stop(hapd, sta);
+
+		/*
+		 * Make sure that the previously registered inactivity timer
+		 * will not remove the STA immediately.
+		 */
+		sta->timeout_next = STA_NULLFUNC;
+	} else {
+		sta = ap_sta_add(hapd, peer);
+		if (!sta) {
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto err;
+		}
+	}
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+	status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie,
+				    elems.rsn_ie_len, elems.owe_dh,
+				    elems.owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		ap_free_sta(hapd, sta);
+
+	return 0;
+err:
+	hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0);
+	return 0;
+}
+#endif /* CONFIG_OWE */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -1680,6 +1761,15 @@
 				    data->assoc_info.req_ies_len,
 				    data->assoc_info.reassoc);
 		break;
+#ifdef CONFIG_OWE
+	case EVENT_UPDATE_DH:
+		if (!data)
+			return;
+		hostapd_notif_update_dh_ie(hapd, data->update_dh.peer,
+					   data->update_dh.ie,
+					   data->update_dh.ie_len);
+		break;
+#endif /* CONFIG_OWE */
 	case EVENT_DISASSOC:
 		if (data)
 			hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
@@ -1696,6 +1786,7 @@
 	case EVENT_AUTH:
 		hostapd_notif_auth(hapd, &data->auth);
 		break;
+	case EVENT_CH_SWITCH_STARTED:
 	case EVENT_CH_SWITCH:
 		if (!data)
 			break;
@@ -1704,7 +1795,8 @@
 					data->ch_switch.ch_offset,
 					data->ch_switch.ch_width,
 					data->ch_switch.cf1,
-					data->ch_switch.cf2);
+					data->ch_switch.cf2,
+					event == EVENT_CH_SWITCH);
 		break;
 	case EVENT_CONNECT_FAILED_REASON:
 		if (!data)
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index a7df810..9567e20 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1522,9 +1522,9 @@
 
 
 #ifdef CONFIG_DPP
-static void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
-					const u8 *sa, u8 dialog_token,
-					int prot, struct wpabuf *buf)
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+				 const u8 *sa, u8 dialog_token,
+				 int prot, struct wpabuf *buf)
 {
 	struct wpabuf *tx_buf;
 
@@ -1681,7 +1681,8 @@
 	if (dpp) {
 		struct wpabuf *msg;
 
-		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen);
+		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
+						  data, len);
 		if (!msg)
 			return;
 		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 2cf1817..1528af4 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -88,4 +88,8 @@
 int gas_serv_init(struct hostapd_data *hapd);
 void gas_serv_deinit(struct hostapd_data *hapd);
 
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+				 const u8 *sa, u8 dialog_token,
+				 int prot, struct wpabuf *buf);
+
 #endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 0bd6892..cc75a77 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -50,6 +50,8 @@
 #include "fils_hlp.h"
 #include "acs.h"
 #include "hs20.h"
+#include "airtime_policy.h"
+#include "wpa_auth_kay.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -260,11 +262,14 @@
 		hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
 		hapd->iconf->ht_capab = oldconf->ht_capab;
 		hapd->iconf->vht_capab = oldconf->vht_capab;
-		hapd->iconf->vht_oper_chwidth = oldconf->vht_oper_chwidth;
-		hapd->iconf->vht_oper_centr_freq_seg0_idx =
-			oldconf->vht_oper_centr_freq_seg0_idx;
-		hapd->iconf->vht_oper_centr_freq_seg1_idx =
-			oldconf->vht_oper_centr_freq_seg1_idx;
+		hostapd_set_oper_chwidth(hapd->iconf,
+					 hostapd_get_oper_chwidth(oldconf));
+		hostapd_set_oper_centr_freq_seg0_idx(
+			hapd->iconf,
+			hostapd_get_oper_centr_freq_seg0_idx(oldconf));
+		hostapd_set_oper_centr_freq_seg1_idx(
+			hapd->iconf,
+			hostapd_get_oper_centr_freq_seg1_idx(oldconf));
 		hapd->conf = newconf->bss[j];
 		hostapd_reload_bss(hapd);
 	}
@@ -369,6 +374,7 @@
 #endif /* CONFIG_NO_RADIUS */
 
 	hostapd_deinit_wps(hapd);
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
 #ifdef CONFIG_DPP
 	hostapd_dpp_deinit(hapd);
 	gas_query_ap_deinit(hapd->gas);
@@ -491,6 +497,7 @@
 	iface->basic_rates = NULL;
 	ap_list_deinit(iface);
 	sta_track_deinit(iface);
+	airtime_policy_update_deinit(iface);
 }
 
 
@@ -1863,10 +1870,13 @@
 				     hapd->iconf->channel,
 				     hapd->iconf->ieee80211n,
 				     hapd->iconf->ieee80211ac,
+				     hapd->iconf->ieee80211ax,
 				     hapd->iconf->secondary_channel,
-				     hapd->iconf->vht_oper_chwidth,
-				     hapd->iconf->vht_oper_centr_freq_seg0_idx,
-				     hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
+				     hostapd_get_oper_chwidth(hapd->iconf),
+				     hostapd_get_oper_centr_freq_seg0_idx(
+					     hapd->iconf),
+				     hostapd_get_oper_centr_freq_seg1_idx(
+					     hapd->iconf))) {
 			wpa_printf(MSG_ERROR, "Could not set channel for "
 				   "kernel driver");
 			goto fail;
@@ -1976,6 +1986,7 @@
 
 	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
 	hostapd_owe_update_trans(iface);
+	airtime_policy_update_init(iface);
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
 	if (hapd->setup_complete_cb)
 		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
@@ -2996,6 +3007,8 @@
 	}
 #endif /* CONFIG_P2P */
 
+	airtime_policy_new_sta(hapd, sta);
+
 	/* Start accounting here, if IEEE 802.1X and WPA are not used.
 	 * IEEE 802.1X/WPA code will start accounting after the station has
 	 * been authorized. */
@@ -3036,6 +3049,14 @@
 		eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
 				       ap_handle_timer, hapd, sta);
 	}
+
+#ifdef CONFIG_MACSEC
+	if (hapd->conf->wpa_key_mgmt == WPA_KEY_MGMT_NONE &&
+	    hapd->conf->mka_psk_set)
+		ieee802_1x_create_preshared_mka_hapd(hapd, sta);
+	else
+		ieee802_1x_alloc_kay_sm_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
 }
 
 
@@ -3195,6 +3216,8 @@
 				      struct hostapd_freq_params *old_params)
 {
 	int channel;
+	u8 seg0, seg1;
+	struct hostapd_hw_modes *mode;
 
 	if (!params->channel) {
 		/* check if the new channel is supported by hw */
@@ -3205,33 +3228,37 @@
 	if (!channel)
 		return -1;
 
+	mode = hapd->iface->current_mode;
+
 	/* if a pointer to old_params is provided we save previous state */
 	if (old_params &&
 	    hostapd_set_freq_params(old_params, conf->hw_mode,
 				    hostapd_hw_get_freq(hapd, conf->channel),
 				    conf->channel, conf->ieee80211n,
-				    conf->ieee80211ac,
+				    conf->ieee80211ac, conf->ieee80211ax,
 				    conf->secondary_channel,
-				    conf->vht_oper_chwidth,
-				    conf->vht_oper_centr_freq_seg0_idx,
-				    conf->vht_oper_centr_freq_seg1_idx,
-				    conf->vht_capab))
+				    hostapd_get_oper_chwidth(conf),
+				    hostapd_get_oper_centr_freq_seg0_idx(conf),
+				    hostapd_get_oper_centr_freq_seg1_idx(conf),
+				    conf->vht_capab,
+				    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+				    NULL))
 		return -1;
 
 	switch (params->bandwidth) {
 	case 0:
 	case 20:
 	case 40:
-		conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
 		break;
 	case 80:
 		if (params->center_freq2)
-			conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
+			hostapd_set_oper_chwidth(conf, CHANWIDTH_80P80MHZ);
 		else
-			conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
 		break;
 	case 160:
-		conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+		hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
 		break;
 	default:
 		return -1;
@@ -3241,9 +3268,11 @@
 	conf->ieee80211n = params->ht_enabled;
 	conf->secondary_channel = params->sec_channel_offset;
 	ieee80211_freq_to_chan(params->center_freq1,
-			       &conf->vht_oper_centr_freq_seg0_idx);
+			       &seg0);
 	ieee80211_freq_to_chan(params->center_freq2,
-			       &conf->vht_oper_centr_freq_seg1_idx);
+			       &seg1);
+	hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
+	hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
 
 	/* TODO: maybe call here hostapd_config_check here? */
 
@@ -3257,7 +3286,7 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
-	u8 chan, vht_bandwidth;
+	u8 chan, bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
 	if (!iface || !iface->freq || hapd->csa_in_progress)
@@ -3266,29 +3295,30 @@
 	switch (settings->freq_params.bandwidth) {
 	case 80:
 		if (settings->freq_params.center_freq2)
-			vht_bandwidth = VHT_CHANWIDTH_80P80MHZ;
+			bandwidth = CHANWIDTH_80P80MHZ;
 		else
-			vht_bandwidth = VHT_CHANWIDTH_80MHZ;
+			bandwidth = CHANWIDTH_80MHZ;
 		break;
 	case 160:
-		vht_bandwidth = VHT_CHANWIDTH_160MHZ;
+		bandwidth = CHANWIDTH_160MHZ;
 		break;
 	default:
-		vht_bandwidth = VHT_CHANWIDTH_USE_HT;
+		bandwidth = CHANWIDTH_USE_HT;
 		break;
 	}
 
 	if (ieee80211_freq_to_channel_ext(
 		    settings->freq_params.freq,
 		    settings->freq_params.sec_channel_offset,
-		    vht_bandwidth,
+		    bandwidth,
 		    &hapd->iface->cs_oper_class,
 		    &chan) == NUM_HOSTAPD_MODES) {
 		wpa_printf(MSG_DEBUG,
-			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)",
+			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)",
 			   settings->freq_params.freq,
 			   settings->freq_params.sec_channel_offset,
-			   settings->freq_params.vht_enabled);
+			   settings->freq_params.vht_enabled,
+			   settings->freq_params.he_enabled);
 		return -1;
 	}
 
@@ -3388,29 +3418,29 @@
 hostapd_switch_channel_fallback(struct hostapd_iface *iface,
 				const struct hostapd_freq_params *freq_params)
 {
-	int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
+	int seg0_idx = 0, seg1_idx = 0, bw = CHANWIDTH_USE_HT;
 
 	wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
 
 	if (freq_params->center_freq1)
-		vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+		seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
 	if (freq_params->center_freq2)
-		vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+		seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
 
 	switch (freq_params->bandwidth) {
 	case 0:
 	case 20:
 	case 40:
-		vht_bw = VHT_CHANWIDTH_USE_HT;
+		bw = CHANWIDTH_USE_HT;
 		break;
 	case 80:
 		if (freq_params->center_freq2)
-			vht_bw = VHT_CHANWIDTH_80P80MHZ;
+			bw = CHANWIDTH_80P80MHZ;
 		else
-			vht_bw = VHT_CHANWIDTH_80MHZ;
+			bw = CHANWIDTH_80MHZ;
 		break;
 	case 160:
-		vht_bw = VHT_CHANWIDTH_160MHZ;
+		bw = CHANWIDTH_160MHZ;
 		break;
 	default:
 		wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
@@ -3421,11 +3451,12 @@
 	iface->freq = freq_params->freq;
 	iface->conf->channel = freq_params->channel;
 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
-	iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx;
-	iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx;
-	iface->conf->vht_oper_chwidth = vht_bw;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
+	hostapd_set_oper_chwidth(iface->conf, bw);
 	iface->conf->ieee80211n = freq_params->ht_enabled;
 	iface->conf->ieee80211ac = freq_params->vht_enabled;
+	iface->conf->ieee80211ax = freq_params->he_enabled;
 
 	/*
 	 * cs_params must not be cleared earlier because the freq_params
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 607bb95..44ef753 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -232,6 +232,10 @@
 	struct wps_stat wps_stats;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_MACSEC
+	struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
+
 	struct hostapd_probereq_cb *probereq_cb;
 	size_t num_probereq_cb;
 
@@ -379,6 +383,13 @@
 	unsigned int dpp_ignore_netaccesskey_mismatch:1;
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_DPP */
+
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int num_backlogged_sta;
+	unsigned int airtime_weight;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	u8 last_1x_eapol_key_replay_counter[8];
 };
 
 
@@ -542,6 +553,12 @@
 	unsigned int num_sta_seen;
 
 	u8 dfs_domain;
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_quantum;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	/* Previous WMM element information */
+	struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
 };
 
 /* hostapd.c */
@@ -608,7 +625,8 @@
 			 const u8 *bssid, const u8 *ie, size_t ie_len,
 			 int ssi_signal);
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
-			     int offset, int width, int cf1, int cf2);
+			     int offset, int width, int cf1, int cf2,
+			     int finished);
 struct survey_results;
 void hostapd_event_get_survey(struct hostapd_iface *iface,
 			      struct survey_results *survey_results);
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8ac33bb..c1f19e2 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -329,9 +329,9 @@
 	res = ieee80211n_allowed_ht40_channel_pair(iface);
 	if (!res) {
 		iface->conf->secondary_channel = 0;
-		iface->conf->vht_oper_centr_freq_seg0_idx = 0;
-		iface->conf->vht_oper_centr_freq_seg1_idx = 0;
-		iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
+		hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT);
 		res = 1;
 		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
 	}
@@ -655,6 +655,14 @@
 }
 #endif /* CONFIG_IEEE80211AC */
 
+
+#ifdef CONFIG_IEEE80211AX
+static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+{
+	return 1;
+}
+#endif /* CONFIG_IEEE80211AX */
+
 #endif /* CONFIG_IEEE80211N */
 
 
@@ -675,6 +683,11 @@
 
 	if (!ieee80211n_supported_ht_capab(iface))
 		return -1;
+#ifdef CONFIG_IEEE80211AX
+	if (iface->conf->ieee80211ax &&
+	    !ieee80211ax_supported_he_capab(iface))
+		return -1;
+#endif /* CONFIG_IEEE80211AX */
 #ifdef CONFIG_IEEE80211AC
 	if (iface->conf->ieee80211ac &&
 	    !ieee80211ac_supported_vht_capab(iface))
@@ -863,12 +876,14 @@
 		return -1;
 
 	if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
-	     iface->conf->ieee80211n || iface->conf->ieee80211ac) &&
+	     iface->conf->ieee80211n || iface->conf->ieee80211ac ||
+	     iface->conf->ieee80211ax) &&
 	    iface->conf->channel == 14) {
-		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14");
+		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14");
 		iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
 		iface->conf->ieee80211n = 0;
 		iface->conf->ieee80211ac = 0;
+		iface->conf->ieee80211ax = 0;
 	}
 
 	iface->current_mode = NULL;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 5cd2562..fff35b7 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -23,6 +23,7 @@
 #include "common/sae.h"
 #include "common/dpp.h"
 #include "common/ocv.h"
+#include "common/wpa_common.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -709,7 +710,8 @@
 	os_memset(&params, 0, sizeof(params));
 	params.status = status;
 	params.bssid = sta->addr;
-	if (status == WLAN_STATUS_SUCCESS && sta->sae)
+	if (status == WLAN_STATUS_SUCCESS && sta->sae &&
+	    !hapd->conf->disable_pmksa_caching)
 		params.pmkid = sta->sae->pmkid;
 
 	hostapd_drv_send_external_auth_status(hapd, &params);
@@ -1038,8 +1040,8 @@
 
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "start SAE authentication (RX commit, status=%u)",
-			       status_code);
+			       "start SAE authentication (RX commit, status=%u (%s))",
+			       status_code, status2str(status_code));
 
 		if ((hapd->conf->mesh & MESH_ENABLED) &&
 		    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
@@ -1182,8 +1184,8 @@
 	} else if (auth_transaction == 2) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "SAE authentication (RX confirm, status=%u)",
-			       status_code);
+			       "SAE authentication (RX confirm, status=%u (%s))",
+			       status_code, status2str(status_code));
 		if (status_code != WLAN_STATUS_SUCCESS)
 			goto remove_sta;
 		if (sta->sae->state >= SAE_CONFIRMED ||
@@ -1224,8 +1226,9 @@
 	} else {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "unexpected SAE authentication transaction %u (status=%u)",
-			       auth_transaction, status_code);
+			       "unexpected SAE authentication transaction %u (status=%u (%s))",
+			       auth_transaction, status_code,
+			       status2str(status_code));
 		if (status_code != WLAN_STATUS_SUCCESS)
 			goto remove_sta;
 		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
@@ -1821,6 +1824,8 @@
 			}
 
 			sta->fils_erp_pmkid_set = 0;
+			wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
+						    sta->fils_erp_pmkid);
 			if (!hapd->conf->disable_pmksa_caching &&
 			    wpa_auth_pmksa_add2(
 				    hapd->wpa_auth, sta->addr,
@@ -2321,8 +2326,11 @@
 		sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
 				WLAN_STA_AUTHORIZED);
 
-		if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0,
-				    NULL, NULL, sta->flags, 0, 0, 0, 0)) {
+		if (hostapd_sta_add(hapd, sta->addr, 0, 0,
+				    sta->supported_rates,
+				    sta->supported_rates_len,
+				    0, NULL, NULL, NULL, 0,
+				    sta->flags, 0, 0, 0, 0)) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_NOTICE,
@@ -2788,6 +2796,123 @@
 	return WLAN_STATUS_SUCCESS;
 }
 
+
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+			 const u8 *rsn_ie, size_t rsn_ie_len,
+			 const u8 *owe_dh, size_t owe_dh_len)
+{
+	struct wpa_ie_data data;
+	int res;
+
+	if (!rsn_ie || rsn_ie_len < 2) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
+			   MAC2STR(peer));
+		return WLAN_STATUS_INVALID_IE;
+	}
+	rsn_ie -= 2;
+	rsn_ie_len += 2;
+
+	res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR
+			   " (res=%d)", MAC2STR(peer), res);
+		wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len);
+		return wpa_res_to_status_code(res);
+	}
+	if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Unexpected key mgmt 0x%x from " MACSTR,
+			   (unsigned int) data.key_mgmt, MAC2STR(peer));
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	}
+	if (!owe_dh) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: No Diffie-Hellman Parameter element from "
+			   MACSTR, MAC2STR(peer));
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 owe_process_rsn_ie(struct hostapd_data *hapd,
+		       struct sta_info *sta,
+		       const u8 *rsn_ie, size_t rsn_ie_len,
+		       const u8 *owe_dh, size_t owe_dh_len)
+{
+	u16 status;
+	u8 *owe_buf, ie[256 * 2];
+	size_t ie_len = 0;
+	int res;
+
+	if (!rsn_ie || rsn_ie_len < 2) {
+		wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
+		status = WLAN_STATUS_INVALID_IE;
+		goto end;
+	}
+
+	if (!sta->wpa_sm)
+		sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,	sta->addr,
+						NULL);
+	if (!sta->wpa_sm) {
+		wpa_printf(MSG_WARNING,
+			   "OWE: Failed to initialize WPA state machine");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto end;
+	}
+	rsn_ie -= 2;
+	rsn_ie_len += 2;
+	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+				  hapd->iface->freq, rsn_ie, rsn_ie_len,
+				  NULL, 0, owe_dh, owe_dh_len);
+	status = wpa_res_to_status_code(res);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto end;
+	status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto end;
+	owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
+						NULL, 0);
+	if (!owe_buf) {
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto end;
+	}
+
+	if (sta->owe_ecdh) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto end;
+		}
+
+		/* OWE Diffie-Hellman Parameter element */
+		*owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+		*owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+							 */
+		WPA_PUT_LE16(owe_buf, sta->owe_group);
+		owe_buf += 2;
+		os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+		owe_buf += wpabuf_len(pub);
+		wpabuf_free(pub);
+		sta->external_dh_updated = 1;
+	}
+	ie_len = owe_buf - ie;
+
+end:
+	wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
+			      MACSTR, status, (unsigned int) ie_len,
+			      MAC2STR(sta->addr));
+	hostapd_drv_update_dh_ie(hapd, sta->addr, status,
+				 status == WLAN_STATUS_SUCCESS ? ie : NULL,
+				 ie_len);
+
+	return status;
+}
+
 #endif /* CONFIG_OWE */
 
 
@@ -2843,10 +2968,6 @@
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 
-		resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation);
-		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
-
 		resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
@@ -2867,6 +2988,15 @@
 			return resp;
 	}
 #endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
+					 elems.he_capabilities,
+					 elems.he_capabilities_len);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
+#endif /* CONFIG_IEEE80211AX */
 
 #ifdef CONFIG_P2P
 	if (elems.p2p) {
@@ -3229,6 +3359,7 @@
 {
 	struct ieee80211_ht_capabilities ht_cap;
 	struct ieee80211_vht_capabilities vht_cap;
+	struct ieee80211_he_capabilities he_cap;
 	int set = 1;
 
 	/*
@@ -3281,6 +3412,12 @@
 	if (sta->flags & WLAN_STA_VHT)
 		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
 #endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	if (sta->flags & WLAN_STA_HE) {
+		hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
+				     sta->he_capab_len);
+	}
+#endif /* CONFIG_IEEE80211AX */
 
 	/*
 	 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
@@ -3292,6 +3429,8 @@
 			    sta->listen_interval,
 			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
 			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+			    sta->flags & WLAN_STA_HE ? &he_cap : NULL,
+			    sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
 			    set)) {
@@ -3329,6 +3468,8 @@
 #ifdef CONFIG_FILS
 	if (sta && sta->fils_hlp_resp)
 		buflen += wpabuf_len(sta->fils_hlp_resp);
+	if (sta)
+		buflen += 150;
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_OWE
 	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
@@ -3390,6 +3531,15 @@
 		}
 	}
 #endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_FILS
+	if (sta && status_code == WLAN_STATUS_SUCCESS &&
+	    (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK))
+		p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
+						   buf + buflen - p,
+						   ies, ies_len);
+#endif /* CONFIG_FILS */
 
 #ifdef CONFIG_OWE
 	if (sta && status_code == WLAN_STATUS_SUCCESS &&
@@ -3432,6 +3582,15 @@
 	}
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+		p = hostapd_eid_he_operation(hapd, p);
+		p = hostapd_eid_spatial_reuse(hapd, p);
+		p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	p = hostapd_eid_ext_capab(hapd, p);
 	p = hostapd_eid_bss_max_idle_period(hapd, p);
 	if (sta && sta->qos_map_enabled)
@@ -3608,6 +3767,12 @@
 		return owe_buf;
 	}
 
+	if (sta->owe_pmk && sta->external_dh_updated) {
+		wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK");
+		*reason = WLAN_STATUS_SUCCESS;
+		return owe_buf;
+	}
+
 	*reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
 	if (*reason != WLAN_STATUS_SUCCESS)
 		return NULL;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index db7badc..b8453c9 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -18,6 +18,7 @@
 struct ieee80211_mgmt;
 struct vlan_description;
 struct hostapd_sta_wpa_psk_short;
+enum ieee80211_op_mode;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 		    struct hostapd_frame_info *fi);
@@ -57,9 +58,11 @@
 u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+			  enum ieee80211_op_mode opmode);
 u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
 
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -70,6 +73,10 @@
 void hostapd_get_vht_capab(struct hostapd_data *hapd,
 			   struct ieee80211_vht_capabilities *vht_cap,
 			   struct ieee80211_vht_capabilities *neg_vht_cap);
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+			  const struct ieee80211_he_capabilities *he_cap,
+			  struct ieee80211_he_capabilities *neg_he_cap,
+			  size_t he_capab_len);
 int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab);
@@ -85,6 +92,9 @@
 		      const u8 *vht_oper);
 u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_opmode);
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      enum ieee80211_op_mode opmode, const u8 *he_capab,
+		      size_t he_capab_len);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 		       const u8 *buf, size_t len, int ack);
 void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -153,6 +163,12 @@
 u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
 			   const u8 *owe_dh, u8 owe_dh_len,
 			   u8 *owe_buf, size_t owe_buf_len, u16 *reason);
+u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *rsn_ie, size_t rsn_ie_len,
+		       const u8 *owe_dh, size_t owe_dh_len);
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+			 const u8 *rsn_ie, size_t rsn_ie_len,
+			 const u8 *owe_dh, size_t owe_dh_len);
 void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
 void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
 void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 0721358..a51f3fc 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -1,6 +1,7 @@
 /*
  * hostapd / IEEE 802.11ax HE
  * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019 John Crispin <john@phrozen.org>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,37 +14,113 @@
 #include "hostapd.h"
 #include "ap_config.h"
 #include "beacon.h"
+#include "sta_info.h"
 #include "ieee802_11.h"
 #include "dfs.h"
 
-u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
+static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+	u8 sz = 0, ru;
+
+	if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+	     HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+		return 0;
+
+	ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
+		HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
+	while (ru) {
+		if (ru & 0x1)
+			sz++;
+		ru >>= 1;
+	}
+
+	sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
+	sz = (sz * 6) + 7;
+	if (sz % 8)
+		sz += 8;
+	sz /= 8;
+
+	return sz;
+}
+
+
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+			  enum ieee80211_op_mode opmode)
 {
 	struct ieee80211_he_capabilities *cap;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
 	u8 *pos = eid;
+	u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
 
-	if (!hapd->iface->current_mode)
+	if (!mode)
 		return eid;
 
+	ie_size = sizeof(struct ieee80211_he_capabilities);
+	ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
+					   mode->he_capab[opmode].phy_cap);
+
+	switch (hapd->iface->conf->he_oper_chwidth) {
+	case CHANWIDTH_80P80MHZ:
+		he_oper_chwidth |=
+			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+		mcs_nss_size += 4;
+		/* fall through */
+	case CHANWIDTH_160MHZ:
+		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+		mcs_nss_size += 4;
+		/* fall through */
+	case CHANWIDTH_80MHZ:
+	case CHANWIDTH_USE_HT:
+		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+		mcs_nss_size += 4;
+		break;
+	}
+
+	ie_size += mcs_nss_size + ppet_size;
+
 	*pos++ = WLAN_EID_EXTENSION;
-	*pos++ = 1 + sizeof(struct ieee80211_he_capabilities);
+	*pos++ = 1 + ie_size;
 	*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
 
 	cap = (struct ieee80211_he_capabilities *) pos;
 	os_memset(cap, 0, sizeof(*cap));
 
+	os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
+		  HE_MAX_MAC_CAPAB_SIZE);
+	os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
+		  HE_MAX_PHY_CAPAB_SIZE);
+	os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
+	if (ppet_size)
+		os_memcpy(&cap->optional[mcs_nss_size],
+			  mode->he_capab[opmode].ppet,  ppet_size);
+
 	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
 			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
+			~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
 
 	if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
 			HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
+			~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
 
 	if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
 		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
 			HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
+			~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
 
-	pos += sizeof(*cap);
+	cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
+		he_oper_chwidth;
+
+	pos += ie_size;
 
 	return pos;
 }
@@ -53,36 +130,43 @@
 {
 	struct ieee80211_he_operation *oper;
 	u8 *pos = eid;
+	int oper_size = 6;
+	u32 params = 0;
 
 	if (!hapd->iface->current_mode)
 		return eid;
 
 	*pos++ = WLAN_EID_EXTENSION;
-	*pos++ = 1 + sizeof(struct ieee80211_he_operation);
+	*pos++ = 1 + oper_size;
 	*pos++ = WLAN_EID_EXT_HE_OPERATION;
 
 	oper = (struct ieee80211_he_operation *) pos;
 	os_memset(oper, 0, sizeof(*oper));
 
-	if (hapd->iface->conf->he_op.he_bss_color)
-		oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color;
-
 	if (hapd->iface->conf->he_op.he_default_pe_duration)
-		oper->he_oper_params |=
-			(hapd->iface->conf->he_op.he_default_pe_duration <<
-			 HE_OPERATION_DFLT_PE_DURATION_OFFSET);
+		params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
+			   HE_OPERATION_DFLT_PE_DURATION_OFFSET);
 
 	if (hapd->iface->conf->he_op.he_twt_required)
-		oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED;
+		params |= HE_OPERATION_TWT_REQUIRED;
 
 	if (hapd->iface->conf->he_op.he_rts_threshold)
-		oper->he_oper_params |=
-			(hapd->iface->conf->he_op.he_rts_threshold <<
-			 HE_OPERATION_RTS_THRESHOLD_OFFSET);
+		params |= (hapd->iface->conf->he_op.he_rts_threshold <<
+			   HE_OPERATION_RTS_THRESHOLD_OFFSET);
+
+	if (hapd->iface->conf->he_op.he_bss_color)
+		params |= (hapd->iface->conf->he_op.he_bss_color <<
+			   HE_OPERATION_BSS_COLOR_OFFSET);
+
+	/* HE minimum required basic MCS and NSS for STAs */
+	oper->he_mcs_nss_set =
+		host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
 
 	/* TODO: conditional MaxBSSID Indicator subfield */
 
-	pos += sizeof(*oper);
+	oper->he_oper_params = host_to_le32(params);
+
+	pos += oper_size;
 
 	return pos;
 }
@@ -117,3 +201,148 @@
 
 	return pos;
 }
+
+
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_spatial_reuse *spr;
+	u8 *pos = eid, *spr_param;
+	u8 sz = 1;
+
+	if (!hapd->iface->conf->spr.sr_control)
+		return eid;
+
+	if (hapd->iface->conf->spr.sr_control &
+	    SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
+		sz++;
+
+	if (hapd->iface->conf->spr.sr_control &
+	    SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
+		sz += 18;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sz;
+	*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
+
+	spr = (struct ieee80211_spatial_reuse *) pos;
+	os_memset(spr, 0, sizeof(*spr));
+
+	spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
+	pos++;
+	spr_param = spr->params;
+	if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
+		*spr_param++ =
+			hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
+		pos++;
+	}
+	if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
+		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
+		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
+		pos += 18;
+	}
+
+	return pos;
+}
+
+
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+			  const struct ieee80211_he_capabilities *he_cap,
+			  struct ieee80211_he_capabilities *neg_he_cap,
+			  size_t he_capab_len)
+{
+	if (!he_cap)
+		return;
+
+	if (he_capab_len > sizeof(*neg_he_cap))
+		he_capab_len = sizeof(*neg_he_cap);
+	/* TODO: mask out unsupported features */
+
+	os_memcpy(neg_he_cap, he_cap, he_capab_len);
+}
+
+
+static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
+			      enum ieee80211_op_mode opmode)
+{
+	u16 sta_rx_mcs_set, ap_tx_mcs_set;
+	u8 mcs_count = 0;
+	const u16 *ap_mcs_set, *sta_mcs_set;
+	int i;
+
+	if (!hapd->iface->current_mode)
+		return 1;
+	ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
+	sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
+			       sta_he_capab)->optional;
+
+	/*
+	 * Disable HE capabilities for STAs for which there is not even a single
+	 * allowed MCS in any supported number of streams, i.e., STA is
+	 * advertising 3 (not supported) as HE MCS rates for all supported
+	 * band/stream cases.
+	 */
+	switch (hapd->iface->conf->he_oper_chwidth) {
+	case CHANWIDTH_80P80MHZ:
+		mcs_count = 3;
+		break;
+	case CHANWIDTH_160MHZ:
+		mcs_count = 2;
+		break;
+	default:
+		mcs_count = 1;
+		break;
+	}
+
+	for (i = 0; i < mcs_count; i++) {
+		int j;
+
+		/* AP Tx MCS map vs. STA Rx MCS map */
+		sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
+		ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
+					     &ap_mcs_set[(i * 2) + 1]);
+
+		for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
+			if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
+				continue;
+
+			if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
+				continue;
+
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching HE MCS found between AP TX and STA RX");
+
+	return 0;
+}
+
+
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      enum ieee80211_op_mode opmode, const u8 *he_capab,
+		      size_t he_capab_len)
+{
+	if (!he_capab || !hapd->iconf->ieee80211ax ||
+	    !check_valid_he_mcs(hapd, he_capab, opmode) ||
+	    he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
+		sta->flags &= ~WLAN_STA_HE;
+		os_free(sta->he_capab);
+		sta->he_capab = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (!sta->he_capab) {
+		sta->he_capab =
+			os_zalloc(sizeof(struct ieee80211_he_capabilities));
+		if (!sta->he_capab)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_HE;
+	os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
+	os_memcpy(sta->he_capab, he_capab, he_capab_len);
+	sta->he_capab_len = he_capab_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 54ee080..269345f 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -242,7 +242,7 @@
 		return eid;
 
 	switch (iface->conf->vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
+	case CHANWIDTH_USE_HT:
 		if (iconf->secondary_channel == 0) {
 			/* Max Transmit Power count = 0 (20 MHz) */
 			tx_pwr_count = 0;
@@ -251,12 +251,12 @@
 			tx_pwr_count = 1;
 		}
 		break;
-	case VHT_CHANWIDTH_80MHZ:
+	case CHANWIDTH_80MHZ:
 		/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
 		tx_pwr_count = 2;
 		break;
-	case VHT_CHANWIDTH_80P80MHZ:
-	case VHT_CHANWIDTH_160MHZ:
+	case CHANWIDTH_80P80MHZ:
+	case CHANWIDTH_160MHZ:
 		/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
 		tx_pwr_count = 3;
 		break;
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 870329a..d628641 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -34,6 +34,7 @@
 /* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
 #include "ieee802_11.h"
 #include "ieee802_1x.h"
+#include "wpa_auth_kay.h"
 
 
 #ifdef CONFIG_HS20
@@ -63,6 +64,10 @@
 
 	xhdr = (struct ieee802_1x_hdr *) buf;
 	xhdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (xhdr->version > 2 && hapd->conf->macsec_policy == 0)
+		xhdr->version = 2;
+#endif /* CONFIG_MACSEC */
 	xhdr->type = type;
 	xhdr->length = host_to_be16(datalen);
 
@@ -157,6 +162,21 @@
 	key->type = EAPOL_KEY_TYPE_RC4;
 	WPA_PUT_BE16(key->key_length, key_len);
 	wpa_get_ntp_timestamp(key->replay_counter);
+	if (os_memcmp(key->replay_counter,
+		      hapd->last_1x_eapol_key_replay_counter,
+		      IEEE8021X_REPLAY_COUNTER_LEN) <= 0) {
+		/* NTP timestamp did not increment from last EAPOL-Key frame;
+		 * use previously used value + 1 instead. */
+		inc_byte_array(hapd->last_1x_eapol_key_replay_counter,
+			       IEEE8021X_REPLAY_COUNTER_LEN);
+		os_memcpy(key->replay_counter,
+			  hapd->last_1x_eapol_key_replay_counter,
+			  IEEE8021X_REPLAY_COUNTER_LEN);
+	} else {
+		os_memcpy(hapd->last_1x_eapol_key_replay_counter,
+			  key->replay_counter,
+			  IEEE8021X_REPLAY_COUNTER_LEN);
+	}
 
 	if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
 		wpa_printf(MSG_ERROR, "Could not get random numbers");
@@ -197,6 +217,10 @@
 	/* This header is needed here for HMAC-MD5, but it will be regenerated
 	 * in ieee802_1x_send() */
 	hdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (hdr->version > 2)
+		hdr->version = 2;
+#endif /* CONFIG_MACSEC */
 	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
 	hdr->length = host_to_be16(len);
 	hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
@@ -1104,6 +1128,13 @@
 		/* TODO: implement support for this; show data */
 		break;
 
+#ifdef CONFIG_MACSEC
+	case IEEE802_1X_TYPE_EAPOL_MKA:
+		wpa_printf(MSG_EXCESSIVE,
+			   "EAPOL type %d will be handled by MKA", hdr->type);
+		break;
+#endif /* CONFIG_MACSEC */
+
 	default:
 		wpa_printf(MSG_DEBUG, "   unknown IEEE 802.1X packet type");
 		sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
@@ -1236,6 +1267,7 @@
 		sta->eapol_sm->portValid = TRUE;
 		if (sta->eapol_sm->eap)
 			eap_sm_notify_cached(sta->eapol_sm->eap);
+		wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
 		return;
 	}
 #endif /* CONFIG_FILS */
@@ -1384,6 +1416,8 @@
 				size_t shared_secret_len)
 {
 	struct radius_ms_mppe_keys *keys;
+	u8 *buf;
+	size_t len;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 	if (sm == NULL)
 		return;
@@ -1392,7 +1426,7 @@
 				      shared_secret_len);
 
 	if (keys && keys->send && keys->recv) {
-		size_t len = keys->send_len + keys->recv_len;
+		len = keys->send_len + keys->recv_len;
 		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
 				keys->send, keys->send_len);
 		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
@@ -1420,6 +1454,20 @@
 		os_free(keys->recv);
 		os_free(keys);
 	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
+				    NULL) == 0) {
+		os_free(sm->eap_if->eapSessionId);
+		sm->eap_if->eapSessionId = os_memdup(buf, len);
+		if (sm->eap_if->eapSessionId) {
+			sm->eap_if->eapSessionIdLen = len;
+			wpa_hexdump(MSG_DEBUG, "EAP-Key Name",
+				    sm->eap_if->eapSessionId,
+				    sm->eap_if->eapSessionIdLen);
+		}
+	} else {
+		sm->eap_if->eapSessionIdLen = 0;
+	}
 }
 
 
@@ -2323,6 +2371,8 @@
 	conf.eap_fast_prov = hapd->conf->eap_fast_prov;
 	conf.pac_key_lifetime = hapd->conf->pac_key_lifetime;
 	conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
+	conf.eap_teap_auth = hapd->conf->eap_teap_auth;
+	conf.eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
 	conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
 	conf.tnc = hapd->conf->tnc;
 	conf.wps = hapd->wps;
@@ -2542,6 +2592,20 @@
 }
 
 
+#ifdef CONFIG_MACSEC
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+				     size_t *len)
+{
+	*len = 0;
+	if (!sm || !sm->eap_if)
+		return NULL;
+
+	*len = sm->eap_if->eapSessionIdLen;
+	return sm->eap_if->eapSessionId;
+}
+#endif /* CONFIG_MACSEC */
+
+
 void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 				    int enabled)
 {
@@ -2832,6 +2896,10 @@
 	}
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MACSEC
+	ieee802_1x_notify_create_actor_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
+
 	key = ieee802_1x_get_key(sta->eapol_sm, &len);
 	if (sta->session_timeout_set) {
 		os_get_reltime(&now);
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index 9594661..d771ba5 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -39,6 +39,8 @@
 				 int idx);
 struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+				     size_t *len);
 void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 				    int enabled);
 void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 2b6f727..5415443 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -139,19 +139,21 @@
 
 #ifdef NEED_AP_MLME
 static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
-						    int ht, int vht)
+						    int ht, int vht, int he)
 {
-	if (!ht && !vht)
+	u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+
+	if (!ht && !vht && !he)
 		return NR_CHAN_WIDTH_20;
 	if (!hapd->iconf->secondary_channel)
 		return NR_CHAN_WIDTH_20;
-	if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+	if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
 		return NR_CHAN_WIDTH_40;
-	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+	if (oper_chwidth == CHANWIDTH_80MHZ)
 		return NR_CHAN_WIDTH_80;
-	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
+	if (oper_chwidth == CHANWIDTH_160MHZ)
 		return NR_CHAN_WIDTH_160;
-	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
+	if (oper_chwidth == CHANWIDTH_80P80MHZ)
 		return NR_CHAN_WIDTH_80P80;
 	return NR_CHAN_WIDTH_20;
 }
@@ -164,6 +166,7 @@
 	u16 capab = hostapd_own_capab_info(hapd);
 	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
 	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+	int he = hapd->iconf->ieee80211ax;
 	struct wpa_ssid_value ssid;
 	u8 channel, op_class;
 	u8 center_freq1_idx = 0, center_freq2_idx = 0;
@@ -205,16 +208,18 @@
 
 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
 					  hapd->iconf->secondary_channel,
-					  hapd->iconf->vht_oper_chwidth,
+					  hostapd_get_oper_chwidth(hapd->iconf),
 					  &op_class, &channel) ==
 	    NUM_HOSTAPD_MODES)
 		return;
-	width = hostapd_get_nr_chan_width(hapd, ht, vht);
+	width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
 	if (vht) {
-		center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+		center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
+			hapd->iconf);
 		if (width == NR_CHAN_WIDTH_80P80)
 			center_freq2_idx =
-				hapd->iconf->vht_oper_centr_freq_seg1_idx;
+				hostapd_get_oper_centr_freq_seg1_idx(
+					hapd->iconf);
 	} else if (ht) {
 		ieee80211_freq_to_chan(hapd->iface->freq +
 				       10 * hapd->iconf->secondary_channel,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 71f9f21..51d7884 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -330,6 +330,7 @@
 	os_free(sta->ht_capabilities);
 	os_free(sta->vht_capabilities);
 	os_free(sta->vht_operation);
+	os_free(sta->he_capab);
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
@@ -670,6 +671,7 @@
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
 {
 	struct sta_info *sta;
+	int i;
 
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
@@ -694,6 +696,15 @@
 		return NULL;
 	}
 
+	for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+		if (!hapd->iface->basic_rates)
+			break;
+		if (hapd->iface->basic_rates[i] < 0)
+			break;
+		sta->supported_rates[i] = hapd->iface->basic_rates[i] / 5;
+	}
+	sta->supported_rates_len = i;
+
 	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
 		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
 			   "for " MACSTR " (%d seconds - ap_max_inactivity)",
@@ -812,6 +823,8 @@
 			       ap_handle_timer, hapd, sta);
 	accounting_sta_stop(hapd, sta);
 	ieee802_1x_free_station(hapd, sta);
+	wpa_auth_sta_deinit(sta->wpa_sm);
+	sta->wpa_sm = NULL;
 
 	sta->disassoc_reason = reason;
 	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index ece0c60..5456a63 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -37,6 +37,7 @@
 #define WLAN_STA_VENDOR_VHT BIT(21)
 #define WLAN_STA_PENDING_FILS_ERP BIT(22)
 #define WLAN_STA_MULTI_AP BIT(23)
+#define WLAN_STA_HE BIT(24)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -119,6 +120,7 @@
 	unsigned int agreed_to_steer:1;
 	unsigned int hs20_t_c_filtering:1;
 	unsigned int ft_over_ds:1;
+	unsigned int external_dh_updated:1;
 
 	u16 auth_alg;
 
@@ -166,6 +168,8 @@
 	struct ieee80211_vht_capabilities *vht_capabilities;
 	struct ieee80211_vht_operation *vht_operation;
 	u8 vht_opmode;
+	struct ieee80211_he_capabilities *he_capab;
+	size_t he_capab_len;
 
 #ifdef CONFIG_IEEE80211W
 	int sa_query_count; /* number of pending SA Query requests;
@@ -275,6 +279,10 @@
 	u8 last_tk[WPA_TK_MAX_LEN];
 	size_t last_tk_len;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_weight;
+	struct os_reltime backlogged_until;
+#endif /* CONFIG_AIRTIME_POLICY */
 };
 
 
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 8054c5d..dc73493 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -20,6 +20,13 @@
 #include "ap_drv_ops.h"
 #include "wmm.h"
 
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
 
 static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
 {
@@ -39,6 +46,62 @@
 }
 
 
+static void
+wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf,
+			 struct hostapd_wmm_ac_params *wmm,
+			 const struct hostapd_wmm_rule *wmm_reg)
+{
+	int ac;
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin);
+		wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax);
+		wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs);
+		wmm[ac].txop_limit =
+			MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop);
+		wmm[ac].admission_control_mandatory =
+			wmm_conf[ac].admission_control_mandatory;
+	}
+}
+
+
+/*
+ * Calculate WMM regulatory limit if any.
+ */
+static void wmm_calc_regulatory_limit(struct hostapd_data *hapd,
+				      struct hostapd_wmm_ac_params *acp)
+{
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	int c;
+
+	os_memcpy(acp, hapd->iconf->wmm_ac_params,
+		  sizeof(hapd->iconf->wmm_ac_params));
+
+	for (c = 0; mode && c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+
+		if (chan->freq != hapd->iface->freq)
+			continue;
+
+		if (chan->wmm_rules_valid)
+			wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params,
+						 acp, chan->wmm_rules);
+		break;
+	}
+
+	/*
+	 * Check if we need to update set count. Since both were initialized to
+	 * zero we can compare the whole array in one shot.
+	 */
+	if (os_memcmp(acp, hapd->iface->prev_wmm,
+		      sizeof(hapd->iconf->wmm_ac_params)) != 0) {
+		os_memcpy(hapd->iface->prev_wmm, acp,
+			  sizeof(hapd->iconf->wmm_ac_params));
+		hapd->parameter_set_count++;
+	}
+}
+
+
 /*
  * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
  * Response frames.
@@ -48,10 +111,12 @@
 	u8 *pos = eid;
 	struct wmm_parameter_element *wmm =
 		(struct wmm_parameter_element *) (pos + 2);
+	struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM] = { 0 };
 	int e;
 
 	if (!hapd->conf->wmm_enabled)
 		return eid;
+	wmm_calc_regulatory_limit(hapd, wmmp);
 	eid[0] = WLAN_EID_VENDOR_SPECIFIC;
 	wmm->oui[0] = 0x00;
 	wmm->oui[1] = 0x50;
@@ -70,8 +135,7 @@
 	/* fill in a parameter set record for each AC */
 	for (e = 0; e < 4; e++) {
 		struct wmm_ac_parameter *ac = &wmm->ac[e];
-		struct hostapd_wmm_ac_params *acp =
-			&hapd->iconf->wmm_ac_params[e];
+		struct hostapd_wmm_ac_params *acp = &wmmp[e];
 
 		ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
 					      acp->admission_control_mandatory,
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index e89a716..e1c0c2c 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -320,6 +320,19 @@
 }
 
 
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
+{
+	if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
+		wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
+			   MACSTR " (%d seconds)", MAC2STR(sm->addr),
+			   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);
+	}
+}
+
+
 static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
 {
 	if (sm->pmksa == ctx)
@@ -921,6 +934,7 @@
 
 	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = TRUE;
 
 	return 0;
@@ -1394,6 +1408,8 @@
 #endif /* CONFIG_SHA256 */
 #endif /* CONFIG_SHA384 */
 
+	forced_memzero(data, sizeof(data));
+
 	return ret;
 }
 
@@ -1735,6 +1751,8 @@
 		sm->pmk_len = 0;
 		os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
 		sm->xxkey_len = 0;
+		os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+		sm->pmk_r1_len = 0;
 #endif /* CONFIG_IEEE80211R_AP */
 		break;
 	case WPA_REAUTH:
@@ -1776,6 +1794,7 @@
 
 		/* Using FT protocol, not WPA auth state machine */
 		sm->ft_completed = 1;
+		wpa_auth_set_ptk_rekey_timer(sm);
 		return 0;
 #else /* CONFIG_IEEE80211R_AP */
 		break;
@@ -2030,7 +2049,7 @@
 		sm->Disconnect = TRUE;
 		return;
 	}
-	os_memset(msk, 0, sizeof(msk));
+	forced_memzero(msk, sizeof(msk));
 
 	sm->req_replay_counter_used = 0;
 	/* IEEE 802.11i does not set keyRun to FALSE, but not doing this
@@ -2130,6 +2149,29 @@
 			wpa_printf(MSG_DEBUG,
 				   "RSN: No KCK available to derive PMKID for message 1/4");
 			pmkid = NULL;
+#ifdef CONFIG_FILS
+		} else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+			if (sm->pmkid_set) {
+				wpa_hexdump(MSG_DEBUG,
+					    "RSN: Message 1/4 PMKID from FILS/ERP",
+					    sm->pmkid, PMKID_LEN);
+				os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+					  sm->pmkid, PMKID_LEN);
+			} else {
+				/* No PMKID available */
+				wpa_printf(MSG_DEBUG,
+					   "RSN: No FILS/ERP PMKID available for message 1/4");
+				pmkid = NULL;
+			}
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+		} else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+			   sm->ft_completed) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: No PMKID in message 1/4 when using FT protocol");
+			pmkid = NULL;
+			pmkid_len = 0;
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
 		} else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
 			if (sm->pmkid_set) {
@@ -2172,8 +2214,20 @@
 	size_t z_len = 0;
 
 #ifdef CONFIG_IEEE80211R_AP
-	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
-		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		if (sm->ft_completed) {
+			u8 ptk_name[WPA_PMK_NAME_LEN];
+
+			return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+						 sm->SNonce, sm->ANonce,
+						 sm->addr, sm->wpa_auth->addr,
+						 sm->pmk_r1_name,
+						 ptk, ptk_name,
+						 sm->wpa_key_mgmt,
+						 sm->pairwise);
+		}
+		return wpa_auth_derive_ptk_ft(sm, ptk);
+	}
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_DPP2
@@ -2234,12 +2288,12 @@
 		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
 			    pmk_r0_name, WPA_PMK_NAME_LEN);
 		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
-		os_memset(fils_ft, 0, sizeof(fils_ft));
+		forced_memzero(fils_ft, sizeof(fils_ft));
 
 		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
 					     sm->addr, sm->pmk_r1_name,
 					     use_sha384);
-		os_memset(pmk_r0, 0, PMK_LEN_MAX);
+		forced_memzero(pmk_r0, PMK_LEN_MAX);
 		if (res < 0)
 			return -1;
 		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
@@ -2257,7 +2311,7 @@
 			       sm->wpa_key_mgmt, sm->fils_key_auth_sta,
 			       sm->fils_key_auth_ap,
 			       &sm->fils_key_auth_len);
-	os_memset(ick, 0, sizeof(ick));
+	forced_memzero(ick, sizeof(ick));
 
 	/* Store nonces for (Re)Association Request/Response frame processing */
 	os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
@@ -2559,7 +2613,7 @@
 	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
 		wpa_printf(MSG_DEBUG,
 			   "FILS: Not enough room for FILS elements");
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		return -1;
 	}
 
@@ -2569,7 +2623,7 @@
 	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
 			    wpabuf_head(plain), wpabuf_len(plain),
 			    5, aad, aad_len, pos) < 0) {
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		return -1;
 	}
 
@@ -2577,7 +2631,7 @@
 		    "FILS: Encrypted Association Response elements",
 		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
 	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	sm->fils_completed = 1;
 
@@ -2631,7 +2685,7 @@
 		 * of GTK in the BSS.
 		 */
 		if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
-			wpabuf_free(plain);
+			wpabuf_clear_free(plain);
 			return NULL;
 		}
 		gtk = dummy_gtk;
@@ -2658,13 +2712,13 @@
 		if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
 			wpa_printf(MSG_WARNING,
 				   "FILS: Failed to get channel info for OCI element");
-			wpabuf_free(plain);
+			wpabuf_clear_free(plain);
 			return NULL;
 		}
 
 		pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
 		if (ocv_insert_extended_oci(&ci, pos) < 0) {
-			wpabuf_free(plain);
+			wpabuf_clear_free(plain);
 			return NULL;
 		}
 	}
@@ -2727,7 +2781,7 @@
 
 	wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
 		   (unsigned int) wpabuf_len(plain));
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 	sm->fils_completed = 1;
 	return pos;
 }
@@ -2796,6 +2850,12 @@
 			pmk_len = sm->pmk_len;
 		}
 
+		if ((!pmk || !pmk_len) && sm->pmksa) {
+			wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
+			pmk = sm->pmksa->pmk;
+			pmk_len = sm->pmksa->pmk_len;
+		}
+
 		if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
 			break;
 
@@ -2973,6 +3033,7 @@
 	sm->MICVerified = TRUE;
 
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = TRUE;
 }
 
@@ -3293,12 +3354,7 @@
 		/* 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);
-		}
+		wpa_auth_set_ptk_rekey_timer(sm);
 
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
 		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
@@ -4307,6 +4363,15 @@
 	    sm->wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
+#ifdef CONFIG_IEEE80211R_AP
+	if (pmk_len >= 2 * PMK_LEN && wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+	    !wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+		/* Cache MPMK/XXKey instead of initial part from MSK */
+		pmk = pmk + PMK_LEN;
+		pmk_len = PMK_LEN;
+	} else
+#endif /* CONFIG_IEEE80211R_AP */
 	if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
 		if (pmk_len > PMK_LEN_SUITE_B_192)
 			pmk_len = PMK_LEN_SUITE_B_192;
@@ -4314,6 +4379,7 @@
 		pmk_len = PMK_LEN;
 	}
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, pmk_len);
 	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
 				 sm->PTK.kck, sm->PTK.kck_len,
 				 sm->wpa_auth->addr, sm->addr, session_timeout,
@@ -4332,6 +4398,7 @@
 	if (wpa_auth == NULL)
 		return -1;
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from preauth", pmk, len);
 	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
 				 NULL, 0,
 				 wpa_auth->addr,
@@ -4349,6 +4416,7 @@
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
 	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
 				 NULL, 0,
 				 wpa_auth->addr, addr, 0, NULL,
@@ -4373,6 +4441,7 @@
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
 	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
 				 NULL, 0, wpa_auth->addr, addr, session_timeout,
 				 NULL, akmp))
@@ -4846,6 +4915,16 @@
 	*fils_kek_len = sm->PTK.kek_len;
 }
 
+
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+				 size_t pmk_len, const u8 *pmkid)
+{
+	os_memcpy(sm->PMK, pmk, pmk_len);
+	sm->pmk_len = pmk_len;
+	os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+	sm->pmkid_set = 1;
+}
+
 #endif /* CONFIG_FILS */
 
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 484e1e5..a348bc2 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -470,9 +470,14 @@
 void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
 				   u8 *fils_anonce, u8 *fils_snonce,
 				   u8 *fils_kek, size_t *fils_kek_len);
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+				 size_t pmk_len, const u8 *pmkid);
 u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
 				   u8 *pos, size_t max_len,
 				   const u8 *req_ies, size_t req_ies_len);
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+				    u8 *pos, size_t max_len,
+				    const u8 *req_ies, size_t req_ies_len);
 void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
 
@@ -486,5 +491,6 @@
 			     void (*cb)(void *ctx1, void *ctx2),
 			     void *ctx1, void *ctx2);
 int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
 
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index fdb7eba..696f8d5 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -25,6 +25,7 @@
 #include "wmm.h"
 #include "wpa_auth.h"
 #include "wpa_auth_i.h"
+#include "pmksa_cache_auth.h"
 
 
 #ifdef CONFIG_IEEE80211R_AP
@@ -2074,8 +2075,7 @@
 }
 
 
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
-			   struct wpa_ptk *ptk)
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
 {
 	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
 	size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
@@ -2095,8 +2095,16 @@
 	const u8 *identity, *radius_cui;
 	size_t identity_len, radius_cui_len;
 	int session_timeout;
+	const u8 *mpmk;
+	size_t mpmk_len;
 
-	if (sm->xxkey_len == 0) {
+	if (sm->xxkey_len > 0) {
+		mpmk = sm->xxkey;
+		mpmk_len = sm->xxkey_len;
+	} else if (sm->pmksa) {
+		mpmk = sm->pmksa->pmk;
+		mpmk_len = sm->pmksa->pmk_len;
+	} else {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
 			   "derivation");
 		return -1;
@@ -2113,7 +2121,7 @@
 					       &radius_cui);
 	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
 
-	if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+	if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
 			      r0kh, r0kh_len, sm->addr,
 			      pmk_r0, pmk_r0_name,
 			      wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
@@ -2218,6 +2226,7 @@
 		return NULL;
 	}
 
+	forced_memzero(keybuf, sizeof(keybuf));
 	*len = subelem_len;
 	return subelem;
 }
@@ -2984,6 +2993,8 @@
 	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
 	sm->pmk_r1_name_valid = 1;
 	os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+	os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
+	sm->pmk_r1_len = pmk_r1_len;
 
 	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
@@ -3089,8 +3100,9 @@
 	status = res;
 
 	wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
-		   " auth_transaction=%d status=%d",
-		   MAC2STR(sm->addr), auth_transaction + 1, status);
+		   " auth_transaction=%d status=%u (%s)",
+		   MAC2STR(sm->addr), auth_transaction + 1, status,
+		   status2str(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);
@@ -3448,8 +3460,9 @@
 	u8 *pos;
 
 	wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
-		   " CurrentAP=" MACSTR " status=%d",
-		   MAC2STR(sm->addr), MAC2STR(current_ap), status);
+		   " CurrentAP=" MACSTR " status=%u (%s)",
+		   MAC2STR(sm->addr), MAC2STR(current_ap), status,
+		   status2str(status));
 	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
 
 	/* RRB - Forward action frame response to the Current AP */
@@ -3555,7 +3568,7 @@
 			       pmk_r0->vlan, src_addr, type,
 			       packet, packet_len);
 
-	os_memset(pmk_r1, 0, sizeof(pmk_r1));
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
 
 	return ret;
 }
@@ -3881,10 +3894,7 @@
 
 	ret = 0;
 out:
-	if (plain) {
-		os_memset(plain, 0, plain_len);
-		os_free(plain);
-	}
+	bin_clear_free(plain, plain_len);
 
 	return ret;
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 45172c6..0800a87 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -53,6 +53,10 @@
 	wconf->rsn_pairwise = conf->rsn_pairwise;
 	wconf->rsn_preauth = conf->rsn_preauth;
 	wconf->eapol_version = conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (wconf->eapol_version > 2)
+		wconf->eapol_version = 2;
+#endif /* CONFIG_MACSEC */
 	wconf->wmm_enabled = conf->wmm_enabled;
 	wconf->wmm_uapsd = conf->wmm_uapsd;
 	wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 3dcf77a..4babd0c 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -119,6 +119,8 @@
 	u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
 				* first 384 bits of MSK */
 	size_t xxkey_len;
+	u8 pmk_r1[PMK_LEN_MAX];
+	unsigned int pmk_r1_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 */
@@ -290,8 +292,7 @@
 		   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);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, 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);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 8580a5a..2e5c916 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1176,3 +1176,23 @@
 	return pos + res;
 }
 #endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+				    u8 *pos, size_t max_len,
+				    const u8 *req_ies, size_t req_ies_len)
+{
+	int res;
+
+	if (!sm ||
+	    sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+				WPA_KEY_MGMT_FT_FILS_SHA384))
+		return pos;
+
+	res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL);
+	if (res < 0)
+		return pos;
+	return pos + res;
+}
+#endif /* CONFIG_FILS */
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
new file mode 100644
index 0000000..b6e4797
--- /dev/null
+++ b/src/ap/wpa_auth_kay.c
@@ -0,0 +1,523 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * 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 "pae/ieee802_1x_key.h"
+#include "pae/ieee802_1x_kay.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "wpa_auth_kay.h"
+#include "ieee802_1x.h"
+
+
+#define DEFAULT_KEY_LEN		16
+/* secure Connectivity Association Key Name (CKN) */
+#define DEFAULT_CKN_LEN		16
+
+
+static int hapd_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_init)
+		return -1;
+	return hapd->driver->macsec_init(hapd->drv_priv, params);
+}
+
+
+static int hapd_macsec_deinit(void *priv)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_deinit)
+		return -1;
+	return hapd->driver->macsec_deinit(hapd->drv_priv);
+}
+
+
+static int hapd_macsec_get_capability(void *priv, enum macsec_cap *cap)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_get_capability)
+		return -1;
+	return hapd->driver->macsec_get_capability(hapd->drv_priv, cap);
+}
+
+
+static int hapd_enable_protect_frames(void *priv, Boolean enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_protect_frames)
+		return -1;
+	return hapd->driver->enable_protect_frames(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_enable_encrypt(void *priv, Boolean enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_encrypt)
+		return -1;
+	return hapd->driver->enable_encrypt(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_set_replay_protect(void *priv, Boolean enabled, u32 window)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_replay_protect)
+		return -1;
+	return hapd->driver->set_replay_protect(hapd->drv_priv, enabled,
+						 window);
+}
+
+
+static int hapd_set_current_cipher_suite(void *priv, u64 cs)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_current_cipher_suite)
+		return -1;
+	return hapd->driver->set_current_cipher_suite(hapd->drv_priv, cs);
+}
+
+
+static int hapd_enable_controlled_port(void *priv, Boolean enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_controlled_port)
+		return -1;
+	return hapd->driver->enable_controlled_port(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->get_receive_lowest_pn)
+		return -1;
+	return hapd->driver->get_receive_lowest_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->get_transmit_next_pn)
+		return -1;
+	return hapd->driver->get_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_transmit_next_pn)
+		return -1;
+	return hapd->driver->set_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static unsigned int conf_offset_val(enum confidentiality_offset co)
+{
+	switch (co) {
+	case CONFIDENTIALITY_OFFSET_30:
+		return 30;
+		break;
+	case CONFIDENTIALITY_OFFSET_50:
+		return 50;
+	default:
+		return 0;
+	}
+}
+
+
+static int hapd_create_receive_sc(void *priv, struct receive_sc *sc,
+				  enum validate_frames vf,
+				  enum confidentiality_offset co)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_receive_sc)
+		return -1;
+	return hapd->driver->create_receive_sc(hapd->drv_priv, sc,
+					       conf_offset_val(co), vf);
+}
+
+
+static int hapd_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_receive_sc)
+		return -1;
+	return hapd->driver->delete_receive_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_receive_sa)
+		return -1;
+	return hapd->driver->create_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_receive_sa)
+		return -1;
+	return hapd->driver->delete_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_receive_sa)
+		return -1;
+	return hapd->driver->enable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->disable_receive_sa)
+		return -1;
+	return hapd->driver->disable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int
+hapd_create_transmit_sc(void *priv, struct transmit_sc *sc,
+			enum confidentiality_offset co)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_transmit_sc)
+		return -1;
+	return hapd->driver->create_transmit_sc(hapd->drv_priv, sc,
+						conf_offset_val(co));
+}
+
+
+static int hapd_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_transmit_sc)
+		return -1;
+	return hapd->driver->delete_transmit_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_transmit_sa)
+		return -1;
+	return hapd->driver->create_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_transmit_sa)
+		return -1;
+	return hapd->driver->delete_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_transmit_sa)
+		return -1;
+	return hapd->driver->enable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->disable_transmit_sa)
+		return -1;
+	return hapd->driver->disable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	struct ieee802_1x_kay_ctx *kay_ctx;
+	struct ieee802_1x_kay *res = NULL;
+	enum macsec_policy policy;
+
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
+
+	if (!hapd->conf || hapd->conf->macsec_policy == 0)
+		return 0;
+
+	if (hapd->conf->macsec_policy == 1) {
+		if (hapd->conf->macsec_integ_only == 1)
+			policy = SHOULD_SECURE;
+		else
+			policy = SHOULD_ENCRYPT;
+	} else {
+		policy = DO_NOT_SECURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: if_name=%s", __func__, hapd->conf->iface);
+	kay_ctx = os_zalloc(sizeof(*kay_ctx));
+	if (!kay_ctx)
+		return -1;
+
+	kay_ctx->ctx = hapd;
+
+	kay_ctx->macsec_init = hapd_macsec_init;
+	kay_ctx->macsec_deinit = hapd_macsec_deinit;
+	kay_ctx->macsec_get_capability = hapd_macsec_get_capability;
+	kay_ctx->enable_protect_frames = hapd_enable_protect_frames;
+	kay_ctx->enable_encrypt = hapd_enable_encrypt;
+	kay_ctx->set_replay_protect = hapd_set_replay_protect;
+	kay_ctx->set_current_cipher_suite = hapd_set_current_cipher_suite;
+	kay_ctx->enable_controlled_port = hapd_enable_controlled_port;
+	kay_ctx->get_receive_lowest_pn = hapd_get_receive_lowest_pn;
+	kay_ctx->get_transmit_next_pn = hapd_get_transmit_next_pn;
+	kay_ctx->set_transmit_next_pn = hapd_set_transmit_next_pn;
+	kay_ctx->create_receive_sc = hapd_create_receive_sc;
+	kay_ctx->delete_receive_sc = hapd_delete_receive_sc;
+	kay_ctx->create_receive_sa = hapd_create_receive_sa;
+	kay_ctx->delete_receive_sa = hapd_delete_receive_sa;
+	kay_ctx->enable_receive_sa = hapd_enable_receive_sa;
+	kay_ctx->disable_receive_sa = hapd_disable_receive_sa;
+	kay_ctx->create_transmit_sc = hapd_create_transmit_sc;
+	kay_ctx->delete_transmit_sc = hapd_delete_transmit_sc;
+	kay_ctx->create_transmit_sa = hapd_create_transmit_sa;
+	kay_ctx->delete_transmit_sa = hapd_delete_transmit_sa;
+	kay_ctx->enable_transmit_sa = hapd_enable_transmit_sa;
+	kay_ctx->disable_transmit_sa = hapd_disable_transmit_sa;
+
+	res = ieee802_1x_kay_init(kay_ctx, policy,
+				  hapd->conf->macsec_replay_protect,
+				  hapd->conf->macsec_replay_window,
+				  hapd->conf->macsec_port,
+				  hapd->conf->mka_priority, hapd->conf->iface,
+				  hapd->own_addr);
+	/* ieee802_1x_kay_init() frees kay_ctx on failure */
+	if (!res)
+		return -1;
+
+	hapd->kay = res;
+
+	return 0;
+}
+
+
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+	if (!hapd->kay)
+		return;
+
+	ieee802_1x_kay_deinit(hapd->kay);
+	hapd->kay = NULL;
+}
+
+
+static int ieee802_1x_auth_get_session_id(struct hostapd_data *hapd,
+					  struct sta_info *sta, u8 *sid,
+					  size_t *len)
+{
+	const u8 *session_id;
+	size_t id_len, need_len;
+
+	session_id = ieee802_1x_get_session_id(sta->eapol_sm, &id_len);
+	if (!session_id) {
+		wpa_printf(MSG_DEBUG,
+			   "MACsec: Failed to get SessionID from EAPOL state machines");
+		return -1;
+	}
+
+	need_len = 1 + 2 * 32 /* random size */;
+	if (need_len > id_len) {
+		wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
+		return -1;
+	}
+
+	os_memcpy(sid, session_id, need_len);
+	*len = need_len;
+
+	return 0;
+}
+
+
+static int ieee802_1x_auth_get_msk(struct hostapd_data *hapd,
+				   struct sta_info *sta, u8 *msk, size_t *len)
+{
+	const u8 *key;
+	size_t keylen;
+
+	if (!sta->eapol_sm)
+		return -1;
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "MACsec: Failed to get MSK from EAPOL state machines");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "MACsec: Successfully fetched key (len=%lu)",
+		   (unsigned long) keylen);
+	wpa_hexdump_key(MSG_DEBUG, "MSK: ", key, keylen);
+
+	if (keylen > *len)
+		keylen = *len;
+	os_memcpy(msk, key, keylen);
+	*len = keylen;
+
+	return 0;
+}
+
+
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+					   struct sta_info *sta)
+{
+	u8 *sid;
+	size_t sid_len = 128;
+	struct mka_key_name *ckn;
+	struct mka_key *cak;
+	struct mka_key *msk;
+	void *res = NULL;
+
+	if (!hapd->kay || hapd->kay->policy == DO_NOT_SECURE)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG,
+		   "IEEE 802.1X: External notification - Create MKA for "
+		   MACSTR, MAC2STR(sta->addr));
+
+	msk = os_zalloc(sizeof(*msk));
+	sid = os_zalloc(sid_len);
+	ckn = os_zalloc(sizeof(*ckn));
+	cak = os_zalloc(sizeof(*cak));
+	if (!msk || !sid || !ckn || !cak)
+		goto fail;
+
+	msk->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_auth_get_msk(hapd, sta, msk->key, &msk->len)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK");
+		goto fail;
+	}
+
+	if (ieee802_1x_auth_get_session_id(hapd, sta, sid, &sid_len))
+	{
+		wpa_printf(MSG_ERROR,
+			   "IEEE 802.1X: Could not get EAP Session Id");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "own_addr", hapd->own_addr, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "sta_addr", sta->addr, ETH_ALEN);
+
+	/* Derive CAK from MSK */
+	cak->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_cak_aes_cmac(msk->key, msk->len, hapd->own_addr,
+				    sta->addr, cak->key, cak->len)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CAK failed");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len);
+
+	/* Derive CKN from MSK */
+	ckn->len = DEFAULT_CKN_LEN;
+	if (ieee802_1x_ckn_aes_cmac(msk->key, msk->len, hapd->own_addr,
+				    sta->addr, sid, sid_len, ckn->name)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CKN failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len);
+
+	res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, EAP_EXCHANGE,
+					TRUE);
+
+fail:
+	bin_clear_free(msk, sizeof(*msk));
+	os_free(sid);
+	os_free(ckn);
+	bin_clear_free(cak, sizeof(*cak));
+
+	return res;
+}
+
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+	struct mka_key *cak;
+	struct mka_key_name *ckn;
+	void *res = NULL;
+
+	if ((hapd->conf->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET)
+		goto end;
+
+	ckn = os_zalloc(sizeof(*ckn));
+	if (!ckn)
+		goto end;
+
+	cak = os_zalloc(sizeof(*cak));
+	if (!cak)
+		goto free_ckn;
+
+	if (ieee802_1x_alloc_kay_sm_hapd(hapd, sta) < 0 || !hapd->kay)
+		goto free_cak;
+
+	if (hapd->kay->policy == DO_NOT_SECURE)
+		goto dealloc;
+
+	cak->len = hapd->conf->mka_cak_len;
+	os_memcpy(cak->key, hapd->conf->mka_cak, cak->len);
+
+	ckn->len = hapd->conf->mka_ckn_len;;
+	os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len);
+
+	res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, TRUE);
+	if (res)
+		goto free_cak;
+
+dealloc:
+	/* Failed to create MKA */
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
+free_cak:
+	os_free(cak);
+free_ckn:
+	os_free(ckn);
+end:
+	return res;
+}
diff --git a/src/ap/wpa_auth_kay.h b/src/ap/wpa_auth_kay.h
new file mode 100644
index 0000000..0dd7e41
--- /dev/null
+++ b/src/ap/wpa_auth_kay.h
@@ -0,0 +1,51 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_KAY_H
+#define WPA_AUTH_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+				 struct sta_info *sta);
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+					   struct sta_info *sta);
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd);
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+					    struct sta_info *sta);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+					       struct sta_info *sta)
+{
+	return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+				    struct sta_info *sta)
+{
+	return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+}
+
+static inline void *
+ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	return NULL;
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPA_AUTH_KAY_H */