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, shell=False):
46 if self.hostname is None:
48 cmd = ' '.join(cmd_array)
51 proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
52 stdout=subprocess.PIPE, shell=shell)
53 out = proc.communicate()[0]
57 return self.host.execute(cmd_array)
59 def request(self, cmd, timeout=10):
60 logger.debug(self.dbg + ": CTRL(global): " + cmd)
61 return self.ctrl.request(cmd, timeout)
63 def wait_event(self, events, timeout):
66 while self.mon.pending():
68 logger.debug(self.dbg + "(global): " + ev)
73 remaining = start + timeout - now
76 if not self.mon.pending(timeout=remaining):
80 def add(self, ifname, driver=None):
81 cmd = "ADD " + ifname + " " + hapd_ctrl
84 res = self.request(cmd)
86 raise Exception("Could not add hostapd interface " + ifname)
88 def add_iface(self, ifname, confname):
89 res = self.request("ADD " + ifname + " config=" + confname)
91 raise Exception("Could not add hostapd interface")
93 def add_bss(self, phy, confname, ignore_error=False):
94 res = self.request("ADD bss_config=" + phy + ":" + confname)
97 raise Exception("Could not add hostapd BSS")
99 def remove(self, ifname):
100 self.request("REMOVE " + ifname, timeout=30)
103 self.request("RELOG")
106 self.request("FLUSH")
108 def get_ctrl_iface_port(self, ifname):
109 if self.hostname is None:
112 res = self.request("INTERFACES ctrl")
113 lines = res.splitlines()
117 if words[0] == ifname:
121 raise Exception("Could not find UDP port for " + ifname)
122 res = line.find("ctrl_iface=udp:")
124 raise Exception("Wrong ctrl_interface format")
125 words = line.split(":")
132 self.ctrl.terminate()
136 def __init__(self, ifname, bssidx=0, hostname=None, port=8877):
137 self.hostname = hostname
138 self.host = remotehost.Host(hostname, ifname)
141 self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
142 self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
145 self.ctrl = wpaspy.Ctrl(hostname, port)
146 self.mon = wpaspy.Ctrl(hostname, port)
147 self.dbg = hostname + "/" + ifname
152 def cmd_execute(self, cmd_array, shell=False):
153 if self.hostname is None:
155 cmd = ' '.join(cmd_array)
158 proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
159 stdout=subprocess.PIPE, shell=shell)
160 out = proc.communicate()[0]
161 ret = proc.returncode
164 return self.host.execute(cmd_array)
166 def close_ctrl(self):
167 if self.mon is not None:
175 if self.bssid is None:
176 self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
179 def request(self, cmd):
180 logger.debug(self.dbg + ": CTRL: " + cmd)
181 return self.ctrl.request(cmd)
184 return "PONG" in self.request("PING")
186 def set(self, field, value):
187 if not "OK" in self.request("SET " + field + " " + value):
188 raise Exception("Failed to set hostapd parameter " + field)
190 def set_defaults(self):
191 self.set("driver", "nl80211")
192 self.set("hw_mode", "g")
193 self.set("channel", "1")
194 self.set("ieee80211n", "1")
195 self.set("logger_stdout", "-1")
196 self.set("logger_stdout_level", "0")
198 def set_open(self, ssid):
200 self.set("ssid", ssid)
202 def set_wpa2_psk(self, ssid, passphrase):
204 self.set("ssid", ssid)
205 self.set("wpa_passphrase", passphrase)
207 self.set("wpa_key_mgmt", "WPA-PSK")
208 self.set("rsn_pairwise", "CCMP")
210 def set_wpa_psk(self, ssid, passphrase):
212 self.set("ssid", ssid)
213 self.set("wpa_passphrase", passphrase)
215 self.set("wpa_key_mgmt", "WPA-PSK")
216 self.set("wpa_pairwise", "TKIP")
218 def set_wpa_psk_mixed(self, ssid, passphrase):
220 self.set("ssid", ssid)
221 self.set("wpa_passphrase", passphrase)
223 self.set("wpa_key_mgmt", "WPA-PSK")
224 self.set("wpa_pairwise", "TKIP")
225 self.set("rsn_pairwise", "CCMP")
227 def set_wep(self, ssid, key):
229 self.set("ssid", ssid)
230 self.set("wep_key0", key)
233 if not "OK" in self.request("ENABLE"):
234 raise Exception("Failed to enable hostapd interface " + self.ifname)
237 if not "OK" in self.request("DISABLE"):
238 raise Exception("Failed to disable hostapd interface " + self.ifname)
240 def dump_monitor(self):
241 while self.mon.pending():
243 logger.debug(self.dbg + ": " + ev)
245 def wait_event(self, events, timeout):
246 start = os.times()[4]
248 while self.mon.pending():
250 logger.debug(self.dbg + ": " + ev)
255 remaining = start + timeout - now
258 if not self.mon.pending(timeout=remaining):
262 def get_status(self):
263 res = self.request("STATUS")
264 lines = res.splitlines()
267 [name,value] = l.split('=', 1)
271 def get_status_field(self, field):
272 vals = self.get_status()
277 def get_driver_status(self):
278 res = self.request("STATUS-DRIVER")
279 lines = res.splitlines()
282 [name,value] = l.split('=', 1)
286 def get_driver_status_field(self, field):
287 vals = self.get_driver_status()
292 def get_config(self):
293 res = self.request("GET_CONFIG")
294 lines = res.splitlines()
297 [name,value] = l.split('=', 1)
301 def mgmt_rx(self, timeout=5):
302 ev = self.wait_event(["MGMT-RX"], timeout=timeout)
306 frame = binascii.unhexlify(ev.split(' ')[1])
309 hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
311 msg['subtype'] = (hdr[0] >> 4) & 0xf
313 msg['duration'] = hdr[0]
315 msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
317 msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
319 msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
321 msg['seq_ctrl'] = hdr[0]
322 msg['payload'] = frame[24:]
326 def mgmt_tx(self, msg):
327 t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
328 hdr = struct.pack('<HH6B6B6BH', *t)
329 self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']))
331 def get_sta(self, addr, info=None, next=False):
332 cmd = "STA-NEXT " if next else "STA "
334 res = self.request("STA-FIRST")
336 res = self.request(cmd + addr + " " + info)
338 res = self.request(cmd + addr)
339 lines = res.splitlines()
343 if first and '=' not in l:
347 [name,value] = l.split('=', 1)
351 def get_mib(self, param=None):
353 res = self.request("MIB " + param)
355 res = self.request("MIB")
356 lines = res.splitlines()
359 name_val = l.split('=', 1)
360 if len(name_val) > 1:
361 vals[name_val[0]] = name_val[1]
364 def get_pmksa(self, addr):
365 res = self.request("PMKSA")
366 lines = res.splitlines()
371 [index,aa,pmkid,expiration,opportunistic] = l.split(' ')
372 vals['index'] = index
373 vals['pmkid'] = pmkid
374 vals['expiration'] = expiration
375 vals['opportunistic'] = opportunistic
379 def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30):
380 if isinstance(apdev, dict):
381 ifname = apdev['ifname']
383 hostname = apdev['hostname']
385 logger.info("Starting AP " + hostname + "/" + port + " " + ifname)
387 logger.info("Starting AP " + ifname)
392 logger.info("Starting AP " + ifname + " (old add_ap argument type)")
395 hapd_global = HostapdGlobal(apdev)
396 hapd_global.remove(ifname)
397 hapd_global.add(ifname)
398 port = hapd_global.get_ctrl_iface_port(ifname)
399 hapd = Hostapd(ifname, hostname=hostname, port=port)
401 raise Exception("Could not ping hostapd")
403 fields = [ "ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
405 "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
406 "acct_server_addr", "osu_server_uri" ]
409 hapd.set(field, params[field])
410 for f,v in params.items():
413 if isinstance(v, list):
422 ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=timeout)
424 raise Exception("AP startup timed out")
425 if "AP-ENABLED" not in ev:
426 raise Exception("AP startup failed")
429 def add_bss(apdev, ifname, confname, ignore_error=False):
430 phy = utils.get_phy(apdev)
432 hostname = apdev['hostname']
434 logger.info("Starting BSS " + hostname + "/" + port + " phy=" + phy + " ifname=" + ifname)
436 logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
439 hapd_global = HostapdGlobal(apdev)
440 hapd_global.add_bss(phy, confname, ignore_error)
441 port = hapd_global.get_ctrl_iface_port(ifname)
442 hapd = Hostapd(ifname, hostname=hostname, port=port)
444 raise Exception("Could not ping hostapd")
447 def add_iface(apdev, confname):
448 ifname = apdev['ifname']
450 hostname = apdev['hostname']
452 logger.info("Starting interface " + hostname + "/" + port + " " + ifname)
454 logger.info("Starting interface " + ifname)
457 hapd_global = HostapdGlobal(apdev)
458 hapd_global.add_iface(ifname, confname)
459 port = hapd_global.get_ctrl_iface_port(ifname)
460 hapd = Hostapd(ifname, hostname=hostname, port=port)
462 raise Exception("Could not ping hostapd")
465 def remove_bss(apdev, ifname=None):
467 ifname = apdev['ifname']
469 hostname = apdev['hostname']
471 logger.info("Removing BSS " + hostname + "/" + port + " " + ifname)
473 logger.info("Removing BSS " + ifname)
474 hapd_global = HostapdGlobal(apdev)
475 hapd_global.remove(ifname)
477 def terminate(apdev):
479 hostname = apdev['hostname']
481 logger.info("Terminating hostapd " + hostname + "/" + port)
483 logger.info("Terminating hostapd")
484 hapd_global = HostapdGlobal(apdev)
485 hapd_global.terminate()
487 def wpa2_params(ssid=None, passphrase=None):
488 params = { "wpa": "2",
489 "wpa_key_mgmt": "WPA-PSK",
490 "rsn_pairwise": "CCMP" }
492 params["ssid"] = ssid
494 params["wpa_passphrase"] = passphrase
497 def wpa_params(ssid=None, passphrase=None):
498 params = { "wpa": "1",
499 "wpa_key_mgmt": "WPA-PSK",
500 "wpa_pairwise": "TKIP" }
502 params["ssid"] = ssid
504 params["wpa_passphrase"] = passphrase
507 def wpa_mixed_params(ssid=None, passphrase=None):
508 params = { "wpa": "3",
509 "wpa_key_mgmt": "WPA-PSK",
510 "wpa_pairwise": "TKIP",
511 "rsn_pairwise": "CCMP" }
513 params["ssid"] = ssid
515 params["wpa_passphrase"] = passphrase
519 params = { "auth_server_addr": "127.0.0.1",
520 "auth_server_port": "1812",
521 "auth_server_shared_secret": "radius",
522 "nas_identifier": "nas.w1.fi" }
525 def wpa_eap_params(ssid=None):
526 params = radius_params()
528 params["wpa_key_mgmt"] = "WPA-EAP"
529 params["wpa_pairwise"] = "TKIP"
530 params["ieee8021x"] = "1"
532 params["ssid"] = ssid
535 def wpa2_eap_params(ssid=None):
536 params = radius_params()
538 params["wpa_key_mgmt"] = "WPA-EAP"
539 params["rsn_pairwise"] = "CCMP"
540 params["ieee8021x"] = "1"
542 params["ssid"] = ssid
545 def b_only_params(channel="1", ssid=None, country=None):
546 params = { "hw_mode" : "b",
547 "channel" : channel }
549 params["ssid"] = ssid
551 params["country_code"] = country
554 def g_only_params(channel="1", ssid=None, country=None):
555 params = { "hw_mode" : "g",
556 "channel" : channel }
558 params["ssid"] = ssid
560 params["country_code"] = country
563 def a_only_params(channel="36", ssid=None, country=None):
564 params = { "hw_mode" : "a",
565 "channel" : channel }
567 params["ssid"] = ssid
569 params["country_code"] = country
572 def ht20_params(channel="1", ssid=None, country=None):
573 params = { "ieee80211n" : "1",
576 if int(channel) > 14:
577 params["hw_mode"] = "a"
579 params["ssid"] = ssid
581 params["country_code"] = country
584 def ht40_plus_params(channel="1", ssid=None, country=None):
585 params = ht20_params(channel, ssid, country)
586 params['ht_capab'] = "[HT40+]"
589 def ht40_minus_params(channel="1", ssid=None, country=None):
590 params = ht20_params(channel, ssid, country)
591 params['ht_capab'] = "[HT40-]"
594 def cmd_execute(apdev, cmd, shell=False):
595 hapd_global = HostapdGlobal(apdev)
596 return hapd_global.cmd_execute(cmd, shell=shell)