wifi-scripts: add ucode based scripts
Add an ucode based re-implementation of the shell script based wifi code. The new code is jsonschema driven. The code has been refactored into several files making it easier to follow. The new scripts are also way faster than the previous sh implementation. The new code is currently opt-in via WIFI_SCRIPTS_UCODE and defaults to EXPERIMENTAL. Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
347
package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh
Executable file
347
package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh
Executable file
@@ -0,0 +1,347 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { set_default, log } from 'wifi.common';
|
||||
import { validate, dump_options } from 'wifi.validate';
|
||||
import * as supplicant from 'wifi.supplicant';
|
||||
import * as hostapd from 'wifi.hostapd';
|
||||
import * as netifd from 'wifi.netifd';
|
||||
import * as iface from 'wifi.iface';
|
||||
import * as fs from 'fs';
|
||||
|
||||
global.radio = ARGV[2];
|
||||
|
||||
const mesh_param_list = [
|
||||
"mesh_retry_timeout", "mesh_confirm_timeout", "mesh_holding_timeout", "mesh_max_peer_links",
|
||||
"mesh_max_retries", "mesh_ttl", "mesh_element_ttl", "mesh_hwmp_max_preq_retries",
|
||||
"mesh_path_refresh_time", "mesh_min_discovery_timeout", "mesh_hwmp_active_path_timeout",
|
||||
"mesh_hwmp_preq_min_interval", "mesh_hwmp_net_diameter_traversal_time", "mesh_hwmp_rootmode",
|
||||
"mesh_hwmp_rann_interval", "mesh_gate_announcements", "mesh_sync_offset_max_neighor",
|
||||
"mesh_rssi_threshold", "mesh_hwmp_active_path_to_root_timeout", "mesh_hwmp_root_interval",
|
||||
"mesh_hwmp_confirmation_interval", "mesh_awake_window", "mesh_plink_timeout",
|
||||
"mesh_auto_open_plinks", "mesh_fwding", "mesh_power_mode"
|
||||
];
|
||||
|
||||
function phy_suffix(radio, sep) {
|
||||
if (radio == null || radio < 0)
|
||||
return "";
|
||||
return sep + radio;
|
||||
}
|
||||
|
||||
function reset_config(phy, radio) {
|
||||
let name = phy + phy_suffix(radio, ".");
|
||||
let prev_config = `/var/run/hostapd-${name}.conf`;
|
||||
|
||||
global.ubus.call('hostapd', 'config_set', { phy, radio, config: '', prev_config });
|
||||
global.ubus.call('wpa_supplicant', 'config_set', { phy, radio, config: []});
|
||||
|
||||
name = phy + phy_suffix(radio, ":");
|
||||
system(`ucode /usr/share/hostap/wdev.uc ${name} set_config '{}'`);
|
||||
}
|
||||
|
||||
function phy_filename(phy, name) {
|
||||
return `/sys/class/ieee80211/${phy}/${name}`;
|
||||
}
|
||||
|
||||
function phy_file(phy, name) {
|
||||
return fs.readfile(phy_filename(phy, name));
|
||||
}
|
||||
|
||||
function phy_index(phy) {
|
||||
return +phy_file(phy, "index");
|
||||
}
|
||||
|
||||
function phy_path_match(phy, path) {
|
||||
let phy_path = fs.realpath(phy_filename(phy, "device"));
|
||||
return substr(phy_path, -length(path)) == path;
|
||||
}
|
||||
|
||||
function find_phy_by_path(phys, path) {
|
||||
if (!path)
|
||||
return null;
|
||||
|
||||
path = split(path, "+");
|
||||
phys = filter(phys, (phy) => phy_path_match(phy, path[0]));
|
||||
phys = sort(phys, (a, b) => phy_index(a) - phy_index(b));
|
||||
|
||||
return phys[+path[1]];
|
||||
}
|
||||
|
||||
function find_phy_by_macaddr(phys, macaddr) {
|
||||
macaddr = lc(macaddr);
|
||||
return filter(phys, (phy) => phy_file(phy, "macaddr") == macaddr)[0];
|
||||
}
|
||||
|
||||
function find_phy_by_name(phys, name) {
|
||||
return index(phys, name) < 0 ? null : name;
|
||||
}
|
||||
|
||||
function find_phy(config) {
|
||||
let phys = fs.lsdir("/sys/class/ieee80211");
|
||||
|
||||
return find_phy_by_path(phys, config.path) ??
|
||||
find_phy_by_macaddr(phys, config.macaddr) ??
|
||||
find_phy_by_name(phys, config.phy);
|
||||
}
|
||||
|
||||
function get_channel_frequency(band, channel) {
|
||||
if (channel < 1)
|
||||
return null;
|
||||
|
||||
switch (band) {
|
||||
case '2g':
|
||||
if (channel == 14)
|
||||
return 2484;
|
||||
return 2407 + channel * 5;
|
||||
case '5g':
|
||||
if (channel >= 182 && channel <= 196)
|
||||
return 4000 + channel * 5;
|
||||
return 5000 + channel * 5;
|
||||
case '6g':
|
||||
if (channel == 2)
|
||||
return 5935;
|
||||
return 5950 + channel * 5;
|
||||
case '60g':
|
||||
return 56160 + channel * 2160;
|
||||
}
|
||||
}
|
||||
|
||||
function setup_phy(phy, config, data) {
|
||||
if (config.channel == "auto")
|
||||
config.channel = 0;
|
||||
config.channel = +config.channel;
|
||||
config.frequency = get_channel_frequency(config.band, config.channel);
|
||||
|
||||
if (config.country) {
|
||||
log(`Setting country code to ${config.country}`);
|
||||
system(`iw reg set ${config.country}`);
|
||||
}
|
||||
|
||||
set_default(config, 'rxantenna', 0xffffffff);
|
||||
set_default(config, 'txantenna', 0xffffffff);
|
||||
|
||||
if (config.txantenna == 'all')
|
||||
config.txantenna = 0xffffffff;
|
||||
if (config.rxantenna == 'all')
|
||||
config.rxantenna = 0xffffffff;
|
||||
|
||||
if (config.txantenna != data?.txantenna || config.rxantenna != data?.rxantenna)
|
||||
reset_config(phy, config.radio);
|
||||
|
||||
netifd.set_data({
|
||||
phy,
|
||||
radio: config.radio,
|
||||
txantenna: config.txantenna,
|
||||
rxantenna: config.rxantenna
|
||||
});
|
||||
|
||||
if (config.txpower)
|
||||
config.txpower = 'fixed ' + config.txpower + '00';
|
||||
else
|
||||
config.txpower = 'auto';
|
||||
|
||||
log(`Configuring '${phy}' txantenna: ${config.txantenna}, rxantenna: ${config.rxantenna} distance: ${config.distance}`);
|
||||
system(`iw phy ${phy} set antenna ${config.txantenna} ${config.rxantenna}`);
|
||||
system(`iw phy ${phy} set distance ${config.distance}`);
|
||||
|
||||
if (config.frag)
|
||||
system(`iw phy ${phy} set frag ${frag}`);
|
||||
if (config.rts)
|
||||
system(`iw phy ${phy} set rts ${rts}`);
|
||||
}
|
||||
|
||||
function iw_htmode(config) {
|
||||
let suffix = substr(config.htmode, 3);
|
||||
if (suffix == "40+" || suffix == "40-")
|
||||
return "HT" + suffix;
|
||||
|
||||
switch (config.htmode ?? "NONE") {
|
||||
case "HT20":
|
||||
case "VHT20":
|
||||
case "HE20":
|
||||
case "EHT20":
|
||||
return "HT20";
|
||||
case "VHT80":
|
||||
case "HE80":
|
||||
case "EHT80":
|
||||
case "HE160":
|
||||
case "EHT160":
|
||||
case "EHT320":
|
||||
return "80MHZ";
|
||||
case "NONE":
|
||||
case "NOHT":
|
||||
return "NOHT";
|
||||
}
|
||||
|
||||
if (substr(config.htmode, 2) == "40") {
|
||||
switch (config.band) {
|
||||
case "2g":
|
||||
if (+config.channel < 7)
|
||||
return "HT40+";
|
||||
else
|
||||
return "HT40-";
|
||||
default:
|
||||
return ((+config.channel / 4) % 2) ? "HT40+" : "HT40-";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function config_add(config, name, val) {
|
||||
if (val != null)
|
||||
config[name] = val;
|
||||
}
|
||||
|
||||
function config_add_mesh_params(config, data) {
|
||||
for (let param in mesh_param_list)
|
||||
config_add(config, param, data[param]);
|
||||
}
|
||||
|
||||
function setup() {
|
||||
let data = json(ARGV[3]);
|
||||
|
||||
data.phy = find_phy(data.config);
|
||||
if (!data.phy) {
|
||||
log('Bug: PHY is undefined for device');
|
||||
netifd.set_retry(false);
|
||||
return 1;
|
||||
}
|
||||
data.phy_suffix = phy_suffix(data.config.radio, ":");
|
||||
data.vif_phy_suffix = phy_suffix(data.config.radio, ".");
|
||||
let active_ifnames = [];
|
||||
|
||||
log('Starting');
|
||||
|
||||
validate('device', data.config);
|
||||
setup_phy(data.phy, data.config, data.data);
|
||||
|
||||
let supplicant_mesh;
|
||||
let has_ap = false;
|
||||
let idx = {};
|
||||
let supplicant_data = [];
|
||||
let wdev_data = {};
|
||||
|
||||
for (let k, v in data.interfaces) {
|
||||
let mode = v.config.mode;
|
||||
idx[mode] ??= 0;
|
||||
let mode_idx = idx[mode]++;
|
||||
|
||||
if (!v.config.ifname)
|
||||
v.config.ifname = data.phy + data.vif_phy_suffix + "-" + mode + mode_idx;
|
||||
push(active_ifnames, v.config.ifname);
|
||||
|
||||
if (v.config.encryption == 'owe' && v.config.owe_transition) {
|
||||
mode_idx = idx[mode]++;
|
||||
v.config.owe_transition_ifname = data.phy + data.vif_phy_suffix + "-" + mode + mode_idx;
|
||||
push(active_ifnames, v.config.ifname);
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case 'ap':
|
||||
has_ap = true;
|
||||
// fallthrough
|
||||
case 'sta':
|
||||
case 'adhoc':
|
||||
case 'mesh':
|
||||
if (mode != "ap")
|
||||
data.config.noscan = true;
|
||||
validate('iface', v.config);
|
||||
iface.prepare(v.config, data.phy + data.phy_suffix, data.config.num_global_macaddr);
|
||||
netifd.set_vif(k, v.config.ifname);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case 'adhoc':
|
||||
if (config.frequency && !v.config.wpa)
|
||||
break;
|
||||
// fallthrough
|
||||
case 'mesh':
|
||||
supplicant_mesh ??= !system("wpa_supplicant -vmesh");
|
||||
if (mode == "mesh" && !supplicant_mesh)
|
||||
break;
|
||||
// fallthrough
|
||||
case 'sta':
|
||||
let config = supplicant.generate(supplicant_data, data, v);
|
||||
if (mode == "mesh")
|
||||
config_add_mesh_params(config, v.config);
|
||||
continue;
|
||||
case 'monitor':
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
// fallback to wdev setup
|
||||
let config = {
|
||||
mode,
|
||||
ssid: v.config.ssid,
|
||||
};
|
||||
|
||||
if (!v.config.default_macaddr)
|
||||
config.macaddr = v.config.macaddr;
|
||||
|
||||
config_add(config, "htmode", wdev_htmode(data.config));
|
||||
if (mode != "monitor") {
|
||||
config_add(config, "basic-rates", supplicant.ratelist(data.config.basic_rate));
|
||||
config_add(config, "mcast-rate", supplicant.ratestr(v.config.mcast_rate));
|
||||
config_add(config, "beacon-interval", data.config.beacon_int);
|
||||
if (mode == "mesh") {
|
||||
config_add(config, "ssid", v.config.mesh_id);
|
||||
config_add_mesh_params(config, v.config);
|
||||
}
|
||||
}
|
||||
|
||||
wdev_data[v.config.ifname] = config;
|
||||
}
|
||||
|
||||
if (length(supplicant_data) > 0)
|
||||
supplicant.setup(supplicant_data, data);
|
||||
|
||||
if (has_ap)
|
||||
hostapd.setup(data);
|
||||
|
||||
system(`ucode /usr/share/hostap/wdev.uc ${data.phy}${data.phy_suffix} set_config '${printf("%J", wdev_data)}' ${join(' ', active_ifnames)}`);
|
||||
|
||||
if (length(supplicant_data) > 0)
|
||||
supplicant.start(data);
|
||||
|
||||
netifd.set_up();
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
let data = json(ARGV[3]);
|
||||
|
||||
if (!data.data?.phy) {
|
||||
log('Bug: PHY is undefined for device');
|
||||
return 1;
|
||||
}
|
||||
|
||||
log(`Tearing down ${data.data.phy}`);
|
||||
|
||||
reset_config(data.data.phy, data.data.radio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
let ret = 1;
|
||||
|
||||
switch(ARGV[1]) {
|
||||
case 'dump':
|
||||
ret = dump_options();
|
||||
break;
|
||||
|
||||
case 'setup':
|
||||
ret = setup();
|
||||
break;
|
||||
|
||||
case 'teardown':
|
||||
ret = teardown();
|
||||
break;
|
||||
}
|
||||
|
||||
exit(ret);
|
||||
Reference in New Issue
Block a user