패킷 분석
tcpdump와 Wireshark로 실제로 오간 패킷을 잡아 읽는 방법을 정리합니다. 캡처 필터와 표시 필터의 차이, tcpdump의 플래그 표기를 읽어 SYN 재전송을 찾는 코드, 두 위치의 캡처로 가설을 가르는 방법, TLS 트래픽에서 볼 수 있는 것과 키 로그로 푸는 방법, 오프로딩과 개인정보 같은 캡처의 함정을 다룹니다.
앞 글의 도구들은 연결이 되는지, 응답이 오는지를 알려 주지만, 그 사이에 실제로 어떤 패킷이 오갔는지는 보여 주지 않습니다. 패킷 캡처는 네트워크 인터페이스를 지나간 패킷을 그대로 기록해 보여 주므로, 다른 도구들이 짐작하게 해 주던 것을 직접 확인하게 해 줍니다. 이 교재에서 배운 핸드셰이크와 재전송, RST, 윈도우가 실제 패킷에서 어떻게 보이는지 확인하는 것으로 네트워크 교재를 마무리합니다.
tcpdump로 잡는다
tcpdump는 Linux와 macOS에서 쓰는 명령줄 캡처 도구입니다. 인터페이스를 지나는 모든 패킷을 볼 수 있어야 하므로 관리자 권한이나 CAP_NET_RAW 권한이 필요합니다.
sudo tcpdump -i eth0 -nn port 443 # 443번 포트, 주소와 포트를 이름으로 바꾸지 않는다
sudo tcpdump -i any -nn host 198.51.100.20 # 모든 인터페이스에서 이 상대와의 패킷
sudo tcpdump -i eth0 -nn -w capture.pcap port 443 # 파일로 저장한다(Wireshark로 열 수 있다)
tcpdump -nn -r capture.pcap # 저장한 파일을 읽는다조건을 적는 필터는 BPF 문법을 씁니다. TCP 헤더의 플래그 바이트를 직접 검사할 수도 있습니다.
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn' # ACK 없는 SYN, 곧 연결 시도
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0' # RST
sudo tcpdump -i eth0 -nn 'udp port 53' # DNS의 질의와 응답
sudo tcpdump -i eth0 -nn net 10.0.0.0/24 # 한 대역의 트래픽
sudo tcpdump -i eth0 -nn -A port 80 # 평문 HTTP를 글자로 본다출력의 한 줄은 시각, 출발지와 목적지의 주소와 포트, 플래그, 순서 번호, 길이로 이루어집니다. tcpdump의 설명서에 따르면 플래그는 S(SYN), F(FIN), P(PSH), R(RST)과 ACK를 뜻하는 .의 조합으로 표시되므로, [S]는 SYN, [S.]는 SYN+ACK, [.]는 ACK만, [P.]는 데이터를 실은 ACK, [R]은 RST입니다.
SYN 재전송을 읽는다
다음은 클라이언트 C(192.0.2.10:50000)에서 서버 S(198.51.100.20:443)로 연결을 시도하는 동안 C의 인터페이스에서 잡았다고 가정한 출력입니다. 실제 캡처가 아니라 설명을 위해 만든 것이며, 절대 순서 번호(-S)로 찍고 시각은 첫 패킷부터 지난 초로 적었다고 하겠습니다. 실제 -ttttt는 같은 상대 시각을 00:00:01.000000처럼 시, 분, 초로 찍으므로, 실제 출력에 이 코드를 쓰려면 시각을 읽는 부분을 그 형식에 맞게 고쳐야 합니다. 다음 코드는 이 줄들에서 플래그를 읽어 흐름을 정리합니다.
import re
lines = """\
0.000000 IP 192.0.2.10.50000 > 198.51.100.20.443: Flags [S], seq 1000, length 0
1.000000 IP 192.0.2.10.50000 > 198.51.100.20.443: Flags [S], seq 1000, length 0
3.000000 IP 192.0.2.10.50000 > 198.51.100.20.443: Flags [S], seq 1000, length 0
3.040000 IP 198.51.100.20.443 > 192.0.2.10.50000: Flags [S.], seq 9000, ack 1001, length 0
3.041000 IP 192.0.2.10.50000 > 198.51.100.20.443: Flags [.], ack 9001, length 0
""".splitlines()
NAMES = {"S": "SYN", "F": "FIN", "P": "PSH", "R": "RST", ".": "ACK"}
pattern = re.compile(r"^(\S+) IP (\S+) > (\S+): Flags \[([^\]]*)\]")
events = []
for line in lines:
t, src, dst, flags = pattern.match(line).groups()
events.append((float(t), "+".join(NAMES[f] for f in flags)))
print(f"{float(t):.3f}초 {src} -> {dst} {events[-1][1]}")
syn_only = [e for e in events if e[1] == "SYN"]
established = next(t for t, name in events if name == "ACK")
print(f"SYN {len(syn_only)}번 가운데 {len(syn_only) - 1}번은 재전송, 연결까지 {established:.3f}초")0.000초 192.0.2.10.50000 -> 198.51.100.20.443 SYN
1.000초 192.0.2.10.50000 -> 198.51.100.20.443 SYN
3.000초 192.0.2.10.50000 -> 198.51.100.20.443 SYN
3.040초 198.51.100.20.443 -> 192.0.2.10.50000 SYN+ACK
3.041초 192.0.2.10.50000 -> 198.51.100.20.443 ACK
SYN 3번 가운데 2번은 재전송, 연결까지 3.041초세 SYN의 순서 번호가 모두 1000이므로 같은 SYN을 다시 보낸 것이고, 간격이 1초, 2초로 늘어나는 것은 재전송 타이머가 두 배씩 늘어나기 때문입니다. 세 번째 SYN 뒤 40ms 만에 SYN+ACK가 왔으므로 RTT는 대략 40ms이고, 사용자는 그보다 75배나 긴 3초를 기다린 셈입니다. "가끔 접속이 3초씩 걸린다"는 증상이 이런 모양으로 나타납니다. SYN+ACK의 ACK 1001은 SYN이 순서 번호 하나를 차지하기 때문이지만, 세 SYN의 번호가 같으므로 이 ACK가 어느 SYN에 대한 답인지는 알 수 없습니다.
처음 두 SYN에 답이 없었던 이유로는 적어도 두 가지 가설이 있습니다. 첫째는 SYN이 S까지 가지 못했다는 것이고, 둘째는 S가 SYN을 받아 SYN+ACK를 보냈지만 그 응답이 C로 돌아오지 못했다는 것입니다. C에서 잡은 캡처만으로는 둘을 가를 수 없습니다. C의 인터페이스에서 SYN이 나가는 것을 보았다고 그것이 S에 닿았다는 뜻은 아니기 때문입니다. 같은 시각에 S에서도 캡처해, 처음 두 SYN이 S에 도착했는지, S가 그에 대한 SYN+ACK를 보냈는지를 보아야 합니다. S에 SYN이 없다면 가는 길의 문제이고, S가 SYN+ACK를 보냈는데 C에 없다면 돌아오는 길의 문제입니다.
두 위치의 캡처를 비교할 때는 두 컴퓨터의 시계가 맞는지, 같은 연결을 보고 있는지부터 확인합니다. 사이에 4장 3편의 NAT가 있다면 양쪽에서 보이는 주소와 포트가 다르므로 그 대응을 알아야 하고, TCP를 끝내는 프록시가 있다면 프록시의 앞과 뒤는 아예 다른 연결입니다. 어느 위치에 패킷이 보이지 않는다는 것만으로 방화벽이 버렸다고 단정하지 말고, 그 위치의 캡처가 패킷을 놓치지 않았는지도 확인해야 합니다.
더 깊이: SYN을 다시 보내는 간격은 운영체제마다 다르다
앞의 예처럼 간격이 1초, 2초로 두 배씩 늘어나는 것은 재전송 타이머를 지수적으로 늘리는 RFC 6298의 방식을 따른 모양입니다. 그런데 Linux는 6.5부터 연결을 시작하는 쪽이 보내는 SYN의 재전송 가운데 처음 몇 번을 같은 간격으로 보냅니다. 커널 문서에 따르면 tcp_syn_linear_timeouts의 기본값은 4이고, 처음 RTO가 1초라면 SYN의 RTO는 1, 1, 1, 1, 1, 2, 4초로 이어집니다. 이 변경을 넣은 커널 커밋은 macOS와 iOS가 10년 넘게 써 온 같은 간격을 따랐다고 밝히며, 링크가 고장 났을 때 라우터가 다른 경로를 찾는 동안 더 자주 시도해 연결을 빨리 되살리려는 것이라고 설명합니다.
그래서 같은 손실이라도 기다리는 시간이 클라이언트에 따라 다릅니다. 처음 SYN 두 개가 사라졌다면 앞의 예처럼 지수적으로 늘리는 클라이언트는 3초쯤 뒤에 연결되지만, 기본값을 쓰는 Linux 6.5 이후의 클라이언트는 2초쯤 뒤에 연결됩니다. 캡처에서 SYN의 간격을 읽을 때는 보낸 쪽의 운영체제와 커널 버전, 그리고 tcp_syn_retries와 tcp_syn_linear_timeouts 같은 설정을 함께 확인해야 합니다. 서버가 보내는 SYN+ACK의 재전송에는 이 설정이 적용되지 않습니다.
Wireshark와 두 가지 필터
Wireshark는 캡처한 패킷을 프로토콜마다 풀어 보여 주는 GUI 도구로, tcpdump로 저장한 파일도 열 수 있습니다. 여기서 두 가지 필터를 구별해야 합니다. 캡처 필터(capture filter)는 tcpdump와 같은 BPF 문법으로 저장할 패킷을 고르고, 표시 필터(display filter)는 Wireshark 고유의 문법으로 이미 저장한 패킷 가운데 화면에 보일 것을 고릅니다.
저장하지 않은 패킷은, 표시 필터로 되살릴 수 없습니다
인터페이스에 가상 패킷 후보 세 개가 있습니다. 저장 파일에 표시 필터 tcp.port == 443을 적용합니다. 각 단계의 값과 전환 이유를 비교합니다.
패킷 분석 · 필터가 적용되는 지점
저장하지 않은 패킷은, 표시 필터로 되살릴 수 없습니다
아래 TCP 443·UDP 443·UDP 53은 필터 차이를 설명하는 가상 후보입니다. 캡처 필터는 저장할 대상을 줄이고, 표시 필터는 저장된 파일에서 보일 대상을 고릅니다.
관측 후보 · 인터페이스에 도착
A · TCP 443/B · UDP 443/C · UDP 53프로토콜과 포트가 함께 조건을 결정합니다.
캡처 필터 · 파일에 저장
예시 조건:
tcp port 443저장 후보: A만 포함합니다. B의 QUIC 후보와 C의 DNS 후보는 제외됩니다.
표시 필터 · 파일에서 선택
예시 조건:
tcp.port == 443표시 후보: 저장된 A입니다. 조건을 바꿔도 파일에 없는 B·C는 복구할 수 없습니다.
두 필터 문법을 서로 바꿔 쓰지 않습니다. 이는 가상 필터 예이며 실제 패킷 캡처가 아닙니다. 인터페이스·방향·시각·snaplen·캡처 손실과 암호화는 추가 관측 범위를 제한합니다. 포트 번호만으로 앱 프로토콜을 확정하지 않습니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 관측 후보부터 표시 선택까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 관계 설명 | 가상 후보 A TCP 443, B UDP 443, C UDP 53의 여섯 노드와 다섯 간선입니다. capture 조건 tcp port 443과 일치하는 A만 파일에 저장하고, 불일치하는 B·C는 저장되지 않습니다. 파일 안의 display 조건 tcp.port 443은 저장된 A를 선택하며 없는 B·C를 복구하지 않습니다. 포트만으로 실제 앱 프로토콜을 확정한 그림은 아닙니다. 모바일 일치 경로는 저장 안 됨 노드를 통과하지 않습니다. 실제 패킷 캡처나 도구 실행은 하지 않았습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 관측 후보 인터페이스에 가상 패킷 후보 세 개가 있습니다. |
|
|
| 캡처 저장 캡처 필터 tcp port 443으로 저장합니다. |
|
|
| 표시 선택 저장 파일에 표시 필터 tcp.port == 443을 적용합니다. |
|
|
저장하지 않은 패킷은, 표시 필터로 되살릴 수 없습니다
인터페이스에 가상 패킷 후보 세 개가 있습니다. 저장 파일에 표시 필터 tcp.port == 443을 적용합니다. 각 단계의 값과 전환 이유를 비교합니다.
패킷 분석 · 필터가 적용되는 지점
저장하지 않은 패킷은, 표시 필터로 되살릴 수 없습니다
아래 TCP 443·UDP 443·UDP 53은 필터 차이를 설명하는 가상 후보입니다. 캡처 필터는 저장할 대상을 줄이고, 표시 필터는 저장된 파일에서 보일 대상을 고릅니다.
관측 후보 · 인터페이스에 도착
A · TCP 443/B · UDP 443/C · UDP 53프로토콜과 포트가 함께 조건을 결정합니다.
캡처 필터 · 파일에 저장
예시 조건:
tcp port 443저장 후보: A만 포함합니다. B의 QUIC 후보와 C의 DNS 후보는 제외됩니다.
표시 필터 · 파일에서 선택
예시 조건:
tcp.port == 443표시 후보: 저장된 A입니다. 조건을 바꿔도 파일에 없는 B·C는 복구할 수 없습니다.
두 필터 문법을 서로 바꿔 쓰지 않습니다. 이는 가상 필터 예이며 실제 패킷 캡처가 아닙니다. 인터페이스·방향·시각·snaplen·캡처 손실과 암호화는 추가 관측 범위를 제한합니다. 포트 번호만으로 앱 프로토콜을 확정하지 않습니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 관측 후보부터 표시 선택까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 관계 설명 | 가상 후보 A TCP 443, B UDP 443, C UDP 53의 여섯 노드와 다섯 간선입니다. capture 조건 tcp port 443과 일치하는 A만 파일에 저장하고, 불일치하는 B·C는 저장되지 않습니다. 파일 안의 display 조건 tcp.port 443은 저장된 A를 선택하며 없는 B·C를 복구하지 않습니다. 포트만으로 실제 앱 프로토콜을 확정한 그림은 아닙니다. 모바일 일치 경로는 저장 안 됨 노드를 통과하지 않습니다. 실제 패킷 캡처나 도구 실행은 하지 않았습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 관측 후보 인터페이스에 가상 패킷 후보 세 개가 있습니다. |
|
|
| 캡처 저장 캡처 필터 tcp port 443으로 저장합니다. |
|
|
| 표시 선택 저장 파일에 표시 필터 tcp.port == 443을 적용합니다. |
|
|
캡처 필터를 좁게 걸면 파일이 작아지지만, 걸러진 패킷은 처음부터 저장되지 않았으므로 나중에 어떤 표시 필터로도 되살릴 수 없습니다. 그림처럼 tcp port 443으로 캡처하면 같은 443번 포트라도 UDP를 쓰는 HTTP/3의 QUIC 패킷과 DNS 조회는 빠집니다. 원인을 아직 모를 때는 캡처 필터를 넓게 걸고 표시 필터로 좁혀 가는 편이 안전합니다.
| 목적 | 표시 필터 | 고르는 범위 |
|---|---|---|
| 한 주소와의 패킷 | ip.addr == 192.0.2.10 | 출발지나 목적지가 그 IPv4 주소(IPv6는 ipv6.addr) |
| TCP 443 | tcp.port == 443 | TCP의 출발지나 목적지 포트가 443(QUIC은 제외) |
| SYN이 켜진 패킷 | tcp.flags.syn == 1 | SYN과 SYN+ACK 모두 |
| 연결 시도만 | tcp.flags.syn == 1 && tcp.flags.ack == 0 | ACK 없는 SYN |
| RST | tcp.flags.reset == 1 | 연결을 끊은 패킷 |
| 재전송으로 보이는 것 | tcp.analysis.retransmission | Wireshark가 재전송으로 판단한 패킷 |
| 제로 윈도우 | tcp.analysis.zero_window | 수신 윈도우가 0인 패킷 |
| DNS 질의 이름 | dns.qry.name contains "example" | 그 문자열이 든 질의 |
| HTTP 오류 응답 | http.response.code >= 400 | 평문 HTTP로 풀린 오류 응답 |
tcp.analysis.로 시작하는 필드는 패킷에 적힌 값이 아니라 Wireshark가 앞뒤 패킷을 보고 내린 판단입니다. 캡처가 패킷을 놓쳤을 때도 재전송처럼 보일 수 있으므로, 같은 판단이 여러 번 되풀이되는지와 다른 위치의 캡처를 함께 봅니다. 평문 HTTP/1.1이라면 패킷을 고르고 Follow TCP Stream을 하면 한 연결에서 오간 내용을 대화처럼 이어 볼 수 있습니다.
TLS 트래픽에서 볼 수 있는 것
HTTPS의 패킷을 잡으면 HTTP의 내용 대신 암호화된 TLS 레코드만 보입니다. 그래도 볼 수 있는 것이 있습니다. ClientHello에서는 지원하는 버전과 암호 조합, ALPN, 그리고 ECH를 쓰지 않는다면 접속하려는 도메인 이름인 SNI가 보이고, ServerHello에서는 서버가 고른 결과가 보입니다. 10장 2편에서 본 것처럼 TLS 1.3에서는 인증서가 ServerHello 뒤에 암호화되어 오므로, 캡처로는 서버가 어떤 인증서를 보냈는지 볼 수 없습니다. 핸드셰이크가 어디서 멈췄는지, 누가 경고(alert)를 보냈는지는 볼 수 있으므로, TLS의 실패를 진단할 때는 이것만으로도 쓸모가 큽니다.
내용까지 봐야 한다면, 클라이언트가 만든 세션 키를 파일로 남기게 해서 Wireshark에 넘겨 줍니다. 주요 브라우저와 curl은 SSLKEYLOGFILE 환경 변수에 적힌 파일에 키를 기록하고, Wireshark는 그 파일로 캡처의 TLS를 풀어 HTTP를 보여 줍니다. 이 파일만 있으면 그 기간의 모든 통신을 풀 수 있으므로, 진단이 끝나면 지우고 남에게 넘기지 않아야 합니다.
자주 만나는 모양
| 캡처에 보이는 것 | 뜻 | 다음에 볼 것 |
|---|---|---|
| SYN에 바로 RST | 그 포트에서 듣는 프로세스가 없거나 거부했다 | ss -tlnp, RST를 보낸 쪽이 서버인지 중간 장비인지 |
| SYN만 되풀이 | 이 위치에서 응답을 보지 못했다 | 반대쪽의 캡처, 방화벽, 경로와 돌아오는 길 |
| 재전송이 많다 | 손실이 있거나 순서가 바뀌었다 | 손실이 시작된 위치, 같은 시각의 링크 오류 |
| 제로 윈도우 | 받는 쪽 버퍼가 가득 찼다 | 받는 애플리케이션이 데이터를 읽고 있는지 |
| 큰 패킷만 사라진다 | 경로 MTU의 문제일 수 있다 | Fragmentation Needed가 오는지, 막혔는지 |
| 502와 함께 뒤쪽 연결의 RST | 프록시와 뒤쪽 서버 사이의 연결이 끊겼다 | 뒤쪽 서버의 유휴 시간과 프록시의 연결 재사용 |
| DNS 응답이 늦다 | 질의의 재시도나 리졸버의 지연 | 질의와 응답의 시각, 재시도의 간격 |
캡처의 함정
캡처는 사실을 보여 주지만, 어디서 어떻게 잡았는지를 모르면 그 사실을 잘못 읽게 됩니다. 몇 가지를 알아 둡니다.
보내는 컴퓨터에서 잡으면, MTU가 1500인데도 길이가 수만 바이트인 TCP 패킷이 보일 수 있습니다. 운영체제가 큰 덩어리를 네트워크 카드에 넘기면 카드가 MTU에 맞게 잘라 보내는 오프로딩(TSO) 때문인데, 캡처는 자르기 전의 덩어리를 보기 때문입니다. 카드가 자르지 못하면 커널이 같은 일을 소프트웨어로 하며, 이를 GSO라고 합니다. 받는 쪽에서도 여러 패킷을 하나로 합쳐 위로 올리는 GRO 때문에 같은 일이 생깁니다. 실제로 회선 위의 패킷 크기를 보고 싶다면 중간의 장비에서 잡거나, 진단하는 동안만 ethtool -K eth0 tso off gso off gro off로 오프로딩을 끕니다.
바쁜 서버에서 캡처하면 tcpdump가 패킷을 다 받아 적지 못해 일부를 놓칠 수 있으며, 끝낼 때 packets dropped by kernel로 그 수를 알려 줍니다. 놓친 패킷이 있다면 그 캡처에서 "패킷이 없다"는 결론을 내리면 안 되므로, 필터를 좁히거나 -s로 패킷마다 저장하는 길이를 줄여 부담을 낮춥니다. 캡처 파일에는 평문 쿠키나 토큰, 개인정보가 들어 있을 수 있으므로, 남에게 넘기기 전에 내용을 확인하고, 필요하면 헤더만 남기거나 민감한 부분을 지운 뒤 넘깁니다.
결국 패킷 분석의 핵심은 짐작하지 않고 확인하는 것입니다. "아마 방화벽 문제일 거야"에서 멈추지 않고, "C에서 SYN이 나갔고 S에서는 그 SYN이 보이지 않으니 가는 길의 어딘가에서 사라졌다"까지 말할 수 있어야 합니다. 그리고 그 판단에는 어디서, 언제, 어떤 필터로 잡은 캡처인지를 함께 적어 둡니다.
정리
- tcpdump는 BPF 필터로 패킷을 잡아 보여 주거나 파일로 저장하며, 플래그는
S,F,P,R과 ACK의.로 표시됩니다. - 순서 번호가 같은 SYN이 1초, 2초 간격으로 되풀이되면 재전송이며, 이것이 "가끔 접속이 몇 초씩 걸린다"는 증상의 흔한 모양입니다.
- 한 위치의 캡처로는 패킷이 가는 길에서 사라졌는지 돌아오는 길에서 사라졌는지 가를 수 없으므로, 반대쪽에서도 잡아 시계와 NAT, 프록시를 맞춰 비교합니다.
- 캡처 필터로 걸러진 패킷은 저장되지 않아 표시 필터로 되살릴 수 없으므로, 원인을 모를 때는 넓게 잡고 표시 필터로 좁힙니다.
- TLS 트래픽에서도 ClientHello와 ServerHello, 경고는 보이며, 내용은
SSLKEYLOGFILE의 키로 풀 수 있지만 그 파일은 엄격히 관리해야 합니다. - 오프로딩은 MTU보다 큰 패킷을 보이게 하고, 바쁜 서버의 캡처는 패킷을 놓칠 수 있으며, 캡처 파일에는 민감한 정보가 들어 있을 수 있습니다.
연습 문제
1. 필터가 숨기는 것
본문의 다섯 줄을 Wireshark로 열어 tcp.flags.syn == 1을 걸면 몇 번째 줄들이 보이나요? tcp.flags.syn == 1 && tcp.flags.ack == 0을 걸면 어떻게 되나요? 두 번째 필터로 본 화면만 보고 "연결이 계속 맺어지지 않는다"고 판단하면 무엇이 잘못되나요?
해설 보기
첫 필터는 SYN 비트가 켜진 패킷을 모두 고르므로 SYN 세 개와 SYN+ACK까지, 첫째부터 넷째 줄이 보입니다. 두 번째 필터는 ACK가 없는 SYN만 고르므로 첫째부터 셋째 줄만 보입니다. 이 화면에는 SYN만 되풀이되는 것처럼 보이지만, 걸러진 넷째와 다섯째 줄에서 실제로는 3초 만에 연결이 맺어졌습니다. 필터를 걸어 본 화면은 고른 패킷만 보여 주므로, 결론을 내리기 전에 필터를 풀거나 같은 주소와 포트의 양방향 패킷을 모두 보는 필터로 바꿔 뒤이은 응답이 있는지 확인해야 합니다.
2. 두 번째 캡처로 가설을 가르기
본문의 상황에서 같은 시각에 서버 S의 인터페이스에서도 캡처했습니다. 첫 번째 가설(SYN이 S에 닿지 못했다)과 두 번째 가설(S의 응답이 돌아오지 못했다)이 각각 맞다면 S의 캡처에는 무엇이 보여야 하나요?
해설 보기
첫 번째 가설이 맞다면 S의 캡처에는 0초와 1초쯤의 SYN이 보이지 않고, 3초쯤의 세 번째 SYN만 도착해 있어야 합니다. 두 번째 가설이 맞다면 S의 캡처에는 세 SYN이 모두 도착해 있고, S가 각각에 대해 SYN+ACK를 보낸 것도 보여야 하며, 그 가운데 앞의 두 응답이 C의 캡처에는 없어야 합니다. S에 SYN은 도착했는데 SYN+ACK를 보내지 않았다면 둘 다 아니며, S의 대기열이 넘쳤거나 S의 방화벽이 응답을 막았을 수 있습니다. 비교하기 전에 두 컴퓨터의 시계 차이와, 사이에 NAT가 있어 주소와 포트가 바뀌지 않았는지를 확인합니다.
3. MTU보다 큰 패킷
MTU가 1500인 서버에서 tcpdump로 잡았더니 length 14480인 TCP 패킷이 보입니다. 이 서버가 회선으로 14KB짜리 패킷을 보내고 있는 것인가요? 회선 위의 실제 크기를 보려면 어떻게 해야 하나요?
해설 보기
아닙니다. 운영체제가 큰 덩어리를 네트워크 카드에 넘기고 카드가 MTU에 맞게 자르는 TSO 때문에, 캡처가 자르기 전의 덩어리를 본 것입니다. 실제로 회선에는 1460바이트 안팎의 세그먼트 열 개쯤으로 나뉘어 나갑니다. 회선 위의 실제 크기를 보려면 받는 쪽이나 중간의 스위치에서 미러링한 포트로 잡거나, 진단하는 동안만 ethtool -K eth0 tso off gso off로 오프로딩을 끄고 다시 잡습니다. 오프로딩을 끄면 CPU를 더 쓰므로 진단이 끝나면 다시 켭니다.
이것으로 네트워크 교재의 본문을 마칩니다. 처음부터 다시 훑어보고 싶다면 네트워크 목차로 돌아가, 각 장의 개념이 서로 어떻게 이어지는지 확인해 보세요.