Just Your Average Linux WiFi Adventure

The goal seemed simple enough.  An existing Linux router with a 2.4GHz TP-Link TL-WN822N v2.0 802.11N USB dongle in AP mode was serving a home network consisting of a mix of devices capable of 2.4GHz and 5GHz operation.  Almost two dozen reachable 2.4GHz networks were nearby, a situation that took the blame for a great deal of wireless dropouts and sluggish performance.  Since by comparison only a few 5GHz networks were penetrating the apartment, adding a USB Edimax EW-7822UAC was the goal, to add 5GHz 802.11AC capability to the router in a bridged configuration with the existing connection.  Well, things always sound simple in concept… The home network has:

  • A laptop with an Atheros AR2425 2.4GHz 802.11B/G card
  • A laptop with an Intel IWL4965 2.4GHZ/5GHz 802.11A/B/G/N card
  • A Macbook Pro that operates on 2.4GHz/5GHz 802.11A/AC/B/G/N
  • A Chromecast that operates on 2.4GHz 802.11B/G/N (and should be controllable from 5GHz clients)
  • Two Android phones which operate on 2.4GHz/5GHz 802.11A/AC/B/G/N

Without going into excruciating detail, I’ll just list the problems I encountered and the general approach to dealing with them.

2.4GHz 802.11N 40MHz isn’t really possible in practice, if you like standards

Well, the hardware should support it no problem.  But in reality, an area where other 2.4GHz devices are already transmitting, especially existing 40MHz devices, is not going to have room for additional 40MHz APs.  Case in point: I can use channel 11 since channel 1 and 4 are heavily used by other users in my area.  But since only HT40- mode can be used on channel 11 (channel 11 is the highest 2.4GHz channel available in US regdomain anyway), this means channel 7 must be free for 40MHz operation on channel 11. Well, in this case, there was a 40MHz user on channel 4, who similarly has no choice but to operate in HT40+ mode, meaning he is using both channel 4 and channel 8. hostapd scans on startup for existing base stations if 40MHz mode is enabled, and will refuse to enable 40MHz mode if existing BSS with overlapping channels are found. Well, this can be worked around, but it’s not very nice to do so since it violates 802.11N.  Anyway, we can live with a slightly-slower-than-optimal 2.4GHz network.

No in-kernel driver for Realtek RTL8812AU

This is always annoying since it means compiling and maintaining an out-of-kernel driver.  Most likely it is because the driver contains a firmware “binary blob”, which is no longer considered acceptable by kernel developers. There are many versions and hacks (to enable operation with the cfg80211 layer) of this driver floating around the web.  The latest version (4.3.8) from Realtek does not support AP mode.  I used a hacked 4.3.2 driver which works in AP mode and is reasonably current.

Wrong country code on TP-Link TL-WN822N V2.0 adapter

The firmware of this device wrongly thinks the device is for China instead of the US:

Jan 24 00:23:34 iqrouter kernel: [174561.443058] usb 1-3: new high-speed USB device number 5 using ehci-pci
Jan 24 00:23:34 iqrouter kernel: [174561.575892] usb 1-3: New USB device found, idVendor=0cf3, idProduct=7015
Jan 24 00:23:34 iqrouter kernel: [174561.575960] usb 1-3: New USB device strings: Mfr=16, Product=32, SerialNumber=48
Jan 24 00:23:34 iqrouter kernel: [174561.576101] usb 1-3: Product: USB WLAN
Jan 24 00:23:34 iqrouter kernel: [174561.576136] usb 1-3: Manufacturer: ATHEROS
Jan 24 00:23:34 iqrouter kernel: [174561.576170] usb 1-3: SerialNumber: 12345
Jan 24 00:23:34 iqrouter kernel: [174561.577573] usb 1-3: ath9k_htc: Firmware htc_7010.fw requested
Jan 24 00:23:34 iqrouter kernel: [174561.577797] usb 1-3: firmware: direct-loading firmware htc_7010.fw
Jan 24 00:23:34 iqrouter kernel: [174561.687570] usb 1-3: ath9k_htc: Transferred FW: htc_7010.fw, size: 72812
Jan 24 00:23:34 iqrouter kernel: [174561.757094] ath9k_htc 1-3:1.0: ath9k_htc: HTC initialized with 45 credits
Jan 24 00:23:34 iqrouter kernel: [174561.961719] ath9k_htc 1-3:1.0: ath9k_htc: FW Version: 1.4
Jan 24 00:23:34 iqrouter kernel: [174561.961777] ath: EEPROM regdomain: 0x809c
Jan 24 00:23:34 iqrouter kernel: [174561.961781] ath: EEPROM indicates we should expect a country code
Jan 24 00:23:34 iqrouter kernel: [174561.961785] ath: doing EEPROM country->regdmn map search
Jan 24 00:23:34 iqrouter kernel: [174561.961789] ath: country maps to regdmn code: 0x52
Jan 24 00:23:34 iqrouter kernel: [174561.961793] ath: Country alpha2 being used: CN
Jan 24 00:23:34 iqrouter kernel: [174561.961796] ath: Regpair used: 0x52
Jan 24 00:23:34 iqrouter kernel: [174561.965246] ieee80211 phy2: Atheros AR9287 Rev:2

Since the host system is US regdomain, only the World regdomain is allowed in the end.  For 2.4GHz operation, this wasn’t much of a problem since the US is actually the most restrictive domain for 2.4GHz.  This can be worked around to override with the correct settings, but there was nothing to be gained by doing so.  Until…

cfg80211 applies regdomain of the system’s _first_ wireless module

After a long time trying to figure out why all 5GHz channels of the Edimax adapter were unavailable to hostapd, throwing messages like channel [2] (40) is disabled for use in AP mode, flags: 0x77 NO-IBSS PASSIVE-SCAN, I found out why. Due to the possibility of interfering with existing spectrum usage, 5GHz devices in the world regulatory domain are not allowed to begin transmitting on their own, unless they have first seen an AP’s beacon (while “passive scanning”, that is, probing traffic on WiFi channels without transmitting) and can thus assume that it is safe to use that 5GHz channel for WiFi in that locale.  This poses a problem when one wishes to become that AP. Well, it shouldn’t be a problem for us, since the Edimax is properly in the US regdomain, as can be observed when it is the only (!) wireless device inserted in the system since boot:

# iw reg get
country US: DFS-FCC
        (2402 - 2472 @ 40), (N/A, 30)
        (5170 - 5250 @ 80), (N/A, 17)
        (5250 - 5330 @ 80), (N/A, 23), DFS
        (5735 - 5835 @ 80), (N/A, 30)
        (57240 - 63720 @ 2160), (N/A, 40)

So why are we stuck not being able to initiate transmission on 5GHz channels when both cards are present?  The answer lies buried in cfg80211 documentation:

Conflicts can arise when you have multiple wireless devices present on a system which claim different regulatory domains. Arbitrarily complex algorithms could be invented to resolve such conflicts; for now the regulatory domain selected by the first wireless card is chosen and if cards have their own regulatory domain that regulatory domain is always respected for that card.

As it happened, the TP-Link card with the wrong regdomain had its driver loaded first at boot time, which subjected subsequent adapters to the defaulted World regdomain, regardless of actual capabilities! To fix this, I added a simple hack to network startup scripts to unload all WiFi hardware drivers and all of their dependencies, thereby unloading the cfg80211 layer as well, and reload them in an order which would apply the US regdomain first:

modprobe -r ath9k_htc
modprobe -r 8812au
modprobe 8812au
modprobe ath9k_htc

After doing this, hostapd was able to be started on both cards.

Configuring bridged mode

There is a minor issue here, in that configuring the WiFi bridge as one would any other bridge on a Debian system, by using a bridge_ports entry in an /etc/network/interfaces device stanza, doesn’t work for WiFi devices.  So the way to make it work is to create a bridge, configure the bridge in /etc/network/interfaces, and then use the bridge option in each WiFi device’s hostapd.conf to add the WiFi device to the existing bridge when hostapd starts. I configured the bridge as follows:

iface wlan_br0 inet static
      hostapd /etc/hostapd/hostapd.conf
      hostapd /etc/hostapd/hostapd_5ghz.conf
      address 172.16.2.1
      netmask 255.255.255.0
      broadcast 172.16.2.255
       up       /etc/network/ifaces/wlan_br0/up
       pre-up   /etc/network/ifaces/wlan_br0/pre-up
       down     /etc/network/ifaces/wlan_br0/down
       pre-down /etc/network/ifaces/wlan_br0/pre-down

And in each hostapd.conf:

bridge=wlan_br0

There’s one remaining problem: ifup won’t configure the bridge device if it doesn’t exist when /etc/init.d/networking runs at boot. So a command to create the bridge must exist in some startup script that is run prior to this point, perhaps in an up script for another network interface:

brctl addbr wlan_br0

Remember to ensure that the DHCP server is restarted after the bridge device is brought up, perhaps in its up script, so that it begins to listen for DHCP traffic received via the wireless bridge.

hostapd configuration

I’ll just list the working hostapd configurations here as a starting point, and then discuss some more things afterwards.

hostapd.conf (2.4GHz, TP-Link TL-WN822N V2.0):

interface=wlan_priv0
bridge=wlan_br0
driver=nl80211
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0
ssid=mynetwork
country_code=US
ieee80211d=1
ieee80211h=1
hw_mode=g
channel=11
beacon_int=100
dtim_period=2
max_num_sta=255
rts_threshold=2347
fragm_threshold=2346
macaddr_acl=0
auth_algs=3
ignore_broadcast_ssid=0
wmm_enabled=1
wmm_ac_bk_cwmin=4
wmm_ac_bk_cwmax=10
wmm_ac_bk_aifs=7
wmm_ac_bk_txop_limit=0
wmm_ac_bk_acm=0
wmm_ac_be_aifs=3
wmm_ac_be_cwmin=4
wmm_ac_be_cwmax=10
wmm_ac_be_txop_limit=0
wmm_ac_be_acm=0
wmm_ac_vi_aifs=2
wmm_ac_vi_cwmin=3
wmm_ac_vi_cwmax=4
wmm_ac_vi_txop_limit=94
wmm_ac_vi_acm=0
wmm_ac_vo_aifs=2
wmm_ac_vo_cwmin=2
wmm_ac_vo_cwmax=3
wmm_ac_vo_txop_limit=47
wmm_ac_vo_acm=0
ieee80211n=1
ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][DSSS_CCK-40]
eapol_key_index_workaround=0
eap_server=0
own_ip_addr=127.0.0.1
wpa=3
wpa_passphrase=password
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP

hostapd_5ghz.conf (5GHz, Edimax EW-7822UAC):

interface=wlan_priv1
bridge=wlan_br0
driver=nl80211
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0
ssid=mynetwork5
country_code=US
ieee80211d=1
ieee80211h=1
hw_mode=a
channel=40
beacon_int=100
dtim_period=2
max_num_sta=255
rts_threshold=2347
fragm_threshold=2346
macaddr_acl=0
auth_algs=3
ignore_broadcast_ssid=0
wmm_enabled=1
wmm_ac_bk_cwmin=4
wmm_ac_bk_cwmax=10
wmm_ac_bk_aifs=7
wmm_ac_bk_txop_limit=0
wmm_ac_bk_acm=0
wmm_ac_be_aifs=3
wmm_ac_be_cwmin=4
wmm_ac_be_cwmax=10
wmm_ac_be_txop_limit=0
wmm_ac_be_acm=0
wmm_ac_vi_aifs=2
wmm_ac_vi_cwmin=3
wmm_ac_vi_cwmax=4
wmm_ac_vi_txop_limit=94
wmm_ac_vi_acm=0
wmm_ac_vo_aifs=2
wmm_ac_vo_cwmin=2
wmm_ac_vo_cwmax=3
wmm_ac_vo_txop_limit=47
wmm_ac_vo_acm=0
ieee80211n=1
ht_capab=[HT40-][SMPS-STATIC][SHORT-GI-20][SHORT-GI-40][MAX-AMSDU-7935][DSSS_CK-40]
require_ht=0
ieee80211ac=1
vht_capab=[RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][RX-STBC-12][SU-BEAMFORMER][SU-BEAMFORMEE][BF-ANTENNA-2][SOUNDING-DIMENSION-2][HTC-VHT][MAX-A_MPDU-LEN-EXP7]
require_vht=0
eapol_key_index_workaround=0
eap_server=0
own_ip_addr=127.0.0.1
wpa=3
wpa_passphrase=password
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP

How to determine ht_capab and vht_capab flags?

Generally we want to allow hostapd to use optional hardware features when they are advertised, but hostapd does not automatically import them, probably due to the possibility of bugs. So we have to create the list of flags ourselves. ht_capab is a list of 802.11N flags, while vht_capab is a list of 802.11AC flags. So how to determine which flags are supported by hardware?

# iw list
[snip]
Capabilities: 0x1862
    HT20/HT40
    Static SM Power Save
    RX HT20 SGI
    RX HT40 SGI
    No RX STBC
    Max AMSDU length: 7935 bytes
    DSSS/CCK HT40
Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
Minimum RX AMPDU time spacing: 16 usec (0x07)
HT TX/RX MCS rate indexes supported: 0-15, 32
[snip]

These capability flags can be easily mapped to the ht_capab list using the comments in the distribution’s hostapd.conf. So that takes care of 802.11N, whether on 2.4GHz or 5GHz band. But what if we want to use 802.11AC on the 5GHz band and therefore need a vht_capab list?  Well, it’s a little more tricky.  To find the list of flags above, I had to look for where they are set in the source code of the 8812au driver.  Additionally, there are module options like rtw_80211d that are by default disabled and should be enabled to obtain the corresponding functionality before it is enabled in the hostapd.conf.  Which leads to another interesting problem:

hostapd HT capability check

Before hostapd will bring up an interface, it checks if all of the capability flags configured in the hostapd.conf are advertised by the WiFi driver, and aborts if any are missing. Using the 8812au driver in AP mode, the cfg80211 capabilities are not actually filled in until the driver has entered AP mode, which leads to hostapd quitting after a line like:

hw vht capab: 0x0, conf vht capab: 0x338001b2

with errors like:

Driver does not support configured VHT capability [HTC-VHT]

The only solution I found was to hack the two checks that call ieee80211ac_supported_vht_capab() out of the hostapd code and build a custom hostapd package. Only after doing so, the RTL8812AU could be started in AP mode with 802.11AC enabled.

End result

A 2.4GHz AP called “mynetwork” and a 5GHz AP called “mynetwork5”, which are equivalent in functionality from the perspective of a wireless client device, except for having the choice of which wireless spectrum to use by connecting to the appropriate AP. Generally, if you can’t figure out what’s going wrong, hostapd -dd hostapd.conf is your friend.

Special note

Just to add insult to hackery, the ath9k_htc driver and opensource firmware currently does not allow more than 7 client stations per adapter in AP mode. This seems to be a limitation of the current firmware architecture and is unlikely to be resolved without a full rewrite.

5 Responses to “Just Your Average Linux WiFi Adventure”

  1. Anonymous Cowherd says:

    Awesome. Thanks for taking the time to write this up.

    Would you mind posting your final working hostapd.conf? Also, are there any other module options required besides rtw_80211d?

  2. Anonymous Cowherd says:

    Oops. Didn’t realize those were your working hostapd.conf’s. Scratch that request. 🙂

  3. nemesis says:

    Well, 802.11d is not required per se, it just might be necessary to find a 5GHz channel if you have a crowded spectrum and can’t use any of the non-passive-scan channels.

  4. mrX says:

    Hi, great article! Keep it up!

    I am struggling with enabling AC on my rtl8812au you mentioned that:

    “The only solution I found was to hack the two checks that call ieee80211ac_supported_vht_capab() out of the hostapd code and build a custom hostapd package. Only after doing so, the RTL8812AU could be started in AP mode with 802.11AC enabled.”

    can you provide more details, maybe even source code of patched hostapd? Sorry I am noob here.. 😉

    Best regards!
    mrX

  5. mac says:

    Hi,

    great article and seems to be exactly what I was looking for.
    Unluckily the “hacked” version of your hostapd/driver version for the Edimax-WLAN stick is not downloadable.

    Is is possible to provide your hostapd or the describion to compile it again.

    Thanks a lot
    mac

Leave a Reply