]>
Commit | Line | Data |
---|---|---|
b8cd4c54 | 1 | # Python class for controlling hostapd |
02a4ac0f | 2 | # Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi> |
b8cd4c54 JM |
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 | |
bfe375ec JM |
10 | import binascii |
11 | import struct | |
b8cd4c54 | 12 | import wpaspy |
8ce4855b | 13 | import remotehost |
9cd6f4c0 | 14 | import utils |
7fd9fbc2 | 15 | import subprocess |
b8cd4c54 | 16 | |
c9aa4308 | 17 | logger = logging.getLogger() |
b8cd4c54 | 18 | hapd_ctrl = '/var/run/hostapd' |
8c87f65f | 19 | hapd_global = '/var/run/hostapd-global' |
b8cd4c54 | 20 | |
bfe375ec | 21 | def mac2tuple(mac): |
fab49f61 | 22 | return struct.unpack('6B', binascii.unhexlify(mac.replace(':', ''))) |
bfe375ec | 23 | |
b8cd4c54 | 24 | class HostapdGlobal: |
02a4ac0f | 25 | def __init__(self, apdev=None, global_ctrl_override=None): |
625bf744 JD |
26 | try: |
27 | hostname = apdev['hostname'] | |
28 | port = apdev['port'] | |
29 | except: | |
30 | hostname = None | |
31 | port = 8878 | |
8ce4855b | 32 | self.host = remotehost.Host(hostname) |
cb73f7e8 JD |
33 | self.hostname = hostname |
34 | self.port = port | |
35 | if hostname is None: | |
02a4ac0f JM |
36 | global_ctrl = hapd_global |
37 | if global_ctrl_override: | |
38 | global_ctrl = global_ctrl_override | |
39 | self.ctrl = wpaspy.Ctrl(global_ctrl) | |
40 | self.mon = wpaspy.Ctrl(global_ctrl) | |
d4944fad | 41 | self.dbg = "" |
cb73f7e8 JD |
42 | else: |
43 | self.ctrl = wpaspy.Ctrl(hostname, port) | |
44 | self.mon = wpaspy.Ctrl(hostname, port) | |
d4944fad | 45 | self.dbg = hostname + "/" + str(port) |
41a256ec AN |
46 | self.mon.attach() |
47 | ||
e9f2d54f | 48 | def cmd_execute(self, cmd_array, shell=False): |
7fd9fbc2 | 49 | if self.hostname is None: |
e9f2d54f JM |
50 | if shell: |
51 | cmd = ' '.join(cmd_array) | |
52 | else: | |
53 | cmd = cmd_array | |
7fd9fbc2 | 54 | proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT, |
e9f2d54f | 55 | stdout=subprocess.PIPE, shell=shell) |
7fd9fbc2 JA |
56 | out = proc.communicate()[0] |
57 | ret = proc.returncode | |
45b0b88f | 58 | return ret, out.decode() |
7fd9fbc2 JA |
59 | else: |
60 | return self.host.execute(cmd_array) | |
61 | ||
d4944fad JD |
62 | def request(self, cmd, timeout=10): |
63 | logger.debug(self.dbg + ": CTRL(global): " + cmd) | |
64 | return self.ctrl.request(cmd, timeout) | |
41a256ec AN |
65 | |
66 | def wait_event(self, events, timeout): | |
67 | start = os.times()[4] | |
68 | while True: | |
69 | while self.mon.pending(): | |
70 | ev = self.mon.recv() | |
d4944fad | 71 | logger.debug(self.dbg + "(global): " + ev) |
41a256ec AN |
72 | for event in events: |
73 | if event in ev: | |
74 | return ev | |
75 | now = os.times()[4] | |
76 | remaining = start + timeout - now | |
77 | if remaining <= 0: | |
78 | break | |
79 | if not self.mon.pending(timeout=remaining): | |
80 | break | |
81 | return None | |
b8cd4c54 | 82 | |
08763216 JM |
83 | def add(self, ifname, driver=None): |
84 | cmd = "ADD " + ifname + " " + hapd_ctrl | |
85 | if driver: | |
86 | cmd += " " + driver | |
d4944fad | 87 | res = self.request(cmd) |
a8b8da11 | 88 | if "OK" not in res: |
b8cd4c54 JM |
89 | raise Exception("Could not add hostapd interface " + ifname) |
90 | ||
77990cd7 | 91 | def add_iface(self, ifname, confname): |
d4944fad | 92 | res = self.request("ADD " + ifname + " config=" + confname) |
a8b8da11 | 93 | if "OK" not in res: |
77990cd7 JM |
94 | raise Exception("Could not add hostapd interface") |
95 | ||
a6333977 | 96 | def add_bss(self, phy, confname, ignore_error=False): |
d4944fad | 97 | res = self.request("ADD bss_config=" + phy + ":" + confname) |
a8b8da11 | 98 | if "OK" not in res: |
a6333977 JM |
99 | if not ignore_error: |
100 | raise Exception("Could not add hostapd BSS") | |
101 | ||
b8cd4c54 | 102 | def remove(self, ifname): |
d4944fad | 103 | self.request("REMOVE " + ifname, timeout=30) |
b8cd4c54 | 104 | |
75428961 | 105 | def relog(self): |
d4944fad | 106 | self.request("RELOG") |
75428961 | 107 | |
f8949f5f | 108 | def flush(self): |
d4944fad | 109 | self.request("FLUSH") |
f8949f5f | 110 | |
4d48d44c JD |
111 | def get_ctrl_iface_port(self, ifname): |
112 | if self.hostname is None: | |
113 | return None | |
114 | ||
d4944fad | 115 | res = self.request("INTERFACES ctrl") |
4d48d44c JD |
116 | lines = res.splitlines() |
117 | found = False | |
118 | for line in lines: | |
119 | words = line.split() | |
120 | if words[0] == ifname: | |
121 | found = True | |
122 | break | |
123 | if not found: | |
124 | raise Exception("Could not find UDP port for " + ifname) | |
125 | res = line.find("ctrl_iface=udp:") | |
126 | if res == -1: | |
127 | raise Exception("Wrong ctrl_interface format") | |
128 | words = line.split(":") | |
129 | return int(words[1]) | |
b8cd4c54 | 130 | |
e3b36d42 JD |
131 | def terminate(self): |
132 | self.mon.detach() | |
133 | self.mon.close() | |
134 | self.mon = None | |
135 | self.ctrl.terminate() | |
136 | self.ctrl = None | |
137 | ||
b8cd4c54 | 138 | class Hostapd: |
cb73f7e8 | 139 | def __init__(self, ifname, bssidx=0, hostname=None, port=8877): |
e2f3f023 | 140 | self.hostname = hostname |
8ce4855b | 141 | self.host = remotehost.Host(hostname, ifname) |
b8cd4c54 | 142 | self.ifname = ifname |
cb73f7e8 JD |
143 | if hostname is None: |
144 | self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname)) | |
145 | self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname)) | |
d4944fad | 146 | self.dbg = ifname |
cb73f7e8 JD |
147 | else: |
148 | self.ctrl = wpaspy.Ctrl(hostname, port) | |
149 | self.mon = wpaspy.Ctrl(hostname, port) | |
d4944fad | 150 | self.dbg = hostname + "/" + ifname |
b47750be | 151 | self.mon.attach() |
f6420942 | 152 | self.bssid = None |
54cf411f | 153 | self.bssidx = bssidx |
f6420942 | 154 | |
e9f2d54f | 155 | def cmd_execute(self, cmd_array, shell=False): |
7fd9fbc2 | 156 | if self.hostname is None: |
e9f2d54f JM |
157 | if shell: |
158 | cmd = ' '.join(cmd_array) | |
159 | else: | |
160 | cmd = cmd_array | |
7fd9fbc2 | 161 | proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT, |
e9f2d54f | 162 | stdout=subprocess.PIPE, shell=shell) |
7fd9fbc2 JA |
163 | out = proc.communicate()[0] |
164 | ret = proc.returncode | |
45b0b88f | 165 | return ret, out.decode() |
7fd9fbc2 JA |
166 | else: |
167 | return self.host.execute(cmd_array) | |
168 | ||
e3b36d42 JD |
169 | def close_ctrl(self): |
170 | if self.mon is not None: | |
171 | self.mon.detach() | |
172 | self.mon.close() | |
173 | self.mon = None | |
174 | self.ctrl.close() | |
175 | self.ctrl = None | |
176 | ||
f6420942 JM |
177 | def own_addr(self): |
178 | if self.bssid is None: | |
54cf411f | 179 | self.bssid = self.get_status_field('bssid[%d]' % self.bssidx) |
f6420942 | 180 | return self.bssid |
b8cd4c54 JM |
181 | |
182 | def request(self, cmd): | |
d4944fad | 183 | logger.debug(self.dbg + ": CTRL: " + cmd) |
b8cd4c54 JM |
184 | return self.ctrl.request(cmd) |
185 | ||
186 | def ping(self): | |
187 | return "PONG" in self.request("PING") | |
188 | ||
189 | def set(self, field, value): | |
a8b8da11 | 190 | if "OK" not in self.request("SET " + field + " " + value): |
b8cd4c54 JM |
191 | raise Exception("Failed to set hostapd parameter " + field) |
192 | ||
193 | def set_defaults(self): | |
194 | self.set("driver", "nl80211") | |
195 | self.set("hw_mode", "g") | |
196 | self.set("channel", "1") | |
197 | self.set("ieee80211n", "1") | |
789b9f1d JM |
198 | self.set("logger_stdout", "-1") |
199 | self.set("logger_stdout_level", "0") | |
b8cd4c54 JM |
200 | |
201 | def set_open(self, ssid): | |
202 | self.set_defaults() | |
203 | self.set("ssid", ssid) | |
204 | ||
205 | def set_wpa2_psk(self, ssid, passphrase): | |
206 | self.set_defaults() | |
207 | self.set("ssid", ssid) | |
208 | self.set("wpa_passphrase", passphrase) | |
209 | self.set("wpa", "2") | |
210 | self.set("wpa_key_mgmt", "WPA-PSK") | |
211 | self.set("rsn_pairwise", "CCMP") | |
212 | ||
e492837b JM |
213 | def set_wpa_psk(self, ssid, passphrase): |
214 | self.set_defaults() | |
215 | self.set("ssid", ssid) | |
216 | self.set("wpa_passphrase", passphrase) | |
217 | self.set("wpa", "1") | |
218 | self.set("wpa_key_mgmt", "WPA-PSK") | |
219 | self.set("wpa_pairwise", "TKIP") | |
220 | ||
221 | def set_wpa_psk_mixed(self, ssid, passphrase): | |
222 | self.set_defaults() | |
223 | self.set("ssid", ssid) | |
224 | self.set("wpa_passphrase", passphrase) | |
225 | self.set("wpa", "3") | |
226 | self.set("wpa_key_mgmt", "WPA-PSK") | |
227 | self.set("wpa_pairwise", "TKIP") | |
228 | self.set("rsn_pairwise", "CCMP") | |
229 | ||
0165c4be JM |
230 | def set_wep(self, ssid, key): |
231 | self.set_defaults() | |
232 | self.set("ssid", ssid) | |
233 | self.set("wep_key0", key) | |
234 | ||
b8cd4c54 | 235 | def enable(self): |
a8b8da11 | 236 | if "OK" not in self.request("ENABLE"): |
b8cd4c54 JM |
237 | raise Exception("Failed to enable hostapd interface " + self.ifname) |
238 | ||
239 | def disable(self): | |
a8b8da11 | 240 | if "OK" not in self.request("DISABLE"): |
b8cd4c54 | 241 | raise Exception("Failed to disable hostapd interface " + self.ifname) |
e259d186 | 242 | |
b47750be JM |
243 | def dump_monitor(self): |
244 | while self.mon.pending(): | |
245 | ev = self.mon.recv() | |
d4944fad | 246 | logger.debug(self.dbg + ": " + ev) |
b47750be JM |
247 | |
248 | def wait_event(self, events, timeout): | |
d1fb06c9 JM |
249 | if not isinstance(events, list): |
250 | raise Exception("Hostapd.wait_event() called with incorrect events argument type") | |
36408936 JM |
251 | start = os.times()[4] |
252 | while True: | |
b47750be JM |
253 | while self.mon.pending(): |
254 | ev = self.mon.recv() | |
d4944fad | 255 | logger.debug(self.dbg + ": " + ev) |
b47750be JM |
256 | for event in events: |
257 | if event in ev: | |
258 | return ev | |
36408936 JM |
259 | now = os.times()[4] |
260 | remaining = start + timeout - now | |
261 | if remaining <= 0: | |
262 | break | |
263 | if not self.mon.pending(timeout=remaining): | |
264 | break | |
b47750be JM |
265 | return None |
266 | ||
938c6e7b | 267 | def wait_sta(self, addr=None, timeout=2): |
c4a9610e | 268 | ev = self.wait_event(["AP-STA-CONNECT"], timeout=timeout) |
938c6e7b JM |
269 | if ev is None: |
270 | raise Exception("AP did not report STA connection") | |
271 | if addr and addr not in ev: | |
272 | raise Exception("Unexpected STA address in connection event: " + ev) | |
273 | ||
b47750be JM |
274 | def get_status(self): |
275 | res = self.request("STATUS") | |
276 | lines = res.splitlines() | |
277 | vals = dict() | |
278 | for l in lines: | |
fab49f61 | 279 | [name, value] = l.split('=', 1) |
b47750be JM |
280 | vals[name] = value |
281 | return vals | |
282 | ||
283 | def get_status_field(self, field): | |
284 | vals = self.get_status() | |
285 | if field in vals: | |
286 | return vals[field] | |
287 | return None | |
288 | ||
a36158be JM |
289 | def get_driver_status(self): |
290 | res = self.request("STATUS-DRIVER") | |
291 | lines = res.splitlines() | |
292 | vals = dict() | |
293 | for l in lines: | |
fab49f61 | 294 | [name, value] = l.split('=', 1) |
a36158be JM |
295 | vals[name] = value |
296 | return vals | |
297 | ||
298 | def get_driver_status_field(self, field): | |
299 | vals = self.get_driver_status() | |
300 | if field in vals: | |
301 | return vals[field] | |
302 | return None | |
303 | ||
65038313 JM |
304 | def get_config(self): |
305 | res = self.request("GET_CONFIG") | |
306 | lines = res.splitlines() | |
307 | vals = dict() | |
308 | for l in lines: | |
fab49f61 | 309 | [name, value] = l.split('=', 1) |
65038313 JM |
310 | vals[name] = value |
311 | return vals | |
312 | ||
bfe375ec JM |
313 | def mgmt_rx(self, timeout=5): |
314 | ev = self.wait_event(["MGMT-RX"], timeout=timeout) | |
315 | if ev is None: | |
316 | return None | |
317 | msg = {} | |
318 | frame = binascii.unhexlify(ev.split(' ')[1]) | |
319 | msg['frame'] = frame | |
320 | ||
321 | hdr = struct.unpack('<HH6B6B6BH', frame[0:24]) | |
322 | msg['fc'] = hdr[0] | |
323 | msg['subtype'] = (hdr[0] >> 4) & 0xf | |
324 | hdr = hdr[1:] | |
325 | msg['duration'] = hdr[0] | |
326 | hdr = hdr[1:] | |
327 | msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6] | |
328 | hdr = hdr[6:] | |
329 | msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6] | |
330 | hdr = hdr[6:] | |
331 | msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6] | |
332 | hdr = hdr[6:] | |
333 | msg['seq_ctrl'] = hdr[0] | |
334 | msg['payload'] = frame[24:] | |
335 | ||
336 | return msg | |
337 | ||
338 | def mgmt_tx(self, msg): | |
339 | t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,) | |
340 | hdr = struct.pack('<HH6B6B6BH', *t) | |
7ab74770 MH |
341 | res = self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']).decode()) |
342 | if "OK" not in res: | |
0851a180 | 343 | raise Exception("MGMT_TX command to hostapd failed") |
bfe375ec | 344 | |
cce26eb4 JM |
345 | def get_sta(self, addr, info=None, next=False): |
346 | cmd = "STA-NEXT " if next else "STA " | |
347 | if addr is None: | |
348 | res = self.request("STA-FIRST") | |
349 | elif info: | |
350 | res = self.request(cmd + addr + " " + info) | |
5dec879d | 351 | else: |
cce26eb4 | 352 | res = self.request(cmd + addr) |
6435799b JM |
353 | lines = res.splitlines() |
354 | vals = dict() | |
355 | first = True | |
356 | for l in lines: | |
2496adf0 | 357 | if first and '=' not in l: |
6435799b JM |
358 | vals['addr'] = l |
359 | first = False | |
360 | else: | |
fab49f61 | 361 | [name, value] = l.split('=', 1) |
6435799b JM |
362 | vals[name] = value |
363 | return vals | |
364 | ||
4fcee244 JM |
365 | def get_mib(self, param=None): |
366 | if param: | |
367 | res = self.request("MIB " + param) | |
368 | else: | |
369 | res = self.request("MIB") | |
7fd15145 JM |
370 | lines = res.splitlines() |
371 | vals = dict() | |
372 | for l in lines: | |
4fcee244 JM |
373 | name_val = l.split('=', 1) |
374 | if len(name_val) > 1: | |
375 | vals[name_val[0]] = name_val[1] | |
7fd15145 JM |
376 | return vals |
377 | ||
865fa1e9 JM |
378 | def get_pmksa(self, addr): |
379 | res = self.request("PMKSA") | |
380 | lines = res.splitlines() | |
381 | for l in lines: | |
382 | if addr not in l: | |
383 | continue | |
384 | vals = dict() | |
fab49f61 | 385 | [index, aa, pmkid, expiration, opportunistic] = l.split(' ') |
865fa1e9 JM |
386 | vals['index'] = index |
387 | vals['pmkid'] = pmkid | |
388 | vals['expiration'] = expiration | |
389 | vals['opportunistic'] = opportunistic | |
390 | return vals | |
391 | return None | |
392 | ||
0422d06b JM |
393 | def dpp_qr_code(self, uri): |
394 | res = self.request("DPP_QR_CODE " + uri) | |
395 | if "FAIL" in res: | |
396 | raise Exception("Failed to parse QR Code URI") | |
397 | return int(res) | |
a5387062 JM |
398 | |
399 | def dpp_bootstrap_gen(self, type="qrcode", chan=None, mac=None, info=None, | |
400 | curve=None, key=None): | |
401 | cmd = "DPP_BOOTSTRAP_GEN type=" + type | |
402 | if chan: | |
403 | cmd += " chan=" + chan | |
404 | if mac: | |
405 | if mac is True: | |
406 | mac = self.own_addr() | |
407 | cmd += " mac=" + mac.replace(':', '') | |
408 | if info: | |
409 | cmd += " info=" + info | |
410 | if curve: | |
411 | cmd += " curve=" + curve | |
412 | if key: | |
413 | cmd += " key=" + key | |
414 | res = self.request(cmd) | |
415 | if "FAIL" in res: | |
416 | raise Exception("Failed to generate bootstrapping info") | |
417 | return int(res) | |
0422d06b | 418 | |
7e009100 JM |
419 | def dpp_listen(self, freq, netrole=None, qr=None, role=None): |
420 | cmd = "DPP_LISTEN " + str(freq) | |
421 | if netrole: | |
422 | cmd += " netrole=" + netrole | |
423 | if qr: | |
424 | cmd += " qr=" + qr | |
425 | if role: | |
426 | cmd += " role=" + role | |
427 | if "OK" not in self.request(cmd): | |
428 | raise Exception("Failed to start listen operation") | |
429 | ||
5725b3e3 JM |
430 | def dpp_auth_init(self, peer=None, uri=None, conf=None, configurator=None, |
431 | extra=None, own=None, role=None, neg_freq=None, | |
432 | ssid=None, passphrase=None, expect_fail=False): | |
433 | cmd = "DPP_AUTH_INIT" | |
434 | if peer is None: | |
435 | peer = self.dpp_qr_code(uri) | |
436 | cmd += " peer=%d" % peer | |
437 | if own is not None: | |
438 | cmd += " own=%d" % own | |
439 | if role: | |
440 | cmd += " role=" + role | |
441 | if extra: | |
442 | cmd += " " + extra | |
443 | if conf: | |
444 | cmd += " conf=" + conf | |
445 | if configurator is not None: | |
446 | cmd += " configurator=%d" % configurator | |
447 | if neg_freq: | |
448 | cmd += " neg_freq=%d" % neg_freq | |
449 | if ssid: | |
450 | cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode() | |
451 | if passphrase: | |
452 | cmd += " pass=" + binascii.hexlify(passphrase.encode()).decode() | |
453 | res = self.request(cmd) | |
454 | if expect_fail: | |
455 | if "FAIL" not in res: | |
456 | raise Exception("DPP authentication started unexpectedly") | |
457 | return | |
458 | if "OK" not in res: | |
459 | raise Exception("Failed to initiate DPP Authentication") | |
460 | ||
6d196e59 JM |
461 | def dpp_pkex_init(self, identifier, code, role=None, key=None, curve=None, |
462 | extra=None, use_id=None): | |
463 | if use_id is None: | |
464 | id1 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve) | |
465 | else: | |
466 | id1 = use_id | |
467 | cmd = "own=%d " % id1 | |
468 | if identifier: | |
469 | cmd += "identifier=%s " % identifier | |
470 | cmd += "init=1 " | |
471 | if role: | |
472 | cmd += "role=%s " % role | |
473 | if extra: | |
474 | cmd += extra + " " | |
475 | cmd += "code=%s" % code | |
476 | res = self.request("DPP_PKEX_ADD " + cmd) | |
477 | if "FAIL" in res: | |
478 | raise Exception("Failed to set PKEX data (initiator)") | |
479 | return id1 | |
480 | ||
481 | def dpp_pkex_resp(self, freq, identifier, code, key=None, curve=None, | |
482 | listen_role=None): | |
483 | id0 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve) | |
484 | cmd = "own=%d " % id0 | |
485 | if identifier: | |
486 | cmd += "identifier=%s " % identifier | |
487 | cmd += "code=%s" % code | |
488 | res = self.request("DPP_PKEX_ADD " + cmd) | |
489 | if "FAIL" in res: | |
490 | raise Exception("Failed to set PKEX data (responder)") | |
491 | self.dpp_listen(freq, role=listen_role) | |
492 | ||
e105110f JM |
493 | def dpp_configurator_add(self, curve=None, key=None): |
494 | cmd = "DPP_CONFIGURATOR_ADD" | |
495 | if curve: | |
496 | cmd += " curve=" + curve | |
497 | if key: | |
498 | cmd += " key=" + key | |
499 | res = self.request(cmd) | |
500 | if "FAIL" in res: | |
501 | raise Exception("Failed to add configurator") | |
502 | return int(res) | |
503 | ||
504 | def dpp_configurator_remove(self, conf_id): | |
505 | res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id) | |
506 | if "OK" not in res: | |
507 | raise Exception("DPP_CONFIGURATOR_REMOVE failed") | |
508 | ||
431802df JM |
509 | def note(self, txt): |
510 | self.request("NOTE " + txt) | |
511 | ||
02a4ac0f | 512 | def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30, |
e374def2 | 513 | global_ctrl_override=None, driver=False): |
78b83193 JD |
514 | if isinstance(apdev, dict): |
515 | ifname = apdev['ifname'] | |
516 | try: | |
517 | hostname = apdev['hostname'] | |
518 | port = apdev['port'] | |
519 | logger.info("Starting AP " + hostname + "/" + port + " " + ifname) | |
520 | except: | |
521 | logger.info("Starting AP " + ifname) | |
522 | hostname = None | |
523 | port = 8878 | |
524 | else: | |
525 | ifname = apdev | |
526 | logger.info("Starting AP " + ifname + " (old add_ap argument type)") | |
527 | hostname = None | |
528 | port = 8878 | |
02a4ac0f JM |
529 | hapd_global = HostapdGlobal(apdev, |
530 | global_ctrl_override=global_ctrl_override) | |
e259d186 | 531 | hapd_global.remove(ifname) |
e374def2 | 532 | hapd_global.add(ifname, driver=driver) |
4d48d44c JD |
533 | port = hapd_global.get_ctrl_iface_port(ifname) |
534 | hapd = Hostapd(ifname, hostname=hostname, port=port) | |
e259d186 JM |
535 | if not hapd.ping(): |
536 | raise Exception("Could not ping hostapd") | |
537 | hapd.set_defaults() | |
fab49f61 JM |
538 | fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt", |
539 | "wpa", | |
540 | "wpa_pairwise", "rsn_pairwise", "auth_server_addr", | |
541 | "acct_server_addr", "osu_server_uri"] | |
e259d186 JM |
542 | for field in fields: |
543 | if field in params: | |
544 | hapd.set(field, params[field]) | |
fab49f61 | 545 | for f, v in list(params.items()): |
93a06242 JM |
546 | if f in fields: |
547 | continue | |
548 | if isinstance(v, list): | |
549 | for val in v: | |
550 | hapd.set(f, val) | |
551 | else: | |
552 | hapd.set(f, v) | |
138ec97e JM |
553 | if no_enable: |
554 | return hapd | |
e259d186 | 555 | hapd.enable() |
629dbdd3 | 556 | if wait_enabled: |
57ff37d0 | 557 | ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=timeout) |
629dbdd3 JM |
558 | if ev is None: |
559 | raise Exception("AP startup timed out") | |
f8ad9dc2 JM |
560 | if "AP-ENABLED" not in ev: |
561 | raise Exception("AP startup failed") | |
b47750be | 562 | return hapd |
e259d186 | 563 | |
9cd6f4c0 JD |
564 | def add_bss(apdev, ifname, confname, ignore_error=False): |
565 | phy = utils.get_phy(apdev) | |
566 | try: | |
567 | hostname = apdev['hostname'] | |
568 | port = apdev['port'] | |
569 | logger.info("Starting BSS " + hostname + "/" + port + " phy=" + phy + " ifname=" + ifname) | |
570 | except: | |
571 | logger.info("Starting BSS phy=" + phy + " ifname=" + ifname) | |
572 | hostname = None | |
573 | port = 8878 | |
625bf744 | 574 | hapd_global = HostapdGlobal(apdev) |
a6333977 | 575 | hapd_global.add_bss(phy, confname, ignore_error) |
4d48d44c JD |
576 | port = hapd_global.get_ctrl_iface_port(ifname) |
577 | hapd = Hostapd(ifname, hostname=hostname, port=port) | |
a6333977 JM |
578 | if not hapd.ping(): |
579 | raise Exception("Could not ping hostapd") | |
fb1a7dcc | 580 | return hapd |
a6333977 | 581 | |
29444a08 JD |
582 | def add_iface(apdev, confname): |
583 | ifname = apdev['ifname'] | |
584 | try: | |
585 | hostname = apdev['hostname'] | |
586 | port = apdev['port'] | |
587 | logger.info("Starting interface " + hostname + "/" + port + " " + ifname) | |
588 | except: | |
589 | logger.info("Starting interface " + ifname) | |
590 | hostname = None | |
591 | port = 8878 | |
625bf744 | 592 | hapd_global = HostapdGlobal(apdev) |
77990cd7 | 593 | hapd_global.add_iface(ifname, confname) |
4d48d44c JD |
594 | port = hapd_global.get_ctrl_iface_port(ifname) |
595 | hapd = Hostapd(ifname, hostname=hostname, port=port) | |
77990cd7 JM |
596 | if not hapd.ping(): |
597 | raise Exception("Could not ping hostapd") | |
fb1a7dcc | 598 | return hapd |
77990cd7 | 599 | |
c92ee957 JD |
600 | def remove_bss(apdev, ifname=None): |
601 | if ifname == None: | |
602 | ifname = apdev['ifname'] | |
603 | try: | |
604 | hostname = apdev['hostname'] | |
605 | port = apdev['port'] | |
606 | logger.info("Removing BSS " + hostname + "/" + port + " " + ifname) | |
607 | except: | |
608 | logger.info("Removing BSS " + ifname) | |
625bf744 | 609 | hapd_global = HostapdGlobal(apdev) |
a6333977 JM |
610 | hapd_global.remove(ifname) |
611 | ||
369f712a JD |
612 | def terminate(apdev): |
613 | try: | |
614 | hostname = apdev['hostname'] | |
615 | port = apdev['port'] | |
625bf744 | 616 | logger.info("Terminating hostapd " + hostname + "/" + port) |
369f712a | 617 | except: |
369f712a | 618 | logger.info("Terminating hostapd") |
625bf744 | 619 | hapd_global = HostapdGlobal(apdev) |
e3b36d42 JD |
620 | hapd_global.terminate() |
621 | ||
e259d186 | 622 | def wpa2_params(ssid=None, passphrase=None): |
fab49f61 JM |
623 | params = {"wpa": "2", |
624 | "wpa_key_mgmt": "WPA-PSK", | |
625 | "rsn_pairwise": "CCMP"} | |
e259d186 JM |
626 | if ssid: |
627 | params["ssid"] = ssid | |
628 | if passphrase: | |
629 | params["wpa_passphrase"] = passphrase | |
630 | return params | |
631 | ||
632 | def wpa_params(ssid=None, passphrase=None): | |
fab49f61 JM |
633 | params = {"wpa": "1", |
634 | "wpa_key_mgmt": "WPA-PSK", | |
635 | "wpa_pairwise": "TKIP"} | |
e259d186 JM |
636 | if ssid: |
637 | params["ssid"] = ssid | |
638 | if passphrase: | |
639 | params["wpa_passphrase"] = passphrase | |
640 | return params | |
641 | ||
642 | def wpa_mixed_params(ssid=None, passphrase=None): | |
fab49f61 JM |
643 | params = {"wpa": "3", |
644 | "wpa_key_mgmt": "WPA-PSK", | |
645 | "wpa_pairwise": "TKIP", | |
646 | "rsn_pairwise": "CCMP"} | |
e259d186 JM |
647 | if ssid: |
648 | params["ssid"] = ssid | |
649 | if passphrase: | |
650 | params["wpa_passphrase"] = passphrase | |
651 | return params | |
9626962d JM |
652 | |
653 | def radius_params(): | |
fab49f61 JM |
654 | params = {"auth_server_addr": "127.0.0.1", |
655 | "auth_server_port": "1812", | |
656 | "auth_server_shared_secret": "radius", | |
657 | "nas_identifier": "nas.w1.fi"} | |
9626962d JM |
658 | return params |
659 | ||
71390dc8 JM |
660 | def wpa_eap_params(ssid=None): |
661 | params = radius_params() | |
662 | params["wpa"] = "1" | |
663 | params["wpa_key_mgmt"] = "WPA-EAP" | |
664 | params["wpa_pairwise"] = "TKIP" | |
665 | params["ieee8021x"] = "1" | |
666 | if ssid: | |
667 | params["ssid"] = ssid | |
668 | return params | |
669 | ||
9626962d JM |
670 | def wpa2_eap_params(ssid=None): |
671 | params = radius_params() | |
672 | params["wpa"] = "2" | |
673 | params["wpa_key_mgmt"] = "WPA-EAP" | |
674 | params["rsn_pairwise"] = "CCMP" | |
675 | params["ieee8021x"] = "1" | |
676 | if ssid: | |
677 | params["ssid"] = ssid | |
678 | return params | |
c0ca24fc JD |
679 | |
680 | def b_only_params(channel="1", ssid=None, country=None): | |
fab49f61 JM |
681 | params = {"hw_mode": "b", |
682 | "channel": channel} | |
c0ca24fc JD |
683 | if ssid: |
684 | params["ssid"] = ssid | |
685 | if country: | |
686 | params["country_code"] = country | |
687 | return params | |
688 | ||
689 | def g_only_params(channel="1", ssid=None, country=None): | |
fab49f61 JM |
690 | params = {"hw_mode": "g", |
691 | "channel": channel} | |
c0ca24fc JD |
692 | if ssid: |
693 | params["ssid"] = ssid | |
694 | if country: | |
695 | params["country_code"] = country | |
696 | return params | |
697 | ||
698 | def a_only_params(channel="36", ssid=None, country=None): | |
fab49f61 JM |
699 | params = {"hw_mode": "a", |
700 | "channel": channel} | |
c0ca24fc JD |
701 | if ssid: |
702 | params["ssid"] = ssid | |
703 | if country: | |
704 | params["country_code"] = country | |
705 | return params | |
706 | ||
707 | def ht20_params(channel="1", ssid=None, country=None): | |
fab49f61 JM |
708 | params = {"ieee80211n": "1", |
709 | "channel": channel, | |
710 | "hw_mode": "g"} | |
c0ca24fc JD |
711 | if int(channel) > 14: |
712 | params["hw_mode"] = "a" | |
713 | if ssid: | |
714 | params["ssid"] = ssid | |
715 | if country: | |
716 | params["country_code"] = country | |
717 | return params | |
718 | ||
719 | def ht40_plus_params(channel="1", ssid=None, country=None): | |
720 | params = ht20_params(channel, ssid, country) | |
721 | params['ht_capab'] = "[HT40+]" | |
722 | return params | |
723 | ||
724 | def ht40_minus_params(channel="1", ssid=None, country=None): | |
725 | params = ht20_params(channel, ssid, country) | |
726 | params['ht_capab'] = "[HT40-]" | |
727 | return params | |
7fd9fbc2 | 728 | |
e9f2d54f | 729 | def cmd_execute(apdev, cmd, shell=False): |
7fd9fbc2 | 730 | hapd_global = HostapdGlobal(apdev) |
e9f2d54f | 731 | return hapd_global.cmd_execute(cmd, shell=shell) |