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>
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>
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>
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>
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>
These locals are never read under any configuration:
- hostapd_notify_response(): elems, pos, len;
- hostapd_bss_wps_status(): rc;
- hostapd_rrm_print_nr(): data;
- hostapd_bss_mgmt_enable(): cur, neigh, beacon;
- hostapd_rrm_beacon_req(): mode, which was assigned only; the frame
reads the Measurement Mode from the request attribute;
- hostapd_bss_transition_request(): sta;
- hostapd_wired_get_clients(): sta_driver_data, r;
- hostapd_ubus_notify_bss_transition_response(): i;
- hostapd_ubus_notify_bss_transition_query(): cl_str, i.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd_bss_del_client() sets reason only when the request carries
"reason". A request without it passed an uninitialised int to
hostapd_drv_sta_deauth() or hostapd_drv_sta_disassoc() and to
ap_sta_deauthenticate() or ap_sta_disassociate(). The station then got
whatever value the stack held as the Reason Code of the Deauthentication
or Disassociation frame.
Start reason at WLAN_REASON_UNSPECIFIED (1). Reason code 0 is reserved
(IEEE Std 802.11-2024, Table 9-79).
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A subscriber that reports a failed connection on an RSNA BSS needs the
RSN Error, which names the frame in which the RSN procedure failed, and
the body of the last frame that the station sent. The key-mismatch
event carried the station address and the interface name only, so a
subscriber could fill in neither field.
For a mismatching SAE Confirm in handle_auth_sae(), the event now also
carries:
- rsn_error: 1 (Authentication frame);
- frame: the Authentication frame body, from the Authentication
Algorithm Number field to the end of the frame, as lowercase hex.
The event name and the address and ifname fields do not change, so a
subscriber that reads only those keeps working. For a non-AP MLD the
address is the STA MLD address: handle_auth() looks up or adds the
station by the MLD address of the Basic Multi-Link element.
The key-mismatch event of hostapd_wpa_auth_psk_failure_report() (EAPOL
message 2) does not change.
The hex encoding of a blobmsg field moves into blobmsg_add_hex(), which
the action-frame event and the neighbour report output use too.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A subscriber that reports each affiliated STA of an associated client
MLD needs the STA's link address and the BSSID of the affiliated AP of
its setup link. hostapd's ubus named the MLD address only, and the
kernel's station dump names neither, so a subscriber reported the MLD
address as the affiliated STA, and could not map the link of a backhaul
STA to its AP link.
For a client MLD, get_clients and sta-authorized now carry
ap_mld_address, assoc_link_id and links [{ link_id, address, bssid }],
one entry per accepted link, read from the station of the association
link. Stations that are not an MLD get no new field.
A new sta-links-changed event carries the same fields where the links
change without a new association:
- 604: when a Link Reconfiguration Response that accepted an addition or
a removal is acknowledged;
- 605: when an affiliated AP leaves the AP MLD and its link stations are
freed.
197 frees a link station through one helper, ap_free_link_sta(), which
also clears the link on the partner stations and updates the state
machine's link information. It undoes the links a request added on every
path that refuses or does not complete it, where before the partner
stations kept them as set up. 604 depends on 197, and 605 builds on
both.
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>
A station whose configuration changed only in its credentials was removed
and added again, or with RECONFIGURE deauthenticated, although the new
settings could still allow the association in use. For example, a change
from PSK and SAE to SAE only, while the station is associated with SAE,
made the station associate a second time.
The iface method network_update() reads the regenerated file with
wpa_config_read() and, where its one network is the current one and the
association in use (SSID, AKM, protocol, ciphers, PMF, the effective
password and SAE PWE) still fits it, moves the credential fields into the
current network without the PMKSA flush that SET_NETWORK does. Otherwise
it refuses, and the caller removes and adds the interface as before.
wpa_supplicant.uc takes this path for a plain station and a station MLD
whose configuration differs only in the credential lines, and applies the
MLD's freq_list again after it.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
601-ucode_support.patch adds the first link of an AP MLD on the
existing netdev with no address, and hostapd_setup_bss() then takes the
netdev address, the MLD address, as the link address. A link on a
radio other than radio 0, whose configured BSSID hostapd.uc keeps apart
from the MLD address (bss_mld_addr()), beaconed with the MLD address,
while ubus get_status reported the configured BSSID. A subscriber that
built its BSS Transition candidates from get_status named a BSSID that
nothing transmitted.
Add a patch that keeps the configured BSSID as the link address and
takes the netdev address as the MLD address, as hostapd_driver_init()
does for the first interface. Without a configured BSSID, the link
takes a random address with the OUI of the netdev, as
hostapd_driver_init() does (pre_setup_mld), so that the link address
cannot be the MLD address: a later link that is given the MLD address
as its BSSID would fail ADD_LINK with -EALREADY. hostapd.uc always sets
a BSSID, so OpenWrt configurations take the configured address.
Have get_status and the channel-switch event report hapd->own_addr,
the address the BSS transmits with, instead of conf->bssid, which is
all zeros where the configuration names none.
Fixes: 816c2d86e7 ("hostapd: add support for MLO interfaces in ucode")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The nl80211 driver gave up the wiphy's Beacon event subscription
whenever one link stopped, and the other links of an AP MLD lost the
OBSS Beacon events that feed OLBC detection and the AP list. Add a
patch that gives it up with the last link.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Patch 186 counts the AP Removal Timer and left the BSS running at 0, so
the removed link kept beaconing until the caller's configuration
removed it, 1.64 s late on MT7996. Add a patch that stops the
affiliated AP at the expiry of its timer and hands the BSS to the ucode
bss_link_removed hook.
hostapd knows the TBTT to one beacon interval only: nl80211 reports
neither a Beacon transmission nor the TSF of an AP. The count runs from
the return of the first Beacon update with the count, and the link
stops after the Beacon with the count 1 and before the TBTT the count
names, so no Beacon carries the reserved value 0.
At the expiry, the other links drop the Reconfiguration element. Each
station associated over the link gets a Disassociation, or a
Deauthentication on a remaining link where it is a non-AP MLD, and the
link waits for their TX status, for at most a quarter of a beacon
interval. The patch message lists the limits of the timing. A link in
an MBSSID set with other BSSes is refused, as mac80211 stops the whole
set with it.
The bss_link_removed hook removes the BSS the way a configuration
without it would: bss.delete(), or the removal of the PHY's interface
where it was the only BSS. It has no fallback to a PHY restart, which
would take the PHY's other BSSes down. The configuration that follows
then finds nothing to do. The hook also takes the radio of the link out
of the MLD's configuration, so that a configuration that brings the
link back creates the MLD netdev again, as mld_config_matches() does
for any radio that joins.
The patch also refuses to announce the removal of a link where no other
link of the AP MLD beacons, and checks again at the expiry, as the other
links can stop during the countdown. IEEE 802.11be-2024 35.3.6.3
removes an affiliated AP from an AP MLD that goes on. Such a link keeps
running with its Beacon rewritten without the Reconfiguration element,
and the caller's configuration decides whether it goes. The
mld_link_remove ubus method answers a refusal with
UBUS_STATUS_NOT_SUPPORTED, and no event reports a link kept at the
expiry.
The mld_link_remove ubus method now answers expiry_ms, the time until
the expiry, rounded down, and removal_ms, the time until the link stops
at the latest, with that wait, rounded up.
A BSS of an AP MLD that goes through bss.delete() or remove_iface
without the timer now has the Beacons of the links that stay rewritten,
as they named it in their Reduced Neighbor Report, the number of
simultaneous links and a Reconfiguration element whose count stopped.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
All links of an AP MLD carry one SSID. With cfg80211 keeping the SSID of
an interface while another link beacons, nl80211_start_ap() refuses a
link whose SSID differs from it ("require identical SSID for MLO"). An
SSID change applied PHY by PHY then leaves the first PHY's link down:
its new SSID is refused while the other links still beacon with the
old one.
Treat an SSID change as a new AP MLD in mld_config_matches(), so every
link stops before any starts with the new SSID.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
mld_config_matches() kept an MLD netdev only where its radio mask was
unchanged. An AP MLD that lost one affiliated AP was therefore removed
and created again, and every link stopped beaconing while it was.
Keep the MLD where its radios are a subset of the radios it runs now.
The removed radio's configuration drops the BSS, and bss.delete() takes
the link off the netdev (NL80211_CMD_REMOVE_LINK) while the other links
keep running. The netdev keeps the radio mask it was created with,
which cannot change while it is up, and which may name a radio without
a link. A radio that joins, the one of a removed link included, still
gets a new netdev: mt7996 keeps the state of a removed link until the
netdev goes, and adding the link again beside it is not known to work.
uc_hostapd_bss_delete() frees the link stations of an MLD BSS before its
STOP_AP, which flushes every station that holds the link: a non-AP MLD
that also holds another link keeps its association only where its link
station goes first.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The links of an AP MLD share one interface name. Each asked for the
object of that name, the first link got it, and the others failed. The
object, its subscribers, and the state a subscriber set through it
belonged to that one link:
- the object went away with that link, although other links remained,
and a subscriber took the removal for the end of the AP MLD and of
every station on it;
- notify_response, the BSS Transition Management Query answer flag and
the bans applied to that link only, while each link read its own;
- bss_mgmt_enable and the rrm_nr methods changed the capabilities and
the neighbor report database of that link only.
Keep one object per AP MLD, from its first link to its last. A method
runs on a link that is there: the AP MLD's first link once its setup
has added it to the object, or, once the last link has left the AP MLD
and before its BSS frees the object, a link the object still counts. A
link is counted once. Notifications of every link go through it.
notify_response, the BTM query answer and the bans are the AP MLD's,
and every link reads them. bss_mgmt_enable, the implicit neighbor
report enable of the rrm_nr methods and rrm_nr_set apply to every
link, and a link that joins later takes the flags and the neighbor list
set before.
The methods that name a station act on the link that holds it:
get_clients lists the stations of every link, a non-AP MLD once with
the station of its association link, and rrm_beacon_req, rrm_lm_req
and update_airtime find the station on any link, as del_client and
bss_transition_request already did. They answered for the first link
only, and after that link left the AP MLD, for another one.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The teardown of a BSS of an AP MLD freed the stations of that BSS and
left the partner link stations of each non-AP MLD associated over it
on the other links, with no state machine. Add a patch that frees them
in hostapd_bss_deinit_no_free(), which every teardown path of a BSS
runs.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A link that leaves the AP MLD frees the link stations of the non-AP
MLDs associated over another link, for example patch 186 at the count
of 0. Each one reported AP-STA-DISCONNECTED and a ubus "disassoc" event
with the MLD address, although the non-AP MLD stays associated over its
other links, and a subscriber took the event for the station's
departure. Add a patch that frees such a link station without the
event.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd_drv_sta_disassoc() gave a link ID only for a non-AP MLD. The
Disassociation to any other station of an AP MLD went out with the
frequency of the interface's first link and the link address of its
BSS, and where that BSS was not the first link, mac80211 refused the
frame with -EBUSY. Add a patch that passes the link ID for every station
of an AP MLD, as hostapd_drv_sta_deauth() does.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A ban set through del_client or the ban call arms a timeout that names
the BSS, and hostapd_ubus_free_bss() neither cancelled it nor freed the
ban. A BSS freed while a ban was pending left a timeout that deleted
from the ban list of freed memory. The name and the bans also leaked
where the ubus context was already gone.
Name the ban list in the timeout rather than the BSS, and drop every
ban of a BSS when it is freed, whether the context is still there or
not.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
__hostapd_bss_mgmt_enable_f() tested
bss->radio_measurements[0] & flags == flags, which C parses as
bss->radio_measurements[0] & (flags == flags), that is, the Link
Measurement bit (bit 0).
For Beacon Report, the test read the wrong bit: Beacon Report was left
off wherever Link Measurement was already on, and was reported as
changed where its bits were already set and Link Measurement was off.
For Link Measurement, whose flag is bit 0, the test gave the right
answer.
Compare the masked bits with the set in both cases.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
`status` says whether a BSS runs, but not why one does not. A BSS in
HT_SCAN starts within seconds, one in DFS waits for the channel
availability check, and one that is DISABLED may not start at all. A
caller that waits for BSSs to come up needs to tell these apart.
Add `state` to each link and each non-MLO entry, as iface.state() names
it: UNINITIALIZED, DISABLED, COUNTRY_UPDATE, ACS, HT_SCAN, DFS, NO_IR or
ENABLED.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
bss.rename() closed the control socket of the BSS and opened a new one
under the new name. This dropped every attached monitor, for example
hostapd_cli -a or a wpa_cli in interactive mode. Keeping the list of
monitors does not help: the socket of a monitor is connected to the old
control socket, and the kernel refuses datagrams to it from any other
socket (unix_may_send()).
Keep the socket and give it the new name: link() the new path to the
socket file, then unlink() the old one. connect() finds a socket by
the inode of its path, so new clients reach the same socket. link()
does not replace an existing file, so a name that is in use stays
intact. If the move fails, for example on a leftover file of that
name, close the socket and open a new one as before.
The ucode code needs the path of the socket, which only
hostapd/ctrl_iface.c builds. Export it through hapd_interfaces, as
ctrl_iface_recv is.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
bss.rename() ignored the results of hostapd_set_ctrl_sock_iface() and
ctrl_iface_init(). A BSS that could not open the control socket under
its new name was reported as renamed, and ran on without a control
socket.
Return null in that case, as add_bss() does when ctrl_iface_init()
fails. hostapd.uc then restarts the phy, which opens the control
sockets again.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
bss.rename() freed the ubus object and closed the control socket before
it renamed the netdev. If the driver rename failed, it only opened the
control socket again, and the BSS stayed without its ubus object.
Rename the netdev first. Nothing else depends on its name, and a
failure now leaves the BSS as it was.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
/etc/board.json contains runtime wlan data, which can differ with the
time of wiphy detection. board-base.json holds only the board.d data.
The DHT id changes once after the upgrade.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
An AP MLD has to announce the removal of an affiliated AP in its Beacon
and Probe Response frames before it removes the AP (IEEE 802.11be
35.3.6.3). hostapd builds the Reconfiguration Multi-Link element only
with CONFIG_TESTING_OPTIONS, and no ubus method reaches it, so a caller
that drops a link of an AP MLD cannot announce it.
Patch 186 builds the link removal without CONFIG_TESTING_OPTIONS, takes
the links of the element from the AP MLD, sets the Critical Update
flag, and at the count of 0 leaves the BSS running instead of disabling
the whole radio.
Add bss.link_remove(count) to the ucode API and mld_link_remove
{ phy, radio, iface, count } to the hostapd object. The reply gives
removal_ms, the time until the count reaches 0; a repeated call keeps
the running count. A count outside 1 to 65535 is an invalid argument.
The caller then removes the affiliated AP by reconfiguring it.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The class 3 deauth went out on the link whose address equals the AP MLD
address, where a station on another link never takes it.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The error path of iface.add_bss() freed an AP MLD link that
hostapd_setup_bss() had already added to mld->links, without the MLD
reference it took. The next link to join that MLD wrote through the
freed node, which crashed hostapd after "Failed to add new bss ap-mld0
on phy phy0.0" and the restart of phy0.0 that follows it. The error
path now unlinks the BSS, drops the reference and frees an MLD left
unused, as hostapd_add_iface() and bss.delete() do.
Patch 182 makes hostapd_mld_remove_link() skip a node that is on no
list, which the new unlink reaches for a setup that fails before the
link joins.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A Measurement Report element of a Beacon report can come without its
Measurement Report field: from a station that refuses the request or
cannot perform it, with the Refused or Incapable bit set (802.11-2024
9.4.2.20.1), and as the empty Beacon report of a station that has no
beacon information for the request, with no bit set (11.10.9.1.1). An
AX210 class station sends the latter for a Passive request on a channel
with no AP. hostapd_handle_beacon_report() passed only reports that
carry the field to hostapd_ubus_notify_beacon_report(), which dropped a
report without one, so such an answer never reached ubus.
Such a report now goes out on ubus with its address, tokens, interface
and rep-mode, and without the fields of the Measurement Report.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
When the AP a station MLD is connected to switches the channel of one
link, wpa_supplicant receives EVENT_LINK_CH_SWITCH_STARTED rather than
EVENT_CH_SWITCH_STARTED. wpas_ucode_event() passed only the latter to
ucode, so hostapd was never told to move the APs on the radio of that
link. mac80211 then could not finalize the shared channel context
("some interfaces aren't switching") and dropped the station link.
Pass the link event with the same fields plus link_id, and handle it
like the interface event; iface_channel_switch() already maps the
frequency to the radio of an MLD.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A per-STA profile in the Basic Multi-Link element of an (Re)Association
Response inherits each element of the frame that carries the Basic
Multi-Link element, unless a Non-Inheritance element lists it (IEEE
802.11be 35.3.3.5.1). ieee80211_ml_build_assoc_resp() adds no such
element. When the station associates on a 5 GHz link, the profile of a
2.4 GHz partner link omits VHT Capabilities and VHT Operation and so
inherits those of the 5 GHz link. mac80211 with IEEE80211_HW_STRICT then
rejects the association ("[link 0] VHT capabilities mismatch"), and a
driver without it sets up the 2.4 GHz link with VHT parameters the AP
does not use there.
List HT Capabilities, HT Operation, VHT Capabilities, VHT Operation and
HE 6 GHz Band Capabilities in a Non-Inheritance element where the
carrying frame has them and the link does not, as a Non-Inheritance
element names only elements of that frame (9.4.2.239). A Link
Reconfiguration Response carries none of them outside the Basic
Multi-Link element, so its profiles get no Non-Inheritance element.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
wpas_dpp_chirp_start() sends each Presence Announcement with a wait time
of 2000 ms. cfg80211 refuses a frame whose wait exceeds the driver's
maximum remain-on-channel duration with -EINVAL (nl80211_tx_mgmt()), so
on a driver with a smaller maximum, such as mac80211_hwsim with 1000 ms,
no chirp is ever transmitted and the enrollee cannot be found.
Clamp the wait time to max_remain_on_chan, as the other DPP transmit
paths in this file do.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
A later link of an AP MLD takes the driver instance of the first link
(hapd->drv_priv = h_hapd->drv_priv) in hostapd_driver_init() and joins
the MLD only after hostapd_drv_link_add() succeeds. When that call, or
the check for driver MLO support, fails, the link returns -1 with the
borrowed pointer still set.
The caller's cleanup, hostapd_deinit_driver(), then treats the instance
as its own: hostapd_cleanup_driver() removes the link with this BSS's
link ID from the shared instance, which is the link of another BSS when
the link IDs collide, or deinits the instance when it looks unshared.
The first link keeps a pointer to a torn down or freed instance, and the
next link that is set up calls get_capa() on it and crashes hostapd.
Seen with an AP MLD whose links span two wiphys: the link on the second
wiphy was assigned link ID 0, hostapd_drv_link_add() refused it, and the
cleanup removed link 0 of the first wiphy.
A link that never joined the MLD now drops the borrowed instance before
it returns, so the cleanup leaves the first link alone.
Signed-off-by: Felix Fietkau <nbd@nbd.name>