+#ifdef CONFIG_SAE
+
+static int index_within_array(const int *array, int idx)
+{
+ int i;
+ for (i = 0; i < idx; i++) {
+ if (array[i] <= 0)
+ return 0;
+ }
+ return 1;
+}
+
+
+static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
+{
+ int *groups = wpa_s->conf->sae_groups;
+ int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+
+ if (!groups || groups[0] <= 0)
+ groups = default_groups;
+
+ /* Configuration may have changed, so validate current index */
+ if (!index_within_array(groups, wpa_s->sme.sae_group_index))
+ return -1;
+
+ for (;;) {
+ int group = groups[wpa_s->sme.sae_group_index];
+ if (group <= 0)
+ break;
+ if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
+ wpa_s->sme.sae.group);
+ return 0;
+ }
+ wpa_s->sme.sae_group_index++;
+ }
+
+ return -1;
+}
+
+
+static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const u8 *bssid)
+{
+ struct wpabuf *buf;
+ size_t len;
+
+ if (ssid->passphrase == NULL) {
+ wpa_printf(MSG_DEBUG, "SAE: No password available");
+ return NULL;
+ }
+
+ if (sme_set_sae_group(wpa_s) < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
+ return NULL;
+ }
+
+ if (sae_prepare_commit(wpa_s->own_addr, bssid,
+ (u8 *) ssid->passphrase,
+ os_strlen(ssid->passphrase),
+ &wpa_s->sme.sae) < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+ return NULL;
+ }
+
+ len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
+ buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_le16(buf, 1); /* Transaction seq# */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
+
+ return buf;
+}
+
+
+static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_le16(buf, 2); /* Transaction seq# */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ sae_write_confirm(&wpa_s->sme.sae, buf);
+
+ return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+/**
+ * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Pointer to the bss which is the target of authentication attempt
+ */
+static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ const u8 rrm_ie_len = 5;
+ u8 *pos;
+ const u8 *rrm_ie;
+
+ wpa_s->rrm.rrm_used = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "RRM: Determining whether RRM can be used - device support: 0x%x",
+ wpa_s->drv_rrm_flags);
+
+ rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES);
+ if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) {
+ wpa_printf(MSG_DEBUG, "RRM: No RRM in network");
+ return;
+ }
+
+ if (!((wpa_s->drv_rrm_flags &
+ WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
+ (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
+ !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Insufficient RRM support in driver - do not use RRM");
+ return;
+ }
+
+ if (sizeof(wpa_s->sme.assoc_req_ie) <
+ wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) {
+ wpa_printf(MSG_INFO,
+ "RRM: Unable to use RRM, no room for RRM IE");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request");
+ pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+ os_memset(pos, 0, 2 + rrm_ie_len);
+ *pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+ *pos++ = rrm_ie_len;
+
+ /* Set supported capabilites flags */
+ if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
+ *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+ if (wpa_s->lci)
+ pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT;
+
+ wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2;
+ wpa_s->rrm.rrm_used = 1;
+}
+
+
+static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid,
+ int start)