단편화와 재조립
고친 사람 github-actions[bot]
단편화와 재조립은 가는 길이 한 번에 실어 나르지 못하는 큰 패킷을 잘라 보내고 받는 쪽에서 다시 이어 붙입니다. 자르는 쪽은 조각마다 원래 패킷의 어디에 들어갈지를 적어 둡니다. 받는 쪽은 그 표시를 보고 조각을 모아 원래 패킷을 되살립니다. 인터넷에서 패킷을 목적지까지 나르는 규약이 이 일을 맡습니다.
쉽고 빠른 이해
무슨 일을 하는가 — 너무 큰 패킷을 조각으로 잘라 보내고 목적지에서 다시 잇는 일입니다. 4000바이트짜리 패킷이 한 번에 1500바이트까지만 싣는 구간을 만나면 세 조각으로 나뉘어 갑니다.
왜 이렇게 하나 — 인터넷의 구간마다 한 번에 실어 나를 수 있는 크기가 다릅니다. 보내는 쪽은 가는 길 중간에 얼마나 좁은 구간이 있는지 미리 모릅니다. 자르지 못하면 큰 패킷은 좁은 구간 앞에서 더 못 갑니다.
어떻게 도나
- 좁은 구간 앞의 장비나 보내는 쪽이 패킷을 조각으로 자릅니다
- 조각마다 같은 번호와 원래 데이터에서 떨어진 거리, 뒤에 조각이 더 있는지를 적습니다
- 목적지가 조각을 모아 거리 순서대로 잇습니다. 정해진 시간 안에 다 안 모이면 모은 조각을 버립니다
대가 — 조각 하나만 잃어도 패킷 전체를 잃습니다. 둘째 조각부터는 포트 번호가 없어 방화벽이 가려 보기 어렵습니다. 그래서 요즘은 처음부터 잘리지 않을 크기로 보내는 쪽을 고릅니다.
상세
긴 편지가 봉투 하나에 안 들어가면 여러 봉투에 나눠 넣습니다. 봉투마다 「이 편지의 몇째 줄부터 이어지는지」와 「뒤에 더 있음」을 적어 둡니다. 받는 사람은 봉투가 어떤 순서로 도착하든 적힌 표시를 보고 편지를 다시 맞춥니다.
IP(Internet Protocol, 인터넷 프로토콜)는 인터넷에서 데이터를 목적지 주소까지 나르는 규약입니다. IP 는 데이터를 패킷이라는 덩이에 담아 한 덩이씩 보냅니다.
단편화(fragmentation)는 이 패킷이 가는 길을 지나기에 너무 클 때 여러 조각으로 자르는 일입니다. 재조립(reassembly)은 목적지가 그 조각들을 모아 원래 패킷으로 되돌리는 일입니다. 둘은 늘 짝으로 돕니다. 자르기만 하고 잇지 않으면 받는 프로그램은 쓸 수 없는 토막만 받게 됩니다.
같은 낱말을 다른 분야에서도 씁니다. 메모리의 빈 공간이 잘게 흩어지는 일도 단편화라고 부릅니다. TCP(Transmission Control Protocol, 전송 제어 프로토콜)가 순서가 뒤섞여 도착한 데이터를 다시 맞추는 일도 재조립이라고 부릅니다. 이 편은 IP 가 패킷을 자르고 잇는 일만 다룹니다.
이 절은 먼저 조각이 왜 생기는지 봅니다. 이어서 조각마다 붙는 세 가지 표시를 봅니다. 4000바이트 패킷 하나를 숫자로 잘라 봅니다. 그다음 목적지가 조각을 잇는 순서와 나누지 말라는 표시, 새 판 IP 에서 바뀐 규칙을 봅니다. 마지막으로 단편화가 치르는 값과 그 값을 피하는 방법을 봅니다.
조각이 생기는 까닭
패킷은 여러 구간을 거쳐 목적지에 닿습니다. 구간 하나는 장비와 장비를 잇는 선 하나입니다. 이것을 링크라고 부릅니다. 내 노트북과 공유기 사이가 링크 하나입니다. 공유기와 통신사 장비 사이가 또 다른 링크입니다.
링크마다 한 번에 실어 나를 수 있는 크기에 상한이 있습니다. 이 상한이 MTU(Maximum Transmission Unit, 최대 전송 단위)입니다. 받는 장비가 덩이 하나를 담을 메모리를 미리 잡아 두기 때문에 상한이 필요합니다. 흔한 유선 랜인 이더넷의 MTU 는 1500바이트입니다.
MTU 는 링크마다 다릅니다. 터널을 지나는 구간이 흔한 예입니다. 터널은 패킷을 한 겹 더 포장해서 나르므로, 그 포장만큼 안에 실을 수 있는 크기가 줄어듭니다.
패킷을 다음 링크로 넘겨 주는 장비를 라우터라고 합니다. 라우터가 받은 패킷이 다음 링크의 MTU 보다 크면 그 패킷을 그대로는 내보낼 수 없습니다. 이때 라우터가 패킷을 다음 링크에 맞는 크기로 잘라 따로따로 내보내는 것이 단편화입니다.
조각마다 붙는 세 가지 표시
받는 쪽이 원래 패킷을 되살리려면 조각마다 세 가지를 알아야 합니다. 이 조각이 어느 패킷에서 나왔는지, 원래 데이터의 어디에 들어가는지, 뒤에 조각이 더 있는지입니다.
IP 는 이 셋을 헤더에 적습니다. 헤더는 패킷 맨 앞에 붙는 안내 정보입니다. 받는 쪽 주소 같은 배달 정보가 여기에 들어갑니다. IPv4(IP version 4, IP 4판)의 헤더에는 이 세 가지를 적는 칸이 처음부터 들어 있습니다.
| 칸 | 크기 | 담는 것 |
|---|---|---|
| 식별자(Identification) | 16비트 | 보내는 쪽이 패킷마다 매기는 번호. 같은 패킷에서 나온 조각은 모두 같은 번호를 단다 |
| MF(More Fragments) 플래그 | 1비트 | 1 이면 뒤에 조각이 더 있다. 0 이면 마지막 조각이다 |
| 조각 오프셋(Fragment Offset) | 13비트 | 이 조각의 데이터가 원래 데이터의 맨 앞에서 얼마나 떨어진 곳에 들어가는지. 8바이트 단위로 센다 |
셋 중 둘은 번호이고 가운데 하나는 플래그입니다. 플래그는 켜짐과 꺼짐만 담는 1비트짜리 표시입니다.
오프셋은 기준점에서 떨어진 거리를 뜻합니다. 조각 오프셋이 100 이면 이 조각의 데이터는 원래 데이터의 800번째 바이트부터 들어갑니다. 8바이트씩 세는 것은 13비트라는 좁은 칸으로 큰 패킷의 끝까지 가리키기 위해서입니다.
8바이트 단위에는 조건이 따라옵니다. 마지막 조각을 뺀 모든 조각은 데이터 길이가 8의 배수여야 합니다. 그래야 다음 조각의 시작점을 오프셋 칸에 적을 수 있습니다.
조각 하나하나는 자기 헤더를 따로 가진 온전한 패킷입니다. 원래 패킷의 헤더를 복사해 붙입니다. 길이와 MF 와 오프셋만 조각에 맞게 고칩니다. 그래서 조각은 서로 기다리지 않고 제각기 목적지로 갑니다.
4000바이트 패킷을 1500바이트 링크로
숫자를 넣어 한 번 잘라 봅니다. 헤더 20바이트와 데이터 3980바이트로 이루어진 4000바이트 패킷이 MTU 1500바이트인 링크를 만났다고 합시다.
조각 하나도 1500바이트를 넘을 수 없습니다. 조각마다 20바이트 헤더가 붙으므로 조각 하나의 데이터 몫은 1480바이트입니다. 1480 은 8의 배수라서 바로 쓸 수 있습니다.
| 조각 | 데이터 범위 | 조각 전체 길이 | 오프셋 값 | MF |
|---|---|---|---|---|
| 첫째 | 0 ~ 1479번째 바이트 | 1500바이트 | 0 | 1 |
| 둘째 | 1480 ~ 2959번째 바이트 | 1500바이트 | 185 | 1 |
| 셋째 | 2960 ~ 3979번째 바이트 | 1040바이트 | 370 | 0 |
오프셋 값 185 는 둘째 조각의 시작점 1480 을 8로 나눈 값입니다. 370 도 2960 을 8로 나눈 값입니다. 셋째 조각만 MF 가 0 이라서 받는 쪽은 여기서 데이터가 끝난다는 것을 압니다. 세 조각은 모두 원래 패킷과 같은 식별자를 답니다.
잘린 조각은 제각기 가므로 도착 순서가 보낸 순서와 다를 수 있습니다. 아래 그림은 가운데 라우터가 자른 조각들이 뒤섞여 도착하는 모습입니다.
sequenceDiagram
participant 보내는 쪽
participant 라우터
participant 받는 쪽
보내는 쪽->>라우터: 4000바이트 패킷 하나
Note over 라우터: 다음 링크가 1500바이트까지라 세 조각으로 자른다
라우터->>받는 쪽: 첫째 조각
라우터->>받는 쪽: 셋째 조각이 먼저 닿는다
라우터->>받는 쪽: 둘째 조각
Note over 받는 쪽: 오프셋 순서로 이어 4000바이트 패킷을 되살린다
보내는 쪽은 패킷 하나를 보냈을 뿐입니다. 자르는 일은 길 중간의 라우터가 했습니다. 잇는 일은 받는 쪽이 했습니다. 셋째 조각이 먼저 와도 오프셋이 제 위치를 알려 주므로 받는 쪽은 헷갈리지 않습니다.
목적지가 조각을 잇는 순서
IPv4 에서 재조립은 목적지만 합니다. 조각은 제각기 가므로 서로 다른 길로 갈 수 있습니다. 중간 라우터는 한 패킷의 조각을 전부 본다는 보장이 없습니다. 라우터가 조각을 모아 두는 일까지 떠안으면 패킷을 빨리 넘기는 본업도 느려집니다.
받는 쪽은 먼저 조각을 원래 패킷별로 묶습니다. 묶는 열쇠는 네 가지입니다. 보낸 쪽 주소, 받는 쪽 주소, 위 계층 프로토콜, 식별자입니다. 위 계층 프로토콜은 패킷에 담긴 데이터를 받아 갈 쪽을 말합니다. TCP 나 UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜)가 그 예입니다.
식별자만으로 묶지 않는 까닭이 있습니다. 서로 다른 컴퓨터가 우연히 같은 번호를 쓸 수 있기 때문입니다. 주소와 프로토콜까지 같이 봐야 다른 패킷의 조각이 섞이지 않습니다.
묶음마다 받는 쪽은 빈 버퍼를 하나 잡습니다. 버퍼는 도착한 데이터를 잠시 모아 두는 메모리 공간입니다. 조각이 오면 오프셋이 가리키는 위치에 데이터를 채웁니다. MF 가 0 인 마지막 조각이 오면 원래 데이터의 전체 길이를 알게 됩니다. 전체 길이만큼 빈 곳 없이 채워지면 패킷이 되살아난 것입니다.
기다림에는 끝이 있습니다. 받는 쪽은 첫 조각이 도착하면 타이머를 켭니다. 정해진 시간 안에 조각이 다 안 모이면 모아 둔 조각을 전부 버립니다. 이 시간이 몇 초인지는 운영체제마다 다릅니다.
버릴 때 받는 쪽은 보낸 쪽에 「재조립 시간이 지났다」는 알림을 보낼 수 있습니다. 이 알림은 ICMP(Internet Control Message Protocol, 인터넷 제어 메시지 프로토콜)에 실려 갑니다. ICMP 는 IP 가 배달 중에 생긴 문제를 알리는 데 쓰는 메시지 규약입니다.
재조립 버퍼 하나가 거치는 상태를 그리면 이렇습니다.
stateDiagram-v2
state "모으는 중" as 모음
[*] --> 모음: 첫 조각 도착 · 타이머 켬
모음 --> 모음: 조각 도착 · 오프셋 위치에 채움
모음 --> 완성: 마지막 조각까지 빈 곳 없이 채움
모음 --> 버림: 타이머 만료
완성 --> [*]: 위 계층 프로토콜에 넘김
버림 --> [*]: 모은 조각을 전부 버림
「모으는 중」에 머무는 동안 버퍼는 받는 쪽의 메모리를 붙잡고 있습니다. 조각이 늦게 올수록 그 시간도 길어집니다.
나누지 말라는 표시
IPv4 헤더에는 나누지 말라는 표시도 있습니다. DF(Don't Fragment) 플래그입니다. 보내는 쪽이 이 플래그를 켜면 라우터는 그 패킷을 자르지 않습니다.
DF 가 켜진 패킷이 다음 링크의 MTU 보다 크면 라우터는 그 패킷을 버립니다. 대신 보낸 쪽에 「잘라야 하는데 나누지 말라는 표시가 있다」는 ICMP 알림을 돌려줍니다. 라우터가 패킷을 만났을 때 고르는 길을 그리면 이렇습니다.
flowchart TD
A["패킷 도착"] --> B{"다음 링크의 MTU 보다 큰가"}
B -->|아니다| C["자르지 않고 넘긴다"]
B -->|크다| D{"DF 가 켜져 있나"}
D -->|꺼져 있다| E["조각으로 잘라 넘긴다"]
D -->|켜져 있다| F["버리고 보낸 쪽에 ICMP 알림"]
보내는 쪽은 이 알림을 받고 경로에 더 작은 링크가 있다는 것을 압니다. 그다음부터 패킷을 그 크기에 맞춰 줄여 보냅니다. 이렇게 알림을 받아 가며 경로의 가장 작은 MTU 를 알아내는 방법을 경로 MTU 탐색이라고 부릅니다.
IPv6 에서 바뀐 규칙
IPv6(IP version 6, IP 6판)는 중간 라우터의 단편화를 없앴습니다. 너무 큰 패킷을 만난 라우터는 자르지 않고 버립니다. 그리고 보낸 쪽에 「패킷이 너무 크다」는 알림을 돌려줍니다. IPv6 에서는 모든 패킷이 DF 가 늘 켜진 것처럼 다뤄지므로 DF 칸이 따로 없습니다.
중간 라우터가 자르지 않는 대신 IPv6 는 링크의 MTU 에 최소값을 둡니다. 모든 링크가 1280바이트 이상을 한 번에 실어 날라야 합니다. 보내는 쪽이 패킷을 1280바이트 이하로 만들면 어느 링크에서도 너무 크다며 버려지지 않습니다. 보내는 쪽이 스스로 자를 필요도 없습니다.
IPv6 에서 자르는 일은 보내는 쪽만 합니다. 패킷이 경로의 가장 작은 MTU 보다 크면 보내는 쪽이 직접 자릅니다. 자른 조각의 세 가지 표시를 적는 곳은 IPv4 와 다릅니다.
IPv6 패킷 맨 앞에는 늘 붙는 고정 헤더가 있습니다. 앞 절에서 본 IPv4 헤더와 같은 역할입니다. 이것을 기본 헤더라고 부릅니다. 확장 헤더는 필요할 때만 기본 헤더 뒤에 덧붙이는 헤더입니다.
IPv6 의 기본 헤더에는 식별자·오프셋·MF 칸이 없습니다. 보내는 쪽이 자를 때만 이 셋을 확장 헤더에 적습니다. 자르지 않는 대부분의 패킷은 이 칸들을 싣고 다니지 않아도 됩니다.
조각 하나를 잃으면 패킷 전체를 잃는다
IP 는 잃어버린 조각만 골라 다시 보내 달라고 하지 않습니다. 조각 하나가 사라지면 받는 쪽은 타이머가 끝날 때 나머지 조각까지 버립니다. 도착한 조각이 아무리 많아도 소용이 없습니다.
다시 보내는 일은 위 계층 프로토콜이 맡습니다. TCP 라면 사라진 조각 하나 몫이 아니라 원래 패킷 전체를 다시 보냅니다. 앞의 예라면 1480바이트를 잃고 4000바이트를 다시 보내는 셈입니다.
조각이 많을수록 잃을 기회도 늘어납니다. 조각 100개 중 1개꼴로 사라지는 망에서 패킷이 세 조각으로 나뉘면, 패킷은 100개 중 3개꼴로 사라집니다. 잘리지 않았다면 100개 중 1개꼴이었을 것입니다.
둘째 조각부터는 포트 번호가 없다
포트 번호는 한 컴퓨터 안에서 어느 프로그램이 데이터를 받을지 가리키는 번호입니다. 이 번호는 TCP 나 UDP 의 헤더에 들어 있습니다. 그 헤더는 IP 가 나르는 데이터의 맨 앞에 붙어 있어서 잘리면 첫째 조각에만 들어갑니다.
둘째 조각부터는 포트 번호가 없습니다. 포트로 통과 여부를 가리는 방화벽은 이런 조각만 보고는 판단할 수 없습니다. 주소와 포트를 바꿔 주는 NAT(Network Address Translation, 네트워크 주소 변환) 장비도 같은 어려움을 겪습니다. 이런 장비는 첫 조각에서 본 것을 기억해 두거나 조각을 직접 모아 본 뒤에 판단합니다. 조각을 아예 막아 두는 망도 있습니다.
재조립 버퍼를 노리는 공격
받는 쪽이 조각을 모으려고 잡아 두는 버퍼는 공격 대상이 되기도 합니다. 이 절은 버퍼를 노리는 두 가지 방법을 봅니다.
첫째는 버퍼를 붙잡아 두는 공격입니다. 조각을 일부만 보내면 받는 쪽은 타이머가 끝날 때까지 버퍼를 붙잡고 기다립니다. 이런 조각을 대량으로 보내면 받는 쪽 메모리가 바닥납니다.
둘째는 범위가 겹치는 조각을 보내는 공격입니다. 두 조각이 같은 바이트를 서로 다른 값으로 채우면 받는 쪽은 둘 중 하나를 골라야 합니다. 방화벽과 목적지가 서로 다른 쪽을 고르면 방화벽이 검사한 내용과 목적지가 되살린 내용이 달라집니다.
단편화를 피하는 방법
단편화는 개발자가 켜고 끄는 기능이 아닙니다. IP 가 크기를 보고 알아서 합니다. 개발자가 고를 수 있는 것은 처음부터 조각이 안 생기는 크기로 보내는 쪽입니다.
TCP 는 연결을 맺을 때 한 번에 실을 데이터의 최대 크기를 서로 알려 줍니다. 이 값이 MSS(Maximum Segment Size, 최대 세그먼트 크기)입니다. 양쪽은 MSS 를 자기 링크의 MTU 에서 IP 헤더와 TCP 헤더를 뺀 값으로 잡습니다. 이더넷에서 두 헤더가 20바이트씩이면 1500 에서 40 을 뺀 1460바이트입니다.
보내는 쪽은 두 MSS 가운데 작은 쪽을 넘는 데이터를 한 번에 싣지 않습니다. 데이터에 두 헤더를 더해도 양 끝 링크의 MTU 를 넘지 않으므로 양 끝에서는 조각이 생기지 않습니다.
경로 중간에 더 좁은 링크가 있으면 이것만으로는 모자랍니다. 경로 MTU 탐색을 쓰는 TCP 는 패킷에 DF 를 켜서 보냅니다. 너무 크다는 알림이 오면 MSS 를 줄입니다. 이 둘 덕분에 TCP 로 오가는 데이터는 좀처럼 잘리지 않습니다.
UDP 에는 이런 흥정이 없습니다. 애플리케이션이 한 번에 넘긴 만큼이 IP 패킷 하나가 됩니다. 그 크기가 경로의 MTU 를 넘으면 IP 가 자릅니다. 그래서 UDP 위에서 도는 프로토콜은 한 번에 보내는 크기를 스스로 작게 잡습니다.
DNS(Domain Name System, 도메인 이름 시스템)가 그 예입니다. DNS 는 보통 UDP 로 답합니다. 답이 너무 커지면 DNS 는 일부를 덜어 낸 답에 TCP 로 다시 물어 달라는 표시를 달아 보냅니다.
이 덜어 낸 답은 IP 가 자른 조각이 아닙니다. DNS 가 스스로 크기를 줄여 만든 답 하나입니다. 큰 답을 IP 조각으로 나눠 보내는 대신 연결을 맺는 쪽을 고른 것입니다.
관련 항목
단편화가 쪼개는 데이터 단위
패킷 · 데이터그램 · 세그먼트 · 프레임 · 페이로드
조각에 적히는 IP 헤더 칸
헤더 · IPv4 헤더 · 식별자 · 조각 오프셋 · MF 플래그 · DF 플래그 · 확장 헤더
단편화를 일으키는 크기 상한
MTU · 경로 MTU · 링크 계층 · 이더넷 · 점보 프레임 · 터널링
단편화와 재조립을 맡는 프로토콜
IP · IPv4 · IPv6 · ICMP · ICMPv6
조각에 실려 가는 위 계층 프로토콜
TCP · UDP · QUIC · DNS · 전송 계층
단편화를 피하려고 크기를 맞추는 방법
경로 MTU 탐색 · MSS · MSS 클램핑 · 패킷화 계층 경로 MTU 탐색
조각을 가려 보는 네트워크 장비
조각 때문에 나는 장애와 공격
패킷 손실 · 재전송 · PMTU 블랙홀 · 조각 겹침 공격 · 서비스 거부 공격
이름이 겹치는 다른 분야의 단편화
단편화 · 외부 단편화 · 내부 단편화 · 디스크 단편화
다른 이름: fragmentation and reassembly · IP fragmentation · IP 단편화 · 패킷 단편화