네트워크 통신에서 MTU는 패킷이 한 번에 지나갈 수 있는 최대 크기를 의미합니다.
일반적인 이더넷 환경에서는 MTU가 보통 1500 byte로 설정되어 있습니다.
하지만 VPN, IPSec, WireGuard, GRE, VXLAN 같은 터널링 환경에서는
기존 패킷에 추가 헤더가 붙기 때문에 실제로 사용할 수 있는 패킷 크기가 줄어들 수 있습니다.
이때 MTU보다 큰 패킷을 보내면 패킷이 여러 조각으로 나뉘거나, 설정에 따라 전송에 실패할 수 있습니다.
이번 글에서는 리눅스 환경에서 MTU 값을 직접 설정하고, ping, tcpdump, tshark, tracepath를 사용하여
패킷 단편화가 어떻게 발생하는지 확인하겠습니다.
MTU란
MTU는 Maximum Transmission Unit의 약자입니다.
우리말로는 최대 전송 단위라고 합니다.
네트워크에서 MTU는 하나의 링크에서 한 번에 전송할 수 있는 IP 패킷의 최대 크기를 의미합니다.
여기서 중요한 점은 MTU가 단순히 애플리케이션 데이터 크기만 의미하는 것이 아니라는 점입니다.
MTU는 IP 헤더를 포함한 IP 패킷 전체 크기를 기준으로 합니다.
일반적인 이더넷 MTU는 1500 byte입니다.
따라서 IP 패킷 전체 크기가 1500 byte 이하이면 보통 단편화 없이 전송될 수 있습니다.
반대로 IP 패킷 전체 크기가 MTU보다 크면 패킷을 그대로 보낼 수 없습니다.
이때 운영체제나 네트워크 장비는 패킷을 더 작은 조각으로 나누어 보내야 합니다.
이 과정을 IP 단편화(Fragmentation)라고 합니다.
패킷 단편화란
패킷 단편화는 MTU보다 큰 IP 패킷을 여러 개의 작은 IP 패킷으로 나누어 전송하는 과정입니다.
원래 하나였던 IP 패킷은 여러 fragment로 나뉘어 목적지까지 전달됩니다.
목적지에서는 이 fragment들을 다시 조립하여 원래의 IP 패킷으로 복원합니다.
IPv4에서는 단편화를 확인할 때 다음 필드들이 중요합니다.
Identification
같은 원본 IP 패킷에서 나온 조각인지 구분하는 값
Flags
DF, MF 같은 플래그를 확인하는 필드
DF
Don't Fragment의 약자로, 패킷 단편화 금지를 의미
MF
More Fragments의 약자로, 뒤에 조각이 더 있다는 의미
Fragment Offset
현재 조각이 원래 패킷의 어느 위치부터 시작하는지 나타내는 값
단편화가 발생하면 같은 Identification 값을 가진 패킷이 여러 개 보입니다.
첫 번째 조각에는 보통 MF 플래그가 설정되어 있고, 마지막 조각에는 MF 플래그가 설정되지 않습니다.
두 번째 조각부터는 Fragment Offset 값이 0보다 크게 나타납니다.
DF 비트와 PMTUD
DF 비트가 설정된 패킷은 중간에서 단편화될 수 없습니다.
따라서 DF 비트가 설정된 패킷이 MTU보다 크면 해당 패킷은 전송되지 못하고 오류가 발생합니다.
이 동작은 PMTUD와 관련이 있습니다.
PMTUD는 Path MTU Discovery의 약자입니다.
목적지까지 가는 경로에서 사용할 수 있는 최대 MTU를 찾는 과정입니다.
네트워크 경로에는 여러 구간이 있을 수 있습니다.
그중 가장 작은 MTU를 가진 구간이 전체 통신에서 사용할 수 있는 최대 패킷 크기를 결정합니다.
따라서 PMTUD는 단편화를 많이 하기 위한 기능이 아니라,
오히려 단편화를 피하기 위해 적절한 패킷 크기를 찾는 과정입니다.
실험 목적
이번 실험의 목적은 리눅스 환경에서 MTU와 패킷 단편화의 동작을 직접 확인하는 것입니다.
구체적으로 확인할 내용은 다음과 같습니다.
첫 번째는 리눅스 네트워크 인터페이스의 MTU 값을 직접 설정하고 확인하는 것입니다.
두 번째는 MTU보다 작은 패킷이 정상적으로 전송되는지 확인하는 것입니다.
세 번째는 DF 비트가 설정된 상태에서 MTU보다 큰 패킷을 보내면 어떤 오류가 발생하는지 확인하는 것입니다.
네 번째는 DF 비트를 설정하지 않았을 때 MTU보다 큰 패킷이 실제로 단편화되는지 확인하는 것입니다.
다섯 번째는 tcpdump와 tshark를 통해 단편화된 패킷의 필드를 직접 확인하는 것입니다.
여섯 번째는 tracepath를 사용하여 경로 MTU를 확인하는 것입니다.
실험 전체 구조
이번 실험은 Ubuntu VM 두 대를 사용하여 진행합니다.
기존 글에서는 하나의 리눅스 안에서 네트워크 네임스페이스를 만들어 실험하는 구조였지만,
이번 실험에서는 실제 Ubuntu VM 두 대를 사용합니다.
첫 번째 VM은 패킷을 보내는 송신측으로 사용합니다.
두 번째 VM은 패킷을 받는 수신측으로 사용합니다.
실험에 사용할 VM 정보는 다음과 같습니다.
VM1 송신측
인터페이스: enX0
IP 주소: 192.168.20.9/24
기존 MTU: 1500
VM2 수신측
인터페이스: enX0
IP 주소: 192.168.20.10/24
기존 MTU: 1500
실험 구조는 다음과 같습니다.
VM1 Ubuntu <----------------> VM2 Ubuntu
192.168.20.9 192.168.20.10
enX0 enX0
MTU 1200 MTU 1200
VM1에서 VM2로 ping 패킷을 보내면서 패킷 크기에 따라 정상 전송, 전송 실패, 단편화 발생 여부를 확인합니다.
이번 실험에서는 두 VM의 enX0 MTU를 1500에서 1200으로 낮춥니다.
MTU를 1200으로 낮추는 이유는 단편화 상황을 더 쉽게 만들기 위해서입니다.
일반적인 이더넷 MTU는 1500이지만,
MTU를 1200으로 낮추면 ping 패킷 크기를 조금만 조절해도 MTU 초과 상황을 쉽게 만들 수 있습니다.
ping 패킷 크기 계산
ping 명령어에서 -s 옵션은 ICMP 데이터 크기를 지정하는 옵션입니다.
여기서 주의해야 할 점은 -s 값이 전체 IP 패킷 크기가 아니라는 점입니다.
IPv4 기준 ping 패킷에는 ICMP 데이터 외에도 IPv4 헤더와 ICMP 헤더가 추가됩니다.
IPv4 헤더는 기본 20 byte입니다.
ICMP 헤더는 8 byte입니다.
따라서 실제 IP 패킷 크기는 다음과 같이 계산됩니다.
실제 IP 패킷 크기 = ping -s 값 + IPv4 헤더 20 byte + ICMP 헤더 8 byte
IPv4 ping에서는 보통 28 byte가 추가됩니다.
이번 실험에서 설정할 MTU는 1200 byte입니다.
따라서 단편화 없이 보낼 수 있는 최대 ping 데이터 크기는 다음과 같습니다.
1200 - 20 - 8 = 1172
따라서 ping -s 1172는 전체 IP 패킷 크기가 1200 byte가 되어 MTU에 딱 맞습니다.
반면 ping -s 1173은 전체 IP 패킷 크기가 1201 byte가 되어 MTU를 초과합니다.
-s 1172 → 1172 + 8 + 20 = 1200 byte
-s 1173 → 1173 + 8 + 20 = 1201 byte
이번 실험에서는 이 1 byte 차이를 이용하여 패킷 전송 결과를 비교합니다.
추가로 ping 결과에서 보이는 bytes 값은 -s 값과 다르게 보일 수 있습니다.
예를 들어 -s 1172로 보냈을 때 결과에는 1180 bytes로 표시될 수 있습니다.
ICMP 데이터 1172 byte에 ICMP 헤더 8 byte가 포함되어 보이기 때문입니다.
하지만 MTU 계산에서는 여기에 IPv4 헤더 20 byte까지 더해야 합니다.
실험 환경 구성
Ubuntu 기준으로 실험을 진행합니다.
먼저 VM1과 VM2에서 필요한 패키지를 설치합니다.
sudo apt install -y iproute2 iputils-ping tcpdump iputils-tracepath tshark
(iproute2와 iputils-ping은 혹시나 서버에 기본적으로 안깔려 있는 분들을 위해 명령어에 추가했습니다.)
각 패키지의 역할은 다음과 같습니다.
iproute2는 네트워크 인터페이스, 라우팅, 주소, MTU를 확인하고 변경하는 도구입니다.
iputils-ping은 ping 명령어를 제공하는 패키지입니다.
tcpdump는 터미널에서 패킷을 캡처하는 도구입니다.
iputils-tracepath는 경로 MTU를 확인할 수 있는 tracepath 명령어를 제공합니다.
tshark는 터미널 기반 패킷 분석 도구입니다.
MTU 값 변경
이제 두 VM의 enX0 인터페이스 MTU를 1200으로 변경합니다.
VM1에서 다음 명령어를 실행합니다.
sudo ip link set dev enX0 mtu 1200
VM2에서도 동일하게 실행합니다.
sudo ip link set dev enX0 mtu 1200
변경 후 MTU가 제대로 적용되었는지 확인합니다.
VM1에서 다음 명령어를 실행합니다.
ip link show enX0
VM2에서도 동일하게 실행합니다.
ip link show enX0
출력 결과에서 mtu 1200이 보이면 정상입니다.

DF 설정 후 MTU 이하 패킷 전송
이번에는 DF 비트를 설정한 상태에서 MTU에 딱 맞는 크기의 패킷을 전송합니다.
DF 비트가 설정되면 해당 패킷은 중간에서 단편화될 수 없습니다.
먼저 MTU를 초과하지 않는 크기로 ping을 보냅니다.
VM1에서 다음 명령어를 실행합니다.
ping -4 -I enX0 -c 3 -M do -s 1172 192.168.20.10
옵션의 의미는 다음과 같습니다.
-4는 IPv4로 ping을 실행한다는 의미입니다.
-I enX0는 enX0 인터페이스를 사용한다는 의미입니다.
-c 3은 ping을 3번만 보내고 종료한다는 의미입니다.
-M do는 DF 비트를 설정하여 단편화를 금지한다는 의미입니다.
-s 1172는 ICMP 데이터 크기를 1172 byte로 설정한다는 의미입니다.
전체 IP 패킷 크기는 다음과 같습니다.
1172 + 8 + 20 = 1200 byte
MTU가 1200이므로 패킷은 단편화 없이 정상적으로 전송됩니다.

여기서 1180 bytes는 ICMP 데이터 1172 byte와 ICMP 헤더 8 byte를 합친 값입니다.
IP 패킷 전체 크기는 여기에 IPv4 헤더 20 byte를 더한 1200 byte입니다.
이 결과는 DF 비트가 설정되어 있어도 패킷 크기가 MTU 이하라면 정상적으로 전송된다는 것을 보여줍니다.
DF 설정 후 MTU 초과 패킷 전송
이번에는 DF 비트를 설정한 상태에서 MTU보다 1 byte 큰 패킷을 전송합니다.
VM1에서 다음 명령어를 실행합니다.
ping -4 -I enX0 -c 3 -M do -s 1173 192.168.20.10
전체 IP 패킷 크기는 다음과 같습니다.
1173 + 8 + 20 = 1201 byte
MTU 1200보다 1 byte 큽니다.
그런데 -M do 옵션으로 DF 비트가 설정되어 있기 때문에 패킷을 쪼갤 수 없습니다.
결과적으로 리눅스 커널은 해당 패킷을 전송하지 못하고 오류를 출력합니다.

이 결과는 MTU보다 큰 패킷이면서 DF 비트가 설정되어 있으면 단편화되지 않고 전송에 실패한다는 것을 보여줍니다.
DF 비트가 설정된 상태에서는 MTU를 초과하는 패킷을 전송할 수 없습니다.
DF 비트 없이 실제 단편화 발생시키기
이번에는 DF 비트를 설정하지 않고 MTU보다 큰 ping 패킷을 전송합니다.
VM1에서 다음 명령어를 실행합니다.
ping -4 -I enX0 -c 3 -M dont -s 2000 192.168.20.10
옵션의 의미는 다음과 같습니다.
-M dont는 DF 비트를 설정하지 않는다는 의미입니다.
필요하면 패킷을 단편화할 수 있습니다.
-s 2000은 ICMP 데이터 크기를 2000 byte로 설정한다는 의미입니다.
전체 IP 패킷 크기는 다음과 같습니다.
2000 + 8 + 20 = 2028 byte
MTU 1200보다 훨씬 큽니다.
하지만 이번에는 DF 비트를 설정하지 않았기 때문에
리눅스는 이 큰 패킷을 여러 개의 작은 IP 패킷으로 나누어 전송할 수 있습니다.

여기서 2008 bytes는 ICMP 데이터 2000 byte와 ICMP 헤더 8 byte를 합친 값입니다.
IP 패킷 전체 크기는 여기에 IPv4 헤더 20 byte를 더한 2028 byte입니다.
이 패킷은 MTU 1200보다 크기 때문에 네트워크로 나갈 때 여러 fragment로 나뉘어 전송됩니다.
tcpdump로 단편화 관찰
단편화가 실제로 일어나는지 확인하기 위해 tcpdump를 사용합니다.
이 실험은 터미널을 두 개 열고 진행하면 좋습니다.
첫 번째 터미널에서는 VM2에서 패킷을 캡처합니다.
VM2에서 다음 명령어를 실행합니다.
sudo tcpdump -i enX0 -nn -vvv '((icmp) or (ip[6:2] & 0x3fff != 0)) and (host 192.168.20.9 and host 192.168.20.10)'
명령어의 의미는 다음과 같습니다.
-i enX0는 enX0 인터페이스에서 패킷을 캡처한다는 의미입니다.
-nn은 IP 주소와 포트 번호를 이름으로 바꾸지 않고 숫자로 출력한다는 의미입니다.
-vvv는 패킷 정보를 자세히 출력한다는 의미입니다.
icmp는 ICMP 패킷을 확인하는 필터입니다.
ip[6:2] & 0x3fff != 0은 IPv4 단편화 관련 필드가 있는 패킷을 확인하는 필터입니다.
host 192.168.20.9 and host 192.168.20.10은 VM1과 VM2 사이의 패킷만 보겠다는 의미입니다.
두 번째 터미널에서는 VM1에서 큰 ping 패킷을 전송합니다.
VM1에서 다음 명령어를 실행합니다.
ping -4 -I enX0 -c 1 -M dont -s 2000 192.168.20.10

tcpdump 출력에서 frag, offset, flags, length와 같은 값을 확인할 수 있습니다.
단편화된 패킷은 같은 원본 패킷에서 나왔기 때문에 같은 Identification 값을 가집니다.
첫 번째 조각에는 뒤에 더 많은 조각이 있다는 의미의 MF 플래그가 설정됩니다.
마지막 조각은 더 이상 뒤에 조각이 없기 때문에 MF 플래그가 설정되지 않습니다.
두 번째 조각부터는 Fragment Offset 값이 존재합니다.
Fragment Offset은 해당 조각이 원래 패킷의 어느 위치부터 시작하는지를 나타냅니다.
pcap 파일로 저장하기
tshark에서 더 자세히 보기 위해 tcpdump 결과를 pcap 파일로 저장할 수 있습니다.
VM2에서 다음 명령어를 실행합니다.
sudo tcpdump -i enX0 -nn -w mtu_fragment.pcap '((icmp) or (ip[6:2] & 0x3fff != 0)) and (host 192.168.20.9 and host 192.168.20.10)'
이 명령어에서 -w는 write의 의미입니다.
캡처 결과를 화면에 출력하지 않고 파일로 저장하겠다는 의미입니다.
mtu_fragment.pcap은 저장할 파일 이름입니다.
그 다음 VM1에서 큰 ping 패킷을 전송합니다.
ping -4 -I enX0 -c 1 -M dont -s 2000 192.168.20.10
캡처를 중단할 때는 tcpdump가 실행 중인 VM2 터미널에서 Ctrl + C를 입력합니다.
tshark로 단편화 확인
pcap 파일을 저장했다면 이제 tshark로 단편화된 패킷을 확인합니다.
tshark는 Wireshark의 터미널 버전이라고 볼 수 있습니다.
GUI 화면 없이도 pcap 파일을 읽고, 필요한 필드만 골라서 확인할 수 있습니다.
ICMP 패킷과 IP 단편화 정보를 함께 깔끔하게 확인하기 위해 다음 명령어를 사용했습니다.
tshark -r mtu_fragment.pcap -o ip.defragment:FALSE -Y 'ip.proto == 1' -T fields \
-e frame.number -e ip.src -e ip.dst -e ip.id -e ip.len \
-e ip.flags.df -e ip.flags.mf -e ip.frag_offset \
-e icmp.type -e icmp.ident -e icmp.seq \
-E header=y -E separator=/t -E occurrence=f | column -t -s $'\t'
-r 옵션은 저장된 pcap 파일을 읽겠다는 의미입니다.
-o ip.defragment:FALSE는 tshark가 조각난 패킷을 자동으로 재조립하지 않고,
fragment 상태 그대로 보여주도록 하는 옵션입니다.
-Y 'ip.proto == 1'은 ICMP 프로토콜 패킷을 확인하겠다는 의미입니다.
-T fields는 필요한 필드만 선택해서 출력하겠다는 의미입니다.
-E header=y는 첫 줄에 필드 이름을 출력하겠다는 의미입니다.
-E separator=/t는 필드 사이를 탭으로 구분하겠다는 의미입니다.
column -t -s $'\t'는 탭으로 구분된 결과를 표처럼 칸에 맞춰 정렬해주는 명령어입니다.
이번 결과에서 중요하게 볼 값은 ip.id, ip.flags.mf, ip.frag_offset입니다.
ip.id는 같은 원본 IP 패킷에서 나온 조각인지 확인하는 값입니다.
ip.flags.mf는 More Fragments 플래그입니다.
값이 True이면 뒤에 조각이 더 있다는 의미입니다.
ip.frag_offset은 현재 조각이 원래 패킷의 어느 위치부터 시작하는지 나타내는 값입니다.
icmp.type은 ICMP 메시지 종류입니다.
icmp.type 8은 Echo Request(ping 요청 패킷)입니다.
icmp.type 0은 Echo Reply(ping 응답 패킷)입니다.

tshark 결과에서는 총 4개의 패킷이 확인되었습니다.
1번과 2번 패킷은 VM1에서 VM2로 가는 Echo Request입니다.
3번과 4번 패킷은 VM2에서 VM1로 돌아오는 Echo Reply입니다.
먼저 1번 패킷을 보면 출발지는 192.168.20.9이고, 목적지는 192.168.20.10입니다.
이는 VM1에서 VM2로 가는 패킷입니다.
icmp.type 값이 8이므로 Echo Request입니다.
ip.id 값은 0x16fe입니다.
ip.flags.mf 값은 True입니다.
ip.frag_offset 값은 0입니다.
따라서 1번 패킷은 Echo Request의 첫 번째 fragment입니다.
MF 값이 True이므로 뒤에 조각이 더 있다는 의미입니다.
2번 패킷도 출발지가 192.168.20.9이고, 목적지가 192.168.20.10입니다.
1번 패킷과 같은 방향으로 가는 패킷입니다.
2번 패킷의 ip.id 값도 0x16fe입니다.
1번 패킷과 2번 패킷의 ip.id 값이 같으므로,
두 패킷은 서로 다른 패킷이 아니라 하나의 원본 Echo Request가 나뉜 조각입니다.
2번 패킷의 ip.flags.mf 값은 False입니다.
ip.frag_offset 값은 147입니다.
따라서 2번 패킷은 Echo Request의 두 번째 fragment입니다.
MF 값이 False이므로 이 조각이 마지막 조각이라는 것을 알 수 있습니다.
이번에는 응답 패킷을 확인합니다.
3번 패킷은 출발지가 192.168.20.10이고, 목적지가 192.168.20.9입니다.
이는 VM2에서 VM1로 돌아오는 패킷입니다.
icmp.type 값이 0이므로 Echo Reply입니다.
ip.id 값은 0x9c40입니다.
ip.flags.mf 값은 True입니다.
ip.frag_offset 값은 0입니다.
따라서 3번 패킷은 Echo Reply의 첫 번째 fragment입니다.
MF 값이 True이므로 뒤에 조각이 더 있다는 의미입니다.
4번 패킷도 출발지가 192.168.20.10이고, 목적지가 192.168.20.9입니다.
3번 패킷과 같은 방향으로 돌아오는 패킷입니다.
4번 패킷의 ip.id 값도 0x9c40입니다.
3번 패킷과 4번 패킷의 ip.id 값이 같으므로, 두 패킷은 하나의 원본 Echo Reply가 나뉜 조각입니다.
4번 패킷의 ip.flags.mf 값은 False입니다.
ip.frag_offset 값은 147입니다.
따라서 4번 패킷은 Echo Reply의 두 번째 fragment입니다.
MF 값이 False이므로 이 조각이 마지막 조각입니다.
정리하면 Echo Request와 Echo Reply가 모두 단편화되었습니다.
Echo Request는 다음 두 조각으로 나뉘었습니다.
1번 패킷: 192.168.20.9 → 192.168.20.10, ip.id 0x16fe, MF True, offset 0
2번 패킷: 192.168.20.9 → 192.168.20.10, ip.id 0x16fe, MF False, offset 147
Echo Reply는 다음 두 조각으로 나뉘었습니다.
3번 패킷: 192.168.20.10 → 192.168.20.9, ip.id 0x9c40, MF True, offset 0
4번 패킷: 192.168.20.10 → 192.168.20.9, ip.id 0x9c40, MF False, offset 147
VM1에서 VM2로 보낸 ping 요청도 단편화되었고, VM2에서 VM1로 돌아온 ping 응답도 단편화되었습니다.
이 결과를 통해 MTU 1200 환경에서
큰 ICMP 패킷이 여러 개의 IP fragment로 나뉘어 전송되었음을 확인할 수 있습니다.
주의할 점
2번 패킷과 4번 패킷에서는 icmp.type, icmp.ident, icmp.seq 값이 비어 있을 수 있습니다.
이것은 오류가 아닙니다.
ICMP 헤더는 첫 번째 fragment에만 들어 있습니다.
뒤쪽 fragment에는 ICMP 데이터의 나머지 부분만 들어 있기 때문에
tshark가 ICMP type이나 sequence 값을 표시하지 못할 수 있습니다.
따라서 단편화 여부를 확인할 때는 icmp.type보다
ip.id, ip.flags.mf, ip.frag_offset 값을 중심으로 보는 것이 좋습니다.
tracepath로 PMTUD 확인
다음으로 tracepath를 사용하여 PMTUD(Path MTU Discovery)를 확인합니다.
목적지까지 가는 경로에서 사용할 수 있는 최대 MTU를 찾는 과정입니다.
이번 실험에서는 VM1과 VM2의 enX0 MTU를 1200으로 설정했습니다.
따라서 VM1에서 VM2로 가는 경로의 MTU도 1200으로 확인될 수 있습니다.
VM1에서 다음 명령어를 실행합니다.
tracepath -n 192.168.20.10
tracepath는 목적지까지의 경로와 PMTU를 확인하는 명령어입니다.
-n 옵션은 DNS 이름 변환 없이 IP 주소를 숫자로 출력한다는 의미입니다.

여기서 pmtu 1200이 보이면
현재 VM1에서 VM2까지의 경로에서 사용할 수 있는 최대 MTU가 1200이라는 의미입니다.
다만 이번 실험은 두 VM이 같은 네트워크 대역에서 직접 통신하는 구조입니다.
따라서 이 결과는 복잡한 인터넷 경로의 PMTUD라기보다는,
현재 VM 간 직접 연결 경로에서의 MTU 확인에 가깝습니다.
실험 환경 원복
실험 확인이 끝났다면 변경했던 MTU 값을 원래대로 되돌립니다.
이번 실험에서는 enX0 인터페이스의 MTU를 1500에서 1200으로 낮춰서 사용했습니다.
따라서 실험이 끝난 뒤에는 두 VM 모두 MTU를 다시 1500으로 변경해야 합니다.
VM1에서 다음 명령어를 실행합니다.
sudo ip link set dev enX0 mtu 1500
VM2에서도 동일하게 실행합니다.
sudo ip link set dev enX0 mtu 1500
변경 후 MTU가 제대로 원복되었는지 확인합니다.
ip link show enX0
출력 결과에서 mtu 1500이 보이면 정상적으로 원복된 것입니다.

실험 결과 정리
이번 실험을 통해 MTU와 패킷 단편화의 동작 방식을 확인할 수 있었습니다.
처음 두 VM의 enX0 인터페이스는 MTU 1500으로 설정되어 있었습니다.
실험을 위해 VM1과 VM2의 enX0 MTU를 1200으로 변경했습니다.
MTU보다 작거나 같은 패킷은 DF 비트가 설정되어 있어도 정상적으로 전송되었습니다.
ping -M do -s 1172는 전체 IP 패킷 크기가 1200 byte였기 때문에 정상적으로 전송되었습니다.
반면 ping -M do -s 1173은 전체 IP 패킷 크기가 1201 byte였기 때문에 MTU 1200을 초과했습니다.
이때 DF 비트가 설정되어 있었기 때문에 패킷은 단편화되지 못하고 전송에 실패했습니다.
DF 비트를 설정하지 않은 ping -M dont -s 2000 명령어에서는
MTU보다 큰 패킷이 여러 개의 IP fragment로 나뉘어 전송되었습니다.
tcpdump로 해당 패킷을 pcap 파일로 저장했고, tshark로 pcap 파일을 읽어 단편화된 패킷을 확인했습니다.
tshark 결과에서 Echo Request는 ip.id 0x16fe를 가진 두 개의 fragment로 나뉘어 있었습니다.
첫 번째 request fragment는 MF 값이 True이고 offset이 0이었습니다.
두 번째 request fragment는 MF 값이 False이고 offset이 147이었습니다.
Echo Reply는 ip.id 0x9c40을 가진 두 개의 fragment로 나뉘어 있었습니다.
첫 번째 reply fragment는 MF 값이 True이고 offset이 0이었습니다.
두 번째 reply fragment는 MF 값이 False이고 offset이 147이었습니다.
이를 통해 ping 요청과 응답 모두 단편화되어 전송되었음을 확인할 수 있었습니다.
tracepath를 사용하여 VM1에서 VM2까지의 경로 MTU도 확인할 수 있었습니다.
결론
MTU는 네트워크에서 한 번에 전송할 수 있는 IP 패킷의 최대 크기입니다.
IPv4 ping에서 실제 패킷 크기는 ICMP 데이터 크기에 IPv4 헤더 20 byte와 ICMP 헤더 8 byte를 더해 계산합니다.
이번 실험에서는 Ubuntu VM 두 대를 사용하여
VM1 192.168.20.9에서 VM2 192.168.20.10으로 ICMP 패킷을 전송했습니다.
두 VM의 enX0 인터페이스 MTU를 1200으로 낮춘 뒤,
ping 명령어의 -M 옵션을 사용하여 DF 비트를 제어했습니다.
MTU 1200 환경에서 ping -s 1172는 전체 IP 패킷 크기가 1200 byte가 되어 정상적으로 전송되었습니다.
반면 ping -s 1173은 전체 IP 패킷 크기가 1201 byte가 되어 MTU를 초과했고,
DF 비트가 설정되어 있었기 때문에 전송에 실패했습니다.
DF 비트를 설정하지 않은 상태에서는 MTU보다 큰 패킷이 여러 fragment로 나뉘어 전송되었습니다.
이후 tcpdump로 저장한 pcap 파일을 tshark로 분석하여
Echo Request와 Echo Reply가 모두 단편화되었음을 확인했습니다.
Echo Request는 ip.id 0x16fe를 가진 두 조각으로 나뉘었고,
Echo Reply는 ip.id 0x9c40을 가진 두 조각으로 나뉘었습니다.
각각 첫 번째 조각은 MF 값이 True이고 offset이 0이었으며,
두 번째 조각은 MF 값이 False이고 offset이 147이었습니다.
이를 통해 하나의 큰 ICMP 요청과 응답이 MTU 1200 환경에서
여러 IP fragment로 나뉘어 전송되었음을 확인할 수 있었습니다.
이 실험은 VPN, 터널링, 클라우드 네트워크, 방화벽 환경에서 발생하는 MTU 문제를 이해하는 데 중요한 기초가 됩니다.
실무에서
"ping은 되는데 특정 서비스가 느리거나 끊기는 문제"
"VPN 연결은 되었지만 일부 사이트가 열리지 않는 문제"
"큰 파일 전송이 중간에 멈추는 문제"는 MTU와 PMTUD 문제와 관련될 수 있습니다.
따라서 MTU와 패킷 단편화의 동작 원리를 이해하는 것은 네트워크 문제 해결에서 매우 중요합니다.
'Network' 카테고리의 다른 글
| Wireshark로 실제 패킷 헤더와 16진수 덤프 확인하기 (0) | 2026.07.09 |
|---|---|
| TCP 흐름제어 (0) | 2026.07.05 |
| TCP 혼잡제어 (0) | 2026.06.25 |
| What is Socket with Vibe Coding (0) | 2026.06.19 |
| T-Pot을 안전하게 운영하기 위한 DMZ 네트워크 설계 (0) | 2026.06.18 |