【実務・中級編】 TCPのFIN/RSTフラグによる接続終了と強制切断の挙動 – ネットワーク基礎とWebセキュリティ実践ガイド

現場のエンジニアに捧ぐ:TCPの「終了」を見極める――FINとRSTの境界線

ネットワークエンジニアとして夜通しトラブルシューティングをしていると、パケットキャプチャの画面が真っ黒に見えてくる瞬間があります。そんな時、一番厄介なのが「なぜか繋がらない」「なぜか切れる」という曖昧な挙動です。

Web APIの設計やインフラ運用において、TCPの終了シーケンスは単なる「おまけ」ではありません。ここを理解していないと、ロードバランサーのアイドルタイムアウトで泣きを見ることになります。今日は、綺麗に幕を引くFINと、問答無用にドアを蹴破るRSTの話をしましょう。

—

1. 礼儀正しい別れ:FINによる正常終了(4ウェイ・ハンドシェイク)

TCP接続の終了は、通信の両端が「もう送るデータはないよ」と合意するプロセスです。これを「4ウェイ・ハンドシェイク」と呼びます。

1. FIN送信: 終了したい側(Active Close側)がFINフラグを立てます。
2. ACK応答: 受け取った側はACKを返し、自分も送るものがないか確認します。
3. FIN送信: 準備ができたら、もう片側もFINを送ります。
4. ACK応答: 最後にACKを返してクローズ完了です。

このプロセスが正しく行われると、SocketはTIME_WAIT状態に入ります。これは「まだネットワーク上に迷子のパケットがいるかもしれないから、しばらく同じポートの再利用を待つね」という、ネットワークの安全を守るための賢い猶予期間です。

—

2. 暴力的な強制切断:RSTフラグの正体

現場で最も嫌われるのが、このRST(Reset)フラグです。これは「会話の途中だけど、もうお前とはやってられない!」という通信の強制破棄を意味します。

なぜRSTが飛ぶのか?

多くの場合、以下のようなケースで発生します。

  • ポートが閉まっている: サーバー側で該当ポートが待ち受け状態ではない。
  • 異常なパケット到着: 確立されていないセッションに対するパケットが届いた。
  • タイムアウト: ロードバランサーが「こいつ、長居しすぎだ」と判断してセッションをブチ切る。
  • バッファオーバーフロー: 処理しきれないデータが届き、通信を継続不能と判断する。

特に、Web API開発で「Connection reset by peer」というエラーに出くわしたら、それはほぼ間違いなく通信経路上やサーバー側でRSTが発行されています。

—

3. 実践:デバッグと検証の現場から

実務で遭遇する「謎の切断」を切り分けるためのTipsを紹介します。

curlでFINとRSTを観察する

curlを使って、特定のサーバーへの接続を観察してみましょう。-v(verbose)オプションは必須です。

# -v でヘッダーのやり取りと接続状況を確認する
curl -v https://api.example.com

もしここで Recv failure: Connection reset by peer と表示されたら、サーバー側のファイアウォール(iptables/nftables)や、手前のWAFが接続を拒否している可能性が高いです。

PythonでRSTを「意図的に」発生させる

実験として、受信バッファを無視して即座にクローズすることでRSTを誘発する例です。

import socket

# ソケットを作成
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 接続
s.connect(('127.0.0.1', 80))
# SO_LINGERオプションでタイムアウトを0に設定して強制終了を誘発
# これにより、FINではなくRSTが投げられる
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, b'\x01\x00\x00\x00\x00\x00\x00\x00')
s.close()

Nginxでの設定ポイント

インフラ運用でよくある「意図しない切断」を防ぐには、keepalive_timeoutの調整が肝です。バックエンドのAPIサーバーが処理に時間がかかる場合、Nginx側で早く切りすぎるとクライアントにRSTが届きます。

# nginx.conf の設定例
http {
    # クライアントとの接続を維持する時間。短すぎると頻繁な切断を招く
    keepalive_timeout 65s; 
    
    # 接続がタイムアウトした際に、即座にRSTを投げるかどうかの制御
    # reset_timedout_connection on;
}

—

4. 最後に:パケットは嘘をつかない

トラブルシューティングの極意は「ログを信じるな、パケットを見ろ」です。アプリケーションのログには「接続エラー」としか出なくても、tcpdumpやWiresharkでRSTフラグの有無を確認すれば、それが「通信の行き止まり」なのか「強制排除」なのかが一目瞭然になります。

エンジニアとして、このTCPの「作法」を知っているかどうかで、障害復旧のスピードは劇的に変わります。次にAPIが切断された時は、ぜひパケットの中身にフラグを立ててみてください。きっと、ネットワークの向こう側の意図が、少しだけ見えるはずです。

それでは、また次回の深掘り記事でお会いしましょう。現場からは以上です。

コメント

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