Paging UE (SDAP): reattach TUN after idle reset

UE paging can recreate the AS-side SDAP entity while NAS still keeps the
PDU session established. Per TS 38.304 clause 7.1 and TS 24.501 clauses
5.6.2.2.1,5.6.1.2 paging/service request restores user-plane resources
for an established PDU session, it does not imply deleting the UE IP/TUN
interface.

Deleting the UE TUN with SDAP entity teardown breaks user-plane continuity
after re-attach. We need SDAP entity lifetime and UE TUN lifetime to be
decoupled. This commit keeps a UE/PDU-session TUN registry outside the
SDAP entity lifetime. When a UE SDAP entity is recreated, reattach it
to the preserved TUN socket and restart the reader thread. On first PDU
session setup, NAS registers the TUN before attaching the reader.

The sequence is the following: (1) NAS register the iface
(2) sdap entity attaches to the iface (3) UE goes IDLE, SDAP
entity is destroyed, iface is deatched and brought down (4)
UE Service Request, SDAP entity is created (5) iface reattaches
to the SDAP entity and is brought up

UE reattach assumes the PDU session TUN interface was registered
before the SDAP entity is recreated.

DL/UL data is intentionally dropped when SDAP is not yet attached
or QFI is unavailable, rather than queued in this layer.

Changes:
- `nr_sdap.c`: keep a UE/PDU-session TUN registry, duplicate the stored
  socket into each SDAP entity, and add attach/detach/destroy helpers.
- `nr_sdap_entity.c`: detach SDAP entities from their TUN reader on
  deletion, preserve UE TUN interfaces across idle cleanup, and destroy
  gNB TUN interfaces when the gNB entity is removed. Drop downlink SDUs
  if the UE SDAP entity is not attached to a TUN socket yet.
- `openair3/NAS/NR_UE/nr_nas_msg.c`: set QFI before UE TUN creation so
  first attach can pick up cached QFI.
- `common/utils/tuntap_if.c`: add `tuntap_set_up`/`tuntap_set_down` helpers
  for UE interface state reflection and initialize `flags` in `tuntap_destroy`
  to avoid maybe-uninitialized build failures.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
This commit is contained in:
Guido Casati
2026-05-14 20:10:58 +02:00
parent 74f661f3d6
commit 8d2895463b
8 changed files with 247 additions and 58 deletions

View File

@@ -184,6 +184,18 @@ static bool set_if_flags(int sock_fd, const char *ifn, short flags)
return true;
}
short tuntap_set_up(const char *ifname, int sock_fd)
{
short flags = 0;
if (!get_if_flags(sock_fd, ifname, &flags))
return -1;
flags |= IFF_UP;
if (!set_if_flags(sock_fd, ifname, flags))
return -1;
return flags;
}
bool tun_config(const char* ifname, const char *ipv4, const char *ipv6)
{
@@ -215,10 +227,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 = set_if_flags(sock_fd, ifname, (IFF_UP | IFF_NOARP | IFF_POINTOPOINT) & ~IFF_MULTICAST);
if (success) {
const short flags = tuntap_set_up(ifname, sock_fd);
success = flags >= 0 && set_if_flags(sock_fd, ifname, (flags | IFF_NOARP | IFF_POINTOPOINT) & ~IFF_MULTICAST);
}
if (success)
LOG_A(OIP, "TUN Interface %s successfully configured, IPv4 %s, IPv6 %s\n", ifname, ipv4, ipv6);
@@ -233,11 +245,11 @@ 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));
LOG_E(UTIL, "tap_config: failed creating socket %d, %s\n", errno, strerror(errno));
return false;
}
bool success = set_if_flags(sock_fd, ifname, IFF_UP);
const bool success = tuntap_set_up(ifname, sock_fd) >= 0;
if (success)
LOG_A(OIP, "TAP interface %s successfully configured\n", ifname);
@@ -282,6 +294,7 @@ int tuntap_generate_ue_ifname(char *ifname, int flag, int instance_id, int pdu_s
char pdu_session_string[10];
snprintf(pdu_session_string, sizeof(pdu_session_string), "p%d", pdu_session_id);
const char *basename = flag == IFF_TUN ? "oaitun_ue" : "oaitap_ue";
// ifname: oaitun_ue<ue_id+1>[p<pdu_session_id>] when not default
return snprintf(ifname, IFNAMSIZ, "%s%d%s", basename, instance_id + 1, pdu_session_id == -1 ? "" : pdu_session_string);
}
@@ -295,7 +308,7 @@ void tuntap_destroy(const char *dev)
}
// interface not up => down
short flags;
short flags = 0;
bool success = get_if_flags(fd, dev, &flags);
success = success && set_if_flags(fd, dev, flags & ~IFF_UP);
if (success) {

View File

@@ -90,4 +90,12 @@ int tuntap_alloc(int flag, const char *dev);
*/
void tuntap_destroy(const char *dev);
/*!
* \brief Bring a TUN/TAP interface administratively up (IOCTL SIOCSIFFLAGS, set IFF_UP).
* \param[in] ifname name of the interface
* \param[in] sock_fd IPv4 SOCK_DGRAM fd for ioctl (opened by the caller)
* \return interface flags with IFF_UP set, or -1 on failure
*/
short tuntap_set_up(const char *ifname, int sock_fd);
#endif /*TUN_IF_H_*/