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