hostapd: add ucode support, use ucode for the main ubus object
authorFelix Fietkau <nbd@nbd.name>
Fri, 26 May 2023 08:23:59 +0000 (10:23 +0200)
committerFelix Fietkau <nbd@nbd.name>
Tue, 1 Aug 2023 08:08:03 +0000 (10:08 +0200)
This implements vastly improved dynamic configuration reload support.
It can handle configuration changes on individual wifi interfaces, as well
as adding/removing interfaces.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
24 files changed:
package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
package/network/services/hostapd/Makefile
package/network/services/hostapd/files/common.uc [new file with mode: 0644]
package/network/services/hostapd/files/hostapd.sh
package/network/services/hostapd/files/hostapd.uc [new file with mode: 0644]
package/network/services/hostapd/files/wdev.uc [new file with mode: 0644]
package/network/services/hostapd/files/wpa_supplicant.uc [new file with mode: 0644]
package/network/services/hostapd/patches/600-ubus_support.patch
package/network/services/hostapd/patches/601-ucode_support.patch [new file with mode: 0644]
package/network/services/hostapd/patches/700-wifi-reload.patch
package/network/services/hostapd/patches/701-reload_config_inline.patch [new file with mode: 0644]
package/network/services/hostapd/patches/720-iface_max_num_sta.patch
package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch
package/network/services/hostapd/patches/761-shared_das_port.patch
package/network/services/hostapd/patches/770-radius_server.patch
package/network/services/hostapd/src/src/ap/ubus.c
package/network/services/hostapd/src/src/ap/ucode.c [new file with mode: 0644]
package/network/services/hostapd/src/src/ap/ucode.h [new file with mode: 0644]
package/network/services/hostapd/src/src/utils/ucode.c [new file with mode: 0644]
package/network/services/hostapd/src/src/utils/ucode.h [new file with mode: 0644]
package/network/services/hostapd/src/wpa_supplicant/ubus.c
package/network/services/hostapd/src/wpa_supplicant/ubus.h
package/network/services/hostapd/src/wpa_supplicant/ucode.c [new file with mode: 0644]
package/network/services/hostapd/src/wpa_supplicant/ucode.h [new file with mode: 0644]

index 5aaba9af26c60813098e24d158d5df0d9976b6aa..a2a7bdba2555901487b714fd4b12bf9cd8ccdf8c 100644 (file)
@@ -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
@@ -661,74 +658,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 +681,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 +692,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 +705,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 +716,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 +763,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 +811,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 +1053,6 @@ band_match && $3 == "MHz" && $4 == channel {
 '
 }
 
-
 chan_is_dfs() {
        local phy="$1"
        local chan="$2"
@@ -1093,27 +1060,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 +1068,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 \
@@ -1141,30 +1096,11 @@ drv_mac80211_setup() {
        }
 
        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 +1113,6 @@ drv_mac80211_setup() {
 
        hostapd_conf_file="/var/run/hostapd-$phy.conf"
 
-       no_ap=1
        macidx=0
        staidx=0
 
@@ -1212,78 +1147,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 +1207,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
index 1d034a17526794030a79b0a8950d223e4ea0ff89..f147c42ad8cf326fc063f5618afa525974e91be1 100644 (file)
@@ -81,12 +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_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
@@ -148,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 +libblobmsg-json
+  DEPENDS:=$(DRV_DEPENDS) +hostapd-common $(CORE_DEPENDS)
   EXTRA_DEPENDS:=hostapd-common (=$(PKG_VERSION)-$(PKG_RELEASE))
   USERID:=network=101:network=101
   PROVIDES:=hostapd
@@ -253,7 +256,7 @@ define Package/wpad/Default
   CATEGORY:=Network
   SUBMENU:=WirelessAPD
   TITLE:=IEEE 802.1x Auth/Supplicant
-  DEPENDS:=$(DRV_DEPENDS) +hostapd-common +libubus +libblobmsg-json
+  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/
@@ -398,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 +libblobmsg-json
+  DEPENDS:=$(DRV_DEPENDS) +hostapd-common $(CORE_DEPENDS)
   EXTRA_DEPENDS:=hostapd-common (=$(PKG_VERSION)-$(PKG_RELEASE))
   USERID:=network=101:network=101
   PROVIDES:=wpa-supplicant
@@ -520,7 +523,7 @@ define Package/eapol-test/Default
   SECTION:=net
   SUBMENU:=WirelessAPD
   CATEGORY:=Network
-  DEPENDS:=$(DRV_DEPENDS) +libubus +libblobmsg-json
+  DEPENDS:=$(DRV_DEPENDS) $(CORE_DEPENDS)
 endef
 
 define Package/eapol-test
@@ -585,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 -lblobmsg_json
+TARGET_LDFLAGS += -lubox -lubus -lblobmsg_json -lucode
 
 ifdef CONFIG_PACKAGE_kmod-cfg80211
   TARGET_LDFLAGS += -lm -lnl-tiny
@@ -703,22 +706,26 @@ 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 (file)
index 0000000..2002572
--- /dev/null
@@ -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;
+
+       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 };
index bf3625c92d8b4e696e3e1fd849230f5d4e20f936..49db03ed46256b6020dd42b36a88c8896179339e 100644 (file)
@@ -1591,29 +1591,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 (file)
index 0000000..cc026cc
--- /dev/null
@@ -0,0 +1,399 @@
+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);
+       if (hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`) < 0) {
+               hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
+               return;
+       }
+}
+
+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 (config.bss[0].ifname != old_config.bss[0].ifname)
+               return false;
+
+       let iface = hostapd.interfaces[config.bss[0].ifname];
+       if (!iface)
+               return false;
+
+       let config_inline = iface_gen_config(phy, config);
+
+       bss_reload_psk(iface.bss[0], config.bss[0], old_config.bss[0]);
+       if (!is_equal(config.bss[0], old_config.bss[0])) {
+               if (phy_is_fullmac(phy))
+                       return false;
+
+               hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
+               if (iface.bss[0].set_config(config_inline, 0) < 0) {
+                       hostapd.printf(`Failed to set config`);
+                       return false;
+               }
+       }
+
+       let bss_list = array_to_obj(iface.bss, "name", 1);
+       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 = bss_list[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] == "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;
+               }
+       },
+       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/wdev.uc b/package/network/services/hostapd/files/wdev.uc
new file mode 100644 (file)
index 0000000..9701af1
--- /dev/null
@@ -0,0 +1,153 @@
+#!/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, 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", ssid, "freq", wdev.freq, wdev.htmode ];
+               for (let key in [ "beacon-interval", "mcast-rate" ])
+                       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 (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.uc b/package/network/services/hostapd/files/wpa_supplicant.uc
new file mode 100644 (file)
index 0000000..22cb130
--- /dev/null
@@ -0,0 +1,161 @@
+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 = {};
+
+function iface_stop(iface)
+{
+       let ifname = iface.config.iface;
+
+       wpas.remove_iface(ifname);
+       wdev_remove(ifname);
+       iface.running = false;
+}
+
+function iface_start(phy, iface)
+{
+       if (iface.running)
+               return;
+
+       let ifname = iface.config.iface;
+
+       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 && old_if.running)
+               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 = {
+       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: {} });
+}
+
+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);
+       }
+};
index 1be2b1fdf80a5ea0174999b3b9150688703ffb50..f420c18644eaac5adb5e695bed4232f0f089e680 100644 (file)
  
  #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;
        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[])
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 (file)
index 0000000..6cfc9b7
--- /dev/null
@@ -0,0 +1,224 @@
+--- 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
+@@ -991,6 +991,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");
+@@ -1000,6 +1001,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;
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -276,6 +276,8 @@ int hostapd_reload_config(struct hostapd
+       size_t j;
+       int i;
++      hostapd_ucode_reload_bss(hapd, reconf);
++
+       if (iface->config_fname == NULL) {
+               /* Only in-memory config in use - assume it has been updated */
+               hostapd_clear_old(iface);
+@@ -455,6 +457,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);
+@@ -619,6 +622,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);
+@@ -1209,6 +1213,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;
+ }
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -197,8 +197,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
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -7636,6 +7636,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;
+ }
+@@ -7663,6 +7664,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 */
+@@ -7973,6 +7975,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;
+ }
+@@ -8011,12 +8014,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;
+ }
+@@ -8049,6 +8048,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;
+@@ -659,6 +660,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
+@@ -4921,6 +4921,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;
+@@ -5022,6 +5023,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);
index 0c7627645f6983aff70c3b73ce30e7e89a303bd5..c0e7e4d16e3229216f1e6176786616a1f53cca24 100644 (file)
        int rts_threshold;
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
+@@ -127,7 +127,7 @@ void hostapd_reconfig_encryption(struct
+ }
+-static void hostapd_reload_bss(struct hostapd_data *hapd)
++void hostapd_reload_bss(struct hostapd_data *hapd)
+ {
+       struct hostapd_ssid *ssid;
 @@ -255,6 +255,10 @@ static int hostapd_iface_conf_changed(st
  {
        size_t i;
@@ -60,7 +69,7 @@
  {
        struct hapd_interfaces *interfaces = iface->interfaces;
        struct hostapd_data *hapd = iface->bss[0];
-@@ -296,6 +300,9 @@ int hostapd_reload_config(struct hostapd
+@@ -298,6 +302,9 @@ int hostapd_reload_config(struct hostapd
                char *fname;
                int res;
  
@@ -70,7 +79,7 @@
                hostapd_clear_old(iface);
  
                wpa_printf(MSG_DEBUG,
-@@ -322,6 +329,24 @@ int hostapd_reload_config(struct hostapd
+@@ -324,6 +331,24 @@ int hostapd_reload_config(struct hostapd
                        wpa_printf(MSG_ERROR,
                                   "Failed to enable interface on config reload");
                return res;
        }
        iface->conf = newconf;
  
-@@ -338,6 +363,12 @@ int hostapd_reload_config(struct hostapd
+@@ -340,6 +365,12 @@ int hostapd_reload_config(struct hostapd
  
        for (j = 0; j < iface->num_bss; j++) {
                hapd = iface->bss[j];
                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
+@@ -1236,8 +1267,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];
+@@ -2705,6 +2735,10 @@ hostapd_alloc_bss_data(struct hostapd_if
        hapd->iconf = conf;
        hapd->conf = bss;
        hapd->iface = hapd_iface;
        if (conf)
                hapd->driver = conf->driver;
        hapd->ctrl_sock = -1;
+@@ -2723,7 +2757,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/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -47,7 +47,7 @@ struct mesh_conf;
+@@ -48,7 +48,7 @@ struct mesh_conf;
  struct hostapd_iface;
  
  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);
-@@ -186,6 +186,7 @@ struct hostapd_data {
-       struct hostapd_config *iconf;
+@@ -192,6 +192,7 @@ struct hostapd_data {
        struct hostapd_bss_config *conf;
        struct hostapd_ubus_bss ubus;
+       struct hostapd_ucode_bss ucode;
 +      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 {
+@@ -696,7 +697,9 @@ 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_reload_bss(struct hostapd_data *hapd);
++void hostapd_bss_deinit(struct hostapd_data *hapd);
  void hostapd_reconfig_encryption(struct hostapd_data *hapd);
  struct hostapd_data *
  hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+@@ -713,6 +716,7 @@ 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_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                          int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *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
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 (file)
index 0000000..071281e
--- /dev/null
@@ -0,0 +1,33 @@
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4814,7 +4814,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));
index 0bb00f9555476e3b398d7ccbbf67385d2fc577ec..5f40aabb5eb8986e61754664b1f0c8716f0423e3 100644 (file)
@@ -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
+@@ -744,6 +744,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);
index 124f5ea6bae525d9b0ae1d427850393e3c2ea3cc..3e282b47a328e9f77aeb541168fb770ff8af96c7 100644 (file)
@@ -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
+@@ -1538,6 +1538,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
+@@ -1545,7 +1546,6 @@ int hostapd_setup_bss(struct hostapd_dat
                wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
                return -1;
        }
index dad7afddf13b66c8d645720ab9781a9bb6866623..d60764ac593fa284d88d011147cf90c76c03302e 100644 (file)
@@ -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
+@@ -1475,6 +1475,7 @@ int hostapd_setup_bss(struct hostapd_dat
  
                        os_memset(&das_conf, 0, sizeof(das_conf));
                        das_conf.port = conf->radius_das_port;
index 3f0f40c0f73d05417512e5bccbff1c7acb4f3e97..6fca86d8814308c2ebf23ded548f0f82d6885c43 100644 (file)
@@ -21,7 +21,7 @@
  
  #ifndef CONFIG_NO_HOSTAPD_LOGGER
  static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
-@@ -665,6 +666,11 @@ int main(int argc, char *argv[])
+@@ -755,6 +756,11 @@ int main(int argc, char *argv[])
        if (os_program_init())
                return -1;
  
index a8a55f0493b13c54a89bfd16e5fc1c3a1c9b71d9..a609eb1ef2a47d124486f23f84758567121d7d66 100644 (file)
@@ -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);
@@ -123,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)
 {
@@ -199,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, 1);
 }
 
 
@@ -683,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,
@@ -1781,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)
@@ -1798,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();
@@ -1845,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 (file)
index 0000000..137cb81
--- /dev/null
@@ -0,0 +1,394 @@
+#include <sys/un.h>
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "hostapd.h"
+#include "ap_drv_ops.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);
+       wpa_ucode_registry_add(bss_registry, val, &hapd->ucode.idx);
+
+       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);
+       wpa_ucode_registry_add(iface_registry, val, &hapd->ucode.idx);
+
+       return val;
+}
+
+static void
+hostapd_ucode_update_bss_list(struct hostapd_iface *iface)
+{
+       uc_value_t *ifval, *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);
+               uc_value_t *proto = ucv_prototype_get(val);
+
+               ucv_object_add(proto, "name", ucv_get(ucv_string_new(hapd->conf->iface)));
+               ucv_object_add(proto, "index", ucv_int64_new(i));
+               ucv_array_set(list, i, ucv_get(val));
+       }
+
+       ifval = hostapd_ucode_iface_get_uval(iface);
+       ucv_object_add(ucv_prototype_get(ifval), "bss", ucv_get(list));
+}
+
+static void
+hostapd_ucode_update_interfaces(void)
+{
+       uc_value_t *ifs = 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);
+       }
+
+       ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
+       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_bss_list(iface);
+       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_bss_list(iface);
+       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);
+}
+
+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 },
+               { "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 }
+       };
+       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, int reconf)
+{
+       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));
+       uc_value_push(ucv_int64_new(reconf));
+       ucv_put(wpa_ucode_call(3));
+       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 (file)
index 0000000..dbc49e6
--- /dev/null
@@ -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, int reconf);
+
+#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, int reconf)
+{
+}
+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 (file)
index 0000000..fabf58a
--- /dev/null
@@ -0,0 +1,237 @@
+#include <unistd.h>
+#include "ucode.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.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_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;
+}
+
+void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx)
+{
+       uc_value_t *data;
+       int i = 0;
+
+       while (ucv_array_get(reg, i))
+               i++;
+
+       ucv_array_set(reg, i, ucv_get(val));
+
+       data = ucv_object_new(&vm);
+       ucv_object_add(ucv_prototype_get(val), "data", ucv_get(data));
+
+       *idx = 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 (file)
index 0000000..4caf8ad
--- /dev/null
@@ -0,0 +1,28 @@
+#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);
+
+void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx);
+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);
+
+#endif
index 804d4659418d13e560747bb5736af33f8879b961..1c477f0c0cb2e8bc4bfe2d3a2b5e500292d69baf 100644 (file)
@@ -207,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)
 {
index bf92b98c0135ef37bf19a5387f05e102d7b2b455..f6681cb26d016db56eb682669b80c539ee4d03c0 100644 (file)
@@ -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 (file)
index 0000000..660357a
--- /dev/null
@@ -0,0 +1,177 @@
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.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_ucode_registry_add(iface_registry, val, &wpa_s->ucode.idx);
+
+       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_string_new(wpa_s->ifname));
+       uc_value_push(ucv_get(val));
+       ucv_put(wpa_ucode_call(2));
+       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);
+       uc_value_t *hapd_ctrl = ucv_object_get(info, "hostapd_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),
+               .hostapd_ctrl = ucv_string_get(hapd_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);
+}
+
+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[] = {
+       };
+       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, "hostapd.iface", iface_fns, NULL);
+
+       iface_registry = ucv_array_new(vm);
+       uc_vm_registry_set(vm, "hostap.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 (file)
index 0000000..fcd2313
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef __WPAS_UCODE_H
+#define __WPAS_UCODE_H
+
+#include "utils/ucode.h"
+
+struct wpa_global;
+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);
+#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)
+{
+}
+
+#endif
+
+#endif