diff --git a/README.md b/README.md index 592ff13f66..17e275f240 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,9 @@

License - Supported OS Ubuntu 20 Supported OS Ubuntu 22 Supported OS Ubuntu 24 - Supported OS RELH9 + Supported OS RHEL9 Supported OS Fedora 41

@@ -16,7 +15,9 @@

- + + +

diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.usrpb200.sc-fdma.conf b/ci-scripts/conf_files/gnb.sa.band78.106prb.usrpb200.sc-fdma.conf index 92b0ad794c..ba06b69dd2 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.106prb.usrpb200.sc-fdma.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.usrpb200.sc-fdma.conf @@ -83,7 +83,7 @@ gNBs = ra_ResponseWindow = 4; #ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR #1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen - ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 2; + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; #one (0..15) 4,8,12,16,...60,64 ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; #ra_ContentionResolutionTimer diff --git a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml index f4ee341c94..5a59880b2f 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml @@ -34,9 +34,24 @@ 020002 030001 030002 + 040002 + 000004 + 020001 + 040002 + 000004 + 020001 + 040002 + 000004 + 020001 + 040002 + 000004 + 020001 + 040002 + 000004 + 020001 040001 000004 - 020002 + 020001 100001 222222 @@ -82,7 +97,7 @@ IdleSleep Sleep - 5 + 1 @@ -92,8 +107,8 @@ cacofonix rfsim5g_ext_dn cacofonix - -c 20 -i0.2 - 5 + -c 20 -i0.5 + 80 @@ -139,13 +154,19 @@ yes + + Custom_Command + Force RRC_IDLE (UE ID 0) + cacofonix + echo ciUE force_RRC_IDLE | nc 192.168.71.150 8091 + yes + + Undeploy_Object true Undeploy all OAI 5G stack ci-scripts/yaml_files/5g_rfsimulator_24prb - 1,0,0,0 - 1,0,0,0 0 0 diff --git a/common/config/yaml/config_yaml.cpp b/common/config/yaml/config_yaml.cpp index 108dee1ac2..138163ce0e 100644 --- a/common/config/yaml/config_yaml.cpp +++ b/common/config/yaml/config_yaml.cpp @@ -25,6 +25,8 @@ extern "C" { #include "common/config/config_userapi.h" void *config_allocate_new(configmodule_interface_t *cfg, int sz, bool autoFree); void config_check_valptr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int elt_sz, int nb_elt); +#include "../config_userapi.h" +#include "../config_common.h" } #include #include @@ -98,6 +100,9 @@ void SetDefault(configmodule_interface_t *cfg, paramdef_t *param) param->uptr = (uint32_t *)param->defintarrayval; } break; + case TYPE_IPV4ADDR: + config_setdefault_ipv4addr(cfg, param, param->defstrval); + break; default: AssertFatal(false, "Unhandled type %d", param->type); } @@ -186,6 +191,16 @@ void SetNonDefault(configmodule_interface_t *cfg, const YAML::Node &node, paramd } break; } + case TYPE_IPV4ADDR: { + std::string ipv4addr = node[optname].as(); + char* ipv4addr_ptr = strdup(ipv4addr.c_str()); + int rst = config_assign_ipv4addr(cfg, param, ipv4addr_ptr); + free(ipv4addr_ptr); + if (rst < 0) { + fprintf(stderr,"[LIBCONFIG] %s not valid for %s \n", ipv4addr.c_str(), optname.c_str()); + } + break; + } default: AssertFatal(false, "Unhandled type %d", param->type); } diff --git a/common/config/yaml/tests/CMakeLists.txt b/common/config/yaml/tests/CMakeLists.txt index 6290f2fc15..d33fa57658 100644 --- a/common/config/yaml/tests/CMakeLists.txt +++ b/common/config/yaml/tests/CMakeLists.txt @@ -10,3 +10,4 @@ configure_file(test_string.yaml test_string.yaml COPYONLY) configure_file(test_list_of_mappings.yml test_list_of_mappings.yml COPYONLY) configure_file(test_int_array.yaml test_int_array.yaml COPYONLY) configure_file(test_read_mapping_as_list.yaml test_read_mapping_as_list.yaml COPYONLY) +configure_file(test_ipv4.yaml test_ipv4.yaml COPYONLY) diff --git a/common/config/yaml/tests/test_ipv4.yaml b/common/config/yaml/tests/test_ipv4.yaml new file mode 100644 index 0000000000..c7d1b4866f --- /dev/null +++ b/common/config/yaml/tests/test_ipv4.yaml @@ -0,0 +1,4 @@ +test: + ipv4_1: 0.0.0.0 + ipv4_2: 192.168.0.1 + #ipv4_3: check default diff --git a/common/config/yaml/tests/test_yaml_config.cpp b/common/config/yaml/tests/test_yaml_config.cpp index deaa52cd1a..384a1f406c 100644 --- a/common/config/yaml/tests/test_yaml_config.cpp +++ b/common/config/yaml/tests/test_yaml_config.cpp @@ -391,6 +391,36 @@ TEST(yaml_config, test_read_mapping_as_list) { end_configmodule(cfg); } +TEST(yaml_config, test_read_ipv4) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_ipv4.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t p = {}; + p.type = TYPE_IPV4ADDR; + strncpy(p.optname, "ipv4_1", sizeof(p.optname) - 1); + uint32_t addr = 0; + p.uptr = &addr; + p.defstrval = nullptr; + char prefix[] = "test"; + config_yaml_get(cfg, &p, 1, prefix); + printf("%x\n", addr); + + strncpy(p.optname, "ipv4_2", sizeof(p.optname) - 1); + config_yaml_get(cfg, &p, 1, prefix); + printf("%x\n", addr); + + p.defstrval = strdup("10.0.0.1"); + strncpy(p.optname, "ipv4_3", sizeof(p.optname) - 1); + config_yaml_get(cfg, &p, 1, prefix); + printf("%x\n", addr); + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); + free(p.defstrval); +} + int main(int argc, char** argv) { logInit(); diff --git a/common/utils/T/DOC/T/wireshark.md b/common/utils/T/DOC/T/wireshark.md index af11535bdc..0119955621 100644 --- a/common/utils/T/DOC/T/wireshark.md +++ b/common/utils/T/DOC/T/wireshark.md @@ -3,7 +3,8 @@ First, read the [basic usage](./basic.md) to compile things. It is possible to use `wireshark` to analyse MAC PDUs for UEs, -MIBs, SIBs, and random accesses seen by the eNodeB. +MIBs, SIBs, and random accesses seen by the eNodeB. It also +works for the gNB, as well as for the nrUE. ## Live usage @@ -20,6 +21,14 @@ Then run: ./macpdu2wireshark -d ../T_messages.txt -live ``` +Note: in some cases a MAC PDU may be bigger than the size of an UDP payload +(slightly less than 64 KiB). This is particularily the case for large +bandwidths in NR and MIMO. In this case macpdu2wireshark, sending UDP packets +in its default mode of operations, will fail. By using the option `-to-file` +a pcap file may be directly generated. You need to configure wireshark to +process the generated files (run `macpdu2wireshark -h` to see how to +configure wireshark for this case). + For other options of `macpdu2wireshark`, run: ```shell diff --git a/common/utils/T/tracer/macpdu2wireshark.c b/common/utils/T/tracer/macpdu2wireshark.c index 607023db6a..6d0a564ab8 100644 --- a/common/utils/T/tracer/macpdu2wireshark.c +++ b/common/utils/T/tracer/macpdu2wireshark.c @@ -115,6 +115,11 @@ typedef struct { int max_sib; int live; int no_bind; + /* config to dump to file instead of UDP */ + /* output_filename != NULL means to output to file */ + char *output_filename; + FILE *output_file; + /* runtime vars */ int cur_mib; int cur_sib; @@ -129,19 +134,96 @@ typedef struct { int nr_next_ue_id; } ev_data; +/****************************************************************************/ +/* direct output to pcap file */ +/****************************************************************************/ + +void write_pcap_header(const char *filename, FILE *f) +{ + /* see 'man pcap-savefile' for format */ + uint32_t magic = 0xa1b23c4d; /* magic for second/nanosecond */ + uint16_t major = 2; + uint16_t minor = 4; + uint32_t reserved = 0; + uint32_t snapshot_length = 0x7fffffff; /* let's be generous */ + uint32_t link_layer_type = 252; /* DLT_WIRESHARK_UPPER_PDU */ + if (fwrite(&magic, 4, 1, f) != 1 + || fwrite(&major, 2, 1, f) != 1 + || fwrite(&minor, 2, 1, f) != 1 + || fwrite(&reserved, 4, 1, f) != 1 + || fwrite(&reserved, 4, 1, f) != 1 + || fwrite(&snapshot_length, 4, 1, f) != 1 + || fwrite(&link_layer_type, 4, 1, f) != 1) { + printf("fatal: error writing header to %s\n", filename); + exit(1); + } +} + +const char *pcap_trace_header(int is_lte) +{ + static char header_nr[] = { + 0x00, 12, /* type = EXP_PDU_TAG_DISSECTOR_NAME */ + 0x00, 16, /* length */ + 'm', 'a', 'c', '-', 'n', 'r', '-', 'f', 'r', 'a', 'm', 'e', 'd', 0, 0, 0, + 0x00, 0x00, 0x00, 0x00 /* EXP_PDU_TAG_END_OF_OPT with length 0 */ + }; + static char header_lte[] = { + 0x00, 12, /* type = EXP_PDU_TAG_DISSECTOR_NAME */ + 0x00, 16, /* length */ + 'm', 'a', 'c', '-', 'l', 't', 'e', '-', 'f', 'r', 'a', 'm', 'e', 'd', 0, 0, + 0x00, 0x00, 0x00, 0x00 /* EXP_PDU_TAG_END_OF_OPT with length 0 */ + }; + return is_lte ? header_lte : header_nr; +} + +void write_pcap_trace(const char *filename, FILE *f, + struct timespec sending_time, + const char *buf, int size, int is_lte) +{ + uint32_t second = sending_time.tv_sec; + uint32_t nanosecond = sending_time.tv_nsec; + uint32_t length = size + 24; + /* untruncated_length == length, we dump everything */ + uint32_t untruncated_length = length; + if (fwrite(&second, 4, 1, f) != 1 + || fwrite(&nanosecond, 4, 1, f) != 1 + || fwrite(&length, 4, 1, f) != 1 + || fwrite(&untruncated_length, 4, 1, f) != 1 + || fwrite(pcap_trace_header(is_lte), 24, 1, f) != 1 + || fwrite(buf, size, 1, f) != 1) { + printf("fatal: error writing to file %s\n", filename); + exit(1); + } +} +void write_pcap_trace_lte(const char *filename, FILE *f, + struct timespec sending_time, + const char *buf, int size) +{ + write_pcap_trace(filename, f, sending_time, buf, size, 1); +} + +void write_pcap_trace_nr(const char *filename, FILE *f, + struct timespec sending_time, + const char *buf, int size) +{ + write_pcap_trace(filename, f, sending_time, buf, size, 0); +} + /****************************************************************************/ /* LTE */ /****************************************************************************/ -void trace_lte(ev_data *d, int direction, int rnti_type, int rnti, - int frame, int subframe, void *buf, int bufsize, int preamble, - int sr_rnti) +void trace_lte(struct timespec sending_time, ev_data *d, int direction, + int rnti_type, int rnti, int frame, int subframe, void *buf, + int bufsize, int preamble, int sr_rnti) { ssize_t ret; int fsf; int i; d->buf.osize = 0; - PUTS(&d->buf, MAC_LTE_START_STRING); + + if (!d->output_filename) + PUTS(&d->buf, MAC_LTE_START_STRING); PUTC(&d->buf, FDD_RADIO); PUTC(&d->buf, direction); PUTC(&d->buf, rnti_type); @@ -202,16 +284,21 @@ void trace_lte(ev_data *d, int direction, int rnti_type, int rnti, for (i = 0; i < bufsize; i++) PUTC(&d->buf, ((char *)buf)[i]); - ret = sendto(d->socket, d->buf.obuf, d->buf.osize, 0, - (struct sockaddr *)&d->to, sizeof(struct sockaddr_in)); + if (d->output_filename) { + write_pcap_trace_lte(d->output_filename, d->output_file, sending_time, + d->buf.obuf, d->buf.osize); + } else { + ret = sendto(d->socket, d->buf.obuf, d->buf.osize, 0, + (struct sockaddr *)&d->to, sizeof(struct sockaddr_in)); - if (ret != d->buf.osize) abort(); + if (ret != d->buf.osize) abort(); + } } void ul(void *_d, event e) { ev_data *d = _d; - trace_lte(d, DIRECTION_UPLINK, C_RNTI, e.e[d->ul_rnti].i, + trace_lte(e.sending_time, d, DIRECTION_UPLINK, C_RNTI, e.e[d->ul_rnti].i, e.e[d->ul_frame].i, e.e[d->ul_subframe].i, e.e[d->ul_data].b, e.e[d->ul_data].bsize, NO_PREAMBLE, NO_SR_RNTI); @@ -229,7 +316,7 @@ void dl(void *_d, event e) d->cur_sib++; } - trace_lte(d, DIRECTION_DOWNLINK, + trace_lte(e.sending_time, d, DIRECTION_DOWNLINK, e.e[d->dl_rnti].i != 0xffff ? C_RNTI : SI_RNTI, e.e[d->dl_rnti].i, e.e[d->dl_frame].i, e.e[d->dl_subframe].i, e.e[d->dl_data].b, e.e[d->dl_data].bsize, @@ -245,7 +332,7 @@ void mib(void *_d, event e) if (d->max_mib && d->cur_mib == d->max_mib) return; d->cur_mib++; - trace_lte(d, DIRECTION_DOWNLINK, NO_RNTI, 0, + trace_lte(e.sending_time, d, DIRECTION_DOWNLINK, NO_RNTI, 0, e.e[d->mib_frame].i, e.e[d->mib_subframe].i, e.e[d->mib_data].b, e.e[d->mib_data].bsize, NO_PREAMBLE, NO_SR_RNTI); @@ -254,7 +341,7 @@ void mib(void *_d, event e) void preamble(void *_d, event e) { ev_data *d = _d; - trace_lte(d, DIRECTION_UPLINK, NO_RNTI, 0, + trace_lte(e.sending_time, d, DIRECTION_UPLINK, NO_RNTI, 0, e.e[d->preamble_frame].i, e.e[d->preamble_subframe].i, NULL, 0, e.e[d->preamble_preamble].i, NO_SR_RNTI); @@ -263,7 +350,7 @@ void preamble(void *_d, event e) void ue_preamble(void *_d, event e) { ev_data *d = _d; - trace_lte(d, DIRECTION_UPLINK, NO_RNTI, 0, + trace_lte(e.sending_time, d, DIRECTION_UPLINK, NO_RNTI, 0, e.e[d->ue_preamble_frame].i, e.e[d->ue_preamble_subframe].i, NULL, 0, e.e[d->ue_preamble_preamble].i, NO_SR_RNTI); @@ -272,7 +359,8 @@ void ue_preamble(void *_d, event e) void rar(void *_d, event e) { ev_data *d = _d; - trace_lte(d, DIRECTION_DOWNLINK, RA_RNTI, e.e[d->rar_rnti].i, + trace_lte(e.sending_time, d, DIRECTION_DOWNLINK, + RA_RNTI, e.e[d->rar_rnti].i, e.e[d->rar_frame].i, e.e[d->rar_subframe].i, e.e[d->rar_data].b, e.e[d->rar_data].bsize, NO_PREAMBLE, NO_SR_RNTI); @@ -281,7 +369,7 @@ void rar(void *_d, event e) void sr(void *_d, event e) { ev_data *d = _d; - trace_lte(d, DIRECTION_UPLINK, NO_RNTI, 0, + trace_lte(e.sending_time, d, DIRECTION_UPLINK, NO_RNTI, 0, e.e[d->sr_frame].i, e.e[d->sr_subframe].i, NULL, 0, NO_PREAMBLE, e.e[d->sr_rnti].i); @@ -310,14 +398,15 @@ void sr(void *_d, event e) #define NR_C_RNTI 3 #define NR_SI_RNTI 4 -void trace_nr(ev_data *d, int direction, int rnti_type, int rnti, - int frame, int slot, int harq_pid, void *buf, int bufsize, - int preamble) +void trace_nr(struct timespec sending_time, ev_data *d, int direction, + int rnti_type, int rnti, int frame, int slot, int harq_pid, void *buf, + int bufsize, int preamble) { ssize_t ret; int i; d->buf.osize = 0; - PUTS(&d->buf, MAC_NR_START_STRING); + if (!d->output_filename) + PUTS(&d->buf, MAC_NR_START_STRING); PUTC(&d->buf, NR_TDD_RADIO); PUTC(&d->buf, direction); PUTC(&d->buf, rnti_type); @@ -361,17 +450,23 @@ void trace_nr(ev_data *d, int direction, int rnti_type, int rnti, for (i = 0; i < bufsize; i++) PUTC(&d->buf, ((char *)buf)[i]); - ret = sendto(d->socket, d->buf.obuf, d->buf.osize, 0, - (struct sockaddr *)&d->to, sizeof(struct sockaddr_in)); + if (d->output_filename) { + write_pcap_trace_nr(d->output_filename, d->output_file, sending_time, + d->buf.obuf, d->buf.osize); + } else { + ret = sendto(d->socket, d->buf.obuf, d->buf.osize, 0, + (struct sockaddr *)&d->to, sizeof(struct sockaddr_in)); - if (ret != d->buf.osize) abort(); + if (ret != d->buf.osize) abort(); + } } void nr_ul(void *_d, event e) { ev_data *d = _d; - trace_nr(d, NR_DIRECTION_UPLINK, NR_C_RNTI, e.e[d->nr_ul_rnti].i, + trace_nr(e.sending_time, d, NR_DIRECTION_UPLINK, + NR_C_RNTI, e.e[d->nr_ul_rnti].i, e.e[d->nr_ul_frame].i, e.e[d->nr_ul_slot].i, e.e[d->nr_ul_harq_pid].i, e.e[d->nr_ul_data].b, e.e[d->nr_ul_data].bsize, NO_PREAMBLE); @@ -389,7 +484,7 @@ void nr_dl(void *_d, event e) d->cur_sib++; } - trace_nr(d, NR_DIRECTION_DOWNLINK, + trace_nr(e.sending_time, d, NR_DIRECTION_DOWNLINK, e.e[d->nr_dl_rnti].i != 0xffff ? NR_C_RNTI : NR_SI_RNTI, e.e[d->nr_dl_rnti].i, e.e[d->nr_dl_frame].i, e.e[d->nr_dl_slot].i, e.e[d->nr_dl_harq_pid].i, e.e[d->nr_dl_data].b, @@ -400,7 +495,8 @@ void nr_dl_retx(void *_d, event e) { ev_data *d = _d; - trace_nr(d, NR_DIRECTION_DOWNLINK, NR_C_RNTI, e.e[d->nr_dl_retx_rnti].i, + trace_nr(e.sending_time, d, NR_DIRECTION_DOWNLINK, + NR_C_RNTI, e.e[d->nr_dl_retx_rnti].i, e.e[d->nr_dl_retx_frame].i, e.e[d->nr_dl_retx_slot].i, e.e[d->nr_dl_retx_harq_pid].i, e.e[d->nr_dl_retx_data].b, e.e[d->nr_dl_retx_data].bsize, NO_PREAMBLE); @@ -416,7 +512,7 @@ void nr_mib(void *_d, event e) d->cur_mib++; - trace_nr(d, NR_DIRECTION_DOWNLINK, NR_NO_RNTI, 0, + trace_nr(e.sending_time, d, NR_DIRECTION_DOWNLINK, NR_NO_RNTI, 0, e.e[d->nr_mib_frame].i, e.e[d->nr_mib_slot].i, 0 /* harq pid */, e.e[d->nr_mib_data].b, e.e[d->nr_mib_data].bsize, NO_PREAMBLE); } @@ -431,7 +527,7 @@ void nr_ue_mib(void *_d, event e) d->cur_mib++; - trace_nr(d, NR_DIRECTION_DOWNLINK, NR_NO_RNTI, 0, + trace_nr(e.sending_time, d, NR_DIRECTION_DOWNLINK, NR_NO_RNTI, 0, e.e[d->nr_ue_mib_frame].i, e.e[d->nr_ue_mib_slot].i, 0 /* harq pid */, e.e[d->nr_ue_mib_data].b, e.e[d->nr_ue_mib_data].bsize, NO_PREAMBLE); } @@ -440,7 +536,8 @@ void nr_rar(void *_d, event e) { ev_data *d = _d; - trace_nr(d, NR_DIRECTION_DOWNLINK, NR_RA_RNTI, e.e[d->nr_rar_rnti].i, + trace_nr(e.sending_time, d, NR_DIRECTION_DOWNLINK, + NR_RA_RNTI, e.e[d->nr_rar_rnti].i, e.e[d->nr_rar_frame].i, e.e[d->nr_rar_slot].i, 0 /* harq pid */, e.e[d->nr_rar_data].b, e.e[d->nr_rar_data].bsize, NO_PREAMBLE); } @@ -449,7 +546,8 @@ void nr_ue_ul(void *_d, event e) { ev_data *d = _d; - trace_nr(d, NR_DIRECTION_UPLINK, NR_C_RNTI, e.e[d->nr_ue_ul_rnti].i, + trace_nr(e.sending_time, d, NR_DIRECTION_UPLINK, + NR_C_RNTI, e.e[d->nr_ue_ul_rnti].i, e.e[d->nr_ue_ul_frame].i, e.e[d->nr_ue_ul_slot].i, e.e[d->nr_ue_ul_harq_pid].i, e.e[d->nr_ue_ul_data].b, e.e[d->nr_ue_ul_data].bsize, NO_PREAMBLE); @@ -467,7 +565,7 @@ void nr_ue_dl(void *_d, event e) d->cur_sib++; } - trace_nr(d, NR_DIRECTION_DOWNLINK, + trace_nr(e.sending_time, d, NR_DIRECTION_DOWNLINK, e.e[d->nr_ue_dl_rnti].i != 0xffff ? NR_C_RNTI : NR_SI_RNTI, e.e[d->nr_ue_dl_rnti].i, e.e[d->nr_ue_dl_frame].i, e.e[d->nr_ue_dl_slot].i, e.e[d->nr_ue_dl_harq_pid].i, @@ -477,7 +575,8 @@ void nr_ue_dl(void *_d, event e) void nr_ue_rar(void *_d, event e) { ev_data *d = _d; - trace_nr(d, DIRECTION_DOWNLINK, RA_RNTI, e.e[d->nr_ue_rar_rnti].i, + trace_nr(e.sending_time, d, DIRECTION_DOWNLINK, + RA_RNTI, e.e[d->nr_ue_rar_rnti].i, e.e[d->nr_ue_rar_frame].i, e.e[d->nr_ue_rar_slot].i, 0, e.e[d->nr_ue_rar_data].b, e.e[d->nr_ue_rar_data].bsize, NO_PREAMBLE); @@ -833,7 +932,15 @@ void usage(void) " -live-port tracee's port (default %d)\n" " -no-bind don't bind to IP address (for remote logging)\n" "-i and -live are mutually exclusive options. One of them must be provided\n" - "but not both.\n", + "but not both.\n" + "\n" + "Use the following options to dump to a file instead of sending UDP packets:\n" + " -to-file dump to file using the mac framed format\n" + "\n" + "To use wireshark with files created using the option '-to-file',\n" + "you need to configure wireshark:\n" + " in Analyze->Enabled Protocols search for MAC-NR-FRAMED and\n" + " MAC-LTE-FRAMED and enable them.\n", DEFAULT_IP, DEFAULT_PORT, DEFAULT_LIVE_IP, @@ -881,11 +988,13 @@ int main(int n, char **v) if (!strcmp(v[i], "-live-ip")) { if(i>n-2)usage(); live_ip = v[++i]; continue; } if (!strcmp(v[i], "-live-port")) { if(i>n-2)usage(); live_port = atoi(v[++i]); continue; } if (!strcmp(v[i], "-no-bind")) { d.no_bind = 1; continue; } + if (!strcmp(v[i], "-to-file")) { if(i>n-2)usage(); d.output_filename = v[++i]; continue; } usage(); } if (database_filename == NULL) { printf("ERROR: provide a database file (-d)\n"); + printf("use -h for help on usage\n"); exit(1); } @@ -992,17 +1101,27 @@ int main(int n, char **v) register_handler_function(h, nr_ue_dl_id, nr_ue_dl, &d); register_handler_function(h, nr_ue_rar_id, nr_ue_rar, &d); - d.socket = socket(AF_INET, SOCK_DGRAM, 0); + if (d.output_filename) { + d.output_file = fopen(d.output_filename, "w"); + if (d.output_file == NULL) { + perror(d.output_filename); + exit(1); + } + write_pcap_header(d.output_filename, d.output_file); + } else { + d.socket = socket(AF_INET, SOCK_DGRAM, 0); - if (d.socket == -1) { - perror("socket"); - exit(1); + if (d.socket == -1) { + perror("socket"); + exit(1); + } + + d.to.sin_family = AF_INET; + d.to.sin_port = htons(port); + d.to.sin_addr.s_addr = inet_addr(ip); + new_thread(receiver, &d); } - d.to.sin_family = AF_INET; - d.to.sin_port = htons(port); - d.to.sin_addr.s_addr = inet_addr(ip); - new_thread(receiver, &d); OBUF ebuf = {.osize = 0, .omaxsize = 0, .obuf = NULL}; /* read messages */ @@ -1025,5 +1144,10 @@ int main(int n, char **v) handle_event(h, e); } + if (d.output_filename) { + if (fclose(d.output_file)) + perror(d.output_filename); + } + return 0; } diff --git a/common/utils/telnetsrv/telnetsrv_ciUE.c b/common/utils/telnetsrv/telnetsrv_ciUE.c index 721cb0e5cd..90e044ed86 100644 --- a/common/utils/telnetsrv/telnetsrv_ciUE.c +++ b/common/utils/telnetsrv/telnetsrv_ciUE.c @@ -102,12 +102,21 @@ int force_crnti_ra(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +static int force_deregistration(char *buf, int debug, telnet_printfunc_t prnt) +{ + MessageDef *msg = itti_alloc_new_message(TASK_NAS_NRUE, 0, NAS_DEREGISTRATION_REQ); + NAS_DEREGISTRATION_REQ(msg).cause = AS_DETACH; + itti_send_msg_to_task(TASK_NAS_NRUE, 0, msg); + return 0; +} + /* Telnet shell command definitions */ static telnetshell_cmddef_t cicmds[] = { {"sync_state", "[UE_ID(int,opt)]", get_sync_state}, {"force_rlf", "", force_rlf}, {"force_RRC_IDLE", "", force_RRC_IDLE}, {"force_crnti_ra", "", force_crnti_ra}, + {"deregistration", "", force_deregistration}, {"", "", NULL}, }; diff --git a/common/utils/tun_if.c b/common/utils/tun_if.c index 87bba5a513..4bb1f7ec58 100644 --- a/common/utils/tun_if.c +++ b/common/utils/tun_if.c @@ -168,10 +168,14 @@ static bool change_interface_state(int sock_fd, const char *ifn, if_action_t if_ struct ifreq ifr = {0}; strncpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name)); + /* get flags of this interface: see netdevice(7) */ bool success = ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t)&ifr) == 0; - if (!success) - goto fail_interface_state; + if (!success && if_action == INTERFACE_DOWN && errno == ENODEV) { + LOG_W(OIP, "trying to remove non-existant device %s\n", ifn); + return true; + } + LOG_D(OIP, "Got flags for %s: 0x%x (action: %s)\n", ifn, ifr.ifr_flags, action); if (if_action == INTERFACE_UP) { ifr.ifr_flags |= IFF_UP | IFF_NOARP | IFF_POINTOPOINT; @@ -183,6 +187,8 @@ static bool change_interface_state(int sock_fd, const char *ifn, if_action_t if_ success = ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t)&ifr) == 0; if (!success) goto fail_interface_state; + LOG_D(OIP, "Interface %s successfully set %s\n", ifn, action); + return true; fail_interface_state: @@ -201,10 +207,6 @@ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6) return false; } - if (!change_interface_state(sock_fd, ifname, INTERFACE_DOWN)) { - close(sock_fd); - return false; - } bool success = true; if (ipv4 != NULL) success = setInterfaceParameter(sock_fd, ifname, AF_INET, ipv4, SIOCSIFADDR); @@ -272,8 +274,17 @@ int tun_generate_ue_ifname(char *ifname, int instance_id, int pdu_session_id) return snprintf(ifname, IFNAMSIZ, "%s%d%s", "oaitun_ue", instance_id + 1, pdu_session_id == -1 ? "" : pdu_session_string); } -void tun_destroy(const char *dev, int fd) { - if (change_interface_state(fd, dev, INTERFACE_DOWN) == 0) { +void tun_destroy(const char *dev) +{ + // Use a new socket for ioctl operations + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + LOG_E(UTIL, "Failed to create socket for interface teardown: %d, %s\n", errno, strerror(errno)); + return; + } + + bool success = change_interface_state(fd, dev, INTERFACE_DOWN); + if (success) { LOG_I(UTIL, "Interface %s is now down.\n", dev); } else { LOG_E(UTIL, "Could not bring interface %s down.\n", dev); diff --git a/common/utils/tun_if.h b/common/utils/tun_if.h index e73b4be94e..8e1463624f 100644 --- a/common/utils/tun_if.h +++ b/common/utils/tun_if.h @@ -84,8 +84,7 @@ int tun_alloc(const char *dev); /*! * \brief This function destroys the TUN interface * \param[in] dev name of the interface - * \param[in] fd file descriptor of the allocated interface */ -void tun_destroy(const char *dev, int fd); +void tun_destroy(const char *dev); #endif /*TUN_IF_H_*/ diff --git a/doc/5Gnas.md b/doc/5Gnas.md index db0c19f07e..a120ed2173 100644 --- a/doc/5Gnas.md +++ b/doc/5Gnas.md @@ -1,106 +1,109 @@ - +# About 5GS NAS +The 5G System Non-Access Stratum (5GS NAS) is defined in 3GPP TS 24.501. It operates within the control plane, facilitating communication between the User Equipment (UE) and the Access and Mobility Management Function (AMF) over the N1 interface. -This tuto for 5G gNB NAS design -{: .text-center} +Key 5GS NAS messages: -# source files +* Mobility Management (5GMM): Handles UE registration, deregistration, authentication and tracking area updates. 5GMM also manages transitions between idle and connected states. +* Session Management (5GSM): Establishes, modifies, and releases PDU sessions. Coordinates with the Session Management Function (SMF) to manage user-plane resources. -openair2/RRC/NR/nr_ngap_gNB.c: skeleton for interface with NGAP -openair3/NAS/COMMON/milenage.h: a simple milenage implementation, depend only on crypto library -openair3/NAS/COMMON/NR_NAS_defs.h: messages defined for NAS implemented in C: C struct, C enums and automatic conversion to labels for debug messages -openair3/NAS/COMMON/nr_common.c: 5G NAS common code between gNB and UE -openair3/UICC/usim_interface.c: UICC simulation for USIM messages +# OAI Implementation Status -openair3/NAS/NR_UE/ue_process_nas.c: NAS code for UE +The following tables lists implemented NAS messages and whether there is an encoder or decoder function, and if a corresponding unit test exists. -openair3/NAS/gNB/network_process_nas.c: NAS code for gNB running without 5GC +| Type | Message | Encoding | Decoding | Unit test | +|-------|-------------------------------------------|----------|----------|------------| +| 5GMM | Service Request | yes | yes | yes | +| 5GMM | Service Accept | yes | yes | yes | +| 5GMM | Service Reject | yes | yes | yes | +| 5GMM | Authentication Response | yes | no | no | +| 5GMM | Identity Response | yes | no | no | +| 5GMM | Security Mode Complete | yes | no | no | +| 5GMM | Uplink NAS Transport | yes | no | no | +| 5GMM | Registration Request | yes | yes | no | +| 5GMM | Registration Accept | yes | yes | yes | +| 5GMM | Registration Complete | yes | yes | no | +| 5GMM | Deregistration Request (UE originating) | yes | no | no | +| 5GSM | PDU Session Establishment Request | yes | no | no | +| 5GSM | PDU Session Establishment Accept | no | yes | no | -openair3/TEST/test5Gnas.c: unitary testing of NAS messages: encodes+decode a standard network entry, mixing code from UE and 5GC (or gNB in noCore) +## Code Structure -# USIM simulation -A new USIM simulation, that parameters are in regular OAI config files +[openair3/NAS/NR_UE/nr_nas_msg.c](../openair3/NAS/NR_UE/nr_nas_msg.c): +* NAS procedures and message handlers/callbacks +* Integration with RRC (ITTI) and SDAP +* Invokes enc/dec libraries +* Handles 5GMM state and mode -## To open the USIM -init_uicc() takes a parameter: the name of the block in the config file -In case we run several UEs in one process, the variable section name allows to make several UEs configurations in one config file. +[openair3/NAS/NR_UE/5GS/fgs_nas_lib.c](../openair3/NAS/NR_UE/5GS/fgs_nas_lib.c): +[openair3/NAS/NR_UE/5GS/NR_NAS_defs.h](../openair3/NAS/NR_UE/5GS/NR_NAS_defs.h): +* encoding and decoding functions for 5G NAS message headers and payloads +* relies on 5GMM/5GSM messages libs for payload encoding -The NAS implementation uses this possibility. +[openair3/NAS/NR_UE/5GS/fgs_nas_utils.h](../openair3/NAS/NR_UE/5GS/fgs_nas_utils.h): +* NAS helpers, macros -# UE side -UEprocessNAS() is the entry for all messages. -It decodes the message type and call the specific function for each messgae +[openair3/NAS/NR_UE/5GS/5GMM](../openair3/NAS/NR_UE/5GS/5GMM): +* encoding/decoding functions and definitions for 5GMM NAS messages payloads -## Identityrequest + IdentityResponse -When the UE receives the request, it stores the 5GC request parameters in the UE context ( UE->uicc pointer) -It calls "scheduleNAS", that would trigger a answer (seeing general archtecture, it will probly be a itti message to future RRC or NGAP thread). +[openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c](../openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c): +[openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h](../openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h): +* encoding/decoding functions and definitions for common 5GMM IEs -When the scheduler wants to encode the answer, it calls identityResponse() -The UE search for a section in the config file called "uicc" -it encodes the NAS answer with the IMSI, as a 4G compatible authentication. -A future release would encode 5G SUPI as native 5G UICC. +[openair3/NAS/NR_UE/5GS/5GSM](../openair3/NAS/NR_UE/5GS/5GSM): +* encoding and decoding functions for 5GSM NAS messages payloads -## Authenticationrequest + authenticationResponse -When the UE receives the request, it stores the 5GC request parameters in the UE context ( UE->uicc pointer) -It calls "scheduleNAS", that would trigger a answer (seeing general archtecture, it will probly be a itti message to future RRC or NGAP thread). +# USIM Simulation -When the scheduler wants to encode the answer, it calls authenticationResponse() -The UE search for a section in the config file called "uicc" -It uses the Milenage parameters of this section to encode a milenage response -A future release would encode 5G new authentication cyphering functions. +OAI includes a simulated USIM implementation that reads its parameters from standard configuration files. This allows for rapid prototyping and testing without relying on a physical UICC card. The USIM-related configuration is handled via the `init_uicc()` function. -## SecurityModeCommand + securityModeComplete -When the UE receives the request it will: -Selected NAS security algorithms: store it for future encoding -Replayed UE security capabilities: check if it is equal to UE capabilities it sent to the 5GC -IMEISV request: to implement -ngKSI: Key set Indicator, similator to 4G to select a ciphering keys set +## Configuration -When the scheduler wants to encode the answer, it calls registrationComplete() +The simulation reads values from a named section in the config file. -## registrationComplete -To be defined in UE, this NAS message is done after RRC sequence completes +**Config options in the `uicc` section**: -# gNB side -gNB NGAP thread receives the NAS message from PHY layers -In normal mode, it send it to the core network with no decoding. +| Parameter | Description | Default value | +|---------------|-----------------------------------|--------------------------------------------| +| `imsi` | User IMSI | `2089900007487` | +| `nmc_size` | Number of digits in NMC | `2` | +| `key` | Subscription key (Ki) | `fec86ba6eb707ed08905757b1bb44b8f` | +| `opc` | OPc value | `c42449363bbad02b66d16bc975d77cc1` | +| `amf` | AMF value | `8000` | +| `sqn` | Sequence number | `000000` | +| `dnn` | Default DNN (APN) | `oai` | +| `nssai_sst` | NSSAI slice/service type | `1` | +| `nssai_sd` | NSSAI slice differentiator | `0xffffff` | +| `imeisv` | IMEISV string | `6754567890123413` | -Here after, the gNB mode "noCore" processing in gNB: NGAP calls the entry function: processNAS() instead of forwarding the packet to the 5GC +These are parsed and stored in the `uicc_t` structure. -## RRCModeComplete + Identityrequest -When the gNB completes RRC attach, it sends a first message to NGAP thread. -Normal processing sends NGAP initial UE message, in noCore mode, it should call: identityRequest() that encode the NAS identity request inside the gNB. -The gNB NGAP thread then should call the piece of code that forward the message to the UE as when it receives a NAS message from 5GC. +## Initialization -## All NAS coming from UE -When NGAP thread receives a NAS message from lower layers, it encapsulates it to forward it to the 5GC. In "noCore" mode it calls processNAS(). +The UE calls `init_uicc` via `checkUicc` to allocate memory for the `uicc_t` structure member and load parameters using `config_get()` from the selected config section. Then the UICC structure is stored in the NAS context. `nr_ue_nas_t`, to be used to fill identity and security credentials when generating responses to 5GC messages such as `Identity Response`, `Authentication Response`, and `Security Mode Complete`. -This function stores in the gNB the NAS data, then it should encode the next message (identity, authentication, security mode) or do nothing (registration complete). +## Milenage Authentication and Key Derivation +When the UE receives a **5GMM Authentication Request**, the function `generateAuthenticationResp` generates a valid `Authentication Response` with the necessary derived NAS and AS security keys. The function `derive_ue_keys` parses the Authentication Request and performs the entire 5G AKA key hierarchy: -

-**Note** -{: .panel-heading} -
+* Extracts the `RAND` and `SQN` +* Performs Milenage Algorithms f2-f5 using `f2345()` from the UICC input +* Computes the `RES` using `transferRES()` +* Derives the keys `KAUSF`, `KSEAF`, `KAMF`, `KNASenc`/`KNASint` via `derive_knas()` and `KGNB` for RRC ciphering (via `derive_kgnb()`) +## Security Mode Complete -
-
+When the UE receives a **Security Mode Command** from the 5GC, it responds with a `Security Mode Complete` message. This message is protected with the newly established NAS security context and may carry additional payloads, including the UE’s identity and a nested NAS message (e.g., `Registration Request`) in the `FGSNasMessageContainer`. The function responsible for building and securing this response is `generateSecurityModeComplete`. + +### IMEISV + +The **IMEISV**, is encoded using the `fill_imeisv()` helper. This function extracts each digit from the configured `imeisvStr` in the UICC context and populates the mobile identity structure. + +See TS 24.501 §4.4 for reference. diff --git a/doc/README.md b/doc/README.md index f1b0ba8284..24050666a7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -86,9 +86,7 @@ Legacy unmaintained files: - [Information on gNB MAC](./MAC/mac-usage.md) - [Information on gNB RRC](./RRC/rrc-usage.md) - [Information on analog beamforming implementation](./analog_beamforming.md) - -Legacy unmaintained files: -- [`5Gnas.md`](./5Gnas.md) +- [Information on the UE 5G NAS implementation](./5Gnas.md) # Building and running from images diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 5a30900c3d..1939c4175b 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -106,9 +106,12 @@ static void *NRUE_phy_stub_standalone_pnf_task(void *arg); static void start_process_slot_tx(void* arg) { notifiedFIFO_elt_t *newTx = arg; nr_rxtx_thread_data_t *curMsgTx = NotifiedFifoData(newTx); - int num_ul_actors_to_use = - get_nrUE_params()->num_ul_actors == 0 ? NUM_UL_ACTORS : min(get_nrUE_params()->num_ul_actors, NUM_UL_ACTORS); - pushNotifiedFIFO(&curMsgTx->UE->ul_actors[curMsgTx->proc.nr_slot_tx % num_ul_actors_to_use].fifo, newTx); + int num_ul_actors = get_nrUE_params()->num_ul_actors; + if (num_ul_actors > 0) { + pushNotifiedFIFO(&curMsgTx->UE->ul_actors[curMsgTx->proc.nr_slot_tx % num_ul_actors].fifo, newTx); + } else { + newTx->processingFunc(curMsgTx); + } } static size_t dump_L1_UE_meas_stats(PHY_VARS_NR_UE *ue, char *output, size_t max_len) @@ -677,10 +680,10 @@ static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE) /* Clearing UE harq while DL actors are active causes race condition. So we let the current execution to complete here.*/ - for (int i = 0; i < NUM_DL_ACTORS; i++) { + for (int i = 0; i < get_nrUE_params()->num_dl_actors; i++) { flush_actor(UE->dl_actors + i); } - for (int i = 0; i < NUM_UL_ACTORS; i++) { + for (int i = 0; i < get_nrUE_params()->num_ul_actors; i++) { flush_actor(UE->ul_actors + i); } @@ -1183,7 +1186,11 @@ void *UE_thread(void *arg) int ret = UE_dl_preprocessing(UE, &curMsgRx->proc, tx_wait_for_dlsch, &curMsgRx->phy_data, &stats_printed); if (ret != INT_MAX) shiftForNextFrame = ret; - pushNotifiedFIFO(&UE->dl_actors[curMsg.proc.nr_slot_rx % NUM_DL_ACTORS].fifo, newRx); + if (get_nrUE_params()->num_dl_actors > 0) { + pushNotifiedFIFO(&UE->dl_actors[curMsg.proc.nr_slot_rx % get_nrUE_params()->num_dl_actors].fifo, newRx); + } else { + newRx->processingFunc(curMsgRx); + } // apply new NTN timing information apply_ntn_config(UE, fp, slot_nr, &duration_rx_to_tx, &timing_advance, &ntn_koffset); diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 980ab3bbd2..3106f823b3 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -506,11 +506,17 @@ int main(int argc, char **argv) UE[CC_id]->sl_mode = get_softmodem_params()->sl_mode; init_actor(&UE[CC_id]->sync_actor, "SYNC_", -1); - for (int i = 0; i < NUM_DL_ACTORS; i++) { - init_actor(&UE[CC_id]->dl_actors[i], "DL_", -1); + if (get_nrUE_params()->num_dl_actors > 0) { + UE[CC_id]->dl_actors = calloc_or_fail(get_nrUE_params()->num_dl_actors, sizeof(*UE[CC_id]->dl_actors)); + for (int i = 0; i < get_nrUE_params()->num_dl_actors; i++) { + init_actor(&UE[CC_id]->dl_actors[i], "DL_", -1); + } } - for (int i = 0; i < NUM_UL_ACTORS; i++) { - init_actor(&UE[CC_id]->ul_actors[i], "UL_", -1); + if (get_nrUE_params()->num_ul_actors > 0) { + UE[CC_id]->ul_actors = calloc_or_fail(get_nrUE_params()->num_ul_actors, sizeof(*UE[CC_id]->ul_actors)); + for (int i = 0; i < get_nrUE_params()->num_ul_actors; i++) { + init_actor(&UE[CC_id]->ul_actors[i], "UL_", -1); + } } init_nr_ue_vars(UE[CC_id], inst); @@ -576,10 +582,10 @@ int main(int argc, char **argv) for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { PHY_VARS_NR_UE *phy_vars = PHY_vars_UE_g[0][CC_id]; if (phy_vars) { - for (int i = 0; i < NUM_UL_ACTORS; i++) { + for (int i = 0; i < get_nrUE_params()->num_ul_actors; i++) { shutdown_actor(&phy_vars->ul_actors[i]); } - for (int i = 0; i < NUM_DL_ACTORS; i++) { + for (int i = 0; i < get_nrUE_params()->num_dl_actors; i++) { shutdown_actor(&phy_vars->dl_actors[i]); } int ret = pthread_join(phy_vars->main_thread, NULL); diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index 5af21a16e8..2eb9138e78 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -22,7 +22,8 @@ extern uint16_t ue_id_g; #define CONFIG_HLP_FREQ_SYNC_I "coefficient for Integrating part of continuous frequency offset compensation PI controller\n" #define CONFIG_HLP_CONT_FO_COMP "Enable continuous frequency offset (FO) estimation and compensation and specify main FO source (1 = local oscillator, 2 = Doppler shift)\n" #define CONFIG_HLP_AGC "Rx Gain control used for UE\n" -#define CONFIG_HLP_NUM_UL_ACTORS "Number of UL actors to use. Set to 0 to use the default value\n" +#define CONFIG_HLP_NUM_UL_ACTORS "Number of UL actors to use. Set to 0 to disable UL actor framework and do processing inline\n" +#define CONFIG_HLP_NUM_DL_ACTORS "Number of DL actors to use. Set to 0 to disable DL actor framework and do processing inline\n" /***************************************************************************************************************************************/ /* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument @@ -78,7 +79,8 @@ extern uint16_t ue_id_g; {"freq-sync-I", CONFIG_HLP_FREQ_SYNC_I, 0, .dblptr=&(nrUE_params.freq_sync_I), .defdblval=0.001, TYPE_DOUBLE, 0}, \ {"cont-fo-comp", CONFIG_HLP_CONT_FO_COMP, 0, .iptr=&(nrUE_params.cont_fo_comp), .defintval=0, TYPE_INT, 0}, \ {"agc", CONFIG_HLP_AGC, PARAMFLAG_BOOL, .iptr=&(nrUE_params.agc), .defintval=0, TYPE_INT, 0}, \ - {"num-ul-actors", CONFIG_HLP_NUM_UL_ACTORS, 0, .iptr=&nrUE_params.num_ul_actors, .defintval=0, TYPE_INT, 0}, \ + {"num-ul-actors", CONFIG_HLP_NUM_UL_ACTORS, 0, .iptr=&nrUE_params.num_ul_actors, .defintval=2, TYPE_INT, 0}, \ + {"num-dl-actors", CONFIG_HLP_NUM_DL_ACTORS, 0, .iptr=&nrUE_params.num_dl_actors, .defintval=4, TYPE_INT, 0}, \ } // clang-format on @@ -119,6 +121,7 @@ typedef struct { int vcdflag; int tx_max_power; int num_ul_actors; + int num_dl_actors; } nrUE_params_t; extern uint64_t get_nrUE_optmask(void); extern uint64_t set_nrUE_optmask(uint64_t bitmask); diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt index 5b89d36f38..f64384568a 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt @@ -7,7 +7,7 @@ add_boolean_option(ENABLE_LDPC_T2 OFF "Build support for LDPC Offload to T2 libr if (ENABLE_LDPC_T2) - pkg_check_modules(LIBDPDK_T2 REQUIRED libdpdk=20.11.9) + pkg_check_modules(LIBDPDK_T2 REQUIRED libdpdk>=20.11.9) find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_T2_LIBRARY_DIRS}) if (NOT PMD_T2) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c index 6eb8f0dacc..04ad571de0 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c @@ -45,8 +45,6 @@ #define SYNC_WAIT 0 #define SYNC_START 1 #define TIME_OUT_POLL 1e8 -/* Increment for next code block in external HARQ memory */ -#define HARQ_INCR 32768 /* Headroom for filler LLRs insertion in HARQ buffer */ #define FILLER_HEADROOM 1024 /* Number of segments that could be stored in HARQ combined buffers */ @@ -55,15 +53,6 @@ pthread_mutex_t encode_mutex; pthread_mutex_t decode_mutex; -const char *typeStr[] = { - "RTE_BBDEV_OP_NONE", /**< Dummy operation that does nothing */ - "RTE_BBDEV_OP_TURBO_DEC", /**< Turbo decode */ - "RTE_BBDEV_OP_TURBO_ENC", /**< Turbo encode */ - "RTE_BBDEV_OP_LDPC_DEC", /**< LDPC decode */ - "RTE_BBDEV_OP_LDPC_ENC", /**< LDPC encode */ - "RTE_BBDEV_OP_TYPE_COUNT", /**< Count of different op types */ -}; - /* Represents tested active devices */ struct active_device { const char *driver_name; @@ -76,9 +65,6 @@ struct active_device { struct rte_mempool *bbdev_enc_op_pool; struct rte_mempool *in_mbuf_pool; struct rte_mempool *hard_out_mbuf_pool; - struct rte_mempool *soft_out_mbuf_pool; - struct rte_mempool *harq_in_mbuf_pool; - struct rte_mempool *harq_out_mbuf_pool; } active_devs[RTE_BBDEV_MAX_DEVS]; static int nb_active_devs; @@ -86,23 +72,12 @@ static int nb_active_devs; struct data_buffers { struct rte_bbdev_op_data *inputs; struct rte_bbdev_op_data *hard_outputs; - struct rte_bbdev_op_data *soft_outputs; - struct rte_bbdev_op_data *harq_inputs; - struct rte_bbdev_op_data *harq_outputs; }; /* Operation parameters specific for given test case */ struct test_op_params { - struct rte_mempool *mp_dec; - struct rte_mempool *mp_enc; - struct rte_bbdev_dec_op *ref_dec_op; - struct rte_bbdev_enc_op *ref_enc_op; - uint16_t burst_sz; - uint16_t num_to_process; uint16_t num_lcores; - int vector_mask; rte_atomic16_t sync; - struct data_buffers q_bufs[RTE_MAX_NUMA_NODES][MAX_QUEUES]; }; /* Contains per lcore params */ @@ -113,18 +88,14 @@ struct thread_params { nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters; nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters; uint8_t iter_count; - rte_atomic16_t nb_dequeued; - rte_atomic16_t processing_status; - rte_atomic16_t burst_sz; struct test_op_params *op_params; - struct rte_bbdev_dec_op *dec_ops[MAX_BURST]; - struct rte_bbdev_enc_op *enc_ops[MAX_BURST]; + struct data_buffers *data_buffers; + struct rte_mempool *bbdev_op_pool; }; static uint16_t nb_segments_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { uint16_t nb_segments = 0; - for (uint16_t h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) - { + for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { nb_segments+=nrLDPC_slot_decoding_parameters->TBs[h].C; } return nb_segments; @@ -132,33 +103,12 @@ static uint16_t nb_segments_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_s static uint16_t nb_segments_encoding(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { uint16_t nb_segments = 0; - for (uint16_t h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) - { + for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { nb_segments+=nrLDPC_slot_encoding_parameters->TBs[h].C; } return nb_segments; } -// DPDK BBDEV copy -static inline void -mbuf_reset(struct rte_mbuf *m) -{ - m->pkt_len = 0; - - do { - m->data_len = 0; - m = m->next; - } while (m != NULL); -} - -/* Read flag value 0/1 from bitmap */ -// DPDK BBDEV copy -static inline bool -check_bit(uint32_t bitmap, uint32_t bitmask) -{ - return bitmap & bitmask; -} - /* calculates optimal mempool size not smaller than the val */ // DPDK BBDEV copy static unsigned int @@ -298,7 +248,7 @@ static int add_dev(uint8_t dev_id, struct rte_bbdev_info *info) /* Display for debug the capabilities of the card */ for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { - printf("device: %d, capability[%d]=%s\n", dev_id, i, typeStr[info->drv.capabilities[i].type]); + printf("device: %d, capability[%d]=%s\n", dev_id, i, rte_bbdev_op_type_str(info->drv.capabilities[i].type)); if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_ENC) { const struct rte_bbdev_op_cap_ldpc_enc cap = info->drv.capabilities[i].cap.ldpc_enc; printf(" buffers: src = %d, dst = %d\n capabilites: ", cap.num_buffers_src, cap.num_buffers_dst); @@ -376,9 +326,9 @@ static int init_op_data_objs_dec(struct rte_bbdev_op_data *bufs, uint16_t min_alignment) { bool large_input = false; - uint16_t j = 0; - for (uint16_t h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { - for (uint16_t i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { + int j = 0; + for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { + for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { uint32_t data_len = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; char *data; struct rte_mbuf *m_head = rte_pktmbuf_alloc(mbuf_pool); @@ -395,9 +345,9 @@ static int init_op_data_objs_dec(struct rte_bbdev_op_data *bufs, bufs[j].data = m_head; bufs[j].offset = 0; bufs[j].length = 0; - - if ((op_type == DATA_INPUT) || (op_type == DATA_HARQ_INPUT)) { - if ((op_type == DATA_INPUT) && large_input) { + + if (op_type == DATA_INPUT) { + if (large_input) { /* Allocate a fake overused mbuf */ data = rte_malloc(NULL, data_len, 0); AssertFatal(data != NULL, "rte malloc failed with %u bytes", data_len); @@ -427,14 +377,13 @@ static int init_op_data_objs_dec(struct rte_bbdev_op_data *bufs, // based on DPDK BBDEV init_op_data_objs static int init_op_data_objs_enc(struct rte_bbdev_op_data *bufs, nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters, - struct rte_mbuf *m_head, struct rte_mempool *mbuf_pool, enum op_data_type op_type, uint16_t min_alignment) { bool large_input = false; - uint16_t j = 0; - for (uint16_t h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { + int j = 0; + for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { for (int i = 0; i < nrLDPC_slot_encoding_parameters->TBs[h].C; ++i) { uint32_t data_len = (nrLDPC_slot_encoding_parameters->TBs[h].K - nrLDPC_slot_encoding_parameters->TBs[h].F + 7) / 8; char *data; @@ -452,9 +401,9 @@ static int init_op_data_objs_enc(struct rte_bbdev_op_data *bufs, bufs[j].data = m_head; bufs[j].offset = 0; bufs[j].length = 0; - - if ((op_type == DATA_INPUT) || (op_type == DATA_HARQ_INPUT)) { - if ((op_type == DATA_INPUT) && large_input) { + + if (op_type == DATA_INPUT) { + if (large_input) { /* Allocate a fake overused mbuf */ data = rte_malloc(NULL, data_len, 0); AssertFatal(data != NULL, "rte malloc failed with %u bytes", data_len); @@ -502,40 +451,24 @@ static int allocate_buffers_on_socket(struct rte_bbdev_op_data **buffers, const } // DPDK BBDEV copy -static void -free_buffers(struct active_device *ad, struct test_op_params *op_params) +static void free_mempools(struct active_device *ad) { rte_mempool_free(ad->bbdev_dec_op_pool); rte_mempool_free(ad->bbdev_enc_op_pool); rte_mempool_free(ad->in_mbuf_pool); rte_mempool_free(ad->hard_out_mbuf_pool); - rte_mempool_free(ad->soft_out_mbuf_pool); - rte_mempool_free(ad->harq_in_mbuf_pool); - rte_mempool_free(ad->harq_out_mbuf_pool); - - for (int i = 2; i < rte_lcore_count(); ++i) { - for (int j = 0; j < RTE_MAX_NUMA_NODES; ++j) { - rte_free(op_params->q_bufs[j][i].inputs); - rte_free(op_params->q_bufs[j][i].hard_outputs); - rte_free(op_params->q_bufs[j][i].soft_outputs); - rte_free(op_params->q_bufs[j][i].harq_inputs); - rte_free(op_params->q_bufs[j][i].harq_outputs); - } - } } // based on DPDK BBDEV copy_reference_ldpc_dec_op static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, - unsigned int start_idx, - struct data_buffers *bufs, + struct rte_bbdev_op_data *inputs, + struct rte_bbdev_op_data *outputs, nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { - unsigned int h; - unsigned int i; - unsigned int j = 0; - for (h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h){ - for (i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { + int j = 0; + for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { + for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { ops[j]->ldpc_dec.cb_params.e = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; ops[j]->ldpc_dec.basegraph = nrLDPC_slot_decoding_parameters->TBs[h].BG; ops[j]->ldpc_dec.z_c = nrLDPC_slot_decoding_parameters->TBs[h].Z; @@ -571,16 +504,8 @@ set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, ops[j]->ldpc_dec.harq_combined_input.offset = harq_combined_offset; ops[j]->ldpc_dec.harq_combined_output.offset = harq_combined_offset; - if (bufs->hard_outputs != NULL) - ops[j]->ldpc_dec.hard_output = bufs->hard_outputs[start_idx + j]; - if (bufs->inputs != NULL) - ops[j]->ldpc_dec.input = bufs->inputs[start_idx + j]; - if (bufs->soft_outputs != NULL) - ops[j]->ldpc_dec.soft_output = bufs->soft_outputs[start_idx + j]; - if (bufs->harq_inputs != NULL) - ops[j]->ldpc_dec.harq_combined_input = bufs->harq_inputs[start_idx + j]; - if (bufs->harq_outputs != NULL) - ops[j]->ldpc_dec.harq_combined_output = bufs->harq_outputs[start_idx + j]; + ops[j]->ldpc_dec.hard_output = outputs[j]; + ops[j]->ldpc_dec.input = inputs[j]; ++j; } } @@ -589,16 +514,13 @@ set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, // based on DPDK BBDEV copy_reference_ldpc_enc_op static void set_ldpc_enc_op(struct rte_bbdev_enc_op **ops, - unsigned int start_idx, struct rte_bbdev_op_data *inputs, struct rte_bbdev_op_data *outputs, nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { - unsigned int h; - unsigned int i; - unsigned int j = 0; - for (h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h){ - for (i = 0; i < nrLDPC_slot_encoding_parameters->TBs[h].C; ++i) { + int j = 0; + for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { + for (int i = 0; i < nrLDPC_slot_encoding_parameters->TBs[h].C; ++i) { ops[j]->ldpc_enc.cb_params.e = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; ops[j]->ldpc_enc.basegraph = nrLDPC_slot_encoding_parameters->TBs[h].BG; ops[j]->ldpc_enc.z_c = nrLDPC_slot_encoding_parameters->TBs[h].Z; @@ -612,34 +534,23 @@ set_ldpc_enc_op(struct rte_bbdev_enc_op **ops, ops[j]->ldpc_enc.rv_index = nrLDPC_slot_encoding_parameters->TBs[h].rv_index; ops[j]->ldpc_enc.op_flags = RTE_BBDEV_LDPC_RATE_MATCH; ops[j]->ldpc_enc.code_block_mode = 1; - ops[j]->ldpc_enc.output = outputs[start_idx + j]; - ops[j]->ldpc_enc.input = inputs[start_idx + j]; + ops[j]->ldpc_enc.output = outputs[j]; + ops[j]->ldpc_enc.input = inputs[j]; ++j; } } } -static int -retrieve_ldpc_dec_op(struct rte_bbdev_dec_op **ops, - const int vector_mask, - nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) +static int retrieve_ldpc_dec_op(struct rte_bbdev_dec_op **ops, nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { - struct rte_bbdev_op_data *hard_output; - uint16_t data_len = 0; - struct rte_mbuf *m; - char *data; - unsigned int h; - unsigned int i; - unsigned int j = 0; - for (h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h){ - for (i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { - hard_output = &ops[j]->ldpc_dec.hard_output; - m = hard_output->data; - data_len = rte_pktmbuf_data_len(m) - hard_output->offset; - data = m->buf_addr; - memcpy(nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c, data + m->data_off, data_len); - rte_pktmbuf_free(ops[j]->ldpc_dec.hard_output.data); - rte_pktmbuf_free(ops[j]->ldpc_dec.input.data); + int j = 0; + for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { + for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { + struct rte_bbdev_op_data *hard_output = &ops[j]->ldpc_dec.hard_output; + struct rte_mbuf *m = hard_output->data; + uint16_t data_len = rte_pktmbuf_data_len(m) - hard_output->offset; + uint8_t *data = rte_pktmbuf_mtod_offset(m, uint8_t *, hard_output->offset); + memcpy(nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c, data, data_len); ++j; } } @@ -651,17 +562,17 @@ retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { uint8_t *p_out = NULL; - unsigned int j = 0; - for (unsigned int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h){ + int j = 0; + for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h){ int E_sum = 0; int bit_offset = 0; int byte_offset = 0; p_out = nrLDPC_slot_encoding_parameters->TBs[h].output; - for (unsigned int r = 0; r < nrLDPC_slot_encoding_parameters->TBs[h].C; ++r) { + for (int r = 0; r < nrLDPC_slot_encoding_parameters->TBs[h].C; ++r) { struct rte_bbdev_op_data *output = &ops[j]->ldpc_enc.output; struct rte_mbuf *m = output->data; uint16_t data_len = rte_pktmbuf_data_len(m) - output->offset; - uint8_t *data = m->buf_addr + m->data_off; + uint8_t *data = rte_pktmbuf_mtod_offset(m, uint8_t *, output->offset); reverse_bits_u8(data, data_len, data); if (bit_offset == 0) { memcpy(&p_out[byte_offset], data, data_len); @@ -688,93 +599,58 @@ retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, E_sum += nrLDPC_slot_encoding_parameters->TBs[h].segments[r].E; byte_offset = (E_sum + 7) / 8; bit_offset = E_sum % 8; - rte_pktmbuf_free(m); - rte_pktmbuf_free(ops[j]->ldpc_enc.input.data); ++j; } } return 0; } -// based on DPDK BBDEV init_test_op_params -static int init_test_op_params(struct test_op_params *op_params, - enum rte_bbdev_op_type op_type, - struct rte_mempool *ops_mp, - uint16_t burst_sz, - uint16_t num_to_process, - uint16_t num_lcores) -{ - int ret = 0; - if (op_type == RTE_BBDEV_OP_LDPC_DEC) { - ret = rte_bbdev_dec_op_alloc_bulk(ops_mp, &op_params->ref_dec_op, num_to_process); - op_params->mp_dec = ops_mp; - } else { - ret = rte_bbdev_enc_op_alloc_bulk(ops_mp, &op_params->ref_enc_op, 1); - op_params->mp_enc = ops_mp; - } - - AssertFatal(ret == 0, "rte_bbdev_op_alloc_bulk() failed"); - - op_params->burst_sz = burst_sz; - op_params->num_to_process = num_to_process; - op_params->num_lcores = num_lcores; - return 0; -} - // based on DPDK BBDEV throughput_pmd_lcore_ldpc_dec static int pmd_lcore_ldpc_dec(void *arg) { struct thread_params *tp = arg; nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters = tp->nrLDPC_slot_decoding_parameters; - uint16_t enq, deq; int time_out = 0; const uint16_t queue_id = tp->queue_id; const uint16_t num_segments = nb_segments_decoding(nrLDPC_slot_decoding_parameters); - struct rte_bbdev_dec_op **ops_enq; - struct rte_bbdev_dec_op **ops_deq; - ops_enq = (struct rte_bbdev_dec_op **)rte_calloc("struct rte_bbdev_dec_op **ops_enq", num_segments, sizeof(struct rte_bbdev_dec_op *), RTE_CACHE_LINE_SIZE); - ops_deq = (struct rte_bbdev_dec_op **)rte_calloc("struct rte_bbdev_dec_op **ops_dec", num_segments, sizeof(struct rte_bbdev_dec_op *), RTE_CACHE_LINE_SIZE); - struct data_buffers *bufs = NULL; - uint16_t h, i, j; - int ret; - struct rte_bbdev_info info; - uint16_t num_to_enq; + struct rte_bbdev_dec_op *ops_enq[num_segments]; + struct rte_bbdev_dec_op *ops_deq[num_segments]; + struct data_buffers *bufs = tp->data_buffers; AssertFatal((num_segments < MAX_BURST), "BURST_SIZE should be <= %u", MAX_BURST); + + struct rte_bbdev_info info; rte_bbdev_info_get(tp->dev_id, &info); - bufs = &tp->op_params->q_bufs[GET_SOCKET(info.socket_id)][queue_id]; while (rte_atomic16_read(&tp->op_params->sync) == SYNC_WAIT) rte_pause(); - ret = rte_bbdev_dec_op_alloc_bulk(tp->op_params->mp_dec, ops_enq, num_segments); + int ret = rte_bbdev_dec_op_alloc_bulk(tp->bbdev_op_pool, ops_enq, num_segments); AssertFatal(ret == 0, "Allocation failed for %d ops", num_segments); - set_ldpc_dec_op(ops_enq, 0, bufs, nrLDPC_slot_decoding_parameters); - - for (enq = 0, deq = 0; enq < num_segments;) { - num_to_enq = num_segments; - if (unlikely(num_segments - enq < num_to_enq)) - num_to_enq = num_segments - enq; + set_ldpc_dec_op(ops_enq, bufs->inputs, bufs->hard_outputs, nrLDPC_slot_decoding_parameters); + uint16_t enq = 0, deq = 0; + while (enq < num_segments) { + uint16_t num_to_enq = num_segments - enq; enq += rte_bbdev_enqueue_ldpc_dec_ops(tp->dev_id, queue_id, &ops_enq[enq], num_to_enq); deq += rte_bbdev_dequeue_ldpc_dec_ops(tp->dev_id, queue_id, &ops_deq[deq], enq - deq); } - /* dequeue the remaining */ while (deq < enq) { deq += rte_bbdev_dequeue_ldpc_dec_ops(tp->dev_id, queue_id, &ops_deq[deq], enq - deq); time_out++; DevAssert(time_out <= TIME_OUT_POLL); } + if (deq == enq) { - ret = retrieve_ldpc_dec_op(ops_deq, tp->op_params->vector_mask, nrLDPC_slot_decoding_parameters); + ret = retrieve_ldpc_dec_op(ops_deq, nrLDPC_slot_decoding_parameters); AssertFatal(ret == 0, "LDPC offload decoder failed!"); tp->iter_count = 0; /* get the max of iter_count for all dequeued ops */ - j = 0; - for (h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h){ - for (i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { + int j = 0; + for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { + for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { bool *status = &nrLDPC_slot_decoding_parameters->TBs[h].segments[i].decodeSuccess; tp->iter_count = RTE_MAX(ops_enq[j]->ldpc_dec.iter_count, tp->iter_count); @@ -804,8 +680,6 @@ pmd_lcore_ldpc_dec(void *arg) } rte_bbdev_dec_op_free_bulk(ops_enq, num_segments); - rte_free(ops_enq); - rte_free(ops_deq); // Return the worst decoding number of iterations for all segments return tp->iter_count; } @@ -815,37 +689,33 @@ static int pmd_lcore_ldpc_enc(void *arg) { struct thread_params *tp = arg; nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters = tp->nrLDPC_slot_encoding_parameters; - uint16_t enq, deq; int time_out = 0; const uint16_t queue_id = tp->queue_id; const uint16_t num_segments = nb_segments_encoding(nrLDPC_slot_encoding_parameters); - struct rte_bbdev_enc_op **ops_enq; - struct rte_bbdev_enc_op **ops_deq; - ops_enq = (struct rte_bbdev_enc_op **)rte_calloc("struct rte_bbdev_dec_op **ops_enq", num_segments, sizeof(struct rte_bbdev_enc_op *), RTE_CACHE_LINE_SIZE); - ops_deq = (struct rte_bbdev_enc_op **)rte_calloc("struct rte_bbdev_dec_op **ops_dec", num_segments, sizeof(struct rte_bbdev_enc_op *), RTE_CACHE_LINE_SIZE); - struct rte_bbdev_info info; - int ret; - struct data_buffers *bufs = NULL; - uint16_t num_to_enq; + struct rte_bbdev_enc_op *ops_enq[num_segments]; + struct rte_bbdev_enc_op *ops_deq[num_segments]; + struct data_buffers *bufs = tp->data_buffers; AssertFatal((num_segments < MAX_BURST), "BURST_SIZE should be <= %u", MAX_BURST); + struct rte_bbdev_info info; rte_bbdev_info_get(tp->dev_id, &info); - bufs = &tp->op_params->q_bufs[GET_SOCKET(info.socket_id)][queue_id]; + while (rte_atomic16_read(&tp->op_params->sync) == SYNC_WAIT) rte_pause(); - ret = rte_bbdev_enc_op_alloc_bulk(tp->op_params->mp_enc, ops_enq, num_segments); - AssertFatal(ret == 0, "Allocation failed for %d ops", num_segments); - set_ldpc_enc_op(ops_enq, 0, bufs->inputs, bufs->hard_outputs, nrLDPC_slot_encoding_parameters); + int ret = rte_bbdev_enc_op_alloc_bulk(tp->bbdev_op_pool, ops_enq, num_segments); + AssertFatal(ret == 0, "Allocation failed for %d ops", num_segments); + set_ldpc_enc_op(ops_enq, bufs->inputs, bufs->hard_outputs, nrLDPC_slot_encoding_parameters); + if (nrLDPC_slot_encoding_parameters->tprep != NULL) stop_meas(nrLDPC_slot_encoding_parameters->tprep); if (nrLDPC_slot_encoding_parameters->tparity != NULL) start_meas(nrLDPC_slot_encoding_parameters->tparity); - for (enq = 0, deq = 0; enq < num_segments;) { - num_to_enq = num_segments; - if (unlikely(num_segments - enq < num_to_enq)) - num_to_enq = num_segments - enq; + + uint16_t enq = 0, deq = 0; + while (enq < num_segments) { + uint16_t num_to_enq = num_segments - enq; enq += rte_bbdev_enqueue_ldpc_enc_ops(tp->dev_id, queue_id, &ops_enq[enq], num_to_enq); deq += rte_bbdev_dequeue_ldpc_enc_ops(tp->dev_id, queue_id, &ops_deq[deq], enq - deq); } @@ -864,21 +734,18 @@ static int pmd_lcore_ldpc_enc(void *arg) if (nrLDPC_slot_encoding_parameters->toutput != NULL) stop_meas(nrLDPC_slot_encoding_parameters->toutput); rte_bbdev_enc_op_free_bulk(ops_enq, num_segments); - rte_free(ops_enq); - rte_free(ops_deq); return ret; } // based on DPDK BBDEV throughput_pmd_lcore_dec int start_pmd_dec(struct active_device *ad, struct test_op_params *op_params, + struct data_buffers *data_buffers, nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { - int ret; unsigned int lcore_id, used_cores = 0; - uint16_t num_lcores; /* Set number of lcores */ - num_lcores = (ad->nb_queues < (op_params->num_lcores)) ? ad->nb_queues : op_params->num_lcores; + int num_lcores = (ad->nb_queues < (op_params->num_lcores)) ? ad->nb_queues : op_params->num_lcores; /* Allocate memory for thread parameters structure */ struct thread_params *t_params = rte_zmalloc(NULL, num_lcores * sizeof(struct thread_params), RTE_CACHE_LINE_SIZE); AssertFatal(t_params != 0, @@ -889,6 +756,8 @@ int start_pmd_dec(struct active_device *ad, t_params[0].dev_id = ad->dev_id; t_params[0].lcore_id = rte_lcore_id(); t_params[0].op_params = op_params; + t_params[0].data_buffers = data_buffers; + t_params[0].bbdev_op_pool = ad->bbdev_dec_op_pool; t_params[0].queue_id = ad->dec_queue; t_params[0].iter_count = 0; t_params[0].nrLDPC_slot_decoding_parameters = nrLDPC_slot_decoding_parameters; @@ -900,13 +769,15 @@ int start_pmd_dec(struct active_device *ad, t_params[used_cores].dev_id = ad->dev_id; t_params[used_cores].lcore_id = lcore_id; t_params[used_cores].op_params = op_params; + t_params[used_cores].data_buffers = data_buffers; + t_params[used_cores].bbdev_op_pool = ad->bbdev_dec_op_pool; t_params[used_cores].queue_id = ad->queue_ids[used_cores]; t_params[used_cores].iter_count = 0; t_params[used_cores].nrLDPC_slot_decoding_parameters = nrLDPC_slot_decoding_parameters; rte_eal_remote_launch(pmd_lcore_ldpc_dec, &t_params[used_cores++], lcore_id); } rte_atomic16_set(&op_params->sync, SYNC_START); - ret = pmd_lcore_ldpc_dec(&t_params[0]); + int ret = pmd_lcore_ldpc_dec(&t_params[0]); /* Master core is always used */ // for (used_cores = 1; used_cores < num_lcores; used_cores++) // ret |= rte_eal_wait_lcore(t_params[used_cores].lcore_id); @@ -917,6 +788,7 @@ int start_pmd_dec(struct active_device *ad, // based on DPDK BBDEV throughput_pmd_lcore_enc int32_t start_pmd_enc(struct active_device *ad, struct test_op_params *op_params, + struct data_buffers *data_buffers, nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { unsigned int lcore_id, used_cores = 0; @@ -928,6 +800,8 @@ int32_t start_pmd_enc(struct active_device *ad, t_params[0].dev_id = ad->dev_id; t_params[0].lcore_id = rte_lcore_id() + 1; t_params[0].op_params = op_params; + t_params[0].data_buffers = data_buffers; + t_params[0].bbdev_op_pool = ad->bbdev_enc_op_pool; t_params[0].queue_id = ad->enc_queue; t_params[0].iter_count = 0; t_params[0].nrLDPC_slot_encoding_parameters = nrLDPC_slot_encoding_parameters; @@ -939,6 +813,8 @@ int32_t start_pmd_enc(struct active_device *ad, t_params[used_cores].dev_id = ad->dev_id; t_params[used_cores].lcore_id = lcore_id; t_params[used_cores].op_params = op_params; + t_params[used_cores].data_buffers = data_buffers; + t_params[used_cores].bbdev_op_pool = ad->bbdev_enc_op_pool; t_params[used_cores].queue_id = ad->queue_ids[1]; t_params[used_cores].iter_count = 0; t_params[used_cores].nrLDPC_slot_encoding_parameters = nrLDPC_slot_encoding_parameters; @@ -952,8 +828,6 @@ int32_t start_pmd_enc(struct active_device *ad, struct test_op_params *op_params = NULL; -struct rte_mbuf *m_head[DATA_NUM_TYPES]; - // OAI CODE int32_t nrLDPC_coding_init() { @@ -1008,12 +882,7 @@ int32_t nrLDPC_coding_init() printf("Couldn't create mempools"); return -1; } - f_ret = init_test_op_params(op_params, RTE_BBDEV_OP_LDPC_DEC, ad->bbdev_dec_op_pool, num_queues, num_queues, 1); - f_ret = init_test_op_params(op_params, RTE_BBDEV_OP_LDPC_ENC, ad->bbdev_enc_op_pool, num_queues, num_queues, 1); - if (f_ret != 0) { - printf("Couldn't init test op params"); - return -1; - } + op_params->num_lcores = 1; return 0; } @@ -1022,7 +891,7 @@ int32_t nrLDPC_coding_shutdown() struct active_device *ad = active_devs; int dev_id = 0; struct rte_bbdev_stats stats; - free_buffers(ad, op_params); + free_mempools(ad); rte_free(op_params); rte_bbdev_stats_get(dev_id, &stats); rte_bbdev_stop(dev_id); @@ -1032,20 +901,17 @@ int32_t nrLDPC_coding_shutdown() return 0; } -int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters){ - +int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) +{ pthread_mutex_lock(&decode_mutex); - uint16_t num_blocks = 0; - for(uint16_t h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h){ - num_blocks += nrLDPC_slot_decoding_parameters->TBs[h].C; - } - uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); + const uint16_t num_segments = nb_segments_decoding(nrLDPC_slot_decoding_parameters); + /* It is not unlikely that l_ol becomes big enough to overflow the stack * If you observe this behavior then move it to the heap * Then you would better do a persistent allocation to limit the overhead */ - uint8_t l_ol[num_blocks * LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); + uint8_t l_ol[num_segments * LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); // hardcoded we use first device struct active_device *ad = active_devs; @@ -1054,35 +920,26 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec rte_bbdev_info_get(ad->dev_id, &info); int socket_id = GET_SOCKET(info.socket_id); // fill_queue_buffers -> init_op_data_objs - struct rte_mempool *in_mp = ad->in_mbuf_pool; - struct rte_mempool *hard_out_mp = ad->hard_out_mbuf_pool; - struct rte_mempool *soft_out_mp = ad->soft_out_mbuf_pool; - struct rte_mempool *harq_in_mp = ad->harq_in_mbuf_pool; - struct rte_mempool *harq_out_mp = ad->harq_out_mbuf_pool; - struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {in_mp, soft_out_mp, hard_out_mp, harq_in_mp, harq_out_mp}; - uint8_t queue_id = ad->dec_queue; - struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&op_params->q_bufs[socket_id][queue_id].inputs, - &op_params->q_bufs[socket_id][queue_id].soft_outputs, - &op_params->q_bufs[socket_id][queue_id].hard_outputs, - &op_params->q_bufs[socket_id][queue_id].harq_inputs, - &op_params->q_bufs[socket_id][queue_id].harq_outputs}; + struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {ad->in_mbuf_pool, ad->hard_out_mbuf_pool}; + struct data_buffers data_buffers; + struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, &data_buffers.hard_outputs}; int offset = 0; - for(uint16_t h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h){ + for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { for (int r = 0; r < nrLDPC_slot_decoding_parameters->TBs[h].C; r++) { + uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); memcpy(z_ol, nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t)); simde__m128i *pv_ol128 = (simde__m128i *)z_ol; simde__m128i *pl_ol128 = (simde__m128i *)&l_ol[offset]; - int kc = nrLDPC_slot_decoding_parameters->TBs[h].BG == 2 ? 52 : 68; - for (int i = 0, j = 0; j < ((kc * nrLDPC_slot_decoding_parameters->TBs[h].Z) >> 4) + 1; i += 2, j++) { + for (int i = 0, j = 0; j < ((nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E + 15) >> 4); i += 2, j++) { pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]); } offset += LDPC_MAX_CB_SIZE; } } - for (enum op_data_type type = DATA_INPUT; type < 3; type += 2) { - ret = allocate_buffers_on_socket(queue_ops[type], num_blocks * sizeof(struct rte_bbdev_op_data), socket_id); + for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + ret = allocate_buffers_on_socket(queue_ops[type], num_segments * sizeof(struct rte_bbdev_op_data), socket_id); AssertFatal(ret == 0, "Couldn't allocate memory for rte_bbdev_op_data structs"); ret = init_op_data_objs_dec(*queue_ops[type], l_ol, @@ -1093,11 +950,17 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec AssertFatal(ret == 0, "Couldn't init rte_bbdev_op_data structs"); } - ret = start_pmd_dec(ad, op_params, nrLDPC_slot_decoding_parameters); + ret = start_pmd_dec(ad, op_params, &data_buffers, nrLDPC_slot_decoding_parameters); if (ret < 0) { LOG_E(PHY, "Couldn't start pmd dec\n"); } + for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + for (int segment = 0; segment < num_segments; ++segment) + rte_pktmbuf_free((*queue_ops[type])[segment].data); + rte_free(*queue_ops[type]); + } + pthread_mutex_unlock(&decode_mutex); return 0; } @@ -1107,6 +970,9 @@ int32_t nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_enc pthread_mutex_lock(&encode_mutex); if (nrLDPC_slot_encoding_parameters->tprep != NULL) start_meas(nrLDPC_slot_encoding_parameters->tprep); + + const uint16_t num_segments = nb_segments_encoding(nrLDPC_slot_encoding_parameters); + // hardcoded to use the first found board struct active_device *ad = active_devs; int ret; @@ -1114,34 +980,25 @@ int32_t nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_enc rte_bbdev_info_get(ad->dev_id, &info); int socket_id = GET_SOCKET(info.socket_id); // fill_queue_buffers -> init_op_data_objs - struct rte_mempool *in_mp = ad->in_mbuf_pool; - struct rte_mempool *hard_out_mp = ad->hard_out_mbuf_pool; - struct rte_mempool *soft_out_mp = ad->soft_out_mbuf_pool; - struct rte_mempool *harq_in_mp = ad->harq_in_mbuf_pool; - struct rte_mempool *harq_out_mp = ad->harq_out_mbuf_pool; - struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {in_mp, soft_out_mp, hard_out_mp, harq_in_mp, harq_out_mp}; - uint8_t queue_id = ad->enc_queue; - struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&op_params->q_bufs[socket_id][queue_id].inputs, - &op_params->q_bufs[socket_id][queue_id].soft_outputs, - &op_params->q_bufs[socket_id][queue_id].hard_outputs, - &op_params->q_bufs[socket_id][queue_id].harq_inputs, - &op_params->q_bufs[socket_id][queue_id].harq_outputs}; - uint16_t num_blocks = 0; - for(uint16_t h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h){ - num_blocks += nrLDPC_slot_encoding_parameters->TBs[h].C; - } - for (enum op_data_type type = DATA_INPUT; type < 3; type += 2) { - ret = allocate_buffers_on_socket(queue_ops[type], num_blocks * sizeof(struct rte_bbdev_op_data), socket_id); + struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {ad->in_mbuf_pool, ad->hard_out_mbuf_pool}; + struct data_buffers data_buffers; + struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, &data_buffers.hard_outputs}; + + for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + ret = allocate_buffers_on_socket(queue_ops[type], num_segments * sizeof(struct rte_bbdev_op_data), socket_id); AssertFatal(ret == 0, "Couldn't allocate memory for rte_bbdev_op_data structs"); - ret = init_op_data_objs_enc(*queue_ops[type], - nrLDPC_slot_encoding_parameters, - m_head[type], - mbuf_pools[type], - type, - info.drv.min_alignment); + ret = init_op_data_objs_enc(*queue_ops[type], nrLDPC_slot_encoding_parameters, mbuf_pools[type], type, info.drv.min_alignment); AssertFatal(ret == 0, "Couldn't init rte_bbdev_op_data structs"); } - ret = start_pmd_enc(ad, op_params, nrLDPC_slot_encoding_parameters); + + ret = start_pmd_enc(ad, op_params, &data_buffers, nrLDPC_slot_encoding_parameters); + + for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + for (int segment = 0; segment < num_segments; ++segment) + rte_pktmbuf_free((*queue_ops[type])[segment].data); + rte_free(*queue_ops[type]); + } + pthread_mutex_unlock(&encode_mutex); return ret; } diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h index 9da33f4671..642ac8f55a 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h @@ -16,10 +16,7 @@ enum op_data_type { DATA_INPUT = 0, - DATA_SOFT_OUTPUT, DATA_HARD_OUTPUT, - DATA_HARQ_INPUT, - DATA_HARQ_OUTPUT, DATA_NUM_TYPES, }; diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index f22e3e212b..db8524eddd 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -81,8 +81,6 @@ #define bzero(s,n) (memset((s),0,(n))) /// suppress compiler warning for unused arguments #define UNUSED(x) (void)x; -#define NUM_DL_ACTORS 4 -#define NUM_UL_ACTORS 2 // Set the number of barriers for processSlotTX to 512. This value has to be at least 483 for NTN where // DL-to-UL offset is up to 483. The selected value is also half of the frame range so that @@ -519,8 +517,8 @@ typedef struct PHY_VARS_NR_UE_s { sl_nr_sidelink_mode_t sl_mode; sl_nr_ue_phy_params_t SL_UE_PHY_PARAMS; Actor_t sync_actor; - Actor_t dl_actors[NUM_DL_ACTORS]; - Actor_t ul_actors[NUM_UL_ACTORS]; + Actor_t *dl_actors; + Actor_t *ul_actors; ntn_config_message_t* ntn_config_message; pthread_t main_thread; pthread_t stat_thread; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index b7dc9cf7a9..2f717a7466 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -336,17 +336,14 @@ static void config_preamble_index(NR_UE_MAC_INST_t *mac) nb_of_preambles = preamb_ga; } } - int rand_preamb = (rand_r(&seed) % ra->ssb_ro_config.preambles_per_ssb); + int pream_per_ssb = min(ra->ssb_ro_config.preambles_per_ssb, nb_of_preambles); + int rand_preamb = rand_r(&seed) % pream_per_ssb; if (ra->ssb_ro_config.ssb_per_ro < 1) ra->ra_PreambleIndex = groupOffset + rand_preamb; else { int ssb_pr_idx = mac->ssb_list.nb_ssb_per_index[mac->mib_ssb] % (int)ra->ssb_ro_config.ssb_per_ro; ra->ra_PreambleIndex = groupOffset + (ssb_pr_idx * ra->ssb_ro_config.preambles_per_ssb) + rand_preamb; } - AssertFatal(ra->ra_PreambleIndex < nb_of_preambles, - "Error! Selected preamble %d which exceeds number of prambles available %d\n", - ra->ra_PreambleIndex, - nb_of_preambles); } static void configure_ra_preamble(NR_UE_MAC_INST_t *mac) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 0df7cf9943..9928c168c1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -973,6 +973,7 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc) ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup); UE->CellGroup = oldUE->CellGroup; oldUE->CellGroup = NULL; + UE->mac_stats = oldUE->mac_stats; mac_remove_nr_ue(mac, *dl_rrc->old_gNB_DU_ue_id); pthread_mutex_unlock(&mac->sched_lock); nr_rlc_remove_ue(dl_rrc->gNB_DU_ue_id); diff --git a/openair2/RRC/LTE/rrc_UE.c b/openair2/RRC/LTE/rrc_UE.c index f7b513d264..2216fb5a75 100644 --- a/openair2/RRC/LTE/rrc_UE.c +++ b/openair2/RRC/LTE/rrc_UE.c @@ -1818,8 +1818,8 @@ rrc_ue_process_ueCapabilityEnquiry( ul_dcch_msg.message.choice.c1.choice.ueCapabilityInformation.rrc_TransactionIdentifier = UECapabilityEnquiry->rrc_TransactionIdentifier; ue_CapabilityRAT_Container.rat_Type = LTE_RAT_Type_eutra; OCTET_STRING_fromBuf(&ue_CapabilityRAT_Container.ueCapabilityRAT_Container, - (const char *)UE_rrc_inst[ctxt_pP->module_id].UECapability, - UE_rrc_inst[ctxt_pP->module_id].UECapability_size); + (const char *)UE_rrc_inst[ctxt_pP->module_id].UECap->sdu, + UE_rrc_inst[ctxt_pP->module_id].UECap->sdu_size); // ue_CapabilityRAT_Container.ueCapabilityRAT_Container.buf = UE_rrc_inst[ue_mod_idP].UECapability; // ue_CapabilityRAT_Container.ueCapabilityRAT_Container.size = UE_rrc_inst[ue_mod_idP].UECapability_size; AssertFatal(UECapabilityEnquiry->criticalExtensions.present == LTE_UECapabilityEnquiry__criticalExtensions_PR_c1, diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index b36c2d676d..4abc712507 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -2562,6 +2562,9 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc, nr_pdcp_release_drb(rrc->ue_id, i); } } + // stop TUN threads and clean up SDAP entities + nr_sdap_delete_ue_entities(rrc->ue_id); + for (int i = 1; i < NR_NUM_SRB; i++) { if (rrc->Srb[i] != RB_NOT_PRESENT) { rrc->Srb[i] = RB_NOT_PRESENT; diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index 44b8fa2cb0..f6602c7597 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -121,8 +121,21 @@ static void *sdap_tun_read_thread(void *arg) while (!entity->stop_thread) { len = read(entity->pdusession_sock, &rx_buf, NL_MAX_PAYLOAD); if (len == -1) { - LOG_E(PDCP, "could not read(): errno %d %s\n", errno, strerror(errno)); - return NULL; + if (errno == EINTR) + continue; // interrupted system call + + if (errno == EBADF || errno == EINVAL) { + LOG_I(SDAP, "Socket closed, exiting TUN read thread for UE %ld, PDU session %d\n", entity->ue_id, entity->pdusession_id); + break; + } + + LOG_E(PDCP, "read() failed: errno %d (%s)\n", errno, strerror(errno)); + break; + } + + if (len == 0) { + LOG_W(SDAP, "TUN socket returned EOF - exiting thread\n"); + break; } LOG_D(SDAP, "read data of size %d\n", len); @@ -194,6 +207,10 @@ void remove_ip_if(nr_sdap_entity_t *entity) DevAssert(entity != NULL); // Stop the read thread entity->stop_thread = true; + + // Close the socket: read() will get EBADF and exit + close(entity->pdusession_sock); + int ret = pthread_join(entity->pdusession_thread, NULL); AssertFatal(ret == 0, "pthread_join() failed, errno: %d, %s\n", errno, strerror(errno)); // Bring down the IP interface @@ -205,5 +222,5 @@ void remove_ip_if(nr_sdap_entity_t *entity) } else { tun_generate_ue_ifname(ifname, entity->ue_id, entity->pdusession_id != default_pdu ? entity->pdusession_id : -1); } - tun_destroy(ifname, entity->pdusession_sock); + tun_destroy(ifname); } diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index d93a508c33..7f2f3dd07c 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -329,7 +329,7 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, LOG_D(SDAP, "RX Entity offset : %d\n", offset); if (len != size-offset) - LOG_E(SDAP, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__); + LOG_E(SDAP, "write failed to fd %d! errno = %s\n", entity->pdusession_sock, strerror(errno)); } } diff --git a/openair3/NAS/NR_UE/5GS/5GMM/IES/CMakeLists.txt b/openair3/NAS/NR_UE/5GS/5GMM/IES/CMakeLists.txt index 40bfcbabcd..a4bb516b89 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/IES/CMakeLists.txt +++ b/openair3/NAS/NR_UE/5GS/5GMM/IES/CMakeLists.txt @@ -11,3 +11,5 @@ target_link_libraries(fgs_5gmm_ies_lib PUBLIC nas_common_ies_lib PRIVATE log_headers ) + +target_include_directories(fgs_5gmm_ies_lib PRIVATE ../../) diff --git a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSMobileIdentity.h b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSMobileIdentity.h index b7e66019cc..4d6d4d7eed 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSMobileIdentity.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSMobileIdentity.h @@ -30,8 +30,8 @@ #include #include #include - #include "OctetString.h" +#include "fgs_nas_utils.h" #ifndef FGS_MOBILE_IDENTITY_H_ #define FGS_MOBILE_IDENTITY_H_ @@ -129,15 +129,22 @@ typedef struct { uint8_t macaddr; } Macaddr5GSMobileIdentity_t; +#define FOREACH_IDENTITY_TYPE(TYPE_DEF) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_NOIDENTITY, 0) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_SUCI, 1) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_5G_GUTI, 2) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_IMEI, 3) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_5GS_TMSI, 4) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_IMEISV, 5) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_MAC_ADDR, 6) \ + TYPE_DEF(FGS_MOBILE_IDENTITY_EUI64, 7) + +typedef enum { FOREACH_IDENTITY_TYPE(TO_ENUM) } fgs_identity_type_t; + +static const text_info_t fgs_identity_type_text[] = {FOREACH_IDENTITY_TYPE(TO_TEXT)}; + // clang-format off typedef union FGSMobileIdentity_tag { -#define FGS_MOBILE_IDENTITY_NOIDENTITY 0b000 -#define FGS_MOBILE_IDENTITY_SUCI 0b001 -#define FGS_MOBILE_IDENTITY_5G_GUTI 0b010 -#define FGS_MOBILE_IDENTITY_IMEI 0b011 -#define FGS_MOBILE_IDENTITY_5GS_TMSI 0b100 -#define FGS_MOBILE_IDENTITY_IMEISV 0b101 -#define FGS_MOBILE_IDENTITY_MAC_ADDR 0b110 NoIdentity5GSMobileIdentity_t noidentity; Suci5GSMobileIdentity_t suci; Guti5GSMobileIdentity_t guti; diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt index 3b51ec748c..bf47e478b9 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(fgs_5gmm_lib OBJECT fgmm_service_reject.c RegistrationRequest.c RegistrationAccept.c + fgmm_identity_request.c FGSIdentityResponse.c FGSAuthenticationResponse.c FGSNASSecurityModeComplete.c diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_identity_request.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_identity_request.c new file mode 100644 index 0000000000..b41035f6ea --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_identity_request.c @@ -0,0 +1,37 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "fgmm_identity_request.h" +#include +#include "FGSMobileIdentity.h" + +int decode_fgs_identity_request(fgs_identity_request_msg_t *msg, const byte_array_t *buffer) +{ + if (buffer->len < 1) { + PRINT_NAS_ERROR("Failed to decode NAS Identity Request: invalid buffer length %ld\n", buffer->len); + return -1; + } + + // Bits 3-1 (least significant 3 bits) + msg->fgsmobileidentity = buffer->buf[0] & 0x07; + + return 1; +} diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_identity_request.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_identity_request.h new file mode 100644 index 0000000000..104f133ed1 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_identity_request.h @@ -0,0 +1,36 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef FGS_IDENTITY_REQUEST_H +#define FGS_IDENTITY_REQUEST_H + +#include +#include +#include "common/utils/ds/byte_array.h" +#include "FGSMobileIdentity.h" + +typedef struct { + fgs_identity_type_t fgsmobileidentity; +} fgs_identity_request_msg_t; + +int decode_fgs_identity_request(fgs_identity_request_msg_t *msg, const byte_array_t *buffer); + +#endif /* FGS_IDENTITY_REQUEST_H */ diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index b1378c3360..d152a5d4a3 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -50,6 +50,7 @@ #include "SORTransparentContainer.h" #include "FGSIdentityResponse.h" #include "fgmm_authentication_request.h" +#include "fgmm_identity_request.h" #include "T.h" #include "aka_functions.h" #include "assertions.h" @@ -69,6 +70,7 @@ #include "fgs_nas_utils.h" #include "fgmm_service_accept.h" #include "fgmm_service_reject.h" +#include "ds/byte_array.h" #define MAX_NAS_UE 4 @@ -76,10 +78,10 @@ static nr_ue_nas_t nr_ue_nas[MAX_NAS_UE] = {0}; #define FOREACH_STATE(TYPE_DEF) \ TYPE_DEF(NAS_SECURITY_NO_SECURITY_CONTEXT, 0) \ - TYPE_DEF(NAS_SECURITY_NEW_SECURITY_CONTEXT, 1) \ - TYPE_DEF(NAS_SECURITY_UNPROTECTED, 2) \ - TYPE_DEF(NAS_SECURITY_INTEGRITY_FAILED, 3) \ - TYPE_DEF(NAS_SECURITY_INTEGRITY_PASSED, 4) \ + TYPE_DEF(NAS_SECURITY_UNPROTECTED, 1) \ + TYPE_DEF(NAS_SECURITY_INTEGRITY_PASSED, 2) \ + TYPE_DEF(NAS_SECURITY_NEW_SECURITY_CONTEXT, 3) \ + TYPE_DEF(NAS_SECURITY_INTEGRITY_FAILED, 4) \ TYPE_DEF(NAS_SECURITY_BAD_INPUT, 5) const char *nr_release_cause_desc[] = {"RRC_CONNECTION_FAILURE", "RRC_RESUME_FAILURE", "OTHER"}; @@ -129,34 +131,69 @@ static const char *print_info(uint8_t id, const text_info_t *array, uint8_t arra return "N/A"; } -static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, uint8_t *pdu_buffer, int pdu_length) +/** @brief Check whether a message belongs to the list of messages that + * are allowed to be integrity unprotected (4.4.4.2 3GPP TS 24.501) */ +static bool unprotected_allowed(byte_array_t buffer, fgs_nas_msg_t msg_type) +{ + switch (msg_type) { + case FGS_IDENTITY_REQUEST: // check on SUCI done in the handler + case FGS_AUTHENTICATION_REQUEST: + case FGS_AUTHENTICATION_RESULT: + case FGS_AUTHENTICATION_REJECT: + case FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING: // for non switch off: deregistration type IE set to NORMAL_DEREGISTRATION + return true; + case FGS_REGISTRATION_REJECT: + case FGS_SERVICE_REJECT: + // unprotected if the 5GMM cause is not #76 + return buffer.buf[4] != Not_authorized_for_this_CAG_or_authorized_for_CAG_cells_only; + default: + return false; + } +} + +static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, byte_array_t buffer, fgs_nas_msg_t msg_type) { if (nas->security_container == NULL) return NAS_SECURITY_NO_SECURITY_CONTEXT; - /* header is 7 bytes, require at least one byte of payload */ - if (pdu_length < 8) - return NAS_SECURITY_BAD_INPUT; - switch (pdu_buffer[1]) { + if (buffer.len < sizeof(fgmm_msg_header_t)) { + LOG_E(NAS, "Invalid buffer length = %ld: must hold at least a plain 5GMM header\n", buffer.len); + return NAS_SECURITY_BAD_INPUT; + } + int security_type = buffer.buf[1]; + LOG_D(NAS, "Security type is: %s\n", print_info(security_type, security_header_type_s, sizeofArray(security_header_type_s))); + + switch (security_type) { case PLAIN_5GS_MSG: - return NAS_SECURITY_UNPROTECTED; + return unprotected_allowed(buffer, msg_type) ? NAS_SECURITY_UNPROTECTED : NAS_SECURITY_BAD_INPUT; + break; + case INTEGRITY_PROTECTED_WITH_NEW_SECU_CTX: + stream_security_container_delete(nas->security_container); + nas->security_container = NULL; + nas->security.nas_count_dl = 0; + return NAS_SECURITY_NO_SECURITY_CONTEXT; break; case INTEGRITY_PROTECTED: - case INTEGRITY_PROTECTED_WITH_NEW_SECU_CTX: case INTEGRITY_PROTECTED_AND_CIPHERED_WITH_NEW_SECU_CTX: /* only accept "integrity protected and ciphered" messages */ - if (pdu_buffer[6] == 0) - LOG_E(NAS, "Received nas_count_dl = %d\n", pdu_buffer[6]); - LOG_E(NAS, "todo: unhandled security type %s (pdu_buffer[1] = %d)\n", security_header_type_s[pdu_buffer[1]].text, pdu_buffer[1]); + if (buffer.buf[6] == 0) + LOG_E(NAS, "Received nas_count_dl = %d\n", buffer.buf[6]); + LOG_E(NAS, "todo: unhandled security type %s (buffer.buf[1] = %d)\n", security_header_type_s[buffer.buf[1]].text, buffer.buf[1]); return NAS_SECURITY_BAD_INPUT; break; default: break; } + /* header is 7 bytes, require at least one byte of payload */ + if (buffer.len < 8) { + LOG_E(NAS, "Invalid buffer length = %ld\n", buffer.len); + return NAS_SECURITY_BAD_INPUT; + } + /* synchronize NAS SQN, based on 24.501 4.4.3.1 */ // Sequence number - int nas_sqn = pdu_buffer[6]; + int nas_sqn = buffer.buf[6]; int target_sqn = nas->security.nas_count_dl & 0xff; if (nas_sqn != target_sqn) { if (nas_sqn < target_sqn) @@ -178,28 +215,28 @@ static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, uint8_t *pdu_b stream_cipher.count = nas->security.nas_count_dl; stream_cipher.bearer = 1; /* todo: don't hardcode */ stream_cipher.direction = 1; - stream_cipher.message = pdu_buffer + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH - 1; + stream_cipher.message = buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH - 1; /* length in bits */ - stream_cipher.blength = (pdu_length - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH + 1) << 3; + stream_cipher.blength = (buffer.len - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH + 1) << 3; stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, computed_mac); - uint8_t *received_mac = pdu_buffer + 2; + uint8_t *received_mac = buffer.buf + 2; if (memcmp(received_mac, computed_mac, NAS_INTEGRITY_SIZE) != 0) return NAS_SECURITY_INTEGRITY_FAILED; /* decipher */ - uint8_t payload_len = pdu_length - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; + uint8_t payload_len = buffer.len - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; uint8_t buf[payload_len]; stream_cipher.context = nas->security_container->ciphering_context; stream_cipher.count = nas->security.nas_count_dl; stream_cipher.bearer = 1; /* todo: don't hardcode */ stream_cipher.direction = 1; - stream_cipher.message = pdu_buffer + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; + stream_cipher.message = buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; /* length in bits */ stream_cipher.blength = (payload_len) << 3; stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); - memcpy(pdu_buffer + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH, buf, payload_len); + memcpy(buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH, buf, payload_len); nas->security.nas_count_dl++; @@ -473,7 +510,7 @@ static void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) derive_kausf(ck, ik, sqn, kausf, nas->uicc); derive_kseaf(kausf, kseaf, nas->uicc); derive_kamf(kseaf, kamf, 0x0000, nas->uicc); - derive_kgnb(kamf, 0, kgnb); + derive_kgnb(kamf, nas->security.nas_count_ul, kgnb); printf("kausf:"); for (int i = 0; i < 32; i++) { @@ -792,7 +829,7 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) } } -void generateIdentityResponse(as_nas_info_t *initialNasMsg, uint8_t identitytype, uicc_t *uicc) +static void generateIdentityResponse(as_nas_info_t *initialNasMsg, const uint8_t identitytype, uicc_t *uicc) { int size = sizeof(fgmm_msg_header_t); fgmm_nas_message_plain_t plain = {0}; @@ -813,6 +850,39 @@ void generateIdentityResponse(as_nas_info_t *initialNasMsg, uint8_t identitytype initialNasMsg->length = mm_msg_encode(&plain, initialNasMsg->nas_data, size); } +static void handle_identity_request(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas, const byte_array_t buffer) +{ + fgmm_msg_header_t mm_header = {0}; + fgs_identity_request_msg_t msg = {0}; + + // Decode plain NAS header + int decoded = decode_5gmm_msg_header(&mm_header, buffer.buf, buffer.len); + if (decoded < 0) { + LOG_E(NAS, "Failed to decode NAS Identity Request header\n"); + return; + } + + byte_array_t payload = {.buf = &buffer.buf[decoded], .len = buffer.len - decoded}; + + // Decode identity request payload + if (decode_fgs_identity_request(&msg, &payload) < 0) { + LOG_E(NAS, "Failed to decode Identity Request message\n"); + return; + } + + LOG_I(NAS, + "Received IDENTITY REQUEST for identity type: %s\n", + print_info(msg.fgsmobileidentity, fgs_identity_type_text, sizeofArray(fgs_identity_type_text))); + + if (mm_header.message_type == NAS_SECURITY_UNPROTECTED && msg.fgsmobileidentity != FGS_MOBILE_IDENTITY_SUCI) { + // see 3GPP TS 24.501 4.4.4.2 + LOG_E(NAS, "Only SUCI mobile identity is expected in a security-unprotected request\n"); + return; + } + + generateIdentityResponse(initialNasMsg, msg.fgsmobileidentity, nas->uicc); +} + static void generateAuthenticationResp(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *buf) { derive_ue_keys(buf, nas); @@ -1227,7 +1297,7 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi // Plain 5GMM fgs_deregistration_request_ue_originating_msg *mm_msg = &sp_msg.plain.mm_msg.fgs_deregistration_request_ue_originating; - mm_msg->deregistrationtype.switchoff = NORMAL_DEREGISTRATION; + mm_msg->deregistrationtype.switchoff = NORMAL_DEREGISTRATION; // note: in case this is changed to SWITCH_OFF, handle in unprotected_allowed mm_msg->deregistrationtype.reregistration_required = REREGISTRATION_NOT_REQUIRED; mm_msg->deregistrationtype.access_type = TGPP_ACCESS; mm_msg->naskeysetidentifier.naskeysetidentifier = 1; @@ -1381,7 +1451,7 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * } } -static uint8_t get_msg_type(uint8_t *pdu_buffer, uint32_t length) +static fgs_nas_msg_t get_msg_type(uint8_t *pdu_buffer, uint32_t length) { if (pdu_buffer == NULL) goto error; @@ -1676,25 +1746,22 @@ void *nas_nrue(void *args_p) NAS_CONN_ESTABLI_CNF(msg_p).errCode, NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); - uint8_t *pdu_buffer = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.nas_data; - int pdu_length = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length; - - security_state_t security_state = nas_security_rx_process(nas, pdu_buffer, pdu_length); - if (security_state != NAS_SECURITY_INTEGRITY_PASSED && security_state != NAS_SECURITY_NO_SECURITY_CONTEXT) { + byte_array_t ba = {.buf = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.nas_data, .len = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length}; + fgs_nas_msg_t msg_type = get_msg_type(ba.buf, ba.len); + security_state_t security_state = nas_security_rx_process(nas, ba, msg_type); + if (security_state > NAS_SECURITY_INTEGRITY_PASSED) { LOG_E(NAS, "NAS integrity failed, discard incoming message: security state is %s\n", security_state_info[security_state].text); break; } - int msg_type = get_msg_type(pdu_buffer, pdu_length); - if (msg_type == FGS_REGISTRATION_ACCEPT) { - handle_registration_accept(nas, pdu_buffer, pdu_length); + handle_registration_accept(nas, ba.buf, ba.len); } else if (msg_type == FGS_PDU_SESSION_ESTABLISHMENT_ACC) { - handle_pdu_session_accept(pdu_buffer, pdu_length, nas->UE_id); + handle_pdu_session_accept(ba.buf, ba.len, nas->UE_id); } // Free NAS buffer memory after use (coming from RRC) - free(pdu_buffer); + free_byte_array(ba); break; } @@ -1744,40 +1811,31 @@ void *nas_nrue(void *args_p) } break; case NAS_DOWNLINK_DATA_IND: { - LOG_I(NAS, - "[UE %ld] Received %s: length %u , buffer %p\n", - nas->UE_id, - ITTI_MSG_NAME(msg_p), - NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length, - NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.nas_data); as_nas_info_t initialNasMsg = {0}; - uint8_t *pdu_buffer = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.nas_data; int pdu_length = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length; - int msg_type = get_msg_type(pdu_buffer, pdu_length); - - security_state_t security_state = nas_security_rx_process(nas, pdu_buffer, pdu_length); - /* special cases accepted without protection */ - if (security_state == NAS_SECURITY_UNPROTECTED) { - /* for the moment, only FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING is accepted */ - if (msg_type == FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING) - security_state = NAS_SECURITY_INTEGRITY_PASSED; - } - - if (security_state != NAS_SECURITY_INTEGRITY_PASSED && security_state != NAS_SECURITY_NO_SECURITY_CONTEXT) { + byte_array_t buffer = {.buf = pdu_buffer, .len = pdu_length}; + fgs_nas_msg_t msg_type = get_msg_type(pdu_buffer, pdu_length); + security_state_t security_state = nas_security_rx_process(nas, buffer, msg_type); + if (security_state > NAS_SECURITY_INTEGRITY_PASSED) { LOG_E(NAS, "NAS integrity failed, discard incoming message\n"); break; } + LOG_I(NAS, + "[UE %ld] Received %s type %s with length %u\n", + nas->UE_id, + ITTI_MSG_NAME(msg_p), + print_info(msg_type, message_text_info, sizeofArray(message_text_info)), + NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length); + switch (msg_type) { case FGS_IDENTITY_REQUEST: - generateIdentityResponse(&initialNasMsg, *(pdu_buffer + 3), nas->uicc); + handle_identity_request(&initialNasMsg, nas, buffer); break; - case FGS_AUTHENTICATION_REQUEST: { - byte_array_t buffer = {.buf = pdu_buffer, .len = pdu_length}; + case FGS_AUTHENTICATION_REQUEST: handle_fgmm_authentication_request(nas, &initialNasMsg, &buffer); break; - } case FGS_SECURITY_MODE_COMMAND: handle_security_mode_command(nas, &initialNasMsg, pdu_buffer, pdu_length); break; @@ -1803,7 +1861,6 @@ void *nas_nrue(void *args_p) exit(1); break; case FGS_SERVICE_ACCEPT: { - byte_array_t buffer = {.buf = pdu_buffer, .len = pdu_length}; handle_service_accept(nas, &buffer); break; } diff --git a/radio/rfsimulator/CMakeLists.txt b/radio/rfsimulator/CMakeLists.txt index 61b053a1ce..efba710bad 100644 --- a/radio/rfsimulator/CMakeLists.txt +++ b/radio/rfsimulator/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(rfsimulator MODULE apply_channelmod.c ../../openair1/PHY/TOOLS/signal_energy.c ) -target_link_libraries(rfsimulator PRIVATE SIMU HASHTABLE) +target_link_libraries(rfsimulator PRIVATE SIMU) set_target_properties(rfsimulator PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) add_executable(replay_node stored_node.c) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 8070bf837e..32648758c8 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -50,7 +50,6 @@ #define CHANNELMOD_DYNAMICLOAD #include #include "rfsimulator.h" -#include "hashtable.h" #define PORT 4043 //default TCP port for this simulator // @@ -70,22 +69,6 @@ #define byteToSample(a,b) ((a)/(sizeof(sample_t)*(b))) #define GENERATE_CHANNEL 10 // each frame (or slot?) in DL - -// This needs to be re-architected in the future -// -// File Descriptors management in rfsimulator is not optimized -// Relying on FD_SETSIZE (actually 1024) is not appropriated -// Also the use of fd value as returned by Linux as an index for buf[] structure is not appropriated -// especially for client (UE) side since only 1 fd per connection to a gNB is needed. On the server -// side the value should be tuned to the maximum number of connections with UE's which corresponds -// to the maximum number of UEs hosted by a gNB which is unlikely to be in the order of thousands -// since all I/Q's would flow through the same TCP transport. -// Until a convenient management is implemented, the MAX_FD_RFSIMU is used everywhere (instead of -// FD_SETSIE) and reduced to 125. This should allow for around 20 simultaeous UEs. -// -// An indirection level via hashtable was added to allow the software to use FDs above MAX_FD_RFSIMU. -// -// #define MAX_FD_RFSIMU FD_SETSIZE #define MAX_FD_RFSIMU 250 #define SEND_BUFF_SIZE 100000000 // Socket buffer size @@ -169,8 +152,6 @@ typedef struct { int saveIQfile; buffer_t buf[MAX_FD_RFSIMU]; int next_buf; - // Hashtable used as an indirection level between file descriptor and the buf array - hash_table_t *fd_to_buf_map; int rx_num_channels; int tx_num_channels; double sample_rate; @@ -187,42 +168,14 @@ typedef struct { int hanging_workaround; } rfsimulator_state_t; - -static buffer_t *get_buff_from_socket(rfsimulator_state_t *simulator_state, int socket) +static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) { - uint64_t buffer_index; - if (hashtable_get(simulator_state->fd_to_buf_map, socket, (void **)&buffer_index) == HASH_TABLE_OK) { - return &simulator_state->buf[buffer_index]; - } else { - return NULL; - } -} - -static void add_buff_to_socket_mapping(rfsimulator_state_t *simulator_state, int socket, uint64_t buff_index) -{ - hashtable_rc_t rc = hashtable_insert(simulator_state->fd_to_buf_map, socket, (void *)buff_index); - AssertFatal(rc == HASH_TABLE_OK, - "%s sock = %d\n", - rc == HASH_TABLE_INSERT_OVERWRITTEN_DATA ? "Duplicate entry in hashtable" : "Hashtable is not allocated", - socket); -} - -static void remove_buff_to_socket_mapping(rfsimulator_state_t *simulator_state, int socket) -{ - // Failure is fine here - hashtable_remove(simulator_state->fd_to_buf_map, socket); -} - -static int allocCirBuf(rfsimulator_state_t *bridge, int sock) -{ - /* TODO: cleanup code so that this AssertFatal becomes useless */ - AssertFatal(sock >= 0 && sock < sizeofArray(bridge->buf), "socket %d is not in range\n", sock); uint64_t buff_index = bridge->next_buf++ % MAX_FD_RFSIMU; buffer_t *ptr=&bridge->buf[buff_index]; ptr->circularBuf = calloc(1, sampleToByte(CirSize, 1)); if (ptr->circularBuf == NULL) { LOG_E(HW, "malloc(%lu) failed\n", sampleToByte(CirSize, 1)); - return -1; + return NULL; } ptr->circularBufEnd=((char *)ptr->circularBuf)+sampleToByte(CirSize,1); ptr->conn_sock=sock; @@ -234,14 +187,14 @@ static int allocCirBuf(rfsimulator_state_t *bridge, int sock) int sendbuff = SEND_BUFF_SIZE; if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)) != 0) { LOG_E(HW, "setsockopt(SO_SNDBUF) failed\n"); - return -1; + return NULL; } struct epoll_event ev= {0}; ev.events = EPOLLIN | EPOLLRDHUP; - ev.data.fd = sock; + ev.data.ptr = ptr; if (epoll_ctl(bridge->epollfd, EPOLL_CTL_ADD, sock, &ev) != 0) { LOG_E(HW, "epoll_ctl(EPOLL_CTL_ADD) failed\n"); - return -1; + return NULL; } if ( bridge->channelmod > 0) { @@ -269,7 +222,7 @@ static int allocCirBuf(rfsimulator_state_t *bridge, int sock) ptr->channel_model = find_channel_desc_fromname(legacy_model_name); if (!ptr->channel_model) { LOG_E(HW, "Channel model %s/%s not found, check config file\n", modelname, legacy_model_name); - return -1; + return NULL; } } @@ -278,34 +231,29 @@ static int allocCirBuf(rfsimulator_state_t *bridge, int sock) random_channel(ptr->channel_model,false); LOG_I(HW, "Random channel %s in rfsimulator activated\n", modelname); } - add_buff_to_socket_mapping(bridge, sock, buff_index); - return 0; + return ptr; } -static void removeCirBuf(rfsimulator_state_t *bridge, int sock) { - if (epoll_ctl(bridge->epollfd, EPOLL_CTL_DEL, sock, NULL) != 0) { +static void removeCirBuf(rfsimulator_state_t *bridge, buffer_t *buf) +{ + if (epoll_ctl(bridge->epollfd, EPOLL_CTL_DEL, buf->conn_sock, NULL) != 0) { LOG_E(HW, "epoll_ctl(EPOLL_CTL_DEL) failed\n"); } - close(sock); - buffer_t* buf = get_buff_from_socket(bridge, sock); - if (buf) { - free(buf->circularBuf); - // Fixme: no free_channel_desc_scm(bridge->buf[sock].channel_model) implemented - // a lot of mem leaks - //free(bridge->buf[sock].channel_model); - memset(buf, 0, sizeof(buffer_t)); - buf->conn_sock=-1; - remove_buff_to_socket_mapping(bridge, sock); - nb_ue--; - } + close(buf->conn_sock); + free(buf->circularBuf); + // Fixme: no free_channel_desc_scm(bridge->buf[sock].channel_model) implemented + // a lot of mem leaks + // free(bridge->buf[sock].channel_model); + memset(buf, 0, sizeof(buffer_t)); + buf->conn_sock = -1; + nb_ue--; } -static void socketError(rfsimulator_state_t *bridge, int sock) { - buffer_t* buf = get_buff_from_socket(bridge, sock); - if (!buf) return; +static void socketError(rfsimulator_state_t *bridge, buffer_t *buf) +{ if (buf->conn_sock != -1) { LOG_W(HW, "Lost socket\n"); - removeCirBuf(bridge, sock); + removeCirBuf(bridge, buf); if (bridge->role == SIMU_ROLE_CLIENT) exit(1); @@ -661,7 +609,7 @@ static int startServer(openair0_device *device) } struct epoll_event ev = {0}; ev.events = EPOLLIN; - ev.data.fd = t->listen_sock; + ev.data.ptr = NULL; if (epoll_ctl(t->epollfd, EPOLL_CTL_ADD, t->listen_sock, &ev) != 0) { LOG_E(HW, "epoll_ctl(EPOLL_CTL_ADD) failed, errno(%d)\n", errno); return -1; @@ -730,7 +678,7 @@ static int startClient(openair0_device *device) if (setblocking(sock, notBlocking) == -1) { return -1; } - return allocCirBuf(t, sock); + return allocCirBuf(t, sock) == NULL ? -1 : 0; } static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags, bool alreadyLocked) { @@ -815,9 +763,9 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi } for (int nbEv = 0; nbEv < nfds; ++nbEv) { - int fd=events[nbEv].data.fd; + buffer_t *b = events[nbEv].data.ptr; - if (events[nbEv].events & EPOLLIN && fd == t->listen_sock) { + if (events[nbEv].events & EPOLLIN && b == NULL) { int conn_sock; conn_sock = accept(t->listen_sock, NULL, NULL); if (conn_sock == -1) { @@ -827,7 +775,8 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi if (setblocking(conn_sock, notBlocking)) { return false; } - if (allocCirBuf(t, conn_sock) == -1) { + buffer_t *new_buf = allocCirBuf(t, conn_sock); + if (new_buf == NULL) { return false; } LOG_I(HW, "A client connects, sending the current time\n"); @@ -840,17 +789,13 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi rfsimulator_write_internal(t, t->lastWroteTS > 1 ? t->lastWroteTS - 1 : 0, samplesVoid, 1, t->tx_num_channels, 1, false); - buffer_t *b = get_buff_from_socket(t, conn_sock); - if (b->channel_model) - b->channel_model->start_TS = t->lastWroteTS; + if (new_buf->channel_model) + new_buf->channel_model->start_TS = t->lastWroteTS; } else { if ( events[nbEv].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP) ) { - socketError(t,fd); + socketError(t, b); continue; } - - buffer_t *b = get_buff_from_socket(t, fd); - if (!b) continue; if ( b->circularBuf == NULL ) { LOG_E(HW, "Received data on not connected socket %d\n", events[nbEv].data.fd); continue; @@ -865,7 +810,7 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi b->remainToTransfer : b->circularBufEnd - b->transferPtr ; - ssize_t sz=recv(fd, b->transferPtr, blockSz, MSG_DONTWAIT); + ssize_t sz = recv(b->conn_sock, b->transferPtr, blockSz, MSG_DONTWAIT); if ( sz < 0 ) { if ( errno != EAGAIN ) { @@ -925,7 +870,7 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi pthread_mutex_lock(&Sockmutex); if (t->lastWroteTS != 0 && (fabs((double)t->lastWroteTS-b->lastReceivedTS) > (double)CirSize)) - LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", fd, t->lastWroteTS, b->lastReceivedTS); + LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", b->conn_sock, t->lastWroteTS, b->lastReceivedTS); pthread_mutex_unlock(&Sockmutex); b->transferPtr=(char *)&b->circularBuf[(b->lastReceivedTS*b->th.nbAnt)%CirSize]; @@ -935,11 +880,11 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi if ( b->headerMode==false ) { if ( ! b->trashingPacket ) { b->lastReceivedTS=b->th.timestamp+b->th.size-byteToSample(b->remainToTransfer,b->th.nbAnt); - LOG_D(HW, "UEsock: %d Set b->lastReceivedTS %ld\n", fd, b->lastReceivedTS); + LOG_D(HW, "UEsock: %d Set b->lastReceivedTS %ld\n", b->conn_sock, b->lastReceivedTS); } if ( b->remainToTransfer==0) { - LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", fd, b->lastReceivedTS); + LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", b->conn_sock, b->lastReceivedTS); b->headerMode=true; b->transferPtr=(char *)&b->th; b->remainToTransfer = sizeof(samplesBlockHeader_t); @@ -1142,10 +1087,9 @@ static void rfsimulator_end(openair0_device *device) { for (int i = 0; i < MAX_FD_RFSIMU; i++) { buffer_t *b = &s->buf[i]; if (b->conn_sock >= 0 ) - removeCirBuf(s, b->conn_sock); + removeCirBuf(s, b); } close(s->epollfd); - hashtable_destroy(&s->fd_to_buf_map); free(s); } static void stopServer(openair0_device *device) @@ -1171,11 +1115,6 @@ static int rfsimulator_write_init(openair0_device *device) { return 0; } -void do_not_free_integer(void *integer) -{ - (void)integer; -} - __attribute__((__visibility__("default"))) int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { // to change the log level, use this on command line @@ -1222,7 +1161,6 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { for (int i = 0; i < MAX_FD_RFSIMU; i++) rfsimulator->buf[i].conn_sock=-1; rfsimulator->next_buf = 0; - rfsimulator->fd_to_buf_map = hashtable_create(MAX_FD_RFSIMU, NULL, do_not_free_integer); AssertFatal((rfsimulator->epollfd = epoll_create1(0)) != -1, "epoll_create1() failed, errno(%d)", errno); // we need to call randominit() for telnet server (use gaussdouble=>uniformrand)