tests: WNM BSS Transition Management and scan behavior
[mech_eap.git] / tests / hwsim / test_wnm.py
1 # WNM tests
2 # Copyright (c) 2013-2014, 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
7 import binascii
8 import struct
9 import time
10 import logging
11 logger = logging.getLogger()
12 import subprocess
13
14 import hostapd
15 from utils import alloc_fail, wait_fail_trigger
16 from wlantest import Wlantest
17
18 def test_wnm_bss_transition_mgmt(dev, apdev):
19     """WNM BSS Transition Management"""
20     params = { "ssid": "test-wnm",
21                "time_advertisement": "2",
22                "time_zone": "EST5",
23                "wnm_sleep_mode": "1",
24                "bss_transition": "1" }
25     hostapd.add_ap(apdev[0]['ifname'], params)
26
27     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
28     dev[0].request("WNM_BSS_QUERY 0")
29
30 def test_wnm_disassoc_imminent(dev, apdev):
31     """WNM Disassociation Imminent"""
32     params = { "ssid": "test-wnm",
33                "time_advertisement": "2",
34                "time_zone": "EST5",
35                "wnm_sleep_mode": "1",
36                "bss_transition": "1" }
37     hostapd.add_ap(apdev[0]['ifname'], params)
38     hapd = hostapd.Hostapd(apdev[0]['ifname'])
39
40     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
41     addr = dev[0].p2p_interface_addr()
42     hapd.request("DISASSOC_IMMINENT " + addr + " 10")
43     ev = dev[0].wait_event(["WNM: Disassociation Imminent"])
44     if ev is None:
45         raise Exception("Timeout while waiting for disassociation imminent")
46     if "Disassociation Timer 10" not in ev:
47         raise Exception("Unexpected disassociation imminent contents")
48     ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
49     if ev is None:
50         raise Exception("Timeout while waiting for re-connection scan")
51
52 def test_wnm_ess_disassoc_imminent(dev, apdev):
53     """WNM ESS Disassociation Imminent"""
54     params = { "ssid": "test-wnm",
55                "time_advertisement": "2",
56                "time_zone": "EST5",
57                "wnm_sleep_mode": "1",
58                "bss_transition": "1" }
59     hostapd.add_ap(apdev[0]['ifname'], params)
60     hapd = hostapd.Hostapd(apdev[0]['ifname'])
61
62     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
63     addr = dev[0].p2p_interface_addr()
64     hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
65     ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
66     if ev is None:
67         raise Exception("Timeout while waiting for ESS disassociation imminent")
68     if "0 1024 http://example.com/session-info" not in ev:
69         raise Exception("Unexpected ESS disassociation imminent message contents")
70     ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
71     if ev is None:
72         raise Exception("Timeout while waiting for re-connection scan")
73
74 def test_wnm_ess_disassoc_imminent_pmf(dev, apdev):
75     """WNM ESS Disassociation Imminent"""
76     params = hostapd.wpa2_params("test-wnm-rsn", "12345678")
77     params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
78     params["ieee80211w"] = "2";
79     params["bss_transition"] = "1"
80     hostapd.add_ap(apdev[0]['ifname'], params)
81     hapd = hostapd.Hostapd(apdev[0]['ifname'])
82
83     dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
84                    key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
85     addr = dev[0].p2p_interface_addr()
86     hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
87     ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
88     if ev is None:
89         raise Exception("Timeout while waiting for ESS disassociation imminent")
90     if "1 1024 http://example.com/session-info" not in ev:
91         raise Exception("Unexpected ESS disassociation imminent message contents")
92     ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
93     if ev is None:
94         raise Exception("Timeout while waiting for re-connection scan")
95
96 def check_wnm_sleep_mode_enter_exit(hapd, dev, interval=None, tfs_req=None):
97     addr = dev.p2p_interface_addr()
98     sta = hapd.get_sta(addr)
99     if "[WNM_SLEEP_MODE]" in sta['flags']:
100         raise Exception("Station unexpectedly in WNM-Sleep Mode")
101
102     logger.info("Going to WNM Sleep Mode")
103     extra = ""
104     if interval is not None:
105         extra += " interval=" + str(interval)
106     if tfs_req:
107         extra += " tfs_req=" + tfs_req
108     if "OK" not in dev.request("WNM_SLEEP enter" + extra):
109         raise Exception("WNM_SLEEP failed")
110     ok = False
111     for i in range(20):
112         time.sleep(0.1)
113         sta = hapd.get_sta(addr)
114         if "[WNM_SLEEP_MODE]" in sta['flags']:
115             ok = True
116             break
117     if not ok:
118         raise Exception("Station failed to enter WNM-Sleep Mode")
119
120     logger.info("Waking up from WNM Sleep Mode")
121     ok = False
122     dev.request("WNM_SLEEP exit")
123     for i in range(20):
124         time.sleep(0.1)
125         sta = hapd.get_sta(addr)
126         if "[WNM_SLEEP_MODE]" not in sta['flags']:
127             ok = True
128             break
129     if not ok:
130         raise Exception("Station failed to exit WNM-Sleep Mode")
131
132 def test_wnm_sleep_mode_open(dev, apdev):
133     """WNM Sleep Mode - open"""
134     params = { "ssid": "test-wnm",
135                "time_advertisement": "2",
136                "time_zone": "EST5",
137                "wnm_sleep_mode": "1",
138                "bss_transition": "1" }
139     hostapd.add_ap(apdev[0]['ifname'], params)
140     hapd = hostapd.Hostapd(apdev[0]['ifname'])
141
142     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
143     ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
144     if ev is None:
145         raise Exception("No connection event received from hostapd")
146     check_wnm_sleep_mode_enter_exit(hapd, dev[0])
147     check_wnm_sleep_mode_enter_exit(hapd, dev[0], interval=100)
148     check_wnm_sleep_mode_enter_exit(hapd, dev[0], tfs_req="5b17010001130e110000071122334455661122334455661234")
149
150     cmds = [ "foo",
151              "exit tfs_req=123 interval=10",
152              "enter tfs_req=qq interval=10" ]
153     for cmd in cmds:
154         if "FAIL" not in dev[0].request("WNM_SLEEP " + cmd):
155             raise Exception("Invalid WNM_SLEEP accepted")
156
157 def test_wnm_sleep_mode_rsn(dev, apdev):
158     """WNM Sleep Mode - RSN"""
159     params = hostapd.wpa2_params("test-wnm-rsn", "12345678")
160     params["time_advertisement"] = "2"
161     params["time_zone"] = "EST5"
162     params["wnm_sleep_mode"] = "1"
163     params["bss_transition"] = "1"
164     hostapd.add_ap(apdev[0]['ifname'], params)
165     hapd = hostapd.Hostapd(apdev[0]['ifname'])
166
167     dev[0].connect("test-wnm-rsn", psk="12345678", scan_freq="2412")
168     ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
169     if ev is None:
170         raise Exception("No connection event received from hostapd")
171     check_wnm_sleep_mode_enter_exit(hapd, dev[0])
172
173 def test_wnm_sleep_mode_ap_oom(dev, apdev):
174     """WNM Sleep Mode - AP side OOM"""
175     params = { "ssid": "test-wnm",
176                "wnm_sleep_mode": "1" }
177     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
178
179     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
180     ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
181     if ev is None:
182         raise Exception("No connection event received from hostapd")
183     with alloc_fail(hapd, 1, "ieee802_11_send_wnmsleep_resp"):
184         dev[0].request("WNM_SLEEP enter")
185         wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
186     with alloc_fail(hapd, 2, "ieee802_11_send_wnmsleep_resp"):
187         dev[0].request("WNM_SLEEP exit")
188         wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
189
190 def test_wnm_sleep_mode_rsn_pmf(dev, apdev):
191     """WNM Sleep Mode - RSN with PMF"""
192     wt = Wlantest()
193     wt.flush()
194     wt.add_passphrase("12345678")
195     params = hostapd.wpa2_params("test-wnm-rsn", "12345678")
196     params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
197     params["ieee80211w"] = "2";
198     params["time_advertisement"] = "2"
199     params["time_zone"] = "EST5"
200     params["wnm_sleep_mode"] = "1"
201     params["bss_transition"] = "1"
202     hostapd.add_ap(apdev[0]['ifname'], params)
203     hapd = hostapd.Hostapd(apdev[0]['ifname'])
204
205     dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
206                    key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
207     ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
208     if ev is None:
209         raise Exception("No connection event received from hostapd")
210     check_wnm_sleep_mode_enter_exit(hapd, dev[0])
211
212 MGMT_SUBTYPE_ACTION = 13
213 ACTION_CATEG_WNM = 10
214 WNM_ACT_BSS_TM_REQ = 7
215 WNM_ACT_BSS_TM_RESP = 8
216 WNM_ACT_SLEEP_MODE_REQ = 16
217 WNM_ACT_SLEEP_MODE_RESP = 17
218 WNM_ACT_NOTIFICATION_REQ = 26
219 WNM_ACT_NOTIFICATION_RESP = 27
220 WNM_NOTIF_TYPE_FW_UPGRADE = 0
221 WNM_NOTIF_TYPE_WFA = 1
222 WLAN_EID_TFS_RESP = 92
223 WLAN_EID_WNMSLEEP = 93
224 WNM_SLEEP_MODE_ENTER = 0
225 WNM_SLEEP_MODE_EXIT = 1
226 WNM_STATUS_SLEEP_ACCEPT = 0
227 WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1
228 WNM_STATUS_DENIED_ACTION = 2
229 WNM_STATUS_DENIED_TMP = 3
230 WNM_STATUS_DENIED_KEY = 4
231 WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
232 WNM_SLEEP_SUBELEM_GTK = 0
233 WNM_SLEEP_SUBELEM_IGTK = 1
234
235 def bss_tm_req(dst, src, dialog_token=1, req_mode=0, disassoc_timer=0,
236                validity_interval=1):
237     msg = {}
238     msg['fc'] = MGMT_SUBTYPE_ACTION << 4
239     msg['da'] = dst
240     msg['sa'] = src
241     msg['bssid'] = src
242     msg['payload'] = struct.pack("<BBBBHB",
243                                  ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
244                                  dialog_token, req_mode, disassoc_timer,
245                                  validity_interval)
246     return msg
247
248 def rx_bss_tm_resp(hapd, expect_dialog=None, expect_status=None):
249     for i in range(0, 100):
250         resp = hapd.mgmt_rx()
251         if resp is None:
252             raise Exception("No BSS TM Response received")
253         if resp['subtype'] == MGMT_SUBTYPE_ACTION:
254             break
255     if i == 99:
256         raise Exception("Not an Action frame")
257     payload = resp['payload']
258     if len(payload) < 2 + 3:
259         raise Exception("Too short payload")
260     (category, action) = struct.unpack('BB', payload[0:2])
261     if category != ACTION_CATEG_WNM or action != WNM_ACT_BSS_TM_RESP:
262         raise Exception("Not a BSS TM Response")
263     pos = payload[2:]
264     (dialog, status, bss_term_delay) = struct.unpack('BBB', pos[0:3])
265     resp['dialog'] = dialog
266     resp['status'] = status
267     resp['bss_term_delay'] = bss_term_delay
268     pos = pos[3:]
269     if len(pos) >= 6 and status == 0:
270         resp['target_bssid'] = binascii.hexlify(pos[0:6])
271         pos = pos[6:]
272     resp['candidates'] = pos
273     if expect_dialog is not None and dialog != expect_dialog:
274         raise Exception("Unexpected dialog token")
275     if expect_status is not None and status != expect_status:
276         raise Exception("Unexpected status code %d" % status)
277     return resp
278
279 def expect_ack(hapd):
280     ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
281     if ev is None:
282         raise Exception("Missing TX status")
283     if "ok=1" not in ev:
284         raise Exception("Action frame not acknowledged")
285
286 def test_wnm_bss_tm_req(dev, apdev):
287     """BSS Transition Management Request"""
288     params = { "ssid": "test-wnm", "bss_transition": "1" }
289     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
290     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
291     hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
292
293     hapd.set("ext_mgmt_frame_handling", "1")
294
295     # truncated BSS TM Request
296     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
297                      req_mode=0x08)
298     req['payload'] = struct.pack("<BBBBH",
299                                  ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
300                                  1, 0, 0)
301     hapd.mgmt_tx(req)
302     expect_ack(hapd)
303
304     # no disassociation and no candidate list
305     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
306                      dialog_token=2)
307     hapd.mgmt_tx(req)
308     resp = rx_bss_tm_resp(hapd, expect_dialog=2, expect_status=1)
309
310     # truncated BSS Termination Duration
311     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
312                      req_mode=0x08)
313     hapd.mgmt_tx(req)
314     expect_ack(hapd)
315
316     # BSS Termination Duration with TSF=0 and Duration=10
317     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
318                      req_mode=0x08, dialog_token=3)
319     req['payload'] += struct.pack("<BBQH", 4, 10, 0, 10)
320     hapd.mgmt_tx(req)
321     resp = rx_bss_tm_resp(hapd, expect_dialog=3, expect_status=1)
322
323     # truncated Session Information URL
324     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
325                      req_mode=0x10)
326     hapd.mgmt_tx(req)
327     expect_ack(hapd)
328     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
329                      req_mode=0x10)
330     req['payload'] += struct.pack("<BBB", 3, 65, 66)
331     hapd.mgmt_tx(req)
332     expect_ack(hapd)
333
334     # Session Information URL
335     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
336                      req_mode=0x10, dialog_token=4)
337     req['payload'] += struct.pack("<BBB", 2, 65, 66)
338     hapd.mgmt_tx(req)
339     resp = rx_bss_tm_resp(hapd, expect_dialog=4, expect_status=0)
340
341     # Preferred Candidate List without any entries
342     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
343                      req_mode=0x01, dialog_token=5)
344     hapd.mgmt_tx(req)
345     resp = rx_bss_tm_resp(hapd, expect_dialog=5, expect_status=7)
346
347     # Preferred Candidate List with a truncated entry
348     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
349                      req_mode=0x01)
350     req['payload'] += struct.pack("<BB", 52, 1)
351     hapd.mgmt_tx(req)
352     expect_ack(hapd)
353
354     # Preferred Candidate List with a too short entry
355     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
356                      req_mode=0x01, dialog_token=6)
357     req['payload'] += struct.pack("<BB", 52, 0)
358     hapd.mgmt_tx(req)
359     resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
360
361     # Preferred Candidate List with a non-matching entry
362     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
363                      req_mode=0x01, dialog_token=6)
364     req['payload'] += struct.pack("<BB6BLBBB", 52, 13,
365                                   1, 2, 3, 4, 5, 6,
366                                   0, 81, 1, 7)
367     hapd.mgmt_tx(req)
368     resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
369
370     # Preferred Candidate List with a truncated subelement
371     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
372                      req_mode=0x01, dialog_token=7)
373     req['payload'] += struct.pack("<BB6BLBBBBB", 52, 13 + 2,
374                                   1, 2, 3, 4, 5, 6,
375                                   0, 81, 1, 7,
376                                   1, 1)
377     hapd.mgmt_tx(req)
378     resp = rx_bss_tm_resp(hapd, expect_dialog=7, expect_status=7)
379
380     # Preferred Candidate List with lots of invalid optional subelements
381     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
382                      req_mode=0x01, dialog_token=8)
383     subelems = struct.pack("<BBHB", 1, 3, 0, 100)
384     subelems += struct.pack("<BBB", 2, 1, 65)
385     subelems += struct.pack("<BB", 3, 0)
386     subelems += struct.pack("<BBQB", 4, 9, 0, 10)
387     subelems += struct.pack("<BBHLB", 5, 7, 0, 0, 0)
388     subelems += struct.pack("<BB", 66, 0)
389     subelems += struct.pack("<BBBBBB", 70, 4, 0, 0, 0, 0)
390     subelems += struct.pack("<BB", 71, 0)
391     req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
392                                   1, 2, 3, 4, 5, 6,
393                                   0, 81, 1, 7) + subelems
394     hapd.mgmt_tx(req)
395     resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
396
397     # Preferred Candidate List with lots of valid optional subelements (twice)
398     req = bss_tm_req(dev[0].p2p_interface_addr(), apdev[0]['bssid'],
399                      req_mode=0x01, dialog_token=8)
400     # TSF Information
401     subelems = struct.pack("<BBHH", 1, 4, 0, 100)
402     # Condensed Country String
403     subelems += struct.pack("<BBBB", 2, 2, 65, 66)
404     # BSS Transition Candidate Preference
405     subelems += struct.pack("<BBB", 3, 1, 100)
406     # BSS Termination Duration
407     subelems += struct.pack("<BBQH", 4, 10, 0, 10)
408     # Bearing
409     subelems += struct.pack("<BBHLH", 5, 8, 0, 0, 0)
410     # Measurement Pilot Transmission
411     subelems += struct.pack("<BBBBB", 66, 3, 0, 0, 0)
412     # RM Enabled Capabilities
413     subelems += struct.pack("<BBBBBBB", 70, 5, 0, 0, 0, 0, 0)
414     # Multiple BSSID
415     subelems += struct.pack("<BBBB", 71, 2, 0, 0)
416     req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems) * 2,
417                                   1, 2, 3, 4, 5, 6,
418                                   0, 81, 1, 7) + subelems + subelems
419     hapd.mgmt_tx(req)
420     resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
421
422 def test_wnm_bss_keep_alive(dev, apdev):
423     """WNM keep-alive"""
424     params = { "ssid": "test-wnm",
425                "ap_max_inactivity": "1" }
426     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
427
428     addr = dev[0].p2p_interface_addr()
429     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
430     start = hapd.get_sta(addr)
431     ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=2)
432     if ev is not None:
433         raise Exception("Unexpected disconnection")
434     end = hapd.get_sta(addr)
435     if int(end['rx_packets']) <= int(start['rx_packets']):
436         raise Exception("No keep-alive packets received")
437     try:
438         # Disable client keep-alive so that hostapd will verify connection
439         # with client poll
440         dev[0].request("SET no_keep_alive 1")
441         for i in range(60):
442             sta = hapd.get_sta(addr)
443             logger.info("timeout_next=%s rx_packets=%s tx_packets=%s" % (sta['timeout_next'], sta['rx_packets'], sta['tx_packets']))
444             if i > 1 and sta['timeout_next'] != "NULLFUNC POLL" and int(sta['tx_packets']) > int(end['tx_packets']):
445                 break
446             ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
447             if ev is not None:
448                 raise Exception("Unexpected disconnection (client poll expected)")
449     finally:
450         dev[0].request("SET no_keep_alive 0")
451     if int(sta['tx_packets']) <= int(end['tx_packets']):
452         raise Exception("No client poll packet seen")
453
454 def test_wnm_bss_tm(dev, apdev):
455     """WNM BSS Transition Management"""
456     try:
457         hapd = None
458         hapd2 = None
459         params = { "ssid": "test-wnm",
460                    "country_code": "FI",
461                    "ieee80211d": "1",
462                    "hw_mode": "g",
463                    "channel": "1",
464                    "bss_transition": "1" }
465         hapd = hostapd.add_ap(apdev[0]['ifname'], params)
466
467         id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
468         dev[0].set_network(id, "scan_freq", "")
469
470         params = { "ssid": "test-wnm",
471                    "country_code": "FI",
472                    "ieee80211d": "1",
473                    "hw_mode": "a",
474                    "channel": "36",
475                    "bss_transition": "1" }
476         hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
477
478         addr = dev[0].p2p_interface_addr()
479         dev[0].dump_monitor()
480
481         logger.info("No neighbor list entries")
482         if "OK" not in hapd.request("BSS_TM_REQ " + addr):
483             raise Exception("BSS_TM_REQ command failed")
484         ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
485         if ev is None:
486             raise Exception("No BSS Transition Management Response")
487         if addr not in ev:
488             raise Exception("Unexpected BSS Transition Management Response address")
489         if "status_code=0" in ev:
490             raise Exception("BSS transition accepted unexpectedly")
491         dev[0].dump_monitor()
492
493         logger.info("Neighbor list entry, but not claimed as Preferred Candidate List")
494         if "OK" not in hapd.request("BSS_TM_REQ " + addr + " neighbor=11:22:33:44:55:66,0x0000,81,3,7"):
495             raise Exception("BSS_TM_REQ command failed")
496         ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
497         if ev is None:
498             raise Exception("No BSS Transition Management Response")
499         if "status_code=0" in ev:
500             raise Exception("BSS transition accepted unexpectedly")
501         dev[0].dump_monitor()
502
503         logger.info("Preferred Candidate List (no matching neighbor) without Disassociation Imminent")
504         if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=22:33:44:55:66:77,0x0000,1,36,7 neighbor=00:11:22:33:44:55,0x0000,81,4,7,03010a"):
505             raise Exception("BSS_TM_REQ command failed")
506         ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
507         if ev is None:
508             raise Exception("No BSS Transition Management Response")
509         if "status_code=0" in ev:
510             raise Exception("BSS transition accepted unexpectedly")
511         ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
512         if ev is None:
513             raise Exception("No scan started")
514         dev[0].dump_monitor()
515
516         logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
517         if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
518             raise Exception("BSS_TM_REQ command failed")
519         ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
520         if ev is None:
521             raise Exception("No BSS Transition Management Response")
522         if "status_code=0" not in ev:
523             raise Exception("BSS transition request was not accepted: " + ev)
524         if "target_bssid=" + apdev[1]['bssid'] not in ev:
525             raise Exception("Unexpected target BSS: " + ev)
526         dev[0].wait_connected(timeout=15, error="No reassociation seen")
527         if apdev[1]['bssid'] not in ev:
528             raise Exception("Unexpected reassociation target: " + ev)
529         ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
530         if ev is not None:
531             raise Exception("Unexpected scan started")
532         dev[0].dump_monitor()
533
534         logger.info("Preferred Candidate List with two matches, no roam needed")
535         if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,030101 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
536             raise Exception("BSS_TM_REQ command failed")
537         ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10)
538         if ev is None:
539             raise Exception("No BSS Transition Management Response")
540         if "status_code=0" not in ev:
541             raise Exception("BSS transition request was not accepted: " + ev)
542         if "target_bssid=" + apdev[1]['bssid'] not in ev:
543             raise Exception("Unexpected target BSS: " + ev)
544         ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
545         if ev is not None:
546             raise Exception("Unexpected scan started")
547         ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
548         if ev is not None:
549             raise Exception("Unexpected reassociation");
550     finally:
551         dev[0].request("DISCONNECT")
552         if hapd:
553             hapd.request("DISABLE")
554         if hapd2:
555             hapd2.request("DISABLE")
556         subprocess.call(['iw', 'reg', 'set', '00'])
557         dev[0].flush_scan_cache()
558
559 def test_wnm_bss_tm_scan_not_needed(dev, apdev):
560     """WNM BSS Transition Management and scan not needed"""
561     try:
562         hapd = None
563         hapd2 = None
564         params = { "ssid": "test-wnm",
565                    "country_code": "FI",
566                    "ieee80211d": "1",
567                    "hw_mode": "g",
568                    "channel": "1",
569                    "bss_transition": "1" }
570         hapd = hostapd.add_ap(apdev[0]['ifname'], params)
571
572         params = { "ssid": "test-wnm",
573                    "country_code": "FI",
574                    "ieee80211d": "1",
575                    "hw_mode": "a",
576                    "channel": "36",
577                    "bss_transition": "1" }
578         hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
579
580         dev[0].scan_for_bss(apdev[1]['bssid'], 5180)
581
582         id = dev[0].connect("test-wnm", key_mgmt="NONE",
583                             bssid=apdev[0]['bssid'], scan_freq="2412")
584         dev[0].set_network(id, "scan_freq", "")
585         dev[0].set_network(id, "bssid", "")
586
587         addr = dev[0].own_addr()
588         dev[0].dump_monitor()
589
590         logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
591         if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
592             raise Exception("BSS_TM_REQ command failed")
593         ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
594         if ev is None:
595             raise Exception("No BSS Transition Management Response")
596         if "status_code=0" not in ev:
597             raise Exception("BSS transition request was not accepted: " + ev)
598         if "target_bssid=" + apdev[1]['bssid'] not in ev:
599             raise Exception("Unexpected target BSS: " + ev)
600         dev[0].wait_connected(timeout=15, error="No reassociation seen")
601         if apdev[1]['bssid'] not in ev:
602             raise Exception("Unexpected reassociation target: " + ev)
603         ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
604         if ev is not None:
605             raise Exception("Unexpected scan started")
606         dev[0].dump_monitor()
607     finally:
608         dev[0].request("DISCONNECT")
609         if hapd:
610             hapd.request("DISABLE")
611         if hapd2:
612             hapd2.request("DISABLE")
613         subprocess.call(['iw', 'reg', 'set', '00'])
614         dev[0].flush_scan_cache()
615
616 def test_wnm_bss_tm_scan_needed(dev, apdev):
617     """WNM BSS Transition Management and scan needed"""
618     try:
619         hapd = None
620         hapd2 = None
621         params = { "ssid": "test-wnm",
622                    "country_code": "FI",
623                    "ieee80211d": "1",
624                    "hw_mode": "g",
625                    "channel": "1",
626                    "bss_transition": "1" }
627         hapd = hostapd.add_ap(apdev[0]['ifname'], params)
628
629         params = { "ssid": "test-wnm",
630                    "country_code": "FI",
631                    "ieee80211d": "1",
632                    "hw_mode": "a",
633                    "channel": "36",
634                    "bss_transition": "1" }
635         hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
636
637         dev[0].scan_for_bss(apdev[1]['bssid'], 5180)
638
639         id = dev[0].connect("test-wnm", key_mgmt="NONE",
640                             bssid=apdev[0]['bssid'], scan_freq="2412")
641         dev[0].set_network(id, "scan_freq", "")
642         dev[0].set_network(id, "bssid", "")
643
644         addr = dev[0].own_addr()
645         dev[0].dump_monitor()
646
647         logger.info("Wait 11 seconds for the last scan result to be too old, but still present in BSS table")
648         time.sleep(11)
649         logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
650         if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
651             raise Exception("BSS_TM_REQ command failed")
652         ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
653         if ev is None:
654             raise Exception("No BSS Transition Management Response")
655         if "status_code=0" not in ev:
656             raise Exception("BSS transition request was not accepted: " + ev)
657         if "target_bssid=" + apdev[1]['bssid'] not in ev:
658             raise Exception("Unexpected target BSS: " + ev)
659         dev[0].wait_connected(timeout=15, error="No reassociation seen")
660         if apdev[1]['bssid'] not in ev:
661             raise Exception("Unexpected reassociation target: " + ev)
662         ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
663         if ev is not None:
664             raise Exception("Unexpected scan started")
665         dev[0].dump_monitor()
666     finally:
667         dev[0].request("DISCONNECT")
668         if hapd:
669             hapd.request("DISABLE")
670         if hapd2:
671             hapd2.request("DISABLE")
672         subprocess.call(['iw', 'reg', 'set', '00'])
673         dev[0].flush_scan_cache()
674
675 def start_wnm_tm(ap, country, dev):
676     params = { "ssid": "test-wnm",
677                "country_code": country,
678                "ieee80211d": "1",
679                "hw_mode": "g",
680                "channel": "1",
681                "bss_transition": "1" }
682     hapd = hostapd.add_ap(ap['ifname'], params)
683     id = dev.connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
684     dev.dump_monitor()
685     dev.set_network(id, "scan_freq", "")
686     return hapd, id
687
688 def stop_wnm_tm(hapd, dev):
689     dev.request("DISCONNECT")
690     try:
691         dev.wait_disconnected()
692     except:
693         pass
694     if hapd:
695         hapd.request("DISABLE")
696     subprocess.call(['iw', 'reg', 'set', '00'])
697     dev.flush_scan_cache()
698
699 def wnm_bss_tm_check(hapd, dev, data):
700     addr = dev.p2p_interface_addr()
701     if "OK" not in hapd.request("BSS_TM_REQ " + addr + " " + data):
702         raise Exception("BSS_TM_REQ command failed")
703     ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
704     if ev is None:
705         raise Exception("No scan started")
706     ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
707     if ev is None:
708         raise Exception("Scan did not complete")
709
710     ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
711     if ev is None:
712         raise Exception("No BSS Transition Management Response")
713     if "status_code=7" not in ev:
714         raise Exception("Unexpected response: " + ev)
715
716 def test_wnm_bss_tm_country_us(dev, apdev):
717     """WNM BSS Transition Management (US)"""
718     try:
719         hapd = None
720         hapd, id = start_wnm_tm(apdev[0], "US", dev[0])
721
722         logger.info("Preferred Candidate List (no matching neighbor, known channels)")
723         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,2,52,7,03010a neighbor=00:11:22:33:44:57,0x0000,4,100,7 neighbor=00:11:22:33:44:59,0x0000,3,149,7 neighbor=00:11:22:33:44:5b,0x0000,34,1,7 neighbor=00:11:22:33:44:5d,0x0000,5,149,7")
724
725         # Make the test take less time by limiting full scans
726         dev[0].set_network(id, "scan_freq", "2412")
727         logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
728         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,12,12,7 neighbor=00:11:22:33:44:55,0x0000,2,35,7,03010a neighbor=00:11:22:33:44:56,0x0000,2,65,7 neighbor=00:11:22:33:44:57,0x0000,4,99,7 neighbor=00:11:22:33:44:58,0x0000,4,145,7")
729
730         logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
731         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:59,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,162,7 neighbor=00:11:22:33:44:5b,0x0000,34,0,7 neighbor=00:11:22:33:44:5c,0x0000,34,4,7 neighbor=00:11:22:33:44:5d,0x0000,5,148,7 neighbor=00:11:22:33:44:5e,0x0000,5,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
732     finally:
733         stop_wnm_tm(hapd, dev[0])
734
735 def test_wnm_bss_tm_country_fi(dev, apdev):
736     """WNM BSS Transition Management (FI)"""
737     addr = dev[0].p2p_interface_addr()
738     try:
739         hapd = None
740         hapd, id = start_wnm_tm(apdev[0], "FI", dev[0])
741
742         logger.info("Preferred Candidate List (no matching neighbor, known channels)")
743         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,4,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,100,7 neighbor=00:11:22:33:44:59,0x0000,17,149,7 neighbor=00:11:22:33:44:5c,0x0000,18,1,7")
744
745         # Make the test take less time by limiting full scans
746         dev[0].set_network(id, "scan_freq", "2412")
747         logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
748         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,4,0,7 neighbor=00:11:22:33:44:01,0x0000,4,14,7 neighbor=00:11:22:33:44:02,0x0000,1,35,7 neighbor=00:11:22:33:44:03,0x0000,1,65,7 neighbor=00:11:22:33:44:04,0x0000,3,99,7 neighbor=00:11:22:33:44:05,0x0000,3,141,7 neighbor=00:11:22:33:44:06,0x0000,17,148,7 neighbor=00:11:22:33:44:07,0x0000,17,170,7 neighbor=00:11:22:33:44:08,0x0000,18,0,7 neighbor=00:11:22:33:44:09,0x0000,18,5,7")
749
750         logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
751         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,0,0,7")
752     finally:
753         stop_wnm_tm(hapd, dev[0])
754
755 def test_wnm_bss_tm_country_jp(dev, apdev):
756     """WNM BSS Transition Management (JP)"""
757     addr = dev[0].p2p_interface_addr()
758     try:
759         hapd = None
760         hapd, id = start_wnm_tm(apdev[0], "JP", dev[0])
761
762         logger.info("Preferred Candidate List (no matching neighbor, known channels)")
763         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,31,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,1,36,7 neighbor=00:11:22:33:44:59,0x0000,34,100,7 neighbor=00:11:22:33:44:5c,0x0000,59,1,7")
764
765         # Make the test take less time by limiting full scans
766         dev[0].set_network(id, "scan_freq", "2412")
767         logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
768         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,30,14,7 neighbor=00:11:22:33:44:56,0x0000,31,13,7 neighbor=00:11:22:33:44:57,0x0000,1,33,7 neighbor=00:11:22:33:44:58,0x0000,1,65,7 neighbor=00:11:22:33:44:5a,0x0000,34,99,7 neighbor=00:11:22:33:44:5b,0x0000,34,141,7 neighbor=00:11:22:33:44:5d,0x0000,59,0,7 neighbor=00:11:22:33:44:5e,0x0000,59,4,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
769     finally:
770         stop_wnm_tm(hapd, dev[0])
771
772 def test_wnm_bss_tm_country_cn(dev, apdev):
773     """WNM BSS Transition Management (CN)"""
774     addr = dev[0].p2p_interface_addr()
775     try:
776         hapd = None
777         hapd, id = start_wnm_tm(apdev[0], "CN", dev[0])
778
779         logger.info("Preferred Candidate List (no matching neighbor, known channels)")
780         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,149,7 neighbor=00:11:22:33:44:59,0x0000,6,149,7")
781
782         # Make the test take less time by limiting full scans
783         dev[0].set_network(id, "scan_freq", "2412")
784         logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
785         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,7,14,7 neighbor=00:11:22:33:44:56,0x0000,1,35,7 neighbor=00:11:22:33:44:57,0x0000,1,65,7 neighbor=00:11:22:33:44:58,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
786     finally:
787         stop_wnm_tm(hapd, dev[0])
788
789 def test_wnm_bss_tm_global(dev, apdev):
790     """WNM BSS Transition Management (global)"""
791     addr = dev[0].p2p_interface_addr()
792     try:
793         hapd = None
794         hapd, id = start_wnm_tm(apdev[0], "XX", dev[0])
795
796         logger.info("Preferred Candidate List (no matching neighbor, known channels)")
797         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,82,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,83,1,7 neighbor=00:11:22:33:44:59,0x0000,115,36,7 neighbor=00:11:22:33:44:5a,0x0000,121,100,7 neighbor=00:11:22:33:44:5c,0x0000,124,149,7 neighbor=00:11:22:33:44:5d,0x0000,125,149,7 neighbor=00:11:22:33:44:5e,0x0000,128,42,7 neighbor=00:11:22:33:44:5f,0x0000,129,50,7 neighbor=00:11:22:33:44:60,0x0000,180,1,7")
798
799         # Make the test take less time by limiting full scans
800         dev[0].set_network(id, "scan_freq", "2412")
801         logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
802         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,81,0,7 neighbor=00:11:22:33:44:01,0x0000,81,14,7 neighbor=00:11:22:33:44:02,0x0000,82,13,7 neighbor=00:11:22:33:44:03,0x0000,83,0,7 neighbor=00:11:22:33:44:04,0x0000,83,14,7 neighbor=00:11:22:33:44:05,0x0000,115,35,7 neighbor=00:11:22:33:44:06,0x0000,115,65,7 neighbor=00:11:22:33:44:07,0x0000,121,99,7 neighbor=00:11:22:33:44:08,0x0000,121,141,7 neighbor=00:11:22:33:44:09,0x0000,124,148,7")
803
804         logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
805         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,124,162,7 neighbor=00:11:22:33:44:01,0x0000,125,148,7 neighbor=00:11:22:33:44:02,0x0000,125,170,7 neighbor=00:11:22:33:44:03,0x0000,128,35,7 neighbor=00:11:22:33:44:04,0x0000,128,162,7 neighbor=00:11:22:33:44:05,0x0000,129,49,7 neighbor=00:11:22:33:44:06,0x0000,129,115,7 neighbor=00:11:22:33:44:07,0x0000,180,0,7 neighbor=00:11:22:33:44:08,0x0000,180,5,7 neighbor=00:11:22:33:44:09,0x0000,0,0,7")
806     finally:
807         stop_wnm_tm(hapd, dev[0])
808
809 def test_wnm_bss_tm_op_class_0(dev, apdev):
810     """WNM BSS Transition Management with invalid operating class"""
811     try:
812         hapd = None
813         hapd, id = start_wnm_tm(apdev[0], "US", dev[0])
814
815         logger.info("Preferred Candidate List (no matching neighbor, invalid op class specified for channels)")
816         wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:59,0x0000,0,149,7 neighbor=00:11:22:33:44:5b,0x0000,0,1,7")
817     finally:
818         stop_wnm_tm(hapd, dev[0])
819
820 def test_wnm_action_proto(dev, apdev):
821     """WNM Action protocol testing"""
822     params = { "ssid": "test-wnm" }
823     params['wnm_sleep_mode'] = '1'
824     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
825     bssid = apdev[0]['bssid']
826     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
827     dev[0].request("WNM_SLEEP enter")
828     time.sleep(0.1)
829     hapd.set("ext_mgmt_frame_handling", "1")
830
831     msg = {}
832     msg['fc'] = MGMT_SUBTYPE_ACTION << 4
833     msg['da'] = dev[0].own_addr()
834     msg['sa'] = bssid
835     msg['bssid'] = bssid
836
837     dialog_token = 1
838
839     logger.debug("Unexpected WNM-Notification Response")
840     # Note: This is actually not registered for user space processing in
841     # driver_nl80211.c nl80211_mgmt_subscribe_non_ap() and as such, won't make
842     # it to wpa_supplicant.
843     msg['payload'] = struct.pack("<BBBB",
844                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_RESP,
845                                  dialog_token, 0)
846     hapd.mgmt_tx(msg)
847     expect_ack(hapd)
848
849     logger.debug("Truncated WNM-Notification Request (no Type field)")
850     msg['payload'] = struct.pack("<BBB",
851                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
852                                  dialog_token)
853     hapd.mgmt_tx(msg)
854     expect_ack(hapd)
855
856     logger.debug("WFA WNM-Notification Request with truncated IE (min)")
857     msg['payload'] = struct.pack("<BBBBBB",
858                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
859                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0, 1)
860     hapd.mgmt_tx(msg)
861     expect_ack(hapd)
862
863     logger.debug("WFA WNM-Notification Request with truncated IE (max)")
864     msg['payload'] = struct.pack("<BBBBBB",
865                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
866                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0, 255)
867     hapd.mgmt_tx(msg)
868     expect_ack(hapd)
869
870     logger.debug("WFA WNM-Notification Request with too short IE")
871     msg['payload'] = struct.pack("<BBBBBB",
872                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
873                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0, 0)
874     hapd.mgmt_tx(msg)
875     expect_ack(hapd)
876
877     logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL")
878     msg['payload'] = struct.pack(">BBBBBBLB",
879                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
880                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 5,
881                                  0x506f9a00, 1)
882     hapd.mgmt_tx(msg)
883     expect_ack(hapd)
884
885     logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL(2)")
886     msg['payload'] = struct.pack(">BBBBBBLBB",
887                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
888                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 6,
889                                  0x506f9a00, 1, 0)
890     hapd.mgmt_tx(msg)
891     expect_ack(hapd)
892
893     logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL(3)")
894     msg['payload'] = struct.pack(">BBBBBBLB",
895                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
896                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 5,
897                                  0x506f9a00, 0xff)
898     hapd.mgmt_tx(msg)
899     expect_ack(hapd)
900
901     logger.debug("WFA WNM-Notification Request with truncated Deauth Imminent URL(min)")
902     msg['payload'] = struct.pack(">BBBBBBLBHB",
903                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
904                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 8,
905                                  0x506f9a01, 0, 0, 1)
906     hapd.mgmt_tx(msg)
907     expect_ack(hapd)
908
909     logger.debug("WFA WNM-Notification Request with truncated Deauth Imminent URL(max)")
910     msg['payload'] = struct.pack(">BBBBBBLBHB",
911                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
912                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 8,
913                                  0x506f9a01, 0, 0, 0xff)
914     hapd.mgmt_tx(msg)
915     expect_ack(hapd)
916
917     logger.debug("WFA WNM-Notification Request with unsupported IE")
918     msg['payload'] = struct.pack("<BBBBBBL",
919                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
920                                  dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 4, 0)
921     hapd.mgmt_tx(msg)
922     expect_ack(hapd)
923
924     logger.debug("WNM-Notification Request with unknown WNM-Notification type 0")
925     msg['payload'] = struct.pack("<BBBB",
926                                  ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
927                                  dialog_token, WNM_NOTIF_TYPE_FW_UPGRADE)
928     hapd.mgmt_tx(msg)
929     expect_ack(hapd)
930
931     logger.debug("Truncated WNM Sleep Mode Response - no Dialog Token")
932     msg['payload'] = struct.pack("<BB",
933                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP)
934     hapd.mgmt_tx(msg)
935     expect_ack(hapd)
936
937     logger.debug("Truncated WNM Sleep Mode Response - no Key Data Length")
938     msg['payload'] = struct.pack("<BBB",
939                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0)
940     hapd.mgmt_tx(msg)
941     expect_ack(hapd)
942
943     logger.debug("Truncated WNM Sleep Mode Response - truncated Key Data (min)")
944     msg['payload'] = struct.pack("<BBBH",
945                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
946                                  1)
947     hapd.mgmt_tx(msg)
948     expect_ack(hapd)
949
950     logger.debug("Truncated WNM Sleep Mode Response - truncated Key Data (max)")
951     msg['payload'] = struct.pack("<BBBH",
952                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
953                                  0xffff)
954     hapd.mgmt_tx(msg)
955     expect_ack(hapd)
956
957     logger.debug("WNM Sleep Mode Response - truncated IE header")
958     msg['payload'] = struct.pack("<BBBHB",
959                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
960                                  0, 0)
961     hapd.mgmt_tx(msg)
962     expect_ack(hapd)
963
964     logger.debug("WNM Sleep Mode Response - truncated IE")
965     msg['payload'] = struct.pack("<BBBHBB",
966                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
967                                  0, 0, 1)
968     hapd.mgmt_tx(msg)
969     expect_ack(hapd)
970
971     logger.debug("WNM Sleep Mode Response - Empty TFS Response")
972     msg['payload'] = struct.pack("<BBBHBB",
973                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
974                                  0, WLAN_EID_TFS_RESP, 0)
975     hapd.mgmt_tx(msg)
976     expect_ack(hapd)
977
978     logger.debug("WNM Sleep Mode Response - EID 0 not recognized")
979     msg['payload'] = struct.pack("<BBBHBB",
980                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
981                                  0, 0, 0)
982     hapd.mgmt_tx(msg)
983     expect_ack(hapd)
984
985     logger.debug("WNM Sleep Mode Response - Empty WNM Sleep Mode element and TFS Response element")
986     msg['payload'] = struct.pack("<BBBHBBBB",
987                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
988                                  0, WLAN_EID_WNMSLEEP, 0, WLAN_EID_TFS_RESP, 0)
989     hapd.mgmt_tx(msg)
990     expect_ack(hapd)
991
992     logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element and empty TFS Response element")
993     msg['payload'] = struct.pack("<BBBHBBBBHBB",
994                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
995                                  0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_ENTER,
996                                  WNM_STATUS_SLEEP_ACCEPT, 0,
997                                  WLAN_EID_TFS_RESP, 0)
998     hapd.mgmt_tx(msg)
999     expect_ack(hapd)
1000
1001     logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element(exit, deny key) and empty TFS Response element")
1002     msg['payload'] = struct.pack("<BBBHBBBBHBB",
1003                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1004                                  0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1005                                  WNM_STATUS_DENIED_KEY, 0,
1006                                  WLAN_EID_TFS_RESP, 0)
1007     hapd.mgmt_tx(msg)
1008     expect_ack(hapd)
1009
1010     logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element(enter, deny key) and empty TFS Response element")
1011     msg['payload'] = struct.pack("<BBBHBBBBHBB",
1012                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1013                                  0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_ENTER,
1014                                  WNM_STATUS_DENIED_KEY, 0,
1015                                  WLAN_EID_TFS_RESP, 0)
1016     hapd.mgmt_tx(msg)
1017     expect_ack(hapd)
1018
1019 def test_wnm_action_proto_pmf(dev, apdev):
1020     """WNM Action protocol testing (PMF enabled)"""
1021     ssid = "test-wnm-pmf"
1022     params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
1023     params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
1024     params["ieee80211w"] = "2"
1025     params['wnm_sleep_mode'] = '1'
1026     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
1027     bssid = apdev[0]['bssid']
1028     dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK-SHA256",
1029                    proto="WPA2", ieee80211w="2", scan_freq="2412")
1030     dev[0].request("WNM_SLEEP enter")
1031     time.sleep(0.1)
1032     hapd.set("ext_mgmt_frame_handling", "1")
1033
1034     msg = {}
1035     msg['fc'] = MGMT_SUBTYPE_ACTION << 4
1036     msg['da'] = dev[0].own_addr()
1037     msg['sa'] = bssid
1038     msg['bssid'] = bssid
1039
1040     logger.debug("WNM Sleep Mode Response - Invalid Key Data element length")
1041     keydata = struct.pack("<BB", 0, 1)
1042     msg['payload'] = struct.pack("<BBBH",
1043                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1044                                  len(keydata))
1045     msg['payload'] += keydata
1046     msg['payload'] += struct.pack("<BBBBHBB",
1047                                   WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1048                                   WNM_STATUS_SLEEP_ACCEPT, 0,
1049                                   WLAN_EID_TFS_RESP, 0)
1050     hapd.mgmt_tx(msg)
1051     expect_ack(hapd)
1052
1053     logger.debug("WNM Sleep Mode Response - Too short GTK subelem")
1054     keydata = struct.pack("<BB", WNM_SLEEP_SUBELEM_GTK, 0)
1055     msg['payload'] = struct.pack("<BBBH",
1056                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1057                                  len(keydata))
1058     msg['payload'] += keydata
1059     msg['payload'] += struct.pack("<BBBBHBB",
1060                                   WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1061                                   WNM_STATUS_SLEEP_ACCEPT, 0,
1062                                   WLAN_EID_TFS_RESP, 0)
1063     hapd.mgmt_tx(msg)
1064     expect_ack(hapd)
1065
1066     logger.debug("WNM Sleep Mode Response - Invalid GTK subelem")
1067     keydata = struct.pack("<BBHB2L4L", WNM_SLEEP_SUBELEM_GTK, 11 + 16,
1068                           0, 17, 0, 0, 0, 0, 0, 0)
1069     msg['payload'] = struct.pack("<BBBH",
1070                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1071                                  len(keydata))
1072     msg['payload'] += keydata
1073     msg['payload'] += struct.pack("<BBBBHBB",
1074                                   WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1075                                   WNM_STATUS_SLEEP_ACCEPT, 0,
1076                                   WLAN_EID_TFS_RESP, 0)
1077     hapd.mgmt_tx(msg)
1078     expect_ack(hapd)
1079
1080     logger.debug("WNM Sleep Mode Response - Invalid GTK subelem (2)")
1081     keydata = struct.pack("<BBHB2L4L", WNM_SLEEP_SUBELEM_GTK, 11 + 16,
1082                           0, 0, 0, 0, 0, 0, 0, 0)
1083     msg['payload'] = struct.pack("<BBBH",
1084                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1085                                  len(keydata))
1086     msg['payload'] += keydata
1087     msg['payload'] += struct.pack("<BBBBHBB",
1088                                   WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1089                                   WNM_STATUS_SLEEP_ACCEPT, 0,
1090                                   WLAN_EID_TFS_RESP, 0)
1091     hapd.mgmt_tx(msg)
1092     expect_ack(hapd)
1093
1094     logger.debug("WNM Sleep Mode Response - GTK subelem and too short IGTK subelem")
1095     keydata = struct.pack("<BBHB", WNM_SLEEP_SUBELEM_GTK, 11 + 16, 0, 16)
1096     keydata += struct.pack(">2L4L", 0x01020304, 0x05060708,
1097                            0x11223344, 0x55667788, 0x9900aabb, 0xccddeeff)
1098     keydata += struct.pack("<BB", WNM_SLEEP_SUBELEM_IGTK, 0)
1099     msg['payload'] = struct.pack("<BBBH",
1100                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1101                                  len(keydata))
1102     msg['payload'] += keydata
1103     msg['payload'] += struct.pack("<BBBBHBB",
1104                                   WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1105                                   WNM_STATUS_SLEEP_ACCEPT, 0,
1106                                   WLAN_EID_TFS_RESP, 0)
1107     hapd.mgmt_tx(msg)
1108     expect_ack(hapd)
1109
1110     logger.debug("WNM Sleep Mode Response - Unknown subelem")
1111     keydata = struct.pack("<BB", 255, 0)
1112     msg['payload'] = struct.pack("<BBBH",
1113                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1114                                  len(keydata))
1115     msg['payload'] += keydata
1116     msg['payload'] += struct.pack("<BBBBHBB",
1117                                   WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1118                                   WNM_STATUS_SLEEP_ACCEPT, 0,
1119                                   WLAN_EID_TFS_RESP, 0)
1120     hapd.mgmt_tx(msg)
1121     expect_ack(hapd)
1122
1123 def test_wnm_action_proto_no_pmf(dev, apdev):
1124     """WNM Action protocol testing (PMF disabled)"""
1125     ssid = "test-wnm-no-pmf"
1126     params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
1127     params['wnm_sleep_mode'] = '1'
1128     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
1129     bssid = apdev[0]['bssid']
1130     dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
1131                    proto="WPA2", ieee80211w="0", scan_freq="2412")
1132     dev[0].request("WNM_SLEEP enter")
1133     time.sleep(0.1)
1134     hapd.set("ext_mgmt_frame_handling", "1")
1135
1136     msg = {}
1137     msg['fc'] = MGMT_SUBTYPE_ACTION << 4
1138     msg['da'] = dev[0].own_addr()
1139     msg['sa'] = bssid
1140     msg['bssid'] = bssid
1141
1142     logger.debug("WNM Sleep Mode Response - GTK subelem and IGTK subelem")
1143     keydata = struct.pack("<BBHB", WNM_SLEEP_SUBELEM_GTK, 11 + 16, 0, 16)
1144     keydata += struct.pack(">2L4L", 0x01020304, 0x05060708,
1145                            0x11223344, 0x55667788, 0x9900aabb, 0xccddeeff)
1146     keydata += struct.pack("<BBHLH4L", WNM_SLEEP_SUBELEM_IGTK, 2 + 6 + 16, 0,
1147                            0x10203040, 0x5060,
1148                            0xf1f2f3f4, 0xf5f6f7f8, 0xf9f0fafb, 0xfcfdfeff)
1149     msg['payload'] = struct.pack("<BBBH",
1150                                  ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1151                                  len(keydata))
1152     msg['payload'] += keydata
1153     msg['payload'] += struct.pack("<BBBBHBB",
1154                                   WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
1155                                   WNM_STATUS_SLEEP_ACCEPT, 0,
1156                                   WLAN_EID_TFS_RESP, 0)
1157     hapd.mgmt_tx(msg)
1158     expect_ack(hapd)
1159
1160     ev = dev[0].wait_event(["WNM: Ignore Key Data"], timeout=5)
1161     if ev is None:
1162         raise Exception("Key Data not ignored")
1163
1164 def test_wnm_bss_tm_req_with_mbo_ie(dev, apdev):
1165     """WNM BSS transition request with MBO IE and reassociation delay attribute"""
1166     ssid = "test-wnm-mbo"
1167     params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
1168     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
1169     bssid = apdev[0]['bssid']
1170     if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
1171         raise Exception("Failed to set STA as cellular data capable")
1172
1173     dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
1174                    proto="WPA2", ieee80211w="0", scan_freq="2412")
1175
1176     logger.debug("BTM request with MBO reassociation delay when disassoc imminent is not set")
1177     if 'FAIL' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " mbo=3:2:1"):
1178         raise Exception("BSS transition management succeeded unexpectedly")
1179
1180     logger.debug("BTM request with invalid MBO transition reason code")
1181     if 'FAIL' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " mbo=10:2:1"):
1182         raise Exception("BSS transition management succeeded unexpectedly")
1183
1184     logger.debug("BTM request with MBO reassociation retry delay of 5 seconds")
1185     if 'OK' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " disassoc_imminent=1 disassoc_timer=3 mbo=3:5:1"):
1186         raise Exception("BSS transition management command failed")
1187
1188     ev = dev[0].wait_event(['MBO-CELL-PREFERENCE'], 1)
1189     if ev is None or "preference=1" not in ev:
1190         raise Exception("Timeout waiting for MBO-CELL-PREFERENCE event")
1191
1192     ev = dev[0].wait_event(['MBO-TRANSITION-REASON'], 1)
1193     if ev is None or "reason=3" not in ev:
1194         raise Exception("Timeout waiting for MBO-TRANSITION-REASON event")
1195
1196     ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
1197     if ev is None:
1198         raise Exception("No BSS Transition Management Response")
1199     if dev[0].own_addr() not in ev:
1200         raise Exception("Unexpected BSS Transition Management Response address")
1201
1202     ev = dev[0].wait_event(['CTRL-EVENT-DISCONNECTED'], 5)
1203     if ev is None:
1204             raise Exception("Station did not disconnect although disassoc imminent was set")
1205
1206     # Set the scan interval to make dev[0] look for connections
1207     if 'OK' not in dev[0].request("SCAN_INTERVAL 1"):
1208             raise Exception("Failed to set scan interval")
1209
1210     # Make sure no connection is made during the retry delay
1211     ev = dev[0].wait_event(['CTRL-EVENT-CONNECTED'], 5)
1212     if ev is not None:
1213             raise Exception("Station connected before assoc retry delay was over")
1214
1215     # After the assoc retry delay is over, we can reconnect
1216     ev = dev[0].wait_event(['CTRL-EVENT-CONNECTED'], 5)
1217     if ev is None:
1218             raise Exception("Station did not connect after assoc retry delay is over")
1219
1220     if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
1221         raise Exception("Failed to set STA as cellular data not-capable")
1222
1223 def test_wnm_bss_transition_mgmt_query(dev, apdev):
1224     """WNM BSS Transition Management query"""
1225     params = { "ssid": "test-wnm",
1226                "bss_transition": "1" }
1227     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
1228     params = { "ssid": "another" }
1229     hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
1230
1231     dev[0].scan_for_bss(apdev[1]['bssid'], 2412)
1232     dev[0].scan_for_bss(apdev[0]['bssid'], 2412)
1233
1234     dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
1235     dev[0].request("WNM_BSS_QUERY 0 list")
1236
1237     ev = dev[0].wait_event(["WNM: BSS Transition Management Request"],
1238                            timeout=5)
1239     if ev is None:
1240         raise Exception("No BSS Transition Management Request frame seen")
1241
1242     ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
1243     if ev is None:
1244         raise Exception("No BSS Transition Management Response frame seen")