Split gsn_t related APIs out of gtp.{c,h}
This way we split the gsn_t object API/logic from the protocol (message handling) code. Change-Id: I47cebb51bf08b9fcf7f115fc8dbea5f3493d4388
This commit is contained in:
parent
0d3bd3435f
commit
724ecc6680
|
@ -6,10 +6,10 @@ LIBVERSION=8:1:2
|
|||
|
||||
lib_LTLIBRARIES = libgtp.la
|
||||
|
||||
include_HEADERS = gtp.h pdp.h gtpie.h
|
||||
include_HEADERS = gtp.h gsn.h pdp.h gtpie.h
|
||||
|
||||
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
||||
|
||||
libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
|
||||
libgtp_la_SOURCES = gtp.c gtp.h gsn.c gsn.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
|
||||
libgtp_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
|
||||
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)
|
||||
|
|
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
#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"
|
||||
|
||||
/* According to section 14.2 of 3GPP TS 29.006 version 6.9.0 */
|
||||
#define N3_REQUESTS 5
|
||||
|
||||
#define T3_REQUEST 3
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
now = time(NULL);
|
||||
|
||||
/* 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)) {
|
||||
if (qmsg->retrans > N3_REQUESTS) { /* Too many retrans */
|
||||
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) {
|
||||
gsn->err_sendto++;
|
||||
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);
|
||||
qmsg->timeout = now + T3_REQUEST;
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GSN_H
|
||||
#define _GSN_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "pdp.h"
|
||||
|
||||
#define GTP_MODE_GGSN 1
|
||||
#define GTP_MODE_SGSN 2
|
||||
|
||||
#define RESTART_FILE "gsn_restart"
|
||||
|
||||
/* ***********************************************************
|
||||
* Information storage for each gsn instance
|
||||
*
|
||||
* Normally each instance of the application corresponds to
|
||||
* one instance of a gsn.
|
||||
*
|
||||
* In order to avoid global variables in the application, and
|
||||
* also in order to allow several instances of a gsn in the same
|
||||
* application this struct is provided in order to store all
|
||||
* relevant information related to the gsn.
|
||||
*
|
||||
* Note that this does not include information storage for '
|
||||
* each pdp context. This is stored in another struct.
|
||||
*************************************************************/
|
||||
|
||||
struct gsn_t {
|
||||
/* Parameters related to the network interface */
|
||||
|
||||
int fd0; /* GTP0 file descriptor */
|
||||
int fd1c; /* GTP1 control plane file descriptor */
|
||||
int fd1u; /* GTP0 user plane file descriptor */
|
||||
int mode; /* Mode of operation: GGSN or SGSN */
|
||||
struct in_addr gsnc; /* IP address of this gsn for signalling */
|
||||
struct in_addr gsnu; /* IP address of this gsn for user traffic */
|
||||
|
||||
/* Parameters related to signalling messages */
|
||||
uint16_t seq_next; /* Next sequence number to use */
|
||||
int seq_first; /* First packet in queue (oldest timeout) */
|
||||
int seq_last; /* Last packet in queue (youngest timeout) */
|
||||
|
||||
unsigned char restart_counter; /* Increment on restart. Stored on disk */
|
||||
char *statedir; /* Disk location for permanent storage */
|
||||
void *priv; /* used by libgtp users to attach their own state) */
|
||||
struct queue_t *queue_req; /* Request queue */
|
||||
struct queue_t *queue_resp; /* Response queue */
|
||||
|
||||
struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
|
||||
struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
|
||||
struct osmo_timer_list queue_timer; /* internal queue_{req,resp} timer */
|
||||
|
||||
/* Call back functions */
|
||||
int (*cb_delete_context) (struct pdp_t *);
|
||||
int (*cb_create_context_ind) (struct pdp_t *);
|
||||
int (*cb_unsup_ind) (struct sockaddr_in * peer);
|
||||
int (*cb_extheader_ind) (struct sockaddr_in * peer);
|
||||
int (*cb_ran_info_relay_ind) (struct sockaddr_in *peer, union gtpie_member **ie);
|
||||
int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
|
||||
int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
|
||||
int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
|
||||
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
|
||||
int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
|
||||
|
||||
/* Counters */
|
||||
|
||||
uint64_t err_socket; /* Number of socket errors */
|
||||
uint64_t err_readfrom; /* Number of readfrom errors */
|
||||
uint64_t err_sendto; /* Number of sendto errors */
|
||||
uint64_t err_memcpy; /* Number of memcpy */
|
||||
uint64_t err_queuefull; /* Number of times queue was full */
|
||||
uint64_t err_seq; /* Number of seq out of range */
|
||||
uint64_t err_address; /* GSN address conversion failed */
|
||||
uint64_t err_unknownpdp; /* GSN address conversion failed */
|
||||
uint64_t err_unknowntid; /* Application supplied unknown imsi+nsapi */
|
||||
uint64_t err_cause; /* Unexpected cause value received */
|
||||
uint64_t err_outofpdp; /* Out of storage for PDP contexts */
|
||||
|
||||
uint64_t empty; /* Number of empty packets */
|
||||
uint64_t unsup; /* Number of unsupported version 29.60 11.1.1 */
|
||||
uint64_t tooshort; /* Number of too short headers 29.60 11.1.2 */
|
||||
uint64_t unknown; /* Number of unknown messages 29.60 11.1.3 */
|
||||
uint64_t unexpect; /* Number of unexpected messages 29.60 11.1.4 */
|
||||
uint64_t duplicate; /* Number of duplicate or unsolicited replies */
|
||||
uint64_t missing; /* Number of missing information field messages */
|
||||
uint64_t incorrect; /* Number of incorrect information field messages */
|
||||
uint64_t invalid; /* Number of invalid message format messages */
|
||||
};
|
||||
|
||||
/* External API functions */
|
||||
|
||||
extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode);
|
||||
|
||||
extern int gtp_free(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
|
||||
extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp);
|
||||
|
||||
extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind) (struct
|
||||
pdp_t *
|
||||
pdp));
|
||||
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind) (struct pdp_t * pdp,
|
||||
void *pack, unsigned len));
|
||||
extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
|
||||
int (*cb_delete_context) (struct pdp_t *
|
||||
pdp));
|
||||
/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
|
||||
int (*cb_create_context) (struct pdp_t* pdp)); */
|
||||
|
||||
extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie));
|
||||
|
||||
extern int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
int (*cb) (int type, int cause, struct pdp_t * pdp,
|
||||
void *cbp));
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery3() instead, to obtain gsn handling the recovery");
|
||||
int gtp_set_cb_recovery3(struct gsn_t *gsn,
|
||||
int (*cb) (struct gsn_t * gsn, struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery));
|
||||
void gtp_clear_queues(struct gsn_t *gsn);
|
||||
extern int gtp_fd(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_retrans(struct gsn_t *gsn) OSMO_DEPRECATED("This API is a no-op, libgtp already does the job internally");
|
||||
extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) OSMO_DEPRECATED("This API is a no-op and will return a 1 day timeout");
|
||||
|
||||
/* Internal APIs: */
|
||||
void gtp_queue_timer_start(struct gsn_t *gsn);
|
||||
|
||||
#endif /* !_GSN_H */
|
467
gtp/gtp.c
467
gtp/gtp.c
|
@ -131,93 +131,7 @@ const struct value_string gtp_type_names[] = {
|
|||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* gtp_decaps */
|
||||
/* gtp_retrans */
|
||||
/* gtp_retranstimeout */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void emit_cb_recovery(struct gsn_t *gsn, struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp, uint8_t recovery)
|
||||
|
@ -230,50 +144,6 @@ static void emit_cb_recovery(struct gsn_t *gsn, struct sockaddr_in * peer,
|
|||
gsn->cb_recovery3(gsn, peer, pdp, recovery);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_default_gtp()
|
||||
* Generate a GPRS Tunneling Protocol signalling packet header, depending
|
||||
|
@ -393,108 +263,6 @@ static uint32_t get_tei(void *pack)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
now = time(NULL);
|
||||
|
||||
/* 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)) {
|
||||
if (qmsg->retrans > N3_REQUESTS) { /* Too many retrans */
|
||||
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) {
|
||||
gsn->err_sendto++;
|
||||
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);
|
||||
qmsg->timeout = now + T3_REQUEST;
|
||||
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;
|
||||
}
|
||||
|
||||
static void 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);
|
||||
|
||||
queue_timer_start(gsn);
|
||||
}
|
||||
|
||||
/* ***********************************************************
|
||||
* Reliable delivery of signalling messages
|
||||
*
|
||||
|
@ -646,31 +414,12 @@ static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
|
|||
|
||||
/* Rearm timer: Retrans time for qmsg just queued may be required
|
||||
before an existing one (for instance a gtp echo req) */
|
||||
queue_timer_start(gsn);
|
||||
gtp_queue_timer_start(gsn);
|
||||
}
|
||||
gsn->seq_next++; /* Count up this time */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/* gtp_conf
|
||||
* Remove signalling packet from retransmission queue.
|
||||
* return 0 on success, EOF if packet was not found */
|
||||
|
@ -705,20 +454,6 @@ static int gtp_conf(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer
|
|||
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;
|
||||
}
|
||||
|
||||
static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
union gtp_packet *packet, int len,
|
||||
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
|
||||
|
@ -782,7 +517,7 @@ static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
|
|||
|
||||
/* Rearm timer: Retrans time for qmsg just queued may be required
|
||||
before an existing one (for instance a gtp echo req) */
|
||||
queue_timer_start(gsn);
|
||||
gtp_queue_timer_start(gsn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -864,195 +599,6 @@ static int gtp_duplicate(struct gsn_t *gsn, uint8_t version,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
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) {
|
||||
(*gsn)->err_socket++;
|
||||
LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
|
||||
"bind(fd1u=%d) failed: Error = %s\n",
|
||||
(*gsn)->fd1u, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Start internal queue timer */
|
||||
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);
|
||||
|
||||
free(gsn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ***********************************************************
|
||||
* Path management messages
|
||||
* Messages: echo and version not supported.
|
||||
|
@ -1455,15 +1001,6 @@ int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp, int cause)
|
|||
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;
|
||||
}
|
||||
|
||||
/* Send Create PDP Context Response */
|
||||
int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
|
||||
uint8_t cause)
|
||||
|
|
147
gtp/gtp.h
147
gtp/gtp.h
|
@ -13,14 +13,10 @@
|
|||
#define _GTP_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "gtpie.h"
|
||||
#include "pdp.h"
|
||||
|
||||
#define GTP_MODE_GGSN 1
|
||||
#define GTP_MODE_SGSN 2
|
||||
#include "gsn.h"
|
||||
|
||||
#define GTP0_PORT 3386
|
||||
#define GTP1C_PORT 2123
|
||||
|
@ -32,12 +28,10 @@
|
|||
#define GTP1_HEADER_SIZE_SHORT 8
|
||||
#define GTP1_HEADER_SIZE_LONG 12
|
||||
|
||||
#define NAMESIZE 1024
|
||||
#define SYSLOG_PRINTSIZE 255
|
||||
#define ERRMSG_SIZE 255
|
||||
|
||||
#define RESTART_FILE "gsn_restart"
|
||||
#define NAMESIZE 1024
|
||||
|
||||
/* GTP version 1 extension header type definitions. */
|
||||
#define GTP_EXT_PDCP_PDU 0xC0 /* PDCP PDU Number */
|
||||
|
||||
|
@ -232,105 +226,13 @@ union gtp_packet {
|
|||
struct gtp1_packet_long gtp1l;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ***********************************************************
|
||||
* Information storage for each gsn instance
|
||||
*
|
||||
* Normally each instance of the application corresponds to
|
||||
* one instance of a gsn.
|
||||
*
|
||||
* In order to avoid global variables in the application, and
|
||||
* also in order to allow several instances of a gsn in the same
|
||||
* application this struct is provided in order to store all
|
||||
* relevant information related to the gsn.
|
||||
*
|
||||
* Note that this does not include information storage for '
|
||||
* each pdp context. This is stored in another struct.
|
||||
*************************************************************/
|
||||
|
||||
struct gsn_t {
|
||||
/* Parameters related to the network interface */
|
||||
|
||||
int fd0; /* GTP0 file descriptor */
|
||||
int fd1c; /* GTP1 control plane file descriptor */
|
||||
int fd1u; /* GTP0 user plane file descriptor */
|
||||
int mode; /* Mode of operation: GGSN or SGSN */
|
||||
struct in_addr gsnc; /* IP address of this gsn for signalling */
|
||||
struct in_addr gsnu; /* IP address of this gsn for user traffic */
|
||||
|
||||
/* Parameters related to signalling messages */
|
||||
uint16_t seq_next; /* Next sequence number to use */
|
||||
int seq_first; /* First packet in queue (oldest timeout) */
|
||||
int seq_last; /* Last packet in queue (youngest timeout) */
|
||||
|
||||
unsigned char restart_counter; /* Increment on restart. Stored on disk */
|
||||
char *statedir; /* Disk location for permanent storage */
|
||||
void *priv; /* used by libgtp users to attach their own state) */
|
||||
struct queue_t *queue_req; /* Request queue */
|
||||
struct queue_t *queue_resp; /* Response queue */
|
||||
|
||||
struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
|
||||
struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
|
||||
struct osmo_timer_list queue_timer; /* internal queue_{req,resp} timer */
|
||||
|
||||
/* Call back functions */
|
||||
int (*cb_delete_context) (struct pdp_t *);
|
||||
int (*cb_create_context_ind) (struct pdp_t *);
|
||||
int (*cb_unsup_ind) (struct sockaddr_in * peer);
|
||||
int (*cb_extheader_ind) (struct sockaddr_in * peer);
|
||||
int (*cb_ran_info_relay_ind) (struct sockaddr_in *peer, union gtpie_member **ie);
|
||||
int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
|
||||
int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
|
||||
int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
|
||||
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
|
||||
int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
|
||||
|
||||
/* Counters */
|
||||
|
||||
uint64_t err_socket; /* Number of socket errors */
|
||||
uint64_t err_readfrom; /* Number of readfrom errors */
|
||||
uint64_t err_sendto; /* Number of sendto errors */
|
||||
uint64_t err_memcpy; /* Number of memcpy */
|
||||
uint64_t err_queuefull; /* Number of times queue was full */
|
||||
uint64_t err_seq; /* Number of seq out of range */
|
||||
uint64_t err_address; /* GSN address conversion failed */
|
||||
uint64_t err_unknownpdp; /* GSN address conversion failed */
|
||||
uint64_t err_unknowntid; /* Application supplied unknown imsi+nsapi */
|
||||
uint64_t err_cause; /* Unexpected cause value received */
|
||||
uint64_t err_outofpdp; /* Out of storage for PDP contexts */
|
||||
|
||||
uint64_t empty; /* Number of empty packets */
|
||||
uint64_t unsup; /* Number of unsupported version 29.60 11.1.1 */
|
||||
uint64_t tooshort; /* Number of too short headers 29.60 11.1.2 */
|
||||
uint64_t unknown; /* Number of unknown messages 29.60 11.1.3 */
|
||||
uint64_t unexpect; /* Number of unexpected messages 29.60 11.1.4 */
|
||||
uint64_t duplicate; /* Number of duplicate or unsolicited replies */
|
||||
uint64_t missing; /* Number of missing information field messages */
|
||||
uint64_t incorrect; /* Number of incorrect information field messages */
|
||||
uint64_t invalid; /* Number of invalid message format messages */
|
||||
};
|
||||
|
||||
/* External API functions */
|
||||
|
||||
extern const char *gtp_version();
|
||||
extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode);
|
||||
|
||||
extern int gtp_free(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
|
||||
extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp);
|
||||
|
||||
extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind) (struct
|
||||
pdp_t *
|
||||
pdp));
|
||||
|
||||
extern int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
int cause);
|
||||
|
||||
|
@ -351,53 +253,10 @@ extern int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in *p
|
|||
const uint8_t *rim_route_addr, size_t rim_route_addr_len,
|
||||
uint8_t rim_route_addr_discr);
|
||||
|
||||
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind) (struct pdp_t * pdp,
|
||||
void *pack, unsigned len));
|
||||
|
||||
extern int gtp_fd(struct gsn_t *gsn);
|
||||
extern int gtp_decaps0(struct gsn_t *gsn);
|
||||
extern int gtp_decaps1c(struct gsn_t *gsn);
|
||||
extern int gtp_decaps1u(struct gsn_t *gsn);
|
||||
extern int gtp_retrans(struct gsn_t *gsn) OSMO_DEPRECATED("This API is a no-op, libgtp already does the job internally");
|
||||
extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) OSMO_DEPRECATED("This API is a no-op and will return a 1 day timeout");
|
||||
|
||||
extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
|
||||
int (*cb_delete_context) (struct pdp_t *
|
||||
pdp));
|
||||
/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
|
||||
int (*cb_create_context) (struct pdp_t* pdp)); */
|
||||
|
||||
extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie));
|
||||
|
||||
extern int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
int (*cb) (int type, int cause, struct pdp_t * pdp,
|
||||
void *cbp));
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery3() instead, to obtain gsn handling the recovery");;
|
||||
int gtp_set_cb_recovery3(struct gsn_t *gsn,
|
||||
int (*cb) (struct gsn_t * gsn, struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery));
|
||||
|
||||
void gtp_clear_queues(struct gsn_t *gsn);
|
||||
|
||||
/* Internal functions (not part of the API */
|
||||
/* Internal functions (not part of the API) */
|
||||
|
||||
extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
|
||||
struct in_addr *inetaddrs);
|
||||
|
|
Loading…
Reference in New Issue