commit 9f3a45b8b22cbbf5cbf298fe5ae080ec7f8915a9 Author: gasc Date: Sun Aug 22 13:24:38 2021 +0200 Importation du code depuis GitHub diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e8db72 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Bureau d'étude réseau - Répertoire de travail +Je mets ici le résultat de mes travaux pour le bureau d'étude réseau de 3ème année MIC à l'INSA de Toulouse. Veuillez noter que je ne mets que `mictcp.c` et que vous allez avoir besoin du reste de l'environnement pour compiler et utiliser le protocole. + +## Avancement +* La v1 a été implémentée et est parfaitement fonctionnelle. +* La v2 a été implémentée et fonctionne. Aucun paquet n'est perdu (des compteurs permettent de le vérifier) et les performances sont acceptables. +* La v3 est fonctionnelle. Elle peut être testée en mettant DO_HANDSHAKE = 0 +* La v4.1 est fonctionnelle et le handshake permet l'échange du ADMITTED_LOSS_RATE + +## Notes pour le correcteur +*Remarque : Ces notes sont disponibles dans l'entête du fichier mictcp.c.* +``` +TP BE Réseaux + + À L'ATTENTION DU CORRECTEUR: + - Ce fichier est le seul fichier que j'ai modifié + + - La v1 a été implémenté et fonctionne avec tsock_texte et + tsock_video. + + - La v2 a été implémentée et reprise de perte ok. Pour tester la + v2, vous devez faire quelques modifications : + * Régler les parametres comme suivant : + MOD_SEQ_NUM = 2 + ADMITTED_LOSS_RATE = 0 + * Il faut décommenter les lignes commençant par // [V2] + Ces lignes sont dans la fonction process_received_PDU(). + + - La v3 est implémentée et fonctionnelle. ADMITTED_LOSS_RATE permet de + parametrer le taux de paquets perdus abandonnés. S'il vaut 5, alors 5% + des paquets perdus ne seront pas renvoyés. Pour tester la v3, passez : + * DO_HANDSHAKE = 0 + Utiliser cette version permet d'avoir moins de ralentissements, mais au + prix de coupure et des pertes d'informations. + + - La v4.1 est implémentée et fonctionnelle. Le handshake permet également + d'échanger le taux de pertes admissibles. Pour tester la v3, le handshake + peut être désactivé avec DO_HANDSHAKE = 0. + On peut faire plusieurs remarques sur le programme final : + - Le taux de pertes admis (ADMITTED_LOSS_RATE) est calculé à chaque + itération à partir de l'ensemble des pertes et des envoies fait jusqu'à + présent. On aurait pu implémenter une fenêtre (e.g. avec une pile FIFO, + et ça serait plus pertinent pour de longues transmissions) mais il est alors + plus difficile d'observer les effets de la v3 et de se s'arrurer que les + taux de pertes admises sont corrects. + - La gestion des pertes admissibles est entièrement gérée par la source + et fonctionne sur le principe du stop and wait uniquement. + + Améliorations possibles : + Je vous propose ici quelques idées pour aller plus loin que je n'ai pas eu + le temps d'implémenter : + - Mettre en place de l'asynchronisme entre le client et MIC TCP. Cela pourrait + être fait avec un buffer créant une file d'attente avant le stop and wait. + - Mettre en place le multiplexage, en passant toutes les variables globales + dans un struct pour chaque socket et utiliser le numéro de port pour identifier + à quel socket un paquet appartient. + - Utiliser le RTT plutôt qu'un timeout fixe + - Regarder l'implémentation de TCP pour le remplacer par MIC-TCP avec par exemple + l'utilisation de LD_PRELOAD (bon ça, c'est vraiment ambitieux). +``` + diff --git a/mictcp.c b/mictcp.c new file mode 100644 index 0000000..7ee3de1 --- /dev/null +++ b/mictcp.c @@ -0,0 +1,536 @@ +#include +#include + +/* + TP BE Réseaux + + À L'ATTENTION DU CORRECTEUR : + - Ce fichier est le seul fichier que j'ai modifié + + - La v1 a été implémenté et fonctionne avec tsock_texte et + tsock_video. + + - La v2 a été implémentée et reprise de perte ok. Pour tester la + v2, vous devez faire quelques modifications : + * Régler les parametres comme suivant : + MOD_SEQ_NUM = 2 + ADMITTED_LOSS_RATE = 0 + * Il faut décommenter les lignes commençant par // [V2] + Ces lignes sont dans la fonction process_received_PDU(). + + - La v3 est implémentée et fonctionnelle. ADMITTED_LOSS_RATE permet de + parametrer le taux de paquets perdus abandonnés. S'il vaut 5, alors 5% + des paquets perdus ne seront pas renvoyés. Pour tester la v3, passez : + * DO_HANDSHAKE = 0 + Utiliser cette version permet d'avoir moins de ralentissements, mais au + prix de coupure et des pertes d'informations. + + - La v4.1 est implémentée et fonctionnelle. Le handshake permet également + d'échanger le taux de pertes admissibles. Pour tester la v3, le handshake + peut être désactivé avec DO_HANDSHAKE = 0. + On peut faire plusieurs remarques sur le programme final : + - Le taux de pertes admis (ADMITTED_LOSS_RATE) est calculé à chaque + itération à partir de l'ensemble des pertes et des envoies fait jusqu'à + présent. On aurait pu implémenter une fenêtre (e.g. avec une pile FIFO, + et ça serait plus pertinent pour de longues transmissions) mais il est alors + plus difficile d'observer les effets de la v3 et de se s'arrurer que les + taux de pertes admises sont corrects. + - La gestion des pertes admissibles est entièrement gérée par la source + et fonctionne sur le principe du stop and wait uniquement. + + Améliorations possibles : + Je vous propose ici quelques idées pour aller plus loin que je n'ai pas eu + le temps d'implémenter : + - Mettre en place de l'asynchronisme entre le client et MIC TCP. Cela pourrait + être fait avec un buffer créant une file d'attente avant le stop and wait. + - Mettre en place le multiplexage, en passant toutes les variables globales + dans un struct pour chaque socket et utiliser le numéro de port pour identifier + à quel socket un paquet appartient. + - Utiliser le RTT plutôt qu'un timeout fixe + - Regarder l'implémentation de TCP pour le remplacer par MIC-TCP avec par exemple + l'utilisation de LD_PRELOAD (bon ça, c'est vraiment ambitieux). + +*/ + + +/* RÉGLAGE DES FONCTIONNALITÉES */ +// Taux de perte simulé (%) +#define LOSS_RATE 10 + +// Taux de perte admis (%)(pour la v3) +#define ADMITTED_LOSS_RATE 5 + +// Nombre de numéros de séquence différents possible (v3 : 2147483646) +#define MOD_SEQ_NUM 2147483646 + +// 0: Ne pas utiliser les fonctions connect() et accept() +// 1: Utiliser ces fonctions (facultatif dans le BE) +#define DO_HANDSHAKE 1 + +// Niveau de verbositée: 0, faible; 1, fort +#define DEBUG 1 + +// Autres parametres +#define MAX_SOCK 10 +#define TIMEOUT 2 +#define N_S 0 + + + +/// Variable globale +// Contient l'ensemble des sockets créés, dans les faits +// seul l'utilisation d'un seul socket a été implémenté (n°0) +mic_tcp_sock tab_sock[MAX_SOCK]; +// Contient le loss rate connu +int admitted_loss_rate = 0; + +// Numéros de séquence +int seq_num = 0; +int ack_num = 0; + + +// Compteur permettant de savoir si des packets +// ont été perdus. --> Aucune pertes pour la v2. +int rcv_c = 0; /* Compteur de paquets reçus */ +int lss_c = 0; /* Compteur de paquets perdus */ + +int snd_c = 0; /* Compteur de paquets envoyés */ + +// Compteur pour la v3 +int acc_c = 0; /* Compteur des pertes renvoyés */ +int iac_c = 1; /* Compteur des pertes abandonnée */ + + + +/* + * Fonction initialisant toutes les composantes d'un PDU + * à zéro. + */ +void reset_pdu(mic_tcp_pdu *pdu) +{ + (*pdu).header.source_port = 0; + (*pdu).header.dest_port = 0; + (*pdu).header.seq_num = 0; + (*pdu).header.ack_num = 0; + (*pdu).header.syn = 0; + (*pdu).header.ack = 0; + (*pdu).header.fin = 0; + (*pdu).payload.data = NULL; + (*pdu).payload.size = 0; +} + + +/* + * Permet de créer un socket entre l’application et MIC-TCP + * Retourne le descripteur du socket ou bien -1 en cas d'erreur + */ +int mic_tcp_socket(start_mode sm) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + } + + int result = -1; + mic_tcp_sock s; + result = initialize_components(sm); /* Appel obligatoire */ + s.fd = 0; + + if (result != -1) + result = s.fd; + + set_loss_rate(LOSS_RATE); + return result; +} + + +/* + * Permet d’attribuer une adresse à un socket. + * Retourne 0 si succès, et -1 en cas d’échec + */ +int mic_tcp_bind(int socket, mic_tcp_sock_addr addr) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + } + + // On test que l'identifiant donné est bon + if (socket < 0 || socket >= MAX_SOCK) + { + return -1; + } + + tab_sock[socket].state = IDLE; + tab_sock[socket].addr = addr; + return 0; +} + + +/* + * Met le socket en état d'acceptation de connexions + * Retourne 0 si succès, -1 si erreur + */ +// Fonction implélentée dans le cadre de la v4 +int mic_tcp_accept(int socket, mic_tcp_sock_addr *addr) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + } + + int success = 0; + + if (!DO_HANDSHAKE) { + admitted_loss_rate = ADMITTED_LOSS_RATE; + goto skip; + } + + mic_tcp_pdu pdu; + + // On attend un SYN, process_received_PDU() doit nous le donner + while (tab_sock[socket].state != SYN_RECEIVED) {} + printf("[CONNECTION STATE] SYN RCV\n"); + + // Construction du SYN ACK + reset_pdu(&pdu); + pdu.header.source_port = tab_sock[socket].addr.port; + pdu.header.dest_port = addr->port; + pdu.header.syn = 1; + pdu.header.ack = 1; + + // On l'envoie + + if ((success = IP_send(pdu, *addr)) == -1) { + return -1; + } + printf("[CONNECTION STATE] SYNACK sent.\n"); + + // On attend l'ACK + while (tab_sock[socket].state != ESTABLISHED) { + IP_send(pdu, *addr); + printf("[CONNECTION STATE] SYNACK re-sent.\n"); + } + printf("[CONNECTION STATE] ESTABLISHED\n"); + + + skip: + tab_sock[socket].state = ESTABLISHED; + return success; +} + + +/* + * Permet de réclamer l’établissement d’une connexion + * Retourne 0 si la connexion est établie, et -1 en cas d’échec + */ +// Fonction implélentée dans le cadre de la v4 +int mic_tcp_connect(int socket, mic_tcp_sock_addr addr) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + } + + admitted_loss_rate = ADMITTED_LOSS_RATE; + + if (!DO_HANDSHAKE) goto skip; + + int success = -1; + mic_tcp_pdu pdu, pdu_rcv; + + // On vérifie que notre socket est prêt + if (tab_sock[socket].state != IDLE){ + return -1; + } + + // On construit notre pdu SYN et on l'envoie + printf("[CONNECTION STATE] SYN\n"); + reset_pdu(&pdu); + pdu.header.dest_port = addr.port; + pdu.header.syn = 1; + pdu.payload.data = (char *) &admitted_loss_rate; + pdu.payload.size = 4; + + printf("[CONNECTION STATE] Config done. Sending..."); + if ((success = IP_send(pdu, addr)) == -1){ + printf("Fail !\n"); + return -1; + } + printf("Done.\n"); + + // On attend le SYN ACK + while (1){ + printf("[CONNECTION STATE] Wait SYNACK (d.: %d)\n", success); + reset_pdu(&pdu_rcv); + success = IP_recv(&pdu_rcv, &addr, TIMEOUT); + if (pdu_rcv.header.syn == 1 && pdu_rcv.header.ack == 1){ + printf("[CONNECTION STATE] SYNACK, sending ACK.\n"); + pdu.header.syn = 0; + pdu.header.ack = 1; + if ((success = IP_send(pdu, addr)) == -1){ + return -1; + } + break; + } + IP_send(pdu, addr); + } + + + // Connecté ! + skip: + tab_sock[socket].state = ESTABLISHED; + tab_sock[socket].addr = addr; + + if (DEBUG) printf("[CONNECTION STATE] ESTABLISHED\n"); + + return 0; +} + +/* + * Fonction determinant si une perte est acceptable ou non. + * Elle effectue des calculs sur integer uniquement pour + * augmenter sa réactivité. + */ +int is_acceptable(){ + return acc_c * 1000000 / (10000 * (iac_c+acc_c)) < admitted_loss_rate && ADMITTED_LOSS_RATE; +} + +/* + * Permet de réclamer l’envoi d’une donnée applicative + * Retourne la taille des données envoyées, et -1 en cas d'erreur + */ +int mic_tcp_send(int mic_sock, char *mesg, int mesg_size) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + } + + if (tab_sock[mic_sock].state != ESTABLISHED) + return -1; + + mic_tcp_pdu pdu, rcv; + int sq = seq_num; + int sent_size = -1; + seq_num = (seq_num + 1) % MOD_SEQ_NUM; + + reset_pdu(&pdu); + pdu.header.dest_port = tab_sock[mic_sock].addr.port; + pdu.header.seq_num = sq; + + pdu.payload.data = mesg; + pdu.payload.size = mesg_size; + + int ok = 0; + int counted = 0; + while (!ok) + { + if (DEBUG) printf("[DEBUG: mic_tcp_send] Envoi du paquet N°%d...\n", sq); + sent_size = IP_send(pdu, tab_sock[mic_sock].addr); + + if (sent_size <= 0){ + printf("[ERREUR] Erreur send().\n"); + return -1; + } + + if (DEBUG) printf("[DEBUG: mic_tcp_send] Attente de l'ACK n°%d...\n", sq); + reset_pdu(&rcv); + IP_recv(&rcv, &(tab_sock[mic_sock].addr), TIMEOUT); + + + // Doit-on ne renvoyer pas le paquet ? + if (rcv.header.ack_num == sq && rcv.header.ack == 1) { + // Envoi ok. + ok++; + snd_c++; + if (DEBUG) printf("[DEBUG V3] Envoi ok : n°%d (%d/%d)\n", sq, acc_c, iac_c); + } + // Conditions pour la v3 uniquement. + else if (is_acceptable()) { + // Envoi nok, mais acceptable + ok++; + lss_c++; + acc_c++; + if (DEBUG) printf("[DEBUG V3] Perte acceptable : n°%d (%d/%d)\n", sq, acc_c, iac_c); + } + else { + // Envoi nok, inacceptable + if (!counted) { + iac_c++; + counted++; + } + if (DEBUG) printf("[DEBUG V3] Perte inacceptable : n°%d (%d/%d)\n", sq, acc_c, iac_c); + } + } + + if (DEBUG) printf("[DEBUG] ACK n°%d reçu. Nb de pckt send : %d\n", rcv.header.ack_num, snd_c); + + return sent_size; +} + + +/* + * Permet à l’application réceptrice de réclamer la récupération d’une donnée + * stockée dans les buffers de réception du socket + * Retourne le nombre d’octets lu ou bien -1 en cas d’erreur + * NB : cette fonction fait appel à la fonction app_buffer_get() + */ +int mic_tcp_recv(int socket, char *mesg, int max_mesg_size) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + } + + if (tab_sock[socket].state != ESTABLISHED) + return -1; + + mic_tcp_payload payload; + payload.data = mesg; + payload.size = max_mesg_size; + + int read_size = app_buffer_get(payload); + if (DEBUG) printf("[DEBUG] Read: read_size = %d\n", read_size); + if (read_size > 0) rcv_c++; + if (DEBUG) printf("[DEBUG] Nb de pckt recu : %d\n", rcv_c); + + return read_size; +} + + +/* + * Permet de réclamer la destruction d’un socket. + * Engendre la fermeture de la connexion suivant le modèle de TCP. + * Retourne 0 si tout se passe bien et -1 en cas d'erreur + */ +// Non implémentée cette année, retourne 0 dans tous les cas +int mic_tcp_close(int socket) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction : "); + printf(__FUNCTION__); + printf("\n"); + } + + tab_sock[socket].state = CLOSED; + return 0; +} + + +/* + * Traitement d’un PDU MIC-TCP reçu (mise à jour des numéros de séquence + * et d'acquittement, etc.) puis insère les données utiles du PDU dans + * le buffer de réception du socket. Cette fonction utilise la fonction + * app_buffer_put(). + */ +void process_received_PDU(mic_tcp_pdu pdu, mic_tcp_sock_addr addr) +{ + if (DEBUG) + { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + } + + switch (tab_sock[N_S].state) + { + case IDLE: + if (pdu.header.syn == 1) + { + admitted_loss_rate = *((int*)pdu.payload.data); + if (DEBUG) printf("[CONNECTION STATE] Getting loss rate : %d\n", admitted_loss_rate); + tab_sock[N_S].state = SYN_RECEIVED; + } + else + { + if (DEBUG) + printf("[DEBUG] Paquet ignoré.\n"); + } + break; + + case SYN_RECEIVED: + if (pdu.header.ack == 1) + { + tab_sock[N_S].state = ESTABLISHED; + } + else + { + if (DEBUG) + printf("[DEBUG] Paquet ignoré.\n"); + } + break; + + case CLOSED: + if (DEBUG) printf("[DEBUG] Reception d'un paquet sur un port fermé\n"); + break; + + case ESTABLISHED: + if (DEBUG) printf("[DEBUG: ESTABLISHED] Rcv n°%d, wait n° %d\n", pdu.header.seq_num, ack_num); + + // [V2] if (pdu.header.seq_num == ack_num) + if (pdu.header.seq_num >= ack_num) + { + if (DEBUG) printf("[DEBUG] Num ok. In buffer.\n"); + app_buffer_put(pdu.payload); + + // [V2] ack_num = (ack_num + 1) % MOD_SEQ_NUM; + ack_num = pdu.header.seq_num + 1; + + // Préparation du ACK + mic_tcp_pdu ack; + reset_pdu(&ack); + int s; + + ack.header.source_port = pdu.header.source_port; + ack.header.dest_port = addr.port; + ack.header.ack_num = pdu.header.seq_num; + ack.header.ack = 1; + + if (DEBUG) printf("[DEBUG] Envoi du ACK\n"); + if ((s = IP_send(ack, addr)) == -1) + { + printf("[ERREUR] Envoi ACK fail.\n"); + return; + } + } + else + { + if (DEBUG) printf("[DEBUG] No go.\n"); + mic_tcp_pdu ack; + reset_pdu(&ack); + int s; + + ack.header.source_port = pdu.header.source_port; + ack.header.dest_port = addr.port; + + ack.header.ack = 1; + ack.header.ack_num = pdu.header.seq_num; + + if (DEBUG) printf("[DEBUG] Envoi du ACK N°%d\n", ack.header.ack_num); + if ((s = IP_send(ack, addr)) == -1) + { + printf("[ERREUR] Envoi ACK fail.\n"); + return; + } + if (DEBUG) printf("[DEBUG] Ignored.\n"); + } + break; + + default: + break; + } +}