blob: 6d56655997561efad6dcbd28f09e2d7cc6d819b1 [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
developerd0c89452024-10-11 16:53:27 +080025hostapd.data.iface_fields = {
26 ft_iface: true,
27 upnp_iface: true,
28 snoop_iface: true,
29 bridge: true,
30 iapp_interface: true,
31};
32
developerf0fd7052023-08-14 20:23:42 +080033function iface_remove(cfg)
34{
35 if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
36 return;
37
developerf0fd7052023-08-14 20:23:42 +080038 for (let bss in cfg.bss)
developer66e89bc2024-04-23 14:50:01 +080039 if (!bss.mld_ap || bss.mld_primary == 1)
40 wdev_remove(bss.ifname);
developerf0fd7052023-08-14 20:23:42 +080041}
42
developere92ee3c2023-10-25 17:01:28 +080043function iface_gen_config(phy, config, start_disabled)
developerf0fd7052023-08-14 20:23:42 +080044{
45 let str = `data:
46${join("\n", config.radio.data)}
47channel=${config.radio.channel}
48`;
49
50 for (let i = 0; i < length(config.bss); i++) {
51 let bss = config.bss[i];
52 let type = i > 0 ? "bss" : "interface";
developere92ee3c2023-10-25 17:01:28 +080053 let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
developerf0fd7052023-08-14 20:23:42 +080054
55 str += `
56${type}=${bss.ifname}
developere92ee3c2023-10-25 17:01:28 +080057bssid=${bss.bssid}
developerf0fd7052023-08-14 20:23:42 +080058${join("\n", bss.data)}
developere92ee3c2023-10-25 17:01:28 +080059nas_identifier=${nasid}
60`;
61 if (start_disabled)
62 str += `
63start_disabled=1
developerf0fd7052023-08-14 20:23:42 +080064`;
65 }
66
67 return str;
68}
69
developere92ee3c2023-10-25 17:01:28 +080070function iface_freq_info(iface, config, params)
71{
72 let freq = params.frequency;
developerab5e7752023-12-21 16:18:59 +080073 let bw320_offset = params.bw320_offset;
developer66e89bc2024-04-23 14:50:01 +080074 let band_idx = params.band_idx;
developer05f3b2b2024-08-19 19:17:34 +080075 let punct_bitmap = params.punct_bitmap;
developere92ee3c2023-10-25 17:01:28 +080076 if (!freq)
77 return null;
78
79 let sec_offset = params.sec_chan_offset;
80 if (sec_offset != -1 && sec_offset != 1)
81 sec_offset = 0;
82
83 let width = 0;
developerab5e7752023-12-21 16:18:59 +080084 if (params.ch_width >= 0){
85 width = params.ch_width;
86 } else {
87 for (let line in config.radio.data) {
88 if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
89 sec_offset = null; // auto-detect
90 continue;
91 }
developere92ee3c2023-10-25 17:01:28 +080092
developerab5e7752023-12-21 16:18:59 +080093 let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth|eht_oper_chwidth)=(\d+)/);
94 if (!val)
95 continue;
developere92ee3c2023-10-25 17:01:28 +080096
developerab5e7752023-12-21 16:18:59 +080097 val = int(val[2]);
98 if (val > width)
99 width = val;
100 }
developere92ee3c2023-10-25 17:01:28 +0800101 }
102
103 if (freq < 4000)
104 width = 0;
105
developer05f3b2b2024-08-19 19:17:34 +0800106 return hostapd.freq_info(freq, sec_offset, width, bw320_offset, band_idx, punct_bitmap);
developere92ee3c2023-10-25 17:01:28 +0800107}
108
109function iface_add(phy, config, phy_status)
110{
111 let config_inline = iface_gen_config(phy, config, !!phy_status);
112
113 let bss = config.bss[0];
114 let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
115 if (ret < 0)
116 return false;
117
118 if (!phy_status)
119 return true;
120
121 let iface = hostapd.interfaces[phy];
122 if (!iface)
123 return false;
124
125 let freq_info = iface_freq_info(iface, config, phy_status);
126
127 return iface.start(freq_info) >= 0;
128}
129
130function iface_config_macaddr_list(config)
developerf0fd7052023-08-14 20:23:42 +0800131{
developere92ee3c2023-10-25 17:01:28 +0800132 let macaddr_list = {};
133 for (let i = 0; i < length(config.bss); i++) {
134 let bss = config.bss[i];
135 if (!bss.default_macaddr)
136 macaddr_list[bss.bssid] = i;
137 }
138
139 return macaddr_list;
140}
141
142function iface_update_supplicant_macaddr(phy, config)
143{
144 let macaddr_list = [];
145 for (let i = 0; i < length(config.bss); i++)
146 push(macaddr_list, config.bss[i].bssid);
developer753619c2024-02-22 13:42:45 +0800147 ubus.defer("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
developere92ee3c2023-10-25 17:01:28 +0800148}
149
developer753619c2024-02-22 13:42:45 +0800150function __iface_pending_next(pending, state, ret, data)
151{
152 let config = pending.config;
153 let phydev = pending.phydev;
154 let phy = pending.phy;
155 let bss = config.bss[0];
156
157 if (pending.defer)
158 pending.defer.abort();
159 delete pending.defer;
160 switch (state) {
161 case "init":
162 let macaddr_list = [];
163 for (let i = 0; i < length(config.bss); i++)
164 push(macaddr_list, config.bss[i].bssid);
165 pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
166 return "create_bss";
167 case "create_bss":
developer66e89bc2024-04-23 14:50:01 +0800168 if (!bss.mld_ap || bss.mld_primary == 1) {
169 let err = wdev_create(config.single_hw == 1 ? "phy0" : phy, bss.ifname, { mode: "ap" });
170 if (err) {
171 hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
172 return null;
173 }
developer753619c2024-02-22 13:42:45 +0800174 }
175
developer66e89bc2024-04-23 14:50:01 +0800176 pending.call("wpa_supplicant", "phy_status", { phy: bss.mld_ap ? "phy0" : phy });
developer753619c2024-02-22 13:42:45 +0800177 return "check_phy";
178 case "check_phy":
179 let phy_status = data;
180 if (phy_status && phy_status.state == "COMPLETED") {
181 if (iface_add(phy, config, phy_status))
182 return "done";
183
184 hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
185 }
developer66e89bc2024-04-23 14:50:01 +0800186 pending.call("wpa_supplicant", "phy_set_state", { phy: bss.mld_ap ? "phy0" : phy, stop: true });
developer753619c2024-02-22 13:42:45 +0800187 return "wpas_stopped";
188 case "wpas_stopped":
189 if (!iface_add(phy, config))
190 hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
developer66e89bc2024-04-23 14:50:01 +0800191 let iface = hostapd.interfaces[phy];
192 if (!bss.mld_ap)
193 pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
194 else if (iface.is_mld_finished())
195 pending.call("wpa_supplicant", "phy_set_state", { phy: "phy0", stop: false });
developer753619c2024-02-22 13:42:45 +0800196 return null;
197 case "done":
198 default:
199 delete hostapd.data.pending_config[phy];
200 break;
201 }
202}
203
204function iface_pending_next(ret, data)
205{
206 let pending = true;
207 let cfg = this;
208
209 while (pending) {
210 this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
211 if (!this.next_state) {
212 __iface_pending_next(cfg, "done");
213 return;
214 }
215 pending = !this.defer;
216 }
217}
218
219function iface_pending_abort()
220{
221 this.next_state = "done";
222 this.next();
223}
224
225function iface_pending_ubus_call(obj, method, arg)
226{
227 let ubus = hostapd.data.ubus;
228 let pending = this;
229 this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) });
230}
231
232const iface_pending_proto = {
233 next: iface_pending_next,
234 call: iface_pending_ubus_call,
235 abort: iface_pending_abort,
236};
237
238function iface_pending_init(phydev, config)
239{
240 let phy = phydev.name;
241
242 let pending = proto({
243 next_state: "init",
244 phydev: phydev,
245 phy: phy,
246 config: config,
247 next: iface_pending_next,
248 }, iface_pending_proto);
249
250 hostapd.data.pending_config[phy] = pending;
251 pending.next();
252}
253
developerd0c89452024-10-11 16:53:27 +0800254function iface_macaddr_init(phydev, config, macaddr_list)
255{
256 let macaddr_data = {
257 num_global: config.num_global_macaddr ?? 1,
258 mbssid: config.mbssid ?? 0,
259 };
260
261 return phydev.macaddr_init(macaddr_list, macaddr_data);
262}
263
developere92ee3c2023-10-25 17:01:28 +0800264function iface_restart(phydev, config, old_config)
265{
266 let phy = phydev.name;
developer753619c2024-02-22 13:42:45 +0800267 let pending = hostapd.data.pending_config[phy];
268
269 if (pending)
270 pending.abort();
developere92ee3c2023-10-25 17:01:28 +0800271
272 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800273 iface_remove(old_config);
274 iface_remove(config);
275
276 if (!config.bss || !config.bss[0]) {
277 hostapd.printf(`No bss for phy ${phy}`);
278 return;
279 }
280
developerd0c89452024-10-11 16:53:27 +0800281 iface_macaddr_init(phydev, config, iface_config_macaddr_list(config));
developere92ee3c2023-10-25 17:01:28 +0800282 for (let i = 0; i < length(config.bss); i++) {
283 let bss = config.bss[i];
284 if (bss.default_macaddr)
285 bss.bssid = phydev.macaddr_next();
286 }
287
developer753619c2024-02-22 13:42:45 +0800288 iface_pending_init(phydev, config);
developerf0fd7052023-08-14 20:23:42 +0800289}
290
291function array_to_obj(arr, key, start)
292{
293 let obj = {};
294
295 start ??= 0;
296 for (let i = start; i < length(arr); i++) {
297 let cur = arr[i];
298 obj[cur[key]] = cur;
299 }
300
301 return obj;
302}
303
304function find_array_idx(arr, key, val)
305{
306 for (let i = 0; i < length(arr); i++)
307 if (arr[i][key] == val)
308 return i;
309
310 return -1;
311}
312
313function bss_reload_psk(bss, config, old_config)
314{
315 if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
316 return;
317
318 old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
319 if (!is_equal(old_config, config))
320 return;
321
322 let ret = bss.ctrl("RELOAD_WPA_PSK");
323 ret ??= "failed";
324
325 hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
326}
327
developere92ee3c2023-10-25 17:01:28 +0800328function remove_file_fields(config)
developerf0fd7052023-08-14 20:23:42 +0800329{
developere92ee3c2023-10-25 17:01:28 +0800330 return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
331}
332
333function bss_remove_file_fields(config)
334{
335 let new_cfg = {};
336
337 for (let key in config)
338 new_cfg[key] = config[key];
339 new_cfg.data = remove_file_fields(new_cfg.data);
340 new_cfg.hash = {};
341 for (let key in config.hash)
342 new_cfg.hash[key] = config.hash[key];
343 delete new_cfg.hash.wpa_psk_file;
344 delete new_cfg.hash.vlan_file;
345
346 return new_cfg;
347}
348
developerd0c89452024-10-11 16:53:27 +0800349function bss_ifindex_list(config)
350{
351 config = filter(config, (line) => !!hostapd.data.iface_fields[split(line, "=")[0]]);
352
353 return join(",", map(config, (line) => {
354 try {
355 let file = "/sys/class/net/" + split(line, "=")[1] + "/ifindex";
356 let val = trim(readfile(file));
357 return val;
358 } catch (e) {
359 return "";
360 }
361 }));
362}
363
developere92ee3c2023-10-25 17:01:28 +0800364function bss_config_hash(config)
365{
developerd0c89452024-10-11 16:53:27 +0800366 return hostapd.sha1(remove_file_fields(config) + bss_ifindex_list(config));
developere92ee3c2023-10-25 17:01:28 +0800367}
368
369function bss_find_existing(config, prev_config, prev_hash)
370{
371 let hash = bss_config_hash(config.data);
372
373 for (let i = 0; i < length(prev_config.bss); i++) {
374 if (!prev_hash[i] || hash != prev_hash[i])
375 continue;
376
377 prev_hash[i] = null;
378 return i;
379 }
380
381 return -1;
382}
383
384function get_config_bss(config, idx)
385{
386 if (!config.bss[idx]) {
387 hostapd.printf(`Invalid bss index ${idx}`);
388 return null;
389 }
390
391 let ifname = config.bss[idx].ifname;
392 if (!ifname)
393 hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
394
395 return hostapd.bss[ifname];
396}
397
398function iface_reload_config(phydev, config, old_config)
399{
400 let phy = phydev.name;
401
developerf0fd7052023-08-14 20:23:42 +0800402 if (!old_config || !is_equal(old_config.radio, config.radio))
403 return false;
404
405 if (is_equal(old_config.bss, config.bss))
406 return true;
407
developer753619c2024-02-22 13:42:45 +0800408 if (hostapd.data.pending_config[phy])
409 return false;
410
developerf0fd7052023-08-14 20:23:42 +0800411 if (!old_config.bss || !old_config.bss[0])
412 return false;
413
developere92ee3c2023-10-25 17:01:28 +0800414 let iface = hostapd.interfaces[phy];
developerab5e7752023-12-21 16:18:59 +0800415 let iface_name = old_config.bss[0].ifname;
developere92ee3c2023-10-25 17:01:28 +0800416 if (!iface) {
417 hostapd.printf(`Could not find previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800418 return false;
developere92ee3c2023-10-25 17:01:28 +0800419 }
developerf0fd7052023-08-14 20:23:42 +0800420
developere92ee3c2023-10-25 17:01:28 +0800421 let first_bss = hostapd.bss[iface_name];
422 if (!first_bss) {
423 hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
developerf0fd7052023-08-14 20:23:42 +0800424 return false;
developere92ee3c2023-10-25 17:01:28 +0800425 }
developerf0fd7052023-08-14 20:23:42 +0800426
developere92ee3c2023-10-25 17:01:28 +0800427 let macaddr_list = iface_config_macaddr_list(config);
428 let bss_list = [];
429 let bss_list_cfg = [];
430 let prev_bss_hash = [];
developerf0fd7052023-08-14 20:23:42 +0800431
developere92ee3c2023-10-25 17:01:28 +0800432 for (let bss in old_config.bss) {
433 let hash = bss_config_hash(bss.data);
434 push(prev_bss_hash, bss_config_hash(bss.data));
435 }
developerf0fd7052023-08-14 20:23:42 +0800436
developere92ee3c2023-10-25 17:01:28 +0800437 // Step 1: find (possibly renamed) interfaces with the same config
438 // and store them in the new order (with gaps)
439 for (let i = 0; i < length(config.bss); i++) {
440 let prev;
441
442 // For fullmac devices, the first interface needs to be preserved,
443 // since it's treated as the master
444 if (!i && phy_is_fullmac(phy)) {
445 prev = 0;
446 prev_bss_hash[0] = null;
447 } else {
448 prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
449 }
450 if (prev < 0)
451 continue;
452
453 let cur_config = config.bss[i];
454 let prev_config = old_config.bss[prev];
455
456 let prev_bss = get_config_bss(old_config, prev);
457 if (!prev_bss)
developerf0fd7052023-08-14 20:23:42 +0800458 return false;
developere92ee3c2023-10-25 17:01:28 +0800459
460 // try to preserve MAC address of this BSS by reassigning another
461 // BSS if necessary
462 if (cur_config.default_macaddr &&
463 !macaddr_list[prev_config.bssid]) {
464 macaddr_list[prev_config.bssid] = i;
465 cur_config.bssid = prev_config.bssid;
developerf0fd7052023-08-14 20:23:42 +0800466 }
developere92ee3c2023-10-25 17:01:28 +0800467
468 bss_list[i] = prev_bss;
469 bss_list_cfg[i] = old_config.bss[prev];
developerf0fd7052023-08-14 20:23:42 +0800470 }
471
developere92ee3c2023-10-25 17:01:28 +0800472 if (config.mbssid && !bss_list_cfg[0]) {
473 hostapd.printf("First BSS changed with MBSSID enabled");
474 return false;
475 }
developerf0fd7052023-08-14 20:23:42 +0800476
developere92ee3c2023-10-25 17:01:28 +0800477 // Step 2: if none were found, rename and preserve the first one
478 if (length(bss_list) == 0) {
479 // can't change the bssid of the first bss
480 if (config.bss[0].bssid != old_config.bss[0].bssid) {
481 if (!config.bss[0].default_macaddr) {
482 hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
483 return false;
484 }
485
486 config.bss[0].bssid = old_config.bss[0].bssid;
developerf0fd7052023-08-14 20:23:42 +0800487 }
488
developere92ee3c2023-10-25 17:01:28 +0800489 let prev_bss = get_config_bss(old_config, 0);
490 if (!prev_bss)
491 return false;
492
493 macaddr_list[config.bss[0].bssid] = 0;
494 bss_list[0] = prev_bss;
495 bss_list_cfg[0] = old_config.bss[0];
496 prev_bss_hash[0] = null;
497 }
498
499 // Step 3: delete all unused old interfaces
500 for (let i = 0; i < length(prev_bss_hash); i++) {
501 if (!prev_bss_hash[i])
developerf0fd7052023-08-14 20:23:42 +0800502 continue;
developerf0fd7052023-08-14 20:23:42 +0800503
developere92ee3c2023-10-25 17:01:28 +0800504 let prev_bss = get_config_bss(old_config, i);
505 if (!prev_bss)
506 return false;
507
508 let ifname = old_config.bss[i].ifname;
509 hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
510 prev_bss.delete();
511 wdev_remove(ifname);
512 }
developerf0fd7052023-08-14 20:23:42 +0800513
developere92ee3c2023-10-25 17:01:28 +0800514 // Step 4: rename preserved interfaces, use temporary name on duplicates
515 let rename_list = [];
516 for (let i = 0; i < length(bss_list); i++) {
517 if (!bss_list[i])
developerf0fd7052023-08-14 20:23:42 +0800518 continue;
519
developere92ee3c2023-10-25 17:01:28 +0800520 let old_ifname = bss_list_cfg[i].ifname;
521 let new_ifname = config.bss[i].ifname;
522 if (old_ifname == new_ifname)
523 continue;
524
525 if (hostapd.bss[new_ifname]) {
526 new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
527 push(rename_list, i);
528 }
529
530 hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
531 if (!bss_list[i].rename(new_ifname)) {
532 hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800533 return false;
534 }
535
developere92ee3c2023-10-25 17:01:28 +0800536 bss_list_cfg[i].ifname = new_ifname;
537 }
538
539 // Step 5: rename interfaces with temporary names
540 for (let i in rename_list) {
541 let new_ifname = config.bss[i].ifname;
542 if (!bss_list[i].rename(new_ifname)) {
543 hostapd.printf(`Failed to rename bss to ${new_ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800544 return false;
545 }
developere92ee3c2023-10-25 17:01:28 +0800546 bss_list_cfg[i].ifname = new_ifname;
developerf0fd7052023-08-14 20:23:42 +0800547 }
548
developere92ee3c2023-10-25 17:01:28 +0800549 // Step 6: assign BSSID for newly created interfaces
developerd0c89452024-10-11 16:53:27 +0800550 macaddr_list = iface_macaddr_init(phydev, config, macaddr_list);
developere92ee3c2023-10-25 17:01:28 +0800551 for (let i = 0; i < length(config.bss); i++) {
552 if (bss_list[i])
553 continue;
554 let bsscfg = config.bss[i];
555
556 let mac_idx = macaddr_list[bsscfg.bssid];
557 if (mac_idx < 0)
558 macaddr_list[bsscfg.bssid] = i;
559 if (mac_idx == i)
560 continue;
561
562 // statically assigned bssid of the new interface is in conflict
563 // with the bssid of a reused interface. reassign the reused interface
564 if (!bsscfg.default_macaddr) {
565 // can't update bssid of the first BSS, need to restart
566 if (!mac_idx < 0)
567 return false;
developerf0fd7052023-08-14 20:23:42 +0800568
developere92ee3c2023-10-25 17:01:28 +0800569 bsscfg = config.bss[mac_idx];
570 }
571
572 let addr = phydev.macaddr_next(i);
573 if (!addr) {
574 hostapd.printf(`Failed to generate mac address for phy ${phy}`);
developerf0fd7052023-08-14 20:23:42 +0800575 return false;
576 }
developere92ee3c2023-10-25 17:01:28 +0800577 bsscfg.bssid = addr;
578 }
developerf0fd7052023-08-14 20:23:42 +0800579
developere92ee3c2023-10-25 17:01:28 +0800580 let config_inline = iface_gen_config(phy, config);
581
582 // Step 7: fill in the gaps with new interfaces
583 for (let i = 0; i < length(config.bss); i++) {
584 let ifname = config.bss[i].ifname;
585 let bss = bss_list[i];
586
587 if (bss)
588 continue;
589
590 hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
591 bss_list[i] = iface.add_bss(config_inline, i);
592 if (!bss_list[i]) {
593 hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
594 return false;
595 }
596 }
597
598 // Step 8: update interface bss order
599 if (!iface.set_bss_order(bss_list)) {
600 hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
601 return false;
602 }
603
604 // Step 9: update config
605 for (let i = 0; i < length(config.bss); i++) {
606 if (!bss_list_cfg[i])
607 continue;
608
609 let ifname = config.bss[i].ifname;
610 let bss = bss_list[i];
611
612 if (is_equal(config.bss[i], bss_list_cfg[i]))
613 continue;
614
615 if (is_equal(bss_remove_file_fields(config.bss[i]),
616 bss_remove_file_fields(bss_list_cfg[i]))) {
617 hostapd.printf(`Update config data files for bss ${ifname}`);
618 if (bss.set_config(config_inline, i, true) < 0) {
619 hostapd.printf(`Could not update config data files for bss ${ifname}`);
620 return false;
621 } else {
622 bss.ctrl("RELOAD_WPA_PSK");
623 continue;
624 }
625 }
626
627 bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
628 if (is_equal(config.bss[i], bss_list_cfg[i]))
629 continue;
630
631 hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
632 if (bss.set_config(config_inline, i) < 0) {
633 hostapd.printf(`Failed to set config for bss ${ifname}`);
developerf0fd7052023-08-14 20:23:42 +0800634 return false;
635 }
636 }
637
638 return true;
639}
640
641function iface_set_config(phy, config)
642{
643 let old_config = hostapd.data.config[phy];
644
645 hostapd.data.config[phy] = config;
646
developere92ee3c2023-10-25 17:01:28 +0800647 if (!config) {
648 hostapd.remove_iface(phy);
developerf0fd7052023-08-14 20:23:42 +0800649 return iface_remove(old_config);
developere92ee3c2023-10-25 17:01:28 +0800650 }
651
652 let phydev = phy_open(phy);
653 if (!phydev) {
654 hostapd.printf(`Failed to open phy ${phy}`);
655 return false;
656 }
developerf0fd7052023-08-14 20:23:42 +0800657
developere92ee3c2023-10-25 17:01:28 +0800658 try {
659 let ret = iface_reload_config(phydev, config, old_config);
660 if (ret) {
661 iface_update_supplicant_macaddr(phy, config);
662 hostapd.printf(`Reloaded settings for phy ${phy}`);
663 return 0;
664 }
665 } catch (e) {
666 hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
developerf0fd7052023-08-14 20:23:42 +0800667 }
668
669 hostapd.printf(`Restart interface for phy ${phy}`);
developere92ee3c2023-10-25 17:01:28 +0800670 let ret = iface_restart(phydev, config, old_config);
671
672 return ret;
developerf0fd7052023-08-14 20:23:42 +0800673}
674
675function config_add_bss(config, name)
676{
677 let bss = {
678 ifname: name,
679 data: [],
680 hash: {}
681 };
682
683 push(config.bss, bss);
684
685 return bss;
686}
687
688function iface_load_config(filename)
689{
690 let f = open(filename, "r");
691 if (!f)
692 return null;
693
694 let config = {
695 radio: {
696 data: []
697 },
698 bss: [],
699 orig_file: filename,
700 };
701
702 let bss;
703 let line;
developer70180b02023-11-14 17:01:47 +0800704 while ((line = rtrim(f.read("line"), "\n")) != null) {
developerf0fd7052023-08-14 20:23:42 +0800705 let val = split(line, "=", 2);
706 if (!val[0])
707 continue;
708
709 if (val[0] == "interface") {
710 bss = config_add_bss(config, val[1]);
711 break;
712 }
713
714 if (val[0] == "channel") {
715 config.radio.channel = val[1];
716 continue;
717 }
718
developere92ee3c2023-10-25 17:01:28 +0800719 if (val[0] == "#num_global_macaddr" ||
720 val[0] == "mbssid")
developerd0c89452024-10-11 16:53:27 +0800721 config[substr(val[0], 1)] = int(val[1]);
developere92ee3c2023-10-25 17:01:28 +0800722
developer66e89bc2024-04-23 14:50:01 +0800723 if (val[0] == "#single_hw")
724 config["single_hw"] = int(val[1]);
725
developerf0fd7052023-08-14 20:23:42 +0800726 push(config.radio.data, line);
727 }
728
developer70180b02023-11-14 17:01:47 +0800729 while ((line = rtrim(f.read("line"), "\n")) != null) {
developere92ee3c2023-10-25 17:01:28 +0800730 if (line == "#default_macaddr")
731 bss.default_macaddr = true;
732
developerf0fd7052023-08-14 20:23:42 +0800733 let val = split(line, "=", 2);
734 if (!val[0])
735 continue;
736
developere92ee3c2023-10-25 17:01:28 +0800737 if (val[0] == "bssid") {
738 bss.bssid = lc(val[1]);
739 continue;
740 }
741
developer66e89bc2024-04-23 14:50:01 +0800742 if (val[0] == "mld_ap" && int(val[1]) == 1)
743 bss.mld_ap = 1;
744
745 if (val[0] == "mld_primary" && int(val[1]) == 1)
746 bss.mld_primary = 1;
747
developere92ee3c2023-10-25 17:01:28 +0800748 if (val[0] == "nas_identifier")
749 bss.nasid = val[1];
750
developerf0fd7052023-08-14 20:23:42 +0800751 if (val[0] == "bss") {
752 bss = config_add_bss(config, val[1]);
753 continue;
754 }
755
756 if (hostapd.data.file_fields[val[0]])
757 bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
758
759 push(bss.data, line);
760 }
761 f.close();
762
developer9237f442024-06-14 17:13:04 +0800763 let first_mld_bss = 0;
764 for (first_mld_bss = 0; first_mld_bss < length(config.bss); first_mld_bss++) {
765 if (config.bss[first_mld_bss].mld_ap == 1)
766 break;
767 }
768
769 if (config.bss[0].mld_ap != 1 && first_mld_bss != length(config.bss)) {
770 let tmp_bss = config.bss[0];
771 config.bss[0] = config.bss[first_mld_bss];
772 config.bss[first_mld_bss] = tmp_bss;
773 hostapd.printf(`mtk: ucode: switch bss[${first_mld_bss}] to first`);
774 }
775
developerf0fd7052023-08-14 20:23:42 +0800776 return config;
777}
778
developere92ee3c2023-10-25 17:01:28 +0800779function ex_wrap(func) {
780 return (req) => {
781 try {
782 let ret = func(req);
783 return ret;
784 } catch(e) {
785 hostapd.printf(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}`);
786 }
787 return libubus.STATUS_UNKNOWN_ERROR;
788 };
789}
developerf0fd7052023-08-14 20:23:42 +0800790
791let main_obj = {
792 reload: {
793 args: {
794 phy: "",
developere92ee3c2023-10-25 17:01:28 +0800795 },
796 call: ex_wrap(function(req) {
797 let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
798 for (let phy_name in phy_list) {
799 let phy = hostapd.data.config[phy_name];
800 let config = iface_load_config(phy.orig_file);
801 iface_set_config(phy_name, config);
802 }
803
804 return 0;
805 })
806 },
807 apsta_state: {
808 args: {
809 phy: "",
810 up: true,
811 frequency: 0,
812 sec_chan_offset: 0,
developerab5e7752023-12-21 16:18:59 +0800813 ch_width: -1,
814 bw320_offset: 1,
developer66e89bc2024-04-23 14:50:01 +0800815 band_idx: 0,
developere92ee3c2023-10-25 17:01:28 +0800816 csa: true,
817 csa_count: 0,
developer05f3b2b2024-08-19 19:17:34 +0800818 punct_bitmap: 0,
developerf0fd7052023-08-14 20:23:42 +0800819 },
developere92ee3c2023-10-25 17:01:28 +0800820 call: ex_wrap(function(req) {
821 if (req.args.up == null || !req.args.phy)
developerf0fd7052023-08-14 20:23:42 +0800822 return libubus.STATUS_INVALID_ARGUMENT;
developere92ee3c2023-10-25 17:01:28 +0800823
developerab5e7752023-12-21 16:18:59 +0800824 hostapd.printf(`ucode: mtk: apsta state update`);
825 hostapd.printf(` * phy: ${req.args.phy}`);
826 hostapd.printf(` * up: ${req.args.up}`);
827 hostapd.printf(` * freqeuncy: ${req.args.frequency}`);
828 hostapd.printf(` * sec_chan_offset: ${req.args.sec_chan_offset}`);
829 hostapd.printf(` * ch_width: ${req.args.ch_width}`);
830 hostapd.printf(` * bw320_offset: ${req.args.bw320_offset}`);
developer66e89bc2024-04-23 14:50:01 +0800831 hostapd.printf(` * band_idx: ${req.args.band_idx}`);
developerab5e7752023-12-21 16:18:59 +0800832 hostapd.printf(` * csa: ${req.args.csa}`);
developer05f3b2b2024-08-19 19:17:34 +0800833 hostapd.printf(` * punct_bitmap: ${req.args.punct_bitmap}`);
developerab5e7752023-12-21 16:18:59 +0800834
developere92ee3c2023-10-25 17:01:28 +0800835 let phy = req.args.phy;
836 let config = hostapd.data.config[phy];
837 if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
838 return 0;
839
840 let iface = hostapd.interfaces[phy];
841 if (!iface)
842 return 0;
843
844 if (!req.args.up) {
845 iface.stop();
846 return 0;
847 }
848
849 if (!req.args.frequency)
850 return libubus.STATUS_INVALID_ARGUMENT;
851
852 let freq_info = iface_freq_info(iface, config, req.args);
853 if (!freq_info)
854 return libubus.STATUS_UNKNOWN_ERROR;
855
856 let ret;
857 if (req.args.csa) {
858 freq_info.csa_count = req.args.csa_count ?? 10;
859 ret = iface.switch_channel(freq_info);
860 } else {
861 ret = iface.start(freq_info);
developerf0fd7052023-08-14 20:23:42 +0800862 }
developere92ee3c2023-10-25 17:01:28 +0800863 if (!ret)
864 return libubus.STATUS_UNKNOWN_ERROR;
developerf0fd7052023-08-14 20:23:42 +0800865
866 return 0;
developere92ee3c2023-10-25 17:01:28 +0800867 })
868 },
869 config_get_macaddr_list: {
870 args: {
871 phy: ""
872 },
873 call: ex_wrap(function(req) {
874 let phy = req.args.phy;
875 if (!phy)
876 return libubus.STATUS_INVALID_ARGUMENT;
877
878 let ret = {
879 macaddr: [],
880 };
881
882 let config = hostapd.data.config[phy];
883 if (!config)
884 return ret;
885
886 ret.macaddr = map(config.bss, (bss) => bss.bssid);
887 return ret;
888 })
developerf0fd7052023-08-14 20:23:42 +0800889 },
890 config_set: {
891 args: {
892 phy: "",
893 config: "",
894 prev_config: "",
895 },
developere92ee3c2023-10-25 17:01:28 +0800896 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800897 let phy = req.args.phy;
898 let file = req.args.config;
899 let prev_file = req.args.prev_config;
900
901 if (!phy)
902 return libubus.STATUS_INVALID_ARGUMENT;
903
developere92ee3c2023-10-25 17:01:28 +0800904 if (prev_file && !hostapd.data.config[phy]) {
905 let config = iface_load_config(prev_file);
906 if (config)
907 config.radio.data = [];
908 hostapd.data.config[phy] = config;
909 }
developerf0fd7052023-08-14 20:23:42 +0800910
developere92ee3c2023-10-25 17:01:28 +0800911 let config = iface_load_config(file);
developerf0fd7052023-08-14 20:23:42 +0800912
developere92ee3c2023-10-25 17:01:28 +0800913 hostapd.printf(`Set new config for phy ${phy}: ${file}`);
914 iface_set_config(phy, config);
developerf0fd7052023-08-14 20:23:42 +0800915
916 return {
917 pid: hostapd.getpid()
918 };
developere92ee3c2023-10-25 17:01:28 +0800919 })
developerf0fd7052023-08-14 20:23:42 +0800920 },
921 config_add: {
922 args: {
923 iface: "",
924 config: "",
925 },
developere92ee3c2023-10-25 17:01:28 +0800926 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800927 if (!req.args.iface || !req.args.config)
928 return libubus.STATUS_INVALID_ARGUMENT;
929
930 if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
931 return libubus.STATUS_INVALID_ARGUMENT;
932
933 return {
934 pid: hostapd.getpid()
935 };
developere92ee3c2023-10-25 17:01:28 +0800936 })
developerf0fd7052023-08-14 20:23:42 +0800937 },
938 config_remove: {
939 args: {
940 iface: ""
941 },
developere92ee3c2023-10-25 17:01:28 +0800942 call: ex_wrap(function(req) {
developerf0fd7052023-08-14 20:23:42 +0800943 if (!req.args.iface)
944 return libubus.STATUS_INVALID_ARGUMENT;
945
946 hostapd.remove_iface(req.args.iface);
947 return 0;
developere92ee3c2023-10-25 17:01:28 +0800948 })
developerf0fd7052023-08-14 20:23:42 +0800949 },
950};
951
952hostapd.data.ubus = ubus;
953hostapd.data.obj = ubus.publish("hostapd", main_obj);
developerab5e7752023-12-21 16:18:59 +0800954hostapd.udebug_set("hostapd", hostapd.data.ubus);
developerf0fd7052023-08-14 20:23:42 +0800955
956function bss_event(type, name, data) {
957 let ubus = hostapd.data.ubus;
958
959 data ??= {};
960 data.name = name;
961 hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
962 ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
963}
964
965return {
966 shutdown: function() {
967 for (let phy in hostapd.data.config)
968 iface_set_config(phy, null);
developerab5e7752023-12-21 16:18:59 +0800969 hostapd.udebug_set(null);
developerf0fd7052023-08-14 20:23:42 +0800970 hostapd.ubus.disconnect();
971 },
developerd0c89452024-10-11 16:53:27 +0800972 bss_add: function(phy, name, obj) {
developerf0fd7052023-08-14 20:23:42 +0800973 bss_event("add", name);
974 },
developerd0c89452024-10-11 16:53:27 +0800975 bss_reload: function(phy, name, obj, reconf) {
developerf0fd7052023-08-14 20:23:42 +0800976 bss_event("reload", name, { reconf: reconf != 0 });
977 },
developerd0c89452024-10-11 16:53:27 +0800978 bss_remove: function(phy, name, obj) {
developerf0fd7052023-08-14 20:23:42 +0800979 bss_event("remove", name);
980 }
981};