blob: 19b1490f3bd186038ae918e0d13def6f5d2759b6 [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;
developer05f3b2b2024-08-19 19:17:34 +080067 let punct_bitmap = params.punct_bitmap;
developere92ee3c2023-10-25 17:01:28 +080068 if (!freq)
69 return null;
70
71 let sec_offset = params.sec_chan_offset;
72 if (sec_offset != -1 && sec_offset != 1)
73 sec_offset = 0;
74
75 let width = 0;
developerab5e7752023-12-21 16:18:59 +080076 if (params.ch_width >= 0){
77 width = params.ch_width;
78 } else {
79 for (let line in config.radio.data) {
80 if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
81 sec_offset = null; // auto-detect
82 continue;
83 }
developere92ee3c2023-10-25 17:01:28 +080084
developerab5e7752023-12-21 16:18:59 +080085 let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth|eht_oper_chwidth)=(\d+)/);
86 if (!val)
87 continue;
developere92ee3c2023-10-25 17:01:28 +080088
developerab5e7752023-12-21 16:18:59 +080089 val = int(val[2]);
90 if (val > width)
91 width = val;
92 }
developere92ee3c2023-10-25 17:01:28 +080093 }
94
95 if (freq < 4000)
96 width = 0;
97
developer05f3b2b2024-08-19 19:17:34 +080098 return hostapd.freq_info(freq, sec_offset, width, bw320_offset, band_idx, punct_bitmap);
developere92ee3c2023-10-25 17:01:28 +080099}
100
101function iface_add(phy, config, phy_status)
102{
103 let config_inline = iface_gen_config(phy, config, !!phy_status);
104
105 let bss = config.bss[0];
106 let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
107 if (ret < 0)
108 return false;
109
110 if (!phy_status)
111 return true;
112
113 let iface = hostapd.interfaces[phy];
114 if (!iface)
115 return false;
116
117 let freq_info = iface_freq_info(iface, config, phy_status);
118
119 return iface.start(freq_info) >= 0;
120}
121
122function iface_config_macaddr_list(config)
developerf0fd7052023-08-14 20:23:42 +0800123{
developere92ee3c2023-10-25 17:01:28 +0800124 let macaddr_list = {};
125 for (let i = 0; i < length(config.bss); i++) {
126 let bss = config.bss[i];
127 if (!bss.default_macaddr)
128 macaddr_list[bss.bssid] = i;
129 }
130
131 return macaddr_list;
132}
133
134function iface_update_supplicant_macaddr(phy, config)
135{
136 let macaddr_list = [];
137 for (let i = 0; i < length(config.bss); i++)
138 push(macaddr_list, config.bss[i].bssid);
developer753619c2024-02-22 13:42:45 +0800139 ubus.defer("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
developere92ee3c2023-10-25 17:01:28 +0800140}
141
developer753619c2024-02-22 13:42:45 +0800142function __iface_pending_next(pending, state, ret, data)
143{
144 let config = pending.config;
145 let phydev = pending.phydev;
146 let phy = pending.phy;
147 let bss = config.bss[0];
148
149 if (pending.defer)
150 pending.defer.abort();
151 delete pending.defer;
152 switch (state) {
153 case "init":
154 let macaddr_list = [];
155 for (let i = 0; i < length(config.bss); i++)
156 push(macaddr_list, config.bss[i].bssid);
157 pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
158 return "create_bss";
159 case "create_bss":
developer66e89bc2024-04-23 14:50:01 +0800160 if (!bss.mld_ap || bss.mld_primary == 1) {
161 let err = wdev_create(config.single_hw == 1 ? "phy0" : phy, bss.ifname, { mode: "ap" });
162 if (err) {
163 hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
164 return null;
165 }
developer753619c2024-02-22 13:42:45 +0800166 }
167
developer66e89bc2024-04-23 14:50:01 +0800168 pending.call("wpa_supplicant", "phy_status", { phy: bss.mld_ap ? "phy0" : phy });
developer753619c2024-02-22 13:42:45 +0800169 return "check_phy";
170 case "check_phy":
171 let phy_status = data;
172 if (phy_status && phy_status.state == "COMPLETED") {
173 if (iface_add(phy, config, phy_status))
174 return "done";
175
176 hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
177 }
developer66e89bc2024-04-23 14:50:01 +0800178 pending.call("wpa_supplicant", "phy_set_state", { phy: bss.mld_ap ? "phy0" : phy, stop: true });
developer753619c2024-02-22 13:42:45 +0800179 return "wpas_stopped";
180 case "wpas_stopped":
181 if (!iface_add(phy, config))
182 hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
developer66e89bc2024-04-23 14:50:01 +0800183 let iface = hostapd.interfaces[phy];
184 if (!bss.mld_ap)
185 pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
186 else if (iface.is_mld_finished())
187 pending.call("wpa_supplicant", "phy_set_state", { phy: "phy0", stop: false });
developer753619c2024-02-22 13:42:45 +0800188 return null;
189 case "done":
190 default:
191 delete hostapd.data.pending_config[phy];
192 break;
193 }
194}
195
196function iface_pending_next(ret, data)
197{
198 let pending = true;
199 let cfg = this;
200
201 while (pending) {
202 this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
203 if (!this.next_state) {
204 __iface_pending_next(cfg, "done");
205 return;
206 }
207 pending = !this.defer;
208 }
209}
210
211function iface_pending_abort()
212{
213 this.next_state = "done";
214 this.next();
215}
216
217function iface_pending_ubus_call(obj, method, arg)
218{
219 let ubus = hostapd.data.ubus;
220 let pending = this;
221 this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) });
222}
223
224const iface_pending_proto = {
225 next: iface_pending_next,
226 call: iface_pending_ubus_call,
227 abort: iface_pending_abort,
228};
229
230function iface_pending_init(phydev, config)
231{
232 let phy = phydev.name;
233
234 let pending = proto({
235 next_state: "init",
236 phydev: phydev,
237 phy: phy,
238 config: config,
239 next: iface_pending_next,
240 }, iface_pending_proto);
241
242 hostapd.data.pending_config[phy] = pending;
243 pending.next();
244}
245
developere92ee3c2023-10-25 17:01:28 +0800246function iface_restart(phydev, config, old_config)
247{
248 let phy = phydev.name;
developer753619c2024-02-22 13:42:45 +0800249 let pending = hostapd.data.pending_config[phy];
250
251 if (pending)
252 pending.abort();
developere92ee3c2023-10-25 17:01:28 +0800253
254 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800255 iface_remove(old_config);
256 iface_remove(config);
257
258 if (!config.bss || !config.bss[0]) {
259 hostapd.printf(`No bss for phy ${phy}`);
260 return;
261 }
262
developere92ee3c2023-10-25 17:01:28 +0800263 phydev.macaddr_init(iface_config_macaddr_list(config));
264 for (let i = 0; i < length(config.bss); i++) {
265 let bss = config.bss[i];
266 if (bss.default_macaddr)
267 bss.bssid = phydev.macaddr_next();
268 }
269
developer753619c2024-02-22 13:42:45 +0800270 iface_pending_init(phydev, config);
developerf0fd7052023-08-14 20:23:42 +0800271}
272
273function array_to_obj(arr, key, start)
274{
275 let obj = {};
276
277 start ??= 0;
278 for (let i = start; i < length(arr); i++) {
279 let cur = arr[i];
280 obj[cur[key]] = cur;
281 }
282
283 return obj;
284}
285
286function find_array_idx(arr, key, val)
287{
288 for (let i = 0; i < length(arr); i++)
289 if (arr[i][key] == val)
290 return i;
291
292 return -1;
293}
294
295function bss_reload_psk(bss, config, old_config)
296{
297 if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
298 return;
299
300 old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
301 if (!is_equal(old_config, config))
302 return;
303
304 let ret = bss.ctrl("RELOAD_WPA_PSK");
305 ret ??= "failed";
306
307 hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
308}
309
developere92ee3c2023-10-25 17:01:28 +0800310function remove_file_fields(config)
developerf0fd7052023-08-14 20:23:42 +0800311{
developere92ee3c2023-10-25 17:01:28 +0800312 return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
313}
314
315function bss_remove_file_fields(config)
316{
317 let new_cfg = {};
318
319 for (let key in config)
320 new_cfg[key] = config[key];
321 new_cfg.data = remove_file_fields(new_cfg.data);
322 new_cfg.hash = {};
323 for (let key in config.hash)
324 new_cfg.hash[key] = config.hash[key];
325 delete new_cfg.hash.wpa_psk_file;
326 delete new_cfg.hash.vlan_file;
327
328 return new_cfg;
329}
330
331function bss_config_hash(config)
332{
333 return hostapd.sha1(remove_file_fields(config) + "");
334}
335
336function bss_find_existing(config, prev_config, prev_hash)
337{
338 let hash = bss_config_hash(config.data);
339
340 for (let i = 0; i < length(prev_config.bss); i++) {
341 if (!prev_hash[i] || hash != prev_hash[i])
342 continue;
343
344 prev_hash[i] = null;
345 return i;
346 }
347
348 return -1;
349}
350
351function get_config_bss(config, idx)
352{
353 if (!config.bss[idx]) {
354 hostapd.printf(`Invalid bss index ${idx}`);
355 return null;
356 }
357
358 let ifname = config.bss[idx].ifname;
359 if (!ifname)
360 hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
361
362 return hostapd.bss[ifname];
363}
364
365function iface_reload_config(phydev, config, old_config)
366{
367 let phy = phydev.name;
368
developerf0fd7052023-08-14 20:23:42 +0800369 if (!old_config || !is_equal(old_config.radio, config.radio))
370 return false;
371
372 if (is_equal(old_config.bss, config.bss))
373 return true;
374
developer753619c2024-02-22 13:42:45 +0800375 if (hostapd.data.pending_config[phy])
376 return false;
377
developerf0fd7052023-08-14 20:23:42 +0800378 if (!old_config.bss || !old_config.bss[0])
379 return false;
380
developere92ee3c2023-10-25 17:01:28 +0800381 let iface = hostapd.interfaces[phy];
developerab5e7752023-12-21 16:18:59 +0800382 let iface_name = old_config.bss[0].ifname;
developere92ee3c2023-10-25 17:01:28 +0800383 if (!iface) {
384 hostapd.printf(`Could not find previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800385 return false;
developere92ee3c2023-10-25 17:01:28 +0800386 }
developerf0fd7052023-08-14 20:23:42 +0800387
developere92ee3c2023-10-25 17:01:28 +0800388 let first_bss = hostapd.bss[iface_name];
389 if (!first_bss) {
390 hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800391 return false;
developere92ee3c2023-10-25 17:01:28 +0800392 }
developerf0fd7052023-08-14 20:23:42 +0800393
developere92ee3c2023-10-25 17:01:28 +0800394 let macaddr_list = iface_config_macaddr_list(config);
395 let bss_list = [];
396 let bss_list_cfg = [];
397 let prev_bss_hash = [];
developerf0fd7052023-08-14 20:23:42 +0800398
developere92ee3c2023-10-25 17:01:28 +0800399 for (let bss in old_config.bss) {
400 let hash = bss_config_hash(bss.data);
401 push(prev_bss_hash, bss_config_hash(bss.data));
402 }
developerf0fd7052023-08-14 20:23:42 +0800403
developere92ee3c2023-10-25 17:01:28 +0800404 // Step 1: find (possibly renamed) interfaces with the same config
405 // and store them in the new order (with gaps)
406 for (let i = 0; i < length(config.bss); i++) {
407 let prev;
408
409 // For fullmac devices, the first interface needs to be preserved,
410 // since it's treated as the master
411 if (!i && phy_is_fullmac(phy)) {
412 prev = 0;
413 prev_bss_hash[0] = null;
414 } else {
415 prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
416 }
417 if (prev < 0)
418 continue;
419
420 let cur_config = config.bss[i];
421 let prev_config = old_config.bss[prev];
422
423 let prev_bss = get_config_bss(old_config, prev);
424 if (!prev_bss)
developerf0fd7052023-08-14 20:23:42 +0800425 return false;
developere92ee3c2023-10-25 17:01:28 +0800426
427 // try to preserve MAC address of this BSS by reassigning another
428 // BSS if necessary
429 if (cur_config.default_macaddr &&
430 !macaddr_list[prev_config.bssid]) {
431 macaddr_list[prev_config.bssid] = i;
432 cur_config.bssid = prev_config.bssid;
developerf0fd7052023-08-14 20:23:42 +0800433 }
developere92ee3c2023-10-25 17:01:28 +0800434
435 bss_list[i] = prev_bss;
436 bss_list_cfg[i] = old_config.bss[prev];
developerf0fd7052023-08-14 20:23:42 +0800437 }
438
developere92ee3c2023-10-25 17:01:28 +0800439 if (config.mbssid && !bss_list_cfg[0]) {
440 hostapd.printf("First BSS changed with MBSSID enabled");
441 return false;
442 }
developerf0fd7052023-08-14 20:23:42 +0800443
developere92ee3c2023-10-25 17:01:28 +0800444 // Step 2: if none were found, rename and preserve the first one
445 if (length(bss_list) == 0) {
446 // can't change the bssid of the first bss
447 if (config.bss[0].bssid != old_config.bss[0].bssid) {
448 if (!config.bss[0].default_macaddr) {
449 hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
450 return false;
451 }
452
453 config.bss[0].bssid = old_config.bss[0].bssid;
developerf0fd7052023-08-14 20:23:42 +0800454 }
455
developere92ee3c2023-10-25 17:01:28 +0800456 let prev_bss = get_config_bss(old_config, 0);
457 if (!prev_bss)
458 return false;
459
460 macaddr_list[config.bss[0].bssid] = 0;
461 bss_list[0] = prev_bss;
462 bss_list_cfg[0] = old_config.bss[0];
463 prev_bss_hash[0] = null;
464 }
465
466 // Step 3: delete all unused old interfaces
467 for (let i = 0; i < length(prev_bss_hash); i++) {
468 if (!prev_bss_hash[i])
developerf0fd7052023-08-14 20:23:42 +0800469 continue;
developerf0fd7052023-08-14 20:23:42 +0800470
developere92ee3c2023-10-25 17:01:28 +0800471 let prev_bss = get_config_bss(old_config, i);
472 if (!prev_bss)
473 return false;
474
475 let ifname = old_config.bss[i].ifname;
476 hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
477 prev_bss.delete();
478 wdev_remove(ifname);
479 }
developerf0fd7052023-08-14 20:23:42 +0800480
developere92ee3c2023-10-25 17:01:28 +0800481 // Step 4: rename preserved interfaces, use temporary name on duplicates
482 let rename_list = [];
483 for (let i = 0; i < length(bss_list); i++) {
484 if (!bss_list[i])
developerf0fd7052023-08-14 20:23:42 +0800485 continue;
486
developere92ee3c2023-10-25 17:01:28 +0800487 let old_ifname = bss_list_cfg[i].ifname;
488 let new_ifname = config.bss[i].ifname;
489 if (old_ifname == new_ifname)
490 continue;
491
492 if (hostapd.bss[new_ifname]) {
493 new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
494 push(rename_list, i);
495 }
496
497 hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
498 if (!bss_list[i].rename(new_ifname)) {
499 hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800500 return false;
501 }
502
developere92ee3c2023-10-25 17:01:28 +0800503 bss_list_cfg[i].ifname = new_ifname;
504 }
505
506 // Step 5: rename interfaces with temporary names
507 for (let i in rename_list) {
508 let new_ifname = config.bss[i].ifname;
509 if (!bss_list[i].rename(new_ifname)) {
510 hostapd.printf(`Failed to rename bss to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800511 return false;
512 }
developere92ee3c2023-10-25 17:01:28 +0800513 bss_list_cfg[i].ifname = new_ifname;
developerf0fd7052023-08-14 20:23:42 +0800514 }
515
developere92ee3c2023-10-25 17:01:28 +0800516 // Step 6: assign BSSID for newly created interfaces
517 let macaddr_data = {
518 num_global: config.num_global_macaddr ?? 1,
519 mbssid: config.mbssid ?? 0,
520 };
521 macaddr_list = phydev.macaddr_init(macaddr_list, macaddr_data);
522 for (let i = 0; i < length(config.bss); i++) {
523 if (bss_list[i])
524 continue;
525 let bsscfg = config.bss[i];
526
527 let mac_idx = macaddr_list[bsscfg.bssid];
528 if (mac_idx < 0)
529 macaddr_list[bsscfg.bssid] = i;
530 if (mac_idx == i)
531 continue;
532
533 // statically assigned bssid of the new interface is in conflict
534 // with the bssid of a reused interface. reassign the reused interface
535 if (!bsscfg.default_macaddr) {
536 // can't update bssid of the first BSS, need to restart
537 if (!mac_idx < 0)
538 return false;
developerf0fd7052023-08-14 20:23:42 +0800539
developere92ee3c2023-10-25 17:01:28 +0800540 bsscfg = config.bss[mac_idx];
541 }
542
543 let addr = phydev.macaddr_next(i);
544 if (!addr) {
545 hostapd.printf(`Failed to generate mac address for phy ${phy}`);
developerf0fd7052023-08-14 20:23:42 +0800546 return false;
547 }
developere92ee3c2023-10-25 17:01:28 +0800548 bsscfg.bssid = addr;
549 }
developerf0fd7052023-08-14 20:23:42 +0800550
developere92ee3c2023-10-25 17:01:28 +0800551 let config_inline = iface_gen_config(phy, config);
552
553 // Step 7: fill in the gaps with new interfaces
554 for (let i = 0; i < length(config.bss); i++) {
555 let ifname = config.bss[i].ifname;
556 let bss = bss_list[i];
557
558 if (bss)
559 continue;
560
561 hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
562 bss_list[i] = iface.add_bss(config_inline, i);
563 if (!bss_list[i]) {
564 hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
565 return false;
566 }
567 }
568
569 // Step 8: update interface bss order
570 if (!iface.set_bss_order(bss_list)) {
571 hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
572 return false;
573 }
574
575 // Step 9: update config
576 for (let i = 0; i < length(config.bss); i++) {
577 if (!bss_list_cfg[i])
578 continue;
579
580 let ifname = config.bss[i].ifname;
581 let bss = bss_list[i];
582
583 if (is_equal(config.bss[i], bss_list_cfg[i]))
584 continue;
585
586 if (is_equal(bss_remove_file_fields(config.bss[i]),
587 bss_remove_file_fields(bss_list_cfg[i]))) {
588 hostapd.printf(`Update config data files for bss ${ifname}`);
589 if (bss.set_config(config_inline, i, true) < 0) {
590 hostapd.printf(`Could not update config data files for bss ${ifname}`);
591 return false;
592 } else {
593 bss.ctrl("RELOAD_WPA_PSK");
594 continue;
595 }
596 }
597
598 bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
599 if (is_equal(config.bss[i], bss_list_cfg[i]))
600 continue;
601
602 hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
603 if (bss.set_config(config_inline, i) < 0) {
604 hostapd.printf(`Failed to set config for bss ${ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800605 return false;
606 }
607 }
608
609 return true;
610}
611
612function iface_set_config(phy, config)
613{
614 let old_config = hostapd.data.config[phy];
615
616 hostapd.data.config[phy] = config;
617
developere92ee3c2023-10-25 17:01:28 +0800618 if (!config) {
619 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800620 return iface_remove(old_config);
developere92ee3c2023-10-25 17:01:28 +0800621 }
622
623 let phydev = phy_open(phy);
624 if (!phydev) {
625 hostapd.printf(`Failed to open phy ${phy}`);
626 return false;
627 }
developerf0fd7052023-08-14 20:23:42 +0800628
developere92ee3c2023-10-25 17:01:28 +0800629 try {
630 let ret = iface_reload_config(phydev, config, old_config);
631 if (ret) {
632 iface_update_supplicant_macaddr(phy, config);
633 hostapd.printf(`Reloaded settings for phy ${phy}`);
634 return 0;
635 }
636 } catch (e) {
637 hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
developerf0fd7052023-08-14 20:23:42 +0800638 }
639
640 hostapd.printf(`Restart interface for phy ${phy}`);
developere92ee3c2023-10-25 17:01:28 +0800641 let ret = iface_restart(phydev, config, old_config);
642
643 return ret;
developerf0fd7052023-08-14 20:23:42 +0800644}
645
646function config_add_bss(config, name)
647{
648 let bss = {
649 ifname: name,
650 data: [],
651 hash: {}
652 };
653
654 push(config.bss, bss);
655
656 return bss;
657}
658
659function iface_load_config(filename)
660{
661 let f = open(filename, "r");
662 if (!f)
663 return null;
664
665 let config = {
666 radio: {
667 data: []
668 },
669 bss: [],
670 orig_file: filename,
671 };
672
673 let bss;
674 let line;
developer70180b02023-11-14 17:01:47 +0800675 while ((line = rtrim(f.read("line"), "\n")) != null) {
developerf0fd7052023-08-14 20:23:42 +0800676 let val = split(line, "=", 2);
677 if (!val[0])
678 continue;
679
680 if (val[0] == "interface") {
681 bss = config_add_bss(config, val[1]);
682 break;
683 }
684
685 if (val[0] == "channel") {
686 config.radio.channel = val[1];
687 continue;
688 }
689
developere92ee3c2023-10-25 17:01:28 +0800690 if (val[0] == "#num_global_macaddr" ||
691 val[0] == "mbssid")
692 config[val[0]] = int(val[1]);
693
developer66e89bc2024-04-23 14:50:01 +0800694 if (val[0] == "#single_hw")
695 config["single_hw"] = int(val[1]);
696
developerf0fd7052023-08-14 20:23:42 +0800697 push(config.radio.data, line);
698 }
699
developer70180b02023-11-14 17:01:47 +0800700 while ((line = rtrim(f.read("line"), "\n")) != null) {
developere92ee3c2023-10-25 17:01:28 +0800701 if (line == "#default_macaddr")
702 bss.default_macaddr = true;
703
developerf0fd7052023-08-14 20:23:42 +0800704 let val = split(line, "=", 2);
705 if (!val[0])
706 continue;
707
developere92ee3c2023-10-25 17:01:28 +0800708 if (val[0] == "bssid") {
709 bss.bssid = lc(val[1]);
710 continue;
711 }
712
developer66e89bc2024-04-23 14:50:01 +0800713 if (val[0] == "mld_ap" && int(val[1]) == 1)
714 bss.mld_ap = 1;
715
716 if (val[0] == "mld_primary" && int(val[1]) == 1)
717 bss.mld_primary = 1;
718
developere92ee3c2023-10-25 17:01:28 +0800719 if (val[0] == "nas_identifier")
720 bss.nasid = val[1];
721
developerf0fd7052023-08-14 20:23:42 +0800722 if (val[0] == "bss") {
723 bss = config_add_bss(config, val[1]);
724 continue;
725 }
726
727 if (hostapd.data.file_fields[val[0]])
728 bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
729
730 push(bss.data, line);
731 }
732 f.close();
733
developer9237f442024-06-14 17:13:04 +0800734 let first_mld_bss = 0;
735 for (first_mld_bss = 0; first_mld_bss < length(config.bss); first_mld_bss++) {
736 if (config.bss[first_mld_bss].mld_ap == 1)
737 break;
738 }
739
740 if (config.bss[0].mld_ap != 1 && first_mld_bss != length(config.bss)) {
741 let tmp_bss = config.bss[0];
742 config.bss[0] = config.bss[first_mld_bss];
743 config.bss[first_mld_bss] = tmp_bss;
744 hostapd.printf(`mtk: ucode: switch bss[${first_mld_bss}] to first`);
745 }
746
developerf0fd7052023-08-14 20:23:42 +0800747 return config;
748}
749
developere92ee3c2023-10-25 17:01:28 +0800750function ex_wrap(func) {
751 return (req) => {
752 try {
753 let ret = func(req);
754 return ret;
755 } catch(e) {
756 hostapd.printf(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}`);
757 }
758 return libubus.STATUS_UNKNOWN_ERROR;
759 };
760}
developerf0fd7052023-08-14 20:23:42 +0800761
762let main_obj = {
763 reload: {
764 args: {
765 phy: "",
developere92ee3c2023-10-25 17:01:28 +0800766 },
767 call: ex_wrap(function(req) {
768 let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
769 for (let phy_name in phy_list) {
770 let phy = hostapd.data.config[phy_name];
771 let config = iface_load_config(phy.orig_file);
772 iface_set_config(phy_name, config);
773 }
774
775 return 0;
776 })
777 },
778 apsta_state: {
779 args: {
780 phy: "",
781 up: true,
782 frequency: 0,
783 sec_chan_offset: 0,
developerab5e7752023-12-21 16:18:59 +0800784 ch_width: -1,
785 bw320_offset: 1,
developer66e89bc2024-04-23 14:50:01 +0800786 band_idx: 0,
developere92ee3c2023-10-25 17:01:28 +0800787 csa: true,
788 csa_count: 0,
developer05f3b2b2024-08-19 19:17:34 +0800789 punct_bitmap: 0,
developerf0fd7052023-08-14 20:23:42 +0800790 },
developere92ee3c2023-10-25 17:01:28 +0800791 call: ex_wrap(function(req) {
792 if (req.args.up == null || !req.args.phy)
developerf0fd7052023-08-14 20:23:42 +0800793 return libubus.STATUS_INVALID_ARGUMENT;
developere92ee3c2023-10-25 17:01:28 +0800794
developerab5e7752023-12-21 16:18:59 +0800795 hostapd.printf(`ucode: mtk: apsta state update`);
796 hostapd.printf(` * phy: ${req.args.phy}`);
797 hostapd.printf(` * up: ${req.args.up}`);
798 hostapd.printf(` * freqeuncy: ${req.args.frequency}`);
799 hostapd.printf(` * sec_chan_offset: ${req.args.sec_chan_offset}`);
800 hostapd.printf(` * ch_width: ${req.args.ch_width}`);
801 hostapd.printf(` * bw320_offset: ${req.args.bw320_offset}`);
developer66e89bc2024-04-23 14:50:01 +0800802 hostapd.printf(` * band_idx: ${req.args.band_idx}`);
developerab5e7752023-12-21 16:18:59 +0800803 hostapd.printf(` * csa: ${req.args.csa}`);
developer05f3b2b2024-08-19 19:17:34 +0800804 hostapd.printf(` * punct_bitmap: ${req.args.punct_bitmap}`);
developerab5e7752023-12-21 16:18:59 +0800805
developere92ee3c2023-10-25 17:01:28 +0800806 let phy = req.args.phy;
807 let config = hostapd.data.config[phy];
808 if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
809 return 0;
810
811 let iface = hostapd.interfaces[phy];
812 if (!iface)
813 return 0;
814
815 if (!req.args.up) {
816 iface.stop();
817 return 0;
818 }
819
820 if (!req.args.frequency)
821 return libubus.STATUS_INVALID_ARGUMENT;
822
823 let freq_info = iface_freq_info(iface, config, req.args);
824 if (!freq_info)
825 return libubus.STATUS_UNKNOWN_ERROR;
826
827 let ret;
828 if (req.args.csa) {
829 freq_info.csa_count = req.args.csa_count ?? 10;
830 ret = iface.switch_channel(freq_info);
831 } else {
832 ret = iface.start(freq_info);
developerf0fd7052023-08-14 20:23:42 +0800833 }
developere92ee3c2023-10-25 17:01:28 +0800834 if (!ret)
835 return libubus.STATUS_UNKNOWN_ERROR;
developerf0fd7052023-08-14 20:23:42 +0800836
837 return 0;
developere92ee3c2023-10-25 17:01:28 +0800838 })
839 },
840 config_get_macaddr_list: {
841 args: {
842 phy: ""
843 },
844 call: ex_wrap(function(req) {
845 let phy = req.args.phy;
846 if (!phy)
847 return libubus.STATUS_INVALID_ARGUMENT;
848
849 let ret = {
850 macaddr: [],
851 };
852
853 let config = hostapd.data.config[phy];
854 if (!config)
855 return ret;
856
857 ret.macaddr = map(config.bss, (bss) => bss.bssid);
858 return ret;
859 })
developerf0fd7052023-08-14 20:23:42 +0800860 },
861 config_set: {
862 args: {
863 phy: "",
864 config: "",
865 prev_config: "",
866 },
developere92ee3c2023-10-25 17:01:28 +0800867 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800868 let phy = req.args.phy;
869 let file = req.args.config;
870 let prev_file = req.args.prev_config;
871
872 if (!phy)
873 return libubus.STATUS_INVALID_ARGUMENT;
874
developere92ee3c2023-10-25 17:01:28 +0800875 if (prev_file && !hostapd.data.config[phy]) {
876 let config = iface_load_config(prev_file);
877 if (config)
878 config.radio.data = [];
879 hostapd.data.config[phy] = config;
880 }
developerf0fd7052023-08-14 20:23:42 +0800881
developere92ee3c2023-10-25 17:01:28 +0800882 let config = iface_load_config(file);
developerf0fd7052023-08-14 20:23:42 +0800883
developere92ee3c2023-10-25 17:01:28 +0800884 hostapd.printf(`Set new config for phy ${phy}: ${file}`);
885 iface_set_config(phy, config);
developerf0fd7052023-08-14 20:23:42 +0800886
887 return {
888 pid: hostapd.getpid()
889 };
developere92ee3c2023-10-25 17:01:28 +0800890 })
developerf0fd7052023-08-14 20:23:42 +0800891 },
892 config_add: {
893 args: {
894 iface: "",
895 config: "",
896 },
developere92ee3c2023-10-25 17:01:28 +0800897 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800898 if (!req.args.iface || !req.args.config)
899 return libubus.STATUS_INVALID_ARGUMENT;
900
901 if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
902 return libubus.STATUS_INVALID_ARGUMENT;
903
904 return {
905 pid: hostapd.getpid()
906 };
developere92ee3c2023-10-25 17:01:28 +0800907 })
developerf0fd7052023-08-14 20:23:42 +0800908 },
909 config_remove: {
910 args: {
911 iface: ""
912 },
developere92ee3c2023-10-25 17:01:28 +0800913 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800914 if (!req.args.iface)
915 return libubus.STATUS_INVALID_ARGUMENT;
916
917 hostapd.remove_iface(req.args.iface);
918 return 0;
developere92ee3c2023-10-25 17:01:28 +0800919 })
developerf0fd7052023-08-14 20:23:42 +0800920 },
921};
922
923hostapd.data.ubus = ubus;
924hostapd.data.obj = ubus.publish("hostapd", main_obj);
developerab5e7752023-12-21 16:18:59 +0800925hostapd.udebug_set("hostapd", hostapd.data.ubus);
developerf0fd7052023-08-14 20:23:42 +0800926
927function bss_event(type, name, data) {
928 let ubus = hostapd.data.ubus;
929
930 data ??= {};
931 data.name = name;
932 hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
933 ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
934}
935
936return {
937 shutdown: function() {
938 for (let phy in hostapd.data.config)
939 iface_set_config(phy, null);
developerab5e7752023-12-21 16:18:59 +0800940 hostapd.udebug_set(null);
developerf0fd7052023-08-14 20:23:42 +0800941 hostapd.ubus.disconnect();
942 },
943 bss_add: function(name, obj) {
944 bss_event("add", name);
945 },
946 bss_reload: function(name, obj, reconf) {
947 bss_event("reload", name, { reconf: reconf != 0 });
948 },
949 bss_remove: function(name, obj) {
950 bss_event("remove", name);
951 }
952};