[PATCH/RFT 23.05 3/4] hostapd: backport from master, including ucode based reload support
Felix Fietkau
nbd at nbd.name
Wed Aug 30 09:15:58 PDT 2023
This significantly improves config reload behavior and also fixes some
corner cases related to running AP + mesh interfaces at the same time.
Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
.../files/lib/netifd/wireless/mac80211.sh | 570 ++++++--------
package/network/services/hostapd/Config.in | 5 -
package/network/services/hostapd/Makefile | 58 +-
.../network/services/hostapd/files/common.uc | 168 ++++
.../network/services/hostapd/files/hostapd.sh | 35 +-
.../network/services/hostapd/files/hostapd.uc | 486 ++++++++++++
.../services/hostapd/files/radius.clients | 1 +
.../services/hostapd/files/radius.config | 9 +
.../services/hostapd/files/radius.init | 42 +
.../services/hostapd/files/radius.users | 14 +
.../network/services/hostapd/files/wdev.uc | 156 ++++
.../hostapd/files/wpa_supplicant-basic.config | 2 +-
.../hostapd/files/wpa_supplicant-full.config | 2 +-
.../hostapd/files/wpa_supplicant-mini.config | 2 +-
.../hostapd/files/wpa_supplicant-p2p.config | 2 +-
.../services/hostapd/files/wpa_supplicant.uc | 254 +++++++
.../hostapd/patches/100-daemonize_fix.patch | 97 ---
...S-coloring-fix-CCA-with-multiple-BSS.patch | 103 +++
.../hostapd/patches/200-multicall.patch | 2 +-
.../patches/340-reload_freq_change.patch | 80 --
.../patches/360-ctrl_iface_reload.patch | 106 ---
.../hostapd/patches/370-ap_sta_support.patch | 392 ----------
.../patches/380-disable_ctrl_iface_mib.patch | 6 +-
.../patches/420-indicate-features.patch | 8 +-
.../hostapd/patches/432-missing-typedef.patch | 10 -
.../hostapd/patches/450-scan_wait.patch | 73 --
...dd-new-config-params-to-be-used-with.patch | 2 +-
.../patches/464-fix-mesh-obss-check.patch | 2 +-
.../patches/500-lto-jobserver-support.patch | 2 +-
.../hostapd/patches/600-ubus_support.patch | 211 ++++--
.../hostapd/patches/601-ucode_support.patch | 333 ++++++++
.../hostapd/patches/700-wifi-reload.patch | 194 -----
.../patches/701-reload_config_inline.patch | 33 +
.../hostapd/patches/710-vlan_no_bridge.patch | 2 +-
.../patches/720-iface_max_num_sta.patch | 15 +-
.../hostapd/patches/730-ft_iface.patch | 2 +-
...750-qos_map_set_without_interworking.patch | 6 +-
.../hostapd/patches/760-dynamic_own_ip.patch | 2 +-
.../hostapd/patches/761-shared_das_port.patch | 2 +-
.../hostapd/patches/770-radius_server.patch | 154 ++++
.../patches/991-Fix-OpenWrt-13156.patch | 4 +-
.../services/hostapd/src/hostapd/radius.c | 715 ++++++++++++++++++
.../services/hostapd/src/src/ap/ubus.c | 215 ++----
.../services/hostapd/src/src/ap/ucode.c | 553 ++++++++++++++
.../services/hostapd/src/src/ap/ucode.h | 54 ++
.../services/hostapd/src/src/utils/ucode.c | 326 ++++++++
.../services/hostapd/src/src/utils/ucode.h | 29 +
.../hostapd/src/wpa_supplicant/ubus.c | 162 +---
.../hostapd/src/wpa_supplicant/ubus.h | 11 -
.../hostapd/src/wpa_supplicant/ucode.c | 270 +++++++
.../hostapd/src/wpa_supplicant/ucode.h | 49 ++
51 files changed, 4300 insertions(+), 1731 deletions(-)
create mode 100644 package/network/services/hostapd/files/common.uc
create mode 100644 package/network/services/hostapd/files/hostapd.uc
create mode 100644 package/network/services/hostapd/files/radius.clients
create mode 100644 package/network/services/hostapd/files/radius.config
create mode 100644 package/network/services/hostapd/files/radius.init
create mode 100644 package/network/services/hostapd/files/radius.users
create mode 100644 package/network/services/hostapd/files/wdev.uc
create mode 100644 package/network/services/hostapd/files/wpa_supplicant.uc
delete mode 100644 package/network/services/hostapd/patches/100-daemonize_fix.patch
create mode 100644 package/network/services/hostapd/patches/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
delete mode 100644 package/network/services/hostapd/patches/340-reload_freq_change.patch
delete mode 100644 package/network/services/hostapd/patches/360-ctrl_iface_reload.patch
delete mode 100644 package/network/services/hostapd/patches/370-ap_sta_support.patch
delete mode 100644 package/network/services/hostapd/patches/432-missing-typedef.patch
delete mode 100644 package/network/services/hostapd/patches/450-scan_wait.patch
create mode 100644 package/network/services/hostapd/patches/601-ucode_support.patch
delete mode 100644 package/network/services/hostapd/patches/700-wifi-reload.patch
create mode 100644 package/network/services/hostapd/patches/701-reload_config_inline.patch
create mode 100644 package/network/services/hostapd/patches/770-radius_server.patch
create mode 100644 package/network/services/hostapd/src/hostapd/radius.c
create mode 100644 package/network/services/hostapd/src/src/ap/ucode.c
create mode 100644 package/network/services/hostapd/src/src/ap/ucode.h
create mode 100644 package/network/services/hostapd/src/src/utils/ucode.c
create mode 100644 package/network/services/hostapd/src/src/utils/ucode.h
create mode 100644 package/network/services/hostapd/src/wpa_supplicant/ucode.c
create mode 100644 package/network/services/hostapd/src/wpa_supplicant/ucode.h
diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
index 5aaba9af26c6..39683376b592 100644
--- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
+++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
@@ -15,12 +15,9 @@ MP_CONFIG_INT="mesh_retry_timeout mesh_confirm_timeout mesh_holding_timeout mesh
MP_CONFIG_BOOL="mesh_auto_open_plinks mesh_fwding"
MP_CONFIG_STRING="mesh_power_mode"
-NEWAPLIST=
-OLDAPLIST=
-NEWSPLIST=
-OLDSPLIST=
-NEWUMLIST=
-OLDUMLIST=
+wdev_tool() {
+ ucode /usr/share/hostap/wdev.uc "$@"
+}
drv_mac80211_init_device_config() {
hostapd_common_add_device_config
@@ -494,8 +491,6 @@ $base_cfg
EOF
json_select ..
- radio_md5sum=$(md5sum $hostapd_conf_file | cut -d" " -f1)
- echo "radio_config_id=${radio_md5sum}" >> $hostapd_conf_file
}
mac80211_hostapd_setup_bss() {
@@ -661,74 +656,6 @@ mac80211_check_ap() {
has_ap=1
}
-mac80211_iw_interface_add() {
- local phy="$1"
- local ifname="$2"
- local type="$3"
- local wdsflag="$4"
- local rc
- local oldifname
-
- iw phy "$phy" interface add "$ifname" type "$type" $wdsflag >/dev/null 2>&1
- rc="$?"
-
- [ "$rc" = 233 ] && {
- # Device might have just been deleted, give the kernel some time to finish cleaning it up
- sleep 1
-
- iw phy "$phy" interface add "$ifname" type "$type" $wdsflag >/dev/null 2>&1
- rc="$?"
- }
-
- [ "$rc" = 233 ] && {
- # Keep matching pre-existing interface
- [ -d "/sys/class/ieee80211/${phy}/device/net/${ifname}" ] && \
- case "$(iw dev $ifname info | grep "^\ttype" | cut -d' ' -f2- 2>/dev/null)" in
- "AP")
- [ "$type" = "__ap" ] && rc=0
- ;;
- "IBSS")
- [ "$type" = "adhoc" ] && rc=0
- ;;
- "managed")
- [ "$type" = "managed" ] && rc=0
- ;;
- "mesh point")
- [ "$type" = "mp" ] && rc=0
- ;;
- "monitor")
- [ "$type" = "monitor" ] && rc=0
- ;;
- esac
- }
-
- [ "$rc" = 233 ] && {
- iw dev "$ifname" del >/dev/null 2>&1
- [ "$?" = 0 ] && {
- sleep 1
-
- iw phy "$phy" interface add "$ifname" type "$type" $wdsflag >/dev/null 2>&1
- rc="$?"
- }
- }
-
- [ "$rc" != 0 ] && {
- # Device might not support virtual interfaces, so the interface never got deleted in the first place.
- # Check if the interface already exists, and avoid failing in this case.
- [ -d "/sys/class/ieee80211/${phy}/device/net/${ifname}" ] && rc=0
- }
-
- [ "$rc" != 0 ] && {
- # Device doesn't support virtual interfaces and may have existing interface other than ifname.
- oldifname="$(basename "/sys/class/ieee80211/${phy}/device/net"/* 2>/dev/null)"
- [ "$oldifname" ] && ip link set "$oldifname" name "$ifname" 1>/dev/null 2>&1
- rc="$?"
- }
-
- [ "$rc" != 0 ] && echo "Failed to create interface $ifname"
- return $rc
-}
-
mac80211_set_ifname() {
local phy="$1"
local prefix="$2"
@@ -752,10 +679,10 @@ mac80211_prepare_vif() {
mac80211_set_ifname "$phy" "$prefix"
}
+ append active_ifnames "$ifname"
set_default wds 0
set_default powersave 0
-
- json_select ..
+ json_add_string _ifname "$ifname"
if [ -z "$macaddr" ]; then
macaddr="$(mac80211_generate_mac $phy)"
@@ -763,10 +690,9 @@ mac80211_prepare_vif() {
elif [ "$macaddr" = 'random' ]; then
macaddr="$(macaddr_random)"
fi
+ json_add_string _macaddr "$macaddr"
+ json_select ..
- json_add_object data
- json_add_string ifname "$ifname"
- json_close_object
[ "$mode" == "ap" ] && {
[ -z "$wpa_psk_file" ] && hostapd_set_psk "$ifname"
@@ -777,9 +703,6 @@ mac80211_prepare_vif() {
# It is far easier to delete and create the desired interface
case "$mode" in
- adhoc)
- mac80211_iw_interface_add "$phy" "$ifname" adhoc || return
- ;;
ap)
# Hostapd will handle recreating the interface and
# subsequent virtual APs belonging to the same PHY
@@ -791,114 +714,16 @@ mac80211_prepare_vif() {
mac80211_hostapd_setup_bss "$phy" "$ifname" "$macaddr" "$type" || return
- NEWAPLIST="${NEWAPLIST}$ifname "
[ -n "$hostapd_ctrl" ] || {
ap_ifname="${ifname}"
hostapd_ctrl="${hostapd_ctrl:-/var/run/hostapd/$ifname}"
}
;;
- mesh)
- mac80211_iw_interface_add "$phy" "$ifname" mp || return
- ;;
- monitor)
- mac80211_iw_interface_add "$phy" "$ifname" monitor || return
- ;;
- sta)
- local wdsflag=
- [ "$enable" = 0 ] || staidx="$(($staidx + 1))"
- [ "$wds" -gt 0 ] && wdsflag="4addr on"
- mac80211_iw_interface_add "$phy" "$ifname" managed "$wdsflag" || return
- if [ "$wds" -gt 0 ]; then
- iw "$ifname" set 4addr on
- else
- iw "$ifname" set 4addr off
- fi
- [ "$powersave" -gt 0 ] && powersave="on" || powersave="off"
- iw "$ifname" set power_save "$powersave"
- ;;
esac
- case "$mode" in
- monitor|mesh)
- [ "$auto_channel" -gt 0 ] || iw dev "$ifname" set channel "$channel" $iw_htmode
- ;;
- esac
-
- if [ "$mode" != "ap" ]; then
- # ALL ap functionality will be passed to hostapd
- # All interfaces must have unique mac addresses
- # which can either be explicitly set in the device
- # section, or automatically generated
- ip link set dev "$ifname" address "$macaddr"
- fi
-
json_select ..
}
-mac80211_setup_supplicant() {
- local enable=$1
- local add_sp=0
- local spobj="$(ubus -S list | grep wpa_supplicant.${ifname})"
-
- [ "$enable" = 0 ] && {
- ubus call wpa_supplicant.${phy} config_remove "{\"iface\":\"$ifname\"}"
- ip link set dev "$ifname" down
- iw dev "$ifname" del
- return 0
- }
-
- wpa_supplicant_prepare_interface "$ifname" nl80211 || {
- iw dev "$ifname" del
- return 1
- }
- if [ "$mode" = "sta" ]; then
- wpa_supplicant_add_network "$ifname"
- else
- wpa_supplicant_add_network "$ifname" "$freq" "$htmode" "$noscan"
- fi
-
- NEWSPLIST="${NEWSPLIST}$ifname "
-
- if [ "${NEWAPLIST%% *}" != "${OLDAPLIST%% *}" ]; then
- [ "$spobj" ] && ubus call wpa_supplicant config_remove "{\"iface\":\"$ifname\"}"
- add_sp=1
- fi
- [ -z "$spobj" ] && add_sp=1
-
- NEW_MD5_SP=$(test -e "${_config}" && md5sum ${_config})
- OLD_MD5_SP=$(uci -q -P /var/state get wireless._${phy}.md5_${ifname})
- if [ "$add_sp" = "1" ]; then
- wpa_supplicant_run "$ifname" "$hostapd_ctrl"
- else
- [ "${NEW_MD5_SP}" == "${OLD_MD5_SP}" ] || ubus call $spobj reload
- fi
- uci -q -P /var/state set wireless._${phy}.md5_${ifname}="${NEW_MD5_SP}"
- return 0
-}
-
-mac80211_setup_supplicant_noctl() {
- local enable=$1
- local spobj="$(ubus -S list | grep wpa_supplicant.${ifname})"
- wpa_supplicant_prepare_interface "$ifname" nl80211 || {
- iw dev "$ifname" del
- return 1
- }
-
- wpa_supplicant_add_network "$ifname" "$freq" "$htmode" "$noscan"
-
- NEWSPLIST="${NEWSPLIST}$ifname "
- [ "$enable" = 0 ] && {
- ubus call wpa_supplicant config_remove "{\"iface\":\"$ifname\"}"
- ip link set dev "$ifname" down
- return 0
- }
- if [ -z "$spobj" ]; then
- wpa_supplicant_run "$ifname"
- else
- ubus call $spobj reload
- fi
-}
-
mac80211_prepare_iw_htmode() {
case "$htmode" in
VHT20|HT20|HE20) iw_htmode=HT20;;
@@ -936,6 +761,13 @@ mac80211_prepare_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
@@ -977,82 +809,216 @@ mac80211_setup_adhoc() {
mcval=
[ -n "$mcast_rate" ] && wpa_supplicant_add_rate mcval "$mcast_rate"
- iw dev "$ifname" set type ibss
- iw dev "$ifname" ibss join "$ssid" $freq $iw_htmode fixed-freq $bssid \
- beacon-interval $beacon_int \
- ${brstr:+basic-rates $brstr} \
- ${mcval:+mcast-rate $mcval} \
- ${keyspec:+keys $keyspec}
+ local prev
+ json_set_namespace wdev_uc prev
+
+ json_add_object "$ifname"
+ json_add_string mode adhoc
+ 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() {
- local enable=$1
json_get_vars ssid mesh_id mcast_rate
- NEWUMLIST="${NEWUMLIST}$ifname "
-
- [ "$enable" = 0 ] && {
- ip link set dev "$ifname" down
- return 0
- }
-
mcval=
[ -n "$mcast_rate" ] && wpa_supplicant_add_rate mcval "$mcast_rate"
[ -n "$mesh_id" ] && ssid="$mesh_id"
- iw dev "$ifname" mesh join "$ssid" freq $freq $iw_htmode \
- ${mcval:+mcast-rate $mcval} \
- beacon-interval $beacon_int
+ local prev
+ json_set_namespace wdev_uc prev
+
+ json_add_object "$ifname"
+ json_add_string mode mesh
+ 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_vif() {
+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"
- local failed
- local action=up
- json_select data
- json_get_vars ifname
+ json_select config
+ json_get_var ifname _ifname
+ json_get_vars vif_txpower
json_select ..
- json_select config
- json_get_vars mode
- json_get_var vif_txpower
- json_get_var vif_enable enable 1
-
- [ "$vif_enable" = 1 ] || action=down
- if [ "$mode" != "ap" ] || [ "$ifname" = "$ap_ifname" ]; then
- ip link set dev "$ifname" "$action" || {
- wireless_setup_vif_failed IFUP_ERROR
- json_select ..
- return
- }
- [ -z "$vif_txpower" ] || iw dev "$ifname" set txpower fixed "${vif_txpower%%.*}00"
+ [ -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 hostapd_ctrl="$3"
+ 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"
+ json_add_string macaddr "$macaddr"
+ [ -n "$network_bridge" ] && json_add_string bridge "$network_bridge"
+ [ -n "$hostapd_ctrl" ] && json_add_string hostapd_ctrl "$hostapd_ctrl"
+ [ -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" "$noscan"
fi
+ wpa_supplicant_add_interface "$ifname" "$mode" "$hostapd_ctrl"
+
+ 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_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 || [ "$wpa" -gt 0 -o "$auto_channel" -gt 0 ] || chan_is_dfs "$phy" "$channel"; then
- mac80211_setup_supplicant $vif_enable || failed=1
+ if wpa_supplicant -vmesh; then
+ mac80211_setup_supplicant || failed=1
else
- mac80211_setup_mesh $vif_enable
+ mac80211_setup_mesh
fi
- for var in $MP_CONFIG_INT $MP_CONFIG_BOOL $MP_CONFIG_STRING; do
- json_get_var mp_val "$var"
- [ -n "$mp_val" ] && iw dev "$ifname" set mesh_param "$var" "$mp_val"
- done
;;
adhoc)
wireless_vif_parse_encryption
if [ "$wpa" -gt 0 -o "$auto_channel" -gt 0 ]; then
- mac80211_setup_supplicant_noctl $vif_enable || failed=1
+ mac80211_setup_supplicant || failed=1
else
- mac80211_setup_adhoc $vif_enable
+ mac80211_setup_adhoc
fi
;;
sta)
- mac80211_setup_supplicant $vif_enable || failed=1
+ mac80211_setup_supplicant || failed=1
+ ;;
+ monitor)
+ mac80211_setup_monitor
;;
esac
@@ -1085,7 +1051,6 @@ band_match && $3 == "MHz" && $4 == channel {
'
}
-
chan_is_dfs() {
local phy="$1"
local chan="$2"
@@ -1093,27 +1058,6 @@ chan_is_dfs() {
return $!
}
-mac80211_vap_cleanup() {
- local service="$1"
- local vaps="$2"
-
- for wdev in $vaps; do
- [ "$service" != "none" ] && ubus call ${service} config_remove "{\"iface\":\"$wdev\"}"
- ip link set dev "$wdev" down 2>/dev/null
- iw dev "$wdev" del
- done
-}
-
-mac80211_interface_cleanup() {
- local phy="$1"
- local primary_ap=$(uci -q -P /var/state get wireless._${phy}.aplist)
- primary_ap=${primary_ap%% *}
-
- mac80211_vap_cleanup hostapd "${primary_ap}"
- mac80211_vap_cleanup wpa_supplicant "$(uci -q -P /var/state get wireless._${phy}.splist)"
- mac80211_vap_cleanup none "$(uci -q -P /var/state get wireless._${phy}.umlist)"
-}
-
mac80211_set_noscan() {
hostapd_noscan=1
}
@@ -1122,6 +1066,15 @@ drv_mac80211_cleanup() {
hostapd_common_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" '{}'
+}
+
drv_mac80211_setup() {
json_select config
json_get_vars \
@@ -1134,37 +1087,22 @@ drv_mac80211_setup() {
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
}
- wireless_set_data phy="$phy"
- [ -z "$(uci -q -P /var/state show wireless._${phy})" ] && uci -q -P /var/state set wireless._${phy}=phy
-
- OLDAPLIST=$(uci -q -P /var/state get wireless._${phy}.aplist)
- OLDSPLIST=$(uci -q -P /var/state get wireless._${phy}.splist)
- OLDUMLIST=$(uci -q -P /var/state get wireless._${phy}.umlist)
-
local wdev
local cwdev
local found
- for wdev in $(list_phy_interfaces "$phy"); do
- found=0
- for cwdev in $OLDAPLIST $OLDSPLIST $OLDUMLIST; do
- if [ "$wdev" = "$cwdev" ]; then
- found=1
- break
- fi
- done
- if [ "$found" = "0" ]; then
- ip link set dev "$wdev" down
- iw dev "$wdev" del
- fi
- done
-
# convert channel to frequency
[ "$auto_channel" -gt 0 ] || freq="$(get_freq "$phy" "$channel" "$band")"
@@ -1177,7 +1115,6 @@ drv_mac80211_setup() {
hostapd_conf_file="/var/run/hostapd-$phy.conf"
- no_ap=1
macidx=0
staidx=0
@@ -1195,6 +1132,9 @@ drv_mac80211_setup() {
[ "$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 antenna_gain $antenna_gain >/dev/null 2>&1
iw phy "$phy" set distance "$distance" >/dev/null 2>&1
@@ -1212,78 +1152,36 @@ drv_mac80211_setup() {
hostapd_ctrl=
ap_ifname=
hostapd_noscan=
+ wpa_supp_init=
for_each_interface "ap" mac80211_check_ap
- rm -f "$hostapd_conf_file"
+ [ -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"
- mac80211_prepare_iw_htmode
- for_each_interface "sta adhoc mesh monitor" mac80211_prepare_vif
- NEWAPLIST=
- for_each_interface "ap" mac80211_prepare_vif
- NEW_MD5=$(test -e "${hostapd_conf_file}" && md5sum ${hostapd_conf_file})
- OLD_MD5=$(uci -q -P /var/state get wireless._${phy}.md5)
- if [ "${NEWAPLIST}" != "${OLDAPLIST}" ]; then
- mac80211_vap_cleanup hostapd "${OLDAPLIST}"
- fi
- [ -n "${NEWAPLIST}" ] && mac80211_iw_interface_add "$phy" "${NEWAPLIST%% *}" __ap
- local add_ap=0
- local primary_ap=${NEWAPLIST%% *}
- [ -n "$hostapd_ctrl" ] && {
- local no_reload=1
- if [ -n "$(ubus list | grep hostapd.$primary_ap)" ]; then
- no_reload=0
- [ "${NEW_MD5}" = "${OLD_MD5}" ] || {
- ubus call hostapd.$primary_ap reload
- no_reload=$?
- if [ "$no_reload" != "0" ]; then
- mac80211_vap_cleanup hostapd "${OLDAPLIST}"
- mac80211_vap_cleanup wpa_supplicant "$(uci -q -P /var/state get wireless._${phy}.splist)"
- mac80211_vap_cleanup none "$(uci -q -P /var/state get wireless._${phy}.umlist)"
- sleep 2
- mac80211_iw_interface_add "$phy" "${NEWAPLIST%% *}" __ap
- for_each_interface "sta adhoc mesh monitor" mac80211_prepare_vif
- fi
- }
- fi
- if [ "$no_reload" != "0" ]; then
- add_ap=1
- ubus wait_for hostapd
- local hostapd_res="$(ubus call hostapd config_add "{\"iface\":\"$primary_ap\", \"config\":\"${hostapd_conf_file}\"}")"
- 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
- fi
- }
- uci -q -P /var/state set wireless._${phy}.aplist="${NEWAPLIST}"
- uci -q -P /var/state set wireless._${phy}.md5="${NEW_MD5}"
+ local prev
+ json_set_namespace wdev_uc prev
+ json_init
+ json_set_namespace "$prev"
- [ "${add_ap}" = 1 ] && sleep 1
- for_each_interface "ap" mac80211_setup_vif
+ wpa_supplicant_init_config
- NEWSPLIST=
- NEWUMLIST=
+ 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
- for_each_interface "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"
- uci -q -P /var/state set wireless._${phy}.splist="${NEWSPLIST}"
- uci -q -P /var/state set wireless._${phy}.umlist="${NEWUMLIST}"
+ [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_start "$phy"
- local foundvap
- local dropvap=""
- for oldvap in $OLDSPLIST; do
- foundvap=0
- for newvap in $NEWSPLIST; do
- [ "$oldvap" = "$newvap" ] && foundvap=1
- done
- [ "$foundvap" = "0" ] && dropvap="$dropvap $oldvap"
- done
- [ -n "$dropvap" ] && mac80211_vap_cleanup wpa_supplicant "$dropvap"
+ json_set_namespace wdev_uc prev
+ wdev_tool "$phy" "$(json_dump)" $active_ifnames
+ json_set_namespace "$prev"
+
+ for_each_interface "ap sta adhoc mesh monitor" mac80211_set_vif_txpower
wireless_set_up
}
@@ -1314,8 +1212,12 @@ drv_mac80211_teardown() {
return 1
}
- mac80211_interface_cleanup "$phy"
- uci -q -P /var/state revert wireless._${phy}
+ 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/services/hostapd/Config.in b/package/network/services/hostapd/Config.in
index 8f28eb2bd4f4..87ad7e093e7d 100644
--- a/package/network/services/hostapd/Config.in
+++ b/package/network/services/hostapd/Config.in
@@ -73,11 +73,6 @@ config WPA_WOLFSSL
select WOLFSSL_HAS_SESSION_TICKET
select WOLFSSL_HAS_WPAS
-config DRIVER_WEXT_SUPPORT
- bool
- select KERNEL_WIRELESS_EXT
- default n
-
config DRIVER_11AC_SUPPORT
bool
default n
diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile
index 287a70c80e31..178dd20fcd1d 100644
--- a/package/network/services/hostapd/Makefile
+++ b/package/network/services/hostapd/Makefile
@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=hostapd
-PKG_RELEASE:=1.2
+PKG_RELEASE:=2
PKG_SOURCE_URL:=http://w1.fi/hostap.git
PKG_SOURCE_PROTO:=git
@@ -27,7 +27,6 @@ PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_hostapd-basic \
CONFIG_PACKAGE_hostapd-mini \
CONFIG_WPA_RFKILL_SUPPORT \
- CONFIG_DRIVER_WEXT_SUPPORT \
CONFIG_DRIVER_11AC_SUPPORT \
CONFIG_DRIVER_11AX_SUPPORT \
CONFIG_WPA_ENABLE_WEP
@@ -82,13 +81,15 @@ ifneq ($(CONFIG_DRIVER_11AX_SUPPORT),)
HOSTAPD_IEEE80211AX:=y
endif
+CORE_DEPENDS = +ucode +libubus +libucode +ucode-mod-fs +ucode-mod-nl80211 +ucode-mod-rtnl +ucode-mod-ubus +ucode-mod-uloop +libblobmsg-json
+
DRIVER_MAKEOPTS= \
CONFIG_ACS=$(CONFIG_PACKAGE_kmod-cfg80211) \
CONFIG_DRIVER_NL80211=$(CONFIG_PACKAGE_kmod-cfg80211) \
CONFIG_IEEE80211AC=$(HOSTAPD_IEEE80211AC) \
CONFIG_IEEE80211AX=$(HOSTAPD_IEEE80211AX) \
- CONFIG_DRIVER_WEXT=$(CONFIG_DRIVER_WEXT_SUPPORT) \
- CONFIG_MBO=$(CONFIG_WPA_MBO_SUPPORT)
+ CONFIG_MBO=$(CONFIG_WPA_MBO_SUPPORT) \
+ CONFIG_UCODE=y
ifeq ($(SSL_VARIANT),openssl)
DRIVER_MAKEOPTS += CONFIG_TLS=openssl CONFIG_SAE=y
@@ -150,7 +151,7 @@ define Package/hostapd/Default
SUBMENU:=WirelessAPD
TITLE:=IEEE 802.1x Authenticator
URL:=http://hostap.epitest.fi/
- DEPENDS:=$(DRV_DEPENDS) +hostapd-common +libubus
+ DEPENDS:=$(DRV_DEPENDS) +hostapd-common $(CORE_DEPENDS)
EXTRA_DEPENDS:=hostapd-common (=$(PKG_VERSION)-$(PKG_RELEASE))
USERID:=network=101:network=101
PROVIDES:=hostapd
@@ -255,7 +256,7 @@ define Package/wpad/Default
CATEGORY:=Network
SUBMENU:=WirelessAPD
TITLE:=IEEE 802.1x Auth/Supplicant
- DEPENDS:=$(DRV_DEPENDS) +hostapd-common +libubus
+ DEPENDS:=$(DRV_DEPENDS) +hostapd-common $(CORE_DEPENDS)
EXTRA_DEPENDS:=hostapd-common (=$(PKG_VERSION)-$(PKG_RELEASE))
USERID:=network=101:network=101
URL:=http://hostap.epitest.fi/
@@ -400,7 +401,7 @@ define Package/wpa-supplicant/Default
SUBMENU:=WirelessAPD
TITLE:=WPA Supplicant
URL:=http://hostap.epitest.fi/wpa_supplicant/
- DEPENDS:=$(DRV_DEPENDS) +hostapd-common +libubus
+ DEPENDS:=$(DRV_DEPENDS) +hostapd-common $(CORE_DEPENDS)
EXTRA_DEPENDS:=hostapd-common (=$(PKG_VERSION)-$(PKG_RELEASE))
USERID:=network=101:network=101
PROVIDES:=wpa-supplicant
@@ -522,7 +523,7 @@ define Package/eapol-test/Default
SECTION:=net
SUBMENU:=WirelessAPD
CATEGORY:=Network
- DEPENDS:=$(DRV_DEPENDS) +libubus
+ DEPENDS:=$(DRV_DEPENDS) $(CORE_DEPENDS)
endef
define Package/eapol-test
@@ -587,7 +588,7 @@ TARGET_CPPFLAGS := \
-D_GNU_SOURCE \
$(if $(CONFIG_WPA_MSG_MIN_PRIORITY),-DCONFIG_MSG_MIN_PRIORITY=$(CONFIG_WPA_MSG_MIN_PRIORITY))
-TARGET_LDFLAGS += -lubox -lubus
+TARGET_LDFLAGS += -lubox -lubus -lblobmsg_json -lucode
ifdef CONFIG_PACKAGE_kmod-cfg80211
TARGET_LDFLAGS += -lm -lnl-tiny
@@ -676,22 +677,55 @@ define Build/Compile
$(Build/Compile/$(BUILD_VARIANT))
endef
+define Install/hostapd/full
+ $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/radius
+ ln -sf hostapd $(1)/usr/sbin/hostapd-radius
+ $(INSTALL_BIN) ./files/radius.init $(1)/etc/init.d/radius
+ $(INSTALL_DATA) ./files/radius.config $(1)/etc/config/radius
+ $(INSTALL_DATA) ./files/radius.clients $(1)/etc/radius/clients
+ $(INSTALL_DATA) ./files/radius.users $(1)/etc/radius/users
+endef
+
+define Package/hostapd-full/conffiles
+/etc/config/radius
+/etc/radius
+endef
+
+ifeq ($(CONFIG_VARIANT),full)
+Package/wpad-mesh-openssl/conffiles = $(Package/hostapd-full/conffiles)
+Package/wpad-mesh-wolfssl/conffiles = $(Package/hostapd-full/conffiles)
+Package/wpad-mesh-mbedtls/conffiles = $(Package/hostapd-full/conffiles)
+Package/wpad/conffiles = $(Package/hostapd-full/conffiles)
+Package/wpad-openssl/conffiles = $(Package/hostapd-full/conffiles)
+Package/wpad-wolfssl/conffiles = $(Package/hostapd-full/conffiles)
+Package/wpad-mbedtls/conffiles = $(Package/hostapd-full/conffiles)
+Package/hostapd/conffiles = $(Package/hostapd-full/conffiles)
+Package/hostapd-openssl/conffiles = $(Package/hostapd-full/conffiles)
+Package/hostapd-wolfssl/conffiles = $(Package/hostapd-full/conffiles)
+Package/hostapd-mbedtls/conffiles = $(Package/hostapd-full/conffiles)
+endif
+
define Install/hostapd
- $(INSTALL_DIR) $(1)/usr/sbin
+ $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/hostap
+ $(INSTALL_DATA) ./files/hostapd.uc $(1)/usr/share/hostap/
+ $(if $(findstring full,$(CONFIG_VARIANT)),$(Install/hostapd/full))
endef
define Install/supplicant
- $(INSTALL_DIR) $(1)/usr/sbin
+ $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/hostap
+ $(INSTALL_DATA) ./files/wpa_supplicant.uc $(1)/usr/share/hostap/
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
+ $(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
new file mode 100644
index 000000000000..9ece3b1af2ea
--- /dev/null
+++ b/package/network/services/hostapd/files/common.uc
@@ -0,0 +1,168 @@
+import * as nl80211 from "nl80211";
+import * as rtnl from "rtnl";
+import { readfile } 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,
+};
+
+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;
+}
+
+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])
+ return false;
+ return true;
+ } else {
+ return val1 == val2;
+ }
+}
+
+function vlist_new(cb) {
+ return proto({
+ cb: cb,
+ data: {}
+ }, vlist_proto);
+}
+
+export { wdev_remove, wdev_create, is_equal, vlist_new, phy_is_fullmac };
diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh
index 28bd210623b4..88dfe25091f9 100644
--- a/package/network/services/hostapd/files/hostapd.sh
+++ b/package/network/services/hostapd/files/hostapd.sh
@@ -121,6 +121,7 @@ hostapd_common_add_device_config() {
config_add_array hostapd_options
config_add_int airtime_mode
+ config_add_int mbssid
hostapd_add_log_config
}
@@ -133,7 +134,8 @@ hostapd_prepare_device_config() {
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
+ rts_threshold beacon_rate rssi_reject_assoc_rssi rssi_ignore_probe_request maxassoc \
+ mbssid:0
hostapd_set_log_options base_cfg
@@ -234,6 +236,7 @@ hostapd_prepare_device_config() {
[ -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
@@ -864,8 +867,9 @@ hostapd_set_bss_options() {
[ "$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
+ 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
@@ -876,6 +880,7 @@ hostapd_set_bss_options() {
[ "$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
@@ -1156,9 +1161,6 @@ hostapd_set_bss_options() {
append bss_conf "$val" "$N"
done
- bss_md5sum="$(echo $bss_conf | md5sum | cut -d" " -f1)"
- append bss_conf "config_id=$bss_md5sum" "$N"
-
append "$var" "$bss_conf" "$N"
return 0
}
@@ -1588,29 +1590,6 @@ EOF
return 0
}
-wpa_supplicant_run() {
- local ifname="$1"
- local hostapd_ctrl="$2"
-
- _wpa_supplicant_common "$ifname"
-
- ubus wait_for wpa_supplicant
- local supplicant_res="$(ubus call wpa_supplicant config_add "{ \
- \"driver\": \"${_w_driver:-wext}\", \"ctrl\": \"$_rpath\", \
- \"iface\": \"$ifname\", \"config\": \"$_config\" \
- ${network_bridge:+, \"bridge\": \"$network_bridge\"} \
- ${hostapd_ctrl:+, \"hostapd_ctrl\": \"$hostapd_ctrl\"} \
- }")"
-
- 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
-
- return $ret
-}
-
hostapd_common_cleanup() {
killall meshd-nl80211
}
diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc
new file mode 100644
index 000000000000..f9f0c31babc2
--- /dev/null
+++ b/package/network/services/hostapd/files/hostapd.uc
@@ -0,0 +1,486 @@
+let libubus = require("ubus");
+import { open, readfile } from "fs";
+import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac } from "common";
+
+let ubus = libubus.connect();
+
+hostapd.data.config = {};
+
+hostapd.data.file_fields = {
+ vlan_file: true,
+ wpa_psk_file: true,
+ accept_mac_file: true,
+ deny_mac_file: true,
+ eap_user_file: true,
+ ca_cert: true,
+ server_cert: true,
+ server_cert2: true,
+ private_key: true,
+ private_key2: true,
+ dh_file: true,
+ eap_sim_db: true,
+};
+
+function iface_remove(cfg)
+{
+ if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
+ return;
+
+ hostapd.remove_iface(cfg.bss[0].ifname);
+ for (let bss in cfg.bss)
+ wdev_remove(bss.ifname);
+}
+
+function iface_gen_config(phy, config)
+{
+ let str = `data:
+${join("\n", config.radio.data)}
+channel=${config.radio.channel}
+`;
+
+ for (let i = 0; i < length(config.bss); i++) {
+ let bss = config.bss[i];
+ let type = i > 0 ? "bss" : "interface";
+
+ str += `
+${type}=${bss.ifname}
+${join("\n", bss.data)}
+`;
+ }
+
+ return str;
+}
+
+function iface_restart(phy, config, old_config)
+{
+ iface_remove(old_config);
+ iface_remove(config);
+
+ if (!config.bss || !config.bss[0]) {
+ hostapd.printf(`No bss for phy ${phy}`);
+ return;
+ }
+
+ let bss = config.bss[0];
+ let err = wdev_create(phy, bss.ifname, { mode: "ap" });
+ if (err)
+ hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
+ let config_inline = iface_gen_config(phy, config);
+
+ let ubus = hostapd.data.ubus;
+ ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
+ if (hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`) < 0)
+ hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
+ ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
+}
+
+function array_to_obj(arr, key, start)
+{
+ let obj = {};
+
+ start ??= 0;
+ for (let i = start; i < length(arr); i++) {
+ let cur = arr[i];
+ obj[cur[key]] = cur;
+ }
+
+ return obj;
+}
+
+function find_array_idx(arr, key, val)
+{
+ for (let i = 0; i < length(arr); i++)
+ if (arr[i][key] == val)
+ return i;
+
+ return -1;
+}
+
+function bss_reload_psk(bss, config, old_config)
+{
+ if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
+ return;
+
+ old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
+ if (!is_equal(old_config, config))
+ return;
+
+ let ret = bss.ctrl("RELOAD_WPA_PSK");
+ ret ??= "failed";
+
+ hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
+}
+
+function iface_reload_config(phy, config, old_config)
+{
+ if (!old_config || !is_equal(old_config.radio, config.radio))
+ return false;
+
+ if (is_equal(old_config.bss, config.bss))
+ return true;
+
+ if (!old_config.bss || !old_config.bss[0])
+ return false;
+
+ if (config.bss[0].ifname != old_config.bss[0].ifname)
+ return false;
+
+ let iface_name = config.bss[0].ifname;
+ let iface = hostapd.interfaces[iface_name];
+ if (!iface)
+ return false;
+
+ let first_bss = hostapd.bss[iface_name];
+ if (!first_bss)
+ return false;
+
+ let config_inline = iface_gen_config(phy, config);
+
+ bss_reload_psk(first_bss, config.bss[0], old_config.bss[0]);
+ if (!is_equal(config.bss[0], old_config.bss[0])) {
+ if (phy_is_fullmac(phy))
+ return false;
+
+ if (config.bss[0].bssid != old_config.bss[0].bssid)
+ return false;
+
+ hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
+ if (first_bss.set_config(config_inline, 0) < 0) {
+ hostapd.printf(`Failed to set config`);
+ return false;
+ }
+ }
+
+ let new_cfg = array_to_obj(config.bss, "ifname", 1);
+ let old_cfg = array_to_obj(old_config.bss, "ifname", 1);
+
+ for (let name in old_cfg) {
+ let bss = hostapd.bss[name];
+ if (!bss) {
+ hostapd.printf(`bss '${name}' not found`);
+ return false;
+ }
+
+ if (!new_cfg[name]) {
+ hostapd.printf(`Remove bss '${name}' on phy '${phy}'`);
+ bss.delete();
+ wdev_remove(name);
+ continue;
+ }
+
+ let new_cfg_data = new_cfg[name];
+ delete new_cfg[name];
+
+ if (is_equal(old_cfg[name], new_cfg_data))
+ continue;
+
+ hostapd.printf(`Reload config for bss '${name}' on phy '${phy}'`);
+ let idx = find_array_idx(config.bss, "ifname", name);
+ if (idx < 0) {
+ hostapd.printf(`bss index not found`);
+ return false;
+ }
+
+ if (bss.set_config(config_inline, idx) < 0) {
+ hostapd.printf(`Failed to set config`);
+ return false;
+ }
+ }
+
+ for (let name in new_cfg) {
+ hostapd.printf(`Add bss '${name}' on phy '${phy}'`);
+
+ let idx = find_array_idx(config.bss, "ifname", name);
+ if (idx < 0) {
+ hostapd.printf(`bss index not found`);
+ return false;
+ }
+
+ if (iface.add_bss(config_inline, idx) < 0) {
+ hostapd.printf(`Failed to add bss`);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function iface_set_config(phy, config)
+{
+ let old_config = hostapd.data.config[phy];
+
+ hostapd.data.config[phy] = config;
+
+ if (!config)
+ return iface_remove(old_config);
+
+ let ret = iface_reload_config(phy, config, old_config);
+ if (ret) {
+ hostapd.printf(`Reloaded settings for phy ${phy}`);
+ return 0;
+ }
+
+ hostapd.printf(`Restart interface for phy ${phy}`);
+ return iface_restart(phy, config, old_config);
+}
+
+function config_add_bss(config, name)
+{
+ let bss = {
+ ifname: name,
+ data: [],
+ hash: {}
+ };
+
+ push(config.bss, bss);
+
+ return bss;
+}
+
+function iface_load_config(filename)
+{
+ let f = open(filename, "r");
+ if (!f)
+ return null;
+
+ let config = {
+ radio: {
+ data: []
+ },
+ bss: [],
+ orig_file: filename,
+ };
+
+ let bss;
+ let line;
+ while ((line = trim(f.read("line"))) != null) {
+ let val = split(line, "=", 2);
+ if (!val[0])
+ continue;
+
+ if (val[0] == "interface") {
+ bss = config_add_bss(config, val[1]);
+ break;
+ }
+
+ if (val[0] == "channel") {
+ config.radio.channel = val[1];
+ continue;
+ }
+
+ push(config.radio.data, line);
+ }
+
+ while ((line = trim(f.read("line"))) != null) {
+ let val = split(line, "=", 2);
+ if (!val[0])
+ continue;
+
+ if (val[0] == "bssid")
+ bss.bssid = val[1];
+
+ if (val[0] == "bss") {
+ bss = config_add_bss(config, val[1]);
+ continue;
+ }
+
+ if (hostapd.data.file_fields[val[0]])
+ bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
+
+ push(bss.data, line);
+ }
+ f.close();
+
+ return config;
+}
+
+
+
+let main_obj = {
+ reload: {
+ args: {
+ phy: "",
+ },
+ call: function(req) {
+ try {
+ let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
+ for (let phy_name in phy_list) {
+ let phy = hostapd.data.config[phy_name];
+ let config = iface_load_config(phy.orig_file);
+ iface_set_config(phy_name, config);
+ }
+ } catch(e) {
+ hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
+ return libubus.STATUS_INVALID_ARGUMENT;
+ }
+
+ return 0;
+ }
+ },
+ apsta_state: {
+ args: {
+ phy: "",
+ up: true,
+ frequency: 0,
+ sec_chan_offset: 0,
+ csa: true,
+ csa_count: 0,
+ },
+ call: function(req) {
+ if (req.args.up == null || !req.args.phy)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ let phy = req.args.phy;
+ let config = hostapd.data.config[phy];
+ if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
+ return 0;
+
+ let iface = hostapd.interfaces[config.bss[0].ifname];
+ if (!iface)
+ return 0;
+
+ if (!req.args.up) {
+ iface.stop();
+ return 0;
+ }
+
+ let freq = req.args.frequency;
+ if (!freq)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ let sec_offset = req.args.sec_chan_offset;
+ if (sec_offset != -1 && sec_offset != 1)
+ sec_offset = 0;
+
+ let width = 0;
+ for (let line in config.radio.data) {
+ if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
+ sec_offset = null; // auto-detect
+ continue;
+ }
+
+ let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth)=(\d+)/);
+ if (!val)
+ continue;
+
+ val = int(val[2]);
+ if (val > width)
+ width = val;
+ }
+
+ if (freq < 4000)
+ width = 0;
+
+ let freq_info = hostapd.freq_info(freq, sec_offset, width);
+ if (!freq_info)
+ return libubus.STATUS_UNKNOWN_ERROR;
+
+ let ret;
+ if (req.args.csa) {
+ freq_info.csa_count = req.args.csa_count ?? 10;
+ ret = iface.switch_channel(freq_info);
+ } else {
+ iface.stop();
+ ret = iface.start(freq_info);
+ }
+ if (!ret)
+ return libubus.STATUS_UNKNOWN_ERROR;
+
+ return 0;
+ }
+ },
+ config_set: {
+ args: {
+ phy: "",
+ config: "",
+ prev_config: "",
+ },
+ call: function(req) {
+ let phy = req.args.phy;
+ let file = req.args.config;
+ let prev_file = req.args.prev_config;
+
+ if (!phy)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ try {
+ if (prev_file && !hostapd.data.config[phy]) {
+ let config = iface_load_config(prev_file);
+ if (config)
+ config.radio.data = [];
+ hostapd.data.config[phy] = config;
+ }
+
+ let config = iface_load_config(file);
+
+ hostapd.printf(`Set new config for phy ${phy}: ${file}`);
+ iface_set_config(phy, config);
+ } catch(e) {
+ hostapd.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
+ return libubus.STATUS_INVALID_ARGUMENT;
+ }
+
+ return {
+ pid: hostapd.getpid()
+ };
+ }
+ },
+ config_add: {
+ args: {
+ iface: "",
+ config: "",
+ },
+ call: function(req) {
+ if (!req.args.iface || !req.args.config)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ return {
+ pid: hostapd.getpid()
+ };
+ }
+ },
+ config_remove: {
+ args: {
+ iface: ""
+ },
+ call: function(req) {
+ if (!req.args.iface)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ hostapd.remove_iface(req.args.iface);
+ return 0;
+ }
+ },
+};
+
+hostapd.data.ubus = ubus;
+hostapd.data.obj = ubus.publish("hostapd", main_obj);
+
+function bss_event(type, name, data) {
+ let ubus = hostapd.data.ubus;
+
+ data ??= {};
+ data.name = name;
+ hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
+ ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
+}
+
+return {
+ shutdown: function() {
+ for (let phy in hostapd.data.config)
+ iface_set_config(phy, null);
+ hostapd.ubus.disconnect();
+ },
+ bss_add: function(name, obj) {
+ bss_event("add", name);
+ },
+ bss_reload: function(name, obj, reconf) {
+ bss_event("reload", name, { reconf: reconf != 0 });
+ },
+ bss_remove: function(name, obj) {
+ bss_event("remove", name);
+ }
+};
diff --git a/package/network/services/hostapd/files/radius.clients b/package/network/services/hostapd/files/radius.clients
new file mode 100644
index 000000000000..3175dcfd04af
--- /dev/null
+++ b/package/network/services/hostapd/files/radius.clients
@@ -0,0 +1 @@
+0.0.0.0/0 radius
diff --git a/package/network/services/hostapd/files/radius.config b/package/network/services/hostapd/files/radius.config
new file mode 100644
index 000000000000..ad8730748bb5
--- /dev/null
+++ b/package/network/services/hostapd/files/radius.config
@@ -0,0 +1,9 @@
+config radius
+ option disabled '1'
+ option ca_cert '/etc/radius/ca.pem'
+ option cert '/etc/radius/cert.pem'
+ option key '/etc/radius/key.pem'
+ option users '/etc/radius/users'
+ option clients '/etc/radius/clients'
+ option auth_port '1812'
+ option acct_port '1813'
diff --git a/package/network/services/hostapd/files/radius.init b/package/network/services/hostapd/files/radius.init
new file mode 100644
index 000000000000..4c562c247387
--- /dev/null
+++ b/package/network/services/hostapd/files/radius.init
@@ -0,0 +1,42 @@
+#!/bin/sh /etc/rc.common
+
+START=30
+
+USE_PROCD=1
+NAME=radius
+
+radius_start() {
+ local cfg="$1"
+
+ config_get_bool disabled "$cfg" disabled 0
+
+ [ "$disabled" -gt 0 ] && return
+
+ config_get ca "$cfg" ca_cert
+ config_get key "$cfg" key
+ config_get cert "$cfg" cert
+ config_get users "$cfg" users
+ config_get clients "$cfg" clients
+ config_get auth_port "$cfg" auth_port 1812
+ config_get acct_port "$cfg" acct_port 1813
+ config_get identity "$cfg" identity "$(cat /proc/sys/kernel/hostname)"
+
+ procd_open_instance $cfg
+ procd_set_param command /usr/sbin/hostapd-radius \
+ -C "$ca" \
+ -c "$cert" -k "$key" \
+ -s "$clients" -u "$users" \
+ -p "$auth_port" -P "$acct_port" \
+ -i "$identity"
+ procd_close_instance
+}
+
+start_service() {
+ config_load radius
+ config_foreach radius_start radius
+}
+
+service_triggers()
+{
+ procd_add_reload_trigger "radius"
+}
diff --git a/package/network/services/hostapd/files/radius.users b/package/network/services/hostapd/files/radius.users
new file mode 100644
index 000000000000..03e2fc8fae7d
--- /dev/null
+++ b/package/network/services/hostapd/files/radius.users
@@ -0,0 +1,14 @@
+{
+ "phase1": {
+ "wildcard": [
+ {
+ "name": "*",
+ "methods": [ "PEAP" ]
+ }
+ ]
+ },
+ "phase2": {
+ "users": {
+ }
+ }
+}
diff --git a/package/network/services/hostapd/files/wdev.uc b/package/network/services/hostapd/files/wdev.uc
new file mode 100644
index 000000000000..78037a9d2713
--- /dev/null
+++ b/package/network/services/hostapd/files/wdev.uc
@@ -0,0 +1,156 @@
+#!/usr/bin/env ucode
+'use strict';
+import { vlist_new, is_equal, wdev_create, wdev_remove } from "/usr/share/hostap/common.uc";
+import { readfile, writefile, basename, readlink, glob } from "fs";
+
+let keep_devices = {};
+let phy = shift(ARGV);
+let new_config = shift(ARGV);
+const mesh_params = [
+ "mesh_retry_timeout", "mesh_confirm_timeout", "mesh_holding_timeout", "mesh_max_peer_links",
+ "mesh_max_retries", "mesh_ttl", "mesh_element_ttl", "mesh_hwmp_max_preq_retries",
+ "mesh_path_refresh_time", "mesh_min_discovery_timeout", "mesh_hwmp_active_path_timeout",
+ "mesh_hwmp_preq_min_interval", "mesh_hwmp_net_diameter_traversal_time", "mesh_hwmp_rootmode",
+ "mesh_hwmp_rann_interval", "mesh_gate_announcements", "mesh_sync_offset_max_neighor",
+ "mesh_rssi_threshold", "mesh_hwmp_active_path_to_root_timeout", "mesh_hwmp_root_interval",
+ "mesh_hwmp_confirmation_interval", "mesh_awake_window", "mesh_plink_timeout",
+ "mesh_auto_open_plinks", "mesh_fwding", "mesh_power_mode"
+];
+
+function 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`)) {
+ system([ "ip", "link", "set", "dev", ifname, "down" ]);
+ wdev_remove(ifname);
+ }
+ wdev_create(phy, ifname, wdev);
+ system([ "ip", "link", "set", "dev", ifname, "up" ]);
+ 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);
+
+ cmd = ["iw", "dev", ifname, "set", "mesh_param" ];
+ let len = length(cmd);
+
+ for (let param in mesh_params)
+ if (wdev[param])
+ push(cmd, param, wdev[param]);
+
+ if (len == length(cmd))
+ return;
+
+ system(cmd);
+ }
+
+}
+
+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] = {};
+ }
+}
+
+
+let statefile = `/var/run/wdev-${phy}.json`;
+
+for (let dev in ARGV)
+ keep_devices[dev] = true;
+
+if (!phy || !new_config) {
+ warn(`Usage: ${basename(sourcepath())} <phy> <config> [<device]...]\n`);
+ exit(1);
+}
+
+if (!readfile(`/sys/class/ieee80211/${phy}/index`)) {
+ warn(`PHY ${phy} does not exist\n`);
+ exit(1);
+}
+
+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);
+
+add_ifname(new_config);
+config.update(new_config);
+
+drop_inactive(config.data);
+delete_ifname(config.data);
+writefile(statefile, sprintf("%J", config.data));
diff --git a/package/network/services/hostapd/files/wpa_supplicant-basic.config b/package/network/services/hostapd/files/wpa_supplicant-basic.config
index 6abd8e2331aa..944b4d928760 100644
--- a/package/network/services/hostapd/files/wpa_supplicant-basic.config
+++ b/package/network/services/hostapd/files/wpa_supplicant-basic.config
@@ -26,7 +26,7 @@
# replacement for WEXT and its use allows wpa_supplicant to properly control
# the driver to improve existing functionality like roaming and to support new
# functionality.
-CONFIG_DRIVER_WEXT=y
+#CONFIG_DRIVER_WEXT=y
# Driver interface for Linux drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y
diff --git a/package/network/services/hostapd/files/wpa_supplicant-full.config b/package/network/services/hostapd/files/wpa_supplicant-full.config
index d24fbbb01f17..b39dabca0696 100644
--- a/package/network/services/hostapd/files/wpa_supplicant-full.config
+++ b/package/network/services/hostapd/files/wpa_supplicant-full.config
@@ -26,7 +26,7 @@
# replacement for WEXT and its use allows wpa_supplicant to properly control
# the driver to improve existing functionality like roaming and to support new
# functionality.
-CONFIG_DRIVER_WEXT=y
+#CONFIG_DRIVER_WEXT=y
# Driver interface for Linux drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y
diff --git a/package/network/services/hostapd/files/wpa_supplicant-mini.config b/package/network/services/hostapd/files/wpa_supplicant-mini.config
index 9eb1111e523e..2a3f8fb69de3 100644
--- a/package/network/services/hostapd/files/wpa_supplicant-mini.config
+++ b/package/network/services/hostapd/files/wpa_supplicant-mini.config
@@ -26,7 +26,7 @@
# replacement for WEXT and its use allows wpa_supplicant to properly control
# the driver to improve existing functionality like roaming and to support new
# functionality.
-CONFIG_DRIVER_WEXT=y
+#CONFIG_DRIVER_WEXT=y
# Driver interface for Linux drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y
diff --git a/package/network/services/hostapd/files/wpa_supplicant-p2p.config b/package/network/services/hostapd/files/wpa_supplicant-p2p.config
index 0dcc88e6486d..7f5140622cc1 100644
--- a/package/network/services/hostapd/files/wpa_supplicant-p2p.config
+++ b/package/network/services/hostapd/files/wpa_supplicant-p2p.config
@@ -26,7 +26,7 @@
# replacement for WEXT and its use allows wpa_supplicant to properly control
# the driver to improve existing functionality like roaming and to support new
# functionality.
-CONFIG_DRIVER_WEXT=y
+#CONFIG_DRIVER_WEXT=y
# Driver interface for Linux drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y
diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc
new file mode 100644
index 000000000000..50da7f14ffe4
--- /dev/null
+++ b/package/network/services/hostapd/files/wpa_supplicant.uc
@@ -0,0 +1,254 @@
+let libubus = require("ubus");
+import { open, readfile } from "fs";
+import { wdev_create, wdev_remove, is_equal, vlist_new } from "common";
+
+let ubus = libubus.connect();
+
+wpas.data.config = {};
+wpas.data.iface_phy = {};
+
+function iface_stop(iface)
+{
+ let ifname = iface.config.iface;
+
+ if (!iface.running)
+ return;
+
+ delete wpas.data.iface_phy[ifname];
+ wpas.remove_iface(ifname);
+ wdev_remove(ifname);
+ iface.running = false;
+}
+
+function iface_start(phy, iface)
+{
+ if (iface.running)
+ return;
+
+ let ifname = iface.config.iface;
+
+ wpas.data.iface_phy[ifname] = phy;
+ wdev_remove(ifname);
+ let ret = wdev_create(phy, ifname, iface.config);
+ if (ret)
+ wpas.printf(`Failed to create device ${ifname}: ${ret}`);
+ wpas.add_iface(iface.config);
+ iface.running = true;
+}
+
+function iface_cb(new_if, old_if)
+{
+ if (old_if && new_if && is_equal(old_if.config, new_if.config)) {
+ new_if.running = old_if.running;
+ return;
+ }
+
+ if (old_if)
+ iface_stop(old_if);
+}
+
+function prepare_config(config)
+{
+ config.config_data = readfile(config.config);
+
+ return { config: config };
+}
+
+function set_config(phy_name, config_list)
+{
+ let phy = wpas.data.config[phy_name];
+
+ if (!phy) {
+ phy = vlist_new(iface_cb, false);
+ wpas.data.config[phy_name] = phy;
+ }
+
+ let values = [];
+ for (let config in config_list)
+ push(values, [ config.iface, prepare_config(config) ]);
+
+ phy.update(values);
+}
+
+function start_pending(phy_name)
+{
+ let phy = wpas.data.config[phy_name];
+
+ for (let ifname in phy.data)
+ iface_start(phy_name, phy.data[ifname]);
+}
+
+let main_obj = {
+ phy_set_state: {
+ args: {
+ phy: "",
+ stop: true,
+ },
+ call: function(req) {
+ if (!req.args.phy || req.args.stop == null)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ let phy = wpas.data.config[req.args.phy];
+ if (!phy)
+ return libubus.STATUS_NOT_FOUND;
+
+ try {
+ if (req.args.stop) {
+ for (let ifname in phy.data)
+ iface_stop(phy.data[ifname]);
+ } else {
+ start_pending(req.args.phy);
+ }
+ } catch (e) {
+ wpas.printf(`Error chaging state: ${e}\n${e.stacktrace[0].context}`);
+ return libubus.STATUS_INVALID_ARGUMENT;
+ }
+ return 0;
+ }
+ },
+ config_set: {
+ args: {
+ phy: "",
+ config: [],
+ defer: true,
+ },
+ call: function(req) {
+ if (!req.args.phy)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ try {
+ if (req.args.config)
+ set_config(req.args.phy, req.args.config);
+
+ if (!req.args.defer)
+ start_pending(req.args.phy);
+ } catch (e) {
+ wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
+ return libubus.STATUS_INVALID_ARGUMENT;
+ }
+
+ return {
+ pid: wpas.getpid()
+ };
+ }
+ },
+ config_add: {
+ args: {
+ driver: "",
+ iface: "",
+ bridge: "",
+ hostapd_ctrl: "",
+ ctrl: "",
+ config: "",
+ },
+ call: function(req) {
+ if (!req.args.iface || !req.args.config)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ if (wpas.add_iface(req.args) < 0)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ return {
+ pid: wpas.getpid()
+ };
+ }
+ },
+ config_remove: {
+ args: {
+ iface: ""
+ },
+ call: function(req) {
+ if (!req.args.iface)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ wpas.remove_iface(req.args.iface);
+ return 0;
+ }
+ },
+};
+
+wpas.data.ubus = ubus;
+wpas.data.obj = ubus.publish("wpa_supplicant", main_obj);
+
+function iface_event(type, name, data) {
+ let ubus = wpas.data.ubus;
+
+ data ??= {};
+ data.name = name;
+ wpas.data.obj.notify(`iface.${type}`, data, null, null, null, -1);
+ ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
+}
+
+function iface_hostapd_notify(phy, ifname, iface, state)
+{
+ let ubus = wpas.data.ubus;
+ let status = iface.status();
+ let msg = { phy: phy };
+
+ switch (state) {
+ case "DISCONNECTED":
+ case "AUTHENTICATING":
+ case "SCANNING":
+ msg.up = false;
+ break;
+ case "INTERFACE_DISABLED":
+ case "INACTIVE":
+ msg.up = true;
+ break;
+ case "COMPLETED":
+ msg.up = true;
+ msg.frequency = status.frequency;
+ msg.sec_chan_offset = status.sec_chan_offset;
+ break;
+ default:
+ return;
+ }
+
+ ubus.call("hostapd", "apsta_state", msg);
+}
+
+function iface_channel_switch(phy, ifname, iface, info)
+{
+ let msg = {
+ phy: phy,
+ up: true,
+ csa: true,
+ csa_count: info.csa_count ? info.csa_count - 1 : 0,
+ frequency: info.frequency,
+ sec_chan_offset: info.sec_chan_offset,
+ };
+ ubus.call("hostapd", "apsta_state", msg);
+}
+
+return {
+ shutdown: function() {
+ for (let phy in wpas.data.config)
+ set_config(phy, []);
+ wpas.ubus.disconnect();
+ },
+ iface_add: function(name, obj) {
+ iface_event("add", name);
+ },
+ iface_remove: function(name, obj) {
+ iface_event("remove", name);
+ },
+ state: function(ifname, iface, state) {
+ let phy = wpas.data.iface_phy[ifname];
+ if (!phy) {
+ wpas.printf(`no PHY for ifname ${ifname}`);
+ return;
+ }
+
+ iface_hostapd_notify(phy, ifname, iface, state);
+ },
+ event: function(ifname, iface, ev, info) {
+ let phy = wpas.data.iface_phy[ifname];
+ if (!phy) {
+ wpas.printf(`no PHY for ifname ${ifname}`);
+ return;
+ }
+
+ if (ev == "CH_SWITCH_STARTED")
+ iface_channel_switch(phy, ifname, iface, info);
+ }
+};
diff --git a/package/network/services/hostapd/patches/100-daemonize_fix.patch b/package/network/services/hostapd/patches/100-daemonize_fix.patch
deleted file mode 100644
index 687bd4082dd4..000000000000
--- a/package/network/services/hostapd/patches/100-daemonize_fix.patch
+++ /dev/null
@@ -1,97 +0,0 @@
---- a/src/utils/os_unix.c
-+++ b/src/utils/os_unix.c
-@@ -10,6 +10,7 @@
-
- #include <time.h>
- #include <sys/wait.h>
-+#include <fcntl.h>
-
- #ifdef ANDROID
- #include <sys/capability.h>
-@@ -188,59 +189,46 @@ int os_gmtime(os_time_t t, struct os_tm
- return 0;
- }
-
--
--#ifdef __APPLE__
--#include <fcntl.h>
--static int os_daemon(int nochdir, int noclose)
-+int os_daemonize(const char *pid_file)
- {
-- int devnull;
-+ int pid = 0, i, devnull;
-
-- if (chdir("/") < 0)
-- return -1;
-+#if defined(__uClinux__) || defined(__sun__)
-+ return -1;
-+#else /* defined(__uClinux__) || defined(__sun__) */
-
-- devnull = open("/dev/null", O_RDWR);
-- if (devnull < 0)
-+#ifndef __APPLE__
-+ pid = fork();
-+ if (pid < 0)
- return -1;
-+#endif
-
-- if (dup2(devnull, STDIN_FILENO) < 0) {
-- close(devnull);
-- return -1;
-+ if (pid > 0) {
-+ if (pid_file) {
-+ FILE *f = fopen(pid_file, "w");
-+ if (f) {
-+ fprintf(f, "%u\n", pid);
-+ fclose(f);
-+ }
-+ }
-+ _exit(0);
- }
-
-- if (dup2(devnull, STDOUT_FILENO) < 0) {
-- close(devnull);
-+ if (setsid() < 0)
- return -1;
-- }
-
-- if (dup2(devnull, STDERR_FILENO) < 0) {
-- close(devnull);
-+ if (chdir("/") < 0)
- return -1;
-- }
--
-- return 0;
--}
--#else /* __APPLE__ */
--#define os_daemon daemon
--#endif /* __APPLE__ */
-
--
--int os_daemonize(const char *pid_file)
--{
--#if defined(__uClinux__) || defined(__sun__)
-- return -1;
--#else /* defined(__uClinux__) || defined(__sun__) */
-- if (os_daemon(0, 0)) {
-- perror("daemon");
-+ devnull = open("/dev/null", O_RDWR);
-+ if (devnull < 0)
- return -1;
-- }
-
-- if (pid_file) {
-- FILE *f = fopen(pid_file, "w");
-- if (f) {
-- fprintf(f, "%u\n", getpid());
-- fclose(f);
-- }
-- }
-+ for (i = 0; i <= STDERR_FILENO; i++)
-+ dup2(devnull, i);
-+
-+ if (devnull > 2)
-+ close(devnull);
-
- return -0;
- #endif /* defined(__uClinux__) || defined(__sun__) */
diff --git a/package/network/services/hostapd/patches/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch b/package/network/services/hostapd/patches/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
new file mode 100644
index 000000000000..7b0435a45334
--- /dev/null
+++ b/package/network/services/hostapd/patches/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
@@ -0,0 +1,103 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 7 Aug 2023 21:55:57 +0200
+Subject: [PATCH] BSS coloring: fix CCA with multiple BSS
+
+Pass bss->ctx instead of drv->ctx in order to avoid multiple reports for
+the first bss. The first report would otherwise clear hapd->cca_color and
+subsequent reports would cause the iface bss color to be set to 0.
+In order to avoid any issues with cancellations, only overwrite the color
+based on hapd->cca_color if it was actually set.
+
+Fixes: 33c4dd26cd11 ("BSS coloring: Handle the collision and CCA events coming from the kernel")
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2260,7 +2260,8 @@ void wpa_supplicant_event(void *ctx, enu
+ case EVENT_CCA_NOTIFY:
+ wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+ hapd->conf->iface);
+- hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
++ if (hapd->cca_color)
++ hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+ hostapd_cleanup_cca_params(hapd);
+ break;
+ #endif /* CONFIG_IEEE80211AX */
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -3653,7 +3653,7 @@ static void nl80211_assoc_comeback(struc
+
+ #ifdef CONFIG_IEEE80211AX
+
+-static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
++static void nl80211_obss_color_collision(struct i802_bss *bss,
+ struct nlattr *tb[])
+ {
+ union wpa_event_data data;
+@@ -3667,37 +3667,37 @@ static void nl80211_obss_color_collision
+
+ wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
+ (long long unsigned int) data.bss_color_collision.bitmap);
+- wpa_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
++ wpa_supplicant_event(bss->ctx, EVENT_BSS_COLOR_COLLISION, &data);
+ }
+
+
+ static void
+-nl80211_color_change_announcement_started(struct wpa_driver_nl80211_data *drv)
++nl80211_color_change_announcement_started(struct i802_bss *bss)
+ {
+ union wpa_event_data data = {};
+
+ wpa_printf(MSG_DEBUG, "nl80211: CCA started");
+- wpa_supplicant_event(drv->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
++ wpa_supplicant_event(bss->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
+ }
+
+
+ static void
+-nl80211_color_change_announcement_aborted(struct wpa_driver_nl80211_data *drv)
++nl80211_color_change_announcement_aborted(struct i802_bss *bss)
+ {
+ union wpa_event_data data = {};
+
+ wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
+- wpa_supplicant_event(drv->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
++ wpa_supplicant_event(bss->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
+ }
+
+
+ static void
+-nl80211_color_change_announcement_completed(struct wpa_driver_nl80211_data *drv)
++nl80211_color_change_announcement_completed(struct i802_bss *bss)
+ {
+ union wpa_event_data data = {};
+
+ wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
+- wpa_supplicant_event(drv->ctx, EVENT_CCA_NOTIFY, &data);
++ wpa_supplicant_event(bss->ctx, EVENT_CCA_NOTIFY, &data);
+ }
+
+ #endif /* CONFIG_IEEE80211AX */
+@@ -3957,16 +3957,16 @@ static void do_process_drv_event(struct
+ break;
+ #ifdef CONFIG_IEEE80211AX
+ case NL80211_CMD_OBSS_COLOR_COLLISION:
+- nl80211_obss_color_collision(drv, tb);
++ nl80211_obss_color_collision(bss, tb);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_STARTED:
+- nl80211_color_change_announcement_started(drv);
++ nl80211_color_change_announcement_started(bss);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_ABORTED:
+- nl80211_color_change_announcement_aborted(drv);
++ nl80211_color_change_announcement_aborted(bss);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_COMPLETED:
+- nl80211_color_change_announcement_completed(drv);
++ nl80211_color_change_announcement_completed(bss);
+ break;
+ #endif /* CONFIG_IEEE80211AX */
+ default:
diff --git a/package/network/services/hostapd/patches/200-multicall.patch b/package/network/services/hostapd/patches/200-multicall.patch
index f7e797a9c800..8ebbed0c32c8 100644
--- a/package/network/services/hostapd/patches/200-multicall.patch
+++ b/package/network/services/hostapd/patches/200-multicall.patch
@@ -189,7 +189,7 @@
{
struct hostapd_data *hapd = ctx;
#ifndef CONFIG_NO_STDOUT_DEBUG
-@@ -2271,7 +2271,7 @@ void wpa_supplicant_event(void *ctx, enu
+@@ -2272,7 +2272,7 @@ void wpa_supplicant_event(void *ctx, enu
}
diff --git a/package/network/services/hostapd/patches/340-reload_freq_change.patch b/package/network/services/hostapd/patches/340-reload_freq_change.patch
deleted file mode 100644
index ae6cd81ea4d8..000000000000
--- a/package/network/services/hostapd/patches/340-reload_freq_change.patch
+++ /dev/null
@@ -1,80 +0,0 @@
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -143,6 +143,29 @@ static void hostapd_reload_bss(struct ho
- #endif /* CONFIG_NO_RADIUS */
-
- ssid = &hapd->conf->ssid;
-+
-+ hostapd_set_freq(hapd, hapd->iconf->hw_mode, hapd->iface->freq,
-+ hapd->iconf->channel,
-+ hapd->iconf->enable_edmg,
-+ hapd->iconf->edmg_channel,
-+ hapd->iconf->ieee80211n,
-+ hapd->iconf->ieee80211ac,
-+ hapd->iconf->ieee80211ax,
-+ hapd->iconf->ieee80211be,
-+ hapd->iconf->secondary_channel,
-+ hostapd_get_oper_chwidth(hapd->iconf),
-+ hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf),
-+ hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf));
-+
-+ if (hapd->iface->current_mode) {
-+ if (hostapd_prepare_rates(hapd->iface, hapd->iface->current_mode)) {
-+ wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
-+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-+ HOSTAPD_LEVEL_WARNING,
-+ "Failed to prepare rates table.");
-+ }
-+ }
-+
- if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
- ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
- /*
-@@ -251,6 +274,7 @@ int hostapd_reload_config(struct hostapd
- struct hostapd_data *hapd = iface->bss[0];
- struct hostapd_config *newconf, *oldconf;
- size_t j;
-+ int i;
-
- if (iface->config_fname == NULL) {
- /* Only in-memory config in use - assume it has been updated */
-@@ -301,6 +325,17 @@ int hostapd_reload_config(struct hostapd
- }
- iface->conf = newconf;
-
-+ for (i = 0; i < iface->num_hw_features; i++) {
-+ struct hostapd_hw_modes *mode = &iface->hw_features[i];
-+ if (mode->mode == iface->conf->hw_mode) {
-+ iface->current_mode = mode;
-+ break;
-+ }
-+ }
-+
-+ if (iface->conf->channel)
-+ iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
-+
- for (j = 0; j < iface->num_bss; j++) {
- hapd = iface->bss[j];
- if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
-@@ -308,21 +343,6 @@ int hostapd_reload_config(struct hostapd
- newconf->bss[j]->config_id) != 0)
- hostapd_clear_old_bss(hapd);
- hapd->iconf = newconf;
-- hapd->iconf->channel = oldconf->channel;
-- hapd->iconf->acs = oldconf->acs;
-- hapd->iconf->secondary_channel = oldconf->secondary_channel;
-- hapd->iconf->ieee80211n = oldconf->ieee80211n;
-- hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
-- hapd->iconf->ht_capab = oldconf->ht_capab;
-- hapd->iconf->vht_capab = oldconf->vht_capab;
-- hostapd_set_oper_chwidth(hapd->iconf,
-- hostapd_get_oper_chwidth(oldconf));
-- hostapd_set_oper_centr_freq_seg0_idx(
-- hapd->iconf,
-- hostapd_get_oper_centr_freq_seg0_idx(oldconf));
-- hostapd_set_oper_centr_freq_seg1_idx(
-- hapd->iconf,
-- hostapd_get_oper_centr_freq_seg1_idx(oldconf));
- hapd->conf = newconf->bss[j];
- hostapd_reload_bss(hapd);
- }
diff --git a/package/network/services/hostapd/patches/360-ctrl_iface_reload.patch b/package/network/services/hostapd/patches/360-ctrl_iface_reload.patch
deleted file mode 100644
index 4d85ea11f906..000000000000
--- a/package/network/services/hostapd/patches/360-ctrl_iface_reload.patch
+++ /dev/null
@@ -1,106 +0,0 @@
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -68,6 +68,7 @@
- #include "fst/fst_ctrl_iface.h"
- #include "config_file.h"
- #include "ctrl_iface.h"
-+#include "config_file.h"
-
-
- #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
-@@ -83,6 +84,7 @@ static void hostapd_ctrl_iface_send(stru
- enum wpa_msg_type type,
- const char *buf, size_t len);
-
-+static char *reload_opts = NULL;
-
- static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
- struct sockaddr_storage *from,
-@@ -134,6 +136,61 @@ static int hostapd_ctrl_iface_new_sta(st
- return 0;
- }
-
-+static char *get_option(char *opt, char *str)
-+{
-+ int len = strlen(str);
-+
-+ if (!strncmp(opt, str, len))
-+ return opt + len;
-+ else
-+ return NULL;
-+}
-+
-+static struct hostapd_config *hostapd_ctrl_iface_config_read(const char *fname)
-+{
-+ struct hostapd_config *conf;
-+ char *opt, *val;
-+
-+ conf = hostapd_config_read(fname);
-+ if (!conf)
-+ return NULL;
-+
-+ for (opt = strtok(reload_opts, " ");
-+ opt;
-+ opt = strtok(NULL, " ")) {
-+
-+ if ((val = get_option(opt, "channel=")))
-+ conf->channel = atoi(val);
-+ else if ((val = get_option(opt, "ht_capab=")))
-+ conf->ht_capab = atoi(val);
-+ else if ((val = get_option(opt, "ht_capab_mask=")))
-+ conf->ht_capab &= atoi(val);
-+ else if ((val = get_option(opt, "sec_chan=")))
-+ conf->secondary_channel = atoi(val);
-+ else if ((val = get_option(opt, "hw_mode=")))
-+ conf->hw_mode = atoi(val);
-+ else if ((val = get_option(opt, "ieee80211n=")))
-+ conf->ieee80211n = atoi(val);
-+ else
-+ break;
-+ }
-+
-+ return conf;
-+}
-+
-+static int hostapd_ctrl_iface_update(struct hostapd_data *hapd, char *txt)
-+{
-+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
-+ struct hostapd_iface *iface = hapd->iface;
-+
-+ config_read_cb = iface->interfaces->config_read_cb;
-+ iface->interfaces->config_read_cb = hostapd_ctrl_iface_config_read;
-+ reload_opts = txt;
-+
-+ hostapd_reload_config(iface);
-+
-+ iface->interfaces->config_read_cb = config_read_cb;
-+}
-
- #ifdef NEED_AP_MLME
- static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
-@@ -3564,6 +3621,8 @@ static int hostapd_ctrl_iface_receive_pr
- } else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
- reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
- reply_size);
-+ } else if (os_strncmp(buf, "UPDATE ", 7) == 0) {
-+ hostapd_ctrl_iface_update(hapd, buf + 7);
- } else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
- ieee802_1x_erp_flush(hapd);
- #ifdef RADIUS_SERVER
---- a/src/ap/ctrl_iface_ap.c
-+++ b/src/ap/ctrl_iface_ap.c
-@@ -1023,7 +1023,13 @@ int hostapd_parse_csa_settings(const cha
-
- int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
- {
-- return hostapd_drv_stop_ap(hapd);
-+ struct hostapd_iface *iface = hapd->iface;
-+ int i;
-+
-+ for (i = 0; i < iface->num_bss; i++)
-+ hostapd_drv_stop_ap(iface->bss[i]);
-+
-+ return 0;
- }
-
-
diff --git a/package/network/services/hostapd/patches/370-ap_sta_support.patch b/package/network/services/hostapd/patches/370-ap_sta_support.patch
deleted file mode 100644
index 3baad2a52e50..000000000000
--- a/package/network/services/hostapd/patches/370-ap_sta_support.patch
+++ /dev/null
@@ -1,392 +0,0 @@
---- a/wpa_supplicant/Makefile
-+++ b/wpa_supplicant/Makefile
-@@ -126,6 +126,8 @@ OBJS_c += ../src/utils/common.o
- OBJS_c += ../src/common/cli.o
- OBJS += wmm_ac.o
-
-+OBJS += ../src/common/wpa_ctrl.o
-+
- ifndef CONFIG_OS
- ifdef CONFIG_NATIVE_WINDOWS
- CONFIG_OS=win32
---- a/wpa_supplicant/bss.c
-+++ b/wpa_supplicant/bss.c
-@@ -11,6 +11,7 @@
- #include "utils/common.h"
- #include "utils/eloop.h"
- #include "common/ieee802_11_defs.h"
-+#include "common/ieee802_11_common.h"
- #include "drivers/driver.h"
- #include "eap_peer/eap.h"
- #include "wpa_supplicant_i.h"
-@@ -283,6 +284,10 @@ void calculate_update_time(const struct
- static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
- struct os_reltime *fetch_time)
- {
-+ struct ieee80211_ht_capabilities *capab;
-+ struct ieee80211_ht_operation *oper;
-+ struct ieee802_11_elems elems;
-+
- dst->flags = src->flags;
- os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
- dst->freq = src->freq;
-@@ -296,6 +301,15 @@ static void wpa_bss_copy_res(struct wpa_
- dst->est_throughput = src->est_throughput;
- dst->snr = src->snr;
-
-+ memset(&elems, 0, sizeof(elems));
-+ ieee802_11_parse_elems((u8 *) (src + 1), src->ie_len, &elems, 0);
-+ capab = (struct ieee80211_ht_capabilities *) elems.ht_capabilities;
-+ oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-+ if (capab)
-+ dst->ht_capab = le_to_host16(capab->ht_capabilities_info);
-+ if (oper)
-+ dst->ht_param = oper->ht_param;
-+
- calculate_update_time(fetch_time, src->age, &dst->last_update);
- }
-
---- a/wpa_supplicant/bss.h
-+++ b/wpa_supplicant/bss.h
-@@ -94,6 +94,10 @@ struct wpa_bss {
- u8 ssid[SSID_MAX_LEN];
- /** Length of SSID */
- size_t ssid_len;
-+ /** HT capabilities */
-+ u16 ht_capab;
-+ /* Five octets of HT Operation Information */
-+ u8 ht_param;
- /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
- int freq;
- /** Beacon interval in TUs (host byte order) */
---- a/wpa_supplicant/main.c
-+++ b/wpa_supplicant/main.c
-@@ -35,7 +35,7 @@ static void usage(void)
- "vW] [-P<pid file>] "
- "[-g<global ctrl>] \\\n"
- " [-G<group>] \\\n"
-- " -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
-+ " -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-H<hostapd path>] "
- "[-p<driver_param>] \\\n"
- " [-b<br_ifname>] [-e<entropy file>]"
- #ifdef CONFIG_DEBUG_FILE
-@@ -75,6 +75,7 @@ static void usage(void)
- " -g = global ctrl_interface\n"
- " -G = global ctrl_interface group\n"
- " -h = show this help text\n"
-+ " -H = connect to a hostapd instance to manage state changes\n"
- " -i = interface name\n"
- " -I = additional configuration file\n"
- " -K = include keys (passwords, etc.) in debug output\n"
-@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
-
- for (;;) {
- c = getopt(argc, argv,
-- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
-+ "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:No:O:p:P:qsTtuvW");
- if (c < 0)
- break;
- switch (c) {
-@@ -249,6 +250,9 @@ int main(int argc, char *argv[])
- usage();
- exitcode = 0;
- goto out;
-+ case 'H':
-+ iface->hostapd_ctrl = optarg;
-+ break;
- case 'i':
- iface->ifname = optarg;
- break;
---- a/wpa_supplicant/wpa_supplicant.c
-+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -131,6 +131,54 @@ static void wpas_update_fils_connect_par
- static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s);
- #endif /* CONFIG_OWE */
-
-+static int hostapd_stop(struct wpa_supplicant *wpa_s)
-+{
-+ const char *cmd = "STOP_AP";
-+ char buf[256];
-+ size_t len = sizeof(buf);
-+
-+ if (wpa_ctrl_request(wpa_s->hostapd, cmd, os_strlen(cmd), buf, &len, NULL) < 0) {
-+ wpa_printf(MSG_ERROR, "\nFailed to stop hostapd AP interfaces\n");
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+static int hostapd_reload(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
-+{
-+ char *cmd = NULL;
-+ char buf[256];
-+ size_t len = sizeof(buf);
-+ enum hostapd_hw_mode hw_mode;
-+ u8 channel;
-+ int sec_chan = 0;
-+ int ret;
-+
-+ if (!bss)
-+ return -1;
-+
-+ if (bss->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
-+ int sec = bss->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-+ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-+ sec_chan = 1;
-+ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-+ sec_chan = -1;
-+ }
-+
-+ hw_mode = ieee80211_freq_to_chan(bss->freq, &channel);
-+ if (asprintf(&cmd, "UPDATE channel=%d sec_chan=%d hw_mode=%d",
-+ channel, sec_chan, hw_mode) < 0)
-+ return -1;
-+
-+ ret = wpa_ctrl_request(wpa_s->hostapd, cmd, os_strlen(cmd), buf, &len, NULL);
-+ free(cmd);
-+
-+ if (ret < 0) {
-+ wpa_printf(MSG_ERROR, "\nFailed to reload hostapd AP interfaces\n");
-+ return -1;
-+ }
-+ return 0;
-+}
-
- #ifdef CONFIG_WEP
- /* Configure default/group WEP keys for static WEP */
-@@ -1026,6 +1074,8 @@ void wpa_supplicant_set_state(struct wpa
-
- sme_sched_obss_scan(wpa_s, 1);
-
-+ if (wpa_s->hostapd)
-+ hostapd_reload(wpa_s, wpa_s->current_bss);
- #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
- if (!fils_hlp_sent && ssid && ssid->eap.erp)
- update_fils_connect_params = true;
-@@ -1036,6 +1086,8 @@ void wpa_supplicant_set_state(struct wpa
- #endif /* CONFIG_OWE */
- } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
- state == WPA_ASSOCIATED) {
-+ if (wpa_s->hostapd)
-+ hostapd_stop(wpa_s);
- wpa_s->new_connection = 1;
- wpa_drv_set_operstate(wpa_s, 0);
- #ifndef IEEE8021X_EAPOL
-@@ -2537,6 +2589,8 @@ void wpa_supplicant_associate(struct wpa
- return;
- }
- wpa_s->current_bss = bss;
-+ if (wpa_s->hostapd)
-+ hostapd_reload(wpa_s, wpa_s->current_bss);
- #else /* CONFIG_MESH */
- wpa_msg(wpa_s, MSG_ERROR,
- "mesh mode support not included in the build");
-@@ -7037,6 +7091,16 @@ static int wpa_supplicant_init_iface(str
- sizeof(wpa_s->bridge_ifname));
- }
-
-+ if (iface->hostapd_ctrl) {
-+ wpa_s->hostapd = wpa_ctrl_open(iface->hostapd_ctrl);
-+ if (!wpa_s->hostapd) {
-+ wpa_printf(MSG_ERROR, "\nFailed to connect to hostapd\n");
-+ return -1;
-+ }
-+ if (hostapd_stop(wpa_s) < 0)
-+ return -1;
-+ }
-+
- /* RSNA Supplicant Key Management - INITIALIZE */
- eapol_sm_notify_portEnabled(wpa_s->eapol, false);
- eapol_sm_notify_portValid(wpa_s->eapol, false);
-@@ -7379,6 +7443,11 @@ static void wpa_supplicant_deinit_iface(
- if (terminate)
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
-
-+ if (wpa_s->hostapd) {
-+ wpa_ctrl_close(wpa_s->hostapd);
-+ wpa_s->hostapd = NULL;
-+ }
-+
- wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
- wpa_s->ctrl_iface = NULL;
-
---- a/wpa_supplicant/wpa_supplicant_i.h
-+++ b/wpa_supplicant/wpa_supplicant_i.h
-@@ -106,6 +106,11 @@ struct wpa_interface {
- const char *ifname;
-
- /**
-+ * hostapd_ctrl - path to hostapd control socket for notification
-+ */
-+ const char *hostapd_ctrl;
-+
-+ /**
- * bridge_ifname - Optional bridge interface name
- *
- * If the driver interface (ifname) is included in a Linux bridge
-@@ -665,6 +670,8 @@ struct wpa_supplicant {
- #endif /* CONFIG_CTRL_IFACE_BINDER */
- char bridge_ifname[16];
-
-+ struct wpa_ctrl *hostapd;
-+
- char *confname;
- char *confanother;
-
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -2751,6 +2751,12 @@ static int hostapd_ctrl_iface_chan_switc
- return 0;
- }
-
-+ if (os_strstr(pos, " auto-ht")) {
-+ settings.freq_params.ht_enabled = iface->conf->ieee80211n;
-+ settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
-+ settings.freq_params.he_enabled = iface->conf->ieee80211ax;
-+ }
-+
- for (i = 0; i < iface->num_bss; i++) {
-
- /* Save CHAN_SWITCH VHT, HE, and EHT config */
---- a/src/ap/beacon.c
-+++ b/src/ap/beacon.c
-@@ -2108,11 +2108,6 @@ static int __ieee802_11_set_beacon(struc
- return -1;
- }
-
-- if (hapd->csa_in_progress) {
-- wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
-- return -1;
-- }
--
- hapd->beacon_set_done = 1;
-
- if (ieee802_11_build_ap_params(hapd, ¶ms) < 0)
---- a/wpa_supplicant/events.c
-+++ b/wpa_supplicant/events.c
-@@ -5345,6 +5345,60 @@ static void wpas_link_reconfig(struct wp
- }
-
-
-+static void
-+supplicant_ch_switch_started(struct wpa_supplicant *wpa_s,
-+ union wpa_event_data *data)
-+{
-+ char buf[256];
-+ size_t len = sizeof(buf);
-+ char *cmd = NULL;
-+ int width = 20;
-+ int ret;
-+
-+ if (!wpa_s->hostapd)
-+ return;
-+
-+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CHANNEL_SWITCH
-+ "count=%d freq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
-+ data->ch_switch.count,
-+ data->ch_switch.freq,
-+ data->ch_switch.ht_enabled,
-+ data->ch_switch.ch_offset,
-+ channel_width_to_string(data->ch_switch.ch_width),
-+ data->ch_switch.cf1,
-+ data->ch_switch.cf2);
-+
-+ switch (data->ch_switch.ch_width) {
-+ case CHAN_WIDTH_20_NOHT:
-+ case CHAN_WIDTH_20:
-+ width = 20;
-+ break;
-+ case CHAN_WIDTH_40:
-+ width = 40;
-+ break;
-+ case CHAN_WIDTH_80:
-+ width = 80;
-+ break;
-+ case CHAN_WIDTH_160:
-+ case CHAN_WIDTH_80P80:
-+ width = 160;
-+ break;
-+ }
-+
-+ asprintf(&cmd, "CHAN_SWITCH %d %d sec_channel_offset=%d center_freq1=%d center_freq2=%d, bandwidth=%d auto-ht\n",
-+ data->ch_switch.count - 1,
-+ data->ch_switch.freq,
-+ data->ch_switch.ch_offset,
-+ data->ch_switch.cf1,
-+ data->ch_switch.cf2,
-+ width);
-+ ret = wpa_ctrl_request(wpa_s->hostapd, cmd, os_strlen(cmd), buf, &len, NULL);
-+ free(cmd);
-+
-+ if (ret < 0)
-+ wpa_printf(MSG_ERROR, "\nFailed to reload hostapd AP interfaces\n");
-+}
-+
- void supplicant_event(void *ctx, enum wpa_event_type event,
- union wpa_event_data *data)
- {
-@@ -5697,8 +5751,10 @@ void supplicant_event(void *ctx, enum wp
- channel_width_to_string(data->ch_switch.ch_width),
- data->ch_switch.cf1,
- data->ch_switch.cf2);
-- if (event == EVENT_CH_SWITCH_STARTED)
-+ if (event == EVENT_CH_SWITCH_STARTED) {
-+ supplicant_ch_switch_started(wpa_s, data);
- break;
-+ }
-
- wpa_s->assoc_freq = data->ch_switch.freq;
- wpa_s->current_ssid->frequency = data->ch_switch.freq;
---- a/src/drivers/driver.h
-+++ b/src/drivers/driver.h
-@@ -6421,6 +6421,7 @@ union wpa_event_data {
-
- /**
- * struct ch_switch
-+ * @count: Count until channel switch activates
- * @freq: Frequency of new channel in MHz
- * @ht_enabled: Whether this is an HT channel
- * @ch_offset: Secondary channel offset
-@@ -6431,6 +6432,7 @@ union wpa_event_data {
- * @punct_bitmap: Puncturing bitmap
- */
- struct ch_switch {
-+ int count;
- int freq;
- int ht_enabled;
- int ch_offset;
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -1202,6 +1202,7 @@ static void mlme_event_ch_switch(struct
- struct nlattr *bw, struct nlattr *cf1,
- struct nlattr *cf2,
- struct nlattr *punct_bitmap,
-+ struct nlattr *count,
- int finished)
- {
- struct i802_bss *bss;
-@@ -1265,6 +1266,8 @@ static void mlme_event_ch_switch(struct
- data.ch_switch.cf1 = nla_get_u32(cf1);
- if (cf2)
- data.ch_switch.cf2 = nla_get_u32(cf2);
-+ if (count)
-+ data.ch_switch.count = nla_get_u32(count);
-
- if (finished)
- bss->flink->freq = data.ch_switch.freq;
-@@ -3848,6 +3851,7 @@ static void do_process_drv_event(struct
- tb[NL80211_ATTR_CENTER_FREQ1],
- tb[NL80211_ATTR_CENTER_FREQ2],
- tb[NL80211_ATTR_PUNCT_BITMAP],
-+ tb[NL80211_ATTR_CH_SWITCH_COUNT],
- 0);
- break;
- case NL80211_CMD_CH_SWITCH_NOTIFY:
-@@ -3860,6 +3864,7 @@ static void do_process_drv_event(struct
- tb[NL80211_ATTR_CENTER_FREQ1],
- tb[NL80211_ATTR_CENTER_FREQ2],
- tb[NL80211_ATTR_PUNCT_BITMAP],
-+ NULL,
- 1);
- break;
- case NL80211_CMD_DISCONNECT:
diff --git a/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch b/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch
index 456599db0921..54a736fe915d 100644
--- a/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch
+++ b/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch
@@ -12,7 +12,7 @@
else
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
-@@ -3377,6 +3377,7 @@ static int hostapd_ctrl_iface_receive_pr
+@@ -3314,6 +3314,7 @@ static int hostapd_ctrl_iface_receive_pr
reply_size);
} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
reply_len = hostapd_drv_status(hapd, reply, reply_size);
@@ -20,7 +20,7 @@
} else if (os_strcmp(buf, "MIB") == 0) {
reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
if (reply_len >= 0) {
-@@ -3418,6 +3419,7 @@ static int hostapd_ctrl_iface_receive_pr
+@@ -3355,6 +3356,7 @@ static int hostapd_ctrl_iface_receive_pr
} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
reply_size);
@@ -30,7 +30,7 @@
reply_len = -1;
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
-@@ -985,6 +985,9 @@ ifdef CONFIG_FILS
+@@ -983,6 +983,9 @@ ifdef CONFIG_FILS
OBJS += ../src/ap/fils_hlp.o
endif
ifdef CONFIG_CTRL_IFACE
diff --git a/package/network/services/hostapd/patches/420-indicate-features.patch b/package/network/services/hostapd/patches/420-indicate-features.patch
index 786b83d3151d..3b28b6e75247 100644
--- a/package/network/services/hostapd/patches/420-indicate-features.patch
+++ b/package/network/services/hostapd/patches/420-indicate-features.patch
@@ -37,16 +37,16 @@
#include "crypto/crypto.h"
#include "fst/fst.h"
#include "wpa_supplicant_i.h"
-@@ -203,7 +204,7 @@ int main(int argc, char *argv[])
+@@ -202,7 +203,7 @@ int main(int argc, char *argv[])
for (;;) {
c = getopt(argc, argv,
-- "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:No:O:p:P:qsTtuvW");
-+ "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:No:O:p:P:qsTtuv::W");
+- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
++ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuv::W");
if (c < 0)
break;
switch (c) {
-@@ -306,8 +307,12 @@ int main(int argc, char *argv[])
+@@ -302,8 +303,12 @@ int main(int argc, char *argv[])
break;
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
case 'v':
diff --git a/package/network/services/hostapd/patches/432-missing-typedef.patch b/package/network/services/hostapd/patches/432-missing-typedef.patch
deleted file mode 100644
index 7a100f1a0d25..000000000000
--- a/package/network/services/hostapd/patches/432-missing-typedef.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- a/src/drivers/linux_wext.h
-+++ b/src/drivers/linux_wext.h
-@@ -26,6 +26,7 @@ typedef int32_t __s32;
- typedef uint16_t __u16;
- typedef int16_t __s16;
- typedef uint8_t __u8;
-+typedef int8_t __s8;
- #ifndef __user
- #define __user
- #endif /* __user */
diff --git a/package/network/services/hostapd/patches/450-scan_wait.patch b/package/network/services/hostapd/patches/450-scan_wait.patch
deleted file mode 100644
index 45886896ee9b..000000000000
--- a/package/network/services/hostapd/patches/450-scan_wait.patch
+++ /dev/null
@@ -1,73 +0,0 @@
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -39,6 +39,8 @@ struct hapd_global {
- };
-
- static struct hapd_global global;
-+static int daemonize = 0;
-+static char *pid_file = NULL;
-
-
- #ifndef CONFIG_NO_HOSTAPD_LOGGER
-@@ -146,6 +148,14 @@ static void hostapd_logger_cb(void *ctx,
- }
- #endif /* CONFIG_NO_HOSTAPD_LOGGER */
-
-+static void hostapd_setup_complete_cb(void *ctx)
-+{
-+ if (daemonize && os_daemonize(pid_file)) {
-+ perror("daemon");
-+ return;
-+ }
-+ daemonize = 0;
-+}
-
- /**
- * hostapd_driver_init - Preparate driver interface
-@@ -217,6 +227,8 @@ static int hostapd_driver_init(struct ho
- }
- #endif /* CONFIG_IEEE80211BE */
-
-+ hapd->setup_complete_cb = hostapd_setup_complete_cb;
-+
- /* Initialize the driver interface */
- if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
- b = NULL;
-@@ -497,8 +509,6 @@ static void hostapd_global_deinit(const
- #endif /* CONFIG_NATIVE_WINDOWS */
-
- eap_server_unregister_methods();
--
-- os_daemonize_terminate(pid_file);
- }
-
-
-@@ -524,18 +534,6 @@ static int hostapd_global_run(struct hap
- }
- #endif /* EAP_SERVER_TNC */
-
-- if (daemonize) {
-- if (os_daemonize(pid_file)) {
-- wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
-- return -1;
-- }
-- if (eloop_sock_requeue()) {
-- wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s",
-- strerror(errno));
-- return -1;
-- }
-- }
--
- eloop_run();
-
- return 0;
-@@ -739,8 +737,7 @@ int main(int argc, char *argv[])
- struct hapd_interfaces interfaces;
- int ret = 1;
- size_t i, j;
-- int c, debug = 0, daemonize = 0;
-- char *pid_file = NULL;
-+ int c, debug = 0;
- const char *log_file = NULL;
- const char *entropy_file = NULL;
- char **bss_config = NULL, **tmp_bss;
diff --git a/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch b/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch
index 4c728681393c..e50c609d9765 100644
--- a/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch
+++ b/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch
@@ -174,7 +174,7 @@ Signed-hostap: Antonio Quartulli <ordex at autistici.org>
* macsec_policy - Determines the policy for MACsec secure session
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -4203,6 +4203,12 @@ static void wpas_start_assoc_cb(struct w
+@@ -4149,6 +4149,12 @@ static void wpas_start_assoc_cb(struct w
params.beacon_int = ssid->beacon_int;
else
params.beacon_int = wpa_s->conf->beacon_int;
diff --git a/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch b/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch
index c7e8cf25ce8f..4d7d85f4ab8e 100644
--- a/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch
+++ b/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch
@@ -1,6 +1,6 @@
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -3094,6 +3094,10 @@ void ibss_mesh_setup_freq(struct wpa_sup
+@@ -3040,6 +3040,10 @@ void ibss_mesh_setup_freq(struct wpa_sup
freq->freq = ssid->frequency;
diff --git a/package/network/services/hostapd/patches/500-lto-jobserver-support.patch b/package/network/services/hostapd/patches/500-lto-jobserver-support.patch
index 046da42ab8b9..67312c5004a7 100644
--- a/package/network/services/hostapd/patches/500-lto-jobserver-support.patch
+++ b/package/network/services/hostapd/patches/500-lto-jobserver-support.patch
@@ -20,7 +20,7 @@
NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
-@@ -2039,31 +2039,31 @@ wpa_supplicant_multi.a: .config $(BCHECK
+@@ -2037,31 +2037,31 @@ wpa_supplicant_multi.a: .config $(BCHECK
@$(AR) cr $@ wpa_supplicant_multi.o $(OBJS)
wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch
index f3936342a5f1..5b2745a3beec 100644
--- a/package/network/services/hostapd/patches/600-ubus_support.patch
+++ b/package/network/services/hostapd/patches/600-ubus_support.patch
@@ -1,11 +1,12 @@
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
-@@ -166,6 +166,11 @@ OBJS += ../src/common/hw_features_common
+@@ -166,6 +166,12 @@ OBJS += ../src/common/hw_features_common
OBJS += ../src/eapol_auth/eapol_auth_sm.o
+ifdef CONFIG_UBUS
+CFLAGS += -DUBUS_SUPPORT
++OBJS += ../src/utils/uloop.o
+OBJS += ../src/ap/ubus.o
+LIBS += -lubox -lubus
+endif
@@ -22,15 +23,6 @@
#define OCE_STA_CFON_ENABLED(hapd) \
((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -92,7 +93,7 @@ struct hapd_interfaces {
- #ifdef CONFIG_CTRL_IFACE_UDP
- unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
- #endif /* CONFIG_CTRL_IFACE_UDP */
--
-+ struct ubus_object ubus;
- };
-
- enum hostapd_chan_status {
@@ -184,6 +185,7 @@ struct hostapd_data {
struct hostapd_iface *iface;
struct hostapd_config *iconf;
@@ -49,7 +41,7 @@
struct hostapd_iface * hostapd_alloc_iface(void);
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
-@@ -455,6 +455,7 @@ void hostapd_free_hapd_data(struct hosta
+@@ -435,6 +435,7 @@ void hostapd_free_hapd_data(struct hosta
hapd->beacon_set_done = 0;
wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
@@ -57,7 +49,7 @@
accounting_deinit(hapd);
hostapd_deinit_wpa(hapd);
vlan_deinit(hapd);
-@@ -1207,6 +1208,8 @@ static int hostapd_start_beacon(struct h
+@@ -1187,6 +1188,8 @@ static int hostapd_start_beacon(struct h
if (hapd->driver && hapd->driver->set_operstate)
hapd->driver->set_operstate(hapd->drv_priv, 1);
@@ -66,7 +58,7 @@
return 0;
}
-@@ -2295,6 +2298,7 @@ static int hostapd_setup_interface_compl
+@@ -2275,6 +2278,7 @@ static int hostapd_setup_interface_compl
if (err)
goto fail;
@@ -74,7 +66,7 @@
wpa_printf(MSG_DEBUG, "Completing interface initialization");
if (iface->freq) {
#ifdef NEED_AP_MLME
-@@ -2514,6 +2518,7 @@ dfs_offload:
+@@ -2494,6 +2498,7 @@ dfs_offload:
fail:
wpa_printf(MSG_ERROR, "Interface initialization failed");
@@ -82,7 +74,7 @@
if (iface->is_no_ir) {
hostapd_set_state(iface, HAPD_IFACE_NO_IR);
-@@ -3004,6 +3009,7 @@ void hostapd_interface_deinit_free(struc
+@@ -2984,6 +2989,7 @@ void hostapd_interface_deinit_free(struc
(unsigned int) iface->conf->num_bss);
driver = iface->bss[0]->driver;
drv_priv = iface->bss[0]->drv_priv;
@@ -330,20 +322,21 @@
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
-@@ -194,6 +194,12 @@ ifdef CONFIG_EAPOL_TEST
+@@ -192,6 +192,13 @@ ifdef CONFIG_EAPOL_TEST
CFLAGS += -Werror -DEAPOL_TEST
endif
+ifdef CONFIG_UBUS
+CFLAGS += -DUBUS_SUPPORT
+OBJS += ubus.o
++OBJS += ../src/utils/uloop.o
+LIBS += -lubox -lubus
+endif
+
ifdef CONFIG_CODE_COVERAGE
CFLAGS += -O0 -fprofile-arcs -ftest-coverage
LIBS += -lgcov
-@@ -989,6 +995,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
+@@ -987,6 +994,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
CFLAGS += -DCONFIG_CTRL_IFACE_MIB
endif
OBJS += ../src/ap/ctrl_iface_ap.o
@@ -355,7 +348,7 @@
CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
-@@ -7635,6 +7635,8 @@ struct wpa_supplicant * wpa_supplicant_a
+@@ -7566,6 +7566,8 @@ struct wpa_supplicant * wpa_supplicant_a
}
#endif /* CONFIG_P2P */
@@ -364,7 +357,7 @@
return wpa_s;
}
-@@ -7661,6 +7663,8 @@ int wpa_supplicant_remove_iface(struct w
+@@ -7592,6 +7594,8 @@ int wpa_supplicant_remove_iface(struct w
struct wpa_supplicant *parent = wpa_s->parent;
#endif /* CONFIG_MESH */
@@ -373,7 +366,7 @@
/* Remove interface from the global list of interfaces */
prev = global->ifaces;
if (prev == wpa_s) {
-@@ -8007,8 +8011,12 @@ int wpa_supplicant_run(struct wpa_global
+@@ -7938,8 +7942,12 @@ int wpa_supplicant_run(struct wpa_global
eloop_register_signal_terminate(wpa_supplicant_terminate, global);
eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
@@ -396,7 +389,7 @@
extern const char *const wpa_supplicant_version;
extern const char *const wpa_supplicant_license;
-@@ -324,6 +325,8 @@ struct wpa_global {
+@@ -319,6 +320,8 @@ struct wpa_global {
#endif /* CONFIG_WIFI_DISPLAY */
struct psk_list_entry *add_psk; /* From group formation */
@@ -405,7 +398,7 @@
};
-@@ -655,6 +658,7 @@ struct wpa_supplicant {
+@@ -650,6 +653,7 @@ struct wpa_supplicant {
unsigned char own_addr[ETH_ALEN];
unsigned char perm_addr[ETH_ALEN];
char ifname[100];
@@ -432,36 +425,18 @@
if (wpa_s->conf->wps_cred_processing == 1)
return 0;
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -991,6 +991,7 @@ int main(int argc, char *argv[])
- }
-
- hostapd_global_ctrl_iface_init(&interfaces);
-+ hostapd_ubus_add(&interfaces);
-
- if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
- wpa_printf(MSG_ERROR, "Failed to start eloop");
-@@ -1000,6 +1001,7 @@ int main(int argc, char *argv[])
- ret = 0;
-
- out:
-+ hostapd_ubus_free(&interfaces);
- hostapd_global_ctrl_iface_deinit(&interfaces);
- /* Deinitialize all interfaces */
- for (i = 0; i < interfaces.count; i++) {
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
-@@ -204,7 +204,7 @@ int main(int argc, char *argv[])
+@@ -203,7 +203,7 @@ int main(int argc, char *argv[])
for (;;) {
c = getopt(argc, argv,
-- "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:No:O:p:P:qsTtuv::W");
-+ "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:nNo:O:p:P:qsTtuv::W");
+- "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuv::W");
++ "b:Bc:C:D:de:f:g:G:hi:I:KLMm:nNo:O:p:P:qsTtuv::W");
if (c < 0)
break;
switch (c) {
-@@ -272,6 +272,9 @@ int main(int argc, char *argv[])
+@@ -268,6 +268,9 @@ int main(int argc, char *argv[])
params.conf_p2p_dev = optarg;
break;
#endif /* CONFIG_P2P */
@@ -623,3 +598,151 @@
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
pos, end - pos);
}
+--- a/src/utils/eloop.c
++++ b/src/utils/eloop.c
+@@ -77,6 +77,9 @@ struct eloop_sock_table {
+ struct eloop_data {
+ int max_sock;
+
++ eloop_timeout_poll_handler timeout_poll_cb;
++ eloop_poll_handler poll_cb;
++
+ size_t count; /* sum of all table counts */
+ #ifdef CONFIG_ELOOP_POLL
+ size_t max_pollfd_map; /* number of pollfds_map currently allocated */
+@@ -1121,6 +1124,12 @@ void eloop_run(void)
+ os_reltime_sub(&timeout->time, &now, &tv);
+ else
+ tv.sec = tv.usec = 0;
++ }
++
++ if (eloop.timeout_poll_cb && eloop.timeout_poll_cb(&tv, !!timeout))
++ timeout = (void *)1;
++
++ if (timeout) {
+ #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
+ timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+ #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+@@ -1190,7 +1199,8 @@ void eloop_run(void)
+ eloop.exceptions.changed = 0;
+
+ eloop_process_pending_signals();
+-
++ if (eloop.poll_cb)
++ eloop.poll_cb();
+
+ /* check if some registered timeouts have occurred */
+ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+@@ -1252,6 +1262,14 @@ out:
+ return;
+ }
+
++int eloop_register_cb(eloop_poll_handler poll_cb,
++ eloop_timeout_poll_handler timeout_cb)
++{
++ eloop.poll_cb = poll_cb;
++ eloop.timeout_poll_cb = timeout_cb;
++
++ return 0;
++}
+
+ void eloop_terminate(void)
+ {
+--- a/src/utils/eloop.h
++++ b/src/utils/eloop.h
+@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(vo
+ */
+ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
++typedef bool (*eloop_timeout_poll_handler)(struct os_reltime *tv, bool tv_set);
++typedef void (*eloop_poll_handler)(void);
++
+ /**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+@@ -73,6 +76,9 @@ typedef void (*eloop_signal_handler)(int
+ */
+ int eloop_init(void);
+
++int eloop_register_cb(eloop_poll_handler poll_cb,
++ eloop_timeout_poll_handler timeout_cb);
++
+ /**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+@@ -320,6 +326,8 @@ int eloop_register_signal_reconfig(eloop
+ */
+ int eloop_sock_requeue(void);
+
++void eloop_add_uloop(void);
++
+ /**
+ * eloop_run - Start the event loop
+ *
+--- /dev/null
++++ b/src/utils/uloop.c
+@@ -0,0 +1,64 @@
++#include <libubox/uloop.h>
++#include "includes.h"
++#include "common.h"
++#include "eloop.h"
++
++static void eloop_uloop_event_cb(int sock, void *eloop_ctx, void *sock_ctx)
++{
++}
++
++static void eloop_uloop_fd_cb(struct uloop_fd *fd, unsigned int events)
++{
++ unsigned int changed = events ^ fd->flags;
++
++ if (changed & ULOOP_READ) {
++ if (events & ULOOP_READ)
++ eloop_register_sock(fd->fd, EVENT_TYPE_READ, eloop_uloop_event_cb, fd, fd);
++ else
++ eloop_unregister_sock(fd->fd, EVENT_TYPE_READ);
++ }
++
++ if (changed & ULOOP_WRITE) {
++ if (events & ULOOP_WRITE)
++ eloop_register_sock(fd->fd, EVENT_TYPE_WRITE, eloop_uloop_event_cb, fd, fd);
++ else
++ eloop_unregister_sock(fd->fd, EVENT_TYPE_WRITE);
++ }
++}
++
++static bool uloop_timeout_poll_handler(struct os_reltime *tv, bool tv_set)
++{
++ struct os_reltime tv_uloop;
++ int timeout_ms = uloop_get_next_timeout();
++
++ if (timeout_ms < 0)
++ return false;
++
++ tv_uloop.sec = timeout_ms / 1000;
++ tv_uloop.usec = (timeout_ms % 1000) * 1000;
++
++ if (!tv_set || os_reltime_before(&tv_uloop, tv)) {
++ *tv = tv_uloop;
++ return true;
++ }
++
++ return false;
++}
++
++static void uloop_poll_handler(void)
++{
++ uloop_run_timeout(0);
++}
++
++void eloop_add_uloop(void)
++{
++ static bool init_done = false;
++
++ if (!init_done) {
++ uloop_init();
++ uloop_fd_set_cb = eloop_uloop_fd_cb;
++ init_done = true;
++ }
++
++ eloop_register_cb(uloop_poll_handler, uloop_timeout_poll_handler);
++}
diff --git a/package/network/services/hostapd/patches/601-ucode_support.patch b/package/network/services/hostapd/patches/601-ucode_support.patch
new file mode 100644
index 000000000000..e0bbf1337da8
--- /dev/null
+++ b/package/network/services/hostapd/patches/601-ucode_support.patch
@@ -0,0 +1,333 @@
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -168,9 +168,21 @@ OBJS += ../src/eapol_auth/eapol_auth_sm.
+
+ ifdef CONFIG_UBUS
+ CFLAGS += -DUBUS_SUPPORT
+-OBJS += ../src/utils/uloop.o
+ OBJS += ../src/ap/ubus.o
+-LIBS += -lubox -lubus
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ../src/ap/ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
++OBJS += ../src/utils/uloop.o
++LIBS += -lubox
+ endif
+
+ ifdef CONFIG_CODE_COVERAGE
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -994,6 +994,7 @@ int main(int argc, char *argv[])
+ }
+
+ hostapd_global_ctrl_iface_init(&interfaces);
++ hostapd_ucode_init(&interfaces);
+
+ if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
+ wpa_printf(MSG_ERROR, "Failed to start eloop");
+@@ -1003,6 +1004,7 @@ int main(int argc, char *argv[])
+ ret = 0;
+
+ out:
++ hostapd_ucode_free();
+ hostapd_global_ctrl_iface_deinit(&interfaces);
+ /* Deinitialize all interfaces */
+ for (i = 0; i < interfaces.count; i++) {
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -19,6 +19,7 @@
+ #include "ap_config.h"
+ #include "drivers/driver.h"
+ #include "ubus.h"
++#include "ucode.h"
+
+ #define OCE_STA_CFON_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_STA_CFON) && \
+@@ -51,6 +52,10 @@ struct hapd_interfaces {
+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
++ int (*ctrl_iface_recv)(struct hostapd_data *hapd,
++ char *buf, char *reply, int reply_size,
++ struct sockaddr_storage *from,
++ socklen_t fromlen);
+ int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+@@ -186,6 +191,7 @@ struct hostapd_data {
+ struct hostapd_config *iconf;
+ struct hostapd_bss_config *conf;
+ struct hostapd_ubus_bss ubus;
++ struct hostapd_ucode_bss ucode;
+ int interface_added; /* virtual interface added for this BSS */
+ unsigned int started:1;
+ unsigned int disabled:1;
+@@ -506,6 +512,7 @@ struct hostapd_sta_info {
+ */
+ struct hostapd_iface {
+ struct hapd_interfaces *interfaces;
++ struct hostapd_ucode_iface ucode;
+ void *owner;
+ char *config_fname;
+ struct hostapd_config *conf;
+@@ -706,6 +713,8 @@ struct hostapd_iface * hostapd_init(stru
+ struct hostapd_iface *
+ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug);
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
++void hostapd_bss_deinit(struct hostapd_data *hapd);
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -252,6 +252,8 @@ int hostapd_reload_config(struct hostapd
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+
++ hostapd_ucode_reload_bss(hapd);
++
+ if (iface->config_fname == NULL) {
+ /* Only in-memory config in use - assume it has been updated */
+ hostapd_clear_old(iface);
+@@ -435,6 +437,7 @@ void hostapd_free_hapd_data(struct hosta
+ hapd->beacon_set_done = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
++ hostapd_ucode_free_bss(hapd);
+ hostapd_ubus_free_bss(hapd);
+ accounting_deinit(hapd);
+ hostapd_deinit_wpa(hapd);
+@@ -599,6 +602,7 @@ void hostapd_cleanup_iface_partial(struc
+ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+ {
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
++ hostapd_ucode_free_iface(iface);
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ NULL);
+@@ -1189,6 +1193,7 @@ static int hostapd_start_beacon(struct h
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+ hostapd_ubus_add_bss(hapd);
++ hostapd_ucode_add_bss(hapd);
+
+ return 0;
+ }
+@@ -1211,8 +1216,7 @@ static int hostapd_start_beacon(struct h
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+-static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+- bool start_beacon)
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ {
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 ssid[SSID_MAX_LEN + 1];
+@@ -2698,7 +2702,7 @@ hostapd_alloc_bss_data(struct hostapd_if
+ }
+
+
+-static void hostapd_bss_deinit(struct hostapd_data *hapd)
++void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
+ if (!hapd)
+ return;
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -195,8 +195,20 @@ endif
+ ifdef CONFIG_UBUS
+ CFLAGS += -DUBUS_SUPPORT
+ OBJS += ubus.o
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
+ OBJS += ../src/utils/uloop.o
+-LIBS += -lubox -lubus
++LIBS += -lubox
+ endif
+
+ ifdef CONFIG_CODE_COVERAGE
+@@ -997,6 +1009,9 @@ OBJS += ../src/ap/ctrl_iface_ap.o
+ ifdef CONFIG_UBUS
+ OBJS += ../src/ap/ubus.o
+ endif
++ifdef CONFIG_UCODE
++OBJS += ../src/ap/ucode.o
++endif
+ endif
+
+ CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -1044,6 +1044,7 @@ void wpa_supplicant_set_state(struct wpa
+ sme_sched_obss_scan(wpa_s, 0);
+ }
+ wpa_s->wpa_state = state;
++ wpas_ucode_update_state(wpa_s);
+
+ #ifdef CONFIG_BGSCAN
+ if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
+@@ -7567,6 +7568,7 @@ struct wpa_supplicant * wpa_supplicant_a
+ #endif /* CONFIG_P2P */
+
+ wpas_ubus_add_bss(wpa_s);
++ wpas_ucode_add_bss(wpa_s);
+
+ return wpa_s;
+ }
+@@ -7594,6 +7596,7 @@ int wpa_supplicant_remove_iface(struct w
+ struct wpa_supplicant *parent = wpa_s->parent;
+ #endif /* CONFIG_MESH */
+
++ wpas_ucode_free_bss(wpa_s);
+ wpas_ubus_free_bss(wpa_s);
+
+ /* Remove interface from the global list of interfaces */
+@@ -7904,6 +7907,7 @@ struct wpa_global * wpa_supplicant_init(
+
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
++ wpas_ucode_init(global);
+
+ return global;
+ }
+@@ -7942,12 +7946,8 @@ int wpa_supplicant_run(struct wpa_global
+ eloop_register_signal_terminate(wpa_supplicant_terminate, global);
+ eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+
+- wpas_ubus_add(global);
+-
+ eloop_run();
+
+- wpas_ubus_free(global);
+-
+ return 0;
+ }
+
+@@ -7980,6 +7980,8 @@ void wpa_supplicant_deinit(struct wpa_gl
+
+ wpas_notify_supplicant_deinitialized(global);
+
++ wpas_ucode_free();
++
+ eap_peer_unregister_methods();
+ #ifdef CONFIG_AP
+ eap_server_unregister_methods();
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -22,6 +22,7 @@
+ #include "wmm_ac.h"
+ #include "pasn/pasn_common.h"
+ #include "ubus.h"
++#include "ucode.h"
+
+ extern const char *const wpa_supplicant_version;
+ extern const char *const wpa_supplicant_license;
+@@ -654,6 +655,7 @@ struct wpa_supplicant {
+ unsigned char perm_addr[ETH_ALEN];
+ char ifname[100];
+ struct wpas_ubus_bss ubus;
++ struct wpas_ucode_bss ucode;
+ #ifdef CONFIG_MATCH_IFACE
+ int matched;
+ #endif /* CONFIG_MATCH_IFACE */
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4856,6 +4856,7 @@ try_again:
+ return -1;
+ }
+
++ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
+ return 0;
+@@ -4957,6 +4958,7 @@ fail:
+ os_free(fname);
+
+ interface->global_ctrl_sock = s;
++ interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+ interface, NULL);
+
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -6426,6 +6426,7 @@ union wpa_event_data {
+
+ /**
+ * struct ch_switch
++ * @count: Count until channel switch activates
+ * @freq: Frequency of new channel in MHz
+ * @ht_enabled: Whether this is an HT channel
+ * @ch_offset: Secondary channel offset
+@@ -6436,6 +6437,7 @@ union wpa_event_data {
+ * @punct_bitmap: Puncturing bitmap
+ */
+ struct ch_switch {
++ int count;
+ int freq;
+ int ht_enabled;
+ int ch_offset;
+--- a/src/drivers/driver_nl80211_event.c
++++ b/src/drivers/driver_nl80211_event.c
+@@ -1202,6 +1202,7 @@ static void mlme_event_ch_switch(struct
+ struct nlattr *bw, struct nlattr *cf1,
+ struct nlattr *cf2,
+ struct nlattr *punct_bitmap,
++ struct nlattr *count,
+ int finished)
+ {
+ struct i802_bss *bss;
+@@ -1265,6 +1266,8 @@ static void mlme_event_ch_switch(struct
+ data.ch_switch.cf1 = nla_get_u32(cf1);
+ if (cf2)
+ data.ch_switch.cf2 = nla_get_u32(cf2);
++ if (count)
++ data.ch_switch.count = nla_get_u32(count);
+
+ if (finished)
+ bss->flink->freq = data.ch_switch.freq;
+@@ -3848,6 +3851,7 @@ static void do_process_drv_event(struct
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
++ tb[NL80211_ATTR_CH_SWITCH_COUNT],
+ 0);
+ break;
+ case NL80211_CMD_CH_SWITCH_NOTIFY:
+@@ -3860,6 +3864,7 @@ static void do_process_drv_event(struct
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
++ NULL,
+ 1);
+ break;
+ case NL80211_CMD_DISCONNECT:
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -5381,6 +5381,7 @@ void supplicant_event(void *ctx, enum wp
+ event_to_string(event), event);
+ #endif /* CONFIG_NO_STDOUT_DEBUG */
+
++ wpas_ucode_event(wpa_s, event, data);
+ switch (event) {
+ case EVENT_AUTH:
+ #ifdef CONFIG_FST
diff --git a/package/network/services/hostapd/patches/700-wifi-reload.patch b/package/network/services/hostapd/patches/700-wifi-reload.patch
deleted file mode 100644
index 0c7627645f69..000000000000
--- a/package/network/services/hostapd/patches/700-wifi-reload.patch
+++ /dev/null
@@ -1,194 +0,0 @@
---- a/hostapd/config_file.c
-+++ b/hostapd/config_file.c
-@@ -2420,6 +2420,8 @@ static int hostapd_config_fill(struct ho
- bss->isolate = atoi(pos);
- } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
- bss->ap_max_inactivity = atoi(pos);
-+ } else if (os_strcmp(buf, "config_id") == 0) {
-+ bss->config_id = os_strdup(pos);
- } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
- bss->skip_inactivity_poll = atoi(pos);
- } else if (os_strcmp(buf, "config_id") == 0) {
-@@ -3130,6 +3132,8 @@ static int hostapd_config_fill(struct ho
- }
- } else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
- conf->acs_exclude_dfs = atoi(pos);
-+ } else if (os_strcmp(buf, "radio_config_id") == 0) {
-+ conf->config_id = os_strdup(pos);
- } else if (os_strcmp(buf, "op_class") == 0) {
- conf->op_class = atoi(pos);
- } else if (os_strcmp(buf, "channel") == 0) {
---- a/src/ap/ap_config.c
-+++ b/src/ap/ap_config.c
-@@ -998,6 +998,7 @@ void hostapd_config_free(struct hostapd_
-
- for (i = 0; i < conf->num_bss; i++)
- hostapd_config_free_bss(conf->bss[i]);
-+ os_free(conf->config_id);
- os_free(conf->bss);
- os_free(conf->supported_rates);
- os_free(conf->basic_rates);
---- a/src/ap/ap_config.h
-+++ b/src/ap/ap_config.h
-@@ -998,6 +998,7 @@ struct eht_phy_capabilities_info {
- struct hostapd_config {
- struct hostapd_bss_config **bss, *last_bss;
- size_t num_bss;
-+ char *config_id;
-
- u16 beacon_int;
- int rts_threshold;
---- a/src/ap/hostapd.c
-+++ b/src/ap/hostapd.c
-@@ -255,6 +255,10 @@ static int hostapd_iface_conf_changed(st
- {
- size_t i;
-
-+ if (newconf->config_id != oldconf->config_id)
-+ if (strcmp(newconf->config_id, oldconf->config_id))
-+ return 1;
-+
- if (newconf->num_bss != oldconf->num_bss)
- return 1;
-
-@@ -268,7 +272,7 @@ static int hostapd_iface_conf_changed(st
- }
-
-
--int hostapd_reload_config(struct hostapd_iface *iface)
-+int hostapd_reload_config(struct hostapd_iface *iface, int reconf)
- {
- struct hapd_interfaces *interfaces = iface->interfaces;
- struct hostapd_data *hapd = iface->bss[0];
-@@ -296,6 +300,9 @@ int hostapd_reload_config(struct hostapd
- char *fname;
- int res;
-
-+ if (reconf)
-+ return -1;
-+
- hostapd_clear_old(iface);
-
- wpa_printf(MSG_DEBUG,
-@@ -322,6 +329,24 @@ int hostapd_reload_config(struct hostapd
- wpa_printf(MSG_ERROR,
- "Failed to enable interface on config reload");
- return res;
-+ } else {
-+ for (j = 0; j < iface->num_bss; j++) {
-+ hapd = iface->bss[j];
-+ if (!hapd->config_id || strcmp(hapd->config_id, newconf->bss[j]->config_id)) {
-+ hostapd_flush_old_stations(iface->bss[j],
-+ WLAN_REASON_PREV_AUTH_NOT_VALID);
-+#ifdef CONFIG_WEP
-+ hostapd_broadcast_wep_clear(iface->bss[j]);
-+#endif
-+
-+#ifndef CONFIG_NO_RADIUS
-+ /* TODO: update dynamic data based on changed configuration
-+ * items (e.g., open/close sockets, etc.) */
-+ radius_client_flush(iface->bss[j]->radius, 0);
-+#endif /* CONFIG_NO_RADIUS */
-+ wpa_printf(MSG_INFO, "bss %zu changed", j);
-+ }
-+ }
- }
- iface->conf = newconf;
-
-@@ -338,6 +363,12 @@ int hostapd_reload_config(struct hostapd
-
- for (j = 0; j < iface->num_bss; j++) {
- hapd = iface->bss[j];
-+ if (hapd->config_id) {
-+ os_free(hapd->config_id);
-+ hapd->config_id = NULL;
-+ }
-+ if (newconf->bss[j]->config_id)
-+ hapd->config_id = strdup(newconf->bss[j]->config_id);
- if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
- os_strcmp(hapd->conf->config_id,
- newconf->bss[j]->config_id) != 0)
-@@ -2700,6 +2731,10 @@ hostapd_alloc_bss_data(struct hostapd_if
- hapd->iconf = conf;
- hapd->conf = bss;
- hapd->iface = hapd_iface;
-+ if (bss && bss->config_id)
-+ hapd->config_id = strdup(bss->config_id);
-+ else
-+ hapd->config_id = NULL;
- if (conf)
- hapd->driver = conf->driver;
- hapd->ctrl_sock = -1;
---- a/src/ap/hostapd.h
-+++ b/src/ap/hostapd.h
-@@ -47,7 +47,7 @@ struct mesh_conf;
- struct hostapd_iface;
-
- struct hapd_interfaces {
-- int (*reload_config)(struct hostapd_iface *iface);
-+ int (*reload_config)(struct hostapd_iface *iface, int reconf);
- struct hostapd_config * (*config_read_cb)(const char *config_fname);
- int (*ctrl_iface_init)(struct hostapd_data *hapd);
- void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
-@@ -186,6 +186,7 @@ struct hostapd_data {
- struct hostapd_config *iconf;
- struct hostapd_bss_config *conf;
- struct hostapd_ubus_bss ubus;
-+ char *config_id;
- int interface_added; /* virtual interface added for this BSS */
- unsigned int started:1;
- unsigned int disabled:1;
-@@ -689,7 +690,7 @@ struct hostapd_iface {
- int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
- int (*cb)(struct hostapd_iface *iface,
- void *ctx), void *ctx);
--int hostapd_reload_config(struct hostapd_iface *iface);
-+int hostapd_reload_config(struct hostapd_iface *iface, int reconf);
- void hostapd_reconfig_encryption(struct hostapd_data *hapd);
- struct hostapd_data *
- hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
---- a/src/drivers/driver_nl80211.c
-+++ b/src/drivers/driver_nl80211.c
-@@ -5322,6 +5322,9 @@ static int wpa_driver_nl80211_set_ap(voi
- if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
- ret, strerror(-ret));
-+ if (!bss->flink->beacon_set)
-+ ret = 0;
-+ bss->flink->beacon_set = 0;
- } else {
- link->beacon_set = 1;
- nl80211_set_bss(bss, params->cts_protect, params->preamble,
---- a/hostapd/ctrl_iface.c
-+++ b/hostapd/ctrl_iface.c
-@@ -187,7 +187,7 @@ static int hostapd_ctrl_iface_update(str
- iface->interfaces->config_read_cb = hostapd_ctrl_iface_config_read;
- reload_opts = txt;
-
-- hostapd_reload_config(iface);
-+ hostapd_reload_config(iface, 0);
-
- iface->interfaces->config_read_cb = config_read_cb;
- }
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -410,7 +410,7 @@ static void handle_term(int sig, void *s
-
- static int handle_reload_iface(struct hostapd_iface *iface, void *ctx)
- {
-- if (hostapd_reload_config(iface) < 0) {
-+ if (hostapd_reload_config(iface, 0) < 0) {
- wpa_printf(MSG_WARNING, "Failed to read new configuration "
- "file - continuing with old.");
- }
---- a/src/ap/wps_hostapd.c
-+++ b/src/ap/wps_hostapd.c
-@@ -315,7 +315,7 @@ static void wps_reload_config(void *eloo
-
- wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
- if (iface->interfaces == NULL ||
-- iface->interfaces->reload_config(iface) < 0) {
-+ iface->interfaces->reload_config(iface, 1) < 0) {
- wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
- "configuration");
- }
diff --git a/package/network/services/hostapd/patches/701-reload_config_inline.patch b/package/network/services/hostapd/patches/701-reload_config_inline.patch
new file mode 100644
index 000000000000..44c8892bae99
--- /dev/null
+++ b/package/network/services/hostapd/patches/701-reload_config_inline.patch
@@ -0,0 +1,33 @@
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4810,7 +4810,12 @@ struct hostapd_config * hostapd_config_r
+ int errors = 0;
+ size_t i;
+
+- f = fopen(fname, "r");
++ if (!strncmp(fname, "data:", 5)) {
++ f = fmemopen((void *)(fname + 5), strlen(fname + 5), "r");
++ fname = "<inline>";
++ } else {
++ f = fopen(fname, "r");
++ }
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+ "for reading.", fname);
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(cons
+ while (cred_tail && cred_tail->next)
+ cred_tail = cred_tail->next;
+
++ if (!strncmp(name, "data:", 5)) {
++ f = fmemopen((void *)(name + 5), strlen(name + 5), "r");
++ name = "<inline>";
++ } else {
++ f = fopen(name, "r");
++ }
+ wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
+- f = fopen(name, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+ "error: %s", name, strerror(errno));
diff --git a/package/network/services/hostapd/patches/710-vlan_no_bridge.patch b/package/network/services/hostapd/patches/710-vlan_no_bridge.patch
index 61f33acb6e8e..63d1b8a3b835 100644
--- a/package/network/services/hostapd/patches/710-vlan_no_bridge.patch
+++ b/package/network/services/hostapd/patches/710-vlan_no_bridge.patch
@@ -30,7 +30,7 @@
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
-@@ -3355,6 +3355,8 @@ static int hostapd_config_fill(struct ho
+@@ -3351,6 +3351,8 @@ static int hostapd_config_fill(struct ho
#ifndef CONFIG_NO_VLAN
} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
bss->ssid.dynamic_vlan = atoi(pos);
diff --git a/package/network/services/hostapd/patches/720-iface_max_num_sta.patch b/package/network/services/hostapd/patches/720-iface_max_num_sta.patch
index 0bb00f955547..1aa4456a5f40 100644
--- a/package/network/services/hostapd/patches/720-iface_max_num_sta.patch
+++ b/package/network/services/hostapd/patches/720-iface_max_num_sta.patch
@@ -1,6 +1,6 @@
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
-@@ -2850,6 +2850,14 @@ static int hostapd_config_fill(struct ho
+@@ -2848,6 +2848,14 @@ static int hostapd_config_fill(struct ho
line, bss->max_num_sta, MAX_STA_COUNT);
return 1;
}
@@ -17,7 +17,7 @@
} else if (os_strcmp(buf, "extended_key_id") == 0) {
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
-@@ -734,6 +734,7 @@ void hostapd_cleanup_cs_params(struct ho
+@@ -742,6 +742,7 @@ void hostapd_cleanup_cs_params(struct ho
void hostapd_periodic_iface(struct hostapd_iface *iface);
int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
@@ -27,10 +27,10 @@
void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
-@@ -272,6 +272,30 @@ static int hostapd_iface_conf_changed(st
+@@ -244,6 +244,29 @@ static int hostapd_iface_conf_changed(st
+ return 0;
}
-
+static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
+{
+ int num_sta = 0;
@@ -54,10 +54,9 @@
+
+ return 0;
+}
-+
- int hostapd_reload_config(struct hostapd_iface *iface, int reconf)
+
+ int hostapd_reload_config(struct hostapd_iface *iface)
{
- struct hapd_interfaces *interfaces = iface->interfaces;
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -1252,7 +1252,7 @@ void handle_probe_req(struct hostapd_dat
@@ -71,7 +70,7 @@
" since no room for additional STA",
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
-@@ -1037,6 +1037,8 @@ struct hostapd_config {
+@@ -1036,6 +1036,8 @@ struct hostapd_config {
unsigned int track_sta_max_num;
unsigned int track_sta_max_age;
diff --git a/package/network/services/hostapd/patches/730-ft_iface.patch b/package/network/services/hostapd/patches/730-ft_iface.patch
index 563fe5b5fbbd..0795ed15a140 100644
--- a/package/network/services/hostapd/patches/730-ft_iface.patch
+++ b/package/network/services/hostapd/patches/730-ft_iface.patch
@@ -1,6 +1,6 @@
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
-@@ -3009,6 +3009,8 @@ static int hostapd_config_fill(struct ho
+@@ -3007,6 +3007,8 @@ static int hostapd_config_fill(struct ho
wpa_printf(MSG_INFO,
"Line %d: Obsolete peerkey parameter ignored", line);
#ifdef CONFIG_IEEE80211R_AP
diff --git a/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch b/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch
index 124f5ea6bae5..97c32df70446 100644
--- a/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch
+++ b/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch
@@ -18,7 +18,7 @@
#ifdef CONFIG_HS20
static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
-@@ -4066,10 +4066,10 @@ static int hostapd_config_fill(struct ho
+@@ -4062,10 +4062,10 @@ static int hostapd_config_fill(struct ho
bss->gas_frag_limit = val;
} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
bss->gas_comeback_delay = atoi(pos);
@@ -32,7 +32,7 @@
os_free(bss->dump_msk_file);
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
-@@ -1534,6 +1534,7 @@ static int hostapd_setup_bss(struct host
+@@ -1486,6 +1486,7 @@ int hostapd_setup_bss(struct hostapd_dat
wpa_printf(MSG_ERROR, "GAS server initialization failed");
return -1;
}
@@ -40,7 +40,7 @@
if (conf->qos_map_set_len &&
hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
-@@ -1541,7 +1542,6 @@ static int hostapd_setup_bss(struct host
+@@ -1493,7 +1494,6 @@ int hostapd_setup_bss(struct hostapd_dat
wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
return -1;
}
diff --git a/package/network/services/hostapd/patches/760-dynamic_own_ip.patch b/package/network/services/hostapd/patches/760-dynamic_own_ip.patch
index 946b4533bf24..2c705a68cf0a 100644
--- a/package/network/services/hostapd/patches/760-dynamic_own_ip.patch
+++ b/package/network/services/hostapd/patches/760-dynamic_own_ip.patch
@@ -98,7 +98,7 @@
hapd->conf->own_ip_addr.af == AF_INET &&
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
-@@ -2690,6 +2690,8 @@ static int hostapd_config_fill(struct ho
+@@ -2688,6 +2688,8 @@ static int hostapd_config_fill(struct ho
} else if (os_strcmp(buf, "iapp_interface") == 0) {
wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
#endif /* CONFIG_IAPP */
diff --git a/package/network/services/hostapd/patches/761-shared_das_port.patch b/package/network/services/hostapd/patches/761-shared_das_port.patch
index dad7afddf13b..cbb2a1be3c47 100644
--- a/package/network/services/hostapd/patches/761-shared_das_port.patch
+++ b/package/network/services/hostapd/patches/761-shared_das_port.patch
@@ -10,7 +10,7 @@
unsigned int time_window;
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
-@@ -1471,6 +1471,7 @@ static int hostapd_setup_bss(struct host
+@@ -1423,6 +1423,7 @@ int hostapd_setup_bss(struct hostapd_dat
os_memset(&das_conf, 0, sizeof(das_conf));
das_conf.port = conf->radius_das_port;
diff --git a/package/network/services/hostapd/patches/770-radius_server.patch b/package/network/services/hostapd/patches/770-radius_server.patch
new file mode 100644
index 000000000000..e4690c76b8b5
--- /dev/null
+++ b/package/network/services/hostapd/patches/770-radius_server.patch
@@ -0,0 +1,154 @@
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -63,6 +63,10 @@ endif
+ OBJS += main.o
+ OBJS += config_file.o
+
++ifdef CONFIG_RADIUS_SERVER
++OBJS += radius.o
++endif
++
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/drv_callbacks.o
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -40,6 +40,7 @@ struct hapd_global {
+
+ static struct hapd_global global;
+
++extern int radius_main(int argc, char **argv);
+
+ #ifndef CONFIG_NO_HOSTAPD_LOGGER
+ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+@@ -758,6 +759,11 @@ int main(int argc, char *argv[])
+ if (os_program_init())
+ return -1;
+
++#ifdef RADIUS_SERVER
++ if (strstr(argv[0], "radius"))
++ return radius_main(argc, argv);
++#endif
++
+ os_memset(&interfaces, 0, sizeof(interfaces));
+ interfaces.reload_config = hostapd_reload_config;
+ interfaces.config_read_cb = hostapd_config_read;
+--- a/src/radius/radius_server.c
++++ b/src/radius/radius_server.c
+@@ -63,6 +63,12 @@ struct radius_server_counters {
+ u32 unknown_acct_types;
+ };
+
++struct radius_accept_attr {
++ u8 type;
++ u16 len;
++ void *data;
++};
++
+ /**
+ * struct radius_session - Internal RADIUS server data for a session
+ */
+@@ -90,7 +96,7 @@ struct radius_session {
+ unsigned int macacl:1;
+ unsigned int t_c_filtering:1;
+
+- struct hostapd_radius_attr *accept_attr;
++ struct radius_accept_attr *accept_attr;
+
+ u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
+ };
+@@ -394,6 +400,7 @@ static void radius_server_session_free(s
+ radius_msg_free(sess->last_reply);
+ os_free(sess->username);
+ os_free(sess->nas_ip);
++ os_free(sess->accept_attr);
+ os_free(sess);
+ data->num_sess--;
+ }
+@@ -554,6 +561,36 @@ radius_server_erp_find_key(struct radius
+ }
+ #endif /* CONFIG_ERP */
+
++static struct radius_accept_attr *
++radius_server_copy_attr(const struct hostapd_radius_attr *data)
++{
++ const struct hostapd_radius_attr *attr;
++ struct radius_accept_attr *attr_new;
++ size_t data_size = 0;
++ void *data_buf;
++ int n_attr = 1;
++
++ for (attr = data; attr; attr = attr->next) {
++ n_attr++;
++ data_size += wpabuf_len(attr->val);
++ }
++
++ attr_new = os_zalloc(n_attr * sizeof(*attr) + data_size);
++ if (!attr_new)
++ return NULL;
++
++ data_buf = &attr_new[n_attr];
++ for (n_attr = 0, attr = data; attr; attr = attr->next) {
++ struct radius_accept_attr *cur = &attr_new[n_attr++];
++
++ cur->type = attr->type;
++ cur->len = wpabuf_len(attr->val);
++ cur->data = memcpy(data_buf, wpabuf_head(attr->val), cur->len);
++ data_buf += cur->len;
++ }
++
++ return attr_new;
++}
+
+ static struct radius_session *
+ radius_server_get_new_session(struct radius_server_data *data,
+@@ -607,7 +644,7 @@ radius_server_get_new_session(struct rad
+ eap_user_free(tmp);
+ return NULL;
+ }
+- sess->accept_attr = tmp->accept_attr;
++ sess->accept_attr = radius_server_copy_attr(tmp->accept_attr);
+ sess->macacl = tmp->macacl;
+ eap_user_free(tmp);
+
+@@ -1118,11 +1155,10 @@ radius_server_encapsulate_eap(struct rad
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+- struct hostapd_radius_attr *attr;
+- for (attr = sess->accept_attr; attr; attr = attr->next) {
+- if (!radius_msg_add_attr(msg, attr->type,
+- wpabuf_head(attr->val),
+- wpabuf_len(attr->val))) {
++ struct radius_accept_attr *attr;
++ for (attr = sess->accept_attr; attr->data; attr++) {
++ if (!radius_msg_add_attr(msg, attr->type, attr->data,
++ attr->len)) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ radius_msg_free(msg);
+ return NULL;
+@@ -1211,11 +1247,10 @@ radius_server_macacl(struct radius_serve
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+- struct hostapd_radius_attr *attr;
+- for (attr = sess->accept_attr; attr; attr = attr->next) {
+- if (!radius_msg_add_attr(msg, attr->type,
+- wpabuf_head(attr->val),
+- wpabuf_len(attr->val))) {
++ struct radius_accept_attr *attr;
++ for (attr = sess->accept_attr; attr->data; attr++) {
++ if (!radius_msg_add_attr(msg, attr->type, attr->data,
++ attr->len)) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ radius_msg_free(msg);
+ return NULL;
+@@ -2512,7 +2547,7 @@ static int radius_server_get_eap_user(vo
+ ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+ phase2, user);
+ if (ret == 0 && user) {
+- sess->accept_attr = user->accept_attr;
++ sess->accept_attr = radius_server_copy_attr(user->accept_attr);
+ sess->remediation = user->remediation;
+ sess->macacl = user->macacl;
+ sess->t_c_timestamp = user->t_c_timestamp;
diff --git a/package/network/services/hostapd/patches/991-Fix-OpenWrt-13156.patch b/package/network/services/hostapd/patches/991-Fix-OpenWrt-13156.patch
index 99d800858f55..3f10fb1eef76 100644
--- a/package/network/services/hostapd/patches/991-Fix-OpenWrt-13156.patch
+++ b/package/network/services/hostapd/patches/991-Fix-OpenWrt-13156.patch
@@ -20,7 +20,7 @@ Signed-off-by: Stijn Tintel <stijn at linux-ipv6.be>
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
-@@ -3615,6 +3615,8 @@ int hostapd_remove_iface(struct hapd_int
+@@ -3563,6 +3563,8 @@ int hostapd_remove_iface(struct hapd_int
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc)
{
@@ -29,7 +29,7 @@ Signed-off-by: Stijn Tintel <stijn at linux-ipv6.be>
if (hapd->tkip_countermeasures) {
hostapd_drv_sta_deauth(hapd, sta->addr,
WLAN_REASON_MICHAEL_MIC_FAILURE);
-@@ -3622,10 +3624,16 @@ void hostapd_new_assoc_sta(struct hostap
+@@ -3570,10 +3572,16 @@ void hostapd_new_assoc_sta(struct hostap
}
#ifdef CONFIG_IEEE80211BE
diff --git a/package/network/services/hostapd/src/hostapd/radius.c b/package/network/services/hostapd/src/hostapd/radius.c
new file mode 100644
index 000000000000..362a22c276e3
--- /dev/null
+++ b/package/network/services/hostapd/src/hostapd/radius.c
@@ -0,0 +1,715 @@
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+
+#include "ap/ap_config.h"
+#include "eap_server/eap.h"
+#include "radius/radius.h"
+#include "radius/radius_server.h"
+#include "eap_register.h"
+
+#include <libubox/blobmsg_json.h>
+#include <libubox/blobmsg.h>
+#include <libubox/avl.h>
+#include <libubox/avl-cmp.h>
+#include <libubox/kvlist.h>
+
+#include <sys/stat.h>
+#include <fnmatch.h>
+
+#define VENDOR_ID_WISPR 14122
+#define VENDOR_ATTR_SIZE 6
+
+struct radius_parse_attr_data {
+ unsigned int vendor;
+ u8 type;
+ int size;
+ char format;
+ const char *data;
+};
+
+struct radius_parse_attr_state {
+ struct hostapd_radius_attr *prev;
+ struct hostapd_radius_attr *attr;
+ struct wpabuf *buf;
+ void *attrdata;
+};
+
+struct radius_user_state {
+ struct avl_node node;
+ struct eap_user data;
+};
+
+struct radius_user_data {
+ struct kvlist users;
+ struct avl_tree user_state;
+ struct blob_attr *wildcard;
+};
+
+struct radius_state {
+ struct radius_server_data *radius;
+ struct eap_config eap;
+
+ struct radius_user_data phase1, phase2;
+ const char *user_file;
+ time_t user_file_ts;
+
+ int n_attrs;
+ struct hostapd_radius_attr *attrs;
+};
+
+struct radius_config {
+ struct tls_connection_params tls;
+ struct radius_server_conf radius;
+};
+
+enum {
+ USER_ATTR_PASSWORD,
+ USER_ATTR_HASH,
+ USER_ATTR_SALT,
+ USER_ATTR_METHODS,
+ USER_ATTR_RADIUS,
+ USER_ATTR_VLAN,
+ USER_ATTR_MAX_RATE_UP,
+ USER_ATTR_MAX_RATE_DOWN,
+ __USER_ATTR_MAX
+};
+
+static void radius_tls_event(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ switch (ev) {
+ case TLS_CERT_CHAIN_SUCCESS:
+ wpa_printf(MSG_DEBUG, "radius: remote certificate verification success");
+ break;
+ case TLS_CERT_CHAIN_FAILURE:
+ wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
+ data->cert_fail.reason,
+ data->cert_fail.depth,
+ data->cert_fail.subject,
+ data->cert_fail.reason_txt);
+ break;
+ case TLS_PEER_CERTIFICATE:
+ wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s",
+ data->peer_cert.depth,
+ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
+ data->peer_cert.subject);
+ break;
+ case TLS_ALERT:
+ if (data->alert.is_local)
+ wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s",
+ data->alert.description);
+ else
+ wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s",
+ data->alert.description);
+ break;
+ case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+ /* Not applicable to TLS server */
+ break;
+ }
+}
+
+static void radius_userdata_init(struct radius_user_data *u)
+{
+ kvlist_init(&u->users, kvlist_blob_len);
+ avl_init(&u->user_state, avl_strcmp, false, NULL);
+}
+
+static void radius_userdata_free(struct radius_user_data *u)
+{
+ struct radius_user_state *s, *tmp;
+
+ kvlist_free(&u->users);
+ free(u->wildcard);
+ u->wildcard = NULL;
+ avl_remove_all_elements(&u->user_state, s, node, tmp)
+ free(s);
+}
+
+static void
+radius_userdata_load(struct radius_user_data *u, struct blob_attr *data)
+{
+ enum {
+ USERSTATE_USERS,
+ USERSTATE_WILDCARD,
+ __USERSTATE_MAX,
+ };
+ static const struct blobmsg_policy policy[__USERSTATE_MAX] = {
+ [USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE },
+ [USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY },
+ };
+ struct blob_attr *tb[__USERSTATE_MAX], *cur;
+ int rem;
+
+ if (!data)
+ return;
+
+ blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data));
+
+ blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem)
+ kvlist_set(&u->users, blobmsg_name(cur), cur);
+
+ if (tb[USERSTATE_WILDCARD])
+ u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]);
+}
+
+static void
+load_userfile(struct radius_state *s)
+{
+ enum {
+ USERDATA_PHASE1,
+ USERDATA_PHASE2,
+ __USERDATA_MAX
+ };
+ static const struct blobmsg_policy policy[__USERDATA_MAX] = {
+ [USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE },
+ [USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE },
+ };
+ struct blob_attr *tb[__USERDATA_MAX], *cur;
+ static struct blob_buf b;
+ struct stat st;
+ int rem;
+
+ if (stat(s->user_file, &st))
+ return;
+
+ if (s->user_file_ts == st.st_mtime)
+ return;
+
+ s->user_file_ts = st.st_mtime;
+ radius_userdata_free(&s->phase1);
+ radius_userdata_free(&s->phase2);
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_json_from_file(&b, s->user_file);
+ blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head));
+ radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]);
+ radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]);
+
+ blob_buf_free(&b);
+}
+
+static struct blob_attr *
+radius_user_get(struct radius_user_data *s, const char *name)
+{
+ struct blob_attr *cur;
+ int rem;
+
+ cur = kvlist_get(&s->users, name);
+ if (cur)
+ return cur;
+
+ blobmsg_for_each_attr(cur, s->wildcard, rem) {
+ static const struct blobmsg_policy policy = {
+ "name", BLOBMSG_TYPE_STRING
+ };
+ struct blob_attr *pattern;
+
+ if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
+ continue;
+
+ blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur));
+ if (!name)
+ continue;
+
+ if (!fnmatch(blobmsg_get_string(pattern), name, 0))
+ return cur;
+ }
+
+ return NULL;
+}
+
+static struct radius_parse_attr_data *
+radius_parse_attr(struct blob_attr *attr)
+{
+ static const struct blobmsg_policy policy[4] = {
+ { .type = BLOBMSG_TYPE_INT32 },
+ { .type = BLOBMSG_TYPE_INT32 },
+ { .type = BLOBMSG_TYPE_STRING },
+ { .type = BLOBMSG_TYPE_STRING },
+ };
+ static struct radius_parse_attr_data data;
+ struct blob_attr *tb[4];
+ const char *format;
+
+ blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr));
+
+ if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
+ return NULL;
+
+ format = blobmsg_get_string(tb[2]);
+ if (strlen(format) != 1)
+ return NULL;
+
+ data.vendor = blobmsg_get_u32(tb[0]);
+ data.type = blobmsg_get_u32(tb[1]);
+ data.format = format[0];
+ data.data = blobmsg_get_string(tb[3]);
+ data.size = strlen(data.data);
+
+ switch (data.format) {
+ case 's':
+ break;
+ case 'x':
+ if (data.size & 1)
+ return NULL;
+ data.size /= 2;
+ break;
+ case 'd':
+ data.size = 4;
+ break;
+ default:
+ return NULL;
+ }
+
+ return &data;
+}
+
+static void
+radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size)
+{
+ struct blob_attr *data = tb[USER_ATTR_RADIUS];
+ struct blob_attr *cur;
+ int rem;
+
+ blobmsg_for_each_attr(cur, data, rem) {
+ struct radius_parse_attr_data *data;
+ size_t prev = *attr_size;
+
+ data = radius_parse_attr(cur);
+ if (!data)
+ continue;
+
+ *attr_size += data->size;
+ if (data->vendor)
+ *attr_size += VENDOR_ATTR_SIZE;
+
+ (*n_attr)++;
+ }
+
+ *n_attr += !!tb[USER_ATTR_VLAN] * 3 +
+ !!tb[USER_ATTR_MAX_RATE_UP] +
+ !!tb[USER_ATTR_MAX_RATE_DOWN];
+ *attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) +
+ !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) +
+ !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE);
+}
+
+static void *
+radius_add_attr(struct radius_parse_attr_state *state,
+ u32 vendor, u8 type, u8 len)
+{
+ struct hostapd_radius_attr *attr;
+ struct wpabuf *buf;
+ void *val;
+
+ val = state->attrdata;
+
+ buf = state->buf++;
+ buf->buf = val;
+
+ attr = state->attr++;
+ attr->val = buf;
+ attr->type = type;
+
+ if (state->prev)
+ state->prev->next = attr;
+ state->prev = attr;
+
+ if (vendor) {
+ u8 *vendor_hdr = val + 4;
+
+ WPA_PUT_BE32(val, vendor);
+ vendor_hdr[0] = type;
+ vendor_hdr[1] = len + 2;
+
+ len += VENDOR_ATTR_SIZE;
+ val += VENDOR_ATTR_SIZE;
+ attr->type = RADIUS_ATTR_VENDOR_SPECIFIC;
+ }
+
+ buf->size = buf->used = len;
+ state->attrdata += len;
+
+ return val;
+}
+
+static void
+radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state)
+{
+ struct blob_attr *data = tb[USER_ATTR_RADIUS];
+ struct hostapd_radius_attr *prev = NULL;
+ struct blob_attr *cur;
+ int len, rem;
+ void *val;
+
+ if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) {
+ char buf[5];
+
+ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4);
+ WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN);
+
+ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4);
+ WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802);
+
+ len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur));
+ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len);
+ memcpy(val, buf, len);
+ }
+
+ if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) {
+ val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4);
+ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
+ }
+
+ if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) {
+ val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4);
+ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
+ }
+
+ blobmsg_for_each_attr(cur, data, rem) {
+ struct radius_parse_attr_data *data;
+ void *val;
+ int size;
+
+ data = radius_parse_attr(cur);
+ if (!data)
+ continue;
+
+ val = radius_add_attr(state, data->vendor, data->type, data->size);
+ switch (data->format) {
+ case 's':
+ memcpy(val, data->data, data->size);
+ break;
+ case 'x':
+ hexstr2bin(data->data, val, data->size);
+ break;
+ case 'd':
+ WPA_PUT_BE32(val, atoi(data->data));
+ break;
+ }
+ }
+}
+
+static void
+radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data)
+{
+ struct blob_attr *cur;
+ int rem, n = 0;
+
+ if (!data)
+ return;
+
+ blobmsg_for_each_attr(cur, data, rem) {
+ const char *method;
+
+ if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
+ continue;
+
+ if (n == EAP_MAX_METHODS)
+ break;
+
+ method = blobmsg_get_string(cur);
+ eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor);
+ if (eap->methods[n].vendor == EAP_VENDOR_IETF &&
+ eap->methods[n].method == EAP_TYPE_NONE) {
+ if (!strcmp(method, "TTLS-PAP")) {
+ eap->ttls_auth |= EAP_TTLS_AUTH_PAP;
+ continue;
+ }
+ if (!strcmp(method, "TTLS-CHAP")) {
+ eap->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+ continue;
+ }
+ if (!strcmp(method, "TTLS-MSCHAP")) {
+ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
+ continue;
+ }
+ if (!strcmp(method, "TTLS-MSCHAPV2")) {
+ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
+ continue;
+ }
+ }
+ n++;
+ }
+}
+
+static struct eap_user *
+radius_user_get_state(struct radius_user_data *u, struct blob_attr *data,
+ const char *id)
+{
+ static const struct blobmsg_policy policy[__USER_ATTR_MAX] = {
+ [USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING },
+ [USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING },
+ [USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING },
+ [USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY },
+ [USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY },
+ [USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 },
+ [USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 },
+ [USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 },
+ };
+ struct blob_attr *tb[__USER_ATTR_MAX], *cur;
+ char *password_buf, *salt_buf, *name_buf;
+ struct radius_parse_attr_state astate = {};
+ struct hostapd_radius_attr *attr;
+ struct radius_user_state *state;
+ int pw_len = 0, salt_len = 0;
+ struct eap_user *eap;
+ struct wpabuf *val;
+ size_t attrsize = 0;
+ void *attrdata;
+ int n_attr = 0;
+
+ state = avl_find_element(&u->user_state, id, state, node);
+ if (state)
+ return &state->data;
+
+ blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
+
+ if ((cur = tb[USER_ATTR_SALT]) != NULL)
+ salt_len = strlen(blobmsg_get_string(cur)) / 2;
+ if ((cur = tb[USER_ATTR_HASH]) != NULL)
+ pw_len = strlen(blobmsg_get_string(cur)) / 2;
+ else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
+ pw_len = blobmsg_len(cur) - 1;
+ radius_count_attrs(tb, &n_attr, &attrsize);
+
+ state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1,
+ &password_buf, pw_len,
+ &salt_buf, salt_len,
+ &astate.attr, n_attr * sizeof(*astate.attr),
+ &astate.buf, n_attr * sizeof(*astate.buf),
+ &astate.attrdata, attrsize);
+ eap = &state->data;
+ eap->salt = salt_len ? salt_buf : NULL;
+ eap->salt_len = salt_len;
+ eap->password = pw_len ? password_buf : NULL;
+ eap->password_len = pw_len;
+ eap->force_version = -1;
+
+ if ((cur = tb[USER_ATTR_SALT]) != NULL)
+ hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len);
+ if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
+ memcpy(password_buf, blobmsg_get_string(cur), pw_len);
+ else if ((cur = tb[USER_ATTR_HASH]) != NULL) {
+ hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len);
+ eap->password_hash = 1;
+ }
+ radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]);
+
+ if (n_attr > 0) {
+ cur = tb[USER_ATTR_RADIUS];
+ eap->accept_attr = astate.attr;
+ radius_parse_attrs(tb, &astate);
+ }
+
+ state->node.key = strcpy(name_buf, id);
+ avl_insert(&u->user_state, &state->node);
+
+ return &state->data;
+
+free:
+ free(state);
+ return NULL;
+}
+
+static int radius_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ struct radius_state *s = ctx;
+ struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1;
+ struct blob_attr *entry;
+ struct eap_user *data;
+ char *id;
+
+ if (identity_len > 512)
+ return -1;
+
+ load_userfile(s);
+
+ id = alloca(identity_len + 1);
+ memcpy(id, identity, identity_len);
+ id[identity_len] = 0;
+
+ entry = radius_user_get(u, id);
+ if (!entry)
+ return -1;
+
+ if (!user)
+ return 0;
+
+ data = radius_user_get_state(u, entry, id);
+ if (!data)
+ return -1;
+
+ *user = *data;
+ if (user->password_len > 0)
+ user->password = os_memdup(user->password, user->password_len);
+ if (user->salt_len > 0)
+ user->salt = os_memdup(user->salt, user->salt_len);
+ user->phase2 = phase2;
+
+ return 0;
+}
+
+static int radius_setup(struct radius_state *s, struct radius_config *c)
+{
+ struct eap_config *eap = &s->eap;
+ struct tls_config conf = {
+ .event_cb = radius_tls_event,
+ .tls_flags = TLS_CONN_DISABLE_TLSv1_3,
+ .cb_ctx = s,
+ };
+
+ eap->eap_server = 1;
+ eap->max_auth_rounds = 100;
+ eap->max_auth_rounds_short = 50;
+ eap->ssl_ctx = tls_init(&conf);
+ if (!eap->ssl_ctx) {
+ wpa_printf(MSG_INFO, "TLS init failed\n");
+ return 1;
+ }
+
+ if (tls_global_set_params(eap->ssl_ctx, &c->tls)) {
+ wpa_printf(MSG_INFO, "failed to set TLS parameters\n");
+ return 1;
+ }
+
+ c->radius.eap_cfg = eap;
+ c->radius.conf_ctx = s;
+ c->radius.get_eap_user = radius_get_eap_user;
+ s->radius = radius_server_init(&c->radius);
+ if (!s->radius) {
+ wpa_printf(MSG_INFO, "failed to initialize radius server\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int radius_init(struct radius_state *s)
+{
+ memset(s, 0, sizeof(*s));
+ radius_userdata_init(&s->phase1);
+ radius_userdata_init(&s->phase2);
+}
+
+static void radius_deinit(struct radius_state *s)
+{
+ if (s->radius)
+ radius_server_deinit(s->radius);
+
+ if (s->eap.ssl_ctx)
+ tls_deinit(s->eap.ssl_ctx);
+
+ radius_userdata_free(&s->phase1);
+ radius_userdata_free(&s->phase2);
+}
+
+static int usage(const char *progname)
+{
+ fprintf(stderr, "Usage: %s <options>\n",
+ progname);
+}
+
+int radius_main(int argc, char **argv)
+{
+ static struct radius_state state = {};
+ static struct radius_config config = {};
+ const char *progname = argv[0];
+ int ret = 0;
+ int ch;
+
+ wpa_debug_setup_stdout();
+ wpa_debug_level = 0;
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ return 1;
+ }
+
+ eap_server_register_methods();
+ radius_init(&state);
+
+ while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) {
+ switch (ch) {
+ case '6':
+ config.radius.ipv6 = 1;
+ break;
+ case 'C':
+ config.tls.ca_cert = optarg;
+ break;
+ case 'c':
+ if (config.tls.client_cert2)
+ return usage(progname);
+
+ if (config.tls.client_cert)
+ config.tls.client_cert2 = optarg;
+ else
+ config.tls.client_cert = optarg;
+ break;
+ case 'd':
+ config.tls.dh_file = optarg;
+ break;
+ case 'i':
+ state.eap.server_id = optarg;
+ state.eap.server_id_len = strlen(optarg);
+ break;
+ case 'k':
+ if (config.tls.private_key2)
+ return usage(progname);
+
+ if (config.tls.private_key)
+ config.tls.private_key2 = optarg;
+ else
+ config.tls.private_key = optarg;
+ break;
+ case 'K':
+ if (config.tls.private_key_passwd2)
+ return usage(progname);
+
+ if (config.tls.private_key_passwd)
+ config.tls.private_key_passwd2 = optarg;
+ else
+ config.tls.private_key_passwd = optarg;
+ break;
+ case 'p':
+ config.radius.auth_port = atoi(optarg);
+ break;
+ case 'P':
+ config.radius.acct_port = atoi(optarg);
+ break;
+ case 's':
+ config.radius.client_file = optarg;
+ break;
+ case 'u':
+ state.user_file = optarg;
+ break;
+ default:
+ return usage(progname);
+ }
+ }
+
+ if (!config.tls.client_cert || !config.tls.private_key ||
+ !config.radius.client_file || !state.eap.server_id ||
+ !state.user_file) {
+ wpa_printf(MSG_INFO, "missing options\n");
+ goto out;
+ }
+
+ ret = radius_setup(&state, &config);
+ if (ret)
+ goto out;
+
+ load_userfile(&state);
+ eloop_run();
+
+out:
+ radius_deinit(&state);
+ os_program_deinit();
+
+ return ret;
+}
diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c
index ddd86447eb13..6ff2257c329b 100644
--- a/package/network/services/hostapd/src/src/ap/ubus.c
+++ b/package/network/services/hostapd/src/src/ap/ubus.c
@@ -29,11 +29,6 @@ static struct ubus_context *ctx;
static struct blob_buf b;
static int ctx_ref;
-static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj)
-{
- return container_of(obj, struct hapd_interfaces, ubus);
-}
-
static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
{
return container_of(obj, struct hostapd_data, ubus.obj);
@@ -44,12 +39,6 @@ struct ubus_banned_client {
u8 addr[ETH_ALEN];
};
-static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx)
-{
- struct ubus_context *ctx = eloop_ctx;
- ubus_handle_event(ctx);
-}
-
static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
{
if (ubus_reconnect(ctx, NULL)) {
@@ -57,12 +46,12 @@ static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
return;
}
- eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+ ubus_add_uloop(ctx);
}
static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
{
- eloop_unregister_read_sock(ctx->sock.fd);
+ uloop_fd_delete(&ctx->sock);
eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
}
@@ -71,12 +60,14 @@ static bool hostapd_ubus_init(void)
if (ctx)
return true;
+ eloop_add_uloop();
ctx = ubus_connect(NULL);
if (!ctx)
return false;
ctx->connection_lost = hostapd_ubus_connection_lost;
- eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+ ubus_add_uloop(ctx);
+
return true;
}
@@ -94,7 +85,7 @@ static void hostapd_ubus_ref_dec(void)
if (ctx_ref)
return;
- eloop_unregister_read_sock(ctx->sock.fd);
+ uloop_fd_delete(&ctx->sock);
ubus_free(ctx);
ctx = NULL;
}
@@ -127,38 +118,6 @@ static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *ev
free(event_type);
}
-static void hostapd_send_procd_event(char *bssname, char *event)
-{
- char *name, *s;
- uint32_t id;
- void *v;
-
- if (!ctx || ubus_lookup_id(ctx, "service", &id))
- return;
-
- if (asprintf(&name, "hostapd.%s.%s", bssname, event) < 0)
- return;
-
- blob_buf_init(&b, 0);
-
- s = blobmsg_alloc_string_buffer(&b, "type", strlen(name) + 1);
- sprintf(s, "%s", name);
- blobmsg_add_string_buffer(&b);
-
- v = blobmsg_open_table(&b, "data");
- blobmsg_close_table(&b, v);
-
- ubus_invoke(ctx, id, "event", b.head, NULL, NULL, 1000);
-
- free(name);
-}
-
-static void hostapd_send_shared_event(struct ubus_object *obj, char *bssname, char *event)
-{
- hostapd_send_procd_event(bssname, event);
- hostapd_notify_ubus(obj, bssname, event);
-}
-
static void
hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
{
@@ -203,10 +162,8 @@ hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
struct blob_attr *msg)
{
struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
- int ret = hostapd_reload_config(hapd->iface, 1);
- hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "reload");
- return ret;
+ return hostapd_reload_config(hapd->iface);
}
@@ -687,68 +644,6 @@ enum {
__CONFIG_MAX
};
-static const struct blobmsg_policy config_add_policy[__CONFIG_MAX] = {
- [CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
- [CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
-};
-
-static int
-hostapd_config_add(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
-{
- struct blob_attr *tb[__CONFIG_MAX];
- struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
- char buf[128];
-
- blobmsg_parse(config_add_policy, __CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
-
- if (!tb[CONFIG_FILE] || !tb[CONFIG_IFACE])
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- snprintf(buf, sizeof(buf), "bss_config=%s:%s",
- blobmsg_get_string(tb[CONFIG_IFACE]),
- blobmsg_get_string(tb[CONFIG_FILE]));
-
- if (hostapd_add_iface(interfaces, buf))
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- blob_buf_init(&b, 0);
- blobmsg_add_u32(&b, "pid", getpid());
- ubus_send_reply(ctx, req, b.head);
-
- return UBUS_STATUS_OK;
-}
-
-enum {
- CONFIG_REM_IFACE,
- __CONFIG_REM_MAX
-};
-
-static const struct blobmsg_policy config_remove_policy[__CONFIG_REM_MAX] = {
- [CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
-};
-
-static int
-hostapd_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
-{
- struct blob_attr *tb[__CONFIG_REM_MAX];
- struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
- char buf[128];
-
- blobmsg_parse(config_remove_policy, __CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
-
- if (!tb[CONFIG_REM_IFACE])
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- if (hostapd_remove_iface(interfaces, blobmsg_get_string(tb[CONFIG_REM_IFACE])))
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- return UBUS_STATUS_OK;
-}
-
enum {
CSA_FREQ,
CSA_BCN_COUNT,
@@ -1669,10 +1564,61 @@ hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
}
#endif
+#ifdef CONFIG_TAXONOMY
+static const struct blobmsg_policy addr_policy[] = {
+ { "address", BLOBMSG_TYPE_STRING }
+};
+
+static bool
+hostapd_add_b64_data(const char *name, const struct wpabuf *buf)
+{
+ char *str;
+
+ if (!buf)
+ return false;
+
+ str = blobmsg_alloc_string_buffer(&b, name, B64_ENCODE_LEN(wpabuf_len(buf)));
+ b64_encode(wpabuf_head(buf), wpabuf_len(buf), str, B64_ENCODE_LEN(wpabuf_len(buf)));
+ blobmsg_add_string_buffer(&b);
+
+ return true;
+}
+
+static int
+hostapd_bss_get_sta_ies(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct blob_attr *tb;
+ struct sta_info *sta;
+ u8 addr[ETH_ALEN];
+
+ blobmsg_parse(addr_policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
+
+ if (!tb || hwaddr_aton(blobmsg_data(tb), addr))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || (!sta->probe_ie_taxonomy && !sta->assoc_ie_taxonomy))
+ return UBUS_STATUS_NOT_FOUND;
+
+ blob_buf_init(&b, 0);
+ hostapd_add_b64_data("probe_ie", sta->probe_ie_taxonomy);
+ hostapd_add_b64_data("assoc_ie", sta->assoc_ie_taxonomy);
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+#endif
+
static const struct ubus_method bss_methods[] = {
UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
+#ifdef CONFIG_TAXONOMY
+ UBUS_METHOD("get_sta_ies", hostapd_bss_get_sta_ies, addr_policy),
+#endif
UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
#ifdef CONFIG_AIRTIME_POLICY
@@ -1734,8 +1680,6 @@ void hostapd_ubus_add_bss(struct hostapd_data *hapd)
obj->n_methods = bss_object_type.n_methods;
ret = ubus_add_object(ctx, obj);
hostapd_ubus_ref_inc();
-
- hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "add");
}
void hostapd_ubus_free_bss(struct hostapd_data *hapd)
@@ -1751,8 +1695,6 @@ void hostapd_ubus_free_bss(struct hostapd_data *hapd)
if (!ctx)
return;
- hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "remove");
-
if (obj->id) {
ubus_remove_object(ctx, obj);
hostapd_ubus_ref_dec();
@@ -1798,47 +1740,6 @@ void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vl
hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
}
-static const struct ubus_method daemon_methods[] = {
- UBUS_METHOD("config_add", hostapd_config_add, config_add_policy),
- UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy),
-};
-
-static struct ubus_object_type daemon_object_type =
- UBUS_OBJECT_TYPE("hostapd", daemon_methods);
-
-void hostapd_ubus_add(struct hapd_interfaces *interfaces)
-{
- struct ubus_object *obj = &interfaces->ubus;
- int ret;
-
- if (!hostapd_ubus_init())
- return;
-
- obj->name = strdup("hostapd");
-
- obj->type = &daemon_object_type;
- obj->methods = daemon_object_type.methods;
- obj->n_methods = daemon_object_type.n_methods;
- ret = ubus_add_object(ctx, obj);
- hostapd_ubus_ref_inc();
-}
-
-void hostapd_ubus_free(struct hapd_interfaces *interfaces)
-{
- struct ubus_object *obj = &interfaces->ubus;
- char *name = (char *) obj->name;
-
- if (!ctx)
- return;
-
- if (obj->id) {
- ubus_remove_object(ctx, obj);
- hostapd_ubus_ref_dec();
- }
-
- free(name);
-}
-
struct ubus_event_req {
struct ubus_notify_request nreq;
int resp;
diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c
new file mode 100644
index 000000000000..080fe48b11ac
--- /dev/null
+++ b/package/network/services/hostapd/src/src/ap/ucode.c
@@ -0,0 +1,553 @@
+#include <sys/un.h>
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "hostapd.h"
+#include "beacon.h"
+#include "hw_features.h"
+#include "ap_drv_ops.h"
+#include "dfs.h"
+#include <libubox/uloop.h>
+
+static uc_resource_type_t *global_type, *bss_type, *iface_type;
+static struct hapd_interfaces *interfaces;
+static uc_value_t *global, *bss_registry, *iface_registry;
+static uc_vm_t *vm;
+
+static uc_value_t *
+hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
+{
+ uc_value_t *val;
+
+ if (hapd->ucode.idx)
+ return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
+
+ val = uc_resource_new(bss_type, hapd);
+ hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
+
+ return val;
+}
+
+static uc_value_t *
+hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
+{
+ uc_value_t *val;
+
+ if (hapd->ucode.idx)
+ return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
+
+ val = uc_resource_new(iface_type, hapd);
+ hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
+
+ return val;
+}
+
+static void
+hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
+{
+ uc_value_t *list;
+ int i;
+
+ list = ucv_array_new(vm);
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+ uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
+
+ ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
+ ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
+ }
+ ucv_object_add(if_bss, iface->phy, ucv_get(list));
+}
+
+static void
+hostapd_ucode_update_interfaces(void)
+{
+ uc_value_t *ifs = ucv_object_new(vm);
+ uc_value_t *if_bss = ucv_array_new(vm);
+ uc_value_t *bss = ucv_object_new(vm);
+ int i;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_iface *iface = interfaces->iface[i];
+
+ ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
+ hostapd_ucode_update_bss_list(iface, if_bss, bss);
+ }
+
+ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
+ ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
+ ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
+ ucv_gc(vm);
+}
+
+static uc_value_t *
+uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *iface = uc_fn_arg(0);
+ int ret;
+
+ if (ucv_type(iface) != UC_STRING)
+ return ucv_int64_new(-1);
+
+ ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
+ hostapd_ucode_update_interfaces();
+
+ return ucv_int64_new(ret);
+}
+
+static uc_value_t *
+uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *iface = uc_fn_arg(0);
+
+ if (ucv_type(iface) != UC_STRING)
+ return NULL;
+
+ hostapd_remove_iface(interfaces, ucv_string_get(iface));
+ hostapd_ucode_update_interfaces();
+
+ return NULL;
+}
+
+static uc_value_t *
+uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+ struct hostapd_bss_config *old_bss;
+ struct hostapd_iface *iface;
+ struct hostapd_config *conf;
+ uc_value_t *file = uc_fn_arg(0);
+ uc_value_t *index = uc_fn_arg(1);
+ unsigned int i, idx = 0;
+ int ret = -1;
+
+ if (!hapd || ucv_type(file) != UC_STRING)
+ goto out;
+
+ if (ucv_type(index) == UC_INTEGER)
+ idx = ucv_int64_get(index);
+
+ iface = hapd->iface;
+ conf = interfaces->config_read_cb(ucv_string_get(file));
+ if (!conf || idx > conf->num_bss || !conf->bss[idx])
+ goto out;
+
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_drv_stop_ap(hapd);
+ hostapd_free_hapd_data(hapd);
+
+ old_bss = hapd->conf;
+ for (i = 0; i < iface->conf->num_bss; i++)
+ if (iface->conf->bss[i] == hapd->conf)
+ iface->conf->bss[i] = conf->bss[idx];
+ hapd->conf = conf->bss[idx];
+ conf->bss[idx] = old_bss;
+ hostapd_config_free(conf);
+
+ hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->mbssid);
+
+ ret = 0;
+
+out:
+ return ucv_int64_new(ret);
+}
+
+static void
+hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
+ struct hostapd_bss_config *conf)
+{
+ int i;
+
+ for (i = 0; i < iconf->num_bss; i++)
+ if (iconf->bss[i] == conf)
+ break;
+
+ if (i == iconf->num_bss)
+ return;
+
+ for (i++; i < iconf->num_bss; i++)
+ iconf->bss[i - 1] = iconf->bss[i];
+ iconf->num_bss--;
+}
+
+
+static uc_value_t *
+uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+ struct hostapd_iface *iface;
+ int i, idx;
+
+ if (!hapd || hapd == hapd->iface->bss[0])
+ return NULL;
+
+ iface = hapd->iface;
+ for (idx = 0; idx < iface->num_bss; idx++)
+ if (iface->bss[idx] == hapd)
+ break;
+
+ if (idx == iface->num_bss)
+ return NULL;
+
+ for (i = idx + 1; i < iface->num_bss; i++)
+ iface->bss[i - 1] = iface->bss[i];
+ iface->num_bss--;
+
+ hostapd_drv_stop_ap(hapd);
+ hostapd_bss_deinit(hapd);
+ hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
+ hostapd_config_free_bss(hapd->conf);
+ os_free(hapd);
+
+ hostapd_ucode_update_interfaces();
+ ucv_gc(vm);
+
+ return NULL;
+}
+
+static uc_value_t *
+uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ struct hostapd_bss_config *bss;
+ struct hostapd_config *conf;
+ struct hostapd_data *hapd;
+ uc_value_t *file = uc_fn_arg(0);
+ uc_value_t *index = uc_fn_arg(1);
+ unsigned int idx = 0;
+ uc_value_t *ret = NULL;
+
+ if (!iface || ucv_type(file) != UC_STRING)
+ goto out;
+
+ if (ucv_type(index) == UC_INTEGER)
+ idx = ucv_int64_get(index);
+
+ conf = interfaces->config_read_cb(ucv_string_get(file));
+ if (!conf || idx > conf->num_bss || !conf->bss[idx])
+ goto out;
+
+ bss = conf->bss[idx];
+ hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+ if (!hapd)
+ goto out;
+
+ hapd->driver = iface->bss[0]->driver;
+ hapd->drv_priv = iface->bss[0]->drv_priv;
+ if (interfaces->ctrl_iface_init &&
+ interfaces->ctrl_iface_init(hapd) < 0)
+ goto free_hapd;
+
+ if (iface->state == HAPD_IFACE_ENABLED &&
+ hostapd_setup_bss(hapd, -1, true))
+ goto deinit_ctrl;
+
+ iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+ sizeof(*iface->bss));
+ iface->bss[iface->num_bss++] = hapd;
+
+ iface->conf->bss = os_realloc_array(iface->conf->bss,
+ iface->conf->num_bss + 1,
+ sizeof(*iface->conf->bss));
+ iface->conf->bss[iface->conf->num_bss] = bss;
+ conf->bss[idx] = NULL;
+ ret = hostapd_ucode_bss_get_uval(hapd);
+ hostapd_ucode_update_interfaces();
+ goto out;
+
+deinit_ctrl:
+ if (interfaces->ctrl_iface_deinit)
+ interfaces->ctrl_iface_deinit(hapd);
+free_hapd:
+ hostapd_free_hapd_data(hapd);
+ os_free(hapd);
+out:
+ hostapd_config_free(conf);
+ return ret;
+}
+
+static uc_value_t *
+uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+ uc_value_t *arg = uc_fn_arg(0);
+ struct sockaddr_storage from = {};
+ static char reply[4096];
+ int reply_len;
+
+ if (!hapd || !interfaces->ctrl_iface_recv ||
+ ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
+ reply, sizeof(reply),
+ &from, sizeof(from));
+ if (reply_len < 0)
+ return NULL;
+
+ if (reply_len && reply[reply_len - 1] == '\n')
+ reply_len--;
+
+ return ucv_string_new_length(reply, reply_len);
+}
+
+static uc_value_t *
+uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ int i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ hostapd_drv_stop_ap(hapd);
+ hapd->started = 0;
+ }
+}
+
+static uc_value_t *
+uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ uc_value_t *info = uc_fn_arg(0);
+ struct hostapd_config *conf;
+ uint64_t intval;
+ int i;
+
+ if (!iface)
+ return NULL;
+
+ if (!info)
+ goto out;
+
+ if (ucv_type(info) != UC_OBJECT)
+ return NULL;
+
+ conf = iface->conf;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "op_class", NULL))) && !errno)
+ conf->op_class = intval;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "hw_mode", NULL))) && !errno)
+ conf->hw_mode = intval;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "channel", NULL))) && !errno)
+ conf->channel = intval;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
+ conf->secondary_channel = intval;
+#ifdef CONFIG_IEEE80211AC
+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL))) && !errno) {
+ conf->vht_oper_centr_freq_seg0_idx = intval;
+#ifdef CONFIG_IEEE80211AX
+ conf->he_oper_centr_freq_seg0_idx = intval;
+#endif
+#ifdef CONFIG_IEEE80211BE
+ conf->eht_oper_centr_freq_seg0_idx = intval;
+#endif
+ }
+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL))) && !errno) {
+ conf->vht_oper_centr_freq_seg1_idx = intval;
+#ifdef CONFIG_IEEE80211AX
+ conf->he_oper_centr_freq_seg1_idx = intval;
+#endif
+#ifdef CONFIG_IEEE80211BE
+ conf->eht_oper_centr_freq_seg1_idx = intval;
+#endif
+ }
+ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
+ if (!errno) {
+ conf->vht_oper_chwidth = intval;
+#ifdef CONFIG_IEEE80211AX
+ conf->he_oper_chwidth = intval;
+#endif
+#ifdef CONFIG_IEEE80211BE
+ conf->eht_oper_chwidth = intval;
+#endif
+ }
+#endif
+
+out:
+ if (conf->channel)
+ iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
+
+ if (hostapd_is_dfs_required(iface) && !hostapd_is_dfs_chan_available(iface)) {
+ wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ return ucv_boolean_new(true);
+ }
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+ int ret;
+
+ hapd->started = 1;
+ hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
+ conf->channel,
+ conf->enable_edmg,
+ conf->edmg_channel,
+ conf->ieee80211n,
+ conf->ieee80211ac,
+ conf->ieee80211ax,
+ conf->ieee80211be,
+ conf->secondary_channel,
+ hostapd_get_oper_chwidth(conf),
+ hostapd_get_oper_centr_freq_seg0_idx(conf),
+ hostapd_get_oper_centr_freq_seg1_idx(conf));
+
+ ieee802_11_set_beacon(hapd);
+ }
+
+ return ucv_boolean_new(true);
+}
+
+static uc_value_t *
+uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+ uc_value_t *info = uc_fn_arg(0);
+ struct hostapd_config *conf;
+ struct csa_settings csa = {};
+ uint64_t intval;
+ int i, ret = 0;
+
+ if (!iface || ucv_type(info) != UC_OBJECT)
+ return NULL;
+
+ conf = iface->conf;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
+ csa.cs_count = intval;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
+ csa.freq_params.sec_channel_offset = intval;
+
+ csa.freq_params.ht_enabled = conf->ieee80211n;
+ csa.freq_params.vht_enabled = conf->ieee80211ac;
+ csa.freq_params.he_enabled = conf->ieee80211ax;
+#ifdef CONFIG_IEEE80211BE
+ csa.freq_params.eht_enabled = conf->ieee80211be;
+#endif
+ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
+ if (errno)
+ intval = hostapd_get_oper_chwidth(conf);
+ if (intval)
+ csa.freq_params.bandwidth = 40 << intval;
+ else
+ csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
+
+ if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
+ csa.freq_params.freq = intval;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
+ csa.freq_params.center_freq1 = intval;
+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
+ csa.freq_params.center_freq2 = intval;
+
+ for (i = 0; i < iface->num_bss; i++)
+ ret = hostapd_switch_channel(iface->bss[i], &csa);
+
+ return ucv_boolean_new(!ret);
+}
+
+int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+{
+ static const uc_function_list_t global_fns[] = {
+ { "printf", uc_wpa_printf },
+ { "getpid", uc_wpa_getpid },
+ { "sha1", uc_wpa_sha1 },
+ { "freq_info", uc_wpa_freq_info },
+ { "add_iface", uc_hostapd_add_iface },
+ { "remove_iface", uc_hostapd_remove_iface },
+ };
+ static const uc_function_list_t bss_fns[] = {
+ { "ctrl", uc_hostapd_bss_ctrl },
+ { "set_config", uc_hostapd_bss_set_config },
+ { "delete", uc_hostapd_bss_delete },
+ };
+ static const uc_function_list_t iface_fns[] = {
+ { "add_bss", uc_hostapd_iface_add_bss },
+ { "stop", uc_hostapd_iface_stop },
+ { "start", uc_hostapd_iface_start },
+ { "switch_channel", uc_hostapd_iface_switch_channel },
+ };
+ uc_value_t *data, *proto;
+
+ interfaces = ifaces;
+ vm = wpa_ucode_create_vm();
+
+ global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
+ bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
+ iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
+
+ bss_registry = ucv_array_new(vm);
+ uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
+
+ iface_registry = ucv_array_new(vm);
+ uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
+
+ global = wpa_ucode_global_init("hostapd", global_type);
+
+ if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
+ goto free_vm;
+ ucv_gc(vm);
+
+ return 0;
+
+free_vm:
+ wpa_ucode_free_vm();
+ return -1;
+}
+
+void hostapd_ucode_free(void)
+{
+ if (wpa_ucode_call_prepare("shutdown") == 0)
+ ucv_put(wpa_ucode_call(0));
+ wpa_ucode_free_vm();
+}
+
+void hostapd_ucode_free_iface(struct hostapd_iface *iface)
+{
+ wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
+}
+
+void hostapd_ucode_add_bss(struct hostapd_data *hapd)
+{
+ uc_value_t *val;
+
+ if (wpa_ucode_call_prepare("bss_add"))
+ return;
+
+ val = hostapd_ucode_bss_get_uval(hapd);
+ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
+ uc_value_push(ucv_get(val));
+ ucv_put(wpa_ucode_call(2));
+ ucv_gc(vm);
+}
+
+void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
+{
+ uc_value_t *val;
+
+ if (wpa_ucode_call_prepare("bss_reload"))
+ return;
+
+ val = hostapd_ucode_bss_get_uval(hapd);
+ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
+ uc_value_push(ucv_get(val));
+ ucv_put(wpa_ucode_call(2));
+ ucv_gc(vm);
+}
+
+void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+{
+ uc_value_t *val;
+
+ val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
+ if (!val)
+ return;
+
+ hapd->ucode.idx = 0;
+ if (wpa_ucode_call_prepare("bss_remove"))
+ return;
+
+ uc_value_push(ucv_string_new(hapd->conf->iface));
+ uc_value_push(ucv_get(val));
+ ucv_put(wpa_ucode_call(2));
+ ucv_gc(vm);
+}
diff --git a/package/network/services/hostapd/src/src/ap/ucode.h b/package/network/services/hostapd/src/src/ap/ucode.h
new file mode 100644
index 000000000000..d00b787169d1
--- /dev/null
+++ b/package/network/services/hostapd/src/src/ap/ucode.h
@@ -0,0 +1,54 @@
+#ifndef __HOSTAPD_AP_UCODE_H
+#define __HOSTAPD_AP_UCODE_H
+
+#include "utils/ucode.h"
+
+struct hostapd_data;
+
+struct hostapd_ucode_bss {
+#ifdef UCODE_SUPPORT
+ int idx;
+#endif
+};
+
+struct hostapd_ucode_iface {
+#ifdef UCODE_SUPPORT
+ int idx;
+#endif
+};
+
+#ifdef UCODE_SUPPORT
+
+int hostapd_ucode_init(struct hapd_interfaces *ifaces);
+
+void hostapd_ucode_free(void);
+void hostapd_ucode_free_iface(struct hostapd_iface *iface);
+void hostapd_ucode_add_bss(struct hostapd_data *hapd);
+void hostapd_ucode_free_bss(struct hostapd_data *hapd);
+void hostapd_ucode_reload_bss(struct hostapd_data *hapd);
+
+#else
+
+static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+{
+ return -EINVAL;
+}
+static inline void hostapd_ucode_free(void)
+{
+}
+static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface)
+{
+}
+static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
+{
+}
+static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd)
+{
+}
+static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+{
+}
+
+#endif
+
+#endif
diff --git a/package/network/services/hostapd/src/src/utils/ucode.c b/package/network/services/hostapd/src/src/utils/ucode.c
new file mode 100644
index 000000000000..44169f0bf0a6
--- /dev/null
+++ b/package/network/services/hostapd/src/src/utils/ucode.c
@@ -0,0 +1,326 @@
+#include <unistd.h>
+#include "ucode.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "common/ieee802_11_common.h"
+#include <libubox/uloop.h>
+#include <ucode/compiler.h>
+
+static uc_value_t *registry;
+static uc_vm_t vm;
+static struct uloop_timeout gc_timer;
+
+static void uc_gc_timer(struct uloop_timeout *timeout)
+{
+ ucv_gc(&vm);
+}
+
+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *level = uc_fn_arg(0);
+ uc_value_t *ret, **args;
+ uc_cfn_ptr_t _sprintf;
+ int l = MSG_INFO;
+ int i, start = 0;
+
+ _sprintf = uc_stdlib_function("sprintf");
+ if (!sprintf)
+ return NULL;
+
+ if (ucv_type(level) == UC_INTEGER) {
+ l = ucv_int64_get(level);
+ start++;
+ }
+
+ if (nargs <= start)
+ return NULL;
+
+ ret = _sprintf(vm, nargs - start);
+ if (ucv_type(ret) != UC_STRING)
+ return NULL;
+
+ wpa_printf(l, "%s", ucv_string_get(ret));
+ ucv_put(ret);
+
+ return NULL;
+}
+
+uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *freq = uc_fn_arg(0);
+ uc_value_t *sec = uc_fn_arg(1);
+ int width = ucv_uint64_get(uc_fn_arg(2));
+ int freq_val, center_idx, center_ofs;
+ enum oper_chan_width chanwidth;
+ enum hostapd_hw_mode hw_mode;
+ u8 op_class, channel, tmp_channel;
+ const char *modestr;
+ int sec_channel = 0;
+ uc_value_t *ret;
+
+ if (ucv_type(freq) != UC_INTEGER)
+ return NULL;
+
+ freq_val = ucv_int64_get(freq);
+ if (ucv_type(sec) == UC_INTEGER)
+ sec_channel = ucv_int64_get(sec);
+ else if (sec)
+ return NULL;
+ else if (freq_val > 4000)
+ sec_channel = (freq_val / 20) & 1 ? 1 : -1;
+ else
+ sec_channel = freq_val < 2442 ? 1 : -1;
+
+ if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
+ return NULL;
+
+ switch (width) {
+ case 0:
+ chanwidth = CONF_OPER_CHWIDTH_USE_HT;
+ break;
+ case 1:
+ chanwidth = CONF_OPER_CHWIDTH_80MHZ;
+ break;
+ case 2:
+ chanwidth = CONF_OPER_CHWIDTH_160MHZ;
+ break;
+ default:
+ return NULL;
+ }
+
+ hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
+ chanwidth, &op_class, &channel);
+ switch (hw_mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ modestr = "b";
+ break;
+ case HOSTAPD_MODE_IEEE80211G:
+ modestr = "g";
+ break;
+ case HOSTAPD_MODE_IEEE80211A:
+ modestr = "a";
+ break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ modestr = "ad";
+ break;
+ default:
+ return NULL;
+ }
+
+ ret = ucv_object_new(vm);
+ ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
+ ucv_object_add(ret, "channel", ucv_int64_new(channel));
+ ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
+ ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
+ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
+ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
+
+ if (!sec_channel)
+ return ret;
+
+ if (freq_val >= 5900)
+ center_ofs = 0;
+ else if (freq_val >= 5745)
+ center_ofs = 20;
+ else
+ center_ofs = 35;
+ tmp_channel = channel - center_ofs;
+ tmp_channel &= ~((8 << width) - 1);
+ center_idx = tmp_channel + center_ofs + (4 << width) - 1;
+
+ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
+ center_idx = (center_idx - channel) * 5 + freq_val;
+ ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
+
+out:
+ return ret;
+}
+
+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
+{
+ return ucv_int64_new(getpid());
+}
+
+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
+{
+ u8 hash[SHA1_MAC_LEN];
+ char hash_hex[2 * ARRAY_SIZE(hash) + 1];
+ uc_value_t *val;
+ size_t *lens;
+ const u8 **args;
+ int i;
+
+ if (!nargs)
+ return NULL;
+
+ args = alloca(nargs * sizeof(*args));
+ lens = alloca(nargs * sizeof(*lens));
+ for (i = 0; i < nargs; i++) {
+ val = uc_fn_arg(i);
+ if (ucv_type(val) != UC_STRING)
+ return NULL;
+
+ args[i] = ucv_string_get(val);
+ lens[i] = ucv_string_length(val);
+ }
+
+ if (sha1_vector(nargs, args, lens, hash))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(hash); i++)
+ sprintf(hash_hex + 2 * i, "%02x", hash[i]);
+
+ return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
+}
+
+uc_vm_t *wpa_ucode_create_vm(void)
+{
+ static uc_parse_config_t config = {
+ .strict_declarations = true,
+ .lstrip_blocks = true,
+ .trim_blocks = true,
+ .raw_mode = true
+ };
+
+ uc_search_path_init(&config.module_search_path);
+ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
+ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
+
+ uc_vm_init(&vm, &config);
+
+ uc_stdlib_load(uc_vm_scope_get(&vm));
+ eloop_add_uloop();
+ gc_timer.cb = uc_gc_timer;
+
+ return &vm;
+}
+
+int wpa_ucode_run(const char *script)
+{
+ uc_source_t *source;
+ uc_program_t *prog;
+ uc_value_t *ops;
+ char *err;
+ int ret;
+
+ source = uc_source_new_file(script);
+ if (!source)
+ return -1;
+
+ prog = uc_compile(vm.config, source, &err);
+ uc_source_put(source);
+ if (!prog) {
+ wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
+ return -1;
+ }
+
+ ret = uc_vm_execute(&vm, prog, &ops);
+ uc_program_put(prog);
+ if (ret || !ops)
+ return -1;
+
+ registry = ucv_array_new(&vm);
+ uc_vm_registry_set(&vm, "hostap.registry", registry);
+ ucv_array_set(registry, 0, ucv_get(ops));
+
+ return 0;
+}
+
+int wpa_ucode_call_prepare(const char *fname)
+{
+ uc_value_t *obj, *func;
+
+ if (!registry)
+ return -1;
+
+ obj = ucv_array_get(registry, 0);
+ if (!obj)
+ return -1;
+
+ func = ucv_object_get(obj, fname, NULL);
+ if (!ucv_is_callable(func))
+ return -1;
+
+ uc_vm_stack_push(&vm, ucv_get(obj));
+ uc_vm_stack_push(&vm, ucv_get(func));
+
+ return 0;
+}
+
+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
+{
+ uc_value_t *global = uc_resource_new(global_type, NULL);
+ uc_value_t *proto;
+
+ uc_vm_registry_set(&vm, "hostap.global", global);
+ proto = ucv_prototype_get(global);
+ ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
+
+#define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
+ ADD_CONST(MSG_EXCESSIVE);
+ ADD_CONST(MSG_MSGDUMP);
+ ADD_CONST(MSG_DEBUG);
+ ADD_CONST(MSG_INFO);
+ ADD_CONST(MSG_WARNING);
+ ADD_CONST(MSG_ERROR);
+#undef ADD_CONST
+
+ ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
+
+ return global;
+}
+
+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
+{
+ uc_value_t *data;
+ int i = 0;
+
+ while (ucv_array_get(reg, i))
+ i++;
+
+ ucv_array_set(reg, i, ucv_get(val));
+
+ return i + 1;
+}
+
+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
+{
+ if (!idx)
+ return NULL;
+
+ return ucv_array_get(reg, idx - 1);
+}
+
+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
+{
+ uc_value_t *val = wpa_ucode_registry_get(reg, idx);
+
+ if (val)
+ ucv_array_set(reg, idx - 1, NULL);
+
+ return val;
+}
+
+
+uc_value_t *wpa_ucode_call(size_t nargs)
+{
+ if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
+ return NULL;
+
+ if (!gc_timer.pending)
+ uloop_timeout_set(&gc_timer, 10);
+
+ return uc_vm_stack_pop(&vm);
+}
+
+void wpa_ucode_free_vm(void)
+{
+ if (!vm.config)
+ return;
+
+ uc_search_path_free(&vm.config->module_search_path);
+ uc_vm_free(&vm);
+ registry = NULL;
+ vm = (uc_vm_t){};
+}
diff --git a/package/network/services/hostapd/src/src/utils/ucode.h b/package/network/services/hostapd/src/src/utils/ucode.h
new file mode 100644
index 000000000000..2c1886976ee5
--- /dev/null
+++ b/package/network/services/hostapd/src/src/utils/ucode.h
@@ -0,0 +1,29 @@
+#ifndef __HOSTAPD_UTILS_UCODE_H
+#define __HOSTAPD_UTILS_UCODE_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include <ucode/lib.h>
+#include <ucode/vm.h>
+
+#define HOSTAPD_UC_PATH "/usr/share/hostap/"
+
+extern uc_value_t *uc_registry;
+uc_vm_t *wpa_ucode_create_vm(void);
+int wpa_ucode_run(const char *script);
+int wpa_ucode_call_prepare(const char *fname);
+uc_value_t *wpa_ucode_call(size_t nargs);
+void wpa_ucode_free_vm(void);
+
+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type);
+
+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx);
+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx);
+
+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
+
+#endif
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.c b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
index 16a68c507317..1c477f0c0cb2 100644
--- a/package/network/services/hostapd/src/wpa_supplicant/ubus.c
+++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
@@ -30,12 +30,6 @@ static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *ob
return container_of(obj, struct wpa_supplicant, ubus.obj);
}
-static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx)
-{
- struct ubus_context *ctx = eloop_ctx;
- ubus_handle_event(ctx);
-}
-
static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
{
if (ubus_reconnect(ctx, NULL)) {
@@ -43,12 +37,12 @@ static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
return;
}
- eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+ ubus_add_uloop(ctx);
}
static void wpas_ubus_connection_lost(struct ubus_context *ctx)
{
- eloop_unregister_read_sock(ctx->sock.fd);
+ uloop_fd_delete(&ctx->sock);
eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
}
@@ -57,12 +51,14 @@ static bool wpas_ubus_init(void)
if (ctx)
return true;
+ eloop_add_uloop();
ctx = ubus_connect(NULL);
if (!ctx)
return false;
ctx->connection_lost = wpas_ubus_connection_lost;
- eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+ ubus_add_uloop(ctx);
+
return true;
}
@@ -80,7 +76,7 @@ static void wpas_ubus_ref_dec(void)
if (ctx_ref)
return;
- eloop_unregister_read_sock(ctx->sock.fd);
+ uloop_fd_delete(&ctx->sock);
ubus_free(ctx);
ctx = NULL;
}
@@ -211,152 +207,6 @@ void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
free(name);
}
-enum {
- WPAS_CONFIG_DRIVER,
- WPAS_CONFIG_IFACE,
- WPAS_CONFIG_BRIDGE,
- WPAS_CONFIG_HOSTAPD_CTRL,
- WPAS_CONFIG_CTRL,
- WPAS_CONFIG_FILE,
- __WPAS_CONFIG_MAX
-};
-
-static const struct blobmsg_policy wpas_config_add_policy[__WPAS_CONFIG_MAX] = {
- [WPAS_CONFIG_DRIVER] = { "driver", BLOBMSG_TYPE_STRING },
- [WPAS_CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
- [WPAS_CONFIG_BRIDGE] = { "bridge", BLOBMSG_TYPE_STRING },
- [WPAS_CONFIG_HOSTAPD_CTRL] = { "hostapd_ctrl", BLOBMSG_TYPE_STRING },
- [WPAS_CONFIG_CTRL] = { "ctrl", BLOBMSG_TYPE_STRING },
- [WPAS_CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
-};
-
-static int
-wpas_config_add(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
-{
- struct blob_attr *tb[__WPAS_CONFIG_MAX];
- struct wpa_global *global = get_wpa_global_from_object(obj);
- struct wpa_interface *iface;
-
- blobmsg_parse(wpas_config_add_policy, __WPAS_CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
-
- if (!tb[WPAS_CONFIG_FILE] || !tb[WPAS_CONFIG_IFACE] || !tb[WPAS_CONFIG_DRIVER])
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- iface = os_zalloc(sizeof(struct wpa_interface));
- if (iface == NULL)
- return UBUS_STATUS_UNKNOWN_ERROR;
-
- iface->driver = blobmsg_get_string(tb[WPAS_CONFIG_DRIVER]);
- iface->ifname = blobmsg_get_string(tb[WPAS_CONFIG_IFACE]);
- iface->confname = blobmsg_get_string(tb[WPAS_CONFIG_FILE]);
-
- if (tb[WPAS_CONFIG_BRIDGE])
- iface->bridge_ifname = blobmsg_get_string(tb[WPAS_CONFIG_BRIDGE]);
-
- if (tb[WPAS_CONFIG_CTRL])
- iface->ctrl_interface = blobmsg_get_string(tb[WPAS_CONFIG_CTRL]);
-
- if (tb[WPAS_CONFIG_HOSTAPD_CTRL])
- iface->hostapd_ctrl = blobmsg_get_string(tb[WPAS_CONFIG_HOSTAPD_CTRL]);
-
- if (!wpa_supplicant_add_iface(global, iface, NULL))
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- blob_buf_init(&b, 0);
- blobmsg_add_u32(&b, "pid", getpid());
- ubus_send_reply(ctx, req, b.head);
-
- return UBUS_STATUS_OK;
-}
-
-enum {
- WPAS_CONFIG_REM_IFACE,
- __WPAS_CONFIG_REM_MAX
-};
-
-static const struct blobmsg_policy wpas_config_remove_policy[__WPAS_CONFIG_REM_MAX] = {
- [WPAS_CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
-};
-
-static int
-wpas_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
-{
- struct blob_attr *tb[__WPAS_CONFIG_REM_MAX];
- struct wpa_global *global = get_wpa_global_from_object(obj);
- struct wpa_supplicant *wpa_s = NULL;
- unsigned int found = 0;
-
- blobmsg_parse(wpas_config_remove_policy, __WPAS_CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
-
- if (!tb[WPAS_CONFIG_REM_IFACE])
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- /* find wpa_s object for to-be-removed interface */
- for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
- if (!strncmp(wpa_s->ifname,
- blobmsg_get_string(tb[WPAS_CONFIG_REM_IFACE]),
- sizeof(wpa_s->ifname)))
- {
- found = 1;
- break;
- }
- }
-
- if (!found)
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- if (wpa_supplicant_remove_iface(global, wpa_s, 0))
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- return UBUS_STATUS_OK;
-}
-
-static const struct ubus_method wpas_daemon_methods[] = {
- UBUS_METHOD("config_add", wpas_config_add, wpas_config_add_policy),
- UBUS_METHOD("config_remove", wpas_config_remove, wpas_config_remove_policy),
-};
-
-static struct ubus_object_type wpas_daemon_object_type =
- UBUS_OBJECT_TYPE("wpa_supplicant", wpas_daemon_methods);
-
-void wpas_ubus_add(struct wpa_global *global)
-{
- struct ubus_object *obj = &global->ubus_global;
- int ret;
-
- if (!wpas_ubus_init())
- return;
-
- obj->name = strdup("wpa_supplicant");
-
- obj->type = &wpas_daemon_object_type;
- obj->methods = wpas_daemon_object_type.methods;
- obj->n_methods = wpas_daemon_object_type.n_methods;
- ret = ubus_add_object(ctx, obj);
- wpas_ubus_ref_inc();
-}
-
-void wpas_ubus_free(struct wpa_global *global)
-{
- struct ubus_object *obj = &global->ubus_global;
- char *name = (char *) obj->name;
-
- if (!ctx)
- return;
-
- if (obj->id) {
- ubus_remove_object(ctx, obj);
- wpas_ubus_ref_dec();
- }
-
- free(name);
-}
-
-
#ifdef CONFIG_WPS
void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
{
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.h b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
index bf92b98c0135..f6681cb26d01 100644
--- a/package/network/services/hostapd/src/wpa_supplicant/ubus.h
+++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
@@ -24,9 +24,6 @@ struct wpas_ubus_bss {
void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
-void wpas_ubus_add(struct wpa_global *global);
-void wpas_ubus_free(struct wpa_global *global);
-
#ifdef CONFIG_WPS
void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
#endif
@@ -34,14 +31,6 @@ void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential
#else
struct wpas_ubus_bss {};
-static inline void wpas_ubus_add_iface(struct wpa_supplicant *wpa_s)
-{
-}
-
-static inline void wpas_ubus_free_iface(struct wpa_supplicant *wpa_s)
-{
-}
-
static inline void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
{
}
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.c b/package/network/services/hostapd/src/wpa_supplicant/ucode.c
new file mode 100644
index 000000000000..d0a78d162535
--- /dev/null
+++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.c
@@ -0,0 +1,270 @@
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "drivers/driver.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "bss.h"
+#include "ucode.h"
+
+static struct wpa_global *wpa_global;
+static uc_resource_type_t *global_type, *iface_type;
+static uc_value_t *global, *iface_registry;
+static uc_vm_t *vm;
+
+static uc_value_t *
+wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s)
+{
+ uc_value_t *val;
+
+ if (wpa_s->ucode.idx)
+ return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
+
+ val = uc_resource_new(iface_type, wpa_s);
+ wpa_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
+
+ return val;
+}
+
+static void
+wpas_ucode_update_interfaces(void)
+{
+ uc_value_t *ifs = ucv_object_new(vm);
+ struct wpa_supplicant *wpa_s;
+ int i;
+
+ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
+ ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
+
+ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
+ ucv_gc(vm);
+}
+
+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
+{
+ uc_value_t *val;
+
+ if (wpa_ucode_call_prepare("iface_add"))
+ return;
+
+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+ uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
+ ucv_put(wpa_ucode_call(2));
+ ucv_gc(vm);
+}
+
+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
+{
+ uc_value_t *val;
+
+ val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx);
+ if (!val)
+ return;
+
+ wpa_s->ucode.idx = 0;
+ if (wpa_ucode_call_prepare("iface_remove"))
+ return;
+
+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+ uc_value_push(ucv_get(val));
+ ucv_put(wpa_ucode_call(2));
+ ucv_gc(vm);
+}
+
+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
+{
+ const char *state;
+ uc_value_t *val;
+
+ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
+ if (!val)
+ return;
+
+ if (wpa_ucode_call_prepare("state"))
+ return;
+
+ state = wpa_supplicant_state_txt(wpa_s->wpa_state);
+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+ uc_value_push(ucv_get(val));
+ uc_value_push(ucv_get(ucv_string_new(state)));
+ ucv_put(wpa_ucode_call(3));
+ ucv_gc(vm);
+}
+
+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
+{
+ const char *state;
+ uc_value_t *val;
+
+ if (event != EVENT_CH_SWITCH_STARTED)
+ return;
+
+ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
+ if (!val)
+ return;
+
+ if (wpa_ucode_call_prepare("event"))
+ return;
+
+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+ uc_value_push(ucv_get(val));
+ uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
+ val = ucv_object_new(vm);
+ uc_value_push(ucv_get(val));
+
+ if (event == EVENT_CH_SWITCH_STARTED) {
+ ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
+ ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
+ ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
+ ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
+ ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
+ }
+
+ ucv_put(wpa_ucode_call(4));
+ ucv_gc(vm);
+}
+
+static const char *obj_stringval(uc_value_t *obj, const char *name)
+{
+ uc_value_t *val = ucv_object_get(obj, name, NULL);
+
+ return ucv_string_get(val);
+}
+
+static uc_value_t *
+uc_wpas_add_iface(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *info = uc_fn_arg(0);
+ uc_value_t *ifname = ucv_object_get(info, "iface", NULL);
+ uc_value_t *bridge = ucv_object_get(info, "bridge", NULL);
+ uc_value_t *config = ucv_object_get(info, "config", NULL);
+ uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL);
+ struct wpa_interface iface;
+ int ret = -1;
+
+ if (ucv_type(info) != UC_OBJECT)
+ goto out;
+
+ iface = (struct wpa_interface){
+ .driver = "nl80211",
+ .ifname = ucv_string_get(ifname),
+ .bridge_ifname = ucv_string_get(bridge),
+ .confname = ucv_string_get(config),
+ .ctrl_interface = ucv_string_get(ctrl),
+ };
+
+ if (!iface.ifname || !iface.confname)
+ goto out;
+
+ ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1;
+ wpas_ucode_update_interfaces();
+
+out:
+ return ucv_int64_new(ret);
+}
+
+static uc_value_t *
+uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs)
+{
+ struct wpa_supplicant *wpa_s = NULL;
+ uc_value_t *ifname_arg = uc_fn_arg(0);
+ const char *ifname = ucv_string_get(ifname_arg);
+ int ret = -1;
+
+ if (!ifname)
+ goto out;
+
+ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
+ if (!strcmp(wpa_s->ifname, ifname))
+ break;
+
+ if (!wpa_s)
+ goto out;
+
+ ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0);
+ wpas_ucode_update_interfaces();
+
+out:
+ return ucv_int64_new(ret);
+}
+
+static uc_value_t *
+uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
+{
+ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ struct wpa_bss *bss;
+ uc_value_t *ret, *val;
+
+ if (!wpa_s)
+ return NULL;
+
+ ret = ucv_object_new(vm);
+
+ val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
+ ucv_object_add(ret, "state", ucv_get(val));
+
+ bss = wpa_s->current_bss;
+ if (bss) {
+ int sec_chan = 0;
+ const u8 *ie;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+ if (ie && ie[1] >= 2) {
+ const struct ieee80211_ht_operation *ht_oper;
+
+ ht_oper = (const void *) (ie + 2);
+ if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+ sec_chan = 1;
+ else if (ht_oper->ht_param &
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+ sec_chan = -1;
+ }
+
+ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+ }
+
+ return ret;
+}
+
+int wpas_ucode_init(struct wpa_global *gl)
+{
+ static const uc_function_list_t global_fns[] = {
+ { "printf", uc_wpa_printf },
+ { "getpid", uc_wpa_getpid },
+ { "add_iface", uc_wpas_add_iface },
+ { "remove_iface", uc_wpas_remove_iface },
+ };
+ static const uc_function_list_t iface_fns[] = {
+ { "status", uc_wpas_iface_status },
+ };
+ uc_value_t *data, *proto;
+
+ wpa_global = gl;
+ vm = wpa_ucode_create_vm();
+
+ global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL);
+ iface_type = uc_type_declare(vm, "wpas.iface", iface_fns, NULL);
+
+ iface_registry = ucv_array_new(vm);
+ uc_vm_registry_set(vm, "wpas.iface_registry", iface_registry);
+
+ global = wpa_ucode_global_init("wpas", global_type);
+
+ if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc"))
+ goto free_vm;
+
+ ucv_gc(vm);
+ return 0;
+
+free_vm:
+ wpa_ucode_free_vm();
+ return -1;
+}
+
+void wpas_ucode_free(void)
+{
+ if (wpa_ucode_call_prepare("shutdown") == 0)
+ ucv_put(wpa_ucode_call(0));
+ wpa_ucode_free_vm();
+}
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.h b/package/network/services/hostapd/src/wpa_supplicant/ucode.h
new file mode 100644
index 000000000000..a429a0ed87bd
--- /dev/null
+++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.h
@@ -0,0 +1,49 @@
+#ifndef __WPAS_UCODE_H
+#define __WPAS_UCODE_H
+
+#include "utils/ucode.h"
+
+struct wpa_global;
+union wpa_event_data;
+struct wpa_supplicant;
+
+struct wpas_ucode_bss {
+#ifdef UCODE_SUPPORT
+ unsigned int idx;
+#endif
+};
+
+#ifdef UCODE_SUPPORT
+int wpas_ucode_init(struct wpa_global *gl);
+void wpas_ucode_free(void);
+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s);
+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s);
+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
+#else
+static inline int wpas_ucode_init(struct wpa_global *gl)
+{
+ return -EINVAL;
+}
+static inline void wpas_ucode_free(void)
+{
+}
+static inline void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
+{
+}
+
+#endif
+
+#endif
--
2.41.0
More information about the openwrt-devel
mailing list