blob: 72209ea900ed88e1495151f4d65e94dbda83806d [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)
31 wdev_remove(bss.ifname);
32}
33
developere92ee3c2023-10-25 17:01:28 +080034function iface_gen_config(phy, config, start_disabled)
developerf0fd7052023-08-14 20:23:42 +080035{
36 let str = `data:
37${join("\n", config.radio.data)}
38channel=${config.radio.channel}
39`;
40
41 for (let i = 0; i < length(config.bss); i++) {
42 let bss = config.bss[i];
43 let type = i > 0 ? "bss" : "interface";
developere92ee3c2023-10-25 17:01:28 +080044 let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
developerf0fd7052023-08-14 20:23:42 +080045
46 str += `
47${type}=${bss.ifname}
developere92ee3c2023-10-25 17:01:28 +080048bssid=${bss.bssid}
developerf0fd7052023-08-14 20:23:42 +080049${join("\n", bss.data)}
developere92ee3c2023-10-25 17:01:28 +080050nas_identifier=${nasid}
51`;
52 if (start_disabled)
53 str += `
54start_disabled=1
developerf0fd7052023-08-14 20:23:42 +080055`;
56 }
57
58 return str;
59}
60
developere92ee3c2023-10-25 17:01:28 +080061function iface_freq_info(iface, config, params)
62{
63 let freq = params.frequency;
developerab5e7752023-12-21 16:18:59 +080064 let bw320_offset = params.bw320_offset;
developere92ee3c2023-10-25 17:01:28 +080065 if (!freq)
66 return null;
67
68 let sec_offset = params.sec_chan_offset;
69 if (sec_offset != -1 && sec_offset != 1)
70 sec_offset = 0;
71
72 let width = 0;
developerab5e7752023-12-21 16:18:59 +080073 if (params.ch_width >= 0){
74 width = params.ch_width;
75 } else {
76 for (let line in config.radio.data) {
77 if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
78 sec_offset = null; // auto-detect
79 continue;
80 }
developere92ee3c2023-10-25 17:01:28 +080081
developerab5e7752023-12-21 16:18:59 +080082 let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth|eht_oper_chwidth)=(\d+)/);
83 if (!val)
84 continue;
developere92ee3c2023-10-25 17:01:28 +080085
developerab5e7752023-12-21 16:18:59 +080086 val = int(val[2]);
87 if (val > width)
88 width = val;
89 }
developere92ee3c2023-10-25 17:01:28 +080090 }
91
92 if (freq < 4000)
93 width = 0;
94
developerab5e7752023-12-21 16:18:59 +080095 return hostapd.freq_info(freq, sec_offset, width, bw320_offset);
developere92ee3c2023-10-25 17:01:28 +080096}
97
98function iface_add(phy, config, phy_status)
99{
100 let config_inline = iface_gen_config(phy, config, !!phy_status);
101
102 let bss = config.bss[0];
103 let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
104 if (ret < 0)
105 return false;
106
107 if (!phy_status)
108 return true;
109
110 let iface = hostapd.interfaces[phy];
111 if (!iface)
112 return false;
113
114 let freq_info = iface_freq_info(iface, config, phy_status);
115
116 return iface.start(freq_info) >= 0;
117}
118
119function iface_config_macaddr_list(config)
developerf0fd7052023-08-14 20:23:42 +0800120{
developere92ee3c2023-10-25 17:01:28 +0800121 let macaddr_list = {};
122 for (let i = 0; i < length(config.bss); i++) {
123 let bss = config.bss[i];
124 if (!bss.default_macaddr)
125 macaddr_list[bss.bssid] = i;
126 }
127
128 return macaddr_list;
129}
130
131function iface_update_supplicant_macaddr(phy, config)
132{
133 let macaddr_list = [];
134 for (let i = 0; i < length(config.bss); i++)
135 push(macaddr_list, config.bss[i].bssid);
developer753619c2024-02-22 13:42:45 +0800136 ubus.defer("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
developere92ee3c2023-10-25 17:01:28 +0800137}
138
developer753619c2024-02-22 13:42:45 +0800139function __iface_pending_next(pending, state, ret, data)
140{
141 let config = pending.config;
142 let phydev = pending.phydev;
143 let phy = pending.phy;
144 let bss = config.bss[0];
145
146 if (pending.defer)
147 pending.defer.abort();
148 delete pending.defer;
149 switch (state) {
150 case "init":
151 let macaddr_list = [];
152 for (let i = 0; i < length(config.bss); i++)
153 push(macaddr_list, config.bss[i].bssid);
154 pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
155 return "create_bss";
156 case "create_bss":
157 let err = wdev_create(phy, bss.ifname, { mode: "ap" });
158 if (err) {
159 hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
160 return null;
161 }
162
163 pending.call("wpa_supplicant", "phy_status", { phy: phy });
164 return "check_phy";
165 case "check_phy":
166 let phy_status = data;
167 if (phy_status && phy_status.state == "COMPLETED") {
168 if (iface_add(phy, config, phy_status))
169 return "done";
170
171 hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
172 }
173 pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
174 return "wpas_stopped";
175 case "wpas_stopped":
176 if (!iface_add(phy, config))
177 hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
178 pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
179 return null;
180 case "done":
181 default:
182 delete hostapd.data.pending_config[phy];
183 break;
184 }
185}
186
187function iface_pending_next(ret, data)
188{
189 let pending = true;
190 let cfg = this;
191
192 while (pending) {
193 this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
194 if (!this.next_state) {
195 __iface_pending_next(cfg, "done");
196 return;
197 }
198 pending = !this.defer;
199 }
200}
201
202function iface_pending_abort()
203{
204 this.next_state = "done";
205 this.next();
206}
207
208function iface_pending_ubus_call(obj, method, arg)
209{
210 let ubus = hostapd.data.ubus;
211 let pending = this;
212 this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) });
213}
214
215const iface_pending_proto = {
216 next: iface_pending_next,
217 call: iface_pending_ubus_call,
218 abort: iface_pending_abort,
219};
220
221function iface_pending_init(phydev, config)
222{
223 let phy = phydev.name;
224
225 let pending = proto({
226 next_state: "init",
227 phydev: phydev,
228 phy: phy,
229 config: config,
230 next: iface_pending_next,
231 }, iface_pending_proto);
232
233 hostapd.data.pending_config[phy] = pending;
234 pending.next();
235}
236
developere92ee3c2023-10-25 17:01:28 +0800237function iface_restart(phydev, config, old_config)
238{
239 let phy = phydev.name;
developer753619c2024-02-22 13:42:45 +0800240 let pending = hostapd.data.pending_config[phy];
241
242 if (pending)
243 pending.abort();
developere92ee3c2023-10-25 17:01:28 +0800244
245 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800246 iface_remove(old_config);
247 iface_remove(config);
248
249 if (!config.bss || !config.bss[0]) {
250 hostapd.printf(`No bss for phy ${phy}`);
251 return;
252 }
253
developere92ee3c2023-10-25 17:01:28 +0800254 phydev.macaddr_init(iface_config_macaddr_list(config));
255 for (let i = 0; i < length(config.bss); i++) {
256 let bss = config.bss[i];
257 if (bss.default_macaddr)
258 bss.bssid = phydev.macaddr_next();
259 }
260
developer753619c2024-02-22 13:42:45 +0800261 iface_pending_init(phydev, config);
developerf0fd7052023-08-14 20:23:42 +0800262}
263
264function array_to_obj(arr, key, start)
265{
266 let obj = {};
267
268 start ??= 0;
269 for (let i = start; i < length(arr); i++) {
270 let cur = arr[i];
271 obj[cur[key]] = cur;
272 }
273
274 return obj;
275}
276
277function find_array_idx(arr, key, val)
278{
279 for (let i = 0; i < length(arr); i++)
280 if (arr[i][key] == val)
281 return i;
282
283 return -1;
284}
285
286function bss_reload_psk(bss, config, old_config)
287{
288 if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
289 return;
290
291 old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
292 if (!is_equal(old_config, config))
293 return;
294
295 let ret = bss.ctrl("RELOAD_WPA_PSK");
296 ret ??= "failed";
297
298 hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
299}
300
developere92ee3c2023-10-25 17:01:28 +0800301function remove_file_fields(config)
developerf0fd7052023-08-14 20:23:42 +0800302{
developere92ee3c2023-10-25 17:01:28 +0800303 return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
304}
305
306function bss_remove_file_fields(config)
307{
308 let new_cfg = {};
309
310 for (let key in config)
311 new_cfg[key] = config[key];
312 new_cfg.data = remove_file_fields(new_cfg.data);
313 new_cfg.hash = {};
314 for (let key in config.hash)
315 new_cfg.hash[key] = config.hash[key];
316 delete new_cfg.hash.wpa_psk_file;
317 delete new_cfg.hash.vlan_file;
318
319 return new_cfg;
320}
321
322function bss_config_hash(config)
323{
324 return hostapd.sha1(remove_file_fields(config) + "");
325}
326
327function bss_find_existing(config, prev_config, prev_hash)
328{
329 let hash = bss_config_hash(config.data);
330
331 for (let i = 0; i < length(prev_config.bss); i++) {
332 if (!prev_hash[i] || hash != prev_hash[i])
333 continue;
334
335 prev_hash[i] = null;
336 return i;
337 }
338
339 return -1;
340}
341
342function get_config_bss(config, idx)
343{
344 if (!config.bss[idx]) {
345 hostapd.printf(`Invalid bss index ${idx}`);
346 return null;
347 }
348
349 let ifname = config.bss[idx].ifname;
350 if (!ifname)
351 hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
352
353 return hostapd.bss[ifname];
354}
355
356function iface_reload_config(phydev, config, old_config)
357{
358 let phy = phydev.name;
359
developerf0fd7052023-08-14 20:23:42 +0800360 if (!old_config || !is_equal(old_config.radio, config.radio))
361 return false;
362
363 if (is_equal(old_config.bss, config.bss))
364 return true;
365
developer753619c2024-02-22 13:42:45 +0800366 if (hostapd.data.pending_config[phy])
367 return false;
368
developerf0fd7052023-08-14 20:23:42 +0800369 if (!old_config.bss || !old_config.bss[0])
370 return false;
371
developere92ee3c2023-10-25 17:01:28 +0800372 let iface = hostapd.interfaces[phy];
developerab5e7752023-12-21 16:18:59 +0800373 let iface_name = old_config.bss[0].ifname;
developere92ee3c2023-10-25 17:01:28 +0800374 if (!iface) {
375 hostapd.printf(`Could not find previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800376 return false;
developere92ee3c2023-10-25 17:01:28 +0800377 }
developerf0fd7052023-08-14 20:23:42 +0800378
developere92ee3c2023-10-25 17:01:28 +0800379 let first_bss = hostapd.bss[iface_name];
380 if (!first_bss) {
381 hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800382 return false;
developere92ee3c2023-10-25 17:01:28 +0800383 }
developerf0fd7052023-08-14 20:23:42 +0800384
developere92ee3c2023-10-25 17:01:28 +0800385 let macaddr_list = iface_config_macaddr_list(config);
386 let bss_list = [];
387 let bss_list_cfg = [];
388 let prev_bss_hash = [];
developerf0fd7052023-08-14 20:23:42 +0800389
developere92ee3c2023-10-25 17:01:28 +0800390 for (let bss in old_config.bss) {
391 let hash = bss_config_hash(bss.data);
392 push(prev_bss_hash, bss_config_hash(bss.data));
393 }
developerf0fd7052023-08-14 20:23:42 +0800394
developere92ee3c2023-10-25 17:01:28 +0800395 // Step 1: find (possibly renamed) interfaces with the same config
396 // and store them in the new order (with gaps)
397 for (let i = 0; i < length(config.bss); i++) {
398 let prev;
399
400 // For fullmac devices, the first interface needs to be preserved,
401 // since it's treated as the master
402 if (!i && phy_is_fullmac(phy)) {
403 prev = 0;
404 prev_bss_hash[0] = null;
405 } else {
406 prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
407 }
408 if (prev < 0)
409 continue;
410
411 let cur_config = config.bss[i];
412 let prev_config = old_config.bss[prev];
413
414 let prev_bss = get_config_bss(old_config, prev);
415 if (!prev_bss)
developerf0fd7052023-08-14 20:23:42 +0800416 return false;
developere92ee3c2023-10-25 17:01:28 +0800417
418 // try to preserve MAC address of this BSS by reassigning another
419 // BSS if necessary
420 if (cur_config.default_macaddr &&
421 !macaddr_list[prev_config.bssid]) {
422 macaddr_list[prev_config.bssid] = i;
423 cur_config.bssid = prev_config.bssid;
developerf0fd7052023-08-14 20:23:42 +0800424 }
developere92ee3c2023-10-25 17:01:28 +0800425
426 bss_list[i] = prev_bss;
427 bss_list_cfg[i] = old_config.bss[prev];
developerf0fd7052023-08-14 20:23:42 +0800428 }
429
developere92ee3c2023-10-25 17:01:28 +0800430 if (config.mbssid && !bss_list_cfg[0]) {
431 hostapd.printf("First BSS changed with MBSSID enabled");
432 return false;
433 }
developerf0fd7052023-08-14 20:23:42 +0800434
developere92ee3c2023-10-25 17:01:28 +0800435 // Step 2: if none were found, rename and preserve the first one
436 if (length(bss_list) == 0) {
437 // can't change the bssid of the first bss
438 if (config.bss[0].bssid != old_config.bss[0].bssid) {
439 if (!config.bss[0].default_macaddr) {
440 hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
441 return false;
442 }
443
444 config.bss[0].bssid = old_config.bss[0].bssid;
developerf0fd7052023-08-14 20:23:42 +0800445 }
446
developere92ee3c2023-10-25 17:01:28 +0800447 let prev_bss = get_config_bss(old_config, 0);
448 if (!prev_bss)
449 return false;
450
451 macaddr_list[config.bss[0].bssid] = 0;
452 bss_list[0] = prev_bss;
453 bss_list_cfg[0] = old_config.bss[0];
454 prev_bss_hash[0] = null;
455 }
456
457 // Step 3: delete all unused old interfaces
458 for (let i = 0; i < length(prev_bss_hash); i++) {
459 if (!prev_bss_hash[i])
developerf0fd7052023-08-14 20:23:42 +0800460 continue;
developerf0fd7052023-08-14 20:23:42 +0800461
developere92ee3c2023-10-25 17:01:28 +0800462 let prev_bss = get_config_bss(old_config, i);
463 if (!prev_bss)
464 return false;
465
466 let ifname = old_config.bss[i].ifname;
467 hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
468 prev_bss.delete();
469 wdev_remove(ifname);
470 }
developerf0fd7052023-08-14 20:23:42 +0800471
developere92ee3c2023-10-25 17:01:28 +0800472 // Step 4: rename preserved interfaces, use temporary name on duplicates
473 let rename_list = [];
474 for (let i = 0; i < length(bss_list); i++) {
475 if (!bss_list[i])
developerf0fd7052023-08-14 20:23:42 +0800476 continue;
477
developere92ee3c2023-10-25 17:01:28 +0800478 let old_ifname = bss_list_cfg[i].ifname;
479 let new_ifname = config.bss[i].ifname;
480 if (old_ifname == new_ifname)
481 continue;
482
483 if (hostapd.bss[new_ifname]) {
484 new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
485 push(rename_list, i);
486 }
487
488 hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
489 if (!bss_list[i].rename(new_ifname)) {
490 hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800491 return false;
492 }
493
developere92ee3c2023-10-25 17:01:28 +0800494 bss_list_cfg[i].ifname = new_ifname;
495 }
496
497 // Step 5: rename interfaces with temporary names
498 for (let i in rename_list) {
499 let new_ifname = config.bss[i].ifname;
500 if (!bss_list[i].rename(new_ifname)) {
501 hostapd.printf(`Failed to rename bss to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800502 return false;
503 }
developere92ee3c2023-10-25 17:01:28 +0800504 bss_list_cfg[i].ifname = new_ifname;
developerf0fd7052023-08-14 20:23:42 +0800505 }
506
developere92ee3c2023-10-25 17:01:28 +0800507 // Step 6: assign BSSID for newly created interfaces
508 let macaddr_data = {
509 num_global: config.num_global_macaddr ?? 1,
510 mbssid: config.mbssid ?? 0,
511 };
512 macaddr_list = phydev.macaddr_init(macaddr_list, macaddr_data);
513 for (let i = 0; i < length(config.bss); i++) {
514 if (bss_list[i])
515 continue;
516 let bsscfg = config.bss[i];
517
518 let mac_idx = macaddr_list[bsscfg.bssid];
519 if (mac_idx < 0)
520 macaddr_list[bsscfg.bssid] = i;
521 if (mac_idx == i)
522 continue;
523
524 // statically assigned bssid of the new interface is in conflict
525 // with the bssid of a reused interface. reassign the reused interface
526 if (!bsscfg.default_macaddr) {
527 // can't update bssid of the first BSS, need to restart
528 if (!mac_idx < 0)
529 return false;
developerf0fd7052023-08-14 20:23:42 +0800530
developere92ee3c2023-10-25 17:01:28 +0800531 bsscfg = config.bss[mac_idx];
532 }
533
534 let addr = phydev.macaddr_next(i);
535 if (!addr) {
536 hostapd.printf(`Failed to generate mac address for phy ${phy}`);
developerf0fd7052023-08-14 20:23:42 +0800537 return false;
538 }
developere92ee3c2023-10-25 17:01:28 +0800539 bsscfg.bssid = addr;
540 }
developerf0fd7052023-08-14 20:23:42 +0800541
developere92ee3c2023-10-25 17:01:28 +0800542 let config_inline = iface_gen_config(phy, config);
543
544 // Step 7: fill in the gaps with new interfaces
545 for (let i = 0; i < length(config.bss); i++) {
546 let ifname = config.bss[i].ifname;
547 let bss = bss_list[i];
548
549 if (bss)
550 continue;
551
552 hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
553 bss_list[i] = iface.add_bss(config_inline, i);
554 if (!bss_list[i]) {
555 hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
556 return false;
557 }
558 }
559
560 // Step 8: update interface bss order
561 if (!iface.set_bss_order(bss_list)) {
562 hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
563 return false;
564 }
565
566 // Step 9: update config
567 for (let i = 0; i < length(config.bss); i++) {
568 if (!bss_list_cfg[i])
569 continue;
570
571 let ifname = config.bss[i].ifname;
572 let bss = bss_list[i];
573
574 if (is_equal(config.bss[i], bss_list_cfg[i]))
575 continue;
576
577 if (is_equal(bss_remove_file_fields(config.bss[i]),
578 bss_remove_file_fields(bss_list_cfg[i]))) {
579 hostapd.printf(`Update config data files for bss ${ifname}`);
580 if (bss.set_config(config_inline, i, true) < 0) {
581 hostapd.printf(`Could not update config data files for bss ${ifname}`);
582 return false;
583 } else {
584 bss.ctrl("RELOAD_WPA_PSK");
585 continue;
586 }
587 }
588
589 bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
590 if (is_equal(config.bss[i], bss_list_cfg[i]))
591 continue;
592
593 hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
594 if (bss.set_config(config_inline, i) < 0) {
595 hostapd.printf(`Failed to set config for bss ${ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800596 return false;
597 }
598 }
599
600 return true;
601}
602
603function iface_set_config(phy, config)
604{
605 let old_config = hostapd.data.config[phy];
606
607 hostapd.data.config[phy] = config;
608
developere92ee3c2023-10-25 17:01:28 +0800609 if (!config) {
610 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800611 return iface_remove(old_config);
developere92ee3c2023-10-25 17:01:28 +0800612 }
613
614 let phydev = phy_open(phy);
615 if (!phydev) {
616 hostapd.printf(`Failed to open phy ${phy}`);
617 return false;
618 }
developerf0fd7052023-08-14 20:23:42 +0800619
developere92ee3c2023-10-25 17:01:28 +0800620 try {
621 let ret = iface_reload_config(phydev, config, old_config);
622 if (ret) {
623 iface_update_supplicant_macaddr(phy, config);
624 hostapd.printf(`Reloaded settings for phy ${phy}`);
625 return 0;
626 }
627 } catch (e) {
628 hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
developerf0fd7052023-08-14 20:23:42 +0800629 }
630
631 hostapd.printf(`Restart interface for phy ${phy}`);
developere92ee3c2023-10-25 17:01:28 +0800632 let ret = iface_restart(phydev, config, old_config);
633
634 return ret;
developerf0fd7052023-08-14 20:23:42 +0800635}
636
637function config_add_bss(config, name)
638{
639 let bss = {
640 ifname: name,
641 data: [],
642 hash: {}
643 };
644
645 push(config.bss, bss);
646
647 return bss;
648}
649
650function iface_load_config(filename)
651{
652 let f = open(filename, "r");
653 if (!f)
654 return null;
655
656 let config = {
657 radio: {
658 data: []
659 },
660 bss: [],
661 orig_file: filename,
662 };
663
664 let bss;
665 let line;
developer70180b02023-11-14 17:01:47 +0800666 while ((line = rtrim(f.read("line"), "\n")) != null) {
developerf0fd7052023-08-14 20:23:42 +0800667 let val = split(line, "=", 2);
668 if (!val[0])
669 continue;
670
671 if (val[0] == "interface") {
672 bss = config_add_bss(config, val[1]);
673 break;
674 }
675
676 if (val[0] == "channel") {
677 config.radio.channel = val[1];
678 continue;
679 }
680
developere92ee3c2023-10-25 17:01:28 +0800681 if (val[0] == "#num_global_macaddr" ||
682 val[0] == "mbssid")
683 config[val[0]] = int(val[1]);
684
developerf0fd7052023-08-14 20:23:42 +0800685 push(config.radio.data, line);
686 }
687
developer70180b02023-11-14 17:01:47 +0800688 while ((line = rtrim(f.read("line"), "\n")) != null) {
developere92ee3c2023-10-25 17:01:28 +0800689 if (line == "#default_macaddr")
690 bss.default_macaddr = true;
691
developerf0fd7052023-08-14 20:23:42 +0800692 let val = split(line, "=", 2);
693 if (!val[0])
694 continue;
695
developere92ee3c2023-10-25 17:01:28 +0800696 if (val[0] == "bssid") {
697 bss.bssid = lc(val[1]);
698 continue;
699 }
700
701 if (val[0] == "nas_identifier")
702 bss.nasid = val[1];
703
developerf0fd7052023-08-14 20:23:42 +0800704 if (val[0] == "bss") {
705 bss = config_add_bss(config, val[1]);
706 continue;
707 }
708
709 if (hostapd.data.file_fields[val[0]])
710 bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
711
712 push(bss.data, line);
713 }
714 f.close();
715
716 return config;
717}
718
developere92ee3c2023-10-25 17:01:28 +0800719function ex_wrap(func) {
720 return (req) => {
721 try {
722 let ret = func(req);
723 return ret;
724 } catch(e) {
725 hostapd.printf(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}`);
726 }
727 return libubus.STATUS_UNKNOWN_ERROR;
728 };
729}
developerf0fd7052023-08-14 20:23:42 +0800730
731let main_obj = {
732 reload: {
733 args: {
734 phy: "",
developere92ee3c2023-10-25 17:01:28 +0800735 },
736 call: ex_wrap(function(req) {
737 let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
738 for (let phy_name in phy_list) {
739 let phy = hostapd.data.config[phy_name];
740 let config = iface_load_config(phy.orig_file);
741 iface_set_config(phy_name, config);
742 }
743
744 return 0;
745 })
746 },
747 apsta_state: {
748 args: {
749 phy: "",
750 up: true,
751 frequency: 0,
752 sec_chan_offset: 0,
developerab5e7752023-12-21 16:18:59 +0800753 ch_width: -1,
754 bw320_offset: 1,
developere92ee3c2023-10-25 17:01:28 +0800755 csa: true,
756 csa_count: 0,
developerf0fd7052023-08-14 20:23:42 +0800757 },
developere92ee3c2023-10-25 17:01:28 +0800758 call: ex_wrap(function(req) {
759 if (req.args.up == null || !req.args.phy)
developerf0fd7052023-08-14 20:23:42 +0800760 return libubus.STATUS_INVALID_ARGUMENT;
developere92ee3c2023-10-25 17:01:28 +0800761
developerab5e7752023-12-21 16:18:59 +0800762 hostapd.printf(`ucode: mtk: apsta state update`);
763 hostapd.printf(` * phy: ${req.args.phy}`);
764 hostapd.printf(` * up: ${req.args.up}`);
765 hostapd.printf(` * freqeuncy: ${req.args.frequency}`);
766 hostapd.printf(` * sec_chan_offset: ${req.args.sec_chan_offset}`);
767 hostapd.printf(` * ch_width: ${req.args.ch_width}`);
768 hostapd.printf(` * bw320_offset: ${req.args.bw320_offset}`);
769 hostapd.printf(` * csa: ${req.args.csa}`);
770
developere92ee3c2023-10-25 17:01:28 +0800771 let phy = req.args.phy;
772 let config = hostapd.data.config[phy];
773 if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
774 return 0;
775
776 let iface = hostapd.interfaces[phy];
777 if (!iface)
778 return 0;
779
780 if (!req.args.up) {
781 iface.stop();
782 return 0;
783 }
784
785 if (!req.args.frequency)
786 return libubus.STATUS_INVALID_ARGUMENT;
787
788 let freq_info = iface_freq_info(iface, config, req.args);
789 if (!freq_info)
790 return libubus.STATUS_UNKNOWN_ERROR;
791
792 let ret;
793 if (req.args.csa) {
794 freq_info.csa_count = req.args.csa_count ?? 10;
795 ret = iface.switch_channel(freq_info);
796 } else {
797 ret = iface.start(freq_info);
developerf0fd7052023-08-14 20:23:42 +0800798 }
developere92ee3c2023-10-25 17:01:28 +0800799 if (!ret)
800 return libubus.STATUS_UNKNOWN_ERROR;
developerf0fd7052023-08-14 20:23:42 +0800801
802 return 0;
developere92ee3c2023-10-25 17:01:28 +0800803 })
804 },
805 config_get_macaddr_list: {
806 args: {
807 phy: ""
808 },
809 call: ex_wrap(function(req) {
810 let phy = req.args.phy;
811 if (!phy)
812 return libubus.STATUS_INVALID_ARGUMENT;
813
814 let ret = {
815 macaddr: [],
816 };
817
818 let config = hostapd.data.config[phy];
819 if (!config)
820 return ret;
821
822 ret.macaddr = map(config.bss, (bss) => bss.bssid);
823 return ret;
824 })
developerf0fd7052023-08-14 20:23:42 +0800825 },
826 config_set: {
827 args: {
828 phy: "",
829 config: "",
830 prev_config: "",
831 },
developere92ee3c2023-10-25 17:01:28 +0800832 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800833 let phy = req.args.phy;
834 let file = req.args.config;
835 let prev_file = req.args.prev_config;
836
837 if (!phy)
838 return libubus.STATUS_INVALID_ARGUMENT;
839
developere92ee3c2023-10-25 17:01:28 +0800840 if (prev_file && !hostapd.data.config[phy]) {
841 let config = iface_load_config(prev_file);
842 if (config)
843 config.radio.data = [];
844 hostapd.data.config[phy] = config;
845 }
developerf0fd7052023-08-14 20:23:42 +0800846
developere92ee3c2023-10-25 17:01:28 +0800847 let config = iface_load_config(file);
developerf0fd7052023-08-14 20:23:42 +0800848
developere92ee3c2023-10-25 17:01:28 +0800849 hostapd.printf(`Set new config for phy ${phy}: ${file}`);
850 iface_set_config(phy, config);
developerf0fd7052023-08-14 20:23:42 +0800851
852 return {
853 pid: hostapd.getpid()
854 };
developere92ee3c2023-10-25 17:01:28 +0800855 })
developerf0fd7052023-08-14 20:23:42 +0800856 },
857 config_add: {
858 args: {
859 iface: "",
860 config: "",
861 },
developere92ee3c2023-10-25 17:01:28 +0800862 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800863 if (!req.args.iface || !req.args.config)
864 return libubus.STATUS_INVALID_ARGUMENT;
865
866 if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
867 return libubus.STATUS_INVALID_ARGUMENT;
868
869 return {
870 pid: hostapd.getpid()
871 };
developere92ee3c2023-10-25 17:01:28 +0800872 })
developerf0fd7052023-08-14 20:23:42 +0800873 },
874 config_remove: {
875 args: {
876 iface: ""
877 },
developere92ee3c2023-10-25 17:01:28 +0800878 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800879 if (!req.args.iface)
880 return libubus.STATUS_INVALID_ARGUMENT;
881
882 hostapd.remove_iface(req.args.iface);
883 return 0;
developere92ee3c2023-10-25 17:01:28 +0800884 })
developerf0fd7052023-08-14 20:23:42 +0800885 },
886};
887
888hostapd.data.ubus = ubus;
889hostapd.data.obj = ubus.publish("hostapd", main_obj);
developerab5e7752023-12-21 16:18:59 +0800890hostapd.udebug_set("hostapd", hostapd.data.ubus);
developerf0fd7052023-08-14 20:23:42 +0800891
892function bss_event(type, name, data) {
893 let ubus = hostapd.data.ubus;
894
895 data ??= {};
896 data.name = name;
897 hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
898 ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
899}
900
901return {
902 shutdown: function() {
903 for (let phy in hostapd.data.config)
904 iface_set_config(phy, null);
developerab5e7752023-12-21 16:18:59 +0800905 hostapd.udebug_set(null);
developerf0fd7052023-08-14 20:23:42 +0800906 hostapd.ubus.disconnect();
907 },
908 bss_add: function(name, obj) {
909 bss_event("add", name);
910 },
911 bss_reload: function(name, obj, reconf) {
912 bss_event("reload", name, { reconf: reconf != 0 });
913 },
914 bss_remove: function(name, obj) {
915 bss_event("remove", name);
916 }
917};