@@ -4269,6 +4269,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->oci_freq_override_wnm_sleep = atoi(pos);
} else if (os_strcmp(buf, "eap_skip_prot_success") == 0) {
bss->eap_skip_prot_success = atoi(pos);
+ } else if (os_strcmp(buf, "delay_eapol_tx") == 0) {
+ conf->delay_eapol_tx = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_SAE
} else if (os_strcmp(buf, "sae_password") == 0) {
@@ -3080,6 +3080,11 @@ own_ip_addr=127.0.0.1
# Include only ECSA IE without CSA IE where possible
# (channel switch operating class is needed)
#ecsa_ie_only=0
+#
+# Delay EAPOL messages 1/4 and 3/4 Tx by not sending the frame until the last
+# attempt (wpa_pairwise_update_count). This will trigger a timeout on all
+# previous attempts and thus delay the frame. (testing only)
+#delay_eapol_tx=0
##### Multiple BSSID support ##################################################
#
@@ -258,6 +258,7 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->ignore_reassoc_probability = 0.0;
conf->corrupt_gtk_rekey_mic_probability = 0.0;
conf->ecsa_ie_only = 0;
+ conf->delay_eapol_tx = 0;
#endif /* CONFIG_TESTING_OPTIONS */
conf->acs = 0;
@@ -1075,6 +1075,7 @@ struct hostapd_config {
double ignore_reassoc_probability;
double corrupt_gtk_rekey_mic_probability;
int ecsa_ie_only;
+ bool delay_eapol_tx;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_ACS
@@ -1736,10 +1736,27 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
if (!sm)
return;
+ ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /* in case delay_eapol_tx is true, delay the EAPOL by actually
+ * sending it on the last attempt and therefore all the previous
+ * attempts will fail on timeout.
+ */
+ if (!wpa_auth->conf.delay_eapol_tx ||
+ (wpa_auth->conf.delay_eapol_tx &&
+ ctr == wpa_auth->conf.wpa_pairwise_update_count))
+ __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde,
+ kde_len, keyidx, encr, 0);
+ else
+ wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+ "DELAY-EAPOL-TX-%d", ctr);
+
+#else
__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
keyidx, encr, 0);
+#endif /* CONFIG_TESTING_OPTIONS */
- ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
if (ctr == 1 && wpa_auth->conf.tx_status)
timeout_ms = pairwise ? eapol_key_timeout_first :
eapol_key_timeout_first_group;
@@ -240,6 +240,7 @@ struct wpa_auth_config {
unsigned int gtk_rsc_override_set:1;
unsigned int igtk_rsc_override_set:1;
int ft_rsnxe_used;
+ bool delay_eapol_tx;
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
@@ -117,6 +117,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
#ifdef CONFIG_TESTING_OPTIONS
wconf->corrupt_gtk_rekey_mic_probability =
iconf->corrupt_gtk_rekey_mic_probability;
+ wconf->delay_eapol_tx = iconf->delay_eapol_tx;
if (conf->own_ie_override &&
wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
@@ -1470,3 +1470,46 @@ def test_ap_pmf_sta_global_require2(dev, apdev):
raise Exception("Unexpected connection")
finally:
dev[0].set("pmf", "0")
+
+def test_ap_pmf_drop_robust_mgmt_prior_to_keys_installation(dev, apdev):
+ """Drop non protected robust management frame prior to keys installation"""
+ ssid = "test-pmf-required"
+ passphrase = '12345678'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['delay_eapol_tx'] = '1'
+ params['ieee80211w'] = '2'
+ params['wpa_pairwise_update_count'] = '5'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ # spectrum mgmt with channel switch IE
+ msg = {'fc': 0x00d0,
+ 'sa': hapd.own_addr(),
+ 'da': dev[0].own_addr(),
+ 'bssid': hapd.own_addr(),
+ 'payload': binascii.unhexlify('00042503000608')
+ }
+
+ dev[0].connect(ssid, psk=passphrase, scan_freq='2412', ieee80211w='1',
+ wait_connect=False)
+
+ # wait for the first delay before sending the frame
+ ev = hapd.wait_event(['DELAY-EAPOL-TX-1'], timeout=10)
+ if ev is None:
+ raise Exception("EAPOL is not delayed")
+
+ # send the action frame while connecting (prior to keys installation)
+ hapd.mgmt_tx(msg)
+
+ dev[0].wait_connected(timeout=10, error="Timeout on connection")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ # verify no channel switch event
+ ev = dev[0].wait_event(['CTRL-EVENT-STARTED-CHANNEL-SWITCH'], timeout=5)
+ if ev is not None:
+ raise Exception("Unexpected CSA prior to keys installation")
+
+ # send the frame after keys installation and verify channel switch event
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_event(['CTRL-EVENT-STARTED-CHANNEL-SWITCH'], timeout=5)
+ if ev is None:
+ raise Exception("Excpeted CSA handling after keys installation")