confd: wifi: Refactor station out from hardware.c

This is run per interface, not per radio so its place
in hardware.c was wrong. This also opens up for multiple
station interfaces per radio, but not for now that it locked
down in a must expression for now.
This commit is contained in:
Mattias Walström
2026-01-23 13:05:30 +01:00
parent 118a7bf1ca
commit 598fbcf326
2 changed files with 120 additions and 178 deletions
+8 -168
View File
@@ -16,8 +16,6 @@
#define XPATH_BASE_ "/ietf-hardware:hardware"
#define HOSTAPD_CONF "/etc/hostapd-%s.conf"
#define HOSTAPD_CONF_NEXT HOSTAPD_CONF"+"
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
#define WPA_SUPPLICANT_CONF_NEXT WPA_SUPPLICANT_CONF"+"
static int dir_cb(const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftwbuf)
@@ -189,82 +187,6 @@ static int wifi_find_interfaces_on_radio(struct lyd_node *ifs, const char *radio
return 0;
}
static int wifi_gen_station(const char *ifname, struct lyd_node *station,
const char *radio, struct lyd_node *config)
{
const char *ssid, *secret_name, *secret, *security_mode;
struct lyd_node *security, *secret_node, *radio_node;
FILE *wpa_supplicant = NULL;
char *security_str = NULL;
const char *country;
int rc = SR_ERR_OK;
/* If station is NULL, we're in scan-only mode (no station container) */
if (station) {
ssid = lydx_get_cattr(station, "ssid");
security = lydx_get_child(station, "security");
security_mode = lydx_get_cattr(security, "mode");
secret_name = lydx_get_cattr(security, "secret");
} else {
ssid = NULL;
security = NULL;
security_mode = "disabled";
secret_name = NULL;
}
radio_node = lydx_get_xpathf(config,
"/hardware/component[name='%s']/wifi-radio", radio);
country = radio_node ? lydx_get_cattr(radio_node, "country-code") : NULL;
if (secret_name && strcmp(security_mode, "disabled") != 0) {
secret_node = lydx_get_xpathf(config,
"/keystore/symmetric-keys/symmetric-key[name='%s']/symmetric-key",
secret_name);
secret = secret_node ? lyd_get_value(secret_node) : NULL;
} else {
secret = NULL;
}
wpa_supplicant = fopenf("w", WPA_SUPPLICANT_CONF_NEXT, ifname);
if (!wpa_supplicant) {
rc = SR_ERR_INTERNAL;
goto out;
}
fprintf(wpa_supplicant,
"ctrl_interface=/run/wpa_supplicant\n"
"autoscan=periodic:10\n"
"ap_scan=1\n");
if (country)
fprintf(wpa_supplicant, "country=%s\n", country);
/* If SSID is present, create network block. Otherwise, scan-only mode */
if (ssid) {
/* Station mode with network configured */
if (!strcmp(security_mode, "disabled")) {
asprintf(&security_str, "key_mgmt=NONE");
} else if (secret) {
asprintf(&security_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret);
}
fprintf(wpa_supplicant,
"network={\n"
"bgscan=\"simple: 30:-45:300\"\n"
"ssid=\"%s\"\n"
"%s\n"
"}\n", ssid, security_str);
free(security_str);
} else {
/* Scan-only mode - no station container configured */
fprintf(wpa_supplicant, "# Scan-only mode - no network configured\n");
}
out:
if (wpa_supplicant)
fclose(wpa_supplicant);
return rc;
}
/* Helper: Find all AP interfaces on a specific radio */
static int wifi_find_radio_aps(struct lyd_node *cifs, const char *radio_name,
char ***ap_list, int *count)
@@ -696,7 +618,7 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
} else if (!strcmp(class, "infix-hardware:wifi")) {
struct lyd_node *interfaces_config, *interfaces_diff;
struct lyd_node **wifi_iface_list = NULL;
struct lyd_node *station, *ap;
struct lyd_node *ap;
struct lyd_node *cwifi_radio;
int wifi_iface_count = 0;
char src[40], dst[40];
@@ -710,58 +632,13 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
case SR_EV_DONE:
interfaces_diff = lydx_get_descendant(diff, "interfaces", "interface", NULL);
/* Handle deleted WiFi interfaces - stop wpa_supplicant */
if (interfaces_diff) {
struct lyd_node *iface, *wifi;
const char *radio;
LYX_LIST_FOR_EACH(interfaces_diff, iface, "interface") {
const char *ifname;
if (lydx_get_op(iface) != LYDX_OP_DELETE)
continue;
wifi = lydx_get_child(iface, "wifi");
if (!wifi)
continue;
radio = lydx_get_cattr(wifi, "radio");
if (!radio || strcmp(radio, name))
continue;
ifname = lydx_get_cattr(iface, "name");
erasef(WPA_SUPPLICANT_CONF, ifname);
erasef(WPA_SUPPLICANT_CONF_NEXT, ifname);
systemf("initctl -bfq disable wifi@%s", ifname);
}
}
wifi_find_interfaces_on_radio(interfaces_diff, name,
&wifi_iface_list, &wifi_iface_count);
if (wifi_iface_count > 0) {
bool running, enabled;
station = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "station", NULL);
ap = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "access-point", NULL);
if (station || !ap || lydx_get_op(ap) == LYDX_OP_DELETE) {
const char *ifname = lydx_get_cattr(wifi_iface_list[0], "name");
running = !systemf("initctl -bfq status wpa_supplicant:%s", ifname);
if (lydx_get_op(station) == LYDX_OP_DELETE) {
erasef(WPA_SUPPLICANT_CONF, ifname);
erasef(WPA_SUPPLICANT_CONF_NEXT, ifname);
systemf("initctl -bfq disable wifi@%s", ifname);
} else {
snprintf(src, sizeof(src), WPA_SUPPLICANT_CONF_NEXT, ifname);
snprintf(dst, sizeof(dst), WPA_SUPPLICANT_CONF, ifname);
running = !systemf("initctl -bfq status wpa_supplicant:%s", ifname);
enabled = fexistf(WPA_SUPPLICANT_CONF_NEXT, ifname);
if (enabled) {
(void)rename(src, dst);
if (running)
systemf("initctl -bfq touch wifi@%s", ifname);
else
systemf("initctl -bfq enable wifi@%s", ifname);
}
}
} else if (wifi_iface_count > 0) {
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);
@@ -802,48 +679,11 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
if (!wifi_iface_count)
continue;
/*
* A radio operates in one of three modes:
* 1. Station mode: One station interface (client mode)
* 2. AP mode: One or more AP interfaces (hostapd multi-SSID)
* 3. Scan-only mode: WiFi interface with radio but no mode configured
*
* Check for station first - there can be at most one per radio.
* If no station or AP is configured, default to scan-only mode.
*/
station = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "station", NULL);
ap = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "access-point", NULL);
if (wifi_iface_count == 1 && station) {
/* Station mode (with or without SSID for scan-only) */
struct lyd_node *iface = wifi_iface_list[0];
if (lydx_is_enabled(iface, "enabled")) {
const char *ifname = lydx_get_cattr(iface, "name");
rc = wifi_gen_station(ifname, station, name, config);
if (rc != SR_ERR_OK) {
ERROR("Failed to generate station config for %s", ifname);
goto next;
}
}
} else if (!station && !ap) {
/* No station/AP configured - default to scan-only mode */
struct lyd_node *iface = wifi_iface_list[0];
if (lydx_is_enabled(iface, "enabled")) {
const char *ifname = lydx_get_cattr(iface, "name");
rc = wifi_gen_station(ifname, NULL, name, config);
if (rc != SR_ERR_OK) {
ERROR("Failed to generate scan-only config for %s", ifname);
goto next;
}
}
} else {
/* Multiple interfaces or APs */
rc = wifi_gen_aps_on_radio(name, interfaces_config, cwifi_radio, config);
if (rc != SR_ERR_OK) {
ERROR("Failed to generate AP config for radio %s", name);
goto next;
}
}
next:
/* Generate AP config (hostapd) for all APs on this radio */
rc = wifi_gen_aps_on_radio(name, interfaces_config, cwifi_radio, config);
if (rc != SR_ERR_OK)
ERROR("Failed to generate AP config for radio %s", name);
/* Free the interface list */
free(wifi_iface_list);
wifi_iface_list = NULL;
+112 -10
View File
@@ -3,12 +3,14 @@
#include "interfaces.h"
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
/*
* WiFi Interface Management
*
* This file handles only virtual WiFi interface creation/deletion.
* WiFi daemon configuration (hostapd/wpa_supplicant) is handled by
* hardware.c when the WiFi radio (phy) is configured.
* This file handles virtual WiFi interface creation/deletion and
* station mode configuration (wpa_supplicant). Access point mode
* configuration (hostapd) is handled by hardware.c.
*/
/*
@@ -19,9 +21,12 @@ typedef enum wifi_mode_t {
wifi_ap,
wifi_unknown
} wifi_mode_t;
static wifi_mode_t wifi_get_mode(struct lyd_node *wifi)
{
if (lydx_get_child(wifi, "access-point"))
struct lyd_node *ap = lydx_get_child(wifi, "access-point");
if (ap && (lydx_get_op(ap) != LYDX_OP_DELETE))
return wifi_ap;
else
return wifi_station; /* Need to return station even if "station" also is false, since that is the default scanning mode */
@@ -29,20 +34,106 @@ static wifi_mode_t wifi_get_mode(struct lyd_node *wifi)
int wifi_mode_changed(struct lyd_node *wifi)
{
struct lyd_node *station, *ap;
enum lydx_op station_op, ap_op;
struct lyd_node *ap;
enum lydx_op ap_op;
if (!wifi)
return 0;
station = lydx_get_child(wifi, "station");
ap = lydx_get_child(wifi, "access-point");
if (station)
station_op = lydx_get_op(station);
if (ap)
ap_op = lydx_get_op(ap);
return ((station && station_op == LYDX_OP_DELETE) || (ap && ap_op == LYDX_OP_DELETE));
ERROR("MODE CHANGED: %d", ap && (ap_op == LYDX_OP_CREATE || ap_op == LYDX_OP_DELETE));
return (ap && (ap_op == LYDX_OP_CREATE || ap_op == LYDX_OP_DELETE));
}
/*
* Generate wpa_supplicant config for station mode
*/
static int wifi_gen_station(struct lyd_node *cif)
{
const char *ifname, *ssid, *secret_name, *secret, *security_mode, *radio;
struct lyd_node *security, *secret_node, *radio_node, *station, *wifi;
FILE *wpa_supplicant = NULL;
char *security_str = NULL;
const char *country;
int rc = SR_ERR_OK;
ifname = lydx_get_cattr(cif, "name");
wifi = lydx_get_child(cif, "wifi");
if (!wifi)
return SR_ERR_OK;
radio = lydx_get_cattr(wifi, "radio");
station = lydx_get_child(wifi, "station");
/* If station is NULL, we're in scan-only mode (no station container) */
if (station) {
ssid = lydx_get_cattr(station, "ssid");
security = lydx_get_child(station, "security");
security_mode = lydx_get_cattr(security, "mode");
secret_name = lydx_get_cattr(security, "secret");
} else {
ssid = NULL;
security = NULL;
security_mode = "disabled";
secret_name = NULL;
}
radio_node = lydx_get_xpathf(cif,
"../../hardware/component[name='%s']/wifi-radio", radio);
country = lydx_get_cattr(radio_node, "country-code");
if (secret_name && strcmp(security_mode, "disabled") != 0) {
secret_node = lydx_get_xpathf(cif,
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
secret_name);
secret = lydx_get_cattr(secret_node, "symmetric-key");
} else {
secret = NULL;
}
wpa_supplicant = fopenf("w", WPA_SUPPLICANT_CONF, ifname);
if (!wpa_supplicant) {
rc = SR_ERR_INTERNAL;
goto out;
}
fprintf(wpa_supplicant,
"ctrl_interface=/run/wpa_supplicant\n"
"autoscan=periodic:10\n"
"ap_scan=1\n");
if (country)
fprintf(wpa_supplicant, "country=%s\n", country);
/* If SSID is present, create network block. Otherwise, scan-only mode */
if (ssid) {
/* Station mode with network configured */
if (!strcmp(security_mode, "disabled")) {
asprintf(&security_str, "key_mgmt=NONE");
} else if (secret) {
asprintf(&security_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret);
}
fprintf(wpa_supplicant,
"network={\n"
"bgscan=\"simple: 30:-45:300\"\n"
"ssid=\"%s\"\n"
"%s\n"
"}\n", ssid, security_str);
free(security_str);
} else {
/* Scan-only mode - no station container configured */
fprintf(wpa_supplicant, "# Scan-only mode - no network configured\n");
}
out:
if (wpa_supplicant)
fclose(wpa_supplicant);
return rc;
}
/*
* Add WiFi virtual interface using iw
*/
@@ -83,6 +174,9 @@ int wifi_add_iface(struct lyd_node *cif, struct dagger *net)
switch(mode) {
case wifi_station:
fprintf(iw, "iw phy %s interface add %s type managed\n", radio, ifname);
wifi_gen_station(cif);
fprintf(iw, "initctl -bfq enable wifi@%s\n", ifname);
fprintf(iw, "initctl -bfq touch wifi@%s\n", ifname);
break;
case wifi_ap:
fprintf(iw, "iw phy %s interface add %s type __ap\n", radio, ifname);
@@ -102,6 +196,7 @@ out:
*/
int wifi_del_iface(struct lyd_node *dif, struct dagger *net)
{
struct lyd_node *wifi;
const char *ifname;
FILE *iw;
@@ -117,6 +212,13 @@ int wifi_del_iface(struct lyd_node *dif, struct dagger *net)
fprintf(iw, "ip link set %s down\n", ifname); /* Required to change modes. */
fprintf(iw, "iw dev %s disconnect\n", ifname);
fprintf(iw, "iw dev %s del\n", ifname);
wifi = lydx_get_child(dif, "wifi");
if (wifi && wifi_get_mode(wifi) == wifi_ap) { /* if not station or scanning mode */
erasef(WPA_SUPPLICANT_CONF, ifname);
fprintf(iw, "initctl -bfq disable wifi@%s\n", ifname);
}
fclose(iw);
return SR_ERR_OK;