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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_*/
|
||||
Reference in New Issue
Block a user