2 # Copyright (c) 2016, Intel Deutschland GmbH
4 # This software may be distributed under the terms of the BSD license.
5 # See README for more details.
7 from remotehost import remote_compatible
9 logger = logging.getLogger()
16 from tshark import run_tshark
17 from utils import alloc_fail, fail_test
19 def set_reg(country_code, apdev0=None, apdev1=None, dev0=None):
21 hostapd.cmd_execute(apdev0, ['iw', 'reg', 'set', country_code])
23 hostapd.cmd_execute(apdev1, ['iw', 'reg', 'set', country_code])
25 dev0.cmd_execute(['iw', 'reg', 'set', country_code])
27 def run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, country):
28 """MBO and supported operating classes"""
29 addr = dev[0].own_addr()
34 dev[0].flush_scan_cache()
37 logger.info("Country: " + country)
38 set_reg(country, apdev[0], apdev[1], dev[0])
40 ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
42 raise Exception("No regdom change event")
43 if "alpha2=" + country in ev:
49 hapd.set("country_code", country)
51 dev[0].scan_for_bss(hapd.own_addr(), 5180, force_scan=True)
52 dev[0].connect("test-wnm-mbo", key_mgmt="NONE", scan_freq="5180")
53 sta = hapd.get_sta(addr)
54 res5 = sta['supp_op_classes'][2:]
55 dev[0].request("REMOVE_NETWORK all")
57 dev[0].wait_disconnected()
60 hapd2.set("country_code", country)
62 dev[0].scan_for_bss(hapd2.own_addr(), 2412, force_scan=True)
63 dev[0].connect("test-wnm-mbo-2", key_mgmt="NONE", scan_freq="2412")
64 sta = hapd2.get_sta(addr)
65 res2 = sta['supp_op_classes'][2:]
66 dev[0].request("REMOVE_NETWORK all")
68 dev[0].wait_disconnected()
73 def test_mbo_supp_oper_classes(dev, apdev):
74 """MBO and supported operating classes"""
75 params = { 'ssid': "test-wnm-mbo",
82 hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
84 params = { 'ssid': "test-wnm-mbo-2",
91 hapd2 = hostapd.add_ap(apdev[1], params, no_enable=True)
94 za2, za5 = run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, "ZA")
95 fi2, fi5 = run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, "FI")
96 us2, us5 = run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, "US")
97 jp2, jp5 = run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, "JP")
98 bd2, bd5 = run_mbo_supp_oper_classes(dev, apdev, None, hapd2, "BD")
99 kz2, kz5 = run_mbo_supp_oper_classes(dev, apdev, None, hapd2, "KZ")
101 dev[0].dump_monitor()
102 set_reg("00", apdev[0], apdev[1], dev[0])
103 ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
105 za = "515354737475767778797a7b808182"
106 fi = "515354737475767778797a7b808182"
107 us = "515354737475767778797a7b7c7d7e7f808182"
108 jp = "51525354737475767778797a7b808182"
109 bd = "5153547c7d7e7f80"
112 tests = [ ("ZA", za, za2, za5, True),
113 ("FI", fi, fi2, fi5, True),
114 ("US", us, us2, us5, True),
115 ("JP", jp, jp2, jp5, True),
116 ("BD", bd, bd2, bd5, False),
117 ("KZ", kz, kz2, kz5, False) ]
118 for country, expected, res2, res5, inc5 in tests:
119 # For now, allow operating class 129 to be missing since not all
120 # installed regdb files include the 160 MHz channels.
121 expected2 = expected.replace('808182', '8082')
122 # For now, allow operating classes 121-123 to be missing since not all
123 # installed regdb files include the related US DFS channels.
124 expected2 = expected2.replace('78797a7b7c', '787c')
125 if res2 != expected and res2 != expected2:
126 raise Exception("Unexpected supp_op_class string (country=%s, 2.4 GHz): %s (expected: %s)" % (country, res2, expected))
127 if inc5 and res5 != expected and res5 != expected2:
128 raise Exception("Unexpected supp_op_class string (country=%s, 5 GHz): %s (expected: %s)" % (country, res5, expected))
130 def test_mbo_assoc_disallow(dev, apdev, params):
131 hapd1 = hostapd.add_ap(apdev[0], { "ssid": "MBO", "mbo": "1" })
132 hapd2 = hostapd.add_ap(apdev[1], { "ssid": "MBO", "mbo": "1" })
134 logger.debug("Set mbo_assoc_disallow with invalid value")
135 if "FAIL" not in hapd1.request("SET mbo_assoc_disallow 2"):
136 raise Exception("Set mbo_assoc_disallow for AP1 succeeded unexpectedly with value 2")
138 logger.debug("Disallow associations to AP1 and allow association to AP2")
139 if "OK" not in hapd1.request("SET mbo_assoc_disallow 1"):
140 raise Exception("Failed to set mbo_assoc_disallow for AP1")
141 if "OK" not in hapd2.request("SET mbo_assoc_disallow 0"):
142 raise Exception("Failed to set mbo_assoc_disallow for AP2")
144 dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412")
146 out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
147 "wlan.fc.type == 0 && wlan.fc.type_subtype == 0x00",
149 if "Destination address: " + hapd1.own_addr() in out:
150 raise Exception("Association request sent to disallowed AP")
152 timestamp = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
153 "wlan.fc.type_subtype == 0x00",
154 display=['frame.time'], wait=False)
156 logger.debug("Allow associations to AP1 and disallow assications to AP2")
157 if "OK" not in hapd1.request("SET mbo_assoc_disallow 0"):
158 raise Exception("Failed to set mbo_assoc_disallow for AP1")
159 if "OK" not in hapd2.request("SET mbo_assoc_disallow 1"):
160 raise Exception("Failed to set mbo_assoc_disallow for AP2")
162 dev[0].request("DISCONNECT")
163 dev[0].wait_disconnected()
165 # Force new scan, so the assoc_disallowed indication is updated */
166 dev[0].request("FLUSH")
168 dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412")
170 filter = 'wlan.fc.type == 0 && wlan.fc.type_subtype == 0x00 && frame.time > "' + timestamp.rstrip() + '"'
171 out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
173 if "Destination address: " + hapd2.own_addr() in out:
174 raise Exception("Association request sent to disallowed AP 2")
177 def test_mbo_cell_capa_update(dev, apdev):
178 """MBO cellular data capability update"""
179 ssid = "test-wnm-mbo"
180 params = { 'ssid': ssid, 'mbo': '1' }
181 hapd = hostapd.add_ap(apdev[0], params)
182 bssid = apdev[0]['bssid']
183 if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
184 raise Exception("Failed to set STA as cellular data capable")
186 dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
188 addr = dev[0].own_addr()
189 sta = hapd.get_sta(addr)
190 if 'mbo_cell_capa' not in sta or sta['mbo_cell_capa'] != '1':
191 raise Exception("mbo_cell_capa missing after association")
193 if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
194 raise Exception("Failed to set STA as cellular data not-capable")
195 # Duplicate update for additional code coverage
196 if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
197 raise Exception("Failed to set STA as cellular data not-capable")
200 sta = hapd.get_sta(addr)
201 if 'mbo_cell_capa' not in sta:
202 raise Exception("mbo_cell_capa missing after update")
203 if sta['mbo_cell_capa'] != '3':
204 raise Exception("mbo_cell_capa not updated properly")
207 def test_mbo_cell_capa_update_pmf(dev, apdev):
208 """MBO cellular data capability update with PMF required"""
209 ssid = "test-wnm-mbo"
210 passphrase = "12345678"
211 params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
212 params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
213 params["ieee80211w"] = "2"
215 hapd = hostapd.add_ap(apdev[0], params)
216 bssid = apdev[0]['bssid']
217 if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
218 raise Exception("Failed to set STA as cellular data capable")
220 dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
221 proto="WPA2", ieee80211w="2", scan_freq="2412")
223 addr = dev[0].own_addr()
224 sta = hapd.get_sta(addr)
225 if 'mbo_cell_capa' not in sta or sta['mbo_cell_capa'] != '1':
226 raise Exception("mbo_cell_capa missing after association")
228 if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
229 raise Exception("Failed to set STA as cellular data not-capable")
232 sta = hapd.get_sta(addr)
233 if 'mbo_cell_capa' not in sta:
234 raise Exception("mbo_cell_capa missing after update")
235 if sta['mbo_cell_capa'] != '3':
236 raise Exception("mbo_cell_capa not updated properly")
238 def test_mbo_wnm_token_wrap(dev, apdev):
239 """MBO WNM token wrap around"""
240 ssid = "test-wnm-mbo"
241 params = { 'ssid': ssid, 'mbo': '1' }
242 hapd = hostapd.add_ap(apdev[0], params)
243 bssid = apdev[0]['bssid']
245 dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
247 # Trigger transmission of 256 WNM-Notification frames to wrap around the
248 # 8-bit mbo_wnm_token counter.
250 if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
251 raise Exception("Failed to set STA as cellular data capable")
252 if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
253 raise Exception("Failed to set STA as cellular data not-capable")
256 def test_mbo_non_pref_chan(dev, apdev):
257 """MBO non-preferred channel list"""
258 ssid = "test-wnm-mbo"
259 params = { 'ssid': ssid, 'mbo': '1' }
260 hapd = hostapd.add_ap(apdev[0], params)
261 bssid = apdev[0]['bssid']
262 if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:99"):
263 raise Exception("Invalid non_pref_chan value accepted")
264 if "FAIL" not in dev[0].request("SET non_pref_chan 81:15:200:3"):
265 raise Exception("Invalid non_pref_chan value accepted")
266 if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3 81:7:201:3"):
267 raise Exception("Invalid non_pref_chan value accepted")
268 if "OK" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
269 raise Exception("Failed to set non-preferred channel list")
270 if "OK" not in dev[0].request("SET non_pref_chan 81:7:200:1 81:9:100:2"):
271 raise Exception("Failed to set non-preferred channel list")
273 dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
275 addr = dev[0].own_addr()
276 sta = hapd.get_sta(addr)
277 logger.debug("STA: " + str(sta))
278 if 'non_pref_chan[0]' not in sta:
279 raise Exception("Missing non_pref_chan[0] value (assoc)")
280 if sta['non_pref_chan[0]'] != '81:200:1:7':
281 raise Exception("Unexpected non_pref_chan[0] value (assoc)")
282 if 'non_pref_chan[1]' not in sta:
283 raise Exception("Missing non_pref_chan[1] value (assoc)")
284 if sta['non_pref_chan[1]'] != '81:100:2:9':
285 raise Exception("Unexpected non_pref_chan[1] value (assoc)")
286 if 'non_pref_chan[2]' in sta:
287 raise Exception("Unexpected non_pref_chan[2] value (assoc)")
289 if "OK" not in dev[0].request("SET non_pref_chan 81:9:100:2"):
290 raise Exception("Failed to update non-preferred channel list")
292 sta = hapd.get_sta(addr)
293 logger.debug("STA: " + str(sta))
294 if 'non_pref_chan[0]' not in sta:
295 raise Exception("Missing non_pref_chan[0] value (update 1)")
296 if sta['non_pref_chan[0]'] != '81:100:2:9':
297 raise Exception("Unexpected non_pref_chan[0] value (update 1)")
298 if 'non_pref_chan[1]' in sta:
299 raise Exception("Unexpected non_pref_chan[1] value (update 1)")
301 if "OK" not in dev[0].request("SET non_pref_chan 81:9:100:2 81:10:100:2 81:8:100:2 81:7:100:1 81:5:100:1"):
302 raise Exception("Failed to update non-preferred channel list")
304 sta = hapd.get_sta(addr)
305 logger.debug("STA: " + str(sta))
306 if 'non_pref_chan[0]' not in sta:
307 raise Exception("Missing non_pref_chan[0] value (update 2)")
308 if sta['non_pref_chan[0]'] != '81:100:1:7,5':
309 raise Exception("Unexpected non_pref_chan[0] value (update 2)")
310 if 'non_pref_chan[1]' not in sta:
311 raise Exception("Missing non_pref_chan[1] value (update 2)")
312 if sta['non_pref_chan[1]'] != '81:100:2:9,10,8':
313 raise Exception("Unexpected non_pref_chan[1] value (update 2)")
314 if 'non_pref_chan[2]' in sta:
315 raise Exception("Unexpected non_pref_chan[2] value (update 2)")
317 if "OK" not in dev[0].request("SET non_pref_chan 81:5:90:2 82:14:91:2"):
318 raise Exception("Failed to update non-preferred channel list")
320 sta = hapd.get_sta(addr)
321 logger.debug("STA: " + str(sta))
322 if 'non_pref_chan[0]' not in sta:
323 raise Exception("Missing non_pref_chan[0] value (update 3)")
324 if sta['non_pref_chan[0]'] != '81:90:2:5':
325 raise Exception("Unexpected non_pref_chan[0] value (update 3)")
326 if 'non_pref_chan[1]' not in sta:
327 raise Exception("Missing non_pref_chan[1] value (update 3)")
328 if sta['non_pref_chan[1]'] != '82:91:2:14':
329 raise Exception("Unexpected non_pref_chan[1] value (update 3)")
330 if 'non_pref_chan[2]' in sta:
331 raise Exception("Unexpected non_pref_chan[2] value (update 3)")
333 if "OK" not in dev[0].request("SET non_pref_chan "):
334 raise Exception("Failed to update non-preferred channel list")
336 sta = hapd.get_sta(addr)
337 logger.debug("STA: " + str(sta))
338 if 'non_pref_chan[0]' in sta:
339 raise Exception("Unexpected non_pref_chan[0] value (update 4)")
342 def test_mbo_sta_supp_op_classes(dev, apdev):
343 """MBO STA supported operating classes"""
344 ssid = "test-wnm-mbo"
345 params = { 'ssid': ssid, 'mbo': '1' }
346 hapd = hostapd.add_ap(apdev[0], params)
348 dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
350 addr = dev[0].own_addr()
351 sta = hapd.get_sta(addr)
352 logger.debug("STA: " + str(sta))
353 if 'supp_op_classes' not in sta:
354 raise Exception("No supp_op_classes")
355 supp = bytearray(sta['supp_op_classes'].decode("hex"))
357 raise Exception("Unexpected current operating class %d" % supp[0])
359 raise Exception("Operating class 115 missing")
361 def test_mbo_failures(dev, apdev):
362 """MBO failure cases"""
363 ssid = "test-wnm-mbo"
364 params = { 'ssid': ssid, 'mbo': '1' }
365 hapd = hostapd.add_ap(apdev[0], params)
367 with alloc_fail(dev[0], 1, "wpas_mbo_ie"):
368 dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
370 with alloc_fail(dev[0], 1, "wpas_mbo_send_wnm_notification"):
371 if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
372 raise Exception("Failed to set STA as cellular data capable")
373 with fail_test(dev[0], 1, "wpas_mbo_send_wnm_notification"):
374 if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
375 raise Exception("Failed to set STA as cellular data not-capable")
376 with alloc_fail(dev[0], 1, "wpas_mbo_update_non_pref_chan"):
377 if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
378 raise Exception("non_pref_chan value accepted during OOM")
379 with alloc_fail(dev[0], 2, "wpas_mbo_update_non_pref_chan"):
380 if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
381 raise Exception("non_pref_chan value accepted during OOM")
383 def test_mbo_wnm_bss_tm_ie_parsing(dev, apdev):
384 """MBO BSS transition request MBO IE parsing"""
385 ssid = "test-wnm-mbo"
386 params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
387 hapd = hostapd.add_ap(apdev[0], params)
388 bssid = apdev[0]['bssid']
389 addr = dev[0].own_addr()
390 dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
391 proto="WPA2", ieee80211w="0", scan_freq="2412")
393 dev[0].request("SET ext_mgmt_frame_handling 1")
394 hdr = "d0003a01" + addr.replace(':', '') + bssid.replace(':', '') + bssid.replace(':', '') + "3000"
395 btm_hdr = "0a070100030001"
397 tests = [ ("Truncated attribute in MBO IE", "dd06506f9a160101"),
398 ("Unexpected cell data capa attribute length in MBO IE",
399 "dd09506f9a160501030500"),
400 ("Unexpected transition reason attribute length in MBO IE",
402 ("Unexpected assoc retry delay attribute length in MBO IE",
403 "dd0c506f9a160100080200000800"),
404 ("Unknown attribute id 255 in MBO IE",
405 "dd06506f9a16ff00") ]
407 for test, mbo_ie in tests:
409 dev[0].request("NOTE " + test)
410 frame = hdr + btm_hdr + mbo_ie
411 if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
412 raise Exception("MGMT_RX_PROCESS failed")
414 logger.info("Unexpected association retry delay")
415 dev[0].request("NOTE Unexpected association retry delay")
416 btm_hdr = "0a070108030001112233445566778899aabbcc"
417 mbo_ie = "dd08506f9a1608020000"
418 frame = hdr + btm_hdr + mbo_ie
419 if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
420 raise Exception("MGMT_RX_PROCESS failed")
422 dev[0].request("SET ext_mgmt_frame_handling 0")