/* * SPDX-License-Identifier: LicenseRef-CSSL-1.0 */ #include using namespace std; #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include "common/platform_types.h" #include "common/utils/system.h" #include #include "common/utils/LOG/log.h" #include #include "sim.h" // TODO these dependencies should not exist and be removed #include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" #include "openair2/LAYER2/RLC/rlc.h" #include "gtp_itf.h" #include "gtpu_extensions.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; typedef struct Gtpv1Error { Gtpv1uMsgHeaderT h; uint8_t teid_data_i; teid_t teid; uint8_t addr_data_i; uint16_t addr_len; } __attribute__((packed)) Gtpv1uError; #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) // GTP bearer context: for sending data typedef struct gtpv1u_bearer_s { int sock_fd; struct sockaddr_storage ip; teid_t teid_incoming; teid_t teid_outgoing; uint16_t seqNum; uint8_t npduNum; int32_t nru_sequence_number; int outgoing_qfi; } gtpv1u_bearer_t; typedef struct { map 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 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); } }; static void gtpv1uReceiverCancel(pthread_t t); 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 instances; map 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) { gtpv1uReceiverCancel(p.second.thrData.t); 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(gtpv1u_bearer_t *bearer, int msgType, uint8_t *Msg, int msgLen, bool seqNumFlag, bool npduNumFlag, gtpu_extension_header_t *extensions, int extensions_count) { 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 = extensions_count != 0; msgHdr->spare = 0; // PT=0 is for GTP' TS 32.295 (charging) msgHdr->PT = 1; msgHdr->version = 1; msgHdr->msgType = msgType; msgHdr->teid = htonl(bearer->teid_outgoing); curPtr += sizeof(Gtpv1uMsgHeaderT); if (msgHdr->PN || msgHdr->S || msgHdr->E) { *(uint16_t *)curPtr = seqNumFlag ? bearer->seqNum : 0x0000; curPtr += sizeof(uint16_t); *(uint8_t *)curPtr = npduNumFlag ? bearer->npduNum : 0x00; curPtr++; *curPtr = extensions_count ? serialize_gtpu_extension_type(extensions[0].type) : 0; curPtr++; } for (int i = 0; i < extensions_count; i++) { int available_size = sizeof(buffer) - (curPtr - buffer); gtpu_extension_header_type_t next = i == extensions_count - 1 ? GTPU_EXT_NONE : extensions[i + 1].type; int len = serialize_extension(&extensions[i], next, curPtr, available_size); if (len == -1) { LOG_E(GTPU, "GTP extension serialization: buffer too small\n"); return GTPNOK; } curPtr += len; } if (Msg != NULL) { int available_size = sizeof(buffer) - (curPtr - buffer); if (msgLen > available_size) { LOG_E(GTPU, "GTP message creation: buffer too small\n"); return GTPNOK; } 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 DevAssert(bearer->ip.ss_family == AF_INET); struct sockaddr_in *to = (struct sockaddr_in *)&bearer->ip; LOG_D(GTPU, "Peer IP:" IPV4_ADDR " port:%u outgoing TEID:0x%x\n", IPV4_ADDR_FORMAT(to->sin_addr.s_addr), htons(to->sin_port), bearer->teid_outgoing); int ret = sendto(bearer->sock_fd, buffer, curPtr - buffer, 0, (struct sockaddr *)to, sizeof(*to)); if (ret != curPtr - buffer) { LOG_E(GTPU, "[SD %d] Failed to send data buffer size %lu, ret: %d, errno: %d\n", bearer->sock_fd, curPtr - buffer, ret, errno); return GTPNOK; } return !GTPNOK; } static void _gtpv1uSendDirect(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len, bool seqNumFlag, bool npduNumFlag, int32_t nru_seqnum) { 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/%d/0x%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 bearer = ptr2->second; pthread_mutex_unlock(&globGtp.gtp_lock); int extension_count = 0; gtpu_extension_header_t ext[2]; if (bearer.outgoing_qfi != -1) { /* 29.281 Figure 5.2.1-3 note 4 says PDU Session Container must come first. * GTPU_EXT_UL_PDU_SESSION_INFORMATION is within a PDU Session Container * so it must be put before any other extension. */ ext[extension_count] = { .type = GTPU_EXT_UL_PDU_SESSION_INFORMATION, .ul_pdu_session_information = { .qmp = false, .dl_delay_ind = false, .ul_delay_ind = false, .snp = false, .n3n9_delay_ind = false, .new_ie_flag = false, .qfi = bearer.outgoing_qfi } }; extension_count++; } if (nru_seqnum != -1) { ext[extension_count] = { .type = GTPU_EXT_DL_USER_DATA, .dl_user_data = { .dl_discard_blocks = false, .dl_flush = false, .report_polling = false, .request_out_of_seq_report = false, .report_delivered = false, .user_data_existence_flag = false, .assistance_info_report_polling_flag = false, .retransmission_flag = false, .nru_sequence_number = (uint32_t)nru_seqnum } }; extension_count++; } DevAssert(compatInst(instance) == bearer.sock_fd); gtpv1uCreateAndSendMsg(&bearer, GTP_GPDU, buf, len, seqNumFlag, npduNumFlag, ext, extension_count); } void gtpv1uSendDirect(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len, bool seqNumFlag, bool npduNumFlag) { _gtpv1uSendDirect(instance, ue_id, bearer_id, buf, len, seqNumFlag, npduNumFlag, -1); } void gtpv1uSendDirectWithNRUSeqNum(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len) { 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; } int32_t nru_seqnum = ptr2->second.nru_sequence_number; ptr2->second.nru_sequence_number++; ptr2->second.nru_sequence_number &= (1 << 24) - 1; pthread_mutex_unlock(&globGtp.gtp_lock); _gtpv1uSendDirect(instance, ue_id, bearer_id, buf, len, false, false, nru_seqnum); } static void fillDlDeliveryStatusReport(gtpu_extension_header_t *ext, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN) { *ext = { .type = GTPU_EXT_DL_DATA_DELIVERY_STATUS, .dl_data_delivery_status = { /* previous version of the code was sending highest_transmitted_nr_pdcp_sn if * it is != 0, let's do the same for the moment */ .highest_transmitted_nr_pdcp_sn_ind = NR_PDCP_PDU_SN != 0, .highest_delivered_nr_pdcp_sn_ind = false, .final_frame_ind = false, .lost_packet_report = false, .delivered_nr_pdcp_sn_range_ind = false, .data_rate_ind = false, .retransmitted_nr_pdcp_sn_ind = false, .delivered_retransmitted_nr_pdcp_ind = false, .cause_report = false, .desired_buffer_size = RLC_buffer_availability, .highest_transmitted_nr_pdcp_sn = NR_PDCP_PDU_SN } }; } 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/%d/0x%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 DevAssert(instance == tmp.sock_fd); DevAssert(tmp.ip.ss_family == AF_INET); struct sockaddr_in *to = (struct sockaddr_in *)&tmp.ip; LOG_D(GTPU, "[%ld] sending end packet to " IPV4_ADDR " port %d\n", instance, IPV4_ADDR_FORMAT(to->sin_addr.s_addr), htons(to->sin_port)); ssize_t ret = sendto(tmp.sock_fd, &msgHdr, sizeof(msgHdr), 0, (struct sockaddr *)to, sizeof(*to)); if (ret != sizeof(msgHdr)) { LOG_E(GTPU, "[%d] Failed to send data with buffer size %lu: ret %ld errno %d\n", tmp.sock_fd, sizeof(msgHdr), ret, errno); } } 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; } /* \brief remove the GTP instance from the list of instances. Does not make an * attempt to free corresponding TEIDs, as we have many and will simply not * reuse it later. */ int gtpv1Term(instance_t instance) { pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); gtpv1uReceiverCancel(inst->thrData.t); close(instance); globGtp.instances.erase(instance); pthread_mutex_unlock(&globGtp.gtp_lock); return 0; } 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; } struct sockaddr_in *sockaddr = (struct sockaddr_in *)&ptr2->second.ip; sockaddr->sin_family = AF_INET; memcpy(&sockaddr->sin_addr, &newOutgoingAddr, sizeof(newOutgoingAddr)); AssertFatal(ptr2->second.ip.ss_family == AF_INET, "only IPv4 is supported\n"); ptr2->second.teid_outgoing = newOutgoingTeid; char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; struct sockaddr_in *sa4 = (struct sockaddr_in *)sockaddr; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sockaddr; LOG_I(GTPU, "[%ld] UE ID %ld: Update tunnel TEID incoming 0x%x outgoing 0x%x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, ptr2->second.teid_incoming, ptr2->second.teid_outgoing, inet_ntop(AF_INET, &sa4->sin_addr, ip4, INET_ADDRSTRLEN), inet_ntop(AF_INET6, &sa6->sin6_addr, ip6, INET6_ADDRSTRLEN), ntohs(sa4->sin_port)); 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 (0x%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 bearer = { .sock_fd = (int) compatInst(instance), // avoid warning on narrowing conversion: instance is long, sock_fd is int .teid_incoming = incoming_teid, .teid_outgoing = outgoing_teid, .outgoing_qfi = outgoing_qfi, }; int addrs_length_in_bytes = remoteAddr.length / 8; struct sockaddr_in *sa4 = (struct sockaddr_in *)&bearer.ip; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&bearer.ip; switch (addrs_length_in_bytes) { case 4: memcpy(&sa4->sin_addr, remoteAddr.buffer, 4); sa4->sin_family = AF_INET; sa4->sin_port = htons(inst->get_dstport()); break; case 16: AssertFatal(false, "IPv6 not supported\n"); break; case 20: AssertFatal(false, "dual-IPv4/v6 not supported\n"); break; default: AssertFatal(false, "SGW Address size impossible"); } inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id] = bearer; pthread_mutex_unlock(&globGtp.gtp_lock); char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; LOG_I(GTPU, "[%ld] UE ID %ld: Create tunnel TEID incoming 0x%x outgoing 0x%x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, bearer.teid_incoming, bearer.teid_outgoing, inet_ntop(AF_INET, &sa4->sin_addr, ip4, INET_ADDRSTRLEN), inet_ntop(AF_INET6, &sa6->sin6_addr, ip6, INET6_ADDRSTRLEN), ntohs(sa4->sin_port)); 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, TEID 0x%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) { UNUSED(instanceP); UNUSED(create_tunnel_req_pP); UNUSED(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::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::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 tunnel for bearer %d\n", __func__, ue_id, rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); return !GTPNOK; } teid_t 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 0x%x for bearer %d of UE %ld, remaining tunnels:\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] UE ID %ld: Delete all tunnels (%d tunnels)\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 gtpv1u_delete_x2u_tunnel(const instance_t instanceP, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { UNUSED(instanceP); UNUSED(req_pP); LOG_E(GTPU, "x2 tunnel not implemented\n"); return 0; } static gtpv1u_bearer_t create_bearer(int socket, const struct sockaddr_in *addr, uint32_t teid, uint16_t seq) { gtpv1u_bearer_t bearer = {.sock_fd = socket, .teid_outgoing = teid, .seqNum = seq}; memcpy(&bearer.ip, addr, sizeof(*addr)); return bearer; } static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, const struct sockaddr_in *addr) { 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: 0x%x, seq: %hu\n", h, msgHdr->teid, seq); uint8_t recovery[2] = {14, 0}; gtpv1u_bearer_t bearer = create_bearer(h, addr, ntohl(msgHdr->teid), seq); return gtpv1uCreateAndSendMsg(&bearer, GTP_ECHO_RSP, recovery, sizeof recovery, true, false, NULL, 0); } static int Gtpv1uHandleError(uint8_t *msgBuf, uint32_t msgBufLen) { if (msgBufLen < sizeof(Gtpv1uError)) LOG_E(GTPU, "Received GTP error indication with truncated size %u (mini size: %lu)\n", msgBufLen,sizeof(Gtpv1uError)+4); Gtpv1uError *msg = ( Gtpv1uError *)msgBuf; LOG_E(GTPU, "Received GTP error indication: \n" " TEID 0x%x (must be 0 from TS 29.281)\n" " TV id for TEID 0x%x (must be 16)\n" " TEID in error 0x%x (should be a TEID we sent)\n" " TV id for GTP addr %u (should be 133)\n" " len for addr of UPF %u (should be IPv4 or IPv6 len)" " (TS 29.281 Sec 7.3.1 Error Handling not implemented)\n", ntohl(msg->h.teid), msg->teid_data_i, ntohl(msg->teid), msg->addr_data_i, msg->addr_len); int rc = GTPNOK; return rc; } static int Gtpv1uHandleSupportedExt() { 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) { 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 (0x%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:0x%x\n", h, ntohl(msgHdr->teid)); return !GTPNOK; } static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) { 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 (0x%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, 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); gtpu_extension_header_t ext; fillDlDeliveryStatusReport(&ext, rlc_tx_buffer_space, NR_PDCP_PDU_SN); uint32_t teid = globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid; gtpv1u_bearer_t bearer = create_bearer(h, addr, teid, 0); gtpv1uCreateAndSendMsg(&bearer, GTP_GPDU, NULL, 0, false, false, &ext, 1); } LOG_D(GTPU, "[%d] Received a %d bytes packet for: TEID:0x%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); return !GTPNOK; } static bool 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 false; } else if (udpDataLen == 0) { LOG_W(GTPU, "[%d] Recvfrom returned 0\n", h); return true; } else { if (udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT)) { LOG_W(GTPU, "[%d] received malformed gtp packet \n", h); return true; } Gtpv1uMsgHeaderT *msg = (Gtpv1uMsgHeaderT *)udpData; if ((int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen) { LOG_W(GTPU, "[%d] received malformed gtp packet length\n", h); return true; } 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, &addr); break; case GTP_ERROR_INDICATION: Gtpv1uHandleError(udpData, udpDataLen); break; case GTP_SUPPORTED_EXTENSION_HEADER_INDICATION: Gtpv1uHandleSupportedExt(); break; case GTP_END_MARKER: Gtpv1uHandleEndMarker(h, udpData); break; case GTP_GPDU: Gtpv1uHandleGpdu(h, udpData, udpDataLen, &addr); break; default: LOG_E(GTPU, "[%d] Received a GTP packet of unknown type: %d\n", h, msg->msgType); break; } } return true; } static void* gtpv1uReceiver(void *thr) { gtpThread_t *gt = (gtpThread_t *)thr; while (gtpv1uReceiveHandleMessage(gt->h)) { } LOG_W(GTPU, "exiting thread\n"); return NULL; } static void gtpv1uReceiverCancel(pthread_t t) { int rc; rc = pthread_cancel(t); DevAssert(rc == 0); rc = pthread_join(t, NULL); DevAssert(rc == 0); } #include void *gtpv1uTask(void *args) { UNUSED(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: LOG_W(GTPU, "Exiting GTP instance %ld\n", myInstance); itti_exit_task(); 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), >PV1U_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