confd: wifi: Refactor to use one hostapd for all radios

This is how hostapd should be used, this enables real
band steering, since the daemon has knownledge about
all bands.
This commit is contained in:
Mattias Walström
2026-06-04 15:27:56 +02:00
parent 420c2cbfe7
commit c4c49ee3c7
2 changed files with 46 additions and 16 deletions
@@ -1,3 +0,0 @@
service <!> name:hostapd :%i \
[2345] hostapd -P/var/run/hostapd-%i.pid /etc/hostapd-%i.conf \
-- Wi-Fi Access Point @%i
+46 -13
View File
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <fnmatch.h>
#include <ftw.h>
#include <glob.h>
#include <jansson.h>
#include <libgen.h>
#include <limits.h>
@@ -19,6 +20,7 @@
#define XPATH_BASE_ "/ietf-hardware:hardware"
#define HOSTAPD_CONF "/etc/hostapd-%s.conf"
#define HOSTAPD_CONF_NEXT HOSTAPD_CONF"+"
#define HOSTAPD_SERVICE "/etc/finit.d/available/hostapd.conf"
#define GPSD_CONF "/etc/finit.d/available/gpsd.conf"
#define GPSD_CONF_NEXT GPSD_CONF"+"
#define GPSD_MAX_DEVICES 4
@@ -1177,6 +1179,7 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
struct lyd_node *difs = NULL, *dif = NULL;
int rc = SR_ERR_OK;
int gps_changed = 0;
int wifi_changed = 0;
if (!lydx_find_xpathf(diff, XPATH_BASE_))
return SR_ERR_OK;
@@ -1238,34 +1241,25 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
wifi_find_interfaces_on_radio(interfaces_diff, name,
&wifi_iface_list, &wifi_iface_count);
if (wifi_iface_count > 0) {
bool running, enabled;
ap = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "access-point", NULL);
if (ap && lydx_get_op(ap) != LYDX_OP_DELETE) {
/* AP mode - activate hostapd for radio */
snprintf(src, sizeof(src), HOSTAPD_CONF_NEXT, name);
snprintf(dst, sizeof(dst), HOSTAPD_CONF, name);
running = !systemf("initctl -bfq status hostapd:%s", name);
enabled = fexistf(HOSTAPD_CONF_NEXT, name);
if (enabled) {
if (fexistf(HOSTAPD_CONF_NEXT, name)) {
(void)rename(src, dst);
ap_interfaces++;
if (running)
finit_reloadf("hostapd@%s", name);
else
finit_enablef("hostapd@%s", name);
}
}
}
if (!ap_interfaces) {
finit_disablef("hostapd@%s", name);
erasef(HOSTAPD_CONF, name);
erasef(HOSTAPD_CONF_NEXT, name);
}
free(wifi_iface_list);
/* All radios share one hostapd process; the service is
* (re)generated after the component loop below. */
wifi_changed = 1;
continue;
default:
continue;
@@ -1361,6 +1355,45 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
break;
}
}
/*
* All AP radios run in a single hostapd process so that
* cross-radio directives (e.g. no_probe_resp_if_seen_on for band
* steering) resolve: those only consult interfaces managed by the
* same process. Regenerate the combined service from every staged
* /etc/hostapd-<radio>.conf and (re)start it. A restart re-reads
* all radio configs, so band edits and radio add/remove both apply.
*/
if (wifi_changed && event == SR_EV_DONE) {
glob_t gl = { 0 };
if (glob("/etc/hostapd-*.conf", 0, NULL, &gl) == 0 && gl.gl_pathc > 0) {
FILE *fp;
fp = fopen(HOSTAPD_SERVICE, "w");
if (!fp) {
ERRNO("Could not open " HOSTAPD_SERVICE);
rc = SR_ERR_INTERNAL;
} else {
size_t i;
fprintf(fp, "# Generated by confd, do not edit.\n");
fprintf(fp, "service <!> name:hostapd \\\n");
fprintf(fp, "\t[2345] hostapd -P /run/hostapd.pid");
for (i = 0; i < gl.gl_pathc; i++)
fprintf(fp, " %s", gl.gl_pathv[i]);
fprintf(fp, " \\\n\t-- Wi-Fi Access Points\n");
fclose(fp);
finit_enable("hostapd");
finit_reload("hostapd");
}
} else {
unlink(HOSTAPD_SERVICE);
finit_disable("hostapd");
}
globfree(&gl);
}
err:
return rc;