1 ECONNREFUSEDとECONNRESETとは?
エラー種別 | 意味 |
---|---|
ECONNREFUSED | TCPの状態がSYN-SENT状態のとき、RSTパケットを受信すると発生します(TCPの場合) |
ICMP port unreachableを受信すると発生します(UDPの場合) | |
ECONNRESET | TCPの状態がSYN-SENT以外のとき、RSTパケットを受信すると発生します。たとえば、TCPコネクション確立後、相手からRSTパケットを受信すると発生します |
2 検証環境
2.1 ネットワーク構成
サーバとクライアントの2台構成です。図中のeth0はNICの名前です。
192.168.122.0/24 client(eth0) ------------------------------------------(eth0) server .181 .216
3 検証
ECONNREFUSEDとECONNRESETがどのような時に発生するか、検証環境で確認してみます。
3.1 ECONNREFUSED(TCPの場合)
クライアントが、Listenしていないポート番号(TCP)に対してSYNパケットを送信すると、サーバがRSTパケットを送信します。このとき、クライアントでECONNREFUSEDが発生します。
client(192.168.122.181) server(192.168.122.216) | | | | | | nc server 11111 |--------------- SYN --------------------->| | | | | ECONNREFUSED |<-------------- RST ----------------------| | | | |
テストで使用するTCPの11111番ポートを開放します。なお、firewall-cmdの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# firewall-cmd --add-port=11111/tcp success
開放したポート番号を確認します。TCPの11111番ポートが開放されたことがわかります。
[root@server ~]# firewall-cmd --list-ports 11111/tcp
サーバでtcpdumpを実行します。このとき、TCPの11111番ポートのみをキャプチャします。なお、tcpdumpの使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# tcpdump -i eth0 port 11111 -nn
クライアントでncコマンドを実行します。このとき、サーバのIPアドレス、ポート番号(11111)を指定します。ncコマンドを実行すると、"Connection refused."が表示されることがわかります。なお、ncコマンドの使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。
[root@client ~]# nc 192.168.122.216 11111 Ncat: Connection refused.
tcpdumpの実行結果を確認すると、サーバからクライアントにRSTパケットが送信されていることがわかります。
[root@server ~]# tcpdump -i eth0 port 11111 -nn tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 19:49:04.276073 IP 192.168.122.181.45640 > 192.168.122.216.11111: Flags [S], seq 2464899466, win 29200, options [mss 1460,sackOK,TS val 2328460 ecr 0,nop,wscale 7], length 0 19:49:04.276148 IP 192.168.122.216.11111 > 192.168.122.181.45640: Flags [R.], seq 0, ack 2464899467, win 0, length 0
3.2 ECONNREFUSED(UDPの場合)
待ち受けしていないUDPポートにパケットを送信すると、サーバからICMPのICMP port unreachableが送信されます。
client(192.168.122.181) server(192.168.122.216) | | | | | | nc server 11111 |--------------- UDP datagram ------------>| | | | | ECONNREFUSED |<-------------- ICMP port unreachable ----| | | | |
tcpdumpを実行します。このとき、UDPの11111番ポート宛てとICMPパケットのみをキャプチャします。
[root@server ~]# tcpdump -i eth0 port 11111 or icmp -nn
ncコマンドを実行します。
[root@client ~]# nc -u 192.168.122.216 11111
次に、クライアントからサーバにUDPデータグラムを送信します。12345とタイプしたあとリターンキーを押下します。リターンを押下すると、"Connection refused."が表示されることがわかります。
[root@client ~]# nc -u 192.168.122.216 11111 12345 Ncat: Connection refused.
サーバからクライアントにICMP port unreachableが送信されていることがわかります。
[root@server ~]# tcpdump -i eth0 udp port 11111 or icmp -nn tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 20:46:54.778677 IP 192.168.122.181.59201 > 192.168.122.216.11111: UDP, length 6 20:46:54.778757 IP 192.168.122.216 > 192.168.122.181: ICMP 192.168.122.216 udp port 11111 unreachable, length 42
3.3 ECONNRESET
ECONNRESETは、SYN-SENT以外のときに、RSTパケットを受信すると発生します。ここでは、TCPコネクションが確立したあと、サーバからクライアントにRSTパケットを送信することで、ECONNRESETを発生させてみます。
client(192.168.122.181) server(192.168.122.216) | | | | nc -kl 11111 | | | | nc server 11111 |--------------- SYN --------------------->| |<-------------- SYN+ACK ------------------| |--------------- ACK --------------------->| -*- | | | | | | | | | TCPコネクション確立状態 | | | (ESTABLISHED状態) | | | ECONNRESET |<-------------- RST ----------------------| -*- | |
サーバでncコマンドを実行します。
[root@server ~]# nc -kl 11111
クライアントでncコマンドを実行します。ncコマンドを実行すると、サーバとクライアントの間でTCPコネクションが確立されます。詳細は、TCPコネクションの確立、解放シーケンス - hana_shinのLinux技術ブログを参照してください。
[root@client ~]# nc 192.168.122.216 11111
もう1つターミナルをオープンしてssコマンドを実行します。サーバとクライアント間でTCPコネクションが確立されたことがわかります。なお、ssコマンドの使い方は、ssコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
root@client ~]# ss -n state established 'dport == :11111' Netid Recv-Q Send-Q Local Address:Port Peer Address:Port tcp 0 0 192.168.122.181:41616 192.168.122.216:11111
サーバでCtrl+zを押下して、サーバのncプロセスを停止します。
[root@server ~]# nc -kl 11111 ^Z [1]+ 停止 nc -kl 11111
psコマンドを実行してプロセスの状態を確認します。ncプロセスが停止状態(T)であることがわかります。なお、psコマンドの使い方は、psコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# ps -C nc -o comm,pid,state COMMAND PID S nc 1467 T
クライアントからサーバにデータを送信します。改行コードも含めて6バイト送信します。
[root@client ~]# nc 192.168.122.216 11111 12345
サーバのncプロセスは停止しているので、カーネルの受信バッファからデータを読み出すことができません。ssコマンドを使ってそのときの様子を確認します。Recv-Qはカーネル受信バッファに残っている受信データのバイト数を表しています。したがって、6バイトのデータがカーネル受信バッファに残ったままになっていることがわかります。詳細はssコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# ss -tn4 'sport == :11111' State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 6 0 192.168.122.216:11111 192.168.122.181:41616
サーバのncプロセスを終了します。
[root@server ~]# kill -9 1467 [1]+ 強制終了 nc -kl 11111
カーネル受信バッファに受信データが残ったままプロセスが終了すると、データ送信元にRSTパケットが送信されます。
[root@server ~]# tcpdump -i eth0 tcp port 11111 -nn tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 20:21:42.428698 IP 192.168.122.216.11111 > 192.168.122.181.41616: Flags [R.], seq 2836811943, ack 622903595, win 227, options [nop,nop,TS val 2681900 ecr 1590444], length 0
ちなみに、カーネル受信バッファに受信データが残っているかどうかの判定処理は以下の場所です。カーネル受信バッファに受信データが残っている場合、tcp_send_active_reset関数の延長でRSTパケットを送信します。
2004 void tcp_close(struct sock *sk, long timeout) -snip- 2050 } else if (data_was_unread) { 2051 /* Unread data was tossed, zap the connection. */ 2052 NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE); 2053 tcp_set_state(sk, TCP_CLOSE); 2054 tcp_send_active_reset(sk, sk->sk_allocation);
クライアントでECONNRESETが発生していることがわかります。
[root@client ~]# nc 192.168.122.216 11111 12345 Ncat: Connection reset by peer.
Z 参考情報
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ