blob: e19a4e178f14535cb1ec895594218e7bf50bd518 [file] [log] [blame]
developere92ee3c2023-10-25 17:01:28 +08001let libubus = require("ubus");
2import { open, readfile } from "fs";
developer70180b02023-11-14 17:01:47 +08003import { wdev_create, wdev_set_mesh_params, wdev_remove, is_equal, wdev_set_up, vlist_new, phy_open } from "common";
developere92ee3c2023-10-25 17:01:28 +08004
5let ubus = libubus.connect();
6
7wpas.data.config = {};
8wpas.data.iface_phy = {};
9wpas.data.macaddr_list = {};
10
11function iface_stop(iface)
12{
13 let ifname = iface.config.iface;
14
15 if (!iface.running)
16 return;
17
18 delete wpas.data.iface_phy[ifname];
19 wpas.remove_iface(ifname);
20 wdev_remove(ifname);
21 iface.running = false;
22}
23
24function iface_start(phydev, iface, macaddr_list)
25{
26 let phy = phydev.name;
27
28 if (iface.running)
29 return;
30
31 let ifname = iface.config.iface;
32 let wdev_config = {};
33 for (let field in iface.config)
34 wdev_config[field] = iface.config[field];
35 if (!wdev_config.macaddr)
36 wdev_config.macaddr = phydev.macaddr_next();
37
38 wpas.data.iface_phy[ifname] = phy;
39 wdev_remove(ifname);
developer66e89bc2024-04-23 14:50:01 +080040 let ret = wdev_create("phy0", ifname, wdev_config);
developere92ee3c2023-10-25 17:01:28 +080041 if (ret)
42 wpas.printf(`Failed to create device ${ifname}: ${ret}`);
developer70180b02023-11-14 17:01:47 +080043 wdev_set_up(ifname, true);
developere92ee3c2023-10-25 17:01:28 +080044 wpas.add_iface(iface.config);
45 iface.running = true;
46}
47
48function iface_cb(new_if, old_if)
49{
50 if (old_if && new_if && is_equal(old_if.config, new_if.config)) {
51 new_if.running = old_if.running;
52 return;
53 }
54
55 if (new_if && old_if)
56 wpas.printf(`Update configuration for interface ${old_if.config.iface}`);
57 else if (old_if)
58 wpas.printf(`Remove interface ${old_if.config.iface}`);
59
60 if (old_if)
61 iface_stop(old_if);
62}
63
64function prepare_config(config)
65{
66 config.config_data = readfile(config.config);
67
68 return { config: config };
69}
70
71function set_config(phy_name, config_list)
72{
73 let phy = wpas.data.config[phy_name];
74
75 if (!phy) {
76 phy = vlist_new(iface_cb, false);
77 wpas.data.config[phy_name] = phy;
78 }
79
80 let values = [];
81 for (let config in config_list)
82 push(values, [ config.iface, prepare_config(config) ]);
83
84 phy.update(values);
85}
86
87function start_pending(phy_name)
88{
89 let phy = wpas.data.config[phy_name];
90 let ubus = wpas.data.ubus;
91
92 if (!phy || !phy.data)
93 return;
94
95 let phydev = phy_open(phy_name);
96 if (!phydev) {
97 wpas.printf(`Could not open phy ${phy_name}`);
98 return;
99 }
100
101 let macaddr_list = wpas.data.macaddr_list[phy_name];
102 phydev.macaddr_init(macaddr_list);
103
104 for (let ifname in phy.data)
105 iface_start(phydev, phy.data[ifname]);
106}
107
108let main_obj = {
109 phy_set_state: {
110 args: {
111 phy: "",
112 stop: true,
113 },
114 call: function(req) {
115 if (!req.args.phy || req.args.stop == null)
116 return libubus.STATUS_INVALID_ARGUMENT;
117
118 let phy = wpas.data.config[req.args.phy];
119 if (!phy)
120 return libubus.STATUS_NOT_FOUND;
121
122 try {
123 if (req.args.stop) {
124 for (let ifname in phy.data)
125 iface_stop(phy.data[ifname]);
126 } else {
127 start_pending(req.args.phy);
128 }
129 } catch (e) {
130 wpas.printf(`Error chaging state: ${e}\n${e.stacktrace[0].context}`);
131 return libubus.STATUS_INVALID_ARGUMENT;
132 }
133 return 0;
134 }
135 },
136 phy_set_macaddr_list: {
137 args: {
138 phy: "",
139 macaddr: [],
140 },
141 call: function(req) {
142 let phy = req.args.phy;
143 if (!phy)
144 return libubus.STATUS_INVALID_ARGUMENT;
145
146 wpas.data.macaddr_list[phy] = req.args.macaddr;
147 return 0;
148 }
149 },
150 phy_status: {
151 args: {
152 phy: ""
153 },
154 call: function(req) {
155 if (!req.args.phy)
156 return libubus.STATUS_INVALID_ARGUMENT;
157
158 let phy = wpas.data.config[req.args.phy];
159 if (!phy)
160 return libubus.STATUS_NOT_FOUND;
161
162 for (let ifname in phy.data) {
163 try {
164 let iface = wpas.interfaces[ifname];
165 if (!iface)
166 continue;
167
168 let status = iface.status();
169 if (!status)
170 continue;
171
172 if (status.state == "INTERFACE_DISABLED")
173 continue;
174
175 status.ifname = ifname;
176 return status;
177 } catch (e) {
178 continue;
179 }
180 }
181
182 return libubus.STATUS_NOT_FOUND;
183 }
184 },
185 config_set: {
186 args: {
187 phy: "",
188 config: [],
189 defer: true,
190 },
191 call: function(req) {
192 if (!req.args.phy)
193 return libubus.STATUS_INVALID_ARGUMENT;
194
195 wpas.printf(`Set new config for phy ${req.args.phy}`);
196 try {
197 if (req.args.config)
198 set_config(req.args.phy, req.args.config);
199
200 if (!req.args.defer)
201 start_pending(req.args.phy);
202 } catch (e) {
203 wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
204 return libubus.STATUS_INVALID_ARGUMENT;
205 }
206
207 return {
208 pid: wpas.getpid()
209 };
210 }
211 },
212 config_add: {
213 args: {
214 driver: "",
215 iface: "",
216 bridge: "",
217 hostapd_ctrl: "",
218 ctrl: "",
219 config: "",
220 },
221 call: function(req) {
222 if (!req.args.iface || !req.args.config)
223 return libubus.STATUS_INVALID_ARGUMENT;
224
225 if (wpas.add_iface(req.args) < 0)
226 return libubus.STATUS_INVALID_ARGUMENT;
227
228 return {
229 pid: wpas.getpid()
230 };
231 }
232 },
233 config_remove: {
234 args: {
235 iface: ""
236 },
237 call: function(req) {
238 if (!req.args.iface)
239 return libubus.STATUS_INVALID_ARGUMENT;
240
241 wpas.remove_iface(req.args.iface);
242 return 0;
243 }
244 },
245};
246
247wpas.data.ubus = ubus;
248wpas.data.obj = ubus.publish("wpa_supplicant", main_obj);
developerab5e7752023-12-21 16:18:59 +0800249wpas.udebug_set("wpa_supplicant", wpas.data.ubus);
developere92ee3c2023-10-25 17:01:28 +0800250
251function iface_event(type, name, data) {
252 let ubus = wpas.data.ubus;
253
254 data ??= {};
255 data.name = name;
256 wpas.data.obj.notify(`iface.${type}`, data, null, null, null, -1);
257 ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
258}
259
developer66e89bc2024-04-23 14:50:01 +0800260function iface_hostapd_notify(phy, ifname, iface, state, link_id)
developere92ee3c2023-10-25 17:01:28 +0800261{
262 let ubus = wpas.data.ubus;
developere92ee3c2023-10-25 17:01:28 +0800263 let msg = { phy: phy };
264
developerab5e7752023-12-21 16:18:59 +0800265 wpas.printf(`ucode: mtk: wpa_s in state ${state} notifies hostapd`);
developere92ee3c2023-10-25 17:01:28 +0800266 switch (state) {
267 case "DISCONNECTED":
268 case "AUTHENTICATING":
269 case "SCANNING":
270 msg.up = false;
271 break;
272 case "INTERFACE_DISABLED":
273 case "INACTIVE":
274 msg.up = true;
275 break;
276 case "COMPLETED":
developer66e89bc2024-04-23 14:50:01 +0800277 let status = iface.status(link_id);
developere92ee3c2023-10-25 17:01:28 +0800278 msg.up = true;
279 msg.frequency = status.frequency;
280 msg.sec_chan_offset = status.sec_chan_offset;
developerab5e7752023-12-21 16:18:59 +0800281 msg.ch_width = status.ch_width;
282 msg.bw320_offset = status.bw320_offset;
developer66e89bc2024-04-23 14:50:01 +0800283 msg.band_idx = status.band_idx;
developere92ee3c2023-10-25 17:01:28 +0800284 break;
285 default:
286 return;
287 }
288
289 ubus.call("hostapd", "apsta_state", msg);
290}
291
292function iface_channel_switch(phy, ifname, iface, info)
293{
294 let msg = {
295 phy: phy,
296 up: true,
297 csa: true,
298 csa_count: info.csa_count ? info.csa_count - 1 : 0,
299 frequency: info.frequency,
developerab5e7752023-12-21 16:18:59 +0800300 ch_width: info.ch_width,
301 bw320_offset: info.bw320_offset,
developere92ee3c2023-10-25 17:01:28 +0800302 sec_chan_offset: info.sec_chan_offset,
303 };
304 ubus.call("hostapd", "apsta_state", msg);
305}
306
307return {
308 shutdown: function() {
309 for (let phy in wpas.data.config)
310 set_config(phy, []);
311 wpas.ubus.disconnect();
312 },
313 iface_add: function(name, obj) {
314 iface_event("add", name);
315 },
316 iface_remove: function(name, obj) {
317 iface_event("remove", name);
318 },
319 state: function(ifname, iface, state) {
320 let phy = wpas.data.iface_phy[ifname];
developer66e89bc2024-04-23 14:50:01 +0800321 let ret = iface.get_valid_links();
322 let link_id = 0, valid_links = ret.valid_links;
323
developere92ee3c2023-10-25 17:01:28 +0800324 if (!phy) {
325 wpas.printf(`no PHY for ifname ${ifname}`);
326 return;
327 }
328
developer66e89bc2024-04-23 14:50:01 +0800329 if (valid_links) {
330 while (valid_links) {
331 if (valid_links & 1)
332 iface_hostapd_notify(phy, ifname, iface, state, link_id);
333
334 link_id++;
335 valid_links >>= 1;
336 }
337 } else {
338 iface_hostapd_notify(phy, ifname, iface, state, -1);
339 }
developer70180b02023-11-14 17:01:47 +0800340
341 if (state != "COMPLETED")
342 return;
343
344 let phy_data = wpas.data.config[phy];
345 if (!phy_data)
346 return;
347
348 let iface_data = phy_data.data[ifname];
349 if (!iface_data)
350 return;
351
352 let wdev_config = iface_data.config;
353 if (!wdev_config || wdev_config.mode != "mesh")
354 return;
355
356 wdev_set_mesh_params(ifname, wdev_config);
developere92ee3c2023-10-25 17:01:28 +0800357 },
358 event: function(ifname, iface, ev, info) {
359 let phy = wpas.data.iface_phy[ifname];
360 if (!phy) {
361 wpas.printf(`no PHY for ifname ${ifname}`);
362 return;
363 }
364
365 if (ev == "CH_SWITCH_STARTED")
366 iface_channel_switch(phy, ifname, iface, info);
367 }
368};