WiFi

Table of Contents

WiFi

前回やった、長家のWiFi問題をChatGPT(5)に聞いてみた。

A: いただいた状況を見る限り、今回の 「WiFi5 → WiFi4 → WiFi5…」のランダム切り替わり は、DHCP のリース更新が直接の原因ではなく、無線の接続レイヤで起きている事象が本筋だと思います。 少し整理しながら推測してみます。

## 1. 起きていることを分解

  • WiFi5 (11ac / VHT-MCS0 / chan 48) → 5GHz帯 スピードテストで約 150Mbps
  • WiFi4 (11n / HT-MCS0 / chan 2) → 2.4GHz帯 スピードテストで約 40Mbps
  • SSID(nwid)は同じ、BSSID も同じに見える(※後述)。
  • DHCPリースは `/etc/resolv.conf` の更新タイムスタンプで追跡すると、不定期に更新されている。
  • 接続中にランダムなタイミングでバンドが変わる。

## 2. 原因の可能性(優先度順)

### (1) バンドステアリング or ローミングの影響

最近の集合住宅用WiFi(とくにメッシュや商用AP)では、同じSSIDで 2.4GHz と 5GHz を同時に提供し、**AP側が端末を勝手にバンド切り替え**する機能があります。

  • 「バンドステアリング」 端末が5GHzを掴んでいても、APが「電波弱い」と判断すると 2.4GHz へ誘導。
  • 「ローミングアシスト」 同一BSSIDや仮想BSSIDで内部的に別チャネルを持ち、APが切り替え命令を出す。
  • 切替基準は RSSI(受信感度)、誤り率、AP負荷 など。 特に商用機は混雑時に「速い帯域(5GHz)」を混雑回避で一部端末から外す設定を持つこともあります。

**OpenBSDの`iwm(4)`はバンド固定オプションがない**ため、APからのステアリング指示に素直に従ってしまいます。

### (2) BSSIDの表示が同じに見えても実は別バンド

ifconfigの `bssid` が同じに見えるのは、APが同一MACアドレスで仮想的に両バンドを提供している場合です。 実際にはハード的に別ラジオですが、ソフト的にMACを統一しているだけなので、ユーザー側からは「同じAPに繋がっている」ように見えます。

### (3) DHCPリース更新は関係薄い

  • DHCPリース時間はサーバー側設定次第で、1時間固定とも限らず、5分や12時間など任意。
  • `/etc/resolv.conf` の更新タイムスタンプは「再リース取得時」だけでなく、「インタフェースの再接続」や「リンクダウン・アップ」でも更新される。
  • 今回のように**WiFiのバンド切替時にインタフェースが再認識され、DHCPが再実行されている**可能性大。 → つまり、リース更新の時間が不定期なのは、AP切替がトリガー。

### (4) AP側のトラフィック制御説

「大量通信で強制的にWiFi4に落とす」という設定は、一般的な家庭用や集合住宅向けの商用WiFiではほぼ聞きません(QoSで速度制限はあり得ますが、バンド切替を直接トラフィックで制御することは稀)。 ただし、**5GHz帯が混雑すると自動的に2.4GHzに追い出す**設定は普通にあります。 これは「制限」というより「負荷分散」のため。

## 3. 推測まとめ

> WiFi4→WiFi5 の切替は、DHCPリースの仕様ではなく、APのバンドステアリングや負荷分散機能で自動的にバンドを変えられている。

OpenBSD側の`iwm0`ドライバは特に固定バンド機能がないため、この挙動を避けにくいです。 Linuxなら `iw dev iwm0 set freq …` などで5GHz固定にできますが、OpenBSDは標準では不可。

## 4. 確認・対策案

  1. 電波強度のログを取る
while true; do
    ifconfig iwm0 | egrep 'media|ieee80211'
    sleep 5
done

切替直前にRSSIが落ちていないか確認。

  1. SSIDを分けてもらう 管理者に依頼し、2.4GHzと5GHzで別SSIDにしてもらえば固定可能。
  2. AP近くで試す 5GHzの感度が安定すれば切替も減る。
  3. 他端末で同現象確認 AndroidやLinuxで接続し、バンドが変わるか比較。

もし希望があれば、この挙動が**AP側の負荷分散なのか、OpenBSD側の再接続ロジックなのか**を切り分けるために、パケットキャプチャと接続イベントの時系列解析方法も書けます。 この方法を使えば、「APが指示して切り替えた」のか、「端末が勝手に切り替えた」のかがわかります。

A: では、**AP側の負荷分散か、OpenBSD側の再接続ロジックか**を切り分けるための手順を、時系列解析できる形でまとめます。 ポイントは、

  1. **無線レベルで何が起きているかを確認**(APの指示か、自主的な再接続か)
  2. 接続イベントとパケットの順序を突き合わせる ことです。

## 1. 接続イベントの取得

OpenBSD では `dmesg -w` や `/var/log/messages` に iwm(4) の状態変化が出ます。 まずは WiFi のバンド切替が起きた瞬間のログを取ります。

# 切替時のドライバメッセージをリアルタイム記録
dmesg -w > /tmp/iwm_events.log &

`ifconfig iwm0` でもメディア種別が変わるので、並行して状態を記録します。

while true; do
    date +"%F %T"
    ifconfig iwm0 | egrep 'media|ieee80211'
    sleep 2
done > /tmp/iwm_status.log &

これで**時刻とバンド切替の瞬間**がわかります。

## 2. パケットキャプチャの取得

WiFiの切替は EAPOL(認証)や Association/Disassociation フレーム が必ず飛びます。 これをキャプチャすれば、

  • APからの**Disassociation フレーム**で切断されているのか
  • 端末側から**Reassociation リクエスト**を出しているのか が判断できます。
ifconfig iwm0 down
ifconfig iwm0 mediaopt monitor
ifconfig iwm0 up

# 別端末でのモニタリング推奨(切替中の自端末だと通信止まるため)
tcpdump -i iwm0 -vvv -s 0 -w /tmp/wifi_cut.pcap
# モニタモード用のインターフェース作成

注意: OpenBSDの`iwm`はモニタモード制約があり、自端末での常用キャプチャは難しいです。 可能なら **別のWiFiアダプタ**(USB WiFiなど)や別PCを用意してモニタモードでキャプチャしてください。 Linuxなら `iw dev wlan0 set monitor` → `tcpdump -I` が可能。

## 3. 解析の着眼点

キャプチャを Wireshark で開き、以下を時系列で確認します。

### 3.1 AP主導の切替(負荷分散)

  • Disassociation / Deauthentication フレーム**が **AP → STA(あなたの端末) 向きで送られる。
  • Reason Code 例:
    • `8` → Disassociated because sending station is leaving (roaming)
    • `5` → Disassociated due to inactivity
    • `17` → Disassociated because AP is unable to handle all currently associated stations (負荷分散で追い出し)
  • その後、新しいチャネルで Association Request が端末から送信される。

### 3.2 端末主導の切替(再接続ロジック)

  • Disassociation / DeauthenticationSTA → AP の向き。
  • 原因コードは `3` (leaving BSS) や `2` (leaving for better AP)。
  • 直前に RSSI低下や認証タイムアウトがないか確認。
  • 端末から新チャネルへ Association Request を送信。

## 4. ログとパケットの突き合わせ

  1. `/tmp/iwmevents.log` と `/tmp/iwmstatus.log` で切替の**正確な時刻**を特定。
  2. Wiresharkでその時刻付近のフレームを検索。
  3. Disassociation の送信元(APか端末か)を確認。

## 5. 補足(実行しやすい代替)

もしモニタモードが難しい場合、**`tcpdump -i iwm0 -vvv -s 0`** でIP層だけでも取れます。 切替直前に DHCP Discover / Request が発生していれば、「再接続によるIP再取得」である証拠です。 ただしこれだとAP主導か端末主導かは判別しにくいです。

これをやれば、

  • 「APが負荷分散で強制切断して2.4GHzへ移した」のか
  • 「OpenBSDの`iwm`が勝手に再接続してしまった」のか をほぼ確実に切り分けられます。

希望があれば、Wiresharkでの**Reason Code早見表**や**OpenBSD iwmドライバの再接続条件コードの抜粋**も作れます。 それを使えばパケット見た瞬間に「これはAPのせい」と断定できるようにできます。

read source

A: いいですね — 手元にソースがあるなら 該当コードを「探して読み、どの条件で再接続(roam/background-scan)しているか」を突き止める のは十分可能です。 ここでは(A)どの文字列/識別子を探せば良いか、(B)見つかった箇所をどう読み解くか、(C)実際に該当箇所を貼ってもらったときに私がどう解析するか、を具体的に示します。参考にした公式情報も付けます。([man.openbsd.org][1], [marc.info][2])

## A. まず検索(`ifiwm.c` のどこを探すか)

端的に言うと "background scan / roaming / scan / assoc / deauth / disassoc" あたりのキーワードを順にgrepしていきます。端末で次を実行してください(`/path/to/src` はあなたのソースルート):

# if_iwm.c のある場所へ
cd /path/to/src/sys/dev/pci

# まずは関連キーワードを一括検索
grep -nE 'background|bgscan|scan|roam|roaming|associa|deauth|disassoc|reassoc|scan_state|ieee80211_scan' if_iwm.c

# もう少し広く、iwmドライバ関連ファイル全体も検索
grep -nE 'background|bgscan|scan|roam|roaming|associa|deauth|disassoc|reassoc|ieee80211_scan' *.c *.h

出力で良く見るべき関数/識別子(ヒットしやすいもの):

  • `background` / `bgscan` / `background scan`(バックグラウンドスキャン処理)
  • `roam` / `roaming`(ローミング処理)
  • `scan` / `iwmscan` / `iwmscanstart`(スキャントリガ/ハンドラ)
  • `assoc` / `associate` / `reassoc`(Association/Reassociation)
  • `deauth` / `disassoc`(切断を送出する箇所)
  • `ieee80211scanstate` / `ieee80211*`(net80211 側とのやり取り)
  • `iwmrx` 系(管理フレーム受信ハンドラを見ると、AP側から deauth/disassoc を受け取っているか判る)

## B. 見つけた箇所の読み方(何をチェックするか)

grepで候補が出たら、以下を順に確認します。

  1. スキャンの発火条件 `if (rssi < SOMETHRESHOLD)` や `if (sc-> sc_bgscan_interval < …)` のような条件がないか。 → RSSI(電界強度)閾値でバックスキャンを起こしているなら端末側トリガ。
  2. 誰が切断しているか(APかSTAか) コード中で ` ieee80211_send_deauth ()` / ` ieee80211_send_disassoc ()` などが呼ばれていれば**端末側が (主动的に) 切っている**。 逆に受信処理で `mgmt->type == DISASSOC` などの分岐があり、受信側処理を経て再接続するなら**APから送られた管理フレーム**で切れている可能性。
  3. net80211 インタフェースとの関係 ` ieee80211_scan_state ` や ` ieee80211_amrr ` など net80211 の API 呼び出しがあるか。 OpenBSD では net80211 側に「バックグラウンドスキャンのハンドラ」があり、ドライバがそれを実装している(7.1 で導入)という事実があります。該当ハンドラの実装を探すと「どの場面でドライバがBSSを落とす/移るか」が分かります。([openbsd.org][3], [marc.info][2])
  4. タイマー/バックオフロジック 「バックグラウンドスキャンは見つからなければ徐々に間隔が開く」等のバックオフ実装があるか。ChangeLogやパッチ紹介でもそのへんの説明が見られます。([marc.info][2], [ftp.riken.jp][4])

## C. よく現れる関数名(そのままgrep候補)

これらを直接探すと効率的です。

  • `iwmscan` `iwmscanresults` `iwmrunscan`
  • `iwmbgscan` `iwmbackground` `iwmbackgroundscan`
  • `iwmroam` `iwmdoroam`
  • `iwmassoc` `iwmassociate` `iwmreassoc`
  • `iwmrxmgmt` `iwmrx` `iwmrxintr`
  • 呼び出し先として ` ieee80211_scan_start ` / ` ieee80211_scan_end ` / ` ieee80211_send_deauth ` / ` ieee80211_send_disassoc `

例(grep サンプル):

grep -nE 'iwm_(scan|bg|roam|assoc|reassoc|rx|rx_mgmt)' if_iwm.c
grep -nE 'ieee80211_(scan_start|scan_end|send_deauth|send_disassoc|scan_state)' if_iwm.c

## D. 実際の該当箇所を私に渡す方法(助けになります)

手元で見つかった**関数の前後 40〜120 行**をコピーしてここに貼ってください。特にこれらが見つかれば優先的にください:

  • `static void iwm_background_scan (…`(存在すれば)
  • `static void iwm_scan_start (…` / `static void iwm_scan_results (…`
  • `/* roam */` や `roaming` が含まれるコメント付きブロック
  • ` ieee80211_send_disassoc ` や ` ieee80211_send_deauth ` を呼んでいる箇所
  • `if (rssi` や `if (sc-> sc_bgscan_interval ` 等、閾値判定がある箇所

貼っていただければ、該当コードが AP主導(受信で切断)か端末主導(内側でdeauth/disassoc発行/scan発火)か をかなり高い確度で判定できます。

## E. 参考(公式情報)

  • `iwm` マニュアル(バックグラウンドスキャンの存在) — OpenBSD man page. ([man.openbsd.org][1])
  • `iwm` にバックグラウンドスキャンを導入した旨のメーリングリスト投稿(実装方針:RSSI閾値でトリガ/より良いAPが見つかればdeauthしてroam)。この説明は実装意図の手掛かりになります。([marc.info][2])
  • ChangeLog にある `ifiwm.c` 関連の修正履歴(バックグラウンドスキャン頻度の調整など)。([ftp.riken.jp][4])

必要なら、あなたが貼ってくれたコード断片を読みながら

  • (1)「ここがRSSI閾値でバックグラウンドスキャンを開始している」
  • (2)「ここで端末側が deauth/disassoc を発行している」
  • (3)「ここで受信した管理フレームに反応している(AP主導)」

のどれかを明確に指摘して差し上げます。貼るのが面倒なら、grep 出力(該当行番号と短い該当行)だけでもOKです。どの方法でも解析します — 貼ってください。

[1]: https://man.openbsd.org/iwm?utm_source=chatgpt.com "iwm(4) - OpenBSD manual pages"

[2]: https://marc.info/?l=openbsd-tech&m=151198235217614&q=raw&utm_source=chatgpt.com "https://marc.info/?l=openbsd-tech&m=15119823521761…"

[3]: https://www.openbsd.org/71.html?utm_source=chatgpt.com "OpenBSD 7.1"

[4]: https://ftp.riken.jp/OpenBSD/Changelogs/ChangeLog.58?utm_source=chatgpt.com "ChangeLog.58 - ftp"

Sメーター

rssiなんて単語を提示されたので、検索してみた。

/*
 * RSSI values are reported by the FW as positive values - need to negate
 * to obtain their dBM.  Account for missing antennas by replacing 0
 * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
 */
iwm_rxmq_get_signal_strength(struct iwm_softc *sc,
    struct iwm_rx_mpdu_desc *desc)
{
        int energy_a, energy_b;

        energy_a = desc->v1.energy_a;
        energy_b = desc->v1.energy_b;
        energy_a = energy_a ? -energy_a : -256;
        energy_b = energy_b ? -energy_b : -256;
        return MAX(energy_a, energy_b);
}

信号強度を取り出してる。無線家が使うSメーターだな。

rssi = iwm_get_signal_strength(sc, phy_info);
rssi = (0 - IWM_MIN_DBM) + rssi;        /* normalize */
rssi = MIN(rssi, ic->ic_max_rssi);      /* clip to max. 100% */

dBmが得意じゃない人用に、無難なパーセンテージにしてるのか。

radio

Q: phyはどうなっていますか?

A: おそらくお持ちの `iwm(4)` が対象としているのは Intel Wireless-AC / Wireless-AX シリーズ なので、 この手のカードは「アナログ無線部」も「デジタル処理部(PHY)」も含めて ひとつのチップ(またはパッケージ) にまとめられています。

## 1. 昔のWiFiカードとの違い

昔(802.11b/g時代)は

  • **BB(Baseband)チップ**:MAC制御・暗号処理・パケット組立
  • **RF(Radio Frequency)チップ**:アナログ変調・ミキサ・増幅器・フィルタ を別パッケージに分け、間を**MIIや専用バス**で接続していました。

ですが、Intelの`iwm`系チップは

  • RF(送受信アンプ、ミキサ、PLL、ADC/DAC)
  • PHY(変調方式処理、MIMO空間ストリーム処理、OFDM/CCK等)
  • MAC(フレーム制御、暗号化、QoS) がほぼ**ワンチップ化**されています。 外部にあるのはアンテナと、場合によってはアンテナスイッチ/LNA(低雑音増幅器)くらいです。

## 2. PHY と MAC の位置づけ

無線LANチップの内部はざっくりこうなっています:

アンテナ
   │
 [LNA] ← 外付けの場合あり
   │
[RF Frontend] ──> [ADC/DAC]
   │
[PHY層 (ベースバンド信号処理)]
   │
[MAC層 (802.11フレーム処理)]
   │
[ホストインターフェース: PCIe / USB]
  • **RF Frontend**:アナログ無線部、周波数変換、送受信切替、フィルタ
  • **PHY層**:変調方式(OFDM/QAM)、MIMOストリームの復号化、誤り訂正
  • **MAC層**:再送制御、暗号化(WPA2/WPA3)、フレーム組立
  • **ホストIF**:OpenBSDの`ifiwm.c`から見える部分。PCIe経由でコマンド/データをやり取りします。

## 3. if\iwm.c から見える「PHY/無線部との境界」

`ifiwm.c`(ドライバ)は実際には

  • ホストとチップの**ファームウェア**の間でコマンドを送受信するだけ
  • RFのレジスタやチャンネル設定も「ファームウェア経由」

という構造です。 つまり、`ifiwm.c` が直接アナログRFの回路を叩くことはなく、 `iwmsendcmd()` で「チャンネルを48に変えろ」「送信パワーを下げろ」といった命令をFWに渡します。 FWがPHYやRFの制御レジスタを叩きます。

## 4. インターフェースの実体

Intelの無線チップの場合、このホスト-無線部間のインターフェースはPCIe経由でやり取りする**独自コマンドセット**で、ドキュメントは非公開です。 ドライバはリバースエンジニアリングやIntel提供のバイナリファームウェア仕様をもとに作られています。

if\iwm.c ではたとえば:

  • `iwmphyctxtcmd()` → PHY設定コマンドをFWへ送信
  • `iwmphydbsetsection()` → チャンネルキャリブレーションやRF設定
  • `iwmmvmconfig()` 系関数 → RFパラメータやアンテナ設定

などが境界にあたります。

## 5. まとめ

  • Intelの`iwm`チップは**RFアナログ部とPHY/MACを統合したワンチップ構造**。
  • if\iwm.c は **PHYやRFに直接触らず**、FWコマンドで制御。
  • 実機では外付けなのはアンテナくらいで、アナログ部もほぼ内蔵。
  • ソース内で「PHY」や「RF」が出てくる部分は、FWへの設定コマンド呼び出しが境界。

もし希望があれば、この「`ifiwm.c` がFWに送るPHY設定コマンド一覧」を抽出して、**PHY制御の全体像マップ**を作ることもできます。 そうすると「ドライバがどこまで無線部を直接制御できるのか」が一目でわかります。

A: いいですね — 手元のソースから実際に「ドライバがファームウェアへ送る PHY 関連コマンド」を抜き出して PHY制御の全体像マップ を作る手順と、その結果(予想されるコマンド一覧+簡単な説明)をまず提示します。 あなたの環境にソースがある前提で、まずは自動で抽出するコマンドを示します。出力をここに貼っていただければ、私が該当箇所(関数/構造体/どのフィールドがPHY設定を決めているか)を詳細に読み解いて「全体像マップ」を作ります。

`ifiwm.c` と周辺ヘッダ(`ifiwmreg.h` 等)から、ファームウェアに送る「コマンド構造体」「送信関数」「PHYに関係しそうな識別子」を抽出します。作業ルートは `sys/dev/pci`(OpenBSDソースツリー内)だと仮定します。

cd /path/to/src/sys/dev/pci    # あなたのソースルートに置き換えてください

# 1) コマンド構造体名(*_cmd)を一覧
grep -nE 'struct [a-zA-Z0-9_]*_cmd' if_iwm.c if_iwmreg.h | sed -E 's/:.*//g' | sort -u

# 2) ファームウェアへ送る関数呼び出しを一覧 (iwm_send_cmd 等)
grep -nE 'iwm_send_cmd|iwm_send_cmd_pdu|iwm_send_ltr_cmd|iwm_cmd' if_iwm.c | sed -E 's/:.*//g' | sort -u

# 3) PHY/DB(キャリブレーション)を示す識別子
grep -nE 'phy|PHY|phy_db|db_cfg|phy_ctxt|phy_cfg|channel|tx_power|antenna|calib' if_iwm.c if_iwmreg.h | head -n 200

# 4) IWM_*_CMD 等の定義(コマンドID)
grep -nE '#define IWM_[A-Z0-9_]+_CMD' if_iwmreg.h || true

# 5) これらを一気に抜いて見やすくする(推奨)
grep -nE 'struct [a-zA-Z0-9_]*_cmd|iwm_send_cmd|iwm_send_cmd_pdu|phy_ctxt|phy_db|IWM_[A-Z0-9_]+_CMD|antenna|tx_power|scan_cmd|beacon_filter' if_iwm.c if_iwmreg.h | sed -n '1,200p'

出力をそのままコピペしていただければ、私が次のステップで「どのコマンドが PHY のどの領域(チャネル、Txパワー、キャリブレーション、アンテナ構成、BB/RFセクション切り替え、ADDBA/BlockAck など)を制御しているか」をマッピングします。

OpenBSD の `iwm` ドライバ・系統(および元になった iwlwifi の慣例)で、PHY周りで出てくる代表的なコマンドと意味はおおむね以下の通りです。ソースを見れば正確な構造体名やフィールドが取れますが、まず全体像を掴むためにまとめます。

  • `*phycontextcmd` / `iwmphyctxtcmd` → PHY コンテキスト(モード・帯域幅・アンテナマップ・チェイン数・各ストリームのMCS上限など)を設定するコマンド。チャネル切替やモード切替時に送られる。
  • `*phydb*` / `iwmphydbsetsection` → PHY 校正データ(キャリブレーション DB)をファームウェアに書き込む。RF キャリブレーション情報やチャネル別パラメータなど。
  • `IWM*CMD`(ID 定義群) → どのコマンドIDが PHY 関連か(ex. `IWMPHYCFGCMD` のような定義がある場合、それに対応する構造体/送信関数を探す)
  • `iwmscancmd` / `scan` / `background scan` → スキャン命令。バックグラウンドスキャンがトリガになって roaming(再接続)を引き起こす場合、このコマンド周りを追う。
  • `txpowercmd` / `iwmsettxpower` など → 送信出力(Tx power)設定。APでの出力制御/国コードに基づく制限を反映。
  • `antennacfgcmd` / `iwmsetant` / `iwmtxantcfg` → アンテナの選択やT/Rスイッチ、アンテナマップの設定。
  • `beaconfiltercmd` / `iwmbeaconfiltercmd` → ビーコン受信フィルタ。省電力や複数BSSIDハンドリングに関係。
  • `timeeventcmd` / `iwmtimeevent` → 特定の時間帯にチャネルを切り替える(TDLSや測定イベント)ような機能。
  • `addbaoffload` / `txagg` 関連コマンド → ブロックACKやA-MPDU関連。これ自体はPHYではないが、再送・throughput に影響するため重要。
  • `calibcmd` / `calibration` → RF キャリブレーション、IQ キャリブレーションなど。PHY性能に直接効く。

(注:実際の識別子・構造体名は `ifiwm.c` / `ifiwmreg.h` に依存します。上は説明的なカテゴリ名です。)

あなたからの出力(grep結果)を受けて、私が作るマップは以下のような形になります。

  1. コマンド一覧(表)
    • コマンド名(構造体名) / コマンドID(IWM\_\*) / 送信関数呼び出し箇所(関数名:行番号) / 目的(チャネル制御、tx power 等)
  2. データフロー図(テキスト)
    • Host(if\iwm.c) → iwm\send\cmd → FW の各モジュール(PHY CTXT、PHY DB、RF フロント)
    • どのタイミング(起動時、チャネル変更、バックグラウンドスキャン、再接続時)に発生するか
  3. キーとなるフィールドの抜粋解説
    • 各構造体の重要フィールド(例:`channel`, `txchain`, `txpowerdbm`, `flags`)と意味
  4. どこまでドライバ制御で、どこからFW任せかの判定
    • 例えば「チャネル決定は host が指示するが、RF キャリブレーションは FW が内部で補正する」等
  5. 再接続(roaming)に関係する箇所の注釈
    • スキャン発火条件、deauth/disassoc の発行箇所(ドライバ側/受信ハンドラ側)

上の抽出コマンドを実行して、出てきた結果の**最初の 200〜400 行**をそのままここに貼ってください(`struct *cmd` 定義群、`IWM*CMD` 定義、`iwmsendcmd` 呼び出し行など)。たとえば以下のようなもの:

struct iwm_phy_cfg_cmd {
    uint32_t foo;
    uint16_t channel;
    ...
};

#define IWM_PHY_CFG_CMD 0x0a
...
iwm_send_cmd(sc, IWM_PHY_CFG_CMD, &cmd, sizeof(cmd));

これを貼っていただければ、私が(1)どのコマンドがPHYに関与しているかを確定し、(2)各コマンドの目的とドライバ・FW間の責任範囲を明確にして、(3)最終的に図式化した「PHY制御の全体像マップ」を作ってお返しします。

出力を自分で用意するのが面倒なら、以下の小スクリプトを `sys/dev/pci` ディレクトリで実行して `phycmds.txt` を作り、それをこちらに貼るだけでOKです。

#!/bin/sh
OUT=phy_cmds.txt
echo "=== struct *_cmd definitions ===" > $OUT
grep -nE 'struct [a-zA-Z0-9_]*_cmd' if_iwm.c if_iwmreg.h >> $OUT 2>/dev/null

echo "\n=== IWM_*_CMD defines ===" >> $OUT
grep -nE '#define IWM_[A-Z0-9_]+_CMD' if_iwmreg.h >> $OUT 2>/dev/null

echo "\n=== send_cmd callers ===" >> $OUT
grep -nE 'iwm_send_cmd|iwm_send_cmd_pdu|iwm_send_ltr_cmd' -n if_iwm.c >> $OUT 2>/dev/null

echo "\n=== phy/scan/antenna/tx_power hits ===" >> $OUT
grep -nE 'phy|phy_ctxt|phy_db|scan_cmd|antenna|tx_power|calib|beacon_filter' if_iwm.c if_iwmreg.h >> $OUT 2>/dev/null

echo "output written to $OUT"

— Q: 全体の流れとして、/etc/resolv.conf がどの様にして更新されるか?

A: これはちょうど「無線LANのリンク変化 → DHCP再実行 → resolv.conf更新」という一連の流れの話ですね。 OpenBSDの場合、その経路はざっくりこうなります。

## 1. 無線側での変化検出

  • **if\iwm.c**(ドライバ)が **NICやFWからの通知**を受け取る。
    • 例:
      • APからのチャネル変更命令(CSA: Channel Switch Announcement)
      • 再association成功/失敗
      • Beaconロス(一定時間応答がない)
  • ドライバは **net80211層**(`sys/net80211/` の ieee80211.c 等)へイベントを通知。
    • この時点では IP のことはまだ関与していない。

## 2. net80211 → ifconfigレベルのイベント

  • net80211は `iflinkstatechange()` を呼び出して**IFF\RUNNING のON/OFFやBSSID変更**をカーネルのifnet層へ伝える。
  • これがユーザ空間に routing socket 経由で `RTMIFINFO` または `RTMNEWADDR` として流れる。
  • 同時に `/sbin/netstart` 実行時などと同じ経路で `dhcpleased(8)` に通知が行く(正確には dhcpleased が routing socket を監視)。

## 3. dhcpleasedによる再リース要求

  • `dhcpleased` は「リンクが上がった」「BSSIDが変わった」などのイベントを受け取ると、 新規に DHCP Discover/Request を送る。
  • もしリース期間が残っていても、BSSIDやチャネルが変わると別ネットワーク扱いになるため再取得が必要になる。

## 4. resolv.conf更新

  • `dhcpleased` はリース取得時に `/etc/dhcpleased.conf` の設定に従い、 `/etc/resolv.conf` を直接書き換える(昔の dhclient と同様)。
    • 具体的には `/var/db/dhcpleased/` にリース情報を保存 → それを使って resolv.conf を上書き。
  • 更新タイムスタンプが変わるのはこの時だけ。

### 重要ポイント

  • **resolv.conf の更新は DHCP成功時のみ**で、チャネル変更やBSSID変更の瞬間に直接書き換えているわけではない。
  • つまり、チャネル変更やローミングが DHCP再実行を誘発 → その結果 resolv.conf が更新される。
  • 無線が不安定で WiFi4↔WiFi5 を行き来すると、そのたびに DHCPが走って resolv.conf が書き換わることがある。

— Q: SDR ?

A: はい、その理解はだいたい合っています。 無線LANカードの「radio」部分は、実質的に小型SDR的構成になっていて、変調・復調のかなりの部分はデジタル領域で処理され、最終的にアナログ領域で周波数変換して目的のRF帯に載せています。

## 実際の流れ(一般的なWi-Fi NICの場合)

  1. ベースバンド処理(デジタル)
    • MAC層の上から受け取ったビット列をOFDM等の方式で変調 (QAM, BPSK, etc.)
    • **DACの入力**は「低い中間周波数(IF)」または**ゼロIF**のI/Q信号。
    • IQキャリブレーションは、このI/Qペアの直交性・振幅差を補正するために実施されます。
      • SDRでも同じで、ミキサやDAC/ADCの特性ズレを補正。
  2. DAC
    • デジタルI/Q信号をアナログに変換。
    • この時点では中心周波数が数MHz〜数十MHz程度(IF信号)か、ゼロIF信号。
  3. ミキサ(アップコンバージョン)
    • PLLやシンセサイザで生成したLO(Local Oscillator)信号と混合。
    • IFやゼロIF信号を最終的なRF周波数(2.4GHz帯や5GHz帯)に変換。
    • 出力は送信パワーアンプへ。
  4. 受信側(逆方向)
    • アンテナからのRF信号をLNA(低雑音増幅器)で増幅。
    • ミキサでLOと混合してIFまたはゼロIFに変換。
    • ADCでデジタル化 → デジタルベースバンド処理(復調・誤り訂正)。

## なぜこの構成か

  • コスト 周波数ごとに直接変調/復調回路を作ると高価になるため、ベースバンドをデジタルで処理し、最後に周波数変換だけアナログでやる方が安い。
  • 柔軟性 同じベースバンド処理部を使って、2.4GHz/5GHzの両方に対応できる。
  • 補正しやすい I/QバランスやDCオフセットなど、ソフトやFWで補正可能。

## つまり

> 「RF(送受信アンプ、ミキサ、PLL、ADC/DAC)があって、中間周波数で変調し、最後にミキサでRF化」

というあなたの認識は、今の無線LAN NICの典型的な構造と一致します。 差があるとすれば、**IFをゼロにして直接LO周波数でDAC/ADCを動かすゼロIF構成**を採用している機種もあり、その場合は「中間周波数帯」というより「I/Qのベースバンド信号」を直接LOと混ぜてます。

もし希望があれば、Intel iwm系NICが使っている**ゼロIF or 低IFのどちらなのか**を、ファームウェアコマンドやPHY設定レジスタの解析から推測することもできます。 これはドライバの`ifiwm.c`と公開特許情報を組み合わせると割と絞り込めます。

きりが無いので、これぐらいにしといたる。

error

どんな無線機が使われているか、遅まきながら確認。

fu$ dmesg | grep iwm0
iwm0 at pci1 dev 0 function 0 "Intel AC 3165" rev 0x99, msi
iwm0: could not read firmware iwm-7265D-29 (error 2)
iwm0: failed to load init firmware
iwm0: hw rev 0x210, fw ver 29.4063824552.0, address 84:ef:18:3c:85:9d
fu$ ls -l /etc/firmware/iwm-*
-rw-r--r--  1 root  bin   918268 Jul  9 21:37 /etc/firmware/iwm-3160-17
-rw-r--r--  1 root  bin  1036288 Jul  9 21:37 /etc/firmware/iwm-3168-29
-rw-r--r--  1 root  bin  1049340 Jul  9 21:37 /etc/firmware/iwm-7260-17
-rw-r--r--  1 root  bin  1180412 Jul  9 21:37 /etc/firmware/iwm-7265-17
-rw-r--r--  1 root  bin  1036772 Jul  9 21:37 /etc/firmware/iwm-7265D-29
-rw-r--r--  1 root  bin  2428004 Jul  9 21:37 /etc/firmware/iwm-8000C-36
-rw-r--r--  1 root  bin  2436632 Jul  9 21:37 /etc/firmware/iwm-8265-36
-rw-r--r--  1 root  bin  1519568 Jul  9 21:37 /etc/firmware/iwm-9000-46
-rw-r--r--  1 root  bin  1490508 Jul  9 21:37 /etc/firmware/iwm-9260-46
-rw-r--r--  1 root  bin     2046 Jul  9 21:37 /etc/firmware/iwm-license

ファームウェアが読めないって、どゆ事?

dev/firmload.c

error = VOP_READ(nid.ni_vp, &uio, 0, p->p_ucred);

ここでエラー(ENOENT 2 * No such file or directory *) になってるはずだけど、何だろう? ちゃんと動作してるから深くは追求しない。

それよりFWをインストールするコマンドに興味あり。shell scriptだった。

fu$ fw_update -lF
http://firmware.openbsd.org/firmware/7.7/SHA256.sig
http://firmware.openbsd.org/firmware/7.7/intel-firmware-20250512v0.tgz
http://firmware.openbsd.org/firmware/7.7/inteldrm-firmware-20250708.tgz
http://firmware.openbsd.org/firmware/7.7/iwm-firmware-20240410.tgz
http://firmware.openbsd.org/firmware/7.7/uvideo-firmware-1.2p3.tgz
http://firmware.openbsd.org/firmware/7.7/vmm-firmware-1.16.3p0.tgz

どうやって必要な物を決定してるか? それは、FWの名前のパターンを用意しておき、 それとdmesgを付き合わせているんだ。

オイラーの所では40M弱だった。全部入りをやってみたら、250Mぐらいになった。 リナだと、常に全部入りが選択されて、優に500Mを越える巨大なものだ。無駄して んな。自マシンでは何が必要かの抽出が面倒? それともサポートが大変なので、 お大尽モードなの。

em0

qemu上のマシンには、WiFiなデバイスは装着されていない。しょうがないので無線部が 付属していないNICであるem0な奴が起動時にどんな風に認識されるかgdbしてみた。 mapplyなんてシンボルはLispを彷彿されるぞ。

dmesgからの便りは下記

em0 at pci0 dev 31 function 6 "Intel I219-V" rev 0x21: msi, address c8:5b:76:12:34:56

(gdb) bt
#0  em_probe (parent=<optimized out>, match=<optimized out>, aux=0xd1215d54)
    at /usr/src/sys/dev/pci/if_em.c:362
#1  0xd0961d91 in mapply (m=0xd1215ccc, cf=0xd0ef19e4 <cfdata+4288>)
    at /usr/src/sys/kern/subr_autoconf.c:150
#2  0xd0961bfe in config_search (fn=0xd0c7d7c0 <pcisubmatch>, parent=0xd4645280, aux=0xd1215d54)
    at /usr/src/sys/kern/subr_autoconf.c:221
#3  0xd096240c in config_found_sm (parent=0xd4645280, aux=0xd1215d54,print=0xd0c7d700 <pciprint>, submatch=0xd0c7d7c0 <pcisubmatch>)
    at /usr/src/sys/kern/subr_autoconf.c:326
#4  0xd0c7dc36 in pci_probe_device (sc=0xd4645280, tag=..., match=0x0, pap=0x0)
    at /usr/src/sys/dev/pci/pci.c:574
#5  0xd0c7c9f8 in pci_enumerate_bus (sc=0xd4645280, match=0x0, pap=0x0)
    at /usr/src/sys/dev/pci/pci.c:877
#6  0xd0c7c7d2 in pciattach (parent=0xd4642140, self=0xd4645280,aux=0xd1215eb4)
    at /usr/src/sys/dev/pci/pci.c:206
#7  0xd09627cb in config_attach (parent=0xd4642140,match=0xd0ef1164 <cfdata+2112>, aux=0xd1215eb4,print=0xd074c500 <mainbus_print>)
    at /usr/src/sys/kern/subr_autoconf.c:417
#8  0xd0962436 in config_found_sm (parent=0xd4642140, aux=0xd1215eb4,print=0xd074c500 <mainbus_print>, submatch=0x0)
    at /usr/src/sys/kern/subr_autoconf.c:327
#9  0xd074c43a in mainbus_attach (parent=<optimized out>, self=0xd4642140,aux=<optimized out>)
    at /usr/src/sys/arch/i386/i386/mainbus.c:244
#10 0xd09627cb in config_attach (parent=0x0, match=0xd0ef1024 <cfdata+1792>,aux=0x0, print=0x0)
    at /usr/src/sys/kern/subr_autoconf.c:417
#11 0xd09628b1 in config_rootfound (rootname=0xd0cb04e1 "mainbus", aux=0x0)
    at /usr/src/sys/kern/subr_autoconf.c:342
#12 0xd0652af4 in cpu_configure ()
    at /usr/src/sys/arch/i386/i386/autoconf.c:143
#13 0xd04a33ee in main (framep=0x0)
    at /usr/src/sys/kern/init_main.c:339

iwm(4), ifmedia(4) にWiFiの事が詳しく説明されてるぞ。


This year's Index

Home