TT Lab
블로그 시작하기
배우기 러닝패스 코스

리눅스 커널과 eBPF

패킷이 소켓 버퍼가 되기 전에

TT Lab 에서 이어서 보기

한 줄 요약

XDP 는 드라이버가 패킷을 받자마자, 커널이 소켓 버퍼(sk_buff)를 만들기도 전에 BPF 프로그램을 돌리는 자리입니다. tc 는 그보다 조금 늦지만 들어오고 나가는 양쪽을 다 보는 자리입니다. Cilium 은 두 자리를 목적에 따라 나눠 씁니다.

왜 두 자리가 필요한가

패킷 하나를 처리하는 비용의 큰 몫은 sk_buff 를 할당하고 채우는 일입니다. 초당 수백만 패킷이 몰리는 DDoS 상황에서는 버릴 패킷에도 이 비용을 내다가 CPU 가 먼저 쓰러집니다. XDP 는 그 할당 전에 판정해서, 버릴 것은 드라이버의 수신 링에서 바로 버립니다(XDP_DROP). 로드 밸런서는 같은 자리에서 패킷을 고쳐 다른 장치로 곧장 보냅니다(XDP_TX, XDP_REDIRECT).

대신 XDP 는 들어오는 쪽만 보고, sk_buff 가 없으니 소켓이나 cgroup 같은 상위 정보를 쓸 수 없습니다. 나가는 패킷을 보거나 파드 단위로 정책을 걸려면 tc 자리가 필요합니다.

어떻게 동작하나

XDP 프로그램은 struct xdp_md *ctx 를 받습니다. ctx->data 부터 ctx->data_end 까지가 패킷입니다. 이더넷 머리, IP 머리를 차례로 읽기 전에 각각 끝이 data_end 를 넘지 않는지 비교해야 검증기가 받아 줍니다. 반환값이 판정입니다. XDP_PASS 는 평소대로 커널 네트워크 스택으로, XDP_DROP 은 버림입니다.

판정은 둘 말고도 더 있습니다. XDP_TX 는 받은 장치로 바로 되돌려 보내고, XDP_REDIRECT 는 다른 장치나 CPU, AF_XDP 소켓으로 넘깁니다. XDP_ABORTED 는 프로그램 오류를 뜻하는 버림인데, xdp:xdp_exception tracepoint 로만 보이므로 패킷이 이유 없이 사라지면 이 tracepoint 부터 켜 봅니다.

붙는 방식은 셋입니다. driver(native) 모드는 드라이버가 XDP 를 지원할 때 수신 경로 맨 앞에서 돕니다. generic 모드는 드라이버가 지원하지 않을 때 커널이 sk_buff 를 만든 뒤에 같은 프로그램을 돌려 주는 대체 경로라, 동작은 같아 보여도 XDP 의 이점이 대부분 사라집니다. offload 는 NIC 에서 직접 돕니다. ip link set dev X xdp obj ... 처럼 모드를 안 고르면 되는 쪽으로 붙으므로, 붙인 뒤 bpftool net show 에서 driver 인지 확인하는 습관이 필요합니다. veth 는 driver 모드를 지원합니다.

tc 프로그램은 struct __sk_buff *skb 를 받고 TC_ACT_OK·TC_ACT_SHOT 같은 판정을 돌려줍니다. 장치에 clsact qdisc 를 만들면 ingress 와 egress 두 훅이 생기고, direct-action 으로 붙이면 반환값이 그대로 판정이 됩니다. 커널 6.6 부터는 qdisc 없이 링크로 붙는 tcx 가 생겨, 여러 프로그램의 순서와 소유를 더 안전하게 다룹니다. tcx 로 붙은 프로그램은 tc filter show 에는 보이지 않고 bpftool net show 에만 보입니다.

현장에서 만나는 모습

Cilium 은 파드마다 생기는 veth 의 호스트 쪽에 tc(최신 커널에서는 tcx) 자리로 프로그램을 붙여, 파드에서 나가고 들어오는 패킷에 정책과 서비스 주소 변환을 적용합니다. kube-proxy 를 대체할 때 서비스 IP 를 백엔드 파드 IP 로 바꾸는 일이 여기서 일어납니다. 노드의 바깥 장치에는 NodePort 가속을 켜면 XDP 프로그램이 붙어, 외부에서 들어온 서비스 트래픽을 sk_buff 를 만들기 전에 처리합니다. 이때 NIC 드라이버가 driver 모드를 지원하는지가 성능을 가릅니다.

장애 조사에서는 "패킷이 어디서 사라졌나" 가 핵심 질문입니다. XDP 단계에서 버려진 패킷은 iptables 카운터에도, tcpdump 에도 보이지 않습니다. tcpdump 는 그보다 뒤에서 잡기 때문입니다. 그래서 XDP 프로그램은 버린 패킷도 반드시 맵에 세어 두어야 합니다.

English corner

다음 실습에서 할 것

veth 한 쌍으로 작은 네트워크를 만들고, 프로토콜별로 세는 XDP 프로그램을 driver 모드로 붙입니다. ping 을 센 뒤 ICMP 만 떨어뜨리게 고치고, 나가는 쪽은 tc egress 에서 셉니다. 마지막으로 bpftool net show 를 읽어 Cilium 이 붙는 자리와 잇습니다.