IMA4 2016/2017 ECP1 : Différence entre versions

De Wiki d'activités IMA
(Rendu projet final)
(remarques)
 
(53 révisions intermédiaires par 2 utilisateurs non affichées)
Ligne 481 : Ligne 481 :
 
       valid_lft forever preferred_lft forever
 
       valid_lft forever preferred_lft forever
  
== Documents ==
+
=Rendu projet pour le 6 juillet=
[[Fichier:Serveur.txt]]
 
=Rendu projet final=
 
  
 
C'est ici que vous trouverez le rendu de mon projet en pièce jointe. Il y aura un dossier nommé "serveur" qui contiendra le dossier "serveur.c" et un fichier "réseau" qui contiendra tous les fichiers relatifs au simulateur (la majorité des fonctions se trouve dans reseau.js). Je vais de plus essayer de détailler les différentes fonctions que j'ai modifiées ou créées, et d'indiquer où elles se trouvent pour que vous puissiez vous retrouver dans les différentes pages de code. J'ai partagé mon travail en plusieurs parties sur lesquelles je travaillais en parallèle. Ces parties sont listées ci dessous. Il est important de préciser que j'ai fait mes tests qu'avec la page html "routeur_s3".
 
C'est ici que vous trouverez le rendu de mon projet en pièce jointe. Il y aura un dossier nommé "serveur" qui contiendra le dossier "serveur.c" et un fichier "réseau" qui contiendra tous les fichiers relatifs au simulateur (la majorité des fonctions se trouve dans reseau.js). Je vais de plus essayer de détailler les différentes fonctions que j'ai modifiées ou créées, et d'indiquer où elles se trouvent pour que vous puissiez vous retrouver dans les différentes pages de code. J'ai partagé mon travail en plusieurs parties sur lesquelles je travaillais en parallèle. Ces parties sont listées ci dessous. Il est important de préciser que j'ai fait mes tests qu'avec la page html "routeur_s3".
 
[[Fichier:Projet simulateur.zip]]
 
  
 
===Récapitulatif===
 
===Récapitulatif===
Ligne 546 : Ligne 542 :
 
===Le schéma===
 
===Le schéma===
  
Vous trouverez le schéma entre serveur et simulateur ainsi que celui de l'architecture interne du simulateur ici et dans le fichier total. J'ai apporté les modifications que vous m'aviez conseillé.
+
Vous trouverez le schéma entre serveur et simulateur ainsi que celui de l'architecture interne du simulateur dans le fichier total. J'ai apporté les modifications que vous m'aviez conseillé.
 
Il a été dur de créer un schéma précis et détaillé mais à la fois facile à lire. J'ai donc essayé de regrouper les fonctions dans des ensembles qui forment une action particulière du simulateur afin d’alléger un peu le dessin
 
Il a été dur de créer un schéma précis et détaillé mais à la fois facile à lire. J'ai donc essayé de regrouper les fonctions dans des ensembles qui forment une action particulière du simulateur afin d’alléger un peu le dessin
 +
 +
===Résultats et conclusion===
  
  
===Résultats et conclusion===
+
*enlever la demande des adresses Ethernet du formulaire
 +
J'ai réussi à enlever les demandes Ethernet du formulaire. Les demandes ARP se font proprement.
 +
 
 +
*implémenter la gestion des écho-requêtes et réponses d’écho (ICMP 8 et 0)
 +
J'ai réussi à faire circuler des paquets ICMP que j'avais créé au préalable grâce à la fonction '''serveur_create_paquet''' qui faisait la requête ARP elle même et utilisait des paquets ICMP reçus de mon interface.
 +
 
 +
*implémenter la création et gestion des paquets ICMP de type 5
 +
J'ai codé une première fonction qui m'a donné une piste pour continuer mais je n'ai pas eu le temps de la finir ou de la tester.
 +
 
 +
*créer un serveur Websocket
 +
Mon serveur semble bien fonctionner, il envoie et réceptionne les différents paquets.
  
*J'ai réussi à enlever les demandes Ethernet du formulaire. Les demandes ARP se font proprement.
+
*créer une interface virtuelle
*J'ai réussi à faire circuler des paquets ICMP que j'avais créé au préalable grâce à la fonction '''serveur_create_paquet''' qui utilisait des paquets ICMP reçus de mon interface.
+
J'ai créé une interface fonctionnant correctement. J'arrive à récupérer des trames que je ping dans le terminal ou des demandes ARP.
*le serveur et l'interface sont opérationnels
 
  
 
J'ai eu un problème qui m'a empêché de tester ma simulation avec des paquets ICMP venant de mon interface qui sont juste encapsulés en trame Ethernet sans avoir été modifiés avec la fonction '''serveur_create_paquet'''. En effet quand je ping, je récupère tout d’abord une trame ARP qui circule correctement jusque mon simulateur qui l'encapsule et la fait circuler jusqu’à l'adresse qui a été demandée dans le PING. Le paquet ARP de réponse est correctement envoyé jusqu'au serveur. Mais je n'ai pas réussi à envoyer ce paquet ARP dans l'interface pour que le système me renvoie le paquet ICMP. C'est dommage car je pense que si j'avais réussi à récupérer ce paquet ICMP il aurait été facile de le faire circuler comme je l'avais fait avec le paquet ARP.
 
J'ai eu un problème qui m'a empêché de tester ma simulation avec des paquets ICMP venant de mon interface qui sont juste encapsulés en trame Ethernet sans avoir été modifiés avec la fonction '''serveur_create_paquet'''. En effet quand je ping, je récupère tout d’abord une trame ARP qui circule correctement jusque mon simulateur qui l'encapsule et la fait circuler jusqu’à l'adresse qui a été demandée dans le PING. Le paquet ARP de réponse est correctement envoyé jusqu'au serveur. Mais je n'ai pas réussi à envoyer ce paquet ARP dans l'interface pour que le système me renvoie le paquet ICMP. C'est dommage car je pense que si j'avais réussi à récupérer ce paquet ICMP il aurait été facile de le faire circuler comme je l'avais fait avec le paquet ARP.
Ligne 562 : Ligne 569 :
 
Mon interface était réglée en TUN au départ. Cela me permettait de recevoir directement le paquet ICMP sans que le système se demande à qui l'envoyer. Pour recevoir les paquets ARP du système j'ai configuré l'interface en TAP.
 
Mon interface était réglée en TUN au départ. Cela me permettait de recevoir directement le paquet ICMP sans que le système se demande à qui l'envoyer. Pour recevoir les paquets ARP du système j'ai configuré l'interface en TAP.
  
L'autre problème est que lorsque je PING l'adresse IP d'une machine qui n'est pas dans le réseau de la station 1 (Celle qui est liée au serveur), le paquet ARP reçus du système ne comporte pas l'adresse IP du routeur (pour récupérer son adresse MAC) mais toujours l'adresse IP du la machine hors du réseau... je ne sais pas comment changer ça non plus.
+
*faire communiquer les différents modules
 +
Mes différents modules communiquent correctement entre eux. Je peux facilement modifier et changer les configurations des communications.
 +
 
 +
*fournir un schéma de l'architecture du projet
 +
 
 +
 
 +
Le second problème que j'ai rencontré est que lorsque je PING l'adresse IP d'une machine qui n'est pas dans le réseau de la station 1 (Celle qui est liée au serveur), le paquet ARP reçus du système ne comporte pas l'adresse IP du routeur (pour récupérer son adresse MAC) mais toujours l'adresse IP du la machine hors du réseau... je ne sais pas comment changer ça non plus.
  
 
J’espère que le travail fourni sera à la hauteur de vos attentes malgré le fait que je n'ai pas pu complètement l'achever. J'ai tenté de le répartir le mieux possible avec celui que je dois faire pour mon stage.
 
J’espère que le travail fourni sera à la hauteur de vos attentes malgré le fait que je n'ai pas pu complètement l'achever. J'ai tenté de le répartir le mieux possible avec celui que je dois faire pour mon stage.
  
[[Fichier:Projet simulateur.zip]]
+
=Compléments du projet suite au jury du 6 juillet =
 +
[[Fichier:Projet final.zip]]
 +
===Demandes du professeur===
 +
*reprise du serveur websocket et utilisation de la fonction callback pour l'envoi et la réception de données dans le navigateur.
 +
*suppression des doublons de code dans la fonction "reseau.js".
 +
*vérification de la pertinence de l'emplacement du code ajouté.
 +
*optimisation du code.
 +
 
 +
===Points revus===
 +
 
 +
J'ai tout d'abord repris le squelette du serveur WebSocket que mon professeur m'avait proposé où j'ai réimplémenté mon interface virtuelle qui n'a fait l'objet d'aucune remarque du professeur. J'ai veillé à bien utiliser la fonction callback pour l'envoi et la réception de données dans le navigateur.
 +
 
 +
J'ai ensuite repris la partie Websocket client que j'avais codé dans mon simulateur. Cela m'a permis de supprimer les doublons et de revoir l'emplacement des codes.
 +
 
 +
Maintenant, il n'existe plus qu'une seule fonction nommée '''received_paquet''' qui prend une trame en paramètre à envoyer au serveur et qui reçoit si nécessaire une trame envoyée par le serveur. Cette fonction est appelée en boucle toutes les secondes dans la fonction '''main''' de '''reseau.js'''. La trame envoyée est une variable globale qui est mise à jour dans la fonction '''router_enqueue_packet'''. La trame est envoyée au serveur uniquement si elle est différente de la précédente, pour éviter de renvoyer une trame déjà stockée dans le buffer précédemment.
 +
 
 +
J'ai revu la fonction '''create_ip4_packet_from_form''' qui permet à présent de gérer les demandes ARP et les lectures de tables de routage lors d'une saisie dans le formulaire. Je me suis basé sur la fonction '''router_route_ip4_packet''' que j'ai adaptée.
 +
 
 +
Pour terminer, j'ai relu l'ensemble des lignes de codes que j'avais implémentées afin de corriger les possibles erreurs.
 +
 
 +
===Points ajoutés===
 +
 
 +
J'ai mis à jour la lecture et l’écriture sur mon interface virtuelle. J'utilise toujours la fonction sprintf pour pouvoir transformer la trame reçue sous forme de String, pour qu'elle soit comprise par mon simulateur. J'ai enlevé plusieurs lignes de code que j'ai réussi à simplifier.
 +
Pour pouvoir envoyer les trames de réponse sur mon interface virtuelle, j'ai dû les modifier pour qu'elles soient compréhensibles par le système d'exploitation. Ne trouvant pas de fonction adaptée, j'ai créé une fonction moi-même s’appelant "conv", qui permet de placer les trames de type string dans un tableau de caractère avec un chiffre hexadécimal par case. Je peux donc renvoyer ce tableau de caractère au système d'exploitation qui comprend à présent les réponses ARP ou ICMP qu'il reçoit.
 +
 
 +
J'ai mis en place une liste chaînée avec un ajout en fin de liste pour les messages arrivant du système et une lecture en début de liste pour les messages envoyés vers le simulateur. Cela me sert de queue afin de stocker les messages s'ils arrivent plus vite que mon simulateur ne les traite.
 +
Pour cela vous trouverez deux fonctions supplémentaires dans '''serveur.c''':
 +
*addmessage
 +
*getmessage
 +
 
 +
Mon système me signalait une erreur de checksum. J'ai donc fait des recherches est constaté qu'il y avait de multiples façons de calculer un checksum et que visiblement mon système ne le calculait pas de la même façon que celui dans le simulateur.
 +
Pour ne pas supprimer le travail qui avait déjà était effectué par mon prédécesseur, j'ai écrit une fonction nouvelle '''ip4checksum2''' adaptée à mon système d'exploitation. Je l'utilise pour calculer les checksums IP et ICMP. Mon ordinateur comprend enfin les messages dans leur entièreté et donc les premiers résultats sont concluants. Je reçois des réponses correctes aux commandes PING.
 +
Toutefois, je dois préciser l'option -W 30 lors du ping afin que le système attende plus longtemps les réponses qui prennent un temps plus long à circuler dans le simulateur.
 +
 
 +
Avant de mettre en place la gestion des messages ICMP 5, j'ai du créer un nouveau scenario '''routeur_s4.html'''. En effet, dans le scénario s3 il n'y avait qu'un seul routeur. Or, pour pouvoir utiliser la redirection de route, il me fallait au moins deux routeurs sur le même réseau. Mon nouveau scénario reprend donc le scénario s3 en ajoutant un routeur relié au réseau 192.168.1.0/24 et au nouveau réseau 192.168.3.0/24 composé que d'une seule station"station 5". Comme vous pourrez le constater, ma station 1 ne connaît pas l’existence de ce nouveau routeur. Ici le but est d'ajouter à sa table de routage les chemins lorsque c'est nécessaire.
 +
 
 +
J'ai mis en place plusieurs fonctions afin de permettre un ajout à la table de routage. la fonction '''redirection''' ,déclenchée lorsque qu'un message arrive au niveau d'un routeur, permet de vérifier si le destinataire de ce message est dans le même réseau que l’émetteur. Si c'est le cas, le routeur continue de transmettre le message normalement et envoie de plus un message ICMP 5 de redirection à la station émettrice. Lorsque la station émettrice reçoit ce type de message, elle ajoute le nouveau routeur à sa table grâce à la fonction '''route_add_entry'''. Pour éviter que cette manipulation d'ajout se déclenche plusieurs fois, j'ai créé une fonction nommée '''route_check_entry''' qui vérifie, avant un ajout, si la table ne contient pas déjà cette route. Évidemment, le but du projet était aussi d'envoyer ce message au système qui ajouterait lui même à sa table, la route en question. Je fais donc circuler le message ICMP5 via mon serveur de façon à ce que cela se fasse. Après cela, lors de l'envoie d'un ping, je constate que le message ne passe plus par la gateway mais directement par le '''routeur 2'''.
 +
 
 +
 
 +
J'ai mis en place un fichier bash '''init.sh''', permettant au professeur d"exécuter au départ les configurations de l'interface virtuelle ainsi que celles du cache ARP (ajout d'adresse mac et d'adresse IP à l'interface ainsi qu'une route pour accéder au réseaux différents du reseau 192.168.1.0. cette configuration de route est la suivante : 192.168.0.0/16 via 192.168.1.1. cela me permet accéder à tous les réseaux compris entre 192.168.0 et 192.168.254 sur mon simulateur. Vous allez penser que j'aurai du définir mon routeur 1 comme passerelle par défaut or ce n'étais pas pratique. En effet ma station 1 qui représente mon ordinateur est en réalité un routeur à présent, car mon ordinateur est aussi relié au réseau local de ma maison ainsi qu'a internet via ma freebox. Ma passerelle par défaut est donc déjà ma freebox. En configurant le routeur 1 comme passerelle par défaut je recevais beaucoup de messages destinés à aller sur internet et non sur mon simulateur. J'aurai pu ajouter tout les réseaux de mon simulateur à ma table de routage. Mais en mettant ce masque (/16) cela permettra au simulateur d’être agrandi sans devoir changer la table de routage de votre station. j'ai laissé par soucis de clarté la table de routage de ma station 1 dans mon simulateur identique. Elle contient donc une route par défaut vers le routeur 1. Cela n'empeche pas le fonctionnement de mon projet même si nous somme d’accord que ma table de routage simulée n'est pas identique à celle réelle.
 +
 
 +
Cette commande doit être lancée une seule fois à l'allumage de l'ordinateur.
 +
 
 +
contenu du fichier init.sh:
 +
 
 +
#!/bin/bash
 +
#configuration de l'interface virtuelle
 +
sudo openvpn --mktun --dev tap0
 +
sudo ip link set tap0 up
 +
sudo ip addr add 192.168.1.100/24 dev tap0
 +
sudo ip link set dev tap0 address 00:11:11:11:11:01
 +
sudo ip route add 192.168.0.0/16 via 192.168.1.1
 +
#suppression du Multicast et de l'IPV6
 +
sudo ifconfig tap0 -multicast
 +
sudo sysctl -w net.ipv6.conf.tap0.disable_ipv6=1
 +
#configuration d'un parametre du cache ARP
 +
sudo ip ntable change name arp_cache retrans 40000 dev tap0
 +
 
 +
 
 +
J'ai créé un Makefile se trouvant dans le dossier '''serveur'''. Il permet de compiler le fichier '''serveur.c'''. Vous pouvez ensuite lancer le serveur via la commande '''./serveur'''.
 +
 
 +
===Tests===
 +
 
 +
J'ai décidé de faire deux vidéos de test. Les deux vidéos se trouvent dans le dossier "vidéo" situé dans le dossier "projet". La première montrera la réponse sur un ping envoyé à une station du même réseau. La deuxième montrera un ping envoyé sur une station d'un autre réseau. Dans cette dernière, nous pourrons visualiser un message de redirection de façon à optimiser la route des messages suivants.
 +
 
 +
Dans la première vidéo, on envoie un ping à l'adresse 192.168.1.200. On voit tout d’abord un message ARP passer qui remplit la cache ARP du systeme, qui envoie ensuite un message icmp, que l'on visualise sur le simulateur. La station en question répond à ce message. On voit ensuite, dans le terminal, que nous recevons bien un paquet ICMP de réponses.
 +
 
 +
[[Fichier:Video1 delatte.MP4]]
 +
 
 +
Dans la seconde vidéo, nous envoyons un ping à la station 192.168.3.1 qui se situe donc dans un réseau différent de ma station émettrice. Le système, qui ne connaît pas le chemin vers ce réseau, envoie son message ICMP à son routeur par défaut, que j'ai configuré comme étant le routeur 192.168.1.1. Le routeur par défaut, ayant dans sa table de routage l'information nécessaire, transmet le message ICMP au routeur 2 qui pourra enfin router ce message vers la station dans le réseau 192.162.1.3. Le message de réponse est renvoyé directement vers la station 1 et apparaît dans le terminal.
 +
 
 +
On voit en parallèle un message ICMP 5 envoyé depuis le routeur par défaut vers la station 1 qui met à jour sa table de routage instantanément. De plus, on constate dans le terminal la réception de ce message et donc l'ajout à la table de routage du système lui-même.
 +
 
 +
Lorsque le premier message de réponse est reçu par le terminal, j'envoie un nouveau ping à la même station. On constate cette fois que cette requête ne passe plus par le routeur par défaut mais directement par le routeur 2. Cela confirme l'ajout de la route optimale dans la table de routage du système.
 +
 
 +
[[Fichier:Video2 delatte.MP4]]
 +
 
 +
===consignes===
 +
 
 +
Je mets ici le protocole pour pouvoir utiliser mon projet.
 +
 
 +
Je me place dans le dossier projet/serveur.
 +
 
 +
J’exécute tout d'abord le fichier '''init.sh''' qui permet d'ouvrir et configurer l'interface '''tap0'''. Il permet de supprimer certains types de messages comme  l'ipv6 ou le multicast de l'interface. J'ai fait ce choix car mon serveur récupérait des fichiers qui ne gênaient pas le fonctionnement du simulateur mais m’empêchaient de voir les paquets qui m’intéressaient réellement.
 +
 
 +
Je compile mon serveur grâce à la commande '''make'''(vérifiez bien la version de libwebsocket nécessaire, j'utilise la version 2.x) et je lance le serveur avec '''./serveur'''. Le serveur se met en attente. Je peux à présent ouvrir un scénario html. Lors de l'ouverture, mon serveur indiquera '''connexion établie'''. Si le client n'arrive pas à se connecter, il affichera une alerte '''non connecté au serveur'''. Cela n'empêche pas pour autant d'envoyer des trames grâce au formulaire.
 +
 
 +
Je peux à présent taper la commande ping à destination d'une station du réseau. Il est important d'ajouter l'option -W 200 qui permet au système d'attendre les réponses plus longtemps que 4 secondes par défaut. Je vous invite à utiliser l'option -c qui permet de limiter le nombre de messages envoyés. En effet, une surcharge de messages sur le simulateur fait ralentir les animations des paquets.
 +
 
 +
==remarques==
 +
[[Fichier:Projet final.zip]]
 +
 
 +
[[Fichier:Serveur revu.zip]]
  
 
== Sources ==
 
== Sources ==

Version actuelle datée du 4 septembre 2017 à 18:59

Présentation du projet

Contexte

L'élève effectue son stage au Vietnam sans aucun matériel informatique personnel. Il lui est proposé une EC purement logicielle pouvant être développée sur n'importe quel système d'exploitation. Une machine virtuelle Linux est cependant nécessaire pour une partie du projet.

Objectif

L'objectif est de compléter un simulateur réseau écrit en JavaScript et tournant dans un navigateur. Ce simulateur se trouve à l'adresse http://rex.plil.fr/reseau.

Description du projet

Il est demandé d'ajouter la gestion des paquets ICMPv4 au simulateur. Cet ajout doit se faire proprement avec gestion des paquets ARP et mise en file d'attente des paquets IPv4 en attente de résolution d'adresses IPv4 en adresses Ethernet. Les paquets ICMPv4 à gérer sont les paquets de demande (ICMP de type 8) et de réponse d'écho (ICMP de type 0) ainsi que les paquet de redirection de route (ICMP de type 5). Les machines et les éléments du réseau pourront ainsi être testées par la commande ping mais aussi générer une erreur ICMP lorsqu'un paquet ne peut pas être routé pour diverses raisons.

En parallèle, pour tester la gestion des ICMPv4, il est demandé que le simulateur puisse gérer des éléments "externes". Le comportement de ces éléments ne sera pas pris en charge par le simulateur mais par un système d'exploitation externe. Le simulateur se contentera d'envoyer les paquets destinés aux éléments externes à une interface Ethernet virtuelle d'une machine Linux. A l'inverse, les paquets générés par l'interface virtuelle seront injectés dans le simulateur. La communication entre le simulateur et les interfaces virtuelles doit s'effectuer par des serveurs WebSocket. Vous utiliserez la bibliothèque WebSocket 2.x de Linux.

Cahier des charges

Un schéma d'ensemble précis du système est demandé. Ce schéma doit à la fois représenter l'architecture de communication entre le simulateur et les interface Ethernet virtuelles mais aussi l'architecture interne des modules du simulateur.

Modifier le programme de façon à enlever la demande d'adresse Ethernet. Les stations trouveront les adresses grâce au protocole ARP.

Planning prévisionnel

Travail effectué

Lundi 5 juin

J'ai tout d'abord commencé à me familiariser avec le logiciel de simulation et tenté de comprendre comment celui-ci fonctionnait. j'ai ensuite ouvert les pages HTML et JAVASCRIPT pour comprendre comment elles étaient organisées. Après avoir relus les notions de bases en java script, ainsi que mes cours de réseau j'ai tenté de comprendre pourquoi une des simulations fournies ne fonctionnait pas. En effet sur le schéma le plus complexe, deux stations ne sont pas capables de recevoir les paquets. Le problème venait d'une mauvaise configuration des masques. Les paquets ARP semblent fonctionner pourtant il m'est demandé de veiller à leur bon fonctionnement dans le cahier des charges.

mardi 6 juin

Après discussion avec Monsieur Redon, j'ai eu la confirmation qu'une coquille au niveau de l'adressage des stations était présente. Deux stations se trouvaient hors du réseau sur la table de routage du routeur. J'ai donc changé leurs adresses et cela refonctionne:

  • nouvelle adresse de la station 4: 192.168.1.200
  • nouvelle adresse de la station 3: 192.168.2.200

mercredi 7 juin

J'ai commencé à modifier le formulaire IP. L'adresse Ethernet source est maintenant déterminée automatiquement grâce à l'adresse IPV4. En effet grâce à l'adresse IPV4, on peut aller chercher, dans les div, directement l'adresse MAC associée. On retrouvera, dans la fonction de création de paquet, le code suivant qui permet de récupérer l'adresse MAC.

for(i=0;i<elements.length;i++){
   if (elements[i].find("[class=ip4port]").text() == ip4source_value){
   var ethsource_value = elements[i].find("[class=ethport]").text();
   var ethsource_color = elements[i].find("[class=ethport]").css('background-color');
   }
 }

Pour pouvoir trouver l'adresse MAC du destinataire, nous faisons appel au protocole ARP afin de simuler avec exactitude le fonctionnement d'un réseau. j'ai donc, pour éviter la redondance de code, relu les fonctions ARP utilisées pour le routeur. Je vais donc en utiliser plusieurs pour pouvoir mettre en place ce protocole au niveau des stations. Je pourrai, après avoir mis cela en place, m'attaquer à la construction du protocole de couche 3 de détection d'erreur: le protocole ICMP.

jeudi 8 juin

Je travaille aujourd’hui sur la recuperation de l’adresse Ethernet via protocole ARP. En utilisant des fonctions existantes, dont une que j'ai légèrement modifiée, j'arrive à faire circuler les paquets ARP dans le même réseau. Ainsi deux machines dans un même réseau peuvent communiquer avec seulement leur adresse IP comme adresse de départ. Cette partie de code permet de retrouver les éléments sources nécessaire à l'envoi d'un paquet IPV4. Toutefois nous pouvons constater qu'il nous manque l'adresse Ethernet destination. Je récupère dans cette partie le cache ARP en prévention pour la partie suivante.

//recherche de l'adresse ethernet,port,cache arp source grâce à l'adresse ip donnée
for(i=0;i<elements.length;i++){
   if (elements[i].find("[class=ip4port]").text() == ip4source_value){
    var ethsource_value = elements[i].find("[class=ethport]").text();
    var ethsource_color = elements[i].find("[class=ethport]").css('background-color');
    var port = elements[i].find("[class=port]");
    var arp=elements[i].children('.cachearp');

Dans la partie de code suivante je cherche dans le cache ARP l'adresse MAC, si l'adresse n'est pas dans le cache, je crée un paquet ARP à envoyer grâce à une fonction déjà existante que j'ai un peu adapté. J'ai donc crée "station_route_ip4" qui est fortement inspirée de "router_route_ipv4".

//requête ARP pour retrouver l'adresse Mac destinataire
var entry=cachearp_find_address(arp,ARPtarget_value);
if(entry===undefined){
  station_route_ip4_retry(form,port,ARPtarget_value);//fonction qui route un paquet arp}
else
{suite du code pour creation IPV4}

vendredi 9 juin

Le problème rencontré est le suivant: Si je possède l'adresse IP d'une machine d'un autre réseau, alors le routeur, ne reconnaissant pas son adresse IP lors de la demande ARP, ne renvoie pas son adresse MAC. Je suis donc en train de mettre au point une consultation de la table de routage de la station source. Ainsi la station émettrice enverra un paquet ARP avec l'adresse IP du routeur correspondant si elle constate que la machine réceptrice ne se trouve pas dans son réseau.

//on regarde si l'adresse IP est dans le réseau
var ip=ip4addr_to_array(ip4target_value);
   elements[i].find('.route').each(function(i,obj){
     var masque = ip4addr_to_array($(obj).children('.route_masque').text());
     var reseau = array_to_ip4addr(array_and(ip,masque));
     
     if(reseau == $(obj).children('.route_adresse').text() && reseau != '0.0.0.0')
       {ARPtarget_value=ip4target_value;bool=true;}
     if(bool==false)
       {ARPtarget_value='192.168.1.1';}
       

Dans le code ci-dessus, je cherche dans la table de routage si la station destinataire se trouve dans le réseau. Si elle se trouve dans un autre réseau,l'adresse destinataire du cache ARP sera celle de la bonne passerelle. Ces modifications ont toutes été faites dans la fonction create_ip4_packet_from_form(form).


Je peux maintenant commencer la partie ICMPV4. Je me suis donc renseigne sur les paquet ICMP que je devrai gérer:

  • Le paquet de type 8 est un echo request.
  • Le paquet de type 0 est un echo reply.

Le ping est en réalité une combinaison de ces deux paquet ICMP.

  • le paquet de type 5, ICMP redirect, indique qu'il y a un chemin plus court vers la destination.

lundi 12 juin

Avant de commencer à ajouter la gestion de paquets ICMP dans mon simulateur, j'ai d’abord décidé de créer le serveur, et de faire en sorte de gérer les paquets Ethernet entrant et sortant sur mon interface.

Le serveur Websocket, de base, fait avec la librairie Websocket linux est disponible dans la partie Document. Il porte le nom serveur.txt (ce n'est pas la version finale). Ce serveur contient les fonctions de bases pour pouvoir communiquer avec notre client simulateur. Je devrai le modifier légèrement pour qu'il puisse réceptionner des trames écrites dans le terminal afin de les transmettre directement au simulateur qui enverra le paquet de l'adresse source à l'adresse destination.

Maintenant que mon serveur tourne, je rajoute le code nécessaire dans le client, c'est à dire sur la page réseau.js. dans le main().

Toutefois je rencontre un problème: Quand je crée une connexion dans le main et que j'utilise cette connexion hors du main pour envoyer un message cela ne fonctionne pas. Je soupçonne que lors de la fin du main(), la connexion se ferme.

J'ai donc lancé cette fonction, non dans la fonction principale, mais dans la fonction "router_enqueue_packet(queue,packet,laddr,paddr)". En effet c'est cette fonction qui est exécutée à la fin du voyage d'un paquet pour le stocker en mémoire sur la station correspondante. A présent la station stocke dans sa mémoire et restitue le paquet, sous forme d'une trame d'octets, au serveur qui l'imprime dans le terminal. Il reste à l'envoyer par l'interface virtuelle.

mardi 13 juin

j'ai essayé de comprendre comment envoyer un paquet internet depuis le serveur. Mais je n'arrive toujours pas à initialiser une connexion depuis le serveur. De plus pour lire les entrées clavier, j’ai voulu utiliser la fonction read. Mais cette fonction est bloquante. Le serveur n'est donc plus à l’écoute des messages arrivant du simulateur.

mercredi 14 juin

J'ai réussi à concevoir mon interface Ethernet virtuelle. Comme vous pouvez le voir je n'ai pas encore rajouté l'option O_NONBLOCK car il me reste des soucis dont je vais parler plus bas.

int creationInterfaceVirtuelle(char *nom)
{
struct ifreq interface;
int fd,erreur; 
/* Ouverture du peripherique principal */
if((fd=open(TAP_PRINCIPAL,O_RDWR))<0)
   { 
       printf("error open");
       return fd;
   }
/* Preparation de la structure */
memset(&interface,0,sizeof(interface));
interface.ifr_flags=IFF_TUN|IFF_NO_PI;
if(nom!=NULL) strncpy(interface.ifr_name,nom,IFNAMSIZ);
/* Creation de l'interface */
if((erreur=ioctl(fd,TUNSETIFF,(void *)&interface))<0){ close(fd); return erreur; }
/* Recuperation du nom de l'interface */
strcpy(nom,interface.ifr_name);
/*retour du descripteur*/
return fd;
}


Il m'a fallu rajouter dans le main les infos importantes pour lancer mon interface

char *a_name = malloc(IFNAMSIZ);
   strcpy(a_name,"tun77");
   fd=creationInterfaceVirtuelle(a_name);

Maintenant que l'interface est fonctionnelle je peux utiliser un read() pour recevoir des trames émises par PING sur un autre terminal.

while (1)
   {
       //lws_service(context, 50);
       nread = read(fd,buffer,sizeof(buffer));
       if(nread < 0) {
           perror("Reading from interface");
           close(fd);
           return -1;
       }
       printf("Read %d bytes from device %s\n", nread, a_name);
       for(int i=0;i<84;i++){printf("%x ",buffer[i]);}
       printf(" \n");
   }

Comme vous pouvez le voir, lws_service(...) est en commentaire. Je rappelle que cette fonction est celle permettant à mon serveur de communiquer avec le simulateur. En effet le problème rencontré est que si je décommente cette ligne, je ne recevrai pas tous les messages du simulateur, car la fonction read est bloquante. Mais si je rajoute O_NONBLOCK j'ai une erreur à la lecture du read. Je n'ai pas encore trouvé de solution. Avant de pouvoir utiliser PING, j'ai du procéder à quelques commandes pour lier mon descripteur de fichier et une adresse IP. Les voici:


openvpn --mktun --dev tun77
ip link set tun77 up
ip addr add 10.0.0.1/24 dev tun77

Je dois faire ces commandes à chaque fois que j'allume le PC.

j'ai de plus, un dernier problème. Les trames reçues, qui doivent être des trames ICMP, que je demande d'afficher en hexadécimale ne sont pas celles attendues... En effet j'ai des chiffres complètement incohérents et je ne sais pas comment retrouver une trame, avec le protocole ICMP, cohérente.

Vendredi 16 juin

Après discussion avec mon professeur, Mr Redon, il s'est avéré que mes trames reçues étaient bonnes. Je suis donc en train de d'essayer de les envoyer vers mon simulateur. Toutefois je rencontre un problème: Il m'a été conseillé d'utiliser régulièrement la fonction callback_on_Writable pour pouvoir déclencher un événement de ce genre:

case LWS_CALLBACK_SERVER_WRITEABLE: {
           //envoie du paquet ICMP reçu par le PING
           unsigned char *buf = (unsigned char*) malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING);
           int i;
           .........................................
           ........................................
           }
           break;
       }

Mais je ne vois pas où appeler cette fonction. Elle nécessite une instance (wsi), je ne peux donc pas l'appeler dans le main... Une fois ce problème résolu je n'aurai plus qu'à traiter l'envoi de ces trames dans le simulateur et mon projet sera terminé.

lundi 19 juin

Je bloque sur le même sujet. Je récupère de l’interface virtuelle des octets que je stocke dans un tableau de caractère. Même si je sais que un octet et un char font la même taille, je ne comprends pas comment on peut les stocker alors que ce n’est pas le bon type. De plus je veux envoyer cette chaîne de caractère au simulateur mais cette chaîne contient des bytes qui ne sont pas lisible à part avec un printf(%x). j'ai cherché comment transformer les bytes en caractère mais je n’ai pas réussi. Recevant des chaînes de caractère du simulateur, je n’arrive pas non plus à les envoyer à l’interface car elles ne sont pas du bon type (error :EINVAL).

mardi 20 juin

En utilisant la fonction sprintf() j'ai réussi à récupérer les bytes de l'interface et à les stocker sous forme de tableau de caractère. voici le code ci-dessous


if(read(fd,buffer,84)>=0)
           {
           printf("Read bytes from device %s\n", a_name);   
           for(int i=0;i<84;i++)
               {
               sprintf(&trame[(LWS_SEND_BUFFER_PRE_PADDING)+i*3],"%x",buffer[i]);
               sprintf(&trame[(LWS_SEND_BUFFER_PRE_PADDING)+(i*3)+2]," ");
               }
           printf(" \n");
           for(int i=0;i<252;i++)
               {
               printf("%c",trame[i]);
               }
           printf(" \n");
       }

La deuxième boucle for ne sert juste qu'à afficher la trame pour vérifier le contenu.

LWS_SEND_BUFFER_PRE_PADDING est une partie qu'il faut que je laisse libre pour l'envoi par le socket. Etant donné que pour représenter un nombre hexadécimal il nous faut deux caractère et que je sépare chaque groupe d'un espace, je place donc la valeur du buffer puis j'avance de trois cases dans la chaîne. Le deuxième sprintf() ne sert juste qu'à mettre des espaces dans les bonnes cases de mon tableau.

J'avais un problème au niveau de ma chaîne récupérée car je réceptionnais des valeurs de ce type "ffffffxx". J'ai compris que j'avais un dépassement dans la mémoire car la valeur était négative. J'ai juste eu à préciser que le type de ma chaîne était "unsigned char".

Après ceci je procède à l'envoi de ma trame grâce à la partie du code suivante.

case LWS_CALLBACK_SERVER_WRITEABLE: {
           //envoi du paquet ICMP reçu par le PING
           lws_write(wsi,&trame[LWS_SEND_BUFFER_PRE_PADDING],252,LWS_WRITE_TEXT);
           printf("test ok \n");          
           break;
       }

J'envoie donc mon code en laissant bien de la place au début de ma trame pour que l'envoi réussisse. Pour l'instant le simulateur n'affiche qu'une alerte de réception de la trame. Il faut donc que je gère l'envoi de cette trame comme un paquet ICMP à travers le simulateur.

Communication du ping- simulation.png

Mercredi 21, jeudi 22juin

Étant donné que je passe ma soutenance de stage mercredi prochain, j'ai mis mon projet de coté pour pouvoir finir mon rapport et avancer le plus loin possible sur le travail demandé.

lundi 26, mardi 27 juin

Après avoir réussi à recevoir une trame venant de mon PING sur le simulateur, j'ai donc commencé à la traiter. Je crée donc une fonction ICMP_create_request(Nouveau nom: serveur_create_packet). Dans cette fonction, je récupère les deux strings des adresses IP source et IP destination que je transforme en forme décimale (exemple: 192.1.23.215).

function icmp_create_request(trame){
var ip4src="";
var ip4tgt="";
var bool=false, ARPtarget_value;
for(i=36;i<=47;i++){
 ip4src+=trame[i];
 ip4tgt+=trame[i+12]
}
var ip4srcaddr= array_to_ip4addr(bytes_to_array(ip4src));
var ip4tgtaddr= array_to_ip4addr(bytes_to_array(ip4tgt));

Comme avec le formulaire, je viens récupérer l'adresse Ethernet source, la couleur de l'adresse, le cache ARP et la couleur de l’adresse IP source aussi.

//recupere l'adresse ethernet/couleur/arp/port de la source
for(i=0;i<elements.length;i++){
   if (elements[i].find("[class=ip4port]").text() == ip4srcaddr){
   var ethsource_value = elements[i].find("[class=ethport]").text();
   var ethsource_color = elements[i].find("[class=ethport]").css('background-color');
   var ip4srccolor = elements[i].find("[class=ip4port]").css('background-color');
   var port = elements[i].find("[class=port]");
   var arp=elements[i].children('.cachearp');


Comme avec le formulaire, je regarde si l'adresse IP est dans le réseau, pour savoir si la trame ARP doit garder comme IP de destination l'IP de la station ou prendre l'IP du routeur.

   //on regarde si l'adresse IP est dans le réseau
   var ip=ip4addr_to_array(ip4tgtaddr);
   elements[i].find('.route').each(function(i,obj){
     var masque = ip4addr_to_array($(obj).children('.route_masque').text());
     var reseau = array_to_ip4addr(array_and(ip,masque));
     if(reseau == $(obj).children('.route_adresse').text() && reseau != '0.0.0.0')
       {ARPtarget_value=ip4tgtaddr;bool=true;}
     });
   if(bool==false){ARPtarget_value='192.168.1.1';}
   //adresse par Default que l'on peut aller chercher dans la table de routage
   }
   if (elements[i].find("[class=ip4port]").text() == ip4tgtaddr){
     var ip4tgtcolor = elements[i].find("[class=ip4port]").css('background-color');
   }
 }

On cherche dans le cache arp pour voir si nous disposons de l'adresse ethernet, sinon on envoie une demande arp.

 var entry=cachearp_find_address(arp,ARPtarget_value);
 if(entry===undefined){
   arp_create_request(port,ARPtarget_value);
   setTimeout(function(){ icmp_create_request(trame);},IP4_RETRY_DELAY);
  }

Si on possède l'adresse Ethernet on envoie notre trame ICMP encapsulée dans un paquet IPV4.

else
 {
 var ethtarget_value=entry['ethernet'];
 var ethtarget_color=find_address_color('ethport',ethtarget_value);
 packet=create_ip4_packet(ethsource_value,ethsource_color,ethtarget_value,ethtarget_color,
          ip4srcaddr,ip4srccolor,ip4tgtaddr,ip4tgtcolor,"64","1",trame,false);
 send_packet_from_source(packet);
 }
}

J'ai de plus commencé à traiter ces paquets. Lorsqu'ils arrivent en fin de course, je reconnais le paquet ICMP et j'envoie un paquet ICMP de réponse d’écho (type 0).

jeudi 29 juin

Aujourd'hui j'ai avancé sur les paquets ICMP dans le simulateur. j'ai pu mettre en place des nouvelles fonctions:

La fonction permettant de savoir si on a affaire à un paquet ICMP.

function packet_is_icmp(packet){
 var content=packet.children("input[name='contenu']").val();
 var bytes=bytes_to_hexa(content);
 alert(bytes[34]+bytes[35]);
 return (bytes[34]+bytes[35]=='0800');
}

La fonction qui permet de renvoyer un paquet ICMP transformé en réponse. Je travaille sur cette dernière car elle n'est pas encore au point.

function icmp_return_answer(packet){
 var temp=[];
 var content=packet.children("input[name='contenu']").val();
 var bytes=bytes_to_hexa(content);

 for(i=0;i<4;i++){
   temp[i]=bytes[i+26];
   bytes[i+26]=bytes[i+30];
   bytes[i+30]=temp[i];
 }
 for(i=0;i<6;i++){
   temp[i]=bytes[i];
   bytes[i]=bytes[i+6];
   bytes[i+6]=temp[i];
 }
 for(i=0;i<4;i++){
   temp[i]=bytes[i+46];
   bytes[i+46]=bytes[i+50];
   bytes[i+50]=temp[i];
 }
 bytes[54]="00";

 alert(bytes);

 content=hexa_to_bytes(bytes);
 packet.children("input[name='contenu']").val(content);

 //on renvoie la paquet
 send_packet_from_source(packet);
}

J'ai de plus rencontré plusieurs erreurs. Je ne supprimais pas l'entête IPV4 sur mes paquets ICMP entre le serveur et le simulateur, donc j'avais deux entêtes empilées. Je n'arrive toujours pas à envoyer une trame du serveur vers le simulateur, si je n'ai pas au préalable envoyé un message du simulateur au serveur.

L’idée principale reste de réussir à faire naviguer une question réponse, en aller retour, de mon interface jusque mon simulateur.

vendredi 30 juin

J'ai réussi à récupérer un paquet ICMP de type 0 (un paquet de réponse à un écho) sur mon serveur. Je le stocke dans un tableau. Il me reste donc à l'envoyer sur mon interface virtuelle. J'ai une erreur sur le write() qui me permet d'envoyer ce paquet ICMP à l'interface. En effet l'erreur est de type EINVAL. Je sais que ça doit être ma trame le problème. Je n'arrive cependant pas à comprendre pourquoi elle serait invalide. Je cherche encore. En parallèle j'essaye de comprendre comment fonctionne le lws_on_writable. Je l'appelle dans l'initialisation mais je dois donc attendre un premier message du client. Mon serveur ne commence donc à envoyer les PING reçus, au simulateur, seulement après réception d'un premier message.

Je bloque aussi sur un problème de fermeture de serveur coté client. En effet quand je tape la commande "ws.close()" qui ferme le connexion "ws" que j'ai lancé auparavant, mon programme plante.

ICMP0 qui arrive sur le serveur

samedi 1 juillet

Ne trouvant pas pourquoi ma trame ne passe pas dans la fonction write(), j'ai réglé les autres problèmes: Mon serveur arrive à communiquer avec le simulateur dès que nous chargeons la page du simulateur. Il gère, tout seul, les paquets ICMP entrants et sortants. La fonction qui m'a permis d'automatiser cela et le suivante. J’appelle cette fonction récursive dans le main de reseau.js. Elle récupère un message envoyé du serveur, le compare à celui qu'elle a reçu précédemment. Si c’est le même la fonction ne fais rien, si il est différent, elle crée une requête ICMP. Après la requête, elle se rappelle elle même.

function received_ICMP(){
//connexion au serveur Websocket
 var ws= new WebSocket('ws://localhost:9500','my');
 ws.onmessage = function(evt) {
 var received_msg = evt.data;
 var trame = bytes_to_hexa(received_msg);
   if(temp!=trame[5]){
     icmp_create_request(received_msg);
   }
 temp=trame[5];
 //alert(temp);
 received_ICMP();
 ws.close();
 };
 ws.onerror = function () {alert("non connecte au serveur");};
 ws.onclose= function (){//alert("message envoye et connection ferme");};  
 }

}

Le petit problème et que le nombre de ping en continue est trop nombreux pour le simulateur qui crash. j'envoie donc une dizaine de ping à chaque fois, pas plus. J'ai aussi réussi à fermé mes connexions grâce à ws.close(). Il s'est avéré que je tapais cette commande au mauvais endroit.

Lundi 3 juillet

J'ai change la fonction enqueue_packet de façon à ce qu'elle laisse passer tout les paquets vers le serveur. Le serveur reçoit donc les requêtes ICMP et ARP ainsi que leurs réponses. Il reçoit de plus les paquets de donné classiques envoyés depuis le formulaire. Je cherche à mettre en place le code ICMP 5 mais je travaille sur le simulateur n'ayant qu'un routeur. Je ne sais donc pas comment tester cette partie.

J'ai travaillé sur le message ICMP5. Le routeur remarque que la route qu'a choisie l'ordinateur émetteur n'est pas optimale car le prochain routeur à passer pour atteindre le destinataire se trouve sur le même réseau que celui de l'ordinateur émetteur. Le routeur envoie l'adresse du prochain routeur à ajouter dans la table de routage de l'ordinateur émetteur de façon que le prochain envoi vers le même destinataire ne passe pas inutilement par lui.

J'ai donc implémenté une fonction dans "router_route_ip4_packet". Cette fonction "redirection(ipdest,srcip4,self)" envoie un paquet icmp 5 si elle constate que l'adresse ip source et ip destination sont dans le mème réseau et que le message ne devait pas passer par lui.

function redirection(ipdest,ipsource,self,port){
var masque = ip4addr_to_array("255.255.255.0");
var reseau_src = array_and(ipsource,masque);
var reseau_tgt = array_and(ipdest,masque);
if(reseau_src == reseau_tgt){
 var ethsrcaddr=port.children('.ethport').html();
 var ethsrccolor=find_address_color('ethport',ethsrcaddr);
 var ethtgtaddr=//a recuperer;
 var ethtgtcolor=//a recuperer;
 var icmp= "05 00 ff ff "
 icmp+=ethaddr_to_hexa(ethsrcaddr)+' '+ip4addr_to_hexa(ip4srcaddr)+' ';
 icmp+="00 00 00 00 00 ";
 packet=create_eth_packet(ethsrcaddr,ethsrccolor,ethtgtaddr,ethtgtcolor,icmp,false);
 send_packet(packet,port);
}

}

Cette fonction n'est pas encore fini car je n'ai pas encore récupéré toutes les informations pour envoyer le paquet Ethernet à la source.

De plus je ne savais pas comment faire pour définir quelle station transférerai les messages envoyés par le serveur web. Dans le code "serveur_create_packet" j'ai arbitrairement entré l'adresse d'une des quatre stations. Je ne sais pas comment faire autrement.

J'ai corrigé la fonction received_ICMP qui s’appelle maintenant Received_paquet.

mardi 4 juillet

Apres discussion avec le professeur, j'ai compris comment recevoir des trames ARP directement de l'interface virtuelle. J'ai eu plusieurs problèmes car mon interface était de type TUN mais pour recevoir les paquets ARP il fallait qu'elle soit de type TAP. Maintenant je reçois bien des paquets ARP que je peux visualiser grâce a la commande tcpdump. Hors quand je les affiches j'ai ce type de trame:

ff ff ff ff ff ff 16 46 1f 64 d4 ae 88 d6 60 c1 b8 c0 16 34 10 f1 16 46 1f 64 d4 ae 
a0 01 11 41 10 f0 f0 60 d0 a0 a0 31 e1 32 00 00 80 00 00 00 00 00 f0 60 d0 fb 14 e9 
14 e9 70 35 ce 5f 70 60 70 40 60 62 60 60 60 60 60 c0 f5 5f 69 70 70 73 74 5f 74 63 

je n'arrive pas a retrouver les différentes adresses et valeurs composants la trame. J'essaye de voir comment régler ce contre temps.

En mettant mon buffer en type char et pas unsigned char j’obtiens cette trame, qui me parait plus logique:

//ethernet
ff ff ff ff ff ff broadcast
16 46 1f 64 ff ff adresse source
f8 a6 type de protol
//ARP
00 01 Ethernet (10Mb)
08 00 IP
06 ethernet
04 ipv4
00 01 request requete
16 46 1f 64 ff ff adresse mac source
a0 a1 01 01 adresse ip source
00 00 00 00 00 00 mac destination
a0 01 01 02 adresse ip destination
//bourrage
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

j'ai des problèmes sur ma traduction de ma trame de bytes vers string car on voie bien que mon type de protocole devrait être "08 06" et qu'il est de f8 a6 On voit aussi pour les adresse ip que je récupère "a0...." alors que je devrais récupérer "0a...." car je ping a l'adresse "10.1.1.2".

En redémarrant le serveur je me suis aperçu que l'erreur disparaissait toute seule... je ne sais pas a quoi elle était due.

J'ai un problème avec les paquets ARP réponses devant sortir du simulateur. Je n'arrive pas a les récupérer sur mon serveur. J'ai trouve la raison. Il s’avère que l'adresse Ethernet du système est différente de celle de ma machine simulée. donc la station simulée pense que le paquet n'est pas pour elle. De plus, laissant passer tout les paquets je reçois un multitude de paquets ARP qui font crasher mon simulateur.

Après discussion avec le professeur j'ai du modifier l'adresse Ethernet de système faisant le ping. Voici le résultat.

   link/ether 00:11:11:11:11:01 brd ff:ff:ff:ff:ff:ff
   inet 192.168.1.100/24 scope global tap0
      valid_lft forever preferred_lft forever

Rendu projet pour le 6 juillet

C'est ici que vous trouverez le rendu de mon projet en pièce jointe. Il y aura un dossier nommé "serveur" qui contiendra le dossier "serveur.c" et un fichier "réseau" qui contiendra tous les fichiers relatifs au simulateur (la majorité des fonctions se trouve dans reseau.js). Je vais de plus essayer de détailler les différentes fonctions que j'ai modifiées ou créées, et d'indiquer où elles se trouvent pour que vous puissiez vous retrouver dans les différentes pages de code. J'ai partagé mon travail en plusieurs parties sur lesquelles je travaillais en parallèle. Ces parties sont listées ci dessous. Il est important de préciser que j'ai fait mes tests qu'avec la page html "routeur_s3".

Récapitulatif

  • Enlever la demande des adresses Ethernet du formulaire
  • implémenter la gestion des écho-requêtes et réponses d’écho(ICMP 8 et 0)
  • implémenter la création et gestion des paquets ICMP de type 5
  • créer un serveur Websocket
  • créer une interface virtuelle
  • faire communiquer les différents modules
  • fournir un schéma de l'architecture du projet

Enlever la demande des adresses Ethernet du formulaire

J'ai enlevé les demandes d'adresses Ethernet dans la page formip4.html. Je récupère l'adresse destination grâce au protocole ARP et l'adresse source dans les div. Toutes les modifications faites pour récupérer les adresses se trouvent dans la fonction create_ip4_packet_from_form. Dans cette fonction, je cherche aussi si l'adresse IP destination est dans le réseau. Cela me permet de savoir à qui envoyer le paquet ARP (si l'adresse IP n'est pas dans le réseau j'envoie le paquet ARP au routeur). Vous aviez codé une fonction du même type pour chercher le réseau mais je n'ai pas réussi à la comprendre. J'ai donc codé la mienne. Elle ne doit pas être très optimisée.

Implémenter la gestion des écho-requêtes et réponses d’écho

A la réception des paquets j'ai tout d’abord créé une fonction serveur_create_paquet qui faisait le même travail de création de paquet que celle utilisée avec le formulaire. Sauf qu'elle puisait les informations nécessaires dans la trame ICMP reçue et non dans le formulaire.

Je n'utilise plus cette fonction car le professeur m'a conseillé de récupérer directement les trames du serveur, de les encapsuler et de les envoyer directement tel quel sur le simulateur. C’est donc le système lui même qui, lors de la commande PING, envoie les paquets ARP à transmettre dans le simulateur, etc. vous la trouverez quand même dans mon code en commentaire car elle fonctionnait et je ne voulais pas supprimer du travail accompli. La fonction maintenant utilisée est received_paquet() dont je parlerai dans la communication entre module. Les paquets envoyés sur le réseau devaient être analysés et permettre l'envoi d'une réponse. Vous trouverez donc, dans la fonction router_behaviour, la fonction packet_is_request_icmp qui détermine si un paquet reçu par une machine est bien un paquet ICMP request et qui le cas échéant renvoie vers la fonction icmp_return_answer. Cette fonction utilise le paquet ICMP d’écho et crée à partir de là, un paquet ICMP de réponse à l'attention de la machine ayant lancé la requête. J'ai de plus codé une petite fonction permettant de savoir si un paquet est de type ICMP réponse. Je ne m'en sers plus à présent mais j'ai laissé cette fonction qui pourrai servir. Son nom est packet_is_answer_icmp. Les paquets reçus par la machine reliée au serveur sont stockés et renvoyés au serveur.

Implémenter la création et gestion des paquets ICMP de type 5

Je n'ai pas pu tester cette partie par manque de temps. Elle est donc juste théorique. J'ai écrit une fonction qui lors du routage d'un paquet par un routeur, vérifie si la destination du paquet est bien hors du réseau. Si elle se trouve dans le réseau alors le paquet est mal routé, il ne devrait pas arriver au routeur ayant reçu le paquet mais à un autre routeur. Dans ce cas le routeur envoie un message à la source en distant qu'il y a un chemin plus optimal à ajouter à la table de routage. Vous trouverez donc cette fonction nommée redirection et qui est appelée dans router_route_ip4_retry. Elle ne fonctionne sûrement pas mais c’était l’idée de base que je voulais mettre en place.

Créer un serveur Websocket

Le serveur Websocket est dans le fichier serveur.c. Vous retrouverez les fonctions appelées par lws_service(context, 50) dans le main. Le serveur utilise une fonction d’initialisation, une de récupération de donnée du simulateur quand un message arrive, et une d'envoi de donnée qui est déclenchée quand la communication est libre grâce à la fonction lws_callback_on_writable(wsi) appelée dans la fonction d'initialisation.

Créer une interface virtuelle

L'interface virtuelle est aussi définie dans le fichier serveur.c. Elle est mise en place grâce à la fonction creationInterfaceVirtuelle appelée au début du main. J'ai défini l'interface qui porte le nom "tap0". Donc quand on associe une adresse Ethernet et une adresse ip pour pouvoir utiliser la commande PING, il faut le faire pour cette interface. Je mets les commandes qu'il faut taper pour configurer l'interface ici:

  • openvpn --mktun --dev tap0

Ouvrir l'interface

  • ip link set dev tap0 address 00:11:11:11:11:01

pour avoir la meme adresse que la station 1 du simulateur

  • ip addr add 192.168.1.0/24 dev tap0

définir un réseau ou ping. Je crois avoir remarqué que le réseau doit contenir tous les réseaux du simulateur car si on ping hors de ce réseau rien ne se passe.

  • ip link set dev tap0 up

ip link opérationnel

Faire communiquer les différents modules

Dans cette partie je décris comment l'interface le serveur et le simulateur communiquent.

Le serveur et l'interface virtuelle se trouvant dans le même fichier peuvent communiquer les trames grâce à des tableaux définis en variable global. Ils peuvent donc lire et écrire dans ces tableaux à leur guise. J'ai quand même eu beaucoup de mal à récupérer une trame correcte en sortie de l'interface. Les bytes, que je demandais en hexadécimal, ne se plaçaient pas dans mes tableaux de trames comme je le voulais. De plus les chiffres en hexadécimal comportant des zéros ne les affichaient pas. Ceci explique la partie de code dans le main() où je récupère la trame dans le "buffer" et que j'effectue plusieurs opérations pour la modifier en une trame utilisable par le simulateur. La communication avec le simulateur est un peu plus compliquée. Le simulateur regarde au chargement de la page si le serveur a un message à envoyer grâce à la fonction received_paquet() qui est lancée dans le main de la page reseau.js. A partir de là, le simulateur traite la trame selon son type. Quand le simulateur arrive dans la fonction router_enqueue_packet, on lance une connexion avec le serveur et on envoie notre trame. Ici j'ai mis une condition sur l'envoi de la trame. Cette condition sert juste à définir quelle station est connectée au serveur. Donc une trame est envoyée au serveur seulement si l'adresse Ethernet de la station est celle de la station 1. Le serveur envoie à son tour un message si il en a un à faire passer. Encore une fois j'ai soumis cette réception de message par le simulateur à la même condition mais pour une raison différente. Je recevais trop de paquets d'un coup et tous les envoyer vers le simulateur le faisait crasher.

Le schéma

Vous trouverez le schéma entre serveur et simulateur ainsi que celui de l'architecture interne du simulateur dans le fichier total. J'ai apporté les modifications que vous m'aviez conseillé. Il a été dur de créer un schéma précis et détaillé mais à la fois facile à lire. J'ai donc essayé de regrouper les fonctions dans des ensembles qui forment une action particulière du simulateur afin d’alléger un peu le dessin

Résultats et conclusion

  • enlever la demande des adresses Ethernet du formulaire

J'ai réussi à enlever les demandes Ethernet du formulaire. Les demandes ARP se font proprement.

  • implémenter la gestion des écho-requêtes et réponses d’écho (ICMP 8 et 0)

J'ai réussi à faire circuler des paquets ICMP que j'avais créé au préalable grâce à la fonction serveur_create_paquet qui faisait la requête ARP elle même et utilisait des paquets ICMP reçus de mon interface.

  • implémenter la création et gestion des paquets ICMP de type 5

J'ai codé une première fonction qui m'a donné une piste pour continuer mais je n'ai pas eu le temps de la finir ou de la tester.

  • créer un serveur Websocket

Mon serveur semble bien fonctionner, il envoie et réceptionne les différents paquets.

  • créer une interface virtuelle

J'ai créé une interface fonctionnant correctement. J'arrive à récupérer des trames que je ping dans le terminal ou des demandes ARP.

J'ai eu un problème qui m'a empêché de tester ma simulation avec des paquets ICMP venant de mon interface qui sont juste encapsulés en trame Ethernet sans avoir été modifiés avec la fonction serveur_create_paquet. En effet quand je ping, je récupère tout d’abord une trame ARP qui circule correctement jusque mon simulateur qui l'encapsule et la fait circuler jusqu’à l'adresse qui a été demandée dans le PING. Le paquet ARP de réponse est correctement envoyé jusqu'au serveur. Mais je n'ai pas réussi à envoyer ce paquet ARP dans l'interface pour que le système me renvoie le paquet ICMP. C'est dommage car je pense que si j'avais réussi à récupérer ce paquet ICMP il aurait été facile de le faire circuler comme je l'avais fait avec le paquet ARP.

La question que l'on pourrait me poser est: Si tu n'arrive pas à récupérer un paquet ICMP quand tu donnes la trame ARP, comment as tu fait dans la partie du début pour récupérer des trames ICMP à analyser pour ta fonction serveur_create_paquet?

Mon interface était réglée en TUN au départ. Cela me permettait de recevoir directement le paquet ICMP sans que le système se demande à qui l'envoyer. Pour recevoir les paquets ARP du système j'ai configuré l'interface en TAP.

  • faire communiquer les différents modules

Mes différents modules communiquent correctement entre eux. Je peux facilement modifier et changer les configurations des communications.

  • fournir un schéma de l'architecture du projet


Le second problème que j'ai rencontré est que lorsque je PING l'adresse IP d'une machine qui n'est pas dans le réseau de la station 1 (Celle qui est liée au serveur), le paquet ARP reçus du système ne comporte pas l'adresse IP du routeur (pour récupérer son adresse MAC) mais toujours l'adresse IP du la machine hors du réseau... je ne sais pas comment changer ça non plus.

J’espère que le travail fourni sera à la hauteur de vos attentes malgré le fait que je n'ai pas pu complètement l'achever. J'ai tenté de le répartir le mieux possible avec celui que je dois faire pour mon stage.

Compléments du projet suite au jury du 6 juillet

Fichier:Projet final.zip

Demandes du professeur

  • reprise du serveur websocket et utilisation de la fonction callback pour l'envoi et la réception de données dans le navigateur.
  • suppression des doublons de code dans la fonction "reseau.js".
  • vérification de la pertinence de l'emplacement du code ajouté.
  • optimisation du code.

Points revus

J'ai tout d'abord repris le squelette du serveur WebSocket que mon professeur m'avait proposé où j'ai réimplémenté mon interface virtuelle qui n'a fait l'objet d'aucune remarque du professeur. J'ai veillé à bien utiliser la fonction callback pour l'envoi et la réception de données dans le navigateur.

J'ai ensuite repris la partie Websocket client que j'avais codé dans mon simulateur. Cela m'a permis de supprimer les doublons et de revoir l'emplacement des codes.

Maintenant, il n'existe plus qu'une seule fonction nommée received_paquet qui prend une trame en paramètre à envoyer au serveur et qui reçoit si nécessaire une trame envoyée par le serveur. Cette fonction est appelée en boucle toutes les secondes dans la fonction main de reseau.js. La trame envoyée est une variable globale qui est mise à jour dans la fonction router_enqueue_packet. La trame est envoyée au serveur uniquement si elle est différente de la précédente, pour éviter de renvoyer une trame déjà stockée dans le buffer précédemment.

J'ai revu la fonction create_ip4_packet_from_form qui permet à présent de gérer les demandes ARP et les lectures de tables de routage lors d'une saisie dans le formulaire. Je me suis basé sur la fonction router_route_ip4_packet que j'ai adaptée.

Pour terminer, j'ai relu l'ensemble des lignes de codes que j'avais implémentées afin de corriger les possibles erreurs.

Points ajoutés

J'ai mis à jour la lecture et l’écriture sur mon interface virtuelle. J'utilise toujours la fonction sprintf pour pouvoir transformer la trame reçue sous forme de String, pour qu'elle soit comprise par mon simulateur. J'ai enlevé plusieurs lignes de code que j'ai réussi à simplifier. Pour pouvoir envoyer les trames de réponse sur mon interface virtuelle, j'ai dû les modifier pour qu'elles soient compréhensibles par le système d'exploitation. Ne trouvant pas de fonction adaptée, j'ai créé une fonction moi-même s’appelant "conv", qui permet de placer les trames de type string dans un tableau de caractère avec un chiffre hexadécimal par case. Je peux donc renvoyer ce tableau de caractère au système d'exploitation qui comprend à présent les réponses ARP ou ICMP qu'il reçoit.

J'ai mis en place une liste chaînée avec un ajout en fin de liste pour les messages arrivant du système et une lecture en début de liste pour les messages envoyés vers le simulateur. Cela me sert de queue afin de stocker les messages s'ils arrivent plus vite que mon simulateur ne les traite. Pour cela vous trouverez deux fonctions supplémentaires dans serveur.c:

  • addmessage
  • getmessage

Mon système me signalait une erreur de checksum. J'ai donc fait des recherches est constaté qu'il y avait de multiples façons de calculer un checksum et que visiblement mon système ne le calculait pas de la même façon que celui dans le simulateur. Pour ne pas supprimer le travail qui avait déjà était effectué par mon prédécesseur, j'ai écrit une fonction nouvelle ip4checksum2 adaptée à mon système d'exploitation. Je l'utilise pour calculer les checksums IP et ICMP. Mon ordinateur comprend enfin les messages dans leur entièreté et donc les premiers résultats sont concluants. Je reçois des réponses correctes aux commandes PING. Toutefois, je dois préciser l'option -W 30 lors du ping afin que le système attende plus longtemps les réponses qui prennent un temps plus long à circuler dans le simulateur.

Avant de mettre en place la gestion des messages ICMP 5, j'ai du créer un nouveau scenario routeur_s4.html. En effet, dans le scénario s3 il n'y avait qu'un seul routeur. Or, pour pouvoir utiliser la redirection de route, il me fallait au moins deux routeurs sur le même réseau. Mon nouveau scénario reprend donc le scénario s3 en ajoutant un routeur relié au réseau 192.168.1.0/24 et au nouveau réseau 192.168.3.0/24 composé que d'une seule station"station 5". Comme vous pourrez le constater, ma station 1 ne connaît pas l’existence de ce nouveau routeur. Ici le but est d'ajouter à sa table de routage les chemins lorsque c'est nécessaire.

J'ai mis en place plusieurs fonctions afin de permettre un ajout à la table de routage. la fonction redirection ,déclenchée lorsque qu'un message arrive au niveau d'un routeur, permet de vérifier si le destinataire de ce message est dans le même réseau que l’émetteur. Si c'est le cas, le routeur continue de transmettre le message normalement et envoie de plus un message ICMP 5 de redirection à la station émettrice. Lorsque la station émettrice reçoit ce type de message, elle ajoute le nouveau routeur à sa table grâce à la fonction route_add_entry. Pour éviter que cette manipulation d'ajout se déclenche plusieurs fois, j'ai créé une fonction nommée route_check_entry qui vérifie, avant un ajout, si la table ne contient pas déjà cette route. Évidemment, le but du projet était aussi d'envoyer ce message au système qui ajouterait lui même à sa table, la route en question. Je fais donc circuler le message ICMP5 via mon serveur de façon à ce que cela se fasse. Après cela, lors de l'envoie d'un ping, je constate que le message ne passe plus par la gateway mais directement par le routeur 2.


J'ai mis en place un fichier bash init.sh, permettant au professeur d"exécuter au départ les configurations de l'interface virtuelle ainsi que celles du cache ARP (ajout d'adresse mac et d'adresse IP à l'interface ainsi qu'une route pour accéder au réseaux différents du reseau 192.168.1.0. cette configuration de route est la suivante : 192.168.0.0/16 via 192.168.1.1. cela me permet accéder à tous les réseaux compris entre 192.168.0 et 192.168.254 sur mon simulateur. Vous allez penser que j'aurai du définir mon routeur 1 comme passerelle par défaut or ce n'étais pas pratique. En effet ma station 1 qui représente mon ordinateur est en réalité un routeur à présent, car mon ordinateur est aussi relié au réseau local de ma maison ainsi qu'a internet via ma freebox. Ma passerelle par défaut est donc déjà ma freebox. En configurant le routeur 1 comme passerelle par défaut je recevais beaucoup de messages destinés à aller sur internet et non sur mon simulateur. J'aurai pu ajouter tout les réseaux de mon simulateur à ma table de routage. Mais en mettant ce masque (/16) cela permettra au simulateur d’être agrandi sans devoir changer la table de routage de votre station. j'ai laissé par soucis de clarté la table de routage de ma station 1 dans mon simulateur identique. Elle contient donc une route par défaut vers le routeur 1. Cela n'empeche pas le fonctionnement de mon projet même si nous somme d’accord que ma table de routage simulée n'est pas identique à celle réelle.

Cette commande doit être lancée une seule fois à l'allumage de l'ordinateur.

contenu du fichier init.sh:

#!/bin/bash
#configuration de l'interface virtuelle
sudo openvpn --mktun --dev tap0
sudo ip link set tap0 up
sudo ip addr add 192.168.1.100/24 dev tap0
sudo ip link set dev tap0 address 00:11:11:11:11:01
sudo ip route add 192.168.0.0/16 via 192.168.1.1
#suppression du Multicast et de l'IPV6
sudo ifconfig tap0 -multicast
sudo sysctl -w net.ipv6.conf.tap0.disable_ipv6=1
#configuration d'un parametre du cache ARP
sudo ip ntable change name arp_cache retrans 40000 dev tap0


J'ai créé un Makefile se trouvant dans le dossier serveur. Il permet de compiler le fichier serveur.c. Vous pouvez ensuite lancer le serveur via la commande ./serveur.

Tests

J'ai décidé de faire deux vidéos de test. Les deux vidéos se trouvent dans le dossier "vidéo" situé dans le dossier "projet". La première montrera la réponse sur un ping envoyé à une station du même réseau. La deuxième montrera un ping envoyé sur une station d'un autre réseau. Dans cette dernière, nous pourrons visualiser un message de redirection de façon à optimiser la route des messages suivants.

Dans la première vidéo, on envoie un ping à l'adresse 192.168.1.200. On voit tout d’abord un message ARP passer qui remplit la cache ARP du systeme, qui envoie ensuite un message icmp, que l'on visualise sur le simulateur. La station en question répond à ce message. On voit ensuite, dans le terminal, que nous recevons bien un paquet ICMP de réponses.

Fichier:Video1 delatte.MP4

Dans la seconde vidéo, nous envoyons un ping à la station 192.168.3.1 qui se situe donc dans un réseau différent de ma station émettrice. Le système, qui ne connaît pas le chemin vers ce réseau, envoie son message ICMP à son routeur par défaut, que j'ai configuré comme étant le routeur 192.168.1.1. Le routeur par défaut, ayant dans sa table de routage l'information nécessaire, transmet le message ICMP au routeur 2 qui pourra enfin router ce message vers la station dans le réseau 192.162.1.3. Le message de réponse est renvoyé directement vers la station 1 et apparaît dans le terminal.

On voit en parallèle un message ICMP 5 envoyé depuis le routeur par défaut vers la station 1 qui met à jour sa table de routage instantanément. De plus, on constate dans le terminal la réception de ce message et donc l'ajout à la table de routage du système lui-même.

Lorsque le premier message de réponse est reçu par le terminal, j'envoie un nouveau ping à la même station. On constate cette fois que cette requête ne passe plus par le routeur par défaut mais directement par le routeur 2. Cela confirme l'ajout de la route optimale dans la table de routage du système.

Fichier:Video2 delatte.MP4

consignes

Je mets ici le protocole pour pouvoir utiliser mon projet.

Je me place dans le dossier projet/serveur.

J’exécute tout d'abord le fichier init.sh qui permet d'ouvrir et configurer l'interface tap0. Il permet de supprimer certains types de messages comme l'ipv6 ou le multicast de l'interface. J'ai fait ce choix car mon serveur récupérait des fichiers qui ne gênaient pas le fonctionnement du simulateur mais m’empêchaient de voir les paquets qui m’intéressaient réellement.

Je compile mon serveur grâce à la commande make(vérifiez bien la version de libwebsocket nécessaire, j'utilise la version 2.x) et je lance le serveur avec ./serveur. Le serveur se met en attente. Je peux à présent ouvrir un scénario html. Lors de l'ouverture, mon serveur indiquera connexion établie. Si le client n'arrive pas à se connecter, il affichera une alerte non connecté au serveur. Cela n'empêche pas pour autant d'envoyer des trames grâce au formulaire.

Je peux à présent taper la commande ping à destination d'une station du réseau. Il est important d'ajouter l'option -W 200 qui permet au système d'attendre les réponses plus longtemps que 4 secondes par défaut. Je vous invite à utiliser l'option -c qui permet de limiter le nombre de messages envoyés. En effet, une surcharge de messages sur le simulateur fait ralentir les animations des paquets.

remarques

Fichier:Projet final.zip

Fichier:Serveur revu.zip

Sources