d50a4c81913f653b26305de37816d01913737154
[mech_eap.git] / tests / hwsim / wpasupplicant.py
1 # Python class for controlling wpa_supplicant
2 # Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
3 #
4 # This software may be distributed under the terms of the BSD license.
5 # See README for more details.
6
7 import os
8 import time
9 import logging
10 import binascii
11 import re
12 import struct
13 import wpaspy
14 import remotehost
15 import subprocess
16
17 logger = logging.getLogger()
18 wpas_ctrl = '/var/run/wpa_supplicant'
19
20 class WpaSupplicant:
21     def __init__(self, ifname=None, global_iface=None, hostname=None,
22                  port=9877, global_port=9878):
23         self.hostname = hostname
24         self.group_ifname = None
25         self.gctrl_mon = None
26         self.host = remotehost.Host(hostname, ifname)
27         self._group_dbg = None
28         if ifname:
29             self.set_ifname(ifname, hostname, port)
30             res = self.get_driver_status()
31             if 'capa.flags' in res and int(res['capa.flags'], 0) & 0x20000000:
32                 self.p2p_dev_ifname = 'p2p-dev-' + self.ifname
33             else:
34                 self.p2p_dev_ifname = ifname
35         else:
36             self.ifname = None
37
38         self.global_iface = global_iface
39         if global_iface:
40             if hostname != None:
41                 self.global_ctrl = wpaspy.Ctrl(hostname, global_port)
42                 self.global_mon = wpaspy.Ctrl(hostname, global_port)
43                 self.global_dbg = hostname + "/" + str(global_port) + "/"
44             else:
45                 self.global_ctrl = wpaspy.Ctrl(global_iface)
46                 self.global_mon = wpaspy.Ctrl(global_iface)
47                 self.global_dbg = ""
48             self.global_mon.attach()
49         else:
50             self.global_mon = None
51
52     def cmd_execute(self, cmd_array, shell=False):
53         if self.hostname is None:
54             if shell:
55                 cmd = ' '.join(cmd_array)
56             else:
57                 cmd = cmd_array
58             proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
59                                     stdout=subprocess.PIPE, shell=shell)
60             out = proc.communicate()[0]
61             ret = proc.returncode
62             return ret, out
63         else:
64             return self.host.execute(cmd_array)
65
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
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
80     def set_ifname(self, ifname, hostname=None, port=9877):
81         self.ifname = ifname
82         if hostname != None:
83             self.ctrl = wpaspy.Ctrl(hostname, port)
84             self.mon = wpaspy.Ctrl(hostname, port)
85             self.host = remotehost.Host(hostname, ifname)
86             self.dbg = hostname + "/" + ifname
87         else:
88             self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
89             self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
90             self.dbg = ifname
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
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
120     def interface_add(self, ifname, config="", driver="nl80211",
121                       drv_params=None, br_ifname=None, create=False,
122                       set_ifname=True, all_params=False, if_type=None):
123         status, groups = self.host.execute(["id"])
124         if status != 0:
125             group = "admin"
126         group = "admin" if "(admin)" in groups else "adm"
127         cmd = "INTERFACE_ADD " + ifname + "\t" + config + "\t" + driver + "\tDIR=/var/run/wpa_supplicant GROUP=" + group
128         if drv_params:
129             cmd = cmd + '\t' + drv_params
130         if br_ifname:
131             if not drv_params:
132                 cmd += '\t'
133             cmd += '\t' + br_ifname
134         if create:
135             if not br_ifname:
136                 cmd += '\t'
137                 if not drv_params:
138                     cmd += '\t'
139             cmd += '\tcreate'
140             if if_type:
141                 cmd += '\t' + if_type
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'
148         if "FAIL" in self.global_request(cmd):
149             raise Exception("Failed to add a dynamic wpa_supplicant interface")
150         if not create and set_ifname:
151             port = self.get_ctrl_iface_port(ifname)
152             self.set_ifname(ifname, self.hostname, port)
153             res = self.get_driver_status()
154             if 'capa.flags' in res and int(res['capa.flags'], 0) & 0x20000000:
155                 self.p2p_dev_ifname = 'p2p-dev-' + self.ifname
156             else:
157                 self.p2p_dev_ifname = ifname
158
159     def interface_remove(self, ifname):
160         self.remove_ifname()
161         self.global_request("INTERFACE_REMOVE " + ifname)
162
163     def request(self, cmd, timeout=10):
164         logger.debug(self.dbg + ": CTRL: " + cmd)
165         return self.ctrl.request(cmd, timeout=timeout)
166
167     def global_request(self, cmd):
168         if self.global_iface is None:
169             return self.request(cmd)
170         else:
171             ifname = self.ifname or self.global_iface
172             logger.debug(self.global_dbg + ifname + ": CTRL(global): " + cmd)
173             return self.global_ctrl.request(cmd)
174
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
187     def group_request(self, cmd):
188         if self.group_ifname and self.group_ifname != self.ifname:
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)
195             return gctrl.request(cmd)
196         return self.request(cmd)
197
198     def ping(self):
199         return "PONG" in self.request("PING")
200
201     def global_ping(self):
202         return "PONG" in self.global_request("PING")
203
204     def reset(self):
205         self.dump_monitor()
206         res = self.request("FLUSH")
207         if not "OK" in res:
208             logger.info("FLUSH to " + self.ifname + " failed: " + res)
209         self.global_request("REMOVE_NETWORK all")
210         self.global_request("SET p2p_no_group_iface 1")
211         self.global_request("P2P_FLUSH")
212         if self.gctrl_mon:
213             try:
214                 self.gctrl_mon.detach()
215             except:
216                 pass
217             self.gctrl_mon = None
218         self.group_ifname = None
219         self.dump_monitor()
220
221         iter = 0
222         while iter < 60:
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:
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"
236             status, buf = self.host.execute(["ifconfig", self.ifname, "down"])
237             if status != 0:
238                 logger.info("ifconfig failed: " + buf)
239                 logger.info(status)
240             status, buf = self.host.execute(["ifconfig", self.ifname, "up"])
241             if status != 0:
242                 logger.info("ifconfig failed: " + buf)
243                 logger.info(status)
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")
248             self.dump_monitor()
249
250         if not self.ping():
251             logger.info("No PING response from " + self.ifname + " after reset")
252
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
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
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
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
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")
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
323     def hs20_enable(self, auto_interworking=False):
324         self.request("SET interworking 1")
325         self.request("SET hs20 1")
326         if auto_interworking:
327             self.request("SET auto_interworking 1")
328         else:
329             self.request("SET auto_interworking 0")
330
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
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
361     def get_cred(self, id, field):
362         return self.request("GET_CRED " + str(id) + " " + field)
363
364     def add_cred_values(self, params):
365         id = self.add_cred()
366
367         quoted = [ "realm", "username", "password", "domain", "imsi",
368                    "excluded_ssid", "milenage", "ca_cert", "client_cert",
369                    "private_key", "domain_suffix_match", "provisioning_sp",
370                    "roaming_partner", "phase1", "phase2", "private_key_passwd" ]
371         for field in quoted:
372             if field in params:
373                 self.set_cred_quoted(id, field, params[field])
374
375         not_quoted = [ "eap", "roaming_consortium", "priority",
376                        "required_roaming_consortium", "sp_priority",
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" ]
380         for field in not_quoted:
381             if field in params:
382                 self.set_cred(id, field, params[field])
383
384         return id
385
386     def select_network(self, id, freq=None):
387         if freq:
388             extra = " freq=" + str(freq)
389         else:
390             extra = ""
391         id = self.request("SELECT_NETWORK " + str(id) + extra)
392         if "FAIL" in id:
393             raise Exception("SELECT_NETWORK failed")
394         return None
395
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
408     def connect_network(self, id, timeout=10):
409         self.dump_monitor()
410         self.select_network(id)
411         self.wait_connected(timeout=timeout)
412         self.dump_monitor()
413
414     def get_status(self, extra=None):
415         if extra:
416             extra = "-" + extra
417         else:
418             extra = ""
419         res = self.request("STATUS" + extra)
420         lines = res.splitlines()
421         vals = dict()
422         for l in lines:
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)
428         return vals
429
430     def get_status_field(self, field, extra=None):
431         vals = self.get_status(extra)
432         if field in vals:
433             return vals[field]
434         return None
435
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)
442         lines = res.splitlines()
443         vals = dict()
444         for l in lines:
445             try:
446                 [name,value] = l.split('=', 1)
447             except ValueError:
448                 logger.info(self.ifname + ": Ignore unexpected status line: " + l)
449                 continue
450             vals[name] = value
451         return vals
452
453     def get_group_status_field(self, field, extra=None):
454         vals = self.get_group_status(extra)
455         if field in vals:
456             return vals[field]
457         return None
458
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)
464             if res.startswith("FAIL"):
465                 return dict()
466         lines = res.splitlines()
467         vals = dict()
468         for l in lines:
469             try:
470                 [name,value] = l.split('=', 1)
471             except ValueError:
472                 logger.info(self.ifname + ": Ignore unexpected status-driver line: " + l)
473                 continue
474             vals[name] = value
475         return vals
476
477     def get_driver_status_field(self, field, ifname=None):
478         vals = self.get_driver_status(ifname)
479         if field in vals:
480             return vals[field]
481         return None
482
483     def get_mcc(self):
484         mcc = int(self.get_driver_status_field('capa.num_multichan_concurrent'))
485         return 1 if mcc < 2 else mcc
486
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
499     def p2p_dev_addr(self):
500         return self.get_status_field("p2p_device_address")
501
502     def p2p_interface_addr(self):
503         return self.get_group_status_field("address")
504
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
512     def p2p_listen(self):
513         return self.global_request("P2P_LISTEN")
514
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
521     def p2p_find(self, social=False, progressive=False, dev_id=None,
522                  dev_type=None, delay=None, freq=None):
523         cmd = "P2P_FIND"
524         if social:
525             cmd = cmd + " type=social"
526         elif progressive:
527             cmd = cmd + " type=progressive"
528         if dev_id:
529             cmd = cmd + " dev_id=" + dev_id
530         if dev_type:
531             cmd = cmd + " dev_type=" + dev_type
532         if delay:
533             cmd = cmd + " delay=" + str(delay)
534         if freq:
535             cmd = cmd + " freq=" + str(freq)
536         return self.global_request(cmd)
537
538     def p2p_stop_find(self):
539         return self.global_request("P2P_STOP_FIND")
540
541     def wps_read_pin(self):
542         self.pin = self.request("WPS_PIN get").rstrip("\n")
543         if "FAIL" in self.pin:
544             raise Exception("Could not generate PIN")
545         return self.pin
546
547     def peer_known(self, peer, full=True):
548         res = self.global_request("P2P_PEER " + peer)
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
555     def discover_peer(self, peer, full=True, timeout=15, social=True,
556                       force_find=False, freq=None):
557         logger.info(self.ifname + ": Trying to discover peer " + peer)
558         if not force_find and self.peer_known(peer, full):
559             return True
560         self.p2p_find(social, freq=freq)
561         count = 0
562         while count < timeout * 4:
563             time.sleep(0.25)
564             count = count + 1
565             if self.peer_known(peer, full):
566                 return True
567         return False
568
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
581     def group_form_result(self, ev, expect_failure=False, go_neg_res=None):
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
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.]*)'
598         s = re.split(exp, ev)
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")
604         res = {}
605         res['result'] = 'success'
606         res['ifname'] = s[2]
607         self.group_ifname = s[2]
608         try:
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)
615             self.gctrl_mon.attach()
616         except:
617             logger.debug("Could not open monitor socket for group interface")
618             self.gctrl_mon = None
619         res['role'] = s[3]
620         res['ssid'] = s[4]
621         res['freq'] = s[5]
622         if "[PERSISTENT]" in ev:
623             res['persistent'] = True
624         else:
625             res['persistent'] = False
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]
633
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
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
649         return res
650
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):
654         if not self.discover_peer(peer):
655             raise Exception("Peer " + peer + " not found")
656         self.dump_monitor()
657         if pin:
658             cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth"
659         else:
660             cmd = "P2P_CONNECT " + peer + " " + method + " auth"
661         if go_intent:
662             cmd = cmd + ' go_intent=' + str(go_intent)
663         if freq:
664             cmd = cmd + ' freq=' + str(freq)
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'
673         if persistent:
674             cmd = cmd + " persistent"
675         if "OK" in self.global_request(cmd):
676             return None
677         raise Exception("P2P_CONNECT (auth) failed")
678
679     def p2p_go_neg_auth_result(self, timeout=1, expect_failure=False):
680         go_neg_res = None
681         ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
682                                      "P2P-GO-NEG-FAILURE"], timeout)
683         if ev is None:
684             if expect_failure:
685                 return None
686             raise Exception("Group formation timed out")
687         if "P2P-GO-NEG-SUCCESS" in ev:
688             go_neg_res = ev
689             ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
690             if ev is None:
691                 if expect_failure:
692                     return None
693                 raise Exception("Group formation timed out")
694         self.dump_monitor()
695         return self.group_form_result(ev, expect_failure, go_neg_res)
696
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):
702         if not self.discover_peer(peer):
703             raise Exception("Peer " + peer + " not found")
704         self.dump_monitor()
705         if pin:
706             cmd = "P2P_CONNECT " + peer + " " + pin + " " + method
707         else:
708             cmd = "P2P_CONNECT " + peer + " " + method
709         if go_intent is not None:
710             cmd = cmd + ' go_intent=' + str(go_intent)
711         if freq:
712             cmd = cmd + ' freq=' + str(freq)
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'
721         if persistent:
722             cmd = cmd + " persistent"
723         elif persistent_id:
724             cmd = cmd + " persistent=" + persistent_id
725         if provdisc:
726             cmd = cmd + " provdisc"
727         if "OK" in self.global_request(cmd):
728             if timeout == 0:
729                 return None
730             go_neg_res = None
731             ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
732                                          "P2P-GO-NEG-FAILURE"], timeout)
733             if ev is None:
734                 if expect_failure:
735                     return None
736                 raise Exception("Group formation timed out")
737             if "P2P-GO-NEG-SUCCESS" in ev:
738                 if not wait_group:
739                     return ev
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")
746             self.dump_monitor()
747             return self.group_form_result(ev, expect_failure, go_neg_res)
748         raise Exception("P2P_CONNECT failed")
749
750     def wait_event(self, events, timeout=10):
751         start = os.times()[4]
752         while True:
753             while self.mon.pending():
754                 ev = self.mon.recv()
755                 logger.debug(self.dbg + ": " + ev)
756                 for event in events:
757                     if event in ev:
758                         return ev
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
765         return None
766
767     def wait_global_event(self, events, timeout):
768         if self.global_iface is None:
769             self.wait_event(events, timeout)
770         else:
771             start = os.times()[4]
772             while True:
773                 while self.global_mon.pending():
774                     ev = self.global_mon.recv()
775                     logger.debug(self.global_dbg + self.ifname + "(global): " + ev)
776                     for event in events:
777                         if event in ev:
778                             return ev
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
785         return None
786
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()
795                     logger.debug(self.group_dbg + "(group): " + ev)
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
809     def wait_go_ending_session(self):
810         if self.gctrl_mon:
811             try:
812                 self.gctrl_mon.detach()
813             except:
814                 pass
815             self.gctrl_mon = None
816         ev = self.wait_global_event(["P2P-GROUP-REMOVED"], timeout=3)
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
822     def dump_monitor(self):
823         count_iface = 0
824         count_global = 0
825         while self.mon.pending():
826             ev = self.mon.recv()
827             logger.debug(self.dbg + ": " + ev)
828             count_iface += 1
829         while self.global_mon and self.global_mon.pending():
830             ev = self.global_mon.recv()
831             logger.debug(self.global_dbg + self.ifname + "(global): " + ev)
832             count_global += 1
833         return (count_iface, count_global)
834
835     def remove_group(self, ifname=None):
836         if self.gctrl_mon:
837             try:
838                 self.gctrl_mon.detach()
839             except:
840                 pass
841             self.gctrl_mon = None
842         if ifname is None:
843             ifname = self.group_ifname if self.group_ifname else self.ifname
844         if "OK" not in self.global_request("P2P_GROUP_REMOVE " + ifname):
845             raise Exception("Group could not be removed")
846         self.group_ifname = None
847
848     def p2p_start_go(self, persistent=None, freq=None, no_event_clear=False):
849         self.dump_monitor()
850         cmd = "P2P_GROUP_ADD"
851         if persistent is None:
852             pass
853         elif persistent is True:
854             cmd = cmd + " persistent"
855         else:
856             cmd = cmd + " persistent=" + str(persistent)
857         if freq:
858             cmd = cmd + " freq=" + str(freq)
859         if "OK" in self.global_request(cmd):
860             ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
861             if ev is None:
862                 raise Exception("GO start up timed out")
863             if not no_event_clear:
864                 self.dump_monitor()
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
870         if "FAIL" in self.group_request(cmd):
871             raise Exception("Failed to authorize client connection on GO")
872         return None
873
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
880     def p2p_connect_group(self, go_addr, pin, timeout=0, social=False,
881                           freq=None):
882         self.dump_monitor()
883         if not self.discover_peer(go_addr, social=social, freq=freq):
884             if social or not self.discover_peer(go_addr, social=social):
885                 raise Exception("GO " + go_addr + " not found")
886         self.p2p_stop_find()
887         self.dump_monitor()
888         cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
889         if freq:
890             cmd += " freq=" + str(freq)
891         if "OK" in self.global_request(cmd):
892             if timeout == 0:
893                 self.dump_monitor()
894                 return None
895             ev = self.wait_global_event(["P2P-GROUP-STARTED",
896                                          "P2P-GROUP-FORMATION-FAILURE"],
897                                         timeout)
898             if ev is None:
899                 raise Exception("Joining the group timed out")
900             if "P2P-GROUP-STARTED" not in ev:
901                 raise Exception("Failed to join the group")
902             self.dump_monitor()
903             return self.group_form_result(ev)
904         raise Exception("P2P_CONNECT(join) failed")
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
917
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
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
934     def add_ts(self, tsid, up, direction="downlink", expect_failure=False,
935                extra=None):
936         params = {
937             "sba": 9000,
938             "nominal_msdu_size": 1500,
939             "min_phy_rate": 6000000,
940             "mean_data_rate": 1500,
941         }
942         cmd = "WMM_AC_ADDTS %s tsid=%d up=%d" % (direction, tsid, up)
943         for (key, value) in params.iteritems():
944             cmd += " %s=%d" % (key, value)
945         if extra:
946             cmd += " " + extra
947
948         if self.request(cmd).strip() != "OK":
949             raise Exception("ADDTS failed (tsid=%d up=%d)" % (tsid, up))
950
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
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
965         if not (tsid, up) in self.tspecs():
966             raise Exception("ADDTS failed (tsid not in tspec list)")
967
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
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
982     def connect(self, ssid=None, ssid2=None, **kwargs):
983         logger.info("Connect STA " + self.ifname + " to AP")
984         id = self.add_network()
985         if ssid:
986             self.set_network_quoted(id, "ssid", ssid)
987         elif ssid2:
988             self.set_network(id, "ssid", ssid2)
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",
994                    "altsubject_match", "subject_match", "pac_file", "dh_file",
995                    "bgscan", "ht_mcs", "id_str", "openssl_ciphers",
996                    "domain_match" ]
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",
1002                        "group", "wep_key0", "wep_key1", "wep_key2", "wep_key3",
1003                        "wep_tx_keyidx", "scan_freq", "freq_list", "eap",
1004                        "eapol_flags", "fragment_size", "scan_ssid", "auth_alg",
1005                        "wpa_ptk_rekey", "disable_ht", "disable_vht", "bssid",
1006                        "disable_max_amsdu", "ampdu_factor", "ampdu_density",
1007                        "disable_ht40", "disable_sgi", "disable_ldpc",
1008                        "ht40_intolerant", "update_identifier", "mac_addr",
1009                        "erp", "bg_scan_period", "bssid_blacklist",
1010                        "bssid_whitelist", "mem_only_psk", "eap_workaround",
1011                        "engine" ]
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']:
1021             self.set_network(id, "peerkey", "1")
1022         if "okc" in kwargs and kwargs['okc']:
1023             self.set_network(id, "proactive_key_caching", "1")
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']:
1027             return id
1028         if "wait_connect" not in kwargs or kwargs['wait_connect']:
1029             if "eap" in kwargs:
1030                 self.connect_network(id, timeout=20)
1031             else:
1032                 self.connect_network(id)
1033         else:
1034             self.dump_monitor()
1035             self.select_network(id)
1036         return id
1037
1038     def scan(self, type=None, freq=None, no_wait=False, only_new=False):
1039         if type:
1040             cmd = "SCAN TYPE=" + type
1041         else:
1042             cmd = "SCAN"
1043         if freq:
1044             cmd = cmd + " freq=" + str(freq)
1045         if only_new:
1046             cmd += " only_new=1"
1047         if not no_wait:
1048             self.dump_monitor()
1049         if not "OK" in self.request(cmd):
1050             raise Exception("Failed to trigger scan")
1051         if no_wait:
1052             return
1053         ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
1054         if ev is None:
1055             raise Exception("Scan timed out")
1056
1057     def scan_for_bss(self, bssid, freq=None, force_scan=False, only_new=False):
1058         if not force_scan and self.get_bss(bssid) is not None:
1059             return
1060         for i in range(0, 10):
1061             self.scan(freq=freq, type="ONLY", only_new=only_new)
1062             if self.get_bss(bssid) is not None:
1063                 return
1064         raise Exception("Could not find BSS " + bssid + " in scan")
1065
1066     def flush_scan_cache(self, freq=2417):
1067         self.request("BSS_FLUSH 0")
1068         self.scan(freq=freq, only_new=True)
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)
1076
1077     def roam(self, bssid, fail_test=False):
1078         self.dump_monitor()
1079         if "OK" not in self.request("ROAM " + bssid):
1080             raise Exception("ROAM failed")
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
1087         self.wait_connected(timeout=10, error="Roaming with the AP timed out")
1088         self.dump_monitor()
1089
1090     def roam_over_ds(self, bssid, fail_test=False):
1091         self.dump_monitor()
1092         if "OK" not in self.request("FT_DS " + bssid):
1093             raise Exception("FT_DS failed")
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
1100         self.wait_connected(timeout=10, error="Roaming with the AP timed out")
1101         self.dump_monitor()
1102
1103     def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None,
1104                 new_passphrase=None, no_wait=False):
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"))
1110             if no_wait:
1111                 return
1112             ev = self.wait_event(["WPS-SUCCESS"], timeout=15)
1113         else:
1114             self.request("WPS_REG " + bssid + " " + pin)
1115             if no_wait:
1116                 return
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")
1123         self.wait_connected(timeout=15)
1124
1125     def relog(self):
1126         self.global_request("RELOG")
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")
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(' ')
1140
1141     def get_bss(self, bssid, ifname=None):
1142         if not ifname or ifname == self.ifname:
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
1149         if "FAIL" in res:
1150             return None
1151         lines = res.splitlines()
1152         vals = dict()
1153         for l in lines:
1154             [name,value] = l.split('=', 1)
1155             vals[name] = value
1156         if len(vals) == 0:
1157             return None
1158         return vals
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
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
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
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
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
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
1247
1248     def get_group_ifname(self):
1249         return self.group_ifname if self.group_ifname else self.ifname
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
1261
1262     def asp_provision(self, peer, adv_id, adv_mac, session_id, session_mac,
1263                       method="1000", info="", status=None, cpt=None, role=None):
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
1271         if role is not None:
1272             params += " role=" + role
1273         if cpt is not None:
1274             params += " cpt=" + cpt
1275
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)