blob: 1bce754ef2530242b8c9d9038ccf1f72ea3e6281 [file] [log] [blame]
developer2a209692023-08-14 20:23:42 +08001let libubus = require("ubus");
2import { open, readfile } from "fs";
developere6670672023-10-25 17:01:28 +08003import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
developer2a209692023-08-14 20:23:42 +08004
5let ubus = libubus.connect();
6
7hostapd.data.config = {};
8
9hostapd.data.file_fields = {
10 vlan_file: true,
11 wpa_psk_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
24function iface_remove(cfg)
25{
26 if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
27 return;
28
developer2a209692023-08-14 20:23:42 +080029 for (let bss in cfg.bss)
30 wdev_remove(bss.ifname);
31}
32
developere6670672023-10-25 17:01:28 +080033function iface_gen_config(phy, config, start_disabled)
developer2a209692023-08-14 20:23:42 +080034{
35 let str = `data:
36${join("\n", config.radio.data)}
37channel=${config.radio.channel}
38`;
39
40 for (let i = 0; i < length(config.bss); i++) {
41 let bss = config.bss[i];
42 let type = i > 0 ? "bss" : "interface";
developere6670672023-10-25 17:01:28 +080043 let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
developer2a209692023-08-14 20:23:42 +080044
45 str += `
46${type}=${bss.ifname}
developere6670672023-10-25 17:01:28 +080047bssid=${bss.bssid}
developer2a209692023-08-14 20:23:42 +080048${join("\n", bss.data)}
developere6670672023-10-25 17:01:28 +080049nas_identifier=${nasid}
50`;
51 if (start_disabled)
52 str += `
53start_disabled=1
developer2a209692023-08-14 20:23:42 +080054`;
55 }
56
57 return str;
58}
59
developere6670672023-10-25 17:01:28 +080060function iface_freq_info(iface, config, params)
61{
62 let freq = params.frequency;
63 let bw320_offset = params.bw320_offset;
64 if (!freq)
65 return null;
66
67 let sec_offset = params.sec_chan_offset;
68 if (sec_offset != -1 && sec_offset != 1)
69 sec_offset = 0;
70
71 let width = 0;
72 if (params.ch_width >= 0){
73 width = params.ch_width;
74 } else {
75 for (let line in config.radio.data) {
76 if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
77 sec_offset = null; // auto-detect
78 continue;
79 }
80
81 let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth|eht_oper_chwidth)=(\d+)/);
82 if (!val)
83 continue;
84
85 val = int(val[2]);
86 if (val > width)
87 width = val;
88 }
89 }
90
91 if (freq < 4000)
92 width = 0;
93
94 return hostapd.freq_info(freq, sec_offset, width, bw320_offset);
95}
96
97function iface_add(phy, config, phy_status)
98{
99 let config_inline = iface_gen_config(phy, config, !!phy_status);
100
101 let bss = config.bss[0];
102 let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
103 if (ret < 0)
104 return false;
105
106 if (!phy_status)
107 return true;
108
109 let iface = hostapd.interfaces[phy];
110 if (!iface)
111 return false;
112
113 let freq_info = iface_freq_info(iface, config, phy_status);
114
115 return iface.start(freq_info) >= 0;
116}
117
118function iface_config_macaddr_list(config)
developer2a209692023-08-14 20:23:42 +0800119{
developere6670672023-10-25 17:01:28 +0800120 let macaddr_list = {};
121 for (let i = 0; i < length(config.bss); i++) {
122 let bss = config.bss[i];
123 if (!bss.default_macaddr)
124 macaddr_list[bss.bssid] = i;
125 }
126
127 return macaddr_list;
128}
129
130function iface_update_supplicant_macaddr(phy, config)
131{
132 let macaddr_list = [];
133 for (let i = 0; i < length(config.bss); i++)
134 push(macaddr_list, config.bss[i].bssid);
135 ubus.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
136}
137
138function iface_restart(phydev, config, old_config)
139{
140 let phy = phydev.name;
141
142 hostapd.remove_iface(phy);
developer2a209692023-08-14 20:23:42 +0800143 iface_remove(old_config);
144 iface_remove(config);
145
146 if (!config.bss || !config.bss[0]) {
147 hostapd.printf(`No bss for phy ${phy}`);
148 return;
149 }
150
developere6670672023-10-25 17:01:28 +0800151 phydev.macaddr_init(iface_config_macaddr_list(config));
152 for (let i = 0; i < length(config.bss); i++) {
153 let bss = config.bss[i];
154 if (bss.default_macaddr)
155 bss.bssid = phydev.macaddr_next();
156 }
157
158 iface_update_supplicant_macaddr(phy, config);
159
developer2a209692023-08-14 20:23:42 +0800160 let bss = config.bss[0];
161 let err = wdev_create(phy, bss.ifname, { mode: "ap" });
162 if (err)
163 hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
developere6670672023-10-25 17:01:28 +0800164
165 let ubus = hostapd.data.ubus;
166 let phy_status = ubus.call("wpa_supplicant", "phy_status", { phy: phy });
167 if (phy_status && phy_status.state == "COMPLETED") {
168 if (iface_add(phy, config, phy_status))
169 return;
170
171 hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
developer2a209692023-08-14 20:23:42 +0800172 }
developere6670672023-10-25 17:01:28 +0800173
174 ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
175 if (!iface_add(phy, config))
176 hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
177 ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
developer2a209692023-08-14 20:23:42 +0800178}
179
180function array_to_obj(arr, key, start)
181{
182 let obj = {};
183
184 start ??= 0;
185 for (let i = start; i < length(arr); i++) {
186 let cur = arr[i];
187 obj[cur[key]] = cur;
188 }
189
190 return obj;
191}
192
193function find_array_idx(arr, key, val)
194{
195 for (let i = 0; i < length(arr); i++)
196 if (arr[i][key] == val)
197 return i;
198
199 return -1;
200}
201
202function bss_reload_psk(bss, config, old_config)
203{
204 if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
205 return;
206
207 old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
208 if (!is_equal(old_config, config))
209 return;
210
211 let ret = bss.ctrl("RELOAD_WPA_PSK");
212 ret ??= "failed";
213
214 hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
215}
216
developere6670672023-10-25 17:01:28 +0800217function remove_file_fields(config)
developer2a209692023-08-14 20:23:42 +0800218{
developere6670672023-10-25 17:01:28 +0800219 return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
220}
221
222function bss_remove_file_fields(config)
223{
224 let new_cfg = {};
225
226 for (let key in config)
227 new_cfg[key] = config[key];
228 new_cfg.data = remove_file_fields(new_cfg.data);
229 new_cfg.hash = {};
230 for (let key in config.hash)
231 new_cfg.hash[key] = config.hash[key];
232 delete new_cfg.hash.wpa_psk_file;
233 delete new_cfg.hash.vlan_file;
234
235 return new_cfg;
236}
237
238function bss_config_hash(config)
239{
240 return hostapd.sha1(remove_file_fields(config) + "");
241}
242
243function bss_find_existing(config, prev_config, prev_hash)
244{
245 let hash = bss_config_hash(config.data);
246
247 for (let i = 0; i < length(prev_config.bss); i++) {
248 if (!prev_hash[i] || hash != prev_hash[i])
249 continue;
250
251 prev_hash[i] = null;
252 return i;
253 }
254
255 return -1;
256}
257
258function get_config_bss(config, idx)
259{
260 if (!config.bss[idx]) {
261 hostapd.printf(`Invalid bss index ${idx}`);
262 return null;
263 }
264
265 let ifname = config.bss[idx].ifname;
266 if (!ifname)
267 hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
268
269 return hostapd.bss[ifname];
270}
271
272function iface_reload_config(phydev, config, old_config)
273{
274 let phy = phydev.name;
275
developer2a209692023-08-14 20:23:42 +0800276 if (!old_config || !is_equal(old_config.radio, config.radio))
277 return false;
278
279 if (is_equal(old_config.bss, config.bss))
280 return true;
281
282 if (!old_config.bss || !old_config.bss[0])
283 return false;
284
developere6670672023-10-25 17:01:28 +0800285 let iface = hostapd.interfaces[phy];
286 if (!iface) {
287 hostapd.printf(`Could not find previous interface ${iface_name}`);
developer2a209692023-08-14 20:23:42 +0800288 return false;
developere6670672023-10-25 17:01:28 +0800289 }
developer2a209692023-08-14 20:23:42 +0800290
developere6670672023-10-25 17:01:28 +0800291 let iface_name = old_config.bss[0].ifname;
292 let first_bss = hostapd.bss[iface_name];
293 if (!first_bss) {
294 hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
developer2a209692023-08-14 20:23:42 +0800295 return false;
developere6670672023-10-25 17:01:28 +0800296 }
developer2a209692023-08-14 20:23:42 +0800297
developere6670672023-10-25 17:01:28 +0800298 let macaddr_list = iface_config_macaddr_list(config);
299 let bss_list = [];
300 let bss_list_cfg = [];
301 let prev_bss_hash = [];
developer2a209692023-08-14 20:23:42 +0800302
developere6670672023-10-25 17:01:28 +0800303 for (let bss in old_config.bss) {
304 let hash = bss_config_hash(bss.data);
305 push(prev_bss_hash, bss_config_hash(bss.data));
306 }
developer2a209692023-08-14 20:23:42 +0800307
developere6670672023-10-25 17:01:28 +0800308 // Step 1: find (possibly renamed) interfaces with the same config
309 // and store them in the new order (with gaps)
310 for (let i = 0; i < length(config.bss); i++) {
311 let prev;
312
313 // For fullmac devices, the first interface needs to be preserved,
314 // since it's treated as the master
315 if (!i && phy_is_fullmac(phy)) {
316 prev = 0;
317 prev_bss_hash[0] = null;
318 } else {
319 prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
320 }
321 if (prev < 0)
322 continue;
323
324 let cur_config = config.bss[i];
325 let prev_config = old_config.bss[prev];
326
327 let prev_bss = get_config_bss(old_config, prev);
328 if (!prev_bss)
developer2a209692023-08-14 20:23:42 +0800329 return false;
developere6670672023-10-25 17:01:28 +0800330
331 // try to preserve MAC address of this BSS by reassigning another
332 // BSS if necessary
333 if (cur_config.default_macaddr &&
334 !macaddr_list[prev_config.bssid]) {
335 macaddr_list[prev_config.bssid] = i;
336 cur_config.bssid = prev_config.bssid;
developer2a209692023-08-14 20:23:42 +0800337 }
developere6670672023-10-25 17:01:28 +0800338
339 bss_list[i] = prev_bss;
340 bss_list_cfg[i] = old_config.bss[prev];
developer2a209692023-08-14 20:23:42 +0800341 }
342
developere6670672023-10-25 17:01:28 +0800343 if (config.mbssid && !bss_list_cfg[0]) {
344 hostapd.printf("First BSS changed with MBSSID enabled");
345 return false;
346 }
developer2a209692023-08-14 20:23:42 +0800347
developere6670672023-10-25 17:01:28 +0800348 // Step 2: if none were found, rename and preserve the first one
349 if (length(bss_list) == 0) {
350 // can't change the bssid of the first bss
351 if (config.bss[0].bssid != old_config.bss[0].bssid) {
352 if (!config.bss[0].default_macaddr) {
353 hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
354 return false;
355 }
356
357 config.bss[0].bssid = old_config.bss[0].bssid;
developer2a209692023-08-14 20:23:42 +0800358 }
359
developere6670672023-10-25 17:01:28 +0800360 let prev_bss = get_config_bss(old_config, 0);
361 if (!prev_bss)
362 return false;
363
364 macaddr_list[config.bss[0].bssid] = 0;
365 bss_list[0] = prev_bss;
366 bss_list_cfg[0] = old_config.bss[0];
367 prev_bss_hash[0] = null;
368 }
369
370 // Step 3: delete all unused old interfaces
371 for (let i = 0; i < length(prev_bss_hash); i++) {
372 if (!prev_bss_hash[i])
developer2a209692023-08-14 20:23:42 +0800373 continue;
developer2a209692023-08-14 20:23:42 +0800374
developere6670672023-10-25 17:01:28 +0800375 let prev_bss = get_config_bss(old_config, i);
376 if (!prev_bss)
377 return false;
378
379 let ifname = old_config.bss[i].ifname;
380 hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
381 prev_bss.delete();
382 wdev_remove(ifname);
383 }
developer2a209692023-08-14 20:23:42 +0800384
developere6670672023-10-25 17:01:28 +0800385 // Step 4: rename preserved interfaces, use temporary name on duplicates
386 let rename_list = [];
387 for (let i = 0; i < length(bss_list); i++) {
388 if (!bss_list[i])
developer2a209692023-08-14 20:23:42 +0800389 continue;
390
developere6670672023-10-25 17:01:28 +0800391 let old_ifname = bss_list_cfg[i].ifname;
392 let new_ifname = config.bss[i].ifname;
393 if (old_ifname == new_ifname)
394 continue;
395
396 if (hostapd.bss[new_ifname]) {
397 new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
398 push(rename_list, i);
399 }
400
401 hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
402 if (!bss_list[i].rename(new_ifname)) {
403 hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
developer2a209692023-08-14 20:23:42 +0800404 return false;
405 }
406
developere6670672023-10-25 17:01:28 +0800407 bss_list_cfg[i].ifname = new_ifname;
408 }
409
410 // Step 5: rename interfaces with temporary names
411 for (let i in rename_list) {
412 let new_ifname = config.bss[i].ifname;
413 if (!bss_list[i].rename(new_ifname)) {
414 hostapd.printf(`Failed to rename bss to ${new_ifname}`);
developer2a209692023-08-14 20:23:42 +0800415 return false;
416 }
developere6670672023-10-25 17:01:28 +0800417 bss_list_cfg[i].ifname = new_ifname;
developer2a209692023-08-14 20:23:42 +0800418 }
419
developere6670672023-10-25 17:01:28 +0800420 // Step 6: assign BSSID for newly created interfaces
421 let macaddr_data = {
422 num_global: config.num_global_macaddr ?? 1,
423 mbssid: config.mbssid ?? 0,
424 };
425 macaddr_list = phydev.macaddr_init(macaddr_list, macaddr_data);
426 for (let i = 0; i < length(config.bss); i++) {
427 if (bss_list[i])
428 continue;
429 let bsscfg = config.bss[i];
430
431 let mac_idx = macaddr_list[bsscfg.bssid];
432 if (mac_idx < 0)
433 macaddr_list[bsscfg.bssid] = i;
434 if (mac_idx == i)
435 continue;
436
437 // statically assigned bssid of the new interface is in conflict
438 // with the bssid of a reused interface. reassign the reused interface
439 if (!bsscfg.default_macaddr) {
440 // can't update bssid of the first BSS, need to restart
441 if (!mac_idx < 0)
442 return false;
developer2a209692023-08-14 20:23:42 +0800443
developere6670672023-10-25 17:01:28 +0800444 bsscfg = config.bss[mac_idx];
445 }
446
447 let addr = phydev.macaddr_next(i);
448 if (!addr) {
449 hostapd.printf(`Failed to generate mac address for phy ${phy}`);
developer2a209692023-08-14 20:23:42 +0800450 return false;
451 }
developere6670672023-10-25 17:01:28 +0800452 bsscfg.bssid = addr;
453 }
developer2a209692023-08-14 20:23:42 +0800454
developere6670672023-10-25 17:01:28 +0800455 let config_inline = iface_gen_config(phy, config);
456
457 // Step 7: fill in the gaps with new interfaces
458 for (let i = 0; i < length(config.bss); i++) {
459 let ifname = config.bss[i].ifname;
460 let bss = bss_list[i];
461
462 if (bss)
463 continue;
464
465 hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
466 bss_list[i] = iface.add_bss(config_inline, i);
467 if (!bss_list[i]) {
468 hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
469 return false;
470 }
471 }
472
473 // Step 8: update interface bss order
474 if (!iface.set_bss_order(bss_list)) {
475 hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
476 return false;
477 }
478
479 // Step 9: update config
480 for (let i = 0; i < length(config.bss); i++) {
481 if (!bss_list_cfg[i])
482 continue;
483
484 let ifname = config.bss[i].ifname;
485 let bss = bss_list[i];
486
487 if (is_equal(config.bss[i], bss_list_cfg[i]))
488 continue;
489
490 if (is_equal(bss_remove_file_fields(config.bss[i]),
491 bss_remove_file_fields(bss_list_cfg[i]))) {
492 hostapd.printf(`Update config data files for bss ${ifname}`);
493 if (bss.set_config(config_inline, i, true) < 0) {
494 hostapd.printf(`Could not update config data files for bss ${ifname}`);
495 return false;
496 } else {
497 bss.ctrl("RELOAD_WPA_PSK");
498 continue;
499 }
500 }
501
502 bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
503 if (is_equal(config.bss[i], bss_list_cfg[i]))
504 continue;
505
506 hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
507 if (bss.set_config(config_inline, i) < 0) {
508 hostapd.printf(`Failed to set config for bss ${ifname}`);
developer2a209692023-08-14 20:23:42 +0800509 return false;
510 }
511 }
512
513 return true;
514}
515
516function iface_set_config(phy, config)
517{
518 let old_config = hostapd.data.config[phy];
519
520 hostapd.data.config[phy] = config;
521
developere6670672023-10-25 17:01:28 +0800522 if (!config) {
523 hostapd.remove_iface(phy);
developer2a209692023-08-14 20:23:42 +0800524 return iface_remove(old_config);
developere6670672023-10-25 17:01:28 +0800525 }
526
527 let phydev = phy_open(phy);
528 if (!phydev) {
529 hostapd.printf(`Failed to open phy ${phy}`);
530 return false;
531 }
developer2a209692023-08-14 20:23:42 +0800532
developere6670672023-10-25 17:01:28 +0800533 try {
534 let ret = iface_reload_config(phydev, config, old_config);
535 if (ret) {
536 iface_update_supplicant_macaddr(phy, config);
537 hostapd.printf(`Reloaded settings for phy ${phy}`);
538 return 0;
539 }
540 } catch (e) {
541 hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
developer2a209692023-08-14 20:23:42 +0800542 }
543
544 hostapd.printf(`Restart interface for phy ${phy}`);
developere6670672023-10-25 17:01:28 +0800545 let ret = iface_restart(phydev, config, old_config);
546
547 return ret;
developer2a209692023-08-14 20:23:42 +0800548}
549
550function config_add_bss(config, name)
551{
552 let bss = {
553 ifname: name,
554 data: [],
555 hash: {}
556 };
557
558 push(config.bss, bss);
559
560 return bss;
561}
562
563function iface_load_config(filename)
564{
565 let f = open(filename, "r");
566 if (!f)
567 return null;
568
569 let config = {
570 radio: {
571 data: []
572 },
573 bss: [],
574 orig_file: filename,
575 };
576
577 let bss;
578 let line;
579 while ((line = trim(f.read("line"))) != null) {
580 let val = split(line, "=", 2);
581 if (!val[0])
582 continue;
583
584 if (val[0] == "interface") {
585 bss = config_add_bss(config, val[1]);
586 break;
587 }
588
589 if (val[0] == "channel") {
590 config.radio.channel = val[1];
591 continue;
592 }
593
developere6670672023-10-25 17:01:28 +0800594 if (val[0] == "#num_global_macaddr" ||
595 val[0] == "mbssid")
596 config[val[0]] = int(val[1]);
597
developer2a209692023-08-14 20:23:42 +0800598 push(config.radio.data, line);
599 }
600
601 while ((line = trim(f.read("line"))) != null) {
developere6670672023-10-25 17:01:28 +0800602 if (line == "#default_macaddr")
603 bss.default_macaddr = true;
604
developer2a209692023-08-14 20:23:42 +0800605 let val = split(line, "=", 2);
606 if (!val[0])
607 continue;
608
developere6670672023-10-25 17:01:28 +0800609 if (val[0] == "bssid") {
610 bss.bssid = lc(val[1]);
611 continue;
612 }
613
614 if (val[0] == "nas_identifier")
615 bss.nasid = val[1];
616
developer2a209692023-08-14 20:23:42 +0800617 if (val[0] == "bss") {
618 bss = config_add_bss(config, val[1]);
619 continue;
620 }
621
622 if (hostapd.data.file_fields[val[0]])
623 bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
624
625 push(bss.data, line);
626 }
627 f.close();
628
629 return config;
630}
631
developere6670672023-10-25 17:01:28 +0800632function ex_wrap(func) {
633 return (req) => {
634 try {
635 let ret = func(req);
636 return ret;
637 } catch(e) {
638 hostapd.printf(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}`);
639 }
640 return libubus.STATUS_UNKNOWN_ERROR;
641 };
642}
developer2a209692023-08-14 20:23:42 +0800643
644let main_obj = {
645 reload: {
646 args: {
647 phy: "",
developere6670672023-10-25 17:01:28 +0800648 },
649 call: ex_wrap(function(req) {
650 let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
651 for (let phy_name in phy_list) {
652 let phy = hostapd.data.config[phy_name];
653 let config = iface_load_config(phy.orig_file);
654 iface_set_config(phy_name, config);
655 }
656
657 return 0;
658 })
659 },
660 apsta_state: {
661 args: {
662 phy: "",
663 up: true,
664 frequency: 0,
665 sec_chan_offset: 0,
666 ch_width: -1,
667 bw320_offset: 1,
668 csa: true,
669 csa_count: 0,
developer2a209692023-08-14 20:23:42 +0800670 },
developere6670672023-10-25 17:01:28 +0800671 call: ex_wrap(function(req) {
672 if (req.args.up == null || !req.args.phy)
developer2a209692023-08-14 20:23:42 +0800673 return libubus.STATUS_INVALID_ARGUMENT;
developere6670672023-10-25 17:01:28 +0800674
675 hostapd.printf(`ucode: mtk: apsta state update`);
676 hostapd.printf(` * phy: ${req.args.phy}`);
677 hostapd.printf(` * up: ${req.args.up}`);
678 hostapd.printf(` * freqeuncy: ${req.args.frequency}`);
679 hostapd.printf(` * sec_chan_offset: ${req.args.sec_chan_offset}`);
680 hostapd.printf(` * ch_width: ${req.args.ch_width}`);
681 hostapd.printf(` * bw320_offset: ${req.args.bw320_offset}`);
682 hostapd.printf(` * csa: ${req.args.csa}`);
683
684 let phy = req.args.phy;
685 let config = hostapd.data.config[phy];
686 if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
687 return 0;
688
689 let iface = hostapd.interfaces[phy];
690 if (!iface)
691 return 0;
692
693 if (!req.args.up) {
694 iface.stop();
695 return 0;
696 }
697
698 if (!req.args.frequency)
699 return libubus.STATUS_INVALID_ARGUMENT;
700
701 let freq_info = iface_freq_info(iface, config, req.args);
702 if (!freq_info)
703 return libubus.STATUS_UNKNOWN_ERROR;
704
705 let ret;
706 if (req.args.csa) {
707 freq_info.csa_count = req.args.csa_count ?? 10;
708 ret = iface.switch_channel(freq_info);
709 } else {
710 ret = iface.start(freq_info);
developer2a209692023-08-14 20:23:42 +0800711 }
developere6670672023-10-25 17:01:28 +0800712 if (!ret)
713 return libubus.STATUS_UNKNOWN_ERROR;
developer2a209692023-08-14 20:23:42 +0800714
715 return 0;
developere6670672023-10-25 17:01:28 +0800716 })
717 },
718 config_get_macaddr_list: {
719 args: {
720 phy: ""
721 },
722 call: ex_wrap(function(req) {
723 let phy = req.args.phy;
724 if (!phy)
725 return libubus.STATUS_INVALID_ARGUMENT;
726
727 let ret = {
728 macaddr: [],
729 };
730
731 let config = hostapd.data.config[phy];
732 if (!config)
733 return ret;
734
735 ret.macaddr = map(config.bss, (bss) => bss.bssid);
736 return ret;
737 })
developer2a209692023-08-14 20:23:42 +0800738 },
739 config_set: {
740 args: {
741 phy: "",
742 config: "",
743 prev_config: "",
744 },
developere6670672023-10-25 17:01:28 +0800745 call: ex_wrap(function(req) {
developer2a209692023-08-14 20:23:42 +0800746 let phy = req.args.phy;
747 let file = req.args.config;
748 let prev_file = req.args.prev_config;
749
750 if (!phy)
751 return libubus.STATUS_INVALID_ARGUMENT;
752
developere6670672023-10-25 17:01:28 +0800753 if (prev_file && !hostapd.data.config[phy]) {
754 let config = iface_load_config(prev_file);
755 if (config)
756 config.radio.data = [];
757 hostapd.data.config[phy] = config;
758 }
developer2a209692023-08-14 20:23:42 +0800759
developere6670672023-10-25 17:01:28 +0800760 let config = iface_load_config(file);
developer2a209692023-08-14 20:23:42 +0800761
developere6670672023-10-25 17:01:28 +0800762 hostapd.printf(`Set new config for phy ${phy}: ${file}`);
763 iface_set_config(phy, config);
developer2a209692023-08-14 20:23:42 +0800764
765 return {
766 pid: hostapd.getpid()
767 };
developere6670672023-10-25 17:01:28 +0800768 })
developer2a209692023-08-14 20:23:42 +0800769 },
770 config_add: {
771 args: {
772 iface: "",
773 config: "",
774 },
developere6670672023-10-25 17:01:28 +0800775 call: ex_wrap(function(req) {
developer2a209692023-08-14 20:23:42 +0800776 if (!req.args.iface || !req.args.config)
777 return libubus.STATUS_INVALID_ARGUMENT;
778
779 if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
780 return libubus.STATUS_INVALID_ARGUMENT;
781
782 return {
783 pid: hostapd.getpid()
784 };
developere6670672023-10-25 17:01:28 +0800785 })
developer2a209692023-08-14 20:23:42 +0800786 },
787 config_remove: {
788 args: {
789 iface: ""
790 },
developere6670672023-10-25 17:01:28 +0800791 call: ex_wrap(function(req) {
developer2a209692023-08-14 20:23:42 +0800792 if (!req.args.iface)
793 return libubus.STATUS_INVALID_ARGUMENT;
794
795 hostapd.remove_iface(req.args.iface);
796 return 0;
developere6670672023-10-25 17:01:28 +0800797 })
developer2a209692023-08-14 20:23:42 +0800798 },
799};
800
801hostapd.data.ubus = ubus;
802hostapd.data.obj = ubus.publish("hostapd", main_obj);
803
804function bss_event(type, name, data) {
805 let ubus = hostapd.data.ubus;
806
807 data ??= {};
808 data.name = name;
809 hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
810 ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
811}
812
813return {
814 shutdown: function() {
815 for (let phy in hostapd.data.config)
816 iface_set_config(phy, null);
817 hostapd.ubus.disconnect();
818 },
819 bss_add: function(name, obj) {
820 bss_event("add", name);
821 },
822 bss_reload: function(name, obj, reconf) {
823 bss_event("reload", name, { reconf: reconf != 0 });
824 },
825 bss_remove: function(name, obj) {
826 bss_event("remove", name);
827 }
828};