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 (李峻宇) <Michael-cy.Lee@mediatek.com> Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
2fe8ecd880
commit
3df9322771
1 changed files with 105 additions and 26 deletions
|
@ -2,9 +2,10 @@ let libubus = require("ubus");
|
||||||
import { open, readfile } from "fs";
|
import { open, readfile } from "fs";
|
||||||
import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
|
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.config = {};
|
||||||
|
hostapd.data.pending_config = {};
|
||||||
|
|
||||||
hostapd.data.file_fields = {
|
hostapd.data.file_fields = {
|
||||||
vlan_file: true,
|
vlan_file: true,
|
||||||
|
@ -122,17 +123,111 @@ function iface_config_macaddr_list(config)
|
||||||
return macaddr_list;
|
return macaddr_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function iface_update_supplicant_macaddr(phy, config)
|
function __iface_pending_next(pending, state, ret, data)
|
||||||
{
|
{
|
||||||
let macaddr_list = [];
|
let config = pending.config;
|
||||||
for (let i = 0; i < length(config.bss); i++)
|
let phydev = pending.phydev;
|
||||||
push(macaddr_list, config.bss[i].bssid);
|
let phy = pending.phy;
|
||||||
ubus.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
|
|
||||||
|
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)
|
function iface_restart(phydev, config, old_config)
|
||||||
{
|
{
|
||||||
let phy = phydev.name;
|
let phy = phydev.name;
|
||||||
|
let pending = hostapd.data.pending_config[phy];
|
||||||
|
|
||||||
|
if (pending)
|
||||||
|
pending.abort();
|
||||||
|
|
||||||
hostapd.remove_iface(phy);
|
hostapd.remove_iface(phy);
|
||||||
iface_remove(old_config);
|
iface_remove(old_config);
|
||||||
|
@ -150,26 +245,7 @@ function iface_restart(phydev, config, old_config)
|
||||||
bss.bssid = phydev.macaddr_next();
|
bss.bssid = phydev.macaddr_next();
|
||||||
}
|
}
|
||||||
|
|
||||||
iface_update_supplicant_macaddr(phy, config);
|
iface_pending_init(phydev, 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 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function array_to_obj(arr, key, start)
|
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))
|
if (is_equal(old_config.bss, config.bss))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (hostapd.data.pending_config[phy])
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!old_config.bss || !old_config.bss[0])
|
if (!old_config.bss || !old_config.bss[0])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue