diff --git a/tests/hwsim/test_rrm.py b/tests/hwsim/test_rrm.py index 237ea09f0..3a7405d3d 100644 --- a/tests/hwsim/test_rrm.py +++ b/tests/hwsim/test_rrm.py @@ -1,16 +1,21 @@ # Radio measurement # Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. # Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. +# Copyright (c) 2017, Jouni Malinen # # This software may be distributed under the terms of the BSD license. # See README for more details. +import binascii import re import logging logger = logging.getLogger() +import struct +import subprocess import hostapd -from utils import HwsimSkip +from utils import HwsimSkip, alloc_fail, fail_test, wait_fail_trigger +from test_ap_ht import clear_scan_cache nr="00112233445500000000510107" lci="01000800101298c0b512926666f6c2f1001c00004104050000c00012" @@ -376,3 +381,442 @@ def _test_rrm_ftm_capa_indication(dev, apdev): if "OK" not in dev[0].request("SET ftm_responder 1"): raise Exception("could not set ftm_responder") dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True) + +class BeaconReport: + def __init__(self, report): + self.opclass, self.channel, self.start, self.duration, self.frame_info, self.rcpi, self.rsni = struct.unpack("= 2: + eid,elen = struct.unpack('BB', report[0:2]) + report = report[2:] + if len(report) < elen: + raise Exception("Invalid subelement in beacon report") + if eid == 1: + # Reported Frame Body + # Contents depends on the reporting detail request: + # 0 = no Reported Frame Body subelement + # 1 = all fixed fields and any elements identified in Request + # element + # 2 = all fixed fields and all elements + # Fixed fields: Timestamp[8] BeaconInt[2] CapabInfo[2] + self.frame_body = report[0:elen] + report = report[elen:] + def __str__(self): + txt = "opclass={} channel={} start={} duration={} frame_info={} rcpi={} rsni={} bssid={} antenna_id={} parent_tsf={}".format(self.opclass, self.channel, self.start, self.duration, self.frame_info, self.rcpi, self.rsni, self.bssid_str, self.antenna_id, self.parent_tsf) + if self.frame_body: + txt += " frame_body=" + binascii.hexlify(self.frame_body) + return txt + +def run_req_beacon(hapd, addr, request): + token = hapd.request("REQ_BEACON " + addr + " " + request) + if "FAIL" in token: + raise Exception("REQ_BEACON failed") + + ev = hapd.wait_event(["BEACON-REQ-TX-STATUS"], timeout=5) + if ev is None: + raise Exception("No TX status event for beacon request received") + fields = ev.split(' ') + if fields[1] != addr: + raise Exception("Unexpected STA address in TX status: " + fields[1]) + if fields[2] != token: + raise Exception("Unexpected dialog token in TX status: " + fields[2] + " (expected " + token + ")") + if fields[3] != "ack=1": + raise Exception("Unexected ACK status in TX status: " + fields[3]) + return token + +def test_rrm_beacon_req_table(dev, apdev): + """Beacon request - beacon table mode""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "another" }) + + tests = [ "REQ_BEACON ", + "REQ_BEACON q", + "REQ_BEACON 11:22:33:44:55:66 1", + "REQ_BEACON 11:22:33:44:55:66 1q", + "REQ_BEACON 11:22:33:44:55:66 11223344556677889900aabbccddeeff" ] + for t in tests: + if "FAIL" not in hapd.request(t): + raise Exception("Invalid command accepted: " + t) + + dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412) + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff") + + for i in range(1, 3): + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report %d response not received" % i) + fields = ev.split(' ') + if fields[1] != addr: + raise Exception("Unexpected STA address in beacon report response: " + fields[1]) + if fields[2] != token: + raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")") + if fields[3] != "00": + raise Exception("Unexpected measurement report mode") + + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + + # Default reporting detail is 2, i.e., all fixed fields and elements. + if not report.frame_body: + raise Exception("Reported Frame Body subelement missing") + if len(report.frame_body) <= 12: + raise Exception("Too short Reported Frame Body subelement") + +def test_rrm_beacon_req_table_detail(dev, apdev): + """Beacon request - beacon table mode - reporting detail""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + logger.info("Reporting Detail 0") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020100") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if report.frame_body: + raise Exception("Reported Frame Body subelement included with Reporting Detail 0") + hapd.dump_monitor() + + logger.info("Reporting Detail 1") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if not report.frame_body: + raise Exception("Reported Frame Body subelement missing") + if len(report.frame_body) != 12: + raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1") + hapd.dump_monitor() + + logger.info("Reporting Detail 2") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020102") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if not report.frame_body: + raise Exception("Reported Frame Body subelement missing") + if len(report.frame_body) <= 12: + raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 2") + hapd.dump_monitor() + + logger.info("Reporting Detail 3 (invalid)") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020103") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response to invalid reporting detail 3") + hapd.dump_monitor() + + logger.info("Reporting Detail (too short)") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0200") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response to invalid reporting detail") + hapd.dump_monitor() + +def test_rrm_beacon_req_table_request(dev, apdev): + """Beacon request - beacon table mode - request element""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if not report.frame_body: + raise Exception("Reported Frame Body subelement missing") + if len(report.frame_body) != 12 + 5 + 10: + raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1 and requested elements SSID + SuppRates") + hapd.dump_monitor() + + logger.info("Incorrect reporting detail with request subelement") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020102" + "0a03000106") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response (invalid reporting detail)") + hapd.dump_monitor() + + logger.info("Invalid request subelement length") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a00") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response (invalid request subelement length)") + hapd.dump_monitor() + + logger.info("Multiple request subelements") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a0100" + "0a0101") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response (multiple request subelements)") + hapd.dump_monitor() + +def test_rrm_beacon_req_table_request_oom(dev, apdev): + """Beacon request - beacon table mode - request element OOM""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + with alloc_fail(dev[0], 1, + "bitfield_alloc;wpas_rm_handle_beacon_req_subelem"): + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106") + wait_fail_trigger(dev[0], "GET_ALLOC_FAIL") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected beacon report response received (OOM)") + + with alloc_fail(dev[0], 1, + "wpabuf_alloc;wpas_rrm_send_msr_report_mpdu"): + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106") + wait_fail_trigger(dev[0], "GET_ALLOC_FAIL") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected beacon report response received (OOM)") + + with fail_test(dev[0], 1, + "wpa_driver_nl80211_send_action;wpas_rrm_send_msr_report_mpdu"): + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106") + wait_fail_trigger(dev[0], "GET_ALLOC_FAIL") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected beacon report response received (OOM)") + + with alloc_fail(dev[0], 1, + "wpabuf_resize;wpas_add_beacon_rep"): + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received (OOM -> empty report)") + fields = ev.split(' ') + if len(fields[4]) > 0: + raise Exception("Unexpected beacon report received") + +def test_rrm_beacon_req_table_bssid(dev, apdev): + """Beacon request - beacon table mode - specific BSSID""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "another" }) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + bssid2 = hapd2.own_addr() + token = run_req_beacon(hapd, addr, "51000000000002" + bssid2.replace(':', '')) + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if "bssid=" + bssid2 not in str(report): + raise Exception("Report for unexpect BSS") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected beacon report response") + +def test_rrm_beacon_req_table_ssid(dev, apdev): + """Beacon request - beacon table mode - specific SSID""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + hapd2 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "another" }) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + bssid2 = hapd2.own_addr() + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0007" + "another".encode('hex')) + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if "bssid=" + bssid2 not in str(report): + raise Exception("Report for unexpect BSS") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected beacon report response") + hapd.dump_monitor() + + logger.info("Wildcard SSID") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0000") + for i in range(2): + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + hapd.dump_monitor() + + logger.info("Too long SSID") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0021" + 33*"00") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response (invalid SSID subelement in request)") + hapd.dump_monitor() + +def test_rrm_beacon_req_table_info(dev, apdev): + """Beacon request - beacon table mode - Reporting Information subelement""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + logger.info("Unsupported reporting information 1") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "01020100") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response (unsupported reporting information 1)") + hapd.dump_monitor() + + logger.info("Invalid reporting information length") + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "010100") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response (invalid reporting information length)") + hapd.dump_monitor() + +def test_rrm_beacon_req_table_unknown_subelem(dev, apdev): + """Beacon request - beacon table mode - unknown subelement""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "330101" + "fe00") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + +def test_rrm_beacon_req_table_truncated_subelem(dev, apdev): + """Beacon request - beacon table mode - Truncated subelement""" + params = { "ssid": "rrm", "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412") + addr = dev[0].own_addr() + + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0001") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2) + if ev is not None: + raise Exception("Unexpected beacon report response (truncated subelement)") + hapd.dump_monitor() + +def test_rrm_beacon_req_table_rsne(dev, apdev): + """Beacon request - beacon table mode - RSNE truncation""" + params = hostapd.wpa2_params(ssid="rrm-rsn", passphrase="12345678") + params["rrm_beacon_report"] = "1" + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].connect("rrm-rsn", psk="12345678", scan_freq="2412") + addr = dev[0].own_addr() + + token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a0130") + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report response not received") + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if not report.frame_body: + raise Exception("Reported Frame Body subelement missing") + if len(report.frame_body) != 12 + 6: + raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1 and requested element RSNE") + if binascii.unhexlify("30040100000f") not in report.frame_body: + raise Exception("Truncated RSNE not found") + +def test_rrm_beacon_req_table_vht(dev, apdev): + """Beacon request - beacon table mode - VHT""" + clear_scan_cache(apdev[0]) + try: + hapd = None + params = { "ssid": "rrm-vht", + "country_code": "FI", + "hw_mode": "a", + "channel": "36", + "ht_capab": "[HT40+]", + "ieee80211n": "1", + "ieee80211ac": "1", + "vht_oper_chwidth": "1", + "vht_oper_centr_freq_seg0_idx": "42", + "rrm_beacon_report": "1" } + hapd = hostapd.add_ap(apdev[0], params) + bssid = apdev[0]['bssid'] + + params = { "ssid": "test-vht40", + "country_code": "FI", + "hw_mode": "a", + "channel": "48", + "ieee80211n": "1", + "ieee80211ac": "1", + "ht_capab": "[HT40-]", + "vht_capab": "", + "vht_oper_chwidth": "0", + "vht_oper_centr_freq_seg0_idx": "0", + } + hapd2 = hostapd.add_ap(apdev[1], params) + + dev[0].scan_for_bss(apdev[1]['bssid'], freq=5240) + dev[0].connect("rrm-vht", key_mgmt="NONE", scan_freq="5180") + + addr = dev[0].own_addr() + + token = run_req_beacon(hapd, addr, "f0000000000002ffffffffffff") + for i in range(2): + ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10) + if ev is None: + raise Exception("Beacon report %d response not received" % i) + fields = ev.split(' ') + report = BeaconReport(binascii.unhexlify(fields[4])) + logger.info("Received beacon report: " + str(report)) + if report.bssid_str == apdev[0]['bssid']: + if report.opclass != 128 or report.channel != 36: + raise Exception("Incorrect opclass/channel for AP0") + elif report.bssid_str == apdev[1]['bssid']: + if report.opclass != 117 or report.channel != 48: + raise Exception("Incorrect opclass/channel for AP1") + except Exception, e: + if isinstance(e, Exception) and str(e) == "AP startup failed": + if not vht_supported(): + raise HwsimSkip("80 MHz channel not supported in regulatory information") + raise + finally: + dev[0].request("DISCONNECT") + if hapd: + hapd.request("DISABLE") + subprocess.call(['iw', 'reg', 'set', '00']) + dev[0].flush_scan_cache()