mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
feat(fronthaul): pcap parsing unit test
Test the new O-RU packet processor against PCAP capture. There are two testcases added: - test_oru_pcap_1 - which tests DL & UL fragmentation using jumbo frames (9600 mtu) - test_oru_pcap_frag - which injects the same pcap but tests the mtu to 1500 to force large amounts of UL UPlane fragments Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org> and assisted-by Gemini
This commit is contained in:
@@ -16,7 +16,8 @@ RUN apt-get update && \
|
||||
libyaml-cpp-dev \
|
||||
libzmq3-dev \
|
||||
dpdk-dev \
|
||||
dpdk
|
||||
dpdk \
|
||||
curl
|
||||
|
||||
RUN rm -Rf /oai-ran
|
||||
WORKDIR /oai-ran
|
||||
|
||||
@@ -15,3 +15,20 @@ target_include_directories(test_oru_packet_processor PRIVATE ${dpdk_INCLUDE_DIRS
|
||||
add_dependencies(tests test_oru_packet_processor)
|
||||
add_test(NAME test_oru_packet_processor COMMAND test_oru_packet_processor ${DPDK_TEST_ARGS} "--file-prefix=test_oru_packet_processor" "--vdev=net_null0" "--vdev=net_null1")
|
||||
set_property(TEST test_oru_packet_processor PROPERTY LABELS fronthaul)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(PCAP REQUIRED libpcap)
|
||||
|
||||
add_executable(test_oru_pcap test_oru_pcap.c)
|
||||
add_dependencies(tests test_oru_pcap)
|
||||
target_link_libraries(test_oru_pcap PRIVATE oru_packet_processor log_headers ${dpdk_LIBRARIES} ${PCAP_LIBRARIES} xran_pkt)
|
||||
target_include_directories(test_oru_pcap PRIVATE ${dpdk_INCLUDE_DIRS} ${PCAP_INCLUDE_DIRS})
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/run_oru_pcap_test.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_test(NAME test_oru_pcap_1 COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://gitlab.eurecom.fr/api/v4/projects/7026/packages/generic/fhi72-pcaps/1.0.0/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 9600 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_1")
|
||||
set_property(TEST test_oru_pcap_1 PROPERTY LABELS fronthaul)
|
||||
|
||||
add_test(NAME test_oru_pcap_frag COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://gitlab.eurecom.fr/api/v4/projects/7026/packages/generic/fhi72-pcaps/1.0.0/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 1500 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_frag")
|
||||
set_property(TEST test_oru_pcap_frag PROPERTY LABELS fronthaul)
|
||||
|
||||
add_dependencies(tests test_oru_pcap)
|
||||
|
||||
30
fronthaul/oru/tests/run_oru_pcap_test.sh
Executable file
30
fronthaul/oru/tests/run_oru_pcap_test.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# This script downloads a PCAP file, unarchives it, and then runs a test executable.
|
||||
#
|
||||
# Arguments:
|
||||
# $1: PCAP_DOWNLOAD_URL - The URL to download the .xz archive from.
|
||||
# $2: TEST_ORU_PCAP_EXECUTABLE - The full path to the test executable (e.g., test_oru_pcap).
|
||||
# $3+: Extra arguments to the executable
|
||||
|
||||
DOWNLOAD_URL="$1"
|
||||
shift
|
||||
TEST_EXECUTABLE="$1"
|
||||
shift
|
||||
# Collect extra arguments
|
||||
EXTRA_ARGS=()
|
||||
while [[ $# -gt 0 ]]; do
|
||||
EXTRA_ARGS+=("$1")
|
||||
shift
|
||||
done
|
||||
|
||||
ARCHIVE_NAME="compressed_pcap.xz"
|
||||
EXTRACTED_FILENAME="uncompressed.pcap"
|
||||
curl -L $DOWNLOAD_URL --output $ARCHIVE_NAME
|
||||
unxz -c $ARCHIVE_NAME > $EXTRACTED_FILENAME
|
||||
|
||||
|
||||
|
||||
echo "Running test ${TEST_EXECUTABLE} with PCAP ${DOWNLOAD_URL} and arguments: ${EXTRA_ARGS[*]}"
|
||||
exec "${TEST_EXECUTABLE}" "${EXTRACTED_FILENAME}" "${EXTRA_ARGS[@]}"
|
||||
279
fronthaul/oru/tests/test_oru_pcap.c
Normal file
279
fronthaul/oru/tests/test_oru_pcap.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <pcap.h>
|
||||
#include <rte_common.h>
|
||||
#include <rte_eal.h>
|
||||
#include <rte_mbuf.h>
|
||||
#include <rte_ether.h>
|
||||
#include <rte_byteorder.h>
|
||||
#include "oru_packet_processor.h"
|
||||
#include "xran_pkt_api.h"
|
||||
#include "log.h"
|
||||
#include "common/config/config_userapi.h"
|
||||
|
||||
#define MAX_ANTENNAS 4
|
||||
|
||||
// OAI Linkage Satisfiers
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assertflag)
|
||||
{
|
||||
fprintf(stderr, "Error at %s:%s:%d - %s\n", file, function, line, s ? s : "None");
|
||||
exit(1);
|
||||
}
|
||||
configmodule_interface_t *uniqCfg = NULL;
|
||||
|
||||
struct rte_mempool *mp = NULL;
|
||||
uint64_t g_total_uplane_sent = 0;
|
||||
|
||||
void *test_alloc_mbuf(void *io_controller)
|
||||
{
|
||||
return rte_pktmbuf_alloc(mp);
|
||||
}
|
||||
|
||||
void test_send_mbuf(void *io_controller, struct rte_mbuf **mbufs, uint32_t num_mbufs)
|
||||
{
|
||||
for (uint32_t i = 0; i < num_mbufs; i++) {
|
||||
g_total_uplane_sent++;
|
||||
rte_pktmbuf_free(mbufs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 11) {
|
||||
printf(
|
||||
"Usage: %s <pcap_file> <initial_symbol> <num_dl_slots> <num_ul_slots> <num_dl_symbols> <num_ul_symbols> "
|
||||
"<tdd_pattern_length_slots> <mtu> <prach_eaxc_offset> -- <eal args>\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int eal_args_start = 1;
|
||||
while (eal_args_start < argc && strncmp("--", argv[eal_args_start], strlen("--")) != 0) {
|
||||
eal_args_start++;
|
||||
}
|
||||
|
||||
if (eal_args_start < argc) {
|
||||
if (rte_eal_init(argc - eal_args_start, argv + eal_args_start) < 0) {
|
||||
fprintf(stderr, "Error: EAL initialization failed\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Default EAL args if none provided
|
||||
char *default_argv[] = {argv[0], "--no-huge", "--iova-mode=va", "--no-pci", "--file-prefix=test_oru_pcap"};
|
||||
if (rte_eal_init(5, default_argv) < 0) {
|
||||
fprintf(stderr, "Error: EAL initialization failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
logInit();
|
||||
|
||||
mp = rte_pktmbuf_pool_create("test_pool", 4096, 0, 0, 10240, rte_socket_id());
|
||||
assert(mp != NULL);
|
||||
|
||||
uint64_t initial_symbol = atoll(argv[2]);
|
||||
int num_dl_slots = atoi(argv[3]);
|
||||
int num_ul_slots = atoi(argv[4]);
|
||||
int num_dl_symbols = atoi(argv[5]);
|
||||
int num_ul_symbols = atoi(argv[6]);
|
||||
int tdd_pattern_length_slots = atoi(argv[7]);
|
||||
size_t mtu = atoi(argv[8]);
|
||||
int prach_eaxc_offset = atoi(argv[9]);
|
||||
|
||||
if (eal_args_start < 10) {
|
||||
printf("Error: Missing '--' separator for EAL arguments\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Starting test with parameters:\n");
|
||||
printf(" pcap: %s\n", argv[1]);
|
||||
printf(" initial_symbol: %lu\n", initial_symbol);
|
||||
printf(" TDD: DL %d slots + %d sym, UL %d slots + %d sym, Pattern Length %d slots\n",
|
||||
num_dl_slots,
|
||||
num_dl_symbols,
|
||||
num_ul_slots,
|
||||
num_ul_symbols,
|
||||
tdd_pattern_length_slots);
|
||||
printf(" MTU: %zu\n", mtu);
|
||||
printf(" PRACH eAxC Offset: %d\n", prach_eaxc_offset);
|
||||
|
||||
void *ctx = init_packet_processor(1,
|
||||
273,
|
||||
0,
|
||||
1500,
|
||||
0,
|
||||
1200,
|
||||
num_dl_slots,
|
||||
num_ul_slots,
|
||||
num_dl_symbols,
|
||||
num_ul_symbols,
|
||||
tdd_pattern_length_slots,
|
||||
test_alloc_mbuf,
|
||||
test_send_mbuf,
|
||||
NULL,
|
||||
mtu,
|
||||
prach_eaxc_offset);
|
||||
assert(ctx != NULL);
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
pcap_t *pcap = pcap_open_offline(argv[1], errbuf);
|
||||
if (!pcap) {
|
||||
fprintf(stderr, "Error: Could not open pcap file '%s': %s\n", argv[1], errbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct pcap_pkthdr *pkthdr;
|
||||
const u_char *packet;
|
||||
int pkt_count = 0;
|
||||
int ecpri_count = 0;
|
||||
|
||||
double first_ts = 0;
|
||||
bool synced = false;
|
||||
|
||||
uint64_t last_tick_sym = initial_symbol;
|
||||
|
||||
static uint32_t *txdataF[MAX_ANTENNAS] = {NULL};
|
||||
for (int i = 0; i < MAX_ANTENNAS; i++)
|
||||
txdataF[i] = malloc(273 * 12 * sizeof(uint32_t));
|
||||
|
||||
while (pcap_next_ex(pcap, &pkthdr, &packet) >= 0) {
|
||||
pkt_count++;
|
||||
double ts = pkthdr->ts.tv_sec + pkthdr->ts.tv_usec / 1000000.0;
|
||||
|
||||
struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mp);
|
||||
if (!mbuf) {
|
||||
fprintf(stderr, "Failed to allocate mbuf at packet %d\n", pkt_count);
|
||||
break;
|
||||
}
|
||||
|
||||
char *dst = rte_pktmbuf_append(mbuf, pkthdr->caplen);
|
||||
memcpy(dst, packet, pkthdr->caplen);
|
||||
|
||||
struct rte_ether_hdr *eth = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);
|
||||
uint16_t eth_type = rte_be_to_cpu_16(eth->ether_type);
|
||||
size_t hdr_len = sizeof(struct rte_ether_hdr);
|
||||
|
||||
if (eth_type == 0x8100) { // VLAN
|
||||
struct rte_vlan_hdr *vlan = (struct rte_vlan_hdr *)(eth + 1);
|
||||
eth_type = rte_be_to_cpu_16(vlan->eth_proto);
|
||||
hdr_len += sizeof(struct rte_vlan_hdr);
|
||||
}
|
||||
|
||||
if (eth_type == 0xAEFE) {
|
||||
ecpri_count++;
|
||||
rte_pktmbuf_adj(mbuf, hdr_len);
|
||||
|
||||
struct xran_ecpri_hdr *ecpri = rte_pktmbuf_mtod(mbuf, struct xran_ecpri_hdr *);
|
||||
|
||||
if (!synced && ecpri->cmnhdr.bits.ecpri_mesg_type == ECPRI_RT_CONTROL_DATA) {
|
||||
struct xran_cp_radioapp_common_header *apphdr = (void *)((uint8_t *)ecpri + sizeof(struct xran_ecpri_hdr));
|
||||
uint32_t fields = rte_be_to_cpu_32(apphdr->field.all_bits);
|
||||
uint8_t frame = (fields >> 16) & 0xFF;
|
||||
uint8_t subframe = (fields >> 12) & 0x0F;
|
||||
uint8_t slot = (fields >> 6) & 0x3F;
|
||||
uint8_t start_sym = (fields) & 0x3F;
|
||||
|
||||
uint64_t target_sym = (uint64_t)frame * 280 + (subframe * 2 + slot) * 14 + start_sym;
|
||||
handle_absolute_symbol_tick(ctx, initial_symbol);
|
||||
last_tick_sym = initial_symbol;
|
||||
first_ts = ts;
|
||||
synced = true;
|
||||
printf("Synced to pcap! First C-plane target sym: %lu, setting initial_symbol: %lu\n", target_sym, initial_symbol);
|
||||
}
|
||||
|
||||
if (synced) {
|
||||
uint64_t elapsed_syms = (uint64_t)((ts - first_ts) / 0.0000357142857);
|
||||
uint64_t current_sym = initial_symbol + elapsed_syms;
|
||||
|
||||
if (current_sym > last_tick_sym) {
|
||||
for (uint64_t s = last_tick_sym + 1; s <= current_sym; s++) {
|
||||
handle_absolute_symbol_tick(ctx, s);
|
||||
|
||||
// For UL verification, we call write_ul_iq for every symbol.
|
||||
// It will internally check TDD and UL C-plane presence.
|
||||
int frame = (s / 280) % 1024;
|
||||
int slot = (s / 14) % 20;
|
||||
int sym = s % 14;
|
||||
write_ul_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
write_prach_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
|
||||
// Drain ready jobs to prevent ring overflow
|
||||
int f, sl, sy;
|
||||
while (get_ready_job_count(ctx) > 0) {
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
|
||||
}
|
||||
}
|
||||
last_tick_sym = current_sym;
|
||||
}
|
||||
|
||||
if (ecpri->cmnhdr.bits.ecpri_mesg_type == ECPRI_IQ_DATA) {
|
||||
handle_uplane_packet(ctx, mbuf);
|
||||
} else if (ecpri->cmnhdr.bits.ecpri_mesg_type == ECPRI_RT_CONTROL_DATA) {
|
||||
handle_cplane_packet(ctx, mbuf);
|
||||
} else {
|
||||
rte_pktmbuf_free(mbuf);
|
||||
}
|
||||
} else {
|
||||
rte_pktmbuf_free(mbuf);
|
||||
}
|
||||
} else {
|
||||
rte_pktmbuf_free(mbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush remaining symbols
|
||||
for (int i = 0; i < 100; i++) {
|
||||
handle_absolute_symbol_tick(ctx, ++last_tick_sym);
|
||||
int frame = (last_tick_sym / 280) % 1024;
|
||||
int slot = (last_tick_sym / 14) % 20;
|
||||
int sym = last_tick_sym % 14;
|
||||
write_ul_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
write_prach_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
|
||||
// Drain ready jobs to prevent ring overflow during flush
|
||||
int f, sl, sy;
|
||||
while (get_ready_job_count(ctx) > 0) {
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\nProcessing complete.\n");
|
||||
printf("Total packets: %d\n", pkt_count);
|
||||
printf("eCPRI packets: %d\n", ecpri_count);
|
||||
|
||||
oru_packet_processor_stats_t stats;
|
||||
get_packet_processor_stats(ctx, &stats);
|
||||
printf("ORU Packet Processor Stats:\n");
|
||||
printf(" Total C-Plane Packets: %lu\n", stats.total_cplane);
|
||||
printf(" Total U-Plane Received: %lu\n", stats.total_uplane_received);
|
||||
printf(" Total U-Plane Sent: %lu\n", stats.total_uplane_sent);
|
||||
printf(" C-Plane Header Errors: %lu\n", stats.cplane_err_hdr);
|
||||
printf(" C-Plane Protocol Version Errors: %lu\n", stats.cplane_err_ver);
|
||||
printf(" C-Plane Timing Early Errors: %lu\n", stats.cplane_err_early);
|
||||
printf(" C-Plane Timing Late Errors: %lu\n", stats.cplane_err_late);
|
||||
printf(" C-Plane Duplicate Packet Errors: %lu\n", stats.cplane_err_dup);
|
||||
printf(" U-Plane Timing Early Errors: %lu\n", stats.uplane_err_early);
|
||||
printf(" U-Plane Timing Late Errors: %lu\n", stats.uplane_err_late);
|
||||
printf(" U-Plane Duplicate Packet Errors: %lu\n", stats.uplane_err_dup);
|
||||
printf(" U-Plane Missing C-Plane Errors: %lu\n", stats.uplane_missing_cplane);
|
||||
printf(" UL C-Plane Missing: %lu\n", stats.ul_cplane_missing);
|
||||
printf(" DL TDD Mismatch: %lu\n", stats.dl_tdd_mismatch);
|
||||
printf(" UL TDD Mismatch: %lu\n", stats.ul_tdd_mismatch);
|
||||
printf(" Out of Mbufs: %lu\n", stats.out_of_mbufs);
|
||||
printf(" Application Too Slow Errors: %lu\n", stats.application_too_slow);
|
||||
|
||||
pcap_close(pcap);
|
||||
cleanup_packet_processor(ctx);
|
||||
if (mp)
|
||||
rte_mempool_free(mp);
|
||||
for (int i = 0; i < MAX_ANTENNAS; i++)
|
||||
free(txdataF[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user