// 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 "node.h" // Static variables static list *data_list; static list *neighbour_list; /* ---- Fonctions utilitaires ---- */ // Looks for more peers int ask_for_peers(int socket_num) { print_debug(">> Asking for more peers..."); // Print out the current peer list. // Only ask for more peers if the neighbour list is small enough int nbr_peers = len_list(neighbour_list); if( nbr_peers >= 5){ print_debug(">> We have enough peers, skipping..."); return 0; } else if (nbr_peers <= 0){ print_debug(">> No peers found in the peer list, something terrible happened."); return -1; } else { // Get random peer neighbour_peer *peer = get_random_neighbour(); struct in6_addr ip = peer->ip; int16_t port = peer->port; /*int ifindex = if_nametoindex("enp3s0"); if(ifindex == 0) { int ifindex = if_nametoindex("eth0"); if(ifindex == 0) { perror("if_nametoindex failed"); return -1; } }*/ int ifindex = 0; // Initialize sockaddr struct sockaddr_in6 dest; memset(&dest, 0, sizeof(struct sockaddr_in6)); dest.sin6_family = AF_INET6; memcpy(&dest.sin6_addr, &ip, 16); dest.sin6_port = htobe16(port); dest.sin6_scope_id = ifindex; // Send neighbour request TLV tlv neighbour_req; neighbour_req.pad1 = NULL; int rc = build_neighbour_req(&neighbour_req); if (rc < 0) { print_debug(">> Failed to build neighbour_req"); } rc = send_single_tlv(&neighbour_req, &dest, socket_num); if (rc < 0) { print_debug(">> Error while sending a TLV."); return -1; } else { print_debug(">> Send TLV."); return 0; } } } // 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() { print_debug(">> Getting random peer..."); // Get a random number srand((unsigned) time(NULL)); int n = (rand() % len_list(neighbour_list)) + 1; // Get nth neighbour list *tmp = neighbour_list; for(int i=1; i < n; i++) { tmp = tmp->next; } return (neighbour_peer*) tmp->data; } // Search for this peer in the neighbour table neighbour_peer *get_neighbour(struct in6_addr *ip, int16_t port) { print_debug(">> Getting neighbour."); if (DEBUG_LEVEL > 1) { char * buff_str_ip[1024]; char * ip_str = (char * ) inet_ntop(AF_INET6,ip,(char * restrict) buff_str_ip, 1024); printf("\x1b[31m[DEBUG]\x1b[0m >> Looking up %s @ %i\n", ip_str, port ); } // Look for neighbour list *tmp = neighbour_list; neighbour_peer *peer; while(tmp != NULL) { // check for same ip and same port peer = (neighbour_peer*) tmp->data; if(memcmp(&peer->ip, ip, sizeof(struct in6_addr)) == 0 && memcmp(&peer->port, &port, sizeof(int16_t)) == 0) { return peer; } // if they differ, get next peer tmp = tmp->next; } return NULL; } // Return -1 if we have enough peers, // 1 if it was added // Return 0 if peer was updated as last_seen int add_n_update_neighbour(struct in6_addr *ip, int16_t port) { // We try to find a peer with this address and port. neighbour_peer *peer = get_neighbour(ip, port); time_t curtime; if (peer == NULL) { print_debug(">> We don't know this peer yet"); // check if there are less than 15 neighbours if(len_list(neighbour_list) >= 15){ return -1; } else { print_debug(">> Adding them to the peer table.\n"); // if there are less, initialize the new peer to add to the list peer = (neighbour_peer*) malloc(sizeof(neighbour_peer)); memcpy(&peer->ip, ip, sizeof(struct in6_addr)); peer->port = LISTEN_PORT; peer->is_temporary = 1; // set last_seen time time(&curtime); peer->last_seen = curtime; list * tmp = neighbour_list; // set new peer as head of list list *node = (list*) malloc(sizeof(list)); node->data = (void*) peer; node->next = tmp; neighbour_list = node; return 1; } } else { if (DEBUG_LEVEL > 0) { char * buff_str_ip[1024]; char * ip_str = (char * ) inet_ntop(AF_INET6,ip,(char * restrict) buff_str_ip, 1024); printf("\x1b[31m[DEBUG]\x1b[0m >> Found peer %s @ %i, updating the last seen time...\n", ip_str, port); } time_t curtime; // if the peer was already in the list, update it time(&curtime); peer->last_seen = curtime; return 0; } } // get data associated with id, if it doesn't exist return NULL pub_data *get_data(uint64_t id) { list *tmp = data_list; pub_data *data; while(tmp != NULL) { data = (pub_data*) tmp->data; tmp = tmp->next; if(data->id == id) return data; } return NULL; } // Take data as args and create a pub_data structure in the heap pub_data *copy_data(unsigned char len, uint64_t id, uint16_t seqno, char *data) { pub_data *new_data = (pub_data*) malloc(sizeof(pub_data)); char *_data = (char*) malloc(len); if (_data == NULL) { print_error("Failed to allocate memory for copying the data !"); return NULL; } new_data->length = len; new_data->id = id; new_data->seqno = seqno; new_data->data = _data; if(memcpy(_data, data, len) == NULL){ print_error("Failed to copy data !"); return NULL; } return new_data; } // A node state TLV was received and either no data associated to it's id is in our data list or the data was updated, return -1 if an error occurend, 0 if nothing had to be done and 1 if something was updated/added int add_data(unsigned char len, uint64_t id, uint16_t seqno, char *data, pub_data *found) { // Check if it's our own id if(id == NODE_ID) { // wtf if(found == NULL) { printf("\x1b[31m[DEBUG]\x1b[0m >> Our own node is not in the data list, something went terribly wrong.\n"); return -1; } // If seqno is bigger or equals than our stored seqno then update seqno if( ((seqno - found->seqno) & 32768) == 0 ) { printf(">> Updating seqno of our own published data.\n"); found->seqno = (seqno + 1) % (65535); return 1; } // Else, do nothing printf(">> Our own seqno didn't need to be updated.\n"); return 0; } // If it's not our own id, update the data if it's already in our data list and seqno is bigger than our stored seqno if(found != NULL) { // Check if seqno is smaller or equals to our stored seqno if( ((found->seqno - seqno) & 32768) == 0 ) { printf(">> Data received has smaller seqno than stored seqno, nothing has to be done.\n"); return 0; } // Update data found->length = len; found->id = id; found->seqno = seqno; // Updata message free(found->data); found->data = (char*) malloc(len); memcpy(found->data, data, len); printf(">> Updated %li's published data.\n", id); return 1; } // Else, add new data pub_data *new_data = copy_data(len, id, seqno, data); // Find correct position for new data list *tmp = data_list; list *last = NULL; list *new_node; uint64_t cur_id; while(tmp != NULL) { cur_id = ((pub_data*) tmp->data)->id; // If id is smaller than cur_id then the new data has to be added at this position if(id < cur_id) { // If last hasn't been set then the new data becomes the head of the list if(last == NULL) { // Update list data_list = (list*) malloc(sizeof(list)); data_list->data = (void*) new_data; data_list->next = tmp; printf(">> Added new message to data list.\n"); return 1; } // Else, we update the last node new_node = (list*) malloc(sizeof(list)); new_node->data = (void*) new_data; new_node->next = tmp; last->next = new_node; printf(">> Added new message to data list.\n"); return 1; } // Get next node in list last = tmp; tmp = tmp->next; } // If no correct position was found then the new data has to be added at the end of the list new_node = (list*) malloc(sizeof(list)); new_node->data = (void*) new_data; new_node->next = NULL; last->next = new_node; printf(">> Added new message to data list.\n"); return 1; } /* ---- Fin fonctions utilitaires ---- */ // Update the neighbour list int update_neighbours() { print_debug(">> Updating neighbours."); list *tmp = neighbour_list, *last = NULL, *node_to_delete; neighbour_peer *peer; time_t curtime; int deleted = 0; // check every neighbour while(tmp != NULL) { peer = (neighbour_peer*) tmp->data; if (DEBUG_LEVEL > 1) { char * buff_str_ip[1024]; char * ip_str = (char * ) inet_ntop(AF_INET6,&peer->ip,(char * restrict) buff_str_ip, 1024); printf("\x1b[31m[DEBUG]\x1b[0m >> Checking for neighbour %s\n", ip_str ); } // Check if peer is temporary if(peer->is_temporary) { // If it's been 70 seconds or more since we last received a packet from this peer then remove it from the list time(&curtime); if(difftime(peer->last_seen, curtime) >= 70) { // increase the count of deleted nodes deleted++; print_debug(">> They have not been seen for the past 70 seconds, deleting..."); // If head of the list if(last == NULL) { // Store node to delete node_to_delete = tmp; // Update list tmp = tmp->next; neighbour_list = tmp; // Free allocated memory free(node_to_delete->data); free(node_to_delete); continue; } // Store node to delete node_to_delete = tmp; // Update list tmp = tmp->next; last->next = tmp; // Free allocated memory free(node_to_delete->data); free(node_to_delete); continue; } else { print_debug(">> Peer has been seen in the last 70 seconds, keeping him in."); } } else { print_debug(">> Peer is not temporary, keeping him in."); } last = tmp; tmp = tmp->next; } // returns the amount of nodes that were deleted return deleted; } // Add TLV to packet, if it does not fit then send the packet and reset the packet buff to be able to add more TLVs that will be sent afterwards int add_tlv(packet *pack, tlv *tlv, struct sockaddr_in6 *dest, int socket_num) { print_debug(">> Adding tlv to packet"); char type = tlv->pad1->type, sent = 0, errval = 0; unsigned char len; // Check if TLV fits in the packet, if not then send the packet and reset it if(type != 1) { len = tlv->padn->length + 2; if(pack->length + len > 1020) { errval = send_packet((char*) pack, pack->length, dest, socket_num); pack->magic = 95; pack->version = 1; pack->length = 0; memset(pack->body, 0, 1020); sent = 1; } } else { // In case we need to add a padding packet, and the packet is full, // we just ignore that padding, and send out the packet. if(pack->length >= 1020) { errval = send_packet((char*) pack, pack->length, dest, socket_num); pack->magic = 95; pack->version = 1; pack->length = 0; memset(pack->body, 0, 1020); sent = 1; } } // Copy data from tlv into body switch(type) { case 1: memcpy(pack->body + pack->length, (char*) &tlv->pad1->type, 1); pack->length += 1; break; case 2: memcpy(pack->body + pack->length, (char*) &tlv->padn->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->padn->length, 1); memcpy(pack->body + pack->length + 2, (char*) &tlv->padn->mbz, tlv->padn->length); pack->length += len; break; case 3: memcpy(pack->body + pack->length, (char*) &tlv->neighbour->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->neighbour->length, 1); pack->length += len; break; case 4: memcpy(pack->body + pack->length, (char*) &tlv->network_hash->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->network_hash->length, 1); memcpy(pack->body + pack->length + 2, (char*) &tlv->network_hash->network_hash, 16); pack->length += len; break; case 5: memcpy(pack->body + pack->length, (char*) &tlv->network_state_req->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->network_state_req->length, 1); pack->length += len; break; case 6: memcpy(pack->body + pack->length, (char*) &tlv->node_hash->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->node_hash->length, 1); memcpy(pack->body + pack->length + 2, (char*) &tlv->node_hash->node_id, 8); memcpy(pack->body + pack->length + 10, (char*) &tlv->node_hash->seqno, 2); memcpy(pack->body + pack->length + 12, (char*) &tlv->node_hash->node_hash, 16); pack->length += len; break; case 7: memcpy(pack->body + pack->length, (char*) &tlv->node_state_req->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->node_state_req->length, 1); memcpy(pack->body + pack->length + 2, (char*) &tlv->node_state_req->node_id, 8); pack->length += len; break; case 8: memcpy(pack->body + pack->length, (char*) &tlv->node_state->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->node_state->length, 1); memcpy(pack->body + pack->length + 2, (char*) &tlv->node_state->node_id, 8); memcpy(pack->body + pack->length + 10, (char*) &tlv->node_state->seqno, 2); memcpy(pack->body + pack->length + 12, (char*) &tlv->node_state->node_hash, 16); memcpy(pack->body + pack->length + 28, (char*) &tlv->node_state->data, tlv->node_state->length - 26); pack->length += len; break; case 9: memcpy(pack->body + pack->length, (char*) &tlv->warning->type, 1); memcpy(pack->body + pack->length + 1, (char*) &tlv->warning->length, 1); memcpy(pack->body + pack->length + 2, (char*) &tlv->warning->message, tlv->warning->length); pack->length += len; break; default: return -1; } print_debug(">> Finished adding the TLVs to the packet"); // If the previous packet was sent return 1 or -1 if there was an error sending it if(sent){ return errval? -1:1; } // Return 0 if the TLV was added to the packet return 0; } // Send length bytes from packet int send_packet(char *packet_buff, uint16_t length, struct sockaddr_in6 *dest, int socket_num) { uint16_t be_len = htobe16(*(uint16_t*) (packet_buff + 2)); memcpy(packet_buff + 2, &be_len, 2); // Vectorized buffer struct iovec vec_buff[1]; vec_buff[0].iov_len = length + 4; vec_buff[0].iov_base = packet_buff; int error_while_sending = 0; // Creating the struct to send out with sendmsg struct msghdr packet_tlv_send_out; memset(&packet_tlv_send_out, 0, sizeof(struct msghdr)); packet_tlv_send_out.msg_name = dest; packet_tlv_send_out.msg_namelen = sizeof(struct sockaddr_in6); packet_tlv_send_out.msg_iov = vec_buff; packet_tlv_send_out.msg_iovlen = 1; // We have only one iovec buffer. But if we had 2, we would write 2. int response_code = sendmsg(socket_num, &packet_tlv_send_out, 0); if (response_code < 0) { print_error("Unable to send out the packet to peer."); error_while_sending = 1; } else if (response_code < length) { print_debug(">> Sent out only part of the packet."); error_while_sending = 1; } else { print_debug(">> Send out packet to peer."); } if (error_while_sending == 1) { return -1; } else { if (DEBUG_LEVEL > 0) { printf("\x1b[31m[DEBUG]\x1b[0m >> Packet successfully sent to peer. %d bytes sent.\n", response_code); } return 0; } } // Send a single TLV to the specified addresses, return -1 if an error was encountered, 0 otherwise int send_single_tlv(tlv *tlv, struct sockaddr_in6 *dest, int socket_num) { char type = tlv->pad1->type; packet pack = (packet) {.magic = 95, .version = 1, .length = 0}; memset(pack.body, 0, 1020); // Copy data from tlv into body switch(type) { case 1: memcpy(pack.body + pack.length, (char*) &tlv->pad1->type, 1); pack.length += 1; break; case 2: memcpy(pack.body + pack.length, (char*) &tlv->padn->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->padn->length, 1); memcpy(pack.body + pack.length + 2, (char*) &tlv->padn->mbz, tlv->padn->length); pack.length += tlv->padn->length + 2; break; case 3: memcpy(pack.body + pack.length, (char*) &tlv->neighbour->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->neighbour->length, 1); pack.length += tlv->neighbour->length + 2; break; case 4: memcpy(pack.body + pack.length, (char*) &tlv->network_hash->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->network_hash->length, 1); memcpy(pack.body + pack.length + 2, (char*) &tlv->network_hash->network_hash, 16); pack.length += tlv->network_hash->length + 2; break; case 5: memcpy(pack.body + pack.length, (char*) &tlv->network_state_req->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->network_state_req->length, 1); pack.length += tlv->network_state_req->length + 2; break; case 6: memcpy(pack.body + pack.length, (char*) &tlv->node_hash->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->node_hash->length, 1); memcpy(pack.body + pack.length + 2, (char*) &tlv->node_hash->node_id, 8); memcpy(pack.body + pack.length + 10, (char*) &tlv->node_hash->seqno, 2); memcpy(pack.body + pack.length + 12, (char*) &tlv->node_hash->node_hash, 16); pack.length += tlv->node_hash->length + 2; break; case 7: memcpy(pack.body + pack.length, (char*) &tlv->node_state_req->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->node_state_req->length, 1); memcpy(pack.body + pack.length + 2, (char*) &tlv->node_state_req->node_id, 8); pack.length += tlv->node_state_req->length + 2; break; case 8: memcpy(pack.body + pack.length, (char*) &tlv->node_state->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->node_state->length, 1); memcpy(pack.body + pack.length + 2, (char*) &tlv->node_state->node_id, 8); memcpy(pack.body + pack.length + 10, (char*) &tlv->node_state->seqno, 2); memcpy(pack.body + pack.length + 12, (char*) &tlv->node_state->node_hash, 16); memcpy(pack.body + pack.length + 28, (char*) &tlv->node_state->data, tlv->node_state->length - 26); pack.length += tlv->node_state->length + 2; break; case 9: memcpy(pack.body + pack.length, (char*) &tlv->warning->type, 1); memcpy(pack.body + pack.length + 1, (char*) &tlv->warning->length, 1); memcpy(pack.body + pack.length + 2, (char*) &tlv->warning->message, tlv->warning->length); pack.length += tlv->warning->length + 2; break; default: return -1; } // Send the packet return send_packet((char*) &pack, pack.length, dest, socket_num); } // TODO This function can be deleted. // int send_tlv(tlv *tlv_to_send, uint16_t tlv_size, struct sockaddr_in6 * dest_list, int dest_list_size, int socket_num){ // print_debug(">> 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 (tlv_size > 1020) { // print_debug(">> Unable to send the tlv, it's size is above 1020 bytes."); // return -1; // } else { // memcpy((void *) pack.body, tlv_to_send, tlv_size); // } // // // Move the content of the paquet struct to a buffer // // That will be send out in a vectorized buffer. // memcpy(&packet_buff,&pack,1024); // // if (DEBUG_LEVEL > 1) { // print_debug(">> 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. // }; // // int response_code = sendmsg((int) socket_num, &packet_tlv_send_out, 0); // if (response_code < 0) { // if (DEBUG_LEVEL > 0) { // printf("\x1b[31m[DEBUG]\x1b[0m >> Unable to send out the packet to peer %li", i); // } // error_while_sending = 1; // continue; // } else if (response_code < sizeof(packet_tlv_send_out)) { // print_debug(">> Sent out only part of the packet."); // error_while_sending = 1; // continue; // } else { // if (DEBUG_LEVEL > 0) { // printf("\x1b[31m[DEBUG]\x1b[0m >> Sent out packet to peer %i", i); // } // } // } // // if (error_while_sending == 1) { // print_debug(">> 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 // another tlv, as we might end up reading bullshit. int validate_tlv(char *data, int pos, uint16_t packet_len){ char type = data[pos]; // Nothing to do in this case if(type == 0){ print_debug(">> Found padding TLV type."); return 0; } // Check that we can read a length if(pos + 1 >= packet_len){ print_debug(">> Reading outside of packet's max length."); return -1; } // 0 1 2 3 = Packet // 4 = type 5 = tlv_len unsigned char tlv_len = data[pos+1]; // Check that the tlv does not exceed the packet length if(pos + tlv_len > packet_len){ print_debug(">> The TLV Length exceeds the packet length\n"); return -1; } if (DEBUG_LEVEL > 1) { printf("\x1b[31m[DEBUG]\x1b[0m >> TLV has type %i\n", type ); } // Returns the type of the tlv or -1 if something went wrong switch(type) { case 1: return 1; case 2: if(tlv_len != LEN_NEIGHBOUR_REQ) return -1; return 2; case 3: if(tlv_len != LEN_NEIGHBOUR) return -1; return 3; case 4: if(tlv_len != LEN_NETWORK_HASH) return -1; return 4; case 5: if(tlv_len != LEN_NETWORK_STATE_REQ) return -1; return 5; case 6: if(tlv_len != LEN_NODE_HASH) return -1; return 6; case 7: if(tlv_len != LEN_NODE_STATE_REQ) return -1; return 7; case 8: if(tlv_len < MIN_LEN_NODE_STATE || tlv_len > MAX_LEN_NODE_STATE) return -1; /* // Check if it has the right hash uint16_t *seqno = (uint16_t*) (data + pos + 10); uint64_t *id = (uint64_t*) (data + pos + 2); pub_data pdata = (pub_data) { .length = tlv_len, .id = *id, .seqno = *seqno, .data = (unsigned char*) malloc(tlv_len) }; unsigned char *cur_hash = (unsigned char*) (data + pos + 12); char *cur_message = data + pos + 28; unsigned char hash[16]; memcpy(pdata.data, cur_message, tlv_len); hash_data(&pdata, hash); if(memcmp(hash, cur_hash, 16) != 0) { print_debug(">> Malformed hash."); printf("\x1b[31m[DEBUG]\x1b[0m >> Received : "); for(int x = 0; x < 16; x++){ printf("%02x", hash[x]); fflush(0); } printf("\n"); printf("\x1b[31m[DEBUG]\x1b[0m >> Received : "); for(int x = 0; x < 16; x++){ printf("%02x", cur_hash[x]); fflush(0); } printf("\n"); return -1; } */ return 8; case 9: return 9; default: return -1; } } // 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 * received_data_buffer, int received_data_len, struct packet * packet_to_return){ packet_to_return = (packet*) received_data_buffer; // We need to check a few things ; // The first byte must be worth 95, if (packet_to_return->magic != 95) { print_debug(">> The magic number of the packet is no good."); return -1; } // The second byte must be worth 1, if (packet_to_return->version != 1) { print_debug(">> The version number of the packet is no good."); return -1; } // Convert to hardware order. ((packet*) packet_to_return)->length = be16toh(((packet*) packet_to_return)->length); if (packet_to_return->length + 4 < received_data_len ) { print_debug(">> The packet length is bigger than the UDP datagram, which is not possible with the current laws of physics."); return -1; } return 0; } int add_message(char * message, int message_len){ // Don't update the message if it's empty if(message_len == 0){ return -1; } // If not, get our data in the list and update it pub_data *our_data = get_data(NODE_ID); if(our_data != NULL) { our_data->seqno = (our_data->seqno + 1) % 65535; our_data->length = message_len; free(our_data->data); our_data->data = (char*) malloc(message_len); memcpy(our_data->data, message, message_len); print_debug(">> Message added."); return 0; } print_debug(">> Message could not be added because our own ID is not in the data_list, something went wrong."); return -1; } // We then look at the differents TLVs in the packet. int work_with_tlvs(char * data, uint16_t total_packet_len, struct sockaddr_in6 *sender, int socket_num){ uint16_t packet_len = ((packet*) data)->length; if(packet_len != total_packet_len - 4) { print_debug(">> Length indicated in packet differs from real length of packet received, disgarding packet."); return -1; } int nbr_of_tlvs = 0; int pos = 4; unsigned char tlv_len, hash[16], hash2[16]; char warn[32]; // The TLV we are going to send back. tlv new_tlv; new_tlv.pad1 = NULL; list * tmp_list; pub_data * pdata; // holds the random neighbour we send back in case of a neighbour_req. struct neighbour_peer *random_neighbour; // memset(random_neighbour, 0, sizeof(struct neighbour_peer)); // Holds the new neighbour send to us by a "neighbour tlv" struct sockaddr_in6 new_neighbour; // We create the packet in which we are going to send back our responses. packet pack = (packet) {.magic = 95, .version = 1, .length = 0}; memset(pack.body, 0, 1020); int ifindex = 0; // Temporary values uint16_t *port, *seqno; uint64_t *id; unsigned char *cur_hash, *ip; char *cur_message; while(pos < total_packet_len) { // Making sure the current TLV we are looking at is valid. // TODO : I think we should reset all the structs here. // memset(random_neighbour, 0, sizeof(struct neighbour_peer)); // memset(pdata, 0, sizeof(struct pub_data)); // memset(tmp_list, 0, sizeof(struct list)); memset(hash, 0, 16); memset(warn, 0, 32); tlv_len = 0; switch(validate_tlv(data, pos, total_packet_len)) { case 0: // We received a padding tlv so it is ignored print_debug(">> Received padding tlv, ignoring..."); pos += 1; nbr_of_tlvs++; break; case 1: // We received a padding tlv so it is ignored print_debug(">> Received padding(n) tlv, ignoring..."); tlv_len = data[pos+1]; pos += tlv_len + 2; nbr_of_tlvs++; break; case 2: // We received a neighbour request so a random neighbor tlv has to be sent print_debug(">> Received neighbour request, sending out a neighbour address."); // Send a neighbour tlv random_neighbour = get_random_neighbour(); if (random_neighbour == NULL) { print_debug(">> Failed to get a random neighbour, failing..."); return -1; } // TODO : Seems to be a bug here, as this frees the new_tlv build_neighbour(&new_tlv, random_neighbour->ip, random_neighbour->port); // TODO : I suppose that since new_tlv is freed above, add_tlv ads an empty // value to the packet ? add_tlv(&pack, &new_tlv, sender, socket_num); // The position is updated tlv_len = data[pos+1]; pos += tlv_len + 2; nbr_of_tlvs++; break; case 3: print_debug(">> Received neighbour tlv, sending back network hash."); // We received a neighbour tlv so a tlv network hash is sent to that address ip = (unsigned char*) (data + pos + 2); port = (uint16_t*) (data + pos + 18); // Init dest socket memset(&new_neighbour, 0, sizeof(new_neighbour)); new_neighbour.sin6_family = AF_INET6; memcpy(&new_neighbour.sin6_addr, ip, 16); new_neighbour.sin6_port = be16toh(*port); new_neighbour.sin6_scope_id = ifindex; // Build network hash build_network_hash(&new_tlv, data_list); send_single_tlv(&new_tlv, &new_neighbour, socket_num); // The position is updated tlv_len = data[pos+1]; pos += tlv_len + 2; nbr_of_tlvs++; break; case 4: print_debug(">> Received network_hash, comparing with our own.."); // We reveived a network hash tlv so we compare the hash with our own, // if they differ we send a network state request tlv cur_hash = (unsigned char*) (data + pos + 2); hash_network(data_list, hash); if (DEBUG_LEVEL > 1) { printf("\x1b[31m[DEBUG]\x1b[0m >> Our hash : "); for(int x = 0; x < 16; x++){ printf("%02x", hash[x]); fflush(0); } printf("\n"); printf("\x1b[31m[DEBUG]\x1b[0m >> Received : "); for(int x = 0; x < 16; x++){ printf("%02x", cur_hash[x]); fflush(0); } printf("\n"); } if(memcmp(hash, cur_hash, 16) != 0) { print_debug(">> Sending out our network hash."); build_network_state_req(&new_tlv); send_single_tlv(&new_tlv, sender, socket_num); } else { print_debug(">> We're up to date."); } // The position is updated tlv_len = data[pos+1]; pos += tlv_len + 2; nbr_of_tlvs++; break; case 5: // We received a network state request tlv // so a series of tlv node hash have to be sent for each data known print_debug(">> Received network state request, sending back hashes for messages."); // for each known data build a node hash and add to packet tmp_list = data_list; while(tmp_list != NULL) { pdata = (pub_data*) tmp_list->data; build_node_hash(&new_tlv, pdata->id, pdata->seqno, pdata->data, pdata->length); add_tlv(&pack, &new_tlv, sender, socket_num); tmp_list = tmp_list->next; printf("%lu\n", *(uint64_t*) (pack.body + 2)); } // The position is updated tlv_len = data[pos+1]; pos += tlv_len + 2; break; case 6: // We received a node hash tlv so // if there is no entry for node_id in the data list or the hashes differ // we send a node state request, //if the hashes are identical nothing has to be done print_debug(">> Received node hash, updating message entry..."); id = (uint64_t*) (data + pos + 2); cur_hash = (unsigned char*) (data + pos + 12); pdata = get_data(be64toh(*id)); // If data is found for this id then we check that both hashes are the same if(pdata != NULL) { print_debug(">> Comparing hashes..."); // We hash the data stored in the data list hash_data(pdata, hash); // If both hashes are the same then nothing has to be done if(memcmp(hash, cur_hash, 16) == 0) { // The position is updated tlv_len = data[pos+1]; pos += tlv_len + 2; print_debug(">> Both hashes are the same, nothing to do."); break; } } print_debug(">> No hash found, or hashes are different, sending back node state request."); // If no pub_data was found or the hashes differ then we send a node state request build_node_state_req(&new_tlv, be64toh(*id)); add_tlv(&pack, &new_tlv, sender, socket_num); printf(">> Sending node state request for node %lu...\n", be64toh(*id)); // The position is updated tlv_len = data[pos+1]; pos += tlv_len + 2; break; case 7: // We received a node state request tlv // so a node state tlv for this node id has to be sent, // if no pub_data exists for this id nothing is sent print_debug(">> Received node state request. Processing..."); id = (uint64_t*) (data + pos + 2); pdata = get_data(be64toh(*id)); if(pdata != NULL) { build_node_state(&new_tlv, pdata->id, pdata->seqno, pdata->data, pdata->length); add_tlv(&pack, &new_tlv, sender, socket_num); } else { print_debug(">> Found no data for the requested node, skipping..."); } // The position is updated tlv_len = data[pos+1]; pos += tlv_len + 2; break; case 8: // We received a node state tlv so // we add it to the data list // or update the data stored print_debug(">> Received node state, updating..."); tlv_len = data[pos + 1]; id = (uint64_t*) (data + pos + 2); seqno = (uint16_t*) (data + pos + 10); cur_hash = (unsigned char*) (data + pos + 12); cur_message = data + pos + 28; print_debug(">> Received message ! "); printf("Type : %d\n", data[pos] ); printf("Length : %d\n", tlv_len ); printf("Node ID : %lu\n", be64toh(*id) ); printf("Seqno : %hu\n", be16toh(*seqno) ); printf("Hash :"); for(int x = 0; x < 16; x++){ printf("%02x", cur_hash[x]); fflush(0); } printf("\n"); for(int x = 0; x < tlv_len - 26; x++){ printf("%c", cur_message[x]); fflush(0); } printf("\n"); // Check if it has the right hash pub_data pdata_check = (pub_data) { .length = tlv_len - 26, .id = be64toh(*id), .seqno = be16toh(*seqno), .data = (char*) malloc(tlv_len) }; memcpy(pdata_check.data, cur_message, tlv_len - 26); hash_data(&pdata_check, hash2); print_debug(">> Built message: "); printf("Type : %d\n", data[pos] ); printf("Length : %d\n", pdata_check.length + 26); printf("Node ID : %lu\n", pdata_check.id); printf("Seqno : %hu\n", pdata_check.seqno); printf("Hash :"); for(int x = 0; x < 16; x++){ printf("%02x", hash2[x]); fflush(0); } printf("\n"); for(int x = 0; x < tlv_len - 26; x++){ printf("%c", pdata_check.data[x]); fflush(0); } printf("\n"); if(memcmp(hash2, cur_hash, 16) != 0) { print_debug(">> Malformed hash."); printf("\x1b[31m[DEBUG]\x1b[0m >> Calculated : "); for(int x = 0; x < 16; x++){ printf("%02x", hash2[x]); fflush(0); } printf("\n"); printf("\x1b[31m[DEBUG]\x1b[0m >> Received : "); for(int x = 0; x < 16; x++){ printf("%02x", cur_hash[x]); fflush(0); } printf("\n"); pos += tlv_len + 2; break; } free(pdata_check.data); /* if (DEBUG_LEVEL > 0) { if (cur_message == NULL) { print_error("The data in the current node is NULL !"); return -1; } printf("\x1b[31m[DEBUG]\x1b[0m >> ā€œ%ls\0ā€\n", (const wchar_t*) cur_message); sleep(1); } */ // Compare hashes pdata = get_data(be64toh(*id)); // If data is found for this id then we check that both hashes are the same if(pdata != NULL) { print_debug(">> Comparing hashes..."); // We hash the data stored in the data list memset(hash, 0, 16); hash_data(pdata, hash); // If both hashes are the same then nothing has to be done if(memcmp(hash, cur_hash, 16) == 0) { // The position is updated pos += tlv_len + 2; break; } } // Else, we update the data int rc = add_data(tlv_len - 26, be64toh(*id), be16toh(*seqno), cur_message, pdata); if (rc < 0) { print_error("Error while adding node state !"); } // The position is updated pos += tlv_len + 2; break; case 9: print_debug(">> \aReceived warning !"); // We received a warning tlv so it's message is printed tlv_len = data[pos+1]; cur_message = data + pos + 2; // Print exactly new_tlv.length characters from new_tlv.message sprintf(warn, ">> WARNING:\n%%.%ds\n", tlv_len + 1); printf(warn, cur_message); // The position is updated pos += tlv_len + 2; break; default: // A malformed packet was found so we stop looking for more packets and send a warning tlv strcpy(warn, "Packet is malformed."); print_debug(">> Malformed packet, we won't treat it."); print_debug(">> Sending back a Warning to sender."); build_warning(&new_tlv, warn, strlen(warn)); add_tlv(&pack, &new_tlv, sender, socket_num); return -1; } } // Free the previously allocated memory free(new_tlv.pad1); // If the packet still has data in it then send it if(pack.length > 0){ send_packet((char*) &pack, pack.length, sender, socket_num); } print_data(data_list); return 0; } int listen_for_packets(char * received_data_buffer, int received_data_len, struct sockaddr_in6 * sender, int sock_fd){ // 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(received_data_buffer, received_data_len, &formated_rec_datagram) < 0){ print_debug(">> Error while checking the header, aborting this packet, by choice, and conviction."); return -1; } // Neighbour check struct in6_addr ip = sender->sin6_addr; int16_t port = htobe16(sender->sin6_port); int rc = add_n_update_neighbour(&ip, port); if( rc == -1) { print_debug(">> We have enough peers, we won't add him.."); return -1; } else if (rc == 1){ print_debug(">> Peer was added to the table.\a"); } else { print_debug(">> Updated the time it was last seen."); } int nbr_success_tlv = work_with_tlvs(received_data_buffer, received_data_len, sender, sock_fd); if (nbr_success_tlv < 0){ print_debug(">> Error while treating the TLVs of the packet."); if (DEBUG_LEVEL > 1) { printf("\x1b[31m[DEBUG]\x1b[0m >> Managed to deal with %i TLVs\n", -nbr_success_tlv ); } return -2; } else { print_debug(">> Done working with the TLVs of the packet, listening for new packets."); return 0; } } int t_ask_for_more_peers(int sock_fd){ if (DEBUG_LEVEL > 1) { print_peers(neighbour_list); sleep(3); } return ask_for_peers(sock_fd); } /* For every peer we know about, we send out a TLV network hash */ int t_get_network_state(int sock_fd){ print_debug(">> Getting network state..."); print_debug(">> Sending out a TLV network hash to every peer we know of."); if (neighbour_list == NULL) { print_error("Our peer list is empty !"); print_error("Skipping.."); return -1; } else { // Build the network hash // We use another variable so that we don't interfier with the current list list * tmp_list = neighbour_list; while (tmp_list != NULL) { neighbour_peer * peer = (neighbour_peer *) tmp_list->data; tlv new_tlv; memset(&new_tlv, 0, sizeof(union tlv)); // Create the structure for the receiver. struct sockaddr_in6 receiver; memset(&receiver, 0, sizeof(struct sockaddr_in6)); receiver.sin6_family = AF_INET6; receiver.sin6_addr = peer->ip; receiver.sin6_port = htobe16(peer->port); receiver.sin6_scope_id = 0; // Send out a TLV network state. if (build_network_hash(&new_tlv, data_list) < 0) { print_error("Error while building a network hash."); return -1; } else { if (send_single_tlv(&new_tlv, &receiver, sock_fd) < 0) { print_error("Error while sending a network hash to a peer."); return -1; } else { print_debug(">> Sent network hash to a peer."); } } tmp_list = tmp_list->next; } } return 0; } int t_update_neighbours(){ return update_neighbours(); } int run_node(int sock_fd){ print_debug(">> Running node..."); int ret; ssize_t bytes; char input_buffer[1024]; char output_buffer[1024]; struct pollfd fds[2]; // Init the ~20s delay for node update. srand(time(NULL)); time_t delay = time(NULL) + 5; /* Descriptor zero is stdin */ fds[0].fd = 0; fds[1].fd = sock_fd; fds[0].events = POLLIN | POLLPRI; fds[1].events = POLLIN | POLLPRI; /* Normally we'd check an exit condition, but for this example * we loop endlessly. */ while (1) { if(time(NULL) >= delay) { t_ask_for_more_peers(sock_fd); t_update_neighbours(); t_get_network_state(sock_fd); print_data(data_list); sleep(3); delay = time(NULL) + 20 + (rand() % 10); } // This might be cool to add, but we need to find a way to write to stdin // while it's running. // if (time(NULL) < delay) { // // Thanks to : // // https://gist.github.com/amullins83/24b5ef48657c08c4005a8fab837b7499 // printf("\b\x1b[2K\r>> Next request in %li seconds..", delay - time(NULL)); // fflush(stdout); // } // printf("\n"); /* Call poll() */ ret = poll(fds, 2, 10); if (ret < 0) { print_error("Poll returned error"); break; } else if (ret > 0) { /* Regardless of requested events, poll() can always return these */ if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { print_error("Poll indicated stdin error\n"); break; } if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { print_error("Poll indicated socket error\n"); break; } // Read data from stdin (new message to post ) if (fds[0].revents & (POLLIN | POLLPRI)) { bytes = read(0, input_buffer, sizeof(input_buffer)); if (bytes < 0) { print_error("stdin error"); break; } // Remove trailing \n input_buffer[strcspn(input_buffer, "\n")] = 0; if (DEBUG_LEVEL > 0) { printf("\x1b[31m[DEBUG]\x1b[0m >> Adding following message to the table : ā€œ%sā€\n", input_buffer ); } // Add message to the message table. if (add_message(input_buffer, bytes-1) < 0) { print_debug(">> Error while trying to add the message to the list of messages, please try again.."); // Reset buffer memset(input_buffer, 0, sizeof(input_buffer)); } } // Read data from the socket ( incoming packet ) if (fds[1].revents & (POLLIN | POLLPRI)) { // Vectorized buffer struct iovec vec_buff_rec = { .iov_len = sizeof(output_buffer), .iov_base = output_buffer }; struct sockaddr_in6 sender; // Creating the struct receive the server reponse. // Is empty, will be filled by recvmsg() struct msghdr msg_from_peer = { .msg_name = &sender, .msg_namelen = sizeof(sender), .msg_iov = &vec_buff_rec, .msg_iovlen = 1 // We have only one iovec buffer. But if we had 2, we would write 2. }; bytes = recvmsg(sock_fd, &msg_from_peer, 0); if (bytes < 0) { if (DEBUG_LEVEL > 0) { printf("\x1b[31m[DEBUG]\x1b[0m >> Error - recvfrom error: %s\n", strerror(errno)); } break; } if (bytes > 0) { if (DEBUG_LEVEL > 0) { printf("\x1b[31m[DEBUG]\x1b[0m >> Received %i bytes as : %s\n", (int)bytes, output_buffer); } // Treat incoming packets. int work_tlv_status = listen_for_packets(output_buffer, bytes, &sender, sock_fd); if (work_tlv_status < 0) { print_error("Error while treating the incoming packet."); } // Reset buffer memset(output_buffer, 0, sizeof(output_buffer)); } } } else { // TODO : Here, we can write all of the current messages we have in stack // to a file, or to stdout. // TODO : Same as above, but for the peers have know about. continue; } } return 0; } // This function runs once, and sets the sock_fd as well as the neighbourhood int bootstrap_node(int * sock_fd, char * root_peer_ip, uint16_t root_peer_port){ print_debug(">> Boostraping node..."); struct sockaddr_in6 server_addr; /* Create UDP socket */ * sock_fd = socket(AF_INET6, SOCK_DGRAM, 0); if ( * sock_fd < 0) { print_error("Failed to open socket"); perror(""); exit(-1); } /* Bind socket */ memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin6_family = AF_INET6; // server_addr.sin6_addr.in6_addr = htonl(INADDR_ANY); server_addr.sin6_port = LISTEN_PORT; if (bind( * sock_fd, (struct sockaddr *)(&server_addr), sizeof(server_addr)) < 0) { print_error("Failed to bind socket"); perror(""); exit(-1); } /* Make the first peer*/ struct neighbour_peer * root_peer = (struct neighbour_peer *) malloc(sizeof(struct neighbour_peer)); time_t root_peer_seen = time(NULL); int inet_p = inet_pton(AF_INET6, root_peer_ip, &root_peer->ip); if(inet_p < 1){ perror(">> Failed to create the root peer."); return -3; } root_peer->port = (int16_t) root_peer_port; root_peer->is_temporary = 0; root_peer->last_seen = root_peer_seen; // TODO: Add the first peer to the neighbourhood print_debug(">> Adding the first root peer to the list..."); neighbour_list = malloc(sizeof(struct list)); memset(neighbour_list, 0, sizeof(struct list)); neighbour_list->data = (void *) root_peer; neighbour_list->next = NULL; print_debug(">> Initializing data list..."); data_list = (list*) malloc(sizeof(list)); memset(data_list, 0, sizeof(struct list)); data_list->data = malloc(sizeof(pub_data)); data_list->next = NULL; pub_data *our_data = (pub_data*) data_list->data; our_data->length = 0; our_data->id = NODE_ID; our_data->seqno = 1337; our_data->data = NULL; print_debug(">> Boostraping done."); return 0; } int main(int argc, const char *argv[]) { if (argc != 3) { print_error("Usage : ./dazibao "); exit(-1); } char *end; intmax_t val = strtoimax(argv[2], &end, 10); if (errno == ERANGE || val < 0 || val > UINT16_MAX || end == argv[2] || *end != '\0'){ print_error("Conversion of port number failed, please choose a smaller number."); } uint16_t root_peer_port = (uint16_t) val; char * root_peer_ip = (char *) argv[1]; setlocale(LC_ALL, ""); print_debug(">> Starting node"); int sock_fd; bootstrap_node(&sock_fd, root_peer_ip, root_peer_port); run_node(sock_fd); close(sock_fd); return 0; }