Initial commit
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
This commit is contained in:
45
package/network/config/wifi-scripts/Makefile
Normal file
45
package/network/config/wifi-scripts/Makefile
Normal file
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# Copyright (C) 2024 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wifi-scripts
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/wifi-scripts
|
||||
SECTION:=utils
|
||||
CATEGORY:=Base system
|
||||
DEPENDS:=+netifd +ucode +ucode-mod-nl80211 +ucode-mod-rtnl +ucode-mod-ubus +ucode-mod-uci
|
||||
TITLE:=Wi-Fi configuration scripts
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/wifi-scripts/description
|
||||
A set of scripts that handle setup and configuration of Wi-Fi devices.
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/wifi-scripts/install
|
||||
$(INSTALL_DIR) $(1)
|
||||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,wifi-scripts))
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ "${ACTION}" = "add" ] && {
|
||||
/sbin/wifi config
|
||||
}
|
||||
1598
package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh
Normal file
1598
package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,439 @@
|
||||
NETIFD_MAIN_DIR="${NETIFD_MAIN_DIR:-/lib/netifd}"
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
. $NETIFD_MAIN_DIR/utils.sh
|
||||
|
||||
CMD_UP=0
|
||||
CMD_SET_DATA=1
|
||||
CMD_PROCESS_ADD=2
|
||||
CMD_PROCESS_KILL_ALL=3
|
||||
CMD_SET_RETRY=4
|
||||
|
||||
add_driver() {
|
||||
return
|
||||
}
|
||||
|
||||
wireless_setup_vif_failed() {
|
||||
local error="$1"
|
||||
echo "Interface $_w_iface setup failed: $error"
|
||||
}
|
||||
|
||||
wireless_setup_failed() {
|
||||
local error="$1"
|
||||
|
||||
echo "Device setup failed: $error"
|
||||
wireless_set_retry 0
|
||||
}
|
||||
|
||||
prepare_key_wep() {
|
||||
local key="$1"
|
||||
local hex=1
|
||||
|
||||
echo -n "$key" | grep -qE "[^a-fA-F0-9]" && hex=0
|
||||
[ "${#key}" -eq 10 -a $hex -eq 1 ] || \
|
||||
[ "${#key}" -eq 26 -a $hex -eq 1 ] || {
|
||||
[ "${key:0:2}" = "s:" ] && key="${key#s:}"
|
||||
key="$(echo -n "$key" | hexdump -ve '1/1 "%02x" ""')"
|
||||
}
|
||||
echo "$key"
|
||||
}
|
||||
|
||||
_wdev_prepare_channel() {
|
||||
json_get_vars channel band hwmode
|
||||
|
||||
auto_channel=0
|
||||
enable_ht=0
|
||||
htmode=
|
||||
hwmode="${hwmode##11}"
|
||||
|
||||
case "$channel" in
|
||||
""|0|auto)
|
||||
channel=0
|
||||
auto_channel=1
|
||||
;;
|
||||
[0-9]*) ;;
|
||||
*)
|
||||
wireless_setup_failed "INVALID_CHANNEL"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$hwmode" in
|
||||
a|b|g|ad) ;;
|
||||
*)
|
||||
if [ "$channel" -gt 14 ]; then
|
||||
hwmode=a
|
||||
else
|
||||
hwmode=g
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$band" in
|
||||
2g) hwmode=g;;
|
||||
5g|6g) hwmode=a;;
|
||||
60g) hwmode=ad;;
|
||||
*)
|
||||
case "$hwmode" in
|
||||
*a) band=5g;;
|
||||
*ad) band=60g;;
|
||||
*b|*g) band=2g;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_wdev_handler() {
|
||||
json_load "$data"
|
||||
|
||||
json_select config
|
||||
_wdev_prepare_channel
|
||||
json_select ..
|
||||
|
||||
eval "drv_$1_$2 \"$interface\""
|
||||
}
|
||||
|
||||
_wdev_msg_call() {
|
||||
local old_cb
|
||||
|
||||
json_set_namespace wdev old_cb
|
||||
"$@"
|
||||
json_set_namespace $old_cb
|
||||
}
|
||||
|
||||
_wdev_wrapper() {
|
||||
while [ -n "$1" ]; do
|
||||
eval "$1() { _wdev_msg_call _$1 \"\$@\"; }"
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
_wdev_notify_init() {
|
||||
local command="$1"; shift;
|
||||
|
||||
json_init
|
||||
json_add_int "command" "$command"
|
||||
json_add_string "device" "$__netifd_device"
|
||||
while [ -n "$1" ]; do
|
||||
local name="$1"; shift
|
||||
local value="$1"; shift
|
||||
json_add_string "$name" "$value"
|
||||
done
|
||||
json_add_object "data"
|
||||
}
|
||||
|
||||
_wdev_notify() {
|
||||
local options="$1"
|
||||
|
||||
json_close_object
|
||||
ubus $options call network.wireless notify "$(json_dump)"
|
||||
}
|
||||
|
||||
_wdev_add_variables() {
|
||||
while [ -n "$1" ]; do
|
||||
local var="${1%%=*}"
|
||||
local val="$1"
|
||||
shift
|
||||
[[ "$var" = "$val" ]] && continue
|
||||
val="${val#*=}"
|
||||
json_add_string "$var" "$val"
|
||||
done
|
||||
}
|
||||
|
||||
_wireless_add_vif() {
|
||||
local name="$1"; shift
|
||||
local ifname="$1"; shift
|
||||
|
||||
_wdev_notify_init $CMD_SET_DATA "interface" "$name"
|
||||
json_add_string "ifname" "$ifname"
|
||||
_wdev_add_variables "$@"
|
||||
_wdev_notify
|
||||
}
|
||||
|
||||
_wireless_add_vlan() {
|
||||
local name="$1"; shift
|
||||
local ifname="$1"; shift
|
||||
|
||||
_wdev_notify_init $CMD_SET_DATA interface "$__cur_interface" "vlan" "$name"
|
||||
json_add_string "ifname" "$ifname"
|
||||
_wdev_add_variables "$@"
|
||||
_wdev_notify
|
||||
}
|
||||
|
||||
_wireless_set_up() {
|
||||
_wdev_notify_init $CMD_UP
|
||||
_wdev_notify
|
||||
}
|
||||
|
||||
_wireless_set_data() {
|
||||
_wdev_notify_init $CMD_SET_DATA
|
||||
_wdev_add_variables "$@"
|
||||
_wdev_notify
|
||||
}
|
||||
|
||||
_wireless_add_process() {
|
||||
_wdev_notify_init $CMD_PROCESS_ADD
|
||||
local exe="$2"
|
||||
[ -L "$exe" ] && exe="$(readlink -f "$exe")"
|
||||
json_add_int pid "$1"
|
||||
json_add_string exe "$exe"
|
||||
[ -n "$3" ] && json_add_boolean required 1
|
||||
[ -n "$4" ] && json_add_boolean keep 1
|
||||
exe2="$(readlink -f /proc/$1/exe)"
|
||||
[ "$exe" != "$exe2" ] && echo "WARNING (wireless_add_process): executable path $exe does not match process $1 path ($exe2)"
|
||||
_wdev_notify
|
||||
}
|
||||
|
||||
_wireless_process_kill_all() {
|
||||
_wdev_notify_init $CMD_PROCESS_KILL_ALL
|
||||
[ -n "$1" ] && json_add_int signal "$1"
|
||||
_wdev_notify
|
||||
}
|
||||
|
||||
_wireless_set_retry() {
|
||||
_wdev_notify_init $CMD_SET_RETRY
|
||||
json_add_int retry "$1"
|
||||
_wdev_notify
|
||||
}
|
||||
|
||||
_wdev_wrapper \
|
||||
wireless_add_vif \
|
||||
wireless_add_vlan \
|
||||
wireless_set_up \
|
||||
wireless_set_data \
|
||||
wireless_add_process \
|
||||
wireless_process_kill_all \
|
||||
wireless_set_retry \
|
||||
|
||||
wireless_vif_parse_encryption() {
|
||||
json_get_vars encryption
|
||||
set_default encryption none
|
||||
|
||||
auth_mode_open=1
|
||||
auth_mode_shared=0
|
||||
auth_type=none
|
||||
|
||||
if [ "$hwmode" = "ad" ]; then
|
||||
wpa_cipher="GCMP"
|
||||
else
|
||||
wpa_cipher="CCMP"
|
||||
fi
|
||||
|
||||
case "$encryption" in
|
||||
*tkip+aes|*tkip+ccmp|*aes+tkip|*ccmp+tkip) wpa_cipher="CCMP TKIP";;
|
||||
*ccmp256) wpa_cipher="CCMP-256";;
|
||||
*aes|*ccmp) wpa_cipher="CCMP";;
|
||||
*tkip) wpa_cipher="TKIP";;
|
||||
*gcmp256) wpa_cipher="GCMP-256";;
|
||||
*gcmp) wpa_cipher="GCMP";;
|
||||
wpa3-192*) wpa_cipher="GCMP-256";;
|
||||
esac
|
||||
|
||||
# 802.11n requires CCMP for WPA
|
||||
[ "$enable_ht:$wpa_cipher" = "1:TKIP" ] && wpa_cipher="CCMP TKIP"
|
||||
|
||||
# Examples:
|
||||
# psk-mixed/tkip => WPA1+2 PSK, TKIP
|
||||
# wpa-psk2/tkip+aes => WPA2 PSK, CCMP+TKIP
|
||||
# wpa2/tkip+aes => WPA2 RADIUS, CCMP+TKIP
|
||||
|
||||
case "$encryption" in
|
||||
wpa2*|wpa3*|*psk2*|psk3*|sae*|owe*)
|
||||
wpa=2
|
||||
;;
|
||||
wpa*mixed*|*psk*mixed*)
|
||||
wpa=3
|
||||
;;
|
||||
wpa*|*psk*)
|
||||
wpa=1
|
||||
;;
|
||||
*)
|
||||
wpa=0
|
||||
wpa_cipher=
|
||||
;;
|
||||
esac
|
||||
wpa_pairwise="$wpa_cipher"
|
||||
|
||||
case "$encryption" in
|
||||
owe*)
|
||||
auth_type=owe
|
||||
;;
|
||||
wpa3-192*)
|
||||
auth_type=eap192
|
||||
;;
|
||||
wpa3-mixed*)
|
||||
auth_type=eap-eap2
|
||||
;;
|
||||
wpa3*)
|
||||
auth_type=eap2
|
||||
;;
|
||||
psk3-mixed*|sae-mixed*)
|
||||
auth_type=psk-sae
|
||||
;;
|
||||
psk3*|sae*)
|
||||
auth_type=sae
|
||||
;;
|
||||
*psk*)
|
||||
auth_type=psk
|
||||
;;
|
||||
*wpa*|*8021x*)
|
||||
auth_type=eap
|
||||
;;
|
||||
*wep*)
|
||||
auth_type=wep
|
||||
case "$encryption" in
|
||||
*shared*)
|
||||
auth_mode_open=0
|
||||
auth_mode_shared=1
|
||||
;;
|
||||
*mixed*)
|
||||
auth_mode_shared=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$encryption" in
|
||||
*osen*)
|
||||
auth_osen=1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_wireless_set_brsnoop_isolation() {
|
||||
local multicast_to_unicast="$1"
|
||||
local isolate
|
||||
|
||||
json_get_vars isolate proxy_arp
|
||||
|
||||
[ ${isolate:-0} -gt 0 -o -z "$network_bridge" ] && return
|
||||
[ ${multicast_to_unicast:-1} -gt 0 -o ${proxy_arp:-0} -gt 0 ] && json_add_boolean isolate 1
|
||||
}
|
||||
|
||||
for_each_interface() {
|
||||
local _w_types="$1"; shift
|
||||
local _w_ifaces _w_iface
|
||||
local _w_type
|
||||
local _w_found
|
||||
|
||||
local multicast_to_unicast
|
||||
|
||||
json_get_keys _w_ifaces interfaces
|
||||
json_select interfaces
|
||||
for _w_iface in $_w_ifaces; do
|
||||
json_select "$_w_iface"
|
||||
if [ -n "$_w_types" ]; then
|
||||
json_get_var network_bridge bridge
|
||||
json_get_var network_ifname bridge-ifname
|
||||
json_get_var multicast_to_unicast multicast_to_unicast
|
||||
json_select config
|
||||
_wireless_set_brsnoop_isolation "$multicast_to_unicast"
|
||||
json_get_var _w_type mode
|
||||
json_select ..
|
||||
_w_types=" $_w_types "
|
||||
[[ "${_w_types%$_w_type*}" = "$_w_types" ]] && {
|
||||
json_select ..
|
||||
continue
|
||||
}
|
||||
fi
|
||||
__cur_interface="$_w_iface"
|
||||
"$@" "$_w_iface"
|
||||
json_select ..
|
||||
done
|
||||
json_select ..
|
||||
}
|
||||
|
||||
for_each_vlan() {
|
||||
local _w_vlans _w_vlan
|
||||
|
||||
json_get_keys _w_vlans vlans
|
||||
json_select vlans
|
||||
for _w_vlan in $_w_vlans; do
|
||||
json_select "$_w_vlan"
|
||||
json_select config
|
||||
"$@" "$_w_vlan"
|
||||
json_select ..
|
||||
json_select ..
|
||||
done
|
||||
json_select ..
|
||||
}
|
||||
|
||||
for_each_station() {
|
||||
local _w_stas _w_sta
|
||||
|
||||
json_get_keys _w_stas stas
|
||||
json_select stas
|
||||
for _w_sta in $_w_stas; do
|
||||
json_select "$_w_sta"
|
||||
json_select config
|
||||
"$@" "$_w_sta"
|
||||
json_select ..
|
||||
json_select ..
|
||||
done
|
||||
json_select ..
|
||||
}
|
||||
|
||||
_wdev_common_device_config() {
|
||||
config_add_string channel hwmode band htmode noscan
|
||||
}
|
||||
|
||||
_wdev_common_iface_config() {
|
||||
config_add_string mode ssid encryption 'key:wpakey'
|
||||
config_add_boolean bridge_isolate
|
||||
}
|
||||
|
||||
_wdev_common_vlan_config() {
|
||||
config_add_string name vid iface
|
||||
config_add_boolean bridge_isolate
|
||||
}
|
||||
|
||||
_wdev_common_station_config() {
|
||||
config_add_string mac key vid iface
|
||||
}
|
||||
|
||||
init_wireless_driver() {
|
||||
name="$1"; shift
|
||||
cmd="$1"; shift
|
||||
|
||||
case "$cmd" in
|
||||
dump)
|
||||
add_driver() {
|
||||
eval "drv_$1_cleanup"
|
||||
|
||||
json_init
|
||||
json_add_string name "$1"
|
||||
|
||||
json_add_array device
|
||||
_wdev_common_device_config
|
||||
eval "drv_$1_init_device_config"
|
||||
json_close_array
|
||||
|
||||
json_add_array iface
|
||||
_wdev_common_iface_config
|
||||
eval "drv_$1_init_iface_config"
|
||||
json_close_array
|
||||
|
||||
json_add_array vlan
|
||||
_wdev_common_vlan_config
|
||||
eval "drv_$1_init_vlan_config"
|
||||
json_close_array
|
||||
|
||||
json_add_array station
|
||||
_wdev_common_station_config
|
||||
eval "drv_$1_init_station_config"
|
||||
json_close_array
|
||||
|
||||
json_dump
|
||||
}
|
||||
;;
|
||||
setup|teardown)
|
||||
interface="$1"; shift
|
||||
data="$1"; shift
|
||||
export __netifd_device="$interface"
|
||||
|
||||
add_driver() {
|
||||
[[ "$name" == "$1" ]] || return 0
|
||||
_wdev_handler "$1" "$cmd"
|
||||
}
|
||||
;;
|
||||
esac
|
||||
}
|
||||
1198
package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh
Executable file
1198
package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh
Executable file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env ucode
|
||||
import { readfile } from "fs";
|
||||
import * as uci from 'uci';
|
||||
|
||||
const bands_order = [ "6G", "5G", "2G" ];
|
||||
const htmode_order = [ "HE", "VHT", "HT" ];
|
||||
|
||||
let board = json(readfile("/etc/board.json"));
|
||||
if (!board.wlan)
|
||||
exit(0);
|
||||
|
||||
let idx = 0;
|
||||
let commit;
|
||||
|
||||
let config = uci.cursor().get_all("wireless") ?? {};
|
||||
|
||||
function radio_exists(path, macaddr, phy) {
|
||||
for (let name, s in config) {
|
||||
if (s[".type"] != "wifi-device")
|
||||
continue;
|
||||
if (s.macaddr & lc(s.macaddr) == lc(macaddr))
|
||||
return true;
|
||||
if (s.phy == phy)
|
||||
return true;
|
||||
if (!s.path || !path)
|
||||
continue;
|
||||
if (substr(s.path, -length(path)) == path)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (let phy_name, phy in board.wlan) {
|
||||
let info = phy.info;
|
||||
if (!info || !length(info.bands))
|
||||
continue;
|
||||
|
||||
while (config[`radio${idx}`])
|
||||
idx++;
|
||||
let name = "radio" + idx++;
|
||||
|
||||
let s = "wireless." + name;
|
||||
let si = "wireless.default_" + name;
|
||||
|
||||
let band_name = filter(bands_order, (b) => info.bands[b])[0];
|
||||
if (!band_name)
|
||||
continue;
|
||||
|
||||
let band = info.bands[band_name];
|
||||
let channel = band.default_channel ?? "auto";
|
||||
|
||||
let width = band.max_width;
|
||||
if (band_name == "2G")
|
||||
width = 20;
|
||||
else if (width > 80)
|
||||
width = 80;
|
||||
|
||||
let htmode = filter(htmode_order, (m) => band[lc(m)])[0];
|
||||
if (htmode)
|
||||
htmode += width;
|
||||
else
|
||||
htmode = "NOHT";
|
||||
|
||||
if (!phy.path)
|
||||
continue;
|
||||
|
||||
let macaddr = trim(readfile(`/sys/class/ieee80211/${phy_name}/macaddress`));
|
||||
if (radio_exists(phy.path, macaddr, phy_name))
|
||||
continue;
|
||||
|
||||
let id = `phy='${phy_name}'`;
|
||||
if (match(phy_name, /^phy[0-9]/))
|
||||
id = `path='${phy.path}'`;
|
||||
|
||||
print(`set ${s}=wifi-device
|
||||
set ${s}.type='mac80211'
|
||||
set ${s}.${id}
|
||||
set ${s}.band='${lc(band_name)}'
|
||||
set ${s}.channel='${channel}'
|
||||
set ${s}.htmode='${htmode}'
|
||||
set ${s}.disabled='1'
|
||||
|
||||
set ${si}=wifi-iface
|
||||
set ${si}.device='${name}'
|
||||
set ${si}.network='lan'
|
||||
set ${si}.mode='ap'
|
||||
set ${si}.ssid='OpenWrt'
|
||||
set ${si}.encryption='none'
|
||||
|
||||
`);
|
||||
commit = true;
|
||||
}
|
||||
|
||||
if (commit)
|
||||
print("commit wireless\n");
|
||||
274
package/network/config/wifi-scripts/files/sbin/wifi
Executable file
274
package/network/config/wifi-scripts/files/sbin/wifi
Executable file
@@ -0,0 +1,274 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2006 OpenWrt.org
|
||||
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [config|up|down|reconf|reload|status|isup]
|
||||
enables (default), disables or configures devices not yet configured.
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
ubus_wifi_cmd() {
|
||||
local cmd="$1"
|
||||
local dev="$2"
|
||||
|
||||
json_init
|
||||
[ -n "$dev" ] && json_add_string device "$dev"
|
||||
ubus call network.wireless "$cmd" "$(json_dump)"
|
||||
}
|
||||
|
||||
wifi_isup() {
|
||||
local dev="$1"
|
||||
|
||||
json_load "$(ubus_wifi_cmd "status" "$dev")"
|
||||
json_get_keys devices
|
||||
|
||||
for device in $devices; do
|
||||
json_select "$device"
|
||||
json_get_var up up
|
||||
[ $up -eq 0 ] && return 1
|
||||
json_select ..
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
find_net_config() {(
|
||||
local vif="$1"
|
||||
local cfg
|
||||
local ifname
|
||||
|
||||
config_get cfg "$vif" network
|
||||
|
||||
[ -z "$cfg" ] && {
|
||||
include /lib/network
|
||||
scan_interfaces
|
||||
|
||||
config_get ifname "$vif" ifname
|
||||
|
||||
cfg="$(find_config "$ifname")"
|
||||
}
|
||||
[ -z "$cfg" ] && return 0
|
||||
echo "$cfg"
|
||||
)}
|
||||
|
||||
|
||||
bridge_interface() {(
|
||||
local cfg="$1"
|
||||
[ -z "$cfg" ] && return 0
|
||||
|
||||
include /lib/network
|
||||
scan_interfaces
|
||||
|
||||
for cfg in $cfg; do
|
||||
config_get iftype "$cfg" type
|
||||
[ "$iftype" = bridge ] && config_get "$cfg" ifname
|
||||
prepare_interface_bridge "$cfg"
|
||||
return $?
|
||||
done
|
||||
)}
|
||||
|
||||
prepare_key_wep() {
|
||||
local key="$1"
|
||||
local hex=1
|
||||
|
||||
echo -n "$key" | grep -qE "[^a-fA-F0-9]" && hex=0
|
||||
[ "${#key}" -eq 10 -a $hex -eq 1 ] || \
|
||||
[ "${#key}" -eq 26 -a $hex -eq 1 ] || {
|
||||
[ "${key:0:2}" = "s:" ] && key="${key#s:}"
|
||||
key="$(echo -n "$key" | hexdump -ve '1/1 "%02x" ""')"
|
||||
}
|
||||
echo "$key"
|
||||
}
|
||||
|
||||
wifi_fixup_hwmode() {
|
||||
local device="$1"
|
||||
local default="$2"
|
||||
local hwmode hwmode_11n
|
||||
|
||||
config_get channel "$device" channel
|
||||
config_get hwmode "$device" hwmode
|
||||
case "$hwmode" in
|
||||
11bg) hwmode=bg;;
|
||||
11a) hwmode=a;;
|
||||
11ad) hwmode=ad;;
|
||||
11b) hwmode=b;;
|
||||
11g) hwmode=g;;
|
||||
11n*)
|
||||
hwmode_11n="${hwmode##11n}"
|
||||
case "$hwmode_11n" in
|
||||
a|g) ;;
|
||||
default) hwmode_11n="$default"
|
||||
esac
|
||||
config_set "$device" hwmode_11n "$hwmode_11n"
|
||||
;;
|
||||
*)
|
||||
hwmode=
|
||||
if [ "${channel:-0}" -gt 0 ]; then
|
||||
if [ "${channel:-0}" -gt 14 ]; then
|
||||
hwmode=a
|
||||
else
|
||||
hwmode=g
|
||||
fi
|
||||
else
|
||||
hwmode="$default"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
config_set "$device" hwmode "$hwmode"
|
||||
}
|
||||
|
||||
_wifi_updown() {
|
||||
for device in ${2:-$DEVICES}; do (
|
||||
config_get disabled "$device" disabled
|
||||
[ "$disabled" = "1" ] && {
|
||||
echo "'$device' is disabled"
|
||||
set disable
|
||||
}
|
||||
config_get iftype "$device" type
|
||||
if eval "type ${1}_$iftype" 2>/dev/null >/dev/null; then
|
||||
eval "scan_$iftype '$device'"
|
||||
eval "${1}_$iftype '$device'" || echo "$device($iftype): ${1} failed"
|
||||
elif [ ! -f /lib/netifd/wireless/$iftype.sh ]; then
|
||||
echo "$device($iftype): Interface type not supported"
|
||||
fi
|
||||
); done
|
||||
}
|
||||
|
||||
wifi_updown() {
|
||||
cmd=down
|
||||
[ enable = "$1" ] && {
|
||||
_wifi_updown disable "$2"
|
||||
ubus_wifi_cmd "$cmd" "$2"
|
||||
ubus call network reload
|
||||
scan_wifi
|
||||
cmd=up
|
||||
}
|
||||
[ reconf = "$1" ] && {
|
||||
ubus call network reload
|
||||
scan_wifi
|
||||
cmd=reconf
|
||||
}
|
||||
ubus_wifi_cmd "$cmd" "$2"
|
||||
_wifi_updown "$@"
|
||||
}
|
||||
|
||||
wifi_reload_legacy() {
|
||||
_wifi_updown "disable" "$1"
|
||||
scan_wifi
|
||||
_wifi_updown "enable" "$1"
|
||||
}
|
||||
|
||||
wifi_reload() {
|
||||
ubus call network reload
|
||||
wifi_reload_legacy
|
||||
}
|
||||
|
||||
wifi_detect_notice() {
|
||||
>&2 echo "WARNING: Wifi detect is deprecated. Use wifi config instead"
|
||||
>&2 echo "For more information, see commit 5f8f8a366136a07df661e31decce2458357c167a"
|
||||
exit 1
|
||||
}
|
||||
|
||||
wifi_config() {
|
||||
[ -e /tmp/.config_pending ] && return
|
||||
ucode /usr/share/hostap/wifi-detect.uc
|
||||
[ ! -f /etc/config/wireless ] && touch /etc/config/wireless
|
||||
ucode /lib/wifi/mac80211.uc | uci -q batch
|
||||
|
||||
for driver in $DRIVERS; do (
|
||||
if eval "type detect_$driver" 2>/dev/null >/dev/null; then
|
||||
eval "detect_$driver" || echo "$driver: Detect failed" >&2
|
||||
else
|
||||
echo "$driver: Hardware detection not supported" >&2
|
||||
fi
|
||||
); done
|
||||
}
|
||||
|
||||
start_net() {(
|
||||
local iface="$1"
|
||||
local config="$2"
|
||||
local vifmac="$3"
|
||||
|
||||
[ -f "/var/run/$iface.pid" ] && kill "$(cat /var/run/${iface}.pid)" 2>/dev/null
|
||||
[ -z "$config" ] || {
|
||||
include /lib/network
|
||||
scan_interfaces
|
||||
for config in $config; do
|
||||
setup_interface "$iface" "$config" "" "$vifmac"
|
||||
done
|
||||
}
|
||||
)}
|
||||
|
||||
set_wifi_up() {
|
||||
local cfg="$1"
|
||||
local ifname="$2"
|
||||
uci_set_state wireless "$cfg" up 1
|
||||
uci_set_state wireless "$cfg" ifname "$ifname"
|
||||
}
|
||||
|
||||
set_wifi_down() {
|
||||
local cfg="$1"
|
||||
local vifs vif vifstr
|
||||
|
||||
[ -f "/var/run/wifi-${cfg}.pid" ] &&
|
||||
kill "$(cat "/var/run/wifi-${cfg}.pid")" 2>/dev/null
|
||||
uci_revert_state wireless "$cfg"
|
||||
config_get vifs "$cfg" vifs
|
||||
for vif in $vifs; do
|
||||
uci_revert_state wireless "$vif"
|
||||
done
|
||||
}
|
||||
|
||||
scan_wifi() {
|
||||
local cfgfile="$1"
|
||||
DEVICES=
|
||||
config_cb() {
|
||||
local type="$1"
|
||||
local section="$2"
|
||||
|
||||
# section start
|
||||
case "$type" in
|
||||
wifi-device)
|
||||
append DEVICES "$section"
|
||||
config_set "$section" vifs ""
|
||||
config_set "$section" ht_capab ""
|
||||
;;
|
||||
esac
|
||||
|
||||
# section end
|
||||
config_get TYPE "$CONFIG_SECTION" TYPE
|
||||
case "$TYPE" in
|
||||
wifi-iface)
|
||||
config_get device "$CONFIG_SECTION" device
|
||||
config_get vifs "$device" vifs
|
||||
append vifs "$CONFIG_SECTION"
|
||||
config_set "$device" vifs "$vifs"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
config_load "${cfgfile:-wireless}"
|
||||
}
|
||||
|
||||
DEVICES=
|
||||
DRIVERS=
|
||||
include /lib/wifi
|
||||
scan_wifi
|
||||
|
||||
case "$1" in
|
||||
down) wifi_updown "disable" "$2";;
|
||||
detect) wifi_detect_notice ;;
|
||||
config) wifi_config ;;
|
||||
status) ubus_wifi_cmd "status" "$2";;
|
||||
isup) wifi_isup "$2"; exit $?;;
|
||||
reload) wifi_reload "$2";;
|
||||
reload_legacy) wifi_reload_legacy "$2";;
|
||||
--help|help) usage;;
|
||||
reconf) wifi_updown "reconf" "$2";;
|
||||
''|up) wifi_updown "enable" "$2";;
|
||||
*) usage; exit 1;;
|
||||
esac
|
||||
@@ -0,0 +1,373 @@
|
||||
import * as nl80211 from "nl80211";
|
||||
import * as rtnl from "rtnl";
|
||||
import { readfile, glob, basename, readlink } from "fs";
|
||||
|
||||
const iftypes = {
|
||||
ap: nl80211.const.NL80211_IFTYPE_AP,
|
||||
mesh: nl80211.const.NL80211_IFTYPE_MESH_POINT,
|
||||
sta: nl80211.const.NL80211_IFTYPE_STATION,
|
||||
adhoc: nl80211.const.NL80211_IFTYPE_ADHOC,
|
||||
monitor: nl80211.const.NL80211_IFTYPE_MONITOR,
|
||||
};
|
||||
|
||||
const mesh_params = {
|
||||
mesh_retry_timeout: "retry_timeout",
|
||||
mesh_confirm_timeout: "confirm_timeout",
|
||||
mesh_holding_timeout: "holding_timeout",
|
||||
mesh_max_peer_links: "max_peer_links",
|
||||
mesh_max_retries: "max_retries",
|
||||
mesh_ttl: "ttl",
|
||||
mesh_element_ttl: "element_ttl",
|
||||
mesh_auto_open_plinks: "auto_open_plinks",
|
||||
mesh_hwmp_max_preq_retries: "hwmp_max_preq_retries",
|
||||
mesh_path_refresh_time: "path_refresh_time",
|
||||
mesh_min_discovery_timeout: "min_discovery_timeout",
|
||||
mesh_hwmp_active_path_timeout: "hwmp_active_path_timeout",
|
||||
mesh_hwmp_preq_min_interval: "hwmp_preq_min_interval",
|
||||
mesh_hwmp_net_diameter_traversal_time: "hwmp_net_diam_trvs_time",
|
||||
mesh_hwmp_rootmode: "hwmp_rootmode",
|
||||
mesh_hwmp_rann_interval: "hwmp_rann_interval",
|
||||
mesh_gate_announcements: "gate_announcements",
|
||||
mesh_sync_offset_max_neighor: "sync_offset_max_neighbor",
|
||||
mesh_rssi_threshold: "rssi_threshold",
|
||||
mesh_hwmp_active_path_to_root_timeout: "hwmp_path_to_root_timeout",
|
||||
mesh_hwmp_root_interval: "hwmp_root_interval",
|
||||
mesh_hwmp_confirmation_interval: "hwmp_confirmation_interval",
|
||||
mesh_awake_window: "awake_window",
|
||||
mesh_plink_timeout: "plink_timeout",
|
||||
mesh_fwding: "forwarding",
|
||||
mesh_power_mode: "power_mode",
|
||||
mesh_nolearn: "nolearn"
|
||||
};
|
||||
|
||||
function wdev_remove(name)
|
||||
{
|
||||
nl80211.request(nl80211.const.NL80211_CMD_DEL_INTERFACE, 0, { dev: name });
|
||||
}
|
||||
|
||||
function __phy_is_fullmac(phyidx)
|
||||
{
|
||||
let data = nl80211.request(nl80211.const.NL80211_CMD_GET_WIPHY, 0, { wiphy: phyidx });
|
||||
|
||||
return !data.software_iftypes.monitor;
|
||||
}
|
||||
|
||||
function phy_is_fullmac(phy)
|
||||
{
|
||||
let phyidx = int(trim(readfile(`/sys/class/ieee80211/${phy}/index`)));
|
||||
|
||||
return __phy_is_fullmac(phyidx);
|
||||
}
|
||||
|
||||
function find_reusable_wdev(phyidx)
|
||||
{
|
||||
if (!__phy_is_fullmac(phyidx))
|
||||
return null;
|
||||
|
||||
let data = nl80211.request(
|
||||
nl80211.const.NL80211_CMD_GET_INTERFACE,
|
||||
nl80211.const.NLM_F_DUMP,
|
||||
{ wiphy: phyidx });
|
||||
for (let res in data)
|
||||
if (trim(readfile(`/sys/class/net/${res.ifname}/operstate`)) == "down")
|
||||
return res.ifname;
|
||||
return null;
|
||||
}
|
||||
|
||||
function wdev_create(phy, name, data)
|
||||
{
|
||||
let phyidx = int(readfile(`/sys/class/ieee80211/${phy}/index`));
|
||||
|
||||
wdev_remove(name);
|
||||
|
||||
if (!iftypes[data.mode])
|
||||
return `Invalid mode: ${data.mode}`;
|
||||
|
||||
let req = {
|
||||
wiphy: phyidx,
|
||||
ifname: name,
|
||||
iftype: iftypes[data.mode],
|
||||
};
|
||||
|
||||
if (data["4addr"])
|
||||
req["4addr"] = data["4addr"];
|
||||
if (data.macaddr)
|
||||
req.mac = data.macaddr;
|
||||
|
||||
nl80211.error();
|
||||
|
||||
let reuse_ifname = find_reusable_wdev(phyidx);
|
||||
if (reuse_ifname &&
|
||||
(reuse_ifname == name ||
|
||||
rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: reuse_ifname, ifname: name}) != false))
|
||||
nl80211.request(
|
||||
nl80211.const.NL80211_CMD_SET_INTERFACE, 0, {
|
||||
wiphy: phyidx,
|
||||
dev: name,
|
||||
iftype: iftypes[data.mode],
|
||||
});
|
||||
else
|
||||
nl80211.request(
|
||||
nl80211.const.NL80211_CMD_NEW_INTERFACE,
|
||||
nl80211.const.NLM_F_CREATE,
|
||||
req);
|
||||
|
||||
let error = nl80211.error();
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (data.powersave != null) {
|
||||
nl80211.request(nl80211.const.NL80211_CMD_SET_POWER_SAVE, 0,
|
||||
{ dev: name, ps_state: data.powersave ? 1 : 0});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function wdev_set_mesh_params(name, data)
|
||||
{
|
||||
let mesh_cfg = {};
|
||||
|
||||
for (let key in mesh_params) {
|
||||
let val = data[key];
|
||||
if (val == null)
|
||||
continue;
|
||||
mesh_cfg[mesh_params[key]] = int(val);
|
||||
}
|
||||
|
||||
if (!length(mesh_cfg))
|
||||
return null;
|
||||
|
||||
nl80211.request(nl80211.const.NL80211_CMD_SET_MESH_CONFIG, 0,
|
||||
{ dev: name, mesh_params: mesh_cfg });
|
||||
|
||||
return nl80211.error();
|
||||
}
|
||||
|
||||
function wdev_set_up(name, up)
|
||||
{
|
||||
rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: name, change: 1, flags: up ? 1 : 0 });
|
||||
}
|
||||
|
||||
function phy_sysfs_file(phy, name)
|
||||
{
|
||||
return trim(readfile(`/sys/class/ieee80211/${phy}/${name}`));
|
||||
}
|
||||
|
||||
function macaddr_split(str)
|
||||
{
|
||||
return map(split(str, ":"), (val) => hex(val));
|
||||
}
|
||||
|
||||
function macaddr_join(addr)
|
||||
{
|
||||
return join(":", map(addr, (val) => sprintf("%02x", val)));
|
||||
}
|
||||
|
||||
function wdev_macaddr(wdev)
|
||||
{
|
||||
return trim(readfile(`/sys/class/net/${wdev}/address`));
|
||||
}
|
||||
|
||||
const phy_proto = {
|
||||
macaddr_init: function(used, options) {
|
||||
this.macaddr_options = options ?? {};
|
||||
this.macaddr_list = {};
|
||||
|
||||
if (type(used) == "object")
|
||||
for (let addr in used)
|
||||
this.macaddr_list[addr] = used[addr];
|
||||
else
|
||||
for (let addr in used)
|
||||
this.macaddr_list[addr] = -1;
|
||||
|
||||
this.for_each_wdev((wdev) => {
|
||||
let macaddr = wdev_macaddr(wdev);
|
||||
this.macaddr_list[macaddr] ??= -1;
|
||||
});
|
||||
|
||||
return this.macaddr_list;
|
||||
},
|
||||
|
||||
macaddr_generate: function(data) {
|
||||
let phy = this.name;
|
||||
let idx = int(data.id ?? 0);
|
||||
let mbssid = int(data.mbssid ?? 0) > 0;
|
||||
let num_global = int(data.num_global ?? 1);
|
||||
let use_global = !mbssid && idx < num_global;
|
||||
|
||||
let base_addr = phy_sysfs_file(phy, "macaddress");
|
||||
if (!base_addr)
|
||||
return null;
|
||||
|
||||
if (!idx && !mbssid)
|
||||
return base_addr;
|
||||
|
||||
let base_mask = phy_sysfs_file(phy, "address_mask");
|
||||
if (!base_mask)
|
||||
return null;
|
||||
|
||||
if (base_mask == "00:00:00:00:00:00" && idx >= num_global) {
|
||||
let addrs = split(phy_sysfs_file(phy, "addresses"), "\n");
|
||||
|
||||
if (idx < length(addrs))
|
||||
return addrs[idx];
|
||||
|
||||
base_mask = "ff:ff:ff:ff:ff:ff";
|
||||
}
|
||||
|
||||
let addr = macaddr_split(base_addr);
|
||||
let mask = macaddr_split(base_mask);
|
||||
let type;
|
||||
|
||||
if (mbssid)
|
||||
type = "b5";
|
||||
else if (use_global)
|
||||
type = "add";
|
||||
else if (mask[0] > 0)
|
||||
type = "b1";
|
||||
else if (mask[5] < 0xff)
|
||||
type = "b5";
|
||||
else
|
||||
type = "add";
|
||||
|
||||
switch (type) {
|
||||
case "b1":
|
||||
if (!(addr[0] & 2))
|
||||
idx--;
|
||||
addr[0] |= 2;
|
||||
addr[0] ^= idx << 2;
|
||||
break;
|
||||
case "b5":
|
||||
if (mbssid)
|
||||
addr[0] |= 2;
|
||||
addr[5] ^= idx;
|
||||
break;
|
||||
default:
|
||||
for (let i = 5; i > 0; i--) {
|
||||
addr[i] += idx;
|
||||
if (addr[i] < 256)
|
||||
break;
|
||||
addr[i] %= 256;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return macaddr_join(addr);
|
||||
},
|
||||
|
||||
macaddr_next: function(val) {
|
||||
let data = this.macaddr_options ?? {};
|
||||
let list = this.macaddr_list;
|
||||
|
||||
for (let i = 0; i < 32; i++) {
|
||||
data.id = i;
|
||||
|
||||
let mac = this.macaddr_generate(data);
|
||||
if (!mac)
|
||||
return null;
|
||||
|
||||
if (list[mac] != null)
|
||||
continue;
|
||||
|
||||
list[mac] = val != null ? val : -1;
|
||||
return mac;
|
||||
}
|
||||
},
|
||||
|
||||
for_each_wdev: function(cb) {
|
||||
let wdevs = glob(`/sys/class/ieee80211/${this.name}/device/net/*`);
|
||||
wdevs = map(wdevs, (arg) => basename(arg));
|
||||
for (let wdev in wdevs) {
|
||||
if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != this.name)
|
||||
continue;
|
||||
|
||||
cb(wdev);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function phy_open(phy)
|
||||
{
|
||||
let phyidx = readfile(`/sys/class/ieee80211/${phy}/index`);
|
||||
if (!phyidx)
|
||||
return null;
|
||||
|
||||
return proto({
|
||||
name: phy,
|
||||
idx: int(phyidx)
|
||||
}, phy_proto);
|
||||
}
|
||||
|
||||
const vlist_proto = {
|
||||
update: function(values, arg) {
|
||||
let data = this.data;
|
||||
let cb = this.cb;
|
||||
let seq = { };
|
||||
let new_data = {};
|
||||
let old_data = {};
|
||||
|
||||
this.data = new_data;
|
||||
|
||||
if (type(values) == "object") {
|
||||
for (let key in values) {
|
||||
old_data[key] = data[key];
|
||||
new_data[key] = values[key];
|
||||
delete data[key];
|
||||
}
|
||||
} else {
|
||||
for (let val in values) {
|
||||
let cur_key = val[0];
|
||||
let cur_obj = val[1];
|
||||
|
||||
old_data[cur_key] = data[cur_key];
|
||||
new_data[cur_key] = val[1];
|
||||
delete data[cur_key];
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in data) {
|
||||
cb(null, data[key], arg);
|
||||
delete data[key];
|
||||
}
|
||||
for (let key in new_data)
|
||||
cb(new_data[key], old_data[key], arg);
|
||||
}
|
||||
};
|
||||
|
||||
function is_equal(val1, val2) {
|
||||
let t1 = type(val1);
|
||||
|
||||
if (t1 != type(val2))
|
||||
return false;
|
||||
|
||||
if (t1 == "array") {
|
||||
if (length(val1) != length(val2))
|
||||
return false;
|
||||
|
||||
for (let i = 0; i < length(val1); i++)
|
||||
if (!is_equal(val1[i], val2[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
} else if (t1 == "object") {
|
||||
for (let key in val1)
|
||||
if (!is_equal(val1[key], val2[key]))
|
||||
return false;
|
||||
for (let key in val2)
|
||||
if (val1[key] == null)
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
return val1 == val2;
|
||||
}
|
||||
}
|
||||
|
||||
function vlist_new(cb) {
|
||||
return proto({
|
||||
cb: cb,
|
||||
data: {}
|
||||
}, vlist_proto);
|
||||
}
|
||||
|
||||
export { wdev_remove, wdev_create, wdev_set_mesh_params, wdev_set_up, is_equal, vlist_new, phy_is_fullmac, phy_open };
|
||||
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env ucode
|
||||
'use strict';
|
||||
import { vlist_new, is_equal, wdev_create, wdev_set_mesh_params, wdev_remove, wdev_set_up, phy_open } from "/usr/share/hostap/common.uc";
|
||||
import { readfile, writefile, basename, readlink, glob } from "fs";
|
||||
let libubus = require("ubus");
|
||||
|
||||
let keep_devices = {};
|
||||
let phy = shift(ARGV);
|
||||
let command = shift(ARGV);
|
||||
let phydev;
|
||||
|
||||
function iface_stop(wdev)
|
||||
{
|
||||
if (keep_devices[wdev.ifname])
|
||||
return;
|
||||
|
||||
wdev_remove(wdev.ifname);
|
||||
}
|
||||
|
||||
function iface_start(wdev)
|
||||
{
|
||||
let ifname = wdev.ifname;
|
||||
|
||||
if (readfile(`/sys/class/net/${ifname}/ifindex`)) {
|
||||
wdev_set_up(ifname, false);
|
||||
wdev_remove(ifname);
|
||||
}
|
||||
let wdev_config = {};
|
||||
for (let key in wdev)
|
||||
wdev_config[key] = wdev[key];
|
||||
if (!wdev_config.macaddr && wdev.mode != "monitor")
|
||||
wdev_config.macaddr = phydev.macaddr_next();
|
||||
wdev_create(phy, ifname, wdev_config);
|
||||
wdev_set_up(ifname, true);
|
||||
let htmode = wdev.htmode || "NOHT";
|
||||
if (wdev.freq)
|
||||
system(`iw dev ${ifname} set freq ${wdev.freq} ${htmode}`);
|
||||
if (wdev.mode == "adhoc") {
|
||||
let cmd = ["iw", "dev", ifname, "ibss", "join", wdev.ssid, wdev.freq, htmode, "fixed-freq" ];
|
||||
if (wdev.bssid)
|
||||
push(cmd, wdev.bssid);
|
||||
for (let key in [ "beacon-interval", "basic-rates", "mcast-rate", "keys" ])
|
||||
if (wdev[key])
|
||||
push(cmd, key, wdev[key]);
|
||||
system(cmd);
|
||||
} else if (wdev.mode == "mesh") {
|
||||
let cmd = [ "iw", "dev", ifname, "mesh", "join", wdev.ssid, "freq", wdev.freq, htmode ];
|
||||
for (let key in [ "mcast-rate", "beacon-interval" ])
|
||||
if (wdev[key])
|
||||
push(cmd, key, wdev[key]);
|
||||
system(cmd);
|
||||
|
||||
wdev_set_mesh_params(ifname, wdev);
|
||||
}
|
||||
}
|
||||
|
||||
function iface_cb(new_if, old_if)
|
||||
{
|
||||
if (old_if && new_if && is_equal(old_if, new_if))
|
||||
return;
|
||||
|
||||
if (old_if)
|
||||
iface_stop(old_if);
|
||||
if (new_if)
|
||||
iface_start(new_if);
|
||||
}
|
||||
|
||||
function drop_inactive(config)
|
||||
{
|
||||
for (let key in config) {
|
||||
if (!readfile(`/sys/class/net/${key}/ifindex`))
|
||||
delete config[key];
|
||||
}
|
||||
}
|
||||
|
||||
function add_ifname(config)
|
||||
{
|
||||
for (let key in config)
|
||||
config[key].ifname = key;
|
||||
}
|
||||
|
||||
function delete_ifname(config)
|
||||
{
|
||||
for (let key in config)
|
||||
delete config[key].ifname;
|
||||
}
|
||||
|
||||
function add_existing(phy, config)
|
||||
{
|
||||
let wdevs = glob(`/sys/class/ieee80211/${phy}/device/net/*`);
|
||||
wdevs = map(wdevs, (arg) => basename(arg));
|
||||
for (let wdev in wdevs) {
|
||||
if (config[wdev])
|
||||
continue;
|
||||
|
||||
if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != phy)
|
||||
continue;
|
||||
|
||||
if (trim(readfile(`/sys/class/net/${wdev}/operstate`)) == "down")
|
||||
config[wdev] = {};
|
||||
}
|
||||
}
|
||||
|
||||
function usage()
|
||||
{
|
||||
warn(`Usage: ${basename(sourcepath())} <phy> <command> [<arguments>]
|
||||
|
||||
Commands:
|
||||
set_config <config> [<device]...] - set phy configuration
|
||||
get_macaddr <id> - get phy MAC address for vif index <id>
|
||||
`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const commands = {
|
||||
set_config: function(args) {
|
||||
let statefile = `/var/run/wdev-${phy}.json`;
|
||||
|
||||
let new_config = shift(args);
|
||||
for (let dev in ARGV)
|
||||
keep_devices[dev] = true;
|
||||
|
||||
if (!new_config)
|
||||
usage();
|
||||
|
||||
new_config = json(new_config);
|
||||
if (!new_config) {
|
||||
warn("Invalid configuration\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let old_config = readfile(statefile);
|
||||
if (old_config)
|
||||
old_config = json(old_config);
|
||||
|
||||
let config = vlist_new(iface_cb);
|
||||
if (type(old_config) == "object")
|
||||
config.data = old_config;
|
||||
|
||||
add_existing(phy, config.data);
|
||||
add_ifname(config.data);
|
||||
drop_inactive(config.data);
|
||||
|
||||
let ubus = libubus.connect();
|
||||
let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phy });
|
||||
let macaddr_list = [];
|
||||
if (type(data) == "object" && data.macaddr)
|
||||
macaddr_list = data.macaddr;
|
||||
ubus.disconnect();
|
||||
phydev.macaddr_init(macaddr_list);
|
||||
|
||||
add_ifname(new_config);
|
||||
config.update(new_config);
|
||||
|
||||
drop_inactive(config.data);
|
||||
delete_ifname(config.data);
|
||||
writefile(statefile, sprintf("%J", config.data));
|
||||
},
|
||||
get_macaddr: function(args) {
|
||||
let data = {};
|
||||
|
||||
for (let arg in args) {
|
||||
arg = split(arg, "=", 2);
|
||||
data[arg[0]] = arg[1];
|
||||
}
|
||||
|
||||
let macaddr = phydev.macaddr_generate(data);
|
||||
if (!macaddr) {
|
||||
warn(`Could not get MAC address for phy ${phy}\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
print(macaddr + "\n");
|
||||
},
|
||||
};
|
||||
|
||||
if (!phy || !command | !commands[command])
|
||||
usage();
|
||||
|
||||
phydev = phy_open(phy);
|
||||
if (!phydev) {
|
||||
warn(`PHY ${phy} does not exist\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
commands[command](ARGV);
|
||||
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env ucode
|
||||
'use strict';
|
||||
import { readfile, writefile, realpath, glob, basename, unlink, open, rename } from "fs";
|
||||
import { is_equal } from "/usr/share/hostap/common.uc";
|
||||
let nl = require("nl80211");
|
||||
|
||||
let board_file = "/etc/board.json";
|
||||
let prev_board_data = json(readfile(board_file));
|
||||
let board_data = json(readfile(board_file));
|
||||
|
||||
function phy_idx(name) {
|
||||
return +rtrim(readfile(`/sys/class/ieee80211/${name}/index`));
|
||||
}
|
||||
|
||||
function phy_path(name) {
|
||||
let devpath = realpath(`/sys/class/ieee80211/${name}/device`);
|
||||
|
||||
devpath = replace(devpath, /^\/sys\/devices\//, "");
|
||||
if (match(devpath, /^platform\/.*\/pci/))
|
||||
devpath = replace(devpath, /^platform\//, "");
|
||||
let dev_phys = map(glob(`/sys/class/ieee80211/${name}/device/ieee80211/*`), basename);
|
||||
sort(dev_phys, (a, b) => phy_idx(a) - phy_idx(b));
|
||||
|
||||
let ofs = index(dev_phys, name);
|
||||
if (ofs > 0)
|
||||
devpath += `+${ofs}`;
|
||||
|
||||
return devpath;
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
let wlan = board_data.wlan;
|
||||
|
||||
for (let name in wlan)
|
||||
if (substr(name, 0, 3) == "phy")
|
||||
delete wlan[name];
|
||||
else
|
||||
delete wlan[name].info;
|
||||
}
|
||||
|
||||
function wiphy_get_entry(phy, path) {
|
||||
board_data.wlan ??= {};
|
||||
|
||||
let wlan = board_data.wlan;
|
||||
for (let name in wlan)
|
||||
if (wlan[name].path == path)
|
||||
return wlan[name];
|
||||
|
||||
wlan[phy] = {
|
||||
path: path
|
||||
};
|
||||
|
||||
return wlan[phy];
|
||||
}
|
||||
|
||||
function freq_to_channel(freq) {
|
||||
if (freq < 1000)
|
||||
return 0;
|
||||
if (freq == 2484)
|
||||
return 14;
|
||||
if (freq == 5935)
|
||||
return 2;
|
||||
if (freq < 2484)
|
||||
return (freq - 2407) / 5;
|
||||
if (freq >= 4910 && freq <= 4980)
|
||||
return (freq - 4000) / 5;
|
||||
if (freq < 5950)
|
||||
return (freq - 5000) / 5;
|
||||
if (freq <= 45000)
|
||||
return (freq - 5950) / 5;
|
||||
if (freq >= 58320 && freq <= 70200)
|
||||
return (freq - 56160) / 2160;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function wiphy_detect() {
|
||||
let phys = nl.request(nl.const.NL80211_CMD_GET_WIPHY, nl.const.NLM_F_DUMP, { split_wiphy_dump: true });
|
||||
if (!phys)
|
||||
return;
|
||||
|
||||
for (let phy in phys) {
|
||||
let name = phy.wiphy_name;
|
||||
let path = phy_path(name);
|
||||
let info = {
|
||||
antenna_rx: phy.wiphy_antenna_avail_rx,
|
||||
antenna_tx: phy.wiphy_antenna_avail_tx,
|
||||
bands: {},
|
||||
};
|
||||
|
||||
let bands = info.bands;
|
||||
for (let band in phy.wiphy_bands) {
|
||||
if (!band || !band.freqs)
|
||||
continue;
|
||||
let freq = band.freqs[0].freq;
|
||||
let band_info = {};
|
||||
let band_name;
|
||||
if (freq > 50000)
|
||||
band_name = "60G";
|
||||
else if (freq > 5900)
|
||||
band_name = "6G";
|
||||
else if (freq > 4000)
|
||||
band_name = "5G";
|
||||
else if (freq > 2000)
|
||||
band_name = "2G";
|
||||
else
|
||||
continue;
|
||||
bands[band_name] = band_info;
|
||||
if (band.ht_capa > 0)
|
||||
band_info.ht = true;
|
||||
if (band.vht_capa > 0)
|
||||
band_info.vht = true;
|
||||
let he_phy_cap = 0;
|
||||
|
||||
for (let ift in band.iftype_data) {
|
||||
if (!ift.he_cap_phy)
|
||||
continue;
|
||||
|
||||
band_info.he = true;
|
||||
he_phy_cap |= ift.he_cap_phy[0];
|
||||
/* TODO: EHT */
|
||||
}
|
||||
|
||||
if (band_name != "2G" &&
|
||||
(he_phy_cap & 0x18) || ((band.vht_capa >> 2) & 0x3))
|
||||
band_info.max_width = 160;
|
||||
else if (band_name != "2G" &&
|
||||
(he_phy_cap & 4) || band.vht_capa > 0)
|
||||
band_info.max_width = 80;
|
||||
else if ((band.ht_capa & 0x2) || (he_phy_cap & 0x2))
|
||||
band_info.max_width = 40;
|
||||
else
|
||||
band_info.max_width = 20;
|
||||
|
||||
let modes = band_info.modes = [ "NOHT" ];
|
||||
if (band_info.ht)
|
||||
push(modes, "HT20");
|
||||
if (band_info.vht)
|
||||
push(modes, "VHT20");
|
||||
if (band_info.he)
|
||||
push(modes, "HE20");
|
||||
if (band.ht_capa & 0x2) {
|
||||
push(modes, "HT40");
|
||||
if (band_info.vht)
|
||||
push(modes, "VHT40")
|
||||
}
|
||||
if (he_phy_cap & 0x2)
|
||||
push(modes, "HE40");
|
||||
|
||||
for (let freq in band.freqs) {
|
||||
if (freq.disabled)
|
||||
continue;
|
||||
let chan = freq_to_channel(freq.freq);
|
||||
if (!chan)
|
||||
continue;
|
||||
band_info.default_channel = chan;
|
||||
break;
|
||||
}
|
||||
|
||||
if (band_name == "2G")
|
||||
continue;
|
||||
if (band_info.vht)
|
||||
push(modes, "VHT80");
|
||||
if (he_phy_cap & 4)
|
||||
push(modes, "HE80");
|
||||
if ((band.vht_capa >> 2) & 0x3)
|
||||
push(modes, "VHT160");
|
||||
if (he_phy_cap & 0x18)
|
||||
push(modes, "HE160");
|
||||
}
|
||||
|
||||
let entry = wiphy_get_entry(name, path);
|
||||
entry.info = info;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup();
|
||||
wiphy_detect();
|
||||
if (!is_equal(prev_board_data, board_data)) {
|
||||
let new_file = board_file + ".new";
|
||||
unlink(new_file);
|
||||
let f = open(new_file, "wx");
|
||||
if (!f)
|
||||
exit(1);
|
||||
f.write(sprintf("%.J\n", board_data));
|
||||
f.close();
|
||||
rename(new_file, board_file);
|
||||
}
|
||||
Reference in New Issue
Block a user