From: Felix Fietkau Date: Thu, 11 Jan 2024 08:14:59 +0000 (+0100) Subject: hostapd: make ubus calls to wpa_supplicant asynchronous X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=3df9322771cc932e5b82438634337fcca6cc97bc;p=openwrt%2Fstaging%2F981213.git hostapd: make ubus calls to wpa_supplicant asynchronous This fixes a deadlock issue where depending on the setup order, hostapd and wpa_supplicant could end up waiting for each other Reported-by: Michael-cy Lee (李峻宇) Signed-off-by: Felix Fietkau --- diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index b85f523b35..0c89cd71cc 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -2,9 +2,10 @@ let libubus = require("ubus"); import { open, readfile } from "fs"; import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common"; -let ubus = libubus.connect(); +let ubus = libubus.connect(null, 60); hostapd.data.config = {}; +hostapd.data.pending_config = {}; hostapd.data.file_fields = { vlan_file: true, @@ -122,17 +123,111 @@ function iface_config_macaddr_list(config) return macaddr_list; } -function iface_update_supplicant_macaddr(phy, config) +function __iface_pending_next(pending, state, ret, data) { - let macaddr_list = []; - for (let i = 0; i < length(config.bss); i++) - push(macaddr_list, config.bss[i].bssid); - ubus.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list }); + let config = pending.config; + let phydev = pending.phydev; + let phy = pending.phy; + + if (pending.defer) + pending.defer.abort(); + delete pending.defer; + switch (state) { + case "init": + let macaddr_list = []; + for (let i = 0; i < length(config.bss); i++) + push(macaddr_list, config.bss[i].bssid); + pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list }); + return "create_bss"; + case "create_bss": + 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}`); + return null; + } + + pending.call("wpa_supplicant", "phy_status", { phy: phy }); + return "check_phy"; + case "check_phy": + let phy_status = data; + if (phy_status && phy_status.state == "COMPLETED") { + if (iface_add(phy, config, phy_status)) + return "done"; + + hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`); + } + pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true }); + return "wpas_stopped"; + case "wpas_stopped": + if (!iface_add(phy, config)) + hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`); + pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false }); + return null; + case "done": + default: + delete hostapd.data.pending_config[phy]; + break; + } +} + +function iface_pending_next(ret, data) +{ + let pending = true; + let cfg = this; + + while (pending) { + this.next_state = __iface_pending_next(cfg, this.next_state, ret, data); + if (!this.next_state) { + __iface_pending_next(cfg, "done"); + return; + } + pending = !this.defer; + } +} + +function iface_pending_abort() +{ + this.next_state = "done"; + this.next(); +} + +function iface_pending_ubus_call(obj, method, arg) +{ + let ubus = hostapd.data.ubus; + let pending = this; + this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) }); +} + +const iface_pending_proto = { + next: iface_pending_next, + call: iface_pending_ubus_call, + abort: iface_pending_abort, +}; + +function iface_pending_init(phydev, config) +{ + let phy = phydev.name; + + let pending = proto({ + next_state: "init", + phydev: phydev, + phy: phy, + config: config, + next: iface_pending_next, + }, iface_pending_proto); + + hostapd.data.pending_config[phy] = pending; + pending.next(); } function iface_restart(phydev, config, old_config) { let phy = phydev.name; + let pending = hostapd.data.pending_config[phy]; + + if (pending) + pending.abort(); hostapd.remove_iface(phy); iface_remove(old_config); @@ -150,26 +245,7 @@ function iface_restart(phydev, config, old_config) bss.bssid = phydev.macaddr_next(); } - iface_update_supplicant_macaddr(phy, config); - - 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 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 (!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 }); + iface_pending_init(phydev, config); } function array_to_obj(arr, key, start) @@ -274,6 +350,9 @@ function iface_reload_config(phydev, config, old_config) if (is_equal(old_config.bss, config.bss)) return true; + if (hostapd.data.pending_config[phy]) + return false; + if (!old_config.bss || !old_config.bss[0]) return false;