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.
14 logger = logging.getLogger()
15 hapd_ctrl = '/var/run/hostapd'
16 hapd_global = '/var/run/hostapd-global'
19 return struct.unpack('6B', binascii.unhexlify(mac.replace(':','')))
23 self.ctrl = wpaspy.Ctrl(hapd_global)
24 self.mon = wpaspy.Ctrl(hapd_global)
27 def request(self, cmd):
28 return self.ctrl.request(cmd)
30 def wait_event(self, events, timeout):
33 while self.mon.pending():
35 logger.debug("(global): " + ev)
40 remaining = start + timeout - now
43 if not self.mon.pending(timeout=remaining):
47 def request(self, cmd):
48 return self.ctrl.request(cmd)
50 def add(self, ifname, driver=None):
51 cmd = "ADD " + ifname + " " + hapd_ctrl
54 res = self.ctrl.request(cmd)
56 raise Exception("Could not add hostapd interface " + ifname)
58 def add_iface(self, ifname, confname):
59 res = self.ctrl.request("ADD " + ifname + " config=" + confname)
61 raise Exception("Could not add hostapd interface")
63 def add_bss(self, phy, confname, ignore_error=False):
64 res = self.ctrl.request("ADD bss_config=" + phy + ":" + confname)
67 raise Exception("Could not add hostapd BSS")
69 def remove(self, ifname):
70 self.ctrl.request("REMOVE " + ifname, timeout=30)
73 self.ctrl.request("RELOG")
76 self.ctrl.request("FLUSH")
80 def __init__(self, ifname, bssidx=0):
82 self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
83 self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
89 if self.bssid is None:
90 self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
93 def request(self, cmd):
94 logger.debug(self.ifname + ": CTRL: " + cmd)
95 return self.ctrl.request(cmd)
98 return "PONG" in self.request("PING")
100 def set(self, field, value):
101 if not "OK" in self.request("SET " + field + " " + value):
102 raise Exception("Failed to set hostapd parameter " + field)
104 def set_defaults(self):
105 self.set("driver", "nl80211")
106 self.set("hw_mode", "g")
107 self.set("channel", "1")
108 self.set("ieee80211n", "1")
109 self.set("logger_stdout", "-1")
110 self.set("logger_stdout_level", "0")
112 def set_open(self, ssid):
114 self.set("ssid", ssid)
116 def set_wpa2_psk(self, ssid, passphrase):
118 self.set("ssid", ssid)
119 self.set("wpa_passphrase", passphrase)
121 self.set("wpa_key_mgmt", "WPA-PSK")
122 self.set("rsn_pairwise", "CCMP")
124 def set_wpa_psk(self, ssid, passphrase):
126 self.set("ssid", ssid)
127 self.set("wpa_passphrase", passphrase)
129 self.set("wpa_key_mgmt", "WPA-PSK")
130 self.set("wpa_pairwise", "TKIP")
132 def set_wpa_psk_mixed(self, ssid, passphrase):
134 self.set("ssid", ssid)
135 self.set("wpa_passphrase", passphrase)
137 self.set("wpa_key_mgmt", "WPA-PSK")
138 self.set("wpa_pairwise", "TKIP")
139 self.set("rsn_pairwise", "CCMP")
141 def set_wep(self, ssid, key):
143 self.set("ssid", ssid)
144 self.set("wep_key0", key)
147 if not "OK" in self.request("ENABLE"):
148 raise Exception("Failed to enable hostapd interface " + self.ifname)
151 if not "OK" in self.request("DISABLE"):
152 raise Exception("Failed to disable hostapd interface " + self.ifname)
154 def dump_monitor(self):
155 while self.mon.pending():
157 logger.debug(self.ifname + ": " + ev)
159 def wait_event(self, events, timeout):
160 start = os.times()[4]
162 while self.mon.pending():
164 logger.debug(self.ifname + ": " + ev)
169 remaining = start + timeout - now
172 if not self.mon.pending(timeout=remaining):
176 def get_status(self):
177 res = self.request("STATUS")
178 lines = res.splitlines()
181 [name,value] = l.split('=', 1)
185 def get_status_field(self, field):
186 vals = self.get_status()
191 def get_driver_status(self):
192 res = self.request("STATUS-DRIVER")
193 lines = res.splitlines()
196 [name,value] = l.split('=', 1)
200 def get_driver_status_field(self, field):
201 vals = self.get_driver_status()
206 def get_config(self):
207 res = self.request("GET_CONFIG")
208 lines = res.splitlines()
211 [name,value] = l.split('=', 1)
215 def mgmt_rx(self, timeout=5):
216 ev = self.wait_event(["MGMT-RX"], timeout=timeout)
220 frame = binascii.unhexlify(ev.split(' ')[1])
223 hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
225 msg['subtype'] = (hdr[0] >> 4) & 0xf
227 msg['duration'] = hdr[0]
229 msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
231 msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
233 msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
235 msg['seq_ctrl'] = hdr[0]
236 msg['payload'] = frame[24:]
240 def mgmt_tx(self, msg):
241 t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
242 hdr = struct.pack('<HH6B6B6BH', *t)
243 self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']))
245 def get_sta(self, addr, info=None, next=False):
246 cmd = "STA-NEXT " if next else "STA "
248 res = self.request("STA-FIRST")
250 res = self.request(cmd + addr + " " + info)
252 res = self.request(cmd + addr)
253 lines = res.splitlines()
257 if first and '=' not in l:
261 [name,value] = l.split('=', 1)
265 def get_mib(self, param=None):
267 res = self.request("MIB " + param)
269 res = self.request("MIB")
270 lines = res.splitlines()
273 name_val = l.split('=', 1)
274 if len(name_val) > 1:
275 vals[name_val[0]] = name_val[1]
278 def add_ap(ifname, params, wait_enabled=True, no_enable=False):
279 logger.info("Starting AP " + ifname)
280 hapd_global = HostapdGlobal()
281 hapd_global.remove(ifname)
282 hapd_global.add(ifname)
283 hapd = Hostapd(ifname)
285 raise Exception("Could not ping hostapd")
287 fields = [ "ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
289 "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
290 "acct_server_addr", "osu_server_uri" ]
293 hapd.set(field, params[field])
294 for f,v in params.items():
297 if isinstance(v, list):
306 ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
308 raise Exception("AP startup timed out")
309 if "AP-ENABLED" not in ev:
310 raise Exception("AP startup failed")
313 def add_bss(phy, ifname, confname, ignore_error=False):
314 logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
315 hapd_global = HostapdGlobal()
316 hapd_global.add_bss(phy, confname, ignore_error)
317 hapd = Hostapd(ifname)
319 raise Exception("Could not ping hostapd")
321 def add_iface(ifname, confname):
322 logger.info("Starting interface " + ifname)
323 hapd_global = HostapdGlobal()
324 hapd_global.add_iface(ifname, confname)
325 hapd = Hostapd(ifname)
327 raise Exception("Could not ping hostapd")
329 def remove_bss(ifname):
330 logger.info("Removing BSS " + ifname)
331 hapd_global = HostapdGlobal()
332 hapd_global.remove(ifname)
334 def wpa2_params(ssid=None, passphrase=None):
335 params = { "wpa": "2",
336 "wpa_key_mgmt": "WPA-PSK",
337 "rsn_pairwise": "CCMP" }
339 params["ssid"] = ssid
341 params["wpa_passphrase"] = passphrase
344 def wpa_params(ssid=None, passphrase=None):
345 params = { "wpa": "1",
346 "wpa_key_mgmt": "WPA-PSK",
347 "wpa_pairwise": "TKIP" }
349 params["ssid"] = ssid
351 params["wpa_passphrase"] = passphrase
354 def wpa_mixed_params(ssid=None, passphrase=None):
355 params = { "wpa": "3",
356 "wpa_key_mgmt": "WPA-PSK",
357 "wpa_pairwise": "TKIP",
358 "rsn_pairwise": "CCMP" }
360 params["ssid"] = ssid
362 params["wpa_passphrase"] = passphrase
366 params = { "auth_server_addr": "127.0.0.1",
367 "auth_server_port": "1812",
368 "auth_server_shared_secret": "radius",
369 "nas_identifier": "nas.w1.fi" }
372 def wpa_eap_params(ssid=None):
373 params = radius_params()
375 params["wpa_key_mgmt"] = "WPA-EAP"
376 params["wpa_pairwise"] = "TKIP"
377 params["ieee8021x"] = "1"
379 params["ssid"] = ssid
382 def wpa2_eap_params(ssid=None):
383 params = radius_params()
385 params["wpa_key_mgmt"] = "WPA-EAP"
386 params["rsn_pairwise"] = "CCMP"
387 params["ieee8021x"] = "1"
389 params["ssid"] = ssid