Importation du code depuis GitHub

This commit is contained in:
gasc 2021-08-22 13:24:38 +02:00
commit 9f3a45b8b2
2 changed files with 597 additions and 0 deletions

61
README.md Normal file
View file

@ -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).
```

536
mictcp.c Normal file
View file

@ -0,0 +1,536 @@
#include <mictcp.h>
#include <api/mictcp_core.h>
/*
TP BE Réseaux
À L'ATTENTION DU CORRECTEUR :
- Ce fichier est le seul fichier que j'ai modifié
- La v1 a é implémenté et fonctionne avec tsock_texte et
tsock_video.
- La v2 a é 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 lapplication 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 dattribuer 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 dune 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 lenvoi dune 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 à lapplication réceptrice de réclamer la récupération dune donnée
* stockée dans les buffers de réception du socket
* Retourne le nombre doctets lu ou bien -1 en cas derreur
* 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 dun 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 dun 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;
}
}