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