📚 네트워크 · 패킷 분석 › 01. TCP/IP 구조 이해 — 40편
이전 글: 39. UDP란 무엇인가 · 다음 글: 41. TCP 3-Way Handshake — 연결이 성립했다는 증거
참고(리눅스 시스템 기초): IP / TCP / UDP / Port — TCP·UDP의 기본 개념과 포트 개념
TCP는 "신뢰성 있는 연결형", UDP는 "빠른 비연결형"이라는 설명은 IP / TCP / UDP / Port 글에서 정리했습니다. 이번 글은 한 단계 더 들어가, 그 차이가 헤더의 어느 필드에서 나오는지를 비교합니다.
관제에서 헤더 구조를 알아야 하는 이유는 다음과 같습니다.
tcp[13], udp[4:2] 같은 표현은 헤더의 바이트 위치를 가리킵니다. 구조를 알아야 읽고 쓸 수 있습니다.각 필드를 Wireshark에서 한 줄씩 해석하는 작업은 03. Wireshark 패킷 분석 시리즈(11. TCP 헤더와 Flag 분석)에서 다루고, 여기서는 구조 비교와 의미에 집중합니다.
TCP 헤더 (최소 20바이트, 옵션 포함 최대 60바이트)
0 1 2 3 (바이트)
+-------------------------------+-------------------------------+
| Source Port (16bit) | Destination Port (16bit) | 0~3
+-------------------------------+-------------------------------+
| Sequence Number (32bit) | 4~7
+---------------------------------------------------------------+
| Acknowledgment Number (32bit) | 8~11
+-------+-------+---------------+-------------------------------+
| Data | Rsrvd | Control Bits | Window (16bit) | 12~15
|Offset | | CWR ECE URG | |
| (4bit)|(4bit) | ACK PSH RST | |
| | | SYN FIN | |
+-------+-------+---------------+-------------------------------+
| Checksum (16bit) | Urgent Pointer (16bit) | 16~19
+-------------------------------+-------------------------------+
| Options (0~40바이트, 4바이트 단위로 맞춤) | 20~
+---------------------------------------------------------------+
UDP 헤더 (고정 8바이트)
+-------------------------------+-------------------------------+
| Source Port (16bit) | Destination Port (16bit) | 0~3
+-------------------------------+-------------------------------+
| Length (16bit) | Checksum (16bit) | 4~7
+-------------------------------+-------------------------------+
| 기능 | TCP 필드 | UDP 필드 | 차이가 만드는 결과 |
|---|---|---|---|
| 프로세스 구분 | Source/Destination Port | Source/Destination Port | 동일 (포트 개념은 같음) |
| 순서 보장 | Sequence Number | 없음 | UDP는 순서가 뒤바뀌어도 모름 |
| 수신 확인 | Acknowledgment Number, ACK 플래그 | 없음 | UDP는 유실을 스스로 알지 못함 |
| 연결 관리 | SYN, FIN, RST 플래그 | 없음 | UDP는 "연결 수립" 단계가 없음 |
| 흐름 제어 | Window | 없음 | 수신 측이 받을 수 있는 양을 TCP만 조절 |
| 헤더 길이 | Data Offset (가변) | 없음 (고정 8바이트) | TCP는 옵션으로 기능 확장 가능 |
| 전체 길이 | 없음 (IP 길이로 계산) | Length | UDP는 헤더+데이터 길이를 직접 기록 |
| 오류 검출 | Checksum (필수) | Checksum (IPv4에서는 0이면 미사용, IPv6에서는 필수) | 둘 다 가상 헤더(IP 주소 등) 포함 계산 |
| 긴급 데이터 | Urgent Pointer, URG 플래그 | 없음 | 현재는 거의 쓰이지 않음 |
TCP 헤더 13번째 바이트(0부터 세면 오프셋 13)에 8개의 플래그가 있습니다. RFC 9293 기준입니다.
| 비트 값 | 플래그 | 의미 | tcpdump 표기 | BPF 이름 |
|---|---|---|---|---|
| 0x01 | FIN | 보낼 데이터가 끝났음 (정상 종료) | F | tcp-fin |
| 0x02 | SYN | 연결 시작, 시퀀스 번호 동기화 | S | tcp-syn |
| 0x04 | RST | 연결 즉시 중단·거부 | R | tcp-rst |
| 0x08 | PSH | 버퍼링하지 말고 애플리케이션에 바로 전달 | P | tcp-push |
| 0x10 | ACK | Acknowledgment Number가 유효함 | . | tcp-ack |
| 0x20 | URG | Urgent Pointer가 유효함 | U | tcp-urg |
| 0x40 | ECE | ECN(명시적 혼잡 통지) 관련 | E | tcp-ece |
| 0x80 | CWR | 혼잡 윈도우 감소 알림 (ECN) | W | tcp-cwr |
연결이 수립된 뒤의 TCP 세그먼트는 거의 모두 ACK 플래그가 켜져 있습니다. SYN·FIN·RST의 구체적인 사용 흐름은 08편(3-Way Handshake)과 09편(FIN·RST)에서 다룹니다.
| Kind | 옵션 | 용도 |
|---|---|---|
| 0 / 1 | End of Option List / No-Operation | 옵션 끝 표시 / 4바이트 정렬용 채움 |
| 2 | MSS (Maximum Segment Size) | 한 세그먼트에 담을 수 있는 최대 데이터 크기 (SYN에서 교환) |
| 3 | Window Scale | Window 값을 2의 거듭제곱만큼 확대 (SYN에서 교환) |
| 4 / 5 | SACK Permitted / SACK | 선택적 확인 응답 |
| 8 | Timestamps | 왕복 시간 측정, 시퀀스 번호 재사용 보호 |
SYN 패킷의 옵션 조합·순서·초기 TTL은 운영체제마다 조금씩 달라서, 패시브 OS 식별의 근거로 쓰이기도 합니다.
같은 "짧은 요청 하나"를 TCP와 UDP로 보낼 때 네트워크에 나타나는 패킷 수를 비교합니다.

그림 1. TCP 헤더 — 신뢰성을 위한 시퀀스·ACK·플래그·윈도우 필드가 있습니다

그림 2. UDP 헤더 — 포트·길이·체크섬만 있는 8바이트 구조
[TCP로 요청 1개] [UDP로 요청 1개]
클라이언트 서버 클라이언트 서버
│── SYN ────────→ │ 연결 수립 │── 요청 ────────→ │
│←──── SYN/ACK ── │ (헤더만, 데이터 0) │←──────── 응답 ── │
│── ACK ────────→ │ │
│── 요청(PSH/ACK)→│ 데이터 전송 (연결 수립·종료 없음,
│←───────── ACK ──│ 유실되면 애플리케이션이
│←─ 응답(PSH/ACK)─│ 재전송 여부를 판단)
│── ACK ────────→ │
│── FIN/ACK ────→ │ 연결 종료
│←──── FIN/ACK ── │
│── ACK ────────→ │
→ 10개 안팎의 패킷, 헤더 20바이트+옵션 → 2개 패킷, 헤더 8바이트
(ACK를 응답에 함께 싣는 등 구현에 따라 달라짐)
이 차이는 헤더 필드에서 그대로 나옵니다.
보안 관점에서 중요한 결과가 하나 있습니다. TCP는 3-Way Handshake를 거쳐야 데이터가 오가므로, 출발지 IP를 위조하면 SYN/ACK가 위조된 주소로 가서 연결을 완성하기 어렵습니다. UDP는 이런 확인 단계가 없어, 위조된 출발지로 요청 한 번만 보내도 응답이 위조된 주소(피해자)로 갑니다. 작은 요청에 큰 응답을 돌려주는 서비스가 반사·증폭 공격에 악용되는 원리입니다.
실습 환경: 본인 소유 Linux VM (Rocky Linux 또는 Ubuntu) 2대(192.168.10.10 클라이언트, 192.168.10.20 서버 예시). 인터페이스 이름은 ens33을 예시로 사용합니다(환경에 따라 다름).
# 도구 설치
sudo dnf install -y tcpdump nmap-ncat # Rocky
sudo apt install -y tcpdump netcat-openbsd # Ubuntu
# [서버 터미널] SSH 연결 시작 부분 10개 캡처, -v로 옵션·TTL 등 상세 표시
sudo tcpdump -nn -v -i ens33 -c 10 'tcp port 22'
# [클라이언트 터미널] 접속 시도
ssh user@192.168.10.20
# [서버 터미널] UDP 9999 대기 (Rocky의 ncat, Ubuntu의 openbsd nc 모두 동작)
nc -u -l 9999
# [서버의 다른 터미널] 캡처
sudo tcpdump -nn -v -i ens33 'udp port 9999'
# [클라이언트 터미널] 5바이트("test\n") 전송
echo test | nc -u -w1 192.168.10.20 9999
# SYN 비트가 켜진 TCP (오프셋 13, 값 0x02)
sudo tcpdump -nn -i ens33 'tcp[13] & 0x02 != 0'
# 위와 같은 의미 (이름 사용)
sudo tcpdump -nn -i ens33 'tcp[tcpflags] & tcp-syn != 0'
# TCP 옵션이 있는 세그먼트 (Data Offset 상위 4비트가 5보다 큼 = 헤더 20바이트 초과)
sudo tcpdump -nn -i ens33 'tcp and (tcp[12] >> 4) > 5'
# UDP Length 필드가 512 초과 (오프셋 4부터 2바이트)
sudo tcpdump -nn -i ens33 'udp[4:2] > 512'
# 헤더를 16진수로 함께 출력해 필드 위치 확인
sudo tcpdump -nn -X -i ens33 -c 1 'tcp port 22'
# TCP 소켓: 상태(State) 열이 LISTEN, ESTAB 등으로 표시
ss -tan
# UDP 소켓: 연결 상태 개념이 없어 대부분 UNCONN으로 표시
ss -uan
ss 명령의 기본 사용법은 ss와 ip 명령어 글을 참고합니다.
📷 [실습 화면 삽입]
tcpdump -nn -v 'tcp port 22'결과 — SYN 패킷의 Flags와 options 부분
tcpdump 출력 형식 예시(값은 환경마다 다름):
# TCP (SYN)
IP (tos 0x0, ttl 64, id 40211, offset 0, flags [DF], proto TCP (6), length 60)
192.168.10.10.51514 > 192.168.10.20.22: Flags [S], cksum 0x9a3c (correct), seq 1180562114, win 64240, options [mss 1460,sackOK,TS val 1920311 ecr 0,nop,wscale 7], length 0
# UDP
IP (tos 0x0, ttl 64, id 5521, offset 0, flags [DF], proto UDP (17), length 33)
192.168.10.10.41234 > 192.168.10.20.9999: UDP, length 5
| 출력 요소 | 해당 헤더 필드 |
|---|---|
proto TCP (6) / proto UDP (17) | IP 헤더의 Protocol 번호 (TCP 6, UDP 17) |
IP length 60 (TCP) | IP 20 + TCP 40 (기본 20 + 옵션 20) + 데이터 0 |
IP length 33 (UDP) | IP 20 + UDP 8 + 데이터 5 |
Flags [S], seq, win | TCP Control Bits, Sequence Number, Window |
options [mss …, wscale 7] | TCP Options (SYN에서 교환) |
UDP, length 5 | UDP 데이터 길이 (UDP Length 필드 13 = 헤더 8 + 데이터 5) |
cksum … (correct) | 체크섬. 송신 호스트에서 캡처하면 NIC 오프로딩 때문에 incorrect로 보일 수 있음 |
tcp[13] 필터와 tcp[tcpflags] 필터가 같은 결과임을 확인했다ss -tan과 ss -uan의 State 열 차이를 설명할 수 있다📷 [실습 화면 삽입] UDP 캡처 결과 —
proto UDP (17), length 33과UDP, length 5
| 관찰 | 정상일 수 있는 경우 | 의심해야 하는 경우 |
|---|---|---|
| TCP 플래그 조합 | SYN, SYN/ACK, ACK, PSH/ACK, FIN/ACK, RST, RST/ACK | 플래그 없음(NULL), FIN만, FIN+PSH+URG, SYN+FIN 등 RFC상 정상 흐름에 없는 조합 (05. 네트워크 스캔 · 공격 징후 분석 시리즈에서 다룸) |
| SYN 패킷에 데이터 포함 | TCP Fast Open 사용 환경 (일부 클라이언트·서버) | 지원하지 않는 환경에서 SYN에 페이로드 |
| TCP 옵션 없는 SYN | 일부 경량 장비·임베디드 기기 | 일반 OS에서 나올 수 없는 옵션 조합 → 도구가 직접 만든 패킷일 가능성 |
| 큰 UDP 응답이 한 방향으로만 대량 | 대용량 DNS 응답(DNSSEC), 스트리밍 | 요청한 적 없는 DNS/NTP 응답이 외부 다수 IP에서 유입 → 반사·증폭 공격 피해 |
| UDP 체크섬 0 (IPv4) | 일부 터널링 프로토콜 구현 | 일반 서비스에서 반복적으로 보임 |
# 플래그가 하나도 없는 TCP (NULL)
sudo tcpdump -nn -i ens33 'tcp[tcpflags] == 0'
# SYN과 FIN이 동시에 켜진 TCP
sudo tcpdump -nn -i ens33 'tcp[tcpflags] & (tcp-syn|tcp-fin) == (tcp-syn|tcp-fin)'
# 외부에서 들어오는 출발지 포트 53 UDP 중 큰 응답 (반사 트래픽 점검용)
sudo tcpdump -nn -i ens33 'udp src port 53 and udp[4:2] > 512'
필터는 후보를 좁히는 용도입니다. 비정상 플래그 한두 개는 네트워크 장비 오류나 패킷 손상일 수도 있으므로, 출발지·빈도·대상 범위를 함께 봐야 합니다.
흔적이 남는 곳
한계와 오탐 주의
tcp[13] 또는 tcp[tcpflags]로 필터링합니다.