hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

TCPの各種状態の作り方



1 はじめに

TCPは以下の状態があります。こここでは、ncコマンド、iptablesコマンドを使って、TCPの各種状態を作ってみます。

状態 意味
LISTEN プロセスが指定したポートでTCPパケットの到着を待っている状態です
ESTABLISHED 3 Way Handshake(SYN,SYN+ACK,ACKのやり取り)が完了した状態です
SYN-SENT SYNを送信して相手からSYN+ACKが返ってくるまでの状態です
SYN-RECEIVED SYN+ACKを送信して、相手からACKが返ってくるまでの状態です
FIN-WAIT-1 アクティブクローズ側がとる状態です。FINを送信して相手からACKが返ってくるまでの状態です
FIN-WAIT-2 アクティブクローズ側がとる状態です。FINを送信して相手からFIN+ACKを受信したあと、相手からFINを受信するまでの状態です
CLOSE-WAIT アプリケーションからTCPコネクションのクローズを待っている状態です。closeシステムコールを実行すると、コネクションをクローズします
LAST-ACK パッシブクローズ側がとる状態です。こちらから相手にFINを送信して、相手からACKを受け取るまでの状態です
TIME-WAIT TIME-WAITは、アクティブクローズ側がとる状態です。相手のFINに対してこちらが送信したACKを、相手が確実に受信できるようにするための状態です
CLOSING アクティブクローズ側がとる状態です。同時クローズをしたときに発生します。FIN-WAIT-1のときに、相手からFINを受信するとCLOSINGになります
CLOSED TCPコネクションが存在しない状態です

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの2台構成です。図中のenp1s0はNICの名前です。

                               192.168.122.0/24
client(enp1s0) ------------------------------------------(enp1s0) server
            .177                                       .68

2.2 版数

サーバとクライアントのAlmaLinuxの版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 8.6 (Sky Tiger)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
4.18.0-372.9.1.el8.x86_64

3 TCPの状態遷移

TCPのコネクション確立/開放時における、TCPの状態遷移を以下に示します。listen(),connect(),close()はシステムコールを表しています。11111はTCPのポート番号を表しています。

                   client                              server(11111)
                      |                                   |
                 |    |                                   |    |
                 |    |                                   |  CLOSED
                 |    |                                   |    |
                 |    |                                   |   -*-   <=== listen()
              CLOSED  |                                   |    |
                 |    |                                   |  LISTEN
                 |    |                                   |    |
 connect() ===> -*-   |-------------- SYN --------------->|   -*-
                 |    |                                   |    |
                 |    |                                   |    |
            SYN-SENT  |                                   |    |
                 |    |                                   |  SYN-RECEIVED
                 |    |                                   |    |
                 |    |                                   |    |
                 |    |<------------- SYN + ACK ----------|    |
                -*-   |-------------- ACK --------------->|   -*-
                 |    |                                   |    |
                 |    |                                   |    |
                 |    |                                   |    |
         ESTABLISHED  |                                   |  ESTABLISHED
                 |    |                                   |    |
                 |    |                                   |    |
                 |    |                                   |    |
   close() ===> -*-   |-------------- FIN --------------->|   -*-
                 |    |                                   |    |
                 |    |                                   |    |
          FIN-WAIT-1  |                                   |    |
                 |    |                                   |    |
                 |    |                                   |    |
                -*-   |<------------- ACK ----------------|  CLOSE-WAIT
                 |    |                                   |    |
                 |    |                                   |    |
          FIN-WAIT-2  |                                   |    |
                 |    |                                   |    |
                 |    |                                   |    |
                 |    |<------------- FIN ----------------|   -*-   <=== close()
                 |    |                                   |    |
                 |    |                                   |    |
                 |    |                                   |   LAST-ACK
                 |    |                                   |    |
                 |    |                                   |    |
                -*-   |-------------- ACK --------------->|   -*-
                 |    |                                   |    |
                 |    |                                   |    |
           TIME-WAIT  |                                   |    |
                 |    |                                   |    |
                 |    |                                   |    |
                -*-   |                                   |   CLOSED
                 |    |                                   |    |
                 |    |                                   |    |
              CLOSED  |                                   |    |
                 |    |                                   |    |

4 LISTEN状態の作り方

サーバでncコマンドを実行します。このとき、ncプロセスがTCPの11111番ポートでListenするようにします。なお、ncコマンドのインストール方法、使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# nc -kl 11111

もう1つターミナルを開いて、ssコマンドを実行します。ソケットの状態を確認すると、TCPの状態(State列)がLISTEN状態であることがわかります。なお、ssコマンドのインストール方法、使い方は、ssコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# ss -an4 'sport == :11111'
Netid        State         Recv-Q        Send-Q                Local Address:Port                  Peer Address:Port        Process
tcp          LISTEN        0             1                           0.0.0.0:11111                      0.0.0.0:*

5 ESTABLISHED状態の作り方

3 Way HandShakeを実行してTCPコネクションを確立すると、TCPの状態がESTABLISHEDになります。ESTABLISHEDになると、ユーザデータのやり取りが可能になります。なお、TCPコネクションの確立、解放シーケンスの詳細は、TCPコネクションの確立、解放シーケンス - hana_shinのLinux技術ブログを参照してください。

             client                              server(11111)
                |                                   |
      connect() |-------------- SYN --------------->|
                |<------------- SYN + ACK ----------|
         -*-    |-------------- ACK --------------->|   -*-
          |     |                                   |    |
    ESTABLISHED |                                   | ESTABLISHED
          |     |                                   |    |

サーバでncコマンドを実行します。このとき、TCPの11111番ポートでListenします。

[root@server ~]# nc -kl 11111

サーバでssコマンドを実行します。ソケットの状態を確認すると、TCPがLISTEN状態であることがわかります。

[root@server ~]# ss -an4 'sport == :11111'
Netid        State         Recv-Q        Send-Q                Local Address:Port                  Peer Address:Port        Process
tcp          LISTEN        0             1                           0.0.0.0:11111                      0.0.0.0:*

クライアントからサーバの11111番ポートにアクセスできるようにするため、TCPの11111番ポートを解放します。なお、firewall-cmdの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# firewall-cmd --add-port=11111/tcp
success

クライアントでncコマンドを実行します。このとき、サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

サーバでssコマンドを実行します。ソケットの状態を確認すると、TCPがESTABLISHED状態であることがわかります。

[root@server ~]# ss -n4 'sport == :11111'
Netid        State        Recv-Q        Send-Q                Local Address:Port                   Peer Address:Port        Process
tcp          ESTAB        0             0                    192.168.122.68:11111               192.168.122.177:42842

次に、クライアントでssコマンドを実行します。ソケットの状態を確認すると、TCPがESTABLISHED状態であることがわかります

[root@client ~]# ss -n4 'dport == :11111'
Netid          State          Recv-Q          Send-Q                       Local Address:Port                        Peer Address:Port           Process
tcp            ESTAB          0               0                          192.168.122.177:42842                     192.168.122.68:11111

6 SYN-SENT状態の作り方

SYN-SENTは、SYNを送信して相手からSYN+ACKが返ってくるまでの状態です。ここでは、クライアントのINPUTチェインにSYN+ACKを廃棄(★)する設定をして、SYN-SENTの状態を作りだしてみます。SYN+ACKを廃棄する理由は、SYN+ACKを破棄せず受信してしまうと、クライアントがACKを送信してTCPの状態がESTABLISHEDになってしまうからです。他の方法として、クライアントのOUTPUTチェイン、または、サーバのINPUTチェインでSYNを破棄しても、同じ結果が得られます。

             client                              server(11111)
                |                                   |
 connect() -*-  |-------------- SYN --------------->|
            |   |                                   |
            |   |                                   |
        SYN-SENT|                                   |
            |   |                                   |
            |   ★ <----------- SYN + ACK ----------|

6.1 作成手順

クライアントでiptablesコマンドを実行します。このとき、サーバからのSYN+ACKパケットを破棄します。

[root@client ~]# iptables -I INPUT -p tcp --sport 11111 --tcp-flags SYN,ACK SYN,ACK -j DROP

設定したルールを確認します。

[root@client ~]# iptables -nvL INPUT 1
    0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp spt:11111 flags:0x12/0x12

サーバでncコマンドを実行します。このとき、ncプロセスがTCPの11111番ポートでListenするようにします。

[root@server ~]# nc -kl 11111

サーバでssコマンドを実行します。ソケットの状態を確認すると、TCPがLISTEN状態であることがわかります。

[root@server ~]# ss -nat4 'sport == :11111'
State     Recv-Q     Send-Q         Local Address:Port          Peer Address:Port    Process
LISTEN    0          1                    0.0.0.0:11111              0.0.0.0:*

クライアントでncコマンドを実行します。このとき、サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

クライントでssコマンドを実行します。ソケットの状態を確認すると、TCPがSYN-SENT状態であることがわかります。

[root@client ~]# ss -nat4 'dport == :11111'
State          Recv-Q       Send-Q               Local Address:Port                Peer Address:Port       Process
SYN-SENT       0            1                  192.168.122.177:44500             192.168.122.68:11111

6.2 後始末(iptablesの設定削除)

INPUTチェインに登録されているルールを確認します。ルールが1つ登録されていることがわかります。

[root@client ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 5544 packets, 20M bytes)
num   pkts bytes target     prot opt in     out     source               destination
1       12   720 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp spt:11111 flags:0x12/0x12

INPUTチェインの1番目のルールを削除します。

[root@client ~]# iptables -D INPUT 1

INPUTチェインに登録されているルールを確認します。INPUTチェインの1番目のルールが削除されたことがわかります。

[root@client ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 5576 packets, 20M bytes)
num   pkts bytes target     prot opt in     out     source               destination

7 SYN-RECEIVED状態の作り方

SYN-RECEIVEDは、SYN+ACKを送信して、相手からACKが返ってくるまでの状態です。ここでは、サーバのINPUTチェインでACKパケットを廃棄(下記★印)することで、サーバでSYN-RECEIVEDの状態を作りだしてみます。他の方法として、クライアントのINPUTチェインでSYN+ACK、またはOUTPUTチェインでACKを破棄しても、同じ結果が得られます。

             client                              server(11111)
                |                                   |
 connect() -*-  |-------------- SYN --------------->|   -*-
            |   |                                   |    |
            |   |                                   |    |
        SYN-SENT|                                   | SYN-RECEIVED
            |   |                                   |    |
            |   |<------------- SYN + ACK ----------|    |
           -*-  |-------------- ACK --------------->★
                |                                   |

7.1 作成手順

サーバでiptablesコマンドを実行します。このとき、クライアントからのACKパケットを破棄します。

[root@server ~]# iptables -I INPUT -p tcp --dport 11111 --tcp-flags ACK ACK -j DROP

設定したルールを確認します。

[root@server ~]# iptables -nvL INPUT 1
    0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:11111 flags:0x10/0x10

サーバでncコマンドを実行します。このとき、TCPの11111番ポートでListenします。

[root@server ~]# nc -kl 11111

サーバでssコマンドを実行します。ソケットの状態を確認すると、TCPがLISTEN状態であることがわかります。

[root@server ~]# ss -nat4 'sport == :11111'
State     Recv-Q     Send-Q         Local Address:Port          Peer Address:Port    Process
LISTEN    0          1                    0.0.0.0:11111              0.0.0.0:*

クライアントでncコマンドを実行します。このとき、サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

クライントでssコマンドを実行します。ソケットの状態を確認すると、TCPがSYN-RECV状態であることがわかります。

[root@server ~]# ss -nat4 'sport == :11111'
State       Recv-Q    Send-Q        Local Address:Port           Peer Address:Port    Process
LISTEN      0         1                   0.0.0.0:11111               0.0.0.0:*
SYN-RECV    0         0            192.168.122.68:11111       192.168.122.177:44538

7.2 後始末(iptablesの設定削除)

INPUTチェインに登録されているルールを確認します。ルールが1つ登録されていることがわかります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 6533 packets, 26M bytes)
num   pkts bytes target     prot opt in     out     source               destination
1       16   832 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:11111 flags:0x10/0x10

INPUTチェインの1番目のルールを削除します。

[root@server ~]# iptables -D INPUT 1

INPUTチェインに登録されているルールを確認します。INPUTチェインの1番目のルールが削除されたことが分かります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 6577 packets, 26M bytes)
num   pkts bytes target     prot opt in     out     source               destination

8 FIN-WAIT-1状態の作り方

アクティブクローズ側(*)がとる状態です。FINを送信して相手からACKが返ってくるまでの状態です。TCPコネクション確立後、クライアントでCtrl + cを押下して、ncコマンドを終了します。ncコマンドを終了すると、クライアンからサーバにFINが送信されます。このとき、サーバのINPUTチェインでFINを廃棄(下記★印)して、クライアントでFIN-WAIT-1の状態を作りだしてみます。
(*) TCPコネクションが確立した状態で、先にclose()を実行してFINパケットを送信した側です。

             client                              server(11111)
          |     |                                   |    |
          |     |                                   |    |
    ESTABLISHED |                                   | ESTABLISHED
          |     |                                   |    |
          |     |                                   |    |
Ctrl + c -*-    |-------------- FIN -------------->★   -*- 
          |     |                                   |
          |     |                                   |
     FIN-WAIT-1 |                                   |
          |     |                                   |
          |     |                                   |

8.1 作成手順

サーバでiptablesコマンドを実行します。このとき、クライアントからのFINパケットを破棄します。

[root@server ~]# iptables -I INPUT -p tcp --dport 11111 --tcp-flags FIN FIN -j DROP

設定したルールを確認します。

[root@server ~]# iptables -nvL INPUT 1
    0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:11111 flags:0x01/0x01

サーバでncコマンドを実行します。このとき、TCPの11111番ポートでListenします。

[root@server ~]# nc -kl 11111

クライアントでncコマンドを実行します。このとき、サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

クライントでssコマンドを実行します。ソケットの状態を確認すると、TCPがESTABLISHED状態であることがわかります。

[root@client ~]# ss -nat4 'dport == :11111'
State       Recv-Q       Send-Q               Local Address:Port                  Peer Address:Port        Process
ESTAB       0            0                  192.168.122.177:44540               192.168.122.68:11111

クライアンでCtrl +cを押下して、ncプロセスを終了します。Ctrl +cを押下することで、クライアントからFINが送信されます。なお、FINの送信は、tcpdumpコマンド、tsharkコマンドを使って確認することができます。tcpdumpコマンドは、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログ、tsharkコマンドは、tsharkコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# nc 192.168.122.68 11111
^C

クライントでssコマンドを実行します。ソケットの状態を確認すると、TCPがFIN-WAIT-1状態であることがわかります。

[root@client ~]# ss -nat4 'dport == :11111'
State           Recv-Q       Send-Q               Local Address:Port                Peer Address:Port       Process
FIN-WAIT-1      0            1                  192.168.122.177:44540             192.168.122.68:11111

8.2 後始末(iptablesの設定削除)

INPUTチェインに登録されているルールを確認します。ルールが1つ登録されていることがわかります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 6659 packets, 26M bytes)
num   pkts bytes target     prot opt in     out     source               destination
1       10   520 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:11111 flags:0x01/0x01

INPUTチェインの1番目のルールを削除します。

[root@server ~]# iptables -D INPUT 1

INPUTチェインに登録されているルールを確認します。INPUTチェインの1番目のルールが削除されたことが分かります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 6680 packets, 26M bytes)
num   pkts bytes target     prot opt in     out     source               destination

9 FIN-WAIT-2状態の作り方

正常なTCPコネクションの開放シーケンスは以下のようになります。クライアントでCtrl + cを押下してncプロセスを終了するとFINが送信されます。そのあと、サーバのncプロセスが送信するデータがなければ、close()を実行してTCPコネクションをクローズします。このとき、サーバからクライアントにFINを送信します。

             client                              server(11111)
          |     |                                   |    |
          |     |                                   |    |
    ESTABLISHED |                                   | ESTABLISHED
          |     |                                   |    |
          |     |                                   |    |
          |     |                                   |    |
Ctrl + c -*-    |-------------- FIN --------------->|    |
          |     |                                   |    |
     FIN-WAIT-1 |                                   |    |
          |     |                                   |    |
         -*-    |<------------- ACK ----------------|   -*-
          |     |                                   |    |
          |     |                                   |    |
     FIN-WAIT-2 |                                   | CLOSE_WAIT
          |     |                                   |    |
          |     |                                   |    |
          |     |<------------- FIN ----------------|   -*-  <--- close()
          |     |                                   |    |
          |     |                                   |    |
          |     |                                   |  LAST_ACK
          |     |                                   |    |
          |     |                                   |    |
         -*-    |-------------- ACK --------------->|   -*-
          |     |                                   |    |
          |     |                                   |    |

次に、意図的にサーバのncプロセスがclose()を実行しない状況を作ってみます。
TCPコネクションが確立した後、サーバのncプロセスをCtrl+zを押下して停止します。次に、クライアントでCtrl+cを押下します。Ctrl+cを押下するとFINがサーバに送信されますが、ncプロセスが停止しているため、close()が実行されません。そのため、サーバからクライアントにFINが送信されず、クライアントはFIN-WAIT-2の状態のままになります。なお、このとき、サーバはCLOSE-WAIT状態となっています。

             client                              server(11111)
          |     |                                   |    |
          |     |                                   |    |
    ESTABLISHED |                                   | ESTABLISHED
          |     |                                   |    |
          |     |                                   |    | Ctrl + z
          |     |                                   |    |
Ctrl + c -*-    |-------------- FIN --------------->|    |
          |     |                                   |    |
     FIN-WAIT-1 |                                   |    |
          |     |                                   |    |
         -*-    |<------------- ACK ----------------|   -*-
          |     |                                   |    |
          |     |                                   |    |
     FIN-WAIT-2 |                                   | CLOSE_WAIT
          |     |                                   |    |
          |     |                                   |    |
          |     |                                   |    | Can't execute close()
          |     |                                   |    |

9.1 作成手順

サーバでncコマンドを実行します。このとき、TCPの11111番ポートでListenします。

[root@server ~]# nc -kl 11111

クライアントでncコマンドを実行します。このとき、サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

クライントでssコマンドを実行します。ソケットの状態を確認すると、TCPがESTABLISHED状態であることがわかります。

[root@client ~]# ss -nat4 'dport == :11111'
State    Recv-Q     Send-Q           Local Address:Port            Peer Address:Port     Process
ESTAB    0          0              192.168.122.177:46320         192.168.122.68:11111

サーバでCtrl + zを押下して、ncプロセスを停止します。ncプロセスは停止しているので、closeシステムコールを実行できません。

[root@server ~]# nc -kl 11111
^Z
[1]+  停止                  nc -kl 11111

サーバでpsコマンドを実行します。S列がTとなっているので、ncプロセスが停止していることがわかります。なお、psコマンドの使い方は、psコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# ps -C nc -o comm,pid,state
COMMAND             PID S
nc                 1612 T

クライアンでCtrl +cを押下して、ncコマンドを終了します。Ctrl +cを押下することで、クライアントからサーバに向けてFINが送信されます。

[root@client ~]# nc 192.168.122.68 11111
^C

クライントでssコマンドを実行します。ソケットの状態がFIN-WAIT-2状態であることがわかります。

[root@client ~]# ss -nat4 'dport == :11111'
State         Recv-Q    Send-Q         Local Address:Port          Peer Address:Port     Process
FIN-WAIT-2    0         0            192.168.122.177:46320       192.168.122.68:11111

10 CLOSE-WAIT状態の作り方

アプリケーションからTCPコネクションのクローズを待っている状態です。closeシステムコールを実行すると、コネクションをクローズすることができます。バグなどによって、closeシステムコールが実行されないと、CLOSE-WAITの状態が残ったままになります。

             client                              server(11111)
          |     |                                   |    |
          |     |                                   |    |
    ESTABLISHED |                                   | ESTABLISHED
          |     |                                   |    |
          |     |                                   |    | Ctrl + z ...(1)
(2)       |     |                                   |    |
Ctrl + c -*-    |-------------- FIN --------------->|   -*- 
          |     |                                   |    |
          |     |                                   |    |
     FIN-WAIT-1 |                                   |    |
          |     |                                   |    |
          |     |                                   |    |
         -*-    |<---------- FIN + ACK -------------|  CLOSE-WAIT  <--- close()
          |     |                                   |    |
          |     |                                   |    |
     FIN-WAIT-2 |                                   |    |
          |     |                                   |    |
          |     |                                   |    |

10.1 作成手順

サーバでncコマンドを実行します。このとき、TCPの11111番ポートでListenします。

[root@server ~]# nc -kl 11111

クライアントでncコマンドを実行します。このとき、サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

サーバでCtrl + zを押下して、ncプロセスを停止します。ncプロセスは停止しているので、closeシステムコールを実行できません。

[root@server ~]# nc -kl 11111
^Z
[1]+  停止                  nc -kl 11111

サーバでpsコマンドを実行します。S列がTとなっているので、ncプロセスが停止していることがわかります。

[root@server ~]# ps -C nc -o comm,pid,state
COMMAND             PID S
nc                 1657 T

クライアンでCtrl +cを押下して、ncコマンドを終了します。Ctrl +cを押下することで、クライアントからサーバに向けてFINが送信されます。

[root@client ~]# nc 192.168.122.68 11111
^C

サーバでssコマンドを実行します。ソケットの状態がCLOSE-WAIT状態であることがわかります。

[root@server ~]# ss -nat4 'sport == :11111'
State         Recv-Q    Send-Q        Local Address:Port           Peer Address:Port     Process
LISTEN        0         1                   0.0.0.0:11111               0.0.0.0:*
CLOSE-WAIT    1         0            192.168.122.68:11111       192.168.122.177:46324

11 LAST-ACK状態の作り方

パッシブクローズ側(*)がとる状態です。こちらから相手にFINを送信(下記1)して、相手からACKを受け取る(下記2)までの状態です。
(*)TCPコネクションが確立した状態で、あとからclose()を実行する側です。

             client                              server(11111)
          |     |                                   |    |
          |     |                                   |    |
    ESTABLISHED |                                   | ESTABLISHED
          |     |                                   |    |
          |     |                                   |    |
          |     |                                   |    |
Ctrl + c -*-    |-------------- FIN --------------->|   -*- 
          |     |                                   |    |
          |     |                                   |    |
     FIN-WAIT-1 |                                   |    |
          |     |                                   |    |
          |     |                                   |    |
         -*-    |<------------- ACK ----------------|  CLOSE-WAIT
          |     |                                   |    |
          |     |                                   |    |
     FIN-WAIT-2 |                                   |    |
          |     |                                   |    |
          |     |                                   |    |
          |     |<------------- FIN ----------------|   -*-   <===(1)
          |     |                                   |    |
          |     |                                   |    |
          |     |                                   |  LAST-ACK
          |     |                                   |    |
          |     |                                   |    |
         -*-    |-------------- ACK ------------->  |   -*-   <===(2)
          |     |                                   |    |
          |     |                                   |  CLSOED
          |     |                                   |    |

11.1 作成手順

サーバでncコマンドを実行します。このとき、TCPの11111番ポートでListenします。

[root@server ~]# nc -kl 11111

クライアントでncコマンドを実行します。このとき、サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

クライアントでiptablesコマンドを実行します。このとき、サーバからのFINパケットを破棄します。

[root@client ~]# iptables -I INPUT -p tcp --sport 11111 --tcp-flags FIN FIN -j DROP
[root@client ~]# iptables -nvL INPUT 1
    0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp spt:11111 flags:0x01/0x01

クライアンでCtrl +cを押下して、ncコマンドを終了します。Ctrl +cを押下することで、クライアントからFINが送信されます。

[root@client ~]# nc 192.168.122.68 11111
^C
[root@server ~]# ss -nat4 'sport == :11111'
State          Recv-Q       Send-Q              Local Address:Port                 Peer Address:Port       Process
LISTEN         0            1                         0.0.0.0:11111                     0.0.0.0:*
LAST-ACK       0            1                  192.168.122.68:11111             192.168.122.177:40098

11.2 後始末(iptablesの設定削除)

[root@client ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 12880 packets, 31M bytes)
num   pkts bytes target     prot opt in     out     source               destination
1       10   520 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp spt:11111 flags:0x01/0x01
[root@client ~]# iptables -D INPUT 1
[root@client ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 12916 packets, 31M bytes)
num   pkts bytes target     prot opt in     out     source               destination

12 TIME-WAIT状態の作り方

TIME-WAITは、アクティブクローズ側がとる状態です。相手のFINに対してこちらが送信したACKを、相手が確実に受信できるようにするための状態です。本検証環境では、TIME-WAITの状態は60秒間継続します。60秒経過すると、TIME-WAITからCLOSEDに状態遷移します。

12.1 作成手順

サーバでncコマンドを実行します。このとき、TCPの11111番ポートでListenします。

[root@server ~]# nc -kl 11111

サーバの11111番ポートにTCPコネクションを確立します。

[root@client ~]# nc 192.168.122.68 11111

クライアンでCtrl +cを押下して、ncプロセスを終了します。Ctrl +cを押下することで、クライアントからFINが送信されます。

[root@client ~]# nc 192.168.122.68 11111
^C

ncコマンドを終了した直後、クライアントでssコマンドを実行します。このとき、oオプションをつけて、TIME-WAIT状態の残り時間を表示してみます。TIME-WAIT状態の残り時間が57秒であることがわかります。

[root@client ~]# ss -no4 state time-wait
Netid            Recv-Q            Send-Q                           Local Address:Port                            Peer Address:Port             Process
tcp              0                 0                              192.168.122.177:44542                         192.168.122.68:11111             timer:(timewait,57sec,0)

クライアントでssコマンドを実行します。TIME-WAIT状態の残り時間が588ミリ秒であることがわかります。

[root@client ~]# ss -no4 state time-wait
Netid            Recv-Q            Send-Q                           Local Address:Port                            Peer Address:Port             Process
tcp              0                 0                              192.168.122.177:44542                         192.168.122.68:11111             timer:(timewait,588ms,0)

クライアントでssコマンドを実行します。TIME-WAIT状態のソケットが存在しないことがわかります。

[root@client ~]# ss -no4 state time-wait
Netid             Recv-Q             Send-Q                         Local Address:Port                           Peer Address:Port             Process

13 CLOSING状態の作り方

アクティブクローズ側がとる状態です。同時クローズをしたときに発生します。つまり、FIN-WAIT-1のときに、相手からFINを受信すると、CLOSINGになります。

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ