Les passerelles VPN à haute capacité et les nœuds de sécurité en périphérie de réseau traitent simultanément deux flux de trafic distincts : le trafic de terminaison locale (notamment les tunnels chiffrés WireGuard ou IPsec à destination de sockets résidant sur l'hôte) et le trafic de transit transféré (les paquets décapsulés des utilisateurs acheminés à travers les interfaces réseau vers le réseau étendu WAN). Au cœur de la pile réseau Linux, la gestion du chemin séparant la classification des trames entrantes et la consultation de la table de routage conditionne directement l'évolutivité du système.

Conçu pour éliminer les recherches répétitives dans l'arborescence FIB (Forwarding Information Base) pour les connexions déjà établies, le mécanisme Linux IP Early Demux (ip_early_demux) optimise le traitement des paquets dès leur entrée. Cependant, sur les équipements remplissant à la fois le rôle de point de terminaison cryptographique et de routeur de transfert à haut volume, le paramétrage d'Early Demux requiert une compréhension fine de ses compromis structurels.


Le dilemme du routage : Consultation FIB Trie contre Mise en cache Socket

Pour appréhender l'utilité d'IP Early Demux, il convient d'analyser le trajet classique suivi par un paquet IPv4 après sa prise en charge par le pilote de la carte réseau (NIC) :

  1. Réception du paquet : Le paquet pénètre dans la couche réseau via ip_rcv() et poursuit son exécution dans ip_rcv_finish_core().
  2. Recherche de routage standard : Par défaut, le noyau sollicite immédiatement ip_route_input_noref() pour interroger l'arbre FIB trie. Cette recherche examine l'adresse IP de destination, l'adresse source, les marqueurs de routage par politique (fwmark) et l'interface d'entrée afin de déterminer si le paquet est destiné à l'hôte local (RTN_LOCAL) ou s'il doit être réacheminé (RTN_UNICAST).
  3. Démultiplexage vers le socket local : Si la route correspond à RTN_LOCAL, le traitement bascule vers ip_local_deliver(). La couche de transport (TCP ou UDP) réalise alors une recherche dans la table de hachage des sockets (inet_lookup_established ou table des ports UDP) pour localiser la structure struct sock réceptrice.
[ Paquet IPv4 entrant ]
          │
          ▼
[ Trajet classique : ip_rcv_finish_core() ]
          │
          ▼
[ Consultation FIB Trie : ip_route_input_noref() ] ──► (Consommation de cycles CPU)
          │
          ├─► Si local (RTN_LOCAL) ──► ip_local_deliver() ──► Hachage Socket
          └─► Si transféré (RTN_UNICAST) ──► ip_forward()

Sur une passerelle traitant 5 millions de paquets par seconde répartis sur des milliers de tunnels VPN actifs, parcourir l'arbre FIB trie pour chaque trame engendre une charge mémoire considérable et perturbe les mémoires caches CPU L2 et L3.


Fonctionnement technique : Comment Early Demux court-circuite la FIB

IP Early Demux inverse cette séquence pour les flux de données établis. Avant même que le sous-système de routage ne soit appelé, le noyau procède à une recherche prédictive du socket récepteur directement depuis ip_rcv_finish_core().

[ Paquet IPv4 entrant ]
          │
          ▼
[ Early Demux : ip_rcv_finish_core() ]
          │
          ▼
[ Recherche précoce de socket : tcp_v4_early_demux() / udp_v4_early_demux() ]
          │
          ├─► Correspondance trouvée (sk->sk_rx_dst valide) :
          │         │
          │         ├─► Attribution directe de la destination en cache (skb_dst_set_noref)
          │         └─► Saut direct vers ip_local_deliver() [Évite l'arbre FIB]
          │
          └─► Aucune correspondance locale :
                    │
                    └─► Retour à la recherche classique via ip_route_input_noref()

Dans l'implémentation du noyau (net/ipv4/ip_input.c), Early Demux déclenche des fonctions d'assistance adaptées à chaque protocole :

/* Logique conceptuelle au sein de net/ipv4/ip_input.c */
static int ip_rcv_finish_core(struct net *net, struct sock *sk,
                             struct sk_buff *skb, struct net_device *dev,
                             const struct sk_buff *hint)
{
    const struct iphdr *iph = ip_hdr(skb);

    if (net->ipv4.sysctl_ip_early_demux && !skb_dst(skb)) {
        const struct net_protocol *ipprot;
        int protocol = iph->protocol;

        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot && ipprot->early_demux)
            ipprot->early_demux(skb);
    }

    /* Si la destination a été attachée par early_demux, la recherche FIB est omise */
    if (!skb_dst(skb)) {
        int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
                                      iph->tos, dev);
        if (unlikely(err))
            goto drop;
    }
    return NET_RX_SUCCESS;
}

Le mécanisme de cache (sk_rx_dst)

Lorsqu'une session TCP est établie ou que des sockets UDP fonctionnent en mode connecté (via l'appel connect()), le noyau enregistre l'entrée de routage de destination dans la structure sk->sk_rx_dst.

Lors de l'exécution de tcp_v4_early_demux(), le système extrait le quadruplet d'identification à partir des en-têtes IP et TCP, identifie le socket correspondant et valide l'entrée sk->sk_rx_dst. Si celle-ci est valide, elle est affectée directement à skb->_skb_refdst par l'intermédiaire de skb_dst_set_noref(skb, dst). Le paquet est immédiatement transmis au niveau supérieur sans solliciter la table de routage globale.


Le compromis opérationnel : Terminaison de tunnel contre Transfert de paquets

Bien que le mécanisme Early Demux procure des gains de débit significatifs pour le trafic local, il impose certains compromis sur les passerelles VPN hybrides :

  • Recherches infructueuses sur le trafic de transit : Lorsque la passerelle achemine les flux décapsulés des utilisateurs vers des interfaces externes, ces paquets ne ciblent aucun socket local. Si Early Demux est actif, le noyau effectue une tentative de recherche dans la table des sockets pour chaque paquet transféré, subissant un échec avant de se replier sur l'arbre FIB.
  • Risque d'invalidation lors de routages asymétriques : En cas de modification d'un routage multi-chemin (ECMP) ou de bascule de lien, l'entrée sk_rx_dst conservée en mémoire cache peut diriger temporairement les trames sur d'anciens paramètres avant réévaluation.
+-------------------------------------------------------------------------+
|                  Scénarios d'exploitation sur passerelle VPN            |
+-------------------------------------------------------------------------+
| Scénario A : Nœud de terminaison de tunnel (Point d'accès RiceGuard)    |
| -> Les paquets ciblent les sockets d'écoute locaux (encapsulation UDP)  |
| -> Optimisation : Activer ip_early_demux et udp_early_demux             |
|                                                                         |
| Scénario B : Routeur de transit et de transfert exclusif                |
| -> Les paquets transitent entre le WAN et les interfaces internes      |
| -> Optimisation : Désactiver ip_early_demux pour éviter les tests vains |
+-------------------------------------------------------------------------+

Configuration et ajustement d'Early Demux sous Linux

Linux propose plusieurs paramètres sysctl permettant de piloter Early Demux globalement ou par protocole de transport.

1. Contrôle des paramètres actuels

Consultez les valeurs actuellement appliquées sur votre serveur :

# Vérification des variables Early Demux
sysctl net.ipv4.ip_early_demux
sysctl net.ipv4.tcp_early_demux
sysctl net.ipv4.udp_early_demux

2. Configuration pour les nœuds de terminaison de tunnels

Sur les serveurs dédiés principalement à la réception et au déchiffrement de tunnels VPN (tels que les passerelles RiceGuard traitant des millions de trames UDP encapsulées), activez le mode précoce pour IP et UDP :

# Activation globale d'Early Demux
sysctl -w net.ipv4.ip_early_demux = 1

# Activation pour TCP (portails web et interfaces d'administration)
sysctl -w net.ipv4.tcp_early_demux = 1

# Activation pour UDP (terminaisons de tunnels connectés)
sysctl -w net.ipv4.udp_early_demux = 1

Pour pérenniser ces paramètres après le redémarrage du système, enregistrez-les dans /etc/sysctl.d/99-early-demux.conf :

cat << 'EOF' > /etc/sysctl.d/99-early-demux.conf
net.ipv4.ip_early_demux = 1
net.ipv4.tcp_early_demux = 1
net.ipv4.udp_early_demux = 1
EOF
sysctl --system

3. Ajustement pour les routeurs de transfert pur et passerelles NAT

Si la machine sert exclusivement de routeur de transit transférant les flux décapsulés sans héberger de sockets de terminaison, la désactivation d'Early Demux économise des cycles de calcul :

# Désactivation sur les routeurs de transit pur
sysctl -w net.ipv4.ip_early_demux = 0

4. Surveillance des performances avec nstat

Analysez l'efficacité du cache de routage et surveillez les compteurs statistiques via nstat :

# Suivi des correspondances de sockets et des compteurs de routage
nstat -az | grep -E "IpEarlyDemux|IpInReceives|IpForwDatagrams"

Comparatif technique : Early Demux actif contre inactif

Les répercussions pratiques d'Early Demux varient sensiblement selon la nature du trafic réseau :

Critère d'évaluation Early Demux actif (Sockets locaux) Early Demux inactif (Trajet standard) Early Demux sur routeur de transit
Charge FIB Trie Évitée pour les flux réguliers Consultée pour chaque paquet Consultation FIB avec recherche vaine
Empreinte Cache L2/L3 Concentrée sur la table de hachage Étendue sur toute l'arborescence FIB Plus élevée en raison du double accès
Cadence en paquets (PPS) 7,8 Mpps soutenus par cœur 5,9 Mpps soutenus par cœur Légère baisse de débit (~4 %)
Latence de transfert Identique (bascule vers la FIB) Déterministe et stable Hausse de 3 à 5 % sur petits paquets
Risque de route obsolète Faible (contrôle de validité dst) Nul (calcul systématique de la route) Nul (aucune association de socket)

Déploiement architectural au sein de RiceGuard

Au sein de l'infrastructure mondiale de VPNRice, les passerelles RiceGuard segmentent la terminaison des flux entrants et le routage de transit :

  1. Interfaces WAN d'entrée : Les interfaces publiques recevant l'encapsulation UDP des clients activent net.ipv4.ip_early_demux = 1 et net.ipv4.udp_early_demux = 1. Les trames WireGuard correspondent instantanément aux descripteurs de sockets connectés, transmettant directement les données aux cœurs cryptographiques de RiceGuard sans passer par la table de routage.
  2. Interfaces de transit de sortie : Les espaces de noms réseau (network namespaces) acheminant le trafic décapsulé des utilisateurs fonctionnent avec net.ipv4.ip_early_demux = 0. Cette disposition permet aux moteurs de transfert de réaliser des consultations FIB trie directes sans subir de tests de hachage superflus.
  3. Protection des plans de contrôle et DNS : Les points de terminaison d'administration TCP et les résolveurs DNS sécurisés opèrent dans des espaces isolés avec tcp_early_demux = 1, garantissant un traitement rapide des requêtes et une protection complète contre les fuites DNS.