XDP / eBPF

Comment écrire sa première règle XDP pour bloquer un port en 2026 ?

5 octobre 2026 · 9 min de lecture
Illustration immersive du sujet : règle XDP eBPF

Une règle XDP eBPF permet de bloquer un port réseau avant même que le paquet n'atteigne la pile TCP/IP du noyau Linux. En 2026, alors que les attaques DDoS volumétriques visent de plus en plus les ports de gestion et de service, maîtriser le filtrage au niveau XDP (eXpress Data Path) devient indispensable pour garantir une protection kernel-level efficace, rapide et peu coûteuse en ressources. Cet article détaille comment construire, déployer et orchestrer une règle XDP pour bloquer un port spécifique, en s'appuyant sur les capacités d'eBPF et sur l'architecture de PAKKT.io.



Pourquoi bloquer un port avec XDP plutôt qu'avec nftables ou iptables ?

La pile réseau classique — iptables, nftables, même conntrack — intervient après la réception du paquet par le driver réseau et son passage dans le sk_buff. Ce cheminement, bien qu'optimisé au fil des versions, consomme des cycles CPU pour allouer des structures, effectuer le parsing complet, puis évaluer les règles de filtrage. Sous une rafale de plusieurs millions de paquets par seconde (Mpps), cette latence cumulée peut saturer le CPU et dégrader les performances légitimes.

XDP intervient immédiatement après la réception du paquet par la carte réseau, dans le contexte du driver (mode natif) ou, à défaut, dans une couche générique (mode SKB). Un programme eBPF attaché à l'interface réseau inspecte chaque paquet en quelques nanosecondes et décide de trois actions possibles :

  • XDP_DROP : abandon du paquet, aucune allocation mémoire, aucun passage au kernel.
  • XDP_PASS : transfert normal vers la pile réseau classique (nftables, conntrack).
  • XDP_TX ou XDP_REDIRECT : renvoi ou redirection vers une autre interface (cas d'usage avancés, load-balancing).

Bloquer un port avec une règle XDP eBPF signifie donc éliminer le trafic indésirable avant toute allocation kernel. Le gain en latence est de l'ordre de la microseconde par paquet, et la charge CPU diminue drastiquement : un cœur saturé à 100 % avec nftables seul peut redescendre sous 20 % avec un programme XDP bien écrit.

Ce mode de filtrage est particulièrement adapté aux serveurs de jeu (Minecraft, FiveM, Rust), aux API publiques exposées sur des ports standards (80, 443, 8080), et aux services critiques où chaque milliseconde de latence compte. En revanche, XDP reste stateless : il n'a pas de table de connexions, ne peut pas analyser finement les flags TCP (SYN, ACK, FIN) sans logique supplémentaire, et ne gère pas nativement le suivi de session. C'est pourquoi PAKKT.io combine un programme XDP unique (PAKKT Engine) avec un firewall nftables stateful dans une table inet pakkt isolée, zéro conflit avec Docker ou fail2ban.



Anatomie d'une règle XDP eBPF : structure et pipeline de décision

Un programme XDP est écrit en C restreint, compilé avec Clang/LLVM en bytecode eBPF, puis chargé dans le noyau et attaché à une interface réseau via ip link ou bpftool. La signature de la fonction principale est :

SEC("xdp")
int xdp_firewall_prog(struct xdp_md *ctx)
{
    // Pointeurs début et fin du paquet
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    // Parsing Ethernet, IPv4/IPv6, TCP/UDP
    // Consultation de BPF maps (règles, blacklist, compteurs)
    // Décision XDP_DROP, XDP_PASS, XDP_TX

    return XDP_PASS; // ou XDP_DROP
}

Le contexte xdp_md fournit un pointeur brut vers les octets du paquet. Le programme doit vérifier les bornes (data + offset < data_end) avant chaque lecture, faute de quoi le verifier eBPF rejettera le programme au chargement. Une fois l'en-tête Ethernet lu, on identifie le protocole (IPv4 = 0x0800, IPv6 = 0x86DD), puis on parse l'en-tête IP pour extraire adresse source, adresse destination, protocole de transport. Enfin, si le protocole est TCP (IPPROTO_TCP = 6) ou UDP (IPPROTO_UDP = 17), on lit le port destination depuis l'en-tête de transport.

Consultation des BPF maps pour appliquer la règle

XDP ne peut pas stocker d'état en mémoire classique : il utilise des BPF maps, structures clé-valeur partagées entre l'espace noyau (programme XDP) et l'espace utilisateur (agent de gestion). PAKKT Engine repose sur une map de type BPF_MAP_TYPE_ARRAY indexée par ID de règle (0 à 255), chaque entrée contenant :

  • port_start, port_end : plage de ports couverte.
  • protocol : TCP (6), UDP (17), ICMP (1), ou any (0).
  • rule_type : block (1), rate_limit (2), allow_only (3).
  • max_pps : seuil global en paquets par seconde (rate-limit).
  • max_port_pps : seuil par port (rate-limit).
  • min_packet_size, max_packet_size : bornes de taille (filtrage des paquets anormaux).

Lors du traitement, le programme XDP parcourt les 256 entrées (en pratique, seules les règles actives sont non nulles), compare le port destination et le protocole, puis applique la logique :

if (rule.rule_type == BLOCK) {
    return XDP_DROP;
}
if (rule.rule_type == RATE_LIMIT) {
    // Incrémenter compteur dans map dédiée
    // Si seuil dépassé : XDP_DROP
}
if (rule.rule_type == ALLOW_ONLY) {
    // Inverser la logique : tout sauf cette règle = XDP_DROP
}

Cette approche permet de modifier les règles à chaud, sans recompiler ni recharger le programme XDP : l'agent PAKKT met à jour la map depuis l'espace utilisateur via bpf_map_update_elem, et le changement est instantané (quelques nanosecondes de latence). C'est ce qui distingue une plateforme comme PAKKT d'un script XDP statique : les 256 règles sont pilotables en temps réel depuis le panel web, avec audit log et synchronisation automatique sur tous les agents.



Déployer une règle XDP pour bloquer le port 25 (SMTP) : exemple pratique

Imaginons un serveur Debian 12 exposé sur Internet, hébergeant une API web sur le port 8080 et un serveur de jeu Minecraft sur le port 25565. Le port 25 (SMTP) n'est pas utilisé, mais subit des tentatives de relay abuse en provenance de botnets. Objectif : bloquer tout trafic TCP entrant sur le port 25 avant même la pile kernel.

Étape 1 : vérifier le support XDP du noyau et du driver

uname -r
# Exemple : 6.1.0-18-amd64

ethtool -i eth0 | grep driver
# Vérifier que le driver supporte XDP natif (ixgbe, i40e, mlx5, etc.)
# Si driver virtio (KVM, VPS), XDP générique (mode SKB) reste possible

Le noyau 5.x ou supérieur embarque le verifier eBPF et le hook XDP. Les drivers réseau récents (Intel ixgbe, i40e, Mellanox mlx5) supportent le XDP natif (meilleure performance). Les VPS sous virtio_net fonctionnent en mode generic XDP (moins rapide, mais toujours 10× plus performant que nftables seul sous DDoS volumétrique).

Étape 2 : écrire la règle dans le panel PAKKT (ou via API)

Dans le dashboard PAKKT.io, section Règles XDP de l'agent concerné :

  • Port range : 25 – 25
  • Protocole : TCP
  • Rule type : block
  • Max PPS : non applicable (block absolu)
  • Min/Max packet size : laisser vide (bloquer toutes tailles)

Enregistrer : l'agent PAKKT reçoit la configuration via mTLS (heartbeat 30s), met à jour la BPF map pakkt_rules à l'index correspondant, et le programme XDP commence immédiatement à dropper tout paquet TCP destination port 25. Aucune commande SSH manuelle, aucun redémarrage de service.

Étape 3 : vérifier l'application de la règle

bpftool prog show
# Repérer le programme XDP attaché à eth0, noter l'ID

bpftool map dump name pakkt_rules
# Affiche les 256 entrées ; chercher celle avec port_start=25, port_end=25, rule_type=1 (block)

tcpdump -i eth0 tcp port 25 -c 10
# Aucun paquet ne doit arriver dans tcpdump (drop avant la pile kernel)

Si des paquets apparaissent encore dans tcpdump, vérifier que le programme XDP est bien chargé en mode natif ou générique (ip link show eth0, chercher xdp ou xdpgeneric). En mode PAKKT, le statut est affiché dans le panel, section Logs internes de l'agent.

Couche nftables complémentaire : state tracking et burst

XDP bloque le paquet brut, mais ne garde aucune mémoire de session. Pour affiner la protection (par exemple, autoriser les connexions établies tout en limitant les nouvelles connexions), PAKKT déploie en parallèle une règle nftables dans la table inet pakkt :

nft add rule inet pakkt input tcp dport 25 ct state established,related accept
nft add rule inet pakkt input tcp dport 25 ct state new limit rate 5/second burst 10 packets accept
nft add rule inet pakkt input tcp dport 25 drop

Cette stratégie dual-layer (XDP stateless + nftables stateful) est orchestrée automatiquement par PAKKT : la règle XDP drop tout par défaut, sauf si l'administrateur active explicitement le mode allow_only dans le panel, auquel cas le programme XDP passe le paquet et nftables applique le rate-limit par connexion. Zéro conflit avec Docker (table filter séparée), fail2ban (chaîne INPUT non touchée), ou iptables-persistent.



Rate-limit XDP : limiter le port 80 à 100 000 pps sans bloquer

Bloquer un port n'est pas toujours la bonne réponse : un site web légitime sur le port 80 doit rester accessible, tout en se protégeant contre les rafales de requêtes HTTP flood. XDP permet un rate-limit stateless par paquet, complémentaire au rate-limit par connexion de nftables.

Principe du compteur per-port dans une BPF map

Le programme XDP maintient une map de type BPF_MAP_TYPE_PERCPU_ARRAY indexée par numéro de port, chaque entrée stockant un timestamp et un compteur de paquets. À chaque paquet arrivant sur le port 80, le programme :

  1. Lit le timestamp actuel (bpf_ktime_get_ns()).
  2. Compare avec le timestamp stocké : si écart > 1 seconde, réinitialise le compteur.
  3. Incrémente le compteur ; si supérieur à max_port_pps (100 000), retourne XDP_DROP.
  4. Sinon, retourne XDP_PASS.

Ce mécanisme est lock-free grâce aux maps per-CPU : chaque cœur a sa propre copie, évitant la contention. En pratique, sur un serveur 8 cœurs, la limite effective sera ~800 000 pps (8 × 100 000), sauf à agréger les compteurs dans l'espace utilisateur (complexité accrue).

Configuration PAKKT pour un rate-limit XDP sur le port 80

  • Port range : 80 – 80
  • Protocole : TCP
  • Rule type : rate_limit
  • Max port PPS : 100000
  • Max PPS global : laisser vide (rate-limit par port uniquement)

L'agent PAKKT écrit la règle dans la map pakkt_rules, et le programme XDP commence à compter. Les métriques (paquets passés, paquets droppés) remontent au panel en temps réel via TimescaleDB, affichées dans un graphique Packets Processed vs Dropped par port et par règle.

Combiner XDP et nftables pour un rate-limit multi-niveau

Pour une protection optimale, PAKKT recommande :

Couche Technologie Limite Granularité
L2/L3 brut XDP eBPF 100 000 pps Par port, stateless
L3/L4 session nftables conntrack 50 connexions/s Par IP source, stateful
L7 applicatif Application (Nginx, HAProxy) 1000 req/s Par route, authentification

Chaque couche filtre un type de menace : XDP élimine les floods volumétriques (amplification DNS, SYN flood massif), nftables bloque les scans de ports et les attaques par connexion lente (Slowloris), l'application gère la logique métier (rate-limit API, authentification). PAKKT orchestre les deux premières couches depuis un panel unique, avec audit log et synchronisation automatique des blacklists/whitelists (double couche XDP BPF map + règle nft).

Pour en savoir plus sur l'architecture kernel-level de PAKKT, consulter la documentation technique du blog et les spécifications eBPF officielles sur ebpf.io.



Conclusion

Bloquer un port avec XDP en 2026 n'est plus une option réservée aux experts du noyau : grâce aux plateformes SaaS comme PAKKT.io, toute équipe technique peut déployer une règle XDP eBPF en quelques clics, bénéficier d'un rate-limit stateless sub-microseconde, et orchestrer un firewall nftables stateful complémentaire, le tout pour 3 € par agent et par mois. La combinaison XDP + nftables offre une protection kernel-level rapide, légère, et zéro conflit avec l'existant, idéale pour sécuriser serveurs de jeu, API publiques et infrastructures critiques face aux DDoS volumétriques de nouvelle génération.



FAQ

Puis-je appliquer une règle XDP eBPF sur plusieurs ports simultanément ?

Oui : le champ port range de PAKKT accepte une plage (ex. 25565–25575) et le programme XDP vérifie si le port destination est compris dans l'intervalle. Vous pouvez définir jusqu'à 256 règles actives simultanément, chacune couvrant un ou plusieurs ports, avec des types (block, rate_limit, allow_only) et des protocoles (TCP, UDP, ICMP, any) différents. Les règles sont évaluées dans l'ordre d'index ; la première correspondance s'applique.

Quelle différence entre le rate-limit XDP (pps) et le rate-limit nftables (connexions/s ou bytes/s) ?

Le rate-limit XDP compte les paquets bruts (stateless, par port, tous protocoles confondus) avant toute allocation kernel. Il stoppe les floods volumétriques (millions de paquets UDP ou SYN). Le rate-limit nftables, lui, fonctionne après le parsing IP et s'appuie sur conntrack pour limiter les nouvelles connexions par seconde ou, avec le module quota, les octets. PAKKT combine les deux : XDP élimine le bulk, nftables affine par IP source et par session, garantissant une protection multi-niveau sans surcharge CPU.

Mon VPS utilise virtio_net : XDP fonctionne-t-il quand même ?

Oui, en mode generic XDP (flag xdpgeneric au chargement). Le programme s'exécute après l'allocation du sk_buff, donc avec une latence légèrement supérieure au mode natif, mais reste 10 à 20 fois plus rapide que nftables seul sous charge DDoS. L'agent PAKKT détecte automatiquement le mode supporté par le driver et charge le programme en conséquence. Les performances restent largement suffisantes pour protéger un serveur de jeu ou une API web contre des attaques de plusieurs centaines de milliers de pps.

Protégez vos serveurs

Déployez PAKKT en 30 secondes

Protection kernel double couche XDP + nftables, pilotable depuis un panel centralisé. Essai gratuit 7 jours.