blob: 2a9de6782b49767d164c91a71ba9e005058bf2d2 [file] [log] [blame]
developere92ee3c2023-10-25 17:01:28 +08001let libubus = require("ubus");
2import { open, readfile } from "fs";
3import { wdev_create, wdev_remove, is_equal, vlist_new, phy_open } from "common";
4
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);
40 let ret = wdev_create(phy, ifname, wdev_config);
41 if (ret)
42 wpas.printf(`Failed to create device ${ifname}: ${ret}`);
43 wpas.add_iface(iface.config);
44 iface.running = true;
45}
46
47function iface_cb(new_if, old_if)
48{
49 if (old_if && new_if && is_equal(old_if.config, new_if.config)) {
50 new_if.running = old_if.running;
51 return;
52 }
53
54 if (new_if && old_if)
55 wpas.printf(`Update configuration for interface ${old_if.config.iface}`);
56 else if (old_if)
57 wpas.printf(`Remove interface ${old_if.config.iface}`);
58
59 if (old_if)
60 iface_stop(old_if);
61}
62
63function prepare_config(config)
64{
65 config.config_data = readfile(config.config);
66
67 return { config: config };
68}
69
70function set_config(phy_name, config_list)
71{
72 let phy = wpas.data.config[phy_name];
73
74 if (!phy) {
75 phy = vlist_new(iface_cb, false);
76 wpas.data.config[phy_name] = phy;
77 }
78
79 let values = [];
80 for (let config in config_list)
81 push(values, [ config.iface, prepare_config(config) ]);
82
83 phy.update(values);
84}
85
86function start_pending(phy_name)
87{
88 let phy = wpas.data.config[phy_name];
89 let ubus = wpas.data.ubus;
90
91 if (!phy || !phy.data)
92 return;
93
94 let phydev = phy_open(phy_name);
95 if (!phydev) {
96 wpas.printf(`Could not open phy ${phy_name}`);
97 return;
98 }
99
100 let macaddr_list = wpas.data.macaddr_list[phy_name];
101 phydev.macaddr_init(macaddr_list);
102
103 for (let ifname in phy.data)
104 iface_start(phydev, phy.data[ifname]);
105}
106
107let main_obj = {
108 phy_set_state: {
109 args: {
110 phy: "",
111 stop: true,
112 },
113 call: function(req) {
114 if (!req.args.phy || req.args.stop == null)
115 return libubus.STATUS_INVALID_ARGUMENT;
116
117 let phy = wpas.data.config[req.args.phy];
118 if (!phy)
119 return libubus.STATUS_NOT_FOUND;
120
121 try {
122 if (req.args.stop) {
123 for (let ifname in phy.data)
124 iface_stop(phy.data[ifname]);
125 } else {
126 start_pending(req.args.phy);
127 }
128 } catch (e) {
129 wpas.printf(`Error chaging state: ${e}\n${e.stacktrace[0].context}`);
130 return libubus.STATUS_INVALID_ARGUMENT;
131 }
132 return 0;
133 }
134 },
135 phy_set_macaddr_list: {
136 args: {
137 phy: "",
138 macaddr: [],
139 },
140 call: function(req) {
141 let phy = req.args.phy;
142 if (!phy)
143 return libubus.STATUS_INVALID_ARGUMENT;
144
145 wpas.data.macaddr_list[phy] = req.args.macaddr;
146 return 0;
147 }
148 },
149 phy_status: {
150 args: {
151 phy: ""
152 },
153 call: function(req) {
154 if (!req.args.phy)
155 return libubus.STATUS_INVALID_ARGUMENT;
156
157 let phy = wpas.data.config[req.args.phy];
158 if (!phy)
159 return libubus.STATUS_NOT_FOUND;
160
161 for (let ifname in phy.data) {
162 try {
163 let iface = wpas.interfaces[ifname];
164 if (!iface)
165 continue;
166
167 let status = iface.status();
168 if (!status)
169 continue;
170
171 if (status.state == "INTERFACE_DISABLED")
172 continue;
173
174 status.ifname = ifname;
175 return status;
176 } catch (e) {
177 continue;
178 }
179 }
180
181 return libubus.STATUS_NOT_FOUND;
182 }
183 },
184 config_set: {
185 args: {
186 phy: "",
187 config: [],
188 defer: true,
189 },
190 call: function(req) {
191 if (!req.args.phy)
192 return libubus.STATUS_INVALID_ARGUMENT;
193
194 wpas.printf(`Set new config for phy ${req.args.phy}`);
195 try {
196 if (req.args.config)
197 set_config(req.args.phy, req.args.config);
198
199 if (!req.args.defer)
200 start_pending(req.args.phy);
201 } catch (e) {
202 wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
203 return libubus.STATUS_INVALID_ARGUMENT;
204 }
205
206 return {
207 pid: wpas.getpid()
208 };
209 }
210 },
211 config_add: {
212 args: {
213 driver: "",
214 iface: "",
215 bridge: "",
216 hostapd_ctrl: "",
217 ctrl: "",
218 config: "",
219 },
220 call: function(req) {
221 if (!req.args.iface || !req.args.config)
222 return libubus.STATUS_INVALID_ARGUMENT;
223
224 if (wpas.add_iface(req.args) < 0)
225 return libubus.STATUS_INVALID_ARGUMENT;
226
227 return {
228 pid: wpas.getpid()
229 };
230 }
231 },
232 config_remove: {
233 args: {
234 iface: ""
235 },
236 call: function(req) {
237 if (!req.args.iface)
238 return libubus.STATUS_INVALID_ARGUMENT;
239
240 wpas.remove_iface(req.args.iface);
241 return 0;
242 }
243 },
244};
245
246wpas.data.ubus = ubus;
247wpas.data.obj = ubus.publish("wpa_supplicant", main_obj);
248
249function iface_event(type, name, data) {
250 let ubus = wpas.data.ubus;
251
252 data ??= {};
253 data.name = name;
254 wpas.data.obj.notify(`iface.${type}`, data, null, null, null, -1);
255 ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
256}
257
258function iface_hostapd_notify(phy, ifname, iface, state)
259{
260 let ubus = wpas.data.ubus;
261 let status = iface.status();
262 let msg = { phy: phy };
263
264 wpas.printf(`ucode: mtk: wpa_s in state ${state} notifies hostapd`);
265 switch (state) {
266 case "DISCONNECTED":
267 case "AUTHENTICATING":
268 case "SCANNING":
269 msg.up = false;
270 break;
271 case "INTERFACE_DISABLED":
272 case "INACTIVE":
273 msg.up = true;
274 break;
275 case "COMPLETED":
276 msg.up = true;
277 msg.frequency = status.frequency;
278 msg.sec_chan_offset = status.sec_chan_offset;
279 msg.ch_width = status.ch_width;
280 msg.bw320_offset = status.bw320_offset;
281 break;
282 default:
283 return;
284 }
285
286 ubus.call("hostapd", "apsta_state", msg);
287}
288
289function iface_channel_switch(phy, ifname, iface, info)
290{
291 let msg = {
292 phy: phy,
293 up: true,
294 csa: true,
295 csa_count: info.csa_count ? info.csa_count - 1 : 0,
296 frequency: info.frequency,
297 ch_width: info.ch_width,
298 bw320_offset: info.bw320_offset,
299 sec_chan_offset: info.sec_chan_offset,
300 };
301 ubus.call("hostapd", "apsta_state", msg);
302}
303
304return {
305 shutdown: function() {
306 for (let phy in wpas.data.config)
307 set_config(phy, []);
308 wpas.ubus.disconnect();
309 },
310 iface_add: function(name, obj) {
311 iface_event("add", name);
312 },
313 iface_remove: function(name, obj) {
314 iface_event("remove", name);
315 },
316 state: function(ifname, iface, state) {
317 let phy = wpas.data.iface_phy[ifname];
318 if (!phy) {
319 wpas.printf(`no PHY for ifname ${ifname}`);
320 return;
321 }
322
323 iface_hostapd_notify(phy, ifname, iface, state);
324 },
325 event: function(ifname, iface, ev, info) {
326 let phy = wpas.data.iface_phy[ifname];
327 if (!phy) {
328 wpas.printf(`no PHY for ifname ${ifname}`);
329 return;
330 }
331
332 if (ev == "CH_SWITCH_STARTED")
333 iface_channel_switch(phy, ifname, iface, info);
334 }
335};