mirror of https://github.com/PentHertz/srsLTE.git
update gtpu test to account for scenario of buffered PDCP SNs, and buffering of direct path until indirect tunnel is closed
This commit is contained in:
parent
a55c4cdca5
commit
e8f6a436af
|
@ -85,10 +85,6 @@ inline bool gtpu_supported_flags_check(gtpu_header_t* header, srslte::log_ref gt
|
||||||
gtpu_log->error("gtpu_header - Unhandled Protocol Type. Flags: 0x%x\n\n", header->flags);
|
gtpu_log->error("gtpu_header - Unhandled Protocol Type. Flags: 0x%x\n\n", header->flags);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (header->flags & GTPU_FLAGS_EXTENDED_HDR) {
|
|
||||||
gtpu_log->error("gtpu_header - Unhandled Header Extensions. Flags: 0x%x\n\n", header->flags);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (header->flags & GTPU_FLAGS_PACKET_NUM) {
|
if (header->flags & GTPU_FLAGS_PACKET_NUM) {
|
||||||
gtpu_log->error("gtpu_header - Unhandled Packet Number. Flags: 0x%x\n\n", header->flags);
|
gtpu_log->error("gtpu_header - Unhandled Packet Number. Flags: 0x%x\n\n", header->flags);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
namespace srslte {
|
namespace srslte {
|
||||||
|
|
||||||
|
const static size_t HEADER_PDCP_PDU_NUMBER_SIZE = 4;
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Header pack/unpack helper functions
|
* Header pack/unpack helper functions
|
||||||
* Ref: 3GPP TS 29.281 v10.1.0 Section 5
|
* Ref: 3GPP TS 29.281 v10.1.0 Section 5
|
||||||
|
@ -43,6 +45,10 @@ bool gtpu_write_header(gtpu_header_t* header, srslte::byte_buffer_t* pdu, srslte
|
||||||
}
|
}
|
||||||
pdu->msg -= GTPU_EXTENDED_HEADER_LEN;
|
pdu->msg -= GTPU_EXTENDED_HEADER_LEN;
|
||||||
pdu->N_bytes += GTPU_EXTENDED_HEADER_LEN;
|
pdu->N_bytes += GTPU_EXTENDED_HEADER_LEN;
|
||||||
|
if (header->next_ext_hdr_type > 0) {
|
||||||
|
pdu->msg -= header->ext_buffer.size();
|
||||||
|
pdu->N_bytes += header->ext_buffer.size();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (pdu->get_headroom() < GTPU_BASE_HEADER_LEN) {
|
if (pdu->get_headroom() < GTPU_BASE_HEADER_LEN) {
|
||||||
gtpu_log->error("gtpu_write_header - No room in PDU for header\n");
|
gtpu_log->error("gtpu_write_header - No room in PDU for header\n");
|
||||||
|
@ -94,6 +100,27 @@ bool gtpu_write_header(gtpu_header_t* header, srslte::byte_buffer_t* pdu, srslte
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool gtpu_read_ext_header(srslte::byte_buffer_t* pdu, uint8_t** ptr, gtpu_header_t* header, srslte::log_ref gtpu_log)
|
||||||
|
{
|
||||||
|
if ((header->flags & GTPU_FLAGS_EXTENDED_HDR) == 0 or header->next_ext_hdr_type == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header->next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) {
|
||||||
|
pdu->msg += HEADER_PDCP_PDU_NUMBER_SIZE;
|
||||||
|
pdu->N_bytes -= HEADER_PDCP_PDU_NUMBER_SIZE;
|
||||||
|
header->ext_buffer.resize(HEADER_PDCP_PDU_NUMBER_SIZE);
|
||||||
|
for (size_t i = 0; i < HEADER_PDCP_PDU_NUMBER_SIZE; ++i) {
|
||||||
|
header->ext_buffer[i] = **ptr;
|
||||||
|
(*ptr)++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gtpu_log->error("gtpu_read_header - Unhandled GTP-U Extension Header Type: 0x%x\n", header->next_ext_hdr_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool gtpu_read_header(srslte::byte_buffer_t* pdu, gtpu_header_t* header, srslte::log_ref gtpu_log)
|
bool gtpu_read_header(srslte::byte_buffer_t* pdu, gtpu_header_t* header, srslte::log_ref gtpu_log)
|
||||||
{
|
{
|
||||||
uint8_t* ptr = pdu->msg;
|
uint8_t* ptr = pdu->msg;
|
||||||
|
@ -105,6 +132,7 @@ bool gtpu_read_header(srslte::byte_buffer_t* pdu, gtpu_header_t* header, srslte:
|
||||||
uint8_to_uint16(ptr, &header->length);
|
uint8_to_uint16(ptr, &header->length);
|
||||||
ptr += 2;
|
ptr += 2;
|
||||||
uint8_to_uint32(ptr, &header->teid);
|
uint8_to_uint32(ptr, &header->teid);
|
||||||
|
ptr += 4;
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
if (!gtpu_supported_flags_check(header, gtpu_log)) {
|
if (!gtpu_supported_flags_check(header, gtpu_log)) {
|
||||||
|
@ -132,12 +160,8 @@ bool gtpu_read_header(srslte::byte_buffer_t* pdu, gtpu_header_t* header, srslte:
|
||||||
header->next_ext_hdr_type = *ptr;
|
header->next_ext_hdr_type = *ptr;
|
||||||
ptr++;
|
ptr++;
|
||||||
|
|
||||||
if ((header->flags & GTPU_FLAGS_EXTENDED_HDR) && (header->next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER)) {
|
if (not gtpu_read_ext_header(pdu, &ptr, header, gtpu_log)) {
|
||||||
header->ext_buffer.resize(4);
|
return false;
|
||||||
for (size_t i = 0; i < 4; ++i) {
|
|
||||||
header->ext_buffer[i] = *ptr;
|
|
||||||
ptr++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pdu->msg += GTPU_BASE_HEADER_LEN;
|
pdu->msg += GTPU_BASE_HEADER_LEN;
|
||||||
|
|
|
@ -330,7 +330,7 @@ void gtpu::handle_gtpu_s1u_rx_packet(srslte::unique_byte_buffer_t pdu, const soc
|
||||||
logger.info(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d, n_bytes=%d", rnti, lcid, pdu->N_bytes);
|
logger.info(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d, n_bytes=%d", rnti, lcid, pdu->N_bytes);
|
||||||
uint32_t pdcp_sn = -1;
|
uint32_t pdcp_sn = -1;
|
||||||
if (header.flags & GTPU_FLAGS_EXTENDED_HDR and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) {
|
if (header.flags & GTPU_FLAGS_EXTENDED_HDR and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) {
|
||||||
pdcp_sn = (header.ext_buffer[1] << 8u) + header.ext_buffer[0];
|
pdcp_sn = (header.ext_buffer[1] << 8u) + header.ext_buffer[2];
|
||||||
}
|
}
|
||||||
pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn);
|
pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "srslte/asn1/s1ap.h"
|
#include "srslte/asn1/s1ap.h"
|
||||||
#include <linux/ip.h>
|
#include <linux/ip.h>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include "srsenb/hdr/stack/upper/gtpu.h"
|
#include "srsenb/hdr/stack/upper/gtpu.h"
|
||||||
#include "srsenb/test/common/dummy_classes.h"
|
#include "srsenb/test/common/dummy_classes.h"
|
||||||
|
@ -22,6 +23,8 @@
|
||||||
|
|
||||||
namespace srsenb {
|
namespace srsenb {
|
||||||
|
|
||||||
|
static const size_t PDU_HEADER_SIZE = 20;
|
||||||
|
|
||||||
class stack_tester : public stack_interface_gtpu_lte
|
class stack_tester : public stack_interface_gtpu_lte
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -40,7 +43,21 @@ public:
|
||||||
last_rnti = rnti;
|
last_rnti = rnti;
|
||||||
last_lcid = lcid;
|
last_lcid = lcid;
|
||||||
}
|
}
|
||||||
|
std::map<uint32_t, srslte::unique_byte_buffer_t> get_buffered_pdus(uint16_t rnti, uint32_t lcid) override
|
||||||
|
{
|
||||||
|
return std::move(buffered_pdus);
|
||||||
|
}
|
||||||
|
void push_buffered_pdu(uint32_t sn, srslte::unique_byte_buffer_t pdu) { buffered_pdus[sn] = std::move(pdu); }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
last_sdu = nullptr;
|
||||||
|
last_pdcp_sn = -1;
|
||||||
|
last_lcid = 0;
|
||||||
|
last_rnti = SRSLTE_INVALID_RNTI;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<uint32_t, srslte::unique_byte_buffer_t> buffered_pdus;
|
||||||
srslte::unique_byte_buffer_t last_sdu;
|
srslte::unique_byte_buffer_t last_sdu;
|
||||||
int last_pdcp_sn = -1;
|
int last_pdcp_sn = -1;
|
||||||
uint16_t last_rnti = SRSLTE_INVALID_RNTI;
|
uint16_t last_rnti = SRSLTE_INVALID_RNTI;
|
||||||
|
@ -77,6 +94,21 @@ srslte::unique_byte_buffer_t encode_gtpu_packet(srslte::span<uint8_t> data,
|
||||||
return pdu;
|
return pdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srslte::unique_byte_buffer_t encode_end_marker(uint32_t teid)
|
||||||
|
{
|
||||||
|
srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
|
||||||
|
|
||||||
|
// header
|
||||||
|
srslte::gtpu_header_t header;
|
||||||
|
header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL;
|
||||||
|
header.message_type = GTPU_MSG_END_MARKER;
|
||||||
|
header.length = 0;
|
||||||
|
header.teid = teid;
|
||||||
|
|
||||||
|
gtpu_write_header(&header, pdu.get(), srslte::log_ref("GTPU"));
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
srslte::unique_byte_buffer_t read_socket(int fd)
|
srslte::unique_byte_buffer_t read_socket(int fd)
|
||||||
{
|
{
|
||||||
srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
|
srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
|
||||||
|
@ -96,6 +128,8 @@ int test_gtpu_direct_tunneling()
|
||||||
srslte::net_utils::set_sockaddr(&tenb_sockaddr, tenb_addr_str, GTPU_PORT);
|
srslte::net_utils::set_sockaddr(&tenb_sockaddr, tenb_addr_str, GTPU_PORT);
|
||||||
uint32_t tenb_addr = ntohl(tenb_sockaddr.sin_addr.s_addr), mme_addr = ntohl(mme_sockaddr.sin_addr.s_addr);
|
uint32_t tenb_addr = ntohl(tenb_sockaddr.sin_addr.s_addr), mme_addr = ntohl(mme_sockaddr.sin_addr.s_addr);
|
||||||
|
|
||||||
|
srslte::unique_byte_buffer_t pdu;
|
||||||
|
|
||||||
// Initiate layers
|
// Initiate layers
|
||||||
srsenb::gtpu senb_gtpu(srslog::fetch_basic_logger("GTPU1")), tenb_gtpu(srslog::fetch_basic_logger("GTPU2"));
|
srsenb::gtpu senb_gtpu(srslog::fetch_basic_logger("GTPU1")), tenb_gtpu(srslog::fetch_basic_logger("GTPU2"));
|
||||||
stack_tester senb_stack, tenb_stack;
|
stack_tester senb_stack, tenb_stack;
|
||||||
|
@ -105,7 +139,19 @@ int test_gtpu_direct_tunneling()
|
||||||
|
|
||||||
// create tunnels MME-SeNB and MME-TeNB
|
// create tunnels MME-SeNB and MME-TeNB
|
||||||
uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, mme_addr, mme_teidout1);
|
uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, mme_addr, mme_teidout1);
|
||||||
uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti, drb1, mme_addr, mme_teidout2);
|
uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, mme_addr, mme_teidout2);
|
||||||
|
|
||||||
|
// Buffer PDUs in SeNB PDCP
|
||||||
|
pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
|
||||||
|
pdu->N_bytes = 10;
|
||||||
|
for (size_t sn = 6; sn < 10; ++sn) {
|
||||||
|
std::vector<uint8_t> data(10, sn);
|
||||||
|
pdu = encode_gtpu_packet(data, senb_teid_in, mme_sockaddr, senb_sockaddr);
|
||||||
|
// remove gtpu header
|
||||||
|
pdu->N_bytes -= 8u;
|
||||||
|
memcpy(pdu->msg, pdu->msg + 8u, pdu->N_bytes);
|
||||||
|
senb_pdcp.push_buffered_pdu(sn, std::move(pdu));
|
||||||
|
}
|
||||||
|
|
||||||
// create direct tunnel SeNB-TeNB
|
// create direct tunnel SeNB-TeNB
|
||||||
gtpu::bearer_props props;
|
gtpu::bearer_props props;
|
||||||
|
@ -117,19 +163,60 @@ int test_gtpu_direct_tunneling()
|
||||||
props.forward_from_teidin = senb_teid_in;
|
props.forward_from_teidin = senb_teid_in;
|
||||||
senb_gtpu.add_bearer(rnti, drb1, tenb_addr, dl_tenb_teid_in, &props);
|
senb_gtpu.add_bearer(rnti, drb1, tenb_addr, dl_tenb_teid_in, &props);
|
||||||
|
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 g(rd());
|
||||||
std::vector<uint8_t> data_vec(10);
|
std::vector<uint8_t> data_vec(10);
|
||||||
std::iota(data_vec.begin(), data_vec.end(), 0);
|
std::iota(data_vec.begin(), data_vec.end(), 0);
|
||||||
|
std::vector<uint8_t> encoded_data;
|
||||||
|
srslte::span<uint8_t> pdu_view{};
|
||||||
|
|
||||||
// TEST: verify that incoming DL data is forwarded through SeNB-TeNB tunnel
|
// TEST: verify that PDCP buffered SNs have been forwarded through SeNB->TeNB tunnel
|
||||||
srslte::unique_byte_buffer_t pdu = encode_gtpu_packet(data_vec, senb_teid_in, mme_sockaddr, senb_sockaddr);
|
for (size_t sn = 6; sn < 10; ++sn) {
|
||||||
std::vector<uint8_t> encoded_data(pdu->msg + 8u, pdu->msg + pdu->N_bytes);
|
|
||||||
senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), mme_sockaddr);
|
|
||||||
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr);
|
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr);
|
||||||
TESTASSERT(tenb_pdcp.last_sdu != nullptr);
|
pdu_view = srslte::make_span(tenb_pdcp.last_sdu);
|
||||||
TESTASSERT(tenb_pdcp.last_sdu->N_bytes == encoded_data.size() and
|
TESTASSERT(std::count(pdu_view.begin() + PDU_HEADER_SIZE, pdu_view.end(), sn) == 10);
|
||||||
memcmp(tenb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0);
|
|
||||||
TESTASSERT(tenb_pdcp.last_rnti == rnti2);
|
TESTASSERT(tenb_pdcp.last_rnti == rnti2);
|
||||||
TESTASSERT(tenb_pdcp.last_lcid == drb1);
|
TESTASSERT(tenb_pdcp.last_lcid == drb1);
|
||||||
|
TESTASSERT(tenb_pdcp.last_pdcp_sn == (int)sn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST: verify that incoming DL data MME->SeNB is forwarded through SeNB->TeNB tunnel
|
||||||
|
std::shuffle(data_vec.begin(), data_vec.end(), g);
|
||||||
|
pdu = encode_gtpu_packet(data_vec, senb_teid_in, mme_sockaddr, senb_sockaddr);
|
||||||
|
encoded_data.assign(pdu->msg + 8u, pdu->msg + pdu->N_bytes);
|
||||||
|
senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), mme_sockaddr);
|
||||||
|
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr);
|
||||||
|
pdu_view = srslte::make_span(tenb_pdcp.last_sdu);
|
||||||
|
TESTASSERT(pdu_view.size() == encoded_data.size() and
|
||||||
|
std::equal(pdu_view.begin(), pdu_view.end(), encoded_data.begin()));
|
||||||
|
TESTASSERT(tenb_pdcp.last_rnti == rnti2 and tenb_pdcp.last_lcid == drb1);
|
||||||
|
|
||||||
|
// TEST: verify that MME->TeNB packets are buffered until SeNB->TeNB tunnel is closed
|
||||||
|
tenb_pdcp.clear();
|
||||||
|
size_t N_pdus = std::uniform_int_distribution<size_t>{1, 30}(g);
|
||||||
|
for (size_t i = 0; i < N_pdus; ++i) {
|
||||||
|
std::fill(data_vec.begin(), data_vec.end(), i);
|
||||||
|
pdu = encode_gtpu_packet(data_vec, senb_teid_in, mme_sockaddr, tenb_sockaddr);
|
||||||
|
tenb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), mme_sockaddr);
|
||||||
|
// The PDUs are being buffered
|
||||||
|
TESTASSERT(tenb_pdcp.last_sdu == nullptr);
|
||||||
|
}
|
||||||
|
// PDUs coming from SeNB-TeNB tunnel are forwarded
|
||||||
|
std::iota(data_vec.begin(), data_vec.end(), 0);
|
||||||
|
std::shuffle(data_vec.begin(), data_vec.end(), g);
|
||||||
|
pdu = encode_gtpu_packet(data_vec, senb_teid_in, mme_sockaddr, senb_sockaddr);
|
||||||
|
encoded_data.assign(pdu->msg + 8u, pdu->msg + pdu->N_bytes);
|
||||||
|
senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), mme_sockaddr);
|
||||||
|
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr);
|
||||||
|
TESTASSERT(tenb_pdcp.last_sdu->N_bytes == encoded_data.size() and
|
||||||
|
memcmp(tenb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0);
|
||||||
|
tenb_pdcp.clear();
|
||||||
|
// EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed
|
||||||
|
pdu = encode_end_marker(senb_teid_in);
|
||||||
|
senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), mme_sockaddr);
|
||||||
|
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr);
|
||||||
|
srslte::span<uint8_t> encoded_data2{tenb_pdcp.last_sdu->msg + 20u, tenb_pdcp.last_sdu->msg + 30u};
|
||||||
|
TESTASSERT(std::all_of(encoded_data2.begin(), encoded_data2.end(), [N_pdus](uint8_t b) { return b == N_pdus - 1; }));
|
||||||
|
|
||||||
return SRSLTE_SUCCESS;
|
return SRSLTE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue