1 # Python class for controlling hostapd
2 # Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
4 # This software may be distributed under the terms of the BSD license.
5 # See README for more details.
17 logger = logging.getLogger()
18 hapd_ctrl = '/var/run/hostapd'
19 hapd_global = '/var/run/hostapd-global'
22 return struct.unpack('6B', binascii.unhexlify(mac.replace(':','')))
25 def __init__(self, apdev=None):
27 hostname = apdev['hostname']
32 self.host = remotehost.Host(hostname)
33 self.hostname = hostname
36 self.ctrl = wpaspy.Ctrl(hapd_global)
37 self.mon = wpaspy.Ctrl(hapd_global)
40 self.ctrl = wpaspy.Ctrl(hostname, port)
41 self.mon = wpaspy.Ctrl(hostname, port)
42 self.dbg = hostname + "/" + str(port)
45 def cmd_execute(self, cmd_array):
46 if self.hostname is None:
47 cmd = ' '.join(cmd_array)
48 proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
49 stdout=subprocess.PIPE, shell=True)
50 out = proc.communicate()[0]
54 return self.host.execute(cmd_array)
56 def request(self, cmd, timeout=10):
57 logger.debug(self.dbg + ": CTRL(global): " + cmd)
58 return self.ctrl.request(cmd, timeout)
60 def wait_event(self, events, timeout):
63 while self.mon.pending():
65 logger.debug(self.dbg + "(global): " + ev)
70 remaining = start + timeout - now
73 if not self.mon.pending(timeout=remaining):
77 def add(self, ifname, driver=None):
78 cmd = "ADD " + ifname + " " + hapd_ctrl
81 res = self.request(cmd)
83 raise Exception("Could not add hostapd interface " + ifname)
85 def add_iface(self, ifname, confname):
86 res = self.request("ADD " + ifname + " config=" + confname)
88 raise Exception("Could not add hostapd interface")
90 def add_bss(self, phy, confname, ignore_error=False):
91 res = self.request("ADD bss_config=" + phy + ":" + confname)
94 raise Exception("Could not add hostapd BSS")
96 def remove(self, ifname):
97 self.request("REMOVE " + ifname, timeout=30)
100 self.request("RELOG")
103 self.request("FLUSH")
105 def get_ctrl_iface_port(self, ifname):
106 if self.hostname is None:
109 res = self.request("INTERFACES ctrl")
110 lines = res.splitlines()
114 if words[0] == ifname:
118 raise Exception("Could not find UDP port for " + ifname)
119 res = line.find("ctrl_iface=udp:")
121 raise Exception("Wrong ctrl_interface format")
122 words = line.split(":")
129 self.ctrl.terminate()
133 def __init__(self, ifname, bssidx=0, hostname=None, port=8877):
134 self.hostname = hostname
135 self.host = remotehost.Host(hostname, ifname)
138 self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
139 self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
142 self.ctrl = wpaspy.Ctrl(hostname, port)
143 self.mon = wpaspy.Ctrl(hostname, port)
144 self.dbg = hostname + "/" + ifname
149 def cmd_execute(self, cmd_array):
150 if self.hostname is None:
152 for arg in cmd_array:
155 proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
156 stdout=subprocess.PIPE, shell=True)
157 out = proc.communicate()[0]
158 ret = proc.returncode
161 return self.host.execute(cmd_array)
163 def close_ctrl(self):
164 if self.mon is not None:
172 if self.bssid is None:
173 self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
176 def request(self, cmd):
177 logger.debug(self.dbg + ": CTRL: " + cmd)
178 return self.ctrl.request(cmd)
181 return "PONG" in self.request("PING")
183 def set(self, field, value):
184 if not "OK" in self.request("SET " + field + " " + value):
185 raise Exception("Failed to set hostapd parameter " + field)
187 def set_defaults(self):
188 self.set("driver", "nl80211")
189 self.set("hw_mode", "g")
190 self.set("channel", "1")
191 self.set("ieee80211n", "1")
192 self.set("logger_stdout", "-1")
193 self.set("logger_stdout_level", "0")
195 def set_open(self, ssid):
197 self.set("ssid", ssid)
199 def set_wpa2_psk(self, ssid, passphrase):
201 self.set("ssid", ssid)
202 self.set("wpa_passphrase", passphrase)
204 self.set("wpa_key_mgmt", "WPA-PSK")
205 self.set("rsn_pairwise", "CCMP")
207 def set_wpa_psk(self, ssid, passphrase):
209 self.set("ssid", ssid)
210 self.set("wpa_passphrase", passphrase)
212 self.set("wpa_key_mgmt", "WPA-PSK")
213 self.set("wpa_pairwise", "TKIP")
215 def set_wpa_psk_mixed(self, ssid, passphrase):
217 self.set("ssid", ssid)
218 self.set("wpa_passphrase", passphrase)
220 self.set("wpa_key_mgmt", "WPA-PSK")
221 self.set("wpa_pairwise", "TKIP")
222 self.set("rsn_pairwise", "CCMP")
224 def set_wep(self, ssid, key):
226 self.set("ssid", ssid)
227 self.set("wep_key0", key)
230 if not "OK" in self.request("ENABLE"):
231 raise Exception("Failed to enable hostapd interface " + self.ifname)
234 if not "OK" in self.request("DISABLE"):
235 raise Exception("Failed to disable hostapd interface " + self.ifname)
237 def dump_monitor(self):
238 while self.mon.pending():
240 logger.debug(self.dbg + ": " + ev)
242 def wait_event(self, events, timeout):
243 start = os.times()[4]
245 while self.mon.pending():
247 logger.debug(self.dbg + ": " + ev)
252 remaining = start + timeout - now
255 if not self.mon.pending(timeout=remaining):
259 def get_status(self):
260 res = self.request("STATUS")
261 lines = res.splitlines()
264 [name,value] = l.split('=', 1)
268 def get_status_field(self, field):
269 vals = self.get_status()
274 def get_driver_status(self):
275 res = self.request("STATUS-DRIVER")
276 lines = res.splitlines()
279 [name,value] = l.split('=', 1)
283 def get_driver_status_field(self, field):
284 vals = self.get_driver_status()
289 def get_config(self):
290 res = self.request("GET_CONFIG")
291 lines = res.splitlines()
294 [name,value] = l.split('=', 1)
298 def mgmt_rx(self, timeout=5):
299 ev = self.wait_event(["MGMT-RX"], timeout=timeout)
303 frame = binascii.unhexlify(ev.split(' ')[1])
306 hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
308 msg['subtype'] = (hdr[0] >> 4) & 0xf
310 msg['duration'] = hdr[0]
312 msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
314 msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
316 msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
318 msg['seq_ctrl'] = hdr[0]
319 msg['payload'] = frame[24:]
323 def mgmt_tx(self, msg):
324 t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
325 hdr = struct.pack('<HH6B6B6BH', *t)
326 self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']))
328 def get_sta(self, addr, info=None, next=False):
329 cmd = "STA-NEXT " if next else "STA "
331 res = self.request("STA-FIRST")
333 res = self.request(cmd + addr + " " + info)
335 res = self.request(cmd + addr)
336 lines = res.splitlines()
340 if first and '=' not in l:
344 [name,value] = l.split('=', 1)
348 def get_mib(self, param=None):
350 res = self.request("MIB " + param)
352 res = self.request("MIB")
353 lines = res.splitlines()
356 name_val = l.split('=', 1)
357 if len(name_val) > 1:
358 vals[name_val[0]] = name_val[1]
361 def get_pmksa(self, addr):
362 res = self.request("PMKSA")
363 lines = res.splitlines()
368 [index,aa,pmkid,expiration,opportunistic] = l.split(' ')
369 vals['index'] = index
370 vals['pmkid'] = pmkid
371 vals['expiration'] = expiration
372 vals['opportunistic'] = opportunistic
376 def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30):
377 if isinstance(apdev, dict):
378 ifname = apdev['ifname']
380 hostname = apdev['hostname']
382 logger.info("Starting AP " + hostname + "/" + port + " " + ifname)
384 logger.info("Starting AP " + ifname)
389 logger.info("Starting AP " + ifname + " (old add_ap argument type)")
392 hapd_global = HostapdGlobal(apdev)
393 hapd_global.remove(ifname)
394 hapd_global.add(ifname)
395 port = hapd_global.get_ctrl_iface_port(ifname)
396 hapd = Hostapd(ifname, hostname=hostname, port=port)
398 raise Exception("Could not ping hostapd")
400 fields = [ "ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
402 "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
403 "acct_server_addr", "osu_server_uri" ]
406 hapd.set(field, params[field])
407 for f,v in params.items():
410 if isinstance(v, list):
419 ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=timeout)
421 raise Exception("AP startup timed out")
422 if "AP-ENABLED" not in ev:
423 raise Exception("AP startup failed")
426 def add_bss(apdev, ifname, confname, ignore_error=False):
427 phy = utils.get_phy(apdev)
429 hostname = apdev['hostname']
431 logger.info("Starting BSS " + hostname + "/" + port + " phy=" + phy + " ifname=" + ifname)
433 logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
436 hapd_global = HostapdGlobal(apdev)
437 hapd_global.add_bss(phy, confname, ignore_error)
438 port = hapd_global.get_ctrl_iface_port(ifname)
439 hapd = Hostapd(ifname, hostname=hostname, port=port)
441 raise Exception("Could not ping hostapd")
444 def add_iface(apdev, confname):
445 ifname = apdev['ifname']
447 hostname = apdev['hostname']
449 logger.info("Starting interface " + hostname + "/" + port + " " + ifname)
451 logger.info("Starting interface " + ifname)
454 hapd_global = HostapdGlobal(apdev)
455 hapd_global.add_iface(ifname, confname)
456 port = hapd_global.get_ctrl_iface_port(ifname)
457 hapd = Hostapd(ifname, hostname=hostname, port=port)
459 raise Exception("Could not ping hostapd")
462 def remove_bss(apdev, ifname=None):
464 ifname = apdev['ifname']
466 hostname = apdev['hostname']
468 logger.info("Removing BSS " + hostname + "/" + port + " " + ifname)
470 logger.info("Removing BSS " + ifname)
471 hapd_global = HostapdGlobal(apdev)
472 hapd_global.remove(ifname)
474 def terminate(apdev):
476 hostname = apdev['hostname']
478 logger.info("Terminating hostapd " + hostname + "/" + port)
480 logger.info("Terminating hostapd")
481 hapd_global = HostapdGlobal(apdev)
482 hapd_global.terminate()
484 def wpa2_params(ssid=None, passphrase=None):
485 params = { "wpa": "2",
486 "wpa_key_mgmt": "WPA-PSK",
487 "rsn_pairwise": "CCMP" }
489 params["ssid"] = ssid
491 params["wpa_passphrase"] = passphrase
494 def wpa_params(ssid=None, passphrase=None):
495 params = { "wpa": "1",
496 "wpa_key_mgmt": "WPA-PSK",
497 "wpa_pairwise": "TKIP" }
499 params["ssid"] = ssid
501 params["wpa_passphrase"] = passphrase
504 def wpa_mixed_params(ssid=None, passphrase=None):
505 params = { "wpa": "3",
506 "wpa_key_mgmt": "WPA-PSK",
507 "wpa_pairwise": "TKIP",
508 "rsn_pairwise": "CCMP" }
510 params["ssid"] = ssid
512 params["wpa_passphrase"] = passphrase
516 params = { "auth_server_addr": "127.0.0.1",
517 "auth_server_port": "1812",
518 "auth_server_shared_secret": "radius",
519 "nas_identifier": "nas.w1.fi" }
522 def wpa_eap_params(ssid=None):
523 params = radius_params()
525 params["wpa_key_mgmt"] = "WPA-EAP"
526 params["wpa_pairwise"] = "TKIP"
527 params["ieee8021x"] = "1"
529 params["ssid"] = ssid
532 def wpa2_eap_params(ssid=None):
533 params = radius_params()
535 params["wpa_key_mgmt"] = "WPA-EAP"
536 params["rsn_pairwise"] = "CCMP"
537 params["ieee8021x"] = "1"
539 params["ssid"] = ssid
542 def b_only_params(channel="1", ssid=None, country=None):
543 params = { "hw_mode" : "b",
544 "channel" : channel }
546 params["ssid"] = ssid
548 params["country_code"] = country
551 def g_only_params(channel="1", ssid=None, country=None):
552 params = { "hw_mode" : "g",
553 "channel" : channel }
555 params["ssid"] = ssid
557 params["country_code"] = country
560 def a_only_params(channel="36", ssid=None, country=None):
561 params = { "hw_mode" : "a",
562 "channel" : channel }
564 params["ssid"] = ssid
566 params["country_code"] = country
569 def ht20_params(channel="1", ssid=None, country=None):
570 params = { "ieee80211n" : "1",
573 if int(channel) > 14:
574 params["hw_mode"] = "a"
576 params["ssid"] = ssid
578 params["country_code"] = country
581 def ht40_plus_params(channel="1", ssid=None, country=None):
582 params = ht20_params(channel, ssid, country)
583 params['ht_capab'] = "[HT40+]"
586 def ht40_minus_params(channel="1", ssid=None, country=None):
587 params = ht20_params(channel, ssid, country)
588 params['ht_capab'] = "[HT40-]"
591 def cmd_execute(apdev, cmd):
592 hapd_global = HostapdGlobal(apdev)
593 return hapd_global.cmd_execute(cmd)