diff options
| author | Felix Fietkau <nbd@nbd.name> | 2024-01-26 11:03:39 +0100 |
|---|---|---|
| committer | Felix Fietkau <nbd@nbd.name> | 2024-02-03 16:16:36 +0100 |
| commit | 2716853132d23a35fc18cbe8b438aa70e4d2c563 (patch) | |
| tree | ba91e297003c55e428748eb8184ff9ea90c33cca /package/network | |
| parent | 32b4498c15964f4fa1da76fa5610c10683677421 (diff) | |
| download | upstream-2716853132d23a35fc18cbe8b438aa70e4d2c563.tar.gz upstream-2716853132d23a35fc18cbe8b438aa70e4d2c563.tar.bz2 upstream-2716853132d23a35fc18cbe8b438aa70e4d2c563.zip | |
wifi-scripts: add new package, move wifi scripts to a single place
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'package/network')
| -rw-r--r-- | package/network/config/netifd/Makefile | 5 | ||||
| -rw-r--r-- | package/network/config/wifi-scripts/Makefile | 45 | ||||
| -rw-r--r-- | package/network/config/wifi-scripts/files/etc/hotplug.d/ieee80211/10-wifi-detect | 5 | ||||
| -rw-r--r-- | package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh (renamed from package/network/services/hostapd/files/hostapd.sh) | 0 | ||||
| -rw-r--r-- | package/network/config/wifi-scripts/files/lib/netifd/netifd-wireless.sh | 439 | ||||
| -rwxr-xr-x | package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh | 1197 | ||||
| -rw-r--r-- | package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh | 217 | ||||
| -rwxr-xr-x | package/network/config/wifi-scripts/files/sbin/wifi | 272 | ||||
| -rw-r--r-- | package/network/config/wifi-scripts/files/usr/share/hostap/common.uc (renamed from package/network/services/hostapd/files/common.uc) | 0 | ||||
| -rw-r--r-- | package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc (renamed from package/network/services/hostapd/files/wdev.uc) | 0 | ||||
| -rw-r--r-- | package/network/services/hostapd/Makefile | 3 |
11 files changed, 2179 insertions, 4 deletions
diff --git a/package/network/config/netifd/Makefile b/package/network/config/netifd/Makefile index 558463e8ea5..ba4516c1dc6 100644 --- a/package/network/config/netifd/Makefile +++ b/package/network/config/netifd/Makefile @@ -43,7 +43,10 @@ define Package/netifd/install $(INSTALL_BIN) $(PKG_BUILD_DIR)/netifd $(1)/sbin/ $(CP) ./files/* $(1)/ $(INSTALL_DIR) $(1)/etc/udhcpc.user.d/ - $(CP) $(PKG_BUILD_DIR)/scripts/* $(1)/lib/netifd/ + $(CP) \ + $(PKG_BUILD_DIR)/scripts/utils.sh \ + $(PKG_BUILD_DIR)/scripts/netifd-proto.sh \ + $(1)/lib/netifd/ endef $(eval $(call BuildPackage,netifd)) diff --git a/package/network/config/wifi-scripts/Makefile b/package/network/config/wifi-scripts/Makefile new file mode 100644 index 00000000000..e57f97fef57 --- /dev/null +++ b/package/network/config/wifi-scripts/Makefile @@ -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-ubus + TITLE:=Wi-Fi configuration scripts + PKGARCH:=all +endef + +define Package/qos-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)) diff --git a/package/network/config/wifi-scripts/files/etc/hotplug.d/ieee80211/10-wifi-detect b/package/network/config/wifi-scripts/files/etc/hotplug.d/ieee80211/10-wifi-detect new file mode 100644 index 00000000000..b8655526613 --- /dev/null +++ b/package/network/config/wifi-scripts/files/etc/hotplug.d/ieee80211/10-wifi-detect @@ -0,0 +1,5 @@ +#!/bin/sh + +[ "${ACTION}" = "add" ] && { + /sbin/wifi config +} diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh index 1a664abbf2f..1a664abbf2f 100644 --- a/package/network/services/hostapd/files/hostapd.sh +++ b/package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh diff --git a/package/network/config/wifi-scripts/files/lib/netifd/netifd-wireless.sh b/package/network/config/wifi-scripts/files/lib/netifd/netifd-wireless.sh new file mode 100644 index 00000000000..5b852e09376 --- /dev/null +++ b/package/network/config/wifi-scripts/files/lib/netifd/netifd-wireless.sh @@ -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 +} diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh b/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh new file mode 100755 index 00000000000..49ffb219bcd --- /dev/null +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh @@ -0,0 +1,1197 @@ +#!/bin/sh +. /lib/netifd/netifd-wireless.sh +. /lib/netifd/hostapd.sh +. /lib/functions/system.sh + +init_wireless_driver "$@" + +MP_CONFIG_INT="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" +MP_CONFIG_BOOL="mesh_auto_open_plinks mesh_fwding" +MP_CONFIG_STRING="mesh_power_mode" + +wdev_tool() { + ucode /usr/share/hostap/wdev.uc "$@" +} + +ubus_call() { + flock /var/run/hostapd.lock ubus call "$@" +} + +drv_mac80211_init_device_config() { + hostapd_common_add_device_config + + config_add_string path phy 'macaddr:macaddr' + config_add_string tx_burst + config_add_string distance + config_add_int beacon_int chanbw frag rts + config_add_int rxantenna txantenna txpower min_tx_power + config_add_int num_global_macaddr multiple_bssid + config_add_boolean noscan ht_coex acs_exclude_dfs background_radar + config_add_array ht_capab + config_add_array channels + config_add_array scan_list + config_add_boolean \ + rxldpc \ + short_gi_80 \ + short_gi_160 \ + tx_stbc_2by1 \ + su_beamformer \ + su_beamformee \ + mu_beamformer \ + mu_beamformee \ + he_su_beamformer \ + he_su_beamformee \ + he_mu_beamformer \ + vht_txop_ps \ + htc_vht \ + rx_antenna_pattern \ + tx_antenna_pattern \ + he_spr_sr_control \ + he_spr_psr_enabled \ + he_bss_color_enabled \ + he_twt_required + config_add_int \ + beamformer_antennas \ + beamformee_antennas \ + vht_max_a_mpdu_len_exp \ + vht_max_mpdu \ + vht_link_adapt \ + vht160 \ + rx_stbc \ + tx_stbc \ + he_bss_color \ + he_spr_non_srg_obss_pd_max_offset + config_add_boolean \ + ldpc \ + greenfield \ + short_gi_20 \ + short_gi_40 \ + max_amsdu \ + dsss_cck_40 +} + +drv_mac80211_init_iface_config() { + hostapd_common_add_bss_config + + config_add_string 'macaddr:macaddr' ifname + + config_add_boolean wds powersave enable + config_add_string wds_bridge + config_add_int maxassoc + config_add_int max_listen_int + config_add_int dtim_period + config_add_int start_disabled + + # mesh + config_add_string mesh_id + config_add_int $MP_CONFIG_INT + config_add_boolean $MP_CONFIG_BOOL + config_add_string $MP_CONFIG_STRING +} + +mac80211_add_capabilities() { + local __var="$1"; shift + local __mask="$1"; shift + local __out= oifs + + oifs="$IFS" + IFS=: + for capab in "$@"; do + set -- $capab + + [ "$(($4))" -gt 0 ] || continue + [ "$(($__mask & $2))" -eq "$((${3:-$2}))" ] || continue + __out="$__out[$1]" + done + IFS="$oifs" + + export -n -- "$__var=$__out" +} + +mac80211_add_he_capabilities() { + local __out= oifs + + oifs="$IFS" + IFS=: + for capab in "$@"; do + set -- $capab + [ "$(($4))" -gt 0 ] || continue + [ "$(((0x$2) & $3))" -gt 0 ] || { + eval "$1=0" + continue + } + append base_cfg "$1=1" "$N" + done + IFS="$oifs" +} + +mac80211_hostapd_setup_base() { + local phy="$1" + + json_select config + + [ "$auto_channel" -gt 0 ] && channel=acs_survey + + [ "$auto_channel" -gt 0 ] && json_get_vars acs_exclude_dfs + [ -n "$acs_exclude_dfs" ] && [ "$acs_exclude_dfs" -gt 0 ] && + append base_cfg "acs_exclude_dfs=1" "$N" + + json_get_vars noscan ht_coex min_tx_power:0 tx_burst + json_get_values ht_capab_list ht_capab + json_get_values channel_list channels + + [ "$auto_channel" = 0 ] && [ -z "$channel_list" ] && \ + channel_list="$channel" + + [ "$min_tx_power" -gt 0 ] && append base_cfg "min_tx_power=$min_tx_power" "$N" + + set_default noscan 0 + + [ "$noscan" -gt 0 ] && hostapd_noscan=1 + [ "$tx_burst" = 0 ] && tx_burst= + + chan_ofs=0 + [ "$band" = "6g" ] && chan_ofs=1 + + ieee80211n=1 + ht_capab= + case "$htmode" in + VHT20|HT20|HE20) ;; + HT40*|VHT40|VHT80|VHT160|HE40|HE80|HE160) + case "$hwmode" in + a) + case "$(( (($channel / 4) + $chan_ofs) % 2 ))" in + 1) ht_capab="[HT40+]";; + 0) ht_capab="[HT40-]";; + esac + ;; + *) + case "$htmode" in + HT40+) ht_capab="[HT40+]";; + HT40-) ht_capab="[HT40-]";; + *) + if [ "$channel" -lt 7 ]; then + ht_capab="[HT40+]" + else + ht_capab="[HT40-]" + fi + ;; + esac + ;; + esac + [ "$auto_channel" -gt 0 ] && ht_capab="[HT40+]" + ;; + *) ieee80211n= ;; + esac + + [ -n "$ieee80211n" ] && { + append base_cfg "ieee80211n=1" "$N" + + set_default ht_coex 0 + append base_cfg "ht_coex=$ht_coex" "$N" + + json_get_vars \ + ldpc:1 \ + greenfield:0 \ + short_gi_20:1 \ + short_gi_40:1 \ + tx_stbc:1 \ + rx_stbc:3 \ + max_amsdu:1 \ + dsss_cck_40:1 + + ht_cap_mask=0 + for cap in $(iw phy "$phy" info | grep 'Capabilities:' | cut -d: -f2); do + ht_cap_mask="$(($ht_cap_mask | $cap))" + done + + cap_rx_stbc=$((($ht_cap_mask >> 8) & 3)) + [ "$rx_stbc" -lt "$cap_rx_stbc" ] && cap_rx_stbc="$rx_stbc" + ht_cap_mask="$(( ($ht_cap_mask & ~(0x300)) | ($cap_rx_stbc << 8) ))" + + mac80211_add_capabilities ht_capab_flags $ht_cap_mask \ + LDPC:0x1::$ldpc \ + GF:0x10::$greenfield \ + SHORT-GI-20:0x20::$short_gi_20 \ + SHORT-GI-40:0x40::$short_gi_40 \ + TX-STBC:0x80::$tx_stbc \ + RX-STBC1:0x300:0x100:1 \ + RX-STBC12:0x300:0x200:1 \ + RX-STBC123:0x300:0x300:1 \ + MAX-AMSDU-7935:0x800::$max_amsdu \ + DSSS_CCK-40:0x1000::$dsss_cck_40 + + ht_capab="$ht_capab$ht_capab_flags" + [ -n "$ht_capab" ] && append base_cfg "ht_capab=$ht_capab" "$N" + } + + # 802.11ac + enable_ac=0 + vht_oper_chwidth=0 + vht_center_seg0= + + idx="$channel" + case "$htmode" in + VHT20|HE20) enable_ac=1;; + VHT40|HE40) + case "$(( (($channel / 4) + $chan_ofs) % 2 ))" in + 1) idx=$(($channel + 2));; + 0) idx=$(($channel - 2));; + esac + enable_ac=1 + vht_center_seg0=$idx + ;; + VHT80|HE80) + case "$(( (($channel / 4) + $chan_ofs) % 4 ))" in + 1) idx=$(($channel + 6));; + 2) idx=$(($channel + 2));; + 3) idx=$(($channel - 2));; + 0) idx=$(($channel - 6));; + esac + enable_ac=1 + vht_oper_chwidth=1 + vht_center_seg0=$idx + ;; + VHT160|HE160) + if [ "$band" = "6g" ]; then + case "$channel" in + 1|5|9|13|17|21|25|29) idx=15;; + 33|37|41|45|49|53|57|61) idx=47;; + 65|69|73|77|81|85|89|93) idx=79;; + 97|101|105|109|113|117|121|125) idx=111;; + 129|133|137|141|145|149|153|157) idx=143;; + 161|165|169|173|177|181|185|189) idx=175;; + 193|197|201|205|209|213|217|221) idx=207;; + esac + else + case "$channel" in + 36|40|44|48|52|56|60|64) idx=50;; + 100|104|108|112|116|120|124|128) idx=114;; + esac + fi + enable_ac=1 + vht_oper_chwidth=2 + vht_center_seg0=$idx + ;; + esac + [ "$band" = "5g" ] && { + json_get_vars background_radar:0 + + [ "$background_radar" -eq 1 ] && append base_cfg "enable_background_radar=1" "$N" + } + [ "$band" = "6g" ] && { + op_class= + case "$htmode" in + HE20) op_class=131;; + HE*) op_class=$((132 + $vht_oper_chwidth)) + esac + [ -n "$op_class" ] && append base_cfg "op_class=$op_class" "$N" + } + [ "$hwmode" = "a" ] || enable_ac=0 + + if [ "$enable_ac" != "0" ]; then + json_get_vars \ + rxldpc:1 \ + short_gi_80:1 \ + short_gi_160:1 \ + tx_stbc_2by1:1 \ + su_beamformer:1 \ + su_beamformee:1 \ + mu_beamformer:1 \ + mu_beamformee:1 \ + vht_txop_ps:1 \ + htc_vht:1 \ + beamformee_antennas:4 \ + beamformer_antennas:4 \ + rx_antenna_pattern:1 \ + tx_antenna_pattern:1 \ + vht_max_a_mpdu_len_exp:7 \ + vht_max_mpdu:11454 \ + rx_stbc:4 \ + vht_link_adapt:3 \ + vht160:2 + + set_default tx_burst 2.0 + append base_cfg "ieee80211ac=1" "$N" + vht_cap=0 + for cap in $(iw phy "$phy" info | awk -F "[()]" '/VHT Capabilities/ { print $2 }'); do + vht_cap="$(($vht_cap | $cap))" + done + + append base_cfg "vht_oper_chwidth=$vht_oper_chwidth" "$N" + append base_cfg "vht_oper_centr_freq_seg0_idx=$vht_center_seg0" "$N" + + cap_rx_stbc=$((($vht_cap >> 8) & 7)) + [ "$rx_stbc" -lt "$cap_rx_stbc" ] && cap_rx_stbc="$rx_stbc" + vht_cap="$(( ($vht_cap & ~(0x700)) | ($cap_rx_stbc << 8) ))" + + [ "$vht_oper_chwidth" -lt 2 ] && { + vht160=0 + short_gi_160=0 + } + + mac80211_add_capabilities vht_capab $vht_cap \ + RXLDPC:0x10::$rxldpc \ + SHORT-GI-80:0x20::$short_gi_80 \ + SHORT-GI-160:0x40::$short_gi_160 \ + TX-STBC-2BY1:0x80::$tx_stbc_2by1 \ + SU-BEAMFORMER:0x800::$su_beamformer \ + SU-BEAMFORMEE:0x1000::$su_beamformee \ + MU-BEAMFORMER:0x80000::$mu_beamformer \ + MU-BEAMFORMEE:0x100000::$mu_beamformee \ + VHT-TXOP-PS:0x200000::$vht_txop_ps \ + HTC-VHT:0x400000::$htc_vht \ + RX-ANTENNA-PATTERN:0x10000000::$rx_antenna_pattern \ + TX-ANTENNA-PATTERN:0x20000000::$tx_antenna_pattern \ + RX-STBC-1:0x700:0x100:1 \ + RX-STBC-12:0x700:0x200:1 \ + RX-STBC-123:0x700:0x300:1 \ + RX-STBC-1234:0x700:0x400:1 \ + + [ "$(($vht_cap & 0x800))" -gt 0 -a "$su_beamformer" -gt 0 ] && { + cap_ant="$(( ( ($vht_cap >> 16) & 3 ) + 1 ))" + [ "$cap_ant" -gt "$beamformer_antennas" ] && cap_ant="$beamformer_antennas" + [ "$cap_ant" -gt 1 ] && vht_capab="$vht_capab[SOUNDING-DIMENSION-$cap_ant]" + } + + [ "$(($vht_cap & 0x1000))" -gt 0 -a "$su_beamformee" -gt 0 ] && { + cap_ant="$(( ( ($vht_cap >> 13) & 3 ) + 1 ))" + [ "$cap_ant" -gt "$beamformee_antennas" ] && cap_ant="$beamformee_antennas" + [ "$cap_ant" -gt 1 ] && vht_capab="$vht_capab[BF-ANTENNA-$cap_ant]" + } + + # supported Channel widths + vht160_hw=0 + [ "$(($vht_cap & 12))" -eq 4 -a 1 -le "$vht160" ] && \ + vht160_hw=1 + [ "$(($vht_cap & 12))" -eq 8 -a 2 -le "$vht160" ] && \ + vht160_hw=2 + [ "$vht160_hw" = 1 ] && vht_capab="$vht_capab[VHT160]" + [ "$vht160_hw" = 2 ] && vht_capab="$vht_capab[VHT160-80PLUS80]" + + # maximum MPDU length + vht_max_mpdu_hw=3895 + [ "$(($vht_cap & 3))" -ge 1 -a 7991 -le "$vht_max_mpdu" ] && \ + vht_max_mpdu_hw=7991 + [ "$(($vht_cap & 3))" -ge 2 -a 11454 -le "$vht_max_mpdu" ] && \ + vht_max_mpdu_hw=11454 + [ "$vht_max_mpdu_hw" != 3895 ] && \ + vht_capab="$vht_capab[MAX-MPDU-$vht_max_mpdu_hw]" + + # maximum A-MPDU length exponent + vht_max_a_mpdu_len_exp_hw=0 + [ "$(($vht_cap & 58720256))" -ge 8388608 -a 1 -le "$vht_max_a_mpdu_len_exp" ] && \ + vht_max_a_mpdu_len_exp_hw=1 + [ "$(($vht_cap & 58720256))" -ge 16777216 -a 2 -le "$vht_max_a_mpdu_len_exp" ] && \ + vht_max_a_mpdu_len_exp_hw=2 + [ "$(($vht_cap & 58720256))" -ge 25165824 -a 3 -le "$vht_max_a_mpdu_len_exp" ] && \ + vht_max_a_mpdu_len_exp_hw=3 + [ "$(($vht_cap & 58720256))" -ge 33554432 -a 4 -le "$vht_max_a_mpdu_len_exp" ] && \ + vht_max_a_mpdu_len_exp_hw=4 + [ "$(($vht_cap & 58720256))" -ge 41943040 -a 5 -le "$vht_max_a_mpdu_len_exp" ] && \ + vht_max_a_mpdu_len_exp_hw=5 + [ "$(($vht_cap & 58720256))" -ge 50331648 -a 6 -le "$vht_max_a_mpdu_len_exp" ] && \ + vht_max_a_mpdu_len_exp_hw=6 + [ "$(($vht_cap & 58720256))" -ge 58720256 -a 7 -le "$vht_max_a_mpdu_len_exp" ] && \ + vht_max_a_mpdu_len_exp_hw=7 + vht_capab="$vht_capab[MAX-A-MPDU-LEN-EXP$vht_max_a_mpdu_len_exp_hw]" + + # whether or not the STA supports link adaptation using VHT variant + vht_link_adapt_hw=0 + [ "$(($vht_cap & 201326592))" -ge 134217728 -a 2 -le "$vht_link_adapt" ] && \ + vht_link_adapt_hw=2 + [ "$(($vht_cap & 201326592))" -ge 201326592 -a 3 -le "$vht_link_adapt" ] && \ + vht_link_adapt_hw=3 + [ "$vht_link_adapt_hw" != 0 ] && \ + vht_capab="$vht_capab[VHT-LINK-ADAPT-$vht_link_adapt_hw]" + + [ -n "$vht_capab" ] && append base_cfg "vht_capab=$vht_capab" "$N" + fi + + # 802.11ax + enable_ax=0 + case "$htmode" in + HE*) enable_ax=1 ;; + esac + + if [ "$enable_ax" != "0" ]; then + json_get_vars \ + he_su_beamformer:1 \ + he_su_beamformee:1 \ + he_mu_beamformer:1 \ + he_twt_required:0 \ + he_spr_sr_control:3 \ + he_spr_psr_enabled:0 \ + he_spr_non_srg_obss_pd_max_offset:0 \ + he_bss_color:128 \ + he_bss_color_enabled:1 + + he_phy_cap=$(iw phy "$phy" info | sed -n '/HE Iftypes: AP/,$p' | awk -F "[()]" '/HE PHY Capabilities/ { print $2 }' | head -1) + he_phy_cap=${he_phy_cap:2} + he_mac_cap=$(iw phy "$phy" info | sed -n '/HE Iftypes: AP/,$p' | awk -F "[()]" '/HE MAC Capabilities/ { print $2 }' | head -1) + he_mac_cap=${he_mac_cap:2} + + append base_cfg "ieee80211ax=1" "$N" + [ "$hwmode" = "a" ] && { + append base_cfg "he_oper_chwidth=$vht_oper_chwidth" "$N" + append base_cfg "he_oper_centr_freq_seg0_idx=$vht_center_seg0" "$N" + } + + mac80211_add_he_capabilities \ + he_su_beamformer:${he_phy_cap:6:2}:0x80:$he_su_beamformer \ + he_su_beamformee:${he_phy_cap:8:2}:0x1:$he_su_beamformee \ + he_mu_beamformer:${he_phy_cap:8:2}:0x2:$he_mu_beamformer \ + he_spr_psr_enabled:${he_phy_cap:14:2}:0x1:$he_spr_psr_enabled \ + he_twt_required:${he_mac_cap:0:2}:0x6:$he_twt_required + + if [ "$he_bss_color_enabled" -gt 0 ]; then + append base_cfg "he_bss_color=$he_bss_color" "$N" + [ "$he_spr_non_srg_obss_pd_max_offset" -gt 0 ] && { \ + append base_cfg "he_spr_non_srg_obss_pd_max_offset=$he_spr_non_srg_obss_pd_max_offset" "$N" + he_spr_sr_control=$((he_spr_sr_control | (1 << 2))) + } + [ "$he_spr_psr_enabled" -gt 0 ] || he_spr_sr_control=$((he_spr_sr_control | (1 << 0))) + append base_cfg "he_spr_sr_control=$he_spr_sr_control" "$N" + else + append base_cfg "he_bss_color_disabled=1" "$N" + fi + + + append base_cfg "he_default_pe_duration=4" "$N" + append base_cfg "he_rts_threshold=1023" "$N" + append base_cfg "he_mu_edca_qos_info_param_count=0" "$N" + append base_cfg "he_mu_edca_qos_info_q_ack=0" "$N" + append base_cfg "he_mu_edca_qos_info_queue_request=0" "$N" + append base_cfg "he_mu_edca_qos_info_txop_request=0" "$N" + append base_cfg "he_mu_edca_ac_be_aifsn=8" "$N" + append base_cfg "he_mu_edca_ac_be_aci=0" "$N" + append base_cfg "he_mu_edca_ac_be_ecwmin=9" "$N" + append base_cfg "he_mu_edca_ac_be_ecwmax=10" "$N" + append base_cfg "he_mu_edca_ac_be_timer=255" "$N" + append base_cfg "he_mu_edca_ac_bk_aifsn=15" "$N" + append base_cfg "he_mu_edca_ac_bk_aci=1" "$N" + append base_cfg "he_mu_edca_ac_bk_ecwmin=9" "$N" + append base_cfg "he_mu_edca_ac_bk_ecwmax=10" "$N" + append base_cfg "he_mu_edca_ac_bk_timer=255" "$N" + append base_cfg "he_mu_edca_ac_vi_ecwmin=5" "$N" + append base_cfg "he_mu_edca_ac_vi_ecwmax=7" "$N" + append base_cfg "he_mu_edca_ac_vi_aifsn=5" "$N" + append base_cfg "he_mu_edca_ac_vi_aci=2" "$N" + append base_cfg "he_mu_edca_ac_vi_timer=255" "$N" + append base_cfg "he_mu_edca_ac_vo_aifsn=5" "$N" + append base_cfg "he_mu_edca_ac_vo_aci=3" "$N" + append base_cfg "he_mu_edca_ac_vo_ecwmin=5" "$N" + append base_cfg "he_mu_edca_ac_vo_ecwmax=7" "$N" + append base_cfg "he_mu_edca_ac_vo_timer=255" "$N" + fi + + hostapd_prepare_device_config "$hostapd_conf_file" nl80211 + cat >> "$hostapd_conf_file" <<EOF +${channel:+channel=$channel} +${channel_list:+chanlist=$channel_list} +${hostapd_noscan:+noscan=1} +${tx_burst:+tx_queue_data2_burst=$tx_burst} +${multiple_bssid:+mbssid=$multiple_bssid} +#num_global_macaddr=$num_global_macaddr +$base_cfg + +EOF + json_select .. +} + +mac80211_hostapd_setup_bss() { + local phy="$1" + local ifname="$2" + local macaddr="$3" + local type="$4" + + hostapd_cfg= + append hostapd_cfg "$type=$ifname" "$N" + + hostapd_set_bss_options hostapd_cfg "$phy" "$vif" || return 1 + json_get_vars wds wds_bridge dtim_period max_listen_int start_disabled + + set_default wds 0 + set_default start_disabled 0 + + [ "$wds" -gt 0 ] && { + append hostapd_cfg "wds_sta=1" "$N" + [ -n "$wds_bridge" ] && append hostapd_cfg "wds_bridge=$wds_bridge" "$N" + } + [ "$staidx" -gt 0 -o "$start_disabled" -eq 1 ] && append hostapd_cfg "start_disabled=1" "$N" + + cat >> /var/run/hostapd-$phy.conf <<EOF +$hostapd_cfg +bssid=$macaddr +${default_macaddr:+#default_macaddr} +${dtim_period:+dtim_period=$dtim_period} +${max_listen_int:+max_listen_interval=$max_listen_int} +EOF +} + +mac80211_get_addr() { + local phy="$1" + local idx="$(($2 + 1))" + + head -n $idx /sys/class/ieee80211/${phy}/addresses | tail -n1 +} + +mac80211_generate_mac() { + local phy="$1" + local id="${macidx:-0}" + + wdev_tool "$phy" get_macaddr id=$id num_global=$num_global_macaddr mbssid=${multiple_bssid:-0} +} + +get_board_phy_name() ( + local path="$1" + local fallback_phy="" + + __check_phy() { + local val="$1" + local key="$2" + local ref_path="$3" + + json_select "$key" + json_get_vars path + json_select .. + + [ "${ref_path%+*}" = "$path" ] && fallback_phy=$key + [ "$ref_path" = "$path" ] || return 0 + + echo "$key" + exit + } + + json_load_file /etc/board.json + json_for_each_item __check_phy wlan "$path" + [ -n "$fallback_phy" ] && echo "${fallback_phy}.${path##*+}" +) + +rename_board_phy_by_path() { + local path="$1" + + local new_phy="$(get_board_phy_name "$path")" + [ -z "$new_phy" -o "$new_phy" = "$phy" ] && return + + iw "$phy" set name "$new_phy" && phy="$new_phy" +} + +rename_board_phy_by_name() ( + local phy="$1" + local suffix="${phy##*.}" + [ "$suffix" = "$phy" ] && suffix= + + json_load_file /etc/board.json + json_select wlan + json_select "${phy%.*}" || return 0 + json_get_vars path + + prev_phy="$(iwinfo nl80211 phyname "path=$path${suffix:++$suffix}")" + [ -n "$prev_phy" ] || return 0 + + [ "$prev_phy" = "$phy" ] && return 0 + + iw "$prev_phy" set name "$phy" +) + +find_phy() { + [ -n "$phy" ] && { + rename_board_phy_by_name "$phy" + [ -d /sys/class/ieee80211/$phy ] && return 0 + } + [ -n "$path" ] && { + phy="$(iwinfo nl80211 phyname "path=$path")" + [ -n "$phy" ] && { + rename_board_phy_by_path "$path" + return 0 + } + } + [ -n "$macaddr" ] && { + for phy in $(ls /sys/class/ieee80211 2>/dev/null); do + grep -i -q "$macaddr" "/sys/class/ieee80211/${phy}/macaddress" && { + path="$(iwinfo nl80211 path "$phy")" + rename_board_phy_by_path "$path" + return 0 + } + done + } + return 1 +} + +mac80211_check_ap() { + has_ap=1 +} + +mac80211_set_ifname() { + local phy="$1" + local prefix="$2" + eval "ifname=\"$phy-$prefix\${idx_$prefix:-0}\"; idx_$prefix=\$((\${idx_$prefix:-0 } + 1))" +} + +mac80211_prepare_vif() { + json_select config + + json_get_vars ifname mode ssid wds powersave macaddr enable wpa_psk_file vlan_file + + [ -n "$ifname" ] || { + local prefix; + + case "$mode" in + ap|sta|mesh) prefix=$mode;; + adhoc) prefix=ibss;; + monitor) prefix=mon;; + esac + + mac80211_set_ifname "$phy" "$prefix" + } + + append active_ifnames "$ifname" + set_default wds 0 + set_default powersave 0 + json_add_string _ifname "$ifname" + + default_macaddr= + if [ -z "$macaddr" ]; then + macaddr="$(mac80211_generate_mac $phy)" + macidx="$(($macidx + 1))" + default_macaddr=1 + elif [ "$macaddr" = 'random' ]; then + macaddr="$(macaddr_random)" + fi + json_add_string _macaddr "$macaddr" + json_add_string _default_macaddr "$default_macaddr" + json_select .. + + + [ "$mode" == "ap" ] && { + [ -z "$wpa_psk_file" ] && hostapd_set_psk "$ifname" + [ -z "$vlan_file" ] && hostapd_set_vlan "$ifname" + } + + json_select config + + # It is far easier to delete and create the desired interface + case "$mode" in + ap) + # Hostapd will handle recreating the interface and + # subsequent virtual APs belonging to the same PHY + if [ -n "$hostapd_ctrl" ]; then + type=bss + else + type=interface + fi + + mac80211_hostapd_setup_bss "$phy" "$ifname" "$macaddr" "$type" || return + + [ -n "$hostapd_ctrl" ] || { + ap_ifname="${ifname}" + hostapd_ctrl="${hostapd_ctrl:-/var/run/hostapd/$ifname}" + } + ;; + esac + + json_select .. +} + +mac80211_prepare_iw_htmode() { + case "$htmode" in + VHT20|HT20|HE20) iw_htmode=HT20;; + HT40*|VHT40|VHT160|HE40) + case "$band" in + 2g) + case "$htmode" in + HT40+) iw_htmode="HT40+";; + HT40-) iw_htmode="HT40-";; + *) + if [ "$channel" -lt 7 ]; then + iw_htmode="HT40+" + else + iw_htmode="HT40-" + fi + ;; + esac + ;; + *) + case "$(( ($channel / 4) % 2 ))" in + 1) iw_htmode="HT40+" ;; + 0) iw_htmode="HT40-";; + esac + ;; + esac + [ "$auto_channel" -gt 0 ] && iw_htmode="HT40+" + ;; + VHT80|HE80) + iw_htmode="80MHZ" + ;; + NONE|NOHT) + iw_htmode="NOHT" + ;; + *) iw_htmode="" ;; + esac +} + +mac80211_add_mesh_params() { + for var in $MP_CONFIG_INT $MP_CONFIG_BOOL $MP_CONFIG_STRING; do + eval "mp_val=\"\$$var\"" + [ -n "$mp_val" ] && json_add_string "$var" "$mp_val" + done +} + +mac80211_setup_adhoc() { + local enable=$1 + json_get_vars bssid ssid key mcast_rate + + NEWUMLIST="${NEWUMLIST}$ifname " + + [ "$enable" = 0 ] && { + ip link set dev "$ifname" down + return 0 + } + + keyspec= + [ "$auth_type" = "wep" ] && { + set_default key 1 + case "$key" in + [1234]) + local idx + for idx in 1 2 3 4; do + json_get_var ikey "key$idx" + + [ -n "$ikey" ] && { + ikey="$(($idx - 1)):$(prepare_key_wep "$ikey")" + [ $idx -eq $key ] && ikey="d:$ikey" + append keyspec "$ikey" + } + done + ;; + *) + append keyspec "d:0:$(prepare_key_wep "$key")" + ;; + esac + } + + brstr= + for br in $basic_rate_list; do + wpa_supplicant_add_rate brstr "$br" + done + + mcval= + [ -n "$mcast_rate" ] && wpa_supplicant_add_rate mcval "$mcast_rate" + + local prev + json_set_namespace wdev_uc prev + + json_add_object "$ifname" + json_add_string mode adhoc + [ -n "$default_macaddr" ] || json_add_string macaddr "$macaddr" + json_add_string ssid "$ssid" + json_add_string freq "$freq" + json_add_string htmode "$iw_htmode" + [ -n "$bssid" ] && json_add_string bssid "$bssid" + json_add_int beacon-interval "$beacon_int" + [ -n "$brstr" ] && json_add_string basic-rates "$brstr" + [ -n "$mcval" ] && json_add_string mcast-rate "$mcval" + [ -n "$keyspec" ] && json_add_string keys "$keyspec" + json_close_object + + json_set_namespace "$prev" +} + +mac80211_setup_mesh() { + json_get_vars ssid mesh_id mcast_rate + + mcval= + [ -n "$mcast_rate" ] && wpa_supplicant_add_rate mcval "$mcast_rate" + [ -n "$mesh_id" ] && ssid="$mesh_id" + + local prev + json_set_namespace wdev_uc prev + + json_add_object "$ifname" + json_add_string mode mesh + [ -n "$default_macaddr" ] || json_add_string macaddr "$macaddr" + json_add_string ssid "$ssid" + json_add_string freq "$freq" + json_add_string htmode "$iw_htmode" + [ -n "$mcval" ] && json_add_string mcast-rate "$mcval" + json_add_int beacon-interval "$beacon_int" + mac80211_add_mesh_params + + json_close_object + + json_set_namespace "$prev" +} + +mac80211_setup_monitor() { + local prev + json_set_namespace wdev_uc prev + + json_add_object "$ifname" + json_add_string mode monitor + [ -n "$freq" ] && json_add_string freq "$freq" + json_add_string htmode "$iw_htmode" + json_close_object + + json_set_namespace "$prev" +} + +mac80211_set_vif_txpower() { + local name="$1" + + json_select config + json_get_var ifname _ifname + json_get_vars vif_txpower + json_select .. + + [ -z "$vif_txpower" ] || iw dev "$ifname" set txpower fixed "${vif_txpower%%.*}00" +} + +wpa_supplicant_init_config() { + json_set_namespace wpa_supp prev + + json_init + json_add_array config + + json_set_namespace "$prev" +} + +wpa_supplicant_add_interface() { + local ifname="$1" + local mode="$2" + local prev + + _wpa_supplicant_common "$ifname" + + json_set_namespace wpa_supp prev + + json_add_object + json_add_string ctrl "$_rpath" + json_add_string iface "$ifname" + json_add_string mode "$mode" + json_add_string config "$_config" + [ -n "$default_macaddr" ] || json_add_string macaddr "$macaddr" + [ -n "$network_bridge" ] && json_add_string bridge "$network_bridge" + [ -n "$wds" ] && json_add_boolean 4addr "$wds" + json_add_boolean powersave "$powersave" + [ "$mode" = "mesh" ] && mac80211_add_mesh_params + json_close_object + + json_set_namespace "$prev" + + wpa_supp_init=1 +} + +wpa_supplicant_set_config() { + local phy="$1" + local prev + + json_set_namespace wpa_supp prev + json_close_array + json_add_string phy "$phy" + json_add_boolean defer 1 + local data="$(json_dump)" + + json_cleanup + json_set_namespace "$prev" + + ubus -S -t 0 wait_for wpa_supplicant || { + [ -n "$wpa_supp_init" ] || return 0 + + ubus wait_for wpa_supplicant + } + + local supplicant_res="$(ubus_call wpa_supplicant config_set "$data")" + ret="$?" + [ "$ret" != 0 -o -z "$supplicant_res" ] && wireless_setup_vif_failed WPA_SUPPLICANT_FAILED + + wireless_add_process "$(jsonfilter -s "$supplicant_res" -l 1 -e @.pid)" "/usr/sbin/wpa_supplicant" 1 1 + +} + +hostapd_set_config() { + [ -n "$hostapd_ctrl" ] || { + ubus_call hostapd config_set '{ "phy": "'"$phy"'", "config": "", "prev_config": "'"${hostapd_conf_file}.prev"'" }' > /dev/null + return 0; + } + + ubus wait_for hostapd + local hostapd_res="$(ubus_call hostapd config_set "{ \"phy\": \"$phy\", \"config\":\"${hostapd_conf_file}\", \"prev_config\": \"${hostapd_conf_file}.prev\"}")" + ret="$?" + [ "$ret" != 0 -o -z "$hostapd_res" ] && { + wireless_setup_failed HOSTAPD_START_FAILED + return + } + wireless_add_process "$(jsonfilter -s "$hostapd_res" -l 1 -e @.pid)" "/usr/sbin/hostapd" 1 1 +} + + +wpa_supplicant_start() { + local phy="$1" + + [ -n "$wpa_supp_init" ] || return 0 + + ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'" }' > /dev/null +} + +mac80211_setup_supplicant() { + local enable=$1 + local add_sp=0 + + wpa_supplicant_prepare_interface "$ifname" nl80211 || return 1 + + if [ "$mode" = "sta" ]; then + wpa_supplicant_add_network "$ifname" + else + wpa_supplicant_add_network "$ifname" "$freq" "$htmode" "$hostapd_noscan" + fi + + wpa_supplicant_add_interface "$ifname" "$mode" + + return 0 +} + +mac80211_setup_vif() { + local name="$1" + local failed + + json_select config + json_get_var ifname _ifname + json_get_var macaddr _macaddr + json_get_var default_macaddr _default_macaddr + json_get_vars mode wds powersave + + set_default powersave 0 + set_default wds 0 + + case "$mode" in + mesh) + json_get_vars $MP_CONFIG_INT $MP_CONFIG_BOOL $MP_CONFIG_STRING + wireless_vif_parse_encryption + [ -z "$htmode" ] && htmode="NOHT"; + if wpa_supplicant -vmesh; then + mac80211_setup_supplicant || failed=1 + else + mac80211_setup_mesh + fi + ;; + adhoc) + wireless_vif_parse_encryption + if [ "$wpa" -gt 0 -o "$auto_channel" -gt 0 ]; then + mac80211_setup_supplicant || failed=1 + else + mac80211_setup_adhoc + fi + ;; + sta) + mac80211_setup_supplicant || failed=1 + ;; + monitor) + mac80211_setup_monitor + ;; + esac + + json_select .. + [ -n "$failed" ] || wireless_add_vif "$name" "$ifname" +} + +get_freq() { + local phy="$1" + local channel="$2" + local band="$3" + + case "$band" in + 2g) band="1:";; + 5g) band="2:";; + 60g) band="3:";; + 6g) band="4:";; + esac + + iw "$phy" info | awk -v band="$band" -v channel="[$channel]" ' + +$1 ~ /Band/ { + band_match = band == $2 +} + +band_match && $3 == "MHz" && $4 == channel { + print $2 + exit +} +' +} + +chan_is_dfs() { + local phy="$1" + local chan="$2" + iw "$phy" info | grep -E -m1 "(\* ${chan:-....} MHz${chan:+|\\[$chan\\]})" | grep -q "MHz.*radar detection" + return $! +} + +mac80211_set_noscan() { + hostapd_noscan=1 +} + +drv_mac80211_cleanup() { + : +} + +mac80211_reset_config() { + local phy="$1" + + hostapd_conf_file="/var/run/hostapd-$phy.conf" + ubus_call hostapd config_set '{ "phy": "'"$phy"'", "config": "", "prev_config": "'"$hostapd_conf_file"'" }' > /dev/null + ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "config": [] }' > /dev/null + wdev_tool "$phy" set_config '{}' +} + +drv_mac80211_setup() { + json_select config + json_get_vars \ + phy macaddr path \ + country chanbw distance \ + txpower \ + rxantenna txantenna \ + frag rts beacon_int:100 htmode \ + num_global_macaddr:1 multiple_bssid + json_get_values basic_rate_list basic_rate + json_get_values scan_list scan_list + json_select .. + + json_select data && { + json_get_var prev_rxantenna rxantenna + json_get_var prev_txantenna txantenna + json_select .. + } + + find_phy || { + echo "Could not find PHY for device '$1'" + wireless_set_retry 0 + return 1 + } + + local wdev + local cwdev + local found + + # convert channel to frequency + [ "$auto_channel" -gt 0 ] || freq="$(get_freq "$phy" "$channel" "$band")" + + [ -n "$country" ] && { + iw reg get | grep -q "^country $country:" || { + iw reg set "$country" + sleep 1 + } + } + + hostapd_conf_file="/var/run/hostapd-$phy.conf" + + macidx=0 + staidx=0 + + [ -n "$chanbw" ] && { + for file in /sys/kernel/debug/ieee80211/$phy/ath9k*/chanbw /sys/kernel/debug/ieee80211/$phy/ath5k/bwmode; do + [ -f "$file" ] && echo "$chanbw" > "$file" + done + } + + set_default rxantenna 0xffffffff + set_default txantenna 0xffffffff + set_default distance 0 + + [ "$txantenna" = "all" ] && txantenna=0xffffffff + [ "$rxantenna" = "all" ] && rxantenna=0xffffffff + + [ "$rxantenna" = "$prev_rxantenna" -a "$txantenna" = "$prev_txantenna" ] || mac80211_reset_config "$phy" + wireless_set_data phy="$phy" txantenna="$txantenna" rxantenna="$rxantenna" + + iw phy "$phy" set antenna $txantenna $rxantenna >/dev/null 2>&1 + iw phy "$phy" set distance "$distance" >/dev/null 2>&1 + + if [ -n "$txpower" ]; then + iw phy "$phy" set txpower fixed "${txpower%%.*}00" + else + iw phy "$phy" set txpower auto + fi + + [ -n "$frag" ] && iw phy "$phy" set frag "${frag%%.*}" + [ -n "$rts" ] && iw phy "$phy" set rts "${rts%%.*}" + + has_ap= + hostapd_ctrl= + ap_ifname= + hostapd_noscan= + wpa_supp_init= + for_each_interface "ap" mac80211_check_ap + + [ -f "$hostapd_conf_file" ] && mv "$hostapd_conf_file" "$hostapd_conf_file.prev" + + for_each_interface "sta adhoc mesh" mac80211_set_noscan + [ -n "$has_ap" ] && mac80211_hostapd_setup_base "$phy" + + local prev + json_set_namespace wdev_uc prev + json_init + json_set_namespace "$prev" + + wpa_supplicant_init_config + + mac80211_prepare_iw_htmode + active_ifnames= + for_each_interface "ap sta adhoc mesh monitor" mac80211_prepare_vif + for_each_interface "ap sta adhoc mesh monitor" mac80211_setup_vif + + [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_set_config "$phy" + [ -x /usr/sbin/hostapd ] && hostapd_set_config "$phy" + + [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_start "$phy" + + json_set_namespace wdev_uc prev + wdev_tool "$phy" set_config "$(json_dump)" $active_ifnames + json_set_namespace "$prev" + + for_each_interface "ap sta adhoc mesh monitor" mac80211_set_vif_txpower + wireless_set_up +} + +_list_phy_interfaces() { + local phy="$1" + if [ -d "/sys/class/ieee80211/${phy}/device/net" ]; then + ls "/sys/class/ieee80211/${phy}/device/net" 2>/dev/null; + else + ls "/sys/class/ieee80211/${phy}/device" 2>/dev/null | grep net: | sed -e 's,net:,,g' + fi +} + +list_phy_interfaces() { + local phy="$1" + + for dev in $(_list_phy_interfaces "$phy"); do + readlink "/sys/class/net/${dev}/phy80211" | grep -q "/${phy}\$" || continue + echo "$dev" + done +} + +drv_mac80211_teardown() { + json_select data + json_get_vars phy + json_select .. + [ -n "$phy" ] || { + echo "Bug: PHY is undefined for device '$1'" + return 1 + } + + mac80211_reset_config "$phy" + + for wdev in $(list_phy_interfaces "$phy"); do + ip link set dev "$wdev" down + iw dev "$wdev" del + done +} + +add_driver mac80211 diff --git a/package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh b/package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh new file mode 100644 index 00000000000..e24a2a634ea --- /dev/null +++ b/package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh @@ -0,0 +1,217 @@ +#!/bin/sh + +append DRIVERS "mac80211" + +check_mac80211_device() { + local device="$1" + local path="$2" + local macaddr="$3" + + [ -n "$found" ] && return 0 + + phy_path= + config_get phy "$device" phy + json_select wlan + [ -n "$phy" ] && case "$phy" in + phy*) + [ -d /sys/class/ieee80211/$phy ] && \ + phy_path="$(iwinfo nl80211 path "$dev")" + ;; + *) + if json_is_a "$phy" object; then + json_select "$phy" + json_get_var phy_path path + json_select .. + elif json_is_a "${phy%.*}" object; then + json_select "${phy%.*}" + json_get_var phy_path path + json_select .. + phy_path="$phy_path+${phy##*.}" + fi + ;; + esac + json_select .. + [ -n "$phy_path" ] || config_get phy_path "$device" path + [ -n "$path" -a "$phy_path" = "$path" ] && { + found=1 + return 0 + } + + config_get dev_macaddr "$device" macaddr + + [ -n "$macaddr" -a "$dev_macaddr" = "$macaddr" ] && found=1 + + return 0 +} + + +__get_band_defaults() { + local phy="$1" + + ( iw phy "$phy" info; echo ) | awk ' +BEGIN { + bands = "" +} + +($1 == "Band" || $1 == "") && band { + if (channel) { + mode="NOHT" + if (ht) mode="HT20" + if (vht && band != "1:") mode="VHT80" + if (he) mode="HE80" + if (he && band == "1:") mode="HE20" + sub("\\[", "", channel) + sub("\\]", "", channel) + bands = bands band channel ":" mode " " + } + band="" +} + +$1 == "Band" { + band = $2 + channel = "" + vht = "" + ht = "" + he = "" +} + +$0 ~ "Capabilities:" { + ht=1 +} + +$0 ~ "VHT Capabilities" { + vht=1 +} + +$0 ~ "HE Iftypes" { + he=1 +} + +$1 == "*" && $3 == "MHz" && $0 !~ /disabled/ && band && !channel { + channel = $4 +} + +END { + print bands +}' +} + +get_band_defaults() { + local phy="$1" + + for c in $(__get_band_defaults "$phy"); do + local band="${c%%:*}" + c="${c#*:}" + local chan="${c%%:*}" + c="${c#*:}" + local mode="${c%%:*}" + + case "$band" in + 1) band=2g;; + 2) band=5g;; + 3) band=60g;; + 4) band=6g;; + *) band="";; + esac + + [ -n "$band" ] || continue + [ -n "$mode_band" -a "$band" = "6g" ] && return + + mode_band="$band" + channel="$chan" + htmode="$mode" + done +} + +check_devidx() { + case "$1" in + radio[0-9]*) + local idx="${1#radio}" + [ "$devidx" -ge "${1#radio}" ] && devidx=$((idx + 1)) + ;; + esac +} + +check_board_phy() { + local name="$2" + + json_select "$name" + json_get_var phy_path path + json_select .. + + if [ "$path" = "$phy_path" ]; then + board_dev="$name" + elif [ "${path%+*}" = "$phy_path" ]; then + fallback_board_dev="$name.${path#*+}" + fi +} + +detect_mac80211() { + devidx=0 + config_load wireless + config_foreach check_devidx wifi-device + + json_load_file /etc/board.json + + for _dev in /sys/class/ieee80211/*; do + [ -e "$_dev" ] || continue + + dev="${_dev##*/}" + + mode_band="" + channel="" + htmode="" + ht_capab="" + + get_band_defaults "$dev" + + path="$(iwinfo nl80211 path "$dev")" + macaddr="$(cat /sys/class/ieee80211/${dev}/macaddress)" + + # work around phy rename related race condition + [ -n "$path" -o -n "$macaddr" ] || continue + + board_dev= + fallback_board_dev= + json_for_each_item check_board_phy wlan + [ -n "$board_dev" ] || board_dev="$fallback_board_dev" + [ -n "$board_dev" ] && dev="$board_dev" + + found= + config_foreach check_mac80211_device wifi-device "$path" "$macaddr" + [ -n "$found" ] && continue + + name="radio${devidx}" + devidx=$(($devidx + 1)) + case "$dev" in + phy*) + if [ -n "$path" ]; then + dev_id="set wireless.${name}.path='$path'" + else + dev_id="set wireless.${name}.macaddr='$macaddr'" + fi + ;; + *) + dev_id="set wireless.${name}.phy='$dev'" + ;; + esac + + uci -q batch <<-EOF + set wireless.${name}=wifi-device + set wireless.${name}.type=mac80211 + ${dev_id} + set wireless.${name}.channel=${channel} + set wireless.${name}.band=${mode_band} + set wireless.${name}.htmode=$htmode + set wireless.${name}.disabled=1 + + set wireless.default_${name}=wifi-iface + set wireless.default_${name}.device=${name} + set wireless.default_${name}.network=lan + set wireless.default_${name}.mode=ap + set wireless.default_${name}.ssid=OpenWrt + set wireless.default_${name}.encryption=none +EOF + uci -q commit wireless + done +} diff --git a/package/network/config/wifi-scripts/files/sbin/wifi b/package/network/config/wifi-scripts/files/sbin/wifi new file mode 100755 index 00000000000..5231063a2b7 --- /dev/null +++ b/package/network/config/wifi-scripts/files/sbin/wifi @@ -0,0 +1,272 @@ +#!/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 + [ ! -f /etc/config/wireless ] && touch /etc/config/wireless + + 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 diff --git a/package/network/services/hostapd/files/common.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc index 4c33779af93..4c33779af93 100644 --- a/package/network/services/hostapd/files/common.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc diff --git a/package/network/services/hostapd/files/wdev.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc index ff4d629fd64..ff4d629fd64 100644 --- a/package/network/services/hostapd/files/wdev.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile index 17f9dcb581d..c8f476f7b8c 100644 --- a/package/network/services/hostapd/Makefile +++ b/package/network/services/hostapd/Makefile @@ -712,13 +712,10 @@ endef define Package/hostapd-common/install $(INSTALL_DIR) $(1)/etc/capabilities $(1)/etc/rc.button $(1)/etc/hotplug.d/ieee80211 $(1)/etc/init.d $(1)/lib/netifd $(1)/usr/share/acl.d $(1)/usr/share/hostap $(INSTALL_BIN) ./files/dhcp-get-server.sh $(1)/lib/netifd/dhcp-get-server.sh - $(INSTALL_DATA) ./files/hostapd.sh $(1)/lib/netifd/hostapd.sh $(INSTALL_BIN) ./files/wpad.init $(1)/etc/init.d/wpad $(INSTALL_BIN) ./files/wps-hotplug.sh $(1)/etc/rc.button/wps $(INSTALL_DATA) ./files/wpad_acl.json $(1)/usr/share/acl.d $(INSTALL_DATA) ./files/wpad.json $(1)/etc/capabilities - $(INSTALL_DATA) ./files/common.uc $(1)/usr/share/hostap/ - $(INSTALL_DATA) ./files/wdev.uc $(1)/usr/share/hostap/ endef define Package/hostapd/install |
