wpa_supplicant.uc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. let libubus = require("ubus");
  2. import { open, readfile } from "fs";
  3. import { wdev_create, wdev_set_mesh_params, wdev_remove, is_equal, wdev_set_up, vlist_new, phy_open } from "common";
  4. let ubus = libubus.connect();
  5. wpas.data.config = {};
  6. wpas.data.iface_phy = {};
  7. wpas.data.macaddr_list = {};
  8. function iface_stop(iface)
  9. {
  10. let ifname = iface.config.iface;
  11. if (!iface.running)
  12. return;
  13. delete wpas.data.iface_phy[ifname];
  14. wpas.remove_iface(ifname);
  15. wdev_remove(ifname);
  16. iface.running = false;
  17. }
  18. function iface_start(phydev, iface, macaddr_list)
  19. {
  20. let phy = phydev.name;
  21. if (iface.running)
  22. return;
  23. let ifname = iface.config.iface;
  24. let wdev_config = {};
  25. for (let field in iface.config)
  26. wdev_config[field] = iface.config[field];
  27. if (!wdev_config.macaddr)
  28. wdev_config.macaddr = phydev.macaddr_next();
  29. wpas.data.iface_phy[ifname] = phy;
  30. wdev_remove(ifname);
  31. let ret = wdev_create(phy, ifname, wdev_config);
  32. if (ret)
  33. wpas.printf(`Failed to create device ${ifname}: ${ret}`);
  34. wdev_set_up(ifname, true);
  35. wpas.add_iface(iface.config);
  36. iface.running = true;
  37. }
  38. function iface_cb(new_if, old_if)
  39. {
  40. if (old_if && new_if && is_equal(old_if.config, new_if.config)) {
  41. new_if.running = old_if.running;
  42. return;
  43. }
  44. if (new_if && old_if)
  45. wpas.printf(`Update configuration for interface ${old_if.config.iface}`);
  46. else if (old_if)
  47. wpas.printf(`Remove interface ${old_if.config.iface}`);
  48. if (old_if)
  49. iface_stop(old_if);
  50. }
  51. function prepare_config(config)
  52. {
  53. config.config_data = readfile(config.config);
  54. return { config: config };
  55. }
  56. function set_config(phy_name, config_list)
  57. {
  58. let phy = wpas.data.config[phy_name];
  59. if (!phy) {
  60. phy = vlist_new(iface_cb, false);
  61. wpas.data.config[phy_name] = phy;
  62. }
  63. let values = [];
  64. for (let config in config_list)
  65. push(values, [ config.iface, prepare_config(config) ]);
  66. phy.update(values);
  67. }
  68. function start_pending(phy_name)
  69. {
  70. let phy = wpas.data.config[phy_name];
  71. let ubus = wpas.data.ubus;
  72. if (!phy || !phy.data)
  73. return;
  74. let phydev = phy_open(phy_name);
  75. if (!phydev) {
  76. wpas.printf(`Could not open phy ${phy_name}`);
  77. return;
  78. }
  79. let macaddr_list = wpas.data.macaddr_list[phy_name];
  80. phydev.macaddr_init(macaddr_list);
  81. for (let ifname in phy.data)
  82. iface_start(phydev, phy.data[ifname]);
  83. }
  84. let main_obj = {
  85. phy_set_state: {
  86. args: {
  87. phy: "",
  88. stop: true,
  89. },
  90. call: function(req) {
  91. if (!req.args.phy || req.args.stop == null)
  92. return libubus.STATUS_INVALID_ARGUMENT;
  93. let phy = wpas.data.config[req.args.phy];
  94. if (!phy)
  95. return libubus.STATUS_NOT_FOUND;
  96. try {
  97. if (req.args.stop) {
  98. for (let ifname in phy.data)
  99. iface_stop(phy.data[ifname]);
  100. } else {
  101. start_pending(req.args.phy);
  102. }
  103. } catch (e) {
  104. wpas.printf(`Error chaging state: ${e}\n${e.stacktrace[0].context}`);
  105. return libubus.STATUS_INVALID_ARGUMENT;
  106. }
  107. return 0;
  108. }
  109. },
  110. phy_set_macaddr_list: {
  111. args: {
  112. phy: "",
  113. macaddr: [],
  114. },
  115. call: function(req) {
  116. let phy = req.args.phy;
  117. if (!phy)
  118. return libubus.STATUS_INVALID_ARGUMENT;
  119. wpas.data.macaddr_list[phy] = req.args.macaddr;
  120. return 0;
  121. }
  122. },
  123. phy_status: {
  124. args: {
  125. phy: ""
  126. },
  127. call: function(req) {
  128. if (!req.args.phy)
  129. return libubus.STATUS_INVALID_ARGUMENT;
  130. let phy = wpas.data.config[req.args.phy];
  131. if (!phy)
  132. return libubus.STATUS_NOT_FOUND;
  133. for (let ifname in phy.data) {
  134. try {
  135. let iface = wpas.interfaces[ifname];
  136. if (!iface)
  137. continue;
  138. let status = iface.status();
  139. if (!status)
  140. continue;
  141. if (status.state == "INTERFACE_DISABLED")
  142. continue;
  143. status.ifname = ifname;
  144. return status;
  145. } catch (e) {
  146. continue;
  147. }
  148. }
  149. return libubus.STATUS_NOT_FOUND;
  150. }
  151. },
  152. config_set: {
  153. args: {
  154. phy: "",
  155. config: [],
  156. defer: true,
  157. },
  158. call: function(req) {
  159. if (!req.args.phy)
  160. return libubus.STATUS_INVALID_ARGUMENT;
  161. wpas.printf(`Set new config for phy ${req.args.phy}`);
  162. try {
  163. if (req.args.config)
  164. set_config(req.args.phy, req.args.config);
  165. if (!req.args.defer)
  166. start_pending(req.args.phy);
  167. } catch (e) {
  168. wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
  169. return libubus.STATUS_INVALID_ARGUMENT;
  170. }
  171. return {
  172. pid: wpas.getpid()
  173. };
  174. }
  175. },
  176. config_add: {
  177. args: {
  178. driver: "",
  179. iface: "",
  180. bridge: "",
  181. hostapd_ctrl: "",
  182. ctrl: "",
  183. config: "",
  184. },
  185. call: function(req) {
  186. if (!req.args.iface || !req.args.config)
  187. return libubus.STATUS_INVALID_ARGUMENT;
  188. if (wpas.add_iface(req.args) < 0)
  189. return libubus.STATUS_INVALID_ARGUMENT;
  190. return {
  191. pid: wpas.getpid()
  192. };
  193. }
  194. },
  195. config_remove: {
  196. args: {
  197. iface: ""
  198. },
  199. call: function(req) {
  200. if (!req.args.iface)
  201. return libubus.STATUS_INVALID_ARGUMENT;
  202. wpas.remove_iface(req.args.iface);
  203. return 0;
  204. }
  205. },
  206. };
  207. wpas.data.ubus = ubus;
  208. wpas.data.obj = ubus.publish("wpa_supplicant", main_obj);
  209. wpas.udebug_set("wpa_supplicant", wpas.data.ubus);
  210. function iface_event(type, name, data) {
  211. let ubus = wpas.data.ubus;
  212. data ??= {};
  213. data.name = name;
  214. wpas.data.obj.notify(`iface.${type}`, data, null, null, null, -1);
  215. ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
  216. }
  217. function iface_hostapd_notify(phy, ifname, iface, state)
  218. {
  219. let ubus = wpas.data.ubus;
  220. let status = iface.status();
  221. let msg = { phy: phy };
  222. switch (state) {
  223. case "DISCONNECTED":
  224. case "AUTHENTICATING":
  225. case "SCANNING":
  226. msg.up = false;
  227. break;
  228. case "INTERFACE_DISABLED":
  229. case "INACTIVE":
  230. msg.up = true;
  231. break;
  232. case "COMPLETED":
  233. msg.up = true;
  234. msg.frequency = status.frequency;
  235. msg.sec_chan_offset = status.sec_chan_offset;
  236. break;
  237. default:
  238. return;
  239. }
  240. ubus.call("hostapd", "apsta_state", msg);
  241. }
  242. function iface_channel_switch(phy, ifname, iface, info)
  243. {
  244. let msg = {
  245. phy: phy,
  246. up: true,
  247. csa: true,
  248. csa_count: info.csa_count ? info.csa_count - 1 : 0,
  249. frequency: info.frequency,
  250. sec_chan_offset: info.sec_chan_offset,
  251. };
  252. ubus.call("hostapd", "apsta_state", msg);
  253. }
  254. return {
  255. shutdown: function() {
  256. for (let phy in wpas.data.config)
  257. set_config(phy, []);
  258. wpas.ubus.disconnect();
  259. },
  260. iface_add: function(name, obj) {
  261. iface_event("add", name);
  262. },
  263. iface_remove: function(name, obj) {
  264. iface_event("remove", name);
  265. },
  266. state: function(ifname, iface, state) {
  267. let phy = wpas.data.iface_phy[ifname];
  268. if (!phy) {
  269. wpas.printf(`no PHY for ifname ${ifname}`);
  270. return;
  271. }
  272. iface_hostapd_notify(phy, ifname, iface, state);
  273. if (state != "COMPLETED")
  274. return;
  275. let phy_data = wpas.data.config[phy];
  276. if (!phy_data)
  277. return;
  278. let iface_data = phy_data.data[ifname];
  279. if (!iface_data)
  280. return;
  281. let wdev_config = iface_data.config;
  282. if (!wdev_config || wdev_config.mode != "mesh")
  283. return;
  284. wdev_set_mesh_params(ifname, wdev_config);
  285. },
  286. event: function(ifname, iface, ev, info) {
  287. let phy = wpas.data.iface_phy[ifname];
  288. if (!phy) {
  289. wpas.printf(`no PHY for ifname ${ifname}`);
  290. return;
  291. }
  292. if (ev == "CH_SWITCH_STARTED")
  293. iface_channel_switch(phy, ifname, iface, info);
  294. }
  295. };