]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - tests/hwsim/test_ap_ft.py
Write multi_ap_backhaul_sta to wpa_supplicant config
[thirdparty/hostap.git] / tests / hwsim / test_ap_ft.py
index ea63d1f026b954cdfe00bd678372a1ccfeaa5890..6ab4f8ca23ef01693ffc825446dcf56659027bdf 100644 (file)
@@ -1,5 +1,5 @@
 # Fast BSS Transition tests
-# Copyright (c) 2013-2017, Jouni Malinen <j@w1.fi>
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
 #
 # This software may be distributed under the terms of the BSD license.
 # See README for more details.
@@ -10,14 +10,18 @@ import os
 import time
 import logging
 logger = logging.getLogger()
+import signal
 import struct
+import subprocess
 
 import hwsim_utils
+from hwsim import HWSimRadio
 import hostapd
 from tshark import run_tshark
 from utils import HwsimSkip, alloc_fail, fail_test, wait_fail_trigger, skip_with_fips, parse_ie
 from wlantest import Wlantest
 from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+from test_rrm import check_beacon_req
 
 def ft_base_rsn():
     params = { "wpa": "2",
@@ -127,7 +131,8 @@ def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False,
               sae=False, eap=False, fail_test=False, roams=1,
               pairwise_cipher="CCMP", group_cipher="TKIP CCMP", ptk_rekey="0",
               test_connectivity=True, eap_identity="gpsk user", conndev=False,
-              force_initial_conn_to_first_ap=False):
+              force_initial_conn_to_first_ap=False, sha384=False,
+              group_mgmt=None, ocv=None):
     logger.info("Connect to first AP")
 
     copts = {}
@@ -137,8 +142,12 @@ def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False,
     copts["pairwise"] = pairwise_cipher
     copts["group"]  = group_cipher
     copts["wpa_ptk_rekey"] = ptk_rekey
+    if group_mgmt:
+        copts["group_mgmt"] = group_mgmt
+    if ocv:
+        copts["ocv"] = ocv
     if eap:
-        copts["key_mgmt"] = "FT-EAP"
+        copts["key_mgmt"] = "FT-EAP-SHA384" if sha384 else "FT-EAP"
         copts["eap"] = "GPSK"
         copts["identity"] = eap_identity
         copts["password"] = "abcdefghijklmnop0123456789abcdef"
@@ -392,6 +401,62 @@ def test_ap_ft_pmf(dev, apdev):
 
     run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
 
+def test_ap_ft_pmf_bip_cmac_128(dev, apdev):
+    """WPA2-PSK-FT AP with PMF/BIP-CMAC-128"""
+    run_ap_ft_pmf_bip(dev, apdev, "AES-128-CMAC")
+
+def test_ap_ft_pmf_bip_gmac_128(dev, apdev):
+    """WPA2-PSK-FT AP with PMF/BIP-GMAC-128"""
+    run_ap_ft_pmf_bip(dev, apdev, "BIP-GMAC-128")
+
+def test_ap_ft_pmf_bip_gmac_256(dev, apdev):
+    """WPA2-PSK-FT AP with PMF/BIP-GMAC-256"""
+    run_ap_ft_pmf_bip(dev, apdev, "BIP-GMAC-256")
+
+def test_ap_ft_pmf_bip_cmac_256(dev, apdev):
+    """WPA2-PSK-FT AP with PMF/BIP-CMAC-256"""
+    run_ap_ft_pmf_bip(dev, apdev, "BIP-CMAC-256")
+
+def run_ap_ft_pmf_bip(dev, apdev, cipher):
+    if cipher not in dev[0].get_capability("group_mgmt"):
+        raise HwsimSkip("Cipher %s not supported" % cipher)
+
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["group_mgmt_cipher"] = cipher
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["group_mgmt_cipher"] = cipher
+    hapd1 = hostapd.add_ap(apdev[1], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+              group_mgmt=cipher)
+
+def test_ap_ft_ocv(dev, apdev):
+    """WPA2-PSK-FT AP with OCV"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["ocv"] = "1"
+    try:
+        hapd0 = hostapd.add_ap(apdev[0], params)
+    except Exception, e:
+        if "Failed to set hostapd parameter ocv" in str(e):
+            raise HwsimSkip("OCV not supported")
+        raise
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["ocv"] = "1"
+    hapd1 = hostapd.add_ap(apdev[1], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, ocv="1")
+
 def test_ap_ft_over_ds(dev, apdev):
     """WPA2-PSK-FT AP over DS"""
     ssid = "test-ft"
@@ -406,6 +471,129 @@ def test_ap_ft_over_ds(dev, apdev):
     check_mib(dev[0], [ ("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-4"),
                         ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-4") ])
 
+def cleanup_ap_ft_separate_hostapd():
+    subprocess.call(["brctl", "delif", "br0ft", "veth0"],
+                    stderr=open('/dev/null', 'w'))
+    subprocess.call(["brctl", "delif", "br1ft", "veth1"],
+                    stderr=open('/dev/null', 'w'))
+    subprocess.call(["ip", "link", "del", "veth0"],
+                    stderr=open('/dev/null', 'w'))
+    subprocess.call(["ip", "link", "del", "veth1"],
+                    stderr=open('/dev/null', 'w'))
+    for ifname in [ 'br0ft', 'br1ft', 'br-ft' ]:
+        subprocess.call(['ip', 'link', 'set', 'dev', ifname, 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', ifname],
+                        stderr=open('/dev/null', 'w'))
+
+def test_ap_ft_separate_hostapd(dev, apdev, params):
+    """WPA2-PSK-FT AP and separate hostapd process"""
+    try:
+        run_ap_ft_separate_hostapd(dev, apdev, params, False)
+    finally:
+        cleanup_ap_ft_separate_hostapd()
+
+def test_ap_ft_over_ds_separate_hostapd(dev, apdev, params):
+    """WPA2-PSK-FT AP over DS and separate hostapd process"""
+    try:
+        run_ap_ft_separate_hostapd(dev, apdev, params, True)
+    finally:
+        cleanup_ap_ft_separate_hostapd()
+
+def run_ap_ft_separate_hostapd(dev, apdev, params, over_ds):
+    ssid = "test-ft"
+    passphrase="12345678"
+    logdir = params['logdir']
+    pidfile = os.path.join(logdir, 'ap_ft_over_ds_separate_hostapd.pid')
+    logfile = os.path.join(logdir, 'ap_ft_over_ds_separate_hostapd.hapd')
+    global_ctrl = '/var/run/hostapd-ft'
+    br_ifname = 'br-ft'
+
+    try:
+        subprocess.check_call(['brctl', 'addbr', br_ifname])
+        subprocess.check_call(['brctl', 'setfd', br_ifname, '0'])
+        subprocess.check_call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+
+        subprocess.check_call([ "ip", "link", "add", "veth0", "type", "veth",
+                                "peer", "name", "veth0br" ])
+        subprocess.check_call([ "ip", "link", "add", "veth1", "type", "veth",
+                                "peer", "name", "veth1br" ])
+        subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth0br', 'up'])
+        subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth1br', 'up'])
+        subprocess.check_call(['brctl', 'addif', br_ifname, 'veth0br'])
+        subprocess.check_call(['brctl', 'addif', br_ifname, 'veth1br'])
+
+        subprocess.check_call(['brctl', 'addbr', 'br0ft'])
+        subprocess.check_call(['brctl', 'setfd', 'br0ft', '0'])
+        subprocess.check_call(['ip', 'link', 'set', 'dev', 'br0ft', 'up'])
+        subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth0', 'up'])
+        subprocess.check_call(['brctl', 'addif', 'br0ft', 'veth0'])
+        subprocess.check_call(['brctl', 'addbr', 'br1ft'])
+        subprocess.check_call(['brctl', 'setfd', 'br1ft', '0'])
+        subprocess.check_call(['ip', 'link', 'set', 'dev', 'br1ft', 'up'])
+        subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth1', 'up'])
+        subprocess.check_call(['brctl', 'addif', 'br1ft', 'veth1'])
+    except subprocess.CalledProcessError:
+        raise HwsimSkip("Bridge or veth not supported (kernel CONFIG_VETH)")
+
+    with HWSimRadio() as (radio, iface):
+        prg = os.path.join(logdir, 'alt-hostapd/hostapd/hostapd')
+        if not os.path.exists(prg):
+            prg = '../../hostapd/hostapd'
+        cmd = [ prg, '-B', '-ddKt',
+                '-P', pidfile, '-f', logfile, '-g', global_ctrl ]
+        subprocess.check_call(cmd)
+
+        hglobal = hostapd.HostapdGlobal(global_ctrl_override=global_ctrl)
+        apdev_ft = { 'ifname': iface }
+        apdev2 = [ apdev_ft, apdev[1] ]
+
+        params = ft_params1(ssid=ssid, passphrase=passphrase)
+        params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+        params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+        params['bridge'] = 'br0ft'
+        hapd0 = hostapd.add_ap(apdev2[0], params,
+                               global_ctrl_override=global_ctrl)
+        apdev2[0]['bssid'] = hapd0.own_addr()
+        params = ft_params2(ssid=ssid, passphrase=passphrase)
+        params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+        params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+        params['bridge'] = 'br1ft'
+        hapd1 = hostapd.add_ap(apdev2[1], params)
+
+        run_roams(dev[0], apdev2, hapd0, hapd1, ssid, passphrase,
+                  over_ds=over_ds, test_connectivity=False)
+
+        hglobal.terminate()
+
+    if os.path.exists(pidfile):
+        with open(pidfile, 'r') as f:
+            pid = int(f.read())
+            f.close()
+        os.kill(pid, signal.SIGTERM)
+
+def test_ap_ft_over_ds_ocv(dev, apdev):
+    """WPA2-PSK-FT AP over DS"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["ocv"] = "1"
+    try:
+        hapd0 = hostapd.add_ap(apdev[0], params)
+    except Exception, e:
+        if "Failed to set hostapd parameter ocv" in str(e):
+            raise HwsimSkip("OCV not supported")
+        raise
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["ocv"] = "1"
+    hapd1 = hostapd.add_ap(apdev[1], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              ocv="1")
+
 def test_ap_ft_over_ds_disabled(dev, apdev):
     """WPA2-PSK-FT AP over DS disabled"""
     ssid = "test-ft"
@@ -596,7 +784,40 @@ def test_ap_ft_pmf_over_ds(dev, apdev):
     params["ieee80211w"] = "2"
     hapd1 = hostapd.add_ap(apdev[1], params)
 
-    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+def test_ap_ft_pmf_bip_cmac_128_over_ds(dev, apdev):
+    """WPA2-PSK-FT AP over DS with PMF/BIP-CMAC-128"""
+    run_ap_ft_pmf_bip_over_ds(dev, apdev, "AES-128-CMAC")
+
+def test_ap_ft_pmf_bip_gmac_128_over_ds(dev, apdev):
+    """WPA2-PSK-FT AP over DS with PMF/BIP-GMAC-128"""
+    run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-GMAC-128")
+
+def test_ap_ft_pmf_bip_gmac_256_over_ds(dev, apdev):
+    """WPA2-PSK-FT AP over DS with PMF/BIP-GMAC-256"""
+    run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-GMAC-256")
+
+def test_ap_ft_pmf_bip_cmac_256_over_ds(dev, apdev):
+    """WPA2-PSK-FT AP over DS with PMF/BIP-CMAC-256"""
+    run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-CMAC-256")
+
+def run_ap_ft_pmf_bip_over_ds(dev, apdev, cipher):
+    if cipher not in dev[0].get_capability("group_mgmt"):
+        raise HwsimSkip("Cipher %s not supported" % cipher)
+
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["group_mgmt_cipher"] = cipher
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    params["group_mgmt_cipher"] = cipher
+    hapd1 = hostapd.add_ap(apdev[1], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              group_mgmt=cipher)
 
 def test_ap_ft_over_ds_pull(dev, apdev):
     """WPA2-PSK-FT AP over DS (pull PMK)"""
@@ -1272,8 +1493,7 @@ def test_ap_ft_gcmp_256(dev, apdev):
     run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
               pairwise_cipher="GCMP-256", group_cipher="GCMP-256")
 
-def test_ap_ft_oom(dev, apdev):
-    """WPA2-PSK-FT and OOM"""
+def setup_ap_ft_oom(dev, apdev):
     skip_with_fips(dev[0])
     ssid = "test-ft"
     passphrase="12345678"
@@ -1291,13 +1511,32 @@ def test_ap_ft_oom(dev, apdev):
         dst = apdev[0]['bssid']
 
     dev[0].scan_for_bss(dst, freq="2412")
+
+    return dst
+
+def test_ap_ft_oom(dev, apdev):
+    """WPA2-PSK-FT and OOM"""
+    dst = setup_ap_ft_oom(dev, apdev)
     with alloc_fail(dev[0], 1, "wpa_ft_gen_req_ies"):
         dev[0].roam(dst)
+
+def test_ap_ft_oom2(dev, apdev):
+    """WPA2-PSK-FT and OOM (2)"""
+    dst = setup_ap_ft_oom(dev, apdev)
     with fail_test(dev[0], 1, "wpa_ft_mic"):
-        dev[0].roam(dst, fail_test=True)
+        dev[0].roam(dst, fail_test=True, assoc_reject_ok=True)
+
+def test_ap_ft_oom3(dev, apdev):
+    """WPA2-PSK-FT and OOM (3)"""
+    dst = setup_ap_ft_oom(dev, apdev)
     with fail_test(dev[0], 1, "os_get_random;wpa_ft_prepare_auth_request"):
-        dev[0].roam(dst, fail_test=True)
+        dev[0].roam(dst)
 
+def test_ap_ft_oom4(dev, apdev):
+    """WPA2-PSK-FT and OOM (4)"""
+    ssid = "test-ft"
+    passphrase="12345678"
+    dst = setup_ap_ft_oom(dev, apdev)
     dev[0].request("REMOVE_NETWORK all")
     with alloc_fail(dev[0], 1, "=sme_update_ft_ies"):
         dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
@@ -1491,8 +1730,8 @@ def test_ap_ft_ap_oom6(dev, apdev):
         dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
                        scan_freq="2412")
 
-def test_ap_ft_ap_oom7(dev, apdev):
-    """WPA2-PSK-FT and AP OOM 7"""
+def test_ap_ft_ap_oom7a(dev, apdev):
+    """WPA2-PSK-FT and AP OOM 7a"""
     ssid = "test-ft"
     passphrase="12345678"
 
@@ -1513,12 +1752,72 @@ def test_ap_ft_ap_oom7(dev, apdev):
     with alloc_fail(hapd1, 1, "wpa_ft_igtk_subelem"):
         # This will fail to roam
         dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom7b(dev, apdev):
+    """WPA2-PSK-FT and AP OOM 7b"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    bssid0 = hapd0.own_addr()
+
+    dev[0].scan_for_bss(bssid0, freq="2412")
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   ieee80211w="2", scan_freq="2412")
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    hapd1 = hostapd.add_ap(apdev[1], params)
+    bssid1 = hapd1.own_addr()
+    dev[0].scan_for_bss(bssid1, freq="2412")
     with fail_test(hapd1, 1, "aes_wrap;wpa_ft_igtk_subelem"):
         # This will fail to roam
         dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom7c(dev, apdev):
+    """WPA2-PSK-FT and AP OOM 7c"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    bssid0 = hapd0.own_addr()
+
+    dev[0].scan_for_bss(bssid0, freq="2412")
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   ieee80211w="2", scan_freq="2412")
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    hapd1 = hostapd.add_ap(apdev[1], params)
+    bssid1 = hapd1.own_addr()
+    dev[0].scan_for_bss(bssid1, freq="2412")
     with alloc_fail(hapd1, 1, "=wpa_sm_write_assoc_resp_ies"):
         # This will fail to roam
         dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom7d(dev, apdev):
+    """WPA2-PSK-FT and AP OOM 7d"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    bssid0 = hapd0.own_addr()
+
+    dev[0].scan_for_bss(bssid0, freq="2412")
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   ieee80211w="2", scan_freq="2412")
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2"
+    hapd1 = hostapd.add_ap(apdev[1], params)
+    bssid1 = hapd1.own_addr()
+    dev[0].scan_for_bss(bssid1, freq="2412")
     with fail_test(hapd1, 1, "wpa_ft_mic;wpa_sm_write_assoc_resp_ies"):
         # This will fail to roam
         dev[0].roam(bssid1)
@@ -2272,3 +2571,110 @@ def test_ap_ft_psk_file(dev, apdev):
     if ev is None:
         raise Exception("Timed out while waiting for failure report")
     dev[1].request("REMOVE_NETWORK all")
+
+def test_ap_ft_eap_ap_config_change(dev, apdev):
+    """WPA2-EAP-FT AP changing from 802.1X-only to FT-only"""
+    ssid = "test-ft"
+    passphrase="12345678"
+    bssid = apdev[0]['bssid']
+
+    radius = hostapd.radius_params()
+    params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=True)
+    params['wpa_key_mgmt'] = "WPA-EAP"
+    params["ieee8021x"] = "1"
+    params["pmk_r1_push"] = "0"
+    params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+    params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+    params["eap_server"] = "0"
+    params = dict(radius.items() + params.items())
+    hapd = hostapd.add_ap(apdev[0], params)
+
+    dev[0].connect(ssid, key_mgmt="FT-EAP WPA-EAP", proto="WPA2",
+                   eap="GPSK", identity="gpsk user",
+                   password="abcdefghijklmnop0123456789abcdef",
+                   scan_freq="2412")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+    dev[0].dump_monitor()
+
+    hapd.disable()
+    hapd.set('wpa_key_mgmt', "FT-EAP")
+    hapd.enable()
+
+    dev[0].request("BSS_FLUSH 0")
+    dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+
+    dev[0].request("RECONNECT")
+    dev[0].wait_connected()
+
+def test_ap_ft_eap_sha384(dev, apdev):
+    """WPA2-EAP-FT with SHA384"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    radius = hostapd.radius_params()
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+    params["ieee8021x"] = "1"
+    params = dict(radius.items() + params.items())
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+    params["ieee8021x"] = "1"
+    params = dict(radius.items() + params.items())
+    hapd1 = hostapd.add_ap(apdev[1], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
+              sha384=True)
+
+def test_ap_ft_eap_sha384_over_ds(dev, apdev):
+    """WPA2-EAP-FT with SHA384 over DS"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    radius = hostapd.radius_params()
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+    params["ieee8021x"] = "1"
+    params = dict(radius.items() + params.items())
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+    params["ieee8021x"] = "1"
+    params = dict(radius.items() + params.items())
+    hapd1 = hostapd.add_ap(apdev[1], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              eap=True, sha384=True)
+
+def test_ap_ft_roam_rrm(dev, apdev):
+    """WPA2-PSK-FT AP and radio measurement request"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["rrm_beacon_report"] = "1"
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    bssid0 = hapd0.own_addr()
+
+    addr = dev[0].own_addr()
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+    check_beacon_req(hapd0, addr, 1)
+
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["rrm_beacon_report"] = "1"
+    hapd1 = hostapd.add_ap(apdev[1], params)
+    bssid1 = hapd1.own_addr()
+
+    dev[0].scan_for_bss(bssid1, freq=2412)
+    dev[0].roam(bssid1)
+    check_beacon_req(hapd1, addr, 2)
+
+    dev[0].scan_for_bss(bssid0, freq=2412)
+    dev[0].roam(bssid0)
+    check_beacon_req(hapd0, addr, 3)