From f2c1786168b7c2f6ffc5ddb78ba519e4f744ff4a Mon Sep 17 00:00:00 2001 From: keplyx Date: Mon, 4 May 2020 10:59:32 +0200 Subject: [PATCH] First commit --- CMakeLists.txt | 22 +++ Makefile | 65 +++++++ clean_proc.sh | 5 + include/api/mictcp_core.h | 59 ++++++ include/mictcp.h | 107 +++++++++++ src/api/mictcp_core.c | 347 +++++++++++++++++++++++++++++++++++ src/apps/client.c | 46 +++++ src/apps/gateway.c | 371 ++++++++++++++++++++++++++++++++++++++ src/apps/server.c | 55 ++++++ src/mictcp.c | 148 +++++++++++++++ tsock_texte | 40 ++++ tsock_video | 63 +++++++ 12 files changed, 1328 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Makefile create mode 100755 clean_proc.sh create mode 100644 include/api/mictcp_core.h create mode 100644 include/mictcp.h create mode 100644 src/api/mictcp_core.c create mode 100644 src/apps/client.c create mode 100644 src/apps/gateway.c create mode 100644 src/apps/server.c create mode 100644 src/mictcp.c create mode 100755 tsock_texte create mode 100755 tsock_video diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..207bf91 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.15) +project(TP C) + +set(CMAKE_C_STANDARD 99) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +include_directories(include) + +set(HEADER_FILES include/mictcp.h include/api/mictcp_core.h) + +add_library(mictcp src/mictcp.c include/mictcp.h) +add_library(mictcp_core src/api/mictcp_core.c include/api/mictcp_core.h) + +add_executable(client src/apps/client.c) +add_executable(gateway src/apps/gateway.c) +add_executable(server src/apps/server.c) + +target_link_libraries(client Threads::Threads) +target_link_libraries(gateway Threads::Threads) +target_link_libraries(server Threads::Threads) \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e8a02cb --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +DATE := `date +'%Y%m%d'` +TAG := moodle-$(USER) +PORT := $(shell expr `id -u` % 2000 + 12487) +PORT2 := $(shell expr $(PORT) + 1) + +ifneq ($(tag),) + TAG := $(TAG)-$(tag) +endif + +CC := gcc +LD := gcc + +TAR_FILENAME := $(DATE)-mictcp-$(TAG).tar.gz + +MODULES := api apps +SRC_DIR := $(addprefix src/,$(MODULES)) src +BUILD_DIR := $(addprefix build/,$(MODULES)) build + +SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) +OBJ := $(patsubst src/%.c,build/%.o,$(SRC)) +OBJ_CLI := $(patsubst build/apps/gateway.o,,$(patsubst build/apps/server.o,,$(OBJ))) +OBJ_SERV := $(patsubst build/apps/gateway.o,,$(patsubst build/apps/client.o,,$(OBJ))) +OBJ_GWAY := $(patsubst build/apps/server.o,,$(patsubst build/apps/client.o,,$(OBJ))) +INCLUDES := include + +vpath %.c $(SRC_DIR) + +define make-goal +$1/%.o: %.c + $(CC) -DAPI_CS_Port=$(PORT) -DAPI_SC_Port=$(PORT2) -std=gnu99 -Wall -g -I $(INCLUDES) -c $$< -o $$@ +endef + +.PHONY: all checkdirs clean + +all: checkdirs build/client build/server build/gateway + +build/client: $(OBJ_CLI) + $(LD) $^ -o $@ -lm -lpthread + +build/server: $(OBJ_SERV) + $(LD) $^ -o $@ -lm -lpthread + +build/gateway: $(OBJ_GWAY) + $(LD) $^ -o $@ -lm -lpthread + +checkdirs: $(BUILD_DIR) + +$(BUILD_DIR): + @mkdir -p $@ + +clean: + @rm -rf $(BUILD_DIR) + +distclean: + @rm -rf $(BUILD_DIR) + @-rm -f *.tar.gz || true + + +$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir)))) + +dist: + @tar --exclude=build --exclude=*tar.gz --exclude=.git* -czvf mictcp-bundle.tar.gz ../mictcp + +prof: + @tar --exclude=build --exclude=.*tar.gz --exclude=video -czvf $(TAR_FILENAME) ../mictcp diff --git a/clean_proc.sh b/clean_proc.sh new file mode 100755 index 0000000..48b2d85 --- /dev/null +++ b/clean_proc.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +killall -9 cvlc > /dev/null 2>&1 +killall -9 vlc > /dev/null 2>&1 +killall -9 gateway > /dev/null 2>&1 \ No newline at end of file diff --git a/include/api/mictcp_core.h b/include/api/mictcp_core.h new file mode 100644 index 0000000..0cccc6f --- /dev/null +++ b/include/api/mictcp_core.h @@ -0,0 +1,59 @@ +#ifndef MICTCP_CORE_H +#define MICTCP_CORE_H + +#include +#include + +/* ************************************************************* + * Public core functions, can be used for implementing mictcp * + * *************************************************************/ + +int initialize_components(start_mode sm); + +int IP_send(mic_tcp_pdu, mic_tcp_sock_addr); + +int IP_recv(mic_tcp_pdu *, mic_tcp_sock_addr *, unsigned long timeout); + +int app_buffer_get(mic_tcp_payload); + +void app_buffer_put(mic_tcp_payload); + +void set_loss_rate(unsigned short); + +unsigned long get_now_time_msec(); + +unsigned long get_now_time_usec(); + +/* ********************************************************************* + * Private core functions, should not be used for implementing mictcp * + * *********************************************************************/ +#ifndef API_CS_Port +#define API_CS_Port 8524 +#endif +#ifndef API_SC_Port +#define API_SC_Port 8525 +#endif +#define API_HD_Size 15 + +typedef struct ip_payload { + char *data; /** données transport */ + int size; /** taille des données */ +} ip_payload; + +int mic_tcp_core_send(mic_tcp_payload); + +mic_tcp_payload get_full_stream(mic_tcp_pdu); + +mic_tcp_payload get_mic_tcp_data(ip_payload); + +mic_tcp_header get_mic_tcp_header(ip_payload); + +void *listening(void *); + +void print_header(mic_tcp_pdu); + +int min_size(int, int); + +float mod(int, float); + +#endif diff --git a/include/mictcp.h b/include/mictcp.h new file mode 100644 index 0000000..74f3adf --- /dev/null +++ b/include/mictcp.h @@ -0,0 +1,107 @@ +#ifndef MICTCP_H +#define MICTCP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Etats du protocole (les noms des états sont donnés à titre indicatif + * et peuvent être modifiés) + */ +typedef enum protocol_state { + IDLE, CLOSED, SYN_SENT, SYN_RECEIVED, ESTABLISHED, CLOSING +} protocol_state; + +/** + * Mode de démarrage du protocole + * NB : nécessaire à l’usage de la fonction initialize_components() + */ +typedef enum start_mode { + CLIENT, SERVER +} start_mode; + +/** + * Structure d’une adresse de socket + */ +typedef struct mic_tcp_sock_addr { + char *ip_addr; + int ip_addr_size; + unsigned short port; +} mic_tcp_sock_addr; + +/** + * Structure d'un socket + */ +typedef struct mic_tcp_sock { + int fd; /** descripteur du socket */ + protocol_state state; /** état du protocole */ + mic_tcp_sock_addr addr; /** adresse du socket */ +} mic_tcp_sock; + +/** + * Structure des données utiles d’un PDU MIC-TCP + */ +typedef struct mic_tcp_payload { + char *data; /** données applicatives */ + int size; /** taille des données */ +} mic_tcp_payload; + +/** + * Structure de l'entête d'un PDU MIC-TCP + */ +typedef struct mic_tcp_header { + unsigned short source_port; /** numéro de port source */ + unsigned short dest_port; /** numéro de port de destination */ + unsigned int seq_num; /** numéro de séquence */ + unsigned int ack_num; /** numéro d'acquittement */ + unsigned char syn; /** flag SYN (valeur 1 si activé et 0 si non) */ + unsigned char ack; /** flag ACK (valeur 1 si activé et 0 si non) */ + unsigned char fin; /** flag FIN (valeur 1 si activé et 0 si non) */ +} mic_tcp_header; + +/** + * Structure d'un PDU MIC-TCP + */ +typedef struct mic_tcp_pdu { + mic_tcp_header header; /** entête du PDU */ + mic_tcp_payload payload; /** charge utile du PDU */ +} mic_tcp_pdu; + +typedef struct app_buffer { + mic_tcp_payload packet; + struct app_buffer *next; + unsigned short id; +} app_buffer; + + +/* *************************** + * Fonctions de l'interface * + * ***************************/ +int mic_tcp_socket(start_mode sm); + +int mic_tcp_bind(int socket, mic_tcp_sock_addr addr); + +int mic_tcp_accept(int socket, mic_tcp_sock_addr *addr); + +int mic_tcp_connect(int socket, mic_tcp_sock_addr addr); + +int mic_tcp_send(int socket, char *mesg, int mesg_size); + +int mic_tcp_recv(int socket, char *mesg, int max_mesg_size); + +void process_received_PDU(mic_tcp_pdu pdu, mic_tcp_sock_addr addr); + +int mic_tcp_close(int socket); + +#endif diff --git a/src/api/mictcp_core.c b/src/api/mictcp_core.c new file mode 100644 index 0000000..692d033 --- /dev/null +++ b/src/api/mictcp_core.c @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include +#include +#include + +/* **************** + * API Variables * + * ****************/ +int initialized = -1; +int sys_socket; +pthread_t listen_th; +pthread_mutex_t lock; +unsigned short loss_rate = 0; +struct sockaddr_in remote_addr; + +/* This is for the buffer */ +TAILQ_HEAD(tailhead, app_buffer_entry) app_buffer_head; +struct tailhead *headp; +struct app_buffer_entry { + mic_tcp_payload bf; + TAILQ_ENTRY(app_buffer_entry) entries; +}; + +/* Condition variable used for passive wait when buffer is empty */ +pthread_cond_t buffer_empty_cond; + +/* ************************ + * Fonctions Utilitaires * + * ************************/ +int initialize_components(start_mode mode) +{ + int bnd; + struct hostent * hp; + struct sockaddr_in local_addr; + + if(initialized != -1) return initialized; + if((sys_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return -1; + else initialized = 1; + + if((mode == SERVER) & (initialized != -1)) + { + TAILQ_INIT(&app_buffer_head); + pthread_cond_init(&buffer_empty_cond, 0); + memset((char *) &local_addr, 0, sizeof(local_addr)); + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(API_CS_Port); + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + bnd = bind(sys_socket, (struct sockaddr *) &local_addr, sizeof(local_addr)); + + if (bnd == -1) + { + initialized = -1; + } + else + { + memset((char *) &remote_addr, 0, sizeof(remote_addr)); + remote_addr.sin_family = AF_INET; + remote_addr.sin_port = htons(API_SC_Port); + hp = gethostbyname("localhost"); + memcpy (&(remote_addr.sin_addr.s_addr), hp->h_addr, hp->h_length); + initialized = 1; + } + + + } + else + { + if(initialized != -1) + { + memset((char *) &remote_addr, 0, sizeof(remote_addr)); + remote_addr.sin_family = AF_INET; + remote_addr.sin_port = htons(API_CS_Port); + hp = gethostbyname("localhost"); + memcpy (&(remote_addr.sin_addr.s_addr), hp->h_addr, hp->h_length); + + memset((char *) &local_addr, 0, sizeof(local_addr)); + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(API_SC_Port); + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + bnd = bind(sys_socket, (struct sockaddr *) &local_addr, sizeof(local_addr)); + } + } + + if((initialized == 1) && (mode == SERVER)) + { + pthread_create (&listen_th, NULL, listening, "1"); + } + + return initialized; +} + + + +int IP_send(mic_tcp_pdu pk, mic_tcp_sock_addr addr) +{ + + int result = 0; + + if(initialized == -1) { + result = -1; + + } else { + mic_tcp_payload tmp = get_full_stream(pk); + int sent_size = mic_tcp_core_send(tmp); + + free (tmp.data); + + /* Correct the sent size */ + result = (sent_size == -1) ? -1 : sent_size - API_HD_Size; + } + + return result; +} + +int IP_recv(mic_tcp_pdu* pk, mic_tcp_sock_addr* addr, unsigned long timeout) +{ + int result = -1; + + struct timeval tv; + struct sockaddr_in tmp_addr; + socklen_t tmp_addr_size = sizeof(struct sockaddr); + + /* Send data over a fake IP */ + if(initialized == -1) { + return -1; + } + + /* Compute the number of entire seconds */ + tv.tv_sec = timeout / 1000; + /* Convert the remainder to microseconds */ + tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000; + + /* Create a reception buffer */ + int buffer_size = API_HD_Size + pk->payload.size; + char *buffer = malloc(buffer_size); + + if ((setsockopt(sys_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) >= 0) { + result = recvfrom(sys_socket, buffer, buffer_size, 0, (struct sockaddr *)&tmp_addr, &tmp_addr_size); + } + + if (result != -1) { + /* Create the mic_tcp_pdu */ + memcpy (&(pk->header), buffer, API_HD_Size); + pk->payload.size = result - API_HD_Size; + memcpy (pk->payload.data, buffer + API_HD_Size, pk->payload.size); + + /* Generate a stub address */ + if (addr != NULL) { + addr->ip_addr = "localhost"; + addr->ip_addr_size = strlen(addr->ip_addr) + 1; // don't forget '\0' + addr->port = pk->header.source_port; + } + + /* Correct the receved size */ + result -= API_HD_Size; + } + + /* Free the reception buffer */ + free(buffer); + + return result; +} + +mic_tcp_payload get_full_stream(mic_tcp_pdu pk) +{ + /* Get a full packet from data and header */ + mic_tcp_payload tmp; + tmp.size = API_HD_Size + pk.payload.size; + tmp.data = malloc (tmp.size); + + memcpy (tmp.data, &pk.header, API_HD_Size); + memcpy (tmp.data + API_HD_Size, pk.payload.data, pk.payload.size); + + return tmp; +} + +mic_tcp_payload get_mic_tcp_data(ip_payload buff) +{ + mic_tcp_payload tmp; + tmp.size = buff.size-API_HD_Size; + tmp.data = malloc(tmp.size); + memcpy(tmp.data, buff.data+API_HD_Size, tmp.size); + return tmp; +} + + +mic_tcp_header get_mic_tcp_header(ip_payload packet) +{ + /* Get a struct header from an incoming packet */ + mic_tcp_header tmp; + memcpy(&tmp, packet.data, API_HD_Size); + return tmp; +} + +int full_send(mic_tcp_payload buff) +{ + int result = 0; + + result = sendto(sys_socket, buff.data, buff.size, 0, (struct sockaddr *)&remote_addr, sizeof(remote_addr)); + + return result; +} + +int mic_tcp_core_send(mic_tcp_payload buff) +{ + int random = rand(); + int result = buff.size; + int lr_tresh = (int) round(((float)loss_rate/100.0)*RAND_MAX); + + if(random > lr_tresh) { + result = sendto(sys_socket, buff.data, buff.size, 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)); + } else { + printf("[MICTCP-CORE] Perte du paquet\n"); + } + + return result; +} + +int app_buffer_get(mic_tcp_payload app_buff) +{ + /* A pointer to a buffer entry */ + struct app_buffer_entry * entry; + + /* The actual size passed to the application */ + int result = 0; + + /* Lock a mutex to protect the buffer from corruption */ + pthread_mutex_lock(&lock); + + /* If the buffer is empty, we wait for insertion */ + while(app_buffer_head.tqh_first == NULL) { + pthread_cond_wait(&buffer_empty_cond, &lock); + } + + /* When we execute the code below, the following conditions are true: + - The buffer contains at least 1 element + - We hold the lock on the mutex + */ + + /* The entry we want is the first one in the buffer */ + entry = app_buffer_head.tqh_first; + + /* How much data are we going to deliver to the application ? */ + result = min_size(entry->bf.size, app_buff.size); + + /* We copy the actual data in the application allocated buffer */ + memcpy(app_buff.data, entry->bf.data, result); + + /* We remove the entry from the buffer */ + TAILQ_REMOVE(&app_buffer_head, entry, entries); + + /* Release the mutex */ + pthread_mutex_unlock(&lock); + + /* Clean up memory */ + free(entry->bf.data); + free(entry); + + return result; +} + +void app_buffer_put(mic_tcp_payload bf) +{ + /* Prepare a buffer entry to store the data */ + struct app_buffer_entry * entry = malloc(sizeof(struct app_buffer_entry)); + entry->bf.size = bf.size; + entry->bf.data = malloc(bf.size); + memcpy(entry->bf.data, bf.data, bf.size); + + /* Lock a mutex to protect the buffer from corruption */ + pthread_mutex_lock(&lock); + + /* Insert the packet in the buffer, at the end of it */ + TAILQ_INSERT_TAIL(&app_buffer_head, entry, entries); + + /* Release the mutex */ + pthread_mutex_unlock(&lock); + + /* We can now signal to any potential thread waiting that the buffer is + no longer empty */ + pthread_cond_broadcast(&buffer_empty_cond); +} + + + +void* listening(void* arg) +{ + mic_tcp_pdu pdu_tmp; + int recv_size; + mic_tcp_sock_addr remote; + + pthread_mutex_init(&lock, NULL); + + printf("[MICTCP-CORE] Demarrage du thread de reception reseau...\n"); + + const int payload_size = 1500 - API_HD_Size; + pdu_tmp.payload.size = payload_size; + pdu_tmp.payload.data = malloc(payload_size); + + + while(1) + { + pdu_tmp.payload.size = payload_size; + recv_size = IP_recv(&pdu_tmp, &remote, 0); + + if(recv_size != -1) + { + process_received_PDU(pdu_tmp, remote); + } else { + /* This should never happen */ + printf("Error in recv\n"); + } + } +} + + +void set_loss_rate(unsigned short rate) +{ + loss_rate = rate; +} + +void print_header(mic_tcp_pdu bf) +{ + mic_tcp_header hd = bf.header; + printf("\nSP: %d, DP: %d, SEQ: %d, ACK: %d", hd.source_port, hd.dest_port, hd.seq_num, hd.ack_num); +} + +unsigned long get_now_time_msec() +{ + return ((unsigned long) (get_now_time_usec() / 1000)); +} + +unsigned long get_now_time_usec() +{ + struct timespec now_time; + clock_gettime( CLOCK_REALTIME, &now_time); + return ((unsigned long)((now_time.tv_nsec / 1000) + (now_time.tv_sec * 1000000))); +} + +int min_size(int s1, int s2) +{ + if(s1 <= s2) return s1; + return s2; +} diff --git a/src/apps/client.c b/src/apps/client.c new file mode 100644 index 0000000..4e51c0e --- /dev/null +++ b/src/apps/client.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#define MAX_SIZE 1000 + +#define COLOR_RED "\x1b[31m" +#define COLOR_GREEN "\x1b[32m" +#define COLOR_BOLD "\x1b[1m" +#define COLOR_RESET "\x1b[0m" + +int main() { + + int sockfd = 0; + char chaine[MAX_SIZE]; + mic_tcp_sock_addr addr; + addr.ip_addr = "127.0.0.1"; + addr.port = 1234; + + if ((sockfd = mic_tcp_socket(CLIENT)) == -1) { + printf("[TSOCK] Creation du socket MICTCP: "COLOR_RED"ERREUR"COLOR_RESET"\n"); + return 1; + } else { + printf("[TSOCK] Creation du socket MICTCP: "COLOR_GREEN"OK"COLOR_RESET"\n"); + } + + if (mic_tcp_connect(sockfd, addr) == -1) { + printf("[TSOCK] Connexion du socket MICTCP: "COLOR_RED"ERREUR"COLOR_RESET"\n"); + return 1; + } else { + printf("[TSOCK] Connexion du socket MICTCP: "COLOR_GREEN"OK"COLOR_RESET"\n"); + } + + memset(chaine, 0, MAX_SIZE); + + printf(COLOR_BOLD"[TSOCK] Entrez vos message a envoyer, CTRL+D pour quitter"COLOR_RESET"\n"); + while (fgets(chaine, MAX_SIZE, stdin) != NULL) { + int sent_size = mic_tcp_send(sockfd, chaine, strlen(chaine) + 1); + printf("[TSOCK] Appel de mic_send avec un message de taille : %lu\n", strlen(chaine) + 1); + printf("[TSOCK] Appel de mic_send valeur de retour : %d\n", sent_size); + } + + mic_tcp_close(sockfd); + + return 0; +} diff --git a/src/apps/gateway.c b/src/apps/gateway.c new file mode 100644 index 0000000..914dd1f --- /dev/null +++ b/src/apps/gateway.c @@ -0,0 +1,371 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Déclaration des types, constantes et macros +// + +#define ENABLE_TCP_LOSS 1 +#define MAX_UDP_SEGMENT_SIZE 1480 +#define MICTCP_PORT 1337 +#define VIDEO_FILE "../video/video.bin" + +/** + * Macro utilisée pour afficher le message d'erreur msg passé en paramètre + * si la condition cond est validée, puis arrêter le programme. + * Le message affiché contient des informations supplémentaires concernant + * le fichier de provenance, la fonction et le numéro de ligne concerné. + * Si errno est set, le message d'erreur associé est aussi affiché. + */ +#define ERROR_IF(cond,msg) \ + if (cond) { \ + if (errno != 0) { \ + fprintf(stderr, "%s:%d [%s()] -> %s (%s)\n", \ + __FILE__, __LINE__, __func__, msg, strerror(errno)); \ + } else { \ + fprintf(stderr, "%s:%d [%s()] -> %s\n", \ + __FILE__, __LINE__, __func__, msg); \ + } \ + exit(EXIT_FAILURE); \ + } + +/** + * Fonctions du programme + */ +enum gateway_function { + UND_FCT, + SOURCE, + PUITS +}; + +/** + * Protocoles pouvant être utilisé + */ +enum gateway_protocol { + PROTO_TCP, + PROTO_MICTCP +}; + +// +// Déclaration des fonctions locales +// + +static void file_to_faketcp(char* filename, char *host, int port); +static void file_to_mictcp(char* filename); +static void mictcp_to_udp(char *host, int port); +static int read_rtp_packet(FILE *fd, struct timespec *timestamp, char *buffer, int buffer_size); +static struct timespec tsSubtract(struct timespec time1, struct timespec time2); +static void usage(void); + +// +// Corps des fonctions publiques +// + +int main(int argc, char** argv) +{ + enum gateway_protocol proto = PROTO_TCP; + enum gateway_function func = UND_FCT; + + int ch; + while ((ch = getopt(argc, argv, "t:sp")) != -1) { + switch (ch) { + case 't': + if (strcmp(optarg, "mictcp") == 0) { + proto = PROTO_MICTCP; + } else if (strcmp(optarg, "tcp") == 0) { + proto = PROTO_TCP; + } else { + printf("Unrecognized transport : %s\n", optarg); + usage(); + } + break; + case 's': + if (func == UND_FCT) { + func = SOURCE; + } else { + usage(); + } + break; + case 'p': + if (func == UND_FCT) { + func = PUITS; + } else { + usage(); + } + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (func == UND_FCT || (func == PUITS && argc != 1) || (func == SOURCE && argc != 2)) { + usage(); + } + + if (proto == PROTO_TCP) { + if (func == SOURCE) { + file_to_faketcp(VIDEO_FILE, argv[0], atoi(argv[1])); + } else { + printf("No gateway needed for puits using UDP\n"); + } + } else { + if (func == SOURCE) { + file_to_mictcp(VIDEO_FILE); + } else { + mictcp_to_udp("127.0.0.1", atoi(argv[0])); + } + } + return 0; +} + +// +// Corps des fonctions privées +// + +/** + * Print usage and exit + */ +static void usage(void) +{ + printf("usage: gateway [-p|-s][-t tcp|mictcp] () \n"); + exit(EXIT_FAILURE); +} + +/** + * Function that emulates TCP behavior while reading a file making it look like TCP was used. + */ +static void file_to_faketcp(char* filename, char *host, int port) +{ + /* Création du socket */ + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + ERROR_IF(sockfd == -1, "Socket error"); + + /* Construction de l'adresse du socket distant */ + struct sockaddr_in s_addr = {0}; + s_addr.sin_family = AF_INET; + s_addr.sin_port = htons(port); + struct hostent* host_info = gethostbyname(host); + ERROR_IF(host_info == NULL, "Error gethostbyname"); + ERROR_IF(host_info->h_addrtype != AF_INET, "gethostbyname bad h_addrtype"); + ERROR_IF(host_info->h_addr == NULL, "gethostbyname no addr"); + memcpy(&(s_addr.sin_addr), host_info->h_addr, host_info->h_length); + + /* Ouverture du fichier vidéo */ + FILE *filefd = fopen(filename, "rb"); + ERROR_IF(filefd == NULL, "Error fopen"); + + uint count = 0; // compteur de paquets + struct timespec current_time, last_time; // stockage des timestamps + char buffer[MAX_UDP_SEGMENT_SIZE]; // buffer de lecture/ecriture + last_time.tv_sec = -1; + last_time.tv_nsec = LONG_MAX; + + /* Lecture jusqu'à la fin du fichier vidéo */ + while (!feof(filefd)) { + + /* Lecture du paquet rtp */ + int nb_read = read_rtp_packet(filefd, ¤t_time, buffer, MAX_UDP_SEGMENT_SIZE); + + /* Attente avant la prochaine lecture */ + struct timespec delay = tsSubtract(current_time, last_time); + nanosleep(&delay, NULL); + + /* Mise à jour du timestamp */ + last_time = current_time; + + if (ENABLE_TCP_LOSS) { + /* On émule les pertes de paquets en délayant l'envoi de 2 secondes */ + if (count++ == 600) { + printf("Simulating TCP loss\n"); + sleep(2); + count = 0; + } + } + + /* Envoi du paquet rtp via faketcp */ + int nb_sent = sendto(sockfd, buffer, nb_read, 0, (struct sockaddr*)&s_addr, sizeof(s_addr)); + ERROR_IF(nb_sent == -1, "Error sendto"); + } + + /* Fermeture du socket et du fichier */ + close(sockfd); + fclose(filefd); +} + +/** + * Function that reads a file and delivers to MICTCP. + */ +static void file_to_mictcp(char* filename) +{ + /* Création du socket MICTCP */ + int sockfd = mic_tcp_socket(CLIENT); + if (sockfd == -1) { + printf("ERROR creating the MICTCP socket\n"); + } + + /* On effectue la connexion */ + mic_tcp_sock_addr dest_addr; + dest_addr.ip_addr = "localhost"; + dest_addr.ip_addr_size = strlen(dest_addr.ip_addr) + 1; // '\0' + dest_addr.port = MICTCP_PORT; + if (mic_tcp_connect(sockfd, dest_addr) == -1) { + printf("ERROR connecting the MICTCP socket\n"); + } + + /* Ouverture du fichier vidéo */ + FILE *filefd = fopen(filename, "rb"); + ERROR_IF(filefd == NULL, "Error fopen"); + + struct timespec current_time, last_time; // stockage des timestamps + char buffer[MAX_UDP_SEGMENT_SIZE]; // buffer de lecture/ecriture + last_time.tv_sec = -1; + last_time.tv_nsec = LONG_MAX; + + /* Lecture jusqu'à la fin du fichier vidéo */ + while (!feof(filefd)) { + + /* Lecture du paquet rtp */ + int nb_read = read_rtp_packet(filefd, ¤t_time, buffer, MAX_UDP_SEGMENT_SIZE); + + /* Attente avant la prochaine lecture */ + struct timespec delay = tsSubtract(current_time, last_time); + nanosleep(&delay, NULL); + + /* Mise à jour du timestamp */ + last_time = current_time; + + /* Envoi du paquet rtp via mictcp */ + int nb_sent = mic_tcp_send(sockfd, buffer, nb_read); + if (nb_sent < 0) { + printf("ERROR on MICTCP send\n"); + } + } + + /* Fermeture du socket et du fichier */ + if (mic_tcp_close(sockfd) == -1) { + printf("ERROR on MICTCP close\n"); + } + fclose(filefd); +} + +/** + * Function that listens on MICTCP and delivers to UDP. + */ +static void mictcp_to_udp(char *host, int port) +{ + /* Création du socket UDP */ + int udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0); + ERROR_IF(udp_sockfd == -1, "Socket error"); + + /* Construction de l'adresse du socket distant */ + struct sockaddr_in remote_s_addr = {0}; + remote_s_addr.sin_family = AF_INET; + remote_s_addr.sin_port = htons(port); + struct hostent* host_info = gethostbyname(host); + ERROR_IF(host_info == NULL, "Error gethostbyname"); + ERROR_IF(host_info->h_addrtype != AF_INET, "gethostbyname bad h_addrtype"); + ERROR_IF(host_info->h_addr == NULL, "gethostbyname no addr"); + memcpy(&(remote_s_addr.sin_addr), host_info->h_addr, host_info->h_length); + + /* Création du socket MICTCP */ + int mictcp_sockfd = mic_tcp_socket(SERVER); + if (mictcp_sockfd == -1) { + printf("ERROR creating the MICTCP socket\n"); + } + + /* On bind le socket mictcp à une adresse locale */ + mic_tcp_sock_addr mt_local_addr; + mt_local_addr.ip_addr = NULL; + mt_local_addr.ip_addr_size = 0; + mt_local_addr.port = MICTCP_PORT; + if (mic_tcp_bind(mictcp_sockfd, mt_local_addr) == -1) { + printf("ERROR on binding the MICTCP socket\n"); + } + + /* Acceptation d'une demande de connexion */ + mic_tcp_sock_addr mt_remote_addr; + if (mic_tcp_accept(mictcp_sockfd, &mt_remote_addr) == -1) { + printf("ERROR on accept on the MICTCP socket\n"); + } + + /* Lecture mictcp vers udp */ + char buff[MAX_UDP_SEGMENT_SIZE]; // buffer de lecture/ecriture + while (1) { + int nb_read = mic_tcp_recv(mictcp_sockfd, buff, MAX_UDP_SEGMENT_SIZE); + if (nb_read <= 0) { + if (nb_read < 0) { + printf("ERROR on mic_recv on the MICTCP socket\n"); + } + break; // Fin de la transmission + } + + int nb_sent = sendto(udp_sockfd, buff, nb_read, 0, (struct sockaddr*)&remote_s_addr, sizeof(remote_s_addr)); + ERROR_IF(nb_sent == -1, "Error sendto"); + } + + /* Fermeture des sockets */ + if (mic_tcp_close(mictcp_sockfd) == -1) { + printf("ERROR on MICTCP close\n"); + } + close(udp_sockfd); +} + +/** + * Read one rtp packet from the video file in the buffer, as well as + * the corresponding timestamp + * Return the number of bytes read + */ +static int read_rtp_packet(FILE *fd, struct timespec *timestamp, char *buffer, int buffer_size) +{ + /* Les champs du timestamp sont stockés sur 4 octets (héritage de la version 32 bits) */ + timestamp->tv_sec = 0; + timestamp->tv_nsec = 0; + fread(&(timestamp->tv_sec), 1, 4, fd); + fread(&(timestamp->tv_nsec), 1, 4, fd); + + /* Taille du packet rdp */ + int packet_size = 0; + fread(&packet_size, 1, sizeof(int), fd); + + /* Lecture du paquet rdp */ + ERROR_IF(packet_size > buffer_size, "Buffer is too small to store the packet"); + return fread(buffer, 1, packet_size, fd); +} + +/** + * Return (time1 - time2) when (time1 > time2), 0 otherwise + */ +static struct timespec tsSubtract(struct timespec time1, struct timespec time2) +{ + struct timespec result; + + /* Subtract the second time from the first. */ + if ((time1.tv_sec < time2.tv_sec) || ((time1.tv_sec == time2.tv_sec) && (time1.tv_nsec <= time2.tv_nsec))) { + /* TIME1 <= TIME2 */ + result.tv_sec = 0; + result.tv_nsec = 0; + } else { /* TIME1 > TIME2 */ + result.tv_sec = time1.tv_sec - time2.tv_sec; + if (time1.tv_nsec < time2.tv_nsec) { + result.tv_nsec = time1.tv_nsec + 1000000000L - time2.tv_nsec; + result.tv_sec--; /* Borrow a second. */ + } else { + result.tv_nsec = time1.tv_nsec - time2.tv_nsec; + } + } + + return (result); +} diff --git a/src/apps/server.c b/src/apps/server.c new file mode 100644 index 0000000..fcca4ee --- /dev/null +++ b/src/apps/server.c @@ -0,0 +1,55 @@ +#include +#include + +#define MAX_SIZE 1000 + +#define COLOR_RED "\x1b[31m" +#define COLOR_GREEN "\x1b[32m" +#define COLOR_BOLD "\x1b[1m" +#define COLOR_RESET "\x1b[0m" + +int main() { + int sockfd; + mic_tcp_sock_addr addr; + mic_tcp_sock_addr remote_addr; + char chaine[MAX_SIZE]; + + addr.ip_addr = "127.0.0.1"; + addr.port = 1234; + + + if ((sockfd = mic_tcp_socket(SERVER)) == -1) { + printf("[TSOCK] Creation du socket MICTCP: "COLOR_RED"ERREUR"COLOR_RESET"\n"); + return 1; + } else { + printf("[TSOCK] Creation du socket MICTCP: "COLOR_GREEN"OK"COLOR_RESET"\n"); + } + + if (mic_tcp_bind(sockfd, addr) == -1) { + printf("[TSOCK] Bind du socket MICTCP: "COLOR_RED"ERREUR"COLOR_RESET"\n"); + return 1; + } else { + printf("[TSOCK] Bind du socket MICTCP: "COLOR_GREEN"OK"COLOR_RESET"\n"); + } + + if (mic_tcp_accept(sockfd, &remote_addr) == -1) { + printf("[TSOCK] Accept sur le socket MICTCP: "COLOR_RED"ERREUR"COLOR_RESET"\n"); + return 1; + } else { + printf("[TSOCK] Accept sur le socket MICTCP: "COLOR_GREEN"OK"COLOR_RESET"\n"); + } + + + memset(chaine, 0, MAX_SIZE); + + printf(COLOR_BOLD"[TSOCK] Appuyez sur CTRL+C pour quitter ...\n"COLOR_RESET); + + while (1) { + int rcv_size = 0; + printf("\n[TSOCK] Attente d'une donnee, appel de mic_recv ...\n"); + rcv_size = mic_tcp_recv(sockfd, chaine, MAX_SIZE); + printf("[TSOCK] Reception d'un message de taille : %d\n", rcv_size); + printf("[TSOCK] Message Recu : "COLOR_BOLD"%s"COLOR_RESET, chaine); + } + return 0; +} diff --git a/src/mictcp.c b/src/mictcp.c new file mode 100644 index 0000000..fd1c6cc --- /dev/null +++ b/src/mictcp.c @@ -0,0 +1,148 @@ +#include +#include + +struct mic_tcp_sock g_sock; + +/** + * Permet de créer un socket entre l’application et MIC-TCP. + * @return le descripteur du socket ou bien -1 en cas d'erreur. + */ +int mic_tcp_socket(start_mode sm) { + int result; + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + result = initialize_components(sm); /* Appel obligatoire */ + set_loss_rate(0); + + g_sock.fd = result; + g_sock.state = IDLE; + + return result; +} + +/** + * Permet d’attribuer une adresse à un socket. + * @return 0 si succès, et -1 en cas d’échec. + */ +int mic_tcp_bind(int socket, mic_tcp_sock_addr addr) { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + + int result = 0; // TODO change in v2 + + g_sock.addr = addr; + + return result; +} + +/** + * Met le socket en état d'acceptation de connexions. + * @return 0 si succès, -1 si erreur. + */ +int mic_tcp_accept(int socket, mic_tcp_sock_addr *addr) { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + + int result = 0; // TODO change in v2 + + g_sock.state = ESTABLISHED; + + return result; +} + +/** + * Permet de réclamer l’établissement d’une connexion. + * @return 0 si la connexion est établie, et -1 en cas d’échec + */ +int mic_tcp_connect(int socket, mic_tcp_sock_addr addr) { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + + int result = 0; // TODO change in v2 + + g_sock.addr = addr; + g_sock.state = ESTABLISHED; + + return result; +} + +/** + * Permet de réclamer l’envoi d’une donnée applicative. + * @return la taille des données envoyées, et -1 en cas d'erreur + */ +int mic_tcp_send(int socket, char *mesg, int mesg_size) { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + + struct mic_tcp_pdu pdu; + pdu.payload.data = mesg; + pdu.payload.size = mesg_size; + + pdu.header.source_port = 0; + pdu.header.dest_port = g_sock.addr.port; + pdu.header.seq_num = 0; + pdu.header.ack_num = 0; + pdu.header.syn = 0; + pdu.header.ack = 0; + pdu.header.fin = 0; + + return IP_send(pdu, g_sock.addr); +} + +/** + * 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. + * @return 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) { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + + int result = -1; + + struct mic_tcp_payload payload; + payload.data = malloc(max_mesg_size * sizeof(char)); + payload.size = max_mesg_size; + int get_size = app_buffer_get(payload); + memcpy(mesg, payload.data, get_size); + free(payload.data); + result = get_size; + + return result; +} + +/** + * Permet de réclamer la destruction d’un socket. + * Engendre la fermeture de la connexion suivant le modèle de TCP. + * @return 0 si tout se passe bien et -1 en cas d'erreur + */ +int mic_tcp_close(int socket) { + printf("[MIC-TCP] Appel de la fonction : "); + printf(__FUNCTION__); + printf("\n"); + + g_sock.state = CLOSED; + + return -1; +} + +/** + * 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) { + printf("[MIC-TCP] Appel de la fonction: "); + printf(__FUNCTION__); + printf("\n"); + + app_buffer_put(pdu.payload); +} diff --git a/tsock_texte b/tsock_texte new file mode 100755 index 0000000..343ffc6 --- /dev/null +++ b/tsock_texte @@ -0,0 +1,40 @@ +#!/bin/bash + +puits=false +sourc=false +protocol="tcp" + +usage() { echo "Usage: $0 [-p|-s]" 1>&2; exit 1; } + +while getopts "pst:" o; do + case "${o}" in + p) + puits=true + ;; + s) + sourc=true + ;; + *) + usage + ;; + esac +done + +if ([ "$puits" = true ] && [ "$sourc" = true ]) || ([ "$puits" = false ] && [ "$sourc" = false ]) ; then + usage + exit 1 +fi + +if [ "$puits" = true ]; then + cd build + ./server + cd .. +fi + +if [ "$sourc" = true ]; then + + cd build + ./client + cd .. +fi + diff --git a/tsock_video b/tsock_video new file mode 100755 index 0000000..320bb83 --- /dev/null +++ b/tsock_video @@ -0,0 +1,63 @@ +#!/bin/bash + +puits=false +sourc=false +protocol="tcp" +port=`expr \`id -u\` % 3000 + 14578` + +usage() { echo "Usage: $0 [[-p|-s] [-t (tcp|mictcp)]" 1>&2; exit 1; } + +while getopts "pst:" o; do + case "${o}" in + t) + protocol=${OPTARG} + if [ "$protocol" != "tcp" ] && [ "$protocol" != "mictcp" ]; then + usage + exit 1 + fi + ;; + p) + puits=true + ;; + s) + sourc=true + ;; + *) + usage + ;; + esac +done + +if ([ "$puits" = true ] && [ "$sourc" = true ]) || ([ "$puits" = false ] && [ "$sourc" = false ]) ; then + usage + exit 1 +fi + +if [ "$puits" = true ]; then + + echo "Lancement du puits, protocole " $protocol + cvlc rtp://127.0.0.1:$port > /dev/null 2>&1 & + + if [ "$protocol" = "mictcp" ]; then + cd build + ./gateway -p -t $protocol $port & + cd .. + fi +fi + +if [ "$sourc" = true ]; then + + echo "Lancement de la source, protocol " $protocol + cd build + ./gateway -s -t $protocol 127.0.0.1 $port & + cd .. +fi + +echo "Appuyez sur ENTREE pour arreter" +read line + +echo "Arret" +killall -9 cvlc > /dev/null 2>&1 +killall -9 vlc > /dev/null 2>&1 +killall -9 gateway > /dev/null 2>&1 +