Aller au contenu principal

Linux eBPF & XDP Networking Primer

Linux eBPF & XDP Networking Primer

Un guide pratique pour les programmes BPF, les crochets XDP et le traitement des paquets de contournement pour les ingénieurs réseau.

1. Qu'est-ce que l'eBPF?

eBPF (extension Berkeley Packet Filter) est un sous-système de noyau Linux qui vous permet d'exécuter des programmes sandboxed à l'intérieur du noyau sans modifier le code source du noyau ou charger des modules du noyau. Les programmes sont vérifiés par un vérificateur d'octets du noyau avant l'exécution, assurant la sécurité.

Pour la mise en réseau, les programmes du FBP e s'attachent à dans la pile réseau du noyau et peut inspecter, modifier, rediriger ou déposer des paquets. L'avantage clé sur ou modules du noyau est performance et programmabilité: les programmes eBPF sont compilés en code natif et peuvent partager l'état via (stockages à valeur clé partagés entre le noyau et l'espace utilisateur).

Crochet Lieu Latence Cas d'utilisation
XDP Pilote NIC, avant l'allocation sk buff Plus bas DDoS chute, équilibre de charge
tc entrée/sortie Après l'allocation de sk buff Faible Trafic, marquage, redirection
filtre socket Socket recevoir le chemin Moyenne filtrage de type tcpdump
kprobe/tracepoint Entrée/sortie de la fonction noyau Variantes Observabilité, traçage

2. Points de crochet XDP

Les programmes XDP (eXpress Data Path) fonctionnent au plus tôt dans la pile réseau — à l'intérieur du pilote NIC, avant que le noyau ne attribue un . Cela signifie:

  • Native XDP
  • XDP génériquesk_buff
  • XDP déchargé

Un programme XDP renvoie l'un des cinq verdicts :

Code de retour Décision
XDP_DROP Déposer le paquet immédiatement — latence la plus basse
XDP_PASS Passer à la pile réseau normale
XDP_TX Transmettre la même interface (bounce)
XDP_REDIRECT Rediriger vers une autre interface ou une socket AF XDP
XDP_ABORTED Chemin d'erreur — goutte avec l'événement de trace

3. Exemple de chute de paquets XDP

Le programme suivant supprime tous les paquets UDP d'une IP source stockée dans une carte eBPF, permettant à un plan de contrôle de l'espace utilisateur de mettre à jour la liste de blocs à l'exécution.

// xdp_drop_udp.c — Drop UDP from IPs in a BPF map
#include 
#include 
#include 
#include 
#include 

// BPF map: src IP → drop flag (1 = drop)
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1024);
    __type(key, __u32);    // source IPv4 address
    __type(value, __u32);  // 1 = block
} blocklist SEC(".maps");

SEC("xdp")
int xdp_drop_udp(struct xdp_md *ctx) {
    void *data     = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    // Parse Ethernet header
    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end) return XDP_PASS;
    if (eth->h_proto != __constant_htons(ETH_P_IP)) return XDP_PASS;

    // Parse IPv4 header
    struct iphdr *ip = (void *)(eth + 1);
    if ((void *)(ip + 1) > data_end) return XDP_PASS;
    if (ip->protocol != IPPROTO_UDP) return XDP_PASS;

    // Check blocklist map
    __u32 src = ip->saddr;
    __u32 *val = bpf_map_lookup_elem(&blocklist, &src);
    if (val && *val == 1) return XDP_DROP;

    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
La vérification des plaies est obligatoire.data_end

Charger et attacher avec :

# Compile
clang -O2 -target bpf -c xdp_drop_udp.c -o xdp_drop_udp.o

# Attach to interface (native XDP)
ip link set eth0 xdp obj xdp_drop_udp.o sec xdp

# Add an IP to the blocklist via bpftool
bpftool map update name blocklist key 0x01 0x02 0x03 0x04 value 0x01 0x00 0x00 0x00

# Remove XDP program
ip link set eth0 xdp off

4. AF XDP : Dépassement du noyau

AF_XDPXDP_REDIRECT

Composantes clés:

  • UMEM
  • Anneaux
  • Mode de copie zéro

AF XDP est idéal pour le traitement personnalisé de paquets au taux de ligne sans la complexité opérationnelle de DPDK (pas de pages énormes, pas de pinning CPU nécessaire pour une utilisation de base).

5. tc BPF: Configuration et filtrage du trafic

tcclsactsk_buff

// tc_mark.c — Mark packets with DSCP EF (46) for VoIP traffic on port 5060
#include 
#include 
#include 
#include 
#include 

SEC("classifier")
int tc_mark_voip(struct __sk_buff *skb) {
    void *data     = (void *)(long)skb->data;
    void *data_end = (void *)(long)skb->data_end;

    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end) return TC_ACT_OK;
    if (eth->h_proto != __constant_htons(ETH_P_IP)) return TC_ACT_OK;

    struct iphdr *ip = (void *)(eth + 1);
    if ((void *)(ip + 1) > data_end) return TC_ACT_OK;
    if (ip->protocol != IPPROTO_UDP) return TC_ACT_OK;

    struct udphdr *udp = (void *)(ip + 1);
    if ((void *)(udp + 1) > data_end) return TC_ACT_OK;

    // Mark SIP traffic (port 5060) with DSCP EF (46 = 0xB8 in TOS byte)
    if (udp->dest == __constant_htons(5060) || udp->source == __constant_htons(5060)) {
        // DSCP EF = 46, shifted left 2 bits in TOS field = 184 (0xB8)
        bpf_skb_store_bytes(skb, offsetof(struct iphdr, tos) + sizeof(struct ethhdr),
                            &((__u8){184}), 1, BPF_F_RECOMPUTE_CSUM);
    }
    return TC_ACT_OK;
}

char _license[] SEC("license") = "GPL";
# Attach tc BPF program
tc qdisc add dev eth0 clsact
tc filter add dev eth0 egress bpf da obj tc_mark.o sec classifier

6. Limites tarifaires avec les cartes eBPF

Les cartes eBPF permettent un traitement de qualité. Le modèle suivant implémente par source-IP limite le taux en utilisant un seau de jeton stocké dans un :

// Conceptual token bucket per source IP — checks tokens, drops if exceeded
struct ratelimit_entry {
    __u64 tokens;        // current token count
    __u64 last_update;   // nanoseconds timestamp
};

struct {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __uint(max_entries, 65536);
    __type(key, __u32);                     // source IP
    __type(value, struct ratelimit_entry);
} rate_map SEC(".maps");

// In XDP program:
// 1. bpf_ktime_get_ns() — get current time
// 2. Lookup entry for src IP
// 3. Refill tokens: tokens += (elapsed_ns / 1e9) * rate_pps
// 4. If tokens >= 1: decrement and XDP_PASS
// 5. Else: XDP_DROP

7. introspection bpftool & bpftrace

Deux outils essentiels pour travailler avec les programmes eBPF en direct :

# bpftool — inspect loaded programs and maps
bpftool prog list                         # list all loaded eBPF programs
bpftool prog show id 42                   # details for program ID 42
bpftool prog dump xlated id 42            # disassemble to eBPF bytecode
bpftool prog dump jited id 42            # dump JIT-compiled native code
bpftool map list                          # list all BPF maps
bpftool map dump name blocklist           # dump all entries in map "blocklist"
bpftool map update name blocklist \
    key 192 168 1 100 value 1 0 0 0       # add entry (network byte order)
# bpftrace — DTrace-style one-liners for kernel tracing
# Count XDP drops per second
bpftrace -e 'tracepoint:xdp:xdp_exception { @drops[args->action] = count(); } interval:s:1 { print(@drops); clear(@drops); }'

# Trace tcp_retransmit_skb — show retransmit events with comm name
bpftrace -e 'kprobe:tcp_retransmit_skb { printf("%s retransmit\n", comm); }'

# Histogram of packet sizes on eth0
bpftrace -e 'tracepoint:net:netif_receive_skb /args->name == "eth0"/ { @size = hist(args->len); }'

8. Comparaison : eBPF/XDP vs DPDK vs RDMA

Fonctionnalité eBPF/XDP DPDK RDMA
Participation du noyau Minimal (XDP dans le pilote) Aucune (passage complet) Aucune (RDMA NIC)
Modèle de mémoire Norme + AF XDP UMEM Pages importantes requises Régions de mémoire enregistrées
Débit maximal ~100 Gbps natif XDP > 100 Gbps 200+ Gbps (InfiniBand)
Utilisation du processeur Faible Élevées (noyaux à forte charge) Près de zéro (déchargement)
Complexité opérationnelle Faible — outils standard Haut — cœurs dédiés, immensespages Haute — gestion des tissus
Cas d'utilisation Atténuation du DDoS, LB, observabilité Routeurs virtuels, NFV, paquet gen Stockage (NVMe-oF), HPC MPI
Langue C / Rouille restreinte C / Rouille API Verbes (C)
Règle du pouce :