hostapd.uc 31 KB

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