1# Test cases for dscp policy 2# Copyright (c) 2021, Jouni Malinen <j@w1.fi> 3# Copyright (c) 2021, The Linux Foundation 4# 5# This software may be distributed under the terms of the BSD license. 6# See README for more details. 7 8import struct 9import time 10import sys 11import socket 12 13import hostapd 14from wpasupplicant import WpaSupplicant 15from utils import * 16 17def register_dscp_req(hapd): 18 type = 0x00d0 19 match = "7e506f9a1a" 20 if "OK" not in hapd.request("REGISTER_FRAME %04x %s" % (type, match)): 21 raise Exception("Could not register frame reception for Vendor specific protected type") 22 23def send_dscp_req(hapd, da, oui_subtype, dialog_token, req_control, qos_ie, 24 truncate=False): 25 type = 0 26 subtype = 13 27 category = 126 28 oui_type = 0x506f9a1a 29 if truncate: 30 req = struct.pack('>BLBB', category, oui_type, oui_subtype, 31 dialog_token) 32 else: 33 req = struct.pack('>BLBBB', category, oui_type, oui_subtype, 34 dialog_token, req_control) 35 if qos_ie: 36 req += qos_ie 37 38 msg = {} 39 msg['fc'] = 0x00d0 40 msg['sa'] = hapd.own_addr() 41 msg['da'] = da 42 msg['bssid'] = hapd.own_addr() 43 msg['type'] = type 44 msg['subtype'] = subtype 45 msg['payload'] = req 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 DSCP Policy Request sent") 51 52def prepare_qos_ie(policy_id, req_type, dscp, start_port=0, end_port=0, 53 frame_classifier=None, frame_class_len=0, domain_name=None): 54 qos_elem_oui_type = 0x229a6f50 55 qos_elem_id = 221 56 57 if policy_id: 58 qos_attr = struct.pack('BBBBB', 2, 3, policy_id, req_type, dscp) 59 qos_attr_len = 5 60 else: 61 qos_attr = 0 62 qos_attr_len = 0 63 64 if start_port and end_port: 65 port_range_attr = struct.pack('>BBHH', 1, 4, start_port, end_port) 66 if qos_attr: 67 qos_attr += port_range_attr 68 else: 69 qos_attr = port_range_attr 70 qos_attr_len += 6 71 72 if frame_classifier and frame_class_len: 73 tclas_attr = struct.pack('>BB%ds' % (len(frame_classifier),), 3, 74 len(frame_classifier), frame_classifier) 75 if qos_attr: 76 qos_attr += tclas_attr 77 else: 78 qos_attr = tclas_attr 79 qos_attr_len += 2 + len(frame_classifier) 80 81 if domain_name: 82 s = bytes(domain_name, 'utf-8') 83 domain_name_attr = struct.pack('>BB%ds' % (len(s),), 4, len(s), s) 84 if qos_attr: 85 qos_attr += domain_name_attr 86 else: 87 qos_attr = domain_name_attr 88 qos_attr_len += 2 + len(s) 89 90 qos_attr_len += 4 91 qos_ie = struct.pack('<BBL', qos_elem_id, qos_attr_len, 92 qos_elem_oui_type) + qos_attr 93 94 return qos_ie 95 96def validate_dscp_req_event(dev, event): 97 ev = dev.wait_event(["CTRL-EVENT-DSCP-POLICY"], timeout=2) 98 if ev is None: 99 raise Exception("No DSCP request reported") 100 if ev != event: 101 raise Exception("Invalid DSCP event received (%s; expected: %s)" % (ev, event)) 102 103def handle_dscp_query(hapd, query): 104 msg = hapd.mgmt_rx() 105 if msg['payload'] != query: 106 raise Exception("Invalid DSCP Query received at AP") 107 108def handle_dscp_response(hapd, response): 109 msg = hapd.mgmt_rx() 110 if msg['payload'] != response: 111 raise Exception("Invalid DSCP Response received at AP") 112 113def ap_sta_connectivity(dev, apdev, params): 114 p = hostapd.wpa2_params(passphrase="12345678") 115 p["wpa_key_mgmt"] = "WPA-PSK" 116 p["ieee80211w"] = "1" 117 p.update(params) 118 hapd = hostapd.add_ap(apdev[0], p) 119 register_dscp_req(hapd) 120 121 dev[0].request("SET enable_dscp_policy_capa 1") 122 dev[0].connect("dscp", psk="12345678", ieee80211w="1", 123 key_mgmt="WPA-PSK WPA-PSK-SHA256", scan_freq="2412") 124 hapd.wait_sta() 125 126 hapd.dump_monitor() 127 hapd.set("ext_mgmt_frame_handling", "1") 128 return hapd 129 130def test_dscp_query(dev, apdev): 131 """DSCP Policy Query""" 132 133 # Positive tests 134 #AP with DSCP Capabilities 135 params = {"ssid": "dscp", 136 "ext_capa": 6*"00" + "40", 137 "assocresp_elements": "dd06506f9a230101", 138 "vendor_elements": "dd06506f9a230101"} 139 140 hapd = ap_sta_connectivity(dev, apdev, params) 141 da = dev[0].own_addr() 142 143 # Query 1 144 cmd = "DSCP_QUERY wildcard" 145 if "OK" not in dev[0].request(cmd): 146 raise Exception("Sending DSCP Query failed") 147 query = b'\x7e\x50\x6f\x9a\x1a\x00\x01' 148 handle_dscp_query(hapd, query) 149 150 # Query 2 151 cmd = "DSCP_QUERY domain_name=example.com" 152 if "OK" not in dev[0].request(cmd): 153 raise Exception("Sending DSCP Query failed") 154 query = b'\x7e\x50\x6f\x9a\x1a\x00\x02\xdd\x11\x50\x6f\x9a\x22\x04\x0b\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d' 155 handle_dscp_query(hapd, query) 156 157 # Negative tests 158 159 cmd = "DSCP_QUERY domain_name=" + 250*'a' + ".example.com" 160 if "FAIL" not in dev[0].request(cmd): 161 raise Exception("Invalid DSCP_QUERY accepted") 162 163 dev[0].disconnect_and_stop_scan() 164 # AP without DSCP Capabilities 165 params = {"ssid": "dscp", 166 "ext_capa": 6*"00" + "40"} 167 hapd = ap_sta_connectivity(dev, apdev, params) 168 169 # Query 3 170 cmd = "DSCP_QUERY wildcard" 171 if "FAIL" not in dev[0].request(cmd): 172 raise Exception("Able to send invalid DSCP Query") 173 174def test_dscp_request(dev, apdev): 175 """DSCP Policy Request""" 176 177 # Positive tests 178 179 #AP with DSCP Capabilities 180 params = {"ssid": "dscp", 181 "ext_capa": 6*"00" + "40", 182 "assocresp_elements": "dd06506f9a230101", 183 "vendor_elements": "dd06506f9a230101"} 184 185 hapd = ap_sta_connectivity(dev, apdev, params) 186 da = dev[0].own_addr() 187 188 # Request 1 189 dialog_token = 5 190 send_dscp_req(hapd, da, 1, dialog_token, 2, 0) 191 event = "<3>CTRL-EVENT-DSCP-POLICY request_start clear_all" 192 validate_dscp_req_event(dev[0], event) 193 event = "<3>CTRL-EVENT-DSCP-POLICY request_end" 194 validate_dscp_req_event(dev[0], event) 195 196 # DSCP Request with multiple QoS IEs 197 # QoS IE 1 198 dialog_token = 1 199 domain_name = "example.com" 200 ipv4_src_addr = socket.inet_pton(socket.AF_INET, "192.168.0.1") 201 ipv4_dest_addr = socket.inet_pton(socket.AF_INET, "192.168.0.2") 202 frame_classifier_start = [4, 91, 4] 203 frame_classifier_end = [12, 34, 12, 34, 0, 17, 0] 204 frame_classifier = bytes(frame_classifier_start) + ipv4_src_addr + ipv4_dest_addr + bytes(frame_classifier_end) 205 frame_len = len(frame_classifier) 206 qos_ie = prepare_qos_ie(1, 0, 22, 0, 0, frame_classifier, frame_len, domain_name) 207 208 # QoS IE 2 209 ipv6_src_addr = socket.inet_pton(socket.AF_INET6, "aaaa:bbbb:cccc::1") 210 ipv6_dest_addr = socket.inet_pton(socket.AF_INET6, "aaaa:bbbb:cccc::2") 211 frame_classifier_start = [4, 79, 6] 212 frame_classifier_end = [0, 12, 34, 0, 0, 17, 0, 0, 0] 213 frame_classifier = bytes(frame_classifier_start) + ipv6_src_addr + ipv6_dest_addr + bytes(frame_classifier_end) 214 frame_len = len(frame_classifier) 215 ie = prepare_qos_ie(5, 0, 48, 12345, 23456, frame_classifier, frame_len, 216 None) 217 qos_ie += ie 218 219 # QoS IE 3 220 ie = prepare_qos_ie(4, 0, 32, 12345, 23456, 0, 0, domain_name) 221 qos_ie += ie 222 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 223 224 event = "<3>CTRL-EVENT-DSCP-POLICY request_start" 225 validate_dscp_req_event(dev[0], event) 226 event = "<3>CTRL-EVENT-DSCP-POLICY add policy_id=1 dscp=22 ip_version=4 src_ip=192.168.0.1 src_port=3106 dst_port=3106 protocol=17 domain_name=example.com" 227 validate_dscp_req_event(dev[0], event) 228 event = "<3>CTRL-EVENT-DSCP-POLICY add policy_id=5 dscp=48 ip_version=6 src_ip=aaaa:bbbb:cccc::1 dst_ip=aaaa:bbbb:cccc::2 src_port=12 protocol=17 start_port=12345 end_port=23456" 229 validate_dscp_req_event(dev[0], event) 230 event = "<3>CTRL-EVENT-DSCP-POLICY add policy_id=4 dscp=32 ip_version=0 start_port=12345 end_port=23456 domain_name=example.com" 231 validate_dscp_req_event(dev[0], event) 232 event = "<3>CTRL-EVENT-DSCP-POLICY request_end" 233 validate_dscp_req_event(dev[0], event) 234 235 # Negative Tests 236 237 # No DSCP policy attribute 238 dialog_token = 4 239 domain_name = "example.com" 240 qos_ie = prepare_qos_ie(0, 0, 0, 12345, 23456, frame_classifier, frame_len, 241 domain_name) 242 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 243 event = "<3>CTRL-EVENT-DSCP-POLICY request_start" 244 validate_dscp_req_event(dev[0], event) 245 event = "<3>CTRL-EVENT-DSCP-POLICY request_end" 246 validate_dscp_req_event(dev[0], event) 247 248 # No DSCP stream classifier params 249 dialog_token = 6 250 qos_ie = prepare_qos_ie(1, 0, 32, 0, 0, 0, 0, None) 251 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 252 event = "<3>CTRL-EVENT-DSCP-POLICY request_start" 253 validate_dscp_req_event(dev[0], event) 254 event = "<3>CTRL-EVENT-DSCP-POLICY reject policy_id=1" 255 validate_dscp_req_event(dev[0], event) 256 event = "<3>CTRL-EVENT-DSCP-POLICY request_end" 257 validate_dscp_req_event(dev[0], event) 258 259 # DSCP request with both destination and domain name 260 dialog_token = 7 261 domain_name = "example.com" 262 ipv4_src_addr = socket.inet_pton(socket.AF_INET, "192.168.0.1") 263 ipv4_dest_addr = socket.inet_pton(socket.AF_INET, "192.168.0.2") 264 frame_classifier_start = [4, 69, 4] 265 frame_classifier_end = [0, 0, 0, 0, 0, 17, 0] 266 frame_classifier = bytes(frame_classifier_start) + ipv4_src_addr + ipv4_dest_addr + bytes(frame_classifier_end) 267 frame_len = len(frame_classifier) 268 qos_ie = prepare_qos_ie(1, 0, 36, 0, 0, frame_classifier, frame_len, 269 domain_name) 270 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 271 event = "<3>CTRL-EVENT-DSCP-POLICY request_start" 272 validate_dscp_req_event(dev[0], event) 273 event = "<3>CTRL-EVENT-DSCP-POLICY reject policy_id=1" 274 validate_dscp_req_event(dev[0], event) 275 event = "<3>CTRL-EVENT-DSCP-POLICY request_end" 276 validate_dscp_req_event(dev[0], event) 277 278 # DSCP request with both port range and destination port 279 frame_classifier_start = [4, 81, 4] 280 frame_classifier_end = [0, 0, 23, 45, 0, 17, 0] 281 frame_classifier = bytes(frame_classifier_start) + ipv4_src_addr + ipv4_dest_addr + bytes(frame_classifier_end) 282 frame_len = len(frame_classifier) 283 qos_ie = prepare_qos_ie(1, 0, 36, 12345, 23456, frame_classifier, frame_len, 284 None) 285 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 286 event = "<3>CTRL-EVENT-DSCP-POLICY request_start" 287 validate_dscp_req_event(dev[0], event) 288 event = "<3>CTRL-EVENT-DSCP-POLICY reject policy_id=1" 289 validate_dscp_req_event(dev[0], event) 290 event = "<3>CTRL-EVENT-DSCP-POLICY request_end" 291 validate_dscp_req_event(dev[0], event) 292 293 # Too short DSCP Policy Request frame 294 dialog_token += 1 295 send_dscp_req(hapd, da, 1, dialog_token, 0, None, truncate=True) 296 297 # Request Type: Remove 298 dialog_token += 1 299 qos_ie = prepare_qos_ie(1, 1, 36) 300 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 301 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY request_start") 302 validate_dscp_req_event(dev[0], 303 "<3>CTRL-EVENT-DSCP-POLICY remove policy_id=1") 304 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY request_end") 305 306 # Request Type: Reserved 307 dialog_token += 1 308 qos_ie = prepare_qos_ie(1, 2, 36) 309 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 310 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY request_start") 311 validate_dscp_req_event(dev[0], 312 "<3>CTRL-EVENT-DSCP-POLICY reject policy_id=1") 313 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY request_end") 314 315def test_dscp_response(dev, apdev): 316 """DSCP Policy Response""" 317 318 # Positive tests 319 320 # AP with DSCP Capabilities 321 params = {"ssid": "dscp", 322 "ext_capa": 6*"00" + "40", 323 "assocresp_elements": "dd06506f9a230101", 324 "vendor_elements": "dd06506f9a230101"} 325 hapd = ap_sta_connectivity(dev, apdev, params) 326 da = dev[0].own_addr() 327 328 # Sending solicited DSCP response after receiving DSCP request 329 dialog_token = 1 330 domain_name = "example.com" 331 ipv4_src_addr = socket.inet_pton(socket.AF_INET, "192.168.0.1") 332 ipv4_dest_addr = socket.inet_pton(socket.AF_INET, "192.168.0.2") 333 frame_classifier_start = [4,91,4] 334 frame_classifier_end = [12,34,12,34,0,17,0] 335 frame_classifier = bytes(frame_classifier_start) + ipv4_src_addr + ipv4_dest_addr + bytes(frame_classifier_end) 336 frame_len = len(frame_classifier) 337 qos_ie = prepare_qos_ie(1, 0, 22, 0, 0, frame_classifier, frame_len, 338 domain_name) 339 ie = prepare_qos_ie(4, 0, 32, 12345, 23456, 0, 0, domain_name) 340 qos_ie += ie 341 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 342 343 ev = dev[0].wait_event(["CTRL-EVENT-DSCP-POLICY"], timeout=5) 344 if ev is None: 345 raise Exception("DSCP event not reported") 346 if "request_start" not in ev: 347 raise Exception("Unexpected DSCP event: " + ev) 348 cmd = "DSCP_RESP solicited policy_id=1 status=0 policy_id=4 status=0" 349 if "OK" not in dev[0].request(cmd): 350 raise Exception("Sending DSCP Response failed") 351 response = b'\x7e\x50\x6f\x9a\x1a\x02\x01\x00\x02\x01\x00\x04\x00' 352 handle_dscp_response(hapd, response) 353 354 # Unsolicited DSCP Response without status duples 355 cmd = "DSCP_RESP reset more" 356 if "OK" not in dev[0].request(cmd): 357 raise Exception("Sending DSCP Response failed") 358 response = b'\x7e\x50\x6f\x9a\x1a\x02\x00\x03\x00' 359 handle_dscp_response(hapd, response) 360 361 # Unsolicited DSCP Response with one status duple 362 cmd = "DSCP_RESP policy_id=2 status=0" 363 if "OK" not in dev[0].request(cmd): 364 raise Exception("Sending DSCP Response failed") 365 response = b'\x7e\x50\x6f\x9a\x1a\x02\x00\x00\x01\x02\x00' 366 handle_dscp_response(hapd, response) 367 368 # Negative tests 369 370 # Send solicited DSCP Response without prior DSCP request 371 cmd = "DSCP_RESP solicited policy_id=1 status=0 policy_id=5 status=0" 372 if "FAIL" not in dev[0].request(cmd): 373 raise Exception("Able to send invalid DSCP response") 374 375def test_dscp_unsolicited_req_at_assoc(dev, apdev): 376 """DSCP Policy and unsolicited request at association""" 377 params = {"ssid": "dscp", 378 "ext_capa": 6*"00" + "40", 379 "assocresp_elements": "dd06506f9a230103", 380 "vendor_elements": "dd06506f9a230103"} 381 hapd = ap_sta_connectivity(dev, apdev, params) 382 da = dev[0].own_addr() 383 384 dialog_token = 1 385 qos_ie = prepare_qos_ie(1, 0, 36, 12345, 23456) 386 send_dscp_req(hapd, da, 1, dialog_token, 0, qos_ie) 387 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY request_start") 388 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY add policy_id=1 dscp=36 ip_version=0 start_port=12345 end_port=23456") 389 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY request_end") 390 391 cmd = "DSCP_QUERY wildcard" 392 if "OK" not in dev[0].request(cmd): 393 raise Exception("Sending DSCP Query failed") 394 395def test_dscp_missing_unsolicited_req_at_assoc(dev, apdev): 396 """DSCP Policy and missing unsolicited request at association""" 397 params = {"ssid": "dscp", 398 "ext_capa": 6*"00" + "40", 399 "assocresp_elements": "dd06506f9a230103", 400 "vendor_elements": "dd06506f9a230103"} 401 hapd = ap_sta_connectivity(dev, apdev, params) 402 da = dev[0].own_addr() 403 404 cmd = "DSCP_QUERY wildcard" 405 if "FAIL" not in dev[0].request(cmd): 406 raise Exception("DSCP_QUERY accepted during wait for unsolicited requesdt") 407 time.sleep(5) 408 validate_dscp_req_event(dev[0], "<3>CTRL-EVENT-DSCP-POLICY request_wait end") 409 410 cmd = "DSCP_QUERY wildcard" 411 if "OK" not in dev[0].request(cmd): 412 raise Exception("Sending DSCP Query failed") 413