2
0

hostapd.uc 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614
  1. let libubus = require("ubus");
  2. import { open, readfile, access } from "fs";
  3. import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask, wdev_set_up } from "common";
  4. let ubus = libubus.connect(null, 60);
  5. function ex_handler(e)
  6. {
  7. e = split(`${e}\n${e.stacktrace[0].context}`, '\n');
  8. for (let line in e)
  9. hostapd.printf(line);
  10. return libubus.STATUS_UNKNOWN_ERROR;
  11. }
  12. libubus.guard(ex_handler);
  13. hostapd.data.config = {};
  14. hostapd.data.pending_config = {};
  15. hostapd.data.file_fields = {
  16. vlan_file: true,
  17. wpa_psk_file: true,
  18. sae_password_file: true,
  19. rxkh_file: true,
  20. accept_mac_file: true,
  21. deny_mac_file: true,
  22. eap_user_file: true,
  23. ca_cert: true,
  24. server_cert: true,
  25. server_cert2: true,
  26. private_key: true,
  27. private_key2: true,
  28. dh_file: true,
  29. eap_sim_db: true,
  30. };
  31. hostapd.data.iface_fields = {
  32. ft_iface: true,
  33. upnp_iface: true,
  34. snoop_iface: true,
  35. bridge: true,
  36. iapp_interface: true,
  37. };
  38. hostapd.data.bss_info_fields = {
  39. // radio
  40. hw_mode: true,
  41. channel: true,
  42. ieee80211ac: true,
  43. ieee80211ax: true,
  44. // bss
  45. bssid: true,
  46. ssid: true,
  47. wpa: true,
  48. wpa_key_mgmt: true,
  49. wpa_pairwise: true,
  50. auth_algs: true,
  51. ieee80211w: true,
  52. owe_transition_ifname: true,
  53. };
  54. hostapd.data.mld = {};
  55. hostapd.data.dpp_hooks = {};
  56. function iface_remove(cfg)
  57. {
  58. if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
  59. return;
  60. for (let bss in cfg.bss)
  61. if (!bss.mld_ap)
  62. wdev_remove(bss.ifname);
  63. }
  64. function iface_gen_config(config, start_disabled)
  65. {
  66. let str = `data:
  67. ${join("\n", config.radio.data)}
  68. channel=${config.radio.channel}
  69. `;
  70. for (let i = 0; i < length(config.bss); i++) {
  71. let bss = config.bss[i];
  72. let type = i > 0 ? "bss" : "interface";
  73. let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
  74. let bssid = bss.bssid;
  75. if (bss.mld_ap)
  76. bssid += "\nmld_addr=" + bss.mld_bssid;
  77. str += `
  78. ${type}=${bss.ifname}
  79. bssid=${bssid}
  80. ${join("\n", bss.data)}
  81. nas_identifier=${nasid}
  82. `;
  83. if (start_disabled)
  84. str += `
  85. start_disabled=1
  86. `;
  87. }
  88. return str;
  89. }
  90. function iface_freq_info(iface, config, params)
  91. {
  92. let freq = params.frequency;
  93. if (!freq)
  94. return null;
  95. let sec_offset = params.sec_chan_offset;
  96. if (sec_offset != -1 && sec_offset != 1)
  97. sec_offset = 0;
  98. let width = 0;
  99. for (let line in config.radio.data) {
  100. if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
  101. sec_offset = null; // auto-detect
  102. continue;
  103. }
  104. let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth)=(\d+)/);
  105. if (!val)
  106. continue;
  107. val = int(val[2]);
  108. if (val > width)
  109. width = val;
  110. }
  111. if (freq < 4000)
  112. width = 0;
  113. return hostapd.freq_info(freq, sec_offset, width);
  114. }
  115. function iface_add(phy, config, phy_status)
  116. {
  117. let config_inline = iface_gen_config(config, !!phy_status);
  118. let bss = config.bss[0];
  119. let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
  120. if (ret < 0)
  121. return false;
  122. if (!phy_status)
  123. return true;
  124. let iface = hostapd.interfaces[phy];
  125. if (!iface)
  126. return false;
  127. let freq_info = iface_freq_info(iface, config, phy_status);
  128. return iface.start(freq_info) >= 0;
  129. }
  130. function iface_config_macaddr_list(config)
  131. {
  132. let macaddr_list = {};
  133. for (let name, mld in hostapd.data.mld)
  134. if (mld.macaddr)
  135. macaddr_list[mld.macaddr] = -1;
  136. for (let i = 0; i < length(config.bss); i++) {
  137. let bss = config.bss[i];
  138. if (!bss.default_macaddr)
  139. macaddr_list[bss.bssid] = i;
  140. }
  141. return macaddr_list;
  142. }
  143. function iface_update_supplicant_macaddr(phydev, config)
  144. {
  145. let macaddr_list = [];
  146. for (let name, mld in hostapd.data.mld)
  147. if (mld.macaddr)
  148. push(macaddr_list, mld.macaddr);
  149. for (let bss in config.bss)
  150. push(macaddr_list, bss.bssid);
  151. ubus.defer("wpa_supplicant", "phy_set_macaddr_list", {
  152. phy: phydev.phy,
  153. radio: phydev.radio ?? -1,
  154. macaddr: macaddr_list
  155. });
  156. }
  157. function __iface_pending_next(pending, state, ret, data)
  158. {
  159. let config = pending.config;
  160. let phydev = pending.phydev;
  161. let phy = pending.phy;
  162. let bss = config.bss[0];
  163. if (pending.defer)
  164. pending.defer.abort();
  165. delete pending.defer;
  166. switch (state) {
  167. case "init":
  168. iface_update_supplicant_macaddr(phydev, config);
  169. return "create_bss";
  170. case "create_bss":
  171. if (!bss.mld_ap) {
  172. let err = phydev.wdev_add(bss.ifname, {
  173. mode: "ap",
  174. radio: phydev.radio,
  175. });
  176. if (err) {
  177. hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
  178. return null;
  179. }
  180. }
  181. pending.call("wpa_supplicant", "phy_status", {
  182. phy: phydev.phy,
  183. radio: phydev.radio ?? -1,
  184. });
  185. return "check_phy";
  186. case "check_phy":
  187. let phy_status = data;
  188. if (phy_status && phy_status.state == "COMPLETED") {
  189. if (iface_add(phy, config, phy_status))
  190. return "done";
  191. hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
  192. }
  193. pending.call("wpa_supplicant", "phy_set_state", {
  194. phy: phydev.phy,
  195. radio: phydev.radio ?? -1,
  196. stop: true
  197. });
  198. return "wpas_stopped";
  199. case "wpas_stopped":
  200. if (!iface_add(phy, config))
  201. hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
  202. pending.call("wpa_supplicant", "phy_set_state", {
  203. phy: phydev.phy,
  204. radio: phydev.radio ?? -1,
  205. stop: false
  206. });
  207. return null;
  208. case "done":
  209. default:
  210. delete hostapd.data.pending_config[phy];
  211. break;
  212. }
  213. }
  214. function iface_pending_next(ret, data)
  215. {
  216. let pending = true;
  217. let cfg = this;
  218. while (pending) {
  219. try {
  220. this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
  221. if (!this.next_state) {
  222. __iface_pending_next(cfg, "done");
  223. return;
  224. }
  225. } catch(e) {
  226. hostapd.printf(`Exception: ${e}\n${e.stacktrace[0].context}`);
  227. return;
  228. }
  229. pending = !this.defer;
  230. }
  231. }
  232. function iface_pending_abort()
  233. {
  234. this.next_state = "done";
  235. this.next();
  236. }
  237. function iface_pending_ubus_call(obj, method, arg)
  238. {
  239. let ubus = hostapd.data.ubus;
  240. let pending = this;
  241. this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) });
  242. }
  243. const iface_pending_proto = {
  244. next: iface_pending_next,
  245. call: iface_pending_ubus_call,
  246. abort: iface_pending_abort,
  247. };
  248. function iface_pending_init(phydev, config)
  249. {
  250. let phy = phydev.name;
  251. let pending = proto({
  252. next_state: "init",
  253. phydev: phydev,
  254. phy: phy,
  255. config: config,
  256. next: iface_pending_next,
  257. }, iface_pending_proto);
  258. hostapd.data.pending_config[phy] = pending;
  259. pending.next();
  260. }
  261. function iface_macaddr_init(phydev, config, macaddr_list)
  262. {
  263. let macaddr_data = {
  264. num_global: config.num_global_macaddr ?? 1,
  265. macaddr_base: config.macaddr_base,
  266. mbssid: config.mbssid ?? 0,
  267. };
  268. return phydev.macaddr_init(macaddr_list, macaddr_data);
  269. }
  270. function iface_restart(phydev, config, old_config)
  271. {
  272. let phy = phydev.name;
  273. let pending = hostapd.data.pending_config[phy];
  274. if (pending)
  275. pending.abort();
  276. hostapd.remove_iface(phy);
  277. iface_remove(old_config);
  278. iface_remove(config);
  279. if (!config.bss || !config.bss[0]) {
  280. hostapd.printf(`No bss for phy ${phy}`);
  281. return;
  282. }
  283. iface_macaddr_init(phydev, config, iface_config_macaddr_list(config));
  284. for (let i = 0; i < length(config.bss); i++) {
  285. let bss = config.bss[i];
  286. if (bss.default_macaddr)
  287. bss.bssid = phydev.macaddr_next();
  288. }
  289. iface_pending_init(phydev, config);
  290. }
  291. function array_to_obj(arr, key, start)
  292. {
  293. let obj = {};
  294. start ??= 0;
  295. for (let i = start; i < length(arr); i++) {
  296. let cur = arr[i];
  297. obj[cur[key]] = cur;
  298. }
  299. return obj;
  300. }
  301. function find_array_idx(arr, key, val)
  302. {
  303. for (let i = 0; i < length(arr); i++)
  304. if (arr[i][key] == val)
  305. return i;
  306. return -1;
  307. }
  308. function bss_reload_psk(bss, config, old_config)
  309. {
  310. if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
  311. return;
  312. old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
  313. if (!is_equal(old_config, config))
  314. return;
  315. let ret = bss.ctrl("RELOAD_WPA_PSK");
  316. ret ??= "failed";
  317. hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
  318. }
  319. function normalize_rxkhs(txt)
  320. {
  321. const pat = {
  322. sep: "\x20",
  323. mac: "([[:xdigit:]]{2}:?){5}[[:xdigit:]]{2}",
  324. r0kh_id: "[\x21-\x7e]{1,48}",
  325. r1kh_id: "([[:xdigit:]]{2}:?){5}[[:xdigit:]]{2}",
  326. key: "[[:xdigit:]]{32,}",
  327. r0kh: function() {
  328. return "r0kh=" + this.mac + this.sep + this.r0kh_id;
  329. },
  330. r1kh: function() {
  331. return "r1kh=" + this.mac + this.sep + this.r1kh_id;
  332. },
  333. rxkh: function() {
  334. return "(" + this.r0kh() + "|" + this.r1kh() + ")" + this.sep + this.key;
  335. },
  336. };
  337. let rxkhs = filter(
  338. split(txt, "\n"), (line) => match(line, regexp("^" + pat.rxkh() + "$"))
  339. ) ?? [];
  340. rxkhs = map(rxkhs, function(k) {
  341. k = split(k, " ", 3);
  342. k[0] = lc(k[0]);
  343. if(match(k[0], /^r1kh/)) {
  344. k[1] = lc(k[1]);
  345. }
  346. if(!k[2] = hostapd.rkh_derive_key(k[2])) {
  347. return;
  348. }
  349. return join(" ", k);
  350. });
  351. return join("\n", sort(filter(rxkhs, length)));
  352. }
  353. function bss_reload_rxkhs(bss, config, old_config)
  354. {
  355. let bss_rxkhs = join("\n", sort(split(bss.ctrl("GET_RXKHS"), "\n")));
  356. let bss_rxkhs_hash = hostapd.sha1(bss_rxkhs);
  357. if (is_equal(config.hash.rxkh_file, bss_rxkhs_hash)) {
  358. if (is_equal(old_config.hash.rxkh_file, config.hash.rxkh_file))
  359. return;
  360. }
  361. old_config.hash.rxkh_file = config.hash.rxkh_file;
  362. if (!is_equal(old_config, config))
  363. return;
  364. let ret = bss.ctrl("RELOAD_RXKHS");
  365. ret ??= "failed";
  366. hostapd.printf(`Reload RxKH file for bss ${config.ifname}: ${ret}`);
  367. }
  368. function remove_file_fields(config)
  369. {
  370. return filter(config, (line) =>
  371. !match(line, /^\s*$/) &&
  372. !match(line, /^\s*#/) &&
  373. !hostapd.data.file_fields[split(line, "=")[0]]
  374. );
  375. }
  376. function bss_remove_file_fields(config)
  377. {
  378. let new_cfg = {};
  379. for (let key in config)
  380. new_cfg[key] = config[key];
  381. new_cfg.data = remove_file_fields(new_cfg.data);
  382. new_cfg.hash = {};
  383. for (let key in config.hash)
  384. new_cfg.hash[key] = config.hash[key];
  385. delete new_cfg.hash.wpa_psk_file;
  386. delete new_cfg.hash.sae_password_file;
  387. delete new_cfg.hash.vlan_file;
  388. return new_cfg;
  389. }
  390. function bss_ifindex_list(config)
  391. {
  392. config = filter(config, (line) => !!hostapd.data.iface_fields[split(line, "=")[0]]);
  393. return join(",", map(config, (line) => {
  394. try {
  395. let file = "/sys/class/net/" + split(line, "=")[1] + "/ifindex";
  396. let val = trim(readfile(file));
  397. return val;
  398. } catch (e) {
  399. return "";
  400. }
  401. }));
  402. }
  403. function bss_config_hash(config)
  404. {
  405. return hostapd.sha1(remove_file_fields(config) + bss_ifindex_list(config));
  406. }
  407. function bss_find_existing(config, prev_config, prev_hash)
  408. {
  409. let hash = bss_config_hash(config.data);
  410. for (let i = 0; i < length(prev_config.bss); i++) {
  411. if (!prev_hash[i] || hash != prev_hash[i])
  412. continue;
  413. prev_hash[i] = null;
  414. return i;
  415. }
  416. return -1;
  417. }
  418. function get_config_bss(name, config, idx)
  419. {
  420. if (!config.bss[idx]) {
  421. hostapd.printf(`Invalid bss index ${idx}`);
  422. return;
  423. }
  424. let ifname = config.bss[idx].ifname;
  425. if (!ifname) {
  426. hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
  427. return;
  428. }
  429. let if_bss = hostapd.bss[name];
  430. if (!if_bss) {
  431. hostapd.printf(`Could not find interface ${name} bss list`);
  432. return;
  433. }
  434. return if_bss[ifname];
  435. }
  436. function iface_reload_config(name, phydev, config, old_config)
  437. {
  438. let phy = phydev.name;
  439. if (!old_config || !is_equal(old_config.radio, config.radio))
  440. return false;
  441. if (is_equal(old_config.bss, config.bss))
  442. return true;
  443. if (hostapd.data.pending_config[name])
  444. return false;
  445. if (!old_config.bss || !old_config.bss[0])
  446. return false;
  447. let iface = hostapd.interfaces[name];
  448. let iface_name = old_config.bss[0].ifname;
  449. if (!iface) {
  450. hostapd.printf(`Could not find previous interface ${iface_name}`);
  451. return false;
  452. }
  453. if (iface.state() != "ENABLED") {
  454. hostapd.printf(`Interface ${iface_name} is not fully configured`);
  455. return false;
  456. }
  457. let first_bss = get_config_bss(name, old_config, 0);
  458. if (!first_bss) {
  459. hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
  460. return false;
  461. }
  462. let macaddr_list = iface_config_macaddr_list(config);
  463. let bss_list = [];
  464. let bss_list_cfg = [];
  465. let prev_bss_hash = [];
  466. for (let bss in old_config.bss) {
  467. let hash = bss_config_hash(bss.data);
  468. push(prev_bss_hash, bss_config_hash(bss.data));
  469. }
  470. // Step 1: find (possibly renamed) interfaces with the same config
  471. // and store them in the new order (with gaps)
  472. for (let i = 0; i < length(config.bss); i++) {
  473. let prev;
  474. // For fullmac devices, the first interface needs to be preserved,
  475. // since it's treated as the master
  476. if (!i && phy_is_fullmac(phy)) {
  477. prev = 0;
  478. prev_bss_hash[0] = null;
  479. } else {
  480. prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
  481. }
  482. if (prev < 0)
  483. continue;
  484. let cur_config = config.bss[i];
  485. let prev_config = old_config.bss[prev];
  486. if (prev_config.force_reload) {
  487. delete prev_config.force_reload;
  488. continue;
  489. }
  490. let prev_bss = get_config_bss(name, old_config, prev);
  491. if (!prev_bss)
  492. return false;
  493. // try to preserve MAC address of this BSS by reassigning another
  494. // BSS if necessary
  495. if ((cur_config.default_macaddr || cur_config.random_macaddr) &&
  496. cur_config.random_macaddr == prev_config.random_macaddr &&
  497. cur_config.default_macaddr == prev_config.default_macaddr &&
  498. !macaddr_list[prev_config.bssid]) {
  499. macaddr_list[prev_config.bssid] = i;
  500. cur_config.bssid = prev_config.bssid;
  501. }
  502. bss_list[i] = prev_bss;
  503. bss_list_cfg[i] = old_config.bss[prev];
  504. }
  505. if (config.mbssid && !bss_list_cfg[0]) {
  506. hostapd.printf("First BSS changed with MBSSID enabled");
  507. return false;
  508. }
  509. // Step 2: if none were found, rename and preserve the first one
  510. if (length(bss_list) == 0) {
  511. // can't change the bssid of the first bss
  512. if (config.bss[0].bssid != old_config.bss[0].bssid) {
  513. if (!config.bss[0].default_macaddr) {
  514. hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
  515. return false;
  516. }
  517. config.bss[0].bssid = old_config.bss[0].bssid;
  518. }
  519. let prev_bss = get_config_bss(name, old_config, 0);
  520. if (!prev_bss)
  521. return false;
  522. macaddr_list[config.bss[0].bssid] = 0;
  523. bss_list[0] = prev_bss;
  524. bss_list_cfg[0] = old_config.bss[0];
  525. prev_bss_hash[0] = null;
  526. }
  527. // Step 3: delete all unused old interfaces
  528. for (let i = 0; i < length(prev_bss_hash); i++) {
  529. if (!prev_bss_hash[i])
  530. continue;
  531. let prev_bss = get_config_bss(name, old_config, i);
  532. if (!prev_bss)
  533. return false;
  534. let ifname = old_config.bss[i].ifname;
  535. hostapd.printf(`Remove bss '${ifname}' on phy '${name}'`);
  536. prev_bss.delete();
  537. if (!old_config.bss[i].mld_ap)
  538. wdev_remove(ifname);
  539. }
  540. // Step 4: rename preserved interfaces, use temporary name on duplicates
  541. let rename_list = [];
  542. for (let i = 0; i < length(bss_list); i++) {
  543. if (!bss_list[i])
  544. continue;
  545. let old_ifname = bss_list_cfg[i].ifname;
  546. let new_ifname = config.bss[i].ifname;
  547. if (old_ifname == new_ifname)
  548. continue;
  549. if (hostapd.bss[name][new_ifname]) {
  550. new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
  551. push(rename_list, i);
  552. }
  553. hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
  554. if (!bss_list[i].rename(new_ifname)) {
  555. hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
  556. return false;
  557. }
  558. bss_list_cfg[i].ifname = new_ifname;
  559. }
  560. // Step 5: rename interfaces with temporary names
  561. for (let i in rename_list) {
  562. let new_ifname = config.bss[i].ifname;
  563. if (!bss_list[i].rename(new_ifname)) {
  564. hostapd.printf(`Failed to rename bss to ${new_ifname}`);
  565. return false;
  566. }
  567. bss_list_cfg[i].ifname = new_ifname;
  568. }
  569. // Step 6: assign BSSID for newly created interfaces
  570. macaddr_list = iface_macaddr_init(phydev, config, macaddr_list);
  571. for (let i = 0; i < length(config.bss); i++) {
  572. if (bss_list[i])
  573. continue;
  574. let bsscfg = config.bss[i];
  575. let mac_idx = macaddr_list[bsscfg.bssid];
  576. if (mac_idx < 0)
  577. macaddr_list[bsscfg.bssid] = i;
  578. if (mac_idx == i)
  579. continue;
  580. // statically assigned bssid of the new interface is in conflict
  581. // with the bssid of a reused interface. reassign the reused interface
  582. if (!bsscfg.default_macaddr) {
  583. // can't update bssid of the first BSS, need to restart
  584. if (!mac_idx < 0)
  585. return false;
  586. bsscfg = config.bss[mac_idx];
  587. }
  588. let addr = phydev.macaddr_next(i);
  589. if (!addr) {
  590. hostapd.printf(`Failed to generate mac address for phy ${name}`);
  591. return false;
  592. }
  593. bsscfg.bssid = addr;
  594. }
  595. let config_inline = iface_gen_config(config);
  596. // Step 7: fill in the gaps with new interfaces
  597. for (let i = 0; i < length(config.bss); i++) {
  598. let ifname = config.bss[i].ifname;
  599. let bss = bss_list[i];
  600. if (bss)
  601. continue;
  602. hostapd.printf(`Add bss ${ifname} on phy ${name}`);
  603. bss_list[i] = iface.add_bss(config_inline, i);
  604. if (!bss_list[i]) {
  605. hostapd.printf(`Failed to add new bss ${ifname} on phy ${name}`);
  606. return false;
  607. }
  608. }
  609. // Step 8: update interface bss order
  610. if (!iface.set_bss_order(bss_list)) {
  611. hostapd.printf(`Failed to update BSS order on phy '${name}'`);
  612. return false;
  613. }
  614. // Step 9: update config
  615. for (let i = 0; i < length(config.bss); i++) {
  616. if (!bss_list_cfg[i])
  617. continue;
  618. let ifname = config.bss[i].ifname;
  619. let bss = bss_list[i];
  620. if (is_equal(config.bss[i], bss_list_cfg[i]))
  621. continue;
  622. if (is_equal(bss_remove_file_fields(config.bss[i]),
  623. bss_remove_file_fields(bss_list_cfg[i]))) {
  624. hostapd.printf(`Update config data files for bss ${ifname}`);
  625. if (bss.set_config(config_inline, i, true) < 0) {
  626. hostapd.printf(`Could not update config data files for bss ${ifname}`);
  627. return false;
  628. } else {
  629. bss.ctrl("RELOAD_WPA_PSK");
  630. continue;
  631. }
  632. }
  633. bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
  634. bss_reload_rxkhs(bss, config.bss[i], bss_list_cfg[i]);
  635. if (is_equal(config.bss[i], bss_list_cfg[i]))
  636. continue;
  637. hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${name}'`);
  638. if (bss.set_config(config_inline, i) < 0) {
  639. hostapd.printf(`Failed to set config for bss ${ifname}`);
  640. return false;
  641. }
  642. }
  643. return true;
  644. }
  645. function bss_check_mld(phydev, iface_name, bss)
  646. {
  647. if (!bss.ifname)
  648. return;
  649. let mld_data = hostapd.data.mld[bss.ifname];
  650. if (!mld_data || !mld_data.ifname || !mld_data.macaddr)
  651. return;
  652. bss.mld_bssid = mld_data.macaddr;
  653. mld_data.iface[iface_name] = true;
  654. if (!access('/sys/class/net/' + bss.ifname, 'x'))
  655. mld_data.has_wdev = false;
  656. if (mld_data.has_wdev)
  657. return true;
  658. hostapd.printf(`Create MLD interface ${bss.ifname} on phy ${phydev.name}, radio mask: ${mld_data.radio_mask}`);
  659. let err = phydev.wdev_add(bss.ifname, {
  660. mode: "ap",
  661. macaddr: mld_data.macaddr,
  662. radio_mask: mld_data.radio_mask,
  663. });
  664. wdev_set_up(bss.ifname, true);
  665. if (err) {
  666. hostapd.printf(`Failed to create MLD ${bss.ifname} on phy ${phydev.name}: ${err}`);
  667. delete mld_data.iface[iface_name];
  668. return;
  669. }
  670. mld_data.has_wdev = true;
  671. return true;
  672. }
  673. function iface_check_mld(phydev, name, config)
  674. {
  675. phydev = phy_open(phydev.phy);
  676. for (let mld_name, mld_data in hostapd.data.mld)
  677. delete mld_data.iface[name];
  678. for (let i = 0; i < length(config.bss); i++) {
  679. let bss = config.bss[i];
  680. if (!bss.mld_ap)
  681. continue;
  682. if (!bss_check_mld(phydev, name, bss)) {
  683. hostapd.printf(`Skip MLD interface ${name} on phy ${phydev.name}`);
  684. splice(config.bss, i--, 1);
  685. }
  686. }
  687. for (let mld_name, mld_data in hostapd.data.mld) {
  688. if (length(mld_data.iface) > 0)
  689. continue;
  690. hostapd.printf(`Remove MLD interface ${mld_name}`);
  691. wdev_remove(mld_name);
  692. delete mld_data.has_wdev;
  693. }
  694. }
  695. function iface_config_remove(name, old_config)
  696. {
  697. hostapd.remove_iface(name);
  698. return iface_remove(old_config);
  699. }
  700. function iface_set_config(name, config)
  701. {
  702. let old_config = hostapd.data.config[name];
  703. hostapd.data.config[name] = config;
  704. let phy = config.phy;
  705. let phydev = phy_open(phy, config.radio_idx);
  706. if (!phydev) {
  707. hostapd.printf(`Failed to open phy ${phy}`);
  708. return false;
  709. }
  710. config.orig_bss = [ ...config.bss ];
  711. iface_check_mld(phydev, name, config);
  712. if (!length(config.bss))
  713. return iface_config_remove(name, old_config);
  714. try {
  715. let ret = iface_reload_config(name, phydev, config, old_config);
  716. if (ret) {
  717. iface_update_supplicant_macaddr(phydev, config);
  718. hostapd.printf(`Reloaded settings for phy ${name}`);
  719. return 0;
  720. }
  721. } catch (e) {
  722. hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
  723. }
  724. hostapd.printf(`Restart interface for phy ${name}`);
  725. let ret = iface_restart(phydev, config, old_config);
  726. return ret;
  727. }
  728. function config_add_bss(config, name)
  729. {
  730. let bss = {
  731. ifname: name,
  732. data: [],
  733. hash: {}
  734. };
  735. push(config.bss, bss);
  736. return bss;
  737. }
  738. function iface_load_config(phy, radio, filename)
  739. {
  740. if (radio < 0)
  741. radio = null;
  742. let config = {
  743. phy,
  744. radio_idx: radio,
  745. radio: {
  746. data: []
  747. },
  748. bss: [],
  749. orig_file: filename,
  750. };
  751. let f = open(filename, "r");
  752. if (!f)
  753. return config;
  754. let bss;
  755. let line;
  756. while ((line = rtrim(f.read("line"), "\n")) != null) {
  757. let val = split(line, "=", 2);
  758. if (!val[0])
  759. continue;
  760. if (val[0] == "interface") {
  761. bss = config_add_bss(config, val[1]);
  762. break;
  763. }
  764. if (val[0] == "channel") {
  765. config.radio.channel = val[1];
  766. continue;
  767. }
  768. if (val[0] == "#num_global_macaddr")
  769. config[substr(val[0], 1)] = int(val[1]);
  770. else if (val[0] == "#macaddr_base")
  771. config[substr(val[0], 1)] = val[1];
  772. else if (val[0] == "mbssid")
  773. config[val[0]] = int(val[1]);
  774. push(config.radio.data, line);
  775. }
  776. while ((line = rtrim(f.read("line"), "\n")) != null) {
  777. if (line == "#default_macaddr")
  778. bss.default_macaddr = true;
  779. if (line == "#random_macaddr")
  780. bss.random_macaddr = true;
  781. let val = split(line, "=", 2);
  782. if (!val[0])
  783. continue;
  784. if (val[0] == "bssid") {
  785. bss.bssid = lc(val[1]);
  786. continue;
  787. }
  788. if (val[0] == "nas_identifier")
  789. bss.nasid = val[1];
  790. if (val[0] == "mld_ap")
  791. bss[val[0]] = int(val[1]);
  792. if (val[0] == "bss") {
  793. bss = config_add_bss(config, val[1]);
  794. continue;
  795. }
  796. if (hostapd.data.file_fields[val[0]]) {
  797. if (val[0] == "rxkh_file") {
  798. bss.hash[val[0]] = hostapd.sha1(normalize_rxkhs(readfile(val[1])));
  799. } else {
  800. bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
  801. }
  802. }
  803. push(bss.data, line);
  804. }
  805. f.close();
  806. return config;
  807. }
  808. function phy_name(phy, radio)
  809. {
  810. if (!phy)
  811. return null;
  812. if (radio != null && radio >= 0)
  813. phy += "." + radio;
  814. return phy;
  815. }
  816. function bss_config(bss_name) {
  817. for (let phy, config in hostapd.data.config) {
  818. if (!config)
  819. continue;
  820. for (let bss in config.bss)
  821. if (bss.ifname == bss_name)
  822. return [ config, bss ];
  823. }
  824. }
  825. function mld_rename_bss(data, name)
  826. {
  827. if (data.ifname == name)
  828. return true;
  829. // TODO: handle rename gracefully
  830. return false;
  831. }
  832. function mld_add_bss(name, data, phy_list, i)
  833. {
  834. let config = data.config;
  835. if (!config.phy)
  836. return;
  837. hostapd.printf(`Add MLD interface ${name}`);
  838. wdev_remove(name);
  839. let phydev = phy_list[config.phy];
  840. if (!phydev) {
  841. phydev = phy_open(config.phy, 0);
  842. if (!phydev)
  843. return;
  844. let macaddr_list = {};
  845. let phy_config = hostapd.data.config[phy_name(config.phy, 0)];
  846. if (phy_config)
  847. macaddr_list = iface_config_macaddr_list(phy_config);
  848. iface_macaddr_init(phydev, data.config, macaddr_list);
  849. phy_list[config.phy] = phydev;
  850. }
  851. data.macaddr = config.macaddr;
  852. if (!data.macaddr) {
  853. data.macaddr = phydev.macaddr_next();
  854. data.default_macaddr = true;
  855. }
  856. let radio_mask = 0;
  857. for (let r in config.radios)
  858. if (r != null)
  859. radio_mask |= 1 << r;
  860. data.radio_mask = radio_mask;
  861. data.ifname = name;
  862. }
  863. function mld_find_matching_config(list, config)
  864. {
  865. for (let name, data in list)
  866. if (is_equal(data.config, config))
  867. return name;
  868. }
  869. function mld_reload_interface(name)
  870. {
  871. let config = hostapd.data.config[name];
  872. if (!config)
  873. return;
  874. config = { ...config };
  875. config.bss = config.orig_bss;
  876. iface_set_config(name, config);
  877. }
  878. function mld_set_config(config)
  879. {
  880. let prev_mld = { ...hostapd.data.mld };
  881. let new_mld = {};
  882. let phy_list = {};
  883. let new_config = !length(prev_mld);
  884. hostapd.printf(`Set MLD config: ${keys(config)}`);
  885. // find renamed/new interfaces
  886. for (let name, data in config) {
  887. let prev = mld_find_matching_config(prev_mld, data);
  888. if (prev) {
  889. let data = prev_mld[prev];
  890. if (mld_rename_bss(data, name)) {
  891. new_mld[name] = data;
  892. delete prev_mld[prev];
  893. continue;
  894. }
  895. }
  896. new_mld[name] = {
  897. config: data,
  898. iface: {},
  899. };
  900. }
  901. let reload_iface = {};
  902. for (let name, data in prev_mld) {
  903. delete hostapd.data.mld[name];
  904. if (!data.ifname)
  905. continue;
  906. for (let iface, bss_list in hostapd.bss) {
  907. if (!bss_list[name])
  908. continue;
  909. reload_iface[iface] = true;
  910. }
  911. }
  912. for (let name in reload_iface)
  913. mld_reload_interface(name);
  914. for (let name, data in prev_mld) {
  915. if (data.ifname)
  916. hostapd.printf(`Remove MLD interface ${name}`);
  917. wdev_remove(name);
  918. }
  919. // add new interfaces
  920. hostapd.data.mld = new_mld;
  921. for (let name, data in new_mld)
  922. if (!data.ifname)
  923. mld_add_bss(name, data, phy_list);
  924. if (!new_config)
  925. return;
  926. hostapd.printf(`Reload all interfaces`);
  927. for (let name in hostapd.data.config)
  928. mld_reload_interface(name);
  929. }
  930. function dpp_find_bss(ifname)
  931. {
  932. for (let phy, bss_list in hostapd.bss) {
  933. if (bss_list[ifname])
  934. return bss_list[ifname];
  935. }
  936. return null;
  937. }
  938. function dpp_channel_handle_request(channel, req)
  939. {
  940. let data = req.args ?? {};
  941. let bss;
  942. switch (req.type) {
  943. case "start":
  944. if (!data.ifname)
  945. return libubus.STATUS_INVALID_ARGUMENT;
  946. let old_hook = hostapd.data.dpp_hooks[data.ifname];
  947. if (old_hook && old_hook.channel != channel)
  948. old_hook.channel.disconnect();
  949. hostapd.data.dpp_hooks[data.ifname] = {
  950. channel: channel,
  951. timeout_count: 0,
  952. };
  953. return 0;
  954. case "stop":
  955. if (!data.ifname)
  956. return libubus.STATUS_INVALID_ARGUMENT;
  957. let hook = hostapd.data.dpp_hooks[data.ifname];
  958. if (hook && hook.channel == channel)
  959. delete hostapd.data.dpp_hooks[data.ifname];
  960. return 0;
  961. case "tx_action":
  962. bss = dpp_find_bss(data.ifname);
  963. if (!bss)
  964. return libubus.STATUS_NOT_FOUND;
  965. if (!bss.dpp_send_action(data.dst, data.freq ?? 0, data.frame))
  966. return libubus.STATUS_UNKNOWN_ERROR;
  967. return 0;
  968. case "tx_gas_resp":
  969. bss = dpp_find_bss(data.ifname);
  970. if (!bss)
  971. return libubus.STATUS_NOT_FOUND;
  972. if (!bss.dpp_send_gas_resp(data.dst, data.dialog_token, data.data, data.freq ?? 0))
  973. return libubus.STATUS_UNKNOWN_ERROR;
  974. return 0;
  975. case "set_cce":
  976. bss = dpp_find_bss(data.ifname);
  977. if (!bss)
  978. return libubus.STATUS_NOT_FOUND;
  979. let val = data.enable ? "dd04506f9a1e" : "";
  980. bss.ctrl("SET vendor_elements " + val);
  981. bss.ctrl("UPDATE_BEACON");
  982. return 0;
  983. default:
  984. return libubus.STATUS_METHOD_NOT_FOUND;
  985. }
  986. }
  987. function dpp_channel_handle_disconnect(channel)
  988. {
  989. for (let ifname, hook in hostapd.data.dpp_hooks) {
  990. if (hook.channel == channel)
  991. delete hostapd.data.dpp_hooks[ifname];
  992. }
  993. }
  994. function dpp_rx_via_channel(ifname, method, data)
  995. {
  996. let hook = hostapd.data.dpp_hooks[ifname];
  997. if (!hook)
  998. return null;
  999. let response = hook.channel.request({
  1000. method: method,
  1001. data: data,
  1002. });
  1003. if (hook.channel.error(true) == libubus.STATUS_TIMEOUT) {
  1004. hook.timeout_count++;
  1005. if (hook.timeout_count >= 3) {
  1006. hostapd.printf(`DPP channel timeout for ${ifname}, disconnecting`);
  1007. hook.channel.disconnect();
  1008. delete hostapd.data.dpp_hooks[ifname];
  1009. }
  1010. return null;
  1011. }
  1012. hook.timeout_count = 0;
  1013. return response;
  1014. }
  1015. let main_obj = {
  1016. reload: {
  1017. args: {
  1018. phy: "",
  1019. radio: 0,
  1020. },
  1021. call: function(req) {
  1022. let phy_list = req.args.phy ? [ phy_name(req.args.phy, req.args.radio) ] : keys(hostapd.data.config);
  1023. for (let phy_name in phy_list) {
  1024. let phy = hostapd.data.config[phy_name];
  1025. let config = iface_load_config(phy.phy, phy.radio_idx, phy.orig_file);
  1026. iface_set_config(phy_name, config);
  1027. }
  1028. return 0;
  1029. }
  1030. },
  1031. apsta_state: {
  1032. args: {
  1033. phy: "",
  1034. radio: 0,
  1035. up: true,
  1036. frequency: 0,
  1037. sec_chan_offset: 0,
  1038. csa: true,
  1039. csa_count: 0,
  1040. },
  1041. call: function(req) {
  1042. let phy = phy_name(req.args.phy, req.args.radio);
  1043. if (req.args.up == null || !phy)
  1044. return libubus.STATUS_INVALID_ARGUMENT;
  1045. let config = hostapd.data.config[phy];
  1046. if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
  1047. return 0;
  1048. let iface = hostapd.interfaces[phy];
  1049. if (!iface)
  1050. return 0;
  1051. if (!req.args.up) {
  1052. iface.stop();
  1053. return 0;
  1054. }
  1055. let freq_info;
  1056. if (req.args.frequency) {
  1057. freq_info = iface_freq_info(iface, config, req.args);
  1058. if (!freq_info)
  1059. return libubus.STATUS_UNKNOWN_ERROR;
  1060. if (req.args.csa) {
  1061. freq_info.csa_count = req.args.csa_count ?? 10;
  1062. let ret = iface.switch_channel(freq_info);
  1063. if (!ret)
  1064. return libubus.STATUS_UNKNOWN_ERROR;
  1065. return 0;
  1066. }
  1067. }
  1068. let ret = iface.start(freq_info);
  1069. if (!ret)
  1070. return libubus.STATUS_UNKNOWN_ERROR;
  1071. return 0;
  1072. }
  1073. },
  1074. config_get_macaddr_list: {
  1075. args: {
  1076. phy: "",
  1077. radio: 0,
  1078. },
  1079. call: function(req) {
  1080. let phy = phy_name(req.args.phy, req.args.radio);
  1081. if (!phy)
  1082. return libubus.STATUS_INVALID_ARGUMENT;
  1083. let ret = {
  1084. macaddr: [],
  1085. };
  1086. let config = hostapd.data.config[phy];
  1087. if (!config)
  1088. return ret;
  1089. ret.macaddr = map(config.bss, (bss) => bss.bssid);
  1090. return ret;
  1091. }
  1092. },
  1093. switch_channel: {
  1094. args: {
  1095. phy: "",
  1096. radio: 0,
  1097. csa_count: 0,
  1098. sec_channel: 0,
  1099. oper_chwidth: 0,
  1100. frequency: 0,
  1101. center_freq1: 0,
  1102. center_freq2: 0,
  1103. },
  1104. call: function(req) {
  1105. let phy = phy_name(req.args.phy, req.args.radio);
  1106. if (!req.args.frequency || !phy)
  1107. return libubus.STATUS_INVALID_ARGUMENT;
  1108. let iface = hostapd.interfaces[phy];
  1109. if (!iface)
  1110. return libubus.STATUS_NOT_FOUND;
  1111. req.args.csa_count ??= 10;
  1112. let ret = iface.switch_channel(req.args);
  1113. if (!ret)
  1114. return libubus.STATUS_UNKNOWN_ERROR;
  1115. return 0;
  1116. },
  1117. },
  1118. mld_set: {
  1119. args: {
  1120. config: {}
  1121. },
  1122. call: function(req) {
  1123. if (!req.args.config)
  1124. return libubus.STATUS_INVALID_ARGUMENT;
  1125. mld_set_config(req.args.config);
  1126. return {
  1127. pid: hostapd.getpid()
  1128. };
  1129. }
  1130. },
  1131. config_reset: {
  1132. args: {
  1133. },
  1134. call: function(req) {
  1135. for (let name in hostapd.data.config)
  1136. iface_set_config(name);
  1137. mld_set_config({});
  1138. return 0;
  1139. }
  1140. },
  1141. config_set: {
  1142. args: {
  1143. phy: "",
  1144. radio: 0,
  1145. config: "",
  1146. prev_config: "",
  1147. },
  1148. call: function(req) {
  1149. let phy = req.args.phy;
  1150. let radio = req.args.radio;
  1151. let name = phy_name(phy, radio);
  1152. let file = req.args.config;
  1153. let prev_file = req.args.prev_config;
  1154. if (!phy)
  1155. return libubus.STATUS_INVALID_ARGUMENT;
  1156. if (prev_file && !hostapd.data.config[name]) {
  1157. let config = iface_load_config(phy, radio, prev_file);
  1158. if (config)
  1159. config.radio.data = [];
  1160. hostapd.data.config[name] = config;
  1161. }
  1162. let config = iface_load_config(phy, radio, file);
  1163. hostapd.printf(`Set new config for phy ${name}: ${file}`);
  1164. iface_set_config(name, config);
  1165. if (hostapd.data.auth_obj)
  1166. hostapd.data.auth_obj.notify("reload", { phy, radio });
  1167. return {
  1168. pid: hostapd.getpid()
  1169. };
  1170. }
  1171. },
  1172. config_add: {
  1173. args: {
  1174. iface: "",
  1175. config: "",
  1176. },
  1177. call: function(req) {
  1178. if (!req.args.iface || !req.args.config)
  1179. return libubus.STATUS_INVALID_ARGUMENT;
  1180. if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
  1181. return libubus.STATUS_INVALID_ARGUMENT;
  1182. return {
  1183. pid: hostapd.getpid()
  1184. };
  1185. }
  1186. },
  1187. config_remove: {
  1188. args: {
  1189. iface: ""
  1190. },
  1191. call: function(req) {
  1192. if (!req.args.iface)
  1193. return libubus.STATUS_INVALID_ARGUMENT;
  1194. hostapd.remove_iface(req.args.iface);
  1195. return 0;
  1196. }
  1197. },
  1198. bss_info: {
  1199. args: {
  1200. iface: ""
  1201. },
  1202. call: function(req) {
  1203. if (!req.args.iface)
  1204. return libubus.STATUS_INVALID_ARGUMENT;
  1205. let config = bss_config(req.args.iface);
  1206. if (!config)
  1207. return libubus.STATUS_NOT_FOUND;
  1208. let bss = config[1];
  1209. config = config[0];
  1210. let ret = {};
  1211. for (let line in [ ...config.radio.data, ...bss.data ]) {
  1212. let fields = split(line, "=", 2);
  1213. let name = fields[0];
  1214. if (hostapd.data.bss_info_fields[name])
  1215. ret[name] = fields[1];
  1216. }
  1217. return ret;
  1218. }
  1219. },
  1220. status: {
  1221. args: {},
  1222. call: function(req) {
  1223. let interfaces = {};
  1224. for (let phy_name, config in hostapd.data.config) {
  1225. if (!config || !config.bss)
  1226. continue;
  1227. let is_pending = !!hostapd.data.pending_config[phy_name];
  1228. let iface = hostapd.interfaces[phy_name];
  1229. let is_running = iface && iface.state() == "ENABLED" && !is_pending;
  1230. for (let bss in config.bss) {
  1231. let ifname = bss.ifname;
  1232. let entry = interfaces[ifname];
  1233. if (bss.mld_ap) {
  1234. if (!entry) {
  1235. let mld = hostapd.data.mld[ifname];
  1236. entry = interfaces[ifname] = {
  1237. wiphy: config.phy,
  1238. macaddr: mld ? mld.macaddr : bss.mld_bssid,
  1239. links: {},
  1240. };
  1241. }
  1242. entry.links[config.radio_idx ?? 0] = {
  1243. radio: config.radio_idx ?? 0,
  1244. macaddr: bss.bssid,
  1245. running: is_running,
  1246. pending: is_pending,
  1247. };
  1248. } else {
  1249. entry = {
  1250. wiphy: config.phy,
  1251. macaddr: bss.bssid,
  1252. running: is_running,
  1253. pending: is_pending,
  1254. };
  1255. if (config.radio_idx != null && config.radio_idx >= 0)
  1256. entry.radio = config.radio_idx;
  1257. interfaces[ifname] = entry;
  1258. }
  1259. }
  1260. }
  1261. return { interfaces };
  1262. }
  1263. },
  1264. dpp_channel: {
  1265. args: {},
  1266. call: function(req) {
  1267. let channel;
  1268. let on_request = (chan_req) => dpp_channel_handle_request(channel, chan_req);
  1269. let on_disconnect = () => dpp_channel_handle_disconnect(channel);
  1270. channel = req.new_channel(on_request, on_disconnect, 1);
  1271. if (!channel)
  1272. return libubus.STATUS_UNKNOWN_ERROR;
  1273. return 0;
  1274. }
  1275. },
  1276. };
  1277. hostapd.data.ubus = ubus;
  1278. hostapd.data.obj = ubus.publish("hostapd", main_obj);
  1279. let auth_obj = {};
  1280. hostapd.data.auth_obj = ubus.publish("hostapd-auth", auth_obj);
  1281. hostapd.udebug_set("hostapd", hostapd.data.ubus);
  1282. function bss_event(type, name, data) {
  1283. let ubus = hostapd.data.ubus;
  1284. data ??= {};
  1285. data.name = name;
  1286. hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
  1287. ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
  1288. }
  1289. return {
  1290. shutdown: function() {
  1291. for (let phy in hostapd.data.config)
  1292. iface_set_config(phy);
  1293. hostapd.udebug_set(null);
  1294. hostapd.ubus.disconnect();
  1295. },
  1296. bss_create: function(phy, name, obj) {
  1297. phy = hostapd.data.config[phy];
  1298. if (!phy)
  1299. return;
  1300. if (phy.radio_idx != null && phy.radio_idx >= 0)
  1301. wdev_set_radio_mask(name, 1 << phy.radio_idx);
  1302. },
  1303. bss_add: function(phy, name, obj) {
  1304. bss_event("add", name);
  1305. },
  1306. bss_reload: function(phy, name, obj, reconf) {
  1307. bss_event("reload", name, { reconf: reconf != 0 });
  1308. },
  1309. bss_remove: function(phy, name, obj) {
  1310. delete hostapd.data.dpp_hooks[name];
  1311. bss_event("remove", name);
  1312. },
  1313. sta_auth: function(iface, sta) {
  1314. let msg = { iface, sta };
  1315. let ret = {};
  1316. let data_cb = (type, data) => {
  1317. ret = { ...ret, ...data };
  1318. };
  1319. if (hostapd.data.auth_obj)
  1320. hostapd.data.auth_obj.notify("sta_auth", msg, data_cb, null, null, 1000);
  1321. return ret;
  1322. },
  1323. sta_connected: function(iface, sta, data) {
  1324. let msg = { iface, sta, ...data };
  1325. let ret = {};
  1326. let data_cb = (type, data) => {
  1327. ret = { ...ret, ...data };
  1328. };
  1329. if (hostapd.data.auth_obj)
  1330. hostapd.data.auth_obj.notify("sta_connected", msg, data_cb, null, null, 1000);
  1331. return ret;
  1332. },
  1333. dpp_rx_action: function(iface, src, frame_type, freq, frame) {
  1334. let response = dpp_rx_via_channel(iface, "rx_action", {
  1335. ifname: iface, src, frame_type, freq, frame,
  1336. });
  1337. if (response && response.handled)
  1338. return true;
  1339. return false;
  1340. },
  1341. dpp_rx_gas: function(iface, src, dialog_token, query, freq) {
  1342. let response = dpp_rx_via_channel(iface, "rx_gas", {
  1343. ifname: iface, src, dialog_token, query, freq,
  1344. });
  1345. if (response && response.response)
  1346. return response.response;
  1347. return null;
  1348. },
  1349. wps_m7_rx: function(ifname, addr, data) {
  1350. let response = dpp_rx_via_channel(ifname, "wps_m7_rx", {
  1351. ifname, addr, data,
  1352. });
  1353. if (!response)
  1354. return null;
  1355. return response;
  1356. },
  1357. };