【PowerShell】ポートの疎通確認をする方法|Test-NetConnectionの見方とUDPの確かめ方

スポンサーリンク
powershell
スポンサーリンク

リモートデスクトップやNAS、サーバーにつながらない時、「そもそも相手のポートまで通信が届いているのか」を確かめたいことがある。PowerShellなら、追加のソフトを入れなくても確認できる。

私は以前、仕事で複数のサーバーへの疎通確認をまとめて行うスクリプトを作った時に、SNMPのポート(161・162)をTest-NetConnectionで調べて、全部Falseになったことがある。SNMPはUDPなので、TCPしか調べられないTest-NetConnectionではFalseになって当然だった。この記事では、TCPの確認方法に加えて、UDPを確かめる方法もまとめる。

結論から書くと、TCPのポートはTest-NetConnectionに-Portを付けて実行し、TcpTestSucceededがTrueならつながる。UDPはTest-NetConnectionでは調べられないので、相手側で待ち受けているかを確認したうえで、実際にデータを送って返事が来るかで確かめる。

Test-NetConnection -ComputerName www.google.com -Port 443

この記事の実行例は、Windows 11のWindows PowerShell 5.1で実行したものだ。自分のPCやNASのIPアドレスは<PCのIP>のように書き換えている。

TCPのポートを確認する:Test-NetConnection -Port

-ComputerNameに相手のホスト名かIPアドレス、-Portにポート番号を指定する。Googleの443番(HTTPS)に試すと、次のようになった。

ComputerName     : www.google.com
RemoteAddress    : 142.251.152.119
RemotePort       : 443
InterfaceAlias   : イーサネット
SourceAddress    : <PCのIP>
TcpTestSucceeded : True

見るところはTcpTestSucceededだ。Trueなら、相手のそのポートにTCPで接続できている。ほかの項目の意味は次のとおり。

項目 意味
ComputerName 指定した相手
RemoteAddress 名前解決した相手のIPアドレス
RemotePort 試したポート番号
InterfaceAlias 通信に使った自分のPCのネットワークアダプター
SourceAddress 通信に使った自分のPCのIPアドレス
TcpTestSucceeded TCPで接続できたか(True/False)

うちのNAS(ファイル共有のSMBは445番)にも試してみた。

Test-NetConnection -ComputerName <NAS> -Port 445
ComputerName     : <NAS>
RemoteAddress    : <NASのIP>
RemotePort       : 445
InterfaceAlias   : イーサネット
SourceAddress    : <PCのIP>
TcpTestSucceeded : True

NASの共有フォルダーが開けないのに445番がTrueなら、通信は届いていて、問題は認証やSMBの設定のほうにある、と切り分けられる。Windows 11 24H2以降でNASにつながらなくなった時の原因は、別の記事にまとめている。

よく使うポートは名前でも指定できる

-CommonTCPPortを使うと、ポート番号の代わりにHTTP・RDP・SMB・WINRMの4つを名前で指定できる。自分のPCのリモートデスクトップ(3389番)を確認するなら次のとおり。

Test-NetConnection -ComputerName localhost -CommonTCPPort RDP
ComputerName     : localhost
RemoteAddress    : ::1
RemotePort       : 3389
InterfaceAlias   : Loopback Pseudo-Interface 1
SourceAddress    : ::1
TcpTestSucceeded : True

つながらない時の出力と、時間がかかる理由

Googleの81番(使われていないポート)に試すと、次のようになった。

Test-NetConnection -ComputerName www.google.com -Port 81
TCP connect to (142.251.152.119 : 81) failed
TCP connect to (142.251.154.119 : 81) failed
TCP connect to (142.251.153.119 : 81) failed
TCP connect to (142.251.156.119 : 81) failed
TCP connect to (142.251.157.119 : 81) failed
TCP connect to (142.251.150.119 : 81) failed
TCP connect to (142.251.155.119 : 81) failed
TCP connect to (142.251.151.119 : 81) failed
TCP connect to (2001:4860:482c:7700:: : 81) failed
TCP connect to (2001:4860:482b:7700:: : 81) failed
TCP connect to (2001:4860:4828:7700:: : 81) failed
TCP connect to (2001:4860:4826:7700:: : 81) failed
TCP connect to (2001:4860:482a:7700:: : 81) failed
TCP connect to (2001:4860:4829:7700:: : 81) failed
TCP connect to (2001:4860:4827:7700:: : 81) failed
TCP connect to (2001:4860:482d:7700:: : 81) failed

ComputerName           : www.google.com
RemoteAddress          : 142.251.152.119
RemotePort             : 81
InterfaceAlias         : イーサネット
SourceAddress          : <PCのIP>
PingSucceeded          : True
PingReplyDetails (RTT) : 2 ms
TcpTestSucceeded       : False

TcpTestSucceededがFalseになり、ポートに接続できなかった時は、PingSucceeded(pingが通ったか)も表示される。ここがTrueでTCPがFalseなら、「相手までは届いているが、そのポートには接続できない」ということになる。ただし、pingを返さない設定の機器も多いので、PingSucceededがFalseでも相手まで届いていないとは限らない。

気をつけたいのは時間だ。この1回で約169秒かかった。TCP connect to ... failedの行を見ると、www.google.comの名前解決で返ってきたIPアドレス16個(IPv4が8個、IPv6が8個)すべてに順番に接続を試している。1つの名前に複数のIPアドレスがある相手だと、失敗した時にとても待たされる。急ぐ時は、後で書くタイムアウトを短くした確認方法を使うとよい。

ホスト名が間違っていて名前解決できない時は、次のようにすぐ終わる。TCPの結果は出ず、RemoteAddressも空になる。

Name resolution of no-such-host.invalid failed

ComputerName   : no-such-host.invalid
RemoteAddress  : 
InterfaceAlias : 
SourceAddress  : 
PingSucceeded  : False

Falseになった時に疑うところ

  • 相手側で、そのポートを使うサービスが動いていない(この後の「相手側で待ち受けているか確認する」で確かめられる)
  • 相手側のWindowsファイアウォールなどで、そのポートへの受信が許可されていない
  • 途中のルーターやファイアウォール、VPNで止められている
  • そもそもUDPのポートを調べている(Test-NetConnectionはTCPしか調べられない)

Tailscale経由のリモートデスクトップがつながらない時の確認ポイントは、別の記事にまとめている。

True/Falseだけ欲しい時:-InformationLevel Quiet

スクリプトの中で使うなら、-InformationLevel Quietを付けると結果がTrueかFalseだけになる。

Test-NetConnection -ComputerName www.google.com -Port 443 -InformationLevel Quiet

443番ではTrueだけが返ってきた。81番で試すと、TCP connect to ... failedの行は表示されたうえで、最後にFalseが返ってきた。かかった時間は約169秒で、Quietにしても速くはならない。

逆に、どこを通って通信しているかまで見たい時は-InformationLevel Detailedを付ける。名前解決の結果(NameResolutionResults)や、次に渡すルーター(NetRoute (NextHop))も表示される。

Test-NetConnection -ComputerName www.google.com -Port 443 -InformationLevel Detailed

タイムアウトを短くして確認する(複数のポートもまとめて)

Test-NetConnectionはタイムアウトを指定できない。待たされたくない時は、.NETのTcpClientで接続を試して、決めた時間で打ち切る。次は1秒(1000ミリ秒)で打ち切る関数だ。

function Test-TcpPort($ComputerName, $Port, $TimeoutMs = 1000) {
    $client = New-Object System.Net.Sockets.TcpClient
    try {
        $client.ConnectAsync($ComputerName, $Port).Wait($TimeoutMs)
    } catch {
        $false
    } finally {
        $client.Dispose()
    }
}

Test-TcpPort www.google.com 443
Test-TcpPort www.google.com 81

時間内に接続できればTrue、できなければFalseが返る。私の環境では443番はTrue、81番は1秒でFalseになった。Test-NetConnectionで約169秒かかった81番が、1秒で終わる。

複数のポートをまとめて確認するなら、上の関数を読み込んだ状態で次のようにする。

foreach ($p in 80, 443, 81) {
    [pscustomobject]@{ Port = $p; Open = (Test-TcpPort www.google.com $p) }
}
Port  Open
----  ----
  80  True
 443  True
  81 False

相手が遠い・回線が遅いと、1秒では足りずにFalseになることもある。Falseが出たら、$TimeoutMsを3000などに延ばしてもう一度試すとよい。複数のサーバーをCSVから読み込んで一括で確認するスクリプトは、以前の記事に載せている。

PowerShell 7ならTest-Connection -TcpPortも使える(試していない)

PowerShell 7では、Test-Connectionに-TcpPortを付けてTCPの接続を確認でき、結果はTrue/Falseで返る。タイムアウトは-TimeoutSecondsで指定でき、既定は5秒だ(Microsoft Learnのドキュメントより)。私のPCにはPowerShell 7を入れていないので、試していない。

Test-Connection www.google.com -TcpPort 443

相手側で待ち受けているか確認する:Get-NetTCPConnection

相手もWindowsで、相手のPCを操作できるなら、相手側でそのポートを待ち受けているか(Listen)を確認する。ここで何も出なければ、通信経路ではなく、サービスが動いていないことが原因だ。リモートデスクトップの3389番なら次のとおり。

Get-NetTCPConnection -LocalPort 3389 -State Listen
LocalAddress                        LocalPort RemoteAddress                       RemotePort State       AppliedSetting OwningProcess 
------------                        --------- -------------                       ---------- -----       -------------- ------------- 
::                                  3389      ::                                  0          Listen                     2204          
0.0.0.0                             3389      0.0.0.0                             0          Listen                     2204          

LocalAddressが0.0.0.0(IPv4)や::(IPv6)なら、PCのすべてのIPアドレスで待ち受けている。127.0.0.1だけになっている場合は、そのPCの中からしか接続できない。

どのプログラムが待ち受けているかも見たい時は、OwningProcess(プロセスID)からプロセス名を出す。

Get-NetTCPConnection -LocalPort 3389 -State Listen | Select-Object LocalAddress, LocalPort, State, OwningProcess, @{n='ProcessName';e={(Get-Process -Id $_.OwningProcess).ProcessName}}

私のPCではsvchostと表示された。リモートデスクトップはサービスとして動いているので、svchostになる。この確認は、管理者でないPowerShellでもできた。

UDPのポートを確認する

UDPがTCPと同じように確認できないのは、UDPには「接続」がないからだ。TCPは接続できたかどうかで判断できるが、UDPはデータを送りっぱなしにするだけなので、相手に届いたかどうかは、相手が何か返してくれないと分からない。しかも、多くのサービスは決まった形式のデータにしか返事をしない。

Microsoftのポート確認ツールPortQryの説明でも、UDPのポートから応答がない場合は「LISTENING または FILTERED」(待ち受けているのか、途中で止められているのか判別できない)と報告する、とされている。そこで、UDPは次の順番で確かめる。

確認1:相手側で待ち受けているか(Get-NetUDPEndpoint)

相手側のWindowsで、そのUDPポートを開いているプログラムがあるかを確認する。

Get-NetUDPEndpoint -LocalPort 3389
LocalAddress                             LocalPort      
------------                             ---------      
::                                       3389           
0.0.0.0                                  3389           

リモートデスクトップは、TCPだけでなくUDPの3389番も使っている。同じように、Tailscaleが既定で使うUDPの41641番も、私のPCでは待ち受けていた。

Get-NetUDPEndpoint -LocalPort 41641

ここで表示されれば、相手側でプログラムがそのポートを開いている。ただし、これだけでは自分のPCから届くかどうかは分からない。

確認2:そのサービスに実際に問い合わせる(DNSの例)

相手のサービスが返事をする問い合わせを送れるなら、それが一番確実だ。たとえばDNS(UDPの53番)なら、Resolve-DnsNameで問い合わせ先のサーバーを指定すればよい。

Resolve-DnsName -Name www.google.com -Server 8.8.8.8 -DnsOnly -Type A
Name                                           Type   TTL   Section    IPAddress                                
----                                           ----   ---   -------    ---------                                
www.google.com                                 A      186   Answer     142.251.150.119                          
www.google.com                                 A      186   Answer     142.251.154.119                          
(以下略)

答えが返ってくれば、8.8.8.8のUDP 53番まで届いて、返事も戻ってきている。DNSサーバーがいない宛先(説明用のアドレス192.0.2.1)を指定すると、約7秒後に次のエラーになった。

Resolve-DnsName : www.google.com : This operation returned because the timeout period expired

ほかのサービス(SNMPなど)も、そのサービス用のコマンドやツールで問い合わせて、返事が来るかで確かめるのが基本になる。

確認3:PowerShellで送受信を試す(両方のPCを操作できる時)

サービスに問い合わせる方法がない時や、サービスを動かす前に経路だけ確かめたい時は、相手側で受信用のPowerShellを動かし、自分側から送ってみる。相手側で受け取ったデータをそのまま送り返すので、自分側で返事が見えれば、行きも帰りも届いている。

相手側(受け取る側)で実行する。ポート番号の50000は例なので、確かめたい番号に変える。そのポートを使うサービスが動いていると同じ番号では待ち受けられないので、サービスを止めるか、空いている番号で経路を確かめる。

$port = 50000
$udp = New-Object System.Net.Sockets.UdpClient $port
$udp.Client.ReceiveTimeout = 60000
$remote = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any, 0)
$data = $udp.Receive([ref]$remote)
$udp.Send($data, $data.Length, $remote) | Out-Null
$udp.Close()
"受信: " + [Text.Encoding]::UTF8.GetString($data) + " from " + $remote

60秒(60000ミリ秒)待っている間に、自分側(送る側)で次を実行する。$targetには相手のIPアドレスを入れる。

$target = "192.168.1.10"
$port = 50000
$udp = New-Object System.Net.Sockets.UdpClient
$udp.Client.ReceiveTimeout = 3000
$bytes = [Text.Encoding]::UTF8.GetBytes("hello")
$udp.Send($bytes, $bytes.Length, $target, $port) | Out-Null
$remote = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any, 0)
try {
    "応答: " + [Text.Encoding]::UTF8.GetString($udp.Receive([ref]$remote))
} catch {
    "応答なし: " + $_.Exception.Message
} finally {
    $udp.Close()
}

私は1台のPCの中で試した(受け取る側をバックグラウンドジョブで動かし、送る側の$targetを127.0.0.1にした)。送る側に応答: hello、受け取る側に受信: hello from 127.0.0.1:50818のように表示された。別のPCとの間では試していない。相手側のWindowsファイアウォールで、受け取る側のPowerShellへの受信が止められていると届かないので、その場合はファイアウォールの許可も確認する。

受け取る側を動かさずに送ると、送る側には次のように表示された。

応答なし: Exception calling "Receive" with "1" argument(s): "An existing connection was forcibly closed by the remote host"

「接続が強制的に切断された」というメッセージだが、UDPなので接続が切れたわけではない。相手から「そのポートで待ち受けているプログラムはない」という知らせ(ICMPの「ポートに到達できない」)が返ってきた時に、こう表示される。PortQryの説明でも、この知らせが返ってきた場合は「NOT LISTENING」(待ち受けていない)と判断している。一方、途中のファイアウォールで捨てられた場合は何も返ってこないので、3秒待ってタイムアウトのエラーになるはずだ(こちらは試していない)。

PortQryを使う方法(試していない)

Microsoftが配布しているPortQryというツールを使うと、TCPとUDPのポートを調べられる。DNSやLDAP、SNMPなど一部のサービスには、そのサービス用の問い合わせを送って、待ち受けているかを判断してくれる。PowerShellのコマンドではなく、別途ダウンロードが必要だ。私は使っていないので、詳しくはMicrosoftの説明を見てほしい。

portqry -n 192.168.1.10 -p udp -e 161

まとめ

確認したいこと コマンド
TCPのポートにつながるか Test-NetConnection -ComputerName 相手 -Port 番号
True/Falseだけ欲しい -InformationLevel Quietを付ける
待たされずに確認したい TcpClientでタイムアウトを指定する(この記事のTest-TcpPort)
相手側でTCPを待ち受けているか Get-NetTCPConnection -LocalPort 番号 -State Listen
相手側でUDPを待ち受けているか Get-NetUDPEndpoint -LocalPort 番号
UDPが届くか サービスに問い合わせる(DNSならResolve-DnsName -Server)か、PowerShellで送受信を試す

Test-NetConnectionで調べられるのはTCPだけだ。UDPのポートをFalseで「つながらない」と判断しないように気をつけたい。

関連記事

参考

コメント

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