Files
openairinterface5g/openair-cn/GTPV1-U/gtpv1u_eNB.c
T

521 lines
18 KiB
C

#include <stdio.h>
#include <errno.h>
#include "NwGtpv1u.h"
#include "NwGtpv1uMsg.h"
#include "NwLog.h"
#include "log.h"
#include "gtpv1u_eNB_defs.h"
#include "udp_primitives_client.h"
#include "UTIL/LOG/log.h"
#include "COMMON/platform_types.h"
#ifdef GTPU_IN_KERNEL
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#endif
extern unsigned char pdcp_data_req (module_id_t module_id, u32_t frame, u8_t eNB_flag, rb_id_t rab_id, u32 muiP, u32 confirmP, sdu_size_t sdu_buffer_size, unsigned char* sdu_buffer, u8 is_data_pdu);
inline NwGtpv1uRcT gtpv1u_eNB_log_request(NwGtpv1uLogMgrHandleT hLogMgr,
NwU32T logLevel,
NwCharT *file,
NwU32T line,
NwCharT *logStr)
{
logIt(GTPU, logLevel, "%s\n", logStr);
return NW_GTPV1U_OK;
}
/* Callback called when a gtpv1u message arrived on UDP interface */
NwGtpv1uRcT gtpv1u_process_stack_req(
NwGtpv1uUlpHandleT hUlp,
NwGtpv1uUlpApiT *pUlpApi)
{
switch(pUlpApi->apiType) {
/* Here there are two type of messages handled:
* - T-PDU
* - END-MARKER
*/
case NW_GTPV1U_ULP_API_RECV_TPDU: {
uint8_t buffer[4096];
uint32_t buffer_len;
/* Nw-gptv1u stack has processed a PDU. we can schedule it to PDCP
* for transmission.
*/
if (NW_GTPV1U_OK != nwGtpv1uMsgGetTpdu(pUlpApi->apiInfo.recvMsgInfo.hMsg,
buffer, &buffer_len)) {
GTPU_ERROR("Error while retrieving T-PDU");
}
GTPU_DEBUG("Received T-PDU from gtpv1u stack %u with size %d",
pUlpApi->apiInfo.recvMsgInfo.teid, buffer_len);
pdcp_data_req(0, // module_id,
0, // frame
1, // enb flag
5, // rb id
0, // mui
0, // confirm
buffer_len,
buffer,
1);
}
break;
default: {
GTPU_ERROR("Received undefined UlpApi (%02x) from gtpv1u stack!\n",
pUlpApi->apiType);
}
}
return NW_GTPV1U_OK;
}
NwGtpv1uRcT gtpv1u_eNB_udp_req(NwGtpv1uUdpHandleT udpHandle,
NwU8T *dataBuf,
NwU32T dataSize,
NwU32T peerIpAddr,
NwU16T peerPort)
{
udp_data_t *udp_data_p;
if (udpHandle == 0) {
return NW_GTPV1U_FAILURE;
}
udp_data_p = (udp_data_t *)udpHandle;
LOG_D(GTPU, "New udp req triggered with sd %d, data size %u\n",
udp_data_p->sd, dataSize);
if (udp_send_to(udp_data_p->sd, peerPort, peerIpAddr, dataBuf, dataSize) < 0) {
return NW_GTPV1U_FAILURE;
}
return NW_GTPV1U_OK;
}
int data_recv_callback(uint16_t port,
uint32_t address,
uint8_t *buffer,
uint32_t length,
void *arg_p)
{
gtpv1u_data_t *gtpv1u_data_p;
if (arg_p == NULL) {
return -1;
}
gtpv1u_data_p = (gtpv1u_data_t *)arg_p;
return nwGtpv1uProcessUdpReq(gtpv1u_data_p->gtpv1u_stack,
buffer,
length,
port,
address);
}
int gtpv1u_create_tunnel_endpoint(gtpv1u_data_t *gtpv1u_data_p, uint8_t ue_id,
uint8_t rab_id, char *sgw_ip_addr, uint16_t port)
{
uint32_t teid;
uint8_t max_attempt = 100;
NwGtpv1uRcT rc;
NwGtpv1uUlpApiT ulp_req;
struct gtpv1u_ue_data_s *new_ue_p;
struct gtpv1u_ue_data_s *temp;
struct gtpv1u_bearer_s *bearer;
hashtbl_rc_t hash_rc;
if (rab_id > MAX_BEARERS_PER_UE) {
LOG_E(GTPU, "Could not use rab_id %d > max %d\n",
rab_id, MAX_BEARERS_PER_UE);
return -1;
}
if ((hash_rc = hashtbl_get(gtpv1u_data_p->ue_mapping, (uint64_t)ue_id, (void**)&new_ue_p)) == HASH_TABLE_OK) {
/* A context for this UE already exist in the tree, use it */
/* We check that the tunnel is not already configured */
if (new_ue_p->bearers[rab_id].state != BEARER_DOWN) {
LOG_E(GTPU, "Cannot create new end-point over already existing tunnel\n");
return -1;
}
} else {
/* Context doesn't exist, create it */
if (rab_id != 0) {
/* UE should first establish Default bearer before trying to setup
* additional bearers.
*/
LOG_E(GTPU, "UE context is not known and rab_id != 0\n");
return -1;
}
new_ue_p = calloc(1, sizeof(struct gtpv1u_ue_data_s));
new_ue_p->ue_id = ue_id;
hash_rc = hashtbl_insert(gtpv1u_data_p->ue_mapping, (uint64_t)ue_id, new_ue_p);
if ((hash_rc != HASH_TABLE_OK) && (hash_rc != HASH_TABLE_INSERT_OVERWRITTEN_DATA)) {
LOG_E(GTPU, "Failed to insert new UE context\n");
free(new_ue_p);
return -1;
}
}
bearer = &new_ue_p->bearers[rab_id];
/* Configure the bearer */
bearer->state = BEARER_IN_CONFIG;
bearer->sgw_ip_addr = inet_addr(sgw_ip_addr);
bearer->port = port;
/* Create the new stack api request */
memset(&ulp_req, 0, sizeof(NwGtpv1uUlpApiT));
ulp_req.apiType = NW_GTPV1U_ULP_API_CREATE_TUNNEL_ENDPOINT;
/* Try to create new tunnel-endpoint.
* If teid generated is already present in the stack, just peek another random
* teid. This could be ok for small number of tunnel but more errors could be
* thrown if we reached high number of tunnels.
* TODO: find a solution for teid
*/
do {
/* Request for a new random TEID */
teid = gtpv1u_new_teid();
ulp_req.apiInfo.createTunnelEndPointInfo.teid = teid;
rc = nwGtpv1uProcessUlpReq(gtpv1u_data_p->gtpv1u_stack, &ulp_req);
if (rc == NW_GTPV1U_OK) {
// LOG_D(GTPU, "Successfully created new tunnel endpoint for teid 0x%x\n",
// teid);
bearer->teid_eNB = teid;
// gtpv1u_initial_req(gtpv1u_data_p, teid, GTPV1U_UDP_PORT,
// inet_addr("192.168.56.101"));
LOG_I(GTPU, "Created eNB tunnel endpoint %u for ue id %u, rab id %u\n", teid, ue_id, rab_id);
return 0;
} else {
LOG_W(GTPU, "Teid %u already in use... %s\n",
teid, (max_attempt > 1) ? "Trying another one" : "Last chance");
}
} while(max_attempt-- && rc != NW_GTPV1U_OK);
bearer->state = BEARER_DOWN;
LOG_I(GTPU, "Failed to created eNB tunnel endpoint %u for ue id %u, rab id %u, bearer down\n", teid, ue_id, rab_id);
return -1;
}
int gtpv1u_initial_req(gtpv1u_data_t *gtpv1u_data_p, uint32_t teid,
uint16_t port, uint32_t address)
{
NwGtpv1uUlpApiT ulp_req;
NwGtpv1uRcT rc;
memset(&ulp_req, 0, sizeof(NwGtpv1uUlpApiT));
ulp_req.apiType = NW_GTPV1U_ULP_API_INITIAL_REQ;
ulp_req.apiInfo.initialReqInfo.teid = teid;
ulp_req.apiInfo.initialReqInfo.peerPort = port;
ulp_req.apiInfo.initialReqInfo.peerIp = address;
rc = nwGtpv1uProcessUlpReq(gtpv1u_data_p->gtpv1u_stack, &ulp_req);
if (rc == NW_GTPV1U_OK) {
LOG_D(GTPU, "Successfully sent initial req for teid %u\n", teid);
} else {
LOG_W(GTPU, "Could not send initial req for teid %u\n", teid);
}
return (rc == NW_GTPV1U_OK) ? 0 : -1;
}
int gtpv1u_new_data_req(gtpv1u_data_t *gtpv1u_data_p,
uint8_t ue_id, uint8_t rab_id,
uint8_t *buffer, uint32_t buf_len)
{
#ifdef GTPU_IN_KERNEL
struct sockaddr_in dummy_dest_addr;
socklen_t socklen = sizeof(struct sockaddr_in);
#endif
NwGtpv1uUlpApiT stack_req;
NwGtpv1uRcT rc;
struct gtpv1u_ue_data_s ue;
struct gtpv1u_ue_data_s *ue_inst_p;
struct gtpv1u_bearer_s *bearer_p;
hashtbl_rc_t hash_rc;
memset(&ue, 0, sizeof(struct gtpv1u_ue_data_s));
ue.ue_id = ue_id;
assert(gtpv1u_data_p != NULL);
assert(rab_id <= MAX_BEARERS_PER_UE);
/* Check that UE context is present in ue map. */
hash_rc = hashtbl_get(gtpv1u_data_p->ue_mapping, (uint64_t)ue_id, (void**)&ue_inst_p);
if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS ) {
LOG_E(GTPU, "[UE %d] Trying to send data on non-existing UE context\n", ue_id);
return -1;
}
bearer_p = &ue_inst_p->bearers[rab_id];
/* Ensure the bearer in ready.
* TODO: handle the cases where the bearer is in HANDOVER state.
* In such case packets should be placed in FIFO.
*/
if (bearer_p->state != BEARER_UP) {
LOG_W(GTPU, "Trying to send data over bearer with state(%u) != BEARER_UP\n",
bearer_p->state);
#warning LG: HACK WHILE WAITING FOR NAS, normally return -1
if (bearer_p->state != BEARER_IN_CONFIG)
return -1;
}
#ifdef GTPU_IN_KERNEL
dummy_dest_addr.sin_family = AF_INET;
dummy_dest_addr.sin_port = 5001;
dummy_dest_addr.sin_addr.s_addr = inet_addr("178.179.180.181");
if (sendto(gtpv1u_data_p->sock_desc[rab_id], (void *)buffer, buf_len, 0, (struct sockaddr *)&dummy_dest_addr, socklen) < 0) {
LOG_E(GTPU, "Error during send to socket %d : (%s:%d)\n", gtpv1u_data_p->sock_desc[rab_id], strerror(errno), errno);
return -1;
} else {
LOG_D(GTPU, "send to UDP socket %d, packet should be handled by iptables\n", gtpv1u_data_p->sock_desc[rab_id]);
}
#else
memset(&stack_req, 0, sizeof(NwGtpv1uUlpApiT));
stack_req.apiType = NW_GTPV1U_ULP_API_SEND_TPDU;
// LG HACK stack_req.apiInfo.sendtoInfo.teid = bearer_p->teid_sgw;
stack_req.apiInfo.sendtoInfo.teid = 1;// LG HACK
stack_req.apiInfo.sendtoInfo.ipAddr = bearer_p->sgw_ip_addr;
rc = nwGtpv1uGpduMsgNew(gtpv1u_data_p->gtpv1u_stack,
//bearer_p->teid_sgw,
1, // LG FORCING 1 instead of bearer_p->teid_sgw
NW_FALSE,
gtpv1u_data_p->seq_num++,
buffer,
buf_len,
&(stack_req.apiInfo.sendtoInfo.hMsg));
if (rc != NW_GTPV1U_OK) {
LOG_E(GTPU, "nwGtpv1uGpduMsgNew failed: 0x%x\n", rc);
return -1;
}
rc = nwGtpv1uProcessUlpReq(gtpv1u_data_p->gtpv1u_stack,
&stack_req);
if (rc != NW_GTPV1U_OK) {
LOG_E(GTPU, "nwGtpv1uProcessUlpReq failed: 0x%x\n", rc);
return -1;
}
rc = nwGtpv1uMsgDelete(gtpv1u_data_p->gtpv1u_stack,
stack_req.apiInfo.sendtoInfo.hMsg);
if (rc != NW_GTPV1U_OK) {
LOG_E(GTPU, "nwGtpv1uMsgDelete failed: 0x%x\n", rc);
return -1;
}
#endif
LOG_E(GTPU, "%s() return code OK\n", __FUNCTION__);
return 0;
}
#ifdef GTPU_IN_KERNEL
#undef GTPV1U_PACKET_RX_RING
/// The number of frames in the ring
// This number is not set in stone. Nor are block_size, block_nr or frame_size
#define CONF_RING_FRAMES 128
/// Offset of data from start of frame
#define PKT_OFFSET (TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + \
TPACKET_ALIGN(sizeof(struct sockaddr_ll)))
int gtpv1u_eNB_create_sockets(gtpv1u_data_t *gtpv1u_data_p)
{
int value, mark;
const int *val_p=&value;
struct ifreq ifr;
#ifdef GTPV1U_PACKET_RX_RING
struct tpacket_req tp;
#endif
int i;
if (gtpv1u_data_p == NULL) {
return -1;
}
GTPU_DEBUG("Creating socket for GTPV1U on %s if index %u\n", gtpv1u_data_p->interface_name, gtpv1u_data_p->interface_index);
for (mark = 0; mark <= 15; mark++) {
gtpv1u_data_p->sock_desc[mark] = socket( PF_INET , SOCK_DGRAM, 0);
if (gtpv1u_data_p->sock_desc[mark] < 0) {
GTPU_ERROR("Error during socket creation (%s:%d)\n",strerror(errno), errno);
goto error;
}
// // socket options, tell the kernel we provide the IP structure
// if(setsockopt(sgi_data_p->sd[rab_id], IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)
// {
// SGI_IF_ERROR("Error during socket setsockopt IP_HDRINCL (%s:%d)\n", strerror(errno), errno);
// goto error;
// }
//
//
// setting socket option to use MARK value
//value = rab_id + SGI_MIN_EPS_BEARER_ID;
value = mark;
if (setsockopt (gtpv1u_data_p->sock_desc[mark], SOL_SOCKET, SO_MARK, val_p, sizeof (value)) < 0)
{
GTPU_ERROR("error notifying kernel about MARK");
goto error;
}
GTPU_DEBUG("Created socket %d for rab_id %d (for any UE context)\n", gtpv1u_data_p->sock_desc[mark], value);
#ifdef SGI_PACKET_RX_RING
// tell kernel to export data through mmap()ped ring
tp.tp_block_size = CONF_RING_FRAMES * getpagesize();
tp.tp_block_nr = 1;
tp.tp_frame_size = getpagesize();
tp.tp_frame_nr = CONF_RING_FRAMES;
if (setsockopt(gtpv1u_data_p->sock_desc[mark], SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp))) {
GTPU_ERROR("setsockopt() ring\n");
goto error;
}
// open ring
gtpv1u_data_p->sock_mmap_ring[mark] = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sgi_data_p->sd[mark], 0);
if (!gtpv1u_data_p->sock_mmap_ring[mark]) {
GTPU_ERROR("Failed to mmap socket (%s:%d)\n", strerror(errno), errno);
goto error;
}
/* Setup our ringbuffer */
gtpv1u_data_p->malloc_ring[mark] = malloc(tp.tp_frame_nr * sizeof(struct iovec));
for(i=0; i<tp.tp_frame_nr; i++) {
gtpv1u_data_p->malloc_ring[mark][i].iov_base=(void *)((long)gtpv1u_data_p->sock_mmap_ring[mark])+(i*tp.tp_frame_size);
gtpv1u_data_p->malloc_ring[mark][i].iov_len=tp.tp_frame_size;
}
#endif
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), gtpv1u_data_p->interface_name);
if (setsockopt (gtpv1u_data_p->sock_desc[mark], SOL_SOCKET, SO_BINDTODEVICE,(void *)&ifr, sizeof(ifr)) < 0)
{
GTPU_ERROR("error notifying kernel about MARK");
goto error;
}
GTPU_DEBUG("Created socket %d for rab_id %d (for any UE context)\n", gtpv1u_data_p->sock_desc[mark], value);
}
return 0;
error:
GTPU_ERROR("ERROR (%s)\n", strerror(errno));
for (mark = 0; mark <= 15; mark++) {
if (gtpv1u_data_p->sock_desc[mark] > 0) {
close(gtpv1u_data_p->sock_desc[mark]);
}
gtpv1u_data_p->sock_desc[mark] = -1;
}
return -1;
}
#endif
int gtpv1u_eNB_init(gtpv1u_data_t *gtpv1u_data_p)
{
NwGtpv1uRcT rc;
NwGtpv1uLogMgrEntityT log;
NwGtpv1uUdpEntityT udp;
NwGtpv1uUlpEntityT ulp;
if (gtpv1u_data_p == NULL) {
return -1;
}
//(gtpv1u_data_p was allocated with calloc (zeroed))
// GTPU_INFO("Initializing GTPU stack for eNB %u\n",
// gtpv1u_data_p->eNB_id);
/* Initialize UE tree */
//RB_INIT(&gtpv1u_data_p->gtpv1u_ue_map_head);
#ifdef GTPU_IN_KERNEL
#warning hardcoded ENB GTPV1U interface name
gtpv1u_data_p->interface_name = "upenb0";
gtpv1u_data_p->interface_index = if_nametoindex(gtpv1u_data_p->interface_name);
gtpv1u_eNB_create_sockets(gtpv1u_data_p);
#endif
/* Initialize UE hashtable */
gtpv1u_data_p->ue_mapping = hashtbl_create (256, NULL, NULL);
if (gtpv1u_data_p->ue_mapping == NULL) {
perror("hashtbl_create");
GTPU_ERROR("Initializing TASK_GTPV1_U task interface: ERROR\n");
return -1;
}
if (udp_create_connection(gtpv1u_data_p->ip_addr, GTPV1U_UDP_PORT,
&gtpv1u_data_p->udp_data, data_recv_callback, (void *)gtpv1u_data_p) < 0) {
return -1;
}
/* Initializing GTPv1-U stack */
if ((rc = nwGtpv1uInitialize(&gtpv1u_data_p->gtpv1u_stack)) != NW_GTPV1U_OK) {
GTPU_ERROR("Failed to setup nwGtpv1u stack %x\n", rc);
return -1;
}
/* Set up the log interface and register the log entity */
log.logReqCallback = gtpv1u_eNB_log_request;
if ((rc = nwGtpv1uSetLogMgrEntity(gtpv1u_data_p->gtpv1u_stack,
&log)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetLogMgrEntity: %x\n", rc);
return -1;
}
udp.hUdp = (NwGtpv1uUdpHandleT) &gtpv1u_data_p->udp_data;
udp.udpDataReqCallback = gtpv1u_eNB_udp_req;
if ((rc = nwGtpv1uSetUdpEntity(gtpv1u_data_p->gtpv1u_stack,
&udp)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetUdpEntity: %x\n", rc);
return -1;
}
/* Set the ULP API callback. Called once message have been processed by the
* nw-gtpv1u stack.
*/
ulp.ulpReqCallback = gtpv1u_process_stack_req;
if ((rc = nwGtpv1uSetUlpEntity(gtpv1u_data_p->gtpv1u_stack,
&ulp)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetUlpEntity: %x\n", rc);
return -1;
}
if ((rc = nwGtpv1uSetLogLevel(gtpv1u_data_p->gtpv1u_stack,
NW_LOG_LEVEL_DEBG)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetLogLevel: %x\n", rc);
return -1;
}
gtpv1u_create_tunnel_endpoint(gtpv1u_data_p, 0, 0, "192.168.1.1", 2152);
// GTPU_INFO("Initializing GTPU stack for eNB %u: DONE\n",
// gtpv1u_data_p->eNB_id);
return 0;
}