From 60c3d79f477356cd0fef368a7ed51721dfc9c352 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 28 Feb 2022 16:20:52 +0100 Subject: [PATCH] rlc, nr: complete packing of status PDUs with 18bit SNs --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 6 + lib/src/rlc/rlc_am_nr_packing.cc | 355 +++++++++++++------ lib/test/rlc/rlc_am_nr_pdu_test.cc | 375 ++++++++++++++++++--- 3 files changed, 598 insertions(+), 138 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 234b4ea22..0bdc845d1 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -113,10 +113,16 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, const uint32_t nof_bytes, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); +uint32_t +rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status); +uint32_t +rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status); int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, const rlc_am_nr_sn_size_t sn_size, byte_buffer_t* pdu); +int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu); +int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu); /** * Logs Status PDU into provided log channel, using fmt_str as format string diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index 2f31ca695..eac502c8e 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -141,6 +141,11 @@ uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, b return len; } +/**************************************************************************** + * Status PDU pack/unpack helper functions + * Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5 + ***************************************************************************/ + uint32_t rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) { @@ -151,6 +156,16 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, const uint32_t nof_bytes, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) +{ + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + return rlc_am_nr_read_status_pdu_12bit_sn(payload, nof_bytes, status); + } else { // 18bit SN + return rlc_am_nr_read_status_pdu_18bit_sn(payload, nof_bytes, status); + } +} + +uint32_t +rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) { uint8_t* ptr = const_cast(payload); @@ -163,68 +178,151 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, return 0; } - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN - ptr++; + status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN + ptr++; + + status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + + // read E1 flag + uint8_t e1 = *ptr & 0x80; + + // sanity check for reserved bits + if ((*ptr & 0x7f) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } - status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN + // all good, continue with next byte depending on E1 + ptr++; + + // reset number of acks + status->N_nack = 0; + + while (e1 != 0) { + // E1 flag set, read a NACK_SN + rlc_status_nack_t nack = {}; + nack.nack_sn = (*ptr & 0xff) << 4; ptr++; - // read E1 flag - uint8_t e1 = *ptr & 0x80; + e1 = *ptr & 0x08; // 1 = further NACKs follow + uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows + uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs) // sanity check for reserved bits - if ((*ptr & 0x7f) != 0) { + if ((*ptr & 0x01) != 0) { fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); return 0; } + nack.nack_sn |= (*ptr & 0xF0) >> 4; + status->nacks[status->N_nack] = nack; - // all good, continue with next byte depending on E1 ptr++; + if (e2 != 0) { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_start |= (*ptr); + ptr++; + status->nacks[status->N_nack].so_end = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_end |= (*ptr); + ptr++; + } + if (e3 != 0) { + status->nacks[status->N_nack].has_nack_range = true; + status->nacks[status->N_nack].nack_range = (*ptr); + ptr++; + } + status->N_nack++; + if (uint32_t(ptr - payload) > nof_bytes) { + fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); + return 0; + } + } - // reset number of acks - status->N_nack = 0; + return SRSRAN_SUCCESS; +} - while (e1 != 0) { - // E1 flag set, read a NACK_SN - rlc_status_nack_t nack = {}; - nack.nack_sn = (*ptr & 0xff) << 4; - ptr++; +uint32_t +rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) +{ + uint8_t* ptr = const_cast(payload); - e1 = *ptr & 0x08; // 1 = further NACKs follow - uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows - uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs) + // fixed part + status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT - // sanity check for reserved bits - if ((*ptr & 0x01) != 0) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - nack.nack_sn |= (*ptr & 0xF0) >> 4; - status->nacks[status->N_nack] = nack; + // sanity check + if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + status->ack_sn = (*ptr & 0x0F) << 14; // upper 4 bits of SN + ptr++; + + status->ack_sn |= (*ptr & 0xFF) << 6; // center 8 bits of SN + ptr++; + + status->ack_sn |= (*ptr & 0xFC) >> 2; // lower 6 bits of SN + + // read E1 flag + uint8_t e1 = *ptr & 0x02; + + // sanity check for reserved bits + if ((*ptr & 0x01) != 0) { + fprintf(stderr, "Malformed PDU, reserved bit is set.\n"); + return 0; + } + + // all good, continue with next byte depending on E1 + ptr++; + // reset number of acks + status->N_nack = 0; + + while (e1 != 0) { + // E1 flag set, read a NACK_SN + rlc_status_nack_t nack = {}; + + nack.nack_sn = (*ptr & 0xFF) << 10; // upper 8 bits of SN + ptr++; + nack.nack_sn |= (*ptr & 0xFF) << 2; // center 8 bits of SN + ptr++; + nack.nack_sn |= (*ptr & 0xC0) >> 6; // lower 2 bits of SN + + e1 = *ptr & 0x20; // 1 = further NACKs follow + uint8_t e2 = *ptr & 0x10; // 1 = set of {so_start, so_end} follows + uint8_t e3 = *ptr & 0x08; // 1 = NACK range follows (i.e. NACK across multiple SNs) + + // sanity check for reserved bits + if ((*ptr & 0x07) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + status->nacks[status->N_nack] = nack; + + ptr++; + if (e2 != 0) { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_start |= (*ptr); + ptr++; + status->nacks[status->N_nack].so_end = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_end |= (*ptr); + ptr++; + } + if (e3 != 0) { + status->nacks[status->N_nack].has_nack_range = true; + status->nacks[status->N_nack].nack_range = (*ptr); ptr++; - if (e2 != 0) { - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = (*ptr) << 8; - ptr++; - status->nacks[status->N_nack].so_start |= (*ptr); - ptr++; - status->nacks[status->N_nack].so_end = (*ptr) << 8; - ptr++; - status->nacks[status->N_nack].so_end |= (*ptr); - ptr++; - } - if (e3 != 0) { - status->nacks[status->N_nack].has_nack_range = true; - status->nacks[status->N_nack].nack_range = (*ptr); - ptr++; - } - status->N_nack++; - if (uint32_t(ptr - payload) > nof_bytes) { - fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); - return 0; - } + } + status->N_nack++; + if (uint32_t(ptr - payload) > nof_bytes) { + fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); + return 0; } } @@ -240,75 +338,134 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, const rlc_am_nr_sn_size_t sn_size, byte_buffer_t* pdu) +{ + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + return rlc_am_nr_write_status_pdu_12bit_sn(status_pdu, pdu); + } else { // 18bit SN + return rlc_am_nr_write_status_pdu_18bit_sn(status_pdu, pdu); + } +} + +int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu) { uint8_t* ptr = pdu->msg; // fixed header part *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - // write first 4 bit of ACK_SN - *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN - ptr++; + // write first 4 bit of ACK_SN + *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN + ptr++; + *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN + ptr++; - // write E1 flag in octet 3 - if (status_pdu.N_nack > 0) { - *ptr = 0x80; - } else { - *ptr = 0x00; - } - ptr++; + // write E1 flag in octet 3 + if (status_pdu.N_nack > 0) { + *ptr = 0x80; + } else { + *ptr = 0x00; + } + ptr++; + + if (status_pdu.N_nack > 0) { + for (uint32_t i = 0; i < status_pdu.N_nack; i++) { + // write first 8 bit of NACK_SN + *ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff; + ptr++; + + // write remaining 4 bits of NACK_SN + *ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4; + // Set E1 if necessary + if (i < (uint32_t)(status_pdu.N_nack - 1)) { + *ptr |= 0x08; + } - if (status_pdu.N_nack > 0) { - for (uint32_t i = 0; i < status_pdu.N_nack; i++) { - // write first 8 bit of NACK_SN - *ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff; + if (status_pdu.nacks[i].has_so) { + // Set E2 + *ptr |= 0x04; + } + + if (status_pdu.nacks[i].has_nack_range) { + // Set E3 + *ptr |= 0x02; + } + + ptr++; + if (status_pdu.nacks[i].has_so) { + (*ptr) = status_pdu.nacks[i].so_start >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_start; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end; ptr++; + } + if (status_pdu.nacks[i].has_nack_range) { + (*ptr) = status_pdu.nacks[i].nack_range; + ptr++; + } + } + } + + pdu->N_bytes = ptr - pdu->msg; + + return SRSRAN_SUCCESS; +} + +int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu) +{ + uint8_t* ptr = pdu->msg; + + // fixed header part + *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero + + *ptr |= (status_pdu.ack_sn >> 14) & 0x0F; // upper 4 bits of SN + ptr++; + *ptr = (status_pdu.ack_sn >> 6) & 0xFF; // center 8 bits of SN + ptr++; + *ptr = (status_pdu.ack_sn << 2) & 0xFC; // lower 6 bits of SN - // write remaining 4 bits of NACK_SN - *ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4; - // Set E1 if necessary - if (i < (uint32_t)(status_pdu.N_nack - 1)) { - *ptr |= 0x08; - } + // set E1 flag if necessary + if (status_pdu.N_nack > 0) { + *ptr |= 0x02; + } + ptr++; - if (status_pdu.nacks[i].has_so) { - // Set E2 - *ptr |= 0x04; - } + if (status_pdu.N_nack > 0) { + for (uint32_t i = 0; i < status_pdu.N_nack; i++) { + *ptr = (status_pdu.nacks[i].nack_sn >> 10) & 0xFF; // upper 8 bits of SN + ptr++; + *ptr = (status_pdu.nacks[i].nack_sn >> 2) & 0xFF; // center 8 bits of SN + ptr++; + *ptr = (status_pdu.nacks[i].nack_sn << 6) & 0xC0; // lower 2 bits of SN - if (status_pdu.nacks[i].has_nack_range) { - // Set E3 - *ptr |= 0x02; - } + if (i < (uint32_t)(status_pdu.N_nack - 1)) { + *ptr |= 0x20; // Set E1 + } + if (status_pdu.nacks[i].has_so) { + *ptr |= 0x10; // Set E2 + } + if (status_pdu.nacks[i].has_nack_range) { + *ptr |= 0x08; // Set E3 + } + ptr++; + if (status_pdu.nacks[i].has_so) { + (*ptr) = status_pdu.nacks[i].so_start >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_start; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end; + ptr++; + } + if (status_pdu.nacks[i].has_nack_range) { + (*ptr) = status_pdu.nacks[i].nack_range; ptr++; - if (status_pdu.nacks[i].has_so) { - (*ptr) = status_pdu.nacks[i].so_start >> 8; - ptr++; - (*ptr) = status_pdu.nacks[i].so_start; - ptr++; - (*ptr) = status_pdu.nacks[i].so_end >> 8; - ptr++; - (*ptr) = status_pdu.nacks[i].so_end; - ptr++; - } - if (status_pdu.nacks[i].has_nack_range) { - (*ptr) = status_pdu.nacks[i].nack_range; - ptr++; - } } } - } else { - // 18bit SN - *ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN - ptr++; - *ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN - ptr++; } pdu->N_bytes = ptr - pdu->msg; diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc index dbeed0423..00c9d9861 100644 --- a/lib/test/rlc/rlc_am_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -221,11 +221,11 @@ int rlc_am_nr_pdu_test6() return SRSRAN_SUCCESS; } -///< Control PDU tests +///< Control PDU tests (12bit SN) // Status PDU for 12bit SN with ACK_SN=2065 and no further NACK_SN (E1 bit not set) -int rlc_am_nr_control_pdu_test1() +int rlc_am_nr_control_pdu_12bit_sn_test1() { - test_delimit_logger delimiter("Control PDU test 1"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 1"); const int len = 3; std::array tv = {0x08, 0x11, 0x00}; srsran::byte_buffer_t pdu = make_pdu_and_log(tv); @@ -253,9 +253,9 @@ int rlc_am_nr_control_pdu_test1() } // Status PDU for 12bit SN with ACK_SN=2065 and NACK_SN=273 (E1 bit set) -int rlc_am_nr_control_pdu_test2() +int rlc_am_nr_control_pdu_12bit_sn_test2() { - test_delimit_logger delimiter("Control PDU test 2"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 2"); const int len = 5; std::array tv = {0x08, 0x11, 0x80, 0x11, 0x10}; srsran::byte_buffer_t pdu = make_pdu_and_log(tv); @@ -274,7 +274,7 @@ int rlc_am_nr_control_pdu_test2() // pack again TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); - // TESTASSERT(pdu.N_bytes == tv.size()); + TESTASSERT(pdu.N_bytes == tv.size()); write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); @@ -285,9 +285,9 @@ int rlc_am_nr_control_pdu_test2() // Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF // E1 and E2 bit set on first NACK, only E2 on second. -int rlc_am_nr_control_pdu_test3() +int rlc_am_nr_control_pdu_12bit_sn_test3() { - test_delimit_logger delimiter("Control PDU test 3"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 3"); const int len = 15; std::array tv = { 0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x34, 0x00, 0x05, 0xFF, 0xFF}; @@ -322,9 +322,9 @@ int rlc_am_nr_control_pdu_test3() // Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275 // E1 and E2 bit set on first NACK, neither E1 or E2 on the second. -int rlc_am_nr_control_pdu_test4() +int rlc_am_nr_control_pdu_12bit_sn_test4() { - test_delimit_logger delimiter("Control PDU test 4"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 4"); const int len = 11; std::array tv = {0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x30}; srsran::byte_buffer_t pdu = make_pdu_and_log(tv); @@ -359,9 +359,9 @@ int rlc_am_nr_control_pdu_test4() // Malformed Status PDU, with E1 still set at the end of the PDU // 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF // E1 and E2 bit set on first NACK, only E2 on second. -int rlc_am_nr_control_pdu_test5() +int rlc_am_nr_control_pdu_12bit_sn_test5() { - test_delimit_logger delimiter("Control PDU test 5"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 5"); const int len = 15; std::array tv = { 0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x3c, 0x00, 0x05, 0xFF, 0xFF}; @@ -382,23 +382,23 @@ int rlc_am_nr_control_pdu_test5() // starting at NACK_SN=276, SO_START=2, // ending at NACK_SN=279, SO_END=5 // E1 and E3 bit set on first NACK, E2 and E3 bit set on the second. -int rlc_am_nr_control_pdu_test_nack_range() +int rlc_am_nr_control_pdu_12bit_sn_test_nack_range() { - test_delimit_logger delimiter("Control PDU test NACK range"); + test_delimit_logger delimiter("Control PDU (12bit SN) test NACK range"); const int len = 13; - std::array tv = {0x08, // D/C | CPT | ACK_SN_upper - 0x11, // ACK_SN_lower - 0x80, // E1 | R - 0x11, // NACK_SN_upper - 0x1a, // NACK_SN_lower | E1 | E2 | E3 | R - 0x03, // NACK_range - 0x11, // NACK_SN_upper - 0x46, // NACK_SN_lower | E1 | E2 | E3 | R - 0x00, // SO_START_upper - 0x02, // SO_START_lower - 0x00, // SO_END_upper - 0x05, // SO_END_lower - 0x04}; // NACK_range + std::array tv = {0x08, // D/C | 3CPT | 4ACK_SN_upper + 0x11, // 8ACK_SN_lower + 0x80, // E1 | 7R + 0x11, // 8NACK_SN_upper + 0x1a, // 4NACK_SN_lower | E1 | E2 | E3 | R + 0x03, // 8NACK_range + 0x11, // 8NACK_SN_upper + 0x46, // 4NACK_SN_lower | E1 | E2 | E3 | R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0x04}; // 8NACK_range srsran::byte_buffer_t pdu = make_pdu_and_log(tv); TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); @@ -433,6 +433,273 @@ int rlc_am_nr_control_pdu_test_nack_range() return SRSRAN_SUCCESS; } +///< Control PDU tests (18bit SN) +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 and no further NACK_SN (E1 bit not set) +int rlc_am_nr_control_pdu_18bit_sn_test1() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 1"); + const int len = 3; + std::array tv = {0x0E, 0x66, 0x64}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 0); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set) +// and NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 +int rlc_am_nr_control_pdu_18bit_sn_test2() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 2"); + const int len = 6; + std::array tv = {0x0E, 0x66, 0x66, 0xD9, 0x99, 0x80}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 1); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E2 bit set), +// SO_START=5, SO_END=0xFFFF +int rlc_am_nr_control_pdu_18bit_sn_test3() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 3"); + const int len = 17; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11010000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x05, // 8SO_START_lower + 0xFF, // 8SO_END_upper + 0xFF}; // 8SO_END_lower + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + TESTASSERT(status_pdu.nacks[0].has_so == true); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 222975); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 5); + TESTASSERT(status_pdu.nacks[1].so_end == 0xFFFF); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E1 and E2 bit not set), +int rlc_am_nr_control_pdu_18bit_sn_test4() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 4"); + const int len = 13; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11000000}; // 2NACK_SN_lower | E1 | E2 | E3 | 3R + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + TESTASSERT(status_pdu.nacks[0].has_so == true); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 222975); + TESTASSERT(status_pdu.nacks[1].has_so == false); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Malformed Status PDU, similar to test3 but with E1 still set at the end of the PDU +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 ([!E1!] and E2 bit set), +// SO_START=5, SO_END=0xFFFF +int rlc_am_nr_control_pdu_18bit_sn_test5() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 5"); + const int len = 17; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11110000, // 2NACK_SN_lower | [!E1!] | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x05, // 8SO_START_lower + 0xFF, // 8SO_END_upper + 0xFF}; // 8SO_END_lower + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=200977=0x31111=0b11 0001 0001 0001 0001, +// NACK range0: 3 full SDUs, NACK_SN=69905=0x11111=0b01 0001 0001 0001 0001 +// NACK range1: missing segment sequence across 4 SDUs +// starting at NACK_SN=69913=0x11119=0b01 0001 0001 0001 1001, SO_START=2, +// ending at NACK_SN=69916, SO_END=5 +// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second. +int rlc_am_nr_control_pdu_18bit_sn_test_nack_range() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test NACK range"); + const int len = 15; + std::array tv = {0b00001100, // D/C | 3CPT | 4ACK_SN_upper + 0b01000100, // 8ACK_SN_center + 0b01000110, // 6ACK_SN_lower | E1 | R + 0b01000100, // 8NACK_SN_upper + 0b01000100, // 8NACK_SN_center + 0b01101000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x03, // 8NACK_range + 0b01000100, // 8NACK_SN_upper + 0b01000110, // 8NACK_SN_center + 0b01011000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0x04}; // 8NACK_range + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 200977); + TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 69905); + TESTASSERT(status_pdu.nacks[0].has_so == false); + TESTASSERT(status_pdu.nacks[0].has_nack_range == true); + TESTASSERT(status_pdu.nacks[0].nack_range == 3); + + TESTASSERT(status_pdu.nacks[1].nack_sn == 69913); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 2); + TESTASSERT(status_pdu.nacks[1].so_end == 5); + TESTASSERT(status_pdu.nacks[1].has_nack_range == true); + TESTASSERT(status_pdu.nacks[1].nack_range == 4); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { static const struct option long_options[] = {{"pcap", no_argument, nullptr, 'p'}, {nullptr, 0, nullptr, 0}}; @@ -489,33 +756,63 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test1()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test1() failed.\n"); + if (rlc_am_nr_control_pdu_12bit_sn_test1()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test1() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test2()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test2() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test3()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test3() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test4()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test4() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test5()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test5() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test_nack_range()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test_nack_range() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test1()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test1() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test2()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test2() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test2()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test2() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test3()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test3() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test3()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test3() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test4()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test4() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test4()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test4() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test5()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test5() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test5()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test5() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test_nack_range()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_range() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test_nack_range()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test_nack_range() failed.\n"); return SRSRAN_ERROR; }