Exercices sur les protocoles

Traiter l’exercice 1 en priorité.

Exercice 1: Une première approche de la couche réseau avec le protocole IP

L’un des rôles du protocole IP est d’attibué à chaque machine présente sur un réseau un identifiant unique dans ce réseau indépendamment de la configuration matérielle. cet identifiant est appelé adresse IP.

La version 4 de ce protocle est notée IPv4. Dans cette version, une adresse est une suite de 4 octets (\(4 \times 8 \)) bits) séparés par un point ..

Une autre version plus récente du protocole existe: l'IPv6 (adresse sur une suite de 16 octets).

Exemple: \(\texttt{11001001.00001001.01110110.00010011}\)

a) Combien d’adresses différentes peut-on créer avec chacun des protocoles précédents IPv4 et IPv6 ?

Compléments ?

Dans la suite de cet exercice, nous travaillerons sur des adresses fournies par le protocole IPv4.

b) Usuellement, l’adresse IP est donnée en base décimale. Vérifier que l’adresse donnée précédemment s’écrit \(\texttt{192.168.0.1}\).
c) Écrire une fontion Python decomposer() de paramètre ip (une adresse IP en base décimale) et qui retourne cette adresse découpée sous forme d’une liste d’entiers, puis une autre fonction Python composer() de paramètre liste (une liste de 4 entiers compris entre 0 et 255) et qui retourne une chaîne de caractères.

>>> decomposer('201.9.118.19')
[201, 9, 118, 19]
>>> composer([201, 9, 118, 19])
'201.9.118.19'

Aide: vous pourrez faire appel , si vous le souhaitez, aux méthodes split() et join() associées aux chaînes de caractères vue notamment dans le chapitre précédent traitant sur les tables de données (4.3)

Exemple d’usage:

>>> "129,'Magikarp',20".split(',')
['129', "'Magikarp'", '20']
>>> "".join(['129,', "'Magikarp',", '20'])
"129,'Magikarp',20"

d) Écrire ensuite en Python un prédicat est_une_ip()qui permet de contrôler la validité d’une adresse IP donnée.

>>> est_une_ip('201.9.118.19')
True
>>> est_une_ip('201.9.118.256')
False

Une adresse IPv4 d’une machine contient deux parties: la première partie (un certain nombre de bits de poids forts) permet d’identifier le réseau auquel appartient cette machine et la seconde partie (les bits de poids faibles restants) permet de l’identifier dans ce réseau.

Afin de connaître de nombre de bits de poids forts nécessaires pour identifier le réseau, on complète l’adresse IP avec un entier (nécessairement entre 1 et 32), appelé masque de sous-réseau.

Voici comment procéder avec \(201.9.118.19\texttt{/}21\).

L’adresse IP est \(201.9.118.19\) soit \(\texttt{11001001.00001001.01110110.00010011}\) en binaire.

Le masque de sous-réseau donné est \(21\).

Par conséquent, l’adresse du sous-réseau est \(\underline{\texttt{11001001.00001001.01110}}\texttt{000.00000000}\) composé des 21 bits de poids forts de l’adresse précédente complétés par autant de \(\texttt{0}\) que nécessaire.

Finalement, l’adresse du sous-réseau est donc \(201.9.112.0\).

Remarque: l’adresse du sous réseau peut être obtenu en effectuant l’opération bits à bits \(\texttt{11001001.00001001.01110110.00010011 \text{ & } 11111111.11111111.11111000.00000000}\) ou ce qui donne en décimal \(201.9.118.19 \text{ & } 255.255.248.0\)

e) Dans un même sous-réseau, combien de machines au maximum peut-on connecter dans ce sous-réseau ? Donner le plus petit masque qui aurait permis de disposer d’au moins 1000 machines dans ce sous-réseau.
f) Vérifier que les machines d’adresse IP \(201.9.118.44\) et \(201.9.113.1\) sont dans le même sous-réseau.
g) Vérifier que la machine d’adresse IP \(201.9.120.10\) n’est pas dans ce sous-réseau.
h) Écrire une fonction masque() qui prend pour paramètre m le masque du sous-réseau et qui retourne le masque sous la forme d’une adresse IP.

>>> masque(21)
'255.255.248.0'

i) Écrire une fonction Python adresse_sousreseau() de paramètres ip et m qui sont respectivement une chaîne de caractères qui désigne l’IP d’une machine et un entier qui désigne le masque du sous-réseau.

>>> adresse_sousreseau('201.9.118.44', 21)
'255.255.248.0'

j) Enfin écrire un prédicat meme_sousreseau() de paramètres ip1 (une chaîne de caractères), ip2 (une chaîne de caractères) et m (un entier entre 1 et 32) qui retourne True si les machines d’adresses ip1 et ip2 sont sur le même sous-réseau de masque m.

>>> meme_sousreseau('201.9.118.44','201.9.113.1', 21)
True
>>> meme_sousreseau('201.9.118.44','201.9.120.10', 21)
False

Exercice 2: Capture et analyse d’une trame

Dans cet exercice, on cherchera dans un premier temps à créer un paquet IPv4/TCP, puis à chercher à vérifier le calcul de la somme de contrôle de l’entête IPv4.

Partie 1: Créer une trame:

a) Lancez Thonny et installez le package scapy.
b) Dans la console Python, exécutez ce qui suit:

>>> from scapy.all import *
>>> pkt = Ether()/IP(dst="frama.io")/TCP()/"La NSI, c'est cool !"
>>> pkt
???

Vous avez ainsi créé un paquet qui contient le message "La NSI, cest cool !" (couche application) traité par le protocole TCP (couche transport), puis par le protocole IP (couche Internet), enfin par le protocole Ethernet (couche physique/réseau privé) avant d’être envoyé au destinataire sur le réseau public.

>>> pkt.src #adresse physique MAC de la source, celle de votre machine
???
>>> pkt.dst #adresse physique MAC du destinataire, celle de votre box
???

Affichons le contenu de cette trame

>>> hexdump(pkt)
???

Tout est écrit en hexadécimal. Pour rappel, en hexadécimal, un octet est codé avec au maximum 2 digits (entre \(\texttt{0}\) et \(\texttt{f}\)). Par exemple, \(\texttt{a4}_{16} = 10 \times 16^1 + 4 \times 16^0 = 164\).

La partie gauche n’a d’utilité que de numéroter les lignes (pour rappel, \(\texttt{0010}_{16}=2\)), ne fait dont pas partie du présent paquet.

Dans l’entête, on retrouve d’abord l’adresse MAC de la source, puis celle du destinataire. La trame IPv4 commence par 4500.

Voici en détail à quoi correspond chaque partie de cette trame.

À l’arrivée de la trame chez le destinataire, les protocoles essayer de détecter d’éventuelles erreurs en vérfiant les sommes de contrôle.

Partie 2: Somme de contrôle de l’en-tête du paquet IPv4:

Celle-ci est notée chksum sur l’image précédente.

Pour calculer cette somme de contrôle (IPv4), il suffit de considérer les 10 groupes successifs de 2 octets dont le premier est 45 00 qui formera par la suite le nombre 4500 (en hexadécimal)'. On a ainsi 10 nombres. Parmi ces nombres, le 6ème n’est autre que la somme de contrôle IPv4 (ce sera toujours ainsi).

La somme de contrôle n’est en fait que la somme des 9 autres nombres réalisée de la façon suivante.

Pour exemple (voir image précédente), voici comme procéder

(i) 0x4500 + 0x003b + 0x0001 + ... + 0x4006 + 0xc0a8 + ... + 0xb74a = 0x2ae49.
(ii) Comme ce résultat s’écrit avec plus de 2 octets, on effectue : 0xae49 + 0x2 = 0xae4b.
(iii) Enfin, on cherche son complementaire à 1 sur 16 bits. ~0xae4b = 51b4.
(iv) Reste à comparer avec le contrôle d’entête présent dans le paquet.

c) Pour pouvoir travailler/manipuler la trame précédente, exécuter et observer le résultat

>>> p = hexstr(pkt).split("  ")[0]
>>> p
???

d) Proposer une fonction de paramètre paquet (du même type que pkt) qui retourne le paquet sous la forme d’une liste des 10 entiers.
e) Proposer pour finir un prédicat est_conforme() de paramètres paquet et chksum (un entier) et qui permet de vérifier que le paquet soit conforme. En pratique, ce prédicat est appliqué par le protocole IP au niveau du destinataire.