aboutsummaryrefslogtreecommitdiffstats
path: root/ggsn
diff options
context:
space:
mode:
Diffstat (limited to 'ggsn')
-rw-r--r--ggsn/Makefile.am10
-rw-r--r--ggsn/cmdline.c41
-rw-r--r--ggsn/cmdline.ggo1
-rw-r--r--ggsn/cmdline.h8
-rw-r--r--ggsn/ggsn.c35
-rw-r--r--ggsn/gtp-kernel.c240
-rw-r--r--ggsn/gtp-kernel.h51
7 files changed, 368 insertions, 18 deletions
diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am
index ada5720..0ea6391 100644
--- a/ggsn/Makefile.am
+++ b/ggsn/Makefile.am
@@ -4,7 +4,15 @@ AM_LDFLAGS = @EXEC_LDFLAGS@
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb
+if ENABLE_GTP_KERNEL
+AM_CFLAGS += -DGTP_KERNEL
+ggsn_LDADD = @EXEC_LDADD@ -lgtp -lgtpnl -L../gtp ../lib/libmisc.a
+else
ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a
+endif
ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
-ggsn_SOURCES = ggsn.c cmdline.c cmdline.h
+ggsn_SOURCES = ggsn.c cmdline.c cmdline.h gtp-kernel.h
+if ENABLE_GTP_KERNEL
+ggsn_SOURCES += gtp-kernel.c
+endif
diff --git a/ggsn/cmdline.c b/ggsn/cmdline.c
index 3fcfbd5..6c7f143 100644
--- a/ggsn/cmdline.c
+++ b/ggsn/cmdline.c
@@ -1,5 +1,5 @@
/*
- File autogenerated by gengetopt version 2.22.6
+ File autogenerated by gengetopt version 2.22.5
generated with the following command:
gengetopt --conf-parser
@@ -29,8 +29,6 @@ const char *gengetopt_args_info_purpose = "";
const char *gengetopt_args_info_usage = "Usage: " CMDLINE_PARSER_PACKAGE " [OPTIONS]...";
-const char *gengetopt_args_info_versiontext = "";
-
const char *gengetopt_args_info_description = "";
const char *gengetopt_args_info_help[] = {
@@ -39,8 +37,8 @@ const char *gengetopt_args_info_help[] = {
" -f, --fg Run in foreground (default=off)",
" -d, --debug Run in debug mode (default=off)",
" -c, --conf=STRING Read configuration file (default=`/etc/ggsn.conf')",
- " --pidfile=STRING Filename of process id file\n (default=`/var/run/ggsn.pid')",
- " --statedir=STRING Directory of nonvolatile data\n (default=`/var/lib/ggsn/')",
+ " --pidfile=STRING Filename of process id file \n (default=`/var/run/ggsn.pid')",
+ " --statedir=STRING Directory of nonvolatile data \n (default=`/var/lib/ggsn/')",
" -l, --listen=STRING Local interface",
" -n, --net=STRING Network (default=`192.168.0.0/24')",
" --ipup=STRING Script to run after link-up",
@@ -53,6 +51,7 @@ const char *gengetopt_args_info_help[] = {
" -a, --apn=STRING Access point name (default=`internet')",
" -q, --qos=INT Requested quality of service (default=`0x0b921f')",
" --logfile=STRING Logfile for errors",
+ " -g, --gtpnl=STRING GTP kernel support (default=`eth0')",
0
};
@@ -121,6 +120,7 @@ void clear_given (struct gengetopt_args_info *args_info)
args_info->apn_given = 0 ;
args_info->qos_given = 0 ;
args_info->logfile_given = 0 ;
+ args_info->gtpnl_given = 0 ;
}
static
@@ -159,6 +159,8 @@ void clear_args (struct gengetopt_args_info *args_info)
args_info->qos_orig = NULL;
args_info->logfile_arg = NULL;
args_info->logfile_orig = NULL;
+ args_info->gtpnl_arg = gengetopt_strdup ("eth0");
+ args_info->gtpnl_orig = NULL;
}
@@ -186,6 +188,7 @@ void init_args_info(struct gengetopt_args_info *args_info)
args_info->apn_help = gengetopt_args_info_help[16] ;
args_info->qos_help = gengetopt_args_info_help[17] ;
args_info->logfile_help = gengetopt_args_info_help[18] ;
+ args_info->gtpnl_help = gengetopt_args_info_help[19] ;
}
@@ -195,9 +198,6 @@ cmdline_parser_print_version (void)
printf ("%s %s\n",
(strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE),
CMDLINE_PARSER_VERSION);
-
- if (strlen(gengetopt_args_info_versiontext) > 0)
- printf("\n%s\n", gengetopt_args_info_versiontext);
}
static void print_help_common(void) {
@@ -297,6 +297,8 @@ cmdline_parser_release (struct gengetopt_args_info *args_info)
free_string_field (&(args_info->qos_orig));
free_string_field (&(args_info->logfile_arg));
free_string_field (&(args_info->logfile_orig));
+ free_string_field (&(args_info->gtpnl_arg));
+ free_string_field (&(args_info->gtpnl_orig));
@@ -365,6 +367,8 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
write_into_file(outfile, "qos", args_info->qos_orig, 0);
if (args_info->logfile_given)
write_into_file(outfile, "logfile", args_info->logfile_orig, 0);
+ if (args_info->gtpnl_given)
+ write_into_file(outfile, "gtpnl", args_info->gtpnl_orig, 0);
i = EXIT_SUCCESS;
@@ -589,17 +593,19 @@ cmdline_parser_internal (
{
int c; /* Character of the parsed option. */
- int error_occurred = 0;
+ int error = 0;
struct gengetopt_args_info local_args_info;
int override;
int initialize;
+ int check_required;
int check_ambiguity;
package_name = argv[0];
override = params->override;
initialize = params->initialize;
+ check_required = params->check_required;
check_ambiguity = params->check_ambiguity;
if (initialize)
@@ -636,10 +642,11 @@ cmdline_parser_internal (
{ "apn", 1, NULL, 'a' },
{ "qos", 1, NULL, 'q' },
{ "logfile", 1, NULL, 0 },
+ { "gtpnl", 1, NULL, 'g' },
{ 0, 0, 0, 0 }
};
- c = getopt_long (argc, argv, "hVfdc:l:n:a:q:", long_options, &option_index);
+ c = getopt_long (argc, argv, "hVfdc:l:n:a:q:g:", long_options, &option_index);
if (c == -1) break; /* Exit from `while (1)' loop. */
@@ -735,6 +742,18 @@ cmdline_parser_internal (
goto failure;
break;
+ case 'g': /* GTP kernel support. */
+
+
+ if (update_arg( (void *)&(args_info->gtpnl_arg),
+ &(args_info->gtpnl_orig), &(args_info->gtpnl_given),
+ &(local_args_info.gtpnl_given), optarg, 0, "eth0", ARG_STRING,
+ check_ambiguity, override, 0, 0,
+ "gtpnl", 'g',
+ additional_error))
+ goto failure;
+
+ break;
case 0: /* Long option with no short option */
/* Filename of process id file. */
@@ -894,7 +913,7 @@ cmdline_parser_internal (
cmdline_parser_release (&local_args_info);
- if ( error_occurred )
+ if ( error )
return (EXIT_FAILURE);
return 0;
diff --git a/ggsn/cmdline.ggo b/ggsn/cmdline.ggo
index 5bb05dc..603df9a 100644
--- a/ggsn/cmdline.ggo
+++ b/ggsn/cmdline.ggo
@@ -32,4 +32,5 @@ option "timelimit" - "Exit after timelimit seconds" int default="0" no
option "apn" a "Access point name" string default="internet" no
option "qos" q "Requested quality of service" int default="0x0b921f" no
option "logfile" - "Logfile for errors" string no
+option "gtpnl" g "GTP kernel support" string default="eth0" no
diff --git a/ggsn/cmdline.h b/ggsn/cmdline.h
index f3636e8..2cb4791 100644
--- a/ggsn/cmdline.h
+++ b/ggsn/cmdline.h
@@ -1,6 +1,6 @@
/** @file cmdline.h
* @brief The header file for the command line option parser
- * generated by GNU Gengetopt version 2.22.6
+ * generated by GNU Gengetopt version 2.22.5
* http://www.gnu.org/software/gengetopt.
* DO NOT modify this file, since it can be overwritten
* @author GNU Gengetopt by Lorenzo Bettini */
@@ -92,6 +92,9 @@ struct gengetopt_args_info
char * logfile_arg; /**< @brief Logfile for errors. */
char * logfile_orig; /**< @brief Logfile for errors original value given at command line. */
const char *logfile_help; /**< @brief Logfile for errors help description. */
+ char * gtpnl_arg; /**< @brief GTP kernel support (default='eth0'). */
+ char * gtpnl_orig; /**< @brief GTP kernel support original value given at command line. */
+ const char *gtpnl_help; /**< @brief GTP kernel support help description. */
unsigned int help_given ; /**< @brief Whether help was given. */
unsigned int version_given ; /**< @brief Whether version was given. */
@@ -112,6 +115,7 @@ struct gengetopt_args_info
unsigned int apn_given ; /**< @brief Whether apn was given. */
unsigned int qos_given ; /**< @brief Whether qos was given. */
unsigned int logfile_given ; /**< @brief Whether logfile was given. */
+ unsigned int gtpnl_given ; /**< @brief Whether gtpnl was given. */
} ;
@@ -129,8 +133,6 @@ struct cmdline_parser_params
extern const char *gengetopt_args_info_purpose;
/** @brief the usage string of the program */
extern const char *gengetopt_args_info_usage;
-/** @brief the description string of the program */
-extern const char *gengetopt_args_info_description;
/** @brief all the lines making the help output */
extern const char *gengetopt_args_info_help[];
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 0022b1b..ddc77ee 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -53,6 +53,7 @@
#include "../gtp/pdp.h"
#include "../gtp/gtp.h"
#include "cmdline.h"
+#include "gtp-kernel.h"
int end = 0;
int maxfd = 0; /* For select() */
@@ -150,6 +151,13 @@ int delete_context(struct pdp_t *pdp)
ippool_freeip(ippool, (struct ippoolm_t *)pdp->peer);
else
sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Peer not defined!");
+
+ if (gtp_kernel_tunnel_del(pdp)) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "Cannot delete tunnel from kernel: %s\n",
+ strerror(errno));
+ }
+
return 0;
}
@@ -184,6 +192,11 @@ int create_context_ind(struct pdp_t *pdp)
pdp->ipif = tun; /* TODO */
member->peer = pdp;
+ if (gtp_kernel_tunnel_add(pdp) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "Cannot add tunnel to kernel: %s\n", strerror(errno));
+ }
+
gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ);
return 0; /* Success */
}
@@ -275,6 +288,8 @@ int main(int argc, char **argv)
printf("pidfile: %s\n", args_info.pidfile_arg);
if (args_info.statedir_arg)
printf("statedir: %s\n", args_info.statedir_arg);
+ if (args_info.gtpnl_arg)
+ printf("gtpnl: %s\n", args_info.gtpnl_arg);
printf("timelimit: %d\n", args_info.timelimit_arg);
}
@@ -318,6 +333,8 @@ int main(int argc, char **argv)
printf("pidfile: %s\n", args_info.pidfile_arg);
if (args_info.statedir_arg)
printf("statedir: %s\n", args_info.statedir_arg);
+ if (args_info.gtpnl_arg)
+ printf("gtpnl: %s\n", args_info.gtpnl_arg);
printf("timelimit: %d\n", args_info.timelimit_arg);
}
@@ -517,10 +534,18 @@ int main(int argc, char **argv)
if (gsn->fd1u > maxfd)
maxfd = gsn->fd1u;
+ /* use GTP kernel module for data packet encapsulation */
+ if (gtp_kernel_init(gsn, &net, &mask, &args_info) < 0)
+ goto err;
+
gtp_set_cb_data_ind(gsn, encaps_tun);
gtp_set_cb_delete_context(gsn, delete_context);
gtp_set_cb_create_context_ind(gsn, create_context_ind);
+ /* skip the configuration of the tun0 if we're using the gtp0 device */
+ if (gtp_kernel_enabled())
+ goto skip_tun;
+
/* Create a tunnel interface */
if (debug)
printf("Creating tun interface\n");
@@ -548,6 +573,8 @@ int main(int argc, char **argv)
if (ipup)
tun_runscript(tun, ipup);
+skip_tun:
+
/******************************************************************/
/* Main select loop */
/******************************************************************/
@@ -578,7 +605,7 @@ int main(int argc, char **argv)
break;
}
- if (tun->fd != -1 && FD_ISSET(tun->fd, &fds) &&
+ if (tun && tun->fd != -1 && FD_ISSET(tun->fd, &fds) &&
tun_decaps(tun) < 0) {
sys_err(LOG_ERR, __FILE__, __LINE__, 0,
"TUN read failed (fd)=(%d)", tun->fd);
@@ -594,11 +621,13 @@ int main(int argc, char **argv)
gtp_decaps1u(gsn);
}
-
+err:
+ gtp_kernel_stop();
cmdline_parser_free(&args_info);
ippool_free(ippool);
gtp_free(gsn);
- tun_free(tun);
+ if (tun)
+ tun_free(tun);
return 1;
diff --git a/ggsn/gtp-kernel.c b/ggsn/gtp-kernel.c
new file mode 100644
index 0000000..16e51ac
--- /dev/null
+++ b/ggsn/gtp-kernel.c
@@ -0,0 +1,240 @@
+#ifdef __linux__
+#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
+#endif
+
+#include "../config.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <syslog.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <libgtpnl/gtp.h>
+#include <libgtpnl/gtpnl.h>
+#include <libmnl/libmnl.h>
+
+#include <errno.h>
+
+#include <time.h>
+
+#include "../lib/tun.h"
+#include "../lib/syserr.h"
+#include "../gtp/pdp.h"
+#include "../gtp/gtp.h"
+#include "cmdline.h"
+
+#include <libgtpnl/gtp.h>
+#include <libgtpnl/gtpnl.h>
+#include <libmnl/libmnl.h>
+
+#include "gtp-kernel.h"
+
+static void pdp_debug(struct pdp_t *pdp)
+{
+ int i;
+ uint64_t teid;
+
+ if (!debug)
+ return;
+
+ printf("version %u\n", pdp->version);
+ if (pdp->version == 0) {
+ teid = pdp_gettid(pdp->imsi, pdp->nsapi);
+ printf("flowid %u\n", pdp->flru);
+ } else {
+ teid = pdp->teid_gn; /* GTPIE_TEI_DI */
+ }
+
+ printf("teid %llx\n", teid);
+ printf("address (%u)\n", pdp->eua.l);
+
+ /* Byte 0: 0xf1 == IETF */
+ /* Byte 1: 0x21 == IPv4 */
+ /* Byte 2-6: IPv4 address */
+
+ for (i = 0; i < 6; i++)
+ printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */
+
+ printf("\n");
+ printf("sgsn-addr (%u)\n", pdp->gsnrc.l);
+
+ for (i = 0; i < 4; i++)
+ printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */
+
+ printf("\n");
+}
+
+static int mask2prefix(struct in_addr *mask)
+{
+ uint32_t tmp = ntohl(mask->s_addr);
+ int k;
+
+ for (k=0; tmp > 0; k++)
+ tmp = (tmp << 1);
+
+ return k;
+}
+
+static struct {
+ int genl_id;
+ struct mnl_socket *nl;
+ bool enabled;
+} gtp_nl;
+
+/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
+#define GTP_DEVNAME "gtp0"
+
+int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
+ struct in_addr *mask,
+ struct gengetopt_args_info *args_info)
+{
+ if (!args_info->gtpnl_given)
+ return 0;
+
+ if (gtp_dev_create(GTP_DEVNAME, args_info->gtpnl_orig,
+ gsn->fd0, gsn->fd1u) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "cannot create GTP tunnel device: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ gtp_nl.enabled = true;
+
+ gtp_nl.nl = genl_socket_open();
+ if (gtp_nl.nl == NULL) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "cannot create genetlink socket\n");
+ return -1;
+ }
+ gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
+ if (gtp_nl.genl_id < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "cannot lookup GTP genetlink ID\n");
+ return -1;
+ }
+ if (debug) {
+ sys_err(LOG_NOTICE, __FILE__, __LINE__, 0,
+ "Using the GTP kernel mode (genl ID is %d)\n",
+ gtp_nl.genl_id);
+ }
+
+ if (debug) {
+ printf("Setting route to reach %s via %s\n",
+ args_info->net_arg, GTP_DEVNAME);
+ }
+
+ if (gtp_dev_config(GTP_DEVNAME, net, mask2prefix(mask)) < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "Cannot add route to reach network %s\n",
+ args_info->net_arg);
+ }
+
+ /* launch script if it is set to bring up the route to reach
+ * the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
+ * using native rtnetlink interface given that we know the
+ * MS network mask, later.
+ */
+ if (ipup) {
+ char cmd[1024];
+ int err;
+
+ /* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
+ snprintf(cmd, sizeof(cmd), "%s %s %s",
+ ipup, GTP_DEVNAME, args_info->net_arg);
+ cmd[sizeof(cmd)-1] = '\0';
+
+ err = system(cmd);
+ if (err < 0) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "Failed to launch script `%s'", ipup);
+ return -1;
+ }
+ }
+ sys_err(LOG_NOTICE, __FILE__, __LINE__, 0, "GTP kernel configured\n");
+
+ return 0;
+}
+
+void gtp_kernel_stop(void)
+{
+ if (!gtp_nl.enabled)
+ return;
+
+ gtp_dev_destroy(GTP_DEVNAME);
+}
+
+int gtp_kernel_tunnel_add(struct pdp_t *pdp)
+{
+ struct in_addr ms, sgsn;
+ struct gtp_tunnel *t;
+ int ret;
+
+ if (!gtp_nl.enabled)
+ return 0;
+
+ pdp_debug(pdp);
+
+ t = gtp_tunnel_alloc();
+ if (t == NULL)
+ return -1;
+
+ memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
+ memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
+
+ gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
+ gtp_tunnel_set_version(t, pdp->version);
+ gtp_tunnel_set_ms_ip4(t, &ms);
+ gtp_tunnel_set_sgsn_ip4(t, &sgsn);
+ if (pdp->version == 0) {
+ gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
+ gtp_tunnel_set_flowid(t, pdp->flru);
+ } else {
+ gtp_tunnel_set_tid(t, pdp->teid_gn); /* GTPIE_TEI_DI */
+ }
+
+ ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
+ gtp_tunnel_free(t);
+
+ return ret;
+}
+
+int gtp_kernel_tunnel_del(struct pdp_t *pdp)
+{
+ struct gtp_tunnel *t;
+ int ret;
+
+ if (!gtp_nl.enabled)
+ return 0;
+
+ pdp_debug(pdp);
+
+ t = gtp_tunnel_alloc();
+ if (t == NULL)
+ return -1;
+
+ gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
+ gtp_tunnel_set_version(t, pdp->version);
+ if (pdp->version == 0) {
+ gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
+ gtp_tunnel_set_flowid(t, pdp->flru);
+ } else {
+ gtp_tunnel_set_tid(t, pdp->teid_gn); /* GTPIE_TEI_DI */
+ }
+
+ ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
+ gtp_tunnel_free(t);
+
+ return ret;
+}
+
+int gtp_kernel_enabled(void)
+{
+ return gtp_nl.enabled;
+}
diff --git a/ggsn/gtp-kernel.h b/ggsn/gtp-kernel.h
new file mode 100644
index 0000000..7bf533d
--- /dev/null
+++ b/ggsn/gtp-kernel.h
@@ -0,0 +1,51 @@
+#ifndef _GTP_KERNEL_H_
+#define _GTP_KERNEL_H_
+
+struct gengetopt_args_info;
+
+extern int debug;
+extern char *ipup;
+
+#ifdef GTP_KERNEL
+int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
+ struct in_addr *mask,
+ struct gengetopt_args_info *args_info);
+void gtp_kernel_stop(void);
+
+int gtp_kernel_tunnel_add(struct pdp_t *pdp);
+int gtp_kernel_tunnel_del(struct pdp_t *pdp);
+
+int gtp_kernel_enabled(void);
+
+#else
+static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
+ struct in_addr *mask,
+ struct gengetopt_args_info *args_info)
+{
+ if (args_info->gtpnl_given) {
+ sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+ "ggsn compiled without GTP kernel support!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static inline void gtp_kernel_stop(void) {}
+
+static inline int gtp_kernel_tunnel_add(struct pdp_t *pdp)
+{
+ return 0;
+}
+
+static inline int gtp_kernel_tunnel_del(struct pdp_t *pdp)
+{
+ return 0;
+}
+
+static inline int gtp_kernel_enabled(void)
+{
+ return 0;
+}
+
+#endif
+#endif /* _GTP_KERNEL_H_ */