Skip to content
Open

Misc2 #1352

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions doc/wifi.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ admin@example:/config/hardware/component/radio0/wifi-radio/> leave
- `country-code`: Two-letter ISO 3166-1 code - determines allowed channels and maximum power. Examples: US, DE, GB, SE, FR, JP. **Must match your physical location for legal compliance.**
- `band`: 2.4GHz, 5GHz, or 6GHz (required for AP mode). Band selection automatically enables appropriate WiFi standards (2.4GHz: 802.11n, 5GHz: 802.11n/ac, 6GHz: 802.11n/ac/ax)
- `channel`: Channel number (1-196) or "auto" (required for AP mode). When set to "auto", defaults to channel 6 for 2.4GHz, channel 36 for 5GHz, or channel 109 for 6GHz
- `enable-wifi6`: Boolean (default: false). Opt-in to enable WiFi 6 (802.11ax) on 2.4GHz and 5GHz bands. The 6GHz band always uses WiFi 6 regardless of this setting
- `enable-80211ax`: Boolean (default: false). Opt-in to enable 802.11ax (WiFi 6) on 2.4GHz and 5GHz bands. The 6GHz band always uses 802.11ax regardless of this setting

> [!NOTE]
> TX power and channel width are automatically determined by the driver based on regulatory constraints, PHY mode, and hardware capabilities.
Expand All @@ -105,7 +105,7 @@ admin@example:/config/> edit hardware component radio0 wifi-radio
admin@example:/config/hardware/component/radio0/wifi-radio/> set country-code DE
admin@example:/config/hardware/component/radio0/wifi-radio/> set band 5GHz
admin@example:/config/hardware/component/radio0/wifi-radio/> set channel 36
admin@example:/config/hardware/component/radio0/wifi-radio/> set enable-wifi6 true
admin@example:/config/hardware/component/radio0/wifi-radio/> set enable-80211ax true
admin@example:/config/hardware/component/radio0/wifi-radio/> leave
```

Expand All @@ -121,7 +121,7 @@ admin@example:/config/hardware/component/radio0/wifi-radio/> leave
- Older WiFi 5/4 clients can still connect but won't use WiFi 6 features

> [!NOTE]
> The 6GHz band always uses WiFi 6 (802.11ax) regardless of the `enable-wifi6`
> The 6GHz band always uses WiFi 6 (802.11ax) regardless of the `enable-80211ax`
> setting, as WiFi 6E requires 802.11ax support.

## Discovering Available Networks (Scanning)
Expand Down Expand Up @@ -379,6 +379,67 @@ radio settings, and `show interface` to see all active AP interfaces.
> AP and Station modes cannot be mixed on the same radio. All virtual interfaces
> on a radio must be the same mode (all APs or all Stations).

## Fast Roaming Between Access Points

Fast roaming enables seamless client handoff between access points through
802.11k/r/v standards. These features can be enabled individually based on
your requirements.

### 802.11r - Fast BSS Transition

Enable 802.11r for fast handoff (<50ms) between APs with the same SSID:

```
admin@example:/config/interface/wifi0/> set wifi access-point roaming enable-80211r true
admin@example:/config/interface/wifi0/> set wifi access-point roaming mobility-domain 4f57
```

**Requirements:**
- All APs in roaming group must have **identical** SSID
- All APs must have **identical** passphrase (same keystore secret)
- All APs must use the **same mobility-domain** identifier

The mobility-domain defaults to `4f57` if not specified.

### 802.11k - Radio Resource Management

Enable 802.11k for client neighbor discovery and better roaming decisions:

```
admin@example:/config/interface/wifi0/> set wifi access-point roaming enable-80211k true
```

Enables neighbor reports and beacon reports, allowing clients to discover
nearby APs before roaming.

### 802.11v - BSS Transition Management

Enable 802.11v for network-assisted roaming:

```
admin@example:/config/interface/wifi0/> set wifi access-point roaming enable-80211v true
```

Allows APs to suggest better APs to clients, improving roaming decisions.

### Recommended Configuration

For optimal roaming experience, enable all three features:

```
admin@example:/config/interface/wifi0/> set wifi access-point roaming enable-80211k true
admin@example:/config/interface/wifi0/> set wifi access-point roaming enable-80211r true
admin@example:/config/interface/wifi0/> set wifi access-point roaming enable-80211v true
admin@example:/config/interface/wifi0/> set wifi access-point roaming mobility-domain 4f57
```

Repeat for all APs that should participate in the roaming group.

> [!NOTE]
> Not all client devices support all roaming features. Modern devices typically
> support 802.11k/r/v, but older devices may only support basic roaming without
> fast transition.

### AP as Bridge Port

WiFi AP interfaces can be added to bridges to integrate wireless devices
Expand Down
31 changes: 25 additions & 6 deletions src/confd/src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <srx/srx_val.h>
#include <srx/common.h>
#include <srx/lyx.h>

#include "interfaces.h"
#include "core.h"

struct confd confd;
Expand Down Expand Up @@ -144,20 +146,37 @@ static confd_dependency_t handle_dependencies(struct lyd_node **diff, struct lyd
struct lyd_node *dif;

LYX_LIST_FOR_EACH(difs, dif, "interface") {
struct lyd_node *dwifi, *radio_node;
struct lyd_node *dwifi, *dcustom_phys, *radio_node, *cwifi;
const char *ifname, *radio_name;
char xpath[256];

/* Check if this interface has a wifi container in the diff */
dwifi = lydx_get_child(dif, "wifi");
if (!dwifi)
continue;
bool is_wifi_change = false;

/* Get the interface name */
ifname = lydx_get_cattr(dif, "name");
if (!ifname)
continue;

if (iftype_from_iface(dif) != IFT_WIFI)
continue;

/* Check if this interface has a wifi container in the diff */
dwifi = lydx_get_child(dif, "wifi");
if (dwifi)
is_wifi_change = true;

/* Check if custom-phys-address changed on a WiFi interface */
if (!is_wifi_change) {
dcustom_phys = lydx_get_child(dif, "custom-phys-address");
if (dcustom_phys) {
/* Check if this interface is a WiFi interface in the config */
cwifi = lydx_get_xpathf(config,
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi",
ifname);
if (cwifi)
is_wifi_change = true;
}
}

/* Get radio reference from config tree using xpath */
radio_node = lydx_get_xpathf(config,
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/radio",
Expand Down
119 changes: 103 additions & 16 deletions src/confd/src/hardware.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@ static int wifi_find_radio_aps(struct lyd_node *cifs, const char *radio_name,
static int wifi_gen_bss_section(FILE *hostapd, struct lyd_node *cifs, const char *ifname, struct lyd_node *config)
{
const char *ssid, *hidden, *security_mode, *secret_name, *secret;
struct lyd_node *cif, *wifi, *ap, *security, *secret_node;
const char *mobility_domain = NULL;
struct lyd_node *cif, *wifi, *ap, *security, *secret_node, *roaming;
bool enable_80211k, enable_80211r, enable_80211v;
char bssid[18];

/* Find the interface node for this BSS */
Expand Down Expand Up @@ -367,28 +369,46 @@ static int wifi_gen_bss_section(FILE *hostapd, struct lyd_node *cifs, const char
}
}

/* Check 802.11k/r/v configuration */
roaming = lydx_get_child(ap, "roaming");
enable_80211k = roaming && lydx_get_bool(roaming, "enable-80211k");
enable_80211r = roaming && lydx_get_bool(roaming, "enable-80211r");
enable_80211v = roaming && lydx_get_bool(roaming, "enable-80211v");

if (enable_80211r)
mobility_domain = lydx_get_cattr(roaming, "mobility-domain");

if (!strcmp(security_mode, "open")) {
fprintf(hostapd, "# Open network\n");
fprintf(hostapd, "auth_algs=1\n");
} else if (!strcmp(security_mode, "wpa2-personal")) {
fprintf(hostapd, "# WPA2-Personal\n");
fprintf(hostapd, "wpa=2\n");
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK\n");
if (enable_80211r)
fprintf(hostapd, "wpa_key_mgmt=FT-PSK WPA-PSK\n");
else
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK\n");
fprintf(hostapd, "wpa_pairwise=CCMP\n");
if (secret)
fprintf(hostapd, "wpa_passphrase=%s\n", secret);
} else if (!strcmp(security_mode, "wpa3-personal")) {
fprintf(hostapd, "# WPA3-Personal\n");
fprintf(hostapd, "wpa=2\n");
fprintf(hostapd, "wpa_key_mgmt=SAE\n");
if (enable_80211r)
fprintf(hostapd, "wpa_key_mgmt=FT-SAE SAE\n");
else
fprintf(hostapd, "wpa_key_mgmt=SAE\n");
fprintf(hostapd, "rsn_pairwise=CCMP\n");
if (secret)
fprintf(hostapd, "sae_password=%s\n", secret);
fprintf(hostapd, "ieee80211w=2\n");
} else if (!strcmp(security_mode, "wpa2-wpa3-personal")) {
fprintf(hostapd, "# WPA2/WPA3 Mixed\n");
fprintf(hostapd, "wpa=2\n");
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK SAE\n");
if (enable_80211r)
fprintf(hostapd, "wpa_key_mgmt=FT-PSK FT-SAE WPA-PSK SAE\n");
else
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK SAE\n");
fprintf(hostapd, "rsn_pairwise=CCMP\n");
if (secret) {
fprintf(hostapd, "wpa_passphrase=%s\n", secret);
Expand All @@ -397,6 +417,28 @@ static int wifi_gen_bss_section(FILE *hostapd, struct lyd_node *cifs, const char
fprintf(hostapd, "ieee80211w=1\n");
}

/* 802.11r: Fast BSS Transition */
if (enable_80211r) {
fprintf(hostapd, "# Fast BSS Transition (802.11r)\n");
fprintf(hostapd, "mobility_domain=%s\n", mobility_domain);
fprintf(hostapd, "ft_over_ds=1\n");
fprintf(hostapd, "ft_psk_generate_local=1\n");
fprintf(hostapd, "nas_identifier=%s.%s\n", ifname, mobility_domain);
}

/* 802.11k: Radio Resource Management */
if (enable_80211k) {
fprintf(hostapd, "# Radio Resource Management (802.11k)\n");
fprintf(hostapd, "rrm_neighbor_report=1\n");
fprintf(hostapd, "rrm_beacon_report=1\n");
}

/* 802.11v: BSS Transition Management */
if (enable_80211v) {
fprintf(hostapd, "# BSS Transition Management (802.11v)\n");
fprintf(hostapd, "bss_transition=1\n");
}

return 0;
}

Expand All @@ -405,16 +447,19 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
struct lyd_node *radio_node, struct lyd_node *config)
{
const char *ssid, *hidden, *security_mode, *secret_name, *secret;
const char *mobility_domain = NULL;
struct lyd_node *primary_cif, *cif;
struct lyd_node *primary_wifi, *primary_ap;
struct lyd_node *security, *secret_node;
struct lyd_node *security, *secret_node, *roaming;
const char *country, *channel, *band;
const char *primary_ifname;
bool enable_80211k, enable_80211r, enable_80211v;
char hostapd_conf[256];
char **ap_list = NULL;
FILE *hostapd = NULL;
bool wifi6_enabled;
bool ax_enabled;
int ap_count = 0;
char bssid[18];
int i;

int rc = SR_ERR_OK;
Expand Down Expand Up @@ -458,7 +503,7 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
country = lydx_get_cattr(radio_node, "country-code");
band = lydx_get_cattr(radio_node, "band");
channel = lydx_get_cattr(radio_node, "channel");
wifi6_enabled = lydx_get_bool(radio_node, "enable_wifi6");
ax_enabled = lydx_get_bool(radio_node, "enable-80211ax");

/* Get secret from keystore if not open network */
if (secret_name && strcmp(security_mode, "open") != 0) {
Expand All @@ -471,6 +516,15 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
}
}

/* Check 802.11k/r/v configuration */
roaming = lydx_get_child(primary_ap, "roaming");
enable_80211k = roaming && lydx_get_bool(roaming, "enable-80211k");
enable_80211r = roaming && lydx_get_bool(roaming, "enable-80211r");
enable_80211v = roaming && lydx_get_bool(roaming, "enable-80211v");

if (enable_80211r)
mobility_domain = lydx_get_cattr(roaming, "mobility-domain");

snprintf(hostapd_conf, sizeof(hostapd_conf), HOSTAPD_CONF_NEXT, radio_name);

hostapd = fopen(hostapd_conf, "w");
Expand All @@ -492,7 +546,6 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
fprintf(hostapd, "ctrl_interface=/run/hostapd\n");

/* Set BSSID if custom MAC is configured */
char bssid[18];
if (!interface_get_phys_addr(primary_cif, bssid))
fprintf(hostapd, "bssid=%s\n", bssid);
fprintf(hostapd, "\n");
Expand Down Expand Up @@ -544,20 +597,20 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,

if (band) {
if (!strcmp(band, "2.4GHz")) {
/* 2.4GHz: Enable 802.11n (HT), optionally WiFi 6 */
/* 2.4GHz: Enable 802.11n (HT), optionally 802.11ax */
fprintf(hostapd, "ieee80211n=1\n");
if (wifi6_enabled) {
if (ax_enabled) {
fprintf(hostapd, "ieee80211ax=1\n");
}
} else if (!strcmp(band, "5GHz")) {
/* 5GHz: Enable 802.11n and 802.11ac, optionally WiFi 6 */
/* 5GHz: Enable 802.11n and 802.11ac, optionally 802.11ax */
fprintf(hostapd, "ieee80211n=1\n");
fprintf(hostapd, "ieee80211ac=1\n");
if (wifi6_enabled) {
if (ax_enabled) {
fprintf(hostapd, "ieee80211ax=1\n");
}
} else if (!strcmp(band, "6GHz")) {
/* 6GHz: Enable 802.11ax (WiFi 6E required) */
/* 6GHz: Enable 802.11ax (required for 6GHz) */
fprintf(hostapd, "ieee80211n=1\n");
fprintf(hostapd, "ieee80211ac=1\n");
fprintf(hostapd, "ieee80211ax=1\n");
Expand All @@ -572,26 +625,60 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
} else if (!strcmp(security_mode, "wpa2-personal")) {
fprintf(hostapd, "# WPA2-Personal\n");
fprintf(hostapd, "wpa=2\n");
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK\n");
if (enable_80211r)
fprintf(hostapd, "wpa_key_mgmt=FT-PSK WPA-PSK\n");
else
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK\n");
fprintf(hostapd, "wpa_pairwise=CCMP\n");
fprintf(hostapd, "wpa_passphrase=%s\n", secret);
} else if (!strcmp(security_mode, "wpa3-personal")) {
fprintf(hostapd, "# WPA3-Personal\n");
fprintf(hostapd, "wpa=2\n");
fprintf(hostapd, "wpa_key_mgmt=SAE\n");
if (enable_80211r)
fprintf(hostapd, "wpa_key_mgmt=FT-SAE SAE\n");
else
fprintf(hostapd, "wpa_key_mgmt=SAE\n");
fprintf(hostapd, "rsn_pairwise=CCMP\n");
fprintf(hostapd, "sae_password=%s\n", secret);
fprintf(hostapd, "ieee80211w=2\n");
} else if (!strcmp(security_mode, "wpa2-wpa3-personal")) {
fprintf(hostapd, "# WPA2/WPA3 Mixed Mode\n");
fprintf(hostapd, "wpa=2\n");
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK SAE\n");
if (enable_80211r)
fprintf(hostapd, "wpa_key_mgmt=FT-PSK FT-SAE WPA-PSK SAE\n");
else
fprintf(hostapd, "wpa_key_mgmt=WPA-PSK SAE\n");
fprintf(hostapd, "rsn_pairwise=CCMP\n");
fprintf(hostapd, "wpa_passphrase=%s\n", secret);
fprintf(hostapd, "sae_password=%s\n", secret);
fprintf(hostapd, "ieee80211w=1\n");
}

/* 802.11r: Fast BSS Transition */
if (enable_80211r) {
fprintf(hostapd, "\n# Fast BSS Transition (802.11r)\n");
fprintf(hostapd, "mobility_domain=%s\n", mobility_domain);
fprintf(hostapd, "ft_over_ds=1\n");
fprintf(hostapd, "ft_psk_generate_local=1\n");
fprintf(hostapd, "nas_identifier=%s.%s\n", primary_ifname, mobility_domain);
}

/* 802.11k: Radio Resource Management */
if (enable_80211k) {
fprintf(hostapd, "\n# Radio Resource Management (802.11k)\n");
fprintf(hostapd, "rrm_neighbor_report=1\n");
fprintf(hostapd, "rrm_beacon_report=1\n");
}

/* 802.11v: BSS Transition Management */
if (enable_80211v) {
fprintf(hostapd, "\n# BSS Transition Management (802.11v)\n");
fprintf(hostapd, "bss_transition=1\n");
}

if (enable_80211k || enable_80211r || enable_80211v)
fprintf(hostapd, "\n");

/* Add BSS sections for secondary APs (multi-SSID) */
for (i = 1; i < ap_count; i++) {
DEBUG("Adding BSS section for secondary AP %s", ap_list[i]);
Expand Down
2 changes: 1 addition & 1 deletion src/confd/src/services.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ static void mdns_conf(struct lyd_node *cfg)
fprintf(fp, "\n[reflector]\n");
ctx = lydx_get_descendant(lyd_child(cfg), "reflector", NULL);
if (ctx) {
fprintf(fp, "enable-reflector=%s\n", lydx_is_enabled(ctx, "enabled") ? "on" : "off");
fprintf(fp, "enable-reflector=%s\n", lydx_is_enabled(ctx, "enabled") ? "yes" : "no");
fput_list(fp, ctx, "service-filter", "reflect-filters=");
}

Expand Down
Loading