Files
Felix Fietkau eb8cbaeebd wifi-scripts, hostapd: take wifi MAC addresses from netifd
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>
2026-10-08 09:50:38 +02:00

57 lines
1.2 KiB
Makefile

#
# Copyright (C) 2024 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=wifi-scripts
PKG_VERSION:=1.0
PKG_RELEASE:=3
PKG_LICENSE:=GPL-2.0
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
PKG_CONFIG_DEPENDS:=CONFIG_WIFI_SCRIPTS_UCODE
include $(INCLUDE_DIR)/package.mk
define Package/wifi-scripts
SECTION:=utils
CATEGORY:=Base system
DEPENDS:=+netifd +ucode +ucode-mod-nl80211 +ucode-mod-rtnl +ucode-mod-ubus +ucode-mod-uci +ucode-mod-digest
TITLE:=Wi-Fi configuration scripts
PKGARCH:=all
endef
define Package/wifi-scripts/config
source "$(SOURCE)/Config.in"
endef
define Package/wifi-scripts/description
A set of scripts that handle setup and configuration of Wi-Fi devices.
endef
define Build/Prepare
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/wifi-scripts/install
$(INSTALL_DIR) $(1)
$(CP) ./files/* $(1)/
ifeq ($(CONFIG_WIFI_SCRIPTS_UCODE),y)
$(CP) ./files-ucode/* $(1)/
endif
ifeq ($(CONFIG_WIFI_SCRIPTS_WPS_BUTTON_DISABLED),y)
rm -f $(1)/etc/rc.button/wps
endif
endef
$(eval $(call BuildPackage,wifi-scripts))