With contributions from * Sakthivel Velumani <velumani@eurecom.fr> * Robert Schmidt <robert.schmidt@openairinterface.org> Squashed commit of the following: commit e8488acbcdaef6ee1688dc233c9e48e85b0fbfc8 Merge: f09468eb135d58645a85Author: laurent <laurent Thomas> Date: Sat Jan 21 15:03:40 2023 +0100 merge develop commit f09468eb13f729c49830786150918b03ae2d7524 Author: laurent <laurent Thomas> Date: Fri Jan 20 10:43:51 2023 +0100 continue e1AP development commit 76545cfbfc7ec5031633b1b0ec599c3560c1e5a7 Author: laurent <laurent Thomas> Date: Wed Jan 18 14:18:47 2023 +0100 fix regressions after merge develop commit 7071c480a6c433eb94501c49f4a7694ba77f4f6d Merge: 569b9fc5f3214aa50595Author: laurent <laurent Thomas> Date: Mon Jan 16 15:51:50 2023 +0100 merge develop commit 569b9fc5f37b9947b722885e423572a211d658da Author: laurent <laurent Thomas> Date: Wed Jan 4 17:07:01 2023 +0100 fix srb2 addition, simplify and remove dead code, fix one race commit 9fa89151c0f9b4a16a79d0685cc04d8cf2adfa44 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Jan 4 20:02:05 2023 +0530 Bearer context release decoder and encoder commit 7011cb518ed6df015ace536e9a08b2d1865188d9 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Jan 4 13:43:58 2023 +0530 Update documentation commit 3e8899fc962cb16fc1ded49590ca5dddede54978 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Jan 4 10:14:11 2023 +0530 Fix function arguments and warnings commit 989ad183a8d66961facaa6719fa7793b9d40d5b4 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 20:18:31 2023 +0530 Moved E1-design.md to doc/ commit 04a8633dd509d9525755c5f3abfad976a1ba704e Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 20:13:46 2023 +0530 Make libraries lower case in CMakeLists commit 033ec174c1f48a0e745a2a2e1c367aa361f5ae76 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 17:55:25 2023 +0530 Renamed and updated cucp cuup config files commit f76a6913352bd8b2bb73ac25f225140cf05f93f2 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 13:31:19 2023 +0530 Removed CU-UP checks from nr-softmodem commit ae70afe44c84ac16b977b388c63e80e24803c43c Author: laurent <laurent Thomas> Date: Mon Jan 2 13:01:45 2023 +0100 fix for gcc version hat doesnt accept declarations in switch commit 6a7a01043bf1e6e0ee9ac40b4d85ced7cdf6dcf4 Author: laurent <laurent Thomas> Date: Tue Dec 27 15:59:28 2022 +0100 first functional commit standalone cu-up commit 0caa4690f9e1acbe578d4de5a82ba62f45444a12 Merge: 3619f9550e1a0c0cd1c9Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Dec 23 14:21:00 2022 +0530 Merge remote-tracking branch 'origin/develop' into e1-implementation-wip commit 3619f9550e4d3e78815334150ff8443db914f738 Merge: 1bcf8d5e00cfe698eb70Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Dec 21 15:47:40 2022 +0530 Merge remote-tracking branch 'origin/develop' into e1-implementation-wip commit 1bcf8d5e00a9b9ce440d825f83ddc1b48086daa8 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Sep 13 02:10:34 2022 -0400 Fix GTP rnti to ue_id after rebase commit 23075ff8b7ef5d56df844c05e0a7100fe7bd35de Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Sep 6 00:32:12 2022 -0400 Added documentation commit 28f6e507f74d3fb069441e05da523378dee0f6ed Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Mon Sep 5 15:02:02 2022 -0400 Header cleaups and unitary simulators build fixes commit e665dbab28c49327066cff72aed22e1de7797b59 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Sep 1 17:52:07 2022 -0400 Common interface between CUCP & CUUP for E1 and non E1 modes commit 5dcc1bf59a37ed87d7d32a617cfcd663d6415757 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Sep 1 13:53:49 2022 -0400 Allocate stack where ever possible Free memory after ASN encode commit 793a5379539a3a2b6c42cd327d8cef79fe21fb55 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Sep 1 10:41:50 2022 -0400 Fixes for build issues commit 0d3564f25a0d8746b0fc550ed371ce21e08cd05a Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 20:57:14 2022 -0400 Fixed uesoftmodem linkage error commit 34adf865820f09900a5efed693ada1ccf6f0f1ad Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 19:31:50 2022 -0400 Removed RRC thread from CUUP commit 536741910aff90a86ba3a60612f943b412c5ab40 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 14:21:31 2022 -0400 Add header guard in one of E1AP header commit 22e9d9b6dea5d84d259ee2dc1aaa7cb943e55b08 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 14:00:47 2022 -0400 Fix bug in PDCP ue_id commit 4c65339b07d347003ed89d5e00f49a62e9b21483 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 18:04:59 2022 -0400 Linked E1AP lib to L2_NR target E1AP builds for nr-softmodem commit 014fe30ac93c84d810e0209bc7f0d3b7a1bbaee3 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 15:49:27 2022 -0400 Remove xer prints from e1ap and gcc warning fixes commit 71f0c9126bbb91bc595b66d57e1e91db676d3b9d Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 15:42:30 2022 -0400 Fixed bug in integrity protection config commit 4e393ef9528c2b14d670a341b52fc63262abf894 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 14:34:37 2022 -0400 GTP init with only local address IP traffic works both directions in Mono, F1 and E1 commit ec3bde2c5deb99f554afc2053e351b9f79525e96 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 00:16:03 2022 -0400 Fix bug in pdcp config and GTP instance UL iperf still not works. Can see packets in wireshark. commit be8df4435eeae0a0755e2ac658f33caa2a3e3c94 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Aug 27 20:15:04 2022 -0400 Fixes in node config after rebase commit 6f42ff88de7f1615f87c46771fd6617a5fdfc48f Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Aug 27 01:38:21 2022 -0400 Added N3 tunnel address in config params fixed bug in address length feild commit b5e132041994344ebb5c2c59ca392612a32658fd Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Aug 26 20:38:47 2022 -0400 Send UL UP address via F1 UE cxt mod msg commit ecfd0fe260aee6aad78c291368c246f88b6bffcf Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Aug 26 15:08:53 2022 -0400 Update GTP remote address from response message Modified GTP tunnel update function accordingly Put GTP tunnel update out of F1AP F1 split works commit 14a37e414de3d653f25ccef1a790bc5b08fc7dc2 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Aug 20 01:17:36 2022 -0400 Moved GTP tunnel creation out of F1AP message sending commit cdd58ab96e84f48cc8ab8f0fe6b3133081ff02f7 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Aug 19 17:40:43 2022 -0400 Moved DRB config out of RRCReconfig complete Monolithic and F1 split works commit 61d66e490a1b61aa2b6ce18d993bb90d112d6374 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 11 01:30:10 2022 -0400 Allow CUUP to have RRC inst without error commit dd7ae889372bb9fdf9e96be87c28eed0d9461d7f Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 11 01:29:21 2022 -0400 Added missing IEs in bearer cxt response handler commit a6f1fe5fd254ccb36a54ede5b652161cb5e542e8 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 10 21:02:30 2022 -0400 Unified bearer context message transfer for E1 and non-E1 mode compiles, to be tested commit f245ace285632edcc28d669123746b9bf458bc38 Author: laurent <laurent Thomas> Date: Tue Aug 9 17:46:52 2022 +0200 remove log_i() of fatal errors commit d2a0f3c8620981f3b65a136f0169e3be9c549742 Author: laurent <laurent Thomas> Date: Fri Aug 5 15:01:39 2022 +0200 fix rnti/ue_id for ue commit 7b52c6cd9e675c28cb9d6347d122f77234ec6542 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 4 19:14:46 2022 -0400 Created separate functions in PDCP for E1 mode commit 46680d2b3d221e675c3aeb675137f45318e31acb Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 4 19:13:10 2022 -0400 Bug fixes in bearer context setup response commit 3d091bfd31b7aba625e4ba4b4f0f337cf2c1f6db Author: laurent <laurent Thomas> Date: Thu Aug 4 15:42:11 2022 +0200 add missing file from previous commit commit 21838572196dec947da99f350f20c4d9408c3c17 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 3 16:59:16 2022 -0400 Minor bug fixes commit 34eb73fbc6f9d33098850d62b4808e54c80bb4ca Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:16:47 2022 -0400 Added config files for testing To be renamed or removed later commit a7e9c6fb9332295ad42585eb4338a6f9ff0ae7c0 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:15:21 2022 -0400 Made rrc_gNB_get_ue_context_from_ngap_ids() non static commit 028f0407571466ff24cf72b5c3814e42c346ae81 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:12:16 2022 -0400 Completed bearer context setup cycle To be tested: Handling of bearer context setup response and subsequent UE context modifition msg to F1AP task commit 4481d11324b13efa73ab0b5f4f156012485661ae Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:01:50 2022 -0400 Fixed E1AP PPID (wireshark fixed) commit 7a22573f3e7e16960e3310609dea2783ddf404a2 Author: laurent <laurent Thomas> Date: Fri Jul 29 13:03:31 2022 +0200 fix compile issues, simplify gtp send function commit 0209011aa75a16e4c9c1eb36ebcf4fe0ff93aba2 Author: laurent <laurent Thomas> Date: Thu Jul 28 11:51:18 2022 +0200 restore compilation capability, gtp replacement of rnti by ueid commit ce3b886fd7f11322a8d37b924d1752e6a6adf777 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Jul 22 08:35:31 2022 -0400 Included missing IEs in bearer cxt setup message commit 1208ae0d6fe4b25341c9036dde111fd4d00fd6e9 Author: Cedric Roux <cedric.roux@eurecom.fr> Date: Tue Jul 19 10:53:35 2022 +0200 replace rnti by ue_id in pdcp some sdap and gtp files had to be touched, the work is not finished in there some changes in nr_pdcp_oai_api.c simply assume ue_id is indeed rnti (will functions in this file be used by cu-up?) commit e67d1c88c786267458194e4de516919d0b5d1002 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jul 19 10:08:39 2022 +0200 Replace RNTI with UE ID in GTP files (half done. Laurent to change ctxt_t) commit 7a141e8e941e5c2624a47da7c764531c03b8f617 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jul 19 10:04:15 2022 +0200 Added RRC handler for bearer cxt setup in CUUP commit fe2106676bf9144347f30969a1bfd351136f536a Author: laurent <laurent Thomas> Date: Mon Jul 11 13:30:30 2022 +0200 fix race in startup procedure (config before starting threads that use the config) commit 3197baab60e53459928da30cea7a4e0514a074e3 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sun Jul 10 11:30:31 2022 +0200 Fixed bugs in bearer creation commit 26be61167dec9068e572e08a21ddc96a0f2a7731 Author: laurent <laurent Thomas> Date: Sun Jul 10 10:50:57 2022 +0200 fix a race, add asn1c debug method commit 26750d1818aa206c4c56ada6dbed9bb66dfe3811 Author: laurent <laurent Thomas> Date: Wed Jul 6 22:34:10 2022 +0200 e1ap bearer setup encoding invalid commit d092788aa3f8229eb5c938a41534dfb318955135 Author: laurent <laurent Thomas> Date: Wed Jul 6 14:18:39 2022 +0200 fix regression with F1, some basic cleaning in NGAP commit 41a135734a33b284808d4c5049c431dbc0ff1c46 Author: laurent <laurent Thomas> Date: Wed Jul 6 11:24:52 2022 +0200 fix some ngap regressions commit 460acd840dd58fd46a995219cb9da8fda103668e Author: laurent <laurent Thomas> Date: Tue Jul 5 21:19:13 2022 +0200 build, run in F1 mode until UE connect but pdu session still fails commit 7c067095df287cc7a542be53e9a1471c11ec5af8 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Jun 25 22:45:46 2022 +0530 Populate PDU and DRB paramenters from NGAP commit c4ada56a8c31b4c07fc1498e15b0954d9b1ad2f6 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jun 7 22:16:58 2022 +0530 setup resp decoding success commit a2f41597c7438934a203ccf3cced35b6b65e8cee Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Jun 4 13:50:55 2022 +0530 Fixed bugs in E1 setup procedure E1 setup request decoding successful commit 6106207ba74c74579b5f375d572930a609abe3a9 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu May 26 20:13:14 2022 +0530 Fixing build issues commit 67235fac01aaca4c17da13bf658144ffe6bd12df Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon May 23 12:04:22 2022 +0200 Make nr-softmodem depend on E1AP module commit 391cee938e8250c355b6b914e747f2004c795817 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon May 23 11:57:15 2022 +0200 Fix ITTI errors in E1AP target commit db4146e4a0432b7e3759d71efbeb450754c1f0d3 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed May 18 06:59:03 2022 +0530 CP UP task created commit 3058d2fc445acf5681a09f70409023718294d093 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue May 10 08:12:19 2022 +0530 Bearer context setup response message commit b911e9db2a4d0a35eb9a449ef02acefff30b558f Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue May 3 16:52:49 2022 +0530 Bearer context setup c structs commit d0f4d5f3539803253b9a995a90a47b3d825ea0ca Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Apr 28 02:21:50 2022 +0530 Bearer context setup message commit 489a5358fa0e199314254561a2d52c1a93f7803e Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Apr 21 10:11:03 2022 +0530 Started with CUUP task commit b416061f5a237cbabbe09c6d81fe92df9e33fd72 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Apr 15 18:46:27 2022 +0530 Setup request and response messages commit faca4b97dba92be857bd8442193365fc2c8ed122 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon Apr 11 15:01:48 2022 +0200 Fixup for cmake execution commit ecfd9f7608a5820ae85278939e9bbaf7f1245dd6 Author: Robert Schmidt <robert.schmidt@eurecom.fr> Date: Tue Nov 24 21:39:20 2020 +0100 Move CMakeLists.txt into root commit 9f41f7c52b3c7a185e5e34bb1591b0eb66459837 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Tue Apr 12 12:07:18 2022 +0200 Remove hwlat refs in build_oai: targets don't exist anymore commit e666cf5eedc67695febd33de1397331d6b7b2320 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon Apr 11 14:37:32 2022 +0200 Create CMake E1AP_RELEASE variable from E1AP_VERSION commit 1f8413361c1b99ea46ee613da035803a1387f65c Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Sun Apr 10 14:25:38 2022 +0200 Add E1AP lib commit 7465cea49243fd6eb9869035b18860dface57098 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Sun Apr 10 14:48:40 2022 +0200 Add E1AP Grammar and build ASN.1 lib at build time commit cde22f83032a199387ae1eae6b702f44ed154f3e Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Fri Apr 22 13:42:51 2022 +0200 Accept higher vers. nums in make_version/MAKE_VERSION commit d5127b23608240374a439d1d9c2ff6d53c4b527c Author: Robert Schmidt <robert.schmidt@eurecom.fr> Date: Tue Nov 24 21:39:20 2020 +0100 Move CMakeLists.txt into root
1283 lines
49 KiB
C++
1283 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 <openair2/COMMON/platform_types.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_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;
|
|
teid_t outgoing_teid;
|
|
} 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;
|
|
|
|
class gtpEndPoint {
|
|
public:
|
|
openAddr_t addr;
|
|
uint8_t foundAddr[20];
|
|
int foundAddrLen;
|
|
int ipVersion;
|
|
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);
|
|
}
|
|
};
|
|
|
|
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] gtpv1uSend failed: while getting ue id %ld in hashtable ue_mapping\n", instance, 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] gtpv1uSend failed: while getting ue id %ld in hashtable ue_mapping\n", instance, 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);
|
|
|
|
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;
|
|
}
|
|
|
|
static void gtpv1uSend(instance_t instance, gtpv1u_tunnel_data_req_t *req, bool seqNumFlag, bool npduNumFlag) {
|
|
uint8_t *buffer=req->buffer+req->offset;
|
|
size_t length=req->length;
|
|
ue_id_t ue_id=req->ue_id;
|
|
int bearer_id=req->bearer_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 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,length, 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,
|
|
buffer,
|
|
length,
|
|
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, buffer, length, seqNumFlag, npduNumFlag, tmp.seqNum, tmp.npduNum, NO_MORE_EXT_HDRS, NULL, 0);
|
|
}
|
|
}
|
|
|
|
static void fillDlDeliveryStatusReport(extensionHeader_t *extensionHeader, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN){
|
|
|
|
extensionHeader->buffer[0] = (1+sizeof(DlDataDeliveryStatus_flagsT)+(NR_PDCP_PDU_SN>0?3:0)+(NR_PDCP_PDU_SN>0?1:0)+1)/4;
|
|
DlDataDeliveryStatus_flagsT DlDataDeliveryStatus;
|
|
DlDataDeliveryStatus.deliveredPdcpSn = 0;
|
|
DlDataDeliveryStatus.transmittedPdcpSn= NR_PDCP_PDU_SN>0?1:0;
|
|
DlDataDeliveryStatus.pduType = 1;
|
|
DlDataDeliveryStatus.drbBufferSize = htonl(RLC_buffer_availability);
|
|
memcpy(extensionHeader->buffer+1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT));
|
|
uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT)+1;
|
|
|
|
if(NR_PDCP_PDU_SN>0){
|
|
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->buffer[offset]: %u, extensionHeader->buffer[offset+1]:%u, extensionHeader->buffer[offset+2]:%u \n", extensionHeader->buffer[offset], extensionHeader->buffer[offset+1],extensionHeader->buffer[offset+2]);
|
|
extensionHeader->buffer[offset+3] = 0x00; //Padding octet
|
|
offset = offset+3;
|
|
}
|
|
extensionHeader->buffer[offset] = 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)+
|
|
(NR_PDCP_PDU_SN>0?3:0)+
|
|
(NR_PDCP_PDU_SN>0?1:0)+1;
|
|
}
|
|
|
|
static void gtpv1uSendDlDeliveryStatus(instance_t instance, gtpv1u_DU_buffer_report_req_t *req){
|
|
ue_id_t ue_id=req->ue_id;
|
|
int bearer_id=req->pdusession_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_D(GTPU,"GTP-U instance: %ld sending a packet to a non existant UE ID:RAB: %lu/%x\n", instance, ue_id, bearer_id);
|
|
pthread_mutex_unlock(&globGtp.gtp_lock);
|
|
return;
|
|
}
|
|
|
|
extensionHeader_t *extensionHeader;
|
|
extensionHeader = (extensionHeader_t *) calloc(1, sizeof(extensionHeader_t));
|
|
fillDlDeliveryStatusReport(extensionHeader, req->buffer_availability,0);
|
|
|
|
LOG_I(GTPU,"[%ld] GTP-U sending DL Data Delivery status to UE ID:RAB:teid %lu/%x/%x, oldseq %d, oldnum %d\n",
|
|
instance, ue_id, bearer_id,ptr2->second.teid_outgoing, ptr2->second.seqNum,ptr2->second.npduNum );
|
|
// copy to release the mutex
|
|
gtpv1u_bearer_t tmp=ptr2->second;
|
|
pthread_mutex_unlock(&globGtp.gtp_lock);
|
|
gtpv1uCreateAndSendMsg(
|
|
compatInst(instance), tmp.outgoing_ip_addr, tmp.outgoing_port, GTP_GPDU, tmp.teid_outgoing, NULL, 0, false, false, 0, 0, NR_RAN_CONTAINER, extensionHeader->buffer, extensionHeader->length);
|
|
}
|
|
|
|
static void gtpv1uEndTunnel(instance_t instance, gtpv1u_tunnel_data_req_t *req) {
|
|
ue_id_t ue_id=req->ue_id;
|
|
int bearer_id=req->bearer_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
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
freeaddrinfo(servinfo); // all done with this structure
|
|
|
|
if (strlen(addr.destinationHost)>1) {
|
|
struct addrinfo hints;
|
|
memset(&hints,0,sizeof(hints));
|
|
hints.ai_family=AF_UNSPEC;
|
|
hints.ai_socktype=SOCK_DGRAM;
|
|
hints.ai_protocol=0;
|
|
hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG;
|
|
struct addrinfo *res=0;
|
|
int err=getaddrinfo(addr.destinationHost,addr.destinationService,&hints,&res);
|
|
|
|
if (err==0) {
|
|
for(p = res; p != NULL; p = p->ai_next) {
|
|
if ((err=connect(sockfd, p->ai_addr, p->ai_addrlen))==0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (err)
|
|
LOG_E(GTPU,"Can't filter remote host: %s, %s\n", addr.destinationHost,addr.destinationService);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
instance_t gtpv1Init(openAddr_t context) {
|
|
pthread_mutex_lock(&globGtp.gtp_lock);
|
|
int id=udpServerSocket(context);
|
|
|
|
if (id>=0) {
|
|
itti_subscribe_event_fd(TASK_GTPV1_U, id);
|
|
} else
|
|
LOG_E(GTPU,"can't create GTP-U instance\n");
|
|
|
|
pthread_mutex_unlock(&globGtp.gtp_lock);
|
|
LOG_I(GTPU, "Created gtpu instance id: %d\n", id);
|
|
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,
|
|
int port,
|
|
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=port;
|
|
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) {
|
|
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));
|
|
|
|
tcp_udp_port_t dstport=inst->get_dstport();
|
|
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],
|
|
dstport,
|
|
pdcp_data_req,
|
|
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() ) {
|
|
LOG_W(GTPU,"[%ld] Update a not existing tunnel, start create the new one (new ue id %u, old ue id %u)\n", instance, create_tunnel_req->rnti, prior_rnti);
|
|
pthread_mutex_unlock(&globGtp.gtp_lock);
|
|
gtpv1u_enb_create_tunnel_resp_t tmp;
|
|
(void)gtpv1u_create_s1u_tunnel(instance, create_tunnel_req, &tmp);
|
|
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) {
|
|
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));
|
|
|
|
tcp_udp_port_t dstport = inst->get_dstport();
|
|
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],
|
|
dstport,
|
|
pdcp_data_req,
|
|
sdap_data_req);
|
|
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);
|
|
}
|
|
|
|
return !GTPNOK;
|
|
}
|
|
|
|
int gtpv1u_update_ngu_tunnel(
|
|
const instance_t instanceP,
|
|
const gtpv1u_gnb_create_tunnel_req_t *const create_tunnel_req_pP,
|
|
const ue_id_t prior_ueid
|
|
) {
|
|
AssertFatal( false, "to be developped\n");
|
|
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 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;
|
|
}
|
|
|
|
// Legacy delete tunnel finish by deleting all the ue id
|
|
// so the list of bearer provided is only a design bug
|
|
int gtpv1u_delete_s1u_tunnel( const instance_t instance,
|
|
const gtpv1u_enb_delete_tunnel_req_t *const req_pP) {
|
|
return newGtpuDeleteAllTunnels(instance, req_pP->rnti);
|
|
}
|
|
|
|
int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, pdusessionid_t *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,"Hadle error to be dev\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);
|
|
// 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, ntohl(msgHdr->teid));
|
|
pthread_mutex_unlock(&globGtp.gtp_lock);
|
|
return GTPNOK;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
uint8_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;
|
|
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 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;
|
|
pthread_mutex_unlock(&globGtp.gtp_lock);
|
|
|
|
if (sdu_buffer_size > 0) {
|
|
if (qfi != -1 && tunnel->second.callBackSDAP) {
|
|
if ( !tunnel->second.callBackSDAP(&ctxt,
|
|
tunnel->second.ue_id,
|
|
srb_flag,
|
|
rb_id,
|
|
mui,
|
|
confirm,
|
|
sdu_buffer_size,
|
|
sdu_buffer,
|
|
mode,
|
|
&sourceL2Id,
|
|
&destinationL2Id,
|
|
qfi,
|
|
rqi,
|
|
tunnel->second.pdusession_id) )
|
|
LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h);
|
|
} else {
|
|
if ( !tunnel->second.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;
|
|
}
|
|
|
|
void gtpv1uReceiver(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;
|
|
}
|
|
}
|
|
}
|
|
|
|
#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 GTPV1U_TUNNEL_DATA_REQ: {
|
|
gtpv1uSend(compatInst(myInstance), >PV1U_TUNNEL_DATA_REQ(message_p), false, false);
|
|
}
|
|
break;
|
|
|
|
case GTPV1U_DU_BUFFER_REPORT_REQ:{
|
|
gtpv1uSendDlDeliveryStatus(compatInst(myInstance), >PV1U_DU_BUFFER_REPORT_REQ(message_p));
|
|
}
|
|
break;
|
|
|
|
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), >PV1U_TUNNEL_DATA_REQ(message_p));
|
|
itti_free(TASK_GTPV1_U, GTPV1U_TUNNEL_DATA_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");
|
|
}
|
|
|
|
struct epoll_event events[20];
|
|
int nb_events = itti_get_events(TASK_GTPV1_U, events, 20);
|
|
|
|
for (int i = 0; i < nb_events; i++)
|
|
if ((events[i].events&EPOLLIN))
|
|
gtpv1uReceiver(events[i].data.fd);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|