홈으로 돌아가기

Rust Traceroute: 경로 추적 작동 원리

이 기사는 Rust traceroute 구현을 분석합니다. TTL과 ICMP 메시지가 경로 맵을 만드는 방법을 보여줍니다. raw sockets 작동과 네트워크 패킷 처리 설명이 포함된 코드 제공.

traceroute가 경로를 구성하는 방법: Rust 기술 분석
Advertisement 728x90

Traceroute 작동 방식: Rust로 처음부터 구현하기

Traceroute는 인터넷을 통해 패킷이 지나가는 경로를 드러내는 네트워크 진단 도구입니다. IP 헤더의 TTL(생존 시간)과 ICMP 오류 메시지라는 두 가지 핵심 메커니즘에 의존합니다. 각 단계에서 TTL을 점진적으로 증가시킴으로써 라우터는 패킷의 수명이 만료될 때 알림을 보내 경로 지도를 구축합니다. Rust로 단 80줄의 코드로 우리만의 버전을 구현하면 프로토콜에 대한 깊은 이해를 통해 저수준 네트워크 작업이 얼마나 명확해지는지 알 수 있습니다.

UDP Probe 아키텍처

Traceroute의 핵심 요소는 제한된 TTL로 대상화된 패킷을 보내는 것입니다. UDP는 TCP 대신 세 가지 이유로 사용됩니다:

  • 핸드셰이크가 없어 오버헤드가 적음
  • 전달 보장이 없음—패킷은 의도적으로 드롭되도록 설계됨
  • 높은 포트 번호(33434부터 시작)로 실행 중인 서비스와의 충돌 최소화

코드에서는 두 개의 소켓을 초기화합니다:

Google AdInline article slot
let send_sock = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
send_sock.set_ttl_v4(ttl)?;

let recv_sock = Socket::new(
    Domain::IPV4,
    Type::from(libc::SOCK_RAW),
    Some(Protocol::ICMPV4),
)?;

첫 번째 소켓은 지정된 TTL로 버려질 패킷을 보내고, 두 번째 소켓은 ICMP 응답을 가로챕니다. 중요: raw 소켓은 네트워크 스택 수준에서 작동하므로 root 권한이 필요합니다.

ICMP 응답 파싱

라우터가 TTL=0에서 패킷을 드롭하면 유형 11(Time Exceeded)의 ICMP 메시지를 보냅니다. 데이터 구조는 다음과 같습니다:

  • 처음 20바이트—응답의 IP 헤더
  • 바이트 12-15—라우터의 IP 주소
  • 바이트 20—ICMP 메시지 유형

초기 구현에서는 IP 주소만 파싱했습니다:

Google AdInline article slot
if buf.len() >= 20 {
    let ip = Ipv4Addr::new(buf[12], buf[13], buf[14], buf[15]);
    Ok(Some(ip))
}

이로 인해 종단점 감지에 오류가 발생했습니다. 올바른 처리를 위해 ICMP 유형을 확인해야 합니다:

match buf[20] {
    11 => Ok(ProbeResult::Hop(ip)),
    3 if ip == target => Ok(ProbeResult::Reached(ip)),
    3 => Ok(ProbeResult::Hop(ip)),
    _ => Ok(ProbeResult::Timeout),
}

유형 3(Destination Unreachable)은 IP가 대상과 일치할 때만 대상 도달을 나타냅니다.

Traceroute 최적화

원래 traceroute는 기본 구현에 없는 두 가지 기법을 사용합니다:

Google AdInline article slot
  • 포트 번호 증가—후속 패킷마다 포트 +1로 보냄. UDP 헤더의 Identification 필드를 통해 ICMP 응답을 요청에 명확히 매칭할 수 있습니다.
  • TCP 모드 지원—방화벽에서 UDP가 차단될 때 낮은 TTL의 SYN 패킷을 사용합니다. 홉 감지 메커니즘은 동일합니다.

초기 코드 버전의 치명적 오류—유형 3 수신 시 IP 주소 확인 무시. if ip == target 조건이 없으면 첫 번째 Destination Unreachable을 반환하는 라우터에서 추적이 멈춥니다.

중요한 점

  • TTL을 제어 도구로—값을 점진적으로 증가시켜 각 홉을 순차적으로 발견
  • ICMP 메시지—진단의 기반—Time Exceeded(유형 11)와 Destination Unreachable(유형 3)이 경로 지도를 형성
  • Raw 소켓은 권한 필요—root 권한 없이 저수준 네트워크 작업 불가
  • UDP vs TCP—프로토콜 선택이 방화벽 우회에 영향을 주지만 기본 알고리즘은 동일
  • 바이너리 데이터 파싱—패킷 바이트에 직접 접근하려면 IP/ICMP 헤더 구조 지식 필요

종료 조건 구현

주요 개선 사항—대상이 도달했을 때 올바르게 감지. 간단한 Option<Ipv4Addr> 대신 ProbeResult enum을 사용합니다:

enum ProbeResult {
    Hop(Ipv4Addr),
    Reached(Ipv4Addr),
    Timeout,
}

메인 루프에서 결과 처리는 다음과 같습니다:

match hop {
    ProbeResult::Hop(ip) => println!("{:>2}  {}", ttl, ip),
    ProbeResult::Reached(ip) => {
        println!("{:>2}  {}", ttl, ip);
        break;
    }
    ProbeResult::Timeout => println!("{:>2}  *", ttl),
}

이렇게 하면 타임아웃이 아닌 대상 호스트 도달 시 추적이 멈춥니다. 실제 시나리오에서는 다음도 추가하세요:

  • TTL당 3회 프로브로 지연 통계 수집
  • 파편화된 패킷 처리
  • IPv6 지원
  • 거리 기반 타임아웃

요약 및 제한 사항

구현된 버전은 traceroute의 핵심을 보여주지만 제한이 있습니다:

  • 비대칭 경로 고려 안 함
  • ICMP 속도 제한 처리 안 함
  • 현대 네트워크의 MPLS 태그 무시
  • 슈퍼유저 권한 필요

프로덕션 솔루션에서는 pnet_packet 같은 라이브러리를 사용해 수동 바이트 파싱을 피하세요. 하지만 raw 버전을 직접 작성하는 것이 네트워크 기본을 이해하는 최고의 방법입니다. 모든 네트워크 엔지니어는 인터넷을 통해 데이터가 어떻게 이동하는지 파악하기 위해 최소 한 번 traceroute를 구현해야 합니다.

— Editorial Team

Advertisement 728x90

다음 읽기