📚 네트워크 · 패킷 분석 › 02. 포트 · 프로토콜 분석 — 60편
이전 글: 59. Established Connection · 다음 글: 61. HTTP 요청·응답 구조
TCP에서 포트는 단순히 "서비스 번호"가 아니라 연결을 식별하는 키의 일부입니다. TCP 연결 하나는 다음 네 값의 조합으로 구분됩니다.
(출발지 IP, 출발지 포트, 목적지 IP, 목적지 포트) = 4-tuple
+ 프로토콜(TCP) = 5-tuple
TCP 자체의 특성(신뢰성, 순서 보장)은 01. TCP/IP 구조 이해 영역 38. TCP란 무엇인가에서 다뤘습니다. 이 글은 TCP가 포트를 어떻게 쓰는가, 즉 들어온 세그먼트를 어느 소켓에 넘기는지와 포트 상태에 따라 TCP가 어떻게 응답하는지만 다룹니다.
| 소켓 종류 | 식별 기준 | 예 |
|---|---|---|
| LISTEN 소켓 | 로컬 IP·포트 (상대는 미정) | 0.0.0.0:443 |
| 연결 소켓 (ESTABLISHED 등) | 4-tuple 전체 | 192.168.10.20:443 ↔ 192.168.10.30:51000 |
서버 커널은 세그먼트가 도착하면 다음 순서로 받을 소켓을 찾습니다(역다중화, Demultiplexing).
세그먼트 도착: 192.168.10.30:51000 → 192.168.10.20:443
↓
① 4-tuple이 정확히 일치하는 연결 소켓이 있는가?
├─ 있음 → 그 연결로 전달 (데이터, ACK, FIN 등)
↓ 없음
② SYN인가? 그리고 목적지 IP·포트에 LISTEN 소켓이 있는가?
├─ 둘 다 예 → SYN/ACK 응답, 새 연결 생성 준비
↓ 아니오
③ 받을 소켓 없음 → RST 응답 (호스트 방화벽이 버리면 무응답)
이 구조 덕분에 서버 포트 443 하나에 수천 개의 연결이 동시에 존재할 수 있습니다. 모든 연결의 로컬 쪽은 192.168.10.20:443으로 같지만, 상대 쪽 IP·포트가 달라 4-tuple이 모두 다르기 때문입니다.
클라이언트 쪽도 마찬가지로, 목적지가 다르면 같은 출발지 포트를 서로 다른 연결에 동시에 쓸 수 있습니다. "포트 하나 = 연결 하나"라는 생각은 틀린 셈입니다.
포트 상태에 따라 SYN에 대한 TCP 반응이 달라집니다. 관제에서 "그 포트가 열려 있었는가"를 판단하는 근거입니다.
| 포트 상태 | SYN에 대한 반응 | 로그·캡처에서 보이는 모습 |
|---|---|---|
| 열림 (LISTEN) | SYN/ACK | Handshake 완료, 세션 생성 |
| 닫힘 (LISTEN 없음) | RST/ACK | 짧은 2패킷 교환, 세션 즉시 종료 |
| 필터링 (방화벽 drop) | 무응답 | SYN 재전송 반복 후 실패 |
| 필터링 (방화벽 reject) | RST 또는 ICMP Destination Unreachable | 방화벽이 대신 응답 (응답 IP·TTL이 서버와 다를 수 있음) |
| 대기열 포화 | 무응답 또는 지연 (OS·설정에 따라 다름) | SYN 재전송, 서버의 SYN-RECV·대기열 지표 증가 |
연결이 끝난 뒤에도 포트는 곧바로 자유로워지지 않습니다.
| 항목 | 내용 |
|---|---|
| TIME_WAIT를 갖는 쪽 | 먼저 연결을 닫은 쪽 (Active Close) |
| 보류 대상 | 번호 하나가 아니라 해당 4-tuple |
| 목적 | 늦게 도착한 이전 연결의 세그먼트가 새 연결에 섞이지 않게 함 |
| 서버 재시작 문제 | 서버가 먼저 닫은 연결의 TIME_WAIT가 남아 있으면 같은 포트 bind()가 실패할 수 있어, 서버 프로그램은 보통 SO_REUSEADDR 옵션을 사용 |
FIN과 RST로 연결이 끝나는 과정 자체는 45. TCP 연결 종료 — FIN과 RST의 차이에서 다룹니다.
실습 예시 — 본인 소유 VM 두 대(서버 192.168.10.20, 클라이언트 192.168.10.30)에서 열린 포트와 닫힌 포트의 반응을 비교합니다(Rocky/Ubuntu 공통, 인터페이스 ens33은 예시).
# 서버: 두 포트의 SYN과 응답 캡처
sudo tcpdump -nn -i ens33 'tcp port 22 or tcp port 9999'
# 클라이언트: 열린 포트(22)와 닫힌 포트(9999)에 각각 연결 시도
timeout 3 bash -c 'exec 3<>/dev/tcp/192.168.10.20/22' && echo open
timeout 3 bash -c 'exec 3<>/dev/tcp/192.168.10.20/9999' || echo closed-or-filtered
tcpdump 출력 형식 예시(값은 환경마다 다름):
192.168.10.30.51000 > 192.168.10.20.22: Flags [S], seq 100, length 0
192.168.10.20.22 > 192.168.10.30.51000: Flags [S.], seq 700, ack 101, length 0
192.168.10.30.51002 > 192.168.10.20.9999: Flags [S], seq 300, length 0
192.168.10.20.9999 > 192.168.10.30.51002: Flags [R.], seq 0, ack 301, length 0
한 포트에 여러 연결이 공존하는 모습은 서버에서 확인합니다.
ss -tn state established '( sport = :22 )' # Local은 모두 :22, Peer만 다름
ss -tan state time-wait | head # TIME_WAIT 4-tuple
Rocky에서 firewalld가 9999를 막고 있다면 RST 대신 ICMP(admin prohibited)가 보일 수 있어, 캡처 결과가 위 예시와 다를 수 있습니다.
| 흔적 위치 | 확인할 수 있는 것 |
|---|---|
| 패킷 캡처 | SYN에 대한 응답 종류(SYN/ACK, RST, ICMP, 무응답) |
| 방화벽 로그 | drop/reject 결과, 짧게 끝난 세션(패킷 1~2개) |
서버 ss | 포트별 연결 수, TIME_WAIT·SYN-RECV 수 |
| IDS/IPS | 닫힌 포트에 대한 연속 시도, 대량 RST |
관제자가 확인할 질문
오탐 주의: RST가 많다고 모두 공격은 아닙니다. 애플리케이션이 연결을 강제로 끊을 때, 로드밸런서 헬스체크, 유휴 타임아웃 후 늦은 패킷에도 RST가 생깁니다. 실제 패킷에서 이 반응들을 구분하는 방법은 03. Wireshark 패킷 분석 영역 127. RST Packet 분석에서 다룹니다.