/*******************************************************************************
OpenAirInterface
Copyright(c) 1999 - 2014 Eurecom
OpenAirInterface is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenAirInterface is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenAirInterface.The full GNU General Public License is
included in this distribution in the file called "COPYING". If not,
see .
Contact Information
OpenAirInterface Admin: openair_admin@eurecom.fr
OpenAirInterface Tech : openair_tech@eurecom.fr
OpenAirInterface Dev : openair4g-devel@eurecom.fr
Address : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
*******************************************************************************/
/*! \file gtpv1u_task.c
* \brief
* \author Sebastien ROUX, Lionel Gauthier
* \company Eurecom
* \email: lionel.gauthier@eurecom.fr
*/
#include
#include
#include
#include
#include
#include "mme_config.h"
#include "assertions.h"
#include "intertask_interface.h"
#include "timer.h"
#include "gtpv1u.h"
#include "NwGtpv1u.h"
#include "NwGtpv1uMsg.h"
#include "NwLog.h"
#include "gtpv1u_sgw_defs.h"
#include "NwGtpv1uPrivate.h"
#include "msc.h"
//static NwGtpv1uStackHandleT gtpv1u_stack = 0;
static gtpv1u_data_t gtpv1u_sgw_data;
static int gtpv1u_create_s1u_tunnel(Gtpv1uCreateTunnelReq *create_tunnel_reqP);
static int gtpv1u_delete_s1u_tunnel(Teid_t context_teidP, Teid_t S1U_teidP);
static int gtpv1u_update_s1u_tunnel(Gtpv1uUpdateTunnelReq *reqP);
static void *gtpv1u_thread(void *args);
NwGtpv1uRcT gtpv1u_send_udp_msg(
NwGtpv1uUdpHandleT udpHandle,
uint8_t *buffer,
uint32_t buffer_len,
uint32_t buffer_offset,
uint32_t peerIpAddr,
uint32_t peerPort);
NwGtpv1uRcT gtpv1u_log_request(
NwGtpv1uLogMgrHandleT hLogMgr,
uint32_t logLevel,
NwCharT *file,
uint32_t line,
NwCharT *logStr);
NwGtpv1uRcT gtpv1u_process_stack_req(
NwGtpv1uUlpHandleT hUlp,
NwGtpv1uUlpApiT *pUlpApi);
//-----------------------------------------------------------------------------
void gtpu_print_hex_octets(unsigned char* dataP, unsigned long sizeP)
//-----------------------------------------------------------------------------
{
unsigned long octet_index = 0;
unsigned long buffer_marker = 0;
unsigned char aindex;
#define GTPU_2_PRINT_BUFFER_LEN 8000
char gtpu_2_print_buffer[GTPU_2_PRINT_BUFFER_LEN];
struct timeval tv;
struct timezone tz;
char timeofday[64];
unsigned int h,m,s;
if (dataP == NULL) {
return;
}
gettimeofday(&tv, &tz);
h = tv.tv_sec/3600/24;
m = (tv.tv_sec / 60) % 60;
s = tv.tv_sec % 60;
snprintf(timeofday, 64, "%02d:%02d:%02d.%06d", h,m,s,tv.tv_usec);
GTPU_DEBUG("%s------+-------------------------------------------------|\n",timeofday);
GTPU_DEBUG("%s | 0 1 2 3 4 5 6 7 8 9 a b c d e f |\n",timeofday);
GTPU_DEBUG("%s------+-------------------------------------------------|\n",timeofday);
for (octet_index = 0; octet_index < sizeP; octet_index++) {
if (GTPU_2_PRINT_BUFFER_LEN < (buffer_marker + 32)) {
buffer_marker+=snprintf(>pu_2_print_buffer[buffer_marker], GTPU_2_PRINT_BUFFER_LEN - buffer_marker,
"... (print buffer overflow)");
GTPU_DEBUG("%s%s",timeofday,gtpu_2_print_buffer);
return;
}
if ((octet_index % 16) == 0) {
if (octet_index != 0) {
buffer_marker+=snprintf(>pu_2_print_buffer[buffer_marker], GTPU_2_PRINT_BUFFER_LEN - buffer_marker, " |\n");
GTPU_DEBUG("%s%s",timeofday, gtpu_2_print_buffer);
buffer_marker = 0;
}
buffer_marker+=snprintf(>pu_2_print_buffer[buffer_marker], GTPU_2_PRINT_BUFFER_LEN - buffer_marker, " %04ld |", octet_index);
}
/*
* Print every single octet in hexadecimal form
*/
buffer_marker+=snprintf(>pu_2_print_buffer[buffer_marker], GTPU_2_PRINT_BUFFER_LEN - buffer_marker, " %02x", dataP[octet_index]);
/*
* Align newline and pipes according to the octets in groups of 2
*/
}
/*
* Append enough spaces and put final pipe
*/
for (aindex = octet_index; aindex < 16; ++aindex)
buffer_marker+=snprintf(>pu_2_print_buffer[buffer_marker], GTPU_2_PRINT_BUFFER_LEN - buffer_marker, " ");
//GTPU_DEBUG(" ");
buffer_marker+=snprintf(>pu_2_print_buffer[buffer_marker], GTPU_2_PRINT_BUFFER_LEN - buffer_marker, " |\n");
GTPU_DEBUG("%s%s",timeofday,gtpu_2_print_buffer);
}
NwGtpv1uRcT gtpv1u_log_request(NwGtpv1uLogMgrHandleT hLogMgr,
uint32_t logLevel,
NwCharT *file,
uint32_t line,
NwCharT *logStr)
{
GTPU_DEBUG("%s\n", logStr);
return NW_GTPV1U_OK;
}
NwGtpv1uRcT gtpv1u_send_udp_msg(
NwGtpv1uUdpHandleT udpHandle,
uint8_t *buffer,
uint32_t buffer_len,
uint32_t buffer_offset,
uint32_t peerIpAddr,
uint32_t peerPort)
{
// Create and alloc new message
MessageDef *message_p;
udp_data_req_t *udp_data_req_p;
message_p = itti_alloc_new_message(TASK_GTPV1_U, UDP_DATA_REQ);
udp_data_req_p = &message_p->ittiMsg.udp_data_req;
udp_data_req_p->peer_address = peerIpAddr;
udp_data_req_p->peer_port = peerPort;
udp_data_req_p->buffer = buffer;
udp_data_req_p->buffer_length = buffer_len;
udp_data_req_p->buffer_offset = buffer_offset;
return itti_send_msg_to_task(TASK_UDP, INSTANCE_DEFAULT, message_p);
}
/* 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;
MessageDef *message_p = NULL;
Gtpv1uTunnelDataInd *data_ind_p = NULL;
/* Nw-gptv1u stack has processed a PDU. we can forward it to IPV4
* task for transmission.
*/
/*if (NW_GTPV1U_OK != nwGtpv1uMsgGetTpdu(pUlpApi->apiInfo.recvMsgInfo.hMsg,
buffer, (uint32_t *)&buffer_len)) {
GTPU_ERROR("Error while retrieving T-PDU\n");
}*/
GTPU_DEBUG("Received TPDU from gtpv1u stack %u with size %d\n",
pUlpApi->apiInfo.recvMsgInfo.teid,
((NwGtpv1uMsgT*)pUlpApi->apiInfo.recvMsgInfo.hMsg)->msgBufLen);
message_p = itti_alloc_new_message(TASK_GTPV1_U, GTPV1U_TUNNEL_DATA_IND);
if (message_p == NULL) {
return -1;
}
data_ind_p = &message_p->ittiMsg.gtpv1uTunnelDataInd;
data_ind_p->buffer = ((NwGtpv1uMsgT*)pUlpApi->apiInfo.recvMsgInfo.hMsg)->msgBuf;
data_ind_p->length = ((NwGtpv1uMsgT*)pUlpApi->apiInfo.recvMsgInfo.hMsg)->msgBufLen;
data_ind_p->offset = ((NwGtpv1uMsgT*)pUlpApi->apiInfo.recvMsgInfo.hMsg)->msgBufOffset;
data_ind_p->local_S1u_teid = pUlpApi->apiInfo.recvMsgInfo.teid;
if (data_ind_p->buffer == NULL) {
GTPU_ERROR("Failed to allocate new buffer\n");
itti_free(ITTI_MSG_ORIGIN_ID(message_p), message_p);
message_p = NULL;
} else {
//memcpy(data_ind_p->buffer, buffer, buffer_len);
//data_ind_p->length = buffer_len;
if (itti_send_msg_to_task(TASK_FW_IP, INSTANCE_DEFAULT, message_p) < 0) {
GTPU_ERROR("Failed to send message to task\n");
itti_free(ITTI_MSG_ORIGIN_ID(message_p), message_p);
message_p = NULL;
}
}
}
break;
case NW_GTPV1U_ULP_API_CREATE_TUNNEL_ENDPOINT: {
}
break;
default: {
GTPU_ERROR("Received undefined UlpApi (%02x) from gtpv1u stack!\n",
pUlpApi->apiType);
}
}
return NW_GTPV1U_OK;
}
static int gtpv1u_create_s1u_tunnel(Gtpv1uCreateTunnelReq *create_tunnel_reqP)
{
/* Create a new nw-gtpv1-u stack req using API */
NwGtpv1uUlpApiT stack_req;
NwGtpv1uRcT rc;
/* Local tunnel end-point identifier */
uint32_t s1u_teid = 0;
gtpv1u_teid2enb_info_t *gtpv1u_teid2enb_info = NULL;
MessageDef *message_p = NULL;
hashtable_rc_t hash_rc;
GTPU_DEBUG("Rx GTPV1U_CREATE_TUNNEL_REQ Context %d\n", create_tunnel_reqP->context_teid);
memset(&stack_req, 0, sizeof(NwGtpv1uUlpApiT));
stack_req.apiType = NW_GTPV1U_ULP_API_CREATE_TUNNEL_ENDPOINT;
do {
s1u_teid = gtpv1u_new_teid();
GTPU_DEBUG("gtpv1u_create_s1u_tunnel() %u\n", s1u_teid);
stack_req.apiInfo.createTunnelEndPointInfo.teid = s1u_teid;
stack_req.apiInfo.createTunnelEndPointInfo.hUlpSession = 0;
stack_req.apiInfo.createTunnelEndPointInfo.hStackSession = 0;
rc = nwGtpv1uProcessUlpReq(gtpv1u_sgw_data.gtpv1u_stack, &stack_req);
GTPU_DEBUG(".\n");
} while (rc != NW_GTPV1U_OK);
gtpv1u_teid2enb_info = malloc (sizeof(gtpv1u_teid2enb_info_t));
memset(gtpv1u_teid2enb_info, 0, sizeof(gtpv1u_teid2enb_info_t));
gtpv1u_teid2enb_info->state = BEARER_IN_CONFIG;
//#warning !!! hack because missing modify session request, so force enb address
// gtpv1u_teid2enb_info->enb_ip_addr.pdn_type = IPv4;
// gtpv1u_teid2enb_info->enb_ip_addr.address.ipv4_address[0] = 192;
// gtpv1u_teid2enb_info->enb_ip_addr.address.ipv4_address[1] = 168;
// gtpv1u_teid2enb_info->enb_ip_addr.address.ipv4_address[2] = 1;
// gtpv1u_teid2enb_info->enb_ip_addr.address.ipv4_address[3] = 2;
message_p = itti_alloc_new_message(TASK_GTPV1_U, GTPV1U_CREATE_TUNNEL_RESP);
message_p->ittiMsg.gtpv1uCreateTunnelResp.S1u_teid = s1u_teid;
message_p->ittiMsg.gtpv1uCreateTunnelResp.context_teid = create_tunnel_reqP->context_teid;
message_p->ittiMsg.gtpv1uCreateTunnelResp.eps_bearer_id = create_tunnel_reqP->eps_bearer_id;
hash_rc = hashtable_is_key_exists(gtpv1u_sgw_data.S1U_mapping, s1u_teid);
if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS) {
hash_rc = hashtable_insert(gtpv1u_sgw_data.S1U_mapping, s1u_teid, gtpv1u_teid2enb_info);
message_p->ittiMsg.gtpv1uCreateTunnelResp.status = 0;
} else {
message_p->ittiMsg.gtpv1uCreateTunnelResp.status = 0xFF;
}
GTPU_DEBUG("Tx GTPV1U_CREATE_TUNNEL_RESP Context %u teid %u eps bearer id %u status %d\n",
message_p->ittiMsg.gtpv1uCreateTunnelResp.context_teid,
message_p->ittiMsg.gtpv1uCreateTunnelResp.S1u_teid,
message_p->ittiMsg.gtpv1uCreateTunnelResp.eps_bearer_id,
message_p->ittiMsg.gtpv1uCreateTunnelResp.status);
return itti_send_msg_to_task(TASK_SPGW_APP, INSTANCE_DEFAULT, message_p);
}
static int gtpv1u_delete_s1u_tunnel(Teid_t context_teidP, Teid_t S1U_teidP)
{
/* Local tunnel end-point identifier */
MessageDef *message_p;
GTPU_DEBUG("Rx GTPV1U_DELETE_TUNNEL Context %u S1U teid %u\n", context_teidP, S1U_teidP);
message_p = itti_alloc_new_message(TASK_GTPV1_U, GTPV1U_DELETE_TUNNEL_RESP);
message_p->ittiMsg.gtpv1uDeleteTunnelResp.S1u_teid = S1U_teidP;
message_p->ittiMsg.gtpv1uDeleteTunnelResp.context_teid = context_teidP;
if (hashtable_remove(gtpv1u_sgw_data.S1U_mapping, S1U_teidP) == HASH_TABLE_OK ) {
message_p->ittiMsg.gtpv1uDeleteTunnelResp.status = 0;
} else {
message_p->ittiMsg.gtpv1uDeleteTunnelResp.status = -1;
}
GTPU_DEBUG("Tx SGW_S1U_ENDPOINT_CREATED Context %u teid %u status %d\n", context_teidP, S1U_teidP, message_p->ittiMsg.gtpv1uDeleteTunnelResp.status);
return itti_send_msg_to_task(TASK_SPGW_APP, INSTANCE_DEFAULT, message_p);
}
static int gtpv1u_update_s1u_tunnel(Gtpv1uUpdateTunnelReq *reqP)
{
hashtable_rc_t hash_rc;
gtpv1u_teid2enb_info_t *gtpv1u_teid2enb_info;
MessageDef *message_p;
GTPU_DEBUG("Rx GTPV1U_UPDATE_TUNNEL_REQ Context %u, S-GW S1U teid %u, eNB S1U teid %u\n",
reqP->context_teid,
reqP->sgw_S1u_teid,
reqP->enb_S1u_teid);
message_p = itti_alloc_new_message(TASK_GTPV1_U, GTPV1U_UPDATE_TUNNEL_RESP);
hash_rc = hashtable_get(gtpv1u_sgw_data.S1U_mapping, reqP->sgw_S1u_teid, (void**)>pv1u_teid2enb_info);
if (hash_rc == HASH_TABLE_OK) {
gtpv1u_teid2enb_info->teid_enb = reqP->enb_S1u_teid;
gtpv1u_teid2enb_info->enb_ip_addr = reqP->enb_ip_address_for_S1u;
gtpv1u_teid2enb_info->state = BEARER_UP;
gtpv1u_teid2enb_info->port = GTPV1U_UDP_PORT;
message_p->ittiMsg.gtpv1uUpdateTunnelResp.status = 0; ///< Status (Failed = 0xFF or Success = 0x0)
} else {
GTPU_ERROR("Mapping not found\n");
message_p->ittiMsg.gtpv1uUpdateTunnelResp.status = 0xFF; ///< Status (Failed = 0xFF or Success = 0x0)
}
message_p->ittiMsg.gtpv1uUpdateTunnelResp.context_teid = reqP->context_teid;
message_p->ittiMsg.gtpv1uUpdateTunnelResp.sgw_S1u_teid = reqP->sgw_S1u_teid;
message_p->ittiMsg.gtpv1uUpdateTunnelResp.enb_S1u_teid = reqP->enb_S1u_teid;
message_p->ittiMsg.gtpv1uUpdateTunnelResp.eps_bearer_id = reqP->eps_bearer_id;
return itti_send_msg_to_task(TASK_SPGW_APP, INSTANCE_DEFAULT, message_p);
}
static NwGtpv1uRcT gtpv1u_start_timer_wrapper(
NwGtpv1uTimerMgrHandleT tmrMgrHandle,
uint32_t timeoutSec,
uint32_t timeoutUsec,
uint32_t tmrType,
void *timeoutArg,
NwGtpv1uTimerHandleT *hTmr)
{
NwGtpv1uRcT rc = NW_GTPV1U_OK;
long timer_id;
if (tmrType == NW_GTPV1U_TMR_TYPE_ONE_SHOT) {
timer_setup(timeoutSec,
timeoutUsec,
TASK_GTPV1_U,
INSTANCE_DEFAULT,
TIMER_ONE_SHOT,
timeoutArg,
&timer_id);
} else {
timer_setup(timeoutSec,
timeoutUsec,
TASK_GTPV1_U,
INSTANCE_DEFAULT,
TIMER_PERIODIC,
timeoutArg,
&timer_id);
}
return rc;
}
static NwGtpv1uRcT gtpv1u_stop_timer_wrapper(
NwGtpv1uTimerMgrHandleT tmrMgrHandle,
NwGtpv1uTimerHandleT hTmr)
{
NwGtpv1uRcT rc = NW_GTPV1U_OK;
return rc;
}
static void *gtpv1u_thread(void *args)
{
itti_mark_task_ready(TASK_GTPV1_U);
MSC_START_USE();
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 *received_message_p = NULL;
itti_receive_msg(TASK_GTPV1_U, &received_message_p);
DevAssert(received_message_p != NULL);
switch (ITTI_MSG_ID(received_message_p)) {
case TERMINATE_MESSAGE: {
itti_exit_task();
}
break;
// DATA COMING FROM UDP
case UDP_DATA_IND: {
udp_data_ind_t *udp_data_ind_p;
udp_data_ind_p = &received_message_p->ittiMsg.udp_data_ind;
nwGtpv1uProcessUdpReq(gtpv1u_sgw_data.gtpv1u_stack,
udp_data_ind_p->buffer,
udp_data_ind_p->buffer_length,
udp_data_ind_p->peer_port,
udp_data_ind_p->peer_address);
//itti_free(ITTI_MSG_ORIGIN_ID(received_message_p), udp_data_ind_p->buffer);
}
break;
case TIMER_HAS_EXPIRED:
nwGtpv1uProcessTimeout(&received_message_p->ittiMsg.timer_has_expired.arg);
break;
default: {
GTPU_ERROR("Unkwnon message ID %d:%s\n",
ITTI_MSG_ID(received_message_p),
ITTI_MSG_NAME(received_message_p));
}
break;
}
itti_free(ITTI_MSG_ORIGIN_ID(received_message_p), received_message_p);
received_message_p = NULL;
}
return NULL;
}
int gtpv1u_init(const mme_config_t *mme_config_p)
{
int ret = 0;
NwGtpv1uRcT rc = 0;
NwGtpv1uUlpEntityT ulp;
NwGtpv1uUdpEntityT udp;
NwGtpv1uLogMgrEntityT log;
NwGtpv1uTimerMgrEntityT tmr;
GTPU_DEBUG("Initializing GTPV1U interface\n");
memset(>pv1u_sgw_data, 0, sizeof(gtpv1u_sgw_data));
gtpv1u_sgw_data.sgw_ip_address_for_S1u_S12_S4_up = mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up;
if (itti_create_task(TASK_GTPV1_U, >pv1u_thread, NULL) < 0) {
GTPU_ERROR("gtpv1u phtread_create: %s", strerror(errno));
return -1;
}
GTPU_DEBUG("Initializing GTPV1U interface: DONE\n");
return ret;
}