Après avoir voulu me remémorer comment fonctionne une capture réseau du point de vue du noyau Linux, je me suis interrogé sur l'activation, en détail, du mode promiscuous.
Une carte réseau Ethernet fait remonter au système d'exploitation uniquement les trames Ethernet qui lui sont destinées. Le tri est opéré sur l'adresse MAC de destination. Sont conservés les paquets dont l'adresse MAC est celle de la carte réseau, l'adresse broadcast (FF:FF:FF:FF:FF:FF), ou l'une des adresses multicast (01:00:5E:XX:XX:XX pour IPv4, 33:33:XX:XX:XX:XX pour IPv6).
Ce filtrage est effectué matériellement par la carte réseau.
Le mode promiscuous permet de retirer ce filtrage, donc d'écouter tout ce qui passe sur le réseau.
Bon, il faut relativiser :
Le mode promiscuous est global à une interface réseau : exécuter un tcpdump -p
(on demande à ne pas activer le mode promiscuous) à côté d'un tcpdump
(mode promiscuous activé) est vain : quel que soit l'ordre de lancement des commandes, le tcpdump -p
verra les paquets qui ne sont pas destinés à la machine tant que le tcpdump
(promiscuous) est en cours d'exécution.
Voir si l'une des interfaces réseau du système est en mode promiscuous : ip -d l sh | grep -B 1 promiscuity
. Si le nombre affiché est supérieur à 1, alors plusieurs applications (+ le noyau voir ligne suivante) ont activé le mode promiscuous.
Activer le mode promiscuous sans passer par libpcap : sudo ip l set promisc on dev <INTERFACE>
. Pour le désactiver : sudo ip l set promisc off dev <INTERFACE>
.
Le mode promiscuous ne permet pas de voir TOUT le trafic réseau adressé à la machine. Une carte réseau peut filtrer matériellement le trafic qu'elle juge invalide : toute trame 802.1Q quand aucun VLAN est configuré sur le système, trames réseaux trop courtes ou dont la somme de contrôle est incorrecte, etc.
Tous les paquets qui arrivent sur une interface configurée en mode promiscuous ne parviendront pas jusqu'aux logiciels "serveurs" en écoute, car le noyau fait le tri.
Le tri le plus simple à mettre en évidence est que, si le transfert de paquets IP n'est pas activé (/proc/sys/net/ipv4/ip_forward = 0), alors tous les paquets IP dont l'adresse IP de destination n'est pas l'une des IPs de la machine (multicast inclus) seront détruits. Ils n'arriveront même pas jusqu'à la chaîne FORWARD de Netfilter/iptables. Il doit exister d'autres filtrages.
Si l'on regarde le code, la libpcap (utilisée par tcpdump
, wireshark
, etc.) utilise la fonction système setsockopt()
pour positionner l'option. Il ne reste plus qu'à remonter le code de Linux.
setsockopt()
est une fonction implémentée pour chaque famille/protocole. Une socket libpcap est de type AF_PACKET / PF_PACKET, donc c'est la fonction packet_setsockopt()
située dans le fichier net/packet/af_packet.c qui est utilisée. Elle appelle packet_mc_add()
qui appelle packet_dev_mc()
, toujours dans af_packet.c.
dev_set_promiscuity()
dans net/core/dev.c est appelée et elle appelle __dev_set_promiscuity()
toujours dans net/core/dev.c. On notera que c'est cette fonction qui journalise « device XXXX entered promiscuous mode » dans kern.log.
__dev_set_promiscuity()
appelle dev_change_rx_flags()
(toujours dans net/core/dev.c) qui appelle ndo_change_rx_flags
. Le pilote peut implémenter une fonction (ou non) pour remplacer celle par défaut et l'enregistrer dans la structure netdev_ops de la structure net_device. Exemple : le pilote e1000e n'implémente pas cette fonction.__dev_set_promiscuity()
appelle dev_set_rx_mode()
(toujours dans net/core/dev.c) qui appelle ndo_set_rx_mode()
. Le pilote e1000e la remplace avec e1000e_set_rx_mode()
(« .ndo_set_rx_mode = e1000e_set_rx_mode, ») dont le commentaire indique « e1000e_set_rx_mode - secondary unicast, Multicast and Promiscuous mode ». Cette fonction met à jour les registres de la carte réseau et, surtout, restaure+écrit les adresses multicast et unicast quand on sort du mode promiscuous.