네트워크 패킷 추적 도구 - PWRU: Packet, Where Are You?

 

네트워크 문제를 해결하다 보면, 패킷이 어디로 갔는지, 왜 도착하지 않는지 알기 어려운 상황에 종종 직면하게 됩니다. tcpdump나 ngrep 같은 기존 도구들은 패킷 추적에 유용하지만, 복잡한 네트워크 환경에서는 한계가 있습니다. 바로 이때 등장하는 것이 pwru입니다.

 

pwru란 무엇인가?

pwru는 "Packet, Where Are You?"의 약자로, eBPF(extended Berkeley Packet Filter) 기술을 사용해 Linux 커널에서 패킷을 추적하는 도구입니다. eBPF는 커널의 동작을 세밀하게 분석할 수 있도록 해주며, 이를 통해 패킷이 이동하는 경로를 정확히 추적할 수 있습니다. 특히 pwru는 Cilium 프로젝트의 일환으로 개발되었기 때문에, 복잡한 네트워크 환경을 갖춘 Kubernetes에서 패킷을 찾아내는 데 매우 유용합니다.

 

pwru가 유용하게 쓰일 수 있는 상황 (Cilium 블로그 사례)

pwru를 언제 사용하면 좋을지 아직 잘 감이 오지 않을 수 있으니 Cilium 블로그에서 소개된 실제 사례를 소개해보고자 합니다.

https://cilium.io/blog/2023/03/22/packet-where-are-you/

 

Going from Packet Where Aren’t You to pwru

Learn how pwru provides end-to-end insight to find the networking culprit even when you don't know it exists...

cilium.io

 

상황

리눅스 호스트에서 Kubernetes를 운영하고 있으며, 이 호스트들은 BGP(Unnumbered) 방식으로 설정되어 자체적으로 라우터 역할을 수행하고 있습니다. BGP Unnumbered 설정이 적용되면서 각 호스트의 네트워크 인터페이스(NIC)는 IPv6 링크 로컬 주소만을 가지게 되며, IPv4 라우팅은 이 NIC들을 경유지(Next-hop)로 지정하는 방식으로 이루어집니다. 결과적으로 IPv4 트래픽이 IPv6 링크 로컬 주소를 통해 라우팅되도록 설정되어 있는 셈입니다.

대부분의 경우 이 설정은 정상적으로 작동했지만, 최근 kube-proxy를 통해 전달되는 일부 트래픽이 예상치 못하게 차단되는 문제가 발생하기 시작했습니다. 라우팅이 정상적으로 진행되다가도 갑자기 멈추고, 한동안 지속되다가 다시 복구되는 상황이 반복되는 것입니다.

 

문제 확인

iptables TRACE 명령어로 패킷을 추적해 본 결과, kube-proxy가 DNAT(Destination Network Address Translation, 목적지 주소 변환) 작업을 수행한 후, MASQUERADE(출발지 주소를 외부 네트워크에 맞게 변경) 작업을 통해 패킷을 전송하고 있었습니다. 추적 결과에서는 모든 작업이 정상적으로 진행된 것처럼 보였지만, 실제로는 패킷이 서버를 떠나지 못하고 중간에 어디에선가 차단되고 있었습니다. iptables의 모든 규칙을 통과한 이후, NIC 드라이버에 도달하기 전에 패킷이 드롭(차단)되는 것으로 보였습니다.

 

 

 

pwru로 다시 분석해보기

pwru로 패킷의 흐름을 다시 분석한 결과, IP 마스커레이드 과정에서 패킷의 소스 주소가 예상과 다르게 설정된 것이 문제라는 사실을 발견했습니다. Linux에서 IP 마스커레이드는 패킷이 나가는 경로에 따라 동적으로 출발지 주소를 선택하지만, 지정된 IPv4 주소가 없는 경우 다른 인터페이스에서 무작위로 주소를 선택해 사용할 수 있습니다. 이로 인해 패킷이 잘못된 소스 주소를 갖게 되고, Linux의 AppArmor 또는 리버스 패스 필터와 같은 보안 정책이 이를 차단하게 된 것입니다.

 

pwru 설치하기

요구사항

커널 버전 요구사항

  • 커널 5.3 이상이 필요
  • --output-skb 옵션을 사용하려면 커널 5.9 이상이 필요 
  • --backend=kprobe-multi 옵션을 사용하려면 커널 5.18 이상이 필요

debugfs 마운트 (선택사항)

debugfs는 Linux 커널에서 제공하는 파일 시스템 중 하나로, 커널의 디버깅 정보에 접근할 수 있도록 해주는 가상 파일 시스템입니다.

debugfs 파일 시스템이 /sys/kernel/debug 경로에 마운트되어 있어야 합니다.

mount -t debugfs none /sys/kernel/debug

 

사용 가능한 옵션 

 

필수 커널 설정

zgrep <설정 값> /proc/config.gz

 

실제 결과입니다.

 

  1. CONFIG_DEBUG_INFO_BTF=y
    • 설명: 커널에 BTF(Binary Type Format) 정보를 포함하도록 설정합니다. BTF는 BPF 프로그램이 커널의 구조체와 변수를 이해할 수 있게 해주는 데이터 형식 정보입니다.
    • 사용 목적: 주로 BPF(Berkeley Packet Filter) 프로그램과 함께 사용되며, 커널 디버깅 및 추적 시 프로그램이 커널의 내부 구조를 이해할 수 있도록 지원합니다.
    • 사용 가능 커널 버전: 5.3 이상
  2. CONFIG_KPROBES=y
    • 설명: kprobes는 커널 함수의 특정 지점에 프로브(프로브를 통해 이벤트 발생 시 실행될 코드)를 삽입하여 디버깅과 성능 분석을 할 수 있도록 해주는 기능입니다.
    • 사용 목적: 특정 커널 함수가 호출될 때 해당 지점에 사용자 정의 동작을 삽입하여, 커널 동작을 분석하고 문제를 추적할 수 있습니다.
    • 활성화 대상: BPF 및 kprobe-multi 백엔드에서 사용
  3. CONFIG_PERF_EVENTS=y
    • 설명: perf_events는 리눅스의 성능 이벤트 수집 및 분석을 위한 프레임워크입니다. CPU 사이클, 캐시 미스와 같은 성능 지표를 모니터링할 수 있습니다.
    • 사용 목적: 성능 분석을 통해 시스템의 병목 현상을 파악하거나, 커널 및 애플리케이션의 성능 최적화를 도울 수 있습니다.
    • 활성화 대상: BPF 및 kprobe-multi 백엔드에서 사용
  4. CONFIG_BPF=y
    • 설명: **BPF(Berkeley Packet Filter)**는 원래 네트워크 패킷 필터링을 위해 개발되었지만, 최근에는 커널 전반의 데이터 패스 및 보안 작업에도 사용됩니다. eBPF는 커널 코드에 안전하게 동적 코드를 삽입할 수 있게 해줍니다.
    • 사용 목적: 네트워크 모니터링, 보안, 성능 최적화 등 다양한 영역에서 사용됩니다.
    • 활성화 대상: BPF 및 kprobe-multi 백엔드에서 사용
  5. CONFIG_BPF_SYSCALL=y
    • 설명: BPF_SYSCALL은 사용자 공간에서 커널로 BPF 프로그램을 로드하고, 해당 프로그램을 커널 내에서 실행할 수 있도록 해주는 기능입니다.
    • 사용 목적: 사용자 공간의 BPF 프로그램이 커널로 전송되어 실행될 수 있도록 지원하며, 네트워크 보안, 모니터링, 로깅에 사용됩니다.
    • 활성화 대상: BPF 및 kprobe-multi 백엔드에서 사용
  6. CONFIG_FUNCTION_TRACER=y
    • 설명: function tracer는 커널 함수 호출을 실시간으로 추적하는 기능을 제공합니다.
    • 사용 목적: 함수 호출 시점과 빈도를 추적해 커널의 성능과 동작을 분석할 수 있습니다.
    • 특별 요구사항: /sys/kernel/debug/tracing/available_filter_functions에서 제공되는 함수 목록을 사용해 특정 함수만 추적할 수 있습니다.
    • 활성화 대상: kprobe-multi 백엔드에서 사용
  7. CONFIG_FPROBE=y
    • 설명: fprobe는 kprobe와 유사하지만, 여러 함수에 대한 일괄적인 추적을 더욱 효율적으로 수행할 수 있도록 설계된 기능입니다.
    • 사용 목적: 여러 커널 함수에 대해 동시 추적을 수행하며, 성능 최적화나 문제 해결 시 유용하게 사용됩니다.
    • 사용 가능 커널 버전: 5.18 이상
    • 활성화 대상: kprobe-multi 백엔드에서 사용

 

실행해보기

pwru는 tcpdump처럼 목적지의 아이피로 가는 트래픽을 추적하게 하거나 하는 등의 설정도 가능하지만 좀 더 다양한 방식의 추적이 가능합니다.  (ex pwru dst port 80 )

  

네트워크 네임스페이스를 지정해서 실행하기

pwru --filter-netns "/proc/<pid>/ns/net

 

xdp가 적용된 패킷 추적

pwru --filter-trace-xdp

 

추적할 패킷 수 지정 과  결과 저장하기

pwru --output-file "/path/to/output.log" --output-limit-lines 100

 

해석하기

  • SKB: 패킷을 식별하는 socket buffer의 메모리 주소로, 각 패킷의 고유 ID와 같습니다.
  • CPU: 패킷을 처리한 CPU 번호로, 여러 CPU가 병렬로 패킷을 처리하는 시스템에서 성능 분석에 활용됩니다.
  • NETNS: 네트워크 네임스페이스의 inode 번호로, 패킷이 어느 네임스페이스에서 처리되고 있는지 구분하는 데 사용됩니다.
  • IFACE: 패킷을 처리한 네트워크 인터페이스를 나타내며, 인터페이스의 이름 또는 번호로 표시됩니다.
  • TUPLE: 패킷의 송수신 주소와 포트를 보여줍니다. 예를 들어, 172.17.0.3:6443->172.17.0.3:58072(tcp)는 송신지와 수신지의 IP와 포트를 나타냅니다.
  • FUNC: 패킷이 현재 처리된 커널 함수로, 예를 들어 nf_hook_slow는 Netfilter 훅을 거쳐 패킷을 처리하는 단계임을 나타냅니다.

 

 

마무리

이 도구는 내부 동작이 kprobe를 이용해 구현되어 있으며, 커널에 동적으로 후킹하여 디버깅 및 성능 분석을 수행할 수 있도록 설계되었습니다. 이를 통해 커널의 복잡한 동작을 효과적으로 추적하고 문제를 진단할 수 있습니다.