diff options
Diffstat (limited to 'package/network/config/wifi-scripts')
9 files changed, 4331 insertions, 0 deletions
diff --git a/package/network/config/wifi-scripts/Makefile b/package/network/config/wifi-scripts/Makefile new file mode 100644 index 0000000000..e57f97fef5 --- /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 0000000000..b865552661 --- /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/config/wifi-scripts/files/lib/netifd/hostapd.sh b/package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh new file mode 100644 index 0000000000..1a664abbf2 --- /dev/null +++ b/package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh @@ -0,0 +1,1598 @@ +. /lib/functions/network.sh +. /lib/functions.sh + +wpa_supplicant_add_rate() { + local var="$1" + local val="$(($2 / 1000))" + local sub="$((($2 / 100) % 10))" + append $var "$val" "," + [ $sub -gt 0 ] && append $var "." +} + +hostapd_add_rate() { + local var="$1" + local val="$(($2 / 100))" + append $var "$val" " " +} + +hostapd_append_wep_key() { + local var="$1" + + wep_keyidx=0 + set_default key 1 + case "$key" in + [1234]) + for idx in 1 2 3 4; do + local zidx + zidx="$(($idx - 1))" + json_get_var ckey "key${idx}" + [ -n "$ckey" ] && \ + append $var "wep_key${zidx}=$(prepare_key_wep "$ckey")" "$N$T" + done + wep_keyidx="$((key - 1))" + ;; + *) + append $var "wep_key0=$(prepare_key_wep "$key")" "$N$T" + ;; + esac +} + +hostapd_append_wpa_key_mgmt() { + local auth_type_l="$(echo $auth_type | tr 'a-z' 'A-Z')" + + case "$auth_type" in + psk|eap) + append wpa_key_mgmt "WPA-$auth_type_l" + [ "${wpa:-2}" -ge 2 ] && [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-${auth_type_l}" + [ "${ieee80211w:-0}" -gt 0 ] && append wpa_key_mgmt "WPA-${auth_type_l}-SHA256" + ;; + eap192) + append wpa_key_mgmt "WPA-EAP-SUITE-B-192" + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-EAP-SHA384" + ;; + eap-eap2) + append wpa_key_mgmt "WPA-EAP" + append wpa_key_mgmt "WPA-EAP-SHA256" + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-EAP" + ;; + eap2) + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-EAP" + append wpa_key_mgmt "WPA-EAP-SHA256" + ;; + sae) + append wpa_key_mgmt "SAE" + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-SAE" + ;; + psk-sae) + append wpa_key_mgmt "WPA-PSK" + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-PSK" + [ "${ieee80211w:-0}" -gt 0 ] && append wpa_key_mgmt "WPA-PSK-SHA256" + append wpa_key_mgmt "SAE" + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-SAE" + ;; + owe) + append wpa_key_mgmt "OWE" + ;; + esac + + [ "$fils" -gt 0 ] && { + case "$auth_type" in + eap-192) + append wpa_key_mgmt FILS-SHA384 + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt FT-FILS-SHA384 + ;; + eap*) + append wpa_key_mgmt FILS-SHA256 + [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt FT-FILS-SHA256 + ;; + esac + } + + [ "$auth_osen" = "1" ] && append wpa_key_mgmt "OSEN" +} + +hostapd_add_log_config() { + config_add_boolean \ + log_80211 \ + log_8021x \ + log_radius \ + log_wpa \ + log_driver \ + log_iapp \ + log_mlme + + config_add_int log_level +} + +hostapd_common_add_device_config() { + config_add_array basic_rate + config_add_array supported_rates + config_add_string beacon_rate + + config_add_string country country3 + config_add_boolean country_ie doth + config_add_boolean spectrum_mgmt_required + config_add_int local_pwr_constraint + config_add_string require_mode + config_add_boolean legacy_rates + config_add_int cell_density + config_add_int rts_threshold + config_add_int rssi_reject_assoc_rssi + config_add_int rssi_ignore_probe_request + config_add_int maxassoc + + config_add_string acs_chan_bias + config_add_array hostapd_options + + config_add_int airtime_mode + config_add_int mbssid + + hostapd_add_log_config +} + +hostapd_prepare_device_config() { + local config="$1" + local driver="$2" + + local base_cfg= + + json_get_vars country country3 country_ie beacon_int:100 doth require_mode legacy_rates \ + acs_chan_bias local_pwr_constraint spectrum_mgmt_required airtime_mode cell_density \ + rts_threshold beacon_rate rssi_reject_assoc_rssi rssi_ignore_probe_request maxassoc \ + mbssid:0 + + hostapd_set_log_options base_cfg + + set_default country_ie 1 + set_default spectrum_mgmt_required 0 + set_default doth 1 + set_default legacy_rates 0 + set_default airtime_mode 0 + set_default cell_density 0 + + [ -n "$country" ] && { + append base_cfg "country_code=$country" "$N" + [ -n "$country3" ] && append base_cfg "country3=$country3" "$N" + + [ "$country_ie" -gt 0 ] && { + append base_cfg "ieee80211d=1" "$N" + [ -n "$local_pwr_constraint" ] && append base_cfg "local_pwr_constraint=$local_pwr_constraint" "$N" + [ "$spectrum_mgmt_required" -gt 0 ] && append base_cfg "spectrum_mgmt_required=$spectrum_mgmt_required" "$N" + } + [ "$hwmode" = "a" -a "$doth" -gt 0 ] && append base_cfg "ieee80211h=1" "$N" + } + + [ -n "$acs_chan_bias" ] && append base_cfg "acs_chan_bias=$acs_chan_bias" "$N" + + local brlist= br + json_get_values basic_rate_list basic_rate + local rlist= r + json_get_values rate_list supported_rates + + [ -n "$hwmode" ] && append base_cfg "hw_mode=$hwmode" "$N" + if [ "$hwmode" = "g" ] || [ "$hwmode" = "a" ]; then + [ -n "$require_mode" ] && legacy_rates=0 + case "$require_mode" in + n) append base_cfg "require_ht=1" "$N";; + ac) append base_cfg "require_vht=1" "$N";; + esac + fi + case "$hwmode" in + b) + if [ "$cell_density" -eq 1 ]; then + set_default rate_list "5500 11000" + set_default basic_rate_list "5500 11000" + elif [ "$cell_density" -ge 2 ]; then + set_default rate_list "11000" + set_default basic_rate_list "11000" + fi + ;; + g) + if [ "$cell_density" -eq 0 ] || [ "$cell_density" -eq 1 ]; then + if [ "$legacy_rates" -eq 0 ]; then + set_default rate_list "6000 9000 12000 18000 24000 36000 48000 54000" + set_default basic_rate_list "6000 12000 24000" + elif [ "$cell_density" -eq 1 ]; then + set_default rate_list "5500 6000 9000 11000 12000 18000 24000 36000 48000 54000" + set_default basic_rate_list "5500 11000" + fi + elif [ "$cell_density" -ge 3 ] && [ "$legacy_rates" -ne 0 ] || [ "$cell_density" -eq 2 ]; then + if [ "$legacy_rates" -eq 0 ]; then + set_default rate_list "12000 18000 24000 36000 48000 54000" + set_default basic_rate_list "12000 24000" + else + set_default rate_list "11000 12000 18000 24000 36000 48000 54000" + set_default basic_rate_list "11000" + fi + elif [ "$cell_density" -ge 3 ]; then + set_default rate_list "24000 36000 48000 54000" + set_default basic_rate_list "24000" + fi + ;; + a) + if [ "$cell_density" -eq 1 ]; then + set_default rate_list "6000 9000 12000 18000 24000 36000 48000 54000" + set_default basic_rate_list "6000 12000 24000" + elif [ "$cell_density" -eq 2 ]; then + set_default rate_list "12000 18000 24000 36000 48000 54000" + set_default basic_rate_list "12000 24000" + elif [ "$cell_density" -ge 3 ]; then + set_default rate_list "24000 36000 48000 54000" + set_default basic_rate_list "24000" + fi + ;; + esac + + for r in $rate_list; do + hostapd_add_rate rlist "$r" + done + + for br in $basic_rate_list; do + hostapd_add_rate brlist "$br" + done + + [ -n "$rssi_reject_assoc_rssi" ] && append base_cfg "rssi_reject_assoc_rssi=$rssi_reject_assoc_rssi" "$N" + [ -n "$rssi_ignore_probe_request" ] && append base_cfg "rssi_ignore_probe_request=$rssi_ignore_probe_request" "$N" + [ -n "$beacon_rate" ] && append base_cfg "beacon_rate=$beacon_rate" "$N" + [ -n "$rlist" ] && append base_cfg "supported_rates=$rlist" "$N" + [ -n "$brlist" ] && append base_cfg "basic_rates=$brlist" "$N" + append base_cfg "beacon_int=$beacon_int" "$N" + [ -n "$rts_threshold" ] && append base_cfg "rts_threshold=$rts_threshold" "$N" + [ "$airtime_mode" -gt 0 ] && append base_cfg "airtime_mode=$airtime_mode" "$N" + [ -n "$maxassoc" ] && append base_cfg "iface_max_num_sta=$maxassoc" "$N" + [ "$mbssid" -gt 0 ] && [ "$mbssid" -le 2 ] && append base_cfg "mbssid=$mbssid" "$N" + + json_get_values opts hostapd_options + for val in $opts; do + append base_cfg "$val" "$N" + done + + cat > "$config" <<EOF +driver=$driver +$base_cfg +EOF +} + +hostapd_common_add_bss_config() { + config_add_string 'bssid:macaddr' 'ssid:string' + config_add_boolean wds wmm uapsd hidden utf8_ssid ppsk + + config_add_int maxassoc max_inactivity + config_add_boolean disassoc_low_ack isolate short_preamble skip_inactivity_poll + + config_add_int \ + wep_rekey eap_reauth_period \ + wpa_group_rekey wpa_pair_rekey wpa_master_rekey + config_add_boolean wpa_strict_rekey + config_add_boolean wpa_disable_eapol_key_retries + + config_add_boolean tdls_prohibit + + config_add_boolean rsn_preauth auth_cache + config_add_int ieee80211w + config_add_int eapol_version + + config_add_array auth_server acct_server + config_add_string 'server:host' + config_add_string auth_secret key + config_add_int 'auth_port:port' 'port:port' + + config_add_string acct_secret + config_add_int acct_port + config_add_int acct_interval + + config_add_int bss_load_update_period chan_util_avg_period + + config_add_string dae_client + config_add_string dae_secret + config_add_int dae_port + + config_add_string nasid + config_add_string ownip + config_add_string radius_client_addr + config_add_string iapp_interface + config_add_string eap_type ca_cert client_cert identity anonymous_identity auth priv_key priv_key_pwd + config_add_boolean ca_cert_usesystem ca_cert2_usesystem + config_add_string subject_match subject_match2 + config_add_array altsubject_match altsubject_match2 + config_add_array domain_match domain_match2 domain_suffix_match domain_suffix_match2 + config_add_string ieee80211w_mgmt_cipher + + config_add_int dynamic_vlan vlan_naming vlan_no_bridge + config_add_string vlan_tagged_interface vlan_bridge + config_add_string vlan_file + + config_add_string 'key1:wepkey' 'key2:wepkey' 'key3:wepkey' 'key4:wepkey' 'password:wpakey' + + config_add_string wpa_psk_file + + config_add_int multi_ap + + config_add_boolean wps_pushbutton wps_label ext_registrar wps_pbc_in_m1 + config_add_int wps_ap_setup_locked wps_independent + config_add_string wps_device_type wps_device_name wps_manufacturer wps_pin + config_add_string multi_ap_backhaul_ssid multi_ap_backhaul_key + + config_add_boolean wnm_sleep_mode wnm_sleep_mode_no_keys bss_transition mbo + config_add_int time_advertisement + config_add_string time_zone + config_add_string vendor_elements + + config_add_boolean ieee80211k rrm_neighbor_report rrm_beacon_report + + config_add_boolean ftm_responder stationary_ap + config_add_string lci civic + + config_add_boolean ieee80211r pmk_r1_push ft_psk_generate_local ft_over_ds + config_add_int r0_key_lifetime reassociation_deadline + config_add_string mobility_domain r1_key_holder + config_add_array r0kh r1kh + + config_add_int ieee80211w_max_timeout ieee80211w_retry_timeout + + config_add_string macfilter 'macfile:file' + config_add_array 'maclist:list(macaddr)' + + config_add_array bssid_blacklist + config_add_array bssid_whitelist + + config_add_int mcast_rate + config_add_array basic_rate + config_add_array supported_rates + + config_add_boolean sae_require_mfp + config_add_int sae_pwe + + config_add_string 'owe_transition_bssid:macaddr' 'owe_transition_ssid:string' + config_add_string owe_transition_ifname + + config_add_boolean iw_enabled iw_internet iw_asra iw_esr iw_uesa + config_add_int iw_access_network_type iw_venue_group iw_venue_type + config_add_int iw_ipaddr_type_availability iw_gas_address3 + config_add_string iw_hessid iw_network_auth_type iw_qos_map_set + config_add_array iw_roaming_consortium iw_domain_name iw_anqp_3gpp_cell_net iw_nai_realm + config_add_array iw_anqp_elem iw_venue_name iw_venue_url + + config_add_boolean hs20 disable_dgaf osen + config_add_int anqp_domain_id + config_add_int hs20_deauth_req_timeout + config_add_array hs20_oper_friendly_name + config_add_array osu_provider + config_add_array operator_icon + config_add_array hs20_conn_capab + config_add_string osu_ssid hs20_wan_metrics hs20_operating_class hs20_t_c_filename hs20_t_c_timestamp + + config_add_string hs20_t_c_server_url + + config_add_array airtime_sta_weight + config_add_int airtime_bss_weight airtime_bss_limit + + config_add_boolean multicast_to_unicast multicast_to_unicast_all proxy_arp per_sta_vif + + config_add_array hostapd_bss_options + config_add_boolean default_disabled + + config_add_boolean request_cui + config_add_array radius_auth_req_attr + config_add_array radius_acct_req_attr + + config_add_int eap_server + config_add_string eap_user_file ca_cert server_cert private_key private_key_passwd server_id + + config_add_boolean fils + config_add_string fils_dhcp + + config_add_int ocv +} + +hostapd_set_vlan_file() { + local ifname="$1" + local vlan="$2" + json_get_vars name vid + echo "${vid} ${ifname}-${name}" >> /var/run/hostapd-${ifname}.vlan + wireless_add_vlan "${vlan}" "${ifname}-${name}" +} + +hostapd_set_vlan() { + local ifname="$1" + + rm -f /var/run/hostapd-${ifname}.vlan + for_each_vlan hostapd_set_vlan_file ${ifname} +} + +hostapd_set_psk_file() { + local ifname="$1" + local vlan="$2" + local vlan_id="" + + json_get_vars mac vid key + set_default mac "00:00:00:00:00:00" + [ -n "$vid" ] && vlan_id="vlanid=$vid " + echo "${vlan_id} ${mac} ${key}" >> /var/run/hostapd-${ifname}.psk +} + +hostapd_set_psk() { + local ifname="$1" + + rm -f /var/run/hostapd-${ifname}.psk + for_each_station hostapd_set_psk_file ${ifname} +} + +append_iw_roaming_consortium() { + [ -n "$1" ] && append bss_conf "roaming_consortium=$1" "$N" +} + +append_iw_domain_name() { + if [ -z "$iw_domain_name_conf" ]; then + iw_domain_name_conf="$1" + else + iw_domain_name_conf="$iw_domain_name_conf,$1" + fi +} + +append_iw_anqp_3gpp_cell_net() { + if [ -z "$iw_anqp_3gpp_cell_net_conf" ]; then + iw_anqp_3gpp_cell_net_conf="$1" + else + iw_anqp_3gpp_cell_net_conf="$iw_anqp_3gpp_cell_net_conf:$1" + fi +} + +append_iw_anqp_elem() { + [ -n "$1" ] && append bss_conf "anqp_elem=$1" "$N" +} + +append_iw_nai_realm() { + [ -n "$1" ] && append bss_conf "nai_realm=$1" "$N" +} + +append_iw_venue_name() { + append bss_conf "venue_name=$1" "$N" +} + +append_iw_venue_url() { + append bss_conf "venue_url=$1" "$N" +} + +append_hs20_oper_friendly_name() { + append bss_conf "hs20_oper_friendly_name=$1" "$N" +} + +append_osu_provider_friendly_name() { + append bss_conf "osu_friendly_name=$1" "$N" +} + +append_osu_provider_service_desc() { + append bss_conf "osu_service_desc=$1" "$N" +} + +append_hs20_icon() { + local width height lang type path + config_get width "$1" width + config_get height "$1" height + config_get lang "$1" lang + config_get type "$1" type + config_get path "$1" path + + append bss_conf "hs20_icon=$width:$height:$lang:$type:$1:$path" "$N" +} + +append_hs20_icons() { + config_load wireless + config_foreach append_hs20_icon hs20-icon +} + +append_operator_icon() { + append bss_conf "operator_icon=$1" "$N" +} + +append_osu_icon() { + append bss_conf "osu_icon=$1" "$N" +} + +append_osu_provider() { + local cfgtype osu_server_uri osu_friendly_name osu_nai osu_nai2 osu_method_list + + config_load wireless + config_get cfgtype "$1" TYPE + [ "$cfgtype" != "osu-provider" ] && return + + append bss_conf "# provider $1" "$N" + config_get osu_server_uri "$1" osu_server_uri + config_get osu_nai "$1" osu_nai + config_get osu_nai2 "$1" osu_nai2 + config_get osu_method_list "$1" osu_method + + append bss_conf "osu_server_uri=$osu_server_uri" "$N" + append bss_conf "osu_nai=$osu_nai" "$N" + append bss_conf "osu_nai2=$osu_nai2" "$N" + append bss_conf "osu_method_list=$osu_method_list" "$N" + + config_list_foreach "$1" osu_service_desc append_osu_provider_service_desc + config_list_foreach "$1" osu_friendly_name append_osu_friendly_name + config_list_foreach "$1" osu_icon append_osu_icon + + append bss_conf "$N" +} + +append_hs20_conn_capab() { + [ -n "$1" ] && append bss_conf "hs20_conn_capab=$1" "$N" +} + +append_radius_acct_req_attr() { + [ -n "$1" ] && append bss_conf "radius_acct_req_attr=$1" "$N" +} + +append_radius_auth_req_attr() { + [ -n "$1" ] && append bss_conf "radius_auth_req_attr=$1" "$N" +} + +append_airtime_sta_weight() { + [ -n "$1" ] && append bss_conf "airtime_sta_weight=$1" "$N" +} + +append_auth_server() { + [ -n "$1" ] || return + append bss_conf "auth_server_addr=$1" "$N" + append bss_conf "auth_server_port=$auth_port" "$N" + [ -n "$auth_secret" ] && append bss_conf "auth_server_shared_secret=$auth_secret" "$N" +} + +append_acct_server() { + [ -n "$1" ] || return + append bss_conf "acct_server_addr=$1" "$N" + append bss_conf "acct_server_port=$acct_port" "$N" + [ -n "$acct_secret" ] && append bss_conf "acct_server_shared_secret=$acct_secret" "$N" +} + +hostapd_set_bss_options() { + local var="$1" + local phy="$2" + local vif="$3" + + wireless_vif_parse_encryption + + local bss_conf bss_md5sum ft_key + local wep_rekey wpa_group_rekey wpa_pair_rekey wpa_master_rekey wpa_key_mgmt + + json_get_vars \ + wep_rekey wpa_group_rekey wpa_pair_rekey wpa_master_rekey wpa_strict_rekey \ + wpa_disable_eapol_key_retries tdls_prohibit \ + maxassoc max_inactivity disassoc_low_ack isolate auth_cache \ + wps_pushbutton wps_label ext_registrar wps_pbc_in_m1 wps_ap_setup_locked \ + wps_independent wps_device_type wps_device_name wps_manufacturer wps_pin \ + macfilter ssid utf8_ssid wmm uapsd hidden short_preamble rsn_preauth \ + iapp_interface eapol_version dynamic_vlan ieee80211w nasid \ + acct_secret acct_port acct_interval \ + bss_load_update_period chan_util_avg_period sae_require_mfp sae_pwe \ + multi_ap multi_ap_backhaul_ssid multi_ap_backhaul_key skip_inactivity_poll \ + ppsk airtime_bss_weight airtime_bss_limit airtime_sta_weight \ + multicast_to_unicast_all proxy_arp per_sta_vif \ + eap_server eap_user_file ca_cert server_cert private_key private_key_passwd server_id \ + vendor_elements fils ocv + + set_default fils 0 + set_default isolate 0 + set_default maxassoc 0 + set_default max_inactivity 0 + set_default short_preamble 1 + set_default disassoc_low_ack 1 + set_default skip_inactivity_poll 0 + set_default hidden 0 + set_default wmm 1 + set_default uapsd 1 + set_default wpa_disable_eapol_key_retries 0 + set_default tdls_prohibit 0 + set_default eapol_version $((wpa & 1)) + set_default acct_port 1813 + set_default bss_load_update_period 60 + set_default chan_util_avg_period 600 + set_default utf8_ssid 1 + set_default multi_ap 0 + set_default ppsk 0 + set_default airtime_bss_weight 0 + set_default airtime_bss_limit 0 + set_default eap_server 0 + + /usr/sbin/hostapd -vfils || fils=0 + + append bss_conf "ctrl_interface=/var/run/hostapd" + if [ "$isolate" -gt 0 ]; then + append bss_conf "ap_isolate=$isolate" "$N" + fi + if [ "$maxassoc" -gt 0 ]; then + append bss_conf "max_num_sta=$maxassoc" "$N" + fi + if [ "$max_inactivity" -gt 0 ]; then + append bss_conf "ap_max_inactivity=$max_inactivity" "$N" + fi + + [ "$airtime_bss_weight" -gt 0 ] && append bss_conf "airtime_bss_weight=$airtime_bss_weight" "$N" + [ "$airtime_bss_limit" -gt 0 ] && append bss_conf "airtime_bss_limit=$airtime_bss_limit" "$N" + json_for_each_item append_airtime_sta_weight airtime_sta_weight + + append bss_conf "bss_load_update_period=$bss_load_update_period" "$N" + append bss_conf "chan_util_avg_period=$chan_util_avg_period" "$N" + append bss_conf "disassoc_low_ack=$disassoc_low_ack" "$N" + append bss_conf "skip_inactivity_poll=$skip_inactivity_poll" "$N" + append bss_conf "preamble=$short_preamble" "$N" + append bss_conf "wmm_enabled=$wmm" "$N" + append bss_conf "ignore_broadcast_ssid=$hidden" "$N" + append bss_conf "uapsd_advertisement_enabled=$uapsd" "$N" + append bss_conf "utf8_ssid=$utf8_ssid" "$N" + append bss_conf "multi_ap=$multi_ap" "$N" + [ -n "$vendor_elements" ] && append bss_conf "vendor_elements=$vendor_elements" "$N" + + [ "$tdls_prohibit" -gt 0 ] && append bss_conf "tdls_prohibit=$tdls_prohibit" "$N" + + [ "$wpa" -gt 0 ] && { + [ -n "$wpa_group_rekey" ] && append bss_conf "wpa_group_rekey=$wpa_group_rekey" "$N" + [ -n "$wpa_pair_rekey" ] && append bss_conf "wpa_ptk_rekey=$wpa_pair_rekey" "$N" + [ -n "$wpa_master_rekey" ] && append bss_conf "wpa_gmk_rekey=$wpa_master_rekey" "$N" + [ -n "$wpa_strict_rekey" ] && append bss_conf "wpa_strict_rekey=$wpa_strict_rekey" "$N" + } + + [ -n "$nasid" ] && append bss_conf "nas_identifier=$nasid" "$N" + + [ -n "$acct_interval" ] && \ + append bss_conf "radius_acct_interim_interval=$acct_interval" "$N" + json_for_each_item append_acct_server acct_server + json_for_each_item append_radius_acct_req_attr radius_acct_req_attr + + [ -n "$ocv" ] && append bss_conf "ocv=$ocv" "$N" + + case "$auth_type" in + sae|owe|eap2|eap192) + set_default ieee80211w 2 + set_default sae_require_mfp 1 + set_default sae_pwe 2 + ;; + psk-sae|eap-eap2) + set_default ieee80211w 1 + set_default sae_require_mfp 1 + set_default sae_pwe 2 + ;; + esac + [ -n "$sae_require_mfp" ] && append bss_conf "sae_require_mfp=$sae_require_mfp" "$N" + [ -n "$sae_pwe" ] && append bss_conf "sae_pwe=$sae_pwe" "$N" + + local vlan_possible="" + + case "$auth_type" in + none|owe) + json_get_vars owe_transition_bssid owe_transition_ssid owe_transition_ifname + + [ -n "$owe_transition_ssid" ] && append bss_conf "owe_transition_ssid=\"$owe_transition_ssid\"" "$N" + [ -n "$owe_transition_bssid" ] && append bss_conf "owe_transition_bssid=$owe_transition_bssid" "$N" + [ -n "$owe_transition_ifname" ] && append bss_conf "owe_transition_ifname=$owe_transition_ifname" "$N" + + wps_possible=1 + # Here we make the assumption that if we're in open mode + # with WPS enabled, we got to be in unconfigured state. + wps_not_configured=1 + ;; + psk|sae|psk-sae) + json_get_vars key wpa_psk_file + if [ "$auth_type" = "psk" ] && [ "$ppsk" -ne 0 ] ; then + json_get_vars auth_secret auth_port + set_default auth_port 1812 + json_for_each_item append_auth_server auth_server + append bss_conf "macaddr_acl=2" "$N" + append bss_conf "wpa_psk_radius=2" "$N" + elif [ ${#key} -eq 64 ]; then + append bss_conf "wpa_psk=$key" "$N" + elif [ ${#key} -ge 8 ] && [ ${#key} -le 63 ]; then + append bss_conf "wpa_passphrase=$key" "$N" + elif [ -n "$key" ] || [ -z "$wpa_psk_file" ]; then + wireless_setup_vif_failed INVALID_WPA_PSK + return 1 + fi + [ -z "$wpa_psk_file" ] && set_default wpa_psk_file /var/run/hostapd-$ifname.psk + [ -n "$wpa_psk_file" ] && { + [ -e "$wpa_psk_file" ] || touch "$wpa_psk_file" + append bss_conf "wpa_psk_file=$wpa_psk_file" "$N" + } + [ "$eapol_version" -ge "1" -a "$eapol_version" -le "2" ] && append bss_conf "eapol_version=$eapol_version" "$N" + + set_default dynamic_vlan 0 + vlan_possible=1 + wps_possible=1 + ;; + eap|eap2|eap-eap2|eap192) + json_get_vars \ + auth_server auth_secret auth_port \ + dae_client dae_secret dae_port \ + dynamic_ownip ownip radius_client_addr \ + eap_reauth_period request_cui \ + erp_domain mobility_domain \ + fils_realm fils_dhcp + + # radius can provide VLAN ID for clients + vlan_possible=1 + + set_default dynamic_ownip 1 + + # legacy compatibility + [ -n "$auth_server" ] || json_get_var auth_server server + [ -n "$auth_port" ] || json_get_var auth_port port + [ -n "$auth_secret" ] || json_get_var auth_secret key + + [ "$fils" -gt 0 ] && { + set_default erp_domain "$mobility_domain" + set_default erp_domain "$(echo "$ssid" | md5sum | head -c 8)" + set_default fils_realm "$erp_domain" + + append bss_conf "erp_send_reauth_start=1" "$N" + append bss_conf "erp_domain=$erp_domain" "$N" + append bss_conf "fils_realm=$fils_realm" "$N" + append bss_conf "fils_cache_id=$(echo "$fils_realm" | md5sum | head -c 4)" "$N" + + [ "$fils_dhcp" = "*" ] && { + json_get_values network network + fils_dhcp= + for net in $network; do + fils_dhcp="$(ifstatus "$net" | jsonfilter -e '@.data.dhcpserver')" + [ -n "$fils_dhcp" ] && break + done + + [ -z "$fils_dhcp" -a -n "$network_bridge" -a -n "$network_ifname" ] && \ + fils_dhcp="$(udhcpc -B -n -q -s /lib/netifd/dhcp-get-server.sh -t 1 -i "$network_ifname" 2>/dev/null)" + } + [ -n "$fils_dhcp" ] && append bss_conf "dhcp_server=$fils_dhcp" "$N" + } + + set_default auth_port 1812 + set_default dae_port 3799 + set_default request_cui 0 + + [ "$eap_server" -eq 0 ] && json_for_each_item append_auth_server auth_server + [ "$request_cui" -gt 0 ] && append bss_conf "radius_request_cui=$request_cui" "$N" + [ -n "$eap_reauth_period" ] && append bss_conf "eap_reauth_period=$eap_reauth_period" "$N" + + [ -n "$dae_client" -a -n "$dae_secret" ] && { + append bss_conf "radius_das_port=$dae_port" "$N" + append bss_conf "radius_das_client=$dae_client $dae_secret" "$N" + } + json_for_each_item append_radius_auth_req_attr radius_auth_req_attr + + if [ -n "$ownip" ]; then + append bss_conf "own_ip_addr=$ownip" "$N" + elif [ "$dynamic_ownip" -gt 0 ]; then + append bss_conf "dynamic_own_ip_addr=$dynamic_ownip" "$N" + fi + + [ -n "$radius_client_addr" ] && append bss_conf "radius_client_addr=$radius_client_addr" "$N" + append bss_conf "eapol_key_index_workaround=1" "$N" + append bss_conf "ieee8021x=1" "$N" + + [ "$eapol_version" -ge "1" -a "$eapol_version" -le "2" ] && append bss_conf "eapol_version=$eapol_version" "$N" + ;; + wep) + local wep_keyidx=0 + json_get_vars key + hostapd_append_wep_key bss_conf + append bss_conf "wep_default_key=$wep_keyidx" "$N" + [ -n "$wep_rekey" ] && append bss_conf "wep_rekey_period=$wep_rekey" "$N" + ;; + esac + + case "$auth_type" in + none|owe|psk|sae|psk-sae|wep) + json_get_vars \ + auth_server auth_port auth_secret \ + ownip radius_client_addr + + [ -n "$auth_server" ] && { + set_default auth_port 1812 + + json_for_each_item append_auth_server auth_server + [ -n "$ownip" ] && append bss_conf "own_ip_addr=$ownip" "$N" + [ -n "$radius_client_addr" ] && append bss_conf "radius_client_addr=$radius_client_addr" "$N" + append bss_conf "macaddr_acl=2" "$N" + } + ;; + esac + + local auth_algs="$((($auth_mode_shared << 1) | $auth_mode_open))" + append bss_conf "auth_algs=${auth_algs:-1}" "$N" + append bss_conf "wpa=$wpa" "$N" + [ -n "$wpa_pairwise" ] && append bss_conf "wpa_pairwise=$wpa_pairwise" "$N" + + set_default wps_pushbutton 0 + set_default wps_label 0 + set_default wps_pbc_in_m1 0 + + config_methods= + [ "$wps_pushbutton" -gt 0 ] && append config_methods push_button + [ "$wps_label" -gt 0 ] && append config_methods label + + # WPS not possible on Multi-AP backhaul-only SSID + [ "$multi_ap" = 1 ] && wps_possible= + + [ -n "$wps_possible" -a -n "$config_methods" ] && { + set_default ext_registrar 0 + set_default wps_device_type "6-0050F204-1" + set_default wps_device_name "OpenWrt AP" + set_default wps_manufacturer "www.openwrt.org" + set_default wps_independent 1 + + wps_state=2 + [ -n "$wps_not_configured" ] && wps_state=1 + + [ "$ext_registrar" -gt 0 -a -n "$network_bridge" ] && append bss_conf "upnp_iface=$network_bridge" "$N" + + append bss_conf "eap_server=1" "$N" + [ -n "$wps_pin" ] && append bss_conf "ap_pin=$wps_pin" "$N" + append bss_conf "wps_state=$wps_state" "$N" + append bss_conf "device_type=$wps_device_type" "$N" + append bss_conf "device_name=$wps_device_name" "$N" + append bss_conf "manufacturer=$wps_manufacturer" "$N" + append bss_conf "config_methods=$config_methods" "$N" + append bss_conf "wps_independent=$wps_independent" "$N" + [ -n "$wps_ap_setup_locked" ] && append bss_conf "ap_setup_locked=$wps_ap_setup_locked" "$N" + [ "$wps_pbc_in_m1" -gt 0 ] && append bss_conf "pbc_in_m1=$wps_pbc_in_m1" "$N" + [ "$multi_ap" -gt 0 ] && [ -n "$multi_ap_backhaul_ssid" ] && { + append bss_conf "multi_ap_backhaul_ssid=\"$multi_ap_backhaul_ssid\"" "$N" + if [ -z "$multi_ap_backhaul_key" ]; then + : + elif [ ${#multi_ap_backhaul_key} -lt 8 ]; then + wireless_setup_vif_failed INVALID_WPA_PSK + return 1 + elif [ ${#multi_ap_backhaul_key} -eq 64 ]; then + append bss_conf "multi_ap_backhaul_wpa_psk=$multi_ap_backhaul_key" "$N" + else + append bss_conf "multi_ap_backhaul_wpa_passphrase=$multi_ap_backhaul_key" "$N" + fi + } + } + + append bss_conf "ssid=$ssid" "$N" + [ -n "$network_bridge" ] && append bss_conf "bridge=$network_bridge${N}wds_bridge=" "$N" + [ -n "$network_ifname" ] && append bss_conf "snoop_iface=$network_ifname" "$N" + [ -n "$iapp_interface" ] && { + local ifname + network_get_device ifname "$iapp_interface" || ifname="$iapp_interface" + append bss_conf "iapp_interface=$ifname" "$N" + } + + json_get_vars time_advertisement time_zone wnm_sleep_mode wnm_sleep_mode_no_keys bss_transition mbo + set_default bss_transition 0 + set_default wnm_sleep_mode 0 + set_default wnm_sleep_mode_no_keys 0 + set_default mbo 0 + + [ -n "$time_advertisement" ] && append bss_conf "time_advertisement=$time_advertisement" "$N" + [ -n "$time_zone" ] && append bss_conf "time_zone=$time_zone" "$N" + if [ "$wnm_sleep_mode" -eq "1" ]; then + append bss_conf "wnm_sleep_mode=1" "$N" + [ "$wnm_sleep_mode_no_keys" -eq "1" ] && append bss_conf "wnm_sleep_mode_no_keys=1" "$N" + fi + [ "$bss_transition" -eq "1" ] && append bss_conf "bss_transition=1" "$N" + [ "$mbo" -eq 1 ] && append bss_conf "mbo=1" "$N" + + json_get_vars ieee80211k rrm_neighbor_report rrm_beacon_report rnr + set_default ieee80211k 0 + set_default rnr 0 + if [ "$ieee80211k" -eq "1" ]; then + set_default rrm_neighbor_report 1 + set_default rrm_beacon_report 1 + else + set_default rrm_neighbor_report 0 + set_default rrm_beacon_report 0 + fi + + [ "$rrm_neighbor_report" -eq "1" ] && append bss_conf "rrm_neighbor_report=1" "$N" + [ "$rrm_beacon_report" -eq "1" ] && append bss_conf "rrm_beacon_report=1" "$N" + [ "$rnr" -eq "1" ] && append bss_conf "rnr=1" "$N" + + json_get_vars ftm_responder stationary_ap lci civic + set_default ftm_responder 0 + if [ "$ftm_responder" -eq "1" ]; then + set_default stationary_ap 0 + iw phy "$phy" info | grep -q "ENABLE_FTM_RESPONDER" && { + append bss_conf "ftm_responder=1" "$N" + [ "$stationary_ap" -eq "1" ] && append bss_conf "stationary_ap=1" "$N" + [ -n "$lci" ] && append bss_conf "lci=$lci" "$N" + [ -n "$civic" ] && append bss_conf "civic=$civic" "$N" + } + fi + + json_get_vars ieee80211r + set_default ieee80211r 0 + if [ "$wpa" -ge "1" ]; then + if [ "$fils" -gt 0 ]; then + json_get_vars fils_realm + set_default fils_realm "$(echo "$ssid" | md5sum | head -c 8)" + fi + + append bss_conf "wpa_disable_eapol_key_retries=$wpa_disable_eapol_key_retries" "$N" + + hostapd_append_wpa_key_mgmt + [ -n "$wpa_key_mgmt" ] && append bss_conf "wpa_key_mgmt=$wpa_key_mgmt" "$N" + fi + + if [ "$wpa" -ge "2" ]; then + if [ "$ieee80211r" -gt "0" ]; then + json_get_vars mobility_domain ft_psk_generate_local ft_over_ds reassociation_deadline + + set_default mobility_domain "$(echo "$ssid" | md5sum | head -c 4)" + set_default ft_over_ds 0 + set_default reassociation_deadline 1000 + + case "$auth_type" in + psk) + set_default ft_psk_generate_local 1 + ;; + *) + set_default ft_psk_generate_local 0 + ;; + esac + + [ -n "$network_ifname" ] && append bss_conf "ft_iface=$network_ifname" "$N" + append bss_conf "mobility_domain=$mobility_domain" "$N" + append bss_conf "ft_psk_generate_local=$ft_psk_generate_local" "$N" + append bss_conf "ft_over_ds=$ft_over_ds" "$N" + append bss_conf "reassociation_deadline=$reassociation_deadline" "$N" + + if [ "$ft_psk_generate_local" -eq "0" ]; then + json_get_vars r0_key_lifetime r1_key_holder pmk_r1_push + json_get_values r0kh r0kh + json_get_values r1kh r1kh + + set_default r0_key_lifetime 10000 + set_default pmk_r1_push 0 + + [ -n "$r0kh" -a -n "$r1kh" ] || { + if [ -z "$auth_secret" -a -z "$key" ]; then + wireless_setup_vif_failed FT_KEY_CANT_BE_DERIVED + return 1 + fi + ft_key=`echo -n "$mobility_domain/${auth_secret:-${key}}" | md5sum | awk '{print $1}'` + + set_default r0kh "ff:ff:ff:ff:ff:ff,*,$ft_key" + set_default r1kh "00:00:00:00:00:00,00:00:00:00:00:00,$ft_key" + } + + [ -n "$r1_key_holder" ] && append bss_conf "r1_key_holder=$r1_key_holder" "$N" + append bss_conf "r0_key_lifetime=$r0_key_lifetime" "$N" + append bss_conf "pmk_r1_push=$pmk_r1_push" "$N" + + for kh in $r0kh; do + append bss_conf "r0kh=${kh//,/ }" "$N" + done + for kh in $r1kh; do + append bss_conf "r1kh=${kh//,/ }" "$N" + done + fi + fi + + if [ -n "$network_bridge" -a "$rsn_preauth" = 1 ]; then + set_default auth_cache 1 + append bss_conf "rsn_preauth=1" "$N" + append bss_conf "rsn_preauth_interfaces=$network_bridge" "$N" + else + case "$auth_type" in + sae|psk-sae|owe) + set_default auth_cache 1 + ;; + *) + set_default auth_cache 0 + ;; + esac + fi + + append bss_conf "okc=$auth_cache" "$N" + [ "$auth_cache" = 0 -a "$fils" = 0 ] && append bss_conf "disable_pmksa_caching=1" "$N" + + # RSN -> allow management frame protection + case "$ieee80211w" in + [012]) + json_get_vars ieee80211w_mgmt_cipher ieee80211w_max_timeout ieee80211w_retry_timeout + append bss_conf "ieee80211w=$ieee80211w" "$N" + [ "$ieee80211w" -gt "0" ] && { + if [ "$auth_type" = "eap192" ]; then + append bss_conf "group_mgmt_cipher=BIP-GMAC-256" "$N" + else + append bss_conf "group_mgmt_cipher=${ieee80211w_mgmt_cipher:-AES-128-CMAC}" "$N" + fi + [ -n "$ieee80211w_max_timeout" ] && \ + append bss_conf "assoc_sa_query_max_timeout=$ieee80211w_max_timeout" "$N" + [ -n "$ieee80211w_retry_timeout" ] && \ + append bss_conf "assoc_sa_query_retry_timeout=$ieee80211w_retry_timeout" "$N" + } + ;; + esac + fi + + _macfile="/var/run/hostapd-$ifname.maclist" + case "$macfilter" in + allow) + append bss_conf "macaddr_acl=1" "$N" + append bss_conf "accept_mac_file=$_macfile" "$N" + # accept_mac_file can be used to set MAC to VLAN ID mapping + vlan_possible=1 + ;; + deny) + append bss_conf "macaddr_acl=0" "$N" + append bss_conf "deny_mac_file=$_macfile" "$N" + ;; + *) + _macfile="" + ;; + esac + + [ -n "$_macfile" ] && { + json_get_vars macfile + json_get_values maclist maclist + + rm -f "$_macfile" + ( + for mac in $maclist; do + echo "$mac" + done + [ -n "$macfile" -a -f "$macfile" ] && cat "$macfile" + ) > "$_macfile" + } + + [ -n "$vlan_possible" -a -n "$dynamic_vlan" ] && { + json_get_vars vlan_naming vlan_tagged_interface vlan_bridge vlan_file vlan_no_bridge + set_default vlan_naming 1 + [ -z "$vlan_file" ] && set_default vlan_file /var/run/hostapd-$ifname.vlan + append bss_conf "dynamic_vlan=$dynamic_vlan" "$N" + append bss_conf "vlan_naming=$vlan_naming" "$N" + if [ -n "$vlan_bridge" ]; then + append bss_conf "vlan_bridge=$vlan_bridge" "$N" + else + set_default vlan_no_bridge 1 + fi + append bss_conf "vlan_no_bridge=$vlan_no_bridge" "$N" + [ -n "$vlan_tagged_interface" ] && \ + append bss_conf "vlan_tagged_interface=$vlan_tagged_interface" "$N" + [ -n "$vlan_file" ] && { + [ -e "$vlan_file" ] || touch "$vlan_file" + append bss_conf "vlan_file=$vlan_file" "$N" + } + } + + json_get_vars iw_enabled iw_internet iw_asra iw_esr iw_uesa iw_access_network_type + json_get_vars iw_hessid iw_venue_group iw_venue_type iw_network_auth_type + json_get_vars iw_roaming_consortium iw_domain_name iw_anqp_3gpp_cell_net iw_nai_realm + json_get_vars iw_anqp_elem iw_qos_map_set iw_ipaddr_type_availability iw_gas_address3 + json_get_vars iw_venue_name iw_venue_url + + set_default iw_enabled 0 + if [ "$iw_enabled" = "1" ]; then + append bss_conf "interworking=1" "$N" + set_default iw_internet 1 + set_default iw_asra 0 + set_default iw_esr 0 + set_default iw_uesa 0 + + append bss_conf "internet=$iw_internet" "$N" + append bss_conf "asra=$iw_asra" "$N" + append bss_conf "esr=$iw_esr" "$N" + append bss_conf "uesa=$iw_uesa" "$N" + + [ -n "$iw_access_network_type" ] && \ + append bss_conf "access_network_type=$iw_access_network_type" "$N" + [ -n "$iw_hessid" ] && append bss_conf "hessid=$iw_hessid" "$N" + [ -n "$iw_venue_group" ] && \ + append bss_conf "venue_group=$iw_venue_group" "$N" + [ -n "$iw_venue_type" ] && append bss_conf "venue_type=$iw_venue_type" "$N" + [ -n "$iw_network_auth_type" ] && \ + append bss_conf "network_auth_type=$iw_network_auth_type" "$N" + [ -n "$iw_gas_address3" ] && append bss_conf "gas_address3=$iw_gas_address3" "$N" + + json_for_each_item append_iw_roaming_consortium iw_roaming_consortium + json_for_each_item append_iw_anqp_elem iw_anqp_elem + json_for_each_item append_iw_nai_realm iw_nai_realm + json_for_each_item append_iw_venue_name iw_venue_name + json_for_each_item append_iw_venue_url iw_venue_url + + iw_domain_name_conf= + json_for_each_item append_iw_domain_name iw_domain_name + [ -n "$iw_domain_name_conf" ] && \ + append bss_conf "domain_name=$iw_domain_name_conf" "$N" + + iw_anqp_3gpp_cell_net_conf= + json_for_each_item append_iw_anqp_3gpp_cell_net iw_anqp_3gpp_cell_net + [ -n "$iw_anqp_3gpp_cell_net_conf" ] && \ + append bss_conf "anqp_3gpp_cell_net=$iw_anqp_3gpp_cell_net_conf" "$N" + fi + + set_default iw_qos_map_set 0,0,2,16,1,1,255,255,18,22,24,38,40,40,44,46,48,56 + case "$iw_qos_map_set" in + *,*);; + *) iw_qos_map_set="";; + esac + [ -n "$iw_qos_map_set" ] && append bss_conf "qos_map_set=$iw_qos_map_set" "$N" + + local hs20 disable_dgaf osen anqp_domain_id hs20_deauth_req_timeout \ + osu_ssid hs20_wan_metrics hs20_operating_class hs20_t_c_filename hs20_t_c_timestamp \ + hs20_t_c_server_url + json_get_vars hs20 disable_dgaf osen anqp_domain_id hs20_deauth_req_timeout \ + osu_ssid hs20_wan_metrics hs20_operating_class hs20_t_c_filename hs20_t_c_timestamp \ + hs20_t_c_server_url + + set_default hs20 0 + set_default disable_dgaf $hs20 + set_default osen 0 + set_default anqp_domain_id 0 + set_default hs20_deauth_req_timeout 60 + if [ "$hs20" = "1" ]; then + append bss_conf "hs20=1" "$N" + append_hs20_icons + append bss_conf "disable_dgaf=$disable_dgaf" "$N" + append bss_conf "osen=$osen" "$N" + append bss_conf "anqp_domain_id=$anqp_domain_id" "$N" + append bss_conf "hs20_deauth_req_timeout=$hs20_deauth_req_timeout" "$N" + [ -n "$osu_ssid" ] && append bss_conf "osu_ssid=$osu_ssid" "$N" + [ -n "$hs20_wan_metrics" ] && append bss_conf "hs20_wan_metrics=$hs20_wan_metrics" "$N" + [ -n "$hs20_operating_class" ] && append bss_conf "hs20_operating_class=$hs20_operating_class" "$N" + [ -n "$hs20_t_c_filename" ] && append bss_conf "hs20_t_c_filename=$hs20_t_c_filename" "$N" + [ -n "$hs20_t_c_timestamp" ] && append bss_conf "hs20_t_c_timestamp=$hs20_t_c_timestamp" "$N" + [ -n "$hs20_t_c_server_url" ] && append bss_conf "hs20_t_c_server_url=$hs20_t_c_server_url" "$N" + json_for_each_item append_hs20_oper_friendly_name hs20_oper_friendly_name + json_for_each_item append_hs20_conn_capab hs20_conn_capab + json_for_each_item append_osu_provider osu_provider + json_for_each_item append_operator_icon operator_icon + fi + + if [ "$eap_server" = "1" ]; then + append bss_conf "eap_server=1" "$N" + append bss_conf "eap_server_erp=1" "$N" + [ -n "$eap_user_file" ] && append bss_conf "eap_user_file=$eap_user_file" "$N" + [ -n "$ca_cert" ] && append bss_conf "ca_cert=$ca_cert" "$N" + [ -n "$server_cert" ] && append bss_conf "server_cert=$server_cert" "$N" + [ -n "$private_key" ] && append bss_conf "private_key=$private_key" "$N" + [ -n "$private_key_passwd" ] && append bss_conf "private_key_passwd=$private_key_passwd" "$N" + [ -n "$server_id" ] && append bss_conf "server_id=$server_id" "$N" + fi + + set_default multicast_to_unicast_all 0 + if [ "$multicast_to_unicast_all" -gt 0 ]; then + append bss_conf "multicast_to_unicast=$multicast_to_unicast_all" "$N" + fi + set_default proxy_arp 0 + if [ "$proxy_arp" -gt 0 ]; then + append bss_conf "proxy_arp=$proxy_arp" "$N" + fi + + set_default per_sta_vif 0 + if [ "$per_sta_vif" -gt 0 ]; then + append bss_conf "per_sta_vif=$per_sta_vif" "$N" + fi + + json_get_values opts hostapd_bss_options + for val in $opts; do + append bss_conf "$val" "$N" + done + + append "$var" "$bss_conf" "$N" + return 0 +} + +hostapd_set_log_options() { + local var="$1" + + local log_level log_80211 log_8021x log_radius log_wpa log_driver log_iapp log_mlme + json_get_vars log_level log_80211 log_8021x log_radius log_wpa log_driver log_iapp log_mlme + + set_default log_level 2 + set_default log_80211 1 + set_default log_8021x 1 + set_default log_radius 1 + set_default log_wpa 1 + set_default log_driver 1 + set_default log_iapp 1 + set_default log_mlme 1 + + local log_mask="$(( \ + ($log_80211 << 0) | \ + ($log_8021x << 1) | \ + ($log_radius << 2) | \ + ($log_wpa << 3) | \ + ($log_driver << 4) | \ + ($log_iapp << 5) | \ + ($log_mlme << 6) \ + ))" + + append "$var" "logger_syslog=$log_mask" "$N" + append "$var" "logger_syslog_level=$log_level" "$N" + append "$var" "logger_stdout=$log_mask" "$N" + append "$var" "logger_stdout_level=$log_level" "$N" + + return 0 +} + +_wpa_supplicant_common() { + local ifname="$1" + + _rpath="/var/run/wpa_supplicant" + _config="${_rpath}-$ifname.conf" +} + +wpa_supplicant_teardown_interface() { + _wpa_supplicant_common "$1" + rm -rf "$_rpath/$1" "$_config" +} + +wpa_supplicant_prepare_interface() { + local ifname="$1" + _w_driver="$2" + + _wpa_supplicant_common "$1" + + json_get_vars mode wds multi_ap + + [ -n "$network_bridge" ] && { + fail= + case "$mode" in + adhoc) + fail=1 + ;; + sta) + [ "$wds" = 1 -o "$multi_ap" = 1 ] || fail=1 + ;; + esac + + [ -n "$fail" ] && { + wireless_setup_vif_failed BRIDGE_NOT_ALLOWED + return 1 + } + } + + local ap_scan= + + _w_mode="$mode" + + [ "$mode" = adhoc ] && { + ap_scan="ap_scan=2" + } + + local country_str= + [ -n "$country" ] && { + country_str="country=$country" + } + + multiap_flag_file="${_config}.is_multiap" + if [ "$multi_ap" = "1" ]; then + touch "$multiap_flag_file" + else + [ -e "$multiap_flag_file" ] && rm "$multiap_flag_file" + fi + wpa_supplicant_teardown_interface "$ifname" + cat > "$_config" <<EOF +${scan_list:+freq_list=$scan_list} +$ap_scan +$country_str +EOF + return 0 +} + +wpa_supplicant_set_fixed_freq() { + local freq="$1" + local htmode="$2" + + append network_data "fixed_freq=1" "$N$T" + append network_data "frequency=$freq" "$N$T" + case "$htmode" in + NOHT) append network_data "disable_ht=1" "$N$T";; + HE20|HT20|VHT20) append network_data "disable_ht40=1" "$N$T";; + HT40*|VHT40|VHT80|VHT160|HE40|HE80|HE160) append network_data "ht40=1" "$N$T";; + esac + case "$htmode" in + VHT*) append network_data "vht=1" "$N$T";; + esac + case "$htmode" in + HE80|VHT80) append network_data "max_oper_chwidth=1" "$N$T";; + HE160|VHT160) append network_data "max_oper_chwidth=2" "$N$T";; + HE20|HE40|VHT20|VHT40) append network_data "max_oper_chwidth=0" "$N$T";; + *) append network_data "disable_vht=1" "$N$T";; + esac +} + +wpa_supplicant_add_network() { + local ifname="$1" + local freq="$2" + local htmode="$3" + local noscan="$4" + + _wpa_supplicant_common "$1" + wireless_vif_parse_encryption + + json_get_vars \ + ssid bssid key \ + basic_rate mcast_rate \ + ieee80211w ieee80211r fils ocv \ + multi_ap \ + default_disabled + + case "$auth_type" in + sae|owe|eap2|eap192) + set_default ieee80211w 2 + ;; + psk-sae) + set_default ieee80211w 1 + ;; + esac + + set_default ieee80211r 0 + set_default multi_ap 0 + set_default default_disabled 0 + + local key_mgmt='NONE' + local network_data= + local T=" " + + local scan_ssid="scan_ssid=1" + local freq wpa_key_mgmt + + [ "$_w_mode" = "adhoc" ] && { + append network_data "mode=1" "$N$T" + [ -n "$freq" ] && wpa_supplicant_set_fixed_freq "$freq" "$htmode" + [ "$noscan" = "1" ] && append network_data "noscan=1" "$N$T" + + scan_ssid="scan_ssid=0" + + [ "$_w_driver" = "nl80211" ] || append wpa_key_mgmt "WPA-NONE" + } + + [ "$_w_mode" = "mesh" ] && { + json_get_vars mesh_id mesh_fwding mesh_rssi_threshold encryption + [ -n "$mesh_id" ] && ssid="${mesh_id}" + + append network_data "mode=5" "$N$T" + [ -n "$mesh_fwding" ] && append network_data "mesh_fwding=${mesh_fwding}" "$N$T" + [ -n "$mesh_rssi_threshold" ] && append network_data "mesh_rssi_threshold=${mesh_rssi_threshold}" "$N$T" + [ -n "$freq" ] && wpa_supplicant_set_fixed_freq "$freq" "$htmode" + [ "$noscan" = "1" ] && append network_data "noscan=1" "$N$T" + [ "$encryption" = "none" -o -z "$encryption" ] || append wpa_key_mgmt "SAE" + scan_ssid="" + } + + [ "$_w_mode" = "sta" ] && { + [ "$multi_ap" = 1 ] && append network_data "multi_ap_backhaul_sta=1" "$N$T" + [ "$default_disabled" = 1 ] && append network_data "disabled=1" "$N$T" + } + + [ -n "$ocv" ] && append network_data "ocv=$ocv" "$N$T" + + case "$auth_type" in + none) ;; + owe) + hostapd_append_wpa_key_mgmt + key_mgmt="$wpa_key_mgmt" + ;; + wep) + local wep_keyidx=0 + hostapd_append_wep_key network_data + append network_data "wep_tx_keyidx=$wep_keyidx" "$N$T" + ;; + wps) + key_mgmt='WPS' + ;; + psk|sae|psk-sae) + local passphrase + + if [ "$_w_mode" != "mesh" ]; then + hostapd_append_wpa_key_mgmt + fi + + key_mgmt="$wpa_key_mgmt" + + if [ "$_w_mode" = "mesh" ] || [ "$auth_type" = "sae" ]; then + passphrase="sae_password=\"${key}\"" + else + if [ ${#key} -eq 64 ]; then + passphrase="psk=${key}" + else + passphrase="psk=\"${key}\"" + fi + fi + append network_data "$passphrase" "$N$T" + ;; + eap|eap2|eap192) + hostapd_append_wpa_key_mgmt + key_mgmt="$wpa_key_mgmt" + + json_get_vars eap_type identity anonymous_identity ca_cert ca_cert_usesystem + + [ "$fils" -gt 0 ] && append network_data "erp=1" "$N$T" + if [ "$ca_cert_usesystem" -eq "1" -a -f "/etc/ssl/certs/ca-certificates.crt" ]; then + append network_data "ca_cert=\"/etc/ssl/certs/ca-certificates.crt\"" "$N$T" + else + [ -n "$ca_cert" ] && append network_data "ca_cert=\"$ca_cert\"" "$N$T" + fi + [ -n "$identity" ] && append network_data "identity=\"$identity\"" "$N$T" + [ -n "$anonymous_identity" ] && append network_data "anonymous_identity=\"$anonymous_identity\"" "$N$T" + case "$eap_type" in + tls) + json_get_vars client_cert priv_key priv_key_pwd + append network_data "client_cert=\"$client_cert\"" "$N$T" + append network_data "private_key=\"$priv_key\"" "$N$T" + append network_data "private_key_passwd=\"$priv_key_pwd\"" "$N$T" + + json_get_vars subject_match + [ -n "$subject_match" ] && append network_data "subject_match=\"$subject_match\"" "$N$T" + + json_get_values altsubject_match altsubject_match + if [ -n "$altsubject_match" ]; then + local list= + for x in $altsubject_match; do + append list "$x" ";" + done + append network_data "altsubject_match=\"$list\"" "$N$T" + fi + + json_get_values domain_match domain_match + if [ -n "$domain_match" ]; then + local list= + for x in $domain_match; do + append list "$x" ";" + done + append network_data "domain_match=\"$list\"" "$N$T" + fi + + json_get_values domain_suffix_match domain_suffix_match + if [ -n "$domain_suffix_match" ]; then + local list= + for x in $domain_suffix_match; do + append list "$x" ";" + done + append network_data "domain_suffix_match=\"$list\"" "$N$T" + fi + ;; + fast|peap|ttls) + json_get_vars auth password ca_cert2 ca_cert2_usesystem client_cert2 priv_key2 priv_key2_pwd + set_default auth MSCHAPV2 + + if [ "$auth" = "EAP-TLS" ]; then + if [ "$ca_cert2_usesystem" -eq "1" -a -f "/etc/ssl/certs/ca-certificates.crt" ]; then + append network_data "ca_cert2=\"/etc/ssl/certs/ca-certificates.crt\"" "$N$T" + else + [ -n "$ca_cert2" ] && append network_data "ca_cert2=\"$ca_cert2\"" "$N$T" + fi + append network_data "client_cert2=\"$client_cert2\"" "$N$T" + append network_data "private_key2=\"$priv_key2\"" "$N$T" + append network_data "private_key2_passwd=\"$priv_key2_pwd\"" "$N$T" + else + append network_data "password=\"$password\"" "$N$T" + fi + + json_get_vars subject_match + [ -n "$subject_match" ] && append network_data "subject_match=\"$subject_match\"" "$N$T" + + json_get_values altsubject_match altsubject_match + if [ -n "$altsubject_match" ]; then + local list= + for x in $altsubject_match; do + append list "$x" ";" + done + append network_data "altsubject_match=\"$list\"" "$N$T" + fi + + json_get_values domain_match domain_match + if [ -n "$domain_match" ]; then + local list= + for x in $domain_match; do + append list "$x" ";" + done + append network_data "domain_match=\"$list\"" "$N$T" + fi + + json_get_values domain_suffix_match domain_suffix_match + if [ -n "$domain_suffix_match" ]; then + local list= + for x in $domain_suffix_match; do + append list "$x" ";" + done + append network_data "domain_suffix_match=\"$list\"" "$N$T" + fi + + phase2proto="auth=" + case "$auth" in + "auth"*) + phase2proto="" + ;; + "EAP-"*) + auth="$(echo $auth | cut -b 5- )" + [ "$eap_type" = "ttls" ] && + phase2proto="autheap=" + json_get_vars subject_match2 + [ -n "$subject_match2" ] && append network_data "subject_match2=\"$subject_match2\"" "$N$T" + + json_get_values altsubject_match2 altsubject_match2 + if [ -n "$altsubject_match2" ]; then + local list= + for x in $altsubject_match2; do + append list "$x" ";" + done + append network_data "altsubject_match2=\"$list\"" "$N$T" + fi + + json_get_values domain_match2 domain_match2 + if [ -n "$domain_match2" ]; then + local list= + for x in $domain_match2; do + append list "$x" ";" + done + append network_data "domain_match2=\"$list\"" "$N$T" + fi + + json_get_values domain_suffix_match2 domain_suffix_match2 + if [ -n "$domain_suffix_match2" ]; then + local list= + for x in $domain_suffix_match2; do + append list "$x" ";" + done + append network_data "domain_suffix_match2=\"$list\"" "$N$T" + fi + ;; + esac + append network_data "phase2=\"$phase2proto$auth\"" "$N$T" + ;; + esac + append network_data "eap=$(echo $eap_type | tr 'a-z' 'A-Z')" "$N$T" + ;; + esac + + [ "$wpa_cipher" = GCMP ] && { + append network_data "pairwise=GCMP" "$N$T" + append network_data "group=GCMP" "$N$T" + } + + [ "$mode" = mesh ] || { + case "$wpa" in + 1) + append network_data "proto=WPA" "$N$T" + ;; + 2) + append network_data "proto=RSN" "$N$T" + ;; + esac + + case "$ieee80211w" in + [012]) + [ "$wpa" -ge 2 ] && append network_data "ieee80211w=$ieee80211w" "$N$T" + ;; + esac + } + [ -n "$bssid" ] && append network_data "bssid=$bssid" "$N$T" + [ -n "$beacon_int" ] && append network_data "beacon_int=$beacon_int" "$N$T" + + local bssid_blacklist bssid_whitelist + json_get_values bssid_blacklist bssid_blacklist + json_get_values bssid_whitelist bssid_whitelist + + [ -n "$bssid_blacklist" ] && append network_data "bssid_blacklist=$bssid_blacklist" "$N$T" + [ -n "$bssid_whitelist" ] && append network_data "bssid_whitelist=$bssid_whitelist" "$N$T" + + [ -n "$basic_rate" ] && { + local br rate_list= + for br in $basic_rate; do + wpa_supplicant_add_rate rate_list "$br" + done + [ -n "$rate_list" ] && append network_data "rates=$rate_list" "$N$T" + } + + [ -n "$mcast_rate" ] && { + local mc_rate= + wpa_supplicant_add_rate mc_rate "$mcast_rate" + append network_data "mcast_rate=$mc_rate" "$N$T" + } + + if [ "$key_mgmt" = "WPS" ]; then + echo "wps_cred_processing=1" >> "$_config" + else + cat >> "$_config" <<EOF +network={ + $scan_ssid + ssid="$ssid" + key_mgmt=$key_mgmt + $network_data +} +EOF + fi + return 0 +} 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 0000000000..5b852e0937 --- /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 0000000000..49ffb219bc --- /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 0000000000..e24a2a634e --- /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 0000000000..5231063a2b --- /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/config/wifi-scripts/files/usr/share/hostap/common.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc new file mode 100644 index 0000000000..4c33779af9 --- /dev/null +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc @@ -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.ap_vlan; +} + +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 }; diff --git a/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc new file mode 100644 index 0000000000..ff4d629fd6 --- /dev/null +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc @@ -0,0 +1,185 @@ +#!/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); + if (wdev.freq) + system(`iw dev ${ifname} set freq ${wdev.freq} ${wdev.htmode}`); + if (wdev.mode == "adhoc") { + let cmd = ["iw", "dev", ifname, "ibss", "join", wdev.ssid, wdev.freq, wdev.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, wdev.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); |