1# Test cases for wpa_supplicant WMM-AC operations
2# Copyright (c) 2014, Intel Corporation
3#
4# This software may be distributed under the terms of the BSD license.
5# See README for more details.
6
7from remotehost import remote_compatible
8import logging
9logger = logging.getLogger()
10import struct
11import sys
12
13import hwsim_utils
14import hostapd
15from utils import fail_test
16
17def add_wmm_ap(apdev, acm_list):
18    params = {"ssid": "wmm_ac",
19              "hw_mode": "g",
20              "channel": "11",
21              "wmm_enabled": "1"}
22
23    for ac in acm_list:
24        params["wmm_ac_%s_acm" % (ac.lower())] = "1"
25
26    return hostapd.add_ap(apdev, params)
27
28def test_tspec(dev, apdev):
29    """Basic addts/delts tests"""
30    # configure ap with VO and VI requiring admission-control
31    hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
32    dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
33    hwsim_utils.test_connectivity(dev[0], hapd)
34    status = dev[0].request("WMM_AC_STATUS")
35    if "WMM AC is Enabled" not in status:
36        raise Exception("WMM-AC not enabled")
37    if "TSID" in status:
38        raise Exception("Unexpected TSID info")
39    if "BK: acm=0 uapsd=0" not in status:
40        raise Exception("Unexpected BK info" + status)
41    if "BE: acm=0 uapsd=0" not in status:
42        raise Exception("Unexpected BE info" + status)
43    if "VI: acm=1 uapsd=0" not in status:
44        raise Exception("Unexpected VI info" + status)
45    if "VO: acm=1 uapsd=0" not in status:
46        raise Exception("Unexpected VO info" + status)
47
48    # no tsid --> tsid out of range
49    if "FAIL" not in dev[0].request("WMM_AC_ADDTS downlink"):
50        raise Exception("Invalid WMM_AC_ADDTS accepted")
51    # no direction
52    if "FAIL" not in dev[0].request("WMM_AC_ADDTS tsid=5"):
53        raise Exception("Invalid WMM_AC_ADDTS accepted")
54    # param out of range
55    if "FAIL" not in dev[0].request("WMM_AC_ADDTS tsid=5 downlink"):
56        raise Exception("Invalid WMM_AC_ADDTS accepted")
57
58    tsid = 5
59
60    # make sure we fail when the ac is not configured for acm
61    try:
62        dev[0].add_ts(tsid, 3)
63        raise Exception("ADDTS succeeded although it should have failed")
64    except Exception as e:
65        if not str(e).startswith("ADDTS failed"):
66            raise
67    status = dev[0].request("WMM_AC_STATUS")
68    if "TSID" in status:
69        raise Exception("Unexpected TSID info")
70
71    # add tspec for UP=6
72    dev[0].add_ts(tsid, 6)
73    status = dev[0].request("WMM_AC_STATUS")
74    if "TSID" not in status:
75        raise Exception("Missing TSID info")
76
77    # using the same tsid for a different ac is invalid
78    try:
79        dev[0].add_ts(tsid, 5)
80        raise Exception("ADDTS succeeded although it should have failed")
81    except Exception as e:
82        if not str(e).startswith("ADDTS failed"):
83            raise
84
85    # update the tspec for a different UP of the same ac
86    dev[0].add_ts(tsid, 7, extra="fixed_nominal_msdu")
87    dev[0].del_ts(tsid)
88    status = dev[0].request("WMM_AC_STATUS")
89    if "TSID" in status:
90        raise Exception("Unexpected TSID info")
91
92    # verify failure on uplink/bidi without driver support
93    tsid = 6
94    try:
95        dev[0].add_ts(tsid, 7, direction="uplink")
96        raise Exception("ADDTS succeeded although it should have failed")
97    except Exception as e:
98        if not str(e).startswith("ADDTS failed"):
99            raise
100    try:
101        dev[0].add_ts(tsid, 7, direction="bidi")
102        raise Exception("ADDTS succeeded although it should have failed")
103    except Exception as e:
104        if not str(e).startswith("ADDTS failed"):
105            raise
106
107    # attempt to delete non-existing tsid
108    try:
109        dev[0].del_ts(tsid)
110        raise Exception("DELTS succeeded although it should have failed")
111    except Exception as e:
112        if not str(e).startswith("DELTS failed"):
113            raise
114
115    # "CTRL: Invalid WMM_AC_ADDTS parameter: 'foo'
116    if "FAIL" not in dev[0].request("WMM_AC_ADDTS foo"):
117        raise Exception("Invalid WMM_AC_ADDTS command accepted")
118
119def test_tspec_protocol(dev, apdev):
120    """Protocol tests for addts/delts"""
121    # configure ap with VO and VI requiring admission-control
122    hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
123    dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
124
125    dev[0].dump_monitor()
126    hapd.set("ext_mgmt_frame_handling", "1")
127
128    tsid = 6
129
130    # timeout on ADDTS response
131    dev[0].add_ts(tsid, 7, expect_failure=True)
132
133    hapd.dump_monitor()
134    req = "WMM_AC_ADDTS downlink tsid=6 up=7 nominal_msdu_size=1500 sba=9000 mean_data_rate=1500 min_phy_rate=6000000"
135    if "OK" not in dev[0].request(req):
136        raise Exception("WMM_AC_ADDTS failed")
137    # a new request while previous is still pending
138    if "FAIL" not in dev[0].request(req):
139        raise Exception("WMM_AC_ADDTS accepted while oen was still pending")
140    msg = hapd.mgmt_rx()
141    payload = msg['payload']
142    (categ, action, dialog, status) = struct.unpack('BBBB', payload[0:4])
143    if action != 0:
144        raise Exception("Unexpected Action code: %d" % action)
145
146    msg['da'] = msg['sa']
147    msg['sa'] = apdev[0]['bssid']
148
149    # unexpected dialog token
150    msg['payload'] = struct.pack('BBBB', 17, 1, (dialog + 1) & 0xff, 0) + payload[4:]
151    hapd.mgmt_tx(msg)
152
153    # valid response
154    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:]
155    hapd.mgmt_tx(msg)
156    ev = dev[0].wait_event(["TSPEC-ADDED"], timeout=10)
157    if ev is None:
158        raise Exception("Timeout on TSPEC-ADDED")
159    if "tsid=%d" % tsid not in ev:
160        raise Exception("Unexpected TSPEC-ADDED contents: " + ev)
161
162    # duplicated response
163    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:]
164    hapd.mgmt_tx(msg)
165
166    # too short ADDTS
167    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0)
168    hapd.mgmt_tx(msg)
169
170    # invalid IE
171    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:] + struct.pack('BB', 0xdd, 100)
172    hapd.mgmt_tx(msg)
173
174    # too short WMM element
175    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:] + b'\xdd\x06\x00\x50\xf2\x02\x02\x01'
176    hapd.mgmt_tx(msg)
177
178    # DELTS
179    dev[0].dump_monitor()
180    msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0) + payload[4:]
181    hapd.mgmt_tx(msg)
182    ev = dev[0].wait_event(['TSPEC-REMOVED'], timeout=6)
183    if ev is None:
184        raise Exception("Timeout on TSPEC-REMOVED event")
185    if "tsid=%d" % tsid not in ev:
186        raise Exception("Unexpected TSPEC-REMOVED contents: " + ev)
187    # DELTS duplicated
188    msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0) + payload[4:]
189    hapd.mgmt_tx(msg)
190
191    # start a new request
192    hapd.dump_monitor()
193    if "OK" not in dev[0].request(req):
194        raise Exception("WMM_AC_ADDTS failed")
195    msg = hapd.mgmt_rx()
196    payload = msg['payload']
197    (categ, action, dialog, status) = struct.unpack('BBBB', payload[0:4])
198    if action != 0:
199        raise Exception("Unexpected Action code: %d" % action)
200
201    msg['da'] = msg['sa']
202    msg['sa'] = apdev[0]['bssid']
203
204    # modified parameters
205    p12int = payload[12] if sys.version_info[0] > 2 else ord(payload[12])
206    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 1) + payload[4:12] + struct.pack('B', p12int & ~0x60) + payload[13:]
207    hapd.mgmt_tx(msg)
208
209    # reject request
210    msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 1) + payload[4:]
211    hapd.mgmt_tx(msg)
212    ev = dev[0].wait_event(["TSPEC-REQ-FAILED"], timeout=10)
213    if ev is None:
214        raise Exception("Timeout on TSPEC-REQ-FAILED")
215    if "tsid=%d" % tsid not in ev:
216        raise Exception("Unexpected TSPEC-REQ-FAILED contents: " + ev)
217
218    hapd.set("ext_mgmt_frame_handling", "0")
219
220@remote_compatible
221def test_tspec_not_enabled(dev, apdev):
222    """addts failing if AP does not support WMM"""
223    params = {"ssid": "wmm_no_ac",
224              "hw_mode": "g",
225              "channel": "11",
226              "wmm_enabled": "0"}
227    hapd = hostapd.add_ap(apdev[0], params)
228    dev[0].connect("wmm_no_ac", key_mgmt="NONE", scan_freq="2462")
229    status = dev[0].request("WMM_AC_STATUS")
230    if "Not associated to a WMM AP, WMM AC is Disabled" not in status:
231        raise Exception("Unexpected WMM_AC_STATUS: " + status)
232
233    try:
234        dev[0].add_ts(5, 6)
235        raise Exception("ADDTS succeeded although it should have failed")
236    except Exception as e:
237        if not str(e).startswith("ADDTS failed"):
238            raise
239
240    # attempt to delete non-existing tsid
241    try:
242        dev[0].del_ts(5)
243        raise Exception("DELTS succeeded although it should have failed")
244    except Exception as e:
245        if not str(e).startswith("DELTS failed"):
246            raise
247
248    # unexpected Action frame when WMM is disabled
249    MGMT_SUBTYPE_ACTION = 13
250    msg = {}
251    msg['fc'] = MGMT_SUBTYPE_ACTION << 4
252    msg['da'] = dev[0].p2p_interface_addr()
253    msg['sa'] = apdev[0]['bssid']
254    msg['bssid'] = apdev[0]['bssid']
255    msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0)
256    hapd.mgmt_tx(msg)
257
258@remote_compatible
259def test_tspec_ap_roam_open(dev, apdev):
260    """Roam between two open APs while having tspecs"""
261    hapd0 = add_wmm_ap(apdev[0], ["VO", "VI"])
262    dev[0].connect("wmm_ac", key_mgmt="NONE")
263    hwsim_utils.test_connectivity(dev[0], hapd0)
264    dev[0].add_ts(5, 6)
265
266    hapd1 = add_wmm_ap(apdev[1], ["VO", "VI"])
267    dev[0].scan_for_bss(apdev[1]['bssid'], freq=2462)
268    dev[0].roam(apdev[1]['bssid'])
269    hwsim_utils.test_connectivity(dev[0], hapd1)
270    if dev[0].tspecs():
271        raise Exception("TSPECs weren't deleted on roaming")
272
273    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2462)
274    dev[0].roam(apdev[0]['bssid'])
275    hwsim_utils.test_connectivity(dev[0], hapd0)
276
277@remote_compatible
278def test_tspec_reassoc(dev, apdev):
279    """Reassociation to same BSS while having tspecs"""
280    hapd0 = add_wmm_ap(apdev[0], ["VO", "VI"])
281    dev[0].connect("wmm_ac", key_mgmt="NONE")
282    hwsim_utils.test_connectivity(dev[0], hapd0)
283    dev[0].add_ts(5, 6)
284    last_tspecs = dev[0].tspecs()
285
286    dev[0].request("REASSOCIATE")
287    dev[0].wait_connected()
288
289    hwsim_utils.test_connectivity(dev[0], hapd0)
290    if dev[0].tspecs() != last_tspecs:
291        raise Exception("TSPECs weren't saved on reassociation")
292
293def test_wmm_element(dev, apdev):
294    """hostapd FTM range request timeout"""
295    try:
296        run_wmm_element(dev, apdev)
297    finally:
298        dev[0].request("VENDOR_ELEM_REMOVE 13 *")
299
300def run_wmm_element(dev, apdev):
301    params = {"ssid": "wmm"}
302    hapd = hostapd.add_ap(apdev[0], params)
303    bssid = hapd.own_addr()
304
305    # Too short WMM IE
306    dev[0].request("VENDOR_ELEM_ADD 13 dd060050f2020001")
307    dev[0].scan_for_bss(bssid, freq=2412)
308    dev[0].connect("wmm", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
309    ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
310    if ev is None:
311        raise Exception("Association not rejected")
312    dev[0].request("REMOVE_NETWORK all")
313
314    # Unsupported WMM IE Subtype/Version
315    dev[0].request("VENDOR_ELEM_ADD 13 dd070050f202000000")
316    dev[0].connect("wmm", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
317    ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
318    if ev is None:
319        raise Exception("Association not rejected")
320    dev[0].request("REMOVE_NETWORK all")
321
322    # Unsupported WMM IE Subtype/Version
323    dev[0].request("VENDOR_ELEM_ADD 13 dd070050f202010100")
324    dev[0].connect("wmm", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
325    ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
326    if ev is None:
327        raise Exception("Association not rejected")
328    dev[0].request("REMOVE_NETWORK all")
329
330def test_tspec_ap_fail(dev, apdev):
331    """AP failing to send tspec response"""
332    # configure ap with VO and VI requiring admission-control
333    hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
334    dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
335    tsid = 5
336
337    with fail_test(hapd, 1, "wmm_send_action"):
338        try:
339            # add tspec for UP=6
340            dev[0].add_ts(tsid, 6)
341        except:
342            pass
343
344def test_tspec_ap_parsing(dev, apdev):
345    """TSPEC AP parsing tests"""
346    # configure ap with VO and VI requiring admission-control
347    hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
348    bssid = hapd.own_addr()
349    dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
350    addr = dev[0].own_addr()
351
352    tests = ["WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=1500 sba=9000 mean_data_rate=1500 min_phy_rate=600000",
353             "WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=1500 sba=8192 mean_data_rate=1500 min_phy_rate=6000000",
354             "WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=32767 sba=65535 mean_data_rate=1500 min_phy_rate=1000000",
355             "WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=10000 sba=65535 mean_data_rate=2147483647 min_phy_rate=1000000"]
356    for t in tests:
357        if "OK" not in dev[0].request(t):
358            raise Exception("WMM_AC_ADDTS failed")
359        ev = dev[0].wait_event(["TSPEC-REQ-FAILED"], timeout=1)
360        if ev is None:
361            raise Exception("No response")
362
363    tests = []
364    # WMM: Invalid Nominal MSDU Size (0)
365    tests += ["11000400dd3d0050f2020201aa300000000000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"]
366    # hostapd_wmm_action - missing or wrong length tspec
367    tests += ["11000400dd3e0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff000000"]
368    # hostapd_wmm_action - could not parse wmm action
369    tests += ["11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff00"]
370    # valid form
371    tests += ["11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"]
372
373    hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
374    hapd.set("ext_mgmt_frame_handling", "1")
375    for t in tests:
376        if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
377            raise Exception("MGMT_RX_PROCESS failed")
378
379    hapd.set("ext_mgmt_frame_handling", "0")
380
381def test_wmm_disabled(dev, apdev):
382    """WMM disabled and unexpected TSPEC"""
383    params = {"ssid": "no-wmm", "ieee80211n": "0", "wmm_enabled": "0"}
384    hapd = hostapd.add_ap(apdev[0], params)
385    bssid = hapd.own_addr()
386    dev[0].connect("no-wmm", key_mgmt="NONE", scan_freq="2412")
387    addr = dev[0].own_addr()
388
389    # wmm action received is not from associated wmm station
390    hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
391    hapd.set("ext_mgmt_frame_handling", "1")
392    if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"):
393        raise Exception("MGMT_RX_PROCESS failed")
394
395    # IEEE 802.11: Ignored Action frame (category=17) from unassociated STA
396    hdr = "d0003a01" + bssid.replace(':', '') + "112233445566" + bssid.replace(':', '') + "1000"
397    if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"):
398        raise Exception("MGMT_RX_PROCESS failed")
399
400    hapd.set("ext_mgmt_frame_handling", "0")
401