Преглед на файлове

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 (李峻宇) <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
Felix Fietkau преди 1 година
родител
ревизия
3df9322771
променени са 1 файла, в които са добавени 105 реда и са изтрити 26 реда
  1. 105 26
      package/network/services/hostapd/files/hostapd.uc

+ 105 - 26
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;