hostapd.uc 30 KB

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