네트워크 이슈를 디버깅하다 보면 이런 말을 자주 듣는다.
"덤프 떠서 확인해보세요."
"pcap 있나요?"
"SYN은 나갔는데 SYN/ACK가 돌아왔나요?"
처음 들으면 살짝 겁난다.
덤프라고 하니까 뭔가 커널 내부를 파헤쳐야 할 것 같고, 패킷이라고 하니까 TCP/IP 책을 다 외워야 할 것 같다.
하지만 시작은 그렇게 어렵지 않다.
tcpdump는 네트워크 인터페이스를 지나가는 패킷을 터미널에서 캡처하는 도구다. 화면에 바로 출력할 수도 있고, .pcap 파일로 저장해서 Wireshark 같은 도구로 다시 열어볼 수도 있다.
이 글은 tcpdump를 처음 쓰는 사람 기준으로,
- 무엇을 캡처해야 하는지
- 어떤 옵션부터 외우면 되는지
- 출력 한 줄을 어떻게 읽는지
- 장애 상황에서 어떤 패킷을 먼저 봐야 하는지
를 정리한다.
패킷 덤프가 뭔가
패킷 덤프는 말 그대로 네트워크로 오간 패킷을 기록한 것이다.
애플리케이션 로그가 이렇게 말한다면:
connection timeout
connection reset by peer
read failed
패킷 덤프는 그 아래에서 실제로 무슨 일이 있었는지 보여준다.
내가 SYN을 보냈는가?
상대가 SYN/ACK를 보냈는가?
3-way handshake가 끝났는가?
데이터를 보냈는데 ACK가 왔는가?
누가 FIN 또는 RST를 보냈는가?
즉 로그가 “증상”에 가깝다면, 패킷 덤프는 대화 기록에 가깝다.
먼저 설치 확인
Linux에서는 이미 설치되어 있는 경우가 많다.
tcpdump --version
없다면 배포판에 맞게 설치한다.
# Ubuntu / Debian
sudo apt update
sudo apt install tcpdump
# RHEL / CentOS / Rocky
sudo dnf install tcpdump
패킷 캡처는 보통 관리자 권한이 필요하다. 그래서 예제는 대부분 sudo를 붙인다.
인터페이스부터 확인하기
패킷은 네트워크 인터페이스를 기준으로 캡처한다.
먼저 어떤 인터페이스가 있는지 본다.
ip addr
또는 tcpdump로 캡처 가능한 인터페이스 목록을 볼 수 있다.
sudo tcpdump -D
예를 들어 서버에 이런 인터페이스가 있다고 하자.
1.eth0
2.lo
3.any
의미는 대략 이렇다.
eth0: 실제 Ethernet 인터페이스lo: localhost, 즉 자기 자신으로 오가는 트래픽any: 여러 인터페이스를 한 번에 보는 특수 인터페이스
처음에는 any가 편하다.
sudo tcpdump -i any
다만 정확히 어느 물리 포트로 들어오고 나가는지 봐야 한다면 eth0, ens33, enp0s3 같은 실제 인터페이스를 지정하는 편이 좋다.
첫 번째 명령어
가장 기본은 이거다.
sudo tcpdump -i any -nn
여기서 옵션은 두 개만 보면 된다.
-i any : 어떤 인터페이스에서 캡처할지 지정
-nn : IP/port를 이름으로 바꾸지 말고 숫자로 보여줌
-nn은 거의 습관처럼 붙이는 편이 좋다.
이 옵션이 없으면 80을 http로 바꾸거나, IP를 hostname으로 바꾸려고 할 수 있다. 그러면 DNS 조회 때문에 출력이 느려지거나, 오히려 분석이 헷갈릴 수 있다.
출력 한 줄 읽기
예를 들어 이런 줄이 나왔다고 하자.
11:21:03.123456 IP 192.168.0.10.54321 > 93.184.216.34.80: Flags [S], seq 1000, win 64240, length 0
처음에는 길어 보이지만 쪼개면 단순하다.
11:21:03.123456 시간
IP IPv4 패킷
192.168.0.10.54321 출발지 IP와 port
> 왼쪽에서 오른쪽으로 감
93.184.216.34.80 목적지 IP와 port
Flags [S] TCP SYN
seq 1000 TCP sequence number
win 64240 TCP receive window
length 0 TCP payload 길이
초보 단계에서는 모든 필드를 다 이해하지 않아도 된다.
처음에는 이 네 가지만 봐도 충분하다.
누가 보냈나? 192.168.0.10
누구에게 보냈나? 93.184.216.34
어떤 포트인가? 80
TCP flag가 뭔가? SYN
TCP flag 빠르게 보기
TCP 문제를 볼 때는 flag가 중요하다.
자주 보는 값은 이 정도다.
[S] SYN 연결 시작 요청
[S.] SYN/ACK 연결 요청에 대한 응답
[.] ACK 확인 응답
[P.] PSH/ACK 데이터 전송이 포함된 경우가 많음
[F.] FIN/ACK 정상 종료 요청
[R] RST 강제 종료
TCP 연결 시작은 보통 이렇게 보인다.
client > server: Flags [S]
server > client: Flags [S.]
client > server: Flags [.]
이게 흔히 말하는 3-way handshake다.
만약 클라이언트가 [S]를 여러 번 보내는데 [S.]가 안 돌아오면:
- 서버까지 패킷이 안 갔거나
- 서버가 응답하지 않거나
- 중간 방화벽이 막았거나
- 응답이 돌아오는 경로에 문제가 있을 수 있다
반대로 [S.]는 왔는데 마지막 ACK가 안 보이면 클라이언트 쪽이나 캡처 위치를 다시 봐야 한다.
너무 많이 보이면 필터를 건다
운영 서버에서 그냥 tcpdump -i any -nn을 치면 화면이 순식간에 지나간다.
그래서 필터를 걸어야 한다.
특정 host만 보고 싶으면:
sudo tcpdump -i any -nn host 192.168.0.10
특정 port만 보고 싶으면:
sudo tcpdump -i any -nn port 443
TCP만 보고 싶으면:
sudo tcpdump -i any -nn tcp
UDP 53번, 즉 DNS만 보고 싶으면:
sudo tcpdump -i any -nn udp port 53
조건은 조합할 수 있다.
sudo tcpdump -i any -nn 'host 192.168.0.10 and tcp port 443'
여기서 따옴표를 붙이는 이유는 and, or, 괄호 같은 표현이 shell에서 먼저 해석되지 않게 하기 위해서다.
방향까지 좁히기
출발지와 목적지를 나눠 보고 싶을 때가 있다.
특정 IP에서 나가는 패킷:
sudo tcpdump -i any -nn 'src host 192.168.0.10'
특정 IP로 들어오는 패킷:
sudo tcpdump -i any -nn 'dst host 192.168.0.10'
특정 서버의 443번 포트로 들어가는 TCP:
sudo tcpdump -i any -nn 'dst host 10.0.0.5 and tcp dst port 443'
필터는 영어 문장처럼 읽으면 된다.
dst host 10.0.0.5 and tcp dst port 443
= 목적지가 10.0.0.5이고, TCP 목적지 포트가 443인 패킷
개수 제한하기
처음 연습할 때는 무한정 캡처하지 말고 개수를 제한하는 게 좋다.
sudo tcpdump -i any -nn -c 20
-c 20 : 20개 패킷만 캡처하고 종료
장애 재현이 짧다면 이렇게 잡아도 된다.
sudo tcpdump -i any -nn -c 100 'host 192.168.0.10 and port 443'
파일로 저장하기
화면 출력은 빠르게 보기에는 좋지만, 분석에는 파일 저장이 더 좋다.
sudo tcpdump -i any -nn -w capture.pcap 'host 192.168.0.10 and port 443'
-w capture.pcap : 화면에 풀어서 출력하지 않고 pcap 파일로 저장
이렇게 저장한 파일은 나중에 다시 읽을 수 있다.
tcpdump -nn -r capture.pcap
또는 Wireshark에서 열어도 된다.
File -> Open -> capture.pcap
실무에서는 보통 서버에서 tcpdump로 파일을 뜨고, 내 PC로 가져와서 Wireshark로 자세히 본다.
payload까지 저장하고 싶을 때
예전 자료를 보면 -s 0 옵션이 자주 나온다.
sudo tcpdump -i any -nn -s 0 -w capture.pcap 'host 192.168.0.10'
-s는 snap length, 즉 패킷 하나에서 몇 바이트까지 캡처할지 정하는 옵션이다.
현대 tcpdump에서는 기본 snap length가 충분히 큰 경우가 많지만, 환경마다 다를 수 있다. 초보자 입장에서는 “payload가 잘려 보이면 -s 0 또는 충분히 큰 값으로 캡처한다” 정도로 기억하면 된다.
단, payload까지 저장하면 민감한 정보가 들어갈 수 있다.
- HTTP body
- 인증 토큰
- 내부 API payload
- 개인정보
HTTPS처럼 암호화된 트래픽은 내용이 바로 보이지 않지만, IP/port/연결 패턴 같은 메타데이터는 여전히 남는다.
tcpdump와 Wireshark 역할 나누기
둘 중 하나만 써야 하는 건 아니다.
tcpdump는 서버에서 캡처하기 좋다.
- 터미널만 있으면 됨
- 원격 서버에서 바로 실행 가능
- 필터를 걸어 작은 pcap을 만들기 좋음
Wireshark는 캡처 후 분석하기 좋다.
- TCP stream 따라가기
- 재전송, 중복 ACK, RST 같은 힌트 보기
- 프로토콜 필드 계층적으로 펼쳐보기
흐름은 보통 이렇다.
서버에서 tcpdump로 pcap 저장
|
v
로컬 PC로 pcap 복사
|
v
Wireshark로 열어서 상세 분석
장애 상황별로 먼저 볼 것
1. connection timeout
timeout은 “응답을 기다렸는데 안 왔다”에 가깝다.
먼저 SYN 흐름을 본다.
sudo tcpdump -i any -nn 'host 10.0.0.5 and tcp port 443'
볼 포인트:
클라이언트가 SYN을 보내는가?
서버가 SYN/ACK를 보내는가?
SYN이 같은 간격으로 재전송되는가?
예를 들어 이런 식이면 서버 응답이 안 보이는 상태다.
client > server: Flags [S]
client > server: Flags [S]
client > server: Flags [S]
이때는 서버 프로세스보다 먼저 네트워크 경로, 방화벽, 라우팅, 보안그룹을 의심한다.
2. connection refused
refused는 보통 상대가 RST를 돌려주는 상황이다.
client > server: Flags [S]
server > client: Flags [R.]
의미는 대략 이렇다.
서버까지는 갔다.
그런데 해당 port를 받아줄 프로세스가 없거나, OS/방화벽이 명시적으로 거절했다.
이때는 서버에서 프로세스가 listen 중인지 본다.
ss -lntp
3. 중간에 끊김
연결이 되었다가 중간에 끊기면 FIN/RST를 본다.
sudo tcpdump -i any -nn 'host 10.0.0.5 and tcp port 443'
볼 포인트:
누가 FIN을 먼저 보냈나?
누가 RST를 보냈나?
RST 직전에 재전송이나 window 문제가 있었나?
정상 종료는 대개 FIN이 오간다.
강제 종료나 비정상 종료는 RST가 보이는 경우가 많다.
4. DNS가 이상함
도메인 접속이 안 되면 DNS부터 따로 본다.
sudo tcpdump -i any -nn 'udp port 53'
볼 포인트:
DNS query가 나가는가?
DNS response가 돌아오는가?
응답 IP가 기대한 값인가?
DNS 문제가 있으면 TCP 연결 자체를 보기 전에 이미 목적지 IP를 못 찾고 있을 수 있다.
캡처 위치가 중요하다
같은 장애라도 어디서 캡처하느냐에 따라 보이는 것이 다르다.
Client ---- Firewall ---- Server
클라이언트에서 보면 SYN이 나갔다.
Client capture:
client > server: SYN
client > server: SYN retransmission
서버에서 보면 아무것도 안 보인다.
Server capture:
no packet
이 경우 서버 애플리케이션 문제가 아니라 중간 경로 문제일 가능성이 커진다.
반대로 서버에서는 SYN이 보이고 SYN/ACK도 나갔는데, 클라이언트에서는 SYN/ACK가 안 보일 수 있다.
Server capture:
client > server: SYN
server > client: SYN/ACK
Client capture:
client > server: SYN
client > server: SYN retransmission
이 경우는 돌아오는 경로를 봐야 한다.
패킷 덤프의 핵심은 “한 곳에서만 보고 단정하지 않는 것”이다.
운영 서버에서 조심할 점
패킷 캡처는 생각보다 민감하다.
운영 서버에서는 아래를 조심한다.
- 필터 없이 오래 캡처하지 않는다.
- 파일 크기를 확인한다.
- payload에 민감 정보가 들어갈 수 있음을 기억한다.
- 장애 재현 시간만 짧게 캡처한다.
- 공유할 때는 IP, 도메인, 토큰, payload를 마스킹한다.
파일 크기가 걱정되면 port와 host를 최대한 좁힌다.
sudo tcpdump -i any -nn -s 0 -w api-443.pcap \
'host 10.0.0.5 and tcp port 443'
오래 떠야 한다면 파일 회전 옵션을 검토한다.
sudo tcpdump -i any -nn -s 0 \
-C 100 -W 5 -w capture.pcap \
'host 10.0.0.5 and tcp port 443'
-C 100 : 파일 하나를 약 100MB 단위로 회전
-W 5 : 최대 5개 파일 유지
초보자용 치트시트
처음에는 아래만 기억해도 충분하다.
# 전체를 빠르게 보기
sudo tcpdump -i any -nn
# 20개만 보고 종료
sudo tcpdump -i any -nn -c 20
# 특정 IP만 보기
sudo tcpdump -i any -nn host 192.168.0.10
# 특정 port만 보기
sudo tcpdump -i any -nn port 443
# 특정 IP + TCP port
sudo tcpdump -i any -nn 'host 192.168.0.10 and tcp port 443'
# DNS 보기
sudo tcpdump -i any -nn 'udp port 53'
# pcap 저장
sudo tcpdump -i any -nn -s 0 -w capture.pcap 'host 192.168.0.10'
# pcap 다시 읽기
tcpdump -nn -r capture.pcap
처음 분석할 때의 순서
패킷을 열었는데 어디서부터 봐야 할지 모르겠다면 이 순서로 보면 된다.
1. 내가 캡처한 위치가 맞는가?
2. 대상 IP와 port가 맞는가?
3. DNS query/response는 정상인가?
4. TCP SYN이 나가는가?
5. SYN/ACK가 돌아오는가?
6. 누가 FIN/RST를 먼저 보내는가?
7. retransmission이 반복되는 구간이 있는가?
8. window size가 0으로 막히는 구간이 있는가?
이 순서만 따라가도 “애플리케이션 문제인지, 네트워크 경로 문제인지, 상대 서버 문제인지”를 꽤 많이 좁힐 수 있다.
정리
tcpdump는 어렵게 보이지만, 처음에는 몇 가지 패턴만 익히면 된다.
-i로 인터페이스를 고른다.-nn으로 숫자 그대로 본다.host,port,tcp,udp로 필터를 건다.-w로 pcap을 저장한다.- Wireshark로 열어서 TCP 흐름을 자세히 본다.
중요한 건 모든 패킷을 다 해석하는 능력이 아니다.
처음에는 이 질문에 답하는 것만으로도 충분하다.
내 패킷은 나갔나?
상대 응답은 돌아왔나?
누가 먼저 끊었나?
어디까지는 보이고, 어디부터 안 보이나?
네트워크 디버깅은 이 질문들을 하나씩 지워가는 작업이다.