2022-11-02 13:57:24 +00:00
/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright ( C ) 2002 , 2003 , 2004 Mondru AB .
* Copyright ( C ) 2010 - 2011 , 2016 - 2017 Harald Welte < laforge @ gnumonks . org >
* Copyright ( C ) 2015 - 2017 sysmocom - s . f . m . c . GmbH
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2 , provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software .
*
*/
/*
* gtp . c : Contains all GTP functionality . Should be able to handle multiple
* tunnels in the same program .
*
* TODO :
* - Do we need to handle fragmentation ?
*/
# ifdef __linux__
# define _GNU_SOURCE 1
# endif
# include <osmocom/core/logging.h>
# include <osmocom/core/utils.h>
2022-11-02 17:17:56 +00:00
# include <osmocom/core/stats.h>
# include <osmocom/core/rate_ctr.h>
2022-11-02 13:57:24 +00:00
# if defined(__FreeBSD__)
# include <sys/endian.h>
# endif
# include "../config.h"
# ifdef HAVE_STDINT_H
# include <stdint.h>
# endif
# include <stdio.h>
# include <stdarg.h>
# include <stdlib.h>
# include <sys/time.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/stat.h>
# include <time.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>
# include <fcntl.h>
# include <inttypes.h>
# include <arpa/inet.h>
/* #include <stdint.h> ISO C99 types */
# include "pdp.h"
# include "gtp.h"
# include "gtpie.h"
# include "queue.h"
/* Error reporting functions */
# define LOGP_WITH_ADDR(ss, level, addr, fmt, args...) \
LOGP ( ss , level , " addr(%s:%d) " fmt , \
inet_ntoa ( ( addr ) . sin_addr ) , htons ( ( addr ) . sin_port ) , \
# #args);
2022-11-02 17:17:56 +00:00
static const struct rate_ctr_desc gsn_ctr_description [ ] = {
[ GSN_CTR_ERR_SOCKET ] = { " err:socket " , " Socket error " } ,
[ GSN_CTR_ERR_READFROM ] = { " err:readfrom " , " readfrom() errors " } ,
[ GSN_CTR_ERR_SENDTO ] = { " err:sendto " , " sendto() errors " } ,
[ GSN_CTR_ERR_QUEUEFULL ] = { " err:queuefull " , " Failed to queue message because queue is full " } ,
[ GSN_CTR_ERR_SEQ ] = { " err:seq " , " Sequence number out of range " } ,
[ GSN_CTR_ERR_ADDRESS ] = { " err:address " , " GSN address conversion failed " } ,
[ GSN_CTR_ERR_UNKNOWN_PDP ] = { " err:unknown_pdp " , " Failed looking up PDP context " } ,
[ GSN_CTR_ERR_UNEXPECTED_CAUSE ] = { " err:unexpected_cause " , " Unexpected cause value received " } ,
[ GSN_CTR_ERR_OUT_OF_PDP ] = { " err:out_of_pdp " , " Out of storage for PDP contexts " } ,
[ GSN_CTR_PKT_EMPTY ] = { " pkt:empty " , " Empty packet received " } ,
[ GSN_CTR_PKT_UNSUP ] = { " pkt:unsupported " , " Unsupported GTP version received " } ,
[ GSN_CTR_PKT_TOOSHORT ] = { " pkt:too_short " , " Packet too short received " } ,
[ GSN_CTR_PKT_UNKNOWN ] = { " pkt:unknown " , " Unknown packet type received " } ,
[ GSN_CTR_PKT_UNEXPECT ] = { " pkt:unexpected " , " Unexpected packet type received " } ,
[ GSN_CTR_PKT_DUPLICATE ] = { " pkt:duplicate " , " Duplicate or unsolicited packet received " } ,
[ GSN_CTR_PKT_MISSING ] = { " pkt:missing " , " Missing IE in packet received " } ,
[ GSN_CTR_PKT_INCORRECT ] = { " pkt:incorrect " , " Incorrect IE in packet received " } ,
[ GSN_CTR_PKT_INVALID ] = { " pkt:invalid " , " Invalid format in packet received " } ,
} ;
static const struct rate_ctr_group_desc gsn_ctrg_desc = {
" gsn " ,
" GSN Statistics " ,
OSMO_STATS_CLASS_PEER ,
ARRAY_SIZE ( gsn_ctr_description ) ,
gsn_ctr_description ,
} ;
static unsigned int gsn_ctr_next_idx = 0 ;
2022-11-02 18:52:19 +00:00
/* Global timer definitions for GTP operation, provided for convenience. To make these user configurable, it is convenient to add
* gtp_gsn_tdefs as one of your program ' s osmo_tdef_group entries and call osmo_tdef_vty_init ( ) . */
struct osmo_tdef gtp_T_defs [ ] = {
{ . T = GTP_GSN_TIMER_T3_RESPONSE , . default_val = 5 , . unit = OSMO_TDEF_S ,
. desc = " Timer T3-RESPONSE holds the maximum wait time for a response of a request message "
} ,
{ . T = GTP_GSN_TIMER_N3_REQUESTS , . default_val = 3 , . unit = OSMO_TDEF_CUSTOM ,
. desc = " Counter N3-REQUESTS holds the maximum number of attempts made by GTP to send a request message "
} ,
2022-11-04 10:21:23 +00:00
{ . T = GTP_GSN_TIMER_T3_HOLD_RESPONSE , . default_val = 5 * 3 /* (GTP_GSN_TIMER_T3_RESPONSE * GTP_GSN_TIMER_N3_REQUESTS) */ , . unit = OSMO_TDEF_S ,
. desc = " Time a GTP respoonse message is kept cached to re-transmit it when a duplicate request is received. Value is generally equal to (T3-RESPONSE * N3-REQUESTS) set at the peer "
} ,
2022-11-02 18:52:19 +00:00
{ }
} ;
2022-11-02 17:17:56 +00:00
2022-11-02 13:57:24 +00:00
/* API Functions */
/* Deprecated, use gtp_pdp_newpdp() instead */
int gtp_newpdp ( struct gsn_t * gsn , struct pdp_t * * pdp ,
uint64_t imsi , uint8_t nsapi )
{
int rc ;
rc = gtp_pdp_newpdp ( gsn , pdp , imsi , nsapi , NULL ) ;
return rc ;
}
int gtp_freepdp ( struct gsn_t * gsn , struct pdp_t * pdp )
{
if ( gsn - > cb_delete_context )
gsn - > cb_delete_context ( pdp ) ;
return pdp_freepdp ( pdp ) ;
}
/* Free pdp and all its secondary PDP contexts. Must be called on the primary PDP context. */
int gtp_freepdp_teardown ( struct gsn_t * gsn , struct pdp_t * pdp )
{
int n ;
struct pdp_t * secondary_pdp ;
OSMO_ASSERT ( ! pdp - > secondary ) ;
for ( n = 0 ; n < PDP_MAXNSAPI ; n + + ) {
if ( pdp - > secondary_tei [ n ] ) {
if ( gtp_pdp_getgtp1 ( gsn , & secondary_pdp ,
pdp - > secondary_tei [ n ] ) ) {
LOGP ( DLGTP , LOGL_ERROR ,
" Unknown secondary PDP context \n " ) ;
continue ;
}
if ( pdp ! = secondary_pdp ) {
gtp_freepdp ( gsn , secondary_pdp ) ;
}
}
}
return gtp_freepdp ( gsn , pdp ) ;
}
/* gtp_gpdu */
extern int gtp_fd ( struct gsn_t * gsn )
{
return gsn - > fd0 ;
}
int gtp_set_cb_unsup_ind ( struct gsn_t * gsn ,
int ( * cb ) ( struct sockaddr_in * peer ) )
{
gsn - > cb_unsup_ind = cb ;
return 0 ;
}
int gtp_set_cb_extheader_ind ( struct gsn_t * gsn ,
int ( * cb ) ( struct sockaddr_in * peer ) )
{
gsn - > cb_extheader_ind = cb ;
return 0 ;
}
int gtp_set_cb_ran_info_relay_ind ( struct gsn_t * gsn ,
int ( * cb ) ( struct sockaddr_in * peer , union gtpie_member * * ie ) )
{
gsn - > cb_ran_info_relay_ind = cb ;
return 0 ;
}
/* API: Initialise delete context callback */
/* Called whenever a pdp context is deleted for any reason */
int gtp_set_cb_delete_context ( struct gsn_t * gsn , int ( * cb ) ( struct pdp_t * pdp ) )
{
gsn - > cb_delete_context = cb ;
return 0 ;
}
int gtp_set_cb_conf ( struct gsn_t * gsn ,
int ( * cb ) ( int type , int cause ,
struct pdp_t * pdp , void * cbp ) )
{
gsn - > cb_conf = cb ;
return 0 ;
}
int gtp_set_cb_recovery ( struct gsn_t * gsn ,
int ( * cb ) ( struct sockaddr_in * peer , uint8_t recovery ) )
{
gsn - > cb_recovery = cb ;
return 0 ;
}
/* cb_recovery()
* pdp may be NULL if Recovery IE was received from a message independent
* of any PDP ctx ( such as Echo Response ) , or because pdp ctx is unknown to the
* local setup . In case pdp is known , caller may want to keep that pdp alive to
* handle subsequent msg cb as this specific pdp ctx is still valid according to
* specs .
*/
int gtp_set_cb_recovery2 ( struct gsn_t * gsn ,
int ( * cb_recovery2 ) ( struct sockaddr_in * peer , struct pdp_t * pdp , uint8_t recovery ) )
{
gsn - > cb_recovery2 = cb_recovery2 ;
return 0 ;
}
/* cb_recovery()
* pdp may be NULL if Recovery IE was received from a message independent
* of any PDP ctx ( such as Echo Response ) , or because pdp ctx is unknown to the
* local setup . In case pdp is known , caller may want to keep that pdp alive to
* handle subsequent msg cb as this specific pdp ctx is still valid according to
* specs .
*/
int gtp_set_cb_recovery3 ( struct gsn_t * gsn ,
int ( * cb_recovery3 ) ( struct gsn_t * gsn , struct sockaddr_in * peer ,
struct pdp_t * pdp , uint8_t recovery ) )
{
gsn - > cb_recovery3 = cb_recovery3 ;
return 0 ;
}
int gtp_set_cb_data_ind ( struct gsn_t * gsn ,
int ( * cb_data_ind ) ( struct pdp_t * pdp ,
void * pack , unsigned len ) )
{
gsn - > cb_data_ind = cb_data_ind ;
return 0 ;
}
static int queue_timer_retrans ( struct gsn_t * gsn )
{
/* Retransmit any outstanding packets */
/* Remove from queue if maxretrans exceeded */
time_t now ;
struct qmsg_t * qmsg ;
2022-11-02 18:52:19 +00:00
unsigned int t3_response , n3_requests ;
2022-11-02 13:57:24 +00:00
now = time ( NULL ) ;
2022-11-02 18:52:19 +00:00
t3_response = osmo_tdef_get ( gsn - > tdef , GTP_GSN_TIMER_T3_RESPONSE , OSMO_TDEF_S , - 1 ) ;
n3_requests = osmo_tdef_get ( gsn - > tdef , GTP_GSN_TIMER_N3_REQUESTS , OSMO_TDEF_CUSTOM , - 1 ) ;
2022-11-02 13:57:24 +00:00
/* get first element in queue, as long as the timeout of that
* element has expired */
while ( ( ! queue_getfirst ( gsn - > queue_req , & qmsg ) ) & &
( qmsg - > timeout < = now ) ) {
2022-11-02 18:52:19 +00:00
if ( qmsg - > retrans > n3_requests ) { /* Too many retrans */
2022-11-02 13:57:24 +00:00
LOGP ( DLGTP , LOGL_NOTICE , " Retransmit req queue timeout of seq % " PRIu16 " \n " ,
qmsg - > seq ) ;
if ( gsn - > cb_conf )
gsn - > cb_conf ( qmsg - > type , EOF , NULL , qmsg - > cbp ) ;
queue_freemsg ( gsn - > queue_req , qmsg ) ;
} else {
LOGP ( DLGTP , LOGL_INFO , " Retransmit (%d) of seq % " PRIu16 " \n " ,
qmsg - > retrans , qmsg - > seq ) ;
if ( sendto ( qmsg - > fd , & qmsg - > p , qmsg - > l , 0 ,
( struct sockaddr * ) & qmsg - > peer ,
sizeof ( struct sockaddr_in ) ) < 0 ) {
2022-11-02 17:17:56 +00:00
rate_ctr_inc2 ( gsn - > ctrg , GSN_CTR_ERR_SENDTO ) ;
2022-11-02 13:57:24 +00:00
LOGP ( DLGTP , LOGL_ERROR ,
" Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s \n " ,
gsn - > fd0 , ( unsigned long ) & qmsg - > p ,
qmsg - > l , strerror ( errno ) ) ;
}
queue_back ( gsn - > queue_req , qmsg ) ;
2022-11-02 18:52:19 +00:00
qmsg - > timeout = now + t3_response ;
2022-11-02 13:57:24 +00:00
qmsg - > retrans + + ;
}
}
/* Also clean up reply timeouts */
while ( ( ! queue_getfirst ( gsn - > queue_resp , & qmsg ) ) & &
( qmsg - > timeout < now ) ) {
LOGP ( DLGTP , LOGL_DEBUG , " Retransmit resp queue seq % "
PRIu16 " expired, removing from queue \n " , qmsg - > seq ) ;
queue_freemsg ( gsn - > queue_resp , qmsg ) ;
}
return 0 ;
}
static int queue_timer_retranstimeout ( struct gsn_t * gsn , struct timeval * timeout )
{
time_t now , later , diff ;
struct qmsg_t * qmsg ;
timeout - > tv_usec = 0 ;
if ( queue_getfirst ( gsn - > queue_req , & qmsg ) ) {
timeout - > tv_sec = 10 ;
} else {
now = time ( NULL ) ;
later = qmsg - > timeout ;
timeout - > tv_sec = later - now ;
if ( timeout - > tv_sec < 0 )
timeout - > tv_sec = 0 ; /* No negative allowed */
if ( timeout - > tv_sec > 10 )
timeout - > tv_sec = 10 ; /* Max sleep for 10 sec */
}
if ( queue_getfirst ( gsn - > queue_resp , & qmsg ) ) {
/* already set by queue_req, do nothing */
} else { /* trigger faster if earlier timeout exists in queue_resp */
now = time ( NULL ) ;
later = qmsg - > timeout ;
diff = later - now ;
if ( diff < 0 )
diff = 0 ;
if ( diff < timeout - > tv_sec )
timeout - > tv_sec = diff ;
}
return 0 ;
}
void gtp_queue_timer_start ( struct gsn_t * gsn )
{
struct timeval next ;
/* Retrieve next retransmission as timeval */
queue_timer_retranstimeout ( gsn , & next ) ;
/* re-schedule the timer */
osmo_timer_schedule ( & gsn - > queue_timer , next . tv_sec , next . tv_usec / 1000 ) ;
}
/* timer callback for libgtp retransmission and ping */
static void queue_timer_cb ( void * data )
{
struct gsn_t * gsn = data ;
/* do all the retransmissions as needed */
queue_timer_retrans ( gsn ) ;
gtp_queue_timer_start ( gsn ) ;
}
/**
* @ brief clear the request and response queue . Useful for debugging to reset " some " state .
* @ param gsn The GGSN instance
*/
void gtp_clear_queues ( struct gsn_t * gsn )
{
struct qmsg_t * qmsg ;
LOGP ( DLGTP , LOGL_INFO , " Clearing req & resp retransmit queues \n " ) ;
while ( ! queue_getfirst ( gsn - > queue_req , & qmsg ) ) {
queue_freemsg ( gsn - > queue_req , qmsg ) ;
}
while ( ! queue_getfirst ( gsn - > queue_resp , & qmsg ) ) {
queue_freemsg ( gsn - > queue_resp , qmsg ) ;
}
}
/* Perform restoration and recovery error handling as described in 29.060 */
static void log_restart ( struct gsn_t * gsn )
{
FILE * f ;
int i , rc ;
int counter = 0 ;
char * filename ;
filename = talloc_asprintf ( NULL , " %s/%s " , gsn - > statedir , RESTART_FILE ) ;
OSMO_ASSERT ( filename ) ;
/* We try to open file. On failure we will later try to create file */
if ( ! ( f = fopen ( filename , " r " ) ) ) {
LOGP ( DLGTP , LOGL_NOTICE ,
" State information file (%s) not found. Creating new file. \n " ,
filename ) ;
} else {
rc = fscanf ( f , " %d " , & counter ) ;
if ( rc ! = 1 ) {
LOGP ( DLGTP , LOGL_ERROR ,
" fscanf failed to read counter value \n " ) ;
goto close_file ;
}
if ( fclose ( f ) ) {
LOGP ( DLGTP , LOGL_ERROR ,
" fclose failed: Error = %s \n " , strerror ( errno ) ) ;
}
}
gsn - > restart_counter = ( unsigned char ) counter ;
gsn - > restart_counter + + ;
/* Keep the umask closely wrapped around our fopen() call in case the
* log outputs cause file creation . */
i = umask ( 022 ) ;
f = fopen ( filename , " w " ) ;
umask ( i ) ;
if ( ! f ) {
LOGP ( DLGTP , LOGL_ERROR ,
" fopen(path=%s, mode=%s) failed: Error = %s \n " , filename ,
" w " , strerror ( errno ) ) ;
goto free_filename ;
}
fprintf ( f , " %d \n " , gsn - > restart_counter ) ;
close_file :
if ( fclose ( f ) )
LOGP ( DLGTP , LOGL_ERROR ,
" fclose failed: Error = %s \n " , strerror ( errno ) ) ;
free_filename :
talloc_free ( filename ) ;
}
int gtp_new ( struct gsn_t * * gsn , char * statedir , struct in_addr * listen ,
int mode )
{
struct sockaddr_in addr ;
LOGP ( DLGTP , LOGL_NOTICE , " GTP: gtp_newgsn() started at %s \n " , inet_ntoa ( * listen ) ) ;
* gsn = calloc ( sizeof ( struct gsn_t ) , 1 ) ; /* TODO */
( * gsn ) - > statedir = statedir ;
log_restart ( * gsn ) ;
/* Initialise sequence number */
( * gsn ) - > seq_next = ( * gsn ) - > restart_counter * 1024 ;
2022-11-02 18:52:19 +00:00
/* Initialize timers: */
( * gsn ) - > tdef = gtp_T_defs ;
/* Small hack to properly reset tdef for old clients not using the tdef_group: */
osmo_static_assert ( gtp_T_defs [ 0 ] . default_val ! = 0 , first_default_val_not_zero ) ;
if ( gtp_T_defs [ 0 ] . val = = 0 )
osmo_tdefs_reset ( ( * gsn ) - > tdef ) ;
2022-11-02 13:57:24 +00:00
/* Initialise request retransmit queue */
queue_new ( & ( * gsn ) - > queue_req ) ;
queue_new ( & ( * gsn ) - > queue_resp ) ;
/* Initialise pdp table */
pdp_init ( * gsn ) ;
/* Initialize internal queue timer */
osmo_timer_setup ( & ( * gsn ) - > queue_timer , queue_timer_cb , * gsn ) ;
2022-11-02 17:17:56 +00:00
/* Initialize counter group: */
( * gsn ) - > ctrg = rate_ctr_group_alloc ( NULL , & gsn_ctrg_desc , gsn_ctr_next_idx + + ) ;
2022-11-02 13:57:24 +00:00
/* Initialise call back functions */
( * gsn ) - > cb_create_context_ind = 0 ;
( * gsn ) - > cb_delete_context = 0 ;
( * gsn ) - > cb_unsup_ind = 0 ;
( * gsn ) - > cb_conf = 0 ;
( * gsn ) - > cb_data_ind = 0 ;
/* Store function parameters */
( * gsn ) - > gsnc = * listen ;
( * gsn ) - > gsnu = * listen ;
( * gsn ) - > mode = mode ;
/* Create GTP version 0 socket */
if ( ( ( * gsn ) - > fd0 = socket ( AF_INET , SOCK_DGRAM , 0 ) ) < 0 ) {
2022-11-02 17:17:56 +00:00
rate_ctr_inc2 ( ( * gsn ) - > ctrg , GSN_CTR_ERR_SOCKET ) ;
2022-11-02 13:57:24 +00:00
LOGP ( DLGTP , LOGL_ERROR ,
" GTPv0 socket(domain=%d, type=%d, protocol=%d) failed: Error = %s \n " ,
AF_INET , SOCK_DGRAM , 0 , strerror ( errno ) ) ;
return - errno ;
}
memset ( & addr , 0 , sizeof ( addr ) ) ;
addr . sin_family = AF_INET ;
addr . sin_addr = * listen ; /* Same IP for user traffic and signalling */
addr . sin_port = htons ( GTP0_PORT ) ;
# if defined(__FreeBSD__) || defined(__APPLE__)
addr . sin_len = sizeof ( addr ) ;
# endif
if ( bind ( ( * gsn ) - > fd0 , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
2022-11-02 17:17:56 +00:00
rate_ctr_inc2 ( ( * gsn ) - > ctrg , GSN_CTR_ERR_SOCKET ) ;
2022-11-02 13:57:24 +00:00
LOGP_WITH_ADDR ( DLGTP , LOGL_ERROR , addr ,
" bind(fd0=%d) failed: Error = %s \n " ,
( * gsn ) - > fd0 , strerror ( errno ) ) ;
return - errno ;
}
/* Create GTP version 1 control plane socket */
if ( ( ( * gsn ) - > fd1c = socket ( AF_INET , SOCK_DGRAM , 0 ) ) < 0 ) {
2022-11-02 17:17:56 +00:00
rate_ctr_inc2 ( ( * gsn ) - > ctrg , GSN_CTR_ERR_SOCKET ) ;
2022-11-02 13:57:24 +00:00
LOGP ( DLGTP , LOGL_ERROR ,
" GTPv1 control plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s \n " ,
AF_INET , SOCK_DGRAM , 0 , strerror ( errno ) ) ;
return - errno ;
}
memset ( & addr , 0 , sizeof ( addr ) ) ;
addr . sin_family = AF_INET ;
addr . sin_addr = * listen ; /* Same IP for user traffic and signalling */
addr . sin_port = htons ( GTP1C_PORT ) ;
# if defined(__FreeBSD__) || defined(__APPLE__)
addr . sin_len = sizeof ( addr ) ;
# endif
if ( bind ( ( * gsn ) - > fd1c , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
2022-11-02 17:17:56 +00:00
rate_ctr_inc2 ( ( * gsn ) - > ctrg , GSN_CTR_ERR_SOCKET ) ;
2022-11-02 13:57:24 +00:00
LOGP_WITH_ADDR ( DLGTP , LOGL_ERROR , addr ,
" bind(fd1c=%d) failed: Error = %s \n " ,
( * gsn ) - > fd1c , strerror ( errno ) ) ;
return - errno ;
}
/* Create GTP version 1 user plane socket */
if ( ( ( * gsn ) - > fd1u = socket ( AF_INET , SOCK_DGRAM , 0 ) ) < 0 ) {
2022-11-02 17:17:56 +00:00
rate_ctr_inc2 ( ( * gsn ) - > ctrg , GSN_CTR_ERR_SOCKET ) ;
2022-11-02 13:57:24 +00:00
LOGP ( DLGTP , LOGL_ERROR ,
" GTPv1 user plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s \n " ,
AF_INET , SOCK_DGRAM , 0 , strerror ( errno ) ) ;
return - errno ;
}
memset ( & addr , 0 , sizeof ( addr ) ) ;
addr . sin_family = AF_INET ;
addr . sin_addr = * listen ; /* Same IP for user traffic and signalling */
addr . sin_port = htons ( GTP1U_PORT ) ;
# if defined(__FreeBSD__) || defined(__APPLE__)
addr . sin_len = sizeof ( addr ) ;
# endif
if ( bind ( ( * gsn ) - > fd1u , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
2022-11-02 17:17:56 +00:00
rate_ctr_inc2 ( ( * gsn ) - > ctrg , GSN_CTR_ERR_SOCKET ) ;
2022-11-02 13:57:24 +00:00
LOGP_WITH_ADDR ( DLGTP , LOGL_ERROR , addr ,
" bind(fd1u=%d) failed: Error = %s \n " ,
( * gsn ) - > fd1u , strerror ( errno ) ) ;
return - errno ;
}
/* Start internal queue timer */
gtp_queue_timer_start ( * gsn ) ;
return 0 ;
}
int gtp_free ( struct gsn_t * gsn )
{
/* Cleanup internal queue timer */
osmo_timer_del ( & gsn - > queue_timer ) ;
/* Clean up retransmit queues */
queue_free ( gsn - > queue_req ) ;
queue_free ( gsn - > queue_resp ) ;
close ( gsn - > fd0 ) ;
close ( gsn - > fd1c ) ;
close ( gsn - > fd1u ) ;
2022-11-02 17:17:56 +00:00
rate_ctr_group_free ( gsn - > ctrg ) ;
2022-11-02 13:57:24 +00:00
free ( gsn ) ;
return 0 ;
}
/* API: Register create context indication callback */
int gtp_set_cb_create_context_ind ( struct gsn_t * gsn ,
int ( * cb_create_context_ind ) ( struct pdp_t *
pdp ) )
{
gsn - > cb_create_context_ind = cb_create_context_ind ;
return 0 ;
}
int gtp_retrans ( struct gsn_t * gsn )
{
/* dummy API, deprecated. */
return 0 ;
}
int gtp_retranstimeout ( struct gsn_t * gsn , struct timeval * timeout )
{
timeout - > tv_sec = 24 * 60 * 60 ;
timeout - > tv_usec = 0 ;
/* dummy API, deprecated. Return a huge timer to do nothing */
return 0 ;
}