Merge "Cumulative patch from commit 4ec1fd8e42bad9390f14a58225b6e5f6fb691950"
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index ea3a39a..9a14be4 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -38,6 +38,9 @@
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
 L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/hostapd\"
 
+# Use Android specific directory for hostapd_cli command completion history
+L_CFLAGS += -DCONFIG_HOSTAPD_CLI_HISTORY_DIR=\"/data/misc/wifi\"
+
 # To force sizeof(enum) = 4
 ifeq ($(TARGET_ARCH),arm)
 L_CFLAGS += -mabi=aapcs-linux
@@ -244,7 +247,7 @@
 endif
 
 ifdef CONFIG_IEEE80211R
-L_CFLAGS += -DCONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -258,6 +261,13 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_FILS
+L_CFLAGS += -DCONFIG_FILS
+NEED_CRC32=y
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 L_CFLAGS += -DCONFIG_WNM
 OBJS += src/ap/wnm_ap.c
@@ -737,6 +747,9 @@
 AESOBJS += src/crypto/aes-cbc.c
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal-dec.c
@@ -852,6 +865,10 @@
 L_CFLAGS += -DCONFIG_ECC
 endif
 
+ifdef NEED_CRC32
+OBJS += src/utils/crc32.c
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 30b1472..d2b669b 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,6 +1,6 @@
 ChangeLog for hostapd
 
-????-??-?? - v2.6
+2016-10-02 - v2.6
 	* fixed EAP-pwd last fragment validation
 	  [http://w1.fi/security/2015-7/] (CVE-2015-5314)
 	* fixed WPS configuration update vulnerability with malformed passphrase
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 46dffe5..52c72dc 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -290,7 +290,7 @@
 endif
 
 ifdef CONFIG_IEEE80211R
-CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -305,6 +305,13 @@
 NEED_AP_MLME=y
 endif
 
+ifdef CONFIG_FILS
+CFLAGS += -DCONFIG_FILS
+NEED_CRC32=y
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 CFLAGS += -DCONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
@@ -777,6 +784,9 @@
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-dec.o
@@ -894,6 +904,10 @@
 CFLAGS += -DCONFIG_ECC
 endif
 
+ifdef NEED_CRC32
+OBJS += ../src/utils/crc32.o
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
diff --git a/hostapd/android.config b/hostapd/android.config
index e382c40..1715cc8 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -199,3 +199,6 @@
 # These extentions facilitate efficient use of multiple frequency bands
 # available to the AP and the devices that may associate with it.
 #CONFIG_MBO=y
+
+# Include internal line edit mode in hostapd_cli.
+CONFIG_WPA_CLI_EDIT=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 5079f69..7795015 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -208,7 +208,8 @@
 
 	fclose(f);
 
-	qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+	if (*acl)
+		qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
 
 	return 0;
 }
@@ -710,6 +711,18 @@
 		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+		else if (os_strcmp(start, "FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA256;
+		else if (os_strcmp(start, "FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA384;
+#ifdef CONFIG_IEEE80211R
+		else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA256;
+		else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -2559,6 +2572,8 @@
 		bss->pmk_r1_push = atoi(pos);
 	} else if (os_strcmp(buf, "ft_over_ds") == 0) {
 		bss->ft_over_ds = atoi(pos);
+	} else if (os_strcmp(buf, "ft_psk_generate_local") == 0) {
+		bss->ft_psk_generate_local = atoi(pos);
 #endif /* CONFIG_IEEE80211R */
 #ifndef CONFIG_NO_CTRL_IFACE
 	} else if (os_strcmp(buf, "ctrl_interface") == 0) {
@@ -3478,19 +3493,39 @@
 	} else if (os_strcmp(buf, "lci") == 0) {
 		wpabuf_free(conf->lci);
 		conf->lci = wpabuf_parse_bin(pos);
+		if (conf->lci && wpabuf_len(conf->lci) == 0) {
+			wpabuf_free(conf->lci);
+			conf->lci = NULL;
+		}
 	} else if (os_strcmp(buf, "civic") == 0) {
 		wpabuf_free(conf->civic);
 		conf->civic = wpabuf_parse_bin(pos);
+		if (conf->civic && wpabuf_len(conf->civic) == 0) {
+			wpabuf_free(conf->civic);
+			conf->civic = NULL;
+		}
 	} else if (os_strcmp(buf, "rrm_neighbor_report") == 0) {
 		if (atoi(pos))
 			bss->radio_measurements[0] |=
 				WLAN_RRM_CAPS_NEIGHBOR_REPORT;
 	} else if (os_strcmp(buf, "gas_address3") == 0) {
 		bss->gas_address3 = atoi(pos);
+	} else if (os_strcmp(buf, "stationary_ap") == 0) {
+		conf->stationary_ap = atoi(pos);
 	} else if (os_strcmp(buf, "ftm_responder") == 0) {
 		bss->ftm_responder = atoi(pos);
 	} else if (os_strcmp(buf, "ftm_initiator") == 0) {
 		bss->ftm_initiator = atoi(pos);
+#ifdef CONFIG_FILS
+	} else if (os_strcmp(buf, "fils_cache_id") == 0) {
+		if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fils_cache_id '%s'",
+				   line, pos);
+			return 1;
+		}
+		bss->fils_cache_id_set = 1;
+#endif /* CONFIG_FILS */
 	} else {
 		wpa_printf(MSG_ERROR,
 			   "Line %d: unknown configuration item '%s'",
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index d7db4a7..0d86b4a 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1117,6 +1117,20 @@
 		pos += ret;
 	}
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS */
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
@@ -1154,6 +1168,20 @@
 			return pos - buf;
 		pos += ret;
 	}
+#ifdef CONFIG_FILS
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "FILS-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "FILS-SHA384 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS */
 
 	if (pos > buf && *(pos - 1) == ' ') {
 		*(pos - 1) = '\0';
@@ -2055,8 +2083,9 @@
 		int ret;
 
 		os_reltime_sub(&now, &info->last_seen, &age);
-		ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
-				  MAC2STR(info->addr), (unsigned int) age.sec);
+		ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n",
+				  MAC2STR(info->addr), (unsigned int) age.sec,
+				  info->ssi_signal);
 		if (os_snprintf_error(end - pos, ret))
 			break;
 		pos += ret;
@@ -2145,6 +2174,7 @@
 	struct wpa_ssid_value ssid;
 	u8 bssid[ETH_ALEN];
 	struct wpabuf *nr, *lci = NULL, *civic = NULL;
+	int stationary = 0;
 	char *tmp;
 	int ret;
 
@@ -2223,8 +2253,15 @@
 		}
 	}
 
+	if (!buf)
+		goto set;
+
+	if (os_strstr(buf, "stat"))
+		stationary = 1;
+
 set:
-	ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic);
+	ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
+				   stationary);
 
 	wpabuf_free(nr);
 	wpabuf_free(lci);
@@ -3478,8 +3515,6 @@
 		}
 	}
 
-	dl_list_init(&interface->global_ctrl_dst);
-	interface->global_ctrl_sock = -1;
 	os_get_random(gcookie, COOKIE_LEN);
 
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 4659dd1..e25ec32 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -343,3 +343,12 @@
 # a client, from which a signature can be produced which can identify the model
 # of client device like "Nexus 6P" or "iPhone 5s".
 #CONFIG_TAXONOMY=y
+
+# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
+# Note: This is an experimental and not yet complete implementation. This
+# should not be enabled for production use.
+#CONFIG_FILS=y
+
+# Include internal line edit mode in hostapd_cli. This can be used to provide
+# limited command line editing and history support.
+#CONFIG_WPA_CLI_EDIT=y
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index fa9a855..54c8b95 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1163,6 +1163,10 @@
 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
 # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
 # added to enable SHA256-based stronger algorithms.
+# FILS-SHA256 = Fast Initial Link Setup with SHA256
+# FILS-SHA384 = Fast Initial Link Setup with SHA384
+# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
+# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
 # (dot11RSNAConfigAuthenticationSuitesTable)
 #wpa_key_mgmt=WPA-PSK WPA-EAP
 
@@ -1273,6 +1277,9 @@
 # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
 #sae_groups=19 20 21 25 26
 
+# FILS Cache Identifier (16-bit value in hexdump format)
+#fils_cache_id=0011
+
 ##### IEEE 802.11r configuration ##############################################
 
 # Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
@@ -1326,6 +1333,14 @@
 # 1 = FT-over-DS enabled (default)
 #ft_over_ds=1
 
+# Whether to generate FT response locally for PSK networks
+# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as
+# the required information (PSK and other session data) is already locally
+# available.
+# 0 = disabled (default)
+# 1 = enabled
+#ft_psk_generate_local=0
+
 ##### Neighbor table ##########################################################
 # Maximum number of entries kept in AP table (either for neigbor table or for
 # detecting Overlapping Legacy BSS Condition). The oldest entry will be
@@ -1925,6 +1940,12 @@
 # This parameter only controls publishing via Extended Capabilities element.
 # Actual functionality is managed outside hostapd.
 #ftm_initiator=0
+#
+# Stationary AP config indicates that the AP doesn't move hence location data
+# can be considered as always up to date. If configured, LCI data will be sent
+# as a radio measurement even if the request doesn't contain a max age element
+# that allows sending of such data. Default: 0.
+#stationary_ap=0
 
 ##### TESTING OPTIONS #########################################################
 #
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 5e62542..8b8d7f9 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -45,6 +45,8 @@
 static void print_help(FILE *stream, const char *cmd);
 static char ** list_cmd_list(void);
 static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx);
+static void update_stations(struct wpa_ctrl *ctrl);
+static void cli_event(const char *str);
 
 
 static void usage(void)
@@ -147,13 +149,45 @@
 }
 
 
+static int hostapd_cli_reconnect(const char *ifname)
+{
+	char *next_ctrl_ifname;
+
+	hostapd_cli_close_connection();
+
+	if (!ifname)
+		return -1;
+
+	next_ctrl_ifname = os_strdup(ifname);
+	os_free(ctrl_ifname);
+	ctrl_ifname = next_ctrl_ifname;
+	if (!ctrl_ifname)
+		return -1;
+
+	ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+	if (!ctrl_conn)
+		return -1;
+	if (!interactive && !action_file)
+		return 0;
+	if (wpa_ctrl_attach(ctrl_conn) == 0) {
+		hostapd_cli_attached = 1;
+		register_event_handler(ctrl_conn);
+		update_stations(ctrl_conn);
+	} else {
+		printf("Warning: Failed to attach to hostapd.\n");
+	}
+	return 0;
+}
+
+
 static void hostapd_cli_msg_cb(char *msg, size_t len)
 {
+	cli_event(msg);
 	printf("%s\n", msg);
 }
 
 
-static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
 {
 	char buf[4096];
 	size_t len;
@@ -181,7 +215,7 @@
 }
 
 
-static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
 {
 	return _wpa_ctrl_command(ctrl, cmd, 1);
 }
@@ -286,6 +320,21 @@
 }
 
 
+static char ** hostapd_complete_sta(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
 static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
@@ -701,8 +750,8 @@
 }
 
 
-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
-				char *addr, size_t addr_len)
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+				char *addr, size_t addr_len, int print)
 {
 	char buf[4096], *pos;
 	size_t len;
@@ -726,7 +775,8 @@
 	buf[len] = '\0';
 	if (memcmp(buf, "FAIL", 4) == 0)
 		return -1;
-	printf("%s", buf);
+	if (print)
+		printf("%s", buf);
 
 	pos = buf;
 	while (*pos != '\0' && *pos != '\n')
@@ -742,16 +792,33 @@
 {
 	char addr[32], cmd[64];
 
-	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
 		return 0;
 	do {
 		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
-	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
 
 	return -1;
 }
 
 
+static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	char addr[32], cmd[64];
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return 0;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			printf("%s\n", addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+
+	return 0;
+}
+
+
 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	print_help(stdout, argc > 0 ? argv[0] : NULL);
@@ -888,6 +955,25 @@
 }
 
 
+static void update_stations(struct wpa_ctrl *ctrl)
+{
+	char addr[32], cmd[64];
+
+	if (!ctrl || !interactive)
+		return;
+
+	cli_txt_list_flush(&stations);
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			cli_txt_list_add(&stations, addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+}
+
+
 static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl,
 				       struct dl_list *interfaces)
 {
@@ -940,23 +1026,7 @@
 		hostapd_cli_list_interfaces(ctrl);
 		return 0;
 	}
-
-	hostapd_cli_close_connection();
-	os_free(ctrl_ifname);
-	ctrl_ifname = os_strdup(argv[0]);
-	if (ctrl_ifname == NULL)
-		return -1;
-
-	if (hostapd_cli_open_connection(ctrl_ifname)) {
-		printf("Connected to interface '%s.\n", ctrl_ifname);
-		if (wpa_ctrl_attach(ctrl_conn) == 0) {
-			hostapd_cli_attached = 1;
-			register_event_handler(ctrl_conn);
-		} else {
-			printf("Warning: Failed to attach to "
-			       "hostapd.\n");
-		}
-	} else {
+	if (hostapd_cli_reconnect(argv[0]) != 0) {
 		printf("Could not connect to interface '%s' - re-trying\n",
 			ctrl_ifname);
 	}
@@ -1185,14 +1255,14 @@
 	char cmd[2048];
 	int res;
 
-	if (argc < 3 || argc > 5) {
-		printf("Invalid set_neighbor command: needs 3-5 arguments\n");
+	if (argc < 3 || argc > 6) {
+		printf("Invalid set_neighbor command: needs 3-6 arguments\n");
 		return -1;
 	}
 
-	res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s",
+	res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s",
 			  argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "",
-			  argc == 5 ? argv[4] : "");
+			  argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : "");
 	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long SET_NEIGHBOR command.\n");
 		return -1;
@@ -1275,10 +1345,12 @@
 	  "= get MIB variables (dot1x, dot11, radius)" },
 	{ "relog", hostapd_cli_cmd_relog, NULL, NULL },
 	{ "status", hostapd_cli_cmd_status, NULL, NULL },
-	{ "sta", hostapd_cli_cmd_sta, NULL,
+	{ "sta", hostapd_cli_cmd_sta, hostapd_complete_sta,
 	  "<addr> = get MIB variables for one station" },
 	{ "all_sta", hostapd_cli_cmd_all_sta, NULL,
 	   "= get MIB variables for all stations" },
+	{ "list_sta", hostapd_cli_cmd_list_sta, NULL,
+	   "= list all stations" },
 	{ "new_sta", hostapd_cli_cmd_new_sta, NULL,
 	  "<addr> = add a new station" },
 	{ "deauthenticate", hostapd_cli_cmd_deauthenticate,
@@ -1504,19 +1576,8 @@
 		printf("Connection to hostapd lost - trying to reconnect\n");
 		hostapd_cli_close_connection();
 	}
-	if (!ctrl_conn) {
-		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
-		if (ctrl_conn) {
-			printf("Connection to hostapd re-established\n");
-			if (wpa_ctrl_attach(ctrl_conn) == 0) {
-				hostapd_cli_attached = 1;
-				register_event_handler(ctrl_conn);
-			} else {
-				printf("Warning: Failed to attach to "
-				       "hostapd.\n");
-			}
-		}
-	}
+	if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0)
+		printf("Connection to hostapd re-established\n");
 	if (ctrl_conn)
 		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
 	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
@@ -1611,17 +1672,34 @@
 
 static void hostapd_cli_interactive(void)
 {
+	char *hfile = NULL;
+	char *home;
+
 	printf("\nInteractive mode\n\n");
 
+#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR
+	home = CONFIG_HOSTAPD_CLI_HISTORY_DIR;
+#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
+	home = getenv("HOME");
+#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
+	if (home) {
+		const char *fname = ".hostapd_cli_history";
+		int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+		hfile = os_malloc(hfile_len);
+		if (hfile)
+			os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
+	}
+
 	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
 	edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
-		  hostapd_cli_edit_completion_cb, NULL, NULL, NULL);
+		  hostapd_cli_edit_completion_cb, NULL, hfile, NULL);
 	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
 
 	eloop_run();
 
 	cli_txt_list_flush(&stations);
-	edit_deinit(NULL, NULL);
+	edit_deinit(hfile, NULL);
+	os_free(hfile);
 	eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
 }
 
@@ -1748,7 +1826,7 @@
 				closedir(dir);
 			}
 		}
-		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+		hostapd_cli_reconnect(ctrl_ifname);
 		if (ctrl_conn) {
 			if (warning_displayed)
 				printf("Connection established.\n");
@@ -1769,17 +1847,8 @@
 		continue;
 	}
 
-	if (interactive || action_file) {
-		if (wpa_ctrl_attach(ctrl_conn) == 0) {
-			hostapd_cli_attached = 1;
-			register_event_handler(ctrl_conn);
-		} else {
-			printf("Warning: Failed to attach to hostapd.\n");
-			if (action_file)
-				return -1;
-		}
-	}
-
+	if (action_file && !hostapd_cli_attached)
+		return -1;
 	if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
 		return -1;
 
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
index 33e3fa1..51c1d96 100644
--- a/hs20/server/spp_server.c
+++ b/hs20/server/spp_server.c
@@ -1823,10 +1823,8 @@
 	}
 
 	if (strcasecmp(req_reason, "User input completed") == 0) {
-		if (devinfo)
-			db_add_session_devinfo(ctx, session_id, devinfo);
-		if (devdetail)
-			db_add_session_devdetail(ctx, session_id, devdetail);
+		db_add_session_devinfo(ctx, session_id, devinfo);
+		db_add_session_devdetail(ctx, session_id, devdetail);
 		ret = hs20_user_input_complete(ctx, user, realm, dmacc,
 					       session_id);
 		hs20_eventlog_node(ctx, user, realm, session_id,
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 98788fe..3b01e63 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -12,7 +12,7 @@
 CFLAGS += -DNEED_AP_MLME
 CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_INTERWORKING
-CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R_AP
 CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_WPS
 CFLAGS += -DCONFIG_PROXYARP
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 228de2b..c986574 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -88,9 +88,9 @@
 	/* Set to -1 as defaults depends on HT in setup */
 	bss->wmm_enabled = -1;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	bss->ft_over_ds = 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	bss->radius_das_time_window = 300;
 
@@ -477,7 +477,7 @@
 	hostapd_config_free_vlan(conf);
 	os_free(conf->time_zone);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	{
 		struct ft_remote_r0kh *r0kh, *r0kh_prev;
 		struct ft_remote_r1kh *r1kh, *r1kh_prev;
@@ -498,7 +498,7 @@
 			os_free(r1kh_prev);
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_WPS
 	os_free(conf->wps_pin_requests);
@@ -802,7 +802,7 @@
 		}
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
 	    (bss->nas_identifier == NULL ||
 	     os_strlen(bss->nas_identifier) < 1 ||
@@ -812,7 +812,7 @@
 			   "string");
 		return -1;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_IEEE80211N
 	if (full_config && conf->ieee80211n &&
@@ -848,6 +848,16 @@
 		wpa_printf(MSG_ERROR,
 			   "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities");
 	}
+
+	if (full_config && conf->ieee80211ac && bss->wpa &&
+	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				   WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+	{
+		bss->disable_11ac = 1;
+		wpa_printf(MSG_ERROR,
+			   "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities");
+	}
 #endif /* CONFIG_IEEE80211AC */
 
 #ifdef CONFIG_WPS
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 8c8f7e2..cace34c 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -329,7 +329,7 @@
 	char *rsn_preauth_interfaces;
 	int peerkey;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	/* IEEE 802.11r - Fast BSS Transition */
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
@@ -339,7 +339,8 @@
 	struct ft_remote_r1kh *r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+	int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
 
 	char *ctrl_interface; /* directory for UNIX domain sockets */
 #ifndef CONFIG_NATIVE_WINDOWS
@@ -595,6 +596,11 @@
 
 	int ftm_responder;
 	int ftm_initiator;
+
+#ifdef CONFIG_FILS
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+	int fils_cache_id_set;
+#endif /* CONFIG_FILS */
 };
 
 
@@ -707,6 +713,7 @@
 
 	struct wpabuf *lci;
 	struct wpabuf *civic;
+	int stationary_ap;
 };
 
 
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index e7308a0..db8a267 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -57,7 +57,11 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
 		       MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
-	if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK &&
+	    !(sta->flags & WLAN_STA_MFP))
 		mlme_deletekeys_request(hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
@@ -105,7 +109,10 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-ASSOCIATE.indication(" MACSTR ")",
 		       MAC2STR(sta->addr));
-	if (sta->auth_alg != WLAN_AUTH_FT)
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK)
 		mlme_deletekeys_request(hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
@@ -130,7 +137,10 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-REASSOCIATE.indication(" MACSTR ")",
 		       MAC2STR(sta->addr));
-	if (sta->auth_alg != WLAN_AUTH_FT)
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK)
 		mlme_deletekeys_request(hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 233320d..811bede 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -491,6 +491,11 @@
 		pos = hostapd_eid_txpower_envelope(hapd, pos);
 		pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
 	}
+#endif /* CONFIG_IEEE80211AC */
+
+	pos = hostapd_eid_fils_indic(hapd, pos, 0);
+
+#ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
@@ -618,7 +623,7 @@
 }
 
 
-void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
 {
 	struct hostapd_sta_info *info;
 
@@ -628,6 +633,7 @@
 		dl_list_del(&info->list);
 		dl_list_add_tail(&iface->sta_seen, &info->list);
 		os_get_reltime(&info->last_seen);
+		info->ssi_signal = ssi_signal;
 		return;
 	}
 
@@ -637,6 +643,7 @@
 		return;
 	os_memcpy(info->addr, addr, ETH_ALEN);
 	os_get_reltime(&info->last_seen);
+	info->ssi_signal = ssi_signal;
 
 	if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
 		/* Expire oldest entry to make room for a new one */
@@ -712,7 +719,7 @@
 		return;
 	ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
 	if (hapd->iconf->track_sta_max_num)
-		sta_track_add(hapd->iface, mgmt->sa);
+		sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
 	ie_len = len - IEEE80211_HDRLEN;
 
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
@@ -1155,6 +1162,11 @@
 		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
 		tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
 	}
+#endif /* CONFIG_IEEE80211AC */
+
+	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+
+#ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index fc71181..a26e308 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -21,7 +21,7 @@
 int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 			       struct wpa_driver_ap_params *params);
 void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
-void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
 void sta_track_del(struct hostapd_sta_info *info);
 void sta_track_expire(struct hostapd_iface *iface, int force);
 struct hostapd_data *
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 3552b3e..f69c655 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -45,10 +45,10 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W)
 	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 	u8 *p = buf;
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
 	u16 status = WLAN_STATUS_SUCCESS;
 	const u8 *p2p_dev_addr = NULL;
@@ -293,7 +293,7 @@
 			sta->flags &= ~WLAN_STA_MFP;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
 							 req_ies_len);
@@ -307,7 +307,7 @@
 				goto fail;
 			}
 		}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	} else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
 		struct wpabuf *wps;
@@ -375,7 +375,7 @@
 skip_wpa_check:
 #endif /* CONFIG_WPS */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
 					sta->auth_alg, req_ies, req_ies_len);
 
@@ -383,11 +383,11 @@
 
 	if (sta->auth_alg == WLAN_AUTH_FT)
 		ap_sta_set_authorized(hapd, sta, 1);
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP */
 	/* Keep compiler silent about unused variables */
 	if (status) {
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
@@ -414,9 +414,9 @@
 	return 0;
 
 fail:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
 	ap_free_sta(hapd, sta);
 	return -1;
@@ -471,8 +471,7 @@
 		       HOSTAPD_LEVEL_INFO,
 		       "disconnected due to excessive missing ACKs");
 	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
-	if (sta)
-		ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+	ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
 }
 
 
@@ -690,7 +689,7 @@
 
 #ifdef HOSTAPD
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
 					  const u8 *bssid,
 					  u16 auth_transaction, u16 status,
@@ -709,7 +708,7 @@
 
 	hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 static void hostapd_notif_auth(struct hostapd_data *hapd,
@@ -730,7 +729,7 @@
 	}
 	sta->flags &= ~WLAN_STA_PREAUTH;
 	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
 		sta->auth_alg = WLAN_AUTH_FT;
 		if (sta->wpa_sm == NULL)
@@ -748,7 +747,7 @@
 				    hostapd_notify_auth_ft_finish, hapd);
 		return;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 fail:
 	hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
 			 status, resp_ies, resp_ies_len);
@@ -781,13 +780,13 @@
 		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
 		return;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (mgmt->u.action.category == WLAN_ACTION_FT) {
 		const u8 *payload = drv_mgmt->frame + 24 + 1;
 
 		wpa_ft_action_rx(sta->wpa_sm, payload, plen);
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
 		ieee802_11_sa_query_action(
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 9fafc7f..21a5408 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -956,10 +956,10 @@
 	if (conf->wmm_enabled < 0)
 		conf->wmm_enabled = hapd->iconf->ieee80211n;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (is_zero_ether_addr(conf->r1_key_holder))
 		os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_MESH
 	if (hapd->iface->mconf == NULL)
@@ -1561,7 +1561,7 @@
 	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
 	struct wpa_ssid_value ssid;
 	u8 channel, op_class;
-	int center_freq1 = 0, center_freq2 = 0;
+	u8 center_freq1_idx = 0, center_freq2_idx = 0;
 	enum nr_chan_width width;
 	u32 bssid_info;
 	struct wpabuf *nr;
@@ -1604,16 +1604,14 @@
 				      &op_class, &channel);
 	width = hostapd_get_nr_chan_width(hapd, ht, vht);
 	if (vht) {
-		center_freq1 = ieee80211_chan_to_freq(
-			NULL, op_class,
-			hapd->iconf->vht_oper_centr_freq_seg0_idx);
+		center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
 		if (width == NR_CHAN_WIDTH_80P80)
-			center_freq2 = ieee80211_chan_to_freq(
-				NULL, op_class,
-				hapd->iconf->vht_oper_centr_freq_seg1_idx);
+			center_freq2_idx =
+				hapd->iconf->vht_oper_centr_freq_seg1_idx;
 	} else if (ht) {
-		center_freq1 = hapd->iface->freq +
-			10 * hapd->iconf->secondary_channel;
+		ieee80211_freq_to_chan(hapd->iface->freq +
+				       10 * hapd->iconf->secondary_channel,
+				       &center_freq1_idx);
 	}
 
 	ssid.ssid_len = hapd->conf->ssid.ssid_len;
@@ -1641,11 +1639,11 @@
 	wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
 	wpabuf_put_u8(nr, 3);
 	wpabuf_put_u8(nr, width);
-	wpabuf_put_u8(nr, center_freq1);
-	wpabuf_put_u8(nr, center_freq2);
+	wpabuf_put_u8(nr, center_freq1_idx);
+	wpabuf_put_u8(nr, center_freq2_idx);
 
 	hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
-			     hapd->iconf->civic);
+			     hapd->iconf->civic, hapd->iconf->stationary_ap);
 
 	wpabuf_free(nr);
 #endif /* NEED_AP_MLME */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dec46f6..fd5aaed 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -109,6 +109,7 @@
 	struct wpabuf *civic;
 	/* LCI update time */
 	struct os_time lci_date;
+	int stationary;
 };
 
 /**
@@ -311,6 +312,7 @@
 	struct dl_list list;
 	u8 addr[ETH_ALEN];
 	struct os_reltime last_seen;
+	int ssi_signal;
 #ifdef CONFIG_TAXONOMY
 	struct wpabuf *probe_ie_taxonomy;
 #endif /* CONFIG_TAXONOMY */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index f1c396b..0b3d2f2 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -303,7 +303,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
 				  u16 auth_transaction, u16 status,
 				  const u8 *ies, size_t ies_len)
@@ -334,7 +334,7 @@
 	sta->flags |= WLAN_STA_AUTH;
 	mlme_authenticate_indication(hapd, sta);
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 #ifdef CONFIG_SAE
@@ -619,7 +619,7 @@
 				 * message now to get alternating sequence of
 				 * Authentication frames between the AP and STA.
 				 * Confirm will be sent in
-				 * Commited -> Confirmed/Accepted transition
+				 * Committed -> Confirmed/Accepted transition
 				 * when receiving Confirm from STA.
 				 */
 			}
@@ -980,6 +980,318 @@
 #endif /* CONFIG_SAE */
 
 
+static u16 wpa_res_to_status_code(int res)
+{
+	if (res == WPA_INVALID_GROUP)
+		return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+	if (res == WPA_INVALID_PAIRWISE)
+		return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+	if (res == WPA_INVALID_AKMP)
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	if (res == WPA_ALLOC_FAIL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+#ifdef CONFIG_IEEE80211W
+	if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+	if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+#endif /* CONFIG_IEEE80211W */
+	if (res == WPA_INVALID_MDIE)
+		return WLAN_STATUS_INVALID_MDIE;
+	if (res != WPA_IE_OK)
+		return WLAN_STATUS_INVALID_IE;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+#ifdef CONFIG_FILS
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct rsn_pmksa_cache_entry *pmksa,
+				    struct wpabuf *erp_resp,
+				    const u8 *msk, size_t msk_len);
+
+static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+			    const struct ieee80211_mgmt *mgmt, size_t len,
+			    u16 auth_transaction, u16 status_code)
+{
+	u16 resp = WLAN_STATUS_SUCCESS;
+	const u8 *pos, *end;
+	struct ieee802_11_elems elems;
+	int res;
+	struct wpa_ie_data rsn;
+	struct rsn_pmksa_cache_entry *pmksa = NULL;
+
+	if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+		return;
+
+	pos = mgmt->u.auth.variable;
+	end = ((const u8 *) mgmt) + len;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+		    pos, end - pos);
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
+	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	/* RSNE */
+	wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
+		    elems.rsn_ie, elems.rsn_ie_len);
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (!sta->wpa_sm)
+		sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
+						NULL);
+	if (!sta->wpa_sm) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to initialize RSN state machine");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				  elems.mdie, elems.mdie_len);
+	resp = wpa_res_to_status_code(res);
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	/* TODO: MDE when using FILS+FT */
+	/* TODO: FTE when using FILS+FT */
+
+	if (!elems.fils_nonce) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
+		    FILS_NONCE_LEN);
+	os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+	/* PMKID List */
+	if (rsn.pmkid && rsn.num_pmkid > 0) {
+		u8 num;
+		const u8 *pmkid;
+
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+			    rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+		pmkid = rsn.pmkid;
+		num = rsn.num_pmkid;
+		while (num) {
+			wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
+			pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
+						   pmkid);
+			if (pmksa)
+				break;
+			pmkid += PMKID_LEN;
+			num--;
+		}
+	}
+	if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
+			   wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
+		pmksa = NULL;
+	}
+	if (pmksa)
+		wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
+
+	/* FILS Session */
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+		    FILS_SESSION_LEN);
+	os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
+
+	/* FILS Wrapped Data */
+	if (elems.fils_wrapped_data) {
+		wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+			    elems.fils_wrapped_data,
+			    elems.fils_wrapped_data_len);
+		if (!pmksa) {
+#ifndef CONFIG_NO_RADIUS
+			if (!sta->eapol_sm) {
+				sta->eapol_sm =
+					ieee802_1x_alloc_eapol_sm(hapd, sta);
+			}
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Forward EAP-Identity/Re-auth Start to authentication server");
+			ieee802_1x_encapsulate_radius(
+				hapd, sta, elems.fils_wrapped_data,
+				elems.fils_wrapped_data_len);
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Will send Authentication frame once the response from authentication server is available");
+			sta->flags |= WLAN_STA_PENDING_FILS_ERP;
+			return;
+#else /* CONFIG_NO_RADIUS */
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+#endif /* CONFIG_NO_RADIUS */
+		}
+	}
+
+fail:
+	handle_auth_fils_finish(hapd, sta, resp, pmksa, NULL, NULL, 0);
+}
+
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct rsn_pmksa_cache_entry *pmksa,
+				    struct wpabuf *erp_resp,
+				    const u8 *msk, size_t msk_len)
+{
+	u8 fils_nonce[FILS_NONCE_LEN];
+	size_t ielen;
+	struct wpabuf *data = NULL;
+	const u8 *ie;
+	u8 *ie_buf = NULL;
+	const u8 *pmk = NULL;
+	size_t pmk_len = 0;
+
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+	if (!ie) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	if (pmksa) {
+		/* Add PMKID of the selected PMKSA into RSNE */
+		ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
+		if (!ie_buf) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		os_memcpy(ie_buf, ie, ielen);
+		if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		ie = ie_buf;
+	}
+
+	if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
+		    fils_nonce, FILS_NONCE_LEN);
+
+	data = wpabuf_alloc(1000 + ielen);
+	if (!data) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	/* RSNE */
+	wpabuf_put_data(data, ie, ielen);
+
+	/* TODO: MDE when using FILS+FT */
+	/* TODO: FTE when using FILS+FT */
+
+	/* FILS Nonce */
+	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
+	wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
+
+	/* FILS Session */
+	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
+
+	/* FILS Wrapped Data */
+	if (!pmksa && erp_resp) {
+		wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+		wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
+		/* Element ID Extension */
+		wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA);
+		wpabuf_put_buf(data, erp_resp);
+
+		pmk = msk;
+		pmk_len = msk_len > PMK_LEN ? PMK_LEN : msk_len;
+	} else if (pmksa) {
+		pmk = pmksa->pmk;
+		pmk_len = pmksa->pmk_len;
+	}
+
+	if (!pmk) {
+		wpa_printf(MSG_DEBUG, "FILS: No PMK available");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		wpabuf_free(data);
+		data = NULL;
+		goto fail;
+	}
+
+	if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
+				 sta->fils_snonce, fils_nonce) < 0) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		wpabuf_free(data);
+		data = NULL;
+		goto fail;
+	}
+
+fail:
+	send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2,
+			resp,
+			data ? wpabuf_head(data) : (u8 *) "",
+			data ? wpabuf_len(data) : 0);
+	wpabuf_free(data);
+
+	if (resp == WLAN_STATUS_SUCCESS) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication OK (FILS)");
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		mlme_authenticate_indication(hapd, sta);
+	}
+
+	os_free(ie_buf);
+}
+
+
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+				 struct sta_info *sta, int success,
+				 struct wpabuf *erp_resp,
+				 const u8 *msk, size_t msk_len)
+{
+	sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
+	handle_auth_fils_finish(hapd, sta, success ? WLAN_STATUS_SUCCESS :
+				WLAN_STATUS_UNSPECIFIED_FAILURE, NULL,
+				erp_resp, msk, msk_len);
+}
+
+#endif /* CONFIG_FILS */
+
+
 static void handle_auth(struct hostapd_data *hapd,
 			const struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -1053,14 +1365,18 @@
 
 	if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
 	       auth_alg == WLAN_AUTH_OPEN) ||
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	      (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
 	       auth_alg == WLAN_AUTH_FT) ||
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
 	      (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
 	       auth_alg == WLAN_AUTH_SAE) ||
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+	       auth_alg == WLAN_AUTH_FILS_SK) ||
+#endif /* CONFIG_FILS */
 	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
 	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
 		wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
@@ -1162,6 +1478,7 @@
 
 	sta = ap_get_sta(hapd, mgmt->sa);
 	if (sta) {
+		sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
 		if ((fc & WLAN_FC_RETRY) &&
 		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
 		    sta->last_seq_ctrl == seq_ctrl &&
@@ -1316,7 +1633,7 @@
 		}
 		break;
 #endif /* CONFIG_NO_RC4 */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	case WLAN_AUTH_FT:
 		sta->auth_alg = WLAN_AUTH_FT;
 		if (sta->wpa_sm == NULL)
@@ -1335,7 +1652,7 @@
 				    handle_auth_ft_finish, hapd);
 		/* handle_auth_ft_finish() callback will complete auth. */
 		return;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
 	case WLAN_AUTH_SAE:
 #ifdef CONFIG_MESH
@@ -1357,6 +1674,12 @@
 				status_code);
 		return;
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	case WLAN_AUTH_FILS_SK:
+		handle_auth_fils(hapd, sta, mgmt, len, auth_transaction,
+				 status_code);
+		return;
+#endif /* CONFIG_FILS */
 	}
 
  fail:
@@ -1645,24 +1968,7 @@
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  wpa_ie, wpa_ie_len,
 					  elems.mdie, elems.mdie_len);
-		if (res == WPA_INVALID_GROUP)
-			resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
-		else if (res == WPA_INVALID_PAIRWISE)
-			resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
-		else if (res == WPA_INVALID_AKMP)
-			resp = WLAN_STATUS_AKMP_NOT_VALID;
-		else if (res == WPA_ALLOC_FAIL)
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-#ifdef CONFIG_IEEE80211W
-		else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
-			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
-		else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
-			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
-#endif /* CONFIG_IEEE80211W */
-		else if (res == WPA_INVALID_MDIE)
-			resp = WLAN_STATUS_INVALID_MDIE;
-		else if (res != WPA_IE_OK)
-			resp = WLAN_STATUS_INVALID_IE;
+		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 #ifdef CONFIG_IEEE80211W
@@ -1690,7 +1996,7 @@
 			sta->flags &= ~WLAN_STA_MFP;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			if (!reassoc) {
 				wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
@@ -1705,7 +2011,7 @@
 			if (resp != WLAN_STATUS_SUCCESS)
 				return resp;
 		}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_SAE
 		if (wpa_auth_uses_sae(sta->wpa_sm) &&
@@ -1923,7 +2229,7 @@
 	/* Extended supported rates */
 	p = hostapd_eid_ext_supp_rates(hapd, p);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (status_code == WLAN_STATUS_SUCCESS) {
 		/* IEEE 802.11r: Mobility Domain Information, Fast BSS
 		 * Transition Information, RSN, [RIC Response] */
@@ -1931,7 +2237,7 @@
 						buf + sizeof(buf) - p,
 						sta->auth_alg, ies, ies_len);
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_IEEE80211W
 	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
@@ -2041,6 +2347,31 @@
 
 	send_len += p - reply->u.assoc_resp.variable;
 
+#ifdef CONFIG_FILS
+	if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    status_code == WLAN_STATUS_SUCCESS) {
+		struct ieee802_11_elems elems;
+
+		if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+		    ParseFailed || !elems.fils_session)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+		/* FILS Session */
+		*p++ = WLAN_EID_EXTENSION; /* Element ID */
+		*p++ = 1 + FILS_SESSION_LEN; /* Length */
+		*p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+		os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
+		send_len += 2 + 1 + FILS_SESSION_LEN;
+
+		send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
+					      sizeof(buf));
+		if (send_len < 0)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_FILS */
+
 	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
 		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
 			   strerror(errno));
@@ -2060,6 +2391,7 @@
 	const u8 *pos;
 	int left, i;
 	struct sta_info *sta;
+	u8 *tmp = NULL;
 
 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
 				      sizeof(mgmt->u.assoc_req))) {
@@ -2117,7 +2449,7 @@
 	}
 
 	sta = ap_get_sta(hapd, mgmt->sa);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (sta && sta->auth_alg == WLAN_AUTH_FT &&
 	    (sta->flags & WLAN_STA_AUTH) == 0) {
 		wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
@@ -2130,7 +2462,7 @@
 		 */
 		sta->flags |= WLAN_STA_AUTH;
 	} else
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "Station tried to "
@@ -2185,6 +2517,30 @@
 	 */
 	sta->capability = capab_info;
 
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		/* The end of the payload is encrypted. Need to decrypt it
+		 * before parsing. */
+
+		tmp = os_malloc(left);
+		if (!tmp) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		os_memcpy(tmp, pos, left);
+
+		left = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+					  len, tmp, left);
+		if (left < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		pos = tmp;
+	}
+#endif /* CONFIG_FILS */
+
 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
 	 * is used */
 	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -2294,6 +2650,7 @@
 		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
 
 	reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+	os_free(tmp);
 
 	/*
 	 * Remove the station in case tranmission of a success response fails
@@ -2506,14 +2863,14 @@
 	}
 
 	switch (mgmt->u.action.category) {
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	case WLAN_ACTION_FT:
 		if (!sta ||
 		    wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
 				     len - IEEE80211_HDRLEN))
 			break;
 		return 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	case WLAN_ACTION_WMM:
 		hostapd_wmm_action(hapd, mgmt, len);
 		return 1;
@@ -2672,7 +3029,7 @@
 	}
 
 	if (hapd->iconf->track_sta_max_num)
-		sta_track_add(hapd->iface, mgmt->sa);
+		sta_track_add(hapd->iface, mgmt->sa, fi->ssi_signal);
 
 	switch (stype) {
 	case WLAN_FC_STYPE_AUTH:
@@ -2846,11 +3203,15 @@
 		new_assoc = 0;
 	sta->flags |= WLAN_STA_ASSOC;
 	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
-	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+	     !hapd->conf->osen) ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK ||
 	    sta->auth_alg == WLAN_AUTH_FT) {
 		/*
-		 * Open, static WEP, or FT protocol; no separate authorization
-		 * step.
+		 * Open, static WEP, FT protocol, or FILS; no separate
+		 * authorization step.
 		 */
 		ap_sta_set_authorized(hapd, sta, 1);
 	}
@@ -2897,6 +3258,18 @@
 	hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
 	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
 
+#ifdef CONFIG_FILS
+	if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    fils_set_tk(sta->wpa_sm) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_UNSPECIFIED);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	if (sta->pending_eapol_rx) {
 		struct os_reltime now, age;
 
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 0327dec..46c92b7 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -135,4 +135,10 @@
 				 const u8 *supp_op_classes,
 				 size_t supp_op_classes_len);
 
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+				 struct sta_info *sta, int success,
+				 struct wpabuf *erp_resp,
+				 const u8 *msk, size_t msk_len);
+
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 259413b..97b1d67 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -218,11 +218,19 @@
 		if (hapd->conf->ssid.utf8_ssid)
 			*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
 		break;
+	case 7: /* Bits 56-63 */
+		break;
 	case 8: /* Bits 64-71 */
 		if (hapd->conf->ftm_responder)
 			*pos |= 0x40; /* Bit 70 - FTM responder */
 		if (hapd->conf->ftm_initiator)
 			*pos |= 0x80; /* Bit 71 - FTM initiator */
+	case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+		if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
+		    wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+			*pos |= 0x01;
+#endif /* CONFIG_FILS */
 		break;
 	}
 }
@@ -258,6 +266,11 @@
 	if (hapd->conf->mbo_enabled && len < 6)
 		len = 6;
 #endif /* CONFIG_MBO */
+#ifdef CONFIG_FILS
+	if ((!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	     !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
+		len = 10;
+#endif /* CONFIG_FILS */
 	if (len < hapd->iface->extended_capa_len)
 		len = hapd->iface->extended_capa_len;
 	if (len == 0)
@@ -584,3 +597,56 @@
 	os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
 		  supp_op_classes_len);
 }
+
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_FILS
+	u8 *len;
+	u16 fils_info = 0;
+
+	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	    !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+		return pos;
+
+	*pos++ = WLAN_EID_FILS_INDICATION;
+	len = pos++;
+	/* TODO: B0..B2: Number of Public Key Identifiers */
+	if (hapd->conf->erp_domain) {
+		/* TODO: Support for setting multiple domain identifiers */
+		/* B3..B5: Number of Realm Identifiers */
+		fils_info |= BIT(3);
+	}
+	/* TODO: B6: FILS IP Address Configuration */
+	if (hapd->conf->fils_cache_id_set)
+		fils_info |= BIT(7);
+	if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
+		fils_info |= BIT(8); /* HESSID Included */
+	/* FILS Shared Key Authentication without PFS Supported */
+	fils_info |= BIT(9);
+	/* TODO: B10: FILS Shared Key Authentication with PFS Supported */
+	/* TODO: B11: FILS Public Key Authentication Supported */
+	/* B12..B15: Reserved */
+	WPA_PUT_LE16(pos, fils_info);
+	pos += 2;
+	if (hapd->conf->fils_cache_id_set) {
+		os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
+		pos += FILS_CACHE_ID_LEN;
+	}
+	if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
+		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+	if (hapd->conf->erp_domain) {
+		u16 hash;
+
+		hash = fils_domain_name_hash(hapd->conf->erp_domain);
+		WPA_PUT_LE16(pos, hash);
+		pos += 2;
+	}
+	*len = pos - len - 1;
+#endif /* CONFIG_FILS */
+
+	return pos;
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 80ff996..7ac337d 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -31,6 +31,8 @@
 #include "ap_drv_ops.h"
 #include "wps_hostapd.h"
 #include "hs20.h"
+/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
+#include "ieee802_11.h"
 #include "ieee802_1x.h"
 
 
@@ -316,6 +318,7 @@
 	     hdr->code != EAP_CODE_INITIATE))
 		return;
 
+	eap_erp_update_identity(sm->eap, eap, len);
 	identity = eap_get_identity(sm->eap, &identity_len);
 	if (identity == NULL)
 		return;
@@ -472,7 +475,7 @@
 		}
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
 	    sta->wpa_sm &&
 	    (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
@@ -485,7 +488,7 @@
 		wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
 		return -1;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
 	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
@@ -588,9 +591,9 @@
 }
 
 
-static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
-					  struct sta_info *sta,
-					  const u8 *eap, size_t len)
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+				   struct sta_info *sta,
+				   const u8 *eap, size_t len)
 {
 	struct radius_msg *msg;
 	struct eapol_state_machine *sm = sta->eapol_sm;
@@ -845,7 +848,7 @@
 }
 
 
-static struct eapol_state_machine *
+struct eapol_state_machine *
 ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int flags = 0;
@@ -1154,7 +1157,7 @@
 
 	sta->eapol_sm->eap_if->portEnabled = TRUE;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (sta->auth_alg == WLAN_AUTH_FT) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -1173,7 +1176,29 @@
 		/* TODO: get vlan_id from R0KH using RRB message */
 		return;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from FILS - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing FILS information. */
+		sta->eapol_sm->keyRun = TRUE;
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = TRUE;
+		sta->eapol_sm->authFail = FALSE;
+		sta->eapol_sm->portValid = TRUE;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		return;
+	}
+#endif /* CONFIG_FILS */
 
 	pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 	if (pmksa) {
@@ -1837,6 +1862,19 @@
 	if (override_eapReq)
 		sm->eap_if->aaaEapReq = FALSE;
 
+#ifdef CONFIG_FILS
+#ifdef NEED_AP_MLME
+	if (sta->flags & WLAN_STA_PENDING_FILS_ERP) {
+		/* TODO: Add a PMKSA entry on success? */
+		ieee802_11_finish_fils_auth(
+			hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
+			sm->eap_if->aaaEapReqData,
+			sm->eap_if->aaaEapKeyData,
+			sm->eap_if->aaaEapKeyDataLen);
+	}
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_FILS */
+
 	eapol_auth_step(sm);
 
 	return RADIUS_RX_QUEUED;
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index ec80199..9594661 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -57,5 +57,10 @@
 			   struct hostapd_radius_attr *req_attr,
 			   struct sta_info *sta,
 			   struct radius_msg *msg);
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+				   struct sta_info *sta,
+				   const u8 *eap, size_t len);
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
 
 #endif /* IEEE802_1X_H */
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index a2efff6..b8fd592 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -43,6 +43,7 @@
 	nr->civic = NULL;
 	os_memset(nr->bssid, 0, sizeof(nr->bssid));
 	os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+	nr->stationary = 0;
 }
 
 
@@ -64,7 +65,7 @@
 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
 			 const struct wpa_ssid_value *ssid,
 			 const struct wpabuf *nr, const struct wpabuf *lci,
-			 const struct wpabuf *civic)
+			 const struct wpabuf *civic, int stationary)
 {
 	struct hostapd_neighbor_entry *entry;
 
@@ -83,18 +84,20 @@
 	if (!entry->nr)
 		goto fail;
 
-	if (lci) {
+	if (lci && wpabuf_len(lci)) {
 		entry->lci = wpabuf_dup(lci);
 		if (!entry->lci || os_get_time(&entry->lci_date))
 			goto fail;
 	}
 
-	if (civic) {
+	if (civic && wpabuf_len(civic)) {
 		entry->civic = wpabuf_dup(civic);
 		if (!entry->civic)
 			goto fail;
 	}
 
+	entry->stationary = stationary;
+
 	return 0;
 
 fail:
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index c22e043..ba46d88 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -16,7 +16,7 @@
 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
 			 const struct wpa_ssid_value *ssid,
 			 const struct wpabuf *nr, const struct wpabuf *lci,
-			 const struct wpabuf *civic);
+			 const struct wpabuf *civic, int stationary);
 int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
 			    const struct wpa_ssid_value *ssid);
 void hostpad_free_neighbor_db(struct hostapd_data *hapd);
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index 3569f95..5ca87c0 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -129,12 +129,12 @@
 	struct os_time curr, diff;
 	unsigned long diff_l;
 
+	if (nr->stationary || max_age == 0xffff)
+		return 1;
+
 	if (!max_age)
 		return 0;
 
-	if (max_age == 0xffff)
-		return 1;
-
 	if (os_get_time(&curr))
 		return 0;
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 099de62..a416337 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -17,6 +17,7 @@
 
 #include "list.h"
 #include "vlan.h"
+#include "common/ieee802_11_defs.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -38,6 +39,7 @@
 #define WLAN_STA_WNM_SLEEP_MODE BIT(19)
 #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
 #define WLAN_STA_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_FILS_ERP BIT(22)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -218,6 +220,11 @@
 	struct wpabuf *probe_ie_taxonomy;
 	struct wpabuf *assoc_ie_taxonomy;
 #endif /* CONFIG_TAXONOMY */
+
+#ifdef CONFIG_FILS
+	u8 fils_snonce[FILS_NONCE_LEN];
+	u8 fils_session[FILS_SESSION_LEN];
+#endif /* CONFIG_FILS */
 };
 
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3587086..43e3558 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -13,7 +13,9 @@
 #include "utils/state_machine.h"
 #include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
 #include "crypto/aes_wrap.h"
+#include "crypto/aes_siv.h"
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
@@ -35,6 +37,10 @@
 static int wpa_sm_step(struct wpa_state_machine *sm);
 static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
 			      size_t data_len);
+#ifdef CONFIG_FILS
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			    u8 *buf, size_t buf_len, u16 *_key_data_len);
+#endif /* CONFIG_FILS */
 static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
 static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
 			      struct wpa_group *group);
@@ -52,6 +58,7 @@
 			  struct wpa_group *group);
 static void wpa_group_put(struct wpa_authenticator *wpa_auth,
 			  struct wpa_group *group);
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
 
 static const u32 dot11RSNAConfigGroupUpdateCount = 4;
 static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
@@ -232,10 +239,10 @@
 static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
 {
 	int ret = 0;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		ret = 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
 		ret = 1;
@@ -443,7 +450,7 @@
 		return NULL;
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
 	if (wpa_auth->ft_pmk_cache == NULL) {
 		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
@@ -453,7 +460,7 @@
 		os_free(wpa_auth);
 		return NULL;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	if (wpa_auth->conf.wpa_gmk_rekey) {
 		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
@@ -513,10 +520,10 @@
 
 	pmksa_cache_auth_deinit(wpa_auth->pmksa);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_P2P
 	bitfield_free(wpa_auth->ip_pool);
@@ -599,7 +606,7 @@
 	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 		return -1;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (sm->ft_completed) {
 		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 				"FT authentication already completed - do not "
@@ -608,7 +615,17 @@
 		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
 		return 0;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sm->fils_completed) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"FILS authentication already completed - do not start 4-way handshake");
+		/* Go to PTKINITDONE state to allow GTK rekeying */
+		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+		return 0;
+	}
+#endif /* CONFIG_FILS */
 
 	if (sm->started) {
 		os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
@@ -660,10 +677,10 @@
 		sm->group->GKeyDoneStations--;
 		sm->GUpdateStationKeys = FALSE;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	os_free(sm->assoc_resp_ftie);
 	wpabuf_free(sm->ft_pending_req_ies);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	os_free(sm->last_rx_eapol_key);
 	os_free(sm->wpa_ie);
 	wpa_group_put(sm->wpa_auth, sm->group);
@@ -739,7 +756,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
 			       struct wpa_state_machine *sm,
 			       struct wpa_eapol_ie_parse *kde)
@@ -786,7 +803,7 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
@@ -830,6 +847,7 @@
 	const u8 *pmk = NULL;
 	unsigned int pmk_len;
 
+	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
 			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
@@ -877,39 +895,42 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info, key_data_length;
 	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
 	       SMK_M1, SMK_M3, SMK_ERROR } msg;
 	char *msgtxt;
 	struct wpa_eapol_ie_parse kde;
-	int ft;
-	const u8 *eapol_key_ie, *key_data;
-	size_t eapol_key_ie_len, keyhdrlen, mic_len;
+	const u8 *key_data;
+	size_t keyhdrlen, mic_len;
+	u8 *mic;
 
 	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 		return;
+	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
 
 	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
-	if (data_len < sizeof(*hdr) + keyhdrlen)
+	if (data_len < sizeof(*hdr) + keyhdrlen) {
+		wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
 		return;
+	}
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	mic = (u8 *) (key + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	if (mic_len == 24) {
-		key_data = (const u8 *) (key192 + 1);
-		key_data_length = WPA_GET_BE16(key192->key_data_length);
-	} else {
-		key_data = (const u8 *) (key + 1);
-		key_data_length = WPA_GET_BE16(key->key_data_length);
-	}
+	key_data = mic + mic_len + 2;
+	key_data_length = WPA_GET_BE16(mic + mic_len);
 	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
-		   " key_info=0x%x type=%u key_data_length=%u",
-		   MAC2STR(sm->addr), key_info, key->type, key_data_length);
+		   " key_info=0x%x type=%u mic_len=%u key_data_length=%u",
+		   MAC2STR(sm->addr), key_info, key->type,
+		   (unsigned int) mic_len, key_data_length);
+	wpa_hexdump(MSG_MSGDUMP,
+		    "WPA: EAPOL-Key header (ending before Key MIC)",
+		    key, sizeof(*key));
+	wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
+		    mic, mic_len);
 	if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
 		wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
 			   "key_data overflow (%d > %lu)",
@@ -968,7 +989,9 @@
 	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
 		msg = GROUP_2;
 		msgtxt = "2/2 Group";
-	} else if (key_data_length == 0) {
+	} else if (key_data_length == 0 ||
+		   (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+		    key_data_length == AES_BLOCK_SIZE)) {
 		msg = PAIRWISE_4;
 		msgtxt = "4/4 Pairwise";
 	} else {
@@ -985,6 +1008,7 @@
 			if (wpa_use_aes_cmac(sm) &&
 			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
 			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -995,6 +1019,7 @@
 			}
 
 			if (!wpa_use_aes_cmac(sm) &&
+			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -1004,7 +1029,8 @@
 			}
 		}
 
-		if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+		if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		     wpa_key_mgmt_fils(sm->wpa_key_mgmt)) &&
 		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
 					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1092,6 +1118,15 @@
 	}
 
 continue_processing:
+#ifdef CONFIG_FILS
+	if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
+	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	switch (msg) {
 	case PAIRWISE_2:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
@@ -1122,67 +1157,6 @@
 			wpa_sta_disconnect(wpa_auth, sm->addr);
 			return;
 		}
-		if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
-					 "received EAPOL-Key msg 2/4 with "
-					 "invalid Key Data contents");
-			return;
-		}
-		if (kde.rsn_ie) {
-			eapol_key_ie = kde.rsn_ie;
-			eapol_key_ie_len = kde.rsn_ie_len;
-		} else if (kde.osen) {
-			eapol_key_ie = kde.osen;
-			eapol_key_ie_len = kde.osen_len;
-		} else {
-			eapol_key_ie = kde.wpa_ie;
-			eapol_key_ie_len = kde.wpa_ie_len;
-		}
-		ft = sm->wpa == WPA_VERSION_WPA2 &&
-			wpa_key_mgmt_ft(sm->wpa_key_mgmt);
-		if (sm->wpa_ie == NULL ||
-		    wpa_compare_rsn_ie(ft,
-				       sm->wpa_ie, sm->wpa_ie_len,
-				       eapol_key_ie, eapol_key_ie_len)) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					"WPA IE from (Re)AssocReq did not "
-					"match with msg 2/4");
-			if (sm->wpa_ie) {
-				wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
-					    sm->wpa_ie, sm->wpa_ie_len);
-			}
-			wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
-				    eapol_key_ie, eapol_key_ie_len);
-			/* MLME-DEAUTHENTICATE.request */
-			wpa_sta_disconnect(wpa_auth, sm->addr);
-			return;
-		}
-#ifdef CONFIG_IEEE80211R
-		if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
-			wpa_sta_disconnect(wpa_auth, sm->addr);
-			return;
-		}
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_P2P
-		if (kde.ip_addr_req && kde.ip_addr_req[0] &&
-		    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
-			int idx;
-			wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
-				   "EAPOL-Key exchange");
-			idx = bitfield_get_first_zero(wpa_auth->ip_pool);
-			if (idx >= 0) {
-				u32 start = WPA_GET_BE32(wpa_auth->conf.
-							 ip_addr_start);
-				bitfield_set(wpa_auth->ip_pool, idx);
-				WPA_PUT_BE32(sm->ip_addr, start + idx);
-				wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
-					   "address %u.%u.%u.%u to " MACSTR,
-					   sm->ip_addr[0], sm->ip_addr[1],
-					   sm->ip_addr[2], sm->ip_addr[3],
-					   MAC2STR(sm->addr));
-			}
-		}
-#endif /* CONFIG_P2P */
 		break;
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1239,15 +1213,26 @@
 		return;
 	}
 
-	if (!(key_info & WPA_KEY_INFO_MIC)) {
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    !(key_info & WPA_KEY_INFO_MIC)) {
 		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 				"received invalid EAPOL-Key: Key MIC not set");
 		return;
 	}
 
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (key_info & WPA_KEY_INFO_MIC)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"received invalid EAPOL-Key: Key MIC set");
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	sm->MICVerified = FALSE;
 	if (sm->PTK_valid && !sm->update_snonce) {
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
 				       data_len) &&
 		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
 		     wpa_try_alt_snonce(sm, data, data_len))) {
@@ -1255,6 +1240,15 @@
 					"received EAPOL-Key with invalid MIC");
 			return;
 		}
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
+				     &key_data_length) < 0) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key with invalid MIC");
+			return;
+		}
+#endif /* CONFIG_FILS */
 		sm->MICVerified = TRUE;
 		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
 		sm->pending_1_of_4_timeout = 0;
@@ -1412,24 +1406,24 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	size_t len, mic_len, keyhdrlen;
 	int alg;
 	int key_data_len, pad_len = 0;
 	u8 *buf, *pos;
 	int version, pairwise;
 	int i;
-	u8 *key_data;
+	u8 *key_mic, *key_data;
 
 	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
 
 	if (force_version)
 		version = force_version;
 	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
-		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		 wpa_key_mgmt_fils(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
 	else if (wpa_use_aes_cmac(sm))
 		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
@@ -1463,6 +1457,8 @@
 	}
 
 	len += key_data_len;
+	if (!mic_len && encr)
+		len += AES_BLOCK_SIZE;
 
 	hdr = os_zalloc(len);
 	if (hdr == NULL)
@@ -1471,7 +1467,7 @@
 	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
 	hdr->length = host_to_be16(len  - sizeof(*hdr));
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_mic = (u8 *) (key + 1);
 	key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
 
 	key->type = sm->wpa == WPA_VERSION_WPA2 ?
@@ -1510,10 +1506,31 @@
 
 	if (kde && !encr) {
 		os_memcpy(key_data, kde, kde_len);
-		if (mic_len == 24)
-			WPA_PUT_BE16(key192->key_data_length, kde_len);
-		else
-			WPA_PUT_BE16(key->key_data_length, kde_len);
+		WPA_PUT_BE16(key_mic + mic_len, kde_len);
+#ifdef CONFIG_FILS
+	} else if (!mic_len) {
+		const u8 *aad[1];
+		size_t aad_len[1];
+
+		WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
+		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+				kde, kde_len);
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
+				sm->PTK.kek, sm->PTK.kek_len);
+		/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+		 * to Key Data (exclusive). */
+		aad[0] = (u8 *) hdr;
+		aad_len[0] = key_mic + 2 - (u8 *) hdr;
+		if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
+				    1, aad, aad_len, key_mic + 2) < 0) {
+			wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+			return;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+			    key_mic + 2, AES_BLOCK_SIZE + kde_len);
+#endif /* CONFIG_FILS */
 	} else if (encr && kde) {
 		buf = os_zalloc(key_data_len);
 		if (buf == NULL) {
@@ -1539,12 +1556,7 @@
 				os_free(buf);
 				return;
 			}
-			if (mic_len == 24)
-				WPA_PUT_BE16(key192->key_data_length,
-					     key_data_len);
-			else
-				WPA_PUT_BE16(key->key_data_length,
-					     key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
 #ifndef CONFIG_NO_RC4
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
@@ -1555,12 +1567,7 @@
 			os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
 			os_memcpy(key_data, buf, key_data_len);
 			rc4_skip(ek, 32, 256, key_data, key_data_len);
-			if (mic_len == 24)
-				WPA_PUT_BE16(key192->key_data_length,
-					     key_data_len);
-			else
-				WPA_PUT_BE16(key->key_data_length,
-					     key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
 #endif /* CONFIG_NO_RC4 */
 		} else {
 			os_free(hdr);
@@ -1571,9 +1578,7 @@
 	}
 
 	if (key_info & WPA_KEY_INFO_MIC) {
-		u8 *key_mic;
-
-		if (!sm->PTK_valid) {
+		if (!sm->PTK_valid || !mic_len) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 					"PTK not valid when sending EAPOL-Key "
 					"frame");
@@ -1581,7 +1586,6 @@
 			return;
 		}
 
-		key_mic = key192->key_mic; /* same offset for key and key192 */
 		wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
 				  sm->wpa_key_mgmt, version,
 				  (u8 *) hdr, len, key_mic);
@@ -1641,10 +1645,9 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info;
 	int ret = 0;
-	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
 	size_t mic_len = wpa_mic_len(akmp);
 
 	if (data_len < sizeof(*hdr) + sizeof(*key))
@@ -1652,16 +1655,16 @@
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	mic_pos = (u8 *) (key + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	os_memcpy(mic, key192->key_mic, mic_len);
-	os_memset(key192->key_mic, 0, mic_len);
+	os_memcpy(mic, mic_pos, mic_len);
+	os_memset(mic_pos, 0, mic_len);
 	if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
 			      key_info & WPA_KEY_INFO_TYPE_MASK,
-			      data, data_len, key192->key_mic) ||
-	    os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
+			      data, data_len, mic_pos) ||
+	    os_memcmp_const(mic, mic_pos, mic_len) != 0)
 		ret = -1;
-	os_memcpy(key192->key_mic, mic, mic_len);
+	os_memcpy(mic_pos, mic, mic_len);
 	return ret;
 }
 
@@ -1670,7 +1673,10 @@
 {
 	sm->PTK_valid = FALSE;
 	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
-	wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
+	if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
+			     0))
+		wpa_printf(MSG_DEBUG,
+			   "RSN: PTK removal from the driver failed");
 	sm->pairwise_set = FALSE;
 	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 }
@@ -1734,7 +1740,7 @@
 		sm->ReAuthenticationRequest = TRUE;
 		break;
 	case WPA_ASSOC_FT:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
 			   "after association");
 		wpa_ft_install_ptk(sm);
@@ -1742,19 +1748,24 @@
 		/* Using FT protocol, not WPA auth state machine */
 		sm->ft_completed = 1;
 		return 0;
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP */
 		break;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	sm->ft_completed = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_IEEE80211W
 	if (sm->mgmt_frame_prot && event == WPA_AUTH)
 		remove_ptk = 0;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (event == WPA_AUTH || event == WPA_ASSOC))
+		remove_ptk = 0;
+#endif /* CONFIG_FILS */
 
 	if (remove_ptk) {
 		sm->PTK_valid = FALSE;
@@ -1904,9 +1915,9 @@
 	size_t len = 2 * PMK_LEN;
 
 	SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	sm->xxkey_len = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (sm->pmksa) {
 		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
@@ -1930,12 +1941,12 @@
 		}
 		os_memcpy(sm->PMK, msk, pmk_len);
 		sm->pmk_len = pmk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		if (len >= 2 * PMK_LEN) {
 			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
 			sm->xxkey_len = PMK_LEN;
 		}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
 			   sm->wpa_auth->cb.get_msk);
@@ -1965,10 +1976,10 @@
 	if (psk) {
 		os_memcpy(sm->PMK, psk, PMK_LEN);
 		sm->pmk_len = PMK_LEN;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	}
 	sm->req_replay_counter_used = 0;
 }
@@ -2031,10 +2042,10 @@
 			  const u8 *pmk, unsigned int pmk_len,
 			  struct wpa_ptk *ptk)
 {
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
 			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
@@ -2042,16 +2053,398 @@
 }
 
 
+#ifdef CONFIG_FILS
+
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce)
+{
+	u8 ick[FILS_ICK_MAX_LEN];
+	size_t ick_len;
+	int res;
+
+	res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
+			      snonce, anonce, &sm->PTK, ick, &ick_len,
+			      sm->wpa_key_mgmt, sm->pairwise);
+	if (res < 0)
+		return res;
+	sm->PTK_valid = TRUE;
+
+	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
+			       sm->addr, sm->wpa_auth->addr,
+			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       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));
+
+	/* Store nonces for (Re)Association Request/Response frame processing */
+	os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+	os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
+	return res;
+}
+
+
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			    u8 *buf, size_t buf_len, u16 *_key_data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 *pos;
+	u16 key_data_len;
+	u8 *tmp;
+	const u8 *aad[1];
+	size_t aad_len[1];
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	pos = (u8 *) (key + 1);
+	key_data_len = WPA_GET_BE16(pos);
+	if (key_data_len < AES_BLOCK_SIZE ||
+	    key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+				"No room for AES-SIV data in the frame");
+		return -1;
+	}
+	pos += 2; /* Pointing at the Encrypted Key Data field */
+
+	tmp = os_malloc(key_data_len);
+	if (!tmp)
+		return -1;
+
+	/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+	 * to Key Data (exclusive). */
+	aad[0] = buf;
+	aad_len[0] = pos - buf;
+	if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
+			    1, aad, aad_len, tmp) < 0) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+				"Invalid AES-SIV data in the frame");
+		bin_clear_free(tmp, key_data_len);
+		return -1;
+	}
+
+	/* AEAD decryption and validation completed successfully */
+	key_data_len -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+			tmp, key_data_len);
+
+	/* Replace Key Data field with the decrypted version */
+	os_memcpy(pos, tmp, key_data_len);
+	pos -= 2; /* Key Data Length field */
+	WPA_PUT_BE16(pos, key_data_len);
+	bin_clear_free(tmp, key_data_len);
+	if (_key_data_len)
+		*_key_data_len = key_data_len;
+	return 0;
+}
+
+
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
+		       u8 *pos, size_t left)
+{
+	u16 fc, stype;
+	const u8 *end, *ie_start, *ie, *session, *crypt;
+	struct ieee802_11_elems elems;
+	const u8 *aad[5];
+	size_t aad_len[5];
+
+	if (!sm || !sm->PTK_valid) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No KEK to decrypt Assocication Request frame");
+		return -1;
+	}
+
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not a FILS AKM - reject association");
+		return -1;
+	}
+
+	end = ((const u8 *) mgmt) + frame_len;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+		ie_start = mgmt->u.reassoc_req.variable;
+	else
+		ie_start = mgmt->u.assoc_req.variable;
+	ie = ie_start;
+
+	/*
+	 * Find FILS Session element which is the last unencrypted element in
+	 * the frame.
+	 */
+	session = NULL;
+	while (ie + 1 < end) {
+		if (ie + 2 + ie[1] > end)
+			break;
+		if (ie[0] == WLAN_EID_EXTENSION &&
+		    ie[1] >= 1 + FILS_SESSION_LEN &&
+		    ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+			session = ie;
+			break;
+		}
+		ie += 2 + ie[1];
+	}
+
+	if (!session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Could not find FILS Session element in Association Request frame - reject");
+		return -1;
+	}
+	if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    fils_session, FILS_SESSION_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+			    session + 3, FILS_SESSION_LEN);
+		return -1;
+	}
+	crypt = session + 2 + session[1];
+
+	if (end - crypt < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Too short frame to include AES-SIV data");
+		return -1;
+	}
+
+	/* AES-SIV AAD vectors */
+
+	/* The STA's MAC address */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The AP's BSSID */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The STA's nonce */
+	aad[2] = sm->SNonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The AP's nonce */
+	aad[3] = sm->ANonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Request frame from the Capability Information
+	 * field to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+	aad_len[4] = crypt - aad[0];
+
+	if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+			    1, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Invalid AES-SIV data in the frame");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+		    pos, left - AES_BLOCK_SIZE);
+
+	if (ieee802_11_parse_elems(pos, left - AES_BLOCK_SIZE, &elems, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to parse decrypted elements");
+		return -1;
+	}
+	if (!elems.fils_key_confirm) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+		return -1;
+	}
+	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Unexpected Key-Auth length %d (expected %d)",
+			   elems.fils_key_confirm_len,
+			   (int) sm->fils_key_auth_len);
+		return -1;
+	}
+	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+		      sm->fils_key_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+			    elems.fils_key_confirm,
+			    elems.fils_key_confirm_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+			    sm->fils_key_auth_sta, sm->fils_key_auth_len);
+		return -1;
+	}
+
+	return left - AES_BLOCK_SIZE;
+}
+
+
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+		       size_t current_len, size_t max_len)
+{
+	u8 *end = buf + max_len;
+	u8 *pos = buf + current_len;
+	struct ieee80211_mgmt *mgmt;
+	struct wpabuf *plain;
+	u8 *len, *tmp, *tmp2;
+	u8 hdr[2];
+	u8 *gtk, dummy_gtk[32];
+	size_t gtk_len;
+	struct wpa_group *gsm;
+	const u8 *aad[5];
+	size_t aad_len[5];
+
+	if (!sm || !sm->PTK_valid)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Association Response frame before FILS processing",
+		    buf, current_len);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	/* AES-SIV AAD vectors */
+
+	/* The AP's BSSID */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The STA's MAC address */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The AP's nonce */
+	aad[2] = sm->ANonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The STA's nonce */
+	aad[3] = sm->SNonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Response frame from the Capability Information
+	 * field (the same offset in both Association and Reassociation
+	 * Response frames) to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+	aad_len[4] = pos - aad[4];
+
+	/* The following elements will be encrypted with AES-SIV */
+
+	plain = wpabuf_alloc(1000);
+	if (!plain)
+		return -1;
+
+	/* TODO: FILS Public Key */
+
+	/* FILS Key Confirmation */
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
+
+	/* TODO: FILS HLP Container */
+
+	/* TODO: FILS IP Address Assignment */
+
+	/* Key Delivery */
+	gsm = sm->group;
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	len = wpabuf_put(plain, 1);
+	wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
+			    wpabuf_put(plain, WPA_KEY_RSC_LEN));
+	/* GTK KDE */
+	gtk = gsm->GTK[gsm->GN - 1];
+	gtk_len = gsm->GTK_len;
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
+			wpabuf_free(plain);
+			return -1;
+		}
+		gtk = dummy_gtk;
+	}
+	hdr[0] = gsm->GN & 0x03;
+	hdr[1] = 0;
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+			   gtk, gtk_len);
+	wpabuf_put(plain, tmp2 - tmp);
+
+	/* IGTK KDE */
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = ieee80211w_kde_add(sm, tmp);
+	wpabuf_put(plain, tmp2 - tmp);
+
+	*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+
+	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not enough room for FILS elements");
+		wpabuf_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+			    plain);
+
+	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);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Encrypted Association Response elements",
+		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+	wpabuf_free(plain);
+
+	sm->fils_completed = 1;
+
+	return current_len;
+}
+
+
+int fils_set_tk(struct wpa_state_machine *sm)
+{
+	enum wpa_alg alg;
+	int klen;
+
+	if (!sm || !sm->PTK_valid)
+		return -1;
+
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+
+	wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+			     sm->PTK.tk, klen)) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_FILS */
+
+
 SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 {
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
 	struct wpa_ptk PTK;
 	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
 	unsigned int pmk_len;
+	int ft;
+	const u8 *eapol_key_ie, *key_data, *mic;
+	u16 key_data_length;
+	size_t mic_len, eapol_key_ie_len;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_ie_parse kde;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
 	sm->update_snonce = FALSE;
+	os_memset(&PTK, 0, sizeof(PTK));
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
 
 	/* WPA with IEEE 802.1X: use the derived PMK from EAP
 	 * WPA-PSK: iterate through possible PSKs and select the one matching
@@ -2071,13 +2464,23 @@
 
 		wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
 
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
 				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
 			ok = 1;
 			break;
 		}
 
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
+				     sm->last_rx_eapol_key_len, NULL) == 0) {
+			ok = 1;
+			break;
+		}
+#endif /* CONFIG_FILS */
+
 		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
 			break;
 	}
@@ -2090,7 +2493,77 @@
 		return;
 	}
 
-#ifdef CONFIG_IEEE80211R
+	/*
+	 * Note: last_rx_eapol_key length fields have already been validated in
+	 * wpa_receive().
+	 */
+	hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
+	key_data_length = WPA_GET_BE16(mic + mic_len);
+	if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+	    sizeof(*key) - mic_len - 2)
+		return;
+
+	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+				 "received EAPOL-Key msg 2/4 with invalid Key Data contents");
+		return;
+	}
+	if (kde.rsn_ie) {
+		eapol_key_ie = kde.rsn_ie;
+		eapol_key_ie_len = kde.rsn_ie_len;
+	} else if (kde.osen) {
+		eapol_key_ie = kde.osen;
+		eapol_key_ie_len = kde.osen_len;
+	} else {
+		eapol_key_ie = kde.wpa_ie;
+		eapol_key_ie_len = kde.wpa_ie_len;
+	}
+	ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+	if (sm->wpa_ie == NULL ||
+	    wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
+			       eapol_key_ie, eapol_key_ie_len)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"WPA IE from (Re)AssocReq did not match with msg 2/4");
+		if (sm->wpa_ie) {
+			wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+				    sm->wpa_ie, sm->wpa_ie_len);
+		}
+		wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+			    eapol_key_ie, eapol_key_ie_len);
+		/* MLME-DEAUTHENTICATE.request */
+		wpa_sta_disconnect(wpa_auth, sm->addr);
+		return;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+		wpa_sta_disconnect(wpa_auth, sm->addr);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+	if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+	    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+		int idx;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: IP address requested in EAPOL-Key exchange");
+		idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+		if (idx >= 0) {
+			u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
+			bitfield_set(wpa_auth->ip_pool, idx);
+			WPA_PUT_BE32(sm->ip_addr, start + idx);
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Assigned IP address %u.%u.%u.%u to "
+				   MACSTR, sm->ip_addr[0], sm->ip_addr[1],
+				   sm->ip_addr[2], sm->ip_addr[3],
+				   MAC2STR(sm->addr));
+		}
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211R_AP
 	if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		/*
 		 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
@@ -2109,7 +2582,7 @@
 			return;
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	sm->pending_1_of_4_timeout = 0;
 	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -2279,12 +2752,12 @@
 	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
 	if (gtk)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
 		kde_len += 300; /* FTIE + 2 * TIE */
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	if (WPA_GET_BE32(sm->ip_addr) > 0)
 		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
@@ -2296,7 +2769,7 @@
 	pos = kde;
 	os_memcpy(pos, wpa_ie, wpa_ie_len);
 	pos += wpa_ie_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		int res;
 		size_t elen;
@@ -2312,7 +2785,7 @@
 		pos -= wpa_ie_len;
 		pos += elen;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (gtk) {
 		u8 hdr[2];
 		hdr[0] = keyidx & 0x03;
@@ -2322,7 +2795,7 @@
 	}
 	pos = ieee80211w_kde_add(sm, pos);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		int res;
 		struct wpa_auth_config *conf;
@@ -2362,7 +2835,7 @@
 		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
 		pos += 4;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	if (WPA_GET_BE32(sm->ip_addr) > 0) {
 		u8 addr[3 * 4];
@@ -2375,7 +2848,8 @@
 #endif /* CONFIG_P2P */
 
 	wpa_send_eapol(sm->wpa_auth, sm,
-		       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
+		       (secure ? WPA_KEY_INFO_SECURE : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
 		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
@@ -2431,9 +2905,9 @@
 			 "pairwise key handshake completed (%s)",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 }
 
 
@@ -2619,7 +3093,8 @@
 	}
 
 	wpa_send_eapol(sm->wpa_auth, sm,
-		       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+		       WPA_KEY_INFO_SECURE |
+		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK |
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
 		       rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
@@ -3376,11 +3851,12 @@
 
 
 struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		   const u8 *pmkid)
 {
 	if (!wpa_auth || !wpa_auth->pmksa)
 		return NULL;
-	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
 }
 
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 0de8d97..743f2e6 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -157,7 +157,7 @@
 	enum mfp_options ieee80211w;
 	int group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
@@ -170,7 +170,8 @@
 	struct ft_remote_r1kh *r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+	int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
 	int disable_gtk;
 	int ap_mlme;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -220,13 +221,13 @@
 						  void *ctx), void *cb_ctx);
 	int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
 			  size_t data_len);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
 	int (*send_ft_action)(void *ctx, const u8 *dst,
 			      const u8 *data, size_t data_len);
 	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
 			 size_t tspec_ielen);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_MESH
 	int (*start_ampe)(void *ctx, const u8 *sta_addr);
 #endif /* CONFIG_MESH */
@@ -302,7 +303,8 @@
 			size_t len);
 void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
 struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		   const u8 *pmkid);
 void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
 			      struct wpa_state_machine *sm,
 			      struct wpa_authenticator *wpa_auth,
@@ -311,7 +313,7 @@
 void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
 				  struct wpa_state_machine *sm, int ack);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 				 size_t max_len, int auth_alg,
 				 const u8 *req_ies, size_t req_ies_len);
@@ -327,7 +329,7 @@
 int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		  const u8 *data, size_t data_len);
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
 void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
@@ -346,5 +348,13 @@
 
 int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce);
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
+		       u8 *pos, size_t left);
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+		       size_t current_len, size_t max_len);
+int fils_set_tk(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 42242a5..637d6d6 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -22,7 +22,7 @@
 #include "wpa_auth_i.h"
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 
 static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     const u8 *current_ap, const u8 *sta_addr,
@@ -51,6 +51,17 @@
 }
 
 
+static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
+				 const u8 *addr, const u8 *p2p_dev_addr,
+				 const u8 *prev_psk)
+{
+	if (wpa_auth->cb.get_psk == NULL)
+		return NULL;
+	return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+				    prev_psk);
+}
+
+
 static struct wpa_state_machine *
 wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
 {
@@ -373,6 +384,7 @@
 	const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
 	const u8 *ssid = sm->wpa_auth->conf.ssid;
 	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+	int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
 
 	if (sm->xxkey_len == 0) {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -384,16 +396,18 @@
 			  r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
-			    sm->pairwise);
+	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+				    sm->pairwise);
 
 	wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
 			  pmk_r1, sm->pmk_r1_name);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
-	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
-			    sm->pairwise);
+	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1,
+				    sm->pmk_r1_name, sm->pairwise);
 
 	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
 				 sm->wpa_auth->addr, sm->pmk_r1_name,
@@ -795,6 +809,89 @@
 }
 
 
+/* Derive PMK-R1 from PSK, check all available PSK */
+static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
+			     const u8 *req_pmk_r1_name,
+			     u8 *out_pmk_r1, int *out_pairwise)
+{
+	const u8 *pmk = NULL;
+	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	const u8 *mdid = wpa_auth->conf.mobility_domain;
+	const u8 *r0kh = sm->r0kh_id;
+	size_t r0kh_len = sm->r0kh_id_len;
+	const u8 *r1kh = wpa_auth->conf.r1_key_holder;
+	const u8 *ssid = wpa_auth->conf.ssid;
+	size_t ssid_len = wpa_auth->conf.ssid_len;
+	int pairwise;
+
+	pairwise = sm->pairwise;
+
+	for (;;) {
+		pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+				     pmk);
+		if (pmk == NULL)
+			break;
+
+		wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
+				  r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
+		wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+				  pmk_r1, pmk_r1_name);
+
+		if (os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0)
+			continue;
+
+		/* We found a PSK that matches the requested pmk_r1_name */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Found PSK to generate PMK-R1 locally");
+		os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
+		if (out_pairwise)
+			*out_pairwise = pairwise;
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "FT: Did not find PSK to generate PMK-R1 locally");
+	return -1;
+}
+
+
+/* Detect the configuration the station asked for.
+ * Required to detect FT-PSK and pairwise cipher.
+ */
+static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
+			       struct wpa_ft_ies *parse)
+{
+	int key_mgmt, ciphers;
+
+	if (sm->wpa_key_mgmt)
+		return 0;
+
+	key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
+	if (!key_mgmt) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
+			   MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
+		return -1;
+	}
+	if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+	ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
+	if (!ciphers) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
+			   MACSTR,
+			   parse->pairwise_cipher, MAC2STR(sm->addr));
+		return -1;
+	}
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+
+	return 0;
+}
+
+
 static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 				   const u8 *ies, size_t ies_len,
 				   u8 **resp_ies, size_t *resp_ies_len)
@@ -856,6 +953,9 @@
 		return WLAN_STATUS_INVALID_PMKID;
 	}
 
+	if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
 	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
 		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
 	wpa_derive_pmk_r1_name(parse.rsn_pmkid,
@@ -864,8 +964,12 @@
 	wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
 		    pmk_r1_name, WPA_PMK_NAME_LEN);
 
-	if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
-		    &pairwise) < 0) {
+	if (conf->ft_psk_generate_local &&
+	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise) < 0)
+			return WLAN_STATUS_INVALID_PMKID;
+	} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+				       pmk_r1, &pairwise) < 0) {
 		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
 			wpa_printf(MSG_DEBUG, "FT: Did not have matching "
 				   "PMK-R1 and unknown R0KH-ID");
@@ -1041,7 +1145,7 @@
 			    ftie->snonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 			    sm->SNonce, WPA_NONCE_LEN);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
@@ -1050,13 +1154,13 @@
 			    ftie->anonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 			    sm->ANonce, WPA_NONCE_LEN);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 
 	if (parse.r0kh_id == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.r0kh_id_len != sm->r0kh_id_len ||
@@ -1068,12 +1172,12 @@
 			    parse.r0kh_id, parse.r0kh_id_len);
 		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
 			    sm->r0kh_id, sm->r0kh_id_len);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.r1kh_id == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
@@ -1084,7 +1188,7 @@
 			    parse.r1kh_id, FT_R1KH_ID_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
 			    sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.rsn_pmkid == NULL ||
@@ -1092,7 +1196,7 @@
 	{
 		wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
 			   "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
-		return -1;
+		return WLAN_STATUS_INVALID_PMKID;
 	}
 
 	count = 3;
@@ -1102,7 +1206,7 @@
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
 			   ftie->mic_control[1], count);
-		return -1;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
 	if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
@@ -1789,4 +1893,4 @@
 	}
 }
 
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 2142414..aabac36 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -53,7 +53,7 @@
 	wconf->ieee80211w = conf->ieee80211w;
 	wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wconf->ssid_len = conf->ssid.ssid_len;
 	if (wconf->ssid_len > SSID_MAX_LEN)
 		wconf->ssid_len = SSID_MAX_LEN;
@@ -73,7 +73,8 @@
 	wconf->r1kh_list = conf->r1kh_list;
 	wconf->pmk_r1_push = conf->pmk_r1_push;
 	wconf->ft_over_ds = conf->ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+	wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_HS20
 	wconf->disable_gtk = conf->disable_dgaf;
 	if (conf->osen) {
@@ -401,7 +402,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 
 struct wpa_auth_ft_iface_iter_data {
 	struct hostapd_data *src_hapd;
@@ -440,7 +441,7 @@
 	return 0;
 }
 
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
@@ -465,7 +466,7 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (proto == ETH_P_RRB && hapd->iface->interfaces &&
 	    hapd->iface->interfaces->for_each_interface) {
 		int res;
@@ -480,7 +481,7 @@
 		if (res == 1)
 			return data_len;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	if (hapd->driver && hapd->driver->send_ether)
 		return hapd->driver->send_ether(hapd->drv_priv, dst,
@@ -503,7 +504,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 
 static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
 					   const u8 *data, size_t data_len)
@@ -588,7 +589,7 @@
 	return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
 }
 
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 int hostapd_setup_wpa(struct hostapd_data *hapd)
@@ -619,11 +620,11 @@
 	cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
 	cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
 	cb.send_ether = hostapd_wpa_auth_send_ether;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
 	cb.add_sta = hostapd_wpa_auth_add_sta;
 	cb.add_tspec = hostapd_wpa_auth_add_tspec;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
 	if (hapd->wpa_auth == NULL) {
 		wpa_printf(MSG_ERROR, "WPA initialization failed.");
@@ -649,7 +650,7 @@
 		return -1;
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (!hostapd_drv_none(hapd) &&
 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
 		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
@@ -664,7 +665,7 @@
 			return -1;
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	return 0;
 
@@ -702,8 +703,8 @@
 	}
 	ieee802_1x_deinit(hapd);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	l2_packet_deinit(hapd->l2);
 	hapd->l2 = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 }
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 72b7eb3..0c5a457 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -88,10 +88,10 @@
 	unsigned int rx_eapol_key_secure:1;
 	unsigned int update_snonce:1;
 	unsigned int alt_snonce_valid:1;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	unsigned int ft_completed:1;
 	unsigned int pmk_r1_name_valid:1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	unsigned int is_wnmsleep:1;
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
@@ -112,7 +112,7 @@
 	u32 dot11RSNAStatsTKIPLocalMICFailures;
 	u32 dot11RSNAStatsTKIPRemoteMICFailures;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
 	size_t xxkey_len;
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
@@ -131,13 +131,20 @@
 	u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
 	u8 ft_pending_auth_transaction;
 	u8 ft_pending_current_ap[ETH_ALEN];
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	int pending_1_of_4_timeout;
 
 #ifdef CONFIG_P2P
 	u8 ip_addr[4];
 #endif /* CONFIG_P2P */
+
+#ifdef CONFIG_FILS
+	u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+	u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+	size_t fils_key_auth_len;
+	unsigned int fils_completed:1;
+#endif /* CONFIG_FILS */
 };
 
 
@@ -244,7 +251,7 @@
 		const u8 *key_data, size_t key_data_len);
 #endif /* CONFIG_PEERKEY */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
 int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
 		   size_t r0kh_id_len,
@@ -256,6 +263,6 @@
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f79783b..1df3009 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -164,7 +164,7 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
 		pos += RSN_SELECTOR_LEN;
@@ -175,7 +175,7 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
@@ -210,6 +210,30 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
+#ifdef CONFIG_FILS
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -407,7 +431,7 @@
 			return res;
 		pos += res;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
 		res = wpa_write_mdie(&wpa_auth->conf, pos,
 				     buf + sizeof(buf) - pos);
@@ -415,7 +439,7 @@
 			return res;
 		pos += res;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
 		res = wpa_write_wpa_ie(&wpa_auth->conf,
 				       pos, buf + sizeof(buf) - pos);
@@ -509,12 +533,24 @@
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+		else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 			selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
 			selector = RSN_AUTH_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
@@ -591,12 +627,24 @@
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+	else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+	else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+	else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
 	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
 	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
@@ -655,7 +703,7 @@
 		sm->mgmt_frame_prot = 1;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
 			wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
@@ -669,7 +717,7 @@
 			return WPA_INVALID_MDIE;
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
 	if (sm->pairwise < 0)
@@ -908,14 +956,14 @@
 		if (*pos == WLAN_EID_RSN) {
 			ie->rsn_ie = pos;
 			ie->rsn_ie_len = pos[1] + 2;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
 			ie->mdie = pos;
 			ie->mdie_len = pos[1] + 2;
 		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
 			ie->ftie = pos;
 			ie->ftie_len = pos[1] + 2;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
 			ret = wpa_parse_generic(pos, end, ie);
 			if (ret < 0)
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index d2067ba..5c3bd18 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -33,12 +33,12 @@
 	const u8 *igtk;
 	size_t igtk_len;
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	const u8 *mdie;
 	size_t mdie_len;
 	const u8 *ftie;
 	size_t ftie_len;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	const u8 *ip_addr_req;
 	const u8 *ip_addr_alloc;
diff --git a/src/common/defs.h b/src/common/defs.h
index 4f56794..4dd5690 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -51,6 +51,10 @@
 #define WPA_KEY_MGMT_OSEN BIT(15)
 #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
 #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
+#define WPA_KEY_MGMT_FILS_SHA256 BIT(18)
+#define WPA_KEY_MGMT_FILS_SHA384 BIT(19)
+#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20)
+#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
@@ -60,7 +64,11 @@
 			 WPA_KEY_MGMT_OSEN |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
 			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
-			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+			 WPA_KEY_MGMT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FILS_SHA384 |
+			 WPA_KEY_MGMT_FT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
 }
 
 static inline int wpa_key_mgmt_wpa_psk(int akm)
@@ -76,7 +84,14 @@
 {
 	return !!(akm & (WPA_KEY_MGMT_FT_PSK |
 			 WPA_KEY_MGMT_FT_IEEE8021X |
-			 WPA_KEY_MGMT_FT_SAE));
+			 WPA_KEY_MGMT_FT_SAE |
+			 WPA_KEY_MGMT_FT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
+}
+
+static inline int wpa_key_mgmt_ft_psk(int akm)
+{
+	return !!(akm & WPA_KEY_MGMT_FT_PSK);
 }
 
 static inline int wpa_key_mgmt_sae(int akm)
@@ -85,17 +100,29 @@
 			 WPA_KEY_MGMT_FT_SAE));
 }
 
+static inline int wpa_key_mgmt_fils(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FILS_SHA384 |
+			 WPA_KEY_MGMT_FT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
+}
+
 static inline int wpa_key_mgmt_sha256(int akm)
 {
 	return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
 			 WPA_KEY_MGMT_OSEN |
-			 WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+			 WPA_KEY_MGMT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA256));
 }
 
 static inline int wpa_key_mgmt_sha384(int akm)
 {
-	return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+	return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+			 WPA_KEY_MGMT_FILS_SHA384 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
 }
 
 static inline int wpa_key_mgmt_suite_b(int akm)
@@ -108,6 +135,7 @@
 {
 	return wpa_key_mgmt_wpa_ieee8021x(akm) ||
 		wpa_key_mgmt_wpa_psk(akm) ||
+		wpa_key_mgmt_fils(akm) ||
 		wpa_key_mgmt_sae(akm);
 }
 
@@ -132,6 +160,7 @@
 #define WPA_AUTH_ALG_LEAP BIT(2)
 #define WPA_AUTH_ALG_FT BIT(3)
 #define WPA_AUTH_ALG_SAE BIT(4)
+#define WPA_AUTH_ALG_FILS BIT(5)
 
 
 enum wpa_alg {
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index b6bc449..26e08b7 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -179,6 +179,90 @@
 }
 
 
+static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
+				      struct ieee802_11_elems *elems,
+				      int show_errors)
+{
+	u8 ext_id;
+
+	if (elen < 1) {
+		if (show_errors) {
+			wpa_printf(MSG_MSGDUMP,
+				   "short information element (Ext)");
+		}
+		return -1;
+	}
+
+	ext_id = *pos++;
+	elen--;
+
+	switch (ext_id) {
+	case WLAN_EID_EXT_ASSOC_DELAY_INFO:
+		if (elen != 1)
+			break;
+		elems->assoc_delay_info = pos;
+		break;
+	case WLAN_EID_EXT_FILS_REQ_PARAMS:
+		if (elen < 3)
+			break;
+		elems->fils_req_params = pos;
+		elems->fils_req_params_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_KEY_CONFIRM:
+		elems->fils_key_confirm = pos;
+		elems->fils_key_confirm_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_SESSION:
+		if (elen != FILS_SESSION_LEN)
+			break;
+		elems->fils_session = pos;
+		break;
+	case WLAN_EID_EXT_FILS_HLP_CONTAINER:
+		if (elen < 2 * ETH_ALEN)
+			break;
+		elems->fils_hlp = pos;
+		elems->fils_hlp_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
+		if (elen < 1)
+			break;
+		elems->fils_ip_addr_assign = pos;
+		elems->fils_ip_addr_assign_len = elen;
+		break;
+	case WLAN_EID_EXT_KEY_DELIVERY:
+		if (elen < WPA_KEY_RSC_LEN)
+			break;
+		elems->key_delivery = pos;
+		elems->key_delivery_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_WRAPPED_DATA:
+		elems->fils_wrapped_data = pos;
+		elems->fils_wrapped_data_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_PUBLIC_KEY:
+		if (elen < 1)
+			break;
+		elems->fils_pk = pos;
+		elems->fils_pk_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_NONCE:
+		if (elen != FILS_NONCE_LEN)
+			break;
+		elems->fils_nonce = pos;
+		break;
+	default:
+		if (show_errors) {
+			wpa_printf(MSG_MSGDUMP,
+				   "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
+				   ext_id, (unsigned int) elen);
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+
 /**
  * ieee802_11_parse_elems - Parse information elements in management frames
  * @start: Pointer to the start of IEs
@@ -379,6 +463,35 @@
 			elems->rrm_enabled = pos;
 			elems->rrm_enabled_len = elen;
 			break;
+		case WLAN_EID_CAG_NUMBER:
+			elems->cag_number = pos;
+			elems->cag_number_len = elen;
+			break;
+		case WLAN_EID_AP_CSN:
+			if (elen < 1)
+				break;
+			elems->ap_csn = pos;
+			break;
+		case WLAN_EID_FILS_INDICATION:
+			if (elen < 2)
+				break;
+			elems->fils_indic = pos;
+			elems->fils_indic_len = elen;
+			break;
+		case WLAN_EID_DILS:
+			if (elen < 2)
+				break;
+			elems->dils = pos;
+			elems->dils_len = elen;
+			break;
+		case WLAN_EID_FRAGMENT:
+			/* TODO */
+			break;
+		case WLAN_EID_EXTENSION:
+			if (ieee802_11_parse_extension(pos, elen, elems,
+						       show_errors))
+				unknown++;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
@@ -681,6 +794,25 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
+	/* 5 GHz, channels 52..64 */
+	if (freq >= 5260 && freq <= 5320) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 119;
+		else if (sec_channel == -1)
+			*op_class = 120;
+		else
+			*op_class = 118;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
 	/* 5 GHz, channels 149..169 */
 	if (freq >= 5745 && freq <= 5845) {
 		if ((freq - 5000) % 5)
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 42f3909..2c20ba6 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -64,6 +64,20 @@
 	const u8 *pref_freq_list;
 	const u8 *supp_op_classes;
 	const u8 *rrm_enabled;
+	const u8 *cag_number;
+	const u8 *ap_csn;
+	const u8 *fils_indic;
+	const u8 *dils;
+	const u8 *assoc_delay_info;
+	const u8 *fils_req_params;
+	const u8 *fils_key_confirm;
+	const u8 *fils_session;
+	const u8 *fils_hlp;
+	const u8 *fils_ip_addr_assign;
+	const u8 *key_delivery;
+	const u8 *fils_wrapped_data;
+	const u8 *fils_pk;
+	const u8 *fils_nonce;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -96,6 +110,16 @@
 	u8 pref_freq_list_len;
 	u8 supp_op_classes_len;
 	u8 rrm_enabled_len;
+	u8 cag_number_len;
+	u8 fils_indic_len;
+	u8 dils_len;
+	u8 fils_req_params_len;
+	u8 fils_key_confirm_len;
+	u8 fils_hlp_len;
+	u8 fils_ip_addr_assign_len;
+	u8 key_delivery_len;
+	u8 fils_wrapped_data_len;
+	u8 fils_pk_len;
 
 	struct mb_ies_info mb_ies;
 };
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 02d2ad7..ca808d7 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -81,6 +81,9 @@
 #define WLAN_AUTH_SHARED_KEY		1
 #define WLAN_AUTH_FT			2
 #define WLAN_AUTH_SAE			3
+#define WLAN_AUTH_FILS_SK		4
+#define WLAN_AUTH_FILS_SK_PFS		5
+#define WLAN_AUTH_FILS_PK		6
 #define WLAN_AUTH_LEAP			128
 
 #define WLAN_AUTH_CHALLENGE_LEN 128
@@ -175,6 +178,8 @@
 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
 #define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
+#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
+#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
 
 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
 #define WLAN_REASON_UNSPECIFIED 1
@@ -292,6 +297,24 @@
 #define WLAN_EID_VHT_QUIET_CHANNEL 198
 #define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
 #define WLAN_EID_VENDOR_SPECIFIC 221
+#define WLAN_EID_CAG_NUMBER 237
+#define WLAN_EID_AP_CSN 239
+#define WLAN_EID_FILS_INDICATION 240
+#define WLAN_EID_DILS 241
+#define WLAN_EID_FRAGMENT 242
+#define WLAN_EID_EXTENSION 255
+
+/* Element ID Extension (EID 255) values */
+#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1
+#define WLAN_EID_EXT_FILS_REQ_PARAMS 2
+#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3
+#define WLAN_EID_EXT_FILS_SESSION 4
+#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5
+#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6
+#define WLAN_EID_EXT_KEY_DELIVERY 7
+#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8
+#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
+#define WLAN_EID_EXT_FILS_NONCE 13
 
 
 /* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
@@ -311,6 +334,7 @@
 #define WLAN_ACTION_SELF_PROTECTED 15
 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
 #define WLAN_ACTION_FST 18
+#define WLAN_ACTION_FILS 26
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
 
 /* Public action codes */
@@ -321,6 +345,7 @@
 #define WLAN_PA_GAS_COMEBACK_REQ 12
 #define WLAN_PA_GAS_COMEBACK_RESP 13
 #define WLAN_TDLS_DISCOVERY_RESPONSE 14
+#define WLAN_PA_FILS_DISCOVERY 34
 
 /* Protected Dual of Public Action frames */
 #define WLAN_PROT_DSE_ENABLEMENT 1
@@ -426,6 +451,10 @@
 	ANQP_TDLS_CAPABILITY = 270,
 	ANQP_EMERGENCY_NAI = 271,
 	ANQP_NEIGHBOR_REPORT = 272,
+	ANQP_QUERY_AP_LIST = 273,
+	ANQP_AP_LIST_RESPONSE = 274,
+	AMQP_FILS_REALM_INFO = 275,
+	ANQP_CAG = 276,
 	ANQP_VENUE_URL = 277,
 	ANQP_ADVICE_OF_CHARGE = 278,
 	ANQP_LOCAL_CONTENT = 279,
@@ -505,6 +534,11 @@
 	LCI_REQ_SUBELEM_MAX_AGE = 4,
 };
 
+#define FILS_NONCE_LEN 16
+#define FILS_SESSION_LEN 8
+#define FILS_CACHE_ID_LEN 2
+#define FILS_MAX_KEY_AUTH_LEN 48
+
 #ifdef _MSC_VER
 #pragma pack(push, 1)
 #endif /* _MSC_VER */
@@ -1180,6 +1214,10 @@
 	WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3,
 };
 
+/* MBO v0.0_r25, 4.3: MBO ANQP-elements */
+#define MBO_ANQP_OUI_TYPE 0x12
+#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1
+
 /* Wi-Fi Direct (P2P) */
 
 #define P2P_OUI_TYPE 9
@@ -1391,6 +1429,10 @@
 #define WLAN_AKM_SUITE_PSK_SHA256	0x000FAC06
 #define WLAN_AKM_SUITE_8021X_SUITE_B	0x000FAC11
 #define WLAN_AKM_SUITE_8021X_SUITE_B_192	0x000FAC12
+#define WLAN_AKM_SUITE_FILS_SHA256	0x000FAC14
+#define WLAN_AKM_SUITE_FILS_SHA384	0x000FAC15
+#define WLAN_AKM_SUITE_FT_FILS_SHA256	0x000FAC16
+#define WLAN_AKM_SUITE_FT_FILS_SHA384	0x000FAC17
 #define WLAN_AKM_SUITE_CCKM		0x00409600
 #define WLAN_AKM_SUITE_OSEN		0x506f9a01
 
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index 8dff303..f017f08 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -42,9 +42,9 @@
 	int wep_tx_keyidx;
 	int local_state_change;
 	int p2p;
-	size_t sae_data_len;
+	size_t auth_data_len;
 	/* followed by ie_len bytes of ie */
-	/* followed by sae_data_len bytes of sae_data */
+	/* followed by auth_data_len bytes of auth_data */
 };
 
 struct privsep_cmd_associate
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index c458cb3..fc391e0 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -164,8 +164,11 @@
  *
  * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of
  *	arrival) measurement with a single peer. Specify peer MAC address in
- *	QCA_WLAN_VENDOR_ATTR_MAC_ADDR and measurement type in
- *	QCA_WLAN_VENDOR_ATTR_AOA_TYPE. Measurement result is reported in
+ *	QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in
+ *	QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel
+ *	scan results cache and use the frequency from there).
+ *	Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE.
+ *	Measurement result is reported in
  *	QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event.
  *
  * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify
@@ -185,6 +188,50 @@
  *
  * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a
  *	specific chain.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level
+ *	configuration for a DMG RF sector. Specify sector index in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules
+ *	to return sector information for in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration
+ *	in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the
+ *	exact time where information was captured in
+ *	QCA_WLAN_VENDOR_ATTR_TSF.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level
+ *	configuration for a DMG RF sector. Specify sector index in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration
+ *	for one or more DMG RF modules in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected
+ *	DMG RF sector for a station. This is the sector that the HW
+ *	will use to communicate with the station. Specify the MAC address
+ *	of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not
+ *	needed for unassociated	station). Specify sector type to return in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected
+ *	sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
+ *	Also return the exact time where the information was captured
+ *	in QCA_WLAN_VENDOR_ATTR_TSF.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the
+ *	selected DMG RF sector for a station. This is the sector that
+ *	the HW will use to communicate with the station.
+ *	Specify the MAC address of associated station/AP/PCP in
+ *	QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index
+ *	in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
+ *	The selected sector will be locked such that it will not be
+ *	modified like it normally does (for example when station
+ *	moves around). To unlock the selected sector for a station
+ *	pass the special value 0xFFFF in the sector index. To unlock
+ *	all connected stations also pass a broadcast MAC address.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior
+ *	in the host driver. The different TDLS configurations are defined
+ *	by the attributes in enum qca_wlan_vendor_attr_tdls_configuration.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -194,7 +241,7 @@
 	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
 	QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY =  11,
 	QCA_NL80211_VENDOR_SUBCMD_NAN =  12,
-	QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
+	QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13,
 	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
 	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
 	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
@@ -285,6 +332,12 @@
 	QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
 	QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137,
 	QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138,
+	/* DMG low level RF sector operations */
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
+	QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143,
 };
 
 
@@ -392,6 +445,42 @@
 	/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
 	 * to report the specific antenna RSSI value (unsigned 32 bit value) */
 	QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27,
+	/* Frequency in MHz, various uses. Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_FREQ = 28,
+	/* TSF timer value, unsigned 64 bit value.
+	 * May be returned by various commands.
+	 */
+	QCA_WLAN_VENDOR_ATTR_TSF = 29,
+	/* DMG RF sector index, unsigned 16 bit number. Valid values are
+	 * 0..127 for sector indices or 65535 as special value used to
+	 * unlock sector selection in
+	 * QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30,
+	/* DMG RF sector type, unsigned 8 bit value. One of the values
+	 * in enum qca_wlan_vendor_attr_dmg_rf_sector_type.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31,
+	/* Bitmask of DMG RF modules for which information is requested. Each
+	 * bit corresponds to an RF module with the same index as the bit
+	 * number. Unsigned 32 bit number but only low 8 bits can be set since
+	 * all DMG chips currently have up to 8 RF modules.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32,
+	/* Array of nested attributes where each entry is DMG RF sector
+	 * configuration for a single RF module.
+	 * Attributes for each entry are taken from enum
+	 * qca_wlan_vendor_attr_dmg_rf_sector_cfg.
+	 * Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG
+	 * and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command
+	 * to report frame aggregation statistics to userspace.
+	 */
+	QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34,
+	QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -789,13 +878,13 @@
  * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands.
  */
 enum qca_wlan_vendor_attr_config {
-	QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID = 0,
 	/* Unsigned 32-bit value to set the DTIM period.
 	 * Whether the wifi chipset wakes at every dtim beacon or a multiple of
 	 * the DTIM period. If DTIM is set to 3, the STA shall wake up every 3
 	 * DTIM beacons.
 	 */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM = 1,
 	/* Unsigned 32-bit value to set the wifi_iface stats averaging factor
 	 * used to calculate statistics like average the TSF offset or average
 	 * number of frame leaked.
@@ -804,52 +893,52 @@
 	 * For instance, when evaluating leaky APs:
 	 * current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000
 	 */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2,
 	/* Unsigned 32-bit value to configure guard time, i.e., when
 	 * implementing IEEE power management based on frame control PM bit, how
 	 * long the driver waits before shutting down the radio and after
 	 * receiving an ACK frame for a Data frame with PM bit set.
 	 */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME = 3,
 	/* Unsigned 32-bit value to change the FTM capability dynamically */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT = 4,
 	/* Unsigned 16-bit value to configure maximum TX rate dynamically */
-	QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE,
+	QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE = 5,
 	/* Unsigned 32-bit value to configure the number of continuous
 	 * Beacon Miss which shall be used by the firmware to penalize
 	 * the RSSI.
 	 */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS = 6,
 	/* Unsigned 8-bit value to configure the channel avoidance indication
 	 * behavior. Firmware to send only one indication and ignore duplicate
 	 * indications when set to avoid multiple Apps wakeups.
 	 */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7,
 	/* 8-bit unsigned value to configure the maximum TX MPDU for
 	 * aggregation. */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8,
 	/* 8-bit unsigned value to configure the maximum RX MPDU for
 	 * aggregation. */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9,
 	/* 8-bit unsigned value to configure the Non aggregrate/11g sw
 	 * retry threshold (0 disable, 31 max). */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10,
 	/* 8-bit unsigned value to configure the aggregrate sw
 	 * retry threshold (0 disable, 31 max). */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11,
 	/* 8-bit unsigned value to configure the MGMT frame
 	 * retry threshold (0 disable, 31 max). */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12,
 	/* 8-bit unsigned value to configure the CTRL frame
 	 * retry threshold (0 disable, 31 max). */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13,
 	/* 8-bit unsigned value to configure the propagation delay for
 	 * 2G/5G band (0~63, units in us) */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14,
 	/* Unsigned 32-bit value to configure the number of unicast TX fail
 	 * packet count. The peer is disconnected once this threshold is
 	 * reached. */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15,
 	/* Attribute used to set scan default IEs to the driver.
 	 *
 	 * These IEs can be used by scan operations that will be initiated by
@@ -860,50 +949,62 @@
 	 * driver. If a particular IE is present in the scan default IEs but not
 	 * present in the scan request, then that IE should be added to the IEs
 	 * sent in the Probe Request frames for that scan request. */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16,
 	/* Unsigned 32-bit attribute for generic commands */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17,
 	/* Unsigned 32-bit value attribute for generic commands */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE = 18,
 	/* Unsigned 32-bit data attribute for generic command response */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19,
 	/* Unsigned 32-bit length attribute for
 	 * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20,
 	/* Unsigned 32-bit flags attribute for
 	 * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21,
 	/* Unsigned 32-bit, defining the access policy.
 	 * See enum qca_access_policy. Used with
 	 * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22,
 	/* Sets the list of full set of IEs for which a specific access policy
 	 * has to be applied. Used along with
 	 * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access.
 	 * Zero length payload can be used to clear this access constraint. */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23,
 	/* Unsigned 32-bit, specifies the interface index (netdev) for which the
 	 * corresponding configurations are applied. If the interface index is
 	 * not specified, the configurations are attributed to the respective
 	 * wiphy. */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24,
 	/* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25,
 	/* 8-bit unsigned value to configure the driver and below layers to
 	 * ignore the assoc disallowed set by APs while connecting
 	 * 1-Ignore, 0-Don't ignore */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26,
 	/* 32-bit unsigned value to trigger antenna diversity features:
 	 * 1-Enable, 0-Disable */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27,
 	/* 32-bit unsigned value to configure specific chain antenna */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28,
 	/* 32-bit unsigned value to trigger cycle selftest
 	 * 1-Enable, 0-Disable */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29,
 	/* 32-bit unsigned to configure the cycle time of selftest
 	 * the unit is micro-second */
-	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30,
+	/* 32-bit unsigned value to set reorder timeout for AC_VO */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31,
+	/* 32-bit unsigned value to set reorder timeout for AC_VI */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32,
+	/* 32-bit unsigned value to set reorder timeout for AC_BE */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33,
+	/* 32-bit unsigned value to set reorder timeout for AC_BK */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34,
+	/* 6-byte MAC address to point out the specific peer */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35,
+	/* 32-bit unsigned value to set window size for specific peer */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
@@ -1320,6 +1421,10 @@
  * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA
  *	measurement every <value> bursts. If 0 or not specified,
  *	AOA measurements will be disabled for this peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where
+ *	the measurement frames are exchanged. Optional; if not
+ *	specified, try to locate the peer in the kernel scan
+ *	results cache and use frequency from there.
  */
 enum qca_wlan_vendor_attr_ftm_peer_info {
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID,
@@ -1328,6 +1433,7 @@
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS,
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID,
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX =
@@ -1587,4 +1693,518 @@
 	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1
 };
 
+/**
+ * enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of
+ * sector for DMG RF sector operations.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector
+ */
+enum qca_wlan_vendor_attr_dmg_rf_sector_type {
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for
+ * DMG RF sector configuration for a single RF module.
+ * The values are defined in a compact way which closely matches
+ * the way it is stored in HW registers.
+ * The configuration provides values for 32 antennas and 8 distribution
+ * amplifiers, and together describes the characteristics of the RF
+ * sector - such as a beam in some direction with some gain.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index
+ *	of RF module for this configuration.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge
+ *	amplifier gain index. Unsigned 32 bit number containing
+ *	bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge
+ *	amplifier gain index. Unsigned 32 bit number containing
+ *	bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge
+ *	amplifier gain index. Unsigned 32 bit number containing
+ *	bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values
+ *	for first 16 antennas, 2 bits per antenna.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values
+ *	for last 16 antennas, 2 bits per antenna.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains
+ *	DTYPE values (3 bits) for each distribution amplifier, followed
+ *	by X16 switch bits for each distribution amplifier. There are
+ *	total of 8 distribution amplifiers.
+ */
+enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX =
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_attr_ll_stats_set {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_clr {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0,
+	/* Unsigned 32bit bitmap for clearing statistics
+	 * All radio statistics                     0x00000001
+	 * cca_busy_time (within radio statistics)  0x00000002
+	 * All channel stats (within radio statistics) 0x00000004
+	 * All scan statistics (within radio statistics) 0x00000008
+	 * All interface statistics                     0x00000010
+	 * All tx rate statistics (within interface statistics) 0x00000020
+	 * All ac statistics (with in interface statistics) 0x00000040
+	 * All contention (min, max, avg) statistics (within ac statisctics)
+	 * 0x00000080.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1,
+	/* Unsigned 8 bit value: Request to stop statistics collection */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2,
+
+	/* Unsigned 32 bit bitmap: Response from the driver
+	 * for the cleared statistics
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3,
+	/* Unsigned 8 bit value: Response from driver/firmware
+	 * for the stop request
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_get {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0,
+	/* Unsigned 32 bit value provided by the caller issuing the GET stats
+	 * command. When reporting the stats results, the driver uses the same
+	 * value to indicate which GET request the results correspond to.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1,
+	/* Unsigned 32 bit value - bit mask to identify what statistics are
+	 * requested for retrieval.
+	 * Radio Statistics 0x00000001
+	 * Interface Statistics 0x00000020
+	 * All Peer Statistics 0x00000040
+	 * Peer Statistics     0x00000080
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_results {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0,
+	/* Unsigned 32bit value. Used by the driver; must match the request id
+	 * provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5,
+	/* Signed 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6,
+	/* Signed 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7,
+	/* Signed 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are
+	 * nested within the interface stats.
+	 */
+
+	/* Interface mode, e.g., STA, SOFTAP, IBSS, etc.
+	 * Type = enum wifi_interface_mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9,
+	/* Interface MAC address. An array of 6 Unsigned int8 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10,
+	/* Type = enum wifi_connection_state, e.g., DISCONNECTED,
+	 * AUTHENTICATING, etc. valid for STA, CLI only.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11,
+	/* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12,
+	/* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13,
+	/* NULL terminated SSID. An array of 33 Unsigned 8bit values */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14,
+	/* BSSID. An array of 6 unsigned 8 bit values */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15,
+	/* Country string advertised by AP. An array of 3 unsigned 8 bit
+	 * values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16,
+	/* Country string for this association. An array of 3 unsigned 8 bit
+	 * values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could
+	 * be nested within the interface stats.
+	 */
+
+	/* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26,
+	/* Unsigned int 32 value corresponding to respective AC  */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32,
+	/* Unsigned 32 bit value. Number of peers */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are
+	 * nested within the interface stats.
+	 */
+
+	/* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34,
+	/* MAC addr corresponding to respective peer. An array of 6 unsigned
+	 * 8 bit values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35,
+	/* Unsigned int 32 bit value representing capabilities corresponding
+	 * to respective peer.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36,
+	/* Unsigned 32 bit value. Number of rates */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
+	 * are nested within the rate stat.
+	 */
+
+	/* Wi-Fi Rate - separate attributes defined for individual fields */
+
+	/* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38,
+	/* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39,
+	/* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40,
+	/* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std
+	 * in the units of 0.5 Mbps HT/VHT it would be MCS index */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41,
+
+	/* Unsigned 32 bit value. Bit rate in units of 100 kbps */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42,
+
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be
+	 * nested within the peer info stats.
+	 */
+
+	/* Unsigned int 32 bit value. Number of successfully transmitted data
+	 * packets, i.e., with ACK received corresponding to the respective
+	 * rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43,
+	/* Unsigned int 32 bit value. Number of received data packets
+	 * corresponding to the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44,
+	/* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK
+	 * received corresponding to the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45,
+	/* Unsigned int 32 bit value. Total number of data packet retries for
+	 * the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46,
+	/* Unsigned int 32 bit value. Total number of short data packet retries
+	 * for the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47,
+	/* Unsigned int 32 bit value. Total number of long data packet retries
+	 * for the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48,
+
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake
+	 * accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50,
+	/* Unsigned 32 bit value. Total number of msecs the radio is
+	 * transmitting accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51,
+	/* Unsigned 32 bit value. Total number of msecs the radio is in active
+	 * receive accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to all scan accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to NAN accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to GSCAN accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to roam scan accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to PNO scan accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to Hotspot 2.0 scans and GAS exchange accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58,
+	/* Unsigned 32 bit value. Number of channels. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could
+	 * be nested within the channel stats.
+	 */
+
+	/* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60,
+	/* Unsigned 32 bit value. Primary 20 MHz channel. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61,
+	/* Unsigned 32 bit value. Center frequency (MHz) first segment. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62,
+	/* Unsigned 32 bit value. Center frequency (MHz) second segment. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be
+	 * nested within the radio stats.
+	 */
+
+	/* Unsigned int 32 bit value representing total number of msecs the
+	 * radio is awake on that channel accruing over time, corresponding to
+	 * the respective channel.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64,
+	/* Unsigned int 32 bit value representing total number of msecs the CCA
+	 * register is busy accruing over time corresponding to the respective
+	 * channel.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65,
+
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
+
+	/* Signifies the nested list of channel attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
+
+	/* Signifies the nested list of peer info attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68,
+
+	/* Signifies the nested list of rate info attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69,
+
+	/* Signifies the nested list of wmm info attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70,
+
+	/* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates
+	 * that more stats, e.g., peers or radio, are to follow in the next
+	 * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event.
+	 * Otherwise, it is set to 0.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71,
+
+	/* Unsigned 64 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77,
+
+	/* Number of msecs the radio spent in transmitting for each power level
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_type
+{
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3,
+
+	/* keep last */
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX =
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for
+ * TDLS configuration to the host driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger
+ *	mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode
+ *	represents the different TDLS trigger modes.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within
+ *      which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number
+ *      of packets shall meet the criteria for implicit TDLS setup.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets
+ *      within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD
+ *      to initiate a TDLS setup.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate
+ *      a TDLS Discovery to the peer.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of
+ *      discovery attempts to know the TDLS capability of the peer. A peer is
+ *      marked as TDLS not capable if there is no response for all the attempts.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32)
+ *      within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD
+ *      number of TX / RX frames meet the criteria for TDLS teardown.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32)
+ *      of Tx/Rx packets within a duration
+ *      QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold
+ *	corresponding to the RSSI of the peer below which a TDLS setup is
+ *	triggered.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold
+ *	corresponding to the RSSI of the peer above which a TDLS teardown is
+ *	triggered.
+ */
+enum qca_wlan_vendor_attr_tdls_configuration {
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1,
+
+	/* Attributes configuring the TDLS Implicit Trigger */
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in
+ *	the driver
+ *
+ * The following are the different values for
+ *	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown
+ *	the TDLS connection to a respective peer comes from the user space.
+ *	wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN,
+ *	TDLS_DISCOVER to do this.
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS
+ *	setup/teardown to the eligible peer once the configured criteria
+ *	(such as TX/RX threshold, RSSI) is met. The attributes
+ *	in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to
+ *	the different configuration criteria for the TDLS trigger from the
+ *	host driver.
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger
+ *	the TDLS setup / teardown through the implicit mode only to the
+ *	configured MAC addresses (wpa_supplicant, with tdls_external_control=1,
+ *	configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands).
+ *	External mode works on top of the implicit mode. Thus the host driver
+ *	is expected to configure in TDLS Implicit mode too to operate in
+ *	External mode.
+ *	Configuring External mode alone without	Implicit mode is invalid.
+ *
+ * All the above implementations work as expected only when the host driver
+ * advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing
+ * that the TDLS message exchange is not internal to the host driver, but
+ * depends on wpa_supplicant to do the message exchange.
+ */
+enum qca_wlan_vendor_tdls_trigger_mode {
+	QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0,
+	QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1,
+	QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/version.h b/src/common/version.h
index ae5c9d4..16c1004 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -9,6 +9,6 @@
 #define GIT_VERSION_STR_POSTFIX ""
 #endif /* GIT_VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.6-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.7-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 299b8bb..a87210e 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/crc32.h"
 #include "crypto/md5.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
@@ -22,25 +23,49 @@
 
 static unsigned int wpa_kck_len(int akmp)
 {
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	switch (akmp) {
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 		return 24;
-	return 16;
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return 0;
+	default:
+		return 16;
+	}
 }
 
 
 static unsigned int wpa_kek_len(int akmp)
 {
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	switch (akmp) {
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return 64;
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
 		return 32;
-	return 16;
+	default:
+		return 16;
+	}
 }
 
 
 unsigned int wpa_mic_len(int akmp)
 {
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	switch (akmp) {
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 		return 24;
-	return 16;
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return 0;
+	default:
+		return 16;
+	}
 }
 
 
@@ -204,6 +229,155 @@
 	return 0;
 }
 
+#ifdef CONFIG_FILS
+
+int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
+		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher)
+{
+	u8 data[2 * ETH_ALEN + 2 * FILS_NONCE_LEN];
+	u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	size_t key_data_len;
+	const char *label = "FILS PTK Derivation";
+
+	/*
+	 * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
+	 *                       SPA || AA || SNonce || ANonce)
+	 * ICK = L(FILS-Key-Data, 0, ICK_bits)
+	 * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
+	 * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
+	 * If doing FT initial mobility domain association:
+	 * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
+	 *             FILS-FT_bits)
+	 */
+	os_memcpy(data, spa, ETH_ALEN);
+	os_memcpy(data + ETH_ALEN, aa, ETH_ALEN);
+	os_memcpy(data + 2 * ETH_ALEN, snonce, FILS_NONCE_LEN);
+	os_memcpy(data + 2 * ETH_ALEN + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+
+	ptk->kck_len = 0;
+	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->tk_len = wpa_cipher_key_len(cipher);
+	if (wpa_key_mgmt_sha384(akmp))
+		*ick_len = 48;
+	else if (wpa_key_mgmt_sha256(akmp))
+		*ick_len = 32;
+	else
+		return -1;
+	key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
+
+	if (wpa_key_mgmt_sha384(akmp))
+		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+			   tmp, key_data_len);
+	else if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+			    tmp, key_data_len) < 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
+		   " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
+
+	os_memcpy(ick, tmp, *ick_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len);
+
+	os_memcpy(ptk->kek, tmp + *ick_len, ptk->kek_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len);
+
+	os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len);
+
+	/* TODO: FILS-FT */
+
+	os_memset(tmp, 0, sizeof(tmp));
+	return 0;
+}
+
+
+int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
+		     const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
+		     const u8 *g_sta, size_t g_sta_len,
+		     const u8 *g_ap, size_t g_ap_len,
+		     int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
+		     size_t *key_auth_len)
+{
+	const u8 *addr[6];
+	size_t len[6];
+	size_t num_elem = 4;
+	int res;
+
+	/*
+	 * For (Re)Association Request frame (STA->AP):
+	 * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID
+	 *                      [ || gSTA || gAP ])
+	 */
+	addr[0] = snonce;
+	len[0] = FILS_NONCE_LEN;
+	addr[1] = anonce;
+	len[1] = FILS_NONCE_LEN;
+	addr[2] = sta_addr;
+	len[2] = ETH_ALEN;
+	addr[3] = bssid;
+	len[3] = ETH_ALEN;
+	if (g_sta && g_ap_len && g_ap && g_ap_len) {
+		addr[4] = g_sta;
+		len[4] = g_sta_len;
+		addr[5] = g_ap;
+		len[5] = g_ap_len;
+		num_elem = 6;
+	}
+
+	if (wpa_key_mgmt_sha384(akmp)) {
+		*key_auth_len = 48;
+		res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_sta);
+	} else if (wpa_key_mgmt_sha256(akmp)) {
+		*key_auth_len = 32;
+		res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_sta);
+	} else {
+		return -1;
+	}
+	if (res < 0)
+		return res;
+
+	/*
+	 * For (Re)Association Response frame (AP->STA):
+	 * Key-Auth = HMAC-Hash(ICK, ANonce || SNonce || AP-BSSID || STA-MAC
+	 *                      [ || gAP || gSTA ])
+	 */
+	addr[0] = anonce;
+	addr[1] = snonce;
+	addr[2] = bssid;
+	addr[3] = sta_addr;
+	if (g_sta && g_ap_len && g_ap && g_ap_len) {
+		addr[4] = g_ap;
+		len[4] = g_ap_len;
+		addr[5] = g_sta;
+		len[5] = g_sta_len;
+	}
+
+	if (wpa_key_mgmt_sha384(akmp))
+		res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_ap);
+	else if (wpa_key_mgmt_sha256(akmp))
+		res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_ap);
+	if (res < 0)
+		return res;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (STA)",
+		    key_auth_sta, *key_auth_len);
+	wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (AP)",
+		    key_auth_ap, *key_auth_len);
+
+	return 0;
+}
+
+#endif /* CONFIG_FILS */
+
 
 #ifdef CONFIG_IEEE80211R
 int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
@@ -376,6 +550,8 @@
 			}
 			if (data.num_pmkid == 1 && data.pmkid)
 				parse->rsn_pmkid = data.pmkid;
+			parse->key_mgmt = data.key_mgmt;
+			parse->pairwise_cipher = data.pairwise_cipher;
 			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
 			if (len < sizeof(struct rsn_mdie))
@@ -510,6 +686,14 @@
 		return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
 		return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256)
+		return WPA_KEY_MGMT_FILS_SHA256;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384)
+		return WPA_KEY_MGMT_FILS_SHA384;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256)
+		return WPA_KEY_MGMT_FT_FILS_SHA256;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
+		return WPA_KEY_MGMT_FT_FILS_SHA384;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
 		return WPA_KEY_MGMT_OSEN;
 	return 0;
@@ -1212,6 +1396,14 @@
 		return "WPA2-EAP-SUITE-B";
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 		return "WPA2-EAP-SUITE-B-192";
+	case WPA_KEY_MGMT_FILS_SHA256:
+		return "FILS-SHA256";
+	case WPA_KEY_MGMT_FILS_SHA384:
+		return "FILS-SHA384";
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
+		return "FT-FILS-SHA256";
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return "FT-FILS-SHA384";
 	default:
 		return "UNKNOWN";
 	}
@@ -1242,6 +1434,14 @@
 		return WLAN_AKM_SUITE_8021X_SUITE_B;
 	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
 		return WLAN_AKM_SUITE_8021X_SUITE_B_192;
+	if (akm & WPA_KEY_MGMT_FILS_SHA256)
+		return WLAN_AKM_SUITE_FILS_SHA256;
+	if (akm & WPA_KEY_MGMT_FILS_SHA384)
+		return WLAN_AKM_SUITE_FILS_SHA384;
+	if (akm & WPA_KEY_MGMT_FT_FILS_SHA256)
+		return WLAN_AKM_SUITE_FT_FILS_SHA256;
+	if (akm & WPA_KEY_MGMT_FT_FILS_SHA384)
+		return WLAN_AKM_SUITE_FT_FILS_SHA384;
 	return 0;
 }
 
@@ -1283,7 +1483,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
 {
 	u8 *start, *end, *rpos, *rend;
@@ -1382,7 +1582,7 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
 
 
 int wpa_cipher_key_len(int cipher)
@@ -1705,3 +1905,25 @@
 		return WPA_CIPHER_CCMP_256;
 	return WPA_CIPHER_CCMP;
 }
+
+
+#ifdef CONFIG_FILS
+u16 fils_domain_name_hash(const char *domain)
+{
+	char buf[255], *wpos = buf;
+	const char *pos = domain;
+	size_t len;
+	u32 crc;
+
+	for (len = 0; len < sizeof(buf) && *pos; len++) {
+		if (isalpha(*pos) && isupper(*pos))
+			*wpos++ = tolower(*pos);
+		else
+			*wpos++ = *pos;
+		pos++;
+	}
+
+	crc = crc32((const u8 *) buf, len);
+	return crc & 0xffff;
+}
+#endif /* CONFIG_FILS */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index af1d0f0..6d28417 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -61,6 +61,10 @@
 #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 #define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
+#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
+#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
+#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
 #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
 
@@ -179,30 +183,16 @@
 	u8 key_iv[16];
 	u8 key_rsc[WPA_KEY_RSC_LEN];
 	u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
-	u8 key_mic[16];
-	u8 key_data_length[2]; /* big endian */
-	/* followed by key_data_length bytes of key_data */
-} STRUCT_PACKED;
-
-struct wpa_eapol_key_192 {
-	u8 type;
-	/* Note: key_info, key_length, and key_data_length are unaligned */
-	u8 key_info[2]; /* big endian */
-	u8 key_length[2]; /* big endian */
-	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
-	u8 key_nonce[WPA_NONCE_LEN];
-	u8 key_iv[16];
-	u8 key_rsc[WPA_KEY_RSC_LEN];
-	u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
-	u8 key_mic[24];
-	u8 key_data_length[2]; /* big endian */
-	/* followed by key_data_length bytes of key_data */
+	/* variable length Key MIC field */
+	/* big endian 2-octet Key Data Length field */
+	/* followed by Key Data Length bytes of Key Data */
 } STRUCT_PACKED;
 
 #define WPA_EAPOL_KEY_MIC_MAX_LEN 24
 #define WPA_KCK_MAX_LEN 24
-#define WPA_KEK_MAX_LEN 32
+#define WPA_KEK_MAX_LEN 64
 #define WPA_TK_MAX_LEN 32
+#define FILS_ICK_MAX_LEN 48
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -340,6 +330,15 @@
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
 		   struct wpa_ptk *ptk, int akmp, int cipher);
+int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
+		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher);
+int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
+		     const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
+		     const u8 *g_sta, size_t g_sta_len,
+		     const u8 *g_ap, size_t g_ap_len,
+		     int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
+		     size_t *key_auth_len);
 
 #ifdef CONFIG_IEEE80211R
 int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
@@ -430,6 +429,8 @@
 	size_t igtk_len;
 	const u8 *ric;
 	size_t ric_len;
+	int key_mgmt;
+	int pairwise_cipher;
 };
 
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
@@ -449,5 +450,6 @@
 int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
 int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
 unsigned int wpa_mic_len(int akmp);
+u16 fils_domain_name_hash(const char *domain);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c
index d4d874d..e27f3bb 100644
--- a/src/crypto/aes-ctr.c
+++ b/src/crypto/aes-ctr.c
@@ -1,5 +1,5 @@
 /*
- * AES-128 CTR
+ * AES-128/192/256 CTR
  *
  * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
  *
@@ -14,15 +14,16 @@
 #include "aes_wrap.h"
 
 /**
- * aes_128_ctr_encrypt - AES-128 CTR mode encryption
- * @key: Key for encryption (16 bytes)
+ * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @key_len: Length of the key (16, 24, or 32 bytes)
  * @nonce: Nonce for counter mode (16 bytes)
  * @data: Data to encrypt in-place
  * @data_len: Length of data in bytes
  * Returns: 0 on success, -1 on failure
  */
-int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
-			u8 *data, size_t data_len)
+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+		    u8 *data, size_t data_len)
 {
 	void *ctx;
 	size_t j, len, left = data_len;
@@ -30,7 +31,7 @@
 	u8 *pos = data;
 	u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
 
-	ctx = aes_encrypt_init(key, 16);
+	ctx = aes_encrypt_init(key, key_len);
 	if (ctx == NULL)
 		return -1;
 	os_memcpy(counter, nonce, AES_BLOCK_SIZE);
@@ -53,3 +54,18 @@
 	aes_encrypt_deinit(ctx);
 	return 0;
 }
+
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+			u8 *data, size_t data_len)
+{
+	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
+}
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
index 5ac82c2..2bb79b5 100644
--- a/src/crypto/aes-siv.c
+++ b/src/crypto/aes-siv.c
@@ -61,26 +61,33 @@
 }
 
 
-static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
-		   size_t *len, u8 *mac)
+static int aes_s2v(const u8 *key, size_t key_len,
+		   size_t num_elem, const u8 *addr[], size_t *len, u8 *mac)
 {
 	u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
 	u8 *buf = NULL;
 	int ret;
 	size_t i;
+	const u8 *data[1];
+	size_t data_len[1];
 
 	if (!num_elem) {
 		os_memcpy(tmp, zero, sizeof(zero));
 		tmp[AES_BLOCK_SIZE - 1] = 1;
-		return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+		data[0] = tmp;
+		data_len[0] = sizeof(tmp);
+		return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
 	}
 
-	ret = omac1_aes_128(key, zero, sizeof(zero), tmp);
+	data[0] = zero;
+	data_len[0] = sizeof(zero);
+	ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp);
 	if (ret)
 		return ret;
 
 	for (i = 0; i < num_elem - 1; i++) {
-		ret = omac1_aes_128(key, addr[i], len[i], tmp2);
+		ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i],
+				       tmp2);
 		if (ret)
 			return ret;
 
@@ -94,7 +101,8 @@
 
 		os_memcpy(buf, addr[i], len[i]);
 		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
-		ret = omac1_aes_128(key, buf, len[i], mac);
+		data[0] = buf;
+		ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac);
 		bin_clear_free(buf, len[i]);
 		return ret;
 	}
@@ -103,24 +111,32 @@
 	pad_block(tmp2, addr[i], len[i]);
 	xor(tmp, tmp2);
 
-	return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+	data[0] = tmp;
+	data_len[0] = sizeof(tmp);
+	return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
 }
 
 
-int aes_siv_encrypt(const u8 *key, const u8 *pw,
-		    size_t pwlen, size_t num_elem,
-		    const u8 *addr[], const size_t *len, u8 *out)
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+		    const u8 *pw, size_t pwlen,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out)
 {
 	const u8 *_addr[6];
 	size_t _len[6];
-	const u8 *k1 = key, *k2 = key + 16;
+	const u8 *k1, *k2;
 	u8 v[AES_BLOCK_SIZE];
 	size_t i;
 	u8 *iv, *crypt_pw;
 
-	if (num_elem > ARRAY_SIZE(_addr) - 1)
+	if (num_elem > ARRAY_SIZE(_addr) - 1 ||
+	    (key_len != 32 && key_len != 48 && key_len != 64))
 		return -1;
 
+	key_len /= 2;
+	k1 = key;
+	k2 = key + key_len;
+
 	for (i = 0; i < num_elem; i++) {
 		_addr[i] = addr[i];
 		_len[i] = len[i];
@@ -128,7 +144,7 @@
 	_addr[num_elem] = pw;
 	_len[num_elem] = pwlen;
 
-	if (aes_s2v(k1, num_elem + 1, _addr, _len, v))
+	if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v))
 		return -1;
 
 	iv = out;
@@ -140,26 +156,31 @@
 	/* zero out 63rd and 31st bits of ctr (from right) */
 	v[8] &= 0x7f;
 	v[12] &= 0x7f;
-	return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen);
+	return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen);
 }
 
 
-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+		    const u8 *iv_crypt, size_t iv_c_len,
 		    size_t num_elem, const u8 *addr[], const size_t *len,
 		    u8 *out)
 {
 	const u8 *_addr[6];
 	size_t _len[6];
-	const u8 *k1 = key, *k2 = key + 16;
+	const u8 *k1, *k2;
 	size_t crypt_len;
 	size_t i;
 	int ret;
 	u8 iv[AES_BLOCK_SIZE];
 	u8 check[AES_BLOCK_SIZE];
 
-	if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1)
+	if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 ||
+	    (key_len != 32 && key_len != 48 && key_len != 64))
 		return -1;
 	crypt_len = iv_c_len - AES_BLOCK_SIZE;
+	key_len /= 2;
+	k1 = key;
+	k2 = key + key_len;
 
 	for (i = 0; i < num_elem; i++) {
 		_addr[i] = addr[i];
@@ -174,11 +195,11 @@
 	iv[8] &= 0x7f;
 	iv[12] &= 0x7f;
 
-	ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len);
+	ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len);
 	if (ret)
 		return ret;
 
-	ret = aes_s2v(k1, num_elem + 1, _addr, _len, check);
+	ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check);
 	if (ret)
 		return ret;
 	if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h
index 463cf65..fb05d80 100644
--- a/src/crypto/aes_siv.h
+++ b/src/crypto/aes_siv.h
@@ -9,10 +9,12 @@
 #ifndef AES_SIV_H
 #define AES_SIV_H
 
-int aes_siv_encrypt(const u8 *key, const u8 *pw,
-		    size_t pwlen, size_t num_elem,
-		    const u8 *addr[], const size_t *len, u8 *out);
-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+		    const u8 *pw, size_t pwlen,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out);
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+		    const u8 *iv_crypt, size_t iv_c_len,
 		    size_t num_elem, const u8 *addr[], const size_t *len,
 		    u8 *out);
 
diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h
index 4a14209..b70b1d2 100644
--- a/src/crypto/aes_wrap.h
+++ b/src/crypto/aes_wrap.h
@@ -3,7 +3,7 @@
  *
  * - AES Key Wrap Algorithm (RFC3394)
  * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
- * - AES-128 CTR mode encryption
+ * - AES-128/192/256 CTR mode encryption
  * - AES-128 EAX mode encryption/decryption
  * - AES-128 CBC
  * - AES-GCM
@@ -33,6 +33,8 @@
 int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
 			       u8 *mac);
 int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+				 u8 *data, size_t data_len);
 int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
 				     u8 *data, size_t data_len);
 int __must_check aes_128_eax_encrypt(const u8 *key,
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index ffd2394..fb91ab4 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -92,7 +92,7 @@
 	addr[0] = ad;
 	len[0] = sizeof(ad);
 
-	if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
+	if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext),
 			    1, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
 		return 1;
@@ -103,7 +103,8 @@
 		return 1;
 	}
 
-	if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
+	if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c),
+			    1, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
 		return 1;
 	}
@@ -121,7 +122,8 @@
 	addr[2] = nonce_2;
 	len[2] = sizeof(nonce_2);
 
-	if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
+	if (aes_siv_encrypt(key_2, sizeof(key_2),
+			    plaintext_2, sizeof(plaintext_2),
 			    3, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
 		return 1;
@@ -132,7 +134,8 @@
 		return 1;
 	}
 
-	if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
+	if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2),
+			    3, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
 		return 1;
 	}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 19e0e2b..b3d1b07 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -611,7 +611,7 @@
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	DH *dh;
 	struct wpabuf *pubkey = NULL, *privkey = NULL;
 	size_t publen, privlen;
@@ -712,7 +712,7 @@
 
 void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	DH *dh;
 
 	dh = DH_new();
diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c
index 66ef331..76c4fe7 100644
--- a/src/crypto/sha512-internal.c
+++ b/src/crypto/sha512-internal.c
@@ -242,7 +242,7 @@
 		md->curlen = 0;
 	}
 
-	/* pad upto 120 bytes of zeroes
+	/* pad up to 120 bytes of zeroes
 	 * note: that from 112 to 120 is the 64 MSB of the length.  We assume
 	 * that you won't hash > 2^64 bits of data... :-)
 	 */
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 23ac64b..a7d4880 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -919,7 +919,7 @@
 		}
 #endif /* OPENSSL_FIPS */
 #endif /* CONFIG_FIPS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 		SSL_load_error_strings();
 		SSL_library_init();
 #ifndef OPENSSL_NO_SHA256
@@ -1043,7 +1043,7 @@
 
 	tls_openssl_ref_count--;
 	if (tls_openssl_ref_count == 0) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 #ifndef OPENSSL_NO_ENGINE
 		ENGINE_cleanup();
 #endif /* OPENSSL_NO_ENGINE */
@@ -2334,7 +2334,7 @@
 		return 0;
 
 #ifdef PKCS12_FUNCS
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
 	/*
 	 * Clear previously set extra chain certificates, if any, from PKCS#12
 	 * processing in tls_parse_pkcs12() to allow OpenSSL to build a new
@@ -3976,7 +3976,7 @@
 		engine_id = "pkcs11";
 
 #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	if (params->flags & TLS_CONN_EAP_FAST) {
 		wpa_printf(MSG_DEBUG,
 			   "OpenSSL: Use TLSv1_method() for EAP-FAST");
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index a449cc9..9a6db90 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -21,6 +21,9 @@
 
 #include "common/defs.h"
 #include "common/ieee802_11_defs.h"
+#ifdef CONFIG_MACSEC
+#include "pae/ieee802_1x_kay.h"
+#endif /* CONFIG_MACSEC */
 #include "utils/list.h"
 
 #define HOSTAPD_CHAN_DISABLED 0x00000001
@@ -485,17 +488,18 @@
 	int p2p;
 
 	/**
-	 * sae_data - SAE elements for Authentication frame
+	 * auth_data - Additional elements for Authentication frame
 	 *
 	 * This buffer starts with the Authentication transaction sequence
-	 * number field. If SAE is not used, this pointer is %NULL.
+	 * number field. If no special handling of such elements is needed, this
+	 * pointer is %NULL. This is used with SAE and FILS.
 	 */
-	const u8 *sae_data;
+	const u8 *auth_data;
 
 	/**
-	 * sae_data_len - Length of sae_data buffer in octets
+	 * auth_data_len - Length of auth_data buffer in octets
 	 */
-	size_t sae_data_len;
+	size_t auth_data_len;
 };
 
 /**
@@ -882,6 +886,27 @@
 	 * AP as usual. Valid for DMG network only.
 	 */
 	int pbss;
+
+	/**
+	 * fils_kek - KEK for FILS association frame protection (AES-SIV)
+	 */
+	const u8 *fils_kek;
+
+	/**
+	 * fils_kek_len: Length of fils_kek in bytes
+	 */
+	size_t fils_kek_len;
+
+	/**
+	 * fils_nonces - Nonces for FILS association frame protection
+	 * (AES-SIV AAD)
+	 */
+	const u8 *fils_nonces;
+
+	/**
+	 * fils_nonces_len: Length of fils_nonce in bytes
+	 */
+	size_t fils_nonces_len;
 };
 
 enum hide_ssid {
@@ -1286,6 +1311,8 @@
 #define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE	0x0000010000000000ULL
 /** Driver supports P2P Listen offload */
 #define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD     0x0000020000000000ULL
+/** Driver supports FILS */
+#define WPA_DRIVER_FLAGS_SUPPORT_FILS		0x0000040000000000ULL
 	u64 flags;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -3295,6 +3322,14 @@
 	int (*macsec_deinit)(void *priv);
 
 	/**
+	 * macsec_get_capability - Inform MKA of this driver's capability
+	 * @priv: Private driver interface data
+	 * @cap: Driver's capability
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
+
+	/**
 	 * enable_protect_frames - Set protect frames status
 	 * @priv: Private driver interface data
 	 * @enabled: TRUE = protect frames enabled
@@ -3333,155 +3368,129 @@
 	/**
 	 * get_receive_lowest_pn - Get receive lowest pn
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @an: association number
-	 * @lowest_pn: lowest accept pn
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
-				     u32 *lowest_pn);
+	int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * get_transmit_next_pn - Get transmit next pn
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @an: association number
-	 * @next_pn: next pn
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
-				    u32 *next_pn);
+	int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * set_transmit_next_pn - Set transmit next pn
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @an: association number
-	 * @next_pn: next pn
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
-				    u32 next_pn);
-
-	/**
-	 * get_available_receive_sc - get available receive channel
-	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * Returns: 0 on success, -1 on failure (or if not supported)
-	 */
-	int (*get_available_receive_sc)(void *priv, u32 *channel);
+	int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * create_receive_sc - create secure channel for receiving
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @sci_addr: secure channel identifier - address
-	 * @sci_port: secure channel identifier - port
+	 * @sc: secure channel
 	 * @conf_offset: confidentiality offset (0, 30, or 50)
 	 * @validation: frame validation policy (0 = Disabled, 1 = Checked,
 	 *	2 = Strict)
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
-				 u16 sci_port, unsigned int conf_offset,
+	int (*create_receive_sc)(void *priv, struct receive_sc *sc,
+				 unsigned int conf_offset,
 				 int validation);
 
 	/**
 	 * delete_receive_sc - delete secure connection for receiving
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
+	 * @sc: secure channel
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*delete_receive_sc)(void *priv, u32 channel);
+	int (*delete_receive_sc)(void *priv, struct receive_sc *sc);
 
 	/**
 	 * create_receive_sa - create secure association for receive
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
-	 * @lowest_pn: the lowest packet number can be received
-	 * @sak: the secure association key
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*create_receive_sa)(void *priv, u32 channel, u8 an,
-				 u32 lowest_pn, const u8 *sak);
+	int (*create_receive_sa)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * delete_receive_sa - Delete secure association for receive
+	 * @priv: Private driver interface data from init()
+	 * @sa: Secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_receive_sa)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * enable_receive_sa - enable the SA for receive
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
+	int (*enable_receive_sa)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * disable_receive_sa - disable SA for receive
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel index
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
-
-	/**
-	 * get_available_transmit_sc - get available transmit channel
-	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * Returns: 0 on success, -1 on failure (or if not supported)
-	 */
-	int (*get_available_transmit_sc)(void *priv, u32 *channel);
+	int (*disable_receive_sa)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * create_transmit_sc - create secure connection for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @sci_addr: secure channel identifier - address
-	 * @sci_port: secure channel identifier - port
+	 * @sc: secure channel
+	 * @conf_offset: confidentiality offset (0, 30, or 50)
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
-				  u16 sci_port, unsigned int conf_offset);
+	int (*create_transmit_sc)(void *priv, struct transmit_sc *sc,
+				  unsigned int conf_offset);
 
 	/**
 	 * delete_transmit_sc - delete secure connection for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
+	 * @sc: secure channel
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*delete_transmit_sc)(void *priv, u32 channel);
+	int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc);
 
 	/**
 	 * create_transmit_sa - create secure association for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel index
-	 * @an: association number
-	 * @next_pn: the packet number used as next transmit packet
-	 * @confidentiality: True if the SA is to provide confidentiality
-	 *                   as well as integrity
-	 * @sak: the secure association key
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
-				  Boolean confidentiality, const u8 *sak);
+	int (*create_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+	/**
+	 * delete_transmit_sa - Delete secure association for transmit
+	 * @priv: Private driver interface data from init()
+	 * @sa: Secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * enable_transmit_sa - enable SA for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
+	int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * disable_transmit_sa - disable SA for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
+	int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa);
 #endif /* CONFIG_MACSEC */
 
 	/**
@@ -3623,6 +3632,16 @@
 	 */
 	int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);
 
+	/**
+	 * set_tdls_mode - Set TDLS trigger mode to the host driver
+	 * @priv: Private driver interface data
+	 * @tdls_external_control: Represents if TDLS external trigger control
+	 *  mode is enabled/disabled.
+	 *
+	 * This optional callback can be used to configure the TDLS external
+	 * trigger control mode to the host driver.
+	 */
+	int (*set_tdls_mode)(void *priv, int tdls_external_control);
 };
 
 
@@ -4043,7 +4062,7 @@
 	 * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
 	 *
 	 * The CAC was not successful, and the channel remains in the previous
-	 * state. This may happen due to a radar beeing detected or other
+	 * state. This may happen due to a radar being detected or other
 	 * external influences.
 	 */
 	EVENT_DFS_CAC_ABORTED,
@@ -4204,6 +4223,16 @@
 		size_t resp_ies_len;
 
 		/**
+		 * resp_frame - (Re)Association Response frame
+		 */
+		const u8 *resp_frame;
+
+		/**
+		 * resp_frame_len - (Re)Association Response frame length
+		 */
+		size_t resp_frame_len;
+
+		/**
 		 * beacon_ies - Beacon or Probe Response IEs
 		 *
 		 * Optional Beacon/ProbeResp data: IEs included in Beacon or
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index 826d3cc..22d414c 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -29,6 +29,7 @@
 #include "utils/eloop.h"
 #include "common/defs.h"
 #include "common/ieee802_1x_defs.h"
+#include "pae/ieee802_1x_kay.h"
 #include "driver.h"
 
 #include "nss_macsec_secy.h"
@@ -55,6 +56,10 @@
 static const u8 pae_group_addr[ETH_ALEN] =
 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
 
+struct channel_map {
+	struct ieee802_1x_mka_sci sci;
+};
+
 struct macsec_qca_data {
 	char ifname[IFNAMSIZ + 1];
 	u32 secy_id;
@@ -71,6 +76,9 @@
 	Boolean protect_frames;
 	Boolean replay_protect;
 	u32 replay_window;
+
+	struct channel_map receive_channel_map[MAXSC];
+	struct channel_map transmit_channel_map[MAXSC];
 };
 
 
@@ -457,6 +465,16 @@
 }
 
 
+static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap)
+{
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	*cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+
+	return 0;
+}
+
+
 static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled)
 {
 	struct macsec_qca_data *drv = priv;
@@ -515,16 +533,82 @@
 }
 
 
-static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an,
-					    u32 *lowest_pn)
+static int macsec_qca_lookup_channel(struct channel_map *map,
+				     struct ieee802_1x_mka_sci *sci,
+				     u32 *channel)
+{
+	u32 i;
+
+	for (i = 0; i < MAXSC; i++) {
+		if (os_memcmp(&map[i].sci, sci,
+			      sizeof(struct ieee802_1x_mka_sci)) == 0) {
+			*channel = i;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static void macsec_qca_register_channel(struct channel_map *map,
+					struct ieee802_1x_mka_sci *sci,
+					u32 channel)
+{
+	os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci));
+}
+
+
+static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv,
+					     struct receive_sc *sc,
+					     u32 *channel)
+{
+	return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci,
+					 channel);
+}
+
+
+static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv,
+						struct receive_sc *sc,
+						u32 channel)
+{
+	macsec_qca_register_channel(drv->receive_channel_map, &sc->sci,
+				    channel);
+}
+
+
+static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv,
+					      struct transmit_sc *sc,
+					      u32 *channel)
+{
+	return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci,
+					 channel);
+}
+
+
+static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv,
+						 struct transmit_sc *sc,
+						 u32 channel)
+{
+	macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci,
+				    channel);
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
 	int ret = 0;
 	u32 next_pn = 0;
 	bool enabled = FALSE;
 	u32 win;
+	u32 channel;
 
-	ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an,
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an,
 						 &next_pn);
 	ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
 							&enabled);
@@ -532,40 +616,49 @@
 							    channel, &win);
 
 	if (enabled)
-		*lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+		sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
 	else
-		*lowest_pn = next_pn;
+		sa->lowest_pn = next_pn;
 
-	wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn);
+	wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn);
 
 	return ret;
 }
 
 
-static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an,
-					   u32 *next_pn)
+static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
 	int ret = 0;
+	u32 channel;
 
-	ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an,
-						 next_pn);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn);
+	ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an,
+						 &sa->next_pn);
+
+	wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn);
 
 	return ret;
 }
 
 
-int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn)
+int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
 	int ret = 0;
+	u32 channel;
 
-	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
-						 next_pn);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn);
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+						 sa->next_pn);
+
+	wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn);
 
 	return ret;
 }
@@ -598,8 +691,7 @@
 }
 
 
-static int macsec_qca_create_receive_sc(void *priv, u32 channel,
-					const u8 *sci_addr, u16 sci_port,
+static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc,
 					unsigned int conf_offset,
 					int validation)
 {
@@ -608,6 +700,13 @@
 	fal_rx_prc_lut_t entry;
 	fal_rx_sc_validate_frame_e vf;
 	enum validate_frames validate_frames = validation;
+	u32 channel;
+	const u8 *sci_addr = sc->sci.addr;
+	u16 sci_port = be_to_host16(sc->sci.port);
+
+	ret = macsec_qca_get_available_receive_sc(priv, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -642,15 +741,22 @@
 							    channel,
 							    drv->replay_window);
 
+	macsec_qca_register_receive_channel(drv, sc, channel);
+
 	return ret;
 }
 
 
-static int macsec_qca_delete_receive_sc(void *priv, u32 channel)
+static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_rx_prc_lut_t entry;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -664,49 +770,68 @@
 }
 
 
-static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an,
-					u32 lowest_pn, const u8 *sak)
+static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_rx_sak_t rx_sak;
 	int i = 0;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
-		   __func__, channel, an, lowest_pn);
+		   __func__, channel, sa->an, sa->lowest_pn);
 
 	os_memset(&rx_sak, 0, sizeof(rx_sak));
 	for (i = 0; i < 16; i++)
-		rx_sak.sak[i] = sak[15 - i];
+		rx_sak.sak[i] = sa->pkey->key[15 - i];
 
-	ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an);
-	ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak);
+	ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an);
+	ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an,
+					  &rx_sak);
 
 	return ret;
 }
 
 
-static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+					    TRUE);
 
 	return ret;
 }
 
 
-static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+					    FALSE);
 
 	return ret;
 }
@@ -715,14 +840,12 @@
 static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
 	u32 sc_ch = 0;
 	bool in_use = FALSE;
 
 	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
-		ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
-							&in_use);
-		if (ret)
+		if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+						      &in_use))
 			continue;
 
 		if (!in_use) {
@@ -739,14 +862,18 @@
 }
 
 
-static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
-					 const u8 *sci_addr, u16 sci_port,
+static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc,
 					 unsigned int conf_offset)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_tx_class_lut_t entry;
 	u8 psci[ETH_ALEN + 2];
+	u32 channel;
+
+	ret = macsec_qca_get_available_transmit_sc(priv, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -757,9 +884,9 @@
 	entry.action = FAL_TX_CLASS_ACTION_FORWARD;
 	entry.channel = channel;
 
-	os_memcpy(psci, sci_addr, ETH_ALEN);
-	psci[6] = (sci_port >> 8) & 0xf;
-	psci[7] = sci_port & 0xf;
+	os_memcpy(psci, sc->sci.addr, ETH_ALEN);
+	psci[6] = (sc->sci.port >> 8) & 0xf;
+	psci[7] = sc->sci.port & 0xf;
 
 	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
 	ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
@@ -769,15 +896,22 @@
 								channel,
 								conf_offset);
 
+	macsec_qca_register_transmit_channel(drv, sc, channel);
+
 	return ret;
 }
 
 
-static int macsec_qca_delete_transmit_sc(void *priv, u32 channel)
+static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_tx_class_lut_t entry;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -791,19 +925,22 @@
 }
 
 
-static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an,
-					 u32 next_pn, Boolean confidentiality,
-					 const u8 *sak)
+static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	u8 tci = 0;
 	fal_tx_sak_t tx_sak;
 	int i;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG,
 		   "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
-		   __func__, channel, an, next_pn, confidentiality);
+		   __func__, channel, sa->an, sa->next_pn, sa->confidentiality);
 
 	if (drv->always_include_sci)
 		tci |= TCI_SC;
@@ -812,45 +949,60 @@
 	else if (drv->use_scb)
 		tci |= TCI_SCB;
 
-	if (confidentiality)
+	if (sa->confidentiality)
 		tci |= TCI_E | TCI_C;
 
 	os_memset(&tx_sak, 0, sizeof(tx_sak));
 	for (i = 0; i < 16; i++)
-		tx_sak.sak[i] = sak[15 - i];
+		tx_sak.sak[i] = sa->pkey->key[15 - i];
 
-	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
-						 next_pn);
-	ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak);
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+						 sa->next_pn);
+	ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an,
+					  &tx_sak);
 	ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
 						 (tci >> 2));
-	ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an);
+	ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an);
 
 	return ret;
 }
 
 
-static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+					    TRUE);
 
 	return ret;
 }
 
 
-static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+					    FALSE);
 
 	return ret;
 }
@@ -867,6 +1019,7 @@
 
 	.macsec_init = macsec_qca_macsec_init,
 	.macsec_deinit = macsec_qca_macsec_deinit,
+	.macsec_get_capability = macsec_qca_get_capability,
 	.enable_protect_frames = macsec_qca_enable_protect_frames,
 	.set_replay_protect = macsec_qca_set_replay_protect,
 	.set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
@@ -874,13 +1027,11 @@
 	.get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
 	.get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
 	.set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
-	.get_available_receive_sc = macsec_qca_get_available_receive_sc,
 	.create_receive_sc = macsec_qca_create_receive_sc,
 	.delete_receive_sc = macsec_qca_delete_receive_sc,
 	.create_receive_sa = macsec_qca_create_receive_sa,
 	.enable_receive_sa = macsec_qca_enable_receive_sa,
 	.disable_receive_sa = macsec_qca_disable_receive_sa,
-	.get_available_transmit_sc = macsec_qca_get_available_transmit_sc,
 	.create_transmit_sc = macsec_qca_create_transmit_sc,
 	.delete_transmit_sc = macsec_qca_delete_transmit_sc,
 	.create_transmit_sa = macsec_qca_create_transmit_sa,
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 1210d43..75c4271 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -3095,11 +3095,11 @@
 	if (params->ie &&
 	    nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
 		goto fail;
-	if (params->sae_data) {
-		wpa_hexdump(MSG_DEBUG, "  * SAE data", params->sae_data,
-			    params->sae_data_len);
-		if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
-			    params->sae_data))
+	if (params->auth_data) {
+		wpa_hexdump(MSG_DEBUG, "  * auth_data", params->auth_data,
+			    params->auth_data_len);
+		if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len,
+			    params->auth_data))
 			goto fail;
 	}
 	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
@@ -3112,6 +3112,8 @@
 		type = NL80211_AUTHTYPE_FT;
 	else if (params->auth_alg & WPA_AUTH_ALG_SAE)
 		type = NL80211_AUTHTYPE_SAE;
+	else if (params->auth_alg & WPA_AUTH_ALG_FILS)
+		type = NL80211_AUTHTYPE_FILS_SK;
 	else
 		goto fail;
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
@@ -5152,6 +5154,22 @@
 	if (ret)
 		goto fail;
 
+	if (params->fils_kek) {
+		wpa_printf(MSG_DEBUG, "  * FILS KEK (len=%u)",
+			   (unsigned int) params->fils_kek_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len,
+			    params->fils_kek))
+			goto fail;
+	}
+	if (params->fils_nonces) {
+		wpa_hexdump(MSG_DEBUG, "  * FILS nonces (for AAD)",
+			    params->fils_nonces,
+			    params->fils_nonces_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_NONCES,
+			    params->fils_nonces_len, params->fils_nonces))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -9307,6 +9325,56 @@
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
+
+static int nl80211_set_tdls_mode(void *priv, int tdls_external_control)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+	u32 tdls_mode;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set TDKS mode: tdls_external_control=%d",
+		   tdls_external_control);
+
+	if (tdls_external_control == 1)
+		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT |
+			QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL;
+	else
+		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
+		goto fail;
+
+	params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!params)
+		goto fail;
+
+	if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE,
+			tdls_mode))
+		goto fail;
+
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set TDLS mode failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	return 0;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -9550,6 +9618,7 @@
 	.p2p_lo_start = nl80211_p2p_lo_start,
 	.p2p_lo_stop = nl80211_p2p_lo_stop,
 	.set_default_scan_ies = nl80211_set_default_scan_ies,
+	.set_tdls_mode = nl80211_set_tdls_mode,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
 	.get_ext_capab = nl80211_get_ext_capab,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 6adc3f6..2507a43 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -362,6 +362,9 @@
 
 	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
 		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA))
+		capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS;
 }
 
 
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 762e3ac..c77e21e 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -247,6 +247,8 @@
 	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
 
 	os_memset(&event, 0, sizeof(event));
+	event.assoc_info.resp_frame = frame;
+	event.assoc_info.resp_frame_len = len;
 	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
 		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
 		event.assoc_info.resp_ies_len =
@@ -516,6 +518,7 @@
 		data.ch_switch.cf2 = nla_get_u32(cf2);
 
 	bss->freq = data.ch_switch.freq;
+	drv->assoc_freq = data.ch_switch.freq;
 
 	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
 }
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 43d4193..5d85033 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -234,7 +234,7 @@
 		   __func__, priv, params->freq, MAC2STR(params->bssid),
 		   params->auth_alg, params->local_state_change, params->p2p);
 
-	buflen = sizeof(*data) + params->ie_len + params->sae_data_len;
+	buflen = sizeof(*data) + params->ie_len + params->auth_data_len;
 	data = os_zalloc(buflen);
 	if (data == NULL)
 		return -1;
@@ -259,8 +259,8 @@
 		os_memcpy(pos, params->ie, params->ie_len);
 		pos += params->ie_len;
 	}
-	if (params->sae_data_len)
-		os_memcpy(pos, params->sae_data, params->sae_data_len);
+	if (params->auth_data_len)
+		os_memcpy(pos, params->auth_data, params->auth_data_len);
 
 	res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
 			   NULL, NULL);
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 2206941..a268a00 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -48,6 +48,7 @@
 #define NL80211_MULTICAST_GROUP_REG		"regulatory"
 #define NL80211_MULTICAST_GROUP_MLME		"mlme"
 #define NL80211_MULTICAST_GROUP_VENDOR		"vendor"
+#define NL80211_MULTICAST_GROUP_NAN		"nan"
 #define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
 
 /**
@@ -838,6 +839,41 @@
  *	not running. The driver indicates the status of the scan through
  *	cfg80211_scan_done().
  *
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ *	%NL80211_ATTR_WDEV interface. This interface must have been previously
+ *	created with %NL80211_CMD_NEW_INTERFACE. After it has been started, the
+ *	NAN interface will create or join a cluster. This command must have a
+ *	valid %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ *	%NL80211_ATTR_NAN_DUAL attributes.
+ *	After this command NAN functions can be added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ *	its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ *	with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ *	operation returns the strictly positive and unique instance id
+ *	(%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ *	of the function upon success.
+ *	Since instance ID's can be re-used, this cookie is the right
+ *	way to identify the function. This will avoid races when a termination
+ *	event is handled by the user space after it has already added a new
+ *	function that got the same instance id from the kernel as the one
+ *	which just terminated.
+ *	This cookie may be used in NAN events even before the command
+ *	returns, so userspace shouldn't process NAN events until it processes
+ *	the response to this command.
+ *	Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ *	This command is also used as a notification sent when a NAN function is
+ *	terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ *	and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN configuration. NAN
+ *	must be operational (%NL80211_CMD_START_NAN was executed).
+ *	It must contain at least one of the following attributes:
+ *	%NL80211_ATTR_NAN_MASTER_PREF, %NL80211_ATTR_NAN_DUAL.
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ *	%NL80211_ATTR_COOKIE.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1026,6 +1062,13 @@
 
 	NL80211_CMD_ABORT_SCAN,
 
+	NL80211_CMD_START_NAN,
+	NL80211_CMD_STOP_NAN,
+	NL80211_CMD_ADD_NAN_FUNCTION,
+	NL80211_CMD_DEL_NAN_FUNCTION,
+	NL80211_CMD_CHANGE_NAN_CONFIG,
+	NL80211_CMD_NAN_MATCH,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1343,7 +1386,13 @@
  *	enum nl80211_band value is used as the index (nla_type() of the nested
  *	data. If a band is not included, it will be configured to allow all
  *	rates based on negotiated supported rates information. This attribute
- *	is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
+ *	is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP,
+ *	and joining mesh networks (not IBSS yet). In the later case, it must
+ *	specify just a single bitrate, which is to be used for the beacon.
+ *	The driver must also specify support for this with the extended
+ *	features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ *	NL80211_EXT_FEATURE_BEACON_RATE_HT and
+ *	NL80211_EXT_FEATURE_BEACON_RATE_VHT.
  *
  * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
  *	at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
@@ -1589,8 +1638,16 @@
  *	the connection request from a station. nl80211_connect_failed_reason
  *	enum has different reasons of connection failure.
  *
- * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts
- *	with the Authentication transaction sequence number field.
+ * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames.
+ *	This contains the authentication frame body (non-IE and IE data),
+ *	excluding the Authentication algorithm number, i.e., starting at the
+ *	Authentication transaction sequence number field. It is used with
+ *	authentication algorithms that need special fields to be added into
+ *	the frames (SAE and FILS). Currently, only the SAE cases use the
+ *	initial two fields (Authentication transaction sequence number and
+ *	Status code). However, those fields are included in the attribute data
+ *	for all authentication algorithms to keep the attribute definition
+ *	consistent.
  *
  * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from
  *	association request when used with NL80211_CMD_NEW_STATION)
@@ -1733,6 +1790,12 @@
  *	regulatory indoor configuration would be owned by the netlink socket
  *	that configured the indoor setting, and the indoor operation would be
  *	cleared when the socket is closed.
+ *	If set during NAN interface creation, the interface will be destroyed
+ *	if the socket is closed just like any other interface. Moreover, only
+ *	the netlink socket that created the interface will be allowed to add
+ *	and remove functions. NAN notifications will be sent in unicast to that
+ *	socket. Without this attribute, any socket can add functions and the
+ *	notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
  *
  * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
  *	the TDLS link initiator.
@@ -1867,6 +1930,26 @@
  * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
  *	used to pull the stored data for mesh peer in power save state.
  *
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ *	%NL80211_CMD_START_NAN and optionally with
+ *	%NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ *	Also, values 1 and 255 are reserved for certification purposes and
+ *	should not be used during a normal device operation.
+ * @NL80211_ATTR_NAN_DUAL: NAN dual band operation config (see
+ *	&enum nl80211_nan_dual_band_conf). This attribute is used with
+ *	%NL80211_CMD_START_NAN and optionally with
+ *	%NL80211_CMD_CHANGE_NAN_CONFIG.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ *	&enum nl80211_nan_func_attributes for description of this nested
+ *	attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ *	See &enum nl80211_nan_match_attributes.
+ * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame
+ *	protection.
+ * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association
+ *	Request/Response frame protection. This attribute contains the 16 octet
+ *	STA Nonce followed by 16 octets of AP Nonce.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2125,7 +2208,7 @@
 
 	NL80211_ATTR_CONN_FAILED_REASON,
 
-	NL80211_ATTR_SAE_DATA,
+	NL80211_ATTR_AUTH_DATA,
 
 	NL80211_ATTR_VHT_CAPABILITY,
 
@@ -2261,6 +2344,14 @@
 
 	NL80211_ATTR_MESH_PEER_AID,
 
+	NL80211_ATTR_NAN_MASTER_PREF,
+	NL80211_ATTR_NAN_DUAL,
+	NL80211_ATTR_NAN_FUNC,
+	NL80211_ATTR_NAN_MATCH,
+
+	NL80211_ATTR_FILS_KEK,
+	NL80211_ATTR_FILS_NONCES,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -2272,6 +2363,7 @@
 #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
 #define	NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
 #define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
+#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
 
 /*
  * Allow user space programs to use #ifdef on new attributes by defining them
@@ -2339,6 +2431,7 @@
  *	commands to create and destroy one
  * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
  *	This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NUM_NL80211_IFTYPES: number of defined interface types
  *
@@ -2359,6 +2452,7 @@
 	NL80211_IFTYPE_P2P_GO,
 	NL80211_IFTYPE_P2P_DEVICE,
 	NL80211_IFTYPE_OCB,
+	NL80211_IFTYPE_NAN,
 
 	/* keep last */
 	NUM_NL80211_IFTYPES,
@@ -3583,6 +3677,9 @@
  * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
  * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
  * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals
+ * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key
+ * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS
+ * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key
  * @__NL80211_AUTHTYPE_NUM: internal
  * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
  * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
@@ -3595,6 +3692,9 @@
 	NL80211_AUTHTYPE_FT,
 	NL80211_AUTHTYPE_NETWORK_EAP,
 	NL80211_AUTHTYPE_SAE,
+	NL80211_AUTHTYPE_FILS_SK,
+	NL80211_AUTHTYPE_FILS_SK_PFS,
+	NL80211_AUTHTYPE_FILS_PK,
 
 	/* keep last */
 	__NL80211_AUTHTYPE_NUM,
@@ -4203,6 +4303,9 @@
  *	of supported channel widths for radar detection.
  * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
  *	of supported regulatory regions for radar detection.
+ * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of
+ *	different beacon intervals supported by all the interface combinations
+ *	in this group (if not present, all beacon intervals be identical).
  * @NUM_NL80211_IFACE_COMB: number of attributes
  * @MAX_NL80211_IFACE_COMB: highest attribute number
  *
@@ -4210,8 +4313,8 @@
  *	limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
  *	=> allows an AP and a STA that must match BIs
  *
- *	numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8
- *	=> allows 8 of AP/GO
+ *	numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8,
+ *	=> allows 8 of AP/GO that can have BI gcd >= min gcd
  *
  *	numbers = [ #{STA} <= 2 ], channels = 2, max = 2
  *	=> allows two STAs on different channels
@@ -4237,6 +4340,7 @@
 	NL80211_IFACE_COMB_NUM_CHANNELS,
 	NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
 	NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+	NL80211_IFACE_COMB_BI_MIN_GCD,
 
 	/* keep last */
 	NUM_NL80211_IFACE_COMB,
@@ -4551,6 +4655,14 @@
  *	(if available).
  * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
  *	channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ *	configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ *	configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ *	configuration (AP/mesh) with VHT rates.
+ * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
+ *	with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4562,6 +4674,10 @@
 	NL80211_EXT_FEATURE_SCAN_START_TIME,
 	NL80211_EXT_FEATURE_BSS_PARENT_TSF,
 	NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+	NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+	NL80211_EXT_FEATURE_BEACON_RATE_HT,
+	NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+	NL80211_EXT_FEATURE_FILS_STA,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -4855,4 +4971,186 @@
 	NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_nan_dual_band_conf - NAN dual band configuration
+ *
+ * Defines the NAN dual band mode of operation
+ *
+ * @NL80211_NAN_BAND_DEFAULT: device default mode
+ * @NL80211_NAN_BAND_2GHZ: 2.4GHz mode
+ * @NL80211_NAN_BAND_5GHZ: 5GHz mode
+  */
+enum nl80211_nan_dual_band_conf {
+	NL80211_NAN_BAND_DEFAULT	= 1 << 0,
+	NL80211_NAN_BAND_2GHZ		= 1 << 1,
+	NL80211_NAN_BAND_5GHZ		= 1 << 2,
+};
+
+/**
+ * enum nl80211_nan_function_type - NAN function type
+ *
+ * Defines the function type of a NAN function
+ *
+ * @NL80211_NAN_FUNC_PUBLISH: function is publish
+ * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+ * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
+ */
+enum nl80211_nan_function_type {
+	NL80211_NAN_FUNC_PUBLISH,
+	NL80211_NAN_FUNC_SUBSCRIBE,
+	NL80211_NAN_FUNC_FOLLOW_UP,
+
+	/* keep last */
+	__NL80211_NAN_FUNC_TYPE_AFTER_LAST,
+	NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum nl80211_nan_publish_type - NAN publish tx type
+ *
+ * Defines how to send publish Service Discovery Frames
+ *
+ * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited
+ * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited
+ */
+enum nl80211_nan_publish_type {
+	NL80211_NAN_SOLICITED_PUBLISH = 1 << 0,
+	NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1,
+};
+
+/**
+ * enum nl80211_nan_func_term_reason - NAN functions termination reason
+ *
+ * Defines termination reasons of a NAN function
+ *
+ * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user
+ * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout
+ * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored
+ */
+enum nl80211_nan_func_term_reason {
+	NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST,
+	NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED,
+	NL80211_NAN_FUNC_TERM_REASON_ERROR,
+};
+
+#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6
+#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff
+#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff
+
+/**
+ * enum nl80211_nan_func_attributes - NAN function attributes
+ * @__NL80211_NAN_FUNC_INVALID: invalid
+ * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8).
+ * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as
+ *	specified in NAN spec. This is a binary attribute.
+ * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is
+ *	publish. Defines the transmission type for the publish Service Discovery
+ *	Frame, see &enum nl80211_nan_publish_type. Its type is u8.
+ * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited
+ *	publish. Should the solicited publish Service Discovery Frame be sent to
+ *	the NAN Broadcast address. This is a flag.
+ * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is
+ *	subscribe. Is the subscribe active. This is a flag.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up.
+ *	The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ *	is follow up. This is a u8.
+ *	The requestor instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ *	follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+ *	close range. The range itself (RSSI) is defined by the device.
+ *	This is a flag.
+ * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should
+ *	stay active. If not present infinite TTL is assumed. This is a u32.
+ * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service
+ *	specific info. This is a binary attribute.
+ * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute.
+ *	See &enum nl80211_nan_srf_attributes.
+ * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested
+ *	attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a
+ *	nested attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function.
+ *	Its type is u8 and it cannot be 0.
+ * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason.
+ *	See &enum nl80211_nan_func_term_reason.
+ *
+ * @NUM_NL80211_NAN_FUNC_ATTR: internal
+ * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute
+ */
+enum nl80211_nan_func_attributes {
+	__NL80211_NAN_FUNC_INVALID,
+	NL80211_NAN_FUNC_TYPE,
+	NL80211_NAN_FUNC_SERVICE_ID,
+	NL80211_NAN_FUNC_PUBLISH_TYPE,
+	NL80211_NAN_FUNC_PUBLISH_BCAST,
+	NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE,
+	NL80211_NAN_FUNC_FOLLOW_UP_ID,
+	NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID,
+	NL80211_NAN_FUNC_FOLLOW_UP_DEST,
+	NL80211_NAN_FUNC_CLOSE_RANGE,
+	NL80211_NAN_FUNC_TTL,
+	NL80211_NAN_FUNC_SERVICE_INFO,
+	NL80211_NAN_FUNC_SRF,
+	NL80211_NAN_FUNC_RX_MATCH_FILTER,
+	NL80211_NAN_FUNC_TX_MATCH_FILTER,
+	NL80211_NAN_FUNC_INSTANCE_ID,
+	NL80211_NAN_FUNC_TERM_REASON,
+
+	/* keep last */
+	NUM_NL80211_NAN_FUNC_ATTR,
+	NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1
+};
+
+/**
+ * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes
+ * @__NL80211_NAN_SRF_INVALID: invalid
+ * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
+ *	This is a flag.
+ * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
+ *	&NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
+ *	&NL80211_NAN_SRF_BF is present. This is a u8.
+ * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
+ *	and only if &NL80211_NAN_SRF_BF isn't present. This is a nested
+ *	attribute. Each nested attribute is a MAC address.
+ * @NUM_NL80211_NAN_SRF_ATTR: internal
+ * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
+ */
+enum nl80211_nan_srf_attributes {
+	__NL80211_NAN_SRF_INVALID,
+	NL80211_NAN_SRF_INCLUDE,
+	NL80211_NAN_SRF_BF,
+	NL80211_NAN_SRF_BF_IDX,
+	NL80211_NAN_SRF_MAC_ADDRS,
+
+	/* keep last */
+	NUM_NL80211_NAN_SRF_ATTR,
+	NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1,
+};
+
+/**
+ * enum nl80211_nan_match_attributes - NAN match attributes
+ * @__NL80211_NAN_MATCH_INVALID: invalid
+ * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the
+ *	match. This is a nested attribute.
+ *	See &enum nl80211_nan_func_attributes.
+ * @NL80211_NAN_MATCH_FUNC_PEER: the peer function
+ *	that caused the match. This is a nested attribute.
+ *	See &enum nl80211_nan_func_attributes.
+ *
+ * @NUM_NL80211_NAN_MATCH_ATTR: internal
+ * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute
+ */
+enum nl80211_nan_match_attributes {
+	__NL80211_NAN_MATCH_INVALID,
+	NL80211_NAN_MATCH_FUNC_LOCAL,
+	NL80211_NAN_MATCH_FUNC_PEER,
+
+	/* keep last */
+	NUM_NL80211_NAN_MATCH_ATTR,
+	NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 9110ca5..1c6116a 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -571,8 +571,7 @@
 
 
 #ifdef CONFIG_ERP
-static int eap_peer_erp_reauth_start(struct eap_sm *sm,
-				     const struct eap_hdr *hdr, size_t len)
+struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id)
 {
 	char *realm;
 	struct eap_erp_key *erp;
@@ -581,16 +580,16 @@
 
 	realm = eap_home_realm(sm);
 	if (!realm)
-		return -1;
+		return NULL;
 
 	erp = eap_erp_get_key(sm, realm);
 	os_free(realm);
 	realm = NULL;
 	if (!erp)
-		return -1;
+		return NULL;
 
 	if (erp->next_seq >= 65536)
-		return -1; /* SEQ has range of 0..65535 */
+		return NULL; /* SEQ has range of 0..65535 */
 
 	/* TODO: check rRK lifetime expiration */
 
@@ -599,9 +598,9 @@
 
 	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
 			    1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
-			    EAP_CODE_INITIATE, hdr->identifier);
+			    EAP_CODE_INITIATE, eap_id);
 	if (msg == NULL)
-		return -1;
+		return NULL;
 
 	wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
 	wpabuf_put_be16(msg, erp->next_seq);
@@ -615,13 +614,28 @@
 	if (hmac_sha256(erp->rIK, erp->rIK_len,
 			wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
 		wpabuf_free(msg);
-		return -1;
+		return NULL;
 	}
 	wpabuf_put_data(msg, hash, 16);
 
-	wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
 	sm->erp_seq = erp->next_seq;
 	erp->next_seq++;
+
+	wpa_hexdump_buf(MSG_DEBUG, "ERP: EAP-Initiate/Re-auth", msg);
+
+	return msg;
+}
+
+
+static int eap_peer_erp_reauth_start(struct eap_sm *sm, u8 eap_id)
+{
+	struct wpabuf *msg;
+
+	msg = eap_peer_build_erp_reauth_start(sm, eap_id);
+	if (!msg)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
 	wpabuf_free(sm->eapRespData);
 	sm->eapRespData = msg;
 	sm->reauthInit = TRUE;
@@ -1566,7 +1580,7 @@
 		/* TODO: Derivation of domain specific keys for local ER */
 	}
 
-	if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+	if (eap_peer_erp_reauth_start(sm, hdr->identifier) == 0)
 		return;
 
 invalid:
@@ -1577,8 +1591,7 @@
 }
 
 
-static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
-			    size_t len)
+void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len)
 {
 #ifdef CONFIG_ERP
 	const u8 *pos = (const u8 *) (hdr + 1);
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index 1a645af..7b013ee 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -348,6 +348,8 @@
 void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
 int eap_peer_was_failure_expected(struct eap_sm *sm);
 void eap_peer_erp_free_keys(struct eap_sm *sm);
+struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id);
+void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len);
 
 #endif /* IEEE8021X_EAPOL */
 
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index d2bc981..662347b 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -345,7 +345,7 @@
 	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
 	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
 	wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
-	wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
+	wpabuf_put_u8(data->outbuf, id->prep);
 	wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
 
 	eap_pwd_state(data, PWD_Commit_Req);
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 69eaab8..93eab62 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -153,5 +153,6 @@
 void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
 				   const u8 *username, size_t username_len,
 				   const u8 *challenge, const u8 *response);
+void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len);
 
 #endif /* EAP_H */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 84ecafc..08cc171 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1968,6 +1968,44 @@
 }
 
 
+void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len)
+{
+#ifdef CONFIG_ERP
+	const struct eap_hdr *hdr;
+	const u8 *pos, *end;
+	struct erp_tlvs parse;
+
+	if (len < sizeof(*hdr) + 1)
+		return;
+	hdr = (const struct eap_hdr *) eap;
+	end = eap + len;
+	pos = (const u8 *) (hdr + 1);
+	if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH)
+		return;
+	pos++;
+	if (pos + 3 > end)
+		return;
+
+	/* Skip Flags and SEQ */
+	pos += 3;
+
+	if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname)
+		return;
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI",
+			  parse.keyname, parse.keyname_len);
+	os_free(sm->identity);
+	sm->identity = os_malloc(parse.keyname_len);
+	if (sm->identity) {
+		os_memcpy(sm->identity, parse.keyname, parse.keyname_len);
+		sm->identity_len = parse.keyname_len;
+	} else {
+		sm->identity_len = 0;
+	}
+#endif /* CONFIG_ERP */
+}
+
+
 /**
  * eap_get_interface - Get pointer to EAP-EAPOL interface data
  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 64bf708..c60539f 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -602,7 +602,9 @@
 	if ((data->group_num != be_to_host16(id->group_num)) ||
 	    (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
 	    (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
-	    (id->prf != EAP_PWD_DEFAULT_PRF)) {
+	    (id->prf != EAP_PWD_DEFAULT_PRF) ||
+	    id->prep !=
+	    data->password_hash ? EAP_PWD_PREP_MS : EAP_PWD_PREP_NONE) {
 		wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
 		eap_pwd_state(data, FAILURE);
 		return;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 65460fc..7fdb277 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -2158,3 +2158,26 @@
 	if (sm)
 		eap_peer_erp_free_keys(sm->eap);
 }
+
+
+struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return NULL;
+	return eap_peer_build_erp_reauth_start(sm->eap, 0);
+#else /* CONFIG_ERP */
+	return NULL;
+#endif /* CONFIG_ERP */
+}
+
+
+void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
+				 size_t len)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return;
+	eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len);
+#endif /* CONFIG_ERP */
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 1309ff7..b48cab2 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -328,6 +328,9 @@
 			     struct ext_password_data *ext);
 int eapol_sm_failed(struct eapol_sm *sm);
 void eapol_sm_erp_flush(struct eapol_sm *sm);
+struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm);
+void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
+				 size_t len);
 int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
@@ -438,6 +441,15 @@
 static inline void eapol_sm_erp_flush(struct eapol_sm *sm)
 {
 }
+static inline struct wpabuf *
+eapol_sm_build_erp_reauth_start(struct eapol_sm *sm)
+{
+	return NULL;
+}
+static inline void eapol_sm_process_erp_finish(struct eapol_sm *sm,
+					       const u8 *buf, size_t len)
+{
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 996b4e8..cd2fba3 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -2822,6 +2822,7 @@
 	}
 
 	p2p->p2ps_adv_list = NULL;
+	p2ps_prov_free(p2p);
 	p2p_dbg(p2p, "All ASP advertisements flushed");
 }
 
@@ -3022,6 +3023,8 @@
 	os_free(p2p->after_scan_tx);
 	p2p->after_scan_tx = NULL;
 	p2p->ssid_set = 0;
+	p2ps_prov_free(p2p);
+	p2p_reset_pending_pd(p2p);
 }
 
 
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 5b5a0bf..7b18dcf 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -2285,7 +2285,7 @@
  * discovery (p2p_find). A random number of 100 TU units is picked for each
  * Listen state iteration from [min_disc_int,max_disc_int] range.
  *
- * max_disc_tu can be used to futher limit the discoverable duration. However,
+ * max_disc_tu can be used to further limit the discoverable duration. However,
  * it should be noted that use of this parameter is not recommended since it
  * would not be compliant with the P2P specification.
  */
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 93a0535..3994ec0 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -1163,6 +1163,9 @@
 					msg.group_id, msg.group_id_len);
 	}
 
+	if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+		p2ps_prov_free(p2p);
+
 	if (reject == P2P_SC_SUCCESS) {
 		switch (config_methods) {
 		case WPS_CONFIG_DISPLAY:
@@ -1581,7 +1584,7 @@
 					 report_config_methods);
 
 	if (p2p->state == P2P_PD_DURING_FIND) {
-		p2p_clear_timeout(p2p);
+		p2p_stop_listen_for_freq(p2p, 0);
 		p2p_continue_find(p2p);
 	}
 }
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index a8e7efc..63bbd13 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -411,6 +411,8 @@
 }
 
 
+static void ieee802_1x_kay_use_data_key(struct data_key *pkey);
+
 /**
  * ieee802_1x_kay_init_receive_sa -
  */
@@ -429,6 +431,7 @@
 		return NULL;
 	}
 
+	ieee802_1x_kay_use_data_key(key);
 	psa->pkey = key;
 	psa->lowest_pn = lowest_pn;
 	psa->next_pn = lowest_pn;
@@ -440,18 +443,21 @@
 
 	dl_list_add(&psc->sa_list, &psa->list);
 	wpa_printf(MSG_DEBUG,
-		   "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC(channel: %d)",
-		   an, lowest_pn, psc->channel);
+		   "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC",
+		   an, lowest_pn);
 
 	return psa;
 }
 
 
+static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey);
+
 /**
  * ieee802_1x_kay_deinit_receive_sa -
  */
 static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
 {
+	ieee802_1x_kay_deinit_data_key(psa->pkey);
 	psa->pkey = NULL;
 	wpa_printf(MSG_DEBUG,
 		   "KaY: Delete receive SA(an: %hhu) of SC",
@@ -465,8 +471,7 @@
  * ieee802_1x_kay_init_receive_sc -
  */
 static struct receive_sc *
-ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci,
-			       int channel)
+ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci)
 {
 	struct receive_sc *psc;
 
@@ -480,19 +485,27 @@
 	}
 
 	os_memcpy(&psc->sci, psci, sizeof(psc->sci));
-	psc->channel = channel;
 
 	os_get_time(&psc->created_time);
 	psc->receiving = FALSE;
 
 	dl_list_init(&psc->sa_list);
-	wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel);
+	wpa_printf(MSG_DEBUG, "KaY: Create receive SC");
 	wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci));
 
 	return psc;
 }
 
 
+static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay,
+					 struct receive_sa *sa)
+{
+	secy_disable_receive_sa(kay, sa);
+	secy_delete_receive_sa(kay, sa);
+	ieee802_1x_kay_deinit_receive_sa(sa);
+}
+
+
 /**
  * ieee802_1x_kay_deinit_receive_sc -
  **/
@@ -502,13 +515,11 @@
 {
 	struct receive_sa *psa, *pre_sa;
 
-	wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)",
-		   psc->channel);
+	wpa_printf(MSG_DEBUG, "KaY: Delete receive SC");
 	dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
-			      list)  {
-		secy_disable_receive_sa(participant->kay, psa);
-		ieee802_1x_kay_deinit_receive_sa(psa);
-	}
+			      list)
+		ieee802_1x_delete_receive_sa(participant->kay, psa);
+
 	dl_list_del(&psc->list);
 	os_free(psc);
 }
@@ -552,7 +563,6 @@
 {
 	struct ieee802_1x_kay_peer *peer;
 	struct receive_sc *rxsc;
-	u32 sc_ch = 0;
 
 	peer = ieee802_1x_kay_create_peer(mi, mn);
 	if (!peer)
@@ -561,9 +571,7 @@
 	os_memcpy(&peer->sci, &participant->current_peer_sci,
 		  sizeof(peer->sci));
 
-	secy_get_available_receive_sc(participant->kay, &sc_ch);
-
-	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci);
 	if (!rxsc) {
 		os_free(peer);
 		return NULL;
@@ -611,12 +619,10 @@
 {
 	struct ieee802_1x_kay_peer *peer;
 	struct receive_sc *rxsc;
-	u32 sc_ch = 0;
 
 	peer = ieee802_1x_kay_get_potential_peer(participant, mi);
 
-	rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci,
-					      sc_ch);
+	rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
 	if (!rxsc)
 		return NULL;
 
@@ -631,8 +637,6 @@
 	dl_list_del(&peer->list);
 	dl_list_add_tail(&participant->live_peers, &peer->list);
 
-	secy_get_available_receive_sc(participant->kay, &sc_ch);
-
 	dl_list_add(&participant->rxsc_list, &rxsc->list);
 	secy_create_receive_sc(participant->kay, rxsc);
 
@@ -1614,6 +1618,7 @@
 	sa_key->an = body->dan;
 	ieee802_1x_kay_init_data_key(sa_key);
 
+	ieee802_1x_kay_use_data_key(sa_key);
 	dl_list_add(&participant->sak_list, &sa_key->list);
 
 	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
@@ -1875,7 +1880,17 @@
 
 
 /**
- * ieee802_1x_kay_deinit_data_key -
+ * ieee802_1x_kay_use_data_key - Take reference on a key
+ */
+static void ieee802_1x_kay_use_data_key(struct data_key *pkey)
+{
+	pkey->user++;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_data_key - Release reference on a key and
+ * free if there are no remaining users
  */
 static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
 {
@@ -1886,7 +1901,6 @@
 	if (pkey->user > 1)
 		return;
 
-	dl_list_del(&pkey->list);
 	os_free(pkey->key);
 	os_free(pkey);
 }
@@ -1996,7 +2010,9 @@
 
 	participant->new_key = sa_key;
 
+	ieee802_1x_kay_use_data_key(sa_key);
 	dl_list_add(&participant->sak_list, &sa_key->list);
+
 	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
 	ieee802_1x_cp_sm_step(kay->cp);
 	ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality);
@@ -2280,6 +2296,16 @@
 
 
 static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
+
+static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,
+					  struct transmit_sa *sa)
+{
+	secy_disable_transmit_sa(kay, sa);
+	secy_delete_transmit_sa(kay, sa);
+	ieee802_1x_kay_deinit_transmit_sa(sa);
+}
+
+
 /**
  * ieee802_1x_participant_timer -
  */
@@ -2354,8 +2380,7 @@
 			dl_list_for_each_safe(txsa, pre_txsa,
 					      &participant->txsc->sa_list,
 					      struct transmit_sa, list) {
-				secy_disable_transmit_sa(kay, txsa);
-				ieee802_1x_kay_deinit_transmit_sa(txsa);
+				ieee802_1x_delete_transmit_sa(kay, txsa);
 			}
 
 			ieee802_1x_cp_connect_authenticated(kay->cp);
@@ -2429,6 +2454,7 @@
 		psa->confidentiality = FALSE;
 
 	psa->an = an;
+	ieee802_1x_kay_use_data_key(key);
 	psa->pkey = key;
 	psa->next_pn = next_PN;
 	psa->sc = psc;
@@ -2438,8 +2464,8 @@
 
 	dl_list_add(&psc->sa_list, &psa->list);
 	wpa_printf(MSG_DEBUG,
-		   "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC(channel: %d)",
-		   an, next_PN, psc->channel);
+		   "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC",
+		   an, next_PN);
 
 	return psa;
 }
@@ -2450,6 +2476,7 @@
  */
 static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
 {
+	ieee802_1x_kay_deinit_data_key(psa->pkey);
 	psa->pkey = NULL;
 	wpa_printf(MSG_DEBUG,
 		   "KaY: Delete transmit SA(an: %hhu) of SC",
@@ -2463,8 +2490,7 @@
  * init_transmit_sc -
  */
 static struct transmit_sc *
-ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
-				int channel)
+ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
 {
 	struct transmit_sc *psc;
 
@@ -2474,7 +2500,6 @@
 		return NULL;
 	}
 	os_memcpy(&psc->sci, sci, sizeof(psc->sci));
-	psc->channel = channel;
 
 	os_get_time(&psc->created_time);
 	psc->transmitting = FALSE;
@@ -2482,7 +2507,7 @@
 	psc->enciphering_sa = FALSE;
 
 	dl_list_init(&psc->sa_list);
-	wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel);
+	wpa_printf(MSG_DEBUG, "KaY: Create transmit SC");
 	wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci));
 
 	return psc;
@@ -2498,13 +2523,9 @@
 {
 	struct transmit_sa *psa, *tmp;
 
-	wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)",
-		   psc->channel);
-	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa,
-			      list) {
-		secy_disable_transmit_sa(participant->kay, psa);
-		ieee802_1x_kay_deinit_transmit_sa(psa);
-	}
+	wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC");
+	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
+		ieee802_1x_delete_transmit_sa(participant->kay, psa);
 
 	os_free(psc);
 }
@@ -2582,6 +2603,32 @@
 }
 
 
+static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an)
+{
+	struct transmit_sa *txsa;
+
+	dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) {
+		if (txsa->an == an)
+			return txsa;
+	}
+
+	return NULL;
+}
+
+
+static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an)
+{
+	struct receive_sa *rxsa;
+
+	dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
+		if (rxsa->an == an)
+			return rxsa;
+	}
+
+	return NULL;
+}
+
+
 /**
  * ieee802_1x_kay_create_sas -
  */
@@ -2616,6 +2663,9 @@
 	}
 
 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+		while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL)
+			ieee802_1x_delete_receive_sa(kay, rxsa);
+
 		rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
 						      latest_sak);
 		if (!rxsa)
@@ -2624,6 +2674,10 @@
 		secy_create_receive_sa(kay, rxsa);
 	}
 
+	while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) !=
+	       NULL)
+		ieee802_1x_delete_transmit_sa(kay, txsa);
+
 	txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
 					       1, latest_sak);
 	if (!txsa)
@@ -2657,20 +2711,16 @@
 	/* remove the transmit sa */
 	dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
 			      struct transmit_sa, list) {
-		if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
-			secy_disable_transmit_sa(kay, txsa);
-			ieee802_1x_kay_deinit_transmit_sa(txsa);
-		}
+		if (is_ki_equal(&txsa->pkey->key_identifier, ki))
+			ieee802_1x_delete_transmit_sa(kay, txsa);
 	}
 
 	/* remove the receive sa */
 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
 		dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
 				      struct receive_sa, list) {
-			if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
-				secy_disable_receive_sa(kay, rxsa);
-				ieee802_1x_kay_deinit_receive_sa(rxsa);
-			}
+			if (is_ki_equal(&rxsa->pkey->key_identifier, ki))
+				ieee802_1x_delete_receive_sa(kay, rxsa);
 		}
 	}
 
@@ -2678,6 +2728,7 @@
 	dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
 			      struct data_key, list) {
 		if (is_ki_equal(&sa_key->key_identifier, ki)) {
+			dl_list_del(&sa_key->list);
 			ieee802_1x_kay_deinit_data_key(sa_key);
 			break;
 		}
@@ -3069,20 +3120,26 @@
 		kay->macsec_replay_window = 0;
 		kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
 	} else {
-		kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+		if (secy_get_capability(kay, &kay->macsec_capable) < 0) {
+			os_free(kay);
+			return NULL;
+		}
+
 		kay->macsec_desired = TRUE;
 		kay->macsec_protect = TRUE;
 		kay->macsec_validate = Strict;
 		kay->macsec_replay_protect = FALSE;
 		kay->macsec_replay_window = 0;
-		kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+		if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF)
+			kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+		else
+			kay->macsec_confidentiality = MACSEC_CAP_INTEGRITY;
 	}
 
 	wpa_printf(MSG_DEBUG, "KaY: state machine created");
 
 	/* Initialize the SecY must be prio to CP, as CP will control SecY */
 	secy_init_macsec(kay);
-	secy_get_available_transmit_sc(kay, &kay->sc_ch);
 
 	wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
 
@@ -3243,8 +3300,7 @@
 	dl_list_init(&participant->sak_list);
 	participant->new_key = NULL;
 	dl_list_init(&participant->rxsc_list);
-	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci,
-							    kay->sc_ch);
+	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci);
 	secy_cp_control_protect_frames(kay, kay->macsec_protect);
 	secy_cp_control_replay(kay, kay->macsec_replay_protect,
 			       kay->macsec_replay_window);
@@ -3340,8 +3396,7 @@
 		sak = dl_list_entry(participant->sak_list.next,
 				    struct data_key, list);
 		dl_list_del(&sak->list);
-		os_free(sak->key);
-		os_free(sak);
+		ieee802_1x_kay_deinit_data_key(sak);
 	}
 	while (!dl_list_empty(&participant->rxsc_list)) {
 		rxsc = dl_list_entry(participant->rxsc_list.next,
@@ -3409,6 +3464,7 @@
 				   unsigned int cs_index)
 {
 	struct ieee802_1x_mka_participant *participant;
+	enum macsec_cap secy_cap;
 
 	if (!kay)
 		return -1;
@@ -3427,6 +3483,12 @@
 	kay->macsec_csindex = cs_index;
 	kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
 
+	if (secy_get_capability(kay, &secy_cap) < 0)
+		return -3;
+
+	if (kay->macsec_capable > secy_cap)
+		kay->macsec_capable = secy_cap;
+
 	participant = ieee802_1x_kay_get_principal_participant(kay);
 	if (participant) {
 		wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index afbaa33..576a8a0 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -15,7 +15,7 @@
 
 struct macsec_init_params;
 
-#define MI_LEN			12
+#define MI_LEN			12  /* 96-bit Member Identifier */
 #define MAX_KEY_LEN		32  /* 32 bytes, 256 bits */
 #define MAX_CKN_LEN		32  /* 32 bytes, 256 bits */
 
@@ -24,6 +24,12 @@
 #define MKA_LIFE_TIME		6000
 #define MKA_SAK_RETIRE_TIME	3000
 
+/**
+ * struct ieee802_1x_mka_ki - Key Identifier (KI)
+ * @mi: Key Server's Member Identifier
+ * @kn: Key Number, assigned by the Key Server
+ * IEEE 802.1X-2010 9.8 SAK generation, distribution, and selection
+ */
 struct ieee802_1x_mka_ki {
 	u8 mi[MI_LEN];
 	u32 kn;
@@ -49,6 +55,84 @@
 	EAP_EXCHANGE,
 };
 
+struct data_key {
+	u8 *key;
+	int key_len;
+	struct ieee802_1x_mka_ki key_identifier;
+	enum confidentiality_offset confidentiality_offset;
+	u8 an;
+	Boolean transmits;
+	Boolean receives;
+	struct os_time created_time;
+	u32 next_pn;
+
+	/* not defined data */
+	Boolean rx_latest;
+	Boolean tx_latest;
+
+	int user;
+
+	struct dl_list list;
+};
+
+/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sc {
+	struct ieee802_1x_mka_sci sci; /* const SCI sci */
+	Boolean transmitting; /* bool transmitting (read only) */
+
+	struct os_time created_time; /* Time createdTime */
+
+	u8 encoding_sa; /* AN encodingSA (read only) */
+	u8 enciphering_sa; /* AN encipheringSA (read only) */
+
+	/* not defined data */
+	struct dl_list list;
+	struct dl_list sa_list;
+};
+
+/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sa {
+	Boolean in_use; /* bool inUse (read only) */
+	u32 next_pn; /* PN nextPN (read only) */
+	struct os_time created_time; /* Time createdTime */
+
+	Boolean enable_transmit; /* bool EnableTransmit */
+
+	u8 an;
+	Boolean confidentiality;
+	struct data_key *pkey;
+
+	struct transmit_sc *sc;
+	struct dl_list list; /* list entry in struct transmit_sc::sa_list */
+};
+
+/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sc {
+	struct ieee802_1x_mka_sci sci; /* const SCI sci */
+	Boolean receiving; /* bool receiving (read only) */
+
+	struct os_time created_time; /* Time createdTime */
+
+	struct dl_list list;
+	struct dl_list sa_list;
+};
+
+/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sa {
+	Boolean enable_receive; /* bool enableReceive */
+	Boolean in_use; /* bool inUse (read only) */
+
+	u32 next_pn; /* PN nextPN (read only) */
+	u32 lowest_pn; /* PN lowestPN (read only) */
+	u8 an;
+	struct os_time created_time;
+
+	struct data_key *pkey;
+	struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
+
+	struct dl_list list;
+};
+
 struct ieee802_1x_kay_ctx {
 	/* pointer to arbitrary upper level context */
 	void *ctx;
@@ -56,34 +140,29 @@
 	/* abstract wpa driver interface */
 	int (*macsec_init)(void *ctx, struct macsec_init_params *params);
 	int (*macsec_deinit)(void *ctx);
+	int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
 	int (*enable_protect_frames)(void *ctx, Boolean enabled);
 	int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window);
 	int (*set_current_cipher_suite)(void *ctx, u64 cs);
 	int (*enable_controlled_port)(void *ctx, Boolean enabled);
-	int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an,
-				     u32 *lowest_pn);
-	int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an,
-				    u32 *next_pn);
-	int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn);
-	int (*get_available_receive_sc)(void *ctx, u32 *channel);
-	int (*create_receive_sc)(void *ctx, u32 channel,
-				 struct ieee802_1x_mka_sci *sci,
+	int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
+	int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+	int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+	int (*create_receive_sc)(void *ctx, struct receive_sc *sc,
 				 enum validate_frames vf,
 				 enum confidentiality_offset co);
-	int (*delete_receive_sc)(void *ctx, u32 channel);
-	int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn,
-				 const u8 *sak);
-	int (*enable_receive_sa)(void *ctx, u32 channel, u8 an);
-	int (*disable_receive_sa)(void *ctx, u32 channel, u8 an);
-	int (*get_available_transmit_sc)(void *ctx, u32 *channel);
-	int (*create_transmit_sc)(void *ctx, u32 channel,
-				  const struct ieee802_1x_mka_sci *sci,
+	int (*delete_receive_sc)(void *ctx, struct receive_sc *sc);
+	int (*create_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*delete_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*enable_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*disable_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*create_transmit_sc)(void *ctx, struct transmit_sc *sc,
 				  enum confidentiality_offset co);
-	int (*delete_transmit_sc)(void *ctx, u32 channel);
-	int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn,
-				  Boolean confidentiality, const u8 *sak);
-	int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an);
-	int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an);
+	int (*delete_transmit_sc)(void *ctx, struct transmit_sc *sc);
+	int (*create_transmit_sa)(void *ctx, struct transmit_sa *sa);
+	int (*delete_transmit_sa)(void *ctx, struct transmit_sa *sa);
+	int (*enable_transmit_sa)(void *ctx, struct transmit_sa *sa);
+	int (*disable_transmit_sa)(void *ctx, struct transmit_sa *sa);
 };
 
 struct ieee802_1x_kay {
@@ -132,7 +211,6 @@
 
 	u8 mka_version;
 	u8 algo_agility[4];
-	u32 sc_ch;
 
 	u32 pn_exhaustion;
 	Boolean port_enable;
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
index 622282e..0c4bb8e 100644
--- a/src/pae/ieee802_1x_kay_i.h
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -54,88 +54,6 @@
 	struct dl_list list;
 };
 
-struct data_key {
-	u8 *key;
-	int key_len;
-	struct ieee802_1x_mka_ki key_identifier;
-	enum confidentiality_offset confidentiality_offset;
-	u8 an;
-	Boolean transmits;
-	Boolean receives;
-	struct os_time created_time;
-	u32 next_pn;
-
-	/* not defined data */
-	Boolean rx_latest;
-	Boolean tx_latest;
-
-	int user;  /* FIXME: to indicate if it can be delete safely */
-
-	struct dl_list list;
-};
-
-/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct transmit_sc {
-	struct ieee802_1x_mka_sci sci; /* const SCI sci */
-	Boolean transmitting; /* bool transmitting (read only) */
-
-	struct os_time created_time; /* Time createdTime */
-
-	u8 encoding_sa; /* AN encodingSA (read only) */
-	u8 enciphering_sa; /* AN encipheringSA (read only) */
-
-	/* not defined data */
-	unsigned int channel;
-
-	struct dl_list list;
-	struct dl_list sa_list;
-};
-
-/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct transmit_sa {
-	Boolean in_use; /* bool inUse (read only) */
-	u32 next_pn; /* PN nextPN (read only) */
-	struct os_time created_time; /* Time createdTime */
-
-	Boolean enable_transmit; /* bool EnableTransmit */
-
-	u8 an;
-	Boolean confidentiality;
-	struct data_key *pkey;
-
-	struct transmit_sc *sc;
-	struct dl_list list; /* list entry in struct transmit_sc::sa_list */
-};
-
-/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct receive_sc {
-	struct ieee802_1x_mka_sci sci; /* const SCI sci */
-	Boolean receiving; /* bool receiving (read only) */
-
-	struct os_time created_time; /* Time createdTime */
-
-	unsigned int channel;
-
-	struct dl_list list;
-	struct dl_list sa_list;
-};
-
-/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct receive_sa {
-	Boolean enable_receive; /* bool enableReceive */
-	Boolean in_use; /* bool inUse (read only) */
-
-	u32 next_pn; /* PN nextPN (read only) */
-	u32 lowest_pn; /* PN lowestPN (read only) */
-	u8 an;
-	struct os_time created_time;
-
-	struct data_key *pkey;
-	struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
-
-	struct dl_list list;
-};
-
 struct macsec_ciphersuite {
 	u64 id;
 	char name[32];
@@ -250,6 +168,22 @@
 
 #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
 
+/**
+ * struct ieee802_1x_mka_basic_body - Basic Parameter Set (Figure 11-8)
+ * @version: MKA Version Identifier
+ * @priority: Key Server Priority
+ * @length: Parameter set body length
+ * @macsec_capability: MACsec capability, as defined in ieee802_1x_defs.h
+ * @macsec_desired: the participant wants MACsec to be used to protect frames
+ *	(9.6.1)
+ * @key_server: the participant has not decided that another participant is or
+ *	will be the key server (9.5.1)
+ * @length1: Parameter set body length (cont)
+ * @actor_mi: Actor's Member Identifier
+ * @actor_mn: Actor's Message Number
+ * @algo_agility: Algorithm Agility parameter
+ * @ckn: CAK Name
+ */
 struct ieee802_1x_mka_basic_body {
 	/* octet 1 */
 	u8 version;
@@ -279,6 +213,14 @@
 	u8 ckn[0];
 };
 
+/**
+ * struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List
+ *	parameter sets (Figure 11-9)
+ * @type: Parameter set type (1 or 2)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ * @peer: array of (MI, MN) pairs
+ */
 struct ieee802_1x_mka_peer_body {
 	/* octet 1 */
 	u8 type;
@@ -299,6 +241,28 @@
 	/* followed by Peers */
 };
 
+/**
+ * struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure
+ *	11-10)
+ * @type: MKA message type
+ * @lan: latest key AN
+ * @ltx: latest key TX
+ * @lrx: latest key RX
+ * @oan: old key AN
+ * @otx: old key TX
+ * @orx: old key RX
+ * @ptx: plain TX, ie protectFrames is False
+ * @prx: plain RX, ie validateFrames is not Strict
+ * @delay_protect: True if LPNs are being reported sufficiently frequently to
+ *	allow the recipient to provide data delay protection. If False, the LPN
+ *	can be reported as zero.
+ * @lsrv_mi: latest key server MI
+ * @lkn: latest key number (together with MI, form the KI)
+ * @llpn: latest lowest acceptable PN (LPN)
+ * @osrv_mi: old key server MI
+ * @okn: old key number (together with MI, form the KI)
+ * @olpn: old lowest acceptable PN (LPN)
+ */
 struct ieee802_1x_mka_sak_use_body {
 	/* octet 1 */
 	u8 type;
@@ -352,7 +316,21 @@
 	be32 olpn;
 };
 
-
+/**
+ * struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set
+ *	(GCM-AES-128, Figure 11-11)
+ * @type: Parameter set type (4)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ *           Total parameter body length values:
+ *            -  0 for plain text
+ *            - 28 for GCM-AES-128
+ *            - 36 or more for other cipher suites
+ * @confid_offset: confidentiality offset, as defined in ieee802_1x_defs.h
+ * @dan: distributed AN (0 for plain text)
+ * @kn: Key Number
+ * @sak: AES Key Wrap of SAK (see 9.8)
+ */
 struct ieee802_1x_mka_dist_sak_body {
 	/* octet 1 */
 	u8 type;
@@ -385,6 +363,41 @@
 	u8 sak[0];
 };
 
+/**
+ * struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure
+ *	11-13)
+ * @type: Parameter set type (5)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ *           Total parameter body length values:
+ *            -  0 for plain text
+ *            - 28 for GCM-AES-128
+ *            - 36 or more for other cipher suites
+ * @cak: AES Key Wrap of CAK (see 9.8)
+ * @ckn: CAK Name
+ */
+struct ieee802_1x_mka_dist_cak_body {
+	/* octet 1 */
+	u8 type;
+	/* octet 2 */
+	u8 reserve;
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u8 length:4;
+	u8 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u8 reserve1:4;
+	u8 length:4;
+#endif
+	/* octet 4 */
+	u8 length1;
+
+	/* octet 5 - 28 */
+	u8 cak[24];
+
+	/* followed by CAK Name, 29- */
+	u8 ckn[0];
+};
 
 struct ieee802_1x_mka_icv_body {
 	/* octet 1 */
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
index 2d12911..b1a9d22 100644
--- a/src/pae/ieee802_1x_secy_ops.c
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -113,6 +113,26 @@
 }
 
 
+int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->macsec_get_capability) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy macsec_get_capability operation not supported");
+		return -1;
+	}
+
+	return ops->macsec_get_capability(ops->ctx, cap);
+}
+
+
 int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
 			       struct receive_sa *rxsa)
 {
@@ -130,10 +150,7 @@
 		return -1;
 	}
 
-	return ops->get_receive_lowest_pn(ops->ctx,
-					rxsa->sc->channel,
-					rxsa->an,
-					&rxsa->lowest_pn);
+	return ops->get_receive_lowest_pn(ops->ctx, rxsa);
 }
 
 
@@ -154,10 +171,7 @@
 		return -1;
 	}
 
-	return ops->get_transmit_next_pn(ops->ctx,
-					txsa->sc->channel,
-					txsa->an,
-					&txsa->next_pn);
+	return ops->get_transmit_next_pn(ops->ctx, txsa);
 }
 
 
@@ -178,30 +192,7 @@
 		return -1;
 	}
 
-	return ops->set_transmit_next_pn(ops->ctx,
-					txsa->sc->channel,
-					txsa->an,
-					txsa->next_pn);
-}
-
-
-int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel)
-{
-	struct ieee802_1x_kay_ctx *ops;
-
-	if (!kay) {
-		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
-		return -1;
-	}
-
-	ops = kay->ctx;
-	if (!ops || !ops->get_available_receive_sc) {
-		wpa_printf(MSG_ERROR,
-			   "KaY: secy get_available_receive_sc operation not supported");
-		return -1;
-	}
-
-	return ops->get_available_receive_sc(ops->ctx, channel);
+	return ops->set_transmit_next_pn(ops->ctx, txsa);
 }
 
 
@@ -221,8 +212,7 @@
 		return -1;
 	}
 
-	return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci,
-				      kay->vf, kay->co);
+	return ops->create_receive_sc(ops->ctx, rxsc, kay->vf, kay->co);
 }
 
 
@@ -242,7 +232,7 @@
 		return -1;
 	}
 
-	return ops->delete_receive_sc(ops->ctx, rxsc->channel);
+	return ops->delete_receive_sc(ops->ctx, rxsc);
 }
 
 
@@ -262,8 +252,27 @@
 		return -1;
 	}
 
-	return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an,
-				      rxsa->lowest_pn, rxsa->pkey->key);
+	return ops->create_receive_sa(ops->ctx, rxsa);
+}
+
+
+int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->delete_receive_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy delete_receive_sa operation not supported");
+		return -1;
+	}
+
+	return ops->delete_receive_sa(ops->ctx, rxsa);
 }
 
 
@@ -285,7 +294,7 @@
 
 	rxsa->enable_receive = TRUE;
 
-	return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+	return ops->enable_receive_sa(ops->ctx, rxsa);
 }
 
 
@@ -307,27 +316,7 @@
 
 	rxsa->enable_receive = FALSE;
 
-	return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
-}
-
-
-int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel)
-{
-	struct ieee802_1x_kay_ctx *ops;
-
-	if (!kay) {
-		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
-		return -1;
-	}
-
-	ops = kay->ctx;
-	if (!ops || !ops->get_available_transmit_sc) {
-		wpa_printf(MSG_ERROR,
-			   "KaY: secy get_available_transmit_sc operation not supported");
-		return -1;
-	}
-
-	return ops->get_available_transmit_sc(ops->ctx, channel);
+	return ops->disable_receive_sa(ops->ctx, rxsa);
 }
 
 
@@ -348,8 +337,7 @@
 		return -1;
 	}
 
-	return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci,
-				       kay->co);
+	return ops->create_transmit_sc(ops->ctx, txsc, kay->co);
 }
 
 
@@ -370,7 +358,7 @@
 		return -1;
 	}
 
-	return ops->delete_transmit_sc(ops->ctx, txsc->channel);
+	return ops->delete_transmit_sc(ops->ctx, txsc);
 }
 
 
@@ -391,9 +379,28 @@
 		return -1;
 	}
 
-	return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an,
-					txsa->next_pn, txsa->confidentiality,
-					txsa->pkey->key);
+	return ops->create_transmit_sa(ops->ctx, txsa);
+}
+
+
+int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->delete_transmit_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy delete_transmit_sa operation not supported");
+		return -1;
+	}
+
+	return ops->delete_transmit_sa(ops->ctx, txsa);
 }
 
 
@@ -416,7 +423,7 @@
 
 	txsa->enable_transmit = TRUE;
 
-	return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+	return ops->enable_transmit_sa(ops->ctx, txsa);
 }
 
 
@@ -439,7 +446,7 @@
 
 	txsa->enable_transmit = FALSE;
 
-	return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+	return ops->disable_transmit_sa(ops->ctx, txsa);
 }
 
 
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
index f5057ee..477120b 100644
--- a/src/pae/ieee802_1x_secy_ops.h
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -13,10 +13,6 @@
 #include "common/ieee802_1x_defs.h"
 
 struct ieee802_1x_kay_conf;
-struct receive_sa;
-struct transmit_sa;
-struct receive_sc;
-struct transmit_sc;
 
 int secy_init_macsec(struct ieee802_1x_kay *kay);
 int secy_deinit_macsec(struct ieee802_1x_kay *kay);
@@ -32,27 +28,29 @@
 int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag);
 
 /****** KaY -> SecY *******/
+int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap);
 int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
 			       struct receive_sa *rxsa);
 int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
 			      struct transmit_sa *txsa);
 int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
 			      struct transmit_sa *txsa);
-int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel);
 int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
 int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
 int secy_disable_receive_sa(struct ieee802_1x_kay *kay,
 			    struct receive_sa *rxsa);
 
-int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel);
 int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
 			    struct transmit_sc *txsc);
 int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
 			    struct transmit_sc *txsc);
 int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
 			    struct transmit_sa *txsa);
+int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa);
 int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
 			    struct transmit_sa *txsa);
 int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index 79764d9..f06ed04 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -65,10 +65,9 @@
 {
 	size_t rlen;
 	struct wpa_eapol_key *err;
-	struct wpa_eapol_key_192 *err192;
 	struct rsn_error_kde error;
-	u8 *rbuf, *pos;
-	size_t kde_len;
+	u8 *rbuf, *pos, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info;
 
 	kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
@@ -76,11 +75,11 @@
 		kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
 
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*err) + kde_len, &rlen,
-				  (void *) &err);
+				  NULL, sizeof(*err) + mic_len + 2 + kde_len,
+				  &rlen, (void *) &err);
 	if (rbuf == NULL)
 		return -1;
-	err192 = (struct wpa_eapol_key_192 *) err;
+	mic = (u8 *) (err + 1);
 
 	err->type = EAPOL_KEY_TYPE_RSN;
 	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -92,8 +91,8 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(err->key_data_length, (u16) kde_len);
-	pos = (u8 *) (err + 1);
+	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
+	pos = mic + mic_len + 2;
 
 	if (peer) {
 		/* Peer MAC Address KDE */
@@ -114,8 +113,8 @@
 			   "(mui %d error_type %d)", mui, error_type);
 	}
 
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst,
-			   ETH_P_EAPOL, rbuf, rlen, err192->key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+			   mic);
 
 	return 0;
 }
@@ -128,9 +127,8 @@
 {
 	size_t rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
-	u8 *rbuf, *pos;
-	size_t kde_len;
+	u8 *rbuf, *pos, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info;
 
 	/* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
@@ -139,11 +137,10 @@
 		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
 
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*reply) + kde_len, &rlen,
-				  (void *) &reply);
+				  NULL, sizeof(*reply) + mic_len + 2 + kde_len,
+				  &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = EAPOL_KEY_TYPE_RSN;
 	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -155,8 +152,9 @@
 
 	os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
 
-	WPA_PUT_BE16(reply->key_data_length, (u16) kde_len);
-	pos = (u8 *) (reply + 1);
+	mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
+	pos = mic + mic_len + 2;
 
 	/* Peer RSN IE */
 	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
@@ -168,8 +166,8 @@
 	wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr,
-			   ETH_P_EAPOL, rbuf, rlen, reply192->key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, src_addr, ETH_P_EAPOL, rbuf, rlen,
+			   mic);
 
 	return 0;
 }
@@ -324,18 +322,19 @@
 {
 	size_t mlen;
 	struct wpa_eapol_key *msg;
-	u8 *mbuf;
-	size_t kde_len;
+	u8 *mbuf, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info, ver;
 
 	kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
 
 	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + kde_len, &mlen,
+				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
 				  (void *) &msg);
 	if (mbuf == NULL)
 		return;
 
+	mic = (u8 *) (msg + 1);
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
 	if (peerkey->cipher != WPA_CIPHER_TKIP)
@@ -355,8 +354,8 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(msg->key_data_length, kde_len);
-	wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
+	WPA_PUT_BE16(mic + mic_len, kde_len);
+	wpa_add_kde(mic + mic_len + 2, RSN_KEY_DATA_PMKID,
 		    peerkey->smkid, PMKID_LEN);
 
 	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
@@ -371,7 +370,7 @@
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
 		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL,
+	wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
 			   mbuf, mlen, NULL);
 }
 
@@ -381,8 +380,8 @@
 {
 	size_t mlen;
 	struct wpa_eapol_key *msg;
-	u8 *mbuf, *pos;
-	size_t kde_len;
+	u8 *mbuf, *pos, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info, ver;
 	be32 lifetime;
 
@@ -390,11 +389,12 @@
 		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
 
 	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + kde_len, &mlen,
+				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
 				  (void *) &msg);
 	if (mbuf == NULL)
 		return;
 
+	mic = (u8 *) (msg + 1);
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
 	if (peerkey->cipher != WPA_CIPHER_TKIP)
@@ -415,8 +415,8 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(msg->key_data_length, kde_len);
-	pos = (u8 *) (msg + 1);
+	WPA_PUT_BE16(mic + mic_len, kde_len);
+	pos = mic + mic_len;
 	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
 	lifetime = host_to_be32(peerkey->lifetime);
 	wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
@@ -426,9 +426,8 @@
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
 		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver,
-			   peerkey->addr, ETH_P_EAPOL, mbuf, mlen,
-			   msg->key_mic);
+	wpa_eapol_key_send(sm, &peerkey->stk, ver, peerkey->addr, ETH_P_EAPOL,
+			   mbuf, mlen, mic);
 }
 
 
@@ -911,10 +910,10 @@
  */
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
 				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key_192 *key, u16 ver,
+				 struct wpa_eapol_key *key, u16 ver,
 				 const u8 *buf, size_t len)
 {
-	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
 	size_t mic_len = 16;
 	int ok = 0;
 
@@ -926,12 +925,13 @@
 		peerkey->stk_set = 1;
 	}
 
-	os_memcpy(mic, key->key_mic, mic_len);
+	mic_pos = (u8 *) (key + 1);
+	os_memcpy(mic, mic_pos, mic_len);
 	if (peerkey->tstk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(mic_pos, 0, mic_len);
 		wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
-				  sm->key_mgmt, ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  sm->key_mgmt, ver, buf, len, mic_pos);
+		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "when using TSTK - ignoring TSTK");
 		} else {
@@ -945,10 +945,10 @@
 	}
 
 	if (!ok && peerkey->stk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(mic_pos, 0, mic_len);
 		wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
-				  sm->key_mgmt, ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  sm->key_mgmt, ver, buf, len, mic_pos);
+		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "- dropping packet");
 			return -1;
@@ -980,10 +980,10 @@
  */
 int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
 {
-	size_t rlen, kde_len;
+	size_t rlen, kde_len, mic_len;
 	struct wpa_eapol_key *req;
 	int key_info, ver;
-	u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos;
+	u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos, *mic;
 	u16 count;
 	struct rsn_ie_hdr *hdr;
 	struct wpa_peerkey *peerkey;
@@ -999,6 +999,7 @@
 		return -1;
 	}
 
+	mic_len = wpa_mic_len(sm->key_mgmt);
 	if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
@@ -1047,7 +1048,7 @@
 	kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
 
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*req) + kde_len, &rlen,
+				  sizeof(*req) + mic_len + 2 + kde_len, &rlen,
 				  (void *) &req);
 	if (rbuf == NULL) {
 		wpa_supplicant_peerkey_free(sm, peerkey);
@@ -1074,8 +1075,10 @@
 	wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
 		    req->key_nonce, WPA_NONCE_LEN);
 
-	WPA_PUT_BE16(req->key_data_length, (u16) kde_len);
-	pos = (u8 *) (req + 1);
+	mic = pos = (u8 *) (req + 1);
+	pos += mic_len;
+	WPA_PUT_BE16(pos, (u16) kde_len);
+	pos += 2;
 
 	/* Initiator RSN IE */
 	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
@@ -1084,8 +1087,8 @@
 
 	wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
 		   MACSTR ")", MAC2STR(peer));
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
-			   ETH_P_EAPOL, rbuf, rlen, req->key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
+			   mic);
 
 	peerkey->next = sm->peerkey;
 	sm->peerkey = peerkey;
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
index 6ccd948..f3d07f3 100644
--- a/src/rsn_supp/peerkey.h
+++ b/src/rsn_supp/peerkey.h
@@ -38,7 +38,7 @@
 
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
 				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key_192 *key, u16 ver,
+				 struct wpa_eapol_key *key, u16 ver,
 				 const u8 *buf, size_t len);
 void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
 			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index e424168..f57311e 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -2878,14 +2878,14 @@
 static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
 {
 	/* bit 38 - TDLS Prohibited */
-	return !!(elems->ext_capab[2 + 4] & 0x40);
+	return !!(elems->ext_capab[4] & 0x40);
 }
 
 
 static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
 {
 	/* bit 39 - TDLS Channel Switch Prohibited */
-	return !!(elems->ext_capab[2 + 4] & 0x80);
+	return !!(elems->ext_capab[4] & 0x80);
 }
 
 
@@ -2898,7 +2898,7 @@
 
 	if (ies == NULL ||
 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
-	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
 		return;
 
 	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
@@ -2917,7 +2917,7 @@
 
 	if (ies == NULL ||
 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
-	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
 		return;
 
 	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 3c47879..65e257a 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -10,10 +10,13 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/aes.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
+#include "crypto/aes_siv.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "wpa.h"
 #include "eloop.h"
@@ -30,8 +33,7 @@
 /**
  * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @kck: Key Confirmation Key (KCK, part of PTK)
- * @kck_len: KCK length in octets
+ * @ptk: PTK for Key Confirmation/Encryption Key
  * @ver: Version field from Key Info
  * @dest: Destination address for the frame
  * @proto: Ethertype (usually ETH_P_EAPOL)
@@ -40,7 +42,7 @@
  * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
  * Returns: >= 0 on success, < 0 on failure
  */
-int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
 		       int ver, const u8 *dest, u16 proto,
 		       u8 *msg, size_t msg_len, u8 *key_mic)
 {
@@ -64,16 +66,87 @@
 				MAC2STR(dest));
 		}
 	}
-	if (key_mic &&
-	    wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
-			      key_mic)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
-			"WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
-			ver, sm->key_mgmt);
+
+	if (mic_len) {
+		if (key_mic && (!ptk || !ptk->kck_len))
+			goto out;
+
+		if (key_mic &&
+		    wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver,
+				      msg, msg_len, key_mic)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+				ver, sm->key_mgmt);
+			goto out;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
+		wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC",
+			    key_mic, mic_len);
+	} else {
+#ifdef CONFIG_FILS
+		/* AEAD cipher - Key MIC field not used */
+		struct ieee802_1x_hdr *s_hdr, *hdr;
+		struct wpa_eapol_key *s_key, *key;
+		u8 *buf, *s_key_data, *key_data;
+		size_t buf_len = msg_len + AES_BLOCK_SIZE;
+		size_t key_data_len;
+		u16 eapol_len;
+		const u8 *aad[1];
+		size_t aad_len[1];
+
+		if (!ptk || !ptk->kek_len)
+			goto out;
+
+		key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) -
+			sizeof(struct wpa_eapol_key) - 2;
+
+		buf = os_malloc(buf_len);
+		if (!buf)
+			goto out;
+
+		os_memcpy(buf, msg, msg_len);
+		hdr = (struct ieee802_1x_hdr *) buf;
+		key = (struct wpa_eapol_key *) (hdr + 1);
+		key_data = ((u8 *) (key + 1)) + 2;
+
+		/* Update EAPOL header to include AES-SIV overhead */
+		eapol_len = be_to_host16(hdr->length);
+		eapol_len += AES_BLOCK_SIZE;
+		hdr->length = host_to_be16(eapol_len);
+
+		/* Update Key Data Length field to include AES-SIV overhead */
+		WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len);
+
+		s_hdr = (struct ieee802_1x_hdr *) msg;
+		s_key = (struct wpa_eapol_key *) (s_hdr + 1);
+		s_key_data = ((u8 *) (s_key + 1)) + 2;
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data",
+				s_key_data, key_data_len);
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+		 /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+		  * to Key Data (exclusive). */
+		aad[0] = buf;
+		aad_len[0] = key_data - buf;
+		if (aes_siv_encrypt(ptk->kek, ptk->kek_len,
+				    s_key_data, key_data_len,
+				    1, aad, aad_len, key_data) < 0) {
+			os_free(buf);
+			goto out;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+			    key_data, AES_BLOCK_SIZE + key_data_len);
+
+		os_free(msg);
+		msg = buf;
+		msg_len = buf_len;
+#else /* CONFIG_FILS */
 		goto out;
+#endif /* CONFIG_FILS */
 	}
-	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
-	wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
+
 	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
 	ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
 	eapol_sm_notify_tx_eapol_key(sm->eapol);
@@ -97,9 +170,8 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	int key_info, ver;
-	u8 bssid[ETH_ALEN], *rbuf, *key_mic;
+	u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
 
 	if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
 	    wpa_key_mgmt_suite_b(sm->key_mgmt))
@@ -119,19 +191,20 @@
 	}
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info = WPA_KEY_INFO_REQUEST | ver;
 	if (sm->ptk_set)
-		key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+		key_info |= WPA_KEY_INFO_SECURE;
+	if (sm->ptk_set && mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
 	if (error)
 		key_info |= WPA_KEY_INFO_ERROR;
 	if (pairwise)
@@ -142,21 +215,19 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 
-	if (mic_len == 24)
-		WPA_PUT_BE16(reply192->key_data_length, 0);
-	else
-		WPA_PUT_BE16(reply->key_data_length, 0);
+	mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(mic + mic_len, 0);
 	if (!(key_info & WPA_KEY_INFO_MIC))
 		key_mic = NULL;
 	else
-		key_mic = reply192->key_mic; /* same offset in reply */
+		key_mic = mic;
 
 	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 		"WPA: Sending EAPOL-Key Request (error=%d "
 		"pairwise=%d ptk_set=%d len=%lu)",
 		error, pairwise, sm->ptk_set, (unsigned long) rlen);
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
-			   ETH_P_EAPOL, rbuf, rlen, key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
+			   key_mic);
 }
 
 
@@ -341,9 +412,9 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	u8 *rbuf, *key_mic;
 	u8 *rsn_ie_buf = NULL;
+	u16 key_info;
 
 	if (wpa_ie == NULL) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
@@ -384,7 +455,7 @@
 	wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
 				  NULL, hdrlen + wpa_ie_len,
 				  &rlen, (void *) &reply);
@@ -392,13 +463,16 @@
 		os_free(rsn_ie_buf);
 		return -1;
 	}
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
-	WPA_PUT_BE16(reply->key_info,
-		     ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
+	key_info = ver | WPA_KEY_INFO_KEY_TYPE;
+	if (mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
+	else
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
@@ -408,21 +482,16 @@
 	wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
 		    WPA_REPLAY_COUNTER_LEN);
 
-	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
-	if (mic_len == 24) {
-		WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
-		os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
-	} else {
-		WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
-		os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
-	}
+	key_mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */
+	os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */
 	os_free(rsn_ie_buf);
 
 	os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
-	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
-				  ETH_P_EAPOL, rbuf, rlen, key_mic);
+	return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+				  key_mic);
 }
 
 
@@ -1147,22 +1216,24 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	u8 *rbuf, *key_mic;
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_SECURE;
-	key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
+	key_info |= ver | WPA_KEY_INFO_KEY_TYPE;
+	if (mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
+	else
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
 	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
@@ -1171,15 +1242,12 @@
 	os_memcpy(reply->replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
 
-	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
-	if (mic_len == 24)
-		WPA_PUT_BE16(reply192->key_data_length, 0);
-	else
-		WPA_PUT_BE16(reply->key_data_length, 0);
+	key_mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(key_mic + mic_len, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
-	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
-				  ETH_P_EAPOL, rbuf, rlen, key_mic);
+	return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+				  key_mic);
 }
 
 
@@ -1456,22 +1524,22 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	u8 *rbuf, *key_mic;
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
-	key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+	key_info |= ver | WPA_KEY_INFO_SECURE;
+	if (mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
 	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
@@ -1480,15 +1548,12 @@
 	os_memcpy(reply->replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
 
-	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
-	if (mic_len == 24)
-		WPA_PUT_BE16(reply192->key_data_length, 0);
-	else
-		WPA_PUT_BE16(reply->key_data_length, 0);
+	key_mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(key_mic + mic_len, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
-	return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver,
-				  sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);
+	return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
+				  rbuf, rlen, key_mic);
 }
 
 
@@ -1564,7 +1629,7 @@
 
 
 static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
-					       struct wpa_eapol_key_192 *key,
+					       struct wpa_eapol_key *key,
 					       u16 ver,
 					       const u8 *buf, size_t len)
 {
@@ -1572,12 +1637,12 @@
 	int ok = 0;
 	size_t mic_len = wpa_mic_len(sm->key_mgmt);
 
-	os_memcpy(mic, key->key_mic, mic_len);
+	os_memcpy(mic, key + 1, mic_len);
 	if (sm->tptk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(key + 1, 0, mic_len);
 		wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  ver, buf, len, (u8 *) (key + 1));
+		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC "
 				"when using TPTK - ignoring TPTK");
@@ -1591,10 +1656,10 @@
 	}
 
 	if (!ok && sm->ptk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(key + 1, 0, mic_len);
 		wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  ver, buf, len, (u8 *) (key + 1));
+		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC - "
 				"dropping packet");
@@ -1619,7 +1684,8 @@
 
 /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
 static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
-					   struct wpa_eapol_key *key, u16 ver,
+					   struct wpa_eapol_key *key,
+					   size_t mic_len, u16 ver,
 					   u8 *key_data, size_t *key_data_len)
 {
 	wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
@@ -1678,7 +1744,7 @@
 		}
 		os_memcpy(key_data, buf, *key_data_len);
 		bin_clear_free(buf, *key_data_len);
-		WPA_PUT_BE16(key->key_data_length, *key_data_len);
+		WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len);
 	} else {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported key_info type %d", ver);
@@ -1741,6 +1807,76 @@
 }
 
 
+#ifdef CONFIG_FILS
+static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len,
+				 size_t *key_data_len)
+{
+	struct wpa_ptk *ptk;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 *pos, *tmp;
+	const u8 *aad[1];
+	size_t aad_len[1];
+
+	if (*key_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame");
+		return -1;
+	}
+
+	if (sm->tptk_set)
+		ptk = &sm->tptk;
+	else if (sm->ptk_set)
+		ptk = &sm->ptk;
+	else
+		return -1;
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	pos = (u8 *) (key + 1);
+	pos += 2; /* Pointing at the Encrypted Key Data field */
+
+	tmp = os_malloc(*key_data_len);
+	if (!tmp)
+		return -1;
+
+	/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+	 * to Key Data (exclusive). */
+	aad[0] = buf;
+	aad_len[0] = pos - buf;
+	if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, *key_data_len,
+			    1, aad, aad_len, tmp) < 0) {
+		wpa_printf(MSG_INFO, "Invalid AES-SIV data in the frame");
+		bin_clear_free(tmp, *key_data_len);
+		return -1;
+	}
+
+	/* AEAD decryption and validation completed successfully */
+	(*key_data_len) -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+			tmp, *key_data_len);
+
+	/* Replace Key Data field with the decrypted version */
+	os_memcpy(pos, tmp, *key_data_len);
+	pos -= 2; /* Key Data Length field */
+	WPA_PUT_BE16(pos, *key_data_len);
+	bin_clear_free(tmp, *key_data_len);
+
+	if (sm->tptk_set) {
+		sm->tptk_set = 0;
+		sm->ptk_set = 1;
+		os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+	}
+
+	os_memcpy(sm->rx_replay_counter, key->replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	sm->rx_replay_counter_set = 1;
+
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+
 /**
  * wpa_sm_rx_eapol - Process received WPA EAPOL frames
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -1763,12 +1899,11 @@
 	size_t plen, data_len, key_data_len;
 	const struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info, ver;
 	u8 *tmp = NULL;
 	int ret = -1;
 	struct wpa_peerkey *peerkey = NULL;
-	u8 *key_data;
+	u8 *mic, *key_data;
 	size_t mic_len, keyhdrlen;
 
 #ifdef CONFIG_IEEE80211R
@@ -1776,7 +1911,7 @@
 #endif /* CONFIG_IEEE80211R */
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	if (len < sizeof(*hdr) + keyhdrlen) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -1828,12 +1963,8 @@
 		goto out;
 	os_memcpy(tmp, buf, data_len);
 	key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
-	key192 = (struct wpa_eapol_key_192 *)
-		(tmp + sizeof(struct ieee802_1x_hdr));
-	if (mic_len == 24)
-		key_data = (u8 *) (key192 + 1);
-	else
-		key_data = (u8 *) (key + 1);
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
 
 	if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
 	{
@@ -1844,11 +1975,8 @@
 		goto out;
 	}
 
-	if (mic_len == 24)
-		key_data_len = WPA_GET_BE16(key192->key_data_length);
-	else
-		key_data_len = WPA_GET_BE16(key->key_data_length);
-	wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+	key_data_len = WPA_GET_BE16(mic + mic_len);
+	wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len);
 
 	if (key_data_len > plen - keyhdrlen) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
@@ -1867,6 +1995,7 @@
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
 	    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Unsupported EAPOL-Key descriptor version %d",
@@ -1882,7 +2011,8 @@
 		goto out;
 	}
 
-	if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	if ((wpa_key_mgmt_suite_b(sm->key_mgmt) ||
+	     wpa_key_mgmt_fils(sm->key_mgmt)) &&
 	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
@@ -1904,6 +2034,7 @@
 	if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
 		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 		    sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
+		    !wpa_key_mgmt_fils(sm->key_mgmt) &&
 		    !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"WPA: AP did not use the "
@@ -1914,6 +2045,7 @@
 #endif /* CONFIG_IEEE80211W */
 	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: CCMP is used, but EAPOL-Key "
@@ -2004,19 +2136,27 @@
 	}
 
 	if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
-	    wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
+	    wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
 		goto out;
 
 #ifdef CONFIG_PEERKEY
 	if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
-	    peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp,
+	    peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp,
 					 data_len))
 		goto out;
 #endif /* CONFIG_PEERKEY */
 
+#ifdef CONFIG_FILS
+	if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len))
+			goto out;
+	}
+#endif /* CONFIG_FILS */
+
 	if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
-	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-		if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
+		if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
+						    ver, key_data,
 						    &key_data_len))
 			goto out;
 	}
@@ -2032,7 +2172,8 @@
 			/* PeerKey 4-Way Handshake */
 			peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
 					      key_data, key_data_len);
-		} else if (key_info & WPA_KEY_INFO_MIC) {
+		} else if (key_info & (WPA_KEY_INFO_MIC |
+				       WPA_KEY_INFO_ENCR_KEY_DATA)) {
 			/* 3/4 4-Way Handshake */
 			wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
 						      key_data_len);
@@ -2047,14 +2188,15 @@
 		peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
 				     ver);
 	} else {
-		if (key_info & WPA_KEY_INFO_MIC) {
+		if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
+		    (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
 			/* 1/2 Group Key Handshake */
 			wpa_supplicant_process_1_of_2(sm, src_addr, key,
 						      key_data, key_data_len,
 						      ver);
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: EAPOL-Key (Group) without Mic bit - "
+				"WPA: EAPOL-Key (Group) without Mic/Encr bit - "
 				"dropped");
 		}
 	}
@@ -2336,6 +2478,16 @@
 		clear_ptk = 0;
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_FILS
+	if (sm->fils_completed) {
+		/*
+		 * Clear portValid to kick EAPOL state machine to re-enter
+		 * AUTHENTICATED state to get the EAPOL port Authorized.
+		 */
+		wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+		clear_ptk = 0;
+	}
+#endif /* CONFIG_FILS */
 
 	if (clear_ptk) {
 		/*
@@ -2378,6 +2530,9 @@
 #ifdef CONFIG_TDLS
 	wpa_tdls_disassoc(sm);
 #endif /* CONFIG_TDLS */
+#ifdef CONFIG_FILS
+	sm->fils_completed = 0;
+#endif /* CONFIG_FILS */
 
 	/* Keys are not needed in the WPA state machine anymore */
 	wpa_sm_drop_sa(sm);
@@ -2726,6 +2881,10 @@
 
 		os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
 		sm->assoc_wpa_ie_len = *wpa_ie_len;
+	} else {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: Leave previously set WPA IE default",
+			    sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
 	}
 
 	return 0;
@@ -3054,3 +3213,441 @@
 	sm->test_assoc_ie = buf;
 }
 #endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_FILS
+
+struct wpabuf * fils_build_auth(struct wpa_sm *sm)
+{
+	struct wpabuf *buf = NULL;
+	struct wpabuf *erp_msg;
+
+	erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
+	if (!erp_msg && !sm->cur_pmksa) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)",
+		   erp_msg != NULL, sm->cur_pmksa != NULL);
+
+	sm->fils_completed = 0;
+
+	if (!sm->assoc_wpa_ie) {
+		wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS");
+		goto fail;
+	}
+
+	if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 ||
+	    random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0)
+		goto fail;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce",
+		    sm->fils_nonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
+		    sm->fils_session, FILS_SESSION_LEN);
+
+	buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len);
+	if (!buf)
+		goto fail;
+
+	/* Fields following the Authentication algorithm number field */
+
+	/* Authentication Transaction seq# */
+	wpabuf_put_le16(buf, 1);
+
+	/* Status Code */
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	/* RSNE */
+	wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
+		    sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
+	wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
+
+	/* TODO: MDE when using FILS for FT initial association */
+	/* TODO: FTE when using FILS for FT initial association */
+
+	/* FILS Nonce */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+	wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN);
+
+	/* FILS Session */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+	/* FILS Wrapped Data */
+	if (erp_msg) {
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+		wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */
+		/* Element ID Extension */
+		wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_WRAPPED_DATA);
+		wpabuf_put_buf(buf, erp_msg);
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame",
+			buf);
+
+fail:
+	wpabuf_free(erp_msg);
+	return buf;
+}
+
+
+int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len)
+{
+	const u8 *pos, *end;
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsn;
+	int pmkid_match = 0;
+	u8 ick[FILS_ICK_MAX_LEN];
+	size_t ick_len;
+	int res;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+		    data, len);
+	pos = data;
+	end = data + len;
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
+	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
+		return -1;
+	}
+
+	/* RSNE */
+	wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie,
+		    elems.rsn_ie_len);
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: No RSN element");
+		return -1;
+	}
+
+	if (!elems.fils_nonce) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+		return -1;
+	}
+	os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
+
+	/* TODO: MDE when using FILS+FT */
+	/* TODO: FTE when using FILS+FT */
+
+	/* PMKID List */
+	if (rsn.pmkid && rsn.num_pmkid > 0) {
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+			    rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+		if (rsn.num_pmkid != 1) {
+			wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
+			return -1;
+		}
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
+		if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
+		{
+			wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
+			wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
+				    sm->cur_pmksa->pmkid, PMKID_LEN);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Matching PMKID - continue using PMKSA caching");
+		pmkid_match = 1;
+	}
+	if (!pmkid_match && sm->cur_pmksa) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No PMKID match - cannot use cached PMKSA entry");
+		sm->cur_pmksa = NULL;
+	}
+
+	/* FILS Session */
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+		    FILS_SESSION_LEN);
+	if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN)
+	    != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    sm->fils_session, FILS_SESSION_LEN);
+		return -1;
+	}
+
+	/* FILS Wrapped Data */
+	if (!sm->cur_pmksa && elems.fils_wrapped_data) {
+		wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+			    elems.fils_wrapped_data,
+			    elems.fils_wrapped_data_len);
+		eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
+					    elems.fils_wrapped_data_len);
+		if (eapol_sm_failed(sm->eapol))
+			return -1;
+
+		res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+		if (res)
+			return -1;
+
+		wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result");
+		sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, PMK_LEN,
+						NULL, NULL, 0, sm->bssid,
+						sm->own_addr,
+						sm->network_ctx, sm->key_mgmt);
+	}
+
+	if (!sm->cur_pmksa) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No remaining options to continue FILS authentication");
+		return -1;
+	}
+
+	if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
+			    sm->fils_nonce, sm->fils_anonce, &sm->ptk,
+			    ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
+		return -1;
+	}
+	sm->ptk_set = 1;
+	sm->tptk_set = 0;
+	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+
+	res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
+			       sm->fils_anonce, sm->own_addr, sm->bssid,
+			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       sm->key_mgmt, sm->fils_key_auth_sta,
+			       sm->fils_key_auth_ap,
+			       &sm->fils_key_auth_len);
+	os_memset(ick, 0, sizeof(ick));
+	return res;
+}
+
+
+struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
+				     size_t *kek_len, const u8 **snonce,
+				     const u8 **anonce)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(1000);
+	if (!buf)
+		return NULL;
+
+	/* FILS Session */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+	/* Everything after FILS Session element gets encrypted in the driver
+	 * with KEK. The buffer returned from here is the plaintext version. */
+
+	/* TODO: FILS Public Key */
+
+	/* FILS Key Confirm */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len);
+
+	/* TODO: FILS HLP Container */
+
+	/* TODO: FILS IP Address Assignment */
+
+	wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf);
+
+	*kek = sm->ptk.kek;
+	*kek_len = sm->ptk.kek_len;
+	wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len);
+	*snonce = sm->fils_nonce;
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD",
+		    *snonce, FILS_NONCE_LEN);
+	*anonce = sm->fils_anonce;
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD",
+		    *anonce, FILS_NONCE_LEN);
+
+	return buf;
+}
+
+
+int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	const u8 *end, *ie_start;
+	struct ieee802_11_elems elems;
+	int keylen, rsclen;
+	enum wpa_alg alg;
+	struct wpa_gtk_data gd;
+	int maxkeylen;
+	struct wpa_eapol_ie_parse kde;
+
+	if (!sm || !sm->ptk_set) {
+		wpa_printf(MSG_DEBUG, "FILS: No KEK available");
+		return -1;
+	}
+
+	if (!wpa_key_mgmt_fils(sm->key_mgmt)) {
+		wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
+		    resp, len);
+
+	mgmt = (const struct ieee80211_mgmt *) resp;
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp))
+		return -1;
+
+	end = resp + len;
+	/* Same offset for Association Response and Reassociation Response */
+	ie_start = mgmt->u.assoc_resp.variable;
+
+	if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to parse decrypted elements");
+		goto fail;
+	}
+
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		return -1;
+	}
+	if (os_memcmp(elems.fils_session, sm->fils_session,
+		      FILS_SESSION_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+			    elems.fils_session, FILS_SESSION_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    sm->fils_session, FILS_SESSION_LEN);
+	}
+
+	/* TODO: FILS Public Key */
+
+	if (!elems.fils_key_confirm) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+		goto fail;
+	}
+	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Unexpected Key-Auth length %d (expected %d)",
+			   elems.fils_key_confirm_len,
+			   (int) sm->fils_key_auth_len);
+		goto fail;
+	}
+	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap,
+		      sm->fils_key_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+			    elems.fils_key_confirm,
+			    elems.fils_key_confirm_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+			    sm->fils_key_auth_ap, sm->fils_key_auth_len);
+		goto fail;
+	}
+
+	/* Key Delivery */
+	if (!elems.key_delivery) {
+		wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
+		goto fail;
+	}
+
+	/* Parse GTK and set the key to the driver */
+	os_memset(&gd, 0, sizeof(gd));
+	if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN,
+				     elems.key_delivery_len - WPA_KEY_RSC_LEN,
+				     &kde) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs");
+		goto fail;
+	}
+	if (!kde.gtk) {
+		wpa_printf(MSG_DEBUG, "FILS: No GTK KDE");
+		goto fail;
+	}
+	maxkeylen = gd.gtk_len = kde.gtk_len - 2;
+	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					      gd.gtk_len, maxkeylen,
+					      &gd.key_rsc_len, &gd.alg))
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len);
+	gd.keyidx = kde.gtk[0] & 0x3;
+	gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+						     !!(kde.gtk[0] & BIT(2)));
+	if (kde.gtk_len - 2 > sizeof(gd.gtk)) {
+		wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)",
+			   (unsigned long) kde.gtk_len - 2);
+		goto fail;
+	}
+	os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2);
+
+	wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver");
+	if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
+		goto fail;
+	}
+
+	if (ieee80211w_set_keys(sm, &kde) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK");
+		goto fail;
+	}
+
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
+			sm->ptk.tk, keylen);
+	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
+			   sm->ptk.tk, keylen) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+			MACSTR ")",
+			alg, keylen, MAC2STR(sm->bssid));
+		goto fail;
+	}
+
+	/* TODO: TK could be cleared after auth frame exchange now that driver
+	 * takes care of association frame encryption/decryption. */
+	/* TK is not needed anymore in supplicant */
+	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+
+	/* TODO: FILS HLP Container */
+
+	/* TODO: FILS IP Address Assignment */
+
+	wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully");
+	sm->fils_completed = 1;
+
+	return 0;
+fail:
+	return -1;
+}
+
+#endif /* CONFIG_FILS */
+
+
+int wpa_fils_is_completed(struct wpa_sm *sm)
+{
+#ifdef CONFIG_FILS
+	return sm && sm->fils_completed;
+#else /* CONFIG_FILS */
+	return 0;
+#endif /* CONFIG_FILS */
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 0b7477f..20d5b8e 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -426,4 +426,12 @@
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
 
+struct wpabuf * fils_build_auth(struct wpa_sm *sm);
+int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len);
+struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
+				     size_t *kek_len, const u8 **snonce,
+				     const u8 **anonce);
+int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len);
+int wpa_fils_is_completed(struct wpa_sm *sm);
+
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index f653ba6..44d5424 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -138,6 +138,16 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	struct wpabuf *test_assoc_ie;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_FILS
+	u8 fils_nonce[FILS_NONCE_LEN];
+	u8 fils_session[FILS_SESSION_LEN];
+	u8 fils_anonce[FILS_NONCE_LEN];
+	u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+	u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+	size_t fils_key_auth_len;
+	unsigned int fils_completed:1;
+#endif /* CONFIG_FILS */
 };
 
 
@@ -353,7 +363,7 @@
 	return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
 }
 
-int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
 		       int ver, const u8 *dest, u16 proto,
 		       u8 *msg, size_t msg_len, u8 *key_mic);
 int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index c44844e..3be3087 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -180,6 +180,18 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+#ifdef CONFIG_FILS
+	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+#ifdef CONFIG_IEEE80211R
+	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
 	} else {
 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
 			   key_mgmt);
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index 6b28417..67b55db 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -482,21 +482,21 @@
 		    os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
 			      "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
 		{
-			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256");
 			decrypted = buf + 19;
 			buflen -= 19;
 		} else if (buflen >= 19 + 48 &&
 		    os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01"
 			      "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0)
 		{
-			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384");
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384");
 			decrypted = buf + 19;
 			buflen -= 19;
 		} else if (buflen >= 19 + 64 &&
 		    os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01"
 			      "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0)
 		{
-			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512");
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512");
 			decrypted = buf + 19;
 			buflen -= 19;
 
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 8aad813..52efc53 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -17,6 +17,7 @@
 	base64.o \
 	bitfield.o \
 	common.o \
+	crc32.o \
 	ip_addr.o \
 	radiotap.o \
 	trace.o \
diff --git a/src/utils/common.c b/src/utils/common.c
index 68413b2..04a533a 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -1001,7 +1001,7 @@
  * @delim: a string of delimiters
  * @last: a pointer to a character following the returned token
  *      It has to be set to NULL for the first call and passed for any
- *      futher call.
+ *      further call.
  * Returns: a pointer to token position in str or NULL
  *
  * This function is similar to str_token, but it can be used with both
diff --git a/src/utils/crc32.h b/src/utils/crc32.h
new file mode 100644
index 0000000..dc31399
--- /dev/null
+++ b/src/utils/crc32.h
@@ -0,0 +1,14 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CRC32_H
+#define CRC32_H
+
+u32 crc32(const u8 *frame, size_t frame_len);
+
+#endif /* CRC32_H */
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index 96cb25c..77ee472 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -244,15 +244,13 @@
 
 	if (a)
 		len += wpabuf_len(a);
-	if (b)
-		len += wpabuf_len(b);
+	len += wpabuf_len(b);
 
 	n = wpabuf_alloc(len);
 	if (n) {
 		if (a)
 			wpabuf_put_buf(n, a);
-		if (b)
-			wpabuf_put_buf(n, b);
+		wpabuf_put_buf(n, b);
 	}
 
 	wpabuf_free(a);
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index e52ff14..22a56b6 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -210,6 +210,10 @@
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_IEEE80211R_AP
+CONFIG_IEEE80211R=y
+endif
+
 ifdef CONFIG_IEEE80211R
 L_CFLAGS += -DCONFIG_IEEE80211R
 OBJS += src/rsn_supp/wpa_ft.c
@@ -238,6 +242,13 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_FILS
+L_CFLAGS += -DCONFIG_FILS
+NEED_CRC32=y
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 L_CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.c
@@ -834,11 +845,6 @@
 endif
 endif
 
-ifdef CONFIG_MBO
-OBJS += mbo.c
-L_CFLAGS += -DCONFIG_MBO
-endif
-
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
@@ -860,13 +866,19 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.c
+L_CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_RSN_AUTHENTICATOR
 L_CFLAGS += -DCONFIG_NO_RADIUS
 NEED_AES_WRAP=y
 OBJS += src/ap/wpa_auth.c
 OBJS += src/ap/wpa_auth_ie.c
 OBJS += src/ap/pmksa_cache_auth.c
-ifdef CONFIG_IEEE80211R
+ifdef CONFIG_IEEE80211R_AP
+L_CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
 endif
 ifdef CONFIG_PEERKEY
@@ -1284,6 +1296,10 @@
 L_CFLAGS += -DCONFIG_ECC
 endif
 
+ifdef NEED_CRC32
+OBJS += src/utils/crc32.c
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index e62d8c7..f28055f 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,6 +1,6 @@
 ChangeLog for wpa_supplicant
 
-????-??-?? - v2.6
+2016-10-02 - v2.6
 	* fixed WNM Sleep Mode processing when PMF is not enabled
 	  [http://w1.fi/security/2015-6/] (CVE-2015-5310)
 	* fixed EAP-pwd last fragment validation
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index f3e86c1..e56ce97 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -243,6 +243,10 @@
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_IEEE80211R_AP
+CONFIG_IEEE80211R=y
+endif
+
 ifdef CONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R
 OBJS += ../src/rsn_supp/wpa_ft.o
@@ -271,6 +275,13 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_FILS
+CFLAGS += -DCONFIG_FILS
+NEED_CRC32=y
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.o
@@ -875,11 +886,6 @@
 endif
 endif
 
-ifdef CONFIG_MBO
-OBJS += mbo.o
-CFLAGS += -DCONFIG_MBO
-endif
-
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
@@ -901,13 +907,19 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.o
+CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_RSN_AUTHENTICATOR
 CFLAGS += -DCONFIG_NO_RADIUS
 NEED_AES_WRAP=y
 OBJS += ../src/ap/wpa_auth.o
 OBJS += ../src/ap/wpa_auth_ie.o
 OBJS += ../src/ap/pmksa_cache_auth.o
-ifdef CONFIG_IEEE80211R
+ifdef CONFIG_IEEE80211R_AP
+CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
 endif
 ifdef CONFIG_PEERKEY
@@ -1330,6 +1342,10 @@
 CFLAGS += -DCONFIG_ECC
 endif
 
+ifdef NEED_CRC32
+OBJS += ../src/utils/crc32.o
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
index 072a1d5..5056a93 100644
--- a/wpa_supplicant/autoscan.c
+++ b/wpa_supplicant/autoscan.c
@@ -47,11 +47,16 @@
 	struct sched_scan_plan *scan_plans;
 
 	/* Give preference to scheduled scan plans if supported/configured */
-	if (wpa_s->sched_scan_plans)
+	if (wpa_s->sched_scan_plans) {
+		wpa_printf(MSG_DEBUG,
+			   "autoscan: sched_scan_plans set - use it instead");
 		return 0;
+	}
 
-	if (wpa_s->autoscan && wpa_s->autoscan_priv)
+	if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+		wpa_printf(MSG_DEBUG, "autoscan: Already initialized");
 		return 0;
+	}
 
 	if (name == NULL)
 		return 0;
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 3687a2e..3a8778d 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -214,8 +214,8 @@
 }
 
 
-static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
-			   const char *reason)
+void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+		    const char *reason)
 {
 	if (wpa_s->last_scan_res) {
 		unsigned int i;
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index f7f72f3..84e8fb0 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -113,6 +113,8 @@
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res,
 			     struct os_reltime *fetch_time);
+void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+		    const char *reason);
 void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
 			int new_scan);
 int wpa_bss_init(struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index dd922ca..a0b64b2 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -719,6 +719,18 @@
 		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+		else if (os_strcmp(start, "FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA256;
+		else if (os_strcmp(start, "FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA384;
+#ifdef CONFIG_IEEE80211R
+		else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA256;
+		else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 1b81797..a754943 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -532,6 +532,8 @@
 #endif /* CONFIG_MBO */
 	} else if (os_strcasecmp(cmd, "lci") == 0) {
 		ret = wpas_ctrl_iface_set_lci(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "tdls_trigger_control") == 0) {
+		ret = wpa_drv_set_tdls_mode(wpa_s, atoi(value));
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -2437,6 +2439,39 @@
 	}
 #endif /* CONFIG_SUITEB192 */
 
+#ifdef CONFIG_FILS
+	if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
 	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
 		ret = os_snprintf(pos, end - pos, "%sOSEN",
 				  pos == start ? "" : "+");
@@ -2608,6 +2643,14 @@
 		pos += ret;
 	}
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_FILS
+	if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
+		ret = os_snprintf(pos, end - pos, "[FILS]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_FST
 	if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
 		ret = os_snprintf(pos, end - pos, "[FST]");
@@ -4006,6 +4049,16 @@
 	}
 #endif /* CONFIG_ACS */
 
+#ifdef CONFIG_FILS
+	if (os_strcmp(field, "fils") == 0 &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) {
+		res = os_snprintf(buf, buflen, "FILS");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_FILS */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -4236,6 +4289,14 @@
 			pos += ret;
 		}
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_FILS
+		if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
+			ret = os_snprintf(pos, end - pos, "[FILS]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+#endif /* CONFIG_FILS */
 
 		ret = os_snprintf(pos, end - pos, "\n");
 		if (os_snprintf_error(end - pos, ret))
@@ -6383,6 +6444,7 @@
 	u16 id[MAX_ANQP_INFO_ID];
 	size_t num_id = 0;
 	u32 subtypes = 0;
+	int get_cell_pref = 0;
 
 	used = hwaddr_aton2(dst, dst_addr);
 	if (used < 0)
@@ -6400,6 +6462,15 @@
 #else /* CONFIG_HS20 */
 			return -1;
 #endif /* CONFIG_HS20 */
+		} else if (os_strncmp(pos, "mbo:", 4) == 0) {
+#ifdef CONFIG_MBO
+			int num = atoi(pos + 4);
+			if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+				return -1;
+			get_cell_pref = 1;
+#else /* CONFIG_MBO */
+			return -1;
+#endif /* CONFIG_MBO */
 		} else {
 			id[num_id] = atoi(pos);
 			if (id[num_id])
@@ -6414,7 +6485,8 @@
 	if (num_id == 0)
 		return -1;
 
-	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
+	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
+			     get_cell_pref);
 }
 
 
@@ -6751,6 +6823,9 @@
 		autoscan_init(wpa_s, 1);
 	else if (state == WPA_SCANNING)
 		wpa_supplicant_reinit_autoscan(wpa_s);
+	else
+		wpa_printf(MSG_DEBUG, "No autoscan update in state %s",
+			   wpa_supplicant_state_txt(state));
 
 	return 0;
 }
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index 1d5dd1c..f16e229 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -325,7 +325,7 @@
  * @wpa_s: %wpa_supplicant data structure
  * Returns: NULL on success or DBus error on failure
  *
- * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull
+ * Handler for "Cancel" method call. Returns NULL if WPS cancel successful
  * or DBus error on WPS cancel failure
  */
 DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 1d05198..aca9e81 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -370,9 +370,13 @@
 # amount of memory/flash.
 #CONFIG_DYNAMIC_EAP_METHODS=y
 
-# IEEE Std 802.11r-2008 (Fast BSS Transition)
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode
 #CONFIG_IEEE80211R=y
 
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for AP mode (implies
+# CONFIG_IEEE80211R).
+#CONFIG_IEEE80211R_AP=y
+
 # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
 #CONFIG_DEBUG_FILE=y
 
@@ -548,3 +552,13 @@
 
 # Support Multi Band Operation
 #CONFIG_MBO=y
+
+# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
+# Note: This is an experimental and not yet complete implementation. This
+# should not be enabled for production use.
+#CONFIG_FILS=y
+
+# Support RSN on IBSS networks
+# This is needed to be able to use mode=1 network profile with proto=RSN and
+# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None).
+#CONFIG_IBSS_RSN=y
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 220b7ba..c9bb20d 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -715,6 +715,14 @@
 	return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
 }
 
+static inline int wpa_drv_macsec_get_capability(struct wpa_supplicant *wpa_s,
+						enum macsec_cap *cap)
+{
+	if (!wpa_s->driver->macsec_get_capability)
+		return -1;
+	return wpa_s->driver->macsec_get_capability(wpa_s->drv_priv, cap);
+}
+
 static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
 						Boolean enabled)
 {
@@ -749,145 +757,127 @@
 }
 
 static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s,
-						u32 channel, u8 an,
-						u32 *lowest_pn)
+						struct receive_sa *sa)
 {
 	if (!wpa_s->driver->get_receive_lowest_pn)
 		return -1;
-	return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel,
-						    an, lowest_pn);
+	return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s,
-						u32 channel, u8 an,
-						u32 *next_pn)
+						struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->get_transmit_next_pn)
 		return -1;
-	return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel,
-						    an, next_pn);
+	return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
-						u32 channel, u8 an,
-						u32 next_pn)
+						struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->set_transmit_next_pn)
 		return -1;
-	return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel,
-						    an, next_pn);
-}
-
-static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s,
-						   u32 *channel)
-{
-	if (!wpa_s->driver->get_available_receive_sc)
-		return -1;
-	return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv,
-						       channel);
+	return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, sa);
 }
 
 static inline int
-wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel,
-			  const u8 *sci_addr, u16 sci_port,
+wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, struct receive_sc *sc,
 			  unsigned int conf_offset, int validation)
 {
 	if (!wpa_s->driver->create_receive_sc)
 		return -1;
-	return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel,
-						sci_addr, sci_port, conf_offset,
-						validation);
+	return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, sc,
+						conf_offset, validation);
 }
 
 static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s,
-					    u32 channel)
+					    struct receive_sc *sc)
 {
 	if (!wpa_s->driver->delete_receive_sc)
 		return -1;
-	return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel);
+	return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, sc);
 }
 
 static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s,
-					    u32 channel, u8 an,
-					    u32 lowest_pn, const u8 *sak)
+					    struct receive_sa *sa)
 {
 	if (!wpa_s->driver->create_receive_sa)
 		return -1;
-	return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an,
-						lowest_pn, sak);
+	return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, sa);
+}
+
+static inline int wpa_drv_delete_receive_sa(struct wpa_supplicant *wpa_s,
+					    struct receive_sa *sa)
+{
+	if (!wpa_s->driver->delete_receive_sa)
+		return -1;
+	return wpa_s->driver->delete_receive_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
-					    u32 channel, u8 an)
+					    struct receive_sa *sa)
 {
 	if (!wpa_s->driver->enable_receive_sa)
 		return -1;
-	return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
-					     u32 channel, u8 an)
+					     struct receive_sa *sa)
 {
 	if (!wpa_s->driver->disable_receive_sa)
 		return -1;
-	return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int
-wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel)
-{
-	if (!wpa_s->driver->get_available_transmit_sc)
-		return -1;
-	return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv,
-							channel);
-}
-
-static inline int
-wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel,
-			   const u8 *sci_addr, u16 sci_port,
+wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, struct transmit_sc *sc,
 			   unsigned int conf_offset)
 {
 	if (!wpa_s->driver->create_transmit_sc)
 		return -1;
-	return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel,
-						 sci_addr, sci_port,
+	return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, sc,
 						 conf_offset);
 }
 
 static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s,
-					     u32 channel)
+					     struct transmit_sc *sc)
 {
 	if (!wpa_s->driver->delete_transmit_sc)
 		return -1;
-	return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel);
+	return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, sc);
 }
 
 static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s,
-					     u32 channel, u8 an,
-					     u32 next_pn,
-					     Boolean confidentiality,
-					     const u8 *sak)
+					     struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->create_transmit_sa)
 		return -1;
-	return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an,
-						 next_pn, confidentiality, sak);
+	return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, sa);
+}
+
+static inline int wpa_drv_delete_transmit_sa(struct wpa_supplicant *wpa_s,
+					     struct transmit_sa *sa)
+{
+	if (!wpa_s->driver->delete_transmit_sa)
+		return -1;
+	return wpa_s->driver->delete_transmit_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s,
-					     u32 channel, u8 an)
+					     struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->enable_transmit_sa)
 		return -1;
-	return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s,
-					      u32 channel, u8 an)
+					      struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->disable_transmit_sa)
 		return -1;
-	return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, sa);
 }
 #endif /* CONFIG_MACSEC */
 
@@ -976,4 +966,13 @@
 	return wpa_s->driver->set_default_scan_ies(wpa_s->drv_priv, ies, len);
 }
 
+static inline int wpa_drv_set_tdls_mode(struct wpa_supplicant *wpa_s,
+					int tdls_external_control)
+{
+	if (!wpa_s->driver->set_tdls_mode)
+		return -1;
+	return wpa_s->driver->set_tdls_mode(wpa_s->drv_priv,
+					    tdls_external_control);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6f8fc81..17f057a 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1047,6 +1047,16 @@
 			continue;
 		}
 
+#ifndef CONFIG_IBSS_RSN
+		if (ssid->mode == WPAS_MODE_IBSS &&
+		    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
+					WPA_KEY_MGMT_WPA_NONE))) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - IBSS RSN not supported in the build");
+			continue;
+		}
+#endif /* !CONFIG_IBSS_RSN */
+
 #ifdef CONFIG_P2P
 		if (ssid->p2p_group &&
 		    !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
@@ -1332,6 +1342,17 @@
 		{
 			if (wpas_network_disabled(wpa_s, ssid))
 				continue;
+#ifndef CONFIG_IBSS_RSN
+			if (ssid->mode == WPAS_MODE_IBSS &&
+			    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
+						WPA_KEY_MGMT_WPA_NONE))) {
+				wpa_msg(wpa_s, MSG_INFO,
+					"IBSS RSN not supported in the build - cannot use the profile for SSID '%s'",
+					wpa_ssid_txt(ssid->ssid,
+						     ssid->ssid_len));
+				continue;
+			}
+#endif /* !CONFIG_IBSS_RSN */
 			if (ssid->mode == IEEE80211_MODE_IBSS ||
 			    ssid->mode == IEEE80211_MODE_AP ||
 			    ssid->mode == IEEE80211_MODE_MESH)
@@ -1474,11 +1495,18 @@
 }
 
 
-/* Return != 0 if no scan results could be fetched or if scan results should not
- * be shared with other virtual interfaces. */
+/*
+ * Return a negative value if no scan results could be fetched or if scan
+ * results should not be shared with other virtual interfaces.
+ * Return 0 if scan results were fetched and may be shared with other
+ * interfaces.
+ * Return 1 if scan results may be shared with other virtual interfaces but may
+ * not trigger any operations.
+ * Return 2 if the interface was removed and cannot be used.
+ */
 static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 					      union wpa_event_data *data,
-					      int own_request)
+					      int own_request, int update_only)
 {
 	struct wpa_scan_results *scan_res = NULL;
 	int ret = 0;
@@ -1528,6 +1556,11 @@
 	}
 #endif /* CONFIG_NO_RANDOM_POOL */
 
+	if (update_only) {
+		ret = 1;
+		goto scan_work_done;
+	}
+
 	if (own_request && wpa_s->scan_res_handler &&
 	    !(data && data->scan_info.external_scan)) {
 		void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
@@ -1536,7 +1569,7 @@
 		scan_res_handler = wpa_s->scan_res_handler;
 		wpa_s->scan_res_handler = NULL;
 		scan_res_handler(wpa_s, scan_res);
-		ret = -2;
+		ret = 1;
 		goto scan_work_done;
 	}
 
@@ -1672,8 +1705,9 @@
 		if (new_scan)
 			wpa_supplicant_rsn_preauth_scan_results(wpa_s);
 		/*
-		 * Do not notify other virtual radios of scan results since we do not
-		 * want them to start other associations at the same time.
+		 * Do not allow other virtual radios to trigger operations based
+		 * on these scan results since we do not want them to start
+		 * other associations at the same time.
 		 */
 		return 1;
 	} else {
@@ -1757,7 +1791,7 @@
 	struct wpa_supplicant *ifs;
 	int res;
 
-	res = _wpa_supplicant_event_scan_results(wpa_s, data, 1);
+	res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0);
 	if (res == 2) {
 		/*
 		 * Interface may have been removed, so must not dereference
@@ -1765,7 +1799,8 @@
 		 */
 		return 1;
 	}
-	if (res != 0) {
+
+	if (res < 0) {
 		/*
 		 * If no scan results could be fetched, then no need to
 		 * notify those interfaces that did not actually request
@@ -1785,7 +1820,10 @@
 		if (ifs != wpa_s) {
 			wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
 				   "sibling", ifs->ifname);
-			_wpa_supplicant_event_scan_results(ifs, data, 0);
+			res = _wpa_supplicant_event_scan_results(ifs, data, 0,
+								 res > 0);
+			if (res < 0)
+				return 0;
 		}
 	}
 
@@ -2041,6 +2079,19 @@
 	if (!found && data->assoc_info.req_ies)
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 
+#ifdef CONFIG_FILS
+#ifdef CONFIG_SME
+	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS &&
+	    (!data->assoc_info.resp_frame ||
+	     fils_process_assoc_resp(wpa_s->wpa,
+				     data->assoc_info.resp_frame,
+				     data->assoc_info.resp_frame_len) < 0)) {
+		wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
+		return -1;
+	}
+#endif /* CONFIG_SME */
+#endif /* CONFIG_FILS */
+
 #ifdef CONFIG_IEEE80211R
 #ifdef CONFIG_SME
 	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
@@ -2262,6 +2313,13 @@
 	ft_completed = wpa_ft_is_completed(wpa_s->wpa);
 	if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
 		return;
+	/*
+	 * FILS authentication can share the same mechanism to mark the
+	 * connection fully authenticated, so set ft_completed also based on
+	 * FILS result.
+	 */
+	if (!ft_completed)
+		ft_completed = wpa_fils_is_completed(wpa_s->wpa);
 
 	if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
 		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
@@ -2518,6 +2576,7 @@
 	struct wpa_bss *fast_reconnect = NULL;
 	struct wpa_ssid *fast_reconnect_ssid = NULL;
 	struct wpa_ssid *last_ssid;
+	struct wpa_bss *curr = NULL;
 
 	authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
 	os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
@@ -2533,6 +2592,19 @@
 		return;
 	}
 
+	if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING &&
+	    reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY &&
+	    locally_generated)
+		/*
+		 * Remove the inactive AP (which is probably out of range) from
+		 * the BSS list after marking disassociation. In particular
+		 * mac80211-based drivers use the
+		 * WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY reason code in
+		 * locally generated disconnection events for cases where the
+		 * AP does not reply anymore.
+		 */
+		curr = wpa_s->current_bss;
+
 	if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
 		wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
 			"pre-shared key may be incorrect");
@@ -2594,6 +2666,9 @@
 	last_ssid = wpa_s->current_ssid;
 	wpa_supplicant_mark_disassoc(wpa_s);
 
+	if (curr)
+		wpa_bss_remove(wpa_s, curr, "Connection to AP lost");
+
 	if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
 		sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
 		wpa_s->current_ssid = last_ssid;
@@ -3219,14 +3294,16 @@
 		free_hw_features(ifs);
 		ifs->hw.modes = wpa_drv_get_hw_feature_data(
 			ifs, &ifs->hw.num_modes, &ifs->hw.flags);
-	}
 
-	/* Restart sched_scan with updated channel list */
-	if (wpa_s->sched_scanning) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Channel list changed restart sched scan.");
-		wpa_supplicant_cancel_sched_scan(wpa_s);
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		/* Restart PNO/sched_scan with updated channel list */
+		if (ifs->pno) {
+			wpas_stop_pno(ifs);
+			wpas_start_pno(ifs);
+		} else if (ifs->sched_scanning && !ifs->pno_sched_pending) {
+			wpa_dbg(ifs, MSG_DEBUG,
+				"Channel list changed - restart sched_scan");
+			wpas_scan_restart_sched_scan(ifs);
+		}
 	}
 
 	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
@@ -4047,6 +4124,20 @@
 			break;
 
 		/*
+		 * If the driver stopped scanning without being requested to,
+		 * request a new scan to continue scanning for networks.
+		 */
+		if (!wpa_s->sched_scan_stop_req &&
+		    wpa_s->wpa_state == WPA_SCANNING) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Restart scanning after unexpected sched_scan stop event");
+			wpa_supplicant_req_scan(wpa_s, 1, 0);
+			break;
+		}
+
+		wpa_s->sched_scan_stop_req = 0;
+
+		/*
 		 * Start a new sched scan to continue searching for more SSIDs
 		 * either if timed out or PNO schedule scan is pending.
 		 */
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 697810e..1fb40c7 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -2692,10 +2692,11 @@
 
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-		  u16 info_ids[], size_t num_ids, u32 subtypes)
+		  u16 info_ids[], size_t num_ids, u32 subtypes,
+		  int get_cell_pref)
 {
 	struct wpabuf *buf;
-	struct wpabuf *hs20_buf = NULL;
+	struct wpabuf *extra_buf = NULL;
 	int ret = 0;
 	int freq;
 	struct wpa_bss *bss;
@@ -2718,15 +2719,31 @@
 
 #ifdef CONFIG_HS20
 	if (subtypes != 0) {
-		hs20_buf = wpabuf_alloc(100);
-		if (hs20_buf == NULL)
+		extra_buf = wpabuf_alloc(100);
+		if (extra_buf == NULL)
 			return -1;
-		hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
+		hs20_put_anqp_req(subtypes, NULL, 0, extra_buf);
 	}
 #endif /* CONFIG_HS20 */
 
-	buf = anqp_build_req(info_ids, num_ids, hs20_buf);
-	wpabuf_free(hs20_buf);
+#ifdef CONFIG_MBO
+	if (get_cell_pref) {
+		struct wpabuf *mbo;
+
+		mbo = mbo_build_anqp_buf(wpa_s, bss);
+		if (mbo) {
+			if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) {
+				wpabuf_free(extra_buf);
+				return -1;
+			}
+			wpabuf_put_buf(extra_buf, mbo);
+			wpabuf_free(mbo);
+		}
+	}
+#endif /* CONFIG_MBO */
+
+	buf = anqp_build_req(info_ids, num_ids, extra_buf);
+	wpabuf_free(extra_buf);
 	if (buf == NULL)
 		return -1;
 
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 3743dc0..3d22292 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -12,7 +12,8 @@
 enum gas_query_result;
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-		  u16 info_ids[], size_t num_ids, u32 subtypes);
+		  u16 info_ids[], size_t num_ids, u32 subtypes,
+		  int get_cell_pref);
 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
 		  enum gas_query_result result,
 		  const struct wpabuf *adv_proto,
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 13d764e..7e049be 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -14,6 +14,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/gas.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
@@ -805,3 +806,31 @@
 	wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
 	wpa_supplicant_set_default_scan_ies(wpa_s);
 }
+
+
+struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss)
+{
+	struct wpabuf *anqp_buf;
+	u8 *len_pos;
+
+	if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
+		wpa_printf(MSG_INFO, "MBO: " MACSTR
+			   " does not support MBO - cannot request MBO ANQP elements from it",
+			   MAC2STR(bss->bssid));
+		return NULL;
+	}
+
+	anqp_buf = wpabuf_alloc(10);
+	if (!anqp_buf)
+		return NULL;
+
+	len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(anqp_buf, OUI_WFA);
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
+
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+	gas_anqp_set_element_len(anqp_buf, len_pos);
+
+	return anqp_buf;
+}
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index d14c7e3..6c3fa14 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -1135,7 +1135,7 @@
 	 */
 	if (!sta && action_field == PLINK_OPEN &&
 	    (!(mconf->security & MESH_CONF_SEC_AMPE) ||
-	     wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa)))
+	     wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa, NULL)))
 		sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
 
 	if (!sta) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 27ab8cb..b1cf138 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -333,7 +333,7 @@
 			return -1;
 	}
 
-	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr);
+	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL);
 	if (pmksa) {
 		if (!sta->wpa_sm)
 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
@@ -579,7 +579,7 @@
 	/* encrypt after MIC */
 	mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE);
 
-	if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3,
+	if (aes_siv_encrypt(sta->aek, sizeof(sta->aek), ampe_ie, 2 + len, 3,
 			    aad, aad_len, mic_payload)) {
 		wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
 		ret = -ENOMEM;
@@ -611,7 +611,7 @@
 	if (!sta->sae) {
 		struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 
-		if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) {
+		if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL)) {
 			wpa_printf(MSG_INFO,
 				   "Mesh RSN: SAE is not prepared yet");
 			return -1;
@@ -650,7 +650,7 @@
 
 	os_memcpy(crypt, elems->mic, crypt_len);
 
-	if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3,
+	if (aes_siv_decrypt(sta->aek, sizeof(sta->aek), crypt, crypt_len, 3,
 			    aad, aad_len, ampe_buf)) {
 		wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!");
 		ret = -2;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b1fdc28..6465e2f 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -807,7 +807,7 @@
 					      wpa_s->own_addr);
 		} else if (!s && !go_wpa_s) {
 			if (wpas_p2p_add_group_interface(wpa_s,
-							 WPA_IF_P2P_GO) < 0) {
+							 WPA_IF_P2P_GROUP) < 0) {
 				wpa_printf(MSG_ERROR,
 					   "P2P: Failed to allocate a new interface for the group");
 				return P2PS_SETUP_NONE;
@@ -5236,8 +5236,10 @@
 		if (!res && max_pref_freq > 0) {
 			*num_pref_freq = max_pref_freq;
 			i = 0;
-			while (wpas_p2p_disallowed_freq(wpa_s->global,
-							pref_freq_list[i]) &&
+			while ((!p2p_supported_freq(wpa_s->global->p2p,
+						    pref_freq_list[i]) ||
+				wpas_p2p_disallowed_freq(wpa_s->global,
+							pref_freq_list[i])) &&
 			       i < *num_pref_freq) {
 				wpa_printf(MSG_DEBUG,
 					   "P2P: preferred_freq_list[%d]=%d is disallowed",
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 60e7b68..172772d 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1256,6 +1256,8 @@
 	if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
 		return -1;
 
+	wpa_s->sched_scan_stop_req = 0;
+
 	if (wpa_s->sched_scanning) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
 		return 0;
@@ -1554,6 +1556,9 @@
 	if (!wpa_s->sched_scanning)
 		return;
 
+	if (wpa_s->sched_scanning)
+		wpa_s->sched_scan_stop_req = 1;
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
 	eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
 	wpa_supplicant_stop_sched_scan(wpa_s);
@@ -1613,7 +1618,13 @@
  */
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
 {
-	return get_ie((const u8 *) (res + 1), res->ie_len, ie);
+	size_t ie_len = res->ie_len;
+
+	/* Use the Beacon frame IEs if res->ie_len is not available */
+	if (!ie_len)
+		ie_len = res->beacon_ie_len;
+
+	return get_ie((const u8 *) (res + 1), ie_len, ie);
 }
 
 
@@ -2172,8 +2183,10 @@
 	}
 #endif /* CONFIG_WPS */
 
-	qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
-	      compar);
+	if (scan_res->res) {
+		qsort(scan_res->res, scan_res->num,
+		      sizeof(struct wpa_scan_res *), compar);
+	}
 	dump_scan_res(scan_res);
 
 	wpa_bss_update_start(wpa_s);
@@ -2415,6 +2428,13 @@
 		}
 	}
 
+	if (wpa_s->sched_scan_stop_req) {
+		wpa_printf(MSG_DEBUG,
+			   "Schedule PNO after previous sched scan has stopped");
+		wpa_s->pno_sched_pending = 1;
+		return 0;
+	}
+
 	os_memset(&params, 0, sizeof(params));
 
 	num_ssid = num_match_ssid = 0;
@@ -2530,6 +2550,7 @@
 		return 0;
 
 	ret = wpa_supplicant_stop_sched_scan(wpa_s);
+	wpa_s->sched_scan_stop_req = 1;
 
 	wpa_s->pno = 0;
 	wpa_s->pno_sched_pending = 0;
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 61fd3b2..ab71f6d 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -543,12 +543,37 @@
 			wpas_connection_failed(wpa_s, bss->bssid);
 			return;
 		}
-		params.sae_data = wpabuf_head(resp);
-		params.sae_data_len = wpabuf_len(resp);
+		params.auth_data = wpabuf_head(resp);
+		params.auth_data_len = wpabuf_len(resp);
 		wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
 	}
 #endif /* CONFIG_SAE */
 
+	old_ssid = wpa_s->current_ssid;
+	wpa_s->current_ssid = ssid;
+	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+	wpa_supplicant_initiate_eapol(wpa_s);
+
+#ifdef CONFIG_FILS
+	/* TODO: FILS operations can in some cases be done between different
+	 * network_ctx (i.e., same credentials can be used with multiple
+	 * networks). */
+	if (params.auth_alg == WPA_AUTH_ALG_OPEN &&
+	    wpa_key_mgmt_fils(ssid->key_mgmt)) {
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+					    ssid, 0) == 0)
+			wpa_printf(MSG_DEBUG,
+				   "SME: Try to use FILS with PMKSA caching");
+		resp = fils_build_auth(wpa_s->wpa);
+		if (resp) {
+			params.auth_alg = WPA_AUTH_ALG_FILS;
+			params.auth_data = wpabuf_head(resp);
+			params.auth_data_len = wpabuf_len(resp);
+			wpa_s->sme.auth_alg = WPA_AUTH_ALG_FILS;
+		}
+	}
+#endif /* CONFIG_FILS */
+
 	wpa_supplicant_cancel_sched_scan(wpa_s);
 	wpa_supplicant_cancel_scan(wpa_s);
 
@@ -558,10 +583,6 @@
 
 	wpa_clear_keys(wpa_s, bss->bssid);
 	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
-	old_ssid = wpa_s->current_ssid;
-	wpa_s->current_ssid = ssid;
-	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
-	wpa_supplicant_initiate_eapol(wpa_s);
 	if (old_ssid != wpa_s->current_ssid)
 		wpas_notify_network_changed(wpa_s);
 
@@ -650,6 +671,10 @@
 		return;
 	}
 
+	/* Starting new connection, so clear the possibly used WPA IE from the
+	 * previous association. */
+	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
 	sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
 }
 
@@ -933,6 +958,24 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_FILS
+	if (data->auth.auth_type == WLAN_AUTH_FILS_SK) {
+		if (fils_process_auth(wpa_s->wpa, data->auth.ies,
+				      data->auth.ies_len) < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: FILS Authentication response processing failed");
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+				MACSTR
+				" reason=%d locally_generated=1",
+				MAC2STR(wpa_s->pending_bssid),
+				WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			return;
+		}
+	}
+#endif /* CONFIG_FILS */
+
 	sme_associate(wpa_s, ssid->mode, data->auth.peer,
 		      data->auth.auth_type);
 }
@@ -943,6 +986,9 @@
 {
 	struct wpa_driver_associate_params params;
 	struct ieee802_11_elems elems;
+#ifdef CONFIG_FILS
+	u8 nonces[2 * FILS_NONCE_LEN];
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_HT_OVERRIDES
 	struct ieee80211_ht_capabilities htcaps;
 	struct ieee80211_ht_capabilities htcaps_mask;
@@ -953,6 +999,37 @@
 #endif /* CONFIG_VHT_OVERRIDES */
 
 	os_memset(&params, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+	if (auth_type == WLAN_AUTH_FILS_SK) {
+		struct wpabuf *buf;
+		const u8 *snonce, *anonce;
+
+		buf = fils_build_assoc_req(wpa_s->wpa, &params.fils_kek,
+					   &params.fils_kek_len, &snonce,
+					   &anonce);
+		if (!buf)
+			return;
+		/* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
+		if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: Not enough buffer room for own AssocReq elements");
+			wpabuf_free(buf);
+			return;
+		}
+		os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+			  wpabuf_head(buf), wpabuf_len(buf));
+		wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
+		wpabuf_free(buf);
+
+		os_memcpy(nonces, snonce, FILS_NONCE_LEN);
+		os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+		params.fils_nonces = nonces;
+		params.fils_nonces_len = sizeof(nonces);
+	}
+#endif /* CONFIG_FILS */
+
 	params.bssid = bssid;
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
diff --git a/wpa_supplicant/wmm_ac.h b/wpa_supplicant/wmm_ac.h
index 5171b16..0d15ad0 100644
--- a/wpa_supplicant/wmm_ac.h
+++ b/wpa_supplicant/wmm_ac.h
@@ -88,7 +88,7 @@
  */
 struct wmm_ac_addts_request {
 	/*
-	 * dialog token - Used to link the recived ADDTS response with this
+	 * dialog token - Used to link the received ADDTS response with this
 	 * saved ADDTS request when ADDTS response is being handled
 	 */
 	u8 dialog_token;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index a848b77..4877989 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -60,6 +60,9 @@
 static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */
+#ifdef CONFIG_AP
+static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
+#endif /* CONFIG_AP */
 
 
 static void print_help(const char *cmd);
@@ -68,6 +71,7 @@
 static char * wpa_cli_get_default_ifname(void);
 static char ** wpa_list_cmd_list(void);
 static void update_networks(struct wpa_ctrl *ctrl);
+static void update_stations(struct wpa_ctrl *ctrl);
 
 
 static void usage(void)
@@ -214,7 +218,7 @@
 }
 
 
-static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
 {
 	char buf[4096];
 	size_t len;
@@ -250,7 +254,7 @@
 }
 
 
-static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+static int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
 {
 	return _wpa_ctrl_command(ctrl, cmd, 1);
 }
@@ -1736,8 +1740,23 @@
 }
 
 
-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
-				char *addr, size_t addr_len)
+static char ** wpa_cli_complete_sta(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+				char *addr, size_t addr_len, int print)
 {
 	char buf[4096], *pos;
 	size_t len;
@@ -1767,7 +1786,8 @@
 	buf[len] = '\0';
 	if (os_memcmp(buf, "FAIL", 4) == 0)
 		return -1;
-	printf("%s", buf);
+	if (print)
+		printf("%s", buf);
 
 	pos = buf;
 	while (*pos != '\0' && *pos != '\n')
@@ -1782,16 +1802,33 @@
 {
 	char addr[32], cmd[64];
 
-	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
 		return 0;
 	do {
 		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
-	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
 
 	return -1;
 }
 
 
+static int wpa_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
+				char *argv[])
+{
+	char addr[32], cmd[64];
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return 0;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			printf("%s\n", addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+
+	return 0;
+}
+
+
 static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
@@ -1799,12 +1836,43 @@
 }
 
 
+static char ** wpa_cli_complete_deauthenticate(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
 static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
 }
 
+
+static char ** wpa_cli_complete_disassociate(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
 static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
 {
@@ -2176,7 +2244,7 @@
 }
 
 
-static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd,
+static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, const char *cmd,
 				     char *addr, size_t addr_len,
 				     int discovered)
 {
@@ -2807,30 +2875,30 @@
 	{ "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<BSSID> = force preauthentication" },
-	{ "identity", wpa_cli_cmd_identity, NULL,
+	{ "identity", wpa_cli_cmd_identity, wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> <identity> = configure identity for an SSID" },
-	{ "password", wpa_cli_cmd_password, NULL,
+	{ "password", wpa_cli_cmd_password, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <password> = configure password for an SSID" },
-	{ "new_password", wpa_cli_cmd_new_password, NULL,
-	  cli_cmd_flag_sensitive,
+	{ "new_password", wpa_cli_cmd_new_password,
+	  wpa_cli_complete_network_id, cli_cmd_flag_sensitive,
 	  "<network id> <password> = change password for an SSID" },
-	{ "pin", wpa_cli_cmd_pin, NULL,
+	{ "pin", wpa_cli_cmd_pin, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <pin> = configure pin for an SSID" },
-	{ "otp", wpa_cli_cmd_otp, NULL,
+	{ "otp", wpa_cli_cmd_otp, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <password> = configure one-time-password for an SSID"
 	},
-	{ "passphrase", wpa_cli_cmd_passphrase, NULL,
+	{ "passphrase", wpa_cli_cmd_passphrase, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <passphrase> = configure private key passphrase\n"
 	  "  for an SSID" },
-	{ "sim", wpa_cli_cmd_sim, NULL,
+	{ "sim", wpa_cli_cmd_sim, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <pin> = report SIM operation result" },
-	{ "bssid", wpa_cli_cmd_bssid, NULL,
+	{ "bssid", wpa_cli_cmd_bssid, wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> <BSSID> = set preferred BSSID for an SSID" },
 	{ "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss,
@@ -3029,17 +3097,20 @@
 	  cli_cmd_flag_none,
 	  "<addr> = request RSN authentication with <addr> in IBSS" },
 #ifdef CONFIG_AP
-	{ "sta", wpa_cli_cmd_sta, NULL,
+	{ "sta", wpa_cli_cmd_sta, wpa_cli_complete_sta,
 	  cli_cmd_flag_none,
 	  "<addr> = get information about an associated station (AP)" },
 	{ "all_sta", wpa_cli_cmd_all_sta, NULL,
 	  cli_cmd_flag_none,
 	  "= get information about all associated stations (AP)" },
-	{ "deauthenticate", wpa_cli_cmd_deauthenticate, NULL,
+	{ "list_sta", wpa_cli_cmd_list_sta, NULL,
 	  cli_cmd_flag_none,
+	  "= list all stations (AP)" },
+	{ "deauthenticate", wpa_cli_cmd_deauthenticate,
+	  wpa_cli_complete_deauthenticate, cli_cmd_flag_none,
 	  "<addr> = deauthenticate a station" },
-	{ "disassociate", wpa_cli_cmd_disassociate, NULL,
-	  cli_cmd_flag_none,
+	{ "disassociate", wpa_cli_cmd_disassociate,
+	  wpa_cli_complete_disassociate, cli_cmd_flag_none,
 	  "<addr> = disassociate a station" },
 	{ "chan_switch", wpa_cli_cmd_chanswitch, NULL,
 	  cli_cmd_flag_none,
@@ -3675,6 +3746,7 @@
 		edit_clear_line();
 		printf("\rConnection to wpa_supplicant re-established\n");
 		edit_redraw();
+		update_stations(ctrl_conn);
 	}
 }
 
@@ -3897,7 +3969,7 @@
 	char buf[4096];
 	size_t len = sizeof(buf);
 	int ret;
-	char *cmd = "BSS RANGE=ALL MASK=0x2";
+	const char *cmd = "BSS RANGE=ALL MASK=0x2";
 	char *pos, *end;
 
 	if (ctrl == NULL)
@@ -3928,7 +4000,7 @@
 	char buf[4096];
 	size_t len = sizeof(buf);
 	int ret;
-	char *cmd = "INTERFACES";
+	const char *cmd = "INTERFACES";
 	char *pos, *end;
 	char txt[200];
 
@@ -3960,7 +4032,7 @@
 	char buf[4096];
 	size_t len = sizeof(buf);
 	int ret;
-	char *cmd = "LIST_NETWORKS";
+	const char *cmd = "LIST_NETWORKS";
 	char *pos, *end;
 	int header = 1;
 
@@ -3987,6 +4059,27 @@
 }
 
 
+static void update_stations(struct wpa_ctrl *ctrl)
+{
+#ifdef CONFIG_AP
+	char addr[32], cmd[64];
+
+	if (!ctrl || !interactive)
+		return;
+
+	cli_txt_list_flush(&stations);
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			cli_txt_list_add(&stations, addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+#endif /* CONFIG_AP */
+}
+
+
 static void try_connection(void *eloop_ctx, void *timeout_ctx)
 {
 	if (ctrl_conn)
@@ -4008,6 +4101,7 @@
 
 	update_bssid_list(ctrl_conn);
 	update_networks(ctrl_conn);
+	update_stations(ctrl_conn);
 
 	if (warning_displayed)
 		printf("Connection established.\n");
@@ -4254,6 +4348,7 @@
 					       "control interface\n");
 				}
 			}
+			update_stations(ctrl_conn);
 		}
 	}
 
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 511df4f..b36d195 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -218,7 +218,7 @@
 	}
 
 	auth = buf;
-	if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) {
+	if (sizeof(*auth) + auth->ie_len + auth->auth_data_len > len) {
 		wpa_printf(MSG_DEBUG, "Authentication request overflow");
 		return;
 	}
@@ -244,9 +244,9 @@
 		params.ie = (u8 *) (auth + 1);
 		params.ie_len = auth->ie_len;
 	}
-	if (auth->sae_data_len) {
-		params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len;
-		params.sae_data_len = auth->sae_data_len;
+	if (auth->auth_data_len) {
+		params.auth_data = ((u8 *) (auth + 1)) + auth->ie_len;
+		params.auth_data_len = auth->auth_data_len;
 	}
 
 	res = iface->driver->authenticate(iface->drv_priv, &params);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 282ef66..25ec502 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1233,6 +1233,22 @@
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"WPA: using KEY_MGMT 802.1X with Suite B");
 #endif /* CONFIG_SUITEB */
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R
+	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384");
+	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
+#endif /* CONFIG_IEEE80211R */
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384");
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R
 	} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
@@ -1466,6 +1482,11 @@
 		if (wpa_s->conf->ftm_initiator)
 			*pos |= 0x80; /* Bit 71 - FTM initiator */
 		break;
+	case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+		*pos |= 0x01;
+#endif /* CONFIG_FILS */
+		break;
 	}
 }
 
@@ -1473,11 +1494,8 @@
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 {
 	u8 *pos = buf;
-	u8 len = 6, i;
+	u8 len = 10, i;
 
-	if (len < 9 &&
-	    (wpa_s->conf->ftm_initiator || wpa_s->conf->ftm_responder))
-		len = 9;
 	if (len < wpa_s->extended_capa_len)
 		len = wpa_s->extended_capa_len;
 	if (buflen < (size_t) len + 2) {
@@ -1673,11 +1691,13 @@
 			wmm_ac_save_tspecs(wpa_s);
 			wpa_s->reassoc_same_bss = 1;
 		}
-	} else if (rand_style > 0) {
+	}
+
+	if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
 		if (wpas_update_random_addr(wpa_s, rand_style) < 0)
 			return;
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-	} else if (wpa_s->mac_addr_changed) {
+	} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
 		if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
 			wpa_msg(wpa_s, MSG_INFO,
 				"Could not restore permanent MAC address");
@@ -1696,6 +1716,13 @@
 #ifdef CONFIG_IBSS_RSN
 	ibss_rsn_deinit(wpa_s->ibss_rsn);
 	wpa_s->ibss_rsn = NULL;
+#else /* CONFIG_IBSS_RSN */
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"IBSS RSN not supported in the build");
+		return;
+	}
 #endif /* CONFIG_IBSS_RSN */
 
 	if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
@@ -2443,12 +2470,14 @@
 	if (bss) {
 		params.ssid = bss->ssid;
 		params.ssid_len = bss->ssid_len;
-		if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+		if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set ||
+		    wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
 			wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
 				   MACSTR " freq=%u MHz based on scan results "
-				   "(bssid_set=%d)",
+				   "(bssid_set=%d wps=%d)",
 				   MAC2STR(bss->bssid), bss->freq,
-				   ssid->bssid_set);
+				   ssid->bssid_set,
+				   wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
 			params.bssid = bss->bssid;
 			params.freq.freq = bss->freq;
 		}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index b3138e3..047ca90 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -822,6 +822,10 @@
 # WPA-EAP-SUITE-B = Suite B 128-bit level
 # WPA-EAP-SUITE-B-192 = Suite B 192-bit level
 # OSEN = Hotspot 2.0 Rel 2 online signup connection
+# FILS-SHA256 = Fast Initial Link Setup with SHA256
+# FILS-SHA384 = Fast Initial Link Setup with SHA384
+# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
+# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
 # If not set, this defaults to: WPA-PSK WPA-EAP
 #
 # ieee80211w: whether management frame protection is enabled
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4f5d41b..4a7e3c7 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -574,6 +574,7 @@
 	struct wpa_radio_work *scan_work;
 	int scanning;
 	int sched_scanning;
+	unsigned int sched_scan_stop_req:1;
 	int new_connection;
 
 	int eapol_received; /* number of EAPOL packets received after the
@@ -720,7 +721,7 @@
 		u8 ssid[SSID_MAX_LEN];
 		size_t ssid_len;
 		int freq;
-		u8 assoc_req_ie[200];
+		u8 assoc_req_ie[300];
 		size_t assoc_req_ie_len;
 		int mfp;
 		int ft_used;
@@ -1204,6 +1205,8 @@
 				    size_t len,
 				    enum mbo_transition_reject_reason reason);
 void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
+struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index d6ec8c5..e032330 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -38,6 +38,12 @@
 }
 
 
+static int wpas_macsec_get_capability(void *priv, enum macsec_cap *cap)
+{
+	return wpa_drv_macsec_get_capability(priv, cap);
+}
+
+
 static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled)
 {
 	return wpa_drv_enable_protect_frames(wpa_s, enabled);
@@ -62,30 +68,21 @@
 }
 
 
-static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel,
-				      u8 an, u32 *lowest_pn)
+static int wpas_get_receive_lowest_pn(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn);
+	return wpa_drv_get_receive_lowest_pn(wpa_s, sa);
 }
 
 
-static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel,
-				      u8 an, u32 *next_pn)
+static int wpas_get_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn);
+	return wpa_drv_get_transmit_next_pn(wpa_s, sa);
 }
 
 
-static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel,
-				      u8 an, u32 next_pn)
+static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn);
-}
-
-
-static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel)
-{
-	return wpa_drv_get_available_receive_sc(wpa_s, channel);
+	return wpa_drv_set_transmit_next_pn(wpa_s, sa);
 }
 
 
@@ -103,83 +100,79 @@
 }
 
 
-static int wpas_create_receive_sc(void *wpa_s, u32 channel,
-				  struct ieee802_1x_mka_sci *sci,
+static int wpas_create_receive_sc(void *wpa_s, struct receive_sc *sc,
 				  enum validate_frames vf,
 				  enum confidentiality_offset co)
 {
-	return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr,
-					 be_to_host16(sci->port),
-					 conf_offset_val(co), vf);
+	return wpa_drv_create_receive_sc(wpa_s, sc, conf_offset_val(co), vf);
 }
 
 
-static int wpas_delete_receive_sc(void *wpa_s, u32 channel)
+static int wpas_delete_receive_sc(void *wpa_s, struct receive_sc *sc)
 {
-	return wpa_drv_delete_receive_sc(wpa_s, channel);
+	return wpa_drv_delete_receive_sc(wpa_s, sc);
 }
 
 
-static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an,
-				  u32 lowest_pn, const u8 *sak)
+static int wpas_create_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak);
+	return wpa_drv_create_receive_sa(wpa_s, sa);
 }
 
 
-static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_delete_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_enable_receive_sa(wpa_s, channel, an);
+	return wpa_drv_delete_receive_sa(wpa_s, sa);
 }
 
 
-static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_enable_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_disable_receive_sa(wpa_s, channel, an);
+	return wpa_drv_enable_receive_sa(wpa_s, sa);
 }
 
 
-static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel)
+static int wpas_disable_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_get_available_transmit_sc(wpa_s, channel);
+	return wpa_drv_disable_receive_sa(wpa_s, sa);
 }
 
 
 static int
-wpas_create_transmit_sc(void *wpa_s, u32 channel,
-			const struct ieee802_1x_mka_sci *sci,
+wpas_create_transmit_sc(void *wpa_s, struct transmit_sc *sc,
 			enum confidentiality_offset co)
 {
-	return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr,
-					  be_to_host16(sci->port),
-					  conf_offset_val(co));
+	return wpa_drv_create_transmit_sc(wpa_s, sc, conf_offset_val(co));
 }
 
 
-static int wpas_delete_transmit_sc(void *wpa_s, u32 channel)
+static int wpas_delete_transmit_sc(void *wpa_s, struct transmit_sc *sc)
 {
-	return wpa_drv_delete_transmit_sc(wpa_s, channel);
+	return wpa_drv_delete_transmit_sc(wpa_s, sc);
 }
 
 
-static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an,
-				   u32 next_pn, Boolean confidentiality,
-				   const u8 *sak)
+static int wpas_create_transmit_sa(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn,
-					  confidentiality, sak);
+	return wpa_drv_create_transmit_sa(wpa_s, sa);
 }
 
 
-static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_delete_transmit_sa(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_enable_transmit_sa(wpa_s, channel, an);
+	return wpa_drv_delete_transmit_sa(wpa_s, sa);
 }
 
 
-static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_enable_transmit_sa(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_disable_transmit_sa(wpa_s, channel, an);
+	return wpa_drv_enable_transmit_sa(wpa_s, sa);
+}
+
+
+static int wpas_disable_transmit_sa(void *wpa_s, struct transmit_sa *sa)
+{
+	return wpa_drv_disable_transmit_sa(wpa_s, sa);
 }
 
 
@@ -204,6 +197,7 @@
 
 	kay_ctx->macsec_init = wpas_macsec_init;
 	kay_ctx->macsec_deinit = wpas_macsec_deinit;
+	kay_ctx->macsec_get_capability = wpas_macsec_get_capability;
 	kay_ctx->enable_protect_frames = wpas_enable_protect_frames;
 	kay_ctx->set_replay_protect = wpas_set_replay_protect;
 	kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite;
@@ -211,16 +205,16 @@
 	kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
 	kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
 	kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
-	kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc;
 	kay_ctx->create_receive_sc = wpas_create_receive_sc;
 	kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
 	kay_ctx->create_receive_sa = wpas_create_receive_sa;
+	kay_ctx->delete_receive_sa = wpas_delete_receive_sa;
 	kay_ctx->enable_receive_sa = wpas_enable_receive_sa;
 	kay_ctx->disable_receive_sa = wpas_disable_receive_sa;
-	kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc;
 	kay_ctx->create_transmit_sc = wpas_create_transmit_sc;
 	kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc;
 	kay_ctx->create_transmit_sa = wpas_create_transmit_sa;
+	kay_ctx->delete_transmit_sa = wpas_delete_transmit_sa;
 	kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa;
 	kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa;