こんにちは!数々の現場で「なぜか繋がらないWi-Fi」という魔物に深夜まで頭を抱えさせられてきた、シニアネットワークエンジニアの私です。
Web APIの設計やクラウドインフラの構築で華麗なアーキテクチャを描くモダンなエンジニアの皆さん、ふとオフィスの無線LANや自宅のスマートホーム機器が原因不明の切断を起こしたとき、どうアプローチしていますか? 「とりあえずルーターを再起動する」「SSIDを繋ぎ直す」――それも大事な初動ですが、インフラのプロとして一歩踏み込み、空中を飛び交う電波のパケットを覗いてみたくはありませんか?
今回は、Wi-Fiの接続確立における心臓部、「ビーコンフレーム」と「プローブ要求・応答(Probe Request / Response)」のパケットフローを深掘りします。なぜ端末がSSIDを見つけられないのか、なぜハンドシェイクの手前で弾かれるのか。その原因をエアチェック(パケットキャプチャ)から論理的に暴くための実務的ノウハウを、現場の泥臭い知見と共にお届けします。
—
1. 無線の第一歩:ビーコンフレーム(Beacon Frame)の正体
Wi-Fiの電波が届く空間にいるとき、スマホやノートPCはまだネットワークに接続していなくても、ルーター(アクセスポイント:AP)が発する電波を勝手にキャッチしていますよね。あの魔法のような現象の正体が、APから周期的に(通常は100msごとにおよそ10Hzで)空中へブロードキャストされているビーコンフレームです。
ビーコンフレームが持つ重要パラメーター
IEEE 802.11の規格において、ビーコンは管理フレーム(Management Frame)の一種であり、以下の致命的な情報(Information Elements: IEs)を詰め込んでばら撒かれています。
- BSSID (Basic Service Set Identifier): APのハードウェアMACアドレス。クライアントはこのアドレスを指して通信のターゲットにします。
- SSID (Service Set Identifier): 人間が認識するネットワーク名。「ゲスト用」や「社内用」といった文字列です。
- Supported Rates(サポートレート): このAPが通信に使える物理層の変調・符号化方式(MCS)の速度リスト(例:
1MbpsからWi-Fi 6の高速ストリームまで)。 - TIM (Traffic Indication Map): 省電力モード(Power Saving Mode)に入っている子機宛てのフレームがAPのバッファに溜まっていることを知らせるフラグ。
SSIDの隠蔽(ステルスAP)の罠
よくセキュリティ対策のつもりで「SSIDのブロードキャストを停止(非公開)」にする運用を見かけますが、ネットワークエンジニアの視点から言えば、これは「目隠しをしたつもりで象が部屋にいる状態」です。
SSIDを隠しても、ビーコンフレーム内のSSIDフィールド長が 0(Null SSID)になるだけで、BSSID や Supported Rates は変わらず垂れ流されています。むしろ、後述する「プローブ要求」を強制するため、かえってクライアント側のバッテリー消費が増えたり、接続トラブルの温床になったりするアンチパターンです。
—
2. 接続のダンス:プローブ要求(Probe Request)と応答(Probe Response)
クライアント(スマホやIoTデバイス)が特定のWi-Fiに接続しようとするとき、あるいは周囲のAPを探すときに行われるのが、プローブのやり取り(Active Scanning)です。
ここには、インフラエンジニアがトラブルシュートで必ず知っておくべき美しい(そしてシビアな)シーケンスが存在します。
[クライアント (STA)] [アクセスポイント (AP)]
| |
|---- 1. Probe Request (Active) ------->| ※「〇〇ってSSIDある?」
|<--- 2. Probe Response ----------------| ※「あるよ!詳細はこちら」
| |
|---- 3. Authentication (認証) ------->|
|<--- 4. Auth Response -----------------|
| |
|---- 5. Association Request ---------->|
|<--- 6. Association Response ----------|
| |
v (4-Way Handshakeへ移行) v
パケットフローの解剖
1. Probe Request(クライアント $\rightarrow$ AP / ブロードキャスト)
クライアントが自発的に「このSSID(またはブロードキャストで周囲すべて)を探しています」と叫ぶフレームです。ステルスAP環境の場合、クライアントは知っているSSID名を明示的に指定したProbe Requestを飛ばさなければ、APの存在に気づけません。
2. Probe Response(AP $\rightarrow$ クライアント)
Requestを受け取ったAPが、「お、うちのエリアに呼んだな」と返信するフレームです。ここには、ビーコンと同様に BSSID、Supported Rates、セキュリティ情報(WPA2/WPA3のRSN要素など)がぎっしり詰まっています。
—
3. 実務でのトラブルシューティング:なぜ接続が拒絶されるのか?
現場で「IoTデバイスがWi-Fiにどうしても繋がらない」という障害に直面したとき、私はよくWi-Fiアダプターをモニターモード(Promiscuous/Monitor mode)にして、tcpdump や Wireshark で空中線を流れるパケットをキャプチャします。
ここでよくある「繋がらない病」の典型的な原因を2つ紹介しましょう。
トラブル事例A:サポートレートのミスマッチ(古すぎるIoT機器)
- 現象: 2.4GHz帯の最新Wi-Fi 6(802.11ax)対応ルーターを導入した途端、海外製の古いスマート家電(ESP8266チップ搭載など)が一切接続できなくなった。
- 原因解析: ビーコンおよびProbe Responseの
Supported Ratesをパケットアナライザで覗くと、最新AP側が古いレガシーレート(1Mbps,2Mbps,5.5Mbps等)を切り捨て、高速なMCSインデックスのみを許可していたケースです。古いデバイスはProbe Requestを送るものの、AP側が対応レートの不一致(Not Supported)を理由に無視(あるいはアソシエーション拒否)していました。 - 対策: AP側の無線設定で「レガシーレートのサポート(Basic Rates)」を有効にするか、IoT専用のSSIDを別バンド(あるいはレガシー互換モード)で切り出します。
トラブル事例B:WPA3トランスディションモードのハンドシェイク失敗
- 現象: スマホは繋がるのに、特定のPCだけが接続プロセスの途中で無限ループに陥る。
- 原因解析: Probe Responseに含まれる
RSN (Robust Security Network)要素を解析すると、WPA2とWPA3の混在(Transition Mode)になっていました。古いドライバを持つNICが、APからの暗号化スイートの提示に対して誤った暗号化パラメータでAssociation Requestを投げ、AP側からStatus Code: 13 (Invalid information element)を食らって切断されていました。
—
4. PythonとScapyで学ぶWi-Fiフレーム解析の自動化
実務の現場では、GUIのWiresharkを開くだけではなく、スクリプトで周辺のビーコンやプローブの挙動を監視したい場面もあります。Pythonの強力なパケット操作ライブラリ Scapy を使えば、空気中を飛ぶマネジメントフレームを簡単にキャッチできます。
以下のコードは、モニターモードに入ったインターフェースで周囲のビーコンフレームを監視し、APの BSSID と SSID、そしてその発信元がサポートしているレート情報を簡易的にダンプするスクリプトの実装例です。
from scapy.all import *
from scapy.layers.dot11 import Dot11, Dot11Beacon, Dot11Elt
# モニターモードに設定済みの無線インターフェース名(環境に合わせて変更してください)
MONITOR_IFACE = "wlan0mon"
def packet_handler(packet):
"""
キャプチャしたパケットをフィルタリングし、ビーコンフレームから
BSSID、SSID、Supported Ratesを抽出しコンソールに表示するコールバック関数
"""
# パケットが802.11のビーコンフレームであるかチェック
if packet.haslayer(Dot11Beacon):
# 送信元MACアドレス(BSSID)の取得
bssid = packet[Dot11].addr2
# SSID(ネットワーク名)の取得
# Dot11Eltレイヤーを走査してID=0(SSID)を探す
ssid = "Hidden or Broadcast SSID"
rates = []
# 情報要素(Information Elements)をイテレート
elt = packet.getlayer(Dot11Elt)
while elt:
if elt.ID == 0: # SSID
ssid = elt.info.decode('utf-8', errors='ignore')
elif elt.ID == 1: # Supported Rates
# レート情報は1バイトあたり0.5Mbps(ビット7は基本レートフラグ)
rates = [f"{ord(chr(r)) * 0.5}Mbps" for r in elt.info]
elt = elt.payload.getlayer(Dot11Elt)
print(f"[+]発見したAP -> BSSID: {bssid} | SSID: {ssid}")
print( f" サポートレート: {', '.join(rates)}")
if __name__ == "__main__":
print(f"[*] インターフェース {MONITOR_IFACE} でWi-Fiビーコンのキャプチャを開始します...")
# 指定したインターフェースでパケットスニッフィングを実行(無限ループ)
# store=0を指定してメモリリークを防ぐ
sniff(iface=MONITOR_IFACE, prn=packet_handler, store=0)
> 【エンジニアへの実務Tips】
> このスクリプトを動かすには、事前にLinux環境で iw や airmon-ng などのツールを使い、無線NICをモニターモード(Monitor Mode)に変更しておく必要があります。マネージドモードのままでは管理フレーム(ビーコンやプローブ)はOSのドライバ層でドロップされてしまうため注意してください。
—
5. おわりに:目に見えない電波を「視る」エンジニアへ
無線通信の世界は、一見すると「魔法の電波」のように片付けられがちですが、その実態は極めて厳密なプロトコル仕様(IEEE 802.11)とパケットの往来によって成り立っています。
Web APIのレスポンスタイムに悩むのと同様に、Wi-Fiの不安定さに直面したときも、勘や経験則に頼るのではなく、ビーコンが何を叫び、クライアントがどのようなプローブ要求で応えているのかというパケットの文脈を読み解くことができれば、どんな難解な接続トラブルも必ず論理的に解決の糸口が見つかります。
「なぜ繋がらないのか」をパケットレベルで突き詰めるこの泥臭い作業こそが、インフラエンジニアとしての最大の武器になります。ぜひ、次のトラブルシューティングではエアチェックの視点を取り入れてみてください!
コメント