Old-School Networking / 2026 ・ No.12
いにしえのネットワーク入門
第12回(最終回) つながらない時の切り分け ── 下の層から、1段ずつ
公開
「申請ボタンを押しても、何も起きません」。最終回は、この一言から始まる調べ物です。
第9・10回の練習用ネットワークを、ケーブル、道、相手の機械、名前、番号、鍵、中身と、わざと1か所ずつ壊しました。そのたびに出たエラーの文面と、どの道具で、どの層で止まっているかを突き止める手順を、下の層から順に確かめます。
「何も起きません」
新井先輩、経理から電話です。「申請ボタンを押しても何も起きない」って。サーバーが落ちたんでしょうか。
古井落ちたかもしれないし、ケーブルが抜けたのかもしれないし、名前が引けないのかもしれない。いきなり答えに飛ぶな。
新井じゃあ、どこから?
古井下からだ。11回かけて作った地図を、指でなぞる。
全12回の路線図(いまここ:第12回)
- 切り分けのはしご ── 層ごとに1つの質問と1つの道具。下から順に
- 7通りに壊す ── ケーブル、道、相手の機械、名前、番号、鍵、nginx の向こう。そのたびの文面と時間(実測)
- 断られたか、捨てられたか ── 0.041 秒の RST と、10 秒の時間切れ(実測と Wireshark)
- 遅いとき ── curl で時間を名前・TCP・TLS・サーバーに分ける(実測)
練習用ネットワークは第9・10回と同じです。PC とプロバイダのリゾルバーが線の手前、DNS サーバーと申請サーバーが片道 20 ミリ秒のいたずら線の向こう。今回は申請サーバーの手前に nginx(80 番)も置きました。毎回1か所だけを壊し、記録を取ったら元に戻します。
切り分けのはしご
切り分けの第一歩は、第11回と同じく、どこから、どこの、何番へ送っているのかを1行で書くことです。今回なら「PC から、expenses.example の 3443 番へ、HTTPS で」。そのうえで、層ごとに1つずつ質問していきます。
層ごとに、1つの質問と1つの道具
はしごは下からのぼります。上の段は下の段の上に乗っているので、口がつながっていなければ名前は引けず、名前が引けなければ TCP は始まらず、TCP がなければ TLS も HTTP もありません。逆に、ある段が通れば、それより下は全部動いている証拠です。もう疑わなくてかまいません。
エラーの文面にだまされない
まず、ケーブルを抜きました(練習では、線の向こう側の口を落としました)。
# ケーブルを抜いた(線の向こう側の口を落とした)
pc$ curl https://expenses.example:3443/api/expenses
curl: (6) Could not resolve host: expenses.example (6.025 s)
# まず自分の口を見る
pc$ ip -br link show eth0
eth0 DOWN <NO-CARRIER,BROADCAST,MULTICAST,UP>
pc$ ping -c2 198.51.100.53
2 packets transmitted, 0 received, 100% packet loss
curl の文面は「Could not resolve host」、名前が引けない、でした。6.02 秒待たされたあとです。でも DNS は何も壊れていません。curl が最初にしようとしたのが名前引きで、そこで止まったので、そう書いただけです。エラーの文面は、いちばん上で起きたことしか言いません。はしごを下から見れば、ip link の NO-CARRIER(向こうに相手がいない)で一目瞭然です。
「DNS が悪い」と言う前に、自分の口と、リゾルバーまでの ping を見ます。Wi-Fi のつかみそこね、ケーブルの抜け、VPN の切れ目は、名前引きの失敗として現れることがよくあります。
住所と道
次は第3層です。まず、203.0.113.0/24 への道(経路)を消しました。
# 203.0.113.0/24 への道(経路)を消した
pc$ curl -v https://expenses.example:3443/api/expenses
* Trying 203.0.113.20:3443...
* Immediate connect fail for 203.0.113.20: Network is unreachable
curl: (7) Failed to connect … after 0 ms: Couldn't connect to server
pc$ ip route get 203.0.113.20
RTNETLINK answers: Network is unreachable
道がなければ、パケットは PC から出ることすらできません。0 ミリ秒で「Network is unreachable」。ip route get で、その住所への道があるかを直接たずねられます。
今度は道はあるが、相手の機械がいない場合です。申請サーバーの住所 203.0.113.20 を外しました。
# 相手の機械がいない(203.0.113.20 を外した)
pc$ curl https://expenses.example:3443/api/expenses
curl: (7) Failed to connect … after 3053 ms: Couldn't connect to server
# 隣の住所(MAC)が引けたか
pc$ ip neigh show 203.0.113.20
203.0.113.20 dev eth0 FAILED
pc$ ping -c3 203.0.113.20
3 packets transmitted, 0 received, 100% packet loss
curl は 3.05 秒待って「つながらない」。表紙の写真が、そのときの PC の口です。第3回の ARP で「203.0.113.20 はだれ?」と3回尋ね、返事がないまま終わっています。ip neigh の FAILED は「隣の宛名(MAC)が引けなかった」という記録で、相手の機械が止まっているか、住所の設定が違うかを疑います。
名前
名前の段は、2通りに壊しました。リゾルバーを止めたときと、名前を打ち間違えたときです。
# ① リゾルバーが止まった
pc$ curl https://expenses.example:3443/api/expenses
curl: (6) Could not resolve host: expenses.example
pc$ dig expenses.example
;; communications error to 198.51.100.53#53: connection refused
# ② 名前の打ち間違い(exmaple)
pc$ curl https://expenses.exmaple:3443/api/expenses
curl: (6) Could not resolve host: expenses.exmaple
pc$ dig expenses.exmaple
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN
# 正しい名前なら
pc$ dig +short expenses.example
203.0.113.20
curl の文面は、どちらも「Could not resolve host」で区別がつきません。dig なら違いがはっきりします。リゾルバーが止まったときは「communications error」(そもそも答えをもらえない)、打ち間違いは status: NXDOMAIN(ちゃんと答えをもらえて、その答えが「そんな名前はない」)。前者は第3層までかリゾルバーの問題、後者は名前そのものの問題です。
第9回のとおり、dig は hosts を見ません。アプリだけ様子がおかしいときは getent hosts(Mac なら dscacheutil)でも引いて、hosts の書き置きを疑います。
番号:断られたか、捨てられたか
第4層でいちばん大事なのは、断られたのか、捨てられたのかの区別です。サーバーのプログラムを止めたときと、相手の防火壁に黙って捨てさせたときを比べました。
# ① サーバーのプログラムが止まった(その番号で待つ者がいない)
pc$ curl https://expenses.example:3443/api/expenses
curl: (7) Failed to connect … after 41 ms: Couldn't connect to server
pc$ nc -vz expenses.example 3443
… port 3443 (tcp) failed: Connection refused
srv$ ss -ltn 'sport = :3443'
(なし)
# ② 相手の防火壁が、黙って捨てる(DROP)
pc$ curl --connect-timeout 10 https://expenses.example:3443/api/expenses
curl: (28) Failed to connect … after 10002 ms: Timeout was reached
pc$ nc -vz -w 3 expenses.example 3443
… port 3443 (tcp) timed out: Operation now in progress
即答の RST と、返事のない SYN
プログラムが止まっていると、相手の OS が「その番号ではだれも待っていない」と RST で即答します。0.041 秒は、いたずら線のちょうど1往復です。相手の機械までは届いているので、見るべきは相手側でプログラムが動いているか(ss -ltn)、番号が合っているかです。
防火壁が捨てると、何も返ってきません。PC は第6回で見たとおり SYN を送り直し、curl は 10 秒で時間切れ。こちらは途中のどこかで消えているので、防火壁、セキュリティグループ、途中の道を疑います。
鍵
TCP がつながっても、TLS の段で止まることがあります。練習用の CA を curl に教えずに、HTTPS で送りました。
# 練習用の CA を知らないまま HTTPS で
pc$ curl https://expenses.example:3443/api/expenses
curl: (60) SSL certificate problem: unable to get local issuer certificate
# 相手が見せた証明書を、openssl で直接見る
pc$ openssl s_client -connect expenses.example:3443 -servername expenses.example
subject=CN = expenses.example
issuer=CN = Kiji Practice Root CA
Verify return code: 21 (unable to verify the first certificate)
# CA を教えれば
pc$ openssl s_client … -CAfile ca.pem
Verify return code: 0 (ok)
openssl s_client は、相手が見せた証明書を直接のぞく道具です。「だれの証明書か(subject)」「だれが発行したか(issuer)」と、確かめの結果(Verify return code)が見えます。CA を教えれば 0(ok)。第8回の4つの場面(発行元を知らない、名前が違う、期限切れ、自己署名)のどれかを、ここで見分けます。直し方は、正しい CA を教えるか、証明書を直すこと。-k で確かめを切るのは、直したことになりません。
中身:だれが返した 5xx か
最後の段は HTTP です。申請サーバーの手前に nginx を置き、その向こうを2通りに壊しました。新しい版(/v2/)のアプリを起動し忘れたときと、アプリが遅すぎるときです。
# 申請サーバーの手前に nginx(80 番)。/v2/ の先(4000 番)は起動し忘れ
pc$ curl http://expenses.example/api/expenses
→ 200 (0.089 s)
pc$ curl http://expenses.example/v2/api/expenses
→ 502 Bad Gateway (0.083 s)
pc$ curl 'http://expenses.example/api/report?ms=3000&n=1'
→ 504 Gateway Time-out (2.085 s)
# nginx の記録(error.log、一部)
connect() failed (111: Connection refused) while connecting to upstream,
upstream: "http://127.0.0.1:4000/api/expenses"
upstream timed out (110: Connection timed out) while reading response header
from upstream, upstream: "http://127.0.0.1:3000/api/report?ms=3000&n=1"
5xx を返したのは手前の nginx。悪いのはその裏
返事が来た、ということは、下の5段はすべて通っています。見るべきはだれが返したかです。502 と 504 は、どちらも PC の目の前の nginx が返しました(写真の Server: nginx)。502 は「裏につなごうとしたら断られた」、504 は「裏が 2 秒以内に答えなかった」。原因は nginx の error.log に、裏の住所つきで書いてあります。4xx なら頼み方(第10回)、5xx ならサーバー側、そのうち 502・504 なら「サーバーの、さらに裏」です。
遅いとき
「つながらない」の親戚が「遅い」です。遅いときは、時間を段ごとに分けます。curl の -w は、名前引き・TCP・TLS・最初の1バイトが届くまでの時刻を、始まりから数えて教えてくれます。
# 300 ミリ秒かかる報告を、リゾルバーの記憶を消してから2回(curl -w で時間を分解)
pc$ curl -w 'dns %{time_namelookup} tcp %{time_connect} tls %{time_appconnect}
first-byte %{time_starttransfer} total %{time_total}\n' …
1回目 dns 0.166 tcp 0.206 tls 0.252 first-byte 0.595 total 0.595
2回目 dns 0.002 tcp 0.043 tls 0.090 first-byte 0.434 total 0.434
0.595 秒の内訳(実測)
差を取ると、1回目は名前 0.166 秒、TCP の握手 0.041 秒、TLS の握手 0.045 秒、サーバーの処理と返事 0.344 秒。2回目は名前が 0.002 秒に縮みました(第9回のキャッシュ)。どちらも、いちばん長いのはサーバー側の約 0.34 秒(300 ミリ秒の処理+1往復)で、ネットワークのせいではありません。「ネットワークが遅い」と言う前に、まずこの分解を見ます。
早見表と、自分の PC で
この連載で実際に出会ったエラーの文面と、最初に疑う段の早見表です。
| 文面(例) | まず疑う段 | 最初の一手 |
|---|---|---|
| Could not resolve host | 名前。ただし、その下も | ip link と dig |
| Network is unreachable | 道(経路がない) | ip route get 住所 |
| 数秒待ってつながらない(ARP に答えなし) | 相手の機械、住所の設定 | ip neigh |
| Connection refused | 番号(届いたが、だれも待っていない) | 相手で ss -ltn |
| Timeout was reached | 途中で捨てられている | 防火壁、nc -vz |
| SSL certificate problem | 鍵(第8回の4場面) | openssl s_client |
| 502 / 504 | 手前のサーバーの、さらに裏 | 手前の error.log |
| CORS のエラー | ブラウザの番人(第10・11回) | API の返すヘッダー |
| ECONNREFUSED 127.0.0.1 | localhost の取り違え(第11回) | どこから送っているか |
はしごの道具は、OS ごとに名前が少し違います。
| 段 | Mac | Windows | Linux |
|---|---|---|---|
| 線と口 | ifconfig | ipconfig /all | ip -br link |
| 道 | netstat -rn | route print | ip route get 住所 |
| 隣 | arp -a | arp -a | ip neigh |
| 名前 | dig 名前 | Resolve-DnsName 名前 | dig 名前 |
| 番号 | nc -vz 名前 番号 | Test-NetConnection 名前 -Port 番号 | nc -vz 名前 番号 |
| 鍵・中身 | curl -v https://名前/ | ||
結 — 全12回の一枚まとめ
申請ボタンの1回の通信を、下から上までたどった12回
- まず1行で書く。どこから、どこの、何番へ。
- はしごは下から。通った段より下は疑わない。
- エラーの文面は、いちばん上で起きたことしか言わない。ケーブルが抜けても「Could not resolve host」。
- 断られた(すぐ)は相手まで届いた証拠、時間切れ(待たされる)は途中で消えた証拠。
- 名前は dig で。止まったリゾルバーと NXDOMAIN を分ける。
- 鍵は openssl s_client で。確かめを切らずに直す。
- 5xx はだれが返したかを見る。遅いときは時間を段ごとに分ける。
新井経理さんの件、ip link から順に見たら、無線の切りかえで線が切れていました。サーバーは元気でした。
古井上出来だ。12回分の地図があれば、どこで止まっても迷子にならない。
新井次は、この申請を受け取るサーバーの中を知りたいです。
古井いいね。網の旅はここまで。続きは、サーバーの中でやろう。
参考
- ip-link(8) / ip-route(8) / ip-neighbour(8)
- dig(1)
- OpenSSL 3.0 — openssl-s_client
- Everything curl — Write out(-w)
- nginx — ngx_http_proxy_module(proxy_read_timeout)
- RFC 9110 — HTTP Semantics(502, 504)
- RFC 9293 — Transmission Control Protocol (TCP)
- RFC 826 — An Ethernet Address Resolution Protocol
- Microsoft Learn — Test-NetConnection
記録は、筆者がクラウド上の Linux(Ubuntu 24.04、カーネル 6.18)にネットワーク名前空間で作った練習用ネットワークで、2026年10月25日に取ったものです。構成は第9・10回と同じ(リゾルバー Unbound 1.19.2、DNS サーバー NSD 4.8.0、申請サーバー Node.js 22.23)で、申請サーバーの手前に nginx 1.24.0 を置き、PC 側は curl 8.5.0、OpenSSL 3.0 です。名前と住所はすべて説明用に予約されたもの、または練習用に作ったものです。PC とサーバーの間は、第6回のいたずら線(片道 20 ミリ秒)です。表の数字や出力はすべて、この記録とそのときのコマンドの出力から読んだ実際の値です(長い出力は一部を省いています)。登場人物は架空の人物です。全12回を読んでくださって、ありがとうございました。
いにしえのネットワーク入門 第12回 — つながらない時の切り分け / 2026.10