컴퓨터 과학(CS)/네트워크

[네트워크] IP 단편화(IP Fragmentation)

마달랭 2025. 6. 6. 12:28

개요

IP 패킷 크기가 링크 MTU보다 클 경우 패킷을 한번에 보낼 수 없기 때문에 IP 단편화가 발생하게 된다.

원본 패킷 [IP헤더|데이터] (3020바이트)
        ↓ MTU 1500바이트로 제한
조각1: [IP헤더|데이터1] (1500바이트)
조각2: [IP헤더|데이터2] (1500바이트)  
조각3: [IP헤더|데이터3] (60바이트)
원본 패킷: 3000 바이트 데이터 + 20 바이트 IP 헤더 = 3020 바이트
MTU: 1500 바이트

조각 1: IP헤더(20) + 데이터(1480) = 1500 바이트
        - MF=1, Offset=0
        
조각 2: IP헤더(20) + 데이터(1480) = 1500 바이트  
        - MF=1, Offset=185 (1480/8)
        
조각 3: IP헤더(20) + 데이터(40) = 60 바이트
        - MF=0, Offset=370 (2960/8)

위와 같이 MTU가 1500 바이트일 경우 패킷의 크기가 3020바이트라면 3조각으로 나누어 진다.

단, TCP기준으로 IP헤더는 20바이트를 가지므로 매 조각마다 IP헤더가 추가되어 총 패킷의 크기는 3060바이트가 된다.

 

 

단편화 발생 조건

IP 패킷 크기가 링크 MTU보다 클 경우 패킷을 한번에 보낼 수 없기 때문에 IP 단편화가 발생하게 된다.
이는 라우터에서 자동으로 수행하며 DF(Don't Fragment) 플래그가 0일 경우 발생하게 된다.

if (패킷_크기 > 링크_MTU && DF_플래그 == 0) {
    // 단편화 수행
    단편화_수행();
} else if (패킷_크기 > 링크_MTU && DF_플래그 == 1) {
    // ICMP 에러 전송
    전송_ICMP_에러();
}

만약 패킷 크기가 MTU 이하이거나 DF 플래그가 1로 설정된 경우 ICMP 에러를 반환하므로 단편화가 발생하지 않는다.

 

 

IP 헤더의 단편화 관련 필드

┌─────────────────────────────────────┐
│ Identification (16bit)              │  ← 같은 원본 패킷 식별
├─────────────────────────────────────┤
│ Flags (3bit) │ Fragment Offset (13bit) │
│ [R][DF][MF]  │                     │
└─────────────────────────────────────┘

- DF (Don't Fragment): 단편화 금지
- MF (More Fragments): 더 많은 조각 있음
- Fragment Offset: 조각의 위치 (8바이트 단위)

1. Identification 필드

  • 같은 원본 패킷의 모든 조각에 동일한 값
  • 수신자가 조각들을 그룹화하는데 사용
  • 16비트로 65536개의 동시 패킷 식별 가능

2. Flags 필드

  • Reserved(R): 항상 0
  • Don't Fragment(DF): 단편화 금지 지시
  • More Fragments(MF): 뒤에 더 많은 조각이 있음을 표시

3. Fragment Offset 필드

  • 원본 데이터에서 해당 조각의 시작 위치
  • 8바이트 단위로 표현 (13비트 × 8 = 최대 65528바이트)
  • 마지막 조각은 MF=0, 나머지는 MF=1

 

단편화 규칙과 제약

1. 8바이트 경계 규칙

  • 마지막 조각을 제외한 모든 조각은 8바이트의 배수여야 함
  • Fragment Offset이 8바이트 단위로 표현되기 때문
  • 이 규칙을 위반하면 패킷이 버려질 수 있음

2. 최소 조각 크기

  • IPv4: 최소 68바이트 (IP헤더 60바이트 + 데이터 8바이트)
  • 실제로는 더 큰 크기가 효율적
  • 너무 작은 조각은 오버헤드가 과도하게 증가

 

재조립 (Reassembly) 이론

1. 수신자에서의 처리

  • Identification 필드로 같은 패킷의 조각들을 식별
  • Fragment Offset으로 조각들의 순서 결정
  • MF 플래그로 마지막 조각 확인
  • 모든 조각이 도착하면 원본 패킷 재구성

2. 재조립 버퍼 관리

  • 불완전한 패킷들을 임시 저장
  • 타임아웃 메커니즘으로 메모리 보호
  • IPv4는 최소 15초, IPv6는 60초 대기

3. 재조립 실패 조건

  • 조각 중 하나라도 손실
  • 재조립 타임아웃 발생
  • 버퍼 공간 부족
  • 조각 순서나 크기의 불일치

 

단편화의 성능 영향 분석

1. 처리 오버헤드

단편화 비용:
- CPU: 패킷 분할 연산
- 메모리: 추가 헤더 공간
- 네트워크: 헤더 오버헤드 증가

재조립 비용:
- CPU: 조각 순서 정렬, 버퍼 관리
- 메모리: 불완전한 패킷 버퍼링
- 타이머: 타임아웃 관리

 

2. 지연 시간 증가

정상 전송: RTT
단편화 전송: RTT + 재조립_대기시간 + 재전송_지연

 

3. 패킷 손실 위험성

원본 패킷 손실률 = 1 - (1 - 단일조각손실률)^조각수

예시:
- 단일 조각 손실률: 1%
- 3개 조각으로 분할
- 전체 패킷 손실률: 1 - (0.99)³ = 2.97%

 

 

리스크

1. 실시간성 저하

// 문제 상황
struct GamePacket {
    uint32_t sequence;
    uint32_t timestamp;
    PlayerAction actions[50];  // 큰 패킷!
};

// 1400바이트 패킷 → 단편화 발생
// 조각 하나라도 손실되면 전체 패킷 폐기

 

2. 대역폭 낭비

원본 데이터: 1400 바이트
단편화 후: 
- 조각1: IP헤더(20) + UDP헤더(8) + 데이터(1472) = 1500
- 조각2: IP헤더(20) + 데이터(928) = 948
총 오버헤드: 40바이트 → 60바이트 (50% 증가)

 

3. 버퍼 오버플로우

// 재조립 버퍼 관리
class FragmentManager {
private:
    std::unordered_map<uint16_t, FragmentBuffer> buffers;
    static const size_t MAX_BUFFERS = 1000;
    
public:
    void cleanup_expired_fragments() {
        auto now = std::chrono::steady_clock::now();
        for (auto it = buffers.begin(); it != buffers.end();) {
            if (now - it->second.timestamp > std::chrono::seconds(30)) {
                it = buffers.erase(it);  // 타임아웃된 조각 제거
            } else {
                ++it;
            }
        }
    }
};

 

 

최적화 전략

1. 패킷 크기 제한

class GamePacketManager {
private:
    static const size_t SAFE_PACKET_SIZE = 1200;  // 안전한 크기
    
public:
    void send_game_state(const GameState& state) {
        if (state.serialized_size() > SAFE_PACKET_SIZE) {
            // 여러 패킷으로 분할 전송
            split_and_send(state);
        } else {
            send_single_packet(state);
        }
    }
};

 

2. 적응형 MTU 설정

class AdaptiveMTU {
private:
    size_t current_mtu = 1500;
    size_t min_mtu = 576;
    
public:
    void handle_fragmentation_needed(size_t suggested_mtu) {
        current_mtu = std::max(min_mtu, suggested_mtu - 50);  // 여유분
        log_info("MTU adjusted to: " + std::to_string(current_mtu));
    }
    
    size_t get_safe_payload_size() {
        return current_mtu - 20 - 8;  // IP헤더 - UDP헤더
    }
};

 

3. 압축과 델타 인코딩

// 패킷 크기 최소화
struct CompressedGameState {
    uint32_t base_sequence;
    std::vector<uint8_t> delta_data;  // 압축된 변경사항만
    
    size_t serialize(uint8_t* buffer) {
        // zlib 압축 적용
        return compress_data(delta_data, buffer);
    }
};

 

4. QoS 기반 패킷 분류

enum PacketPriority {
    CRITICAL = 0,    // 작은 패킷, 단편화 방지
    NORMAL = 1,      // 중간 크기 허용
    BULK = 2         // 큰 패킷, 단편화 허용
};

void send_packet(const void* data, size_t size, PacketPriority priority) {
    if (priority == CRITICAL && size > SAFE_SIZE) {
        // 중요한 패킷은 분할 전송
        send_fragmented_application_layer(data, size);
    } else {
        send_normal(data, size);
    }
}

 

 

결론

  1. MTU는 네트워크에서 한 번에 전송할 수 있는 최대 패킷 크기이다.
  2. MTU보다 큰 패킷이 송신되어야 할 경우, DF값이 0일 경우 IP 단편화가 발생하게 된다.
  3. 단편화된 조각을 수신자가 받아 재조립 과정을 거쳐야 한다.
  4. IP 단편화로 인한 조각이 많아질 수록 패킷 손실 위험성이 커진다.
  5. 재조립 과정에서 하나의 조각이라도 손실되면 전체 패킷을 재전송해야한다.
  6. 실시간이 중요한 서비스에서 이는 CPU 오버헤드가 발생한다. 즉 서비스 품질 저하로 직결된다.
  7. 패킷 크기 제한, 압축과 델타 인코딩 등을 통해 최적화 전략을 수립해야 한다.
728x90