Je ne me souvenais plus : comment fonctionne une capture réseau avec la libpcap (tcpdump
, wireshark
, etc.) dans le noyau Linux ? À quel endroit du noyau le trafic est-il capturé par la libpcap ?
Explication concise dans les pages 10-12 de ce document que l'on peut recouper avec le code Linux de la libpcap.
En gros :
-
Création d'une socket de type RAW (entête Ethernet conservé), famille d'adresses AF/PF_PACKET (contournement de la partie TCP/IP de la pile réseau), protocole ETH_P_ALL (tous les protocoles). Dans le noyau (Linux), le protocole PF_PACKET, en tant que protocole générique, est traité très tôt par la pile réseau, après que le pilote de la carte réseau ait copié le paquet en RAM, et bien avant la couche IP (schéma) et Netfilter (le pare-feu de Linux). On voit la socket dans /proc/<PID_tcpdump>/fd
;
- Détails : la carte réseau émet une IRQ matérielle. Son pilote la traite en créant un espace mémoire de type sk_buff (réprésentation noyau d'un paquet réseau), en copiant le paquet depuis le tampon de la carte réseau vers l'espace mémoire, et en appelant netif_rx(), la fonction noyau générique de réception d'un paquet réseau. Cette dernière gère la congestion (celle liée aux IRQ, pas la congestion TCP), met le traitement du paquet dans la file d'attente d'un CPU, et génère une IRQ logicielle (soft IRQ). La fonction net_rx_action() traite les IRQ logicielles liées au réseau. Entre autres, elle appelle la fonction qui permet de traiter le protocole niveau 3 (IP, IPX, etc.). ("Récemment", c'est aussi elle qui peut se retrouver à déclencher la fonction poll() de récupération d'un paquet dans le tampon d'une carte réseau.) En tant que protocole générique, PF_PACKET est traité ici et prioritairement par la fonction packet_rcv()… qui duplique le paquet (si un filtre ne le supprime pas avant) afin qu'il soit aussi traité par le "vrai" gestionnaire du protocole réseau de niveau 3 (majoritairement IP donc ip_rcv() ‒ dont la fin d'exécution déclenche le prerouting de Netfilter ‒). Source 1. Source 2.
- Association de la socket à une interface réseau (bind()) ;
- Activation éventuelle du mode promiscuous.
tcpdump -p
permet de ne pas l'activer ;
- Application d'un éventuel filtre BPF/LSF (kernel-land) sur la socket.
tcpdump -d
affiche le bytecode BPF ;
- Activation d'un espace mémoire partagé kernel/user-land afin d'éviter des copies inutiles du paquet. Détails ;
- Lire les paquets reçus.