On an EHT BSS or an AP MLD with sae, psk3, sae-mixed or psk3-mixed, the
RSNE Override 2 element carries SAE-EXT-KEY and GCMP-256 (CCMP without
phy support). An explicit sae_ext_key 1 and gcmp256 1 also put both
items into the RSNE, and the element stayed as a duplicate.
A configuration that asks for an RSNE with AKM 24 and GCMP-256 and an
RSNXE only sets sae_ext_key 1 and gcmp256 1. The Beacon then had an
RSNO2E and an RSNXOE in addition.
A duplicate RSNO2E adds nothing. A client sees both items in the RSNE,
and the RSNE meets WPA3 Specification v3.5 2.5 items 4 and 5.
Build the element only when the RSNE lacks its AKM or its pairwise
cipher. Without an RSNO2E, hostapd writes no RSNXOE either
(wpa_auth_gen_wpa_ie()). The default keeps the element as before, with
the client fixes of commits fc652db52a and 5e067465ff, and so does a
BSS with one of the two options. One exception: without phy support for
GCMP-256, the element carries SAE-EXT-KEY and CCMP, which an RSNE with
sae_ext_key 1 already offers, so that BSS sends no element. 0 still
keeps an item out of both elements. sae-compat keeps its own elements.
On an AP MLD, the decision covers all links (ap.uc mld_rsno2()): WPA3
Specification v3.5 14.2 requires an identical RSNE Override 2 element
on all links of an AP MLD that uses RSN overriding. The element goes
only where the RSNE of every link offers both items. A psk2 or
psk-mixed AP MLD has PSK in the RSNE of its 2.4 and 5 GHz links, so all
its links keep the element, also the 6 GHz link, whose RSNE has SAE. An
AP MLD of sae whose RSNE has both items on every link uses no RSN
overriding. IEEE 802.11be-2024 12.6.2 then requires the same RSNE on
every link except the AKM list and MFPR, and one common AKM. The links
share SAE and SAE-EXT-KEY, also on a 6 GHz link.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
setup_sta() mapped the encryption mode by the band of the radio that
rendered the network block. An MLD station on 2.4, 5 and 6 GHz radios
was set up for 6 GHz, so psk2 became sae with PMF required. The
station then skipped a WPA2-PSK BSS on 2.4 GHz.
An AP MLD maps each link by its own band: the 6 GHz link takes SAE,
and the other links keep psk2. A station has one network block for
all its links, so it must allow the AKM of each band it can join.
netifd passes mlo_bands to an MLD station as well. With 6 GHz among
other bands, psk2 now becomes sae-mixed and wpa2 becomes wpa3-mixed.
wpa_supplicant removes the PSK AKMs for a 6 GHz BSS (events.c, "WPA
PSK is not allowed on the 6 GHz band"), so the station uses SAE
there. PMF optional meets the MFPR that a 6 GHz BSS sets. With 6 GHz
bands only, the 6 GHz mapping applies as before. Other modes keep
their station behaviour on a mixed MLD.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
iface_wpa_stations() wrote the wifi-station PSKs to
/var/run/hostapd-<ifname>.psk and named that file only where the user
had not set wpa_psk_file. With a user's file, hostapd read that file
alone, and the wifi-station PSKs never reached it, as it was for SAE
before the commit "wifi-scripts: wifi-station SAE passwords with a
user's file".
hostapd keeps one wpa_psk_file: config_file.c replaces the path at each
wpa_psk_file line, and hostapd_config_read_wpa_psk() reads lines of
"[keyid=..] [wps=..] [vlanid=..] <MAC> <PSK or passphrase>", skips
empty lines and lines that start with '#', and fails the setup of the
BSS when the file is missing. A second wpa_psk_file line is therefore
no option, and the user's lines are copied into the generated file.
Move the copy that iface_sae_stations() does into station_file_write()
and use it for both files: it writes the user's lines, then the
wifi-station lines, and names the generated file; a user's file that
cannot be read stays named, so hostapd reports it as before.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
iface_sae_stations() wrote the wifi-station SAE passwords to
/var/run/hostapd-<ifname>.sae and named that file only where the user
had not set sae_password_file. With a user's file, hostapd read that
file alone, and the wifi-station passwords never reached it.
hostapd's parse_sae_password_file() adds each line of the file to the
SAE password list, and each sae_password_file line reads one more file,
so two lines would work for hostapd. The hostapd service keeps one hash
per file option (file_fields in hostapd.uc), though, and a second line
would replace the hash of the first, so a change in one of the files
could go unnoticed. Copy the lines of the user's file into the generated
file instead, before the wifi-station entries, and always name the
generated file. Where the user's file cannot be read, keep naming it, so
hostapd reports it as before.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
key_kind() took every key of 64 characters as a raw PSK, and ap.uc wrote
it as wpa_psk, or as multi_ap_backhaul_wpa_psk for the Multi-AP backhaul
key. hostapd's config_file.c reads both with hexstr2bin() and counts a
key that is not 64 hex digits as an error ("Invalid PSK"), so it
rejected the configuration of the whole radio, and every BSS on it
stayed down.
Take a key of 64 characters as a raw PSK only where all of them are hex
digits, upper or lower case, and as invalid otherwise. bss_refusal()
then refuses that BSS with INVALID_WPA_PSK, for the BSS key and for the
backhaul key alike, and the other BSSes of the radio come up. A psk BSS
without a key stays as it is: outside 6 GHz, wpa_psk_file gives its
per-station PSKs.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Each radio renders its own link of an AP MLD, and bss_refusal() looked
at that link only. Some refusals depend on the band: a 6 GHz link of a
transition mode is SAE only, and WPS with its backhaul key runs outside
6 GHz only. With a raw PSK, the 6 GHz link of a sae-mixed AP MLD was
refused while its 2.4 and 5 GHz links came up, and a Multi-AP backhaul
key that is no key refused the 2.4 and 5 GHz links but not 6 GHz.
All links run one configuration. For an AP MLD, mld_refusal() maps and
parses the configuration of each band in mlo_bands, in its order, and
returns the first refusal.
The RSNE Override 2 element of a psk2 AP MLD, and so MLD_SAE_NO_PASSWORD,
followed the htmode of the radio that renders the link. A link on an HE
radio was written while the EHT links were refused. hostapd refuses
mld_ap without IEEE 802.11be ("Cannot enable mld_ap when IEEE 802.11be
is disabled"), so parse_encryption() now treats an AP MLD as EHT for
that element. Every link gets the same result, so either the AP MLD is
refused on every link with the same reason, or on none.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
parse_bool() compared its argument with the strings "1", "true", "0"
and "false" only. A service-injected wireless config arrives as JSON, so
a value of true, false, 1 or 0 gave null. wireless.uc then read
"mlo": true of such a wifi-iface as no MLO, and parse_attribute_list()
dropped every boolean attribute that came as JSON.
Accept true and 1 as true, and false and 0 as false. The result for the
UCI strings stays the same.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Commit adb1fce19b ("wifi-scripts: set rsn_overriding for client mode
interfaces") made setup_sta() write rsn_overriding=1 for a station with
rsn_override set and an HE or EHT htmode. The wifi-iface schema did not
declare rsn_override, so netifd dropped the option before the handler
ran, and that branch could not be reached.
Declare it as a boolean without a default.
Fixes: adb1fce19b ("wifi-scripts: set rsn_overriding for client mode interfaces")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
WPA3 Specification v3.5 2.5 item 10: "A STA that enables EHT or MLO
shall, in its Network Profile, allow GCMP-256 to be selected as a
pairwise cipher." setup_sta() wrote pairwise=CCMP for every RSN mode,
because parse_encryption() got no phy features in station mode.
Read the GCMP-256 support of the phy in supplicant.generate() for an
EHT htmode, and write pairwise=CCMP GCMP-256 for an RSN station when
the phy supports GCMP-256. An explicit cipher or gcmp256 0 is kept.
The GCMP-256 test moves to iface.phy_cipher_gcmp256(), which
hostapd.uc uses as well.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
An AP MLD with wpa or wpa-mixed and no 6 GHz link enabled WPA version
1, which has no RSNE, so a multi-link association had no RSN parameters
to use. With a 6 GHz link these modes already became wpa2.
Render wpa and wpa-mixed as wpa2 on every AP MLD, as psk and psk-mixed
become psk2.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
An AP MLD with psk, psk2 or psk-mixed advertised PSK alone. A
multi-link association then used PSK. On an AP MLD with a 6 GHz link,
where the 6 GHz link uses SAE, the links had no AKM in common.
For every AP MLD, keep PSK in the RSNE of the 2.4 and 5 GHz links for
single-link stations, and offer SAE with MFP required in the RSNE
Override 2 element of every link. A multi-link association with RSN
overriding uses that element (WPA3 Specification v3.5 14.2), so it
always uses SAE, and all links share it. The 6 GHz link keeps SAE in
its RSNE. psk and psk-mixed become psk2, because an AP MLD needs RSN
on every link and WPA3 Specification v3.5 2.5 item 1 forbids WPA
version 1 beside WPA3-Personal.
The element carries the SAE-EXT-KEY AKM, which IEEE 802.11be-2024
12.12.9 requires for SAE between EHT stations, with GCMP-256 (CCMP
without phy support), as for the WPA3-Personal modes on EHT. psk2 on
an AP MLD now sets up SAE:
- the password file, sae_pwe, sae_groups and sae_require_mfp;
- okc on by default, as for the SAE and OWE auth types, so that its
SAE stations get PMKSA caching;
- with ieee80211r, FT-SAE-EXT-KEY in the element. hostapd's local FT
response covers FT-PSK only ("Whether to generate FT response locally
for PSK networks", hostapd.conf), so ft_psk_generate_local stays off,
the r0kh and r1kh key holders are written, and bss_refusal() requires
a key to derive them from.
A key of 64 hex digits is a raw PSK, which SAE cannot use. Without
another SAE password, every SAE authentication would end in "SAE: No
password available", so bss_refusal() refuses such a BSS with
MLD_SAE_NO_PASSWORD, as an SAE-only BSS without a password is refused.
The BSS is left out of the radio, and netifd shows the reason as the
`error` of the interface. The same sources give SAE a password as
there: a passphrase, PPSK, a wifi-station password, a
sae_password_file option, or a sae_password or sae_password_file line
in hostapd_bss_options.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
WPA3 Specification v3.5 2.5, which applies to all WPA3-Personal modes:
"4. If an AP's BSS Configuration enables EHT or MLO, it shall enable AKM
suite selector 00-0F-AC:24. 5. If an AP's BSS Configuration enables EHT
or MLO. It shall enable GCMP-256 as a pairwise cipher." Items 9 and 10
require the same of a station profile that enables EHT or MLO.
Commits fc652db52a and 5e067465ff kept both out of the RSNE of sae
and sae-mixed, because clients failed with them there, and every
station parses the RSNE.
Advertise them in the RSNE Override 2 element, for sae, psk3,
sae-mixed and psk3-mixed with an EHT htmode: SAE-EXT-KEY, GCMP-256 (or
CCMP when the phy has no GCMP-256), and MFP required. The RSNE keeps
its AKM and cipher lists. A station reads the override elements only
if it supports RSN overriding, and a multi-link association uses the
RSNE Override 2 element (WPA3 Specification v3.5 14.2). sae-compat
keeps its own elements. With ieee80211r the element also carries
FT-SAE-EXT-KEY, as it does for sae-compat.
An explicit sae_ext_key 1 or gcmp256 1 also offers the item in the RSNE,
and the RSNE Override 2 element stays as it is. Only 0 keeps the item
out of both elements.
The station profile allows SAE-EXT-KEY on EHT (2.5 item 9).
Signed-off-by: Felix Fietkau <nbd@nbd.name>
IEEE 802.11be-2024 12.6.2: "All APs affiliated with an AP MLD shall
advertise the same RSNE, and RSNXE if included, with the exception of
the AKM Suite List field and the MFPR subfield of the RSN Capabilities
field" and "shall advertise at least one common AKM suite selector".
The 6 GHz link of an AP MLD renders none as owe and the WPA2-Enterprise
modes as wpa3. The other links kept open or WPA, so some links had no
RSNE, or the links had no common AKM.
netifd now passes the bands of all radios of an AP MLD to each link as
mlo_bands. With a 6 GHz band among them, the other links render none
as owe, and wpa and wpa-mixed as wpa2. Every link then shares OWE or
WPA-EAP-SHA256 with the 6 GHz link, and the MFPC of the links agrees.
For such an AP MLD, this also meets WPA3 Specification v3.5 11.3,
which forbids legacy open with EHT or MLO. A disabled radio, which runs no
link, and a radio without a band option add nothing to mlo_bands.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
setup_sta() had no case for the auth type psk-sae-compat, so a station
with encryption sae-compat got key_mgmt=NONE in every band.
WPA3 Specification v3.5 2.4: "There is no separate WPA3-Personal
Compatibility Mode for STAs. A STA in WPA3-Personal Transition Mode or
a WPA2-Personal mode can, irrespective of whether it supports RSN
overriding, connect with an AP in WPA3-Personal Compatibility Mode".
Treat sae-compat as sae-mixed in station mode, which 2.3 defines for a
station: WPA-PSK, WPA-PSK-SHA256 and SAE with PMF capable, and SAE
alone in the 6 GHz band. RSN overriding stays off, as before.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
In an AP MLD with sae-compat, the 6 GHz link has SAE in its base RSNE,
and the 2.4 and 5 GHz links have WPA-PSK there and SAE in the RSN
Override element. A station without RSN overriding support that sets up
the MLD on the 6 GHz link with SAE sends SAE in the per-STA profile of a
partner link. hostapd's wpa_validate_wpa_ie() matches that against the
base wpa_key_mgmt of the partner link, WPA-PSK, and rejects it.
hostap's hostapd/hostapd.conf documents the option for this case:
"Allow MLO partner link association from a non-RSNO-capable STA that
negotiates an AKM from the override set (e.g., SAE on the 6 GHz primary
link) but whose partner link per-STA profile RSNE would not match the
base wpa_key_mgmt of that link (e.g., WPA-PSK on 2.4 GHz in WPA3-Personal
Compatibility Mode). When enabled, the AP MLD falls back to
rsn_override_key_mgmt for the partner link key_mgmt validation".
wpa_validate_wpa_ie() uses it only for a partner link, and only when
the base AKM list has no match.
Write rsn_override_mlo_compat=1 on every sae-compat MLD link outside
6 GHz. A link does not know the bands of its partner links, and the
option has no effect when no partner link offers SAE in its base RSNE.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
parse_encryption() turns wpa3-mixed into the auth type eap-eap2, which
setup_sta() did not handle: the station got key_mgmt=NONE and no EAP
settings, so it could not join a WPA3-Enterprise transition network.
Handle eap-eap2 with the other EAP auth types, and set ieee80211w to 1
(optional) unless it is set, as the AP side does for this mode.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
In the 6 GHz band an AP turns none into owe, the psk modes into sae, the
WPA2-Enterprise modes into wpa3, and a transition mode into its WPA3
part. A station with the same configuration kept WPA-PSK, NONE, the WPA
protocol or the 802.1X SHA-1 AKM. wpa_supplicant skips a 6 GHz BSS
without an RSNE and removes the PSK AKMs there (events.c, "WPA PSK is
not allowed on the 6 GHz band"), so that station could not join. WPA3
Specification v3.5 11.2 also says that a WPA3 station connecting there
"shall not allow ... 802.1X SHA-1 AKM".
Move encryption_band() from ap.uc to iface.uc and apply it in
setup_sta() for mode sta. Mesh and ad-hoc keep their own handling.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
With encryption owe and owe_transition, setup() writes a hidden OWE BSS
and an open companion BSS that points to it. IEEE 802.11-2024 12.12.2
allows no open BSS in the 6 GHz band. hostapd refused the companion
there, which stopped the radio. Since none became owe in that band, the
companion was a second OWE BSS that pointed to the first.
Decide the pair in ap.owe_transition(), which answers no for the 6 GHz
band, and let setup() call it. A 6 GHz BSS is then one plain OWE BSS
with its configured SSID.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd's sae_get_password() takes an SAE password entry, the
wpa_passphrase, or a passphrase that RADIUS returns for the station.
wifi-scripts writes a key of 64 hex digits as wpa_psk, a raw PSK, which
SAE cannot use. With encryption sae, hostapd accepted the BSS, and every
SAE authentication then ended in "SAE: No password available". Since
psk, psk2 and psk-mixed become sae in the 6 GHz band, the same happened
to them there, and to sae-compat, whose 6 GHz base RSNE has SAE only.
Refuse the BSS with SAE_NO_PASSWORD in bss_refusal() when SAE is the
only password AKM and it has no password: no passphrase, no PPSK, no
wifi-station password, no sae_password_file option and no sae_password
or sae_password_file line in hostapd_bss_options. The BSS is left out of
the radio, and netifd shows the reason as the `error` of the interface.
station_password_count() counts the entries iface_sae_stations()
writes, before the BSS is generated. Transition and compatibility modes
outside 6 GHz keep their PSK AKM, which works with a raw PSK, so they
are not refused.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
For an invalid key, an invalid Multi-AP backhaul key and an FT key that
cannot be derived, ap.generate() called netifd.setup_failed(). That
logs the reason and stops later retries, but the handler still wrote
the BSS into the hostapd configuration and set the radio up. A reason
of one BSS also concerned the radio as a whole.
Decide these reasons in bss_refusal(), after parse_encryption() and
before ap.generate() writes anything. A refused BSS is left out:
- ap.bss_add() rewinds the configuration to where the BSS started and
reports the reason with netifd.bss_failed(), which logs the section
and the ifname of the BSS;
- netifd.bss_failed() sends CMD_SET_DATA for the interface with an
`error` field, which netifd shows in "ubus call network.wireless
status";
- hostapd.uc setup_interface() gives the index of a refused BSS to the
next one, so the first BSS that is written has the `interface=` line;
- setup() skips the OWE transition companion of a refused BSS.
The other BSSes of the radio come up. setup_failed() stays for the
radio-level reasons HOSTAPD_START_FAILED, SUPPLICANT_START_FAILED and
BRIDGE_NOT_ALLOWED.
netifd's wdev_set_data() replaces the handler data of the interface, so
the data that bss_failed() sends drops the ifname that set_vif() gave
before. A refused interface has no ifname in the status, and readers
rely on that, as the BSS has no netdev.
With mbssid, the first BSS written is the transmitting BSS. Where a
refusal changes which BSS comes first, the hostapd service can find no
match for the new first BSS, logs "First BSS changed with MBSSID
enabled" and restarts the radio.
wps_enabled() and ft_psk_local_default() hold the conditions that
iface_wps() and iface_roaming() used, so the refusal and the written
BSS use the same ones.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A handler can report data for one wifi-iface section through notify()
with CMD_SET_DATA, and wdev_set_data() keeps it as the handler data of
that section. get_status_data() copied only the ifname from it into the
interface entry of "ubus call network.wireless status".
Copy an `error` field too, so a handler that leaves one interface out of
the radio can name the reason where the interface is listed. The handler
data is reset on each setup and teardown, so the error lasts until the
next setup of the radio.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
iface_wps() wrote multi_ap_backhaul_key as a passphrase only above 8
characters, so a valid passphrase of 8 characters reported
INVALID_WPA_PSK, and one above 63 characters reached hostapd, which
rejects it. IEEE 802.11-2024 J.4.1: "passPhrase is a sequence of
between 8 and 63 ASCII-encoded characters".
Test both keys with key_kind(), which iface_auth_type() uses for the
BSS key as well: 64 characters are a raw PSK, 8 to 63 a passphrase.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A WPS enrollee associates with Open System authentication and without
an RSNE; hostapd's check_assoc_ies() accepts a station with a WPS IE
and drops its RSNE. IEEE 802.11-2024 12.12.2 forbids Open System
authentication without encryption in the 6 GHz band.
iface_wps() enabled WPS on any BSS that iface_auth_type() marked
wps_possible (open, OWE and the personal modes) when wps_pushbutton or
wps_label was set, whatever the band. hostapd_config_check_bss_6g()
does not look at wps_state, so the 6 GHz link of an AP MLD ran WPS, and
an enrollee was accepted with Open System authentication there.
Clear wps_possible in iface_wps() for the 6 GHz band, as it already does
for multi_ap 1. iface_wps() is the one place that decides whether WPS is
written, so the rule holds for every auth type that sets wps_possible.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
On a sae-compat BSS, the SAE-EXT-KEY AKM goes into the RSN Override 2
element. generate() writes that element only when its AKM list, its
pairwise cipher and its MFP setting are all set, and parse_encryption()
set the pairwise cipher only for GCMP-256. With sae_ext_key on and no
GCMP-256, because gcmp256 is off or the phy does not support it, the AKM
was silently dropped. Commit fc652db52a meant the AKM and the cipher
to follow their own options.
hostap's hostapd/hostapd.conf says the second override set is "required
to be used for Wi-Fi 7 (EHT/MLO) associations with RSN overriding", and
sae-compat enables sae_ext_key by default on EHT. On a phy without
GCMP-256, an EHT sae-compat BSS therefore had no such element.
Use CCMP for the RSN Override 2 element when sae_ext_key is set and
GCMP-256 is not used.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
IEEE 802.11be-2024 12.12.9 says "An EHT non-AP STA and EHT AP shall
have dot11BeaconProtectionEnabled set to 1 when using RSN", and its note
adds that such an AP has PMF activated, which its RSNE advertises with
MFPC = 1. 12.6.2 requires all APs of an AP MLD to advertise the same
RSNE except for the AKM list and MFPR, so the MFPC of the links must
agree.
psk2, psk-mixed, wpa2 and wpa-mixed left ieee80211w at 0 on EHT, so
hostapd enabled no beacon protection. In an AP MLD with a 6 GHz link,
which requires PMF, the 2.4 and 5 GHz links then advertised MFPC = 0
and the 6 GHz link MFPC = 1.
With an EHT htmode, default ieee80211w to 1 (optional) for every mode
with WPA2 or later except sae-compat. An explicit ieee80211w still
wins.
WPA3 Specification v3.5 2.4 item 4a, WPA3-Personal Compatibility Mode:
"The AP shall set MFPC in the RSNE in the 2.4 and 5 GHz bands to 1 if
it has enabled functionality that requires PMF to be enabled with
WPA2-Personal (e.g. Wi-Fi Agile Multiband), or to 0 otherwise. The AP
shall set MFPR to 0 in the RSNE in the 2.4 and 5 GHz bands." The base
RSNE of sae-compat therefore keeps MFPC 0 unless mbo, ocv or dpp needs
PMF. The RSNE Override elements still set MFPR, so hostapd enables
beacon protection, which IEEE 802.11be-2024 12.12.9 requires for EHT.
In an AP MLD, a station uses the RSNE Override 2 element, which is the
same on every link (WPA3 Specification v3.5 14.2).
Signed-off-by: Felix Fietkau <nbd@nbd.name>
IEEE 802.11-2024 12.12.2 forbids an open BSS without encryption in the
6 GHz band. It names the replacement: "The STA shall use Opportunistic
Wireless Encryption (12.14), when connecting in an infrastructure BSS
without authentication (as a replacement for Open System authentication
without encryption)". hostapd_config_check_bss_6g() refuses the open
form, so encryption none stopped a 6 GHz radio, and in an AP MLD the phy
of every link.
In the 6 GHz band, replace none with owe. An unset encryption is none.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
IEEE 802.11-2024 12.12.2 says "An AP shall require management frame
protection (MFPR = 1) when using RSN" in the 6 GHz band, and
hostapd_config_check_bss_6g() refuses a BSS with "Management frame
protection is required in 6 GHz".
sae-compat used set_default(), so an explicit ieee80211w 0 or 1 won. In
an AP MLD one configuration serves every link, so an ieee80211w value
that suits the 2.4 and 5 GHz links stopped the 6 GHz one.
In the 6 GHz band, set ieee80211w to 2 for every mode with WPA2 or later,
also over an explicit value, which cannot work there.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
On 6 GHz, generate() replaced the psk and psk-sae auth types with sae,
and eap and eap-eap2 with eap2, after parse_encryption() had run. A
replacement that must also change wpa or the pairwise cipher cannot work
that way, because parse_encryption() derives both from the encryption
string. For psk, psk-mixed, wpa and wpa-mixed, wpa kept the value 1 or
3, and hostapd_config_check_bss_6g() refuses such a BSS with "Pre-RSNA
security methods are not allowed in 6 GHz".
Replace the encryption mode before parse_encryption() runs, and keep any
cipher suffix:
- sae-mixed, psk3-mixed, psk, psk2 and psk-mixed become sae.
- wpa3-mixed, wpa, wpa2 and wpa-mixed become wpa3. WPA3 Specification
v3.5 11.2 item c forbids the 802.1X SHA-1 AKM in the 6 GHz band.
Each mode gets the same auth type as before. psk, psk-mixed, wpa and
wpa-mixed now also get wpa 2.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd_config_check_bss() refuses a BSS with "MBO: PMF needs to be
enabled whenever using WPA2 with MBO", and one with "OCV: PMF needs to be
enabled whenever using OCV". A refused BSS stops the whole phy.
Outside 6 GHz, several modes leave ieee80211w at 0 unless it is set:
psk2, psk-mixed, wpa2, wpa-mixed, and sae-compat, which sets 0 for its
base RSNE. With "option mbo 1" or "option ocv 1", each of them failed.
On a BSS with WPA2 or later, set ieee80211w to 1 (optional) when mbo or
ocv is set and ieee80211w is 0 or not set. An explicit 0 cannot work
with these options, so it is raised as well, as the dpp option does.
hostapd enables ieee80211w itself when it reads "ocv", but only if no
ieee80211w line came before it, and wifi-scripts writes "ieee80211w=0"
after "ocv". Write ocv beside ieee80211w in iface_mfp() instead. On a
BSS without WPA2 there is no PMF, so ocv is no longer written there.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
In WPA3-Personal Compatibility Mode outside 6 GHz, the base RSNE must
look like WPA2-Personal to legacy stations. wpa_key_mgmt() added
WPA-PSK-SHA256 to that RSNE whenever ieee80211w was set, which the dpp
option already does, and which an explicit ieee80211w does too.
hostapd's own example for this mode in hostapd/hostapd.conf ("Example
configuration for WPA2-Personal/PMF-optional in RSNE and
WPA3-Personal/PMF-required/MLO in override elements") uses ieee80211w=1
with wpa_key_mgmt=WPA-PSK alone. Commit 86b9eec8f0, which added the
mode, cites the WPA3 Deployment Guide for a base RSNE with WPA-PSK only.
Advertise WPA-PSK, and FT-PSK with ieee80211r, in that RSNE whatever the
PMF setting is.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
netifd numbers the MLO interfaces in the order of their sections
(mlo_vif_create()), so the name of an AP MLD is no identity: a new
section that sorts first renames the MLDs after it (ap-mld0 to ap-mld1).
hostapd took the renamed MLD for a new one and gave it a new address.
Its links got new addresses too, although the configuration of the MLD
did not change.
- mld_prev_match() in common.uc decides which removed MLD a new MLD
continues. On one wiphy, two passes in order: the same SSID and
radios, then the same SSID. The first pass separates two MLDs with one
SSID. A new MLD that matches none asks netifd by its name, as before:
it gets the address that its name had, unless another MLD continues
that one, and else a new address, as a new BSS does.
- The MLD sync names the address of a continued MLD with another name
for the new name. The entry of the old name loses the address to it,
so a new MLD with the old name finds no entry.
- The links of a removed MLD release their entries on every radio that
hostapd has a configuration for (macaddr_release). The reload of
mld_set_config() reaches only the links that run, so a link whose BSS
failed or was pending kept its entry, which shares the address of the
old MLD.
- The entry of a link of a continued MLD moves to the link of the new
MLD on that radio (macaddr_release with to), where that name has no
live entry. A stale entry on the target name is a leftover of an
earlier removal, unless the target is the old name of another MLD of
the same change; hostapd lets the move replace it only in the first
case (replace). Without that, the leftover kept the target name: the
continued MLD got the old link address of that removed MLD, and an
unrelated new MLD took the link address of the continued one.
In a chain of renames, the MLD whose new name is free moves
first and frees its old name for the next one. A cycle (two MLDs that
swap their names) has no free name: the entries stay stale with their
old names, and the links of the new MLDs claim them by name. A link
that carries the MLD address follows the MLD; each other link keeps
the address that its name had, not the one of the link it continues.
Each move happens within one netifd. The sync and the releases go out
before the requests of the new MLDs, and netifd takes the requests of
one connection in order.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd, wpa_supplicant and wdev.uc now request the address of each
interface from netifd (network.wireless macaddr_get) and tell it which
names they keep (macaddr_sync). The used lists that they built from
their own configuration, from the lists of each other and from the
netdevs are gone, and with them the ubus methods that passed the lists:
config_get_macaddr_list of hostapd and wpa_supplicant,
wpas_set_macaddr_list of hostapd and phy_set_macaddr_list of
wpa_supplicant. The allocator of common.uc and the get_macaddr command
of wdev.uc are gone too.
Names:
- hostapd: <phy>/<ifname> per BSS, in the group of the PHY
configuration, and the AP MLD name in the group "mld". A link of an
AP MLD shares the address of the MLD where the configuration names
it as its BSSID; a link on radio 0 also follows a generated MLD
address.
- wpa_supplicant: the ifname, in the group of the PHY configuration,
and the station MLD name in the group "mld". A station MLD keeps its
address when its radios change.
- wdev.uc: the ifname, in the group of the radio.
A name keeps its address, so a reconfigured station, mesh or IBSS
interface keeps the address that its peers know. An empty configuration
of a radio releases the entries of its owner for that radio: in
hostapd (iface_set_config(), also when iface_check_mld() takes the last
BSS), in wpa_supplicant (set_config()) and in wdev.uc. An expired link
of an AP MLD (mld_link_bss_remove()) releases its entry too. netifd
keeps the released entries as stale entries, so the interfaces that
come back get their addresses by name, and a configured address takes
them.
A reload of hostapd moves the address of a reused BSS to its new name
in one macaddr_sync, so that a rename or a swap of names keeps each
address with its BSS. A reused first BSS of a fullmac device whose kind
of address changed needs a restart, since its netdev keeps its address.
A configured BSSID that another interface holds is refused, and the
interface does not start.
If netifd cannot be reached, each consumer uses the address that the
name had before, where it knows one, and a hostapd reload keeps the
addresses of the reused BSSes.
The configuration files carry no placeholder BSSID for an interface
without a configured address: hostapd replaced it and wpa_supplicant
and wdev.uc dropped it. hostapd keeps the BSSIDs of an unchanged BSS
list by the name of each BSS instead.
The allocation table lives in netifd only, so a new netifd has no
record of the addresses that the previous one gave. /etc/init.d/network
restart runs wifi down before netifd stops, but a crash of netifd tears
nothing down: the new netifd found its radios running, none of the
consumers asked for an address, and a new interface could get one of
the running addresses. After wifi down too, hostapd kept the data of an
AP MLD whose configuration did not change, and the netdev of that MLD
took its address from that data. wireless.uc therefore resets the
radios when netifd loads it, before config_init() sends mld_set:
- hostapd config_reset,
- wpa_supplicant config_reset, a new method: an empty configuration for
each radio, and an empty MLD configuration, as hostapd does,
- wdev.uc reset, a new command, for each /var/run/wdev-<phy>.json. It
removes the interfaces that the state file names and that this radio
created (drop_inactive()), and writes empty state and id files. It
opens no ubus connection, as network.wireless does not exist yet at
this point.
netifd waits for each of these. The two calls use a connection of their
own with a timeout of 3 s, not the global connection of netifd with
30 s, so a hostapd or wpa_supplicant that does not answer holds netifd
start for at most 3 s each. A result other than "not found" goes to the
log. netifd loads wireless.uc once, in netifd_ucode_init() from main(),
so the reset runs only when netifd starts, not at a wifi reload, a
network reload or a reconf.
The cost: a crash of netifd that procd answers with a respawn, and
"ubus call network restart", now restart all wifi, as
/etc/init.d/network restart already did through wifi down. Outside the
ETSI region, cfg80211 sets an available DFS channel back to usable 2 s
after no interface operates on it (cfg80211_dfs_channels_update_work()),
so a radio on a DFS channel runs a new CAC, and the connection of a
backhaul station drops.
Other parts:
- wdev_create() refuses a netdev other than a monitor without an
address, and hostapd creates its first BSS with its BSSID: without an
address, mac80211 takes the first free entry of the wiphy address
list, which netifd can have given to another interface.
- A failed allocation in iface_restart() starts no BSS of the radio and
restarts wpa_supplicant there, as the other early returns do.
- On a fullmac wiphy, wdev_create() reuses a netdev that is down, and
nl80211_set_interface() does not read NL80211_ATTR_MAC. The reused
netdev now gets the requested address through RTM_SETLINK. A driver
without ndo_set_mac_address, such as ath6kl, keeps the old address,
with a warning.
- iface_reload_config() checks the fullmac property of the wiphy. It
passed the configuration name, such as phy1.0, which has no sysfs
entry, so phy_is_fullmac() checked wiphy 0.
- wpa_supplicant reports the generated addresses in its status method.
- wdev.uc takes num_global=<n> and macaddr_base=<addr> among its
arguments again.
- wpad_acl.json allows the calls to network.wireless, and the
postinst of hostapd-common makes ubusd read the ACLs again.
hostapd.uc and wpa_supplicant.uc import common.uc of wifi-scripts, so
both packages must be updated together.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd, wpa_supplicant and wdev.uc each allocate addresses with the
allocator of common.uc, and each sees the addresses of the others only
through lists that they pass to each other or through the netdevs that
exist at that moment. An address that one of them keeps for an
interface without a netdev is free for the others.
Add one allocation table to the wireless code of netifd, with four
methods on network.wireless:
- macaddr_get gives an address to a name of an owner (hostapd,
wpa_supplicant or wdev). A name keeps its address while the wiphy,
the radio and the generator options stay the same; with any_radio,
also when the radio or the options change, as a station MLD needs.
A configured (static) address that
another name holds is refused. share lets a name use the address of
another name of the same owner, as an AP MLD link does with the MLD
address.
- macaddr_sync sets the names of an owner and group in one call: names
that it does not list are released, and an address can move to
another name of the group (from), for a renamed or swapped BSS.
- macaddr_release releases one name.
- macaddr_list shows the table.
Addresses are unique per wiphy. Generation skips the addresses of the
interfaces in the UCI configuration, and the addresses of netdevs
without an entry. The generator is a copy of the one in common.uc, with
the per-radio ranges; it reads the wiphy once per request and stops at
the first free address.
An entry that shares the address of another name holds that address
for the shared name only: a request for that name does not count the
entry as a holder. Such an entry accounts for no netdev, so the check
of the live netdevs still runs.
A released entry, and each entry of an owner whose ubus object goes
away, stays as a stale entry for the life of netifd, so that the
interface or the process that comes back gets its address by name:
- A request, or a sync that keeps the name, claims the entry again.
- A released entry with a configured address goes instead. The address
comes back with the configuration.
- A new name gets a stale address only where the range of the request
has no other free address.
- A stale entry yields to a request that names its address: a
configured address, a shared one, or a value of a sync. A request
that takes the address removes the stale entries of other names with
it. In a sync, the entries of the same owner lose the address; a
stale entry of another owner keeps it, and its name finds a holder
when it asks again.
The table lives in netifd only. A restart of netifd through its init
script tears the radios down first, so the new table starts empty.
The configured addresses are lower-cased again in update_config(),
because hostapd and wpa_supplicant compare them with the addresses that
netifd returns.
Nothing uses the methods yet.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Commit da4be02fcd ("hostapd: fix a race condition on adding AP mode
wds sta interfaces") made netifd the only process that adds WDS station
interfaces to the bridge. Its hostapd patch 711 copies bridge into
wds_bridge when it parses bridge= and wds_bridge is still empty, and
hostapd.sh writes an empty wds_bridge= after bridge=.
ap.uc does not write the empty wds_bridge=, so hostapd adds each WDS
station interface to the bridge, and netifd adds it again. The commit
above names the effects of the race: incomplete bridge vlan settings,
or a client that hostapd refuses.
Write an empty wds_bridge= for a BSS with a bridge, as hostapd.sh does.
The line must follow bridge=, because patch 711 fills an empty
wds_bridge at parse time; append_vars() writes bridge first. A
wds_bridge that the config sets for a wds BSS still applies, as in
mac80211.sh.
The previous change makes netifd link WDS station interfaces that
appeared while it had no data for their vif.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
netifd links a WDS station interface <vif>.staN from its hotplug event,
through the handler data of the vif. wdev_config_init() clears the vif
entries of the handler data, and the handler sets them again with
set_vif(). The handlers of all radios run one at a time, so this gap
can be long. hostapd keeps an unchanged BSS through a reload, and a
station can associate in the gap. Its hotplug event then finds no vif,
and wdev_mark_up() links only the vif itself, so the WDS station
interface never joins the bridge. Today hostapd adds it to the bridge
for the ucode scripts; with hostapd.sh, which turns that off, the port
is already lost.
When a radio is marked up, also link the existing WDS station
interfaces of each vif and vlan, with the pattern that hotplug() uses.
This matches MLD names such as ap-mld1.sta1 and leaves out VLAN
subinterfaces such as wlan0.sta1.100. A link that exists already does
not change.
Keeping the old vif entries until set_vif() replaces them would also
close the gap, but the old entries can name a vif that the new config
removed or networks that it changed, and they do not cover interfaces
that exist before netifd has any data for the radio. Mark-up is the
point where the handler data is complete, so the links follow it there.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The generated station file named no ctrl_interface; the socket path came
from add_iface alone. RECONFIGURE then read a configuration without it,
deinitialised the control interface and set up none, so the reload method
left /var/run/wpa_supplicant/<ifname> gone. The file now names the same
directory that add_iface passes, which keeps the socket, and the reload
method applies a station MLD's freq_list again.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
wifi-detect now reads the board.d data from /etc/board-base.json and
writes the result to /tmp/sysinfo/board.json, the target of the
/etc/board.json symlink. A detection run no longer writes to flash.
The /etc/board.json checks before 'wifi config' follow the symlink, so
wifi-detect can run without a base file, for example after firstboot
without a reboot. It exits with status 1 in that case.
The base file has no phy entries and no info objects, so cleanup() has
no effect and is removed.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
setup() ran wdev.uc set_config once per interface, each time with a
configuration of that interface alone. set_config replaces the whole
configuration of the radio, so each call stopped the interfaces of the
calls before it: with two monitors, or a monitor and an adhoc
interface, on one radio, only the last one stayed up. The configuration
also passed through the shell inside single quotes, so a single quote
in an SSID or a mesh_id broke the command, and two of them made the
text between them run as shell code.
Pass all interfaces in one call, also when the radio has none, so that
removed interfaces stop. Pass the arguments as an array, which system()
runs without a shell.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drop_inactive() kept every name of the state file whose netdev existed,
and the configuration update then stopped it. A name can belong to
another owner by then: the interface moved to another radio, or it is
now an AP of hostapd or a station of wpa_supplicant. The old radio then
deleted the interface of the new owner.
Record the wdev id of each interface in /var/run/wdev-<phy>.id.json,
and keep a name only if the interface dump shows it on this radio with
the recorded id. A state without recorded ids, from an older version,
falls back to the name.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Bringing a netdev up can fail, for example with ENOTUNIQ from
ieee80211_do_open() when another running interface of the wiphy has the
same address. wdev_set_up() ignored the result, and wpa_supplicant's
iface_start() and mld_add(), hostapd's bss_check_mld() and wdev.uc went
on as if the netdev ran. wpa_supplicant and wdev.uc also went on after a
failed netdev creation.
Return the error from wdev_set_up(). On a failure to create the netdev
or to bring it up, stop there; after a failure to bring it up, also
remove the netdev. wpa_supplicant leaves the interface not running for
the next start_pending() or mld_start(), hostapd skips the links of the
AP MLD, and wdev.uc drops it from its state for the next setup.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
wdev.uc read phydev.phy before it checked that phy_open() found the
phy, so a missing phy stopped the script with "left-hand side
expression is null" instead of the message "PHY ... does not exist".
Check phydev first and drop the phy variable, which nothing reads. Also
use a logical OR in the check of the command.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
ap.uc and supplicant.uc already write dpp_connector, dpp_csign and
dpp_netaccesskey, but the schema did not declare them, so netifd dropped
them before the handlers ran. Declare the three options.
Easy Connect 3.0 section 8.4.2 requires PMF for an association that uses
the DPP AKM, and wpa_supplicant does not enable it by itself. Set
ieee80211w to at least optional on APs and stations that enable dpp.
This also overrides an explicit ieee80211w 0. With sae-compat outside
6 GHz, the base RSNE then advertises PMF, which that mode otherwise
carries only in the RSN override element. On an AP the PMF setting also
enables beacon protection by default. The schema description of dpp
states this.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The modem was started after the LED init script, at the very end of
the boot process, although its driver is ready much earlier. On a
FRITZ!Box 7530 the control application started 61 seconds after power
on, 45 seconds after the driver was loaded, and the line needed
another 90 seconds to synchronize.
The LED is handled by ltq-dsl-base now, so start the modem before the
network. This brings the line up earlier by the time the rest of the
boot process takes.
Assisted-by: Claude:claude-fable-5-1
Link: https://github.com/openwrt/openwrt/pull/25599
Signed-off-by: Nick Hainke <vincent@systemli.org>
The existing 6 GHz auth_type override only covers mixed modes
(psk-sae, eap-eap2), upgrading them to their WPA3 equivalents.
However, if a user configures pure WPA2 (psk or eap) on a 6 GHz
radio, the configuration falls through unchanged and hostapd rejects
it with a confusing error about invalid AKM suite.
Extend the override to also cover pure WPA2 modes:
- psk / psk-sae -> sae
- eap / eap-eap2 -> eap2
This matches the existing silent-upgrade pattern and ensures the
radio comes up regardless of which legacy encryption the user
selected.
Signed-off-by: Michael Pfeifroth <micpf@westermo.com>
Link: https://github.com/openwrt/openwrt/pull/23914
Signed-off-by: Robert Marko <robimarko@gmail.com>
A factory reset drops /etc/config/wireless, so a regdomain that the user
set there is gone after the reset. The provisioning partition survives a
factory reset, so read "wireless.country" from it and let it override the
country from board.json when the wireless config is generated:
provision set wireless.country DE
A provisioned country also keeps the default 5G and 6G interfaces
enabled, which is what a board.json country does today.
The provision package is optional, so the module is loaded at runtime and
a missing module leaves the previous behaviour unchanged.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
iface.uc adds the DPP AKM beside the one the encryption keyword selects when
`dpp` is set, which is how a credential naming several AKMs is expressed, but
the option was in no schema. netifd answered "dpp is not present in the
schema" on every reload that used it.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
210816c6ab67 bridge: keep hotplug vlan ports across reloads while the bridge is down
5c954b9f70e3 bridge, bonding: only restore the ipv6 setting when it was overridden
625425e08699 vlandev: fail instead of falling back to vid 1 on invalid vid
a8b101fe9a7c vlan: clean up the kernel device when bringing the vlan up fails
d1e5a8499071 vlan: do not free the vlan device with live users on rename failure
efbceaa722d5 vlan, vlandev: use a local buffer for forwarded hotplug vlan lists
ab1e53f20138 vlan: refresh hotplug ops when the parent vlan list changes
68493c7d96f4 device: release the parent claim when enabling a device fails
d25c083f65ec device: validate extra vlan ranges against the valid vid range
61a8bcc59f12 interface: do not bring up an interface pending removal
6905ab6abf5e interface: restore the previous hotplug attachment on a failed add
f597379945ac interface: carry the renew option over on config reload
2fcaee3eb9af ucode: reset VM state after freeing it
4b78f2529c9f ucode: release process resources after the exit callback
aa4f99092de2 extdev: fix use after free in extdev_hotplug_remove
d81d1deb6eef extdev: wait for the external handler when the initial lookup fails
d47841fd0196 extdev: fix nested list handling in device info dumps
0b0250d3381d extdev: parse stats dumps with the stats parameter list
368caea53e9a extdev: track the active state when creating an empty bridge
8f52fd28cb02 extdev: handle device_init failure in __bridge_create
0d5ff9ef2115 extdev: resynchronise devices when the external handler returns
c6c2a2be1afc interface-ip: keep addresses that moved to a different list index
8d77f1b07f20 interface-ip: validate the route source mask
f35f095dbf46 iprule: only keep unchanged rules whose installation succeeded
9b62f2a8195d interface-ip: keep host routes in sync with their parent route
7f6cf9895f93 system-linux: check the nlmsg_parse result in cb_rtnl_event
1e4a008f3a99 bridge: drop force_active when the bridge_empty option is removed
362b76935dd3 bridge: ignore stale member check timer events
32ee16c6f3cc extdev: clear the bridge empty state when removed from the config
ab060e0f1fb5 extdev: cancel pending retry timers on device free
b462dead154d system-linux: fix byte order of multicast GRE tunnel keys
109610f5b5de system-linux: validate FMR ealen and offset before use
c51979e37908 config: match the device when overriding vlan config entries
7e9928f0fc00 config: keep vlan config entries until interfaces are parsed
e97e36f9b5b5 config: accept 'true' for the interface disabled option
06d06c86d757 proto-ucode: obtain script path via stdlib sourcepath()
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The packet steering script treats both threaded NAPI and the mt76 TX
worker as tasks belonging to the same wireless device. assign_dev_cpu()
then assigns all of these tasks to the same CPU.
This unnecessarily restricts the mt76 TX worker to the NAPI CPU even
though its affinity does not otherwise need to be constrained.
On MT7988A with MT7996 this caused the TX worker to share CPU3 with the
NAPI thread. Moving it to another CPU increased measured TX throughput
from 1.45 Gbit/s to 2.09 Gbit/s.
Stop matching the mt76 TX worker in the packet steering script. This
leaves its CPU affinity unrestricted and lets the scheduler place it
more appropriately.
Fixes: https://github.com/openwrt/openwrt/issues/24697
Signed-off-by: Gary Dotzler <geldot@protonmail.com>
Link: https://github.com/openwrt/openwrt/pull/25016
Signed-off-by: Robert Marko <robimarko@gmail.com>
parse_encryption returns leaves config.auth_type unset in case
encryption is unset. However, the owe_transition_iface config option for
the unencrypted network is currently only set in case encryption is
explicitly set to 'none'.
These two should be identical in their behavior. Set the encryption to
"none" in case it is unset and do not return. This aligns the behavior
of unset encryption with encryption explicitly set to 'none'.
Signed-off-by: David Bauer <mail@david-bauer.net>