Arch Linuxでath10kドライバーを使って11acフルチャンネル対応したAPを立てる

Linuxを利用して、11acに対応したAtherosのWiFiモジュールを使って、全対応チャンネルを有効化したAPを建てる方法を記録しておく。 ここで記載する内容は、カーネルバージョンやコンフィグレーションに強く依存するため、他のバージョンなどでは動作が異なる可能性がある。

今回は以下の環境で検証した。

LinuxでAPを立てるには、WiFiモジュールとhostapdを適切に設定する必要がある。 AtherosのWiFiモジュールはカーネルにドライバーが組み込まれているので、基本的にそのまま使うことができる。 ただし、利用可能な電波は各国の規制状況によって異なるため、規制情報データベースが別に用意されている。 このデータベースはArch Linuxではwireless-regdbパッケージとして用意されていて、 cfg80211モジュールを使ってWiFiの設定を行う際にカーネルが読み込むようになっている。

実は規制情報はAtherosドライバー自体にも組み込まれていて、wireless-regdbとドライバーの情報の組み合わせによって、 挙動が変わるのが設定を難しくしている。

cfg80211モジュールは、モジュールオプションやiwコマンドで規制情報を設定することができる。 規制情報はregulatory domainと表現されていて、iwコマンドではiw reg set/getなどで扱うことができる。 基本的に、国によって規制が変わるので、regulatory domainは国コードを使って指定することになる。

次の操作では、iwコマンドではなく、cfg80211モジュールのオプションを使ってregulatory domainを設定する。 iwコマンドだと、cfg80211モジュールや、ドライバーモジュールの初期化が終わってから設定することになり、 理由は後述するが、うまく設定が反映されないからである。

オプションは、/etc/modprobe.d/に適当なファイルをおいて以下のように記載する。

options cfg80211 ieee80211_regdom=JP

あるいは、modprobeコマンドで指定することもできる。 実験時はmodprobeコマンドで指定するのが楽だった。

まずは、オプションを設定しないときの挙動を確認する。 (cfg80211は00をデフォルトのregulatory domainとして利用していて、これを指定しても良い)

このとき、iw reg getの結果は以下のようになった。

global
country 00: DFS-UNSET
        (755 - 928 @ 2), (N/A, 20), (N/A), PASSIVE-SCAN
        (2402 - 2472 @ 40), (N/A, 20), (N/A)
        (2457 - 2482 @ 20), (N/A, 20), (N/A), AUTO-BW, PASSIVE-SCAN
        (2474 - 2494 @ 20), (N/A, 20), (N/A), NO-OFDM, PASSIVE-SCAN
        (5170 - 5250 @ 80), (N/A, 20), (N/A), AUTO-BW, PASSIVE-SCAN
        (5250 - 5330 @ 80), (N/A, 20), (0 ms), DFS, AUTO-BW, PASSIVE-SCAN
        (5490 - 5730 @ 160), (N/A, 20), (0 ms), DFS, PASSIVE-SCAN
        (5735 - 5835 @ 80), (N/A, 20), (N/A), PASSIVE-SCAN
        (57240 - 63720 @ 2160), (N/A, 0), (N/A)

phy#0
country 99: DFS-UNSET
        (2402 - 2472 @ 40), (N/A, 20), (N/A)
        (5140 - 5360 @ 80), (N/A, 30), (N/A), PASSIVE-SCAN
        (5715 - 5860 @ 80), (N/A, 30), (N/A), PASSIVE-SCAN

また、iw phyコマンドの結果は次のようになった。 (ここでは5GHzだけ抜粋している)

                Frequencies:
                        * 5180 MHz [36] (30.0 dBm) (no IR)
                        * 5200 MHz [40] (30.0 dBm) (no IR)
                        * 5220 MHz [44] (30.0 dBm) (no IR)
                        * 5240 MHz [48] (30.0 dBm) (no IR)
                        * 5260 MHz [52] (30.0 dBm) (no IR, radar detection)
                        * 5280 MHz [56] (30.0 dBm) (no IR, radar detection)
                        * 5300 MHz [60] (30.0 dBm) (no IR, radar detection)
                        * 5320 MHz [64] (30.0 dBm) (no IR, radar detection)
                        * 5500 MHz [100] (disabled)
                        * 5520 MHz [104] (disabled)
                        * 5540 MHz [108] (disabled)
                        * 5560 MHz [112] (disabled)
                        * 5580 MHz [116] (disabled)
                        * 5600 MHz [120] (disabled)
                        * 5620 MHz [124] (disabled)
                        * 5640 MHz [128] (disabled)
                        * 5660 MHz [132] (disabled)
                        * 5680 MHz [136] (disabled)
                        * 5700 MHz [140] (disabled)
                        * 5720 MHz [144] (disabled)
                        * 5745 MHz [149] (30.0 dBm) (no IR)
                        * 5765 MHz [153] (30.0 dBm) (no IR)
                        * 5785 MHz [157] (30.0 dBm) (no IR)
                        * 5805 MHz [161] (30.0 dBm) (no IR)
                        * 5825 MHz [165] (30.0 dBm) (no IR)
                        * 5845 MHz [169] (30.0 dBm) (no IR)
                        * 5865 MHz [173] (disabled)

これを見ると、100から140チャンネルは無効化され、その他はno IRフラグやradar detectionフラグが設定されている。 disabledフラグはその名の通り、完全にそのチャンネルが利用できないことを示す。 no IRフラグは、電波の発信を許可されないことを示す。DFSを使ってパッシブスキャンすれば利用できるらしい。 (最終的にうまく行った設定では、no IRフラグ単体の帯域は存在しなかったので本当にそうなのかはわからないが。) またradar detectionはDFSを利用する必要がある帯域らしい。 no IRとradar detectionが何となくかぶっている気がするので詳しい人がいれば教えてほしい。 一応以下のドキュメントも読んだがいまいちわからなかった。 https://wireless.wiki.kernel.org/en/developers/regulatory/processing_rules#post_processing_mechanisms

デフォルトのregulatory domainではhostapdを起動してもうまく動かなかった。 上記のように利用できる電波帯がないので仕方がないだろう。

次にregulatory domainにUSを設定してみる。

iw reg getは以下のようになり、globalとphyがどちらもUSになっている。

global
country US: DFS-FCC
        (902 - 904 @ 2), (N/A, 30), (N/A)
        (904 - 920 @ 16), (N/A, 30), (N/A)
        (920 - 928 @ 8), (N/A, 30), (N/A)
        (2400 - 2472 @ 40), (N/A, 30), (N/A)
        (5150 - 5250 @ 80), (N/A, 23), (N/A), AUTO-BW
        (5250 - 5350 @ 80), (N/A, 24), (0 ms), DFS, AUTO-BW
        (5470 - 5730 @ 160), (N/A, 24), (0 ms), DFS
        (5730 - 5850 @ 80), (N/A, 30), (N/A), AUTO-BW
        (5850 - 5895 @ 40), (N/A, 27), (N/A), NO-OUTDOOR, AUTO-BW, PASSIVE-SCAN
        (57240 - 71000 @ 2160), (N/A, 40), (N/A)

phy#0
country US: DFS-FCC
        (902 - 904 @ 2), (N/A, 30), (N/A)
        (904 - 920 @ 16), (N/A, 30), (N/A)
        (920 - 928 @ 8), (N/A, 30), (N/A)
        (2400 - 2472 @ 40), (N/A, 30), (N/A)
        (5150 - 5250 @ 80), (N/A, 23), (N/A), AUTO-BW
        (5250 - 5350 @ 80), (N/A, 24), (0 ms), DFS, AUTO-BW
        (5470 - 5730 @ 160), (N/A, 24), (0 ms), DFS
        (5730 - 5850 @ 80), (N/A, 30), (N/A), AUTO-BW
        (5850 - 5895 @ 40), (N/A, 27), (N/A), NO-OUTDOOR, AUTO-BW, PASSIVE-SCAN
        (57240 - 71000 @ 2160), (N/A, 40), (N/A)

一方、iw phyは以下のようになっており、00を指定したときと変わっていない。

                Frequencies:
                        * 5180 MHz [36] (23.0 dBm) (no IR)
                        * 5200 MHz [40] (23.0 dBm) (no IR)
                        * 5220 MHz [44] (23.0 dBm) (no IR)
                        * 5240 MHz [48] (23.0 dBm) (no IR)
                        * 5260 MHz [52] (24.0 dBm) (no IR, radar detection)
                        * 5280 MHz [56] (24.0 dBm) (no IR, radar detection)
                        * 5300 MHz [60] (24.0 dBm) (no IR, radar detection)
                        * 5320 MHz [64] (24.0 dBm) (no IR, radar detection)
                        * 5500 MHz [100] (disabled)
                        * 5520 MHz [104] (disabled)
                        * 5540 MHz [108] (disabled)
                        * 5560 MHz [112] (disabled)
                        * 5580 MHz [116] (disabled)
                        * 5600 MHz [120] (disabled)
                        * 5620 MHz [124] (disabled)
                        * 5640 MHz [128] (disabled)
                        * 5660 MHz [132] (disabled)
                        * 5680 MHz [136] (disabled)
                        * 5700 MHz [140] (disabled)
                        * 5720 MHz [144] (disabled)
                        * 5745 MHz [149] (30.0 dBm) (no IR)
                        * 5765 MHz [153] (30.0 dBm) (no IR)
                        * 5785 MHz [157] (30.0 dBm) (no IR)
                        * 5805 MHz [161] (30.0 dBm) (no IR)
                        * 5825 MHz [165] (30.0 dBm) (no IR)
                        * 5845 MHz [169] (27.0 dBm) (no IR)
                        * 5865 MHz [173] (disabled)

次にregulatory domainにJPを設定してみる。 iw reg getは以下のようになる。 globalは98という謎のdomainになり、phyはなぜかUSが設定されている。

iw reg get
global
country 98: DFS-UNSET
        (2402 - 2472 @ 40), (N/A, 20), (N/A)
        (5170 - 5250 @ 80), (N/A, 20), (N/A), AUTO-BW
        (5250 - 5330 @ 80), (N/A, 20), (0 ms), DFS, AUTO-BW
        (5490 - 5710 @ 160), (N/A, 23), (0 ms), DFS
        (57240 - 66000 @ 2160), (N/A, 10), (N/A)

phy#0
country US: DFS-FCC
        (902 - 904 @ 2), (N/A, 30), (N/A)
        (904 - 920 @ 16), (N/A, 30), (N/A)
        (920 - 928 @ 8), (N/A, 30), (N/A)
        (2400 - 2472 @ 40), (N/A, 30), (N/A)
        (5150 - 5250 @ 80), (N/A, 23), (N/A), AUTO-BW
        (5250 - 5350 @ 80), (N/A, 24), (0 ms), DFS, AUTO-BW
        (5470 - 5730 @ 160), (N/A, 24), (0 ms), DFS
        (5730 - 5850 @ 80), (N/A, 30), (N/A), AUTO-BW
        (5850 - 5895 @ 40), (N/A, 27), (N/A), NO-OUTDOOR, AUTO-BW, PASSIVE-SCAN
        (57240 - 71000 @ 2160), (N/A, 40), (N/A)

一方でiw phyの出力は以下のようになり、利用可能なチャンネルが現れた。

                Frequencies:
                        * 5180 MHz [36] (23.0 dBm)
                        * 5200 MHz [40] (23.0 dBm)
                        * 5220 MHz [44] (23.0 dBm)
                        * 5240 MHz [48] (23.0 dBm)
                        * 5260 MHz [52] (24.0 dBm) (no IR, radar detection)
                        * 5280 MHz [56] (24.0 dBm) (no IR, radar detection)
                        * 5300 MHz [60] (24.0 dBm) (no IR, radar detection)
                        * 5320 MHz [64] (24.0 dBm) (no IR, radar detection)
                        * 5500 MHz [100] (24.0 dBm) (no IR, radar detection)
                        * 5520 MHz [104] (24.0 dBm) (no IR, radar detection)
                        * 5540 MHz [108] (24.0 dBm) (no IR, radar detection)
                        * 5560 MHz [112] (24.0 dBm) (no IR, radar detection)
                        * 5580 MHz [116] (24.0 dBm) (no IR, radar detection)
                        * 5600 MHz [120] (24.0 dBm) (no IR, radar detection)
                        * 5620 MHz [124] (24.0 dBm) (no IR, radar detection)
                        * 5640 MHz [128] (24.0 dBm) (no IR, radar detection)
                        * 5660 MHz [132] (24.0 dBm) (no IR, radar detection)
                        * 5680 MHz [136] (24.0 dBm) (no IR, radar detection)
                        * 5700 MHz [140] (24.0 dBm) (no IR, radar detection)
                        * 5720 MHz [144] (24.0 dBm) (radar detection)
                        * 5745 MHz [149] (30.0 dBm)
                        * 5765 MHz [153] (30.0 dBm)
                        * 5785 MHz [157] (30.0 dBm)
                        * 5805 MHz [161] (30.0 dBm)
                        * 5825 MHz [165] (30.0 dBm)
                        * 5845 MHz [169] (27.0 dBm) (no IR)
                        * 5865 MHz [173] (27.0 dBm) (no IR)

このときにhostapdを使って設定してみたが、36から48チャンネルは正しく使えてたものの、 52以降はhostapdがDFSを利用を試みていたが使用できなくてプロセスが終了してしまった。 (144以降は日本では規制されているので試していない)

ここまでで、cfg80211でregulatory domainが正しく設定できないことと、DFSが利用できないことがわかった。

色々検索したところ、ドライバーにno IRを外すパッチを適用するというハックが見つかるが、 規制回避になるためよろしくない。 ただ調べる中で、ath10kでregulatory domainを扱う、drivers/net/wireless/ath/regd.cでregulatory doaminをユーザーが変更可能とするカーネルオプションがあることがわかった。

  • CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS このオプションが設定されていない場合、ユーザーによるregulatory domainの設定を拒否する
  • CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING このオプションが設定されていない場合、USやJPへの設定を拒否する

これらのオプションを有効にしてathモジュールをビルドして、regulatory domainをJPに設定してみる。

iw reg getの結果は以下のとおりとなり、正しくJPが設定されている。

iw reg get
global
country JP: DFS-JP
        (2402 - 2482 @ 40), (N/A, 20), (N/A)
        (2474 - 2494 @ 20), (N/A, 20), (N/A), NO-OFDM
        (4910 - 4990 @ 40), (N/A, 23), (N/A)
        (5170 - 5250 @ 80), (N/A, 20), (N/A), AUTO-BW
        (5250 - 5330 @ 80), (N/A, 20), (0 ms), DFS, AUTO-BW
        (5490 - 5710 @ 160), (N/A, 23), (0 ms), DFS
        (57000 - 66000 @ 2160), (N/A, 10), (N/A)

phy#0
country JP: DFS-JP
        (2402 - 2482 @ 40), (N/A, 20), (N/A)
        (2474 - 2494 @ 20), (N/A, 20), (N/A), NO-OFDM
        (4910 - 4990 @ 40), (N/A, 23), (N/A)
        (5170 - 5250 @ 80), (N/A, 20), (N/A), AUTO-BW
        (5250 - 5330 @ 80), (N/A, 20), (0 ms), DFS, AUTO-BW
        (5490 - 5710 @ 160), (N/A, 23), (0 ms), DFS
        (57000 - 66000 @ 2160), (N/A, 10), (N/A)

一方でiw phyの結果は、100チャンネル以降が無効化されてしまった。

                Frequencies:
                        * 5180 MHz [36] (20.0 dBm)
                        * 5200 MHz [40] (20.0 dBm)
                        * 5220 MHz [44] (20.0 dBm)
                        * 5240 MHz [48] (20.0 dBm)
                        * 5260 MHz [52] (20.0 dBm) (no IR, radar detection)
                        * 5280 MHz [56] (20.0 dBm) (no IR, radar detection)
                        * 5300 MHz [60] (20.0 dBm) (no IR, radar detection)
                        * 5320 MHz [64] (20.0 dBm) (no IR, radar detection)
                        * 5500 MHz [100] (disabled)
                        * 5520 MHz [104] (disabled)
                        * 5540 MHz [108] (disabled)
                        * 5560 MHz [112] (disabled)
                        * 5580 MHz [116] (disabled)
                        * 5600 MHz [120] (disabled)
                        * 5620 MHz [124] (disabled)
                        * 5640 MHz [128] (disabled)
                        * 5660 MHz [132] (disabled)
                        * 5680 MHz [136] (disabled)
                        * 5700 MHz [140] (disabled)
                        * 5720 MHz [144] (disabled)
                        * 5745 MHz [149] (disabled)
                        * 5765 MHz [153] (disabled)
                        * 5785 MHz [157] (disabled)
                        * 5805 MHz [161] (disabled)
                        * 5825 MHz [165] (disabled)
                        * 5845 MHz [169] (disabled)
                        * 5865 MHz [173] (disabled)

ここで、cfg80211モジュールのデバッグ出力を以下のように有効化する。

echo 'file net/wireless/reg.c +p' > /sys/kernel/debug/dynamic_debug/control

カーネルのログには以下のようなメッセージが出る。

ath: EEPROM regdomain: 0x0
ath: EEPROM indicates default country code should be used
ath: doing EEPROM country->regdmn map search
ath: country maps to regdmn code: 0x3a
ath: Country alpha2 being used: US
ath: Regpair used: 0x3a
cfg80211: Disabling freq 2467.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 2472.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 2484.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5500.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5520.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5540.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5560.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5580.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5600.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5620.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5640.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5660.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5680.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5700.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5720.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5865.000 MHz as custom regd has no rule that fits it
cfg80211: Disabling freq 5720.000 MHz
cfg80211: Disabling freq 5745.000 MHz
cfg80211: Disabling freq 5765.000 MHz
cfg80211: Disabling freq 5785.000 MHz
cfg80211: Disabling freq 5805.000 MHz
cfg80211: Disabling freq 5825.000 MHz
cfg80211: Disabling freq 5845.000 MHz
cfg80211: Disabling freq 5865.000 MHz
ath: EEPROM regdomain: 0x8188
ath: EEPROM indicates we should expect a country code
ath: doing EEPROM country->regdmn map search
ath: country maps to regdmn code: 0x40
ath: Country alpha2 being used: JP
ath: Regpair used: 0x40
ath: regdomain 0x8188 dynamically updated by user

このメッセージを手がかりにソースをコードを読むと次のようなことがわかった。 はじめのathモジュールのメッセージは、ドライバー自体がEEPROMをもとにregulatory domainを設定している。 EEPROMには0が書き込まれているらしく、フォールバックとしてUSを国コードとして使うらしい。 また、regulatory domainの情報自体をドライバーが保持しているが、wireless-regdbを参照せず、 最も厳しい規制を適用するようだ。 Disabling freq 2467.000 MHz as custom regd has no rule that fits itのようなメッセージが、 ドライバーによって無効化されたチャンネルらしい。 また残りのDisabling freq 5720.000 MHzのようなメッセージは、cfg80211で指定したJPによる規制のようだ。

https://github.com/torvalds/linux/blob/v5.18/drivers/net/wireless/ath/regd.c#L635-L666 ドライバーの規制情報は、REGULATORY_STRICT_REGとして設定されており、cfg80211で設定する規制で許可されていても、ドライバーが許可していない場合は無効化されてしまう。

ここについては他の回避手段がないので、より弱い規制を使うようにパッチすることにした。 このパッチ各国規制より弱くなる可能性があるので、必ずwireless-regdbを使って規制する必要がある。

diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index f15e7bd690b5..5873fec9ac14 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -215,8 +215,7 @@ EXPORT_SYMBOL(ath_is_world_regd);

 static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
 {
-       /* this is the most restrictive */
-       return &ath_world_regdom_64;
+       return &ath_world_regdom_60_61_62;
 }

 static const struct

ここまでで、regulatory domainの設定が正しくできるようになった。 ここで、hostapdを起動してみるが、やはりDFSが失敗してしまう。

コードを読んでいると、CONFIG_ATH10K_DFS_CERTIFIEDというカーネルオプションがあるのでこれを有効にする。

以下のような設定をhostapdに行ったところ、116チャンネルを使って通信ができるようになった。 (パラメータが多くて最小化できていないので、不必要なオプションがあるかもしれない)

country_code=JP
ieee80211d=1
ieee80211h=1
hw_mode=a
channel=0
chanlist=36-140
ieee80211n=1
ht_capab=[HT40+][SHIORT-GI-20][SHORT-GI-40]
ieee80211ac=1
vht_capab=[SHORT-GI-80]
vht_oper_chwidth=1
vht_oper_centr_freq_seg0_idx=42

まとめ

ここまで試行したとおり、LinuxWiFi APを立てるのは、法規制、カーネルオプション、ドライバーなどが複雑に関連して難しい。 Arch Linuxでath10kを使う場合少なくとも以下のことを行う必要がある。

  • wireless-regdbパッケージをインストールする
  • カーネルオプションを有効化する
    • CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS
    • CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING
    • CONFIG_ATH10K_DFS_CERTIFIED
  • ドライバーにパッチを当てる
  • hostapdを適切に設定する

おまけ

デフォルト状態でregulatory domainにJPを設定した場合、利用チャンネルが増えたのはなぜか。 cfg80211モジュールは、ドライバーが指定した規制情報とユーザーが指定した規制情報が食い違ったときに、 両者を合体させたような規制情報を作成する。 このときに、98という特別な国コードを利用する。 この食い違いの判定は国コードによって行われるため、USを指定した場合は、ドライバーフォールバックであるUSと食い違わなかったため、ドライバーが指定する規制情報がそのまま利用されていた。 また、00を指定したときは、ユーザー指定がないものとして扱われるので、このときもドライバーの規制情報が利用される。

Archのデフォルトでregulatory domainをJPに設定した場合、利用チャンネルは増えるのだが、 DFSが利用できないので、11acで利用できるフルのチャンネルは利用できない。 また仮に、DFSが不要な36から48チャンネルであっても、電波出力が規制に合致しないので利用すべきではないだろう。