Cumulative patch from commit 8960afa4bfa423774a8ca58cc21a4a7c1ab6089e

8960afa wpa_cli: Run action file in case of an AP event
1cece2f OpenSSL: Comment out tls_connection_get_eap_fast_key without EAP-FAST
7358170 TLS: Split tls_connection_prf() into two functions
f150db6 OpenSSL: Remove two more accesses of ssl_ctx->cert_store
6dc3206 scan: Fix a memory leak on an error path
9ce3e61 nl80211: Add TEST_FAIL() to nl80211_set_mac_addr()
8880240 scan: Clean up code a bit - phase1 is used in all WPS cases
e1ae287 scan: Clean up code a bit - ssid cannot be NULL here
a170394 Update ChangeLog files for v2.6
a26c9c2 Fix sending non-Public Action frames over P2P Device interface
4d916ed nl80211: Register for only for specific Action frames in AP mode
31d7fb1 P2PS: Allow P2P_CONNECT command for P2PS connection with/without PIN
467fc14 P2PS: Correct config_methods for different P2P cases
9d136b0 EAP-SAKE: Do not debug print result if eap_sake_compute_mic() fails
0884633 EAP-PAX: Do not debug print result if eap_pax_mac() fails
92abe37 EAP-FAST: Check sha1_t_prf() result in eap_fast_get_cmk()
636a238 WPS: Check sha256_vector() result in wps_build_oob_dev_pw()
2c3d95c Check md5_vector() result in decrypt_ms_key()
38eee0f Check hmac_md5() result in radius_msg_verify_msg_auth()
05dad94 Check md5_vector() result in radius_msg_verify()
aae125e WPS: Fix debug prints in wps_derive_psk() error case
7d1007a Fix external radio work debug printing on removal
b6317b4 wpa_supplicant: Add wps_disabled parameter to network block
23d71a0 Set wpa_psk_set in wpa_supplicant AP mode is PSK is available
6641954 Fix AP mode key_mgmt configuration in wpa_supplicant default case
ad6cee3 P2P: Do not enable P2P group processing for non-P2P AP mode
a185e9b tests/remote: Add hwsim wrapper
ff9bb8a tests/remote: Add test_example.py
a73fa13 tests/remote: Add utils file
ede4719 tests/remote: Add monitor.py
3b11ad3 Send CTRL-EVENT-REGDOM-CHANGE event on the parent interface
4de70e2 Add MGMT_RX_PROCESS test command for wpa_supplicant
f42c3ce mesh: Calculate MTK before sending it to MAC in case Open is dropped
baa1213 mesh: Add missing action to cancel timer
e8a1b6b D-Bus: Check driver capability for IBSS in Modes property of Capabilities
7a1887f wpa_cli: Add backspace key process for some terminal
3dd0e9e wpa_supplicant: Fix CONFIG_AP build without CTRL_IFACE
d58b60d drivers: Add NEED_RADIOTAP
6a9681e OpenSSL: Make dh5_init() match the generic implementation
46bac65 WPS: Fix segmentation fault in new DH key derivation
e447133 OpenSSL: BoringSSL has SSL_get_client_random(), etc.
03626e9 Skip connection attempt for non-RSN networks if PMF is set to required
2295004 Ignore pmf=1/2 parameter for non-RSN networks
2a3f565 Reject SET commands with newline characters in the string values
b166cd8 Reject SET_CRED commands with newline characters in the string values
0fe5a23 Remove newlines from wpa_supplicant config network output
73e4abb Reject psk parameter set with invalid passphrase character
ecbb0b3 WPS: Reject a Credential with invalid passphrase
f4830be nl80211: Try running without mgmt frame subscription (driver AP SME)
df5bde8 Android: Remove EAP-FAST option
60d9f67 WPS: Explicitly clear wpabuf memory with key information
9b377be P2P: Copy config from p2pdev when not using dedicated group interface
3c88d26 P2P: Fix wpas_p2p_nfc_auth_join()
2f19563 WNM: Fetch scan results before checking transition candidates
4ac3398 Use a shared helper function for parsing hostapd.conf IEs
a911227 Add assocresp_elements parameter for hostapd
49fe2ad OpenSSL: Support OpenSSL 1.1.0 DH opacity
b92d2a5 FT: Fix RRB for FT over-the-air case
9e5a5de systemd: Update service files according to D-Bus interface version
ac7aea8 Assign QCA vendor command/attributes for set/get wifi configuration
57b3888 P2P: Add P2P_GROUP_MEMBER command to fetch client interface address
0ee8925 P2P: Trigger event when invitation is accepted
bd86ea0 nl80211: Get rid of unused assignment warning
18ae3a6 bsd: Set level correctly for non FreeBSD systems
cf667c6 RRM: Modify the processing of a received neighbor report
00ed0aa SME: Add support for global RRM flag
b5d172e nl80211: Add support for global RRM flag
a7f0bb7 driver: Add global RRM support flag
864b952 nl80211: Register to receive Radio Measurement Request frames
4a74201 wpa_supplicant: Handle LCI request
220754c hostapd: Add FTM range request
f4f185a hostapd: Add LCI request
629e180 hostapd: Save RM enabled capability of station
2572df3 hostapd: Handle Neighbor Report Request frame
061269b hostapd: Add own neighbor report data to neighbor database
9b4b226 hostapd: Add a database of neighboring APs
0101821 hostapd: Extend the configuration of RRM capabilities
6a4f0ed Fix spelling of "neighbor" in a function name
d41a535 wpa_supplicant: Add LCI and civic request to Neighbor Report Request
624b8a0 utils: Add ssid_parse() function
e4fbc8d Add measurement and neighbor report definitions
9d955f7 utils: Rename hostapd_parse_bin to wpabuf_parse_bin and move it
74e982d hostapd: Set LCI and Location Civic information in configuration
1854eec Add POLL_STA command to check connectivity in AP mode
3dbfb28 Allow AP to disconnect STA without sending Deauth/Disassoc frame
de92314 Add inactive_msec into STA output
61c1011 Extend VENDOR_ELEM parameters to cover non-P2P Probe Request frame
6922d44 nl80211: Implement configure_data_frame_filters() callback
ece4ac5 HS 2.0: Add support for configuring frame filters
e42adb9 driver: Add a packet filtering function declaration
ae33239 AP: Pass station P2P PS capabilities info during station add/set
7405bb0 Sync with mac80211-next.git include/uapi/linux/nl80211.h
90f1496 wpa_supplicant: "don't care" value for pbss in ssid structure
e52a698 RADIUS: Fix a possible memory leak on an error path
f1863f2 RADIUS: Fix possible memory leak when parsing per-STA passphrase
3433721 P2P: Continue p2p_find after sending non-success Invitation Response
0f34665 Mark wpa_supplicant_{start,stop}_sched_scan() static

Also reverting:
c7f648c wpa_supplicant_8: Add a temporary hack to work around BoringSSL incompatibility
1e1c48d2 Update AP IE regardless WPA_DRIVER_FLAGS_BSS_SELECTION flag

Change-Id: Idb9bfa80e9d9a4d10323dab5ce2bb24f4baf550c
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 66b843c..2345dd9 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -567,6 +567,7 @@
 #endif /* CONFIG_HS20 */
 
 	wpabuf_free(conf->vendor_elements);
+	wpabuf_free(conf->assocresp_elements);
 
 	os_free(conf->sae_groups);
 
@@ -606,6 +607,8 @@
 #ifdef CONFIG_ACS
 	os_free(conf->acs_chan_bias);
 #endif /* CONFIG_ACS */
+	wpabuf_free(conf->lci);
+	wpabuf_free(conf->civic);
 
 	os_free(conf);
 }
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 2d07c67..b263eb0 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -557,6 +557,7 @@
 #endif /* CONFIG_RADIUS_TEST */
 
 	struct wpabuf *vendor_elements;
+	struct wpabuf *assocresp_elements;
 
 	unsigned int sae_anti_clogging_threshold;
 	int *sae_groups;
@@ -572,7 +573,7 @@
 #define MESH_ENABLED BIT(0)
 	int mesh;
 
-	int radio_measurements;
+	u8 radio_measurements[RRM_CAPABILITIES_IE_LEN];
 
 	int vendor_vht;
 
@@ -693,6 +694,9 @@
 	} *acs_chan_bias;
 	unsigned int num_acs_chan_bias;
 #endif /* CONFIG_ACS */
+
+	struct wpabuf *lci;
+	struct wpabuf *civic;
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index b89f60e..dca6b8b 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -179,6 +179,7 @@
 
 	add_buf(&beacon, hapd->conf->vendor_elements);
 	add_buf(&proberesp, hapd->conf->vendor_elements);
+	add_buf(&assocresp, hapd->conf->assocresp_elements);
 
 	*beacon_ret = beacon;
 	*proberesp_ret = proberesp;
@@ -362,7 +363,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo, u8 vht_opmode, int set)
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+		    int set)
 {
 	struct hostapd_sta_add_params params;
 
@@ -384,6 +386,7 @@
 	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
 	params.qosinfo = qosinfo;
+	params.support_p2p_ps = supp_p2p_ps;
 	params.set = set;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
 }
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 757a706..6ea1dab 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -41,7 +41,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo, u8 vht_opmode, int set);
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+		    int set);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
 			     size_t elem_len);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 19bff7b..0570ab7 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -36,18 +36,21 @@
 static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
 					 size_t len)
 {
-	if (!hapd->conf->radio_measurements || len < 2 + 4)
+	size_t i;
+
+	for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+		if (hapd->conf->radio_measurements[i])
+			break;
+	}
+
+	if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
 		return eid;
 
 	*eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
-	*eid++ = 5;
-	*eid++ = (hapd->conf->radio_measurements & BIT(0)) ?
-		WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00;
-	*eid++ = 0x00;
-	*eid++ = 0x00;
-	*eid++ = 0x00;
-	*eid++ = 0x00;
-	return eid;
+	*eid++ = RRM_CAPABILITIES_IE_LEN;
+	os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
+
+	return eid + RRM_CAPABILITIES_IE_LEN;
 }
 
 
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 317b238..917341c 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -36,9 +36,9 @@
 		return 0;
 
 	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
-			  "rx_bytes=%llu\ntx_bytes=%llu\n",
+			  "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n",
 			  data.rx_packets, data.tx_packets,
-			  data.rx_bytes, data.tx_bytes);
+			  data.rx_bytes, data.tx_bytes, data.inactive_msec);
 	if (os_snprintf_error(buflen, ret))
 		return 0;
 	return ret;
@@ -352,7 +352,10 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_deauth(hapd, addr, reason);
+	if (os_strstr(txtaddr, " tx=0"))
+		hostapd_drv_sta_remove(hapd, addr);
+	else
+		hostapd_drv_sta_deauth(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
 		ap_sta_deauthenticate(hapd, sta, reason);
@@ -412,7 +415,10 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_disassoc(hapd, addr, reason);
+	if (os_strstr(txtaddr, " tx=0"))
+		hostapd_drv_sta_remove(hapd, addr);
+	else
+		hostapd_drv_sta_disassoc(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
 		ap_sta_disassociate(hapd, sta, reason);
@@ -423,6 +429,27 @@
 }
 
 
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+				const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
+				sta->flags & WLAN_STA_WMM);
+	return 0;
+}
+
+
 int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
 			      size_t buflen)
 {
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 3ad622f..6095d7d 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -19,6 +19,8 @@
 				      const char *txtaddr);
 int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
 				    const char *txtaddr);
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+				const char *txtaddr);
 int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
 			      size_t buflen);
 int hostapd_parse_csa_settings(const char *pos,
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index ee80f4f..42c1aaa 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -43,6 +43,8 @@
 #include "x_snoop.h"
 #include "dhcp_snoop.h"
 #include "ndisc_snoop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -335,6 +337,8 @@
 	wpabuf_free(hapd->mesh_pending_auth);
 	hapd->mesh_pending_auth = NULL;
 #endif /* CONFIG_MESH */
+
+	hostapd_clean_rrm(hapd);
 }
 
 
@@ -906,6 +910,7 @@
 		return -1;
 	}
 	hapd->started = 1;
+	dl_list_init(&hapd->nr_db);
 
 	if (!first || first == -1) {
 		u8 *addr = hapd->own_addr;
@@ -1524,6 +1529,126 @@
 #endif /* CONFIG_FST */
 
 
+#ifdef NEED_AP_MLME
+static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+						    int ht, int vht)
+{
+	if (!ht && !vht)
+		return NR_CHAN_WIDTH_20;
+	if (!hapd->iconf->secondary_channel)
+		return NR_CHAN_WIDTH_20;
+	if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+		return NR_CHAN_WIDTH_40;
+	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+		return NR_CHAN_WIDTH_80;
+	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
+		return NR_CHAN_WIDTH_160;
+	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
+		return NR_CHAN_WIDTH_80P80;
+	return NR_CHAN_WIDTH_20;
+}
+#endif /* NEED_AP_MLME */
+
+
+static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
+{
+#ifdef NEED_AP_MLME
+	u16 capab = hostapd_own_capab_info(hapd);
+	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+	struct wpa_ssid_value ssid;
+	u8 channel, op_class;
+	int center_freq1 = 0, center_freq2 = 0;
+	enum nr_chan_width width;
+	u32 bssid_info;
+	struct wpabuf *nr;
+
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+		return;
+
+	bssid_info = 3; /* AP is reachable */
+	bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
+	bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
+
+	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
+		bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+
+	bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
+
+	if (hapd->conf->wmm_enabled) {
+		bssid_info |= NEI_REP_BSSID_INFO_QOS;
+
+		if (hapd->conf->wmm_uapsd &&
+		    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+			bssid_info |= NEI_REP_BSSID_INFO_APSD;
+	}
+
+	if (ht) {
+		bssid_info |= NEI_REP_BSSID_INFO_HT |
+			NEI_REP_BSSID_INFO_DELAYED_BA;
+
+		/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+		if (vht)
+			bssid_info |= NEI_REP_BSSID_INFO_VHT;
+	}
+
+	/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+
+	ieee80211_freq_to_channel_ext(hapd->iface->freq,
+				      hapd->iconf->secondary_channel,
+				      hapd->iconf->vht_oper_chwidth,
+				      &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);
+		if (width == NR_CHAN_WIDTH_80P80)
+			center_freq2 = ieee80211_chan_to_freq(
+				NULL, op_class,
+				hapd->iconf->vht_oper_centr_freq_seg1_idx);
+	} else if (ht) {
+		center_freq1 = hapd->iface->freq +
+			10 * hapd->iconf->secondary_channel;
+	}
+
+	ssid.ssid_len = hapd->conf->ssid.ssid_len;
+	os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+
+	/*
+	 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
+	 * phy type + wide bandwidth channel subelement.
+	 */
+	nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
+	if (!nr)
+		return;
+
+	wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
+	wpabuf_put_le32(nr, bssid_info);
+	wpabuf_put_u8(nr, op_class);
+	wpabuf_put_u8(nr, channel);
+	wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+
+	/*
+	 * Wide Bandwidth Channel subelement may be needed to allow the
+	 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
+	 * Figure 9-301.
+	 */
+	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);
+
+	hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
+			     hapd->iconf->civic);
+
+	wpabuf_free(nr);
+#endif /* NEED_AP_MLME */
+}
+
+
 static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 						 int err)
 {
@@ -1709,6 +1834,9 @@
 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
 		iface->interfaces->terminate_on_error--;
 
+	for (j = 0; j < iface->num_bss; j++)
+		hostapd_set_own_neighbor_report(iface->bss[j]);
+
 	return 0;
 
 fail:
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 75a7c04..4dba8cb 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -99,6 +99,16 @@
 	u8 peer_addr[ETH_ALEN];
 };
 
+struct hostapd_neighbor_entry {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	struct wpa_ssid_value ssid;
+	struct wpabuf *nr;
+	struct wpabuf *lci;
+	struct wpabuf *civic;
+	/* LCI update time */
+	struct os_time lci_date;
+};
 
 /**
  * struct hostapd_data - hostapd per-BSS data structure
@@ -286,6 +296,13 @@
 #ifdef CONFIG_MBO
 	unsigned int mbo_assoc_disallow;
 #endif /* CONFIG_MBO */
+
+	struct dl_list nr_db;
+
+	u8 lci_req_token;
+	u8 range_req_token;
+	unsigned int lci_req_active:1;
+	unsigned int range_req_active:1;
 };
 
 
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 6a373c5..781afa2 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -43,6 +43,7 @@
 #include "ieee802_11.h"
 #include "dfs.h"
 #include "mbo_ap.h"
+#include "rrm.h"
 
 
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -140,6 +141,7 @@
 	int capab = WLAN_CAPABILITY_ESS;
 	int privacy;
 	int dfs;
+	int i;
 
 	/* Check if any of configured channels require DFS */
 	dfs = hostapd_is_dfs_required(hapd->iface);
@@ -187,8 +189,12 @@
 	    (hapd->iconf->spectrum_mgmt_required || dfs))
 		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 
-	if (hapd->conf->radio_measurements)
-		capab |= IEEE80211_CAP_RRM;
+	for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+		if (hapd->conf->radio_measurements[i]) {
+			capab |= IEEE80211_CAP_RRM;
+			break;
+		}
+	}
 
 	return capab;
 }
@@ -1221,7 +1227,7 @@
 				WLAN_STA_AUTHORIZED);
 
 		if (hostapd_sta_add(hapd, sta->addr, 0, 0, 0, 0, 0,
-				    NULL, NULL, sta->flags, 0, 0, 0)) {
+				    NULL, NULL, sta->flags, 0, 0, 0, 0)) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_NOTICE,
@@ -1743,6 +1749,12 @@
 	ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
 				    elems.supp_op_classes_len);
 
+	if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
+	    elems.rrm_enabled &&
+	    elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
+		os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
+			  sizeof(sta->rrm_enabled_capa));
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -1805,7 +1817,8 @@
 			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
 			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
-			    sta->vht_opmode, sta->added_unassoc)) {
+			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
+			    sta->added_unassoc)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
 			       "Could not %s STA to kernel driver",
@@ -1947,6 +1960,14 @@
 
 	p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p);
 
+	if (hapd->conf->assocresp_elements &&
+	    (size_t) (buf + sizeof(buf) - p) >=
+	    wpabuf_len(hapd->conf->assocresp_elements)) {
+		os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+			  wpabuf_len(hapd->conf->assocresp_elements));
+		p += wpabuf_len(hapd->conf->assocresp_elements);
+	}
+
 	send_len += p - reply->u.assoc_resp.variable;
 
 	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
@@ -2087,6 +2108,12 @@
 	}
 #endif /* CONFIG_MBO */
 
+	/*
+	 * sta->capability is used in check_assoc_ies() for RRM enabled
+	 * capability element.
+	 */
+	sta->capability = capab_info;
+
 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
 	 * is used */
 	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -2100,7 +2127,6 @@
 		goto fail;
 	}
 
-	sta->capability = capab_info;
 	sta->listen_interval = listen_interval;
 
 	if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
@@ -2467,6 +2493,9 @@
 				return 1;
 		}
 		break;
+	case WLAN_ACTION_RADIO_MEASUREMENT:
+		hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
+		return 1;
 	}
 
 	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -3012,6 +3041,8 @@
 	}
 	if (sta == NULL)
 		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
+		MAC2STR(sta->addr));
 	if (!(sta->flags & WLAN_STA_PENDING_POLL))
 		return;
 
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 9609152..b890537 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -457,7 +457,7 @@
 
 		if (passphraselen < MIN_PASSPHRASE_LEN ||
 		    passphraselen > MAX_PASSPHRASE_LEN + 1)
-			continue;
+			goto free_pass;
 
 		/*
 		 * passphrase does not contain the NULL termination.
@@ -484,6 +484,7 @@
 		}
 skip:
 		os_free(psk);
+free_pass:
 		os_free(passphrase);
 	}
 }
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
new file mode 100644
index 0000000..a2efff6
--- /dev/null
+++ b/src/ap/neighbor_db.c
@@ -0,0 +1,133 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "neighbor_db.h"
+
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+		     const struct wpa_ssid_value *ssid)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
+		    (!ssid ||
+		     (ssid->ssid_len == nr->ssid.ssid_len &&
+		      os_memcmp(ssid->ssid, nr->ssid.ssid,
+				ssid->ssid_len) == 0)))
+			return nr;
+	}
+	return NULL;
+}
+
+
+static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
+{
+	wpabuf_free(nr->nr);
+	nr->nr = NULL;
+	wpabuf_free(nr->lci);
+	nr->lci = NULL;
+	wpabuf_free(nr->civic);
+	nr->civic = NULL;
+	os_memset(nr->bssid, 0, sizeof(nr->bssid));
+	os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+}
+
+
+static struct hostapd_neighbor_entry *
+hostapd_neighbor_add(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
+	if (!nr)
+		return NULL;
+
+	dl_list_add(&hapd->nr_db, &nr->list);
+
+	return nr;
+}
+
+
+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)
+{
+	struct hostapd_neighbor_entry *entry;
+
+	entry = hostapd_neighbor_get(hapd, bssid, ssid);
+	if (!entry)
+		entry = hostapd_neighbor_add(hapd);
+	if (!entry)
+		return -1;
+
+	hostapd_neighbor_clear_entry(entry);
+
+	os_memcpy(entry->bssid, bssid, ETH_ALEN);
+	os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
+
+	entry->nr = wpabuf_dup(nr);
+	if (!entry->nr)
+		goto fail;
+
+	if (lci) {
+		entry->lci = wpabuf_dup(lci);
+		if (!entry->lci || os_get_time(&entry->lci_date))
+			goto fail;
+	}
+
+	if (civic) {
+		entry->civic = wpabuf_dup(civic);
+		if (!entry->civic)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	hostapd_neighbor_remove(hapd, bssid, ssid);
+	return -1;
+}
+
+
+int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+			    const struct wpa_ssid_value *ssid)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	nr = hostapd_neighbor_get(hapd, bssid, ssid);
+	if (!nr)
+		return -1;
+
+	hostapd_neighbor_clear_entry(nr);
+	dl_list_del(&nr->list);
+	os_free(nr);
+
+	return 0;
+}
+
+
+void hostpad_free_neighbor_db(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr, *prev;
+
+	dl_list_for_each_safe(nr, prev, &hapd->nr_db,
+			      struct hostapd_neighbor_entry, list) {
+		hostapd_neighbor_clear_entry(nr);
+		dl_list_del(&nr->list);
+		os_free(nr);
+	}
+}
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
new file mode 100644
index 0000000..c22e043
--- /dev/null
+++ b/src/ap/neighbor_db.h
@@ -0,0 +1,24 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NEIGHBOR_DB_H
+#define NEIGHBOR_DB_H
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+		     const struct wpa_ssid_value *ssid);
+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);
+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);
+
+#endif /* NEIGHBOR_DB_H */
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
new file mode 100644
index 0000000..3569f95
--- /dev/null
+++ b/src/ap/rrm.c
@@ -0,0 +1,544 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "eloop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
+
+#define HOSTAPD_RRM_REQUEST_TIMEOUT 5
+
+
+static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
+		   hapd->lci_req_token);
+	hapd->lci_req_active = 0;
+}
+
+
+static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
+				      const u8 *pos, size_t len)
+{
+	if (!hapd->lci_req_active || hapd->lci_req_token != token) {
+		wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
+		return;
+	}
+
+	hapd->lci_req_active = 0;
+	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+	wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
+}
+
+
+static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
+		   hapd->range_req_token);
+	hapd->range_req_active = 0;
+}
+
+
+static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
+					const u8 *pos, size_t len)
+{
+	if (!hapd->range_req_active || hapd->range_req_token != token) {
+		wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
+			   token);
+		return;
+	}
+
+	hapd->range_req_active = 0;
+	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+	wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
+}
+
+
+static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
+					     const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+	const u8 *pos, *ie, *end;
+	u8 token;
+
+	end = buf + len;
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+
+	while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
+		if (ie[1] < 5) {
+			wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]);
+
+		switch (ie[4]) {
+		case MEASURE_TYPE_LCI:
+			hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
+			break;
+		case MEASURE_TYPE_FTM_RANGE:
+			hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "Measurement report type %u is not supported",
+				   ie[4]);
+			break;
+		}
+
+		pos = ie + ie[1] + 2;
+	}
+}
+
+
+static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
+{
+	const u8 *subelem;
+
+	/* Range Request element + Location Subject + Maximum Age subelement */
+	if (len < 3 + 1 + 4)
+		return 0;
+
+	/* Subelements are arranged as IEs */
+	subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
+	if (subelem && subelem[1] == 2)
+		return *(u16 *) (subelem + 2);
+
+	return 0;
+}
+
+
+static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
+{
+	struct os_time curr, diff;
+	unsigned long diff_l;
+
+	if (!max_age)
+		return 0;
+
+	if (max_age == 0xffff)
+		return 1;
+
+	if (os_get_time(&curr))
+		return 0;
+
+	os_time_sub(&curr, &nr->lci_date, &diff);
+
+	/* avoid overflow */
+	if (diff.sec > 0xffff)
+		return 0;
+
+	/* LCI age is calculated in 10th of a second units. */
+	diff_l = diff.sec * 10 + diff.usec / 100000;
+
+	return max_age > diff_l;
+}
+
+
+static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
+					  struct hostapd_neighbor_entry *nr,
+					  int send_lci, int send_civic)
+{
+	size_t len = 2 + wpabuf_len(nr->nr);
+
+	if (send_lci && nr->lci)
+		len += 2 + wpabuf_len(nr->lci);
+
+	if (send_civic && nr->civic)
+		len += 2 + wpabuf_len(nr->civic);
+
+	return len;
+}
+
+
+static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+					 const u8 *addr, u8 dialog_token,
+					 struct wpa_ssid_value *ssid, u8 lci,
+					 u8 civic, u16 lci_max_age)
+{
+	struct hostapd_neighbor_entry *nr;
+	struct wpabuf *buf;
+	u8 *msmt_token;
+
+	/*
+	 * The number and length of the Neighbor Report elements in a Neighbor
+	 * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
+	 * of RRM header.
+	 */
+	buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
+	if (!buf)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
+	wpabuf_put_u8(buf, dialog_token);
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		int send_lci;
+		size_t len;
+
+		if (ssid->ssid_len != nr->ssid.ssid_len ||
+		    os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
+			continue;
+
+		send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
+		len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
+
+		if (len - 2 > 0xff) {
+			wpa_printf(MSG_DEBUG,
+				   "NR entry for " MACSTR " exceeds 0xFF bytes",
+				   MAC2STR(nr->bssid));
+			continue;
+		}
+
+		if (len > wpabuf_tailroom(buf))
+			break;
+
+		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+		wpabuf_put_u8(buf, len - 2);
+		wpabuf_put_buf(buf, nr->nr);
+
+		if (send_lci && nr->lci) {
+			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+			wpabuf_put_u8(buf, wpabuf_len(nr->lci));
+			/*
+			 * Override measurement token - the first byte of the
+			 * Measurement Report element.
+			 */
+			msmt_token = wpabuf_put(buf, 0);
+			wpabuf_put_buf(buf, nr->lci);
+			*msmt_token = lci;
+		}
+
+		if (civic && nr->civic) {
+			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+			wpabuf_put_u8(buf, wpabuf_len(nr->civic));
+			/*
+			 * Override measurement token - the first byte of the
+			 * Measurement Report element.
+			 */
+			msmt_token = wpabuf_put(buf, 0);
+			wpabuf_put_buf(buf, nr->civic);
+			*msmt_token = civic;
+		}
+	}
+
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+}
+
+
+static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
+					  const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+	const u8 *pos, *ie, *end;
+	struct wpa_ssid_value ssid = {
+		.ssid_len = 0
+	};
+	u8 token;
+	u8 lci = 0, civic = 0; /* Measurement tokens */
+	u16 lci_max_age = 0;
+
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+		return;
+
+	end = buf + len;
+
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+	len = end - pos;
+
+	ie = get_ie(pos, len, WLAN_EID_SSID);
+	if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
+		ssid.ssid_len = ie[1];
+		os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
+	} else {
+		ssid.ssid_len = hapd->conf->ssid.ssid_len;
+		os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+	}
+
+	while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
+		if (ie[1] < 3)
+			break;
+
+		wpa_printf(MSG_DEBUG,
+			   "Neighbor report request, measure type %u",
+			   ie[4]);
+
+		switch (ie[4]) { /* Measurement Type */
+		case MEASURE_TYPE_LCI:
+			lci = ie[2]; /* Measurement Token */
+			lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
+									 ie[1]);
+			break;
+		case MEASURE_TYPE_LOCATION_CIVIC:
+			civic = ie[2]; /* Measurement token */
+			break;
+		}
+
+		pos = ie + ie[1] + 2;
+		len = end - pos;
+	}
+
+	hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
+				     lci_max_age);
+}
+
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+				      const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+
+	/*
+	 * Check for enough bytes: header + (1B)Category + (1B)Action +
+	 * (1B)Dialog Token.
+	 */
+	if (len < IEEE80211_HDRLEN + 3)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
+		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+
+	switch (mgmt->u.action.u.rrm.action) {
+	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+		hostapd_handle_radio_msmt_report(hapd, buf, len);
+		break;
+	case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
+		hostapd_handle_nei_report_req(hapd, buf, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
+			   mgmt->u.action.u.rrm.action);
+		break;
+	}
+}
+
+
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	int ret;
+
+	if (!sta) {
+		wpa_printf(MSG_INFO,
+			   "Request LCI: Destination address is not in station list");
+		return -1;
+	}
+
+	if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Request LCI: Destination address is not connected");
+		return -1;
+	}
+
+	if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
+		wpa_printf(MSG_INFO,
+			   "Request LCI: Station does not support LCI in RRM");
+		return -1;
+	}
+
+	if (hapd->lci_req_active) {
+		wpa_printf(MSG_DEBUG,
+			   "Request LCI: LCI request is already in process, overriding");
+		hapd->lci_req_active = 0;
+		eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
+				     NULL);
+	}
+
+	/* Measurement request (5) + Measurement element with LCI (10) */
+	buf = wpabuf_alloc(5 + 10);
+	if (!buf)
+		return -1;
+
+	hapd->lci_req_token++;
+	/* For wraparounds - the token must be nonzero */
+	if (!hapd->lci_req_token)
+		hapd->lci_req_token++;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->lci_req_token);
+	wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	wpabuf_put_u8(buf, 3 + 1 + 4);
+
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	/*
+	 * Parallel and Enable bits are 0, Duration, Request, and Report are
+	 * reserved.
+	 */
+	wpabuf_put_u8(buf, 0);
+	wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
+
+	wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+
+	wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
+	wpabuf_put_u8(buf, 2);
+	wpabuf_put_le16(buf, 0xffff);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret)
+		return ret;
+
+	hapd->lci_req_active = 1;
+
+	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+			       hostapd_lci_rep_timeout_handler, hapd, NULL);
+
+	return 0;
+}
+
+
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   u16 random_interval, u8 min_ap,
+			   const u8 *responders, unsigned int n_responders)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta;
+	u8 *len;
+	unsigned int i;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
+		   " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
+		   random_interval, min_ap, n_responders);
+
+	if (min_ap == 0 || min_ap > n_responders) {
+		wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Request range: Destination address is not connected");
+		return -1;
+	}
+
+	if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
+		wpa_printf(MSG_ERROR,
+			   "Request range: Destination station does not support FTM range report in RRM");
+		return -1;
+	}
+
+	if (hapd->range_req_active) {
+		wpa_printf(MSG_DEBUG,
+			   "Request range: Range request is already in process; overriding");
+		hapd->range_req_active = 0;
+		eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+				       hostapd_range_rep_timeout_handler, hapd,
+				       NULL);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	buf = wpabuf_alloc(7 + 255);
+	if (!buf)
+		return -1;
+
+	hapd->range_req_token++;
+	if (!hapd->range_req_token) /* For wraparounds */
+		hapd->range_req_token++;
+
+	/* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
+	wpabuf_put_le16(buf, 0); /* Number of Repetitions */
+
+	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	len = wpabuf_put(buf, 1); /* Length will be set later */
+
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	/*
+	 * Parallel and Enable bits are 0; Duration, Request, and Report are
+	 * reserved.
+	 */
+	wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+	wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
+
+	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
+	wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
+	wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
+
+	/* FTM Range Subelements */
+
+	/*
+	 * Taking the neighbor report part of the range request from neighbor
+	 * database instead of requesting the separate bits of data from the
+	 * user.
+	 */
+	for (i = 0; i < n_responders; i++) {
+		struct hostapd_neighbor_entry *nr;
+
+		nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
+					  NULL);
+		if (!nr) {
+			wpa_printf(MSG_INFO, "Missing neighbor report for "
+				   MACSTR, MAC2STR(responders + ETH_ALEN * i));
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
+			wpa_printf(MSG_ERROR, "Too long range request");
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+		wpabuf_put_u8(buf, wpabuf_len(nr->nr));
+		wpabuf_put_buf(buf, nr->nr);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	*len = wpabuf_len(buf) - 7;
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret)
+		return ret;
+
+	hapd->range_req_active = 1;
+
+	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+			       hostapd_range_rep_timeout_handler, hapd, NULL);
+
+	return 0;
+}
+
+
+void hostapd_clean_rrm(struct hostapd_data *hapd)
+{
+	hostpad_free_neighbor_db(hapd);
+	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+	hapd->lci_req_active = 0;
+	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+	hapd->range_req_active = 0;
+}
diff --git a/src/ap/rrm.h b/src/ap/rrm.h
new file mode 100644
index 0000000..f07fd41
--- /dev/null
+++ b/src/ap/rrm.h
@@ -0,0 +1,28 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RRM_H
+#define RRM_H
+
+/*
+ * Max measure request length is 255, -6 of the body we have 249 for the
+ * neighbor report elements. Each neighbor report element is at least 2 + 13
+ * bytes, so we can't have more than 16 responders in the request.
+ */
+#define RRM_RANGE_REQ_MAX_RESPONDERS 16
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+				      const u8 *buf, size_t len);
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   u16 random_interval, u8 min_ap,
+			   const u8 *responders, unsigned int n_responders);
+void hostapd_clean_rrm(struct hostapd_data *hapd);
+
+#endif /* RRM_H */
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 3d9a928..c49402d 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -202,6 +202,8 @@
 
 	u8 *supp_op_classes; /* Supported Operating Classes element, if
 			      * received, starting from the Length field */
+
+	u8 rrm_enabled_capa[5];
 };
 
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 5fe0987..2142414 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -650,7 +650,7 @@
 	}
 
 #ifdef CONFIG_IEEE80211R
-	if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds &&
+	if (!hostapd_drv_none(hapd) &&
 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
 		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
 					  hapd->conf->bridge :