From e4338fb8ed5a290ab19502aac8809b10239b9318 Mon Sep 17 00:00:00 2001 From: Vany Ingenzi <ingenzi@intel18.info.ucl.ac.be> Date: Sun, 10 Apr 2022 21:36:44 +0200 Subject: [PATCH] Added functional FEC yet to be tests --- receiver | Bin 0 -> 40320 bytes sender | Bin 0 -> 35816 bytes src/packet_interface.c | 48 ++++++------ src/receiver_utils.c | 165 +++++++++++++++++++++++++++++++---------- src/receiver_utils.h | 2 +- tests/advanced_test.sh | 4 +- 6 files changed, 156 insertions(+), 63 deletions(-) create mode 100755 receiver create mode 100755 sender diff --git a/receiver b/receiver new file mode 100755 index 0000000000000000000000000000000000000000..8c6dfadb164089605220e1d66fee01d592c3bad7 GIT binary patch literal 40320 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 SiO<B}^N>z)T^d7LQu<%RcQwBN literal 0 HcmV?d00001 diff --git a/sender b/sender new file mode 100755 index 0000000000000000000000000000000000000000..fbf5ff9642b83c5144fe8e84989eef913eb139d0 GIT binary patch literal 35816 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 TX_)8YKYf=<F(Zp1D=YmcG*)rd literal 0 HcmV?d00001 diff --git a/src/packet_interface.c b/src/packet_interface.c index 783380c..94e6580 100644 --- a/src/packet_interface.c +++ b/src/packet_interface.c @@ -55,27 +55,33 @@ pkt_status_code pkt_decode_ack_nack(const char *data, const size_t len, pkt_t *p pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *pkt) { - uint16_t length; - memcpy((void *) &length, (void *) &data[1], 2); - length = ntohs(length); + uint16_t payload_length, actual_indicated_length; + ptypes_t type = (data[0] & TYPE_MASK) >> TYPE_SHIFT; + + memcpy((void *) &actual_indicated_length, (void *) &data[1], 2); + actual_indicated_length = ntohs(actual_indicated_length); + payload_length = (type == PTYPE_DATA) ? actual_indicated_length : MAX_PAYLOAD_SIZE; // Fec are always of length 512 if ( len < PKT_MIN_HEADERLEN ) return E_NOHEADER; - if ( len > PKT_MAX_LEN || length > MAX_PAYLOAD_SIZE ) + if ( len > PKT_MAX_LEN || payload_length > MAX_PAYLOAD_SIZE ) return E_LENGTH; - ptypes_t type = (data[0] & TYPE_MASK) >> TYPE_SHIFT; + if ( type == PTYPE_FEC && (data[0] & TR_MASK ) != 0 ) return E_TR; - size_t expected_len = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + length; + size_t expected_len = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + payload_length; - if ( length > 0 ) + if ( payload_length > 0 ) expected_len += CRC_SIZE; - if ( len != expected_len && type != PTYPE_FEC) + if ( len != expected_len ) + { return E_UNCONSISTENT; + } + // We set the TR to 0 in order to calculcate the CRC on the header char modified_header[8]; @@ -94,11 +100,11 @@ pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *p @brief : We don't check the checksum and the window here **/ uint32_t crc2; - if ( (type == PTYPE_DATA && (data[0] & TR_MASK ) == 0 && length > 0) || type == PTYPE_FEC ) + if ( type == PTYPE_DATA && (data[0] & TR_MASK ) == 0 && payload_length > 0 ) { - memcpy((void *) &crc2, &data[12+length], 4); + memcpy((void *) &crc2, &data[12+payload_length], 4); crc2 = ntohl((uint32_t) crc2); - if (calculate_crc(&data[12], length) != crc2) + if (calculate_crc(&data[12], payload_length) != crc2) return E_CRC; pkt_set_crc2(pkt, crc2); } @@ -107,11 +113,11 @@ pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *p memcpy((void *) ×tamp, &data[4], 4); memcpy(&(pkt->header.front), data, 1); - pkt_set_length(pkt, length); + pkt_set_length(pkt, actual_indicated_length); pkt_set_seqnum(pkt, seqnum); pkt_set_timestamp(pkt, timestamp); pkt_set_crc1(pkt, crc1); - pkt_set_payload(pkt, &data[12], length); + pkt_set_payload(pkt, &data[12], payload_length); return PKT_OK; } @@ -145,7 +151,7 @@ pkt_status_code pkt_encode_ACK_NACK(const pkt_t *pkt, char *buf, size_t *len) pkt_status_code pkt_encode_DATA_FEC(const pkt_t *pkt, char *buf, size_t *len) { // Let's first copy the header - if ( *len < 12 ) return E_NOMEM; + if ( *len < PKT_MIN_HEADERLEN ) return E_NOMEM; memcpy((void *) &buf[0], (void *) &(pkt->header.front), 1); uint16_t n_length = htons(pkt_get_length(pkt)); memcpy((void *) &buf[1], (void *) &n_length, 2); @@ -157,20 +163,20 @@ pkt_status_code pkt_encode_DATA_FEC(const pkt_t *pkt, char *buf, size_t *len) size_t required_size; ptypes_t type = pkt_get_type(pkt); - uint16_t length = (type == PTYPE_DATA && pkt_get_tr(pkt) == 0) ? pkt_get_length(pkt) : MAX_PAYLOAD_SIZE; - required_size = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + length; + uint16_t payload_length = (type == PTYPE_DATA && pkt_get_tr(pkt) == 0) ? pkt_get_length(pkt) : MAX_PAYLOAD_SIZE; // Cause FEC payload is always max size + required_size = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + payload_length; if ( pkt_get_length(pkt) > 0 ) required_size += CRC_SIZE; if ( *len < required_size ) return E_NOMEM; - *len = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + length; - if ( pkt_get_length(pkt) > 0 ) + *len = sizeof(header_t) + TIMESTAMP_SIZE + CRC_SIZE + payload_length; + if ( payload_length > 0 ) { - memcpy((void *) &buf[12], (void *) pkt->payload, length); - uint32_t crc2 = htonl(calculate_crc(&buf[12], length)); - memcpy((void *) &buf[12 + length], (void *) &crc2, CRC_SIZE); + memcpy((void *) &buf[12], (void *) pkt->payload, payload_length); + uint32_t crc2 = htonl(calculate_crc(&buf[12], payload_length)); + memcpy((void *) &buf[12 + payload_length], (void *) &crc2, CRC_SIZE); *len = *len + CRC_SIZE; } return PKT_OK; diff --git a/src/receiver_utils.c b/src/receiver_utils.c index f8cfc56..0cd9365 100644 --- a/src/receiver_utils.c +++ b/src/receiver_utils.c @@ -50,17 +50,17 @@ int send_if_inneed(struct pollfd * pfd, receiver_state_t * state) int consume_data_pkt(receiver_state_t * state, uint8_t seqnum_to_consume) { - ASSERT(state->recvd_buf[seqnum_to_consume] != NULL); - size_t written = fwrite((void *) pkt_get_payload(state->recvd_buf[seqnum_to_consume]), sizeof(char), (size_t) pkt_get_length(state->recvd_buf[seqnum_to_consume]), stdout); - if (written != pkt_get_length(state->recvd_buf[seqnum_to_consume])) + ASSERT(state->recvd_data_buf[seqnum_to_consume] != NULL); + size_t written = fwrite((void *) pkt_get_payload(state->recvd_data_buf[seqnum_to_consume]), sizeof(char), (size_t) pkt_get_length(state->recvd_data_buf[seqnum_to_consume]), stdout); + if (written != pkt_get_length(state->recvd_data_buf[seqnum_to_consume])) { ERROR("Couldn't write the full packet content"); return -1; } fflush(stdout); - state->latest_timestamp = pkt_get_timestamp(state->recvd_buf[seqnum_to_consume]); - pkt_del(state->recvd_buf[seqnum_to_consume]); - state->recvd_buf[seqnum_to_consume] = NULL; + state->latest_timestamp = pkt_get_timestamp(state->recvd_data_buf[seqnum_to_consume]); + pkt_del(state->recvd_data_buf[seqnum_to_consume]); + state->recvd_data_buf[seqnum_to_consume] = NULL; return 0; } @@ -77,7 +77,7 @@ uint16_t next_four_packets_received(receiver_state_t * state, uint16_t position_ for (uint16_t i = position_to_start; i < position_to_start + FEC_CALCULATED_ON; i++) { - if (state->recvd_buf[i] == NULL) + if (state->recvd_data_buf[i] == NULL) return 0; } return 1; @@ -98,7 +98,7 @@ uint16_t can_consume(receiver_state_t * state, const pkt_t * latest_packet_recei ASSERT(state->next_to_consume >= 0 && state->next_to_consume < TWO_EXP_EIGHT); if (pkt_get_length(latest_packet_received) == 0) { // Last packet we read everything from the buffer - for (uint16_t i = state->next_to_consume; state->recvd_buf[i] != NULL; i = (i + 1) % TWO_EXP_EIGHT ) + for (uint16_t i = state->next_to_consume; state->recvd_data_buf[i] != NULL; i = (i + 1) % TWO_EXP_EIGHT ) to_consume++; } else { // We only read blocks of 4 packets. @@ -116,13 +116,13 @@ int update_buffer_upon_new_data(receiver_state_t * state, const pkt_t * pkt) uint8_t seqnum = pkt_get_seqnum(pkt); // New packet - if (state->recvd_buf[seqnum] == NULL) + if (state->recvd_data_buf[seqnum] == NULL) { pkt_t * pkt_to_store = pkt_new(); if (pkt_to_store == NULL) return -1; memcpy((void *) pkt_to_store, (void *) pkt, sizeof(pkt_t)); - state->recvd_buf[seqnum] = pkt_to_store; + state->recvd_data_buf[seqnum] = pkt_to_store; state->curr_recv_window = (state->curr_recv_window > 0) ? state->curr_recv_window-1 : 0; } @@ -131,7 +131,7 @@ int update_buffer_upon_new_data(receiver_state_t * state, const pkt_t * pkt) { state->last_received_in_order = seqnum; uint16_t idx = seqnum; - for (;state->recvd_buf[idx] != NULL; idx = (idx + 1) % TWO_EXP_EIGHT) + for (;state->recvd_data_buf[idx] != NULL; idx = (idx + 1) % TWO_EXP_EIGHT) state->last_received_in_order = idx; } uint16_t to_consume = can_consume(state, pkt); @@ -198,28 +198,6 @@ int prepare_ack_to_send(receiver_state_t * state) return 0; } -/** - * @brief This function handles PTYPE_FEC arriving packets and updates the state. - * - * @param state: The receiver state. - * @param pkt: The DATA packet. - * @returns 0 upon success else -1. - * - * @modifies: state. - */ -int handle_fec_pkt(receiver_state_t * state, const pkt_t * pkt) -{ - ASSERT(state != NULL && pkt != NULL); - if (state->last_received_in_order > pkt_get_seqnum(pkt)) - { - DEBUG("Received FEC with seqnum %d but wasn't used since last received in order is %d", pkt_get_seqnum(pkt), state->last_received_in_order); - return 0; - } - // Add FEC to state buffer of fec - // See if there's FEC that can be used and update buffer - return 0; -} - /** * @brief This function handles PTYPE_DATA arriving packets and updates the state. * @@ -254,6 +232,115 @@ int handle_data_pkt(receiver_state_t * state, const pkt_t * pkt) state->last_data_packet = pkt_get_seqnum(pkt); return 0; +} + +int use_fec(receiver_state_t * state, const pkt_t * fec, uint16_t missing_seqnum) +{ + pkt_t * new_packet = pkt_new(); + if ( new_packet == NULL ) + { + ERROR("An error occured when initiating a new packet in order to use FEC"); + return -1; + } + uint16_t fec_seqnum = pkt_get_seqnum(fec); + pkt_set_type(new_packet, PTYPE_DATA); + pkt_set_seqnum(new_packet, missing_seqnum); + pkt_set_length(new_packet, pkt_get_length(fec)); + for ( uint16_t idx = fec_seqnum; idx < fec_seqnum + FEC_CALCULATED_ON; idx++ ) + { + if ( state->recvd_data_buf[idx] != NULL) + pkt_set_length(new_packet, pkt_get_length(new_packet) ^ pkt_get_length(state->recvd_data_buf[idx])); + } + uint16_t payload_length = pkt_get_length(new_packet); + uint8_t new_payload[payload_length]; + uint8_t * fec_payload = (uint8_t *) pkt_get_payload(fec); + uint8_t * other_payloads[FEC_CALCULATED_ON-1]; + + for ( uint16_t idx = fec_seqnum, idx_other_payloads = 0; idx < fec_seqnum + FEC_CALCULATED_ON; idx++ ) + { + if ( state->recvd_data_buf[idx] != NULL) + { + other_payloads[idx_other_payloads] = (uint8_t *) pkt_get_payload(state->recvd_data_buf[idx]); + idx_other_payloads++; + } + } + + for ( uint16_t i = 0; i < payload_length; i++ ) + { + new_payload[i] = fec_payload[i]; + for ( uint16_t idx_other_payloads = 0; idx_other_payloads < FEC_CALCULATED_ON-1; idx_other_payloads++ ) + new_payload[i] = new_payload[i] ^ other_payloads[idx_other_payloads][i]; + } + pkt_set_payload(new_packet, (char *) new_payload, payload_length); + + DEBUG("Used FEC packet [%d] to recover data packet with seqnum [%d] | The new packet payload length is %d", + fec_seqnum, missing_seqnum, payload_length); + + // Add packet to received data buffer and treat the packet + // We pass the packet to the rest as if it had just came from the network + if (handle_data_pkt(state, new_packet) != 0) return -1; + + pkt_del(new_packet); + return 0; +} + +int can_fec_be_used(receiver_state_t * state, uint16_t fec_seqnum, uint16_t * missing_seqnum) +{ + uint16_t idx = fec_seqnum; + uint16_t in_range_missing = 0; + uint16_t in_range_available = 0; + for (; idx < fec_seqnum + FEC_CALCULATED_ON; idx++) + { + if (state->recvd_data_buf[idx] != NULL) + { + in_range_available++; + } else + { + in_range_missing++; + *missing_seqnum = idx; + } + } + return (in_range_available == FEC_CALCULATED_ON-1 && in_range_missing == 1); +} + +int potential_usage_of_fec(receiver_state_t * state, const pkt_t * pkt) +{ + uint16_t fec_seqnum = pkt_get_seqnum(pkt); + uint16_t missing_data_seqnum = 0; + if ( can_fec_be_used(state, fec_seqnum, &missing_data_seqnum) ) + { + DEBUG("Going to use FEC packet with seqnum %d to recover data packet with seqnum %d", + fec_seqnum, missing_data_seqnum); + if ( use_fec(state, pkt, missing_data_seqnum) != 0 ) return -1; + } else + { + DEBUG("Received FEC with seqnum [%d] but wasn't used", fec_seqnum); + } + return 0; +} + +/** + * @brief This function handles PTYPE_FEC arriving packets and updates the state. + * + * @param state: The receiver state. + * @param pkt: The DATA packet. + * @returns 0 upon success else -1. + * + * @requires: - The packet is in the current receiving window + * + * @modifies: state. + */ +int handle_fec_pkt(receiver_state_t * state, const pkt_t * pkt) +{ + ASSERT(state != NULL && pkt != NULL); + uint8_t seqnum = pkt_get_seqnum(pkt); + if (seqnum + 3 < state->last_received_in_order ) + { + DEBUG("Received FEC with seqnum [%d] but wasn't used since last received in order : %d", + pkt_get_seqnum(pkt), state->last_received_in_order); + return 0; + } + return potential_usage_of_fec(state, pkt); } /** @@ -272,12 +359,13 @@ int handle_data_pkt(receiver_state_t * state, const pkt_t * pkt) */ int handle_truncated_pkt(receiver_state_t * state, const pkt_t * pkt) { + DEBUG("Received a truncated packet with seqnum %d", pkt_get_seqnum(pkt)); if (pkt_get_type(pkt) != PTYPE_DATA) return 0; int free_idx = 0; for (; free_idx < RECV_MAX_SLCTV_RPT_WDW && state->nack_to_send[free_idx] != NULL; free_idx++); - if ( free_idx == 0 ) return 0; + if ( free_idx == 32 ) return 0; pkt_t * pkt_to_send = pkt_new(); if (pkt_to_send == NULL) return -1; @@ -334,7 +422,6 @@ int handle_valid_pkt(receiver_state_t * state, const pkt_t * pkt) { /* Type FEC */ return handle_fec_pkt(state, pkt); } - return 0; } @@ -366,10 +453,10 @@ int handle_incoming(struct pollfd * pfd, receiver_state_t * state) if (handle_valid_pkt(state, (const pkt_t *) pkt) != 0) return -1; } else { - DEBUG("Received a damaged packet with %d status.", pkt_status); + DEBUG("Received a damaged packet with %d status. and seqnum as %d", pkt_status, pkt_get_seqnum(pkt)); // See if there's a FEC that can be used and update buffer pkt_del(pkt); - return prepare_ack_to_send(state); + return 0; } pkt_del(pkt); return 0; @@ -432,7 +519,7 @@ receiver_state_t * state_new() if (to_return == NULL) return NULL; for (size_t i = 0; i < TWO_EXP_EIGHT; i++) - to_return->recvd_buf[i] = NULL; + to_return->recvd_data_buf[i] = NULL; for (size_t i = 0; i < RECV_MAX_SLCTV_RPT_WDW; i++) to_return->nack_to_send[i] = NULL; @@ -456,7 +543,7 @@ void state_del(receiver_state_t * state) { ASSERT(state != NULL); for (int i = 0; i < 31; i++) - pkt_del(state->recvd_buf[i]); + pkt_del(state->recvd_data_buf[i]); free(state->ack_to_send); free(state); diff --git a/src/receiver_utils.h b/src/receiver_utils.h index 180bda3..e6d9f04 100644 --- a/src/receiver_utils.h +++ b/src/receiver_utils.h @@ -41,7 +41,7 @@ typedef struct __attribute__((__packed__)) uint8_t recv_window_start; uint8_t last_received_in_order; uint32_t latest_timestamp; - pkt_t * recvd_buf[TWO_EXP_EIGHT]; + pkt_t * recvd_data_buf[TWO_EXP_EIGHT]; pkt_t * ack_to_send; pkt_t * nack_to_send[RECV_MAX_SLCTV_RPT_WDW]; uint8_t transfer_done; diff --git a/tests/advanced_test.sh b/tests/advanced_test.sh index 72fb0aa..5e29774 100755 --- a/tests/advanced_test.sh +++ b/tests/advanced_test.sh @@ -30,11 +30,11 @@ port1=$(comm -23 <(seq 65000 65200 | sort) <(ss -Htan | awk '{print $4}' | cut - port2=$(comm -23 <(seq 65000 65200 | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1) # We first launch the link simulator -./linksimulator/link_sim -p $port2 -P $port1 -l 20 -d 300 -e 8 -c 12 -R \ +./linksimulator/link_sim -p $port2 -P $port1 -l 25 -d 0 -e 20 -c 10 \ &>${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_link.log & link_pid=$! # We launch the receiver and capture its output -valgrind --leak-check=full --log-file=${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_valgrind_receiver.log \ +valgrind --leak-check=full --log-file=${TEST_OUTPUT_FILES}/adv_valgrind_${BSN_PRE}_receiver.log \ ./receiver ::1 $port1 1> ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_received_file.${BSN_EXT} \ 2> ${TEST_OUTPUT_FILES}/adv_${BSN_PRE}_receiver.log & receiver_pid=$! -- GitLab