hostapd: ucode: restart on config-initiated channel switch failure
iface.switch_channel() only reports whether a CSA was started, not whether it completed, so a config-driven channel switch that the driver never finishes would leave the AP stuck without recovery. After issuing the CSA, arm a timer and, if the switch has not completed by the deadline (csa_in_progress still set), fall back to a full interface restart. The timer is armed only on the config-initiated path, so ubus- and apsta-triggered channel switches keep their own recovery. Add an iface.csa_in_progress() ucode method wrapping hostapd_csa_in_progress(). Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
55b9263596
commit
c6d20bbf51
2 changed files with 61 additions and 0 deletions
|
|
@ -1,4 +1,5 @@
|
|||
let libubus = require("ubus");
|
||||
import * as uloop from "uloop";
|
||||
import { open, readfile, access } from "fs";
|
||||
import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask, wdev_set_up } from "common";
|
||||
|
||||
|
|
@ -320,11 +321,22 @@ function iface_macaddr_init(phydev, config, macaddr_list)
|
|||
return phydev.macaddr_init(macaddr_list, macaddr_data);
|
||||
}
|
||||
|
||||
function csa_timer_cancel(name)
|
||||
{
|
||||
let timers = hostapd.data.csa_timer;
|
||||
if (timers && timers[name]) {
|
||||
timers[name].cancel();
|
||||
delete timers[name];
|
||||
}
|
||||
}
|
||||
|
||||
function iface_restart(phydev, config, old_config)
|
||||
{
|
||||
let phy = phydev.name;
|
||||
let pending = hostapd.data.pending_config[phy];
|
||||
|
||||
csa_timer_cancel(phy);
|
||||
|
||||
if (pending)
|
||||
pending.abort();
|
||||
|
||||
|
|
@ -580,6 +592,22 @@ function iface_channel_is_dfs(radio)
|
|||
(freq >= 5490 && freq <= 5730));
|
||||
}
|
||||
|
||||
function iface_csa_check(name)
|
||||
{
|
||||
if (hostapd.data.csa_timer)
|
||||
delete hostapd.data.csa_timer[name];
|
||||
|
||||
let config = hostapd.data.config[name];
|
||||
let iface = hostapd.interfaces[name];
|
||||
if (!config || !iface || !iface.csa_in_progress())
|
||||
return;
|
||||
|
||||
hostapd.printf(`Config channel switch on phy ${name} did not complete, restarting`);
|
||||
let phydev = phy_open(config.phy, config.radio_idx);
|
||||
if (phydev)
|
||||
iface_restart(phydev, config, config);
|
||||
}
|
||||
|
||||
function iface_channel_switch(name, config)
|
||||
{
|
||||
let radio = config.radio;
|
||||
|
|
@ -618,6 +646,27 @@ function iface_channel_switch(name, config)
|
|||
return false;
|
||||
|
||||
hostapd.printf(`Channel switch to ${radio.channel} on phy ${name}`);
|
||||
|
||||
/*
|
||||
* Verify the switch actually completes; if the driver never finishes it,
|
||||
* fall back to a full restart. Armed only here, so ubus- or apsta-
|
||||
* triggered channel switches are left to their own recovery.
|
||||
*/
|
||||
hostapd.data.csa_timer ??= {};
|
||||
csa_timer_cancel(name);
|
||||
|
||||
let beacon_int = 100;
|
||||
for (let line in config.radio.data) {
|
||||
let m = match(line, /^beacon_int=([0-9]+)/);
|
||||
if (m) {
|
||||
beacon_int = int(m[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hostapd.data.csa_timer[name] = uloop.timer(freq_info.csa_count * beacon_int + 2000,
|
||||
() => iface_csa_check(name));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -769,6 +769,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
|
|||
return ucv_boolean_new(!ret);
|
||||
}
|
||||
|
||||
static uc_value_t *
|
||||
uc_hostapd_iface_csa_in_progress(uc_vm_t *vm, size_t nargs)
|
||||
{
|
||||
struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
|
||||
|
||||
if (!iface)
|
||||
return NULL;
|
||||
|
||||
return ucv_boolean_new(hostapd_csa_in_progress(iface));
|
||||
}
|
||||
|
||||
static uc_value_t *
|
||||
uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
|
||||
{
|
||||
|
|
@ -1225,6 +1236,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
|
|||
{ "stop", uc_hostapd_iface_stop },
|
||||
{ "start", uc_hostapd_iface_start },
|
||||
{ "switch_channel", uc_hostapd_iface_switch_channel },
|
||||
{ "csa_in_progress", uc_hostapd_iface_csa_in_progress },
|
||||
};
|
||||
uc_value_t *data, *proto;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue