IMA4 2016/2017 ECP1
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 a enlever la demande d'adresse Ethernet. les stations trouverons les adresses grâce au protocole ARP.
Planning prévisionnel
Travail effectué
Lundi 5 juin
J'ai tout d'abord commencé a 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é a modifier le formulaire IP. L'adresse Ethernet source et maintenant déterminée automatiquement grâce a l'adresse IPV4. En effet grâce a 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 packet, 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 pour simuler avec exactitude le fonctionnement d'un reseau. 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 a 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 modifie j'arrive a faire circuler les paquet 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ément sources nécessaire a l'envoie d'un packet 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 ladresse ethernet,port,cachearp source grace a l'adresse ip donnee 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 packet ARP a envoyer grâce a une fonction déjà existante que j'ai un peu adapter. j'ai donc crée station_route_ip4 qui est fortement inspirée de router_route_ipv4.
//requete ARP pour retrouver ladresse 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 packet arp} else {suite du code pour creation IPV4}
vendredi 9 juin
Le problème rencontre est que si je possède l'adresse IP d'une machine d'un autre réseau, 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 ladresse IP est dans le reseau 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 ete toute faite dans la fonction create_ip4_packet_from_form(form).
Je peux maintenant commencer la partie ICMPV4.
Je me suis donc renseigne sur les pauet ICMP aue je devrai gerer:
- Le paquet de type 8 est un echo request.
- Le paquet de type 0 est un echo reply.
Le ping est en realite 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 a 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, fais avec la librairie Websocket linux est disponible dans la partie Document. Il porte le nom serveur.txt. Ce serveur contient les fonctions de bases pouvoir communiquer avec notre client simulateur. Je devrai le modifier légèrement pour qu.il puisse réceptionner des trame écrites dans le terminale afin de les transmettre directement au simulateur qui enverra le paquet de l'adresse source a l'adresse destination.
Maintenant que mon serveur tourne, je rajoute le code nécessaire dans le client, c'est a dire sur la page réseau.js. dans le main().
Toutefois je rencontre un problème: quand je cree 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 a la fin du voyage d'un paquet pour stocker celui ci en mémoire sur la station correspondante. A pressent 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.
mardi 13 juin
j'ai essaye de comprendre comment envoyer un paquet internet depuis le serveur. Mais je n'arrive toujours pas a initialiser une connexion depuis le serveur. De plus pour lire les entrée clavier j’ai voulu utiliser la fonction read. Mais cette fonction est bloquante. Le serveur n'est donc plus a l’écoute des messages arrivant du simulateur.
mercredi 14 juin
j'ai réussi a concevoir mon interface Ethernet virtuelle. Comme vous pouvez le voir je n'ai pas encore rajouter 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 fallut rajouter dans le main les finfos 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 emises 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 a mon serveur de communiquer avec le simulateur. En effet le problème rencontré est que si je decommente cette ligne, je ne recevrai pas tout les messages du simulateur, car la fonction read est bloquante. Mais si je rajoute O_NONBLOCK j'ai une erreur a la lecture du read. Je n'ai pas encore trouve de solution. Avant de pouvoir ping j'ai du procéder a quelque 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
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 attendue... 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 avere 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é conseille d'utiliser régulièrement la fonction callback_on_Writable pour pouvoir decleclencher un evennement de ce genre:
case LWS_CALLBACK_SERVER_WRITEABLE: { //envoie du paquet ICMP recu 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 ou 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 que a traiter l'envoie de ces trame dans le simulateur et mon projet sera termine.