1# Test cases for MSCS 2# Copyright (c) 2021, Jouni Malinen <j@w1.fi> 3# 4# This software may be distributed under the terms of the BSD license. 5# See README for more details. 6 7import struct 8import time 9 10import hostapd 11from utils import * 12 13def register_mcsc_req(hapd): 14 type = 0x00d0 15 match = "1304" 16 if "OK" not in hapd.request("REGISTER_FRAME %04x %s" % (type, match)): 17 raise Exception("Could not register frame reception for Robust AV Streaming") 18 19def fill_change_params(dialog_token, status_code, params): 20 req_type = params['req_type'] if 'req_type' in params else 2 21 return struct.pack('<BBBHBBBBBBI', 19, 5, dialog_token, status_code, 22 255, 8, 88, req_type, params['up_bitmap'], 23 params['up_limit'], params['stream_timeout']) 24 25def handle_mscs_req(hapd, wrong_dialog=False, status_code=0, 26 change_params=None): 27 msg = hapd.mgmt_rx() 28 if msg['subtype'] != 13: 29 logger.info("RX:" + str(msg)) 30 raise Exception("Received unexpected Management frame") 31 categ, act, dialog_token = struct.unpack('BBB', msg['payload'][0:3]) 32 if categ != 19 or act != 4: 33 logger.info("RX:" + str(msg)) 34 raise Exception("Received unexpected Action frame") 35 36 if wrong_dialog: 37 dialog_token = (dialog_token + 1) % 256 38 msg['da'] = msg['sa'] 39 msg['sa'] = hapd.own_addr() 40 41 if change_params is not None: 42 msg['payload'] = fill_change_params(dialog_token, status_code, 43 change_params) 44 else: 45 msg['payload'] = struct.pack('<BBBH', 19, 5, dialog_token, status_code) 46 47 hapd.mgmt_tx(msg) 48 ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5) 49 if ev is None or "stype=13 ok=1" not in ev: 50 raise Exception("No TX status reported") 51 52def wait_mscs_result(dev, expect_status=0, change_params=None): 53 ev = dev.wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1) 54 if ev is None: 55 raise Exception("No MSCS result reported") 56 if "status_code=%d" % expect_status not in ev: 57 raise Exception("Unexpected MSCS result: " + ev) 58 if change_params is None and 'change' in ev: 59 raise Exception("Unexpected 'change' in MSCS result: " + ev) 60 if change_params is not None and \ 61 ("change" not in ev or 62 "up_limit=%d" % change_params['up_limit'] not in ev or 63 "up_bitmap=%d" % change_params['up_bitmap'] not in ev or 64 "stream_timeout=%d" % change_params['stream_timeout'] not in ev): 65 raise Exception("Unexpected MSCS result: " + ev) 66 67def add_mscs_ap(apdev, reg_mscs_req=True, mscs_supported=True, 68 assocresp_elements=None): 69 params = {"ssid": "mscs"} 70 71 if mscs_supported: 72 params["ext_capa"] = 10*"00" + "20" 73 else: 74 params["ext_capa_mask"] = 10*"00" + "20" 75 76 if assocresp_elements is not None: 77 params['assocresp_elements'] = assocresp_elements 78 79 hapd = hostapd.add_ap(apdev, params) 80 81 if reg_mscs_req: 82 register_mcsc_req(hapd) 83 84 return hapd 85 86def test_mscs_invalid_params(dev, apdev): 87 """MSCS command invalid parameters""" 88 tests = ["", 89 "add Xp_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F", 90 "add up_bitmap=F0 Xp_limit=7 stream_timeout=12345 frame_classifier=045F", 91 "add up_bitmap=F0 up_limit=7 Xtream_timeout=12345 frame_classifier=045F", 92 "add up_bitmap=F0 up_limit=7 stream_timeout=12345 Xrame_classifier=045F", 93 "add up_bitmap=X0 up_limit=7 stream_timeout=12345 frame_classifier=045F", 94 "add up_bitmap=F0 up_limit=7 stream_timeout=0 frame_classifier=045F", 95 "add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=X45F", 96 "change "] 97 for t in tests: 98 if "FAIL" not in dev[0].request("MSCS " + t): 99 raise Exception("Invalid MSCS parameters accepted: " + t) 100 101def mscs_run(dev, apdev, func): 102 try: 103 func(dev, apdev) 104 finally: 105 dev[0].request("MSCS remove") 106 107def test_mscs_without_ap_support(dev, apdev): 108 """MSCS without AP support""" 109 mscs_run(dev, apdev, run_mscs_without_ap_support) 110 111def run_mscs_without_ap_support(dev, apdev): 112 add_mscs_ap(apdev[0], reg_mscs_req=False, mscs_supported=False) 113 114 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 115 if "OK" not in dev[0].request(cmd): 116 raise Exception("Failed to configure MSCS") 117 118 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412") 119 120 cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 121 if "FAIL" not in dev[0].request(cmd): 122 raise Exception("MSCS change accepted unexpectedly") 123 124 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 125 if "FAIL" not in dev[0].request(cmd): 126 raise Exception("MSCS add accepted unexpectedly") 127 128def test_mscs_post_assoc(dev, apdev): 129 """MSCS configuration post-association""" 130 mscs_run(dev, apdev, run_mscs_post_assoc) 131 132def run_mscs_post_assoc(dev, apdev): 133 hapd = add_mscs_ap(apdev[0]) 134 135 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412") 136 137 hapd.dump_monitor() 138 hapd.set("ext_mgmt_frame_handling", "1") 139 140 cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 141 if "FAIL" not in dev[0].request(cmd): 142 raise Exception("MSCS change accepted unexpectedly") 143 144 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 145 if "OK" not in dev[0].request(cmd): 146 raise Exception("MSCS add failed") 147 148 handle_mscs_req(hapd) 149 wait_mscs_result(dev[0]) 150 151 cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 152 if "OK" not in dev[0].request(cmd): 153 raise Exception("MSCS change failed") 154 155 handle_mscs_req(hapd) 156 wait_mscs_result(dev[0]) 157 158 cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 159 if "OK" not in dev[0].request(cmd): 160 raise Exception("MSCS change failed") 161 162 handle_mscs_req(hapd, status_code=23456) 163 wait_mscs_result(dev[0], expect_status=23456) 164 165def test_mscs_pre_assoc(dev, apdev): 166 """MSCS configuration pre-association""" 167 mscs_run(dev, apdev, run_mscs_pre_assoc) 168 169def run_mscs_pre_assoc(dev, apdev): 170 ies = "ff0c5800000000000000" + "01020000" 171 hapd = add_mscs_ap(apdev[0], assocresp_elements=ies) 172 173 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 174 if "OK" not in dev[0].request(cmd): 175 raise Exception("MSCS add failed") 176 177 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412", 178 wait_connect=False) 179 wait_mscs_result(dev[0]) 180 dev[0].wait_connected() 181 182 hapd.set("ext_mgmt_frame_handling", "1") 183 184 cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 185 if "OK" not in dev[0].request(cmd): 186 raise Exception("MSCS change failed") 187 188 handle_mscs_req(hapd) 189 wait_mscs_result(dev[0]) 190 191 cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 192 if "OK" not in dev[0].request(cmd): 193 raise Exception("MSCS change failed") 194 195 handle_mscs_req(hapd, wrong_dialog=True) 196 197 ev = dev[0].wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1) 198 if ev is not None: 199 raise Exception("Unexpected MSCS result reported") 200 201def test_mscs_assoc_failure(dev, apdev): 202 """MSCS configuration failure during association exchange""" 203 mscs_run(dev, apdev, run_mscs_assoc_failure) 204 205def run_mscs_assoc_failure(dev, apdev): 206 ies = "ff0c5800000000000000" + "01020001" 207 hapd = add_mscs_ap(apdev[0], assocresp_elements=ies) 208 209 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 210 if "OK" not in dev[0].request(cmd): 211 raise Exception("MSCS add failed") 212 213 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412", 214 wait_connect=False) 215 wait_mscs_result(dev[0], expect_status=256) 216 dev[0].wait_connected() 217 dev[0].request("REMOVE_NETWORK all") 218 dev[0].wait_disconnected() 219 220 hapd.dump_monitor() 221 # No MSCS Status subelement 222 hapd.set("assocresp_elements", "ff085800000000000000") 223 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412", 224 wait_connect=False) 225 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-MSCS-RESULT"], 226 timeout=10) 227 if ev is None: 228 raise Exception("No connection event") 229 if "CTRL-EVENT-MSCS-RESULT" in ev: 230 raise Exception("Unexpected MSCS result") 231 232def test_mscs_local_errors(dev, apdev): 233 """MSCS configuration local errors""" 234 mscs_run(dev, apdev, run_mscs_local_errors) 235 236def run_mscs_local_errors(dev, apdev): 237 hapd = add_mscs_ap(apdev[0]) 238 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412") 239 240 hapd.dump_monitor() 241 hapd.set("ext_mgmt_frame_handling", "1") 242 243 for count in range(1, 3): 244 with alloc_fail(dev[0], count, "wpas_send_mscs_req"): 245 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 246 if "FAIL" not in dev[0].request(cmd): 247 raise Exception("MSCS add succeeded in error case") 248 249def test_mscs_assoc_change_response(dev, apdev): 250 """MSCS configuration failure during assoc - AP response with change request""" 251 mscs_run(dev, apdev, run_mscs_assoc_change_response) 252 253def run_mscs_assoc_change_response(dev, apdev): 254 ies = "ff0c5802060701000000" + "01020001" 255 hapd = add_mscs_ap(apdev[0], assocresp_elements=ies) 256 257 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 258 if "OK" not in dev[0].request(cmd): 259 raise Exception("MSCS add failed") 260 261 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412", 262 wait_connect=False) 263 264 # Based on the Association Response elements above 265 change_params = { 266 'up_bitmap': 6, 267 'up_limit': 7, 268 'stream_timeout': 1 269 } 270 wait_mscs_result(dev[0], expect_status=256, change_params=change_params) 271 dev[0].wait_connected() 272 273 hapd.dump_monitor() 274 hapd.set("ext_mgmt_frame_handling", "1") 275 276 # Verify we're able to send a new MSCS Request frame. 277 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345` frame_classifier=045F" 278 if "OK" not in dev[0].request(cmd): 279 raise Exception("MSCS add failed") 280 281 handle_mscs_req(hapd) 282 wait_mscs_result(dev[0]) 283 284def test_mscs_post_assoc_change_response(dev, apdev): 285 """MSCS configuration failure post assoc - AP response with MSCS Response frame change request""" 286 mscs_run(dev, apdev, run_mscs_post_assoc_change_response) 287 288def run_mscs_post_assoc_change_response(dev, apdev): 289 hapd = add_mscs_ap(apdev[0]) 290 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412") 291 292 hapd.dump_monitor() 293 hapd.set("ext_mgmt_frame_handling", "1") 294 295 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 296 if "OK" not in dev[0].request(cmd): 297 raise Exception("MSCS add failed") 298 299 change_params = { 300 'up_bitmap': 6, 301 'up_limit': 7, 302 'stream_timeout': 1 303 } 304 305 handle_mscs_req(hapd, status_code=256, change_params=change_params) 306 wait_mscs_result(dev[0], expect_status=256, change_params=change_params) 307 308 # Verify we're able to send new valid MSCS request 309 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 310 if "OK" not in dev[0].request(cmd): 311 raise Exception("MSCS add failed") 312 313 handle_mscs_req(hapd) 314 wait_mscs_result(dev[0]) 315 316def test_mscs_invalid_req_type_response(dev, apdev): 317 """MSCS AP response with invalid request type""" 318 mscs_run(dev, apdev, run_mscs_invalid_req_type_response) 319 320def run_mscs_invalid_req_type_response(dev, apdev): 321 hapd = add_mscs_ap(apdev[0]) 322 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412") 323 324 hapd.dump_monitor() 325 hapd.set("ext_mgmt_frame_handling", "1") 326 327 change_params = { 328 'up_bitmap': 6, 329 'up_limit': 7, 330 'stream_timeout': 1 331 } 332 333 # Verify frames with invalid MSCS decriptor request type dropped 334 for req_type in [3, 4, 10]: 335 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 336 if "OK" not in dev[0].request(cmd): 337 raise Exception("MSCS add failed") 338 339 change_params['req_type'] = req_type 340 handle_mscs_req(hapd, status_code=256, change_params=change_params) 341 ev = dev[0].wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1) 342 if ev is not None: 343 raise Exception("Unexpected MSCS result reported") 344 345def send_unsolicited_mscs_response(dev, hapd, status_code, params=None, 346 wrong_dialog=False): 347 dialog_token = 0 if not wrong_dialog else 10 348 if params is not None: 349 payload = fill_change_params(dialog_token, status_code, params) 350 else: 351 payload = struct.pack('<BBBH', 19, 5, dialog_token, status_code) 352 353 msg = { 354 'fc': 0xd0, 355 'sa': hapd.own_addr(), 356 'da': dev.own_addr(), 357 'bssid': hapd.own_addr(), 358 'payload': payload, 359 } 360 361 hapd.mgmt_tx(msg) 362 ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5) 363 if ev is None or "stype=13 ok=1" not in ev: 364 raise Exception("No TX status reported") 365 366def test_mscs_unsolicited_response(dev, apdev): 367 """MSCS configured and AP sends unsolicited response to terminate / change the session""" 368 mscs_run(dev, apdev, run_mscs_unsolicited_response) 369 370def run_mscs_unsolicited_response(dev, apdev): 371 hapd = add_mscs_ap(apdev[0]) 372 dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412") 373 374 hapd.dump_monitor() 375 hapd.set("ext_mgmt_frame_handling", "1") 376 377 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 378 if "OK" not in dev[0].request(cmd): 379 raise Exception("MSCS add failed") 380 381 handle_mscs_req(hapd) 382 wait_mscs_result(dev[0]) 383 384 status_code = 256 385 386 # Verify unsolicited response with wrong dialog token (othar than 0) is 387 # dropped 388 send_unsolicited_mscs_response(dev[0], hapd, status_code=status_code, 389 wrong_dialog=True) 390 ev = dev[0].wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1) 391 if ev is not None: 392 raise Exception("Unexpected MSCS result reported") 393 394 # Verify unsolicited response without change request (termination) 395 send_unsolicited_mscs_response(dev[0], hapd, status_code=status_code) 396 wait_mscs_result(dev[0], expect_status=status_code) 397 398 # Send new MSCS request 399 cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F" 400 if "OK" not in dev[0].request(cmd): 401 raise Exception("MSCS add failed") 402 403 handle_mscs_req(hapd) 404 wait_mscs_result(dev[0]) 405 406 params = { 407 'up_bitmap': 6, 408 'up_limit': 7, 409 'stream_timeout': 1 410 } 411 412 # Verify unsolicited response handling with change request 413 send_unsolicited_mscs_response(dev[0], hapd, status_code=status_code, 414 params=params) 415 wait_mscs_result(dev[0], expect_status=status_code, change_params=params) 416