# Radio measurement
# Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
# Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+# Copyright (c) 2017, Jouni Malinen <j@w1.fi>
#
# 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"
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("<BBQHBBB", report[0:15])
+ report = report[15:]
+ self.bssid = report[0:6]
+ self.bssid_str = "%02x:%02x:%02x:%02x:%02x:%02x" % (struct.unpack('6B', self.bssid))
+ report = report[6:]
+ self.antenna_id, self.parent_tsf = struct.unpack("<BI", report[0:5])
+ report = report[5:]
+ self.subelems = report
+ self.frame_body = None
+ while len(report) >= 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()