Files
openairinterface5g/openair-cn/GTPV1-U/gtpv1u_task.c
T
2013-11-15 13:36:54 +00:00

555 lines
20 KiB
C

/*******************************************************************************
Eurecom OpenAirInterface
Copyright(c) 1999 - 2012 Eurecom
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope 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
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Contact Information
Openair Admin: openair_admin@eurecom.fr
Openair Tech : openair_tech@eurecom.fr
Forums : http://forums.eurecom.fr/openairinterface
Address : EURECOM, Campus SophiaTech, 450 Route des Chappes
06410 Biot FRANCE
*******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#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"
//static NwGtpv1uStackHandleT gtpv1u_stack = 0;
static gtpv1u_data_t gtpv1u_sgw_data;
static int gtpv1u_send_init_udp(uint16_t port_number);
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,
NwU8T *buffer,
NwU32T buffer_len,
NwU32T peerIpAddr,
NwU32T peerPort);
NwGtpv1uRcT gtpv1u_log_request(
NwGtpv1uLogMgrHandleT hLogMgr,
NwU32T logLevel,
NwCharT *file,
NwU32T line,
NwCharT *logStr);
NwGtpv1uRcT gtpv1u_process_stack_req(
NwGtpv1uUlpHandleT hUlp,
NwGtpv1uUlpApiT *pUlpApi);
static int gtpv1u_send_init_udp(uint16_t port_number)
{
// Create and alloc new message
MessageDef *message_p;
struct in_addr addr;
message_p = itti_alloc_new_message(TASK_GTPV1_U, UDP_INIT);
if (message_p == NULL) {
return -1;
}
message_p->msg.udp_init.port = port_number;
//LG message_p->msg.udp_init.address = "0.0.0.0"; //ANY address
addr.s_addr = gtpv1u_sgw_data.sgw_ip_address_for_S1u_S12_S4_up;
message_p->msg.udp_init.address = inet_ntoa(addr);
GTPU_DEBUG("Tx UDP_INIT IP addr %s\n", message_p->msg.udp_init.address);
return itti_send_msg_to_task(TASK_UDP, INSTANCE_DEFAULT, message_p);
}
NwGtpv1uRcT gtpv1u_log_request(NwGtpv1uLogMgrHandleT hLogMgr,
NwU32T logLevel,
NwCharT *file,
NwU32T line,
NwCharT *logStr)
{
GTPU_DEBUG("%s\n", logStr);
return NW_GTPV1U_OK;
}
NwGtpv1uRcT gtpv1u_send_udp_msg(
NwGtpv1uUdpHandleT udpHandle,
NwU8T *buffer,
NwU32T buffer_len,
NwU32T peerIpAddr,
NwU32T 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->msg.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;
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;
Gtpv1uTunnelDataInd *data_ind_p;
/* 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, (NwU32T *)&buffer_len)) {
GTPU_ERROR("Error while retrieving T-PDU");
}
GTPU_DEBUG("Received TPDU from gtpv1u stack %u with size %d", pUlpApi->apiInfo.recvMsgInfo.teid, buffer_len);
message_p = itti_alloc_new_message(TASK_GTPV1_U, GTPV1U_TUNNEL_DATA_IND);
if (message_p == NULL) {
return -1;
}
data_ind_p = &message_p->msg.gtpv1uTunnelDataInd;
data_ind_p->buffer = malloc(sizeof(uint8_t) * buffer_len);
data_ind_p->local_S1u_teid = pUlpApi->apiInfo.recvMsgInfo.teid;
if (data_ind_p->buffer == NULL) {
GTPU_ERROR("Failed to allocate new buffer\n");
free(message_p);
} 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");
free(message_p);
}
}
}
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;
gtpv1u_teid2enb_info_t *gtpv1u_teid2enb_info;
MessageDef *message_p;
hashtbl_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;
gtpv1u_teid2enb_info->state = BEARER_IN_CONFIG;
message_p = itti_alloc_new_message(TASK_GTPV1_U, GTPV1U_CREATE_TUNNEL_RESP);
message_p->msg.gtpv1uCreateTunnelResp.S1u_teid = s1u_teid;
message_p->msg.gtpv1uCreateTunnelResp.context_teid = create_tunnel_reqP->context_teid;
message_p->msg.gtpv1uCreateTunnelResp.eps_bearer_id = create_tunnel_reqP->eps_bearer_id;
hash_rc = hashtbl_is_key_exists(gtpv1u_sgw_data.S1U_mapping, s1u_teid);
if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS) {
hash_rc = hashtbl_insert(gtpv1u_sgw_data.S1U_mapping, s1u_teid, gtpv1u_teid2enb_info);
message_p->msg.gtpv1uCreateTunnelResp.status = 0;
} else {
message_p->msg.gtpv1uCreateTunnelResp.status = 0xFF;
}
GTPU_DEBUG("Tx GTPV1U_CREATE_TUNNEL_RESP Context %u teid %u eps bearer id %u status %d\n",
message_p->msg.gtpv1uCreateTunnelResp.context_teid,
message_p->msg.gtpv1uCreateTunnelResp.S1u_teid,
message_p->msg.gtpv1uCreateTunnelResp.eps_bearer_id,
message_p->msg.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->msg.gtpv1uDeleteTunnelResp.S1u_teid = S1U_teidP;
message_p->msg.gtpv1uDeleteTunnelResp.context_teid = context_teidP;
if (hashtbl_remove(gtpv1u_sgw_data.S1U_mapping, S1U_teidP) == HASH_TABLE_OK ) {
message_p->msg.gtpv1uDeleteTunnelResp.status = 0;
} else {
message_p->msg.gtpv1uDeleteTunnelResp.status = -1;
}
GTPU_DEBUG("Tx SGW_S1U_ENDPOINT_CREATED Context %u teid %u status %d\n", context_teidP, S1U_teidP, message_p->msg.gtpv1uDeleteTunnelResp.status);
return itti_send_msg_to_task(TASK_SPGW_APP, INSTANCE_DEFAULT, message_p);
}
static int gtpv1u_update_s1u_tunnel(Gtpv1uUpdateTunnelReq *reqP)
{
hashtbl_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 = hashtbl_get(gtpv1u_sgw_data.S1U_mapping, reqP->sgw_S1u_teid, (void**)&gtpv1u_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->msg.gtpv1uUpdateTunnelResp.status = 0; ///< Status (Failed = 0xFF or Success = 0x0)
} else {
GTPU_ERROR("Mapping not found\n");
message_p->msg.gtpv1uUpdateTunnelResp.status = 0xFF; ///< Status (Failed = 0xFF or Success = 0x0)
}
message_p->msg.gtpv1uUpdateTunnelResp.context_teid = reqP->context_teid;
message_p->msg.gtpv1uUpdateTunnelResp.sgw_S1u_teid = reqP->sgw_S1u_teid;
message_p->msg.gtpv1uUpdateTunnelResp.enb_S1u_teid = reqP->enb_S1u_teid;
message_p->msg.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,
NwU32T timeoutSec,
NwU32T timeoutUsec,
NwU32T 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);
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 GTPV1U_CREATE_TUNNEL_REQ: {
gtpv1u_create_s1u_tunnel(&received_message_p->msg.gtpv1uCreateTunnelReq);
}
break;
case GTPV1U_DELETE_TUNNEL_REQ: {
gtpv1u_delete_s1u_tunnel(received_message_p->msg.gtpv1uDeleteTunnelReq.context_teid, received_message_p->msg.gtpv1uDeleteTunnelReq.S1u_teid);
}
break;
case GTPV1U_UPDATE_TUNNEL_REQ: {
gtpv1u_update_s1u_tunnel(&received_message_p->msg.gtpv1uUpdateTunnelReq);
}
break;
// DATA COMING FROM UDP
case UDP_DATA_IND: {
udp_data_ind_t *udp_data_ind_p;
udp_data_ind_p = &received_message_p->msg.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);
free(udp_data_ind_p->buffer);
}
break;
// DATA TO BE SENT TO UDP
case GTPV1U_TUNNEL_DATA_REQ: {
Gtpv1uTunnelDataReq *data_req_p;
NwGtpv1uUlpApiT stack_req;
NwGtpv1uRcT rc;
hashtbl_rc_t hash_rc;
gtpv1u_teid2enb_info_t *gtpv1u_teid2enb_info;
data_req_p = &received_message_p->msg.gtpv1uTunnelDataReq;
//ipv4_send_data(ipv4_data_p->sd, data_ind_p->buffer, data_ind_p->length);
memset(&stack_req, 0, sizeof(NwGtpv1uUlpApiT));
/*
* typedef struct
{
NW_IN NwU32T teid;
NW_IN NwU32T ipAddr;
NW_IN NwU8T flags;
NW_IN NwGtpv1uMsgHandleT hMsg;
} NwGtpv1uSendtoInfoT;*/
hash_rc = hashtbl_get(gtpv1u_sgw_data.S1U_mapping, (uint64_t)data_req_p->local_S1u_teid, (void**)&gtpv1u_teid2enb_info);
if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS) {
GTPU_ERROR("nwGtpv1uProcessUlpReq failed: while getting teid %u in hashtable S1U_mapping\n", data_req_p->local_S1u_teid);
} else {
stack_req.apiType = NW_GTPV1U_ULP_API_SEND_TPDU;
stack_req.apiInfo.sendtoInfo.teid = data_req_p->local_S1u_teid;
BUFFER_TO_NwU32T(gtpv1u_teid2enb_info->enb_ip_addr.address.ipv4_address, stack_req.apiInfo.sendtoInfo.ipAddr);
/*nwGtpv1uGpduMsgNew( NW_IN NwGtpv1uStackHandleT hGtpuStackHandle,
NW_IN NwU32T teid,
NW_IN NwU8T seqNumFlag,
NW_IN NwU16T seqNum,
NW_IN NwU8T *tpdu,
NW_IN NwU16T tpduLength,
NW_OUT NwGtpv1uMsgHandleT *phMsg)*/
rc = nwGtpv1uGpduMsgNew(gtpv1u_sgw_data.gtpv1u_stack,
00,// TO DO bearer_p->port, but not needed when looking at processing
NW_FALSE,
gtpv1u_sgw_data.seq_num++,
data_req_p->buffer,
data_req_p->length,
&(stack_req.apiInfo.sendtoInfo.hMsg));
if (rc != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uGpduMsgNew failed: 0x%x\n", rc);
} else {
rc = nwGtpv1uProcessUlpReq(gtpv1u_sgw_data.gtpv1u_stack, &stack_req);
if (rc != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uProcessUlpReq failed: 0x%x\n", rc);
}
rc = nwGtpv1uMsgDelete(gtpv1u_sgw_data.gtpv1u_stack,
stack_req.apiInfo.sendtoInfo.hMsg);
if (rc != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uMsgDelete failed: 0x%x\n", rc);
}
}
}
/* Buffer is no longer needed, free it */
free(data_req_p->buffer);
}
break;
case TIMER_HAS_EXPIRED:
nwGtpv1uProcessTimeout(&received_message_p->msg.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;
}
free(received_message_p);
received_message_p = NULL;
}
return NULL;
}
int gtpv1u_init(const mme_config_t *mme_config_p)
{
int ret;
NwGtpv1uRcT rc;
NwGtpv1uUlpEntityT ulp;
NwGtpv1uUdpEntityT udp;
NwGtpv1uLogMgrEntityT log;
NwGtpv1uTimerMgrEntityT tmr;
GTPU_DEBUG("Initializing GTPV1U interface\n");
memset(&gtpv1u_sgw_data, 0, sizeof(gtpv1u_sgw_data));
gtpv1u_sgw_data.S1U_mapping = hashtbl_create (8192, NULL, NULL);
if (gtpv1u_sgw_data.S1U_mapping == NULL) {
perror("hashtbl_create");
GTPU_ERROR("Initializing TASK_GTPV1_U task interface: ERROR\n");
return -1;
}
gtpv1u_sgw_data.sgw_ip_address_for_S1u_S12_S4_up = mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up;
/* Initializing GTPv1-U stack */
if ((rc = nwGtpv1uInitialize(&gtpv1u_sgw_data.gtpv1u_stack)) != NW_GTPV1U_OK) {
GTPU_ERROR("Failed to setup nwGtpv1u stack %x\n", rc);
return -1;
}
if ((rc = nwGtpv1uSetLogLevel(gtpv1u_sgw_data.gtpv1u_stack,
NW_LOG_LEVEL_DEBG)) != NW_GTPV1U_OK) {
GTPU_ERROR("Failed to setup loglevel for stack %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_sgw_data.gtpv1u_stack, &ulp)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetUlpEntity: %x", rc);
return -1;
}
/* nw-gtpv1u stack requires an udp callback to send data over UDP.
* We provide a wrapper to UDP task.
*/
udp.udpDataReqCallback = gtpv1u_send_udp_msg;
if ((rc = nwGtpv1uSetUdpEntity(gtpv1u_sgw_data.gtpv1u_stack, &udp)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetUdpEntity: %x", rc);
return -1;
}
log.logReqCallback = gtpv1u_log_request;
if ((rc = nwGtpv1uSetLogMgrEntity(gtpv1u_sgw_data.gtpv1u_stack, &log)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetLogMgrEntity: %x", rc);
return -1;
}
/* Timer interface is more complicated as both wrappers doesn't send a message
* to the timer task but call the timer API functions start/stop timer.
*/
tmr.tmrMgrHandle = 0;
tmr.tmrStartCallback = gtpv1u_start_timer_wrapper;
tmr.tmrStopCallback = gtpv1u_stop_timer_wrapper;
if ((rc = nwGtpv1uSetTimerMgrEntity(gtpv1u_sgw_data.gtpv1u_stack, &tmr)) != NW_GTPV1U_OK) {
GTPU_ERROR("nwGtpv1uSetTimerMgrEntity: %x", rc);
return -1;
}
if (itti_create_task(TASK_GTPV1_U, &gtpv1u_thread, NULL) < 0) {
GTPU_ERROR("gtpv1u phtread_create: %s", strerror(errno));
return -1;
}
ret = gtpv1u_send_init_udp(mme_config_p->gtpv1u_config.port_number);
if (ret < 0) {
return ret;
}
GTPU_DEBUG("Initializing GTPV1U interface: DONE\n");
return ret;
}