}
+#ifdef CONFIG_FILS
+
+static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd)
+{
+ u16 cap_info, phy_index = 0;
+ u8 chwidth = FD_CAP_BSS_CHWIDTH_20, mcs_nss_size = 4;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+
+ cap_info = FD_CAP_ESS;
+ if (hapd->conf->wpa)
+ cap_info |= FD_CAP_PRIVACY;
+
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ phy_index = FD_CAP_PHY_INDEX_HE;
+
+ switch (hapd->iconf->op_class) {
+ case 135:
+ mcs_nss_size += 4;
+ /* fallthrough */
+ case 134:
+ mcs_nss_size += 4;
+ chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
+ break;
+ case 133:
+ chwidth = FD_CAP_BSS_CHWIDTH_80;
+ break;
+ case 132:
+ chwidth = FD_CAP_BSS_CHWIDTH_40;
+ break;
+ }
+ } else {
+ switch (hostapd_get_oper_chwidth(hapd->iconf)) {
+ case CHANWIDTH_80P80MHZ:
+ mcs_nss_size += 4;
+ /* fallthrough */
+ case CHANWIDTH_160MHZ:
+ mcs_nss_size += 4;
+ chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
+ break;
+ case CHANWIDTH_80MHZ:
+ chwidth = FD_CAP_BSS_CHWIDTH_80;
+ break;
+ case CHANWIDTH_USE_HT:
+ if (hapd->iconf->secondary_channel)
+ chwidth = FD_CAP_BSS_CHWIDTH_40;
+ else
+ chwidth = FD_CAP_BSS_CHWIDTH_20;
+ break;
+ }
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
+ phy_index = FD_CAP_PHY_INDEX_HE;
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AC
+ if (!phy_index &&
+ hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
+ phy_index = FD_CAP_PHY_INDEX_VHT;
+#endif /* CONFIG_IEEE80211AC */
+ if (!phy_index &&
+ hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
+ phy_index = FD_CAP_PHY_INDEX_HT;
+ }
+
+ cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
+ cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
+
+ if (mode) {
+ u16 *mcs = (u16 *) mode->he_capab[IEEE80211_MODE_AP].mcs;
+ int i;
+ u16 nss = 0;
+
+ for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
+ u16 nss_mask = 0x3 << (i * 2);
+
+ if (mcs_nss_size == 4 &&
+ (((mcs[0] & nss_mask) == nss_mask) ||
+ ((mcs[1] & nss_mask) == nss_mask)))
+ continue;
+
+ if (mcs_nss_size == 8 &&
+ (((mcs[2] & nss_mask) == nss_mask) ||
+ ((mcs[3] & nss_mask) == nss_mask)))
+ continue;
+
+ if (mcs_nss_size == 12 &&
+ (((mcs[4] & nss_mask) == nss_mask) ||
+ ((mcs[5] & nss_mask) == nss_mask)))
+ continue;
+
+ nss++;
+ }
+
+ if (nss > 4)
+ cap_info |= FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT;
+ else if (nss)
+ cap_info |= (nss - 1) << FD_CAP_NSS_SHIFT;
+ }
+
+ return cap_info;
+}
+
+
+static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
+{
+ struct ieee80211_mgmt *head;
+ const u8 *mobility_domain;
+ u8 *pos, *length_pos, buf[200];
+ u16 ctl = 0;
+ u8 fd_rsn_info[5];
+ size_t total_len, buf_len;
+
+ total_len = 24 + 2 + 12;
+
+ /* FILS Discovery Frame Control */
+ ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) |
+ FD_FRAME_CTL_SHORT_SSID_PRESENT |
+ FD_FRAME_CTL_LENGTH_PRESENT |
+ FD_FRAME_CTL_CAP_PRESENT;
+ total_len += 4 + 1 + 2;
+
+ /* Check for optional subfields and calculate length */
+ if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) {
+ ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT;
+ total_len += sizeof(fd_rsn_info);
+ }
+
+ mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
+ if (mobility_domain) {
+ ctl |= FD_FRAME_CTL_MD_PRESENT;
+ total_len += 3;
+ }
+
+ pos = hostapd_eid_fils_indic(hapd, buf, 0);
+ buf_len = pos - buf;
+ total_len += buf_len;
+
+ head = os_zalloc(total_len);
+ if (!head)
+ return NULL;
+
+ head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memset(head->da, 0xff, ETH_ALEN);
+ os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+
+ head->u.action.category = WLAN_ACTION_PUBLIC;
+ head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY;
+
+ pos = &head->u.action.u.public_action.variable[0];
+
+ /* FILS Discovery Information field */
+
+ /* FILS Discovery Frame Control */
+ WPA_PUT_LE16(pos, ctl);
+ pos += 2;
+
+ /* Hardware or low-level driver will fill in the Timestamp value */
+ pos += 8;
+
+ /* Beacon Interval */
+ WPA_PUT_LE16(pos, hapd->iconf->beacon_int);
+ pos += 2;
+
+ /* Short SSID */
+ WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid);
+ pos += sizeof(hapd->conf->ssid.short_ssid);
+
+ /* Store position of FILS discovery information element Length field */
+ length_pos = pos++;
+
+ /* FD Capability */
+ WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd));
+ pos += 2;
+
+ /* Operating Class - not present */
+
+ /* Primary Channel - not present */
+
+ /* AP Configuration Sequence Number - not present */
+
+ /* Access Network Options - not present */
+
+ /* FD RSN Information */
+ if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) {
+ os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info));
+ pos += sizeof(fd_rsn_info);
+ }
+
+ /* Channel Center Frequency Segment 1 - not present */
+
+ /* Mobility Domain */
+ if (ctl & FD_FRAME_CTL_MD_PRESENT) {
+ os_memcpy(pos, &mobility_domain[2], 3);
+ pos += 3;
+ }
+
+ /* Fill in the Length field value */
+ *length_pos = pos - (length_pos + 1);
+
+ /* FILS Indication element */
+ if (buf_len) {
+ os_memcpy(pos, buf, buf_len);
+ pos += buf_len;
+ }
+
+ *len = pos - (u8 *) head;
+ wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template",
+ head, pos - (u8 *) head);
+ return (u8 *) head;
+}
+
+
+/* Configure FILS Discovery frame transmission parameters */
+static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ params->fd_max_int = hapd->conf->fils_discovery_max_int;
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
+ params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
+
+ params->fd_min_int = hapd->conf->fils_discovery_min_int;
+ if (params->fd_min_int > params->fd_max_int)
+ params->fd_min_int = params->fd_max_int;
+
+ if (params->fd_max_int)
+ return hostapd_gen_fils_discovery(hapd,
+ ¶ms->fd_frame_tmpl_len);
+
+ return NULL;
+}
+
+#endif /* CONFIG_FILS */
+
+
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
params->head = NULL;
os_free(params->proberesp);
params->proberesp = NULL;
+#ifdef CONFIG_FILS
+ os_free(params->fd_frame_tmpl);
+ params->fd_frame_tmpl = NULL;
+#endif /* CONFIG_FILS */
}
params.sae_pwe = hapd->conf->sae_pwe;
#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ params.fd_frame_tmpl = hostapd_fils_discovery(hapd, ¶ms);
+#endif /* CONFIG_FILS */
+
if (cmode &&
hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
iconf->channel, iconf->enable_edmg,
#ifdef CONFIG_FILS
+
u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
u8 *pos, size_t max_len,
const u8 *req_ies, size_t req_ies_len)
return pos;
return pos + res;
}
+
+
+bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
+ u8 *fd_rsn_info)
+{
+ struct wpa_auth_config *conf;
+ u32 selectors = 0;
+ u8 *pos = fd_rsn_info;
+ int i, res;
+ u32 cipher, suite, selector, mask;
+ u8 tmp[10 * RSN_SELECTOR_LEN];
+
+ if (!wpa_auth)
+ return false;
+ conf = &wpa_auth->conf;
+
+ if (!(conf->wpa & WPA_PROTO_RSN))
+ return false;
+
+ /* RSN Capability (B0..B15) */
+ WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+ pos += 2;
+
+ /* Group Data Cipher Suite Selector (B16..B21) */
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+ if (suite == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
+ cipher = 63; /* No cipher suite selected */
+ if ((suite >> 8) == 0x000fac && ((suite & 0xff) <= 13))
+ cipher = suite & 0xff;
+ else
+ cipher = 62; /* vendor specific */
+ selectors |= cipher;
+
+ /* Group Management Cipher Suite Selector (B22..B27) */
+ cipher = 63; /* Default to no cipher suite selected */
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ switch (conf->group_mgmt_cipher) {
+ case WPA_CIPHER_AES_128_CMAC:
+ cipher = RSN_CIPHER_SUITE_AES_128_CMAC & 0xff;
+ break;
+ case WPA_CIPHER_BIP_GMAC_128:
+ cipher = RSN_CIPHER_SUITE_BIP_GMAC_128 & 0xff;
+ break;
+ case WPA_CIPHER_BIP_GMAC_256:
+ cipher = RSN_CIPHER_SUITE_BIP_GMAC_256 & 0xff;
+ break;
+ case WPA_CIPHER_BIP_CMAC_256:
+ cipher = RSN_CIPHER_SUITE_BIP_CMAC_256 & 0xff;
+ break;
+ }
+ }
+ selectors |= cipher << 6;
+
+ /* Pairwise Cipher Suite Selector (B28..B33) */
+ cipher = 63; /* Default to no cipher suite selected */
+ res = rsn_cipher_put_suites(tmp, conf->rsn_pairwise);
+ if (res == 1 && tmp[0] == 0x00 && tmp[1] == 0x0f && tmp[2] == 0xac &&
+ tmp[3] <= 13)
+ cipher = tmp[3];
+ selectors |= cipher << 12;
+
+ /* AKM Suite Selector (B34..B39) */
+ selector = 0; /* default to AKM from RSNE in Beacon/Probe Response */
+ mask = WPA_KEY_MGMT_FILS_SHA256 | WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA384;
+ if ((conf->wpa_key_mgmt & mask) && (conf->wpa_key_mgmt & ~mask) == 0) {
+ suite = conf->wpa_key_mgmt & mask;
+ if (suite == WPA_KEY_MGMT_FILS_SHA256)
+ selector = 1; /* 00-0f-ac:14 */
+ else if (suite == WPA_KEY_MGMT_FILS_SHA384)
+ selector = 2; /* 00-0f-ac:15 */
+ else if (suite == (WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FILS_SHA384))
+ selector = 3; /* 00-0f-ac:14 or 00-0f-ac:15 */
+ else if (suite == WPA_KEY_MGMT_FT_FILS_SHA384)
+ selector = 4; /* 00-0f-ac:17 */
+ }
+ selectors |= selector << 18;
+
+ for (i = 0; i < 3; i++) {
+ *pos++ = selectors & 0xff;
+ selectors >>= 8;
+ }
+
+ return true;
+}
+
#endif /* CONFIG_FILS */