| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- minikube
- golang
- aws
- 티스토리챌린지
- LLM
- 코틀린 코루틴의 정석
- AWS EKS
- Kubernetes
- SRE
- CKA
- Rag
- docker
- PETERICA
- 바이브코딩
- 공부
- 오블완
- 기록으로 실력을 쌓자
- go
- CKA 기출문제
- kotlin
- AI
- Claude
- tucker의 go 언어 프로그래밍
- CloudWatch
- kotlin coroutine
- MySQL
- Spring
- Network
- 정보처리기사 실기 기출문제
- Java
- Today
- Total
피터의 개발이야기
[Network] 19. 작은 편지는 가는데 큰 소포만 멈추는 이유는 무엇일까? 본문

목차: [Network] TCP/IP를 우편 시스템으로 이해하기
TCP/IP를 우편 시스템으로 이해하기 — Part 6. 너무 큰 소포와 좁은 길의 문제
ㅁ 들어가며
같은 Server에 연결했는데 짧은 요청은 성공하고 큰 Data를 보낼 때만 멈추는 경우가 있다.
주소도 맞고 Route도 있으며 작은 Packet은 왕복한다. 그렇다면 연결 전체가 끊긴 것은 아니다.
문제는 “얼마나 많이 보내는가”가 아니라 “한 Packet에 얼마나 크게 담았는가”일 수 있다.
전송량의 문제
→ 한동안 몇 Byte를 길 위에 둘 것인가?
→ rwnd, cwnd
Packet 크기의 문제
→ 한 번에 몇 Byte를 한 Link에 실을 수 있는가?
→ MTU
앞 글의 Window는 이동 중인 Data의 총량을 제한했다.
MTU(Maximum Transmission Unit)는 네트워크를 통해 한 번에 전송할 수 있는 최대 데이터 패킷 크기(바이트 단위)를 말한다.
다시 말해, MTU은 한 Link에서 하나의 Network Layer Packet을 단편화 없이 운반할 수 있는 크기를 제한한다.
큰 Packet이 좁은 Link를 만나면 작게 나누거나, 보내지 못한다는 안내를 Source로 돌려보내야 한다.
이번 글에서는
Ethernet MTU, IPv4 Fragmentation, Identification, DF/MF Flag, Fragment Offset, Reassembly, TCP MSS를 실제 Byte 계산으로 살펴본다.
ㅁ 큰 파일과 큰 패킷은 같은 말일까?
1GB 파일을 보낸다고 1GB짜리 IP Packet 하나가 만들어지는 것은 아니다.
Application Data는 여러 계층을 지나며 작은 전송 단위로 나뉜다.
1GB File
↓ Application이 Stream에 기록
TCP Segment 여러 개
↓
IP Packet 여러 개
↓
각 Link의 Frame 여러 개
TCP는 Byte Stream을 MSS에 맞는 Segment로 나누어 보내는 것이 일반적이다.
반대로 Application이 큰 UDP Datagram 하나를 만들면 IP 계층에 큰 Packet 하나로 전달될 수 있고,
경로 조건에 따라 IP Fragmentation이 필요해질 수 있다.
따라서 다음 문장은 구분해야 한다.
큰 파일을 전송한다
≠ 큰 IP Packet 하나를 전송한다
큰 Application Message
≠ 항상 큰 IP Packet 하나
문제의 기준은 전체 업무 Data 크기가 아니라 각 IP Packet이 다음 Link의 MTU 안에 들어가는가다.
ㅁ MTU는 Frame 전체 크기일까?
Ethernet에서 흔히 보는 MTU는 1500바이트다.
이를 “Ethernet Frame 전체가 1500바이트”라고 이해하면 계층의 크기가 섞인다.
일반적인 Ethernet MTU 1500은 Ethernet Payload로 단편화 없이 실을 수 있는 IP Packet의 최대 크기를 뜻한다.
Ethernet Frame
├─ Ethernet Header
├─ Payload: IP Packet, 최대 1500바이트
└─ Frame Check Sequence 등
Ethernet Header와 FCS까지 포함한 실제 Frame은 1500바이트보다 크다.
VLAN Tag, Preamble, Inter-Frame Gap을 어느 계산에 포함하는지도 목적에 따라 달라진다.
Ethernet MTU 1500
→ “선 위의 모든 Byte를 합친 크기”가 아님
→ Layer 3 Packet을 실을 수 있는 Payload 한계
MTU는 Interface와 Link 기술마다 다를 수 있다.
- 일반 Ethernet에서 흔한 값: 1500
- Tunnel 내부에서 Overhead를 고려해 더 작게 설정한 값
- 모든 구간이 지원하도록 구성한 Jumbo Frame 환경의 더 큰 값
- PPP 계열이나 특수 Link의 다른 값
“인터넷의 모든 MTU는 1500”이라는 규칙은 없다.
ㅁ Router는 어느 MTU를 확인할까?
Host A에서 Host B까지 세 Link를 지난다고 하자.
Host A ── MTU 1500 ── R1 ── MTU 1200 ── R2 ── MTU 1500 ── Host B
Host A가 1400바이트 IPv4 Packet을 만들면 첫 Link에는 들어간다.
하지만 R1이 다음 Hop으로 내보낼 Interface의 MTU는 1200이다.
들어온 Packet: 1400바이트
나갈 Link MTU: 1200바이트
1400 > 1200
Router가 확인하는 핵심 한계는 Outgoing Interface의 MTU다.
어떤 Link에서 들어올 수 있었다는 사실이 다음 Link에도 같은 크기로 나갈 수 있음을 보장하지 않는다.
경로 전체에서 가장 작은 MTU를 Path MTU(PMTU)라고 한다.
PMTU = min(경로를 이루는 각 Link의 MTU)
위 경로의 PMTU는 1200이다. Source가 이를 아직 모르면 중간 Router에서 큰 Packet 문제를 처음 만날 수 있다.
ㅁ IPv4 Router는 큰 Packet을 어떻게 나눌까?
IPv4 Packet이 Outgoing MTU보다 크고 Don't Fragment(DF) Flag가 설정되지 않았다면 Router가 Packet을 여러 Fragment로 나눌 수 있다.
원래 IPv4 Packet
↓ Fragmentation
Fragment 1
Fragment 2
Fragment 3
각 Fragment는 독립적으로 전달되는 IPv4 Packet이다.
각 Fragment
├─ 자체 IPv4 Header
├─ 원래 Payload의 일부
└─ 같은 원본임을 나타내는 Fragment 정보
한 Frame을 조각내 이어 붙여 보내는 것이 아니다. 원래 IPv4 Payload를 나누고 각 조각 앞에 IPv4 Header를 새로 붙인다.
따라서 Fragmentation은 Header 수와 처리해야 할 Packet 수를 늘린다.
ㅁ 조각은 서로 같은 원본임을 어떻게 알까?
IPv4 Header에는 Fragmentation과 Reassembly에 필요한 Field가 있다.
Identification
Flags
├─ DF: Don't Fragment
└─ MF: More Fragments
Fragment Offset
Total Length
ㅇ Identification
같은 원래 Datagram에서 만들어진 Fragment들이 공유하는 식별값이다.
Destination은 Source Address, Destination Address, Protocol, Identification 같은 정보를 함께 사용해 어느 Fragment가 같은 원본에 속하는지 구분한다.
ㅇ DF — Don't Fragment
DF=1이면 Router가 IPv4 Packet을 단편화하지 말라는 뜻이다.
Packet이 Outgoing MTU보다 크면 Router는 그대로 전달하거나 나눌 수 없으므로 Packet을 버리고 오류를 알릴 수 있다.
ㅇ MF — More Fragments
MF=1이면 이 Fragment 뒤에 원래 Datagram의 조각이 더 있다는 뜻이다.
마지막 Fragment는 MF=0이다.
ㅇ Fragment Offset
이 Fragment의 Payload가 원래 IPv4 Payload에서 어디부터 시작하는지 나타낸다.
Header에는 8바이트 단위로 저장된다.
실제 Payload 시작 위치 = Fragment Offset 값 × 8바이트
마지막을 제외한 Fragment의 Payload 크기를 8바이트 배수로 맞추는 이유가 여기에 있다.
ㅇ Total Length
각 Fragment 자신의 IPv4 Header와 Payload를 합한 전체 길이다.
모든 Fragment의 Total Length가 원래 Packet 길이와 같지는 않다.
ㅁ 4000바이트 Packet은 MTU 1500에서 어떻게 나뉠까?
IPv4 Option이 없는 20바이트 Header를 가정하자.
원래 IPv4 Total Length: 4000바이트
IPv4 Header : 20바이트
원래 IPv4 Payload : 3980바이트
Outgoing MTU : 1500바이트
Fragment 하나에도 20바이트 IPv4 Header가 필요하다.
한 Fragment에 실을 수 있는 최대 Payload는 다음처럼 계산한다.
1500 - 20 = 1480바이트
1480은 8바이트로 나누어떨어진다.
1480 ÷ 8 = 185
첫 번째 Fragment는 원래 Payload의 앞 1480바이트를 담는다.
Fragment 1
Payload 범위 : [0, 1480)
Payload 길이 : 1480
Total Length : 20 + 1480 = 1500
Offset Field : 0 ÷ 8 = 0
MF : 1
두 번째 Fragment는 그다음 1480바이트를 담는다.
Fragment 2
Payload 범위 : [1480, 2960)
Payload 길이 : 1480
Total Length : 20 + 1480 = 1500
Offset Field : 1480 ÷ 8 = 185
MF : 1
남은 Payload는 1020바이트다.
3980 - 1480 - 1480 = 1020
세 번째 Fragment가 마지막 조각을 담는다.
Fragment 3
Payload 범위 : [2960, 3980)
Payload 길이 : 1020
Total Length : 20 + 1020 = 1040
Offset Field : 2960 ÷ 8 = 370
MF : 0
표로 모으면 다음과 같다.
| Fragment | Payload 범위 | Payload 길이 | Total Length | Offset Field | MF |
| 1 | [0, 1480) | 1480 | 1500 | 0 | 1 |
| 2 | [1480, 2960) | 1480 | 1500 | 185 | 1 |
| 3 | [2960, 3980) | 1020 | 1040 | 370 | 0 |
세 Fragment의 Payload를 합하면 원래 Payload와 같아야 한다.
1480 + 1480 + 1020 = 3980
각각 새 IPv4 Header를 가지므로 Link를 지나는 IP Byte 수는 늘어난다.
1500 + 1500 + 1040 = 4040바이트
원래 4000바이트보다 40바이트가 늘었다.
원래 하나였던 IPv4 Header가 세 개가 되면서 Header 두 개가 추가된 결과다.
IPv4 Header에 Option이 있으면 Header 길이와 Fragment 계산도 달라질 수 있다.
ㅁ Fragment Offset은 왜 8바이트 단위일까?
IPv4 Header의 Fragment Offset Field는 13비트다.
Byte 단위로만 표현하면 약 8KB 위치까지만 가리킬 수 있다.
8바이트 단위로 해석하면 제한된 Bit로 IPv4 최대 길이 범위의 위치를 나타낼 수 있다.
Offset Field 185
→ 원래 Payload의 185 × 8
→ 1480바이트 위치부터 시작
Packet Capture 도구는 Offset Field의 원래 단위 값이나 Byte로 환산한 위치를 표시할 수 있다.
숫자를 볼 때 도구가 어느 단위를 보여 주는지 함께 확인해야 한다.
핵심은 Offset이 “두 번째 조각” 같은 Fragment 순번이 아니라 원래 Payload 안의 시작 위치라는 점이다.
ㅁ 조각은 중간 Router에서 다시 합칠까?
일반적인 IPv4 Forwarding에서 Router가 만든 Fragment는 최종 Destination에서 Reassembly된다.
Source
│ 원래 Packet
▼
Router R1
│ Fragmentation
├─ F1 ───────────────┐
├─ F2 ───────────────┼─> Destination에서 Reassembly
└─ F3 ───────────────┘
다음 Router가 조각을 다시 합쳐 원본으로 만든 뒤 전달하는 방식이 아니다.
Fragment들은 서로 다른 Queue와 경로를 거칠 수도 있고 순서가 바뀌어 도착할 수도 있다.
Destination은 Fragment Offset을 이용해 제자리를 맞춘다.
도착 순서: F2 → F1 → F3
재조립 결과: F1 + F2 + F3
Destination은 모든 조각이 도착할 때까지 Reassembly 상태와 Buffer를 유지해야 한다.
ㅁ 조각 하나만 사라지면 나머지는 사용할 수 있을까?
원래 IPv4 Packet의 Fragment 세 개 중 두 개만 도착했다고 하자.
F1 도착
F2 손실
F3 도착
IP는 F1과 F3만으로 원래 Payload를 상위 Protocol에 전달할 수 없다.
Reassembly Timer 안에 필요한 Fragment가 모두 모이지 않으면 Destination은 미완성 상태를 버린다.
Fragment 하나 손실
→ 원래 IPv4 Datagram 전체 재조립 실패
IPv4 자체에는 “F2만 다시 보내 달라”는 End-to-End 재전송 기능이 없다.
- TCP Data였다면 TCP가 ACK와 Timer로 손실을 감지해 해당 Byte 범위를 재전송할 수 있다.
- UDP Datagram이었다면 UDP가 자동으로 원본 Datagram을 재전송하지 않는다.
하나의 큰 Packet이 여러 Fragment로 나뉘면 그중 하나라도 손실될 표면이 커진다.
조각 수가 많을수록 Reassembly 실패 가능성과 처리 비용도 커진다.
ㅁ Fragmentation은 왜 가능하면 피하려 할까?
IPv4 Fragmentation은 서로 다른 MTU를 가진 Link를 이어 주는 호환 장치다.
그러나 비용이 있다.
ㅇ Packet과 Header가 늘어난다
각 Fragment에 IPv4 Header가 필요하므로 전송 Byte와 Packet 처리 횟수가 늘어난다.
ㅇ Fragment 하나의 손실이 원본 전체에 영향을 준다
상위 계층은 완성되지 않은 원래 Datagram을 받지 못한다.
ㅇ Destination의 Reassembly 자원을 사용한다
미완성 Fragment를 일정 시간 기억해야 하므로 Memory와 상태가 필요하다.
ㅇ 중간 장비의 처리가 어려워진다
TCP와 UDP Port는 일반적으로 첫 Fragment의 Transport Header에만 있다.
첫 Fragment
├─ IP Header
├─ TCP/UDP Header
└─ Data 일부
뒤 Fragment
├─ IP Header
└─ Data의 나머지
Firewall이나 NAT가 Transport Port를 기준으로 판단할 때 뒤 Fragment만 보고는 같은 정보를 바로 확인하기 어렵다. 장비는 Fragment 상태를 추적하거나 정책에 따라 Fragment를 제한할 수 있다.
ㅇ 보안 검사가 복잡해진다
비정상적인 Offset이나 겹치는 Fragment는 Reassembly 해석 차이를 악용할 수 있다. 운영체제와 보안 장비는 이런 입력을 엄격하게 처리할 수 있다.
이 때문에 현대의 일반적인 End-to-End 통신은 Source가 Path MTU에 맞는 Packet을 만들어 중간 Router의 Fragmentation을 피하는 방향을 선호한다.
ㅁ DF가 설정된 큰 Packet은 어떻게 될까?
IPv4 Packet이 Outgoing MTU보다 큰데 DF=1이라고 하자.
Router는 Packet을 나눌 수 없다.
Packet 1400바이트
Outgoing MTU 1200
DF=1
→ Fragmentation 금지
→ Packet Drop
Router는 일반적으로 Source에 다음 ICMP 오류를 보낼 수 있다.
ICMP Destination Unreachable
Type 3, Code 4
Fragmentation Needed and DF Set
오류 정보에는 다음 Hop에서 허용되는 MTU 값이 포함될 수 있다.
Source는 이 안내를 바탕으로 더 작은 Packet을 만들어 다시 시도할 수 있다.
하지만 ICMP가 정책으로 차단되거나 Return Path에서 사라지면 Source는 “작게 보내라”는 정보를 얻지 못한다.
작은 Packet → MTU 안에 들어가 성공
큰 Packet → DF 때문에 Drop
ICMP 안내 → 차단
결과: 연결은 되는 것처럼 보이지만 큰 Data에서 멈춤
이 현상을 Path MTU Black Hole이라고 부른다. Source가 경로의 가장 작은 MTU를 알아내고 이에 대응하는 과정은 다음 글에서 자세히 다룬다.
ㅁ TCP는 큰 Data를 보낼 때 항상 IP Fragmentation을 만들까?
일반적으로는 그렇지 않도록 설계한다.
TCP Handshake에서 Endpoint는 자신이 받을 수 있는 최대 TCP Payload 크기를
Maximum Segment Size(MSS)로 알릴 수 있다.
Ethernet MTU 1500, Option 없는 IPv4 Header 20바이트, 기본 TCP Header 20바이트를 가정하면 다음과 같다.
1500 - IPv4 Header 20 - TCP Header 20
= TCP Payload 1460바이트
따라서 흔히 보는 IPv4 TCP MSS가 1460이다.
IPv6 기본 Header 40바이트를 가정하면 다음과 같다.
1500 - IPv6 Header 40 - TCP Header 20
= TCP Payload 1440바이트
Header Option이나 Extension Header가 있으면 실제 한 Packet에 담을 TCP Data 크기는 더 작아질 수 있다.
TCP는 Application이 수 MB를 한 번에 write()해도 적절한 Segment로 나누어 보낸다.
Application write: 1MB
↓
TCP Segment: 대략 MSS 이하의 Payload 여러 개
↓
MTU 이하의 IP Packet 여러 개
MSS는 TCP Payload 기준이고 MTU는 IP Packet 전체 기준이라는 차이를 기억해야 한다.
MSS만으로 경로 중간의 더 작은 MTU를 항상 알 수 있는 것은 아니다.
상대가 광고한 MSS는 주로 상대 Interface 쪽 수신 조건을 반영하며,
중간 Tunnel이나 더 좁은 Link는 별도로 발견해야 할 수 있다.
ㅁ UDP와 ping의 크기는 어떻게 계산할까?
IPv4 Option이 없고 MTU가 1500이라고 하자.
하나의 IPv4 Packet에 단편화 없이 담을 수 있는 UDP Payload의 단순 최대값은 다음과 같다.
1500 - IPv4 Header 20 - UDP Header 8
= UDP Payload 1472바이트
ICMP Echo Request의 ping -s 값도 많은 Linux 구현에서 ICMP Data 크기를 뜻한다.
IPv4 Header 20
+ ICMP Header 8
+ ping Data 1472
= IPv4 Total Length 1500
따라서 Ethernet MTU 1500 경로에서 IPv4 ping -s 1472가 경계 확인 예시로 자주 사용된다.
하지만 IP Option, Tunnel, 다른 Link MTU, 명령 구현 차이에 따라 숫자는 달라진다.
ping -s가 항상 IP Packet 전체 크기라는 뜻도 아니다.
UDP API는 1472보다 큰 Datagram을 만들 수 있지만,
그것이 Ethernet MTU 1500 경로에서 하나의 IP Packet으로 그대로 전달된다는 뜻은 아니다.
IPv4 Fragmentation이나 오류가 발생할 수 있다.
ㅁ IPv6 Router도 큰 Packet을 나눌까?
IPv6에서는 중간 Router가 Packet을 Fragmentation하지 않는다.
Router가 다음 Link로 전달하기에 너무 큰 IPv6 Packet을 받으면 Packet을 버리고
Source에 ICMPv6 Packet Too Big 메시지를 보낸다.
IPv4
→ 조건에 따라 Router Fragmentation 가능
IPv6
→ Router Fragmentation 없음
→ Source만 Fragment Header를 사용해 Fragment 가능
따라서 IPv6에서는 Source가 경로의 MTU를 알고 Packet 크기를 조절하는 과정이 필수적이다.
ICMPv6 Packet Too Big을 무조건 차단하면 큰 Packet 전달이 멈출 수 있다.
ICMP가 단순한 진단용 부가 기능이 아니라 정상적인 전달 과정의 제어 메시지이기도 한 이유다.
ㅁ Tunnel을 지나면 왜 MTU가 더 작아질까?
VPN이나 Overlay Tunnel은 원래 Packet 바깥에 새로운 Header를 붙일 수 있다.
Outer IP Header
Tunnel Header
Inner IP Packet
물리 Link가 허용하는 전체 크기는 그대로인데 바깥 Header가 공간을 차지하면 내부 Packet에 사용할 수 있는 크기는 줄어든다.
물리 Link MTU 1500
- Tunnel Overhead
= Tunnel 내부에서 안전한 Packet 크기
Tunnel Endpoint가 내부 Interface MTU를 줄이거나 TCP MSS를 조정하는 이유가 여기에 있다.
반대로 양 끝 Host만 Jumbo Frame을 지원한다고 중간 모든 Link에서 큰 Packet이 통과하는 것도 아니다.
같은 Layer 2 구간과 경로 장비 전체가 해당 크기를 일관되게 지원해야 한다.
ㅁ Capture에서 Fragment를 어떻게 알아볼까?
Wireshark에서 IPv4 Fragment 관련 Field를 확인한다.
ip.id
ip.len
ip.flags.df
ip.flags.mf
ip.frag_offset
IPv4 Fragment를 좁혀 보는 Display Filter는 다음과 같다.
ip.flags.mf == 1 || ip.frag_offset > 0
같은 원래 Datagram의 Fragment에서는 다음을 비교한다.
ip.id가 같은가?ip.len은 각 Fragment의 Total Length와 맞는가?- 마지막을 제외한 Fragment에
MF=1이 있는가? - Offset을 따라 Payload 범위가 이어지는가?
- Reassembly 결과가 표시되는가?
뒤 Fragment에는 Transport Header가 없으므로 Wireshark가
첫 Fragment와 Reassembly 상태를 보지 못하면 TCP나 UDP로 완전히 해석하지 못할 수 있다.
ㅁ 큰 Packet처럼 보이는데 실제 Link에도 나갔을까?
Host 내부에서 Packet Capture를 보면 Interface MTU보다 큰 TCP Packet처럼 보일 때가 있다.
Network Interface Card와 Kernel은 CPU 비용을 줄이기 위해 여러 최적화를 사용할 수 있다.
TSO / GSO
→ Kernel에서 큰 단위로 넘기고 NIC 또는 하위 계층에서 실제 전송 크기로 분할
GRO / LRO
→ 받은 여러 Packet을 Host 내부에서 큰 단위로 합쳐 상위 계층에 전달
Capture 지점이 분할 전이거나 병합 후이면 실제 Wire에 존재하지 않았던 큰 단위가 보일 수 있다.
Host Capture에서 32KB로 보임
≠ Wire에 32KB Ethernet Frame 하나가 나감
정확한 Wire 크기를 확인하려면 Offload 영향을 이해하고,
가능하면 다른 장비의 Mirror Port나 적절한 Capture 지점에서 관찰해야 한다.
MTU 문제를 분석할 때 Capture 화면의 한 줄만 보고 “Jumbo Packet이 전송됐다”고 결론 내리면 안 된다.
ㅁ 우편 비유를 네트워크 용어로 바꾸면
비유를 실제 Header와 동작으로 다시 연결해 보자.
| 우편 이야기 | 실제 용어 | 정확한 의미 |
| 한 도로가 운반할 수 있는 소포 크기 | Link MTU | 해당 Link에서 단편화 없이 실을 수 있는 IP Packet 한계 |
| 전체 경로에서 가장 좁은 소포 규격 | Path MTU | Source와 Destination 경로의 최소 MTU |
| 큰 소포를 여러 상자로 나눔 | IPv4 Fragmentation | 원래 IP Payload를 나누고 각 조각에 IPv4 Header를 부착 |
| 같은 주문의 상자 번호표 | Identification | 같은 원래 IPv4 Datagram의 Fragment를 연관시킴 |
| 절대 나누지 말라는 표시 | DF Flag | Router Fragmentation을 금지 |
| 뒤에 상자가 더 있다는 표시 | MF Flag | 마지막이 아닌 Fragment임을 표시 |
| 원래 내용에서 조각의 위치 | Fragment Offset | 8바이트 단위로 Payload 시작 위치 표현 |
| 목적지에서 모든 상자를 다시 맞춤 | Reassembly | Fragment를 원래 IPv4 Datagram으로 복원 |
| TCP 한 봉투에 넣을 본문 한계 | MSS | TCP Segment의 최대 Data 크기 |
| 너무 크니 줄여 달라는 반송 안내 | ICMP Fragmentation Needed | DF Packet이 MTU를 넘었음을 Source에 알림 |
| 포장지가 차지하는 공간 | Tunnel Overhead | Outer Header 때문에 내부 Packet 가용 크기가 감소 |
크기 관계를 한 번 더 정리하면 다음과 같다.
Application Data
↓ TCP가 MSS를 고려해 나눔
TCP Header + TCP Data
↓ IP Header를 붙임
IP Packet ≤ Path MTU가 되도록 조절
↓ Link Header와 Trailer를 붙임
Link Frame
ㅁ 격리된 Linux 환경에서 직접 관찰해 보기
다음 실습은 ip, ping, tcpdump가 있는 개인 Linux VM에서 수행한다. 세 Network Namespace 안에만 가상 경로를 만들며 실제 Interface MTU는 바꾸지 않는다.
mtu-client ── MTU 1500 ── mtu-router ── MTU 1200 ── mtu-server
Namespace와 가상 Link를 만든다.
sudo ip netns add mtu-client
sudo ip netns add mtu-router
sudo ip netns add mtu-server
sudo ip link add veth-c type veth peer name veth-rc
sudo ip link add veth-rs type veth peer name veth-s
sudo ip link set veth-c netns mtu-client
sudo ip link set veth-rc netns mtu-router
sudo ip link set veth-rs netns mtu-router
sudo ip link set veth-s netns mtu-server
Address와 Interface를 설정한다.
sudo ip -n mtu-client addr add 10.19.1.2/24 dev veth-c
sudo ip -n mtu-router addr add 10.19.1.1/24 dev veth-rc
sudo ip -n mtu-router addr add 10.19.2.1/24 dev veth-rs
sudo ip -n mtu-server addr add 10.19.2.2/24 dev veth-s
sudo ip -n mtu-client link set lo up
sudo ip -n mtu-router link set lo up
sudo ip -n mtu-server link set lo up
sudo ip -n mtu-client link set veth-c up
sudo ip -n mtu-router link set veth-rc up
sudo ip -n mtu-router link set veth-rs up mtu 1200
sudo ip -n mtu-server link set veth-s up mtu 1200
Route와 IPv4 Forwarding을 설정한다.
sudo ip -n mtu-client route add default via 10.19.1.1
sudo ip -n mtu-server route add default via 10.19.2.1
sudo ip netns exec mtu-router \
sysctl -w net.ipv4.ip_forward=1
Capture Terminal에서 Router의 모든 Interface를 관찰한다.
sudo ip netns exec mtu-router \
tcpdump -n -vv -i any -s 0 -w /tmp/mtu-fragments.pcap \
'icmp or (ip[6:2] & 0x3fff != 0)'
첫 번째 실험은 DF를 사용하지 않아 Router가 Fragmentation할 수 있게 한다. Linux ping의 -s는 ICMP Data 크기다.
sudo ip netns exec mtu-client \
ping -M dont -c 1 -s 1400 10.19.2.2
IPv4 Header 20바이트와 ICMP Header 8바이트를 더하면 1428바이트다. 첫 Link의 MTU 1500에는 들어가지만 Router의 다음 Link MTU 1200은 넘는다.
Capture에서 같은 ip.id를 가진 Fragment와 MF, Offset을 확인한다.
두 번째 실험은 DF를 설정한다.
sudo ip netns exec mtu-client \
ping -M do -c 1 -s 1400 10.19.2.2
Router는 1428바이트 Packet을 MTU 1200 Link로 내보내기 위해 나눌 수 없으므로 Drop하고 ICMP Fragmentation Needed를 돌려보낼 수 있다.
세 번째 실험은 전체 IPv4 Packet을 정확히 1200바이트에 맞춘다.
IPv4 Header 20 + ICMP Header 8 + Data 1172 = 1200
sudo ip netns exec mtu-client \
ping -M do -c 1 -s 1172 10.19.2.2
DF가 있어도 Packet이 Path MTU 안에 들어가므로 정상 Echo Reply를 기대할 수 있다.
Linux ping -M 동작과 출력은 iputils 버전에 따라 다를 수 있다. 이 실습은 IPv4 기본 Header를 가정하며 Offload와 Kernel 설정에 따라 Capture 표현이 조금 달라질 수 있다.
실습을 마치면 Namespace를 삭제한다. 가상 Interface와 MTU 설정도 함께 제거된다.
sudo ip netns del mtu-client
sudo ip netns del mtu-router
sudo ip netns del mtu-server
공용 Server에 큰 Probe를 반복해서 보내지 않는다. MTU 변경과 고의적인 Fragmentation은 개인 격리 환경에서만 관찰한다.
ㅁ 핵심 정리
- 큰 파일과 큰 IP Packet은 같은 개념이 아니며 TCP는 큰 Byte Stream을 여러 Segment로 나눈다.
- 일반적인 Ethernet MTU 1500은 Ethernet Frame 전체가 아니라 Frame Payload로 실을 수 있는 IP Packet 크기 기준이다.
- Router는 Packet을 내보낼 Outgoing Interface의 MTU와 IPv4 DF Flag를 확인한다.
- IPv4에서 DF가 없으면 Router가 큰 Packet의 Payload를 여러 Fragment로 나눌 수 있다.
- 같은 원본의 Fragment는 Identification을 공유하고 MF와 Fragment Offset으로 끝과 위치를 나타낸다.
- Fragment Offset Field는 8바이트 단위이며 마지막을 제외한 Fragment Payload는 8바이트 배수로 구성된다.
- IPv4 Fragment는 일반적으로 최종 Destination에서 Reassembly된다.
- Fragment 하나가 손실되면 원래 Datagram 전체를 재조립할 수 없으며 IPv4가 조각만 재전송하지는 않는다.
- DF Packet이 Outgoing MTU보다 크면 Router는 Drop하고 ICMP Type 3 Code 4를 보낼 수 있다.
- MSS는 TCP Payload 크기이고 MTU는 IP Packet 전체 크기라는 차이가 있다.
- IPv6 Router는 Fragmentation하지 않으며 너무 큰 Packet에 ICMPv6 Packet Too Big으로 응답한다.
- Tunnel Header와 Offload는 Capture에서 보이는 크기와 실제 Link 크기를 해석할 때 함께 고려해야 한다.
핵심 용어: Maximum Transmission Unit, MTU, Path MTU, IPv4 Fragmentation, Identification, Don't Fragment, DF, More Fragments, MF, Fragment Offset, Total Length, Reassembly, Reassembly Timer, Maximum Segment Size, MSS, ICMP Fragmentation Needed, IPv6 Packet Too Big, Tunnel Overhead, TSO, GSO, GRO, LRO
MTU는 한 번의 배송에 담을 수 있는 IP Packet의 크기다. 큰 Packet을 억지로 조각내면 전달은 이어질 수 있지만, 조각 수와 Header, 손실 표면, 재조립 상태가 함께 늘어난다.
ㅁ 다음 이야기
중간 Router가 큰 IPv4 Packet을 Fragmentation할 수 있다는 것은 알게 되었다.
그러나 현대 네트워크는 중간 Fragmentation을 피하고 Source가 처음부터 경로에 맞는 크기로 보내는 방식을 선호한다.
Source는 아직 가 보지 않은 경로의 가장 작은 MTU를 어떻게 알 수 있을까?
그리고 “너무 크다”는 ICMP 안내가 Firewall에서 사라지면 왜 Handshake와 작은 Data는 되는데 큰 Data만 멈출까?
다음 글에서는 「가장 좁은 길의 크기를 출발지에서 어떻게 알 수 있을까?」라는 질문을 통해 Path MTU Discovery, ICMP Fragmentation Needed, ICMPv6 Packet Too Big, PLPMTUD와 MTU Black Hole을 살펴본다.
'DevOps > Network' 카테고리의 다른 글
| [Network] 21. 여러 집이 하나의 공인 주소를 함께 쓸 수 있을까? (0) | 2026.07.23 |
|---|---|
| [Network] 20. 가장 좁은 길의 크기를 출발지에서 어떻게 알 수 있을까? (0) | 2026.07.22 |
| [Network] 18. 받는 사람이 감당하지 못할 만큼 보내면 어떻게 될까? (0) | 2026.07.20 |
| [Network] 17. 편지가 사라졌다는 사실은 어떻게 알아챌까? (0) | 2026.07.20 |
| [Network] 16. 대화를 시작하기 전에 왜 세 번이나 인사할까? (1) | 2026.07.19 |
