애플리케이션 로그에는 요청이 없는데 클라이언트는 분명 보냈다고 말합니다. 방화벽인지 라우팅인지 앱 문제인지 논쟁만 이어질 때 패킷 캡처가 사실을 보여줍니다.
tcpdump는 네트워크 장애의 블랙박스입니다. 패킷이 실제로 왔는지부터 확인하면 추측을 줄일 수 있습니다.
패킷 캡처와 심층 분석 (tcpdump)
네트워크 장애를 진단할 때 로그만으로는 한계가 있습니다. 정말 패킷이 가고 있는지, 어디서 차단되는지, TCP 연결이 정상적으로 수립되는지 확인하려면 패킷 수준의 분석이 필요합니다.
tcpdump는 네트워크 인터페이스를 지나가는 패킷을 실시간으로 캡처하고 분석하는 도구입니다. 인프라 엔지니어라면 반드시 익혀야 할 핵심 진단 도구입니다.
- 1패킷 스니핑 원리와 BPF(Berkeley Packet Filter) 동작 방식을 설명할 수 있다
- 2TCP 3-Way Handshake를 패킷 수준에서 상세 분석할 수 있다
- 3tcpdump 주요 옵션과 BPF 필터 문법을 활용할 수 있다
- 4pcap 파일을 저장하고 Wireshark와 연동해 분석할 수 있다
- 5TCP Retransmission·RST 패킷으로 네트워크 장애를 진단할 수 있다
- 6실전 시나리오별 tcpdump 필터를 작성하고 응용할 수 있다
sudo dnf install -y tcpdump # CentOS/RHEL
sudo apt-get install -y tcpdump # Ubuntu/Debiansudo tcpdump -Dsudo tcpdump -i any -n -c 20로컬 GUI 환경에서 Wireshark를 설치하면 -w 옵션으로 저장한 .pcap 파일을 시각적으로 분석할 수 있습니다
패킷 스니핑 원리와 tcpdump 동작 방식
두 서비스 사이에서 API 호출이 실패합니다. 클라이언트 쪽 로그엔 "connection refused"가 찍히는데, 서버 쪽 로그엔 아무것도 없습니다. 요청이 서버까지 도달했는지, 응답이 돌아가는 중에 끊기는지, 아니면 아예 도달하지 않는지를 알 수 없습니다. tcpdump로 패킷을 직접 보면 이 모든 의문이 1분 안에 해결됩니다.
확대
패킷 스니핑이란
네트워크 인터페이스를 통해 지나가는 패킷을 복사해서 분석하는 기술입니다. 네트워크의 전화 도청과 비슷하지만, 자신이 관리하는 서버에서의 진단 목적으로 합법적으로 사용됩니다.
일반 모드 vs 무차별(Promiscuous) 모드
네트워크 카드는 기본적으로 자신에게 온 패킷만 처리합니다. tcpdump가 더 많은 패킷을 보려면 무차별 모드로 전환해야 합니다.
일반 모드(Normal Mode):
서버는 자신의 MAC 주소가 목적지인 패킷만 받아들임
"내 것이 아닌 패킷은 무시"
무차별 모드(Promiscuous Mode):
서버는 모든 패킷을 받아들임 (자신에게 온 것이 아니어도)
"지나가는 모든 패킷 다 캡처"
tcpdump는 필요에 따라 무차별 모드를 자동으로 활성화합니다. 스위치 환경에서는 포트 미러링 없이는 다른 서버의 트래픽을 볼 수 없습니다.
BPF(Berkeley Packet Filter)
tcpdump의 핵심 기술입니다. 커널 레벨에서 동작해 매칭되는 패킷만 사용자 공간으로 올려줍니다.
네트워크 → [커널 NIC 드라이버] → [BPF 필터 엔진] → [매칭 패킷만] → tcpdump 출력
↑
"port 80 and host 1.2.3.4" 같은 규칙
BPF 필터링을 커널에서 처리하므로, 필터가 없으면 모든 패킷을 사용자 공간으로 올려 CPU와 메모리를 낭비하지만, 필터를 지정하면 해당 패킷만 처리해 성능 부담이 매우 낮습니다.
tcpdump 권한
네트워크 인터페이스를 무차별 모드로 전환하는 것은 시스템 권한이 필요한 작업입니다.
# 실습 디렉토리 준비
mkdir -p /tmp/networking/part5/exam_25 && cd /tmp/networking/part5/exam_25
# root 또는 sudo 필요
sudo tcpdump ...
# 또는 CAP_NET_ADMIN 권한을 tcpdump에 부여 (보안상 권장)
sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
- Flags 열을 먼저 확인 — [S]=SYN(연결 요청), [S.]=SYN+ACK(수락), [.]=ACK(수립), [R]=RST(거부/리셋), [F]=FIN(정상 종료) 순서로 흐름을 읽는다
- 캡처 패킷 수와 방향(src→dst) 기준: 요청 패킷만 있고 응답 패킷이 없으면 방화벽 DROP 또는 서버 미응답; 10초에 1000개 이상 동일 seq 재전송이면 대역폭 포화 또는 방화벽 DROP
- SYN 전송 후 즉각 RST 수신 → 포트 미오픈(서비스 없음); SYN만 반복 재전송(1→2→4→8초 간격) + RST 없음 → 방화벽 DROP; SYN→SYN-ACK→ACK 완성 후 [P.] 있음 → 정상 연결 수립
tcpdump가 패킷을 낚아채는 지점 — NIC에서 화면까지 5단계
tcpdump가 패킷을 잡는 정확한 지점 — NIC 도착부터 화면까지 5단계
tcpdump -i eth0 host 10.0.0.5 한 줄이면 그 호스트의 패킷이 화면에 흐릅니다. 그런데 tcpdump가 정확히 어느 지점에서 패킷을 복사해 가는지 모르면, "분명 보냈다는데 왜 안 잡히지" 또는 "잡히는데 왜 앱은 못 받지"를 설명할 수 없습니다. 캡처 지점이 커널 스택의 어디인지, 암호화·NAT·방화벽의 앞인지 뒤인지를 단계로 알아야 tcpdump 결과를 올바로 읽습니다.
[캡처 호스트] tcpdump -i eth0 host 10.0.0.5
│
회선 → NIC로 프레임 도착
│
① NIC 수신 → 커널 드라이버로 전달 (오프로드: 체크섬·세그먼트 병합 대행)
│
② 스택 진입부에서 AF_PACKET 탭이 복사본 추출 (수신은 방화벽 INPUT 훅보다 '앞')
│
③ BPF 필터가 커널 수준에서 선별 (host·port 조건에 맞는 것만)
│
④ libpcap → 사용자 공간 tcpdump로 전달 (원본은 스택을 계속 진행)
│
⑤ tcpdump 해석·출력 / -w면 pcap 저장
▼
14:32:01 IP 10.0.0.5.54321 > 10.0.0.1.80: Flags [S], ...
각 단계가 하는 일과, 어긋날 때의 신호:
| 단계 | 하는 일 | 여기서 어긋나면 |
|---|---|---|
| ① NIC 수신·오프로드 | NIC가 프레임을 받아 드라이버로 전달. 성능용으로 체크섬 채우기(TX)·세그먼트 병합(GRO)을 대행 | 오프로드 탓에 송신 패킷이 cksum incorrect·MTU 초과 거대 세그먼트로 보임 = 착시(회선은 정상) |
| ② 캡처 지점(탭) | 스택 진입부에서 AF_PACKET 소켓이 복사본을 뜬다. 수신은 방화벽 INPUT 훅보다 앞 | tcpdump엔 SYN이 보여도 그 뒤 iptables가 DROP할 수 있음 = "tcpdump에 잡힘 = 방화벽 통과"가 아님 |
| ③ BPF 선별 | host 10.0.0.5 and port 443 같은 필터를 커널에서 적용해 맞는 것만 올림 | 필터가 틀리거나 너무 좁으면 원하는 패킷이 안 뜸 / 필터 없으면 폭주해 드롭·디스크 풀 |
| ④ 사용자 공간 전달 | libpcap이 선별된 복사본을 tcpdump로 넘김. 원본 트래픽 흐름엔 영향 없음 | 잘못된 인터페이스(-i) 지정 시 아무것도 안 잡힘 — 트래픽이 다른 NIC·lo로 흐르는 경우 |
| ⑤ 해석·저장 | 헤더를 사람이 읽게 풀거나 -w로 pcap 저장(Wireshark 연동) | HTTPS는 이 지점이 TLS 암호화 '뒤'라 페이로드는 암호문 — 평문은 못 봄 |
즉 tcpdump는 회선을 그대로 보는 게 아니라 커널 스택의 한 지점에서 복사본을 보는 것이고, 그 지점이 방화벽·NAT·암호화의 어느 쪽이냐가 해석을 가릅니다. 가령 아웃바운드 캡처는 SNAT 변환 '앞'이라 사설 출발지 IP가 그대로 보이고, HTTPS 페이로드는 TLS 암호화 '뒤'라 암호문만 보입니다. 그래서 "안 잡힌다"의 3대 원인은 잘못된 인터페이스(-i 확인, tcpdump -D로 목록 조회)·너무 좁거나 틀린 필터·오프로드로 인한 왜곡이고, "잡히는데 앱이 못 받는다"는 캡처 지점(②) 뒤의 방화벽 DROP을 의심하는 신호입니다. 정확히 회선을 보려면 캡처 동안만 ethtool -K로 오프로드를 끄고, 다른 위치(상대·중간 장비)에서도 함께 떠 교차 검증합니다.
TCP 3-Way Handshake: 패킷 수준 상세 분석
tcpdump 출력에서 [S], [S.], [.], [F], [R] 플래그들이 무엇을 뜻하는지 모르면 패킷 캡처 결과를 보고도 읽을 수가 없습니다. TCP 연결이 어느 단계에서 끊겼는지, 서버가 연결을 거부했는지, 클라이언트가 먼저 끊었는지 — 패킷 수준에서 Handshake를 이해해야 이 플래그들이 의미 있는 정보가 됩니다.
확대
TCP 연결 수립 3단계
HTTP 요청을 보내기 전에 반드시 TCP 연결이 먼저 수립됩니다.
클라이언트(C) 서버(S)
| |
|--- SYN (seq=x) ------------→ | 1단계: "연결하고 싶어"
| |
| ←--- SYN-ACK (seq=y,ack=x+1)| 2단계: "OK, 나도 준비됐어"
| |
|--- ACK (ack=y+1) ----------→ | 3단계: "확인했어, 이제 얘기하자"
| |
|=== 데이터 전송 시작 ===========|
각 패킷의 역할
SYN (Synchronize)
- 클라이언트가 연결을 요청합니다.
- 초기 시퀀스 번호(ISN, Initial Sequence Number)를 랜덤으로 선택합니다.
SYN플래그만 설정됩니다.
SYN-ACK (Synchronize-Acknowledge)
- 서버가 연결 요청을 수락합니다.
- 서버도 자신의 ISN을 랜덤으로 선택합니다.
ACK번호 = 클라이언트 ISN + 1 (다음에 이 번호부터 받겠다는 의미)SYN과ACK플래그 모두 설정됩니다.
ACK (Acknowledge)
- 클라이언트가 서버의 SYN-ACK를 확인합니다.
ACK번호 = 서버 ISN + 1- 이제 양방향 연결이 수립됩니다.
tcpdump에서 3-Way Handshake 읽기
실제 tcpdump 출력 (sudo tcpdump -i eth0 port 80 -n):
10:25:13.001 IP 10.0.0.5.54321 > 10.0.0.1.80: Flags [S], seq 1234567890
↑ 클라이언트IP.포트 ↑ 서버IP.포트 ↑[S]=SYN
10:25:13.002 IP 10.0.0.1.80 > 10.0.0.5.54321: Flags [S.], seq 987654321, ack 1234567891
↑ 서버→클라이언트 ↑[S.]=SYN+ACK
10:25:13.002 IP 10.0.0.5.54321 > 10.0.0.1.80: Flags [.], ack 987654322
↑ 클라이언트→서버 ↑[.]=ACK (연결 수립!)
10:25:13.003 IP 10.0.0.5.54321 > 10.0.0.1.80: Flags [P.], seq 1:78, ack 1
↑ HTTP 요청 데이터 전송 시작 ↑[P.]=PUSH+ACK
TCP 연결 종료: 4-Way Handshake
연결 종료도 패킷 교환으로 이루어집니다.
클라이언트 서버
|--- FIN --→ | "나는 더 보낼 게 없어"
| ←-- ACK -- | "알겠어"
| ←-- FIN -- | "나도 더 보낼 게 없어"
|--- ACK --→ | "알겠어, 연결 종료"
tcpdump에서 [F.]는 FIN+ACK를 나타냅니다.
이상 상태 플래그
확대
| 플래그 | tcpdump 표시 | 의미 |
|---|---|---|
| RST | [R] | 연결 강제 리셋 (방화벽 차단 또는 포트 미오픈) |
| FIN | [F] | 정상 연결 종료 요청 |
| PUSH | [P] | 데이터 즉시 처리 요청 |
| URG | [U] | 긴급 데이터 |
tcpdump 설치 확인
which tcpdump
tcpdump --version
# 없으면 설치
sudo apt-get install -y tcpdump # Ubuntu/Debian
sudo yum install -y tcpdump # CentOS/RHEL
기본 옵션 정리
| 옵션 | 설명 | 예시 |
|---|---|---|
-i | 인터페이스 지정 | -i eth0 |
-n | DNS 역조회 비활성화 (IP 그대로 표시) | -n |
-nn | IP + 포트 이름 모두 숫자로 | -nn |
-v | 상세 출력 | -v |
-vv | 더 상세 출력 | -vv |
-w | pcap 파일로 저장 | -w capture.pcap |
-r | pcap 파일 읽기 | -r capture.pcap |
-c | 패킷 수 제한 | -c 100 |
-s | 캡처 크기 제한 (bytes) | -s 0 (전체) |
-A | 패킷 내용을 ASCII로 출력 | -A |
-X | 패킷 내용을 Hex+ASCII로 출력 | -X |
BPF 필터 문법 실습
# 1. 특정 포트만 캡처
sudo tcpdump -i eth0 -n port 80
# 2. 특정 호스트만 캡처
sudo tcpdump -i eth0 -n host 192.168.1.10
# 3. AND 조합: 특정 호스트의 특정 포트
sudo tcpdump -i eth0 -n host 192.168.1.10 and port 443
# 4. OR 조합: HTTP 또는 HTTPS
sudo tcpdump -i eth0 -n port 80 or port 443
# 5. 특정 호스트 제외
sudo tcpdump -i eth0 -n not host 192.168.1.1
# 6. 출발지(src) 또는 목적지(dst) 지정
sudo tcpdump -i eth0 -n src host 192.168.1.5
sudo tcpdump -i eth0 -n dst port 22
# 7. 네트워크 대역 전체
sudo tcpdump -i eth0 -n net 192.168.1.0/24
# 8. ICMP만 캡처 (ping 분석용)
sudo tcpdump -i eth0 -n icmp
# 9. TCP SYN 패킷만 (연결 시도만 모니터링)
sudo tcpdump -i eth0 -n 'tcp[tcpflags] & tcp-syn != 0'
# 10. TCP RST 패킷만 (연결 거부/리셋 모니터링)
sudo tcpdump -i eth0 -n 'tcp[tcpflags] & tcp-rst != 0'
모든 인터페이스 목록 확인
# 캡처 가능한 인터페이스 목록
sudo tcpdump -D
# 1. eth0
# 2. lo
# 3. any ← 모든 인터페이스
-i any를 사용하면 모든 인터페이스의 패킷을 동시에 캡처합니다.
1단계: 터미널 2개 준비
- 터미널 1: tcpdump 실행 (캡처 담당)
- 터미널 2: curl 요청 실행 (트래픽 생성)
2단계: tcpdump로 캡처 시작
# 터미널 1에서 실행
# 로컬 80포트 또는 테스트 서버 IP를 지정
sudo tcpdump -i lo -n port 8080 -v
# 외부 웹서버를 대상으로 할 경우
sudo tcpdump -i eth0 -n 'port 80 and host example.com'
3단계: HTTP 요청 발생
# 터미널 2에서 실행
# 로컬 테스트 서버가 없으면 먼저 간단한 서버 실행
python3 -m http.server 8080 &
# curl로 요청
curl -v http://localhost:8080/
4단계: 3-Way Handshake 캡처 결과 분석
# tcpdump 출력 예시 (주석 추가)
# [1단계] SYN: 클라이언트(포트 54322)가 서버(포트 8080)로 연결 요청
14:32:01.001234 IP 127.0.0.1.54322 > 127.0.0.1.8080:
Flags [S], seq 3847291836, win 65495, options [mss 65495,...], length 0
# [S] = SYN 플래그
# seq = 클라이언트의 초기 시퀀스 번호(ISN)
# [2단계] SYN-ACK: 서버가 수락 응답
14:32:01.001345 IP 127.0.0.1.8080 > 127.0.0.1.54322:
Flags [S.], seq 2910847362, ack 3847291837, win 65483, length 0
# [S.] = SYN + ACK 플래그
# ack = 클라이언트 ISN + 1 (다음 번 이 번호를 기대)
# seq = 서버의 ISN
# [3단계] ACK: 클라이언트가 확인
14:32:01.001356 IP 127.0.0.1.54322 > 127.0.0.1.8080:
Flags [.], ack 2910847363, win 512, length 0
# [.] = ACK 플래그 (. 은 ACK만 있다는 의미)
# ack = 서버 ISN + 1
# [데이터] HTTP GET 요청 전송
14:32:01.001400 IP 127.0.0.1.54322 > 127.0.0.1.8080:
Flags [P.], seq 1:78, ack 1, length 77: HTTP: GET / HTTP/1.1
# [P.] = PUSH + ACK
# length 77 = HTTP 요청 헤더 크기
5단계: pcap 파일로 저장 후 재분석
# 캡처를 파일로 저장
sudo tcpdump -i lo -n port 8080 -w /tmp/web_capture.pcap
# 다른 터미널에서 curl 실행 후 Ctrl+C로 캡처 중지
# 저장된 파일 읽기
tcpdump -r /tmp/web_capture.pcap -n
tcpdump -r /tmp/web_capture.pcap -n -A # ASCII로 HTTP 내용도 확인
# 저장된 파일에서 BPF 필터 적용해서 읽기
tcpdump -r /tmp/web_capture.pcap -n 'tcp[tcpflags] & tcp-syn != 0'
pcap 파일은 Wireshark에서 열어 GUI로 분석할 수도 있습니다. Wireshark에서 Statistics → TCP Stream Graphs → Time-Sequence Graph를 보면 패킷 흐름을 시각적으로 확인할 수 있습니다.
시나리오 1: 포트 차단 vs 포트 미오픈 구분
두 상황은 클라이언트 입장에서 "연결 실패"로 보이지만, 패킷 레벨에서 전혀 다릅니다.
# 포트 미오픈인 경우 (서비스가 해당 포트에서 listening하지 않음)
# RST 패킷이 즉시 돌아옴
sudo tcpdump -i eth0 -n 'port 9999 and host 192.168.1.10'
# 다른 터미널
curl http://192.168.1.10:9999/
# 예상 캡처:
# 클라이언트 → 서버: [S] (SYN)
# 서버 → 클라이언트: [R.] (RST+ACK) - "이 포트는 없어"
# 방화벽 DROP인 경우 (패킷을 조용히 버림)
# RST도 없고, SYN만 보내고 응답 없음 (timeout까지 대기)
# 예상 캡처:
# 클라이언트 → 서버: [S] (SYN)
# (응답 없음)
# 클라이언트 → 서버: [S] (SYN 재전송, ~1초 후)
# 클라이언트 → 서버: [S] (SYN 재전송, ~3초 후)
# 클라이언트 → 서버: [S] (SYN 재전송, ~7초 후)
진단 기준:
- RST 즉시 수신 → 포트 미오픈 (서비스 죽었거나 잘못된 포트)
- 무응답 + SYN 재전송 → 방화벽 DROP 또는 패킷 라우팅 문제
시나리오 2: TCP Retransmission 분석
# Retransmission 패킷 캡처 (실제 네트워크 문제 상황)
sudo tcpdump -i eth0 -n -w /tmp/retransmit.pcap port 80
# 저장된 파일 분석
tcpdump -r /tmp/retransmit.pcap -n
# Retransmission 패킷은 이전에 보낸 seq 번호가 그대로 재등장
# 예시:
# 14:00:01.000 seq 1001:2001 ← 최초 전송
# 14:00:01.200 seq 1001:2001 ← Retransmission! (같은 seq 다시 전송)
# 14:00:01.600 seq 1001:2001 ← 재전송 #2 (간격 2배로 증가: Exponential Backoff)
Wireshark에서는 Retransmission 패킷을 자동으로 빨간색으로 표시해줍니다.
시나리오 3: HTTP 응답 내용 확인
# HTTP 요청/응답 내용을 ASCII로 덤프
sudo tcpdump -i eth0 -n -A port 80 | grep -A 5 "HTTP/"
# 예시 출력:
# GET / HTTP/1.1
# Host: example.com
# User-Agent: curl/7.81.0
# ---
# HTTP/1.1 200 OK
# Content-Type: text/html
주의: HTTPS(TLS 암호화) 트래픽은 tcpdump로 내용을 볼 수 없습니다. TLS 핸드셰이크 패킷만 보입니다.
캡처 크기와 스냅샷 길이 조정
# 기본 snaplen은 패킷 헤더 정도만 캡처
# -s 0 으로 전체 패킷 캡처 (파일 크기 주의)
sudo tcpdump -i eth0 -n -s 0 -w /tmp/full.pcap port 80
# 첫 100바이트만 (헤더 분석용, 파일 크기 절약)
sudo tcpdump -i eth0 -n -s 100 -w /tmp/header.pcap port 80
상황
애플리케이션 로그에 타임아웃 오류가 급증합니다. 서버와 서비스는 모두 정상처럼 보이지만, 클라이언트에서 간헐적으로 요청이 실패합니다.
1단계: 패킷 캡처로 현상 확인
# 문제가 발생하는 포트에서 패킷 캡처
sudo tcpdump -i eth0 -n port 443 -w /tmp/issue.pcap
# 5분간 캡처 후 분석
tcpdump -r /tmp/issue.pcap -n | grep -c "Retransmission"
# → 수백 건의 Retransmission 발견
2단계: 원인 구분 - 대역폭 포화인가?
# 인터페이스 트래픽 확인
sar -n DEV 1 10
# 또는
ifstat -i eth0 1 10
# 출력 예시:
# eth0: rxpck/s txpck/s rxkB/s txkB/s
# 8420 7830 9800 9750
네트워크 인터페이스의 최대 속도(예: 1Gbps = 125MB/s)에 근접하면 대역폭 포화입니다.
대역폭 포화 시 Retransmission 패턴:
seq 1001:2001 → 전송
seq 2001:3001 → 전송
seq 3001:4001 → 전송
(buffer overflow → Drop)
seq 1001:2001 → Retransmission
seq 2001:3001 → Retransmission
대량의 연속적인 Retransmission이 발생하며, 전반적으로 트래픽이 급증한 시점과 일치합니다.
3단계: 원인 구분 - 방화벽 Drop인가?
방화벽이 DROP 규칙으로 패킷을 버리면 RST 응답이 없습니다.
# Retransmission 간격 분석
tcpdump -r /tmp/issue.pcap -nn -tt | awk '/Retransmission/{print}' | head -20
# 방화벽 Drop 패턴:
# SYN 전송 → 1초 후 SYN 재전송 → 3초 후 재전송 → 7초 후 재전송
# (Exponential Backoff: 1→2→4→8초 간격)
# RST 없이 조용히 실패
방화벽에서 상태 테이블이 가득 차면, 기존 연결의 중간 패킷도 DROP될 수 있습니다.
# Linux conntrack 테이블 상태 확인
sudo conntrack -C
# 또는
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# count가 max에 근접하면 새 연결 + 기존 연결 패킷 Drop 시작
해결 방법
대역폭 포화인 경우:
# 트래픽 우선순위 설정 (tc qdisc)
# 또는 업링크 증설
# 단기: 대역폭 많이 쓰는 프로세스 제한
sudo tc qdisc add dev eth0 root tbf rate 100mbit burst 10mbit latency 50ms
방화벽 Drop인 경우:
이 명령은 시스템 설정 파일을 변경합니다. 기존 파일 백업과 적용 후 검증 방법을 준비하지 않으면 재부팅이나 서비스 재시작 때 장애가 반복될 수 있습니다.
# conntrack 테이블 증설
sudo sysctl -w net.netfilter.nf_conntrack_max=131072
echo "net.netfilter.nf_conntrack_max = 131072" >> /etc/sysctl.conf
# 또는 불필요한 conntrack 항목 정리
sudo conntrack -F
핵심 교훈
Retransmission 자체보다 패턴이 중요합니다. 연속적이고 대량이면 대역폭 문제, 특정 서버로만 가는 패킷에서 Exponential Backoff 패턴이면 방화벽 Drop을 의심하세요.
상황
장애 진단을 위해 tcpdump를 실행했는데, 30분 후 서버 전체가 응답 불능이 됩니다. 확인하니 / 파티션이 100%입니다.
원인
ls -lh /tmp/capture.pcap
# -rw-r--r-- 1 root root 47G ← 47기가바이트짜리 pcap 파일!
# 트래픽이 많은 서버에서 필터 없이 -i any 로 캡처하면
# 분당 수 기가바이트가 쌓일 수 있음
안전한 tcpdump 사용 방법
1. 항상 필터를 명확히 지정
# 나쁜 예: 필터 없이 전체 캡처
sudo tcpdump -i eth0 -w /tmp/all.pcap
# 좋은 예: 필요한 포트/호스트만
sudo tcpdump -i eth0 -n port 80 and host 10.0.0.5 -w /tmp/targeted.pcap
2. 파일 크기 또는 패킷 수 제한
# 패킷 수 제한 (-c)
sudo tcpdump -i eth0 -n port 443 -c 10000 -w /tmp/limited.pcap
# 파일 크기 제한 + 순환 저장 (-C: MB 단위, -W: 파일 수)
sudo tcpdump -i eth0 -n port 80 -w /tmp/rotate.pcap -C 100 -W 5
# 100MB 파일 5개를 순환 → 최대 500MB 사용
3. 저장 위치 주의
# / 파티션 대신 여유 공간이 많은 파티션 사용
df -h
# /data 파티션이 1TB 여유 있다면
sudo tcpdump -i eth0 -n port 80 -w /data/captures/web.pcap
4. 실시간으로 파이프로 처리 (파일 저장 없이)
# 파일 저장 없이 실시간 분석
sudo tcpdump -i eth0 -n port 80 -A | grep "HTTP"
# 특정 패턴 발견 시 자동 중지
sudo tcpdump -i eth0 -n port 80 -c 100
심화 — tcpdump가 보여주는 것 ≠ 회선의 진실
심화: tcpdump를 오독하게 만드는 것 — 캡처 지점과 NIC 오프로드
tcpdump는 '회선을 그대로 본다'고 믿기 쉽지만, 실제로는 커널 스택의 특정 지점에서 패킷 복사본을 받는 도구입니다. 이 지점이 어디인지, NIC가 무엇을 대신 처리하는지를 모르면 멀쩡한 패킷을 장애로 오해하거나 그 반대를 겪습니다.
- tcpdump가 뜨는 지점: 리눅스에서 tcpdump는 AF_PACKET으로 드라이버 근처에서 패킷을 복사해 갑니다. 수신 방향은 netfilter(iptables INPUT) 훅보다 앞이라, iptables가 DROP할 패킷도 tcpdump에는 이미 잡힙니다. 그래서 '서버 tcpdump에 SYN이 보인다 = 방화벽이 안 막았다'는 거짓일 수 있습니다 — 로컬 INPUT이 캡처 이후에 버린 것입니다.
- 체크섬 오프로드: 요즘 NIC는 TCP/IP 체크섬을 하드웨어에서 채웁니다(TX checksum offload). tcpdump는 그 전에 캡처하므로 송신 패킷에
cksum incorrect/bad cksum경고를 붙입니다 — 회선의 실제 패킷은 정상입니다. 이 경고를 버그로 오해하지 마세요. - 세그멘테이션 오프로드(TSO/GSO/GRO/LRO): 성능을 위해 커널·NIC는 송신 시 큰 버퍼를 회선 직전에 MSS로 쪼개고(TSO/GSO), 수신 시 여러 세그먼트를 하나로 합칩니다(GRO/LRO). tcpdump는 쪼개기 전·합친 후를 보므로 MSS(예: 1460)보다 훨씬 큰 수만 바이트짜리 '패킷'을 보여줍니다. MTU/MSS를 tcpdump 길이로 판단하면 틀립니다.
- 정확히 회선을 보려면: 캡처 동안만 오프로드를 끄거나(
ethtool -K eth0 gro off gso off tso off), 상대편·중간 장비에서도 같이 떠서 교차 검증합니다. 로컬 한 지점의 tcpdump만으로 회선의 진실을 단정하지 않는 것이 핵심입니다.
상황: 송신 트래픽을 로컬에서 tcpdump로 뜨니 다수 패킷에 cksum 0x.... incorrect 경고가 붙고, 일부 세그먼트는 length 65160처럼 MTU를 훨씬 초과합니다. 네트워크가 손상됐거나 MTU 설정이 잘못됐다고 의심하게 됩니다.
원인: NIC 오프로드가 만든 착시입니다. TX 체크섬 오프로드로 체크섬은 NIC가 전송 직전에 채우므로 tcpdump에는 미완성(incorrect)으로 보이고, TSO/GSO로 커널이 넘긴 큰 버퍼는 회선 직전에야 MSS로 쪼개지므로 tcpdump는 쪼개기 전 거대 세그먼트를 봅니다. 수신 쪽 GRO/LRO도 여러 세그먼트를 합쳐 보여줍니다. 회선의 실제 패킷은 정상 크기·정상 체크섬입니다.
진단: 이 경고·거대 길이가 로컬에서 캡처할 때만 나타나는지 확인합니다. ethtool -k eth0로 tx-checksumming, tcp-segmentation-offload, generic-receive-offload가 on인지 봅니다. 중간 라우터나 상대 서버에서 같은 흐름을 뜨면 정상 크기·정상 체크섬으로 보입니다 — 로컬 캡처만의 아티팩트라는 증거입니다.
해결: 오해를 없애려면 캡처 동안만 오프로드를 끕니다 — ethtool -K eth0 gro off gso off tso off tx off 후 캡처, 진단이 끝나면 반드시 원복합니다(오프로드를 끄면 CPU·처리량 영향이 큽니다). 근본적으로는 로컬 tcpdump의 cksum incorrect와 MSS 초과 길이를 정상 아티팩트로 이해하고, 실제 회선 문제는 상대편·중간 지점 캡처로 판단합니다.
실무 활용 시나리오
1. 장애 사후 분석 (Post-mortem)
프로덕션 환경에서 간헐적 장애가 발생할 때, 평소에 핵심 서버 간 트래픽을 pcap으로 30분~1시간씩 순환 저장(ring buffer)해두면 장애 발생 직전의 패킷을 나중에 분석할 수 있습니다.
# 평소에 백그라운드로 순환 저장
sudo tcpdump -i eth0 -n 'port 3306 or port 6379' \
-w /data/captures/db_%Y%m%d_%H%M%S.pcap \
-G 1800 -W 4 &
# 30분(-G 1800초)마다 새 파일, 최대 4개 유지
2. 애플리케이션 프로토콜 디버깅
HTTP 라이브러리나 SDK가 올바른 헤더를 보내는지, Keep-Alive가 동작하는지, Content-Encoding이 제대로 설정되는지 tcpdump + -A 옵션으로 직접 확인할 수 있습니다.
3. 보안 감사
예상치 못한 외부 접속이나 이상한 포트로의 통신을 탐지할 때 사용합니다.
# 80/443 외 포트로 외부 통신하는 패킷 캡처
sudo tcpdump -i eth0 -n 'not port 80 and not port 443 and not port 22' \
and 'dst net not 10.0.0.0/8' -w /tmp/suspicious.pcap
클라우드 환경에서의 대안
클라우드 인스턴스에서 tcpdump를 직접 사용하기 어려운 경우:
| 클라우드 | 패킷 분석 도구 |
|---|---|
| AWS | VPC Traffic Mirroring, VPC Flow Logs |
| GCP | Packet Mirroring, VPC Flow Logs |
| Azure | Network Watcher Packet Capture |
VPC Flow Logs는 패킷 내용은 저장하지 않지만 IP/포트/바이트 수/허용여부를 기록합니다. 세부 디버깅이 필요하면 Traffic Mirroring을 사용합니다.
면접에서 자주 나오는 질문
- "TCP Retransmission이 대량 발생할 때 어떻게 진단하나요?"
- tcpdump로 패킷 캡처 → Retransmission 패턴 분석 → 대역폭 포화 vs 방화벽 DROP 구분 설명
- "SYN Flood 공격을 패킷 레벨에서 어떻게 탐지하나요?"
tcp[tcpflags] & tcp-syn != 0필터로 SYN만 필터링 → 단시간 대량의 다양한 소스 IP에서 SYN 확인
정리
| 개념 | 핵심 요약 |
|---|---|
| BPF 필터 | 커널 레벨에서 필터링, host/port/and/or/src/dst 조합 |
-n 옵션 | DNS 역조회 비활성화, 장애 시 빠른 분석에 필수 |
-w / -r | pcap 파일 저장 / 읽기, Wireshark와 연동 가능 |
| 3-Way Handshake | SYN → SYN-ACK → ACK, tcpdump에서 [S], [S.], [.]로 표시 |
| RST 즉시 수신 | 포트 미오픈 (서비스 없음) |
| 무응답 + SYN 재전송 | 방화벽 DROP 또는 라우팅 문제 |
| Retransmission 패턴 | 연속 대량이면 대역폭, Backoff 패턴이면 방화벽 DROP |
명령어·단축키 빠른 참조
이 모듈에서 다룬 tcpdump 옵션과 BPF 필터를 실전 조합과 함께 모았습니다. "예" 열을 그대로 써도 됩니다.
| 명령어/옵션 | 용도 | 자주 쓰는 예 |
|---|---|---|
tcpdump -i <iface> | 캡처할 인터페이스 지정 | sudo tcpdump -i eth0 -nn port 443 |
-n / -nn | DNS·포트 역조회 끄기(빠른 분석) | tcpdump -i eth0 -nn host 10.0.0.5 |
host <IP> and port <N> | BPF로 특정 호스트·포트만 | sudo tcpdump -i eth0 -nn host 10.0.0.5 and port 443 |
src / dst | 출발지·목적지 방향 필터 | tcpdump -i eth0 -n dst port 22 |
not / or | 제외·다중 조건 결합 | tcpdump -i eth0 -n port 80 or port 443 |
'tcp[tcpflags] & tcp-syn != 0' | SYN·RST만 골라 연결 시도·거부 추적 | sudo tcpdump -i eth0 -n 'tcp[tcpflags] & tcp-rst != 0' |
-w <파일> / -r <파일> | pcap 저장·재분석(Wireshark 연동) | tcpdump -i eth0 -w cap.pcap → tcpdump -r cap.pcap -n |
-c <N> / -s <바이트> | 패킷 수·캡처 길이 제한 | tcpdump -i eth0 -c 10000 -s 0 port 443 |
-C <MB> -W <개수> / -G <초> | 파일 순환 저장(디스크 풀 방지) | tcpdump -w rot.pcap -C 100 -W 5 |
-A / -X | 페이로드 ASCII·Hex 덤프 | sudo tcpdump -i eth0 -A port 80 | grep HTTP |
tcpdump -D | 캡처 가능한 인터페이스 목록 | sudo tcpdump -D |
ethtool -K <iface> … | 캡처 중 오프로드 끄기(cksum·거대세그먼트 착시 제거) | ethtool -K eth0 gro off gso off tso off |
관련 모듈로 더 깊이:
- netstat과 ss 명령어로 커넥션 상태(ESTABLISHED 등) 분석 — 패킷을 뜨기 전에 연결 상태를 ss로 먼저 확인하는 법
- ping과 ICMP 프로토콜을 이용한 초동 경로 진단 — 무응답 패킷이 방화벽 문제인지 ICMP로 교차 검증하는 법
- 웹 서버에서 DB 접속 실패 시 원인 격리 프로세스 — tcpdump 결과를 계층별 고립 진단에 끼워 넣는 법
다음 챕터에서는 VPN과 제로 트러스트 보안 아키텍처를 학습합니다.