ncやtelnetなしにポートの疎通確認をしたい

ポートの疎通確認にはnc(netcat)やtelnetをよく使う。 しかしコンテナや最小構成のサーバではこれらのコマンドが入っていないことがある。

bashには/dev/tcpという仮想ファイルシステムが組み込まれており、外部コマンドなしにTCPの疎通確認ができる。

/dev/tcp の使い方

/dev/tcp/ホスト/ポートに書き込むとTCP接続を試みる。 接続に成功すれば終了コード0、失敗すれば1が返る。

$ (echo >/dev/tcp/8.8.8.8/53) 2>/dev/null && echo "open" || echo "closed"
open

接続できないポートを指定した場合はclosedと表示される。

$ (echo >/dev/tcp/8.8.8.8/9999) 2>/dev/null && echo "open" || echo "closed"
closed

(echo >/dev/tcp/...) のようにサブシェルで囲むのは、接続後にファイルディスクリプタを確実に閉じるため。

タイムアウトを設定する

ポートが閉じていてもファイアウォールでパケットが落とされている場合、接続待ちで長時間ブロックされる。 timeoutコマンドで待機時間の上限を設定する。

$ timeout 3 bash -c "(echo >/dev/tcp/8.8.8.8/53) 2>/dev/null" && echo "open" || echo "closed"
open

timeout 3で3秒以内に接続できなければタイムアウトとして処理される。

ホスト名でも使える

IPアドレスだけでなくホスト名も指定できる。

$ (echo >/dev/tcp/example.com/80) 2>/dev/null && echo "open" || echo "closed"
open

変数で指定する

ホストとポートを変数で渡せる。

HOST=8.8.8.8
PORT=53
(echo >/dev/tcp/$HOST/$PORT) 2>/dev/null && echo "$HOST:$PORT is open" || echo "$HOST:$PORT is closed"
8.8.8.8:53 is open

複数ポートをループでチェックする

HOST=example.com
for PORT in 22 80 443; do
    timeout 3 bash -c "(echo >/dev/tcp/$HOST/$PORT) 2>/dev/null" \
        && echo "$HOST:$PORT open" \
        || echo "$HOST:$PORT closed"
done
example.com:22 closed
example.com:80 open
example.com:443 open

注意点

/dev/tcpはbash固有の機能。shzshでは使えない。

$ sh -c "(echo >/dev/tcp/8.8.8.8/53)"
sh: /dev/tcp/8.8.8.8/53: No such file or directory

スクリプトで使う場合はシバンに#!/bin/bashを指定する。

関連

参考