「Connection timed out」の切り分け - ファイアウォール・経路

「Connection timed out」の切り分け - ファイアウォール・経路

「Connection timed out」とは何を示すのか?

結論: 接続要求を送ったのに何も応答が返ってこない状態。相手や経路上の機器がパケットを黙って捨てている(DROP)か、相手が落ちていて、カーネルが再送を繰り返した末に諦める。エラーが返るまで数秒〜十数秒待たされるのが最大の特徴。

Connection timed out は、接続先へ SYN を送ったのに SYN+ACK が返らず、カーネルが規定回数の再送を尽くして諦めたときに出る。システムコールレベルでは connect()ETIMEDOUT を返し、アプリはこれを「Connection timed out」と表示する。

$ ssh user@192.0.2.10
ssh: connect to host 192.0.2.10 port 22: Connection timed out
$ curl http://192.0.2.10/
curl: (28) Failed to connect to 192.0.2.10 port 80 after 129000 ms: Connection timed out

refused(即座に弾かれる)や No route to host(即座に到達不能と返る)と決定的に違うのは、長い沈黙のあとに失敗する点。これは「誰かがパケットを黙って捨てている」サインで、ほとんどの場合ファイアウォールの DROP相手の不在を意味する。

前提(対象環境)

  • OS:Ubuntu / 一般的な Linux(ss / traceroute / tcpdump を使用)
  • 対象:IP・ポートを指定して接続したいホスト
  • 一部の確認は sudotcpdump / nft 等)が必要

refused・No route to host とどう違うのか?

結論: refused は「届いたがポートで拒否(即 RST)」、No route to host は「経路が無い(即 ICMP)」、timed out は「応答が一切無い(長い待ち)」。同じ「接続できない」でも、応答が返るまでの時間で原因の層が分かる。

接続エラーは応答の返り方で 3 系統に分かれる。まずどれなのかを確定させる。

エラー 返ってくるもの 原因の層
Connection refused TCP RST(即座) サービス / ポート(L4・アプリ)
No route to host ICMP 到達不能(即座) 経路 / ARP / REJECT(L2〜L3)
Connection timed out 無応答(長い待ち) DROP / 沈黙する経路(L3〜L4)
$ time curl -sS http://192.0.2.10/
curl: (28) Failed to connect to 192.0.2.10 port 80 after 129000 ms: Connection timed out

real    2m9.012s

失敗まで数秒以上かかったなら timed out 系で、誰かがパケットを黙って捨てている。即座に弾かれたなら別記事へ。サービス停止・ポート閉塞は Connection refused の切り分け、経路が無い場合は No route to host の切り分け を参照。本記事は Connection timed out を扱う。

refused は「ホストは生きているがポートが拒否した」、timed out は「そもそも誰も返事をしない」。前者は RST という明確な返事があり、後者は沈黙。沈黙は DROP(破棄)の特徴で、ファイアウォールが -j DROP で捨てているか、相手が居ない。

なぜ「Connection timed out」は起きるのか?

結論: 原因は 4 つに集約される。①ファイアウォールの DROP(最頻)、②クラウドのセキュリティグループ未開放、③相手ホストの停止・ポート未待受、④経路途中のブラックホール。自分のホストから相手へ向かって順に切り分ける。

ETIMEDOUT を生む経路を原因別に整理する。

原因 何が起きているか 最初に見る場所
ファイアウォール DROP サーバ / 経路上の機器が SYN を黙って破棄 nft list ruleset
セキュリティグループ未開放 クラウドの SG / NACL が該当ポートを許可していない クラウド管理画面
相手ホスト停止・未待受 サーバ自体が落ちている / サービスが起動していない サーバ側 ss -ltn
経路途中のブラックホール ルータ設定ミス / VPN・ルーティングの穴でパケット消失 traceroute / mtr

切り分けは自分のホストに近い層から外側へ進める。まず応答時間で timed out だと確定し、次に経路のどこまで届くか、最後にサーバ側とファイアウォールを見る。

DROP と REJECT は対照的。DROP はパケットを黙って捨てるため client は無応答で timed out になる。REJECT は ICMP や RST を返すため No route to hostrefused になる。つまり「長い沈黙=DROP」「即エラー=REJECT」と、症状からファイアウォールの捨て方が逆算できる。

最初に何を確認するのか?(応答時間を測る)

結論: curl --connect-timeoutnc -w で待ち時間を区切り、接続だけを試す。指定秒数いっぱい待ってから失敗するなら無応答(timed out)が確定する。デフォルトの長いタイムアウトを待つ必要はない。

まず短いタイムアウトを明示して、接続フェーズだけを試す。デフォルト(curl は約 2 分)を待つのは時間の無駄。

# 接続確立だけに 5 秒の制限をかける
$ curl -sS --connect-timeout 5 http://192.0.2.10/
curl: (28) Failed to connect to 192.0.2.10 port 80 after 5001 ms: Connection timed out

nc(netcat)なら到達確認をさらに端的に行える。

$ nc -vz -w3 192.0.2.10 80
nc: connect to 192.0.2.10 port 80 (tcp) failed: Connection timed out

指定した秒数いっぱい(上では 3 秒)待ってから失敗したら、相手は SYN に何も返していない。即座に refused と出たならポートは閉じているがホストは応答しており、これは timed out ではない。

--connect-timeout接続確立までの制限で、-m--max-time)は通信全体の制限。切り分けでは接続フェーズだけを見たいので --connect-timeout を使う。nc -w のタイムアウトも同様に接続待ちを区切る。

経路のどこで止まっているのか?(traceroute / mtr)

結論: traceroute / mtr で、自分のホストから相手まで ICMP/UDP/TCP がどのホップまで届くかを見る。特定のホップから先がすべて * * * になる地点が、パケットが消えている(DROP されている)境界。

経路上のどこでパケットが消えているかを可視化する。

$ traceroute -n 192.0.2.10
 1  10.0.0.1    0.4 ms   0.3 ms   0.3 ms
 2  100.64.0.1  1.2 ms   1.1 ms   1.0 ms
 3  * * *
 4  * * *

ホップ 2 までは応答があり、3 以降がすべて * * * なら、その境界でパケットが黙って捨てられている。ここから先がブラックホールで、ファイアウォールやルーティングの穴が疑わしい。

ただし traceroute は ICMP/UDP を使うため、TCP は通るが ICMP だけ遮断されている環境では誤って「途中で止まる」ように見える。実際に接続したいポートで試すには TCP モードを使う。

# 宛先ポート 80 へ TCP SYN で経路を辿る
$ sudo traceroute -T -p 80 -n 192.0.2.10

連続して状態を見たいときは mtr が便利。

$ mtr -n -T -P 80 192.0.2.10

最終ホップだけが * * * で手前まで届く場合、相手のファイアウォールが ICMP は落とすが TCP は通すケースもある。「traceroute が途中で切れる」だけで DROP と断定せず、必ず接続したいプロトコル・ポート-T -p)でも確認する。

SYN に応答が無いことをどう確認するのか?(tcpdump)

結論: tcpdump で実際のパケットを観測すると、SYN を送っているのに SYN+ACK が返っていないことが直接見える。client 側で「送るだけで返事が無い」、server 側で「SYN すら届いていない」のどちらかで、DROP の位置が絞れる。

確証が欲しければパケットを直接見る。client 側でキャプチャしながら接続を試す。

# 別端末で実行してから接続を試す
$ sudo tcpdump -ni any host 192.0.2.10 and port 80
10:00:00.100 IP 10.0.0.42.51000 > 192.0.2.10.80: Flags [S], seq 1, ...
10:00:01.100 IP 10.0.0.42.51000 > 192.0.2.10.80: Flags [S], seq 1, ...
10:00:03.100 IP 10.0.0.42.51000 > 192.0.2.10.80: Flags [S], seq 1, ...

Flags [S](SYN)だけが等間隔(1s, 2s, 4s…)で再送され、Flags [S.](SYN+ACK)が返っていない。これが timed out の正体で、相手まで届いていないか、届いても黙殺されている。

もしサーバにログインできるなら、サーバ側でも同時にキャプチャすると決定的になる。

# サーバ側で実行
$ sudo tcpdump -ni any tcp port 80
  • サーバ側に SYN が届いていない → 経路上(途中のルータ / クラウド SG)で DROP
  • サーバ側に SYN は届くが応答しない → サーバのローカルファイアウォール DROP か、ポート未待受

SYN の再送間隔(約 1, 2, 4, 8 秒…の指数バックオフ)が見えたら、それ自体が「無応答」の動かぬ証拠。再送回数は net.ipv4.tcp_syn_retries(既定 6)で決まり、これが client 側の総待ち時間を左右する。

ファイアウォール DROP をどう見極めるのか?

結論: timed out かつ経路が相手まで届いているなら、-j DROP 系のルールが最有力。サーバ側で nft list ruleset / iptables -L を確認し、該当ポートが許可されているかを見る。クラウドでは OS のファイアウォールより手前のセキュリティグループが原因のことが多い。

サーバ側でフィルタリングルールを確認する。

# nftables のルールを確認
$ sudo nft list ruleset | grep -i -E 'drop|policy'

# iptables の場合
$ sudo iptables -L -n -v --line-numbers
Chain INPUT (policy DROP)
pkts bytes target  prot  ...  destination
 ...  ...  ACCEPT  tcp   ...  tcp dpt:22

policy DROP で、許可されているのが 22 番だけなら、80 番への SYN は黙って捨てられ client は timed out になる。ufw を使っているなら許可状況を確認する。

$ sudo ufw status verbose

ufw で SSH まで巻き込んで締め出した場合の復旧は ufw でSSHが繋がらない時 を参照。

クラウド環境(AWS / GCP / Azure 等)では、OS のファイアウォールより手前のセキュリティグループ / ネットワーク ACL が DROP していることが圧倒的に多い。 nft list ruleset が空でも timed out なら、まずクラウド側の SG で該当ポート(と送信元 IP レンジ)が許可されているかを管理画面で確認する。SG の既定は「受信全拒否(= DROP 相当)」。

サーバ側で待ち受けているか?

結論: ファイアウォールが開いていても、サービスが起動していなければ繋がらない。サーバ側で ss -ltn を見て、該当ポートが LISTEN しているか、待受アドレスが 127.0.0.1 に閉じていないかを確認する。

サーバにログインできるなら、ポートが待ち受けているかを直接見る。

$ ss -ltn
State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port
LISTEN  0       128         127.0.0.1:80          0.0.0.0:*
LISTEN  0       128           0.0.0.0:22          0.0.0.0:*

ここで 80 番が 127.0.0.1:80 で待ち受けていると、ローカルからしか接続できず外部からは届かない(ファイアウォールを開けても無駄)。0.0.0.0:80(全インターフェース)または該当 IP で待ち受けるよう、サービスの bind 設定を直す。

該当ポートが一覧に無いなら、サービスが起動していない。起動状態を確認する。

$ systemctl status nginx

127.0.0.1 待受は timed out ではなく即 refused になるのが本来だが、その前段のファイアウォールが DROP していると症状が timed out で覆い隠される。「ファイアウォールを開けたのに繋がらない」ときは bind アドレスを必ず疑う。

まだ繋がらないときのチェックリスト

結論: timed out は「無応答」を意味する。自分のホストから外側へ、応答時間 → 経路 → SYN 応答 → ファイアウォール → 待受、の順に追えば、原因は DROP・SG・サービス停止・経路ブラックホールのいずれかに収束する。

  • [ ] 失敗まで数秒以上かかったか(即エラーなら refused / No route to host)
  • [ ] curl --connect-timeout / nc -w で接続フェーズだけを区切って確認したか
  • [ ] traceroute -T -p <port> で、接続したいポートでも経路を辿ったか
  • [ ] どのホップから先が * * * になるか(DROP 境界)を特定したか
  • [ ] tcpdump で SYN が再送され SYN+ACK が返らないことを確認したか
  • [ ] サーバ側にも SYN が届いているか(届かない=経路 / SG、届く=ローカル FW / 未待受)
  • [ ] nft list ruleset / iptables -L に該当ポートを落とす DROP が無いか
  • [ ] クラウドのセキュリティグループ / NACL で該当ポートと送信元が許可されているか
  • [ ] ss -ltn で該当ポートが 0.0.0.0(外部公開)で LISTEN しているか

次に読む