Setting the wireless regulatory domain in Linux on your laptop

I travel internationally and want to make sure that my laptop uses the legal wireless networking frequencies in the country I am visiting. In Linux, CRDA (Central Regulatory Domain Agent) is the udev helper used to communicate between userspace and the kernel, and it enables you to view and alter the wireless regulatory domain your kernel uses. For more information see the Regulatory page on the Linux Wireless Wiki site.

CFG80211 is the Linux wireless LAN (802.11) configuration API. The kernel on my main laptop has the following configuration settings relating to CFG80211:

# cat /usr/src/linux/.config | grep CFG80211
CONFIG_CFG80211=m
# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
# CONFIG_CFG80211_REG_DEBUG is not set
CONFIG_CFG80211_DEFAULT_PS=y
# CONFIG_CFG80211_DEBUGFS is not set
# CONFIG_CFG80211_INTERNAL_REGDB is not set
CONFIG_CFG80211_WEXT=y

and the cfg80211 module is loaded:

# lsmod | grep cfg80211
cfg80211 145747 3 iwlwifi,mac80211,iwldvm

I have the package crda installed, and I have the following udev rule file /etc/udev/rules.d/regulatory.rules to allow the kernel to communicate with userspace:

KERNEL=="regulatory*", ACTION=="change", SUBSYSTEM=="platform", RUN+="/sbin/crda"

So, how do you check which wireless regulatory domain your kernel is currently using, and switch to another domain if necessary? These tasks are performed using the iw command. You’ll need to install the package iw if it is not already installed.

To see the regulatory domain your laptop is using now, enter the following command as root user:

iw reg get

When I use the above command on my laptop after start-up, I normally see the following:

# iw reg get
country 00:
(2402 - 2472 @ 40), (3, 20)
(2457 - 2482 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS
(2474 - 2494 @ 20), (3, 20), NO-OFDM, PASSIVE-SCAN, NO-IBSS
(5170 - 5250 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS
(5735 - 5835 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS

The country code 00 is not the code of the country I am in at present. To tell the kernel which wireless regulatory domain you wish to use, enter the following command as root user:

iw reg set ISO_3166-1_alpha-2

where ISO_3166-1_alpha-2 is the 2-character code for the country you are in. You can find the list of ISO 3166-1 alpha-2 codes on the Wikipedia page ISO 3166-1 alpha-2.

For example, if I were in the UK then I would enter the following command:

# iw reg set GB

and the regulatory domain would then be reported like this:

# iw reg get
country GB:
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ 40), (N/A, 20)
(5250 - 5330 @ 40), (N/A, 20), DFS
(5490 - 5710 @ 40), (N/A, 27), DFS

It is not a big deal to use the command line, but I wanted to make it even easier. I’m using KDE on my main laptop, so I created a Desktop Configuration File /home/fitzcarraldo/Desktop/Set_wireless_regulatory_domain containing the following:

[Desktop Entry]
Comment[en_GB]=
Comment=
Exec=/home/fitzcarraldo/iw_reg.sh
GenericName[en_GB]=Set wireless regulatory domain
GenericName=Set wireless regulatory domain
Icon=/home/fitzcarraldo/national-flags-icon.png
MimeType=
Name[en_GB]=Set_wireless_regulatory_domain
Name=Set_wireless_regulatory_domain
Path=
StartupNotify=true
Terminal=true
TerminalOptions=\s--noclose
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=none
X-KDE-SubstituteUID=false
X-KDE-Username=

and gave it the following file permissions:

# chmod 744 /home/fitzcarraldo/Desktop/Set_wireless_regulatory_domain
# ls -la /home/fitzcarraldo/Desktop/Set_wireless_regulatory_domain
-rwxr--r-- 1 fitzcarraldo users 496 Jan 15 21:53 /home/fitzcarraldo/Desktop/Set_wireless_regulatory_domain

I used a search engine to find a nice PNG icon consisting of several overlapping national flags, and saved it with the file name name national-flags-icon.png in my home directory.

I created a Bash shell script /home/fitzcarraldo/iw_reg.sh containing the following:

#!/bin/bash
echo "First you need to enter the password of your user account..."
sudo echo ""
echo "The ISO 3166-1 alpha-2 codes are listed on Web page https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2"
echo ""
echo "The current wireless regulatory domain is set as: "
echo ""
sudo iw reg get
echo ""
echo -n "Enter the ISO 3166-1 alpha-2 code (upper case) for the country you are in now, and press ENTER: "
read REGULATORYDOMAIN
sudo iw reg set $REGULATORYDOMAIN
echo ""
echo "The current wireless regulatory domain is now set as: "
echo ""
sudo iw reg get
echo ""
echo "All done. You can close this window."

and gave it the following file permissions:

# chmod 744 /home/fitzcarraldo/iw_reg.sh
# ls -la /home/fitzcarraldo/iw_reg.sh
-rwxr--r-- 1 fitzcarraldo users 632 Jan 15 21:33 /home/fitzcarraldo/iw_reg.sh

Now, if I double-click on the icon for Set_wireless_regulatory_domain on my desktop, a Konsole window pops up with a prompt for me to enter my user account password. When I enter my password the window displays the current wireless regulatory domain the kernel is using and prompts me to enter the 2-character code for the regulatory domain I wish to use instead. When I enter the country code the window displays the new regulatory domain, as shown in the sample below.


First you need to enter the password of your user account...
Password:

The ISO 3166-1 alpha-2 codes are listed on Web page https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2

The current wireless regulatory domain is set as:

country SA:
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ 20), (3, 23)
(5250 - 5330 @ 20), (3, 23), DFS
(5735 - 5835 @ 20), (3, 30)

Enter the ISO 3166-1 alpha-2 code (upper case) for the country you are in now, and press ENTER: GB

The current wireless regulatory domain is now set as:

country GB:
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ 40), (N/A, 20)
(5250 - 5330 @ 40), (N/A, 20), DFS
(5490 - 5710 @ 40), (N/A, 27), DFS

All done. You can close this window.

The task of viewing and changing the regulatory domain after start-up is now very easy for me. The only thing that would be easier than this would be if Linux could detect automatically which country I’m in and set the regulatory domain automatically.

EDIT (July 10, 2015): Note the following restriction when changing the regulatory domain: Helping compliance by allowing to change regulatory domains. See also the Gentoo Forums thread [solved] Atheros and regulatory domain.

About Fitzcarraldo
A Linux user with an interest in all things technical.

5 Responses to Setting the wireless regulatory domain in Linux on your laptop

  1. Rodney Anonymous says:

    “The only thing that would be easier than this would be if Linux could detect automatically which country I’m in and set the regulatory domain automatically.”

    As you like it …

    I have a script that runs a quick survey of Wi-Fi access points to find advertised regulatory country data. The script takes that data and turns it into a list of occurrences, selecting the most commonly advertised value.

    Since you’re looking at access points that are presumably well-configured (by vendors, IT staff, etc.) with the correct values, rather than random Wi-Fi users with odd settings, the selection should be correct most of the time.

    You could improve this by adding some code that refuses to set the regulatory country if there aren’t many APs available, although I don’t know if that provides any practical value.

    With that, here you go. 🙂

    — snip snip snip —
    #!/bin/bash

    #
    # iw-country-guess — guess which regulatory country
    # we’re in by surveying the Wi-Fi
    # access points within range
    #
    #

    # Set interface and IW command …..
    IFACE=”wlan0″
    IW=”/sbin/iw”

    # Reset PATH to something sane …..
    PATH=”/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin”
    export PATH

    # Grab a list of countries …..
    country=`${IW} dev ${IFACE} scan 2>&1 | \
    egrep “Country” | sed -e ‘1,$s/Country.//g’ | \
    awk ‘{ print $1 }’ | sort | uniq -c | sort -nr | \
    head -1 | awk ‘{ print $2 }’`
    country=`echo $country`

    # Try again if we come up empty …..
    if [ “q$country” = “q” ]
    then
    echo “Need to try again to guess the country …” >&2
    sleep 10

    country=`${IW} dev ${IFACE} scan 2>&1 | \
    egrep “Country” | sed -e ‘1,$s/Country.//g’ | \
    awk ‘{ print $1 }’ | sort | uniq -c | sort -nr | \
    head -1 | awk ‘{ print $2 }’`
    country=`echo $country`
    fi

    if [ “q$country” = “q” ]
    then
    echo “No clue what country we’re in!” >&2
    exit 1
    else
    echo “Setting regulatory country to ‘${country}’ …”

    ${IW} reg set ${country} 2>&1

    echo “Done.”
    fi

    exit 0

    — snip snip snip —

    • Fitzcarraldo says:

      Thanks for commenting. I’ll be in a less-developed country next week and will try your script to see how well it works. However, I have often wondered whether access points and laptops in such countries have been configured correctly for the country concerned, especially in airports and hotels. I would have more confidence in the regulatory domain being correct in access points and laptops sold in developed countries, but less so in many other parts of the World.

      I wonder whether geolocation by IP address would be another way of doing it. Even if the ISP is not located in the same place as the user, the ISP should at least be in the same country. Mind you, I have to use corporate wireless networks in some of the places I use my laptop, and those have proxy servers so I’m not sure if geolocation by IP address from my laptop would work in that case.

    • Fitzcarraldo says:

      I have now had a chance to try your script. The only thing I needed to change in order to get it to work in the Gentoo installation on my main laptop was the path of the iw binary, as follows:

      IW="/usr/sbin/iw"

      When I use your script at home it does set the correct regulatory country code (my laptop wireless card detects my network and the networks of several neighbours). However, when I run your script in a large international hotel in a less-developed country where I am at the moment on a work visit:

      # ./iw-country-guess.sh
      Password:
      Need to try again to guess the country ...
      No clue what country we're in!
      # ./iw-country-guess.sh
      Setting regulatory country to 'XZ' ...
      Done.
      #

      ‘XZ’ is used by the UN/LOCODE to represent installations in international waters outside national boundaries.

      I also ran the script in the international airport of that country (free WiFi service by the airport operator) but your script set the regulatory country code to that of an adjacent country.

      So it seems that, as I suspected, automatic detection of the regulatory country code from access points may not be viable in less-developed countries. I assume this is because access point equipment is imported and, unlike the situation in more-developed countries (USA, Western Europe, Japan and the like), the installers do not bother to configure the correct regulatory domain. I therefore used my script to set manually the regulatory domain for the wireless card in my laptop.

  2. mishmich says:

    I was really hopeful when I saw this, and this would be really helpful, if
    iw dev (wlan0) scan
    returned a “Country”
    for me, there’s nothing to grep
    Ubuntu 16.04 MATE

    The adapter starts as country 00.
    This started because although my ISP-supplied router is set to NZ, on my Mac, the 2.4 GHz is reporting “00”, and the 5 GHz “DE”. There is no option to change this on the router. The Mac complains about conflicting country codes when switching between these, as it sets the country code to whichever it connects to first. There is no way of setting this on the Mac, as they say it needs to be done on the router. Factory reset on the router makes no difference. Tried a different Mac the same.

    I’ve spent a while trying to find a way to find out what a linux laptop sees (so I can determine whether the issue really is the router, or just the way Macs read the output), but this is the closest I have got to somebody suggesting a way of getting the country code via a scan.

    On my linux laptop, the code reads as 00, and when I set it to NZ, it reads as NZ (or AU, or GB, tried a few). If I reboot, it is set back to 00. If I run a scan, and grep on “ountry” nothing shows. I’ve reviewed the output, and can see nothing comparable either.

    Thanks though, this is the closest I’ve got to finding the country code of the routers in my location without using a Mac.

  3. mishmich says:

    I made a mistake – the adapter only reports a country code if the country code is set. So, it does report a country code for “DE”, but not for “00”. This is great – it proves that the issue is with the router, not the Mac.

    ~$ sudo iw dev wlp2s0 scan | egrep “ountry”
    Country: DE Environment: Indoor/Outdoor

    Thanks, apologies for the waffle. The other three SSID’s are blank, and do not show up in the grep. Odd, because the other router is set to AU, but that is not picked up by this on Linux, nor the airport utility on the Mac.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.