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