WiFi (2)
Table of Contents
Markdown -> Org
ChatGPTからの出力をクリップ(コピー)して、emacsのorg-mode原稿にペーストしている。 その原稿をorg-modeでhtml化してる。
でも、ChatGPTが想定するフォーマット(markdown ?)と、org-modeでは、解釈が違う ので、見難い出力になってしまうんだ。その都度、手修正してきたんだけど、前回 の記事作成で、ぶち切れた。自動でやるのがあたり前だろう。そこでsedですよ。
困った事に、BSD sedは、正規表現の融通が効かない。しょうがないので、GNU sed(gsed) を入れた。gsedと言えども、-r(若しくは、-E)をつけて、拡張正規表現を有効に しないと、昔のperl時代のようにはいかない。 reformat(7) なんてやると、 その辺の事情が説明される。もしやと思って調べたらOpenBSD備え付けのsedでも、-r をサポートしてたわい。
特に酷いのが、スネークケースの関数名等。org-modeでは、'_' が、下付き文字の 始まりと解釈されちゃうんだ。で、その解釈を禁止するには、関数名の両端を '=' で囲う約束になってる。その他、冒頭の'*' は、H1タグだよとかね。
#!/bin/sh # clip ChatGPT out(by mouse) and convert to org-format then clip it xsel -bo | sed -r 's/([a-zA-Z]+_[\.a-zA-Z0-9_]+)/ =\1= /g' | sed -r 's/^\* / * /' | sed -r 's/^```[a-z]+/#+begin_example/' | sed -r 's/^```/#+end_example/' | xsel -b
それらを変換する様にした(超簡易版)。ChatGPTの出力をマウスクリックで コピー。それをこのスクリプトで変換。最後は出力をクリップ。後はemacs上でペースト。
markdown -> Org フォーマット変換の完全版を作ってて指示。40秒の思考の後、 こんなコードを提示してきた。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import re import sys text = sys.stdin.read().rstrip("\n").splitlines() in_code = False code_is_src = False code_lang = None in_quote = False out = [] # 正規表現(事前コンパイル) re_fence_open = re.compile(r'^\s*```([a-zA-Z0-9_+-]*)\s*$') re_fence_close = re.compile(r'^\s*```\s*$') re_atx = re.compile(r'^\s{0,3}(#{1,6})\s*(.*?)\s*#*\s*$') # #... 見出し re_hr = re.compile(r'^\s{0,3}((?:-|\*|_)\s*){3,}$') # --- *** ___ re_blockquote = re.compile(r'^\s*>\s?(.*)$') re_ul_star = re.compile(r'^\* (.*)$') # 先頭 * は見出し衝突回避 re_ul_md = re.compile(r'^(\s*)([-+*])\s+(.*)$') # -, +, * re_ol_md = re.compile(r'^(\s*)(\d+)\.\s+(.*)$') # 1. re_img = re.compile(r'!\[([^\]]*)\]\(([^)]+)\)') re_link = re.compile(r'\[([^\]]+)\]\(([^)]+)\)') re_auto = re.compile(r'<(https?://[^>\s]+)>') re_inline_code = re.compile(r'`([^`]+)`') # snake_case: 例: foo_bar, do_something_v2, memset_s, printf_like_func, with . in suffix (e.g., version_1.2) re_snake = re.compile(r'\b([A-Za-z][A-Za-z0-9]*_[A-Za-z0-9_.]*[A-Za-z0-9])\b') # Org の保護対象 (=...=, ~...~, [[...]]) re_org_verbatim = re.compile(r'=(?:[^=\n]|==)+=') re_org_code = re.compile(r'~(?:[^~\n]|~~)+~') re_org_link = re.compile(r'\[\[[^\]]+\](?:\[[^\]]*\])?\]') def mask_spans(s, patterns): """=...=, ~...~, [[..][..]] をプレースホルダに退避""" saved = [] def repl(m): saved.append(m.group(0)) return f"\u0000{len(saved)-1}\u0000" for pat in patterns: s = pat.sub(repl, s) return s, saved def unmask_spans(s, saved): def repl(m): idx = int(m.group(1)) return saved[idx] return re.sub(r'\u0000(\d+)\u0000', repl, s) for line in text: # コードフェンス内は素通し(終了検出のみ) if in_code: if re_fence_close.match(line): out.append('#+end_src' if code_is_src else '#+end_example') in_code = False code_is_src = False code_lang = None else: out.append(line) continue # フェンス開始 ```lang m = re_fence_open.match(line) if m: lang = (m.group(1) or '').strip() in_code = True code_lang = lang if lang else None if code_lang: out.append(f'#+begin_src {code_lang}') code_is_src = True else: out.append('#+begin_example') code_is_src = False continue # ATX見出し -> Org見出し m = re_atx.match(line) if m: level = len(m.group(1)) title = m.group(2) out.append(f'{"*"*level} {title}') continue # 水平線 if re_hr.match(line): out.append('-----') continue # ブロック引用(> ...)を #+begin_quote / #+end_quote に m = re_blockquote.match(line) if m: if not in_quote: out.append('#+begin_quote') in_quote = True out.append(m.group(1)) continue else: if in_quote: out.append('#+end_quote') in_quote = False # 先頭 * を安全化(Org 見出し化を避ける) if re_ul_star.match(line): # Markdownの * を Orgの - に置換(先頭でも見出し化しない) line = re_ul_md.sub(lambda mm: f'{mm.group(1)}- {mm.group(3)}', line, count=1) # リスト(- + *)はそのままでもOrg対応だが、* だけは上で - に換装 m = re_ul_md.match(line) if m: indent, bullet, rest = m.groups() if bullet == '*': bullet = '-' # 見出し衝突を避ける line = f'{indent}{bullet} {rest}' # 番号付きリスト(1. 2.)はOrgも同じ表記 # → そのままでOK(特別処理なし) # 画像  -> [[url][alt]] alt未指定なら [[url]] def img_repl(mm): alt, url = mm.group(1).strip(), mm.group(2).strip() return f'[[{url}][{alt}]]' if alt else f'[[{url}]]' line = re_img.sub(img_repl, line) # リンク [text](url) -> [[url][text]] line = re_link.sub(lambda mm: f'[[{mm.group(2).strip()}][{mm.group(1).strip()}]]', line) # 自動リンク <http://...> -> [[http://...]] line = re_auto.sub(lambda mm: f'[[{mm.group(1)}]]', line) # インラインコード `code` -> =code= line = re_inline_code.sub(lambda mm: f'={mm.group(1)}=', line) # **bold** -> *bold* (単純系のみ対応) line = re.sub(r'\*\*([^\*\n]+)\*\*', r'*\1*', line) # *italic* -> /italic/ ただし既に * を見出し化回避した先頭は除外 # (先頭 "* " パターンは上で処理済みなのでここはインラインのみ) line = re.sub(r'(?<!\*)\*([^\*\n]+)\*(?!\*)', r'/\1/', line) # 既存の Org スパン (=...=, ~...~, [[...]] ) を保護してから snake_case を =...= で囲む masked, saved = mask_spans(line, [re_org_link, re_org_verbatim, re_org_code]) masked = re_snake.sub(r'=\1=', masked) line = unmask_spans(masked, saved) out.append(line) # 末尾で引用が開いていたら閉じる if in_quote: out.append('#+end_quote') sys.stdout.write("\n".join(out) + "\n")
pythonはChatGPTの第一プログラミング言語っぽいな。頭を悩ます正規表現なんかは、 AIに任せてしまうのが得策。そして人類は正規表現の読み書きができなくなる。 そう、昔perlがどんどんと正規表現を拡張するものだから、みんな嫌気をさしたのと 同じ伝だな。
#!/bin/sh # Clipboard (markdown) -> org-mode -> Clipboard # 依存: xsel, python3 xsel -bo | python3 "$(dirname "$0")/md2org.py" | xsel -b
そして、そのパイソンを駆動するスクリプト。完全変換は複雑なんで、ChatGPTが 常用してるタグに絞ってますってさ(免責事項の告知ですな)。
次のセクション iwm0 は前回の続きを、パイソン語で変換したもの。見出しが派手 すぎるって 苦情は、ChatGPTに言ってくれ。
iwm0
Q: 751行のphycmds.txtを採取しました。 解説したい点は多数有ると思いますが、 下記に絞ってラジオ部との I/O制御を解説してください。
- 送受すべきデータの受け渡し
- チャンネルの指定
- 信号強度の取り込み。 送受の切り替え方法が有るなら、その方法。 アマチュア無線のトランシーバーだと PTT(Push to talk)ボタンで切りかえ、 進化した方法だと、常に受信してて、 しゃべり出すと、自動的に送信モードになる、VOX方式になってますんで。
A: phy_cmds.txt
を拝見すると、=ifiwm.c= と if_iwmreg.h
から抽出された /PHY 関連コマンド/や /構造体定義/、=iwmsendcmd()= 呼び出しが一覧になっています。
ご質問の 3 点に沿って、/ラジオ部(RF)とのI/O制御がどう行われているか/を整理します。
1. 送受すべきデータの受け渡し
実際の経路
- 送信
- ホスト(OpenBSDカーネル)が上位プロトコルスタックから受け取ったパケットを、ドライバ (
if_iwm.c
) が PCIe 経由で NIC の \/\/Tx バッファ(キュー)\/\/に書き込みます。 - このときドライバはファームウェアに「このメモリ領域のデータを送信せよ」という/送信命令/(Tx コマンド)を発行します。
→ コマンド例:
IWM_TX_CMD
,iwm_tx()
関数経由。 - PHY/RF 側は、このTxキューを取りに行き、変調 → DAC → ミキサ → RFアンプへ。
- ホスト(OpenBSDカーネル)が上位プロトコルスタックから受け取ったパケットを、ドライバ (
- 受信
- RF部で復調されたI/Q信号がデジタルPHYでパケット化され、NIC内部の /Rx バッファ/に置かれる。
- ドライバはPCIe割り込みで受信完了を知り、バッファからデータを読み出して上位に渡す(=iwmnotifintr()=等)。
※ つまり、送受の実データは/PCIe DMA経由/でやり取りし、PHYとの境界はすべてファームウェア内で抽象化されており、=ifiwm.c= からは直接RF信号に触らない構造です。
2. チャンネルの指定
- ファームウェアに対して /PHYコンテキスト設定コマンド/を送信する形で行われます。
- 関連構造体: struct
iwm_phy_ctxt_cmd
- 関連関数: =iwmphyctxtcmd()=、=iwmmvmaddchanctx()=(Linux側の類似実装)
- コマンドID例:=IWMPHYCONTEXTCMD=(定義は =ifiwmreg.h=)
- 関連構造体: struct
- このコマンドの中で
- =channel=(チャネル番号)
- =band=(2.4GHz / 5GHz)
- =ctrl channel=(20/40/80MHz幅の中心)
- =txchainmask=(送信アンテナ構成)
- =rxchainmask=(受信アンテナ構成) などが指定されます。
- 実際のRF設定(PLLの周波数設定、フィルタ帯域切替、PA/LNAバイアス調整)はFWが行います。
3. 信号強度の取り込み
- 受信したパケットには RSSI(受信信号強度指標)がPHYで計算されて付加されます。
- ドライバ側では
- 関数:=iwmrxrxmpdu()= や =iwmmvmrxrxmpdu()=(名前は系統によって違う)でRSSIを取り出す。
- RSSIはヘッダに dBm 単位または相対値で格納され、これを
ic->ic_node
構造体に反映して上位の net80211 層に渡します。
- net80211層はこれを利用して
- バックグラウンドスキャンのトリガ(閾値以下でスキャン開始)
- roaming 判定 に使います。
4. 送受切り替え方法(PTTに相当するもの)
- 無線LANの場合、/半二重のTDD(Time Division Duplex)/ ですが、送受の切替はPTTのような外部信号ではなく、/MACスケジューラ/と/PHY制御ロジック/で自動的に行われます。
- 受信待機がデフォルト状態。
- 送信要求が来るとスロットを取得し(CSMA/CA)、Txパスを有効化、送信終了後に即Rxパスに戻る。
- 実装上は、送信コマンド(=IWMTXCMD=)をFWに投げると、FWが
- 受信部を一時停止(Rxチェイン停止)
- PA(送信アンプ)を有効化
- パケット送信
- PAを停止し、Rxチェインを再度有効化 というシーケンスを内部で行います。
- このため、ドライバやユーザがPTTのように「送信モードON/OFF」を制御することはありません。
まとめ図(簡易)
[カーネルTCP/IP] ↓ (mbuf) [iwmドライバ] ├── iwm_send_cmd(TX_CMD, DMAバッファ) ├── iwm_phy_ctxt_cmd(チャネル,帯域,アンテナ設定) └── RSSI取得 → net80211へ ↓ PCIe DMA [ファームウェア] ├── RF設定(PLL, フィルタ, アンプ) ├── I/Q変調・復調(ベースバンド処理) └── 送受切替は内部スケジューラで自動 [RFフロントエンド] ├── LNA/PA ├── ミキサ └── アンテナ
もしご希望があれば、この=phycmds.txt=の中から /実際にチャンネルを変えるコマンドの定義と送信部分/を抜き出し、構造体フィールドごとに「何がRFに反映されるか」をさらに詳しくマッピングできます。 そうすると、アマチュア無線機の「周波数切替」との比較がしやすくなります。
Q: これ是非お願いします。 蛇足になるかも知れませんが、ハムの世界では、スプリット運用なんてのがあります。 21M帯で送信、28M帯で受信なんて事もDXペディションでは実施されたりします。 WiFiの世界はどうなんだろう? チャンネルを束ねて、通信速度を稼ぐなんて事は あるのかな?
A: では、まずご依頼の 「チャンネル変更コマンドの定義と送信部分」 を
phy_cmds.txt
から抜き出してマッピングします。そのあとで、蛇足で触れてくださった Wi-Fiにおけるスプリット的な発想やチャネル束ね の話もします。
1. チャンネル変更に関わるコマンドと関数
phy_cmds.txt
を見ると、代表的なのは以下です(名称や行番号はお手元のOpenBSD 7.6ソース準拠)。
関連構造体
struct iwm_phy_ctxt_cmd { uint32_t id_and_color; uint32_t action; struct iwm_fw_channel_info ci; // チャンネル情報 uint32_t txchain_info; uint32_t rxchain_info; uint32_t reserved; };
- id\and\color
- PHYコンテキストの識別子+色(複数コンテキストを区別するための値)
- action
- 追加/削除/変更といった動作指示
- ci
- 実際のチャネル番号、バンド、帯域幅などが入る。
- txchain\info / rxchain\info
- 送信/受信アンテナのマスク(どのチェインを使うか)。
関連構造体(チャンネル詳細)
struct iwm_fw_channel_info { uint32_t channel; // 中心チャネル番号 uint32_t band; // 2.4GHz/5GHz uint32_t width; // 20/40/80MHz帯域幅 uint32_t ctrl_channel; // 制御チャネル };
- ここが/RFのPLLやフィルタ帯域/に直結します(FWが内部で設定)。
width
が「束ねるかどうか」(後述のチャネルボンディング)を決める。
コマンドID定義(`ifiwmreg.h`)
#define IWM_PHY_CONTEXT_CMD 0x8a
送信部(`ifiwm.c` 抜粋イメージ)
int iwm_phy_ctxt_cmd(struct iwm_softc *sc, struct iwm_phy_ctxt *ctxt, uint32_t action) { struct iwm_phy_ctxt_cmd cmd; memset(&cmd, 0, sizeof(cmd)); cmd.id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ctxt->id, ctxt->color)); cmd.action = htole32(action); cmd.ci.channel = ctxt->channel; cmd.ci.band = ctxt->band; cmd.ci.width = ctxt->width; cmd.ci.ctrl_channel = ctxt->ctrl_channel; cmd.txchain_info = ctxt->tx_chain; cmd.rxchain_info = ctxt->rx_chain; return iwm_send_cmd_pdu(sc, IWM_PHY_CONTEXT_CMD, 0, sizeof(cmd), &cmd); }
iwm_send_cmd_pdu()
がFWにPCIe経由でコマンド送信- RFの周波数変更は、このコマンドを受けたFWがPLL・ミキサ設定を変えることで実現
2. RFにどう反映されるか(マッピング)
構造体フィールド | RF側での意味 |
----------------- | ----------------------------------- |
ci.channel |
PLLで生成するLO周波数の元(チャネル→中心周波数へ換算) |
ci.band |
2.4GHz/5GHzでPA/LNAのバンド切替、フィルタ切替 |
ci.width |
RFフィルタ・ADC/DACサンプリング設定(20/40/80MHz) |
ci.ctrl_channel |
ボンディング時の基準チャネル |
txchain_info |
送信アンテナ(チェイン)選択、PA駆動数 |
rxchain_info |
受信アンテナ(チェイン)選択、LNA数 |
3. アマチュア無線機との比較
- /ハム機/の「周波数切替」= VFOノブでVCO/PLLのLO周波数を変更
→ /Wi-Fi/では
ci.channel
とci.band
がそれに相当します。 - \/\/送信/受信の切替(PTT)\/\/は、Wi-Fiでは完全にFW任せで、CSMA/CAスケジューラが自動制御。 ユーザが「送信モード固定」という指定はできません。
4. スプリット運用やチャネル束ねの話(Wi-Fi的視点)
- スプリット運用(送信と受信が異バンド)
- Wi-Fiの標準的な動作では不可。基本は同一チャネル内での半二重通信。
- ただし、一部の規格(802.11axのUplink OFDMAなど)では「APが複数サブチャネルを分けて同時に複数端末の送信を受ける」ことはありますが、これは周波数スプリットではなく/周波数分割多元接続/の一形態。
- チャネル束ね(Channel Bonding)
- 802.11n以降でサポート。
- 例:
- 20MHzチャネル×2=40MHz
- 80MHz(802.11ac)
- 160MHz(802.11ac/ax)
ci.width
が20/40/80/160を指定すると、FWが複数チャネルを束ねてRFとベースバンドの帯域を拡張します。- 束ねたチャネルのうち、\/\/「制御チャネル」\/\/は管理・制御フレーム送受信用に使われます(=ci.ctrlchannel=)。
まとめ図(チャンネル変更)
[if_iwm.c] iwk_phy_ctxt_cmd() ↓ (PCIe DMA) [FW] 受信 → PLL設定, Band切替, Filter設定 ↓ [RF Frontend] ├── LNA/PA ├── ミキサ (LO周波数 ← PLL) ├── Band-pass Filter (帯域幅設定) └── アンテナ
この説明をベースにすれば、「ハムの周波数切替」と「Wi-Fiのチャネル設定」の対応関係がかなり直感的に分かるはずです。
もしご希望なら、この iwm_phy_ctxt_cmd
を呼び出している 全パス(例:スキャン時、ローミング時、起動時) を追跡して、/どのイベントでRF周波数が切り替わっているかの時系列図/ も作れます。
そうすると「勝手にWiFi4↔WiFi5が切り替わる原因」をコードレベルで突き止められます。
ビーコン
Q: アマチュア無線になぞらえて質問します。 APが発するコールサイン(ssid)とかを聞いて、端末は交信の成立を これと思うAPに依頼しますよね。 成立するまでに、どんな作法(プロトコル)が交わされるのでしょうか? sshdとssh間の通信確立と同様な事が行なわれてると想像します。
なお、下記は端末側のAPサーチスクリプトです。
fu$ cat wifi.sh #!/bin/sh ifconfig iwm0 scan | sed '/ieee80211:/d' | awk '/%/{print($7, sprintf("%3d",$4), substr($2,1,11))}' | sort -nr
プライバシィー保護の為、SSID部は2文字に制限
fu$ ./wifi.sh | cut -b-10 | head -5 85% 2 SS 80% 11 Bu 74% 36 at 73% 1 Bu 68% 100 SS
A: アマチュア無線的に言えば、 AP(基地局)が常時ビーコン信号で「こちら○○局、周波数××MHzで待機中!」とコールサイン(SSIDや各種パラメータ)を流し、 端末(クライアント)がそれを聴き取って「交信申込み(接続要求)」をする…という流れです。
Wi-Fi(802.11)の場合、\*\*接続確立までに行われる作法(プロトコルの段取り)\*\*は、おおざっぱに以下の順序になります。
—
## 1. スキャン(Scanning)
- 受動スキャン(Passive Scan)
- 端末はチャネルを順に聴いて、APが定期的に送っている ビーコンフレーム を受信。
- ビーコンにはSSID、サポート規格(802.11n/ac等)、チャンネル、暗号化方式などが入っている。
- 端末はチャネルを順に聴いて、APが定期的に送っている ビーコンフレーム を受信。
- 能動スキャン(Active Scan)
- 端末が Probe Request を送信し、APから Probe Response を受け取る。
- これによりビーコン待ちより早く情報が得られる。
—
## 2. 認証(Authentication)
- オープン認証(Open System Authentication) が主流(実質「ハロー」のやりとり)。
- STA(端末)→ AP : Authentication Request
- AP → STA : Authentication Response(成功コードなど)
- WEP時代はここで共有キー認証もあったが、現在はほぼ使われない。
—
## 3. アソシエーション(Association)
- 認証成功後、端末が「正式にこのAPのメンバーになります」と申請。
- STA → AP : Association Request(SSID、サポートMCS、機能要求など)
- AP → STA : Association Response(承諾とパラメータ)
- ここで**BSSID**(APのMACアドレス)が確定し、リンクが確立する。
—
## 4. 暗号化ハンドシェイク(WPA2/WPA3の場合)
- 4-way handshake により暗号キーを交換する。
- 事前共有キー(PSK)または802.1X経由で得たPMK(Pairwise Master Key)から一時鍵を生成。
- EAPOLフレームを4往復して暗号セッションを開始。
—
## 5. IP層への移行(DHCPなど)
- 物理リンク確立後、IPアドレスをDHCPで取得。
- この時点で `/etc/resolv.conf` の更新も行われる(前に説明した経路)。
—
### sshdとsshになぞらえると…
- ビーコン/プローブ → sshポートを叩いてバナー確認するようなもの(相手が誰かを知る)
- 認証フレーム → 「ユーザ名OKですか?」と聞く
- アソシエーション → 「ではこのセッションで正式に話しましょう」と確定
- 4-way handshake → セッションキーを作って暗号化チャネル確立(sshの鍵交換)
- DHCP → 実データ送受信に必要なIP設定(sshでいうところのシェル準備)
—
もしご希望があれば、この 「スキャン→認証→アソシエーション→暗号化→IP取得」 の各段階で実際にやり取りされる 802.11フレーム種別と主要フィールド を表にまとめられます。 そうすると、あなたの `wifi.sh` が拾っている情報(ビーコンの一部)が、接続全体のどのステップに属するかも完全に対応づけできます。
では、Wi-Fi接続確立までの段階を、**802.11フレーム種別**と**主要フィールド**で整理した表を作ります。 あなたの `wifi.sh` が見ているのは最初の「ビーコン」や「プローブ応答」の一部にあたります。
—
## Wi-Fi接続確立プロセスとフレーム対応表
段階 | フレーム種別 | 方向 | 主なフィールド / 意味 | 備考 |
------------------------ | ----------------------------------------- | -------------------- | ----------------------------------------------------------------- | ----------------------------------------------- |
1. スキャン | **Beacon**(ビーコン) | AP → 全端末 | SSID(コールサイン)、BSSID(MACアドレス)、チャネル番号、サポートレート、暗号方式、信号強度(RSSIは受信側で測定) | 定期送信(通常100ms間隔)。`wifi.sh` はこれをifconfig経由で取得している |
Probe Request | STA → 全AP(または指定SSID) | SSID(空欄なら全AP応答要求)、サポートレートなど | 能動スキャン時のみ | |
Probe Response | AP → STA | Beaconとほぼ同じ情報 | 能動スキャン応答 | |
2. 認証 | Authentication Request | STA → AP | 認証方式(Open/WEPなど)、トランザクション番号 | 現在はほぼOpen方式(実質ハンドシェイクの前置き) |
Authentication Response | AP → STA | ステータスコード(成功/失敗)、同上 | ||
3. アソシエーション | Association Request | STA → AP | SSID、サポートMCS、チャネル帯域幅、QoS要件など | ここで「あなたの局に参加します」と宣言 |
Association Response | AP → STA | ステータスコード、AID(Association ID)、設定確認 | 成功すればリンク確立 | |
**4. 暗号化鍵交換**(WPA2/WPA3) | EAPOL Key (4-way handshake) | STA ↔ AP | Nonce(乱数)、PTK生成用パラメータ、MIC(メッセージ認証コード) | この4往復で暗号セッション鍵を確立 |
5. IP層設定 | DHCP Discover / Offer / Request / Ack | STA ↔ DHCPサーバ(AP経由) | IPアドレス、デフォルトゲートウェイ、DNSサーバ | `/etc/resolv.conf` 更新はここで |
—
### `wifi.sh` が見ている部分
- `ifconfig iwm0 scan` が返すのは主に Beacon または Probe Response の内容。
- `awk`で取り出しているのは:
- `$7` → 信号強度(RSSI%)
- `$4` → チャネル番号
- `$2` → SSID(先頭部分)
—
### 補足(アマチュア無線的視点)
- Beacon = 常時CQ呼び出し(周波数・局情報を送信)
- Probe Request/Response = 「この局聞こえますか?」→「はい聞こえます」
- Authentication = 呼び出し符号交換
- Association = QSO(交信)開始宣言
- 4-way handshake = 暗号化付きQSOの鍵交換
- DHCP = QSO前の周波数・ルート調整
—
もし興味があれば、このフローを**tcpdumpで実際にキャプチャして可視化**する手順を提示できます。 そうすると、APサーチから接続までの全フレームを時系列で追えて、「どのSSIDにどうやって参加しているか」が丸見えになります。
grok.com
ChatGPTのセカンド・オピニオンとしてgeminiなんかが有るけど、最近赤丸急 上昇中のものもあり。あのイーロン・マスクってのが急所になるかも知れないけど どんな奴か試してみよう。
Grok 4が無料ユーザーにも開放&数百ページの長大PDFファイルを読み込み可能に
Q: OpenBSD 7.7 で、/etc/resolv.conf が、任意の間隔で更新されます。 その実行経路を説明してください。
環境と推測は下記の通りです。
- NICにiwm0を使用して、WiFi接続で利用
- アクセスポイント若しくはNICが無線レーヤーで、接続チャネル等を 変更してると推測
- DHCPのリース間隔は4時間と設定されてるが、それよりずっと短いランダムな 時間間隔で更新されてる
A: OpenBSD 7.7において、`/etc/resolv.conf`が任意の間隔で更新される現象について、その実行経路を以下に説明します。ご提供の環境情報(WiFi接続、NICとして`iwm0`、DHCPリース間隔4時間、しかしより短いランダムな間隔で更新)を基に、考えられるメカニズムを詳しく解説します。
### 1. `/etc/resolv.conf`の更新メカニズム OpenBSDでは、`/etc/resolv.conf`は通常、ネットワーク設定やDNS情報を動的に管理するために、DHCPクライアントや関連デーモンによって更新されます。以下のコンポーネントが関与している可能性があります:
#### a. DHCPクライアント(`dhclient`) OpenBSDでは、DHCPによるネットワーク設定の取得に`dhclient`が使用されます。`iwm0`インターフェースがWiFi接続で使用されている場合、`/etc/dhclient.conf`やインターフェース設定ファイル(`/etc/hostname.iwm0`)に基づいて動作します。
- **通常の動作**:
- `dhclient`はDHCPサーバーからIPアドレス、DNSサーバー情報(`/etc/resolv.conf`に記載される`nameserver`エントリ)、およびリース期間を取得します。
- リース期間(例:4時間)の約50%〜90%が経過すると、リース更新(`DHCPREQUEST`)が行われ、必要に応じて`/etc/resolv.conf`が更新されます。
- しかし、リース期間(4時間)より短い間隔で更新される場合、単なるリース更新以外の要因が考えられます。
- **`/etc/resolv.conf`の書き換え**:
- `dhclient`は、DHCPサーバーから受け取ったDNS情報を`/etc/resolv.conf`に反映します。
- OpenBSDでは、`resolvconf`コマンド(または類似の機能)が内部的に使用され、`/etc/resolv.conf`を更新します。
- デフォルトでは、`dhclient`が新しいDNS情報を取得するたびに、`/etc/resolv.conf`が上書きされます。
#### b. WiFi接続特有の挙動(`iwm0`) WiFi接続では、アクセスポイント(AP)との接続状態が変化すると、ネットワーク設定が再取得される場合があります。以下のシナリオが考えられます:
- **アクセスポイントの変更や再接続**:
- `iwm0`が接続するAPが切り替わった場合(例:ローミング、信号強度の変化、チャネル変更)、`dhclient`が新しいDHCPリースを取得し直すことがあります。
- 例えば、APがチャネルを切り替える(DFSによるチャネル変更や、混雑回避のための動的調整)場合、WiFi接続が一時的に切断・再接続され、これが`dhclient`の再実行を誘発します。
- この際、新しいDNS情報が提供されれば、`/etc/resolv.conf`が更新されます。
- **WiFiドライバの挙動**:
- `iwm0`(Intel WiFiチップ用ドライバ)は、接続状態の変化を検知すると、`ifconfig`や関連プロセスを通じてネットワークの再設定をトリガーします。
- OpenBSDの`netstart`スクリプトや`ifconfig`設定(`/etc/hostname.iwm0`)により、接続が再確立されるたびに`dhclient`が実行され、`/etc/resolv.conf`が更新される可能性があります。
#### c. `resolvd`の関与 OpenBSD 7.7では、`resolvd`デーモンがDNS解決の管理に関与します。`resolvd`は、複数のネットワークインターフェースやソースからDNS情報を収集し、`/etc/resolv.conf`を動的に管理します。
- **動作概要**:
- `resolvd`は、ネットワーク状態の変化(例:`iwm0`の接続状態変更)を監視し、必要に応じて`/etc/resolv.conf`を更新します。
- DHCPサーバーから新しいDNS情報が提供された場合、`resolvd`がそれを反映します。
- また、`resolvd`は、ネットワークインターフェースが不安定な場合や、複数のDNSサーバーが提供された場合に、優先順位や有効性を判断して`/etc/resolv.conf`を頻繁に更新する可能性があります。
- **ランダムな更新間隔**:
- ご指摘の「ランダムな時間間隔での更新」は、`resolvd`がネットワークの状態変化を検知し、`/etc/resolv.conf`を再生成するタイミングによる可能性があります。
- WiFi接続の不安定さや、AP側の設定変更(チャネル変更、信号強度変動)が頻繁に発生すると、`resolvd`が短い間隔で動作をトリガーします。
### 2. 推測される原因 ご提供の情報(DHCPリース4時間、しかしより短いランダムな間隔で更新)に基づき、以下の要因が考えられます:
- **WiFi接続の不安定性**:
- `iwm0`が接続するAPが頻繁にチャネル変更や再接続を行っている場合、`dhclient`が新しいリースを取得し、`/etc/resolv.conf`が更新されます。
- 例えば、APがDFS(Dynamic Frequency Selection)によりレーダー検知でチャネルを変更したり、混雑を避けるためにチャネルを切り替えると、接続が一時的にリセットされ、DHCPプロセスが再実行されます。
- **`resolvd`の積極的な更新**:
- `resolvd`がネットワーク状態の変化を検知し、`/etc/resolv.conf`を頻繁に再生成している可能性があります。
- OpenBSD 7.7の`resolvd`は、複数のDNS情報ソース(例:DHCP、静的設定、VPNなど)を統合する際、短い間隔で状態をチェックし、必要に応じて更新を行うことがあります。
- **DHCPサーバーの設定**:
- DHCPサーバーが短い間隔で新しいDNS情報を提供している場合、`dhclient`がその情報を反映して`/etc/resolv.conf`を更新します。
- 例えば、DHCPサーバーが動的にDNSサーバーリストを変更している場合、リース更新時以外でも`/etc/resolv.conf`が変更される可能性があります。
- **不適切な設定ファイル**:
- `/etc/hostname.iwm0`や`/etc/dhclient.conf`に、特定の動作を誘発する設定(例:短い間隔での再接続試行)が含まれている可能性。
- 例えば、`dhclient`が短い間隔で`DHCPDISCOVER`や`DHCPREQUEST`を送信する設定になっている場合、`/etc/resolv.conf`が頻繁に更新される。
### 3. 実行経路の詳細 以下は、`/etc/resolv.conf`が更新される典型的な実行経路です:
- **WiFi接続の変化**:
- `iwm0`がAPとの接続状態を変更(例:チャネル変更、再接続)。
- `ifconfig`やカーネルのネットワークスタックがこの変化を検知し、インターフェースを再設定。
- **`dhclient`の起動**:
- `/etc/hostname.iwm0`に記述された設定(例:`inet autoconf`)に基づき、`dhclient iwm0`が実行される。
- `dhclient`がDHCPサーバーに接続し、IPアドレスやDNS情報を取得。
- **`resolvd`による管理**:
- `dhclient`が取得したDNS情報を`resolvd`に渡す。
- `resolvd`が`/etc/resolv.conf`を更新し、適切な`nameserver`エントリを反映。
- **ランダムな更新のトリガー**:
- WiFi接続の不安定さやAPの挙動により、`iwm0`の接続状態が頻繁に変化。
- 変化ごとに`dhclient`や`resolvd`が動作し、`/etc/resolv.conf`が更新される。
### 4. 確認と対処方法 この現象の原因を特定し、制御するため、以下の手順をお勧めします:
#### a. ログの確認
- `/var/log/daemon`や`/var/log/messages`を確認し、`dhclient`や`resolvd`の動作ログを調査。
```sh
sudo less /var/log/messages
```
- キーワード:`dhclient`、`resolvd`、`iwm0`、`resolv.conf`。
- WiFi接続の状態変化を確認:
```sh
dmesg | grep iwm0
```
- APの切断/再接続やチャネル変更の記録を確認。
#### b. ネットワーク設定の確認
- `/etc/hostname.iwm0`を確認:
```sh
cat /etc/hostname.iwm0
```
- 例:`inet autoconf`や`join <SSID> wpakey <password>`が設定されているか。
- 不必要な再接続を防ぐため、`join`リストやチャネル設定を固定する(例:`chan 1`)。
- `/etc/dhclient.conf`を確認:
```sh
cat /etc/dhclient.conf
```
- 例:`supersede domain-name-servers`が設定されている場合、DNS情報の上書きを制御可能。
#### c. `resolvd`の設定
- `resolvd`の動作を制御するため、必要に応じて無効化または設定変更:
```sh
sudo rcctl disable resolvd
sudo rcctl stop resolvd
```
- ただし、`resolvd`を無効化すると、動的なDNS管理ができなくなるため、静的設定(`/etc/resolv.conf`を手動編集)が必要。
#### d. WiFi接続の安定化
- APの設定を確認し、チャネル変更やDFSを無効化(可能であれば)。
- `iwm0`の接続状態を監視:
```sh
ifconfig iwm0
```
- 接続が頻繁に切断される場合、信号強度や干渉を調査。
#### e. `/etc/resolv.conf`の保護
- 頻繁な更新を防ぐため、`resolv.conf`を不変にする:
```sh
sudo chflags schg /etc/resolv.conf
```
- ただし、動的なDNS情報が必要な場合、この方法は不適切。
### 5. 結論 `/etc/resolv.conf`がランダムな間隔で更新される原因は、以下のいずれか(または複合要因)である可能性が高いです:
- WiFi接続(`iwm0`)の不安定さによる再接続と`dhclient`の再実行。
- `resolvd`がネットワーク状態の変化を検知し、積極的に`/etc/resolv.conf`を更新。
- DHCPサーバーやAPの設定による頻繁なDNS情報更新。
まず、ログ確認(`/var/log/messages`、`dmesg`)と設定ファイル(`/etc/hostname.iwm0`、`/etc/dhclient.conf`)の調査を行い、WiFi接続の安定性向上や`resolvd`の動作制御を試みてください。必要に応じて、具体的なログや設定内容を共有いただければ、さらに詳細な分析が可能です。
kismet
802.11 layer2 wireless network detector, sniffer, and IDS
なんてのを教えて頂いた。こういう特殊品をちょっと試してみたい時は、 河豚板が最適。使いこなせないと思ったら、そのまま放置するだけ。
正確には、 pkg_add
hoge した後、不要になったら、 pkg_delete
hoge;
pkg_delete
-a して、関連品も削除。
これは、pkgの管理情報と実体の管理エリアが、usbfadmの管轄エリア内か外かの
違いによるものだ。
そして、 haltしちゃえば、何の痕跡も残さずに、無かった事にできますから。