NetworkTips

Windows標準のpktmonでパケットが落ちた場所を突き止める【Wiresharkを入れられない現場で】

スポンサーラベル
pktmonはパケットが通るコンポーネントを階層で見せる Network

当サイトはアフィリエイト広告を利用しています。

「通信が通らない」の切り分けで困るのは、パケットが消えた場所が分からないことです。相手まで届いていないのか、届いたが捨てられたのか、自分のPCの中で落ちているのか。

業務PCにWiresharkを入れられない現場も多いですが、Windows 10 1809以降には pktmon が標準で入っています。しかも、Wiresharkには無い機能があります。

pktmonはパケットが通るコンポーネントを階層で見せる

パケットが通るコンポーネントを階層で見せ、どこで、どんな理由で落ちたかを教えてくれます。この記事は Windows 11(build 26200)での実測です。

1. まず、すべて管理者権限が要る

最初につまずくのがここです。

pktmon list
PktMon ドライバーとの通信に失敗しました: アクセスが拒否されました。

一覧を見るだけの list も、状態を見るだけの status も落ちます。 管理者権限で開いたPowerShellでないと、1つも動きません。

# 昇格したウィンドウを開く
Start-Process powershell -Verb RunAs

2. list と list --all は別物

pktmon list はネットワークアダプターの一覧です。

   ID MAC アドレス          名前
   14 00-11-22-33-44-55 Intel(R) Ethernet Controller (3) I225-V
   18                   Wintun Userspace Tunnel
  139 00-11-22-33-44-55 vEthernet (LAN)
  142 00-15-5D-AA-BB-01 vEthernet (Default Switch)
  145 00-15-5D-AA-BB-02 vEthernet (WSL (Hyper-V firewall))

物理NIC(14)と vEthernet(139)のMACが同じなのは、Hyper-Vの仮想スイッチが物理NICのMACを引き継ぐためです。

本番はこちらです。

pktmon list --all
vSwitch: LAN
    ID: 138
    NIC: Intel(R) Ethernet Controller (3) I225-V
        ID: 14
        ドライバー: e2f.sys
        ifIndex: 10
        フィルター ドライバー:
            36 wfplwfs.sys WFP Native Filter
        プロトコル:
            94 vmswitch.sys VMS Protocol * (すべて)
            89 mslldp.sys   MSLLDP       LLDP
    NIC: vEthernet (LAN)
        ID: 139
        ドライバー: vmswitch.sys
        ifIndex: 8
        フィルター ドライバー:
            39 wfplwfs.sys WFP Native Filter
            38 pacer.sys   QoS Packet Scheduler
            37 wfplwfs.sys WFP 802.3 Filter
        プロトコル:
            97 tcpip.sys  TCPIP6 IPv6
            96 tcpip.sys  TCPIP  IPv4, ARP

パケットが通るドライバが、.sys のファイル名つきで全部並びます。これが pktmon の一番の価値だと思います。

VPNクライアントやEDR、ウイルス対策ソフトがNDISフィルタとして割り込んでいる場合、ここに名前が出ます。 「何かが通信を邪魔している気がする」を、製品名で確認できます。

物理NICが ifIndex 10、vEthernet が ifIndex 8 という対応も読み取れます。Windowsで通信が思った経路を通らないで触れた「物理NICにIPアドレスが付いていない」状態は、この構造が原因です。

3. counters でコンポーネント別のドロップ数を見る

ログを書かずにカウンターだけ取る方法があります。

# --counters-only ならログファイルを作らない。いちばん軽い
pktmon start --capture --counters-only
# (ここで再現操作をする)
pktmon counters
pktmon stop

実測の抜粋です。

vSwitch: LAN
 ID 名前                    カウンター  方向            パケット        バイト
 14 Intel(R) Ethernet C...  ドロップ   Rx               470       101,073
                            Upper      Rx               377        69,777
 36 WFP Native Filter       Lower      Rx               470       101,073
139 vEthernet (LAN)         ドロップ   エグレス           3           369
 97 TCPIP6                  ドロップ   Rx               126        26,564
 96 TCPIP                   ドロップ   Rx               313        64,523

Lower と Upper は、そのコンポーネントの下側と上側です。両者の差が、そのコンポーネントで消えた分になります。上の例なら物理NICで Rx 470 入って Upper 377 しか上がっていません。

8秒のpingの裏でこれだけ落ちています。平常時でもドロップは常にあるので、「ドロップがある=異常」ではありません。再現操作の前後で数えて、増えた分を見るのが正しい使い方です。

4. --type drop なら理由が付く

ここがWiresharkとの決定的な違いです。

# 落ちたパケットだけを、全長(--pkt-size 0)で記録する
pktmon start --capture --type drop --pkt-size 0 --file-name C:\temp\drops.etl
# (再現操作)
pktmon stop
pktmon etl2txt C:\temp\drops.etl --out C:\temp\drops.txt

出力の1件はこうなります。

ドロップ: PktGroupId 8348、PktNumber 1、外部 0、方向 Rx 、種類 IP 、
         コンポーネント 126、フィルター 0、Not locally destined 、
    ドロップ: ip: 192.168.10.104.50160 > 239.255.255.251.50160: UDP, length 547

「コンポーネント 126 が Not locally destined で落とした」まで書いてあります。実測で拾えた理由を数えました。

実測したドロップ理由の一覧
件数理由意味
875Unsupported EtherTypeそのコンポーネントが扱わない種別
50INET: session state errorTCPの状態と合わないセグメント
14INET: transport endpoint was not found待ち受けているプロセスが無い
11Not locally destined自分宛てではない
4INET: duplicate segment再送などの重複
3ARP: not locally destined自分宛てでないARP
2INET: received RST相手からRSTが返った
1Port unreachableポートが閉じている

切り分けで効くのは太字の行です。transport endpoint was not found が出ていれば、ファイアウォールではなく「アプリが起動していない・別ポートで待っている」と分かります。ここを取り違えると、ファイアウォールの設定を延々と見直すことになります。

ドロップのログに出るコンポーネント番号は pktmon list には載っていないことがあります(上の126もそうです)。アダプタしか出ないためで、pktmon list --all と突き合わせてください。

5. Wiresharkへ渡すなら etl2pcap

サブコマンド名は pcapng ではなく etl2pcap です。

pktmon etl2pcap C:\temp\drops.etl --out C:\temp\drops.pcapng

ただし--type drop で取ったものを変換したら、156バイトのほぼ空のファイルになりました(テキスト側は3,018行取れています)。Wiresharkで中身を見たいときは、--type drop ではなく通常のキャプチャで取るほうが確実です。

6. フィルタで絞る

pktmon filter remove                                  # 既存を全部消してから
pktmon filter add 'icmp-only' --transport-protocol ICMP
pktmon filter add 'dns-only' --port 53
pktmon filter list
     # 名前       プロトコル   ポート
     1 icmp-only ICMP
     2 dns-only           53

フィルタは明示的に消すまで残ります。 調査が終わったら pktmon filter remove を忘れないでください。次の人が「なぜか特定の通信しか取れない」で悩みます。

7. 取った後の取り扱いに注意

実測して気づいたのですが、キャプチャには想像以上に他人の情報が入ります。

  • 同じLANにいる別の機器のMACアドレス(マルチキャストやブロードキャストの送信元)
  • 自分のPCのグローバルIPv6アドレス(プレフィックスから契約回線が分かる)
  • 接続先のIPアドレスとポート(どのサービスを使っているか)

ベンダーに送る前、チケットに添付する前に中身を確認してください。 社内のトラブルシューティングのつもりが、社外に経路情報を渡すことになります。

もう1つ、PowerShellで出力をファイルに残すときの罠があります。

# やってはいけない:ファイルまで40行で切れる
pktmon list --all | Tee-Object -FilePath out.txt | Select-Object -First 40

# 正しい:ファイルに書いてから、表示だけ絞る
pktmon list --all | Out-File out.txt
Get-Content out.txt -TotalCount 40

Select-Object -First は上流のパイプラインに停止を伝えるので、Tee-Object の書き込みごと打ち切られます。実際にこれで一覧の後半(別の仮想スイッチの分)を失いました。

なお Out-File と Tee-Object の既定の文字コードは、Windows PowerShell 5.1 では UTF-16 です。findstr や grep で検索できないので、-Encoding utf8 を付けておくと後が楽です。

8. まとめ

  • pktmon は Windows 10 1809以降に標準搭載。 インストール不要
  • すべてのサブコマンドが管理者権限必須。 list も status も落ちる
  • pktmon list はアダプタだけ。list --all で全階層がドライバ名つきで出る
  • 割り込んでいるNDISフィルタが製品名で分かる。 これは他のツールには無い
  • counters の Lower と Upper の差が、そこで消えた分
  • 平常時でもドロップはある。 再現操作の前後で増分を見る
  • --type drop なら理由が付く。 transport endpoint was not found ならアプリ側
  • 変換は etl2pcap(pcapng ではない)
  • フィルタは消すまで残る。 終わったら pktmon filter remove
  • キャプチャには他人のMACと自分のグローバルIPv6が残る。 渡す前に確認する

Wiresharkが入れられない現場でも、「どこで落ちたか」だけならこれで十分だと思います。

コメント

タイトルとURLをコピーしました