]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - tests/hwsim/test_ap_hs20.py
tests: Convert test skipping to use exception
[thirdparty/hostap.git] / tests / hwsim / test_ap_hs20.py
index 598f7f8cda74cbefec458989b2cfed4faf5b154d..2172c3d2f71ade12754119960aaa742ac6371ecc 100644 (file)
@@ -16,6 +16,8 @@ import socket
 import subprocess
 
 import hostapd
+from utils import HwsimSkip
+import hwsim_utils
 from wlantest import Wlantest
 from wpasupplicant import WpaSupplicant
 
@@ -51,9 +53,7 @@ def hs20_ap_params(ssid="test-hs20"):
 def check_auto_select(dev, bssid):
     dev.scan_for_bss(bssid, freq="2412")
     dev.request("INTERWORKING_SELECT auto freq=2412")
-    ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    ev = dev.wait_connected(timeout=15)
     if bssid not in ev:
         raise Exception("Connected to incorrect network")
     dev.request("REMOVE_NETWORK all")
@@ -96,12 +96,9 @@ def check_sp_type(dev, sp_type):
 
 def hlr_auc_gw_available():
     if not os.path.exists("/tmp/hlr_auc_gw.sock"):
-        logger.info("No hlr_auc_gw available");
-        return False
+        raise HwsimSkip("No hlr_auc_gw socket available")
     if not os.path.exists("../../hostapd/hlr_auc_gw"):
-        logger.info("No hlr_auc_gw available");
-        return False
-    return True
+        raise HwsimSkip("No hlr_auc_gw available")
 
 def interworking_ext_sim_connect(dev, bssid, method):
     dev.request("INTERWORKING_CONNECT " + bssid)
@@ -132,9 +129,7 @@ def interworking_ext_sim_auth(dev, method):
     resp = res.split(' ')[2].rstrip()
 
     dev.request("CTRL-RSP-SIM-" + id + ":GSM-AUTH:" + resp)
-    ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    dev.wait_connected(timeout=15)
 
 def interworking_connect(dev, bssid, method):
     dev.request("INTERWORKING_CONNECT " + bssid)
@@ -147,9 +142,7 @@ def interworking_auth(dev, method):
     if "(" + method + ")" not in ev:
         raise Exception("Unexpected EAP method selection")
 
-    ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    dev.wait_connected(timeout=15)
 
 def check_probe_resp(wt, bssid_unexpected, bssid_expected):
     if bssid_unexpected:
@@ -239,8 +232,53 @@ def test_ap_nai_home_realm_query(dev, apdev):
     if "example.org".encode('hex') not in nai2:
         raise Exception("Non-home realm not reported in wildcard query ")
 
+    cmds = [ "foo",
+             "00:11:22:33:44:55 123",
+             "00:11:22:33:44:55 qq" ]
+    for cmd in cmds:
+        if "FAIL" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + cmd):
+            raise Exception("Invalid HS20_GET_NAI_HOME_REALM_LIST accepted: " + cmd)
+
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid):
+        raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+    ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+    if ev is None:
+        raise Exception("ANQP operation timed out")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=0.1)
+    if ev is not None:
+        raise Exception("Unexpected ANQP response: " + ev)
+
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid + " 01000b6578616d706c652e636f6d"):
+        raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=10)
+    if ev is None:
+        raise Exception("No ANQP response")
+    if "NAI Realm list" not in ev:
+        raise Exception("Missing NAI Realm list: " + ev)
+
+    dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
+                             'password': "secret",
+                             'domain': "example.com" })
+    dev[0].dump_monitor()
+    if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid):
+        raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=10)
+    if ev is None:
+        raise Exception("No ANQP response")
+    if "NAI Realm list" not in ev:
+        raise Exception("Missing NAI Realm list: " + ev)
+
 def test_ap_interworking_scan_filtering(dev, apdev):
     """Interworking scan filtering with HESSID and access network type"""
+    try:
+        _test_ap_interworking_scan_filtering(dev, apdev)
+    finally:
+        dev[0].request("SET hessid 00:00:00:00:00:00")
+        dev[0].request("SET access_network_type 15")
+
+def _test_ap_interworking_scan_filtering(dev, apdev):
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     ssid = "test-hs20-ap1"
@@ -338,6 +376,10 @@ def test_ap_hs20_select(dev, apdev):
     dev[0].set_cred_quoted(id, "realm", "no.match.example.com");
     interworking_select(dev[0], bssid, no_match=True, freq="2412")
 
+    res = dev[0].request("SCAN_RESULTS")
+    if "[HS20]" not in res:
+        raise Exception("HS20 flag missing from scan results: " + res)
+
     bssid2 = apdev[1]['bssid']
     params = hs20_ap_params()
     params['nai_realm'] = [ "0,example.org,21" ]
@@ -367,8 +409,7 @@ def hs20_simulated_sim(dev, ap, method):
 
 def test_ap_hs20_sim(dev, apdev):
     """Hotspot 2.0 with simulated SIM and EAP-SIM"""
-    if not hlr_auc_gw_available():
-        return "skip"
+    hlr_auc_gw_available()
     hs20_simulated_sim(dev[0], apdev[0], "SIM")
     dev[0].request("INTERWORKING_SELECT auto freq=2412")
     ev = dev[0].wait_event(["INTERWORKING-ALREADY-CONNECTED"], timeout=15)
@@ -377,20 +418,17 @@ def test_ap_hs20_sim(dev, apdev):
 
 def test_ap_hs20_aka(dev, apdev):
     """Hotspot 2.0 with simulated USIM and EAP-AKA"""
-    if not hlr_auc_gw_available():
-        return "skip"
+    hlr_auc_gw_available()
     hs20_simulated_sim(dev[0], apdev[0], "AKA")
 
 def test_ap_hs20_aka_prime(dev, apdev):
     """Hotspot 2.0 with simulated USIM and EAP-AKA'"""
-    if not hlr_auc_gw_available():
-        return "skip"
+    hlr_auc_gw_available()
     hs20_simulated_sim(dev[0], apdev[0], "AKA'")
 
 def test_ap_hs20_ext_sim(dev, apdev):
     """Hotspot 2.0 with external SIM processing"""
-    if not hlr_auc_gw_available():
-        return "skip"
+    hlr_auc_gw_available()
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['hessid'] = bssid
@@ -399,16 +437,18 @@ def test_ap_hs20_ext_sim(dev, apdev):
     hostapd.add_ap(apdev[0]['ifname'], params)
 
     dev[0].hs20_enable()
-    dev[0].request("SET external_sim 1")
-    dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
-    interworking_select(dev[0], "home", freq="2412")
-    interworking_ext_sim_connect(dev[0], bssid, "SIM")
-    check_sp_type(dev[0], "home")
+    try:
+        dev[0].request("SET external_sim 1")
+        dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
+        interworking_select(dev[0], "home", freq="2412")
+        interworking_ext_sim_connect(dev[0], bssid, "SIM")
+        check_sp_type(dev[0], "home")
+    finally:
+        dev[0].request("SET external_sim 0")
 
 def test_ap_hs20_ext_sim_roaming(dev, apdev):
     """Hotspot 2.0 with external SIM processing in roaming network"""
-    if not hlr_auc_gw_available():
-        return "skip"
+    hlr_auc_gw_available()
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['hessid'] = bssid
@@ -417,11 +457,14 @@ def test_ap_hs20_ext_sim_roaming(dev, apdev):
     hostapd.add_ap(apdev[0]['ifname'], params)
 
     dev[0].hs20_enable()
-    dev[0].request("SET external_sim 1")
-    dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
-    interworking_select(dev[0], "roaming", freq="2412")
-    interworking_ext_sim_connect(dev[0], bssid, "SIM")
-    check_sp_type(dev[0], "roaming")
+    try:
+        dev[0].request("SET external_sim 1")
+        dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
+        interworking_select(dev[0], "roaming", freq="2412")
+        interworking_ext_sim_connect(dev[0], bssid, "SIM")
+        check_sp_type(dev[0], "roaming")
+    finally:
+        dev[0].request("SET external_sim 0")
 
 def test_ap_hs20_username(dev, apdev):
     """Hotspot 2.0 connection in username/password credential"""
@@ -463,6 +506,7 @@ def test_ap_hs20_connect_api(dev, apdev):
     wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
     wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
     wpas.hs20_enable()
+    wpas.flush_scan_cache()
     id = wpas.add_cred_values({ 'realm': "example.com",
                                   'username': "hs20-test",
                                   'password': "password",
@@ -494,9 +538,7 @@ def test_ap_hs20_auto_interworking(dev, apdev):
                                   'domain': "example.com",
                                   'update_identifier': "1234" })
     dev[0].request("REASSOCIATE")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    dev[0].wait_connected(timeout=15)
     check_sp_type(dev[0], "home")
     status = dev[0].get_status()
     if status['pairwise_cipher'] != "CCMP":
@@ -800,6 +842,12 @@ def test_ap_hs20_gas_while_associated(dev, apdev):
 
 def test_ap_hs20_gas_while_associated_with_pmf(dev, apdev):
     """Hotspot 2.0 connection with GAS query while associated and using PMF"""
+    try:
+        _test_ap_hs20_gas_while_associated_with_pmf(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_gas_while_associated_with_pmf(dev, apdev):
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['hessid'] = bssid
@@ -931,6 +979,11 @@ def test_ap_hs20_disallow_aps(dev, apdev):
     if "FAIL" not in ret:
         raise Exception("INTERWORKING_CONNECT to disallowed BSS not rejected")
 
+    if "FAIL" not in dev[0].request("INTERWORKING_CONNECT foo"):
+        raise Exception("Invalid INTERWORKING_CONNECT not rejected")
+    if "FAIL" not in dev[0].request("INTERWORKING_CONNECT 00:11:22:33:44:55"):
+        raise Exception("Invalid INTERWORKING_CONNECT not rejected")
+
 def policy_test(dev, ap, values, only_one=True):
     dev.dump_monitor()
     if ap:
@@ -960,9 +1013,7 @@ def policy_test(dev, ap, values, only_one=True):
                 raise Exception("Selected incorrect BSS")
             break
 
-    ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    ev = dev.wait_connected(timeout=15)
     if bssid and bssid not in ev:
         raise Exception("Connected to incorrect BSS")
 
@@ -1215,8 +1266,13 @@ def test_ap_hs20_max_bss_load2(dev, apdev):
 
 def test_ap_hs20_multi_cred_sp_prio(dev, apdev):
     """Hotspot 2.0 multi-cred sp_priority"""
-    if not hlr_auc_gw_available():
-        return "skip"
+    try:
+        _test_ap_hs20_multi_cred_sp_prio(dev, apdev)
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def _test_ap_hs20_multi_cred_sp_prio(dev, apdev):
+    hlr_auc_gw_available()
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['hessid'] = bssid
@@ -1252,8 +1308,13 @@ def test_ap_hs20_multi_cred_sp_prio(dev, apdev):
 
 def test_ap_hs20_multi_cred_sp_prio2(dev, apdev):
     """Hotspot 2.0 multi-cred sp_priority with two BSSes"""
-    if not hlr_auc_gw_available():
-        return "skip"
+    try:
+        _test_ap_hs20_multi_cred_sp_prio2(dev, apdev)
+    finally:
+        dev[0].request("SET external_sim 0")
+
+def _test_ap_hs20_multi_cred_sp_prio2(dev, apdev):
+    hlr_auc_gw_available()
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['hessid'] = bssid
@@ -1534,6 +1595,12 @@ def test_ap_hs20_min_bandwidth_no_wan_metrics(dev, apdev):
 
 def test_ap_hs20_deauth_req_ess(dev, apdev):
     """Hotspot 2.0 connection and deauthentication request for ESS"""
+    try:
+        _test_ap_hs20_deauth_req_ess(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_ess(dev, apdev):
     dev[0].request("SET pmf 2")
     eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
     dev[0].dump_monitor()
@@ -1546,9 +1613,7 @@ def test_ap_hs20_deauth_req_ess(dev, apdev):
     if "1 120 http://example.com/" not in ev:
         raise Exception("Unexpected deauth imminent notice: " + ev)
     hapd.request("DEAUTHENTICATE " + addr)
-    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
-    if ev is None:
-        raise Exception("Timeout on disconnection")
+    dev[0].wait_disconnected(timeout=10)
     if "[TEMP-DISABLED]" not in dev[0].list_networks()[0]['flags']:
         raise Exception("Network not marked temporarily disabled")
     ev = dev[0].wait_event(["SME: Trying to authenticate",
@@ -1559,6 +1624,12 @@ def test_ap_hs20_deauth_req_ess(dev, apdev):
 
 def test_ap_hs20_deauth_req_bss(dev, apdev):
     """Hotspot 2.0 connection and deauthentication request for BSS"""
+    try:
+        _test_ap_hs20_deauth_req_bss(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_bss(dev, apdev):
     dev[0].request("SET pmf 2")
     eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
     dev[0].dump_monitor()
@@ -1571,9 +1642,7 @@ def test_ap_hs20_deauth_req_bss(dev, apdev):
     if "0 120 http://example.com/" not in ev:
         raise Exception("Unexpected deauth imminent notice: " + ev)
     hapd.request("DEAUTHENTICATE " + addr + " reason=4")
-    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
-    if ev is None:
-        raise Exception("Timeout on disconnection")
+    ev = dev[0].wait_disconnected(timeout=10)
     if "reason=4" not in ev:
         raise Exception("Unexpected disconnection reason")
     if "[TEMP-DISABLED]" not in dev[0].list_networks()[0]['flags']:
@@ -1586,6 +1655,12 @@ def test_ap_hs20_deauth_req_bss(dev, apdev):
 
 def test_ap_hs20_deauth_req_from_radius(dev, apdev):
     """Hotspot 2.0 connection and deauthentication request from RADIUS"""
+    try:
+        _test_ap_hs20_deauth_req_from_radius(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_from_radius(dev, apdev):
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['nai_realm'] = [ "0,example.com,21[2:4]" ]
@@ -1604,12 +1679,16 @@ def test_ap_hs20_deauth_req_from_radius(dev, apdev):
         raise Exception("Timeout on deauth imminent notice")
     if " 1 100" not in ev:
         raise Exception("Unexpected deauth imminent contents")
-    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
-    if ev is None:
-        raise Exception("Timeout on disconnection")
+    dev[0].wait_disconnected(timeout=3)
 
 def test_ap_hs20_remediation_required(dev, apdev):
     """Hotspot 2.0 connection and remediation required from RADIUS"""
+    try:
+        _test_ap_hs20_remediation_required(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_remediation_required(dev, apdev):
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['nai_realm'] = [ "0,example.com,21[2:4]" ]
@@ -1630,6 +1709,12 @@ def test_ap_hs20_remediation_required(dev, apdev):
 
 def test_ap_hs20_remediation_required_ctrl(dev, apdev):
     """Hotspot 2.0 connection and subrem from ctrl_iface"""
+    try:
+        _test_ap_hs20_remediation_required_ctrl(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_remediation_required_ctrl(dev, apdev):
     bssid = apdev[0]['bssid']
     addr = dev[0].p2p_dev_addr()
     params = hs20_ap_params()
@@ -1665,6 +1750,12 @@ def test_ap_hs20_remediation_required_ctrl(dev, apdev):
 
 def test_ap_hs20_session_info(dev, apdev):
     """Hotspot 2.0 connection and session information from RADIUS"""
+    try:
+        _test_ap_hs20_session_info(dev, apdev)
+    finally:
+        dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_session_info(dev, apdev):
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['nai_realm'] = [ "0,example.com,21[2:4]" ]
@@ -1738,9 +1829,7 @@ def test_ap_hs20_network_preference(dev, apdev):
 
     dev[0].scan_for_bss(bssid, freq="2412")
     dev[0].request("INTERWORKING_SELECT auto freq=2412")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    ev = dev[0].wait_connected(timeout=15)
     if bssid not in ev:
         raise Exception("Unexpected network selected")
 
@@ -1780,9 +1869,7 @@ def test_ap_hs20_network_preference2(dev, apdev):
 
     dev[0].scan_for_bss(bssid2, freq="2412")
     dev[0].request("INTERWORKING_SELECT auto freq=2412")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    ev = dev[0].wait_connected(timeout=15)
     if bssid2 not in ev:
         raise Exception("Unexpected network selected")
 
@@ -1826,9 +1913,7 @@ def test_ap_hs20_network_preference3(dev, apdev):
     dev[0].scan_for_bss(bssid, freq="2412")
     dev[0].scan_for_bss(bssid2, freq="2412")
     dev[0].request("INTERWORKING_SELECT auto freq=2412")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    ev = dev[0].wait_connected(timeout=15)
     if bssid not in ev:
         raise Exception("Unexpected network selected")
 
@@ -1870,9 +1955,7 @@ def test_ap_hs20_network_preference4(dev, apdev):
     dev[0].scan_for_bss(bssid, freq="2412")
     dev[0].scan_for_bss(bssid2, freq="2412")
     dev[0].request("INTERWORKING_SELECT auto freq=2412")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Connection timed out")
+    ev = dev[0].wait_connected(timeout=15)
     if bssid not in ev:
         raise Exception("Unexpected network selected")
 
@@ -1930,6 +2013,8 @@ def test_ap_hs20_fetch_osu(dev, apdev):
         dev[1].scan_for_bss(bssid, freq="2412")
         dev[0].request("SET osu_dir " + dir)
         dev[0].request("FETCH_OSU")
+        if "FAIL" not in dev[1].request("HS20_ICON_REQUEST foo w1fi_logo"):
+            raise Exception("Invalid HS20_ICON_REQUEST accepted")
         if "OK" not in dev[1].request("HS20_ICON_REQUEST " + bssid + " w1fi_logo"):
             raise Exception("HS20_ICON_REQUEST failed")
         icons = 0
@@ -1975,6 +2060,91 @@ def test_ap_hs20_fetch_osu(dev, apdev):
     if "Icon Binary File" not in ev:
         raise Exception("Unexpected ANQP element")
 
+def test_ap_hs20_fetch_osu_stop(dev, apdev):
+    """Hotspot 2.0 OSU provider fetch stopped"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+    params['osu_ssid'] = '"HS 2.0 OSU open"'
+    params['osu_method_list'] = "1"
+    params['osu_friendly_name'] = [ "eng:Test OSU", "fin:Testi-OSU" ]
+    params['osu_icon'] = "w1fi_logo"
+    params['osu_service_desc'] = [ "eng:Example services", "fin:Esimerkkipalveluja" ]
+    params['osu_server_uri'] = "https://example.com/osu/"
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dir = "/tmp/osu-fetch"
+    if os.path.isdir(dir):
+       files = [ f for f in os.listdir(dir) if f.startswith("osu-") ]
+       for f in files:
+           os.remove(dir + "/" + f)
+    else:
+        try:
+            os.makedirs(dir)
+        except:
+            pass
+    try:
+        dev[0].request("SET osu_dir " + dir)
+        dev[0].request("SCAN freq=2412-2462")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=10)
+        if ev is None:
+            raise Exception("Scan did not start")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while scanning")
+        ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+        if ev is None:
+            raise Exception("Scan timed out")
+        hapd.set("ext_mgmt_frame_handling", "1")
+        dev[0].request("FETCH_ANQP")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in FETCH_ANQP")
+        dev[0].request("STOP_FETCH_ANQP")
+        dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+        dev[0].dump_monitor()
+        hapd.dump_monitor()
+        dev[0].request("INTERWORKING_SELECT freq=2412")
+        for i in range(5):
+            msg = hapd.mgmt_rx()
+            if msg['subtype'] == 13:
+                break
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in INTERWORKING_SELECT")
+        ev = dev[0].wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
+                               timeout=15)
+        if ev is None:
+            raise Exception("Network selection timed out");
+
+        dev[0].dump_monitor()
+        if "OK" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU failed")
+        dev[0].request("CANCEL_FETCH_OSU")
+
+        for i in range(15):
+            time.sleep(0.5)
+            if dev[0].get_driver_status_field("scan_state") == "SCAN_COMPLETED":
+                break
+
+        dev[0].dump_monitor()
+        if "OK" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU failed")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in FETCH_OSU")
+        ev = dev[0].wait_event(["GAS-QUERY-START"], 10)
+        if ev is None:
+            raise Exception("GAS timed out")
+        if "FAIL" not in dev[0].request("FETCH_OSU"):
+            raise Exception("FETCH_OSU accepted while in FETCH_OSU")
+        dev[0].request("CANCEL_FETCH_OSU")
+        ev = dev[0].wait_event(["GAS-QUERY-DONE"], 10)
+        if ev is None:
+            raise Exception("GAS event timed out after CANCEL_FETCH_OSU")
+    finally:
+        files = [ f for f in os.listdir(dir) if f.startswith("osu-") ]
+        for f in files:
+            os.remove(dir + "/" + f)
+        os.rmdir(dir)
+
 def test_ap_hs20_ft(dev, apdev):
     """Hotspot 2.0 connection with FT"""
     bssid = apdev[0]['bssid']
@@ -2001,7 +2171,7 @@ def test_ap_hs20_remediation_sql(dev, apdev, params):
     try:
         import sqlite3
     except ImportError:
-        return "skip"
+        raise HwsimSkip("No sqlite3 module available")
     dbfile = os.path.join(params['logdir'], "eap-user.db")
     try:
         os.remove(dbfile)
@@ -2057,6 +2227,7 @@ def test_ap_hs20_remediation_sql(dev, apdev, params):
 
     finally:
         os.remove(dbfile)
+        dev[0].request("SET pmf 0")
 
 def test_ap_hs20_external_selection(dev, apdev):
     """Hotspot 2.0 connection using external network selection and creation"""
@@ -2089,7 +2260,7 @@ def test_ap_hs20_random_mac_addr(dev, apdev):
     wpas.request("SET preassoc_mac_addr 1")
     wpas.request("SET rand_addr_lifetime 60")
     wpas.hs20_enable()
-    wpas.scan(freq="2412", only_new=True)
+    wpas.flush_scan_cache()
     id = wpas.add_cred_values({ 'realm': "example.com",
                                   'username': "hs20-test",
                                   'password': "password",
@@ -2109,6 +2280,41 @@ def test_ap_hs20_random_mac_addr(dev, apdev):
     if sta['addr'] != addr1:
         raise Exception("STA association with random address not found")
 
+def test_ap_hs20_multi_network_and_cred_removal(dev, apdev):
+    """Multiple networks and cred removal"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,25[3:26]"]
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].add_network()
+    dev[0].hs20_enable()
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "user",
+                                  'password': "password" })
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "PEAP")
+    dev[0].add_network()
+
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected(timeout=10)
+
+    hapd.disable()
+    hapd.set("ssid", "another ssid")
+    hapd.enable()
+
+    interworking_select(dev[0], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "PEAP")
+    dev[0].add_network()
+    if len(dev[0].list_networks()) != 5:
+        raise Exception("Unexpected number of networks prior to remove_crec")
+
+    dev[0].dump_monitor()
+    dev[0].remove_cred(id)
+    if len(dev[0].list_networks()) != 3:
+        raise Exception("Unexpected number of networks after to remove_crec")
+    dev[0].wait_disconnected(timeout=10)
+
 def _test_ap_hs20_proxyarp(dev, apdev):
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
@@ -2127,8 +2333,7 @@ def _test_ap_hs20_proxyarp(dev, apdev):
         hapd.enable()
     except:
         # For now, do not report failures due to missing kernel support
-        logger.info("Could not start hostapd - assume proxyarp not supported in kernel version")
-        return "skip"
+        raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
     ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
     if ev is None:
         raise Exception("AP startup timed out")
@@ -2198,16 +2403,118 @@ def _test_ap_hs20_proxyarp(dev, apdev):
 
 def test_ap_hs20_proxyarp(dev, apdev):
     """Hotspot 2.0 and ProxyARP"""
-    res = None
     try:
-        res = _test_ap_hs20_proxyarp(dev, apdev)
+        _test_ap_hs20_proxyarp(dev, apdev)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
+
+def _test_ap_hs20_proxyarp_dgaf(dev, apdev, disabled):
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1' if disabled else '0'
+    params['proxy_arp'] = '1'
+    params['ap_isolate'] = '1'
+    params['bridge'] = 'ap-br0'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+    try:
+        hapd.enable()
+    except:
+        # For now, do not report failures due to missing kernel support
+        raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+    ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+    if ev is None:
+        raise Exception("AP startup timed out")
+
+    dev[0].hs20_enable()
+    subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+    id = dev[0].add_cred_values({ 'realm': "example.com",
+                                  'username': "hs20-test",
+                                  'password': "password",
+                                  'ca_cert': "auth_serv/ca.pem",
+                                  'domain': "example.com",
+                                  'update_identifier': "1234" })
+    interworking_select(dev[0], bssid, "home", freq="2412")
+    interworking_connect(dev[0], bssid, "TTLS")
+
+    dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="hs20-test", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   scan_freq="2412")
+    time.sleep(0.1)
+
+    addr0 = dev[0].p2p_interface_addr()
+
+    src_ll_opt0 = "\x01\x01" + binascii.unhexlify(addr0.replace(':',''))
+
+    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
+                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
+                   opt=src_ll_opt0)
+    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_ra(src_ll=apdev[0]['bssid'], ip_src="aaaa:bbbb:cccc::33",
+                   ip_dst="ff01::1")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_na(src_ll=apdev[0]['bssid'], ip_src="aaaa:bbbb:cccc::44",
+                   ip_dst="ff01::1", target="aaaa:bbbb:cccc::55")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.123", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+    # another copy for additional code coverage
+    pkt = build_dhcp_ack(dst_ll=addr0, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.123", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After connect: " + str(matches))
+    if len(matches) != 2:
+        raise Exception("Unexpected number of neighbor entries after connect")
+    if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 addr missing")
+    if '192.168.1.123 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 IPv4 addr missing")
+    dev[0].request("DISCONNECT")
+    dev[1].request("DISCONNECT")
+    time.sleep(0.5)
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After disconnect: " + str(matches))
+    if len(matches) > 0:
+        raise Exception("Unexpected neighbor entries after disconnect")
+
+def test_ap_hs20_proxyarp_disable_dgaf(dev, apdev):
+    """Hotspot 2.0 and ProxyARP with DGAF disabled"""
+    try:
+        _test_ap_hs20_proxyarp_dgaf(dev, apdev, True)
     finally:
         subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
                         stderr=open('/dev/null', 'w'))
         subprocess.call(['brctl', 'delbr', 'ap-br0'],
                         stderr=open('/dev/null', 'w'))
 
-    return res
+def test_ap_hs20_proxyarp_enable_dgaf(dev, apdev):
+    """Hotspot 2.0 and ProxyARP with DGAF enabled"""
+    try:
+        _test_ap_hs20_proxyarp_dgaf(dev, apdev, False)
+    finally:
+        subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+                        stderr=open('/dev/null', 'w'))
+        subprocess.call(['brctl', 'delbr', 'ap-br0'],
+                        stderr=open('/dev/null', 'w'))
 
 def ip_checksum(buf):
     sum = 0
@@ -2220,6 +2527,19 @@ def ip_checksum(buf):
         sum = (sum & 0xffff) + (sum >> 16)
     return struct.pack('H', ~sum & 0xffff)
 
+def ipv6_solicited_node_mcaddr(target):
+    prefix = socket.inet_pton(socket.AF_INET6, "ff02::1:ff00:0")
+    mask = socket.inet_pton(socket.AF_INET6, "::ff:ffff")
+    _target = socket.inet_pton(socket.AF_INET6, target)
+    p = struct.unpack('4I', prefix)
+    m = struct.unpack('4I', mask)
+    t = struct.unpack('4I', _target)
+    res = (p[0] | (t[0] & m[0]),
+           p[1] | (t[1] & m[1]),
+           p[2] | (t[2] & m[2]),
+           p[3] | (t[3] & m[3]))
+    return socket.inet_ntop(socket.AF_INET6, struct.pack('4I', *res))
+
 def build_icmpv6(ipv6_addrs, type, code, payload):
     start = struct.pack("BB", type, code)
     end = payload
@@ -2228,12 +2548,36 @@ def build_icmpv6(ipv6_addrs, type, code, payload):
     csum = ip_checksum(pseudo + icmp)
     return start + csum + end
 
+def build_ra(src_ll, ip_src, ip_dst, cur_hop_limit=0, router_lifetime=0,
+             reachable_time=0, retrans_timer=0, opt=None):
+    link_mc = binascii.unhexlify("3333ff000002")
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x86\xdd'
+    ehdr = link_mc + _src_ll + proto
+    _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+    _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+    adv = struct.pack('>BBHLL', cur_hop_limit, 0, router_lifetime,
+                      reachable_time, retrans_timer)
+    if opt:
+        payload = adv + opt
+    else:
+        payload = adv
+    icmp = build_icmpv6(_ip_src + _ip_dst, 134, 0, payload)
+
+    ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+    ipv6 += _ip_src + _ip_dst
+
+    return ehdr + ipv6 + icmp
+
 def build_ns(src_ll, ip_src, ip_dst, target, opt=None):
     link_mc = binascii.unhexlify("3333ff000002")
     _src_ll = binascii.unhexlify(src_ll.replace(':',''))
     proto = '\x86\xdd'
     ehdr = link_mc + _src_ll + proto
     _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+    if ip_dst is None:
+        ip_dst = ipv6_solicited_node_mcaddr(target)
     _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
 
     reserved = '\x00\x00\x00\x00'
@@ -2249,13 +2593,169 @@ def build_ns(src_ll, ip_src, ip_dst, target, opt=None):
 
     return ehdr + ipv6 + icmp
 
+def send_ns(dev, src_ll=None, target=None, ip_src=None, ip_dst=None, opt=None,
+            hapd_bssid=None):
+    if hapd_bssid:
+        if src_ll is None:
+            src_ll = hapd_bssid
+        cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+    else:
+        if src_ll is None:
+            src_ll = dev.p2p_interface_addr()
+        cmd = "DATA_TEST_FRAME "
+
+    if opt is None:
+        opt = "\x01\x01" + binascii.unhexlify(src_ll.replace(':',''))
+
+    pkt = build_ns(src_ll=src_ll, ip_src=ip_src, ip_dst=ip_dst, target=target,
+                   opt=opt)
+    if "OK" not in dev.request(cmd + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def build_na(src_ll, ip_src, ip_dst, target, opt=None):
+    link_mc = binascii.unhexlify("3333ff000002")
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x86\xdd'
+    ehdr = link_mc + _src_ll + proto
+    _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+    _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+    reserved = '\x00\x00\x00\x00'
+    _target = socket.inet_pton(socket.AF_INET6, target)
+    if opt:
+        payload = reserved + _target + opt
+    else:
+        payload = reserved + _target
+    icmp = build_icmpv6(_ip_src + _ip_dst, 136, 0, payload)
+
+    ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+    ipv6 += _ip_src + _ip_dst
+
+    return ehdr + ipv6 + icmp
+
+def send_na(dev, src_ll=None, target=None, ip_src=None, ip_dst=None, opt=None,
+            hapd_bssid=None):
+    if hapd_bssid:
+        if src_ll is None:
+            src_ll = hapd_bssid
+        cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+    else:
+        if src_ll is None:
+            src_ll = dev.p2p_interface_addr()
+        cmd = "DATA_TEST_FRAME "
+
+    pkt = build_na(src_ll=src_ll, ip_src=ip_src, ip_dst=ip_dst, target=target,
+                   opt=opt)
+    if "OK" not in dev.request(cmd + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def build_dhcp_ack(dst_ll, src_ll, ip_src, ip_dst, yiaddr, chaddr,
+                   subnet_mask="255.255.255.0", truncated_opt=False,
+                   wrong_magic=False, force_tot_len=None, no_dhcp=False):
+    _dst_ll = binascii.unhexlify(dst_ll.replace(':',''))
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x08\x00'
+    ehdr = _dst_ll + _src_ll + proto
+    _ip_src = socket.inet_pton(socket.AF_INET, ip_src)
+    _ip_dst = socket.inet_pton(socket.AF_INET, ip_dst)
+    _subnet_mask = socket.inet_pton(socket.AF_INET, subnet_mask)
+
+    _ciaddr = '\x00\x00\x00\x00'
+    _yiaddr = socket.inet_pton(socket.AF_INET, yiaddr)
+    _siaddr = '\x00\x00\x00\x00'
+    _giaddr = '\x00\x00\x00\x00'
+    _chaddr = binascii.unhexlify(chaddr.replace(':','') + "00000000000000000000")
+    payload = struct.pack('>BBBBL3BB', 2, 1, 6, 0, 12345, 0, 0, 0, 0)
+    payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + 192*'\x00'
+    # magic
+    if wrong_magic:
+        payload += '\x63\x82\x53\x00'
+    else:
+        payload += '\x63\x82\x53\x63'
+    if truncated_opt:
+        payload += '\x22\xff\x00'
+    # Option: DHCP Message Type = ACK
+    payload += '\x35\x01\x05'
+    # Pad Option
+    payload += '\x00'
+    # Option: Subnet Mask
+    payload += '\x01\x04' + _subnet_mask
+    # Option: Time Offset
+    payload += struct.pack('>BBL', 2, 4, 0)
+    # End Option
+    payload += '\xff'
+    # Pad Option
+    payload += '\x00\x00\x00\x00'
+
+    if no_dhcp:
+        payload = struct.pack('>BBBBL3BB', 2, 1, 6, 0, 12345, 0, 0, 0, 0)
+        payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + 192*'\x00'
+
+    udp = struct.pack('>HHHH', 67, 68, 8 + len(payload), 0) + payload
+
+    if force_tot_len:
+        tot_len = force_tot_len
+    else:
+        tot_len = 20 + len(udp)
+    start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+    ipv4 = start + '\x00\x00' + _ip_src + _ip_dst
+    csum = ip_checksum(ipv4)
+    ipv4 = start + csum + _ip_src + _ip_dst
+
+    return ehdr + ipv4 + udp
+
+def build_arp(dst_ll, src_ll, opcode, sender_mac, sender_ip,
+              target_mac, target_ip):
+    _dst_ll = binascii.unhexlify(dst_ll.replace(':',''))
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\x08\x06'
+    ehdr = _dst_ll + _src_ll + proto
+
+    _sender_mac = binascii.unhexlify(sender_mac.replace(':',''))
+    _sender_ip = socket.inet_pton(socket.AF_INET, sender_ip)
+    _target_mac = binascii.unhexlify(target_mac.replace(':',''))
+    _target_ip = socket.inet_pton(socket.AF_INET, target_ip)
+
+    arp = struct.pack('>HHBBH', 1, 0x0800, 6, 4, opcode)
+    arp += _sender_mac + _sender_ip
+    arp += _target_mac + _target_ip
+
+    return ehdr + arp
+
+def send_arp(dev, dst_ll="ff:ff:ff:ff:ff:ff", src_ll=None, opcode=1,
+             sender_mac=None, sender_ip="0.0.0.0",
+             target_mac="00:00:00:00:00:00", target_ip="0.0.0.0",
+             hapd_bssid=None):
+    if hapd_bssid:
+        if src_ll is None:
+            src_ll = hapd_bssid
+        if sender_mac is None:
+            sender_mac = hapd_bssid
+        cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+    else:
+        if src_ll is None:
+            src_ll = dev.p2p_interface_addr()
+        if sender_mac is None:
+            sender_mac = dev.p2p_interface_addr()
+        cmd = "DATA_TEST_FRAME "
+
+    pkt = build_arp(dst_ll=dst_ll, src_ll=src_ll, opcode=opcode,
+                    sender_mac=sender_mac, sender_ip=sender_ip,
+                    target_mac=target_mac, target_ip=target_ip)
+    if "OK" not in dev.request(cmd + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
 def get_permanent_neighbors(ifname):
     cmd = subprocess.Popen(['ip', 'nei'], stdout=subprocess.PIPE)
     res = cmd.stdout.read()
     cmd.stdout.close()
     return [ line for line in res.splitlines() if "PERMANENT" in line and ifname in line ]
 
-def _test_proxyarp_open(dev, apdev):
+def _test_proxyarp_open(dev, apdev, params):
+    cap_br = os.path.join(params['logdir'], "proxyarp_open.ap-br0.pcap")
+    cap_dev0 = os.path.join(params['logdir'], "proxyarp_open.%s.pcap" % dev[0].ifname)
+    cap_dev1 = os.path.join(params['logdir'], "proxyarp_open.%s.pcap" % dev[1].ifname)
+
     bssid = apdev[0]['bssid']
     params = { 'ssid': 'open' }
     params['proxy_arp'] = '1'
@@ -2267,8 +2767,7 @@ def _test_proxyarp_open(dev, apdev):
         hapd.enable()
     except:
         # For now, do not report failures due to missing kernel support
-        logger.info("Could not start hostapd - assume proxyarp not supported in kernel version")
-        return "skip"
+        raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
     ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
     if ev is None:
         raise Exception("AP startup timed out")
@@ -2278,6 +2777,39 @@ def _test_proxyarp_open(dev, apdev):
     subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
     subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
 
+    for chain in [ 'FORWARD', 'OUTPUT' ]:
+        subprocess.call(['ebtables', '-A', chain, '-p', 'ARP',
+                         '-d', 'Broadcast', '-o', apdev[0]['ifname'],
+                         '-j', 'DROP'])
+        subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+                         '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                         '--ip6-icmp-type', 'neighbor-solicitation',
+                         '-o', apdev[0]['ifname'], '-j', 'DROP'])
+        subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+                         '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                         '--ip6-icmp-type', 'neighbor-advertisement',
+                         '-o', apdev[0]['ifname'], '-j', 'DROP'])
+        subprocess.call(['ebtables', '-A', chain,
+                         '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                         '--ip6-icmp-type', 'router-solicitation',
+                         '-o', apdev[0]['ifname'], '-j', 'DROP'])
+        # Multicast Listener Report Message
+        subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+                         '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                         '--ip6-icmp-type', '143',
+                         '-o', apdev[0]['ifname'], '-j', 'DROP'])
+
+    cmd = {}
+    cmd[0] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', 'ap-br0',
+                               '-w', cap_br, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+    cmd[1] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', dev[0].ifname,
+                               '-w', cap_dev0, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+    cmd[2] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', dev[1].ifname,
+                               '-w', cap_dev1, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+
     dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
     dev[1].connect("open", key_mgmt="NONE", scan_freq="2412")
     time.sleep(0.1)
@@ -2289,58 +2821,97 @@ def _test_proxyarp_open(dev, apdev):
     src_ll_opt1 = "\x01\x01" + binascii.unhexlify(addr1.replace(':',''))
 
     # DAD NS
-    pkt = build_ns(src_ll=addr0, ip_src="::", ip_dst="ff02::1:ff00:2",
-                   target="aaaa:bbbb:cccc::2", opt=src_ll_opt0)
-    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
-        raise Exception("DATA_TEST_FRAME failed")
+    send_ns(dev[0], ip_src="::", target="aaaa:bbbb:cccc::2")
 
-    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
-                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
-                   opt=src_ll_opt0)
-    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
-        raise Exception("DATA_TEST_FRAME failed")
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2")
     # test frame without source link-layer address option
-    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
-                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2")
-    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
-        raise Exception("DATA_TEST_FRAME failed")
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt='')
     # test frame with bogus option
-    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
-                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
-                   opt="\x70\x01\x01\x02\x03\x04\x05\x05")
-    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
-        raise Exception("DATA_TEST_FRAME failed")
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt="\x70\x01\x01\x02\x03\x04\x05\x05")
     # test frame with truncated source link-layer address option
-    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
-                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
-                   opt="\x01\x01\x01\x02\x03\x04")
-    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
-        raise Exception("DATA_TEST_FRAME failed")
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt="\x01\x01\x01\x02\x03\x04")
     # test frame with foreign source link-layer address option
-    pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
-                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
-                   opt="\x01\x01\x01\x02\x03\x04\x05\x06")
-    if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+    send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+            opt="\x01\x01\x01\x02\x03\x04\x05\x06")
+
+    send_ns(dev[1], ip_src="aaaa:bbbb:dddd::2", target="aaaa:bbbb:dddd::2")
+
+    send_ns(dev[1], ip_src="aaaa:bbbb:eeee::2", target="aaaa:bbbb:eeee::2")
+    # another copy for additional code coverage
+    send_ns(dev[1], ip_src="aaaa:bbbb:eeee::2", target="aaaa:bbbb:eeee::2")
+
+    pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.124", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+    # Change address and verify unicast
+    pkt = build_dhcp_ack(dst_ll=addr0, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.123", chaddr=addr0)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
         raise Exception("DATA_TEST_FRAME failed")
 
-    pkt = build_ns(src_ll=addr1, ip_src="aaaa:bbbb:dddd::2",
-                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:dddd::2",
-                   opt=src_ll_opt1)
-    if "OK" not in dev[1].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+    # Not-associated client MAC address
+    pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.125", chaddr="22:33:44:55:66:77")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
         raise Exception("DATA_TEST_FRAME failed")
 
-    pkt = build_ns(src_ll=addr1, ip_src="aaaa:bbbb:eeee::2",
-                   ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:eeee::2",
-                   opt=src_ll_opt1)
-    if "OK" not in dev[1].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+    # No IP address
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="0.0.0.0", chaddr=addr1)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
         raise Exception("DATA_TEST_FRAME failed")
-    # another copy for additional code coverage
-    if "OK" not in dev[1].request("DATA_TEST_FRAME " + binascii.hexlify(pkt)):
+
+    # Zero subnet mask
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.126", chaddr=addr1,
+                         subnet_mask="0.0.0.0")
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Truncated option
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.127", chaddr=addr1,
+                         truncated_opt=True)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Wrong magic
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.128", chaddr=addr1,
+                         wrong_magic=True)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # Wrong IPv4 total length
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.129", chaddr=addr1,
+                         force_tot_len=1000)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    # BOOTP
+    pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+                         ip_src="192.168.1.1", ip_dst="255.255.255.255",
+                         yiaddr="192.168.1.129", chaddr=addr1,
+                         no_dhcp=True)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
         raise Exception("DATA_TEST_FRAME failed")
 
     matches = get_permanent_neighbors("ap-br0")
     logger.info("After connect: " + str(matches))
-    if len(matches) != 3:
+    if len(matches) != 4:
         raise Exception("Unexpected number of neighbor entries after connect")
     if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
         raise Exception("dev0 addr missing")
@@ -2348,24 +2919,121 @@ def _test_proxyarp_open(dev, apdev):
         raise Exception("dev1 addr(1) missing")
     if 'aaaa:bbbb:eeee::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
         raise Exception("dev1 addr(2) missing")
+    if '192.168.1.123 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+        raise Exception("dev0 IPv4 addr missing")
+
+    targets = [ "192.168.1.123", "192.168.1.124", "192.168.1.125",
+                "192.168.1.126" ]
+    for target in targets:
+        send_arp(dev[1], sender_ip="192.168.1.100", target_ip=target)
+
+    for target in targets:
+        send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.101",
+                 target_ip=target)
+
+    # ARP Probe from wireless STA
+    send_arp(dev[1], target_ip="192.168.1.127")
+    # ARP Announcement from wireless STA
+    send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.127")
+    send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.127",
+             opcode=2)
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After ARP Probe + Announcement: " + str(matches))
+
+    # ARP Request for the newly introduced IP address from wireless STA
+    send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.127")
+
+    # ARP Request for the newly introduced IP address from bridge
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.102",
+             target_ip="192.168.1.127")
+
+    # ARP Probe from bridge
+    send_arp(hapd, hapd_bssid=bssid, target_ip="192.168.1.130")
+    # ARP Announcement from bridge (not to be learned by AP for proxyarp)
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.130",
+             target_ip="192.168.1.130")
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.130",
+             target_ip="192.168.1.130", opcode=2)
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After ARP Probe + Announcement: " + str(matches))
+
+    # ARP Request for the newly introduced IP address from wireless STA
+    send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.130")
+    # ARP Response from bridge (AP does not proxy for non-wireless devices)
+    send_arp(hapd, hapd_bssid=bssid, dst_ll=addr0, sender_ip="192.168.1.130",
+             target_ip="192.168.1.123", opcode=2)
+
+    # ARP Request for the newly introduced IP address from bridge
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.102",
+             target_ip="192.168.1.130")
+
+    # ARP Probe from wireless STA (duplicate address; learned through DHCP)
+    send_arp(dev[1], target_ip="192.168.1.123")
+    # ARP Probe from wireless STA (duplicate address; learned through ARP)
+    send_arp(dev[0], target_ip="192.168.1.127")
+
+    # Gratuitous ARP Reply for another STA's IP address
+    send_arp(dev[0], opcode=2, sender_mac=addr0, sender_ip="192.168.1.127",
+             target_mac=addr1, target_ip="192.168.1.127")
+    send_arp(dev[1], opcode=2, sender_mac=addr1, sender_ip="192.168.1.123",
+             target_mac=addr0, target_ip="192.168.1.123")
+    # ARP Request to verify previous mapping
+    send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.123")
+    send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.127")
+
+    time.sleep(0.1)
+
+    send_ns(dev[0], target="aaaa:bbbb:dddd::2", ip_src="aaaa:bbbb:cccc::2")
+    time.sleep(0.1)
+    send_ns(dev[1], target="aaaa:bbbb:cccc::2", ip_src="aaaa:bbbb:dddd::2")
+    time.sleep(0.1)
+    send_ns(hapd, hapd_bssid=bssid, target="aaaa:bbbb:dddd::2",
+            ip_src="aaaa:bbbb:ffff::2")
+    time.sleep(0.1)
+
+    # Try to probe for an already assigned address
+    send_ns(dev[1], target="aaaa:bbbb:cccc::2", ip_src="::")
+    time.sleep(0.1)
+    send_ns(hapd, hapd_bssid=bssid, target="aaaa:bbbb:cccc::2", ip_src="::")
+    time.sleep(0.1)
+
+    # Unsolicited NA
+    send_na(dev[1], target="aaaa:bbbb:cccc:aeae::3",
+            ip_src="aaaa:bbbb:cccc:aeae::3", ip_dst="ff02::1")
+    send_na(hapd, hapd_bssid=bssid, target="aaaa:bbbb:cccc:aeae::4",
+            ip_src="aaaa:bbbb:cccc:aeae::4", ip_dst="ff02::1")
+
+    hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+    hwsim_utils.test_connectivity_iface(dev[1], hapd, "ap-br0")
+    hwsim_utils.test_connectivity(dev[0], dev[1])
 
     dev[0].request("DISCONNECT")
     dev[1].request("DISCONNECT")
     time.sleep(0.5)
+    for i in range(3):
+        cmd[i].terminate()
     matches = get_permanent_neighbors("ap-br0")
     logger.info("After disconnect: " + str(matches))
     if len(matches) > 0:
         raise Exception("Unexpected neighbor entries after disconnect")
+    cmd = subprocess.Popen(['ebtables', '-L', '--Lc'], stdout=subprocess.PIPE)
+    res = cmd.stdout.read()
+    cmd.stdout.close()
+    logger.info("ebtables results:\n" + res)
 
-def test_proxyarp_open(dev, apdev):
+def test_proxyarp_open(dev, apdev, params):
     """ProxyARP with open network"""
-    res = None
     try:
-        res = _test_proxyarp_open(dev, apdev)
+        _test_proxyarp_open(dev, apdev, params)
     finally:
+        try:
+            subprocess.call(['ebtables', '-F', 'FORWARD'])
+            subprocess.call(['ebtables', '-F', 'OUTPUT'])
+        except:
+            pass
         subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
                         stderr=open('/dev/null', 'w'))
         subprocess.call(['brctl', 'delbr', 'ap-br0'],
                         stderr=open('/dev/null', 'w'))
-
-    return res