mac80211: add support for dynamically reconfiguring wifi
authorJohn Crispin <john@phrozen.org>
Wed, 30 Oct 2019 15:58:19 +0000 (16:58 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Tue, 12 Nov 2019 10:52:33 +0000 (11:52 +0100)
Change scripts to use ubus interface of hostapd/wpa_supplicant to
add/remove/modify wireless interfaces instead of (re-)starting the
services.

Signed-off-by: John Crispin <john@phrozen.org>
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
package/kernel/mac80211/Makefile
package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh

index dd5d4f94564af59a5cfc188582078c9d1d9a66fd..422c0436e487225173a7cde4174f4fa030952240 100644 (file)
@@ -11,7 +11,7 @@ include $(INCLUDE_DIR)/kernel.mk
 PKG_NAME:=mac80211
 
 PKG_VERSION:=5.4-rc2-1
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 PKG_SOURCE_URL:=@KERNEL/linux/kernel/projects/backports/stable/v5.4-rc2/
 PKG_HASH:=b3baedc135b455f09f266cb77e73276ca21bceeb0f24bac2184cc4b97d09cdbf
 
index 12af2d1d0e8e756937da3d94d72f058bedb6ebbb..5b174cded6403046c8b2848e75b03488cfdbf75f 100644 (file)
@@ -19,6 +19,11 @@ iw() {
        command iw $@ || logger -t mac80211 "Failed command: iw $@"
 }
 
+NEWAPLIST=
+OLDAPLIST=
+NEWSPLIST=
+OLDSPLIST=
+
 drv_mac80211_init_device_config() {
        hostapd_common_add_device_config
 
@@ -58,7 +63,7 @@ drv_mac80211_init_iface_config() {
 
        config_add_string 'macaddr:macaddr' ifname
 
-       config_add_boolean wds powersave
+       config_add_boolean wds powersave enable
        config_add_int maxassoc
        config_add_int max_listen_int
        config_add_int dtim_period
@@ -454,7 +459,7 @@ mac80211_iw_interface_add() {
 mac80211_prepare_vif() {
        json_select config
 
-       json_get_vars ifname mode ssid wds powersave macaddr
+       json_get_vars ifname mode ssid wds powersave macaddr enable
 
        [ -n "$ifname" ] || ifname="wlan${phy#phy}${if_idx:+-$if_idx}"
        if_idx=$((${if_idx:-0} + 1))
@@ -490,8 +495,8 @@ mac80211_prepare_vif() {
 
                        mac80211_hostapd_setup_bss "$phy" "$ifname" "$macaddr" "$type" || return
 
+                       NEWAPLIST="${NEWAPLIST}$ifname "
                        [ -n "$hostapd_ctrl" ] || {
-                               mac80211_iw_interface_add "$phy" "$ifname" __ap || return
                                hostapd_ctrl="${hostapd_ctrl:-/var/run/hostapd/$ifname}"
                        }
                ;;
@@ -503,7 +508,7 @@ mac80211_prepare_vif() {
                ;;
                sta)
                        local wdsflag=
-                       staidx="$(($staidx + 1))"
+                       [ "$enable" = 0 ] || staidx="$(($staidx + 1))"
                        [ "$wds" -gt 0 ] && wdsflag="4addr on"
                        mac80211_iw_interface_add "$phy" "$ifname" managed "$wdsflag" || return
                        [ "$powersave" -gt 0 ] && powersave="on" || powersave="off"
@@ -529,19 +534,62 @@ mac80211_prepare_vif() {
 }
 
 mac80211_setup_supplicant() {
+       local enable=$1
+       local add_sp=0
+       local spobj="$(ubus -S list | grep wpa_supplicant.${ifname})"
+
        wpa_supplicant_prepare_interface "$ifname" nl80211 || return 1
+       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
-       wpa_supplicant_run "$ifname" ${hostapd_ctrl:+-H $hostapd_ctrl}
+
+       NEWSPLIST="${NEWSPLIST}$ifname "
+
+       if [ "${NEWAPLIST%% *}" != "${OLDAPLIST%% *}" ]; then
+               [ "$spobj" ] && ubus call wpa_supplicant.${phy} config_del "{\"iface\":\"$ifname\"}"
+               add_sp=1
+       fi
+       [ "$enable" = 0 ] && {
+               ubus call wpa_supplicant.${phy} config_del "{\"iface\":\"$ifname\"}"
+               ip link set dev "$ifname" down
+               return 0
+       }
+       [ -z "$spobj" ] && add_sp=1
+
+       if [ "$add_sp" = "1" ]; then
+               wpa_supplicant_run "$ifname" "$hostapd_ctrl"
+       else
+               ubus call $spobj reload
+       fi
 }
 
 mac80211_setup_supplicant_noctl() {
-       wpa_supplicant_prepare_interface "$ifname" nl80211 || return 1
+       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"
-       wpa_supplicant_run "$ifname"
+
+       NEWSPLIST="${NEWSPLIST}$ifname "
+       [ "$enable" = 0 ] && {
+               ubus call wpa_supplicant.${phy} config_del "{\"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_setup_adhoc_htmode() {
@@ -579,12 +627,17 @@ mac80211_setup_adhoc_htmode() {
                ;;
                *) ibss_htmode="" ;;
        esac
-
 }
 
 mac80211_setup_adhoc() {
+       local enable=$1
        json_get_vars bssid ssid key mcast_rate
 
+       [ "$enable" = 0 ] && {
+               ip link set dev "$ifname" down
+               return 0
+       }
+
        keyspec=
        [ "$auth_type" = "wep" ] && {
                set_default key 1
@@ -623,8 +676,14 @@ mac80211_setup_adhoc() {
 }
 
 mac80211_setup_mesh() {
+       local enable=$1
        json_get_vars ssid mesh_id mcast_rate
 
+       [ "$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"
@@ -670,6 +729,7 @@ mac80211_setup_mesh() {
 mac80211_setup_vif() {
        local name="$1"
        local failed
+       local action=up
 
        json_select data
        json_get_vars ifname
@@ -678,13 +738,15 @@ mac80211_setup_vif() {
        json_select config
        json_get_vars mode
        json_get_var vif_txpower txpower
+       json_get_var vif_enable enable 1
 
-       ip link set dev "$ifname" up || {
+       [ "$vif_enable" = 1 ] || action=down
+       logger ip link set dev "$ifname" $action
+       ip link set dev "$ifname" "$action" || {
                wireless_setup_vif_failed IFUP_ERROR
                json_select ..
                return
        }
-
        set_default vif_txpower "$txpower"
        [ -z "$vif_txpower" ] || iw dev "$ifname" set txpower fixed "${vif_txpower%%.*}00"
 
@@ -693,9 +755,9 @@ mac80211_setup_vif() {
                        wireless_vif_parse_encryption
                        freq="$(get_freq "$phy" "$channel")"
                        if [ "$wpa" -gt 0 -o "$auto_channel" -gt 0 ] || chan_is_dfs "$phy" "$channel"; then
-                               mac80211_setup_supplicant || failed=1
+                               mac80211_setup_supplicant $vif_enable || failed=1
                        else
-                               mac80211_setup_mesh
+                               mac80211_setup_mesh $vif_enable
                        fi
                        for var in $MP_CONFIG_INT $MP_CONFIG_BOOL $MP_CONFIG_STRING; do
                                json_get_var mp_val "$var"
@@ -707,13 +769,13 @@ mac80211_setup_vif() {
                        mac80211_setup_adhoc_htmode
                        if [ "$wpa" -gt 0 -o "$auto_channel" -gt 0 ]; then
                                freq="$(get_freq "$phy" "$channel")"
-                               mac80211_setup_supplicant_noctl || failed=1
+                               mac80211_setup_supplicant_noctl $vif_enable || failed=1
                        else
-                               mac80211_setup_adhoc
+                               mac80211_setup_adhoc $vif_enable
                        fi
                ;;
                sta)
-                       mac80211_setup_supplicant || failed=1
+                       mac80211_setup_supplicant $vif_enable || failed=1
                ;;
        esac
 
@@ -734,18 +796,26 @@ chan_is_dfs() {
        return $!
 }
 
-mac80211_interface_cleanup() {
-       local phy="$1"
+mac80211_vap_cleanup() {
+       local service="$1"
+       local vaps="$2"
 
-       for wdev in $(list_phy_interfaces "$phy"); do
-               local wdev_phy="$(readlink /sys/class/net/${wdev}/phy80211)"
-               wdev_phy="$(basename "$wdev_phy")"
-               [ -n "$wdev_phy" -a "$wdev_phy" != "$phy" ] && continue
+       for wdev in $vaps; do
+               ubus call ${service}.${phy} 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_set_noscan() {
        hostapd_noscan=1
 }
@@ -771,8 +841,10 @@ drv_mac80211_setup() {
                return 1
        }
 
-       wireless_set_data phy="$phy"
-       mac80211_interface_cleanup "$phy"
+       [ -z "$(uci -q -P /var/state show wireless._${phy})" ] && {
+               uci -q -P /var/state set wireless._${phy}=phy
+               wireless_set_data phy="$phy"
+       }
 
        # convert channel to frequency
        [ "$auto_channel" -gt 0 ] || freq="$(get_freq "$phy" "$channel")"
@@ -822,30 +894,55 @@ drv_mac80211_setup() {
        [ -n "$has_ap" ] && mac80211_hostapd_setup_base "$phy"
 
        for_each_interface "sta adhoc mesh monitor" mac80211_prepare_vif
+       NEWAPLIST=
        for_each_interface "ap" mac80211_prepare_vif
-
+       OLDAPLIST=$(uci -q -P /var/state get wireless._${phy}.aplist)
+       NEW_MD5=$(md5sum ${hostapd_conf_file})
+       OLD_MD5=$(uci -q -P /var/state get wireless._${phy}.md5)
+       if [ "${NEWAPLIST}" != "${OLDAPLIST}" ]; then
+               mac80211_vap_cleanup hostapd "${OLDAPLIST}"
+               [ -n "${NEWAPLIST}" ] && mac80211_iw_interface_add "$phy" "${NEWAPLIST%% *}" __ap || return
+       fi
+       local add_ap=0
+       local primary_ap=${NEWAPLIST%% *}
        [ -n "$hostapd_ctrl" ] && {
-               /usr/sbin/hostapd -s -P /var/run/wifi-$phy.pid -B "$hostapd_conf_file"
+               if [ -n "$(ubus list | grep hostapd.$primary_ap)" ]; then
+                       [ "${NEW_MD5}" = "${OLD_MD5}" ] || {
+                               ubus call hostapd.$primary_ap reload
+                       }
+               else
+                       add_ap=1
+                       ubus call hostapd.${phy} config_add "{\"iface\":\"$primary_ap\", \"config\":\"${hostapd_conf_file}\"}"
+               fi
                ret="$?"
-               wireless_add_process "$(cat /var/run/wifi-$phy.pid)" "/usr/sbin/hostapd" 1
                [ "$ret" != 0 ] && {
                        wireless_setup_failed HOSTAPD_START_FAILED
                        return
                }
        }
+       uci -q -P /var/state set wireless._${phy}.aplist="${NEWAPLIST}"
+       uci -q -P /var/state set wireless._${phy}.md5="${NEW_MD5}"
 
-       for_each_interface "ap sta adhoc mesh monitor" mac80211_setup_vif
+       [ "${add_ap}" = 1 ] && sleep 1
+       for_each_interface "ap" mac80211_setup_vif
 
-       wireless_set_up
-}
+       NEWSPLIST=
+       OLDSPLIST=$(uci -q -P /var/state get wireless._${phy}.splist)
+       for_each_interface "sta adhoc mesh monitor" mac80211_setup_vif
 
-list_phy_interfaces() {
-       local phy="$1"
-       if [ -d "/sys/class/ieee80211/${phy}/device/net" ]; then
-               ls "/sys/class/ieee80211/${phy}/device/net" 2>/dev/null;
-       else
-               ls "/sys/class/ieee80211/${phy}/device" 2>/dev/null | grep net: | sed -e 's,net:,,g'
-       fi
+       uci -q -P /var/state set wireless._${phy}.splist="${NEWSPLIST}"
+
+       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"
+       wireless_set_up
 }
 
 drv_mac80211_teardown() {
@@ -856,6 +953,7 @@ drv_mac80211_teardown() {
        json_select ..
 
        mac80211_interface_cleanup "$phy"
+       uci -q -P /var/state revert wireless._${phy}
 }
 
 add_driver mac80211