Что такое XDP
XDP (eXpress Data Path) - наиболее ранний hook для [[ebpf-basics|eBPF]]
в receive-pipeline сетевого стека. Программа выполняется до того,
как ядро аллоцирует sk_buff (универсальный буфер пакета) -
буквально на raw frame в DMA-области от NIC. Это даёт:
- Минимум CPU/пакет - 5-10x быстрее чем iptables
- Pre-allocation - можно дропнуть пакет без аллокации
- Прямой доступ к payload через указатель
Применяется для:
- DDoS filtering (Cloudflare обрабатывает 10M+ pps на одном CPU)
- L4 load balancer (Facebook Katran заменил IPVS)
- kube-proxy replacement в [[cni-plugins|cilium]]
- Sampling/mirroring для observability
Где XDP в сетевом стеке
NIC → DMA → ┌────────────────┐
│ XDP program │ ← здесь, до skb_alloc
│ return ACTION │
└─────┬──────────┘
│ XDP_PASS
▼
┌────────────────┐
│ skb_alloc │
│ netif_receive │
└─────┬──────────┘
│
▼
┌────────────────┐
│ tc-clsact │ ← вторая точка для eBPF (tc/ingress)
└─────┬──────────┘
│
▼
┌────────────────┐
│ netfilter │ ← iptables / nftables
│ (PREROUTING) │
└─────┬──────────┘
│
▼
routing → socket → app
Чем раньше - тем быстрее. XDP < tc-clsact < iptables по latency на пакет.
XDP actions
Программа возвращает один из action-кодов:
| Action | Что делает |
|---|---|
XDP_DROP | дропнуть, без аллокации skb (cheapest) |
XDP_PASS | передать в обычный сетевой стек |
XDP_TX | отправить пакет обратно на тот же интерфейс |
XDP_REDIRECT | перенаправить на другой интерфейс или socket |
XDP_ABORTED | дроп + tracepoint (debugging) |
REDIRECT - самое мощное: можно переправить в другой NIC (мост через
XDP), в [[veth-pair|veth]] (для container ingress), или в AF_XDP-сокет
(kernel bypass).
Три mode driver-поддержки
XDP исполняется быстрее всего, если NIC-драйвер его поддерживает напрямую. Возможные modes:
| Mode | Где исполняется | Скорость |
|---|---|---|
| Native (driver) | в RX hook драйвера, до DMA-completion | максимум |
| Offloaded | в SmartNIC | ещё быстрее, но редко (Netronome) |
| Generic (skb) | в netif_receive_skb (после skb_alloc) | fallback, медленнее |
Native-mode driver'ы (на 2025): mlx5, ixgbe, i40e, ice (Intel), bnxt_en (Broadcom), qede (Marvell), virtio-net (с 5.10+).
Если драйвер не поддерживает - грузится в generic mode (xdpgeneric),
работает но медленнее (skb уже аллоцирован).
Проверить:
ip -d link show eth0 | grep xdp
Прицепить XDP-программу
Через bpftool (libbpf-style):
bpftool prog loadall drop_all.bpf.o /sys/fs/bpf/drop type xdp
bpftool net attach xdp pinned /sys/fs/bpf/drop dev eth0
Через ip link (старее):
ip link set dev eth0 xdpdrv obj prog.o sec xdp_prog
Снять:
ip link set dev eth0 xdpdrv off
bpftool net detach xdp dev eth0
Опции xdpdrv (driver), xdpgeneric (force generic), xdpoffload
(HW offload).
Минимальная программа
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("xdp")int drop_icmp(struct xdp_md *ctx) {void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end) return XDP_PASS;
if (eth->h_proto != bpf_htons(ETH_P_IP)) return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end) return XDP_PASS;
if (ip->protocol == IPPROTO_ICMP) return XDP_DROP;
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";Каждая проверка > data_end обязательна - verifier требует доказать,
что доступ не выйдет за payload (см. ebpf-basics).
tc-clsact - второй hook на уровне TC
XDP - только на ingress. Для egress-фильтрации (или если нужен skb, например для conntrack-info) есть tc-clsact - eBPF на уровне Traffic Control:
tc qdisc add dev eth0 clsact
tc filter add dev eth0 ingress bpf da obj prog.o sec ingress
tc filter add dev eth0 egress bpf da obj prog.o sec egress
Программа в tc видит уже аллоцированный skb, имеет доступ к conntrack-info, но платит skb_alloc cost. Используется в cilium для pod-egress policy.
Сравнение:
| Hook | Когда | Action |
|---|---|---|
| XDP | до skb | DROP/PASS/TX/REDIRECT |
| tc-ingress | после skb_alloc, до netfilter | OK/SHOT/REDIRECT |
| tc-egress | перед отправкой | OK/SHOT/REDIRECT |
| netfilter (iptables/nft) | в L3 chain | ACCEPT/DROP/MARK/... |
AF_XDP - zero-copy в userspace
XDP может перенаправить пакет в AF_XDP-сокет - тогда фрейм приходит userspace без копирования через ring-buffer (как DPDK/Netmap, но в kernel-friendly формате).
Архитектура:
- 4 ring'а: TX, RX, FILL (free buffers), COMPLETION
- UMEM (User Memory) - shared между app и kernel
- kernel пишет packet pointer в RX ring
- app читает RX, обрабатывает, кладёт buffer в FILL для повторного использования
Throughput: 50+ Mpps на одном ядре, latency меньше микросекунды. Применяется в HFT, telco vRAN, DPI системах.
Минусы:
- Сложно (нужно управлять UMEM, ring'ами, busy-polling)
- Эксклюзивный pinning интерфейса (один app - один queue)
- Требует современный driver с zero-copy (mlx5, ice, i40e)
Real-world use cases
1. Cloudflare DDoS-фильтр
Cloudflare пишет XDP-программу, которая дропает SYN-flood, UDP amp, malformed-пакеты до netfilter'а. На каждом edge-сервере 10M+ pps фильтруется одним CPU. iptables на этом железе = 100% CPU.
2. Facebook/Meta Katran - L4 load balancer
Katran (open source) принимает все incoming пакеты, делает consistent hash по 5-tuple, выбирает backend, перенаправляет через XDP_TX или XDP_REDIRECT с IP-encapsulation. Заменил IPVS, обрабатывает 10x больше трафика на том же железе.
3. Cilium kube-proxy replacement
В [[cni-plugins|Cilium]] XDP+tc-eBPF реализуют:
- Service ClusterIP → backend resolution (заменяет IPVS/iptables)
- NodePort load balancing
- NetworkPolicy в eBPF
- Hubble observability
Latency пакета меньше, scale до 10K+ services без iptables-degradation.
XDP и MTU
XDP-программа может расширить пакет (bpf_xdp_adjust_head,
bpf_xdp_adjust_tail) - типично для encapsulation (IP-in-IP, VXLAN
через XDP). Если расширить за linkmtu + headroom - драйвер дропнет.
Headroom: 256 байт по умолчанию (можно bpf_xdp_adjust_head(-N) чтобы
добавить N байт перед пакетом).
Связь с [[mtu-and-pmtud|MTU и PMTUD]]: XDP-encap должен учитывать MTU underlay'я, иначе blackhole.
Pinned maps - shared state между программами
XDP-программа держит state в [[ebpf-basics|BPF maps]]. Чтобы карта
пережила unload программы и shared'илась между несколькими progs -
её pin в /sys/fs/bpf/:
bpftool map pin name allow_list /sys/fs/bpf/allow_list
Userspace process читает/обновляет:
bpftool map update pinned /sys/fs/bpf/allow_list key 0x01 0x02 0x03 0x04 \
value 0x01
Типичный паттерн: control-plane process поддерживает allow/block list, XDP-программа читает map на каждый пакет.
Когда что-то пошло не так
- Программа не загружается, "verifier rejected" - access за
data_end, или unbounded loop. Запусти сbpftool prog load ... -Lдля verifier-log. - Native mode не включается -
ip -d link showпоказываетxdpgenericвместоxdpdrv. Драйвер не поддерживает - или обновлять kernel + driver, или жить в generic. XDP_TXшлёт пакет в never-never - twin-port NIC иногда маршрутизирует TX на тот же физический интерфейс непредсказуемо. Проверьethtool -S eth0.- Падает throughput при включении XDP - generic-mode + сложная
программа. Профилируй через
perf top -p $(pidof <prog>). - AF_XDP socket не получает пакеты - забыл наполнить FILL ring,
или неправильный queue-id (
ethtool -Lдля multi-queue). - Conflict между XDP-программами - один интерфейс - одна XDP
программа. Используй
xdp_dispatcher(libxdp) для chaining.