]> git.ipfire.org Git - thirdparty/hostap.git/blame - tests/hwsim/wpasupplicant.py
tests: Additional invalid NDEF records
[thirdparty/hostap.git] / tests / hwsim / wpasupplicant.py
CommitLineData
1ae73b03 1# Python class for controlling wpa_supplicant
d7a99700 2# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
1ae73b03
JM
3#
4# This software may be distributed under the terms of the BSD license.
5# See README for more details.
6
7import os
8import time
9import logging
daf3806d 10import binascii
c68f9a61 11import re
daf3806d 12import struct
6ca3a98b 13import subprocess
1ae73b03
JM
14import wpaspy
15
c9aa4308 16logger = logging.getLogger()
1ae73b03
JM
17wpas_ctrl = '/var/run/wpa_supplicant'
18
19class WpaSupplicant:
9489637b 20 def __init__(self, ifname=None, global_iface=None):
f3f8ee88 21 self.group_ifname = None
4823566c 22 self.gctrl_mon = None
9489637b
JM
23 if ifname:
24 self.set_ifname(ifname)
25 else:
26 self.ifname = None
1ae73b03 27
0fa28afe
JM
28 self.global_iface = global_iface
29 if global_iface:
30 self.global_ctrl = wpaspy.Ctrl(global_iface)
31 self.global_mon = wpaspy.Ctrl(global_iface)
32 self.global_mon.attach()
c8836a4f
JM
33 else:
34 self.global_mon = None
0fa28afe 35
a66d2248
JM
36 def close_ctrl(self):
37 if self.global_mon:
38 self.global_mon.detach()
39 self.global_mon = None
40 self.global_ctrl = None
41 self.remove_ifname()
42
9489637b
JM
43 def set_ifname(self, ifname):
44 self.ifname = ifname
45 self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
46 self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
47 self.mon.attach()
48
49 def remove_ifname(self):
50 if self.ifname:
51 self.mon.detach()
52 self.mon = None
53 self.ctrl = None
54 self.ifname = None
55
a1512a0c 56 def interface_add(self, ifname, config="", driver="nl80211",
138bf118
JM
57 drv_params=None, br_ifname=None, create=False,
58 set_ifname=True, all_params=False):
9489637b
JM
59 try:
60 groups = subprocess.check_output(["id"])
61 group = "admin" if "(admin)" in groups else "adm"
62 except Exception, e:
63 group = "admin"
117caa4a 64 cmd = "INTERFACE_ADD " + ifname + "\t" + config + "\t" + driver + "\tDIR=/var/run/wpa_supplicant GROUP=" + group
6e917c3e
JM
65 if drv_params:
66 cmd = cmd + '\t' + drv_params
a1512a0c
JM
67 if br_ifname:
68 if not drv_params:
69 cmd += '\t'
70 cmd += '\t' + br_ifname
25f2cb61
JM
71 if create:
72 if not br_ifname:
73 cmd += '\t'
74 if not drv_params:
75 cmd += '\t'
76 cmd += '\tcreate'
138bf118
JM
77 if all_params and not create:
78 if not br_ifname:
79 cmd += '\t'
80 if not drv_params:
81 cmd += '\t'
82 cmd += '\t'
9489637b
JM
83 if "FAIL" in self.global_request(cmd):
84 raise Exception("Failed to add a dynamic wpa_supplicant interface")
138bf118 85 if not create and set_ifname:
25f2cb61 86 self.set_ifname(ifname)
9489637b
JM
87
88 def interface_remove(self, ifname):
89 self.remove_ifname()
90 self.global_request("INTERFACE_REMOVE " + ifname)
91
874057da 92 def request(self, cmd, timeout=10):
1ae73b03 93 logger.debug(self.ifname + ": CTRL: " + cmd)
874057da 94 return self.ctrl.request(cmd, timeout=timeout)
1ae73b03 95
0fa28afe
JM
96 def global_request(self, cmd):
97 if self.global_iface is None:
98 self.request(cmd)
99 else:
9489637b 100 ifname = self.ifname or self.global_iface
521b7e79 101 logger.debug(ifname + ": CTRL(global): " + cmd)
0fa28afe
JM
102 return self.global_ctrl.request(cmd)
103
f3f8ee88
JM
104 def group_request(self, cmd):
105 if self.group_ifname and self.group_ifname != self.ifname:
106 logger.debug(self.group_ifname + ": CTRL: " + cmd)
107 gctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname))
108 return gctrl.request(cmd)
109 return self.request(cmd)
110
1ae73b03
JM
111 def ping(self):
112 return "PONG" in self.request("PING")
113
521b7e79
JM
114 def global_ping(self):
115 return "PONG" in self.global_request("PING")
116
1ae73b03 117 def reset(self):
521b7e79 118 self.dump_monitor()
ea0e92ee
JM
119 res = self.request("FLUSH")
120 if not "OK" in res:
121 logger.info("FLUSH to " + self.ifname + " failed: " + res)
09f60224 122 self.global_request("REMOVE_NETWORK all")
a2168cf4
IP
123 self.global_request("SET p2p_add_cli_chan 0")
124 self.global_request("SET p2p_no_go_freq ")
125 self.global_request("SET p2p_pref_chan ")
126 self.global_request("SET p2p_no_group_iface 1")
127 self.global_request("SET p2p_go_intent 7")
128 self.global_request("P2P_FLUSH")
eec0cc8d 129 self.request("SET ignore_old_scan_res 0")
4823566c
JM
130 if self.gctrl_mon:
131 try:
132 self.gctrl_mon.detach()
133 except:
134 pass
135 self.gctrl_mon = None
f3f8ee88 136 self.group_ifname = None
6edaee9c 137 self.dump_monitor()
6ca3a98b
JM
138
139 iter = 0
140 while iter < 60:
141 state = self.get_driver_status_field("scan_state")
142 if "SCAN_STARTED" in state or "SCAN_REQUESTED" in state:
143 logger.info(self.ifname + ": Waiting for scan operation to complete before continuing")
144 time.sleep(1)
145 else:
146 break
147 iter = iter + 1
148 if iter == 60:
149 logger.error(self.ifname + ": Driver scan state did not clear")
150 print "Trying to clear cfg80211/mac80211 scan state"
151 try:
c4668009 152 cmd = ["ifconfig", self.ifname, "down"]
6ca3a98b
JM
153 subprocess.call(cmd)
154 except subprocess.CalledProcessError, e:
155 logger.info("ifconfig failed: " + str(e.returncode))
156 logger.info(e.output)
157 try:
c4668009 158 cmd = ["ifconfig", self.ifname, "up"]
6ca3a98b
JM
159 subprocess.call(cmd)
160 except subprocess.CalledProcessError, e:
161 logger.info("ifconfig failed: " + str(e.returncode))
162 logger.info(e.output)
c57c1ed6
JM
163 if iter > 0:
164 # The ongoing scan could have discovered BSSes or P2P peers
165 logger.info("Run FLUSH again since scan was in progress")
166 self.request("FLUSH")
b21df6e7 167 self.dump_monitor()
6ca3a98b 168
ea0e92ee
JM
169 if not self.ping():
170 logger.info("No PING response from " + self.ifname + " after reset")
1ae73b03 171
07a2e61b
JM
172 def add_network(self):
173 id = self.request("ADD_NETWORK")
174 if "FAIL" in id:
175 raise Exception("ADD_NETWORK failed")
176 return int(id)
177
178 def remove_network(self, id):
179 id = self.request("REMOVE_NETWORK " + str(id))
180 if "FAIL" in id:
181 raise Exception("REMOVE_NETWORK failed")
182 return None
183
ca5b81a5
JM
184 def get_network(self, id, field):
185 res = self.request("GET_NETWORK " + str(id) + " " + field)
186 if res == "FAIL\n":
187 return None
188 return res
189
07a2e61b
JM
190 def set_network(self, id, field, value):
191 res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
192 if "FAIL" in res:
193 raise Exception("SET_NETWORK failed")
194 return None
195
196 def set_network_quoted(self, id, field, value):
197 res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
198 if "FAIL" in res:
199 raise Exception("SET_NETWORK failed")
200 return None
201
8e751cfa
BR
202 def list_networks(self, p2p=False):
203 if p2p:
204 res = self.global_request("LIST_NETWORKS")
205 else:
206 res = self.request("LIST_NETWORKS")
6a0b4002
JM
207 lines = res.splitlines()
208 networks = []
209 for l in lines:
210 if "network id" in l:
211 continue
212 [id,ssid,bssid,flags] = l.split('\t')
213 network = {}
214 network['id'] = id
215 network['ssid'] = ssid
216 network['bssid'] = bssid
217 network['flags'] = flags
218 networks.append(network)
219 return networks
220
543f9f7e 221 def hs20_enable(self, auto_interworking=False):
bbe86767
JM
222 self.request("SET interworking 1")
223 self.request("SET hs20 1")
543f9f7e
JM
224 if auto_interworking:
225 self.request("SET auto_interworking 1")
226 else:
227 self.request("SET auto_interworking 0")
bbe86767 228
22653762
JM
229 def interworking_add_network(self, bssid):
230 id = self.request("INTERWORKING_ADD_NETWORK " + bssid)
231 if "FAIL" in id or "OK" in id:
232 raise Exception("INTERWORKING_ADD_NETWORK failed")
233 return int(id)
234
93a06242
JM
235 def add_cred(self):
236 id = self.request("ADD_CRED")
237 if "FAIL" in id:
238 raise Exception("ADD_CRED failed")
239 return int(id)
240
241 def remove_cred(self, id):
242 id = self.request("REMOVE_CRED " + str(id))
243 if "FAIL" in id:
244 raise Exception("REMOVE_CRED failed")
245 return None
246
247 def set_cred(self, id, field, value):
248 res = self.request("SET_CRED " + str(id) + " " + field + " " + value)
249 if "FAIL" in res:
250 raise Exception("SET_CRED failed")
251 return None
252
253 def set_cred_quoted(self, id, field, value):
254 res = self.request("SET_CRED " + str(id) + " " + field + ' "' + value + '"')
255 if "FAIL" in res:
256 raise Exception("SET_CRED failed")
257 return None
258
aa45859e
JM
259 def get_cred(self, id, field):
260 return self.request("GET_CRED " + str(id) + " " + field)
261
2232edf8 262 def add_cred_values(self, params):
bbe86767 263 id = self.add_cred()
2232edf8 264
d355372c 265 quoted = [ "realm", "username", "password", "domain", "imsi",
dcd68168 266 "excluded_ssid", "milenage", "ca_cert", "client_cert",
2253ea44 267 "private_key", "domain_suffix_match", "provisioning_sp",
e2afdef2 268 "roaming_partner", "phase1", "phase2" ]
2232edf8
JM
269 for field in quoted:
270 if field in params:
271 self.set_cred_quoted(id, field, params[field])
272
a96066a5 273 not_quoted = [ "eap", "roaming_consortium", "priority",
19839f8e 274 "required_roaming_consortium", "sp_priority",
9714fbcd
JM
275 "max_bss_load", "update_identifier", "req_conn_capab",
276 "min_dl_bandwidth_home", "min_ul_bandwidth_home",
277 "min_dl_bandwidth_roaming", "min_ul_bandwidth_roaming" ]
2232edf8
JM
278 for field in not_quoted:
279 if field in params:
280 self.set_cred(id, field, params[field])
281
bbe86767
JM
282 return id;
283
ca4fd182
JM
284 def select_network(self, id, freq=None):
285 if freq:
22653762 286 extra = " freq=" + str(freq)
ca4fd182
JM
287 else:
288 extra = ""
289 id = self.request("SELECT_NETWORK " + str(id) + extra)
81266da7
JM
290 if "FAIL" in id:
291 raise Exception("SELECT_NETWORK failed")
292 return None
293
68157c06
JL
294 def mesh_group_add(self, id):
295 id = self.request("MESH_GROUP_ADD " + str(id))
296 if "FAIL" in id:
297 raise Exception("MESH_GROUP_ADD failed")
298 return None
299
300 def mesh_group_remove(self):
301 id = self.request("MESH_GROUP_REMOVE " + str(self.ifname))
302 if "FAIL" in id:
303 raise Exception("MESH_GROUP_REMOVE failed")
304 return None
305
7559ad7a 306 def connect_network(self, id, timeout=10):
81266da7
JM
307 self.dump_monitor()
308 self.select_network(id)
5f35a5e2 309 self.wait_connected(timeout=timeout)
81266da7
JM
310 self.dump_monitor()
311
f44c45ac
JM
312 def get_status(self, extra=None):
313 if extra:
314 extra = "-" + extra
315 else:
316 extra = ""
317 res = self.request("STATUS" + extra)
1ae73b03 318 lines = res.splitlines()
302b7a1b 319 vals = dict()
1ae73b03 320 for l in lines:
e01929c6
JM
321 try:
322 [name,value] = l.split('=', 1)
323 vals[name] = value
324 except ValueError, e:
325 logger.info(self.ifname + ": Ignore unexpected STATUS line: " + l)
302b7a1b
JM
326 return vals
327
f44c45ac
JM
328 def get_status_field(self, field, extra=None):
329 vals = self.get_status(extra)
302b7a1b
JM
330 if field in vals:
331 return vals[field]
1ae73b03
JM
332 return None
333
f44c45ac
JM
334 def get_group_status(self, extra=None):
335 if extra:
336 extra = "-" + extra
337 else:
338 extra = ""
339 res = self.group_request("STATUS" + extra)
7cb08cdb 340 lines = res.splitlines()
302b7a1b 341 vals = dict()
7cb08cdb 342 for l in lines:
6bb9d861
JB
343 try:
344 [name,value] = l.split('=', 1)
345 except ValueError:
346 logger.info(self.ifname + ": Ignore unexpected status line: " + l)
347 continue
302b7a1b
JM
348 vals[name] = value
349 return vals
350
f44c45ac
JM
351 def get_group_status_field(self, field, extra=None):
352 vals = self.get_group_status(extra)
302b7a1b
JM
353 if field in vals:
354 return vals[field]
7cb08cdb
JM
355 return None
356
6ca3a98b
JM
357 def get_driver_status(self):
358 res = self.request("STATUS-DRIVER")
359 lines = res.splitlines()
360 vals = dict()
361 for l in lines:
6bb9d861
JB
362 try:
363 [name,value] = l.split('=', 1)
364 except ValueError:
365 logger.info(self.ifname + ": Ignore unexpected status-driver line: " + l)
366 continue
6ca3a98b
JM
367 vals[name] = value
368 return vals
369
370 def get_driver_status_field(self, field):
371 vals = self.get_driver_status()
372 if field in vals:
373 return vals[field]
374 return None
375
5fe7a426
HD
376 def get_mcc(self):
377 mcc = int(self.get_driver_status_field('capa.num_multichan_concurrent'))
378 return 1 if mcc < 2 else mcc
379
44bb9106
JM
380 def get_mib(self):
381 res = self.request("MIB")
382 lines = res.splitlines()
383 vals = dict()
384 for l in lines:
385 try:
386 [name,value] = l.split('=', 1)
387 vals[name] = value
388 except ValueError, e:
389 logger.info(self.ifname + ": Ignore unexpected MIB line: " + l)
390 return vals
391
1ae73b03 392 def p2p_dev_addr(self):
302b7a1b 393 return self.get_status_field("p2p_device_address")
1ae73b03 394
7cb08cdb 395 def p2p_interface_addr(self):
302b7a1b 396 return self.get_group_status_field("address")
7cb08cdb 397
f6420942
JM
398 def own_addr(self):
399 try:
400 res = self.p2p_interface_addr()
401 except:
402 res = self.p2p_dev_addr()
403 return res
404
1ae73b03 405 def p2p_listen(self):
0fa28afe 406 return self.global_request("P2P_LISTEN")
1ae73b03 407
7457c635 408 def p2p_find(self, social=False, progressive=False, dev_id=None,
6d0b4474 409 dev_type=None, delay=None, freq=None):
c70ebce0 410 cmd = "P2P_FIND"
1ae73b03 411 if social:
c70ebce0 412 cmd = cmd + " type=social"
5070b14a
JM
413 elif progressive:
414 cmd = cmd + " type=progressive"
c70ebce0
JM
415 if dev_id:
416 cmd = cmd + " dev_id=" + dev_id
417 if dev_type:
418 cmd = cmd + " dev_type=" + dev_type
7457c635
JM
419 if delay:
420 cmd = cmd + " delay=" + str(delay)
6d0b4474
JM
421 if freq:
422 cmd = cmd + " freq=" + str(freq)
c70ebce0 423 return self.global_request(cmd)
1ae73b03 424
5743006d 425 def p2p_stop_find(self):
0fa28afe 426 return self.global_request("P2P_STOP_FIND")
5743006d 427
1ae73b03 428 def wps_read_pin(self):
861671b6
JM
429 self.pin = self.request("WPS_PIN get").rstrip("\n")
430 if "FAIL" in self.pin:
431 raise Exception("Could not generate PIN")
1ae73b03
JM
432 return self.pin
433
434 def peer_known(self, peer, full=True):
0fa28afe 435 res = self.global_request("P2P_PEER " + peer)
1ae73b03
JM
436 if peer.lower() not in res.lower():
437 return False
438 if not full:
439 return True
440 return "[PROBE_REQ_ONLY]" not in res
441
d4b21766 442 def discover_peer(self, peer, full=True, timeout=15, social=True, force_find=False):
1ae73b03 443 logger.info(self.ifname + ": Trying to discover peer " + peer)
d4b21766 444 if not force_find and self.peer_known(peer, full):
1ae73b03 445 return True
d963f037 446 self.p2p_find(social)
1ae73b03 447 count = 0
ee3f9f38
JM
448 while count < timeout * 4:
449 time.sleep(0.25)
1ae73b03
JM
450 count = count + 1
451 if self.peer_known(peer, full):
452 return True
453 return False
454
451afb4f
JM
455 def get_peer(self, peer):
456 res = self.global_request("P2P_PEER " + peer)
457 if peer.lower() not in res.lower():
458 raise Exception("Peer information not available")
459 lines = res.splitlines()
460 vals = dict()
461 for l in lines:
462 if '=' in l:
463 [name,value] = l.split('=', 1)
464 vals[name] = value
465 return vals
466
46f2cfce 467 def group_form_result(self, ev, expect_failure=False, go_neg_res=None):
f7b1a750
JM
468 if expect_failure:
469 if "P2P-GROUP-STARTED" in ev:
470 raise Exception("Group formation succeeded when expecting failure")
471 exp = r'<.>(P2P-GO-NEG-FAILURE) status=([0-9]*)'
472 s = re.split(exp, ev)
473 if len(s) < 3:
474 return None
475 res = {}
476 res['result'] = 'go-neg-failed'
477 res['status'] = int(s[2])
478 return res
479
480 if "P2P-GROUP-STARTED" not in ev:
481 raise Exception("No P2P-GROUP-STARTED event seen")
482
c9dc5623 483 exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*) ip_addr=([0-9.]*) ip_mask=([0-9.]*) go_ip_addr=([0-9.]*)'
c68f9a61 484 s = re.split(exp, ev)
c9dc5623
JM
485 if len(s) < 11:
486 exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*)'
487 s = re.split(exp, ev)
488 if len(s) < 8:
489 raise Exception("Could not parse P2P-GROUP-STARTED")
c68f9a61
JM
490 res = {}
491 res['result'] = 'success'
492 res['ifname'] = s[2]
f3f8ee88 493 self.group_ifname = s[2]
4823566c
JM
494 try:
495 self.gctrl_mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname))
496 self.gctrl_mon.attach()
497 except:
498 logger.debug("Could not open monitor socket for group interface")
499 self.gctrl_mon = None
c68f9a61
JM
500 res['role'] = s[3]
501 res['ssid'] = s[4]
502 res['freq'] = s[5]
451afb4f
JM
503 if "[PERSISTENT]" in ev:
504 res['persistent'] = True
505 else:
506 res['persistent'] = False
c68f9a61
JM
507 p = re.match(r'psk=([0-9a-f]*)', s[6])
508 if p:
509 res['psk'] = p.group(1)
510 p = re.match(r'passphrase="(.*)"', s[6])
511 if p:
512 res['passphrase'] = p.group(1)
513 res['go_dev_addr'] = s[7]
46f2cfce 514
c9dc5623
JM
515 if len(s) > 8 and len(s[8]) > 0:
516 res['ip_addr'] = s[8]
517 if len(s) > 9:
518 res['ip_mask'] = s[9]
519 if len(s) > 10:
520 res['go_ip_addr'] = s[10]
521
46f2cfce
JM
522 if go_neg_res:
523 exp = r'<.>(P2P-GO-NEG-SUCCESS) role=(GO|client) freq=([0-9]*)'
524 s = re.split(exp, go_neg_res)
525 if len(s) < 4:
526 raise Exception("Could not parse P2P-GO-NEG-SUCCESS")
527 res['go_neg_role'] = s[2]
528 res['go_neg_freq'] = s[3]
529
c68f9a61
JM
530 return res
531
46f2cfce 532 def p2p_go_neg_auth(self, peer, pin, method, go_intent=None, persistent=False, freq=None):
1ae73b03
JM
533 if not self.discover_peer(peer):
534 raise Exception("Peer " + peer + " not found")
535 self.dump_monitor()
1f53fe03
JM
536 if pin:
537 cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth"
538 else:
539 cmd = "P2P_CONNECT " + peer + " " + method + " auth"
809079d3
JM
540 if go_intent:
541 cmd = cmd + ' go_intent=' + str(go_intent)
46f2cfce
JM
542 if freq:
543 cmd = cmd + ' freq=' + str(freq)
451afb4f
JM
544 if persistent:
545 cmd = cmd + " persistent"
0fa28afe 546 if "OK" in self.global_request(cmd):
1ae73b03
JM
547 return None
548 raise Exception("P2P_CONNECT (auth) failed")
549
809079d3 550 def p2p_go_neg_auth_result(self, timeout=1, expect_failure=False):
46f2cfce
JM
551 go_neg_res = None
552 ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
553 "P2P-GO-NEG-FAILURE"], timeout);
c68f9a61 554 if ev is None:
809079d3
JM
555 if expect_failure:
556 return None
c68f9a61 557 raise Exception("Group formation timed out")
46f2cfce
JM
558 if "P2P-GO-NEG-SUCCESS" in ev:
559 go_neg_res = ev
560 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout);
561 if ev is None:
562 if expect_failure:
563 return None
564 raise Exception("Group formation timed out")
c68f9a61 565 self.dump_monitor()
46f2cfce 566 return self.group_form_result(ev, expect_failure, go_neg_res)
c68f9a61 567
b43ac5d5 568 def p2p_go_neg_init(self, peer, pin, method, timeout=0, go_intent=None, expect_failure=False, persistent=False, persistent_id=None, freq=None, provdisc=False, wait_group=True):
1ae73b03
JM
569 if not self.discover_peer(peer):
570 raise Exception("Peer " + peer + " not found")
571 self.dump_monitor()
1a4d80b8
JM
572 if pin:
573 cmd = "P2P_CONNECT " + peer + " " + pin + " " + method
574 else:
575 cmd = "P2P_CONNECT " + peer + " " + method
809079d3
JM
576 if go_intent:
577 cmd = cmd + ' go_intent=' + str(go_intent)
ef2bd5a3
JM
578 if freq:
579 cmd = cmd + ' freq=' + str(freq)
451afb4f
JM
580 if persistent:
581 cmd = cmd + " persistent"
bdc44764
JM
582 elif persistent_id:
583 cmd = cmd + " persistent=" + persistent_id
08e4bd87
JM
584 if provdisc:
585 cmd = cmd + " provdisc"
0fa28afe 586 if "OK" in self.global_request(cmd):
1ae73b03
JM
587 if timeout == 0:
588 self.dump_monitor()
589 return None
46f2cfce
JM
590 go_neg_res = None
591 ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
592 "P2P-GO-NEG-FAILURE"], timeout)
c68f9a61 593 if ev is None:
809079d3
JM
594 if expect_failure:
595 return None
c68f9a61 596 raise Exception("Group formation timed out")
46f2cfce 597 if "P2P-GO-NEG-SUCCESS" in ev:
b43ac5d5
JM
598 if not wait_group:
599 return ev
46f2cfce
JM
600 go_neg_res = ev
601 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
602 if ev is None:
603 if expect_failure:
604 return None
605 raise Exception("Group formation timed out")
c68f9a61 606 self.dump_monitor()
46f2cfce 607 return self.group_form_result(ev, expect_failure, go_neg_res)
1ae73b03
JM
608 raise Exception("P2P_CONNECT failed")
609
cf9189b9 610 def wait_event(self, events, timeout=10):
36408936
JM
611 start = os.times()[4]
612 while True:
1ae73b03
JM
613 while self.mon.pending():
614 ev = self.mon.recv()
809079d3 615 logger.debug(self.ifname + ": " + ev)
f7b1a750
JM
616 for event in events:
617 if event in ev:
618 return ev
36408936
JM
619 now = os.times()[4]
620 remaining = start + timeout - now
621 if remaining <= 0:
622 break
623 if not self.mon.pending(timeout=remaining):
624 break
c68f9a61 625 return None
1ae73b03 626
0fa28afe
JM
627 def wait_global_event(self, events, timeout):
628 if self.global_iface is None:
629 self.wait_event(events, timeout)
630 else:
36408936
JM
631 start = os.times()[4]
632 while True:
0fa28afe
JM
633 while self.global_mon.pending():
634 ev = self.global_mon.recv()
9d507452 635 logger.debug(self.ifname + "(global): " + ev)
0fa28afe
JM
636 for event in events:
637 if event in ev:
638 return ev
36408936
JM
639 now = os.times()[4]
640 remaining = start + timeout - now
641 if remaining <= 0:
642 break
643 if not self.global_mon.pending(timeout=remaining):
644 break
0fa28afe
JM
645 return None
646
4823566c
JM
647 def wait_group_event(self, events, timeout=10):
648 if self.group_ifname and self.group_ifname != self.ifname:
649 if self.gctrl_mon is None:
650 return None
651 start = os.times()[4]
652 while True:
653 while self.gctrl_mon.pending():
654 ev = self.gctrl_mon.recv()
655 logger.debug(self.group_ifname + ": " + ev)
656 for event in events:
657 if event in ev:
658 return ev
659 now = os.times()[4]
660 remaining = start + timeout - now
661 if remaining <= 0:
662 break
663 if not self.gctrl_mon.pending(timeout=remaining):
664 break
665 return None
666
667 return self.wait_event(events, timeout)
668
2c914e24 669 def wait_go_ending_session(self):
4823566c
JM
670 if self.gctrl_mon:
671 try:
672 self.gctrl_mon.detach()
673 except:
674 pass
675 self.gctrl_mon = None
b0d697be 676 ev = self.wait_global_event(["P2P-GROUP-REMOVED"], timeout=3)
2c914e24
JM
677 if ev is None:
678 raise Exception("Group removal event timed out")
679 if "reason=GO_ENDING_SESSION" not in ev:
680 raise Exception("Unexpected group removal reason")
681
1ae73b03
JM
682 def dump_monitor(self):
683 while self.mon.pending():
684 ev = self.mon.recv()
685 logger.debug(self.ifname + ": " + ev)
c8836a4f 686 while self.global_mon and self.global_mon.pending():
9d507452
JM
687 ev = self.global_mon.recv()
688 logger.debug(self.ifname + "(global): " + ev)
3eb29b7b 689
a311c61d 690 def remove_group(self, ifname=None):
4823566c
JM
691 if self.gctrl_mon:
692 try:
693 self.gctrl_mon.detach()
694 except:
695 pass
696 self.gctrl_mon = None
a311c61d 697 if ifname is None:
451afb4f 698 ifname = self.group_ifname if self.group_ifname else self.ifname
0fa28afe 699 if "OK" not in self.global_request("P2P_GROUP_REMOVE " + ifname):
3eb29b7b 700 raise Exception("Group could not be removed")
f3f8ee88 701 self.group_ifname = None
4ea8d3b5 702
25057d92 703 def p2p_start_go(self, persistent=None, freq=None, no_event_clear=False):
4ea8d3b5
JM
704 self.dump_monitor()
705 cmd = "P2P_GROUP_ADD"
07a2e61b
JM
706 if persistent is None:
707 pass
708 elif persistent is True:
709 cmd = cmd + " persistent"
710 else:
711 cmd = cmd + " persistent=" + str(persistent)
5924d4c1 712 if freq:
ef2bd5a3 713 cmd = cmd + " freq=" + str(freq)
0fa28afe
JM
714 if "OK" in self.global_request(cmd):
715 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
4ea8d3b5
JM
716 if ev is None:
717 raise Exception("GO start up timed out")
25057d92
JM
718 if not no_event_clear:
719 self.dump_monitor()
4ea8d3b5
JM
720 return self.group_form_result(ev)
721 raise Exception("P2P_GROUP_ADD failed")
722
723 def p2p_go_authorize_client(self, pin):
724 cmd = "WPS_PIN any " + pin
f3f8ee88 725 if "FAIL" in self.group_request(cmd):
4ea8d3b5
JM
726 raise Exception("Failed to authorize client connection on GO")
727 return None
728
b162675f
JM
729 def p2p_go_authorize_client_pbc(self):
730 cmd = "WPS_PBC"
731 if "FAIL" in self.group_request(cmd):
732 raise Exception("Failed to authorize client connection on GO")
733 return None
734
ee3f9f38
JM
735 def p2p_connect_group(self, go_addr, pin, timeout=0, social=False,
736 freq=None):
4ea8d3b5 737 self.dump_monitor()
41af1305 738 if not self.discover_peer(go_addr, social=social):
54c20c9b
JM
739 if social or not self.discover_peer(go_addr, social=social):
740 raise Exception("GO " + go_addr + " not found")
4ea8d3b5
JM
741 self.dump_monitor()
742 cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
ee3f9f38
JM
743 if freq:
744 cmd += " freq=" + str(freq)
0fa28afe 745 if "OK" in self.global_request(cmd):
4ea8d3b5
JM
746 if timeout == 0:
747 self.dump_monitor()
748 return None
0fa28afe 749 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
4ea8d3b5
JM
750 if ev is None:
751 raise Exception("Joining the group timed out")
752 self.dump_monitor()
753 return self.group_form_result(ev)
754 raise Exception("P2P_CONNECT(join) failed")
7cb08cdb
JM
755
756 def tdls_setup(self, peer):
757 cmd = "TDLS_SETUP " + peer
758 if "FAIL" in self.group_request(cmd):
759 raise Exception("Failed to request TDLS setup")
760 return None
761
762 def tdls_teardown(self, peer):
763 cmd = "TDLS_TEARDOWN " + peer
764 if "FAIL" in self.group_request(cmd):
765 raise Exception("Failed to request TDLS teardown")
766 return None
b61e418c 767
2380d804
OG
768 def tdls_link_status(self, peer):
769 cmd = "TDLS_LINK_STATUS " + peer
770 ret = self.group_request(cmd)
771 if "FAIL" in ret:
772 raise Exception("Failed to request TDLS link status")
773 return ret
774
bc32f830
EP
775 def tspecs(self):
776 """Return (tsid, up) tuples representing current tspecs"""
777 res = self.request("WMM_AC_STATUS")
778 tspecs = re.findall(r"TSID=(\d+) UP=(\d+)", res)
779 tspecs = [tuple(map(int, tspec)) for tspec in tspecs]
780
781 logger.debug("tspecs: " + str(tspecs))
782 return tspecs
783
ceb767d5
JM
784 def add_ts(self, tsid, up, direction="downlink", expect_failure=False,
785 extra=None):
76133458
EP
786 params = {
787 "sba": 9000,
788 "nominal_msdu_size": 1500,
789 "min_phy_rate": 6000000,
790 "mean_data_rate": 1500,
791 }
ceb767d5 792 cmd = "WMM_AC_ADDTS %s tsid=%d up=%d" % (direction, tsid, up)
76133458
EP
793 for (key, value) in params.iteritems():
794 cmd += " %s=%d" % (key, value)
ceb767d5
JM
795 if extra:
796 cmd += " " + extra
76133458
EP
797
798 if self.request(cmd).strip() != "OK":
799 raise Exception("ADDTS failed (tsid=%d up=%d)" % (tsid, up))
800
ceb767d5
JM
801 if expect_failure:
802 ev = self.wait_event(["TSPEC-REQ-FAILED"], timeout=2)
803 if ev is None:
804 raise Exception("ADDTS failed (time out while waiting failure)")
805 if "tsid=%d" % (tsid) not in ev:
806 raise Exception("ADDTS failed (invalid tsid in TSPEC-REQ-FAILED")
807 return
808
76133458
EP
809 ev = self.wait_event(["TSPEC-ADDED"], timeout=1)
810 if ev is None:
811 raise Exception("ADDTS failed (time out)")
812 if "tsid=%d" % (tsid) not in ev:
813 raise Exception("ADDTS failed (invalid tsid in TSPEC-ADDED)")
814
bc32f830
EP
815 if not (tsid, up) in self.tspecs():
816 raise Exception("ADDTS failed (tsid not in tspec list)")
817
76133458
EP
818 def del_ts(self, tsid):
819 if self.request("WMM_AC_DELTS %d" % (tsid)).strip() != "OK":
820 raise Exception("DELTS failed")
821
822 ev = self.wait_event(["TSPEC-REMOVED"], timeout=1)
823 if ev is None:
824 raise Exception("DELTS failed (time out)")
825 if "tsid=%d" % (tsid) not in ev:
826 raise Exception("DELTS failed (invalid tsid in TSPEC-REMOVED)")
827
bc32f830
EP
828 tspecs = [(t, u) for (t, u) in self.tspecs() if t == tsid]
829 if tspecs:
830 raise Exception("DELTS failed (still in tspec list)")
831
6f939e59 832 def connect(self, ssid=None, ssid2=None, **kwargs):
b61e418c
JM
833 logger.info("Connect STA " + self.ifname + " to AP")
834 id = self.add_network()
d78f3303
JM
835 if ssid:
836 self.set_network_quoted(id, "ssid", ssid)
837 elif ssid2:
838 self.set_network(id, "ssid", ssid2)
6f939e59
JM
839
840 quoted = [ "psk", "identity", "anonymous_identity", "password",
841 "ca_cert", "client_cert", "private_key",
842 "private_key_passwd", "ca_cert2", "client_cert2",
843 "private_key2", "phase1", "phase2", "domain_suffix_match",
31ec9faf 844 "altsubject_match", "subject_match", "pac_file", "dh_file",
061cbb25
JM
845 "bgscan", "ht_mcs", "id_str", "openssl_ciphers",
846 "domain_match" ]
6f939e59
JM
847 for field in quoted:
848 if field in kwargs and kwargs[field]:
849 self.set_network_quoted(id, field, kwargs[field])
850
851 not_quoted = [ "proto", "key_mgmt", "ieee80211w", "pairwise",
168d4b09
JM
852 "group", "wep_key0", "wep_key1", "wep_key2", "wep_key3",
853 "wep_tx_keyidx", "scan_freq", "eap",
fb5c8cea 854 "eapol_flags", "fragment_size", "scan_ssid", "auth_alg",
08429720
JM
855 "wpa_ptk_rekey", "disable_ht", "disable_vht", "bssid",
856 "disable_max_amsdu", "ampdu_factor", "ampdu_density",
bd6bb3e3 857 "disable_ht40", "disable_sgi", "disable_ldpc",
acc9a635 858 "ht40_intolerant", "update_identifier", "mac_addr",
c2096d99 859 "erp", "bg_scan_period", "bssid_blacklist",
febf5752 860 "bssid_whitelist", "mem_only_psk", "eap_workaround" ]
6f939e59
JM
861 for field in not_quoted:
862 if field in kwargs and kwargs[field]:
863 self.set_network(id, field, kwargs[field])
864
865 if "raw_psk" in kwargs and kwargs['raw_psk']:
866 self.set_network(id, "psk", kwargs['raw_psk'])
867 if "password_hex" in kwargs and kwargs['password_hex']:
868 self.set_network(id, "password", kwargs['password_hex'])
869 if "peerkey" in kwargs and kwargs['peerkey']:
4a5a5792 870 self.set_network(id, "peerkey", "1")
6f939e59 871 if "okc" in kwargs and kwargs['okc']:
0fab9ce6 872 self.set_network(id, "proactive_key_caching", "1")
6f939e59
JM
873 if "ocsp" in kwargs and kwargs['ocsp']:
874 self.set_network(id, "ocsp", str(kwargs['ocsp']))
875 if "only_add_network" in kwargs and kwargs['only_add_network']:
a6cf5cd6 876 return id
6f939e59
JM
877 if "wait_connect" not in kwargs or kwargs['wait_connect']:
878 if "eap" in kwargs:
7559ad7a
JM
879 self.connect_network(id, timeout=20)
880 else:
881 self.connect_network(id)
9626962d
JM
882 else:
883 self.dump_monitor()
884 self.select_network(id)
709f18d5 885 return id
5126138c 886
b3ec107c 887 def scan(self, type=None, freq=None, no_wait=False, only_new=False):
5126138c
JM
888 if type:
889 cmd = "SCAN TYPE=" + type
890 else:
891 cmd = "SCAN"
0589f401 892 if freq:
243dcc4a 893 cmd = cmd + " freq=" + str(freq)
b3ec107c
JM
894 if only_new:
895 cmd += " only_new=1"
d7a99700
JM
896 if not no_wait:
897 self.dump_monitor()
5126138c
JM
898 if not "OK" in self.request(cmd):
899 raise Exception("Failed to trigger scan")
d7a99700
JM
900 if no_wait:
901 return
5126138c
JM
902 ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
903 if ev is None:
904 raise Exception("Scan timed out")
905
d2432b8b 906 def scan_for_bss(self, bssid, freq=None, force_scan=False, only_new=False):
841bed04
JM
907 if not force_scan and self.get_bss(bssid) is not None:
908 return
909 for i in range(0, 10):
d2432b8b 910 self.scan(freq=freq, type="ONLY", only_new=only_new)
841bed04
JM
911 if self.get_bss(bssid) is not None:
912 return
913 raise Exception("Could not find BSS " + bssid + " in scan")
914
970a23f6 915 def flush_scan_cache(self, freq=2417):
243dcc4a 916 self.request("BSS_FLUSH 0")
970a23f6 917 self.scan(freq=freq, only_new=True)
c1e38fec
JM
918 res = self.request("SCAN_RESULTS")
919 if len(res.splitlines()) > 1:
920 self.request("BSS_FLUSH 0")
921 self.scan(freq=2422, only_new=True)
922 res = self.request("SCAN_RESULTS")
923 if len(res.splitlines()) > 1:
924 logger.info("flush_scan_cache: Could not clear all BSS entries. These remain:\n" + res)
243dcc4a 925
3b808945 926 def roam(self, bssid, fail_test=False):
5126138c 927 self.dump_monitor()
655bc8bf
JM
928 if "OK" not in self.request("ROAM " + bssid):
929 raise Exception("ROAM failed")
3b808945
JM
930 if fail_test:
931 ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
932 if ev is not None:
933 raise Exception("Unexpected connection")
934 self.dump_monitor()
935 return
5f35a5e2 936 self.wait_connected(timeout=10, error="Roaming with the AP timed out")
5126138c 937 self.dump_monitor()
6edaee9c 938
3b808945 939 def roam_over_ds(self, bssid, fail_test=False):
b553eab1 940 self.dump_monitor()
655bc8bf
JM
941 if "OK" not in self.request("FT_DS " + bssid):
942 raise Exception("FT_DS failed")
3b808945
JM
943 if fail_test:
944 ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
945 if ev is not None:
946 raise Exception("Unexpected connection")
947 self.dump_monitor()
948 return
5f35a5e2 949 self.wait_connected(timeout=10, error="Roaming with the AP timed out")
b553eab1
JM
950 self.dump_monitor()
951
6edaee9c 952 def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None,
6645ff50 953 new_passphrase=None, no_wait=False):
6edaee9c
JM
954 self.dump_monitor()
955 if new_ssid:
956 self.request("WPS_REG " + bssid + " " + pin + " " +
957 new_ssid.encode("hex") + " " + key_mgmt + " " +
958 cipher + " " + new_passphrase.encode("hex"))
6645ff50
JM
959 if no_wait:
960 return
6edaee9c
JM
961 ev = self.wait_event(["WPS-SUCCESS"], timeout=15)
962 else:
963 self.request("WPS_REG " + bssid + " " + pin)
6645ff50
JM
964 if no_wait:
965 return
6edaee9c
JM
966 ev = self.wait_event(["WPS-CRED-RECEIVED"], timeout=15)
967 if ev is None:
968 raise Exception("WPS cred timed out")
969 ev = self.wait_event(["WPS-FAIL"], timeout=15)
970 if ev is None:
971 raise Exception("WPS timed out")
5f35a5e2 972 self.wait_connected(timeout=15)
57661377
JM
973
974 def relog(self):
5f797376 975 self.global_request("RELOG")
41af1305
JM
976
977 def wait_completed(self, timeout=10):
978 for i in range(0, timeout * 2):
979 if self.get_status_field("wpa_state") == "COMPLETED":
980 return
981 time.sleep(0.5)
982 raise Exception("Timeout while waiting for COMPLETED state")
0eff1ab3
JM
983
984 def get_capability(self, field):
985 res = self.request("GET_CAPABILITY " + field)
986 if "FAIL" in res:
987 return None
988 return res.split(' ')
2cdd91d8 989
fb9cf82e
IP
990 def get_bss(self, bssid, ifname=None):
991 if not ifname or ifname == self.ifname:
992 res = self.request("BSS " + bssid)
993 elif ifname == self.group_ifname:
994 res = self.group_request("BSS " + bssid)
995 else:
996 return None
997
c126cb4d
JM
998 if "FAIL" in res:
999 return None
2cdd91d8
JM
1000 lines = res.splitlines()
1001 vals = dict()
1002 for l in lines:
1003 [name,value] = l.split('=', 1)
1004 vals[name] = value
c126cb4d
JM
1005 if len(vals) == 0:
1006 return None
2cdd91d8 1007 return vals
0fab9ce6
JM
1008
1009 def get_pmksa(self, bssid):
1010 res = self.request("PMKSA")
1011 lines = res.splitlines()
1012 for l in lines:
1013 if bssid not in l:
1014 continue
1015 vals = dict()
1016 [index,aa,pmkid,expiration,opportunistic] = l.split(' ')
1017 vals['index'] = index
1018 vals['pmkid'] = pmkid
1019 vals['expiration'] = expiration
1020 vals['opportunistic'] = opportunistic
1021 return vals
1022 return None
e1a5e09a
JM
1023
1024 def get_sta(self, addr, info=None, next=False):
1025 cmd = "STA-NEXT " if next else "STA "
1026 if addr is None:
1027 res = self.request("STA-FIRST")
1028 elif info:
1029 res = self.request(cmd + addr + " " + info)
1030 else:
1031 res = self.request(cmd + addr)
1032 lines = res.splitlines()
1033 vals = dict()
1034 first = True
1035 for l in lines:
1036 if first:
1037 vals['addr'] = l
1038 first = False
1039 else:
1040 [name,value] = l.split('=', 1)
1041 vals[name] = value
1042 return vals
daf3806d
JM
1043
1044 def mgmt_rx(self, timeout=5):
1045 ev = self.wait_event(["MGMT-RX"], timeout=timeout)
1046 if ev is None:
1047 return None
1048 msg = {}
1049 items = ev.split(' ')
1050 field,val = items[1].split('=')
1051 if field != "freq":
1052 raise Exception("Unexpected MGMT-RX event format: " + ev)
1053 msg['freq'] = val
1054 frame = binascii.unhexlify(items[4])
1055 msg['frame'] = frame
1056
1057 hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
1058 msg['fc'] = hdr[0]
1059 msg['subtype'] = (hdr[0] >> 4) & 0xf
1060 hdr = hdr[1:]
1061 msg['duration'] = hdr[0]
1062 hdr = hdr[1:]
1063 msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
1064 hdr = hdr[6:]
1065 msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
1066 hdr = hdr[6:]
1067 msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
1068 hdr = hdr[6:]
1069 msg['seq_ctrl'] = hdr[0]
1070 msg['payload'] = frame[24:]
1071
1072 return msg
5f35a5e2
JM
1073
1074 def wait_connected(self, timeout=10, error="Connection timed out"):
1075 ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=timeout)
1076 if ev is None:
1077 raise Exception(error)
1078 return ev
1079
1080 def wait_disconnected(self, timeout=10, error="Disconnection timed out"):
1081 ev = self.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=timeout)
1082 if ev is None:
1083 raise Exception(error)
1084 return ev
bbcbbd37
DS
1085
1086 def get_group_ifname(self):
1087 return self.group_ifname if self.group_ifname else self.ifname
7ebc7e8f
JM
1088
1089 def get_config(self):
1090 res = self.request("DUMP")
1091 if res.startswith("FAIL"):
1092 raise Exception("DUMP failed")
1093 lines = res.splitlines()
1094 vals = dict()
1095 for l in lines:
1096 [name,value] = l.split('=', 1)
1097 vals[name] = value
1098 return vals