diff --git a/.gitignore b/.gitignore index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..be60ba4ba10b667314730f52599a8b87bd5e00c5 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +.vscode/ \ No newline at end of file diff --git a/Makefile b/Makefile index 1aa601e7f92667314805137327237211aa6d1ea7..d819753b4a3bd627db7dcf8bd5e9a76fe315da6a 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,10 @@ # You can use clang if you prefer CC = gcc - -# Feel free to add other C flags CFLAGS += -c -std=gnu99 -Wall -Werror -Wextra -O2 -# By default, we colorize the output, but this might be ugly in log files, so feel free to remove the following line. CFLAGS += -D_COLOR -# You may want to add something here -LDFLAGS += +HEADERS_DIR = -Iheaders +LDFLAGS = -lz # Adapt these as you want to fit with your project SENDER_SOURCES = $(wildcard src/sender.c src/log.c) @@ -19,7 +16,7 @@ RECEIVER_OBJECTS = $(RECEIVER_SOURCES:.c=.o) SENDER = sender RECEIVER = receiver -all: $(SENDER) $(RECEIVER) +all: debug $(SENDER) $(RECEIVER) $(SENDER): $(SENDER_OBJECTS) $(CC) $(SENDER_OBJECTS) -o $@ $(LDFLAGS) @@ -28,11 +25,11 @@ $(RECEIVER): $(RECEIVER_OBJECTS) $(CC) $(RECEIVER_OBJECTS) -o $@ $(LDFLAGS) %.o: %.c - $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + $(CC) $(HEADERS_DIR) $(CFLAGS) $< -o $@ $(LDFLAGS) .PHONY: clean mrproper -clean: +clean: mrproper rm -f $(SENDER_OBJECTS) $(RECEIVER_OBJECTS) mrproper: diff --git a/README.md b/README.md index afcd6691d8f0f55fd60f8f1eeee85a3a922456e5..cf89af80a13e9df43202bebfa7016693ab52106b 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ -# My Wonderful LINFO1341 Project +# Implementation of TRTP Protocol - LINFO1341 Project -The very first thing you might want to do in this folder is the following command: -```bash -git init -``` +## Introduction -This will initialize your Git repository. -You should also put it in a **private** repository (GitHub, GitLab, Bitbucket,... it is up to you but it **has to** stay private). +For a 3rd year bachelors Computer Networks given to computer science students at UCLouvain, we had to implement a transport protocol. The protocol is entitled Truncated Relieable Transport Protocol (TRTP)on top of a Ipv6 Network layer. The implementation also implement the Forward Erasure Correction (FEC) to recover corrupted packets. The full description is to be found in the `statement.pdf`. -The Makefile contains all the required targets, but you might want to extend their behavior. +## Folder Organization -Very basic skelettons of receiver and sender source files are present, have a look to understand how you can enable logging or not. +``` +| `headers\` +| `src\` +| `tests\` +``` -A very simple test case is present, you probably want to update it. +## Execution -You might be interested in the link simulator that can be found at https://github.com/cnp3/Linksimulator +## Testing -And finally, if this message is still there at your final submission, it looks like you forgot to provide a proper README. \ No newline at end of file +## System Architecture \ No newline at end of file diff --git a/src/log.h b/headers/log.h similarity index 100% rename from src/log.h rename to headers/log.h diff --git a/headers/packet_interface.h b/headers/packet_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..37b5c9c087dc731f8f8b572974b0542cd8aadc0f --- /dev/null +++ b/headers/packet_interface.h @@ -0,0 +1,182 @@ +/** + * @file packet_interface.h + * @brief This header contains structs and definitions needed in order to encode and decode TRTP packets + */ + +#ifndef __PACKET_INTERFACE_H_ +#define __PACKET_INTERFACE_H_ + +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintx_t */ +#include <stdio.h> /* ssize_t */ + +/* Raccourci pour struct pkt */ +typedef struct pkt pkt_t; + +/* Types de paquets */ +typedef enum { + PTYPE_DATA = 1, + PTYPE_ACK = 2, + PTYPE_NACK = 3, +} ptypes_t; + +/* Taille maximale permise pour le payload */ +#define MAX_PAYLOAD_SIZE 512 +/* Taille maximale de Window */ +#define MAX_WINDOW_SIZE 31 + +/* Valeur de retours des fonctions */ +typedef enum { + PKT_OK = 0, /* Le paquet a ete traite avec succes */ + E_TYPE, /* Erreur liee au champs Type */ + E_TR, /* Erreur liee au champ TR */ + E_LENGTH, /* Erreur liee au champs Length */ + E_CRC, /* CRC invalide */ + E_WINDOW, /* Erreur liee au champs Window */ + E_SEQNUM, /* Numero de sequence invalide */ + E_NOMEM, /* Pas assez de memoire */ + E_NOHEADER, /* Le paquet n'a pas de header (trop court) */ + E_UNCONSISTENT, /* Le paquet est incoherent */ +} pkt_status_code; + +/* Alloue et initialise une struct pkt + * @return: NULL en cas d'erreur */ +pkt_t* pkt_new(); +/* Libere le pointeur vers la struct pkt, ainsi que toutes les + * ressources associees + */ +void pkt_del(pkt_t*); + +/* + * Decode des donnees recues et cree une nouvelle structure pkt. + * Le paquet recu est en network byte-order. + * La fonction verifie que: + * - Le CRC32 du header recu est le même que celui decode a la fin + * du header (en considerant le champ TR a 0) + * - S'il est present, le CRC32 du payload recu est le meme que celui + * decode a la fin du payload + * - Le type du paquet est valide + * - La longueur du paquet et le champ TR sont valides et coherents + * avec le nombre d'octets recus. + * + * @data: L'ensemble d'octets constituant le paquet recu + * @len: Le nombre de bytes recus + * @pkt: Une struct pkt valide + * @post: pkt est la representation du paquet recu + * + * @return: Un code indiquant si l'operation a reussi ou representant + * l'erreur rencontree. + */ +pkt_status_code pkt_decode(const char *data, const size_t len, pkt_t *pkt); + +/* + * Encode une struct pkt dans un buffer, prêt a être envoye sur le reseau + * (c-a-d en network byte-order), incluant le CRC32 du header et + * eventuellement le CRC32 du payload si celui-ci est non nul. + * + * @pkt: La structure a encoder + * @buf: Le buffer dans lequel la structure sera encodee + * @len: La taille disponible dans le buffer + * @len-POST: Le nombre de d'octets ecrit dans le buffer + * @return: Un code indiquant si l'operation a reussi ou E_NOMEM si + * le buffer est trop petit. + */ +pkt_status_code pkt_encode(const pkt_t*, char *buf, size_t *len); + +/* Accesseurs pour les champs toujours presents du paquet. + * Les valeurs renvoyees sont toutes dans l'endianness native + * de la machine! + */ +ptypes_t pkt_get_type (const pkt_t*); +uint8_t pkt_get_tr (const pkt_t*); +uint8_t pkt_get_window (const pkt_t*); +uint8_t pkt_get_seqnum (const pkt_t*); +uint16_t pkt_get_length (const pkt_t*); +uint32_t pkt_get_timestamp(const pkt_t*); +uint32_t pkt_get_crc1 (const pkt_t*); +/* Renvoie un pointeur vers le payload du paquet, ou NULL s'il n'y + * en a pas. + */ +const char* pkt_get_payload(const pkt_t*); +/* Renvoie le CRC2 dans l'endianness native de la machine. Si + * ce field n'est pas present, retourne 0. + */ +uint32_t pkt_get_crc2(const pkt_t*); + +/* Setters pour les champs obligatoires du paquet. Si les valeurs + * fournies ne sont pas dans les limites acceptables, les fonctions + * doivent renvoyer un code d'erreur adapte. + * Les valeurs fournies sont dans l'endianness native de la machine! + */ +pkt_status_code pkt_set_type (pkt_t*, const ptypes_t type); +pkt_status_code pkt_set_tr (pkt_t*, const uint8_t tr); +pkt_status_code pkt_set_window (pkt_t*, const uint8_t window); +pkt_status_code pkt_set_seqnum (pkt_t*, const uint8_t seqnum); +pkt_status_code pkt_set_length (pkt_t*, const uint16_t length); +pkt_status_code pkt_set_timestamp(pkt_t*, const uint32_t timestamp); +pkt_status_code pkt_set_crc1 (pkt_t*, const uint32_t crc1); +/* Defini la valeur du champs payload du paquet. + * @data: Une succession d'octets representants le payload + * @length: Le nombre d'octets composant le payload + * @POST: pkt_get_length(pkt) == length */ +pkt_status_code pkt_set_payload(pkt_t*, + const char *data, + const uint16_t length); +/* Setter pour CRC2. Les valeurs fournies sont dans l'endianness + * native de la machine! + */ +pkt_status_code pkt_set_crc2(pkt_t*, const uint32_t crc2); + + +/* + * Decode un varuint (entier non signe de taille variable dont le premier bit indique la longueur) + * encode en network byte-order dans le buffer data disposant d'une taille maximale len. + * @post: place à l'adresse retval la valeur en host byte-order de l'entier de taille variable stocke + * dans data si aucune erreur ne s'est produite + * @return: + * + * -1 si data ne contient pas un varuint valide (la taille du varint + * est trop grande par rapport à la place disponible dans data) + * + * le nombre de bytes utilises si aucune erreur ne s'est produite + */ +ssize_t varuint_decode(const uint8_t *data, const size_t len, uint16_t *retval); + + +/* + * Encode un varuint en network byte-order dans le buffer data disposant d'une + * taille maximale len. + * @pre: val < 0x8000 (val peut être encode en varuint) + * @return: + * -1 si data ne contient pas une taille suffisante pour encoder le varuint + * + * la taille necessaire pour encoder le varuint (1 ou 2 bytes) si aucune erreur ne s'est produite + */ +ssize_t varuint_encode(uint16_t val, uint8_t *data, const size_t len); + + +/* + * @pre: data pointe vers un buffer d'au moins 1 byte + * @return: la taille en bytes du varuint stocke dans data, soit 1 ou 2 bytes. + */ +size_t varuint_len(const uint8_t *data); + + +/* + * @return: la taille en bytes que prendra la valeur val + * une fois encodee en varuint si val contient une valeur varuint valide (val < 0x8000). + -1 si val ne contient pas une valeur varuint valide + */ +ssize_t varuint_predict_len(uint16_t val); + + +/* + * Retourne la longueur du header en bytes si le champs pkt->length + * a une valeur valide pour un champs de type varuint (i.e. pkt->length < 0x8000). + * Retourne -1 sinon + * @pre: pkt contient une adresse valide (!= NULL) + */ +ssize_t predict_header_length(const pkt_t *pkt); + + +#endif /* __PACKET_INTERFACE_H_ */ \ No newline at end of file diff --git a/headers/receiver.h b/headers/receiver.h new file mode 100644 index 0000000000000000000000000000000000000000..c1fcb39dc5a5e3a8f6b3b68c0b849b86fbd58fdc --- /dev/null +++ b/headers/receiver.h @@ -0,0 +1,4 @@ +/** + * @file receiver.h + * @brief This header contains all the structures and functions definitions that ae going to be used by the receiver.c + */ \ No newline at end of file diff --git a/headers/sender.h b/headers/sender.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/packet_interface.c b/src/packet_interface.c new file mode 100644 index 0000000000000000000000000000000000000000..d119a7c67ffae6ffed81dbf24b85a9384e8affe2 --- /dev/null +++ b/src/packet_interface.c @@ -0,0 +1,208 @@ +#include "packet_interface.h" + +#include <arpa/inet.h> +#include <zlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +/*************** Structures definition ***************/ +struct __attribute__((__packed__)) pkt { + header_t header; + uint8_t payload[512]; +}; + +typedef uint8_t header_t[32]; + +/************* Functions definition *************/ +pkt_t* pkt_new() +{ + pkt_t * toReturn = (pkt_t *) malloc(sizeof(pkt_t)); + return toReturn; +} + +void pkt_del(pkt_t *pkt) +{ + free(pkt); +} + +/** + * @brief Calculates the CRC32 + * + * @param buffer : The buffer on which to calculate CRC32. + * @param len : The number of bytes of the buffer. + * @return uint32_t + */ +uint32_t calculate_crc(char * buffer, uint32_t len) +{ + return (uint32_t) crc32(0, (const void *) buffer, len); +} + +pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *pkt) +{ + +} + +pkt_status_code pkt_decode_ack_nack(const char *data, const size_t len, pkt_t *pkt) +{ + if (len < 8) + return E_NOHEADER; + + // REMINDER : *data will read the first byte + ptypes_t type = ((*data) & 0xC0) >> 6; + if ( type != PTYPE_ACK && type != PTYPE_NACK ) + return E_TYPE; + + if ( ((*data) & 0x20) != 0) + return E_TR; + + uint8_t seqnum, window; + memcpy((char *) &seqnum, data+1, 1); + memcpy((char *) &window, data, 1); + window = window & 0x1F; + if (seqnum > window) + return E_SEQNUM; + + // CheckSum no need to set TR at 0 as it should be at 0 + char crc[4]; + memcpy(crc, data+4, 4); + if (calculate_crc(data, 4) != ntohl((uint32_t) crc)) + return E_CRC; +} + +pkt_status_code pkt_decode(const char *data, const size_t len, pkt_t *pkt) +{ + int is_ack_nack = len <= 8; // Maximum ACK or NACK size is 12 bytes + if (is_ack_nack) + return pkt_decode_ack_nack(data, len, pkt); + else + return pkt_decode_data_fec(data, len, pkt); +} + +pkt_status_code pkt_encode(const pkt_t* pkt, char *buf, size_t *len) +{ + /* Your code will be inserted here */ +} + +ptypes_t pkt_get_type (const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +uint8_t pkt_get_tr(const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +uint8_t pkt_get_window(const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +uint8_t pkt_get_seqnum(const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +uint16_t pkt_get_length(const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +uint32_t pkt_get_timestamp (const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +uint32_t pkt_get_crc1 (const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +uint32_t pkt_get_crc2 (const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + +const char* pkt_get_payload(const pkt_t* pkt) +{ + /* Your code will be inserted here */ +} + + +pkt_status_code pkt_set_type(pkt_t *pkt, const ptypes_t type) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_tr(pkt_t *pkt, const uint8_t tr) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_window(pkt_t *pkt, const uint8_t window) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_seqnum(pkt_t *pkt, const uint8_t seqnum) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_length(pkt_t *pkt, const uint16_t length) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_timestamp(pkt_t *pkt, const uint32_t timestamp) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_crc1(pkt_t *pkt, const uint32_t crc1) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_crc2(pkt_t *pkt, const uint32_t crc2) +{ + /* Your code will be inserted here */ +} + +pkt_status_code pkt_set_payload(pkt_t *pkt, + const char *data, + const uint16_t length) +{ + /* Your code will be inserted here */ +} + + +ssize_t varuint_decode(const uint8_t *data, const size_t len, uint16_t *retval) +{ + /* Your code will be inserted here */ +} + + +ssize_t varuint_encode(uint16_t val, uint8_t *data, const size_t len) +{ + /* Your code will be inserted here */ +} + +size_t varuint_len(const uint8_t *data) +{ + /* Your code will be inserted here */ +} + + +ssize_t varuint_predict_len(uint16_t val) +{ + /* Your code will be inserted here */ +} + + +ssize_t predict_header_length(const pkt_t *pkt) +{ + /* Your code will be inserted here */ +} \ No newline at end of file diff --git a/statement.pdf b/statement.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ad289f5ca0f4a03278573b7632151b1b1897d12e Binary files /dev/null and b/statement.pdf differ diff --git a/test b/test new file mode 100755 index 0000000000000000000000000000000000000000..6a4d932ce38e508374b215828ab9d9780ddde912 Binary files /dev/null and b/test differ diff --git a/test.c b/test.c new file mode 100644 index 0000000000000000000000000000000000000000..5a5644f804aa58e9c9e69ee54797622c6f64bc16 --- /dev/null +++ b/test.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <zlib.h> + +int main() { + char * arr = "123456789"; + printf("%ld\n", crc32(0, (const void *) arr, 9)); + return 0; +} + \ No newline at end of file diff --git a/test_files/dummy.txt b/test_files/dummy.txt new file mode 100644 index 0000000000000000000000000000000000000000..3af24e47a473fb2b4392ba53dec1e1ae0212df9d --- /dev/null +++ b/test_files/dummy.txt @@ -0,0 +1 @@ +Hello Receiver I'm a Friendly Sender ! \ No newline at end of file