Previous Contents

2   Organisation du travail

2.1   Généralités

Il est fortement conseillé de suivre les étapes proposées ci-après pour réaliser le travail.

2.2   Organisation modulaire

Le projet est constitué de 3 bibliothèques et des programmes commutateur virtuel et client d'administration. Chacune de ces entités est développée dans un répertoire propre. On gère donc les cinq répertoires suivants :
Network -
pour les sources de la bibliothèque contenant les fonctions de gestion réseau libnet ;
Threads -
pour les sources de la bibliothèque contenant les fonctions de gestion des threads libthrd ;
IPC -
pour les sources de la bibliothèque contenant les fonctions de gestion des communications inter-processus libipc ;
Switch -
pour le programme principal, l'algorithme du commutateur, la gestion des connexion et le traitement des commandes de l'administrateur ;
Admin -
pour le client d'administration.
Cette arborescence et quelques squelettes de fichiers sont disponibles sous forme d'un fichier au format tar et compressé à l'URL http://www.plil.net/~rex/Enseignement/Systeme/Tutorat.GIS4.commutateur/commutateur.tgz. Transférez ce fichier dans votre compte Polytech'Lille et décompressez-le avec la commande tar xvzf commutateur.tgz. Vous pouvez constater que très peu de code vous est fourni : Cependant, le répertoire créé contient déjà un fichier Makefile global et des Makefile annexes dans chaque sous-répertoire. La cible clean du Makefile global est opérationnelle, à vous de compléter le fichier pour rendre la cible all, elle aussi, opérationnelle. Vous avez un exemple de Makefile permettant de générer un exécutable et un exemple de Makefile permettant de construire une bibliothèque. Appuyez vous sur ces deux exemples pour compléter les autres Makefile. Le projet doit pouvoir être généré par la simple commande make exécutée dans le répertoire principal du projet. Il doit être aussi possible de regénérer complètement le projet par la commande make clean all lancée du même répertoire.

On prévoira de pouvoir compiler les différents sources avec un drapeau DEBUG (option -DDEBUG de gcc), permettant un affichage conditionnel d'informations de déverminage des programmes.

2.3   Sockets et serveur TCP

Il s'agit dans cette étape de réaliser un serveur TCP basique à l'aide de l'interface de programmation des sockets.

Ecrivez dans le module libnet.c (répertoire Network), les deux fonctions suivantes : Testez cette bibliothèque en écrivant un programme commutateur.c (répertoire Switch) qui l'utilise et dont la fonction de traitement des connexions (celle appelée par boucleServeur) effectue juste une écriture de message dans la socket et clôt la connexion. Pour écrire, et plus tard lire, sur la connexion utilisez les fonctions classiques comme fgets ou fprintf. Pour y arriver vous devez transformer le descripteur de socket en une structure de fichier par la primitive fdopen.

Ce programme peut prendre des arguments : -p <port> ou --port <port> pour spécifier un numéro de port différent de celui par défaut (port 4000). Pour traiter les arguments, utilisez la fonction getopt_long (voir la page de manuel correspondante). Si les arguments sont incorrects, on doit afficher un message qui précise la syntaxe. Pour plus de clarté, l'analyse des arguments et l'affichage de la syntaxe seront écrits dans des fonctions séparées.

Modifiez la fonction de traitement des connexions afin que le serveur affiche le nom de la machine distante (sur la sortie standard) et qu'elle ne ferme la connexion qu'après le traitement des commandes lport et dport. Vous devrez écrire dans libnet une fonction SocketVersNom qui prend une socket en argument et renvoie le nom dans une chaîne de caractères. Vous utiliserez les fonctions getpeername et gethostbyaddr (voir les pages de manuel et le support du cours de réseau http://www.plil.net/~rex/Enseignement/Reseau/Reseau.GIS4)

Testez votre serveur avec plusieurs commandes nc simultanées. Conclusions ?

Maintenant que vous avez écrit un embryon de serveur TCP, passez au client TCP. Ajoutez les fonctions nomVersAdresse et connexionServeur dans libnet (les prototypes sont fournies dans le fichier de définition de la bibliothèque). Réalisez un exécutable temporaire utilisant ces deux fonctions pour se connecter sur le serveur commutateur en envoyant les deux commandes lport et dport.

2.4   Un commutateur virtuel à base de processus légers

Pour que votre commutateur puisse traiter plusieurs ports simultanément, vous allez lancer un processus léger (thread) par port. Pour cela, implémentez la fonction publique de libthrd (répertoire Threads) :

int lanceThread(void (*)(int),int);
Cette fonction doit avoir comme action de lancer un thread dans le mode détaché. Ce thread doit exécuter la fonction passée en paramètre. La dite fonction prenant elle même comme paramètre le second paramètre de lanceThread. Il vous est conseillé d'utiliser une fonction intermédiaire récupérant un pointeur vers une structure comprenant les deux paramètres de lanceThread.

Testez votre fonction lanceThread en créant une nouvelle fonction dans commutateur.c qui appelle votre fonction de traitement des connexions via lanceThread. Utilisez la nouvelle fonction comme paramètre de boucleServeur. Pour plus de clarté, ce serait une bonne idée de déplacer les fonctions de traitement de connexions du fichier commutateur.c vers le fichier gestionConnexions.c.

Vérifiez que votre commutateur est maintenant capable de gérer plusieurs connexions simultanément (avec votre exécutable client TCP ou avec l'utilitaire nc).

La fonction lanceThread est utilisée dans 5 situations différentes. Tout d'abord comme expliqué plus haut pour gérer des connexions TCP ; dans ce cas l'entier passé à la fonction paramètre est un descripteur de socket de dialogue. Mais lanceThread est aussi utilisée pour démarrer le processus léger de gestion de la file de message d'administration et le processus léger de purge des adresses Ethernet. Dans ces deux cas, l'entier passé à la fonction paramètre n'est pas utilisé (passez la valeur 0 par exemple). La fonction lanceThread est enfin utilisée pour lancer des processus légers de gestion de port suite à des requêtes de connexion du client d'administration à une interface Ethernet virtuelle ou à un commutateur distant. Dans ces deux derniers cas, les entiers passés à la fonction paramètre sont des descripteurs ; un descripteur de fichier pour une connexion à une interface Ethernet virtuelle et un descripteur de socket pour une connexion à un commutateur distant.

2.5   Structure de données du commutateur

La structure de données du commutateur a déjà été évoquée plus haut, en particulier dans la section 1.4.

Définissez cette structure de données dans le fichier entête commutateur.h et écrivez sa fonction d'initialisation.

La structure des clients d'administration est plus à sa place dans le fichier entête gestionAdmin.h.

2.6   Analyse des opérations sur la structure de données

Analysez les opérations nécessaires sur les structures de données afin de déterminer celles qui nécessitent l'utilisation de sémaphores. Implantez dans votre bibliothèque libthrd.a les deux fonctions publiques :

void P(int) ;
void V(int) ;
Ces fonctions cachent totalement le fait que vous utilisez des verrous d'exclusion mutelle pour threads POSIX. En particulier, les verrous sont représentés par une constante.

En implantant dans le module algorithmeCommutateur.c les fonctions nécessaires au fonctionnement du commutateur et dans le module commandesAdmin.c les fonctions nécessaires à la gestion du client d'administration vous prendrez soin d'y ajouter les poses et levées de verrous nécessaires.

2.7   Le client d'administration

Il vous faut implanter le client d'administration et le processus léger chargé de traiter les requêtes de l'administrateur dans le commutateur.

Les commandes sont passées en argument au client d'administration (donc pas d'interface textuelle, mais une analyse des arguments au moyen de getopt_long). Ce processus communique avec le commutateur au moyen de deux files de messages : une pour les commandes et une pour les réponses. La file des commandes est créée par le commutateur à l'initialisation. Un thread, lancé par le commutateur est chargé de scruter en permanence cette file. La file des réponses est créée par le client d'administration.

La bibliothèque libipc contiendra toutes les fonctions permettant de cacher le fait que la communication est implantée par IPC (selon le même principe que celui utilisé pour les sémaphores).

Le processus léger de gestion des requêtes est lancé par la fonction lanceThread de la bibliothèque libthrd. Cette fois le second paramètre n'est pas utile, vous pouvez passer une valeur quelconque.

Lorsque que le commutateur se termine, tous les clients d'administration en mode de scrutation ou de reniflage doivent se terminer aussi.

2.8   Le fonctionnement du commutateur

Vous pouvez maintenant vous attaquer à l'écriture du code concernant le commutateur virtuel. Il vous est conseillé d'utiliser les fichiers suivants : Vous ferez en sorte que les écritures de paquets sur les connexions TCP se fassent dans un format ASCII. Il est demandé que la taille du paquet soit d'abord écrite en décimal sur une ligne puis que les octets du paquets soient écrit à la suite en hexadécimal sur deux chiffres. Les octets sont séparés par des espaces. Vous pouvez effectuer des sauts de lignes pour faciliter la lecture des paquets. De cette façon le fonctionnement du commutateur virtuel peut être testé en lançant plusieurs nc, en effectuant manuellement les commandes lport et dport puis en envoyant des paquets toujours manuellement.

Pour la création des interfaces Ethernet virtuelles vous utiliserez la fonction creationInterfaceVirtuelle disponible dans la bibliothèque libnet. Un exécutable d'un utilisateur de base n'ayant pas les droits pour créer une interface réseau vous utiliserez le script super setcap pour ajouter ces droits. De la même façon vous ne pouvez pas directement configurer les interfaces virtuelles. Vous passerez donc par le script super ifconfig.

Pour réaliser la déconnexion d'un port, une solution consiste à ajouter un tube (pipe) dans la structure des ports. Le processus léger de gestion de port se met alors en écoute sur le descripteur du port et sur le tube (primitive select). Si l'activité est détectée sur le tube, le processus léger se termine sinon le paquet Ethernet est lu sur le descripteur du port. L'arrêt d'un processus léger de gestion de port peut ainsi s'obtenir par simple écriture dans son tube.
Previous Contents