hostapd-utils and wpa-cli use VARIANT:=*, so every variant packs them.
Since the variants are built in parallel, they write the same package
file and pkginfo files at the same time. This makes the build fail
randomly:
Package hostapd-utils is missing dependencies for the following libraries:
libgcc_s.so.1
Only pack them in the first selected variant that builds them.
Fixes: b029d56e0b ("hostapd: build the variants in parallel")
Link: https://github.com/openwrt/openwrt/pull/25796
Signed-off-by: Nick Hainke <vincent@systemli.org>
The PPPoA plugin only uses text2atm() and text2qos() from libatm. ppp
ships copies of both and uses them unless configured --with-atm, so
link the bundled copies and drop the dependency on linux-atm.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25707
Signed-off-by: Robert Marko <robimarko@gmail.com>
The spatial reuse settings that a radio runs with were not visible at
runtime: get_status did not report them, and nl80211 does not carry
them. A radio with he_spr_sr_control=3 could not be told apart from an
unrestricted one.
get_status now adds, beside the BSS Color fields, at the top level and
in each entry of the links of an AP MLD, the running values of the
radio's configuration:
- he_spr_sr_control, he_spr_non_srg_obss_pd_max_offset,
he_spr_srg_obss_pd_min_offset and he_spr_srg_obss_pd_max_offset, as
numbers;
- he_spr_srg_bss_colors and he_spr_srg_partial_bssid, as the bitmap
value with bit n for colour or partial BSSID n (IEEE Std 802.11-2024
9.4.2.251), in 16 lowercase hex digits. The element holds the value
little-endian; a ubus integer is signed, and colour 63 is bit 63.
he_spr_srg_bss_colors=7 8 reads "0000000000000180".
Signed-off-by: Felix Fietkau <nbd@nbd.name>
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>
iface_restart() aborts the pending setup of a phy before it removes the
interface, but iface_config_remove() does not. A config_set without
interfaces, the shutdown and the removal of the last link of an AP MLD
left the setup pending, and its wpa_supplicant reply later added an
interface for a configuration that no longer exists.
Abort the pending setup in iface_config_remove() as well.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
When nl80211_setup_ap() fails in wpa_driver_nl80211_if_add(), the new
i802_bss stays linked into the driver and in *drv_priv. For an AP MLD
BSS, the caller's cleanup then runs driver_nl80211_link_remove() on
that BSS, which reports EVENT_MLD_INTERFACE_FREED with a NULL context,
and hostapd crashes in hostapd_wpa_event(). A failure of
nl80211_set_mode() leaks the BSS.
198-nl80211-undo-a-failed-ap-bss-add undoes the add on both failures,
as the other error paths of the function do. Upstream hostap has the
same code.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The bss_remove hook of hostapd.uc sends a service event with a
synchronous ubus call. While libubus waits for that reply, it runs the
completion callbacks of other pending requests, among them the
wpa_supplicant phy_status or phy_set_state request of an interface
setup in progress. The callback advanced the setup to
hostapd.add_iface() nested inside the teardown that called the hook.
With an AP MLD, a config_set for radio 0 followed by one for radio 1
added the radio 0 link while the radio 1 link was half removed and
still held its driver BSS and management frame registrations. The add
failed with "Match already configured", and its cleanup crashed hostapd
in hostapd_wpa_event(). This happened on the first such reconfiguration
after a bring-up.
Run the next step of the pending setup from a uloop timer instead, and
cancel the timer where the pending request is aborted.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The links of an AP MLD share one wiphy, which runs one scan at a time.
With ACS on several links, each link scans for itself, and every link
after the first gets -EBUSY and retries every 5 seconds.
172-acs-scan-for-all-links-of-an-ap-mld-with-one-request (from John
Crispin) adds the channels of every partner link in ACS to the scan
request, and hands the survey of that scan to each link. The partner
scan check of hostapd_driver_scan() from 171 moves into
hostapd_mld_partner_scanning(). Changes to the patch as received:
- every link, the requesting one included, continues from the event
loop. The partner fallback of EVENT_SCAN_RESULTS otherwise runs the
scan callback of a partner whose next scan a link started from its
own callback, and counts that scan as completed at once;
- every link keeps its scan callback until the survey is fetched, so
that each link of the scan gets its survey on whichever link the
driver reports it;
- a link that the scan of a partner link includes waits at most
ACS_SCAN_WAIT_TIMEOUT seconds for its results and then scans for
itself, so that it does not stay in ACS for good when the results
never come, for example when the partner link is torn down during the
scan;
- acs_cleanup() drops the ACS scan callback, so that a link disabled
during its scan does not make hostapd_mld_partner_scanning() refuse
the scans of its partner links.
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>
hostapd_switch_color_timeout_handler() sets he_bss_color_disabled
before it starts the colour change of each BSS of the radio. The
driver installs the CCA beacons, which carry BSS Color Disabled, only
for a change it starts. Where hostapd_drv_switch_color() fails, or the
settings cannot be built, for every BSS, no beacon carries the disabled
state, but the configuration keeps it: get_status reports -1 for a
colour the beacons still use, and a later beacon rebuild disables BSS
Color without a colour change.
607-ap-restore-bss-color-disabled-when-no-color-change-starts counts
the colour changes that started, and where none did, restores the
state BSS Color Disabled had before. That also resumes the ageing of
the OBSS colours, which the disabled state paused (174). The
bss-color-change notification after the loop (606) then reports the
restored state.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A subscriber that reports the BSS Color and the OBSS colours of a radio
must know when they change without a configuration change. The kernel announces a colour change that the
driver starts, aborts or completes, but hostapd takes the new colour,
or leaves BSS Color disabled, only when it handles that event. Some
changes have no event at all:
- with no free colour left, the collision timeout disables BSS Color
and picks a new colour itself;
- a collision timeout whose colour change the driver does not start
leaves BSS Color disabled;
- the OBSS colours of the radio change.
606-ap-notify-ubus-of-a-bss-color-change calls the new
hostapd_ubus_notify_bss_color():
- in the no free colour branch of the collision timeout;
- after the colour change loop of the collision timeout. This call
fires in every case, whether the driver started the change or not;
- at the end of hostapd_event_color_change(), after hostapd took the
new colour at a completion, or left BSS Color disabled at an abort;
- when the aged set of OBSS colours of the radio changes, as a colour
is added or leaves it (174), and not at each repeated collision
event.
It sends the bss-color-change notification, { ifname, bssid, link_id }
(link_id for an affiliated AP of an AP MLD), on the object of the BSS.
get_status carries the new values.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A subscriber that reports the BSS colours of a radio needs three
fields: the BSS Color subfield and the Partial BSS Color subfield that
the radio transmits, and the colours of the OBSSs that the AP
identified itself or learnt from the BSS Color Collision reports of its
stations. get_status named the
colour, as -1 while BSS Color is disabled, and neither of the others.
hostapd kept its colours in use per BSS, and each collision event
replaced them. It also added the colour of a BSS Color In Use report,
which names a colour a station uses with a peer (IEEE Std 802.11-2024
11.21.2.8), not an OBSS colour. 174-ap-keep-the-obss-colors-of-a-radio
keeps two sets per radio:
- the OBSS colours, which the kernel's collision events and the BSS
Color Collision reports of stations add to. A colour leaves the set
60 s after an event named it, or 130 s after a report did (the
longest dot11BSSColorCollisionSTAPeriod, 120 s, plus a 10 s margin),
and the ageing pauses while BSS Color is disabled;
- the colours of BSS Color In Use reports, held per station until a
report of colour 0 or until the station leaves.
A colour change picks a colour outside both sets.
get_status now adds, beside bss_color, at the top level and in each
entry of the links of an AP MLD:
- he_bss_color: the BSS Color subfield of the HE Operation element.
It keeps the colour while BSS Color is disabled
(IEEE Std 802.11-2024 26.11.4); bss_color keeps -1 then;
- bss_color_partial: he_bss_color_partial of the radio;
- bss_colors_in_use: the OBSS colours of the radio, 1 to 63. The
colours of BSS Color In Use reports are not among them.
On mt76 the kernel's collision events name the colliding colour only:
mt7996 does not detect collisions itself, and mac80211
(ieee80211_rx_check_bss_color_collision()) reports an OBSS whose colour
equals the BSS's own. Other OBSS colours reach the set only through
the BSS Color Collision reports of stations.
The colours are a list rather than the 64-bit bitmap, because a blobmsg
int64 with bit 63 set reads as a negative number in ucode and JSON.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The links of an AP MLD share one ubus object, and get_status describes
the BSS of one link (mld->fbss, or the first link that joined the
object). Each affiliated AP runs on its own radio, with its own
hostapd_iface and so its own channel and BSS Color. A subscriber could
read the BSS Color of one radio of an AP MLD only, not the BSS Color
that each radio transmits.
For an AP MLD, get_status now adds links [{ link_id, bssid, freq,
bss_color }], one entry per link that joined the object: the link ID,
the BSSID of the affiliated AP, its frequency, and its BSS Color with
the meaning of the top-level field (-1 while BSS Color is disabled).
The top-level fields are unchanged. A BSS that is not affiliated with
an AP MLD gets no new field.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The links of an AP MLD share one ubus object, and the ifname of the
key-mismatch event is the MLD netdev. A subscriber could only take the
BSSID of get_status, the BSSID of the first BSS of the MLD, whichever
link the station used. A subscriber that reports a failed connection
needs the BSSID of the BSS that the station tried to connect to, so it
named the wrong BSS for an attempt on another link.
hostapd_ubus_notify_key_mismatch() now adds:
- bssid: own_addr of the hostapd_data that handles the failure, the
BSSID of the link for an affiliated AP, as it is: the link of the
SAE Authentication frame, or the association link for the 4-way
handshake;
- link_id: the link ID of that affiliated AP, for an AP MLD only.
The 4-way PSK failure of hostapd_wpa_auth_psk_failure_report() (an
invalid MIC in EAPOL message 2) now goes through the same function, so
its event carries the two fields too. It carries no rsn_error:
HOSTAPD_UBUS_RSN_ERROR_NONE (0, a reserved value) leaves the field out.
RSN Error 3 (EAPOL message 2) belongs to a separate patch that changes
the psk_failure_report callback to pass the EAPOL-Key frame. Until that
patch lands, the event of a PSK mismatch on an RSNA BSS carries no
rsn_error. The SAE Confirm event does not change otherwise.
The two lines the call takes in 600 move the wpa_auth_glue.c hunks of
601 and 730 by one; refresh them so that the series applies without
offsets.
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>
A new station MLD took over the address of a removed one only where
the names were the same. netifd numbers the MLO interfaces in the order
of their sections, so a new section renames a station MLD (sta-mld0 to
sta-mld1), and the station MLD got a new address. Other devices then
take it for a new station MLD, although it continues the existing one.
mld_set_config() now uses the rule of the AP MLD (mld_prev_match()):
on one wiphy, the same SSID and radios, then the same SSID. For a
continued station MLD with another name, the MLD sync names the address
of the old one; the entry of the old name loses it, and the new name
claims it. A station MLD that continues none asks by its name, as
before.
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>
wnm_send_bss_tm_req() passes buf + sizeof(buf) - pos as the space left
for the MBO element, with buf a pointer. On a 64-bit target sizeof(buf)
is 8 and pos is at least buf + 31, so the value converts to a size_t
near SIZE_MAX and the check in mbo_add_ie() never fails. The allocation,
os_zalloc(1000 + nei_rep_len + mbo_len), leaves room for at most 299
octets of fixed content and the 6 octet MBO element header, so no write
goes past it.
Upstream hostap has the same line on main, pending, main-check, pqc and
eppke as of today, and no commit changes it, so 072 is a local patch
that passes the space left in the allocation.
The two lines that 072 adds move the last hunk of 590 by two; refresh
it so that the series applies without offsets.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd_bss_tr_send() allocates nr for the neighbour reports and freed
it only where hexstr2bin() failed. wnm_send_bss_tm_req() copies the
buffer and does not take it over, so every request with neighbors leaked
it, and so did the returns of the MBO checks after the allocation.
The MBO checks and attributes need nothing from nr, so they now run
before the allocation, and nr is freed after wnm_send_bss_tm_req(),
whatever it returns. The one return after the allocation that remains,
a failure of hexstr2bin(), frees nr with os_free(), which matches
os_zalloc().
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd_bss_transition_request() read each value as a u32 and then
narrowed it without a check. reassoc_delay, mbo_reason and cell_pref were
u8 locals, and hostapd_bss_tr_send() took da_timer as u16 and
valid_period and dialog_token as u8. A value that did not fit lost its
high bits:
- an Association Retry Delay of 256 to 65535 went out as its low octet,
although the attribute holds two octets, and the check against 65535
could never be true;
- a Transition Reason Code of 256 became 0 and passed the check against
MBO_TRANSITION_REASON_PREMIUM_AP;
- the same applied to the Cellular Data Connection Preference, the
Validity Interval, the Dialog Token and the Disassociation Timer.
The request values now stay u32, and the handler answers
UBUS_STATUS_INVALID_ARGUMENT for a value larger than its field:
- Dialog Token and Validity Interval: one octet; Disassociation Timer:
two octets (IEEE Std 802.11-2024, 9.6.13.9, Figure 9-1275);
- Transition Reason Code and Cellular Data Connection Preference: one
octet (MBO v0.0_r19, 4.2.6 and 4.2.5, as ieee802_11_defs.h names
them); Association Retry Delay: two octets, as hostapd_bss_tr_send()
writes it. hostapd's MBO code names no section for that attribute.
hostapd_bss_tr_send() takes reassoc_delay as u16 and keeps only the
check that a delay needs Disassociation Imminent.
Signed-off-by: Felix Fietkau <nbd@nbd.name>