Annonce le début de la trame et permet la synchronisation. Il contient
8 octets dont la valeur est 10101010
(on alterne des 1 et des 0),
sauf pour le dernier octet dont les 2 derniers bits sont à 1.
Adresse physique de la carte Ethernet destinataire de la trame. On représente
une adresse Ethernet comme ses 6 octets en hexadécimal, par exemple :
08:00:07:5C:10:0A
. La destination peut être une adresse de
(multi-)diffusion. En particulier, l’adresse FF:FF:FF:FF:FF:FF
(diffusion ou broadcast) correspond à toutes les stations du réseau physique
Ethernet.
Adresse physique de la carte Ethernet émettrice de la trame.
Indique quel protocole est concerné par le message. La carte réalise un démultiplexage en fournissant les données au protocole concerné. Quelques types courants (en hexadécimal) définis par la RFC 1700 :
0x0600
: Xerox Network Systems0x0800
: IP (Internet Protocol)0x8100
: 802.1q (encapsulation VLAN)0x0806
: ARP (Address Resolution Protocol)0x8035
: RARP (Reverse ARP)0x8137
et 0x8138
: NovellLes données véhiculées par la trame. Sur la station destinataire de la trame, ces octets seront communiqués à l’entité (protocole) indiquée par le champ EtherType. Notons que la taille minimale des données est de 46 octets. Des octets à 0, dits de “bourrage”, sont utilisés pour compléter des données dont la taille est inférieure à 46 octets.
Champ de contrôle de la redondance cyclique. Permet de s’assurer que la trame a été correctement transmise et que les données peuvent donc être délivrées au protocole destinataire.
typedef struct ethernet {
uint8_t status; // status
uint8_t nextpage; // prochaine page
uint16_t length; // Longueur
uint8_t destnodeid[6]; // Adresse MAC du destinataire
uint8_t sourcenodeid[6]; // Adresse MAC de la source
uint16_t protocal; // Protocole
uint8_t packet[300]; // paquet
} ethernet;
Indique le numéro de version du protocole IP utilisé (généralement 4).
Indique la longueur de l'en-tête en nombre de mots de 32 bits.
Il est divisé en deux parties :
1000
= Telnet, 0100
= FTP data, 0010
= SNMP.Exprimé en octets. Il est utilisé pour distinguer le bourrage dans une trame lorsque la longueur dépasse la taille maximale.
Permet d'identifier un datagramme en cas de fragmentation. Il est recopié dans chaque segment.
Il contient trois bits : |0|DF|MF|
.
Utilisé pour la reconstruction des fragments lors de la traversée de certains supports. Indique la position relative, en multiples de 8 octets, du fragment dans la trame initiale. Le compteur est décrémenté à chaque seconde tant que tous les fragments n'ont pas été reçus.
Indique une durée de vie en secondes de la trame. Elle est détruite lorsque ce champ devient nul. Chaque traversée d'un nœud se traduit par une décrémentation de ce champ.
Indique les protocoles utilisés au niveau supérieur :
Un CRC recalculé par chaque routeur avant la retransmission. Il permet de détecter les incohérences et erreurs de transmission dans l'en-tête, mais ne prend pas les données en compte.
Indiquent les adresses IP source et destination du paquet.
De longueur variable, il peut être nul, avec bourrage pour obtenir un multiple de 32 bits.
typedef struct ip {
uint16_t head[9]; // (correspond à la trame Ethernet)
uint8_t verandihl; // Version et Header
uint8_t typeofserver; // Type de service
uint16_t totallength; // Longueur de la trame totale
uint16_t frameindex; // Identification
uint16_t segment; // Flags et Position du fragment
uint8_t ttl; // Durée de vie
uint8_t protocal; // Protocole
uint16_t crc; // Somme de contrôle d'entête
uint8_t sourceip[4]; // Adresse de la source
uint8_t destip[4]; // Adresse du destinataire
uint8_t packet[280]; // Paquet
} ip;
Cette structure représente un exemple de trame IP en C, avec des champs pour la version, le service, la longueur totale, l'identification, la durée de vie, le protocole, et les adresses IP source et destination.
Ce champ indique le type de réseau sur lequel la séquence ARP a été activée. Pour Ethernet,
le code est généralement (0001)h
.
Ce champ spécifie le nombre d'octets dans les adresses MAC des paquets ARP. En général,
la valeur est (0006)h
, indiquant une longueur de 6 octets pour une adresse MAC.
Ce champ indique la longueur des adresses de niveau 3 (adresse IP) en octets. Pour une adresse IP,
la valeur est généralement (0004)h
, soit 4 octets.
Le code d'opération précise le type de paquet ARP :
(0001)h
: ARP_Request (requête ARP)(0002)h
: ARP_Response (réponse ARP ou ARP_Reply)Ces champs contiennent l'adresse MAC et l'adresse protocolaire (IP) de l'émetteur du paquet. Pour un paquet ARP_Request, ces adresses correspondent à celles de l'initiateur de la requête. Pour un ARP_Reply, ces champs sont remplacés par les adresses de la station qui répond à la requête ARP.
Ces champs contiennent les adresses MAC et IP de la station cible. Dans un paquet ARP_Request, l'adresse MAC destination est inconnue et est donc définie à 0, tandis que l'adresse IP destination est bien renseignée (celle de la station recherchée). Dans un ARP_Reply, la station répondante copie les adresses source du paquet ARP_Request dans les champs destination de la réponse.
typedef struct arp {
uint16_t head[9]; // Correspond à la trame Ethernet
uint16_t harewaretype; // Type de réseau (Ethernet = 0x0001)
uint16_t protocaltype; // Protocole (IP = 0x0800)
uint8_t halength; // Longueur de l'adresse physique (MAC = 6 octets)
uint8_t palength; // Longueur de l'adresse logique (IP = 4 octets)
uint16_t operation; // Type d'opération (1: requête ARP, 2: réponse ARP)
uint8_t sourcenodeid[6]; // Adresse MAC source
uint8_t sourceip[4]; // Adresse IP source
uint8_t destnodeid[6]; // Adresse MAC destination (0 si inconnue)
uint8_t destip[4]; // Adresse IP destination
} arp;
Cette structure représente un paquet ARP en langage C, avec des champs pour le type de réseau, les adresses MAC et IP source et destination, ainsi que le type d'opération (requête ou réponse ARP).
Ce champ indique le port depuis lequel le paquet a été envoyé. Il correspond à une valeur sur 16 bits qui identifie le port source de l'application émettrice.
Le champ de port de destination spécifie à quel port le paquet doit être envoyé. Ce port est également représenté sur 16 bits, et il identifie l'application cible sur la machine réceptrice.
Le champ Longueur indique la longueur totale du segment UDP, exprimée en octets, y compris l'en-tête et les données. La longueur minimale d'un segment UDP est de 8 octets, correspondant à la taille de l'en-tête UDP sans données.
Le champ Somme de contrôle permet de garantir l'intégrité des données transmises. Si ce champ est non nul, il est utilisé pour vérifier que le paquet a été correctement transmis et n'a pas subi d'altérations. Le checksum est calculé sur l'ensemble de l'en-tête UDP et des données, ainsi que sur un pseudo en-tête.
Le checksum est calculé en suivant ces étapes :
Le checksum est calculé en tenant compte d'un pseudo en-tête de 96 bits, qui inclut :
L'ajout de ce pseudo en-tête permet à l'UDP de se protéger contre les erreurs de routage, en incluant les informations essentielles de l'en-tête IP dans le calcul du checksum.
Note : MBZ (Must Be Zero) correspond à un octet réservé qui doit être entièrement composé de zéros.
La longueur spécifiée dans ce pseudo-en-tête est la longueur totale de l'en-tête UDP réel. Les 12 octets supplémentaires du pseudo-en-tête ne sont pas pris en compte dans ce calcul.
La pseudo-en-tête UDP est une structure temporaire utilisée uniquement pour le calcul du checksum. Bien qu'elle ne soit jamais transmise sur le réseau, elle joue un rôle crucial dans la vérification de l'intégrité du paquet UDP.
En résumé, la pseudo-en-tête UDP n'est ni avant ni après l'en-tête UDP dans le paquet transmis. Elle est simplement une aide temporaire pour calculer le checksum et garantir l'intégrité du paquet.
Remarque : En cherchant sur Internet, j'ai constaté que l'explication du calcul du checksum UDP n'était pas toujours très claire. Voici donc une interprétation corrigée, avec quelques précisions supplémentaires.
L'interprétation de la RFC, telle que mentionnée ci-dessus, incite à complémenter à 1 avant d'additionner les valeurs. Cependant, d'après les fonctions que j'ai trouvées, on additionne simplement tous les mots de 16 bits, puis le résultat final est complémenté à 1.
Il existe aussi une astuce lorsque la somme dépasse la taille de 16 bits : si c'est le cas, il faut ajouter la retenue à la somme. Si une nouvelle retenue est générée, il faut également l'ajouter. Voici un exemple pour clarifier ce processus :
Valeur hexadécimale |
---|
00 30 D3 20 4A 5D D8 D3 85 93 15 18 08 00 45 00 00 34 58 9E 40 00 80 06 00 00 C0 A8 01 65 C0 A8 |
01 6B C4 6C 00 50 2A B9 35 95 00 00 00 00 80 02 20 00 84 47 00 00 02 04 05 B4 01 03 03 08 01 01 |
04 02 |
On fait donc la somme de 0030 + D320 + 4A5D + D8D3 + 8593 + ...
, ce qui donne >6B29F
.
Comme on travaille sur 16 bits mais que la retenue est conservée, on fait B29F + 6
,
ce qui donne
B2A5
.
Si cette somme génère à nouveau une retenue, il faut continuer jusqu'à ce que le résultat tienne sur 16 bits.
Ensuite, on effectue le complément à 1 de >B2A5
, ce qui donne
>4D5A
, qui correspond
bien au checksum attendu.
unsigned int chksum(unsigned char *check, unsigned int size)
{
uint16_t i;
uint16_t val;
uint32_t sum = 0;
uint16_t *ptr = (uint16_t *)check;
for (i = 0; i < (size) / 2; i++) {
val = *ptr;
sum += val;
ptr++;
}
if (size & 0x01) {
val = *ptr & 0xff;
sum += val;
}
sum = (sum & 0xffff) + ((sum >> 16) & 0xffff);
if (sum & 0xffff0000) {
sum++;
}
return ((uint16_t)(sum & 0xffff));
}
Remarque :
Dans les exemples de requêtes DHCP, je ne vois pas apparaître la partie
MBZ
, Type
, et
Longueur UDP
> dans le
pseudo-en-tête UDP. Par conséquent, je tiendrai compte d'un pseudo-en-tête
tronqué, c'est-à-dire seulement l'adresse IP source et l'adresse IP de destination !
Voici les différents champs d'un message DHCP et leur signification :
Le passage de paramètres comme le nom de la machine se fait via des options, documentées dans la RFC 2132. Chaque option est identifiée par un numéro. Par exemple :
Il est possible d'envoyer plusieurs options dans un même message DHCP. Dans tous les cas, la zone des options doit se terminer par l'option 255 (fin de message).
Le format des options est simple et permet d'ajouter diverses informations importantes pour le client DHCP.
Dans le protocole DHCP, les options sont essentielles pour transmettre des informations spécifiques au client ou au serveur. Voici une explication détaillée de leur fonctionnement :
Le numéro de chaque option est codé sur 1 octet, ce qui limite le nombre total d'options possibles à 256. Le deuxième octet spécifie la longueur du champ de données associé, sans inclure les deux octets utilisés pour coder le numéro et la longueur de l'option.
Certaines options, comme l'option 255 (fin de message), ne comportent pas de données supplémentaires. Dans ce cas, ni champ de longueur ni champ de données ne sont présents.
Les messages DHCP tels que DHCPACK ou DHCPDISCOVER sont eux-mêmes des options. Par exemple, l'option 53 contient un champ de données de longueur 1, indiquant le type de requête (1 pour DHCPDISCOVER, etc.).
Les quatre premiers octets du champ d'options doivent être initialisés avec les valeurs 99, 130, 83, 99 (en décimal), formant ce qu'on appelle le "magic cookie".
Un client DHCP peut spécifier une taille maximale pour le champ d'options à l'aide de l'option 57.
Si le serveur ne peut pas inclure toutes les options dans la réponse à cause de cette limite, il peut
utiliser les champs sname
et file
pour envoyer le reste des options.
Le client est averti par l'option 52 dans la zone d'options.
Code Len Valeur (hex) Description
------------------------------------------------------------------
0x35 0x01 0x01 DHCP Message Type 01 = Discover
0x3d 0x07 0x01, Adresse MAC Client Identifier
0x0C 0x0f 0x7a, 0x66, ... Hostname (zfphfr 100_ans..)
0x37 0x0f 0x01, 0x03, ... Parameter Request List
Voici la structure de données utilisée pour gérer les messages DHCP dans mon application :
typedef struct dhcp {
uint16_t head[9]; // Trame Ethernet
uint16_t iphead[10]; // Trame IP
uint8_t op; // Type de message DHCP
uint8_t htype; // Type d'adresse hardware
uint8_t hlen; // Longueur de l'adresse hardware
uint8_t hops; // Relais DHCP
uint32_t xid; // Identifiant aléatoire
uint16_t secs; // Temps écoulé
uint16_t flags; // Divers flags
uint32_t ciaddr; // Adresse IP actuelle du client
uint32_t yiaddr; // Adresse IP à attribuer au client
uint32_t siaddr; // Adresse IP du serveur
uint32_t giaddr; // Adresse IP du relais
uint8_t chaddr[16]; // Adresse hardware du client
uint8_t sname[64]; // Nom du serveur (optionnel)
uint8_t file[128]; // Nom du fichier pour le boot
uint8_t options[64]; // Champ d'options (RFC 2132)
} dhcp;
Concernant les longueurs :
TCP (Transmission Control Protocol), ou en français : Protocole de Contrôle de
Transmission, est l'un des principaux protocoles de la couche transport dans le modèle TCP/IP.
Ce protocole gère les données au niveau des applications et les prépare pour la couche inférieure,
c'est-à-dire le protocole IP.
Lorsqu'il reçoit des données à transmettre, le protocole IP les encapsule dans des datagrammes IP,
en fixant le champ protocole à la valeur 6,
indiquant que le protocole au-dessus est TCP.
TCP est un protocole orienté connexion, ce qui signifie qu'il permet à deux machines en communication de suivre l'état de la transmission et d'assurer la fiabilité des échanges. Voici les principales caractéristiques de TCP :
Voici une description des principaux champs utilisés dans une trame TCP :
Somme de contrôle (Checksum ou CRC) : La somme de contrôle vérifie l'intégrité de l'en-tête TCP et inclut une pseudo-en-tête de 96 bits avec les adresses IP source et destination, le type de protocole et la longueur du message TCP.
Note : Lors du calcul du checksum, on ajoute la pseudo-en-tête de 12 octets (adresses IP source et destination, type de protocole et longueur du message TCP). Le checksum couvre ainsi l'en-tête TCP et les données, à l'exclusion des 12 octets de la pseudo-en-tête.
Pour établir une connexion entre deux machines, un processus de communication est utilisé, basé sur l'émission de données et un accusé de réception. Ce processus repose sur un numéro d'ordre, appelé généralement numéro de séquence, que les deux machines doivent synchroniser pour permettre une communication fiable. Voici les étapes principales :
Pour synchroniser leurs séquences, les deux machines utilisent une procédure appelée three-way handshake (poignée de main en trois temps). Ce processus en trois étapes est également utilisé lors de la clôture de la session.
Note : Une fois cette séquence de trois échanges terminée, les deux machines sont synchronisées et prêtes à communiquer efficacement.
Bien que deux systèmes puissent établir une connexion simultanément, dans la majorité des cas, l'un des systèmes ouvre une socket (point d'accès à une connexion TCP) et attend de manière passive les demandes de connexion de l'autre système. Ce processus est appelé ouverture passive, et il est typiquement utilisé du côté serveur de la connexion.
De l'autre côté, le client effectue une ouverture active, qui se déroule en trois étapes :
Lors de cet échange initial, les numéros de séquence des deux systèmes sont synchronisés comme suit :
x
) dans le champ Numéro d'ordre, et il active le drapeau SYN dans le champ FLAGS.y
), il active les drapeaux SYN/ACK dans le champ FLAGS et place le numéro d'ordre du client augmenté de 1 (x+1
) dans le champ Numéro d'accusé de réception.x+1
et un Numéro d'accusé de réception correspondant au numéro de séquence du serveur augmenté de 1 (y+1
).Note : Cet échange permet de synchroniser les numéros de séquence des deux parties, garantissant une communication ordonnée et fiable entre le client et le serveur.
00 | 30 | d3 | 20 | 4a | 5d | d8 | d3 | 85 | 93 | 15 | 18 | 08 | 00 | 45 | 00 |
00 | 34 | 58 | 9e | 40 | 00 | 80 | 06 | 00 | 00 | c0 | a8 | 01 | 65 | c0 | a8 |
01 | 6b | c4 | 6c | 00 | 50 | 2a | b9 | 35 | 95 | 00 | 00 | 00 | 00 | 80 | 02 |
20 | 00 | 84 | 47 | 00 | 00 | 02 | 04 | 05 | b4 | 01 | 03 | 03 | 08 | 01 | 01 |
04 | 02 |
Note : On remarque que le numéro d'accusé de réception est à zéro et que le drapeau SYN est activé (8002).
00 | 30 | d3 | 20 | 4a | 5d | d8 | d3 | 85 | 93 | 15 | 18 | 08 | 00 | 45 | 00 |
00 | 34 | 58 | 9e | 40 | 00 | 80 | 06 | 00 | 00 | c0 | a8 | 01 | 65 | c0 | a8 |
01 | 65 | 00 | 50 | c4 | 6c | 00 | 60 | 0d | 00 | 2a | b9 | 35 | 96 | 80 | 12 |
83 | 2c | 35 | 4f | 00 | 00 | 02 | 04 | 05 | b4 | 01 | 03 | 03 | 00 | 01 | 01 |
04 | 02 |
Note : La réponse du serveur implique naturellement un échange entre les deux ports. Le numéro de séquence du client devient le numéro d'accusé de réception du serveur, auquel on ajoute 1. Le serveur attribue ensuite son propre numéro de séquence. Il est important de noter que les indicateurs SYN et ACK sont actifs dans cette phase.
00 | 30 | d3 | 20 | 4a | 5d | d8 | d3 | 85 | 93 | 15 | 18 | 08 | 00 | 45 | 00 |
00 | 34 | 58 | 9e | 40 | 00 | 80 | 06 | 00 | 00 | c0 | a8 | 01 | 65 | c0 | a8 |
01 | 6b | c4 | 6c | 00 | 50 | 2a | b9 | 35 | 96 | 00 | 60 | 0d | 01 | 50 | 10 |
01 | 00 | 84 | 3b | 00 | 00 |
Note : Comme dans le premier cas, les deux ports sont à nouveau inversés. Le numéro d'accusé de réception du serveur devient le numéro de séquence du client, et le numéro d'accusé de réception du client devient le numéro de séquence du serveur + 1. Concernant les indicateurs, seul le flag ACK reste actif.
La liaison entre le client et le serveur est alors établie (socket). Les quatre premiers bits du champ "Décalage + Flags" permettent d'indiquer si le champ des options est utilisé. Dans le dernier exemple, la valeur 5 indique qu'il n'est pas utilisé. Une valeur supérieure à 5 indique la présence d'options. Dans l'exemple précédent, le nombre 8 est placé dans le champ Décalage (Offset), ce qui implique la présence de 12 octets d'options.
Le flux TCP est contrôlé de part et d'autre pour les octets compris dans une zone bien délimitée et nommée "fenêtre". La taille de celle-ci est définie par un entier non signé de 16 bits, limitant théoriquement sa taille à 65 535 octets (ce n'est pas tout à fait exact, voir plus loin l'option wscale). Chaque partie annonce ainsi la taille de son buffer de réception. Par construction, l'émetteur n'envoie pas plus de données que le récepteur ne peut en accepter.
Le CHECKSUM est un calcul qui porte sur la totalité du segment, en-tête et données.
Après l’adresse d’urgence, il peut y avoir une série d’options :
02 04 05 b4 01 03 03 00 01 01 04 02
Dans l'exemple ci-dessus, il y a plusieurs options :
La liste complète des options se trouve dans la RFC xxxx.