hostapd.uc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. let libubus = require("ubus");
  2. import { open, readfile } from "fs";
  3. import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
  4. let ubus = libubus.connect(null, 60);
  5. hostapd.data.config = {};
  6. hostapd.data.pending_config = {};
  7. hostapd.data.file_fields = {
  8. vlan_file: true,
  9. wpa_psk_file: true,
  10. accept_mac_file: true,
  11. deny_mac_file: true,
  12. eap_user_file: true,
  13. ca_cert: true,
  14. server_cert: true,
  15. server_cert2: true,
  16. private_key: true,
  17. private_key2: true,
  18. dh_file: true,
  19. eap_sim_db: true,
  20. };
  21. function iface_remove(cfg)
  22. {
  23. if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
  24. return;
  25. for (let bss in cfg.bss)
  26. wdev_remove(bss.ifname);
  27. }
  28. function iface_gen_config(phy, config, start_disabled)
  29. {
  30. let str = `data:
  31. ${join("\n", config.radio.data)}
  32. channel=${config.radio.channel}
  33. `;
  34. for (let i = 0; i < length(config.bss); i++) {
  35. let bss = config.bss[i];
  36. let type = i > 0 ? "bss" : "interface";
  37. let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
  38. str += `
  39. ${type}=${bss.ifname}
  40. bssid=${bss.bssid}
  41. ${join("\n", bss.data)}
  42. nas_identifier=${nasid}
  43. `;
  44. if (start_disabled)
  45. str += `
  46. start_disabled=1
  47. `;
  48. }
  49. return str;
  50. }
  51. function iface_freq_info(iface, config, params)
  52. {
  53. let freq = params.frequency;
  54. if (!freq)
  55. return null;
  56. let sec_offset = params.sec_chan_offset;
  57. if (sec_offset != -1 && sec_offset != 1)
  58. sec_offset = 0;
  59. let width = 0;
  60. for (let line in config.radio.data) {
  61. if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
  62. sec_offset = null; // auto-detect
  63. continue;
  64. }
  65. let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth)=(\d+)/);
  66. if (!val)
  67. continue;
  68. val = int(val[2]);
  69. if (val > width)
  70. width = val;
  71. }
  72. if (freq < 4000)
  73. width = 0;
  74. return hostapd.freq_info(freq, sec_offset, width);
  75. }
  76. function iface_add(phy, config, phy_status)
  77. {
  78. let config_inline = iface_gen_config(phy, config, !!phy_status);
  79. let bss = config.bss[0];
  80. let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
  81. if (ret < 0)
  82. return false;
  83. if (!phy_status)
  84. return true;
  85. let iface = hostapd.interfaces[phy];
  86. if (!iface)
  87. return false;
  88. let freq_info = iface_freq_info(iface, config, phy_status);
  89. return iface.start(freq_info) >= 0;
  90. }
  91. function iface_config_macaddr_list(config)
  92. {
  93. let macaddr_list = {};
  94. for (let i = 0; i < length(config.bss); i++) {
  95. let bss = config.bss[i];
  96. if (!bss.default_macaddr)
  97. macaddr_list[bss.bssid] = i;
  98. }
  99. return macaddr_list;
  100. }
  101. function iface_update_supplicant_macaddr(phy, config)
  102. {
  103. let macaddr_list = [];
  104. for (let i = 0; i < length(config.bss); i++)
  105. push(macaddr_list, config.bss[i].bssid);
  106. ubus.defer("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
  107. }
  108. function __iface_pending_next(pending, state, ret, data)
  109. {
  110. let config = pending.config;
  111. let phydev = pending.phydev;
  112. let phy = pending.phy;
  113. let bss = config.bss[0];
  114. if (pending.defer)
  115. pending.defer.abort();
  116. delete pending.defer;
  117. switch (state) {
  118. case "init":
  119. let macaddr_list = [];
  120. for (let i = 0; i < length(config.bss); i++)
  121. push(macaddr_list, config.bss[i].bssid);
  122. pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
  123. return "create_bss";
  124. case "create_bss":
  125. let err = wdev_create(phy, bss.ifname, { mode: "ap" });
  126. if (err) {
  127. hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
  128. return null;
  129. }
  130. pending.call("wpa_supplicant", "phy_status", { phy: phy });
  131. return "check_phy";
  132. case "check_phy":
  133. let phy_status = data;
  134. if (phy_status && phy_status.state == "COMPLETED") {
  135. if (iface_add(phy, config, phy_status))
  136. return "done";
  137. hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
  138. }
  139. pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
  140. return "wpas_stopped";
  141. case "wpas_stopped":
  142. if (!iface_add(phy, config))
  143. hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
  144. pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
  145. return null;
  146. case "done":
  147. default:
  148. delete hostapd.data.pending_config[phy];
  149. break;
  150. }
  151. }
  152. function iface_pending_next(ret, data)
  153. {
  154. let pending = true;
  155. let cfg = this;
  156. while (pending) {
  157. this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
  158. if (!this.next_state) {
  159. __iface_pending_next(cfg, "done");
  160. return;
  161. }
  162. pending = !this.defer;
  163. }
  164. }
  165. function iface_pending_abort()
  166. {
  167. this.next_state = "done";
  168. this.next();
  169. }
  170. function iface_pending_ubus_call(obj, method, arg)
  171. {
  172. let ubus = hostapd.data.ubus;
  173. let pending = this;
  174. this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) });
  175. }
  176. const iface_pending_proto = {
  177. next: iface_pending_next,
  178. call: iface_pending_ubus_call,
  179. abort: iface_pending_abort,
  180. };
  181. function iface_pending_init(phydev, config)
  182. {
  183. let phy = phydev.name;
  184. let pending = proto({
  185. next_state: "init",
  186. phydev: phydev,
  187. phy: phy,
  188. config: config,
  189. next: iface_pending_next,
  190. }, iface_pending_proto);
  191. hostapd.data.pending_config[phy] = pending;
  192. pending.next();
  193. }
  194. function iface_restart(phydev, config, old_config)
  195. {
  196. let phy = phydev.name;
  197. let pending = hostapd.data.pending_config[phy];
  198. if (pending)
  199. pending.abort();
  200. hostapd.remove_iface(phy);
  201. iface_remove(old_config);
  202. iface_remove(config);
  203. if (!config.bss || !config.bss[0]) {
  204. hostapd.printf(`No bss for phy ${phy}`);
  205. return;
  206. }
  207. phydev.macaddr_init(iface_config_macaddr_list(config));
  208. for (let i = 0; i < length(config.bss); i++) {
  209. let bss = config.bss[i];
  210. if (bss.default_macaddr)
  211. bss.bssid = phydev.macaddr_next();
  212. }
  213. iface_pending_init(phydev, config);
  214. }
  215. function array_to_obj(arr, key, start)
  216. {
  217. let obj = {};
  218. start ??= 0;
  219. for (let i = start; i < length(arr); i++) {
  220. let cur = arr[i];
  221. obj[cur[key]] = cur;
  222. }
  223. return obj;
  224. }
  225. function find_array_idx(arr, key, val)
  226. {
  227. for (let i = 0; i < length(arr); i++)
  228. if (arr[i][key] == val)
  229. return i;
  230. return -1;
  231. }
  232. function bss_reload_psk(bss, config, old_config)
  233. {
  234. if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
  235. return;
  236. old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
  237. if (!is_equal(old_config, config))
  238. return;
  239. let ret = bss.ctrl("RELOAD_WPA_PSK");
  240. ret ??= "failed";
  241. hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
  242. }
  243. function remove_file_fields(config)
  244. {
  245. return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
  246. }
  247. function bss_remove_file_fields(config)
  248. {
  249. let new_cfg = {};
  250. for (let key in config)
  251. new_cfg[key] = config[key];
  252. new_cfg.data = remove_file_fields(new_cfg.data);
  253. new_cfg.hash = {};
  254. for (let key in config.hash)
  255. new_cfg.hash[key] = config.hash[key];
  256. delete new_cfg.hash.wpa_psk_file;
  257. delete new_cfg.hash.vlan_file;
  258. return new_cfg;
  259. }
  260. function bss_config_hash(config)
  261. {
  262. return hostapd.sha1(remove_file_fields(config) + "");
  263. }
  264. function bss_find_existing(config, prev_config, prev_hash)
  265. {
  266. let hash = bss_config_hash(config.data);
  267. for (let i = 0; i < length(prev_config.bss); i++) {
  268. if (!prev_hash[i] || hash != prev_hash[i])
  269. continue;
  270. prev_hash[i] = null;
  271. return i;
  272. }
  273. return -1;
  274. }
  275. function get_config_bss(config, idx)
  276. {
  277. if (!config.bss[idx]) {
  278. hostapd.printf(`Invalid bss index ${idx}`);
  279. return null;
  280. }
  281. let ifname = config.bss[idx].ifname;
  282. if (!ifname)
  283. hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
  284. return hostapd.bss[ifname];
  285. }
  286. function iface_reload_config(phydev, config, old_config)
  287. {
  288. let phy = phydev.name;
  289. if (!old_config || !is_equal(old_config.radio, config.radio))
  290. return false;
  291. if (is_equal(old_config.bss, config.bss))
  292. return true;
  293. if (hostapd.data.pending_config[phy])
  294. return false;
  295. if (!old_config.bss || !old_config.bss[0])
  296. return false;
  297. let iface = hostapd.interfaces[phy];
  298. let iface_name = old_config.bss[0].ifname;
  299. if (!iface) {
  300. hostapd.printf(`Could not find previous interface ${iface_name}`);
  301. return false;
  302. }
  303. let first_bss = hostapd.bss[iface_name];
  304. if (!first_bss) {
  305. hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
  306. return false;
  307. }
  308. let macaddr_list = iface_config_macaddr_list(config);
  309. let bss_list = [];
  310. let bss_list_cfg = [];
  311. let prev_bss_hash = [];
  312. for (let bss in old_config.bss) {
  313. let hash = bss_config_hash(bss.data);
  314. push(prev_bss_hash, bss_config_hash(bss.data));
  315. }
  316. // Step 1: find (possibly renamed) interfaces with the same config
  317. // and store them in the new order (with gaps)
  318. for (let i = 0; i < length(config.bss); i++) {
  319. let prev;
  320. // For fullmac devices, the first interface needs to be preserved,
  321. // since it's treated as the master
  322. if (!i && phy_is_fullmac(phy)) {
  323. prev = 0;
  324. prev_bss_hash[0] = null;
  325. } else {
  326. prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
  327. }
  328. if (prev < 0)
  329. continue;
  330. let cur_config = config.bss[i];
  331. let prev_config = old_config.bss[prev];
  332. let prev_bss = get_config_bss(old_config, prev);
  333. if (!prev_bss)
  334. return false;
  335. // try to preserve MAC address of this BSS by reassigning another
  336. // BSS if necessary
  337. if (cur_config.default_macaddr &&
  338. !macaddr_list[prev_config.bssid]) {
  339. macaddr_list[prev_config.bssid] = i;
  340. cur_config.bssid = prev_config.bssid;
  341. }
  342. bss_list[i] = prev_bss;
  343. bss_list_cfg[i] = old_config.bss[prev];
  344. }
  345. if (config.mbssid && !bss_list_cfg[0]) {
  346. hostapd.printf("First BSS changed with MBSSID enabled");
  347. return false;
  348. }
  349. // Step 2: if none were found, rename and preserve the first one
  350. if (length(bss_list) == 0) {
  351. // can't change the bssid of the first bss
  352. if (config.bss[0].bssid != old_config.bss[0].bssid) {
  353. if (!config.bss[0].default_macaddr) {
  354. hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
  355. return false;
  356. }
  357. config.bss[0].bssid = old_config.bss[0].bssid;
  358. }
  359. let prev_bss = get_config_bss(old_config, 0);
  360. if (!prev_bss)
  361. return false;
  362. macaddr_list[config.bss[0].bssid] = 0;
  363. bss_list[0] = prev_bss;
  364. bss_list_cfg[0] = old_config.bss[0];
  365. prev_bss_hash[0] = null;
  366. }
  367. // Step 3: delete all unused old interfaces
  368. for (let i = 0; i < length(prev_bss_hash); i++) {
  369. if (!prev_bss_hash[i])
  370. continue;
  371. let prev_bss = get_config_bss(old_config, i);
  372. if (!prev_bss)
  373. return false;
  374. let ifname = old_config.bss[i].ifname;
  375. hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
  376. prev_bss.delete();
  377. wdev_remove(ifname);
  378. }
  379. // Step 4: rename preserved interfaces, use temporary name on duplicates
  380. let rename_list = [];
  381. for (let i = 0; i < length(bss_list); i++) {
  382. if (!bss_list[i])
  383. continue;
  384. let old_ifname = bss_list_cfg[i].ifname;
  385. let new_ifname = config.bss[i].ifname;
  386. if (old_ifname == new_ifname)
  387. continue;
  388. if (hostapd.bss[new_ifname]) {
  389. new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
  390. push(rename_list, i);
  391. }
  392. hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
  393. if (!bss_list[i].rename(new_ifname)) {
  394. hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
  395. return false;
  396. }
  397. bss_list_cfg[i].ifname = new_ifname;
  398. }
  399. // Step 5: rename interfaces with temporary names
  400. for (let i in rename_list) {
  401. let new_ifname = config.bss[i].ifname;
  402. if (!bss_list[i].rename(new_ifname)) {
  403. hostapd.printf(`Failed to rename bss to ${new_ifname}`);
  404. return false;
  405. }
  406. bss_list_cfg[i].ifname = new_ifname;
  407. }
  408. // Step 6: assign BSSID for newly created interfaces
  409. let macaddr_data = {
  410. num_global: config.num_global_macaddr ?? 1,
  411. mbssid: config.mbssid ?? 0,
  412. };
  413. macaddr_list = phydev.macaddr_init(macaddr_list, macaddr_data);
  414. for (let i = 0; i < length(config.bss); i++) {
  415. if (bss_list[i])
  416. continue;
  417. let bsscfg = config.bss[i];
  418. let mac_idx = macaddr_list[bsscfg.bssid];
  419. if (mac_idx < 0)
  420. macaddr_list[bsscfg.bssid] = i;
  421. if (mac_idx == i)
  422. continue;
  423. // statically assigned bssid of the new interface is in conflict
  424. // with the bssid of a reused interface. reassign the reused interface
  425. if (!bsscfg.default_macaddr) {
  426. // can't update bssid of the first BSS, need to restart
  427. if (!mac_idx < 0)
  428. return false;
  429. bsscfg = config.bss[mac_idx];
  430. }
  431. let addr = phydev.macaddr_next(i);
  432. if (!addr) {
  433. hostapd.printf(`Failed to generate mac address for phy ${phy}`);
  434. return false;
  435. }
  436. bsscfg.bssid = addr;
  437. }
  438. let config_inline = iface_gen_config(phy, config);
  439. // Step 7: fill in the gaps with new interfaces
  440. for (let i = 0; i < length(config.bss); i++) {
  441. let ifname = config.bss[i].ifname;
  442. let bss = bss_list[i];
  443. if (bss)
  444. continue;
  445. hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
  446. bss_list[i] = iface.add_bss(config_inline, i);
  447. if (!bss_list[i]) {
  448. hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
  449. return false;
  450. }
  451. }
  452. // Step 8: update interface bss order
  453. if (!iface.set_bss_order(bss_list)) {
  454. hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
  455. return false;
  456. }
  457. // Step 9: update config
  458. for (let i = 0; i < length(config.bss); i++) {
  459. if (!bss_list_cfg[i])
  460. continue;
  461. let ifname = config.bss[i].ifname;
  462. let bss = bss_list[i];
  463. if (is_equal(config.bss[i], bss_list_cfg[i]))
  464. continue;
  465. if (is_equal(bss_remove_file_fields(config.bss[i]),
  466. bss_remove_file_fields(bss_list_cfg[i]))) {
  467. hostapd.printf(`Update config data files for bss ${ifname}`);
  468. if (bss.set_config(config_inline, i, true) < 0) {
  469. hostapd.printf(`Could not update config data files for bss ${ifname}`);
  470. return false;
  471. } else {
  472. bss.ctrl("RELOAD_WPA_PSK");
  473. continue;
  474. }
  475. }
  476. bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
  477. if (is_equal(config.bss[i], bss_list_cfg[i]))
  478. continue;
  479. hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
  480. if (bss.set_config(config_inline, i) < 0) {
  481. hostapd.printf(`Failed to set config for bss ${ifname}`);
  482. return false;
  483. }
  484. }
  485. return true;
  486. }
  487. function iface_set_config(phy, config)
  488. {
  489. let old_config = hostapd.data.config[phy];
  490. hostapd.data.config[phy] = config;
  491. if (!config) {
  492. hostapd.remove_iface(phy);
  493. return iface_remove(old_config);
  494. }
  495. let phydev = phy_open(phy);
  496. if (!phydev) {
  497. hostapd.printf(`Failed to open phy ${phy}`);
  498. return false;
  499. }
  500. try {
  501. let ret = iface_reload_config(phydev, config, old_config);
  502. if (ret) {
  503. iface_update_supplicant_macaddr(phy, config);
  504. hostapd.printf(`Reloaded settings for phy ${phy}`);
  505. return 0;
  506. }
  507. } catch (e) {
  508. hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
  509. }
  510. hostapd.printf(`Restart interface for phy ${phy}`);
  511. let ret = iface_restart(phydev, config, old_config);
  512. return ret;
  513. }
  514. function config_add_bss(config, name)
  515. {
  516. let bss = {
  517. ifname: name,
  518. data: [],
  519. hash: {}
  520. };
  521. push(config.bss, bss);
  522. return bss;
  523. }
  524. function iface_load_config(filename)
  525. {
  526. let f = open(filename, "r");
  527. if (!f)
  528. return null;
  529. let config = {
  530. radio: {
  531. data: []
  532. },
  533. bss: [],
  534. orig_file: filename,
  535. };
  536. let bss;
  537. let line;
  538. while ((line = rtrim(f.read("line"), "\n")) != null) {
  539. let val = split(line, "=", 2);
  540. if (!val[0])
  541. continue;
  542. if (val[0] == "interface") {
  543. bss = config_add_bss(config, val[1]);
  544. break;
  545. }
  546. if (val[0] == "channel") {
  547. config.radio.channel = val[1];
  548. continue;
  549. }
  550. if (val[0] == "#num_global_macaddr" ||
  551. val[0] == "mbssid")
  552. config[val[0]] = int(val[1]);
  553. push(config.radio.data, line);
  554. }
  555. while ((line = rtrim(f.read("line"), "\n")) != null) {
  556. if (line == "#default_macaddr")
  557. bss.default_macaddr = true;
  558. let val = split(line, "=", 2);
  559. if (!val[0])
  560. continue;
  561. if (val[0] == "bssid") {
  562. bss.bssid = lc(val[1]);
  563. continue;
  564. }
  565. if (val[0] == "nas_identifier")
  566. bss.nasid = val[1];
  567. if (val[0] == "bss") {
  568. bss = config_add_bss(config, val[1]);
  569. continue;
  570. }
  571. if (hostapd.data.file_fields[val[0]])
  572. bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
  573. push(bss.data, line);
  574. }
  575. f.close();
  576. return config;
  577. }
  578. function ex_wrap(func) {
  579. return (req) => {
  580. try {
  581. let ret = func(req);
  582. return ret;
  583. } catch(e) {
  584. hostapd.printf(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}`);
  585. }
  586. return libubus.STATUS_UNKNOWN_ERROR;
  587. };
  588. }
  589. let main_obj = {
  590. reload: {
  591. args: {
  592. phy: "",
  593. },
  594. call: ex_wrap(function(req) {
  595. let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
  596. for (let phy_name in phy_list) {
  597. let phy = hostapd.data.config[phy_name];
  598. let config = iface_load_config(phy.orig_file);
  599. iface_set_config(phy_name, config);
  600. }
  601. return 0;
  602. })
  603. },
  604. apsta_state: {
  605. args: {
  606. phy: "",
  607. up: true,
  608. frequency: 0,
  609. sec_chan_offset: 0,
  610. csa: true,
  611. csa_count: 0,
  612. },
  613. call: ex_wrap(function(req) {
  614. if (req.args.up == null || !req.args.phy)
  615. return libubus.STATUS_INVALID_ARGUMENT;
  616. let phy = req.args.phy;
  617. let config = hostapd.data.config[phy];
  618. if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
  619. return 0;
  620. let iface = hostapd.interfaces[phy];
  621. if (!iface)
  622. return 0;
  623. if (!req.args.up) {
  624. iface.stop();
  625. return 0;
  626. }
  627. if (!req.args.frequency)
  628. return libubus.STATUS_INVALID_ARGUMENT;
  629. let freq_info = iface_freq_info(iface, config, req.args);
  630. if (!freq_info)
  631. return libubus.STATUS_UNKNOWN_ERROR;
  632. let ret;
  633. if (req.args.csa) {
  634. freq_info.csa_count = req.args.csa_count ?? 10;
  635. ret = iface.switch_channel(freq_info);
  636. } else {
  637. ret = iface.start(freq_info);
  638. }
  639. if (!ret)
  640. return libubus.STATUS_UNKNOWN_ERROR;
  641. return 0;
  642. })
  643. },
  644. config_get_macaddr_list: {
  645. args: {
  646. phy: ""
  647. },
  648. call: ex_wrap(function(req) {
  649. let phy = req.args.phy;
  650. if (!phy)
  651. return libubus.STATUS_INVALID_ARGUMENT;
  652. let ret = {
  653. macaddr: [],
  654. };
  655. let config = hostapd.data.config[phy];
  656. if (!config)
  657. return ret;
  658. ret.macaddr = map(config.bss, (bss) => bss.bssid);
  659. return ret;
  660. })
  661. },
  662. config_set: {
  663. args: {
  664. phy: "",
  665. config: "",
  666. prev_config: "",
  667. },
  668. call: ex_wrap(function(req) {
  669. let phy = req.args.phy;
  670. let file = req.args.config;
  671. let prev_file = req.args.prev_config;
  672. if (!phy)
  673. return libubus.STATUS_INVALID_ARGUMENT;
  674. if (prev_file && !hostapd.data.config[phy]) {
  675. let config = iface_load_config(prev_file);
  676. if (config)
  677. config.radio.data = [];
  678. hostapd.data.config[phy] = config;
  679. }
  680. let config = iface_load_config(file);
  681. hostapd.printf(`Set new config for phy ${phy}: ${file}`);
  682. iface_set_config(phy, config);
  683. return {
  684. pid: hostapd.getpid()
  685. };
  686. })
  687. },
  688. config_add: {
  689. args: {
  690. iface: "",
  691. config: "",
  692. },
  693. call: ex_wrap(function(req) {
  694. if (!req.args.iface || !req.args.config)
  695. return libubus.STATUS_INVALID_ARGUMENT;
  696. if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
  697. return libubus.STATUS_INVALID_ARGUMENT;
  698. return {
  699. pid: hostapd.getpid()
  700. };
  701. })
  702. },
  703. config_remove: {
  704. args: {
  705. iface: ""
  706. },
  707. call: ex_wrap(function(req) {
  708. if (!req.args.iface)
  709. return libubus.STATUS_INVALID_ARGUMENT;
  710. hostapd.remove_iface(req.args.iface);
  711. return 0;
  712. })
  713. },
  714. };
  715. hostapd.data.ubus = ubus;
  716. hostapd.data.obj = ubus.publish("hostapd", main_obj);
  717. hostapd.udebug_set("hostapd", hostapd.data.ubus);
  718. function bss_event(type, name, data) {
  719. let ubus = hostapd.data.ubus;
  720. data ??= {};
  721. data.name = name;
  722. hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
  723. ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
  724. }
  725. return {
  726. shutdown: function() {
  727. for (let phy in hostapd.data.config)
  728. iface_set_config(phy, null);
  729. hostapd.udebug_set(null);
  730. hostapd.ubus.disconnect();
  731. },
  732. bss_add: function(name, obj) {
  733. bss_event("add", name);
  734. },
  735. bss_reload: function(name, obj, reconf) {
  736. bss_event("reload", name, { reconf: reconf != 0 });
  737. },
  738. bss_remove: function(name, obj) {
  739. bss_event("remove", name);
  740. }
  741. };