PCAP으로 TCP 손실 복구 관찰하기
발표 때 사용한 두 PCAP과 rack-experiment.txt를 다시 확인했습니다. 첫 번째 캡처에서는 중복 ACK가 세 번 쌓인 뒤 빈 구간이 채워지고, 두 번째 캡처에서는 RACK을 켠 환경에서 중복 ACK 한 번 뒤 빈 구간이 채워집니다. 원본으로 발표 표와 생략된 프레임을 다시 확인했지만, 전체 실행 명령과 정확한 캡처 지점·방향은 남아 있지 않아 실험 전체를 그대로 재현할 수는 없습니다.
데이터 경로와 캡처 경로
실험 구성은 다음과 같습니다.
chunked_server 애플리케이션
→ 서버 OS TCP 스택
→ netem egress 손실
→ Docker 네트워크
→ 클라이언트
관찰 지점
└─ tcpdump가 인터페이스에서 복사본을 수집
→ PCAP
→ Wireshark 또는 tshark로 확인
chunked_server가 쓴 바이트는 TCP 스택과 netem이 적용된 egress, Docker 네트워크를 지나 클라이언트로 전달됩니다. tcpdump는 지정한 인터페이스에서 보이는 패킷의 복사본을 저장하므로, 캡처 위치를 모르면 netem 처리 전후 중 어느 쪽인지 구분할 수 없습니다.
오프로딩도 캡처 모양에 영향을 줍니다. TSO·GSO·GRO가 켜진 지점에서는 운영체제나 장치가 세그먼트를 합치거나 나누기 전의 형태가 보일 수 있습니다. 관련 동작은 Linux 커널의 Segmentation Offloads 문서에 정리돼 있습니다. 패킷 수나 Len만 비교하려면 캡처 지점과 오프로딩 상태를 함께 남겨야 합니다.
Sequence Number와 ACK, SACK은 패킷에 기록된 값입니다. 반면 프레임 번호와 상대 시각은 캡처 기록에서 가져오고, TCP Fast Retransmission이나 Out-Of-Order 같은 이름은 Wireshark가 앞뒤 패킷을 보고 붙입니다. RACK 내부 상태와 실제 커널 판정 분기는 PCAP에 나타나지 않습니다. 아래 표는 원본 PCAP에서 tshark로 다시 추출한 값입니다.
중복 ACK 세 번 뒤에 빈 구간이 채워진 흐름
loss-classic-3dupack.pcap에서 손실 전후 프레임을 추리면 다음과 같습니다. 시간은 캡처의 상대 시각입니다.
| 프레임 | 상대 시각 | 필드 | 표시·설명 |
|---|---|---|---|
| 8 | 5.795ms | Seq=102 Len=1000 | 정상 데이터 |
| 9 | 5.824ms | ACK=1102 | 누적 ACK |
| 10 | 17.918ms | Seq=2102 Len=1000 | 빈 구간 뒤 데이터 |
| 11 | 18.326ms | ACK=1102 SACK=[2102,3102) | 중복 ACK 1 |
| 12 | 24.504ms | Seq=3102 Len=1000 | 뒤쪽 데이터 |
| 13 | 24.518ms | ACK=1102 SACK=[2102,4102) | 중복 ACK 2 |
| 14 | 30.621ms | Seq=4102 Len=1000 | 뒤쪽 데이터 |
| 15 | 30.642ms | ACK=1102 SACK=[2102,5102) | 중복 ACK 3 |
| 16 | 30.730ms | Seq=1102 Len=1000 | 빠른 재전송 |
| 17 | 30.813ms | ACK=5102 | 빈 구간 복구, ACK 전진 |
9번 프레임의 ACK=1102는 1102 앞까지 연속해서 받았다는 뜻입니다. 이후 [2102, 3102)부터 뒤쪽 범위가 도착하지만 ACK는 1102에 머뭅니다. SACK 블록만 [2102, 3102), [2102, 4102), [2102, 5102)로 늘어납니다. 이 필드만으로도 수신자가 뒤쪽 바이트는 받았지만 [1102, 2102)는 아직 연속된 범위에 포함하지 못했다는 사실을 확인할 수 있습니다.
발표 표에 없던 12번과 14번 프레임도 확인했습니다. 세 번째 중복 ACK 직후 16번 프레임에 Seq=1102 Len=1000이 나타나고, 17번 프레임의 ACK는 5102로 진행합니다. Wireshark도 16번 프레임을 TCP Fast Retransmission으로 표시합니다.
다만 최초의 Seq=1102 전송은 이 PCAP에 보이지 않습니다. 10번 프레임에 붙은 Previous segment not captured도 Wireshark의 분석 표시입니다. 해당 패킷이 캡처 지점보다 뒤에서 손실됐는지, 캡처 과정에서 빠졌는지는 이 파일만으로 구분할 수 없습니다. 표와 원본에서 확인되는 것은 중복 ACK 세 번 뒤 같은 시퀀스 구간이 나타나고 누적 ACK가 전진했다는 데까지입니다.
중복 ACK 한 번 뒤 빈 구간이 채워진 흐름
두 번째 파일은 loss-rack-consistent.pcap입니다. 함께 남아 있던 설정 메모에는 Linux 6.12.54-linuxkit, tcp_recovery=1, tcp_early_retrans=0, tcp_sack=1이 적혀 있습니다. 양쪽 엔드포인트의 TSO·GSO·GRO를 끄고 netem에 무작위 손실 4%를 적용했으며, 21개 패킷 가운데 1개가 드롭됐다는 기록도 있습니다. PCAP의 SYN과 SYN-ACK에는 모두 SACK-Permitted가 있고, 13번 ACK에는 실제 SACK 블록이 들어 있습니다.
| 프레임 | 상대 시각 | 필드 | 표시·설명 |
|---|---|---|---|
| 10 | 20.570ms | Seq=1102 Len=1000 | 정상 데이터 |
| 11 | 20.602ms | ACK=2102 | ACK 전진 |
| 12 | 30.475ms | Seq=3102 Len=1000 | 빈 구간 뒤 데이터 |
| 13 | 30.493ms | ACK=2102 SACK=[3102,4102) | 중복 ACK 1 |
| 14 | 30.515ms | Seq=2102 Len=1000 | 순서 바뀜 |
| 15 | 30.531ms | ACK=4102 | 빈 구간 복구 |
13번 프레임에서 ACK가 2102에 머물렀고, 캡처 시각을 기준으로 0.022ms 뒤 14번 프레임에 Seq=2102 Len=1000이 나타납니다. 이어지는 15번 프레임에서 ACK는 4102로 진행합니다.
이 흐름은 RACK 재전송과 일치하지만 PCAP만으로 확정할 수는 없습니다. 최초의 Seq=2102가 캡처에 없어 14번 프레임이 재전송인지 늦게 도착한 원본인지 구분할 수 없고, Out-of-Order도 Wireshark의 분석 표시입니다. 0.022ms 역시 두 프레임 사이의 캡처 간격일 뿐, 3-DupACK 방식과 RACK의 성능 차이를 뜻하지 않습니다.
같은 실험을 다시 한다면 커널과 TCP 설정, netem 명령, 캡처 지점·방향과 offload 상태, 트래픽 조건, 양쪽 PCAP과 분석 명령을 함께 남겨야 합니다. RACK의 reo_wnd와 내부 판정까지 확인하려면 커널 추적도 필요합니다. 설정값은 IP sysctl 문서와 tc-netem(8) 매뉴얼을 참고했습니다.