3 # Python class for controlling wpa_supplicant
4 # Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
6 # This software may be distributed under the terms of the BSD license.
7 # See README for more details.
16 logger = logging.getLogger()
17 wpas_ctrl = '/var/run/wpa_supplicant'
20 def __init__(self, ifname=None, global_iface=None):
21 self.group_ifname = None
23 self.set_ifname(ifname)
27 self.global_iface = global_iface
29 self.global_ctrl = wpaspy.Ctrl(global_iface)
30 self.global_mon = wpaspy.Ctrl(global_iface)
31 self.global_mon.attach()
33 def set_ifname(self, ifname):
35 self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
36 self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
39 def remove_ifname(self):
46 def interface_add(self, ifname, driver="nl80211", drv_params=None):
48 groups = subprocess.check_output(["id"])
49 group = "admin" if "(admin)" in groups else "adm"
52 cmd = "INTERFACE_ADD " + ifname + "\t\t" + driver + "\tDIR=/var/run/wpa_supplicant GROUP=" + group
54 cmd = cmd + '\t' + drv_params
55 if "FAIL" in self.global_request(cmd):
56 raise Exception("Failed to add a dynamic wpa_supplicant interface")
57 self.set_ifname(ifname)
59 def interface_remove(self, ifname):
61 self.global_request("INTERFACE_REMOVE " + ifname)
63 def request(self, cmd):
64 logger.debug(self.ifname + ": CTRL: " + cmd)
65 return self.ctrl.request(cmd)
67 def global_request(self, cmd):
68 if self.global_iface is None:
71 ifname = self.ifname or self.global_iface
72 logger.debug(ifname + ": CTRL: " + cmd)
73 return self.global_ctrl.request(cmd)
75 def group_request(self, cmd):
76 if self.group_ifname and self.group_ifname != self.ifname:
77 logger.debug(self.group_ifname + ": CTRL: " + cmd)
78 gctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname))
79 return gctrl.request(cmd)
80 return self.request(cmd)
83 return "PONG" in self.request("PING")
86 res = self.request("FLUSH")
88 logger.info("FLUSH to " + self.ifname + " failed: " + res)
89 self.request("SET external_sim 0")
90 self.request("SET hessid 00:00:00:00:00:00")
91 self.request("SET access_network_type 15")
92 self.request("SET p2p_add_cli_chan 0")
93 self.request("SET p2p_no_go_freq ")
94 self.request("SET p2p_pref_chan ")
95 self.request("SET p2p_no_group_iface 1")
96 self.request("SET p2p_go_intent 7")
97 self.group_ifname = None
102 state = self.get_driver_status_field("scan_state")
103 if "SCAN_STARTED" in state or "SCAN_REQUESTED" in state:
104 logger.info(self.ifname + ": Waiting for scan operation to complete before continuing")
110 logger.error(self.ifname + ": Driver scan state did not clear")
111 print "Trying to clear cfg80211/mac80211 scan state"
113 cmd = ["sudo", "ifconfig", self.ifname, "down"]
115 except subprocess.CalledProcessError, e:
116 logger.info("ifconfig failed: " + str(e.returncode))
117 logger.info(e.output)
119 cmd = ["sudo", "ifconfig", self.ifname, "up"]
121 except subprocess.CalledProcessError, e:
122 logger.info("ifconfig failed: " + str(e.returncode))
123 logger.info(e.output)
125 # The ongoing scan could have discovered BSSes or P2P peers
126 logger.info("Run FLUSH again since scan was in progress")
127 self.request("FLUSH")
131 logger.info("No PING response from " + self.ifname + " after reset")
133 def add_network(self):
134 id = self.request("ADD_NETWORK")
136 raise Exception("ADD_NETWORK failed")
139 def remove_network(self, id):
140 id = self.request("REMOVE_NETWORK " + str(id))
142 raise Exception("REMOVE_NETWORK failed")
145 def set_network(self, id, field, value):
146 res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
148 raise Exception("SET_NETWORK failed")
151 def set_network_quoted(self, id, field, value):
152 res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
154 raise Exception("SET_NETWORK failed")
157 def list_networks(self):
158 res = self.request("LIST_NETWORKS")
159 lines = res.splitlines()
162 if "network id" in l:
164 [id,ssid,bssid,flags] = l.split('\t')
167 network['ssid'] = ssid
168 network['bssid'] = bssid
169 network['flags'] = flags
170 networks.append(network)
173 def hs20_enable(self):
174 self.request("SET interworking 1")
175 self.request("SET hs20 1")
178 id = self.request("ADD_CRED")
180 raise Exception("ADD_CRED failed")
183 def remove_cred(self, id):
184 id = self.request("REMOVE_CRED " + str(id))
186 raise Exception("REMOVE_CRED failed")
189 def set_cred(self, id, field, value):
190 res = self.request("SET_CRED " + str(id) + " " + field + " " + value)
192 raise Exception("SET_CRED failed")
195 def set_cred_quoted(self, id, field, value):
196 res = self.request("SET_CRED " + str(id) + " " + field + ' "' + value + '"')
198 raise Exception("SET_CRED failed")
201 def add_cred_values(self, params):
204 quoted = [ "realm", "username", "password", "domain", "imsi",
205 "excluded_ssid", "milenage", "ca_cert", "client_cert",
209 self.set_cred_quoted(id, field, params[field])
211 not_quoted = [ "eap", "roaming_consortium",
212 "required_roaming_consortium" ]
213 for field in not_quoted:
215 self.set_cred(id, field, params[field])
219 def select_network(self, id):
220 id = self.request("SELECT_NETWORK " + str(id))
222 raise Exception("SELECT_NETWORK failed")
225 def connect_network(self, id, timeout=10):
227 self.select_network(id)
228 ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=timeout)
230 raise Exception("Association with the AP timed out")
233 def get_status(self):
234 res = self.request("STATUS")
235 lines = res.splitlines()
238 [name,value] = l.split('=', 1)
242 def get_status_field(self, field):
243 vals = self.get_status()
248 def get_group_status(self):
249 res = self.group_request("STATUS")
250 lines = res.splitlines()
253 [name,value] = l.split('=', 1)
257 def get_group_status_field(self, field):
258 vals = self.get_group_status()
263 def get_driver_status(self):
264 res = self.request("STATUS-DRIVER")
265 lines = res.splitlines()
268 [name,value] = l.split('=', 1)
272 def get_driver_status_field(self, field):
273 vals = self.get_driver_status()
278 def p2p_dev_addr(self):
279 return self.get_status_field("p2p_device_address")
281 def p2p_interface_addr(self):
282 return self.get_group_status_field("address")
284 def p2p_listen(self):
285 return self.global_request("P2P_LISTEN")
287 def p2p_find(self, social=False, dev_id=None, dev_type=None):
290 cmd = cmd + " type=social"
292 cmd = cmd + " dev_id=" + dev_id
294 cmd = cmd + " dev_type=" + dev_type
295 return self.global_request(cmd)
297 def p2p_stop_find(self):
298 return self.global_request("P2P_STOP_FIND")
300 def wps_read_pin(self):
301 #TODO: make this random
302 self.pin = "12345670"
305 def peer_known(self, peer, full=True):
306 res = self.global_request("P2P_PEER " + peer)
307 if peer.lower() not in res.lower():
311 return "[PROBE_REQ_ONLY]" not in res
313 def discover_peer(self, peer, full=True, timeout=15, social=True, force_find=False):
314 logger.info(self.ifname + ": Trying to discover peer " + peer)
315 if not force_find and self.peer_known(peer, full):
317 self.p2p_find(social)
319 while count < timeout:
322 if self.peer_known(peer, full):
326 def get_peer(self, peer):
327 res = self.global_request("P2P_PEER " + peer)
328 if peer.lower() not in res.lower():
329 raise Exception("Peer information not available")
330 lines = res.splitlines()
334 [name,value] = l.split('=', 1)
338 def group_form_result(self, ev, expect_failure=False, go_neg_res=None):
340 if "P2P-GROUP-STARTED" in ev:
341 raise Exception("Group formation succeeded when expecting failure")
342 exp = r'<.>(P2P-GO-NEG-FAILURE) status=([0-9]*)'
343 s = re.split(exp, ev)
347 res['result'] = 'go-neg-failed'
348 res['status'] = int(s[2])
351 if "P2P-GROUP-STARTED" not in ev:
352 raise Exception("No P2P-GROUP-STARTED event seen")
354 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.]*)'
355 s = re.split(exp, ev)
357 exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*)'
358 s = re.split(exp, ev)
360 raise Exception("Could not parse P2P-GROUP-STARTED")
362 res['result'] = 'success'
364 self.group_ifname = s[2]
368 if "[PERSISTENT]" in ev:
369 res['persistent'] = True
371 res['persistent'] = False
372 p = re.match(r'psk=([0-9a-f]*)', s[6])
374 res['psk'] = p.group(1)
375 p = re.match(r'passphrase="(.*)"', s[6])
377 res['passphrase'] = p.group(1)
378 res['go_dev_addr'] = s[7]
380 if len(s) > 8 and len(s[8]) > 0:
381 res['ip_addr'] = s[8]
383 res['ip_mask'] = s[9]
385 res['go_ip_addr'] = s[10]
388 exp = r'<.>(P2P-GO-NEG-SUCCESS) role=(GO|client) freq=([0-9]*)'
389 s = re.split(exp, go_neg_res)
391 raise Exception("Could not parse P2P-GO-NEG-SUCCESS")
392 res['go_neg_role'] = s[2]
393 res['go_neg_freq'] = s[3]
397 def p2p_go_neg_auth(self, peer, pin, method, go_intent=None, persistent=False, freq=None):
398 if not self.discover_peer(peer):
399 raise Exception("Peer " + peer + " not found")
401 cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth"
403 cmd = cmd + ' go_intent=' + str(go_intent)
405 cmd = cmd + ' freq=' + str(freq)
407 cmd = cmd + " persistent"
408 if "OK" in self.global_request(cmd):
410 raise Exception("P2P_CONNECT (auth) failed")
412 def p2p_go_neg_auth_result(self, timeout=1, expect_failure=False):
414 ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
415 "P2P-GO-NEG-FAILURE"], timeout);
419 raise Exception("Group formation timed out")
420 if "P2P-GO-NEG-SUCCESS" in ev:
422 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout);
426 raise Exception("Group formation timed out")
428 return self.group_form_result(ev, expect_failure, go_neg_res)
430 def p2p_go_neg_init(self, peer, pin, method, timeout=0, go_intent=None, expect_failure=False, persistent=False, freq=None):
431 if not self.discover_peer(peer):
432 raise Exception("Peer " + peer + " not found")
435 cmd = "P2P_CONNECT " + peer + " " + pin + " " + method
437 cmd = "P2P_CONNECT " + peer + " " + method
439 cmd = cmd + ' go_intent=' + str(go_intent)
441 cmd = cmd + ' freq=' + str(freq)
443 cmd = cmd + " persistent"
444 if "OK" in self.global_request(cmd):
449 ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
450 "P2P-GO-NEG-FAILURE"], timeout)
454 raise Exception("Group formation timed out")
455 if "P2P-GO-NEG-SUCCESS" in ev:
457 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
461 raise Exception("Group formation timed out")
463 return self.group_form_result(ev, expect_failure, go_neg_res)
464 raise Exception("P2P_CONNECT failed")
466 def wait_event(self, events, timeout=10):
467 start = os.times()[4]
469 while self.mon.pending():
471 logger.debug(self.ifname + ": " + ev)
476 remaining = start + timeout - now
479 if not self.mon.pending(timeout=remaining):
483 def wait_global_event(self, events, timeout):
484 if self.global_iface is None:
485 self.wait_event(events, timeout)
487 start = os.times()[4]
489 while self.global_mon.pending():
490 ev = self.global_mon.recv()
491 logger.debug(self.ifname + "(global): " + ev)
496 remaining = start + timeout - now
499 if not self.global_mon.pending(timeout=remaining):
503 def wait_go_ending_session(self):
504 ev = self.wait_event(["P2P-GROUP-REMOVED"], timeout=3)
506 raise Exception("Group removal event timed out")
507 if "reason=GO_ENDING_SESSION" not in ev:
508 raise Exception("Unexpected group removal reason")
510 def dump_monitor(self):
511 while self.mon.pending():
513 logger.debug(self.ifname + ": " + ev)
514 while self.global_mon.pending():
515 ev = self.global_mon.recv()
516 logger.debug(self.ifname + "(global): " + ev)
518 def remove_group(self, ifname=None):
520 ifname = self.group_ifname if self.group_ifname else self.ifname
521 if "OK" not in self.global_request("P2P_GROUP_REMOVE " + ifname):
522 raise Exception("Group could not be removed")
523 self.group_ifname = None
525 def p2p_start_go(self, persistent=None, freq=None):
527 cmd = "P2P_GROUP_ADD"
528 if persistent is None:
530 elif persistent is True:
531 cmd = cmd + " persistent"
533 cmd = cmd + " persistent=" + str(persistent)
535 cmd = cmd + " freq=" + str(freq)
536 if "OK" in self.global_request(cmd):
537 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
539 raise Exception("GO start up timed out")
541 return self.group_form_result(ev)
542 raise Exception("P2P_GROUP_ADD failed")
544 def p2p_go_authorize_client(self, pin):
545 cmd = "WPS_PIN any " + pin
546 if "FAIL" in self.group_request(cmd):
547 raise Exception("Failed to authorize client connection on GO")
550 def p2p_go_authorize_client_pbc(self):
552 if "FAIL" in self.group_request(cmd):
553 raise Exception("Failed to authorize client connection on GO")
556 def p2p_connect_group(self, go_addr, pin, timeout=0, social=False):
558 if not self.discover_peer(go_addr, social=social):
559 raise Exception("GO " + go_addr + " not found")
561 cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
562 if "OK" in self.global_request(cmd):
566 ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
568 raise Exception("Joining the group timed out")
570 return self.group_form_result(ev)
571 raise Exception("P2P_CONNECT(join) failed")
573 def tdls_setup(self, peer):
574 cmd = "TDLS_SETUP " + peer
575 if "FAIL" in self.group_request(cmd):
576 raise Exception("Failed to request TDLS setup")
579 def tdls_teardown(self, peer):
580 cmd = "TDLS_TEARDOWN " + peer
581 if "FAIL" in self.group_request(cmd):
582 raise Exception("Failed to request TDLS teardown")
585 def connect(self, ssid=None, ssid2=None, psk=None, proto=None,
586 key_mgmt=None, wep_key0=None,
587 ieee80211w=None, pairwise=None, group=None, scan_freq=None,
588 eap=None, identity=None, anonymous_identity=None,
589 password=None, phase1=None, phase2=None, ca_cert=None,
590 domain_suffix_match=None, password_hex=None,
591 client_cert=None, private_key=None, peerkey=False, okc=False,
592 eapol_flags=None, fragment_size=None,
593 wait_connect=True, only_add_network=False,
594 ca_cert2=None, client_cert2=None, private_key2=None,
595 scan_ssid=None, raw_psk=None, pac_file=None,
596 subject_match=None, altsubject_match=None,
597 private_key_passwd=None, ocsp=None):
598 logger.info("Connect STA " + self.ifname + " to AP")
599 id = self.add_network()
601 self.set_network_quoted(id, "ssid", ssid)
603 self.set_network(id, "ssid", ssid2)
605 self.set_network_quoted(id, "psk", psk)
607 self.set_network(id, "psk", raw_psk)
609 self.set_network(id, "proto", proto)
611 self.set_network(id, "key_mgmt", key_mgmt)
613 self.set_network(id, "ieee80211w", ieee80211w)
615 self.set_network(id, "pairwise", pairwise)
617 self.set_network(id, "group", group)
619 self.set_network(id, "wep_key0", wep_key0)
621 self.set_network(id, "scan_freq", scan_freq)
623 self.set_network(id, "eap", eap)
625 self.set_network_quoted(id, "identity", identity)
626 if anonymous_identity:
627 self.set_network_quoted(id, "anonymous_identity",
630 self.set_network_quoted(id, "password", password)
632 self.set_network(id, "password", password_hex)
634 self.set_network_quoted(id, "ca_cert", ca_cert)
636 self.set_network_quoted(id, "client_cert", client_cert)
638 self.set_network_quoted(id, "private_key", private_key)
639 if private_key_passwd:
640 self.set_network_quoted(id, "private_key_passwd",
643 self.set_network_quoted(id, "ca_cert2", ca_cert2)
645 self.set_network_quoted(id, "client_cert2", client_cert2)
647 self.set_network_quoted(id, "private_key2", private_key2)
649 self.set_network_quoted(id, "phase1", phase1)
651 self.set_network_quoted(id, "phase2", phase2)
652 if domain_suffix_match:
653 self.set_network_quoted(id, "domain_suffix_match",
656 self.set_network_quoted(id, "altsubject_match",
659 self.set_network_quoted(id, "subject_match",
662 self.set_network(id, "peerkey", "1")
664 self.set_network(id, "proactive_key_caching", "1")
666 self.set_network(id, "eapol_flags", eapol_flags)
668 self.set_network(id, "fragment_size", fragment_size)
670 self.set_network(id, "scan_ssid", scan_ssid)
672 self.set_network_quoted(id, "pac_file", pac_file)
674 self.set_network(id, "ocsp", str(ocsp))
679 self.connect_network(id, timeout=20)
681 self.connect_network(id)
684 self.select_network(id)
687 def scan(self, type=None, freq=None, no_wait=False):
689 cmd = "SCAN TYPE=" + type
693 cmd = cmd + " freq=" + freq
696 if not "OK" in self.request(cmd):
697 raise Exception("Failed to trigger scan")
700 ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
702 raise Exception("Scan timed out")
704 def roam(self, bssid):
706 self.request("ROAM " + bssid)
707 ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
709 raise Exception("Roaming with the AP timed out")
712 def roam_over_ds(self, bssid):
714 self.request("FT_DS " + bssid)
715 ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
717 raise Exception("Roaming with the AP timed out")
720 def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None,
721 new_passphrase=None, no_wait=False):
724 self.request("WPS_REG " + bssid + " " + pin + " " +
725 new_ssid.encode("hex") + " " + key_mgmt + " " +
726 cipher + " " + new_passphrase.encode("hex"))
729 ev = self.wait_event(["WPS-SUCCESS"], timeout=15)
731 self.request("WPS_REG " + bssid + " " + pin)
734 ev = self.wait_event(["WPS-CRED-RECEIVED"], timeout=15)
736 raise Exception("WPS cred timed out")
737 ev = self.wait_event(["WPS-FAIL"], timeout=15)
739 raise Exception("WPS timed out")
740 ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
742 raise Exception("Association with the AP timed out")
745 self.request("RELOG")
747 def wait_completed(self, timeout=10):
748 for i in range(0, timeout * 2):
749 if self.get_status_field("wpa_state") == "COMPLETED":
752 raise Exception("Timeout while waiting for COMPLETED state")
754 def get_capability(self, field):
755 res = self.request("GET_CAPABILITY " + field)
758 return res.split(' ')
760 def get_bss(self, bssid):
761 res = self.request("BSS " + bssid)
762 lines = res.splitlines()
765 [name,value] = l.split('=', 1)
769 def get_pmksa(self, bssid):
770 res = self.request("PMKSA")
771 lines = res.splitlines()
776 [index,aa,pmkid,expiration,opportunistic] = l.split(' ')
777 vals['index'] = index
778 vals['pmkid'] = pmkid
779 vals['expiration'] = expiration
780 vals['opportunistic'] = opportunistic