hostapd.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #!/usr/bin/python
  2. #
  3. # Python class for controlling hostapd
  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. import os
  9. import time
  10. import logging
  11. import binascii
  12. import struct
  13. import wpaspy
  14. logger = logging.getLogger()
  15. hapd_ctrl = '/var/run/hostapd'
  16. hapd_global = '/var/run/hostapd-global'
  17. def mac2tuple(mac):
  18. return struct.unpack('6B', binascii.unhexlify(mac.replace(':','')))
  19. class HostapdGlobal:
  20. def __init__(self):
  21. self.ctrl = wpaspy.Ctrl(hapd_global)
  22. def add(self, ifname):
  23. res = self.ctrl.request("ADD " + ifname + " " + hapd_ctrl)
  24. if not "OK" in res:
  25. raise Exception("Could not add hostapd interface " + ifname)
  26. def add_iface(self, ifname, confname):
  27. res = self.ctrl.request("ADD " + ifname + " config=" + confname)
  28. if not "OK" in res:
  29. raise Exception("Could not add hostapd interface")
  30. def add_bss(self, phy, confname, ignore_error=False):
  31. res = self.ctrl.request("ADD bss_config=" + phy + ":" + confname)
  32. if not "OK" in res:
  33. if not ignore_error:
  34. raise Exception("Could not add hostapd BSS")
  35. def remove(self, ifname):
  36. self.ctrl.request("REMOVE " + ifname)
  37. def relog(self):
  38. self.ctrl.request("RELOG")
  39. class Hostapd:
  40. def __init__(self, ifname):
  41. self.ifname = ifname
  42. self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
  43. self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
  44. self.mon.attach()
  45. def request(self, cmd):
  46. logger.debug(self.ifname + ": CTRL: " + cmd)
  47. return self.ctrl.request(cmd)
  48. def ping(self):
  49. return "PONG" in self.request("PING")
  50. def set(self, field, value):
  51. logger.debug(self.ifname + ": SET " + field + "=" + value)
  52. if not "OK" in self.request("SET " + field + " " + value):
  53. raise Exception("Failed to set hostapd parameter " + field)
  54. def set_defaults(self):
  55. self.set("driver", "nl80211")
  56. self.set("hw_mode", "g")
  57. self.set("channel", "1")
  58. self.set("ieee80211n", "1")
  59. self.set("logger_stdout", "-1")
  60. self.set("logger_stdout_level", "0")
  61. def set_open(self, ssid):
  62. self.set_defaults()
  63. self.set("ssid", ssid)
  64. def set_wpa2_psk(self, ssid, passphrase):
  65. self.set_defaults()
  66. self.set("ssid", ssid)
  67. self.set("wpa_passphrase", passphrase)
  68. self.set("wpa", "2")
  69. self.set("wpa_key_mgmt", "WPA-PSK")
  70. self.set("rsn_pairwise", "CCMP")
  71. def set_wpa_psk(self, ssid, passphrase):
  72. self.set_defaults()
  73. self.set("ssid", ssid)
  74. self.set("wpa_passphrase", passphrase)
  75. self.set("wpa", "1")
  76. self.set("wpa_key_mgmt", "WPA-PSK")
  77. self.set("wpa_pairwise", "TKIP")
  78. def set_wpa_psk_mixed(self, ssid, passphrase):
  79. self.set_defaults()
  80. self.set("ssid", ssid)
  81. self.set("wpa_passphrase", passphrase)
  82. self.set("wpa", "3")
  83. self.set("wpa_key_mgmt", "WPA-PSK")
  84. self.set("wpa_pairwise", "TKIP")
  85. self.set("rsn_pairwise", "CCMP")
  86. def set_wep(self, ssid, key):
  87. self.set_defaults()
  88. self.set("ssid", ssid)
  89. self.set("wep_key0", key)
  90. def enable(self):
  91. if not "OK" in self.ctrl.request("ENABLE"):
  92. raise Exception("Failed to enable hostapd interface " + self.ifname)
  93. def disable(self):
  94. if not "OK" in self.ctrl.request("ENABLE"):
  95. raise Exception("Failed to disable hostapd interface " + self.ifname)
  96. def dump_monitor(self):
  97. while self.mon.pending():
  98. ev = self.mon.recv()
  99. logger.debug(self.ifname + ": " + ev)
  100. def wait_event(self, events, timeout):
  101. count = 0
  102. while count < timeout * 10:
  103. count = count + 1
  104. time.sleep(0.1)
  105. while self.mon.pending():
  106. ev = self.mon.recv()
  107. logger.debug(self.ifname + ": " + ev)
  108. for event in events:
  109. if event in ev:
  110. return ev
  111. return None
  112. def get_status(self):
  113. res = self.request("STATUS")
  114. lines = res.splitlines()
  115. vals = dict()
  116. for l in lines:
  117. [name,value] = l.split('=', 1)
  118. vals[name] = value
  119. return vals
  120. def get_status_field(self, field):
  121. vals = self.get_status()
  122. if field in vals:
  123. return vals[field]
  124. return None
  125. def mgmt_rx(self, timeout=5):
  126. ev = self.wait_event(["MGMT-RX"], timeout=timeout)
  127. if ev is None:
  128. return None
  129. msg = {}
  130. frame = binascii.unhexlify(ev.split(' ')[1])
  131. msg['frame'] = frame
  132. hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
  133. msg['fc'] = hdr[0]
  134. msg['subtype'] = (hdr[0] >> 4) & 0xf
  135. hdr = hdr[1:]
  136. msg['duration'] = hdr[0]
  137. hdr = hdr[1:]
  138. msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
  139. hdr = hdr[6:]
  140. msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
  141. hdr = hdr[6:]
  142. msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
  143. hdr = hdr[6:]
  144. msg['seq_ctrl'] = hdr[0]
  145. msg['payload'] = frame[24:]
  146. return msg
  147. def mgmt_tx(self, msg):
  148. t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
  149. hdr = struct.pack('<HH6B6B6BH', *t)
  150. self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']))
  151. def get_sta(self, addr, info=None):
  152. if info:
  153. res = self.request("STA " + addr + " " + info)
  154. else:
  155. res = self.request("STA " + addr)
  156. lines = res.splitlines()
  157. vals = dict()
  158. first = True
  159. for l in lines:
  160. if first:
  161. vals['addr'] = l
  162. first = False
  163. else:
  164. [name,value] = l.split('=', 1)
  165. vals[name] = value
  166. return vals
  167. def get_mib(self):
  168. res = self.request("MIB")
  169. lines = res.splitlines()
  170. vals = dict()
  171. for l in lines:
  172. [name,value] = l.split('=', 1)
  173. vals[name] = value
  174. return vals
  175. def add_ap(ifname, params, wait_enabled=True):
  176. logger.info("Starting AP " + ifname)
  177. hapd_global = HostapdGlobal()
  178. hapd_global.remove(ifname)
  179. hapd_global.add(ifname)
  180. hapd = Hostapd(ifname)
  181. if not hapd.ping():
  182. raise Exception("Could not ping hostapd")
  183. hapd.set_defaults()
  184. fields = [ "ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
  185. "wpa",
  186. "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
  187. "acct_server_addr" ]
  188. for field in fields:
  189. if field in params:
  190. hapd.set(field, params[field])
  191. for f,v in params.items():
  192. if f in fields:
  193. continue
  194. if isinstance(v, list):
  195. for val in v:
  196. hapd.set(f, val)
  197. else:
  198. hapd.set(f, v)
  199. hapd.enable()
  200. if wait_enabled:
  201. ev = hapd.wait_event(["AP-ENABLED"], timeout=30)
  202. if ev is None:
  203. raise Exception("AP startup timed out")
  204. return hapd
  205. def add_bss(phy, ifname, confname, ignore_error=False):
  206. logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
  207. hapd_global = HostapdGlobal()
  208. hapd_global.add_bss(phy, confname, ignore_error)
  209. hapd = Hostapd(ifname)
  210. if not hapd.ping():
  211. raise Exception("Could not ping hostapd")
  212. def add_iface(ifname, confname):
  213. logger.info("Starting interface " + ifname)
  214. hapd_global = HostapdGlobal()
  215. hapd_global.add_iface(ifname, confname)
  216. hapd = Hostapd(ifname)
  217. if not hapd.ping():
  218. raise Exception("Could not ping hostapd")
  219. def remove_bss(ifname):
  220. logger.info("Removing BSS " + ifname)
  221. hapd_global = HostapdGlobal()
  222. hapd_global.remove(ifname)
  223. def wpa2_params(ssid=None, passphrase=None):
  224. params = { "wpa": "2",
  225. "wpa_key_mgmt": "WPA-PSK",
  226. "rsn_pairwise": "CCMP" }
  227. if ssid:
  228. params["ssid"] = ssid
  229. if passphrase:
  230. params["wpa_passphrase"] = passphrase
  231. return params
  232. def wpa_params(ssid=None, passphrase=None):
  233. params = { "wpa": "1",
  234. "wpa_key_mgmt": "WPA-PSK",
  235. "wpa_pairwise": "TKIP" }
  236. if ssid:
  237. params["ssid"] = ssid
  238. if passphrase:
  239. params["wpa_passphrase"] = passphrase
  240. return params
  241. def wpa_mixed_params(ssid=None, passphrase=None):
  242. params = { "wpa": "3",
  243. "wpa_key_mgmt": "WPA-PSK",
  244. "wpa_pairwise": "TKIP",
  245. "rsn_pairwise": "CCMP" }
  246. if ssid:
  247. params["ssid"] = ssid
  248. if passphrase:
  249. params["wpa_passphrase"] = passphrase
  250. return params
  251. def radius_params():
  252. params = { "auth_server_addr": "127.0.0.1",
  253. "auth_server_port": "1812",
  254. "auth_server_shared_secret": "radius",
  255. "nas_identifier": "nas.w1.fi" }
  256. return params
  257. def wpa_eap_params(ssid=None):
  258. params = radius_params()
  259. params["wpa"] = "1"
  260. params["wpa_key_mgmt"] = "WPA-EAP"
  261. params["wpa_pairwise"] = "TKIP"
  262. params["ieee8021x"] = "1"
  263. if ssid:
  264. params["ssid"] = ssid
  265. return params
  266. def wpa2_eap_params(ssid=None):
  267. params = radius_params()
  268. params["wpa"] = "2"
  269. params["wpa_key_mgmt"] = "WPA-EAP"
  270. params["rsn_pairwise"] = "CCMP"
  271. params["ieee8021x"] = "1"
  272. if ssid:
  273. params["ssid"] = ssid
  274. return params