blob: 7e9fde498b332835ebc31d8b78ea12f1ce650de6 [file] [log] [blame]
developerf0fd7052023-08-14 20:23:42 +08001let libubus = require("ubus");
2import { open, readfile } from "fs";
developere92ee3c2023-10-25 17:01:28 +08003import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
developerf0fd7052023-08-14 20:23:42 +08004
developer753619c2024-02-22 13:42:45 +08005let ubus = libubus.connect(null, 60);
developerf0fd7052023-08-14 20:23:42 +08006
7hostapd.data.config = {};
developer753619c2024-02-22 13:42:45 +08008hostapd.data.pending_config = {};
developerf0fd7052023-08-14 20:23:42 +08009
10hostapd.data.file_fields = {
11 vlan_file: true,
12 wpa_psk_file: true,
13 accept_mac_file: true,
14 deny_mac_file: true,
15 eap_user_file: true,
16 ca_cert: true,
17 server_cert: true,
18 server_cert2: true,
19 private_key: true,
20 private_key2: true,
21 dh_file: true,
22 eap_sim_db: true,
23};
24
25function iface_remove(cfg)
26{
27 if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
28 return;
29
developerf0fd7052023-08-14 20:23:42 +080030 for (let bss in cfg.bss)
developer66e89bc2024-04-23 14:50:01 +080031 if (!bss.mld_ap || bss.mld_primary == 1)
32 wdev_remove(bss.ifname);
developerf0fd7052023-08-14 20:23:42 +080033}
34
developere92ee3c2023-10-25 17:01:28 +080035function iface_gen_config(phy, config, start_disabled)
developerf0fd7052023-08-14 20:23:42 +080036{
37 let str = `data:
38${join("\n", config.radio.data)}
39channel=${config.radio.channel}
40`;
41
42 for (let i = 0; i < length(config.bss); i++) {
43 let bss = config.bss[i];
44 let type = i > 0 ? "bss" : "interface";
developere92ee3c2023-10-25 17:01:28 +080045 let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
developerf0fd7052023-08-14 20:23:42 +080046
47 str += `
48${type}=${bss.ifname}
developere92ee3c2023-10-25 17:01:28 +080049bssid=${bss.bssid}
developerf0fd7052023-08-14 20:23:42 +080050${join("\n", bss.data)}
developere92ee3c2023-10-25 17:01:28 +080051nas_identifier=${nasid}
52`;
53 if (start_disabled)
54 str += `
55start_disabled=1
developerf0fd7052023-08-14 20:23:42 +080056`;
57 }
58
59 return str;
60}
61
developere92ee3c2023-10-25 17:01:28 +080062function iface_freq_info(iface, config, params)
63{
64 let freq = params.frequency;
developerab5e7752023-12-21 16:18:59 +080065 let bw320_offset = params.bw320_offset;
developer66e89bc2024-04-23 14:50:01 +080066 let band_idx = params.band_idx;
developere92ee3c2023-10-25 17:01:28 +080067 if (!freq)
68 return null;
69
70 let sec_offset = params.sec_chan_offset;
71 if (sec_offset != -1 && sec_offset != 1)
72 sec_offset = 0;
73
74 let width = 0;
developerab5e7752023-12-21 16:18:59 +080075 if (params.ch_width >= 0){
76 width = params.ch_width;
77 } else {
78 for (let line in config.radio.data) {
79 if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
80 sec_offset = null; // auto-detect
81 continue;
82 }
developere92ee3c2023-10-25 17:01:28 +080083
developerab5e7752023-12-21 16:18:59 +080084 let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth|eht_oper_chwidth)=(\d+)/);
85 if (!val)
86 continue;
developere92ee3c2023-10-25 17:01:28 +080087
developerab5e7752023-12-21 16:18:59 +080088 val = int(val[2]);
89 if (val > width)
90 width = val;
91 }
developere92ee3c2023-10-25 17:01:28 +080092 }
93
94 if (freq < 4000)
95 width = 0;
96
developer66e89bc2024-04-23 14:50:01 +080097 return hostapd.freq_info(freq, sec_offset, width, bw320_offset, band_idx);
developere92ee3c2023-10-25 17:01:28 +080098}
99
100function iface_add(phy, config, phy_status)
101{
102 let config_inline = iface_gen_config(phy, config, !!phy_status);
103
104 let bss = config.bss[0];
105 let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
106 if (ret < 0)
107 return false;
108
109 if (!phy_status)
110 return true;
111
112 let iface = hostapd.interfaces[phy];
113 if (!iface)
114 return false;
115
116 let freq_info = iface_freq_info(iface, config, phy_status);
117
118 return iface.start(freq_info) >= 0;
119}
120
121function iface_config_macaddr_list(config)
developerf0fd7052023-08-14 20:23:42 +0800122{
developere92ee3c2023-10-25 17:01:28 +0800123 let macaddr_list = {};
124 for (let i = 0; i < length(config.bss); i++) {
125 let bss = config.bss[i];
126 if (!bss.default_macaddr)
127 macaddr_list[bss.bssid] = i;
128 }
129
130 return macaddr_list;
131}
132
133function iface_update_supplicant_macaddr(phy, config)
134{
135 let macaddr_list = [];
136 for (let i = 0; i < length(config.bss); i++)
137 push(macaddr_list, config.bss[i].bssid);
developer753619c2024-02-22 13:42:45 +0800138 ubus.defer("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
developere92ee3c2023-10-25 17:01:28 +0800139}
140
developer753619c2024-02-22 13:42:45 +0800141function __iface_pending_next(pending, state, ret, data)
142{
143 let config = pending.config;
144 let phydev = pending.phydev;
145 let phy = pending.phy;
146 let bss = config.bss[0];
147
148 if (pending.defer)
149 pending.defer.abort();
150 delete pending.defer;
151 switch (state) {
152 case "init":
153 let macaddr_list = [];
154 for (let i = 0; i < length(config.bss); i++)
155 push(macaddr_list, config.bss[i].bssid);
156 pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
157 return "create_bss";
158 case "create_bss":
developer66e89bc2024-04-23 14:50:01 +0800159 if (!bss.mld_ap || bss.mld_primary == 1) {
160 let err = wdev_create(config.single_hw == 1 ? "phy0" : phy, bss.ifname, { mode: "ap" });
161 if (err) {
162 hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
163 return null;
164 }
developer753619c2024-02-22 13:42:45 +0800165 }
166
developer66e89bc2024-04-23 14:50:01 +0800167 pending.call("wpa_supplicant", "phy_status", { phy: bss.mld_ap ? "phy0" : phy });
developer753619c2024-02-22 13:42:45 +0800168 return "check_phy";
169 case "check_phy":
170 let phy_status = data;
171 if (phy_status && phy_status.state == "COMPLETED") {
172 if (iface_add(phy, config, phy_status))
173 return "done";
174
175 hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
176 }
developer66e89bc2024-04-23 14:50:01 +0800177 pending.call("wpa_supplicant", "phy_set_state", { phy: bss.mld_ap ? "phy0" : phy, stop: true });
developer753619c2024-02-22 13:42:45 +0800178 return "wpas_stopped";
179 case "wpas_stopped":
180 if (!iface_add(phy, config))
181 hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
developer66e89bc2024-04-23 14:50:01 +0800182 let iface = hostapd.interfaces[phy];
183 if (!bss.mld_ap)
184 pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
185 else if (iface.is_mld_finished())
186 pending.call("wpa_supplicant", "phy_set_state", { phy: "phy0", stop: false });
developer753619c2024-02-22 13:42:45 +0800187 return null;
188 case "done":
189 default:
190 delete hostapd.data.pending_config[phy];
191 break;
192 }
193}
194
195function iface_pending_next(ret, data)
196{
197 let pending = true;
198 let cfg = this;
199
200 while (pending) {
201 this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
202 if (!this.next_state) {
203 __iface_pending_next(cfg, "done");
204 return;
205 }
206 pending = !this.defer;
207 }
208}
209
210function iface_pending_abort()
211{
212 this.next_state = "done";
213 this.next();
214}
215
216function iface_pending_ubus_call(obj, method, arg)
217{
218 let ubus = hostapd.data.ubus;
219 let pending = this;
220 this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) });
221}
222
223const iface_pending_proto = {
224 next: iface_pending_next,
225 call: iface_pending_ubus_call,
226 abort: iface_pending_abort,
227};
228
229function iface_pending_init(phydev, config)
230{
231 let phy = phydev.name;
232
233 let pending = proto({
234 next_state: "init",
235 phydev: phydev,
236 phy: phy,
237 config: config,
238 next: iface_pending_next,
239 }, iface_pending_proto);
240
241 hostapd.data.pending_config[phy] = pending;
242 pending.next();
243}
244
developere92ee3c2023-10-25 17:01:28 +0800245function iface_restart(phydev, config, old_config)
246{
247 let phy = phydev.name;
developer753619c2024-02-22 13:42:45 +0800248 let pending = hostapd.data.pending_config[phy];
249
250 if (pending)
251 pending.abort();
developere92ee3c2023-10-25 17:01:28 +0800252
253 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800254 iface_remove(old_config);
255 iface_remove(config);
256
257 if (!config.bss || !config.bss[0]) {
258 hostapd.printf(`No bss for phy ${phy}`);
259 return;
260 }
261
developere92ee3c2023-10-25 17:01:28 +0800262 phydev.macaddr_init(iface_config_macaddr_list(config));
263 for (let i = 0; i < length(config.bss); i++) {
264 let bss = config.bss[i];
265 if (bss.default_macaddr)
266 bss.bssid = phydev.macaddr_next();
267 }
268
developer753619c2024-02-22 13:42:45 +0800269 iface_pending_init(phydev, config);
developerf0fd7052023-08-14 20:23:42 +0800270}
271
272function array_to_obj(arr, key, start)
273{
274 let obj = {};
275
276 start ??= 0;
277 for (let i = start; i < length(arr); i++) {
278 let cur = arr[i];
279 obj[cur[key]] = cur;
280 }
281
282 return obj;
283}
284
285function find_array_idx(arr, key, val)
286{
287 for (let i = 0; i < length(arr); i++)
288 if (arr[i][key] == val)
289 return i;
290
291 return -1;
292}
293
294function bss_reload_psk(bss, config, old_config)
295{
296 if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
297 return;
298
299 old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
300 if (!is_equal(old_config, config))
301 return;
302
303 let ret = bss.ctrl("RELOAD_WPA_PSK");
304 ret ??= "failed";
305
306 hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
307}
308
developere92ee3c2023-10-25 17:01:28 +0800309function remove_file_fields(config)
developerf0fd7052023-08-14 20:23:42 +0800310{
developere92ee3c2023-10-25 17:01:28 +0800311 return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
312}
313
314function bss_remove_file_fields(config)
315{
316 let new_cfg = {};
317
318 for (let key in config)
319 new_cfg[key] = config[key];
320 new_cfg.data = remove_file_fields(new_cfg.data);
321 new_cfg.hash = {};
322 for (let key in config.hash)
323 new_cfg.hash[key] = config.hash[key];
324 delete new_cfg.hash.wpa_psk_file;
325 delete new_cfg.hash.vlan_file;
326
327 return new_cfg;
328}
329
330function bss_config_hash(config)
331{
332 return hostapd.sha1(remove_file_fields(config) + "");
333}
334
335function bss_find_existing(config, prev_config, prev_hash)
336{
337 let hash = bss_config_hash(config.data);
338
339 for (let i = 0; i < length(prev_config.bss); i++) {
340 if (!prev_hash[i] || hash != prev_hash[i])
341 continue;
342
343 prev_hash[i] = null;
344 return i;
345 }
346
347 return -1;
348}
349
350function get_config_bss(config, idx)
351{
352 if (!config.bss[idx]) {
353 hostapd.printf(`Invalid bss index ${idx}`);
354 return null;
355 }
356
357 let ifname = config.bss[idx].ifname;
358 if (!ifname)
359 hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
360
361 return hostapd.bss[ifname];
362}
363
364function iface_reload_config(phydev, config, old_config)
365{
366 let phy = phydev.name;
367
developerf0fd7052023-08-14 20:23:42 +0800368 if (!old_config || !is_equal(old_config.radio, config.radio))
369 return false;
370
371 if (is_equal(old_config.bss, config.bss))
372 return true;
373
developer753619c2024-02-22 13:42:45 +0800374 if (hostapd.data.pending_config[phy])
375 return false;
376
developerf0fd7052023-08-14 20:23:42 +0800377 if (!old_config.bss || !old_config.bss[0])
378 return false;
379
developere92ee3c2023-10-25 17:01:28 +0800380 let iface = hostapd.interfaces[phy];
developerab5e7752023-12-21 16:18:59 +0800381 let iface_name = old_config.bss[0].ifname;
developere92ee3c2023-10-25 17:01:28 +0800382 if (!iface) {
383 hostapd.printf(`Could not find previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800384 return false;
developere92ee3c2023-10-25 17:01:28 +0800385 }
developerf0fd7052023-08-14 20:23:42 +0800386
developere92ee3c2023-10-25 17:01:28 +0800387 let first_bss = hostapd.bss[iface_name];
388 if (!first_bss) {
389 hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800390 return false;
developere92ee3c2023-10-25 17:01:28 +0800391 }
developerf0fd7052023-08-14 20:23:42 +0800392
developere92ee3c2023-10-25 17:01:28 +0800393 let macaddr_list = iface_config_macaddr_list(config);
394 let bss_list = [];
395 let bss_list_cfg = [];
396 let prev_bss_hash = [];
developerf0fd7052023-08-14 20:23:42 +0800397
developere92ee3c2023-10-25 17:01:28 +0800398 for (let bss in old_config.bss) {
399 let hash = bss_config_hash(bss.data);
400 push(prev_bss_hash, bss_config_hash(bss.data));
401 }
developerf0fd7052023-08-14 20:23:42 +0800402
developere92ee3c2023-10-25 17:01:28 +0800403 // Step 1: find (possibly renamed) interfaces with the same config
404 // and store them in the new order (with gaps)
405 for (let i = 0; i < length(config.bss); i++) {
406 let prev;
407
408 // For fullmac devices, the first interface needs to be preserved,
409 // since it's treated as the master
410 if (!i && phy_is_fullmac(phy)) {
411 prev = 0;
412 prev_bss_hash[0] = null;
413 } else {
414 prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
415 }
416 if (prev < 0)
417 continue;
418
419 let cur_config = config.bss[i];
420 let prev_config = old_config.bss[prev];
421
422 let prev_bss = get_config_bss(old_config, prev);
423 if (!prev_bss)
developerf0fd7052023-08-14 20:23:42 +0800424 return false;
developere92ee3c2023-10-25 17:01:28 +0800425
426 // try to preserve MAC address of this BSS by reassigning another
427 // BSS if necessary
428 if (cur_config.default_macaddr &&
429 !macaddr_list[prev_config.bssid]) {
430 macaddr_list[prev_config.bssid] = i;
431 cur_config.bssid = prev_config.bssid;
developerf0fd7052023-08-14 20:23:42 +0800432 }
developere92ee3c2023-10-25 17:01:28 +0800433
434 bss_list[i] = prev_bss;
435 bss_list_cfg[i] = old_config.bss[prev];
developerf0fd7052023-08-14 20:23:42 +0800436 }
437
developere92ee3c2023-10-25 17:01:28 +0800438 if (config.mbssid && !bss_list_cfg[0]) {
439 hostapd.printf("First BSS changed with MBSSID enabled");
440 return false;
441 }
developerf0fd7052023-08-14 20:23:42 +0800442
developere92ee3c2023-10-25 17:01:28 +0800443 // Step 2: if none were found, rename and preserve the first one
444 if (length(bss_list) == 0) {
445 // can't change the bssid of the first bss
446 if (config.bss[0].bssid != old_config.bss[0].bssid) {
447 if (!config.bss[0].default_macaddr) {
448 hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
449 return false;
450 }
451
452 config.bss[0].bssid = old_config.bss[0].bssid;
developerf0fd7052023-08-14 20:23:42 +0800453 }
454
developere92ee3c2023-10-25 17:01:28 +0800455 let prev_bss = get_config_bss(old_config, 0);
456 if (!prev_bss)
457 return false;
458
459 macaddr_list[config.bss[0].bssid] = 0;
460 bss_list[0] = prev_bss;
461 bss_list_cfg[0] = old_config.bss[0];
462 prev_bss_hash[0] = null;
463 }
464
465 // Step 3: delete all unused old interfaces
466 for (let i = 0; i < length(prev_bss_hash); i++) {
467 if (!prev_bss_hash[i])
developerf0fd7052023-08-14 20:23:42 +0800468 continue;
developerf0fd7052023-08-14 20:23:42 +0800469
developere92ee3c2023-10-25 17:01:28 +0800470 let prev_bss = get_config_bss(old_config, i);
471 if (!prev_bss)
472 return false;
473
474 let ifname = old_config.bss[i].ifname;
475 hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
476 prev_bss.delete();
477 wdev_remove(ifname);
478 }
developerf0fd7052023-08-14 20:23:42 +0800479
developere92ee3c2023-10-25 17:01:28 +0800480 // Step 4: rename preserved interfaces, use temporary name on duplicates
481 let rename_list = [];
482 for (let i = 0; i < length(bss_list); i++) {
483 if (!bss_list[i])
developerf0fd7052023-08-14 20:23:42 +0800484 continue;
485
developere92ee3c2023-10-25 17:01:28 +0800486 let old_ifname = bss_list_cfg[i].ifname;
487 let new_ifname = config.bss[i].ifname;
488 if (old_ifname == new_ifname)
489 continue;
490
491 if (hostapd.bss[new_ifname]) {
492 new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
493 push(rename_list, i);
494 }
495
496 hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
497 if (!bss_list[i].rename(new_ifname)) {
498 hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800499 return false;
500 }
501
developere92ee3c2023-10-25 17:01:28 +0800502 bss_list_cfg[i].ifname = new_ifname;
503 }
504
505 // Step 5: rename interfaces with temporary names
506 for (let i in rename_list) {
507 let new_ifname = config.bss[i].ifname;
508 if (!bss_list[i].rename(new_ifname)) {
509 hostapd.printf(`Failed to rename bss to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800510 return false;
511 }
developere92ee3c2023-10-25 17:01:28 +0800512 bss_list_cfg[i].ifname = new_ifname;
developerf0fd7052023-08-14 20:23:42 +0800513 }
514
developere92ee3c2023-10-25 17:01:28 +0800515 // Step 6: assign BSSID for newly created interfaces
516 let macaddr_data = {
517 num_global: config.num_global_macaddr ?? 1,
518 mbssid: config.mbssid ?? 0,
519 };
520 macaddr_list = phydev.macaddr_init(macaddr_list, macaddr_data);
521 for (let i = 0; i < length(config.bss); i++) {
522 if (bss_list[i])
523 continue;
524 let bsscfg = config.bss[i];
525
526 let mac_idx = macaddr_list[bsscfg.bssid];
527 if (mac_idx < 0)
528 macaddr_list[bsscfg.bssid] = i;
529 if (mac_idx == i)
530 continue;
531
532 // statically assigned bssid of the new interface is in conflict
533 // with the bssid of a reused interface. reassign the reused interface
534 if (!bsscfg.default_macaddr) {
535 // can't update bssid of the first BSS, need to restart
536 if (!mac_idx < 0)
537 return false;
developerf0fd7052023-08-14 20:23:42 +0800538
developere92ee3c2023-10-25 17:01:28 +0800539 bsscfg = config.bss[mac_idx];
540 }
541
542 let addr = phydev.macaddr_next(i);
543 if (!addr) {
544 hostapd.printf(`Failed to generate mac address for phy ${phy}`);
developerf0fd7052023-08-14 20:23:42 +0800545 return false;
546 }
developere92ee3c2023-10-25 17:01:28 +0800547 bsscfg.bssid = addr;
548 }
developerf0fd7052023-08-14 20:23:42 +0800549
developere92ee3c2023-10-25 17:01:28 +0800550 let config_inline = iface_gen_config(phy, config);
551
552 // Step 7: fill in the gaps with new interfaces
553 for (let i = 0; i < length(config.bss); i++) {
554 let ifname = config.bss[i].ifname;
555 let bss = bss_list[i];
556
557 if (bss)
558 continue;
559
560 hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
561 bss_list[i] = iface.add_bss(config_inline, i);
562 if (!bss_list[i]) {
563 hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
564 return false;
565 }
566 }
567
568 // Step 8: update interface bss order
569 if (!iface.set_bss_order(bss_list)) {
570 hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
571 return false;
572 }
573
574 // Step 9: update config
575 for (let i = 0; i < length(config.bss); i++) {
576 if (!bss_list_cfg[i])
577 continue;
578
579 let ifname = config.bss[i].ifname;
580 let bss = bss_list[i];
581
582 if (is_equal(config.bss[i], bss_list_cfg[i]))
583 continue;
584
585 if (is_equal(bss_remove_file_fields(config.bss[i]),
586 bss_remove_file_fields(bss_list_cfg[i]))) {
587 hostapd.printf(`Update config data files for bss ${ifname}`);
588 if (bss.set_config(config_inline, i, true) < 0) {
589 hostapd.printf(`Could not update config data files for bss ${ifname}`);
590 return false;
591 } else {
592 bss.ctrl("RELOAD_WPA_PSK");
593 continue;
594 }
595 }
596
597 bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
598 if (is_equal(config.bss[i], bss_list_cfg[i]))
599 continue;
600
601 hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
602 if (bss.set_config(config_inline, i) < 0) {
603 hostapd.printf(`Failed to set config for bss ${ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800604 return false;
605 }
606 }
607
608 return true;
609}
610
611function iface_set_config(phy, config)
612{
613 let old_config = hostapd.data.config[phy];
614
615 hostapd.data.config[phy] = config;
616
developere92ee3c2023-10-25 17:01:28 +0800617 if (!config) {
618 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800619 return iface_remove(old_config);
developere92ee3c2023-10-25 17:01:28 +0800620 }
621
622 let phydev = phy_open(phy);
623 if (!phydev) {
624 hostapd.printf(`Failed to open phy ${phy}`);
625 return false;
626 }
developerf0fd7052023-08-14 20:23:42 +0800627
developere92ee3c2023-10-25 17:01:28 +0800628 try {
629 let ret = iface_reload_config(phydev, config, old_config);
630 if (ret) {
631 iface_update_supplicant_macaddr(phy, config);
632 hostapd.printf(`Reloaded settings for phy ${phy}`);
633 return 0;
634 }
635 } catch (e) {
636 hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
developerf0fd7052023-08-14 20:23:42 +0800637 }
638
639 hostapd.printf(`Restart interface for phy ${phy}`);
developere92ee3c2023-10-25 17:01:28 +0800640 let ret = iface_restart(phydev, config, old_config);
641
642 return ret;
developerf0fd7052023-08-14 20:23:42 +0800643}
644
645function config_add_bss(config, name)
646{
647 let bss = {
648 ifname: name,
649 data: [],
650 hash: {}
651 };
652
653 push(config.bss, bss);
654
655 return bss;
656}
657
658function iface_load_config(filename)
659{
660 let f = open(filename, "r");
661 if (!f)
662 return null;
663
664 let config = {
665 radio: {
666 data: []
667 },
668 bss: [],
669 orig_file: filename,
670 };
671
672 let bss;
673 let line;
developer70180b02023-11-14 17:01:47 +0800674 while ((line = rtrim(f.read("line"), "\n")) != null) {
developerf0fd7052023-08-14 20:23:42 +0800675 let val = split(line, "=", 2);
676 if (!val[0])
677 continue;
678
679 if (val[0] == "interface") {
680 bss = config_add_bss(config, val[1]);
681 break;
682 }
683
684 if (val[0] == "channel") {
685 config.radio.channel = val[1];
686 continue;
687 }
688
developere92ee3c2023-10-25 17:01:28 +0800689 if (val[0] == "#num_global_macaddr" ||
690 val[0] == "mbssid")
691 config[val[0]] = int(val[1]);
692
developer66e89bc2024-04-23 14:50:01 +0800693 if (val[0] == "#single_hw")
694 config["single_hw"] = int(val[1]);
695
developerf0fd7052023-08-14 20:23:42 +0800696 push(config.radio.data, line);
697 }
698
developer70180b02023-11-14 17:01:47 +0800699 while ((line = rtrim(f.read("line"), "\n")) != null) {
developere92ee3c2023-10-25 17:01:28 +0800700 if (line == "#default_macaddr")
701 bss.default_macaddr = true;
702
developerf0fd7052023-08-14 20:23:42 +0800703 let val = split(line, "=", 2);
704 if (!val[0])
705 continue;
706
developere92ee3c2023-10-25 17:01:28 +0800707 if (val[0] == "bssid") {
708 bss.bssid = lc(val[1]);
709 continue;
710 }
711
developer66e89bc2024-04-23 14:50:01 +0800712 if (val[0] == "mld_ap" && int(val[1]) == 1)
713 bss.mld_ap = 1;
714
715 if (val[0] == "mld_primary" && int(val[1]) == 1)
716 bss.mld_primary = 1;
717
developere92ee3c2023-10-25 17:01:28 +0800718 if (val[0] == "nas_identifier")
719 bss.nasid = val[1];
720
developerf0fd7052023-08-14 20:23:42 +0800721 if (val[0] == "bss") {
722 bss = config_add_bss(config, val[1]);
723 continue;
724 }
725
726 if (hostapd.data.file_fields[val[0]])
727 bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
728
729 push(bss.data, line);
730 }
731 f.close();
732
733 return config;
734}
735
developere92ee3c2023-10-25 17:01:28 +0800736function ex_wrap(func) {
737 return (req) => {
738 try {
739 let ret = func(req);
740 return ret;
741 } catch(e) {
742 hostapd.printf(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}`);
743 }
744 return libubus.STATUS_UNKNOWN_ERROR;
745 };
746}
developerf0fd7052023-08-14 20:23:42 +0800747
748let main_obj = {
749 reload: {
750 args: {
751 phy: "",
developere92ee3c2023-10-25 17:01:28 +0800752 },
753 call: ex_wrap(function(req) {
754 let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
755 for (let phy_name in phy_list) {
756 let phy = hostapd.data.config[phy_name];
757 let config = iface_load_config(phy.orig_file);
758 iface_set_config(phy_name, config);
759 }
760
761 return 0;
762 })
763 },
764 apsta_state: {
765 args: {
766 phy: "",
767 up: true,
768 frequency: 0,
769 sec_chan_offset: 0,
developerab5e7752023-12-21 16:18:59 +0800770 ch_width: -1,
771 bw320_offset: 1,
developer66e89bc2024-04-23 14:50:01 +0800772 band_idx: 0,
developere92ee3c2023-10-25 17:01:28 +0800773 csa: true,
774 csa_count: 0,
developerf0fd7052023-08-14 20:23:42 +0800775 },
developere92ee3c2023-10-25 17:01:28 +0800776 call: ex_wrap(function(req) {
777 if (req.args.up == null || !req.args.phy)
developerf0fd7052023-08-14 20:23:42 +0800778 return libubus.STATUS_INVALID_ARGUMENT;
developere92ee3c2023-10-25 17:01:28 +0800779
developerab5e7752023-12-21 16:18:59 +0800780 hostapd.printf(`ucode: mtk: apsta state update`);
781 hostapd.printf(` * phy: ${req.args.phy}`);
782 hostapd.printf(` * up: ${req.args.up}`);
783 hostapd.printf(` * freqeuncy: ${req.args.frequency}`);
784 hostapd.printf(` * sec_chan_offset: ${req.args.sec_chan_offset}`);
785 hostapd.printf(` * ch_width: ${req.args.ch_width}`);
786 hostapd.printf(` * bw320_offset: ${req.args.bw320_offset}`);
developer66e89bc2024-04-23 14:50:01 +0800787 hostapd.printf(` * band_idx: ${req.args.band_idx}`);
developerab5e7752023-12-21 16:18:59 +0800788 hostapd.printf(` * csa: ${req.args.csa}`);
789
developere92ee3c2023-10-25 17:01:28 +0800790 let phy = req.args.phy;
791 let config = hostapd.data.config[phy];
792 if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
793 return 0;
794
795 let iface = hostapd.interfaces[phy];
796 if (!iface)
797 return 0;
798
799 if (!req.args.up) {
800 iface.stop();
801 return 0;
802 }
803
804 if (!req.args.frequency)
805 return libubus.STATUS_INVALID_ARGUMENT;
806
807 let freq_info = iface_freq_info(iface, config, req.args);
808 if (!freq_info)
809 return libubus.STATUS_UNKNOWN_ERROR;
810
811 let ret;
812 if (req.args.csa) {
813 freq_info.csa_count = req.args.csa_count ?? 10;
814 ret = iface.switch_channel(freq_info);
815 } else {
816 ret = iface.start(freq_info);
developerf0fd7052023-08-14 20:23:42 +0800817 }
developere92ee3c2023-10-25 17:01:28 +0800818 if (!ret)
819 return libubus.STATUS_UNKNOWN_ERROR;
developerf0fd7052023-08-14 20:23:42 +0800820
821 return 0;
developere92ee3c2023-10-25 17:01:28 +0800822 })
823 },
824 config_get_macaddr_list: {
825 args: {
826 phy: ""
827 },
828 call: ex_wrap(function(req) {
829 let phy = req.args.phy;
830 if (!phy)
831 return libubus.STATUS_INVALID_ARGUMENT;
832
833 let ret = {
834 macaddr: [],
835 };
836
837 let config = hostapd.data.config[phy];
838 if (!config)
839 return ret;
840
841 ret.macaddr = map(config.bss, (bss) => bss.bssid);
842 return ret;
843 })
developerf0fd7052023-08-14 20:23:42 +0800844 },
845 config_set: {
846 args: {
847 phy: "",
848 config: "",
849 prev_config: "",
850 },
developere92ee3c2023-10-25 17:01:28 +0800851 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800852 let phy = req.args.phy;
853 let file = req.args.config;
854 let prev_file = req.args.prev_config;
855
856 if (!phy)
857 return libubus.STATUS_INVALID_ARGUMENT;
858
developere92ee3c2023-10-25 17:01:28 +0800859 if (prev_file && !hostapd.data.config[phy]) {
860 let config = iface_load_config(prev_file);
861 if (config)
862 config.radio.data = [];
863 hostapd.data.config[phy] = config;
864 }
developerf0fd7052023-08-14 20:23:42 +0800865
developere92ee3c2023-10-25 17:01:28 +0800866 let config = iface_load_config(file);
developerf0fd7052023-08-14 20:23:42 +0800867
developere92ee3c2023-10-25 17:01:28 +0800868 hostapd.printf(`Set new config for phy ${phy}: ${file}`);
869 iface_set_config(phy, config);
developerf0fd7052023-08-14 20:23:42 +0800870
871 return {
872 pid: hostapd.getpid()
873 };
developere92ee3c2023-10-25 17:01:28 +0800874 })
developerf0fd7052023-08-14 20:23:42 +0800875 },
876 config_add: {
877 args: {
878 iface: "",
879 config: "",
880 },
developere92ee3c2023-10-25 17:01:28 +0800881 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800882 if (!req.args.iface || !req.args.config)
883 return libubus.STATUS_INVALID_ARGUMENT;
884
885 if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
886 return libubus.STATUS_INVALID_ARGUMENT;
887
888 return {
889 pid: hostapd.getpid()
890 };
developere92ee3c2023-10-25 17:01:28 +0800891 })
developerf0fd7052023-08-14 20:23:42 +0800892 },
893 config_remove: {
894 args: {
895 iface: ""
896 },
developere92ee3c2023-10-25 17:01:28 +0800897 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800898 if (!req.args.iface)
899 return libubus.STATUS_INVALID_ARGUMENT;
900
901 hostapd.remove_iface(req.args.iface);
902 return 0;
developere92ee3c2023-10-25 17:01:28 +0800903 })
developerf0fd7052023-08-14 20:23:42 +0800904 },
905};
906
907hostapd.data.ubus = ubus;
908hostapd.data.obj = ubus.publish("hostapd", main_obj);
developerab5e7752023-12-21 16:18:59 +0800909hostapd.udebug_set("hostapd", hostapd.data.ubus);
developerf0fd7052023-08-14 20:23:42 +0800910
911function bss_event(type, name, data) {
912 let ubus = hostapd.data.ubus;
913
914 data ??= {};
915 data.name = name;
916 hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
917 ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
918}
919
920return {
921 shutdown: function() {
922 for (let phy in hostapd.data.config)
923 iface_set_config(phy, null);
developerab5e7752023-12-21 16:18:59 +0800924 hostapd.udebug_set(null);
developerf0fd7052023-08-14 20:23:42 +0800925 hostapd.ubus.disconnect();
926 },
927 bss_add: function(name, obj) {
928 bss_event("add", name);
929 },
930 bss_reload: function(name, obj, reconf) {
931 bss_event("reload", name, { reconf: reconf != 0 });
932 },
933 bss_remove: function(name, obj) {
934 bss_event("remove", name);
935 }
936};