]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FILS: Add generation of FILS Discovery frame template
authorAloka Dixit <alokad@codeaurora.org>
Tue, 15 Dec 2020 02:04:27 +0000 (18:04 -0800)
committerJouni Malinen <j@w1.fi>
Sun, 14 Feb 2021 16:03:49 +0000 (18:03 +0200)
Add hostapd configuration parameters for FILS Discovery frame
transmission interval and prepare a template for FILS Discovery frame
for the driver interface. The actual driver interface changes are not
included in this commit.

Signed-off-by: Aloka Dixit <alokad@codeaurora.org>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/beacon.c
src/ap/wpa_auth.h
src/ap/wpa_auth_ie.c
src/drivers/driver.h

index eaf190b86f1d6ed9fe87052e7c36e68dc9023a8e..a07e71eda18256ce7dd89aefb3a6e31ba5eeab39 100644 (file)
@@ -4423,6 +4423,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                bss->dhcp_server_port = atoi(pos);
        } else if (os_strcmp(buf, "dhcp_relay_port") == 0) {
                bss->dhcp_relay_port = atoi(pos);
+       } else if (os_strcmp(buf, "fils_discovery_min_interval") == 0) {
+               bss->fils_discovery_min_int = atoi(pos);
+       } else if (os_strcmp(buf, "fils_discovery_max_interval") == 0) {
+               bss->fils_discovery_max_int = atoi(pos);
 #endif /* CONFIG_FILS */
        } else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
                bss->multicast_to_unicast = atoi(pos);
index 769fab66abae57586c24480b794de5bca52fa690..d8a4e9474b6dad7be8bd0b815e5dd4b6a053f3bd 100644 (file)
@@ -1987,6 +1987,13 @@ own_ip_addr=127.0.0.1
 # default: 30 TUs (= 30.72 milliseconds)
 #fils_hlp_wait_time=30
 
+# FILS Discovery frame transmission minimum and maximum interval settings.
+# If fils_discovery_max_interval is non-zero, the AP enables FILS Discovery
+# frame transmission. These values use TUs as the unit and have allowed range
+# of 0-10000. fils_discovery_min_interval defaults to 20.
+#fils_discovery_min_interval=20
+#fils_discovery_max_interval=0
+
 # Transition Disable indication
 # The AP can notify authenticated stations to disable transition mode in their
 # network profiles when the network has completed transition steps, i.e., once
index 8b98e67554514cb9c7ec7a6570d45e316d7327e5..419bf7cad4d350fbc650a0969d94dc982d04c1a1 100644 (file)
@@ -131,6 +131,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
        bss->fils_hlp_wait_time = 30;
        bss->dhcp_server_port = DHCP_SERVER_PORT;
        bss->dhcp_relay_port = DHCP_SERVER_PORT;
+       bss->fils_discovery_min_int = 20;
 #endif /* CONFIG_FILS */
 
        bss->broadcast_deauth = 1;
index cb2bf9b24ede00cba48fbeacd3c15cff97a5a933..23b5b2a5fdeb775d873456f516732bc96f99029f 100644 (file)
@@ -730,6 +730,8 @@ struct hostapd_bss_config {
        unsigned int fils_hlp_wait_time;
        u16 dhcp_server_port;
        u16 dhcp_relay_port;
+       u32 fils_discovery_min_int;
+       u32 fils_discovery_max_int;
 #endif /* CONFIG_FILS */
 
        int multicast_to_unicast;
index fbbe8f4db79fe0045ea442b1c341d675e44bce34..6187c09638416f6caaf734b91068073396883c51 100644 (file)
@@ -1134,6 +1134,243 @@ void sta_track_del(struct hostapd_sta_info *info)
 }
 
 
+#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,
+                                                 &params->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)
 {
@@ -1478,6 +1715,10 @@ void ieee802_11_free_ap_params(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 */
 }
 
 
@@ -1534,6 +1775,10 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
        params.sae_pwe = hapd->conf->sae_pwe;
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_FILS
+       params.fd_frame_tmpl = hostapd_fils_discovery(hapd, &params);
+#endif /* CONFIG_FILS */
+
        if (cmode &&
            hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
                                    iconf->channel, iconf->enable_edmg,
index cf0801dc3e660000234c77bb1747ff69390d5d28..eaa2cafc8088c6c2e4ba80bde2045e4f8583eaa4 100644 (file)
@@ -537,6 +537,8 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
 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);
+bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
+                               u8 *fd_rsn_info);
 void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
 void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
index ab427868aee11833babc06db82ec6a63bbcf47a6..d4710316a96d80817a09262b4a03b2965ec90dd0 100644 (file)
@@ -1125,6 +1125,7 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
 
 
 #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)
@@ -1141,4 +1142,91 @@ u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
                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 */
index 1a42381d73e65d8fae42db5907055b84d34fb891..2a61b550e6bd98fcc3f055413065929b66292f1d 100644 (file)
@@ -1541,6 +1541,26 @@ struct wpa_driver_ap_params {
         * 2 = both hunting-and-pecking loop and hash-to-element enabled
         */
        int sae_pwe;
+
+       /**
+        * FILS Discovery frame minimum interval in TUs
+        */
+       u32 fd_min_int;
+
+       /**
+        * FILS Discovery frame maximum interval in TUs (0 = FD frame disabled)
+        */
+       u32 fd_max_int;
+
+       /**
+        * FILS Discovery frame template data
+        */
+       u8 *fd_frame_tmpl;
+
+       /**
+        * FILS Discovery frame template length
+        */
+       size_t fd_frame_tmpl_len;
 };
 
 struct wpa_driver_mesh_bss_params {