Files
openairinterface5g/openair3/ocp-gtpu/gtp_itf.cpp
Robert Schmidt 5d9db1688a Refactor port number out of newGtpuCreateTunnel()
The (remote) port number of the GTP module is configured during
initialization. It is therefore not required when calling
newGtpuCreateTunnel(), and could even lead to bugs (if another port
number is used than specified during GTP init).
2025-08-04 12:34:23 +02:00

1337 lines
49 KiB
C++

#include <map>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include "common/platform_types.h"
#include "common/utils/system.h"
#include <openair3/UTILS/conversions.h>
#include "common/utils/LOG/log.h"
#include <common/utils/ocp_itti/intertask_interface.h>
#include <openair2/COMMON/gtpv1_u_messages_types.h>
#include <openair3/ocp-gtpu/gtp_itf.h>
#include <openair2/LAYER2/PDCP_v10.1.0/pdcp.h>
#include <openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h>
#include <openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h>
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
#include "sim.h"
#pragma pack(1)
typedef struct Gtpv1uMsgHeader {
uint8_t PN: 1;
uint8_t S: 1;
uint8_t E: 1;
uint8_t spare: 1;
uint8_t PT: 1;
uint8_t version: 3;
uint8_t msgType;
uint16_t msgLength;
teid_t teid;
} __attribute__((packed)) Gtpv1uMsgHeaderT;
// TS 38.425, Figure 5.5.2.2-1
typedef struct DlDataDeliveryStatus_flags {
uint8_t LPR: 1; // Lost packet report
uint8_t FFI: 1; // Final Frame Ind
uint8_t deliveredPdcpSn: 1; // Highest Delivered NR PDCP SN Ind
uint8_t transmittedPdcpSn: 1; // Highest Transmitted NR PDCP SN Ind
uint8_t pduType: 4; // PDU type
uint8_t CR: 1; // Cause Report
uint8_t deliveredReTxPdcpSn: 1; // Delivered retransmitted NR PDCP SN Ind
uint8_t reTxPdcpSn: 1; // Retransmitted NR PDCP SN Ind
uint8_t DRI: 1; // Data Rate Indication
uint8_t deliveredPdcpSnRange: 1; // Delivered NR PDCP SN Range Ind
uint8_t spare: 3;
uint32_t drbBufferSize; // Desired buffer size for the data radio bearer
} __attribute__((packed)) DlDataDeliveryStatus_flagsT;
typedef struct Gtpv1uMsgHeaderOptFields {
uint8_t seqNum1Oct;
uint8_t seqNum2Oct;
uint8_t NPDUNum;
uint8_t NextExtHeaderType;
} __attribute__((packed)) Gtpv1uMsgHeaderOptFieldsT;
#define DL_PDU_SESSION_INFORMATION 0
#define UL_PDU_SESSION_INFORMATION 1
typedef struct PDUSessionContainer {
uint8_t spare: 4;
uint8_t PDU_type: 4;
uint8_t QFI: 6;
uint8_t Reflective_QoS_activation: 1;
uint8_t Paging_Policy_Indicator: 1;
} __attribute__((packed)) PDUSessionContainerT;
typedef struct Gtpv1uExtHeader {
uint8_t ExtHeaderLen;
PDUSessionContainerT pdusession_cntr;
uint8_t NextExtHeaderType;
} __attribute__((packed)) Gtpv1uExtHeaderT;
#pragma pack()
// TS 29.281, fig 5.2.1-3
#define PDU_SESSION_CONTAINER (0x85)
#define NR_RAN_CONTAINER (0x84)
// TS 29.281, 5.2.1
#define EXT_HDR_LNTH_OCTET_UNITS (4)
#define NO_MORE_EXT_HDRS (0)
// TS 29.060, table 7.1 defines the possible message types
// here are all the possible messages (3GPP R16)
#define GTP_ECHO_REQ (1)
#define GTP_ECHO_RSP (2)
#define GTP_ERROR_INDICATION (26)
#define GTP_SUPPORTED_EXTENSION_HEADER_INDICATION (31)
#define GTP_END_MARKER (254)
#define GTP_GPDU (255)
typedef struct gtpv1u_bearer_s {
/* TEID used in dl and ul */
teid_t teid_incoming; ///< eNB TEID
teid_t teid_outgoing; ///< Remote TEID
in_addr_t outgoing_ip_addr;
struct in6_addr outgoing_ip6_addr;
tcp_udp_port_t outgoing_port;
uint16_t seqNum;
uint8_t npduNum;
int outgoing_qfi;
} gtpv1u_bearer_t;
typedef struct {
map<ue_id_t, gtpv1u_bearer_t> bearers;
} teidData_t;
typedef struct {
ue_id_t ue_id;
ebi_t incoming_rb_id;
gtpCallback callBack;
teid_t outgoing_teid;
gtpCallbackSDAP callBackSDAP;
int pdusession_id;
} ueidData_t;
typedef struct {
int h;
pthread_t t;
} gtpThread_t;
class gtpEndPoint {
public:
openAddr_t addr;
uint8_t foundAddr[20];
int foundAddrLen;
int ipVersion;
gtpThread_t thrData;
map<uint64_t, teidData_t> ue2te_mapping;
// we use the same port number for source and destination address
// this allow using non standard gtp port number (different from 2152)
// and so, for example tu run 4G and 5G cores on one system
tcp_udp_port_t get_dstport()
{
return (tcp_udp_port_t)atol(addr.destinationService);
}
};
class gtpEndPoints {
public:
pthread_mutex_t gtp_lock = PTHREAD_MUTEX_INITIALIZER;
// the instance id will be the Linux socket handler, as this is uniq
map<uint64_t, gtpEndPoint> instances;
map<uint64_t, ueidData_t> te2ue_mapping;
gtpEndPoints()
{
unsigned int seed;
fill_random(&seed, sizeof(seed));
srandom(seed);
}
~gtpEndPoints()
{
// automatically close all sockets on quit
for (const auto &p : instances)
close(p.first);
}
};
static gtpEndPoints globGtp;
// note TEid 0 is reserved for specific usage: echo req/resp, error and supported extensions
static teid_t gtpv1uNewTeid(void)
{
#ifdef GTPV1U_LINEAR_TEID_ALLOCATION
g_gtpv1u_teid = g_gtpv1u_teid + 1;
return g_gtpv1u_teid;
#else
return random() + random() % (RAND_MAX - 1) + 1;
#endif
}
instance_t legacyInstanceMapping = 0;
#define compatInst(a) ((a) == 0 || (a) == INSTANCE_DEFAULT ? legacyInstanceMapping : a)
#define getInstRetVoid(insT) \
auto instChk = globGtp.instances.find(compatInst(insT)); \
if (instChk == globGtp.instances.end()) { \
LOG_E(GTPU, "try to get a gtp-u not existing output\n"); \
pthread_mutex_unlock(&globGtp.gtp_lock); \
return; \
} \
gtpEndPoint *inst = &instChk->second;
#define getInstRetInt(insT) \
auto instChk = globGtp.instances.find(compatInst(insT)); \
if (instChk == globGtp.instances.end()) { \
LOG_E(GTPU, "try to get a gtp-u not existing output\n"); \
pthread_mutex_unlock(&globGtp.gtp_lock); \
return GTPNOK; \
} \
gtpEndPoint *inst = &instChk->second;
#define getUeRetVoid(insT, Ue) \
auto ptrUe = insT->ue2te_mapping.find(Ue); \
\
if (ptrUe == insT->ue2te_mapping.end()) { \
LOG_E(GTPU, "[%ld] %s failed: while getting ue id %ld in hashtable ue_mapping\n", instance, __func__, ue_id); \
pthread_mutex_unlock(&globGtp.gtp_lock); \
return; \
}
#define getUeRetInt(insT, Ue) \
auto ptrUe = insT->ue2te_mapping.find(Ue); \
\
if (ptrUe == insT->ue2te_mapping.end()) { \
LOG_E(GTPU, "[%ld] %s failed: while getting ue id %ld in hashtable ue_mapping\n", instance, __func__, ue_id); \
pthread_mutex_unlock(&globGtp.gtp_lock); \
return GTPNOK; \
}
#define HDR_MAX 256 // 256 is supposed to be larger than any gtp header
static int gtpv1uCreateAndSendMsg(int h,
uint32_t peerIp,
uint16_t peerPort,
int msgType,
teid_t teid,
uint8_t *Msg,
int msgLen,
bool seqNumFlag,
bool npduNumFlag,
int seqNum,
int npduNum,
int extHdrType,
uint8_t *extensionHeader_buffer,
uint8_t extensionHeader_length)
{
LOG_D(GTPU, "Peer IP:%u peer port:%u outgoing teid:%u \n", peerIp, peerPort, teid);
DevAssert(msgLen + HDR_MAX < 65536); // maximum size of UDP packet
uint8_t buffer[msgLen + HDR_MAX];
uint8_t *curPtr = buffer;
Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)buffer;
// N should be 0 for us (it was used only in 2G and 3G)
msgHdr->PN = npduNumFlag;
msgHdr->S = seqNumFlag;
msgHdr->E = extHdrType != NO_MORE_EXT_HDRS;
msgHdr->spare = 0;
// PT=0 is for GTP' TS 32.295 (charging)
msgHdr->PT = 1;
msgHdr->version = 1;
msgHdr->msgType = msgType;
msgHdr->teid = htonl(teid);
curPtr += sizeof(Gtpv1uMsgHeaderT);
if (seqNumFlag || (extHdrType != NO_MORE_EXT_HDRS) || npduNumFlag) {
*(uint16_t *)curPtr = seqNumFlag ? seqNum : 0x0000;
curPtr += sizeof(uint16_t);
*(uint8_t *)curPtr = npduNumFlag ? npduNum : 0x00;
curPtr++;
*(uint8_t *)curPtr = extHdrType;
curPtr++;
}
// Bug: if there is more than one extension, infinite loop on extensionHeader_buffer
while (extHdrType != NO_MORE_EXT_HDRS) {
if (extensionHeader_length > 0) {
memcpy(curPtr, extensionHeader_buffer, extensionHeader_length);
curPtr += extensionHeader_length;
LOG_D(GTPU,
"Extension Header for DDD added. The length is: %d, extension header type is: %x \n",
extensionHeader_length,
*((uint8_t *)(buffer + 11)));
extHdrType = extensionHeader_buffer[extensionHeader_length - 1];
LOG_D(GTPU, "Next extension header type is: %x \n", *((uint8_t *)(buffer + 11)));
} else {
LOG_W(GTPU, "Extension header type not supported, returning... \n");
}
}
if (Msg != NULL) {
memcpy(curPtr, Msg, msgLen);
curPtr += msgLen;
}
msgHdr->msgLength = htons(curPtr - (buffer + sizeof(Gtpv1uMsgHeaderT)));
AssertFatal(curPtr - (buffer + msgLen) < HDR_MAX, "fixed max size of all headers too short");
// Fix me: add IPv6 support, using flag ipVersion
struct sockaddr_in to = {0};
to.sin_family = AF_INET;
to.sin_port = htons(peerPort);
to.sin_addr.s_addr = peerIp;
LOG_D(GTPU, "sending packet size: %ld to %s\n", curPtr - buffer, inet_ntoa(to.sin_addr));
int ret;
if ((ret = sendto(h, (void *)buffer, curPtr - buffer, 0, (struct sockaddr *)&to, sizeof(to))) != curPtr - buffer) {
LOG_E(GTPU,
"[SD %d] Failed to send data to " IPV4_ADDR " on port %d, buffer size %lu, ret: %d, errno: %d\n",
h,
IPV4_ADDR_FORMAT(peerIp),
peerPort,
curPtr - buffer,
ret,
errno);
return GTPNOK;
}
return !GTPNOK;
}
void gtpv1uSendDirect(instance_t instance,
ue_id_t ue_id,
int bearer_id,
uint8_t *buf,
size_t len,
bool seqNumFlag,
bool npduNumFlag)
{
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetVoid(compatInst(instance));
getUeRetVoid(inst, ue_id);
auto ptr2 = ptrUe->second.bearers.find(bearer_id);
if (ptr2 == ptrUe->second.bearers.end()) {
LOG_E(GTPU, "[%ld] GTP-U instance: sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id);
pthread_mutex_unlock(&globGtp.gtp_lock);
return;
}
LOG_D(GTPU,
"[%ld] sending a packet to UE:RAB:teid %lx/%x/%x, len %lu, oldseq %d, oldnum %d\n",
instance,
ue_id,
bearer_id,
ptr2->second.teid_outgoing,
len,
ptr2->second.seqNum,
ptr2->second.npduNum);
if (seqNumFlag)
ptr2->second.seqNum++;
if (npduNumFlag)
ptr2->second.npduNum++;
// copy to release the mutex
gtpv1u_bearer_t tmp = ptr2->second;
pthread_mutex_unlock(&globGtp.gtp_lock);
if (tmp.outgoing_qfi != -1) {
Gtpv1uExtHeaderT ext = {0};
ext.ExtHeaderLen = 1; // in quad bytes EXT_HDR_LNTH_OCTET_UNITS
ext.pdusession_cntr.spare = 0;
ext.pdusession_cntr.PDU_type = UL_PDU_SESSION_INFORMATION;
ext.pdusession_cntr.QFI = tmp.outgoing_qfi;
ext.pdusession_cntr.Reflective_QoS_activation = false;
ext.pdusession_cntr.Paging_Policy_Indicator = false;
ext.NextExtHeaderType = NO_MORE_EXT_HDRS;
gtpv1uCreateAndSendMsg(compatInst(instance),
tmp.outgoing_ip_addr,
tmp.outgoing_port,
GTP_GPDU,
tmp.teid_outgoing,
buf,
len,
seqNumFlag,
npduNumFlag,
tmp.seqNum,
tmp.npduNum,
PDU_SESSION_CONTAINER,
(uint8_t *)&ext,
sizeof(ext));
} else {
gtpv1uCreateAndSendMsg(compatInst(instance),
tmp.outgoing_ip_addr,
tmp.outgoing_port,
GTP_GPDU,
tmp.teid_outgoing,
buf,
len,
seqNumFlag,
npduNumFlag,
tmp.seqNum,
tmp.npduNum,
NO_MORE_EXT_HDRS,
NULL,
0);
}
}
static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req)
{
ue_id_t ue_id = req->rnti;
int bearer_id = req->rab_id;
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetVoid(compatInst(instance));
getUeRetVoid(inst, ue_id);
auto ptr2 = ptrUe->second.bearers.find(bearer_id);
if (ptr2 == ptrUe->second.bearers.end()) {
LOG_E(GTPU, "[%ld] GTP-U sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id);
pthread_mutex_unlock(&globGtp.gtp_lock);
return;
}
LOG_D(GTPU,
"[%ld] sending a end packet packet to UE:RAB:teid %lx/%x/%x\n",
instance,
ue_id,
bearer_id,
ptr2->second.teid_outgoing);
gtpv1u_bearer_t tmp = ptr2->second;
pthread_mutex_unlock(&globGtp.gtp_lock);
Gtpv1uMsgHeaderT msgHdr;
// N should be 0 for us (it was used only in 2G and 3G)
msgHdr.PN = 0;
msgHdr.S = 0;
msgHdr.E = 0;
msgHdr.spare = 0;
// PT=0 is for GTP' TS 32.295 (charging)
msgHdr.PT = 1;
msgHdr.version = 1;
msgHdr.msgType = GTP_END_MARKER;
msgHdr.msgLength = htons(0);
msgHdr.teid = htonl(tmp.teid_outgoing);
// Fix me: add IPv6 support, using flag ipVersion
static struct sockaddr_in to = {0};
to.sin_family = AF_INET;
to.sin_port = htons(tmp.outgoing_port);
to.sin_addr.s_addr = tmp.outgoing_ip_addr;
char ip4[INET_ADDRSTRLEN];
// char ip6[INET6_ADDRSTRLEN];
LOG_D(GTPU, "[%ld] sending end packet to %s\n", instance, inet_ntoa(to.sin_addr));
if (sendto(compatInst(instance), (void *)&msgHdr, sizeof(msgHdr), 0, (struct sockaddr *)&to, sizeof(to)) != sizeof(msgHdr)) {
LOG_E(GTPU,
"[%ld] Failed to send data to %s on port %d, buffer size %lu\n",
compatInst(instance),
inet_ntop(AF_INET, &tmp.outgoing_ip_addr, ip4, INET_ADDRSTRLEN),
tmp.outgoing_port,
sizeof(msgHdr));
}
}
static int udpServerSocket(openAddr_s addr)
{
LOG_I(GTPU, "Initializing UDP for local address %s with port %s\n", addr.originHost, addr.originService);
int status;
struct addrinfo hints = {0}, *servinfo, *p;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((status = getaddrinfo(addr.originHost, addr.originService, &hints, &servinfo)) != 0) {
LOG_E(GTPU, "getaddrinfo error: %s\n", gai_strerror(status));
return -1;
}
int sockfd = -1;
// loop through all the results and bind to the first we can
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
LOG_W(GTPU, "socket: %s\n", strerror(errno));
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
LOG_W(GTPU, "bind: %s\n", strerror(errno));
continue;
} else {
// We create the gtp instance on the socket
globGtp.instances[sockfd].addr = addr;
if (p->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
memcpy(globGtp.instances[sockfd].foundAddr, &ipv4->sin_addr.s_addr, sizeof(ipv4->sin_addr.s_addr));
globGtp.instances[sockfd].foundAddrLen = sizeof(ipv4->sin_addr.s_addr);
globGtp.instances[sockfd].ipVersion = 4;
break;
} else if (p->ai_family == AF_INET6) {
LOG_W(GTPU, "Local address is IP v6\n");
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
memcpy(globGtp.instances[sockfd].foundAddr, &ipv6->sin6_addr.s6_addr, sizeof(ipv6->sin6_addr.s6_addr));
globGtp.instances[sockfd].foundAddrLen = sizeof(ipv6->sin6_addr.s6_addr);
globGtp.instances[sockfd].ipVersion = 6;
} else
AssertFatal(false, "Local address is not IPv4 or IPv6");
}
break; // if we get here, we must have connected successfully
}
freeaddrinfo(servinfo); // all done with this structure
if (p == NULL) {
// looped off the end of the list with no successful bind
LOG_E(GTPU, "failed to bind socket: %s %s \n", addr.originHost, addr.originService);
return -1;
}
int sendbuff = 1000 * 1000 * 10;
AssertFatal(0 == setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)), "");
LOG_D(GTPU,
"[%d] Created listener for paquets to: %s:%s, send buffer size: %d\n",
sockfd,
addr.originHost,
addr.originService,
sendbuff);
return sockfd;
}
static void* gtpv1uReceiver(void *thr);
instance_t gtpv1Init(openAddr_t context)
{
pthread_mutex_lock(&globGtp.gtp_lock);
int id = udpServerSocket(context);
if (id >= 0) {
LOG_I(GTPU, "Created gtpu instance id: %d\n", id);
getInstRetInt(compatInst(id));
inst->thrData.h = id;
char name[32];
snprintf(name, sizeof(name), "GTPrx_%d", id);
threadCreate(&inst->thrData.t, gtpv1uReceiver, &inst->thrData, name, -1, OAI_PRIORITY_RT);
} else
LOG_E(GTPU, "can't create GTP-U instance\n");
pthread_mutex_unlock(&globGtp.gtp_lock);
return id;
}
void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance,
ue_id_t ue_id,
ebi_t bearer_id,
in_addr_t newOutgoingAddr,
teid_t newOutgoingTeid)
{
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetVoid(compatInst(instance));
getUeRetVoid(inst, ue_id);
auto ptr2 = ptrUe->second.bearers.find(bearer_id);
if (ptr2 == ptrUe->second.bearers.end()) {
LOG_E(GTPU, "[%ld] Update tunnel for a existing ue id %lu, but wrong bearer_id %u\n", instance, ue_id, bearer_id);
pthread_mutex_unlock(&globGtp.gtp_lock);
return;
}
ptr2->second.outgoing_ip_addr = newOutgoingAddr;
ptr2->second.teid_outgoing = newOutgoingTeid;
LOG_I(GTPU,
"[%ld] Tunnel Outgoing TEID updated to %x and address to %x\n",
instance,
ptr2->second.teid_outgoing,
ptr2->second.outgoing_ip_addr);
pthread_mutex_unlock(&globGtp.gtp_lock);
return;
}
teid_t newGtpuCreateTunnel(instance_t instance,
ue_id_t ue_id,
int incoming_bearer_id,
int outgoing_bearer_id,
teid_t outgoing_teid,
int outgoing_qfi,
transport_layer_addr_t remoteAddr,
gtpCallback callBack,
gtpCallbackSDAP callBackSDAP)
{
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
auto it = inst->ue2te_mapping.find(ue_id);
if (it != inst->ue2te_mapping.end() && it->second.bearers.find(outgoing_bearer_id) != it->second.bearers.end()) {
LOG_W(GTPU, "[%ld] Create a config for a already existing GTP tunnel (ue id %lu)\n", instance, ue_id);
inst->ue2te_mapping.erase(it);
}
teid_t incoming_teid = gtpv1uNewTeid();
while (globGtp.te2ue_mapping.find(incoming_teid) != globGtp.te2ue_mapping.end()) {
LOG_W(GTPU, "[%ld] generated a random Teid that exists, re-generating (%x)\n", instance, incoming_teid);
incoming_teid = gtpv1uNewTeid();
};
globGtp.te2ue_mapping[incoming_teid].ue_id = ue_id;
globGtp.te2ue_mapping[incoming_teid].incoming_rb_id = incoming_bearer_id;
globGtp.te2ue_mapping[incoming_teid].outgoing_teid = outgoing_teid;
globGtp.te2ue_mapping[incoming_teid].callBack = callBack;
globGtp.te2ue_mapping[incoming_teid].callBackSDAP = callBackSDAP;
globGtp.te2ue_mapping[incoming_teid].pdusession_id = (uint8_t)outgoing_bearer_id;
gtpv1u_bearer_t *tmp = &inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id];
int addrs_length_in_bytes = remoteAddr.length / 8;
switch (addrs_length_in_bytes) {
case 4:
memcpy(&tmp->outgoing_ip_addr, remoteAddr.buffer, 4);
break;
case 16:
memcpy(tmp->outgoing_ip6_addr.s6_addr, remoteAddr.buffer, 16);
break;
case 20:
memcpy(&tmp->outgoing_ip_addr, remoteAddr.buffer, 4);
memcpy(tmp->outgoing_ip6_addr.s6_addr, remoteAddr.buffer + 4, 16);
default:
AssertFatal(false, "SGW Address size impossible");
}
tmp->teid_incoming = incoming_teid;
tmp->outgoing_port = inst->get_dstport();
tmp->teid_outgoing = outgoing_teid;
tmp->outgoing_qfi = outgoing_qfi;
pthread_mutex_unlock(&globGtp.gtp_lock);
char ip4[INET_ADDRSTRLEN];
char ip6[INET6_ADDRSTRLEN];
LOG_I(GTPU,
"[%ld] Created tunnel for UE ID %lu, teid for incoming: %x, teid for outgoing %x to remote IPv4: %s, IPv6 %s\n",
instance,
ue_id,
tmp->teid_incoming,
tmp->teid_outgoing,
inet_ntop(AF_INET, (void *)&tmp->outgoing_ip_addr, ip4, INET_ADDRSTRLEN),
inet_ntop(AF_INET6, (void *)&tmp->outgoing_ip6_addr.s6_addr, ip6, INET6_ADDRSTRLEN));
return incoming_teid;
}
int gtpv1u_create_s1u_tunnel(instance_t instance,
const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req,
gtpv1u_enb_create_tunnel_resp_t *create_tunnel_resp,
gtpCallback callBack)
{
LOG_D(GTPU,
"[%ld] Start create tunnels for UE ID %u, num_tunnels %d, sgw_S1u_teid %x\n",
instance,
create_tunnel_req->rnti,
create_tunnel_req->num_tunnels,
create_tunnel_req->sgw_S1u_teid[0]);
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
uint8_t addr[inst->foundAddrLen];
memcpy(addr, inst->foundAddr, inst->foundAddrLen);
pthread_mutex_unlock(&globGtp.gtp_lock);
for (int i = 0; i < create_tunnel_req->num_tunnels; i++) {
AssertFatal(create_tunnel_req->eps_bearer_id[i] > 4,
"From legacy code not clear, seems impossible (bearer=%d)\n",
create_tunnel_req->eps_bearer_id[i]);
int incoming_rb_id = create_tunnel_req->eps_bearer_id[i] - 4;
teid_t teid = newGtpuCreateTunnel(compatInst(instance),
create_tunnel_req->rnti,
incoming_rb_id,
create_tunnel_req->eps_bearer_id[i],
create_tunnel_req->sgw_S1u_teid[i],
-1, // no pdu session in 4G
create_tunnel_req->sgw_addr[i],
callBack,
NULL);
create_tunnel_resp->status = 0;
create_tunnel_resp->rnti = create_tunnel_req->rnti;
create_tunnel_resp->num_tunnels = create_tunnel_req->num_tunnels;
create_tunnel_resp->enb_S1u_teid[i] = teid;
create_tunnel_resp->eps_bearer_id[i] = create_tunnel_req->eps_bearer_id[i];
memcpy(create_tunnel_resp->enb_addr.buffer, addr, sizeof(addr));
create_tunnel_resp->enb_addr.length = sizeof(addr);
}
return !GTPNOK;
}
int gtpv1u_update_s1u_tunnel(const instance_t instance,
const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req,
const rnti_t prior_rnti)
{
LOG_D(GTPU,
"[%ld] Start update tunnels for old RNTI %x, new RNTI %x, num_tunnels %d, sgw_S1u_teid %x, eps_bearer_id %x\n",
instance,
prior_rnti,
create_tunnel_req->rnti,
create_tunnel_req->num_tunnels,
create_tunnel_req->sgw_S1u_teid[0],
create_tunnel_req->eps_bearer_id[0]);
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
if (inst->ue2te_mapping.find(create_tunnel_req->rnti) == inst->ue2te_mapping.end()) {
LOG_E(GTPU,
"[%ld] Update not already existing tunnel (new rnti %x, old rnti %x)\n",
instance,
create_tunnel_req->rnti,
prior_rnti);
}
auto it = inst->ue2te_mapping.find(prior_rnti);
if (it != inst->ue2te_mapping.end()) {
pthread_mutex_unlock(&globGtp.gtp_lock);
AssertFatal(false,
"logic bug: update of non-existing tunnel (new ue id %u, old ue id %u)\n",
create_tunnel_req->rnti,
prior_rnti);
/* we don't know if we need 4G or 5G PDCP and can therefore not create a
* new tunnel */
return 0;
}
inst->ue2te_mapping[create_tunnel_req->rnti] = it->second;
inst->ue2te_mapping.erase(it);
pthread_mutex_unlock(&globGtp.gtp_lock);
return 0;
}
int gtpv1u_create_ngu_tunnel(const instance_t instance,
const gtpv1u_gnb_create_tunnel_req_t *const create_tunnel_req,
gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp,
gtpCallback callBack,
gtpCallbackSDAP callBackSDAP)
{
LOG_D(GTPU,
"[%ld] Start create tunnels for ue id %lu, num_tunnels %d, sgw_S1u_teid %x\n",
instance,
create_tunnel_req->ue_id,
create_tunnel_req->num_tunnels,
create_tunnel_req->outgoing_teid[0]);
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
uint8_t addr[inst->foundAddrLen];
memcpy(addr, inst->foundAddr, inst->foundAddrLen);
pthread_mutex_unlock(&globGtp.gtp_lock);
for (int i = 0; i < create_tunnel_req->num_tunnels; i++) {
teid_t teid = newGtpuCreateTunnel(instance,
create_tunnel_req->ue_id,
create_tunnel_req->incoming_rb_id[i],
create_tunnel_req->pdusession_id[i],
create_tunnel_req->outgoing_teid[i],
create_tunnel_req->outgoing_qfi[i],
create_tunnel_req->dst_addr[i],
callBack,
callBackSDAP);
create_tunnel_resp->status = 0;
create_tunnel_resp->ue_id = create_tunnel_req->ue_id;
create_tunnel_resp->num_tunnels = create_tunnel_req->num_tunnels;
create_tunnel_resp->gnb_NGu_teid[i] = teid;
memcpy(create_tunnel_resp->gnb_addr.buffer, addr, sizeof(addr));
create_tunnel_resp->gnb_addr.length = sizeof(addr);
create_tunnel_resp->pdusession_id[i] = create_tunnel_req->pdusession_id[i];
}
return !GTPNOK;
}
int gtpv1u_update_ue_id(const instance_t instanceP, ue_id_t old_ue_id, ue_id_t new_ue_id)
{
pthread_mutex_lock(&globGtp.gtp_lock);
auto inst = &globGtp.instances[compatInst(instanceP)];
auto it = inst->ue2te_mapping.find(old_ue_id);
if (it == inst->ue2te_mapping.end()) {
LOG_W(GTPU, "[%ld] Update GTP tunnels for UEid: %lx, but no tunnel exits\n", instanceP, old_ue_id);
pthread_mutex_unlock(&globGtp.gtp_lock);
return GTPNOK;
}
for (unsigned i = 0; i < it->second.bearers.size(); ++i) {
teid_t incoming_teid = inst->ue2te_mapping[old_ue_id].bearers[i].teid_incoming;
if (globGtp.te2ue_mapping[incoming_teid].ue_id == old_ue_id) {
globGtp.te2ue_mapping[incoming_teid].ue_id = new_ue_id;
}
}
inst->ue2te_mapping[new_ue_id] = it->second;
inst->ue2te_mapping.erase(it);
pthread_mutex_unlock(&globGtp.gtp_lock);
LOG_I(GTPU, "[%ld] Updated tunnels from UEid %lx to UEid %lx\n", instanceP, old_ue_id, new_ue_id);
return !GTPNOK;
}
int gtpv1u_create_x2u_tunnel(const instance_t instanceP,
const gtpv1u_enb_create_x2u_tunnel_req_t *const create_tunnel_req_pP,
gtpv1u_enb_create_x2u_tunnel_resp_t *const create_tunnel_resp_pP)
{
AssertFatal(false, "to be developped\n");
}
int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id)
{
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
map<uint64_t, teidData_t>::iterator ue_it = inst->ue2te_mapping.find(ue_id);
if (ue_it == inst->ue2te_mapping.end()) {
LOG_E(GTPU, "%s() no such UE %ld\n", __func__, ue_id);
pthread_mutex_unlock(&globGtp.gtp_lock);
return !GTPNOK;
}
map<ue_id_t, gtpv1u_bearer_t>::iterator rb_it = ue_it->second.bearers.find(rb_id);
if (rb_it == ue_it->second.bearers.end()) {
LOG_E(GTPU, "%s() UE %ld has no bearer %d, available\n", __func__, ue_id, rb_id);
pthread_mutex_unlock(&globGtp.gtp_lock);
return !GTPNOK;
}
int teid = rb_it->second.teid_incoming;
globGtp.te2ue_mapping.erase(teid);
ue_it->second.bearers.erase(rb_id);
pthread_mutex_unlock(&globGtp.gtp_lock);
LOG_I(GTPU, "Deleted tunnel TEID %d (RB %d) for ue id %ld, remaining bearers:\n", teid, rb_id, ue_id);
for (auto b : ue_it->second.bearers)
LOG_I(GTPU, "bearer %ld\n", b.first);
return !GTPNOK;
}
int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id)
{
LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", instance, ue_id);
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
getUeRetInt(inst, ue_id);
int nb = 0;
for (auto j = ptrUe->second.bearers.begin(); j != ptrUe->second.bearers.end(); ++j) {
globGtp.te2ue_mapping.erase(j->second.teid_incoming);
nb++;
}
inst->ue2te_mapping.erase(ptrUe);
pthread_mutex_unlock(&globGtp.gtp_lock);
LOG_I(GTPU, "[%ld] Deleted all tunnels for ue id %ld (%d tunnels deleted)\n", instance, ue_id, nb);
return !GTPNOK;
}
int gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP)
{
LOG_D(GTPU, "[%ld] Start delete tunnels for RNTI %x\n", instance, req_pP->rnti);
pthread_mutex_lock(&globGtp.gtp_lock);
auto inst = &globGtp.instances[compatInst(instance)];
auto ptrRNTI = inst->ue2te_mapping.find(req_pP->rnti);
if (ptrRNTI == inst->ue2te_mapping.end()) {
LOG_W(GTPU, "[%ld] Delete Released GTP tunnels for rnti: %x, but no tunnel exits\n", instance, req_pP->rnti);
pthread_mutex_unlock(&globGtp.gtp_lock);
return -1;
}
int nb = 0;
for (int i = 0; i < req_pP->num_erab; i++) {
auto ptr2 = ptrRNTI->second.bearers.find(req_pP->eps_bearer_id[i]);
if (ptr2 == ptrRNTI->second.bearers.end()) {
LOG_E(GTPU,
"[%ld] GTP-U instance: delete of not existing tunnel RNTI:RAB: %x/%x\n",
instance,
req_pP->rnti,
req_pP->eps_bearer_id[i]);
} else {
globGtp.te2ue_mapping.erase(ptr2->second.teid_incoming);
nb++;
}
}
if (ptrRNTI->second.bearers.size() == 0)
// no tunnels on this rnti, erase the ue entry
inst->ue2te_mapping.erase(ptrRNTI);
pthread_mutex_unlock(&globGtp.gtp_lock);
LOG_I(GTPU, "[%ld] Deleted released tunnels for RNTI %x (%d tunnels deleted)\n", instance, req_pP->rnti, nb);
return !GTPNOK;
}
// Legacy delete tunnel finish by deleting all the ue id
int gtpv1u_delete_all_s1u_tunnel(const instance_t instance, const rnti_t rnti)
{
return newGtpuDeleteAllTunnels(instance, rnti);
}
int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id)
{
LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", instance, ue_id);
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
getUeRetInt(inst, ue_id);
int nb = 0;
for (int i = 0; i < nbTunnels; i++) {
auto ptr2 = ptrUe->second.bearers.find(pdusession_id[i]);
if (ptr2 == ptrUe->second.bearers.end()) {
LOG_E(GTPU, "[%ld] GTP-U instance: delete of not existing tunnel UE ID:RAB: %ld/%x\n", instance, ue_id, pdusession_id[i]);
} else {
globGtp.te2ue_mapping.erase(ptr2->second.teid_incoming);
nb++;
}
}
if (ptrUe->second.bearers.size() == 0)
// no tunnels on this ue id, erase the ue entry
inst->ue2te_mapping.erase(ptrUe);
pthread_mutex_unlock(&globGtp.gtp_lock);
LOG_I(GTPU, "[%ld] Deleted all tunnels for ue id %lu (%d tunnels deleted)\n", instance, ue_id, nb);
return !GTPNOK;
}
int gtpv1u_delete_x2u_tunnel(const instance_t instanceP, const gtpv1u_enb_delete_tunnel_req_t *const req_pP)
{
LOG_E(GTPU, "x2 tunnel not implemented\n");
return 0;
}
int gtpv1u_delete_ngu_tunnel(const instance_t instance, gtpv1u_gnb_delete_tunnel_req_t *req)
{
return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id);
}
static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp)
{
Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf;
if (msgHdr->version != 1 || msgHdr->PT != 1) {
LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h);
return GTPNOK;
}
if (msgHdr->S != 1) {
LOG_E(GTPU, "[%d] Received a echo request packet with no sequence number \n", h);
return GTPNOK;
}
uint16_t seq = ntohs(*(uint16_t *)(msgHdr + 1));
LOG_D(GTPU, "[%d] Received a echo request, TEID: %d, seq: %hu\n", h, msgHdr->teid, seq);
uint8_t recovery[2] = {14, 0};
return gtpv1uCreateAndSendMsg(h,
peerIp,
peerPort,
GTP_ECHO_RSP,
ntohl(msgHdr->teid),
recovery,
sizeof recovery,
true,
false,
seq,
0,
NO_MORE_EXT_HDRS,
NULL,
0);
}
static int Gtpv1uHandleError(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp)
{
LOG_E(GTPU, "Received GTP error indication (error handling is missing/not implemented)\n");
int rc = GTPNOK;
return rc;
}
static int Gtpv1uHandleSupportedExt(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp)
{
LOG_E(GTPU, "Supported extensions to be dev\n");
int rc = GTPNOK;
return rc;
}
// When end marker arrives, we notify the client with buffer size = 0
// The client will likely call "delete tunnel"
// nevertheless we don't take the initiative
static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp)
{
Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf;
if (msgHdr->version != 1 || msgHdr->PT != 1) {
LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h);
return GTPNOK;
}
pthread_mutex_lock(&globGtp.gtp_lock);
// the socket Linux file handler is the instance id
getInstRetInt(h);
auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid));
if (tunnel == globGtp.te2ue_mapping.end()) {
LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, msgHdr->teid);
pthread_mutex_unlock(&globGtp.gtp_lock);
return GTPNOK;
}
// This context is not good for gtp
// frame, ... has no meaning
// manyother attributes may come from create tunnel
protocol_ctxt_t ctxt;
ctxt.module_id = 0;
ctxt.enb_flag = 1;
ctxt.instance = inst->addr.originInstance;
ctxt.rntiMaybeUEid = tunnel->second.ue_id;
ctxt.frame = 0;
ctxt.subframe = 0;
ctxt.eNB_index = 0;
ctxt.brOption = 0;
const srb_flag_t srb_flag = SRB_FLAG_NO;
const rb_id_t rb_id = tunnel->second.incoming_rb_id;
const mui_t mui = RLC_MUI_UNDEFINED;
const confirm_t confirm = RLC_SDU_CONFIRM_NO;
const pdcp_transmission_mode_t mode = PDCP_TRANSMISSION_MODE_DATA;
const uint32_t sourceL2Id = 0;
const uint32_t destinationL2Id = 0;
pthread_mutex_unlock(&globGtp.gtp_lock);
if (!tunnel->second.callBack(&ctxt, srb_flag, rb_id, mui, confirm, 0, NULL, mode, &sourceL2Id, &destinationL2Id))
LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h);
LOG_D(GTPU, "[%d] Received END marker packet for: teid:%x\n", h, ntohl(msgHdr->teid));
return !GTPNOK;
}
static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp)
{
Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf;
if (msgHdr->version != 1 || msgHdr->PT != 1) {
LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h);
return GTPNOK;
}
pthread_mutex_lock(&globGtp.gtp_lock);
auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid));
if (tunnel == globGtp.te2ue_mapping.end()) {
LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, ntohl(msgHdr->teid));
pthread_mutex_unlock(&globGtp.gtp_lock);
return GTPNOK;
}
ueidData_t uedata = tunnel->second;
pthread_mutex_unlock(&globGtp.gtp_lock);
/* see TS 29.281 5.1 */
// Minimum length of GTP-U header if non of the optional fields are present
unsigned int offset = sizeof(Gtpv1uMsgHeaderT);
int8_t qfi = -1;
bool rqi = false;
uint32_t NR_PDCP_PDU_SN = 0;
/* if E, S, or PN is set then there are 4 more bytes of header */
if (msgHdr->E || msgHdr->S || msgHdr->PN)
offset += 4;
if (msgHdr->E) {
int next_extension_header_type = msgBuf[offset - 1];
int extension_header_length;
while (next_extension_header_type != NO_MORE_EXT_HDRS) {
extension_header_length = msgBuf[offset];
switch (next_extension_header_type) {
case PDU_SESSION_CONTAINER: {
if (offset + sizeof(PDUSessionContainerT) > msgBufLen) {
LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n");
return GTPNOK;
}
PDUSessionContainerT *pdusession_cntr = (PDUSessionContainerT *)(msgBuf + offset + 1);
qfi = pdusession_cntr->QFI;
rqi = pdusession_cntr->Reflective_QoS_activation;
break;
}
case NR_RAN_CONTAINER: {
if (offset + 1 > msgBufLen) {
LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n");
return GTPNOK;
}
uint8_t PDU_type = (msgBuf[offset + 1] >> 4) & 0x0f;
if (PDU_type == 0) { // DL USER Data Format
int additional_offset = 6; // Additional offset capturing the first non-mandatory octet (TS 38.425, Figure 5.5.2.1-1)
if (msgBuf[offset + 1] >> 2 & 0x1) { // DL Discard Blocks flag is present
LOG_I(GTPU, "DL User Data: DL Discard Blocks handling not enabled\n");
additional_offset = additional_offset + 9; // For the moment ignore
}
if (msgBuf[offset + 1] >> 1 & 0x1) { // DL Flush flag is present
LOG_I(GTPU, "DL User Data: DL Flush handling not enabled\n");
additional_offset = additional_offset + 3; // For the moment ignore
}
if ((msgBuf[offset + 2] >> 3) & 0x1) { //"Report delivered" enabled (TS 38.425, 5.4)
/*Store the NR PDCP PDU SN for which a delivery status report shall be generated once the
*PDU gets forwarded to the lower layers*/
// NR_PDCP_PDU_SN = msgBuf[offset+6] << 16 | msgBuf[offset+7] << 8 | msgBuf[offset+8];
NR_PDCP_PDU_SN = msgBuf[offset + additional_offset] << 16 | msgBuf[offset + additional_offset + 1] << 8
| msgBuf[offset + additional_offset + 2];
LOG_D(GTPU, " NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN);
}
} else {
LOG_W(GTPU, "NR-RAN container type: %d not supported \n", PDU_type);
}
break;
}
default:
LOG_W(GTPU, "unhandled extension 0x%2.2x, skipping\n", next_extension_header_type);
break;
}
offset += extension_header_length * EXT_HDR_LNTH_OCTET_UNITS;
if (offset > msgBufLen) {
LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n");
return GTPNOK;
}
next_extension_header_type = msgBuf[offset - 1];
}
}
// This context is not good for gtp
// frame, ... has no meaning
// manyother attributes may come from create tunnel
protocol_ctxt_t ctxt = { .enb_flag = 1, .rntiMaybeUEid = uedata.ue_id, };
const srb_flag_t srb_flag = SRB_FLAG_NO;
const rb_id_t rb_id = uedata.incoming_rb_id;
const mui_t mui = RLC_MUI_UNDEFINED;
const confirm_t confirm = RLC_SDU_CONFIRM_NO;
const sdu_size_t sdu_buffer_size = msgBufLen - offset;
unsigned char *const sdu_buffer = msgBuf + offset;
const pdcp_transmission_mode_t mode = PDCP_TRANSMISSION_MODE_DATA;
const uint32_t sourceL2Id = 0;
const uint32_t destinationL2Id = 0;
if (sdu_buffer_size > 0) {
if (qfi != -1 && uedata.callBackSDAP) {
if (!uedata.callBackSDAP(&ctxt,
uedata.ue_id,
srb_flag,
rb_id,
mui,
confirm,
sdu_buffer_size,
sdu_buffer,
mode,
&sourceL2Id,
&destinationL2Id,
qfi,
rqi,
uedata.pdusession_id))
LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h);
} else {
if (!uedata.callBack(&ctxt, srb_flag, rb_id, mui, confirm, sdu_buffer_size, sdu_buffer, mode, &sourceL2Id, &destinationL2Id))
LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h);
}
}
if (NR_PDCP_PDU_SN > 0 && NR_PDCP_PDU_SN % 5 == 0) {
LOG_D(GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN);
int rlc_tx_buffer_space = nr_rlc_get_available_tx_space(ctxt.rntiMaybeUEid, rb_id + 3);
LOG_D(GTPU, "Available buffer size in RLC for Tx: %d \n", rlc_tx_buffer_space);
/*Total size of DDD_status PDU = 1 octet to report extension header length
* size of mandatory part + 3 octets for highest transmitted/delivered PDCP SN
* 1 octet for padding + 1 octet for next extension header type,
* according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/
extensionHeader_t *extensionHeader;
extensionHeader = (extensionHeader_t *)calloc(1, sizeof(extensionHeader_t));
extensionHeader->buffer[0] = (1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1) / 4;
DlDataDeliveryStatus_flagsT DlDataDeliveryStatus;
DlDataDeliveryStatus.deliveredPdcpSn = 0;
DlDataDeliveryStatus.transmittedPdcpSn = 1;
DlDataDeliveryStatus.pduType = 1;
DlDataDeliveryStatus.drbBufferSize =
htonl(rlc_tx_buffer_space); // htonl(10000000); //hardcoded for now but normally we should extract it from RLC
memcpy(extensionHeader->buffer + 1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT));
uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT) + 1;
extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff;
extensionHeader->buffer[offset + 1] = (NR_PDCP_PDU_SN >> 8) & 0xff;
extensionHeader->buffer[offset + 2] = NR_PDCP_PDU_SN & 0xff;
LOG_D(GTPU,
"Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n",
extensionHeader->buffer[offset],
extensionHeader->buffer[offset + 1],
extensionHeader->buffer[offset + 2]);
extensionHeader->buffer[offset + 3] = 0x00; // Padding octet
extensionHeader->buffer[offset + 4] = 0x00; // No more extension headers
/*Total size of DDD_status PDU = size of mandatory part +
* 3 octets for highest transmitted/delivered PDCP SN +
* 1 octet for padding + 1 octet for next extension header type,
* according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/
extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1;
gtpv1uCreateAndSendMsg(h,
peerIp,
peerPort,
GTP_GPDU,
globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid,
NULL,
0,
false,
false,
0,
0,
NR_RAN_CONTAINER,
extensionHeader->buffer,
extensionHeader->length);
}
LOG_D(GTPU, "[%d] Received a %d bytes packet for: teid:%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid));
return !GTPNOK;
}
static void gtpv1uReceiveHandleMessage(int h)
{
uint8_t udpData[65536];
int udpDataLen;
socklen_t from_len;
struct sockaddr_in addr;
from_len = (socklen_t)sizeof(struct sockaddr_in);
if ((udpDataLen = recvfrom(h, udpData, sizeof(udpData), 0, (struct sockaddr *)&addr, &from_len)) < 0) {
LOG_E(GTPU, "[%d] Recvfrom failed (%s)\n", h, strerror(errno));
return;
} else if (udpDataLen == 0) {
LOG_W(GTPU, "[%d] Recvfrom returned 0\n", h);
return;
} else {
if (udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT)) {
LOG_W(GTPU, "[%d] received malformed gtp packet \n", h);
return;
}
Gtpv1uMsgHeaderT *msg = (Gtpv1uMsgHeaderT *)udpData;
if ((int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen) {
LOG_W(GTPU, "[%d] received malformed gtp packet length\n", h);
return;
}
LOG_D(GTPU, "[%d] Received GTP data, msg type: %x\n", h, msg->msgType);
switch (msg->msgType) {
case GTP_ECHO_RSP:
break;
case GTP_ECHO_REQ:
Gtpv1uHandleEchoReq(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr);
break;
case GTP_ERROR_INDICATION:
Gtpv1uHandleError(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr);
break;
case GTP_SUPPORTED_EXTENSION_HEADER_INDICATION:
Gtpv1uHandleSupportedExt(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr);
break;
case GTP_END_MARKER:
Gtpv1uHandleEndMarker(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr);
break;
case GTP_GPDU:
Gtpv1uHandleGpdu(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr);
break;
default:
LOG_E(GTPU, "[%d] Received a GTP packet of unknown type: %d\n", h, msg->msgType);
break;
}
}
}
static void* gtpv1uReceiver(void *thr)
{
gtpThread_t *gt = (gtpThread_t *)thr;
while (true) {
gtpv1uReceiveHandleMessage(gt->h);
}
LOG_W(GTPU, "exiting thread\n");
return NULL;
}
#include <openair2/ENB_APP/enb_paramdef.h>
void *gtpv1uTask(void *args)
{
while (1) {
/* Trying to fetch a message from the message queue.
If the queue is empty, this function will block till a
message is sent to the task.
*/
MessageDef *message_p = NULL;
itti_receive_msg(TASK_GTPV1_U, &message_p);
if (message_p != NULL) {
openAddr_t addr = {{0}};
const instance_t myInstance = ITTI_MSG_DESTINATION_INSTANCE(message_p);
const int msgType = ITTI_MSG_ID(message_p);
LOG_D(GTPU, "GTP-U received %s for instance %ld\n", messages_info[msgType].name, myInstance);
switch (msgType) {
// DATA TO BE SENT TO UDP
case TERMINATE_MESSAGE:
break;
case TIMER_HAS_EXPIRED:
LOG_E(GTPU, "Received unexpected timer expired (no need of timers in this version) %s\n", ITTI_MSG_NAME(message_p));
break;
case GTPV1U_ENB_END_MARKER_REQ:
gtpv1uEndTunnel(compatInst(myInstance), &GTPV1U_ENB_END_MARKER_REQ(message_p));
itti_free(TASK_GTPV1_U, GTPV1U_ENB_END_MARKER_REQ(message_p).buffer);
break;
case GTPV1U_ENB_DATA_FORWARDING_REQ:
case GTPV1U_ENB_DATA_FORWARDING_IND:
case GTPV1U_ENB_END_MARKER_IND:
LOG_E(GTPU, "to be developped %s\n", ITTI_MSG_NAME(message_p));
abort();
break;
case GTPV1U_REQ:
// to be dev: should be removed, to use API
strcpy(addr.originHost, GTPV1U_REQ(message_p).localAddrStr);
strcpy(addr.originService, GTPV1U_REQ(message_p).localPortStr);
strcpy(addr.destinationService, addr.originService);
AssertFatal((legacyInstanceMapping = gtpv1Init(addr)) != 0, "Instance 0 reserved for legacy\n");
break;
default:
LOG_E(GTPU, "Received unexpected message %s\n", ITTI_MSG_NAME(message_p));
abort();
break;
}
AssertFatal(EXIT_SUCCESS == itti_free(TASK_GTPV1_U, message_p), "Failed to free memory!\n");
}
}
return NULL;
}
#ifdef __cplusplus
}
#endif