Windowsからのポート疎通を確認する方法の比較

## はじめに
クラウド基盤の構築をするにあたり、クラウドのネットワーク機能のファイアウォール(以降、F/W)をプロトコル・ポート単位で細かく制御しているときに、Windowsから接続する際のポート疎通確認方法が課題になったので調査しました。

## 課題
Windows PowerShell の Test-NetConnection では、単純なポート単位の疎通確認に使用できないケースがある。
→　**接続するポートで待ち受けているプロセスが接続先サーバに居ないとチェック"失敗"**となってしまう

Linux の `taraceroute -p <port番号>` とは少し異なり、より厳密なチェックがされているように見える。


## 結論
ツールの追加が許されるのであれば、Windowsからのポート疎通確認は **[Portqry][Portqry] がお勧め**。
理由として、何故つながらないのかをある程度切り分けることができるため。

単純な疎通だけでなく、接続時のレイテンシも合わせて見たいときは [PsPing][PsPing] が良さそう。

詳細は検証内容に記載している。

## 検証内容

### シナリオ
- TCP, IPv4 での Windows -> Linux の接続を想定
- 接続元のWindowsで、下記3つのコマンド/ツールの動作を確認
    - [Test-NetConnection][Test-NetConnection]
    - [PsPing][PsPing]
    - [Portqry][Portqry]
- 各コマンド/ツールについて、以下4パターンでTCPプロトコルでの接続性を確認（UDP,ICMPは常に不許可）

パターン|F/W許可|ポート待受状態|待機の方式
---|---|---|---
1|不許可|待受なし|N/A
2|許可|待受なし|N/A
3|許可|LISTENのみ|Linuxのnc -lコマンドでTCP port:61000をLISTEN
4|許可|プロセス動作あり|起動しているsshd(port:22)

### 環境情報
- 接続元OS：Windows Server 2016 Standard 10.0.14393 Build 14393
    - IP Address：10.0.0.19
- 接続先OS：Oracle Linux 7.7 (Kernel: 4.14.35-1902.300.11.el7uek.x86_64)
    - IP Address：10.0.0.96
- ツールVersion：
    - PowerShell(Test-NetConnection)：5.1（上記OS標準搭載）
    - PsPing2.45（解凍後"psversion.txt"に記載あり）
    - Portqry2.0（解凍後"readme.txt"に記載あり）

### 検証結果

#### パターン1：
F/W許可|ポート待受状態|待機の方式
---|---|---
不許可|待受なし|N/A

##### Test-NetConnection

`TcpTestSucceeded        : False`と表示される。

オプション名|説明
---|---
-InformationLevel Detailed|詳細な結果を表示

```powershell:接続元Windows
PS C:\> Test-NetConnection 10.0.0.96 -InformationLevel Detailed -Port 61000
WARNING: TCP connect to 10.0.0.96:61000 failed
WARNING: Ping to 10.0.0.96 failed -- Status: TimedOut


ComputerName            : 10.0.0.96
RemoteAddress           : 10.0.0.96
RemotePort              : 61000
NameResolutionResults   : 10.0.0.96
MatchingIPsecRules      :
NetworkIsolationContext : Internet
IsAdmin                 : False
InterfaceAlias          : Ethernet
SourceAddress           : 10.0.0.19
NetRoute (NextHop)      : 0.0.0.0
PingSucceeded           : False
PingReplyDetails (RTT)  : 0 ms
TcpTestSucceeded        : False
```

##### PsPing

`This operation returned because the timeout period expired.`でタイムアウトしていることが分かる。

オプション名|説明
---|---
-n 5|接続を5回試行
-4|IPv4を強制

```powershell:接続元Windows
PS C:\> psping64.exe -n 5 -4 10.0.0.96:61000

PsPing v2.10 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

TCP connect to 10.0.0.96:61000:
6 iterations (warmup 1) ping test:
Connecting to 10.0.0.96:61000 (warmup): from 0.0.0.0:52545:
This operation returned because the timeout period expired.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52546:
This operation returned because the timeout period expired.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52548:
This operation returned because the timeout period expired.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52549:
This operation returned because the timeout period expired.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52550:
This operation returned because the timeout period expired.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52554:
This operation returned because the timeout period expired.

TCP connect statistics for 10.0.0.96:61000:
  Sent = 5, Received = 0, Lost = 5 (100% loss),
  Minimum = 0.00ms, Maximum = 0.00ms, Average = 0.00ms
```

##### Portqry

`FILTERED`と表示され、ブロックされていることが分かる。

```powershell:接続元Windows
PS C:\> PortQry.exe -n 10.0.0.96 -p TCP -e 61000

Querying target system called:

 10.0.0.96

Attempting to resolve IP address to a name...

Failed to resolve IP address to name

querying...

TCP port 61000 (unknown service): FILTERED
```

#### パターン2：
F/W許可|ポート待受状態|待機の方式
---|---|---
許可|待受なし|N/A

##### 準備

接続先のLinuxで、TCP port:61000で待受けしているプロセスが居ないことを確認。

```console:接続先Linux
# lsof -i4:61000 -P
（結果表示なし）

# netstat -atpn | grep 61000
（結果表示なし）
```

##### Test-NetConnection

パターン1同様に`TcpTestSucceeded        : False`としか表示されない。パターン1と区別がつかない。（課題に記載している内容）

```powershell:接続元Windows
PS C:\> Test-NetConnection 10.0.0.96 -InformationLevel Detailed -Port 61000
WARNING: TCP connect to 10.0.0.96:61000 failed
WARNING: Ping to 10.0.0.96 failed -- Status: TimedOut


ComputerName            : 10.0.0.96
RemoteAddress           : 10.0.0.96
RemotePort              : 61000
NameResolutionResults   : 10.0.0.96
MatchingIPsecRules      :
NetworkIsolationContext : Internet
IsAdmin                 : False
InterfaceAlias          : Ethernet
SourceAddress           : 10.0.0.19
NetRoute (NextHop)      : 0.0.0.0
PingSucceeded           : False
PingReplyDetails (RTT)  : 0 ms
TcpTestSucceeded        : False
```

##### PsPing

エラーが変わり`The remote computer refused the network connection.`となる。パターン1と区別が可能。

```powershell:接続元Windows
PS C:\> psping64.exe -n 5 -4 10.0.0.96:61000

PsPing v2.10 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

TCP connect to 10.0.0.96:61000:
6 iterations (warmup 1) ping test:
Connecting to 10.0.0.96:61000 (warmup): from 0.0.0.0:52780:
The remote computer refused the network connection.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52781:
The remote computer refused the network connection.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52782:
The remote computer refused the network connection.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52783:
The remote computer refused the network connection.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52784:
The remote computer refused the network connection.
Connecting to 10.0.0.96:61000: from 0.0.0.0:52785:
The remote computer refused the network connection.

TCP connect statistics for 10.0.0.96:61000:
  Sent = 5, Received = 0, Lost = 5 (100% loss),
  Minimum = 0.00ms, Maximum = 0.00ms, Average = 0.00ms
```

##### Portqry

`NOT LISTENING`になり、疎通はできるがLISTEN状態でないことまで分かる。

```powershell:接続元Windows
PS C:\> PortQry.exe -n 10.0.0.96 -p TCP -e 61000

Querying target system called:

 10.0.0.96

Attempting to resolve IP address to a name...

Failed to resolve IP address to name

querying...

TCP port 61000 (unknown service): NOT LISTENING
```

#### パターン3：
F/W許可|ポート待受状態|待機の方式
---|---|---
許可|LISTENのみ|Linuxのnc -lコマンドでTCP port:61000をLISTEN

##### 準備

接続先のLinuxで、ncコマンドでTCP port:61000で待受けさせる。
参考：[ncコマンドの使い方](https://qiita.com/hana_shin/items/97e6c03ac5e5ed67ce38)

```console:接続先Linux
# nc -4 -kl 61000 &
[1] 1379
```

LISTEN状態であることを確認。

```console:接続先Linux
# lsof -i4:61000 -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      1379 root    3u  IPv4 165166      0t0  TCP *:61000 (LISTEN)

# netstat -atpn | grep 61000
tcp        0      0 0.0.0.0:61000           0.0.0.0:*               LISTEN      1379/nc
```

##### Test-NetConnection

接続できる。Falseが`True`に変わるのみ。

```powershell:接続元Windows
PS C:\> Test-NetConnection 10.0.0.96 -InformationLevel Detailed -Port 61000


ComputerName            : 10.0.0.96
RemoteAddress           : 10.0.0.96
RemotePort              : 61000
NameResolutionResults   : 10.0.0.96
MatchingIPsecRules      :
NetworkIsolationContext : Internet
IsAdmin                 : False
InterfaceAlias          : Ethernet
SourceAddress           : 10.0.0.19
NetRoute (NextHop)      : 0.0.0.0
TcpTestSucceeded        : True
```

##### PsPing

接続できる。エラーメッセージが消えて時間が表示される。

```powershell:接続元Windows
PS C:\> psping64.exe -n 5 -4 10.0.0.96:61000

PsPing v2.10 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

TCP connect to 10.0.0.96:61000:
6 iterations (warmup 1) ping test:
Connecting to 10.0.0.96:61000 (warmup): from 10.0.0.19:52637: 0.43ms
Connecting to 10.0.0.96:61000: from 10.0.0.19:52639: 0.44ms
Connecting to 10.0.0.96:61000: from 10.0.0.19:52640: 0.63ms
Connecting to 10.0.0.96:61000: from 10.0.0.19:52641: 0.46ms
Connecting to 10.0.0.96:61000: from 10.0.0.19:52642: 0.43ms
Connecting to 10.0.0.96:61000: from 10.0.0.19:52644: 0.42ms

TCP connect statistics for 10.0.0.96:61000:
  Sent = 5, Received = 5, Lost = 0 (0% loss),
  Minimum = 0.42ms, Maximum = 0.63ms, Average = 0.48ms
```

##### Portqry

`LISTENING`に変わる。

```powershell:接続元Windows
PS C:\> PortQry.exe -n 10.0.0.96 -p TCP -e 61000

Querying target system called:

 10.0.0.96

Attempting to resolve IP address to a name...

Failed to resolve IP address to name

querying...

TCP port 61000 (unknown service): LISTENING
```


#### パターン4：
F/W許可|ポート待受状態|待機の方式
---|---|---
許可|プロセス動作あり|起動しているsshd(port:22)

##### 準備
LISTEN状態であることを確認。

```console:接続先Linux
# lsof -i4:22 -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    2146 root    3u  IPv4  25288      0t0  TCP *:22 (LISTEN)
sshd    5600 root    3u  IPv4 183167      0t0  TCP tyofctestf:22->10.0.0.5.24:58516 (ESTABLISHED)
sshd    5612  opc    3u  IPv4 183167      0t0  TCP tyofctestf:22->10.0.0.5.24:58516 (ESTABLISHED)

# netstat -atpn | grep 22
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      2146/sshd
tcp        0      0 10.0.0.96:22          10.0.0.5.24:58516     ESTABLISHED 5600/sshd: opc [pri
tcp        1      0 10.0.0.96:54522       169.254.169.254:80      CLOSE_WAIT  2139/oracle-cloud-a
tcp6       0      0 :::22                   :::*                    LISTEN      2146/sshd
```

##### Test-NetConnection

パターン3と同じ。

```powershell:接続元Windows
PS C:\> Test-NetConnection 10.0.0.96 -InformationLevel Detailed -Port 22


ComputerName            : 10.0.0.96
RemoteAddress           : 10.0.0.96
RemotePort              : 22
NameResolutionResults   : 10.0.0.96
MatchingIPsecRules      :
NetworkIsolationContext : Internet
IsAdmin                 : False
InterfaceAlias          : Ethernet
SourceAddress           : 10.0.0.19
NetRoute (NextHop)      : 0.0.0.0
TcpTestSucceeded        : True
```

##### PsPing

パターン3と同じ。

```powershell:接続元Windows
PS C:\> psping64.exe -n 5 -4 10.0.0.96:22

PsPing v2.10 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

TCP connect to 10.0.0.96:22:
6 iterations (warmup 1) ping test:
Connecting to 10.0.0.96:22 (warmup): from 10.0.0.19:52647: 0.58ms
Connecting to 10.0.0.96:22: from 10.0.0.19:52648: 0.43ms
Connecting to 10.0.0.96:22: from 10.0.0.19:52649: 0.45ms
Connecting to 10.0.0.96:22: from 10.0.0.19:52650: 0.50ms
Connecting to 10.0.0.96:22: from 10.0.0.19:52651: 0.45ms
Connecting to 10.0.0.96:22: from 10.0.0.19:52652: 0.44ms

TCP connect statistics for 10.0.0.96:22:
  Sent = 5, Received = 5, Lost = 0 (0% loss),
  Minimum = 0.43ms, Maximum = 0.50ms, Average = 0.45ms
```

##### Portqry

パターン3と同じくLISTENING。さらに、`unknown service`のところが`ssh service`と表示されるようになった。

```powershell:接続元Windows
PS C:\> PortQry.exe -n 10.0.0.96 -p TCP -e 22

Querying target system called:

 10.0.0.96

Attempting to resolve IP address to a name...

Failed to resolve IP address to name

querying...

TCP port 22 (ssh service): LISTENING
```

### サマリ
- **Test-NetConnection**
    - OSに標準搭載されているため使いやすいが、LISTENしていないケースが"失敗"となってしまい区別できない点に注意。

- **PsPing**
    - LISTENしていないケースもエラーが変わることで区別できる。レイテンシの確認も同時に実施したいときにお勧め。追加インストール必要。

- **Portqry**
    - 各パターンをしっかり区別できるため、ポート疎通確認では最もお勧め。追加インストール必要。

## さらに調べると良さそうなこと
- コマンドやツールだけでなくコーディングでの実施方法（追加ツールのインストールが許されない環境向け）

## Tips
- Portqryのサポートバージョンは下記ページのタイトル真下にある"Applies to:"を参照
    - 参考：[Description of the Portqry.exe command-line utility][Portqry]

- Test-NetConnection が使用できるのはPowerShell4以降で、PowerShell4が標準搭載されているのはWindows8.1/Windows Server 2012 R2以降
    - 参考：[stackoverflow - How do I get PowerShell 4 cmdlets such as Test-NetConnection to work on Windows 7?
](https://stackoverflow.com/questions/21252824/how-do-i-get-powershell-4-cmdlets-such-as-test-netconnection-to-work-on-windows)
    - 参考：[Wikipedia - PowerShell - Windows PowerShellのバージョンと対応OS](https://ja.wikipedia.org/wiki/PowerShell)



[Test-NetConnection]:https://docs.microsoft.com/en-us/powershell/module/nettcpip/test-netconnection?view=win10-ps
[PsPing]:https://docs.microsoft.com/en-us/sysinternals/downloads/psping
[Portqry]:https://support.microsoft.com/en-us/help/310099/description-of-the-portqry-exe-command-line-utility