blob: cf438f7715fc678c060a5646ffd401040106ebe0 [file] [log] [blame]
developerf0fd7052023-08-14 20:23:42 +08001#!/usr/bin/env ucode
2'use strict';
developere92ee3c2023-10-25 17:01:28 +08003import { vlist_new, is_equal, wdev_create, wdev_remove, phy_open } from "/usr/share/hostap/common.uc";
developerf0fd7052023-08-14 20:23:42 +08004import { readfile, writefile, basename, readlink, glob } from "fs";
developere92ee3c2023-10-25 17:01:28 +08005let libubus = require("ubus");
developerf0fd7052023-08-14 20:23:42 +08006
7let keep_devices = {};
8let phy = shift(ARGV);
developere92ee3c2023-10-25 17:01:28 +08009let command = shift(ARGV);
10let phydev;
11
developerf0fd7052023-08-14 20:23:42 +080012const mesh_params = [
13 "mesh_retry_timeout", "mesh_confirm_timeout", "mesh_holding_timeout", "mesh_max_peer_links",
14 "mesh_max_retries", "mesh_ttl", "mesh_element_ttl", "mesh_hwmp_max_preq_retries",
15 "mesh_path_refresh_time", "mesh_min_discovery_timeout", "mesh_hwmp_active_path_timeout",
16 "mesh_hwmp_preq_min_interval", "mesh_hwmp_net_diameter_traversal_time", "mesh_hwmp_rootmode",
17 "mesh_hwmp_rann_interval", "mesh_gate_announcements", "mesh_sync_offset_max_neighor",
18 "mesh_rssi_threshold", "mesh_hwmp_active_path_to_root_timeout", "mesh_hwmp_root_interval",
19 "mesh_hwmp_confirmation_interval", "mesh_awake_window", "mesh_plink_timeout",
20 "mesh_auto_open_plinks", "mesh_fwding", "mesh_power_mode"
21];
22
23function iface_stop(wdev)
24{
25 if (keep_devices[wdev.ifname])
26 return;
27
28 wdev_remove(wdev.ifname);
29}
30
31function iface_start(wdev)
32{
33 let ifname = wdev.ifname;
34
35 if (readfile(`/sys/class/net/${ifname}/ifindex`)) {
36 system([ "ip", "link", "set", "dev", ifname, "down" ]);
37 wdev_remove(ifname);
38 }
developere92ee3c2023-10-25 17:01:28 +080039 let wdev_config = {};
40 for (let key in wdev)
41 wdev_config[key] = wdev[key];
42 if (!wdev_config.macaddr && wdev.mode != "monitor")
43 wdev_config.macaddr = phydev.macaddr_next();
44 wdev_create(phy, ifname, wdev_config);
developerf0fd7052023-08-14 20:23:42 +080045 system([ "ip", "link", "set", "dev", ifname, "up" ]);
46 if (wdev.freq)
47 system(`iw dev ${ifname} set freq ${wdev.freq} ${wdev.htmode}`);
48 if (wdev.mode == "adhoc") {
49 let cmd = ["iw", "dev", ifname, "ibss", "join", wdev.ssid, wdev.freq, wdev.htmode, "fixed-freq" ];
50 if (wdev.bssid)
51 push(cmd, wdev.bssid);
52 for (let key in [ "beacon-interval", "basic-rates", "mcast-rate", "keys" ])
53 if (wdev[key])
54 push(cmd, key, wdev[key]);
55 system(cmd);
56 } else if (wdev.mode == "mesh") {
57 let cmd = [ "iw", "dev", ifname, "mesh", "join", wdev.ssid, "freq", wdev.freq, wdev.htmode ];
developere92ee3c2023-10-25 17:01:28 +080058 for (let key in [ "mcast-rate", "beacon-interval" ])
developerf0fd7052023-08-14 20:23:42 +080059 if (wdev[key])
60 push(cmd, key, wdev[key]);
61 system(cmd);
62
63 cmd = ["iw", "dev", ifname, "set", "mesh_param" ];
64 let len = length(cmd);
65
66 for (let param in mesh_params)
67 if (wdev[param])
68 push(cmd, param, wdev[param]);
69
70 if (len == length(cmd))
71 return;
72
73 system(cmd);
74 }
75
76}
77
78function iface_cb(new_if, old_if)
79{
80 if (old_if && new_if && is_equal(old_if, new_if))
81 return;
82
83 if (old_if)
84 iface_stop(old_if);
85 if (new_if)
86 iface_start(new_if);
87}
88
89function drop_inactive(config)
90{
91 for (let key in config) {
92 if (!readfile(`/sys/class/net/${key}/ifindex`))
93 delete config[key];
94 }
95}
96
97function add_ifname(config)
98{
99 for (let key in config)
100 config[key].ifname = key;
101}
102
103function delete_ifname(config)
104{
105 for (let key in config)
106 delete config[key].ifname;
107}
108
109function add_existing(phy, config)
110{
111 let wdevs = glob(`/sys/class/ieee80211/${phy}/device/net/*`);
112 wdevs = map(wdevs, (arg) => basename(arg));
113 for (let wdev in wdevs) {
114 if (config[wdev])
115 continue;
116
117 if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != phy)
118 continue;
119
120 if (trim(readfile(`/sys/class/net/${wdev}/operstate`)) == "down")
121 config[wdev] = {};
122 }
123}
124
developere92ee3c2023-10-25 17:01:28 +0800125function usage()
126{
127 warn(`Usage: ${basename(sourcepath())} <phy> <command> [<arguments>]
developerf0fd7052023-08-14 20:23:42 +0800128
developere92ee3c2023-10-25 17:01:28 +0800129Commands:
130 set_config <config> [<device]...] - set phy configuration
131 get_macaddr <id> - get phy MAC address for vif index <id>
132`);
developerf0fd7052023-08-14 20:23:42 +0800133 exit(1);
134}
135
developere92ee3c2023-10-25 17:01:28 +0800136const commands = {
137 set_config: function(args) {
138 let statefile = `/var/run/wdev-${phy}.json`;
developerf0fd7052023-08-14 20:23:42 +0800139
developere92ee3c2023-10-25 17:01:28 +0800140 let new_config = shift(args);
141 for (let dev in ARGV)
142 keep_devices[dev] = true;
developerf0fd7052023-08-14 20:23:42 +0800143
developere92ee3c2023-10-25 17:01:28 +0800144 if (!new_config)
145 usage();
developerf0fd7052023-08-14 20:23:42 +0800146
developere92ee3c2023-10-25 17:01:28 +0800147 new_config = json(new_config);
148 if (!new_config) {
149 warn("Invalid configuration\n");
150 exit(1);
151 }
developerf0fd7052023-08-14 20:23:42 +0800152
developere92ee3c2023-10-25 17:01:28 +0800153 let old_config = readfile(statefile);
154 if (old_config)
155 old_config = json(old_config);
developerf0fd7052023-08-14 20:23:42 +0800156
developere92ee3c2023-10-25 17:01:28 +0800157 let config = vlist_new(iface_cb);
158 if (type(old_config) == "object")
159 config.data = old_config;
160
161 add_existing(phy, config.data);
162 add_ifname(config.data);
163 drop_inactive(config.data);
164
165 let ubus = libubus.connect();
166 let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phy });
167 let macaddr_list = [];
168 if (type(data) == "object" && data.macaddr)
169 macaddr_list = data.macaddr;
170 ubus.disconnect();
171 phydev.macaddr_init(macaddr_list);
172
173 add_ifname(new_config);
174 config.update(new_config);
175
176 drop_inactive(config.data);
177 delete_ifname(config.data);
178 writefile(statefile, sprintf("%J", config.data));
179 },
180 get_macaddr: function(args) {
181 let data = {};
182
183 for (let arg in args) {
184 arg = split(arg, "=", 2);
185 data[arg[0]] = arg[1];
186 }
187
188 let macaddr = phydev.macaddr_generate(data);
189 if (!macaddr) {
190 warn(`Could not get MAC address for phy ${phy}\n`);
191 exit(1);
192 }
193
194 print(macaddr + "\n");
195 },
196};
197
198if (!phy || !command | !commands[command])
199 usage();
200
201phydev = phy_open(phy);
202if (!phydev) {
203 warn(`PHY ${phy} does not exist\n`);
204 exit(1);
205}
developerf0fd7052023-08-14 20:23:42 +0800206
developere92ee3c2023-10-25 17:01:28 +0800207commands[command](ARGV);