Traiter l’exercice 1 en priorité.
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 ?
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 Pythondecomposer()
de paramètreip
(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 Pythoncomposer()
de paramètreliste
(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 fonctionmasque()
qui prend pour paramètrem
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ètresip
etm
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ètresip1
(une chaîne de caractères),ip2
(une chaîne de caractères) etm
(un entier entre 1 et 32) qui retourneTrue
si les machines d’adressesip1
etip2
sont sur le même sous-réseau de masquem
.
>>> meme_sousreseau('201.9.118.44','201.9.113.1', 21)
True
>>> meme_sousreseau('201.9.118.44','201.9.120.10', 21)
False
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.
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.
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 quepkt
) qui retourne le paquet sous la forme d’une liste des 10 entiers.
e) Proposer pour finir un prédicatest_conforme()
de paramètrespaquet
etchksum
(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.