Merge branch 'next' into agpl_next

This commit is contained in:
Codebot 2022-05-22 21:12:28 +00:00 committed by SRS codebot
commit 49554c2c46
21 changed files with 778 additions and 263 deletions

View File

@ -552,6 +552,12 @@ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
endif()
# GCC >= 12.1.0: Disable analysis on "maybe-uninitialized" variables because of the high false-positive rate
if(CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-maybe-uninitialized")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized")
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
# Increase inlining limit to allow gcc compilation on e.g. RPi2
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --param large-function-growth=1600")

View File

@ -341,6 +341,9 @@ public:
constexpr static void (Derived::*react_fn)(SrcState&, const Event&) = ReactFn;
constexpr static bool (Derived::*guard_fn)(SrcState&, const Event&) = GuardFn;
// ignore warning "never nullptr" for template specialization w/wo defaults for ReactFn or GuardFn
_Pragma("GCC diagnostic push"); // save current diagnostic config
_Pragma("GCC diagnostic ignored \"-Waddress\""); // ignore -Waddress
static bool react(derived_view* f, src_state_t& s, const event_t& ev)
{
if (guard_fn == nullptr or (f->*guard_fn)(s, ev)) {
@ -351,6 +354,7 @@ public:
}
return false;
}
_Pragma("GCC diagnostic pop"); // restore diagnostic config
template <typename SrcState2, typename Event2>
using is_match = std::is_same<type_list<SrcState2, Event2>, type_list<src_state_t, event_t> >;
@ -372,6 +376,9 @@ public:
constexpr static void (Derived::*react_fn)(const Event&) = ReactFn;
constexpr static bool (Derived::*guard_fn)(const Event&) = GuardFn;
// ignore warning "never nullptr" for template specialization w/wo defaults for ReactFn or GuardFn
_Pragma("GCC diagnostic push"); // save current diagnostic config
_Pragma("GCC diagnostic ignored \"-Waddress\""); // ignore -Waddress
template <typename SrcState>
static bool react(derived_view* f, SrcState& s, const event_t& ev)
{
@ -383,6 +390,7 @@ public:
}
return false;
}
_Pragma("GCC diagnostic pop"); // restore diagnostic config
template <typename SrcState2, typename Event2>
using is_match = std::is_same<Event2, event_t>;

View File

@ -202,8 +202,12 @@ public:
{
size_ = nof_items;
cap_ = nof_items;
data_ = new T[cap_];
std::copy(ptr, ptr + size_, data_);
if (ptr != NULL) {
data_ = new T[cap_];
std::copy(ptr, ptr + size_, data_);
} else {
data_ = NULL;
}
}
~dyn_array()
{

View File

@ -27,6 +27,7 @@
#include "srsran/adt/intrusive_list.h"
#include "srsran/common/buffer_pool.h"
#include <array>
#include <list>
#include <vector>
namespace srsran {
@ -458,6 +459,89 @@ private:
size_t rpos = 0;
};
template <class T>
class pdu_retx_queue_list
{
std::list<T> queue;
public:
~pdu_retx_queue_list() = default;
T& push()
{
queue.emplace_back();
return queue.back();
}
void pop()
{
if (not queue.empty()) {
queue.pop_front();
}
}
T& front()
{
assert(not queue.empty());
return queue.front();
}
const std::list<T>& get_inner_queue() const { return queue; }
void clear() { queue.clear(); }
size_t size() const { return queue.size(); }
bool empty() const { return queue.empty(); }
bool has_sn(uint32_t sn) const
{
if (queue.empty()) {
return false;
}
for (auto elem : queue) {
if (elem.sn == sn) {
return true;
}
}
return false;
};
bool has_sn(uint32_t sn, uint32_t so) const
{
if (queue.empty()) {
return false;
}
for (auto elem : queue) {
if (elem.sn == sn) {
if (elem.overlaps(so)) {
return true;
}
}
}
return false;
};
/**
* @brief remove_sn removes SN from queue and returns after first match
* @param sn sequence number to be removed from queue
* @return true if one element was removed, false if no element to remove was found
*/
bool remove_sn(uint32_t sn)
{
if (queue.empty()) {
return false;
}
auto iter = queue.begin();
while (iter != queue.end()) {
if (iter->sn == sn) {
iter = queue.erase(iter);
return true;
} else {
++iter;
}
}
return false;
}
};
} // namespace srsran
#endif // SRSRAN_RLC_AM_DATA_STRUCTS_H

View File

@ -163,7 +163,7 @@ private:
std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> > tx_window;
// Queues, buffers and container
std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> > retx_queue;
pdu_retx_queue_list<rlc_amd_retx_nr_t> retx_queue;
uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented.
pdcp_sn_vector_t notify_info_vec;
@ -189,6 +189,7 @@ public:
void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing.
rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing.
uint32_t get_tx_window_utilization() { return tx_window->size(); } // This should only be used for testing.
size_t get_retx_queue_size() const { return retx_queue.size(); } // This should only be used for testing.
// Debug Helpers
void debug_state() const;

View File

@ -85,7 +85,7 @@ public:
} else {
integrity_direction = direction;
}
logger.debug("LCID=%d, integrity=%s", lcid, srsran_direction_text[integrity_direction]);
logger.debug("Enabled integrity. LCID=%d, integrity=%s", lcid, srsran_direction_text[integrity_direction]);
}
void enable_encryption(srsran_direction_t direction = DIRECTION_TXRX)
@ -98,7 +98,7 @@ public:
} else {
encryption_direction = direction;
}
logger.debug("LCID=%d, encryption=%s", lcid, srsran_direction_text[integrity_direction]);
logger.debug("Enabled encryption. LCID=%d, encryption=%s", lcid, srsran_direction_text[integrity_direction]);
}
void enable_security_timed(srsran_direction_t direction, uint32_t sn)

View File

@ -35,7 +35,6 @@
#include <map>
namespace srsran {
/****************************************************************************
* NR PDCP Entity
* PDCP entity for 5G NR
@ -49,7 +48,7 @@ public:
srsran::task_sched_handle task_sched_,
srslog::basic_logger& logger,
uint32_t lcid);
~pdcp_entity_nr() final;
~pdcp_entity_nr() final = default;
bool configure(const pdcp_config_t& cnfg_) final;
void reset() final;
void reestablish() final;
@ -113,6 +112,11 @@ private:
// COUNT overflow protection
bool tx_overflow = false;
bool rx_overflow = false;
enum class rlc_mode_t {
UM,
AM,
} rlc_mode;
};
/*

View File

@ -117,18 +117,18 @@ bool pdcp_entity_base::integrity_verify(uint8_t* msg, uint32_t msg_len, uint32_t
if (sec_cfg.integ_algo != INTEGRITY_ALGORITHM_ID_EIA0) {
for (uint8_t i = 0; i < 4; i++) {
if (mac[i] != mac_exp[i]) {
logger.error(mac_exp, 4, "MAC mismatch (expected)");
logger.error(mac, 4, "MAC mismatch (found)");
is_valid = false;
break;
}
}
srslog::log_channel& channel = is_valid ? logger.debug : logger.warning;
channel("Integrity check input: COUNT %" PRIu32 ", Bearer ID %d, Direction %s",
channel("Integrity check input - COUNT %" PRIu32 ", Bearer ID %d, Direction %s",
count,
cfg.bearer_id,
cfg.rx_direction == SECURITY_DIRECTION_DOWNLINK ? "Downlink" : "Uplink");
channel(k_int, 32, "Integrity check key:");
channel(mac_exp, 4, "MAC %s (expected):", is_valid ? "match" : "mismatch");
channel(mac, 4, "MAC %s (found):", is_valid ? "match" : "mismatch");
channel(msg, msg_len, "Integrity check input msg (Bytes=%" PRIu32 "):", msg_len);
}

View File

@ -343,6 +343,8 @@ void pdcp_entity_lte::handle_srb_pdu(srsran::unique_byte_buffer_t pdu)
logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rb_name.c_str());
rrc->notify_pdcp_integrity_error(lcid);
return; // Discard
} else {
logger.debug(pdu->msg, pdu->N_bytes, "%s: Integrity verification successful", rb_name.c_str());
}
}

View File

@ -41,15 +41,6 @@ pdcp_entity_nr::pdcp_entity_nr(srsue::rlc_interface_pdcp* rlc_,
encryption_direction = DIRECTION_NONE;
}
pdcp_entity_nr::~pdcp_entity_nr() {}
// Reestablishment procedure: 38.323 5.2
void pdcp_entity_nr::reestablish()
{
logger.info("Re-establish %s with bearer ID: %d", rb_name.c_str(), cfg.bearer_id);
// TODO
}
bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_)
{
if (active) {
@ -65,6 +56,8 @@ bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_)
rb_name = cfg.get_rb_name();
window_size = 1 << (cfg.sn_len - 1);
rlc_mode = rlc->rb_is_um(lcid) ? rlc_mode_t::UM : rlc_mode_t::AM;
// Timers
reordering_timer = task_sched.get_unique_timer();
@ -73,15 +66,28 @@ bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_)
reordering_timer.set(static_cast<uint32_t>(cfg.t_reordering), *reordering_fnc);
}
active = true;
logger.info("%s PDCP-NR entity configured. SN_LEN=%d, Discard timer %d, Re-ordering timer %d, RAT=%s",
logger.info("%s PDCP-NR entity configured. SN_LEN=%d, Discard timer %d, Re-ordering timer %d, RLC=%s, RAT=%s",
rb_name,
cfg.sn_len,
cfg.discard_timer,
cfg.t_reordering,
rlc_mode == rlc_mode_t::UM ? "UM" : "AM",
to_string(cfg.rat));
// disable discard timer if using UM
if (rlc_mode == rlc_mode_t::UM) {
cfg.discard_timer = pdcp_discard_timer_t::infinity;
}
return true;
}
// Reestablishment procedure: 38.323 5.2
void pdcp_entity_nr::reestablish()
{
logger.info("Re-establish %s with bearer ID: %d", rb_name.c_str(), cfg.bearer_id);
// TODO
}
// Used to stop/pause the entity (called on RRC conn release)
void pdcp_entity_nr::reset()
{
@ -95,8 +101,9 @@ void pdcp_entity_nr::write_sdu(unique_byte_buffer_t sdu, int sn)
// Log SDU
logger.info(sdu->msg,
sdu->N_bytes,
"TX %s SDU, integrity=%s, encryption=%s",
"TX %s SDU (%dB), integrity=%s, encryption=%s",
rb_name.c_str(),
sdu->N_bytes,
srsran_direction_text[integrity_direction],
srsran_direction_text[encryption_direction]);
@ -121,22 +128,43 @@ void pdcp_entity_nr::write_sdu(unique_byte_buffer_t sdu, int sn)
// Perform header compression TODO
// Integrity protection
uint8_t mac[4];
integrity_generate(sdu->msg, sdu->N_bytes, tx_next, mac);
// Ciphering
cipher_encrypt(sdu->msg, sdu->N_bytes, tx_next, sdu->msg);
// Write PDCP header info
write_data_header(sdu, tx_next);
// TS 38.323, section 5.9: Integrity protection
// The data unit that is integrity protected is the PDU header
// and the data part of the PDU before ciphering.
uint8_t mac[4] = {};
if (is_srb() || (is_drb() && (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX))) {
integrity_generate(sdu->msg, sdu->N_bytes, tx_next, mac);
}
// Append MAC-I
append_mac(sdu, mac);
if (is_srb() || (is_drb() && (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX))) {
append_mac(sdu, mac);
}
// TS 38.323, section 5.8: Ciphering
// The data unit that is ciphered is the MAC-I and the
// data part of the PDCP Data PDU except the
// SDAP header and the SDAP Control PDU if included in the PDCP SDU.
if (encryption_direction == DIRECTION_TX || encryption_direction == DIRECTION_TXRX) {
cipher_encrypt(
&sdu->msg[cfg.hdr_len_bytes], sdu->N_bytes - cfg.hdr_len_bytes, tx_next, &sdu->msg[cfg.hdr_len_bytes]);
}
// Set meta-data for RLC AM
sdu->md.pdcp_sn = tx_next;
logger.info(sdu->msg,
sdu->N_bytes,
"TX %s PDU (%dB), HFN=%d, SN=%d, integrity=%s, encryption=%s",
rb_name.c_str(),
sdu->N_bytes,
HFN(tx_next),
SN(tx_next),
srsran_direction_text[integrity_direction],
srsran_direction_text[encryption_direction]);
// Check if PDCP is associated with more than on RLC entity TODO
// Write to lower layers
rlc->write_sdu(lcid, std::move(sdu));
@ -157,18 +185,19 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu)
srsran_direction_text[integrity_direction],
srsran_direction_text[encryption_direction]);
if (rx_overflow) {
logger.warning("Rx PDCP COUNTs have overflowed. Discarding SDU.");
return;
}
// Sanity check
if (pdu->N_bytes <= cfg.hdr_len_bytes) {
return;
}
logger.debug("Rx PDCP state - RX_NEXT=%u, RX_DELIV=%u, RX_REORD=%u", rx_next, rx_deliv, rx_reord);
// Extract RCVD_SN from header
uint32_t rcvd_sn = read_data_header(pdu);
discard_data_header(pdu); // TODO: Check wheather the header is part of integrity check.
// Extract MAC
uint8_t mac[4];
extract_mac(pdu, mac);
// Calculate RCVD_COUNT
uint32_t rcvd_hfn, rcvd_count;
@ -181,17 +210,40 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu)
}
rcvd_count = COUNT(rcvd_hfn, rcvd_sn);
logger.debug("RCVD_HFN %u RCVD_SN %u, RCVD_COUNT %u", rcvd_hfn, rcvd_sn, rcvd_count);
logger.debug("Estimated RCVD_HFN=%u, RCVD_SN=%u, RCVD_COUNT=%u", rcvd_hfn, rcvd_sn, rcvd_count);
// Decripting
cipher_decrypt(pdu->msg, pdu->N_bytes, rcvd_count, pdu->msg);
// Integrity check
bool is_valid = integrity_verify(pdu->msg, pdu->N_bytes, rcvd_count, mac);
if (!is_valid) {
return; // Invalid packet, drop.
// TS 38.323, section 5.8: Deciphering
// The data unit that is ciphered is the MAC-I and the
// data part of the PDCP Data PDU except the
// SDAP header and the SDAP Control PDU if included in the PDCP SDU.
if (encryption_direction == DIRECTION_RX || encryption_direction == DIRECTION_TXRX) {
cipher_decrypt(
&pdu->msg[cfg.hdr_len_bytes], pdu->N_bytes - cfg.hdr_len_bytes, rcvd_count, &pdu->msg[cfg.hdr_len_bytes]);
}
// Extract MAC-I
// Always extract from SRBs, only extract from DRBs if integrity is enabled
uint8_t mac[4] = {};
if (is_srb() || (is_drb() && (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX))) {
extract_mac(pdu, mac);
}
// TS 38.323, section 5.9: Integrity verification
// The data unit that is integrity protected is the PDU header
// and the data part of the PDU before ciphering.
if (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX) {
bool is_valid = integrity_verify(pdu->msg, pdu->N_bytes, rcvd_count, mac);
if (!is_valid) {
logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rb_name.c_str());
rrc->notify_pdcp_integrity_error(lcid);
return; // Invalid packet, drop.
} else {
logger.debug(pdu->msg, pdu->N_bytes, "%s: Integrity verification successful", rb_name.c_str());
}
}
// After checking the integrity, we can discard the header.
discard_data_header(pdu);
// Check valid rcvd_count
if (rcvd_count < rx_deliv) {
logger.debug("Out-of-order after time-out, duplicate or COUNT wrap-around");
@ -215,7 +267,7 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu)
// TODO if out-of-order configured, submit to upper layer
if (rcvd_count == rx_deliv) {
// Deliver to upper layers in ascending order of associeted COUNT
// Deliver to upper layers in ascending order of associated COUNT
deliver_all_consecutive_counts();
}
@ -228,6 +280,7 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu)
rx_reord = rx_next;
reordering_timer.run();
}
logger.debug("Rx PDCP state - RX_NEXT=%u, RX_DELIV=%u, RX_REORD=%u", rx_next, rx_deliv, rx_reord);
}
// Notification of delivery/failure
@ -245,7 +298,7 @@ void pdcp_entity_nr::notify_failure(const pdcp_sn_vector_t& pdcp_sns)
* Packing / Unpacking Helpers
*/
// Deliver all consecutivly associated COUNTs.
// Deliver all consecutively associated COUNTs.
// Update RX_NEXT after submitting to higher layers
void pdcp_entity_nr::deliver_all_consecutive_counts()
{
@ -277,9 +330,9 @@ void pdcp_entity_nr::deliver_all_consecutive_counts()
// Reordering Timer Callback (t-reordering)
void pdcp_entity_nr::reordering_callback::operator()(uint32_t timer_id)
{
parent->logger.debug("Reordering timer expired");
parent->logger.info("Reordering timer expired. Re-order queue size=%d", parent->reorder_queue.size());
// Deliver all PDCP SDU(s) with associeted COUNT value(s) < RX_REORD
// Deliver all PDCP SDU(s) with associated COUNT value(s) < RX_REORD
for (std::map<uint32_t, unique_byte_buffer_t>::iterator it = parent->reorder_queue.begin();
it != parent->reorder_queue.end() && it->first < parent->rx_reord;
parent->reorder_queue.erase(it++)) {
@ -287,7 +340,7 @@ void pdcp_entity_nr::reordering_callback::operator()(uint32_t timer_id)
parent->pass_to_upper_layers(std::move(it->second));
}
// Deliver all PDCP SDU(s) consecutivly associeted COUNT value(s) starting from RX_REORD
// Deliver all PDCP SDU(s) consecutively associated COUNT value(s) starting from RX_REORD
parent->deliver_all_consecutive_counts();
if (parent->rx_deliv < parent->rx_next) {

View File

@ -179,7 +179,7 @@ uint32_t rlc_am::read_pdu(uint8_t* payload, uint32_t nof_bytes)
uint32_t read_bytes = tx_base->read_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_pdus++;
metrics.num_tx_pdus += read_bytes > 0 ? 1 : 0;
metrics.num_tx_pdu_bytes += read_bytes;
return read_bytes;
}

View File

@ -62,15 +62,11 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
min_hdr_size = 2;
tx_window = std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> >(
new rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, am_window_size(rlc_am_nr_sn_size_t::size12bits)>);
retx_queue = std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> >(
new pdu_retx_queue<rlc_amd_retx_nr_t, am_window_size(rlc_am_nr_sn_size_t::size12bits)>);
break;
case rlc_am_nr_sn_size_t::size18bits:
min_hdr_size = 3;
tx_window = std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> >(
new rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, am_window_size(rlc_am_nr_sn_size_t::size18bits)>);
retx_queue = std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> >(
new pdu_retx_queue<rlc_amd_retx_nr_t, am_window_size(rlc_am_nr_sn_size_t::size18bits)>);
break;
default:
RlcError("attempt to configure unsupported tx_sn_field_length %s", to_string(cfg.tx_sn_field_length));
@ -103,8 +99,8 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
bool rlc_am_nr_tx::has_data()
{
return do_status() || // if we have a status PDU to transmit
tx_sdu_queue.get_n_sdus() != 0 || !retx_queue->empty(); // or if there is a SDU queued up for transmission
return do_status() || // if we have a status PDU to transmit
tx_sdu_queue.get_n_sdus() != 0 || !retx_queue.empty(); // or if there is a SDU queued up for transmission
}
/**
@ -143,8 +139,8 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
}
// Retransmit if required
if (not retx_queue->empty()) {
RlcInfo("Re-transmission required. Retransmission queue size: %d", retx_queue->size());
if (not retx_queue.empty()) {
RlcInfo("Re-transmission required. Retransmission queue size: %d", retx_queue.size());
return build_retx_pdu(payload, nof_bytes);
}
@ -272,10 +268,10 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t*
// Sanity check: can this SDU be sent considering header overhead?
if (nof_bytes <= min_hdr_size) { // Small header as SO is not present
RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, "
"min_hdr_size=%d",
nof_bytes,
min_hdr_size);
RlcInfo("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, "
"min_hdr_size=%d",
nof_bytes,
min_hdr_size);
return 0;
}
@ -345,10 +341,10 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu,
// Sanity check: can this SDU be sent considering header overhead?
if (nof_bytes <= max_hdr_size) { // Larger header size, as SO is present
RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, "
"max_header_size=%d",
nof_bytes,
max_hdr_size);
RlcInfo("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, "
"max_header_size=%d",
nof_bytes,
max_hdr_size);
return 0;
}
@ -443,19 +439,19 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu,
uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
{
// Check there is at least 1 element before calling front()
if (retx_queue->empty()) {
if (retx_queue.empty()) {
RlcError("in build_retx_pdu(): retx_queue is empty");
return 0;
}
rlc_amd_retx_nr_t& retx = retx_queue->front();
rlc_amd_retx_nr_t& retx = retx_queue.front();
// Sanity check - drop any retx SNs not present in tx_window
while (not tx_window->has_sn(retx.sn)) {
RlcInfo("SN=%d not in tx window, probably already ACKed. Skip and remove from retx queue", retx.sn);
retx_queue->pop();
if (!retx_queue->empty()) {
retx = retx_queue->front();
retx_queue.pop();
if (!retx_queue.empty()) {
retx = retx_queue.front();
} else {
RlcInfo("empty retx queue, cannot provide any retx PDU");
return 0;
@ -549,7 +545,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(const rlc_amd_retx_nr_t retx,
// Update RETX queue. This must be done before calculating
// the polling bit, to make sure the poll bit is calculated correctly
retx_queue->pop();
retx_queue.pop();
// Write header to payload
rlc_am_nr_pdu_header_t new_header = tx_pdu.header;
@ -622,7 +618,10 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx,
// Sanity check: are there enough bytes for header plus data?
if (nof_bytes <= expected_hdr_len) {
RlcError("called %s, but there are not enough bytes for data plus header. SN=%d", __FUNCTION__, retx.sn);
RlcInfo("Not enough bytes for RETX payload plus header. SN=%d, nof_bytes=%d, hdr_len=%d",
retx.sn,
nof_bytes,
expected_hdr_len);
return 0;
}
@ -785,13 +784,33 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status);
log_rlc_am_nr_status_pdu_to_string(logger.info, "RX status PDU: %s", &status, parent->rb_name);
/*
* Sanity check the received status report.
* Checking if the ACK_SN is inside the the TX_WINDOW makes sure we discard out of order status reports
* Checking if ACK_SN > Tx_Next makes sure we do not receive a ACK/NACK for something we did not TX
*/
if (not inside_tx_window(status.ack_sn)) {
RlcInfo("Received ACK with SN outside of TX_WINDOW, ignoring status report. ACK_SN=%d, TX_NEXT_ACK=%d.",
status.ack_sn,
st.tx_next_ack);
info_state();
return;
}
if (tx_mod_base_nr(status.ack_sn) > tx_mod_base_nr(st.tx_next)) {
RlcWarning("Received ACK with SN larger than TX_NEXT, ignoring status report. SN=%d, TX_NEXT_ACK=%d, TX_NEXT=%d",
status.ack_sn,
st.tx_next_ack,
st.tx_next);
info_state();
return;
}
/**
* Section 5.3.3.3: Reception of a STATUS report
* - if the STATUS report comprises a positive or negative acknowledgement for the RLC SDU with sequence
* number equal to POLL_SN:
* - if t-PollRetransmit is running:
* - stop and reset t-PollRetransmit.
*
*/
if (tx_mod_base_nr(st.poll_sn) <= tx_mod_base_nr(status.ack_sn)) {
if (poll_retransmit_timer.is_running()) {
@ -810,19 +829,14 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
* - consider the RLC SDU or the RLC SDU segment for which a negative acknowledgement was received for
* retransmission.
*/
// Process ACKs
uint32_t stop_sn = status.nacks.size() == 0
? status.ack_sn
: status.nacks[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists.
if (tx_mod_base_nr(stop_sn) > tx_mod_base_nr(st.tx_next)) {
RlcError("Received ACK or NACK with SN=%d larger than TX_NEXT=%d. Ignoring status report", stop_sn, st.tx_next);
info_state();
return;
}
// Process ACKs
for (uint32_t sn = st.tx_next_ack; tx_mod_base_nr(sn) < tx_mod_base_nr(stop_sn); sn = (sn + 1) % mod_nr) {
if (tx_window->has_sn(sn)) {
notify_info_vec.push_back((*tx_window)[sn].pdcp_sn);
retx_queue.remove_sn(sn); // remove any pending retx for that SN
tx_window->remove_pdu(sn);
st.tx_next_ack = (sn + 1) % mod_nr;
} else {
@ -901,8 +915,8 @@ void rlc_am_nr_tx::handle_nack(const rlc_status_nack_t& nack, std::set<uint32_t>
bool segment_found = false;
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
if (segm.so >= nack.so_start && segm.so <= nack.so_end) {
if (not retx_queue->has_sn(nack.nack_sn, segm.so)) {
rlc_amd_retx_nr_t& retx = retx_queue->push();
if (not retx_queue.has_sn(nack.nack_sn, segm.so)) {
rlc_amd_retx_nr_t& retx = retx_queue.push();
retx.sn = nack.nack_sn;
retx.is_segment = true;
retx.so_start = segm.so;
@ -934,10 +948,10 @@ void rlc_am_nr_tx::handle_nack(const rlc_status_nack_t& nack, std::set<uint32_t>
} else {
// NACK'ing full SDU.
// add to retx queue if it's not already there
if (not retx_queue->has_sn(nack.nack_sn)) {
if (not retx_queue.has_sn(nack.nack_sn)) {
// Have we segmented the SDU already?
if ((*tx_window)[nack.nack_sn].segment_list.empty()) {
rlc_amd_retx_nr_t& retx = retx_queue->push();
rlc_amd_retx_nr_t& retx = retx_queue.push();
retx.sn = nack.nack_sn;
retx.is_segment = false;
retx.so_start = 0;
@ -949,7 +963,7 @@ void rlc_am_nr_tx::handle_nack(const rlc_status_nack_t& nack, std::set<uint32_t>
RlcInfo("Scheduled RETX of SDU SN=%d", nack.nack_sn);
retx_sn_set.insert(nack.nack_sn);
for (auto segm : (*tx_window)[nack.nack_sn].segment_list) {
rlc_amd_retx_nr_t& retx = retx_queue->push();
rlc_amd_retx_nr_t& retx = retx_queue.push();
retx.sn = nack.nack_sn;
retx.is_segment = true;
retx.so_start = segm.so;
@ -1025,9 +1039,7 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri
}
// Bytes needed for retx
size_t n_retx = retx_queue->size();
for (size_t i = 0; i < n_retx; i++) {
rlc_amd_retx_nr_t& retx = (*retx_queue)[i];
for (const rlc_amd_retx_nr_t& retx : retx_queue.get_inner_queue()) {
RlcDebug("buffer state - retx - SN=%d, Segment: %s, %d:%d",
retx.sn,
retx.is_segment ? "true" : "false",
@ -1042,6 +1054,8 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri
n_bytes_prio += (req_bytes + hdr_req_bytes);
RlcDebug("buffer state - retx: %d bytes", n_bytes_prio);
}
} else {
RlcWarning("buffer state - retx for SN=%d is outside the tx_window", retx.sn);
}
}
@ -1128,7 +1142,7 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes
* - if no new RLC SDU can be transmitted after the transmission of the AMD PDU (e.g. due to window stalling);
* - include a poll in the AMD PDU as described below.
*/
if ((tx_sdu_queue.is_empty() && retx_queue->empty() && sdu_under_segmentation_sn == INVALID_RLC_SN) ||
if ((tx_sdu_queue.is_empty() && retx_queue.empty() && sdu_under_segmentation_sn == INVALID_RLC_SN) ||
tx_window->full()) {
RlcDebug("Setting poll bit due to empty buffers/inablity to TX. SN=%d, POLL_SN=%d", sn, st.poll_sn);
poll = 1;
@ -1209,7 +1223,7 @@ void rlc_am_nr_tx::stop()
tx_window->clear();
// Drop all messages in RETX queue
retx_queue->clear();
retx_queue.clear();
tx_enabled = false;
}
@ -1231,7 +1245,7 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id)
* - consider any RLC SDU which has not been positively acknowledged for retransmission.
* - include a poll in an AMD PDU as described in section 5.3.3.2.
*/
if ((tx_sdu_queue.is_empty() && retx_queue->empty()) || tx_window->full()) {
if ((tx_sdu_queue.is_empty() && retx_queue.empty()) || tx_window->full()) {
if (tx_window->empty()) {
RlcError("t-PollRetransmit expired, but the tx_window is empty. POLL_SN=%d, Tx_Next_Ack=%d, tx_window_size=%d",
st.poll_sn,
@ -1250,7 +1264,7 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id)
// RETX first RLC SDU that has not been ACKed
// or first SDU segment of the first RLC SDU
// that has not been acked
rlc_amd_retx_nr_t& retx = retx_queue->push();
rlc_amd_retx_nr_t& retx = retx_queue.push();
retx.sn = st.tx_next_ack;
if ((*tx_window)[st.tx_next_ack].segment_list.empty()) {
// Full SDU
@ -1875,37 +1889,8 @@ void rlc_am_nr_rx::write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu
}
/*
* Window Helpers
* Segment Helpers
*/
uint32_t rlc_am_nr_rx::rx_mod_base_nr(uint32_t sn) const
{
return (sn - st.rx_next) % mod_nr;
}
uint32_t rlc_am_nr_rx::rx_window_size() const
{
return am_window_size(cfg.rx_sn_field_length);
}
bool rlc_am_nr_rx::inside_rx_window(uint32_t sn)
{
// RX_Next <= SN < RX_Next + AM_Window_Size
return rx_mod_base_nr(sn) < rx_window_size();
}
/*
* Metrics
*/
uint32_t rlc_am_nr_rx::get_sdu_rx_latency_ms()
{
return 0;
}
uint32_t rlc_am_nr_rx::get_rx_buffered_bytes()
{
return 0;
}
void rlc_am_nr_rx::insert_received_segment(rlc_amd_rx_pdu_nr segment,
std::set<rlc_amd_rx_pdu_nr, rlc_amd_rx_pdu_nr_cmp>& segment_list) const
{
@ -1942,6 +1927,25 @@ void rlc_am_nr_rx::update_segment_inventory(rlc_amd_rx_sdu_nr_t& rx_sdu) const
rx_sdu.fully_received = false;
}
/*
* Window Helpers
*/
uint32_t rlc_am_nr_rx::rx_mod_base_nr(uint32_t sn) const
{
return (sn - st.rx_next) % mod_nr;
}
uint32_t rlc_am_nr_rx::rx_window_size() const
{
return am_window_size(cfg.rx_sn_field_length);
}
bool rlc_am_nr_rx::inside_rx_window(uint32_t sn)
{
// RX_Next <= SN < RX_Next + AM_Window_Size
return rx_mod_base_nr(sn) < rx_window_size();
}
/*
* Debug Helpers
*/
@ -1959,4 +1963,17 @@ void rlc_am_nr_rx::debug_window() const
RlcDebug(
"RX window state: Rx_Next=%d, Rx_Next_Highest=%d, SDUs %d", st.rx_next, st.rx_next_highest, rx_window->size());
}
/*
* Metrics
*/
uint32_t rlc_am_nr_rx::get_sdu_rx_latency_ms()
{
return 0;
}
uint32_t rlc_am_nr_rx::get_rx_buffered_bytes()
{
return 0;
}
} // namespace srsran

View File

@ -90,7 +90,7 @@ uint32_t rlc_um_nr::rlc_um_nr_tx::get_buffer_state()
std::lock_guard<std::mutex> lock(mutex);
// Bytes needed for tx SDUs
uint32_t n_sdus = tx_sdu_queue.size();
uint32_t n_sdus = tx_sdu_queue.get_n_sdus();
uint32_t n_bytes = tx_sdu_queue.size_bytes();
if (tx_sdu) {
n_sdus++;
@ -110,7 +110,6 @@ uint32_t rlc_um_nr::rlc_um_nr_tx::get_buffer_state()
if (bsr_callback) {
bsr_callback(parent->get_lcid(), n_bytes, 0);
}
return n_bytes;
}
@ -156,7 +155,13 @@ uint32_t rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8
// Select segmentation information and header size
if (tx_sdu == nullptr) {
// Read a new SDU
tx_sdu = tx_sdu_queue.read();
do {
tx_sdu = tx_sdu_queue.read();
} while (tx_sdu == nullptr && tx_sdu_queue.size() != 0);
if (tx_sdu == nullptr) {
RlcDebug("Cannot build any PDU, tx_sdu_queue has no non-null SDU.");
return 0;
}
next_so = 0;
// Check for full SDU case

View File

@ -66,18 +66,18 @@ uint8_t sdu1[] = {0x18, 0xe2};
uint8_t sdu2[] = {0xde, 0xad};
// Test PDUs for rx (generated from SDU1)
uint8_t pdu1_count0_snlen12[] = {0x80, 0x00, 0x8f, 0xe3, 0xe0, 0xdf, 0x82, 0x92};
uint8_t pdu1_count2048_snlen12[] = {0x88, 0x00, 0x8d, 0x2c, 0x47, 0x5e, 0xb1, 0x5b};
uint8_t pdu1_count4096_snlen12[] = {0x80, 0x00, 0x97, 0xbe, 0xa3, 0x32, 0xfa, 0x61};
uint8_t pdu1_count4294967295_snlen12[] = {0x8f, 0xff, 0x1e, 0x47, 0xe6, 0x86, 0x28, 0x6c};
uint8_t pdu1_count0_snlen18[] = {0x80, 0x00, 0x00, 0x8f, 0xe3, 0xe0, 0xdf, 0x82, 0x92};
uint8_t pdu1_count131072_snlen18[] = {0x82, 0x00, 0x00, 0x15, 0x01, 0xf4, 0xb0, 0xfc, 0xc5};
uint8_t pdu1_count262144_snlen18[] = {0x80, 0x00, 0x00, 0xc2, 0x47, 0xa8, 0xdd, 0xc0, 0x73};
uint8_t pdu1_count4294967295_snlen18[] = {0x83, 0xff, 0xff, 0x1e, 0x47, 0xe6, 0x86, 0x28, 0x6c};
uint8_t pdu1_count0_snlen12[] = {0x80, 0x00, 0x8f, 0xe3, 0xc7, 0x1b, 0xad, 0x14};
uint8_t pdu1_count2048_snlen12[] = {0x88, 0x00, 0x8d, 0x2c, 0xe5, 0x38, 0xc0, 0x42};
uint8_t pdu1_count4096_snlen12[] = {0x80, 0x00, 0x97, 0xbe, 0xee, 0x62, 0xf5, 0xe0};
uint8_t pdu1_count4294967295_snlen12[] = {0x8f, 0xff, 0x1e, 0x47, 0xa9, 0x55, 0xa9, 0xd8};
uint8_t pdu1_count0_snlen18[] = {0x80, 0x00, 0x00, 0x8f, 0xe3, 0x37, 0x33, 0xd5, 0x64};
uint8_t pdu1_count131072_snlen18[] = {0x82, 0x00, 0x00, 0x15, 0x01, 0x99, 0x97, 0xe0, 0x4e};
uint8_t pdu1_count262144_snlen18[] = {0x80, 0x00, 0x00, 0xc2, 0x47, 0xc2, 0xee, 0x46, 0xd9};
uint8_t pdu1_count4294967295_snlen18[] = {0x83, 0xff, 0xff, 0x1e, 0x47, 0x78, 0xb8, 0x7a, 0x9f};
// Test PDUs for rx (generated from SDU2)
uint8_t pdu2_count1_snlen12[] = {0x80, 0x01, 0x5e, 0x3d, 0x64, 0xaf, 0xac, 0x7c};
uint8_t pdu2_count1_snlen18[] = {0x80, 0x00, 0x01, 0x5e, 0x3d, 0x64, 0xaf, 0xac, 0x7c};
uint8_t pdu2_count1_snlen12[] = {0x80, 0x01, 0x5e, 0x3d, 0x70, 0x6a, 0xa4, 0x90};
uint8_t pdu2_count1_snlen18[] = {0x80, 0x00, 0x01, 0x5e, 0x3d, 0x93, 0xfe, 0xcc, 0x2e};
// This is the normal initial state. All state variables are set to zero
pdcp_initial_state normal_init_state = {};

View File

@ -58,7 +58,7 @@ int test_rx(std::vector<pdcp_test_event_t> events,
}
// Test if the number of RX packets
TESTASSERT(gw_rx->rx_count == n_sdus_exp);
TESTASSERT_EQ(gw_rx->rx_count, n_sdus_exp);
srsran::unique_byte_buffer_t sdu_act = srsran::make_byte_buffer();
gw_rx->get_last_pdu(sdu_act);
TESTASSERT(compare_two_packets(sdu_exp, sdu_act) == 0);
@ -83,7 +83,9 @@ int test_rx_all(srslog::basic_logger& logger)
* This tests correct handling of HFN in the case of SN wraparound (SN LEN 12)
*/
{
std::vector<uint32_t> test1_counts(2); // Test two packets
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("RX COUNT [4095,4096], 12 bit SN");
std::vector<uint32_t> test1_counts(2); // Test two packets
std::iota(test1_counts.begin(), test1_counts.end(), 4095); // Starting at COUNT 4095
std::vector<pdcp_test_event_t> test1_pdus =
gen_expected_pdus_vector(tst_sdu1, test1_counts, srsran::PDCP_SN_LEN_12, sec_cfg, logger);
@ -97,7 +99,9 @@ int test_rx_all(srslog::basic_logger& logger)
* Packet that wraparound should be dropped, so only one packet should be received at the GW.
*/
{
std::vector<uint32_t> test2_counts(2); // Test two packets
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("RX COUNT [4294967295,0], 12 bit SN");
std::vector<uint32_t> test2_counts(2); // Test two packets
std::iota(test2_counts.begin(), test2_counts.end(), 4294967295); // Starting at COUNT 4294967295
std::vector<pdcp_test_event_t> test2_pdus =
gen_expected_pdus_vector(tst_sdu1, test2_counts, srsran::PDCP_SN_LEN_12, sec_cfg, logger);
@ -111,7 +115,9 @@ int test_rx_all(srslog::basic_logger& logger)
* This tests correct handling of HFN in the case of SN wraparound (SN LEN 18)
*/
{
std::vector<uint32_t> test3_counts(2); // Test two packets
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("RX COUNT [262144,262145], 12 bit SN");
std::vector<uint32_t> test3_counts(2); // Test two packets
std::iota(test3_counts.begin(), test3_counts.end(), 262144); // Starting at COUNT 262144
std::vector<pdcp_test_event_t> test3_pdus =
gen_expected_pdus_vector(tst_sdu1, test3_counts, srsran::PDCP_SN_LEN_18, sec_cfg, logger);
@ -125,7 +131,9 @@ int test_rx_all(srslog::basic_logger& logger)
* This tests correct handling of COUNT in the case of [HFN|SN] wraparound
*/
{
std::vector<uint32_t> test4_counts(2); // Test two packets
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("RX COUNT [4294967295,4294967296], 18 bit SN");
std::vector<uint32_t> test4_counts(2); // Test two packets
std::iota(test4_counts.begin(), test4_counts.end(), 4294967295); // Starting at COUNT 4294967295
std::vector<pdcp_test_event_t> test4_pdus =
gen_expected_pdus_vector(tst_sdu1, test4_counts, srsran::PDCP_SN_LEN_18, sec_cfg, logger);
@ -139,6 +147,8 @@ int test_rx_all(srslog::basic_logger& logger)
* Test reception of two out-of-order packets, starting at COUNT 0.
*/
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("RX out-of-order COUNT [1,0], 12 bit SN");
std::vector<pdcp_test_event_t> test5_pdus;
pdcp_initial_state test5_init_state = {};
@ -163,6 +173,8 @@ int test_rx_all(srslog::basic_logger& logger)
* Test reception of two out-of-order packets, starting at COUNT 0.
*/
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("RX out-of-order COUNT [1,0], 18 bit SN");
std::vector<pdcp_test_event_t> test6_pdus;
pdcp_initial_state test6_init_state = {};
@ -187,6 +199,8 @@ int test_rx_all(srslog::basic_logger& logger)
* Test Reception of one out-of-order packet.
*/
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("RX out-of-order COUNT [1,0] t_reordering expired, 12 bit SN");
std::vector<pdcp_test_event_t> test7_pdus;
pdcp_initial_state test7_init_state = {};
@ -206,6 +220,7 @@ int test_rx_all(srslog::basic_logger& logger)
* Test reception of two duplicate PDUs, with COUNT 0.
*/
{
srsran::test_delimit_logger delimiter("RX duplicate COUNTs [0,0], 12 bit SN");
std::vector<pdcp_test_event_t> test8_pdus;
pdcp_initial_state test8_init_state = {};

View File

@ -74,122 +74,152 @@ int test_tx_all(srslog::basic_logger& logger)
* TX Test 1: PDCP Entity with SN LEN = 12
* TX_NEXT = 0.
* Input: {0x18, 0xE2}
* Output: PDCP Header {0x80, 0x00}, Ciphered Text {0x8f, 0xe3}, MAC-I {0xe0, 0xdf, 0x82, 0x92}
* Output: {0x80, 0x00, 0x8f, 0xe3, 0xc7, 0x1b, 0xad, 0x14}
*/
n_packets = 1;
srsran::unique_byte_buffer_t pdu_exp_count0_len12 = srsran::make_byte_buffer();
pdu_exp_count0_len12->append_bytes(pdu1_count0_snlen12, sizeof(pdu1_count0_snlen12));
TESTASSERT(
test_tx(
n_packets, normal_init_state, srsran::PDCP_SN_LEN_12, n_packets, std::move(pdu_exp_count0_len12), logger) ==
0);
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT 0, 12 bit SN");
n_packets = 1;
srsran::unique_byte_buffer_t pdu_exp_count0_len12 = srsran::make_byte_buffer();
pdu_exp_count0_len12->append_bytes(pdu1_count0_snlen12, sizeof(pdu1_count0_snlen12));
TESTASSERT(
test_tx(
n_packets, normal_init_state, srsran::PDCP_SN_LEN_12, n_packets, std::move(pdu_exp_count0_len12), logger) ==
0);
}
/*
* TX Test 2: PDCP Entity with SN LEN = 12
* TX_NEXT = 2048.
* Input: {0x18, 0xE2}
* Output: PDCP Header {0x88, 0x00}, Ciphered Text {0x8d, 0x2c}, MAC-I {0x47, 0x5e, 0xb1, 0x5b}
* Output: {0x88, 0x00, 0x8d, 0x2c, 0xe5, 0x38, 0xc0, 0x42}
*/
n_packets = 2049;
srsran::unique_byte_buffer_t pdu_exp_count2048_len12 = srsran::make_byte_buffer();
pdu_exp_count2048_len12->append_bytes(pdu1_count2048_snlen12, sizeof(pdu1_count2048_snlen12));
TESTASSERT(test_tx(n_packets,
normal_init_state,
srsran::PDCP_SN_LEN_12,
n_packets,
std::move(pdu_exp_count2048_len12),
logger) == 0);
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT 2048, 12 bit SN");
n_packets = 2049;
srsran::unique_byte_buffer_t pdu_exp_count2048_len12 = srsran::make_byte_buffer();
pdu_exp_count2048_len12->append_bytes(pdu1_count2048_snlen12, sizeof(pdu1_count2048_snlen12));
TESTASSERT(test_tx(n_packets,
normal_init_state,
srsran::PDCP_SN_LEN_12,
n_packets,
std::move(pdu_exp_count2048_len12),
logger) == 0);
}
/*
* TX Test 3: PDCP Entity with SN LEN = 12
* TX_NEXT = 4096.
* Input: {0x18, 0xE2}
* Output: PDCP Header {0x80,0x00}, Ciphered Text {0x97, 0xbe}, MAC-I {0xa3, 0x32, 0xfa, 0x61}
* Output: {0x80, 0x00, 0x97, 0xbe, 0xee, 0x62, 0xf5, 0xe0}
*/
n_packets = 4097;
srsran::unique_byte_buffer_t pdu_exp_count4096_len12 = srsran::make_byte_buffer();
pdu_exp_count4096_len12->append_bytes(pdu1_count4096_snlen12, sizeof(pdu1_count4096_snlen12));
TESTASSERT(test_tx(n_packets,
normal_init_state,
srsran::PDCP_SN_LEN_12,
n_packets,
std::move(pdu_exp_count4096_len12),
logger) == 0);
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT 4096, 12 bit SN");
n_packets = 4097;
srsran::unique_byte_buffer_t pdu_exp_count4096_len12 = srsran::make_byte_buffer();
pdu_exp_count4096_len12->append_bytes(pdu1_count4096_snlen12, sizeof(pdu1_count4096_snlen12));
TESTASSERT(test_tx(n_packets,
normal_init_state,
srsran::PDCP_SN_LEN_12,
n_packets,
std::move(pdu_exp_count4096_len12),
logger) == 0);
}
/*
* TX Test 4: PDCP Entity with SN LEN = 18
* TX_NEXT = 0.
* Input: {0x18, 0xE2}
* Output: PDCP Header {0x80, 0x80, 0x00}, Ciphered Text {0x8f, 0xe3}, MAC-I {0xe0, 0xdf, 0x82, 0x92}
* Output: {0x80, 0x00, 0x00, 0x8f, 0xe3, 0x37, 0x33, 0xd5, 0x64}
*/
n_packets = 1;
srsran::unique_byte_buffer_t pdu_exp_count0_len18 = srsran::make_byte_buffer();
pdu_exp_count0_len18->append_bytes(pdu1_count0_snlen18, sizeof(pdu1_count0_snlen18));
TESTASSERT(
test_tx(
n_packets, normal_init_state, srsran::PDCP_SN_LEN_18, n_packets, std::move(pdu_exp_count0_len18), logger) ==
0);
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT 0, 18 bit SN");
n_packets = 1;
srsran::unique_byte_buffer_t pdu_exp_count0_len18 = srsran::make_byte_buffer();
pdu_exp_count0_len18->append_bytes(pdu1_count0_snlen18, sizeof(pdu1_count0_snlen18));
TESTASSERT(
test_tx(
n_packets, normal_init_state, srsran::PDCP_SN_LEN_18, n_packets, std::move(pdu_exp_count0_len18), logger) ==
0);
}
/*
* TX Test 5: PDCP Entity with SN LEN = 18
* TX_NEXT = 131072.
* Input: {0x18, 0xE2}
* Output: PDCP Header {0x82, 0x00, 0x00}, Ciphered Text {0x15, 0x01}, MAC-I {0xf4, 0xb0, 0xfc, 0xc5}
* Output: {0x82, 0x00, 0x00, 0x15, 0x01, 0x99, 0x97, 0xe0, 0x4e}
*/
n_packets = 131073;
srsran::unique_byte_buffer_t pdu_exp_sn131072_len18 = srsran::make_byte_buffer();
pdu_exp_sn131072_len18->append_bytes(pdu1_count131072_snlen18, sizeof(pdu1_count131072_snlen18));
TESTASSERT(
test_tx(
n_packets, normal_init_state, srsran::PDCP_SN_LEN_18, n_packets, std::move(pdu_exp_sn131072_len18), logger) ==
0);
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT 131072, 18 bit SN");
n_packets = 131073;
srsran::unique_byte_buffer_t pdu_exp_sn131072_len18 = srsran::make_byte_buffer();
pdu_exp_sn131072_len18->append_bytes(pdu1_count131072_snlen18, sizeof(pdu1_count131072_snlen18));
TESTASSERT(test_tx(n_packets,
normal_init_state,
srsran::PDCP_SN_LEN_18,
n_packets,
std::move(pdu_exp_sn131072_len18),
logger) == 0);
}
/*
* TX Test 6: PDCP Entity with SN LEN = 18
* TX_NEXT = 262144.
* Input: {0x18, 0xE2}
* Output: PDCP Header {0x80, 0x00, 0x00}, Ciphered Text {0xc2, 0x47}, MAC-I {0xa8, 0xdd, 0xc0, 0x73}
* Output: {0x80, 0x00, 0x00, 0xc2, 0x47, 0xc2, 0xee, 0x46, 0xd9}
*/
n_packets = 262145;
srsran::unique_byte_buffer_t pdu_exp_count262144_len18 = srsran::make_byte_buffer();
pdu_exp_count262144_len18->append_bytes(pdu1_count262144_snlen18, sizeof(pdu1_count262144_snlen18));
TESTASSERT(test_tx(n_packets,
normal_init_state,
srsran::PDCP_SN_LEN_18,
n_packets,
std::move(pdu_exp_count262144_len18),
logger) == 0);
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT 262144, 18 bit SN");
n_packets = 262145;
srsran::unique_byte_buffer_t pdu_exp_count262144_len18 = srsran::make_byte_buffer();
pdu_exp_count262144_len18->append_bytes(pdu1_count262144_snlen18, sizeof(pdu1_count262144_snlen18));
TESTASSERT(test_tx(n_packets,
normal_init_state,
srsran::PDCP_SN_LEN_18,
n_packets,
std::move(pdu_exp_count262144_len18),
logger) == 0);
}
/*
* TX Test 7: PDCP Entity with SN LEN = 12
* Test TX at COUNT wraparound.
* Should print a warning and drop all packets after wraparound.
* Should print a warning and drop all packets after wrap around.
*/
n_packets = 5;
srsran::unique_byte_buffer_t pdu_exp_count4294967295_len12 = srsran::make_byte_buffer();
pdu_exp_count4294967295_len12->append_bytes(pdu1_count4294967295_snlen12, sizeof(pdu1_count4294967295_snlen12));
TESTASSERT(test_tx(n_packets,
near_wraparound_init_state,
srsran::PDCP_SN_LEN_12,
1,
std::move(pdu_exp_count4294967295_len12),
logger) == 0);
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT wrap arround, 12 bit SN");
n_packets = 5;
srsran::unique_byte_buffer_t pdu_exp_count4294967295_len12 = srsran::make_byte_buffer();
pdu_exp_count4294967295_len12->append_bytes(pdu1_count4294967295_snlen12, sizeof(pdu1_count4294967295_snlen12));
TESTASSERT(test_tx(n_packets,
near_wraparound_init_state,
srsran::PDCP_SN_LEN_12,
1,
std::move(pdu_exp_count4294967295_len12),
logger) == 0);
}
/*
* TX Test 8: PDCP Entity with SN LEN = 18
* Test TX at COUNT wraparound.
* Should print a warning and drop all packets after wraparound.
*/
n_packets = 5;
srsran::unique_byte_buffer_t pdu_exp_count4294967295_len18 = srsran::make_byte_buffer();
pdu_exp_count4294967295_len18->append_bytes(pdu1_count4294967295_snlen18, sizeof(pdu1_count4294967295_snlen18));
TESTASSERT(test_tx(n_packets,
near_wraparound_init_state,
srsran::PDCP_SN_LEN_18,
1,
std::move(pdu_exp_count4294967295_len18),
logger) == 0);
return 0;
{
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
srsran::test_delimit_logger delimiter("TX COUNT wrap arround, 12 bit SN");
n_packets = 5;
srsran::unique_byte_buffer_t pdu_exp_count4294967295_len18 = srsran::make_byte_buffer();
pdu_exp_count4294967295_len18->append_bytes(pdu1_count4294967295_snlen18, sizeof(pdu1_count4294967295_snlen18));
TESTASSERT(test_tx(n_packets,
near_wraparound_init_state,
srsran::PDCP_SN_LEN_18,
1,
std::move(pdu_exp_count4294967295_len18),
logger) == 0);
}
return SRSRAN_SUCCESS;
}
// Setup all tests

View File

@ -901,6 +901,193 @@ int lost_pdus_trimmed_nack_test(rlc_am_nr_sn_size_t sn_size)
return SRSRAN_SUCCESS;
}
/*
* Test if retx queue is cleared of SDUs that are ACK'ed by a late/delayed ACK.
*/
int clean_retx_queue_of_acked_sdus_test(rlc_am_nr_sn_size_t sn_size)
{
rlc_am_tester tester;
timer_handler timers(8);
byte_buffer_t pdu_bufs[NBUFS];
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
test_delimit_logger delimiter("Clean retx_queue of SDUs that are ACK'ed by a late/delayed ACK ({} bit SN)",
to_number(sn_size));
constexpr uint32_t payload_size = 1;
uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
uint32_t data_pdu_size = header_size + payload_size;
uint32_t expect_buffer_state = NBUFS * data_pdu_size;
if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) {
return -1;
}
rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size));
if (not rlc2.configure(rlc2_config)) {
return -1;
}
rlc_am_nr_tx* rlc1_tx = dynamic_cast<rlc_am_nr_tx*>(rlc1.get_tx());
// after configuring entity
TESTASSERT(0 == rlc1.get_buffer_state());
basic_test_tx(&rlc1, pdu_bufs, sn_size);
// Write 5 PDUs into RLC2
for (int i = 0; i < NBUFS; i++) {
if (i != 3) {
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3.
}
}
// Only after t-reassembly has expired, will the status report include NACKs.
TESTASSERT_EQ(3, rlc2.get_buffer_state());
{
// Read status PDU from RLC2
byte_buffer_t status_buf;
int len = rlc2.read_pdu(status_buf.msg, 5);
status_buf.N_bytes = len;
TESTASSERT(0 == rlc2.get_buffer_state());
// Assert status is correct
rlc_am_nr_status_pdu_t status_check(sn_size);
rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check);
TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.)
// Write status PDU to RLC1
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
// Check there is nothing pending in RLC1
TESTASSERT_EQ(0, rlc1.get_buffer_state());
}
// Step timers until reassambly timeout expires
for (int cnt = 0; cnt < 35; cnt++) {
timers.step_all();
}
// t-reassembly has expired. There should be a NACK in the status report.
constexpr uint32_t status_pdu_ack_size = 3;
uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state());
{
// Read status PDU from RLC2
byte_buffer_t status_buf;
uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size);
status_buf.N_bytes = len;
TESTASSERT_EQ(0, rlc2.get_buffer_state());
// Assert status is correct
rlc_am_nr_status_pdu_t status_check(sn_size);
rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check);
TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN.
TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU.
TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3.
// Write status PDU to RLC1
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
// Check there is only one Retx of SN=3
TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state());
}
// now we deliver the late PDU SN=3 to rlc2
rlc2.write_pdu(pdu_bufs[3].msg, pdu_bufs[3].N_bytes);
// Check there is only one Retx of SN=3
TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state());
TESTASSERT_EQ(1, rlc1_tx->get_retx_queue_size());
// Step timers until reassambly timeout expires
for (int cnt = 0; cnt < 35; cnt++) {
timers.step_all();
}
// t-reassembly has expired. There should be an ACK in the status report.
TESTASSERT_EQ(status_pdu_ack_size, rlc2.get_buffer_state());
{
// Read status PDU from RLC2
byte_buffer_t status_buf;
uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size);
status_buf.N_bytes = len;
TESTASSERT_EQ(0, rlc2.get_buffer_state());
// Assert status is correct
rlc_am_nr_status_pdu_t status_check(sn_size);
rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check);
TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN.
TESTASSERT_EQ(0, status_check.nacks.size()); // Nothing else lost
// Write status PDU to RLC1
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
// Check the Retx of SN=3 has been removed
TESTASSERT_EQ(0, rlc1.get_buffer_state());
TESTASSERT_EQ(0, rlc1_tx->get_retx_queue_size());
}
{
// Attempt to read from rlc1 to verify there nothing to read from it
byte_buffer_t retx_buf;
uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size);
retx_buf.N_bytes = len;
TESTASSERT_EQ(0, len);
}
{
// rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit)
int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit;
for (int cnt = 0; cnt < checktime; cnt++) {
timers.step_all();
TESTASSERT_EQ(0, rlc2.get_buffer_state());
}
}
// Check statistics
rlc_bearer_metrics_t metrics1 = rlc1.get_metrics();
rlc_bearer_metrics_t metrics2 = rlc2.get_metrics();
uint32_t total_tx_pdu_bytes1 = (NBUFS)*data_pdu_size; // (NBUFS) * PDU size
uint32_t total_rx_pdu_bytes1 = 3 * status_pdu_ack_size + 1 * status_pdu_nack_size; // 3 status PDU (1 with a NACK)
uint32_t total_tx_pdu_bytes2 =
3 * status_pdu_ack_size + status_pdu_nack_size; // Three status PDU (one with a NACK, two without)
uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 Late) * PDU size
// SDU metrics
TESTASSERT_EQ(5, metrics1.num_tx_sdus);
TESTASSERT_EQ(0, metrics1.num_rx_sdus);
TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes);
TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes);
TESTASSERT_EQ(0, metrics1.num_lost_sdus);
// PDU metrics
TESTASSERT_EQ(5, metrics1.num_tx_pdus); // 5 transmissions, no re-transmission
TESTASSERT_EQ(3, metrics1.num_rx_pdus); // 3 status PDUs
TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS) * PDU size
TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // 3 status PDU (1 with a NACK)
TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs
// SDU metrics
TESTASSERT_EQ(0, metrics2.num_tx_sdus);
TESTASSERT_EQ(5, metrics2.num_rx_sdus);
TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes);
TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes);
TESTASSERT_EQ(0, metrics2.num_lost_sdus);
// PDU metrics
TESTASSERT_EQ(3, metrics2.num_tx_pdus); // 3 status PDUs
TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 transmissions, no re-transmission
TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without)
TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 Late) * PDU size
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
return SRSRAN_SUCCESS;
}
/*
* Test the basic segmentation of a single SDU.
* A single SDU of 3 bytes is segmented into 3 PDUs
@ -2548,7 +2735,7 @@ int rx_nack_range_no_so_test(rlc_am_nr_sn_size_t sn_size)
// Deliver dummy status report with nack range betwen PDU 6 and 10.
rlc_am_nr_status_pdu_t status(sn_size);
status.ack_sn = 5;
rlc_status_nack_t nack = {};
nack.nack_sn = 1;
nack.has_nack_range = true;
@ -2640,6 +2827,7 @@ int rx_nack_range_with_so_test(rlc_am_nr_sn_size_t sn_size)
// Deliver dummy status report with nack range betwen PDU 6 and 10.
rlc_am_nr_status_pdu_t status(sn_size);
status.ack_sn = 5;
rlc_status_nack_t nack = {};
nack.nack_sn = 1;
@ -2658,6 +2846,76 @@ int rx_nack_range_with_so_test(rlc_am_nr_sn_size_t sn_size)
return SRSRAN_SUCCESS;
}
int out_of_order_status(rlc_am_nr_sn_size_t sn_size)
{
rlc_am_tester tester;
timer_handler timers(8);
byte_buffer_t pdu_bufs[NBUFS];
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
test_delimit_logger delimiter("out of order status report ({} bit SN)", to_number(sn_size));
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
rlc_am_nr_tx* tx1 = dynamic_cast<rlc_am_nr_tx*>(rlc1.get_tx());
rlc_am_nr_rx* rx1 = dynamic_cast<rlc_am_nr_rx*>(rlc1.get_rx());
if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) {
return -1;
}
TESTASSERT_EQ(0, rlc1.get_buffer_state());
basic_test_tx(&rlc1, pdu_bufs, sn_size);
// Status 1, ACK SN=2, NACK_SN = 1
rlc_am_nr_status_pdu_t status1(sn_size);
status1.ack_sn = 2;
{
rlc_status_nack_t nack = {};
nack.nack_sn = 1;
status1.push_nack(nack);
}
// Status 2, ACK SN=5, NACK SN = 3
rlc_am_nr_status_pdu_t status2(sn_size);
status2.ack_sn = 5;
{
rlc_status_nack_t nack = {};
nack.nack_sn = 3;
status2.push_nack(nack);
}
// pack into PDU
byte_buffer_t status1_pdu;
rlc_am_nr_write_status_pdu(status1, sn_size, &status1_pdu);
// pack into PDU
byte_buffer_t status2_pdu;
rlc_am_nr_write_status_pdu(status2, sn_size, &status2_pdu);
// Write status 2 to RLC1
rlc1.write_pdu(status2_pdu.msg, status2_pdu.N_bytes);
// Check TX_NEXT_ACK
{
rlc_am_nr_tx_state_t st = tx1->get_tx_state();
TESTASSERT_EQ(3, st.tx_next_ack); // SN=3 was nacked on status report 2
TESTASSERT_EQ(2, tx1->get_tx_window_utilization()); // 2 PDUs still in TX_WINDOW
}
// Write status 1 to RLC1
rlc1.write_pdu(status1_pdu.msg, status1_pdu.N_bytes);
// Check TX_NEXT_ACK
{
rlc_am_nr_tx_state_t st = tx1->get_tx_state();
TESTASSERT_EQ(3, st.tx_next_ack);
TESTASSERT_EQ(2, tx1->get_tx_window_utilization());
}
// Check statistics
rlc_bearer_metrics_t metrics1 = rlc1.get_metrics();
return SRSRAN_SUCCESS;
}
int main()
{
// Setup the log message spy to intercept error and warning log entries from RLC
@ -2691,6 +2949,7 @@ int main()
TESTASSERT(lost_pdu_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(lost_pdu_duplicated_nack_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(lost_pdus_trimmed_nack_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(clean_retx_queue_of_acked_sdus_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(basic_segmentation_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(segment_retx_and_loose_segments_test(sn_size) == SRSRAN_SUCCESS);
@ -2704,6 +2963,7 @@ int main()
TESTASSERT(poll_retx_expiry(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(rx_nack_range_no_so_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(rx_nack_range_with_so_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(out_of_order_status(sn_size) == SRSRAN_SUCCESS);
}
return SRSRAN_SUCCESS;
}

View File

@ -95,48 +95,50 @@ qci_config = (
);
// 5G Section
// srb1_5g_config = {
// rlc_config = {
// ul_am = {
// sn_field_len = 12;
// t_poll_retx = 50;
// poll_pdu = 4;
// poll_byte = 3000;
// max_retx_thres = 4;
// };
// dl_am = {
// sn_field_len = 12;
// t_reassembly = 50;
// t_status_prohibit = 50;
// };
// };
// }
srb1_5g_config = {
rlc_config = {
ul_am = {
sn_field_len = 12;
t_poll_retx = 45;
poll_pdu = -1;
poll_byte = -1;
max_retx_thres = 8;
};
dl_am = {
sn_field_len = 12;
t_reassembly = 35;
t_status_prohibit = 10;
};
};
}
// srb2_5g_config = {
// rlc_config = {
// ul_am = {
// sn_field_len = 12;
// t_poll_retx = 50;
// poll_pdu = 4;
// poll_byte = 3000;
// max_retx_thres = 4;
// };
// dl_am = {
// sn_field_len = 12;
// t_reassembly = 50;
// t_status_prohibit = 50;
// };
// };
// }
srb2_5g_config = {
rlc_config = {
ul_am = {
sn_field_len = 12;
t_poll_retx = 45;
poll_pdu = -1;
poll_byte = -1;
max_retx_thres = 8;
};
dl_am = {
sn_field_len = 12;
t_reassembly = 35;
t_status_prohibit = 10;
};
};
}
five_qi_config = (
{
five_qi = 7;
pdcp_nr_config = {
drb = {
discard_timer = 50;
pdcp_sn_size_ul = 18;
pdcp_sn_size_dl = 18;
discard_timer = 50;
integrity_protection = false;
status_report = false;
};
t_reordering = 50;
};
@ -156,9 +158,11 @@ five_qi_config = (
five_qi = 9;
pdcp_nr_config = {
drb = {
discard_timer = 50;
pdcp_sn_size_ul = 18;
pdcp_sn_size_dl = 18;
discard_timer = 50;
integrity_protection = false;
status_report = false;
};
t_reordering = 50;
};

View File

@ -752,15 +752,6 @@ int field_five_qi::parse(libconfig::Setting& root)
asn1::rrc_nr::pdcp_cfg_s::drb_s_* drb_cfg = &pdcp_cfg->drb;
pdcp_cfg->drb_present = true;
// Discard timer
field_asn1_enum_number<asn1::rrc_nr::pdcp_cfg_s::drb_s_::discard_timer_e_> discard_timer("discard_timer",
&drb_cfg->discard_timer);
if (discard_timer.parse(drb) == -1) {
drb_cfg->discard_timer_present = false;
} else {
drb_cfg->discard_timer_present = true;
}
// PDCP SN size UL
field_asn1_enum_number<asn1::rrc_nr::pdcp_cfg_s::drb_s_::pdcp_sn_size_ul_e_> pdcp_sn_size_ul(
"pdcp_sn_size_ul", &drb_cfg->pdcp_sn_size_ul);
@ -779,6 +770,15 @@ int field_five_qi::parse(libconfig::Setting& root)
drb_cfg->pdcp_sn_size_dl_present = true;
}
// Discard timer
field_asn1_enum_number<asn1::rrc_nr::pdcp_cfg_s::drb_s_::discard_timer_e_> discard_timer("discard_timer",
&drb_cfg->discard_timer);
if (discard_timer.parse(drb) == -1) {
drb_cfg->discard_timer_present = false;
} else {
drb_cfg->discard_timer_present = true;
}
parser::field<bool> status_report_required("status_report_required", &drb_cfg->status_report_required_present);
status_report_required.parse(drb);
@ -2476,6 +2476,12 @@ int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_)
if (not srb2_present) {
rrc_cfg_->srb2_cfg.rlc_cfg.set_default_value();
}
if (!srb1_5g_present || !srb2_5g_present) {
fprintf(stderr, "Optional 5G SRB configuration is not supported yet.\n");
fprintf(stderr, "Please specify 5G SRB1 and SRB2 configuration.\n");
return SRSRAN_ERROR;
}
rrc_nr_cfg_->srb1_cfg.present = srb1_5g_present;
rrc_nr_cfg_->srb2_cfg.present = srb1_5g_present;

View File

@ -1401,7 +1401,7 @@ int rrc_nr::ue::update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radi
parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg);
if (sec_ctx.is_as_sec_cfg_valid()) {
update_as_security(rlc_bearer->lc_ch_id);
update_as_security(rlc_bearer->lc_ch_id, drb.pdcp_cfg.drb.integrity_protection_present, true);
}
}

View File

@ -1030,6 +1030,7 @@ bool rrc_nr::configure_sk_counter(uint16_t sk_counter)
if (usim->generate_nr_context(sk_counter, &sec_cfg) == false) {
return false;
}
security_is_activated = true;
return true;
}
@ -2004,6 +2005,7 @@ bool rrc_nr::apply_drb_release(const uint8_t drb)
bool rrc_nr::apply_srb_add_mod(const srb_to_add_mod_s& srb_cfg)
{
logger.debug("Applying SRB Add/Mod to SRB%d", srb_cfg.srb_id);
if (srb_cfg.pdcp_cfg_present) {
logger.error("Cannot add SRB - only default configuration supported.");
return false;
@ -2017,6 +2019,7 @@ bool rrc_nr::apply_srb_add_mod(const srb_to_add_mod_s& srb_cfg)
bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg)
{
logger.debug("Applying DRB Add/Mod to DRB%d", drb_cfg.drb_id);
if (!drb_cfg.pdcp_cfg_present) {
logger.error("Cannot add DRB - incomplete configuration");
return false;
@ -2080,15 +2083,25 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg)
drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl.to_number());
}
if (not security_is_activated) {
logger.error("Trying to setup DRB%d, but security is not activated", drb_cfg.drb_id);
return false;
}
srsran::pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(drb_cfg.drb_id, true, drb_cfg.pdcp_cfg);
pdcp->add_bearer(lcid, pdcp_cfg);
// Use already configured sec config, if no other sec config present in the RadioBearerConfig
pdcp->config_security(lcid, sec_cfg);
pdcp->enable_encryption(lcid, DIRECTION_TXRX);
if (drb_cfg.pdcp_cfg.drb.integrity_protection_present) {
pdcp->enable_integrity(lcid, DIRECTION_TXRX);
}
return true;
}
bool rrc_nr::apply_security_cfg(const security_cfg_s& security_cfg)
{
// TODO derive correct keys
logger.debug("Applying Security config");
if (security_cfg.key_to_use_present) {
if (security_cfg.key_to_use.value != security_cfg_s::key_to_use_opts::options::secondary) {
logger.warning("Only secondary key supported yet");
@ -2129,6 +2142,7 @@ bool rrc_nr::apply_security_cfg(const security_cfg_s& security_cfg)
// Apply security config for all known NR lcids
for (auto& lcid : lcid_drb) {
logger.debug("Applying PDCP security config. LCID=%d", lcid.first);
pdcp->config_security(lcid.first, sec_cfg);
pdcp->enable_encryption(lcid.first);
}
@ -2158,6 +2172,8 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg)
if (apply_security_cfg(radio_bearer_cfg.security_cfg) == false) {
return false;
}
} else {
logger.debug("No Security Config Present");
}
return true;
}