From 48f9d8a56dc16f72f2cc5c1c1d9ebe6127d4aee1 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 20 Oct 2025 11:42:53 +0200 Subject: [PATCH 01/20] Clean up: header not required --- openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.c | 1 - openair3/NAS/NR_UE/nr_nas_msg.c | 1 - 2 files changed, 2 deletions(-) diff --git a/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.c b/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.c index cb12e5dc6c..f59111cf43 100644 --- a/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.c +++ b/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.c @@ -23,7 +23,6 @@ #include #include "PduSessionEstablishmentAccept.h" #include "common/utils/LOG/log.h" -#include "common/utils/tun_if.h" #include "fgs_nas_utils.h" /** diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 5a58af6b84..e1619382f4 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -56,7 +56,6 @@ #include "aka_functions.h" #include "assertions.h" #include "common/utils/ds/byte_array.h" -#include "common/utils/tun_if.h" #include "commonDef.h" #include "intertask_interface.h" #include "kdf.h" From 53095c72391e28216d355d06ef08f4f3630c9241 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 15 Nov 2025 10:50:09 +0100 Subject: [PATCH 02/20] Remove unnecessary header function declaration --- openair2/SDAP/nr_sdap/nr_sdap.c | 2 +- openair2/SDAP/nr_sdap/nr_sdap.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index 1aec02814e..7062895b69 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -170,7 +170,7 @@ void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id) threadCreate(&entity->pdusession_thread, sdap_tun_read_thread, entity, "gnb_tun_read_thread", -1, OAI_PRIORITY_RT_LOW); } -void start_sdap_tun_ue(ue_id_t ue_id, int pdu_session_id, int sock) +static void start_sdap_tun_ue(ue_id_t ue_id, int pdu_session_id, int sock) { nr_sdap_entity_t *entity = nr_sdap_get_entity(ue_id, pdu_session_id); DevAssert(entity != NULL); diff --git a/openair2/SDAP/nr_sdap/nr_sdap.h b/openair2/SDAP/nr_sdap/nr_sdap.h index 1164641970..c681c34ae2 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.h +++ b/openair2/SDAP/nr_sdap/nr_sdap.h @@ -60,7 +60,6 @@ void sdap_data_ind(int pdcp_entity, char *buf, int size); -void start_sdap_tun_ue(ue_id_t ue_id, int pdu_session_id, int sock); void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id); void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id); From 345d5315e5f8bb680bd545a4bd364059c319b335 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 20 Jan 2026 09:32:56 +0100 Subject: [PATCH 03/20] config_module: make pointers const --- common/config/config_load_configmodule.c | 2 +- common/config/config_load_configmodule.h | 2 +- common/config/config_userapi.c | 2 +- common/config/config_userapi.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/config/config_load_configmodule.c b/common/config/config_load_configmodule.c index 5d8bb2cfcc..da589d3759 100644 --- a/common/config/config_load_configmodule.c +++ b/common/config/config_load_configmodule.c @@ -144,7 +144,7 @@ int config_cmdlineonly_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, paramdef_t *params, int numparams, - char *prefix) + const char *prefix) { ParamList->numelt = 0; return 0; diff --git a/common/config/config_load_configmodule.h b/common/config/config_load_configmodule.h index 2c45f788d4..93753ef358 100644 --- a/common/config/config_load_configmodule.h +++ b/common/config/config_load_configmodule.h @@ -61,7 +61,7 @@ struct configmodule_interface; typedef int (*configmodule_getfunc_t)(struct configmodule_interface *, paramdef_t *, int numparams, const char *prefix); typedef int ( - *configmodule_getlistfunc_t)(struct configmodule_interface *, paramlist_def_t *, paramdef_t *, int numparams, char *prefix); + *configmodule_getlistfunc_t)(struct configmodule_interface *, paramlist_def_t *, paramdef_t *, int numparams, const char *prefix); typedef int (*configmodule_setfunc_t)(paramdef_t *cfgoptions, int numoptions, const char *prefix); typedef void (*configmodule_endfunc_t)(struct configmodule_interface *cfg); diff --git a/common/config/config_userapi.c b/common/config/config_userapi.c index 7c92a611de..271b9ba0a9 100644 --- a/common/config/config_userapi.c +++ b/common/config/config_userapi.c @@ -142,7 +142,7 @@ int config_get(configmodule_interface_t *cfgif, paramdef_t *params, int numparam return ret; } -int config_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, paramdef_t *params, int numparams, char *prefix) +int config_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, paramdef_t *params, int numparams, const char *prefix) { if (CONFIG_ISFLAGSET(CONFIG_ABORT)) { fprintf(stderr,"[CONFIG] config_get skipped, config module not properly initialized\n"); diff --git a/common/config/config_userapi.h b/common/config/config_userapi.h index 3aeeeec3c1..57d738ae83 100644 --- a/common/config/config_userapi.h +++ b/common/config/config_userapi.h @@ -62,7 +62,7 @@ int config_assign_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions #define CONFIG_CHECKALLSECTIONS "ALLSECTIONS" int config_check_unknown_cmdlineopt(configmodule_interface_t *cfg, char *prefix); int config_get(configmodule_interface_t *cfg, paramdef_t *params, int numparams, const char *prefix); -int config_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, paramdef_t *params, int numparams, char *prefix); +int config_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, paramdef_t *params, int numparams, const char *prefix); /* apis to set some of the paramdef_t fields before using the get/getlist api's */ void config_set_checkfunctions(paramdef_t *params, checkedparam_t *checkfunctions, int numparams); From 42fd19dc5c21e6e277441ad543621f1c53775e3b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 7 Jan 2026 16:46:31 +0100 Subject: [PATCH 04/20] Bugfix: Abort processing on PDU session establishment accept decode failure --- openair3/NAS/NR_UE/nr_nas_msg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index e1619382f4..98efd8b47e 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1544,8 +1544,10 @@ static void handle_pdu_session_accept(uint8_t *pdu_buffer, uint32_t msg_length, size += decoded; // decode PDU Session Establishment Accept - if (!decode_pdu_session_establishment_accept_msg(&msg, pdu_buffer + size, msg_length)) + if (!decode_pdu_session_establishment_accept_msg(&msg, pdu_buffer + size, msg_length)) { LOG_E(NAS, "decode_pdu_session_establishment_accept_msg failure\n"); + return; + } // process PDU Session if (msg.pdu_addr_ie.pdu_length) From e9ac0c70c810e09451c866d0ddd0be13b7492831 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 21 Oct 2025 19:08:10 +0200 Subject: [PATCH 05/20] Separate change_interface_state() into get/set_if_flags() For configuring a TAP interface, we need to set different options, which is not easily feasible with the current change_interface_state() API (or would make it more complex). To simplify while allowing to set flags more flexibly, separate into two functions get/set_if_flags(). --- common/utils/tun_if.c | 70 ++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/common/utils/tun_if.c b/common/utils/tun_if.c index f74ab3c7f2..c2b21c02b2 100644 --- a/common/utils/tun_if.c +++ b/common/utils/tun_if.c @@ -158,42 +158,47 @@ static bool setInterfaceParameter(int sock_fd, const char *ifn, int af, const ch } -typedef enum { INTERFACE_DOWN, INTERFACE_UP } if_action_t; /* -* \brief bring interface up (up != 0) or down (up == 0) +* \brief Get an interface's flags. +* \param[in] sock_fd socket to use for getting the flags +* \param[in] ifn the interface name for which to get flags +* \param[out] flags the flags of the interface +* \return true on success, false otherwise */ -static bool change_interface_state(int sock_fd, const char *ifn, if_action_t if_action) +static bool get_if_flags(int sock_fd, const char *ifn, short *flags) { - const char *action = if_action == INTERFACE_DOWN ? "DOWN" : "UP"; - struct ifreq ifr = {0}; strncpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name) - 1); /* get flags of this interface: see netdevice(7) */ - bool success = ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t)&ifr) == 0; - if (!success && if_action == INTERFACE_DOWN && errno == ENODEV) { - LOG_W(OIP, "trying to remove non-existant device %s\n", ifn); - return true; + int success = ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t)&ifr) == 0; + if (!success) { + LOG_W(OIP, "On interface %s: ioctl() failed: %d, %s\n", ifn, errno, strerror(errno)); + return false; } - 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; - ifr.ifr_flags &= ~IFF_MULTICAST; - } else { - ifr.ifr_flags &= ~IFF_UP; - } - - 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); - + *flags = ifr.ifr_flags; return true; +} -fail_interface_state: - LOG_E(OIP, "Bringing interface %s for %s: ioctl call failed: %d, %s\n", action, ifn, errno, strerror(errno)); - return false; +/* + * \brief Set an interface's flags. +* \param[in] sock_fd socket to use for setting the flags +* \param[in] ifn the interface name for which to set flags +* \param[in] flags the flags to set for the interface +* \return true on success, false otherwise + */ +static bool set_if_flags(int sock_fd, const char *ifn, short flags) +{ + struct ifreq ifr = {0}; + strncpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name) - 1); + ifr.ifr_flags = flags; + + int success = ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t)&ifr) == 0; + if (!success) { + LOG_W(OIP, "On interface %s: ioctl() failed: %d, %s\n", ifn, errno, strerror(errno)); + return false; + } + return true; } @@ -227,8 +232,10 @@ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6) close(sock_fd); } + // if successfully set IP addresses: set iterface up, disable ARP, no + // multicast, point-to-point if (success) - success = change_interface_state(sock_fd, ifname, INTERFACE_UP); + success = set_if_flags(sock_fd, ifname, (IFF_UP | IFF_NOARP | IFF_POINTOPOINT) & ~IFF_MULTICAST); if (success) LOG_I(OIP, "Interface %s successfully configured, IPv4 %s, IPv6 %s\n", ifname, ipv4, ipv6); @@ -283,11 +290,12 @@ void tun_destroy(const char *dev) return; } - bool success = change_interface_state(fd, dev, INTERFACE_DOWN); + // interface not up => down + short flags; + bool success = get_if_flags(fd, dev, &flags); + success = success && set_if_flags(fd, dev, flags & ~IFF_UP); if (success) { LOG_I(UTIL, "Interface %s is now down.\n", dev); - } else { - LOG_E(UTIL, "Could not bring interface %s down.\n", dev); - } + } // no else: get_if_flags()/set_if_flags() should have printed error close(fd); } From 2557df87f5114ae8bb93ab9d9373324650535931 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 19 Jan 2026 17:42:32 +0100 Subject: [PATCH 06/20] Hardcode (noS1) PDU session ID to 10 Defacto, the PDU session ID for noS1 modes is already hardcoded, as starting the nr-softmodem with --noS1 --default-pdu-id 1 leads to Assertion (entity != ((void *)0)) failed! In start_sdap_tun_gnb_first_ue_default_pdu_session() openair2/SDAP/nr_sdap/nr_sdap.c:163 Further, the --default-pdu-id parameter will be deactivated in a later commit, and we cannot rely on it. Therefore: - hardcode noS1 PDU ID (also used in --do-ra/--phy-test modes) to 10 and use in gNB&UE - pass the PDU session ID used in SDAP entity initialization down to start_sdap_tun_gnb_first_ue_default_pdu_session() to always use the correct one --- common/platform_constants.h | 3 +++ openair2/RRC/NR/rrc_gNB_nsa.c | 2 +- openair2/SDAP/nr_sdap/nr_sdap.c | 4 ++-- openair2/SDAP/nr_sdap/nr_sdap.h | 2 +- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 2 +- openair3/NAS/NR_UE/nr_nas_msg.c | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/common/platform_constants.h b/common/platform_constants.h index 2faadc3dcd..f76025c06a 100644 --- a/common/platform_constants.h +++ b/common/platform_constants.h @@ -100,5 +100,8 @@ // SDAP #define MAX_QOS_FLOWS 64 +// SDAP/5G NAS NOS1 +#define DEFAULT_NOS1_PDU_ID 10 + #define SPEED_OF_LIGHT 299792458 // in m/s #endif /* __PLATFORM_CONSTANTS_H__ */ diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 3e3c462145..8c24b8aedd 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -201,7 +201,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc, x2ap_ENDC_sgnb_addition_req_t *m, sctp_ byte_array_t cgci = {0}; if (get_softmodem_params()->phy_test == 1 || get_softmodem_params()->do_ra == 1) { DevAssert(m == NULL); - UE->rb_config = get_default_rbconfig(10 /* EPS bearer ID */, 1 /* drb ID */, NR_CipheringAlgorithm_nea0, NR_SecurityConfig__keyToUse_master, &rrc->pdcp_config); + UE->rb_config = get_default_rbconfig(DEFAULT_NOS1_PDU_ID, 1 /* drb ID */, NR_CipheringAlgorithm_nea0, NR_SecurityConfig__keyToUse_master, &rrc->pdcp_config); int len = cg_config_info_from_ue_cap_file(sizeof tmp, tmp); DevAssert(len > 0); cgci = create_byte_array(len, tmp); diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index 7062895b69..7315504328 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -157,9 +157,9 @@ static void *sdap_tun_read_thread(void *arg) return NULL; } -void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id) +void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id, int pdu_session_id) { - nr_sdap_entity_t *entity = nr_sdap_get_entity(ue_id, get_softmodem_params()->default_pdu_session_id); + nr_sdap_entity_t *entity = nr_sdap_get_entity(ue_id, pdu_session_id); DevAssert(entity != NULL); DevAssert(entity->is_gnb); char *ifprefix = get_softmodem_params()->nsa ? "oaitun_gnb" : "oaitun_enb"; diff --git a/openair2/SDAP/nr_sdap/nr_sdap.h b/openair2/SDAP/nr_sdap/nr_sdap.h index c681c34ae2..49527359b3 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.h +++ b/openair2/SDAP/nr_sdap/nr_sdap.h @@ -60,7 +60,7 @@ void sdap_data_ind(int pdcp_entity, char *buf, int size); -void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id); +void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id, int pdu_session_id); void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id); #endif diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index dc747e5a60..3a4cb12b03 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -534,7 +534,7 @@ static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap if (IS_SOFTMODEM_NOS1 && is_gnb) { // In NOS1 mode, terminate SDAP for the first UE on the gNB. This allows injecting/receiving // PDCP SDUs to/from the TUN interface. - start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id); + start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id, sdap_entity->pdusession_id); } } diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 98efd8b47e..06b75477b6 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -2160,7 +2160,7 @@ void *nas_nrue(void *args_p) } break; case NAS_INIT_NOS1_IF: { - const int pdu_session_id = get_softmodem_params()->default_pdu_session_id; + const int pdu_session_id = DEFAULT_NOS1_PDU_ID; const char *ip = "10.0.1.2"; const int qfi = 7; create_ue_ip_if(ip, NULL, nas->UE_id, pdu_session_id); From 7ede84cb1d1abc6b972548e0cf7fcccac0a2824b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 22 Oct 2025 08:52:03 +0200 Subject: [PATCH 07/20] SDAP entity: store interface name Simplify interface handling by storing the name: this allows to simplify to remove_ip_if(), which does not need to recreate a valid name, and will simplify handling of ethernet PDU sessions with a possibly different name. --- openair2/SDAP/nr_sdap/nr_sdap.c | 20 +++++++++----------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 1 + 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index 7315504328..98b1a48514 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -166,16 +166,18 @@ void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id, int pdu_sess char ifname[IFNAMSIZ]; tun_generate_ifname(ifname, ifprefix, ue_id - 1); entity->pdusession_sock = tun_alloc(ifname); + entity->pdusession_if_name = strdup(ifname); tun_config(ifname, "10.0.1.1", NULL); threadCreate(&entity->pdusession_thread, sdap_tun_read_thread, entity, "gnb_tun_read_thread", -1, OAI_PRIORITY_RT_LOW); } -static void start_sdap_tun_ue(ue_id_t ue_id, int pdu_session_id, int sock) +static void start_sdap_tun_ue(ue_id_t ue_id, int pdu_session_id, int sock, const char *ifname) { nr_sdap_entity_t *entity = nr_sdap_get_entity(ue_id, pdu_session_id); DevAssert(entity != NULL); DevAssert(!entity->is_gnb); entity->pdusession_sock = sock; + entity->pdusession_if_name = strdup(ifname); entity->stop_thread = false; char thread_name[64]; snprintf(thread_name, sizeof(thread_name), "ue_tun_read_%ld_p%d", ue_id, pdu_session_id); @@ -193,12 +195,14 @@ void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_sess if (ipv4) { setup_ue_ipv4_route(ifname, ue_id, ipv4); } - start_sdap_tun_ue(ue_id, pdu_session_id, sock); // interface name suffix is ue_id+1 + start_sdap_tun_ue(ue_id, pdu_session_id, sock, ifname); // interface name suffix is ue_id+1 } void remove_ip_if(nr_sdap_entity_t *entity) { DevAssert(entity != NULL); + DevAssert(entity->pdusession_if_name != NULL); + // Stop the read thread entity->stop_thread = true; @@ -208,13 +212,7 @@ void remove_ip_if(nr_sdap_entity_t *entity) 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 - int default_pdu = get_softmodem_params()->default_pdu_session_id; - char ifname[IFNAMSIZ]; - if (entity->is_gnb) { - char *ifprefix = get_softmodem_params()->nsa ? "oaitun_gnb" : "oaitun_enb"; - tun_generate_ifname(ifname, ifprefix, entity->ue_id - 1); - } else { - tun_generate_ue_ifname(ifname, entity->ue_id, entity->pdusession_id != default_pdu ? entity->pdusession_id : -1); - } - tun_destroy(ifname); + tun_destroy(entity->pdusession_if_name); + free(entity->pdusession_if_name); + entity->pdusession_if_name = NULL; } diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index 9f85ac21cf..c8a43dbc2a 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -102,6 +102,7 @@ typedef struct nr_sdap_entity_s { bool enable_sdap; int pdusession_id; int pdusession_sock; + char *pdusession_if_name; pthread_t pdusession_thread; bool stop_thread; int qfi; From 1e5c28279e795ab2a08d9d8304d40968cf4adf3d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 22 Oct 2025 09:10:32 +0200 Subject: [PATCH 08/20] Rename tun_if.{c,h} => tuntap_if.c{c,h} The next commit introduces functions to also manage TAP interfaces. --- common/utils/CMakeLists.txt | 2 +- common/utils/{tun_if.c => tuntap_if.c} | 2 +- common/utils/{tun_if.h => tuntap_if.h} | 0 doc/Doxyfile | 4 ++-- openair2/LAYER2/PDCP_v10.1.0/pdcp.c | 2 +- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 4 ++-- openair2/RRC/LTE/rrc_UE.c | 2 +- openair2/RRC/LTE/rrc_eNB.c | 2 +- openair2/SDAP/nr_sdap/nr_sdap.c | 2 +- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 2 +- openair3/NAS/UE/ESM/esm_ebr_context.c | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) rename common/utils/{tun_if.c => tuntap_if.c} (99%) rename common/utils/{tun_if.h => tuntap_if.h} (100%) diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt index dc997db6c7..395139132d 100644 --- a/common/utils/CMakeLists.txt +++ b/common/utils/CMakeLists.txt @@ -14,7 +14,7 @@ add_subdirectory(nr) add_subdirectory(LOG) add_subdirectory(threadPool) add_subdirectory(time_manager) -add_library(utils utils.c system.c time_meas.c time_stat.c tun_if.c) +add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c) target_include_directories(utils PUBLIC .) target_link_libraries(utils PRIVATE ${T_LIB}) if (ENABLE_TESTS) diff --git a/common/utils/tun_if.c b/common/utils/tuntap_if.c similarity index 99% rename from common/utils/tun_if.c rename to common/utils/tuntap_if.c index c2b21c02b2..4056f2aba3 100644 --- a/common/utils/tun_if.c +++ b/common/utils/tuntap_if.c @@ -30,7 +30,7 @@ #include #include -#include "tun_if.h" +#include "tuntap_if.h" #include "common/platform_constants.h" #include "common/utils/LOG/log.h" #include "common/utils/system.h" diff --git a/common/utils/tun_if.h b/common/utils/tuntap_if.h similarity index 100% rename from common/utils/tun_if.h rename to common/utils/tuntap_if.h diff --git a/doc/Doxyfile b/doc/Doxyfile index 39c7983e72..20202b3c00 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -866,8 +866,8 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../common/config/config_cmdline.c \ @CMAKE_CURRENT_SOURCE_DIR@/../common/utils/time_meas.c \ @CMAKE_CURRENT_SOURCE_DIR@/../common/utils/time_meas.h \ -@CMAKE_CURRENT_SOURCE_DIR@/../common/utils/tun_if.h \ -@CMAKE_CURRENT_SOURCE_DIR@/../common/utils/tun_if.c \ +@CMAKE_CURRENT_SOURCE_DIR@/../common/utils/tuntap_if.h \ +@CMAKE_CURRENT_SOURCE_DIR@/../common/utils/tuntap_if.c \ @CMAKE_CURRENT_SOURCE_DIR@/../common/utils/utils.c \ @CMAKE_CURRENT_SOURCE_DIR@/../common/utils/oai_asn1.h \ @CMAKE_CURRENT_SOURCE_DIR@/../common/utils/system.h \ diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c index 884c5f5621..03b8ecc2d3 100644 --- a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c +++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c @@ -49,7 +49,7 @@ #include "common/ngran_types.h" #include "common/openairinterface5g_limits.h" #include "executables/lte-softmodem.h" -#include "common/utils/tun_if.h" +#include "common/utils/tuntap_if.h" #include "intertask_interface.h" #include "openair3/S1AP/s1ap_eNB.h" #include diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index 3fd4c4bdd4..88f9fec043 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -46,7 +46,7 @@ #include "common/platform_constants.h" #include "common/ran_context.h" #include "common/utils/T/T.h" -#include "common/utils/tun_if.h" +#include "common/utils/tuntap_if.h" #include "cuup_cucp_if.h" #include "executables/lte-softmodem.h" #include "executables/softmodem-common.h" @@ -405,7 +405,7 @@ void nr_pdcp_layer_init(void) #include "nfapi/oai_integration/vendor_ext.h" #include "executables/lte-softmodem.h" -#include "common/utils/tun_if.h" +#include "common/utils/tuntap_if.h" #include "openair2/SDAP/nr_sdap/nr_sdap.h" static void deliver_sdu_drb(void *_ue, nr_pdcp_entity_t *entity, diff --git a/openair2/RRC/LTE/rrc_UE.c b/openair2/RRC/LTE/rrc_UE.c index 70692bfaa9..428e59ca5c 100644 --- a/openair2/RRC/LTE/rrc_UE.c +++ b/openair2/RRC/LTE/rrc_UE.c @@ -64,7 +64,7 @@ #include "LTE_UECapabilityEnquiry.h" #include "LTE_UE-CapabilityRequest.h" -#include "common/utils/tun_if.h" +#include "common/utils/tuntap_if.h" #if ENABLE_RAL #include "rrc_UE_ral.h" #endif diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c index 4ddd7fc625..36d1a72653 100644 --- a/openair2/RRC/LTE/rrc_eNB.c +++ b/openair2/RRC/LTE/rrc_eNB.c @@ -79,7 +79,7 @@ #include "T.h" #include "LTE_MeasResults.h" -#include "common/utils/tun_if.h" +#include "common/utils/tuntap_if.h" #include "rrc_eNB_S1AP.h" #include "rrc_eNB_GTPV1U.h" diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index 98b1a48514..1ab78be406 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -29,7 +29,7 @@ #include "common/utils/LOG/log.h" #include "errno.h" #include "rlc.h" -#include "tun_if.h" +#include "tuntap_if.h" #include "system.h" static void reblock_tun_socket(int fd) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 3a4cb12b03..266fff17ae 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -31,7 +31,7 @@ #include "gtpv1_u_messages_types.h" #include "intertask_interface.h" #include "rlc.h" -#include "tun_if.h" +#include "tuntap_if.h" #include "nr_sdap.h" #define NO_SDAP_HEADER 0 diff --git a/openair3/NAS/UE/ESM/esm_ebr_context.c b/openair3/NAS/UE/ESM/esm_ebr_context.c index cec4c268bd..63cfabbac4 100644 --- a/openair3/NAS/UE/ESM/esm_ebr_context.c +++ b/openair3/NAS/UE/ESM/esm_ebr_context.c @@ -45,7 +45,7 @@ Description Defines functions used to handle EPS bearer contexts. #include "esm_ebr.h" #include "esm_ebr_context.h" -#include "common/utils/tun_if.h" +#include "common/utils/tuntap_if.h" #include "emm_sap.h" #include "system.h" From cadd031c81ced6a55a5c4d0d9a0f60312d606f7e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 20 Oct 2025 11:05:20 +0200 Subject: [PATCH 09/20] Modify TUN interface handling to also handle TAP Modify all functions to also handle TAP (where applicable). The actual TAP interface code is not used in this commit, it will be used later. To make the interface creation stand out, make "Interface XXX successfully configured" a LOG_A. --- common/utils/tuntap_if.c | 39 +++++++++++++++++++++++++-------- common/utils/tuntap_if.h | 36 +++++++++++++++++++++++------- openair2/SDAP/nr_sdap/nr_sdap.c | 8 +++---- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/common/utils/tuntap_if.c b/common/utils/tuntap_if.c index 4056f2aba3..b4722d69f1 100644 --- a/common/utils/tuntap_if.c +++ b/common/utils/tuntap_if.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include "tuntap_if.h" @@ -38,7 +37,7 @@ int nas_sock_fd[MAX_MOBILES_PER_ENB * 2]; // Allocated for both LTE UE and NR UE. int nas_sock_mbms_fd; -int tun_alloc(const char *dev) +int tuntap_alloc(int flag, const char *dev) { struct ifreq ifr; int fd, err; @@ -54,7 +53,8 @@ int tun_alloc(const char *dev) * * IFF_NO_PI - Do not provide packet information */ - ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + DevAssert(flag == IFF_TUN || flag == IFF_TAP); + ifr.ifr_flags = flag | IFF_NO_PI; strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name) - 1); if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) { close(fd); @@ -76,7 +76,7 @@ int tun_alloc(const char *dev) int tun_init_mbms(char *ifname) { - nas_sock_mbms_fd = tun_alloc(ifname); + nas_sock_mbms_fd = tuntap_alloc(IFF_TUN, ifname); if (nas_sock_mbms_fd == -1) { LOG_E(UTIL, "Error opening mbms socket %s (%d:%s)\n", ifname, errno, strerror(errno)); @@ -96,7 +96,7 @@ int tun_init_mbms(char *ifname) int tun_init(const char *ifname, int instance_id) { - nas_sock_fd[instance_id] = tun_alloc(ifname); + nas_sock_fd[instance_id] = tuntap_alloc(IFF_TUN, ifname); if (nas_sock_fd[instance_id] == -1) { LOG_E(UTIL, "Error opening socket %s (%d:%s)\n", ifname, errno, strerror(errno)); @@ -238,7 +238,7 @@ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6) success = set_if_flags(sock_fd, ifname, (IFF_UP | IFF_NOARP | IFF_POINTOPOINT) & ~IFF_MULTICAST); if (success) - LOG_I(OIP, "Interface %s successfully configured, IPv4 %s, IPv6 %s\n", ifname, ipv4, ipv6); + LOG_A(OIP, "TUN Interface %s successfully configured, IPv4 %s, IPv6 %s\n", ifname, ipv4, ipv6); else LOG_E(OIP, "Interface %s couldn't be configured (IPv4 %s, IPv6 %s)\n", ifname, ipv4, ipv6); @@ -246,6 +246,25 @@ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6) return success; } +bool tap_config(const char* ifname) +{ + int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd < 0) { + LOG_E(UTIL, "Failed creating socket for interface management: %d, %s\n", errno, strerror(errno)); + return false; + } + + bool success = set_if_flags(sock_fd, ifname, IFF_UP); + + if (success) + LOG_A(OIP, "TAP interface %s successfully configured\n", ifname); + else + LOG_E(OIP, "Tap interface %s couldn't be configured\n", ifname); + + close(sock_fd); + return success; +} + void setup_ue_ipv4_route(const char* ifname, int instance_id, const char *ipv4) { int table_id = instance_id - 1 + 10000; @@ -274,14 +293,16 @@ int tun_generate_ifname(char *ifname, const char *ifprefix, int instance_id) return snprintf(ifname, IFNAMSIZ, "%s%d", ifprefix, instance_id + 1); } -int tun_generate_ue_ifname(char *ifname, int instance_id, int pdu_session_id) +int tuntap_generate_ue_ifname(char *ifname, int flag, int instance_id, int pdu_session_id) { + DevAssert(flag == IFF_TUN || flag == IFF_TAP); char pdu_session_string[10]; snprintf(pdu_session_string, sizeof(pdu_session_string), "p%d", pdu_session_id); - return snprintf(ifname, IFNAMSIZ, "%s%d%s", "oaitun_ue", instance_id + 1, pdu_session_id == -1 ? "" : pdu_session_string); + const char *basename = flag == IFF_TUN ? "oaitun_ue" : "oaitap_ue"; + return snprintf(ifname, IFNAMSIZ, "%s%d%s", basename, instance_id + 1, pdu_session_id == -1 ? "" : pdu_session_string); } -void tun_destroy(const char *dev) +void tuntap_destroy(const char *dev) { // Use a new socket for ioctl operations int fd = socket(AF_INET, SOCK_DGRAM, 0); diff --git a/common/utils/tuntap_if.h b/common/utils/tuntap_if.h index 8e1463624f..152cc5064f 100644 --- a/common/utils/tuntap_if.h +++ b/common/utils/tuntap_if.h @@ -24,6 +24,7 @@ #include #include +#include /*! * \brief This function generates the name of the interface based on the prefix and @@ -34,7 +35,17 @@ * \param[in] instance_id unique instance number */ int tun_generate_ifname(char *ifname, const char *ifprefix, int instance_id); -int tun_generate_ue_ifname(char *ifname, int instance_id, int pdu_session_id); + +/*! + * \brief This function generates the name of the interface based on the prefix and + * the instance id for a UE. + * + * \param[in,out] ifname name of the interface + * \param[in] flag IFF_TUN (for TUN device) or IFF_TAP (for TAP device) + * \param[in] ifprefix prefix of the interface + * \param[in] instance_id unique instance number + */ +int tuntap_generate_ue_ifname(char *ifname, int flag, int instance_id, int pdu_session_id); /*! * \brief This function initializes the TUN interface @@ -50,9 +61,8 @@ int tun_init(const char *ifname, int instance_id); int tun_init_mbms(char *ifname); /*! - * \brief This function initializes the nasmesh interface using the basic values, - * basic address, network mask and broadcast address, as the default configured - * ones + * \brief Initializes the TUN interface with an IPv4 or IPv6 address and + * activates the interface. * \param[in] ifname name of the interface * \param[in] ipv4 IPv4 address of this interface as a string * \param[in] ipv6 IPv6 address of this interface as a string @@ -62,6 +72,15 @@ int tun_init_mbms(char *ifname); */ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6); +/*! + * \brief Initializes and activates the TAP interface. + * \param[in] ifname name of the interface + * \return true on success, otherwise false + * \note + * @ingroup _nas + */ +bool tap_config(const char* ifname); + /*! * \brief Setup a IPv4 rule in table (interface_id - 1 + 10000) and route to * force packets coming into interface back through it, and workaround @@ -75,16 +94,17 @@ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6); void setup_ue_ipv4_route(const char* ifname, int instance_id, const char *ipv4); /*! - * \brief This function allocates a TUN interface + * \brief This function allocates a TUN or TAP interface + * \param[in] flag IFF_TUN (for TUN device) or IFF_TAP (for TAP device) * \param[in] dev name of the interface * \return file descriptor of the allocated interface */ -int tun_alloc(const char *dev); +int tuntap_alloc(int flag, const char *dev); /*! - * \brief This function destroys the TUN interface + * \brief This function destroys the TUN or TAP interface * \param[in] dev name of the interface */ -void tun_destroy(const char *dev); +void tuntap_destroy(const char *dev); #endif /*TUN_IF_H_*/ diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index 1ab78be406..fbbe40fcfd 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -165,7 +165,7 @@ void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id, int pdu_sess char *ifprefix = get_softmodem_params()->nsa ? "oaitun_gnb" : "oaitun_enb"; char ifname[IFNAMSIZ]; tun_generate_ifname(ifname, ifprefix, ue_id - 1); - entity->pdusession_sock = tun_alloc(ifname); + entity->pdusession_sock = tuntap_alloc(IFF_TUN, ifname); entity->pdusession_if_name = strdup(ifname); tun_config(ifname, "10.0.1.1", NULL); threadCreate(&entity->pdusession_thread, sdap_tun_read_thread, entity, "gnb_tun_read_thread", -1, OAI_PRIORITY_RT_LOW); @@ -189,8 +189,8 @@ void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_sess { int default_pdu = get_softmodem_params()->default_pdu_session_id; char ifname[IFNAMSIZ]; - tun_generate_ue_ifname(ifname, ue_id, pdu_session_id != default_pdu ? pdu_session_id : -1); - const int sock = tun_alloc(ifname); + tuntap_generate_ue_ifname(ifname, IFF_TUN, ue_id, pdu_session_id != default_pdu ? pdu_session_id : -1); + const int sock = tuntap_alloc(IFF_TUN, ifname); tun_config(ifname, ipv4, ipv6); if (ipv4) { setup_ue_ipv4_route(ifname, ue_id, ipv4); @@ -212,7 +212,7 @@ void remove_ip_if(nr_sdap_entity_t *entity) 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 - tun_destroy(entity->pdusession_if_name); + tuntap_destroy(entity->pdusession_if_name); free(entity->pdusession_if_name); entity->pdusession_if_name = NULL; } From 542b2d017e8dedf872488950bc03bf8bdb1d120d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 16 Nov 2025 13:28:25 +0100 Subject: [PATCH 10/20] Add configurable DNN in PDU session request encoding --- openair2/COMMON/as_message.h | 1 + openair3/NAS/NR_UE/nr_nas_msg.c | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/openair2/COMMON/as_message.h b/openair2/COMMON/as_message.h index a134c1b47c..d82d1ce53f 100644 --- a/openair2/COMMON/as_message.h +++ b/openair2/COMMON/as_message.h @@ -474,6 +474,7 @@ typedef struct nas_pdu_session_req_s { int pdusession_type; int sst; int sd; + char dnn[103]; /* 24.501 §9.11.2.1A: max length 102 + 0-byte */ } nas_pdu_session_req_t; /* diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 06b75477b6..da8d70f0e2 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1697,11 +1697,11 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * if (has_nssai_sd) INT24_TO_BUFFER(pdu_req->sd, &mm_msg->snssai.value[1]); size += 1 + 1 + nssai_len; - int dnnSize = strlen(nas->uicc->dnnStr); + int dnnSize = strlen(pdu_req->dnn); mm_msg->dnn.value = calloc(1, dnnSize + 1); mm_msg->dnn.length = dnnSize + 1; mm_msg->dnn.value[0] = dnnSize; - memcpy(mm_msg->dnn.value + 1, nas->uicc->dnnStr, dnnSize); + memcpy(mm_msg->dnn.value + 1, pdu_req->dnn, dnnSize); size += (1 + 1 + dnnSize + 1); // encode the message @@ -1776,10 +1776,12 @@ static void send_nas_5gmm_ind(instance_t instance, const Guti5GSMobileIdentity_t void request_pdusession(nr_ue_nas_t *nas, int pdusession_id) { MessageDef *message_p = itti_alloc_new_message(TASK_NAS_NRUE, nas->UE_id, NAS_PDU_SESSION_REQ); - NAS_PDU_SESSION_REQ(message_p).pdusession_id = pdusession_id; - NAS_PDU_SESSION_REQ(message_p).pdusession_type = 0x91; // 0x91 = IPv4, 0x92 = IPv6, 0x93 = IPv4v6 - NAS_PDU_SESSION_REQ(message_p).sst = nas->uicc->nssai_sst; - NAS_PDU_SESSION_REQ(message_p).sd = nas->uicc->nssai_sd; + nas_pdu_session_req_t *pdu_req = &NAS_PDU_SESSION_REQ(message_p); + pdu_req->pdusession_id = pdusession_id; + pdu_req->pdusession_type = 0x91; // 0x91 = IPv4, 0x92 = IPv6, 0x93 = IPv4v6 + pdu_req->sst = nas->uicc->nssai_sst; + pdu_req->sd = nas->uicc->nssai_sd; + snprintf(pdu_req->dnn, sizeof(pdu_req->dnn), "%s", nas->uicc->dnnStr); itti_send_msg_to_task(TASK_NAS_NRUE, nas->UE_id, message_p); } From 0eddb4d3e3b062345dfd4f493fddebc1aa853a2f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 16 Nov 2025 14:42:48 +0100 Subject: [PATCH 11/20] get_user_nssai_idx(): make NSSAI check configurable We need to check arbitrary PDU session configurations in a later commit, so make this configurable. --- openair3/NAS/NR_UE/nr_nas_msg.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index da8d70f0e2..1df4409bbe 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1785,14 +1785,14 @@ void request_pdusession(nr_ue_nas_t *nas, int pdusession_id) itti_send_msg_to_task(TASK_NAS_NRUE, nas->UE_id, message_p); } -static int get_user_nssai_idx(const nr_nas_msg_snssai_t allowed_nssai[NAS_MAX_NUMBER_SLICES], const nr_ue_nas_t *nas) +static int get_user_nssai_idx(nssai_t ch_nssai, const nr_nas_msg_snssai_t allowed_nssai[NAS_MAX_NUMBER_SLICES]) { for (int i = 0; i < NAS_MAX_NUMBER_SLICES; i++) { const nr_nas_msg_snssai_t *nssai = allowed_nssai + i; /* If it was received in Registration Accept, check the SD in the stored Allowed N-SSAI, else, consider the SD valid */ - bool sd_match = !nssai->sd || (nas->uicc->nssai_sd == *nssai->sd); - if ((nas->uicc->nssai_sst == nssai->sst) && sd_match) + bool sd_match = !nssai->sd || (ch_nssai.sd == *nssai->sd); + if ((ch_nssai.sst == nssai->sst) && sd_match) return i; } return -1; @@ -1867,7 +1867,8 @@ static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buff send_nas_uplink_data_req(nas, &initialNasMsg); LOG_I(NAS, "Send NAS_UPLINK_DATA_REQ message(RegistrationComplete)\n"); } - if (get_user_nssai_idx(msg.nas_allowed_nssai, nas) < 0) { + nssai_t ch_nssai = {nas->uicc->nssai_sst, nas->uicc->nssai_sd}; + if (get_user_nssai_idx(ch_nssai, msg.nas_allowed_nssai) < 0) { LOG_E(NAS, "NSSAI parameters not match with allowed NSSAI. Couldn't request PDU session.\n"); } else { request_pdusession(nas, get_softmodem_params()->default_pdu_session_id); From 69edfa25cbe6c2df9e7c3994d4551959ed2a667e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 7 Jan 2026 18:01:56 +0100 Subject: [PATCH 12/20] Extend PDU session types and make PDU session req flexible --- common/utils/telnetsrv/telnetsrv_ciUE.c | 5 ++++- .../5GSM/MSG/PduSessionEstablishmentAccept.h | 2 ++ openair3/NAS/NR_UE/nr_nas_msg.c | 21 ++++++++++++------- openair3/NAS/NR_UE/nr_nas_msg.h | 3 ++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/common/utils/telnetsrv/telnetsrv_ciUE.c b/common/utils/telnetsrv/telnetsrv_ciUE.c index cc580b1a83..81451796e0 100644 --- a/common/utils/telnetsrv/telnetsrv_ciUE.c +++ b/common/utils/telnetsrv/telnetsrv_ciUE.c @@ -172,7 +172,10 @@ static int add_pdu_session(char *buf, int debug, telnet_printfunc_t prnt) if (!nas) ERROR_MSG_RET("No NAS context found for UE_ID %d\n", ue_id); - request_pdusession(nas, pdusession_id); + DevAssert(nas->uicc); + nssai_t nssai = {nas->uicc->nssai_sst, nas->uicc->nssai_sd}; + const char *dnn = nas->uicc->dnnStr; + request_pdusession(nas, pdusession_id, 1 /* = PDU_SESSION_TYPE_IPV4 */, nssai, dnn); prnt("Triggered PDU session request for UE %d with ID %d\n", ue_id, pdusession_id); return 0; } diff --git a/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.h b/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.h index 80694814b4..9c515e858f 100644 --- a/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.h +++ b/openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.h @@ -49,6 +49,8 @@ typedef enum { FOREACH_IEI(TO_ENUM) } pduSessionEstablishment_IEI_t; #define PDU_SESSION_TYPE_IPV4 0b001 #define PDU_SESSION_TYPE_IPV6 0b010 #define PDU_SESSION_TYPE_IPV4V6 0b011 +#define PDU_SESSION_TYPE_UNSTRUCT 0b100 +#define PDU_SESSION_TYPE_ETHER 0b101 #define IPv4_ADDRESS_LENGTH 4 // length of the IPv4 address associated with a PDU session #define IPv6_INTERFACE_ID_LENGTH 8 // interface identifier for the IPv6 link local address #define SMF_IPv6_LLA_LENGTH 16 diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 1df4409bbe..f839ff4be6 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1773,15 +1773,21 @@ static void send_nas_5gmm_ind(instance_t instance, const Guti5GSMobileIdentity_t itti_send_msg_to_task(TASK_RRC_NRUE, instance, msg); } -void request_pdusession(nr_ue_nas_t *nas, int pdusession_id) +void request_pdusession(nr_ue_nas_t *nas, int pdusession_id, int t, nssai_t nssai, const char *dnn) { + AssertFatal(t == PDU_SESSION_TYPE_IPV4 || t == PDU_SESSION_TYPE_IPV6 || t == PDU_SESSION_TYPE_IPV4V6 + || t == PDU_SESSION_TYPE_UNSTRUCT || t == PDU_SESSION_TYPE_ETHER, + "illegal PDU session type %d\n", + t); + AssertFatal(t != PDU_SESSION_TYPE_UNSTRUCT, "unstructured PDU sessions not handled yet\n"); MessageDef *message_p = itti_alloc_new_message(TASK_NAS_NRUE, nas->UE_id, NAS_PDU_SESSION_REQ); nas_pdu_session_req_t *pdu_req = &NAS_PDU_SESSION_REQ(message_p); pdu_req->pdusession_id = pdusession_id; - pdu_req->pdusession_type = 0x91; // 0x91 = IPv4, 0x92 = IPv6, 0x93 = IPv4v6 - pdu_req->sst = nas->uicc->nssai_sst; - pdu_req->sd = nas->uicc->nssai_sd; - snprintf(pdu_req->dnn, sizeof(pdu_req->dnn), "%s", nas->uicc->dnnStr); + // 24.501: joint PDU session type IEI (0x9-, Table 8.3.1.1.1) and type (9.11.4.11) + pdu_req->pdusession_type = 0x90 | t; + pdu_req->sst = nssai.sst; + pdu_req->sd = nssai.sd; + snprintf(pdu_req->dnn, sizeof(pdu_req->dnn), "%s", dnn); itti_send_msg_to_task(TASK_NAS_NRUE, nas->UE_id, message_p); } @@ -1871,9 +1877,10 @@ static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buff if (get_user_nssai_idx(ch_nssai, msg.nas_allowed_nssai) < 0) { LOG_E(NAS, "NSSAI parameters not match with allowed NSSAI. Couldn't request PDU session.\n"); } else { - request_pdusession(nas, get_softmodem_params()->default_pdu_session_id); + nssai_t nssai = {nas->uicc->nssai_sst, nas->uicc->nssai_sd}; + request_pdusession(nas, get_softmodem_params()->default_pdu_session_id, PDU_SESSION_TYPE_IPV4, nssai, nas->uicc->dnnStr); if (get_nrUE_params()->extra_pdu_id != -1) { - request_pdusession(nas, get_nrUE_params()->extra_pdu_id); + request_pdusession(nas, get_nrUE_params()->extra_pdu_id, PDU_SESSION_TYPE_IPV4, nssai, nas->uicc->dnnStr); } } // Free local message after processing diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h index 7a19997444..1dd5afafe9 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -32,6 +32,7 @@ #define __NR_NAS_MSG_SIM_H__ #include +#include "common/5g_platform_types.h" #include #include #include @@ -113,6 +114,6 @@ void *nas_nrue_task(void *args_p); void *nas_nrue(void *args_p); void nas_init_nrue(int num_ues); void nr_ue_create_ip_if(const char *ifnameprefix, const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id); -void request_pdusession(nr_ue_nas_t *nas, int pdusession_id); +void request_pdusession(nr_ue_nas_t *nas, int pdusession_id, int type, nssai_t nssai, const char *dnn); #endif /* __NR_NAS_MSG_SIM_H__*/ From 9b4898ee5794d6d579f1689f91cc8e225a415a99 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 16 Nov 2025 14:21:12 +0100 Subject: [PATCH 13/20] Add new PDU session list configuration Add new configuration description for PDU sessions, including conformity checks. --- openair3/UICC/CMakeLists.txt | 2 +- openair3/UICC/pdu_session.c | 116 +++++++++++++++++++++++++++++++++++ openair3/UICC/pdu_session.h | 36 +++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 openair3/UICC/pdu_session.c create mode 100644 openair3/UICC/pdu_session.h diff --git a/openair3/UICC/CMakeLists.txt b/openair3/UICC/CMakeLists.txt index c80694e1a5..3e72949a1b 100644 --- a/openair3/UICC/CMakeLists.txt +++ b/openair3/UICC/CMakeLists.txt @@ -1,3 +1,3 @@ -add_library(lib_uicc OBJECT usim_interface.c) +add_library(lib_uicc OBJECT usim_interface.c pdu_session.c) target_include_directories(lib_uicc PRIVATE .) target_link_libraries(lib_uicc PRIVATE log_headers) diff --git a/openair3/UICC/pdu_session.c b/openair3/UICC/pdu_session.c new file mode 100644 index 0000000000..f3d13334f7 --- /dev/null +++ b/openair3/UICC/pdu_session.c @@ -0,0 +1,116 @@ +/* + * 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 + +#include "pdu_session.h" +// we can't include the file for PDU session type defines, as it pulls in too +// much. However, we only use it for mapping types to integers, which are +// standard defined, so it should be ok. +//#include "openair3/NAS/NR_UE/5GS/5GSM/MSG/PduSessionEstablishmentAccept.h" +#include "common/utils/utils.h" +#include "common/config/config_paramdesc.h" +#include "common/config/config_userapi.h" + + +// clang-format off +// Note: Default PDU session ID 0 does not exist and will be replaced with PDU +// session index if not set to a parameter > 0! +#define PDU_SESS_PARAMS_DESC { \ + {"id", "PDU session ID\n", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ + {"type", "PDU session type\n", 0, .strptr=NULL, .defstrval="IPv4", TYPE_STRING, 0}, \ + {"nssai_sst", "NSSAI slice type\n", 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \ + {"nssai_sd", "NSSAI slice differentiator\n", 0, .uptr=NULL, .defintval=0xffffff, TYPE_UINT32, 0}, \ + {"dnn", "PDU session DNN (APN)\n", 0, .strptr=NULL, .defstrval="oai", TYPE_STRING, 0}, \ +} + +#define PDU_SESS_PARAMS_CHECK { \ + {.s2 = {config_check_intrange, {0, 255}}}, \ + {.s3a = {config_checkstr_assign_integer, \ + {"IPv4", "IPv6", "IPv4v6", "Ethernet"}, \ + {1 /*PDU_SESSION_TYPE_IPV4*/, \ + 2 /*PDU_SESSION_TYPE_IPV6*/, \ + 3 /*PDU_SESSION_TYPE_IPV4V6*/, \ + 5 /*PDU_SESSION_TYPE_ETHER*/}, \ + 4}}, \ + {.s2 = {config_check_intrange, {1, 4}}}, \ + {.s2 = {config_check_intrange, {1, 0xffffff}}}, \ + {.s4 = {config_check_dnn}}, \ +} +// clang-format on + +static int config_check_dnn(configmodule_interface_t *cfg, paramdef_t *param) +{ + const char *dnn = *param->strptr; + int dnn_len = strlen(dnn); + if (dnn_len > 102) { + printf("DNN '%s' exceeds max len of 102 (is %d) (TS 24.501 §9.11.2.1A)\n", dnn, dnn_len); + return -1; + } + + // check for valid characters in the APN (TS 23.003 §9.1) + // we don't enforce all conditions + const char *allowed = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789.-"; + while (*dnn) { + if (!strchr(allowed, *dnn)) { + printf("character '%c' in DNN '%s' is not allowed (TS 23.003 §9.1)\n", *dnn, *param->strptr); + return -1; + } + dnn++; + } + + return 0; +} + +int get_pdu_session_configs(int module_id, pdu_session_config_t *configs, int max_len) +{ + paramdef_t pdu_sess_params[] = PDU_SESS_PARAMS_DESC; + const int npsp = sizeofArray(pdu_sess_params); + paramlist_def_t pdu_sess_list = {"pdu_sessions"}; + checkedparam_t pdu_sess_params_check[] = PDU_SESS_PARAMS_CHECK; + static_assert(sizeofArray(pdu_sess_params) == sizeofArray(pdu_sess_params_check), + "param_def_t array and corresponding checkedparam_t array should have the same size"); + for (int i = 0; i < sizeofArray(pdu_sess_params); ++i) + pdu_sess_params[i].chkPptr = &pdu_sess_params_check[i]; + int ret = config_getlist(config_get_if(), &pdu_sess_list, pdu_sess_params, sizeofArray(pdu_sess_params), NULL); + if (ret < 0) { + // couldn't load configuration from pdu_sessions, caller has to handle + return -1; + } + + int num = pdu_sess_list.numelt; + AssertFatal(num <= max_len, "cannot handle more than %d PDU sessions\n", max_len); + + for (int i = 0; i < num; ++i) { + pdu_session_config_t *pdu = configs + i; + paramdef_t *p = pdu_sess_list.paramarray[i]; + int id = *gpd(p, npsp, "id")->iptr; + pdu->id = id > 0 ? id : i + 1; // if not user-specified ID, use PDU session index + pdu->type = config_get_processedint(config_get_if(), (paramdef_t *)gpd(p, npsp, "type")); + pdu->nssai.sst = *gpd(p, npsp, "nssai_sst")->iptr; + pdu->nssai.sd = *gpd(p, npsp, "nssai_sd")->iptr; + pdu->dnn = strdup(*gpd(p, npsp, "dnn")->strptr); + } + + return num; +} diff --git a/openair3/UICC/pdu_session.h b/openair3/UICC/pdu_session.h new file mode 100644 index 0000000000..d74d4664ab --- /dev/null +++ b/openair3/UICC/pdu_session.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 PDU_SESSION_H_ +#define PDU_SESSION_H_ + +#include "common/5g_platform_types.h" + +typedef struct { + int id; + int type; + nssai_t nssai; + char *dnn; // [103]? +} pdu_session_config_t; + +int get_pdu_session_configs(int module_id, pdu_session_config_t *configs, int max_len); + +#endif /* PDU_SESSION_H_ */ From 27e01e081b9fd557e37b163a2d7fd5f70beb0450 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 17 Nov 2025 01:09:10 +0100 Subject: [PATCH 14/20] Read PDU session configuration and request Use the new PDU session configuration functionality of the parent commit. It is now possible to request multiple PDU sessions, and to request specific PDU session types. While the new uicc0.pdu_sessions is preferred, it is still possible to use the "legacy" uicc0.dnn/nssai_sst/nssai_sd. The old parameters --default-pdu-id and --extra-pdu-id are not allowed anymore, as it is possible to manually define this in the UE configuration. Correspondingly, fix the use of those options, where applicable. Note: since the default_pdu_id is changed to -1, all interface names will have pX to interface names appended (oaitun_ue1pX). The next commit will correct this. --- ci-scripts/conf_files/nrue.uicc.2pdu.conf | 11 ++++++ .../docker-compose.yaml | 4 +-- common/utils/telnetsrv/telnetsrv_ciUE.c | 5 +-- executables/nr-uesoftmodem.c | 6 ++-- executables/softmodem-common.c | 4 +++ executables/softmodem-common.h | 2 +- openair3/NAS/NR_UE/nr_nas_msg.c | 31 +++++++++------- openair3/NAS/NR_UE/nr_nas_msg.h | 2 +- openair3/UICC/pdu_session.c | 4 +-- openair3/UICC/pdu_session.h | 2 +- openair3/UICC/usim_interface.c | 36 ++++++++++++++++--- openair3/UICC/usim_interface.h | 3 ++ 12 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 ci-scripts/conf_files/nrue.uicc.2pdu.conf diff --git a/ci-scripts/conf_files/nrue.uicc.2pdu.conf b/ci-scripts/conf_files/nrue.uicc.2pdu.conf new file mode 100644 index 0000000000..03f64f47fc --- /dev/null +++ b/ci-scripts/conf_files/nrue.uicc.2pdu.conf @@ -0,0 +1,11 @@ +uicc0 = { + imsi = "208990100001100"; + key = "fec86ba6eb707ed08905757b1bb44b8f"; + opc= "C42449363BBAD02B66D16BC975D77CC1"; + pdu_sessions = ( + { id = 1; dnn = "oai"; nssai_sst = 1; }, + { id = 2; dnn = "oai"; nssai_sst = 1; } + ) +} + +thread-pool = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml index c4a4e2ebea..ebb006213d 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml @@ -149,7 +149,7 @@ services: - NET_RAW # for ping environment: USE_ADDITIONAL_OPTIONS: --rfsim -r 25 --numerology 0 --uicc0.imsi 208990100001100 --band 66 -C 2152680000 --CO -400000000 --ssb 48 - --rfsimulator.serveraddr 192.168.71.141 --extra-pdu-id 2 --log_config.global_log_options level,nocolor,time + --rfsimulator.serveraddr 192.168.71.141 --log_config.global_log_options level,nocolor,time --telnetsrv --telnetsrv.shrmod ciUE --telnetsrv.listenaddr 192.168.71.150 --telnetsrv.listenport 8091 ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: @@ -160,7 +160,7 @@ services: devices: - /dev/net/tun:/dev/net/tun volumes: - - ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf + - ../../conf_files/nrue.uicc.2pdu.conf:/opt/oai-nr-ue/etc/nr-ue.conf healthcheck: test: /bin/bash -c "pgrep nr-uesoftmodem" start_period: 10s diff --git a/common/utils/telnetsrv/telnetsrv_ciUE.c b/common/utils/telnetsrv/telnetsrv_ciUE.c index 81451796e0..457ba77b4b 100644 --- a/common/utils/telnetsrv/telnetsrv_ciUE.c +++ b/common/utils/telnetsrv/telnetsrv_ciUE.c @@ -174,8 +174,9 @@ static int add_pdu_session(char *buf, int debug, telnet_printfunc_t prnt) DevAssert(nas->uicc); nssai_t nssai = {nas->uicc->nssai_sst, nas->uicc->nssai_sd}; - const char *dnn = nas->uicc->dnnStr; - request_pdusession(nas, pdusession_id, 1 /* = PDU_SESSION_TYPE_IPV4 */, nssai, dnn); + pdu_session_config_t c = {pdusession_id, 1 /* = PDU_SESSION_TYPE_IPV4 */, nssai, nas->uicc->dnnStr}; + nas->uicc->pdu_sessions[nas->uicc->n_pdu_sessions++] = c; + request_pdusession(nas, &c); prnt("Triggered PDU session request for UE %d with ID %d\n", ue_id, pdusession_id); return 0; } diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index f1cc659cae..0d3c4053b2 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -181,10 +181,8 @@ static void get_options(configmodule_interface_t *cfg) config_get(cfg, cmdline_params, numparams, NULL); if (nrUE_params.vcdflag > 0) ouput_vcd = 1; - AssertFatal(nrUE_params.extra_pdu_id != get_softmodem_params()->default_pdu_session_id, - "Default PDU ID (%d) and Extra PDU ID (%d) must be different!\n", - get_softmodem_params()->default_pdu_session_id, - nrUE_params.extra_pdu_id); + AssertFatal(nrUE_params.extra_pdu_id == -1, + "Add additional PDU sessions in uicc.pdu_sessions array instead\n"); } // set PHY vars from command line diff --git a/executables/softmodem-common.c b/executables/softmodem-common.c index 09fced4aa3..558fd4968b 100644 --- a/executables/softmodem-common.c +++ b/executables/softmodem-common.c @@ -156,6 +156,10 @@ void get_common_options(configmodule_interface_t *cfg) nfapi_setmode(nfapi_mode); if (stats_disabled) IS_SOFTMODEM_NOSTATS = true; + + // To be removed in a future release (after 1 month) + AssertFatal(get_softmodem_params()->default_pdu_session_id == -1, + "Use uicc0.pdu_sessions.[0].id to change the requested PDU session ID (is %d)\n", get_softmodem_params()->default_pdu_session_id); } void softmodem_verify_mode(const softmodem_params_t *p) diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index 4dfa5eb680..cf5c6ad553 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -180,7 +180,7 @@ extern int usrp_tx_thread; {"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, .iptr=&softmodem_params.threequarter_fs, .defintval=0, TYPE_INT, 0}, \ {"imscope" , CONFIG_HLP_IMSCOPE, PARAMFLAG_BOOL, .uptr=&enable_imscope, .defintval=0, TYPE_UINT, 0}, \ {"imscope-record" , CONFIG_HLP_IMSCOPE_RECORD,PARAMFLAG_BOOL, .uptr=&enable_imscope_record, .defintval=0, TYPE_UINT, 0}, \ - {"default-pdu-id", NULL, 0, .iptr=&DEFAULT_PDU_ID, .defintval=10, TYPE_INT, 0}, \ + {"default-pdu-id", NULL, 0, .iptr=&DEFAULT_PDU_ID, .defintval=-1, TYPE_INT, 0}, \ } // clang-format on diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index f839ff4be6..59472df32a 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1773,8 +1773,9 @@ static void send_nas_5gmm_ind(instance_t instance, const Guti5GSMobileIdentity_t itti_send_msg_to_task(TASK_RRC_NRUE, instance, msg); } -void request_pdusession(nr_ue_nas_t *nas, int pdusession_id, int t, nssai_t nssai, const char *dnn) +void request_pdusession(nr_ue_nas_t *nas, const pdu_session_config_t *pdu) { + int t = pdu->type; AssertFatal(t == PDU_SESSION_TYPE_IPV4 || t == PDU_SESSION_TYPE_IPV6 || t == PDU_SESSION_TYPE_IPV4V6 || t == PDU_SESSION_TYPE_UNSTRUCT || t == PDU_SESSION_TYPE_ETHER, "illegal PDU session type %d\n", @@ -1782,12 +1783,12 @@ void request_pdusession(nr_ue_nas_t *nas, int pdusession_id, int t, nssai_t nssa AssertFatal(t != PDU_SESSION_TYPE_UNSTRUCT, "unstructured PDU sessions not handled yet\n"); MessageDef *message_p = itti_alloc_new_message(TASK_NAS_NRUE, nas->UE_id, NAS_PDU_SESSION_REQ); nas_pdu_session_req_t *pdu_req = &NAS_PDU_SESSION_REQ(message_p); - pdu_req->pdusession_id = pdusession_id; + pdu_req->pdusession_id = pdu->id; // 24.501: joint PDU session type IEI (0x9-, Table 8.3.1.1.1) and type (9.11.4.11) pdu_req->pdusession_type = 0x90 | t; - pdu_req->sst = nssai.sst; - pdu_req->sd = nssai.sd; - snprintf(pdu_req->dnn, sizeof(pdu_req->dnn), "%s", dnn); + pdu_req->sst = pdu->nssai.sst; + pdu_req->sd = pdu->nssai.sd; + snprintf(pdu_req->dnn, sizeof(pdu_req->dnn), "%s", pdu->dnn); itti_send_msg_to_task(TASK_NAS_NRUE, nas->UE_id, message_p); } @@ -1873,14 +1874,18 @@ static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buff send_nas_uplink_data_req(nas, &initialNasMsg); LOG_I(NAS, "Send NAS_UPLINK_DATA_REQ message(RegistrationComplete)\n"); } - nssai_t ch_nssai = {nas->uicc->nssai_sst, nas->uicc->nssai_sd}; - if (get_user_nssai_idx(ch_nssai, msg.nas_allowed_nssai) < 0) { - LOG_E(NAS, "NSSAI parameters not match with allowed NSSAI. Couldn't request PDU session.\n"); - } else { - nssai_t nssai = {nas->uicc->nssai_sst, nas->uicc->nssai_sd}; - request_pdusession(nas, get_softmodem_params()->default_pdu_session_id, PDU_SESSION_TYPE_IPV4, nssai, nas->uicc->dnnStr); - if (get_nrUE_params()->extra_pdu_id != -1) { - request_pdusession(nas, get_nrUE_params()->extra_pdu_id, PDU_SESSION_TYPE_IPV4, nssai, nas->uicc->dnnStr); + if (nas->uicc->n_pdu_sessions == 0) + LOG_W(SIM, "no PDU sessions to request configured\n"); + for (const pdu_session_config_t *pdu = nas->uicc->pdu_sessions; pdu < nas->uicc->pdu_sessions + nas->uicc->n_pdu_sessions; ++pdu) { + if (get_user_nssai_idx(pdu->nssai, msg.nas_allowed_nssai) < 0) { + LOG_E(NAS, + "PDU session ID %d NSSAI %d.%d: mismatch for allowed NSSAI. Couldn't request PDU session.\n", + pdu->id, + pdu->nssai.sst, + pdu->nssai.sd); + } else { + LOG_I(NAS, "requested PDU session ID %d type %d NSSAI %d.%d DNN %s\n", pdu->id, pdu->type, pdu->nssai.sst, pdu->nssai.sd, pdu->dnn); + request_pdusession(nas, pdu); } } // Free local message after processing diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h index 1dd5afafe9..1bed56aa3c 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -114,6 +114,6 @@ void *nas_nrue_task(void *args_p); void *nas_nrue(void *args_p); void nas_init_nrue(int num_ues); void nr_ue_create_ip_if(const char *ifnameprefix, const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id); -void request_pdusession(nr_ue_nas_t *nas, int pdusession_id, int type, nssai_t nssai, const char *dnn); +void request_pdusession(nr_ue_nas_t *nas, const pdu_session_config_t *pdu); #endif /* __NR_NAS_MSG_SIM_H__*/ diff --git a/openair3/UICC/pdu_session.c b/openair3/UICC/pdu_session.c index f3d13334f7..1eabeb7f37 100644 --- a/openair3/UICC/pdu_session.c +++ b/openair3/UICC/pdu_session.c @@ -82,7 +82,7 @@ static int config_check_dnn(configmodule_interface_t *cfg, paramdef_t *param) return 0; } -int get_pdu_session_configs(int module_id, pdu_session_config_t *configs, int max_len) +int get_pdu_session_configs(const char *uiccName, pdu_session_config_t *configs, int max_len) { paramdef_t pdu_sess_params[] = PDU_SESS_PARAMS_DESC; const int npsp = sizeofArray(pdu_sess_params); @@ -92,7 +92,7 @@ int get_pdu_session_configs(int module_id, pdu_session_config_t *configs, int ma "param_def_t array and corresponding checkedparam_t array should have the same size"); for (int i = 0; i < sizeofArray(pdu_sess_params); ++i) pdu_sess_params[i].chkPptr = &pdu_sess_params_check[i]; - int ret = config_getlist(config_get_if(), &pdu_sess_list, pdu_sess_params, sizeofArray(pdu_sess_params), NULL); + int ret = config_getlist(config_get_if(), &pdu_sess_list, pdu_sess_params, sizeofArray(pdu_sess_params), uiccName); if (ret < 0) { // couldn't load configuration from pdu_sessions, caller has to handle return -1; diff --git a/openair3/UICC/pdu_session.h b/openair3/UICC/pdu_session.h index d74d4664ab..c1acaa82b1 100644 --- a/openair3/UICC/pdu_session.h +++ b/openair3/UICC/pdu_session.h @@ -31,6 +31,6 @@ typedef struct { char *dnn; // [103]? } pdu_session_config_t; -int get_pdu_session_configs(int module_id, pdu_session_config_t *configs, int max_len); +int get_pdu_session_configs(const char *uiccName, pdu_session_config_t *configs, int max_len); #endif /* PDU_SESSION_H_ */ diff --git a/openair3/UICC/usim_interface.c b/openair3/UICC/usim_interface.c index f1019366b1..f722d6ccd6 100644 --- a/openair3/UICC/usim_interface.c +++ b/openair3/UICC/usim_interface.c @@ -22,6 +22,7 @@ */ #include +#include "common/platform_constants.h" #include #include extern uint16_t NB_UE_INST; @@ -76,10 +77,7 @@ uicc_t *init_uicc(char *sectionName) { // key, OPc, sqn, amf don't need to be read from the true USIM int ret = config_get(config_get_if(), uicc_params, sizeofArray(uicc_params), sectionName); AssertFatal(ret >= 0, "configuration couldn't be performed for uicc name: %s", sectionName); - LOG_I(SIM, - "UICC simulation: IMSI=%s, IMEISV=%s, Ki=%s, OPc=%s, DNN=%s, SST=0x%02x, SD=0x%06x\n", - uicc->imsiStr, uicc->imeisvStr, uicc->keyStr, uicc->opcStr, - uicc->dnnStr, uicc->nssai_sst, uicc->nssai_sd); + LOG_I(SIM, "UICC simulation: IMSI=%s, IMEISV=%s, Ki=%s, OPc=%s\n", uicc->imsiStr, uicc->imeisvStr, uicc->keyStr, uicc->opcStr); to_hex(uicc->keyStr,uicc->key, sizeof(uicc->key) ); to_hex(uicc->opcStr,uicc->opc, sizeof(uicc->opc) ); to_hex(uicc->sqnStr,uicc->sqn, sizeof(uicc->sqn) ); @@ -112,6 +110,36 @@ uicc_t * checkUicc(int Mod_id) { char uiccName[64]; sprintf(uiccName,"uicc%d", Mod_id); uiccArray[Mod_id]=(void*)init_uicc(uiccName); + uicc_t *uicc = uiccArray[Mod_id]; + uicc->n_pdu_sessions = get_pdu_session_configs(uiccName, uicc->pdu_sessions, sizeof(uicc->pdu_sessions)); + if (uicc->n_pdu_sessions > 0) { + LOG_I(SIM, "overriding potential %s.dnn/nssai_sst/nssai_sd from %s.pdu_sessions\n", uiccName, uiccName); + } else if (uicc->n_pdu_sessions < 0) { + // we could not load from "new" pdu_sessions list, so read from "legacy" + // config + LOG_W(SIM, + "loading PDU session parameters from legacy UICC configuration. Please update to use %s.pdu_sessions instead to make this warning disappear\n", + uiccName); + sleep(3); + uicc->n_pdu_sessions = 1; + pdu_session_config_t *pdu = &uicc->pdu_sessions[0]; + AssertFatal(uicc->dnnStr != NULL, "no default DNN, cannot create\n"); + pdu->id = DEFAULT_NOS1_PDU_ID; // any default would do + pdu->dnn = strdup(uicc->dnnStr); + pdu->nssai.sst = uicc->nssai_sst; + pdu->nssai.sd = uicc->nssai_sd; + pdu->type = 1; /* = PDU_SESSION_TYPE_IPV4 */ + } // else: uicc->n_pdu_sessions == 0 => don't request a PDU session + for (int i = 0; i < uicc->n_pdu_sessions; ++i) { + const pdu_session_config_t *pdu = &uicc->pdu_sessions[i]; + LOG_I(SIM, + "will request PDU session ID %d, type %d, DNN %s, NSSAI %d.%6x\n", + pdu->id, + pdu->type, + pdu->dnn, + pdu->nssai.sst, + pdu->nssai.sd); + } } return (uicc_t*) uiccArray[Mod_id]; } diff --git a/openair3/UICC/usim_interface.h b/openair3/UICC/usim_interface.h index 1a275db887..f4689594c2 100644 --- a/openair3/UICC/usim_interface.h +++ b/openair3/UICC/usim_interface.h @@ -34,6 +34,7 @@ #include #include #include "common_lib.h" +#include "pdu_session.h" /* 3GPP glossary RES RESponse @@ -53,6 +54,8 @@ typedef struct { char *dnnStr; int nssai_sst; int nssai_sd; + pdu_session_config_t pdu_sessions[256]; + int n_pdu_sessions; uint8_t key[16]; uint8_t opc[16]; uint8_t amf[2]; From 78720d5d9c8a498a4e65ebaed693c0d1adb1eed5 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 20 Jan 2026 08:23:09 +0100 Subject: [PATCH 15/20] Make default oaitun_ueY name for default (first) PDU session Look up the index of a PDU session in the list of PDU sessions upon accept to determine if the PDU session is the default (first PDU session) or not. If it is the default, it will not append a suffix to the interface name (oaitun_ueY), otherwise append the PDU session ID (e.g., for 5: oaitun_ueYp5). --- openair2/SDAP/nr_sdap/nr_sdap.c | 5 ++-- openair2/SDAP/nr_sdap/nr_sdap.h | 2 +- openair3/NAS/NR_UE/nr_nas_msg.c | 45 +++++++++++++++++++++++---------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index fbbe40fcfd..a38550f993 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -185,11 +185,10 @@ static void start_sdap_tun_ue(ue_id_t ue_id, int pdu_session_id, int sock, const } -void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id) +void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id, bool is_default) { - int default_pdu = get_softmodem_params()->default_pdu_session_id; char ifname[IFNAMSIZ]; - tuntap_generate_ue_ifname(ifname, IFF_TUN, ue_id, pdu_session_id != default_pdu ? pdu_session_id : -1); + tuntap_generate_ue_ifname(ifname, IFF_TUN, ue_id, is_default ? -1 : pdu_session_id); const int sock = tuntap_alloc(IFF_TUN, ifname); tun_config(ifname, ipv4, ipv6); if (ipv4) { diff --git a/openair2/SDAP/nr_sdap/nr_sdap.h b/openair2/SDAP/nr_sdap/nr_sdap.h index 49527359b3..682c97b6fc 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.h +++ b/openair2/SDAP/nr_sdap/nr_sdap.h @@ -61,6 +61,6 @@ void sdap_data_ind(int pdcp_entity, int size); void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id, int pdu_session_id); -void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id); +void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id, bool is_default); #endif diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 59472df32a..3add94a549 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1472,7 +1472,10 @@ static int capture_ipv6_addr(const uint8_t *addr, char *ip, size_t len) * @brief Process PDU Session Address in PDU Session Establishment Accept message * and configure the tun interface */ -static void process_pdu_session_addr(pdu_session_establishment_accept_msg_t *msg, int instance_id, int pdu_session_id) +static void process_pdu_session_addr(pdu_session_establishment_accept_msg_t *msg, + int instance_id, + int pdu_session_id, + bool is_default) { uint8_t *addr = msg->pdu_addr_ie.pdu_addr_oct; @@ -1480,13 +1483,13 @@ static void process_pdu_session_addr(pdu_session_establishment_accept_msg_t *msg case PDU_SESSION_TYPE_IPV4: { char ip[20]; capture_ipv4_addr(&addr[0], ip, sizeof(ip)); - create_ue_ip_if(ip, NULL, instance_id, pdu_session_id); + create_ue_ip_if(ip, NULL, instance_id, pdu_session_id, is_default); } break; case PDU_SESSION_TYPE_IPV6: { char ipv6[40]; capture_ipv6_addr(addr, ipv6, sizeof(ipv6)); - create_ue_ip_if(NULL, ipv6, instance_id, pdu_session_id); + create_ue_ip_if(NULL, ipv6, instance_id, pdu_session_id, is_default); } break; case PDU_SESSION_TYPE_IPV4V6: { @@ -1494,7 +1497,7 @@ static void process_pdu_session_addr(pdu_session_establishment_accept_msg_t *msg capture_ipv6_addr(addr, ipv6, sizeof(ipv6)); char ipv4[20]; capture_ipv4_addr(&addr[IPv6_INTERFACE_ID_LENGTH], ipv4, sizeof(ipv4)); - create_ue_ip_if(ipv4, ipv6, instance_id, pdu_session_id); + create_ue_ip_if(ipv4, ipv6, instance_id, pdu_session_id, is_default); } break; default: @@ -1506,7 +1509,7 @@ static void process_pdu_session_addr(pdu_session_establishment_accept_msg_t *msg /** * @brief Handle PDU Session Establishment Accept and process decoded message */ -static void handle_pdu_session_accept(uint8_t *pdu_buffer, uint32_t msg_length, int instance) +static void handle_pdu_session_accept(const nr_ue_nas_t *nas, uint8_t *pdu_buffer, uint32_t msg_length, int instance) { pdu_session_establishment_accept_msg_t msg = {0}; int size = 0; @@ -1549,9 +1552,24 @@ static void handle_pdu_session_accept(uint8_t *pdu_buffer, uint32_t msg_length, return; } - // process PDU Session + int idx; + for (idx = 0; idx < nas->uicc->n_pdu_sessions; ++idx) { + const pdu_session_config_t *pdu = &nas->uicc->pdu_sessions[idx]; + if (pdu->id == sm_header.pdu_session_id && pdu->type == msg.pdu_type) + break; + } + if (idx == nas->uicc->n_pdu_sessions) { + LOG_E(NAS, + "PDU session establishment accept for ID %d type %d not in list of configured PDU sessions, abort transaction\n", + sm_header.pdu_session_id, + msg.pdu_type); + return; + } + + // process PDU Session: pass ID -1 to not append PDU ID to interface + bool is_default = idx == 0; if (msg.pdu_addr_ie.pdu_length) - process_pdu_session_addr(&msg, instance, sm_header.pdu_session_id); + process_pdu_session_addr(&msg, instance, sm_header.pdu_session_id, is_default); else LOG_W(NAS, "Optional PDU Address IE was not provided\n"); @@ -1561,7 +1579,7 @@ static void handle_pdu_session_accept(uint8_t *pdu_buffer, uint32_t msg_length, /** * @brief Handle DL NAS Transport and process piggybacked 5GSM messages */ -void handleDownlinkNASTransport(uint8_t * pdu_buffer, int pdu_length, int instance) +void handleDownlinkNASTransport(const nr_ue_nas_t *nas, uint8_t * pdu_buffer, int pdu_length, int instance) { if (pdu_length < 17) { LOG_E(NAS, "Received DL NAS Transport message too short (%d)\n", pdu_length); @@ -1570,7 +1588,7 @@ void handleDownlinkNASTransport(uint8_t * pdu_buffer, int pdu_length, int instan uint8_t msg_type = *(pdu_buffer + 16); if (msg_type == FGS_PDU_SESSION_ESTABLISHMENT_ACC) { LOG_A(NAS, "Received PDU Session Establishment Accept in DL NAS Transport\n"); - handle_pdu_session_accept(pdu_buffer, pdu_length, instance); + handle_pdu_session_accept(nas, pdu_buffer, pdu_length, instance); } else { LOG_E(NAS, "Received unexpected message in DLinformationTransfer %d\n", msg_type); } @@ -2033,7 +2051,7 @@ void *nas_nrue(void *args_p) if (msg_type == FGS_REGISTRATION_ACCEPT) { handle_registration_accept(nas, ba.buf, ba.len); } else if (msg_type == FGS_PDU_SESSION_ESTABLISHMENT_ACC) { - handle_pdu_session_accept(ba.buf, ba.len, nas->UE_id); + handle_pdu_session_accept(nas, ba.buf, ba.len, nas->UE_id); } // Free NAS buffer memory after use (coming from RRC) @@ -2119,7 +2137,7 @@ void *nas_nrue(void *args_p) handle_security_mode_command(nas, &initialNasMsg, pdu_buffer, pdu_length); break; case FGS_DOWNLINK_NAS_TRANSPORT: - handleDownlinkNASTransport(pdu_buffer, pdu_length, nas->UE_id); + handleDownlinkNASTransport(nas, pdu_buffer, pdu_length, nas->UE_id); break; case FGS_REGISTRATION_ACCEPT: handle_registration_accept(nas, pdu_buffer, pdu_length); @@ -2130,7 +2148,7 @@ void *nas_nrue(void *args_p) nas->fiveGMM_state = FGS_DEREGISTERED; break; case FGS_PDU_SESSION_ESTABLISHMENT_ACC: - handle_pdu_session_accept(pdu_buffer, pdu_length, nas->UE_id); + handle_pdu_session_accept(nas, pdu_buffer, pdu_length, nas->UE_id); break; case FGS_PDU_SESSION_ESTABLISHMENT_REJ: LOG_E(NAS, "Received PDU Session Establishment reject\n"); @@ -2178,7 +2196,8 @@ void *nas_nrue(void *args_p) const int pdu_session_id = DEFAULT_NOS1_PDU_ID; const char *ip = "10.0.1.2"; const int qfi = 7; - create_ue_ip_if(ip, NULL, nas->UE_id, pdu_session_id); + const bool is_default = true; + create_ue_ip_if(ip, NULL, nas->UE_id, pdu_session_id, is_default); set_qfi(qfi, pdu_session_id, nas->UE_id); break; } From dc968d4dc965bcf753635f9d8f82360774e5fcdf Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 7 Jan 2026 17:27:51 +0100 Subject: [PATCH 16/20] Handle PDU session type Ethernet in NAS and SDAP --- openair2/SDAP/nr_sdap/nr_sdap.c | 9 +++++++++ openair2/SDAP/nr_sdap/nr_sdap.h | 1 + openair3/NAS/NR_UE/nr_nas_msg.c | 11 +++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index a38550f993..29e5dc93db 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -197,6 +197,15 @@ void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_sess start_sdap_tun_ue(ue_id, pdu_session_id, sock, ifname); // interface name suffix is ue_id+1 } +void create_ue_eth_if(int ue_id, int pdu_session_id, bool is_default) +{ + char ifname[IFNAMSIZ]; + tuntap_generate_ue_ifname(ifname, IFF_TAP, ue_id, is_default ? -1 : pdu_session_id); + const int sock = tuntap_alloc(IFF_TAP, ifname); + tap_config(ifname); // brings the interface up + start_sdap_tun_ue(ue_id, pdu_session_id, sock, ifname); +} + void remove_ip_if(nr_sdap_entity_t *entity) { DevAssert(entity != NULL); diff --git a/openair2/SDAP/nr_sdap/nr_sdap.h b/openair2/SDAP/nr_sdap/nr_sdap.h index 682c97b6fc..bb85fdf249 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.h +++ b/openair2/SDAP/nr_sdap/nr_sdap.h @@ -62,5 +62,6 @@ void sdap_data_ind(int pdcp_entity, void start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id_t ue_id, int pdu_session_id); void create_ue_ip_if(const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id, bool is_default); +void create_ue_eth_if(int ue_id, int pdu_session_id, bool is_default); #endif diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 3add94a549..1e6c98b744 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1568,11 +1568,14 @@ static void handle_pdu_session_accept(const nr_ue_nas_t *nas, uint8_t *pdu_buffe // process PDU Session: pass ID -1 to not append PDU ID to interface bool is_default = idx == 0; - if (msg.pdu_addr_ie.pdu_length) + if (msg.pdu_type == PDU_SESSION_TYPE_ETHER) { + create_ue_eth_if(instance, sm_header.pdu_session_id, is_default); + } else if (msg.pdu_addr_ie.pdu_length) { process_pdu_session_addr(&msg, instance, sm_header.pdu_session_id, is_default); - else - LOG_W(NAS, "Optional PDU Address IE was not provided\n"); - + } else { + LOG_W(NAS, "Unhandled PDU session type %d, ignoring PDU session ID %d\n", msg.pdu_type, sm_header.pdu_session_id); + } + set_qfi(msg.qos_rules.rule->qfi, sm_header.pdu_session_id, instance); } From eea52e4384220d96fe59db8f7370baa3c057f419 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 Oct 2025 14:42:06 +0200 Subject: [PATCH 17/20] Dockerfile nrUE ubuntu: install arping The CN team would like to test the ethernet PDU session capabilities of the UPF. Install arping in the UE by default for ubuntu to help them test the PDU session by pinging over ARP. --- docker/Dockerfile.nrUE.ubuntu | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/Dockerfile.nrUE.ubuntu b/docker/Dockerfile.nrUE.ubuntu index 3b55a64acd..d8c5fd8166 100644 --- a/docker/Dockerfile.nrUE.ubuntu +++ b/docker/Dockerfile.nrUE.ubuntu @@ -39,6 +39,7 @@ ENV TZ=Europe/Paris RUN apt-get update && \ apt-get upgrade --yes && \ apt-get install --yes \ + arping \ software-properties-common \ procps \ libsctp1 \ From 831e407298f0b6a84fd363717c64e9e412c3ecbe Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 8 Jan 2026 09:32:30 +0100 Subject: [PATCH 18/20] Remove outdated comment --- openair3/NAS/NR_UE/nr_nas_msg.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 1e6c98b744..8f4f9bd280 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1710,9 +1710,6 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * const bool has_nssai_sd = pdu_req->sd != 0xffffff; // 0xffffff means "no SD", TS 23.003 const size_t nssai_len = has_nssai_sd ? 4 : 1; mm_msg->snssai.length = nssai_len; - // Fixme: it seems there are a lot of memory errors in this: this value was on the stack, - // but pushed in a itti message to another thread - // this kind of error seems in many places in 5G NAS mm_msg->snssai.value = calloc(1, nssai_len); mm_msg->snssai.value[0] = pdu_req->sst; if (has_nssai_sd) From 6e69491f4a2382a8b831a0cb392cbfeb4d3ca2c8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 26 Jan 2026 14:31:53 +0100 Subject: [PATCH 19/20] Convert legacy PDU session config to new format --- .../conf_files/nrue.band78.106prb.l2sim.conf | 3 +- ci-scripts/conf_files/nrue.uicc.conf | 5 ++-- ci-scripts/conf_files/nrue.uicc.ntn-leo.conf | 3 +- ci-scripts/conf_files/nrue.uicc.yaml | 5 ++-- .../conf_files/nrue.vrtsim.chanmod.yaml | 5 ++-- ci-scripts/conf_files/ue.sa.conf | 3 +- .../untested/episci/episci_nr-ue.nfapi.conf | 4 +-- .../untested/episci/proxy_nr-ue.nfapi.conf | 4 +-- .../yaml_files/5g_rfsimulator/README.md | 3 +- .../5g_rfsimulator_e1/docker-compose.yaml | 4 +-- .../5g_rfsimulator_multiue/nrue.uicc.conf | 30 +++++++------------ doc/NR_SA_Tutorial_OAI_nrUE.md | 3 +- targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf | 4 +-- 13 files changed, 28 insertions(+), 48 deletions(-) diff --git a/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf b/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf index 0dfd31a789..4c382c4c37 100644 --- a/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf +++ b/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf @@ -12,8 +12,7 @@ uicc0 = { imsi = "208990100001100"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; -dnn= "oai"; -nssai_sst=1; +pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } MACRLCs = ( diff --git a/ci-scripts/conf_files/nrue.uicc.conf b/ci-scripts/conf_files/nrue.uicc.conf index 5424abe30f..82ffd140b4 100644 --- a/ci-scripts/conf_files/nrue.uicc.conf +++ b/ci-scripts/conf_files/nrue.uicc.conf @@ -2,8 +2,7 @@ uicc0 = { imsi = "208990100001100"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } position0 = { @@ -40,4 +39,4 @@ channelmod = { ds_tdl = 0; } ); -}; \ No newline at end of file +}; diff --git a/ci-scripts/conf_files/nrue.uicc.ntn-leo.conf b/ci-scripts/conf_files/nrue.uicc.ntn-leo.conf index d8ad297180..71b916edd3 100644 --- a/ci-scripts/conf_files/nrue.uicc.ntn-leo.conf +++ b/ci-scripts/conf_files/nrue.uicc.ntn-leo.conf @@ -2,8 +2,7 @@ uicc0 = { imsi = "208990100001100"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } position0 = { diff --git a/ci-scripts/conf_files/nrue.uicc.yaml b/ci-scripts/conf_files/nrue.uicc.yaml index da3b7e7e19..47d9099df6 100644 --- a/ci-scripts/conf_files/nrue.uicc.yaml +++ b/ci-scripts/conf_files/nrue.uicc.yaml @@ -2,8 +2,9 @@ uicc0: imsi: 208990100001100 key: fec86ba6eb707ed08905757b1bb44b8f opc: C42449363BBAD02B66D16BC975D77CC1 - dnn: oai - nssai_sst: 1 + pdu_sessions: + - dnn: oai + nssai_sst: 1 thread-pool: "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" diff --git a/ci-scripts/conf_files/nrue.vrtsim.chanmod.yaml b/ci-scripts/conf_files/nrue.vrtsim.chanmod.yaml index a9488007d7..6be862e432 100644 --- a/ci-scripts/conf_files/nrue.vrtsim.chanmod.yaml +++ b/ci-scripts/conf_files/nrue.vrtsim.chanmod.yaml @@ -2,8 +2,9 @@ uicc0: imsi: 208990100001100 key: fec86ba6eb707ed08905757b1bb44b8f opc: C42449363BBAD02B66D16BC975D77CC1 - dnn: oai - nssai_sst: 1 + pdu_sessions: + - dnn: oai + nssai_sst: 1 thread-pool: "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" diff --git a/ci-scripts/conf_files/ue.sa.conf b/ci-scripts/conf_files/ue.sa.conf index dc744fb732..d02095ac61 100644 --- a/ci-scripts/conf_files/ue.sa.conf +++ b/ci-scripts/conf_files/ue.sa.conf @@ -2,8 +2,7 @@ uicc0 = { imsi = "208970100001127"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "c42449363bbad02b66d16bc975d77cc1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } thread-pool = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" diff --git a/ci-scripts/conf_files/untested/episci/episci_nr-ue.nfapi.conf b/ci-scripts/conf_files/untested/episci/episci_nr-ue.nfapi.conf index fcdce84334..4603651eb0 100644 --- a/ci-scripts/conf_files/untested/episci/episci_nr-ue.nfapi.conf +++ b/ci-scripts/conf_files/untested/episci/episci_nr-ue.nfapi.conf @@ -12,9 +12,7 @@ uicc0 = { imsi = "208950000000031"; key = "0c0a34601d4f07677303652c0462535b"; opc= "63bfa50ee6523365ff14c1f45f88737d"; -dnn= "oai"; -nssai_sst=222; -nssai_sd=123; +pdu_sessions = ({ dnn = "oai"; nssai_sst = 222; nssai_sd = 123; }); } MACRLCs = ( diff --git a/ci-scripts/conf_files/untested/episci/proxy_nr-ue.nfapi.conf b/ci-scripts/conf_files/untested/episci/proxy_nr-ue.nfapi.conf index 8d6cca014f..b297e509c9 100644 --- a/ci-scripts/conf_files/untested/episci/proxy_nr-ue.nfapi.conf +++ b/ci-scripts/conf_files/untested/episci/proxy_nr-ue.nfapi.conf @@ -12,9 +12,7 @@ uicc0 = { imsi = "2089900007487"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; -dnn= "oai"; -nssai_sst=1; -nssai_sd=1; +pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; nssai_sd = 1; }); } MACRLCs = ( diff --git a/ci-scripts/yaml_files/5g_rfsimulator/README.md b/ci-scripts/yaml_files/5g_rfsimulator/README.md index 629d0860fd..cc4167d0ee 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/README.md +++ b/ci-scripts/yaml_files/5g_rfsimulator/README.md @@ -518,8 +518,7 @@ uicc0 = { imsi = "208990100001100"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } ``` diff --git a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml index 200a1b6e3a..18c6b31553 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml @@ -338,7 +338,7 @@ services: environment: USE_ADDITIONAL_OPTIONS: --rfsim --log_config.global_log_options level,nocolor,time -r 106 --numerology 1 -C 3619200000 - --uicc0.imsi 208990100001101 --uicc0.nssai_sst 2 + --uicc0.imsi 208990100001101 --uicc0.pdu_sessions.[0].nssai_sst 2 --rfsimulator.serveraddr 192.168.78.3 depends_on: - oai-du2 @@ -368,7 +368,7 @@ services: environment: USE_ADDITIONAL_OPTIONS: --rfsim --log_config.global_log_options level,nocolor,time -r 106 --numerology 1 -C 3619200000 - --uicc0.imsi 208990100001102 --uicc0.nssai_sst 3 + --uicc0.imsi 208990100001102 --uicc0.pdu_sessions.[0].nssai_sst 3 --rfsimulator.serveraddr 192.168.78.4 ASAN_OPTIONS: detect_leaks=0 depends_on: diff --git a/ci-scripts/yaml_files/5g_rfsimulator_multiue/nrue.uicc.conf b/ci-scripts/yaml_files/5g_rfsimulator_multiue/nrue.uicc.conf index 92ccf9dca2..32d90b502d 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_multiue/nrue.uicc.conf +++ b/ci-scripts/yaml_files/5g_rfsimulator_multiue/nrue.uicc.conf @@ -2,71 +2,61 @@ uicc0 = { imsi = "208990100001100"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc1 = { imsi = "208990100001101"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc2 = { imsi = "208990100001102"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc3 = { imsi = "208990100001103"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc4 = { imsi = "208990100001104"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc5 = { imsi = "208990100001105"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc6 = { imsi = "208990100001106"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc7 = { imsi = "208990100001107"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc8 = { imsi = "208990100001108"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } uicc9 = { imsi = "208990100001109"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; - dnn= "oai"; - nssai_sst=1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } diff --git a/doc/NR_SA_Tutorial_OAI_nrUE.md b/doc/NR_SA_Tutorial_OAI_nrUE.md index e5024ce053..01a9cc172f 100644 --- a/doc/NR_SA_Tutorial_OAI_nrUE.md +++ b/doc/NR_SA_Tutorial_OAI_nrUE.md @@ -179,8 +179,7 @@ uicc0 = { imsi = "001010000000001"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc = "C42449363BBAD02B66D16BC975D77CC1"; - dnn = "oai"; - nssai_sst = 1; + pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); } ``` diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf index 4cea93ea46..d4f51cd6ea 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf @@ -2,9 +2,7 @@ uicc0 = { imsi = "2089900007487"; key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; -dnn= "oai"; -nssai_sst=1; -nssai_sd=1; +pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; nssai_sd = 1; }); } position0 = { From 780a2467aafcbe8cd1aa8c9d32e90a4d900bd875 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 26 Jan 2026 14:35:15 +0100 Subject: [PATCH 20/20] Add documentation for multi-PDU session configuration --- doc/NR_SA_Tutorial_OAI_nrUE.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/doc/NR_SA_Tutorial_OAI_nrUE.md b/doc/NR_SA_Tutorial_OAI_nrUE.md index 01a9cc172f..7af5dd16b5 100644 --- a/doc/NR_SA_Tutorial_OAI_nrUE.md +++ b/doc/NR_SA_Tutorial_OAI_nrUE.md @@ -188,8 +188,23 @@ uicc0 = { | **IMSI** | Unique identifier for the UE within the mobile network. Used by the network to identify the UE during authentication. It ensures that the UE is correctly identified by the network. | 001010000000001 | | **key** | Cryptographic key shared between the UE and the network, used for encryption during the authentication process. | `fec86ba6eb707ed08905757b1bb44b8f` | | **OPC** | Operator key for the Milenage Authentication and Key Agreement algorithm used for encryption during the authentication process. | Ensures secure communication between the UE and the network by matching the encryption keys. | `C42449363BBAD02B66D16BC975D77CC1` | -| **DNN** | Specifies the name of the data network the UE wishes to connect to, similar to an APN in 4G networks. | `oai` | -| **NSSAI** | Allows the UE to select the appropriate network slice, which provides different QoS. | `1` | +| **DNN** | _Deprecated_: Specifies the name of the data network the UE wishes to connect to, similar to an APN in 4G networks. | `oai` | +| **NSSAI** | _Deprecated_: Allows the UE to select the appropriate network slice, which provides different QoS. | `1` | +| **pdu_sessions** | list of PDU sessions to request | empty array (no PDU session) | + +Note that DNN and NSSAI parameters are deprecated, and `pdu_sessions` should be +used. If the `pdu_sessions` array is present, DNN and NSSAI are ignored. + +Each element within the `pdu_sessions` array takes the following parameters. +Multiple PDU sessions can be requested. + +| **Parameter** | **Description** | **Default Value** | +|---------------|-----------------|-------------------| +| `id` | ID of the PDU session to request | index of the current element (1..16) | +| `type` | Type of the PDU session to request (allowed: `IPv4`, `IPv6`, `IPv4v6`, `Ethernet` | `IPv4` | +| `dnn` | Specifies the name of the data network the UE wishes to connect to | `oai` | +| `nssai_sst` | Slice Service Type to request (1=eMBB, 2=URLLC, 3=mMTC) | `1` | +| `nssai_sd` | Slice Differentiator to request | `0xffffff` (meaning "no SD") | The UE configuration must match the one of the network's AMF. The nrUE can connect by default to OAI CN5G with no need to provide the configuration file.