네트워크 기초 — 리눅스 VM 에서 손으로 · TCP·UDP·ICMP · 이론
TCP·UDP·ICMP — '안 됨' 의 세 가지 얼굴
한 줄 요약
TCP 는 SYN·SYN-ACK·ACK 세 번으로 연결을 열고 FIN 으로 닫으며, 닫힌 포트는 RST 로 답한다. UDP 는 연결이 없고 닫힌 포트는 ICMP port unreachable 로 알려 준다. MTU 를 넘는 패킷은 조각나거나(DF 없이) 버려지며(DF), 그 사실을 알리는 것도 ICMP 다.
왜 이게 필요했나
"연결이 안 된다" 는 한 문장에 원인이 다른 세 가지가 섞여 있다. SYN 을 보냈는데 RST 가 오면 상대는 살아 있고 그 포트에 아무도 없다. 아무것도 안 오면 방화벽이 버렸거나 경로가 없다. SYN-ACK 까지 왔는데 데이터가 안 오면 애플리케이션이 멈춘 것이다. 사용자에게는 셋 다 "안 됨" 이지만 고치는 사람이 완전히 다르다. 플래그를 읽을 줄 알면 첫 30초에 그 셋이 갈린다.
어떻게 동작하나
TCP 핸드셰이크가 세 번인 이유는 양쪽이 서로의 시작 순서 번호를 확인했다는 확인까지 받아야 하기 때문이다. 먼저 닫은 쪽은 TIME-WAIT 에 60초 머문다 — 마지막 ACK 가 유실됐을 때 다시 보내 줄 책임과, 늦게 오는 옛 세그먼트가 새 연결에 섞이지 않게 4-튜플을 봉인할 책임이 있어서다. 짧은 연결을 초당 수천 번 여는 클라이언트는 이것으로 임시 포트(ip_local_port_range, 보통 32768~60999)가 고갈된다. 클라이언트 포트는 커널이 이 범위에서 고르고, 서버 포트만 약속이다.
UDP 는 보내면 끝이다. 상대가 듣는지도 받았는지도 모른다. 닫힌 포트로 보내면 커널이 ICMP port unreachable 을 돌려주는데, 방화벽이 ICMP 를 통째로 막으면 그 답도 사라져 "응답 없음" 만 남는다. DNS 가 재시도를 스스로 하는 이유다.
MTU 는 링크가 실어 나르는 IP 패킷의 최대 크기다. 이더넷 1500 에서 IP 헤더 20, ICMP 헤더 8 을 빼면 ping 페이로드는 1472 까지다. -M do 로 DF 를 켜면 넘는 순간 message too long 이고, 끄면 조각난다. 조각은 되긴 되지만 하나만 잃어도 전체를 다시 보내야 하고 방화벽은 뒤 조각의 포트를 못 본다. 그래서 요즘은 DF 를 켜고 경로 MTU 를 찾는데(PMTUD), 그 신호인 ICMP fragmentation needed 를 방화벽이 막으면 작은 패킷만 통과하고 큰 패킷만 사라지는 고장이 된다.
현장에서 만나는 모습
SSH 는 되는데 파일 전송만 멈춘다. VPN 을 새로 놓은 뒤부터다. 터널 헤더가 붙어 실제 MTU 가 1500 보다 작아졌는데 DF 패킷이 그것을 넘고, ICMP 는 막혀 있다. 프롬프트 같은 작은 패킷은 통과하고 본문만 죽는다. ping -M do -s 1472 로 어디서 깨지는지 찾으면 된다.
임시 포트 고갈. 프록시 서버가 초당 수천 개의 짧은 연결을 뒷단으로 열었다. ss -s 에 TIME-WAIT 가 2만 개. 새 연결이 Cannot assign requested address 로 실패한다. 답은 keep-alive 로 연결을 재사용하는 것이다.
다음 실습에서 할 것
lo 위에서 nc 서버와 클라이언트를 놓고 tcpdump 로 핸드셰이크·종료·RST 를 잡고 TIME-WAIT 와 임시 포트를 ss 로 본다. 그다음 네임스페이스 둘 사이에서 ICMP echo 를 잡고, ping -M do 로 MTU 의 경계를 찾고, 3000 바이트 ping 이 조각나는 것과 닫힌 UDP 포트의 unreachable 을 캡처한다.