]>
Commit | Line | Data |
---|---|---|
1ae73b03 JM |
1 | #!/usr/bin/python |
2 | # | |
3 | # Python class for controlling wpa_supplicant | |
4 | # Copyright (c) 2013, Jouni Malinen <j@w1.fi> | |
5 | # | |
6 | # This software may be distributed under the terms of the BSD license. | |
7 | # See README for more details. | |
8 | ||
9 | import os | |
10 | import time | |
11 | import logging | |
c68f9a61 | 12 | import re |
6ca3a98b | 13 | import subprocess |
1ae73b03 JM |
14 | import wpaspy |
15 | ||
c9aa4308 | 16 | logger = logging.getLogger() |
1ae73b03 JM |
17 | wpas_ctrl = '/var/run/wpa_supplicant' |
18 | ||
19 | class WpaSupplicant: | |
0fa28afe | 20 | def __init__(self, ifname, global_iface=None): |
1ae73b03 | 21 | self.ifname = ifname |
f3f8ee88 | 22 | self.group_ifname = None |
1ae73b03 JM |
23 | self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) |
24 | self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) | |
25 | self.mon.attach() | |
26 | ||
0fa28afe JM |
27 | self.global_iface = global_iface |
28 | if global_iface: | |
29 | self.global_ctrl = wpaspy.Ctrl(global_iface) | |
30 | self.global_mon = wpaspy.Ctrl(global_iface) | |
31 | self.global_mon.attach() | |
32 | ||
1ae73b03 JM |
33 | def request(self, cmd): |
34 | logger.debug(self.ifname + ": CTRL: " + cmd) | |
35 | return self.ctrl.request(cmd) | |
36 | ||
0fa28afe JM |
37 | def global_request(self, cmd): |
38 | if self.global_iface is None: | |
39 | self.request(cmd) | |
40 | else: | |
41 | logger.debug(self.ifname + ": CTRL: " + cmd) | |
42 | return self.global_ctrl.request(cmd) | |
43 | ||
f3f8ee88 JM |
44 | def group_request(self, cmd): |
45 | if self.group_ifname and self.group_ifname != self.ifname: | |
46 | logger.debug(self.group_ifname + ": CTRL: " + cmd) | |
47 | gctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname)) | |
48 | return gctrl.request(cmd) | |
49 | return self.request(cmd) | |
50 | ||
1ae73b03 JM |
51 | def ping(self): |
52 | return "PONG" in self.request("PING") | |
53 | ||
54 | def reset(self): | |
ea0e92ee JM |
55 | res = self.request("FLUSH") |
56 | if not "OK" in res: | |
57 | logger.info("FLUSH to " + self.ifname + " failed: " + res) | |
803edd1c | 58 | self.request("SET ignore_old_scan_res 0") |
efd43d85 | 59 | self.request("SET external_sim 0") |
715bf904 JM |
60 | self.request("SET hessid 00:00:00:00:00:00") |
61 | self.request("SET access_network_type 15") | |
46f2cfce JM |
62 | self.request("SET p2p_add_cli_chan 0") |
63 | self.request("SET p2p_no_go_freq ") | |
64 | self.request("SET p2p_pref_chan ") | |
b4264f8f | 65 | self.request("SET disallow_aps ") |
19ae1d07 | 66 | self.request("SET p2p_no_group_iface 1") |
b162675f | 67 | self.request("P2P_SET per_sta_psk 0") |
7ebf841f | 68 | self.request("P2P_SET disabled 0") |
5f3eddac | 69 | self.request("P2P_SERVICE_FLUSH") |
f3f8ee88 | 70 | self.group_ifname = None |
6edaee9c | 71 | self.dump_monitor() |
6ca3a98b JM |
72 | |
73 | iter = 0 | |
74 | while iter < 60: | |
75 | state = self.get_driver_status_field("scan_state") | |
76 | if "SCAN_STARTED" in state or "SCAN_REQUESTED" in state: | |
77 | logger.info(self.ifname + ": Waiting for scan operation to complete before continuing") | |
78 | time.sleep(1) | |
79 | else: | |
80 | break | |
81 | iter = iter + 1 | |
82 | if iter == 60: | |
83 | logger.error(self.ifname + ": Driver scan state did not clear") | |
84 | print "Trying to clear cfg80211/mac80211 scan state" | |
85 | try: | |
86 | cmd = ["sudo", "ifconfig", self.ifname, "down"] | |
87 | subprocess.call(cmd) | |
88 | except subprocess.CalledProcessError, e: | |
89 | logger.info("ifconfig failed: " + str(e.returncode)) | |
90 | logger.info(e.output) | |
91 | try: | |
92 | cmd = ["sudo", "ifconfig", self.ifname, "up"] | |
93 | subprocess.call(cmd) | |
94 | except subprocess.CalledProcessError, e: | |
95 | logger.info("ifconfig failed: " + str(e.returncode)) | |
96 | logger.info(e.output) | |
97 | ||
ea0e92ee JM |
98 | if not self.ping(): |
99 | logger.info("No PING response from " + self.ifname + " after reset") | |
1ae73b03 | 100 | |
07a2e61b JM |
101 | def add_network(self): |
102 | id = self.request("ADD_NETWORK") | |
103 | if "FAIL" in id: | |
104 | raise Exception("ADD_NETWORK failed") | |
105 | return int(id) | |
106 | ||
107 | def remove_network(self, id): | |
108 | id = self.request("REMOVE_NETWORK " + str(id)) | |
109 | if "FAIL" in id: | |
110 | raise Exception("REMOVE_NETWORK failed") | |
111 | return None | |
112 | ||
113 | def set_network(self, id, field, value): | |
114 | res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value) | |
115 | if "FAIL" in res: | |
116 | raise Exception("SET_NETWORK failed") | |
117 | return None | |
118 | ||
119 | def set_network_quoted(self, id, field, value): | |
120 | res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"') | |
121 | if "FAIL" in res: | |
122 | raise Exception("SET_NETWORK failed") | |
123 | return None | |
124 | ||
6a0b4002 JM |
125 | def list_networks(self): |
126 | res = self.request("LIST_NETWORKS") | |
127 | lines = res.splitlines() | |
128 | networks = [] | |
129 | for l in lines: | |
130 | if "network id" in l: | |
131 | continue | |
132 | [id,ssid,bssid,flags] = l.split('\t') | |
133 | network = {} | |
134 | network['id'] = id | |
135 | network['ssid'] = ssid | |
136 | network['bssid'] = bssid | |
137 | network['flags'] = flags | |
138 | networks.append(network) | |
139 | return networks | |
140 | ||
bbe86767 JM |
141 | def hs20_enable(self): |
142 | self.request("SET interworking 1") | |
143 | self.request("SET hs20 1") | |
144 | ||
93a06242 JM |
145 | def add_cred(self): |
146 | id = self.request("ADD_CRED") | |
147 | if "FAIL" in id: | |
148 | raise Exception("ADD_CRED failed") | |
149 | return int(id) | |
150 | ||
151 | def remove_cred(self, id): | |
152 | id = self.request("REMOVE_CRED " + str(id)) | |
153 | if "FAIL" in id: | |
154 | raise Exception("REMOVE_CRED failed") | |
155 | return None | |
156 | ||
157 | def set_cred(self, id, field, value): | |
158 | res = self.request("SET_CRED " + str(id) + " " + field + " " + value) | |
159 | if "FAIL" in res: | |
160 | raise Exception("SET_CRED failed") | |
161 | return None | |
162 | ||
163 | def set_cred_quoted(self, id, field, value): | |
164 | res = self.request("SET_CRED " + str(id) + " " + field + ' "' + value + '"') | |
165 | if "FAIL" in res: | |
166 | raise Exception("SET_CRED failed") | |
167 | return None | |
168 | ||
2232edf8 | 169 | def add_cred_values(self, params): |
bbe86767 | 170 | id = self.add_cred() |
2232edf8 | 171 | |
d355372c | 172 | quoted = [ "realm", "username", "password", "domain", "imsi", |
dcd68168 JM |
173 | "excluded_ssid", "milenage", "ca_cert", "client_cert", |
174 | "private_key" ] | |
2232edf8 JM |
175 | for field in quoted: |
176 | if field in params: | |
177 | self.set_cred_quoted(id, field, params[field]) | |
178 | ||
e209eb98 JM |
179 | not_quoted = [ "eap", "roaming_consortium", |
180 | "required_roaming_consortium" ] | |
2232edf8 JM |
181 | for field in not_quoted: |
182 | if field in params: | |
183 | self.set_cred(id, field, params[field]) | |
184 | ||
bbe86767 JM |
185 | return id; |
186 | ||
81266da7 JM |
187 | def select_network(self, id): |
188 | id = self.request("SELECT_NETWORK " + str(id)) | |
189 | if "FAIL" in id: | |
190 | raise Exception("SELECT_NETWORK failed") | |
191 | return None | |
192 | ||
193 | def connect_network(self, id): | |
194 | self.dump_monitor() | |
195 | self.select_network(id) | |
196 | ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10) | |
197 | if ev is None: | |
198 | raise Exception("Association with the AP timed out") | |
199 | self.dump_monitor() | |
200 | ||
302b7a1b | 201 | def get_status(self): |
1ae73b03 JM |
202 | res = self.request("STATUS") |
203 | lines = res.splitlines() | |
302b7a1b | 204 | vals = dict() |
1ae73b03 JM |
205 | for l in lines: |
206 | [name,value] = l.split('=', 1) | |
302b7a1b JM |
207 | vals[name] = value |
208 | return vals | |
209 | ||
210 | def get_status_field(self, field): | |
211 | vals = self.get_status() | |
212 | if field in vals: | |
213 | return vals[field] | |
1ae73b03 JM |
214 | return None |
215 | ||
302b7a1b | 216 | def get_group_status(self): |
7cb08cdb JM |
217 | res = self.group_request("STATUS") |
218 | lines = res.splitlines() | |
302b7a1b | 219 | vals = dict() |
7cb08cdb JM |
220 | for l in lines: |
221 | [name,value] = l.split('=', 1) | |
302b7a1b JM |
222 | vals[name] = value |
223 | return vals | |
224 | ||
225 | def get_group_status_field(self, field): | |
226 | vals = self.get_group_status() | |
227 | if field in vals: | |
228 | return vals[field] | |
7cb08cdb JM |
229 | return None |
230 | ||
6ca3a98b JM |
231 | def get_driver_status(self): |
232 | res = self.request("STATUS-DRIVER") | |
233 | lines = res.splitlines() | |
234 | vals = dict() | |
235 | for l in lines: | |
236 | [name,value] = l.split('=', 1) | |
237 | vals[name] = value | |
238 | return vals | |
239 | ||
240 | def get_driver_status_field(self, field): | |
241 | vals = self.get_driver_status() | |
242 | if field in vals: | |
243 | return vals[field] | |
244 | return None | |
245 | ||
1ae73b03 | 246 | def p2p_dev_addr(self): |
302b7a1b | 247 | return self.get_status_field("p2p_device_address") |
1ae73b03 | 248 | |
7cb08cdb | 249 | def p2p_interface_addr(self): |
302b7a1b | 250 | return self.get_group_status_field("address") |
7cb08cdb | 251 | |
1ae73b03 | 252 | def p2p_listen(self): |
0fa28afe | 253 | return self.global_request("P2P_LISTEN") |
1ae73b03 JM |
254 | |
255 | def p2p_find(self, social=False): | |
256 | if social: | |
0fa28afe JM |
257 | return self.global_request("P2P_FIND type=social") |
258 | return self.global_request("P2P_FIND") | |
1ae73b03 | 259 | |
5743006d | 260 | def p2p_stop_find(self): |
0fa28afe | 261 | return self.global_request("P2P_STOP_FIND") |
5743006d | 262 | |
1ae73b03 JM |
263 | def wps_read_pin(self): |
264 | #TODO: make this random | |
265 | self.pin = "12345670" | |
266 | return self.pin | |
267 | ||
268 | def peer_known(self, peer, full=True): | |
0fa28afe | 269 | res = self.global_request("P2P_PEER " + peer) |
1ae73b03 JM |
270 | if peer.lower() not in res.lower(): |
271 | return False | |
272 | if not full: | |
273 | return True | |
274 | return "[PROBE_REQ_ONLY]" not in res | |
275 | ||
d963f037 | 276 | def discover_peer(self, peer, full=True, timeout=15, social=True): |
1ae73b03 JM |
277 | logger.info(self.ifname + ": Trying to discover peer " + peer) |
278 | if self.peer_known(peer, full): | |
279 | return True | |
d963f037 | 280 | self.p2p_find(social) |
1ae73b03 JM |
281 | count = 0 |
282 | while count < timeout: | |
283 | time.sleep(1) | |
284 | count = count + 1 | |
285 | if self.peer_known(peer, full): | |
286 | return True | |
287 | return False | |
288 | ||
451afb4f JM |
289 | def get_peer(self, peer): |
290 | res = self.global_request("P2P_PEER " + peer) | |
291 | if peer.lower() not in res.lower(): | |
292 | raise Exception("Peer information not available") | |
293 | lines = res.splitlines() | |
294 | vals = dict() | |
295 | for l in lines: | |
296 | if '=' in l: | |
297 | [name,value] = l.split('=', 1) | |
298 | vals[name] = value | |
299 | return vals | |
300 | ||
46f2cfce | 301 | def group_form_result(self, ev, expect_failure=False, go_neg_res=None): |
f7b1a750 JM |
302 | if expect_failure: |
303 | if "P2P-GROUP-STARTED" in ev: | |
304 | raise Exception("Group formation succeeded when expecting failure") | |
305 | exp = r'<.>(P2P-GO-NEG-FAILURE) status=([0-9]*)' | |
306 | s = re.split(exp, ev) | |
307 | if len(s) < 3: | |
308 | return None | |
309 | res = {} | |
310 | res['result'] = 'go-neg-failed' | |
311 | res['status'] = int(s[2]) | |
312 | return res | |
313 | ||
314 | if "P2P-GROUP-STARTED" not in ev: | |
315 | raise Exception("No P2P-GROUP-STARTED event seen") | |
316 | ||
c68f9a61 JM |
317 | exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*)' |
318 | s = re.split(exp, ev) | |
319 | if len(s) < 8: | |
320 | raise Exception("Could not parse P2P-GROUP-STARTED") | |
321 | res = {} | |
322 | res['result'] = 'success' | |
323 | res['ifname'] = s[2] | |
f3f8ee88 | 324 | self.group_ifname = s[2] |
c68f9a61 JM |
325 | res['role'] = s[3] |
326 | res['ssid'] = s[4] | |
327 | res['freq'] = s[5] | |
451afb4f JM |
328 | if "[PERSISTENT]" in ev: |
329 | res['persistent'] = True | |
330 | else: | |
331 | res['persistent'] = False | |
c68f9a61 JM |
332 | p = re.match(r'psk=([0-9a-f]*)', s[6]) |
333 | if p: | |
334 | res['psk'] = p.group(1) | |
335 | p = re.match(r'passphrase="(.*)"', s[6]) | |
336 | if p: | |
337 | res['passphrase'] = p.group(1) | |
338 | res['go_dev_addr'] = s[7] | |
46f2cfce JM |
339 | |
340 | if go_neg_res: | |
341 | exp = r'<.>(P2P-GO-NEG-SUCCESS) role=(GO|client) freq=([0-9]*)' | |
342 | s = re.split(exp, go_neg_res) | |
343 | if len(s) < 4: | |
344 | raise Exception("Could not parse P2P-GO-NEG-SUCCESS") | |
345 | res['go_neg_role'] = s[2] | |
346 | res['go_neg_freq'] = s[3] | |
347 | ||
c68f9a61 JM |
348 | return res |
349 | ||
46f2cfce | 350 | def p2p_go_neg_auth(self, peer, pin, method, go_intent=None, persistent=False, freq=None): |
1ae73b03 JM |
351 | if not self.discover_peer(peer): |
352 | raise Exception("Peer " + peer + " not found") | |
353 | self.dump_monitor() | |
354 | cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth" | |
809079d3 JM |
355 | if go_intent: |
356 | cmd = cmd + ' go_intent=' + str(go_intent) | |
46f2cfce JM |
357 | if freq: |
358 | cmd = cmd + ' freq=' + str(freq) | |
451afb4f JM |
359 | if persistent: |
360 | cmd = cmd + " persistent" | |
0fa28afe | 361 | if "OK" in self.global_request(cmd): |
1ae73b03 JM |
362 | return None |
363 | raise Exception("P2P_CONNECT (auth) failed") | |
364 | ||
809079d3 | 365 | def p2p_go_neg_auth_result(self, timeout=1, expect_failure=False): |
46f2cfce JM |
366 | go_neg_res = None |
367 | ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS", | |
368 | "P2P-GO-NEG-FAILURE"], timeout); | |
c68f9a61 | 369 | if ev is None: |
809079d3 JM |
370 | if expect_failure: |
371 | return None | |
c68f9a61 | 372 | raise Exception("Group formation timed out") |
46f2cfce JM |
373 | if "P2P-GO-NEG-SUCCESS" in ev: |
374 | go_neg_res = ev | |
375 | ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout); | |
376 | if ev is None: | |
377 | if expect_failure: | |
378 | return None | |
379 | raise Exception("Group formation timed out") | |
c68f9a61 | 380 | self.dump_monitor() |
46f2cfce | 381 | return self.group_form_result(ev, expect_failure, go_neg_res) |
c68f9a61 | 382 | |
ef2bd5a3 | 383 | def p2p_go_neg_init(self, peer, pin, method, timeout=0, go_intent=None, expect_failure=False, persistent=False, freq=None): |
1ae73b03 JM |
384 | if not self.discover_peer(peer): |
385 | raise Exception("Peer " + peer + " not found") | |
386 | self.dump_monitor() | |
1a4d80b8 JM |
387 | if pin: |
388 | cmd = "P2P_CONNECT " + peer + " " + pin + " " + method | |
389 | else: | |
390 | cmd = "P2P_CONNECT " + peer + " " + method | |
809079d3 JM |
391 | if go_intent: |
392 | cmd = cmd + ' go_intent=' + str(go_intent) | |
ef2bd5a3 JM |
393 | if freq: |
394 | cmd = cmd + ' freq=' + str(freq) | |
451afb4f JM |
395 | if persistent: |
396 | cmd = cmd + " persistent" | |
0fa28afe | 397 | if "OK" in self.global_request(cmd): |
1ae73b03 JM |
398 | if timeout == 0: |
399 | self.dump_monitor() | |
400 | return None | |
46f2cfce JM |
401 | go_neg_res = None |
402 | ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS", | |
403 | "P2P-GO-NEG-FAILURE"], timeout) | |
c68f9a61 | 404 | if ev is None: |
809079d3 JM |
405 | if expect_failure: |
406 | return None | |
c68f9a61 | 407 | raise Exception("Group formation timed out") |
46f2cfce JM |
408 | if "P2P-GO-NEG-SUCCESS" in ev: |
409 | go_neg_res = ev | |
410 | ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout) | |
411 | if ev is None: | |
412 | if expect_failure: | |
413 | return None | |
414 | raise Exception("Group formation timed out") | |
c68f9a61 | 415 | self.dump_monitor() |
46f2cfce | 416 | return self.group_form_result(ev, expect_failure, go_neg_res) |
1ae73b03 JM |
417 | raise Exception("P2P_CONNECT failed") |
418 | ||
cf9189b9 | 419 | def wait_event(self, events, timeout=10): |
1ae73b03 | 420 | count = 0 |
4d7d61b6 | 421 | while count < timeout * 10: |
1ae73b03 | 422 | count = count + 1 |
c68f9a61 | 423 | time.sleep(0.1) |
1ae73b03 JM |
424 | while self.mon.pending(): |
425 | ev = self.mon.recv() | |
809079d3 | 426 | logger.debug(self.ifname + ": " + ev) |
f7b1a750 JM |
427 | for event in events: |
428 | if event in ev: | |
429 | return ev | |
c68f9a61 | 430 | return None |
1ae73b03 | 431 | |
0fa28afe JM |
432 | def wait_global_event(self, events, timeout): |
433 | if self.global_iface is None: | |
434 | self.wait_event(events, timeout) | |
435 | else: | |
436 | count = 0 | |
437 | while count < timeout * 10: | |
438 | count = count + 1 | |
439 | time.sleep(0.1) | |
440 | while self.global_mon.pending(): | |
441 | ev = self.global_mon.recv() | |
9d507452 | 442 | logger.debug(self.ifname + "(global): " + ev) |
0fa28afe JM |
443 | for event in events: |
444 | if event in ev: | |
445 | return ev | |
446 | return None | |
447 | ||
2c914e24 JM |
448 | def wait_go_ending_session(self): |
449 | ev = self.wait_event(["P2P-GROUP-REMOVED"], timeout=3) | |
450 | if ev is None: | |
451 | raise Exception("Group removal event timed out") | |
452 | if "reason=GO_ENDING_SESSION" not in ev: | |
453 | raise Exception("Unexpected group removal reason") | |
454 | ||
1ae73b03 JM |
455 | def dump_monitor(self): |
456 | while self.mon.pending(): | |
457 | ev = self.mon.recv() | |
458 | logger.debug(self.ifname + ": " + ev) | |
9d507452 JM |
459 | while self.global_mon.pending(): |
460 | ev = self.global_mon.recv() | |
461 | logger.debug(self.ifname + "(global): " + ev) | |
3eb29b7b | 462 | |
a311c61d JM |
463 | def remove_group(self, ifname=None): |
464 | if ifname is None: | |
451afb4f | 465 | ifname = self.group_ifname if self.group_ifname else self.ifname |
0fa28afe | 466 | if "OK" not in self.global_request("P2P_GROUP_REMOVE " + ifname): |
3eb29b7b | 467 | raise Exception("Group could not be removed") |
f3f8ee88 | 468 | self.group_ifname = None |
4ea8d3b5 | 469 | |
5924d4c1 | 470 | def p2p_start_go(self, persistent=None, freq=None): |
4ea8d3b5 JM |
471 | self.dump_monitor() |
472 | cmd = "P2P_GROUP_ADD" | |
07a2e61b JM |
473 | if persistent is None: |
474 | pass | |
475 | elif persistent is True: | |
476 | cmd = cmd + " persistent" | |
477 | else: | |
478 | cmd = cmd + " persistent=" + str(persistent) | |
5924d4c1 | 479 | if freq: |
ef2bd5a3 | 480 | cmd = cmd + " freq=" + str(freq) |
0fa28afe JM |
481 | if "OK" in self.global_request(cmd): |
482 | ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout=5) | |
4ea8d3b5 JM |
483 | if ev is None: |
484 | raise Exception("GO start up timed out") | |
485 | self.dump_monitor() | |
486 | return self.group_form_result(ev) | |
487 | raise Exception("P2P_GROUP_ADD failed") | |
488 | ||
489 | def p2p_go_authorize_client(self, pin): | |
490 | cmd = "WPS_PIN any " + pin | |
f3f8ee88 | 491 | if "FAIL" in self.group_request(cmd): |
4ea8d3b5 JM |
492 | raise Exception("Failed to authorize client connection on GO") |
493 | return None | |
494 | ||
b162675f JM |
495 | def p2p_go_authorize_client_pbc(self): |
496 | cmd = "WPS_PBC" | |
497 | if "FAIL" in self.group_request(cmd): | |
498 | raise Exception("Failed to authorize client connection on GO") | |
499 | return None | |
500 | ||
41af1305 | 501 | def p2p_connect_group(self, go_addr, pin, timeout=0, social=False): |
4ea8d3b5 | 502 | self.dump_monitor() |
41af1305 | 503 | if not self.discover_peer(go_addr, social=social): |
4ea8d3b5 JM |
504 | raise Exception("GO " + go_addr + " not found") |
505 | self.dump_monitor() | |
506 | cmd = "P2P_CONNECT " + go_addr + " " + pin + " join" | |
0fa28afe | 507 | if "OK" in self.global_request(cmd): |
4ea8d3b5 JM |
508 | if timeout == 0: |
509 | self.dump_monitor() | |
510 | return None | |
0fa28afe | 511 | ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout) |
4ea8d3b5 JM |
512 | if ev is None: |
513 | raise Exception("Joining the group timed out") | |
514 | self.dump_monitor() | |
515 | return self.group_form_result(ev) | |
516 | raise Exception("P2P_CONNECT(join) failed") | |
7cb08cdb JM |
517 | |
518 | def tdls_setup(self, peer): | |
519 | cmd = "TDLS_SETUP " + peer | |
520 | if "FAIL" in self.group_request(cmd): | |
521 | raise Exception("Failed to request TDLS setup") | |
522 | return None | |
523 | ||
524 | def tdls_teardown(self, peer): | |
525 | cmd = "TDLS_TEARDOWN " + peer | |
526 | if "FAIL" in self.group_request(cmd): | |
527 | raise Exception("Failed to request TDLS teardown") | |
528 | return None | |
b61e418c | 529 | |
9626962d JM |
530 | def connect(self, ssid, psk=None, proto=None, key_mgmt=None, wep_key0=None, |
531 | ieee80211w=None, pairwise=None, group=None, scan_freq=None, | |
532 | eap=None, identity=None, anonymous_identity=None, | |
533 | password=None, phase1=None, phase2=None, ca_cert=None, | |
22b99086 | 534 | domain_suffix_match=None, password_hex=None, |
4a5a5792 | 535 | client_cert=None, private_key=None, peerkey=False, |
9626962d | 536 | wait_connect=True): |
b61e418c JM |
537 | logger.info("Connect STA " + self.ifname + " to AP") |
538 | id = self.add_network() | |
539 | self.set_network_quoted(id, "ssid", ssid) | |
540 | if psk: | |
541 | self.set_network_quoted(id, "psk", psk) | |
542 | if proto: | |
543 | self.set_network(id, "proto", proto) | |
544 | if key_mgmt: | |
545 | self.set_network(id, "key_mgmt", key_mgmt) | |
835a546b JM |
546 | if ieee80211w: |
547 | self.set_network(id, "ieee80211w", ieee80211w) | |
7ebf841f JM |
548 | if pairwise: |
549 | self.set_network(id, "pairwise", pairwise) | |
550 | if group: | |
551 | self.set_network(id, "group", group) | |
b61e418c JM |
552 | if wep_key0: |
553 | self.set_network(id, "wep_key0", wep_key0) | |
7ebf841f JM |
554 | if scan_freq: |
555 | self.set_network(id, "scan_freq", scan_freq) | |
9626962d JM |
556 | if eap: |
557 | self.set_network(id, "eap", eap) | |
558 | if identity: | |
559 | self.set_network_quoted(id, "identity", identity) | |
560 | if anonymous_identity: | |
561 | self.set_network_quoted(id, "anonymous_identity", | |
562 | anonymous_identity) | |
563 | if password: | |
564 | self.set_network_quoted(id, "password", password) | |
22b99086 JM |
565 | if password_hex: |
566 | self.set_network(id, "password", password_hex) | |
9626962d JM |
567 | if ca_cert: |
568 | self.set_network_quoted(id, "ca_cert", ca_cert) | |
e114c49c JM |
569 | if client_cert: |
570 | self.set_network_quoted(id, "client_cert", client_cert) | |
571 | if private_key: | |
572 | self.set_network_quoted(id, "private_key", private_key) | |
9626962d JM |
573 | if phase1: |
574 | self.set_network_quoted(id, "phase1", phase1) | |
575 | if phase2: | |
576 | self.set_network_quoted(id, "phase2", phase2) | |
72c052d5 JM |
577 | if domain_suffix_match: |
578 | self.set_network_quoted(id, "domain_suffix_match", | |
579 | domain_suffix_match) | |
4a5a5792 JM |
580 | if peerkey: |
581 | self.set_network(id, "peerkey", "1") | |
9626962d JM |
582 | if wait_connect: |
583 | self.connect_network(id) | |
584 | else: | |
585 | self.dump_monitor() | |
586 | self.select_network(id) | |
709f18d5 | 587 | return id |
5126138c | 588 | |
0589f401 | 589 | def scan(self, type=None, freq=None): |
5126138c JM |
590 | if type: |
591 | cmd = "SCAN TYPE=" + type | |
592 | else: | |
593 | cmd = "SCAN" | |
0589f401 JM |
594 | if freq: |
595 | cmd = cmd + " freq=" + freq | |
5126138c JM |
596 | self.dump_monitor() |
597 | if not "OK" in self.request(cmd): | |
598 | raise Exception("Failed to trigger scan") | |
599 | ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15) | |
600 | if ev is None: | |
601 | raise Exception("Scan timed out") | |
602 | ||
603 | def roam(self, bssid): | |
604 | self.dump_monitor() | |
605 | self.request("ROAM " + bssid) | |
606 | ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10) | |
607 | if ev is None: | |
608 | raise Exception("Roaming with the AP timed out") | |
609 | self.dump_monitor() | |
6edaee9c | 610 | |
b553eab1 JM |
611 | def roam_over_ds(self, bssid): |
612 | self.dump_monitor() | |
613 | self.request("FT_DS " + bssid) | |
614 | ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10) | |
615 | if ev is None: | |
616 | raise Exception("Roaming with the AP timed out") | |
617 | self.dump_monitor() | |
618 | ||
6edaee9c JM |
619 | def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None, |
620 | new_passphrase=None): | |
621 | self.dump_monitor() | |
622 | if new_ssid: | |
623 | self.request("WPS_REG " + bssid + " " + pin + " " + | |
624 | new_ssid.encode("hex") + " " + key_mgmt + " " + | |
625 | cipher + " " + new_passphrase.encode("hex")) | |
626 | ev = self.wait_event(["WPS-SUCCESS"], timeout=15) | |
627 | else: | |
628 | self.request("WPS_REG " + bssid + " " + pin) | |
629 | ev = self.wait_event(["WPS-CRED-RECEIVED"], timeout=15) | |
630 | if ev is None: | |
631 | raise Exception("WPS cred timed out") | |
632 | ev = self.wait_event(["WPS-FAIL"], timeout=15) | |
633 | if ev is None: | |
634 | raise Exception("WPS timed out") | |
635 | ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15) | |
636 | if ev is None: | |
637 | raise Exception("Association with the AP timed out") | |
57661377 JM |
638 | |
639 | def relog(self): | |
640 | self.request("RELOG") | |
41af1305 JM |
641 | |
642 | def wait_completed(self, timeout=10): | |
643 | for i in range(0, timeout * 2): | |
644 | if self.get_status_field("wpa_state") == "COMPLETED": | |
645 | return | |
646 | time.sleep(0.5) | |
647 | raise Exception("Timeout while waiting for COMPLETED state") | |
0eff1ab3 JM |
648 | |
649 | def get_capability(self, field): | |
650 | res = self.request("GET_CAPABILITY " + field) | |
651 | if "FAIL" in res: | |
652 | return None | |
653 | return res.split(' ') | |
2cdd91d8 JM |
654 | |
655 | def get_bss(self, bssid): | |
656 | res = self.request("BSS " + bssid) | |
657 | lines = res.splitlines() | |
658 | vals = dict() | |
659 | for l in lines: | |
660 | [name,value] = l.split('=', 1) | |
661 | vals[name] = value | |
662 | return vals |