wifi-scripts: add new package, move wifi scripts to a single place
authorFelix Fietkau <nbd@nbd.name>
Fri, 26 Jan 2024 10:03:39 +0000 (11:03 +0100)
committerFelix Fietkau <nbd@nbd.name>
Sat, 3 Feb 2024 15:16:36 +0000 (16:16 +0100)
Signed-off-by: Felix Fietkau <nbd@nbd.name>
20 files changed:
package/base-files/files/sbin/wifi [deleted file]
package/kernel/broadcom-wl/Makefile
package/kernel/mac80211/Makefile
package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh [deleted file]
package/kernel/mac80211/files/lib/wifi/mac80211.sh [deleted file]
package/kernel/mac80211/files/mac80211.hotplug [deleted file]
package/network/config/netifd/Makefile
package/network/config/wifi-scripts/Makefile [new file with mode: 0644]
package/network/config/wifi-scripts/files/etc/hotplug.d/ieee80211/10-wifi-detect [new file with mode: 0644]
package/network/config/wifi-scripts/files/lib/netifd/hostapd.sh [new file with mode: 0644]
package/network/config/wifi-scripts/files/lib/netifd/netifd-wireless.sh [new file with mode: 0644]
package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh [new file with mode: 0755]
package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh [new file with mode: 0644]
package/network/config/wifi-scripts/files/sbin/wifi [new file with mode: 0755]
package/network/config/wifi-scripts/files/usr/share/hostap/common.uc [new file with mode: 0644]
package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc [new file with mode: 0644]
package/network/services/hostapd/Makefile
package/network/services/hostapd/files/common.uc [deleted file]
package/network/services/hostapd/files/hostapd.sh [deleted file]
package/network/services/hostapd/files/wdev.uc [deleted file]

diff --git a/package/base-files/files/sbin/wifi b/package/base-files/files/sbin/wifi
deleted file mode 100755 (executable)
index 5231063..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-#!/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
index a1feacbe2901a4e3efffe729f6dd23c22f53c505..101dbccbb546feeaa2b0e020a9dcdef7ce6e27e6 100644 (file)
@@ -40,7 +40,7 @@ endef
 define KernelPackage/brcm-wl/Default
   $(call Package/broadcom-wl/Default)
   SECTION:=kernel
-  DEPENDS:=@(TARGET_bcm47xx||TARGET_bcm63xx) +wireless-tools +@KERNEL_WIRELESS_EXT
+  DEPENDS:=@(TARGET_bcm47xx||TARGET_bcm63xx) +wireless-tools +wifi-scripts +@KERNEL_WIRELESS_EXT
   TITLE:=Kernel driver for BCM43xx chipsets
   FILES:=$(PKG_BUILD_DIR)/driver$(1)/wl.ko $(PKG_BUILD_DIR)/glue/wl_glue.ko
   AUTOLOAD:=$(call AutoProbe,wl)
index 17e0d9451e1c4bf7f0f2bbb0515e5056a4d6c0f7..6ca5c89cfaeb80ecbab2963ce7de5453c15cafe3 100644 (file)
@@ -92,7 +92,7 @@ PKG_CONFIG_DEPENDS += \
 define KernelPackage/cfg80211
   $(call KernelPackage/mac80211/Default)
   TITLE:=cfg80211 - wireless configuration API
-  DEPENDS+= +iw +iwinfo +wireless-regdb +USE_RFKILL:kmod-rfkill
+  DEPENDS+= +iw +iwinfo +wifi-scripts +wireless-regdb +USE_RFKILL:kmod-rfkill
   ABI_VERSION:=$(PKG_VERSION)-$(PKG_RELEASE)
   FILES:= \
        $(PKG_BUILD_DIR)/compat/compat.ko \
@@ -384,14 +384,6 @@ define Build/InstallDev
 endef
 
 
-define KernelPackage/cfg80211/install
-       $(INSTALL_DIR) $(1)/lib/wifi $(1)/lib/netifd/wireless
-       $(INSTALL_DATA) ./files/lib/wifi/mac80211.sh $(1)/lib/wifi
-       $(INSTALL_BIN) ./files/lib/netifd/wireless/mac80211.sh $(1)/lib/netifd/wireless
-       $(INSTALL_DIR) $(1)/etc/hotplug.d/ieee80211
-       $(INSTALL_DATA) ./files/mac80211.hotplug $(1)/etc/hotplug.d/ieee80211/10-wifi-detect
-endef
-
 $(eval $(foreach drv,$(PKG_DRIVERS),$(call KernelPackage,$(drv))))
 $(eval $(call KernelPackage,cfg80211))
 $(eval $(call KernelPackage,mac80211))
diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
deleted file mode 100644 (file)
index 49ffb21..0000000
+++ /dev/null
@@ -1,1197 +0,0 @@
-#!/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/kernel/mac80211/files/lib/wifi/mac80211.sh b/package/kernel/mac80211/files/lib/wifi/mac80211.sh
deleted file mode 100644 (file)
index e24a2a6..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-#!/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/kernel/mac80211/files/mac80211.hotplug b/package/kernel/mac80211/files/mac80211.hotplug
deleted file mode 100644 (file)
index b865552..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-[ "${ACTION}" = "add" ] && {
-       /sbin/wifi config
-}
index 558463e8ea5bc9e4944577a826f25973d07ed28c..ba4516c1dc6cc7db1830e8e61e271791cf513bc2 100644 (file)
@@ -43,7 +43,10 @@ define Package/netifd/install
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/netifd $(1)/sbin/
        $(CP) ./files/* $(1)/
        $(INSTALL_DIR) $(1)/etc/udhcpc.user.d/
-       $(CP) $(PKG_BUILD_DIR)/scripts/* $(1)/lib/netifd/
+       $(CP) \
+               $(PKG_BUILD_DIR)/scripts/utils.sh \
+               $(PKG_BUILD_DIR)/scripts/netifd-proto.sh \
+               $(1)/lib/netifd/
 endef
 
 $(eval $(call BuildPackage,netifd))
diff --git a/package/network/config/wifi-scripts/Makefile b/package/network/config/wifi-scripts/Makefile
new file mode 100644 (file)
index 0000000..e57f97f
--- /dev/null
@@ -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 (file)
index 0000000..b865552
--- /dev/null
@@ -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 (file)
index 0000000..1a664ab
--- /dev/null
@@ -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 (file)
index 0000000..5b852e0
--- /dev/null
@@ -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 (executable)
index 0000000..49ffb21
--- /dev/null
@@ -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 (file)
index 0000000..e24a2a6
--- /dev/null
@@ -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 (executable)
index 0000000..5231063
--- /dev/null
@@ -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 (file)
index 0000000..4c33779
--- /dev/null
@@ -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 (file)
index 0000000..ff4d629
--- /dev/null
@@ -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);
index 17f9dcb581dbea4d045910329ebe7ce5bc8cc6ce..c8f476f7b8c88e5c97ce9697d55613ea5920ca82 100644 (file)
@@ -712,13 +712,10 @@ endef
 define Package/hostapd-common/install
        $(INSTALL_DIR) $(1)/etc/capabilities $(1)/etc/rc.button $(1)/etc/hotplug.d/ieee80211 $(1)/etc/init.d $(1)/lib/netifd  $(1)/usr/share/acl.d $(1)/usr/share/hostap
        $(INSTALL_BIN) ./files/dhcp-get-server.sh $(1)/lib/netifd/dhcp-get-server.sh
-       $(INSTALL_DATA) ./files/hostapd.sh $(1)/lib/netifd/hostapd.sh
        $(INSTALL_BIN) ./files/wpad.init $(1)/etc/init.d/wpad
        $(INSTALL_BIN) ./files/wps-hotplug.sh $(1)/etc/rc.button/wps
        $(INSTALL_DATA) ./files/wpad_acl.json $(1)/usr/share/acl.d
        $(INSTALL_DATA) ./files/wpad.json $(1)/etc/capabilities
-       $(INSTALL_DATA) ./files/common.uc $(1)/usr/share/hostap/
-       $(INSTALL_DATA) ./files/wdev.uc $(1)/usr/share/hostap/
 endef
 
 define Package/hostapd/install
diff --git a/package/network/services/hostapd/files/common.uc b/package/network/services/hostapd/files/common.uc
deleted file mode 100644 (file)
index 4c33779..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-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/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh
deleted file mode 100644 (file)
index 1a664ab..0000000
+++ /dev/null
@@ -1,1598 +0,0 @@
-. /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/services/hostapd/files/wdev.uc b/package/network/services/hostapd/files/wdev.uc
deleted file mode 100644 (file)
index ff4d629..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/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);