From 4a0b1af905694b4e5206207672e67c364d3b47a1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 3 Sep 2023 09:51:28 +0200 Subject: [PATCH] hostapd: allow adding initial AP without breaking STA interface connection When switching from a STA-only configuration to AP+STA on the same phy, the STA was previously restarted in order to notify hostapd of the new frequency, which might not match the AP configuration. Fix the STA restart by querying the operating frequency from within hostapd when bringing up the AP. Signed-off-by: Felix Fietkau --- .../files/lib/netifd/wireless/mac80211.sh | 4 +- .../network/services/hostapd/files/hostapd.uc | 99 +++++++++++++------ .../services/hostapd/files/wpa_supplicant.uc | 41 ++++++++ .../services/hostapd/src/src/ap/ucode.c | 1 + 4 files changed, 112 insertions(+), 33 deletions(-) diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh index db1ba231f3a3..3b88af467959 100644 --- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh +++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh @@ -889,7 +889,6 @@ wpa_supplicant_init_config() { wpa_supplicant_add_interface() { local ifname="$1" local mode="$2" - local hostapd_ctrl="$3" local prev _wpa_supplicant_common "$ifname" @@ -903,7 +902,6 @@ wpa_supplicant_add_interface() { 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 @@ -978,7 +976,7 @@ mac80211_setup_supplicant() { wpa_supplicant_add_network "$ifname" "$freq" "$htmode" "$noscan" fi - wpa_supplicant_add_interface "$ifname" "$mode" "$hostapd_ctrl" + wpa_supplicant_add_interface "$ifname" "$mode" return 0 } diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index f9f0c31babc2..384c5c2eb08d 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -31,7 +31,7 @@ function iface_remove(cfg) wdev_remove(bss.ifname); } -function iface_gen_config(phy, config) +function iface_gen_config(phy, config, start_disabled) { let str = `data: ${join("\n", config.radio.data)} @@ -45,12 +45,69 @@ channel=${config.radio.channel} str += ` ${type}=${bss.ifname} ${join("\n", bss.data)} +`; + if (start_disabled) + str += ` +start_disabled=1 `; } return str; } +function iface_freq_info(iface, config, params) +{ + let freq = params.frequency; + if (!freq) + return null; + + let sec_offset = params.sec_chan_offset; + if (sec_offset != -1 && sec_offset != 1) + sec_offset = 0; + + let width = 0; + for (let line in config.radio.data) { + if (!sec_offset && match(line, /^ht_capab=.*HT40/)) { + sec_offset = null; // auto-detect + continue; + } + + let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth)=(\d+)/); + if (!val) + continue; + + val = int(val[2]); + if (val > width) + width = val; + } + + if (freq < 4000) + width = 0; + + return hostapd.freq_info(freq, sec_offset, width); +} + +function iface_add(phy, config, phy_status) +{ + let config_inline = iface_gen_config(phy, config, !!phy_status); + + let bss = config.bss[0]; + let ret = hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`); + if (ret < 0) + return false; + + if (!phy_status) + return true; + + let iface = hostapd.interfaces[bss.ifname]; + if (!iface) + return false; + + let freq_info = iface_freq_info(iface, config, phy_status); + + return iface.start(freq_info) >= 0; +} + function iface_restart(phy, config, old_config) { iface_remove(old_config); @@ -65,11 +122,18 @@ function iface_restart(phy, config, old_config) let err = wdev_create(phy, bss.ifname, { mode: "ap" }); if (err) hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`); - let config_inline = iface_gen_config(phy, config); let ubus = hostapd.data.ubus; + let phy_status = ubus.call("wpa_supplicant", "phy_status", { phy: phy }); + if (phy_status && phy_status.state == "COMPLETED") { + if (iface_add(phy, config, phy_status)) + return; + + hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`); + } + ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true }); - if (hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`) < 0) + if (!iface_add(phy, config)) hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`); ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false }); } @@ -295,7 +359,6 @@ function iface_load_config(filename) } - let main_obj = { reload: { args: { @@ -344,34 +407,10 @@ let main_obj = { return 0; } - let freq = req.args.frequency; - if (!freq) + if (!req.args.frequency) return libubus.STATUS_INVALID_ARGUMENT; - let sec_offset = req.args.sec_chan_offset; - if (sec_offset != -1 && sec_offset != 1) - sec_offset = 0; - - let width = 0; - for (let line in config.radio.data) { - if (!sec_offset && match(line, /^ht_capab=.*HT40/)) { - sec_offset = null; // auto-detect - continue; - } - - let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth)=(\d+)/); - if (!val) - continue; - - val = int(val[2]); - if (val > width) - width = val; - } - - if (freq < 4000) - width = 0; - - let freq_info = hostapd.freq_info(freq, sec_offset, width); + let freq_info = iface_freq_info(iface, config, req.args); if (!freq_info) return libubus.STATUS_UNKNOWN_ERROR; diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc index 50da7f14ffe4..f8a3fcb52531 100644 --- a/package/network/services/hostapd/files/wpa_supplicant.uc +++ b/package/network/services/hostapd/files/wpa_supplicant.uc @@ -43,6 +43,11 @@ function iface_cb(new_if, old_if) return; } + if (new_if && old_if) + wpas.printf(`Update configuration for interface ${old_if.config.iface}`); + else if (old_if) + wpas.printf(`Remove interface ${old_if.config.iface}`); + if (old_if) iface_stop(old_if); } @@ -106,6 +111,41 @@ let main_obj = { return 0; } }, + phy_status: { + args: { + phy: "" + }, + call: function(req) { + if (!req.args.phy) + return libubus.STATUS_INVALID_ARGUMENT; + + let phy = wpas.data.config[req.args.phy]; + if (!phy) + return libubus.STATUS_NOT_FOUND; + + for (let ifname in phy.data) { + try { + let iface = wpas.interfaces[ifname]; + if (!iface) + continue; + + let status = iface.status(); + if (!status) + continue; + + if (status.state == "INTERFACE_DISABLED") + continue; + + status.ifname = ifname; + return status; + } catch (e) { + continue; + } + } + + return libubus.STATUS_NOT_FOUND; + } + }, config_set: { args: { phy: "", @@ -116,6 +156,7 @@ let main_obj = { if (!req.args.phy) return libubus.STATUS_INVALID_ARGUMENT; + wpas.printf(`Set new config for phy ${req.args.phy}`); try { if (req.args.config) set_config(req.args.phy, req.args.config); diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index f0b4ce4eb82b..0326f6fc82dc 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -362,6 +362,7 @@ out: int ret; hapd->started = 1; + hapd->conf->start_disabled = 0; hostapd_set_freq(hapd, conf->hw_mode, iface->freq, conf->channel, conf->enable_edmg, -- 2.30.2