Merge remote-tracking branch 'origin/add-support-eth-pdu-sess' into integration_2026_w05 (!3769)

Add Ethernet PDU session support and make PDU session request configurable

This MR adds support for Ethernet PDU sessions, and makes PDU sessions
configurable: per-PDU session ID, type (IPv4/IPv4v6/IPv6/Ethernet [1]),
DNN, NSSAI.

Concretely, it adds a new configuration file section pdu_sessions below
uicc0 that looks like this if everything is specified (default values
are shown):

    uicc0:
      [...]
      pdu_sessions:
        - id: 1
          type: "IPv4"
          nssai_sst: 1
          nssai_sd: 0xffffff
          dnn: "oai"

It is possible to list multiple PDU sessions (add new line   - id: 2 to
request a second PDU session). It is backwards-compatible to the current
PDU session configuration (uicc0.dnn/nssai_sst/etc), the idea being that
either legacy or new configuration could be used, but (1) if
pdu_sessions exist, it will overwrite the legacy configuration, and (2)
multiple PDU sessions, and type, can only be configured using the new
config. The old parameters --extra-pdu-id and --default-pdu-id are not
accepted anymore. Documentation has been added to the UE tutorial.

It is possible to test an Ethernet PDU session [1]. It is possible to
test the PDU session inside the UE by verifying it has an TAP interface
oaitap_ue1, and ARPing the data network (arping has been added to
oai-nr-ue container for that purpose):

    ip addr add 192.168.72.140/26 dev oaitap_ue1  # same subnet as data network!
    ip link set dev oaitap_ue1 up
    arping -c 20 -I oaitap_ue1 192.168.72.135     # will send ARP request in Ethernet frame to get IP address of data network

[1] See description MR !3769 for more information. On the UE side, only
the type has to be changed to "Ethernet".
This commit is contained in:
Robert Schmidt
2026-01-29 07:52:48 +01:00
49 changed files with 483 additions and 190 deletions
@@ -12,8 +12,7 @@ uicc0 = {
imsi = "208990100001100";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai";
nssai_sst=1;
pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; });
}
MACRLCs = (
+11
View File
@@ -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"
+1 -2
View File
@@ -2,8 +2,7 @@ uicc0 = {
imsi = "208990100001100";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai";
nssai_sst=1;
pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; });
}
position0 = {
+1 -2
View File
@@ -2,8 +2,7 @@ uicc0 = {
imsi = "208990100001100";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai";
nssai_sst=1;
pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; });
}
position0 = {
+3 -2
View File
@@ -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"
@@ -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"
+1 -2
View File
@@ -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"
@@ -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 = (
@@ -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 = (
@@ -518,8 +518,7 @@ uicc0 = {
imsi = "208990100001100";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai";
nssai_sst=1;
pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; });
}
```
@@ -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.[0].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.[0].serveraddr 192.168.78.4
ASAN_OPTIONS: detect_leaks=0
depends_on:
@@ -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; });
}
thread-pool: "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
@@ -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.[0].serveraddr 192.168.71.141 --extra-pdu-id 2 --log_config.global_log_options level,nocolor,time
--rfsimulator.[0].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
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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");
+1 -1
View File
@@ -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);
+3
View File
@@ -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__ */
+1 -1
View File
@@ -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)
+5 -1
View File
@@ -172,7 +172,11 @@ 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};
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;
}
@@ -27,10 +27,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/ipv6.h>
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include "tun_if.h"
#include "tuntap_if.h"
#include "common/platform_constants.h"
#include "common/utils/LOG/log.h"
#include "common/utils/system.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));
@@ -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,11 +232,13 @@ 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);
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);
@@ -239,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;
@@ -267,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);
@@ -283,11 +311,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);
}
@@ -24,6 +24,7 @@
#include <stdbool.h>
#include <net/if.h>
#include <linux/if_tun.h>
/*!
* \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_*/
+2 -2
View File
@@ -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 \
+18 -4
View File
@@ -173,8 +173,7 @@ uicc0 = {
imsi = "001010000000001";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc = "C42449363BBAD02B66D16BC975D77CC1";
dnn = "oai";
nssai_sst = 1;
pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; });
}
```
@@ -183,8 +182,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.
+1
View File
@@ -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 \
+2 -4
View File
@@ -168,10 +168,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
+4
View File
@@ -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)
+1 -1
View File
@@ -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
+1
View File
@@ -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;
/*
+1 -1
View File
@@ -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 <pthread.h>
+2 -2
View File
@@ -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,
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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);
+25 -19
View File
@@ -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)
@@ -157,25 +157,27 @@ 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";
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);
}
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);
@@ -183,22 +185,32 @@ void start_sdap_tun_ue(ue_id_t ue_id, int pdu_session_id, int sock)
}
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];
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, is_default ? -1 : pdu_session_id);
const int sock = tuntap_alloc(IFF_TUN, ifname);
tun_config(ifname, ipv4, ipv6);
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 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);
DevAssert(entity->pdusession_if_name != NULL);
// Stop the read thread
entity->stop_thread = true;
@@ -208,13 +220,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);
tuntap_destroy(entity->pdusession_if_name);
free(entity->pdusession_if_name);
entity->pdusession_if_name = NULL;
}
+3 -3
View File
@@ -60,8 +60,8 @@ 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);
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
+2 -2
View File
@@ -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
@@ -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);
}
}
+1
View File
@@ -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;
@@ -23,7 +23,6 @@
#include <arpa/inet.h>
#include "PduSessionEstablishmentAccept.h"
#include "common/utils/LOG/log.h"
#include "common/utils/tun_if.h"
#include "fgs_nas_utils.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
+74 -39
View File
@@ -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"
@@ -1473,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;
@@ -1481,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: {
@@ -1495,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:
@@ -1507,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;
@@ -1545,22 +1547,42 @@ 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;
}
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_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, "Unhandled PDU session type %d, ignoring PDU session ID %d\n", msg.pdu_type, sm_header.pdu_session_id);
}
// process PDU Session
if (msg.pdu_addr_ie.pdu_length)
process_pdu_session_addr(&msg, instance, sm_header.pdu_session_id);
else
LOG_W(NAS, "Optional PDU Address IE was not provided\n");
set_qfi(msg.qos_rules.rule->qfi, sm_header.pdu_session_id, instance);
}
/**
* @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);
@@ -1569,7 +1591,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);
}
@@ -1688,19 +1710,16 @@ 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)
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
@@ -1772,24 +1791,33 @@ 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, 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",
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(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 = 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 = 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);
}
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;
@@ -1864,12 +1892,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");
}
if (get_user_nssai_idx(msg.nas_allowed_nssai, nas) < 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);
if (get_nrUE_params()->extra_pdu_id != -1) {
request_pdusession(nas, get_nrUE_params()->extra_pdu_id);
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
@@ -2017,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)
@@ -2103,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);
@@ -2114,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");
@@ -2159,10 +2193,11 @@ 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);
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;
}
+2 -1
View File
@@ -32,6 +32,7 @@
#define __NR_NAS_MSG_SIM_H__
#include <common/utils/assertions.h>
#include "common/5g_platform_types.h"
#include <openair3/UICC/usim_interface.h>
#include <stdbool.h>
#include <stdint.h>
@@ -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, const pdu_session_config_t *pdu);
#endif /* __NR_NAS_MSG_SIM_H__*/
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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)
+116
View File
@@ -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 <assert.h>
#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(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);
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), uiccName);
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;
}
+36
View File
@@ -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(const char *uiccName, pdu_session_config_t *configs, int max_len);
#endif /* PDU_SESSION_H_ */
+32 -4
View File
@@ -22,6 +22,7 @@
*/
#include <ctype.h>
#include "common/platform_constants.h"
#include <openair3/UICC/usim_interface.h>
#include <openair3/NAS/COMMON/milenage.h>
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];
}
+3
View File
@@ -34,6 +34,7 @@
#include <common/utils/load_module_shlib.h>
#include <common/config/config_userapi.h>
#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];
+1 -3
View File
@@ -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 = {