From 188f9547c7a91d511c1af51d94f8d2e36f58e729 Mon Sep 17 00:00:00 2001 From: gonzalef Date: Sat, 7 Mar 2020 21:33:11 +0100 Subject: [PATCH 01/26] structs --- tlv.h | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tlv.h diff --git a/tlv.h b/tlv.h new file mode 100644 index 0000000..c26b9a1 --- /dev/null +++ b/tlv.h @@ -0,0 +1,78 @@ +#include +#include + +typedef union tlv { + pad1 *pad1; + padn *padn; + neighbour_req *neighbour_req; + neighbour *neighbour; + network_hash *network_hash; + network_state_req *network_state_req; + node_hash *node_hash; + node_state_req *node_state_req; + node_state *node_state; + warning *warning; +} tlv; + +typedef struct pad1 { + char type; +} pad1; + +typedef struct padn { + char type; + char length; + char *mbz; +} padn; + +typedef struct neighbour_req { + char type; + char length; +} neighbour_req; + +typedef struct neighbour { + char type; + char length; + struct in6_addr ip; + short port; +} neighbour; + +typedef struct network_hash { + char type; + char length; + char *network_hash; +} network_hash; + +typedef struct network_state_req { + char type; + char length; +} network_state_req; + +typedef struct node_hash { + char type; + char length; + long node_id; + short seqno; + char *node_hash; +} node_hash; + +typedef struct node_state_req { + char type; + char length; + long node_id; +} node_state_req; + +typedef struct node_state { + char type; + char length; + long node_id; + short seqno; + char *node_hash; + char *data; +} node_state; + +typedef struct warning { + char type; + char length; + char *message; +} warning; + From bbd4412cf3f3ea1458c276dca75cb1374b0d8305 Mon Sep 17 00:00:00 2001 From: gonzalef Date: Fri, 13 Mar 2020 13:25:25 +0100 Subject: [PATCH 02/26] unsigned --- tlv.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tlv.h b/tlv.h index c26b9a1..75c9703 100644 --- a/tlv.h +++ b/tlv.h @@ -15,55 +15,55 @@ typedef union tlv { } tlv; typedef struct pad1 { - char type; + unsigned unsigned char type; } pad1; typedef struct padn { - char type; - char length; + unsigned char type; + unsigned char length; char *mbz; } padn; typedef struct neighbour_req { - char type; - char length; + unsigned char type; + unsigned char length; } neighbour_req; typedef struct neighbour { - char type; - char length; + unsigned char type; + unsigned char length; struct in6_addr ip; short port; } neighbour; typedef struct network_hash { - char type; - char length; + unsigned char type; + unsigned char length; char *network_hash; } network_hash; typedef struct network_state_req { - char type; - char length; + unsigned char type; + unsigned char length; } network_state_req; typedef struct node_hash { - char type; - char length; + unsigned char type; + unsigned char length; long node_id; short seqno; char *node_hash; } node_hash; typedef struct node_state_req { - char type; - char length; + unsigned char type; + unsigned char length; long node_id; } node_state_req; typedef struct node_state { - char type; - char length; + unsigned char type; + unsigned char length; long node_id; short seqno; char *node_hash; @@ -71,8 +71,8 @@ typedef struct node_state { } node_state; typedef struct warning { - char type; - char length; + unsigned char type; + unsigned char length; char *message; } warning; From 656a3092a1deac6a36dd752eb282fe4cb247044c Mon Sep 17 00:00:00 2001 From: gonzalef Date: Fri, 13 Mar 2020 13:36:51 +0100 Subject: [PATCH 03/26] tailles --- tlv.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tlv.h b/tlv.h index 75c9703..9c90bca 100644 --- a/tlv.h +++ b/tlv.h @@ -14,21 +14,26 @@ typedef union tlv { warning *warning; } tlv; + +// 1 octet typedef struct pad1 { unsigned unsigned char type; } pad1; +// 2 octets min, 257 octets max (unsigned char 0 -> 255) typedef struct padn { unsigned char type; unsigned char length; char *mbz; } padn; +// 2 octets typedef struct neighbour_req { unsigned char type; unsigned char length; } neighbour_req; +// 132 octets typedef struct neighbour { unsigned char type; unsigned char length; @@ -36,17 +41,20 @@ typedef struct neighbour { short port; } neighbour; +// 18 octets typedef struct network_hash { unsigned char type; unsigned char length; char *network_hash; } network_hash; +// 2 octets typedef struct network_state_req { unsigned char type; unsigned char length; } network_state_req; +// 28 octets typedef struct node_hash { unsigned char type; unsigned char length; @@ -55,12 +63,14 @@ typedef struct node_hash { char *node_hash; } node_hash; +// 10 octets typedef struct node_state_req { unsigned char type; unsigned char length; long node_id; } node_state_req; +// 28 octets min, 220 octets max (data 0 -> 192) typedef struct node_state { unsigned char type; unsigned char length; @@ -70,9 +80,9 @@ typedef struct node_state { char *data; } node_state; +// 2 octets min, 257 ocets max (unsigned char 0 -> 255) typedef struct warning { unsigned char type; unsigned char length; char *message; -} warning; - +} warning; \ No newline at end of file From b6b87e575e9cebf1c40317bba10b2bc2f781f19b Mon Sep 17 00:00:00 2001 From: gonzalef Date: Fri, 13 Mar 2020 14:27:44 +0100 Subject: [PATCH 04/26] packet --- tlv.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tlv.h b/tlv.h index 9c90bca..105b198 100644 --- a/tlv.h +++ b/tlv.h @@ -1,6 +1,14 @@ #include #include +// 8 octets min (struct pointer 4 octets), 1024 octets max +typedef struct packet { + unsigned char magic; // 95 (si autre, ignorer) + unsigned char version; // 1 (si autre, ignorer) + short length; // 1020 max + char *body; +} packet; + typedef union tlv { pad1 *pad1; padn *padn; From a8c32c2be28d36dfeb8913cad09c37e6ec19ad4b Mon Sep 17 00:00:00 2001 From: gonzalef Date: Thu, 19 Mar 2020 19:39:37 +0100 Subject: [PATCH 05/26] parser --- parser.h | 41 +++++++++++++++++++++++++++++++++++++++++ tlv.h | 26 +++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 parser.h diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..b7694e0 --- /dev/null +++ b/parser.h @@ -0,0 +1,41 @@ +#include +#include + +enum cmd_type { + NEIGHBOUR_REQ, NETWORK_STATE_REQ, NODE_STATE_REQ, SEND, ERROR +}; + +struct cmd_token { + enum cmd_type type; + char arg[193]; +}; + +// retourne le type de commande à exécuter +struct cmd_token parse_cmd() { + char buf[198], cmd[5], arg[193]; + struct cmd_token token; + token.type = ERROR; + memset(token.arg, 0, 193); + + if(fgets(buf, 198, stdin) == NULL) + return token; + + // cmd sera le premier mot rencontré et arg la suite de mots après celui ci, si les deux variables ne sont pas remplies alors il y a une erreur + if(sscanf(buf, "%s %[^\t\n]", cmd, arg) != 2) + return token; + + if(strcmp("req", cmd) == 0) { + if(strcmp("neighbour", arg) == 0) + token.type = NEIGHBOUR_REQ; + else if(strcmp("network state", arg) == 0) + token.type = NETWORK_STATE_REQ; + else if(strcmp("node state", arg) == 0) + token.type = NODE_STATE_REQ; + } else if(strcmp("send", cmd) == 0) { + token.type = SEND; + //arg[192] = 0; + strcpy(token.arg, arg); + } + + return token; +} \ No newline at end of file diff --git a/tlv.h b/tlv.h index 105b198..9fbbf07 100644 --- a/tlv.h +++ b/tlv.h @@ -1,5 +1,6 @@ #include #include +#include "parser.h" // 8 octets min (struct pointer 4 octets), 1024 octets max typedef struct packet { @@ -93,4 +94,27 @@ typedef struct warning { unsigned char type; unsigned char length; char *message; -} warning; \ No newline at end of file +} warning; + +// creer un tlv +void build_tlv(tlv *t) { + struct cmd_token token = parse_cmd(); + + switch(token.type) { + case NEIGHBOUR_REQ: + // a remplir + break; + case NETWORK_STATE_REQ: + // a remplir + break; + case NODE_STATE_REQ: + // a remplir + break; + case SEND: + // a remplir + break; + case ERROR: + printf("Wrong format, use 'req {neighbour | network state | node state}' or 'send {message}'"); + break; + } +} \ No newline at end of file From 4ebed74a15d003e44e2002c32751084525eb2c9c Mon Sep 17 00:00:00 2001 From: gonzalef Date: Fri, 20 Mar 2020 17:39:17 +0100 Subject: [PATCH 06/26] create tlv --- parser.c | 31 +++++++++ parser.h | 39 ++---------- tlv.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tlv.h | 77 +++++++++++------------ 4 files changed, 260 insertions(+), 75 deletions(-) create mode 100644 parser.c create mode 100644 tlv.c diff --git a/parser.c b/parser.c new file mode 100644 index 0000000..fa2f061 --- /dev/null +++ b/parser.c @@ -0,0 +1,31 @@ +#include "parser.h" + +// retourne le type de commande à exécuter +cmd_token parse_cmd() { + char buf[198], cmd[5], arg[193]; + cmd_token token; + token.type = ERROR; + memset(token.arg, 0, 193); + + if(fgets(buf, 198, stdin) == NULL) + return token; + + // cmd sera le premier mot rencontré et arg la suite de mots après celui ci, si les deux variables ne sont pas remplies alors il y a une erreur + if(sscanf(buf, "%s %[^\t\n]", cmd, arg) != 2) + return token; + + if(strcmp("req", cmd) == 0) { + if(strcmp("neighbour", arg) == 0) + token.type = NEIGHBOUR_REQ; + else if(strcmp("network state", arg) == 0) + token.type = NETWORK_STATE_REQ; + else if(strcmp("node state", arg) == 0) + token.type = NODE_STATE_REQ; + } else if(strcmp("send", cmd) == 0) { + token.type = SEND; + //arg[192] = 0; + strcpy(token.arg, arg); + } + + return token; +} \ No newline at end of file diff --git a/parser.h b/parser.h index b7694e0..4573a86 100644 --- a/parser.h +++ b/parser.h @@ -1,41 +1,14 @@ #include #include -enum cmd_type { +typedef enum cmd_type { NEIGHBOUR_REQ, NETWORK_STATE_REQ, NODE_STATE_REQ, SEND, ERROR -}; +} cmd_type; -struct cmd_token { - enum cmd_type type; +typedef struct cmd_token { + cmd_type type; char arg[193]; -}; +} cmd_token; // retourne le type de commande à exécuter -struct cmd_token parse_cmd() { - char buf[198], cmd[5], arg[193]; - struct cmd_token token; - token.type = ERROR; - memset(token.arg, 0, 193); - - if(fgets(buf, 198, stdin) == NULL) - return token; - - // cmd sera le premier mot rencontré et arg la suite de mots après celui ci, si les deux variables ne sont pas remplies alors il y a une erreur - if(sscanf(buf, "%s %[^\t\n]", cmd, arg) != 2) - return token; - - if(strcmp("req", cmd) == 0) { - if(strcmp("neighbour", arg) == 0) - token.type = NEIGHBOUR_REQ; - else if(strcmp("network state", arg) == 0) - token.type = NETWORK_STATE_REQ; - else if(strcmp("node state", arg) == 0) - token.type = NODE_STATE_REQ; - } else if(strcmp("send", cmd) == 0) { - token.type = SEND; - //arg[192] = 0; - strcpy(token.arg, arg); - } - - return token; -} \ No newline at end of file +cmd_token parse_cmd(); \ No newline at end of file diff --git a/tlv.c b/tlv.c new file mode 100644 index 0000000..ec0e5e3 --- /dev/null +++ b/tlv.c @@ -0,0 +1,188 @@ +#include "tlv.h" + +// creer un tlv +int build_tlv(tlv *tlv, cmd_token token) { + switch(token.type) { + case NEIGHBOUR_REQ: + // a remplir + break; + case NETWORK_STATE_REQ: + // a remplir + break; + case NODE_STATE_REQ: + // a remplir + break; + case SEND: + // a remplir + break; + case ERROR: + printf("Wrong format, use 'req {neighbour | network state | node state}' or 'send {message}'"); + break; + } +} + +int build_pad1(tlv *tlv) { + pad1 *new = (pad1*) malloc(sizeof(pad1)); + + if(new == NULL) + return -1; + + new->type = 0; + + tlv->pad1 = new; + + return 0; +} + +int build_padn(tlv *tlv, size_t len) { + padn *new = (padn*) malloc(sizeof(padn)); + + if(new == NULL) + return -1; + + new->type = 1; + new->length = len; + new->mbz = (char*) calloc(sizeof(char), len); + + tlv->padn = new; + + return 0; +} + +int build_neighbour_req(tlv *tlv) { + neighbour_req *new = (neighbour_req*) malloc(sizeof(neighbour_req)); + + if(new == NULL) + return -1; + + new->type = 2; + new->length = 0; + + tlv->neighbour_req = new; + + return 0; +} + +int build_neighbour(tlv *tlv, struct in6_addr ip, short port) { + neighbour *new = (neighbour*) malloc(sizeof(neighbour)); + + if(new == NULL) + return -1; + + new->type = 3; + new->length = 18; + new->ip = ip; + new->port = port; + + tlv->neighbour = new; + + return 0; +} + +int build_network_hash(tlv *tlv, char *hash) { + network_hash *new = (network_hash*) malloc(sizeof(network_hash)); + + if(new == NULL) + return -1; + + new->type = 4; + new->length = 16; + memcpy(new->network_hash, hash, 16); + + tlv->network_hash = new; + + return 0; +} + +int build_network_state_req(tlv *tlv) { + network_state_req *new = (network_state_req*) malloc(sizeof(network_state_req)); + + if(new == NULL) + return -1; + + new->type = 5; + new->length = 0; + + tlv->network_state_req = new; + + return 0; +} + +int build_node_hash(tlv *tlv, long node_id, short seqno, char *hash) { + node_hash *new = (node_hash*) malloc(sizeof(node_hash)); + + if(new == NULL) + return -1; + + new->type = 6; + new->length = 26; + new->node_id = node_id; + new->seqno = seqno; + memcpy(new->node_hash, hash, 16); + + tlv->node_hash = new; + + return 0; +} + +int build_node_state_req(tlv *tlv, long node_id) { + node_state_req *new = (node_state_req*) malloc(sizeof(node_state_req)); + + if(new == NULL) + return -1; + + new->type = 7; + new->length = 8; + new->node_id = node_id; + + tlv->node_state_req = new; + + return 0; +} + +int build_node_state(tlv *tlv, long node_id, short seqno, char *node_hash, char *data) { + node_state *new = (node_state*) malloc(sizeof(node_state)); + int len = strlen(data); + + if(new == NULL) + return -1; + + // en mettant cet octet à 0 on est surs de traiter un champ data de taille 192 max + if(len > 192) { + data[192] = 0; + len = 192; + } + + new->type = 8; + new->length = 26 + len; + new->node_id = node_id; + new->seqno = seqno; + memcpy(new->node_hash, node_hash, 16); + memcpy(new->data, data, len); + + tlv->node_state = new; + + return 0; +} + +int build_warning(tlv *tlv, char *message) { + warning *new = (warning*) malloc(sizeof(warning)); + int len = strlen(message); + + if(new == NULL) + return -1; + + // en mettant cet octet à 0 on est surs de traiter un champ message de taille 256 max + if(len > 256) { + message[256] = 0; + len = 256; + } + + new->type = 9; + new->length = len; + memcpy(new->message, message, len); + + tlv->warning = new; + + return 0; +} \ No newline at end of file diff --git a/tlv.h b/tlv.h index 9fbbf07..d42dae6 100644 --- a/tlv.h +++ b/tlv.h @@ -1,5 +1,6 @@ #include #include +#include #include "parser.h" // 8 octets min (struct pointer 4 octets), 1024 octets max @@ -10,26 +11,12 @@ typedef struct packet { char *body; } packet; -typedef union tlv { - pad1 *pad1; - padn *padn; - neighbour_req *neighbour_req; - neighbour *neighbour; - network_hash *network_hash; - network_state_req *network_state_req; - node_hash *node_hash; - node_state_req *node_state_req; - node_state *node_state; - warning *warning; -} tlv; - - // 1 octet typedef struct pad1 { - unsigned unsigned char type; + unsigned char type; } pad1; -// 2 octets min, 257 octets max (unsigned char 0 -> 255) +// 2 octets min, 258 octets max (unsigned char 0 -> 255) typedef struct padn { unsigned char type; unsigned char length; @@ -42,7 +29,7 @@ typedef struct neighbour_req { unsigned char length; } neighbour_req; -// 132 octets +// 20 octets typedef struct neighbour { unsigned char type; unsigned char length; @@ -54,7 +41,7 @@ typedef struct neighbour { typedef struct network_hash { unsigned char type; unsigned char length; - char *network_hash; + char network_hash[16]; } network_hash; // 2 octets @@ -69,7 +56,7 @@ typedef struct node_hash { unsigned char length; long node_id; short seqno; - char *node_hash; + char node_hash[16]; } node_hash; // 10 octets @@ -85,36 +72,42 @@ typedef struct node_state { unsigned char length; long node_id; short seqno; - char *node_hash; + char node_hash[16]; char *data; } node_state; -// 2 octets min, 257 ocets max (unsigned char 0 -> 255) +// 2 octets min, 258 ocets max (unsigned char 0 -> 255) typedef struct warning { unsigned char type; unsigned char length; char *message; } warning; -// creer un tlv -void build_tlv(tlv *t) { - struct cmd_token token = parse_cmd(); +typedef union tlv { + pad1 *pad1; + padn *padn; + neighbour_req *neighbour_req; + neighbour *neighbour; + network_hash *network_hash; + network_state_req *network_state_req; + node_hash *node_hash; + node_state_req *node_state_req; + node_state *node_state; + warning *warning; +} tlv; - switch(token.type) { - case NEIGHBOUR_REQ: - // a remplir - break; - case NETWORK_STATE_REQ: - // a remplir - break; - case NODE_STATE_REQ: - // a remplir - break; - case SEND: - // a remplir - break; - case ERROR: - printf("Wrong format, use 'req {neighbour | network state | node state}' or 'send {message}'"); - break; - } -} \ No newline at end of file + +// creer un tlv +int build_tlv(tlv *tlv, cmd_token token); + +// creer un tlv specifique +int build_pad1(tlv *tlv); +int build_padn(tlv *tlv, size_t len); +int build_neighbour_req(tlv *tlv); +int build_neighbour(tlv *tlv, struct in6_addr ip, short seqno); +int build_network_hash(tlv *tlv, char *network_hash); +int build_network_state_req(tlv *tlv); +int build_node_hash(tlv *tlv, long node_id, short seqno, char *node_hash); +int build_node_state_req(tlv *tlv, long node_id); +int build_node_state(tlv *tlv, long node_id, short seqno, char *node_hash, char *data); +int build_warning(tlv *tlv, char *message); \ No newline at end of file From 2ff5c656ea9ba07362f4ed8787e70cc5b69c7128 Mon Sep 17 00:00:00 2001 From: n07070 Date: Tue, 24 Mar 2020 12:19:59 +0100 Subject: [PATCH 07/26] Moved files in src/, Added Makefile, Updated Notes.md with server address --- Makefile | 0 Notes.md | 6 ++++++ parser.c => src/parser.c | 0 parser.h => src/parser.h | 0 tlv.c => src/tlv.c | 0 tlv.h => src/tlv.h | 0 6 files changed, 6 insertions(+) create mode 100644 Makefile rename parser.c => src/parser.c (100%) rename parser.h => src/parser.h (100%) rename tlv.c => src/tlv.c (100%) rename tlv.h => src/tlv.h (100%) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/Notes.md b/Notes.md index c4d7d4b..f0fd541 100644 --- a/Notes.md +++ b/Notes.md @@ -7,3 +7,9 @@ s ⊕ n = (s + n) and 65535 s ≼ s ′ lorsque ((s ′ − s) mod 2 16 ) < 32768 s ≼ s ′ lorsque ((s ′ − s) and 32768) = 0 + +------- + +jch.irif.fr port UDP 1212 + +http://jch.irif.fr:8082/ diff --git a/parser.c b/src/parser.c similarity index 100% rename from parser.c rename to src/parser.c diff --git a/parser.h b/src/parser.h similarity index 100% rename from parser.h rename to src/parser.h diff --git a/tlv.c b/src/tlv.c similarity index 100% rename from tlv.c rename to src/tlv.c diff --git a/tlv.h b/src/tlv.h similarity index 100% rename from tlv.h rename to src/tlv.h From 35d50d178a631c87a29599569418c668c878e028 Mon Sep 17 00:00:00 2001 From: n07070 Date: Tue, 24 Mar 2020 12:24:56 +0100 Subject: [PATCH 08/26] Added conditional includes --- src/node.h | 4 ++++ src/parser.h | 7 ++++++- src/tlv.h | 9 +++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/node.h b/src/node.h index 21329be..ed313b3 100644 --- a/src/node.h +++ b/src/node.h @@ -1,5 +1,7 @@ // Define constants +#ifndef NODE_H +#define NODE_H // fonctions signatures void listen_for_packets(); @@ -24,3 +26,5 @@ void t_get_network_state(); char * hash(); short * get_seq_no(short s, int n); + +#endif diff --git a/src/parser.h b/src/parser.h index 4573a86..04e9f86 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,6 +1,9 @@ #include #include +#ifndef PARSER_H +#define PARSER_H + typedef enum cmd_type { NEIGHBOUR_REQ, NETWORK_STATE_REQ, NODE_STATE_REQ, SEND, ERROR } cmd_type; @@ -11,4 +14,6 @@ typedef struct cmd_token { } cmd_token; // retourne le type de commande à exécuter -cmd_token parse_cmd(); \ No newline at end of file +cmd_token parse_cmd(); + +#endif diff --git a/src/tlv.h b/src/tlv.h index d42dae6..33083b8 100644 --- a/src/tlv.h +++ b/src/tlv.h @@ -3,7 +3,10 @@ #include #include "parser.h" -// 8 octets min (struct pointer 4 octets), 1024 octets max +#ifndef TLV_H +#define TLV_H + +// 8 octets min (struct pointer 4 octets), 1024 octets max typedef struct packet { unsigned char magic; // 95 (si autre, ignorer) unsigned char version; // 1 (si autre, ignorer) @@ -110,4 +113,6 @@ int build_network_state_req(tlv *tlv); int build_node_hash(tlv *tlv, long node_id, short seqno, char *node_hash); int build_node_state_req(tlv *tlv, long node_id); int build_node_state(tlv *tlv, long node_id, short seqno, char *node_hash, char *data); -int build_warning(tlv *tlv, char *message); \ No newline at end of file +int build_warning(tlv *tlv, char *message); + +#endif From fafebaa32f9cc34751d5de16ae197e697580381e Mon Sep 17 00:00:00 2001 From: n07070 Date: Tue, 24 Mar 2020 13:25:18 +0100 Subject: [PATCH 09/26] Added new structures for the messages, the neighbours, and broke line feed for a comment --- src/node.c | 14 +++++++++----- src/node.h | 34 ++++++++++++++++++++++++++++++++++ src/parser.c | 7 ++++--- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/node.c b/src/node.c index fdfb6fe..f826554 100644 --- a/src/node.c +++ b/src/node.c @@ -7,9 +7,10 @@ packet listen_for_packets(){ } +// We need to make sure the TLV announces a length that will no go onto +// another tlv, as we might end up reading bullshit. int validate_tlvs(union tlv tlv_to_validate){ - // We need to make sure the TLV announces a length that will no go onto - // another tlv, as we might end up reading bullshit. + } void work_with_tlvs(struct tlvs_list receivied_tlvs){ @@ -17,7 +18,7 @@ void work_with_tlvs(struct tlvs_list receivied_tlvs){ // For every TLV, // We make sure the TLV is legal. if(!validate_tlvs(tlv)){ - perror(">> Invalid TLV receivied, is will be ignored."); + perror(">> Invalid TLV receivied, it will be ignored."); } // Switch @@ -52,11 +53,14 @@ void work_with_tlvs(struct tlvs_list receivied_tlvs){ } int main(int argc, char const *argv[]) { - - while(CONTINUE){ + int continue = 1; + + while(continue){ // We create the neighbourhood table + neighbour_peer neighbour_list[NEIGHBOUR_MAX]; // We create the message table + // We create our own message. // Listen for incoming packets diff --git a/src/node.h b/src/node.h index ed313b3..b871865 100644 --- a/src/node.h +++ b/src/node.h @@ -3,6 +3,40 @@ #ifndef NODE_H #define NODE_H +// The node ID +#define NODE_ID 203242402519736214145149136169422092269247115186189140178187251487819615911212154252117172522111472481308026129190139512419121015210238252292031613214452118122204415160254 + +// The number of neighbours +// The neighbour table has 15 entries +#define NEIGHBOUR_MAX 15 + +/* la table de voisins, qui est indexée par adresses de socket (des paires (IP, Port)), + * et dont chaque entrée contient un booléen indiquant si le pair est permanent + * (configuré au lancement) ou transitoire, et la date de dernière réception d’un + * paquet de la part de ce pair ; +*/ +typedef struct neighbour_peer { + struct in6_addr ip; + short port; + char is_temporary; + struct timeval last_seen; +} neighbour_peer; + +// The strucuture to hold the messages +/* It's a list of triplets, (Li,Si,Di) + * Li : The Node ID of the publisher 64 bits + * Si : the sequence number 16 bits + * Di : the data of the message 192 bytes +*/ + +typedef struct message { + long node_id_publisher; + short seqno; + char *data; +} message; + +// TODO + // fonctions signatures void listen_for_packets(); diff --git a/src/parser.c b/src/parser.c index fa2f061..58c0c58 100644 --- a/src/parser.c +++ b/src/parser.c @@ -10,12 +10,13 @@ cmd_token parse_cmd() { if(fgets(buf, 198, stdin) == NULL) return token; - // cmd sera le premier mot rencontré et arg la suite de mots après celui ci, si les deux variables ne sont pas remplies alors il y a une erreur + // cmd sera le premier mot rencontré et arg la suite de mots après celui ci, + // si les deux variables ne sont pas remplies alors il y a une erreur if(sscanf(buf, "%s %[^\t\n]", cmd, arg) != 2) return token; if(strcmp("req", cmd) == 0) { - if(strcmp("neighbour", arg) == 0) + if(strcmp("neighbour", arg) == 0) token.type = NEIGHBOUR_REQ; else if(strcmp("network state", arg) == 0) token.type = NETWORK_STATE_REQ; @@ -28,4 +29,4 @@ cmd_token parse_cmd() { } return token; -} \ No newline at end of file +} From f4d3dcbd6dc93dad499a5eb6df78d13cade50203 Mon Sep 17 00:00:00 2001 From: gonzalef Date: Tue, 24 Mar 2020 15:39:31 +0100 Subject: [PATCH 10/26] post --- parser.c | 6 +++--- parser.h | 4 ++-- tlv.c | 4 ++-- tlv.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/parser.c b/parser.c index fa2f061..5b1b48a 100644 --- a/parser.c +++ b/parser.c @@ -21,11 +21,11 @@ cmd_token parse_cmd() { token.type = NETWORK_STATE_REQ; else if(strcmp("node state", arg) == 0) token.type = NODE_STATE_REQ; - } else if(strcmp("send", cmd) == 0) { - token.type = SEND; + } else if(strcmp("post", cmd) == 0) { + token.type = POST; //arg[192] = 0; strcpy(token.arg, arg); } return token; -} \ No newline at end of file +} diff --git a/parser.h b/parser.h index 4573a86..8663a5a 100644 --- a/parser.h +++ b/parser.h @@ -2,7 +2,7 @@ #include typedef enum cmd_type { - NEIGHBOUR_REQ, NETWORK_STATE_REQ, NODE_STATE_REQ, SEND, ERROR + NEIGHBOUR_REQ, NETWORK_STATE_REQ, NODE_STATE_REQ, POST, ERROR } cmd_type; typedef struct cmd_token { @@ -11,4 +11,4 @@ typedef struct cmd_token { } cmd_token; // retourne le type de commande à exécuter -cmd_token parse_cmd(); \ No newline at end of file +cmd_token parse_cmd(); diff --git a/tlv.c b/tlv.c index ec0e5e3..2284815 100644 --- a/tlv.c +++ b/tlv.c @@ -12,11 +12,11 @@ int build_tlv(tlv *tlv, cmd_token token) { case NODE_STATE_REQ: // a remplir break; - case SEND: + case POST: // a remplir break; case ERROR: - printf("Wrong format, use 'req {neighbour | network state | node state}' or 'send {message}'"); + printf("Wrong format, use 'req {neighbour | network state | node state}' or 'post {message}'"); break; } } diff --git a/tlv.h b/tlv.h index d42dae6..35625ac 100644 --- a/tlv.h +++ b/tlv.h @@ -20,7 +20,7 @@ typedef struct pad1 { typedef struct padn { unsigned char type; unsigned char length; - char *mbz; + char mbz[256]; } padn; // 2 octets @@ -73,7 +73,7 @@ typedef struct node_state { long node_id; short seqno; char node_hash[16]; - char *data; + char data[192]; } node_state; // 2 octets min, 258 ocets max (unsigned char 0 -> 255) From 0609dc11b94986c0f113ecf635d6601298abe986 Mon Sep 17 00:00:00 2001 From: gonzalef Date: Tue, 24 Mar 2020 16:34:45 +0100 Subject: [PATCH 11/26] src --- src/Notes.md | 1 + src/README.md | 5 ++ src/parser.c | 31 ++++++++ src/parser.h | 14 ++++ src/test | Bin 0 -> 8392 bytes src/test.c | 15 ++++ src/tlv.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++ src/tlv.h | 113 +++++++++++++++++++++++++++++ src/énoncé.pdf | Bin 0 -> 101060 bytes 9 files changed, 367 insertions(+) create mode 100644 src/Notes.md create mode 100644 src/README.md create mode 100644 src/parser.c create mode 100644 src/parser.h create mode 100755 src/test create mode 100644 src/test.c create mode 100644 src/tlv.c create mode 100644 src/tlv.h create mode 100644 src/énoncé.pdf diff --git a/src/Notes.md b/src/Notes.md new file mode 100644 index 0000000..4fd3e73 --- /dev/null +++ b/src/Notes.md @@ -0,0 +1 @@ +# Notes et recherches sur le projet diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..084ce3a --- /dev/null +++ b/src/README.md @@ -0,0 +1,5 @@ +# dazibao + +Le but de ce projet est d’implémenter un dazibao (« journal à grandes lettres »), semblable à +un « mur » de réseau social, mais de façon complètement distribuée. Le protocole est basé sur un +algorithme non-fiable d’inondation. \ No newline at end of file diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..5b1b48a --- /dev/null +++ b/src/parser.c @@ -0,0 +1,31 @@ +#include "parser.h" + +// retourne le type de commande à exécuter +cmd_token parse_cmd() { + char buf[198], cmd[5], arg[193]; + cmd_token token; + token.type = ERROR; + memset(token.arg, 0, 193); + + if(fgets(buf, 198, stdin) == NULL) + return token; + + // cmd sera le premier mot rencontré et arg la suite de mots après celui ci, si les deux variables ne sont pas remplies alors il y a une erreur + if(sscanf(buf, "%s %[^\t\n]", cmd, arg) != 2) + return token; + + if(strcmp("req", cmd) == 0) { + if(strcmp("neighbour", arg) == 0) + token.type = NEIGHBOUR_REQ; + else if(strcmp("network state", arg) == 0) + token.type = NETWORK_STATE_REQ; + else if(strcmp("node state", arg) == 0) + token.type = NODE_STATE_REQ; + } else if(strcmp("post", cmd) == 0) { + token.type = POST; + //arg[192] = 0; + strcpy(token.arg, arg); + } + + return token; +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..8663a5a --- /dev/null +++ b/src/parser.h @@ -0,0 +1,14 @@ +#include +#include + +typedef enum cmd_type { + NEIGHBOUR_REQ, NETWORK_STATE_REQ, NODE_STATE_REQ, POST, ERROR +} cmd_type; + +typedef struct cmd_token { + cmd_type type; + char arg[193]; +} cmd_token; + +// retourne le type de commande à exécuter +cmd_token parse_cmd(); diff --git a/src/test b/src/test new file mode 100755 index 0000000000000000000000000000000000000000..d50175c9ad896b2d3a20c825de2e00e829d65f3b GIT binary patch literal 8392 zcmeHMZEREL6@DEjU?6ea0Bs6vxE)AgTTB8WgtnA8U))RqI!IdB8g8A~PAnW7+1Fdb zrVS~j!)0`6>JQXu-~B;<_G4WTUD{5XM5TR{?nAU{r@)GusVmKxhB zEg`<##4LHMMM!FuS#b^O6WmLb?OMt#z)jbQ9w;JYDxrynlQb-|cu=K+Cct(a>$xhE zB^opM#?W%itLVcR%ir*Sr~cxV_{?LQe|5NN*Z0)Thp)WLw)jx>V;7w? zgC(>%o|+Q)eT(4R7s0t+mnB$7y}-96oH?fRC`me96h zy-Dfay?gX%EE5|_WQ|y+cTamVor?8F29oS!Xe6ByAG)PlQjxn;MzhEB$Fs=PLx_hc z)>Upzf_9=;bqlsCk)Rz^rmWS#`!GlnrLbRSWbvMN;pYYS1)TceU$3@sR^#?juG(!>#c#)zxJijy|(Q8-LfQovP?x9#q|x)$2Q0vhx}- zecI`YH;HOHX0`k~#=R8sbIel6RsDsl@lf^pCr$tTmpICKp6p*>#nanmTW`2ca#Rn+Ba zjlQNT8j1ICILA4#_b-u4>JO{;UyG9l*J$(uQSDUu#yV2b@}0AdlT)1^s_6@Jg~Ex+ z+`8%uogY@eIGLZKZd@Rh33fGolB9ood#ZERWAfUmzS*d@b(QJFsF*%P;#bjb__6S# z-T6O+`@%HV8=IGyLs`%CKsbN9JAboh6G!3GF5dP|zjwP(&`#Vif{lMf|9kScdh$0r z^6!NUtNx^oU-D=>{+zqcYvhal;m5=M;lpA5(wX`3exH_(=}%S>@5_zR<;t_x`yl^j z@YEyj{-hOw1!6I6{4d&h(xD}Hpr&p^O}q2*nZ_14S3d8F7=)9EDbQu zztQK&*JoHkN@;ziHjte4>qx(qb^U=Zf9>b0SA5xbTNr|D9yq~~Kj zDQx>VecC8yKI>K3A9$*)y=qC>7bqgj=Botr9K~4P<_~m@A~2KP!hJ za;Gzr3gt`mH&WYy4VcOQ@k~lZAeSJ5%hX)!BlH zv>A9QC&7K#2AektqojU$aosufD-_n76JMgRE}i&Nh52&gK81O9;>#4)lM`RAu{QSb}?gbTkRy5?(6) z#rpMq(@%~1EDkUOc*R z|7BT+*v2af51PK5U*9nCh3jE6;Q?AF7=Kt*5niJfuK#8eht@pZzX%>*1V2GIx9eV? z-zGdb@3vU`A>ntG-J@`BLhw_@$rJXY{5+^%5YFTCgp5y241P{DK{uiEaLDDbzjy0Gu zvPLc*4-KmM_a@yK(Fgg(DNC94Ob{B zQ!lFYjzfFHd%D}567zM9AbPXZJ2mm3b?jIA?w);Z;U0ZoSJ#2gUcEQm*3-#e%{RHh zY|f1MKfk@jq7frPCeGKs)>b^@HOa%@Ji-rq3!eSlV)kmi=#j4Tc~6gK)B12E73HVD z?tP>cO{DZ(Hb&i_kDLdP_=oG>}WsLv$jlLd*?EvcoDA9ZQiviy9eA z@}*cNn@Fb|j83vlEE!<~sU1xkDr9y%MAgtxnph(?O5eQlNGWYjRwy5;74c?m)LRt9mM%OZBhCSvHg!>F`YHPG z(FA+sXP`ln*$d7!V2xC<+ar$y-2g?QZu<_xZ&D78_RmTM6!9Zna;GnQ$)4{Ru%D0$ z=4)?U65ow%V1cj{g}zpLN+IuLecDa04FnyDoeD?t;GMnsBrWe?NBF+n zif(=IpuZ#AYMVXo8|URA1A3%l7Vw~N63S~2?c-j8|84RAEY>INFpr#&94)p=qGS)yJdx8BXJ4kid z+bPzbUA0x(ua$x0-x+{nOIG92AAR?f(t& Cabz$6 literal 0 HcmV?d00001 diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..8ac6a3e --- /dev/null +++ b/src/test.c @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() { + char a[1]; + a[0] = 0; + + if(fork() == 0) { + while(1) + printf("%d\n", a[0]); + } else { + a[1] = 1; + } +} \ No newline at end of file diff --git a/src/tlv.c b/src/tlv.c new file mode 100644 index 0000000..2284815 --- /dev/null +++ b/src/tlv.c @@ -0,0 +1,188 @@ +#include "tlv.h" + +// creer un tlv +int build_tlv(tlv *tlv, cmd_token token) { + switch(token.type) { + case NEIGHBOUR_REQ: + // a remplir + break; + case NETWORK_STATE_REQ: + // a remplir + break; + case NODE_STATE_REQ: + // a remplir + break; + case POST: + // a remplir + break; + case ERROR: + printf("Wrong format, use 'req {neighbour | network state | node state}' or 'post {message}'"); + break; + } +} + +int build_pad1(tlv *tlv) { + pad1 *new = (pad1*) malloc(sizeof(pad1)); + + if(new == NULL) + return -1; + + new->type = 0; + + tlv->pad1 = new; + + return 0; +} + +int build_padn(tlv *tlv, size_t len) { + padn *new = (padn*) malloc(sizeof(padn)); + + if(new == NULL) + return -1; + + new->type = 1; + new->length = len; + new->mbz = (char*) calloc(sizeof(char), len); + + tlv->padn = new; + + return 0; +} + +int build_neighbour_req(tlv *tlv) { + neighbour_req *new = (neighbour_req*) malloc(sizeof(neighbour_req)); + + if(new == NULL) + return -1; + + new->type = 2; + new->length = 0; + + tlv->neighbour_req = new; + + return 0; +} + +int build_neighbour(tlv *tlv, struct in6_addr ip, short port) { + neighbour *new = (neighbour*) malloc(sizeof(neighbour)); + + if(new == NULL) + return -1; + + new->type = 3; + new->length = 18; + new->ip = ip; + new->port = port; + + tlv->neighbour = new; + + return 0; +} + +int build_network_hash(tlv *tlv, char *hash) { + network_hash *new = (network_hash*) malloc(sizeof(network_hash)); + + if(new == NULL) + return -1; + + new->type = 4; + new->length = 16; + memcpy(new->network_hash, hash, 16); + + tlv->network_hash = new; + + return 0; +} + +int build_network_state_req(tlv *tlv) { + network_state_req *new = (network_state_req*) malloc(sizeof(network_state_req)); + + if(new == NULL) + return -1; + + new->type = 5; + new->length = 0; + + tlv->network_state_req = new; + + return 0; +} + +int build_node_hash(tlv *tlv, long node_id, short seqno, char *hash) { + node_hash *new = (node_hash*) malloc(sizeof(node_hash)); + + if(new == NULL) + return -1; + + new->type = 6; + new->length = 26; + new->node_id = node_id; + new->seqno = seqno; + memcpy(new->node_hash, hash, 16); + + tlv->node_hash = new; + + return 0; +} + +int build_node_state_req(tlv *tlv, long node_id) { + node_state_req *new = (node_state_req*) malloc(sizeof(node_state_req)); + + if(new == NULL) + return -1; + + new->type = 7; + new->length = 8; + new->node_id = node_id; + + tlv->node_state_req = new; + + return 0; +} + +int build_node_state(tlv *tlv, long node_id, short seqno, char *node_hash, char *data) { + node_state *new = (node_state*) malloc(sizeof(node_state)); + int len = strlen(data); + + if(new == NULL) + return -1; + + // en mettant cet octet à 0 on est surs de traiter un champ data de taille 192 max + if(len > 192) { + data[192] = 0; + len = 192; + } + + new->type = 8; + new->length = 26 + len; + new->node_id = node_id; + new->seqno = seqno; + memcpy(new->node_hash, node_hash, 16); + memcpy(new->data, data, len); + + tlv->node_state = new; + + return 0; +} + +int build_warning(tlv *tlv, char *message) { + warning *new = (warning*) malloc(sizeof(warning)); + int len = strlen(message); + + if(new == NULL) + return -1; + + // en mettant cet octet à 0 on est surs de traiter un champ message de taille 256 max + if(len > 256) { + message[256] = 0; + len = 256; + } + + new->type = 9; + new->length = len; + memcpy(new->message, message, len); + + tlv->warning = new; + + return 0; +} \ No newline at end of file diff --git a/src/tlv.h b/src/tlv.h new file mode 100644 index 0000000..35625ac --- /dev/null +++ b/src/tlv.h @@ -0,0 +1,113 @@ +#include +#include +#include +#include "parser.h" + +// 8 octets min (struct pointer 4 octets), 1024 octets max +typedef struct packet { + unsigned char magic; // 95 (si autre, ignorer) + unsigned char version; // 1 (si autre, ignorer) + short length; // 1020 max + char *body; +} packet; + +// 1 octet +typedef struct pad1 { + unsigned char type; +} pad1; + +// 2 octets min, 258 octets max (unsigned char 0 -> 255) +typedef struct padn { + unsigned char type; + unsigned char length; + char mbz[256]; +} padn; + +// 2 octets +typedef struct neighbour_req { + unsigned char type; + unsigned char length; +} neighbour_req; + +// 20 octets +typedef struct neighbour { + unsigned char type; + unsigned char length; + struct in6_addr ip; + short port; +} neighbour; + +// 18 octets +typedef struct network_hash { + unsigned char type; + unsigned char length; + char network_hash[16]; +} network_hash; + +// 2 octets +typedef struct network_state_req { + unsigned char type; + unsigned char length; +} network_state_req; + +// 28 octets +typedef struct node_hash { + unsigned char type; + unsigned char length; + long node_id; + short seqno; + char node_hash[16]; +} node_hash; + +// 10 octets +typedef struct node_state_req { + unsigned char type; + unsigned char length; + long node_id; +} node_state_req; + +// 28 octets min, 220 octets max (data 0 -> 192) +typedef struct node_state { + unsigned char type; + unsigned char length; + long node_id; + short seqno; + char node_hash[16]; + char data[192]; +} node_state; + +// 2 octets min, 258 ocets max (unsigned char 0 -> 255) +typedef struct warning { + unsigned char type; + unsigned char length; + char *message; +} warning; + +typedef union tlv { + pad1 *pad1; + padn *padn; + neighbour_req *neighbour_req; + neighbour *neighbour; + network_hash *network_hash; + network_state_req *network_state_req; + node_hash *node_hash; + node_state_req *node_state_req; + node_state *node_state; + warning *warning; +} tlv; + + +// creer un tlv +int build_tlv(tlv *tlv, cmd_token token); + +// creer un tlv specifique +int build_pad1(tlv *tlv); +int build_padn(tlv *tlv, size_t len); +int build_neighbour_req(tlv *tlv); +int build_neighbour(tlv *tlv, struct in6_addr ip, short seqno); +int build_network_hash(tlv *tlv, char *network_hash); +int build_network_state_req(tlv *tlv); +int build_node_hash(tlv *tlv, long node_id, short seqno, char *node_hash); +int build_node_state_req(tlv *tlv, long node_id); +int build_node_state(tlv *tlv, long node_id, short seqno, char *node_hash, char *data); +int build_warning(tlv *tlv, char *message); \ No newline at end of file diff --git a/src/énoncé.pdf b/src/énoncé.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0d725fe367d238e73efb94d38b35069576a73513 GIT binary patch literal 101060 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]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%0D-#`zX$uCjO--zS@H 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 Kq@wa-(EkJReAZ+D literal 0 HcmV?d00001 From ac657e4bbdeda64b45b67f50111266dd6a1e839e Mon Sep 17 00:00:00 2001 From: n07070 Date: Tue, 24 Mar 2020 18:19:35 +0100 Subject: [PATCH 12/26] Added tlv_list and check_header --- src/node.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++------ src/node.h | 13 ++++-- src/tlv.h | 5 +++ 3 files changed, 128 insertions(+), 18 deletions(-) diff --git a/src/node.c b/src/node.c index f826554..51b0ba1 100644 --- a/src/node.c +++ b/src/node.c @@ -1,15 +1,124 @@ // This is the main file of the Dazibao project. It represents the node, and // handles all of the main logic, including the network connexions. +#include +#include +#include +#include +#include +#include + +#include "tlv.h" +#include "node.h" -// Will return a packet when we receive one that's valid. -packet listen_for_packets(){ +// For every packet recivied, +// then we make sure it's conform +// We then extract the data from it to make it easy to work with +int check_header(char * req[], int buffer_size, packet * packet_to_return){ + packet * packet_to_return = (packet*) req; + + // We need to check a few things ; + // The first byte must be worth 95, + if (packet_to_return->magic != 95) { + perror(">> The magic number of the packet is no good."); + return -1; + } + + // The second byte must be worth 1, + if (packet_to_return->version != 1) { + perror(">> The version number of the packet is no good."); + return -1; + } + + if (packet_to_return.length + 4 > buffer_size ) { + perror(">> The packet length is bigger than the UDP datagram, which is not possible with the current laws of physics."); + return -1; + } + + return 0; } +// We listen forever for new paquets; +void listen_for_packets(){ + + // Create new socket for UDP + int s = socket(AF_INET6, SOCK_DGRAM, 0); + + if(s < 0) { + perror(">> Error, cannot create socket."); + perror(">> Exiting...") + exit(1); + } + + struct sockaddr_in6 server; + memset(&server, 0, sizeof(server)); + + server.sin6_family = AF_INET6; + server.sin6_port = htons(LISTEN_PORT); + int rc = bind(s, (struct sockaddr*)&server, sizeof(server)); + if(rc < 0) { + perror(">> Error, cannot bind socket to choosen port."); + perror(">> Exiting...") + exit(1); + } + + // A paquet has at most a length of 1024 bytes + char req[1024]; + struct sockaddr_in6 client; + struct iovec io = { .iov_len = 1024, .iov_base = req }; + struct msghdr msg_to_receive = { + .msg_name = &client, + .msg_namelen = sizeof(client), + .msg_iov = &io, + .msg_iovlen = 1 + }; + while(1){ + memset(req, '\0', 1024); + + struct sockaddr_in6 client; + unsigned int client_len = sizeof(client); + + rc = recvmsg(s, &msg_to_receive, 0); + if(rc < 0) { + perror(">> Error while receiving a new datagram."); + perror(">> Ignoring, continuing...") + continue; + } + + printf(">> New paquet received :\n"); + printf("%s\n", req); + + // We verify the received packet is well formated, + // and we return it in the struct designed to work with it. + struct packet * formated_rec_datagram; + if(check_header(&req, 1024, formated_rec_datagram) < 0){ + perror(">> Error while checking the header, aborting this packet, by choice, and conviction."); + continue; + } + + struct tlv_list received_tlvs = validate_tlvs(formated_rec_datagram); + } +} + + + + +// If the sender is not in the neighbourhood, and we have 15 neighbours, we +// ignore the packet. Otherwise, we add him to the neighbourhood, marked as +// temporary. +int update_neighbours(){ + +}; + +// We then look at the differents TLVs in the packet. +void work_with_tlvs(){ + +}; + // We need to make sure the TLV announces a length that will no go onto // another tlv, as we might end up reading bullshit. -int validate_tlvs(union tlv tlv_to_validate){ +tlv_list validate_tlvs(packet packet_to_validate){ } @@ -54,7 +163,7 @@ void work_with_tlvs(struct tlvs_list receivied_tlvs){ int main(int argc, char const *argv[]) { int continue = 1; - + while(continue){ // We create the neighbourhood table @@ -65,17 +174,8 @@ int main(int argc, char const *argv[]) { // Listen for incoming packets listen_for_packets(); - // For every packet recivied, we fork, - // then we make sure it's conform - // We then extract the data from it to make it easy to work with - check_header(); - // If the sender is not in the neighbourhood, and we have 15 neighbours, we - // ignore the packet. Otherwise, we add him to the neighbourhood, marked as - // temporary. - update_neighbours(); - // We then look at the differents TLVs in the packet. - work_with_tlvs(); + // This is in it's own fork. time_t delay = time(NULL) + 20; while(! (delay < time(NULL)){ // Theses functions are there for general book-keeping,and run in there own diff --git a/src/node.h b/src/node.h index b871865..ad988fd 100644 --- a/src/node.h +++ b/src/node.h @@ -3,6 +3,11 @@ #ifndef NODE_H #define NODE_H +#include "tlv.h" + +// On which port do we listen to +#define LISTEN_PORT 1212 + // The node ID #define NODE_ID 203242402519736214145149136169422092269247115186189140178187251487819615911212154252117172522111472481308026129190139512419121015210238252292031613214452118122204415160254 @@ -40,14 +45,14 @@ typedef struct message { // fonctions signatures void listen_for_packets(); -void check_header(); +int check_header(char * received_datagram[], int len, packet pack); -void update_neighbours(); +int validate_tlvs(packet * pack, tlv_list * tlv_l); + +int update_neighbours(); void work_with_tlvs(); -int validate_tlvs(); - // threaded functions void t_ask_for_more_peers(); diff --git a/src/tlv.h b/src/tlv.h index 33083b8..61df5cb 100644 --- a/src/tlv.h +++ b/src/tlv.h @@ -99,6 +99,11 @@ typedef union tlv { warning *warning; } tlv; +typedef struct tlv_list { + union tlv one_tlv; + struct tlv_list * next; +} tlv_list; + // creer un tlv int build_tlv(tlv *tlv, cmd_token token); From 13bc1e327af9a38c5979ac100a91627a25767afb Mon Sep 17 00:00:00 2001 From: gonzalef Date: Tue, 24 Mar 2020 18:23:18 +0100 Subject: [PATCH 13/26] djgn --- src/node.c | 34 +++++++++++++++++++++++++++++++++- src/node.h | 2 +- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/node.c b/src/node.c index f826554..79aa5e9 100644 --- a/src/node.c +++ b/src/node.c @@ -9,8 +9,40 @@ packet listen_for_packets(){ // We need to make sure the TLV announces a length that will no go onto // another tlv, as we might end up reading bullshit. -int validate_tlvs(union tlv tlv_to_validate){ +int validate_tlv(unsigned char *data, int pos){ + switch(data[pos]) { + case 0: + break; + case 1: + break; + case 2: + if(data[pos+1] != 0) return 0; + break; + case 3: + if(data[pos+1] != 18) return 0; + break; + case 4: + if(data[pos+1] != 16) return 0; + break; + case 5: + if(data[pos+1] != 0) return 0; + break; + case 6: + if(data[pos+1] != 26) return 0; + break; + case 7: + if(data[pos+1] != 8) return 0; + break; + case 8: + if(data[pos+1] < 26 || data[pos+1] > 218) return 0; + break; + case 9: + break; + default: + return 0; + } + return 1; } void work_with_tlvs(struct tlvs_list receivied_tlvs){ diff --git a/src/node.h b/src/node.h index b871865..f7680ad 100644 --- a/src/node.h +++ b/src/node.h @@ -46,7 +46,7 @@ void update_neighbours(); void work_with_tlvs(); -int validate_tlvs(); +int validate_tlv(unsigned char *data, int pos); // threaded functions From 882b75433aa6f3c59c5571e217eefda6f2692103 Mon Sep 17 00:00:00 2001 From: n07070 Date: Tue, 24 Mar 2020 18:27:31 +0100 Subject: [PATCH 14/26] Modif valid tlv --- src/node.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/node.c b/src/node.c index 51b0ba1..c47bc62 100644 --- a/src/node.c +++ b/src/node.c @@ -10,6 +10,11 @@ #include "tlv.h" #include "node.h" +// We need to make sure the TLV announces a length that will no go onto +// another tlv, as we might end up reading bullshit. +tlv_list validate_tlvs(packet packet_to_validate){ + +} // For every packet recivied, // then we make sure it's conform @@ -97,7 +102,11 @@ void listen_for_packets(){ continue; } - struct tlv_list received_tlvs = validate_tlvs(formated_rec_datagram); + struct tlv_list received_tlvs; + if (validate_tlvs(formated_rec_datagram) < 0) { + + /* code */ + } } } @@ -116,11 +125,7 @@ void work_with_tlvs(){ }; -// We need to make sure the TLV announces a length that will no go onto -// another tlv, as we might end up reading bullshit. -tlv_list validate_tlvs(packet packet_to_validate){ -} void work_with_tlvs(struct tlvs_list receivied_tlvs){ From 8adf0d2bec1282cbbf0ea9a7120fe20958aa0d82 Mon Sep 17 00:00:00 2001 From: n07070 Date: Tue, 24 Mar 2020 19:39:16 +0100 Subject: [PATCH 15/26] Added work_with_tlvs --- src/node.c | 205 ++++++++++++++++++++++++++++++++++------------------- src/node.h | 8 ++- 2 files changed, 138 insertions(+), 75 deletions(-) diff --git a/src/node.c b/src/node.c index c47bc62..31271c7 100644 --- a/src/node.c +++ b/src/node.c @@ -6,20 +6,124 @@ #include #include #include +#include #include "tlv.h" #include "node.h" +// We then look at the differents TLVs in the packet. +int work_with_tlvs(struct packet received_packet, char * data_from_packet[], struct sockaddr_in6 sender){ + + // For every TLV, + // We make sure the TLV is legal. + int number_of_good_tlvs = 0; + int padding_to_next_tlv = 0; + int end_analysis = 0; + while (!end_analysis) { + + // Switch + switch (data_from_packet[4 + padding_to_next_tlv]) { + case 0: + // Padding tlv, we can ignore, pass to next tlv. + padding_to_next_tlv++; + number_of_good_tlvs++; + break; + case 1: + // PadN TLV, we can ignore, pass to next tlv. + // Add the length to get to next TLV + padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; + number_of_good_tlvs++; + break; + case 2: + // Neighbour Request TLV + padding_to_next_tlv += 2; + number_of_good_tlvs++; + break; + case 3: + // Neighbour TLV + padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; + number_of_good_tlvs++; + break; + case 4: + // Network hash TLV + padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; + number_of_good_tlvs++; + + // TLV Network Hash + // We calculate a network hash, + + // We compare both, + + // If they differ, we send a TLV Network State Request + // back to the sender.$ + + break; + case 5: + // Network State Request + padding_to_next_tlv += 2; + number_of_good_tlvs++; + + // TLV Network State Request + // We check our neighbourhood, and for each peer, we send back + // to the sender a TLV Node Hash + break; + case 6: + // Node Hash + padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; + number_of_good_tlvs++; + // TLV Node hash + // We get a hash _h_ for the node _l_ + // If we don't have an entry for _l_, or if we have the same one as the + // on we just receivied, we send out a TLV Node State Request back. + break; + case 7: + // Node State Request + padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; + number_of_good_tlvs++; + break; + case 8: + // Node State + padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; + number_of_good_tlvs++; + // TLV Node State + // We get a hash _h_, sequence number _s_, data _d_ for node _l_. + // We compute a network hash, + // We compare the hash, if they differ, then with l',s',d' our data and + // h' the corresponding hash, + // if _l_ is our own node id, then + // if s >> s' then we update our sequence number to s ⊕ 1 mod 2^16 + // If it's another's node id, then + // If there is no entry for the sender, + // we store the entry in our data table. + break; + case 9: + // Warning + padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; + number_of_good_tlvs++; + break; + default: + return -number_of_good_tlvs; + // We have a faulty TLV, thus we ignore and return. + } + // If we arrived at the end of the packet + // or if the length is not possible + if (received_packet.length <= padding_to_next_tlv + 4) { + end_analysis = 1; + } + } + return 0; +} + // We need to make sure the TLV announces a length that will no go onto // another tlv, as we might end up reading bullshit. -tlv_list validate_tlvs(packet packet_to_validate){ +int validate_tlvs(struct packet packet_to_validate){ } // For every packet recivied, // then we make sure it's conform // We then extract the data from it to make it easy to work with -int check_header(char * req[], int buffer_size, packet * packet_to_return){ +int check_header(char * req[], int buffer_size, struct packet * packet_to_return){ packet * packet_to_return = (packet*) req; @@ -44,6 +148,13 @@ int check_header(char * req[], int buffer_size, packet * packet_to_return){ return 0; } +// If the sender is not in the neighbourhood, and we have 15 neighbours, we +// ignore the packet. Otherwise, we add him to the neighbourhood, marked as +// temporary. +int update_neighbours(){ + return 0; +}; + // We listen forever for new paquets; void listen_for_packets(){ @@ -52,7 +163,7 @@ void listen_for_packets(){ if(s < 0) { perror(">> Error, cannot create socket."); - perror(">> Exiting...") + perror(">> Exiting..."); exit(1); } @@ -64,36 +175,35 @@ void listen_for_packets(){ int rc = bind(s, (struct sockaddr*)&server, sizeof(server)); if(rc < 0) { perror(">> Error, cannot bind socket to choosen port."); - perror(">> Exiting...") + perror(">> Exiting..."); exit(1); } // A paquet has at most a length of 1024 bytes char req[1024]; - struct sockaddr_in6 client; + struct sockaddr_in6 sender; struct iovec io = { .iov_len = 1024, .iov_base = req }; struct msghdr msg_to_receive = { - .msg_name = &client, - .msg_namelen = sizeof(client), + .msg_name = &sender, + .msg_namelen = sizeof(sender), .msg_iov = &io, .msg_iovlen = 1 }; while(1){ memset(req, '\0', 1024); - struct sockaddr_in6 client; - unsigned int client_len = sizeof(client); - rc = recvmsg(s, &msg_to_receive, 0); if(rc < 0) { perror(">> Error while receiving a new datagram."); - perror(">> Ignoring, continuing...") + perror(">> Ignoring, continuing..."); continue; } printf(">> New paquet received :\n"); printf("%s\n", req); + // TODO : Here, we need to fork. + // We verify the received packet is well formated, // and we return it in the struct designed to work with it. struct packet * formated_rec_datagram; @@ -102,74 +212,25 @@ void listen_for_packets(){ continue; } - struct tlv_list received_tlvs; - if (validate_tlvs(formated_rec_datagram) < 0) { + // TODO : Add the neighbour check here. - /* code */ + // struct tlv_list received_tlvs; + // if (validate_tlvs(formated_rec_datagram) < 0) + + int nbr_success_tlv = work_with_tlvs(formated_rec_datagram, &req, sender); + if (nbr_success_tlv < 0){ + perror(">> Error while treating the TLVs of the packet."); + printf(">> Managed to deal with %i TLVs\n", -nbr_success_tlv ); + } else { + printf(">> Done working with the TLVs of the packet, listenin for new packets.\n"); } } } - - - -// If the sender is not in the neighbourhood, and we have 15 neighbours, we -// ignore the packet. Otherwise, we add him to the neighbourhood, marked as -// temporary. -int update_neighbours(){ - -}; - -// We then look at the differents TLVs in the packet. -void work_with_tlvs(){ - -}; - - - -void work_with_tlvs(struct tlvs_list receivied_tlvs){ - - // For every TLV, - // We make sure the TLV is legal. - if(!validate_tlvs(tlv)){ - perror(">> Invalid TLV receivied, it will be ignored."); - } - - // Switch - - // TLV Network Hash - // We calculate a network hash, - - // We compare both, - - // If they differ, we send a TLV Network State Request - // back to the sender. - - // TLV Network State Request - // We check our neighbourhood, and for each peer, we send back - // to the sender a TLV Node Hash - - // TLV Node hash - // We get a hash _h_ for the node _l_ - // If we don't have an entry for _l_, or if we have the same one as the - // on we just receivied, we send out a TLV Node State Request back. - - // TLV Node State - // We get a hash _h_, sequence number _s_, data _d_ for node _l_. - // We compute a network hash, - // We compare the hash, if they differ, then with l',s',d' our data and - // h' the corresponding hash, - // if _l_ is our own node id, then - // if s >> s' then we update our sequence number to s ⊕ 1 mod 2^16 - // If it's another's node id, then - // If there is no entry for the sender, - // we store the entry in our data table. -} - int main(int argc, char const *argv[]) { int continue = 1; - while(continue){ + while(continue != 0){ // We create the neighbourhood table neighbour_peer neighbour_list[NEIGHBOUR_MAX]; @@ -182,7 +243,7 @@ int main(int argc, char const *argv[]) { // This is in it's own fork. time_t delay = time(NULL) + 20; - while(! (delay < time(NULL)){ + while(! (delay < time(NULL))) { // Theses functions are there for general book-keeping,and run in there own // thread, being run every 20 seconds. // Every 20 sec, if we have less than 5 neighbours, we ask for more peers diff --git a/src/node.h b/src/node.h index ad988fd..d0bf8fb 100644 --- a/src/node.h +++ b/src/node.h @@ -3,6 +3,8 @@ #ifndef NODE_H #define NODE_H +#include +#include #include "tlv.h" // On which port do we listen to @@ -45,13 +47,13 @@ typedef struct message { // fonctions signatures void listen_for_packets(); -int check_header(char * received_datagram[], int len, packet pack); +int check_header(char * received_datagram[], int len, struct packet pack); -int validate_tlvs(packet * pack, tlv_list * tlv_l); +int validate_tlvs(struct packet * pack, struct tlv_list * tlv_l); int update_neighbours(); -void work_with_tlvs(); +int work_with_tlvs(struct packet received_packet, char * data_from_packet[], struct sockaddr_in6 sender); // threaded functions From 7db155e671f3a6cd5299216e70fc511fac68d6de Mon Sep 17 00:00:00 2001 From: gonzalef Date: Tue, 31 Mar 2020 18:28:12 +0200 Subject: [PATCH 16/26] hash --- src/Notes.md | 3 +++ src/hash.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hash.h | 23 ++++++++++++++++++++ src/node.c | 8 ------- src/node.h | 15 ++++++++++++- 5 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 src/hash.c create mode 100644 src/hash.h diff --git a/src/Notes.md b/src/Notes.md index 4fd3e73..5137b8a 100644 --- a/src/Notes.md +++ b/src/Notes.md @@ -1 +1,4 @@ # Notes et recherches sur le projet + +Telecharger la librarie OpenSSl avec 'sudo apt-get install libssl-dev' +Utiliser 'gcc -o {exec} {fichier.c} -lssl -lcrypto' pour utiliser la librarie OpenSSL \ No newline at end of file diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000..287e2c4 --- /dev/null +++ b/src/hash.c @@ -0,0 +1,60 @@ +#include "hash.h" + +// Hash a single data +void hash_data(pub_data *data, unsigned char *buf) { + // All three fields are concatenated into a single buffer + int totlen = data->length + 10; + unsigned char concat[totlen]; + concat_data(data, concat); + + // The resulting buf is hashed and put into a buffer + unsigned char hash[SHA256_DIGEST_LENGTH]; + SHA256(concat, totlen, hash); + + // Put truncated hash into buf + hash_trunc(hash, buf); +} + +// Hash every data contained in data_list then return a network hash +void hash_network(list *data_list, unsigned char *buf) { + unsigned char *concat = (unsigned char*) malloc(0); + unsigned char hash[SHA256_DIGEST_LENGTH]; + int totlen = 0; + list *tmp = data_list; + + // Hash every known data and concatenate it to buffer concat + while(tmp != NULL) { + hash_data((pub_data*) tmp->data, hash); + concat_hash(concat, hash, totlen); + totlen += 16; + tmp = tmp->next; + } + + // Hash all of concat to obtain the network hash + SHA256(concat, totlen, hash); + + // Put truncated hash into buf + hash_trunc(hash, buf); + + // Get rid of concat + free(concat); +} + +// Truncate 32 octet hash to 16 octets +void hash_trunc(unsigned char *hash32oct, unsigned char *buf) { + // Copy the first 16 octets from hash32oct + memcpy(buf, hash32oct, 16); +} + +// Concat all fields of data and put them in buf +void concat_data(pub_data *data, unsigned char *buf) { + memcpy(buf, &(data->id), 8); + memcpy(buf+8, &(data->seqno), 2); + memcpy(buf+10, data->data, data->length); +} + +// Concat hash2 to hash1 (hash1 is modified) +void concat_hash(unsigned char *hash1, unsigned char *hash2, size_t size) { + hash1 = (unsigned char*) realloc(hash1, size + 16); + memcpy(hash1+size, hash2, 16); +} \ No newline at end of file diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..358c7b5 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,23 @@ +#ifndef HASH_H +#define HASH_H + +#include +#include "node.h" +#include "tlv.h" + +// Hash a single data +void hash_data(pub_data *data, unsigned char *buf); + +// Hash every data contained in data_list then return a network hash +void hash_network(list *data_list, unsigned char *buf); + +// Truncate 32 octet hash to 16 octets +void hash_trunc(unsigned char *hash256bit, unsigned char *buf); + +// Concat all fields of data and put them in buf +void concat_data(pub_data *data, unsigned char *buf); + +// Concat hash2 to hash1 (hash1 is modified) +void concat_hash(unsigned char *hash1, unsigned char *hash2, size_t size); + +#endif \ No newline at end of file diff --git a/src/node.c b/src/node.c index fe718a3..32f7894 100644 --- a/src/node.c +++ b/src/node.c @@ -1,13 +1,5 @@ // This is the main file of the Dazibao project. It represents the node, and // handles all of the main logic, including the network connexions. -#include -#include -#include -#include -#include -#include - -#include "tlv.h" #include "node.h" // For every packet recivied, diff --git a/src/node.h b/src/node.h index db3eb83..d520ad9 100644 --- a/src/node.h +++ b/src/node.h @@ -3,8 +3,15 @@ #ifndef NODE_H #define NODE_H +#include +#include +#include +#include +#include +#include + #include "tlv.h" -#include "misc.h" +#include "hash.h" // On which port do we listen to #define LISTEN_PORT 1212 @@ -42,6 +49,12 @@ typedef struct pub_data { char *data; } pub_data; +// General list +typedef struct list { + void *data; + void *next; +} list; + // static lists static list *data_list; static list *neighbour_list; From a82c8cb60a6dc0c97cc0afbaed5398e48040bd4a Mon Sep 17 00:00:00 2001 From: gonzalef Date: Tue, 31 Mar 2020 18:30:06 +0200 Subject: [PATCH 17/26] delete misc --- src/misc.c | 27 --------------------------- src/misc.h | 23 ----------------------- 2 files changed, 50 deletions(-) delete mode 100644 src/misc.c delete mode 100644 src/misc.h diff --git a/src/misc.c b/src/misc.c deleted file mode 100644 index 45de1b2..0000000 --- a/src/misc.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "misc.h" - -char *hash_data(pub_data *data) { - // Copy data to buf to pass it as string argument - char cpy_data[data->length + 1]; - memcpy(cpy_data, data->data, data->length); - cpy_data[data->length] = '\0'; - - // All three fields are concatenated into a single buf - char *concat_data = concat(3, data->id, data->seqno, data->data); - - // The resulting buf is hashed - char *hash = SHA256(concat_data, data->length + 10, 0); - - // Free the concatenated buf - free(concat_data); - - return hash_trunc(hash); -} - -char *hash_network(list *data_list) { - -} - -char *hash_trunc(char *hash256bit); - -char *concat(int argc, ...); \ No newline at end of file diff --git a/src/misc.h b/src/misc.h deleted file mode 100644 index 737b5d0..0000000 --- a/src/misc.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef MISC_H -#define MISC_H - -#include -#include -#include "node.h" -#include "tlv.h" - -// General list -typedef struct list { - void *data; - void *next; -} list; - -// Hash functions -char *hash_data(pub_data *data); -char *hash_network(list *data_list); -char *hash_trunc(char *hash256bit); -char *concat(int argc, ...); - - - -#endif \ No newline at end of file From 12745c0100dc20092e79249ad495f7e2b696299c Mon Sep 17 00:00:00 2001 From: gonzalef Date: Tue, 31 Mar 2020 19:16:23 +0200 Subject: [PATCH 18/26] build tlvs #NOT FINISHED# --- src/node.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- src/node.h | 7 ++++--- src/tlv.h | 9 +++++---- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/node.c b/src/node.c index 32f7894..8da0b90 100644 --- a/src/node.c +++ b/src/node.c @@ -175,21 +175,33 @@ void work_with_tlvs(char *data, short packet_len){ pos += tlv_len + 2; break; case 2: - // We received a neighbour request so a neighbor tlv has to be sent + // We received a neighbour request so a random neighbor tlv has to be sent tlv_len = data[pos+1]; pos += tlv_len + 2; - // NOT FINISHED - Where are ip and seqno stored? - build_neighbour(&tmp_tlv, ip, seqno); + // Send a neighbour tlv + neighbour_peer *random = get_random_neighbour(); + build_neighbour(&tmp_tlv, random->ip, random->port); + // NOT FINISHED - What packet is it added to? add_tlv(packet, &tmp_tlv, 3); break; case 3: // We received a neighbour tlv so a tlv network hash is sent to that address + neighbour* cur_tlv = ((neighbour*) data) + pos; + struct in6_addr ip = cur_tlv->ip; + short port = cur_tlv->port; + tlv_len = data[pos+1]; pos += tlv_len + 2; - tlv.neighbour = (neighbour*) (data + pos); + // Build network hash + unsigned char hash[16]; + hash_network(neighbour_list, hash); + build_network_hash(&tmp_tlv, hash); + + // NOT FINISHED - What packet is it added to? + add_tlv(packet, &tmp_tlv, 4); break; case 4: // We reveived a network hash tlv so @@ -274,6 +286,36 @@ void work_with_tlvs(struct tlvs_list receivied_tlvs){ // we store the entry in our data table. } +// Get list length +int len_list(list *l) { + int len = 0; + list *tmp = l; + + while(tmp != NULL) { + tmp = tmp->next; + len++; + } + + return len; +} + +// Get a random neighbour +neighbour_peer *get_random_neighbour() { + // Get a random number + time_t t; + srand((unsigned) time(&t)); + int n = rand() % len_list(neighbour_list); + + // Get nth neighbour + list *tmp = neighbour_list; + + for(int i=0; inext; + } + + return (neighbour_peer*) tmp->data; +} + int main(int argc, const char *argv[]) { int cont = 1; diff --git a/src/node.h b/src/node.h index d520ad9..ba52057 100644 --- a/src/node.h +++ b/src/node.h @@ -4,6 +4,7 @@ #define NODE_H #include +#include #include #include #include @@ -55,7 +56,7 @@ typedef struct list { void *next; } list; -// static lists +// Static variables static list *data_list; static list *neighbour_list; @@ -83,8 +84,8 @@ void t_update_neighbours(); void t_get_network_state(); // Helper functions -char * hash(); +int len_list(list *l); -short * get_seq_no(short s, int n); +neighbour_peer *get_random_neighbour(); #endif diff --git a/src/tlv.h b/src/tlv.h index a49a8f7..c9a3d00 100644 --- a/src/tlv.h +++ b/src/tlv.h @@ -1,10 +1,11 @@ +#ifndef TLV_H +#define TLV_H + #include #include #include #include "parser.h" - -#ifndef TLV_H -#define TLV_H +#include "hash.h" #define LEN_NEIGHBOUR_REQ 0 #define LEN_NEIGHBOUR 18 @@ -115,7 +116,7 @@ int build_tlv(tlv *tlv, cmd_token token); int build_pad1(tlv *tlv); int build_padn(tlv *tlv, size_t len); int build_neighbour_req(tlv *tlv); -int build_neighbour(tlv *tlv, struct in6_addr ip, short seqno); +int build_neighbour(tlv *tlv, struct in6_addr ip, short port); int build_network_hash(tlv *tlv, char *network_hash); int build_network_state_req(tlv *tlv); int build_node_hash(tlv *tlv, long node_id, short seqno, char *node_hash); From 728283acddba4c5ba8e70e105f6c76b2dc5b3a0c Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 17:12:24 +0200 Subject: [PATCH 19/26] =?UTF-8?q?R=C3=A9solution=20du=20merge=20Changement?= =?UTF-8?q?=20de=20place=20des=20fonctions=20Ajout=20d'une=20section=20pou?= =?UTF-8?q?r=20les=20fonctions=20utilitaires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/node.c | 374 ++++++++++++++++++----------------------------------- 1 file changed, 125 insertions(+), 249 deletions(-) diff --git a/src/node.c b/src/node.c index a547a31..499651d 100644 --- a/src/node.c +++ b/src/node.c @@ -11,226 +11,39 @@ #include "tlv.h" #include "node.h" -// We then look at the differents TLVs in the packet. -int work_with_tlvs(struct packet received_packet, char * data_from_packet[], struct sockaddr_in6 sender){ +/* ---- Fonctions utilitaires ---- */ - // For every TLV, - // We make sure the TLV is legal. - int number_of_good_tlvs = 0; - int padding_to_next_tlv = 0; - int end_analysis = 0; - while (!end_analysis) { +// Get list length +int len_list(list *l) { + int len = 0; + list *tmp = l; - // Switch - switch (data_from_packet[4 + padding_to_next_tlv]) { - case 0: - // Padding tlv, we can ignore, pass to next tlv. - padding_to_next_tlv++; - number_of_good_tlvs++; - break; - case 1: - // PadN TLV, we can ignore, pass to next tlv. - // Add the length to get to next TLV - padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; - number_of_good_tlvs++; - break; - case 2: - // Neighbour Request TLV - padding_to_next_tlv += 2; - number_of_good_tlvs++; - break; - case 3: - // Neighbour TLV - padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; - number_of_good_tlvs++; - break; - case 4: - // Network hash TLV - padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; - number_of_good_tlvs++; + while(tmp != NULL) { + tmp = tmp->next; + len++; + } - // TLV Network Hash - // We calculate a network hash, - - // We compare both, - - // If they differ, we send a TLV Network State Request - // back to the sender.$ - - break; - case 5: - // Network State Request - padding_to_next_tlv += 2; - number_of_good_tlvs++; - - // TLV Network State Request - // We check our neighbourhood, and for each peer, we send back - // to the sender a TLV Node Hash - break; - case 6: - // Node Hash - padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; - number_of_good_tlvs++; - // TLV Node hash - // We get a hash _h_ for the node _l_ - // If we don't have an entry for _l_, or if we have the same one as the - // on we just receivied, we send out a TLV Node State Request back. - break; - case 7: - // Node State Request - padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; - number_of_good_tlvs++; - break; - case 8: - // Node State - padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; - number_of_good_tlvs++; - // TLV Node State - // We get a hash _h_, sequence number _s_, data _d_ for node _l_. - // We compute a network hash, - // We compare the hash, if they differ, then with l',s',d' our data and - // h' the corresponding hash, - // if _l_ is our own node id, then - // if s >> s' then we update our sequence number to s ⊕ 1 mod 2^16 - // If it's another's node id, then - // If there is no entry for the sender, - // we store the entry in our data table. - break; - case 9: - // Warning - padding_to_next_tlv += data_from_packet[4 + padding_to_next_tlv + 1]; - number_of_good_tlvs++; - break; - default: - return -number_of_good_tlvs; - // We have a faulty TLV, thus we ignore and return. - } - // If we arrived at the end of the packet - // or if the length is not possible - if (received_packet.length <= padding_to_next_tlv + 4) { - end_analysis = 1; - } - } - return 0; + return len; } -// We need to make sure the TLV announces a length that will no go onto -// another tlv, as we might end up reading bullshit. -int validate_tlvs(struct packet packet_to_validate){ +// Get a random neighbour +neighbour_peer *get_random_neighbour() { + // Get a random number + time_t t; + srand((unsigned) time(&t)); + int n = rand() % len_list(neighbour_list); + // Get nth neighbour + list *tmp = neighbour_list; + + for(int i=0; inext; + } + + return (neighbour_peer*) tmp->data; } -// For every packet recivied, -// then we make sure it's conform -// We then extract the data from it to make it easy to work with -int check_header(char * req[], int buffer_size, struct packet * packet_to_return){ - - packet * packet_to_return = (packet*) req; - - // We need to check a few things ; - // The first byte must be worth 95, - if (packet_to_return->magic != 95) { - perror(">> The magic number of the packet is no good."); - return -1; - } - - // The second byte must be worth 1, - if (packet_to_return->version != 1) { - perror(">> The version number of the packet is no good."); - return -1; - } - - if (packet_to_return.length + 4 > buffer_size ) { - perror(">> The packet length is bigger than the UDP datagram, which is not possible with the current laws of physics."); - return -1; - } - - return 0; -} - -// If the sender is not in the neighbourhood, and we have 15 neighbours, we -// ignore the packet. Otherwise, we add him to the neighbourhood, marked as -// temporary. -int update_neighbours(){ - return 0; -}; - -// We listen forever for new paquets; -void listen_for_packets(){ - - // Create new socket for UDP - int s = socket(AF_INET6, SOCK_DGRAM, 0); - - if(s < 0) { - perror(">> Error, cannot create socket."); - perror(">> Exiting..."); - exit(1); - } - - struct sockaddr_in6 server; - memset(&server, 0, sizeof(server)); - - server.sin6_family = AF_INET6; - server.sin6_port = htons(LISTEN_PORT); - int rc = bind(s, (struct sockaddr*)&server, sizeof(server)); - if(rc < 0) { - perror(">> Error, cannot bind socket to choosen port."); - perror(">> Exiting..."); - exit(1); - } - - // A paquet has at most a length of 1024 bytes - char req[1024]; - struct sockaddr_in6 sender; - struct iovec io = { .iov_len = 1024, .iov_base = req }; - struct msghdr msg_to_receive = { - .msg_name = &sender, - .msg_namelen = sizeof(sender), - .msg_iov = &io, - .msg_iovlen = 1 - }; - while(1){ - memset(req, '\0', 1024); - - rc = recvmsg(s, &msg_to_receive, 0); - if(rc < 0) { - perror(">> Error while receiving a new datagram."); - perror(">> Ignoring, continuing..."); - continue; - } - - printf(">> New paquet received :\n"); - printf("%s\n", req); - - // TODO : Here, we need to fork. - - // We verify the received packet is well formated, - // and we return it in the struct designed to work with it. - struct packet * formated_rec_datagram; - if(check_header(&req, 1024, formated_rec_datagram) < 0){ - perror(">> Error while checking the header, aborting this packet, by choice, and conviction."); - continue; - } - - // TODO : Add the neighbour check here. - - // struct tlv_list received_tlvs; - // if (validate_tlvs(formated_rec_datagram) < 0) - -<<<<<<< HEAD - int nbr_success_tlv = work_with_tlvs(formated_rec_datagram, &req, sender); - if (nbr_success_tlv < 0){ - perror(">> Error while treating the TLVs of the packet."); - printf(">> Managed to deal with %i TLVs\n", -nbr_success_tlv ); - } else { - printf(">> Done working with the TLVs of the packet, listenin for new packets.\n"); -======= -// If the sender is not in the neighbourhood, and we have 15 neighbours, we -// ignore the packet. Otherwise, we add him to the neighbourhood, marked as -// temporary. -int update_neighbours(){ - -}; +/* ---- Fin fonctions utilitaires ---- */ // We need to make sure the TLV announces a length that will no go onto // another tlv, as we might end up reading bullshit. @@ -283,8 +96,43 @@ int validate_tlv(char *data, int pos, short packet_len){ } } +// For every packet recivied, +// then we make sure it's conform +// We then extract the data from it to make it easy to work with +int check_header(char * req[], int buffer_size, struct packet * packet_to_return){ + + packet * packet_to_return = (packet*) req; + + // We need to check a few things ; + // The first byte must be worth 95, + if (packet_to_return->magic != 95) { + perror(">> The magic number of the packet is no good."); + return -1; + } + + // The second byte must be worth 1, + if (packet_to_return->version != 1) { + perror(">> The version number of the packet is no good."); + return -1; + } + + if (packet_to_return.length + 4 > buffer_size ) { + perror(">> The packet length is bigger than the UDP datagram, which is not possible with the current laws of physics."); + return -1; + } + + return 0; +} + +// If the sender is not in the neighbourhood, and we have 15 neighbours, we +// ignore the packet. Otherwise, we add him to the neighbourhood, marked as +// temporary. +int update_neighbours(){ + return 0; +}; + // We then look at the differents TLVs in the packet. -void work_with_tlvs(char *data, short packet_len){ +void work_with_tlvs(char *data, short packet_len, struct sockaddr_in6 sender){ int pos = 0; unsigned char tlv_len; tlv tmp_tlv; @@ -371,49 +219,77 @@ void work_with_tlvs(char *data, short packet_len){ } } +// We listen forever for new paquets; +void listen_for_packets(){ + // Create new socket for UDP + int s = socket(AF_INET6, SOCK_DGRAM, 0); -void work_with_tlvs(struct tlvs_list receivied_tlvs){ + if(s < 0) { + perror(">> Error, cannot create socket."); + perror(">> Exiting..."); + exit(1); + } - // For every TLV, - // We make sure the TLV is legal. - if(!validate_tlvs(tlv)){ - perror(">> Invalid TLV receivied, it will be ignored."); ->>>>>>> dev-felipe + struct sockaddr_in6 server; + memset(&server, 0, sizeof(server)); + + server.sin6_family = AF_INET6; + server.sin6_port = htons(LISTEN_PORT); + int rc = bind(s, (struct sockaddr*)&server, sizeof(server)); + if(rc < 0) { + perror(">> Error, cannot bind socket to choosen port."); + perror(">> Exiting..."); + exit(1); + } + + // A paquet has at most a length of 1024 bytes + char req[1024]; + struct sockaddr_in6 sender; + struct iovec io = { .iov_len = 1024, .iov_base = req }; + struct msghdr msg_to_receive = { + .msg_name = &sender, + .msg_namelen = sizeof(sender), + .msg_iov = &io, + .msg_iovlen = 1 + }; + while(1){ + memset(req, '\0', 1024); + + rc = recvmsg(s, &msg_to_receive, 0); + if(rc < 0) { + perror(">> Error while receiving a new datagram."); + perror(">> Ignoring, continuing..."); + continue; + } + + printf(">> New paquet received :\n"); + printf("%s\n", req); + + // TODO : Here, we need to fork. + + // We verify the received packet is well formated, + // and we return it in the struct designed to work with it. + struct packet * formated_rec_datagram; + if(check_header(&req, 1024, formated_rec_datagram) < 0){ + perror(">> Error while checking the header, aborting this packet, by choice, and conviction."); + continue; + } + + // TODO : Add the neighbour check here. + + // struct tlv_list received_tlvs; + // if (validate_tlvs(formated_rec_datagram) < 0) + int nbr_success_tlv = work_with_tlvs(formated_rec_datagram, &req, sender); + if (nbr_success_tlv < 0){ + perror(">> Error while treating the TLVs of the packet."); + printf(">> Managed to deal with %i TLVs\n", -nbr_success_tlv ); + } else { + printf(">> Done working with the TLVs of the packet, listenin for new packets.\n"); } } } -// Get list length -int len_list(list *l) { - int len = 0; - list *tmp = l; - - while(tmp != NULL) { - tmp = tmp->next; - len++; - } - - return len; -} - -// Get a random neighbour -neighbour_peer *get_random_neighbour() { - // Get a random number - time_t t; - srand((unsigned) time(&t)); - int n = rand() % len_list(neighbour_list); - - // Get nth neighbour - list *tmp = neighbour_list; - - for(int i=0; inext; - } - - return (neighbour_peer*) tmp->data; -} - int main(int argc, const char *argv[]) { int cont = 1; From d39efa7501c86e79f440eb11383ba58c618abb7d Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 17:16:46 +0200 Subject: [PATCH 20/26] =?UTF-8?q?Changement=20du=20node=5Fid=20pour=20un?= =?UTF-8?q?=20nombre=20de=2064bit=20(!bytes)=20G=C3=A9n=C3=A9rer=20avec=20?= =?UTF-8?q?python3.random(64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.h b/src/node.h index 4039e25..d4ba94b 100644 --- a/src/node.h +++ b/src/node.h @@ -18,7 +18,7 @@ #define LISTEN_PORT 1212 // The node ID -#define NODE_ID 203242402519736214145149136169422092269247115186189140178187251487819615911212154252117172522111472481308026129190139512419121015210238252292031613214452118122204415160254 +#define NODE_ID 42675882021843277 // The number of neighbours // The neighbour table has 15 entries From 4a616dbfe05a8f637e69b5b75ed50b625db43155 Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 17:51:25 +0200 Subject: [PATCH 21/26] Ajout de la signature de la fonction d'envoie de paquet --- src/node.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/node.h b/src/node.h index d4ba94b..0b709b4 100644 --- a/src/node.h +++ b/src/node.h @@ -77,6 +77,12 @@ void add_tlv(packet *packet, tlv *tlv, char type); int send_packet(struct tlv_list tlvs_to_send, ); +/* Prend un tlv, et construit un paquet pour ensuite l'envoyer à la liste + * des paires. + * Retourne -1 en cas d'échec, 0 en cas de succès. + */ +int send_tlv(struct tlv, struct sockaddr_in6 * sender_list[], int sender_list_size); + // threaded functions void t_ask_for_more_peers(); From d637d8c1ef3eb62e1b131aeae6d670c9c26fbd4e Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 17:51:54 +0200 Subject: [PATCH 22/26] Suppression temporaire de la fonction send_paquet. --- src/node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.h b/src/node.h index 0b709b4..fbed67f 100644 --- a/src/node.h +++ b/src/node.h @@ -75,7 +75,7 @@ int work_with_tlvs(struct packet received_packet, char * data_from_packet[], str void add_tlv(packet *packet, tlv *tlv, char type); -int send_packet(struct tlv_list tlvs_to_send, ); +int send_packet(); /* Prend un tlv, et construit un paquet pour ensuite l'envoyer à la liste * des paires. From b7478364abe752d3fce3bd7d34f86e7f77868efe Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 17:54:01 +0200 Subject: [PATCH 23/26] Ajout de la signature de la fonction envoyant plusieurs TLV --- src/node.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/node.h b/src/node.h index fbed67f..006f803 100644 --- a/src/node.h +++ b/src/node.h @@ -83,8 +83,14 @@ int send_packet(); */ int send_tlv(struct tlv, struct sockaddr_in6 * sender_list[], int sender_list_size); -// threaded functions +/* Prend une liste de tlv, et construit un paquet pour ensuite les envoyer à la liste + * des paires. Chaque pair recevera la même liste de TLV. + * Retourne -1 en cas d'échec, 0 en cas de succès. + */ +int send_tlvs(struct list * tlv_list, struct sockaddr_in6 * sender_list[], int sender_list_size); + +// threaded functions void t_ask_for_more_peers(); void t_update_neighbours(); From e1276448f1a0a3428ed83843f2d79d7288237fe6 Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 17:56:17 +0200 Subject: [PATCH 24/26] Traduction des commentaires en anglais, Modification de la signature des deux fonctions --- src/node.c | 5 +++++ src/node.h | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/node.c b/src/node.c index 499651d..dff0c29 100644 --- a/src/node.c +++ b/src/node.c @@ -45,6 +45,11 @@ neighbour_peer *get_random_neighbour() { /* ---- Fin fonctions utilitaires ---- */ +// This function +int send_tlv(struct tlv, struct sockaddr_in6 * sender_list[], int sender_list_size){ + +} + // We need to make sure the TLV announces a length that will no go onto // another tlv, as we might end up reading bullshit. int validate_tlv(char *data, int pos, short packet_len){ diff --git a/src/node.h b/src/node.h index 006f803..38fbc1b 100644 --- a/src/node.h +++ b/src/node.h @@ -77,18 +77,18 @@ void add_tlv(packet *packet, tlv *tlv, char type); int send_packet(); -/* Prend un tlv, et construit un paquet pour ensuite l'envoyer à la liste +/* Takes a TLV and sends it over to everyone in the list of addresses. * des paires. * Retourne -1 en cas d'échec, 0 en cas de succès. */ -int send_tlv(struct tlv, struct sockaddr_in6 * sender_list[], int sender_list_size); +int send_tlv(struct tlv tlv_to_send, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size); /* Prend une liste de tlv, et construit un paquet pour ensuite les envoyer à la liste * des paires. Chaque pair recevera la même liste de TLV. * Retourne -1 en cas d'échec, 0 en cas de succès. */ -int send_tlvs(struct list * tlv_list, struct sockaddr_in6 * sender_list[], int sender_list_size); +int send_tlvs(struct list * tlv_list, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size); // threaded functions void t_ask_for_more_peers(); From b96655da72587cc738883c319e2c7f37281c2d3c Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 18:43:53 +0200 Subject: [PATCH 25/26] Ajout de la fonction d'envoie de tlv --- src/node.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/node.h | 4 ++-- src/tlv.c | 4 ++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/node.c b/src/node.c index dff0c29..fbe2830 100644 --- a/src/node.c +++ b/src/node.c @@ -45,9 +45,63 @@ neighbour_peer *get_random_neighbour() { /* ---- Fin fonctions utilitaires ---- */ -// This function -int send_tlv(struct tlv, struct sockaddr_in6 * sender_list[], int sender_list_size){ +int send_tlv(struct tlv tlv_to_send, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size, int sock_num){ + debug_print("Building packet to send a TLV."); + // We first need to build the packet, + char packet_buff[1024]; + struct packet pack; + + pack.magic = 95; + pack.version = 1; + if(sizeof(tlv_to_send) > 1020){ + debug_print("Unable to send TLV, the size is bigger than 1020 bits."); + return -1; + } else { + pack.length = sizeof(tlv_to_send); + strcpy(pack.body, (char) tlv_to_send); + } + + // Casting the struct to a buffer. + packet_buff = (char *) pack; + + debug_print("Packet has been built."); + + // Vectorized buffer + struct iovec vec_buff = { .iov_len = sizeof(packet_buff), .iov_base = packet_buff }; + + int error_while_sending = 0; + + // For every dest + for (size_t i = 0; i < dest_list_size; i++) { + // Creating the struct to send out with sendmsg + struct msghdr packet_tlv_send_out = { + .msg_name = &dest_list[i], + .msg_namelen = sizeof(dest_list[i]), + .msg_iov = &vec_buff, + .msg_iovlen = 1 // We have only one iovec buffer. But if we had 2, we would write 2. + }; + + response_code = sendmsg((int) sock_num, &packet_tlv_send_out, 0); + if (response_code < 0) { + debug_print("Unable to send out the packet to peer %i", i); + error_while_sending = 1; + continue; + } else if (response_code < sizeof(packet_tlv_send_out)) { + debug_print("Sent out only part of the packet."); + error_while_sending = 1; + continue; + } else { + debug_print("Send out packet to peer %i", i); + } + } + + if (error_while_sending == 1) { + debug_print("Error occured while sending out a packet."); + return -1; + } else { + return 0; + } } // We need to make sure the TLV announces a length that will no go onto diff --git a/src/node.h b/src/node.h index 38fbc1b..87fe047 100644 --- a/src/node.h +++ b/src/node.h @@ -81,14 +81,14 @@ int send_packet(); * des paires. * Retourne -1 en cas d'échec, 0 en cas de succès. */ -int send_tlv(struct tlv tlv_to_send, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size); +int send_tlv(struct tlv tlv_to_send, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size, int socket_num); /* Prend une liste de tlv, et construit un paquet pour ensuite les envoyer à la liste * des paires. Chaque pair recevera la même liste de TLV. * Retourne -1 en cas d'échec, 0 en cas de succès. */ -int send_tlvs(struct list * tlv_list, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size); +int send_tlvs(struct list * tlv_list, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size, int socket_num); // threaded functions void t_ask_for_more_peers(); diff --git a/src/tlv.c b/src/tlv.c index 2284815..e19cfd9 100644 --- a/src/tlv.c +++ b/src/tlv.c @@ -180,9 +180,9 @@ int build_warning(tlv *tlv, char *message) { new->type = 9; new->length = len; - memcpy(new->message, message, len); + memcpy(new->message, message, len); tlv->warning = new; return 0; -} \ No newline at end of file +} From 05e934672654a8cc3121148e352acab2b693bd37 Mon Sep 17 00:00:00 2001 From: n07070 Date: Wed, 1 Apr 2020 18:47:38 +0200 Subject: [PATCH 26/26] Traduction des commentaires vers l'anglais --- src/node.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/node.h b/src/node.h index 87fe047..0f7e2c6 100644 --- a/src/node.h +++ b/src/node.h @@ -78,15 +78,12 @@ void add_tlv(packet *packet, tlv *tlv, char type); int send_packet(); /* Takes a TLV and sends it over to everyone in the list of addresses. - * des paires. - * Retourne -1 en cas d'échec, 0 en cas de succès. + * Returns -1 in case of error, 0 otherwise. */ int send_tlv(struct tlv tlv_to_send, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size, int socket_num); - -/* Prend une liste de tlv, et construit un paquet pour ensuite les envoyer à la liste - * des paires. Chaque pair recevera la même liste de TLV. - * Retourne -1 en cas d'échec, 0 en cas de succès. +/* Takes a list of TLV and sends them over to everyone in the list of addresses. + * Returns -1 in case of error, 0 otherwise. */ int send_tlvs(struct list * tlv_list, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size, int socket_num);