Sniffer Réseau

Ce que j’entends par ‘Sniffer Réseau’

Les switch (commutateurs) réseau utilisent un protocole pour s’annoncer aux autres switchs : protocole CDP chez Cisco et LLDP chez les autres (ouais, Cisco ne fait jamais comme les autres). Dans ces messages, on a (entre autres choses) le nom du switch qui l’a envoyé, et le numéro du port sur lequel il a été envoyé. L’idée du projet (qui n’est pas de moi !) est d’écouter ce message et de l’afficher sur un écran.

Quand tu commences à avoir dans les 200 ou 300 prises dans un bâtiment, il est souvent compliqué de savoir que la prise de l’ordinateur de Josiane de la compta est relié au Switch A sur sa prise 23… Normalement on a un inventaire de ces connexions, avec le numéro de la prise et le switch sur lequel il est relié. Dans la vie réelle, ce fichier n’est tenu à jour que pendant quelques mois, et après c’est l’anarchie. En branchant ce genre d’outil sur la prise sur laquelle Josiane de la compta branche son ordinateur, on saura tout de suite à quel switch et quel port elle se connecte…. Bref, c’est un outil pour les branleurs et les feignants.

Construction d’un prototype

Bien que plusieurs personnes aient déjà réalisé un système similaire, il faut adapter le code aux équipements qu’on utilise ici :

  • Un écran SSD1306, petit écran OLED monochrome d’une définition de 128 x 16 pixels, qui s’interface en I2C ;
  • Un module Ethernet ENC28J60, qui s’interface en SPI et qui supporte le protocole Ethernet 10 ;
  • Un Arduino Uno, qui sera remplacé au final par un Nano ;
  • Une batterie 18650 Li-Ion

Phase 1 - L’écran

J’utilise la lib SSD1306 écrite par Alexey Dynda, en version 1.8.3 (https://www.arduino.cc/reference/en/libraries/ssd1306/). Elle est plus légère que la librairie d’Adafruit.

Après avoir testé les différentes polices disponibles, je me suis arrêté sur la ssd1306xled_font8x16, qui m’offre un affichage de 2 lignes de 16 caractères. Pour mon usage, c’est parfait : la première ligne contiendra l’info sur le switch, la seconde sur le port.

L’utilisation est simple. L’initialisation se fait avec :

// initialisation de l'écran (128x32)
ssd1306_128x32_i2c_init();
// remplir en noir
ssd1306_fillScreen(0x00);
// Sélection de la police : 16 colonnes x 2 lignes
ssd1306_setFixedFont(ssd1306xled_font8x16);

Puis pour écrire :

// écrire sur la première ligne
ssd1306_printFixed(0, 0, "Première ligne", STYLE_NORMAL);
ssd1306_printFixed(0, 16, "Deuxième ligne", STYLE_NORMAL);

Je n’utilise pas la fonction “clear”, car elle provoque un scintillement pas très agréable. Le plus simple est d’afficher une chaine d’au moins 16 caractères de long. On complète avec des espaces si nécessaire pour enlever l’affichage précédent.

Phase 2 - La réception de trames ethernet

Tout d’abord, il ne faut pas utiliser la librairie Ethernet, fournie en standard par Arduino. Celle-ci ne permet pas de travailler simplement sur les trames Ethernet. On utilise la librairie EtherCard de Jean-Claude Wippler, en version 1.1.0 (https://reference.arduino.cc/reference/en/libraries/ethercard/).

Encore une fois, elle est très simple à utiliser. L’initialisation se fait avec :

// L'adresse MAC qu'on affecte (de façon arbitraire) à notre module ENC
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Le buffer dans lequel les trames seront stockées à la réception
byte Ethernet::buffer[501];
// On configure le buffer de réception et l'addresse MAC
// 10 étant le pin du Chip Select
ether.begin(sizeof Ethernet::buffer, mac, 10);
// On désactive le filtrage des trames afin de recevoir  
// également celles qui ne nous sont pas destinées
ENC28J60::enablePromiscuous();

La lecture des trames se fait ainsi :

int plen = ether.packetReceive();
// plen vaut 0 si aucune trame n'a été reçue.
// Si une trame est reçue, on peut la lire dans Ethernet::buffer

Une fois une trame reçue, on vérifie qu’elle est adressée à l’adresse MAC du protocole LLDP ou CDP

byte cdp_mac[] = {0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc};
byte lldp_mac[] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e};

Phase 3 - L’analyse et le décodage

Une trame LLDP contient une entête de 14 octets : 6 pour l’adresse MAC source, 6 pour l’adresse MAC de destination (toujours 01:80:C2:00:00:0E pour celle qui nous intéresse), et 2 octets pour définir le type de contenu (0x88CC dans notre cas).

La charge utile est sous une forme classique de triplets Type-Longueur-Valeur (TLV) : 7 bits pour le Type, 9 bits pour la Longueur, et la Valeur dont la longueur est variable et donnée par l’élément Longueur.

Concernant les trames CDP, c’est très similaire sauf que l’en-tête fait 26 octets, et que les triplets TLV sont plus long : 2 octets pour le Type, 2 octets pour la Longueur. La zone Longueur inclue également la longueur des zones Type et Longueur. Il faut donc soustraire 4 pour avoir la longueur de la zone Valeur.

Les différents types de données qui nous intéressent en LLDP sont les suivants :

  • 0x02 : l’identifiant de port. La valeur est écrite sous forme ASCII, après le premier octet qui donne le sous-type de l’identifiant (identifiant local, alias, adresse MAC…) ;
  • 0x04 : la description du port, écrite sous forme ASCII ;
  • 0x05 : le nom du switch, écrit sous forme ASCII ;
  • 0x07 : le descriptif du switch, écrit sous forme ASCII.

Pour CDP nous récupérons les types suivants :

  • 0x0001 : le nom du switch, écrit sous forme ASCII ;
  • 0x0003 : le nom du port, écrit sous forme ASCII ;
  • 0x0006 : le modèle du switch, écrit sous forme ASCII.

CDP

Contenu d’une trame CDP

Types CDP :

  • 0x0001 : le nom du switch, écrit sous forme ASCII ;
  • 0x0002 : la ou les adresses ;
  • 0x0003 : le nom du port, écrit sous forme ASCII ;
  • 0x0004 : les capacités du switch (switch, routeur….) ;
  • 0x0005 : la version de l’OS qui tourne ;
  • 0x0006 : le modèle du switch, écrit sous forme ASCII.

Plus qu’à assembler

Pour l’assemblage, et la mise en boitier, ce sera dans un autre article. À suivre…