Linux Wifi Regulatory Framework
This review of the Linux Wifi regulatory functions exists in order to help better understand the effects of FCC Regulation on OpenWrt. Both the kernel regulatory module and CRDA functions are summarized in this document. Drivers are not since their construction varies between drivers.
- 1 Kernel
- 1.1 net/wireless/reg.c
- 1.1.1 regulatory_init
- 1.1.2 regulatory_hint_core
- 1.1.3 reg_todo
- 1.1.4 reg_process_pending_hints
- 1.1.5 reg_process_pending_beacon_hints
- 1.1.6 reg_process_self_managed_hints
- 1.1.7 reg_check_channels
- 1.1.8 reg_process_hint
- 1.1.9 reg_process_hint_core
- 1.1.10 reg_process_hint_user
- 1.1.11 reg_process_hint_driver
- 1.1.12 reg_process_hint_country_ie
- 1.1.13 reg_call_crda
- 1.1.14 call_crda
- 1.1.15 reg_regdb_query
- 1.1.16 reg_regdb_search
- 1.1.17 set_regdom
- 1.1.18 reg_set_rd_core
- 1.1.19 reg_set_rd_user
- 1.1.20 reg_set_rd_driver
- 1.1.21 reg_set_rd_country_ie
- 1.1.22 update_world_regdomain
- 1.1.23 reset_regdomains
- 1.1.24 wiphy_update_new_beacon
- 1.1.25 handle_reg_beacon
- 1.2 net/wireless/nl80211.c
- 1.1 net/wireless/reg.c
- 2 CRDA
Based upon commit a6c5170d1edea97c538c81e377e56c7b5c5b7e63 of the Linux kernel.
The heart of the regulatory system is in the regulatory kernel module.
regulatory_init is the initialization function for the wireless regulatory kernel module.
On line 3086, a simple device is registered. This device is used to notify udev to notify listeners of a request for regulatory information. This device will be used later for starting CRDA via udev IF CRDA IS INSTALLED.
At line 3095, the regulatory domain is set to the world regulatory domain. . This exists to have something a default domain in the situation
regulatory_hint_core adds work into the queue used by a worker thread for processing hint requests. The regulatory_hint_core is used is that the world_regulatory_domain may be updated in CRDA or in the internal regulatory DB array. The worker thread calls reg_todo.
reg_todo calls the functions for handling all pending hints.
reg_process_pending_hints processes the first regulatory hint in the queue by taking out the first queue item (or the last request depending on situation). In the event that all the wifi card driver has self-managed regulatory domains, we’ll return at line 2176 and the regulatory request will never be processed and thrown out. Otherwise, reg_process_hint is called at line 2179.
reg_process_pending_beacon_hints processes beacon hints. Beacon hints will loosen the restriction on a device radiating intentionally.
reg_process_self_managed_hints sets the main regdomain when a driver says it self-manages wifi regulatory functionality.
Most of the function handles the process of setting the regulatory functionality for a particular wiphy. That said, one notable line is line 2240 where user mode is notified that the available channels might have changed. Additionally, the reg_check_channels function is called.
reg_check_channels schedules a check of the currently used channels to verify they are still allowed under the current regulatory domain. The check will occur in 60 seconds after the job is scheduled. Once the job commences, WiFi connections using prohibited channels under the new regulatory domain will be disconnected without any additional warning.
reg_process_hint, at line 2074 handles a single regulatory request passed into it. Used for processing all regulatory requests, each request is handled differently based upon the hint initiator.
- Hint from country information element (NL80211_REGDOM_SET_BY_COUNTRY_IE) - reg_process_hint_country_ie
__reg_process_hint_user uses the current state of the regulatory system and request information to figure out how the user hint should be handled. . If the user hint is considered OK or should be intersected with the current regulatory domain, it's processed and sent to reg_call_crda at line 1902. Otherwise it's rejected and not applied .
reg_process_hint_driver processes a single driver-hinted regulatory request. NOTE: This is NOT how fully self-managed WiFi drivers are handled; instead they are handled in reg_process_self_managed_hints. The main functionality of reg_process_hint_driver occurs in __reg_process_hint_driver called at line 1944.
__reg_process_hint_driver uses the current state of the regulatory system and request information to figure out how the driver hint should be handled. . If the driver hint is considered OK or should be intersected with the current regulatory domain, it's processed and sent to reg_call_crda at line 1984. If the driver hint has already been processed, userspace isnotified of the regulatory domain change.
reg_process_hint_country_ie processes a single country information element (ie) regulatory request. The main functionality of reg_process_hint_country_ie occurs in __reg_process_hint_country_ie called at line 2044.
__reg_process_hint_country_ie uses the current state of the regulatory system and request information to figure out how the driver hint should be handled.
After getting a result from __reg_process_hint_country_ie returns, a set of decisions are made based upon how the hint should be handled. If the driver hint is considered OK, it's processed and sent to reg_call_crda at line 2070. If the country ie hint has already been processed, the function returns at https://github.com/torvalds/linux/blob/a6c5170d1edea97c538c81e377e56c7b5c5b7e63/net/wireless/reg.c#L2054 line 2054]
The unfortunately named call_crda is the root of setting regdomains based on country code. It does this in a two ways:
- first, it calls reg_regdb_query
- second, it raises a udev device change event. During CRDA installation, a udev rule responding to that event is set up.
The incompletely named reg_regdb_search searches through the internal array of regdomains by country code and sets the regdomain if an appropriate one if found.. A C file is written and built during the main kernel compilation process. The source of the array is from the regdb.txt file which can also be used by CRDA. The intent of this internal "db" is to have the up-to-date regulatory information built into the kernel at compile time. CRDA can supplement that by using a newer, verified copy of the regulatory database.
set_regdom is responsible for setting the regulatory domain, whether globally or for a particular wiphy. This varies based upon the the type of regdomain request which we get via get_last_request:
- If the request is to set via a country information element, a call is made to reg_set_rd_country_ie.
reg_set_rd_user is responsible for the final setting of a user initiated regdomain change. Outside of some checks on the appropriate values of the input, there are two main paths in the function based upon whether the user request should be intersected with the current regdomain.
reg_set_rd_driver is responsible for the final setting of a driver initiated regdomain change. Outside of some checks on the appropriate values of the input, there are two main paths in the function based upon whether the driver request should be intersected with the current regdomain.
reg_set_rd_country_ie is responsible for the final setting of a country information element (ie) initiated regdomain change. After some checks on the appropriate values of the input, reset_regdomains is called.
update_world_regdomain does two things:
- Calls reset_regdomains to set our current regdomain the new world regdomain
- Sets the world regdomain to the new regdomain
reset_regdomains handles properly setting our main regdomain to a newly passed in regdomain. This is necessary because there are some corner cases of handling the world regdomain being passed in. Additionally, there's a case when restoring the regulatory settings or shutting the regulatory system down where one would want to fully reset the regulatory system to an initial state.
wiphy_update_new_beacon is "[c]alled when a scan on a wiphy finds a beacon on a new channel."
A very simple method, wiphy_update_new_beacon verifies that the wiphy device actually can support the band of the reg beacon was transmitted on. A call to handle_reg_beacon is made for each of the channels on the band that the beacon transmitted on.
handle_reg_beacon handles a beacon hint on a channel and, if certain conditions are true and regdomain currently bans transmission on that channel, enables transmission for that channel on the current regdomain.
handle_reg_beacon checks if the channel passed in matches the channel of the beacon channel. If it doesn't, the function returns.
If the channel matches, the channel will be marked as allowing transmission if the following conditions are true:
- the current wiphy device's regdomain is set to world roaming.
- the current wiphy device allows beacon hints (REGULATORY_DISABLE_BEACON_HINTS flag not set)
- the channel is currently marked as not allowing active transmission (IEEE80211_CHAN_NO_IR flag is set)
If the channel is newly marked as allowing transmission, a netlink event notifying listeners of the beacon hint event is sent.
nl80211_set_reg is called when the kernel receives a netlink message with the netlink message family of NL80211_CMD_SET_REG. This is intended to come from CRDA but another application could potentially send an appropriate netlink message.
Most lines of the nl80211_set_reg function deals with parsing the netlink message and error handling. The line most relevant to the Linux wifi regulatory framework is line 5646 where set_regdom defined in net/wireless/reg.c is called.
Based upon CRDA repository commit 2019d7085c8b4dea3bb22f13e168fe4a8844e5ba
CRDA provides a way of userspace providing updated regulatory information to the kernel. This allows regulatory information to be updated without a new build of the kernel and a corresponding reboot.
The primary functions of CRDA occur in a single file, crda.c.
CRDA message sends regulatory information using via netlink with a L80211_CMD_SET_REG. Once sent, the information triggers nl80211_set_reg in net/wireless/nl80211.c.
CRDA runs by using the COUNTRY environment variable as the two letter abbreviation of the regulatory domain. It then validates the signed hash of the first database it finds in the from regdb_paths array found at line 156. For validation it uses all of the public keys in PUBKEY_DIR which is declared in the CRDA Makefile or by calling make.
The CRDA db format is a binary file consisting of a header containing information about the db file, the db contents and a hash of the db contents. The hash is encrypted using an RSA private key of the trusted creator of the database. The creator is usually the maintainer of the official wireless-regdb at the time but doesn't have to be necessarily.
OpenWrt does not include CRDA by default and simply uses the internal kernel database of regdomains.